summaryrefslogtreecommitdiffstats
path: root/sc/source/filter
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sc/source/filter/dif/difexp.cxx279
-rw-r--r--sc/source/filter/dif/difimp.cxx674
-rw-r--r--sc/source/filter/excel/colrowst.cxx359
-rw-r--r--sc/source/filter/excel/excdoc.cxx905
-rw-r--r--sc/source/filter/excel/excel.cxx494
-rw-r--r--sc/source/filter/excel/excform.cxx1911
-rw-r--r--sc/source/filter/excel/excform8.cxx1671
-rw-r--r--sc/source/filter/excel/excimp8.cxx817
-rw-r--r--sc/source/filter/excel/excrecds.cxx1177
-rw-r--r--sc/source/filter/excel/exctools.cxx245
-rw-r--r--sc/source/filter/excel/expop2.cxx150
-rw-r--r--sc/source/filter/excel/export/SparklineExt.cxx243
-rw-r--r--sc/source/filter/excel/fontbuff.cxx130
-rw-r--r--sc/source/filter/excel/frmbase.cxx209
-rw-r--r--sc/source/filter/excel/impop.cxx1414
-rw-r--r--sc/source/filter/excel/namebuff.cxx187
-rw-r--r--sc/source/filter/excel/ooxml-export-TODO.txt164
-rw-r--r--sc/source/filter/excel/read.cxx1314
-rw-r--r--sc/source/filter/excel/tokstack.cxx752
-rw-r--r--sc/source/filter/excel/xechart.cxx3475
-rw-r--r--sc/source/filter/excel/xecontent.cxx2211
-rw-r--r--sc/source/filter/excel/xedbdata.cxx261
-rw-r--r--sc/source/filter/excel/xeescher.cxx2046
-rw-r--r--sc/source/filter/excel/xeextlst.cxx652
-rw-r--r--sc/source/filter/excel/xeformula.cxx2709
-rw-r--r--sc/source/filter/excel/xehelper.cxx1071
-rw-r--r--sc/source/filter/excel/xelink.cxx2660
-rw-r--r--sc/source/filter/excel/xename.cxx870
-rw-r--r--sc/source/filter/excel/xepage.cxx512
-rw-r--r--sc/source/filter/excel/xepivot.cxx1702
-rw-r--r--sc/source/filter/excel/xepivotxml.cxx1187
-rw-r--r--sc/source/filter/excel/xerecord.cxx264
-rw-r--r--sc/source/filter/excel/xeroot.cxx360
-rw-r--r--sc/source/filter/excel/xestream.cxx1259
-rw-r--r--sc/source/filter/excel/xestring.cxx565
-rw-r--r--sc/source/filter/excel/xestyle.cxx3306
-rw-r--r--sc/source/filter/excel/xetable.cxx2841
-rw-r--r--sc/source/filter/excel/xeview.cxx537
-rw-r--r--sc/source/filter/excel/xichart.cxx4415
-rw-r--r--sc/source/filter/excel/xicontent.cxx1442
-rw-r--r--sc/source/filter/excel/xiescher.cxx4453
-rw-r--r--sc/source/filter/excel/xiformula.cxx107
-rw-r--r--sc/source/filter/excel/xihelper.cxx896
-rw-r--r--sc/source/filter/excel/xilink.cxx962
-rw-r--r--sc/source/filter/excel/xiname.cxx322
-rw-r--r--sc/source/filter/excel/xipage.cxx401
-rw-r--r--sc/source/filter/excel/xipivot.cxx1737
-rw-r--r--sc/source/filter/excel/xiroot.cxx298
-rw-r--r--sc/source/filter/excel/xistream.cxx1084
-rw-r--r--sc/source/filter/excel/xistring.cxx212
-rw-r--r--sc/source/filter/excel/xistyle.cxx2084
-rw-r--r--sc/source/filter/excel/xiview.cxx300
-rw-r--r--sc/source/filter/excel/xladdress.cxx161
-rw-r--r--sc/source/filter/excel/xlchart.cxx1283
-rw-r--r--sc/source/filter/excel/xlescher.cxx335
-rw-r--r--sc/source/filter/excel/xlformula.cxx1022
-rw-r--r--sc/source/filter/excel/xlpage.cxx262
-rw-r--r--sc/source/filter/excel/xlpivot.cxx1043
-rw-r--r--sc/source/filter/excel/xlroot.cxx438
-rw-r--r--sc/source/filter/excel/xlstyle.cxx1744
-rw-r--r--sc/source/filter/excel/xltoolbar.cxx439
-rw-r--r--sc/source/filter/excel/xltoolbar.hxx106
-rw-r--r--sc/source/filter/excel/xltools.cxx744
-rw-r--r--sc/source/filter/excel/xltracer.cxx133
-rw-r--r--sc/source/filter/excel/xlview.cxx103
-rw-r--r--sc/source/filter/ftools/fapihelper.cxx369
-rw-r--r--sc/source/filter/ftools/fprogressbar.cxx234
-rw-r--r--sc/source/filter/ftools/ftools.cxx365
-rw-r--r--sc/source/filter/ftools/sharedformulagroups.cxx40
-rw-r--r--sc/source/filter/html/htmlexp.cxx1378
-rw-r--r--sc/source/filter/html/htmlexp2.cxx223
-rw-r--r--sc/source/filter/html/htmlimp.cxx239
-rw-r--r--sc/source/filter/html/htmlpars.cxx3168
-rw-r--r--sc/source/filter/importfilterdata.cxx19
-rw-r--r--sc/source/filter/inc/SparklineFragment.hxx74
-rw-r--r--sc/source/filter/inc/XclExpChangeTrack.hxx610
-rw-r--r--sc/source/filter/inc/XclImpChangeTrack.hxx144
-rw-r--r--sc/source/filter/inc/addressconverter.hxx505
-rw-r--r--sc/source/filter/inc/autofilterbuffer.hxx286
-rw-r--r--sc/source/filter/inc/autofiltercontext.hxx114
-rw-r--r--sc/source/filter/inc/biffhelper.hxx621
-rw-r--r--sc/source/filter/inc/chartsheetfragment.hxx51
-rw-r--r--sc/source/filter/inc/colrowst.hxx86
-rw-r--r--sc/source/filter/inc/commentsbuffer.hxx91
-rw-r--r--sc/source/filter/inc/commentsfragment.hxx53
-rw-r--r--sc/source/filter/inc/condformatbuffer.hxx322
-rw-r--r--sc/source/filter/inc/condformatcontext.hxx89
-rw-r--r--sc/source/filter/inc/connectionsbuffer.hxx160
-rw-r--r--sc/source/filter/inc/connectionsfragment.hxx60
-rw-r--r--sc/source/filter/inc/decl.h25
-rw-r--r--sc/source/filter/inc/defnamesbuffer.hxx182
-rw-r--r--sc/source/filter/inc/dif.hxx152
-rw-r--r--sc/source/filter/inc/drawingbase.hxx133
-rw-r--r--sc/source/filter/inc/drawingfragment.hxx203
-rw-r--r--sc/source/filter/inc/eeimport.hxx62
-rw-r--r--sc/source/filter/inc/eeparser.hxx132
-rw-r--r--sc/source/filter/inc/excdefs.hxx93
-rw-r--r--sc/source/filter/inc/excdoc.hxx99
-rw-r--r--sc/source/filter/inc/excelchartconverter.hxx47
-rw-r--r--sc/source/filter/inc/excelfilter.hxx61
-rw-r--r--sc/source/filter/inc/excelhandlers.hxx80
-rw-r--r--sc/source/filter/inc/excelvbaproject.hxx48
-rw-r--r--sc/source/filter/inc/excform.hxx141
-rw-r--r--sc/source/filter/inc/excimp8.hxx115
-rw-r--r--sc/source/filter/inc/excrecds.hxx457
-rw-r--r--sc/source/filter/inc/excscen.hxx70
-rw-r--r--sc/source/filter/inc/exp_op.hxx62
-rw-r--r--sc/source/filter/inc/expbase.hxx63
-rw-r--r--sc/source/filter/inc/export/SparklineExt.hxx55
-rw-r--r--sc/source/filter/inc/externallinkbuffer.hxx350
-rw-r--r--sc/source/filter/inc/externallinkfragment.hxx99
-rw-r--r--sc/source/filter/inc/extlstcontext.hxx146
-rw-r--r--sc/source/filter/inc/fapihelper.hxx294
-rw-r--r--sc/source/filter/inc/flttypes.hxx39
-rw-r--r--sc/source/filter/inc/formel.hxx188
-rw-r--r--sc/source/filter/inc/formulabase.hxx780
-rw-r--r--sc/source/filter/inc/formulabuffer.hxx119
-rw-r--r--sc/source/filter/inc/formulaparser.hxx131
-rw-r--r--sc/source/filter/inc/fprogressbar.hxx223
-rw-r--r--sc/source/filter/inc/ftools.hxx295
-rw-r--r--sc/source/filter/inc/htmlexp.hxx185
-rw-r--r--sc/source/filter/inc/htmlimp.hxx38
-rw-r--r--sc/source/filter/inc/htmlpars.hxx630
-rw-r--r--sc/source/filter/inc/imp_op.hxx200
-rw-r--r--sc/source/filter/inc/lotattr.hxx134
-rw-r--r--sc/source/filter/inc/lotfntbf.hxx59
-rw-r--r--sc/source/filter/inc/lotform.hxx114
-rw-r--r--sc/source/filter/inc/lotimpop.hxx136
-rw-r--r--sc/source/filter/inc/lotrange.hxx114
-rw-r--r--sc/source/filter/inc/namebuff.hxx190
-rw-r--r--sc/source/filter/inc/numberformatsbuffer.hxx116
-rw-r--r--sc/source/filter/inc/ooxformulaparser.hxx89
-rw-r--r--sc/source/filter/inc/op.h58
-rw-r--r--sc/source/filter/inc/optab.h48
-rw-r--r--sc/source/filter/inc/orcusfiltersimpl.hxx47
-rw-r--r--sc/source/filter/inc/orcusinterface.hxx656
-rw-r--r--sc/source/filter/inc/otlnbuff.hxx50
-rw-r--r--sc/source/filter/inc/pagesettings.hxx186
-rw-r--r--sc/source/filter/inc/pivotcachebuffer.hxx458
-rw-r--r--sc/source/filter/inc/pivotcachefragment.hxx91
-rw-r--r--sc/source/filter/inc/pivottablebuffer.hxx404
-rw-r--r--sc/source/filter/inc/pivottablefragment.hxx83
-rw-r--r--sc/source/filter/inc/qpro.hxx57
-rw-r--r--sc/source/filter/inc/qproform.hxx71
-rw-r--r--sc/source/filter/inc/qprostyle.hxx56
-rw-r--r--sc/source/filter/inc/querytablebuffer.hxx83
-rw-r--r--sc/source/filter/inc/querytablefragment.hxx47
-rw-r--r--sc/source/filter/inc/revisionfragment.hxx76
-rw-r--r--sc/source/filter/inc/richstring.hxx264
-rw-r--r--sc/source/filter/inc/richstringcontext.hxx55
-rw-r--r--sc/source/filter/inc/root.hxx74
-rw-r--r--sc/source/filter/inc/rtfexp.hxx42
-rw-r--r--sc/source/filter/inc/rtfimp.hxx30
-rw-r--r--sc/source/filter/inc/rtfparse.hxx78
-rw-r--r--sc/source/filter/inc/scenariobuffer.hxx126
-rw-r--r--sc/source/filter/inc/scenariocontext.hxx63
-rw-r--r--sc/source/filter/inc/scfobj.hxx34
-rw-r--r--sc/source/filter/inc/scmem.h29
-rw-r--r--sc/source/filter/inc/sharedformulagroups.hxx51
-rw-r--r--sc/source/filter/inc/sharedstringsbuffer.hxx48
-rw-r--r--sc/source/filter/inc/sharedstringsfragment.hxx43
-rw-r--r--sc/source/filter/inc/sheetdatabuffer.hxx245
-rw-r--r--sc/source/filter/inc/sheetdatacontext.hxx124
-rw-r--r--sc/source/filter/inc/stylesbuffer.hxx891
-rw-r--r--sc/source/filter/inc/stylesfragment.hxx129
-rw-r--r--sc/source/filter/inc/tablebuffer.hxx124
-rw-r--r--sc/source/filter/inc/tablecolumnsbuffer.hxx96
-rw-r--r--sc/source/filter/inc/tablecolumnscontext.hxx63
-rw-r--r--sc/source/filter/inc/tablefragment.hxx47
-rw-r--r--sc/source/filter/inc/themebuffer.hxx50
-rw-r--r--sc/source/filter/inc/tokstack.hxx426
-rw-r--r--sc/source/filter/inc/tool.h133
-rw-r--r--sc/source/filter/inc/unitconverter.hxx96
-rw-r--r--sc/source/filter/inc/viewsettings.hxx189
-rw-r--r--sc/source/filter/inc/workbookfragment.hxx64
-rw-r--r--sc/source/filter/inc/workbookhelper.hxx275
-rw-r--r--sc/source/filter/inc/workbooksettings.hxx119
-rw-r--r--sc/source/filter/inc/worksheetbuffer.hxx108
-rw-r--r--sc/source/filter/inc/worksheetfragment.hxx182
-rw-r--r--sc/source/filter/inc/worksheethelper.hxx305
-rw-r--r--sc/source/filter/inc/worksheetsettings.hxx115
-rw-r--r--sc/source/filter/inc/xcl97esc.hxx175
-rw-r--r--sc/source/filter/inc/xcl97rec.hxx600
-rw-r--r--sc/source/filter/inc/xechart.hxx1191
-rw-r--r--sc/source/filter/inc/xecontent.hxx416
-rw-r--r--sc/source/filter/inc/xedbdata.hxx66
-rw-r--r--sc/source/filter/inc/xeescher.hxx464
-rw-r--r--sc/source/filter/inc/xeextlst.hxx208
-rw-r--r--sc/source/filter/inc/xeformula.hxx91
-rw-r--r--sc/source/filter/inc/xehelper.hxx438
-rw-r--r--sc/source/filter/inc/xelink.hxx222
-rw-r--r--sc/source/filter/inc/xename.hxx73
-rw-r--r--sc/source/filter/inc/xepage.hxx124
-rw-r--r--sc/source/filter/inc/xepivot.hxx436
-rw-r--r--sc/source/filter/inc/xepivotxml.hxx90
-rw-r--r--sc/source/filter/inc/xerecord.hxx421
-rw-r--r--sc/source/filter/inc/xeroot.hxx191
-rw-r--r--sc/source/filter/inc/xestream.hxx359
-rw-r--r--sc/source/filter/inc/xestring.hxx264
-rw-r--r--sc/source/filter/inc/xestyle.hxx773
-rw-r--r--sc/source/filter/inc/xetable.hxx1028
-rw-r--r--sc/source/filter/inc/xeview.hxx164
-rw-r--r--sc/source/filter/inc/xichart.hxx1433
-rw-r--r--sc/source/filter/inc/xicontent.hxx330
-rw-r--r--sc/source/filter/inc/xiescher.hxx1212
-rw-r--r--sc/source/filter/inc/xiformula.hxx54
-rw-r--r--sc/source/filter/inc/xihelper.hxx350
-rw-r--r--sc/source/filter/inc/xilink.hxx227
-rw-r--r--sc/source/filter/inc/xiname.hxx107
-rw-r--r--sc/source/filter/inc/xipage.hxx70
-rw-r--r--sc/source/filter/inc/xipivot.hxx427
-rw-r--r--sc/source/filter/inc/xiroot.hxx219
-rw-r--r--sc/source/filter/inc/xistream.hxx552
-rw-r--r--sc/source/filter/inc/xistring.hxx104
-rw-r--r--sc/source/filter/inc/xistyle.hxx669
-rw-r--r--sc/source/filter/inc/xiview.hxx83
-rw-r--r--sc/source/filter/inc/xladdress.hxx169
-rw-r--r--sc/source/filter/inc/xlchart.hxx1437
-rw-r--r--sc/source/filter/inc/xlconst.hxx259
-rw-r--r--sc/source/filter/inc/xlcontent.hxx187
-rw-r--r--sc/source/filter/inc/xlescher.hxx434
-rw-r--r--sc/source/filter/inc/xlformula.hxx551
-rw-r--r--sc/source/filter/inc/xllink.hxx99
-rw-r--r--sc/source/filter/inc/xlname.hxx63
-rw-r--r--sc/source/filter/inc/xlpage.hxx166
-rw-r--r--sc/source/filter/inc/xlpivot.hxx774
-rw-r--r--sc/source/filter/inc/xlroot.hxx269
-rw-r--r--sc/source/filter/inc/xlstream.hxx38
-rw-r--r--sc/source/filter/inc/xlstring.hxx87
-rw-r--r--sc/source/filter/inc/xlstyle.hxx596
-rw-r--r--sc/source/filter/inc/xltable.hxx167
-rw-r--r--sc/source/filter/inc/xltools.hxx256
-rw-r--r--sc/source/filter/inc/xltracer.hxx86
-rw-r--r--sc/source/filter/inc/xlview.hxx162
-rw-r--r--sc/source/filter/lotus/filter.cxx211
-rw-r--r--sc/source/filter/lotus/lotattr.cxx250
-rw-r--r--sc/source/filter/lotus/lotfilter.hxx63
-rw-r--r--sc/source/filter/lotus/lotform.cxx2106
-rw-r--r--sc/source/filter/lotus/lotimpop.cxx489
-rw-r--r--sc/source/filter/lotus/lotread.cxx336
-rw-r--r--sc/source/filter/lotus/lotus.cxx100
-rw-r--r--sc/source/filter/lotus/memory.cxx55
-rw-r--r--sc/source/filter/lotus/op.cxx684
-rw-r--r--sc/source/filter/lotus/optab.cxx235
-rw-r--r--sc/source/filter/lotus/tool.cxx524
-rw-r--r--sc/source/filter/oox/SparklineFragment.cxx273
-rw-r--r--sc/source/filter/oox/addressconverter.cxx483
-rw-r--r--sc/source/filter/oox/autofilterbuffer.cxx955
-rw-r--r--sc/source/filter/oox/autofiltercontext.cxx219
-rw-r--r--sc/source/filter/oox/biffhelper.cxx99
-rw-r--r--sc/source/filter/oox/chartsheetfragment.cxx174
-rw-r--r--sc/source/filter/oox/commentsbuffer.cxx243
-rw-r--r--sc/source/filter/oox/commentsfragment.cxx144
-rw-r--r--sc/source/filter/oox/condformatbuffer.cxx1399
-rw-r--r--sc/source/filter/oox/condformatcontext.cxx264
-rw-r--r--sc/source/filter/oox/connectionsbuffer.cxx315
-rw-r--r--sc/source/filter/oox/connectionsfragment.cxx161
-rw-r--r--sc/source/filter/oox/defnamesbuffer.cxx442
-rw-r--r--sc/source/filter/oox/drawingbase.cxx295
-rw-r--r--sc/source/filter/oox/drawingfragment.cxx807
-rw-r--r--sc/source/filter/oox/excelchartconverter.cxx125
-rw-r--r--sc/source/filter/oox/excelfilter.cxx215
-rw-r--r--sc/source/filter/oox/excelhandlers.cxx42
-rw-r--r--sc/source/filter/oox/excelvbaproject.cxx127
-rw-r--r--sc/source/filter/oox/externallinkbuffer.cxx694
-rw-r--r--sc/source/filter/oox/externallinkfragment.cxx338
-rw-r--r--sc/source/filter/oox/extlstcontext.cxx432
-rw-r--r--sc/source/filter/oox/formulabase.cxx1714
-rw-r--r--sc/source/filter/oox/formulabuffer.cxx476
-rw-r--r--sc/source/filter/oox/formulaparser.cxx1855
-rw-r--r--sc/source/filter/oox/numberformatsbuffer.cxx2094
-rw-r--r--sc/source/filter/oox/ooxformulaparser.cxx174
-rw-r--r--sc/source/filter/oox/pagesettings.cxx1065
-rw-r--r--sc/source/filter/oox/pivotcachebuffer.cxx1225
-rw-r--r--sc/source/filter/oox/pivotcachefragment.cxx325
-rw-r--r--sc/source/filter/oox/pivottablebuffer.cxx1480
-rw-r--r--sc/source/filter/oox/pivottablefragment.cxx280
-rw-r--r--sc/source/filter/oox/querytablebuffer.cxx286
-rw-r--r--sc/source/filter/oox/querytablefragment.cxx72
-rw-r--r--sc/source/filter/oox/revisionfragment.cxx455
-rw-r--r--sc/source/filter/oox/richstring.cxx597
-rw-r--r--sc/source/filter/oox/richstringcontext.cxx91
-rw-r--r--sc/source/filter/oox/scenariobuffer.cxx215
-rw-r--r--sc/source/filter/oox/scenariocontext.cxx112
-rw-r--r--sc/source/filter/oox/sharedstringsbuffer.cxx48
-rw-r--r--sc/source/filter/oox/sharedstringsfragment.cxx88
-rw-r--r--sc/source/filter/oox/sheetdatabuffer.cxx823
-rw-r--r--sc/source/filter/oox/sheetdatacontext.cxx583
-rw-r--r--sc/source/filter/oox/stylesbuffer.cxx3064
-rw-r--r--sc/source/filter/oox/stylesfragment.cxx317
-rw-r--r--sc/source/filter/oox/tablebuffer.cxx214
-rw-r--r--sc/source/filter/oox/tablecolumnsbuffer.cxx128
-rw-r--r--sc/source/filter/oox/tablecolumnscontext.cxx92
-rw-r--r--sc/source/filter/oox/tablefragment.cxx97
-rw-r--r--sc/source/filter/oox/themebuffer.cxx54
-rw-r--r--sc/source/filter/oox/unitconverter.cxx228
-rw-r--r--sc/source/filter/oox/viewsettings.cxx651
-rw-r--r--sc/source/filter/oox/workbookfragment.cxx641
-rw-r--r--sc/source/filter/oox/workbookhelper.cxx1036
-rw-r--r--sc/source/filter/oox/workbooksettings.cxx298
-rw-r--r--sc/source/filter/oox/worksheetbuffer.cxx251
-rw-r--r--sc/source/filter/oox/worksheetfragment.cxx911
-rw-r--r--sc/source/filter/oox/worksheethelper.cxx1655
-rw-r--r--sc/source/filter/oox/worksheetsettings.cxx294
-rw-r--r--sc/source/filter/orcus/filterdetect.cxx107
-rw-r--r--sc/source/filter/orcus/interface.cxx2198
-rw-r--r--sc/source/filter/orcus/orcusfiltersimpl.cxx150
-rw-r--r--sc/source/filter/orcus/xmlcontext.cxx292
-rw-r--r--sc/source/filter/qpro/README4
-rw-r--r--sc/source/filter/qpro/qpro.cxx320
-rw-r--r--sc/source/filter/qpro/qproform.cxx752
-rw-r--r--sc/source/filter/qpro/qprostyle.cxx139
-rw-r--r--sc/source/filter/rtf/eeimpars.cxx657
-rw-r--r--sc/source/filter/rtf/expbase.cxx75
-rw-r--r--sc/source/filter/rtf/rtfexp.cxx233
-rw-r--r--sc/source/filter/rtf/rtfimp.cxx47
-rw-r--r--sc/source/filter/rtf/rtfparse.cxx403
-rw-r--r--sc/source/filter/xcl97/XclExpChangeTrack.cxx1647
-rw-r--r--sc/source/filter/xcl97/XclImpChangeTrack.cxx517
-rw-r--r--sc/source/filter/xcl97/xcl97esc.cxx574
-rw-r--r--sc/source/filter/xcl97/xcl97rec.cxx2026
-rw-r--r--sc/source/filter/xml/SparklineGroupsExport.cxx218
-rw-r--r--sc/source/filter/xml/SparklineGroupsExport.hxx46
-rw-r--r--sc/source/filter/xml/SparklineGroupsImportContext.cxx333
-rw-r--r--sc/source/filter/xml/SparklineGroupsImportContext.hxx63
-rw-r--r--sc/source/filter/xml/XMLCalculationSettingsContext.cxx198
-rw-r--r--sc/source/filter/xml/XMLCalculationSettingsContext.hxx76
-rw-r--r--sc/source/filter/xml/XMLCellRangeSourceContext.cxx96
-rw-r--r--sc/source/filter/xml/XMLCellRangeSourceContext.hxx51
-rw-r--r--sc/source/filter/xml/XMLChangeTrackingExportHelper.cxx687
-rw-r--r--sc/source/filter/xml/XMLChangeTrackingExportHelper.hxx84
-rw-r--r--sc/source/filter/xml/XMLChangeTrackingImportHelper.cxx808
-rw-r--r--sc/source/filter/xml/XMLChangeTrackingImportHelper.hxx220
-rw-r--r--sc/source/filter/xml/XMLCodeNameProvider.cxx178
-rw-r--r--sc/source/filter/xml/XMLCodeNameProvider.hxx53
-rw-r--r--sc/source/filter/xml/XMLColumnRowGroupExport.cxx148
-rw-r--r--sc/source/filter/xml/XMLColumnRowGroupExport.hxx62
-rw-r--r--sc/source/filter/xml/XMLConsolidationContext.cxx123
-rw-r--r--sc/source/filter/xml/XMLConsolidationContext.hxx49
-rw-r--r--sc/source/filter/xml/XMLConverter.cxx630
-rw-r--r--sc/source/filter/xml/XMLConverter.hxx148
-rw-r--r--sc/source/filter/xml/XMLDDELinksContext.cxx362
-rw-r--r--sc/source/filter/xml/XMLDDELinksContext.hxx158
-rw-r--r--sc/source/filter/xml/XMLDetectiveContext.cxx190
-rw-r--r--sc/source/filter/xml/XMLDetectiveContext.hxx127
-rw-r--r--sc/source/filter/xml/XMLEmptyContext.cxx41
-rw-r--r--sc/source/filter/xml/XMLEmptyContext.hxx35
-rw-r--r--sc/source/filter/xml/XMLExportDDELinks.cxx159
-rw-r--r--sc/source/filter/xml/XMLExportDDELinks.hxx41
-rw-r--r--sc/source/filter/xml/XMLExportDataPilot.cxx901
-rw-r--r--sc/source/filter/xml/XMLExportDataPilot.hxx74
-rw-r--r--sc/source/filter/xml/XMLExportDatabaseRanges.cxx752
-rw-r--r--sc/source/filter/xml/XMLExportDatabaseRanges.hxx43
-rw-r--r--sc/source/filter/xml/XMLExportIterator.cxx731
-rw-r--r--sc/source/filter/xml/XMLExportIterator.hxx374
-rw-r--r--sc/source/filter/xml/XMLExportSharedData.cxx142
-rw-r--r--sc/source/filter/xml/XMLExportSharedData.hxx84
-rw-r--r--sc/source/filter/xml/XMLStylesExportHelper.cxx1066
-rw-r--r--sc/source/filter/xml/XMLStylesExportHelper.hxx254
-rw-r--r--sc/source/filter/xml/XMLStylesImportHelper.cxx401
-rw-r--r--sc/source/filter/xml/XMLStylesImportHelper.hxx147
-rw-r--r--sc/source/filter/xml/XMLTableHeaderFooterContext.cxx237
-rw-r--r--sc/source/filter/xml/XMLTableHeaderFooterContext.hxx78
-rw-r--r--sc/source/filter/xml/XMLTableMasterPageExport.cxx205
-rw-r--r--sc/source/filter/xml/XMLTableMasterPageExport.hxx53
-rw-r--r--sc/source/filter/xml/XMLTableShapeImportHelper.cxx231
-rw-r--r--sc/source/filter/xml/XMLTableShapeImportHelper.hxx52
-rw-r--r--sc/source/filter/xml/XMLTableShapeResizer.cxx145
-rw-r--r--sc/source/filter/xml/XMLTableShapeResizer.hxx56
-rw-r--r--sc/source/filter/xml/XMLTableShapesContext.cxx53
-rw-r--r--sc/source/filter/xml/XMLTableShapesContext.hxx38
-rw-r--r--sc/source/filter/xml/XMLTableSourceContext.cxx106
-rw-r--r--sc/source/filter/xml/XMLTableSourceContext.hxx45
-rw-r--r--sc/source/filter/xml/XMLTrackedChangesContext.cxx1398
-rw-r--r--sc/source/filter/xml/XMLTrackedChangesContext.hxx46
-rw-r--r--sc/source/filter/xml/cachedattraccess.cxx55
-rw-r--r--sc/source/filter/xml/cachedattraccess.hxx44
-rw-r--r--sc/source/filter/xml/celltextparacontext.cxx442
-rw-r--r--sc/source/filter/xml/celltextparacontext.hxx191
-rw-r--r--sc/source/filter/xml/datastreamimport.cxx84
-rw-r--r--sc/source/filter/xml/datastreamimport.hxx36
-rw-r--r--sc/source/filter/xml/editattributemap.cxx91
-rw-r--r--sc/source/filter/xml/editattributemap.hxx44
-rw-r--r--sc/source/filter/xml/importcontext.cxx28
-rw-r--r--sc/source/filter/xml/importcontext.hxx29
-rw-r--r--sc/source/filter/xml/pivotsource.cxx120
-rw-r--r--sc/source/filter/xml/pivotsource.hxx78
-rw-r--r--sc/source/filter/xml/sheetdata.cxx245
-rw-r--r--sc/source/filter/xml/xmlannoi.cxx171
-rw-r--r--sc/source/filter/xml/xmlannoi.hxx103
-rw-r--r--sc/source/filter/xml/xmlbodyi.cxx272
-rw-r--r--sc/source/filter/xml/xmlbodyi.hxx56
-rw-r--r--sc/source/filter/xml/xmlcelli.cxx1521
-rw-r--r--sc/source/filter/xml/xmlcelli.hxx153
-rw-r--r--sc/source/filter/xml/xmlcoli.cxx250
-rw-r--r--sc/source/filter/xml/xmlcoli.hxx66
-rw-r--r--sc/source/filter/xml/xmlcondformat.cxx1009
-rw-r--r--sc/source/filter/xml/xmlcondformat.hxx154
-rw-r--r--sc/source/filter/xml/xmlconti.cxx65
-rw-r--r--sc/source/filter/xml/xmlconti.hxx41
-rw-r--r--sc/source/filter/xml/xmlcvali.cxx557
-rw-r--r--sc/source/filter/xml/xmlcvali.hxx35
-rw-r--r--sc/source/filter/xml/xmldpimp.cxx1536
-rw-r--r--sc/source/filter/xml/xmldpimp.hxx470
-rw-r--r--sc/source/filter/xml/xmldrani.cxx812
-rw-r--r--sc/source/filter/xml/xmldrani.hxx256
-rw-r--r--sc/source/filter/xml/xmlexprt.cxx5454
-rw-r--r--sc/source/filter/xml/xmlexprt.hxx280
-rw-r--r--sc/source/filter/xml/xmlexternaltabi.cxx378
-rw-r--r--sc/source/filter/xml/xmlexternaltabi.hxx132
-rw-r--r--sc/source/filter/xml/xmlfilti.cxx784
-rw-r--r--sc/source/filter/xml/xmlfilti.hxx265
-rw-r--r--sc/source/filter/xml/xmlfonte.cxx156
-rw-r--r--sc/source/filter/xml/xmlimprt.cxx1766
-rw-r--r--sc/source/filter/xml/xmlimprt.hxx353
-rw-r--r--sc/source/filter/xml/xmllabri.cxx98
-rw-r--r--sc/source/filter/xml/xmllabri.hxx57
-rw-r--r--sc/source/filter/xml/xmlmappingi.cxx140
-rw-r--r--sc/source/filter/xml/xmlmappingi.hxx45
-rw-r--r--sc/source/filter/xml/xmlnexpi.cxx165
-rw-r--r--sc/source/filter/xml/xmlnexpi.hxx106
-rw-r--r--sc/source/filter/xml/xmlrowi.cxx360
-rw-r--r--sc/source/filter/xml/xmlrowi.hxx72
-rw-r--r--sc/source/filter/xml/xmlsceni.cxx125
-rw-r--r--sc/source/filter/xml/xmlsceni.hxx52
-rw-r--r--sc/source/filter/xml/xmlsorti.cxx244
-rw-r--r--sc/source/filter/xml/xmlsorti.hxx81
-rw-r--r--sc/source/filter/xml/xmlstyle.cxx1902
-rw-r--r--sc/source/filter/xml/xmlstyle.hxx340
-rw-r--r--sc/source/filter/xml/xmlstyli.cxx1037
-rw-r--r--sc/source/filter/xml/xmlstyli.hxx225
-rw-r--r--sc/source/filter/xml/xmlsubti.cxx291
-rw-r--r--sc/source/filter/xml/xmlsubti.hxx108
-rw-r--r--sc/source/filter/xml/xmltabi.cxx470
-rw-r--r--sc/source/filter/xml/xmltabi.hxx70
-rw-r--r--sc/source/filter/xml/xmltransformationi.cxx637
-rw-r--r--sc/source/filter/xml/xmltransformationi.hxx169
-rw-r--r--sc/source/filter/xml/xmlwrap.cxx998
438 files changed, 197878 insertions, 0 deletions
diff --git a/sc/source/filter/dif/difexp.cxx b/sc/source/filter/dif/difexp.cxx
new file mode 100644
index 000000000..89bafd754
--- /dev/null
+++ b/sc/source/filter/dif/difexp.cxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dif.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <progress.hxx>
+#include <rtl/tencinfo.h>
+#include <ftools.hxx>
+#include <cellvalue.hxx>
+#include <rtl/strbuf.hxx>
+#include <osl/diagnose.h>
+#include <formula/errorcodes.hxx>
+#include <tools/stream.hxx>
+
+void ScFormatFilterPluginImpl::ScExportDif( SvStream& rStream, ScDocument* pDoc,
+ const ScAddress& rOutPos, const rtl_TextEncoding eNach )
+{
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pDoc->GetTableArea( rOutPos.Tab(), nEndCol, nEndRow );
+ ScAddress aEnd( nEndCol, nEndRow, rOutPos.Tab() );
+ ScAddress aStart( rOutPos );
+
+ aStart.PutInOrder( aEnd );
+
+ ScExportDif( rStream, pDoc, ScRange( aStart, aEnd ), eNach );
+}
+
+void ScFormatFilterPluginImpl::ScExportDif( SvStream& rOut, ScDocument* pDoc,
+ const ScRange&rRange, const rtl_TextEncoding eCharSet )
+{
+ OSL_ENSURE( rRange.aStart <= rRange.aEnd, "*ScExportDif(): Range not sorted!" );
+ OSL_ENSURE( rRange.aStart.Tab() == rRange.aEnd.Tab(),
+ "ScExportDif(): only one table please!" );
+
+ const rtl_TextEncoding eStreamCharSet = rOut.GetStreamCharSet();
+ if ( eStreamCharSet != eCharSet )
+ rOut.SetStreamCharSet( eCharSet );
+
+ sal_Unicode cStrDelim('"');
+ OString aStrDelimEncoded; // only used if not Unicode
+ OUString aStrDelimDecoded; // only used if context encoding
+ bool bContextOrNotAsciiEncoding;
+ if ( eCharSet == RTL_TEXTENCODING_UNICODE )
+ {
+ rOut.StartWritingUnicodeText();
+ bContextOrNotAsciiEncoding = false;
+ }
+ else
+ {
+ aStrDelimEncoded = OString(&cStrDelim, 1, eCharSet);
+ rtl_TextEncodingInfo aInfo;
+ aInfo.StructSize = sizeof(aInfo);
+ if ( rtl_getTextEncodingInfo( eCharSet, &aInfo ) )
+ {
+ bContextOrNotAsciiEncoding =
+ (((aInfo.Flags & RTL_TEXTENCODING_INFO_CONTEXT) != 0) ||
+ ((aInfo.Flags & RTL_TEXTENCODING_INFO_ASCII) == 0));
+ if ( bContextOrNotAsciiEncoding )
+ aStrDelimDecoded = OStringToOUString(aStrDelimEncoded, eCharSet);
+ }
+ else
+ bContextOrNotAsciiEncoding = false;
+ }
+
+ const char p2DoubleQuotes_LF[] = "\"\"\n";
+ const char pSpecDataType_LF[] = "-1,0\n";
+ const char pEmptyData[] = "1,0\n\"\"\n";
+ const char pStringData[] = "1,0\n";
+ const char pNumData[] = "0,";
+ const char pNumDataERROR[] = "0,0\nERROR\n";
+
+ OUStringBuffer aOS;
+ OUString aString;
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCCOL nNumCols = nEndCol - rRange.aStart.Col() + 1;
+ SCROW nNumRows = nEndRow - rRange.aStart.Row() + 1;
+ SCTAB nTab = rRange.aStart.Tab();
+
+ ScProgress aPrgrsBar( pDoc->GetDocumentShell(), ScResId( STR_LOAD_DOC ), nNumRows, true );
+
+ aPrgrsBar.SetState( 0 );
+
+ // TABLE
+ OSL_ENSURE( pDoc->HasTable( nTab ), "*ScExportDif(): Table not existent!" );
+
+ aOS.append(pKeyTABLE);
+ aOS.append("\n0,1\n\"");
+
+ pDoc->GetName( nTab, aString );
+ aOS.append(aString);
+ aOS.append("\"\n");
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ // VECTORS
+ aOS.append(pKeyVECTORS);
+ aOS.append("\n0,");
+ aOS.append(static_cast<sal_Int32>(nNumCols));
+ aOS.append('\n');
+ aOS.append(p2DoubleQuotes_LF);
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ // TUPLES
+ aOS.append(pKeyTUPLES);
+ aOS.append("\n0,");
+ aOS.append(static_cast<sal_Int32>(nNumRows));
+ aOS.append('\n');
+ aOS.append(p2DoubleQuotes_LF);
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ // DATA
+ aOS.append(pKeyDATA);
+ aOS.append("\n0,0\n");
+ aOS.append(p2DoubleQuotes_LF);
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ SCCOL nColCnt;
+ SCROW nRowCnt;
+
+ for( nRowCnt = rRange.aStart.Row() ; nRowCnt <= nEndRow ; nRowCnt++ )
+ {
+ assert( aOS.isEmpty() && "aOS should be empty");
+ aOS.append(pSpecDataType_LF);
+ aOS.append(pKeyBOT);
+ aOS.append('\n');
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+ for( nColCnt = rRange.aStart.Col() ; nColCnt <= nEndCol ; nColCnt++ )
+ {
+ assert( aOS.isEmpty() && "aOS should be empty");
+ bool bWriteStringData = false;
+ ScRefCellValue aCell(*pDoc, ScAddress(nColCnt, nRowCnt, nTab));
+
+ switch (aCell.meType)
+ {
+ case CELLTYPE_NONE:
+ aOS.append(pEmptyData);
+ break;
+ case CELLTYPE_VALUE:
+ aOS.append(pNumData);
+ aString = pDoc->GetInputString( nColCnt, nRowCnt, nTab );
+ aOS.append(aString);
+ aOS.append("\nV\n");
+ break;
+ case CELLTYPE_EDIT:
+ case CELLTYPE_STRING:
+ aString = aCell.getString(pDoc);
+ bWriteStringData = true;
+ break;
+ case CELLTYPE_FORMULA:
+ if (aCell.mpFormula->GetErrCode() != FormulaError::NONE)
+ aOS.append(pNumDataERROR);
+ else if (aCell.mpFormula->IsValue())
+ {
+ aOS.append(pNumData);
+ aString = pDoc->GetInputString( nColCnt, nRowCnt, nTab );
+ aOS.append(aString);
+ aOS.append("\nV\n");
+ }
+ else
+ {
+ aString = aCell.mpFormula->GetString().getString();
+ bWriteStringData = true;
+ }
+
+ break;
+ default:;
+ }
+
+ if ( !bWriteStringData )
+ {
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+ }
+ else
+ {
+ // for an explanation why this complicated, see
+ // sc/source/ui/docsh.cxx:ScDocShell::AsciiSave()
+ // In fact we should create a common method if this would be
+ // needed just one more time...
+ assert( aOS.isEmpty() && "aOS should be empty");
+ OUString aTmpStr = aString;
+ aOS.append(pStringData);
+ rOut.WriteUnicodeOrByteText(aOS, eCharSet);
+ aOS.setLength(0);
+ if ( eCharSet == RTL_TEXTENCODING_UNICODE )
+ {
+ sal_Int32 nPos = aTmpStr.indexOf( cStrDelim );
+ while ( nPos != -1 )
+ {
+ aTmpStr = aTmpStr.replaceAt( nPos, 0, rtl::OUStringChar(cStrDelim) );
+ nPos = aTmpStr.indexOf( cStrDelim, nPos+2 );
+ }
+ rOut.WriteUniOrByteChar( cStrDelim, eCharSet );
+ write_uInt16s_FromOUString(rOut, aTmpStr);
+ rOut.WriteUniOrByteChar( cStrDelim, eCharSet );
+ }
+ else if ( bContextOrNotAsciiEncoding )
+ {
+ // to byte encoding
+ OString aStrEnc = OUStringToOString(aTmpStr, eCharSet);
+ // back to Unicode
+ OUString aStrDec = OStringToOUString(aStrEnc, eCharSet);
+ // search on re-decoded string
+ sal_Int32 nPos = aStrDec.indexOf(aStrDelimDecoded);
+ while (nPos >= 0)
+ {
+ OUStringBuffer aBuf(aStrDec);
+ aBuf.insert(nPos, aStrDelimDecoded);
+ aStrDec = aBuf.makeStringAndClear();
+ nPos = aStrDec.indexOf(
+ aStrDelimDecoded, nPos+1+aStrDelimDecoded.getLength());
+ }
+ // write byte re-encoded
+ rOut.WriteUniOrByteChar( cStrDelim, eCharSet );
+ rOut.WriteUnicodeOrByteText( aStrDec, eCharSet );
+ rOut.WriteUniOrByteChar( cStrDelim, eCharSet );
+ }
+ else
+ {
+ OString aStrEnc = OUStringToOString(aTmpStr, eCharSet);
+ // search on encoded string
+ sal_Int32 nPos = aStrEnc.indexOf(aStrDelimEncoded);
+ while (nPos >= 0)
+ {
+ OStringBuffer aBuf(aStrEnc);
+ aBuf.insert(nPos, aStrDelimEncoded);
+ aStrEnc = aBuf.makeStringAndClear();
+ nPos = aStrEnc.indexOf(
+ aStrDelimEncoded, nPos+1+aStrDelimEncoded.getLength());
+ }
+ // write byte encoded
+ rOut.WriteBytes(aStrDelimEncoded.getStr(), aStrDelimEncoded.getLength());
+ rOut.WriteBytes(aStrEnc.getStr(), aStrEnc.getLength());
+ rOut.WriteBytes(aStrDelimEncoded.getStr(), aStrDelimEncoded.getLength());
+ }
+ rOut.WriteUniOrByteChar( '\n', eCharSet );
+ }
+ }
+ aPrgrsBar.SetState( nRowCnt );
+ }
+
+ assert( aOS.isEmpty() && "aOS should be empty");
+ aOS.append(pSpecDataType_LF);
+ aOS.append(pKeyEOD);
+ aOS.append('\n');
+ rOut.WriteUnicodeOrByteText(aOS);
+ aOS.setLength(0);
+
+ // restore original value
+ rOut.SetStreamCharSet( eStreamCharSet );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/dif/difimp.cxx b/sc/source/filter/dif/difimp.cxx
new file mode 100644
index 000000000..fd88cdf4c
--- /dev/null
+++ b/sc/source/filter/dif/difimp.cxx
@@ -0,0 +1,674 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/numformat.hxx>
+#include <tools/stream.hxx>
+#include <osl/diagnose.h>
+#include <dif.hxx>
+#include <docpool.hxx>
+#include <document.hxx>
+#include <fprogressbar.hxx>
+#include <ftools.hxx>
+#include <patattr.hxx>
+#include <scerrors.hxx>
+#include <scitems.hxx>
+#include <stringutil.hxx>
+#include <table.hxx>
+#include <memory>
+
+const std::u16string_view pKeyTABLE = u"TABLE";
+const std::u16string_view pKeyVECTORS = u"VECTORS";
+const std::u16string_view pKeyTUPLES = u"TUPLES";
+const std::u16string_view pKeyDATA = u"DATA";
+const std::u16string_view pKeyBOT = u"BOT";
+const std::u16string_view pKeyEOD = u"EOD";
+
+ErrCode ScFormatFilterPluginImpl::ScImportDif(SvStream& rIn, ScDocument* pDoc, const ScAddress& rInsPos,
+ const rtl_TextEncoding eVon )
+{
+ DifParser aDifParser( rIn, *pDoc, eVon );
+
+ SCTAB nBaseTab = rInsPos.Tab();
+
+ TOPIC eTopic = T_UNKNOWN;
+ bool bSyntErrWarn = false;
+ bool bOverflowWarn = false;
+
+ OUStringBuffer& rData = aDifParser.m_aData;
+
+ rIn.Seek( 0 );
+
+ ScfStreamProgressBar aPrgrsBar( rIn, pDoc->GetDocumentShell() );
+
+ while( eTopic != T_DATA && eTopic != T_END )
+ {
+ eTopic = aDifParser.GetNextTopic();
+
+ aPrgrsBar.Progress();
+
+ const bool bData = !rData.isEmpty();
+
+ switch( eTopic )
+ {
+ case T_TABLE:
+ {
+ if( aDifParser.nVector != 0 || aDifParser.nVal != 1 )
+ bSyntErrWarn = true;
+ if( bData )
+ pDoc->RenameTab(nBaseTab, rData.toString());
+ }
+ break;
+ case T_VECTORS:
+ {
+ if( aDifParser.nVector != 0 )
+ bSyntErrWarn = true;
+ }
+ break;
+ case T_TUPLES:
+ {
+ if( aDifParser.nVector != 0 )
+ bSyntErrWarn = true;
+ }
+ break;
+ case T_DATA:
+ {
+ if( aDifParser.nVector != 0 || aDifParser.nVal != 0 )
+ bSyntErrWarn = true;
+ }
+ break;
+ case T_LABEL:
+ case T_COMMENT:
+ case T_SIZE:
+ case T_PERIODICITY:
+ case T_MAJORSTART:
+ case T_MINORSTART:
+ case T_TRUELENGTH:
+ case T_UINITS:
+ case T_DISPLAYUNITS:
+ case T_END:
+ case T_UNKNOWN:
+ break;
+ default:
+ OSL_FAIL( "ScImportDif - missing enum" );
+ }
+
+ }
+
+ if( eTopic == T_DATA )
+ { // data starts here
+ SCCOL nBaseCol = rInsPos.Col();
+
+ SCCOL nColCnt = SCCOL_MAX;
+ SCROW nRowCnt = rInsPos.Row();
+ DifAttrCache aAttrCache;
+
+ DATASET eCurrent = D_UNKNOWN;
+
+ ScSetStringParam aStrParam; // used to set string value without number detection.
+ aStrParam.setTextInput();
+
+ while( eCurrent != D_EOD )
+ {
+ eCurrent = aDifParser.GetNextDataset();
+
+ aPrgrsBar.Progress();
+ ScAddress aPos(nColCnt, nRowCnt, nBaseTab);
+ const OUString aData = rData.makeStringAndClear();
+
+ switch( eCurrent )
+ {
+ case D_BOT:
+ if( nColCnt < SCCOL_MAX )
+ nRowCnt++;
+ nColCnt = nBaseCol;
+ break;
+ case D_EOD:
+ break;
+ case D_NUMERIC: // Number cell
+ if( nColCnt == SCCOL_MAX )
+ nColCnt = nBaseCol;
+
+ if( pDoc->ValidCol(nColCnt) && pDoc->ValidRow(nRowCnt) )
+ {
+ pDoc->EnsureTable(nBaseTab);
+
+ if( DifParser::IsV( aData.getStr() ) )
+ {
+ pDoc->SetValue(aPos, aDifParser.fVal);
+ aAttrCache.SetNumFormat( pDoc, nColCnt, nRowCnt,
+ aDifParser.nNumFormat );
+ }
+ else if( aData == "TRUE" || aData == "FALSE" )
+ {
+ pDoc->SetValue(aPos, aDifParser.fVal);
+ aAttrCache.SetNumFormat( pDoc, nColCnt, nRowCnt,
+ aDifParser.nNumFormat );
+ }
+ else if( aData == "NA" || aData == "ERROR" )
+ {
+ pDoc->SetString(aPos, aData, &aStrParam);
+ }
+ else
+ {
+ OUString aTmp = "#IND:" + aData + "?";
+ pDoc->SetString(aPos, aTmp, &aStrParam);
+ }
+ }
+ else
+ bOverflowWarn = true;
+
+ nColCnt++;
+ break;
+ case D_STRING: // Text cell
+ if( nColCnt == SCCOL_MAX )
+ nColCnt = nBaseCol;
+
+ if( pDoc->ValidCol(nColCnt) && pDoc->ValidRow(nRowCnt) )
+ {
+ if (!aData.isEmpty())
+ {
+ pDoc->EnsureTable(nBaseTab);
+ pDoc->SetTextCell(aPos, aData);
+ }
+ }
+ else
+ bOverflowWarn = true;
+
+ nColCnt++;
+ break;
+ case D_UNKNOWN:
+ break;
+ case D_SYNT_ERROR:
+ break;
+ default:
+ OSL_FAIL( "ScImportDif - missing enum" );
+ }
+ }
+
+ aAttrCache.Apply( *pDoc, nBaseTab );
+ }
+ else
+ return SCERR_IMPORT_FORMAT;
+
+ if( bSyntErrWarn )
+
+ // FIXME: Add proper warning!
+ return SCWARN_IMPORT_RANGE_OVERFLOW;
+
+ else if( bOverflowWarn )
+ return SCWARN_IMPORT_RANGE_OVERFLOW;
+ else
+ return ERRCODE_NONE;
+}
+
+DifParser::DifParser( SvStream& rNewIn, const ScDocument& rDoc, rtl_TextEncoding eCharSet )
+ : fVal(0.0)
+ , nVector(0)
+ , nVal(0)
+ , nNumFormat(0)
+ , pNumFormatter(rDoc.GetFormatTable())
+ , rIn(rNewIn)
+{
+ if ( rIn.GetStreamCharSet() != eCharSet )
+ {
+ OSL_FAIL( "CharSet passed overrides and modifies StreamCharSet" );
+ rIn.SetStreamCharSet( eCharSet );
+ }
+ rIn.StartReadingUnicodeText( eCharSet );
+}
+
+TOPIC DifParser::GetNextTopic()
+{
+ enum STATE { S_VectorVal, S_Data, S_END, S_START, S_UNKNOWN, S_ERROR_L2 };
+
+ static const std::u16string_view ppKeys[] =
+ {
+ pKeyTABLE, // 0
+ pKeyVECTORS,
+ pKeyTUPLES,
+ pKeyDATA,
+ u"LABEL",
+ u"COMMENT", // 5
+ u"SIZE",
+ u"PERIODICITY",
+ u"MAJORSTART",
+ u"MINORSTART",
+ u"TRUELENGTH", // 10
+ u"UINITS",
+ u"DISPLAYUNITS",
+ u"" // 13
+ };
+
+ static const TOPIC pTopics[] =
+ {
+ T_TABLE, // 0
+ T_VECTORS,
+ T_TUPLES,
+ T_DATA,
+ T_LABEL,
+ T_COMMENT, // 5
+ T_SIZE,
+ T_PERIODICITY,
+ T_MAJORSTART,
+ T_MINORSTART,
+ T_TRUELENGTH, // 10
+ T_UINITS,
+ T_DISPLAYUNITS,
+ T_UNKNOWN // 13
+ };
+
+ STATE eS = S_START;
+ OUString aLine;
+
+ nVector = 0;
+ nVal = 0;
+ TOPIC eRet = T_UNKNOWN;
+
+ while( eS != S_END )
+ {
+ if( !ReadNextLine( aLine ) )
+ {
+ eS = S_END;
+ eRet = T_END;
+ }
+
+ switch( eS )
+ {
+ case S_START:
+ {
+ const std::u16string_view* pRef;
+ sal_uInt16 nCnt = 0;
+ bool bSearch = true;
+
+ pRef = &ppKeys[ nCnt ];
+
+ while( bSearch )
+ {
+ if( aLine == *pRef )
+ {
+ eRet = pTopics[ nCnt ];
+ bSearch = false;
+ }
+ else
+ {
+ nCnt++;
+ pRef = &ppKeys[ nCnt ];
+ if( pRef->empty() )
+ bSearch = false;
+ }
+ }
+
+ if( !pRef->empty() )
+ eS = S_VectorVal;
+ else
+ eS = S_UNKNOWN;
+ }
+ break;
+ case S_VectorVal:
+ {
+ const sal_Unicode* pCur = aLine.getStr();
+
+ pCur = ScanIntVal( pCur, nVector );
+
+ if( pCur && *pCur == ',' )
+ {
+ pCur++;
+ ScanIntVal( pCur, nVal );
+ eS = S_Data;
+ }
+ else
+ eS = S_ERROR_L2;
+ }
+ break;
+ case S_Data:
+ OSL_ENSURE( aLine.getLength() >= 2,
+ "+GetNextTopic(): <String> is too short!" );
+ if( aLine.getLength() > 2 )
+ m_aData.append(aLine.subView(1, aLine.getLength() - 2));
+ else
+ m_aData.truncate();
+ eS = S_END;
+ break;
+ case S_END:
+ OSL_FAIL( "DifParser::GetNextTopic - unexpected state" );
+ break;
+ case S_UNKNOWN:
+ // skip 2 lines
+ ReadNextLine( aLine );
+ [[fallthrough]];
+ case S_ERROR_L2: // error happened in line 2
+ // skip 1 line
+ ReadNextLine( aLine );
+ eS = S_END;
+ break;
+ default:
+ OSL_FAIL( "DifParser::GetNextTopic - missing enum" );
+ }
+ }
+
+ return eRet;
+}
+
+static void lcl_DeEscapeQuotesDif(OUStringBuffer& rString)
+{
+ // Special handling for DIF import: Escaped (duplicated) quotes are resolved.
+ // Single quote characters are left in place because older versions didn't
+ // escape quotes in strings (and Excel doesn't when using the clipboard).
+ // The quotes around the string are removed before this function is called.
+
+ rString = rString.makeStringAndClear().replaceAll("\"\"", "\"");
+}
+
+// Determine if passed in string is numeric data and set fVal/nNumFormat if so
+DATASET DifParser::GetNumberDataset( const sal_Unicode* pPossibleNumericData )
+{
+ DATASET eRet = D_SYNT_ERROR;
+
+ OSL_ENSURE( pNumFormatter, "-DifParser::GetNumberDataset(): No Formatter, more fun!" );
+ OUString aTestVal( pPossibleNumericData );
+ sal_uInt32 nFormat = 0;
+ double fTmpVal;
+ if( pNumFormatter->IsNumberFormat( aTestVal, nFormat, fTmpVal ) )
+ {
+ fVal = fTmpVal;
+ nNumFormat = nFormat;
+ eRet = D_NUMERIC;
+ }
+ else
+ eRet = D_SYNT_ERROR;
+
+ return eRet;
+}
+
+bool DifParser::ReadNextLine( OUString& rStr )
+{
+ if( aLookAheadLine.isEmpty() )
+ {
+ return rIn.ReadUniOrByteStringLine( rStr, rIn.GetStreamCharSet() );
+ }
+ else
+ {
+ rStr = aLookAheadLine;
+ aLookAheadLine.clear();
+ return true;
+ }
+}
+
+// Look ahead in the stream to determine if the next line is the first line of
+// a valid data record structure
+bool DifParser::LookAhead()
+{
+ const sal_Unicode* pCurrentBuffer;
+ bool bValidStructure = false;
+
+ OSL_ENSURE( aLookAheadLine.isEmpty(), "*DifParser::LookAhead(): LookAhead called twice in a row" );
+ rIn.ReadUniOrByteStringLine( aLookAheadLine, rIn.GetStreamCharSet() );
+
+ pCurrentBuffer = aLookAheadLine.getStr();
+
+ switch( *pCurrentBuffer )
+ {
+ case '-': // Special Datatype
+ pCurrentBuffer++;
+
+ if( Is1_0( pCurrentBuffer ) )
+ {
+ bValidStructure = true;
+ }
+ break;
+ case '0': // Numeric Data
+ pCurrentBuffer++;
+ if( *pCurrentBuffer == ',' )
+ {
+ pCurrentBuffer++;
+ bValidStructure = ( GetNumberDataset(pCurrentBuffer) != D_SYNT_ERROR );
+ }
+ break;
+ case '1': // String Data
+ if( Is1_0( aLookAheadLine.getStr() ) )
+ {
+ bValidStructure = true;
+ }
+ break;
+ }
+ return bValidStructure;
+}
+
+DATASET DifParser::GetNextDataset()
+{
+ DATASET eRet = D_UNKNOWN;
+ OUString aLine;
+ const sal_Unicode* pCurrentBuffer;
+
+ ReadNextLine( aLine );
+
+ pCurrentBuffer = aLine.getStr();
+
+ switch( *pCurrentBuffer )
+ {
+ case '-': // Special Datatype
+ pCurrentBuffer++;
+
+ if( Is1_0( pCurrentBuffer ) )
+ {
+ ReadNextLine( aLine );
+ if( IsBOT( aLine.getStr() ) )
+ eRet = D_BOT;
+ else if( IsEOD( aLine.getStr() ) )
+ eRet = D_EOD;
+ }
+ break;
+ case '0': // Numeric Data
+ pCurrentBuffer++; // value in fVal, 2. line in m_aData
+ if( *pCurrentBuffer == ',' )
+ {
+ pCurrentBuffer++;
+ eRet = GetNumberDataset(pCurrentBuffer);
+ OUString aTmpLine;
+ ReadNextLine( aTmpLine );
+ if ( eRet == D_SYNT_ERROR )
+ { // for broken records write "#ERR: data" to cell
+ m_aData = OUString::Concat("#ERR: ") + pCurrentBuffer + " (" + aTmpLine + ")";
+ eRet = D_STRING;
+ }
+ else
+ {
+ m_aData = aTmpLine;
+ }
+ }
+ break;
+ case '1': // String Data
+ if( Is1_0( aLine.getStr() ) )
+ {
+ ReadNextLine( aLine );
+ sal_Int32 nLineLength = aLine.getLength();
+ const sal_Unicode* pLine = aLine.getStr();
+
+ if( nLineLength >= 1 && *pLine == '"' )
+ {
+ // Quotes are not always escaped (duplicated), see lcl_DeEscapeQuotesDif
+ // A look ahead into the next line is needed in order to deal with
+ // multiline strings containing quotes
+ if( LookAhead() )
+ {
+ // Single line string
+ if( nLineLength >= 2 && pLine[nLineLength - 1] == '"' )
+ {
+ m_aData = aLine.subView( 1, nLineLength - 2 );
+ lcl_DeEscapeQuotesDif(m_aData);
+ eRet = D_STRING;
+ }
+ }
+ else
+ {
+ // Multiline string
+ m_aData = aLine.subView( 1 );
+ bool bContinue = true;
+ while ( bContinue )
+ {
+ m_aData.append("\n");
+ bContinue = !rIn.eof() && ReadNextLine( aLine );
+ if( bContinue )
+ {
+ nLineLength = aLine.getLength();
+ if( nLineLength >= 1 )
+ {
+ pLine = aLine.getStr();
+ bContinue = !LookAhead();
+ if( bContinue )
+ {
+ m_aData.append(aLine);
+ }
+ else if( pLine[nLineLength - 1] == '"' )
+ {
+ m_aData.append(aLine.subView(0, nLineLength -1));
+ lcl_DeEscapeQuotesDif(m_aData);
+ eRet = D_STRING;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ if( eRet == D_UNKNOWN )
+ ReadNextLine( aLine );
+
+ if( rIn.eof() )
+ eRet = D_EOD;
+
+ return eRet;
+}
+
+const sal_Unicode* DifParser::ScanIntVal( const sal_Unicode* pStart, sal_uInt32& rRet )
+{
+ // eat leading whitespace, not specified, but seen in the wild
+ while (*pStart == ' ' || *pStart == '\t')
+ ++pStart;
+
+ sal_Unicode cCurrent = *pStart;
+
+ if( IsNumber( cCurrent ) )
+ rRet = static_cast<sal_uInt32>( cCurrent - '0' );
+ else
+ return nullptr;
+
+ pStart++;
+ cCurrent = *pStart;
+
+ while( IsNumber( cCurrent ) && rRet < ( 0xFFFFFFFF / 10 ) )
+ {
+ rRet *= 10;
+ rRet += static_cast<sal_uInt32>( cCurrent - '0' );
+
+ pStart++;
+ cCurrent = *pStart;
+ }
+
+ return pStart;
+}
+
+DifColumn::DifColumn ()
+ : mpCurrent(nullptr)
+{
+}
+
+void DifColumn::SetNumFormat( const ScDocument* pDoc, SCROW nRow, const sal_uInt32 nNumFormat )
+{
+ OSL_ENSURE( pDoc->ValidRow(nRow), "*DifColumn::SetNumFormat(): Row too big!" );
+
+ if( nNumFormat > 0 )
+ {
+ if(mpCurrent)
+ {
+ OSL_ENSURE( nRow > 0,
+ "*DifColumn::SetNumFormat(): more cannot be zero!" );
+ OSL_ENSURE( nRow > mpCurrent->nEnd,
+ "*DifColumn::SetNumFormat(): start from scratch?" );
+
+ if( mpCurrent->nNumFormat == nNumFormat && mpCurrent->nEnd == nRow - 1 )
+ mpCurrent->nEnd = nRow;
+ else
+ NewEntry( nRow, nNumFormat );
+ }
+ else
+ NewEntry(nRow,nNumFormat );
+ }
+ else
+ mpCurrent = nullptr;
+}
+
+void DifColumn::NewEntry( const SCROW nPos, const sal_uInt32 nNumFormat )
+{
+ maEntries.emplace_back();
+ mpCurrent = &maEntries.back();
+ mpCurrent->nStart = mpCurrent->nEnd = nPos;
+ mpCurrent->nNumFormat = nNumFormat;
+
+}
+
+void DifColumn::Apply( ScDocument& rDoc, const SCCOL nCol, const SCTAB nTab )
+{
+ ScPatternAttr aAttr( rDoc.GetPool() );
+ SfxItemSet &rItemSet = aAttr.GetItemSet();
+
+ for (const auto& rEntry : maEntries)
+ {
+ OSL_ENSURE( rEntry.nNumFormat > 0,
+ "+DifColumn::Apply(): Number format must not be 0!" );
+
+ rItemSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, rEntry.nNumFormat ) );
+
+ rDoc.ApplyPatternAreaTab( nCol, rEntry.nStart, nCol, rEntry.nEnd, nTab, aAttr );
+
+ rItemSet.ClearItem();
+ }
+}
+
+DifAttrCache::DifAttrCache()
+{
+}
+
+DifAttrCache::~DifAttrCache()
+{
+}
+
+void DifAttrCache::SetNumFormat( const ScDocument* pDoc, const SCCOL nCol, const SCROW nRow, const sal_uInt32 nNumFormat )
+{
+ OSL_ENSURE( pDoc->ValidCol(nCol), "-DifAttrCache::SetNumFormat(): Col too big!" );
+
+ if( !maColMap.count(nCol) )
+ maColMap[ nCol ].reset( new DifColumn );
+
+ maColMap[ nCol ]->SetNumFormat( pDoc, nRow, nNumFormat );
+}
+
+void DifAttrCache::Apply( ScDocument& rDoc, SCTAB nTab )
+{
+ for( SCCOL nCol : rDoc.GetWritableColumnsRange(nTab, 0, rDoc.MaxCol()) )
+ {
+ if( maColMap.count(nCol) )
+ maColMap[ nCol ]->Apply( rDoc, nCol, nTab );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/colrowst.cxx b/sc/source/filter/excel/colrowst.cxx
new file mode 100644
index 000000000..e194b7309
--- /dev/null
+++ b/sc/source/filter/excel/colrowst.cxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <colrowst.hxx>
+
+#include <document.hxx>
+#include <ftools.hxx>
+#include <xltable.hxx>
+#include <xistyle.hxx>
+#include <excimp8.hxx>
+#include <table.hxx>
+
+XclImpColRowSettings::XclImpColRowSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maColWidths(0, rRoot.GetDoc().GetSheetLimits().GetMaxColCount(), 0),
+ maColFlags(0, rRoot.GetDoc().GetSheetLimits().GetMaxColCount(), ExcColRowFlags::NONE),
+ maRowHeights(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), 0),
+ maRowFlags(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), ExcColRowFlags::NONE),
+ maHiddenRows(0, rRoot.GetDoc().GetSheetLimits().GetMaxRowCount(), false),
+ mnLastScRow( -1 ),
+ mnDefWidth( STD_COL_WIDTH ),
+ mnDefHeight( ScGlobal::nStdRowHeight ),
+ mnDefRowFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mbHasStdWidthRec( false ),
+ mbHasDefHeight( false ),
+ mbDirty( true )
+{
+}
+
+XclImpColRowSettings::~XclImpColRowSettings()
+{
+}
+
+void XclImpColRowSettings::SetDefWidth( sal_uInt16 nDefWidth, bool bStdWidthRec )
+{
+ if( bStdWidthRec )
+ {
+ // STANDARDWIDTH record overrides DEFCOLWIDTH record
+ mnDefWidth = nDefWidth;
+ mbHasStdWidthRec = true;
+ }
+ else if( !mbHasStdWidthRec )
+ {
+ // use DEFCOLWIDTH record only, if no STANDARDWIDTH record exists
+ mnDefWidth = nDefWidth;
+ }
+}
+
+void XclImpColRowSettings::SetWidthRange( SCCOL nCol1, SCCOL nCol2, sal_uInt16 nWidth )
+{
+ ScDocument& rDoc = GetDoc();
+ nCol2 = ::std::min( nCol2, rDoc.MaxCol() );
+ if (nCol2 == 256)
+ // In BIFF8, the column range is 0-255, and the use of 256 probably
+ // means the range should extend to the max column if the loading app
+ // support columns beyond 255.
+ nCol2 = rDoc.MaxCol();
+
+ nCol1 = ::std::min( nCol1, nCol2 );
+ maColWidths.insert_back(nCol1, nCol2+1, nWidth);
+
+ // We need to apply flag values individually since all flag values are aggregated for each column.
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ ApplyColFlag(nCol, ExcColRowFlags::Used);
+}
+
+void XclImpColRowSettings::HideCol( SCCOL nCol )
+{
+ if (!GetDoc().ValidCol(nCol))
+ return;
+
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+}
+
+void XclImpColRowSettings::HideColRange( SCCOL nCol1, SCCOL nCol2 )
+{
+ nCol2 = ::std::min( nCol2, GetDoc().MaxCol() );
+ nCol1 = ::std::min( nCol1, nCol2 );
+
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+}
+
+void XclImpColRowSettings::SetDefHeight( sal_uInt16 nDefHeight, sal_uInt16 nFlags )
+{
+ mnDefHeight = nDefHeight;
+ mnDefRowFlags = nFlags;
+ if( mnDefHeight == 0 )
+ {
+ mnDefHeight = ScGlobal::nStdRowHeight;
+ ::set_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN );
+ }
+ mbHasDefHeight = true;
+}
+
+void XclImpColRowSettings::SetHeight( SCROW nScRow, sal_uInt16 nHeight )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ sal_uInt16 nRawHeight = nHeight & EXC_ROW_HEIGHTMASK;
+ bool bDefHeight = ::get_flag( nHeight, EXC_ROW_FLAGDEFHEIGHT ) || (nRawHeight == 0);
+ maRowHeights.insert_back(nScRow, nScRow+1, nRawHeight);
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ ::set_flag(nFlagVal, ExcColRowFlags::Used);
+ ::set_flag(nFlagVal, ExcColRowFlags::Default, bDefHeight);
+
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+
+ if (nScRow > mnLastScRow)
+ mnLastScRow = nScRow;
+}
+
+void XclImpColRowSettings::SetRowSettings( SCROW nScRow, sal_uInt16 nHeight, sal_uInt16 nFlags )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ SetHeight(nScRow, nHeight);
+
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ if (::get_flag(nFlags, EXC_ROW_UNSYNCED))
+ ::set_flag(nFlagVal, ExcColRowFlags::Man);
+
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+
+ if (::get_flag(nFlags, EXC_ROW_HIDDEN))
+ maHiddenRows.insert_back(nScRow, nScRow+1, true);
+}
+
+void XclImpColRowSettings::SetManualRowHeight( SCROW nScRow )
+{
+ if (!GetDoc().ValidRow(nScRow))
+ return;
+
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maRowFlags.search(nScRow, nFlagVal).second)
+ return;
+
+ nFlagVal |= ExcColRowFlags::Man;
+ maRowFlags.insert_back(nScRow, nScRow+1, nFlagVal);
+}
+
+void XclImpColRowSettings::SetDefaultXF( SCCOL nCol1, SCCOL nCol2, sal_uInt16 nXFIndex )
+{
+ /* assign the default column formatting here to ensure that
+ explicit cell formatting is not overwritten. */
+ OSL_ENSURE( (nCol1 <= nCol2) && GetDoc().ValidCol( nCol2 ), "XclImpColRowSettings::SetDefaultXF - invalid column index" );
+ nCol2 = ::std::min( nCol2, GetDoc().MaxCol() );
+ nCol1 = ::std::min( nCol1, nCol2 );
+ XclImpXFRangeBuffer& rXFRangeBuffer = GetXFRangeBuffer();
+ for( SCCOL nCol = nCol1; nCol <= nCol2; ++nCol )
+ rXFRangeBuffer.SetColumnDefXF( nCol, nXFIndex );
+}
+
+void XclImpColRowSettings::Convert( SCTAB nScTab )
+{
+ if( !mbDirty )
+ return;
+
+ ScDocument& rDoc = GetDoc();
+
+ // column widths ----------------------------------------------------------
+
+ maColWidths.build_tree();
+ for (SCCOL nCol = 0; nCol <= rDoc.MaxCol(); ++nCol)
+ {
+ sal_uInt16 nWidth = mnDefWidth;
+ if (GetColFlag(nCol, ExcColRowFlags::Used))
+ {
+ sal_uInt16 nTmp;
+ if (maColWidths.search_tree(nCol, nTmp).second)
+ nWidth = nTmp;
+ }
+
+ /* Hidden columns: remember hidden state, but do not set hidden state
+ in document here. Needed for #i11776#, no HIDDEN flags in the
+ document, until filters and outlines are inserted. */
+ if( nWidth == 0 )
+ {
+ ApplyColFlag(nCol, ExcColRowFlags::Hidden);
+ nWidth = mnDefWidth;
+ }
+ rDoc.SetColWidthOnly( nCol, nScTab, nWidth );
+ }
+
+ // row heights ------------------------------------------------------------
+
+ // #i54252# set default row height
+ rDoc.SetRowHeightOnly( 0, rDoc.MaxRow(), nScTab, mnDefHeight );
+ if( ::get_flag( mnDefRowFlags, EXC_DEFROW_UNSYNCED ) )
+ // first access to row flags, do not ask for old flags
+ rDoc.SetRowFlags( 0, rDoc.MaxRow(), nScTab, CRFlags::ManualSize );
+
+ maRowHeights.build_tree();
+ if (!maRowHeights.is_tree_valid())
+ return;
+
+ SCROW nPrevRow = -1;
+ ExcColRowFlags nPrevFlags = ExcColRowFlags::NONE;
+ for (const auto& [nRow, nFlags] : maRowFlags)
+ {
+ if (nPrevRow >= 0)
+ {
+ sal_uInt16 nHeight = 0;
+
+ if (nPrevFlags & ExcColRowFlags::Used)
+ {
+ if (nPrevFlags & ExcColRowFlags::Default)
+ {
+ nHeight = mnDefHeight;
+ rDoc.SetRowHeightOnly(nPrevRow, nRow-1, nScTab, nHeight);
+ }
+ else
+ {
+ for (SCROW i = nPrevRow; i <= nRow - 1; ++i)
+ {
+ SCROW nLast;
+ if (!maRowHeights.search_tree(i, nHeight, nullptr, &nLast).second)
+ {
+ // search failed for some reason
+ return;
+ }
+
+ if (nLast > nRow)
+ nLast = nRow;
+
+ rDoc.SetRowHeightOnly(i, nLast-1, nScTab, nHeight);
+ i = nLast-1;
+ }
+ }
+
+ if (nPrevFlags & ExcColRowFlags::Man)
+ rDoc.SetManualHeight(nPrevRow, nRow-1, nScTab, true);
+ }
+ else
+ {
+ nHeight = mnDefHeight;
+ rDoc.SetRowHeightOnly(nPrevRow, nRow-1, nScTab, nHeight);
+ }
+ }
+
+ nPrevRow = nRow;
+ nPrevFlags = nFlags;
+ }
+
+ mbDirty = false;
+}
+
+void XclImpColRowSettings::ConvertHiddenFlags( SCTAB nScTab )
+{
+ ScDocument& rDoc = GetDoc();
+
+ // hide the columns
+ for( SCCOL nCol : rDoc.GetColumnsRange(nScTab, 0, rDoc.MaxCol()) )
+ if (GetColFlag(nCol, ExcColRowFlags::Hidden))
+ rDoc.ShowCol( nCol, nScTab, false );
+
+ // #i38093# rows hidden by filter need extra flag
+ SCROW nFirstFilterScRow = SCROW_MAX;
+ SCROW nLastFilterScRow = SCROW_MAX;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ const XclImpAutoFilterData* pFilter = GetFilterManager().GetByTab( nScTab );
+ // #i70026# use IsFiltered() to set the CRFlags::Filtered flag for active filters only
+ if( pFilter && pFilter->IsActive() && pFilter->IsFiltered() )
+ {
+ nFirstFilterScRow = pFilter->StartRow();
+ nLastFilterScRow = pFilter->EndRow();
+ }
+ }
+
+ // In case the excel row limit is lower than calc's, use the visibility of
+ // the last row and extend it to calc's last row.
+ SCROW nLastXLRow = GetRoot().GetXclMaxPos().Row();
+ if (nLastXLRow < rDoc.MaxRow())
+ {
+ bool bHidden = false;
+ if (!maHiddenRows.search(nLastXLRow, bHidden).second)
+ return;
+
+ maHiddenRows.insert_back(nLastXLRow, GetDoc().GetSheetLimits().GetMaxRowCount(), bHidden);
+ }
+
+ SCROW nPrevRow = -1;
+ bool bPrevHidden = false;
+ for (const auto& [nRow, bHidden] : maHiddenRows)
+ {
+ if (nPrevRow >= 0)
+ {
+ if (bPrevHidden)
+ {
+ rDoc.SetRowHidden(nPrevRow, nRow-1, nScTab, true);
+ // #i38093# rows hidden by filter need extra flag
+ if (nFirstFilterScRow <= nPrevRow && nPrevRow <= nLastFilterScRow)
+ {
+ SCROW nLast = ::std::min(nRow-1, nLastFilterScRow);
+ rDoc.SetRowFiltered(nPrevRow, nLast, nScTab, true);
+ }
+ }
+ }
+
+ nPrevRow = nRow;
+ bPrevHidden = bHidden;
+ }
+
+ // #i47438# if default row format is hidden, hide remaining rows
+ if( ::get_flag( mnDefRowFlags, EXC_DEFROW_HIDDEN ) && (mnLastScRow < rDoc.MaxRow()) )
+ rDoc.ShowRows( mnLastScRow + 1, rDoc.MaxRow(), nScTab, false );
+}
+
+void XclImpColRowSettings::ApplyColFlag(SCCOL nCol, ExcColRowFlags nNewVal)
+{
+ // Get the original flag value.
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ std::pair<ColRowFlagsType::const_iterator,bool> r = maColFlags.search(nCol, nFlagVal);
+ if (!r.second)
+ // Search failed.
+ return;
+
+ ::set_flag(nFlagVal, nNewVal);
+
+ // Re-insert the flag value.
+ maColFlags.insert(r.first, nCol, nCol+1, nFlagVal);
+}
+
+bool XclImpColRowSettings::GetColFlag(SCCOL nCol, ExcColRowFlags nMask) const
+{
+ ExcColRowFlags nFlagVal = ExcColRowFlags::NONE;
+ if (!maColFlags.search(nCol, nFlagVal).second)
+ return false;
+ // Search failed.
+
+ return bool(nFlagVal & nMask);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx
new file mode 100644
index 000000000..c01dde329
--- /dev/null
+++ b/sc/source/filter/excel/excdoc.cxx
@@ -0,0 +1,905 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/objsh.hxx>
+#include <rtl/ustring.hxx>
+
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <docoptio.hxx>
+#include <tabprotection.hxx>
+#include <postit.hxx>
+#include <root.hxx>
+
+#include <excdoc.hxx>
+#include <xeextlst.hxx>
+#include <biffhelper.hxx>
+
+#include <xcl97rec.hxx>
+#include <xetable.hxx>
+#include <xelink.hxx>
+#include <xepage.hxx>
+#include <xeview.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xepivot.hxx>
+#include <export/SparklineExt.hxx>
+#include <XclExpChangeTrack.hxx>
+#include <xepivotxml.hxx>
+#include <xedbdata.hxx>
+#include <xlcontent.hxx>
+#include <xlname.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <o3tl/safeint.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <memory>
+
+using namespace oox;
+
+static OUString lcl_GetVbaTabName( SCTAB n )
+{
+ OUString aRet = "__VBA__" + OUString::number( static_cast<sal_uInt16>(n) );
+ return aRet;
+}
+
+static void lcl_AddBookviews( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_bookViews ) );
+ aRecList.AppendNewRecord( new XclExpWindow1( self.GetRoot() ) );
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_bookViews ) );
+}
+
+static void lcl_AddCalcPr( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ ScDocument& rDoc = self.GetDoc();
+
+ aRecList.AppendNewRecord( new XclExpXmlStartSingleElementRecord( XML_calcPr ) );
+ // OOXTODO: calcCompleted, calcId, calcMode, calcOnSave,
+ // concurrentCalc, concurrentManualCount,
+ // forceFullCalc, fullCalcOnLoad, fullPrecision
+ aRecList.AppendNewRecord( new XclCalccount( rDoc ) );
+ aRecList.AppendNewRecord( new XclRefmode( rDoc ) );
+ aRecList.AppendNewRecord( new XclIteration( rDoc ) );
+ aRecList.AppendNewRecord( new XclDelta( rDoc ) );
+ aRecList.AppendNewRecord( new XclExpBoolRecord(oox::xls::BIFF_ID_SAVERECALC, true) );
+ aRecList.AppendNewRecord( new XclExpXmlEndSingleElementRecord() ); // XML_calcPr
+}
+
+static void lcl_AddWorkbookProtection( XclExpRecordList<>& aRecList, const ExcTable& self )
+{
+ aRecList.AppendNewRecord( new XclExpXmlStartSingleElementRecord( XML_workbookProtection ) );
+
+ const ScDocProtection* pProtect = self.GetDoc().GetDocProtection();
+ if (pProtect && pProtect->isProtected())
+ {
+ aRecList.AppendNewRecord( new XclExpWindowProtection(pProtect->isOptionEnabled(ScDocProtection::WINDOWS)) );
+ aRecList.AppendNewRecord( new XclExpProtection(pProtect->isOptionEnabled(ScDocProtection::STRUCTURE)) );
+ aRecList.AppendNewRecord( new XclExpPassHash(pProtect->getPasswordHash(PASSHASH_XL)) );
+ }
+
+ aRecList.AppendNewRecord( new XclExpXmlEndSingleElementRecord() ); // XML_workbookProtection
+}
+
+static void lcl_AddScenariosAndFilters( XclExpRecordList<>& aRecList, const XclExpRoot& rRoot, SCTAB nScTab )
+{
+ // Scenarios
+ aRecList.AppendNewRecord( new ExcEScenarioManager( rRoot, nScTab ) );
+ // filter
+ aRecList.AppendRecord( rRoot.GetFilterManager().CreateRecord( nScTab ) );
+}
+
+ExcTable::ExcTable( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnScTab( SCTAB_GLOBAL ),
+ nExcTab( EXC_NOTAB ),
+ mxNoteList( new XclExpNoteList )
+{
+}
+
+ExcTable::ExcTable( const XclExpRoot& rRoot, SCTAB nScTab ) :
+ XclExpRoot( rRoot ),
+ mnScTab( nScTab ),
+ nExcTab( rRoot.GetTabInfo().GetXclTab( nScTab ) ),
+ mxNoteList( new XclExpNoteList )
+{
+}
+
+ExcTable::~ExcTable()
+{
+}
+
+void ExcTable::Add( XclExpRecordBase* pRec )
+{
+ OSL_ENSURE( pRec, "-ExcTable::Add(): pRec is NULL!" );
+ aRecList.AppendNewRecord( pRec );
+}
+
+void ExcTable::FillAsHeaderBinary( ExcBoundsheetList& rBoundsheetList )
+{
+ InitializeGlobals();
+
+ RootData& rR = GetOldRoot();
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ if ( GetBiff() <= EXC_BIFF5 )
+ Add( new ExcBofW );
+ else
+ Add( new ExcBofW8 );
+
+ sal_uInt16 nExcTabCount = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodenames = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+
+ SfxObjectShell* pShell = GetDocShell();
+ sal_uInt16 nWriteProtHash = pShell ? pShell->GetModifyPasswordHash() : 0;
+ bool bRecommendReadOnly = pShell && pShell->IsLoadReadonly();
+
+ if( (nWriteProtHash > 0) || bRecommendReadOnly )
+ Add( new XclExpEmptyRecord( EXC_ID_WRITEPROT ) );
+
+ // TODO: correct codepage for BIFF5?
+ sal_uInt16 nCodePage = XclTools::GetXclCodePage( (GetBiff() <= EXC_BIFF5) ? RTL_TEXTENCODING_MS_1252 : RTL_TEXTENCODING_UNICODE );
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new XclExpEmptyRecord( EXC_ID_INTERFACEHDR ) );
+ Add( new XclExpUInt16Record( EXC_ID_MMS, 0 ) );
+ Add( new XclExpEmptyRecord( EXC_ID_TOOLBARHDR ) );
+ Add( new XclExpEmptyRecord( EXC_ID_TOOLBAREND ) );
+ Add( new XclExpEmptyRecord( EXC_ID_INTERFACEEND ) );
+ Add( new ExcDummy_00 );
+ }
+ else
+ {
+ if( IsDocumentEncrypted() )
+ Add( new XclExpFileEncryption( GetRoot() ) );
+ Add( new XclExpInterfaceHdr( nCodePage ) );
+ Add( new XclExpUInt16Record( EXC_ID_MMS, 0 ) );
+ Add( new XclExpInterfaceEnd );
+ Add( new XclExpWriteAccess );
+ }
+
+ Add( new XclExpFileSharing( GetRoot(), nWriteProtHash, bRecommendReadOnly ) );
+ Add( new XclExpUInt16Record( EXC_ID_CODEPAGE, nCodePage ) );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpBoolRecord( EXC_ID_DSF, false ) );
+ Add( new XclExpEmptyRecord( EXC_ID_XL9FILE ) );
+ rR.pTabId = new XclExpChTrTabId( std::max( nExcTabCount, nCodenames ) );
+ Add( rR.pTabId );
+ if( HasVbaStorage() )
+ {
+ Add( new XclObproj );
+ const OUString& rCodeName = GetExtDocOptions().GetDocSettings().maGlobCodeName;
+ if( !rCodeName.isEmpty() )
+ Add( new XclCodename( rCodeName ) );
+ }
+ }
+
+ Add( new XclExpUInt16Record( EXC_ID_FNGROUPCOUNT, 14 ) );
+
+ if ( GetBiff() <= EXC_BIFF5 )
+ {
+ // global link table: EXTERNCOUNT, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+ }
+
+ // document protection options
+ lcl_AddWorkbookProtection( aRecList, *this );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpProt4Rev );
+ Add( new XclExpProt4RevPass );
+ }
+
+ lcl_AddBookviews( aRecList, *this );
+
+ Add( new XclExpXmlStartSingleElementRecord( XML_workbookPr ) );
+ if ( GetBiff() == EXC_BIFF8 && GetOutput() != EXC_OUTPUT_BINARY )
+ {
+ Add( new XclExpBoolRecord(0x0040, false, XML_backupFile ) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false, XML_showObjects ) ); // HIDEOBJ
+ }
+
+ if ( GetBiff() == EXC_BIFF8 )
+ {
+ Add( new XclExpBoolRecord(0x0040, false) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false) ); // HIDEOBJ
+ }
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new ExcDummy_040 );
+ Add( new Exc1904( rDoc ) );
+ Add( new ExcDummy_041 );
+ }
+ else
+ {
+ // BIFF8
+ Add( new Exc1904( rDoc ) );
+ Add( new XclExpBoolRecord( 0x000E, !rDoc.GetDocOptions().IsCalcAsShown() ) );
+ Add( new XclExpBoolRecord(0x01B7, false) ); // REFRESHALL
+ Add( new XclExpBoolRecord(0x00DA, false) ); // BOOKBOOL
+ }
+
+ // Formatting: FONT, FORMAT, XF, STYLE, PALETTE
+ aRecList.AppendRecord( CreateRecord( EXC_ID_FONTLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_FORMATLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_XFLIST ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_PALETTE ) );
+
+ SCTAB nC;
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ // Bundlesheet
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+ }
+ else
+ {
+ // Pivot Cache
+ GetPivotTableManager().CreatePivotTables();
+ aRecList.AppendRecord( GetPivotTableManager().CreatePivotCachesRecord() );
+
+ // Change tracking
+ if( rDoc.GetChangeTrack() )
+ {
+ rR.pUserBViewList = new XclExpUserBViewList( *rDoc.GetChangeTrack() );
+ Add( rR.pUserBViewList );
+ }
+
+ // Natural Language Formulas Flag
+ aRecList.AppendNewRecord( new XclExpBoolRecord( EXC_ID_USESELFS, GetDoc().GetDocOptions().IsLookUpColRowNames() ) );
+
+ // Bundlesheet
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ OUString aTmpString;
+ for( SCTAB nAdd = 0; nC < static_cast<SCTAB>(nCodenames) ; nC++, nAdd++ )
+ {
+ aTmpString = lcl_GetVbaTabName( nAdd );
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( aTmpString );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ // COUNTRY - in BIFF8 in workbook globals
+ Add( new XclExpCountry( GetRoot() ) );
+
+ // link table: SUPBOOK, XCT, CRN, EXTERNNAME, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+
+ Add( new XclExpRecalcId );
+
+ // MSODRAWINGGROUP per-document data
+ aRecList.AppendRecord( GetObjectManager().CreateDrawingGroup() );
+ // Shared string table: SST, EXTSST
+ aRecList.AppendRecord( CreateRecord( EXC_ID_SST ) );
+
+ Add( new XclExpBookExt );
+ }
+
+ Add( new ExcEof );
+}
+
+void ExcTable::FillAsHeaderXml( ExcBoundsheetList& rBoundsheetList )
+{
+ InitializeGlobals();
+
+ RootData& rR = GetOldRoot();
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ sal_uInt16 nExcTabCount = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodenames = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+
+ rR.pTabId = new XclExpChTrTabId( std::max( nExcTabCount, nCodenames ) );
+ Add( rR.pTabId );
+
+ Add( new XclExpXmlStartSingleElementRecord( XML_workbookPr ) );
+ Add( new XclExpBoolRecord(0x0040, false, XML_backupFile ) ); // BACKUP
+ Add( new XclExpBoolRecord(0x008D, false, XML_showObjects ) ); // HIDEOBJ
+
+ Add( new Exc1904( rDoc ) );
+ // OOXTODO: The following /workbook/workbookPr attributes are mapped
+ // to various BIFF records that are not currently supported:
+ //
+ // XML_allowRefreshQuery: QSISTAG 802h: fEnableRefresh
+ // XML_autoCompressPictures: COMPRESSPICTURES 89Bh: fAutoCompressPictures
+ // XML_checkCompatibility: COMPAT12 88Ch: fNoCompatChk
+ // XML_codeName: "Calc"
+ // XML_defaultThemeVersion: ???
+ // XML_filterPrivacy: BOOKEXT 863h: fFilterPrivacy
+ // XML_hidePivotFieldList: BOOKBOOL DAh: fHidePivotTableFList
+ // XML_promptedSolutions: BOOKEXT 863h: fBuggedUserAboutSolution
+ // XML_publishItems: NAMEPUBLISH 893h: fPublished
+ // XML_saveExternalLinkValues: BOOKBOOL DAh: fNoSavSupp
+ // XML_showBorderUnselectedTables: BOOKBOOL DAh: fHideBorderUnsels
+ // XML_showInkAnnotation: BOOKEXT 863h: fShowInkAnnotation
+ // XML_showPivotChart: PIVOTCHARTBITS 859h: fGXHide??
+ // XML_updateLinks: BOOKBOOL DAh: grbitUpdateLinks
+ Add( new XclExpXmlEndSingleElementRecord() ); // XML_workbookPr
+
+ // Formatting: FONT, FORMAT, XF, STYLE, PALETTE
+ aRecList.AppendNewRecord( new XclExpXmlStyleSheet( *this ) );
+
+ // Change tracking
+ if( rDoc.GetChangeTrack() )
+ {
+ rR.pUserBViewList = new XclExpUserBViewList( *rDoc.GetChangeTrack() );
+ Add( rR.pUserBViewList );
+ }
+
+ lcl_AddWorkbookProtection( aRecList, *this );
+ lcl_AddBookviews( aRecList, *this );
+
+ // Bundlesheet
+ SCTAB nC;
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_sheets ) );
+ for( nC = 0 ; nC < nScTabCount ; nC++ )
+ if( rTabInfo.IsExportTab( nC ) )
+ {
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( rR, nC );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_sheets ) );
+
+ OUString aTmpString;
+ for( SCTAB nAdd = 0; nC < static_cast<SCTAB>(nCodenames) ; nC++, nAdd++ )
+ {
+ aTmpString = lcl_GetVbaTabName( nAdd );
+ ExcBoundsheetList::RecordRefType xBoundsheet = new ExcBundlesheet8( aTmpString );
+ aRecList.AppendRecord( xBoundsheet );
+ rBoundsheetList.AppendRecord( xBoundsheet );
+ }
+
+ // link table: SUPBOOK, XCT, CRN, EXTERNNAME, EXTERNSHEET, NAME
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+ aRecList.AppendRecord( CreateRecord( EXC_ID_NAME ) );
+
+ lcl_AddCalcPr( aRecList, *this );
+
+ // MSODRAWINGGROUP per-document data
+ aRecList.AppendRecord( GetObjectManager().CreateDrawingGroup() );
+ // Shared string table: SST, EXTSST
+ aRecList.AppendRecord( CreateRecord( EXC_ID_SST ) );
+}
+
+void ExcTable::FillAsTableBinary( SCTAB nCodeNameIdx )
+{
+ InitializeTable( mnScTab );
+
+ RootData& rR = GetOldRoot();
+ XclBiff eBiff = GetBiff();
+ ScDocument& rDoc = GetDoc();
+
+ OSL_ENSURE( (mnScTab >= 0) && (mnScTab <= MAXTAB), "-ExcTable::Table(): mnScTab - no ordinary table!" );
+ OSL_ENSURE( nExcTab <= o3tl::make_unsigned(MAXTAB), "-ExcTable::Table(): nExcTab - no ordinary table!" );
+
+ // create a new OBJ list for this sheet (may be used by notes, autofilter, data validation)
+ if( eBiff == EXC_BIFF8 )
+ GetObjectManager().StartSheet();
+
+ // cell table: DEFROWHEIGHT, DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ mxCellTable = new XclExpCellTable( GetRoot() );
+
+ //export cell notes
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+ for (const auto& rNote : aNotes)
+ {
+ if (rNote.maPos.Tab() != mnScTab)
+ continue;
+
+ mxNoteList->AppendNewRecord(new XclExpNote(GetRoot(), rNote.maPos, rNote.mpNote, u""));
+ }
+
+ // WSBOOL needs data from page settings, create it here, add it later
+ rtl::Reference<XclExpPageSettings> xPageSett = new XclExpPageSettings( GetRoot() );
+ bool bFitToPages = xPageSett->GetPageData().mbFitToPages;
+
+ if( eBiff <= EXC_BIFF5 )
+ {
+ Add( new ExcBof );
+ Add( new ExcDummy_02a );
+ }
+ else
+ {
+ Add( new ExcBof8 );
+ lcl_AddCalcPr( aRecList, *this );
+ }
+
+ // GUTS (count & size of outline icons)
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) );
+ // DEFROWHEIGHT, created by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID2_DEFROWHEIGHT ) );
+
+ // COUNTRY - in BIFF5/7 in every worksheet
+ if( eBiff <= EXC_BIFF5 )
+ Add( new XclExpCountry( GetRoot() ) );
+
+ Add( new XclExpWsbool( bFitToPages ) );
+
+ // page settings (SETUP and various other records)
+ aRecList.AppendRecord( xPageSett );
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnScTab);
+ if (pTabProtect && pTabProtect->isProtected())
+ {
+ Add( new XclExpProtection(true) );
+ Add( new XclExpBoolRecord(oox::xls::BIFF_ID_SCENPROTECT, pTabProtect->isOptionEnabled(ScTableProtection::SCENARIOS)) );
+ if (pTabProtect->isOptionEnabled(ScTableProtection::OBJECTS))
+ Add( new XclExpBoolRecord(oox::xls::BIFF_ID_OBJECTPROTECT, true ));
+ Add( new XclExpPassHash(pTabProtect->getPasswordHash(PASSHASH_XL)) );
+ }
+
+ // local link table: EXTERNCOUNT, EXTERNSHEET
+ if( eBiff <= EXC_BIFF5 )
+ aRecList.AppendRecord( CreateRecord( EXC_ID_EXTERNSHEET ) );
+
+ if ( eBiff == EXC_BIFF8 )
+ lcl_AddScenariosAndFilters( aRecList, GetRoot(), mnScTab );
+
+ // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ aRecList.AppendRecord( mxCellTable );
+
+ // MERGEDCELLS record, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_MERGEDCELLS ) );
+ // label ranges
+ if( eBiff == EXC_BIFF8 )
+ Add( new XclExpLabelranges( GetRoot() ) );
+ // data validation (DVAL and list of DV records), generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) );
+
+ if( eBiff == EXC_BIFF8 )
+ {
+ // all MSODRAWING and OBJ stuff of this sheet goes here
+ aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( mnScTab ) ) );
+ // pivot tables
+ aRecList.AppendRecord( GetPivotTableManager().CreatePivotTablesRecord( mnScTab ) );
+ }
+
+ // list of NOTE records, generated by the cell table
+ aRecList.AppendRecord( mxNoteList );
+
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+
+ if( eBiff == EXC_BIFF8 )
+ {
+ // sheet protection options
+ Add( new XclExpSheetProtectOptions( GetRoot(), mnScTab ) );
+
+ // enhanced protections if there are
+ if (pTabProtect)
+ {
+ const ::std::vector<ScEnhancedProtection>& rProts( pTabProtect->getEnhancedProtection());
+ for (const auto& rProt : rProts)
+ {
+ Add( new XclExpSheetEnhancedProtection( GetRoot(), rProt));
+ }
+ }
+
+ // web queries
+ Add( new XclExpWebQueryBuffer( GetRoot() ) );
+
+ // conditional formats
+ Add( new XclExpCondFormatBuffer( GetRoot(), XclExtLstRef() ) );
+
+ if( HasVbaStorage() )
+ if( nCodeNameIdx < GetExtDocOptions().GetCodeNameCount() )
+ Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) );
+ }
+
+ // list of HLINK records, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_HLINK ) );
+
+ // change tracking
+ if( rR.pUserBViewList )
+ {
+ XclExpUserBViewList::const_iterator iter;
+ for ( iter = rR.pUserBViewList->cbegin(); iter != rR.pUserBViewList->cend(); ++iter)
+ {
+ Add( new XclExpUsersViewBegin( (*iter).GetGUID(), nExcTab ) );
+ Add( new XclExpUsersViewEnd );
+ }
+ }
+
+ // EOF
+ Add( new ExcEof );
+}
+
+void ExcTable::FillAsTableXml()
+{
+ InitializeTable( mnScTab );
+
+ ScDocument& rDoc = GetDoc();
+
+ OSL_ENSURE( (mnScTab >= 0) && (mnScTab <= MAXTAB), "-ExcTable::Table(): mnScTab - no ordinary table!" );
+ OSL_ENSURE( nExcTab <= o3tl::make_unsigned(MAXTAB), "-ExcTable::Table(): nExcTab - no ordinary table!" );
+
+ // create a new OBJ list for this sheet (may be used by notes, autofilter, data validation)
+ GetObjectManager().StartSheet();
+
+ // cell table: DEFROWHEIGHT, DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ mxCellTable = new XclExpCellTable( GetRoot() );
+
+ //export cell notes
+ std::vector<sc::NoteEntry> aNotes;
+ rDoc.GetAllNoteEntries(aNotes);
+ for (const auto& rNote : aNotes)
+ {
+ if (rNote.maPos.Tab() != mnScTab)
+ continue;
+
+ mxNoteList->AppendNewRecord(new XclExpNote(GetRoot(), rNote.maPos, rNote.mpNote, u""));
+ }
+
+ // WSBOOL needs data from page settings, create it here, add it later
+ rtl::Reference<XclExpPageSettings> xPageSett = new XclExpPageSettings( GetRoot() );
+ XclExtLstRef xExtLst = new XclExtLst( GetRoot() );
+ bool bFitToPages = xPageSett->GetPageData().mbFitToPages;
+
+ Color aTabColor = GetRoot().GetDoc().GetTabBgColor(mnScTab);
+ Add(new XclExpXmlSheetPr(bFitToPages, mnScTab, aTabColor, &GetFilterManager()));
+
+ // GUTS (count & size of outline icons)
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) );
+ // DEFROWHEIGHT, created by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID2_DEFROWHEIGHT ) );
+
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID3_DIMENSIONS ) );
+
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+
+ // cell table: DEFCOLWIDTH, COLINFO, DIMENSIONS, ROW, cell records
+ aRecList.AppendRecord( mxCellTable );
+
+ // list of NOTE records, generated by the cell table
+ // not in the worksheet file
+ if( mxNoteList != nullptr && !mxNoteList->IsEmpty() )
+ aRecList.AppendNewRecord( new XclExpComments( mnScTab, *mxNoteList ) );
+
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnScTab);
+ if (pTabProtect && pTabProtect->isProtected())
+ Add( new XclExpSheetProtection(true, mnScTab) );
+
+ lcl_AddScenariosAndFilters( aRecList, GetRoot(), mnScTab );
+
+ // MERGEDCELLS record, generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_MERGEDCELLS ) );
+
+ // conditional formats
+ Add( new XclExpCondFormatBuffer( GetRoot(), xExtLst ) );
+
+ Add(new xcl::exp::SparklineBuffer(GetRoot(), xExtLst));
+
+ // data validation (DVAL and list of DV records), generated by the cell table
+ aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_DVAL ) );
+
+ // list of HLINK records, generated by the cell table
+ XclExpRecordRef xHyperlinks = mxCellTable->CreateRecord( EXC_ID_HLINK );
+ XclExpHyperlinkList* pHyperlinkList = dynamic_cast<XclExpHyperlinkList*>(xHyperlinks.get());
+ if( pHyperlinkList != nullptr && !pHyperlinkList->IsEmpty() )
+ {
+ aRecList.AppendNewRecord( new XclExpXmlStartElementRecord( XML_hyperlinks ) );
+ aRecList.AppendRecord( xHyperlinks );
+ aRecList.AppendNewRecord( new XclExpXmlEndElementRecord( XML_hyperlinks ) );
+ }
+
+ aRecList.AppendRecord( xPageSett );
+
+ // all MSODRAWING and OBJ stuff of this sheet goes here
+ aRecList.AppendRecord( GetObjectManager().ProcessDrawing( GetSdrPage( mnScTab ) ) );
+
+ XclExpImgData* pImgData = xPageSett->getGraphicExport();
+ if (pImgData)
+ aRecList.AppendRecord(pImgData);
+
+ // <tableParts> after <drawing> and before <extLst>
+ aRecList.AppendRecord( GetTablesManager().GetTablesBySheet( mnScTab));
+
+ aRecList.AppendRecord( xExtLst );
+}
+
+void ExcTable::FillAsEmptyTable( SCTAB nCodeNameIdx )
+{
+ InitializeTable( mnScTab );
+
+ if( !(HasVbaStorage() && (nCodeNameIdx < GetExtDocOptions().GetCodeNameCount())) )
+ return;
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ Add( new ExcBof );
+ }
+ else
+ {
+ Add( new ExcBof8 );
+ Add( new XclCodename( GetExtDocOptions().GetCodeName( nCodeNameIdx ) ) );
+ }
+ // sheet view settings: WINDOW2, SCL, PANE, SELECTION
+ aRecList.AppendNewRecord( new XclExpTabViewSettings( GetRoot(), mnScTab ) );
+ Add( new ExcEof );
+}
+
+void ExcTable::Write( XclExpStream& rStrm )
+{
+ SetCurrScTab( mnScTab );
+ if( mxCellTable )
+ mxCellTable->Finalize(true);
+ aRecList.Save( rStrm );
+}
+
+void ExcTable::WriteXml( XclExpXmlStream& rStrm )
+{
+ if (!GetTabInfo().IsExportTab(mnScTab))
+ {
+ // header export.
+ SetCurrScTab(mnScTab);
+ if (mxCellTable)
+ mxCellTable->Finalize(false);
+ aRecList.SaveXml(rStrm);
+
+ return;
+ }
+
+ // worksheet export
+ OUString sSheetName = XclXmlUtils::GetStreamName( "xl/", "worksheets/sheet", mnScTab+1 );
+
+ sax_fastparser::FSHelperPtr pWorksheet = rStrm.GetStreamForPath( sSheetName );
+
+ rStrm.PushStream( pWorksheet );
+
+ pWorksheet->startElement( XML_worksheet,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ FSNS(XML_xmlns, XML_xdr), "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing", // rStrm.getNamespaceURL(OOX_NS(xm)).toUtf8() -> "http://schemas.microsoft.com/office/excel/2006/main",
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)).toUtf8(),
+ FSNS(XML_xmlns, XML_xr2), rStrm.getNamespaceURL(OOX_NS(xr2)).toUtf8(),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)).toUtf8());
+
+ SetCurrScTab( mnScTab );
+ if (mxCellTable)
+ mxCellTable->Finalize(false);
+ aRecList.SaveXml( rStrm );
+
+ XclExpXmlPivotTables* pPT = GetXmlPivotTableManager().GetTablesBySheet(mnScTab);
+ if (pPT)
+ pPT->SaveXml(rStrm);
+
+ rStrm.GetCurrentStream()->endElement( XML_worksheet );
+ rStrm.PopStream();
+}
+
+ExcDocument::ExcDocument( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ aHeader( rRoot )
+{
+}
+
+ExcDocument::~ExcDocument()
+{
+ maTableList.RemoveAllRecords(); // for the following assertion!
+}
+
+void ExcDocument::ReadDoc()
+{
+ InitializeConvert();
+
+ if (GetOutput() == EXC_OUTPUT_BINARY)
+ aHeader.FillAsHeaderBinary(maBoundsheetList);
+ else
+ {
+ aHeader.FillAsHeaderXml(maBoundsheetList);
+ GetXmlPivotTableManager().Initialize();
+ GetTablesManager().Initialize(); // Move outside conditions if we wanted to support BIFF.
+ }
+
+ SCTAB nScTab = 0, nScTabCount = GetTabInfo().GetScTabCount();
+ SCTAB nCodeNameIdx = 0, nCodeNameCount = GetExtDocOptions().GetCodeNameCount();
+
+ for( ; nScTab < nScTabCount; ++nScTab )
+ {
+ if( GetTabInfo().IsExportTab( nScTab ) )
+ {
+ ExcTableList::RecordRefType xTab = new ExcTable( GetRoot(), nScTab );
+ maTableList.AppendRecord( xTab );
+ if (GetOutput() == EXC_OUTPUT_BINARY)
+ xTab->FillAsTableBinary(nCodeNameIdx);
+ else
+ xTab->FillAsTableXml();
+
+ ++nCodeNameIdx;
+ }
+ }
+ for( ; nCodeNameIdx < nCodeNameCount; ++nScTab, ++nCodeNameIdx )
+ {
+ ExcTableList::RecordRefType xTab = new ExcTable( GetRoot(), nScTab );
+ maTableList.AppendRecord( xTab );
+ xTab->FillAsEmptyTable( nCodeNameIdx );
+ }
+
+ if ( GetBiff() == EXC_BIFF8 )
+ {
+ // complete temporary Escher stream
+ GetObjectManager().EndDocument();
+
+ // change tracking
+ if ( GetDoc().GetChangeTrack() )
+ m_xExpChangeTrack.reset(new XclExpChangeTrack( GetRoot() ));
+ }
+}
+
+void ExcDocument::Write( SvStream& rSvStrm )
+{
+ if( !maTableList.IsEmpty() )
+ {
+ InitializeSave();
+
+ XclExpStream aXclStrm( rSvStrm, GetRoot() );
+
+ aHeader.Write( aXclStrm );
+
+ OSL_ENSURE( maTableList.GetSize() == maBoundsheetList.GetSize(),
+ "ExcDocument::Write - different number of sheets and BOUNDSHEET records" );
+
+ for( size_t nTab = 0, nTabCount = maTableList.GetSize(); nTab < nTabCount; ++nTab )
+ {
+ // set current stream position in BOUNDSHEET record
+ ExcBoundsheetRef xBoundsheet = maBoundsheetList.GetRecord( nTab );
+ if( xBoundsheet )
+ xBoundsheet->SetStreamPos( aXclStrm.GetSvStreamPos() );
+ // write the table
+ maTableList.GetRecord( nTab )->Write( aXclStrm );
+ }
+
+ // write the table stream positions into the BOUNDSHEET records
+ for( size_t nBSheet = 0, nBSheetCount = maBoundsheetList.GetSize(); nBSheet < nBSheetCount; ++nBSheet )
+ maBoundsheetList.GetRecord( nBSheet )->UpdateStreamPos( aXclStrm );
+ }
+ if( m_xExpChangeTrack )
+ m_xExpChangeTrack->Write();
+}
+
+void ExcDocument::WriteXml( XclExpXmlStream& rStrm )
+{
+ SfxObjectShell* pDocShell = GetDocShell();
+
+ using namespace ::com::sun::star;
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference<document::XDocumentProperties> xDocProps = xDPS->getDocumentProperties();
+
+ OUString sUserName = GetUserName();
+ sal_uInt32 nWriteProtHash = pDocShell->GetModifyPasswordHash();
+ bool bHasPasswordHash = nWriteProtHash && !sUserName.isEmpty();
+ const uno::Sequence<beans::PropertyValue> aInfo = pDocShell->GetModifyPasswordInfo();
+ OUString sAlgorithm, sSalt, sHash;
+ sal_Int32 nCount = 0;
+ for (const auto& prop : aInfo)
+ {
+ if (prop.Name == "algorithm-name")
+ prop.Value >>= sAlgorithm;
+ else if (prop.Name == "salt")
+ prop.Value >>= sSalt;
+ else if (prop.Name == "iteration-count")
+ prop.Value >>= nCount;
+ else if (prop.Name == "hash")
+ prop.Value >>= sHash;
+ }
+ bool bHasPasswordInfo
+ = sAlgorithm != "PBKDF2" && !sSalt.isEmpty() && !sHash.isEmpty() && !sUserName.isEmpty();
+ rStrm.exportDocumentProperties(xDocProps, pDocShell->IsSecurityOptOpenReadOnly()
+ && !bHasPasswordHash && !bHasPasswordInfo);
+ rStrm.exportCustomFragments();
+
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_workbook,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8() );
+ rWorkbook->singleElement( XML_fileVersion,
+ XML_appName, "Calc"
+ // OOXTODO: XML_codeName
+ // OOXTODO: XML_lastEdited
+ // OOXTODO: XML_lowestEdited
+ // OOXTODO: XML_rupBuild
+ );
+
+ if (bHasPasswordHash)
+ rWorkbook->singleElement(XML_fileSharing,
+ XML_userName, sUserName,
+ XML_reservationPassword, OString::number(nWriteProtHash, 16).getStr());
+ else if (bHasPasswordInfo)
+ rWorkbook->singleElement(XML_fileSharing,
+ XML_userName, sUserName,
+ XML_algorithmName, sAlgorithm.toUtf8().getStr(),
+ XML_hashValue, sHash.toUtf8().getStr(),
+ XML_saltValue, sSalt.toUtf8().getStr(),
+ XML_spinCount, OString::number(nCount).getStr());
+
+ if( !maTableList.IsEmpty() )
+ {
+ InitializeSave();
+
+ aHeader.WriteXml( rStrm );
+
+ for( size_t nTab = 0, nTabCount = maTableList.GetSize(); nTab < nTabCount; ++nTab )
+ {
+ // write the table
+ maTableList.GetRecord( nTab )->WriteXml( rStrm );
+ }
+ }
+
+ if( m_xExpChangeTrack )
+ m_xExpChangeTrack->WriteXml( rStrm );
+
+ XclExpXmlPivotCaches& rCaches = GetXmlPivotTableManager().GetCaches();
+ if (rCaches.HasCaches())
+ rCaches.SaveXml(rStrm);
+
+ const ScCalcConfig& rCalcConfig = GetDoc().GetCalcConfig();
+ formula::FormulaGrammar::AddressConvention eConv = rCalcConfig.meStringRefAddressSyntax;
+
+ // don't save "unspecified" string ref syntax ... query formula grammar
+ // and save that instead
+ if( eConv == formula::FormulaGrammar::CONV_UNSPECIFIED)
+ {
+ eConv = GetDoc().GetAddressConvention();
+ }
+
+ // write if it has been read|imported or explicitly changed
+ // or if ref syntax isn't what would be native for our file format
+ // i.e. ExcelA1 in this case
+ if ( rCalcConfig.mbHasStringRefSyntax ||
+ (eConv != formula::FormulaGrammar::CONV_XL_A1) )
+ {
+ XclExtLstRef xExtLst = new XclExtLst( GetRoot() );
+ xExtLst->AddRecord( new XclExpExtCalcPr( GetRoot(), eConv ) );
+ xExtLst->SaveXml(rStrm);
+ }
+
+ rWorkbook->endElement( XML_workbook );
+ rWorkbook.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excel.cxx b/sc/source/filter/excel/excel.cxx
new file mode 100644
index 000000000..edc60721a
--- /dev/null
+++ b/sc/source/filter/excel/excel.cxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sot/storage.hxx>
+#include <sot/exchange.hxx>
+#include <filter/msfilter/classids.hxx>
+#include <tools/globname.hxx>
+#include <com/sun/star/packages/XPackageEncryption.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <unotools/streamwrap.hxx>
+#include <unotools/defaultencoding.hxx>
+#include <unotools/wincodepage.hxx>
+#include <osl/diagnose.h>
+#include <filter.hxx>
+#include <document.hxx>
+#include <xistream.hxx>
+#include <xltools.hxx>
+#include <docoptio.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <docsh.hxx>
+#include <scerrors.hxx>
+#include <imp_op.hxx>
+#include <excimp8.hxx>
+#include <exp_op.hxx>
+#include <scdll.hxx>
+
+#include <memory>
+
+using namespace css;
+
+static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, const OUString& sPrefix)
+{
+ SvStorageInfoList aElements;
+ pStorage->FillInfoList(&aElements);
+ for (const auto & aElement : aElements)
+ {
+ OUString sStreamFullName = sPrefix.getLength() ? sPrefix + "/" + aElement.GetName() : aElement.GetName();
+ if (aElement.IsStorage())
+ {
+ tools::SvRef<SotStorage> xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL);
+ lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName);
+ }
+ else
+ {
+ // Read stream
+ tools::SvRef<SotStorageStream> rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (rStream.is())
+ {
+ sal_Int32 nStreamSize = rStream->GetSize();
+ uno::Sequence< sal_Int8 > oData;
+ oData.realloc(nStreamSize);
+ sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize);
+ if (nStreamSize == nReadBytes)
+ aStreamsData[sStreamFullName] <<= oData;
+ }
+ }
+ }
+}
+
+static tools::SvRef<SotStorage> lcl_DRMDecrypt(const SfxMedium& rMedium, const tools::SvRef<SotStorage>& rStorage, std::shared_ptr<SvStream>& rNewStorageStrm)
+{
+ tools::SvRef<SotStorage> aNewStorage;
+
+ // We have DRM encrypted storage. We should try to decrypt it first, if we can
+ uno::Sequence< uno::Any > aArguments;
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference< packages::XPackageEncryption > xPackageEncryption(
+ xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.oox.crypto.DRMDataSpace", aArguments, xComponentContext), uno::UNO_QUERY);
+
+ if (!xPackageEncryption.is())
+ {
+ // We do not know how to decrypt this
+ return aNewStorage;
+ }
+
+ comphelper::SequenceAsHashMap aStreamsData;
+ lcl_getListOfStreams(rStorage.get(), aStreamsData, "");
+
+ try {
+ uno::Sequence<beans::NamedValue> aStreams = aStreamsData.getAsConstNamedValueList();
+ if (!xPackageEncryption->readEncryptionInfo(aStreams))
+ {
+ // We failed with decryption
+ return aNewStorage;
+ }
+
+ tools::SvRef<SotStorageStream> rContentStream = rStorage->OpenSotStream("\011DRMContent", StreamMode::READ | StreamMode::SHARE_DENYALL);
+ if (!rContentStream.is())
+ {
+ return aNewStorage;
+ }
+
+ rNewStorageStrm = std::make_shared<SvMemoryStream>();
+
+ uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false));
+ uno::Reference<io::XOutputStream > xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*rNewStorageStrm));
+
+ if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream))
+ {
+ // We failed with decryption
+ return aNewStorage;
+ }
+
+ rNewStorageStrm->Seek(0);
+
+ // Further reading is done from new document
+ aNewStorage = new SotStorage(*rNewStorageStrm);
+
+ // Set the media descriptor data
+ uno::Sequence<beans::NamedValue> aEncryptionData = xPackageEncryption->createEncryptionData("");
+ rMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
+ }
+ catch (const std::exception&)
+ {
+ return aNewStorage;
+ }
+
+ return aNewStorage;
+}
+
+ErrCode ScFormatFilterPluginImpl::ScImportExcel( SfxMedium& rMedium, ScDocument* pDocument, const EXCIMPFORMAT eFormat )
+{
+ // check the passed Calc document
+ OSL_ENSURE( pDocument, "::ScImportExcel - no document" );
+ if( !pDocument ) return SCERR_IMPORT_INTERNAL; // should not happen
+
+ /* Import all BIFF versions regardless on eFormat, needed for import of
+ external cells (file type detection returns Excel4.0). */
+ if( (eFormat != EIF_AUTO) && (eFormat != EIF_BIFF_LE4) && (eFormat != EIF_BIFF5) && (eFormat != EIF_BIFF8) )
+ {
+ OSL_FAIL( "::ScImportExcel - wrong file format specification" );
+ return SCERR_IMPORT_FORMAT;
+ }
+
+ // check the input stream from medium
+ SvStream* pMedStrm = rMedium.GetInStream();
+ OSL_ENSURE( pMedStrm, "::ScImportExcel - medium without input stream" );
+ if( !pMedStrm ) return SCERR_IMPORT_OPEN; // should not happen
+
+ SvStream* pBookStrm = nullptr; // The "Book"/"Workbook" stream containing main data.
+ XclBiff eBiff = EXC_BIFF_UNKNOWN; // The BIFF version of the main stream.
+
+ // try to open an OLE storage
+ tools::SvRef<SotStorage> xRootStrg;
+ tools::SvRef<SotStorageStream> xStrgStrm;
+ std::shared_ptr<SvStream> aNewStorageStrm;
+ if( SotStorage::IsStorageFile( pMedStrm ) )
+ {
+ xRootStrg = new SotStorage( pMedStrm, false );
+ if( xRootStrg->GetError() )
+ xRootStrg = nullptr;
+ }
+
+ // try to open "Book" or "Workbook" stream in OLE storage
+ if( xRootStrg.is() )
+ {
+ // Check if there is DRM encryption in storage
+ tools::SvRef<SotStorageStream> xDRMStrm = ScfTools::OpenStorageStreamRead(xRootStrg, "\011DRMContent");
+ if (xDRMStrm.is())
+ {
+ xRootStrg = lcl_DRMDecrypt(rMedium, xRootStrg, aNewStorageStrm);
+ }
+
+ // try to open the "Book" stream
+ tools::SvRef<SotStorageStream> xBookStrm = ScfTools::OpenStorageStreamRead( xRootStrg, EXC_STREAM_BOOK );
+ XclBiff eBookBiff = xBookStrm.is() ? XclImpStream::DetectBiffVersion( *xBookStrm ) : EXC_BIFF_UNKNOWN;
+
+ // try to open the "Workbook" stream
+ tools::SvRef<SotStorageStream> xWorkbookStrm = ScfTools::OpenStorageStreamRead( xRootStrg, EXC_STREAM_WORKBOOK );
+ XclBiff eWorkbookBiff = xWorkbookStrm.is() ? XclImpStream::DetectBiffVersion( *xWorkbookStrm ) : EXC_BIFF_UNKNOWN;
+
+ // decide which stream to use
+ if( (eWorkbookBiff != EXC_BIFF_UNKNOWN) && ((eBookBiff == EXC_BIFF_UNKNOWN) || (eWorkbookBiff > eBookBiff)) )
+ {
+ /* Only "Workbook" stream exists; or both streams exist,
+ and "Workbook" has higher BIFF version than "Book" stream. */
+ xStrgStrm = xWorkbookStrm;
+ eBiff = eWorkbookBiff;
+ }
+ else if( eBookBiff != EXC_BIFF_UNKNOWN )
+ {
+ /* Only "Book" stream exists; or both streams exist,
+ and "Book" has higher BIFF version than "Workbook" stream. */
+ xStrgStrm = xBookStrm;
+ eBiff = eBookBiff;
+ }
+
+ pBookStrm = xStrgStrm.get();
+ }
+
+ // no "Book" or "Workbook" stream found, try plain input stream from medium (even for BIFF5+)
+ if( !pBookStrm )
+ {
+ eBiff = XclImpStream::DetectBiffVersion( *pMedStrm );
+ if( eBiff != EXC_BIFF_UNKNOWN )
+ pBookStrm = pMedStrm;
+ }
+
+ // try to import the file
+ ErrCode eRet = SCERR_IMPORT_UNKNOWN_BIFF;
+ if( pBookStrm )
+ {
+ pBookStrm->SetBufferSize( 0x8000 ); // still needed?
+
+ XclImpRootData aImpData(
+ eBiff, rMedium, xRootStrg, *pDocument,
+ utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding()));
+ std::unique_ptr< ImportExcel > xFilter;
+ switch( eBiff )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ xFilter.reset( new ImportExcel( aImpData, *pBookStrm ) );
+ break;
+ case EXC_BIFF8:
+ xFilter.reset( new ImportExcel8( aImpData, *pBookStrm ) );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ eRet = xFilter ? xFilter->Read() : SCERR_IMPORT_INTERNAL;
+ }
+
+ return eRet;
+}
+
+static ErrCode lcl_ExportExcelBiff( SfxMedium& rMedium, ScDocument *pDocument,
+ SvStream* pMedStrm, bool bBiff8, rtl_TextEncoding eNach )
+{
+ uno::Reference< packages::XPackageEncryption > xPackageEncryption;
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(rMedium.GetItemSet(), SID_ENCRYPTIONDATA, false);
+ SvStream* pOriginalMediaStrm = pMedStrm;
+ std::shared_ptr<SvStream> pMediaStrm;
+ if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData))
+ {
+ ::comphelper::SequenceAsHashMap aHashData(aEncryptionData);
+ OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString());
+
+ if (sCryptoType.getLength())
+ {
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Sequence<uno::Any> aArguments{
+ uno::Any(beans::NamedValue("Binary", uno::Any(true))) };
+ xPackageEncryption.set(
+ xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), uno::UNO_QUERY);
+
+ if (xPackageEncryption.is())
+ {
+ // We have an encryptor. Export document into memory stream and encrypt it later
+ pMediaStrm = std::make_shared<SvMemoryStream>();
+ pMedStrm = pMediaStrm.get();
+
+ // Temp removal of EncryptionData to avoid password protection triggering
+ rMedium.GetItemSet()->ClearItem(SID_ENCRYPTIONDATA);
+ }
+ }
+ }
+
+ // try to open an OLE storage
+ tools::SvRef<SotStorage> xRootStrg = new SotStorage( pMedStrm, false );
+ if( xRootStrg->GetError() ) return SCERR_IMPORT_OPEN;
+
+ // create BIFF dependent strings
+ OUString aStrmName, aClipName, aClassName;
+ if( bBiff8 )
+ {
+ aStrmName = EXC_STREAM_WORKBOOK;
+ aClipName = "Biff8";
+ aClassName = "Microsoft Excel 97-Tabelle";
+ }
+ else
+ {
+ aStrmName = EXC_STREAM_BOOK;
+ aClipName = "Biff5";
+ aClassName = "Microsoft Excel 5.0-Tabelle";
+ }
+
+ // open the "Book"/"Workbook" stream
+ tools::SvRef<SotStorageStream> xStrgStrm = ScfTools::OpenStorageStreamWrite( xRootStrg, aStrmName );
+ if( !xStrgStrm.is() || xStrgStrm->GetError() ) return SCERR_IMPORT_OPEN;
+
+ xStrgStrm->SetBufferSize( 0x8000 ); // still needed?
+
+ ErrCode eRet = SCERR_IMPORT_UNKNOWN_BIFF;
+ XclExpRootData aExpData( bBiff8 ? EXC_BIFF8 : EXC_BIFF5, rMedium, xRootStrg, *pDocument, eNach );
+ if ( bBiff8 )
+ {
+ ExportBiff8 aFilter( aExpData, *xStrgStrm );
+ eRet = aFilter.Write();
+ }
+ else
+ {
+ ExportBiff5 aFilter( aExpData, *xStrgStrm );
+ eRet = aFilter.Write();
+ }
+
+ if( eRet == SCWARN_IMPORT_RANGE_OVERFLOW )
+ eRet = SCWARN_EXPORT_MAXROW;
+
+ SvGlobalName aGlobName(MSO_EXCEL5_CLASSID);
+ SotClipboardFormatId nClip = SotExchange::RegisterFormatName( aClipName );
+ xRootStrg->SetClass( aGlobName, nClip, aClassName );
+
+ xStrgStrm->Commit();
+ xRootStrg->Commit();
+
+ if (xPackageEncryption.is())
+ {
+ // Perform DRM encryption
+ pMedStrm->Seek(0);
+
+ xPackageEncryption->setupEncryption(aEncryptionData);
+
+ uno::Reference<io::XInputStream > xInputStream(new utl::OSeekableInputStreamWrapper(pMedStrm, false));
+ uno::Sequence<beans::NamedValue> aStreams = xPackageEncryption->encrypt(xInputStream);
+
+ tools::SvRef<SotStorage> xEncryptedRootStrg = new SotStorage(pOriginalMediaStrm, false);
+ for (const beans::NamedValue & aStreamData : std::as_const(aStreams))
+ {
+ // To avoid long paths split and open substorages recursively
+ // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09
+ tools::SvRef<SotStorage> pStorage = xEncryptedRootStrg.get();
+ OUString sFileName;
+ sal_Int32 idx = 0;
+ do
+ {
+ OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx);
+ if (!sPathElem.isEmpty())
+ {
+ if (idx < 0)
+ {
+ sFileName = sPathElem;
+ }
+ else
+ {
+ pStorage = pStorage->OpenSotStorage(sPathElem);
+ }
+ }
+ } while (pStorage && idx >= 0);
+
+ if (!pStorage)
+ {
+ eRet = ERRCODE_IO_GENERAL;
+ break;
+ }
+
+ tools::SvRef<SotStorageStream> pStream = pStorage->OpenSotStream(sFileName);
+ if (!pStream)
+ {
+ eRet = ERRCODE_IO_GENERAL;
+ break;
+ }
+ uno::Sequence<sal_Int8> aStreamContent;
+ aStreamData.Value >>= aStreamContent;
+ size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getConstArray(), aStreamContent.getLength());
+ if (nBytesWritten != static_cast<size_t>(aStreamContent.getLength()))
+ {
+ eRet = ERRCODE_IO_CANTWRITE;
+ break;
+ }
+ }
+ xEncryptedRootStrg->Commit();
+
+ // Restore encryption data
+ rMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, uno::Any(aEncryptionData)));
+ }
+
+ return eRet;
+}
+
+ErrCode ScFormatFilterPluginImpl::ScExportExcel5( SfxMedium& rMedium, ScDocument *pDocument,
+ ExportFormatExcel eFormat, rtl_TextEncoding eNach )
+{
+ if( eFormat != ExpBiff5 && eFormat != ExpBiff8 )
+ return SCERR_IMPORT_NI;
+
+ // check the passed Calc document
+ OSL_ENSURE( pDocument, "::ScExportExcel5 - no document" );
+ if( !pDocument ) return SCERR_IMPORT_INTERNAL; // should not happen
+
+ // check the output stream from medium
+ SvStream* pMedStrm = rMedium.GetOutStream();
+ OSL_ENSURE( pMedStrm, "::ScExportExcel5 - medium without output stream" );
+ if( !pMedStrm ) return SCERR_IMPORT_OPEN; // should not happen
+
+ ErrCode eRet = lcl_ExportExcelBiff(rMedium, pDocument, pMedStrm, eFormat == ExpBiff8, eNach);
+ return eRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportCalcRTF(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ ScRange aRange;
+
+ bool bRet;
+
+ try
+ {
+ bRet = ScFormatFilter::Get().ScImportRTF(rStream, OUString(), &aDocument, aRange) == ERRCODE_NONE;
+ }
+ catch (const std::range_error&)
+ {
+ return false;
+ }
+
+ return bRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportXLS(SvStream& rStream)
+{
+ ScDLL::Init();
+ SfxMedium aMedium;
+ css::uno::Reference<css::io::XInputStream> xStm(new utl::OInputStreamWrapper(rStream));
+ aMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_INPUTSTREAM, css::uno::Any(xStm)));
+
+ ScDocShellRef xDocShell = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT |
+ SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS |
+ SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
+
+ xDocShell->DoInitNew();
+
+ ScDocument& rDoc = xDocShell->GetDocument();
+
+ ScDocOptions aDocOpt = rDoc.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ rDoc.SetDocOptions(aDocOpt);
+ rDoc.MakeTable(0);
+ rDoc.EnableExecuteLink(false);
+ rDoc.SetInsertingFromOtherDoc(true);
+ rDoc.InitDrawLayer(xDocShell.get());
+ bool bRet(false);
+ try
+ {
+ bRet = ScFormatFilter::Get().ScImportExcel(aMedium, &rDoc, EIF_AUTO) == ERRCODE_NONE;
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ }
+ catch (const std::out_of_range&)
+ {
+ }
+ xDocShell->DoClose();
+ xDocShell.clear();
+ return bRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportDIF(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ return ScFormatFilter::Get().ScImportDif(rStream, &aDocument, ScAddress(0, 0, 0), RTL_TEXTENCODING_IBM_850) == ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx
new file mode 100644
index 000000000..d217c9622
--- /dev/null
+++ b/sc/source/filter/excel/excform.cxx
@@ -0,0 +1,1911 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excform.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <scmatrix.hxx>
+
+#include <formula/errorcodes.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+
+#include <imp_op.hxx>
+#include <namebuff.hxx>
+#include <root.hxx>
+#include <xltracer.hxx>
+#include <xihelper.hxx>
+#include <xiname.hxx>
+#include <xistyle.hxx>
+#include <documentimport.hxx>
+
+using ::std::vector;
+
+const sal_uInt16 ExcelToSc::nRowMask = 0x3FFF;
+
+void ImportExcel::Formula25()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXF = 0, nFormLen;
+ double fCurVal;
+ bool bShrFmla;
+
+ aIn >> aXclPos;
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// BIFF2
+ aIn.Ignore( 3 );
+
+ fCurVal = aIn.ReadDouble();
+ aIn.Ignore( 1 );
+ nFormLen = aIn.ReaduInt8();
+ bShrFmla = false;
+ }
+ else
+ {// BIFF5
+ sal_uInt8 nFlag0;
+ nXF = aIn.ReaduInt16();
+ fCurVal = aIn.ReadDouble();
+ nFlag0 = aIn.ReaduInt8();
+ aIn.Ignore( 5 );
+
+ nFormLen = aIn.ReaduInt16();
+
+ bShrFmla = nFlag0 & 0x08; // shared or not shared
+ }
+
+ Formula( aXclPos, nXF, nFormLen, fCurVal, bShrFmla );
+}
+
+void ImportExcel::Formula3()
+{
+ Formula4();
+}
+
+void ImportExcel::Formula4()
+{
+ XclAddress aXclPos;
+
+ aIn >> aXclPos;
+ sal_uInt16 nXF = aIn.ReaduInt16();
+ double fCurVal = aIn.ReadDouble();
+ aIn.Ignore( 2 );
+ sal_uInt16 nFormLen = aIn.ReaduInt16();
+
+ Formula( aXclPos, nXF, nFormLen, fCurVal, false );
+}
+
+void ImportExcel::Formula(
+ const XclAddress& rXclPos, sal_uInt16 nXF, sal_uInt16 nFormLen, double fCurVal, bool bShrFmla)
+{
+ if (!nFormLen)
+ return;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if (!GetAddressConverter().ConvertAddress(aScPos, rXclPos, GetCurrScTab(), true))
+ // Conversion failed.
+ return;
+
+ // Formula will be read next, length in nFormLen
+ std::unique_ptr<ScTokenArray> pResult;
+
+ pFormConv->Reset( aScPos );
+ ScDocumentImport& rDoc = GetDocImport();
+
+ if (bShrFmla)
+ {
+ // This is a shared formula. Get the token array from the shared formula pool.
+ SCCOL nSharedCol;
+ SCROW nSharedRow;
+ if (ExcelToSc::ReadSharedFormulaPosition(maStrm, nSharedCol, nSharedRow))
+ {
+ ScAddress aRefPos(nSharedCol, nSharedRow, GetCurrScTab());
+ const ScTokenArray* pSharedCode = pFormConv->GetSharedFormula(aRefPos);
+ if (pSharedCode)
+ {
+ ScFormulaCell* pCell;
+ pCell = new ScFormulaCell(rD, aScPos, pSharedCode->Clone());
+ pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().EnsureTable(aScPos.Tab());
+ rDoc.setFormulaCell(aScPos, pCell);
+ pCell->SetNeedNumberFormat(false);
+ if (std::isfinite(fCurVal))
+ pCell->SetResultDouble(fCurVal);
+
+ GetXFRangeBuffer().SetXF(aScPos, nXF);
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, pCell);
+ }
+ else
+ {
+ // Shared formula not found even though it's clearly a shared formula.
+ // The cell will be created in the following shared formula
+ // record.
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, nullptr);
+ }
+ return;
+ }
+ }
+
+ ConvErr eErr = pFormConv->Convert( pResult, maStrm, nFormLen, true );
+
+ ScFormulaCell* pCell = nullptr;
+
+ if (pResult)
+ {
+ pCell = new ScFormulaCell(rDoc.getDoc(), aScPos, std::move(pResult));
+ pCell->GetCode()->WrapReference(aScPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+ rDoc.getDoc().EnsureTable(aScPos.Tab());
+ rDoc.setFormulaCell(aScPos, pCell);
+ SetLastFormula(aScPos.Col(), aScPos.Row(), fCurVal, nXF, pCell);
+ }
+ else
+ {
+ pCell = rDoc.getDoc().GetFormulaCell(aScPos);
+ if (pCell)
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ }
+
+ if (pCell)
+ {
+ pCell->SetNeedNumberFormat(false);
+ if( eErr != ConvErr::OK )
+ ExcelToSc::SetError( *pCell, eErr );
+
+ if (std::isfinite(fCurVal))
+ pCell->SetResultDouble(fCurVal);
+ }
+
+ GetXFRangeBuffer().SetXF(aScPos, nXF);
+}
+
+ExcelToSc::ExcelToSc( XclImpRoot& rRoot ) :
+ ExcelConverterBase(rRoot.GetDocImport().getDoc().GetSharedStringPool()),
+ XclImpRoot( rRoot ),
+ maFuncProv( rRoot ),
+ meBiff( rRoot.GetBiff() )
+{
+}
+
+ExcelToSc::~ExcelToSc()
+{
+}
+
+std::unique_ptr<ScTokenArray> ExcelToSc::GetDummy()
+{
+ aPool.Store( "Dummy()" );
+ aPool >> aStack;
+ return aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+}
+
+// if bAllowArrays is false stream seeks to first byte after <nFormulaLen>
+// otherwise it will seek to the first byte after the additional content (eg
+// inline arrays) following <nFormulaLen>
+ConvErr ExcelToSc::Convert( std::unique_ptr<ScTokenArray>& pResult, XclImpStream& aIn, std::size_t nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT )
+{
+ RootData& rR = GetOldRoot();
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ bool bArrayFormula = false;
+ TokenId nBuf0;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bConditional = eFT == FT_CondFormat;
+ const bool bRNorSF = bRangeName || bSharedFormula || bConditional;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ ExtensionTypeVec aExtensions;
+
+ if( nFormulaLen == 0 )
+ {
+ aPool.Store( "-/-" );
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return ConvErr::OK;
+ }
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ {
+ sal_uInt16 nUINT16 = 3;
+
+ if( meBiff != EXC_BIFF2 )
+ nUINT16++;
+
+ aIn.Ignore( nUINT16 );
+
+ bArrayFormula = true;
+ break;
+ }
+ case 0x03: // Addition [312 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocAdd << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x04: // Subtraction [313 264]
+ // SECOND-TOP minus TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocSub << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x05: // Multiplication [313 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocMul << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x06: // Division [313 264]
+ // divide TOP by SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocDiv << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x07: // Exponentiation [313 265]
+ // raise SECOND-TOP to power of TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocPow << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x08: // Concatenation [313 265]
+ // append TOP to SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocAmpersand << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x09: // Less Than [313 265]
+ // SECOND-TOP < TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLess << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0A: // Less Than or Equal [313 265]
+ // SECOND-TOP <= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLessEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0B: // Equal [313 265]
+ // SECOND-TOP == TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0C: // Greater Than or Equal [313 265]
+ // SECOND-TOP >= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreaterEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0D: // Greater Than [313 265]
+ // SECOND-TOP > TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreater << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0E: // Not Equal [313 265]
+ // SECOND-TOP != TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocNotEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0F: // Intersection [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocIntersect << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x10: // Union [314 265]
+ // ocSep instead of 'ocUnion'
+ aStack >> nBuf0;
+ aPool << aStack << ocSep << nBuf0;
+ // doesn't fit exactly, but is more Excel-like
+ aPool >> aStack;
+ break;
+ case 0x11: // Range [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocRange << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x12: // Unary Plus [312 264]
+ aPool << ocAdd << aStack;
+ aPool >> aStack;
+ break;
+ case 0x13: // Unary Minus [312 264]
+ aPool << ocNegSub << aStack;
+ aPool >> aStack;
+ break;
+ case 0x14: // Percent Sign [312 264]
+ aPool << aStack << ocPercentSign;
+ aPool >> aStack;
+ break;
+ case 0x15: // Parenthesis [326 278]
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case 0x16: // Missing Argument [314 266]
+ aPool << ocMissing;
+ aPool >> aStack;
+ GetTracer().TraceFormulaMissingArg();
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ nLen = aIn.ReaduInt8();
+ OUString aString = aIn.ReadRawByteString( nLen );
+
+ aStack << aPool.Store( aString );
+ break;
+ }
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+
+ if( meBiff == EXC_BIFF2 )
+ {
+ nData = aIn.ReaduInt8();
+ nFactor = 1;
+ }
+ else
+ {
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+ }
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ else if( nOpt & 0x10 ) // AttrSum
+ DoMulArgs( ocSum, 1 );
+ }
+ break;
+ case 0x1A: // External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 7 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 10 ); break;
+ case EXC_BIFF5:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1A does not exist in Biff5!" );
+ [[fallthrough]];
+ default:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1B: // End External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 3 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 4 ); break;
+ case EXC_BIFF5:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1B does not exist in Biff5!" );
+ [[fallthrough]];
+ default:
+ SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1D: // Boolean [315 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ if( nByte == 0 )
+ aPool << ocFalse << ocOpen << ocClose;
+ else
+ aPool << ocTrue << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1E: // Integer [315 266]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aStack << aPool.Store( static_cast<double>(nUINT16) );
+ break;
+ }
+ case 0x1F: // Number [315 266]
+ {
+ double fDouble = aIn.ReadDouble();
+ aStack << aPool.Store( fDouble );
+ break;
+ }
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ {
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 6 : 7 );
+ if( bAllowArrays )
+ {
+ aStack << aPool.StoreMatrix();
+ aExtensions.push_back( EXTENSION_ARRAY );
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ {
+ sal_uInt16 nXclFunc;
+ if( meBiff <= EXC_BIFF3 )
+ nXclFunc = aIn.ReaduInt8();
+ else
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, pFuncInfo->mnMaxParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ }
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ {
+ sal_uInt16 nXclFunc;
+ sal_uInt8 nParamCount;
+ nParamCount = aIn.ReaduInt8();
+ nParamCount &= 0x7F;
+ if( meBiff <= EXC_BIFF3 )
+ nXclFunc = aIn.ReaduInt8();
+ else
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, nParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ }
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ switch( meBiff )
+ {
+ case EXC_BIFF2: aIn.Ignore( 5 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: aIn.Ignore( 8 ); break;
+ case EXC_BIFF5: aIn.Ignore( 12 ); break;
+ default:
+ OSL_FAIL(
+ "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ const XclImpName* pName = GetNameManager().GetName( nUINT16 );
+ if(pName && !pName->GetScRangeData())
+ aStack << aPool.Store( ocMacro, pName->GetXclName() );
+ else
+ aStack << aPool.StoreName(nUINT16, -1);
+ }
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aSRD.SetAbsCol(static_cast<SCCOL>(nByte));
+ aSRD.SetAbsRow(nUINT16 & 0x3FFF);
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRangeName );
+
+ switch ( nOp )
+ {
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+ ScSingleRefData& rSRef1 = aCRD.Ref1;
+ ScSingleRefData& rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ // no information which part is deleted, set all
+ rSRef1.SetColDeleted( true );
+ rSRef1.SetRowDeleted( true );
+ rSRef2.SetColDeleted( true );
+ rSRef2.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ aExtensions.push_back( EXTENSION_MEMAREA );
+ [[fallthrough]];
+
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 4 : 6 );
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8(); // >> Attribute, Row >> Col
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRNorSF );
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8( );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ aIn.Ignore( (meBiff == EXC_BIFF2) ? 1 : 2 );
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ {
+ OUString aString = "COMM_EQU_FUNC";
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aString += OUString::number( nByte );
+ nByte = aIn.ReaduInt8();
+ aStack << aPool.Store( aString );
+ DoMulArgs( ocPush, nByte + 1 );
+ break;
+ }
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ {
+ sal_Int16 nINT16 = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ if( nINT16 >= 0 )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ else
+ aStack << aPool.StoreName( nUINT16, -1 );
+ aIn.Ignore( 12 );
+ break;
+ }
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRow;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nCol;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ { // from external
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a SingleRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ { // in current Workbook
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel( nRow, nCol, aSRD, bRangeName );
+
+ switch ( nOp )
+ {
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+ if ( !ValidTab(static_cast<SCTAB>(nTabFirst)) )
+ aSRD.SetTabDeleted( true );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aCRD.Ref2.SetTabDeleted( !ValidTab(static_cast<SCTAB>(nTabLast)) );
+ aStack << aPool.Store( aCRD );
+ }
+ else
+ aStack << aPool.Store( aSRD );
+ }
+ }
+
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRowFirst, nRowLast;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nColFirst, nColLast;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a CompleteRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ // first part of range
+ ScSingleRefData& rR1 = aCRD.Ref1;
+ ScSingleRefData& rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nTabFirst != nTabLast );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ // no information which part is deleted, set all
+ rR1.SetColDeleted( true );
+ rR1.SetRowDeleted( true );
+ rR2.SetColDeleted( true );
+ rR2.SetRowDeleted( true );
+ }
+ if ( !ValidTab(static_cast<SCTAB>(nTabFirst)) )
+ rR1.SetTabDeleted( true );
+ if ( !ValidTab(static_cast<SCTAB>(nTabLast)) )
+ rR2.SetTabDeleted( true );
+
+ aStack << aPool.Store( aCRD );
+ }//END in current Workbook
+ }
+ break;
+ default: bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Ni;
+ }
+ else if( aIn.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Count;
+ }
+ else if( bArrayFormula )
+ {
+ pResult = nullptr;
+ eRet = ConvErr::OK;
+ }
+ else
+ {
+ pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::OK;
+ }
+
+ aIn.Seek( nEndPos );
+
+ if( eRet == ConvErr::OK )
+ ReadExtensions( aExtensions, aIn );
+
+ return eRet;
+}
+
+// stream seeks to first byte after <nFormulaLen>
+ConvErr ExcelToSc::Convert( ScRangeListTabs& rRangeList, XclImpStream& aIn, std::size_t nFormulaLen,
+ SCTAB nTab, const FORMULA_TYPE eFT )
+{
+ RootData& rR = GetOldRoot();
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeName || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ aCRD.Ref1.SetAbsTab(aEingPos.Tab());
+ aCRD.Ref2.SetAbsTab(aEingPos.Tab());
+
+ if( nFormulaLen == 0 )
+ return ConvErr::OK;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+ std::size_t nIgnore = 0;
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ nIgnore = (meBiff == EXC_BIFF2) ? 3 : 4;
+ break;
+ case 0x02: // Data Table [325 277]
+ nIgnore = (meBiff == EXC_BIFF2) ? 3 : 4;
+ break;
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x17: // String Constant [314 266]
+ nLen = aIn.ReaduInt8();
+ nIgnore = nLen;
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+
+ if( meBiff == EXC_BIFF2 )
+ {
+ nData = aIn.ReaduInt8();
+ nFactor = 1;
+ }
+ else
+ {
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+ }
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ }
+ break;
+ case 0x1A: // External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 7; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 10; break;
+ case EXC_BIFF5: SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1A does not exist in Biff5!" );
+ [[fallthrough]];
+ default: SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1B: // End External Reference [330 ]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 3; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 4; break;
+ case EXC_BIFF5: SAL_INFO( "sc", "-ExcelToSc::Convert(): 0x1B does not exist in Biff5!" );
+ [[fallthrough]];
+ default: SAL_INFO( "sc", "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nIgnore = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ nIgnore = 2;
+ break;
+ case 0x1F: // Number [315 266]
+ nIgnore = 8;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nIgnore = (meBiff == EXC_BIFF2) ? 6 : 7;
+ break;
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ nIgnore = (meBiff <= EXC_BIFF3) ? 1 : 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ nIgnore = (meBiff <= EXC_BIFF3) ? 2 : 3;
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ switch( meBiff )
+ {
+ case EXC_BIFF2: nIgnore = 7; break;
+ case EXC_BIFF3:
+ case EXC_BIFF4: nIgnore = 10; break;
+ case EXC_BIFF5: nIgnore = 14; break;
+ default: OSL_FAIL( "-ExcelToSc::Convert(): A little oblivious?" );
+ }
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aSRD.SetAbsCol(static_cast<SCCOL>(nByte));
+ aSRD.SetAbsRow(nUINT16 & 0x3FFF);
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRangeName );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ nIgnore = (meBiff == EXC_BIFF2) ? 4 : 6;
+ break;
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nIgnore = 3;
+ break;
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nIgnore = 6;
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ sal_uInt8 nByte = aIn.ReaduInt8(); // >> Attribute, Row >> Col
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel( nUINT16, nByte, aSRD, bRNorSF );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt8 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ nIgnore = (meBiff == EXC_BIFF2) ? 1 : 2;
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nIgnore = 2;
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nIgnore = 24;
+ break;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRow;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nCol;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a SingleRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ bool b3D = ( static_cast<SCTAB>(nTabFirst) != aEingPos.Tab() ) || bRangeName;
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D( b3D );
+
+ ExcRelToScRel( nRow, nCol, aSRD, bRangeName );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aSRD;
+ aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(static_cast<SCTAB>(nTabLast));
+ b3D = ( static_cast<SCTAB>(nTabLast) != aEingPos.Tab() );
+ aCRD.Ref2.SetFlag3D( b3D );
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ else
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ {
+ sal_uInt16 nTabFirst, nTabLast, nRowFirst, nRowLast;
+ sal_Int16 nExtSheet;
+ sal_uInt8 nColFirst, nColLast;
+
+ nExtSheet = aIn.ReadInt16();
+ aIn.Ignore( 8 );
+ nTabFirst = aIn.ReaduInt16();
+ nTabLast = aIn.ReaduInt16();
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+
+ if( nExtSheet >= 0 )
+ // from external
+ {
+ if( rR.pExtSheetBuff->GetScTabIndex( nExtSheet, nTabLast ) )
+ {
+ nTabFirst = nTabLast;
+ nExtSheet = 0; // found
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ nExtSheet = 1; // don't create a CompleteRef
+ }
+ }
+
+ if( nExtSheet <= 0 )
+ {// in current Workbook
+ // first part of range
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D( ( static_cast<SCTAB>(nTabFirst) != aEingPos.Tab() ) || bRangeName );
+ rR2.SetFlag3D( ( static_cast<SCTAB>(nTabLast) != aEingPos.Tab() ) || bRangeName );
+
+ ExcRelToScRel( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }//END in current Workbook
+ }
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ nIgnore = 17;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nIgnore = 20;
+ break;
+ default: bError = true;
+ }
+ bError |= !aIn.IsValid();
+
+ aIn.Ignore( nIgnore );
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ eRet = ConvErr::Ni;
+ else if( aIn.GetRecPos() != nEndPos )
+ eRet = ConvErr::Count;
+ else
+ eRet = ConvErr::OK;
+
+ aIn.Seek( nEndPos );
+ return eRet;
+}
+
+void ExcelToSc::ConvertExternName( std::unique_ptr<ScTokenArray>& /*rpArray*/, XclImpStream& /*rStrm*/, std::size_t /*nFormulaLen*/,
+ const OUString& /*rUrl*/, const vector<OUString>& /*rTabNames*/ )
+{
+}
+
+void ExcelToSc::GetAbsRefs( ScRangeList& rRangeList, XclImpStream& rStrm, std::size_t nLen )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF5 );
+ if( GetBiff() != EXC_BIFF5 )
+ return;
+
+ sal_uInt8 nOp;
+ sal_uInt16 nRow1, nRow2;
+ sal_uInt8 nCol1, nCol2;
+ SCTAB nTab1, nTab2;
+ sal_uInt16 nTabFirst, nTabLast;
+ sal_Int16 nRefIdx;
+
+ std::size_t nSeek;
+ std::size_t nEndPos = rStrm.GetRecPos() + nLen;
+
+ while( rStrm.IsValid() && (rStrm.GetRecPos() < nEndPos) )
+ {
+ nOp = rStrm.ReaduInt8();
+ nSeek = 0;
+
+ switch( nOp )
+ {
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ nRow1 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ // Area Reference Within a Shared Formula[ 274]
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+ nCol2 = rStrm.ReaduInt8();
+
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nTabFirst = rStrm.ReaduInt16();
+ nTabLast = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+
+ goto _3d_common;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nTabFirst = rStrm.ReaduInt16();
+ nTabLast = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nCol1 = rStrm.ReaduInt8();
+ nCol2 = rStrm.ReaduInt8();
+
+ _3d_common:
+ nTab1 = static_cast< SCTAB >( nTabFirst );
+ nTab2 = static_cast< SCTAB >( nTabLast );
+
+ // skip references to deleted sheets
+ if( (nRefIdx >= 0) || !ValidTab( nTab1 ) || (nTab1 != nTab2) )
+ break;
+
+ goto _common;
+ _common:
+ // do not check abs/rel flags, linked controls have set them!
+ {
+ ScRange aScRange;
+ nRow1 &= 0x3FFF;
+ nRow2 &= 0x3FFF;
+ if( GetAddressConverter().ConvertRange( aScRange, XclRange( nCol1, nRow1, nCol2, nRow2 ), nTab1, nTab2, true ) )
+ rRangeList.push_back( aScRange );
+ }
+ break;
+
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nSeek = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nSeek = 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nSeek = 3;
+ break;
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ nSeek = 4;
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nSeek = 6;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nSeek = 7;
+ break;
+ case 0x1F: // Number [315 266]
+ nSeek = 8;
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ nSeek = 14;
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ nSeek = 17;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nSeek = 20;
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nSeek = 24;
+ break;
+ case 0x17: // String Constant [314 266]
+ nSeek = rStrm.ReaduInt8();
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt8 nOpt;
+ sal_uInt16 nData;
+ nOpt = rStrm.ReaduInt8();
+ nData = rStrm.ReaduInt16();
+ if( nOpt & 0x04 )
+ nSeek = nData * 2 + 2;
+ }
+ break;
+ }
+
+ rStrm.Ignore( nSeek );
+ }
+ rStrm.Seek( nEndPos );
+}
+
+void ExcelToSc::DoMulArgs( DefTokenId eId, sal_uInt8 nCnt )
+{
+ TokenId eParam[ 256 ];
+ sal_Int32 nPass;
+
+ if( eId == ocCeil || eId == ocFloor )
+ {
+ aStack << aPool.Store( 1.0 ); // default, because not present in Excel
+ nCnt++;
+ }
+
+ for( nPass = 0; aStack.HasMoreTokens() && (nPass < nCnt); nPass++ )
+ aStack >> eParam[ nPass ];
+ // #i70925# reduce parameter count, if no more tokens available on token stack
+ if( nPass < nCnt )
+ nCnt = static_cast< sal_uInt8 >( nPass );
+
+ if( nCnt > 0 && eId == ocExternal )
+ {
+ TokenId n = eParam[ nCnt - 1 ];
+//##### ADJUST STUPIDITY FOR BASIC-FUNCS!
+ if( const OUString* pExt = aPool.GetExternal( n ) )
+ {
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclMacroName( *pExt ) )
+ aPool << pFuncInfo->meOpCode;
+ else
+ aPool << n;
+ nCnt--;
+ }
+ else
+ aPool << eId;
+ }
+ else
+ aPool << eId;
+
+ aPool << ocOpen;
+
+ if( nCnt > 0 )
+ {
+ // attention: 0 = last parameter, nCnt-1 = first parameter
+ sal_Int16 nSkipEnd = -1; // skip all parameters <= nSkipEnd
+
+ sal_Int16 nLast = nCnt - 1;
+
+ // functions for which parameters have to be skipped
+ if( eId == ocPercentrank && nCnt == 3 )
+ nSkipEnd = 0; // skip last parameter if necessary
+
+ // Joost special cases
+ else if( eId == ocIf )
+ {
+ sal_uInt16 nNullParam = 0;
+ for( nPass = 0 ; nPass < nCnt ; nPass++ )
+ {
+ if( aPool.IsSingleOp( eParam[ nPass ], ocMissing ) )
+ {
+ if( !nNullParam )
+ nNullParam = static_cast<sal_uInt16>(aPool.Store( 0.0 ));
+ eParam[ nPass ] = nNullParam;
+ }
+ }
+ }
+
+ // [Parameter{;Parameter}]
+ if( nLast > nSkipEnd )
+ {
+ // nSkipEnd is either 0 or -1 => nLast >= 0
+ aPool << eParam[ nLast ];
+ for( nPass = nLast - 1 ; nPass > nSkipEnd ; nPass-- )
+ {
+ // nPass > nSkipEnd => nPass >= 0
+ aPool << ocSep << eParam[nPass];
+ }
+ }
+ }
+ aPool << ocClose;
+
+ aPool >> aStack;
+}
+
+void ExcelToSc::ExcRelToScRel( sal_uInt16 nRow, sal_uInt8 nCol, ScSingleRefData &rSRD, const bool bName )
+{
+ if( bName )
+ {
+ // C O L
+ if( nRow & 0x4000 )
+ rSRD.SetRelCol(nCol);
+ else
+ rSRD.SetAbsCol(nCol);
+
+ // R O W
+ if( nRow & 0x8000 )
+ {// rel Row
+ if( nRow & 0x2000 ) // Bit 13 set?
+ // Row negative
+ rSRD.SetRelRow(nRow | 0xC000);
+ else
+ // Row positive
+ rSRD.SetRelRow(nRow & nRowMask);
+ }
+ else
+ {// abs Row
+ rSRD.SetAbsRow(nRow & nRowMask);
+ }
+
+ // T A B
+ // abs needed if rel in shared formula for ScCompiler UpdateNameReference
+ if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() )
+ rSRD.SetAbsTab(GetCurrScTab());
+ }
+ else
+ {
+ bool bColRel = (nRow & 0x4000) > 0;
+ bool bRowRel = (nRow & 0x8000) > 0;
+
+ if (bColRel)
+ rSRD.SetRelCol(nCol - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(nCol);
+
+ rSRD.SetAbsRow(nRow & nRowMask);
+ if (bRowRel)
+ rSRD.SetRelRow(rSRD.Row() - aEingPos.Row());
+
+ // T A B
+ // #i10184# abs needed if rel in shared formula for ScCompiler UpdateNameReference
+ if ( rSRD.IsTabRel() && !rSRD.IsFlag3D() )
+ rSRD.SetAbsTab(GetCurrScTab() + rSRD.Tab());
+ }
+}
+
+std::unique_ptr<ScTokenArray> ExcelToSc::GetBoolErr( XclBoolError eType )
+{
+ FormulaError nError;
+ aPool.Reset();
+ aStack.Reset();
+
+ DefTokenId eOc;
+
+ switch( eType )
+ {
+ case xlErrNull: eOc = ocStop; nError = FormulaError::NoCode; break;
+ case xlErrDiv0: eOc = ocStop; nError = FormulaError::DivisionByZero; break;
+ case xlErrValue: eOc = ocStop; nError = FormulaError::NoValue; break;
+ case xlErrRef: eOc = ocStop; nError = FormulaError::NoRef; break;
+ case xlErrName: eOc = ocStop; nError = FormulaError::NoName; break;
+ case xlErrNum: eOc = ocStop; nError = FormulaError::IllegalFPOperation; break;
+ case xlErrNA: eOc = ocNotAvail; nError = FormulaError::NotAvailable; break;
+ case xlErrTrue: eOc = ocTrue; nError = FormulaError::NONE; break;
+ case xlErrFalse: eOc = ocFalse; nError = FormulaError::NONE; break;
+ case xlErrUnknown: eOc = ocStop; nError = FormulaError::UnknownState; break;
+ default:
+ OSL_FAIL( "ExcelToSc::GetBoolErr - wrong enum!" );
+ eOc = ocNoName;
+ nError = FormulaError::UnknownState;
+ }
+
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+
+ aPool >> aStack;
+
+ std::unique_ptr<ScTokenArray> pResult = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ if( nError != FormulaError::NONE )
+ pResult->SetCodeError( nError );
+
+ pResult->SetExclusiveRecalcModeNormal();
+
+ return pResult;
+}
+
+bool ExcelToSc::ReadSharedFormulaPosition( XclImpStream& rStrm, SCCOL& rCol, SCROW& rRow )
+{
+ rStrm.PushPosition();
+
+ sal_uInt8 nOp;
+ nOp = rStrm.ReaduInt8();
+
+ if (nOp != 0x01) // must be PtgExp token.
+ {
+ rStrm.PopPosition();
+ return false;
+ }
+
+ sal_uInt16 nRow, nCol;
+ nRow = rStrm.ReaduInt16();
+ nCol = rStrm.ReaduInt16();
+ rStrm.PopPosition();
+ rCol = nCol;
+ rRow = nRow;
+ return true;
+}
+
+const ScTokenArray* ExcelToSc::GetSharedFormula( const ScAddress& rRefPos ) const
+{
+ return GetOldRoot().pShrfmlaBuff->Find(rRefPos);
+}
+
+void ExcelToSc::SetError( ScFormulaCell &rCell, const ConvErr eErr )
+{
+ FormulaError nInd;
+
+ switch( eErr )
+ {
+ case ConvErr::Ni: nInd = FormulaError::UnknownToken; break;
+ case ConvErr::Count: nInd = FormulaError::CodeOverflow; break;
+ default: nInd = FormulaError::NoCode; // I had no better idea
+ }
+
+ rCell.SetErrCode( nInd );
+}
+
+void ExcelToSc::SetComplCol( ScComplexRefData &rCRD )
+{
+ ScSingleRefData &rSRD = rCRD.Ref2;
+ ScDocument& rDoc = GetDocImport().getDoc();
+ if( rSRD.IsColRel() )
+ rSRD.SetRelCol(rDoc.MaxCol() - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(rDoc.MaxCol());
+}
+
+void ExcelToSc::SetComplRow( ScComplexRefData &rCRD )
+{
+ ScSingleRefData &rSRD = rCRD.Ref2;
+ ScDocument& rDoc = GetDocImport().getDoc();
+ if( rSRD.IsRowRel() )
+ rSRD.SetRelRow(rDoc.MaxRow() - aEingPos.Row());
+ else
+ rSRD.SetAbsRow(rDoc.MaxRow());
+}
+
+void ExcelToSc::ReadExtensionArray( unsigned int n, XclImpStream& aIn )
+{
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+
+ SCSIZE nC, nCols;
+ SCSIZE nR, nRows;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ nCols = nByte + 1;
+ nRows = nUINT16 + 1;
+ }
+ else
+ {
+ nCols = nByte ? nByte : 256;
+ nRows = nUINT16;
+ }
+
+ ScMatrix* pMatrix = aPool.GetMatrix( n );
+
+ if( nullptr != pMatrix )
+ {
+ pMatrix->Resize(nCols, nRows);
+ pMatrix->GetDimensions( nC, nR);
+ if( nC != nCols || nR != nRows )
+ {
+ OSL_FAIL( "ExcelToSc::ReadExtensionArray - matrix size mismatch" );
+ pMatrix = nullptr;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "ExcelToSc::ReadExtensionArray - missing matrix" );
+ }
+
+ //assuming worst case scenario of unknown types
+ const size_t nMinRecordSize = 1;
+ const size_t nMaxRows = aIn.GetRecLeft() / (nMinRecordSize * nCols);
+ if (nRows > nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << nRows << " claimed, truncating");
+ nRows = nMaxRows;
+ }
+
+ svl::SharedStringPool& rPool = GetDoc().GetSharedStringPool();
+ for( nR = 0 ; nR < nRows; nR++ )
+ {
+ for( nC = 0 ; nC < nCols; nC++ )
+ {
+ nByte = aIn.ReaduInt8();
+ switch( nByte )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ aIn.Ignore( 8 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutEmpty( nC, nR );
+ }
+ break;
+
+ case EXC_CACHEDVAL_DOUBLE:
+ {
+ double fDouble = aIn.ReadDouble();
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutDouble( fDouble, nC, nR );
+ }
+ break;
+ }
+ case EXC_CACHEDVAL_STRING:
+ {
+ OUString aString;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ nUINT16 = aIn.ReaduInt16();
+ aString = aIn.ReadUniString( nUINT16 );
+ }
+ else
+ {
+ nByte = aIn.ReaduInt8();
+ aString = aIn.ReadRawByteString( nByte );
+ }
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutString(rPool.intern(aString), nC, nR);
+ }
+ break;
+ }
+ case EXC_CACHEDVAL_BOOL:
+ nByte = aIn.ReaduInt8();
+ aIn.Ignore( 7 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutBoolean( nByte != 0, nC, nR );
+ }
+ break;
+
+ case EXC_CACHEDVAL_ERROR:
+ nByte = aIn.ReaduInt8();
+ aIn.Ignore( 7 );
+ if( nullptr != pMatrix )
+ {
+ pMatrix->PutError( XclTools::GetScErrorCode( nByte ), nC, nR );
+ }
+ break;
+ }
+ }
+ }
+}
+
+void ExcelToSc::ReadExtensionNlr( XclImpStream& aIn )
+{
+ sal_uInt32 nFlags;
+ nFlags = aIn.ReaduInt32();
+
+ sal_uInt32 nCount = nFlags & EXC_TOK_NLR_ADDMASK;
+ aIn.Ignore( nCount * 4 ); // Drop the cell positions
+}
+
+void ExcelToSc::ReadExtensionMemArea( XclImpStream& aIn )
+{
+ sal_uInt16 nCount = aIn.ReaduInt16();
+
+ aIn.Ignore( static_cast<std::size_t>(nCount) * ((GetBiff() == EXC_BIFF8) ? 8 : 6) ); // drop the ranges
+}
+
+void ExcelToSc::ReadExtensions( const ExtensionTypeVec& rExtensions,
+ XclImpStream& aIn )
+{
+ unsigned int nArray = 0;
+
+ for(int eType : rExtensions)
+ {
+ switch( eType )
+ {
+ case EXTENSION_ARRAY:
+ ReadExtensionArray( nArray++, aIn );
+ break;
+
+ case EXTENSION_NLR:
+ ReadExtensionNlr( aIn );
+ break;
+
+ case EXTENSION_MEMAREA:
+ ReadExtensionMemArea( aIn );
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excform8.cxx b/sc/source/filter/excel/excform8.cxx
new file mode 100644
index 000000000..62e184204
--- /dev/null
+++ b/sc/source/filter/excel/excform8.cxx
@@ -0,0 +1,1671 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excform.hxx>
+
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+
+#include <externalrefmgr.hxx>
+
+#include <cstring>
+
+#include <o3tl/safeint.hxx>
+
+using ::std::vector;
+
+namespace {
+
+/**
+ * Extract a file path from OLE link path. An OLE link path is expected to
+ * be in the following format:
+ *
+ * Excel.Sheet.8 \3 [file path]
+ */
+bool extractFilePath(const OUString& rUrl, OUString& rPath)
+{
+ const char* prefix = "Excel.Sheet.8\3";
+ size_t nPrefixLen = ::std::strlen(prefix);
+
+ sal_Int32 n = rUrl.getLength();
+ if (n <= static_cast<sal_Int32>(nPrefixLen))
+ // needs to have the specified prefix.
+ return false;
+
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rUrl.getStr();
+ for (size_t i = 0; i < o3tl::make_unsigned(n); ++i, ++p)
+ {
+ if (i < nPrefixLen)
+ {
+ sal_Unicode pc = static_cast<sal_Unicode>(*prefix++);
+ if (pc != *p)
+ return false;
+
+ continue;
+ }
+ aBuf.append(*p);
+ }
+
+ rPath = aBuf.makeStringAndClear();
+ return true;
+}
+
+}
+
+ExcelToSc8::ExternalTabInfo::ExternalTabInfo() :
+ mnFileId(0), mbExternal(false)
+{
+}
+
+ExcelToSc8::ExcelToSc8( XclImpRoot& rRoot ) :
+ ExcelToSc( rRoot ),
+ rLinkMan( rRoot.GetLinkManager() )
+{
+}
+
+ExcelToSc8::~ExcelToSc8()
+{
+}
+
+bool ExcelToSc8::GetExternalFileIdFromXti( sal_uInt16 nIxti, sal_uInt16& rFileId ) const
+{
+ const OUString* pFileUrl = rLinkMan.GetSupbookUrl(nIxti);
+ if (!pFileUrl || pFileUrl->isEmpty() || !GetDocShell())
+ return false;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(*pFileUrl, GetDocShell());
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ rFileId = pRefMgr->getExternalFileId(aFileUrl);
+
+ return true;
+}
+
+bool ExcelToSc8::Read3DTabReference( sal_uInt16 nIxti, SCTAB& rFirstTab, SCTAB& rLastTab, ExternalTabInfo& rExtInfo )
+{
+ rFirstTab = rLastTab = 0;
+ rExtInfo.mbExternal = !rLinkMan.IsSelfRef(nIxti);
+ bool bSuccess = rLinkMan.GetScTabRange(rFirstTab, rLastTab, nIxti);
+ if (!bSuccess)
+ return false;
+
+ if (!rExtInfo.mbExternal)
+ // This is internal reference. Stop here.
+ return true;
+
+ rExtInfo.maTabName = rLinkMan.GetSupbookTabName(nIxti, rFirstTab);
+ return GetExternalFileIdFromXti(nIxti, rExtInfo.mnFileId);
+}
+
+bool ExcelToSc8::HandleOleLink(sal_uInt16 nXtiIndex, const XclImpExtName& rExtName, ExternalTabInfo& rExtInfo)
+{
+ const OUString* pUrl = rLinkMan.GetSupbookUrl(nXtiIndex);
+ if (!pUrl)
+ return false;
+
+ OUString aPath;
+ if (!extractFilePath(*pUrl, aPath))
+ // file path extraction failed.
+ return false;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(aPath, GetDocShell());
+ return rExtName.CreateOleData(GetDoc(), aFileUrl, rExtInfo.mnFileId, rExtInfo.maTabName, rExtInfo.maRange);
+}
+
+// if bAllowArrays is false stream seeks to first byte after <nFormulaLen>
+// otherwise it will seek to the first byte past additional content after <nFormulaLen>
+ConvErr ExcelToSc8::Convert( std::unique_ptr<ScTokenArray>& rpTokArray, XclImpStream& aIn, std::size_t nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT )
+{
+ bool bError = false;
+ bool bArrayFormula = false;
+ TokenId nBuf0;
+ const bool bCondFormat = eFT == FT_CondFormat;
+ const bool bRangeName = eFT == FT_RangeName;
+ const bool bRangeNameOrCond = bRangeName || bCondFormat;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeNameOrCond || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+ ExtensionTypeVec aExtensions;
+
+ if( nFormulaLen == 0 )
+ {
+ aPool.Store( "-/-" );
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return ConvErr::OK;
+ }
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ sal_uInt8 nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ case 0x02: // Data Table [325 277]
+ aIn.Ignore( 4 );
+
+ bArrayFormula = true;
+ break;
+ case 0x03: // Addition [312 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocAdd << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x04: // Subtraction [313 264]
+ // SECOND-TOP minus TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocSub << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x05: // Multiplication [313 264]
+ aStack >> nBuf0;
+ aPool << aStack << ocMul << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x06: // Division [313 264]
+ // divide TOP by SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocDiv << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x07: // Exponentiation [313 265]
+ // raise SECOND-TOP to power of TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocPow << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x08: // Concatenation [313 265]
+ // append TOP to SECOND-TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocAmpersand << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x09: // Less Than [313 265]
+ // SECOND-TOP < TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLess << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0A: // Less Than or Equal [313 265]
+ // SECOND-TOP <= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocLessEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0B: // Equal [313 265]
+ // SECOND-TOP == TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0C: // Greater Than or Equal [313 265]
+ // SECOND-TOP >= TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreaterEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0D: // Greater Than [313 265]
+ // SECOND-TOP > TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocGreater << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0E: // Not Equal [313 265]
+ // SECOND-TOP != TOP
+ aStack >> nBuf0;
+ aPool << aStack << ocNotEqual << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x0F: // Intersection [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocIntersect << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x10: // Union [314 265]
+ // ocSep instead of 'ocUnion'
+ aStack >> nBuf0;
+ aPool << aStack << ocSep << nBuf0;
+ // doesn't fit exactly, but is more Excel-like
+ aPool >> aStack;
+ break;
+ case 0x11: // Range [314 265]
+ aStack >> nBuf0;
+ aPool << aStack << ocRange << nBuf0;
+ aPool >> aStack;
+ break;
+ case 0x12: // Unary Plus [312 264]
+ aPool << ocAdd << aStack;
+ aPool >> aStack;
+ break;
+ case 0x13: // Unary Minus [312 264]
+ aPool << ocNegSub << aStack;
+ aPool >> aStack;
+ break;
+ case 0x14: // Percent Sign [312 264]
+ aPool << aStack << ocPercentSign;
+ aPool >> aStack;
+ break;
+ case 0x15: // Parenthesis [326 278]
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case 0x16: // Missing Argument [314 266]
+ aPool << ocMissing;
+ aPool >> aStack;
+ GetTracer().TraceFormulaMissingArg();
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ sal_uInt8 nLen = aIn.ReaduInt8(); // Why?
+ OUString aString = aIn.ReadUniString( nLen ); // reads Grbit even if nLen==0
+
+ aStack << aPool.Store( aString );
+ break;
+ }
+ case 0x18: // natural language formula
+ {
+ sal_uInt8 nEptg;
+ sal_uInt16 nCol, nRow;
+ nEptg = aIn.ReaduInt8();
+ switch( nEptg )
+ { // name size ext type
+ case 0x01: // Lel 4 - err
+ aIn.Ignore( 4 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x02: // Rw 4 - ref
+ case 0x03: // Col 4 - ref
+ case 0x06: // RwV 4 - val
+ case 0x07: // ColV 4 - val
+ {
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+ ScAddress aAddr(static_cast<SCCOL>(nCol & 0xFF), static_cast<SCROW>(nRow), aEingPos.Tab());
+ aSRD.InitAddress(aAddr);
+
+ if( nEptg == 0x02 || nEptg == 0x06 )
+ aSRD.SetRowRel(true);
+ else
+ aSRD.SetColRel(true);
+
+ aSRD.SetAddress(GetDocImport().getDoc().GetSheetLimits(), aAddr, aEingPos);
+
+ aStack << aPool.StoreNlf( aSRD );
+
+ break;
+ }
+ case 0x0A: // Radical 13 - ref
+ {
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+ aIn.Ignore( 9 );
+ ScAddress aAddr(static_cast<SCCOL>(nCol & 0xFF), static_cast<SCROW>(nRow), aEingPos.Tab());
+ aSRD.InitAddress(aAddr);
+ aSRD.SetColRel(true);
+ aSRD.SetAddress(GetDocImport().getDoc().GetSheetLimits(), aAddr, aEingPos);
+
+ aStack << aPool.StoreNlf( aSRD );
+
+ break;
+ }
+ case 0x0B: // RadicalS 13 x ref
+ aIn.Ignore( 13 );
+ aExtensions.push_back( EXTENSION_NLR );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x0C: // RwS 4 x ref
+ case 0x0D: // ColS 4 x ref
+ case 0x0E: // RwSV 4 x val
+ case 0x0F: // ColSV 4 x val
+ aIn.Ignore( 4 );
+ aExtensions.push_back( EXTENSION_NLR );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case 0x10: // RadicalLel 4 - err
+ case 0x1D: // SxName 4 - val
+ aIn.Ignore( 4 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ default:
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ }
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ nData++;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ else if( nOpt & 0x10 ) // AttrSum
+ DoMulArgs( ocSum, 1 );
+ break;
+ }
+ case 0x1C: // Error Value [314 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+
+ break;
+ }
+ case 0x1D: // Boolean [315 266]
+ {
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ if( nByte == 0 )
+ aPool << ocFalse << ocOpen << ocClose;
+ else
+ aPool << ocTrue << ocOpen << ocClose;
+ aPool >> aStack;
+ break;
+ }
+ case 0x1E: // Integer [315 266]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aStack << aPool.Store( static_cast<double>(nUINT16) );
+ break;
+ }
+ case 0x1F: // Number [315 266]
+ {
+ double fDouble = aIn.ReadDouble();
+ aStack << aPool.Store( fDouble );
+ break;
+ }
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ {
+ aIn.Ignore( 7 );
+ if( bAllowArrays )
+ {
+ aStack << aPool.StoreMatrix();
+ aExtensions.push_back( EXTENSION_ARRAY );
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ {
+ sal_uInt16 nXclFunc;
+ nXclFunc = aIn.ReaduInt16();
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, pFuncInfo->mnMaxParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ break;
+ }
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ {
+ sal_uInt16 nXclFunc;
+ sal_uInt8 nParamCount;
+ nParamCount = aIn.ReaduInt8();
+ nXclFunc = aIn.ReaduInt16();
+ nParamCount &= 0x7F;
+ if( const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromXclFunc( nXclFunc ) )
+ DoMulArgs( pFuncInfo->meOpCode, nParamCount );
+ else
+ DoMulArgs( ocNoName, 0 );
+ break;
+ }
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ {
+ sal_uInt16 nUINT16 = aIn.ReaduInt16();
+ aIn.Ignore( 2 );
+ const XclImpName* pName = GetNameManager().GetName( nUINT16 );
+ if (pName)
+ {
+ if (pName->IsMacro())
+ // user-defined macro name.
+ aStack << aPool.Store(ocMacro, pName->GetXclName());
+ else
+ aStack << aPool.StoreName(nUINT16, pName->IsGlobal() ? -1 : pName->GetScTab());
+ }
+ break;
+ }
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ {
+ sal_uInt16 nCol, nRow;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRangeNameOrCond );
+
+ switch ( nOp )
+ {
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName );
+ rSRef2.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRangeNameOrCond );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRangeNameOrCond );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ // no information which part is deleted, set all
+ rSRef1.SetColDeleted( true );
+ rSRef1.SetRowDeleted( true );
+ rSRef2.SetColDeleted( true );
+ rSRef2.SetRowDeleted( true );
+ }
+
+ aStack << aPool.Store( aCRD );
+ break;
+ }
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ aExtensions.push_back( EXTENSION_MEMAREA );
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nRow, nCol;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRNorSF );
+
+ aStack << aPool.Store( aSRD );
+ break;
+ }
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ bool bColRel = aCRD.Ref1.IsColRel() || aCRD.Ref2.IsColRel();
+ bool bRowRel = aCRD.Ref1.IsRowRel() || aCRD.Ref2.IsRowRel();
+
+ if( !bColRel && IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( !bRowRel && IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ aStack << aPool.Store( aCRD );
+ break;
+ }
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ {
+ OUString aString = "COMM_EQU_FUNC";
+ sal_uInt8 nByte = aIn.ReaduInt8();
+ aString += OUString::number( nByte );
+ nByte = aIn.ReaduInt8();
+ aStack << aPool.Store( aString );
+ DoMulArgs( ocPush, nByte + 1 );
+ break;
+ }
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ {
+ sal_uInt16 nXtiIndex, nNameIdx;
+ nXtiIndex = aIn.ReaduInt16();
+ nNameIdx = aIn.ReaduInt16();
+ aIn.Ignore( 2 );
+
+ if( rLinkMan.IsSelfRef( nXtiIndex ) )
+ {
+ // internal defined name with explicit sheet, i.e.: =Sheet1!AnyName
+ const XclImpName* pName = GetNameManager().GetName( nNameIdx );
+ if (pName)
+ {
+ if (pName->GetScRangeData())
+ aStack << aPool.StoreName( nNameIdx, pName->IsGlobal() ? -1 : pName->GetScTab());
+ else
+ aStack << aPool.Store(ocMacro, pName->GetXclName());
+ }
+ }
+ else if( const XclImpExtName* pExtName = rLinkMan.GetExternName( nXtiIndex, nNameIdx ) )
+ {
+ switch( pExtName->GetType() )
+ {
+ case xlExtName:
+ {
+ /* FIXME: enable this code for #i4385# once
+ * external name reference can be stored in ODF,
+ * which remains to be done for #i3740#. Until then
+ * create a #NAME? token. */
+#if 1
+ sal_uInt16 nFileId;
+ if (!GetExternalFileIdFromXti(nXtiIndex, nFileId) || !pExtName->HasFormulaTokens())
+ {
+ aStack << aPool.Store(ocNoName, pExtName->GetName());
+ break;
+ }
+
+ aStack << aPool.StoreExtName(nFileId, pExtName->GetName());
+ pExtName->CreateExtNameData(GetDoc(), nFileId);
+#else
+ aStack << aPool.Store( ocNoName, pExtName->GetName() );
+#endif
+ }
+ break;
+
+ case xlExtAddIn:
+ {
+ aStack << aPool.Store( ocExternal, pExtName->GetName() );
+ }
+ break;
+
+ case xlExtDDE:
+ {
+ OUString aApplic, aTopic;
+ if( rLinkMan.GetLinkData( aApplic, aTopic, nXtiIndex ) )
+ {
+ TokenId nPar1 = aPool.Store( aApplic );
+ TokenId nPar2 = aPool.Store( aTopic );
+ nBuf0 = aPool.Store( pExtName->GetName() );
+ aPool << ocDde << ocOpen << nPar1 << ocSep << nPar2 << ocSep
+ << nBuf0 << ocClose;
+ aPool >> aStack;
+ pExtName->CreateDdeData( GetDoc(), aApplic, aTopic );
+ GetDoc().SetLinkFormulaNeedingCheck(true);
+ }
+ }
+ break;
+
+ case xlExtEuroConvert:
+ {
+ aStack << aPool.Store( ocEuroConvert, OUString() );
+ }
+ break;
+ case xlExtOLE:
+ {
+ ExternalTabInfo aExtInfo;
+ if (HandleOleLink(nXtiIndex, *pExtName, aExtInfo))
+ {
+ if (aExtInfo.maRange.aStart == aExtInfo.maRange.aEnd)
+ {
+ // single cell
+ aSRD.InitAddress(aExtInfo.maRange.aStart);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aSRD);
+ }
+ else
+ {
+ // range
+ aCRD.InitRange(aExtInfo.maRange);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ }
+ else
+ aStack << aPool.Store(ocNoName, pExtName->GetName());
+ }
+ break;
+ default:
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ }
+ }
+ else
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ }
+ break;
+ }
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ {
+ sal_uInt16 nIxti, nRw, nGrbitCol;
+ SCTAB nTabFirst, nTabLast;
+
+ nIxti = aIn.ReaduInt16();
+ nRw = aIn.ReaduInt16();
+ nGrbitCol = aIn.ReaduInt16();
+
+ ExternalTabInfo aExtInfo;
+ if (!Read3DTabReference(nIxti, nTabFirst, nTabLast, aExtInfo))
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ }
+
+ aSRD.SetAbsTab(nTabFirst);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel8( nRw, nGrbitCol, aSRD, bRangeNameOrCond );
+
+ switch ( nOp )
+ {
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ // no information which part is deleted, set both
+ aSRD.SetColDeleted( true );
+ aSRD.SetRowDeleted( true );
+ }
+
+ if (aExtInfo.mbExternal)
+ {
+ // nTabFirst and nTabLast are the indices of the referenced
+ // sheets in the SUPBOOK record, hence do not represent
+ // the actual indices of the original sheets since the
+ // SUPBOOK record only stores referenced sheets and skips
+ // the ones that are not referenced.
+
+ if (nTabLast != nTabFirst)
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ else
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aSRD);
+ }
+ else
+ {
+ if ( !ValidTab(nTabFirst))
+ aSRD.SetTabDeleted( true );
+
+ if( nTabLast != nTabFirst )
+ {
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nTabLast);
+ aCRD.Ref2.SetTabDeleted( !ValidTab(nTabLast) );
+ aStack << aPool.Store( aCRD );
+ }
+ else
+ aStack << aPool.Store( aSRD );
+ }
+ break;
+ }
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ {
+ sal_uInt16 nIxti, nRw1, nGrbitCol1, nRw2, nGrbitCol2;
+ SCTAB nTabFirst, nTabLast;
+ nIxti = aIn.ReaduInt16();
+ nRw1 = aIn.ReaduInt16();
+ nRw2 = aIn.ReaduInt16();
+ nGrbitCol1 = aIn.ReaduInt16();
+ nGrbitCol2 = aIn.ReaduInt16();
+
+ ExternalTabInfo aExtInfo;
+ if (!Read3DTabReference(nIxti, nTabFirst, nTabLast, aExtInfo))
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ }
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nTabFirst);
+ rR2.SetAbsTab(nTabLast);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nTabFirst != nTabLast );
+
+ ExcRelToScRel8( nRw1, nGrbitCol1, aCRD.Ref1, bRangeNameOrCond );
+ ExcRelToScRel8( nRw2, nGrbitCol2, aCRD.Ref2, bRangeNameOrCond );
+
+ if( IsComplColRange( nGrbitCol1, nGrbitCol2 ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRw1, nRw2 ) )
+ SetComplRow( aCRD );
+
+ switch ( nOp )
+ {
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ // no information which part is deleted, set all
+ rR1.SetColDeleted( true );
+ rR1.SetRowDeleted( true );
+ rR2.SetColDeleted( true );
+ rR2.SetRowDeleted( true );
+ }
+
+ if (aExtInfo.mbExternal)
+ {
+ aStack << aPool.StoreExtRef(aExtInfo.mnFileId, aExtInfo.maTabName, aCRD);
+ }
+ else
+ {
+ if ( !ValidTab(nTabFirst) )
+ rR1.SetTabDeleted( true );
+ if ( !ValidTab(nTabLast) )
+ rR2.SetTabDeleted( true );
+
+ aStack << aPool.Store( aCRD );
+ }
+ break;
+ }
+ default:
+ bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Ni;
+ }
+ else if( aIn.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::Count;
+ }
+ else if( bArrayFormula )
+ {
+ rpTokArray = nullptr;
+ eRet = ConvErr::OK;
+ }
+ else
+ {
+ rpTokArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ eRet = ConvErr::OK;
+ }
+
+ aIn.Seek( nEndPos );
+
+ if( eRet == ConvErr::OK)
+ ReadExtensions( aExtensions, aIn );
+
+ return eRet;
+}
+
+// stream seeks to first byte after <nFormulaLen>
+ConvErr ExcelToSc8::Convert( ScRangeListTabs& rRangeList, XclImpStream& aIn, std::size_t nFormulaLen,
+ SCTAB nTab, const FORMULA_TYPE eFT )
+{
+ sal_uInt8 nOp, nLen;
+ bool bError = false;
+ const bool bCondFormat = eFT == FT_CondFormat;
+ const bool bRangeName = eFT == FT_RangeName || bCondFormat;
+ const bool bSharedFormula = eFT == FT_SharedFormula;
+ const bool bRNorSF = bRangeName || bSharedFormula;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+
+ if( nFormulaLen == 0 )
+ return ConvErr::OK;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nFormulaLen;
+
+ while( (aIn.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = aIn.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp ) // book page:
+ { // SDK4 SDK5
+ case 0x01: // Array Formula [325 ]
+ // Array Formula or Shared Formula [ 277]
+ aIn.Ignore( 4 );
+ break;
+ case 0x02: // Data Table [325 277]
+ aIn.Ignore( 4 );
+ break;
+ case 0x03: // Addition [312 264]
+ case 0x04: // Subtraction [313 264]
+ case 0x05: // Multiplication [313 264]
+ case 0x06: // Division [313 264]
+ case 0x07: // Exponetiation [313 265]
+ case 0x08: // Concatenation [313 265]
+ case 0x09: // Less Than [313 265]
+ case 0x0A: // Less Than or Equal [313 265]
+ case 0x0B: // Equal [313 265]
+ case 0x0C: // Greater Than or Equal [313 265]
+ case 0x0D: // Greater Than [313 265]
+ case 0x0E: // Not Equal [313 265]
+ case 0x0F: // Intersection [314 265]
+ case 0x10: // Union [314 265]
+ case 0x11: // Range [314 265]
+ case 0x12: // Unary Plus [312 264]
+ case 0x13: // Unary Minus [312 264]
+ case 0x14: // Percent Sign [312 264]
+ case 0x15: // Parenthesis [326 278]
+ case 0x16: // Missing Argument [314 266]
+ break;
+ case 0x17: // String Constant [314 266]
+ nLen = aIn.ReaduInt8(); // Why?
+
+ aIn.IgnoreUniString( nLen ); // reads Grbit even if nLen==0
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData(0), nFactor(0);
+
+ sal_uInt8 nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ nFactor = 2;
+
+ if( nOpt & 0x04 )
+ {
+ // nFactor -> skip bytes or words AttrChoose
+ ++nData;
+ aIn.Ignore(static_cast<std::size_t>(nData) * nFactor);
+ }
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ aIn.Ignore( 1 );
+ break;
+ case 0x1E: // Integer [315 266]
+ aIn.Ignore( 2 );
+ break;
+ case 0x1F: // Number [315 266]
+ aIn.Ignore( 8 );
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ aIn.Ignore( 7 );
+ break;
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ aIn.Ignore( 2 );
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ aIn.Ignore( 3 );
+ break;
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ aIn.Ignore( 4 );
+ break;
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ {
+ sal_uInt16 nCol, nRow;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName && !bCondFormat );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRangeName );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ {
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+ ScSingleRefData &rSRef1 = aCRD.Ref1;
+ ScSingleRefData &rSRef2 = aCRD.Ref2;
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+
+ rSRef1.SetRelTab(0);
+ rSRef2.SetRelTab(0);
+ rSRef1.SetFlag3D( bRangeName && !bCondFormat );
+ rSRef2.SetFlag3D( bRangeName && !bCondFormat );
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRangeName );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ aIn.Ignore( 6 ); // There isn't any more
+ break;
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ aIn.Ignore( 2 ); // There isn't any more
+ break;
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ aIn.Ignore( 3 );
+ break;
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ aIn.Ignore( 6 );
+ break;
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ {
+ sal_uInt16 nRow, nCol;
+
+ nRow = aIn.ReaduInt16();
+ nCol = aIn.ReaduInt16();
+
+ aSRD.SetRelTab(0);
+ aSRD.SetFlag3D( bRangeName );
+
+ ExcRelToScRel8( nRow, nCol, aSRD, bRNorSF );
+
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ { // Area Reference Within a Shared Formula[ 274]
+ sal_uInt16 nRowFirst, nRowLast;
+ sal_uInt16 nColFirst, nColLast;
+
+ aCRD.Ref1.SetRelTab(0);
+ aCRD.Ref2.SetRelTab(0);
+ aCRD.Ref1.SetFlag3D( bRangeName );
+ aCRD.Ref2.SetFlag3D( bRangeName );
+
+ nRowFirst = aIn.ReaduInt16();
+ nRowLast = aIn.ReaduInt16();
+ nColFirst = aIn.ReaduInt16( );
+ nColLast = aIn.ReaduInt16();
+
+ ExcRelToScRel8( nRowFirst, nColFirst, aCRD.Ref1, bRNorSF );
+ ExcRelToScRel8( nRowLast, nColLast, aCRD.Ref2, bRNorSF );
+
+ if( IsComplColRange( nColFirst, nColLast ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRowFirst, nRowLast ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ break;
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ aIn.Ignore( 2 );
+ break;
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ aIn.Ignore( 24 );
+ break;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ {
+ sal_uInt16 nIxti, nRw, nGrbitCol;
+
+ nIxti = aIn.ReaduInt16();
+ nRw = aIn.ReaduInt16();
+ nGrbitCol = aIn.ReaduInt16();
+
+ SCTAB nFirstScTab, nLastScTab;
+ if( rLinkMan.GetScTabRange( nFirstScTab, nLastScTab, nIxti ) )
+ {
+ aSRD.SetAbsTab(nFirstScTab);
+ aSRD.SetFlag3D(true);
+
+ ExcRelToScRel8( nRw, nGrbitCol, aSRD, bRangeName );
+
+ if( nFirstScTab != nLastScTab )
+ {
+ aCRD.Ref1 = aSRD;
+ aCRD.Ref2 = aSRD;
+ aCRD.Ref2.SetAbsTab(nLastScTab);
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ else
+ rRangeList.Append(aSRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+ break;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ {
+ sal_uInt16 nIxti, nRw1, nGrbitCol1, nRw2, nGrbitCol2;
+
+ nIxti = aIn.ReaduInt16();
+ nRw1 = aIn.ReaduInt16();
+ nRw2 = aIn.ReaduInt16();
+ nGrbitCol1 = aIn.ReaduInt16();
+ nGrbitCol2 = aIn.ReaduInt16();
+
+ SCTAB nFirstScTab, nLastScTab;
+ if( rLinkMan.GetScTabRange( nFirstScTab, nLastScTab, nIxti ) )
+ {
+ ScSingleRefData &rR1 = aCRD.Ref1;
+ ScSingleRefData &rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nFirstScTab);
+ rR2.SetAbsTab(nLastScTab);
+ rR1.SetFlag3D(true);
+ rR2.SetFlag3D( nFirstScTab != nLastScTab );
+
+ ExcRelToScRel8( nRw1, nGrbitCol1, aCRD.Ref1, bRangeName );
+ ExcRelToScRel8( nRw2, nGrbitCol2, aCRD.Ref2, bRangeName );
+
+ if( IsComplColRange( nGrbitCol1, nGrbitCol2 ) )
+ SetComplCol( aCRD );
+ else if( IsComplRowRange( nRw1, nRw2 ) )
+ SetComplRow( aCRD );
+
+ rRangeList.Append(aCRD.toAbs(GetDocImport().getDoc(), aEingPos), nTab);
+ }
+ }
+ break;
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ aIn.Ignore( 6 );
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ aIn.Ignore( 10 );
+ break;
+ default:
+ bError = true;
+ }
+ bError |= !aIn.IsValid();
+ }
+
+ ConvErr eRet;
+
+ if( bError )
+ eRet = ConvErr::Ni;
+ else if( aIn.GetRecPos() != nEndPos )
+ eRet = ConvErr::Count;
+ else
+ eRet = ConvErr::OK;
+
+ aIn.Seek( nEndPos );
+ return eRet;
+}
+
+void ExcelToSc8::ConvertExternName( std::unique_ptr<ScTokenArray>& rpArray, XclImpStream& rStrm, std::size_t nFormulaLen,
+ const OUString& rUrl, const vector<OUString>& rTabNames )
+{
+ if( !GetDocShell() )
+ return;
+
+ OUString aFileUrl = ScGlobal::GetAbsDocName(rUrl, GetDocShell());
+
+ sal_uInt8 nOp, nByte;
+ bool bError = false;
+
+ ScSingleRefData aSRD;
+ ScComplexRefData aCRD;
+
+ if (nFormulaLen == 0)
+ {
+ aPool.Store("-/-");
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ return;
+ }
+
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFileUrl);
+ sal_uInt16 nTabCount = static_cast< sal_uInt16 >( rTabNames.size() );
+
+ std::size_t nEndPos = rStrm.GetRecPos() + nFormulaLen;
+
+ while( (rStrm.GetRecPos() < nEndPos) && !bError )
+ {
+ nOp = rStrm.ReaduInt8();
+
+ // always reset flags
+ aSRD.InitFlags();
+ aCRD.InitFlags();
+
+ switch( nOp )
+ {
+ case 0x1C: // Error Value
+ {
+ nByte = rStrm.ReaduInt8();
+ DefTokenId eOc;
+ switch( nByte )
+ {
+ case EXC_ERR_NULL:
+ case EXC_ERR_DIV0:
+ case EXC_ERR_VALUE:
+ case EXC_ERR_REF:
+ case EXC_ERR_NAME:
+ case EXC_ERR_NUM: eOc = ocStop; break;
+ case EXC_ERR_NA: eOc = ocNotAvail; break;
+ default: eOc = ocNoName;
+ }
+ aPool << eOc;
+ if( eOc != ocStop )
+ aPool << ocOpen << ocClose;
+ aPool >> aStack;
+ }
+ break;
+ case 0x3A:
+ {
+ // cell reference in external range name
+ sal_uInt16 nExtTab1, nExtTab2, nRow, nGrbitCol;
+ nExtTab1 = rStrm.ReaduInt16();
+ nExtTab2 = rStrm.ReaduInt16();
+ nRow = rStrm.ReaduInt16();
+ nGrbitCol = rStrm.ReaduInt16();
+ if (nExtTab1 >= nTabCount || nExtTab2 >= nTabCount)
+ {
+ bError = true;
+ break;
+ }
+
+ aSRD.SetAbsTab(nExtTab1);
+ aSRD.SetFlag3D(true);
+ ExcRelToScRel8(nRow, nGrbitCol, aSRD, true);
+ aCRD.Ref1 = aCRD.Ref2 = aSRD;
+ OUString aTabName = rTabNames[nExtTab1];
+
+ if (nExtTab1 == nExtTab2)
+ {
+ // single cell reference
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aSRD);
+ }
+ else
+ {
+ // area reference
+ aCRD.Ref2.SetAbsTab(nExtTab2);
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aCRD);
+ }
+ }
+ break;
+ case 0x3B:
+ {
+ // area reference
+ sal_uInt16 nExtTab1, nExtTab2, nRow1, nRow2, nGrbitCol1, nGrbitCol2;
+ nExtTab1 = rStrm.ReaduInt16();
+ nExtTab2 = rStrm.ReaduInt16();
+ nRow1 = rStrm.ReaduInt16();
+ nRow2 = rStrm.ReaduInt16();
+ nGrbitCol1 = rStrm.ReaduInt16();
+ nGrbitCol2 = rStrm.ReaduInt16();
+
+ if (nExtTab1 >= nTabCount || nExtTab2 >= nTabCount)
+ {
+ bError = true;
+ break;
+ }
+
+ ScSingleRefData& rR1 = aCRD.Ref1;
+ ScSingleRefData& rR2 = aCRD.Ref2;
+
+ rR1.SetAbsTab(nExtTab1);
+ rR1.SetFlag3D(true);
+ ExcRelToScRel8(nRow1, nGrbitCol1, rR1, true);
+
+ rR2.SetAbsTab(nExtTab2);
+ rR2.SetFlag3D(true);
+ ExcRelToScRel8(nRow2, nGrbitCol2, rR2, true);
+
+ OUString aTabName = rTabNames[nExtTab1];
+ aStack << aPool.StoreExtRef(nFileId, aTabName, aCRD);
+ }
+ break;
+ default:
+ bError = true;
+ }
+ bError |= !rStrm.IsValid();
+ }
+
+ if( bError )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+ else if( rStrm.GetRecPos() != nEndPos )
+ {
+ aPool << ocBad;
+ aPool >> aStack;
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+ else
+ {
+ rpArray = aPool.GetTokenArray( GetDocImport().getDoc(), aStack.Get());
+ }
+
+ rStrm.Seek(nEndPos);
+}
+
+void ExcelToSc8::ExcRelToScRel8( sal_uInt16 nRow, sal_uInt16 nC, ScSingleRefData &rSRD, const bool bName )
+{
+ const bool bColRel = ( nC & 0x4000 ) != 0;
+ const bool bRowRel = ( nC & 0x8000 ) != 0;
+ const sal_uInt8 nCol = static_cast<sal_uInt8>(nC);
+
+ if( bName )
+ {
+ // C O L
+ if( bColRel )
+ {
+ SCCOL nRelCol = static_cast<sal_Int8>(nC);
+ sal_Int16 nDiff = aEingPos.Col() + nRelCol;
+ if ( nDiff < 0)
+ {
+ // relative column references wrap around
+ nRelCol = static_cast<sal_Int16>(256 + static_cast<int>(nRelCol));
+ }
+ rSRD.SetRelCol(nRelCol);
+ }
+ else
+ rSRD.SetAbsCol(static_cast<SCCOL>(nCol));
+
+ // R O W
+ if( bRowRel )
+ {
+ SCROW nRelRow = static_cast<sal_Int16>(nRow);
+ sal_Int32 nDiff = aEingPos.Row() + nRelRow;
+ if (nDiff < 0)
+ {
+ // relative row references wrap around
+ nRelRow = 65536 + nRelRow;
+ }
+ rSRD.SetRelRow(nRelRow);
+ }
+ else
+ rSRD.SetAbsRow(std::min( static_cast<SCROW>(nRow), GetDoc().MaxRow()));
+ }
+ else
+ {
+ // C O L
+ if ( bColRel )
+ rSRD.SetRelCol(static_cast<SCCOL>(nCol) - aEingPos.Col());
+ else
+ rSRD.SetAbsCol(nCol);
+
+ // R O W
+ if ( bRowRel )
+ rSRD.SetRelRow(static_cast<SCROW>(nRow) - aEingPos.Row());
+ else
+ rSRD.SetAbsRow(nRow);
+ }
+}
+
+// stream seeks to first byte after <nLen>
+void ExcelToSc8::GetAbsRefs( ScRangeList& r, XclImpStream& aIn, std::size_t nLen )
+{
+ sal_uInt8 nOp;
+ sal_uInt16 nRow1, nRow2, nCol1, nCol2;
+ SCTAB nTab1, nTab2;
+ sal_uInt16 nIxti;
+
+ std::size_t nSeek;
+
+ std::size_t nEndPos = aIn.GetRecPos() + nLen;
+
+ while( aIn.IsValid() && (aIn.GetRecPos() < nEndPos) )
+ {
+ nOp = aIn.ReaduInt8();
+ nSeek = 0;
+
+ switch( nOp )
+ {
+ case 0x44:
+ case 0x64:
+ case 0x24: // Cell Reference [319 270]
+ case 0x4C:
+ case 0x6C:
+ case 0x2C: // Cell Reference Within a Name [323 ]
+ // Cell Reference Within a Shared Formula[ 273]
+ nRow1 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x45:
+ case 0x65:
+ case 0x25: // Area Reference [320 270]
+ case 0x4D:
+ case 0x6D:
+ case 0x2D: // Area Reference Within a Name [324 ]
+ // Area Reference Within a Shared Formula[ 274]
+ nRow1 = aIn.ReaduInt16();
+ nRow2 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+ nCol2 = aIn.ReaduInt16();
+
+ nTab1 = nTab2 = GetCurrScTab();
+ goto _common;
+ case 0x5A:
+ case 0x7A:
+ case 0x3A: // 3-D Cell Reference [ 275]
+ nIxti = aIn.ReaduInt16();
+ nRow1 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+
+ nRow2 = nRow1;
+ nCol2 = nCol1;
+
+ goto _3d_common;
+ case 0x5B:
+ case 0x7B:
+ case 0x3B: // 3-D Area Reference [ 276]
+ nIxti = aIn.ReaduInt16();
+ nRow1 = aIn.ReaduInt16();
+ nRow2 = aIn.ReaduInt16();
+ nCol1 = aIn.ReaduInt16();
+ nCol2 = aIn.ReaduInt16();
+
+ _3d_common:
+ // skip references to deleted sheets
+ if( !rLinkMan.GetScTabRange( nTab1, nTab2, nIxti ) || !ValidTab( nTab1 ) || !ValidTab( nTab2 ) )
+ break;
+
+ goto _common;
+ _common:
+ // do not check abs/rel flags, linked controls have set them!
+ {
+ ScRange aScRange;
+ nCol1 &= 0x3FFF;
+ nCol2 &= 0x3FFF;
+ if( GetAddressConverter().ConvertRange( aScRange, XclRange( nCol1, nRow1, nCol2, nRow2 ), nTab1, nTab2, true ) )
+ r.push_back( aScRange );
+ }
+ break;
+ case 0x1C: // Error Value [314 266]
+ case 0x1D: // Boolean [315 266]
+ nSeek = 1;
+ break;
+ case 0x1E: // Integer [315 266]
+ case 0x41:
+ case 0x61:
+ case 0x21: // Function, Fixed Number of Arguments [333 282]
+ case 0x49:
+ case 0x69:
+ case 0x29: // Variable Reference Subexpression [331 281]
+ case 0x4E:
+ case 0x6E:
+ case 0x2E: // Reference Subexpression Within a Name [332 282]
+ case 0x4F:
+ case 0x6F:
+ case 0x2F: // Incomplete Reference Subexpression... [332 282]
+ case 0x58:
+ case 0x78:
+ case 0x38: // Command-Equivalent Function [333 ]
+ nSeek = 2;
+ break;
+ case 0x42:
+ case 0x62:
+ case 0x22: // Function, Variable Number of Arg. [333 283]
+ nSeek = 3;
+ break;
+ case 0x01: // Array Formula [325 ]
+ case 0x02: // Data Table [325 277]
+ case 0x43:
+ case 0x63:
+ case 0x23: // Name [318 269]
+ case 0x4A:
+ case 0x6A:
+ case 0x2A: // Deleted Cell Reference [323 273]
+ nSeek = 4;
+ break;
+ case 0x46:
+ case 0x66:
+ case 0x26: // Constant Reference Subexpression [321 271]
+ case 0x47:
+ case 0x67:
+ case 0x27: // Erroneous Constant Reference Subexpr. [322 272]
+ case 0x48:
+ case 0x68:
+ case 0x28: // Incomplete Constant Reference Subexpr.[331 281]
+ case 0x5C:
+ case 0x7C:
+ case 0x3C: // Deleted 3-D Cell Reference [ 277]
+ case 0x59:
+ case 0x79:
+ case 0x39: // Name or External Name [ 275]
+ nSeek = 6;
+ break;
+ case 0x40:
+ case 0x60:
+ case 0x20: // Array Constant [317 268]
+ nSeek = 7;
+ break;
+ case 0x1F: // Number [315 266]
+ case 0x4B:
+ case 0x6B:
+ case 0x2B: // Deleted Area Reference [323 273]
+ nSeek = 8;
+ break;
+ case 0x5D:
+ case 0x7D:
+ case 0x3D: // Deleted 3-D Area Reference [ 277]
+ nSeek = 10;
+ break;
+ case 0x17: // String Constant [314 266]
+ {
+ sal_uInt8 nStrLen;
+ nStrLen = aIn.ReaduInt8();
+ aIn.IgnoreUniString( nStrLen ); // reads Grbit even if nLen==0
+ nSeek = 0;
+ }
+ break;
+ case 0x19: // Special Attribute [327 279]
+ {
+ sal_uInt16 nData;
+ sal_uInt8 nOpt;
+ nOpt = aIn.ReaduInt8();
+ nData = aIn.ReaduInt16();
+ if( nOpt & 0x04 )
+ {// nFactor -> skip bytes or words AttrChoose
+ nData++;
+ nSeek = nData * 2;
+ }
+ }
+ break;
+ }
+
+ aIn.Ignore( nSeek );
+ }
+ aIn.Seek( nEndPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excimp8.cxx b/sc/source/filter/excel/excimp8.cxx
new file mode 100644
index 000000000..d5db209a1
--- /dev/null
+++ b/sc/source/filter/excel/excimp8.cxx
@@ -0,0 +1,817 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+
+#include <excimp8.hxx>
+
+#include <scitems.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <unotools/fltrcfg.hxx>
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docinf.hxx>
+#include <sot/storage.hxx>
+#include <svl/sharedstringpool.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/ustring.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <document.hxx>
+#include <attrib.hxx>
+#include <dbdata.hxx>
+#include <globalnames.hxx>
+#include <docoptio.hxx>
+#include <xihelper.hxx>
+#include <xicontent.hxx>
+#include <xilink.hxx>
+#include <xiescher.hxx>
+#include <xistyle.hxx>
+#include <excdefs.hxx>
+
+#include <excform.hxx>
+#include <queryentry.hxx>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <cppuhelper/implbase.hxx>
+#include "xltoolbar.hxx"
+#include <oox/ole/vbaproject.hxx>
+#include <oox/ole/olestorage.hxx>
+
+using namespace com::sun::star;
+using namespace ::comphelper;
+
+//OleNameOverrideContainer
+
+namespace {
+
+class OleNameOverrideContainer : public ::cppu::WeakImplHelper< container::XNameContainer >
+{
+private:
+ typedef std::unordered_map< OUString, uno::Reference< container::XIndexContainer > > NamedIndexToOleName;
+ NamedIndexToOleName IdToOleNameHash;
+ ::osl::Mutex m_aMutex;
+public:
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override { return cppu::UnoType<container::XIndexContainer>::get(); }
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return ( !IdToOleNameHash.empty() );
+ }
+ // XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !hasByName(aName) )
+ throw container::NoSuchElementException();
+ return uno::Any( IdToOleNameHash[ aName ] );
+ }
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return comphelper::mapKeysToSequence( IdToOleNameHash);
+ }
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return ( IdToOleNameHash.find( aName ) != IdToOleNameHash.end() );
+ }
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( hasByName( aName ) )
+ throw container::ElementExistException();
+ uno::Reference< container::XIndexContainer > xElement;
+ if ( ! ( aElement >>= xElement ) )
+ throw lang::IllegalArgumentException();
+ IdToOleNameHash[ aName ] = xElement;
+ }
+ virtual void SAL_CALL removeByName( const OUString& aName ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( IdToOleNameHash.erase( aName ) == 0 )
+ throw container::NoSuchElementException();
+ }
+ virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !hasByName( aName ) )
+ throw container::NoSuchElementException();
+ uno::Reference< container::XIndexContainer > xElement;
+ if ( ! ( aElement >>= xElement ) )
+ throw lang::IllegalArgumentException();
+ IdToOleNameHash[ aName ] = xElement;
+ }
+};
+
+/** Future Record Type header.
+ @return whether read rt matches nRecordID
+ */
+bool readFrtHeader( XclImpStream& rStrm, sal_uInt16 nRecordID )
+{
+ sal_uInt16 nRt = rStrm.ReaduInt16();
+ rStrm.Ignore(10); // grbitFrt (2 bytes) and reserved (8 bytes)
+ return nRt == nRecordID;
+}
+
+}
+
+ImportExcel8::ImportExcel8( XclImpRootData& rImpData, SvStream& rStrm ) :
+ ImportExcel( rImpData, rStrm )
+{
+ // replace BIFF2-BIFF5 formula importer with BIFF8 formula importer
+ pFormConv.reset(new ExcelToSc8( GetRoot() ));
+ pExcRoot->pFmlaConverter = pFormConv.get();
+}
+
+ImportExcel8::~ImportExcel8()
+{
+}
+
+void ImportExcel8::Calccount()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIterCount( aIn.ReaduInt16() );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Precision()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetCalcAsShown( aIn.ReaduInt16() == 0 );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Delta()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIterEps( aIn.ReadDouble() );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Iteration()
+{
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetIter( aIn.ReaduInt16() == 1 );
+ rD.SetDocOptions( aOpt );
+}
+
+void ImportExcel8::Boundsheet()
+{
+ sal_uInt8 nLen;
+ sal_uInt16 nGrbit;
+
+ aIn.DisableDecryption();
+ maSheetOffsets.push_back( aIn.ReaduInt32() );
+ aIn.EnableDecryption();
+ nGrbit = aIn.ReaduInt16();
+ nLen = aIn.ReaduInt8();
+
+ OUString aName( aIn.ReadUniString( nLen ) );
+ GetTabInfo().AppendXclTabName( aName, nBdshtTab );
+
+ SCTAB nScTab = nBdshtTab;
+ if( nScTab > 0 )
+ {
+ OSL_ENSURE( !rD.HasTable( nScTab ), "ImportExcel8::Boundsheet - sheet exists already" );
+ rD.MakeTable( nScTab );
+ }
+
+ if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) )
+ rD.SetVisible( nScTab, false );
+
+ if( !rD.RenameTab( nScTab, aName ) )
+ {
+ rD.CreateValidTabName( aName );
+ rD.RenameTab( nScTab, aName );
+ }
+
+ nBdshtTab++;
+}
+
+void ImportExcel8::Scenman()
+{
+ sal_uInt16 nLastDispl;
+
+ aIn.Ignore( 4 );
+ nLastDispl = aIn.ReaduInt16();
+
+ maScenList.nLastScenario = nLastDispl;
+}
+
+void ImportExcel8::Scenario()
+{
+ maScenList.aEntries.push_back( std::make_unique<ExcScenario>( aIn, *pExcRoot ) );
+}
+
+void ImportExcel8::Labelsst()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXF;
+ sal_uInt32 nSst;
+
+ aIn >> aXclPos;
+ nXF = aIn.ReaduInt16();
+ nSst = aIn.ReaduInt32( );
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ GetXFRangeBuffer().SetXF( aScPos, nXF );
+ const XclImpString* pXclStr = GetSst().GetString(nSst);
+ if (pXclStr)
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, *this, *pXclStr, nXF);
+ }
+}
+
+void ImportExcel8::FeatHdr()
+{
+ if (!readFrtHeader( aIn, 0x0867))
+ return;
+
+ // Feature type (isf) can be EXC_ISFPROTECTION, EXC_ISFFEC2 or
+ // EXC_ISFFACTOID.
+ sal_uInt16 nFeatureType = aIn.ReaduInt16();
+ if (nFeatureType != EXC_ISFPROTECTION)
+ // We currently only support import of enhanced protection data.
+ return;
+
+ aIn.Ignore(1); // always 1
+
+ GetSheetProtectBuffer().ReadOptions( aIn, GetCurrScTab() );
+}
+
+void ImportExcel8::Feat()
+{
+ if (!readFrtHeader( aIn, 0x0868))
+ return;
+
+ // Feature type (isf) can be EXC_ISFPROTECTION, EXC_ISFFEC2 or
+ // EXC_ISFFACTOID.
+ sal_uInt16 nFeatureType = aIn.ReaduInt16();
+ if (nFeatureType != EXC_ISFPROTECTION)
+ // We currently only support import of enhanced protection data.
+ return;
+
+ aIn.Ignore(5); // reserved1 (1 byte) and reserved2 (4 bytes)
+
+ sal_uInt16 nCref = aIn.ReaduInt16(); // number of ref elements
+ aIn.Ignore(4); // size if EXC_ISFFEC2, else 0 and to be ignored
+ aIn.Ignore(2); // reserved3 (2 bytes)
+
+ ScEnhancedProtection aProt;
+ if (nCref)
+ {
+ XclRangeList aRefs;
+ aRefs.Read( aIn, true, nCref);
+ if (!aRefs.empty())
+ {
+ aProt.maRangeList = new ScRangeList;
+ GetAddressConverter().ConvertRangeList( *aProt.maRangeList, aRefs, GetCurrScTab(), false);
+ }
+ }
+
+ // FeatProtection structure follows in record.
+
+ aProt.mnAreserved = aIn.ReaduInt32();
+ aProt.mnPasswordVerifier = aIn.ReaduInt32();
+ aProt.maTitle = aIn.ReadUniString();
+ if ((aProt.mnAreserved & 0x00000001) == 0x00000001)
+ {
+ sal_uInt32 nCbSD = aIn.ReaduInt32();
+ // TODO: could here be some sanity check applied to not allocate 4GB?
+ aProt.maSecurityDescriptor.resize( nCbSD);
+ std::size_t nRead = aIn.Read(aProt.maSecurityDescriptor.data(), nCbSD);
+ if (nRead < nCbSD)
+ aProt.maSecurityDescriptor.resize( nRead);
+ }
+
+ GetSheetProtectBuffer().AppendEnhancedProtection( aProt, GetCurrScTab() );
+}
+
+void ImportExcel8::ReadBasic()
+{
+ SfxObjectShell* pShell = GetDocShell();
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if( !pShell || !xRootStrg.is() )
+ return;
+
+ try
+ {
+ // #FIXME need to get rid of this, we can also do this from within oox
+ // via the "ooo.vba.VBAGlobals" service
+ if( ( rFilterOpt.IsLoadExcelBasicCode() ||
+ rFilterOpt.IsLoadExcelBasicStorage() ) &&
+ rFilterOpt.IsLoadExcelBasicExecutable() )
+ {
+ // see if we have the XCB stream
+ tools::SvRef<SotStorageStream> xXCB = xRootStrg->OpenSotStream( "XCB", StreamMode::STD_READ );
+ if ( xXCB.is()|| ERRCODE_NONE == xXCB->GetError() )
+ {
+ ScCTBWrapper wrapper;
+ if ( wrapper.Read( *xXCB ) )
+ {
+#ifdef DEBUG_SC_EXCEL
+ wrapper.Print( stderr );
+#endif
+ wrapper.ImportCustomToolBar( *pShell );
+ }
+ }
+ }
+ try
+ {
+ uno::Reference< uno::XComponentContext > aCtx( ::comphelper::getProcessComponentContext() );
+ SfxMedium& rMedium = GetMedium();
+ uno::Reference< io::XInputStream > xIn = rMedium.GetInputStream();
+ oox::ole::OleStorage root( aCtx, xIn, false );
+ oox::StorageRef vbaStg = root.openSubStorage( "_VBA_PROJECT_CUR", false );
+ if ( vbaStg )
+ {
+ oox::ole::VbaProject aVbaPrj( aCtx, pShell->GetModel(), u"Calc" );
+ // collect names of embedded form controls, as specified in the VBA project
+ uno::Reference< container::XNameContainer > xOleNameOverrideSink( new OleNameOverrideContainer );
+ aVbaPrj.setOleOverridesSink( xOleNameOverrideSink );
+ aVbaPrj.importVbaProject( *vbaStg );
+ GetObjectManager().SetOleNameOverrideInfo( xOleNameOverrideSink );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+void ImportExcel8::EndSheet()
+{
+ ImportExcel::EndSheet();
+ GetCondFormatManager().Apply();
+ GetValidationManager().Apply();
+}
+
+void ImportExcel8::PostDocLoad()
+{
+#if HAVE_FEATURE_SCRIPTING
+ // reading basic has been delayed until sheet objects (codenames etc.) are read
+ if( HasBasic() )
+ ReadBasic();
+#endif
+ // #i11776# filtered ranges before outlines and hidden rows
+ if( pExcRoot->pAutoFilterBuffer )
+ pExcRoot->pAutoFilterBuffer->Apply();
+
+ GetWebQueryBuffer().Apply(); //TODO: test if extant
+ GetSheetProtectBuffer().Apply();
+ GetDocProtectBuffer().Apply();
+
+ ImportExcel::PostDocLoad();
+
+ // check scenarios; Attention: This increases the table count of the document!!
+ if( !rD.IsClipboard() && !maScenList.aEntries.empty() )
+ {
+ rD.UpdateChartListenerCollection(); // references in charts must be updated
+
+ maScenList.Apply( GetRoot() );
+ }
+
+ // read doc info (no docshell while pasting from clipboard)
+ SfxObjectShell* pShell = GetDocShell();
+ if(!pShell)
+ return;
+
+ // BIFF5+ without storage is possible
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ if( xRootStrg.is() ) try
+ {
+ uno::Reference< document::XDocumentPropertiesSupplier > xDPS( pShell->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< document::XDocumentProperties > xDocProps( xDPS->getDocumentProperties(), uno::UNO_SET_THROW );
+ sfx2::LoadOlePropertySet( xDocProps, xRootStrg.get() );
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ // #i45843# Pivot tables are now handled outside of PostDocLoad, so they are available
+ // when formula cells are calculated, for the GETPIVOTDATA function.
+}
+
+// autofilter
+
+void ImportExcel8::FilterMode()
+{
+ // The FilterMode record exists: if either the AutoFilter
+ // record exists or an Advanced Filter is saved and stored
+ // in the sheet. Thus if the FilterMode records only exists
+ // then the latter is true...
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ pData->SetAutoOrAdvanced();
+}
+
+void ImportExcel8::AutoFilterInfo()
+{
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ {
+ pData->SetAdvancedRange( nullptr );
+ pData->Activate();
+ }
+}
+
+void ImportExcel8::AutoFilter()
+{
+ if( !pExcRoot->pAutoFilterBuffer ) return;
+
+ XclImpAutoFilterData* pData = pExcRoot->pAutoFilterBuffer->GetByTab( GetCurrScTab() );
+ if( pData )
+ pData->ReadAutoFilter(aIn, GetDoc().GetSharedStringPool());
+}
+
+XclImpAutoFilterData::XclImpAutoFilterData( RootData* pRoot, const ScRange& rRange ) :
+ ExcRoot( pRoot ),
+ pCurrDBData(nullptr),
+ bActive( false ),
+ bCriteria( false ),
+ bAutoOrAdvanced(false)
+{
+ aParam.nCol1 = rRange.aStart.Col();
+ aParam.nRow1 = rRange.aStart.Row();
+ aParam.nTab = rRange.aStart.Tab();
+ aParam.nCol2 = rRange.aEnd.Col();
+ aParam.nRow2 = rRange.aEnd.Row();
+
+ aParam.bInplace = true;
+
+}
+
+namespace {
+
+OUString CreateFromDouble( double fVal )
+{
+ return rtl::math::doubleToUString(fVal,
+ rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0], true);
+}
+
+}
+
+void XclImpAutoFilterData::SetCellAttribs()
+{
+ ScDocument& rDoc = pExcRoot->pIR->GetDoc();
+ for ( SCCOL nCol = StartCol(); nCol <= EndCol(); nCol++ )
+ {
+ ScMF nFlag = rDoc.GetAttr( nCol, StartRow(), Tab(), ATTR_MERGE_FLAG )->GetValue();
+ rDoc.ApplyAttr( nCol, StartRow(), Tab(), ScMergeFlagAttr( nFlag | ScMF::Auto) );
+ }
+}
+
+void XclImpAutoFilterData::InsertQueryParam()
+{
+ if (!pCurrDBData)
+ return;
+
+ ScRange aAdvRange;
+ bool bHasAdv = pCurrDBData->GetAdvancedQuerySource( aAdvRange );
+ if( bHasAdv )
+ pExcRoot->pIR->GetDoc().CreateQueryParam(aAdvRange, aParam);
+
+ pCurrDBData->SetQueryParam( aParam );
+ if( bHasAdv )
+ pCurrDBData->SetAdvancedQuerySource( &aAdvRange );
+ else
+ {
+ pCurrDBData->SetAutoFilter( true );
+ SetCellAttribs();
+ }
+}
+
+static void ExcelQueryToOooQuery( OUString& aStr, ScQueryEntry& rEntry )
+{
+ if (rEntry.eOp != SC_EQUAL && rEntry.eOp != SC_NOT_EQUAL)
+ return;
+
+ sal_Int32 nLen = aStr.getLength();
+ sal_Unicode nStart = aStr[0];
+ sal_Unicode nEnd = aStr[ nLen-1 ];
+ if( nLen > 2 && nStart == '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 1, nLen-2 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_CONTAINS : SC_DOES_NOT_CONTAIN;
+ }
+ else if( nLen > 1 && nStart == '*' && nEnd != '*' )
+ {
+ aStr = aStr.copy( 1 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_ENDS_WITH : SC_DOES_NOT_END_WITH;
+ }
+ else if( nLen > 1 && nStart != '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 0, nLen-1 );
+ rEntry.eOp = ( rEntry.eOp == SC_EQUAL ) ? SC_BEGINS_WITH : SC_DOES_NOT_BEGIN_WITH;
+ }
+ else if( nLen == 2 && nStart == '*' && nEnd == '*' )
+ {
+ aStr = aStr.copy( 1 );
+ }
+}
+
+void XclImpAutoFilterData::ReadAutoFilter(
+ XclImpStream& rStrm, svl::SharedStringPool& rPool )
+{
+ sal_uInt16 nCol, nFlags;
+ nCol = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ ScQueryConnect eConn = ::get_flagvalue( nFlags, EXC_AFFLAG_ANDORMASK, SC_OR, SC_AND );
+ bool bSimple1 = ::get_flag(nFlags, EXC_AFFLAG_SIMPLE1);
+ bool bSimple2 = ::get_flag(nFlags, EXC_AFFLAG_SIMPLE2);
+ bool bTop10 = ::get_flag(nFlags, EXC_AFFLAG_TOP10);
+ bool bTopOfTop10 = ::get_flag(nFlags, EXC_AFFLAG_TOP10TOP);
+ bool bPercent = ::get_flag(nFlags, EXC_AFFLAG_TOP10PERC);
+ sal_uInt16 nCntOfTop10 = nFlags >> 7;
+
+ if( bTop10 )
+ {
+ ScQueryEntry& aEntry = aParam.AppendEntry();
+ ScQueryEntry::Item& rItem = aEntry.GetQueryItem();
+ aEntry.bDoQuery = true;
+ aEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol));
+ aEntry.eOp = bTopOfTop10 ?
+ (bPercent ? SC_TOPPERC : SC_TOPVAL) : (bPercent ? SC_BOTPERC : SC_BOTVAL);
+ aEntry.eConnect = SC_AND;
+
+ rItem.meType = ScQueryEntry::ByString;
+ rItem.maString = rPool.intern(OUString::number(nCntOfTop10));
+
+ rStrm.Ignore(20);
+ return;
+ }
+
+ sal_uInt8 nType, nOper, nBoolErr, nVal;
+ sal_Int32 nRK;
+ double fVal;
+
+ sal_uInt8 nStrLen[2] = { 0, 0 };
+ ScQueryEntry aEntries[2];
+
+ for (size_t nE = 0; nE < 2; ++nE)
+ {
+ ScQueryEntry& rEntry = aEntries[nE];
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ bool bIgnore = false;
+
+ nType = rStrm.ReaduInt8();
+ nOper = rStrm.ReaduInt8();
+ switch( nOper )
+ {
+ case EXC_AFOPER_LESS:
+ rEntry.eOp = SC_LESS;
+ break;
+ case EXC_AFOPER_EQUAL:
+ rEntry.eOp = SC_EQUAL;
+ break;
+ case EXC_AFOPER_LESSEQUAL:
+ rEntry.eOp = SC_LESS_EQUAL;
+ break;
+ case EXC_AFOPER_GREATER:
+ rEntry.eOp = SC_GREATER;
+ break;
+ case EXC_AFOPER_NOTEQUAL:
+ rEntry.eOp = SC_NOT_EQUAL;
+ break;
+ case EXC_AFOPER_GREATEREQUAL:
+ rEntry.eOp = SC_GREATER_EQUAL;
+ break;
+ default:
+ rEntry.eOp = SC_EQUAL;
+ }
+
+ switch( nType )
+ {
+ case EXC_AFTYPE_RK:
+ nRK = rStrm.ReadInt32();
+ rStrm.Ignore( 4 );
+ rItem.maString = rPool.intern(
+ CreateFromDouble(XclTools::GetDoubleFromRK(nRK)));
+ break;
+ case EXC_AFTYPE_DOUBLE:
+ fVal = rStrm.ReadDouble();
+ rItem.maString = rPool.intern(CreateFromDouble(fVal));
+ break;
+ case EXC_AFTYPE_STRING:
+ rStrm.Ignore( 4 );
+ nStrLen[ nE ] = rStrm.ReaduInt8();
+ rStrm.Ignore( 3 );
+ rItem.maString = svl::SharedString();
+ break;
+ case EXC_AFTYPE_BOOLERR:
+ nBoolErr = rStrm.ReaduInt8();
+ nVal = rStrm.ReaduInt8();
+ rStrm.Ignore( 6 );
+ rItem.maString = rPool.intern(OUString::number(nVal));
+ bIgnore = (nBoolErr != 0);
+ break;
+ case EXC_AFTYPE_EMPTY:
+ rEntry.SetQueryByEmpty();
+ break;
+ case EXC_AFTYPE_NOTEMPTY:
+ rEntry.SetQueryByNonEmpty();
+ break;
+ default:
+ rStrm.Ignore( 8 );
+ bIgnore = true;
+ }
+
+ if (!bIgnore)
+ {
+ rEntry.bDoQuery = true;
+ rItem.meType = ScQueryEntry::ByString;
+ rEntry.nField = static_cast<SCCOLROW>(StartCol() + static_cast<SCCOL>(nCol));
+ rEntry.eConnect = nE ? eConn : SC_AND;
+ }
+ }
+
+ if (eConn == SC_AND)
+ {
+ for (size_t nE = 0; nE < 2; ++nE)
+ {
+ if (nStrLen[nE] && aEntries[nE].bDoQuery)
+ {
+ OUString aStr = rStrm.ReadUniString(nStrLen[nE]);
+ ExcelQueryToOooQuery(aStr, aEntries[nE]);
+ aEntries[nE].GetQueryItem().maString = rPool.intern(aStr);
+ aParam.AppendEntry() = aEntries[nE];
+ }
+ }
+ }
+ else
+ {
+ assert( eConn == SC_OR && "eConn should be SC_AND or SC_OR");
+ // Import only when both conditions are for simple equality, else
+ // import only the 1st condition due to conflict with the ordering of
+ // conditions. #i39464#.
+ //
+ // Example: Let A1 be a condition of column A, and B1 and B2
+ // conditions of column B, connected with OR. Excel performs 'A1 AND
+ // (B1 OR B2)' in this case, but Calc would do '(A1 AND B1) OR B2'
+ // instead.
+
+ if (bSimple1 && bSimple2 && nStrLen[0] && nStrLen[1])
+ {
+ // Two simple OR'ed equal conditions. We can import this correctly.
+ ScQueryEntry& rEntry = aParam.AppendEntry();
+ rEntry.bDoQuery = true;
+ rEntry.eOp = SC_EQUAL;
+ rEntry.eConnect = SC_AND;
+ ScQueryEntry::QueryItemsType aItems;
+ aItems.reserve(2);
+ ScQueryEntry::Item aItem1, aItem2;
+ aItem1.maString = rPool.intern(rStrm.ReadUniString(nStrLen[0]));
+ aItem1.meType = ScQueryEntry::ByString;
+ aItem2.maString = rPool.intern(rStrm.ReadUniString(nStrLen[1]));
+ aItem2.meType = ScQueryEntry::ByString;
+ aItems.push_back(aItem1);
+ aItems.push_back(aItem2);
+ rEntry.GetQueryItems().swap(aItems);
+ }
+ else if (nStrLen[0] && aEntries[0].bDoQuery)
+ {
+ // Due to conflict, we can import only the first condition.
+ OUString aStr = rStrm.ReadUniString(nStrLen[0]);
+ ExcelQueryToOooQuery(aStr, aEntries[0]);
+ aEntries[0].GetQueryItem().maString = rPool.intern(aStr);
+ aParam.AppendEntry() = aEntries[0];
+ }
+ }
+}
+
+void XclImpAutoFilterData::SetAdvancedRange( const ScRange* pRange )
+{
+ if (pRange)
+ {
+ aCriteriaRange = *pRange;
+ bCriteria = true;
+ }
+ else
+ bCriteria = false;
+}
+
+void XclImpAutoFilterData::SetExtractPos( const ScAddress& rAddr )
+{
+ aParam.nDestCol = rAddr.Col();
+ aParam.nDestRow = rAddr.Row();
+ aParam.nDestTab = rAddr.Tab();
+ aParam.bInplace = false;
+ aParam.bDestPers = true;
+}
+
+void XclImpAutoFilterData::Apply()
+{
+ // Create the ScDBData() object if the AutoFilter is activated
+ // or if we need to create the Advanced Filter.
+ if( bActive || bCriteria)
+ {
+ ScDocument& rDoc = pExcRoot->pIR->GetDoc();
+ pCurrDBData = new ScDBData(STR_DB_LOCAL_NONAME, Tab(),
+ StartCol(),StartRow(), EndCol(),EndRow() );
+ if(bCriteria)
+ {
+ EnableRemoveFilter();
+
+ pCurrDBData->SetQueryParam( aParam );
+ pCurrDBData->SetAdvancedQuerySource(&aCriteriaRange);
+ }
+ else
+ pCurrDBData->SetAdvancedQuerySource(nullptr);
+ rDoc.SetAnonymousDBData(Tab(), std::unique_ptr<ScDBData>(pCurrDBData));
+ }
+
+ if( bActive )
+ {
+ InsertQueryParam();
+ }
+}
+
+void XclImpAutoFilterData::EnableRemoveFilter()
+{
+ // only if this is a saved Advanced filter
+ if( !bActive && bAutoOrAdvanced )
+ {
+ ScQueryEntry& aEntry = aParam.AppendEntry();
+ aEntry.bDoQuery = true;
+ }
+
+ // TBD: force the automatic activation of the
+ // "Remove Filter" by setting a virtual mouse click
+ // inside the advanced range
+}
+
+void XclImpAutoFilterBuffer::Insert( RootData* pRoot, const ScRange& rRange)
+{
+ if( !GetByTab( rRange.aStart.Tab() ) )
+ maFilters.push_back( std::make_shared<XclImpAutoFilterData>( pRoot, rRange ));
+}
+
+void XclImpAutoFilterBuffer::AddAdvancedRange( const ScRange& rRange )
+{
+ XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() );
+ if( pData )
+ pData->SetAdvancedRange( &rRange );
+}
+
+void XclImpAutoFilterBuffer::AddExtractPos( const ScRange& rRange )
+{
+ XclImpAutoFilterData* pData = GetByTab( rRange.aStart.Tab() );
+ if( pData )
+ pData->SetExtractPos( rRange.aStart );
+}
+
+void XclImpAutoFilterBuffer::Apply()
+{
+ for( const auto& rFilterPtr : maFilters )
+ rFilterPtr->Apply();
+}
+
+XclImpAutoFilterData* XclImpAutoFilterBuffer::GetByTab( SCTAB nTab )
+{
+ for( const auto& rFilterPtr : maFilters )
+ {
+ if( rFilterPtr->Tab() == nTab )
+ return rFilterPtr.get();
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/excrecds.cxx b/sc/source/filter/excel/excrecds.cxx
new file mode 100644
index 000000000..b175445bc
--- /dev/null
+++ b/sc/source/filter/excel/excrecds.cxx
@@ -0,0 +1,1177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excrecds.hxx>
+
+#include <map>
+#include <filter/msfilter/countryid.hxx>
+
+#include <svl/numformat.hxx>
+#include <sal/log.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <string.h>
+
+#include <global.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <queryentry.hxx>
+#include <queryparam.hxx>
+#include <sortparam.hxx>
+#include <userlist.hxx>
+#include <root.hxx>
+
+#include <xeescher.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xlname.hxx>
+#include <xestyle.hxx>
+
+#include <xcl97rec.hxx>
+#include <tabprotection.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::uno::Sequence;
+
+//--------------------------------------------------------- class ExcDummy_00 -
+const sal_uInt8 ExcDummy_00::pMyData[] = {
+ 0x5c, 0x00, 0x20, 0x00, 0x04, 'C', 'a', 'l', 'c', // WRITEACCESS
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20
+};
+const std::size_t ExcDummy_00::nMyLen = sizeof( ExcDummy_00::pMyData );
+
+//-------------------------------------------------------- class ExcDummy_04x -
+const sal_uInt8 ExcDummy_040::pMyData[] = {
+ 0x40, 0x00, 0x02, 0x00, 0x00, 0x00, // BACKUP
+ 0x8d, 0x00, 0x02, 0x00, 0x00, 0x00, // HIDEOBJ
+};
+const std::size_t ExcDummy_040::nMyLen = sizeof( ExcDummy_040::pMyData );
+
+const sal_uInt8 ExcDummy_041::pMyData[] = {
+ 0x0e, 0x00, 0x02, 0x00, 0x01, 0x00, // PRECISION
+ 0xda, 0x00, 0x02, 0x00, 0x00, 0x00 // BOOKBOOL
+};
+const std::size_t ExcDummy_041::nMyLen = sizeof( ExcDummy_041::pMyData );
+
+//-------------------------------------------------------- class ExcDummy_02a -
+const sal_uInt8 ExcDummy_02a::pMyData[] = {
+ 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, // CALCMODE
+ 0x0c, 0x00, 0x02, 0x00, 0x64, 0x00, // CALCCOUNT
+ 0x0f, 0x00, 0x02, 0x00, 0x01, 0x00, // REFMODE
+ 0x11, 0x00, 0x02, 0x00, 0x00, 0x00, // ITERATION
+ 0x10, 0x00, 0x08, 0x00, 0xfc, 0xa9, 0xf1, 0xd2, 0x4d, // DELTA
+ 0x62, 0x50, 0x3f,
+ 0x5f, 0x00, 0x02, 0x00, 0x01, 0x00 // SAVERECALC
+};
+const std::size_t ExcDummy_02a::nMyLen = sizeof( ExcDummy_02a::pMyData );
+
+//----------------------------------------------------------- class ExcRecord -
+
+void ExcRecord::Save( XclExpStream& rStrm )
+{
+ SetRecHeader( GetNum(), GetLen() );
+ XclExpRecord::Save( rStrm );
+}
+
+void ExcRecord::SaveCont( XclExpStream& /*rStrm*/ )
+{
+}
+
+void ExcRecord::WriteBody( XclExpStream& rStrm )
+{
+ SaveCont( rStrm );
+}
+
+void ExcRecord::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+//--------------------------------------------------------- class ExcEmptyRec -
+
+void ExcEmptyRec::Save( XclExpStream& /*rStrm*/ )
+{
+}
+
+sal_uInt16 ExcEmptyRec::GetNum() const
+{
+ return 0;
+}
+
+std::size_t ExcEmptyRec::GetLen() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------- class ExcDummyRec -
+
+void ExcDummyRec::Save( XclExpStream& rStrm )
+{
+ rStrm.Write( GetData(), GetLen() ); // raw write mode
+}
+
+sal_uInt16 ExcDummyRec::GetNum() const
+{
+ return 0x0000;
+}
+
+//------------------------------------------------------- class ExcBoolRecord -
+
+void ExcBoolRecord::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(bVal ? 0x0001 : 0x0000);
+}
+
+std::size_t ExcBoolRecord::GetLen() const
+{
+ return 2;
+}
+
+//--------------------------------------------------------- class ExcBof_Base -
+
+ExcBof_Base::ExcBof_Base()
+ : nDocType(0)
+ , nVers(0)
+ , nRupBuild(0x096C) // copied from Excel
+ , nRupYear(0x07C9) // copied from Excel
+{
+}
+
+//-------------------------------------------------------------- class ExcBof -
+
+ExcBof::ExcBof()
+{
+ nDocType = 0x0010;
+ nVers = 0x0500;
+}
+
+void ExcBof::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nVers << nDocType << nRupBuild << nRupYear;
+}
+
+sal_uInt16 ExcBof::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBof::GetLen() const
+{
+ return 8;
+}
+
+//------------------------------------------------------------- class ExcBofW -
+
+ExcBofW::ExcBofW()
+{
+ nDocType = 0x0005;
+ nVers = 0x0500;
+}
+
+void ExcBofW::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nVers << nDocType << nRupBuild << nRupYear;
+}
+
+sal_uInt16 ExcBofW::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBofW::GetLen() const
+{
+ return 8;
+}
+
+//-------------------------------------------------------------- class ExcEof -
+
+sal_uInt16 ExcEof::GetNum() const
+{
+ return 0x000A;
+}
+
+std::size_t ExcEof::GetLen() const
+{
+ return 0;
+}
+
+//--------------------------------------------------------- class ExcDummy_00 -
+
+std::size_t ExcDummy_00::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_00::GetData() const
+{
+ return pMyData;
+}
+
+//-------------------------------------------------------- class ExcDummy_04x -
+
+std::size_t ExcDummy_040::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_040::GetData() const
+{
+ return pMyData;
+}
+
+std::size_t ExcDummy_041::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_041::GetData() const
+{
+ return pMyData;
+}
+
+//------------------------------------------------------------- class Exc1904 -
+
+Exc1904::Exc1904( const ScDocument& rDoc )
+{
+ const Date& rDate = rDoc.GetFormatTable()->GetNullDate();
+ bVal = (rDate == Date( 1, 1, 1904 ));
+ bDateCompatibility = (rDate != Date( 30, 12, 1899 ));
+}
+
+sal_uInt16 Exc1904::GetNum() const
+{
+ return 0x0022;
+}
+
+void Exc1904::SaveXml( XclExpXmlStream& rStrm )
+{
+ bool bISOIEC = ( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 );
+
+ if( bISOIEC )
+ {
+ rStrm.WriteAttributes(XML_dateCompatibility, ToPsz(bDateCompatibility));
+ }
+
+ if( !bISOIEC || bDateCompatibility )
+ {
+ rStrm.WriteAttributes(XML_date1904, ToPsz(bVal));
+ }
+}
+
+//------------------------------------------------------ class ExcBundlesheet -
+
+ExcBundlesheetBase::ExcBundlesheetBase( const RootData& rRootData, SCTAB nTabNum ) :
+ m_nStrPos( STREAM_SEEK_TO_END ),
+ m_nOwnPos( STREAM_SEEK_TO_END ),
+ nGrbit( rRootData.pER->GetTabInfo().IsVisibleTab( nTabNum ) ? 0x0000 : 0x0001 ),
+ nTab( nTabNum )
+{
+}
+
+ExcBundlesheetBase::ExcBundlesheetBase() :
+ m_nStrPos( STREAM_SEEK_TO_END ),
+ m_nOwnPos( STREAM_SEEK_TO_END ),
+ nGrbit( 0x0000 ),
+ nTab( SCTAB_GLOBAL )
+{
+}
+
+void ExcBundlesheetBase::UpdateStreamPos( XclExpStream& rStrm )
+{
+ rStrm.SetSvStreamPos( m_nOwnPos );
+ rStrm.DisableEncryption();
+ rStrm << static_cast<sal_uInt32>(m_nStrPos);
+ rStrm.EnableEncryption();
+}
+
+sal_uInt16 ExcBundlesheetBase::GetNum() const
+{
+ return 0x0085;
+}
+
+ExcBundlesheet::ExcBundlesheet( const RootData& rRootData, SCTAB _nTab ) :
+ ExcBundlesheetBase( rRootData, _nTab )
+{
+ OUString sTabName = rRootData.pER->GetTabInfo().GetScTabName( _nTab );
+ OSL_ENSURE( sTabName.getLength() < 256, "ExcBundlesheet::ExcBundlesheet - table name too long" );
+ aName = OUStringToOString(sTabName, rRootData.pER->GetTextEncoding());
+}
+
+void ExcBundlesheet::SaveCont( XclExpStream& rStrm )
+{
+ m_nOwnPos = rStrm.GetSvStreamPos();
+ rStrm << sal_uInt32(0x00000000) // dummy (stream position of the sheet)
+ << nGrbit;
+ rStrm.WriteByteString(aName); // 8 bit length, max 255 chars
+}
+
+std::size_t ExcBundlesheet::GetLen() const
+{
+ return 7 + std::min( aName.getLength(), sal_Int32(255) );
+}
+
+//--------------------------------------------------------- class ExcDummy_02 -
+
+std::size_t ExcDummy_02a::GetLen() const
+{
+ return nMyLen;
+}
+
+const sal_uInt8* ExcDummy_02a::GetData() const
+{
+ return pMyData;
+}
+//--------------------------------------------------------- class ExcDummy_02 -
+
+XclExpCountry::XclExpCountry( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_COUNTRY, 4 )
+{
+ /* #i31530# set document country as UI country too -
+ needed for correct behaviour of number formats. */
+ mnUICountry = mnDocCountry = static_cast< sal_uInt16 >(
+ ::msfilter::ConvertLanguageToCountry( rRoot.GetDocLanguage() ) );
+}
+
+void XclExpCountry::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnUICountry << mnDocCountry;
+}
+
+// XclExpWsbool ===============================================================
+
+XclExpWsbool::XclExpWsbool( bool bFitToPages )
+ : XclExpUInt16Record( EXC_ID_WSBOOL, EXC_WSBOOL_DEFAULTFLAGS )
+{
+ if( bFitToPages )
+ SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE );
+}
+
+XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages, SCTAB nScTab, const Color& rTabColor, XclExpFilterManager* pManager ) :
+ mnScTab(nScTab), mpManager(pManager), mbFitToPage(bFitToPages), maTabColor(rTabColor) {}
+
+void XclExpXmlSheetPr::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_sheetPr,
+ // OOXTODO: XML_syncHorizontal,
+ // OOXTODO: XML_syncVertical,
+ // OOXTODO: XML_syncRef,
+ // OOXTODO: XML_transitionEvaluation,
+ // OOXTODO: XML_transitionEntry,
+ // OOXTODO: XML_published,
+ // OOXTODO: XML_codeName,
+ XML_filterMode, mpManager ? ToPsz(mpManager->HasFilterMode(mnScTab)) : nullptr
+ // OOXTODO: XML_enableFormatConditionsCalculation
+ );
+
+ // Note : the order of child elements is significant. Don't change the order.
+
+ // OOXTODO: XML_outlinePr
+
+ if (maTabColor != COL_AUTO)
+ rWorksheet->singleElement(XML_tabColor, XML_rgb, XclXmlUtils::ToOString(maTabColor));
+
+ rWorksheet->singleElement(XML_pageSetUpPr,
+ // OOXTODO: XML_autoPageBreaks,
+ XML_fitToPage, ToPsz(mbFitToPage));
+
+ rWorksheet->endElement( XML_sheetPr );
+}
+
+// XclExpWindowProtection ===============================================================
+
+XclExpWindowProtection::XclExpWindowProtection(bool bValue) :
+ XclExpBoolRecord(EXC_ID_WINDOWPROTECT, bValue)
+{
+}
+
+void XclExpWindowProtection::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_lockWindows, ToPsz(GetBool()));
+}
+
+// XclExpDocProtection ===============================================================
+
+XclExpProtection::XclExpProtection(bool bValue) :
+ XclExpBoolRecord(EXC_ID_PROTECT, bValue)
+{
+}
+
+XclExpSheetProtection::XclExpSheetProtection(bool bValue, SCTAB nTab ) :
+ XclExpProtection( bValue),
+ mnTab(nTab)
+{
+}
+
+void XclExpSheetProtection::SaveXml( XclExpXmlStream& rStrm )
+{
+ ScDocument& rDoc = rStrm.GetRoot().GetDoc();
+ const ScTableProtection* pTabProtect = rDoc.GetTabProtection(mnTab);
+ if ( !pTabProtect )
+ return;
+
+ const ScOoxPasswordHash& rPH = pTabProtect->getPasswordHash();
+ // Do not write any hash attributes if there is no password.
+ ScOoxPasswordHash aPH;
+ if (rPH.hasPassword())
+ aPH = rPH;
+
+ Sequence<sal_Int8> aHash = pTabProtect->getPasswordHash(PASSHASH_XL);
+ OString sHash;
+ if (aHash.getLength() >= 2)
+ {
+ sHash = OString::number(
+ ( static_cast<sal_uInt8>(aHash[0]) << 8
+ | static_cast<sal_uInt8>(aHash[1]) ),
+ 16 );
+ }
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->singleElement( XML_sheetProtection,
+ XML_algorithmName, aPH.maAlgorithmName.isEmpty() ? nullptr : aPH.maAlgorithmName.toUtf8().getStr(),
+ XML_hashValue, aPH.maHashValue.isEmpty() ? nullptr : aPH.maHashValue.toUtf8().getStr(),
+ XML_saltValue, aPH.maSaltValue.isEmpty() ? nullptr : aPH.maSaltValue.toUtf8().getStr(),
+ XML_spinCount, aPH.mnSpinCount ? OString::number( aPH.mnSpinCount).getStr() : nullptr,
+ XML_sheet, ToPsz( true ),
+ XML_password, sHash.isEmpty()? nullptr : sHash.getStr(),
+ XML_objects, pTabProtect->isOptionEnabled( ScTableProtection::OBJECTS ) ? nullptr : ToPsz( true ),
+ XML_scenarios, pTabProtect->isOptionEnabled( ScTableProtection::SCENARIOS ) ? nullptr : ToPsz( true ),
+ XML_formatCells, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_CELLS ) ? ToPsz( false ) : nullptr,
+ XML_formatColumns, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_formatRows, pTabProtect->isOptionEnabled( ScTableProtection::FORMAT_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_insertColumns, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_insertRows, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_insertHyperlinks, pTabProtect->isOptionEnabled( ScTableProtection::INSERT_HYPERLINKS ) ? ToPsz( false ) : nullptr,
+ XML_deleteColumns, pTabProtect->isOptionEnabled( ScTableProtection::DELETE_COLUMNS ) ? ToPsz( false ) : nullptr,
+ XML_deleteRows, pTabProtect->isOptionEnabled( ScTableProtection::DELETE_ROWS ) ? ToPsz( false ) : nullptr,
+ XML_selectLockedCells, pTabProtect->isOptionEnabled( ScTableProtection::SELECT_LOCKED_CELLS ) ? nullptr : ToPsz( true ),
+ XML_sort, pTabProtect->isOptionEnabled( ScTableProtection::SORT ) ? ToPsz( false ) : nullptr,
+ XML_autoFilter, pTabProtect->isOptionEnabled( ScTableProtection::AUTOFILTER ) ? ToPsz( false ) : nullptr,
+ XML_pivotTables, pTabProtect->isOptionEnabled( ScTableProtection::PIVOT_TABLES ) ? ToPsz( false ) : nullptr,
+ XML_selectUnlockedCells, pTabProtect->isOptionEnabled( ScTableProtection::SELECT_UNLOCKED_CELLS ) ? nullptr : ToPsz( true ) );
+
+ const ::std::vector<ScEnhancedProtection>& rProts( pTabProtect->getEnhancedProtection());
+ if (rProts.empty())
+ return;
+
+ rWorksheet->startElement(XML_protectedRanges);
+ for (const auto& rProt : rProts)
+ {
+ SAL_WARN_IF( rProt.maSecurityDescriptorXML.isEmpty() && !rProt.maSecurityDescriptor.empty(),
+ "sc.filter", "XclExpSheetProtection::SaveXml: losing BIFF security descriptor");
+ rWorksheet->singleElement( XML_protectedRange,
+ XML_name, rProt.maTitle.isEmpty() ? nullptr : rProt.maTitle.toUtf8().getStr(),
+ XML_securityDescriptor, rProt.maSecurityDescriptorXML.isEmpty() ? nullptr : rProt.maSecurityDescriptorXML.toUtf8().getStr(),
+ /* XXX 'password' is not part of OOXML, but Excel2013
+ * writes it if loaded from BIFF, in which case
+ * 'algorithmName', 'hashValue', 'saltValue' and
+ * 'spinCount' are absent; so do we if it was present. */
+ XML_password, rProt.mnPasswordVerifier ? OString::number( rProt.mnPasswordVerifier, 16).getStr() : nullptr,
+ XML_algorithmName, rProt.maPasswordHash.maAlgorithmName.isEmpty() ? nullptr : rProt.maPasswordHash.maAlgorithmName.toUtf8().getStr(),
+ XML_hashValue, rProt.maPasswordHash.maHashValue.isEmpty() ? nullptr : rProt.maPasswordHash.maHashValue.toUtf8().getStr(),
+ XML_saltValue, rProt.maPasswordHash.maSaltValue.isEmpty() ? nullptr : rProt.maPasswordHash.maSaltValue.toUtf8().getStr(),
+ XML_spinCount, rProt.maPasswordHash.mnSpinCount ? OString::number( rProt.maPasswordHash.mnSpinCount).getStr() : nullptr,
+ XML_sqref, rProt.maRangeList.is() ? XclXmlUtils::ToOString( rStrm.GetRoot().GetDoc(), *rProt.maRangeList).getStr() : nullptr);
+ }
+ rWorksheet->endElement( XML_protectedRanges);
+}
+
+XclExpPassHash::XclExpPassHash(const Sequence<sal_Int8>& aHash) :
+ XclExpRecord(EXC_ID_PASSWORD, 2),
+ mnHash(0x0000)
+{
+ if (aHash.getLength() >= 2)
+ {
+ mnHash = ((aHash[0] << 8) & 0xFFFF);
+ mnHash |= (aHash[1] & 0xFF);
+ }
+}
+
+XclExpPassHash::~XclExpPassHash()
+{
+}
+
+void XclExpPassHash::WriteBody(XclExpStream& rStrm)
+{
+ rStrm << mnHash;
+}
+
+XclExpFiltermode::XclExpFiltermode() :
+ XclExpEmptyRecord( EXC_ID_FILTERMODE )
+{
+}
+
+XclExpAutofilterinfo::XclExpAutofilterinfo( const ScAddress& rStartPos, SCCOL nScCol ) :
+ XclExpUInt16Record( EXC_ID_AUTOFILTERINFO, static_cast< sal_uInt16 >( nScCol ) ),
+ maStartPos( rStartPos )
+{
+}
+
+ExcFilterCondition::ExcFilterCondition() :
+ nType( EXC_AFTYPE_NOTUSED ),
+ nOper( EXC_AFOPER_EQUAL )
+{
+}
+
+ExcFilterCondition::~ExcFilterCondition()
+{
+}
+
+std::size_t ExcFilterCondition::GetTextBytes() const
+{
+ return pText ? (1 + pText->GetBufferSize()) : 0;
+}
+
+void ExcFilterCondition::SetCondition( sal_uInt8 nTp, sal_uInt8 nOp, const OUString* pT )
+{
+ nType = nTp;
+ nOper = nOp;
+ pText.reset( pT ? new XclExpString( *pT, XclStrFlags::EightBitLength ) : nullptr);
+}
+
+void ExcFilterCondition::Save( XclExpStream& rStrm )
+{
+ rStrm << nType << nOper;
+ if (nType == EXC_AFTYPE_STRING)
+ {
+ OSL_ENSURE(pText, "ExcFilterCondition::Save() -- pText is NULL!");
+ rStrm << sal_uInt32(0) << static_cast<sal_uInt8>(pText->Len()) << sal_uInt16(0) << sal_uInt8(0);
+ }
+ else
+ rStrm << sal_uInt32(0) << sal_uInt32(0);
+}
+
+static const char* lcl_GetOperator( sal_uInt8 nOper )
+{
+ switch( nOper )
+ {
+ case EXC_AFOPER_EQUAL: return "equal";
+ case EXC_AFOPER_GREATER: return "greaterThan";
+ case EXC_AFOPER_GREATEREQUAL: return "greaterThanOrEqual";
+ case EXC_AFOPER_LESS: return "lessThan";
+ case EXC_AFOPER_LESSEQUAL: return "lessThanOrEqual";
+ case EXC_AFOPER_NOTEQUAL: return "notEqual";
+ case EXC_AFOPER_NONE:
+ default: return "**none**";
+ }
+}
+
+static OString lcl_GetValue( sal_uInt8 nType, const XclExpString* pStr )
+{
+ if (nType == EXC_AFTYPE_STRING)
+ return XclXmlUtils::ToOString(*pStr);
+ else
+ return OString();
+}
+
+void ExcFilterCondition::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( IsEmpty() )
+ return;
+
+ rStrm.GetCurrentStream()->singleElement( XML_customFilter,
+ XML_operator, lcl_GetOperator( nOper ),
+ XML_val, lcl_GetValue(nType, pText.get()) );
+}
+
+void ExcFilterCondition::SaveText( XclExpStream& rStrm )
+{
+ if( nType == EXC_AFTYPE_STRING )
+ {
+ OSL_ENSURE( pText, "ExcFilterCondition::SaveText() -- pText is NULL!" );
+ pText->WriteFlagField( rStrm );
+ pText->WriteBuffer( rStrm );
+ }
+}
+
+XclExpAutofilter::XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC ) :
+ XclExpRecord( EXC_ID_AUTOFILTER, 24 ),
+ XclExpRoot( rRoot ),
+ meType(FilterCondition),
+ nCol( nC ),
+ nFlags( 0 ),
+ bHasBlankValue( false )
+{
+}
+
+bool XclExpAutofilter::AddCondition( ScQueryConnect eConn, sal_uInt8 nType, sal_uInt8 nOp,
+ const OUString* pText, bool bSimple )
+{
+ if( !aCond[ 1 ].IsEmpty() )
+ return false;
+
+ sal_uInt16 nInd = aCond[ 0 ].IsEmpty() ? 0 : 1;
+
+ if( nInd == 1 )
+ nFlags |= (eConn == SC_OR) ? EXC_AFFLAG_OR : EXC_AFFLAG_AND;
+ if( bSimple )
+ nFlags |= (nInd == 0) ? EXC_AFFLAG_SIMPLE1 : EXC_AFFLAG_SIMPLE2;
+
+ aCond[ nInd ].SetCondition( nType, nOp, pText );
+
+ AddRecSize( aCond[ nInd ].GetTextBytes() );
+
+ return true;
+}
+
+bool XclExpAutofilter::HasCondition() const
+{
+ return !aCond[0].IsEmpty();
+}
+
+bool XclExpAutofilter::AddEntry( const ScQueryEntry& rEntry )
+{
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+
+ if (rItems.empty())
+ {
+ if (GetOutput() != EXC_OUTPUT_BINARY)
+ {
+ // tdf#123353 XLSX export
+ meType = BlankValue;
+ return false;
+ }
+ // XLS export
+ return true;
+ }
+
+ if (GetOutput() != EXC_OUTPUT_BINARY && rItems.size() > 1)
+ {
+ AddMultiValueEntry(rEntry);
+ return false;
+ }
+
+ bool bConflict = false;
+ OUString sText;
+ const ScQueryEntry::Item& rItem = rItems[0];
+ if (!rItem.maString.isEmpty())
+ {
+ sText = rItem.maString.getString();
+ switch( rEntry.eOp )
+ {
+ case SC_CONTAINS:
+ case SC_DOES_NOT_CONTAIN:
+ {
+ sText = "*" + sText + "*";
+ }
+ break;
+ case SC_BEGINS_WITH:
+ case SC_DOES_NOT_BEGIN_WITH:
+ sText += "*";
+ break;
+ case SC_ENDS_WITH:
+ case SC_DOES_NOT_END_WITH:
+ sText = "*" + sText;
+ break;
+ default:
+ {
+ //nothing
+ }
+ }
+ }
+
+ // empty/nonempty fields
+ if (rEntry.IsQueryByEmpty())
+ {
+ bConflict = !AddCondition(rEntry.eConnect, EXC_AFTYPE_EMPTY, EXC_AFOPER_NONE, nullptr, true);
+ bHasBlankValue = true;
+ }
+ else if(rEntry.IsQueryByNonEmpty())
+ bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_NOTEMPTY, EXC_AFOPER_NONE, nullptr, true );
+ else if (rEntry.IsQueryByTextColor() || rEntry.IsQueryByBackgroundColor())
+ {
+ AddColorEntry(rEntry);
+ }
+ // other conditions
+ else
+ {
+ // top10 flags
+ sal_uInt16 nNewFlags = 0x0000;
+ switch( rEntry.eOp )
+ {
+ case SC_TOPVAL:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP);
+ break;
+ case SC_BOTVAL:
+ nNewFlags = EXC_AFFLAG_TOP10;
+ break;
+ case SC_TOPPERC:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10TOP | EXC_AFFLAG_TOP10PERC);
+ break;
+ case SC_BOTPERC:
+ nNewFlags = (EXC_AFFLAG_TOP10 | EXC_AFFLAG_TOP10PERC);
+ break;
+ default:;
+ }
+ bool bNewTop10 = ::get_flag( nNewFlags, EXC_AFFLAG_TOP10 );
+
+ bConflict = HasTop10() && bNewTop10;
+ if( !bConflict )
+ {
+ if( bNewTop10 )
+ {
+ sal_uInt32 nIndex = 0;
+ double fVal = 0.0;
+ if (GetFormatter().IsNumberFormat(sText, nIndex, fVal))
+ {
+ if (fVal < 0) fVal = 0;
+ if (fVal >= 501) fVal = 500;
+ }
+ nFlags |= (nNewFlags | static_cast<sal_uInt16>(fVal) << 7);
+ }
+ // normal condition
+ else
+ {
+ if (GetOutput() != EXC_OUTPUT_BINARY && rEntry.eOp == SC_EQUAL)
+ {
+ AddMultiValueEntry(rEntry);
+ return false;
+ }
+
+ sal_uInt8 nOper = EXC_AFOPER_NONE;
+
+ switch( rEntry.eOp )
+ {
+ case SC_EQUAL: nOper = EXC_AFOPER_EQUAL; break;
+ case SC_LESS: nOper = EXC_AFOPER_LESS; break;
+ case SC_GREATER: nOper = EXC_AFOPER_GREATER; break;
+ case SC_LESS_EQUAL: nOper = EXC_AFOPER_LESSEQUAL; break;
+ case SC_GREATER_EQUAL: nOper = EXC_AFOPER_GREATEREQUAL; break;
+ case SC_NOT_EQUAL: nOper = EXC_AFOPER_NOTEQUAL; break;
+ case SC_CONTAINS:
+ case SC_BEGINS_WITH:
+ case SC_ENDS_WITH:
+ nOper = EXC_AFOPER_EQUAL; break;
+ case SC_DOES_NOT_CONTAIN:
+ case SC_DOES_NOT_BEGIN_WITH:
+ case SC_DOES_NOT_END_WITH:
+ nOper = EXC_AFOPER_NOTEQUAL; break;
+ default:;
+ }
+ bConflict = !AddCondition( rEntry.eConnect, EXC_AFTYPE_STRING, nOper, &sText);
+ }
+ }
+ }
+ return bConflict;
+}
+
+void XclExpAutofilter::AddMultiValueEntry( const ScQueryEntry& rEntry )
+{
+ meType = MultiValue;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ for (const auto& rItem : rItems)
+ {
+ if( rItem.maString.isEmpty() )
+ bHasBlankValue = true;
+ else
+ maMultiValues.push_back(std::make_pair(rItem.maString.getString(), rItem.meType == ScQueryEntry::ByDate));
+ }
+}
+
+void XclExpAutofilter::AddColorEntry(const ScQueryEntry& rEntry)
+{
+ meType = ColorValue;
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ for (const auto& rItem : rItems)
+ {
+ maColorValues.push_back(
+ std::make_pair(rItem.maColor, rItem.meType == ScQueryEntry::ByBackgroundColor));
+ // Ensure that selected color(s) will be added to dxf: selection can be not in list
+ // of already added to dfx colors taken from filter range
+ if (GetDxfs().GetDxfByColor(rItem.maColor) == -1)
+ GetDxfs().AddColor(rItem.maColor);
+ }
+}
+
+void XclExpAutofilter::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << nCol << nFlags;
+ aCond[ 0 ].Save( rStrm );
+ aCond[ 1 ].Save( rStrm );
+ aCond[ 0 ].SaveText( rStrm );
+ aCond[ 1 ].SaveText( rStrm );
+}
+
+void XclExpAutofilter::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (meType == FilterCondition && !HasCondition() && !HasTop10())
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_filterColumn,
+ XML_colId, OString::number(nCol)
+ // OOXTODO: XML_hiddenButton, AutoFilter12 fHideArrow?
+ // OOXTODO: XML_showButton
+ );
+
+ switch (meType)
+ {
+ case FilterCondition:
+ {
+ if( HasTop10() )
+ {
+ rWorksheet->singleElement( XML_top10,
+ XML_top, ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10TOP ) ),
+ XML_percent, ToPsz( get_flag( nFlags, EXC_AFFLAG_TOP10PERC ) ),
+ XML_val, OString::number(nFlags >> 7)
+ // OOXTODO: XML_filterVal
+ );
+ }
+ else
+ {
+ rWorksheet->startElement(XML_customFilters, XML_and,
+ ToPsz((nFlags & EXC_AFFLAG_ANDORMASK) == EXC_AFFLAG_AND));
+ aCond[0].SaveXml(rStrm);
+ aCond[1].SaveXml(rStrm);
+ rWorksheet->endElement(XML_customFilters);
+ }
+ // OOXTODO: XML_dynamicFilter, XML_extLst, XML_filters, XML_iconFilter
+ }
+ break;
+ case ColorValue:
+ {
+ if (!maColorValues.empty())
+ {
+ Color color = maColorValues[0].first;
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ if (maColorValues[0].second) // is background color
+ {
+ pAttrList->add(XML_cellColor, OString::number(1));
+ }
+ else
+ {
+ pAttrList->add(XML_cellColor, OString::number(0));
+ }
+ pAttrList->add(XML_dxfId, OString::number(GetDxfs().GetDxfByColor(color)));
+ rWorksheet->singleElement(XML_colorFilter, pAttrList);
+ }
+ }
+ break;
+ case BlankValue:
+ {
+ rWorksheet->singleElement(XML_filters, XML_blank, "1");
+ }
+ break;
+ case MultiValue:
+ {
+ if( bHasBlankValue )
+ rWorksheet->startElement(XML_filters, XML_blank, "1");
+ else
+ rWorksheet->startElement(XML_filters);
+
+ for (const auto& rMultiValue : maMultiValues)
+ {
+ OString aStr = OUStringToOString(rMultiValue.first, RTL_TEXTENCODING_UTF8);
+ if( !rMultiValue.second )
+ {
+ const char* pz = aStr.getStr();
+ rWorksheet->singleElement(XML_filter, XML_val, pz);
+ }
+ else
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ sal_Int32 aDateGroup[3] = { XML_year, XML_month, XML_day };
+ sal_Int32 idx = 0;
+ for (size_t i = 0; idx >= 0 && i < 3; i++)
+ {
+ OString kw = aStr.getToken(0, '-', idx);
+ kw = kw.trim();
+ if (!kw.isEmpty())
+ {
+ pAttrList->add(aDateGroup[i], kw);
+ }
+ }
+ // TODO: date filter can only handle YYYY-MM-DD date formats, so XML_dateTimeGrouping value
+ // will be "day" as default, until date filter cannot handle HH:MM:SS.
+ pAttrList->add(XML_dateTimeGrouping, "day");
+ rWorksheet->singleElement(XML_dateGroupItem, pAttrList);
+ }
+ }
+ rWorksheet->endElement(XML_filters);
+ }
+ break;
+ }
+ rWorksheet->endElement( XML_filterColumn );
+}
+
+ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const ScDBData* pDefinedData ) :
+ XclExpRoot( rRoot ),
+ mbAutoFilter (false)
+{
+ XclExpNameManager& rNameMgr = GetNameManager();
+
+ bool bFound = false;
+ bool bAdvanced = false;
+ const ScDBData* pData = (pDefinedData ? pDefinedData : rRoot.GetDoc().GetAnonymousDBData(nTab));
+ ScRange aAdvRange;
+ if (pData)
+ {
+ bAdvanced = pData->GetAdvancedQuerySource( aAdvRange );
+ bFound = (pData->HasQueryParam() || pData->HasAutoFilter() || bAdvanced);
+ }
+ if( !bFound )
+ return;
+
+ ScQueryParam aParam;
+ pData->GetQueryParam( aParam );
+
+ ScRange aRange( aParam.nCol1, aParam.nRow1, aParam.nTab,
+ aParam.nCol2, aParam.nRow2, aParam.nTab );
+ SCCOL nColCnt = aParam.nCol2 - aParam.nCol1 + 1;
+
+ maRef = aRange;
+
+ // #i2394# built-in defined names must be sorted by containing sheet name
+ if (!pDefinedData)
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_FILTERDATABASE, aRange );
+
+ // advanced filter
+ if( bAdvanced )
+ {
+ // filter criteria, excel allows only same table
+ if( !pDefinedData && aAdvRange.aStart.Tab() == nTab )
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_CRITERIA, aAdvRange );
+
+ // filter destination range, excel allows only same table
+ if( !aParam.bInplace )
+ {
+ ScRange aDestRange( aParam.nDestCol, aParam.nDestRow, aParam.nDestTab );
+ aDestRange.aEnd.IncCol( nColCnt - 1 );
+ if( !pDefinedData && aDestRange.aStart.Tab() == nTab )
+ rNameMgr.InsertBuiltInName( EXC_BUILTIN_EXTRACT, aDestRange );
+ }
+
+ m_pFilterMode = new XclExpFiltermode;
+ }
+ // AutoFilter
+ else
+ {
+ bool bConflict = false;
+ bool bContLoop = true;
+ bool bHasOr = false;
+ SCCOLROW nFirstField = aParam.GetEntry( 0 ).nField;
+
+ // create AUTOFILTER records for filtered columns
+ for( SCSIZE nEntry = 0; !bConflict && bContLoop && (nEntry < aParam.GetEntryCount()); nEntry++ )
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry( nEntry );
+
+ bContLoop = rEntry.bDoQuery;
+ if( bContLoop )
+ {
+ XclExpAutofilter* pFilter = GetByCol( static_cast<SCCOL>(rEntry.nField) - aRange.aStart.Col() );
+
+ if( nEntry > 0 )
+ bHasOr |= (rEntry.eConnect == SC_OR);
+
+ bConflict = (nEntry > 1) && bHasOr;
+ if( !bConflict )
+ bConflict = (nEntry == 1) && (rEntry.eConnect == SC_OR) &&
+ (nFirstField != rEntry.nField);
+ if( !bConflict )
+ bConflict = pFilter->AddEntry( rEntry );
+ }
+ }
+
+ // additional tests for conflicts
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); !bConflict && (nPos < nSize); ++nPos )
+ {
+ XclExpAutofilterRef xFilter = maFilterList.GetRecord( nPos );
+ bConflict = xFilter->HasCondition() && xFilter->HasTop10();
+ }
+
+ if( bConflict )
+ maFilterList.RemoveAllRecords();
+
+ if( !maFilterList.IsEmpty() )
+ m_pFilterMode = new XclExpFiltermode;
+ m_pFilterInfo = new XclExpAutofilterinfo( aRange.aStart, nColCnt );
+
+ if (maFilterList.IsEmpty () && !bConflict)
+ mbAutoFilter = true;
+
+ // get sort criteria
+ {
+ ScSortParam aSortParam;
+ pData->GetSortParam( aSortParam );
+
+ ScUserList* pList = ScGlobal::GetUserList();
+ if (aSortParam.bUserDef && pList && pList->size() > aSortParam.nUserIndex)
+ {
+ // get sorted area without headers
+ maSortRef = ScRange(
+ aParam.nCol1, aParam.nRow1 + (aSortParam.bHasHeader? 1 : 0), aParam.nTab,
+ aParam.nCol2, aParam.nRow2, aParam.nTab );
+
+ // get sorted columns with custom lists
+ const ScUserListData& rData = (*pList)[aSortParam.nUserIndex];
+
+ // get column index and sorting direction
+ SCCOLROW nField = 0;
+ bool bSortAscending=true;
+ for (const auto & rKey : aSortParam.maKeyState)
+ {
+ if (rKey.bDoSort)
+ {
+ nField = rKey.nField;
+ bSortAscending = rKey.bAscending;
+ break;
+ }
+ }
+
+ // remember sort criteria
+ const ScRange aSortedColumn(
+ nField, aParam.nRow1 + (aSortParam.bHasHeader? 1 : 0), aParam.nTab,
+ nField, aParam.nRow2, aParam.nTab );
+ const OUString aItemList = rData.GetString();
+
+ maSortCustomList.emplace_back(aSortedColumn, aItemList, !bSortAscending);
+ }
+ }
+ }
+}
+
+ExcAutoFilterRecs::~ExcAutoFilterRecs()
+{
+}
+
+XclExpAutofilter* ExcAutoFilterRecs::GetByCol( SCCOL nCol )
+{
+ XclExpAutofilterRef xFilter;
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ {
+ xFilter = maFilterList.GetRecord( nPos );
+ if( xFilter->GetCol() == static_cast<sal_uInt16>(nCol) )
+ return xFilter.get();
+ }
+ xFilter = new XclExpAutofilter( GetRoot(), static_cast<sal_uInt16>(nCol) );
+ maFilterList.AppendRecord( xFilter );
+ return xFilter.get();
+}
+
+bool ExcAutoFilterRecs::IsFiltered( SCCOL nCol )
+{
+ for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
+ if( maFilterList.GetRecord( nPos )->GetCol() == static_cast<sal_uInt16>(nCol) )
+ return true;
+ return false;
+}
+
+void ExcAutoFilterRecs::AddObjRecs()
+{
+ if( m_pFilterInfo )
+ {
+ ScAddress aAddr( m_pFilterInfo->GetStartPos() );
+ for( SCCOL nObj = 0, nCount = m_pFilterInfo->GetColCount(); nObj < nCount; nObj++ )
+ {
+ std::unique_ptr<XclObj> pObjRec(new XclObjDropDown( GetObjectManager(), aAddr, IsFiltered( nObj ) ));
+ GetObjectManager().AddObj( std::move(pObjRec) );
+ aAddr.IncCol();
+ }
+ }
+}
+
+void ExcAutoFilterRecs::Save( XclExpStream& rStrm )
+{
+ if( m_pFilterMode )
+ m_pFilterMode->Save( rStrm );
+ if( m_pFilterInfo )
+ m_pFilterInfo->Save( rStrm );
+ maFilterList.Save( rStrm );
+}
+
+void ExcAutoFilterRecs::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFilterList.IsEmpty() && !mbAutoFilter )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_autoFilter, XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maRef));
+ // OOXTODO: XML_extLst, XML_sortState
+ if( !maFilterList.IsEmpty() )
+ maFilterList.SaveXml( rStrm );
+
+ if (!maSortCustomList.empty())
+ {
+ rWorksheet->startElement(XML_sortState, XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maSortRef));
+
+ for (const auto & rSortCriteria : maSortCustomList)
+ {
+ if (std::get<2>(rSortCriteria))
+ rWorksheet->singleElement(XML_sortCondition,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(),
+ std::get<0>(rSortCriteria)),
+ XML_descending, "1",
+ XML_customList, std::get<1>(rSortCriteria).toUtf8().getStr());
+ else
+ rWorksheet->singleElement(XML_sortCondition,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(),
+ std::get<0>(rSortCriteria)),
+ XML_customList, std::get<1>(rSortCriteria).toUtf8().getStr());
+ }
+
+ rWorksheet->endElement(XML_sortState);
+ }
+
+ rWorksheet->endElement( XML_autoFilter );
+}
+
+bool ExcAutoFilterRecs::HasFilterMode() const
+{
+ return m_pFilterMode != nullptr;
+}
+
+XclExpFilterManager::XclExpFilterManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpFilterManager::InitTabFilter( SCTAB nScTab )
+{
+ maFilterMap[ nScTab ] = new ExcAutoFilterRecs( GetRoot(), nScTab, nullptr );
+}
+
+XclExpRecordRef XclExpFilterManager::CreateRecord( SCTAB nScTab )
+{
+ XclExpTabFilterRef xRec;
+ XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
+ if( aIt != maFilterMap.end() )
+ {
+ xRec = aIt->second;
+ xRec->AddObjRecs();
+ }
+ return xRec;
+}
+
+bool XclExpFilterManager::HasFilterMode( SCTAB nScTab )
+{
+ XclExpTabFilterMap::iterator aIt = maFilterMap.find( nScTab );
+ if( aIt != maFilterMap.end() )
+ {
+ return aIt->second->HasFilterMode();
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/exctools.cxx b/sc/source/filter/excel/exctools.cxx
new file mode 100644
index 000000000..6e9d91777
--- /dev/null
+++ b/sc/source/filter/excel/exctools.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <scextopt.hxx>
+#include <olinetab.hxx>
+
+#include <root.hxx>
+#include <excimp8.hxx>
+#include <namebuff.hxx>
+#include <otlnbuff.hxx>
+#include <formel.hxx>
+#include <xilink.hxx>
+
+#include <memory>
+#include <vector>
+
+RootData::RootData()
+{
+ eDateiTyp = BiffX;
+ pFmlaConverter = nullptr;
+
+ pTabId = nullptr;
+ pUserBViewList = nullptr;
+
+ pIR = nullptr;
+ pER = nullptr;
+ pColRowBuff = nullptr;
+}
+
+RootData::~RootData()
+{
+ pExtSheetBuff.reset();
+ pShrfmlaBuff.reset();
+ pExtNameBuff.reset();
+ pAutoFilterBuffer.reset();
+}
+
+XclImpOutlineBuffer::XclImpOutlineBuffer( SCSIZE nNewSize ) :
+ maLevels(0, nNewSize, 0),
+ mpOutlineArray(nullptr),
+ mnEndPos(nNewSize),
+ mnMaxLevel(0),
+ mbButtonAfter(true)
+{
+}
+
+XclImpOutlineBuffer::~XclImpOutlineBuffer()
+{
+}
+
+void XclImpOutlineBuffer::SetLevel( SCSIZE nIndex, sal_uInt8 nVal, bool bCollapsed )
+{
+ maLevels.insert_back(nIndex, nIndex+1, nVal);
+ if (nVal > mnMaxLevel)
+ mnMaxLevel = nVal;
+ if (bCollapsed)
+ maCollapsedPosSet.insert(nIndex);
+}
+
+void XclImpOutlineBuffer::SetOutlineArray( ScOutlineArray* pOArray )
+{
+ mpOutlineArray = pOArray;
+}
+
+void XclImpOutlineBuffer::MakeScOutline()
+{
+ if (!mpOutlineArray)
+ return;
+
+ ::std::vector<SCSIZE> aOutlineStack;
+ aOutlineStack.reserve(mnMaxLevel);
+ for (const auto& [nPos, nLevel] : maLevels)
+ {
+ if (nPos >= mnEndPos)
+ {
+ // Don't go beyond the max allowed position.
+ OSL_ENSURE(aOutlineStack.empty(), "XclImpOutlineBuffer::MakeScOutline: outline stack not empty but expected to be.");
+ break;
+ }
+ sal_uInt8 nCurLevel = static_cast<sal_uInt8>(aOutlineStack.size());
+ if (nLevel > nCurLevel)
+ {
+ for (sal_uInt8 i = 0; i < nLevel - nCurLevel; ++i)
+ aOutlineStack.push_back(nPos);
+ }
+ else
+ {
+ OSL_ENSURE(nLevel <= nCurLevel, "XclImpOutlineBuffer::MakeScOutline: unexpected level!");
+ for (sal_uInt8 i = 0; i < nCurLevel - nLevel; ++i)
+ {
+ if (aOutlineStack.empty())
+ {
+ // Something is wrong.
+ return;
+ }
+ SCSIZE nFirstPos = aOutlineStack.back();
+ aOutlineStack.pop_back();
+ bool bCollapsed = false;
+ if (mbButtonAfter)
+ bCollapsed = maCollapsedPosSet.count(nPos) > 0;
+ else if (nFirstPos > 0)
+ bCollapsed = maCollapsedPosSet.count(nFirstPos-1) > 0;
+
+ bool bDummy;
+ mpOutlineArray->Insert(nFirstPos, nPos-1, bDummy, bCollapsed);
+ }
+ }
+ }
+}
+
+void XclImpOutlineBuffer::SetLevelRange( SCSIZE nF, SCSIZE nL, sal_uInt8 nVal, bool bCollapsed )
+{
+ if (nF > nL)
+ // invalid range
+ return;
+
+ maLevels.insert_back(nF, nL+1, nVal);
+
+ if (bCollapsed)
+ maCollapsedPosSet.insert(nF);
+}
+
+void XclImpOutlineBuffer::SetButtonMode( bool bRightOrUnder )
+{
+ mbButtonAfter = bRightOrUnder;
+}
+
+ExcScenarioCell::ExcScenarioCell( const sal_uInt16 nC, const sal_uInt16 nR )
+ : nCol( nC ), nRow( nR )
+{
+}
+
+ExcScenario::ExcScenario( XclImpStream& rIn, const RootData& rR )
+ : nTab( rR.pIR->GetCurrScTab() )
+{
+ sal_uInt16 nCref;
+ sal_uInt8 nName, nComment;
+
+ nCref = rIn.ReaduInt16();
+ nProtected = rIn.ReaduInt8();
+ rIn.Ignore( 1 ); // Hide
+ nName = rIn.ReaduInt8();
+ nComment = rIn.ReaduInt8();
+ rIn.Ignore( 1 ); // instead of nUser!
+
+ if( nName )
+ aName = rIn.ReadUniString( nName );
+ else
+ {
+ aName = "Scenery";
+ rIn.Ignore( 1 );
+ }
+
+ rIn.ReadUniString(); // username
+
+ if( nComment )
+ aComment = rIn.ReadUniString();
+
+ sal_uInt16 n = nCref;
+ sal_uInt16 nC, nR;
+ aEntries.reserve(n);
+ while( n )
+ {
+ nR = rIn.ReaduInt16();
+ nC = rIn.ReaduInt16();
+
+ aEntries.emplace_back( nC, nR );
+
+ n--;
+ }
+
+ for (auto& rEntry : aEntries)
+ rEntry.SetValue(rIn.ReadUniString());
+}
+
+void ExcScenario::Apply( const XclImpRoot& rRoot, const bool bLast )
+{
+ ScDocument& r = rRoot.GetDoc();
+ OUString aSzenName( aName );
+ sal_uInt16 nNewTab = nTab + 1;
+
+ if( !r.InsertTab( nNewTab, aSzenName ) )
+ return;
+
+ r.SetScenario( nNewTab, true );
+ // do not show scenario frames
+ const ScScenarioFlags nFlags = ScScenarioFlags::CopyAll
+ | (nProtected ? ScScenarioFlags::Protected : ScScenarioFlags::NONE);
+ /* | ScScenarioFlags::ShowFrame*/
+ r.SetScenarioData( nNewTab, aComment, COL_LIGHTGRAY, nFlags);
+
+ for (const auto& rEntry : aEntries)
+ {
+ sal_uInt16 nCol = rEntry.nCol;
+ sal_uInt16 nRow = rEntry.nRow;
+ OUString aVal = rEntry.GetValue();
+
+ r.ApplyFlagsTab( nCol, nRow, nCol, nRow, nNewTab, ScMF::Scenario );
+
+ r.SetString( nCol, nRow, nNewTab, aVal );
+ }
+
+ if( bLast )
+ r.SetActiveScenario( nNewTab, true );
+
+ // modify what the Active tab is set to if the new
+ // scenario tab occurs before the active tab.
+ ScExtDocSettings& rDocSett = rRoot.GetExtDocOptions().GetDocSettings();
+ if( (static_cast< SCCOL >( nTab ) < rDocSett.mnDisplTab) && (rDocSett.mnDisplTab < MAXTAB) )
+ ++rDocSett.mnDisplTab;
+ rRoot.GetTabInfo().InsertScTab( nNewTab );
+}
+
+void ExcScenarioList::Apply( const XclImpRoot& rRoot )
+{
+ sal_uInt16 n = static_cast<sal_uInt16>(aEntries.size());
+
+ std::vector< std::unique_ptr<ExcScenario> >::reverse_iterator iter;
+ for (iter = aEntries.rbegin(); iter != aEntries.rend(); ++iter)
+ {
+ n--;
+ (*iter)->Apply(rRoot, n == nLastScenario);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/expop2.cxx b/sc/source/filter/excel/expop2.cxx
new file mode 100644
index 000000000..ee8ba0fff
--- /dev/null
+++ b/sc/source/filter/excel/expop2.cxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unotools/fltrcfg.hxx>
+
+#include <osl/diagnose.h>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docinf.hxx>
+#include <filter/msfilter/svxmsbas.hxx>
+
+#include <oox/ole/vbaexport.hxx>
+
+#include <scerrors.hxx>
+
+#include <root.hxx>
+#include <excdoc.hxx>
+#include <exp_op.hxx>
+
+#include <xehelper.hxx>
+
+#include <officecfg/Office/Calc.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+namespace com::sun::star::document { class XDocumentProperties; }
+
+namespace {
+
+enum class VBAExportMode
+{
+ NONE,
+ REEXPORT_STREAM,
+ FULL_EXPORT
+};
+
+}
+
+ExportBiff5::ExportBiff5( XclExpRootData& rExpData, SvStream& rStrm ):
+ ExportTyp( rStrm ),
+ XclExpRoot( rExpData )
+{
+ // only need part of the Root data
+ pExcRoot = &GetOldRoot();
+ pExcRoot->pER = this; // ExcRoot -> XclExpRoot
+ pExcRoot->eDateiTyp = Biff5;
+ pExcDoc.reset( new ExcDocument( *this ) );
+}
+
+ExportBiff5::~ExportBiff5()
+{
+}
+
+ErrCode ExportBiff5::Write()
+{
+ SfxObjectShell* pDocShell = GetDocShell();
+ OSL_ENSURE( pDocShell, "ExportBiff5::Write - no document shell" );
+
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ OSL_ENSURE( xRootStrg.is(), "ExportBiff5::Write - no root storage" );
+
+ VBAExportMode eVbaExportMode = VBAExportMode::NONE;
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ if (officecfg::Office::Calc::Filter::Import::VBA::UseExport::get())
+ eVbaExportMode = VBAExportMode::FULL_EXPORT;
+ else
+ {
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if (rFilterOpt.IsLoadExcelBasicStorage())
+ eVbaExportMode = VBAExportMode::REEXPORT_STREAM;
+ }
+ }
+
+ if ( pDocShell && xRootStrg.is() && eVbaExportMode == VBAExportMode::FULL_EXPORT)
+ {
+ VbaExport aExport(pDocShell->GetModel());
+ if (aExport.containsVBAProject())
+ {
+ tools::SvRef<SotStorage> xVBARoot = xRootStrg->OpenSotStorage("_VBA_PROJECT_CUR");
+ aExport.exportVBA( xVBARoot.get() );
+ }
+ }
+ else if( pDocShell && xRootStrg.is() && eVbaExportMode == VBAExportMode::REEXPORT_STREAM )
+ {
+ SvxImportMSVBasic aBasicImport( *pDocShell, *xRootStrg );
+ const ErrCode nErr = aBasicImport.SaveOrDelMSVBAStorage( true, EXC_STORAGE_VBA_PROJECT );
+ if( nErr != ERRCODE_NONE )
+ pDocShell->SetError(nErr);
+ }
+
+ pExcDoc->ReadDoc(); // ScDoc -> ExcDoc
+ pExcDoc->Write( aOut ); // wechstreamen
+
+ if( pDocShell && xRootStrg.is() )
+ {
+ using namespace ::com::sun::star;
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps
+ = xDPS->getDocumentProperties();
+ if ( SvtFilterOptions::Get().IsEnableCalcPreview() )
+ {
+ std::shared_ptr<GDIMetaFile> xMetaFile =
+ pDocShell->GetPreviewMetaFile();
+ uno::Sequence<sal_Int8> metaFile(
+ sfx2::convertMetaFile(xMetaFile.get()));
+ sfx2::SaveOlePropertySet( xDocProps, xRootStrg.get(), &metaFile );
+ }
+ else
+ sfx2::SaveOlePropertySet( xDocProps, xRootStrg.get() );
+ }
+
+ const XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsRowTruncated() )
+ return SCWARN_EXPORT_MAXROW;
+ if( rAddrConv.IsColTruncated() )
+ return SCWARN_EXPORT_MAXCOL;
+ if( rAddrConv.IsTabTruncated() )
+ return SCWARN_EXPORT_MAXTAB;
+
+ return ERRCODE_NONE;
+}
+
+ExportBiff8::ExportBiff8( XclExpRootData& rExpData, SvStream& rStrm ) :
+ ExportBiff5( rExpData, rStrm )
+{
+ pExcRoot->eDateiTyp = Biff8;
+}
+
+ExportBiff8::~ExportBiff8()
+{
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/export/SparklineExt.cxx b/sc/source/filter/excel/export/SparklineExt.cxx
new file mode 100644
index 000000000..487698e19
--- /dev/null
+++ b/sc/source/filter/excel/export/SparklineExt.cxx
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <export/SparklineExt.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineList.hxx>
+
+using namespace oox;
+
+namespace xcl::exp
+{
+SparklineExt::SparklineExt(const XclExpRoot& rRoot)
+ : XclExpExt(rRoot)
+{
+ maURI = "{05C60535-1F16-4fd2-B633-F4F36F0B64E0}";
+}
+
+void SparklineExt::SaveXml(XclExpXmlStream& rStream)
+{
+ auto& rDocument = GetDoc();
+
+ auto* pSparklineList = rDocument.GetSparklineList(GetCurrScTab());
+ if (!pSparklineList)
+ return;
+
+ auto const& rSparklineGroups = pSparklineList->getSparklineGroups();
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+ rWorksheet->startElement(XML_ext, FSNS(XML_xmlns, XML_x14),
+ rStream.getNamespaceURL(OOX_NS(xls14Lst)), XML_uri, maURI);
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklineGroups, FSNS(XML_xmlns, XML_xm),
+ rStream.getNamespaceURL(OOX_NS(xm)));
+
+ for (auto const& pSparklineGroup : rSparklineGroups)
+ {
+ auto const& rSparklineVector = pSparklineList->getSparklinesFor(pSparklineGroup);
+ addSparklineGroup(rStream, *pSparklineGroup, rSparklineVector);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_sparklineGroups);
+ rWorksheet->endElement(XML_ext);
+}
+
+void SparklineExt::addSparklineGroupAttributes(
+ rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList,
+ sc::SparklineAttributes& rAttributes)
+{
+ if (rAttributes.getLineWeight() != 0.75)
+ pAttrList->add(XML_lineWeight, OString::number(rAttributes.getLineWeight()));
+
+ if (rAttributes.getType() != sc::SparklineType::Line)
+ {
+ if (rAttributes.getType() == sc::SparklineType::Column)
+ pAttrList->add(XML_type, "column");
+ else if (rAttributes.getType() == sc::SparklineType::Stacked)
+ pAttrList->add(XML_type, "stacked");
+ }
+
+ if (rAttributes.isDateAxis())
+ pAttrList->add(XML_dateAxis, "1");
+
+ if (rAttributes.getDisplayEmptyCellsAs() != sc::DisplayEmptyCellsAs::Zero)
+ {
+ if (rAttributes.getDisplayEmptyCellsAs() == sc::DisplayEmptyCellsAs::Gap)
+ pAttrList->add(XML_displayEmptyCellsAs, "gap");
+ else if (rAttributes.getDisplayEmptyCellsAs() == sc::DisplayEmptyCellsAs::Span)
+ pAttrList->add(XML_displayEmptyCellsAs, "span");
+ }
+
+ if (rAttributes.isMarkers())
+ pAttrList->add(XML_markers, "1");
+ if (rAttributes.isHigh())
+ pAttrList->add(XML_high, "1");
+ if (rAttributes.isLow())
+ pAttrList->add(XML_low, "1");
+ if (rAttributes.isFirst())
+ pAttrList->add(XML_first, "1");
+ if (rAttributes.isLast())
+ pAttrList->add(XML_last, "1");
+ if (rAttributes.isNegative())
+ pAttrList->add(XML_negative, "1");
+ if (rAttributes.shouldDisplayXAxis())
+ pAttrList->add(XML_displayXAxis, "1");
+ if (rAttributes.shouldDisplayHidden())
+ pAttrList->add(XML_displayHidden, "1");
+
+ if (rAttributes.getMinAxisType() != sc::AxisType::Individual)
+ {
+ if (rAttributes.getMinAxisType() == sc::AxisType::Group)
+ pAttrList->add(XML_minAxisType, "group");
+ else if (rAttributes.getMinAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_minAxisType, "custom");
+ }
+
+ if (rAttributes.getMaxAxisType() != sc::AxisType::Individual)
+ {
+ if (rAttributes.getMaxAxisType() == sc::AxisType::Group)
+ pAttrList->add(XML_maxAxisType, "group");
+ else if (rAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_maxAxisType, "custom");
+ }
+
+ if (rAttributes.isRightToLeft())
+ pAttrList->add(XML_rightToLeft, "1");
+
+ if (rAttributes.getManualMax() && rAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_manualMax, OString::number(*rAttributes.getManualMax()));
+
+ if (rAttributes.getManualMin() && rAttributes.getMinAxisType() == sc::AxisType::Custom)
+ pAttrList->add(XML_manualMin, OString::number(*rAttributes.getManualMin()));
+}
+
+void SparklineExt::addSparklineGroupColors(XclExpXmlStream& rStream,
+ sc::SparklineAttributes& rAttributes)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+
+ rWorksheet->singleElementNS(XML_x14, XML_colorSeries, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorSeries()));
+
+ if (rAttributes.getColorNegative() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorNegative, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorNegative()));
+ }
+
+ if (rAttributes.getColorAxis() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorAxis, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorAxis()));
+ }
+
+ if (rAttributes.getColorMarkers() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorMarkers, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorMarkers()));
+ }
+
+ if (rAttributes.getColorFirst() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorFirst, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorFirst()));
+ }
+
+ if (rAttributes.getColorLast() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorLast, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorLast()));
+ }
+
+ if (rAttributes.getColorHigh() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorHigh, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorHigh()));
+ }
+
+ if (rAttributes.getColorLow() != COL_TRANSPARENT)
+ {
+ rWorksheet->singleElementNS(XML_x14, XML_colorLow, XML_rgb,
+ XclXmlUtils::ToOString(rAttributes.getColorLow()));
+ }
+}
+
+void SparklineExt::addSparklineGroup(XclExpXmlStream& rStream, sc::SparklineGroup& rSparklineGroup,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& rSparklines)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStream.GetCurrentStream();
+
+ // Sparkline Group Attributes
+ auto pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+
+ // Write ID
+ OString sUID = rSparklineGroup.getID().getString();
+ pAttrList->addNS(XML_xr2, XML_uid, sUID);
+
+ // Write attributes
+ addSparklineGroupAttributes(pAttrList, rSparklineGroup.getAttributes());
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklineGroup, pAttrList);
+
+ addSparklineGroupColors(rStream, rSparklineGroup.getAttributes());
+
+ // Sparklines
+
+ rWorksheet->startElementNS(XML_x14, XML_sparklines);
+ for (auto const& rSparkline : rSparklines)
+ {
+ rWorksheet->startElementNS(XML_x14, XML_sparkline);
+
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+
+ OUString sRangeFormula;
+ ScRefFlags eFlags = ScRefFlags::VALID | ScRefFlags::TAB_3D;
+ rSparkline->getInputRange().Format(sRangeFormula, eFlags, GetDoc(),
+ formula::FormulaGrammar::CONV_XL_OOX, ' ', true);
+
+ rWorksheet->writeEscaped(sRangeFormula);
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ }
+
+ {
+ rWorksheet->startElementNS(XML_xm, XML_sqref);
+
+ ScAddress::Details detailsXL(formula::FormulaGrammar::CONV_XL_OOX);
+ ScAddress aAddress(rSparkline->getColumn(), rSparkline->getRow(), GetCurrScTab());
+ OUString sLocation = aAddress.Format(ScRefFlags::VALID, &GetDoc(), detailsXL);
+
+ rWorksheet->writeEscaped(sLocation);
+ rWorksheet->endElementNS(XML_xm, XML_sqref);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_sparkline);
+ }
+ rWorksheet->endElementNS(XML_x14, XML_sparklines);
+ rWorksheet->endElementNS(XML_x14, XML_sparklineGroup);
+}
+
+SparklineBuffer::SparklineBuffer(const XclExpRoot& rRoot, XclExtLstRef const& xExtLst)
+ : XclExpRoot(rRoot)
+{
+ auto& rDocument = GetDoc();
+ auto* pSparklineList = rDocument.GetSparklineList(GetCurrScTab());
+ if (pSparklineList && !pSparklineList->getSparklineGroups().empty())
+ {
+ xExtLst->AddRecord(new xcl::exp::SparklineExt(GetRoot()));
+ }
+}
+
+} // end namespace xcl::exp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/fontbuff.cxx b/sc/source/filter/excel/fontbuff.cxx
new file mode 100644
index 000000000..40e04fcb0
--- /dev/null
+++ b/sc/source/filter/excel/fontbuff.cxx
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <lotfntbf.hxx>
+
+#include <scitems.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemset.hxx>
+
+void LotusFontBuffer::Fill( const sal_uInt8 nIndex, SfxItemSet& rItemSet )
+{
+ sal_uInt8 nIntIndex = nIndex & 0x07;
+
+ ENTRY* pCurrent = pData + nIntIndex;
+
+ if( pCurrent->pFont )
+ rItemSet.Put( *pCurrent->pFont );
+
+ if( pCurrent->pHeight )
+ rItemSet.Put( *pCurrent->pHeight );
+
+ if( nIndex & 0x08 )
+ {
+ SvxWeightItem aWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT );
+ rItemSet.Put( aWeightItem );
+ }
+
+ if( nIndex & 0x10 )
+ {
+ SvxPostureItem aAttr( ITALIC_NORMAL, ATTR_FONT_POSTURE );
+ rItemSet.Put( aAttr );
+ }
+
+ FontLineStyle eUnderline;
+ switch( nIndex & 0x60 ) // Bit 5+6
+ {
+ case 0x60:
+ case 0x20: eUnderline = LINESTYLE_SINGLE; break;
+ case 0x40: eUnderline = LINESTYLE_DOUBLE; break;
+ default: eUnderline = LINESTYLE_NONE;
+ }
+ if( eUnderline != LINESTYLE_NONE )
+ {
+ SvxUnderlineItem aUndItem( eUnderline, ATTR_FONT_UNDERLINE );
+ rItemSet.Put( aUndItem );
+ }
+}
+
+void LotusFontBuffer::SetName( const sal_uInt16 nIndex, const OUString& rName )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetName(): Array too small!" );
+ if( nIndex < nSize )
+ {
+ ENTRY* pEntry = pData + nIndex;
+ pEntry->TmpName( rName );
+
+ if( pEntry->nType >= 0 )
+ MakeFont( pEntry );
+ }
+}
+
+void LotusFontBuffer::SetHeight( const sal_uInt16 nIndex, const sal_uInt16 nHeight )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetHeight(): Array too small!" );
+ if( nIndex < nSize )
+ pData[ nIndex ].Height( std::make_unique<SvxFontHeightItem>( static_cast<sal_uInt32>(nHeight) * 20, 100, ATTR_FONT_HEIGHT ) );
+}
+
+void LotusFontBuffer::SetType( const sal_uInt16 nIndex, const sal_uInt16 nType )
+{
+ OSL_ENSURE( nIndex < nSize, "*LotusFontBuffer::SetType(): Array too small!" );
+ if( nIndex < nSize )
+ {
+ ENTRY* pEntry = pData + nIndex;
+ pEntry->Type( nType );
+
+ if( pEntry->xTmpName )
+ MakeFont( pEntry );
+ }
+}
+
+void LotusFontBuffer::MakeFont( ENTRY* pEntry )
+{
+ FontFamily eFamily = FAMILY_DONTKNOW;
+ FontPitch ePitch = PITCH_DONTKNOW;
+ rtl_TextEncoding eCharSet = RTL_TEXTENCODING_DONTKNOW;
+
+ switch( pEntry->nType )
+ {
+ case 0x00: // Helvetica
+ eFamily = FAMILY_SWISS;
+ ePitch = PITCH_VARIABLE;
+ break;
+ case 0x01: // Times Roman
+ eFamily = FAMILY_ROMAN;
+ ePitch = PITCH_VARIABLE;
+ break;
+ case 0x02: // Courier
+ ePitch = PITCH_FIXED;
+ break;
+ case 0x03: // Symbol
+ eCharSet = RTL_TEXTENCODING_SYMBOL;
+ break;
+ }
+
+ pEntry->pFont.reset( new SvxFontItem( eFamily, *pEntry->xTmpName, OUString(), ePitch, eCharSet, ATTR_FONT ) );
+
+ pEntry->xTmpName.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/frmbase.cxx b/sc/source/filter/excel/frmbase.cxx
new file mode 100644
index 000000000..73ef59dad
--- /dev/null
+++ b/sc/source/filter/excel/frmbase.cxx
@@ -0,0 +1,209 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formel.hxx>
+
+#include <osl/diagnose.h>
+
+ScRangeListTabs::ScRangeListTabs( const XclImpRoot& rRoot )
+: XclImpRoot( rRoot )
+{
+}
+
+ScRangeListTabs::~ScRangeListTabs()
+{
+}
+
+void ScRangeListTabs::Append( const ScAddress& aSRD, SCTAB nTab )
+{
+ ScAddress a = aSRD;
+ ScDocument& rDoc = GetRoot().GetDoc();
+
+ if (a.Tab() > MAXTAB)
+ a.SetTab(MAXTAB);
+
+ if (a.Col() > rDoc.MaxCol())
+ a.SetCol(rDoc.MaxCol());
+
+ if (a.Row() > rDoc.MaxRow())
+ a.SetRow(rDoc.MaxRow());
+
+ if( nTab == SCTAB_MAX)
+ return;
+ if( nTab < 0)
+ nTab = a.Tab();
+
+ if (nTab < 0 || MAXTAB < nTab)
+ return;
+
+ TabRangeType::iterator itr = m_TabRanges.find(nTab);
+ if (itr == m_TabRanges.end())
+ {
+ // No entry for this table yet. Insert a new one.
+ std::pair<TabRangeType::iterator, bool> r =
+ m_TabRanges.insert(std::make_pair(nTab, RangeListType()));
+
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ itr->second.push_back(ScRange(a.Col(),a.Row(),a.Tab()));
+}
+
+void ScRangeListTabs::Append( const ScRange& aCRD, SCTAB nTab )
+{
+ ScRange a = aCRD;
+ ScDocument& rDoc = GetRoot().GetDoc();
+
+ // ignore 3D ranges
+ if (a.aStart.Tab() != a.aEnd.Tab())
+ return;
+
+ if (a.aStart.Tab() > MAXTAB)
+ a.aStart.SetTab(MAXTAB);
+ else if (a.aStart.Tab() < 0)
+ a.aStart.SetTab(0);
+
+ if (a.aStart.Col() > rDoc.MaxCol())
+ a.aStart.SetCol(rDoc.MaxCol());
+ else if (a.aStart.Col() < 0)
+ a.aStart.SetCol(0);
+
+ if (a.aStart.Row() > rDoc.MaxRow())
+ a.aStart.SetRow(rDoc.MaxRow());
+ else if (a.aStart.Row() < 0)
+ a.aStart.SetRow(0);
+
+ if (a.aEnd.Col() > rDoc.MaxCol())
+ a.aEnd.SetCol(rDoc.MaxCol());
+ else if (a.aEnd.Col() < 0)
+ a.aEnd.SetCol(0);
+
+ if (a.aEnd.Row() > rDoc.MaxRow())
+ a.aEnd.SetRow(rDoc.MaxRow());
+ else if (a.aEnd.Row() < 0)
+ a.aEnd.SetRow(0);
+
+ if( nTab == SCTAB_MAX)
+ return;
+
+ if( nTab < -1)
+ nTab = a.aStart.Tab();
+
+ if (nTab < 0 || MAXTAB < nTab)
+ return;
+
+ TabRangeType::iterator itr = m_TabRanges.find(nTab);
+ if (itr == m_TabRanges.end())
+ {
+ // No entry for this table yet. Insert a new one.
+ std::pair<TabRangeType::iterator, bool> r =
+ m_TabRanges.insert(std::make_pair(nTab, RangeListType()));
+
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ itr->second.push_back(a);
+}
+
+const ScRange* ScRangeListTabs::First( SCTAB n )
+{
+ OSL_ENSURE( ValidTab(n), "-ScRangeListTabs::First(): Good bye!" );
+
+ TabRangeType::iterator itr = m_TabRanges.find(n);
+ if (itr == m_TabRanges.end())
+ // No range list exists for this table.
+ return nullptr;
+
+ const RangeListType& rList = itr->second;
+ maItrCur = rList.begin();
+ maItrCurEnd = rList.end();
+ return rList.empty() ? nullptr : &(*maItrCur);
+}
+
+const ScRange* ScRangeListTabs::Next ()
+{
+ ++maItrCur;
+ if (maItrCur == maItrCurEnd)
+ return nullptr;
+
+ return &(*maItrCur);
+}
+
+ConverterBase::ConverterBase( svl::SharedStringPool& rSPool ) :
+ aPool(rSPool),
+ aEingPos( 0, 0, 0 )
+{
+}
+
+ConverterBase::~ConverterBase()
+{
+}
+
+void ConverterBase::Reset()
+{
+ aPool.Reset();
+ aStack.Reset();
+}
+
+ExcelConverterBase::ExcelConverterBase( svl::SharedStringPool& rSPool ) :
+ ConverterBase(rSPool)
+{
+}
+
+ExcelConverterBase::~ExcelConverterBase()
+{
+}
+
+void ExcelConverterBase::Reset( const ScAddress& rEingPos )
+{
+ ConverterBase::Reset();
+ aEingPos = rEingPos;
+}
+
+void ExcelConverterBase::Reset()
+{
+ ConverterBase::Reset();
+ aEingPos.Set( 0, 0, 0 );
+}
+
+LotusConverterBase::LotusConverterBase( SvStream &rStr, svl::SharedStringPool& rSPool ) :
+ ConverterBase(rSPool),
+ aIn( rStr ),
+ nBytesLeft( 0 )
+{
+}
+
+LotusConverterBase::~LotusConverterBase()
+{
+}
+
+void LotusConverterBase::Reset( const ScAddress& rEingPos )
+{
+ ConverterBase::Reset();
+ nBytesLeft = 0;
+ aEingPos = rEingPos;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/impop.cxx b/sc/source/filter/excel/impop.cxx
new file mode 100644
index 000000000..9ddc6e6e7
--- /dev/null
+++ b/sc/source/filter/excel/impop.cxx
@@ -0,0 +1,1414 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <imp_op.hxx>
+
+#include <filter/msfilter/countryid.hxx>
+
+#include <scitems.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <sfx2/docfile.hxx>
+#include <svx/svxids.hrc>
+#include <svl/numformat.hxx>
+#include <unotools/configmgr.hxx>
+#include <sal/log.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <docuno.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <global.hxx>
+#include <olinetab.hxx>
+#include <stlpool.hxx>
+#include <viewopti.hxx>
+#include <docoptio.hxx>
+#include <scextopt.hxx>
+#include <unonames.hxx>
+#include <paramisc.hxx>
+#include <colrowst.hxx>
+#include <otlnbuff.hxx>
+#include <xistyle.hxx>
+
+#include <namebuff.hxx>
+#include <xltools.hxx>
+#include <xltable.hxx>
+#include <xltracer.hxx>
+#include <xihelper.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+#include <xiescher.hxx>
+#include <xicontent.hxx>
+
+#include <excform.hxx>
+#include <documentimport.hxx>
+
+#if defined(_WIN32)
+#include <math.h>
+#endif
+
+using namespace ::com::sun::star;
+
+ImportTyp::ImportTyp(ScDocument& rDoc, rtl_TextEncoding eQ)
+ : eQuellChar(eQ)
+ , rD(rDoc)
+
+{
+}
+
+ImportTyp::~ImportTyp()
+{
+}
+
+ImportExcel::ImportExcel( XclImpRootData& rImpData, SvStream& rStrm ):
+ ImportTyp( rImpData.mrDoc, rImpData.meTextEnc ),
+ XclImpRoot( rImpData ),
+ maStrm( rStrm, GetRoot() ),
+ aIn( maStrm ),
+ maScOleSize( ScAddress::INITIALIZE_INVALID ),
+ pColOutlineBuff(nullptr),
+ pRowOutlineBuff(nullptr),
+ pColRowBuff(nullptr),
+ mpLastFormula(nullptr),
+ mnLastRefIdx( 0 ),
+ mnIxfeIndex( 0 ),
+ mnLastRecId(0),
+ mbBiff2HasXfs(false),
+ mbBiff2HasXfsValid(false)
+{
+ nBdshtTab = 0;
+
+ // fill in root data - after new's without root as parameter
+ pExcRoot = &GetOldRoot();
+ pExcRoot->pIR = this; // ExcRoot -> XclImpRoot
+ pExcRoot->eDateiTyp = BiffX;
+ pExcRoot->pExtSheetBuff.reset( new ExtSheetBuffer( pExcRoot ) ); //&aExtSheetBuff;
+ pExcRoot->pShrfmlaBuff.reset( new SharedFormulaBuffer( pExcRoot ) ); //&aShrfrmlaBuff;
+ pExcRoot->pExtNameBuff.reset( new ExtNameBuff ( *this ) );
+
+ pOutlineListBuffer.reset(new XclImpOutlineListBuffer);
+
+ // from Biff8 on
+ pFormConv.reset(new ExcelToSc( GetRoot() ));
+ pExcRoot->pFmlaConverter = pFormConv.get();
+
+ bTabTruncated = false;
+
+ // Excel document per Default on 31.12.1899, accords to Excel settings with 1.1.1900
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetDate( 30, 12, 1899 );
+ rD.SetDocOptions( aOpt );
+ rD.GetFormatTable()->ChangeNullDate( 30, 12, 1899 );
+
+ ScDocOptions aDocOpt( rD.GetDocOptions() );
+ aDocOpt.SetIgnoreCase( true ); // always in Excel
+ aDocOpt.SetFormulaRegexEnabled( false ); // regular expressions? what's that?
+ aDocOpt.SetFormulaWildcardsEnabled( true ); // Excel uses wildcard expressions
+ aDocOpt.SetLookUpColRowNames( false ); // default: no natural language refs
+ rD.SetDocOptions( aDocOpt );
+}
+
+ImportExcel::~ImportExcel()
+{
+ GetDoc().SetSrcCharSet( GetTextEncoding() );
+
+ pOutlineListBuffer.reset();
+
+ pFormConv.reset();
+}
+
+void ImportExcel::SetLastFormula( SCCOL nCol, SCROW nRow, double fVal, sal_uInt16 nXF, ScFormulaCell* pCell )
+{
+ LastFormulaMapType::iterator it = maLastFormulaCells.find(nCol);
+ if (it == maLastFormulaCells.end())
+ {
+ std::pair<LastFormulaMapType::iterator, bool> r =
+ maLastFormulaCells.emplace(nCol, LastFormula());
+ it = r.first;
+ }
+
+ it->second.mnCol = nCol;
+ it->second.mnRow = nRow;
+ it->second.mpCell = pCell;
+ it->second.mfValue = fVal;
+ it->second.mnXF = nXF;
+
+ mpLastFormula = &it->second;
+}
+
+void ImportExcel::ReadFileSharing()
+{
+ sal_uInt16 nRecommendReadOnly, nPasswordHash;
+ nRecommendReadOnly = maStrm.ReaduInt16();
+ nPasswordHash = maStrm.ReaduInt16();
+
+ if((nRecommendReadOnly == 0) && (nPasswordHash == 0))
+ return;
+
+ if( SfxItemSet* pItemSet = GetMedium().GetItemSet() )
+ pItemSet->Put( SfxBoolItem( SID_DOC_READONLY, true ) );
+
+ if( SfxObjectShell* pShell = GetDocShell() )
+ {
+ if( nRecommendReadOnly != 0 )
+ pShell->SetLoadReadonly( true );
+ if( nPasswordHash != 0 )
+ pShell->SetModifyPasswordHash( nPasswordHash );
+ }
+}
+
+sal_uInt16 ImportExcel::ReadXFIndex( const ScAddress& rScPos, bool bBiff2 )
+{
+ sal_uInt16 nXFIdx = 0;
+ if( bBiff2 )
+ {
+ /* #i71453# On first call, check if the file contains XF records (by
+ trying to access the first XF with index 0). If there are no XFs,
+ the explicit formatting information contained in each cell record
+ will be used instead. */
+ if( !mbBiff2HasXfsValid )
+ {
+ mbBiff2HasXfsValid = true;
+ mbBiff2HasXfs = GetXFBuffer().GetXF( 0 ) != nullptr;
+ }
+ // read formatting information (includes the XF identifier)
+ sal_uInt8 nFlags1, nFlags2, nFlags3;
+ nFlags1 = maStrm.ReaduInt8();
+ nFlags2 = maStrm.ReaduInt8();
+ nFlags3 = maStrm.ReaduInt8();
+ /* If the file contains XFs, extract and set the XF identifier,
+ otherwise get the explicit formatting. */
+ if( mbBiff2HasXfs )
+ {
+ nXFIdx = ::extract_value< sal_uInt16 >( nFlags1, 0, 6 );
+ /* If the identifier is equal to 63, then the real identifier is
+ contained in the preceding IXFE record (stored in mnBiff2XfId). */
+ if( nXFIdx == 63 )
+ nXFIdx = mnIxfeIndex;
+ }
+ else
+ {
+ /* Let the XclImpXF class do the conversion of the imported
+ formatting. The XF buffer is empty, therefore will not do any
+ conversion based on the XF index later on. */
+ XclImpXF::ApplyPatternForBiff2CellFormat( GetRoot(), rScPos, nFlags1, nFlags2, nFlags3 );
+ }
+ }
+ else
+ nXFIdx = aIn.ReaduInt16();
+ return nXFIdx;
+}
+
+void ImportExcel::ReadDimensions()
+{
+ XclRange aXclUsedArea;
+ if( (maStrm.GetRecId() == EXC_ID2_DIMENSIONS) || (GetBiff() <= EXC_BIFF5) )
+ {
+ maStrm >> aXclUsedArea;
+ if( (aXclUsedArea.GetColCount() > 1) && (aXclUsedArea.GetRowCount() > 1) )
+ {
+ // Excel stores first unused row/column index
+ --aXclUsedArea.maLast.mnCol;
+ --aXclUsedArea.maLast.mnRow;
+ // create the Calc range
+ SCTAB nScTab = GetCurrScTab();
+ ScRange& rScUsedArea = GetExtDocOptions().GetOrCreateTabSettings( nScTab ).maUsedArea;
+ GetAddressConverter().ConvertRange( rScUsedArea, aXclUsedArea, nScTab, nScTab, false );
+ // if any error occurs in ConvertRange(), rScUsedArea keeps untouched
+ }
+ }
+ else
+ {
+ sal_uInt32 nXclRow1 = 0, nXclRow2 = 0;
+ nXclRow1 = maStrm.ReaduInt32();
+ nXclRow2 = maStrm.ReaduInt32();
+ aXclUsedArea.maFirst.mnCol = maStrm.ReaduInt16();
+ aXclUsedArea.maLast.mnCol = maStrm.ReaduInt16();
+ if( (nXclRow1 < nXclRow2) && (aXclUsedArea.GetColCount() > 1) &&
+ (nXclRow1 <= o3tl::make_unsigned( GetScMaxPos().Row() )) )
+ {
+ // Excel stores first unused row/column index
+ --nXclRow2;
+ --aXclUsedArea.maLast.mnCol;
+ // convert row indexes to 16-bit values
+ aXclUsedArea.maFirst.mnRow = static_cast< sal_uInt16 >( nXclRow1 );
+ aXclUsedArea.maLast.mnRow = limit_cast< sal_uInt16 >( nXclRow2, aXclUsedArea.maFirst.mnRow, SAL_MAX_UINT16 );
+ // create the Calc range
+ SCTAB nScTab = GetCurrScTab();
+ ScRange& rScUsedArea = GetExtDocOptions().GetOrCreateTabSettings( nScTab ).maUsedArea;
+ GetAddressConverter().ConvertRange( rScUsedArea, aXclUsedArea, nScTab, nScTab, false );
+ // if any error occurs in ConvertRange(), rScUsedArea keeps untouched
+ }
+ }
+}
+
+void ImportExcel::ReadBlank()
+{
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_BLANK );
+
+ GetXFRangeBuffer().SetBlankXF( aScPos, nXFIdx );
+ }
+}
+
+void ImportExcel::ReadInteger()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, true );
+ sal_uInt16 nValue;
+ nValue = maStrm.ReaduInt16();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, nValue);
+ }
+}
+
+void ImportExcel::ReadNumber()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_NUMBER );
+ double fValue;
+ fValue = maStrm.ReadDouble();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, fValue);
+ }
+}
+
+void ImportExcel::ReadLabel()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ /* Record ID BIFF XF type String type
+ 0x0004 2-7 3 byte 8-bit length, byte string
+ 0x0004 8 3 byte 16-bit length, unicode string
+ 0x0204 2-7 2 byte 16-bit length, byte string
+ 0x0204 8 2 byte 16-bit length, unicode string */
+ bool bBiff2 = maStrm.GetRecId() == EXC_ID2_LABEL;
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, bBiff2 );
+ XclStrFlags nFlags = (bBiff2 && (GetBiff() <= EXC_BIFF5)) ? XclStrFlags::EightBitLength : XclStrFlags::NONE;
+ XclImpString aString;
+
+ // #i63105# use text encoding from FONT record
+ rtl_TextEncoding eOldTextEnc = GetTextEncoding();
+ if( const XclImpFont* pFont = GetXFBuffer().GetFont( nXFIdx ) )
+ SetTextEncoding( pFont->GetFontEncoding() );
+ aString.Read( maStrm, nFlags );
+ SetTextEncoding( eOldTextEnc );
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, GetRoot(), aString, nXFIdx);
+}
+
+void ImportExcel::ReadBoolErr()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, maStrm.GetRecId() == EXC_ID2_BOOLERR );
+ sal_uInt8 nValue, nType;
+ nValue = maStrm.ReaduInt8();
+ nType = maStrm.ReaduInt8();
+
+ if( nType == EXC_BOOLERR_BOOL )
+ GetXFRangeBuffer().SetBoolXF( aScPos, nXFIdx );
+ else
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+
+ double fValue;
+ std::unique_ptr<ScTokenArray> pScTokArr = ErrorToFormula( nType != EXC_BOOLERR_BOOL, nValue, fValue );
+ ScFormulaCell* pCell = pScTokArr
+ ? new ScFormulaCell(rD, aScPos, std::move(pScTokArr))
+ : new ScFormulaCell(rD, aScPos);
+ pCell->SetHybridDouble( fValue );
+ GetDocImport().setFormulaCell(aScPos, pCell);
+}
+
+void ImportExcel::ReadRk()
+{
+ XclAddress aXclPos;
+ maStrm >> aXclPos;
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ {
+ sal_uInt16 nXFIdx = ReadXFIndex( aScPos, false );
+ sal_Int32 nRk;
+ nRk = maStrm.ReadInt32();
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ GetDocImport().setNumericCell(aScPos, XclTools::GetDoubleFromRK(nRk));
+ }
+}
+
+void ImportExcel::Window1()
+{
+ GetDocViewSettings().ReadWindow1( maStrm );
+}
+
+void ImportExcel::Row25()
+{
+ sal_uInt16 nRow, nRowHeight;
+
+ nRow = aIn.ReaduInt16();
+ aIn.Ignore( 4 );
+
+ if( !GetRoot().GetDoc().ValidRow( nRow ) )
+ return;
+
+ nRowHeight = aIn.ReaduInt16(); // specify direct in Twips
+ aIn.Ignore( 2 );
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// -------------------- BIFF2
+ pColRowBuff->SetHeight( nRow, nRowHeight );
+ }
+ else
+ {// -------------------- BIFF5
+ sal_uInt16 nGrbit;
+
+ aIn.Ignore( 2 ); // reserved
+ nGrbit = aIn.ReaduInt16();
+
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nGrbit, 0, 3 );
+ pRowOutlineBuff->SetLevel( nRow, nLevel, ::get_flag( nGrbit, EXC_ROW_COLLAPSED ) );
+ pColRowBuff->SetRowSettings( nRow, nRowHeight, nGrbit );
+ }
+}
+
+void ImportExcel::Bof2()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff2C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff2M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff2;
+}
+
+void ImportExcel::Eof()
+{
+ // POST: cannot be called after an invalid table!
+ EndSheet();
+ IncCurrScTab();
+}
+
+void ImportExcel::SheetPassword()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetSheetProtectBuffer().ReadPasswordHash( aIn, GetCurrScTab() );
+}
+
+void ImportExcel::Externsheet()
+{
+ OUString aUrl, aTabName;
+ bool bSameWorkBook;
+ OUString aEncodedUrl( aIn.ReadByteString( false ) );
+ XclImpUrlHelper::DecodeUrl( aUrl, aTabName, bSameWorkBook, *pExcRoot->pIR, aEncodedUrl );
+ mnLastRefIdx = pExcRoot->pExtSheetBuff->Add( aUrl, aTabName, bSameWorkBook );
+}
+
+void ImportExcel:: WinProtection()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadWinProtect( aIn );
+}
+
+void ImportExcel::Columndefault()
+{// Default Cell Attributes
+ sal_uInt16 nColMic, nColMac;
+ sal_uInt8 nOpt0;
+
+ nColMic = aIn.ReaduInt16();
+ nColMac = aIn.ReaduInt16();
+
+ OSL_ENSURE( aIn.GetRecLeft() == static_cast<std::size_t>(nColMac - nColMic) * 3 + 2,
+ "ImportExcel::Columndefault - wrong record size" );
+
+ nColMac--;
+
+ if( nColMac > rD.MaxCol() )
+ nColMac = static_cast<sal_uInt16>(rD.MaxCol());
+
+ for( sal_uInt16 nCol = nColMic ; nCol <= nColMac ; nCol++ )
+ {
+ nOpt0 = aIn.ReaduInt8();
+ aIn.Ignore( 2 ); // only 0. Attribute-Byte used
+
+ if( nOpt0 & 0x80 ) // Col hidden?
+ pColRowBuff->HideCol( nCol );
+ }
+}
+
+void ImportExcel::Array25()
+{
+ sal_uInt16 nFormLen;
+ sal_uInt16 nFirstRow = aIn.ReaduInt16();
+ sal_uInt16 nLastRow = aIn.ReaduInt16();
+ sal_uInt8 nFirstCol = aIn.ReaduInt8();
+ sal_uInt8 nLastCol = aIn.ReaduInt8();
+
+ if( GetBiff() == EXC_BIFF2 )
+ {// BIFF2
+ aIn.Ignore( 1 );
+ nFormLen = aIn.ReaduInt8();
+ }
+ else
+ {// BIFF5
+ aIn.Ignore( 6 );
+ nFormLen = aIn.ReaduInt16();
+ }
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ if (GetRoot().GetDoc().ValidColRow(nLastCol, nLastRow))
+ {
+ // the read mark is now on the formula, length in nFormLen
+
+ pFormConv->Reset( ScAddress( static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow), GetCurrScTab() ) );
+ pFormConv->Convert(pResult, maStrm, nFormLen, true);
+
+ SAL_WARN_IF(!pResult, "sc", "*ImportExcel::Array25(): ScTokenArray is NULL!");
+ }
+
+ if (pResult)
+ {
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aArrayRange(nFirstCol, nFirstRow, GetCurrScTab(), nLastCol, nLastRow, GetCurrScTab());
+ rDoc.setMatrixCells(aArrayRange, *pResult, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1);
+ }
+}
+
+void ImportExcel::Rec1904()
+{
+ sal_uInt16 n1904;
+
+ n1904 = aIn.ReaduInt16();
+
+ if( n1904 )
+ {// 1904 date system
+ ScDocOptions aOpt = rD.GetDocOptions();
+ aOpt.SetDate( 1, 1, 1904 );
+ rD.SetDocOptions( aOpt );
+ rD.GetFormatTable()->ChangeNullDate( 1, 1, 1904 );
+ }
+}
+
+void ImportExcel::Externname25()
+{
+ sal_uInt32 nRes;
+ sal_uInt16 nOpt;
+
+ nOpt = aIn.ReaduInt16();
+ nRes = aIn.ReaduInt32();
+
+ aIn.ReadByteString( false ); // name
+
+ if( ( nOpt & 0x0001 ) || ( ( nOpt & 0xFFFE ) == 0x0000 ) )
+ {// external name
+ pExcRoot->pExtNameBuff->AddName( mnLastRefIdx );
+ }
+ else if( nOpt & 0x0010 )
+ {// ole link
+ pExcRoot->pExtNameBuff->AddOLE( mnLastRefIdx, nRes ); // nRes is storage ID
+ }
+ else
+ {// dde link
+ pExcRoot->pExtNameBuff->AddDDE( mnLastRefIdx );
+ }
+}
+
+void ImportExcel::Colwidth()
+{// Column Width
+ sal_uInt8 nColFirst, nColLast;
+ sal_uInt16 nColWidth;
+
+ nColFirst = aIn.ReaduInt8();
+ nColLast = aIn.ReaduInt8();
+ nColWidth = aIn.ReaduInt16();
+
+//TODO: add a check for the unlikely case of changed MAXCOL (-> XclImpAddressConverter)
+// if( nColLast > rD.MaxCol() )
+// nColLast = static_cast<sal_uInt16>(rD.MaxCol());
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( nColWidth, GetCharWidth() );
+ pColRowBuff->SetWidthRange( nColFirst, nColLast, nScWidth );
+}
+
+void ImportExcel::Defrowheight2()
+{
+ sal_uInt16 nDefHeight;
+ nDefHeight = maStrm.ReaduInt16();
+ nDefHeight &= 0x7FFF;
+ pColRowBuff->SetDefHeight( nDefHeight, EXC_DEFROW_UNSYNCED );
+}
+
+void ImportExcel::SheetProtect()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetSheetProtectBuffer().ReadProtect( aIn, GetCurrScTab() );
+}
+
+void ImportExcel::DocProtect()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadDocProtect( aIn );
+}
+
+void ImportExcel::DocPassword()
+{
+ if (GetRoot().GetBiff() != EXC_BIFF8)
+ return;
+
+ GetRoot().GetDocProtectBuffer().ReadPasswordHash( aIn );
+}
+
+void ImportExcel::Codepage()
+{
+ SetCodePage( maStrm.ReaduInt16() );
+}
+
+void ImportExcel::Ixfe()
+{
+ mnIxfeIndex = maStrm.ReaduInt16();
+}
+
+void ImportExcel::DefColWidth()
+{
+ // stored as entire characters -> convert to 1/256 of characters (as in COLINFO)
+ double fDefWidth = 256.0 * maStrm.ReaduInt16();
+
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::DefColWidth(): pColRowBuff is NULL!");
+ return;
+ }
+
+ // #i3006# additional space for default width - Excel adds space depending on font size
+ tools::Long nFontHt = GetFontBuffer().GetAppFontData().mnHeight;
+ fDefWidth += XclTools::GetXclDefColWidthCorrection( nFontHt );
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( limit_cast< sal_uInt16 >( fDefWidth ), GetCharWidth() );
+ pColRowBuff->SetDefWidth( nScWidth );
+}
+
+void ImportExcel::Colinfo()
+{// Column Formatting Information
+ sal_uInt16 nColFirst, nColLast, nColWidth, nXF;
+ sal_uInt16 nOpt;
+
+ nColFirst = aIn.ReaduInt16();
+ nColLast = aIn.ReaduInt16();
+ nColWidth = aIn.ReaduInt16();
+ nXF = aIn.ReaduInt16();
+ nOpt = aIn.ReaduInt16();
+
+ if( nColFirst > rD.MaxCol() )
+ return;
+
+ if( nColLast > rD.MaxCol() )
+ nColLast = static_cast<sal_uInt16>(rD.MaxCol());
+
+ bool bHidden = ::get_flag( nOpt, EXC_COLINFO_HIDDEN );
+ bool bCollapsed = ::get_flag( nOpt, EXC_COLINFO_COLLAPSED );
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nOpt, 8, 3 );
+ pColOutlineBuff->SetLevelRange( nColFirst, nColLast, nLevel, bCollapsed );
+
+ if( bHidden )
+ pColRowBuff->HideColRange( nColFirst, nColLast );
+
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( nColWidth, GetCharWidth() );
+ pColRowBuff->SetWidthRange( nColFirst, nColLast, nScWidth );
+ pColRowBuff->SetDefaultXF( nColFirst, nColLast, nXF );
+}
+
+void ImportExcel::Wsbool()
+{
+ sal_uInt16 nFlags;
+ nFlags = aIn.ReaduInt16();
+
+ pRowOutlineBuff->SetButtonMode( ::get_flag( nFlags, EXC_WSBOOL_ROWBELOW ) );
+ pColOutlineBuff->SetButtonMode( ::get_flag( nFlags, EXC_WSBOOL_COLBELOW ) );
+
+ GetPageSettings().SetFitToPages( ::get_flag( nFlags, EXC_WSBOOL_FITTOPAGE ) );
+}
+
+void ImportExcel::Boundsheet()
+{
+ sal_uInt16 nGrbit = 0;
+
+ if( GetBiff() == EXC_BIFF5 )
+ {
+ aIn.DisableDecryption();
+ maSheetOffsets.push_back( aIn.ReaduInt32() );
+ aIn.EnableDecryption();
+ nGrbit = aIn.ReaduInt16();
+ }
+
+ OUString aName( aIn.ReadByteString( false ) );
+
+ SCTAB nScTab = nBdshtTab;
+ if( nScTab > 0 )
+ {
+ OSL_ENSURE( !rD.HasTable( nScTab ), "ImportExcel::Boundsheet - sheet exists already" );
+ rD.MakeTable( nScTab );
+ }
+
+ if( ( nGrbit & 0x0001 ) || ( nGrbit & 0x0002 ) )
+ rD.SetVisible( nScTab, false );
+
+ if( !rD.RenameTab( nScTab, aName ) )
+ {
+ rD.CreateValidTabName( aName );
+ rD.RenameTab( nScTab, aName );
+ }
+
+ nBdshtTab++;
+}
+
+void ImportExcel::Country()
+{
+ sal_uInt16 nUICountry, nDocCountry;
+ nUICountry = maStrm.ReaduInt16();
+ nDocCountry = maStrm.ReaduInt16();
+
+ // Store system language in XclRoot
+ LanguageType eLanguage = ::msfilter::ConvertCountryToLanguage( static_cast< ::msfilter::CountryId >( nDocCountry ) );
+ if( eLanguage != LANGUAGE_DONTKNOW )
+ SetDocLanguage( eLanguage );
+
+ // Set Excel UI language in add-in name translator
+ eLanguage = ::msfilter::ConvertCountryToLanguage( static_cast< ::msfilter::CountryId >( nUICountry ) );
+ if( eLanguage != LANGUAGE_DONTKNOW )
+ SetUILanguage( eLanguage );
+}
+
+void ImportExcel::ReadUsesElfs()
+{
+ if( maStrm.ReaduInt16() != 0 )
+ {
+ ScDocOptions aDocOpt = GetDoc().GetDocOptions();
+ aDocOpt.SetLookUpColRowNames( true );
+ GetDoc().SetDocOptions( aDocOpt );
+ }
+}
+
+void ImportExcel::Hideobj()
+{
+ sal_uInt16 nHide;
+ ScVObjMode eOle, eChart, eDraw;
+
+ nHide = aIn.ReaduInt16();
+
+ ScViewOptions aOpts( rD.GetViewOptions() );
+
+ switch( nHide )
+ {
+ case 1: // Placeholders
+ eOle = VOBJ_MODE_SHOW; // in Excel 97 only charts as place holder are displayed
+ eChart = VOBJ_MODE_SHOW; //#i80528# VOBJ_MODE_DUMMY replaced by VOBJ_MODE_SHOW now
+ eDraw = VOBJ_MODE_SHOW;
+ break;
+ case 2: // Hide all
+ eOle = VOBJ_MODE_HIDE;
+ eChart = VOBJ_MODE_HIDE;
+ eDraw = VOBJ_MODE_HIDE;
+ break;
+ default: // Show all
+ eOle = VOBJ_MODE_SHOW;
+ eChart = VOBJ_MODE_SHOW;
+ eDraw = VOBJ_MODE_SHOW;
+ break;
+ }
+
+ aOpts.SetObjMode( VOBJ_TYPE_OLE, eOle );
+ aOpts.SetObjMode( VOBJ_TYPE_CHART, eChart );
+ aOpts.SetObjMode( VOBJ_TYPE_DRAW, eDraw );
+
+ rD.SetViewOptions( aOpts );
+}
+
+void ImportExcel::Standardwidth()
+{
+ sal_uInt16 nScWidth = XclTools::GetScColumnWidth( maStrm.ReaduInt16(), GetCharWidth() );
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::Standardwidth(): pColRowBuff is NULL!");
+ return;
+ }
+ pColRowBuff->SetDefWidth( nScWidth, true );
+}
+
+void ImportExcel::Shrfmla()
+{
+ switch (mnLastRecId)
+ {
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA:
+ // This record MUST immediately follow a FORMULA record.
+ break;
+ default:
+ return;
+ }
+
+ if (!mpLastFormula)
+ // The last FORMULA record should have left this data.
+ return;
+
+ aIn.Ignore( 8 );
+ sal_uInt16 nLenExpr = aIn.ReaduInt16();
+
+ // read mark is now on the formula
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ // The shared range in this record is erroneous more than half the time.
+ // Don't ever rely on it. Use the one from the formula cell above.
+ SCCOL nCol1 = mpLastFormula->mnCol;
+ SCROW nRow1 = mpLastFormula->mnRow;
+
+ ScAddress aPos(nCol1, nRow1, GetCurrScTab());
+ pFormConv->Reset(aPos);
+ pFormConv->Convert( pResult, maStrm, nLenExpr, true, FT_SharedFormula );
+
+ if (!pResult)
+ {
+ SAL_WARN("sc", "+ImportExcel::Shrfmla(): ScTokenArray is NULL!");
+ return;
+ }
+
+ pExcRoot->pShrfmlaBuff->Store(aPos, *pResult);
+
+ // Create formula cell for the last formula record.
+
+ ScDocumentImport& rDoc = GetDocImport();
+
+ ScFormulaCell* pCell = new ScFormulaCell(rD, aPos, std::move(pResult));
+ pCell->GetCode()->WrapReference(aPos, EXC_MAXCOL8, EXC_MAXROW8);
+ rDoc.getDoc().CheckLinkFormulaNeedingCheck( *pCell->GetCode());
+ rDoc.getDoc().EnsureTable(aPos.Tab());
+ rDoc.setFormulaCell(aPos, pCell);
+ pCell->SetNeedNumberFormat(false);
+ if (std::isfinite(mpLastFormula->mfValue))
+ pCell->SetResultDouble(mpLastFormula->mfValue);
+
+ GetXFRangeBuffer().SetXF(aPos, mpLastFormula->mnXF);
+ mpLastFormula->mpCell = pCell;
+}
+
+void ImportExcel::Mulrk()
+{
+ /* rw (2 bytes): An Rw structure that specifies the row containing the
+ cells with numeric data.
+
+ colFirst (2 bytes): A Col structure that specifies the first column in
+ the series of numeric cells within the sheet. The value of colFirst.col
+ MUST be less than or equal to 254.
+
+ rgrkrec (variable): An array of RkRec structures. Each element in the
+ array specifies an RkRec in the row. The number of entries in the array
+ MUST be equal to the value given by the following formula:
+
+ Number of entries in rgrkrec = (colLast.col – colFirst.col +1)
+
+ colLast (2 bytes): A Col structure that specifies the last column in
+ the set of numeric cells within the sheet. This colLast.col value MUST
+ be greater than the colFirst.col value. */
+
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ XclAddress aCurrXclPos(aXclPos);
+ while (true)
+ {
+ if (aXclPos.mnCol > aCurrXclPos.mnCol)
+ break;
+ if (aIn.GetRecLeft() <= 2)
+ break;
+
+ sal_uInt16 nXF = aIn.ReaduInt16();
+ sal_Int32 nRkNum = aIn.ReadInt32();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aCurrXclPos, GetCurrScTab(), true ) )
+ {
+ GetXFRangeBuffer().SetXF( aScPos, nXF );
+ GetDocImport().setNumericCell(aScPos, XclTools::GetDoubleFromRK(nRkNum));
+ }
+ ++aCurrXclPos.mnCol;
+ }
+}
+
+void ImportExcel::Mulblank()
+{
+ /* rw (2 bytes): An Rw structure that specifies a row containing the blank
+ cells.
+
+ colFirst (2 bytes): A Col structure that specifies the first column in
+ the series of blank cells within the sheet. The value of colFirst.col
+ MUST be less than or equal to 254.
+
+ rgixfe (variable): An array of IXFCell structures. Each element of this
+ array contains an IXFCell structure corresponding to a blank cell in the
+ series. The number of entries in the array MUST be equal to the value
+ given by the following formula:
+
+ Number of entries in rgixfe = (colLast.col – colFirst.col +1)
+
+ colLast (2 bytes): A Col structure that specifies the last column in
+ the series of blank cells within the sheet. This colLast.col value MUST
+ be greater than colFirst.col value. */
+
+ XclAddress aXclPos;
+ aIn >> aXclPos;
+
+ XclAddress aCurrXclPos(aXclPos);
+ while (true)
+ {
+ if (aXclPos.mnCol > aCurrXclPos.mnCol)
+ break;
+ if (aIn.GetRecLeft() <= 2)
+ break;
+
+ sal_uInt16 nXF = aIn.ReaduInt16();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScPos, aCurrXclPos, GetCurrScTab(), true ) )
+ GetXFRangeBuffer().SetBlankXF( aScPos, nXF );
+ ++aCurrXclPos.mnCol;
+ }
+}
+
+void ImportExcel::Rstring()
+{
+ XclAddress aXclPos;
+ sal_uInt16 nXFIdx;
+ aIn >> aXclPos;
+ nXFIdx = aIn.ReaduInt16();
+
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScPos, aXclPos, GetCurrScTab(), true ) )
+ return;
+
+ // unformatted Unicode string with separate formatting information
+ XclImpString aString;
+ aString.Read( maStrm );
+
+ // character formatting runs
+ if( !aString.IsRich() )
+ aString.ReadFormats( maStrm );
+
+ GetXFRangeBuffer().SetXF( aScPos, nXFIdx );
+ XclImpStringHelper::SetToDocument(GetDocImport(), aScPos, *this, aString, nXFIdx);
+}
+
+void ImportExcel::Cellmerging()
+{
+ XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ SCTAB nScTab = GetCurrScTab();
+
+ sal_uInt16 nCount = maStrm.ReaduInt16();
+ sal_uInt16 nIdx = 0;
+ while (true)
+ {
+ if (maStrm.GetRecLeft() < 8)
+ break;
+ if (nIdx >= nCount)
+ break;
+ XclRange aXclRange;
+ maStrm >> aXclRange; // 16-bit rows and columns
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( rAddrConv.ConvertRange( aScRange, aXclRange, nScTab, nScTab, true ) )
+ GetXFRangeBuffer().SetMerge( aScRange.aStart.Col(), aScRange.aStart.Row(), aScRange.aEnd.Col(), aScRange.aEnd.Row() );
+ ++nIdx;
+ }
+}
+
+void ImportExcel::Olesize()
+{
+ XclRange aXclOleSize( ScAddress::UNINITIALIZED );
+ maStrm.Ignore( 2 );
+ aXclOleSize.Read( maStrm, false );
+
+ SCTAB nScTab = GetCurrScTab();
+ GetAddressConverter().ConvertRange( maScOleSize, aXclOleSize, nScTab, nScTab, false );
+}
+
+void ImportExcel::Row34()
+{
+ sal_uInt16 nRow, nRowHeight, nGrbit, nXF;
+
+ nRow = aIn.ReaduInt16();
+ aIn.Ignore( 4 );
+
+ SCROW nScRow = static_cast< SCROW >( nRow );
+
+ if( !GetRoot().GetDoc().ValidRow( nScRow ) )
+ return;
+
+ nRowHeight = aIn.ReaduInt16(); // specify direct in Twips
+ aIn.Ignore( 4 );
+
+ nRowHeight = nRowHeight & 0x7FFF; // Bit 15: Row Height not changed manually
+ if( !nRowHeight )
+ nRowHeight = (GetBiff() == EXC_BIFF2) ? 0x25 : 0x225;
+
+ nGrbit = aIn.ReaduInt16();
+ nXF = aIn.ReaduInt16();
+
+ sal_uInt8 nLevel = ::extract_value< sal_uInt8 >( nGrbit, 0, 3 );
+ pRowOutlineBuff->SetLevel( nScRow, nLevel, ::get_flag( nGrbit, EXC_ROW_COLLAPSED ) );
+ pColRowBuff->SetRowSettings( nScRow, nRowHeight, nGrbit );
+
+ if( nGrbit & EXC_ROW_USEDEFXF )
+ GetXFRangeBuffer().SetRowDefXF( nScRow, nXF & EXC_ROW_XFMASK );
+}
+
+void ImportExcel::Bof3()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ OSL_ENSURE( nSubType != 0x0100, "*ImportExcel::Bof3(): Biff3 as Workbook?!" );
+ if( nSubType == 0x0100 ) // Book
+ pExcRoot->eDateiTyp = Biff3W;
+ else if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff3C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff3M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff3;
+}
+
+void ImportExcel::Array34()
+{
+ sal_uInt16 nFirstRow, nLastRow, nFormLen;
+ sal_uInt8 nFirstCol, nLastCol;
+
+ nFirstRow = aIn.ReaduInt16();
+ nLastRow = aIn.ReaduInt16();
+ nFirstCol = aIn.ReaduInt8();
+ nLastCol = aIn.ReaduInt8();
+ aIn.Ignore( (GetBiff() >= EXC_BIFF5) ? 6 : 2 );
+ nFormLen = aIn.ReaduInt16();
+
+ std::unique_ptr<ScTokenArray> pResult;
+
+ if( GetRoot().GetDoc().ValidColRow( nLastCol, nLastRow ) )
+ {
+ // the read mark is now on the formula, length in nFormLen
+
+ pFormConv->Reset( ScAddress( static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow), GetCurrScTab() ) );
+ pFormConv->Convert( pResult, maStrm, nFormLen, true );
+
+ SAL_WARN_IF(!pResult, "sc", "+ImportExcel::Array34(): ScTokenArray is NULL!");
+ }
+
+ if (pResult)
+ {
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aArrayRange(nFirstCol, nFirstRow, GetCurrScTab(), nLastCol, nLastRow, GetCurrScTab());
+ rDoc.setMatrixCells(aArrayRange, *pResult, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1);
+ }
+}
+
+void ImportExcel::Defrowheight345()
+{
+ sal_uInt16 nFlags, nDefHeight;
+ nFlags = maStrm.ReaduInt16();
+ nDefHeight = maStrm.ReaduInt16();
+
+ if (!pColRowBuff)
+ {
+ SAL_WARN("sc", "*ImportExcel::Defrowheight345(): pColRowBuff is NULL!");
+ return;
+ }
+
+ pColRowBuff->SetDefHeight( nDefHeight, nFlags );
+}
+
+void ImportExcel::TableOp()
+{
+ sal_uInt16 nFirstRow = aIn.ReaduInt16();
+ sal_uInt16 nLastRow = aIn.ReaduInt16();
+ sal_uInt8 nFirstCol = aIn.ReaduInt8();
+ sal_uInt8 nLastCol = aIn.ReaduInt8();
+ sal_uInt16 nGrbit = aIn.ReaduInt16();
+ sal_uInt16 nInpRow = aIn.ReaduInt16();
+ sal_uInt16 nInpCol = aIn.ReaduInt16();
+ sal_uInt16 nInpRow2 = aIn.ReaduInt16();
+ sal_uInt16 nInpCol2 = aIn.ReaduInt16();
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ //shrink to smallish arbitrary value to not timeout
+ nLastRow = std::min<sal_uInt16>(nLastRow, MAXROW_30 / 2);
+ }
+
+ if( GetRoot().GetDoc().ValidColRow( nLastCol, nLastRow ) )
+ {
+ if( nFirstCol && nFirstRow )
+ {
+ ScTabOpParam aTabOpParam;
+ aTabOpParam.meMode = (nGrbit & EXC_TABLEOP_BOTH) ? ScTabOpParam::Both : ((nGrbit & EXC_TABLEOP_ROW) ? ScTabOpParam::Row : ScTabOpParam::Column);
+ sal_uInt16 nCol = nFirstCol - 1;
+ sal_uInt16 nRow = nFirstRow - 1;
+ SCTAB nTab = GetCurrScTab();
+ switch (aTabOpParam.meMode)
+ {
+ case ScTabOpParam::Column:
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefFormulaEnd.Set(
+ static_cast<SCCOL>(nLastCol),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefColCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ nRow++;
+ break;
+ case ScTabOpParam::Row:
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nFirstRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefFormulaEnd.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nLastRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefRowCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ nCol++;
+ break;
+ case ScTabOpParam::Both: // TWO-INPUT
+ aTabOpParam.aRefFormulaCell.Set(
+ static_cast<SCCOL>(nFirstCol - 1),
+ static_cast<SCROW>(nFirstRow - 1), nTab, false,
+ false, false );
+ aTabOpParam.aRefRowCell.Set( static_cast<SCCOL>(nInpCol),
+ static_cast<SCROW>(nInpRow), nTab, false, false,
+ false );
+ aTabOpParam.aRefColCell.Set( static_cast<SCCOL>(nInpCol2),
+ static_cast<SCROW>(nInpRow2), nTab, false, false,
+ false );
+ break;
+ }
+
+ ScDocumentImport& rDoc = GetDocImport();
+ ScRange aTabOpRange(nCol, nRow, nTab, nLastCol, nLastRow, nTab);
+ rDoc.setTableOpCells(aTabOpRange, aTabOpParam);
+ }
+ }
+ else
+ {
+ bTabTruncated = true;
+ GetTracer().TraceInvalidRow(nLastRow, rD.MaxRow());
+ }
+}
+
+void ImportExcel::Bof4()
+{
+ sal_uInt16 nSubType;
+ maStrm.DisableDecryption();
+ maStrm.Ignore( 2 );
+ nSubType = maStrm.ReaduInt16();
+
+ if( nSubType == 0x0100 ) // Book
+ pExcRoot->eDateiTyp = Biff4W;
+ else if( nSubType == 0x0020 ) // Chart
+ pExcRoot->eDateiTyp = Biff4C;
+ else if( nSubType == 0x0040 ) // Macro
+ pExcRoot->eDateiTyp = Biff4M;
+ else // #i51490# Excel interprets invalid indexes as worksheet
+ pExcRoot->eDateiTyp = Biff4;
+}
+
+void ImportExcel::Bof5()
+{
+ //POST: eDateiTyp = Type of the file to be read
+ sal_uInt16 nSubType, nVers;
+ BiffTyp eDatei;
+
+ maStrm.DisableDecryption();
+ nVers = maStrm.ReaduInt16();
+ nSubType = maStrm.ReaduInt16( );
+
+ switch( nSubType )
+ {
+ case 0x0005: eDatei = Biff5W; break; // workbook globals
+ case 0x0006: eDatei = Biff5V; break; // VB module
+ case 0x0020: eDatei = Biff5C; break; // chart
+ case 0x0040: eDatei = Biff5M4; break; // macro sheet
+ case 0x0010: // worksheet
+ default: eDatei = Biff5; break; // tdf#144732 Excel interprets invalid indexes as worksheet
+ }
+
+ if( nVers == 0x0600 && (GetBiff() == EXC_BIFF8) )
+ eDatei = static_cast<BiffTyp>( eDatei - Biff5 + Biff8 );
+
+ pExcRoot->eDateiTyp = eDatei;
+}
+
+void ImportExcel::EndSheet()
+{
+ pExcRoot->pExtSheetBuff->Reset();
+
+ if( GetBiff() <= EXC_BIFF5 )
+ {
+ pExcRoot->pExtNameBuff->Reset();
+ mnLastRefIdx = 0;
+ }
+
+ FinalizeTable();
+}
+
+void ImportExcel::NewTable()
+{
+ SCTAB nTab = GetCurrScTab();
+ if( nTab > 0 && !rD.HasTable( nTab ) )
+ rD.MakeTable( nTab );
+
+ if (nTab == 0 && GetBiff() == EXC_BIFF2)
+ {
+ // For Excel 2.1 Worksheet file, we need to set the file name as the
+ // sheet name.
+ INetURLObject aURL(GetDocUrl());
+ rD.RenameTab(0, aURL.getBase());
+ }
+
+ pExcRoot->pShrfmlaBuff->Clear();
+ maLastFormulaCells.clear();
+ mpLastFormula = nullptr;
+
+ InitializeTable( nTab );
+
+ XclImpOutlineDataBuffer* pNewItem = new XclImpOutlineDataBuffer( GetRoot(), nTab );
+ pOutlineListBuffer->push_back( std::unique_ptr<XclImpOutlineDataBuffer>(pNewItem) );
+ pExcRoot->pColRowBuff = pColRowBuff = pNewItem->GetColRowBuff();
+ pColOutlineBuff = pNewItem->GetColOutline();
+ pRowOutlineBuff = pNewItem->GetRowOutline();
+}
+
+std::unique_ptr<ScTokenArray> ImportExcel::ErrorToFormula( bool bErrOrVal, sal_uInt8 nError, double& rVal )
+{
+ return pFormConv->GetBoolErr( XclTools::ErrorToEnum( rVal, bErrOrVal, nError ) );
+}
+
+void ImportExcel::AdjustRowHeight()
+{
+ /* Speed up chart import: import all sheets without charts, then
+ update row heights (here), last load all charts -> do not any longer
+ update inside of ScDocShell::ConvertFrom() (causes update of existing
+ charts during each and every change of row height). */
+ if( ScModelObj* pDocObj = GetDocModelObj() )
+ pDocObj->UpdateAllRowHeights();
+}
+
+void ImportExcel::PostDocLoad()
+{
+ /* Set automatic page numbering in Default page style (default is "page number = 1").
+ Otherwise hidden tables (i.e. for scenarios) which have Default page style will
+ break automatic page numbering. */
+ if( SfxStyleSheetBase* pStyleSheet = GetStyleSheetPool().Find( ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Page ) )
+ pStyleSheet->GetItemSet().Put( SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, 0 ) );
+
+ // outlines for all sheets, sets hidden rows and columns (#i11776# after filtered ranges)
+ for (auto& rxBuffer : *pOutlineListBuffer)
+ rxBuffer->Convert();
+
+ // document view settings (before visible OLE area)
+ GetDocViewSettings().Finalize();
+
+ // process all drawing objects (including OLE, charts, controls; after hiding rows/columns; before visible OLE area)
+ GetObjectManager().ConvertObjects();
+
+ // visible area (used if this document is an embedded OLE object)
+ if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ // visible area if embedded
+ const ScExtDocSettings& rDocSett = GetExtDocOptions().GetDocSettings();
+ SCTAB nDisplScTab = rDocSett.mnDisplTab;
+
+ /* #i44077# If a new OLE object is inserted from file, there is no
+ OLESIZE record in the Excel file. Calculate used area from file
+ contents (used cells and drawing objects). */
+ if( !maScOleSize.IsValid() )
+ {
+ // used area of displayed sheet (cell contents)
+ if( const ScExtTabSettings* pTabSett = GetExtDocOptions().GetTabSettings( nDisplScTab ) )
+ maScOleSize = pTabSett->maUsedArea;
+ // add all valid drawing objects
+ ScRange aScObjArea = GetObjectManager().GetUsedArea( nDisplScTab );
+ if( aScObjArea.IsValid() )
+ maScOleSize.ExtendTo( aScObjArea );
+ }
+
+ // valid size found - set it at the document
+ if( maScOleSize.IsValid() )
+ {
+ pDocShell->SetVisArea( GetDoc().GetMMRect(
+ maScOleSize.aStart.Col(), maScOleSize.aStart.Row(),
+ maScOleSize.aEnd.Col(), maScOleSize.aEnd.Row(), nDisplScTab ) );
+ GetDoc().SetVisibleTab( nDisplScTab );
+ }
+ }
+
+ // open forms in alive mode (has no effect, if no controls in document)
+ if( ScModelObj* pDocObj = GetDocModelObj() )
+ pDocObj->setPropertyValue( SC_UNO_APPLYFMDES, uno::Any( false ) );
+
+ // enables extended options to be set to the view after import
+ GetExtDocOptions().SetChanged( true );
+
+ // root data owns the extended document options -> create a new object
+ GetDoc().SetExtDocOptions( std::make_unique<ScExtDocOptions>( GetExtDocOptions() ) );
+
+ const SCTAB nLast = rD.GetTableCount();
+ const ScRange* p;
+
+ if( GetRoot().GetPrintAreaBuffer().HasRanges() )
+ {
+ for( SCTAB n = 0 ; n < nLast ; n++ )
+ {
+ p = GetRoot().GetPrintAreaBuffer().First(n);
+ if( p )
+ {
+ rD.ClearPrintRanges( n );
+ while( p )
+ {
+ rD.AddPrintRange( n, *p );
+ p = GetRoot().GetPrintAreaBuffer().Next();
+ }
+ }
+ else
+ {
+ // #i4063# no print ranges -> print entire sheet
+ rD.SetPrintEntireSheet( n );
+ }
+ }
+ GetTracer().TracePrintRange();
+ }
+
+ if( !GetRoot().GetTitleAreaBuffer().HasRanges() )
+ return;
+
+ for( SCTAB n = 0 ; n < nLast ; n++ )
+ {
+ p = GetRoot().GetTitleAreaBuffer().First(n);
+ if( p )
+ {
+ bool bRowVirgin = true;
+ bool bColVirgin = true;
+
+ while( p )
+ {
+ if( p->aStart.Col() == 0 && p->aEnd.Col() == rD.MaxCol() && bRowVirgin )
+ {
+ rD.SetRepeatRowRange( n, *p );
+ bRowVirgin = false;
+ }
+
+ if( p->aStart.Row() == 0 && p->aEnd.Row() == rD.MaxRow() && bColVirgin )
+ {
+ rD.SetRepeatColRange( n, *p );
+ bColVirgin = false;
+ }
+
+ p = GetRoot().GetTitleAreaBuffer().Next();
+ }
+ }
+ }
+}
+
+XclImpOutlineDataBuffer::XclImpOutlineDataBuffer( const XclImpRoot& rRoot, SCTAB nScTab ) :
+ XclImpRoot( rRoot ),
+ mxColOutlineBuff( std::make_shared<XclImpOutlineBuffer>( rRoot.GetXclMaxPos().Col() + 1 ) ),
+ mxRowOutlineBuff( std::make_shared<XclImpOutlineBuffer>( rRoot.GetXclMaxPos().Row() + 1 ) ),
+ mxColRowBuff( std::make_shared<XclImpColRowSettings>( rRoot ) ),
+ mnScTab( nScTab )
+{
+}
+
+XclImpOutlineDataBuffer::~XclImpOutlineDataBuffer()
+{
+}
+
+void XclImpOutlineDataBuffer::Convert()
+{
+ mxColOutlineBuff->SetOutlineArray( &GetDoc().GetOutlineTable( mnScTab, true )->GetColArray() );
+ mxColOutlineBuff->MakeScOutline();
+
+ mxRowOutlineBuff->SetOutlineArray( &GetDoc().GetOutlineTable( mnScTab, true )->GetRowArray() );
+ mxRowOutlineBuff->MakeScOutline();
+
+ mxColRowBuff->ConvertHiddenFlags( mnScTab );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/namebuff.cxx b/sc/source/filter/excel/namebuff.cxx
new file mode 100644
index 000000000..523145209
--- /dev/null
+++ b/sc/source/filter/excel/namebuff.cxx
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <namebuff.hxx>
+
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <tokenarray.hxx>
+
+#include <root.hxx>
+#include <xiroot.hxx>
+
+#include <osl/diagnose.h>
+
+sal_uInt32 StringHashEntry::MakeHashCode( const OUString& r )
+{
+ sal_uInt32 n = 0;
+ const sal_Unicode* pCurrent = r.getStr();
+ sal_Unicode cCurrent = *pCurrent;
+
+ while( cCurrent )
+ {
+ n *= 70;
+ n += static_cast<sal_uInt32>(cCurrent);
+ pCurrent++;
+ cCurrent = *pCurrent;
+ }
+
+ return n;
+}
+
+SharedFormulaBuffer::SharedFormulaBuffer(RootData* pRD)
+ : ExcRoot(pRD)
+{
+}
+
+SharedFormulaBuffer::~SharedFormulaBuffer()
+{
+ Clear();
+}
+
+void SharedFormulaBuffer::Clear()
+{
+ maTokenArrays.clear();
+}
+
+void SharedFormulaBuffer::Store( const ScAddress& rPos, const ScTokenArray& rArray )
+{
+ ScTokenArray aCode(rArray.CloneValue());
+ aCode.GenHash();
+ maTokenArrays.emplace(rPos, std::move(aCode));
+}
+
+const ScTokenArray* SharedFormulaBuffer::Find( const ScAddress& rRefPos ) const
+{
+ TokenArraysType::const_iterator it = maTokenArrays.find(rRefPos);
+ if (it == maTokenArrays.end())
+ return nullptr;
+
+ return &it->second;
+}
+
+sal_Int16 ExtSheetBuffer::Add( const OUString& rFPAN, const OUString& rTN, const bool bSWB )
+{
+ maEntries.emplace_back( rFPAN, rTN, bSWB );
+ // return 1-based index of EXTERNSHEET
+ return static_cast< sal_Int16 >( maEntries.size() );
+}
+
+bool ExtSheetBuffer::GetScTabIndex( sal_uInt16 nExcIndex, sal_uInt16& rScIndex )
+{
+ OSL_ENSURE( nExcIndex,
+ "*ExtSheetBuffer::GetScTabIndex(): Sheet-Index == 0!" );
+
+ if ( !nExcIndex || nExcIndex > maEntries.size() )
+ return false;
+
+ Cont* pCur = &maEntries[ nExcIndex - 1 ];
+ sal_uInt16& rTabNum = pCur->nTabNum;
+
+ if( rTabNum < 0xFFFD )
+ {
+ rScIndex = rTabNum;
+ return true;
+ }
+
+ if( rTabNum == 0xFFFF )
+ {// create new table
+ SCTAB nNewTabNum;
+ if( pCur->bSWB )
+ {// table is in the same workbook!
+ if( pExcRoot->pIR->GetDoc().GetTable( pCur->aTab, nNewTabNum ) )
+ {
+ rScIndex = rTabNum = static_cast<sal_uInt16>(nNewTabNum);
+ return true;
+ }
+ else
+ rTabNum = 0xFFFD;
+ }
+ else if( pExcRoot->pIR->GetDocShell() )
+ {// table is 'really' external
+ if( pExcRoot->pIR->GetExtDocOptions().GetDocSettings().mnLinkCnt == 0 )
+ {
+ OUString aURL( ScGlobal::GetAbsDocName( pCur->aFile,
+ pExcRoot->pIR->GetDocShell() ) );
+ OUString aTabName( ScGlobal::GetDocTabName( aURL, pCur->aTab ) );
+ if( pExcRoot->pIR->GetDoc().LinkExternalTab( nNewTabNum, aTabName, aURL, pCur->aTab ) )
+ {
+ rScIndex = rTabNum = static_cast<sal_uInt16>(nNewTabNum);
+ return true;
+ }
+ else
+ rTabNum = 0xFFFE; // no table is created for now -> and likely
+ // will not be created later...
+ }
+ else
+ rTabNum = 0xFFFE;
+
+ }
+ }
+
+ return false;
+}
+
+void ExtSheetBuffer::Reset()
+{
+ maEntries.clear();
+}
+
+bool ExtName::IsOLE() const
+{
+ return ( nFlags & 0x0002 ) != 0;
+}
+
+ExtNameBuff::ExtNameBuff( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void ExtNameBuff::AddDDE( sal_Int16 nRefIdx )
+{
+ ExtName aNew( 0x0001 );
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+void ExtNameBuff::AddOLE( sal_Int16 nRefIdx, sal_uInt32 nStorageId )
+{
+ ExtName aNew( 0x0002 );
+ aNew.nStorageId = nStorageId;
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+void ExtNameBuff::AddName( sal_Int16 nRefIdx )
+{
+ ExtName aNew( 0x0004 );
+ maExtNames[ nRefIdx ].push_back( aNew );
+}
+
+const ExtName* ExtNameBuff::GetNameByIndex( sal_Int16 nRefIdx, sal_uInt16 nNameIdx ) const
+{
+ OSL_ENSURE( nNameIdx > 0, "ExtNameBuff::GetNameByIndex() - invalid name index" );
+ ExtNameMap::const_iterator aIt = maExtNames.find( nRefIdx );
+ return ((aIt != maExtNames.end()) && (0 < nNameIdx) && (nNameIdx <= aIt->second.size())) ? &aIt->second[ nNameIdx - 1 ] : nullptr;
+}
+
+void ExtNameBuff::Reset()
+{
+ maExtNames.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/ooxml-export-TODO.txt b/sc/source/filter/excel/ooxml-export-TODO.txt
new file mode 100644
index 000000000..d995598d2
--- /dev/null
+++ b/sc/source/filter/excel/ooxml-export-TODO.txt
@@ -0,0 +1,164 @@
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+TODO/Unimplemented Calc OOXML Export Features:
+=============================================
+
+Partially implemented features are not mentioned here; grep for OOXTODO within
+sc/source/filter/*.
+
+In updated OfficeFileFormatsProtocols.zip [MS-XLS].pdf,
+Section §2.3.1 (p.154) provides the record name :: record number mapping, and
+Section §2.3.2 (p.165) provides the record number :: record name mapping.
+
+Elements:
+ - Workbook (§3.2):
+ - customWorkbookViews (§3.2.3)
+ - ext (§3.2.7)
+ - extLst (§3.2.10)
+ - fileRecoveryPr (§3.2.11) [ CRASHRECERR? 865h ]
+ - fileSharing (§3.2.12) [ FILESHARING 5Bh ]
+ - functionGroup (§3.2.14) [ FNGRP12 898h; FNGROUPNAME 9Ah ]
+ - functionGroups (§3.2.15) [ FNGROUPCOUNT: 9Ch ]
+ - oleSize (§3.2.16) [ OLESIZE DEh ]
+ - smartTagPr (§3.2.21) [ BOOKEXT 863h ]
+ - smartTagType (§3.2.22) [ unknown record ]
+ - smartTagTypes (§3.2.23) [ unknown record ]
+ - webPublishing (§3.2.24) [ WOPT 80Bh ]
+ - webPublishObject (§3.2.25) [ WEBPUB 801h ]
+ - webPublishObjects (§3.2.26) [ unsupported ]
+ - Worksheets (§3.3.1):
+ - autoFilter (§3.3.1.1) [ AutoFilter 9Eh ]
+ - cellSmartTag (§3.3.1.4) [ FEAT 868h ]
+ - cellSmartTagPr (§3.3.1.5) [ FEAT? 868h ]
+ - cellSmartTags (§3.3.1.6) [ FEAT 868h ]
+ - cellWatch (§3.3.1.7) [ CELLWATCH 86Ch ]
+ - cellWatches (§3.3.1.8) [ CELLWATCH 86Ch ]
+ - cfRule (§3.3.1.9) [ CF 1B1h ]
+ - cfvo (§3.3.1.10) [ CF12 87Ah ]
+ - chartsheet (§3.3.1.11) [ CHARTFRTINFO 850h, FRTWRAPPER 851h...]
+ - color (§3.3.1.14) [ DXF 88Dh xfpropBorder?
+ XFEXT 87Dh xclrType? ]
+ - colorScale (§3.3.1.15) [ DXF 88Dh? ]
+ - control (§3.3.1.18) [ ??? ]
+ - controls (§3.3.1.19) [ ??? ]
+ - customPr (§3.3.1.20) [ ??? ]
+ - customProperties (§3.3.1.21) [ ??? ]
+ - customSheetView (§3.3.1.22) [ ???; for charts; see chartsheet? ]
+ - customSheetView (§3.3.1.23) [ ??? ]
+ - customSheetViews (§3.3.1.24) [ ???; for charts; see chartsheet? ]
+ - customSheetViews (§3.3.1.25) [ ??? ]
+ - dataBar (§3.3.1.26) [ CF12 87Ah ct=Databar ]
+ - dataConsolidate (§3.3.1.27) [ DCON 50h ]
+ - dataRef (§3.3.1.28) [ DCONBIN 1B5h ]
+ - dataRefs (§3.3.1.29) [ ??? ]
+ - dialogsheet (§3.3.1.32) [ ??? ]
+ - drawing (§3.3.1.34) [ ??? ]
+ - evenFooter (§3.3.1.35) [ HeaderFooter 89Ch ]
+ - evenHeader (§3.3.1.36) [ HeaderFooter 89Ch ]
+ - firstFooter (§3.3.1.38) [ HeaderFooter 89Ch ]
+ - firstHeader (§3.3.1.39) [ HeaderFooter 89Ch ]
+ - formula (§3.3.1.40) [ CF 1B1h ]
+ - iconSet (§3.3.1.46) [ CF12 87Ah ct=CFMultistate ]
+ - ignoredError (§3.3.1.47) [ Feat/FeatFormulaErr2/FFErrorCheck 868h ]
+ - ignoredErrors (§3.3.1.48) [ Feat 868h ]
+ - legacyDrawing (§3.3.1.51) [ MsoDrawing ECh ]
+ - legacyDrawingHF (§3.3.1.52) [ ??? ]
+ - oleObject (§3.3.1.57) [ ??? ]
+ - oleObjects (§3.3.1.58) [ ??? ]
+ - outlinePr (§3.3.1.59) [ ??? ]
+ - pageSetup (§3.3.1.62) [ ???; for charts; see chartsheet? ]
+ - picture (§3.3.1.65) [ BkHim E9h; see XclExpBitmap ]
+ - pivotArea (§3.3.1.66) [ ??? ]
+ - pivotSelection (§3.3.1.67) [ ??? ]
+ - protectedRange (§3.3.1.69) [ ??? ]
+ - protectedRanges (§3.3.1.70) [ ??? ]
+ - sheetCalcPr (§3.3.1.76) [ REFRESHALL?? ]
+ - sheetFormatPr (§3.3.1.78) [ lots of records? ]
+ @defaultColWidth: DefColWidth
+ @defaultRowHeight: DEFROWHEIGHT
+ @baseColWidth: ColInfo/coldx?
+ @customHeight: ColInfo/fUserSet?
+ @zeroHeight: ColInfo/fHidden?
+ @thickTop: ?
+ @thickBottom: ?
+ @outlineLevelRow: ?
+ @outlineLevelCol: ColInfo/iOutLevel?
+ - sheetPr (§3.3.1.80) [ ??? ; for charts ]
+ - sheetView (§3.3.1.84) [ ??? ; for charts ]
+ - sheetViews (§3.3.1.86) [ ??? ; for charts ]
+ - smartTags (§3.3.1.87) [ FEAT 868h; isf=ISFFACTOID ]
+ - sortCondition (§3.3.1.88) [ SortData 895h? ]
+ - sortState (§3.3.1.89) [ Sort 90h ]
+ - tabColor (§3.3.1.90) [ SheetExt 862h ]
+ - tablePart (§3.3.1.91) [ ??? ]
+ - tableParts (§3.3.1.92) [ ??? ]
+ - webPublishItem (§3.3.1.94) [ WebPub 801h ]
+ - webPublishItems (§3.3.1.95)
+ - AutoFilter Settings (§3.3.2):
+ - colorFilter (§3.3.2.1) [ AutoFilter12 87Eh,
+ DXFN12NoCB struct ]
+ - dateGroupItem (§3.3.2.4) [ AutoFilter12 87Eh,
+ AF12DateInfo struct ]
+ - dynamicFilter (§3.3.2.5) [ AutoFilter12 87Eh, cft field ]
+ - filter (§3.3.2.6) [ AutoFilter12 87Eh, rgCriteria? ]
+ - filters (§3.3.2.9) [ AutoFilter12 87Eh, rgCriteria? ]
+ - iconFilter (§3.3.2.9) [ AutoFilter12 87Eh,
+ AF12CellIcon struct ]
+ - Shared String Table (§3.4):
+ - phoneticPr (§3.4.3)
+ - rPh (§3.4.6)
+ - Tables (§3.5.1):
+ - calculatedColumnFormula (§3.5.1.1)
+ [ ??? ]
+ - table (§3.5.1.2) [ ??? ]
+ - tableColumn (§3.5.1.3) [ ??? ]
+ - tableColumns (§3.5.1.4) [ ??? ]
+ - tableStyleInfo (§3.5.1.5) [ ??? ]
+ - totalRowFormula (§3.5.1.6) [ ??? ]
+ - xmlColumnPr (§3.5.1.7) [ ??? ]
+ - Single Cell Tables (§3.5.2):
+ - singleXmlCell (§3.5.2.1) [ ??? ]
+ - singleXmlCells (§3.5.2.2) [ ??? ]
+ - xmlCellPr (§3.5.2.3) [ ??? ]
+ - xmlPr (§3.5.2.4) [ ??? ]
+ - Calculation Chain (§3.6):
+ - c (§3.6.1) [ ??? ]
+ - calcChain (§3.6.2) [ ??? ]
+ - Comments (§3.7):
+ - Note: Excel *requires* that there be a drawing object associated
+ with the comment before it will show it. If you _just_ generate the
+ <comments/> XML part and create a <Relationship/> for it, Excel
+ will NOT display the comment.
+ - As drawing is not currently implemented, comments support is
+ incomplete.
+ - TODO: text formatting. Currently we only write unformatted text
+ into comments?.xml, as I'm not sure how formatted text is handled.
+ - Styles (§3.8):
+ - dxf (§3.8.14): [ DXF 88Dh; unsupported ]
+ - dxfs (§3.8.15): [ DXF 88Dh ]
+ - gradientFill (§3.8.23): [ ??? ]
+ - horizontal (§3.8.24): [ DXF 88Dh fNewBorder, xfprops ]
+ - mruColors (§3.8.28): [ ??? ]
+ - scheme (§3.8.36): [ ??? ]
+ - stop (§3.8.38): [ ??? ]
+ - tableStyle (§3.8.40): [ TableStyle 88Fh; unsupported ]
+ - tableStyleElement (§3.8.41): [ TableStyleElement 890h; unsupported ]
+ - tableStyles (§3.8.42): [ TableStyles 88Eh; unsupported ]
+ - vertical (§3.8.44): [ DXF 88Dh fNewBorder, xfprops ]
+
diff --git a/sc/source/filter/excel/read.cxx b/sc/source/filter/excel/read.cxx
new file mode 100644
index 000000000..cf9465a37
--- /dev/null
+++ b/sc/source/filter/excel/read.cxx
@@ -0,0 +1,1314 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include <scerrors.hxx>
+#include <fprogressbar.hxx>
+#include <globstr.hrc>
+#include <xlcontent.hxx>
+#include <xltracer.hxx>
+#include <xltable.hxx>
+#include <xihelper.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+#include <xlname.hxx>
+#include <xicontent.hxx>
+#include <xiescher.hxx>
+#include <xipivot.hxx>
+#include <xistyle.hxx>
+#include <XclImpChangeTrack.hxx>
+#include <documentimport.hxx>
+
+#include <root.hxx>
+#include <imp_op.hxx>
+#include <excimp8.hxx>
+
+#include <unotools/configmgr.hxx>
+
+#include <memory>
+
+namespace
+{
+ bool TryStartNextRecord(XclImpStream& rIn, std::size_t nProgressBasePos)
+ {
+ bool bValid = true;
+ // i#115255 fdo#40304 BOUNDSHEET doesn't point to a valid
+ // BOF record position. Scan the records manually (from
+ // the BOUNDSHEET position) until we find a BOF. Some 3rd
+ // party Russian programs generate invalid xls docs with
+ // this kind of silliness.
+ if (rIn.PeekRecId(nProgressBasePos) == EXC_ID5_BOF)
+ // BOUNDSHEET points to a valid BOF record. Good.
+ rIn.StartNextRecord(nProgressBasePos);
+ else
+ {
+ while (bValid && rIn.GetRecId() != EXC_ID5_BOF)
+ bValid = rIn.StartNextRecord();
+ }
+ return bValid;
+ }
+}
+
+ErrCode ImportExcel::Read()
+{
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+ XclImpPalette& rPal = GetPalette();
+ XclImpFontBuffer& rFontBfr = GetFontBuffer();
+ XclImpNumFmtBuffer& rNumFmtBfr = GetNumFmtBuffer();
+ XclImpXFBuffer& rXFBfr = GetXFBuffer();
+ XclImpNameManager& rNameMgr = GetNameManager();
+ // call to GetCurrSheetDrawing() cannot be cached (changes in new sheets)
+
+ enum STATE {
+ Z_BiffNull, // not a valid Biff-Format
+ Z_Biff2, // Biff2: only one table
+
+ Z_Biff3, // Biff3: only one table
+
+ Z_Biff4, // Biff4: only one table
+ Z_Biff4W, // Biff4 Workbook: Globals
+ Z_Biff4T, // Biff4 Workbook: a table itself
+ Z_Biff4E, // Biff4 Workbook: between tables
+
+ Z_Biff5WPre,// Biff5: Prefetch Workbook
+ Z_Biff5W, // Biff5: Globals
+ Z_Biff5TPre,// Biff5: Prefetch for Shrfmla/Array Formula
+ Z_Biff5T, // Biff5: a table itself
+ Z_Biff5E, // Biff5: between tables
+ Z_Biffn0, // all Biffs: skip table till next EOF
+ Z_End };
+
+ STATE eCurrent = Z_BiffNull, ePrev = Z_BiffNull;
+
+ ErrCode eLastErr = ERRCODE_NONE;
+ sal_uInt16 nOpcode;
+ sal_uInt16 nBofLevel = 0;
+
+ std::unique_ptr< ScfSimpleProgressBar > pProgress( new ScfSimpleProgressBar(
+ aIn.GetSvStreamSize(), GetDocShell(), STR_LOAD_DOC ) );
+
+ /* #i104057# Need to track a base position for progress bar calculation,
+ because sheet substreams may not be in order of sheets. */
+ std::size_t nProgressBasePos = 0;
+ std::size_t nProgressBaseSize = 0;
+
+ for (; eCurrent != Z_End; mnLastRecId = nOpcode)
+ {
+ if( eCurrent == Z_Biff5E )
+ {
+ sal_uInt16 nScTab = GetCurrScTab();
+ if( nScTab < maSheetOffsets.size() )
+ {
+ nProgressBaseSize += (aIn.GetSvStreamPos() - nProgressBasePos);
+ nProgressBasePos = maSheetOffsets[ nScTab ];
+
+ bool bValid = TryStartNextRecord(aIn, nProgressBasePos);
+ if (!bValid)
+ {
+ // Safeguard ourselves from potential infinite loop.
+ eCurrent = Z_End;
+ }
+ }
+ else
+ eCurrent = Z_End;
+ }
+ else
+ aIn.StartNextRecord();
+
+ nOpcode = aIn.GetRecId();
+
+ if( !aIn.IsValid() )
+ {
+ // finalize table if EOF is missing
+ switch( eCurrent )
+ {
+ case Z_Biff2:
+ case Z_Biff3:
+ case Z_Biff4:
+ case Z_Biff4T:
+ case Z_Biff5TPre:
+ case Z_Biff5T:
+ rNumFmtBfr.CreateScFormats();
+ Eof();
+ break;
+ default:;
+ }
+ break;
+ }
+
+ if( eCurrent == Z_End )
+ break;
+
+ if( eCurrent != Z_Biff5TPre && eCurrent != Z_Biff5WPre )
+ pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos );
+
+ switch( eCurrent )
+ {
+
+ case Z_BiffNull: // ------------------------------- Z_BiffNull -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF:
+ {
+ // #i23425# don't rely on the record ID, but on the detected BIFF version
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ Bof2();
+ if( pExcRoot->eDateiTyp == Biff2 )
+ {
+ eCurrent = Z_Biff2;
+ NewTable();
+ }
+ break;
+ case EXC_BIFF3:
+ Bof3();
+ if( pExcRoot->eDateiTyp == Biff3 )
+ {
+ eCurrent = Z_Biff3;
+ NewTable();
+ }
+ break;
+ case EXC_BIFF4:
+ Bof4();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4;
+ NewTable();
+ }
+ else if( pExcRoot->eDateiTyp == Biff4W )
+ eCurrent = Z_Biff4W;
+ break;
+ case EXC_BIFF5:
+ Bof5();
+ if( pExcRoot->eDateiTyp == Biff5W )
+ {
+ eCurrent = Z_Biff5WPre;
+
+ nBdshtTab = 0;
+
+ aIn.StoreGlobalPosition(); // store position
+ }
+ else if( pExcRoot->eDateiTyp == Biff5 )
+ {
+ // #i62752# possible to have BIFF5 sheet without globals
+ NewTable();
+ eCurrent = Z_Biff5TPre; // Shrfmla Prefetch, Row-Prefetch
+ nBofLevel = 0;
+ aIn.StoreGlobalPosition(); // store position
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case Z_Biff2: // ---------------------------------- Z_Biff2 -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x06: Formula25(); break; // FORMULA [ 2 5]
+ case 0x08: Row25(); break; // ROW [ 2 5]
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x18: rNameMgr.ReadName( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x20: Columndefault(); break; // COLUMNDEFAULT[ 2 ]
+ case 0x21: Array25(); break; // ARRAY [ 2 5]
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x24: Colwidth(); break; // COLWIDTH [ 2 ]
+ case 0x25: Defrowheight2(); break; // DEFAULTROWHEI[ 2 ]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID2_FONT: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID_EFONT: rFontBfr.ReadEfont( maStrm ); break;
+ case 0x3E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x43: rXFBfr.ReadXF( maStrm ); break;
+ case 0x44: Ixfe(); break; // IXFE [ 2 ]
+ }
+ }
+ break;
+
+ case Z_Biff3: // ---------------------------------- Z_Biff3 -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x0206: Formula3(); break; // FORMULA [ 3 ]
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0243: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+ }
+ break;
+
+ case Z_Biff4: // ---------------------------------- Z_Biff4 -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_End;
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0406: Formula4(); break; // FORMULA [ 4 ]
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+ }
+ break;
+
+ case Z_Biff4W: // --------------------------------- Z_Biff4W -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ rNameMgr.ConvertAllTokens();
+ eCurrent = Z_End;
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5]
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID4_BOF: // BOF [ 4 ]
+ Bof4();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4T;
+ NewTable();
+ }
+ else
+ eCurrent = Z_End;
+ break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff4T: // --------------------------------- Z_Biff4T -
+ {
+ switch( nOpcode )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case 0x0A: // EOF [ 2345]
+ rNameMgr.ConvertAllTokens();
+ Eof();
+ eCurrent = Z_Biff4E;
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0218: rNameMgr.ReadName( maStrm ); break;
+ case 0x0221: Array34(); break;
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x0231: rFontBfr.ReadFont( maStrm ); break;
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case 0x0406: Formula4(); break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x0443: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff4E: // --------------------------------- Z_Biff4E -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_End;
+ break;
+ case 0x8F: break; // BUNDLEHEADER [ 4 ]
+ case EXC_ID4_BOF: // BOF [ 4 ]
+ Bof4();
+ NewTable();
+ if( pExcRoot->eDateiTyp == Biff4 )
+ {
+ eCurrent = Z_Biff4T;
+ }
+ else
+ {
+ ePrev = eCurrent;
+ eCurrent = Z_Biffn0;
+ }
+ break;
+ }
+
+ }
+ break;
+ case Z_Biff5WPre: // ------------------------------ Z_Biff5WPre -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_Biff5W;
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5]
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x3D: Window1(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x85: Boundsheet(); break; // BOUNDSHEET [ 5]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ // PALETTE follows XFs, but already needed while reading the XFs
+ case 0x92: rPal.ReadPalette( maStrm ); break;
+ }
+ }
+ break;
+ case Z_Biff5W: // --------------------------------- Z_Biff5W -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ rNumFmtBfr.CreateScFormats();
+ rXFBfr.CreateUserStyles();
+ rNameMgr.ConvertAllTokens();
+ eCurrent = Z_Biff5E;
+ break;
+ case 0x18: rNameMgr.ReadName( maStrm ); break;
+ case 0x1E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case 0x22: Rec1904(); break; // 1904 [ 2345]
+ case 0x31: rFontBfr.ReadFont( maStrm ); break;
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8D: Hideobj(); break; // HIDEOBJ [ 345]
+ case 0xDE: Olesize(); break;
+ case 0xE0: rXFBfr.ReadXF( maStrm ); break;
+ case 0x0293: rXFBfr.ReadStyle( maStrm ); break;
+ case 0x041E: rNumFmtBfr.ReadFormat( maStrm ); break;
+ }
+
+ }
+ break;
+
+ case Z_Biff5TPre: // ------------------------------- Z_Biff5Pre -
+ {
+ if (nOpcode == EXC_ID5_BOF)
+ nBofLevel++;
+ else if( (nOpcode == 0x000A) && nBofLevel )
+ nBofLevel--;
+ else if( !nBofLevel ) // don't read chart records
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+ case 0x08: Row25(); break; // ROW [ 2 5]
+ case 0x0A: // EOF [ 2345]
+ eCurrent = Z_Biff5T;
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ case 0x12: SheetProtect(); break; // SHEET PROTECTION
+ case 0x1A:
+ case 0x1B: rPageSett.ReadPageBreaks( maStrm ); break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x21: Array25(); break; // ARRAY [ 2 5]
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x41: rTabViewSett.ReadPane( maStrm ); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345]
+ case 0x55: DefColWidth(); break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345]
+ case 0x81: Wsbool(); break; // WSBOOL [ 2345]
+ case 0x8C: Country(); break; // COUNTRY [ 345]
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45]
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case 0x0221: Array34(); break; // ARRAY [ 34 ]
+ case 0x0223: break; // EXTERNNAME [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345]
+ case 0x023E: rTabViewSett.ReadWindow2( maStrm, false );break;
+ }
+ }
+ }
+ break;
+
+ case Z_Biff5T: // --------------------------------- Z_Biff5T -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA: Formula25(); break;
+ case EXC_ID_SHRFMLA: Shrfmla(); break;
+ case 0x0A: Eof(); eCurrent = Z_Biff5E; break;
+ case 0x14:
+ case 0x15: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case 0x17: Externsheet(); break; // EXTERNSHEET [ 2345]
+ case 0x1C: GetCurrSheetDrawing().ReadNote( maStrm );break;
+ case 0x1D: rTabViewSett.ReadSelection( maStrm ); break;
+ case 0x23: Externname25(); break; // EXTERNNAME [ 2 5]
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29: rPageSett.ReadMargin( maStrm ); break;
+ case 0x2A: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case 0x2B: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case 0x2F: // FILEPASS [ 2345]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = Z_End;
+ break;
+ case 0x5D: GetCurrSheetDrawing().ReadObj( maStrm );break;
+ case 0x83:
+ case 0x84: rPageSett.ReadCenter( maStrm ); break;
+ case 0xA0: rTabViewSett.ReadScl( maStrm ); break;
+ case 0xA1: rPageSett.ReadSetup( maStrm ); break;
+ case 0xBD: Mulrk(); break; // MULRK [ 5]
+ case 0xBE: Mulblank(); break; // MULBLANK [ 5]
+ case 0xD6: Rstring(); break; // RSTRING [ 5]
+ case 0x00E5: Cellmerging(); break; // #i62300#
+ case 0x0236: TableOp(); break; // TABLE [ 5]
+ case EXC_ID5_BOF: // BOF [ 5]
+ XclTools::SkipSubStream( maStrm );
+ break;
+ }
+
+ }
+ break;
+
+ case Z_Biff5E: // --------------------------------- Z_Biff5E -
+ {
+ switch( nOpcode )
+ {
+ case EXC_ID5_BOF: // BOF [ 5]
+ Bof5();
+ NewTable();
+ switch( pExcRoot->eDateiTyp )
+ {
+ case Biff5:
+ case Biff5M4:
+ eCurrent = Z_Biff5TPre; // Shrfmla Prefetch, Row-Prefetch
+ nBofLevel = 0;
+ aIn.StoreGlobalPosition(); // store position
+ break;
+ case Biff5C: // chart sheet
+ GetCurrSheetDrawing().ReadTabChart( maStrm );
+ Eof();
+ GetTracer().TraceChartOnlySheet();
+ break;
+ case Biff5V:
+ default:
+ rD.SetVisible( GetCurrScTab(), false );
+ ePrev = eCurrent;
+ eCurrent = Z_Biffn0;
+ }
+ OSL_ENSURE( pExcRoot->eDateiTyp != Biff5W,
+ "+ImportExcel::Read(): Doppel-Whopper-Workbook!" );
+
+ break;
+ }
+
+ }
+ break;
+ case Z_Biffn0: // --------------------------------- Z_Biffn0 -
+ {
+ switch( nOpcode )
+ {
+ case 0x0A: // EOF [ 2345]
+ eCurrent = ePrev;
+ IncCurrScTab();
+ break;
+ }
+
+ }
+ break;
+
+ case Z_End: // ----------------------------------- Z_End -
+ OSL_FAIL( "*ImportExcel::Read(): Not possible state!" );
+ break;
+ default: OSL_FAIL( "-ImportExcel::Read(): state forgotten!" );
+ }
+ }
+
+ if( eLastErr == ERRCODE_NONE )
+ {
+ pProgress.reset();
+
+ GetDocImport().finalize();
+ if (!utl::ConfigManager::IsFuzzing())
+ AdjustRowHeight();
+ PostDocLoad();
+
+ rD.CalcAfterLoad(false);
+
+ const XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsTabTruncated() )
+ eLastErr = SCWARN_IMPORT_SHEET_OVERFLOW;
+ else if( bTabTruncated || rAddrConv.IsRowTruncated() )
+ eLastErr = SCWARN_IMPORT_ROW_OVERFLOW;
+ else if( rAddrConv.IsColTruncated() )
+ eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW;
+ }
+
+ return eLastErr;
+}
+
+ErrCode ImportExcel8::Read()
+{
+#ifdef EXC_INCL_DUMPER
+ {
+ Biff8RecDumper aDumper( GetRoot(), sal_True );
+ if( aDumper.Dump( aIn ) )
+ return ERRCODE_ABORT;
+ }
+#endif
+ // read the entire BIFF8 stream
+ // don't look too close - this stuff seriously needs to be reworked
+
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+ XclImpPalette& rPal = GetPalette();
+ XclImpFontBuffer& rFontBfr = GetFontBuffer();
+ XclImpNumFmtBuffer& rNumFmtBfr = GetNumFmtBuffer();
+ XclImpXFBuffer& rXFBfr = GetXFBuffer();
+ XclImpSst& rSst = GetSst();
+ XclImpTabInfo& rTabInfo = GetTabInfo();
+ XclImpNameManager& rNameMgr = GetNameManager();
+ XclImpLinkManager& rLinkMgr = GetLinkManager();
+ XclImpObjectManager& rObjMgr = GetObjectManager();
+ // call to GetCurrSheetDrawing() cannot be cached (changes in new sheets)
+ XclImpCondFormatManager& rCondFmtMgr = GetCondFormatManager();
+ XclImpValidationManager& rValidMgr = GetValidationManager();
+ XclImpPivotTableManager& rPTableMgr = GetPivotTableManager();
+ XclImpWebQueryBuffer& rWQBfr = GetWebQueryBuffer();
+
+ bool bInUserView = false; // true = In USERSVIEW(BEGIN|END) record block.
+
+ enum XclImpReadState
+ {
+ EXC_STATE_BEFORE_GLOBALS, /// Before workbook globals (wait for initial BOF).
+ EXC_STATE_GLOBALS_PRE, /// Prefetch for workbook globals.
+ EXC_STATE_GLOBALS, /// Workbook globals.
+ EXC_STATE_BEFORE_SHEET, /// Before worksheet (wait for new worksheet BOF).
+ EXC_STATE_SHEET_PRE, /// Prefetch for worksheet.
+ EXC_STATE_SHEET, /// Worksheet.
+ EXC_STATE_END /// Stop reading.
+ };
+
+ XclImpReadState eCurrent = EXC_STATE_BEFORE_GLOBALS;
+
+ ErrCode eLastErr = ERRCODE_NONE;
+
+ std::unique_ptr< ScfSimpleProgressBar > pProgress( new ScfSimpleProgressBar(
+ aIn.GetSvStreamSize(), GetDocShell(), STR_LOAD_DOC ) );
+
+ /* #i104057# Need to track a base position for progress bar calculation,
+ because sheet substreams may not be in order of sheets. */
+ std::size_t nProgressBasePos = 0;
+ std::size_t nProgressBaseSize = 0;
+
+ bool bSheetHasCodeName = false;
+
+ std::vector<OUString> aCodeNames;
+ std::vector < SCTAB > nTabsWithNoCodeName;
+
+ sal_uInt16 nRecId = 0;
+
+ for (; eCurrent != EXC_STATE_END; mnLastRecId = nRecId)
+ {
+ if( eCurrent == EXC_STATE_BEFORE_SHEET )
+ {
+ sal_uInt16 nScTab = GetCurrScTab();
+ if( nScTab < maSheetOffsets.size() )
+ {
+ nProgressBaseSize += (maStrm.GetSvStreamPos() - nProgressBasePos);
+ nProgressBasePos = maSheetOffsets[ nScTab ];
+
+ bool bValid = TryStartNextRecord(aIn, nProgressBasePos);
+ if (!bValid)
+ {
+ // Safeguard ourselves from potential infinite loop.
+ eCurrent = EXC_STATE_END;
+ }
+
+ // import only 256 sheets
+ if( nScTab > GetScMaxPos().Tab() )
+ {
+ if( maStrm.GetRecId() != EXC_ID_EOF )
+ XclTools::SkipSubStream( maStrm );
+ // #i29930# show warning box
+ GetAddressConverter().CheckScTab( nScTab );
+ eCurrent = EXC_STATE_END;
+ }
+ else
+ {
+ // #i109800# SHEET record may point to any record inside the
+ // sheet substream
+ bool bIsBof = maStrm.GetRecId() == EXC_ID5_BOF;
+ if( bIsBof )
+ Bof5(); // read the BOF record
+ else
+ pExcRoot->eDateiTyp = Biff8; // on missing BOF, assume a standard worksheet
+ NewTable();
+ switch( pExcRoot->eDateiTyp )
+ {
+ case Biff8: // worksheet
+ case Biff8M4: // macro sheet
+ eCurrent = EXC_STATE_SHEET_PRE; // Shrfmla Prefetch, Row-Prefetch
+ // go to next record
+ if( bIsBof ) maStrm.StartNextRecord();
+ maStrm.StoreGlobalPosition();
+ break;
+ case Biff8C: // chart sheet
+ GetCurrSheetDrawing().ReadTabChart( maStrm );
+ Eof();
+ GetTracer().TraceChartOnlySheet();
+ break;
+ case Biff8W: // workbook
+ OSL_FAIL( "ImportExcel8::Read - double workbook globals" );
+ [[fallthrough]];
+ case Biff8V: // VB module
+ default:
+ // TODO: do not create a sheet in the Calc document
+ rD.SetVisible( nScTab, false );
+ XclTools::SkipSubStream( maStrm );
+ IncCurrScTab();
+ }
+ }
+ }
+ else
+ eCurrent = EXC_STATE_END;
+ }
+ else
+ aIn.StartNextRecord();
+
+ if( !aIn.IsValid() )
+ {
+ // #i63591# finalize table if EOF is missing
+ switch( eCurrent )
+ {
+ case EXC_STATE_SHEET_PRE:
+ eCurrent = EXC_STATE_SHEET;
+ aIn.SeekGlobalPosition();
+ continue; // next iteration in while loop
+ case EXC_STATE_SHEET:
+ Eof();
+ eCurrent = EXC_STATE_END;
+ break;
+ default:
+ eCurrent = EXC_STATE_END;
+ }
+ }
+
+ if( eCurrent == EXC_STATE_END )
+ break;
+
+ if( eCurrent != EXC_STATE_SHEET_PRE && eCurrent != EXC_STATE_GLOBALS_PRE )
+ pProgress->ProgressAbs( nProgressBaseSize + aIn.GetSvStreamPos() - nProgressBasePos );
+
+ nRecId = aIn.GetRecId();
+
+ /* #i39464# Ignore records between USERSVIEWBEGIN and USERSVIEWEND
+ completely (user specific view settings). Otherwise view settings
+ and filters are loaded multiple times, which at least causes
+ problems in auto-filters. */
+ switch( nRecId )
+ {
+ case EXC_ID_USERSVIEWBEGIN:
+ OSL_ENSURE( !bInUserView, "ImportExcel8::Read - nested user view settings" );
+ bInUserView = true;
+ break;
+ case EXC_ID_USERSVIEWEND:
+ OSL_ENSURE( bInUserView, "ImportExcel8::Read - not in user view settings" );
+ bInUserView = false;
+ break;
+ }
+
+ if( !bInUserView ) switch( eCurrent )
+ {
+
+ // before workbook globals: wait for initial workbook globals BOF
+ case EXC_STATE_BEFORE_GLOBALS:
+ {
+ if( nRecId == EXC_ID5_BOF )
+ {
+ OSL_ENSURE( GetBiff() == EXC_BIFF8, "ImportExcel8::Read - wrong BIFF version" );
+ Bof5();
+ if( pExcRoot->eDateiTyp == Biff8W )
+ {
+ eCurrent = EXC_STATE_GLOBALS_PRE;
+ maStrm.StoreGlobalPosition();
+ nBdshtTab = 0;
+ }
+ else if( pExcRoot->eDateiTyp == Biff8 )
+ {
+ // #i62752# possible to have BIFF8 sheet without globals
+ NewTable();
+ eCurrent = EXC_STATE_SHEET_PRE; // Shrfmla Prefetch, Row-Prefetch
+ bSheetHasCodeName = false; // reset
+ aIn.StoreGlobalPosition();
+ }
+ }
+ }
+ break;
+
+ // prefetch for workbook globals
+ case EXC_STATE_GLOBALS_PRE:
+ {
+ switch( nRecId )
+ {
+ case EXC_ID_EOF:
+ case EXC_ID_EXTSST:
+ /* #i56376# evil hack: if EOF for globals is missing,
+ simulate it. This hack works only for the bugdoc
+ given in the issue, where the sheet substreams
+ start directly after the EXTSST record. A future
+ implementation should be more robust against
+ missing EOFs. */
+ if( (nRecId == EXC_ID_EOF) ||
+ ((nRecId == EXC_ID_EXTSST) && (maStrm.GetNextRecId() == EXC_ID5_BOF)) )
+ {
+ eCurrent = EXC_STATE_GLOBALS;
+ aIn.SeekGlobalPosition();
+ }
+ break;
+ case 0x12: DocProtect(); break; // PROTECT [ 5678]
+ case 0x13: DocPassword(); break;
+ case 0x19: WinProtection(); break;
+ case 0x2F: // FILEPASS [ 2345 ]
+ eLastErr = XclImpDecryptHelper::ReadFilepass( maStrm );
+ if( eLastErr != ERRCODE_NONE )
+ eCurrent = EXC_STATE_END;
+ break;
+ case EXC_ID_FILESHARING: ReadFileSharing(); break;
+ case 0x3D: Window1(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345 ]
+ case 0x85: Boundsheet(); break; // BOUNDSHEET [ 5 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345 ]
+
+ // PALETTE follows XFs, but already needed while reading the XFs
+ case EXC_ID_PALETTE: rPal.ReadPalette( maStrm ); break;
+ }
+ }
+ break;
+
+ // workbook globals
+ case EXC_STATE_GLOBALS:
+ {
+ switch( nRecId )
+ {
+ case EXC_ID_EOF:
+ case EXC_ID_EXTSST:
+ /* #i56376# evil hack: if EOF for globals is missing,
+ simulate it. This hack works only for the bugdoc
+ given in the issue, where the sheet substreams
+ start directly after the EXTSST record. A future
+ implementation should be more robust against
+ missing EOFs. */
+ if( (nRecId == EXC_ID_EOF) ||
+ ((nRecId == EXC_ID_EXTSST) && (maStrm.GetNextRecId() == EXC_ID5_BOF)) )
+ {
+ rNumFmtBfr.CreateScFormats();
+ rXFBfr.CreateUserStyles();
+ rPTableMgr.ReadPivotCaches( maStrm );
+ rNameMgr.ConvertAllTokens();
+ eCurrent = EXC_STATE_BEFORE_SHEET;
+ }
+ break;
+ case 0x0E: Precision(); break; // PRECISION
+ case 0x22: Rec1904(); break; // 1904 [ 2345 ]
+ case 0x56: break; // BUILTINFMTCNT[ 34 ]
+ case 0x8D: Hideobj(); break; // HIDEOBJ [ 345 ]
+ case 0xD3: SetHasBasic(); break;
+ case 0xDE: Olesize(); break;
+
+ case EXC_ID_CODENAME: ReadCodeName( aIn, true ); break;
+ case EXC_ID_USESELFS: ReadUsesElfs(); break;
+
+ case EXC_ID2_FONT: rFontBfr.ReadFont( maStrm ); break;
+ case EXC_ID4_FORMAT: rNumFmtBfr.ReadFormat( maStrm ); break;
+ case EXC_ID5_XF: rXFBfr.ReadXF( maStrm ); break;
+ case EXC_ID_STYLE: rXFBfr.ReadStyle( maStrm ); break;
+
+ case EXC_ID_SST: rSst.ReadSst( maStrm ); break;
+ case EXC_ID_TABID: rTabInfo.ReadTabid( maStrm ); break;
+ case EXC_ID_NAME: rNameMgr.ReadName( maStrm ); break;
+
+ case EXC_ID_EXTERNSHEET: rLinkMgr.ReadExternsheet( maStrm ); break;
+ case EXC_ID_SUPBOOK: rLinkMgr.ReadSupbook( maStrm ); break;
+ case EXC_ID_XCT: rLinkMgr.ReadXct( maStrm ); break;
+ case EXC_ID_CRN: rLinkMgr.ReadCrn( maStrm ); break;
+ case EXC_ID_EXTERNNAME: rLinkMgr.ReadExternname( maStrm, pFormConv.get() ); break;
+
+ case EXC_ID_MSODRAWINGGROUP:rObjMgr.ReadMsoDrawingGroup( maStrm ); break;
+
+ case EXC_ID_SXIDSTM: rPTableMgr.ReadSxidstm( maStrm ); break;
+ case EXC_ID_SXVS: rPTableMgr.ReadSxvs( maStrm ); break;
+ case EXC_ID_DCONREF: rPTableMgr.ReadDconref( maStrm ); break;
+ case EXC_ID_DCONNAME: rPTableMgr.ReadDConName( maStrm ); break;
+ }
+
+ }
+ break;
+
+ // prefetch for worksheet
+ case EXC_STATE_SHEET_PRE:
+ {
+ switch( nRecId )
+ {
+ // skip chart substream
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( maStrm, false );break;
+ case EXC_ID_SCL: rTabViewSett.ReadScl( maStrm ); break;
+ case EXC_ID_PANE: rTabViewSett.ReadPane( maStrm ); break;
+ case EXC_ID_SELECTION: rTabViewSett.ReadSelection( maStrm ); break;
+
+ case EXC_ID2_DIMENSIONS:
+ case EXC_ID3_DIMENSIONS: ReadDimensions(); break;
+
+ case EXC_ID_CODENAME: ReadCodeName( aIn, false ); bSheetHasCodeName = true; break;
+
+ case 0x0A: // EOF [ 2345 ]
+ {
+ eCurrent = EXC_STATE_SHEET;
+ OUString sName;
+ GetDoc().GetName( GetCurrScTab(), sName );
+ if ( !bSheetHasCodeName )
+ {
+ nTabsWithNoCodeName.push_back( GetCurrScTab() );
+ }
+ else
+ {
+ OUString sCodeName;
+ GetDoc().GetCodeName( GetCurrScTab(), sCodeName );
+ aCodeNames.push_back( sCodeName );
+ }
+
+ bSheetHasCodeName = false; // reset
+
+ aIn.SeekGlobalPosition(); // and back to old position
+ break;
+ }
+ case 0x12: SheetProtect(); break;
+ case 0x13: SheetPassword(); break;
+ case 0x42: Codepage(); break; // CODEPAGE [ 2345 ]
+ case 0x55: DefColWidth(); break;
+ case 0x7D: Colinfo(); break; // COLINFO [ 345 ]
+ case 0x81: Wsbool(); break; // WSBOOL [ 2345 ]
+ case 0x8C: Country(); break; // COUNTRY [ 345 ]
+ case 0x99: Standardwidth(); break; // STANDARDWIDTH[ 45 ]
+ case 0x9B: FilterMode(); break; // FILTERMODE
+ case EXC_ID_AUTOFILTERINFO: AutoFilterInfo(); break;// AUTOFILTERINFO
+ case EXC_ID_AUTOFILTER: AutoFilter(); break; // AUTOFILTER
+ case 0x0208: Row34(); break; // ROW [ 34 ]
+ case EXC_ID2_ARRAY:
+ case EXC_ID3_ARRAY: Array34(); break; // ARRAY [ 34 ]
+ case 0x0225: Defrowheight345();break;//DEFAULTROWHEI[ 345 ]
+ case 0x0867: FeatHdr(); break; // FEATHDR
+ case 0x0868: Feat(); break; // FEAT
+ }
+ }
+ break;
+
+ // worksheet
+ case EXC_STATE_SHEET:
+ {
+ switch( nRecId )
+ {
+ // skip unknown substreams
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( maStrm ); break;
+
+ case EXC_ID_EOF: Eof(); eCurrent = EXC_STATE_BEFORE_SHEET; break;
+
+ case EXC_ID2_BLANK:
+ case EXC_ID3_BLANK: ReadBlank(); break;
+ case EXC_ID2_INTEGER: ReadInteger(); break;
+ case EXC_ID2_NUMBER:
+ case EXC_ID3_NUMBER: ReadNumber(); break;
+ case EXC_ID2_LABEL:
+ case EXC_ID3_LABEL: ReadLabel(); break;
+ case EXC_ID2_BOOLERR:
+ case EXC_ID3_BOOLERR: ReadBoolErr(); break;
+ case EXC_ID_RK: ReadRk(); break;
+
+ case EXC_ID2_FORMULA:
+ case EXC_ID3_FORMULA:
+ case EXC_ID4_FORMULA: Formula25(); break;
+ case EXC_ID_SHRFMLA: Shrfmla(); break;
+ case 0x000C: Calccount(); break; // CALCCOUNT
+ case 0x0010: Delta(); break; // DELTA
+ case 0x0011: Iteration(); break; // ITERATION
+ case 0x007E:
+ case 0x00AE: Scenman(); break; // SCENMAN
+ case 0x00AF: Scenario(); break; // SCENARIO
+ case 0x00BD: Mulrk(); break; // MULRK [ 5 ]
+ case 0x00BE: Mulblank(); break; // MULBLANK [ 5 ]
+ case 0x00D6: Rstring(); break; // RSTRING [ 5 ]
+ case 0x00E5: Cellmerging(); break; // CELLMERGING
+ case 0x00FD: Labelsst(); break; // LABELSST [ 8 ]
+ case 0x0236: TableOp(); break; // TABLE
+
+ case EXC_ID_HORPAGEBREAKS:
+ case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( maStrm ); break;
+ case EXC_ID_HEADER:
+ case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( maStrm ); break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( maStrm ); break;
+ case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( maStrm ); break;
+ case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( maStrm ); break;
+ case EXC_ID_HCENTER:
+ case EXC_ID_VCENTER: rPageSett.ReadCenter( maStrm ); break;
+ case EXC_ID_SETUP: rPageSett.ReadSetup( maStrm ); break;
+ case EXC_ID8_IMGDATA: rPageSett.ReadImgData( maStrm ); break;
+
+ case EXC_ID_MSODRAWING: GetCurrSheetDrawing().ReadMsoDrawing( maStrm ); break;
+ // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
+ case EXC_ID_OBJ: GetCurrSheetDrawing().ReadObj( maStrm ); break;
+ case EXC_ID_NOTE: GetCurrSheetDrawing().ReadNote( maStrm ); break;
+
+ case EXC_ID_HLINK: XclImpHyperlink::ReadHlink( maStrm ); break;
+ case EXC_ID_LABELRANGES: XclImpLabelranges::ReadLabelranges( maStrm ); break;
+
+ case EXC_ID_CONDFMT: rCondFmtMgr.ReadCondfmt( maStrm ); break;
+ case EXC_ID_CF: rCondFmtMgr.ReadCF( maStrm ); break;
+
+ case EXC_ID_DVAL: XclImpValidationManager::ReadDval( maStrm ); break;
+ case EXC_ID_DV: rValidMgr.ReadDV( maStrm ); break;
+
+ case EXC_ID_QSI: rWQBfr.ReadQsi( maStrm ); break;
+ case EXC_ID_WQSTRING: rWQBfr.ReadWqstring( maStrm ); break;
+ case EXC_ID_PQRY: rWQBfr.ReadParamqry( maStrm ); break;
+ case EXC_ID_WQSETT: rWQBfr.ReadWqsettings( maStrm ); break;
+ case EXC_ID_WQTABLES: rWQBfr.ReadWqtables( maStrm ); break;
+
+ case EXC_ID_SXVIEW: rPTableMgr.ReadSxview( maStrm ); break;
+ case EXC_ID_SXVD: rPTableMgr.ReadSxvd( maStrm ); break;
+ case EXC_ID_SXVI: rPTableMgr.ReadSxvi( maStrm ); break;
+ case EXC_ID_SXIVD: rPTableMgr.ReadSxivd( maStrm ); break;
+ case EXC_ID_SXPI: rPTableMgr.ReadSxpi( maStrm ); break;
+ case EXC_ID_SXDI: rPTableMgr.ReadSxdi( maStrm ); break;
+ case EXC_ID_SXVDEX: rPTableMgr.ReadSxvdex( maStrm ); break;
+ case EXC_ID_SXEX: rPTableMgr.ReadSxex( maStrm ); break;
+ case EXC_ID_SHEETEXT: rTabViewSett.ReadTabBgColor( maStrm, rPal ); break;
+ case EXC_ID_SXVIEWEX9: rPTableMgr.ReadSxViewEx9( maStrm ); break;
+ case EXC_ID_SXADDL: rPTableMgr.ReadSxAddl( maStrm ); break;
+ }
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ if( eLastErr == ERRCODE_NONE )
+ {
+ // In some strange circumstances the codename might be missing
+ // # Create any missing Sheet CodeNames
+ for ( const auto& rTab : nTabsWithNoCodeName )
+ {
+ SCTAB nTab = 1;
+ while ( true )
+ {
+ OUStringBuffer aBuf;
+ aBuf.append("Sheet");
+ aBuf.append(static_cast<sal_Int32>(nTab++));
+ OUString sTmpName = aBuf.makeStringAndClear();
+
+ if ( std::find(aCodeNames.begin(), aCodeNames.end(), sTmpName) == aCodeNames.end() ) // generated codename not found
+ {
+ // Set new codename
+ GetDoc().SetCodeName( rTab, sTmpName );
+ // Record newly used codename
+ aCodeNames.push_back(sTmpName);
+ break;
+ }
+ }
+ }
+ // #i45843# Convert pivot tables before calculation, so they are available
+ // for the GETPIVOTDATA function.
+ if( GetBiff() == EXC_BIFF8 )
+ GetPivotTableManager().ConvertPivotTables();
+
+ ScDocumentImport& rDoc = GetDocImport();
+ rDoc.finalize();
+ pProgress.reset();
+#if 0
+ // Excel documents look much better without this call; better in the
+ // sense that the row heights are identical to the original heights in
+ // Excel.
+ if ( !rD.IsAdjustHeightLocked())
+ AdjustRowHeight();
+#endif
+ PostDocLoad();
+
+ rD.CalcAfterLoad(false);
+
+ // import change tracking data
+ XclImpChangeTrack aImpChTr( GetRoot(), maStrm );
+ aImpChTr.Apply();
+
+ const XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ if( rAddrConv.IsTabTruncated() )
+ eLastErr = SCWARN_IMPORT_SHEET_OVERFLOW;
+ else if( bTabTruncated || rAddrConv.IsRowTruncated() )
+ eLastErr = SCWARN_IMPORT_ROW_OVERFLOW;
+ else if( rAddrConv.IsColTruncated() )
+ eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW;
+
+ if( GetBiff() == EXC_BIFF8 )
+ GetPivotTableManager().MaybeRefreshPivotTables();
+ }
+
+ return eLastErr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/tokstack.cxx b/sc/source/filter/excel/tokstack.cxx
new file mode 100644
index 000000000..a0e3974ca
--- /dev/null
+++ b/sc/source/filter/excel/tokstack.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tokstack.hxx>
+#include <scmatrix.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <string.h>
+
+const sal_uInt16 TokenPool::nScTokenOff = 8192;
+
+TokenStack::TokenStack( )
+ : pStack( new TokenId[ nSize ] )
+{
+ Reset();
+}
+
+TokenStack::~TokenStack()
+{
+}
+
+// !ATTENTION!": to the outside the numbering starts with 1!
+// !ATTENTION!": SC-Token are stored with an offset nScTokenOff
+// -> to distinguish from other tokens
+
+TokenPool::TokenPool( svl::SharedStringPool& rSPool ) :
+ mrStringPool(rSPool)
+{
+ // pool for Id-sequences
+ nP_Id = 256;
+ pP_Id.reset( new sal_uInt16[ nP_Id ] );
+
+ // pool for Ids
+ nElement = 32;
+ pElement.reset( new sal_uInt16[ nElement ] );
+ pType.reset( new E_TYPE[ nElement ] );
+ pSize.reset( new sal_uInt16[ nElement ] );
+ nP_IdLast = 0;
+
+ nP_Matrix = 16;
+ ppP_Matrix.reset( new ScMatrix*[ nP_Matrix ] );
+ memset( ppP_Matrix.get(), 0, sizeof( ScMatrix* ) * nP_Matrix );
+
+ Reset();
+}
+
+TokenPool::~TokenPool()
+{
+ ClearMatrix();
+}
+
+/** Returns the new number of elements, or 0 if overflow. */
+static sal_uInt16 lcl_canGrow( sal_uInt16 nOld )
+{
+ if (!nOld)
+ return 1;
+ if (nOld == SAL_MAX_UINT16)
+ return 0;
+ sal_uInt32 nNew = ::std::max( static_cast<sal_uInt32>(nOld) * 2,
+ static_cast<sal_uInt32>(nOld) + 1);
+ if (nNew > SAL_MAX_UINT16)
+ nNew = SAL_MAX_UINT16;
+ if (nNew - 1 < nOld)
+ nNew = 0;
+ return static_cast<sal_uInt16>(nNew);
+}
+
+bool TokenPool::GrowId()
+{
+ sal_uInt16 nP_IdNew = lcl_canGrow( nP_Id);
+ if (!nP_IdNew)
+ return false;
+
+ sal_uInt16* pP_IdNew = new (::std::nothrow) sal_uInt16[ nP_IdNew ];
+ if (!pP_IdNew)
+ return false;
+
+ for( sal_uInt16 nL = 0 ; nL < nP_Id ; nL++ )
+ pP_IdNew[ nL ] = pP_Id[ nL ];
+
+ nP_Id = nP_IdNew;
+
+ pP_Id.reset( pP_IdNew );
+ return true;
+}
+
+bool TokenPool::CheckElementOrGrow()
+{
+ // Last possible ID to be assigned somewhere is nElementCurrent+1
+ if (nElementCurrent + 1 == nScTokenOff - 1)
+ {
+ SAL_WARN("sc.filter","TokenPool::CheckElementOrGrow - last possible ID " << nElementCurrent+1);
+ return false;
+ }
+
+ if (nElementCurrent >= nElement)
+ return GrowElement();
+
+ return true;
+}
+
+bool TokenPool::GrowElement()
+{
+ sal_uInt16 nElementNew = lcl_canGrow( nElement);
+ if (!nElementNew)
+ return false;
+
+ std::unique_ptr<sal_uInt16[]> pElementNew(new (::std::nothrow) sal_uInt16[ nElementNew ]);
+ std::unique_ptr<E_TYPE[]> pTypeNew(new (::std::nothrow) E_TYPE[ nElementNew ]);
+ std::unique_ptr<sal_uInt16[]> pSizeNew(new (::std::nothrow) sal_uInt16[ nElementNew ]);
+ if (!pElementNew || !pTypeNew || !pSizeNew)
+ {
+ return false;
+ }
+
+ for( sal_uInt16 nL = 0 ; nL < nElement ; nL++ )
+ {
+ pElementNew[ nL ] = pElement[ nL ];
+ pTypeNew[ nL ] = pType[ nL ];
+ pSizeNew[ nL ] = pSize[ nL ];
+ }
+
+ nElement = nElementNew;
+
+ pElement = std::move( pElementNew );
+ pType = std::move( pTypeNew );
+ pSize = std::move( pSizeNew );
+ return true;
+}
+
+bool TokenPool::GrowMatrix()
+{
+ sal_uInt16 nNewSize = lcl_canGrow( nP_Matrix);
+ if (!nNewSize)
+ return false;
+
+ ScMatrix** ppNew = new (::std::nothrow) ScMatrix*[ nNewSize ];
+ if (!ppNew)
+ return false;
+
+ memset( ppNew, 0, sizeof( ScMatrix* ) * nNewSize );
+ for( sal_uInt16 nL = 0 ; nL < nP_Matrix ; nL++ )
+ ppNew[ nL ] = ppP_Matrix[ nL ];
+
+ ppP_Matrix.reset( ppNew );
+ nP_Matrix = nNewSize;
+ return true;
+}
+
+bool TokenPool::GetElement( const sal_uInt16 nId, ScTokenArray* pScToken )
+{
+ if (nId >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter","TokenPool::GetElement - Id too large, " << nId << " >= " << nElementCurrent);
+ return false;
+ }
+
+ bool bRet = true;
+ if( pType[ nId ] == T_Id )
+ bRet = GetElementRek( nId, pScToken );
+ else
+ {
+ switch( pType[ nId ] )
+ {
+ case T_Str:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto* p = ppP_Str.getIfInRange( n );
+ if (p)
+ pScToken->AddString(mrStringPool.intern(**p));
+ else
+ bRet = false;
+ }
+ break;
+ case T_D:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < pP_Dbl.m_writemark)
+ pScToken->AddDouble( pP_Dbl[ n ] );
+ else
+ bRet = false;
+ }
+ break;
+ case T_Err:
+ break;
+/* TODO: in case we had FormulaTokenArray::AddError() */
+#if 0
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < nP_Err)
+ pScToken->AddError( pP_Err[ n ] );
+ else
+ bRet = false;
+ }
+#endif
+ case T_RefC:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_RefTr.getIfInRange(n);
+ if (p)
+ pScToken->AddSingleReference( **p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_RefA:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ if (n < ppP_RefTr.m_writemark && ppP_RefTr[ n ] && n+1 < ppP_RefTr.m_writemark && ppP_RefTr[ n + 1 ])
+ {
+ ScComplexRefData aScComplexRefData;
+ aScComplexRefData.Ref1 = *ppP_RefTr[ n ];
+ aScComplexRefData.Ref2 = *ppP_RefTr[ n + 1 ];
+ pScToken->AddDoubleReference( aScComplexRefData );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_RN:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maRangeNames.size())
+ {
+ const RangeName& r = maRangeNames[n];
+ pScToken->AddRangeName(r.mnIndex, r.mnSheet);
+ }
+ }
+ break;
+ case T_Ext:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_Ext.getIfInRange(n);
+
+ if( p )
+ {
+ if( (*p)->eId == ocEuroConvert )
+ pScToken->AddOpCode( (*p)->eId );
+ else
+ pScToken->AddExternal( (*p)->aText, (*p)->eId );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_Nlf:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ auto p = ppP_Nlf.getIfInRange(n);
+
+ if( p )
+ pScToken->AddColRowName( **p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_Matrix:
+ {
+ sal_uInt16 n = pElement[ nId ];
+ ScMatrix* p = ( n < nP_Matrix )? ppP_Matrix[ n ] : nullptr;
+
+ if( p )
+ pScToken->AddMatrix( p );
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtName:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtNames.size())
+ {
+ const ExtName& r = maExtNames[n];
+ pScToken->AddExternalName(r.mnFileId, mrStringPool.intern( r.maName));
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtRefC:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtCellRefs.size())
+ {
+ const ExtCellRef& r = maExtCellRefs[n];
+ pScToken->AddExternalSingleReference(r.mnFileId, mrStringPool.intern( r.maTabName), r.maRef);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case T_ExtRefA:
+ {
+ sal_uInt16 n = pElement[nId];
+ if (n < maExtAreaRefs.size())
+ {
+ const ExtAreaRef& r = maExtAreaRefs[n];
+ pScToken->AddExternalDoubleReference(r.mnFileId, mrStringPool.intern( r.maTabName), r.maRef);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ default:
+ OSL_FAIL("-TokenPool::GetElement(): undefined state!?");
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+bool TokenPool::GetElementRek( const sal_uInt16 nId, ScTokenArray* pScToken )
+{
+#ifdef DBG_UTIL
+ m_nRek++;
+ OSL_ENSURE(m_nRek <= nP_Id, "*TokenPool::GetElement(): recursion loops!?");
+#endif
+
+ OSL_ENSURE( nId < nElementCurrent, "*TokenPool::GetElementRek(): nId >= nElementCurrent" );
+
+ if (nId >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter", "*TokenPool::GetElementRek(): nId >= nElementCurrent");
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return false;
+ }
+
+ if (pType[ nId ] != T_Id)
+ {
+ SAL_WARN("sc.filter", "-TokenPool::GetElementRek(): pType[ nId ] != T_Id");
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return false;
+ }
+
+ bool bRet = true;
+ sal_uInt16 nCnt = pSize[ nId ];
+ sal_uInt16 nFirstId = pElement[ nId ];
+ if (nFirstId >= nP_Id)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: nFirstId >= nP_Id");
+ nCnt = 0;
+ bRet = false;
+ }
+ sal_uInt16* pCurrent = nCnt ? &pP_Id[ nFirstId ] : nullptr;
+ if (nCnt > nP_Id - nFirstId)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: nCnt > nP_Id - nFirstId");
+ nCnt = nP_Id - nFirstId;
+ bRet = false;
+ }
+ for( ; nCnt > 0 ; nCnt--, pCurrent++ )
+ {
+ assert(pCurrent);
+ if( *pCurrent < nScTokenOff )
+ {// recursion or not?
+ if (*pCurrent >= nElementCurrent)
+ {
+ SAL_WARN("sc.filter", "TokenPool::GetElementRek: *pCurrent >= nElementCurrent");
+ bRet = false;
+ }
+ else
+ {
+ if (pType[ *pCurrent ] == T_Id)
+ bRet = GetElementRek( *pCurrent, pScToken );
+ else
+ bRet = GetElement( *pCurrent, pScToken );
+ }
+ }
+ else // elementary SC_Token
+ pScToken->AddOpCode( static_cast<DefTokenId>( *pCurrent - nScTokenOff ) );
+ }
+
+#ifdef DBG_UTIL
+ m_nRek--;
+#endif
+ return bRet;
+}
+
+void TokenPool::operator >>( TokenId& rId )
+{
+ rId = static_cast<TokenId>( nElementCurrent + 1 );
+
+ if (!CheckElementOrGrow())
+ return;
+
+ pElement[ nElementCurrent ] = nP_IdLast; // Start of Token-sequence
+ pType[ nElementCurrent ] = T_Id; // set Typeinfo
+ pSize[ nElementCurrent ] = nP_IdCurrent - nP_IdLast;
+ // write from nP_IdLast to nP_IdCurrent-1 -> length of the sequence
+
+ nElementCurrent++; // start value for next sequence
+ nP_IdLast = nP_IdCurrent;
+}
+
+TokenId TokenPool::Store( const double& rDouble )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( pP_Dbl.m_writemark >= pP_Dbl.m_capacity )
+ if (!pP_Dbl.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = pP_Dbl.m_writemark; // Index in Double-Array
+ pType[ nElementCurrent ] = T_D; // set Typeinfo Double
+
+ pP_Dbl[ pP_Dbl.m_writemark ] = rDouble;
+
+ pSize[ nElementCurrent ] = 1; // does not matter
+
+ nElementCurrent++;
+ pP_Dbl.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const sal_uInt16 nIndex )
+{
+ return StoreName(nIndex, -1);
+}
+
+TokenId TokenPool::Store( const OUString& rString )
+{
+ // mostly copied to Store( const char* ), to avoid a temporary string
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Str.m_writemark >= ppP_Str.m_capacity )
+ if (!ppP_Str.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Str.m_writemark; // Index in String-Array
+ pType[ nElementCurrent ] = T_Str; // set Typeinfo String
+
+ // create String
+ if( !ppP_Str[ ppP_Str.m_writemark ] )
+ //...but only, if it does not exist already
+ ppP_Str[ ppP_Str.m_writemark ].reset( new OUString( rString ) );
+ else
+ //...copy otherwise
+ *ppP_Str[ ppP_Str.m_writemark ] = rString;
+
+ /* attention truncate to 16 bits */
+ pSize[ nElementCurrent ] = static_cast<sal_uInt16>(ppP_Str[ ppP_Str.m_writemark ]->getLength());
+
+ nElementCurrent++;
+ ppP_Str.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const ScSingleRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_RefTr.m_writemark >= ppP_RefTr.m_capacity )
+ if (!ppP_RefTr.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_RefTr.m_writemark;
+ pType[ nElementCurrent ] = T_RefC; // set Typeinfo Cell-Ref
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr;
+
+ nElementCurrent++;
+ ppP_RefTr.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const ScComplexRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_RefTr.m_writemark + 1 >= ppP_RefTr.m_capacity )
+ if (!ppP_RefTr.Grow(2))
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_RefTr.m_writemark;
+ pType[ nElementCurrent ] = T_RefA; // setTypeinfo Area-Ref
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr.Ref1 ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr.Ref1;
+ ppP_RefTr.m_writemark++;
+
+ if( !ppP_RefTr[ ppP_RefTr.m_writemark ] )
+ ppP_RefTr[ ppP_RefTr.m_writemark ].reset( new ScSingleRefData( rTr.Ref2 ) );
+ else
+ *ppP_RefTr[ ppP_RefTr.m_writemark ] = rTr.Ref2;
+ ppP_RefTr.m_writemark++;
+
+ nElementCurrent++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::Store( const DefTokenId e, const OUString& r )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Ext.m_writemark >= ppP_Ext.m_capacity )
+ if (!ppP_Ext.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Ext.m_writemark;
+ pType[ nElementCurrent ] = T_Ext; // set Typeinfo String
+
+ if( ppP_Ext[ ppP_Ext.m_writemark ] )
+ {
+ ppP_Ext[ ppP_Ext.m_writemark ]->eId = e;
+ ppP_Ext[ ppP_Ext.m_writemark ]->aText = r;
+ }
+ else
+ ppP_Ext[ ppP_Ext.m_writemark ].reset( new EXTCONT( e, r ) );
+
+ nElementCurrent++;
+ ppP_Ext.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent); // return old value + 1!
+}
+
+TokenId TokenPool::StoreNlf( const ScSingleRefData& rTr )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( ppP_Nlf.m_writemark >= ppP_Nlf.m_capacity )
+ if (!ppP_Nlf.Grow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = ppP_Nlf.m_writemark;
+ pType[ nElementCurrent ] = T_Nlf;
+
+ if( ppP_Nlf[ ppP_Nlf.m_writemark ] )
+ {
+ *ppP_Nlf[ ppP_Nlf.m_writemark ] = rTr;
+ }
+ else
+ ppP_Nlf[ ppP_Nlf.m_writemark ].reset( new ScSingleRefData( rTr ) );
+
+ nElementCurrent++;
+ ppP_Nlf.m_writemark++;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreMatrix()
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ if( nP_MatrixCurrent >= nP_Matrix )
+ if (!GrowMatrix())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[ nElementCurrent ] = nP_MatrixCurrent;
+ pType[ nElementCurrent ] = T_Matrix;
+
+ ScMatrix* pM = new ScMatrix( 0, 0 );
+ pM->IncRef( );
+ ppP_Matrix[ nP_MatrixCurrent ] = pM;
+
+ nElementCurrent++;
+ nP_MatrixCurrent++;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreName( sal_uInt16 nIndex, sal_Int16 nSheet )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maRangeNames.size());
+ pType[nElementCurrent] = T_RN;
+
+ maRangeNames.emplace_back();
+ RangeName& r = maRangeNames.back();
+ r.mnIndex = nIndex;
+ r.mnSheet = nSheet;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtName( sal_uInt16 nFileId, const OUString& rName )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtNames.size());
+ pType[nElementCurrent] = T_ExtName;
+
+ maExtNames.emplace_back();
+ ExtName& r = maExtNames.back();
+ r.mnFileId = nFileId;
+ r.maName = rName;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtCellRefs.size());
+ pType[nElementCurrent] = T_ExtRefC;
+
+ maExtCellRefs.emplace_back();
+ ExtCellRef& r = maExtCellRefs.back();
+ r.mnFileId = nFileId;
+ r.maTabName = rTabName;
+ r.maRef = rRef;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+TokenId TokenPool::StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef )
+{
+ if (!CheckElementOrGrow())
+ return static_cast<const TokenId>(nElementCurrent+1);
+
+ pElement[nElementCurrent] = static_cast<sal_uInt16>(maExtAreaRefs.size());
+ pType[nElementCurrent] = T_ExtRefA;
+
+ maExtAreaRefs.emplace_back();
+ ExtAreaRef& r = maExtAreaRefs.back();
+ r.mnFileId = nFileId;
+ r.maTabName = rTabName;
+ r.maRef = rRef;
+
+ ++nElementCurrent;
+
+ return static_cast<const TokenId>(nElementCurrent);
+}
+
+void TokenPool::Reset()
+{
+ nP_IdCurrent = nP_IdLast = nElementCurrent
+ = ppP_Str.m_writemark = pP_Dbl.m_writemark = pP_Err.m_writemark
+ = ppP_RefTr.m_writemark = ppP_Ext.m_writemark = ppP_Nlf.m_writemark = nP_MatrixCurrent = 0;
+ maRangeNames.clear();
+ maExtNames.clear();
+ maExtCellRefs.clear();
+ maExtAreaRefs.clear();
+ ClearMatrix();
+}
+
+bool TokenPool::IsSingleOp( const TokenId& rId, const DefTokenId eId ) const
+{
+ sal_uInt16 nId = static_cast<sal_uInt16>(rId);
+ if( nId && nId <= nElementCurrent )
+ {// existent?
+ nId--;
+ if( T_Id == pType[ nId ] )
+ {// Token-Sequence?
+ if( pSize[ nId ] == 1 )
+ {// EXACTLY 1 Token
+ sal_uInt16 nPid = pElement[ nId ];
+ if (nPid < nP_Id)
+ {
+ sal_uInt16 nSecId = pP_Id[ nPid ];
+ if( nSecId >= nScTokenOff )
+ {// Default-Token?
+ return static_cast<DefTokenId>( nSecId - nScTokenOff ) == eId; // wanted?
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+const OUString* TokenPool::GetExternal( const TokenId& rId ) const
+{
+ const OUString* p = nullptr;
+ sal_uInt16 n = static_cast<sal_uInt16>(rId);
+ if( n && n <= nElementCurrent )
+ {
+ n--;
+ if( pType[ n ] == T_Ext )
+ {
+ sal_uInt16 nExt = pElement[ n ];
+ if ( nExt < ppP_Ext.m_writemark && ppP_Ext[ nExt ] )
+ p = &ppP_Ext[ nExt ]->aText;
+ }
+ }
+
+ return p;
+}
+
+ScMatrix* TokenPool::GetMatrix( unsigned int n ) const
+{
+ if( n < nP_MatrixCurrent )
+ return ppP_Matrix[ n ];
+ else
+ SAL_WARN("sc.filter", "GetMatrix: " << n << " >= " << nP_MatrixCurrent);
+ return nullptr;
+}
+
+void TokenPool::ClearMatrix()
+{
+ for(sal_uInt16 n = 0 ; n < nP_Matrix ; n++ )
+ {
+ if( ppP_Matrix[ n ] )
+ {
+ ppP_Matrix[ n ]->DecRef( );
+ ppP_Matrix[n] = nullptr;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xechart.cxx b/sc/source/filter/excel/xechart.cxx
new file mode 100644
index 000000000..64525457f
--- /dev/null
+++ b/sc/source/filter/excel/xechart.cxx
@@ -0,0 +1,3475 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xechart.hxx>
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart/ChartLegendExpansion.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart/TimeInterval.hpp>
+#include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart/XAxisSupplier.hpp>
+#include <com/sun/star/chart/XDiagramPositioning.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/XDiagram.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
+#include <com/sun/star/chart2/XTitled.hpp>
+#include <com/sun/star/chart2/XColorScheme.hpp>
+#include <com/sun/star/chart2/data/XDataSource.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/CurveStyle.hpp>
+#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/LegendPosition.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/TickmarkStyle.hpp>
+
+#include <tools/gen.hxx>
+#include <filter/msfilter/escherex.hxx>
+
+#include <document.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+#include <xeescher.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xepage.hxx>
+#include <xestyle.hxx>
+#include <xltools.hxx>
+
+#include <memory>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::i18n::XBreakIterator;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XShapes;
+
+using ::com::sun::star::chart2::IncrementData;
+using ::com::sun::star::chart2::RelativePosition;
+using ::com::sun::star::chart2::RelativeSize;
+using ::com::sun::star::chart2::ScaleData;
+using ::com::sun::star::chart2::SubIncrement;
+using ::com::sun::star::chart2::XAxis;
+using ::com::sun::star::chart2::XChartDocument;
+using ::com::sun::star::chart2::XChartTypeContainer;
+using ::com::sun::star::chart2::XColorScheme;
+using ::com::sun::star::chart2::XCoordinateSystem;
+using ::com::sun::star::chart2::XCoordinateSystemContainer;
+using ::com::sun::star::chart2::XChartType;
+using ::com::sun::star::chart2::XDataSeries;
+using ::com::sun::star::chart2::XDataSeriesContainer;
+using ::com::sun::star::chart2::XDiagram;
+using ::com::sun::star::chart2::XFormattedString;
+using ::com::sun::star::chart2::XLegend;
+using ::com::sun::star::chart2::XRegressionCurve;
+using ::com::sun::star::chart2::XRegressionCurveContainer;
+using ::com::sun::star::chart2::XTitle;
+using ::com::sun::star::chart2::XTitled;
+
+using ::com::sun::star::chart2::data::XDataSequence;
+using ::com::sun::star::chart2::data::XDataSource;
+using ::com::sun::star::chart2::data::XLabeledDataSequence;
+
+using ::formula::FormulaToken;
+using ::formula::FormulaTokenArrayPlainIterator;
+
+namespace cssc = ::com::sun::star::chart;
+namespace cssc2 = ::com::sun::star::chart2;
+
+// Helpers ====================================================================
+
+namespace {
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclChRectangle& rRect )
+{
+ return rStrm << rRect.mnX << rRect.mnY << rRect.mnWidth << rRect.mnHeight;
+}
+
+void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef const & xRec )
+{
+ if( xRec )
+ xRec->Save( rStrm );
+}
+
+/** Saves the passed record (group) together with a leading value record. */
+template< typename Type >
+void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef const & xRec, sal_uInt16 nRecId, Type nValue )
+{
+ if( xRec )
+ {
+ XclExpValueRecord< Type >( nRecId, nValue ).Save( rStrm );
+ xRec->Save( rStrm );
+ }
+}
+
+template<typename ValueType, typename KeyType>
+void lclSaveRecord(XclExpStream& rStrm, ValueType* pRec, sal_uInt16 nRecId, KeyType nValue)
+{
+ if (pRec)
+ {
+ XclExpValueRecord<KeyType>(nRecId, nValue).Save(rStrm);
+ pRec->Save(rStrm);
+ }
+}
+
+void lclWriteChFrBlockRecord( XclExpStream& rStrm, const XclChFrBlock& rFrBlock, bool bBegin )
+{
+ sal_uInt16 nRecId = bBegin ? EXC_ID_CHFRBLOCKBEGIN : EXC_ID_CHFRBLOCKEND;
+ rStrm.StartRecord( nRecId, 12 );
+ rStrm << nRecId << EXC_FUTUREREC_EMPTYFLAGS << rFrBlock.mnType << rFrBlock.mnContext << rFrBlock.mnValue1 << rFrBlock.mnValue2;
+ rStrm.EndRecord();
+}
+
+template< typename Type >
+bool lclIsAutoAnyOrGetValue( Type& rValue, const Any& rAny )
+{
+ return !rAny.hasValue() || !(rAny >>= rValue);
+}
+
+bool lclIsAutoAnyOrGetScaledValue( double& rfValue, const Any& rAny, bool bLogScale )
+{
+ bool bIsAuto = lclIsAutoAnyOrGetValue( rfValue, rAny );
+ if( !bIsAuto && bLogScale )
+ rfValue = log( rfValue ) / log( 10.0 );
+ return bIsAuto;
+}
+
+sal_uInt16 lclGetTimeValue( const XclExpRoot& rRoot, double fSerialDate, sal_uInt16 nTimeUnit )
+{
+ DateTime aDateTime = rRoot.GetDateTimeFromDouble( fSerialDate );
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS:
+ return ::limit_cast< sal_uInt16, double >( fSerialDate, 0, SAL_MAX_UINT16 );
+ case EXC_CHDATERANGE_MONTHS:
+ return ::limit_cast< sal_uInt16, sal_uInt16 >( 12 * (aDateTime.GetYear() - rRoot.GetBaseYear()) + aDateTime.GetMonth() - 1, 0, SAL_MAX_INT16 );
+ case EXC_CHDATERANGE_YEARS:
+ return ::limit_cast< sal_uInt16, sal_uInt16 >( aDateTime.GetYear() - rRoot.GetBaseYear(), 0, SAL_MAX_INT16 );
+ default:
+ OSL_ENSURE( false, "lclGetTimeValue - unexpected time unit" );
+ }
+ return ::limit_cast< sal_uInt16, double >( fSerialDate, 0, SAL_MAX_UINT16 );
+}
+
+bool lclConvertTimeValue( const XclExpRoot& rRoot, sal_uInt16& rnValue, const Any& rAny, sal_uInt16 nTimeUnit )
+{
+ double fSerialDate = 0;
+ bool bAuto = lclIsAutoAnyOrGetValue( fSerialDate, rAny );
+ if( !bAuto )
+ rnValue = lclGetTimeValue( rRoot, fSerialDate, nTimeUnit );
+ return bAuto;
+}
+
+sal_uInt16 lclGetTimeUnit( sal_Int32 nApiTimeUnit )
+{
+ switch( nApiTimeUnit )
+ {
+ case cssc::TimeUnit::DAY: return EXC_CHDATERANGE_DAYS;
+ case cssc::TimeUnit::MONTH: return EXC_CHDATERANGE_MONTHS;
+ case cssc::TimeUnit::YEAR: return EXC_CHDATERANGE_YEARS;
+ default: OSL_ENSURE( false, "lclGetTimeUnit - unexpected time unit" );
+ }
+ return EXC_CHDATERANGE_DAYS;
+}
+
+bool lclConvertTimeInterval( sal_uInt16& rnValue, sal_uInt16& rnTimeUnit, const Any& rAny )
+{
+ cssc::TimeInterval aInterval;
+ bool bAuto = lclIsAutoAnyOrGetValue( aInterval, rAny );
+ if( !bAuto )
+ {
+ rnValue = ::limit_cast< sal_uInt16, sal_Int32 >( aInterval.Number, 1, SAL_MAX_UINT16 );
+ rnTimeUnit = lclGetTimeUnit( aInterval.TimeUnit );
+ }
+ return bAuto;
+}
+
+} // namespace
+
+// Common =====================================================================
+
+/** Stores global data needed in various classes of the Chart export filter. */
+struct XclExpChRootData : public XclChRootData
+{
+ typedef ::std::vector< XclChFrBlock > XclChFrBlockVector;
+
+ XclExpChChart& mrChartData; /// The chart data object.
+ XclChFrBlockVector maWrittenFrBlocks; /// Stack of future record levels already written out.
+ XclChFrBlockVector maUnwrittenFrBlocks; /// Stack of future record levels not yet written out.
+
+ explicit XclExpChRootData( XclExpChChart& rChartData ) : mrChartData( rChartData ) {}
+
+ /** Registers a new future record level. */
+ void RegisterFutureRecBlock( const XclChFrBlock& rFrBlock );
+ /** Initializes the current future record level (writes all unwritten CHFRBLOCKBEGIN records). */
+ void InitializeFutureRecBlock( XclExpStream& rStrm );
+ /** Finalizes the current future record level (writes CHFRBLOCKEND record if needed). */
+ void FinalizeFutureRecBlock( XclExpStream& rStrm );
+};
+
+void XclExpChRootData::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock )
+{
+ maUnwrittenFrBlocks.push_back( rFrBlock );
+}
+
+void XclExpChRootData::InitializeFutureRecBlock( XclExpStream& rStrm )
+{
+ // first call from a future record writes all missing CHFRBLOCKBEGIN records
+ if( maUnwrittenFrBlocks.empty() )
+ return;
+
+ // write the leading CHFRINFO record
+ if( maWrittenFrBlocks.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_CHFRINFO, 20 );
+ rStrm << EXC_ID_CHFRINFO << EXC_FUTUREREC_EMPTYFLAGS << EXC_CHFRINFO_EXCELXP2003 << EXC_CHFRINFO_EXCELXP2003 << sal_uInt16( 3 );
+ rStrm << sal_uInt16( 0x0850 ) << sal_uInt16( 0x085A ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x086A ) << sal_uInt16( 0x086B );
+ rStrm.EndRecord();
+ }
+ // write all unwritten CHFRBLOCKBEGIN records
+ for( const auto& rUnwrittenFrBlock : maUnwrittenFrBlocks )
+ {
+ OSL_ENSURE( rUnwrittenFrBlock.mnType != EXC_CHFRBLOCK_TYPE_UNKNOWN, "XclExpChRootData::InitializeFutureRecBlock - unknown future record block type" );
+ lclWriteChFrBlockRecord( rStrm, rUnwrittenFrBlock, true );
+ }
+ // move all record infos to vector of written blocks
+ maWrittenFrBlocks.insert( maWrittenFrBlocks.end(), maUnwrittenFrBlocks.begin(), maUnwrittenFrBlocks.end() );
+ maUnwrittenFrBlocks.clear();
+}
+
+void XclExpChRootData::FinalizeFutureRecBlock( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !maUnwrittenFrBlocks.empty() || !maWrittenFrBlocks.empty(), "XclExpChRootData::FinalizeFutureRecBlock - no future record level found" );
+ if( !maUnwrittenFrBlocks.empty() )
+ {
+ // no future record has been written, just forget the topmost level
+ maUnwrittenFrBlocks.pop_back();
+ }
+ else if( !maWrittenFrBlocks.empty() )
+ {
+ // write the CHFRBLOCKEND record for the topmost block and delete it
+ lclWriteChFrBlockRecord( rStrm, maWrittenFrBlocks.back(), false );
+ maWrittenFrBlocks.pop_back();
+ }
+}
+
+XclExpChRoot::XclExpChRoot( const XclExpRoot& rRoot, XclExpChChart& rChartData ) :
+ XclExpRoot( rRoot ),
+ mxChData( std::make_shared<XclExpChRootData>( rChartData ) )
+{
+}
+
+XclExpChRoot::~XclExpChRoot()
+{
+}
+
+Reference< XChartDocument > const & XclExpChRoot::GetChartDocument() const
+{
+ return mxChData->mxChartDoc;
+}
+
+XclExpChChart& XclExpChRoot::GetChartData() const
+{
+ return mxChData->mrChartData;
+}
+
+const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( XclChTypeId eType ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
+}
+
+const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( std::u16string_view rServiceName ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfoFromService( rServiceName );
+}
+
+const XclChFormatInfo& XclExpChRoot::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
+}
+
+void XclExpChRoot::InitConversion( css::uno::Reference< css::chart2::XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) const
+{
+ mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
+}
+
+void XclExpChRoot::FinishConversion() const
+{
+ mxChData->FinishConversion();
+}
+
+bool XclExpChRoot::IsSystemColor( const Color& rColor, sal_uInt16 nSysColorIdx ) const
+{
+ XclExpPalette& rPal = GetPalette();
+ return rPal.IsSystemColor( nSysColorIdx ) && (rColor == rPal.GetDefColor( nSysColorIdx ));
+}
+
+void XclExpChRoot::SetSystemColor( Color& rColor, sal_uInt32& rnColorId, sal_uInt16 nSysColorIdx ) const
+{
+ OSL_ENSURE( GetPalette().IsSystemColor( nSysColorIdx ), "XclExpChRoot::SetSystemColor - invalid color index" );
+ rColor = GetPalette().GetDefColor( nSysColorIdx );
+ rnColorId = XclExpPalette::GetColorIdFromIndex( nSysColorIdx );
+}
+
+sal_Int32 XclExpChRoot::CalcChartXFromHmm( sal_Int32 nPosX ) const
+{
+ return ::limit_cast< sal_Int32, double >( (nPosX - mxChData->mnBorderGapX) / mxChData->mfUnitSizeX, 0, EXC_CHART_TOTALUNITS );
+}
+
+sal_Int32 XclExpChRoot::CalcChartYFromHmm( sal_Int32 nPosY ) const
+{
+ return ::limit_cast< sal_Int32, double >( (nPosY - mxChData->mnBorderGapY) / mxChData->mfUnitSizeY, 0, EXC_CHART_TOTALUNITS );
+}
+
+XclChRectangle XclExpChRoot::CalcChartRectFromHmm( const css::awt::Rectangle& rRect ) const
+{
+ XclChRectangle aRect;
+ aRect.mnX = CalcChartXFromHmm( rRect.X );
+ aRect.mnY = CalcChartYFromHmm( rRect.Y );
+ aRect.mnWidth = CalcChartXFromHmm( rRect.Width );
+ aRect.mnHeight = CalcChartYFromHmm( rRect.Height );
+ return aRect;
+}
+
+void XclExpChRoot::ConvertLineFormat( XclChLineFormat& rLineFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().ReadLineProperties(
+ rLineFmt, *mxChData->mxLineDashTable, rPropSet, ePropMode );
+}
+
+bool XclExpChRoot::ConvertAreaFormat( XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ return GetChartPropSetHelper().ReadAreaProperties( rAreaFmt, rPropSet, ePropMode );
+}
+
+void XclExpChRoot::ConvertEscherFormat(
+ XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().ReadEscherProperties( rEscherFmt, rPicFmt,
+ *mxChData->mxGradientTable, *mxChData->mxHatchTable, *mxChData->mxBitmapTable, rPropSet, ePropMode );
+}
+
+sal_uInt16 XclExpChRoot::ConvertFont( const ScfPropertySet& rPropSet, sal_Int16 nScript ) const
+{
+ XclFontData aFontData;
+ GetFontPropSetHelper().ReadFontProperties( aFontData, rPropSet, EXC_FONTPROPSET_CHART, nScript );
+ return GetFontBuffer().Insert( aFontData, EXC_COLOR_CHARTTEXT );
+}
+
+sal_uInt16 XclExpChRoot::ConvertPieRotation( const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nApiRot = 0;
+ rPropSet.GetProperty( nApiRot, EXC_CHPROP_STARTINGANGLE );
+ return static_cast< sal_uInt16 >( (450 - (nApiRot % 360)) % 360 );
+}
+
+void XclExpChRoot::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock )
+{
+ mxChData->RegisterFutureRecBlock( rFrBlock );
+}
+
+void XclExpChRoot::InitializeFutureRecBlock( XclExpStream& rStrm )
+{
+ mxChData->InitializeFutureRecBlock( rStrm );
+}
+
+void XclExpChRoot::FinalizeFutureRecBlock( XclExpStream& rStrm )
+{
+ mxChData->FinalizeFutureRecBlock( rStrm );
+}
+
+XclExpChGroupBase::XclExpChGroupBase( const XclExpChRoot& rRoot,
+ sal_uInt16 nFrType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ XclExpChRoot( rRoot ),
+ maFrBlock( nFrType )
+{
+}
+
+XclExpChGroupBase::~XclExpChGroupBase()
+{
+}
+
+void XclExpChGroupBase::Save( XclExpStream& rStrm )
+{
+ // header record
+ XclExpRecord::Save( rStrm );
+ // group records
+ if( !HasSubRecords() )
+ return;
+
+ // register the future record context corresponding to this record group
+ RegisterFutureRecBlock( maFrBlock );
+ // CHBEGIN record
+ XclExpEmptyRecord( EXC_ID_CHBEGIN ).Save( rStrm );
+ // embedded records
+ WriteSubRecords( rStrm );
+ // finalize the future records, must be done before the closing CHEND
+ FinalizeFutureRecBlock( rStrm );
+ // CHEND record
+ XclExpEmptyRecord( EXC_ID_CHEND ).Save( rStrm );
+}
+
+bool XclExpChGroupBase::HasSubRecords() const
+{
+ return true;
+}
+
+void XclExpChGroupBase::SetFutureRecordContext( sal_uInt16 nFrContext, sal_uInt16 nFrValue1, sal_uInt16 nFrValue2 )
+{
+ maFrBlock.mnContext = nFrContext;
+ maFrBlock.mnValue1 = nFrValue1;
+ maFrBlock.mnValue2 = nFrValue2;
+}
+
+XclExpChFutureRecordBase::XclExpChFutureRecordBase( const XclExpChRoot& rRoot,
+ XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpFutureRecord( eRecType, nRecId, nRecSize ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChFutureRecordBase::Save( XclExpStream& rStrm )
+{
+ InitializeFutureRecBlock( rStrm );
+ XclExpFutureRecord::Save( rStrm );
+}
+
+// Frame formatting ===========================================================
+
+XclExpChFramePos::XclExpChFramePos( sal_uInt16 nTLMode ) :
+ XclExpRecord( EXC_ID_CHFRAMEPOS, 20 )
+{
+ maData.mnTLMode = nTLMode;
+ maData.mnBRMode = EXC_CHFRAMEPOS_PARENT;
+}
+
+void XclExpChFramePos::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnTLMode << maData.mnBRMode << maData.maRect;
+}
+
+XclExpChLineFormat::XclExpChLineFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHLINEFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 10 ),
+ mnColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChLineFormat::SetDefault( XclChFrameType eDefFrameType )
+{
+ switch( eDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ SetAuto( true );
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ SetAuto( false );
+ maData.mnPattern = EXC_CHLINEFORMAT_NONE;
+ break;
+ default:
+ OSL_FAIL( "XclExpChLineFormat::SetDefault - unknown frame type" );
+ }
+}
+
+void XclExpChLineFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ rRoot.ConvertLineFormat( maData, rPropSet, rFmtInfo.mePropMode );
+ if( HasLine() )
+ {
+ // detect system color, set color identifier (TODO: detect automatic series line)
+ if( (eObjType != EXC_CHOBJTYPE_LINEARSERIES) && rRoot.IsSystemColor( maData.maColor, rFmtInfo.mnAutoLineColorIdx ) )
+ {
+ // store color index from automatic format data
+ mnColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoLineColorIdx );
+ // try to set automatic mode
+ bool bAuto = (maData.mnPattern == EXC_CHLINEFORMAT_SOLID) && (maData.mnWeight == rFmtInfo.mnAutoLineWeight);
+ ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO, bAuto );
+ }
+ else
+ {
+ // user defined color - register in palette
+ mnColorId = rRoot.GetPalette().InsertColor( maData.maColor, EXC_COLOR_CHARTLINE );
+ }
+ }
+ else
+ {
+ // no line - set default system color
+ rRoot.SetSystemColor( maData.maColor, mnColorId, EXC_COLOR_CHWINDOWTEXT );
+ }
+}
+
+bool XclExpChLineFormat::IsDefault( XclChFrameType eDefFrameType ) const
+{
+ return
+ ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasLine()) ||
+ ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto());
+}
+
+void XclExpChLineFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maColor << maData.mnPattern << maData.mnWeight << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ rStrm << rStrm.GetRoot().GetPalette().GetColorIndex( mnColorId );
+}
+
+namespace {
+
+/** Creates a CHLINEFORMAT record from the passed property set. */
+XclExpChLineFormatRef lclCreateLineFormat( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ XclExpChLineFormatRef xLineFmt = new XclExpChLineFormat( rRoot );
+ xLineFmt->Convert( rRoot, rPropSet, eObjType );
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( rFmtInfo.mbDeleteDefFrame && xLineFmt->IsDefault( rFmtInfo.meDefFrameType ) )
+ xLineFmt.clear();
+ return xLineFmt;
+}
+
+} // namespace
+
+XclExpChAreaFormat::XclExpChAreaFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHAREAFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 16 : 12 ),
+ mnPattColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ),
+ mnBackColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+bool XclExpChAreaFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ bool bComplexFill = rRoot.ConvertAreaFormat( maData, rPropSet, rFmtInfo.mePropMode );
+ if( HasArea() )
+ {
+ bool bSolid = maData.mnPattern == EXC_PATT_SOLID;
+ // detect system color, set color identifier (TODO: detect automatic series area)
+ if( (eObjType != EXC_CHOBJTYPE_FILLEDSERIES) && rRoot.IsSystemColor( maData.maPattColor, rFmtInfo.mnAutoPattColorIdx ) )
+ {
+ // store color index from automatic format data
+ mnPattColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoPattColorIdx );
+ // set automatic mode
+ ::set_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO, bSolid );
+ }
+ else
+ {
+ // user defined color - register color in palette
+ mnPattColorId = rRoot.GetPalette().InsertColor( maData.maPattColor, EXC_COLOR_CHARTAREA );
+ }
+ // background color (default system color for solid fills)
+ if( bSolid )
+ rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT );
+ else
+ mnBackColorId = rRoot.GetPalette().InsertColor( maData.maBackColor, EXC_COLOR_CHARTAREA );
+ }
+ else
+ {
+ // no area - set default system colors
+ rRoot.SetSystemColor( maData.maPattColor, mnPattColorId, EXC_COLOR_CHWINDOWBACK );
+ rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT );
+ }
+ return bComplexFill;
+}
+
+void XclExpChAreaFormat::SetDefault( XclChFrameType eDefFrameType )
+{
+ switch( eDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ SetAuto( true );
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ SetAuto( false );
+ maData.mnPattern = EXC_PATT_NONE;
+ break;
+ default:
+ OSL_FAIL( "XclExpChAreaFormat::SetDefault - unknown frame type" );
+ }
+}
+
+bool XclExpChAreaFormat::IsDefault( XclChFrameType eDefFrameType ) const
+{
+ return
+ ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasArea()) ||
+ ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto());
+}
+
+void XclExpChAreaFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maPattColor << maData.maBackColor << maData.mnPattern << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ const XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ rStrm << rPal.GetColorIndex( mnPattColorId ) << rPal.GetColorIndex( mnBackColorId );
+ }
+}
+
+XclExpChEscherFormat::XclExpChEscherFormat( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_UNKNOWN, EXC_ID_CHESCHERFORMAT ),
+ mnColor1Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ),
+ mnColor2Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+}
+
+void XclExpChEscherFormat::Convert( const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ const XclChFormatInfo& rFmtInfo = GetFormatInfo( eObjType );
+ ConvertEscherFormat( maData, maPicFmt, rPropSet, rFmtInfo.mePropMode );
+ // register colors in palette
+ mnColor1Id = RegisterColor( ESCHER_Prop_fillColor );
+ mnColor2Id = RegisterColor( ESCHER_Prop_fillBackColor );
+}
+
+bool XclExpChEscherFormat::IsValid() const
+{
+ return static_cast< bool >(maData.mxEscherSet);
+}
+
+void XclExpChEscherFormat::Save( XclExpStream& rStrm )
+{
+ if( maData.mxEscherSet )
+ {
+ // replace RGB colors with palette indexes in the Escher container
+ const XclExpPalette& rPal = GetPalette();
+ maData.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, 0x08000000 | rPal.GetColorIndex( mnColor1Id ) );
+ maData.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x08000000 | rPal.GetColorIndex( mnColor2Id ) );
+
+ // save the record group
+ XclExpChGroupBase::Save( rStrm );
+ }
+}
+
+bool XclExpChEscherFormat::HasSubRecords() const
+{
+ // no subrecords for gradients
+ return maPicFmt.mnBmpMode != EXC_CHPICFORMAT_NONE;
+}
+
+void XclExpChEscherFormat::WriteSubRecords( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( EXC_ID_CHPICFORMAT, 14 );
+ rStrm << maPicFmt.mnBmpMode << sal_uInt16( 0 ) << maPicFmt.mnFlags << maPicFmt.mfScale;
+ rStrm.EndRecord();
+}
+
+sal_uInt32 XclExpChEscherFormat::RegisterColor( sal_uInt16 nPropId )
+{
+ sal_uInt32 nBGRValue;
+ if( maData.mxEscherSet && maData.mxEscherSet->GetOpt( nPropId, nBGRValue ) )
+ {
+ // swap red and blue
+ Color aColor( nBGRValue & 0xff, (nBGRValue >> 8) & 0xff, (nBGRValue >> 16) & 0xff );
+ return GetPalette().InsertColor( aColor, EXC_COLOR_CHARTAREA );
+ }
+ return XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK );
+}
+
+void XclExpChEscherFormat::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( maData.mxEscherSet, "XclExpChEscherFormat::WriteBody - missing property container" );
+ // write Escher property container via temporary memory stream
+ SvMemoryStream aMemStrm;
+ maData.mxEscherSet->Commit( aMemStrm );
+ aMemStrm.FlushBuffer();
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( aMemStrm );
+}
+
+XclExpChFrameBase::XclExpChFrameBase()
+{
+}
+
+XclExpChFrameBase::~XclExpChFrameBase()
+{
+}
+
+void XclExpChFrameBase::ConvertFrameBase( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ // line format
+ mxLineFmt = new XclExpChLineFormat( rRoot );
+ mxLineFmt->Convert( rRoot, rPropSet, eObjType );
+ // area format (only for frame objects)
+ if( !rRoot.GetFormatInfo( eObjType ).mbIsFrame )
+ return;
+
+ mxAreaFmt = new XclExpChAreaFormat( rRoot );
+ bool bComplexFill = mxAreaFmt->Convert( rRoot, rPropSet, eObjType );
+ if( (rRoot.GetBiff() == EXC_BIFF8) && bComplexFill )
+ {
+ mxEscherFmt = new XclExpChEscherFormat( rRoot );
+ mxEscherFmt->Convert( rPropSet, eObjType );
+ if( mxEscherFmt->IsValid() )
+ mxAreaFmt->SetAuto( false );
+ else
+ mxEscherFmt.clear();
+ }
+}
+
+void XclExpChFrameBase::SetDefaultFrameBase( const XclExpChRoot& rRoot,
+ XclChFrameType eDefFrameType, bool bIsFrame )
+{
+ // line format
+ mxLineFmt = new XclExpChLineFormat( rRoot );
+ mxLineFmt->SetDefault( eDefFrameType );
+ // area format (only for frame objects)
+ if( bIsFrame )
+ {
+ mxAreaFmt = new XclExpChAreaFormat( rRoot );
+ mxAreaFmt->SetDefault( eDefFrameType );
+ mxEscherFmt.clear();
+ }
+}
+
+bool XclExpChFrameBase::IsDefaultFrameBase( XclChFrameType eDefFrameType ) const
+{
+ return
+ (!mxLineFmt || mxLineFmt->IsDefault( eDefFrameType )) &&
+ (!mxAreaFmt || mxAreaFmt->IsDefault( eDefFrameType ));
+}
+
+void XclExpChFrameBase::WriteFrameRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxLineFmt );
+ lclSaveRecord( rStrm, mxAreaFmt );
+ lclSaveRecord( rStrm, mxEscherFmt );
+}
+
+XclExpChFrame::XclExpChFrame( const XclExpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_FRAME, EXC_ID_CHFRAME, 4 ),
+ meObjType( eObjType )
+{
+}
+
+void XclExpChFrame::Convert( const ScfPropertySet& rPropSet )
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType );
+}
+
+void XclExpChFrame::SetAutoFlags( bool bAutoPos, bool bAutoSize )
+{
+ ::set_flag( maData.mnFlags, EXC_CHFRAME_AUTOPOS, bAutoPos );
+ ::set_flag( maData.mnFlags, EXC_CHFRAME_AUTOSIZE, bAutoSize );
+}
+
+bool XclExpChFrame::IsDefault() const
+{
+ return IsDefaultFrameBase( GetFormatInfo( meObjType ).meDefFrameType );
+}
+
+bool XclExpChFrame::IsDeleteable() const
+{
+ return IsDefault() && GetFormatInfo( meObjType ).mbDeleteDefFrame;
+}
+
+void XclExpChFrame::Save( XclExpStream& rStrm )
+{
+ switch( meObjType )
+ {
+ // wall/floor frame without CHFRAME header record
+ case EXC_CHOBJTYPE_WALL3D:
+ case EXC_CHOBJTYPE_FLOOR3D:
+ WriteFrameRecords( rStrm );
+ break;
+ default:
+ XclExpChGroupBase::Save( rStrm );
+ }
+}
+
+void XclExpChFrame::WriteSubRecords( XclExpStream& rStrm )
+{
+ WriteFrameRecords( rStrm );
+}
+
+void XclExpChFrame::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnFormat << maData.mnFlags;
+}
+
+namespace {
+
+/** Creates a CHFRAME record from the passed property set. */
+XclExpChFrameRef lclCreateFrame( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ XclExpChFrameRef xFrame = new XclExpChFrame( rRoot, eObjType );
+ xFrame->Convert( rPropSet );
+ if( xFrame->IsDeleteable() )
+ xFrame.clear();
+ return xFrame;
+}
+
+} // namespace
+
+// Source links ===============================================================
+
+namespace {
+
+void lclAddDoubleRefData(
+ ScTokenArray& orArray, const FormulaToken& rToken,
+ SCTAB nScTab1, SCCOL nScCol1, SCROW nScRow1,
+ SCTAB nScTab2, SCCOL nScCol2, SCROW nScRow2 )
+{
+ ScComplexRefData aComplexRef;
+ aComplexRef.InitRange(ScRange(nScCol1,nScRow1,nScTab1,nScCol2,nScRow2,nScTab2));
+ aComplexRef.Ref1.SetFlag3D( true );
+
+ if( orArray.GetLen() > 0 )
+ orArray.AddOpCode( ocUnion );
+
+ OSL_ENSURE( (rToken.GetType() == ::formula::svDoubleRef) || (rToken.GetType() == ::formula::svExternalDoubleRef),
+ "lclAddDoubleRefData - double reference token expected");
+ if( rToken.GetType() == ::formula::svExternalDoubleRef )
+ orArray.AddExternalDoubleReference(
+ rToken.GetIndex(), rToken.GetString(), aComplexRef);
+ else
+ orArray.AddDoubleReference( aComplexRef );
+}
+
+} // namespace
+
+XclExpChSourceLink::XclExpChSourceLink( const XclExpChRoot& rRoot, sal_uInt8 nDestType ) :
+ XclExpRecord( EXC_ID_CHSOURCELINK ),
+ XclExpChRoot( rRoot )
+{
+ maData.mnDestType = nDestType;
+ maData.mnLinkType = EXC_CHSRCLINK_DIRECTLY;
+}
+
+sal_uInt16 XclExpChSourceLink::ConvertDataSequence( Reference< XDataSequence > const & xDataSeq, bool bSplitToColumns, sal_uInt16 nDefCount )
+{
+ mxLinkFmla.reset();
+ maData.mnLinkType = EXC_CHSRCLINK_DEFAULT;
+
+ if( !xDataSeq.is() )
+ return nDefCount;
+
+ // Compile the range representation string into token array. Note that the
+ // source range text depends on the current grammar.
+ OUString aRangeRepr = xDataSeq->getSourceRangeRepresentation();
+ ScCompiler aComp( GetDoc(), ScAddress(), GetDoc().GetGrammar() );
+ std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aRangeRepr));
+ if( !pArray )
+ return nDefCount;
+
+ ScTokenArray aArray(GetRoot().GetDoc());
+ sal_uInt32 nValueCount = 0;
+ FormulaTokenArrayPlainIterator aIter(*pArray);
+ for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
+ {
+ switch( pToken->GetType() )
+ {
+ case ::formula::svSingleRef:
+ case ::formula::svExternalSingleRef:
+ // for a single ref token, just add it to the new token array as is
+ if( aArray.GetLen() > 0 )
+ aArray.AddOpCode( ocUnion );
+ aArray.AddToken( *pToken );
+ ++nValueCount;
+ break;
+
+ case ::formula::svDoubleRef:
+ case ::formula::svExternalDoubleRef:
+ {
+ // split 3-dimensional ranges into single sheets
+ const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
+ ScAddress aAbs1 = rComplexRef.Ref1.toAbs(GetRoot().GetDoc(), ScAddress());
+ ScAddress aAbs2 = rComplexRef.Ref2.toAbs(GetRoot().GetDoc(), ScAddress());
+ for (SCTAB nScTab = aAbs1.Tab(); nScTab <= aAbs2.Tab(); ++nScTab)
+ {
+ // split 2-dimensional ranges into single columns
+ if (bSplitToColumns && (aAbs1.Col() < aAbs2.Col()) && (aAbs1.Row() < aAbs2.Row()))
+ for (SCCOL nScCol = aAbs1.Col(); nScCol <= aAbs2.Col(); ++nScCol)
+ lclAddDoubleRefData(aArray, *pToken, nScTab, nScCol, aAbs1.Row(), nScTab, nScCol, aAbs2.Row());
+ else
+ lclAddDoubleRefData(aArray, *pToken, nScTab, aAbs1.Col(), aAbs1.Row(), nScTab, aAbs2.Col(), aAbs2.Row());
+ }
+ sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
+ sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
+ sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
+ nValueCount += nCols * nRows * nTabs;
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ const ScAddress aBaseCell;
+ mxLinkFmla = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aArray, &aBaseCell );
+ maData.mnLinkType = EXC_CHSRCLINK_WORKSHEET;
+ return ulimit_cast< sal_uInt16 >( nValueCount, EXC_CHDATAFORMAT_MAXPOINTCOUNT );
+}
+
+void XclExpChSourceLink::ConvertString( const OUString& aString )
+{
+ mxString = XclExpStringHelper::CreateString( GetRoot(), aString, XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+}
+
+sal_uInt16 XclExpChSourceLink::ConvertStringSequence( const Sequence< Reference< XFormattedString > >& rStringSeq )
+{
+ mxString.reset();
+ sal_uInt16 nFontIdx = EXC_FONT_APP;
+ if( rStringSeq.hasElements() )
+ {
+ mxString = XclExpStringHelper::CreateString( GetRoot(), OUString(), XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+ Reference< XBreakIterator > xBreakIt = GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+ // convert all formatted string entries from the sequence
+ for( const Reference< XFormattedString >& rString : rStringSeq )
+ {
+ if( rString.is() )
+ {
+ sal_uInt16 nWstrnFontIdx = EXC_FONT_NOTFOUND;
+ sal_uInt16 nAsianFontIdx = EXC_FONT_NOTFOUND;
+ sal_uInt16 nCmplxFontIdx = EXC_FONT_NOTFOUND;
+ OUString aText = rString->getString();
+ ScfPropertySet aStrProp( rString );
+
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( GetRoot(), aText );
+
+ // process all script portions
+ sal_Int32 nPortionPos = 0;
+ sal_Int32 nTextLen = aText.getLength();
+ while( nPortionPos < nTextLen )
+ {
+ // get script type and end position of next script portion
+ sal_Int16 nScript = xBreakIt->getScriptType( aText, nPortionPos );
+ sal_Int32 nPortionEnd = xBreakIt->endOfScript( aText, nPortionPos, nScript );
+
+ // reuse previous script for following weak portions
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+
+ // Excel start position of this portion
+ sal_uInt16 nXclPortionStart = mxString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *mxString, GetRoot(), aText.subView( nPortionPos, nPortionEnd - nPortionPos ) );
+ if( nXclPortionStart < mxString->Len() )
+ {
+ // find font index variable dependent on script type
+ sal_uInt16& rnFontIdx = (nScript == ApiScriptType::COMPLEX) ? nCmplxFontIdx :
+ ((nScript == ApiScriptType::ASIAN) ? nAsianFontIdx : nWstrnFontIdx);
+
+ // insert font into buffer (if not yet done)
+ if( rnFontIdx == EXC_FONT_NOTFOUND )
+ rnFontIdx = ConvertFont( aStrProp, nScript );
+
+ // insert font index into format run vector
+ mxString->AppendFormat( nXclPortionStart, rnFontIdx );
+ }
+
+ // go to next script portion
+ nLastScript = nScript;
+ nPortionPos = nPortionEnd;
+ }
+ }
+ }
+ if( !mxString->IsEmpty() )
+ {
+ // get leading font index
+ const XclFormatRunVec& rFormats = mxString->GetFormats();
+ OSL_ENSURE( !rFormats.empty() && (rFormats.front().mnChar == 0),
+ "XclExpChSourceLink::ConvertStringSequenc - missing leading format" );
+ // remove leading format run, if entire string is equally formatted
+ if( rFormats.size() == 1 )
+ nFontIdx = mxString->RemoveLeadingFont();
+ else if( !rFormats.empty() )
+ nFontIdx = rFormats.front().mnFontIdx;
+ // add trailing format run, if string is rich-formatted
+ if( mxString->IsRich() )
+ mxString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ }
+ return nFontIdx;
+}
+
+void XclExpChSourceLink::ConvertNumFmt( const ScfPropertySet& rPropSet, bool bPercent )
+{
+ sal_Int32 nApiNumFmt = 0;
+ if( bPercent ? rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_PERCENTAGENUMFMT ) : rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
+ {
+ ::set_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
+ maData.mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
+ }
+}
+
+void XclExpChSourceLink::AppendString( std::u16string_view rStr )
+{
+ if (!mxString)
+ return;
+ XclExpStringHelper::AppendString( *mxString, GetRoot(), rStr );
+}
+
+void XclExpChSourceLink::Save( XclExpStream& rStrm )
+{
+ // CHFORMATRUNS record
+ if( mxString && mxString->IsRich() )
+ {
+ std::size_t nRecSize = (1 + mxString->GetFormatsCount()) * ((GetBiff() == EXC_BIFF8) ? 2 : 1);
+ rStrm.StartRecord( EXC_ID_CHFORMATRUNS, nRecSize );
+ mxString->WriteFormats( rStrm, true );
+ rStrm.EndRecord();
+ }
+ // CHSOURCELINK record
+ XclExpRecord::Save( rStrm );
+ // CHSTRING record
+ if( mxString && !mxString->IsEmpty() )
+ {
+ rStrm.StartRecord( EXC_ID_CHSTRING, 2 + mxString->GetSize() );
+ rStrm << sal_uInt16( 0 ) << *mxString;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpChSourceLink::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnDestType
+ << maData.mnLinkType
+ << maData.mnFlags
+ << maData.mnNumFmtIdx
+ << mxLinkFmla;
+}
+
+// Text =======================================================================
+
+XclExpChFont::XclExpChFont( sal_uInt16 nFontIdx ) :
+ XclExpUInt16Record( EXC_ID_CHFONT, nFontIdx )
+{
+}
+
+XclExpChObjectLink::XclExpChObjectLink( sal_uInt16 nLinkTarget, const XclChDataPointPos& rPointPos ) :
+ XclExpRecord( EXC_ID_CHOBJECTLINK, 6 )
+{
+ maData.mnTarget = nLinkTarget;
+ maData.maPointPos = rPointPos;
+}
+
+void XclExpChObjectLink::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnTarget << maData.maPointPos.mnSeriesIdx << maData.maPointPos.mnPointIdx;
+}
+
+XclExpChFrLabelProps::XclExpChFrLabelProps( const XclExpChRoot& rRoot ) :
+ XclExpChFutureRecordBase( rRoot, EXC_FUTUREREC_UNUSEDREF, EXC_ID_CHFRLABELPROPS, 4 )
+{
+}
+
+void XclExpChFrLabelProps::Convert( const ScfPropertySet& rPropSet,
+ bool bShowCateg, bool bShowValue, bool bShowPercent, bool bShowBubble )
+{
+ // label value flags
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWSERIES, false );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWCATEG, bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWVALUE, bShowValue );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWPERCENT, bShowPercent );
+ ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWBUBBLE, bShowBubble );
+
+ // label value separator
+ maData.maSeparator = rPropSet.GetStringProperty( EXC_CHPROP_LABELSEPARATOR );
+ if( maData.maSeparator.isEmpty() )
+ maData.maSeparator = " ";
+}
+
+void XclExpChFrLabelProps::WriteBody( XclExpStream& rStrm )
+{
+ XclExpString aXclSep( maData.maSeparator, XclStrFlags::ForceUnicode | XclStrFlags::SmartFlags );
+ rStrm << maData.mnFlags << aXclSep;
+}
+
+XclExpChFontBase::~XclExpChFontBase()
+{
+}
+
+void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, sal_uInt16 nFontIdx )
+{
+ if( const XclExpFont* pFont = rRoot.GetFontBuffer().GetFont( nFontIdx ) )
+ {
+ XclExpChFontRef xFont = new XclExpChFont( nFontIdx );
+ SetFont( xFont, pFont->GetFontData().maColor, pFont->GetFontColorId() );
+ }
+}
+
+void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet )
+{
+ ConvertFontBase( rRoot, rRoot.ConvertFont( rPropSet, rRoot.GetDefApiScript() ) );
+}
+
+void XclExpChFontBase::ConvertRotationBase(const ScfPropertySet& rPropSet, bool bSupportsStacked )
+{
+ sal_uInt16 nRotation = XclChPropSetHelper::ReadRotationProperties( rPropSet, bSupportsStacked );
+ SetRotation( nRotation );
+}
+
+XclExpChText::XclExpChText( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TEXT, EXC_ID_CHTEXT, (rRoot.GetBiff() == EXC_BIFF8) ? 32 : 26 ),
+ mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChText::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId )
+{
+ mxFont = xFont;
+ maData.maTextColor = rColor;
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, rColor == COL_AUTO );
+ mnTextColorId = nColorId;
+}
+
+void XclExpChText::SetRotation( sal_uInt16 nRotation )
+{
+ maData.mnRotation = nRotation;
+ ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 8, 3 );
+}
+
+void XclExpChText::ConvertTitle( Reference< XTitle > const & xTitle, sal_uInt16 nTarget, const OUString* pSubTitle )
+{
+ switch( nTarget )
+ {
+ case EXC_CHOBJLINK_TITLE: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_TITLE ); break;
+ case EXC_CHOBJLINK_YAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 1 ); break;
+ case EXC_CHOBJLINK_XAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE ); break;
+ case EXC_CHOBJLINK_ZAXIS: SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 2 ); break;
+ }
+
+ mxSrcLink.clear();
+ mxObjLink = new XclExpChObjectLink( nTarget, XclChDataPointPos( 0, 0 ) );
+
+ if( xTitle.is() )
+ {
+ // title frame formatting
+ ScfPropertySet aTitleProp( xTitle );
+ mxFrame = lclCreateFrame( GetChRoot(), aTitleProp, EXC_CHOBJTYPE_TEXT );
+
+ // string sequence
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ sal_uInt16 nFontIdx = mxSrcLink->ConvertStringSequence( xTitle->getText() );
+ if (pSubTitle)
+ {
+ // append subtitle as the 2nd line of the title.
+ OUString aSubTitle = "\n" + *pSubTitle;
+ mxSrcLink->AppendString(aSubTitle);
+ }
+
+ ConvertFontBase( GetChRoot(), nFontIdx );
+
+ // rotation
+ ConvertRotationBase( aTitleProp, true );
+
+ // manual text position - only for main title
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT );
+ if( nTarget == EXC_CHOBJLINK_TITLE )
+ {
+ Any aRelPos;
+ if( aTitleProp.GetAnyProperty( aRelPos, EXC_CHPROP_RELATIVEPOSITION ) && aRelPos.has< RelativePosition >() ) try
+ {
+ // calculate absolute position for CHTEXT record
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< XShape > xTitleShape( xChart1Doc->getTitle(), UNO_SET_THROW );
+ css::awt::Point aPos = xTitleShape->getPosition();
+ css::awt::Size aSize = xTitleShape->getSize();
+ css::awt::Rectangle aRect( aPos.X, aPos.Y, aSize.Width, aSize.Height );
+ maData.maRect = CalcChartRectFromHmm( aRect );
+ ::insert_value( maData.mnFlags2, EXC_CHTEXT_POS_MOVED, 0, 4 );
+ // manual title position implies manual plot area
+ GetChartData().SetManualPlotArea();
+ // calculate the default title position in chart units
+ sal_Int32 nDefPosX = ::std::max< sal_Int32 >( (EXC_CHART_TOTALUNITS - maData.maRect.mnWidth) / 2, 0 );
+ sal_Int32 nDefPosY = 85;
+ // set the position relative to the standard position
+ XclChRectangle& rFrameRect = mxFramePos->GetFramePosData().maRect;
+ rFrameRect.mnX = maData.maRect.mnX - nDefPosX;
+ rFrameRect.mnY = maData.maRect.mnY - nDefPosY;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+ }
+ else
+ {
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED );
+ }
+}
+
+void XclExpChText::ConvertLegend( const ScfPropertySet& rPropSet )
+{
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOGEN );
+ ConvertFontBase( GetChRoot(), rPropSet );
+}
+
+bool XclExpChText::ConvertDataLabel( const ScfPropertySet& rPropSet,
+ const XclChTypeInfo& rTypeInfo, const XclChDataPointPos& rPointPos )
+{
+ SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_DATALABEL, rPointPos.mnPointIdx, rPointPos.mnSeriesIdx );
+
+ cssc2::DataPointLabel aPointLabel;
+ if( !rPropSet.GetProperty( aPointLabel, EXC_CHPROP_LABEL ) )
+ return false;
+
+ // percentage only allowed in pie and donut charts
+ bool bIsPie = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE;
+ // bubble sizes only allowed in bubble charts
+ bool bIsBubble = rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES;
+ OSL_ENSURE( (GetBiff() == EXC_BIFF8) || !bIsBubble, "XclExpChText::ConvertDataLabel - bubble charts only in BIFF8" );
+
+ // raw show flags
+ bool bShowValue = !bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size
+ bool bShowPercent = bIsPie && aPointLabel.ShowNumberInPercent; // percentage only in pie/donut charts
+ bool bShowCateg = aPointLabel.ShowCategoryName;
+ bool bShowBubble = bIsBubble && aPointLabel.ShowNumber; // Chart2 uses 'ShowNumber' for bubble size
+ bool bShowAny = bShowValue || bShowPercent || bShowCateg || bShowBubble;
+
+ // create the CHFRLABELPROPS record for extended settings in BIFF8
+ if( bShowAny && (GetBiff() == EXC_BIFF8) )
+ {
+ mxLabelProps = new XclExpChFrLabelProps( GetChRoot() );
+ mxLabelProps->Convert( rPropSet, bShowCateg, bShowValue, bShowPercent, bShowBubble );
+ }
+
+ // restrict to combinations allowed in CHTEXT
+ if( bShowPercent ) bShowValue = false; // percent wins over value
+ if( bShowValue ) bShowCateg = false; // value wins over category
+ if( bShowValue || bShowCateg ) bShowBubble = false; // value or category wins over bubble size
+
+ // set all flags
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bShowValue );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bShowPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bShowPercent && bShowCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWBUBBLE, bShowBubble );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL, bShowAny && aPointLabel.ShowLegendSymbol );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bShowAny );
+
+ if( bShowAny )
+ {
+ // font settings
+ ConvertFontBase( GetChRoot(), rPropSet );
+ ConvertRotationBase( rPropSet, false );
+ // label placement
+ sal_Int32 nPlacement = 0;
+ sal_uInt16 nLabelPos = EXC_CHTEXT_POS_AUTO;
+ if( rPropSet.GetProperty( nPlacement, EXC_CHPROP_LABELPLACEMENT ) )
+ {
+ using namespace cssc::DataLabelPlacement;
+ if( nPlacement == rTypeInfo.mnDefaultLabelPos )
+ {
+ nLabelPos = EXC_CHTEXT_POS_DEFAULT;
+ }
+ else switch( nPlacement )
+ {
+ case AVOID_OVERLAP: nLabelPos = EXC_CHTEXT_POS_AUTO; break;
+ case CENTER: nLabelPos = EXC_CHTEXT_POS_CENTER; break;
+ case TOP: nLabelPos = EXC_CHTEXT_POS_ABOVE; break;
+ case TOP_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case BOTTOM_LEFT: nLabelPos = EXC_CHTEXT_POS_LEFT; break;
+ case BOTTOM: nLabelPos = EXC_CHTEXT_POS_BELOW; break;
+ case BOTTOM_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case TOP_RIGHT: nLabelPos = EXC_CHTEXT_POS_RIGHT; break;
+ case INSIDE: nLabelPos = EXC_CHTEXT_POS_INSIDE; break;
+ case OUTSIDE: nLabelPos = EXC_CHTEXT_POS_OUTSIDE; break;
+ case NEAR_ORIGIN: nLabelPos = EXC_CHTEXT_POS_AXIS; break;
+ default: OSL_FAIL( "XclExpChText::ConvertDataLabel - unknown label placement type" );
+ }
+ }
+ ::insert_value( maData.mnFlags2, nLabelPos, 0, 4 );
+ // source link (contains number format)
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ if( bShowValue || bShowPercent )
+ // percentage format wins over value format
+ mxSrcLink->ConvertNumFmt( rPropSet, bShowPercent );
+ // object link
+ mxObjLink = new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos );
+ }
+
+ /* Return true to indicate valid label settings:
+ - for existing labels at entire series
+ - for any settings at single data point (to be able to delete a point label) */
+ return bShowAny || (rPointPos.mnPointIdx != EXC_CHDATAFORMAT_ALLPOINTS);
+}
+
+void XclExpChText::ConvertTrendLineEquation( const ScfPropertySet& rPropSet, const XclChDataPointPos& rPointPos )
+{
+ // required flags
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ); // must set this to make equation visible in Excel
+ // frame formatting
+ mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_TEXT );
+ // font settings
+ maData.mnHAlign = EXC_CHTEXT_ALIGN_TOPLEFT;
+ maData.mnVAlign = EXC_CHTEXT_ALIGN_TOPLEFT;
+ ConvertFontBase( GetChRoot(), rPropSet );
+ // source link (contains number format)
+ mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ mxSrcLink->ConvertNumFmt( rPropSet, false );
+ // object link
+ mxObjLink = new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos );
+}
+
+sal_uInt16 XclExpChText::GetAttLabelFlags() const
+{
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWVALUE, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWPERCENT, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEGPERC, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC ) );
+ ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEG, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ) );
+ return nFlags;
+}
+
+void XclExpChText::WriteSubRecords( XclExpStream& rStrm )
+{
+ // CHFRAMEPOS record
+ lclSaveRecord( rStrm, mxFramePos );
+ // CHFONT record
+ lclSaveRecord( rStrm, mxFont );
+ // CHSOURCELINK group
+ lclSaveRecord( rStrm, mxSrcLink );
+ // CHFRAME group
+ lclSaveRecord( rStrm, mxFrame );
+ // CHOBJECTLINK record
+ lclSaveRecord( rStrm, mxObjLink );
+ // CHFRLABELPROPS record
+ lclSaveRecord( rStrm, mxLabelProps );
+}
+
+void XclExpChText::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnHAlign
+ << maData.mnVAlign
+ << maData.mnBackMode
+ << maData.maTextColor
+ << maData.maRect
+ << maData.mnFlags;
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ rStrm << GetPalette().GetColorIndex( mnTextColorId )
+ << maData.mnFlags2
+ << maData.mnRotation;
+ }
+}
+
+namespace {
+
+/** Creates and returns an Excel text object from the passed title. */
+XclExpChTextRef lclCreateTitle( const XclExpChRoot& rRoot, Reference< XTitled > const & xTitled, sal_uInt16 nTarget,
+ const OUString* pSubTitle = nullptr )
+{
+ Reference< XTitle > xTitle;
+ if( xTitled.is() )
+ xTitle = xTitled->getTitleObject();
+
+ XclExpChTextRef xText = new XclExpChText( rRoot );
+ xText->ConvertTitle( xTitle, nTarget, pSubTitle );
+ /* Do not delete the CHTEXT group for the main title. A missing CHTEXT
+ will be interpreted as auto-generated title showing the series title in
+ charts that contain exactly one data series. */
+ if( (nTarget != EXC_CHOBJLINK_TITLE) && !xText->HasString() )
+ xText.clear();
+
+ return xText;
+}
+
+}
+
+// Data series ================================================================
+
+XclExpChMarkerFormat::XclExpChMarkerFormat( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHMARKERFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 20 : 12 ),
+ mnLineColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ),
+ mnFillColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) )
+{
+}
+
+void XclExpChMarkerFormat::Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx )
+{
+ XclChPropSetHelper::ReadMarkerProperties( maData, rPropSet, nFormatIdx );
+ /* Set marker line/fill color to series line color.
+ TODO: remove this if OOChart supports own colors in markers. */
+ Color aLineColor;
+ if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) )
+ maData.maLineColor = maData.maFillColor = aLineColor;
+ // register colors in palette
+ RegisterColors( rRoot );
+}
+
+void XclExpChMarkerFormat::ConvertStockSymbol( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, bool bCloseSymbol )
+{
+ // clear the automatic flag
+ ::set_flag( maData.mnFlags, EXC_CHMARKERFORMAT_AUTO, false );
+ // symbol type and color
+ if( bCloseSymbol )
+ {
+ // set symbol type for the 'close' data series
+ maData.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ;
+ maData.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE;
+ // set symbol line/fill color to series line color
+ Color aLineColor;
+ if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) )
+ {
+ maData.maLineColor = maData.maFillColor = aLineColor;
+ RegisterColors( rRoot );
+ }
+ }
+ else
+ {
+ // set invisible symbol
+ maData.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL;
+ }
+}
+
+void XclExpChMarkerFormat::RegisterColors( const XclExpChRoot& rRoot )
+{
+ if( HasMarker() )
+ {
+ if( HasLineColor() )
+ mnLineColorId = rRoot.GetPalette().InsertColor( maData.maLineColor, EXC_COLOR_CHARTLINE );
+ if( HasFillColor() )
+ mnFillColorId = rRoot.GetPalette().InsertColor( maData.maFillColor, EXC_COLOR_CHARTAREA );
+ }
+}
+
+void XclExpChMarkerFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maLineColor << maData.maFillColor << maData.mnMarkerType << maData.mnFlags;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ const XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ rStrm << rPal.GetColorIndex( mnLineColorId ) << rPal.GetColorIndex( mnFillColorId ) << maData.mnMarkerSize;
+ }
+}
+
+XclExpChPieFormat::XclExpChPieFormat() :
+ XclExpUInt16Record( EXC_ID_CHPIEFORMAT, 0 )
+{
+}
+
+void XclExpChPieFormat::Convert( const ScfPropertySet& rPropSet )
+{
+ double fApiDist(0.0);
+ if( rPropSet.GetProperty( fApiDist, EXC_CHPROP_OFFSET ) )
+ SetValue( limit_cast< sal_uInt16 >( fApiDist * 100.0, 0, 100 ) );
+}
+
+XclExpCh3dDataFormat::XclExpCh3dDataFormat() :
+ XclExpRecord( EXC_ID_CH3DDATAFORMAT, 2 )
+{
+}
+
+void XclExpCh3dDataFormat::Convert( const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nApiType(0);
+ if( !rPropSet.GetProperty( nApiType, EXC_CHPROP_GEOMETRY3D ) )
+ return;
+
+ using namespace cssc2::DataPointGeometry3D;
+ switch( nApiType )
+ {
+ case CUBOID:
+ maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
+ maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
+ break;
+ case PYRAMID:
+ maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
+ maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
+ break;
+ case CYLINDER:
+ maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
+ maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
+ break;
+ case CONE:
+ maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
+ maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
+ break;
+ default:
+ OSL_FAIL( "XclExpCh3dDataFormat::Convert - unknown 3D bar format" );
+ }
+}
+
+void XclExpCh3dDataFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnBase << maData.mnTop;
+}
+
+XclExpChAttachedLabel::XclExpChAttachedLabel( sal_uInt16 nFlags ) :
+ XclExpUInt16Record( EXC_ID_CHATTACHEDLABEL, nFlags )
+{
+}
+
+XclExpChDataFormat::XclExpChDataFormat( const XclExpChRoot& rRoot,
+ const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DATAFORMAT, EXC_ID_CHDATAFORMAT, 8 )
+{
+ maData.maPointPos = rPointPos;
+ maData.mnFormatIdx = nFormatIdx;
+}
+
+void XclExpChDataFormat::ConvertDataSeries( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo )
+{
+ // line and area formatting
+ ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType() );
+
+ // data point symbols
+ bool bIsFrame = rTypeInfo.IsSeriesFrameFormat();
+ if( !bIsFrame )
+ {
+ mxMarkerFmt = new XclExpChMarkerFormat( GetChRoot() );
+ mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx );
+ }
+
+ // pie segments
+ if( rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE )
+ {
+ mxPieFmt = new XclExpChPieFormat();
+ mxPieFmt->Convert( rPropSet );
+ }
+
+ // 3D bars (only allowed for entire series in BIFF8)
+ if( IsSeriesFormat() && (GetBiff() == EXC_BIFF8) && rTypeInfo.mb3dChart && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) )
+ {
+ mx3dDataFmt = new XclExpCh3dDataFormat();
+ mx3dDataFmt->Convert( rPropSet );
+ }
+
+ // spline
+ if( IsSeriesFormat() && rTypeInfo.mbSpline && !bIsFrame )
+ mxSeriesFmt = new XclExpUInt16Record( EXC_ID_CHSERIESFORMAT, EXC_CHSERIESFORMAT_SMOOTHED );
+
+ // data point labels
+ XclExpChTextRef xLabel = new XclExpChText( GetChRoot() );
+ if( xLabel->ConvertDataLabel( rPropSet, rTypeInfo, maData.maPointPos ) )
+ {
+ // CHTEXT groups for data labels are stored in global CHCHART group
+ GetChartData().SetDataLabel( xLabel );
+ mxAttLabel = new XclExpChAttachedLabel( xLabel->GetAttLabelFlags() );
+ }
+}
+
+void XclExpChDataFormat::ConvertStockSeries( const ScfPropertySet& rPropSet, bool bCloseSymbol )
+{
+ // set line format to invisible
+ SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, false );
+ // set symbols to invisible or to 'close' series symbol
+ mxMarkerFmt = new XclExpChMarkerFormat( GetChRoot() );
+ mxMarkerFmt->ConvertStockSymbol( GetChRoot(), rPropSet, bCloseSymbol );
+}
+
+void XclExpChDataFormat::ConvertLine( const ScfPropertySet& rPropSet, XclChObjectType eObjType )
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, eObjType );
+}
+
+void XclExpChDataFormat::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mx3dDataFmt );
+ WriteFrameRecords( rStrm );
+ lclSaveRecord( rStrm, mxPieFmt );
+ lclSaveRecord( rStrm, mxMarkerFmt );
+ lclSaveRecord( rStrm, mxSeriesFmt );
+ lclSaveRecord( rStrm, mxAttLabel );
+}
+
+void XclExpChDataFormat::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maPointPos.mnPointIdx
+ << maData.maPointPos.mnSeriesIdx
+ << maData.mnFormatIdx
+ << maData.mnFlags;
+}
+
+XclExpChSerTrendLine::XclExpChSerTrendLine( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHSERTRENDLINE, 28 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+bool XclExpChSerTrendLine::Convert( Reference< XRegressionCurve > const & xRegCurve, sal_uInt16 nSeriesIdx )
+{
+ if( !xRegCurve.is() )
+ return false;
+
+ // trend line type
+ ScfPropertySet aCurveProp( xRegCurve );
+
+ OUString aService = aCurveProp.GetServiceName();
+ if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL;
+ maData.mnOrder = 1;
+ }
+ else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_EXPONENTIAL;
+ }
+ else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_LOGARITHMIC;
+ }
+ else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POWER;
+ }
+ else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL;
+ sal_Int32 aDegree;
+ aCurveProp.GetProperty(aDegree, EXC_CHPROP_POLYNOMIAL_DEGREE);
+ maData.mnOrder = static_cast<sal_uInt8> (aDegree);
+ }
+ else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
+ {
+ maData.mnLineType = EXC_CHSERTREND_MOVING_AVG;
+ sal_Int32 aPeriod;
+ aCurveProp.GetProperty(aPeriod, EXC_CHPROP_MOVING_AVERAGE_PERIOD);
+ maData.mnOrder = static_cast<sal_uInt8> (aPeriod);
+ }
+ else
+ {
+ return false;
+ }
+
+ aCurveProp.GetProperty(maData.mfForecastFor, EXC_CHPROP_EXTRAPOLATE_FORWARD);
+ aCurveProp.GetProperty(maData.mfForecastBack, EXC_CHPROP_EXTRAPOLATE_BACKWARD);
+ bool bIsForceIntercept = false;
+ aCurveProp.GetProperty(bIsForceIntercept, EXC_CHPROP_FORCE_INTERCEPT);
+ if (bIsForceIntercept)
+ aCurveProp.GetProperty(maData.mfIntercept, EXC_CHPROP_INTERCEPT_VALUE);
+
+ // line formatting
+ XclChDataPointPos aPointPos( nSeriesIdx );
+ mxDataFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, 0 );
+ mxDataFmt->ConvertLine( aCurveProp, EXC_CHOBJTYPE_TRENDLINE );
+
+ // #i83100# show equation and correlation coefficient
+ ScfPropertySet aEquationProp( xRegCurve->getEquationProperties() );
+ maData.mnShowEquation = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWEQUATION ) ? 1 : 0;
+ maData.mnShowRSquared = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWCORRELATION ) ? 1 : 0;
+
+ // #i83100# formatting of the equation text box
+ if( (maData.mnShowEquation != 0) || (maData.mnShowRSquared != 0) )
+ {
+ mxLabel = new XclExpChText( GetChRoot() );
+ mxLabel->ConvertTrendLineEquation( aEquationProp, aPointPos );
+ }
+
+ // missing features
+ // #i5085# manual trend line size
+ // #i34093# manual crossing point
+ return true;
+}
+
+void XclExpChSerTrendLine::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnLineType
+ << maData.mnOrder
+ << maData.mfIntercept
+ << maData.mnShowEquation
+ << maData.mnShowRSquared
+ << maData.mfForecastFor
+ << maData.mfForecastBack;
+}
+
+XclExpChSerErrorBar::XclExpChSerErrorBar( const XclExpChRoot& rRoot, sal_uInt8 nBarType ) :
+ XclExpRecord( EXC_ID_CHSERERRORBAR, 14 ),
+ XclExpChRoot( rRoot )
+{
+ maData.mnBarType = nBarType;
+}
+
+bool XclExpChSerErrorBar::Convert( XclExpChSourceLink& rValueLink, sal_uInt16& rnValueCount, const ScfPropertySet& rPropSet )
+{
+ sal_Int32 nBarStyle = 0;
+ bool bOk = rPropSet.GetProperty( nBarStyle, EXC_CHPROP_ERRORBARSTYLE );
+ if( bOk )
+ {
+ switch( nBarStyle )
+ {
+ case cssc::ErrorBarStyle::ABSOLUTE:
+ maData.mnSourceType = EXC_CHSERERR_FIXED;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR );
+ break;
+ case cssc::ErrorBarStyle::RELATIVE:
+ maData.mnSourceType = EXC_CHSERERR_PERCENT;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR );
+ break;
+ case cssc::ErrorBarStyle::STANDARD_DEVIATION:
+ maData.mnSourceType = EXC_CHSERERR_STDDEV;
+ rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_WEIGHT );
+ break;
+ case cssc::ErrorBarStyle::STANDARD_ERROR:
+ maData.mnSourceType = EXC_CHSERERR_STDERR;
+ break;
+ case cssc::ErrorBarStyle::FROM_DATA:
+ {
+ bOk = false;
+ maData.mnSourceType = EXC_CHSERERR_CUSTOM;
+ Reference< XDataSource > xDataSource( rPropSet.GetApiPropertySet(), UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ // find first sequence with current role
+ OUString aRole = XclChartHelper::GetErrorBarValuesRole( maData.mnBarType );
+ Reference< XDataSequence > xValueSeq;
+
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aCurrRole;
+ if( aValueProp.GetProperty( aCurrRole, EXC_CHPROP_ROLE ) && (aCurrRole == aRole) )
+ {
+ xValueSeq = xTmpValueSeq;
+ break;
+ }
+ }
+ if( xValueSeq.is() )
+ {
+ // #i86465# pass value count back to series
+ rnValueCount = maData.mnValueCount = rValueLink.ConvertDataSequence( xValueSeq, true );
+ bOk = maData.mnValueCount > 0;
+ }
+ }
+ }
+ break;
+ default:
+ bOk = false;
+ }
+ }
+ return bOk;
+}
+
+void XclExpChSerErrorBar::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnBarType
+ << maData.mnSourceType
+ << maData.mnLineEnd
+ << sal_uInt8( 1 ) // must be 1 to make line visible
+ << maData.mfValue
+ << maData.mnValueCount;
+}
+
+namespace {
+
+/** Returns the property set of the specified data point. */
+ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_Int32 nPointIdx )
+{
+ ScfPropertySet aPropSet;
+ try
+ {
+ aPropSet.Set( xDataSeries->getDataPointByIndex( nPointIdx ) );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "lclGetPointPropSet - no data point property set" );
+ }
+ return aPropSet;
+}
+
+} // namespace
+
+XclExpChSeries::XclExpChSeries( const XclExpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_SERIES, EXC_ID_CHSERIES, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 8 ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE ),
+ mnSeriesIdx( nSeriesIdx ),
+ mnParentIdx( EXC_CHSERIES_INVALID )
+{
+ // CHSOURCELINK records are always required, even if unused
+ mxTitleLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
+ mxValueLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_VALUES );
+ mxCategLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_CATEGORY );
+ if( GetBiff() == EXC_BIFF8 )
+ mxBubbleLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_BUBBLES );
+}
+
+bool XclExpChSeries::ConvertDataSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries,
+ const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx )
+{
+ bool bOk = false;
+ Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ Reference< XDataSequence > xYValueSeq, xTitleSeq, xXValueSeq, xBubbleSeq;
+
+ // find first sequence with role 'values-y'
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aRole;
+ if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) )
+ {
+ if( !xYValueSeq.is() && (aRole == EXC_CHPROP_ROLE_YVALUES) )
+ {
+ xYValueSeq = xTmpValueSeq;
+ if( !xTitleSeq.is() )
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ }
+ else if( !xXValueSeq.is() && !rTypeInfo.mbCategoryAxis && (aRole == EXC_CHPROP_ROLE_XVALUES) )
+ {
+ xXValueSeq = xTmpValueSeq;
+ }
+ else if( !xBubbleSeq.is() && (rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES) && (aRole == EXC_CHPROP_ROLE_SIZEVALUES) )
+ {
+ xBubbleSeq = xTmpValueSeq;
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ }
+ }
+ }
+
+ bOk = xYValueSeq.is();
+ if( bOk )
+ {
+ // chart type group index
+ mnGroupIdx = nGroupIdx;
+
+ // convert source links
+ maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true );
+ mxTitleLink->ConvertDataSequence( xTitleSeq, true );
+
+ // X values of XY charts
+ maData.mnCategCount = mxCategLink->ConvertDataSequence( xXValueSeq, false, maData.mnValueCount );
+
+ // size values of bubble charts
+ if( mxBubbleLink )
+ mxBubbleLink->ConvertDataSequence( xBubbleSeq, false, maData.mnValueCount );
+
+ // series formatting
+ XclChDataPointPos aPointPos( mnSeriesIdx );
+ ScfPropertySet aSeriesProp( xDataSeries );
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx );
+ mxSeriesFmt->ConvertDataSeries( aSeriesProp, rTypeInfo );
+
+ // trend lines
+ CreateTrendLines( xDataSeries );
+
+ // error bars
+ CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARX, EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
+ CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARY, EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
+
+ if( maData.mnValueCount > 0 )
+ {
+ const sal_Int32 nMaxPointCount = maData.mnValueCount;
+
+ /* #i91063# Create missing fill properties in pie/doughnut charts.
+ If freshly created (never saved to ODF), these charts show
+ varying point colors but do not return these points via API. */
+ if( xDiagram.is() && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE) )
+ {
+ Reference< XColorScheme > xColorScheme = xDiagram->getDefaultColorScheme();
+ if( xColorScheme.is() )
+ {
+ static const OUStringLiteral aFillStyleName = u"FillStyle";
+ static const OUStringLiteral aColorName = u"Color";
+ namespace cssd = ::com::sun::star::drawing;
+ for( sal_Int32 nPointIdx = 0; nPointIdx < nMaxPointCount; ++nPointIdx )
+ {
+ aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIdx );
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
+ // test that the point fill style is solid, but no color is set
+ cssd::FillStyle eFillStyle = cssd::FillStyle_NONE;
+ if( aPointProp.GetProperty( eFillStyle, aFillStyleName ) &&
+ (eFillStyle == cssd::FillStyle_SOLID) &&
+ !aPointProp.HasProperty( aColorName ) )
+ {
+ aPointProp.SetProperty( aColorName, xColorScheme->getColorByIndex( nPointIdx ) );
+ }
+ }
+ }
+ }
+
+ // data point formatting
+ Sequence< sal_Int32 > aPointIndexes;
+ if( aSeriesProp.GetProperty( aPointIndexes, EXC_CHPROP_ATTRIBDATAPOINTS ) && aPointIndexes.hasElements() )
+ {
+ for( const sal_Int32 nPointIndex : std::as_const(aPointIndexes) )
+ {
+ if (nPointIndex >= nMaxPointCount)
+ break;
+ aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIndex );
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIndex );
+ XclExpChDataFormatRef xPointFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx );
+ xPointFmt->ConvertDataSeries( aPointProp, rTypeInfo );
+ maPointFmts.AppendRecord( xPointFmt );
+ }
+ }
+ }
+ }
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertStockSeries( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx, bool bCloseSymbol )
+{
+ bool bOk = false;
+ Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY );
+ if( xDataSource.is() )
+ {
+ Reference< XDataSequence > xYValueSeq, xTitleSeq;
+
+ // find first sequence with passed role
+ const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
+ for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
+ {
+ Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
+ ScfPropertySet aValueProp( xTmpValueSeq );
+ OUString aRole;
+ if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) && (aRole == rValueRole) )
+ {
+ xYValueSeq = xTmpValueSeq;
+ xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
+ break;
+ }
+ }
+
+ bOk = xYValueSeq.is();
+ if( bOk )
+ {
+ // chart type group index
+ mnGroupIdx = nGroupIdx;
+ // convert source links
+ maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true );
+ mxTitleLink->ConvertDataSequence( xTitleSeq, true );
+ // series formatting
+ ScfPropertySet aSeriesProp( xDataSeries );
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), nFormatIdx );
+ mxSeriesFmt->ConvertStockSeries( aSeriesProp, bCloseSymbol );
+ }
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertTrendLine( const XclExpChSeries& rParent, Reference< XRegressionCurve > const & xRegCurve )
+{
+ InitFromParent( rParent );
+
+ mxTrendLine = new XclExpChSerTrendLine( GetChRoot() );
+ bool bOk = mxTrendLine->Convert( xRegCurve, mnSeriesIdx );
+ if( bOk )
+ {
+ OUString aName;
+ ScfPropertySet aProperties( xRegCurve );
+ aProperties.GetProperty(aName, EXC_CHPROP_CURVENAME);
+ mxTitleLink->ConvertString(aName);
+
+ mxSeriesFmt = mxTrendLine->GetDataFormat();
+ GetChartData().SetDataLabel( mxTrendLine->GetDataLabel() );
+ }
+ return bOk;
+}
+
+bool XclExpChSeries::ConvertErrorBar( const XclExpChSeries& rParent, const ScfPropertySet& rPropSet, sal_uInt8 nBarId )
+{
+ InitFromParent( rParent );
+ // error bar settings
+ mxErrorBar = new XclExpChSerErrorBar( GetChRoot(), nBarId );
+ bool bOk = mxErrorBar->Convert( *mxValueLink, maData.mnValueCount, rPropSet );
+ if( bOk )
+ {
+ // error bar formatting
+ mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), 0 );
+ mxSeriesFmt->ConvertLine( rPropSet, EXC_CHOBJTYPE_ERRORBAR );
+ }
+ return bOk;
+}
+
+void XclExpChSeries::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
+{
+ if( xCategSeq.is() )
+ maData.mnCategCount = mxCategLink->ConvertDataSequence( xCategSeq->getValues(), false );
+}
+
+void XclExpChSeries::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxTitleLink );
+ lclSaveRecord( rStrm, mxValueLink );
+ lclSaveRecord( rStrm, mxCategLink );
+ lclSaveRecord( rStrm, mxBubbleLink );
+ lclSaveRecord( rStrm, mxSeriesFmt );
+ maPointFmts.Save( rStrm );
+ if( mnGroupIdx != EXC_CHSERGROUP_NONE )
+ XclExpUInt16Record( EXC_ID_CHSERGROUP, mnGroupIdx ).Save( rStrm );
+ if( mnParentIdx != EXC_CHSERIES_INVALID )
+ XclExpUInt16Record( EXC_ID_CHSERPARENT, mnParentIdx ).Save( rStrm );
+ lclSaveRecord( rStrm, mxTrendLine );
+ lclSaveRecord( rStrm, mxErrorBar );
+}
+
+void XclExpChSeries::InitFromParent( const XclExpChSeries& rParent )
+{
+ // index to parent series is stored 1-based
+ mnParentIdx = rParent.mnSeriesIdx + 1;
+ /* #i86465# MSO2007 SP1 expects correct point counts in child series
+ (there was no problem in Excel2003 or Excel2007 without SP1...) */
+ maData.mnCategCount = rParent.maData.mnCategCount;
+ maData.mnValueCount = rParent.maData.mnValueCount;
+}
+
+void XclExpChSeries::CreateTrendLines( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries )
+{
+ Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
+ if( xRegCurveCont.is() )
+ {
+ const Sequence< Reference< XRegressionCurve > > aRegCurveSeq = xRegCurveCont->getRegressionCurves();
+ for( const Reference< XRegressionCurve >& rRegCurve : aRegCurveSeq )
+ {
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries && !xSeries->ConvertTrendLine( *this, rRegCurve ) )
+ GetChartData().RemoveLastSeries();
+ }
+ }
+}
+
+void XclExpChSeries::CreateErrorBars( const ScfPropertySet& rPropSet,
+ const OUString& rBarPropName, sal_uInt8 nPosBarId, sal_uInt8 nNegBarId )
+{
+ Reference< XPropertySet > xErrorBar;
+ if( rPropSet.GetProperty( xErrorBar, rBarPropName ) && xErrorBar.is() )
+ {
+ ScfPropertySet aErrorProp( xErrorBar );
+ CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWPOSITIVEERROR, nPosBarId );
+ CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWNEGATIVEERROR, nNegBarId );
+ }
+}
+
+void XclExpChSeries::CreateErrorBar( const ScfPropertySet& rPropSet,
+ const OUString& rShowPropName, sal_uInt8 nBarId )
+{
+ if( rPropSet.GetBoolProperty( rShowPropName ) )
+ {
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries && !xSeries->ConvertErrorBar( *this, rPropSet, nBarId ) )
+ GetChartData().RemoveLastSeries();
+ }
+}
+
+void XclExpChSeries::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnCategType << maData.mnValueType << maData.mnCategCount << maData.mnValueCount;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnBubbleType << maData.mnBubbleCount;
+}
+
+// Chart type groups ==========================================================
+
+XclExpChType::XclExpChType( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHUNKNOWN ),
+ XclExpChRoot( rRoot ),
+ maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
+{
+}
+
+void XclExpChType::Convert( Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool bSwappedAxesSet, bool bHasXLabels )
+{
+ if( !xChartType.is() )
+ return;
+
+ maTypeInfo = GetChartTypeInfo( xChartType->getChartType() );
+ // special handling for some chart types
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_BAR:
+ {
+ maTypeInfo = GetChartTypeInfo( bSwappedAxesSet ? EXC_CHTYPEID_HORBAR : EXC_CHTYPEID_BAR );
+ ::set_flag( maData.mnFlags, EXC_CHBAR_HORIZONTAL, bSwappedAxesSet );
+ ScfPropertySet aTypeProp( xChartType );
+ Sequence< sal_Int32 > aInt32Seq;
+ maData.mnOverlap = 0;
+ if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_OVERLAPSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
+ maData.mnOverlap = limit_cast< sal_Int16 >( -aInt32Seq[ nApiAxesSetIdx ], -100, 100 );
+ maData.mnGap = 150;
+ if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_GAPWIDTHSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
+ maData.mnGap = limit_cast< sal_uInt16 >( aInt32Seq[ nApiAxesSetIdx ], 0, 500 );
+ }
+ break;
+ case EXC_CHTYPECATEG_RADAR:
+ ::set_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS, bHasXLabels );
+ break;
+ case EXC_CHTYPECATEG_PIE:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ bool bDonut = aTypeProp.GetBoolProperty( EXC_CHPROP_USERINGS );
+ maTypeInfo = GetChartTypeInfo( bDonut ? EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
+ maData.mnPieHole = bDonut ? 50 : 0;
+ // #i85166# starting angle of first pie slice
+ ScfPropertySet aDiaProp( xDiagram );
+ maData.mnRotation = XclExpChRoot::ConvertPieRotation( aDiaProp );
+ }
+ break;
+ case EXC_CHTYPECATEG_SCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maData.mnFlags, EXC_CHSCATTER_BUBBLES, maTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES );
+ break;
+ default:;
+ }
+ SetRecId( maTypeInfo.mnRecId );
+}
+
+void XclExpChType::SetStacked( bool bPercent )
+{
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ ::set_flag( maData.mnFlags, EXC_CHLINE_STACKED );
+ ::set_flag( maData.mnFlags, EXC_CHLINE_PERCENT, bPercent );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ ::set_flag( maData.mnFlags, EXC_CHBAR_STACKED );
+ ::set_flag( maData.mnFlags, EXC_CHBAR_PERCENT, bPercent );
+ maData.mnOverlap = -100;
+ break;
+ default:;
+ }
+}
+
+void XclExpChType::WriteBody( XclExpStream& rStrm )
+{
+ switch( GetRecId() )
+ {
+ case EXC_ID_CHBAR:
+ rStrm << maData.mnOverlap << maData.mnGap << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHLINE:
+ case EXC_ID_CHAREA:
+ case EXC_ID_CHRADARLINE:
+ case EXC_ID_CHRADARAREA:
+ rStrm << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHPIE:
+ rStrm << maData.mnRotation << maData.mnPieHole;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnFlags;
+ break;
+
+ case EXC_ID_CHSCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << maData.mnBubbleSize << maData.mnBubbleType << maData.mnFlags;
+ break;
+
+ default:
+ OSL_FAIL( "XclExpChType::WriteBody - unknown chart type" );
+ }
+}
+
+XclExpChChart3d::XclExpChChart3d() :
+ XclExpRecord( EXC_ID_CHCHART3D, 14 )
+{
+}
+
+void XclExpChChart3d::Convert( const ScfPropertySet& rPropSet, bool b3dWallChart )
+{
+ sal_Int32 nRotationY = 0;
+ rPropSet.GetProperty( nRotationY, EXC_CHPROP_ROTATIONVERTICAL );
+ sal_Int32 nRotationX = 0;
+ rPropSet.GetProperty( nRotationX, EXC_CHPROP_ROTATIONHORIZONTAL );
+ sal_Int32 nPerspective = 15;
+ rPropSet.GetProperty( nPerspective, EXC_CHPROP_PERSPECTIVE );
+
+ if( b3dWallChart )
+ {
+ // Y rotation (Excel [0..359], Chart2 [-179,180])
+ if( nRotationY < 0 ) nRotationY += 360;
+ maData.mnRotation = static_cast< sal_uInt16 >( nRotationY );
+ // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
+ maData.mnElevation = limit_cast< sal_Int16 >( nRotationX, -90, 90 );
+ // perspective (Excel and Chart2 [0,100])
+ maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 );
+ // flags
+ maData.mnFlags = 0;
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D, !rPropSet.GetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES ) );
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_AUTOHEIGHT );
+ ::set_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS );
+ }
+ else
+ {
+ // Y rotation not used in pie charts, but 'first pie slice angle'
+ maData.mnRotation = XclExpChRoot::ConvertPieRotation( rPropSet );
+ // X rotation a.k.a. elevation (map Chart2 [-80,-10] to Excel [10..80])
+ maData.mnElevation = limit_cast< sal_Int16 >( (nRotationX + 270) % 180, 10, 80 );
+ // perspective (Excel and Chart2 [0,100])
+ maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 );
+ // flags
+ maData.mnFlags = 0;
+ }
+}
+
+void XclExpChChart3d::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnRotation
+ << maData.mnElevation
+ << maData.mnEyeDist
+ << maData.mnRelHeight
+ << maData.mnRelDepth
+ << maData.mnDepthGap
+ << maData.mnFlags;
+}
+
+XclExpChLegend::XclExpChLegend( const XclExpChRoot& rRoot ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_LEGEND, EXC_ID_CHLEGEND, 20 )
+{
+}
+
+void XclExpChLegend::Convert( const ScfPropertySet& rPropSet )
+{
+ // frame properties
+ mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_LEGEND );
+ // text properties
+ mxText = new XclExpChText( GetChRoot() );
+ mxText->ConvertLegend( rPropSet );
+
+ // legend position and size
+ Any aRelPosAny, aRelSizeAny;
+ rPropSet.GetAnyProperty( aRelPosAny, EXC_CHPROP_RELATIVEPOSITION );
+ rPropSet.GetAnyProperty( aRelSizeAny, EXC_CHPROP_RELATIVESIZE );
+ cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ rPropSet.GetProperty( eApiExpand, EXC_CHPROP_EXPANSION );
+ if( aRelPosAny.has< RelativePosition >() || ((eApiExpand == cssc::ChartLegendExpansion_CUSTOM) && aRelSizeAny.has< RelativeSize >()) )
+ {
+ try
+ {
+ /* The 'RelativePosition' or 'RelativeSize' properties are used as
+ indicator of manually changed legend position/size, but due to
+ the different anchor modes used by this property (in the
+ RelativePosition.Anchor member) it cannot be used to calculate
+ the position easily. For this, the Chart1 API will be used
+ instead. */
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< XShape > xChart1Legend( xChart1Doc->getLegend(), UNO_SET_THROW );
+ // coordinates in CHLEGEND record written but not used by Excel
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_CHARTSIZE );
+ XclChFramePos& rFramePos = mxFramePos->GetFramePosData();
+ rFramePos.mnTLMode = EXC_CHFRAMEPOS_CHARTSIZE;
+ css::awt::Point aLegendPos = xChart1Legend->getPosition();
+ rFramePos.maRect.mnX = maData.maRect.mnX = CalcChartXFromHmm( aLegendPos.X );
+ rFramePos.maRect.mnY = maData.maRect.mnY = CalcChartYFromHmm( aLegendPos.Y );
+ // legend size, Excel expects points in CHFRAMEPOS record
+ rFramePos.mnBRMode = EXC_CHFRAMEPOS_ABSSIZE_POINTS;
+ css::awt::Size aLegendSize = xChart1Legend->getSize();
+ rFramePos.maRect.mnWidth = o3tl::convert(aLegendSize.Width, o3tl::Length::mm100, o3tl::Length::pt);
+ rFramePos.maRect.mnHeight = o3tl::convert(aLegendSize.Height, o3tl::Length::mm100, o3tl::Length::pt);
+ maData.maRect.mnWidth = CalcChartXFromHmm( aLegendSize.Width );
+ maData.maRect.mnHeight = CalcChartYFromHmm( aLegendSize.Height );
+ eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ // manual legend position implies manual plot area
+ GetChartData().SetManualPlotArea();
+ maData.mnDockMode = EXC_CHLEGEND_NOTDOCKED;
+ // a CHFRAME record with cleared auto flags is needed
+ if( !mxFrame )
+ mxFrame = new XclExpChFrame( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ mxFrame->SetAutoFlags( false, false );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclExpChLegend::Convert - cannot get legend shape" );
+ maData.mnDockMode = EXC_CHLEGEND_RIGHT;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ }
+ }
+ else
+ {
+ cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
+ rPropSet.GetProperty( eApiPos, EXC_CHPROP_ANCHORPOSITION );
+ switch( eApiPos )
+ {
+ case cssc2::LegendPosition_LINE_START: maData.mnDockMode = EXC_CHLEGEND_LEFT; break;
+ case cssc2::LegendPosition_LINE_END: maData.mnDockMode = EXC_CHLEGEND_RIGHT; break;
+ case cssc2::LegendPosition_PAGE_START: maData.mnDockMode = EXC_CHLEGEND_TOP; break;
+ case cssc2::LegendPosition_PAGE_END: maData.mnDockMode = EXC_CHLEGEND_BOTTOM; break;
+ default:
+ OSL_FAIL( "XclExpChLegend::Convert - unrecognized legend position" );
+ maData.mnDockMode = EXC_CHLEGEND_RIGHT;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ }
+ }
+ ::set_flag( maData.mnFlags, EXC_CHLEGEND_STACKED, eApiExpand == cssc::ChartLegendExpansion_HIGH );
+
+ // other flags
+ ::set_flag( maData.mnFlags, EXC_CHLEGEND_AUTOSERIES );
+ const sal_uInt16 nAutoFlags = EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY;
+ ::set_flag( maData.mnFlags, nAutoFlags, maData.mnDockMode != EXC_CHLEGEND_NOTDOCKED );
+}
+
+void XclExpChLegend::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxFramePos );
+ lclSaveRecord( rStrm, mxText );
+ lclSaveRecord( rStrm, mxFrame );
+}
+
+void XclExpChLegend::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.maRect << maData.mnDockMode << maData.mnSpacing << maData.mnFlags;
+}
+
+XclExpChDropBar::XclExpChDropBar( const XclExpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DROPBAR, EXC_ID_CHDROPBAR, 2 ),
+ meObjType( eObjType )
+{
+}
+
+void XclExpChDropBar::Convert( const ScfPropertySet& rPropSet )
+{
+ if( rPropSet.Is() )
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType );
+ else
+ SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, true );
+}
+
+void XclExpChDropBar::WriteSubRecords( XclExpStream& rStrm )
+{
+ WriteFrameRecords( rStrm );
+}
+
+void XclExpChDropBar::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(100); // Distance between bars (CHDROPBAR record).
+}
+
+XclExpChTypeGroup::XclExpChTypeGroup( const XclExpChRoot& rRoot, sal_uInt16 nGroupIdx ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TYPEGROUP, EXC_ID_CHTYPEGROUP, 20 ),
+ maType( rRoot ),
+ maTypeInfo( maType.GetTypeInfo() )
+{
+ maData.mnGroupIdx = nGroupIdx;
+}
+
+void XclExpChTypeGroup::ConvertType(
+ Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool b3dChart, bool bSwappedAxesSet, bool bHasXLabels )
+{
+ // chart type settings
+ maType.Convert( xDiagram, xChartType, nApiAxesSetIdx, bSwappedAxesSet, bHasXLabels );
+
+ // spline - TODO: get from single series (#i66858#)
+ ScfPropertySet aTypeProp( xChartType );
+ cssc2::CurveStyle eCurveStyle;
+ bool bSpline = aTypeProp.GetProperty( eCurveStyle, EXC_CHPROP_CURVESTYLE ) &&
+ (eCurveStyle != cssc2::CurveStyle_LINES);
+
+ // extended type info
+ maTypeInfo.Set( maType.GetTypeInfo(), b3dChart, bSpline );
+
+ // 3d chart settings
+ if( maTypeInfo.mb3dChart ) // only true, if Excel chart supports 3d mode
+ {
+ mxChart3d = new XclExpChChart3d();
+ ScfPropertySet aDiaProp( xDiagram );
+ mxChart3d->Convert( aDiaProp, Is3dWallChart() );
+ }
+}
+
+void XclExpChTypeGroup::ConvertSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
+ sal_Int32 nGroupAxesSetIdx, bool bPercent, bool bConnectBars )
+{
+ Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
+ if( !xSeriesCont.is() )
+ return;
+
+ std::vector< Reference< XDataSeries > > aSeriesVec;
+
+ // copy data series attached to the current axes set to the vector
+ const Sequence< Reference< XDataSeries > > aSeriesSeq = xSeriesCont->getDataSeries();
+ for( const Reference< XDataSeries >& rSeries : aSeriesSeq )
+ {
+ ScfPropertySet aSeriesProp( rSeries );
+ sal_Int32 nSeriesAxesSetIdx(0);
+ if( aSeriesProp.GetProperty( nSeriesAxesSetIdx, EXC_CHPROP_ATTAXISINDEX ) && (nSeriesAxesSetIdx == nGroupAxesSetIdx) )
+ aSeriesVec.push_back( rSeries );
+ }
+
+ // Are there any series in the current axes set?
+ if( aSeriesVec.empty() )
+ return;
+
+ // stacking direction (stacked/percent/deep 3d) from first series
+ ScfPropertySet aSeriesProp( aSeriesVec.front() );
+ cssc2::StackingDirection eStacking;
+ if( !aSeriesProp.GetProperty( eStacking, EXC_CHPROP_STACKINGDIR ) )
+ eStacking = cssc2::StackingDirection_NO_STACKING;
+
+ // stacked or percent chart
+ if( maTypeInfo.mbSupportsStacking && (eStacking == cssc2::StackingDirection_Y_STACKING) )
+ {
+ // percent overrides simple stacking
+ maType.SetStacked( bPercent );
+
+ // connected data points (only in stacked bar charts)
+ if (bConnectBars && (maTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR))
+ {
+ sal_uInt16 nKey = EXC_CHCHARTLINE_CONNECT;
+ m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
+ }
+ }
+ else
+ {
+ // reverse series order for some unstacked 2D chart types
+ if( maTypeInfo.mbReverseSeries && !Is3dChart() )
+ ::std::reverse( aSeriesVec.begin(), aSeriesVec.end() );
+ }
+
+ // deep 3d chart or clustered 3d chart (stacked is not clustered)
+ if( (eStacking == cssc2::StackingDirection_NO_STACKING) && Is3dWallChart() )
+ mxChart3d->SetClustered();
+
+ // varied point colors
+ ::set_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS, aSeriesProp.GetBoolProperty( EXC_CHPROP_VARYCOLORSBY ) );
+
+ // process all series
+ for( const auto& rxSeries : aSeriesVec )
+ {
+ // create Excel series object, stock charts need special processing
+ if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
+ CreateAllStockSeries( xChartType, rxSeries );
+ else
+ CreateDataSeries( xDiagram, rxSeries );
+ }
+}
+
+void XclExpChTypeGroup::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
+{
+ for( size_t nIdx = 0, nSize = maSeries.GetSize(); nIdx < nSize; ++nIdx )
+ maSeries.GetRecord( nIdx )->ConvertCategSequence( xCategSeq );
+}
+
+void XclExpChTypeGroup::ConvertLegend( const ScfPropertySet& rPropSet )
+{
+ if( rPropSet.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ {
+ mxLegend = new XclExpChLegend( GetChRoot() );
+ mxLegend->Convert( rPropSet );
+ }
+}
+
+void XclExpChTypeGroup::WriteSubRecords( XclExpStream& rStrm )
+{
+ maType.Save( rStrm );
+ lclSaveRecord( rStrm, mxChart3d );
+ lclSaveRecord( rStrm, mxLegend );
+ lclSaveRecord( rStrm, mxUpBar );
+ lclSaveRecord( rStrm, mxDownBar );
+ for (auto const& it : m_ChartLines)
+ {
+ lclSaveRecord( rStrm, it.second.get(), EXC_ID_CHCHARTLINE, it.first );
+ }
+}
+
+sal_uInt16 XclExpChTypeGroup::GetFreeFormatIdx() const
+{
+ return static_cast< sal_uInt16 >( maSeries.GetSize() );
+}
+
+void XclExpChTypeGroup::CreateDataSeries(
+ Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries )
+{
+ // let chart create series object with correct series index
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries )
+ {
+ if( xSeries->ConvertDataSeries( xDiagram, xDataSeries, maTypeInfo, GetGroupIdx(), GetFreeFormatIdx() ) )
+ maSeries.AppendRecord( xSeries );
+ else
+ GetChartData().RemoveLastSeries();
+ }
+}
+
+void XclExpChTypeGroup::CreateAllStockSeries(
+ Reference< XChartType > const & xChartType, Reference< XDataSeries > const & xDataSeries )
+{
+ // create existing series objects
+ bool bHasOpen = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_OPENVALUES, false );
+ bool bHasHigh = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_HIGHVALUES, false );
+ bool bHasLow = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_LOWVALUES, false );
+ bool bHasClose = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_CLOSEVALUES, !bHasOpen );
+
+ // formatting of special stock chart elements
+ ScfPropertySet aTypeProp( xChartType );
+ // hi-lo lines
+ if( bHasHigh && bHasLow && aTypeProp.GetBoolProperty( EXC_CHPROP_SHOWHIGHLOW ) )
+ {
+ ScfPropertySet aSeriesProp( xDataSeries );
+ XclExpChLineFormatRef xLineFmt = new XclExpChLineFormat( GetChRoot() );
+ xLineFmt->Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
+ sal_uInt16 nKey = EXC_CHCHARTLINE_HILO;
+ m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
+ }
+ // dropbars
+ if( !(bHasOpen && bHasClose) )
+ return;
+
+ // dropbar type is dependent on position in the file - always create both
+ Reference< XPropertySet > xWhitePropSet, xBlackPropSet;
+ // white dropbar format
+ aTypeProp.GetProperty( xWhitePropSet, EXC_CHPROP_WHITEDAY );
+ ScfPropertySet aWhiteProp( xWhitePropSet );
+ mxUpBar = new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_WHITEDROPBAR );
+ mxUpBar->Convert( aWhiteProp );
+ // black dropbar format
+ aTypeProp.GetProperty( xBlackPropSet, EXC_CHPROP_BLACKDAY );
+ ScfPropertySet aBlackProp( xBlackPropSet );
+ mxDownBar = new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_BLACKDROPBAR );
+ mxDownBar->Convert( aBlackProp );
+}
+
+bool XclExpChTypeGroup::CreateStockSeries( Reference< XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole, bool bCloseSymbol )
+{
+ bool bOk = false;
+ // let chart create series object with correct series index
+ XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
+ if( xSeries )
+ {
+ bOk = xSeries->ConvertStockSeries( xDataSeries,
+ rValueRole, GetGroupIdx(), GetFreeFormatIdx(), bCloseSymbol );
+ if( bOk )
+ maSeries.AppendRecord( xSeries );
+ else
+ GetChartData().RemoveLastSeries();
+ }
+ return bOk;
+}
+
+void XclExpChTypeGroup::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.WriteZeroBytes( 16 );
+ rStrm << maData.mnFlags << maData.mnGroupIdx;
+}
+
+// Axes =======================================================================
+
+XclExpChLabelRange::XclExpChLabelRange( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHLABELRANGE, 8 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChLabelRange::Convert( const ScaleData& rScaleData, const ScfPropertySet& rChart1Axis, bool bMirrorOrient )
+{
+ /* Base time unit (using the property 'ExplicitTimeIncrement' from the old
+ chart API allows to detect axis type (date axis, if property exists),
+ and to receive the base time unit currently used in case the base time
+ unit is set to 'automatic'. */
+ cssc::TimeIncrement aTimeIncrement;
+ if( rChart1Axis.GetProperty( aTimeIncrement, EXC_CHPROP_EXPTIMEINCREMENT ) )
+ {
+ // property exists -> this is a date axis currently
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
+
+ // automatic base time unit, if the UNO Any 'rScaleData.TimeIncrement.TimeResolution' does not contain a valid value...
+ bool bAutoBase = !rScaleData.TimeIncrement.TimeResolution.has< cssc::TimeIncrement >();
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE, bAutoBase );
+
+ // ...but get the current base time unit from the property of the old chart API
+ sal_Int32 nApiTimeUnit = 0;
+ bool bValidBaseUnit = aTimeIncrement.TimeResolution >>= nApiTimeUnit;
+ OSL_ENSURE( bValidBaseUnit, "XclExpChLabelRange::Convert - cannot get base time unit" );
+ maDateData.mnBaseUnit = bValidBaseUnit ? lclGetTimeUnit( nApiTimeUnit ) : EXC_CHDATERANGE_DAYS;
+
+ /* Min/max values depend on base time unit, they specify the number of
+ days, months, or years starting from null date. */
+ bool bAutoMin = lclConvertTimeValue( GetRoot(), maDateData.mnMinDate, rScaleData.Minimum, maDateData.mnBaseUnit );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN, bAutoMin );
+ bool bAutoMax = lclConvertTimeValue( GetRoot(), maDateData.mnMaxDate, rScaleData.Maximum, maDateData.mnBaseUnit );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX, bAutoMax );
+ }
+
+ // automatic axis type detection
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE, rScaleData.AutoDateAxis );
+
+ // increment
+ bool bAutoMajor = lclConvertTimeInterval( maDateData.mnMajorStep, maDateData.mnMajorUnit, rScaleData.TimeIncrement.MajorTimeInterval );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR, bAutoMajor );
+ bool bAutoMinor = lclConvertTimeInterval( maDateData.mnMinorStep, maDateData.mnMinorUnit, rScaleData.TimeIncrement.MinorTimeInterval );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR, bAutoMinor );
+
+ // origin
+ double fOrigin = 0.0;
+ if( !lclIsAutoAnyOrGetValue( fOrigin, rScaleData.Origin ) )
+ maLabelData.mnCross = limit_cast< sal_uInt16 >( fOrigin, 1, 31999 );
+
+ // reverse order
+ if( (rScaleData.Orientation == cssc2::AxisOrientation_REVERSE) != bMirrorOrient )
+ ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE );
+}
+
+void XclExpChLabelRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
+{
+ cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
+ rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION );
+ double fCrossingPos = 1.0;
+ rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE );
+
+ bool bDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
+ switch( eAxisPos )
+ {
+ case cssc::ChartAxisPosition_ZERO:
+ case cssc::ChartAxisPosition_START:
+ maLabelData.mnCross = 1;
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ break;
+ case cssc::ChartAxisPosition_END:
+ ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_MAXCROSS );
+ break;
+ case cssc::ChartAxisPosition_VALUE:
+ maLabelData.mnCross = limit_cast< sal_uInt16 >( fCrossingPos, 1, 31999 );
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS, false );
+ if( bDateAxis )
+ maDateData.mnCross = lclGetTimeValue( GetRoot(), fCrossingPos, maDateData.mnBaseUnit );
+ break;
+ default:
+ maLabelData.mnCross = 1;
+ ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ }
+}
+
+void XclExpChLabelRange::Save( XclExpStream& rStrm )
+{
+ // the CHLABELRANGE record
+ XclExpRecord::Save( rStrm );
+
+ // the CHDATERANGE record with date axis settings (BIFF8 only)
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ rStrm.StartRecord( EXC_ID_CHDATERANGE, 18 );
+ rStrm << maDateData.mnMinDate
+ << maDateData.mnMaxDate
+ << maDateData.mnMajorStep
+ << maDateData.mnMajorUnit
+ << maDateData.mnMinorStep
+ << maDateData.mnMinorUnit
+ << maDateData.mnBaseUnit
+ << maDateData.mnCross
+ << maDateData.mnFlags;
+ rStrm.EndRecord();
+}
+
+void XclExpChLabelRange::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maLabelData.mnCross << maLabelData.mnLabelFreq << maLabelData.mnTickFreq << maLabelData.mnFlags;
+}
+
+XclExpChValueRange::XclExpChValueRange( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHVALUERANGE, 42 ),
+ XclExpChRoot( rRoot )
+{
+}
+
+void XclExpChValueRange::Convert( const ScaleData& rScaleData )
+{
+ // scaling algorithm
+ bool bLogScale = ScfApiHelper::GetServiceName( rScaleData.Scaling ) == "com.sun.star.chart2.LogarithmicScaling";
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, bLogScale );
+
+ // min/max
+ bool bAutoMin = lclIsAutoAnyOrGetScaledValue( maData.mfMin, rScaleData.Minimum, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN, bAutoMin );
+ bool bAutoMax = lclIsAutoAnyOrGetScaledValue( maData.mfMax, rScaleData.Maximum, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX, bAutoMax );
+
+ // origin
+ bool bAutoCross = lclIsAutoAnyOrGetScaledValue( maData.mfCross, rScaleData.Origin, bLogScale );
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, bAutoCross );
+
+ // major increment
+ const IncrementData& rIncrementData = rScaleData.IncrementData;
+ const bool bAutoMajor = lclIsAutoAnyOrGetValue( maData.mfMajorStep, rIncrementData.Distance ) || (maData.mfMajorStep <= 0.0);
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR, bAutoMajor );
+ // minor increment
+ const Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
+ sal_Int32 nCount = 0;
+
+ // tdf#114168 If IntervalCount is 5, then enable automatic minor calculation.
+ // During import, if minorUnit is set and majorUnit not, then it is impossible
+ // to calculate IntervalCount.
+ const bool bAutoMinor = bLogScale || bAutoMajor || !rSubIncrementSeq.hasElements() ||
+ lclIsAutoAnyOrGetValue( nCount, rSubIncrementSeq[ 0 ].IntervalCount ) || (nCount < 1) || (nCount == 5);
+
+ if( maData.mfMajorStep && !bAutoMinor )
+ maData.mfMinorStep = maData.mfMajorStep / nCount;
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR, bAutoMinor );
+
+ // reverse order
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE, rScaleData.Orientation == cssc2::AxisOrientation_REVERSE );
+}
+
+void XclExpChValueRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
+{
+ cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
+ double fCrossingPos = 0.0;
+ if( !(rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ) && rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE )) )
+ return;
+
+ switch( eAxisPos )
+ {
+ case cssc::ChartAxisPosition_ZERO:
+ case cssc::ChartAxisPosition_START:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ break;
+ case cssc::ChartAxisPosition_END:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS );
+ break;
+ case cssc::ChartAxisPosition_VALUE:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, false );
+ maData.mfCross = ::get_flagvalue< double >( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, log( fCrossingPos ) / log( 10.0 ), fCrossingPos );
+ break;
+ default:
+ ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ }
+}
+
+void XclExpChValueRange::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mfMin
+ << maData.mfMax
+ << maData.mfMajorStep
+ << maData.mfMinorStep
+ << maData.mfCross
+ << maData.mnFlags;
+}
+
+namespace {
+
+sal_uInt8 lclGetXclTickPos( sal_Int32 nApiTickmarks )
+{
+ using namespace cssc2::TickmarkStyle;
+ sal_uInt8 nXclTickPos = 0;
+ ::set_flag( nXclTickPos, EXC_CHTICK_INSIDE, ::get_flag( nApiTickmarks, INNER ) );
+ ::set_flag( nXclTickPos, EXC_CHTICK_OUTSIDE, ::get_flag( nApiTickmarks, OUTER ) );
+ return nXclTickPos;
+}
+
+} // namespace
+
+XclExpChTick::XclExpChTick( const XclExpChRoot& rRoot ) :
+ XclExpRecord( EXC_ID_CHTICK, (rRoot.GetBiff() == EXC_BIFF8) ? 30 : 26 ),
+ XclExpChRoot( rRoot ),
+ mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
+{
+}
+
+void XclExpChTick::Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType )
+{
+ // tick mark style
+ sal_Int32 nApiTickmarks = 0;
+ if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MAJORTICKS ) )
+ maData.mnMajor = lclGetXclTickPos( nApiTickmarks );
+ if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MINORTICKS ) )
+ maData.mnMinor = lclGetXclTickPos( nApiTickmarks );
+
+ // axis labels
+ if( (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) && (nAxisType == EXC_CHAXIS_X) )
+ {
+ /* Radar charts disable their category labels via chart type, not via
+ axis, and axis labels are always 'near axis'. */
+ maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ else if( !rPropSet.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ) )
+ {
+ // no labels
+ maData.mnLabelPos = EXC_CHTICK_NOLABEL;
+ }
+ else if( rTypeInfo.mb3dChart && (nAxisType == EXC_CHAXIS_Y) )
+ {
+ // Excel expects 'near axis' at Y axes in 3D charts
+ maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ else
+ {
+ cssc::ChartAxisLabelPosition eApiLabelPos = cssc::ChartAxisLabelPosition_NEAR_AXIS;
+ rPropSet.GetProperty( eApiLabelPos, EXC_CHPROP_LABELPOSITION );
+ switch( eApiLabelPos )
+ {
+ case cssc::ChartAxisLabelPosition_NEAR_AXIS:
+ case cssc::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE: maData.mnLabelPos = EXC_CHTICK_NEXT; break;
+ case cssc::ChartAxisLabelPosition_OUTSIDE_START: maData.mnLabelPos = EXC_CHTICK_LOW; break;
+ case cssc::ChartAxisLabelPosition_OUTSIDE_END: maData.mnLabelPos = EXC_CHTICK_HIGH; break;
+ default: maData.mnLabelPos = EXC_CHTICK_NEXT;
+ }
+ }
+}
+
+void XclExpChTick::SetFontColor( const Color& rColor, sal_uInt32 nColorId )
+{
+ maData.maTextColor = rColor;
+ ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR, rColor == COL_AUTO );
+ mnTextColorId = nColorId;
+}
+
+void XclExpChTick::SetRotation( sal_uInt16 nRotation )
+{
+ maData.mnRotation = nRotation;
+ ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOROT, false );
+ ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 2, 3 );
+}
+
+void XclExpChTick::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnMajor
+ << maData.mnMinor
+ << maData.mnLabelPos
+ << maData.mnBackMode;
+ rStrm.WriteZeroBytes( 16 );
+ rStrm << maData.maTextColor
+ << maData.mnFlags;
+ if( GetBiff() == EXC_BIFF8 )
+ rStrm << GetPalette().GetColorIndex( mnTextColorId ) << maData.mnRotation;
+}
+
+namespace {
+
+/** Returns an API axis object from the passed coordinate system. */
+Reference< XAxis > lclGetApiAxis( Reference< XCoordinateSystem > const & xCoordSystem,
+ sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
+{
+ Reference< XAxis > xAxis;
+ if( (nApiAxisDim >= 0) && xCoordSystem.is() ) try
+ {
+ xAxis = xCoordSystem->getAxisByDimension( nApiAxisDim, nApiAxesSetIdx );
+ }
+ catch( Exception& )
+ {
+ }
+ return xAxis;
+}
+
+Reference< cssc::XAxis > lclGetApiChart1Axis( Reference< XChartDocument > const & xChartDoc,
+ sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
+{
+ Reference< cssc::XAxis > xChart1Axis;
+ try
+ {
+ Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY_THROW );
+ Reference< cssc::XAxisSupplier > xChart1AxisSupp( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
+ switch( nApiAxesSetIdx )
+ {
+ case EXC_CHART_AXESSET_PRIMARY:
+ xChart1Axis = xChart1AxisSupp->getAxis( nApiAxisDim );
+ break;
+ case EXC_CHART_AXESSET_SECONDARY:
+ xChart1Axis = xChart1AxisSupp->getSecondaryAxis( nApiAxisDim );
+ break;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ return xChart1Axis;
+}
+
+} // namespace
+
+XclExpChAxis::XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXIS, EXC_ID_CHAXIS, 18 ),
+ mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
+{
+ maData.mnType = nAxisType;
+}
+
+void XclExpChAxis::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId )
+{
+ mxFont = xFont;
+ if( mxTick )
+ mxTick->SetFontColor( rColor, nColorId );
+}
+
+void XclExpChAxis::SetRotation( sal_uInt16 nRotation )
+{
+ if( mxTick )
+ mxTick->SetRotation( nRotation );
+}
+
+void XclExpChAxis::Convert( Reference< XAxis > const & xAxis, Reference< XAxis > const & xCrossingAxis,
+ Reference< cssc::XAxis > const & xChart1Axis, const XclChExtTypeInfo& rTypeInfo )
+{
+ ScfPropertySet aAxisProp( xAxis );
+ bool bCategoryAxis = ((GetAxisType() == EXC_CHAXIS_X) && rTypeInfo.mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z);
+
+ // axis line format -------------------------------------------------------
+
+ mxAxisLine = new XclExpChLineFormat( GetChRoot() );
+ mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
+ // #i58688# axis enabled
+ mxAxisLine->SetShowAxis( aAxisProp.GetBoolProperty( EXC_CHPROP_SHOW ) );
+
+ // axis scaling and increment ---------------------------------------------
+
+ ScfPropertySet aCrossingProp( xCrossingAxis );
+ if( bCategoryAxis )
+ {
+ mxLabelRange = new XclExpChLabelRange( GetChRoot() );
+ mxLabelRange->SetTicksBetweenCateg( rTypeInfo.mbTicksBetweenCateg );
+ if( xAxis.is() )
+ {
+ ScfPropertySet aChart1AxisProp( xChart1Axis );
+ // #i71684# radar charts have reversed rotation direction
+ mxLabelRange->Convert( xAxis->getScaleData(), aChart1AxisProp, (GetAxisType() == EXC_CHAXIS_X) && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) );
+ }
+ // get position of crossing axis on this axis from passed axis object
+ if( aCrossingProp.Is() )
+ mxLabelRange->ConvertAxisPosition( aCrossingProp );
+ }
+ else
+ {
+ mxValueRange = new XclExpChValueRange( GetChRoot() );
+ if( xAxis.is() )
+ mxValueRange->Convert( xAxis->getScaleData() );
+ // get position of crossing axis on this axis from passed axis object
+ if( aCrossingProp.Is() )
+ mxValueRange->ConvertAxisPosition( aCrossingProp );
+ }
+
+ // axis caption text ------------------------------------------------------
+
+ // axis ticks properties
+ mxTick = new XclExpChTick( GetChRoot() );
+ mxTick->Convert( aAxisProp, rTypeInfo, GetAxisType() );
+
+ // axis label formatting and rotation
+ ConvertFontBase( GetChRoot(), aAxisProp );
+ ConvertRotationBase( aAxisProp, true );
+
+ // axis number format
+ sal_Int32 nApiNumFmt = 0;
+ if( !bCategoryAxis && aAxisProp.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
+ {
+ bool bLinkNumberFmtToSource = false;
+ if ( !aAxisProp.GetProperty( bLinkNumberFmtToSource, EXC_CHPROP_NUMBERFORMAT_LINKSRC ) || !bLinkNumberFmtToSource )
+ mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
+ }
+
+ // grid -------------------------------------------------------------------
+
+ if( !xAxis.is() )
+ return;
+
+ // main grid
+ ScfPropertySet aGridProp( xAxis->getGridProperties() );
+ if( aGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ mxMajorGrid = lclCreateLineFormat( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ // sub grid
+ Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
+ if( aSubGridPropSeq.hasElements() )
+ {
+ ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
+ if( aSubGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) )
+ mxMinorGrid = lclCreateLineFormat( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ }
+}
+
+void XclExpChAxis::ConvertWall( css::uno::Reference< css::chart2::XDiagram > const & xDiagram )
+{
+ if( !xDiagram.is() )
+ return;
+
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ {
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxWallFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_WALL3D );
+ }
+ break;
+ case EXC_CHAXIS_Y:
+ {
+ ScfPropertySet aFloorProp( xDiagram->getFloor() );
+ mxWallFrame = lclCreateFrame( GetChRoot(), aFloorProp, EXC_CHOBJTYPE_FLOOR3D );
+ }
+ break;
+ default:
+ mxWallFrame.clear();
+ }
+}
+
+void XclExpChAxis::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxLabelRange );
+ lclSaveRecord( rStrm, mxValueRange );
+ if( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
+ XclExpUInt16Record( EXC_ID_CHFORMAT, mnNumFmtIdx ).Save( rStrm );
+ lclSaveRecord( rStrm, mxTick );
+ lclSaveRecord( rStrm, mxFont );
+ lclSaveRecord( rStrm, mxAxisLine, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_AXISLINE );
+ lclSaveRecord( rStrm, mxMajorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MAJORGRID );
+ lclSaveRecord( rStrm, mxMinorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MINORGRID );
+ lclSaveRecord( rStrm, mxWallFrame, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_WALLS );
+}
+
+void XclExpChAxis::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnType;
+ rStrm.WriteZeroBytes( 16 );
+}
+
+XclExpChAxesSet::XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
+ XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXESSET, EXC_ID_CHAXESSET, 18 )
+{
+ maData.mnAxesSetId = nAxesSetId;
+ SetFutureRecordContext( 0, nAxesSetId );
+
+ /* Need to set a reasonable size for the plot area, otherwise Excel will
+ move away embedded shapes while auto-sizing the plot area. This is just
+ a wild guess, but will be fixed with implementing manual positioning of
+ chart elements. */
+ maData.maRect.mnX = 262;
+ maData.maRect.mnY = 626;
+ maData.maRect.mnWidth = 3187;
+ maData.maRect.mnHeight = 2633;
+}
+
+sal_uInt16 XclExpChAxesSet::Convert( Reference< XDiagram > const & xDiagram, sal_uInt16 nFirstGroupIdx )
+{
+ /* First unused chart type group index is passed to be able to continue
+ counting of chart type groups for secondary axes set. */
+ sal_uInt16 nGroupIdx = nFirstGroupIdx;
+ Reference< XCoordinateSystemContainer > xCoordSysCont( xDiagram, UNO_QUERY );
+ if( xCoordSysCont.is() )
+ {
+ Sequence< Reference< XCoordinateSystem > > aCoordSysSeq = xCoordSysCont->getCoordinateSystems();
+ if( aCoordSysSeq.hasElements() )
+ {
+ /* Process first coordinate system only. Import filter puts all
+ chart types into one coordinate system. */
+ Reference< XCoordinateSystem > xCoordSystem = aCoordSysSeq[ 0 ];
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+
+ // 3d mode
+ bool b3dChart = xCoordSystem.is() && (xCoordSystem->getDimension() == 3);
+
+ // percent charts
+ namespace ApiAxisType = cssc2::AxisType;
+ Reference< XAxis > xApiYAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_Y, nApiAxesSetIdx );
+ bool bPercent = xApiYAxis.is() && (xApiYAxis->getScaleData().AxisType == ApiAxisType::PERCENT);
+
+ // connector lines in bar charts
+ ScfPropertySet aDiaProp( xDiagram );
+ bool bConnectBars = aDiaProp.GetBoolProperty( EXC_CHPROP_CONNECTBARS );
+
+ // swapped axes sets
+ ScfPropertySet aCoordSysProp( xCoordSystem );
+ bool bSwappedAxesSet = aCoordSysProp.GetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS );
+
+ // X axis for later use
+ Reference< XAxis > xApiXAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_X, nApiAxesSetIdx );
+ // X axis labels
+ ScfPropertySet aXAxisProp( xApiXAxis );
+ bool bHasXLabels = aXAxisProp.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS );
+
+ // process chart types
+ Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
+ if( xChartTypeCont.is() )
+ {
+ const Sequence< Reference< XChartType > > aChartTypeSeq = xChartTypeCont->getChartTypes();
+ for( const Reference< XChartType >& rChartType : aChartTypeSeq )
+ {
+ XclExpChTypeGroupRef xTypeGroup = new XclExpChTypeGroup( GetChRoot(), nGroupIdx );
+ xTypeGroup->ConvertType( xDiagram, rChartType, nApiAxesSetIdx, b3dChart, bSwappedAxesSet, bHasXLabels );
+ /* If new chart type group cannot be inserted into a combination
+ chart with existing type groups, insert all series into last
+ contained chart type group instead of creating a new group. */
+ XclExpChTypeGroupRef xLastGroup = GetLastTypeGroup();
+ if( xLastGroup && !(xTypeGroup->IsCombinable2d() && xLastGroup->IsCombinable2d()) )
+ {
+ xLastGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
+ }
+ else
+ {
+ xTypeGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
+ if( xTypeGroup->IsValidGroup() )
+ {
+ maTypeGroups.AppendRecord( xTypeGroup );
+ ++nGroupIdx;
+ }
+ }
+ }
+ }
+
+ if( XclExpChTypeGroup* pGroup = GetFirstTypeGroup().get() )
+ {
+ const XclChExtTypeInfo& rTypeInfo = pGroup->GetTypeInfo();
+
+ // create axes according to chart type (no axes for pie and donut charts)
+ if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
+ {
+ ConvertAxis( mxXAxis, EXC_CHAXIS_X, mxXAxisTitle, EXC_CHOBJLINK_XAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_Y );
+ ConvertAxis( mxYAxis, EXC_CHAXIS_Y, mxYAxisTitle, EXC_CHOBJLINK_YAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_X );
+ if( pGroup->Is3dDeepChart() )
+ ConvertAxis( mxZAxis, EXC_CHAXIS_Z, mxZAxisTitle, EXC_CHOBJLINK_ZAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_NONE );
+ }
+
+ // X axis category ranges
+ if( rTypeInfo.mbCategoryAxis && xApiXAxis.is() )
+ {
+ const ScaleData aScaleData = xApiXAxis->getScaleData();
+ for( size_t nIdx = 0, nSize = maTypeGroups.GetSize(); nIdx < nSize; ++nIdx )
+ maTypeGroups.GetRecord( nIdx )->ConvertCategSequence( aScaleData.Categories );
+ }
+
+ // legend
+ if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
+ {
+ Reference< XLegend > xLegend = xDiagram->getLegend();
+ if( xLegend.is() )
+ {
+ ScfPropertySet aLegendProp( xLegend );
+ pGroup->ConvertLegend( aLegendProp );
+ }
+ }
+ }
+ }
+ }
+
+ // wall/floor/diagram frame formatting
+ if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
+ {
+ XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup && xTypeGroup->Is3dWallChart() )
+ {
+ // wall/floor formatting (3D charts)
+ if( mxXAxis )
+ mxXAxis->ConvertWall( xDiagram );
+ if( mxYAxis )
+ mxYAxis->ConvertWall( xDiagram );
+ }
+ else
+ {
+ // diagram background formatting
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxPlotFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_PLOTFRAME );
+ }
+ }
+
+ // inner and outer plot area position and size
+ try
+ {
+ Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
+ Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
+ // set manual flag in chart data
+ if( !xPositioning->isAutomaticDiagramPositioning() )
+ GetChartData().SetManualPlotArea();
+ // the CHAXESSET record contains the inner plot area
+ maData.maRect = CalcChartRectFromHmm( xPositioning->calculateDiagramPositionExcludingAxes() );
+ // the embedded CHFRAMEPOS record contains the outer plot area
+ mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT );
+ // for pie charts, always use inner plot area size to exclude the data labels as Excel does
+ const XclExpChTypeGroup* pFirstTypeGroup = GetFirstTypeGroup().get();
+ bool bPieChart = pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE);
+ mxFramePos->GetFramePosData().maRect = bPieChart ? maData.maRect :
+ CalcChartRectFromHmm( xPositioning->calculateDiagramPositionIncludingAxes() );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // return first unused chart type group index for next axes set
+ return nGroupIdx;
+}
+
+bool XclExpChAxesSet::Is3dChart() const
+{
+ XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ return xTypeGroup && xTypeGroup->Is3dChart();
+}
+
+void XclExpChAxesSet::WriteSubRecords( XclExpStream& rStrm )
+{
+ lclSaveRecord( rStrm, mxFramePos );
+ lclSaveRecord( rStrm, mxXAxis );
+ lclSaveRecord( rStrm, mxYAxis );
+ lclSaveRecord( rStrm, mxZAxis );
+ lclSaveRecord( rStrm, mxXAxisTitle );
+ lclSaveRecord( rStrm, mxYAxisTitle );
+ lclSaveRecord( rStrm, mxZAxisTitle );
+ if( mxPlotFrame )
+ {
+ XclExpEmptyRecord( EXC_ID_CHPLOTFRAME ).Save( rStrm );
+ mxPlotFrame->Save( rStrm );
+ }
+ maTypeGroups.Save( rStrm );
+}
+
+XclExpChTypeGroupRef XclExpChAxesSet::GetFirstTypeGroup() const
+{
+ return maTypeGroups.GetFirstRecord();
+}
+
+XclExpChTypeGroupRef XclExpChAxesSet::GetLastTypeGroup() const
+{
+ return maTypeGroups.GetLastRecord();
+}
+
+void XclExpChAxesSet::ConvertAxis(
+ XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType,
+ XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget,
+ Reference< XCoordinateSystem > const & xCoordSystem, const XclChExtTypeInfo& rTypeInfo,
+ sal_Int32 nCrossingAxisDim )
+{
+ // create and convert axis object
+ rxChAxis = new XclExpChAxis( GetChRoot(), nAxisType );
+ sal_Int32 nApiAxisDim = rxChAxis->GetApiAxisDimension();
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ Reference< XAxis > xAxis = lclGetApiAxis( xCoordSystem, nApiAxisDim, nApiAxesSetIdx );
+ Reference< XAxis > xCrossingAxis = lclGetApiAxis( xCoordSystem, nCrossingAxisDim, nApiAxesSetIdx );
+ Reference< cssc::XAxis > xChart1Axis = lclGetApiChart1Axis( GetChartDocument(), nApiAxisDim, nApiAxesSetIdx );
+ rxChAxis->Convert( xAxis, xCrossingAxis, xChart1Axis, rTypeInfo );
+
+ // create and convert axis title
+ Reference< XTitled > xTitled( xAxis, UNO_QUERY );
+ rxChAxisTitle = lclCreateTitle( GetChRoot(), xTitled, nTitleTarget );
+}
+
+void XclExpChAxesSet::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maData.mnAxesSetId << maData.maRect;
+}
+
+// The chart object ===========================================================
+
+static void lcl_getChartSubTitle(const Reference<XChartDocument>& xChartDoc,
+ OUString& rSubTitle)
+{
+ Reference< css::chart::XChartDocument > xChartDoc1(xChartDoc, UNO_QUERY);
+ if (!xChartDoc1.is())
+ return;
+
+ Reference< XPropertySet > xProp(xChartDoc1->getSubTitle(), UNO_QUERY);
+ if (!xProp.is())
+ return;
+
+ OUString aTitle;
+ Any any = xProp->getPropertyValue("String");
+ if (any >>= aTitle)
+ rSubTitle = aTitle;
+}
+
+XclExpChChart::XclExpChChart( const XclExpRoot& rRoot,
+ Reference< XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) :
+ XclExpChGroupBase( XclExpChRoot( rRoot, *this ), EXC_CHFRBLOCK_TYPE_CHART, EXC_ID_CHCHART, 16 )
+{
+ Size aPtSize = o3tl::convert( rChartRect.GetSize(), o3tl::Length::mm100, o3tl::Length::pt );
+ // rectangle is stored in 16.16 fixed-point format
+ maRect.mnX = maRect.mnY = 0;
+ maRect.mnWidth = static_cast< sal_Int32 >( aPtSize.Width() << 16 );
+ maRect.mnHeight = static_cast< sal_Int32 >( aPtSize.Height() << 16 );
+
+ // global chart properties (default values)
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, false );
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_MANPLOTAREA );
+ maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP;
+
+ // always create both axes set objects
+ mxPrimAxesSet = std::make_shared<XclExpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
+ mxSecnAxesSet = std::make_shared<XclExpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
+
+ if( !xChartDoc.is() )
+ return;
+
+ Reference< XDiagram > xDiagram = xChartDoc->getFirstDiagram();
+
+ // global chart properties (only 'include hidden cells' attribute for now)
+ ScfPropertySet aDiagramProp( xDiagram );
+ bool bIncludeHidden = aDiagramProp.GetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS );
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, !bIncludeHidden );
+
+ // initialize API conversion (remembers xChartDoc and rChartRect internally)
+ InitConversion( xChartDoc, rChartRect );
+
+ // chart frame
+ ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
+ mxFrame = lclCreateFrame( GetChRoot(), aFrameProp, EXC_CHOBJTYPE_BACKGROUND );
+
+ // chart title
+ Reference< XTitled > xTitled( xChartDoc, UNO_QUERY );
+ OUString aSubTitle;
+ lcl_getChartSubTitle(xChartDoc, aSubTitle);
+ mxTitle = lclCreateTitle( GetChRoot(), xTitled, EXC_CHOBJLINK_TITLE,
+ !aSubTitle.isEmpty() ? &aSubTitle : nullptr );
+
+ // diagrams (axes sets)
+ sal_uInt16 nFreeGroupIdx = mxPrimAxesSet->Convert( xDiagram, 0 );
+ if( !mxPrimAxesSet->Is3dChart() )
+ mxSecnAxesSet->Convert( xDiagram, nFreeGroupIdx );
+
+ // treatment of missing values
+ ScfPropertySet aDiaProp( xDiagram );
+ sal_Int32 nMissingValues = 0;
+ if( aDiaProp.GetProperty( nMissingValues, EXC_CHPROP_MISSINGVALUETREATMENT ) )
+ {
+ using namespace cssc::MissingValueTreatment;
+ switch( nMissingValues )
+ {
+ case LEAVE_GAP: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP; break;
+ case USE_ZERO: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_ZERO; break;
+ case CONTINUE: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_INTERPOLATE; break;
+ }
+ }
+
+ // finish API conversion
+ FinishConversion();
+}
+
+XclExpChSeriesRef XclExpChChart::CreateSeries()
+{
+ XclExpChSeriesRef xSeries;
+ sal_uInt16 nSeriesIdx = static_cast< sal_uInt16 >( maSeries.GetSize() );
+ if( nSeriesIdx <= EXC_CHSERIES_MAXSERIES )
+ {
+ xSeries = new XclExpChSeries( GetChRoot(), nSeriesIdx );
+ maSeries.AppendRecord( xSeries );
+ }
+ return xSeries;
+}
+
+void XclExpChChart::RemoveLastSeries()
+{
+ if( !maSeries.IsEmpty() )
+ maSeries.RemoveRecord( maSeries.GetSize() - 1 );
+}
+
+void XclExpChChart::SetDataLabel( XclExpChTextRef const & xText )
+{
+ if( xText )
+ maLabels.AppendRecord( xText );
+}
+
+void XclExpChChart::SetManualPlotArea()
+{
+ // this flag does not exist in BIFF5
+ if( GetBiff() == EXC_BIFF8 )
+ ::set_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
+}
+
+void XclExpChChart::WriteSubRecords( XclExpStream& rStrm )
+{
+ // background format
+ lclSaveRecord( rStrm, mxFrame );
+
+ // data series
+ maSeries.Save( rStrm );
+
+ // CHPROPERTIES record
+ rStrm.StartRecord( EXC_ID_CHPROPERTIES, 4 );
+ rStrm << maProps.mnFlags << maProps.mnEmptyMode << sal_uInt8( 0 );
+ rStrm.EndRecord();
+
+ // axes sets (always save primary axes set)
+ sal_uInt16 nUsedAxesSets = mxSecnAxesSet->IsValidAxesSet() ? 2 : 1;
+ XclExpUInt16Record( EXC_ID_CHUSEDAXESSETS, nUsedAxesSets ).Save( rStrm );
+ mxPrimAxesSet->Save( rStrm );
+ if( mxSecnAxesSet->IsValidAxesSet() )
+ mxSecnAxesSet->Save( rStrm );
+
+ // chart title and data labels
+ lclSaveRecord( rStrm, mxTitle );
+ maLabels.Save( rStrm );
+}
+
+void XclExpChChart::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maRect;
+}
+
+XclExpChartDrawing::XclExpChartDrawing( const XclExpRoot& rRoot,
+ const Reference< XModel >& rxModel, const Size& rChartSize ) :
+ XclExpRoot( rRoot )
+{
+ if( rChartSize.IsEmpty() )
+ return;
+
+ ScfPropertySet aPropSet( rxModel );
+ Reference< XShapes > xShapes;
+ if( !(aPropSet.GetProperty( xShapes, EXC_CHPROP_ADDITIONALSHAPES ) && xShapes.is() && (xShapes->getCount() > 0)) )
+ return;
+
+ /* Create a new independent object manager with own DFF stream for the
+ DGCONTAINER, pass global manager as parent for shared usage of
+ global DFF data (picture container etc.). */
+ mxObjMgr = std::make_shared<XclExpEmbeddedObjectManager>( GetObjectManager(), rChartSize, EXC_CHART_TOTALUNITS, EXC_CHART_TOTALUNITS );
+ // initialize the drawing object list
+ mxObjMgr->StartSheet();
+ // process the draw page (convert all shapes)
+ mxObjRecs = mxObjMgr->ProcessDrawing( xShapes );
+ // finalize the DFF stream
+ mxObjMgr->EndDocument();
+}
+
+XclExpChartDrawing::~XclExpChartDrawing()
+{
+}
+
+void XclExpChartDrawing::Save( XclExpStream& rStrm )
+{
+ if( mxObjRecs )
+ mxObjRecs->Save( rStrm );
+}
+
+XclExpChart::XclExpChart( const XclExpRoot& rRoot, Reference< XModel > const & xModel, const tools::Rectangle& rChartRect ) :
+ XclExpSubStream( EXC_BOF_CHART ),
+ XclExpRoot( rRoot )
+{
+ AppendNewRecord( new XclExpChartPageSettings( rRoot ) );
+ AppendNewRecord( new XclExpBoolRecord( EXC_ID_PROTECT, false ) );
+ AppendNewRecord( new XclExpChartDrawing( rRoot, xModel, rChartRect.GetSize() ) );
+ AppendNewRecord( new XclExpUInt16Record( EXC_ID_CHUNITS, EXC_CHUNITS_TWIPS ) );
+
+ Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
+ AppendNewRecord( new XclExpChChart( rRoot, xChartDoc, rChartRect ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xecontent.cxx b/sc/source/filter/excel/xecontent.cxx
new file mode 100644
index 000000000..65f8bb2f4
--- /dev/null
+++ b/sc/source/filter/excel/xecontent.cxx
@@ -0,0 +1,2211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xecontent.hxx>
+
+#include <vector>
+#include <algorithm>
+#include <string_view>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/XAreaLinks.hpp>
+#include <com/sun/star/sheet/XAreaLink.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <sfx2/objsh.hxx>
+#include <tools/urlobj.hxx>
+#include <formula/grammar.hxx>
+#include <scitems.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <validat.hxx>
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <stlpool.hxx>
+#include <patattr.hxx>
+#include <fapihelper.hxx>
+#include <xehelper.hxx>
+#include <xestyle.hxx>
+#include <xename.hxx>
+#include <xlcontent.hxx>
+#include <xltools.hxx>
+#include <xeformula.hxx>
+#include <rtl/uuid.h>
+#include <sal/log.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::table::CellRangeAddress;
+using ::com::sun::star::sheet::XAreaLinks;
+using ::com::sun::star::sheet::XAreaLink;
+
+// Shared string table ========================================================
+
+namespace {
+
+/** A single string entry in the hash table. */
+struct XclExpHashEntry
+{
+ const XclExpString* mpString; /// Pointer to the string (no ownership).
+ sal_uInt32 mnSstIndex; /// The SST index of this string.
+ explicit XclExpHashEntry( const XclExpString* pString, sal_uInt32 nSstIndex ) :
+ mpString( pString ), mnSstIndex( nSstIndex ) {}
+};
+
+/** Function object for strict weak ordering. */
+struct XclExpHashEntrySWO
+{
+ bool operator()( const XclExpHashEntry& rLeft, const XclExpHashEntry& rRight ) const
+ { return *rLeft.mpString < *rRight.mpString; }
+};
+
+}
+
+/** Implementation of the SST export.
+ @descr Stores all passed strings in a hash table and prevents repeated
+ insertion of equal strings. */
+class XclExpSstImpl
+{
+public:
+ explicit XclExpSstImpl();
+
+ /** Inserts the passed string, if not already inserted, and returns the unique SST index. */
+ sal_uInt32 Insert( XclExpStringRef xString );
+
+ /** Writes the complete SST and EXTSST records. */
+ void Save( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ typedef ::std::vector< XclExpHashEntry > XclExpHashVec;
+
+ std::vector< XclExpStringRef > maStringVector; /// List of unique strings (in SST ID order).
+ std::vector< XclExpHashVec >
+ maHashTab; /// Hashed table that manages string pointers.
+ sal_uInt32 mnTotal; /// Total count of strings (including doubles).
+ sal_uInt32 mnSize; /// Size of the SST (count of unique strings).
+};
+
+const sal_uInt32 EXC_SST_HASHTABLE_SIZE = 2048;
+
+XclExpSstImpl::XclExpSstImpl() :
+ maHashTab( EXC_SST_HASHTABLE_SIZE ),
+ mnTotal( 0 ),
+ mnSize( 0 )
+{
+}
+
+sal_uInt32 XclExpSstImpl::Insert( XclExpStringRef xString )
+{
+ OSL_ENSURE( xString, "XclExpSstImpl::Insert - empty pointer not allowed" );
+ if( !xString )
+ xString.reset( new XclExpString );
+
+ ++mnTotal;
+ sal_uInt32 nSstIndex = 0;
+
+ // calculate hash value in range [0,EXC_SST_HASHTABLE_SIZE)
+ sal_uInt16 nHash = xString->GetHash();
+ nHash = (nHash ^ (nHash / EXC_SST_HASHTABLE_SIZE)) % EXC_SST_HASHTABLE_SIZE;
+
+ XclExpHashVec& rVec = maHashTab[ nHash ];
+ XclExpHashEntry aEntry( xString.get(), mnSize );
+ XclExpHashVec::iterator aIt = ::std::lower_bound( rVec.begin(), rVec.end(), aEntry, XclExpHashEntrySWO() );
+ if( (aIt == rVec.end()) || (*aIt->mpString != *xString) )
+ {
+ nSstIndex = mnSize;
+ maStringVector.push_back( xString );
+ rVec.insert( aIt, aEntry );
+ ++mnSize;
+ }
+ else
+ {
+ nSstIndex = aIt->mnSstIndex;
+ }
+
+ return nSstIndex;
+}
+
+void XclExpSstImpl::Save( XclExpStream& rStrm )
+{
+ if( maStringVector.empty() )
+ return;
+
+ SvMemoryStream aExtSst( 8192 );
+
+ sal_uInt32 nBucket = mnSize;
+ while( nBucket > 0x0100 )
+ nBucket /= 2;
+
+ sal_uInt16 nPerBucket = llimit_cast< sal_uInt16 >( nBucket, 8 );
+ sal_uInt16 nBucketIndex = 0;
+
+ // *** write the SST record ***
+
+ rStrm.StartRecord( EXC_ID_SST, 8 );
+
+ rStrm << mnTotal << mnSize;
+ for (auto const& elem : maStringVector)
+ {
+ if( !nBucketIndex )
+ {
+ // write bucket info before string to get correct record position
+ sal_uInt32 nStrmPos = static_cast< sal_uInt32 >( rStrm.GetSvStreamPos() );
+ sal_uInt16 nRecPos = rStrm.GetRawRecPos() + 4;
+ aExtSst.WriteUInt32( nStrmPos ) // stream position
+ .WriteUInt16( nRecPos ) // position from start of SST or CONTINUE
+ .WriteUInt16( 0 ); // reserved
+ }
+
+ rStrm << *elem;
+
+ if( ++nBucketIndex == nPerBucket )
+ nBucketIndex = 0;
+ }
+
+ rStrm.EndRecord();
+
+ // *** write the EXTSST record ***
+
+ rStrm.StartRecord( EXC_ID_EXTSST, 0 );
+
+ rStrm << nPerBucket;
+ rStrm.SetSliceSize( 8 ); // size of one bucket info
+ aExtSst.Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( aExtSst );
+
+ rStrm.EndRecord();
+}
+
+void XclExpSstImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maStringVector.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr pSst = rStrm.CreateOutputStream(
+ "xl/sharedStrings.xml",
+ u"sharedStrings.xml",
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
+ oox::getRelationship(Relationship::SHAREDSTRINGS));
+ rStrm.PushStream( pSst );
+
+ pSst->startElement( XML_sst,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ XML_count, OString::number(mnTotal),
+ XML_uniqueCount, OString::number(mnSize) );
+
+ for (auto const& elem : maStringVector)
+ {
+ pSst->startElement(XML_si);
+ elem->WriteXml( rStrm );
+ pSst->endElement( XML_si );
+ }
+
+ pSst->endElement( XML_sst );
+
+ rStrm.PopStream();
+}
+
+XclExpSst::XclExpSst() :
+ mxImpl( new XclExpSstImpl )
+{
+}
+
+XclExpSst::~XclExpSst()
+{
+}
+
+sal_uInt32 XclExpSst::Insert( const XclExpStringRef& xString )
+{
+ return mxImpl->Insert( xString );
+}
+
+void XclExpSst::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpSst::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+// Merged cells ===============================================================
+
+XclExpMergedcells::XclExpMergedcells( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpMergedcells::AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maMergedRanges.push_back( rRange );
+ maBaseXFIds.push_back( nBaseXFId );
+ }
+}
+
+sal_uInt32 XclExpMergedcells::GetBaseXFId( const ScAddress& rPos ) const
+{
+ OSL_ENSURE( maBaseXFIds.size() == maMergedRanges.size(), "XclExpMergedcells::GetBaseXFId - invalid lists" );
+ ScfUInt32Vec::const_iterator aIt = maBaseXFIds.begin();
+ ScRangeList& rNCRanges = const_cast< ScRangeList& >( maMergedRanges );
+ for ( size_t i = 0, nRanges = rNCRanges.size(); i < nRanges; ++i, ++aIt )
+ {
+ const ScRange & rScRange = rNCRanges[ i ];
+ if( rScRange.Contains( rPos ) )
+ return *aIt;
+ }
+ return EXC_XFID_NOTFOUND;
+}
+
+void XclExpMergedcells::Save( XclExpStream& rStrm )
+{
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ XclRangeList aXclRanges;
+ GetAddressConverter().ConvertRangeList( aXclRanges, maMergedRanges, true );
+ size_t nFirstRange = 0;
+ size_t nRemainingRanges = aXclRanges.size();
+ while( nRemainingRanges > 0 )
+ {
+ size_t nRangeCount = ::std::min< size_t >( nRemainingRanges, EXC_MERGEDCELLS_MAXCOUNT );
+ rStrm.StartRecord( EXC_ID_MERGEDCELLS, 2 + 8 * nRangeCount );
+ aXclRanges.WriteSubList( rStrm, nFirstRange, nRangeCount );
+ rStrm.EndRecord();
+ nFirstRange += nRangeCount;
+ nRemainingRanges -= nRangeCount;
+ }
+}
+
+void XclExpMergedcells::SaveXml( XclExpXmlStream& rStrm )
+{
+ size_t nCount = maMergedRanges.size();
+ if( !nCount )
+ return;
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_mergeCells, XML_count, OString::number(nCount));
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ const ScRange & rRange = maMergedRanges[ i ];
+ rWorksheet->singleElement(XML_mergeCell, XML_ref,
+ XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rRange));
+ }
+ rWorksheet->endElement( XML_mergeCells );
+}
+
+// Hyperlinks =================================================================
+
+XclExpHyperlink::XclExpHyperlink( const XclExpRoot& rRoot, const SvxURLField& rUrlField, const ScAddress& rScPos ) :
+ XclExpRecord( EXC_ID_HLINK ),
+ maScPos( rScPos ),
+ mxVarData( new SvMemoryStream ),
+ mnFlags( 0 )
+{
+ const OUString& rUrl = rUrlField.GetURL();
+ const OUString& rRepr = rUrlField.GetRepresentation();
+ INetURLObject aUrlObj( rUrl );
+ const INetProtocol eProtocol = aUrlObj.GetProtocol();
+ bool bWithRepr = !rRepr.isEmpty();
+ XclExpStream aXclStrm( *mxVarData, rRoot ); // using in raw write mode.
+
+ // description
+ if( bWithRepr )
+ {
+ XclExpString aDescr( rRepr, XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << sal_uInt32( aDescr.Len() + 1 ); // string length + 1 trailing zero word
+ aDescr.WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_DESCR;
+ m_Repr = rRepr;
+ }
+
+ // file link or URL
+ if( eProtocol == INetProtocol::File || eProtocol == INetProtocol::Smb )
+ {
+ sal_uInt16 nLevel;
+ bool bRel;
+ OUString aFileName(
+ BuildFileName(nLevel, bRel, rUrl, rRoot, rRoot.GetOutput() == EXC_OUTPUT_XML_2007));
+
+ if( eProtocol == INetProtocol::Smb )
+ {
+ // #n382718# (and #n261623#) Convert smb notation to '\\'
+ aFileName = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ aFileName = aFileName.copy(4); // skip the 'smb:' part
+ aFileName = aFileName.replace('/', '\\');
+ }
+
+ if( !bRel )
+ mnFlags |= EXC_HLINK_ABS;
+ mnFlags |= EXC_HLINK_BODY;
+
+ OString aAsciiLink(OUStringToOString(aFileName,
+ rRoot.GetTextEncoding()));
+ XclExpString aLink( aFileName, XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << XclTools::maGuidFileMoniker
+ << nLevel
+ << sal_uInt32( aAsciiLink.getLength() + 1 ); // string length + 1 trailing zero byte
+ aXclStrm.Write( aAsciiLink.getStr(), aAsciiLink.getLength() );
+ aXclStrm << sal_uInt8( 0 )
+ << sal_uInt32( 0xDEADFFFF );
+ aXclStrm.WriteZeroBytes( 20 );
+ aXclStrm << sal_uInt32( aLink.GetBufferSize() + 6 )
+ << sal_uInt32( aLink.GetBufferSize() ) // byte count, not string length
+ << sal_uInt16( 0x0003 );
+ aLink.WriteBuffer( aXclStrm ); // NO flags
+
+ if (m_Repr.isEmpty())
+ m_Repr = aFileName;
+
+ msTarget = XclXmlUtils::ToOUString( aLink );
+
+ if( bRel )
+ {
+ for( int i = 0; i < nLevel; ++i )
+ msTarget = "../" + msTarget;
+ }
+ else if (rRoot.GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ // xls expects the file:/// part appended ( or at least
+ // ms2007 does, ms2010 is more tolerant )
+ msTarget = "file:///" + msTarget;
+ }
+ }
+ else if( eProtocol != INetProtocol::NotValid )
+ {
+ XclExpString aUrl( aUrlObj.GetURLNoMark(), XclStrFlags::ForceUnicode, 255 );
+ aXclStrm << XclTools::maGuidUrlMoniker
+ << sal_uInt32( aUrl.GetBufferSize() + 2 ); // byte count + 1 trailing zero word
+ aUrl.WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_BODY | EXC_HLINK_ABS;
+ if (m_Repr.isEmpty())
+ m_Repr = rUrl;
+
+ msTarget = XclXmlUtils::ToOUString( aUrl );
+ }
+ else if( !rUrl.isEmpty() && rUrl[0] == '#' ) // hack for #89066#
+ {
+ OUString aTextMark( rUrl.copy( 1 ) );
+
+ sal_Int32 nSepPos = aTextMark.lastIndexOf( '!' );
+ sal_Int32 nPointPos = aTextMark.lastIndexOf( '.' );
+ // last dot is the separator, if there is no ! after it
+ if(nSepPos < nPointPos)
+ {
+ nSepPos = nPointPos;
+ aTextMark = aTextMark.replaceAt( nSepPos, 1, u"!" );
+ }
+
+ if (nSepPos != -1)
+ {
+ std::u16string_view aSheetName(aTextMark.subView(0, nSepPos));
+
+ if (aSheetName.find(' ') != std::u16string_view::npos && aSheetName[0] != '\'')
+ {
+ aTextMark = "'" + aTextMark.replaceAt(nSepPos, 0, u"'");
+ }
+ }
+ else
+ {
+ SCTAB nTab;
+ if (rRoot.GetDoc().GetTable(aTextMark, nTab))
+ aTextMark += "!A1"; // tdf#143220 link to sheet not valid without cell reference
+ }
+
+ mxTextMark.reset( new XclExpString( aTextMark, XclStrFlags::ForceUnicode, 255 ) );
+ }
+
+ // text mark
+ if( !mxTextMark && aUrlObj.HasMark() )
+ mxTextMark.reset( new XclExpString( aUrlObj.GetMark(), XclStrFlags::ForceUnicode, 255 ) );
+
+ if( mxTextMark )
+ {
+ aXclStrm << sal_uInt32( mxTextMark->Len() + 1 ); // string length + 1 trailing zero word
+ mxTextMark->WriteBuffer( aXclStrm ); // NO flags
+ aXclStrm << sal_uInt16( 0 );
+
+ mnFlags |= EXC_HLINK_MARK;
+
+ OUString location = XclXmlUtils::ToOUString(*mxTextMark);
+ if (!location.isEmpty() && msTarget.endsWith(OUStringConcatenation("#" + location)))
+ msTarget = msTarget.copy(0, msTarget.getLength() - location.getLength() - 1);
+ }
+
+ SetRecSize( 32 + mxVarData->Tell() );
+}
+
+XclExpHyperlink::~XclExpHyperlink()
+{
+}
+
+OUString XclExpHyperlink::BuildFileName(
+ sal_uInt16& rnLevel, bool& rbRel, const OUString& rUrl, const XclExpRoot& rRoot, bool bEncoded )
+{
+ INetURLObject aURLObject( rUrl );
+ OUString aDosName(bEncoded ? aURLObject.GetMainURL(INetURLObject::DecodeMechanism::ToIUri)
+ : aURLObject.getFSysPath(FSysStyle::Dos));
+ rnLevel = 0;
+ rbRel = rRoot.IsRelUrl();
+
+ if( rbRel )
+ {
+ // try to convert to relative file name
+ OUString aTmpName( aDosName );
+ aDosName = INetURLObject::GetRelURL( rRoot.GetBasePath(), rUrl,
+ INetURLObject::EncodeMechanism::WasEncoded,
+ (bEncoded ? INetURLObject::DecodeMechanism::ToIUri : INetURLObject::DecodeMechanism::WithCharset));
+
+ if (aDosName.startsWith(INET_FILE_SCHEME))
+ {
+ // not converted to rel -> back to old, return absolute flag
+ aDosName = aTmpName;
+ rbRel = false;
+ }
+ else if (aDosName.startsWith("./"))
+ {
+ aDosName = aDosName.copy(2);
+ }
+ else
+ {
+ while (aDosName.startsWith("../"))
+ {
+ aDosName = aDosName.copy(3);
+ ++rnLevel;
+ }
+ }
+ }
+ return aDosName;
+}
+
+void XclExpHyperlink::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( maScPos.Col() );
+ sal_uInt16 nXclRow = static_cast< sal_uInt16 >( maScPos.Row() );
+ rStrm << nXclRow << nXclRow << nXclCol << nXclCol;
+ WriteEmbeddedData( rStrm );
+}
+
+void XclExpHyperlink::WriteEmbeddedData( XclExpStream& rStrm )
+{
+ rStrm << XclTools::maGuidStdLink
+ << sal_uInt32( 2 )
+ << mnFlags;
+
+ mxVarData->Seek( STREAM_SEEK_TO_BEGIN );
+ rStrm.CopyFromStream( *mxVarData );
+}
+
+void XclExpHyperlink::SaveXml( XclExpXmlStream& rStrm )
+{
+ OUString sId = !msTarget.isEmpty() ? rStrm.addRelation( rStrm.GetCurrentStream()->getOutputStream(),
+ oox::getRelationship(Relationship::HYPERLINK),
+ msTarget, true ) : OUString();
+ std::optional<OString> sTextMark;
+ if (mxTextMark)
+ sTextMark = XclXmlUtils::ToOString(*mxTextMark);
+ rStrm.GetCurrentStream()->singleElement( XML_hyperlink,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScPos),
+ FSNS( XML_r, XML_id ), sax_fastparser::UseIf(sId, !sId.isEmpty()),
+ XML_location, sTextMark,
+ // OOXTODO: XML_tooltip, from record HLinkTooltip 800h wzTooltip
+ XML_display, m_Repr );
+}
+
+// Label ranges ===============================================================
+
+XclExpLabelranges::XclExpLabelranges( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ SCTAB nScTab = GetCurrScTab();
+ // row label ranges
+ FillRangeList( maRowRanges, rRoot.GetDoc().GetRowNameRangesRef(), nScTab );
+ // row labels only over 1 column (restriction of Excel97/2000/XP)
+ for ( size_t i = 0, nRanges = maRowRanges.size(); i < nRanges; ++i )
+ {
+ ScRange & rScRange = maRowRanges[ i ];
+ if( rScRange.aStart.Col() != rScRange.aEnd.Col() )
+ rScRange.aEnd.SetCol( rScRange.aStart.Col() );
+ }
+ // col label ranges
+ FillRangeList( maColRanges, rRoot.GetDoc().GetColNameRangesRef(), nScTab );
+}
+
+void XclExpLabelranges::FillRangeList( ScRangeList& rScRanges,
+ const ScRangePairListRef& xLabelRangesRef, SCTAB nScTab )
+{
+ for ( size_t i = 0, nPairs = xLabelRangesRef->size(); i < nPairs; ++i )
+ {
+ const ScRangePair & rRangePair = (*xLabelRangesRef)[i];
+ const ScRange& rScRange = rRangePair.GetRange( 0 );
+ if( rScRange.aStart.Tab() == nScTab )
+ rScRanges.push_back( rScRange );
+ }
+}
+
+void XclExpLabelranges::Save( XclExpStream& rStrm )
+{
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ XclRangeList aRowXclRanges, aColXclRanges;
+ rAddrConv.ConvertRangeList( aRowXclRanges, maRowRanges, false );
+ rAddrConv.ConvertRangeList( aColXclRanges, maColRanges, false );
+ if( !aRowXclRanges.empty() || !aColXclRanges.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_LABELRANGES, 4 + 8 * (aRowXclRanges.size() + aColXclRanges.size()) );
+ rStrm << aRowXclRanges << aColXclRanges;
+ rStrm.EndRecord();
+ }
+}
+
+// Conditional formatting ====================================================
+
+/** Represents a CF record that contains one condition of a conditional format. */
+class XclExpCFImpl : protected XclExpRoot
+{
+public:
+ explicit XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin );
+
+ /** Writes the body of the CF record. */
+ void WriteBody( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ const ScCondFormatEntry& mrFormatEntry; /// Calc conditional format entry.
+ ScAddress maOrigin; /// Top left cell of the combined range
+ XclFontData maFontData; /// Font formatting attributes.
+ XclExpCellBorder maBorder; /// Border formatting attributes.
+ XclExpCellArea maArea; /// Pattern formatting attributes.
+ XclTokenArrayRef mxTokArr1; /// Formula for first condition.
+ XclTokenArrayRef mxTokArr2; /// Formula for second condition.
+ sal_uInt32 mnFontColorId; /// Font color ID.
+ sal_uInt8 mnType; /// Type of the condition (cell/formula).
+ sal_uInt8 mnOperator; /// Comparison operator for cell type.
+ sal_Int32 mnPriority; /// Priority of this entry; needed for oox export
+ bool mbFontUsed; /// true = Any font attribute used.
+ bool mbHeightUsed; /// true = Font height used.
+ bool mbWeightUsed; /// true = Font weight used.
+ bool mbColorUsed; /// true = Font color used.
+ bool mbUnderlUsed; /// true = Font underline type used.
+ bool mbItalicUsed; /// true = Font posture used.
+ bool mbStrikeUsed; /// true = Font strikeout used.
+ bool mbBorderUsed; /// true = Border attribute used.
+ bool mbPattUsed; /// true = Pattern attribute used.
+ bool mbFormula2;
+};
+
+XclExpCFImpl::XclExpCFImpl( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
+ XclExpRoot( rRoot ),
+ mrFormatEntry( rFormatEntry ),
+ maOrigin( aOrigin ),
+ mnFontColorId( 0 ),
+ mnType( EXC_CF_TYPE_CELL ),
+ mnOperator( EXC_CF_CMP_NONE ),
+ mnPriority( nPriority ),
+ mbFontUsed( false ),
+ mbHeightUsed( false ),
+ mbWeightUsed( false ),
+ mbColorUsed( false ),
+ mbUnderlUsed( false ),
+ mbItalicUsed( false ),
+ mbStrikeUsed( false ),
+ mbBorderUsed( false ),
+ mbPattUsed( false ),
+ mbFormula2(false)
+{
+ // Set correct tab for maOrigin from GetValidSrcPos() of the format-entry.
+ ScAddress aValidSrcPos = mrFormatEntry.GetValidSrcPos();
+ maOrigin.SetTab(aValidSrcPos.Tab());
+
+ /* Get formatting attributes here, and not in WriteBody(). This is needed to
+ correctly insert all colors into the palette. */
+
+ if( SfxStyleSheetBase* pStyleSheet = GetDoc().GetStyleSheetPool()->Find( mrFormatEntry.GetStyle(), SfxStyleFamily::Para ) )
+ {
+ const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+
+ // font
+ mbHeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_HEIGHT, true );
+ mbWeightUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_WEIGHT, true );
+ mbColorUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_COLOR, true );
+ mbUnderlUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_UNDERLINE, true );
+ mbItalicUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_POSTURE, true );
+ mbStrikeUsed = ScfTools::CheckItem( rItemSet, ATTR_FONT_CROSSEDOUT, true );
+ mbFontUsed = mbHeightUsed || mbWeightUsed || mbColorUsed || mbUnderlUsed || mbItalicUsed || mbStrikeUsed;
+ if( mbFontUsed )
+ {
+ vcl::Font aFont;
+ ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW );
+ maFontData.FillFromVclFont( aFont );
+ mnFontColorId = GetPalette().InsertColor( maFontData.maColor, EXC_COLOR_CELLTEXT );
+ }
+
+ // border
+ mbBorderUsed = ScfTools::CheckItem( rItemSet, ATTR_BORDER, true );
+ if( mbBorderUsed )
+ maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff() );
+
+ // pattern
+ mbPattUsed = ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true );
+ if( mbPattUsed )
+ maArea.FillFromItemSet( rItemSet, GetPalette(), true );
+ }
+
+ // *** mode and comparison operator ***
+
+ switch( rFormatEntry.GetOperation() )
+ {
+ case ScConditionMode::NONE:
+ mnType = EXC_CF_TYPE_NONE;
+ break;
+ case ScConditionMode::Between:
+ mnOperator = EXC_CF_CMP_BETWEEN;
+ mbFormula2 = true;
+ break;
+ case ScConditionMode::NotBetween:
+ mnOperator = EXC_CF_CMP_NOT_BETWEEN;
+ mbFormula2 = true;
+ break;
+ case ScConditionMode::Equal:
+ mnOperator = EXC_CF_CMP_EQUAL;
+ break;
+ case ScConditionMode::NotEqual:
+ mnOperator = EXC_CF_CMP_NOT_EQUAL;
+ break;
+ case ScConditionMode::Greater:
+ mnOperator = EXC_CF_CMP_GREATER;
+ break;
+ case ScConditionMode::Less:
+ mnOperator = EXC_CF_CMP_LESS;
+ break;
+ case ScConditionMode::EqGreater:
+ mnOperator = EXC_CF_CMP_GREATER_EQUAL;
+ break;
+ case ScConditionMode::EqLess:
+ mnOperator = EXC_CF_CMP_LESS_EQUAL;
+ break;
+ case ScConditionMode::Direct:
+ mnType = EXC_CF_TYPE_FMLA;
+ break;
+ default:
+ mnType = EXC_CF_TYPE_NONE;
+ OSL_FAIL( "XclExpCF::WriteBody - unknown condition type" );
+ }
+}
+
+void XclExpCFImpl::WriteBody( XclExpStream& rStrm )
+{
+
+ // *** formulas ***
+
+ XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
+
+ std::unique_ptr< ScTokenArray > xScTokArr( mrFormatEntry.CreateFlatCopiedTokenArray( 0 ) );
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
+
+ if (mbFormula2)
+ {
+ xScTokArr = mrFormatEntry.CreateFlatCopiedTokenArray( 1 );
+ mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_CONDFMT, *xScTokArr );
+ }
+
+ // *** mode and comparison operator ***
+
+ rStrm << mnType << mnOperator;
+
+ // *** formula sizes ***
+
+ sal_uInt16 nFmlaSize1 = mxTokArr1 ? mxTokArr1->GetSize() : 0;
+ sal_uInt16 nFmlaSize2 = mxTokArr2 ? mxTokArr2->GetSize() : 0;
+ rStrm << nFmlaSize1 << nFmlaSize2;
+
+ // *** formatting blocks ***
+
+ if( mbFontUsed || mbBorderUsed || mbPattUsed )
+ {
+ sal_uInt32 nFlags = EXC_CF_ALLDEFAULT;
+
+ ::set_flag( nFlags, EXC_CF_BLOCK_FONT, mbFontUsed );
+ ::set_flag( nFlags, EXC_CF_BLOCK_BORDER, mbBorderUsed );
+ ::set_flag( nFlags, EXC_CF_BLOCK_AREA, mbPattUsed );
+
+ // attributes used -> set flags to 0.
+ ::set_flag( nFlags, EXC_CF_BORDER_ALL, !mbBorderUsed );
+ ::set_flag( nFlags, EXC_CF_AREA_ALL, !mbPattUsed );
+
+ rStrm << nFlags << sal_uInt16( 0 );
+
+ if( mbFontUsed )
+ {
+ // font height, 0xFFFFFFFF indicates unused
+ sal_uInt32 nHeight = mbHeightUsed ? maFontData.mnHeight : 0xFFFFFFFF;
+ // font style: italic and strikeout
+ sal_uInt32 nStyle = 0;
+ ::set_flag( nStyle, EXC_CF_FONT_STYLE, maFontData.mbItalic );
+ ::set_flag( nStyle, EXC_CF_FONT_STRIKEOUT, maFontData.mbStrikeout );
+ // font color, 0xFFFFFFFF indicates unused
+ sal_uInt32 nColor = mbColorUsed ? GetPalette().GetColorIndex( mnFontColorId ) : 0xFFFFFFFF;
+ // font used flags for italic, weight, and strikeout -> 0 = used, 1 = default
+ sal_uInt32 nFontFlags1 = EXC_CF_FONT_ALLDEFAULT;
+ ::set_flag( nFontFlags1, EXC_CF_FONT_STYLE, !(mbItalicUsed || mbWeightUsed) );
+ ::set_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT, !mbStrikeUsed );
+ // font used flag for underline -> 0 = used, 1 = default
+ sal_uInt32 nFontFlags3 = mbUnderlUsed ? 0 : EXC_CF_FONT_UNDERL;
+
+ rStrm.WriteZeroBytesToRecord( 64 );
+ rStrm << nHeight
+ << nStyle
+ << maFontData.mnWeight
+ << EXC_FONTESC_NONE
+ << maFontData.mnUnderline;
+ rStrm.WriteZeroBytesToRecord( 3 );
+ rStrm << nColor
+ << sal_uInt32( 0 )
+ << nFontFlags1
+ << EXC_CF_FONT_ESCAPEM // escapement never used -> set the flag
+ << nFontFlags3;
+ rStrm.WriteZeroBytesToRecord( 16 );
+ rStrm << sal_uInt16( 1 ); // must be 1
+ }
+
+ if( mbBorderUsed )
+ {
+ sal_uInt16 nLineStyle = 0;
+ sal_uInt32 nLineColor = 0;
+ maBorder.SetFinalColors( GetPalette() );
+ maBorder.FillToCF8( nLineStyle, nLineColor );
+ rStrm << nLineStyle << nLineColor << sal_uInt16( 0 );
+ }
+
+ if( mbPattUsed )
+ {
+ sal_uInt16 nPattern = 0, nColor = 0;
+ maArea.SetFinalColors( GetPalette() );
+ maArea.FillToCF8( nPattern, nColor );
+ rStrm << nPattern << nColor;
+ }
+ }
+ else
+ {
+ // no data blocks at all
+ rStrm << sal_uInt32( 0 ) << sal_uInt16( 0 );
+ }
+
+ // *** formulas ***
+
+ if( mxTokArr1 )
+ mxTokArr1->WriteArray( rStrm );
+ if( mxTokArr2 )
+ mxTokArr2->WriteArray( rStrm );
+}
+
+namespace {
+
+const char* GetOperatorString(ScConditionMode eMode, bool& bFrmla2)
+{
+ const char *pRet = nullptr;
+ switch(eMode)
+ {
+ case ScConditionMode::Equal:
+ pRet = "equal";
+ break;
+ case ScConditionMode::Less:
+ pRet = "lessThan";
+ break;
+ case ScConditionMode::Greater:
+ pRet = "greaterThan";
+ break;
+ case ScConditionMode::EqLess:
+ pRet = "lessThanOrEqual";
+ break;
+ case ScConditionMode::EqGreater:
+ pRet = "greaterThanOrEqual";
+ break;
+ case ScConditionMode::NotEqual:
+ pRet = "notEqual";
+ break;
+ case ScConditionMode::Between:
+ bFrmla2 = true;
+ pRet = "between";
+ break;
+ case ScConditionMode::NotBetween:
+ bFrmla2 = true;
+ pRet = "notBetween";
+ break;
+ case ScConditionMode::Duplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::NotDuplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::BeginsWith:
+ pRet = "beginsWith";
+ break;
+ case ScConditionMode::EndsWith:
+ pRet = "endsWith";
+ break;
+ case ScConditionMode::ContainsText:
+ pRet = "containsText";
+ break;
+ case ScConditionMode::NotContainsText:
+ pRet = "notContains";
+ break;
+ case ScConditionMode::Direct:
+ break;
+ case ScConditionMode::NONE:
+ default:
+ break;
+ }
+ return pRet;
+}
+
+const char* GetTypeString(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Direct:
+ return "expression";
+ case ScConditionMode::Top10:
+ case ScConditionMode::TopPercent:
+ case ScConditionMode::Bottom10:
+ case ScConditionMode::BottomPercent:
+ return "top10";
+ case ScConditionMode::AboveAverage:
+ case ScConditionMode::BelowAverage:
+ case ScConditionMode::AboveEqualAverage:
+ case ScConditionMode::BelowEqualAverage:
+ return "aboveAverage";
+ case ScConditionMode::NotDuplicate:
+ return "uniqueValues";
+ case ScConditionMode::Duplicate:
+ return "duplicateValues";
+ case ScConditionMode::Error:
+ return "containsErrors";
+ case ScConditionMode::NoError:
+ return "notContainsErrors";
+ case ScConditionMode::BeginsWith:
+ return "beginsWith";
+ case ScConditionMode::EndsWith:
+ return "endsWith";
+ case ScConditionMode::ContainsText:
+ return "containsText";
+ case ScConditionMode::NotContainsText:
+ return "notContainsText";
+ default:
+ return "cellIs";
+ }
+}
+
+bool IsTopBottomRule(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Top10:
+ case ScConditionMode::Bottom10:
+ case ScConditionMode::TopPercent:
+ case ScConditionMode::BottomPercent:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool IsTextRule(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool RequiresFormula(ScConditionMode eMode)
+{
+ if (IsTopBottomRule(eMode))
+ return false;
+ else if (IsTextRule(eMode))
+ return false;
+
+ switch (eMode)
+ {
+ case ScConditionMode::NoError:
+ case ScConditionMode::Error:
+ case ScConditionMode::Duplicate:
+ case ScConditionMode::NotDuplicate:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool RequiresFixedFormula(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::NoError:
+ case ScConditionMode::Error:
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, std::string_view rText)
+{
+ OStringBuffer aBuffer;
+ XclXmlUtils::ToOString(aBuffer, rAddress);
+ OString aPos = aBuffer.makeStringAndClear();
+ switch (eMode)
+ {
+ case ScConditionMode::Error:
+ return OString("ISERROR(" + aPos + ")") ;
+ case ScConditionMode::NoError:
+ return OString("NOT(ISERROR(" + aPos + "))") ;
+ case ScConditionMode::BeginsWith:
+ return OString("LEFT(" + aPos + ",LEN(\"" + rText + "\"))=\"" + rText + "\"");
+ case ScConditionMode::EndsWith:
+ return OString("RIGHT(" + aPos +",LEN(\"" + rText + "\"))=\"" + rText + "\"");
+ case ScConditionMode::ContainsText:
+ return OString(OString::Concat("NOT(ISERROR(SEARCH(\"") + rText + "\"," + aPos + ")))");
+ case ScConditionMode::NotContainsText:
+ return OString(OString::Concat("ISERROR(SEARCH(\"") + rText + "\"," + aPos + "))");
+ default:
+ break;
+ }
+
+ return "";
+}
+
+}
+
+void XclExpCFImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ bool bFmla2 = false;
+ ScConditionMode eOperation = mrFormatEntry.GetOperation();
+ bool bAboveAverage = eOperation == ScConditionMode::AboveAverage ||
+ eOperation == ScConditionMode::AboveEqualAverage;
+ bool bEqualAverage = eOperation == ScConditionMode::AboveEqualAverage ||
+ eOperation == ScConditionMode::BelowEqualAverage;
+ bool bBottom = eOperation == ScConditionMode::Bottom10
+ || eOperation == ScConditionMode::BottomPercent;
+ bool bPercent = eOperation == ScConditionMode::TopPercent ||
+ eOperation == ScConditionMode::BottomPercent;
+ OUString aRank("0");
+ if(IsTopBottomRule(eOperation))
+ {
+ // position and formula grammar are not important
+ // we only store a number there
+ aRank = mrFormatEntry.GetExpression(ScAddress(0,0,0), 0);
+ }
+ OString aText;
+ if(IsTextRule(eOperation))
+ {
+ // we need to write the text without quotes
+ // we have to actually get the string from
+ // the token array for that
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
+ if(pTokenArray->GetLen())
+ aText = pTokenArray->FirstToken()->GetString().getString().toUtf8();
+ }
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, GetTypeString( mrFormatEntry.GetOperation() ),
+ XML_priority, OString::number(mnPriority + 1),
+ XML_operator, GetOperatorString( mrFormatEntry.GetOperation(), bFmla2 ),
+ XML_aboveAverage, ToPsz10(bAboveAverage),
+ XML_equalAverage, ToPsz10(bEqualAverage),
+ XML_bottom, ToPsz10(bBottom),
+ XML_percent, ToPsz10(bPercent),
+ XML_rank, aRank,
+ XML_text, aText,
+ XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyle())) );
+
+ if (RequiresFixedFormula(eOperation))
+ {
+ rWorksheet->startElement(XML_formula);
+ OString aFormula = GetFixedFormula(eOperation, maOrigin, aText);
+ rWorksheet->writeEscaped(aFormula.getStr());
+ rWorksheet->endElement( XML_formula );
+ }
+ else if(RequiresFormula(eOperation))
+ {
+ rWorksheet->startElement(XML_formula);
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormatEntry.CreateFlatCopiedTokenArray(0));
+ rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
+ pTokenArray.get()));
+ rWorksheet->endElement( XML_formula );
+ if (bFmla2)
+ {
+ rWorksheet->startElement(XML_formula);
+ std::unique_ptr<ScTokenArray> pTokenArray2(mrFormatEntry.CreateFlatCopiedTokenArray(1));
+ rWorksheet->writeEscaped(XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormatEntry.GetValidSrcPos(),
+ pTokenArray2.get()));
+ rWorksheet->endElement( XML_formula );
+ }
+ }
+ // OOXTODO: XML_extLst
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpCF::XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin ) :
+ XclExpRecord( EXC_ID_CF ),
+ XclExpRoot( rRoot ),
+ mxImpl( new XclExpCFImpl( rRoot, rFormatEntry, nPriority, aOrigin ) )
+{
+}
+
+XclExpCF::~XclExpCF()
+{
+}
+
+void XclExpCF::WriteBody( XclExpStream& rStrm )
+{
+ mxImpl->WriteBody( rStrm );
+}
+
+void XclExpCF::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+XclExpDateFormat::XclExpDateFormat( const XclExpRoot& rRoot, const ScCondDateFormatEntry& rFormatEntry, sal_Int32 nPriority ):
+ XclExpRecord( EXC_ID_CF ),
+ XclExpRoot( rRoot ),
+ mrFormatEntry(rFormatEntry),
+ mnPriority(nPriority)
+{
+}
+
+XclExpDateFormat::~XclExpDateFormat()
+{
+}
+
+namespace {
+
+const char* getTimePeriodString( condformat::ScCondFormatDateType eType )
+{
+ switch(eType)
+ {
+ case condformat::TODAY:
+ return "today";
+ case condformat::YESTERDAY:
+ return "yesterday";
+ case condformat::TOMORROW:
+ return "yesterday";
+ case condformat::THISWEEK:
+ return "thisWeek";
+ case condformat::LASTWEEK:
+ return "lastWeek";
+ case condformat::NEXTWEEK:
+ return "nextWeek";
+ case condformat::THISMONTH:
+ return "thisMonth";
+ case condformat::LASTMONTH:
+ return "lastMonth";
+ case condformat::NEXTMONTH:
+ return "nextMonth";
+ case condformat::LAST7DAYS:
+ return "last7Days";
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+}
+
+void XclExpDateFormat::SaveXml( XclExpXmlStream& rStrm )
+{
+ // only write the supported entries into OOXML
+ const char* sTimePeriod = getTimePeriodString(mrFormatEntry.GetDateType());
+ if(!sTimePeriod)
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "timePeriod",
+ XML_priority, OString::number(mnPriority + 1),
+ XML_timePeriod, sTimePeriod,
+ XML_dxfId, OString::number(GetDxfs().GetDxfId(mrFormatEntry.GetStyleName())) );
+ rWorksheet->endElement( XML_cfRule);
+}
+
+XclExpCfvo::XclExpCfvo(const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rAddr, bool bFirst):
+ XclExpRoot( rRoot ),
+ mrEntry(rEntry),
+ maSrcPos(rAddr),
+ mbFirst(bFirst)
+{
+}
+
+namespace {
+
+OString getColorScaleType( const ScColorScaleEntry& rEntry, bool bFirst )
+{
+ switch(rEntry.GetType())
+ {
+ case COLORSCALE_MIN:
+ return "min";
+ case COLORSCALE_MAX:
+ return "max";
+ case COLORSCALE_PERCENT:
+ return "percent";
+ case COLORSCALE_FORMULA:
+ return "formula";
+ case COLORSCALE_AUTO:
+ if(bFirst)
+ return "min";
+ else
+ return "max";
+ case COLORSCALE_PERCENTILE:
+ return "percentile";
+ default:
+ break;
+ }
+
+ return "num";
+}
+
+}
+
+void XclExpCfvo::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ OString aValue;
+ if(mrEntry.GetType() == COLORSCALE_FORMULA)
+ {
+ OUString aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), maSrcPos,
+ mrEntry.GetFormula());
+ aValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
+ }
+ else
+ {
+ aValue = OString::number( mrEntry.GetValue() );
+ }
+
+ rWorksheet->startElement( XML_cfvo,
+ XML_type, getColorScaleType(mrEntry, mbFirst),
+ XML_val, aValue );
+
+ rWorksheet->endElement( XML_cfvo );
+}
+
+XclExpColScaleCol::XclExpColScaleCol( const XclExpRoot& rRoot, const Color& rColor ):
+ XclExpRoot( rRoot ),
+ mrColor( rColor )
+{
+}
+
+XclExpColScaleCol::~XclExpColScaleCol()
+{
+}
+
+void XclExpColScaleCol::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(XML_color, XML_rgb, XclXmlUtils::ToOString(mrColor));
+
+ rWorksheet->endElement( XML_color );
+}
+
+namespace {
+
+OString createHexStringFromDigit(sal_uInt8 nDigit)
+{
+ OString aString = OString::number( nDigit, 16 );
+ if(aString.getLength() == 1)
+ aString += OString::number(0);
+ return aString;
+}
+
+OString createGuidStringFromInt(sal_uInt8 nGuid[16])
+{
+ OStringBuffer aBuffer;
+ aBuffer.append('{');
+ for(size_t i = 0; i < 16; ++i)
+ {
+ aBuffer.append(createHexStringFromDigit(nGuid[i]));
+ if(i == 3|| i == 5 || i == 7 || i == 9 )
+ aBuffer.append('-');
+ }
+ aBuffer.append('}');
+ OString aString = aBuffer.makeStringAndClear();
+ return aString.toAsciiUpperCase();
+}
+
+OString generateGUIDString()
+{
+ sal_uInt8 nGuid[16];
+ rtl_createUuid(nGuid, nullptr, true);
+ return createGuidStringFromInt(nGuid);
+}
+
+}
+
+XclExpCondfmt::XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat, const XclExtLstRef& xExtLst, sal_Int32& rIndex ) :
+ XclExpRecord( EXC_ID_CONDFMT ),
+ XclExpRoot( rRoot )
+{
+ const ScRangeList& aScRanges = rCondFormat.GetRange();
+ GetAddressConverter().ConvertRangeList( maXclRanges, aScRanges, true );
+ if( maXclRanges.empty() )
+ return;
+
+ std::vector<XclExpExtCondFormatData> aExtEntries;
+ ScAddress aOrigin = aScRanges.Combine().aStart;
+ for( size_t nIndex = 0, nCount = rCondFormat.size(); nIndex < nCount; ++nIndex )
+ if( const ScFormatEntry* pFormatEntry = rCondFormat.GetEntry( nIndex ) )
+ {
+ if(pFormatEntry->GetType() == ScFormatEntry::Type::Condition)
+ maCFList.AppendNewRecord( new XclExpCF( GetRoot(), static_cast<const ScCondFormatEntry&>(*pFormatEntry), ++rIndex, aOrigin ) );
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ const ScCondFormatEntry& rFormat = static_cast<const ScCondFormatEntry&>(*pFormatEntry);
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = ++rIndex;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rFormat;
+ aExtEntries.push_back(aExtEntry);
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
+ maCFList.AppendNewRecord( new XclExpColorScale( GetRoot(), static_cast<const ScColorScaleFormat&>(*pFormatEntry), ++rIndex ) );
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
+ {
+ const ScDataBarFormat& rFormat = static_cast<const ScDataBarFormat&>(*pFormatEntry);
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = -1;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rFormat;
+ aExtEntries.push_back(aExtEntry);
+
+ maCFList.AppendNewRecord( new XclExpDataBar( GetRoot(), rFormat, ++rIndex, aExtEntry.aGUID));
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
+ {
+ // don't export iconSet entries that are not in OOXML
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
+ bool bNeedsExt = false;
+ switch (rIconSet.GetIconSetData()->eIconSetType)
+ {
+ case IconSet_3Smilies:
+ case IconSet_3ColorSmilies:
+ case IconSet_3Stars:
+ case IconSet_3Triangles:
+ case IconSet_5Boxes:
+ {
+ bNeedsExt = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ bNeedsExt |= rIconSet.GetIconSetData()->mbCustom;
+
+ if (bNeedsExt)
+ {
+ XclExpExtCondFormatData aExtEntry;
+ aExtEntry.nPriority = ++rIndex;
+ aExtEntry.aGUID = generateGUIDString();
+ aExtEntry.pEntry = &rIconSet;
+ aExtEntries.push_back(aExtEntry);
+ }
+ else
+ maCFList.AppendNewRecord( new XclExpIconSet( GetRoot(), rIconSet, ++rIndex ) );
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
+ maCFList.AppendNewRecord( new XclExpDateFormat( GetRoot(), static_cast<const ScCondDateFormatEntry&>(*pFormatEntry), ++rIndex ) );
+ }
+ aScRanges.Format( msSeqRef, ScRefFlags::VALID, GetDoc(), formula::FormulaGrammar::CONV_XL_OOX, ' ', true );
+
+ if(!aExtEntries.empty() && xExtLst)
+ {
+ XclExpExt* pParent = xExtLst->GetItem( XclExpExtDataBarType );
+ if( !pParent )
+ {
+ xExtLst->AddRecord( new XclExpExtCondFormat( *xExtLst ) );
+ pParent = xExtLst->GetItem( XclExpExtDataBarType );
+ }
+ static_cast<XclExpExtCondFormat*>(xExtLst->GetItem( XclExpExtDataBarType ))->AddRecord(
+ new XclExpExtConditionalFormatting( *pParent, aExtEntries, aScRanges));
+ }
+}
+
+XclExpCondfmt::~XclExpCondfmt()
+{
+}
+
+bool XclExpCondfmt::IsValidForBinary() const
+{
+ // ccf (2 bytes): An unsigned integer that specifies the count of CF records that follow this
+ // record. MUST be greater than or equal to 0x0001, and less than or equal to 0x0003.
+
+ SAL_WARN_IF( maCFList.GetSize() > 3, "sc.filter", "More than 3 conditional filters for cell(s), won't export");
+
+ return !maCFList.IsEmpty() && maCFList.GetSize() <= 3 && !maXclRanges.empty();
+}
+
+bool XclExpCondfmt::IsValidForXml() const
+{
+ return !maCFList.IsEmpty() && !maXclRanges.empty();
+}
+
+void XclExpCondfmt::Save( XclExpStream& rStrm )
+{
+ if( IsValidForBinary() )
+ {
+ XclExpRecord::Save( rStrm );
+ maCFList.Save( rStrm );
+ }
+}
+
+void XclExpCondfmt::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !maCFList.IsEmpty(), "XclExpCondfmt::WriteBody - no CF records to write" );
+ OSL_ENSURE( !maXclRanges.empty(), "XclExpCondfmt::WriteBody - no cell ranges found" );
+
+ rStrm << static_cast< sal_uInt16 >( maCFList.GetSize() )
+ << sal_uInt16( 1 )
+ << maXclRanges.GetEnclosingRange()
+ << maXclRanges;
+}
+
+void XclExpCondfmt::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !IsValidForXml() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_conditionalFormatting,
+ XML_sqref, msSeqRef
+ // OOXTODO: XML_pivot
+ );
+
+ maCFList.SaveXml( rStrm );
+
+ rWorksheet->endElement( XML_conditionalFormatting );
+}
+
+XclExpColorScale::XclExpColorScale( const XclExpRoot& rRoot, const ScColorScaleFormat& rFormat, sal_Int32 nPriority ):
+ XclExpRoot( rRoot ),
+ mnPriority( nPriority )
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ for(const auto& rxColorScaleEntry : rFormat)
+ {
+ // exact position is not important, we allow only absolute refs
+
+ XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *rxColorScaleEntry, aAddr ) );
+ maCfvoList.AppendRecord( xCfvo );
+ XclExpColScaleColList::RecordRefType xClo( new XclExpColScaleCol( GetRoot(), rxColorScaleEntry->GetColor() ) );
+ maColList.AppendRecord( xClo );
+ }
+}
+
+void XclExpColorScale::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "colorScale",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ rWorksheet->startElement(XML_colorScale);
+
+ maCfvoList.SaveXml(rStrm);
+ maColList.SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_colorScale );
+
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpDataBar::XclExpDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, sal_Int32 nPriority, const OString& rGUID):
+ XclExpRoot( rRoot ),
+ mrFormat( rFormat ),
+ mnPriority( nPriority ),
+ maGUID(rGUID)
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ // exact position is not important, we allow only absolute refs
+ mpCfvoLowerLimit.reset(
+ new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpLowerLimit, aAddr, true));
+ mpCfvoUpperLimit.reset(
+ new XclExpCfvo(GetRoot(), *mrFormat.GetDataBarData()->mpUpperLimit, aAddr, false));
+
+ mpCol.reset( new XclExpColScaleCol( GetRoot(), mrFormat.GetDataBarData()->maPositiveColor ) );
+}
+
+void XclExpDataBar::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "dataBar",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ rWorksheet->startElement( XML_dataBar,
+ XML_showValue, ToPsz10(!mrFormat.GetDataBarData()->mbOnlyBar),
+ XML_minLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMinLength)),
+ XML_maxLength, OString::number(sal_uInt32(mrFormat.GetDataBarData()->mnMaxLength)) );
+
+ mpCfvoLowerLimit->SaveXml(rStrm);
+ mpCfvoUpperLimit->SaveXml(rStrm);
+ mpCol->SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_dataBar );
+
+ // extLst entries for Excel 2010 and 2013
+ rWorksheet->startElement(XML_extLst);
+ rWorksheet->startElement(XML_ext,
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
+ XML_uri, "{B025F937-C7B1-47D3-B67F-A62EFF666E3E}");
+
+ rWorksheet->startElementNS( XML_x14, XML_id );
+ rWorksheet->write(maGUID);
+ rWorksheet->endElementNS( XML_x14, XML_id );
+
+ rWorksheet->endElement( XML_ext );
+ rWorksheet->endElement( XML_extLst );
+
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpIconSet::XclExpIconSet( const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, sal_Int32 nPriority ):
+ XclExpRoot( rRoot ),
+ mrFormat( rFormat ),
+ mnPriority( nPriority )
+{
+ const ScRange & rRange = rFormat.GetRange().front();
+ ScAddress aAddr = rRange.aStart;
+ for (auto const& itr : rFormat)
+ {
+ // exact position is not important, we allow only absolute refs
+
+ XclExpCfvoList::RecordRefType xCfvo( new XclExpCfvo( GetRoot(), *itr, aAddr ) );
+ maCfvoList.AppendRecord( xCfvo );
+ }
+}
+
+void XclExpIconSet::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement( XML_cfRule,
+ XML_type, "iconSet",
+ XML_priority, OString::number(mnPriority + 1) );
+
+ const char* pIconSetName = ScIconSetFormat::getIconSetName(mrFormat.GetIconSetData()->eIconSetType);
+ rWorksheet->startElement( XML_iconSet,
+ XML_iconSet, pIconSetName,
+ XML_showValue, sax_fastparser::UseIf("0", !mrFormat.GetIconSetData()->mbShowValue),
+ XML_reverse, sax_fastparser::UseIf("1", mrFormat.GetIconSetData()->mbReverse));
+
+ maCfvoList.SaveXml( rStrm );
+
+ rWorksheet->endElement( XML_iconSet );
+ rWorksheet->endElement( XML_cfRule );
+}
+
+XclExpCondFormatBuffer::XclExpCondFormatBuffer( const XclExpRoot& rRoot, const XclExtLstRef& xExtLst ) :
+ XclExpRoot( rRoot )
+{
+ if( const ScConditionalFormatList* pCondFmtList = GetDoc().GetCondFormList(GetCurrScTab()) )
+ {
+ sal_Int32 nIndex = 0;
+ for( const auto& rxCondFmt : *pCondFmtList)
+ {
+ XclExpCondfmtList::RecordRefType xCondfmtRec( new XclExpCondfmt( GetRoot(), *rxCondFmt, xExtLst, nIndex ));
+ if( xCondfmtRec->IsValidForXml() )
+ maCondfmtList.AppendRecord( xCondfmtRec );
+ }
+ }
+}
+
+void XclExpCondFormatBuffer::Save( XclExpStream& rStrm )
+{
+ maCondfmtList.Save( rStrm );
+}
+
+void XclExpCondFormatBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ maCondfmtList.SaveXml( rStrm );
+}
+
+// Validation =================================================================
+
+namespace {
+
+/** Writes a formula for the DV record. */
+void lclWriteDvFormula( XclExpStream& rStrm, const XclTokenArray* pXclTokArr )
+{
+ sal_uInt16 nFmlaSize = pXclTokArr ? pXclTokArr->GetSize() : 0;
+ rStrm << nFmlaSize << sal_uInt16( 0 );
+ if( pXclTokArr )
+ pXclTokArr->WriteArray( rStrm );
+}
+
+/** Writes a formula for the DV record, based on a single string. */
+void lclWriteDvFormula( XclExpStream& rStrm, const XclExpString& rString )
+{
+ // fake a formula with a single tStr token
+ rStrm << static_cast< sal_uInt16 >( rString.GetSize() + 1 )
+ << sal_uInt16( 0 )
+ << EXC_TOKID_STR
+ << rString;
+}
+
+const char* lcl_GetValidationType( sal_uInt32 nFlags )
+{
+ switch( nFlags & EXC_DV_MODE_MASK )
+ {
+ case EXC_DV_MODE_ANY: return "none";
+ case EXC_DV_MODE_WHOLE: return "whole";
+ case EXC_DV_MODE_DECIMAL: return "decimal";
+ case EXC_DV_MODE_LIST: return "list";
+ case EXC_DV_MODE_DATE: return "date";
+ case EXC_DV_MODE_TIME: return "time";
+ case EXC_DV_MODE_TEXTLEN: return "textLength";
+ case EXC_DV_MODE_CUSTOM: return "custom";
+ }
+ return nullptr;
+}
+
+const char* lcl_GetOperatorType( sal_uInt32 nFlags )
+{
+ switch( nFlags & EXC_DV_COND_MASK )
+ {
+ case EXC_DV_COND_BETWEEN: return "between";
+ case EXC_DV_COND_NOTBETWEEN: return "notBetween";
+ case EXC_DV_COND_EQUAL: return "equal";
+ case EXC_DV_COND_NOTEQUAL: return "notEqual";
+ case EXC_DV_COND_GREATER: return "greaterThan";
+ case EXC_DV_COND_LESS: return "lessThan";
+ case EXC_DV_COND_EQGREATER: return "greaterThanOrEqual";
+ case EXC_DV_COND_EQLESS: return "lessThanOrEqual";
+ }
+ return nullptr;
+}
+
+const char* lcl_GetErrorType( sal_uInt32 nFlags )
+{
+ switch (nFlags & EXC_DV_ERROR_MASK)
+ {
+ case EXC_DV_ERROR_STOP: return "stop";
+ case EXC_DV_ERROR_WARNING: return "warning";
+ case EXC_DV_ERROR_INFO: return "information";
+ }
+ return nullptr;
+}
+
+void lcl_SetValidationText(const OUString& rText, XclExpString& rValidationText)
+{
+ if ( !rText.isEmpty() )
+ {
+ // maximum length allowed in Excel is 255 characters
+ if ( rText.getLength() > 255 )
+ {
+ OUStringBuffer aBuf( rText );
+ rValidationText.Assign(
+ comphelper::string::truncateToLength(aBuf, 255).makeStringAndClear() );
+ }
+ else
+ rValidationText.Assign( rText );
+ }
+ else
+ rValidationText.Assign( '\0' );
+}
+
+} // namespace
+
+XclExpDV::XclExpDV( const XclExpRoot& rRoot, sal_uLong nScHandle ) :
+ XclExpRecord( EXC_ID_DV ),
+ XclExpRoot( rRoot ),
+ mnFlags( 0 ),
+ mnScHandle( nScHandle )
+{
+ if( const ScValidationData* pValData = GetDoc().GetValidationEntry( mnScHandle ) )
+ {
+ // prompt box - empty string represented by single NUL character
+ OUString aTitle, aText;
+ bool bShowPrompt = pValData->GetInput( aTitle, aText );
+ lcl_SetValidationText(aTitle, maPromptTitle);
+ lcl_SetValidationText(aText, maPromptText);
+
+ // error box - empty string represented by single NUL character
+ ScValidErrorStyle eScErrorStyle;
+ bool bShowError = pValData->GetErrMsg( aTitle, aText, eScErrorStyle );
+ lcl_SetValidationText(aTitle, maErrorTitle);
+ lcl_SetValidationText(aText, maErrorText);
+
+ // flags
+ switch( pValData->GetDataMode() )
+ {
+ case SC_VALID_ANY: mnFlags |= EXC_DV_MODE_ANY; break;
+ case SC_VALID_WHOLE: mnFlags |= EXC_DV_MODE_WHOLE; break;
+ case SC_VALID_DECIMAL: mnFlags |= EXC_DV_MODE_DECIMAL; break;
+ case SC_VALID_LIST: mnFlags |= EXC_DV_MODE_LIST; break;
+ case SC_VALID_DATE: mnFlags |= EXC_DV_MODE_DATE; break;
+ case SC_VALID_TIME: mnFlags |= EXC_DV_MODE_TIME; break;
+ case SC_VALID_TEXTLEN: mnFlags |= EXC_DV_MODE_TEXTLEN; break;
+ case SC_VALID_CUSTOM: mnFlags |= EXC_DV_MODE_CUSTOM; break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown mode" );
+ }
+
+ switch( pValData->GetOperation() )
+ {
+ case ScConditionMode::NONE:
+ case ScConditionMode::Equal: mnFlags |= EXC_DV_COND_EQUAL; break;
+ case ScConditionMode::Less: mnFlags |= EXC_DV_COND_LESS; break;
+ case ScConditionMode::Greater: mnFlags |= EXC_DV_COND_GREATER; break;
+ case ScConditionMode::EqLess: mnFlags |= EXC_DV_COND_EQLESS; break;
+ case ScConditionMode::EqGreater: mnFlags |= EXC_DV_COND_EQGREATER; break;
+ case ScConditionMode::NotEqual: mnFlags |= EXC_DV_COND_NOTEQUAL; break;
+ case ScConditionMode::Between: mnFlags |= EXC_DV_COND_BETWEEN; break;
+ case ScConditionMode::NotBetween: mnFlags |= EXC_DV_COND_NOTBETWEEN; break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown condition" );
+ }
+ switch( eScErrorStyle )
+ {
+ case SC_VALERR_STOP: mnFlags |= EXC_DV_ERROR_STOP; break;
+ case SC_VALERR_WARNING: mnFlags |= EXC_DV_ERROR_WARNING; break;
+ case SC_VALERR_INFO: mnFlags |= EXC_DV_ERROR_INFO; break;
+ case SC_VALERR_MACRO:
+ // set INFO for validity with macro call, delete title
+ mnFlags |= EXC_DV_ERROR_INFO;
+ maErrorTitle.Assign( '\0' ); // contains macro name
+ break;
+ default: OSL_FAIL( "XclExpDV::XclExpDV - unknown error style" );
+ }
+ ::set_flag( mnFlags, EXC_DV_IGNOREBLANK, pValData->IsIgnoreBlank() );
+ ::set_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN, pValData->GetListType() == css::sheet::TableValidationVisibility::INVISIBLE );
+ ::set_flag( mnFlags, EXC_DV_SHOWPROMPT, bShowPrompt );
+ ::set_flag( mnFlags, EXC_DV_SHOWERROR, bShowError );
+
+ // formulas
+ XclExpFormulaCompiler& rFmlaComp = GetFormulaCompiler();
+
+ // first formula
+ std::unique_ptr< ScTokenArray > xScTokArr = pValData->CreateFlatCopiedTokenArray( 0 );
+ if (xScTokArr)
+ {
+ if( pValData->GetDataMode() == SC_VALID_LIST )
+ {
+ OUString aString;
+ if( XclTokenArrayHelper::GetStringList( aString, *xScTokArr, '\n' ) )
+ {
+ bool bList = false;
+ OUStringBuffer sListBuf;
+ OUStringBuffer sFormulaBuf;
+ sFormulaBuf.append( '"' );
+ /* Formula is a list of string tokens -> build the Excel string.
+ Data validity is BIFF8 only (important for the XclExpString object).
+ Excel uses the NUL character as string list separator. */
+ mxString1.reset( new XclExpString( XclStrFlags::EightBitLength ) );
+ if (!aString.isEmpty())
+ {
+ sal_Int32 nStringIx = 0;
+ for(;;)
+ {
+ const std::u16string_view aToken( o3tl::getToken(aString, 0, '\n', nStringIx ) );
+ if (aToken.find(',') != std::u16string_view::npos)
+ {
+ sListBuf.append('"');
+ sListBuf.append(aToken);
+ sListBuf.append('"');
+ bList = true;
+ }
+ else
+ sListBuf.append(aToken);
+ mxString1->Append( aToken );
+ sFormulaBuf.append( aToken );
+ if (nStringIx<0)
+ break;
+ sal_Unicode cUnicodeChar = 0;
+ mxString1->Append( std::u16string_view(&cUnicodeChar, 1) );
+ sFormulaBuf.append( ',' );
+ sListBuf.append( ',' );
+ }
+ }
+ ::set_flag( mnFlags, EXC_DV_STRINGLIST );
+
+ // maximum length allowed in Excel is 255 characters, and don't end with an empty token
+ // It should be 8192 but Excel doesn't accept it for unknown reason
+ // See also https://bugs.documentfoundation.org/show_bug.cgi?id=137167#c2 for more details
+ sal_uInt32 nLen = sFormulaBuf.getLength();
+ if( nLen > 256 ) // 255 + beginning quote mark
+ {
+ nLen = 256;
+ if( sFormulaBuf[nLen - 1] == ',' )
+ --nLen;
+ sFormulaBuf.truncate(nLen);
+ }
+
+ sFormulaBuf.append( '"' );
+ msFormula1 = sFormulaBuf.makeStringAndClear();
+ if (bList)
+ msList = sListBuf.makeStringAndClear();
+ else
+ sListBuf.remove(0, sListBuf.getLength());
+ }
+ else
+ {
+ /* All other formulas in validation are stored like conditional
+ formatting formulas (with tRefN/tAreaN tokens as value or
+ array class). But NOT the cell references and defined names
+ in list validation - they are stored as reference class
+ tokens... Example:
+ 1) Cell must be equal to A1 -> formula is =A1 -> writes tRefNV token
+ 2) List is taken from A1 -> formula is =A1 -> writes tRefNR token
+ Formula compiler supports this by offering two different functions
+ CreateDataValFormula() and CreateListValFormula(). */
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_LISTVAL, *xScTokArr );
+ else
+ msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+ else
+ {
+ // no list validation -> convert the formula
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr1 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
+ else
+ msFormula1 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+
+ // second formula
+ xScTokArr = pValData->CreateFlatCopiedTokenArray( 1 );
+ if (xScTokArr)
+ {
+ if(GetOutput() == EXC_OUTPUT_BINARY)
+ mxTokArr2 = rFmlaComp.CreateFormula( EXC_FMLATYPE_DATAVAL, *xScTokArr );
+ else
+ msFormula2 = XclXmlUtils::ToOUString( GetCompileFormulaContext(), pValData->GetSrcPos(),
+ xScTokArr.get());
+ }
+ }
+ else
+ {
+ OSL_FAIL( "XclExpDV::XclExpDV - missing core data" );
+ mnScHandle = ULONG_MAX;
+ }
+}
+
+XclExpDV::~XclExpDV()
+{
+}
+
+void XclExpDV::InsertCellRange( const ScRange& rRange )
+{
+ maScRanges.Join( rRange );
+}
+
+bool XclExpDV::Finalize()
+{
+ GetAddressConverter().ConvertRangeList( maXclRanges, maScRanges, true );
+ return (mnScHandle != ULONG_MAX) && !maXclRanges.empty();
+}
+
+void XclExpDV::WriteBody( XclExpStream& rStrm )
+{
+ // flags and strings
+ rStrm << mnFlags << maPromptTitle << maErrorTitle << maPromptText << maErrorText;
+ // condition formulas
+ if( mxString1 )
+ lclWriteDvFormula( rStrm, *mxString1 );
+ else
+ lclWriteDvFormula( rStrm, mxTokArr1.get() );
+ lclWriteDvFormula( rStrm, mxTokArr2.get() );
+ // cell ranges
+ rStrm << maXclRanges;
+}
+
+void XclExpDV::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_dataValidation,
+ XML_allowBlank, ToPsz( ::get_flag( mnFlags, EXC_DV_IGNOREBLANK ) ),
+ XML_error, XESTRING_TO_PSZ( maErrorText ),
+ XML_errorStyle, lcl_GetErrorType(mnFlags),
+ XML_errorTitle, XESTRING_TO_PSZ( maErrorTitle ),
+ // OOXTODO: XML_imeMode,
+ XML_operator, lcl_GetOperatorType( mnFlags ),
+ XML_prompt, XESTRING_TO_PSZ( maPromptText ),
+ XML_promptTitle, XESTRING_TO_PSZ( maPromptTitle ),
+ // showDropDown should have been showNoDropDown - check oox/xlsx import for details
+ XML_showDropDown, ToPsz( ::get_flag( mnFlags, EXC_DV_SUPPRESSDROPDOWN ) ),
+ XML_showErrorMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWERROR ) ),
+ XML_showInputMessage, ToPsz( ::get_flag( mnFlags, EXC_DV_SHOWPROMPT ) ),
+ XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maScRanges),
+ XML_type, lcl_GetValidationType(mnFlags) );
+ if (!msList.isEmpty())
+ {
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_x12ac),rStrm.getNamespaceURL(OOX_NS(x12ac)),
+ FSNS(XML_xmlns, XML_mc),rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x12ac");
+ rWorksheet->startElement(FSNS(XML_x12ac, XML_list));
+ rWorksheet->writeEscaped(msList);
+ rWorksheet->endElement(FSNS(XML_x12ac, XML_list));
+ rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Fallback));
+ rWorksheet->startElement(XML_formula1);
+ rWorksheet->writeEscaped(msFormula1);
+ rWorksheet->endElement(XML_formula1);
+ rWorksheet->endElement(FSNS(XML_mc, XML_Fallback));
+ rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
+ }
+ if (msList.isEmpty() && !msFormula1.isEmpty())
+ {
+ rWorksheet->startElement(XML_formula1);
+ rWorksheet->writeEscaped( msFormula1 );
+ rWorksheet->endElement( XML_formula1 );
+ }
+ if( !msFormula2.isEmpty() )
+ {
+ rWorksheet->startElement(XML_formula2);
+ rWorksheet->writeEscaped( msFormula2 );
+ rWorksheet->endElement( XML_formula2 );
+ }
+ rWorksheet->endElement( XML_dataValidation );
+}
+
+XclExpDval::XclExpDval( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_DVAL, 18 ),
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpDval::~XclExpDval()
+{
+}
+
+void XclExpDval::InsertCellRange( const ScRange& rRange, sal_uLong nScHandle )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ XclExpDV& rDVRec = SearchOrCreateDv( nScHandle );
+ rDVRec.InsertCellRange( rRange );
+ }
+}
+
+void XclExpDval::Save( XclExpStream& rStrm )
+{
+ // check all records
+ size_t nPos = maDVList.GetSize();
+ while( nPos )
+ {
+ --nPos; // backwards to keep nPos valid
+ XclExpDVRef xDVRec = maDVList.GetRecord( nPos );
+ if( !xDVRec->Finalize() )
+ maDVList.RemoveRecord( nPos );
+ }
+
+ // write the DVAL and the DV's
+ if( !maDVList.IsEmpty() )
+ {
+ XclExpRecord::Save( rStrm );
+ maDVList.Save( rStrm );
+ }
+}
+
+void XclExpDval::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maDVList.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_dataValidations,
+ XML_count, OString::number(maDVList.GetSize())
+ // OOXTODO: XML_disablePrompts,
+ // OOXTODO: XML_xWindow,
+ // OOXTODO: XML_yWindow
+ );
+ maDVList.SaveXml( rStrm );
+ rWorksheet->endElement( XML_dataValidations );
+}
+
+XclExpDV& XclExpDval::SearchOrCreateDv( sal_uLong nScHandle )
+{
+ // test last found record
+ if( mxLastFoundDV && (mxLastFoundDV->GetScHandle() == nScHandle) )
+ return *mxLastFoundDV;
+
+ // binary search
+ size_t nCurrPos = 0;
+ if( !maDVList.IsEmpty() )
+ {
+ size_t nFirstPos = 0;
+ size_t nLastPos = maDVList.GetSize() - 1;
+ bool bLoop = true;
+ sal_uLong nCurrScHandle = ::std::numeric_limits< sal_uLong >::max();
+ while( (nFirstPos <= nLastPos) && bLoop )
+ {
+ nCurrPos = (nFirstPos + nLastPos) / 2;
+ mxLastFoundDV = maDVList.GetRecord( nCurrPos );
+ nCurrScHandle = mxLastFoundDV->GetScHandle();
+ if( nCurrScHandle == nScHandle )
+ bLoop = false;
+ else if( nCurrScHandle < nScHandle )
+ nFirstPos = nCurrPos + 1;
+ else if( nCurrPos )
+ nLastPos = nCurrPos - 1;
+ else // special case for nLastPos = -1
+ bLoop = false;
+ }
+ if( nCurrScHandle == nScHandle )
+ return *mxLastFoundDV;
+ else if( nCurrScHandle < nScHandle )
+ ++nCurrPos;
+ }
+
+ // create new DV record
+ mxLastFoundDV = new XclExpDV( *this, nScHandle );
+ maDVList.InsertRecord( mxLastFoundDV, nCurrPos );
+ return *mxLastFoundDV;
+}
+
+void XclExpDval::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.WriteZeroBytes( 10 );
+ rStrm << EXC_DVAL_NOOBJ << static_cast< sal_uInt32 >( maDVList.GetSize() );
+}
+
+// Web Queries ================================================================
+
+XclExpWebQuery::XclExpWebQuery(
+ const OUString& rRangeName,
+ const OUString& rUrl,
+ std::u16string_view rSource,
+ sal_Int32 nRefrSecs ) :
+ maDestRange( rRangeName ),
+ maUrl( rUrl ),
+ // refresh delay time: seconds -> minutes
+ mnRefresh( ulimit_cast< sal_Int16 >( (nRefrSecs + 59) / 60 ) ),
+ mbEntireDoc( false )
+{
+ // comma separated list of HTML table names or indexes
+ OUString aNewTables;
+ OUString aAppendTable;
+ bool bExitLoop = false;
+ if (!rSource.empty())
+ {
+ sal_Int32 nStringIx = 0;
+ do
+ {
+ OUString aToken( o3tl::getToken(rSource, 0, ';', nStringIx ) );
+ mbEntireDoc = ScfTools::IsHTMLDocName( aToken );
+ bExitLoop = mbEntireDoc || ScfTools::IsHTMLTablesName( aToken );
+ if( !bExitLoop && ScfTools::GetHTMLNameFromName( aToken, aAppendTable ) )
+ aNewTables = ScGlobal::addToken( aNewTables, aAppendTable, ',' );
+ }
+ while (nStringIx>0 && !bExitLoop);
+ }
+
+ if( !bExitLoop ) // neither HTML_all nor HTML_tables found
+ {
+ if( !aNewTables.isEmpty() )
+ mxQryTables.reset( new XclExpString( aNewTables ) );
+ else
+ mbEntireDoc = true;
+ }
+}
+
+XclExpWebQuery::~XclExpWebQuery()
+{
+}
+
+void XclExpWebQuery::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( !mbEntireDoc || !mxQryTables, "XclExpWebQuery::Save - illegal mode" );
+ sal_uInt16 nFlags;
+
+ // QSI record
+ rStrm.StartRecord( EXC_ID_QSI, 10 + maDestRange.GetSize() );
+ rStrm << EXC_QSI_DEFAULTFLAGS
+ << sal_uInt16( 0x0010 )
+ << sal_uInt16( 0x0012 )
+ << sal_uInt32( 0x00000000 )
+ << maDestRange;
+ rStrm.EndRecord();
+
+ // PARAMQRY record
+ nFlags = 0;
+ ::insert_value( nFlags, EXC_PQRYTYPE_WEBQUERY, 0, 3 );
+ ::set_flag( nFlags, EXC_PQRY_WEBQUERY );
+ ::set_flag( nFlags, EXC_PQRY_TABLES, !mbEntireDoc );
+ rStrm.StartRecord( EXC_ID_PQRY, 12 );
+ rStrm << nFlags
+ << sal_uInt16( 0x0000 )
+ << sal_uInt16( 0x0001 );
+ rStrm.WriteZeroBytes( 6 );
+ rStrm.EndRecord();
+
+ // WQSTRING record
+ rStrm.StartRecord( EXC_ID_WQSTRING, maUrl.GetSize() );
+ rStrm << maUrl;
+ rStrm.EndRecord();
+
+ // unknown record 0x0802
+ rStrm.StartRecord( EXC_ID_0802, 16 + maDestRange.GetSize() );
+ rStrm << EXC_ID_0802; // repeated record id ?!?
+ rStrm.WriteZeroBytes( 6 );
+ rStrm << sal_uInt16( 0x0003 )
+ << sal_uInt32( 0x00000000 )
+ << sal_uInt16( 0x0010 )
+ << maDestRange;
+ rStrm.EndRecord();
+
+ // WEBQRYSETTINGS record
+ nFlags = mxQryTables ? EXC_WQSETT_SPECTABLES : EXC_WQSETT_ALL;
+ rStrm.StartRecord( EXC_ID_WQSETT, 28 );
+ rStrm << EXC_ID_WQSETT // repeated record id ?!?
+ << sal_uInt16( 0x0000 )
+ << sal_uInt16( 0x0004 )
+ << sal_uInt16( 0x0000 )
+ << EXC_WQSETT_DEFAULTFLAGS
+ << nFlags;
+ rStrm.WriteZeroBytes( 10 );
+ rStrm << mnRefresh // refresh delay in minutes
+ << EXC_WQSETT_FORMATFULL
+ << sal_uInt16( 0x0000 );
+ rStrm.EndRecord();
+
+ // WEBQRYTABLES record
+ if( mxQryTables )
+ {
+ rStrm.StartRecord( EXC_ID_WQTABLES, 4 + mxQryTables->GetSize() );
+ rStrm << EXC_ID_WQTABLES // repeated record id ?!?
+ << sal_uInt16( 0x0000 )
+ << *mxQryTables; // comma separated list of source tables
+ rStrm.EndRecord();
+ }
+}
+
+XclExpWebQueryBuffer::XclExpWebQueryBuffer( const XclExpRoot& rRoot )
+{
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ SfxObjectShell* pShell = rRoot.GetDocShell();
+ if( !pShell ) return;
+ ScfPropertySet aModelProp( pShell->GetModel() );
+ if( !aModelProp.Is() ) return;
+
+ Reference< XAreaLinks > xAreaLinks;
+ aModelProp.GetProperty( xAreaLinks, SC_UNO_AREALINKS );
+ if( !xAreaLinks.is() ) return;
+
+ for( sal_Int32 nIndex = 0, nCount = xAreaLinks->getCount(); nIndex < nCount; ++nIndex )
+ {
+ Reference< XAreaLink > xAreaLink( xAreaLinks->getByIndex( nIndex ), UNO_QUERY );
+ if( xAreaLink.is() )
+ {
+ CellRangeAddress aDestRange( xAreaLink->getDestArea() );
+ if( static_cast< SCTAB >( aDestRange.Sheet ) == nScTab )
+ {
+ ScfPropertySet aLinkProp( xAreaLink );
+ OUString aFilter;
+ if( aLinkProp.GetProperty( aFilter, SC_UNONAME_FILTER ) &&
+ (aFilter == EXC_WEBQRY_FILTER) )
+ {
+ // get properties
+ OUString /*aFilterOpt,*/ aUrl;
+ sal_Int32 nRefresh = 0;
+
+// aLinkProp.GetProperty( aFilterOpt, SC_UNONAME_FILTOPT );
+ aLinkProp.GetProperty( aUrl, SC_UNONAME_LINKURL );
+ aLinkProp.GetProperty( nRefresh, SC_UNONAME_REFDELAY );
+
+ OUString aAbsDoc( ScGlobal::GetAbsDocName( aUrl, pShell ) );
+ INetURLObject aUrlObj( aAbsDoc );
+ OUString aWebQueryUrl( aUrlObj.getFSysPath( FSysStyle::Dos ) );
+ if( aWebQueryUrl.isEmpty() )
+ aWebQueryUrl = aAbsDoc;
+
+ // find range or create a new range
+ OUString aRangeName;
+ ScRange aScDestRange;
+ ScUnoConversion::FillScRange( aScDestRange, aDestRange );
+ if( const ScRangeData* pRangeData = rRoot.GetNamedRanges().findByRange( aScDestRange ) )
+ {
+ aRangeName = pRangeData->GetName();
+ }
+ else
+ {
+ XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
+ XclExpNameManager& rNameMgr = rRoot.GetNameManager();
+
+ // create a new unique defined name containing the range
+ XclTokenArrayRef xTokArr = rFmlaComp.CreateFormula( EXC_FMLATYPE_WQUERY, aScDestRange );
+ sal_uInt16 nNameIdx = rNameMgr.InsertUniqueName( aUrlObj.getBase(), xTokArr, nScTab );
+ aRangeName = rNameMgr.GetOrigName( nNameIdx );
+ }
+
+ // create and store the web query record
+ if( !aRangeName.isEmpty() )
+ AppendNewRecord( new XclExpWebQuery(
+ aRangeName, aWebQueryUrl, xAreaLink->getSourceArea(), nRefresh ) );
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xedbdata.cxx b/sc/source/filter/excel/xedbdata.cxx
new file mode 100644
index 000000000..350f6f70e
--- /dev/null
+++ b/sc/source/filter/excel/xedbdata.cxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <xedbdata.hxx>
+#include <excrecds.hxx>
+#include <dbdata.hxx>
+#include <document.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+
+using namespace oox;
+
+namespace {
+
+/** (So far) dummy implementation of table export for BIFF5/BIFF7. */
+class XclExpTablesImpl5 : public XclExpTables
+{
+public:
+ explicit XclExpTablesImpl5( const XclExpRoot& rRoot );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+/** Implementation of table export for OOXML, so far dummy for BIFF8. */
+class XclExpTablesImpl8 : public XclExpTables
+{
+public:
+ explicit XclExpTablesImpl8( const XclExpRoot& rRoot );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+}
+
+XclExpTablesImpl5::XclExpTablesImpl5( const XclExpRoot& rRoot ) :
+ XclExpTables( rRoot )
+{
+}
+
+void XclExpTablesImpl5::Save( XclExpStream& /*rStrm*/ )
+{
+ // not implemented
+}
+
+void XclExpTablesImpl5::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+ // not applicable
+}
+
+
+XclExpTablesImpl8::XclExpTablesImpl8( const XclExpRoot& rRoot ) :
+ XclExpTables( rRoot )
+{
+}
+
+void XclExpTablesImpl8::Save( XclExpStream& /*rStrm*/ )
+{
+ // not implemented
+}
+
+void XclExpTablesImpl8::SaveXml( XclExpXmlStream& rStrm )
+{
+
+ sax_fastparser::FSHelperPtr& pWorksheetStrm = rStrm.GetCurrentStream();
+ pWorksheetStrm->startElement(XML_tableParts);
+ for (auto const& it : maTables)
+ {
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pTableStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/tables/", "table", it.mnTableId),
+ XclXmlUtils::GetStreamName("../tables/", "table", it.mnTableId),
+ pWorksheetStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("table"),
+ CREATE_OFFICEDOC_RELATION_TYPE("table"),
+ &aRelId);
+
+ pWorksheetStrm->singleElement(XML_tablePart, FSNS(XML_r, XML_id), aRelId.toUtf8());
+
+ rStrm.PushStream( pTableStrm);
+ SaveTableXml( rStrm, it);
+ rStrm.PopStream();
+ }
+ pWorksheetStrm->endElement( XML_tableParts);
+}
+
+
+XclExpTablesManager::XclExpTablesManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpTablesManager::~XclExpTablesManager()
+{
+}
+
+void XclExpTablesManager::Initialize()
+{
+ // All non-const to be able to call RefreshTableColumnNames().
+ ScDocument& rDoc = GetDoc();
+ ScDBCollection* pDBColl = rDoc.GetDBCollection();
+ if (!pDBColl)
+ return;
+
+ ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs();
+ if (rDBs.empty())
+ return;
+
+ sal_Int32 nTableId = 0;
+ for (const auto& rxDB : rDBs)
+ {
+ ScDBData* pDBData = rxDB.get();
+ pDBData->RefreshTableColumnNames( &rDoc); // currently not in sync, so refresh
+ ScRange aRange( ScAddress::UNINITIALIZED);
+ pDBData->GetArea( aRange);
+ SCTAB nTab = aRange.aStart.Tab();
+ TablesMapType::iterator it = maTablesMap.find( nTab);
+ if (it == maTablesMap.end())
+ {
+ rtl::Reference< XclExpTables > pNew;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ pNew = new XclExpTablesImpl5( GetRoot());
+ break;
+ case EXC_BIFF8:
+ pNew = new XclExpTablesImpl8( GetRoot());
+ break;
+ default:
+ assert(!"Unknown BIFF type!");
+ continue; // for
+ }
+ ::std::pair< TablesMapType::iterator, bool > ins( maTablesMap.insert( ::std::make_pair( nTab, pNew)));
+ if (!ins.second)
+ {
+ assert(!"XclExpTablesManager::Initialize - XclExpTables insert failed");
+ continue; // for
+ }
+ it = ins.first;
+ }
+ it->second->AppendTable( pDBData, ++nTableId);
+ }
+}
+
+rtl::Reference< XclExpTables > XclExpTablesManager::GetTablesBySheet( SCTAB nTab )
+{
+ TablesMapType::iterator it = maTablesMap.find(nTab);
+ return it == maTablesMap.end() ? nullptr : it->second;
+}
+
+XclExpTables::Entry::Entry( const ScDBData* pData, sal_Int32 nTableId ) :
+ mpData(pData), mnTableId(nTableId)
+{
+}
+
+XclExpTables::XclExpTables( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot)
+{
+}
+
+XclExpTables::~XclExpTables()
+{
+}
+
+void XclExpTables::AppendTable( const ScDBData* pData, sal_Int32 nTableId )
+{
+ maTables.emplace_back( pData, nTableId);
+}
+
+void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry )
+{
+ const ScDBData& rData = *rEntry.mpData;
+ ScRange aRange( ScAddress::UNINITIALIZED);
+ rData.GetArea( aRange);
+ sax_fastparser::FSHelperPtr& pTableStrm = rStrm.GetCurrentStream();
+ pTableStrm->startElement( XML_table,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ XML_id, OString::number( rEntry.mnTableId),
+ XML_name, rData.GetName().toUtf8(),
+ XML_displayName, rData.GetName().toUtf8(),
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aRange),
+ XML_headerRowCount, ToPsz10(rData.HasHeader()),
+ XML_totalsRowCount, ToPsz10(rData.HasTotals()),
+ XML_totalsRowShown, ToPsz10(rData.HasTotals()) // we don't support that but if there are totals they are shown
+ // OOXTODO: XML_comment, ...,
+ // OOXTODO: XML_connectionId, ...,
+ // OOXTODO: XML_dataCellStyle, ...,
+ // OOXTODO: XML_dataDxfId, ...,
+ // OOXTODO: XML_headerRowBorderDxfId, ...,
+ // OOXTODO: XML_headerRowCellStyle, ...,
+ // OOXTODO: XML_headerRowDxfId, ...,
+ // OOXTODO: XML_insertRow, ...,
+ // OOXTODO: XML_insertRowShift, ...,
+ // OOXTODO: XML_published, ...,
+ // OOXTODO: XML_tableBorderDxfId, ...,
+ // OOXTODO: XML_tableType, ...,
+ // OOXTODO: XML_totalsRowBorderDxfId, ...,
+ // OOXTODO: XML_totalsRowCellStyle, ...,
+ // OOXTODO: XML_totalsRowDxfId, ...
+ );
+
+ if (rData.HasAutoFilter())
+ {
+ /* TODO: does this need to exclude totals row? */
+
+ /* TODO: in OOXML 12.3.21 Table Definition Part has information
+ * that an applied autoFilter has child elements
+ * <af:filterColumn><af:filters><af:filter>.
+ * When not applied but buttons hidden, Excel writes, for example,
+ * <filterColumn colId="0" hiddenButton="1"/> */
+
+ ExcAutoFilterRecs aAutoFilter( rStrm.GetRoot(), aRange.aStart.Tab(), &rData);
+ aAutoFilter.SaveXml( rStrm);
+ }
+
+ const std::vector< OUString >& rColNames = rData.GetTableColumnNames();
+ if (!rColNames.empty())
+ {
+ pTableStrm->startElement(XML_tableColumns,
+ XML_count, OString::number(aRange.aEnd.Col() - aRange.aStart.Col() + 1));
+
+ for (size_t i=0, n=rColNames.size(); i < n; ++i)
+ {
+ // OOXTODO: write <calculatedColumnFormula> once we support it, in
+ // which case we'd need start/endElement XML_tableColumn for such
+ // column.
+
+ // OOXTODO: write <totalsRowFormula> once we support it.
+
+ pTableStrm->singleElement( XML_tableColumn,
+ XML_id, OString::number(i+1),
+ XML_name, rColNames[i].toUtf8()
+ // OOXTODO: XML_dataCellStyle, ...,
+ // OOXTODO: XML_dataDxfId, ...,
+ // OOXTODO: XML_headerRowCellStyle, ...,
+ // OOXTODO: XML_headerRowDxfId, ...,
+ // OOXTODO: XML_queryTableFieldId, ...,
+ // OOXTODO: XML_totalsRowCellStyle, ...,
+ // OOXTODO: XML_totalsRowDxfId, ...,
+ // OOXTODO: XML_totalsRowFunction, ...,
+ // OOXTODO: XML_totalsRowLabel, ...,
+ // OOXTODO: XML_uniqueName, ...
+ );
+ }
+
+ pTableStrm->endElement( XML_tableColumns);
+ }
+
+ // OOXTODO: write <tableStyleInfo> once we have table styles.
+
+ pTableStrm->endElement( XML_table);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeescher.cxx b/sc/source/filter/excel/xeescher.cxx
new file mode 100644
index 000000000..d166b172a
--- /dev/null
+++ b/sc/source/filter/excel/xeescher.cxx
@@ -0,0 +1,2046 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xeescher.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/awt/ScrollBarOrientation.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+
+#include <set>
+#include <vcl/BitmapReadAccess.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/outlobj.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <svtools/embedhlp.hxx>
+
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <postit.hxx>
+
+#include <fapihelper.hxx>
+#include <xcl97esc.hxx>
+#include <xechart.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xestyle.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+#include <userdat.hxx>
+#include <drwlayer.hxx>
+#include <svl/itemset.hxx>
+#include <svx/sdtaitm.hxx>
+#include <document.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/drawingml.hxx>
+#include <oox/export/chartexport.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::lang::XServiceInfo;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XShapes;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::form::binding::XBindableValue;
+using ::com::sun::star::form::binding::XListEntrySink;
+using ::com::sun::star::script::ScriptEventDescriptor;
+using ::com::sun::star::table::CellAddress;
+using ::com::sun::star::table::CellRangeAddress;
+using ::oox::drawingml::DrawingML;
+using ::oox::drawingml::ChartExport;
+using namespace oox;
+
+namespace
+{
+
+const char *ToHorizAlign( SdrTextHorzAdjust eAdjust )
+{
+ switch( eAdjust )
+ {
+ case SDRTEXTHORZADJUST_CENTER:
+ return "center";
+ case SDRTEXTHORZADJUST_RIGHT:
+ return "right";
+ case SDRTEXTHORZADJUST_BLOCK:
+ return "justify";
+ case SDRTEXTHORZADJUST_LEFT:
+ default:
+ return "left";
+ }
+}
+
+const char *ToVertAlign( SdrTextVertAdjust eAdjust )
+{
+ switch( eAdjust )
+ {
+ case SDRTEXTVERTADJUST_CENTER:
+ return "center";
+ case SDRTEXTVERTADJUST_BOTTOM:
+ return "bottom";
+ case SDRTEXTVERTADJUST_BLOCK:
+ return "justify";
+ case SDRTEXTVERTADJUST_TOP:
+ default:
+ return "top";
+ }
+}
+
+void lcl_WriteAnchorVertex( sax_fastparser::FSHelperPtr const & rComments, const tools::Rectangle &aRect )
+{
+ rComments->startElement(FSNS(XML_xdr, XML_col));
+ rComments->writeEscaped( OUString::number( aRect.Left() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_col ) );
+ rComments->startElement(FSNS(XML_xdr, XML_colOff));
+ rComments->writeEscaped( OUString::number( aRect.Top() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_colOff ) );
+ rComments->startElement(FSNS(XML_xdr, XML_row));
+ rComments->writeEscaped( OUString::number( aRect.Right() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_row ) );
+ rComments->startElement(FSNS(XML_xdr, XML_rowOff));
+ rComments->writeEscaped( OUString::number( aRect.Bottom() ) );
+ rComments->endElement( FSNS( XML_xdr, XML_rowOff ) );
+}
+
+tools::Long lcl_hmm2output(tools::Long value, bool bInEMU)
+{
+ return o3tl::convert(value, o3tl::Length::mm100, bInEMU ? o3tl::Length::emu : o3tl::Length::px);
+}
+
+void lcl_GetFromTo( const XclExpRoot& rRoot, const tools::Rectangle &aRect, sal_Int32 nTab, tools::Rectangle &aFrom, tools::Rectangle &aTo, bool bInEMU = false )
+{
+ sal_Int32 nCol = 0, nRow = 0;
+ sal_Int32 nColOff = 0, nRowOff= 0;
+
+ const bool bRTL = rRoot.GetDoc().IsNegativePage( nTab );
+ if (!bRTL)
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Left() <= aRect.Left() )
+ {
+ nCol++;
+ nColOff = aRect.Left() - r.Left();
+ }
+ if( r.Top() <= aRect.Top() )
+ {
+ nRow++;
+ nRowOff = aRect.Top() - r.Top();
+ }
+ if( r.Left() > aRect.Left() && r.Top() > aRect.Top() )
+ {
+ aFrom = tools::Rectangle( nCol-1, lcl_hmm2output( nColOff, bInEMU ),
+ nRow-1, lcl_hmm2output( nRowOff, bInEMU ) );
+ break;
+ }
+ }
+ }
+ else
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Left() >= aRect.Left() )
+ {
+ nCol++;
+ nColOff = r.Left() - aRect.Left();
+ }
+ if( r.Top() <= aRect.Top() )
+ {
+ nRow++;
+ nRowOff = aRect.Top() - r.Top();
+ }
+ if( r.Left() < aRect.Left() && r.Top() > aRect.Top() )
+ {
+ aFrom = tools::Rectangle( nCol-1, lcl_hmm2output( nColOff, bInEMU ),
+ nRow-1, lcl_hmm2output( nRowOff, bInEMU ) );
+ break;
+ }
+ }
+ }
+ if (!bRTL)
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Right() < aRect.Right() )
+ nCol++;
+ if( r.Bottom() < aRect.Bottom() )
+ nRow++;
+ if( r.Right() >= aRect.Right() && r.Bottom() >= aRect.Bottom() )
+ {
+ aTo = tools::Rectangle( nCol, lcl_hmm2output( aRect.Right() - r.Left(), bInEMU ),
+ nRow, lcl_hmm2output( aRect.Bottom() - r.Top(), bInEMU ));
+ break;
+ }
+ }
+ }
+ else
+ {
+ while(true)
+ {
+ tools::Rectangle r = rRoot.GetDoc().GetMMRect( nCol,nRow,nCol,nRow,nTab );
+ if( r.Right() >= aRect.Right() )
+ nCol++;
+ if( r.Bottom() < aRect.Bottom() )
+ nRow++;
+ if( r.Right() < aRect.Right() && r.Bottom() >= aRect.Bottom() )
+ {
+ aTo = tools::Rectangle( nCol, lcl_hmm2output( r.Left() - aRect.Right(), bInEMU ),
+ nRow, lcl_hmm2output( aRect.Bottom() - r.Top(), bInEMU ));
+ break;
+ }
+ }
+ }
+}
+
+} // namespace
+
+// Escher client anchor =======================================================
+
+XclExpDffAnchorBase::XclExpDffAnchorBase( const XclExpRoot& rRoot, sal_uInt16 nFlags ) :
+ XclExpRoot( rRoot ),
+ mnFlags( nFlags )
+{
+}
+
+void XclExpDffAnchorBase::SetFlags( const SdrObject& rSdrObj )
+{
+ ImplSetFlags( rSdrObj );
+}
+
+void XclExpDffAnchorBase::SetSdrObject( const SdrObject& rSdrObj )
+{
+ ImplSetFlags( rSdrObj );
+ ImplCalcAnchorRect( rSdrObj.GetCurrentBoundRect(), MapUnit::Map100thMM );
+}
+
+void XclExpDffAnchorBase::WriteDffData( EscherEx& rEscherEx ) const
+{
+ rEscherEx.AddAtom( 18, ESCHER_ClientAnchor );
+ rEscherEx.GetStream().WriteUInt16( mnFlags );
+ WriteXclObjAnchor( rEscherEx.GetStream(), maAnchor );
+}
+
+void XclExpDffAnchorBase::WriteData( EscherEx& rEscherEx, const tools::Rectangle& rRect )
+{
+ // the passed rectangle is in twips
+ ImplCalcAnchorRect( rRect, MapUnit::MapTwip );
+ WriteDffData( rEscherEx );
+}
+
+void XclExpDffAnchorBase::ImplSetFlags( const SdrObject& )
+{
+ OSL_FAIL( "XclExpDffAnchorBase::ImplSetFlags - not implemented" );
+}
+
+void XclExpDffAnchorBase::ImplCalcAnchorRect( const tools::Rectangle&, MapUnit )
+{
+ OSL_FAIL( "XclExpDffAnchorBase::ImplCalcAnchorRect - not implemented" );
+}
+
+XclExpDffSheetAnchor::XclExpDffSheetAnchor( const XclExpRoot& rRoot ) :
+ XclExpDffAnchorBase( rRoot ),
+ mnScTab( rRoot.GetCurrScTab() )
+{
+}
+
+void XclExpDffSheetAnchor::ImplSetFlags( const SdrObject& rSdrObj )
+{
+ // set flags for cell/page anchoring
+ if ( ScDrawLayer::GetAnchorType( rSdrObj ) == SCA_CELL )
+ mnFlags = 0;
+ else
+ mnFlags = EXC_ESC_ANCHOR_LOCKED;
+}
+
+void XclExpDffSheetAnchor::ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ maAnchor.SetRect( GetRoot(), mnScTab, rRect, eMapUnit );
+}
+
+XclExpDffEmbeddedAnchor::XclExpDffEmbeddedAnchor( const XclExpRoot& rRoot,
+ const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) :
+ XclExpDffAnchorBase( rRoot ),
+ maPageSize( rPageSize ),
+ mnScaleX( nScaleX ),
+ mnScaleY( nScaleY )
+{
+}
+
+void XclExpDffEmbeddedAnchor::ImplSetFlags( const SdrObject& /*rSdrObj*/ )
+{
+ // TODO (unsupported feature): fixed size
+}
+
+void XclExpDffEmbeddedAnchor::ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ maAnchor.SetRect( maPageSize, mnScaleX, mnScaleY, rRect, eMapUnit );
+}
+
+XclExpDffNoteAnchor::XclExpDffNoteAnchor( const XclExpRoot& rRoot, const tools::Rectangle& rRect ) :
+ XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_SIZELOCKED )
+{
+ maAnchor.SetRect( rRoot, rRoot.GetCurrScTab(), rRect, MapUnit::Map100thMM );
+}
+
+XclExpDffDropDownAnchor::XclExpDffDropDownAnchor( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
+ XclExpDffAnchorBase( rRoot, EXC_ESC_ANCHOR_POSLOCKED )
+{
+ GetAddressConverter().ConvertAddress( maAnchor.maFirst, rScPos, true );
+ maAnchor.maLast.mnCol = maAnchor.maFirst.mnCol + 1;
+ maAnchor.maLast.mnRow = maAnchor.maFirst.mnRow + 1;
+ maAnchor.mnLX = maAnchor.mnTY = maAnchor.mnRX = maAnchor.mnBY = 0;
+}
+
+// MSODRAWING* records ========================================================
+
+XclExpMsoDrawingBase::XclExpMsoDrawingBase( XclEscherEx& rEscherEx, sal_uInt16 nRecId ) :
+ XclExpRecord( nRecId ),
+ mrEscherEx( rEscherEx ),
+ mnFragmentKey( rEscherEx.InitNextDffFragment() )
+{
+}
+
+void XclExpMsoDrawingBase::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mrEscherEx.GetStreamPos() == mrEscherEx.GetDffFragmentPos( mnFragmentKey ),
+ "XclExpMsoDrawingBase::WriteBody - DFF stream position mismatch" );
+ rStrm.CopyFromStream( mrEscherEx.GetStream(), mrEscherEx.GetDffFragmentSize( mnFragmentKey ) );
+}
+
+XclExpMsoDrawingGroup::XclExpMsoDrawingGroup( XclEscherEx& rEscherEx ) :
+ XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWINGGROUP )
+{
+ SvStream& rDffStrm = mrEscherEx.GetStream();
+
+ // write the DGGCONTAINER with some default settings
+ mrEscherEx.OpenContainer( ESCHER_DggContainer );
+
+ // TODO: stuff the OPT atom with our own document defaults?
+ static const sal_uInt8 spnDffOpt[] = {
+ 0xBF, 0x00, 0x08, 0x00, 0x08, 0x00, 0x81, 0x01,
+ 0x09, 0x00, 0x00, 0x08, 0xC0, 0x01, 0x40, 0x00,
+ 0x00, 0x08
+ };
+ mrEscherEx.AddAtom( sizeof( spnDffOpt ), ESCHER_OPT, 3, 3 );
+ rDffStrm.WriteBytes(spnDffOpt, sizeof(spnDffOpt));
+
+ // SPLITMENUCOLORS contains colors in toolbar
+ static const sal_uInt8 spnDffSplitMenuColors[] = {
+ 0x0D, 0x00, 0x00, 0x08, 0x0C, 0x00, 0x00, 0x08,
+ 0x17, 0x00, 0x00, 0x08, 0xF7, 0x00, 0x00, 0x10
+ };
+ mrEscherEx.AddAtom( sizeof( spnDffSplitMenuColors ), ESCHER_SplitMenuColors, 0, 4 );
+ rDffStrm.WriteBytes(spnDffSplitMenuColors, sizeof(spnDffSplitMenuColors));
+
+ // close the DGGCONTAINER
+ mrEscherEx.CloseContainer();
+ mrEscherEx.UpdateDffFragmentEnd();
+}
+
+XclExpMsoDrawing::XclExpMsoDrawing( XclEscherEx& rEscherEx ) :
+ XclExpMsoDrawingBase( rEscherEx, EXC_ID_MSODRAWING )
+{
+}
+
+XclExpImgData::XclExpImgData( const Graphic& rGraphic, sal_uInt16 nRecId ) :
+ maGraphic( rGraphic ),
+ mnRecId( nRecId )
+{
+}
+
+void XclExpImgData::Save( XclExpStream& rStrm )
+{
+ Bitmap aBmp = maGraphic.GetBitmapEx().GetBitmap();
+ if (aBmp.getPixelFormat() != vcl::PixelFormat::N24_BPP)
+ aBmp.Convert( BmpConversion::N24Bit );
+
+ Bitmap::ScopedReadAccess pAccess(aBmp);
+ if( !pAccess )
+ return;
+
+ sal_Int32 nWidth = ::std::min< sal_Int32 >( pAccess->Width(), 0xFFFF );
+ sal_Int32 nHeight = ::std::min< sal_Int32 >( pAccess->Height(), 0xFFFF );
+ if( (nWidth <= 0) || (nHeight <= 0) )
+ return;
+
+ sal_uInt8 nPadding = static_cast< sal_uInt8 >( nWidth & 0x03 );
+ sal_uInt32 nTmpSize = static_cast< sal_uInt32 >( (nWidth * 3 + nPadding) * nHeight + 12 );
+
+ rStrm.StartRecord( mnRecId, nTmpSize + 4 );
+
+ rStrm << EXC_IMGDATA_BMP // BMP format
+ << EXC_IMGDATA_WIN // Windows
+ << nTmpSize // size after _this_ field
+ << sal_uInt32( 12 ) // BITMAPCOREHEADER size
+ << static_cast< sal_uInt16 >( nWidth ) // width
+ << static_cast< sal_uInt16 >( nHeight ) // height
+ << sal_uInt16( 1 ) // planes
+ << sal_uInt16( 24 ); // bits per pixel
+
+ for( sal_Int32 nY = nHeight - 1; nY >= 0; --nY )
+ {
+ Scanline pScanline = pAccess->GetScanline( nY );
+ for( sal_Int32 nX = 0; nX < nWidth; ++nX )
+ {
+ const BitmapColor& rBmpColor = pAccess->GetPixelFromData( pScanline, nX );
+ rStrm << rBmpColor.GetBlue() << rBmpColor.GetGreen() << rBmpColor.GetRed();
+ }
+ rStrm.WriteZeroBytes( nPadding );
+ }
+
+ rStrm.EndRecord();
+}
+
+void XclExpImgData::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pWorksheet = rStrm.GetCurrentStream();
+
+ DrawingML aDML(pWorksheet, &rStrm, drawingml::DOCUMENT_XLSX);
+ OUString rId = aDML.WriteImage( maGraphic );
+ pWorksheet->singleElement(XML_picture, FSNS(XML_r, XML_id), rId);
+}
+
+XclExpControlHelper::XclExpControlHelper( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnEntryCount( 0 )
+{
+}
+
+XclExpControlHelper::~XclExpControlHelper()
+{
+}
+
+void XclExpControlHelper::ConvertSheetLinks( Reference< XShape > const & xShape )
+{
+ mxCellLink.reset();
+ mxCellLinkAddress.SetInvalid();
+ mxSrcRange.reset();
+ mnEntryCount = 0;
+
+ // get control model
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ // *** cell link *** ------------------------------------------------------
+
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY );
+ if( xBindable.is() )
+ {
+ Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY );
+ if( xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_VALBIND ) )
+ {
+ ScfPropertySet aBindProp( xServInfo );
+ CellAddress aApiAddress;
+ if( aBindProp.GetProperty( aApiAddress, SC_UNONAME_BOUNDCELL ) )
+ {
+ ScUnoConversion::FillScAddress( mxCellLinkAddress, aApiAddress );
+ if( GetTabInfo().IsExportTab( mxCellLinkAddress.Tab() ) )
+ mxCellLink = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, mxCellLinkAddress );
+ }
+ }
+ }
+
+ // *** source range *** ---------------------------------------------------
+
+ Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY );
+ if( !xEntrySink.is() )
+ return;
+
+ Reference< XServiceInfo > xServInfo( xEntrySink->getListEntrySource(), UNO_QUERY );
+ if( !(xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_LISTSOURCE )) )
+ return;
+
+ ScfPropertySet aSinkProp( xServInfo );
+ CellRangeAddress aApiRange;
+ if( aSinkProp.GetProperty( aApiRange, SC_UNONAME_CELLRANGE ) )
+ {
+ ScRange aSrcRange;
+ ScUnoConversion::FillScRange( aSrcRange, aApiRange );
+ if( (aSrcRange.aStart.Tab() == aSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( aSrcRange.aStart.Tab() ) )
+ mxSrcRange = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CONTROL, aSrcRange );
+ mnEntryCount = static_cast< sal_uInt16 >( aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1 );
+ }
+}
+
+void XclExpControlHelper::WriteFormula( XclExpStream& rStrm, const XclTokenArray& rTokArr )
+{
+ sal_uInt16 nFmlaSize = rTokArr.GetSize();
+ rStrm << nFmlaSize << sal_uInt32( 0 );
+ rTokArr.WriteArray( rStrm );
+ if( nFmlaSize & 1 ) // pad to 16-bit
+ rStrm << sal_uInt8( 0 );
+}
+
+void XclExpControlHelper::WriteFormulaSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId, const XclTokenArray& rTokArr )
+{
+ rStrm.StartRecord( nSubRecId, (rTokArr.GetSize() + 5) & ~1 );
+ WriteFormula( rStrm, rTokArr );
+ rStrm.EndRecord();
+}
+
+//delete for exporting OCX
+//#if EXC_EXP_OCX_CTRL
+
+XclExpOcxControlObj::XclExpOcxControlObj( XclExpObjectManager& rObjMgr, Reference< XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor, const OUString& rClassName, sal_uInt32 nStrmStart, sal_uInt32 nStrmSize ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_PICTURE, true ),
+ XclExpControlHelper( rObjMgr.GetRoot() ),
+ maClassName( rClassName ),
+ mnStrmStart( nStrmStart ),
+ mnStrmSize( nStrmSize )
+{
+ ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) );
+
+ // OBJ record flags
+ SetLocked( true );
+ SetPrintable( aCtrlProp.GetBoolProperty( "Printable" ) );
+ SetAutoFill( false );
+ SetAutoLine( false );
+
+ // fill DFF property set
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl,
+ ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | ShapeFlag::OLEShape );
+ tools::Rectangle aDummyRect;
+ EscherPropertyContainer aPropOpt( mrEscherEx.GetGraphicProvider(), mrEscherEx.QueryPictureStream(), aDummyRect );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x08000040 );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field
+
+ // #i51348# name of the control, may overwrite shape name
+ OUString aCtrlName;
+ if( aCtrlProp.GetProperty( aCtrlName, "Name" ) && !aCtrlName.isEmpty() )
+ aPropOpt.AddOpt( ESCHER_Prop_wzName, aCtrlName );
+
+ // meta file
+ //TODO - needs check
+ Reference< XPropertySet > xShapePS( xShape, UNO_QUERY );
+ if( xShapePS.is() && aPropOpt.CreateGraphicProperties( xShapePS, "MetaFile", false ) )
+ {
+ sal_uInt32 nBlipId;
+ if( aPropOpt.GetOpt( ESCHER_Prop_pib, nBlipId ) )
+ aPropOpt.AddOpt( ESCHER_Prop_pictureId, nBlipId );
+ }
+
+ // write DFF property set to stream
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ ImplWriteAnchor( SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // spreadsheet links
+ ConvertSheetLinks( xShape );
+}
+
+void XclExpOcxControlObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ // OBJCF - clipboard format
+ rStrm.StartRecord( EXC_ID_OBJCF, 2 );
+ rStrm << sal_uInt16( 2 );
+ rStrm.EndRecord();
+
+ // OBJFLAGS
+ rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 );
+ rStrm << sal_uInt16( 0x0031 );
+ rStrm.EndRecord();
+
+ // OBJPICTFMLA
+ XclExpString aClass( maClassName );
+ sal_uInt16 nClassNameSize = static_cast< sal_uInt16 >( aClass.GetSize() );
+ sal_uInt16 nClassNamePad = nClassNameSize & 1;
+ sal_uInt16 nFirstPartSize = 12 + nClassNameSize + nClassNamePad;
+
+ const XclTokenArray* pCellLink = GetCellLinkTokArr();
+ sal_uInt16 nCellLinkSize = pCellLink ? ((pCellLink->GetSize() + 7) & 0xFFFE) : 0;
+
+ const XclTokenArray* pSrcRange = GetSourceRangeTokArr();
+ sal_uInt16 nSrcRangeSize = pSrcRange ? ((pSrcRange->GetSize() + 7) & 0xFFFE) : 0;
+
+ sal_uInt16 nPictFmlaSize = nFirstPartSize + nCellLinkSize + nSrcRangeSize + 18;
+ rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nPictFmlaSize );
+
+ rStrm << nFirstPartSize // size of first part
+ << sal_uInt16( 5 ) // formula size
+ << sal_uInt32( 0 ) // unknown ID
+ << sal_uInt8( 0x02 ) << sal_uInt32( 0 ) // tTbl token with unknown ID
+ << sal_uInt8( 3 ) // pad to word
+ << aClass; // "Forms.***.1"
+ rStrm.WriteZeroBytes( nClassNamePad ); // pad to word
+ rStrm << mnStrmStart // start in 'Ctls' stream
+ << mnStrmSize // size in 'Ctls' stream
+ << sal_uInt32( 0 ); // class ID size
+ // cell link
+ rStrm << nCellLinkSize;
+ if( pCellLink )
+ WriteFormula( rStrm, *pCellLink );
+ // list source range
+ rStrm << nSrcRangeSize;
+ if( pSrcRange )
+ WriteFormula( rStrm, *pSrcRange );
+
+ rStrm.EndRecord();
+}
+
+//#else
+
+XclExpTbxControlObj::XclExpTbxControlObj( XclExpObjectManager& rRoot, Reference< XShape > const & xShape , const tools::Rectangle* pChildAnchor ) :
+ XclObj( rRoot, EXC_OBJTYPE_UNKNOWN, true ),
+ XclMacroHelper( rRoot ),
+ mxShape( xShape ),
+ meEventType( EXC_TBX_EVENT_ACTION ),
+ mnHeight( 0 ),
+ mnState( 0 ),
+ mnLineCount( 0 ),
+ mnSelEntry( 0 ),
+ mnScrollValue( 0 ),
+ mnScrollMin( 0 ),
+ mnScrollMax( 100 ),
+ mnScrollStep( 1 ),
+ mnScrollPage( 10 ),
+ mbFlatButton( false ),
+ mbFlatBorder( false ),
+ mbMultiSel( false ),
+ mbScrollHor( false ),
+ mbPrint( false ),
+ mbVisible( false ),
+ mnShapeId( 0 ),
+ mrRoot(rRoot)
+{
+ namespace FormCompType = css::form::FormComponentType;
+ namespace AwtVisualEffect = css::awt::VisualEffect;
+ namespace AwtScrollOrient = css::awt::ScrollBarOrientation;
+
+ ScfPropertySet aCtrlProp( XclControlHelper::GetControlModel( xShape ) );
+ if( !xShape.is() || !aCtrlProp.Is() )
+ return;
+
+ mnHeight = xShape->getSize().Height;
+ if( mnHeight <= 0 )
+ return;
+
+ // control type
+ sal_Int16 nClassId = 0;
+ if( aCtrlProp.GetProperty( nClassId, "ClassId" ) )
+ {
+ switch( nClassId )
+ {
+ case FormCompType::COMMANDBUTTON: mnObjType = EXC_OBJTYPE_BUTTON; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::RADIOBUTTON: mnObjType = EXC_OBJTYPE_OPTIONBUTTON; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::CHECKBOX: mnObjType = EXC_OBJTYPE_CHECKBOX; meEventType = EXC_TBX_EVENT_ACTION; break;
+ case FormCompType::LISTBOX: mnObjType = EXC_OBJTYPE_LISTBOX; meEventType = EXC_TBX_EVENT_CHANGE; break;
+ case FormCompType::COMBOBOX: mnObjType = EXC_OBJTYPE_DROPDOWN; meEventType = EXC_TBX_EVENT_CHANGE; break;
+ case FormCompType::GROUPBOX: mnObjType = EXC_OBJTYPE_GROUPBOX; meEventType = EXC_TBX_EVENT_MOUSE; break;
+ case FormCompType::FIXEDTEXT: mnObjType = EXC_OBJTYPE_LABEL; meEventType = EXC_TBX_EVENT_MOUSE; break;
+ case FormCompType::SCROLLBAR: mnObjType = EXC_OBJTYPE_SCROLLBAR; meEventType = EXC_TBX_EVENT_VALUE; break;
+ case FormCompType::SPINBUTTON: mnObjType = EXC_OBJTYPE_SPIN; meEventType = EXC_TBX_EVENT_VALUE; break;
+ }
+ }
+ if( mnObjType == EXC_OBJTYPE_UNKNOWN )
+ return;
+
+ // OBJ record flags
+ SetLocked( true );
+ mbPrint = aCtrlProp.GetBoolProperty( "Printable" );
+ SetPrintable( mbPrint );
+ SetAutoFill( false );
+ SetAutoLine( false );
+
+ // fill DFF property set
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ EscherPropertyContainer aPropOpt;
+ mbVisible = aCtrlProp.GetBoolProperty( "EnableVisible" );
+ aPropOpt.AddOpt( ESCHER_Prop_fPrint, mbVisible ? 0x00080000 : 0x00080002 ); // visible flag
+
+ aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01000100 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 ); // Text ID
+ aPropOpt.AddOpt( ESCHER_Prop_WrapText, 0x00000001 );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x001A0008 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00100000 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field
+
+ // #i51348# name of the control, may overwrite shape name
+ if( aCtrlProp.GetProperty( msCtrlName, "Name" ) && !msCtrlName.isEmpty() )
+ aPropOpt.AddOpt( ESCHER_Prop_wzName, msCtrlName );
+
+ //Export description as alt text
+ if( SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ) )
+ {
+ OUString aAltTxt;
+ OUString aDescrText = pSdrObj->GetDescription();
+ if(!aDescrText.isEmpty())
+ aAltTxt = aDescrText.copy( 0, std::min<sal_Int32>(MSPROP_DESCRIPTION_MAX_LEN, aDescrText.getLength()) );
+ aPropOpt.AddOpt( ESCHER_Prop_wzDescription, aAltTxt );
+ }
+
+ // write DFF property set to stream
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ ImplWriteAnchor( SdrObject::getSdrObjectFromXShape( xShape ), pChildAnchor );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // control label
+ if( aCtrlProp.GetProperty( msLabel, "Label" ) )
+ {
+ /* Be sure to construct the MSODRAWING record containing the
+ ClientTextbox atom after the base OBJ's MSODRAWING record data is
+ completed. */
+ pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
+ mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ sal_uInt16 nXclFont = EXC_FONT_APP;
+ if( !msLabel.isEmpty() )
+ {
+ XclFontData aFontData;
+ GetFontPropSetHelper().ReadFontProperties( aFontData, aCtrlProp, EXC_FONTPROPSET_CONTROL );
+ if( (!aFontData.maName.isEmpty() ) && (aFontData.mnHeight > 0) )
+ nXclFont = GetFontBuffer().Insert( aFontData, EXC_COLOR_CTRLTEXT );
+ }
+
+ pTxo.reset( new XclTxo( msLabel, nXclFont ) );
+ pTxo->SetHorAlign( (mnObjType == EXC_OBJTYPE_BUTTON) ? EXC_OBJ_HOR_CENTER : EXC_OBJ_HOR_LEFT );
+ pTxo->SetVerAlign( EXC_OBJ_VER_CENTER );
+ }
+
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+
+ // other properties
+ aCtrlProp.GetProperty( mnLineCount, "LineCount" );
+
+ // border style
+ sal_Int16 nApiButton = AwtVisualEffect::LOOK3D;
+ sal_Int16 nApiBorder = AwtVisualEffect::LOOK3D;
+ switch( nClassId )
+ {
+ case FormCompType::LISTBOX:
+ case FormCompType::COMBOBOX:
+ aCtrlProp.GetProperty( nApiBorder, "Border" );
+ break;
+ case FormCompType::CHECKBOX:
+ case FormCompType::RADIOBUTTON:
+ aCtrlProp.GetProperty( nApiButton, "VisualEffect" );
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ // Push button cannot be set to flat in Excel
+ case FormCompType::COMMANDBUTTON:
+ nApiBorder = AwtVisualEffect::LOOK3D;
+ break;
+ // Label does not support a border in Excel
+ case FormCompType::FIXEDTEXT:
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ /* Scroll bar and spin button have a "Border" property, but it is
+ really used for a border, and not for own 3D/flat look (#i34712#). */
+ case FormCompType::SCROLLBAR:
+ case FormCompType::SPINBUTTON:
+ nApiButton = AwtVisualEffect::LOOK3D;
+ nApiBorder = AwtVisualEffect::NONE;
+ break;
+ // Group box does not support flat style (#i34712#)
+ case FormCompType::GROUPBOX:
+ nApiBorder = AwtVisualEffect::LOOK3D;
+ break;
+ }
+ mbFlatButton = nApiButton != AwtVisualEffect::LOOK3D;
+ mbFlatBorder = nApiBorder != AwtVisualEffect::LOOK3D;
+
+ // control state
+ sal_Int16 nApiState = 0;
+ if( aCtrlProp.GetProperty( nApiState, "State" ) )
+ {
+ switch( nApiState )
+ {
+ case 0: mnState = EXC_OBJ_CHECKBOX_UNCHECKED; break;
+ case 1: mnState = EXC_OBJ_CHECKBOX_CHECKED; break;
+ case 2: mnState = EXC_OBJ_CHECKBOX_TRISTATE; break;
+ }
+ }
+
+ // special control contents
+ switch( nClassId )
+ {
+ case FormCompType::LISTBOX:
+ {
+ mbMultiSel = aCtrlProp.GetBoolProperty( "MultiSelection" );
+ Sequence< sal_Int16 > aSelection;
+ if( aCtrlProp.GetProperty( aSelection, "SelectedItems" ) )
+ {
+ if( aSelection.hasElements() )
+ {
+ mnSelEntry = aSelection[ 0 ] + 1;
+ comphelper::sequenceToContainer(maMultiSel, aSelection);
+ }
+ }
+
+ // convert listbox with dropdown button to Excel dropdown
+ if( aCtrlProp.GetBoolProperty( "Dropdown" ) )
+ mnObjType = EXC_OBJTYPE_DROPDOWN;
+ }
+ break;
+
+ case FormCompType::COMBOBOX:
+ {
+ Sequence< OUString > aStringList;
+ OUString aDefText;
+ if( aCtrlProp.GetProperty( aStringList, "StringItemList" ) &&
+ aCtrlProp.GetProperty( aDefText, "Text" ) &&
+ aStringList.hasElements() && !aDefText.isEmpty() )
+ {
+ auto nIndex = comphelper::findValue(aStringList, aDefText);
+ if( nIndex != -1 )
+ mnSelEntry = static_cast< sal_Int16 >( nIndex + 1 ); // 1-based
+ if( mnSelEntry > 0 )
+ maMultiSel.resize( 1, mnSelEntry - 1 );
+ }
+
+ // convert combobox without dropdown button to Excel listbox
+ if( !aCtrlProp.GetBoolProperty( "Dropdown" ) )
+ mnObjType = EXC_OBJTYPE_LISTBOX;
+ }
+ break;
+
+ case FormCompType::SCROLLBAR:
+ {
+ sal_Int32 nApiValue = 0;
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValueMin" ) )
+ mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValueMax" ) )
+ mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "ScrollValue" ) )
+ mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax );
+ if( aCtrlProp.GetProperty( nApiValue, "LineIncrement" ) )
+ mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "BlockIncrement" ) )
+ mnScrollPage = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "Orientation" ) )
+ mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL;
+ }
+ break;
+
+ case FormCompType::SPINBUTTON:
+ {
+ sal_Int32 nApiValue = 0;
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValueMin" ) )
+ mnScrollMin = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValueMax" ) )
+ mnScrollMax = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinValue" ) )
+ mnScrollValue = limit_cast< sal_uInt16 >( nApiValue, mnScrollMin, mnScrollMax );
+ if( aCtrlProp.GetProperty( nApiValue, "SpinIncrement" ) )
+ mnScrollStep = limit_cast< sal_uInt16 >( nApiValue, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ if( aCtrlProp.GetProperty( nApiValue, "Orientation" ) )
+ mbScrollHor = nApiValue == AwtScrollOrient::HORIZONTAL;
+ }
+ break;
+ }
+
+ {
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ if( xCtrlModel.is() )
+ {
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY );
+ if( xBindable.is() )
+ {
+ Reference< XServiceInfo > xServInfo( xBindable->getValueBinding(), UNO_QUERY );
+ if( xServInfo.is() && xServInfo->supportsService( SC_SERVICENAME_VALBIND ) )
+ {
+ ScfPropertySet aBindProp( xServInfo );
+ CellAddress aApiAddress;
+ if( aBindProp.GetProperty( aApiAddress, SC_UNONAME_BOUNDCELL ) )
+ {
+ ScUnoConversion::FillScAddress( mxCellLinkAddress, aApiAddress );
+ if( SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape ) )
+ lcl_GetFromTo( rRoot, pSdrObj->GetLogicRect(), mxCellLinkAddress.Tab(), maAreaFrom, maAreaTo, true );
+ }
+ }
+ }
+ }
+ }
+
+ // spreadsheet links
+ ConvertSheetLinks( xShape );
+}
+
+bool XclExpTbxControlObj::SetMacroLink( const ScriptEventDescriptor& rEvent )
+{
+ return XclMacroHelper::SetMacroLink( rEvent, meEventType );
+}
+
+void XclExpTbxControlObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ switch( mnObjType )
+ {
+ // *** Push buttons, labels ***
+
+ case EXC_OBJTYPE_BUTTON:
+ case EXC_OBJTYPE_LABEL:
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ break;
+
+ // *** Check boxes, option buttons ***
+
+ case EXC_OBJTYPE_CHECKBOX:
+ case EXC_OBJTYPE_OPTIONBUTTON:
+ {
+ // ftCbls - box properties
+ sal_uInt16 nStyle = 0;
+ ::set_flag( nStyle, EXC_OBJ_CHECKBOX_FLAT, mbFlatButton );
+
+ rStrm.StartRecord( EXC_ID_OBJCBLS, 12 );
+ rStrm << mnState;
+ rStrm.WriteZeroBytes( 8 );
+ rStrm << nStyle;
+ rStrm.EndRecord();
+
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftCblsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJCBLSFMLA );
+
+ // ftCblsData subrecord - box properties, again
+ rStrm.StartRecord( EXC_ID_OBJCBLS, 8 );
+ rStrm << mnState;
+ rStrm.WriteZeroBytes( 4 );
+ rStrm << nStyle;
+ rStrm.EndRecord();
+ }
+ break;
+
+ // *** List boxes, combo boxes ***
+
+ case EXC_OBJTYPE_LISTBOX:
+ case EXC_OBJTYPE_DROPDOWN:
+ {
+ sal_uInt16 nEntryCount = GetSourceEntryCount();
+
+ // ftSbs subrecord - Scroll bars
+ sal_Int32 nLineHeight = XclTools::GetHmmFromTwips( 200 ); // always 10pt
+ if( mnObjType == EXC_OBJTYPE_LISTBOX )
+ mnLineCount = static_cast< sal_uInt16 >( mnHeight / nLineHeight );
+ mnScrollValue = 0;
+ mnScrollMin = 0;
+ sal_uInt16 nInvisLines = (nEntryCount >= mnLineCount) ? (nEntryCount - mnLineCount) : 0;
+ mnScrollMax = limit_cast< sal_uInt16 >( nInvisLines, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ mnScrollStep = 1;
+ mnScrollPage = limit_cast< sal_uInt16 >( mnLineCount, EXC_OBJ_SCROLLBAR_MIN, EXC_OBJ_SCROLLBAR_MAX );
+ mbScrollHor = false;
+ WriteSbs( rStrm );
+
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftSbsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA );
+
+ // ftLbsData - source data range and box properties
+ sal_uInt16 nStyle = 0;
+ ::insert_value( nStyle, mbMultiSel ? EXC_OBJ_LISTBOX_MULTI : EXC_OBJ_LISTBOX_SINGLE, 4, 2 );
+ ::set_flag( nStyle, EXC_OBJ_LISTBOX_FLAT, mbFlatBorder );
+
+ rStrm.StartRecord( EXC_ID_OBJLBSDATA, 0 );
+
+ if( const XclTokenArray* pSrcRange = GetSourceRangeTokArr() )
+ {
+ rStrm << static_cast< sal_uInt16 >( (pSrcRange->GetSize() + 7) & 0xFFFE );
+ WriteFormula( rStrm, *pSrcRange );
+ }
+ else
+ rStrm << sal_uInt16( 0 );
+
+ rStrm << nEntryCount << mnSelEntry << nStyle << sal_uInt16( 0 );
+ if( mnObjType == EXC_OBJTYPE_LISTBOX )
+ {
+ if( nEntryCount )
+ {
+ ScfUInt8Vec aSelEx( nEntryCount, 0 );
+ for( const auto& rItem : maMultiSel )
+ if( rItem < nEntryCount )
+ aSelEx[ rItem ] = 1;
+ rStrm.Write( aSelEx.data(), aSelEx.size() );
+ }
+ }
+ else if( mnObjType == EXC_OBJTYPE_DROPDOWN )
+ {
+ rStrm << sal_uInt16( 0 ) << mnLineCount << sal_uInt16( 0 ) << sal_uInt16( 0 );
+ }
+
+ rStrm.EndRecord();
+ }
+ break;
+
+ // *** Spin buttons, scrollbars ***
+
+ case EXC_OBJTYPE_SPIN:
+ case EXC_OBJTYPE_SCROLLBAR:
+ {
+ // ftSbs subrecord - scroll bars
+ WriteSbs( rStrm );
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+ // ftSbsFmla subrecord - cell link
+ WriteCellLinkSubRec( rStrm, EXC_ID_OBJSBSFMLA );
+ }
+ break;
+
+ // *** Group boxes ***
+
+ case EXC_OBJTYPE_GROUPBOX:
+ {
+ // ftMacro - macro link
+ WriteMacroSubRec( rStrm );
+
+ // ftGboData subrecord - group box properties
+ sal_uInt16 nStyle = 0;
+ ::set_flag( nStyle, EXC_OBJ_GROUPBOX_FLAT, mbFlatBorder );
+
+ rStrm.StartRecord( EXC_ID_OBJGBODATA, 6 );
+ rStrm << sal_uInt32( 0 )
+ << nStyle;
+ rStrm.EndRecord();
+ }
+ break;
+ }
+}
+
+void XclExpTbxControlObj::WriteCellLinkSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId )
+{
+ if( const XclTokenArray* pCellLink = GetCellLinkTokArr() )
+ WriteFormulaSubRec( rStrm, nSubRecId, *pCellLink );
+}
+
+void XclExpTbxControlObj::WriteSbs( XclExpStream& rStrm )
+{
+ sal_uInt16 nOrient = 0;
+ ::set_flag( nOrient, EXC_OBJ_SCROLLBAR_HOR, mbScrollHor );
+ sal_uInt16 nStyle = EXC_OBJ_SCROLLBAR_DEFFLAGS;
+ ::set_flag( nStyle, EXC_OBJ_SCROLLBAR_FLAT, mbFlatButton );
+
+ rStrm.StartRecord( EXC_ID_OBJSBS, 20 );
+ rStrm << sal_uInt32( 0 ) // reserved
+ << mnScrollValue // thumb position
+ << mnScrollMin // thumb min pos
+ << mnScrollMax // thumb max pos
+ << mnScrollStep // line increment
+ << mnScrollPage // page increment
+ << nOrient // 0 = vertical, 1 = horizontal
+ << sal_uInt16( 15 ) // thumb width
+ << nStyle; // flags/style
+ rStrm.EndRecord();
+}
+
+void XclExpTbxControlObj::setShapeId(sal_Int32 aShapeId)
+{
+ mnShapeId = aShapeId;
+}
+
+namespace
+{
+/// Handles the VML export of form controls (e.g. checkboxes).
+class VmlFormControlExporter : public oox::vml::VMLExport
+{
+ sal_uInt16 m_nObjType;
+ tools::Rectangle m_aAreaFrom;
+ tools::Rectangle m_aAreaTo;
+ OUString m_aLabel;
+ OUString m_aMacroName;
+
+public:
+ VmlFormControlExporter(const sax_fastparser::FSHelperPtr& p, sal_uInt16 nObjType,
+ const tools::Rectangle& rAreaFrom, const tools::Rectangle& rAreaTo,
+ const OUString& rLabel, const OUString& rMacroName);
+
+protected:
+ using VMLExport::StartShape;
+ sal_Int32 StartShape() override;
+ using VMLExport::EndShape;
+ void EndShape(sal_Int32 nShapeElement) override;
+};
+
+VmlFormControlExporter::VmlFormControlExporter(const sax_fastparser::FSHelperPtr& p,
+ sal_uInt16 nObjType,
+ const tools::Rectangle& rAreaFrom,
+ const tools::Rectangle& rAreaTo,
+ const OUString& rLabel, const OUString& rMacroName)
+ : VMLExport(p)
+ , m_nObjType(nObjType)
+ , m_aAreaFrom(rAreaFrom)
+ , m_aAreaTo(rAreaTo)
+ , m_aLabel(rLabel)
+ , m_aMacroName(rMacroName)
+{
+}
+
+sal_Int32 VmlFormControlExporter::StartShape()
+{
+ // Host control.
+ AddShapeAttribute(XML_type, "#_x0000_t201");
+ return VMLExport::StartShape();
+}
+
+void VmlFormControlExporter::EndShape(sal_Int32 nShapeElement)
+{
+ sax_fastparser::FSHelperPtr pVmlDrawing = GetFS();
+
+ pVmlDrawing->startElement(FSNS(XML_v, XML_textbox));
+ pVmlDrawing->startElement(XML_div);
+ pVmlDrawing->write(m_aLabel);
+ pVmlDrawing->endElement(XML_div);
+ pVmlDrawing->endElement(FSNS(XML_v, XML_textbox));
+
+ OString aObjectType;
+ switch (m_nObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ aObjectType = "Checkbox";
+ break;
+ case EXC_OBJTYPE_BUTTON:
+ aObjectType = "Button";
+ break;
+ }
+ pVmlDrawing->startElement(FSNS(XML_x, XML_ClientData), XML_ObjectType, aObjectType);
+ OString aAnchor
+ = OString::number(m_aAreaFrom.Left()) + ", " + OString::number(m_aAreaFrom.Top()) + ", "
+ + OString::number(m_aAreaFrom.Right()) + ", " + OString::number(m_aAreaFrom.Bottom()) + ", "
+ + OString::number(m_aAreaTo.Left()) + ", " + OString::number(m_aAreaTo.Top()) + ", "
+ + OString::number(m_aAreaTo.Right()) + ", " + OString::number(m_aAreaTo.Bottom());
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_Anchor), aAnchor);
+
+ if (!m_aMacroName.isEmpty())
+ {
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_FmlaMacro), m_aMacroName);
+ }
+
+ // XclExpOcxControlObj::WriteSubRecs() has the same fixed values.
+ if (m_nObjType == EXC_OBJTYPE_BUTTON)
+ {
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_TextHAlign), "Center");
+ }
+ XclXmlUtils::WriteElement(pVmlDrawing, FSNS(XML_x, XML_TextVAlign), "Center");
+
+ pVmlDrawing->endElement(FSNS(XML_x, XML_ClientData));
+ VMLExport::EndShape(nShapeElement);
+}
+
+}
+
+/// Save into xl/drawings/vmlDrawing1.vml.
+void XclExpTbxControlObj::SaveVml(XclExpXmlStream& rStrm)
+{
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ tools::Rectangle aAreaFrom;
+ tools::Rectangle aAreaTo;
+ // Unlike XclExpTbxControlObj::SaveXml(), this is not calculated in EMUs.
+ lcl_GetFromTo(mrRoot, pObj->GetLogicRect(), GetTab(), aAreaFrom, aAreaTo);
+ VmlFormControlExporter aFormControlExporter(rStrm.GetCurrentStream(), GetObjType(), aAreaFrom,
+ aAreaTo, msLabel, GetMacroName());
+ aFormControlExporter.AddSdrObject(*pObj, /*bIsFollowingTextFlow=*/false, /*eHOri=*/-1,
+ /*eVOri=*/-1, /*eHRel=*/-1, /*eVRel=*/-1,
+ /*pWrapAttrList=*/nullptr, /*bOOxmlExport=*/true);
+}
+
+// save into xl\drawings\drawing1.xml
+void XclExpTbxControlObj::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pDrawing = rStrm.GetCurrentStream();
+
+ pDrawing->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)));
+ pDrawing->startElement(FSNS(XML_mc, XML_Choice),
+ FSNS(XML_xmlns, XML_a14), rStrm.getNamespaceURL(OOX_NS(a14)),
+ XML_Requires, "a14");
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_twoCellAnchor), XML_editAs, "oneCell");
+ {
+ pDrawing->startElement(FSNS(XML_xdr, XML_from));
+ lcl_WriteAnchorVertex(pDrawing, maAreaFrom);
+ pDrawing->endElement(FSNS(XML_xdr, XML_from));
+ pDrawing->startElement(FSNS(XML_xdr, XML_to));
+ lcl_WriteAnchorVertex(pDrawing, maAreaTo);
+ pDrawing->endElement(FSNS(XML_xdr, XML_to));
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_sp));
+ {
+ // xdr:nvSpPr
+ pDrawing->startElement(FSNS(XML_xdr, XML_nvSpPr));
+ {
+ pDrawing->singleElement(FSNS(XML_xdr, XML_cNvPr),
+ XML_id, OString::number(mnShapeId).getStr(),
+ XML_name, msCtrlName, // control name
+ XML_descr, msLabel, // description as alt text
+ XML_hidden, mbVisible ? "0" : "1");
+ pDrawing->singleElement(FSNS(XML_xdr, XML_cNvSpPr));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_nvSpPr));
+
+ // xdr:spPr
+ pDrawing->startElement(FSNS(XML_xdr, XML_spPr));
+ {
+ // a:xfrm
+ pDrawing->startElement(FSNS(XML_a, XML_xfrm));
+ {
+ pDrawing->singleElement(FSNS(XML_a, XML_off),
+ XML_x, "0",
+ XML_y, "0");
+ pDrawing->singleElement(FSNS(XML_a, XML_ext),
+ XML_cx, "0",
+ XML_cy, "0");
+ }
+ pDrawing->endElement(FSNS(XML_a, XML_xfrm));
+
+ // a:prstGeom
+ pDrawing->startElement(FSNS(XML_a, XML_prstGeom), XML_prst, "rect");
+ {
+ pDrawing->singleElement(FSNS(XML_a, XML_avLst));
+ }
+ pDrawing->endElement(FSNS(XML_a, XML_prstGeom));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_spPr));
+
+ // xdr:txBody
+ {
+ pDrawing->startElement(FSNS(XML_xdr, XML_txBody));
+
+#define DEFLRINS 254
+#define DEFTBINS 127
+ sal_Int32 nLeft, nRight, nTop, nBottom;
+ nLeft = nRight = DEFLRINS;
+ nTop = nBottom = DEFTBINS;
+
+ // top inset looks a bit different compared to ppt export
+ // check if something related doesn't work as expected
+ Reference< XPropertySet > rXPropSet(mxShape, UNO_QUERY);
+
+ try
+ {
+ css::uno::Any mAny;
+
+ mAny = rXPropSet->getPropertyValue("TextLeftDistance");
+ if (mAny.hasValue())
+ mAny >>= nLeft;
+
+ mAny = rXPropSet->getPropertyValue("TextRightDistance");
+ if (mAny.hasValue())
+ mAny >>= nRight;
+
+ mAny = rXPropSet->getPropertyValue("TextUpperDistance");
+ if (mAny.hasValue())
+ mAny >>= nTop;
+
+ mAny = rXPropSet->getPropertyValue("TextLowerDistance");
+ if (mAny.hasValue())
+ mAny >>= nBottom;
+ }
+ catch (...)
+ {
+ }
+
+ // Specifies the inset of the bounding rectangle.
+ // Insets are used just as internal margins for text boxes within shapes.
+ // If this attribute is omitted, then a value of 45720 or 0.05 inches is implied.
+ pDrawing->startElementNS(XML_a, XML_bodyPr,
+ XML_lIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), nLeft != DEFLRINS),
+ XML_rIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), nRight != DEFLRINS),
+ XML_tIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), nTop != DEFTBINS),
+ XML_bIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)), nBottom != DEFTBINS),
+ XML_anchor, "ctr");
+
+ {
+ bool bTextAutoGrowHeight = false;
+
+ try
+ {
+ css::uno::Any mAny;
+
+ mAny = rXPropSet->getPropertyValue("TextAutoGrowHeight");
+ if (mAny.hasValue())
+ mAny >>= bTextAutoGrowHeight;
+ }
+ catch (...)
+ {
+ }
+
+ pDrawing->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
+ }
+
+ pDrawing->endElementNS(XML_a, XML_bodyPr);
+
+ {
+ pDrawing->startElementNS(XML_a, XML_p);
+ pDrawing->startElementNS(XML_a, XML_r);
+ pDrawing->startElementNS(XML_a, XML_t);
+ pDrawing->write(msLabel);
+ pDrawing->endElementNS(XML_a, XML_t);
+ pDrawing->endElementNS(XML_a, XML_r);
+ pDrawing->endElementNS(XML_a, XML_p);
+ }
+
+ pDrawing->endElement(FSNS(XML_xdr, XML_txBody));
+ }
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_sp));
+ pDrawing->singleElement(FSNS(XML_xdr, XML_clientData));
+ }
+ pDrawing->endElement(FSNS(XML_xdr, XML_twoCellAnchor));
+ pDrawing->endElement( FSNS( XML_mc, XML_Choice ) );
+ pDrawing->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+}
+
+// output into ctrlProp1.xml
+OUString XclExpTbxControlObj::SaveControlPropertiesXml(XclExpXmlStream& rStrm) const
+{
+ OUString sIdFormControlPr;
+
+ switch (mnObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ {
+ const sal_Int32 nDrawing = DrawingML::getNewDrawingUniqueId();
+ sax_fastparser::FSHelperPtr pFormControl = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "ctrlProps/ctrlProps", nDrawing ),
+ XclXmlUtils::GetStreamName( "../", "ctrlProps/ctrlProps", nDrawing ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.ms-excel.controlproperties+xml",
+ oox::getRelationship(Relationship::CTRLPROP),
+ &sIdFormControlPr );
+
+ rStrm.PushStream( pFormControl );
+ // checkbox
+ // <formControlPr
+ // xmlns="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
+ // objectType="CheckBox" checked="Checked" lockText="1" noThreeD="1"/>
+ //
+ pFormControl->write("<formControlPr xmlns=\"http://schemas.microsoft.com/office/spreadsheetml/2009/9/main\" objectType=\"CheckBox\"");
+ if (mnState == EXC_OBJ_CHECKBOX_CHECKED)
+ pFormControl->write(" checked=\"Checked\"");
+
+ pFormControl->write(" autoLine=\"false\"");
+
+ if (mbPrint)
+ pFormControl->write(" print=\"true\"");
+ else
+ pFormControl->write(" print=\"false\"");
+
+ if (mxCellLinkAddress.IsValid())
+ {
+ OUString aCellLink = mxCellLinkAddress.Format(ScRefFlags::ADDR_ABS,
+ &GetDoc(),
+ ScAddress::Details(::formula::FormulaGrammar::CONV_XL_A1));
+
+ // "Sheet1!$C$5"
+ pFormControl->write(" fmlaLink=\"");
+ if (aCellLink.indexOf('!') < 0)
+ {
+ pFormControl->write(GetTabInfo().GetScTabName(mxCellLinkAddress.Tab()));
+ pFormControl->write("!");
+ }
+ pFormControl->write(aCellLink);
+ pFormControl->write("\"");
+ }
+
+ pFormControl->write(" lockText=\"1\" noThreeD=\"1\"/>");
+ rStrm.PopStream();
+
+ break;
+ }
+ case EXC_OBJTYPE_BUTTON:
+ {
+ sal_Int32 nDrawing = DrawingML::getNewDrawingUniqueId();
+ sax_fastparser::FSHelperPtr pFormControl = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/", "ctrlProps/ctrlProps", nDrawing),
+ XclXmlUtils::GetStreamName("../", "ctrlProps/ctrlProps", nDrawing),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.ms-excel.controlproperties+xml",
+ oox::getRelationship(Relationship::CTRLPROP), &sIdFormControlPr);
+
+ pFormControl->singleElement(XML_formControlPr, XML_xmlns,
+ rStrm.getNamespaceURL(OOX_NS(xls14Lst)), XML_objectType,
+ "Button", XML_lockText, "1");
+ break;
+ }
+ }
+
+ return sIdFormControlPr;
+}
+
+// output into sheet1.xml
+void XclExpTbxControlObj::SaveSheetXml(XclExpXmlStream& rStrm, const OUString& aIdFormControlPr) const
+{
+ switch (mnObjType)
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ {
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
+
+ rWorksheet->startElement(
+ XML_control,
+ XML_shapeId, OString::number(mnShapeId).getStr(),
+ FSNS(XML_r, XML_id), aIdFormControlPr,
+ XML_name, msLabel); // text to display with checkbox button
+
+ rWorksheet->write("<controlPr defaultSize=\"0\" locked=\"1\" autoFill=\"0\" autoLine=\"0\" autoPict=\"0\"");
+
+ if (mbPrint)
+ rWorksheet->write(" print=\"true\"");
+ else
+ rWorksheet->write(" print=\"false\"");
+
+ if (!msCtrlName.isEmpty())
+ {
+ rWorksheet->write(" altText=\"");
+ rWorksheet->write(msCtrlName); // alt text
+ rWorksheet->write("\"");
+ }
+
+ rWorksheet->write(">");
+
+ rWorksheet->startElement(XML_anchor, XML_moveWithCells, "true", XML_sizeWithCells, "false");
+ rWorksheet->startElement(XML_from);
+ lcl_WriteAnchorVertex(rWorksheet, maAreaFrom);
+ rWorksheet->endElement(XML_from);
+ rWorksheet->startElement(XML_to);
+ lcl_WriteAnchorVertex(rWorksheet, maAreaTo);
+ rWorksheet->endElement(XML_to);
+ rWorksheet->endElement( XML_anchor );
+
+ rWorksheet->write("</controlPr>");
+
+ rWorksheet->endElement(XML_control);
+ rWorksheet->endElement( FSNS( XML_mc, XML_Choice ) );
+ rWorksheet->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+
+ break;
+ }
+ case EXC_OBJTYPE_BUTTON:
+ {
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent), FSNS(XML_xmlns, XML_mc),
+ rStrm.getNamespaceURL(OOX_NS(mce)));
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
+
+ rWorksheet->startElement(XML_control, XML_shapeId, OString::number(mnShapeId).getStr(),
+ FSNS(XML_r, XML_id), aIdFormControlPr, XML_name, msCtrlName);
+
+ OString aMacroName = GetMacroName().toUtf8();
+ // Omit the macro attribute if it would be empty.
+ const char* pMacroName = aMacroName.isEmpty() ? nullptr : aMacroName.getStr();
+ rWorksheet->startElement(XML_controlPr, XML_defaultSize, "0", XML_print,
+ mbPrint ? "true" : "false", XML_autoFill, "0", XML_autoPict,
+ "0", XML_macro, pMacroName);
+
+ rWorksheet->startElement(XML_anchor, XML_moveWithCells, "true", XML_sizeWithCells,
+ "false");
+
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ tools::Rectangle aAreaFrom;
+ tools::Rectangle aAreaTo;
+ lcl_GetFromTo(mrRoot, pObj->GetLogicRect(), GetTab(), aAreaFrom, aAreaTo,
+ /*bInEMU=*/true);
+
+ rWorksheet->startElement(XML_from);
+ lcl_WriteAnchorVertex(rWorksheet, aAreaFrom);
+ rWorksheet->endElement(XML_from);
+ rWorksheet->startElement(XML_to);
+ lcl_WriteAnchorVertex(rWorksheet, aAreaTo);
+ rWorksheet->endElement(XML_to);
+ rWorksheet->endElement(XML_anchor);
+
+ rWorksheet->endElement(XML_controlPr);
+
+ rWorksheet->endElement(XML_control);
+ rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
+ rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
+ break;
+ }
+ }
+}
+
+//#endif
+
+XclExpChartObj::XclExpChartObj( XclExpObjectManager& rObjMgr, Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor, ScDocument* pDoc ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_CHART ),
+ XclExpRoot( rObjMgr.GetRoot() ), mxShape( xShape ),
+ mpDoc(pDoc)
+{
+ // create the MSODRAWING record contents for the chart object
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ EscherPropertyContainer aPropOpt;
+ aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 );
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 );
+ aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x0800004E );
+ aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x0800004D );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 );
+ aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x0800004D );
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080008 );
+ aPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00020000 );
+ aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080000 );
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ // anchor
+ SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape( xShape );
+ ImplWriteAnchor( pSdrObj, pChildAnchor );
+
+ // client data (the following OBJ record)
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData );
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ // load the chart OLE object
+ if( SdrOle2Obj* pSdrOleObj = dynamic_cast< SdrOle2Obj* >( pSdrObj ) )
+ svt::EmbeddedObjectRef::TryRunningState( pSdrOleObj->GetObjRef() );
+
+ // create the chart substream object
+ ScfPropertySet aShapeProp( xShape );
+ css::awt::Rectangle aBoundRect;
+ aShapeProp.GetProperty( aBoundRect, "BoundRect" );
+ tools::Rectangle aChartRect( Point( aBoundRect.X, aBoundRect.Y ), Size( aBoundRect.Width, aBoundRect.Height ) );
+ mxChart = std::make_shared<XclExpChart>(GetRoot(), GetChartDoc(), aChartRect);
+}
+
+XclExpChartObj::~XclExpChartObj()
+{
+}
+
+void XclExpChartObj::Save( XclExpStream& rStrm )
+{
+ // content of OBJ record
+ XclObj::Save( rStrm );
+ // chart substream
+ mxChart->Save( rStrm );
+}
+
+void XclExpChartObj::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
+
+ // FIXME: two cell? it seems the two cell anchor is incorrect.
+ pDrawing->startElement( FSNS( XML_xdr, XML_twoCellAnchor ), // OOXTODO: oneCellAnchor, absoluteAnchor
+ XML_editAs, "oneCell" );
+ Reference< XPropertySet > xPropSet( mxShape, UNO_QUERY );
+ if (xPropSet.is())
+ {
+ XclObjAny::WriteFromTo( rStrm, mxShape, GetTab() );
+ ChartExport aChartExport(XML_xdr, pDrawing, GetChartDoc(), &rStrm, drawingml::DOCUMENT_XLSX);
+ auto pURLTransformer = std::make_shared<ScURLTransformer>(*mpDoc);
+ aChartExport.SetURLTranslator(pURLTransformer);
+ static sal_Int32 nChartCount = 0;
+ nChartCount++;
+ sal_Int32 nID = rStrm.GetUniqueId();
+ aChartExport.WriteChartObj( mxShape, nID, nChartCount );
+ // TODO: get the correcto chart number
+ }
+
+ pDrawing->singleElement( FSNS( XML_xdr, XML_clientData)
+ // OOXTODO: XML_fLocksWithSheet
+ // OOXTODO: XML_fPrintsWithSheet
+ );
+ pDrawing->endElement( FSNS( XML_xdr, XML_twoCellAnchor ) );
+}
+
+css::uno::Reference<css::chart::XChartDocument> XclExpChartObj::GetChartDoc() const
+{
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ if (!pObj || pObj->GetObjIdentifier() != SdrObjKind::OLE2)
+ return {};
+ // May load here - makes sure that we are working with actually loaded OLE object
+ return css::uno::Reference<css::chart::XChartDocument>(
+ static_cast<SdrOle2Obj*>(pObj)->getXModel(), css::uno::UNO_QUERY);
+}
+
+XclExpNote::XclExpNote(const XclExpRoot& rRoot, const ScAddress& rScPos,
+ const ScPostIt* pScNote, std::u16string_view rAddText)
+ : XclExpRecord(EXC_ID_NOTE)
+ , mrRoot(rRoot)
+ , maScPos(rScPos)
+ , mnObjId(EXC_OBJ_INVALID_ID)
+ , mbVisible(pScNote && pScNote->IsCaptionShown())
+ , meTHA(SDRTEXTHORZADJUST_LEFT)
+ , meTVA(SDRTEXTVERTADJUST_TOP)
+ , mbAutoScale(false)
+ , mbLocked(false)
+ , mbAutoFill(false)
+ , mbColHidden(false)
+ , mbRowHidden(false)
+{
+ // get the main note text
+ OUString aNoteText;
+ if( pScNote )
+ {
+ aNoteText = pScNote->GetText();
+ const EditTextObject *pEditObj = pScNote->GetEditTextObject();
+ if( pEditObj )
+ mpNoteContents = XclExpStringHelper::CreateString( rRoot, *pEditObj );
+ }
+ // append additional text
+ aNoteText = ScGlobal::addToken( aNoteText, rAddText, '\n', 2 );
+
+ // initialize record dependent on BIFF type
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF5:
+ maNoteText = OUStringToOString(aNoteText, rRoot.GetTextEncoding());
+ break;
+
+ case EXC_BIFF8:
+ {
+ // TODO: additional text
+ if( pScNote )
+ {
+ if( SdrCaptionObj* pCaption = pScNote->GetOrCreateCaption( maScPos ) )
+ {
+ lcl_GetFromTo( rRoot, pCaption->GetLogicRect(), maScPos.Tab(), maCommentFrom, maCommentTo );
+ if( const OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() )
+ mnObjId = rRoot.GetObjectManager().AddObj( std::make_unique<XclObjComment>( rRoot.GetObjectManager(), pCaption->GetLogicRect(), pOPO->GetTextObject(), pCaption, mbVisible, maScPos, maCommentFrom, maCommentTo ) );
+
+ SfxItemSet aItemSet = pCaption->GetMergedItemSet();
+ meTVA = pCaption->GetTextVerticalAdjust();
+ meTHA = pCaption->GetTextHorizontalAdjust();
+ mbAutoScale = pCaption->GetFitToSize() != drawing::TextFitToSizeType_NONE;
+ mbLocked = pCaption->IsMoveProtect() || pCaption->IsResizeProtect();
+
+ // AutoFill style would change if Postit.cxx object creation values are changed
+ OUString aCol(aItemSet.Get(XATTR_FILLCOLOR).GetValue());
+ mbAutoFill = aCol.isEmpty() && (aItemSet.Get(XATTR_FILLSTYLE).GetValue() == drawing::FillStyle_SOLID);
+ mbRowHidden = (rRoot.GetDoc().RowHidden(maScPos.Row(),maScPos.Tab()));
+ mbColHidden = (rRoot.GetDoc().ColHidden(maScPos.Col(),maScPos.Tab()));
+ }
+ // stAuthor (variable): An XLUnicodeString that specifies the name of the comment
+ // author. String length MUST be greater than or equal to 1 and less than or equal
+ // to 54.
+ if( pScNote->GetAuthor().isEmpty() )
+ maAuthor = XclExpString( " " );
+ else
+ maAuthor = XclExpString( pScNote->GetAuthor(), XclStrFlags::NONE, 54 );
+ }
+
+ SetRecSize( 9 + maAuthor.GetSize() );
+ }
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpNote::Save( XclExpStream& rStrm )
+{
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ // write the NOTE record directly, there may be the need to create more than one
+ const char* pcBuffer = maNoteText.getStr();
+ sal_uInt16 nCharsLeft = static_cast< sal_uInt16 >( maNoteText.getLength() );
+
+ while( nCharsLeft )
+ {
+ sal_uInt16 nWriteChars = ::std::min( nCharsLeft, EXC_NOTE5_MAXLEN );
+
+ rStrm.StartRecord( EXC_ID_NOTE, 6 + nWriteChars );
+ if( pcBuffer == maNoteText.getStr() )
+ {
+ // first record: row, col, length of complete text
+ rStrm << static_cast< sal_uInt16 >( maScPos.Row() )
+ << static_cast< sal_uInt16 >( maScPos.Col() )
+ << nCharsLeft; // still contains full length
+ }
+ else
+ {
+ // next records: -1, 0, length of current text segment
+ rStrm << sal_uInt16( 0xFFFF )
+ << sal_uInt16( 0 )
+ << nWriteChars;
+ }
+ rStrm.Write( pcBuffer, nWriteChars );
+ rStrm.EndRecord();
+
+ pcBuffer += nWriteChars;
+ nCharsLeft = nCharsLeft - nWriteChars;
+ }
+ }
+ break;
+
+ case EXC_BIFF8:
+ if( mnObjId != EXC_OBJ_INVALID_ID )
+ XclExpRecord::Save( rStrm );
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpNote::WriteBody( XclExpStream& rStrm )
+{
+ // BIFF5/BIFF7 is written separately
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 );
+
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_NOTE_VISIBLE, mbVisible );
+
+ rStrm << static_cast< sal_uInt16 >( maScPos.Row() )
+ << static_cast< sal_uInt16 >( maScPos.Col() )
+ << nFlags
+ << mnObjId
+ << maAuthor
+ << sal_uInt8( 0 );
+}
+
+void XclExpNote::WriteXml( sal_Int32 nAuthorId, XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr rComments = rStrm.GetCurrentStream();
+
+ rComments->startElement( XML_comment,
+ XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), maScPos),
+ XML_authorId, OString::number(nAuthorId)
+ // OOXTODO: XML_guid
+ );
+ rComments->startElement(XML_text);
+ // OOXTODO: phoneticPr, rPh, r
+ if( mpNoteContents )
+ mpNoteContents->WriteXml( rStrm );
+ rComments->endElement( XML_text );
+
+/*
+ Export of commentPr is disabled, since the current (Oct 2010)
+ version of MSO 2010 doesn't yet support commentPr
+ */
+#if 1//def XLSX_OOXML_FUTURE
+ if( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 )
+ {
+ rComments->startElement(FSNS(XML_mc, XML_AlternateContent));
+ rComments->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "v2");
+ rComments->startElement( XML_commentPr,
+ XML_autoFill, ToPsz( mbAutoFill ),
+ XML_autoScale, ToPsz( mbAutoScale ),
+ XML_colHidden, ToPsz( mbColHidden ),
+ XML_locked, ToPsz( mbLocked ),
+ XML_rowHidden, ToPsz( mbRowHidden ),
+ XML_textHAlign, ToHorizAlign( meTHA ),
+ XML_textVAlign, ToVertAlign( meTVA ) );
+ rComments->startElement(XML_anchor, XML_moveWithCells, "false", XML_sizeWithCells, "false");
+ rComments->startElement(FSNS(XML_xdr, XML_from));
+ lcl_WriteAnchorVertex( rComments, maCommentFrom );
+ rComments->endElement( FSNS( XML_xdr, XML_from ) );
+ rComments->startElement(FSNS(XML_xdr, XML_to));
+ lcl_WriteAnchorVertex( rComments, maCommentTo );
+ rComments->endElement( FSNS( XML_xdr, XML_to ) );
+ rComments->endElement( XML_anchor );
+ rComments->endElement( XML_commentPr );
+
+ rComments->endElement( FSNS( XML_mc, XML_Choice ) );
+ rComments->startElement(FSNS(XML_mc, XML_Fallback));
+ // Any fallback code ?
+ rComments->endElement( FSNS( XML_mc, XML_Fallback ) );
+ rComments->endElement( FSNS( XML_mc, XML_AlternateContent ) );
+ }
+#endif
+ rComments->endElement( XML_comment );
+}
+
+XclMacroHelper::XclMacroHelper( const XclExpRoot& rRoot ) :
+ XclExpControlHelper( rRoot )
+{
+}
+
+XclMacroHelper::~XclMacroHelper()
+{
+}
+
+void XclMacroHelper::WriteMacroSubRec( XclExpStream& rStrm )
+{
+ if( mxMacroLink )
+ WriteFormulaSubRec( rStrm, EXC_ID_OBJMACRO, *mxMacroLink );
+}
+
+const OUString& XclMacroHelper::GetMacroName() const { return maMacroName; }
+
+bool
+XclMacroHelper::SetMacroLink( const ScriptEventDescriptor& rEvent, const XclTbxEventType& nEventType )
+{
+ maMacroName = XclControlHelper::ExtractFromMacroDescriptor(rEvent, nEventType);
+ if (!maMacroName.isEmpty())
+ {
+ return SetMacroLink(maMacroName);
+ }
+ return false;
+}
+
+bool
+XclMacroHelper::SetMacroLink( const OUString& rMacroName )
+{
+ // OOXML documents do not store any defined name for VBA macros (while BIFF documents do).
+ bool bOOXML = GetOutput() == EXC_OUTPUT_XML_2007;
+ if (!rMacroName.isEmpty() && !bOOXML)
+ {
+ sal_uInt16 nExtSheet = GetLocalLinkManager().FindExtSheet( EXC_EXTSH_OWNDOC );
+ sal_uInt16 nNameIdx
+ = GetNameManager().InsertMacroCall(rMacroName, /*bVBasic=*/true, /*bFunc=*/false);
+ mxMacroLink = GetFormulaCompiler().CreateNameXFormula( nExtSheet, nNameIdx );
+ return true;
+ }
+ return false;
+}
+
+XclExpShapeObj::XclExpShapeObj( XclExpObjectManager& rRoot, css::uno::Reference< css::drawing::XShape > const & xShape, ScDocument* pDoc ) :
+ XclObjAny( rRoot, xShape, pDoc ),
+ XclMacroHelper( rRoot )
+{
+ if (SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape))
+ {
+ ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( pSdrObj );
+ if ( pInfo && !pInfo->GetMacro().isEmpty() )
+// FIXME ooo330-m2: XclControlHelper::GetXclMacroName was removed in upstream sources; they started to call XclTools::GetXclMacroName instead; is this enough? it has only one parameter
+// SetMacroLink( XclControlHelper::GetXclMacroName( pInfo->GetMacro(), rRoot.GetDocShell() ) );
+ SetMacroLink( XclTools::GetXclMacroName( pInfo->GetMacro() ) );
+ }
+}
+
+XclExpShapeObj::~XclExpShapeObj()
+{
+}
+
+void XclExpShapeObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ XclObjAny::WriteSubRecs( rStrm );
+ WriteMacroSubRec( rStrm );
+}
+
+XclExpComments::XclExpComments( SCTAB nTab, XclExpRecordList< XclExpNote >& rNotes )
+ : mnTab( nTab ), mrNotes( rNotes )
+{
+}
+
+void XclExpComments::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mrNotes.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr rComments = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "comments", mnTab + 1 ),
+ XclXmlUtils::GetStreamName( "../", "comments", mnTab + 1 ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml",
+ oox::getRelationship(Relationship::COMMENTS));
+ rStrm.PushStream( rComments );
+
+ if( rStrm.getVersion() == oox::core::ISOIEC_29500_2008 )
+ rComments->startElement( XML_comments,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)),
+ FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)),
+ FSNS(XML_xmlns, XML_v2), rStrm.getNamespaceURL(OOX_NS(mceTest)),
+ FSNS( XML_mc, XML_Ignorable ), "v2" );
+ else
+ rComments->startElement( XML_comments,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)) );
+
+ rComments->startElement(XML_authors);
+
+ typedef std::set<OUString> Authors;
+ Authors aAuthors;
+
+ size_t nNotes = mrNotes.GetSize();
+ for( size_t i = 0; i < nNotes; ++i )
+ {
+ aAuthors.insert( XclXmlUtils::ToOUString( mrNotes.GetRecord( i )->GetAuthor() ) );
+ }
+
+ for( const auto& rAuthor : aAuthors )
+ {
+ rComments->startElement(XML_author);
+ rComments->writeEscaped( rAuthor );
+ rComments->endElement( XML_author );
+ }
+
+ rComments->endElement( XML_authors );
+ rComments->startElement(XML_commentList);
+
+ Authors::const_iterator aAuthorsBegin = aAuthors.begin();
+ for( size_t i = 0; i < nNotes; ++i )
+ {
+ XclExpRecordList< XclExpNote >::RecordRefType xNote = mrNotes.GetRecord( i );
+ Authors::const_iterator aAuthor = aAuthors.find(
+ XclXmlUtils::ToOUString( xNote->GetAuthor() ) );
+ sal_Int32 nAuthorId = distance( aAuthorsBegin, aAuthor );
+ xNote->WriteXml( nAuthorId, rStrm );
+ }
+
+ rComments->endElement( XML_commentList );
+ rComments->endElement( XML_comments );
+
+ rStrm.PopStream();
+}
+
+// object manager =============================================================
+
+XclExpObjectManager::XclExpObjectManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ InitStream( true );
+ mxEscherEx = std::make_shared<XclEscherEx>( GetRoot(), *this, *mxDffStrm );
+}
+
+XclExpObjectManager::XclExpObjectManager( const XclExpObjectManager& rParent ) :
+ XclExpRoot( rParent.GetRoot() )
+{
+ InitStream( false );
+ mxEscherEx = std::make_shared<XclEscherEx>( GetRoot(), *this, *mxDffStrm, rParent.mxEscherEx.get() );
+}
+
+XclExpObjectManager::~XclExpObjectManager()
+{
+}
+
+XclExpDffAnchorBase* XclExpObjectManager::CreateDffAnchor() const
+{
+ return new XclExpDffSheetAnchor( GetRoot() );
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::CreateDrawingGroup()
+{
+ return new XclExpMsoDrawingGroup( *mxEscherEx );
+}
+
+void XclExpObjectManager::StartSheet()
+{
+ mxObjList = new XclExpObjList( GetRoot(), *mxEscherEx );
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const SdrPage* pSdrPage )
+{
+ if( pSdrPage )
+ mxEscherEx->AddSdrPage( *pSdrPage, GetOutput() != EXC_OUTPUT_BINARY );
+ // the first dummy object may still be open
+ OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" );
+ while( mxEscherEx->GetGroupLevel() )
+ mxEscherEx->LeaveGroup();
+ mxObjList->EndSheet();
+ return mxObjList;
+}
+
+rtl::Reference< XclExpRecordBase > XclExpObjectManager::ProcessDrawing( const Reference< XShapes >& rxShapes )
+{
+ if( rxShapes.is() )
+ mxEscherEx->AddUnoShapes( rxShapes, GetOutput() != EXC_OUTPUT_BINARY );
+ // the first dummy object may still be open
+ OSL_ENSURE( mxEscherEx->GetGroupLevel() <= 1, "XclExpObjectManager::ProcessDrawing - still groups open?" );
+ while( mxEscherEx->GetGroupLevel() )
+ mxEscherEx->LeaveGroup();
+ mxObjList->EndSheet();
+ return mxObjList;
+}
+
+void XclExpObjectManager::EndDocument()
+{
+ mxEscherEx->EndDocument();
+}
+
+XclExpMsoDrawing* XclExpObjectManager::GetMsodrawingPerSheet()
+{
+ return mxObjList->GetMsodrawingPerSheet();
+}
+
+bool XclExpObjectManager::HasObj() const
+{
+ return !mxObjList->empty();
+}
+
+sal_uInt16 XclExpObjectManager::AddObj( std::unique_ptr<XclObj> pObjRec )
+{
+ return mxObjList->Add( std::move(pObjRec) );
+}
+
+std::unique_ptr<XclObj> XclExpObjectManager::RemoveLastObj()
+{
+ return mxObjList->pop_back();
+}
+
+void XclExpObjectManager::InitStream( bool bTempFile )
+{
+ if( bTempFile )
+ {
+ mxTempFile = std::make_shared<::utl::TempFile>();
+ if( mxTempFile->IsValid() )
+ {
+ mxTempFile->EnableKillingFile();
+ mxDffStrm = ::utl::UcbStreamHelper::CreateStream( mxTempFile->GetURL(), StreamMode::STD_READWRITE );
+ }
+ }
+
+ if( !mxDffStrm )
+ mxDffStrm = std::make_unique<SvMemoryStream>();
+
+ mxDffStrm->SetEndian( SvStreamEndian::LITTLE );
+}
+
+XclExpEmbeddedObjectManager::XclExpEmbeddedObjectManager(
+ const XclExpObjectManager& rParent, const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY ) :
+ XclExpObjectManager( rParent ),
+ maPageSize( rPageSize ),
+ mnScaleX( nScaleX ),
+ mnScaleY( nScaleY )
+{
+}
+
+XclExpDffAnchorBase* XclExpEmbeddedObjectManager::CreateDffAnchor() const
+{
+ return new XclExpDffEmbeddedAnchor( GetRoot(), maPageSize, mnScaleX, mnScaleY );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeextlst.cxx b/sc/source/filter/excel/xeextlst.cxx
new file mode 100644
index 000000000..242f21dbb
--- /dev/null
+++ b/sc/source/filter/excel/xeextlst.cxx
@@ -0,0 +1,652 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <xeextlst.hxx>
+#include <xeroot.hxx>
+#include <xestyle.hxx>
+#include <stlpool.hxx>
+#include <scitems.hxx>
+#include <svl/itemset.hxx>
+#include <svl/intitem.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+
+using namespace ::oox;
+
+XclExpExt::XclExpExt( const XclExpRoot& rRoot ):
+ XclExpRoot(rRoot)
+{
+}
+
+XclExtLst::XclExtLst( const XclExpRoot& rRoot ):
+ XclExpRoot(rRoot)
+{
+}
+
+XclExpExtNegativeColor::XclExpExtNegativeColor( const Color& rColor ):
+ maColor(rColor)
+{
+}
+
+void XclExpExtNegativeColor::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElementNS( XML_x14, XML_negativeFillColor,
+ XML_rgb, XclXmlUtils::ToOString(maColor) );
+}
+
+XclExpExtAxisColor::XclExpExtAxisColor( const Color& rColor ):
+ maAxisColor(rColor)
+{
+}
+
+void XclExpExtAxisColor::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElementNS( XML_x14, XML_axisColor,
+ XML_rgb, XclXmlUtils::ToOString(maAxisColor) );
+}
+
+XclExpExtIcon::XclExpExtIcon(const XclExpRoot& rRoot, const std::pair<ScIconSetType, sal_Int32>& rCustomEntry):
+ XclExpRoot(rRoot),
+ nIndex(rCustomEntry.second)
+{
+ pIconSetName = ScIconSetFormat::getIconSetName(rCustomEntry.first);
+}
+
+void XclExpExtIcon::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ if (nIndex == -1)
+ {
+ nIndex = 0;
+ pIconSetName = "NoIcons";
+ }
+
+ rWorksheet->singleElementNS(XML_x14, XML_cfIcon,
+ XML_iconSet, pIconSetName,
+ XML_iconId, OString::number(nIndex));
+}
+
+XclExpExtCfvo::XclExpExtCfvo( const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rSrcPos, bool bFirst ):
+ XclExpRoot(rRoot),
+ meType(rEntry.GetType()),
+ mbFirst(bFirst)
+{
+ if( rEntry.GetType() == COLORSCALE_FORMULA )
+ {
+ const ScTokenArray* pArr = rEntry.GetFormula();
+ OUString aFormula;
+ if(pArr)
+ {
+ aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), rSrcPos, pArr);
+ }
+ maValue = OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8 );
+ }
+ else
+ maValue = OString::number(rEntry.GetValue());
+}
+
+namespace {
+
+const char* getColorScaleType( ScColorScaleEntryType eType, bool bFirst )
+{
+ switch(eType)
+ {
+ case COLORSCALE_MIN:
+ return "min";
+ case COLORSCALE_MAX:
+ return "max";
+ case COLORSCALE_PERCENT:
+ return "percent";
+ case COLORSCALE_FORMULA:
+ return "formula";
+ case COLORSCALE_AUTO:
+ if(bFirst)
+ return "autoMin";
+ else
+ return "autoMax";
+ case COLORSCALE_PERCENTILE:
+ return "percentile";
+ default:
+ break;
+ }
+ return "num";
+}
+
+}
+
+void XclExpExtCfvo::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS(XML_x14, XML_cfvo, XML_type, getColorScaleType(meType, mbFirst));
+
+ if (meType == COLORSCALE_FORMULA ||
+ meType == COLORSCALE_PERCENT ||
+ meType == COLORSCALE_PERCENTILE ||
+ meType == COLORSCALE_VALUE)
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+ rWorksheet->writeEscaped(maValue.getStr());
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_cfvo);
+}
+
+XclExpExtCF::XclExpExtCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormat ):
+ XclExpRoot(rRoot),
+ mrFormat(rFormat)
+{
+}
+
+namespace {
+
+bool RequiresFixedFormula(ScConditionMode eMode)
+{
+ switch (eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+OString GetFixedFormula(ScConditionMode eMode, const ScAddress& rAddress, std::string_view rText)
+{
+ OStringBuffer aBuffer;
+ XclXmlUtils::ToOString(aBuffer, rAddress);
+ OString aPos = aBuffer.makeStringAndClear();
+ switch (eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ return OString("LEFT(" + aPos + ",LEN(" + rText + "))=" + rText);
+ case ScConditionMode::EndsWith:
+ return OString("RIGHT(" + aPos + ",LEN(" + rText + "))=" + rText);
+ case ScConditionMode::ContainsText:
+ return OString(OString::Concat("NOT(ISERROR(SEARCH(") + rText + "," + aPos + ")))");
+ case ScConditionMode::NotContainsText:
+ return OString(OString::Concat("ISERROR(SEARCH(") + rText + "," + aPos + "))");
+ default:
+ break;
+ }
+
+ return "";
+}
+
+}
+
+void XclExpExtCF::SaveXml( XclExpXmlStream& rStrm )
+{
+ OUString aStyleName = mrFormat.GetStyle();
+ SfxStyleSheetBasePool* pPool = GetDoc().GetStyleSheetPool();
+ SfxStyleSheetBase* pStyle = pPool->Find(aStyleName, SfxStyleFamily::Para);
+ SfxItemSet& rSet = pStyle->GetItemSet();
+
+ std::unique_ptr<ScTokenArray> pTokenArray(mrFormat.CreateFlatCopiedTokenArray(0));
+ aFormula = XclXmlUtils::ToOUString( GetCompileFormulaContext(), mrFormat.GetValidSrcPos(), pTokenArray.get());
+
+ std::unique_ptr<XclExpColor> pColor(new XclExpColor);
+ if(!pColor->FillFromItemSet( rSet ))
+ pColor.reset();
+
+ std::unique_ptr<XclExpCellBorder> pBorder(new XclExpCellBorder);
+ if (!pBorder->FillFromItemSet( rSet, GetPalette(), GetBiff()) )
+ pBorder.reset();
+
+ std::unique_ptr<XclExpCellAlign> pAlign(new XclExpCellAlign);
+ if (!pAlign->FillFromItemSet(*this, rSet, false, GetBiff()))
+ pAlign.reset();
+
+ std::unique_ptr<XclExpCellProt> pCellProt(new XclExpCellProt);
+ if (!pCellProt->FillFromItemSet( rSet ))
+ pCellProt.reset();
+
+ std::unique_ptr<XclExpDxfFont> pFont(new XclExpDxfFont(GetRoot(), rSet));
+
+ std::unique_ptr<XclExpNumFmt> pNumFormat;
+ if( const SfxUInt32Item* pPoolItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ sal_uInt32 nScNumFmt = pPoolItem->GetValue();
+ XclExpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
+ sal_uInt32 nXclNumFmt = rNumFmtBuffer.Insert(nScNumFmt);
+ pNumFormat.reset(new XclExpNumFmt(nScNumFmt, nXclNumFmt, rNumFmtBuffer.GetFormatCode(nScNumFmt)));
+ }
+
+ XclExpDxf rDxf( GetRoot(),
+ std::move(pAlign),
+ std::move(pBorder),
+ std::move(pFont),
+ std::move(pNumFormat),
+ std::move(pCellProt),
+ std::move(pColor) );
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ ScConditionMode eOperation = mrFormat.GetOperation();
+ if (RequiresFixedFormula(eOperation))
+ {
+ ScAddress aFixedFormulaPos = mrFormat.GetValidSrcPos();
+ OString aFixedFormulaText = aFormula.toUtf8();
+ OString aFixedFormula = GetFixedFormula(eOperation, aFixedFormulaPos, aFixedFormulaText);
+ rWorksheet->startElementNS( XML_xm, XML_f );
+ rWorksheet->writeEscaped(aFixedFormula.getStr());
+ rWorksheet->endElementNS( XML_xm, XML_f );
+
+ rWorksheet->startElementNS( XML_xm, XML_f );
+ rWorksheet->writeEscaped( aFormula );
+ rWorksheet->endElementNS( XML_xm, XML_f );
+ rDxf.SaveXmlExt(rStrm);
+ }
+ else
+ {
+ rWorksheet->startElementNS(XML_xm, XML_f);
+ rWorksheet->writeEscaped(aFormula);
+ rWorksheet->endElementNS(XML_xm, XML_f);
+ rDxf.SaveXmlExt(rStrm);
+ }
+}
+
+XclExpExtDataBar::XclExpExtDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, const ScAddress& rPos ):
+ XclExpRoot(rRoot)
+{
+ const ScDataBarFormatData& rFormatData = *rFormat.GetDataBarData();
+ mpLowerLimit.reset(new XclExpExtCfvo(*this, *rFormatData.mpLowerLimit, rPos, true));
+ mpUpperLimit.reset(new XclExpExtCfvo(*this, *rFormatData.mpUpperLimit, rPos, false));
+ if (rFormatData.mxNegativeColor)
+ mpNegativeColor.reset(new XclExpExtNegativeColor(*rFormatData.mxNegativeColor));
+ else
+ mpNegativeColor.reset( new XclExpExtNegativeColor( rFormatData.maPositiveColor ) );
+ mpAxisColor.reset( new XclExpExtAxisColor( rFormatData.maAxisColor ) );
+
+ meAxisPosition = rFormatData.meAxisPosition;
+ mbGradient = rFormatData.mbGradient;
+ mnMinLength = rFormatData.mnMinLength;
+ mnMaxLength = rFormatData.mnMaxLength;
+}
+
+namespace {
+
+const char* getAxisPosition(databar::ScAxisPosition eAxisPosition)
+{
+ switch(eAxisPosition)
+ {
+ case databar::NONE:
+ return "none";
+ case databar::AUTOMATIC:
+ return "automatic";
+ case databar::MIDDLE:
+ return "middle";
+ }
+ return "";
+}
+
+const char* GetOperatorString(ScConditionMode eMode)
+{
+ const char* pRet = nullptr;
+ switch(eMode)
+ {
+ case ScConditionMode::Equal:
+ pRet = "equal";
+ break;
+ case ScConditionMode::Less:
+ pRet = "lessThan";
+ break;
+ case ScConditionMode::Greater:
+ pRet = "greaterThan";
+ break;
+ case ScConditionMode::EqLess:
+ pRet = "lessThanOrEqual";
+ break;
+ case ScConditionMode::EqGreater:
+ pRet = "greaterThanOrEqual";
+ break;
+ case ScConditionMode::NotEqual:
+ pRet = "notEqual";
+ break;
+ case ScConditionMode::Between:
+ pRet = "between";
+ break;
+ case ScConditionMode::NotBetween:
+ pRet = "notBetween";
+ break;
+ case ScConditionMode::Duplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::NotDuplicate:
+ pRet = nullptr;
+ break;
+ case ScConditionMode::BeginsWith:
+ pRet = "beginsWith";
+ break;
+ case ScConditionMode::EndsWith:
+ pRet = "endsWith";
+ break;
+ case ScConditionMode::ContainsText:
+ pRet = "containsText";
+ break;
+ case ScConditionMode::NotContainsText:
+ pRet = "notContains";
+ break;
+ case ScConditionMode::Direct:
+ break;
+ case ScConditionMode::NONE:
+ default:
+ break;
+ }
+ return pRet;
+}
+
+const char* GetTypeString(ScConditionMode eMode)
+{
+ switch(eMode)
+ {
+ case ScConditionMode::Direct:
+ return "expression";
+ case ScConditionMode::BeginsWith:
+ return "beginsWith";
+ case ScConditionMode::EndsWith:
+ return "endsWith";
+ case ScConditionMode::ContainsText:
+ return "containsText";
+ case ScConditionMode::NotContainsText:
+ return "notContainsText";
+ default:
+ return "cellIs";
+ }
+}
+
+}
+
+void XclExpExtDataBar::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_dataBar,
+ XML_minLength, OString::number(mnMinLength),
+ XML_maxLength, OString::number(mnMaxLength),
+ XML_axisPosition, getAxisPosition(meAxisPosition),
+ XML_gradient, ToPsz(mbGradient) );
+
+ mpLowerLimit->SaveXml( rStrm );
+ mpUpperLimit->SaveXml( rStrm );
+ mpNegativeColor->SaveXml( rStrm );
+ mpAxisColor->SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_dataBar );
+}
+
+XclExpExtIconSet::XclExpExtIconSet(const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, const ScAddress& rPos):
+ XclExpRoot(rRoot)
+{
+ const ScIconSetFormatData& rData = *rFormat.GetIconSetData();
+ for (auto const& itr : rData.m_Entries)
+ {
+ maCfvos.AppendNewRecord(new XclExpExtCfvo(*this, *itr, rPos, false));
+ }
+ mbCustom = rData.mbCustom;
+ mbReverse = rData.mbReverse;
+ mbShowValue = rData.mbShowValue;
+ mpIconSetName = ScIconSetFormat::getIconSetName(rData.eIconSetType);
+
+ if (mbCustom)
+ {
+ for (const auto& rItem : rData.maCustomVector)
+ {
+ maCustom.AppendNewRecord(new XclExpExtIcon(*this, rItem));
+ }
+ }
+}
+
+void XclExpExtIconSet::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElementNS(XML_x14, XML_iconSet,
+ XML_iconSet, mpIconSetName,
+ XML_custom, sax_fastparser::UseIf(ToPsz10(mbCustom), mbCustom),
+ XML_reverse, ToPsz10(mbReverse),
+ XML_showValue, ToPsz10(mbShowValue));
+
+ maCfvos.SaveXml(rStrm);
+
+ if (mbCustom)
+ {
+ maCustom.SaveXml(rStrm);
+ }
+
+ rWorksheet->endElementNS(XML_x14, XML_iconSet);
+}
+
+XclExpExtCfRule::XclExpExtCfRule( const XclExpRoot& rRoot, const ScFormatEntry& rFormat, const ScAddress& rPos, const OString& rId, sal_Int32 nPriority ):
+ XclExpRoot(rRoot),
+ maId(rId),
+ pType(nullptr),
+ mnPriority(nPriority),
+ mOperator(nullptr)
+{
+ switch (rFormat.GetType())
+ {
+ case ScFormatEntry::Type::Databar:
+ {
+ const ScDataBarFormat& rDataBar = static_cast<const ScDataBarFormat&>(rFormat);
+ mxEntry = new XclExpExtDataBar( *this, rDataBar, rPos );
+ pType = "dataBar";
+ }
+ break;
+ case ScFormatEntry::Type::Iconset:
+ {
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(rFormat);
+ mxEntry = new XclExpExtIconSet(*this, rIconSet, rPos);
+ pType = "iconSet";
+ }
+ break;
+ case ScFormatEntry::Type::ExtCondition:
+ {
+ const ScCondFormatEntry& rCondFormat = static_cast<const ScCondFormatEntry&>(rFormat);
+ mxEntry = new XclExpExtCF(*this, rCondFormat);
+ pType = GetTypeString(rCondFormat.GetOperation());
+ mOperator = GetOperatorString( rCondFormat.GetOperation() );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void XclExpExtCfRule::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (!mxEntry)
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_cfRule,
+ XML_type, pType,
+ XML_priority, sax_fastparser::UseIf(OString::number(mnPriority + 1), mnPriority != -1),
+ XML_operator, mOperator,
+ XML_id, maId );
+
+ mxEntry->SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_cfRule );
+
+}
+
+XclExpExtConditionalFormatting::XclExpExtConditionalFormatting( const XclExpRoot& rRoot,
+ std::vector<XclExpExtCondFormatData>& rData, const ScRangeList& rRange):
+ XclExpRoot(rRoot),
+ maRange(rRange)
+{
+ ScAddress aAddr = maRange.front().aStart;
+ for (const auto& rItem : rData)
+ {
+ const ScFormatEntry* pEntry = rItem.pEntry;
+ switch (pEntry->GetType())
+ {
+ case ScFormatEntry::Type::Iconset:
+ {
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pEntry);
+ bool bNeedsExt = false;
+ switch (rIconSet.GetIconSetData()->eIconSetType)
+ {
+ case IconSet_3Triangles:
+ case IconSet_3Smilies:
+ case IconSet_3ColorSmilies:
+ case IconSet_5Boxes:
+ case IconSet_3Stars:
+ bNeedsExt = true;
+ break;
+ default:
+ break;
+ }
+
+ if (rIconSet.GetIconSetData()->mbCustom)
+ bNeedsExt = true;
+
+ if (bNeedsExt)
+ {
+ maCfRules.AppendNewRecord(new XclExpExtCfRule(*this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ }
+ }
+ break;
+ case ScFormatEntry::Type::Databar:
+ maCfRules.AppendNewRecord(new XclExpExtCfRule( *this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ break;
+ case ScFormatEntry::Type::ExtCondition:
+ maCfRules.AppendNewRecord(new XclExpExtCfRule( *this, *pEntry, aAddr, rItem.aGUID, rItem.nPriority));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void XclExpExtConditionalFormatting::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElementNS( XML_x14, XML_conditionalFormatting,
+ FSNS( XML_xmlns, XML_xm ), rStrm.getNamespaceURL(OOX_NS(xm)) );
+
+ maCfRules.SaveXml( rStrm );
+ rWorksheet->startElementNS(XML_xm, XML_sqref);
+ rWorksheet->write(XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maRange));
+
+ rWorksheet->endElementNS( XML_xm, XML_sqref );
+
+ rWorksheet->endElementNS( XML_x14, XML_conditionalFormatting );
+}
+
+XclExpExtCalcPr::XclExpExtCalcPr( const XclExpRoot& rRoot, formula::FormulaGrammar::AddressConvention eConv ):
+ XclExpExt( rRoot )
+{
+ maURI = OString("{7626C862-2A13-11E5-B345-FEFF819CDC9F}");
+
+ switch (eConv)
+ {
+ case formula::FormulaGrammar::CONV_OOO:
+ maSyntax = OString("CalcA1");
+ break;
+ case formula::FormulaGrammar::CONV_XL_A1:
+ maSyntax = OString("ExcelA1");
+ break;
+ case formula::FormulaGrammar::CONV_XL_R1C1:
+ maSyntax = OString("ExcelR1C1");
+ break;
+ case formula::FormulaGrammar::CONV_A1_XL_A1:
+ maSyntax = OString("CalcA1ExcelA1");
+ break;
+ case formula::FormulaGrammar::CONV_UNSPECIFIED:
+ case formula::FormulaGrammar::CONV_ODF:
+ case formula::FormulaGrammar::CONV_XL_OOX:
+ case formula::FormulaGrammar::CONV_LOTUS_A1:
+ case formula::FormulaGrammar::CONV_LAST:
+ maSyntax = OString("Unspecified");
+ break;
+ }
+}
+
+void XclExpExtCalcPr::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_ext,
+ FSNS(XML_xmlns, XML_loext), rStrm.getNamespaceURL(OOX_NS(loext)),
+ XML_uri, maURI );
+
+ rWorksheet->singleElementNS(XML_loext, XML_extCalcPr, XML_stringRefSyntax, maSyntax);
+
+ rWorksheet->endElement( XML_ext );
+}
+
+XclExpExtCondFormat::XclExpExtCondFormat( const XclExpRoot& rRoot ):
+ XclExpExt( rRoot )
+{
+ maURI = OString("{78C0D931-6437-407d-A8EE-F0AAD7539E65}");
+}
+
+void XclExpExtCondFormat::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_ext,
+ FSNS(XML_xmlns, XML_x14), rStrm.getNamespaceURL(OOX_NS(xls14Lst)),
+ XML_uri, maURI );
+
+ rWorksheet->startElementNS(XML_x14, XML_conditionalFormattings);
+
+ maCF.SaveXml( rStrm );
+
+ rWorksheet->endElementNS( XML_x14, XML_conditionalFormattings );
+ rWorksheet->endElement( XML_ext );
+}
+
+void XclExpExtCondFormat::AddRecord( XclExpExtConditionalFormatting* pEntry )
+{
+ maCF.AppendRecord( pEntry );
+}
+
+void XclExtLst::SaveXml( XclExpXmlStream& rStrm )
+{
+ if(maExtEntries.IsEmpty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_extLst);
+
+ maExtEntries.SaveXml(rStrm);
+
+ rWorksheet->endElement( XML_extLst );
+}
+
+void XclExtLst::AddRecord( XclExpExt* pEntry )
+{
+ maExtEntries.AppendRecord( pEntry );
+}
+
+XclExpExt* XclExtLst::GetItem( XclExpExtType eType )
+{
+ size_t n = maExtEntries.GetSize();
+ for( size_t i = 0; i < n; ++i )
+ {
+ if (maExtEntries.GetRecord( i )->GetType() == eType)
+ return maExtEntries.GetRecord( i );
+ }
+
+ return nullptr;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeformula.cxx b/sc/source/filter/excel/xeformula.cxx
new file mode 100644
index 000000000..e6eabd69c
--- /dev/null
+++ b/sc/source/filter/excel/xeformula.cxx
@@ -0,0 +1,2709 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <map>
+#include <addincol.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+#include <externalrefmgr.hxx>
+#include <rangelst.hxx>
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xestring.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+
+using namespace ::formula;
+
+// External reference log =====================================================
+
+XclExpRefLogEntry::XclExpRefLogEntry() :
+ mpUrl( nullptr ),
+ mpFirstTab( nullptr ),
+ mpLastTab( nullptr ),
+ mnFirstXclTab( EXC_TAB_DELETED ),
+ mnLastXclTab( EXC_TAB_DELETED )
+{
+}
+
+// Formula compiler ===========================================================
+
+namespace {
+
+/** Wrapper structure for a processed Calc formula token with additional
+ settings (whitespaces). */
+struct XclExpScToken
+{
+ const FormulaToken* mpScToken; /// Currently processed Calc token.
+ sal_uInt8 mnSpaces; /// Number of spaces before the Calc token.
+
+ explicit XclExpScToken() : mpScToken( nullptr ), mnSpaces( 0 ) {}
+ bool Is() const { return mpScToken != nullptr; }
+ StackVar GetType() const { return mpScToken ? mpScToken->GetType() : svUnknown; }
+ OpCode GetOpCode() const { return mpScToken ? mpScToken->GetOpCode() : ocNone; }
+};
+
+/** Effective token class conversion types. */
+enum XclExpClassConv
+{
+ EXC_CLASSCONV_ORG, /// Keep original class of the token.
+ EXC_CLASSCONV_VAL, /// Convert ARR tokens to VAL class (REF remains unchanged).
+ EXC_CLASSCONV_ARR /// Convert VAL tokens to ARR class (REF remains unchanged).
+};
+
+/** Token class conversion and position of a token in the token array. */
+struct XclExpTokenConvInfo
+{
+ sal_uInt16 mnTokPos; /// Position of the token in the token array.
+ XclFuncParamConv meConv; /// Token class conversion type.
+ bool mbValType; /// Data type (false = REFTYPE, true = VALTYPE).
+};
+
+/** Vector of token position and conversion for all operands of an operator,
+ or for all parameters of a function. */
+struct XclExpOperandList : public std::vector< XclExpTokenConvInfo >
+{
+ explicit XclExpOperandList() { reserve( 2 ); }
+ void AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType );
+};
+
+void XclExpOperandList::AppendOperand( sal_uInt16 nTokPos, XclFuncParamConv eConv, bool bValType )
+{
+ resize( size() + 1 );
+ XclExpTokenConvInfo& rConvInfo = back();
+ rConvInfo.mnTokPos = nTokPos;
+ rConvInfo.meConv = eConv;
+ rConvInfo.mbValType = bValType;
+}
+
+typedef std::shared_ptr< XclExpOperandList > XclExpOperandListRef;
+
+/** Encapsulates all data needed for a call to an external function (macro, add-in). */
+struct XclExpExtFuncData
+{
+ OUString maFuncName; /// Name of the function.
+ bool mbVBasic; /// True = Visual Basic macro call.
+ bool mbHidden; /// True = Create hidden defined name.
+
+ explicit XclExpExtFuncData() : mbVBasic( false ), mbHidden( false ) {}
+ void Set( const OUString& rFuncName, bool bVBasic, bool bHidden );
+};
+
+void XclExpExtFuncData::Set( const OUString& rFuncName, bool bVBasic, bool bHidden )
+{
+ maFuncName = rFuncName;
+ mbVBasic = bVBasic;
+ mbHidden = bHidden;
+}
+
+/** Encapsulates all data needed to process an entire function. */
+class XclExpFuncData
+{
+public:
+ explicit XclExpFuncData(
+ const XclExpScToken& rTokData,
+ const XclFunctionInfo& rFuncInfo,
+ const XclExpExtFuncData& rExtFuncData );
+
+ const FormulaToken& GetScToken() const { return *mrTokData.mpScToken; }
+ OpCode GetOpCode() const { return mrFuncInfo.meOpCode; }
+ sal_uInt16 GetXclFuncIdx() const { return mrFuncInfo.mnXclFunc; }
+ bool IsVolatile() const { return mrFuncInfo.IsVolatile(); }
+ bool IsFixedParamCount() const { return mrFuncInfo.IsFixedParamCount(); }
+ bool IsAddInEquivalent() const { return mrFuncInfo.IsAddInEquivalent(); }
+ bool IsMacroFunc() const { return mrFuncInfo.IsMacroFunc(); }
+ sal_uInt8 GetSpaces() const { return mrTokData.mnSpaces; }
+ const XclExpExtFuncData& GetExtFuncData() const { return maExtFuncData; }
+ sal_uInt8 GetReturnClass() const { return mrFuncInfo.mnRetClass; }
+
+ const XclFuncParamInfo& GetParamInfo() const;
+ bool IsCalcOnlyParam() const;
+ bool IsExcelOnlyParam() const;
+ void IncParamInfoIdx();
+
+ sal_uInt8 GetMinParamCount() const { return mrFuncInfo.mnMinParamCount; }
+ sal_uInt8 GetMaxParamCount() const { return mrFuncInfo.mnMaxParamCount; }
+ sal_uInt8 GetParamCount() const { return static_cast< sal_uInt8 >( mxOperands->size() ); }
+ void FinishParam( sal_uInt16 nTokPos );
+ const XclExpOperandListRef& GetOperandList() const { return mxOperands; }
+
+ ScfUInt16Vec& GetAttrPosVec() { return maAttrPosVec; }
+ void AppendAttrPos( sal_uInt16 nPos ) { maAttrPosVec.push_back( nPos ); }
+
+private:
+ ScfUInt16Vec maAttrPosVec; /// Token array positions of tAttr tokens.
+ const XclExpScToken& mrTokData; /// Data about processed function name token.
+ const XclFunctionInfo& mrFuncInfo; /// Constant data about processed function.
+ XclExpExtFuncData maExtFuncData; /// Data for external functions (macro, add-in).
+ XclExpOperandListRef mxOperands; /// Class conversion and position of all parameters.
+ const XclFuncParamInfo* mpParamInfo; /// Information for current parameter.
+};
+
+XclExpFuncData::XclExpFuncData( const XclExpScToken& rTokData,
+ const XclFunctionInfo& rFuncInfo, const XclExpExtFuncData& rExtFuncData ) :
+ mrTokData( rTokData ),
+ mrFuncInfo( rFuncInfo ),
+ maExtFuncData( rExtFuncData ),
+ mxOperands( std::make_shared<XclExpOperandList>() ),
+ mpParamInfo( rFuncInfo.mpParamInfos )
+{
+ OSL_ENSURE( mrTokData.mpScToken, "XclExpFuncData::XclExpFuncData - missing core token" );
+ // set name of an add-in function
+ if( (maExtFuncData.maFuncName.isEmpty()) && dynamic_cast< const FormulaExternalToken* >( mrTokData.mpScToken ) )
+ maExtFuncData.Set( GetScToken().GetExternal(), true, false );
+}
+
+const XclFuncParamInfo& XclExpFuncData::GetParamInfo() const
+{
+ static const XclFuncParamInfo saInvalidInfo = { EXC_PARAM_NONE, EXC_PARAMCONV_ORG, false };
+ return mpParamInfo ? *mpParamInfo : saInvalidInfo;
+}
+
+bool XclExpFuncData::IsCalcOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_CALCONLY);
+}
+
+bool XclExpFuncData::IsExcelOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == EXC_PARAM_EXCELONLY);
+}
+
+void XclExpFuncData::IncParamInfoIdx()
+{
+ if( !mpParamInfo )
+ return;
+
+ // move pointer to next entry, if something explicit follows
+ if( (o3tl::make_unsigned( mpParamInfo - mrFuncInfo.mpParamInfos + 1 ) < EXC_FUNCINFO_PARAMINFO_COUNT) && (mpParamInfo[ 1 ].meValid != EXC_PARAM_NONE) )
+ ++mpParamInfo;
+ // if last parameter type is 'Excel-only' or 'Calc-only', do not repeat it
+ else if( IsExcelOnlyParam() || IsCalcOnlyParam() )
+ mpParamInfo = nullptr;
+ // points to last info, but parameter pairs expected, move to previous info
+ else if( mrFuncInfo.IsParamPairs() )
+ --mpParamInfo;
+ // otherwise: repeat last parameter class
+}
+
+void XclExpFuncData::FinishParam( sal_uInt16 nTokPos )
+{
+ // write token class conversion info for this parameter
+ const XclFuncParamInfo& rParamInfo = GetParamInfo();
+ mxOperands->AppendOperand( nTokPos, rParamInfo.meConv, rParamInfo.mbValType );
+ // move to next parameter info structure
+ IncParamInfoIdx();
+}
+
+// compiler configuration -----------------------------------------------------
+
+/** Type of token class handling. */
+enum XclExpFmlaClassType
+{
+ EXC_CLASSTYPE_CELL, /// Cell formula, shared formula.
+ EXC_CLASSTYPE_ARRAY, /// Array formula, conditional formatting, data validation.
+ EXC_CLASSTYPE_NAME /// Defined name, range list.
+};
+
+/** Configuration data of the formula compiler. */
+struct XclExpCompConfig
+{
+ XclFormulaType meType; /// Type of the formula to be created.
+ XclExpFmlaClassType meClassType; /// Token class handling type.
+ bool mbLocalLinkMgr; /// True = local (per-sheet) link manager, false = global.
+ bool mbFromCell; /// True = Any kind of cell formula (cell, array, shared).
+ bool mb3DRefOnly; /// True = Only 3D references allowed (e.g. names).
+ bool mbAllowArrays; /// True = Allow inline arrays.
+};
+
+/** The table containing configuration data for all formula types. */
+const XclExpCompConfig spConfigTable[] =
+{
+ // formula type token class type lclLM inCell 3dOnly allowArray
+ { EXC_FMLATYPE_CELL, EXC_CLASSTYPE_CELL, true, true, false, true },
+ { EXC_FMLATYPE_SHARED, EXC_CLASSTYPE_CELL, true, true, false, true },
+ { EXC_FMLATYPE_MATRIX, EXC_CLASSTYPE_ARRAY, true, true, false, true },
+ { EXC_FMLATYPE_CONDFMT, EXC_CLASSTYPE_ARRAY, true, false, false, false },
+ { EXC_FMLATYPE_DATAVAL, EXC_CLASSTYPE_ARRAY, true, false, false, false },
+ { EXC_FMLATYPE_NAME, EXC_CLASSTYPE_NAME, false, false, true, true },
+ { EXC_FMLATYPE_CHART, EXC_CLASSTYPE_NAME, true, false, true, true },
+ { EXC_FMLATYPE_CONTROL, EXC_CLASSTYPE_NAME, true, false, false, false },
+ { EXC_FMLATYPE_WQUERY, EXC_CLASSTYPE_NAME, true, false, true, false },
+ { EXC_FMLATYPE_LISTVAL, EXC_CLASSTYPE_NAME, true, false, false, false }
+};
+
+/** Working data of the formula compiler. Used to push onto a stack for recursive calls. */
+struct XclExpCompData
+{
+ typedef std::shared_ptr< ScTokenArray > ScTokenArrayRef;
+
+ const XclExpCompConfig& mrCfg; /// Configuration for current formula type.
+ ScTokenArrayRef mxOwnScTokArr; /// Own clone of a Calc token array.
+ XclTokenArrayIterator maTokArrIt; /// Iterator in Calc token array.
+ XclExpLinkManager* mpLinkMgr; /// Link manager for current context (local/global).
+ XclExpRefLog* mpRefLog; /// Log for external references.
+ const ScAddress* mpScBasePos; /// Current cell position of the formula.
+
+ ScfUInt8Vec maTokVec; /// Byte vector containing token data.
+ ScfUInt8Vec maExtDataVec; /// Byte vector containing extended data (arrays, stacked NLRs).
+ std::vector< XclExpOperandListRef >
+ maOpListVec; /// Formula structure, maps operators to their operands.
+ ScfUInt16Vec maOpPosStack; /// Stack with positions of operand tokens waiting for an operator.
+ bool mbStopAtSep; /// True = Stop subexpression creation at an ocSep token.
+ bool mbVolatile; /// True = Formula contains volatile function.
+ bool mbOk; /// Current state of the compiler.
+
+ explicit XclExpCompData( const XclExpCompConfig* pCfg );
+};
+
+XclExpCompData::XclExpCompData( const XclExpCompConfig* pCfg ) :
+ mrCfg( pCfg ? *pCfg : spConfigTable[ 0 ] ),
+ mpLinkMgr( nullptr ),
+ mpRefLog( nullptr ),
+ mpScBasePos( nullptr ),
+ mbStopAtSep( false ),
+ mbVolatile( false ),
+ mbOk( pCfg != nullptr )
+{
+ OSL_ENSURE( pCfg, "XclExpFmlaCompImpl::Init - unknown formula type" );
+}
+
+} // namespace
+
+/** Implementation class of the export formula compiler. */
+class XclExpFmlaCompImpl : protected XclExpRoot, protected XclTokenArrayHelper
+{
+public:
+ explicit XclExpFmlaCompImpl( const XclExpRoot& rRoot );
+
+ /** Creates an Excel token array from the passed Calc token array. */
+ XclTokenArrayRef CreateFormula(
+ XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos = nullptr, XclExpRefLog* pRefLog = nullptr );
+ /** Creates a single error token containing the passed error code. */
+ XclTokenArrayRef CreateErrorFormula( sal_uInt8 nErrCode );
+ /** Creates a single token for a special cell reference. */
+ XclTokenArrayRef CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos );
+ /** Creates a single tNameXR token for a reference to an external name. */
+ XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName );
+
+ /** Returns true, if the passed formula type allows 3D references only. */
+ bool Is3DRefOnly( XclFormulaType eType ) const;
+
+ bool IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const;
+ bool IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const;
+
+private:
+ const XclExpCompConfig* GetConfigForType( XclFormulaType eType ) const;
+ sal_uInt16 GetSize() const { return static_cast< sal_uInt16 >( mxData->maTokVec.size() ); }
+
+ void Init( XclFormulaType eType );
+ void Init( XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog );
+
+ void RecalcTokenClasses();
+ void RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo, XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass );
+
+ void FinalizeFormula();
+ XclTokenArrayRef CreateTokenArray();
+
+ // compiler ---------------------------------------------------------------
+ // XclExpScToken: pass-by-value and return-by-value is intended
+
+ const FormulaToken* GetNextRawToken();
+ const FormulaToken* PeekNextRawToken() const;
+
+ bool GetNextToken( XclExpScToken& rTokData );
+ XclExpScToken GetNextToken();
+
+ XclExpScToken Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep );
+ XclExpScToken SkipExpression( XclExpScToken aTokData, bool bStopAtSep );
+
+ XclExpScToken OrTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken AndTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken CompareTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken ConcatTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken AddSubTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken MulDivTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken PowTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken ListTerm( XclExpScToken aTokData, bool bInParentheses );
+ XclExpScToken IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp );
+ XclExpScToken RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp );
+ XclExpScToken Factor( XclExpScToken aTokData );
+
+ // formula structure ------------------------------------------------------
+
+ void ProcessDouble( const XclExpScToken& rTokData );
+ void ProcessString( const XclExpScToken& rTokData );
+ void ProcessMissing( const XclExpScToken& rTokData );
+ void ProcessBad( const XclExpScToken& rTokData );
+ void ProcessParentheses( const XclExpScToken& rTokData );
+ void ProcessBoolean( const XclExpScToken& rTokData );
+ void ProcessDdeLink( const XclExpScToken& rTokData );
+ void ProcessExternal( const XclExpScToken& rTokData );
+ void ProcessMatrix( const XclExpScToken& rTokData );
+
+ void ProcessFunction( const XclExpScToken& rTokData );
+ void PrepareFunction( const XclExpFuncData& rFuncData );
+ void FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces );
+ void FinishIfFunction( XclExpFuncData& rFuncData );
+ void FinishChooseFunction( XclExpFuncData& rFuncData );
+
+ XclExpScToken ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData );
+ void PrepareParam( XclExpFuncData& rFuncData );
+ void FinishParam( XclExpFuncData& rFuncData );
+ void AppendDefaultParam( XclExpFuncData& rFuncData );
+ void AppendTrailingParam( XclExpFuncData& rFuncData );
+
+ // reference handling -----------------------------------------------------
+
+ SCTAB GetScTab( const ScSingleRefData& rRefData ) const;
+
+ void ConvertRefData( ScSingleRefData& rRefData, XclAddress& rXclPos,
+ bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const;
+ void ConvertRefData( ScComplexRefData& rRefData, XclRange& rXclRange,
+ bool bNatLangRef ) const;
+
+ XclExpRefLogEntry* GetNewRefLogEntry();
+ void ProcessCellRef( const XclExpScToken& rTokData );
+ void ProcessRangeRef( const XclExpScToken& rTokData );
+ void ProcessExternalCellRef( const XclExpScToken& rTokData );
+ void ProcessExternalRangeRef( const XclExpScToken& rTokData );
+ void ProcessDefinedName( const XclExpScToken& rTokData );
+ void ProcessExternalName( const XclExpScToken& rTokData );
+
+ // token vector -----------------------------------------------------------
+
+ void PushOperandPos( sal_uInt16 nTokPos );
+ void PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands );
+ sal_uInt16 PopOperandPos();
+
+ void Append( sal_uInt8 nData );
+ void Append( sal_uInt8 nData, size_t nCount );
+ void Append( sal_uInt16 nData );
+ void Append( sal_uInt32 nData );
+ void Append( double fData );
+ void Append( const OUString& rString );
+
+ void AppendAddress( const XclAddress& rXclPos );
+ void AppendRange( const XclRange& rXclRange );
+
+ void AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount );
+
+ void AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 );
+ void AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces = 0 );
+ void AppendNumToken( double fValue, sal_uInt8 nSpaces = 0 );
+ void AppendBoolToken( bool bValue, sal_uInt8 nSpaces = 0 );
+ void AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces = 0 );
+ void AppendMissingToken( sal_uInt8 nSpaces = 0 );
+ void AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces = 0 );
+ void AppendMissingNameToken( const OUString& rName, sal_uInt8 nSpaces = 0 );
+ void AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces = 0 );
+ void AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData );
+ void AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData );
+ void AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData );
+
+ void AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces = 0 );
+ void AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces = 0 );
+ void AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces = 0 );
+ void AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount );
+ void AppendFuncToken( const XclExpFuncData& rFuncData );
+
+ void AppendParenToken( sal_uInt8 nOpenSpaces = 0, sal_uInt8 nCloseSpaces = 0 );
+ void AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType );
+
+ void InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize );
+ void Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset );
+
+ void UpdateAttrGoto( sal_uInt16 nAttrPos );
+
+ bool IsSpaceToken( sal_uInt16 nPos ) const;
+ void RemoveTrailingParen();
+
+ void AppendExt( sal_uInt8 nData );
+ void AppendExt( sal_uInt8 nData, size_t nCount );
+ void AppendExt( sal_uInt16 nData );
+ void AppendExt( double fData );
+ void AppendExt( const OUString& rString );
+
+private:
+ typedef std::map< XclFormulaType, XclExpCompConfig > XclExpCompConfigMap;
+ typedef std::shared_ptr< XclExpCompData > XclExpCompDataRef;
+
+ XclExpCompConfigMap maCfgMap; /// Compiler configuration map for all formula types.
+ XclFunctionProvider maFuncProv; /// Excel function data provider.
+ XclExpCompDataRef mxData; /// Working data for current formula.
+ std::vector< XclExpCompDataRef >
+ maDataStack; /// Stack for working data, when compiler is called recursively.
+ const XclBiff meBiff; /// Cached BIFF version to save GetBiff() calls.
+ const SCCOL mnMaxAbsCol; /// Maximum column index.
+ const SCROW mnMaxAbsRow; /// Maximum row index.
+ const SCCOL mnMaxScCol; /// Maximum column index in Calc itself.
+ const SCROW mnMaxScRow; /// Maximum row index in Calc itself.
+ const sal_uInt16 mnMaxColMask; /// Mask to delete invalid bits in column fields.
+ const sal_uInt32 mnMaxRowMask; /// Mask to delete invalid bits in row fields.
+};
+
+XclExpFmlaCompImpl::XclExpFmlaCompImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maFuncProv( rRoot ),
+ meBiff( rRoot.GetBiff() ),
+ mnMaxAbsCol( rRoot.GetXclMaxPos().Col() ),
+ mnMaxAbsRow( rRoot.GetXclMaxPos().Row() ),
+ mnMaxScCol( rRoot.GetScMaxPos().Col() ),
+ mnMaxScRow( rRoot.GetScMaxPos().Row() ),
+ mnMaxColMask( static_cast< sal_uInt16 >( rRoot.GetXclMaxPos().Col() ) ),
+ mnMaxRowMask( static_cast< sal_uInt32 >( rRoot.GetXclMaxPos().Row() ) )
+{
+ // build the configuration map
+ for(auto const &rEntry : spConfigTable)
+ maCfgMap[ rEntry.meType ] = rEntry;
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateFormula( XclFormulaType eType,
+ const ScTokenArray& rScTokArr, const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ // initialize the compiler
+ Init( eType, rScTokArr, pScBasePos, pRefLog );
+
+ // start compilation, if initialization didn't fail
+ if( mxData->mbOk )
+ {
+ XclExpScToken aTokData( GetNextToken() );
+ FormulaError nScError = rScTokArr.GetCodeError();
+ if( (nScError != FormulaError::NONE) && (!aTokData.Is() || (aTokData.GetOpCode() == ocStop)) )
+ {
+ // #i50253# convert simple ocStop token to error code formula (e.g. =#VALUE!)
+ AppendErrorToken( XclTools::GetXclErrorCode( nScError ), aTokData.mnSpaces );
+ }
+ else if( aTokData.Is() )
+ {
+ aTokData = Expression( aTokData, false, false );
+ }
+ else
+ {
+ OSL_FAIL( "XclExpFmlaCompImpl::CreateFormula - empty token array" );
+ mxData->mbOk = false;
+ }
+
+ if( mxData->mbOk )
+ {
+ // #i44907# auto-generated SUBTOTAL formula cells have trailing ocStop token
+ mxData->mbOk = !aTokData.Is() || (aTokData.GetOpCode() == ocStop);
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::CreateFormula - unknown garbage behind formula" );
+ }
+ }
+
+ // finalize (add tAttrVolatile token, calculate all token classes)
+ RecalcTokenClasses();
+ FinalizeFormula();
+
+ // leave recursive call, create and return the final token array
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateErrorFormula( sal_uInt8 nErrCode )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendErrorToken( nErrCode );
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendOperandTokenId( nTokenId );
+ Append( static_cast<sal_uInt16>(rXclPos.mnRow) );
+ Append( rXclPos.mnCol ); // do not use AppendAddress(), we always need 16-bit column here
+ return CreateTokenArray();
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName )
+{
+ Init( EXC_FMLATYPE_NAME );
+ AppendNameXToken( nExtSheet, nExtName );
+ return CreateTokenArray();
+}
+
+bool XclExpFmlaCompImpl::Is3DRefOnly( XclFormulaType eType ) const
+{
+ const XclExpCompConfig* pCfg = GetConfigForType( eType );
+ return pCfg && pCfg->mb3DRefOnly;
+}
+
+// private --------------------------------------------------------------------
+
+const XclExpCompConfig* XclExpFmlaCompImpl::GetConfigForType( XclFormulaType eType ) const
+{
+ XclExpCompConfigMap::const_iterator aIt = maCfgMap.find( eType );
+ OSL_ENSURE( aIt != maCfgMap.end(), "XclExpFmlaCompImpl::GetConfigForType - unknown formula type" );
+ return (aIt == maCfgMap.end()) ? nullptr : &aIt->second;
+}
+
+void XclExpFmlaCompImpl::Init( XclFormulaType eType )
+{
+ // compiler invoked recursively? - store old working data
+ if( mxData )
+ maDataStack.push_back( mxData );
+ // new compiler working data structure
+ mxData = std::make_shared<XclExpCompData>( GetConfigForType( eType ) );
+}
+
+void XclExpFmlaCompImpl::Init( XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ // common initialization
+ Init( eType );
+
+ // special initialization
+ if( mxData->mbOk ) switch( mxData->mrCfg.meType )
+ {
+ case EXC_FMLATYPE_CELL:
+ case EXC_FMLATYPE_MATRIX:
+ case EXC_FMLATYPE_CHART:
+ mxData->mbOk = pScBasePos != nullptr;
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::Init - missing cell address" );
+ mxData->mpScBasePos = pScBasePos;
+ break;
+ case EXC_FMLATYPE_SHARED:
+ mxData->mbOk = pScBasePos != nullptr;
+ assert(mxData->mbOk && "XclExpFmlaCompImpl::Init - missing cell address");
+ if (mxData->mbOk)
+ {
+ // clone the passed token array, convert references relative to current cell position
+ mxData->mxOwnScTokArr = rScTokArr.Clone();
+ ScCompiler::MoveRelWrap( *mxData->mxOwnScTokArr, GetDoc(), *pScBasePos, GetDoc().MaxCol(), GetDoc().MaxRow() );
+ // don't remember pScBasePos in mxData->mpScBasePos, shared formulas use real relative refs
+ }
+ break;
+ default:;
+ }
+
+ if( mxData->mbOk )
+ {
+ // link manager to be used
+ mxData->mpLinkMgr = mxData->mrCfg.mbLocalLinkMgr ? &GetLocalLinkManager() : &GetGlobalLinkManager();
+
+ // token array iterator (use cloned token array if present)
+ mxData->maTokArrIt.Init( mxData->mxOwnScTokArr ? *mxData->mxOwnScTokArr : rScTokArr, false );
+ mxData->mpRefLog = pRefLog;
+ // Only for OOXML
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ mxData->mpScBasePos = pScBasePos;
+ }
+}
+
+void XclExpFmlaCompImpl::RecalcTokenClasses()
+{
+ if( !mxData->mbOk )
+ return;
+
+ mxData->mbOk = mxData->maOpPosStack.size() == 1;
+ OSL_ENSURE( mxData->mbOk, "XclExpFmlaCompImpl::RecalcTokenClasses - position of root token expected on stack" );
+ if( mxData->mbOk )
+ {
+ /* Cell and array formulas start with VAL conversion and VALTYPE
+ parameter type, defined names start with ARR conversion and
+ REFTYPE parameter type for the root token. */
+ bool bNameFmla = mxData->mrCfg.meClassType == EXC_CLASSTYPE_NAME;
+ XclFuncParamConv eParamConv = bNameFmla ? EXC_PARAMCONV_ARR : EXC_PARAMCONV_VAL;
+ XclExpClassConv eClassConv = bNameFmla ? EXC_CLASSCONV_ARR : EXC_CLASSCONV_VAL;
+ XclExpTokenConvInfo aConvInfo = { PopOperandPos(), eParamConv, !bNameFmla };
+ RecalcTokenClass( aConvInfo, eParamConv, eClassConv, bNameFmla );
+ }
+
+ // clear operand vectors (calls to the expensive InsertZeros() may follow)
+ mxData->maOpListVec.clear();
+ mxData->maOpPosStack.clear();
+}
+
+void XclExpFmlaCompImpl::RecalcTokenClass( const XclExpTokenConvInfo& rConvInfo,
+ XclFuncParamConv ePrevConv, XclExpClassConv ePrevClassConv, bool bWasRefClass )
+{
+ OSL_ENSURE( rConvInfo.mnTokPos < GetSize(), "XclExpFmlaCompImpl::RecalcTokenClass - invalid token position" );
+ sal_uInt8& rnTokenId = mxData->maTokVec[ rConvInfo.mnTokPos ];
+ sal_uInt8 nTokClass = GetTokenClass( rnTokenId );
+
+ // REF tokens in VALTYPE parameters behave like VAL tokens
+ if( rConvInfo.mbValType && (nTokClass == EXC_TOKCLASS_REF) )
+ {
+ nTokClass = EXC_TOKCLASS_VAL;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+
+ // replace RPO conversion of operator with parent conversion
+ XclFuncParamConv eConv = (rConvInfo.meConv == EXC_PARAMCONV_RPO) ? ePrevConv : rConvInfo.meConv;
+
+ // find the effective token class conversion to be performed for this token
+ XclExpClassConv eClassConv = EXC_CLASSCONV_ORG;
+ switch( eConv )
+ {
+ case EXC_PARAMCONV_ORG:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_VAL:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_VAL;
+ break;
+ case EXC_PARAMCONV_ARR:
+ // conversion is forced independent of parent conversion
+ eClassConv = EXC_CLASSCONV_ARR;
+ break;
+ case EXC_PARAMCONV_RPT:
+ switch( ePrevConv )
+ {
+ case EXC_PARAMCONV_ORG:
+ case EXC_PARAMCONV_VAL:
+ case EXC_PARAMCONV_ARR:
+ /* If parent token has REF class (REF token in REFTYPE
+ function parameter), then RPT does not repeat the
+ previous explicit ORG or ARR conversion, but always
+ falls back to VAL conversion. */
+ eClassConv = bWasRefClass ? EXC_CLASSCONV_VAL : ePrevClassConv;
+ break;
+ case EXC_PARAMCONV_RPT:
+ // nested RPT repeats the previous effective conversion
+ eClassConv = ePrevClassConv;
+ break;
+ case EXC_PARAMCONV_RPX:
+ /* If parent token has REF class (REF token in REFTYPE
+ function parameter), then RPX repeats the previous
+ effective conversion (which will be either ORG or ARR,
+ but never VAL), otherwise falls back to ORG conversion. */
+ eClassConv = bWasRefClass ? ePrevClassConv : EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_RPO: // does not occur
+ break;
+ }
+ break;
+ case EXC_PARAMCONV_RPX:
+ /* If current token still has REF class, set previous effective
+ conversion as current conversion. This will not have an effect
+ on the REF token but is needed for RPT parameters of this
+ function that want to repeat this conversion type. If current
+ token is VAL or ARR class, the previous ARR conversion will be
+ repeated on the token, but VAL conversion will not. */
+ eClassConv = ((nTokClass == EXC_TOKCLASS_REF) || (ePrevClassConv == EXC_CLASSCONV_ARR)) ?
+ ePrevClassConv : EXC_CLASSCONV_ORG;
+ break;
+ case EXC_PARAMCONV_RPO: // does not occur (see above)
+ break;
+ }
+
+ // do the token class conversion
+ switch( eClassConv )
+ {
+ case EXC_CLASSCONV_ORG:
+ /* Cell formulas: leave the current token class. Cell formulas
+ are the only type of formulas where all tokens can keep
+ their original token class.
+ Array and defined name formulas: convert VAL to ARR. */
+ if( (mxData->mrCfg.meClassType != EXC_CLASSTYPE_CELL) && (nTokClass == EXC_TOKCLASS_VAL) )
+ {
+ nTokClass = EXC_TOKCLASS_ARR;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ case EXC_CLASSCONV_VAL:
+ // convert ARR to VAL
+ if( nTokClass == EXC_TOKCLASS_ARR )
+ {
+ nTokClass = EXC_TOKCLASS_VAL;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ case EXC_CLASSCONV_ARR:
+ // convert VAL to ARR
+ if( nTokClass == EXC_TOKCLASS_VAL )
+ {
+ nTokClass = EXC_TOKCLASS_ARR;
+ ChangeTokenClass( rnTokenId, nTokClass );
+ }
+ break;
+ }
+
+ // do conversion for nested operands, if token is an operator or function
+ if( rConvInfo.mnTokPos < mxData->maOpListVec.size() )
+ if( const XclExpOperandList* pOperands = mxData->maOpListVec[ rConvInfo.mnTokPos ].get() )
+ for( const auto& rOperand : *pOperands )
+ RecalcTokenClass( rOperand, eConv, eClassConv, nTokClass == EXC_TOKCLASS_REF );
+}
+
+void XclExpFmlaCompImpl::FinalizeFormula()
+{
+ if( mxData->mbOk )
+ {
+ // Volatile? Add a tAttrVolatile token at the beginning of the token array.
+ if( mxData->mbVolatile )
+ {
+ // tAttrSpace token can be extended with volatile flag
+ if( !IsSpaceToken( 0 ) )
+ {
+ InsertZeros( 0, 4 );
+ mxData->maTokVec[ 0 ] = EXC_TOKID_ATTR;
+ }
+ mxData->maTokVec[ 1 ] |= EXC_TOK_ATTR_VOLATILE;
+ }
+
+ // Token array too long? -> error
+ mxData->mbOk = mxData->maTokVec.size() <= EXC_TOKARR_MAXLEN;
+ }
+
+ if( !mxData->mbOk )
+ {
+ // Any unrecoverable error? -> Create a =#NA formula.
+ mxData->maTokVec.clear();
+ mxData->maExtDataVec.clear();
+ mxData->mbVolatile = false;
+ AppendErrorToken( EXC_ERR_NA );
+ }
+}
+
+XclTokenArrayRef XclExpFmlaCompImpl::CreateTokenArray()
+{
+ // create the Excel token array from working data before resetting mxData
+ OSL_ENSURE( mxData->mrCfg.mbAllowArrays || mxData->maExtDataVec.empty(), "XclExpFmlaCompImpl::CreateTokenArray - unexpected extended data" );
+ if( !mxData->mrCfg.mbAllowArrays )
+ mxData->maExtDataVec.clear();
+ XclTokenArrayRef xTokArr = std::make_shared<XclTokenArray>( mxData->maTokVec, mxData->maExtDataVec, mxData->mbVolatile );
+ mxData.reset();
+
+ // compiler invoked recursively? - restore old working data
+ if( !maDataStack.empty() )
+ {
+ mxData = maDataStack.back();
+ maDataStack.pop_back();
+ }
+
+ return xTokArr;
+}
+
+// compiler -------------------------------------------------------------------
+
+const FormulaToken* XclExpFmlaCompImpl::GetNextRawToken()
+{
+ const FormulaToken* pScToken = mxData->maTokArrIt.Get();
+ ++mxData->maTokArrIt;
+ return pScToken;
+}
+
+const FormulaToken* XclExpFmlaCompImpl::PeekNextRawToken() const
+{
+ /* Returns pointer to next raw token in the token array. The token array
+ iterator already points to the next token (A call to GetNextToken()
+ always increases the iterator), so this function just returns the token
+ the iterator points to. To skip space tokens, a copy of the iterator is
+ created and set to the passed skip-spaces mode. If spaces have to be
+ skipped, and the iterator currently points to a space token, the
+ constructor will move it to the next non-space token. */
+ XclTokenArrayIterator aTempIt( mxData->maTokArrIt, true/*bSkipSpaces*/ );
+ return aTempIt.Get();
+}
+
+bool XclExpFmlaCompImpl::GetNextToken( XclExpScToken& rTokData )
+{
+ rTokData.mpScToken = GetNextRawToken();
+ rTokData.mnSpaces = 0;
+ /* TODO: handle ocWhitespace characters? */
+ while (rTokData.GetOpCode() == ocSpaces || rTokData.GetOpCode() == ocWhitespace)
+ {
+ rTokData.mnSpaces += rTokData.mpScToken->GetByte();
+ rTokData.mpScToken = GetNextRawToken();
+ }
+ return rTokData.Is();
+}
+
+XclExpScToken XclExpFmlaCompImpl::GetNextToken()
+{
+ XclExpScToken aTokData;
+ GetNextToken( aTokData );
+ return aTokData;
+}
+
+namespace {
+
+/** Returns the Excel token ID of a comparison operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetCompareTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocLess: return EXC_TOKID_LT;
+ case ocLessEqual: return EXC_TOKID_LE;
+ case ocEqual: return EXC_TOKID_EQ;
+ case ocGreaterEqual: return EXC_TOKID_GE;
+ case ocGreater: return EXC_TOKID_GT;
+ case ocNotEqual: return EXC_TOKID_NE;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a string concatenation operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetConcatTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocAmpersand) ? EXC_TOKID_CONCAT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of an addition/subtraction operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetAddSubTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocAdd: return EXC_TOKID_ADD;
+ case ocSub: return EXC_TOKID_SUB;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a multiplication/division operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetMulDivTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocMul: return EXC_TOKID_MUL;
+ case ocDiv: return EXC_TOKID_DIV;
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a power operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetPowTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocPow) ? EXC_TOKID_POWER : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a trailing unary operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetUnaryPostTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocPercentSign) ? EXC_TOKID_PERCENT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a leading unary operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetUnaryPreTokenId( OpCode eOpCode )
+{
+ switch( eOpCode )
+ {
+ case ocAdd: return EXC_TOKID_UPLUS; // +(1)
+ case ocNeg: return EXC_TOKID_UMINUS; // NEG(1)
+ case ocNegSub: return EXC_TOKID_UMINUS; // -(1)
+ default:;
+ }
+ return EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference list operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetListTokenId( OpCode eOpCode, bool bStopAtSep )
+{
+ return ((eOpCode == ocUnion) || (!bStopAtSep && (eOpCode == ocSep))) ? EXC_TOKID_LIST : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference intersection operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetIntersectTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocIntersect) ? EXC_TOKID_ISECT : EXC_TOKID_NONE;
+}
+
+/** Returns the Excel token ID of a reference range operator or EXC_TOKID_NONE. */
+sal_uInt8 lclGetRangeTokenId( OpCode eOpCode )
+{
+ return (eOpCode == ocRange) ? EXC_TOKID_RANGE : EXC_TOKID_NONE;
+}
+
+} // namespace
+
+XclExpScToken XclExpFmlaCompImpl::Expression( XclExpScToken aTokData, bool bInParentheses, bool bStopAtSep )
+{
+ if( mxData->mbOk && aTokData.Is() )
+ {
+ // remember old stop-at-ocSep mode, restored below
+ bool bOldStopAtSep = mxData->mbStopAtSep;
+ mxData->mbStopAtSep = bStopAtSep;
+ // start compilation of the subexpression
+ aTokData = OrTerm( aTokData, bInParentheses );
+ // restore old stop-at-ocSep mode
+ mxData->mbStopAtSep = bOldStopAtSep;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::SkipExpression( XclExpScToken aTokData, bool bStopAtSep )
+{
+ while( mxData->mbOk && aTokData.Is() && (aTokData.GetOpCode() != ocClose) && (!bStopAtSep || (aTokData.GetOpCode() != ocSep)) )
+ {
+ if( aTokData.GetOpCode() == ocOpen )
+ {
+ aTokData = SkipExpression( GetNextToken(), false );
+ if( mxData->mbOk ) mxData->mbOk = aTokData.GetOpCode() == ocClose;
+ }
+ aTokData = GetNextToken();
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::OrTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = AndTerm( aTokData, bInParentheses );
+ sal_uInt8 nParamCount = 1;
+ while( mxData->mbOk && (aTokData.GetOpCode() == ocOr) )
+ {
+ RemoveTrailingParen();
+ aTokData = AndTerm( GetNextToken(), bInParentheses );
+ RemoveTrailingParen();
+ ++nParamCount;
+ if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM;
+ }
+ if( mxData->mbOk && (nParamCount > 1) )
+ AppendLogicalOperatorToken( EXC_FUNCID_OR, nParamCount );
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::AndTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = CompareTerm( aTokData, bInParentheses );
+ sal_uInt8 nParamCount = 1;
+ while( mxData->mbOk && (aTokData.GetOpCode() == ocAnd) )
+ {
+ RemoveTrailingParen();
+ aTokData = CompareTerm( GetNextToken(), bInParentheses );
+ RemoveTrailingParen();
+ ++nParamCount;
+ if( mxData->mbOk ) mxData->mbOk = nParamCount <= EXC_FUNC_MAXPARAM;
+ }
+ if( mxData->mbOk && (nParamCount > 1) )
+ AppendLogicalOperatorToken( EXC_FUNCID_AND, nParamCount );
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::CompareTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = ConcatTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetConcatTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = ConcatTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::ConcatTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = AddSubTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetCompareTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = AddSubTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::AddSubTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = MulDivTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetAddSubTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = MulDivTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::MulDivTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = PowTerm( aTokData, bInParentheses );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetMulDivTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = PowTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::PowTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = UnaryPostTerm( aTokData, bInParentheses );
+ sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
+ while( mxData->mbOk )
+ {
+ nOpTokenId = lclGetPowTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = UnaryPostTerm( GetNextToken(), bInParentheses );
+ AppendBinaryOperatorToken( nOpTokenId, true, nSpaces );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::UnaryPostTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ aTokData = UnaryPreTerm( aTokData, bInParentheses );
+ sal_uInt8 nOpTokenId = EXC_TOKID_NONE;
+ while( mxData->mbOk )
+ {
+ nOpTokenId = lclGetUnaryPostTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ AppendUnaryOperatorToken( nOpTokenId, aTokData.mnSpaces );
+ GetNextToken( aTokData );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::UnaryPreTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ sal_uInt8 nOpTokenId = mxData->mbOk ? lclGetUnaryPreTokenId( aTokData.GetOpCode() ) : EXC_TOKID_NONE;
+ if( nOpTokenId != EXC_TOKID_NONE )
+ {
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = UnaryPreTerm( GetNextToken(), bInParentheses );
+ AppendUnaryOperatorToken( nOpTokenId, nSpaces );
+ }
+ else
+ {
+ aTokData = ListTerm( aTokData, bInParentheses );
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::ListTerm( XclExpScToken aTokData, bool bInParentheses )
+{
+ sal_uInt16 nSubExprPos = GetSize();
+ bool bHasAnyRefOp = false;
+ bool bHasListOp = false;
+ aTokData = IntersectTerm( aTokData, bHasAnyRefOp );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetListTokenId( aTokData.GetOpCode(), mxData->mbStopAtSep );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = IntersectTerm( GetNextToken(), bHasAnyRefOp );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ bHasAnyRefOp = bHasListOp = true;
+ }
+ if( bHasAnyRefOp )
+ {
+ // add a tMemFunc token enclosing the entire reference subexpression
+ sal_uInt16 nSubExprSize = GetSize() - nSubExprPos;
+ InsertZeros( nSubExprPos, 3 );
+ mxData->maTokVec[ nSubExprPos ] = GetTokenId( EXC_TOKID_MEMFUNC, EXC_TOKCLASS_REF );
+ Overwrite( nSubExprPos + 1, nSubExprSize );
+ // update the operand/operator stack (set the list expression as operand of the tMemFunc)
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_VAL, false );
+ PushOperatorPos( nSubExprPos, xOperands );
+ }
+ // #i86439# enclose list operator into parentheses, e.g. Calc's =AREAS(A1~A2) to Excel's =AREAS((A1;A2))
+ if( bHasListOp && !bInParentheses )
+ AppendParenToken();
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::IntersectTerm( XclExpScToken aTokData, bool& rbHasRefOp )
+{
+ aTokData = RangeTerm( aTokData, rbHasRefOp );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetIntersectTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId ==EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = RangeTerm( GetNextToken(), rbHasRefOp );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ rbHasRefOp = true;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::RangeTerm( XclExpScToken aTokData, bool& rbHasRefOp )
+{
+ aTokData = Factor( aTokData );
+ while( mxData->mbOk )
+ {
+ sal_uInt8 nOpTokenId = lclGetRangeTokenId( aTokData.GetOpCode() );
+ if (nOpTokenId == EXC_TOKID_NONE)
+ break;
+ sal_uInt8 nSpaces = aTokData.mnSpaces;
+ aTokData = Factor( GetNextToken() );
+ AppendBinaryOperatorToken( nOpTokenId, false, nSpaces );
+ rbHasRefOp = true;
+ }
+ return aTokData;
+}
+
+XclExpScToken XclExpFmlaCompImpl::Factor( XclExpScToken aTokData )
+{
+ if( !mxData->mbOk || !aTokData.Is() ) return XclExpScToken();
+
+ switch( aTokData.GetType() )
+ {
+ case svUnknown: mxData->mbOk = false; break;
+ case svDouble: ProcessDouble( aTokData ); break;
+ case svString: ProcessString( aTokData ); break;
+ case svSingleRef: ProcessCellRef( aTokData ); break;
+ case svDoubleRef: ProcessRangeRef( aTokData ); break;
+ case svExternalSingleRef: ProcessExternalCellRef( aTokData ); break;
+ case svExternalDoubleRef: ProcessExternalRangeRef( aTokData ); break;
+ case svExternalName: ProcessExternalName( aTokData ); break;
+ case svMatrix: ProcessMatrix( aTokData ); break;
+ case svExternal: ProcessExternal( aTokData ); break;
+
+ default: switch( aTokData.GetOpCode() )
+ {
+ case ocNone: /* do nothing */ break;
+ case ocMissing: ProcessMissing( aTokData ); break;
+ case ocBad: ProcessBad( aTokData ); break;
+ case ocOpen: ProcessParentheses( aTokData ); break;
+ case ocName: ProcessDefinedName( aTokData ); break;
+ case ocFalse:
+ case ocTrue: ProcessBoolean( aTokData ); break;
+ case ocDde: ProcessDdeLink( aTokData ); break;
+ default: ProcessFunction( aTokData );
+ }
+ }
+
+ return GetNextToken();
+}
+
+// formula structure ----------------------------------------------------------
+
+void XclExpFmlaCompImpl::ProcessDouble( const XclExpScToken& rTokData )
+{
+ double fValue = rTokData.mpScToken->GetDouble();
+ double fInt;
+ double fFrac = modf( fValue, &fInt );
+ if( (fFrac == 0.0) && (0.0 <= fInt) && (fInt <= 65535.0) )
+ AppendIntToken( static_cast< sal_uInt16 >( fInt ), rTokData.mnSpaces );
+ else
+ AppendNumToken( fValue, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessString( const XclExpScToken& rTokData )
+{
+ AppendOperandTokenId( EXC_TOKID_STR, rTokData.mnSpaces );
+ Append( rTokData.mpScToken->GetString().getString() );
+}
+
+void XclExpFmlaCompImpl::ProcessMissing( const XclExpScToken& rTokData )
+{
+ AppendMissingToken( rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessBad( const XclExpScToken& rTokData )
+{
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessParentheses( const XclExpScToken& rTokData )
+{
+ XclExpScToken aTokData = Expression( GetNextToken(), true, false );
+ mxData->mbOk = aTokData.GetOpCode() == ocClose;
+ AppendParenToken( rTokData.mnSpaces, aTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessBoolean( const XclExpScToken& rTokData )
+{
+ mxData->mbOk = GetNextToken().GetOpCode() == ocOpen;
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose;
+ if( mxData->mbOk )
+ AppendBoolToken( rTokData.GetOpCode() == ocTrue, rTokData.mnSpaces );
+}
+
+namespace {
+
+bool lclGetTokenString( OUString& rString, const XclExpScToken& rTokData )
+{
+ bool bIsStr = (rTokData.GetType() == svString) && (rTokData.GetOpCode() == ocPush);
+ if( bIsStr )
+ rString = rTokData.mpScToken->GetString().getString();
+ return bIsStr;
+}
+
+} // namespace
+
+void XclExpFmlaCompImpl::ProcessDdeLink( const XclExpScToken& rTokData )
+{
+ OUString aApplic, aTopic, aItem;
+
+ mxData->mbOk = GetNextToken().GetOpCode() == ocOpen;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aApplic, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aTopic, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocSep;
+ if( mxData->mbOk ) mxData->mbOk = lclGetTokenString( aItem, GetNextToken() );
+ if( mxData->mbOk ) mxData->mbOk = GetNextToken().GetOpCode() == ocClose;
+ if( mxData->mbOk ) mxData->mbOk = !aApplic.isEmpty() && !aTopic.isEmpty() && !aItem.isEmpty();
+ if( mxData->mbOk )
+ {
+ sal_uInt16 nExtSheet(0), nExtName(0);
+ if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertDde( nExtSheet, nExtName, aApplic, aTopic, aItem ) )
+ AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces );
+ else
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternal( const XclExpScToken& rTokData )
+{
+ /* #i47228# Excel import generates svExternal/ocMacro tokens for invalid
+ names and for external/invalid function calls. This function looks for
+ the next token in the token array. If it is an opening parenthesis, the
+ token is processed as external function call, otherwise as undefined name. */
+ const FormulaToken* pNextScToken = PeekNextRawToken();
+ if( !pNextScToken || (pNextScToken->GetOpCode() != ocOpen) )
+ AppendMissingNameToken( rTokData.mpScToken->GetExternal(), rTokData.mnSpaces );
+ else
+ ProcessFunction( rTokData );
+}
+
+void XclExpFmlaCompImpl::ProcessMatrix( const XclExpScToken& rTokData )
+{
+ const ScMatrix* pMatrix = rTokData.mpScToken->GetMatrix();
+ if( pMatrix && mxData->mrCfg.mbAllowArrays )
+ {
+ SCSIZE nScCols, nScRows;
+ pMatrix->GetDimensions( nScCols, nScRows );
+ OSL_ENSURE( (nScCols > 0) && (nScRows > 0), "XclExpFmlaCompImpl::ProcessMatrix - invalid matrix size" );
+ sal_uInt16 nCols = ::limit_cast< sal_uInt16 >( nScCols, 0, 256 );
+ sal_uInt16 nRows = ::limit_cast< sal_uInt16 >( nScRows, 0, 1024 );
+
+ // create the tArray token
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_ARRAY, EXC_TOKCLASS_ARR ), rTokData.mnSpaces );
+ Append( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) );
+ Append( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) );
+ Append( static_cast< sal_uInt32 >( 0 ) );
+
+ // create the extended data containing the array values
+ AppendExt( static_cast< sal_uInt8 >( (meBiff == EXC_BIFF8) ? (nCols - 1) : nCols ) );
+ AppendExt( static_cast< sal_uInt16 >( (meBiff == EXC_BIFF8) ? (nRows - 1) : nRows ) );
+ for( SCSIZE nScRow = 0; nScRow < nScRows; ++nScRow )
+ {
+ for( SCSIZE nScCol = 0; nScCol < nScCols; ++nScCol )
+ {
+ ScMatrixValue nMatVal = pMatrix->Get( nScCol, nScRow );
+ if( ScMatrix::IsValueType( nMatVal.nType ) ) // value, boolean, or error
+ {
+ FormulaError nErr;
+ if( ScMatrix::IsBooleanType( nMatVal.nType ) )
+ {
+ AppendExt( EXC_CACHEDVAL_BOOL );
+ AppendExt( static_cast< sal_uInt8 >( nMatVal.GetBoolean() ? 1 : 0 ) );
+ AppendExt( 0, 7 );
+ }
+ else if( (nErr = nMatVal.GetError()) != FormulaError::NONE )
+ {
+ AppendExt( EXC_CACHEDVAL_ERROR );
+ AppendExt( XclTools::GetXclErrorCode( nErr ) );
+ AppendExt( 0, 7 );
+ }
+ else
+ {
+ AppendExt( EXC_CACHEDVAL_DOUBLE );
+ AppendExt( nMatVal.fVal );
+ }
+ }
+ else // string or empty
+ {
+ const OUString aStr( nMatVal.GetString().getString());
+ if( aStr.isEmpty() )
+ {
+ AppendExt( EXC_CACHEDVAL_EMPTY );
+ AppendExt( 0, 8 );
+ }
+ else
+ {
+ AppendExt( EXC_CACHEDVAL_STRING );
+ AppendExt( aStr );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // array in places that do not allow it (cond fmts, data validation)
+ AppendErrorToken( EXC_ERR_NA, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessFunction( const XclExpScToken& rTokData )
+{
+ OpCode eOpCode = rTokData.GetOpCode();
+ const XclFunctionInfo* pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( eOpCode );
+
+ XclExpExtFuncData aExtFuncData;
+
+ // no exportable function found - try to create an external macro call
+ if( !pFuncInfo && (eOpCode >= SC_OPCODE_START_NO_PAR) )
+ {
+ const OUString& rFuncName = ScCompiler::GetNativeSymbol( eOpCode );
+ if( !rFuncName.isEmpty() )
+ {
+ aExtFuncData.Set( rFuncName, true, false );
+ pFuncInfo = maFuncProv.GetFuncInfoFromOpCode( ocMacro );
+ }
+ }
+
+ mxData->mbOk = pFuncInfo != nullptr;
+ if( !mxData->mbOk ) return;
+
+ // internal functions equivalent to an existing add-in
+ if( pFuncInfo->IsAddInEquivalent() )
+ aExtFuncData.Set( pFuncInfo->GetAddInEquivalentFuncName(), true, false );
+ // functions simulated by a macro call in file format
+ else if( pFuncInfo->IsMacroFunc() )
+ aExtFuncData.Set( pFuncInfo->GetMacroFuncName(), false, true );
+
+ XclExpFuncData aFuncData( rTokData, *pFuncInfo, aExtFuncData );
+ XclExpScToken aTokData;
+
+ // preparations for special functions, before function processing starts
+ PrepareFunction( aFuncData );
+
+ enum { STATE_START, STATE_OPEN, STATE_PARAM, STATE_SEP, STATE_CLOSE, STATE_END }
+ eState = STATE_START;
+ while( eState != STATE_END ) switch( eState )
+ {
+ case STATE_START:
+ mxData->mbOk = GetNextToken( aTokData ) && (aTokData.GetOpCode() == ocOpen);
+ eState = mxData->mbOk ? STATE_OPEN : STATE_END;
+ break;
+ case STATE_OPEN:
+ mxData->mbOk = GetNextToken( aTokData );
+ eState = mxData->mbOk ? ((aTokData.GetOpCode() == ocClose) ? STATE_CLOSE : STATE_PARAM) : STATE_END;
+ break;
+ case STATE_PARAM:
+ aTokData = ProcessParam( aTokData, aFuncData );
+ switch( aTokData.GetOpCode() )
+ {
+ case ocSep: eState = STATE_SEP; break;
+ case ocClose: eState = STATE_CLOSE; break;
+ default: mxData->mbOk = false;
+ }
+ if( !mxData->mbOk ) eState = STATE_END;
+ break;
+ case STATE_SEP:
+ mxData->mbOk = (aFuncData.GetParamCount() < EXC_FUNC_MAXPARAM) && GetNextToken( aTokData );
+ eState = mxData->mbOk ? STATE_PARAM : STATE_END;
+ break;
+ case STATE_CLOSE:
+ FinishFunction( aFuncData, aTokData.mnSpaces );
+ eState = STATE_END;
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::PrepareFunction( const XclExpFuncData& rFuncData )
+{
+ // For OOXML these are not rewritten anymore.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ return;
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocCosecant: // simulate CSC(x) by (1/SIN(x))
+ case ocSecant: // simulate SEC(x) by (1/COS(x))
+ case ocCot: // simulate COT(x) by (1/TAN(x))
+ case ocCosecantHyp: // simulate CSCH(x) by (1/SINH(x))
+ case ocSecantHyp: // simulate SECH(x) by (1/COSH(x))
+ case ocCotHyp: // simulate COTH(x) by (1/TANH(x))
+ AppendIntToken( 1 );
+ break;
+ case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x))
+ AppendNumToken( M_PI_2 );
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::FinishFunction( XclExpFuncData& rFuncData, sal_uInt8 nCloseSpaces )
+{
+ // append missing parameters required in Excel, may modify param count
+ AppendTrailingParam( rFuncData );
+
+ // check if parameter count fits into the limits of the function
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ if( (rFuncData.GetMinParamCount() <= nParamCount) && (nParamCount <= rFuncData.GetMaxParamCount()) )
+ {
+ // first put the tAttrSpace tokens, they must not be included in tAttrGoto handling
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces );
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, rFuncData.GetSpaces() );
+
+ // add tAttrGoto tokens for IF or CHOOSE functions
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ case ocChoose:
+ AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO );
+ break;
+ default:;
+ }
+
+ // put the tFunc or tFuncVar token (or another special token, e.g. tAttrSum)
+ AppendFuncToken( rFuncData );
+
+ // update volatile flag - is set if at least one used function is volatile
+ mxData->mbVolatile |= rFuncData.IsVolatile();
+
+ // update jump tokens for specific functions, add additional tokens
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ FinishIfFunction( rFuncData );
+ break;
+ case ocChoose:
+ FinishChooseFunction( rFuncData );
+ break;
+
+ case ocCosecant: // simulate CSC(x) by (1/SIN(x))
+ case ocSecant: // simulate SEC(x) by (1/COS(x))
+ case ocCot: // simulate COT(x) by (1/TAN(x))
+ case ocCosecantHyp: // simulate CSCH(x) by (1/SINH(x))
+ case ocSecantHyp: // simulate SECH(x) by (1/COSH(x))
+ case ocCotHyp: // simulate COTH(x) by (1/TANH(x))
+ // For OOXML not rewritten anymore.
+ if (GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ AppendBinaryOperatorToken( EXC_TOKID_DIV, true );
+ AppendParenToken();
+ }
+ break;
+ case ocArcCot: // simulate ACOT(x) by (PI/2-ATAN(x))
+ // For OOXML not rewritten anymore.
+ if (GetOutput() != EXC_OUTPUT_XML_2007)
+ {
+ AppendBinaryOperatorToken( EXC_TOKID_SUB, true );
+ AppendParenToken();
+ }
+ break;
+
+ default:;
+ }
+ }
+ else
+ mxData->mbOk = false;
+}
+
+void XclExpFmlaCompImpl::FinishIfFunction( XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nParamCount = rFuncData.GetParamCount();
+ OSL_ENSURE( (nParamCount == 2) || (nParamCount == 3), "XclExpFmlaCompImpl::FinishIfFunction - wrong parameter count" );
+ const ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec();
+ OSL_ENSURE( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishIfFunction - wrong number of tAttr tokens" );
+ // update tAttrIf token following the condition parameter
+ Overwrite( rAttrPos[ 0 ] + 2, static_cast< sal_uInt16 >( rAttrPos[ 1 ] - rAttrPos[ 0 ] ) );
+ // update the tAttrGoto tokens following true and false parameters
+ UpdateAttrGoto( rAttrPos[ 1 ] );
+ if( nParamCount == 3 )
+ UpdateAttrGoto( rAttrPos[ 2 ] );
+}
+
+void XclExpFmlaCompImpl::FinishChooseFunction( XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nParamCount = rFuncData.GetParamCount();
+ ScfUInt16Vec& rAttrPos = rFuncData.GetAttrPosVec();
+ OSL_ENSURE( nParamCount == rAttrPos.size(), "XclExpFmlaCompImpl::FinishChooseFunction - wrong number of tAttr tokens" );
+ // number of choices is parameter count minus 1
+ sal_uInt16 nChoices = nParamCount - 1;
+ // tAttrChoose token contains number of choices
+ Overwrite( rAttrPos[ 0 ] + 2, nChoices );
+ // cache position of the jump table (follows number of choices in tAttrChoose token)
+ sal_uInt16 nJumpArrPos = rAttrPos[ 0 ] + 4;
+ // size of jump table: number of choices, plus 1 for error position
+ sal_uInt16 nJumpArrSize = 2 * (nChoices + 1);
+ // insert the jump table into the tAttrChoose token
+ InsertZeros( nJumpArrPos, nJumpArrSize );
+ // update positions of tAttrGoto tokens after jump table insertion
+ sal_uInt16 nIdx;
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ rAttrPos[ nIdx ] = rAttrPos[ nIdx ] + nJumpArrSize;
+ // update the tAttrGoto tokens (they contain a value one-less to real distance)
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ UpdateAttrGoto( rAttrPos[ nIdx ] );
+ // update the distances in the jump table
+ Overwrite( nJumpArrPos, nJumpArrSize );
+ for( nIdx = 1; nIdx < nParamCount; ++nIdx )
+ Overwrite( nJumpArrPos + 2 * nIdx, static_cast< sal_uInt16 >( rAttrPos[ nIdx ] + 4 - nJumpArrPos ) );
+}
+
+XclExpScToken XclExpFmlaCompImpl::ProcessParam( XclExpScToken aTokData, XclExpFuncData& rFuncData )
+{
+ if( rFuncData.IsCalcOnlyParam() )
+ {
+ // skip Calc-only parameter, stop at next ocClose or ocSep
+ aTokData = SkipExpression( aTokData, true );
+ rFuncData.IncParamInfoIdx();
+ }
+ else
+ {
+ // insert Excel-only parameters, modifies param count and class in rFuncData
+ while( rFuncData.IsExcelOnlyParam() )
+ AppendDefaultParam( rFuncData );
+
+ // process the parameter, stop at next ocClose or ocSep
+ PrepareParam( rFuncData );
+ /* #i37355# insert tMissArg token for missing parameters --
+ Excel import filter adds ocMissing token (handled in Factor()),
+ but Calc itself does not do this if a new formula is entered. */
+ switch( aTokData.GetOpCode() )
+ {
+ case ocSep:
+ case ocClose: AppendMissingToken(); break; // empty parameter
+ default: aTokData = Expression( aTokData, false, true );
+ }
+ // finalize the parameter and add special tokens, e.g. for IF or CHOOSE parameters
+ if( mxData->mbOk ) FinishParam( rFuncData );
+ }
+ return aTokData;
+}
+
+void XclExpFmlaCompImpl::PrepareParam( XclExpFuncData& rFuncData )
+{
+ // index of this parameter is equal to number of already finished parameters
+ sal_uInt8 nParamIdx = rFuncData.GetParamCount();
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ switch( nParamIdx )
+ {
+ // add a tAttrIf token before true-parameter (second parameter)
+ case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_IF ); break;
+ // add a tAttrGoto token before false-parameter (third parameter)
+ case 2: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO ); break;
+ }
+ break;
+
+ case ocChoose:
+ switch( nParamIdx )
+ {
+ // do nothing for first parameter
+ case 0: break;
+ // add a tAttrChoose token before first value parameter (second parameter)
+ case 1: AppendJumpToken( rFuncData, EXC_TOK_ATTR_CHOOSE ); break;
+ // add a tAttrGoto token before other value parameters
+ default: AppendJumpToken( rFuncData, EXC_TOK_ATTR_GOTO );
+ }
+ break;
+
+ case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x))
+ if( nParamIdx == 0 )
+ AppendIntToken( 1 );
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::FinishParam( XclExpFuncData& rFuncData )
+{
+ // increase parameter count, update operand stack
+ rFuncData.FinishParam( PopOperandPos() );
+
+ // append more tokens for parameters of some special functions
+ sal_uInt8 nParamIdx = rFuncData.GetParamCount() - 1;
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocArcCotHyp: // simulate ACOTH(x) by ATANH(1/(x))
+ if( nParamIdx == 0 )
+ {
+ AppendParenToken();
+ AppendBinaryOperatorToken( EXC_TOKID_DIV, true );
+ }
+ break;
+ default:;
+ }
+}
+
+void XclExpFmlaCompImpl::AppendDefaultParam( XclExpFuncData& rFuncData )
+{
+ // prepare parameters of some special functions
+ PrepareParam( rFuncData );
+
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocExternal:
+ AppendAddInCallToken( rFuncData.GetExtFuncData() );
+ break;
+ case ocEuroConvert:
+ AppendEuroToolCallToken( rFuncData.GetExtFuncData() );
+ break;
+ case ocMacro:
+ // Do not write the OOXML <definedName> element.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ AppendNameToken( 0 ); // dummy to keep parameter count valid
+ else
+ AppendMacroCallToken( rFuncData.GetExtFuncData() );
+ break;
+ default:
+ {
+ if( rFuncData.IsAddInEquivalent() )
+ {
+ AppendAddInCallToken( rFuncData.GetExtFuncData() );
+ }
+ else if( rFuncData.IsMacroFunc() )
+ {
+ // Do not write the OOXML <definedName> element for new _xlfn.
+ // prefixed functions.
+ if (GetOutput() == EXC_OUTPUT_XML_2007)
+ AppendNameToken( 0 ); // dummy to keep parameter count valid
+ else
+ AppendMacroCallToken( rFuncData.GetExtFuncData() );
+ }
+ else
+ {
+ SAL_WARN( "sc.filter", "XclExpFmlaCompImpl::AppendDefaultParam - unknown opcode" );
+ AppendMissingToken(); // to keep parameter count valid
+ }
+ }
+ }
+
+ // update parameter count, add special parameter tokens
+ FinishParam( rFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendTrailingParam( XclExpFuncData& rFuncData )
+{
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ switch( rFuncData.GetOpCode() )
+ {
+ case ocIf:
+ if( nParamCount == 1 )
+ {
+ // Excel needs at least two parameters in IF function
+ PrepareParam( rFuncData );
+ AppendBoolToken( true );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocRound:
+ case ocRoundUp:
+ case ocRoundDown:
+ if( nParamCount == 1 )
+ {
+ // ROUND, ROUNDUP, ROUNDDOWN functions are fixed to 2 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 0 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocIndex:
+ if( nParamCount == 1 )
+ {
+ // INDEX function needs at least 2 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendMissingToken();
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocExternal:
+ case ocMacro:
+ // external or macro call without parameters needs the external name reference
+ if( nParamCount == 0 )
+ AppendDefaultParam( rFuncData );
+ break;
+
+ case ocGammaDist:
+ if( nParamCount == 3 )
+ {
+ // GAMMADIST function needs 4 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocPoissonDist:
+ if( nParamCount == 2 )
+ {
+ // POISSON function needs 3 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocNormDist:
+ if( nParamCount == 3 )
+ {
+ // NORMDIST function needs 4 parameters in Excel
+ PrepareParam( rFuncData );
+ AppendBoolToken( true );
+ FinishParam( rFuncData );
+ }
+ break;
+
+ case ocLogNormDist:
+ case ocLogInv:
+ switch( nParamCount )
+ {
+ // LOGNORMDIST function needs 3 parameters in Excel
+ case 1:
+ PrepareParam( rFuncData );
+ AppendIntToken( 0 );
+ FinishParam( rFuncData );
+ [[fallthrough]]; // add next default parameter
+ case 2:
+ PrepareParam( rFuncData );
+ AppendIntToken( 1 );
+ FinishParam( rFuncData );
+ break;
+ default:;
+ }
+
+ break;
+
+ default:
+ // #i108420# function without parameters stored as macro call needs the external name reference
+ if( (nParamCount == 0) && rFuncData.IsMacroFunc() )
+ AppendDefaultParam( rFuncData );
+
+ }
+}
+
+// reference handling ---------------------------------------------------------
+
+namespace {
+
+bool lclIsRefRel2D( const ScSingleRefData& rRefData )
+{
+ return rRefData.IsColRel() || rRefData.IsRowRel();
+}
+
+bool lclIsRefDel2D( const ScSingleRefData& rRefData )
+{
+ return rRefData.IsColDeleted() || rRefData.IsRowDeleted();
+}
+
+bool lclIsRefRel2D( const ScComplexRefData& rRefData )
+{
+ return lclIsRefRel2D( rRefData.Ref1 ) || lclIsRefRel2D( rRefData.Ref2 );
+}
+
+bool lclIsRefDel2D( const ScComplexRefData& rRefData )
+{
+ return lclIsRefDel2D( rRefData.Ref1 ) || lclIsRefDel2D( rRefData.Ref2 );
+}
+
+} // namespace
+
+SCTAB XclExpFmlaCompImpl::GetScTab( const ScSingleRefData& rRefData ) const
+{
+ if (rRefData.IsTabDeleted())
+ return SCTAB_INVALID;
+
+ if (!rRefData.IsTabRel())
+ // absolute address
+ return rRefData.Tab();
+
+ if (!mxData->mpScBasePos)
+ return SCTAB_INVALID;
+
+ return rRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos).Tab();
+}
+
+bool XclExpFmlaCompImpl::IsRef2D( const ScSingleRefData& rRefData, bool bCheck3DFlag ) const
+{
+ /* rRefData.IsFlag3D() determines if sheet name is always visible, even on
+ the own sheet. If 3D references are allowed, the passed reference does
+ not count as 2D reference. */
+
+ // conditional formatting does not allow 3D refs in xls
+ if (mxData && mxData->mrCfg.meType == EXC_FMLATYPE_CONDFMT)
+ return true;
+
+ if (bCheck3DFlag && rRefData.IsFlag3D())
+ return false;
+
+ if (rRefData.IsTabDeleted())
+ return false;
+
+ if (rRefData.IsTabRel())
+ return rRefData.Tab() == 0;
+ else
+ return rRefData.Tab() == GetCurrScTab();
+}
+
+bool XclExpFmlaCompImpl::IsRef2D( const ScComplexRefData& rRefData, bool bCheck3DFlag ) const
+{
+ return IsRef2D(rRefData.Ref1, bCheck3DFlag) && IsRef2D(rRefData.Ref2, bCheck3DFlag);
+}
+
+void XclExpFmlaCompImpl::ConvertRefData(
+ ScSingleRefData& rRefData, XclAddress& rXclPos,
+ bool bNatLangRef, bool bTruncMaxCol, bool bTruncMaxRow ) const
+{
+ if( mxData->mpScBasePos )
+ {
+ // *** reference position exists (cell, matrix) - convert to absolute ***
+ ScAddress aAbs = rRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos);
+
+ // convert column index
+ if (bTruncMaxCol && (aAbs.Col() == mnMaxScCol))
+ aAbs.SetCol(mnMaxAbsCol);
+ else if ((aAbs.Col() < 0) || (aAbs.Col() > mnMaxAbsCol))
+ rRefData.SetColDeleted(true);
+ rXclPos.mnCol = static_cast<sal_uInt16>(aAbs.Col()) & mnMaxColMask;
+
+ // convert row index
+ if (bTruncMaxRow && (aAbs.Row() == mnMaxScRow))
+ aAbs.SetRow(mnMaxAbsRow);
+ else if ((aAbs.Row() < 0) || (aAbs.Row() > mnMaxAbsRow))
+ rRefData.SetRowDeleted(true);
+ rXclPos.mnRow = static_cast<sal_uInt32>(aAbs.Row()) & mnMaxRowMask;
+
+ // Update the reference.
+ rRefData.SetAddress(GetRoot().GetDoc().GetSheetLimits(), aAbs, *mxData->mpScBasePos);
+ }
+ else
+ {
+ // *** no reference position (shared, names, condfmt) - use relative values ***
+
+ // convert column index (2-step-cast ScsCOL->sal_Int16->sal_uInt16 to get all bits correctly)
+ sal_Int16 nXclRelCol = static_cast<sal_Int16>(rRefData.Col());
+ rXclPos.mnCol = static_cast< sal_uInt16 >( nXclRelCol ) & mnMaxColMask;
+
+ // convert row index (2-step-cast ScsROW->sal_Int32->sal_uInt32 to get all bits correctly)
+ sal_Int32 nXclRelRow = static_cast<sal_Int32>(rRefData.Row());
+ rXclPos.mnRow = static_cast< sal_uInt32 >( nXclRelRow ) & mnMaxRowMask;
+ }
+
+ // flags for relative column and row
+ if( bNatLangRef )
+ {
+ OSL_ENSURE( meBiff == EXC_BIFF8, "XclExpFmlaCompImpl::ConvertRefData - NLRs only for BIFF8" );
+ // Calc does not support absolute reference mode in natural language references
+ ::set_flag( rXclPos.mnCol, EXC_TOK_NLR_REL );
+ }
+ else
+ {
+ sal_uInt16 rnRelRow = rXclPos.mnRow;
+ sal_uInt16& rnRelField = (meBiff <= EXC_BIFF5) ? rnRelRow : rXclPos.mnCol;
+ ::set_flag( rnRelField, EXC_TOK_REF_COLREL, rRefData.IsColRel() );
+ ::set_flag( rnRelField, EXC_TOK_REF_ROWREL, rRefData.IsRowRel() );
+ }
+}
+
+void XclExpFmlaCompImpl::ConvertRefData(
+ ScComplexRefData& rRefData, XclRange& rXclRange, bool bNatLangRef ) const
+{
+ // convert start and end of the range
+ ConvertRefData( rRefData.Ref1, rXclRange.maFirst, bNatLangRef, false, false );
+ bool bTruncMaxCol = !rRefData.Ref1.IsColDeleted() && (rXclRange.maFirst.mnCol == 0);
+ bool bTruncMaxRow = !rRefData.Ref1.IsRowDeleted() && (rXclRange.maFirst.mnRow == 0);
+ ConvertRefData( rRefData.Ref2, rXclRange.maLast, bNatLangRef, bTruncMaxCol, bTruncMaxRow );
+}
+
+XclExpRefLogEntry* XclExpFmlaCompImpl::GetNewRefLogEntry()
+{
+ if( mxData->mpRefLog )
+ {
+ mxData->mpRefLog->emplace_back();
+ return &mxData->mpRefLog->back();
+ }
+ return nullptr;
+}
+
+void XclExpFmlaCompImpl::ProcessCellRef( const XclExpScToken& rTokData )
+{
+ // get the Excel address components, adjust internal data in aRefData
+ bool bNatLangRef = (meBiff == EXC_BIFF8) && mxData->mpScBasePos && (rTokData.GetOpCode() == ocColRowName);
+ ScSingleRefData aRefData = *rTokData.mpScToken->GetSingleRef();
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclPos, bNatLangRef, false, false );
+
+ if( bNatLangRef )
+ {
+ OSL_ENSURE( aRefData.IsColRel() != aRefData.IsRowRel(),
+ "XclExpFmlaCompImpl::ProcessCellRef - broken natural language reference" );
+ // create tNlr token for natural language reference
+ sal_uInt8 nSubId = aRefData.IsColRel() ? EXC_TOK_NLR_COLV : EXC_TOK_NLR_ROWV;
+ AppendOperandTokenId( EXC_TOKID_NLR, rTokData.mnSpaces );
+ Append( nSubId );
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ // store external cell contents in CRN records
+ if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCell(aRefData, *mxData->mpScBasePos);
+
+ // create the tRef, tRefErr, tRefN, tRef3d, or tRefErr3d token
+ if (!mxData->mrCfg.mb3DRefOnly && IsRef2D(aRefData, mxData->mpLinkMgr != nullptr))
+ {
+ // 2D reference (not in defined names, but allowed in range lists)
+ sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_REFN :
+ (lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR : EXC_TOKID_REF);
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ AppendAddress( aXclPos );
+ }
+ else if( mxData->mpLinkMgr ) // 3D reference
+ {
+ // 1-based EXTERNSHEET index and 0-based Excel sheet index
+ sal_uInt16 nExtSheet, nXclTab;
+ mxData->mpLinkMgr->FindExtSheet( nExtSheet, nXclTab, GetScTab( aRefData ), GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nXclTab );
+ Append( nXclTab );
+ }
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ // 3D ref in cond. format, or 2D ref in name
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessRangeRef( const XclExpScToken& rTokData )
+{
+ // get the Excel address components, adjust internal data in aRefData
+ ScComplexRefData aRefData = *rTokData.mpScToken->GetDoubleRef();
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclRange, false );
+
+ // store external cell contents in CRN records
+ if( mxData->mrCfg.mbFromCell && mxData->mpLinkMgr && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCellRange(aRefData, *mxData->mpScBasePos);
+
+ // create the tArea, tAreaErr, tAreaN, tArea3d, or tAreaErr3d token
+ if (!mxData->mrCfg.mb3DRefOnly && IsRef2D(aRefData, mxData->mpLinkMgr != nullptr))
+ {
+ // 2D reference (not in name formulas, but allowed in range lists)
+ sal_uInt8 nBaseId = (!mxData->mpScBasePos && lclIsRefRel2D( aRefData )) ? EXC_TOKID_AREAN :
+ (lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR : EXC_TOKID_AREA);
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ AppendRange( aXclRange );
+ }
+ else if( mxData->mpLinkMgr ) // 3D reference
+ {
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstXclTab, nLastXclTab;
+ mxData->mpLinkMgr->FindExtSheet( nExtSheet, nFirstXclTab, nLastXclTab,
+ GetScTab( aRefData.Ref1 ), GetScTab( aRefData.Ref2 ), GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstXclTab );
+ Append( nLastXclTab );
+ }
+ AppendRange( aXclRange );
+ }
+ else
+ {
+ // 3D ref in cond. format, or 2D ref in name
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternalCellRef( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ // get the Excel address components, adjust internal data in aRefData
+ ScSingleRefData aRefData = *rTokData.mpScToken->GetSingleRef();
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclPos, false, false, false );
+
+ // store external cell contents in CRN records
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aTabName = rTokData.mpScToken->GetString().getString();
+ if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCell(nFileId, aTabName, aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab;
+ mxData->mpLinkMgr->FindExtSheet( nFileId, aTabName, 1, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry() );
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_REFERR3D : EXC_TOKID_REF3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstSBTab );
+ Append( nLastSBTab );
+ }
+ AppendAddress( aXclPos );
+ }
+ else
+ {
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessExternalRangeRef( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ // get the Excel address components, adjust internal data in aRefData
+ ScComplexRefData aRefData = *rTokData.mpScToken->GetDoubleRef();
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ ConvertRefData( aRefData, aXclRange, false );
+
+ // store external cell contents in CRN records
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aTabName = rTokData.mpScToken->GetString().getString();
+ if( mxData->mrCfg.mbFromCell && mxData->mpScBasePos )
+ mxData->mpLinkMgr->StoreCellRange(nFileId, aTabName, aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+
+ // 1-based EXTERNSHEET index and 0-based Excel sheet indexes
+ sal_uInt16 nExtSheet, nFirstSBTab, nLastSBTab;
+ sal_uInt16 nTabSpan = static_cast<sal_uInt16>(aRefData.Ref2.Tab() - aRefData.Ref1.Tab() + 1);
+ mxData->mpLinkMgr->FindExtSheet(
+ nFileId, aTabName, nTabSpan, nExtSheet, nFirstSBTab, nLastSBTab, GetNewRefLogEntry());
+ // write the token
+ sal_uInt8 nBaseId = lclIsRefDel2D( aRefData ) ? EXC_TOKID_AREAERR3D : EXC_TOKID_AREA3D;
+ AppendOperandTokenId( GetTokenId( nBaseId, EXC_TOKCLASS_REF ), rTokData.mnSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( 0, 8 );
+ Append( nFirstSBTab );
+ Append( nLastSBTab );
+ }
+ AppendRange( aXclRange );
+ }
+ else
+ {
+ AppendErrorToken( EXC_ERR_REF, rTokData.mnSpaces );
+ }
+}
+
+void XclExpFmlaCompImpl::ProcessDefinedName( const XclExpScToken& rTokData )
+{
+ sal_Int16 nSheet = rTokData.mpScToken->GetSheet();
+ SCTAB nTab = (nSheet < 0 ? SCTAB_GLOBAL : nSheet);
+
+ XclExpNameManager& rNameMgr = GetNameManager();
+ sal_uInt16 nNameIdx = rNameMgr.InsertName(nTab, rTokData.mpScToken->GetIndex(), GetCurrScTab());
+ if( nNameIdx != 0 )
+ {
+ // global names always with tName token, local names dependent on config
+ SCTAB nScTab = rNameMgr.GetScTab( nNameIdx );
+ if( (nScTab == SCTAB_GLOBAL) || (!mxData->mrCfg.mb3DRefOnly && (nScTab == GetCurrScTab())) )
+ {
+ AppendNameToken( nNameIdx, rTokData.mnSpaces );
+ }
+ else if( mxData->mpLinkMgr )
+ {
+ // use the same special EXTERNNAME to refer to any local name
+ sal_uInt16 nExtSheet = mxData->mpLinkMgr->FindExtSheet( EXC_EXTSH_OWNDOC );
+ AppendNameXToken( nExtSheet, nNameIdx, rTokData.mnSpaces );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+ // volatile names (containing volatile functions)
+ mxData->mbVolatile |= rNameMgr.IsVolatile( nNameIdx );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+}
+
+void XclExpFmlaCompImpl::ProcessExternalName( const XclExpScToken& rTokData )
+{
+ if( mxData->mpLinkMgr )
+ {
+ ScExternalRefManager& rExtRefMgr = *GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = rTokData.mpScToken->GetIndex();
+ OUString aName = rTokData.mpScToken->GetString().getString();
+ ScExternalRefCache::TokenArrayRef xArray = rExtRefMgr.getRangeNameTokens( nFileId, aName );
+ if( xArray )
+ {
+ // store external cell contents in CRN records
+ if( mxData->mpScBasePos )
+ {
+ FormulaTokenArrayPlainIterator aIter(*xArray);
+ for( FormulaToken* pScToken = aIter.First(); pScToken; pScToken = aIter.Next() )
+ {
+ if( pScToken->IsExternalRef() )
+ {
+ switch( pScToken->GetType() )
+ {
+ case svExternalSingleRef:
+ {
+ ScSingleRefData aRefData = *pScToken->GetSingleRef();
+ mxData->mpLinkMgr->StoreCell(
+ nFileId, pScToken->GetString().getString(), aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+ }
+ break;
+ case svExternalDoubleRef:
+ {
+ ScComplexRefData aRefData = *pScToken->GetDoubleRef();
+ mxData->mpLinkMgr->StoreCellRange(
+ nFileId, pScToken->GetString().getString(), aRefData.toAbs(GetRoot().GetDoc(), *mxData->mpScBasePos));
+ }
+ break;
+ default:
+ ; // nothing, avoid compiler warning
+ }
+ }
+ }
+ }
+
+ // insert the new external name and create the tNameX token
+ sal_uInt16 nExtSheet = 0, nExtName = 0;
+ const OUString* pFile = rExtRefMgr.getExternalFileName( nFileId );
+ if( pFile && mxData->mpLinkMgr->InsertExtName( nExtSheet, nExtName, *pFile, aName, xArray ) )
+ {
+ AppendNameXToken( nExtSheet, nExtName, rTokData.mnSpaces );
+ return;
+ }
+ }
+ }
+
+ // on any error: create a #NAME? error
+ AppendErrorToken( EXC_ERR_NAME, rTokData.mnSpaces );
+}
+
+// token vector ---------------------------------------------------------------
+
+void XclExpFmlaCompImpl::PushOperandPos( sal_uInt16 nTokPos )
+{
+ mxData->maOpPosStack.push_back( nTokPos );
+}
+
+void XclExpFmlaCompImpl::PushOperatorPos( sal_uInt16 nTokPos, const XclExpOperandListRef& rxOperands )
+{
+ PushOperandPos( nTokPos );
+ OSL_ENSURE( rxOperands, "XclExpFmlaCompImpl::AppendOperatorTokenId - missing operand list" );
+ if( mxData->maOpListVec.size() <= nTokPos )
+ mxData->maOpListVec.resize( nTokPos + 1, XclExpOperandListRef() );
+ mxData->maOpListVec[ nTokPos ] = rxOperands;
+}
+
+sal_uInt16 XclExpFmlaCompImpl::PopOperandPos()
+{
+ OSL_ENSURE( !mxData->mbOk || !mxData->maOpPosStack.empty(), "XclExpFmlaCompImpl::PopOperandPos - token stack broken" );
+ mxData->mbOk &= !mxData->maOpPosStack.empty();
+ if( mxData->mbOk )
+ {
+ sal_uInt16 nTokPos = mxData->maOpPosStack.back();
+ mxData->maOpPosStack.pop_back();
+ return nTokPos;
+ }
+ return 0;
+}
+
+namespace {
+
+void lclAppend( ScfUInt8Vec& orVector, sal_uInt16 nData )
+{
+ orVector.resize( orVector.size() + 2 );
+ ShortToSVBT16( nData, &*(orVector.end() - 2) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, sal_uInt32 nData )
+{
+ orVector.resize( orVector.size() + 4 );
+ UInt32ToSVBT32( nData, &*(orVector.end() - 4) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, double fData )
+{
+ orVector.resize( orVector.size() + 8 );
+ DoubleToSVBT64( fData, &*(orVector.end() - 8) );
+}
+
+void lclAppend( ScfUInt8Vec& orVector, const XclExpRoot& rRoot, const OUString& rString, XclStrFlags nStrFlags )
+{
+ XclExpStringRef xXclStr = XclExpStringHelper::CreateString( rRoot, rString, nStrFlags, EXC_TOK_STR_MAXLEN );
+ size_t nSize = orVector.size();
+ orVector.resize( nSize + xXclStr->GetSize() );
+ xXclStr->WriteToMem( &orVector[ nSize ] );
+}
+
+} // namespace
+
+void XclExpFmlaCompImpl::Append( sal_uInt8 nData )
+{
+ mxData->maTokVec.push_back( nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt8 nData, size_t nCount )
+{
+ mxData->maTokVec.resize( mxData->maTokVec.size() + nCount, nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt16 nData )
+{
+ lclAppend( mxData->maTokVec, nData );
+}
+
+void XclExpFmlaCompImpl::Append( sal_uInt32 nData )
+{
+ lclAppend( mxData->maTokVec, nData );
+}
+
+void XclExpFmlaCompImpl::Append( double fData )
+{
+ lclAppend( mxData->maTokVec, fData );
+}
+
+void XclExpFmlaCompImpl::Append( const OUString& rString )
+{
+ lclAppend( mxData->maTokVec, GetRoot(), rString, XclStrFlags::EightBitLength );
+}
+
+void XclExpFmlaCompImpl::AppendAddress( const XclAddress& rXclPos )
+{
+ Append( static_cast<sal_uInt16>(rXclPos.mnRow) );
+ if( meBiff <= EXC_BIFF5 )
+ Append( static_cast< sal_uInt8 >( rXclPos.mnCol ) );
+ else
+ Append( rXclPos.mnCol );
+}
+
+void XclExpFmlaCompImpl::AppendRange( const XclRange& rXclRange )
+{
+ Append( static_cast<sal_uInt16>(rXclRange.maFirst.mnRow) );
+ Append( static_cast<sal_uInt16>(rXclRange.maLast.mnRow) );
+ if( meBiff <= EXC_BIFF5 )
+ {
+ Append( static_cast< sal_uInt8 >( rXclRange.maFirst.mnCol ) );
+ Append( static_cast< sal_uInt8 >( rXclRange.maLast.mnCol ) );
+ }
+ else
+ {
+ Append( rXclRange.maFirst.mnCol );
+ Append( rXclRange.maLast.mnCol );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendSpaceToken( sal_uInt8 nType, sal_uInt8 nCount )
+{
+ if( nCount > 0 )
+ {
+ Append( EXC_TOKID_ATTR );
+ Append( EXC_TOK_ATTR_SPACE );
+ Append( nType );
+ Append( nCount );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendOperandTokenId( sal_uInt8 nTokenId, sal_uInt8 nSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces );
+ PushOperandPos( GetSize() );
+ Append( nTokenId );
+}
+
+void XclExpFmlaCompImpl::AppendIntToken( sal_uInt16 nValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_INT, nSpaces );
+ Append( nValue );
+}
+
+void XclExpFmlaCompImpl::AppendNumToken( double fValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_NUM, nSpaces );
+ Append( fValue );
+}
+
+void XclExpFmlaCompImpl::AppendBoolToken( bool bValue, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_BOOL, nSpaces );
+ Append( bValue ? EXC_TOK_BOOL_TRUE : EXC_TOK_BOOL_FALSE );
+}
+
+void XclExpFmlaCompImpl::AppendErrorToken( sal_uInt8 nErrCode, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_ERR, nSpaces );
+ Append( nErrCode );
+}
+
+void XclExpFmlaCompImpl::AppendMissingToken( sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( EXC_TOKID_MISSARG, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendNameToken( sal_uInt16 nNameIdx, sal_uInt8 nSpaces )
+{
+ if( nNameIdx > 0 )
+ {
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_NAME, EXC_TOKCLASS_REF ), nSpaces );
+ Append( nNameIdx );
+ Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 );
+ }
+ else
+ AppendErrorToken( EXC_ERR_NAME );
+}
+
+void XclExpFmlaCompImpl::AppendMissingNameToken( const OUString& rName, sal_uInt8 nSpaces )
+{
+ sal_uInt16 nNameIdx = GetNameManager().InsertRawName( rName );
+ AppendNameToken( nNameIdx, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendNameXToken( sal_uInt16 nExtSheet, sal_uInt16 nExtName, sal_uInt8 nSpaces )
+{
+ AppendOperandTokenId( GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ), nSpaces );
+ Append( nExtSheet );
+ if( meBiff <= EXC_BIFF5 )
+ Append( 0, 8 );
+ Append( nExtName );
+ Append( 0, (meBiff <= EXC_BIFF5) ? 12 : 2 );
+}
+
+void XclExpFmlaCompImpl::AppendMacroCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ sal_uInt16 nNameIdx = GetNameManager().InsertMacroCall( rExtFuncData.maFuncName, rExtFuncData.mbVBasic, true, rExtFuncData.mbHidden );
+ AppendNameToken( nNameIdx );
+}
+
+void XclExpFmlaCompImpl::AppendAddInCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ OUString aXclFuncName;
+ if( mxData->mpLinkMgr && ScGlobal::GetAddInCollection()->GetExcelName( rExtFuncData.maFuncName, GetUILanguage(), aXclFuncName ) )
+ {
+ sal_uInt16 nExtSheet, nExtName;
+ if( mxData->mpLinkMgr->InsertAddIn( nExtSheet, nExtName, aXclFuncName ) )
+ {
+ AppendNameXToken( nExtSheet, nExtName );
+ return;
+ }
+ }
+ AppendMacroCallToken( rExtFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendEuroToolCallToken( const XclExpExtFuncData& rExtFuncData )
+{
+ sal_uInt16 nExtSheet(0), nExtName(0);
+ if( mxData->mpLinkMgr && mxData->mpLinkMgr->InsertEuroTool( nExtSheet, nExtName, rExtFuncData.maFuncName ) )
+ AppendNameXToken( nExtSheet, nExtName );
+ else
+ AppendMacroCallToken( rExtFuncData );
+}
+
+void XclExpFmlaCompImpl::AppendOperatorTokenId( sal_uInt8 nTokenId, const XclExpOperandListRef& rxOperands, sal_uInt8 nSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP, nSpaces );
+ PushOperatorPos( GetSize(), rxOperands );
+ Append( nTokenId );
+}
+
+void XclExpFmlaCompImpl::AppendUnaryOperatorToken( sal_uInt8 nTokenId, sal_uInt8 nSpaces )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, true );
+ AppendOperatorTokenId( nTokenId, xOperands, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendBinaryOperatorToken( sal_uInt8 nTokenId, bool bValType, sal_uInt8 nSpaces )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType );
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPO, bValType );
+ AppendOperatorTokenId( nTokenId, xOperands, nSpaces );
+}
+
+void XclExpFmlaCompImpl::AppendLogicalOperatorToken( sal_uInt16 nXclFuncIdx, sal_uInt8 nOpCount )
+{
+ XclExpOperandListRef xOperands = std::make_shared<XclExpOperandList>();
+ for( sal_uInt8 nOpIdx = 0; nOpIdx < nOpCount; ++nOpIdx )
+ xOperands->AppendOperand( PopOperandPos(), EXC_PARAMCONV_RPX, false );
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, EXC_TOKCLASS_VAL ), xOperands );
+ Append( nOpCount );
+ Append( nXclFuncIdx );
+}
+
+void XclExpFmlaCompImpl::AppendFuncToken( const XclExpFuncData& rFuncData )
+{
+ sal_uInt16 nXclFuncIdx = rFuncData.GetXclFuncIdx();
+ sal_uInt8 nParamCount = rFuncData.GetParamCount();
+ sal_uInt8 nRetClass = rFuncData.GetReturnClass();
+
+ if( (nXclFuncIdx == EXC_FUNCID_SUM) && (nParamCount == 1) )
+ {
+ // SUM with only one parameter
+ AppendOperatorTokenId( EXC_TOKID_ATTR, rFuncData.GetOperandList() );
+ Append( EXC_TOK_ATTR_SUM );
+ Append( sal_uInt16( 0 ) );
+ }
+ else if( rFuncData.IsFixedParamCount() )
+ {
+ // fixed number of parameters
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNC, nRetClass ), rFuncData.GetOperandList() );
+ Append( nXclFuncIdx );
+ }
+ else
+ {
+ // variable number of parameters
+ AppendOperatorTokenId( GetTokenId( EXC_TOKID_FUNCVAR, nRetClass ), rFuncData.GetOperandList() );
+ Append( nParamCount );
+ Append( nXclFuncIdx );
+ }
+}
+
+void XclExpFmlaCompImpl::AppendParenToken( sal_uInt8 nOpenSpaces, sal_uInt8 nCloseSpaces )
+{
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_OPEN, nOpenSpaces );
+ AppendSpaceToken( EXC_TOK_ATTR_SPACE_SP_CLOSE, nCloseSpaces );
+ Append( EXC_TOKID_PAREN );
+}
+
+void XclExpFmlaCompImpl::AppendJumpToken( XclExpFuncData& rFuncData, sal_uInt8 nAttrType )
+{
+ // store the start position of the token
+ rFuncData.AppendAttrPos( GetSize() );
+ // create the tAttr token
+ Append( EXC_TOKID_ATTR );
+ Append( nAttrType );
+ Append( sal_uInt16( 0 ) ); // placeholder that will be updated later
+}
+
+void XclExpFmlaCompImpl::InsertZeros( sal_uInt16 nInsertPos, sal_uInt16 nInsertSize )
+{
+ // insert zeros into the token array
+ OSL_ENSURE( nInsertPos < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Insert - invalid position" );
+ mxData->maTokVec.insert( mxData->maTokVec.begin() + nInsertPos, nInsertSize, 0 );
+
+ // update positions of operands waiting for an operator
+ for( auto& rOpPos : mxData->maOpPosStack )
+ if( nInsertPos <= rOpPos )
+ rOpPos += nInsertSize;
+
+ // update operand lists of all operator tokens
+ if( nInsertPos < mxData->maOpListVec.size() )
+ mxData->maOpListVec.insert( mxData->maOpListVec.begin() + nInsertPos, nInsertSize, XclExpOperandListRef() );
+ for( auto& rxOpList : mxData->maOpListVec )
+ if( rxOpList )
+ for( auto& rOp : *rxOpList )
+ if( nInsertPos <= rOp.mnTokPos )
+ rOp.mnTokPos += nInsertSize;
+}
+
+void XclExpFmlaCompImpl::Overwrite( sal_uInt16 nWriteToPos, sal_uInt16 nOffset )
+{
+ OSL_ENSURE( o3tl::make_unsigned( nWriteToPos + 1 ) < mxData->maTokVec.size(), "XclExpFmlaCompImpl::Overwrite - invalid position" );
+ ShortToSVBT16( nOffset, &mxData->maTokVec[ nWriteToPos ] );
+}
+
+void XclExpFmlaCompImpl::UpdateAttrGoto( sal_uInt16 nAttrPos )
+{
+ /* tAttrGoto contains distance from end of tAttr token to position behind
+ the function token (for IF or CHOOSE function), which is currently at
+ the end of the token array. Additionally this distance is decreased by
+ one, for whatever reason. So we have to subtract 4 and 1 from the
+ distance between the tAttr token start and the end of the token array. */
+ Overwrite( nAttrPos + 2, static_cast< sal_uInt16 >( GetSize() - nAttrPos - 5 ) );
+}
+
+bool XclExpFmlaCompImpl::IsSpaceToken( sal_uInt16 nPos ) const
+{
+ return
+ (o3tl::make_unsigned( nPos + 4 ) <= mxData->maTokVec.size()) &&
+ (mxData->maTokVec[ nPos ] == EXC_TOKID_ATTR) &&
+ (mxData->maTokVec[ nPos + 1 ] == EXC_TOK_ATTR_SPACE);
+}
+
+void XclExpFmlaCompImpl::RemoveTrailingParen()
+{
+ // remove trailing tParen token
+ if( !mxData->maTokVec.empty() && (mxData->maTokVec.back() == EXC_TOKID_PAREN) )
+ mxData->maTokVec.pop_back();
+ // remove remaining tAttrSpace tokens
+ while( (mxData->maTokVec.size() >= 4) && IsSpaceToken( GetSize() - 4 ) )
+ mxData->maTokVec.erase( mxData->maTokVec.end() - 4, mxData->maTokVec.end() );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData )
+{
+ mxData->maExtDataVec.push_back( nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt8 nData, size_t nCount )
+{
+ mxData->maExtDataVec.resize( mxData->maExtDataVec.size() + nCount, nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( sal_uInt16 nData )
+{
+ lclAppend( mxData->maExtDataVec, nData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( double fData )
+{
+ lclAppend( mxData->maExtDataVec, fData );
+}
+
+void XclExpFmlaCompImpl::AppendExt( const OUString& rString )
+{
+ lclAppend( mxData->maExtDataVec, GetRoot(), rString, (meBiff == EXC_BIFF8) ? XclStrFlags::NONE : XclStrFlags::EightBitLength );
+}
+
+namespace {
+
+void lclInitOwnTab( ScSingleRefData& rRef, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ if( b3DRefOnly )
+ {
+ // no reduction to 2D reference, if global link manager is used
+ rRef.SetFlag3D(true);
+ }
+ else if( rScPos.Tab() == nCurrScTab )
+ {
+ rRef.SetRelTab(0);
+ }
+}
+
+void lclPutCellToTokenArray( ScTokenArray& rScTokArr, const ScAddress& rScPos, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ ScSingleRefData aRef;
+ aRef.InitAddress( rScPos );
+ lclInitOwnTab( aRef, rScPos, nCurrScTab, b3DRefOnly );
+ rScTokArr.AddSingleReference( aRef );
+}
+
+void lclPutRangeToTokenArray( ScTokenArray& rScTokArr, const ScRange& rScRange, SCTAB nCurrScTab, bool b3DRefOnly )
+{
+ if( rScRange.aStart == rScRange.aEnd )
+ {
+ lclPutCellToTokenArray( rScTokArr, rScRange.aStart, nCurrScTab, b3DRefOnly );
+ }
+ else
+ {
+ ScComplexRefData aRef;
+ aRef.InitRange( rScRange );
+ lclInitOwnTab( aRef.Ref1, rScRange.aStart, nCurrScTab, b3DRefOnly );
+ lclInitOwnTab( aRef.Ref2, rScRange.aEnd, nCurrScTab, b3DRefOnly );
+ rScTokArr.AddDoubleReference( aRef );
+ }
+}
+
+} // namespace
+
+XclExpFormulaCompiler::XclExpFormulaCompiler( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxImpl( std::make_shared<XclExpFmlaCompImpl>( rRoot ) )
+{
+}
+
+XclExpFormulaCompiler::~XclExpFormulaCompiler()
+{
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula(
+ XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos, XclExpRefLog* pRefLog )
+{
+ return mxImpl->CreateFormula( eType, rScTokArr, pScBasePos, pRefLog );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScAddress& rScPos )
+{
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ lclPutCellToTokenArray( aScTokArr, rScPos, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) );
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRange& rScRange )
+{
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ lclPutRangeToTokenArray( aScTokArr, rScRange, GetCurrScTab(), mxImpl->Is3DRefOnly( eType ) );
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateFormula( XclFormulaType eType, const ScRangeList& rScRanges )
+{
+ size_t nCount = rScRanges.size();
+ if( nCount == 0 )
+ return XclTokenArrayRef();
+
+ ScTokenArray aScTokArr(GetRoot().GetDoc());
+ SCTAB nCurrScTab = GetCurrScTab();
+ bool b3DRefOnly = mxImpl->Is3DRefOnly( eType );
+ for( size_t nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ if( nIdx > 0 )
+ aScTokArr.AddOpCode( ocUnion );
+ lclPutRangeToTokenArray( aScTokArr, rScRanges[ nIdx ], nCurrScTab, b3DRefOnly );
+ }
+ return mxImpl->CreateFormula( eType, aScTokArr );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateErrorFormula( sal_uInt8 nErrCode )
+{
+ return mxImpl->CreateErrorFormula( nErrCode );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateSpecialRefFormula(
+ sal_uInt8 nTokenId, const XclAddress& rXclPos )
+{
+ return mxImpl->CreateSpecialRefFormula( nTokenId, rXclPos );
+}
+
+XclTokenArrayRef XclExpFormulaCompiler::CreateNameXFormula(
+ sal_uInt16 nExtSheet, sal_uInt16 nExtName )
+{
+ return mxImpl->CreateNameXFormula( nExtSheet, nExtName );
+}
+
+bool XclExpFormulaCompiler::IsRef2D( const ScSingleRefData& rRefData ) const
+{
+ return mxImpl->IsRef2D(rRefData, true);
+}
+
+bool XclExpFormulaCompiler::IsRef2D( const ScComplexRefData& rRefData ) const
+{
+ return mxImpl->IsRef2D(rRefData, true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xehelper.cxx b/sc/source/filter/excel/xehelper.cxx
new file mode 100644
index 000000000..038ec8f71
--- /dev/null
+++ b/sc/source/filter/excel/xehelper.cxx
@@ -0,0 +1,1071 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <sfx2/objsh.hxx>
+#include <vcl/font.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/itemset.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/outlobj.hxx>
+#include <scitems.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/editids.hrc>
+
+#include <document.hxx>
+#include <docpool.hxx>
+#include <editutil.hxx>
+#include <patattr.hxx>
+#include <scmatrix.hxx>
+#include <xestyle.hxx>
+#include <fprogressbar.hxx>
+#include <globstr.hrc>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xecontent.hxx>
+#include <xelink.hxx>
+#include <xehelper.hxx>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::i18n::XBreakIterator;
+
+// Export progress bar ========================================================
+
+XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
+ mpSubProgress( nullptr ),
+ mpSubRowCreate( nullptr ),
+ mpSubRowFinal( nullptr ),
+ mnSegRowFinal( SCF_INV_SEGMENT ),
+ mnRowCount( 0 )
+{
+}
+
+XclExpProgressBar::~XclExpProgressBar()
+{
+}
+
+void XclExpProgressBar::Initialize()
+{
+ const ScDocument& rDoc = GetDoc();
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ SCTAB nScTabCount = rTabInfo.GetScTabCount();
+
+ // *** segment: creation of ROW records *** -------------------------------
+
+ sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
+ mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
+ maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
+
+ for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
+ {
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ SCCOL nLastUsedScCol;
+ SCROW nLastUsedScRow;
+ rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
+ std::size_t nSegSize = static_cast< std::size_t >( nLastUsedScRow + 1 );
+ maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
+ }
+ }
+
+ // *** segment: writing all ROW records *** -------------------------------
+
+ mnSegRowFinal = mxProgress->AddSegment( 1000 );
+ // sub progress bar and segment are created later in ActivateFinalRowsSegment()
+}
+
+void XclExpProgressBar::IncRowRecordCount()
+{
+ ++mnRowCount;
+}
+
+void XclExpProgressBar::ActivateCreateRowsSegment()
+{
+ OSL_ENSURE( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
+ "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
+ sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
+ OSL_ENSURE( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
+ if( nSeg != SCF_INV_SEGMENT )
+ {
+ mpSubProgress = mpSubRowCreate;
+ mpSubProgress->ActivateSegment( nSeg );
+ }
+ else
+ mpSubProgress = nullptr;
+}
+
+void XclExpProgressBar::ActivateFinalRowsSegment()
+{
+ if( !mpSubRowFinal && (mnRowCount > 0) )
+ {
+ mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
+ mpSubRowFinal->AddSegment( mnRowCount );
+ }
+ mpSubProgress = mpSubRowFinal;
+ if( mpSubProgress )
+ mpSubProgress->Activate();
+}
+
+void XclExpProgressBar::Progress()
+{
+ if( mpSubProgress && !mpSubProgress->IsFull() )
+ mpSubProgress->Progress();
+}
+
+// Calc->Excel cell address/range conversion ==================================
+
+namespace {
+
+/** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
+void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
+{
+ rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
+ rXclPos.mnRow = static_cast< sal_uInt32 >( nScRow );
+}
+
+} // namespace
+
+XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
+ XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
+{
+}
+
+// cell address ---------------------------------------------------------------
+
+bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
+{
+ // ScAddress::operator<=() doesn't do what we want here
+ bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
+ bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
+ bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
+
+ bool bValid = bValidCol && bValidRow && bValidTab;
+ if( !bValid )
+ {
+ mbColTrunc |= !bValidCol;
+ mbRowTrunc |= !bValidRow;
+ }
+ if( !bValid && bWarn )
+ {
+ mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
+ mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
+ }
+ return bValid;
+}
+
+bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
+ const ScAddress& rScPos, bool bWarn )
+{
+ bool bValid = CheckAddress( rScPos, bWarn );
+ if( bValid )
+ lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
+ return bValid;
+}
+
+XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
+{
+ XclAddress aXclPos( ScAddress::UNINITIALIZED );
+ if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
+ lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
+ return aXclPos;
+}
+
+// cell range -----------------------------------------------------------------
+
+bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
+{
+ return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
+}
+
+bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
+{
+ rScRange.PutInOrder();
+
+ // check start position
+ bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
+ if( bValidStart )
+ {
+ // check & correct end position
+ ScAddress& rScEnd = rScRange.aEnd;
+ if( !CheckAddress( rScEnd, bWarn ) )
+ {
+ rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
+ rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
+ rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
+ }
+ }
+
+ return bValidStart;
+}
+
+bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
+ const ScRange& rScRange, bool bWarn )
+{
+ // check start position
+ bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
+ if( bValidStart )
+ {
+ lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
+
+ // check & correct end position
+ SCCOL nScCol2 = rScRange.aEnd.Col();
+ SCROW nScRow2 = rScRange.aEnd.Row();
+ if( !CheckAddress( rScRange.aEnd, bWarn ) )
+ {
+ nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
+ nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
+ }
+ lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
+ }
+ return bValidStart;
+}
+
+// cell range list ------------------------------------------------------------
+
+void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
+{
+ for ( size_t nRange = rScRanges.size(); nRange > 0; )
+ {
+ ScRange & rScRange = rScRanges[ --nRange ];
+ if( !CheckRange( rScRange, bWarn ) )
+ rScRanges.Remove(nRange);
+ }
+}
+
+void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
+ const ScRangeList& rScRanges, bool bWarn )
+{
+ rXclRanges.clear();
+ for( size_t nPos = 0, nCount = rScRanges.size(); nPos < nCount; ++nPos )
+ {
+ const ScRange & rScRange = rScRanges[ nPos ];
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ if( ConvertRange( aXclRange, rScRange, bWarn ) )
+ rXclRanges.push_back( aXclRange );
+ }
+}
+
+// EditEngine->String conversion ==============================================
+
+namespace {
+
+OUString lclGetUrlRepresentation( const SvxURLField& rUrlField )
+{
+ const OUString& aRepr = rUrlField.GetRepresentation();
+ // no representation -> use URL
+ return aRepr.isEmpty() ? rUrlField.GetURL() : aRepr;
+}
+
+} // namespace
+
+XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
+ XclExpRoot( rRoot ),
+ maScPos( rScPos ),
+ mbMultipleUrls( false )
+{
+}
+
+XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
+{
+}
+
+OUString XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
+{
+ OUString aUrlRepr;
+
+ if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
+ {
+ // there was/is already a HLINK record
+ mbMultipleUrls = static_cast< bool >(mxLinkRec);
+
+ mxLinkRec = new XclExpHyperlink( GetRoot(), rUrlField, maScPos );
+
+ if( const OUString* pRepr = mxLinkRec->GetRepr() )
+ aUrlRepr = *pRepr;
+
+ // add URL to note text
+ maUrlList = ScGlobal::addToken( maUrlList, rUrlField.GetURL(), '\n' );
+ }
+
+ // no hyperlink representation from Excel HLINK record -> use it from text field
+ return aUrlRepr.isEmpty() ? lclGetUrlRepresentation(rUrlField) : aUrlRepr;
+}
+
+bool XclExpHyperlinkHelper::HasLinkRecord() const
+{
+ return !mbMultipleUrls && mxLinkRec;
+}
+
+XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord() const
+{
+ if( HasLinkRecord() )
+ return mxLinkRec;
+ return XclExpHyperlinkRef();
+}
+
+namespace {
+
+/** Creates a new formatted string from the passed unformatted string.
+
+ Creates a Unicode string or a byte string, depending on the current BIFF
+ version contained in the passed XclExpRoot object. May create a formatted
+ string object, if the text contains different script types.
+
+ @param pCellAttr
+ Cell attributes used for font formatting.
+ @param nFlags
+ Modifiers for string export.
+ @param nMaxLen
+ The maximum number of characters to store in this string.
+ @return
+ The new string object.
+ */
+XclExpStringRef lclCreateFormattedString(
+ const XclExpRoot& rRoot, const OUString& rText, const ScPatternAttr* pCellAttr,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ /* Create an empty Excel string object with correctly initialized BIFF mode,
+ because this function only uses Append() functions that require this. */
+ XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, OUString(), nFlags, nMaxLen );
+
+ // script type handling
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
+
+ // font buffer and cell item set
+ XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
+
+ // process all script portions
+ sal_Int32 nPortionPos = 0;
+ sal_Int32 nTextLen = rText.getLength();
+ while( nPortionPos < nTextLen )
+ {
+ // get script type and end position of next script portion
+ sal_Int16 nScript = xBreakIt->getScriptType( rText, nPortionPos );
+ sal_Int32 nPortionEnd = xBreakIt->endOfScript( rText, nPortionPos, nScript );
+
+ // reuse previous script for following weak portions
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+
+ // construct font from current text portion
+ SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, rItemSet, nScript ) );
+
+ // Excel start position of this portion
+ sal_Int32 nXclPortionStart = xString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *xString, rRoot, rText.subView( nPortionPos, nPortionEnd - nPortionPos ) );
+ if( nXclPortionStart < xString->Len() )
+ {
+ // insert font into buffer
+ sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
+ // insert font index into format run vector
+ xString->AppendFormat( nXclPortionStart, nFontIdx );
+ }
+
+ // go to next script portion
+ nLastScript = nScript;
+ nPortionPos = nPortionEnd;
+ }
+
+ return xString;
+}
+
+/** Creates a new formatted string from an edit engine text object.
+
+ Creates a Unicode string or a byte string, depending on the current BIFF
+ version contained in the passed XclExpRoot object.
+
+ @param rEE
+ The edit engine in use. The text object must already be set.
+ @param nFlags
+ Modifiers for string export.
+ @param nMaxLen
+ The maximum number of characters to store in this string.
+ @return
+ The new string object.
+ */
+XclExpStringRef lclCreateFormattedString(
+ const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ /* Create an empty Excel string object with correctly initialized BIFF mode,
+ because this function only uses Append() functions that require this. */
+ XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, OUString(), nFlags, nMaxLen );
+
+ // font buffer and helper item set for edit engine -> Calc item conversion
+ XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *rRoot.GetDoc().GetPool() );
+
+ // script type handling
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ // #i63255# get script type for leading weak characters
+ sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
+
+ // process all paragraphs
+ sal_Int32 nParaCount = rEE.GetParagraphCount();
+ for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
+ {
+ ESelection aSel( nPara, 0 );
+ OUString aParaText( rEE.GetText( nPara ) );
+
+ std::vector<sal_Int32> aPosList;
+ rEE.GetPortions( nPara, aPosList );
+
+ // process all portions in the paragraph
+ for( const auto& rPos : aPosList )
+ {
+ aSel.nEndPos = rPos;
+ OUString aXclPortionText = aParaText.copy( aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
+
+ aItemSet.ClearItem();
+ SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
+ ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
+
+ // get escapement value
+ short nEsc = aEditSet.Get( EE_CHAR_ESCAPEMENT ).GetEsc();
+
+ // process text fields
+ bool bIsHyperlink = false;
+ if( aSel.nStartPos + 1 == aSel.nEndPos )
+ {
+ // test if the character is a text field
+ if( const SvxFieldItem* pItem = aEditSet.GetItemIfSet( EE_FEATURE_FIELD, false ) )
+ {
+ const SvxFieldData* pField = pItem->GetField();
+ if( const SvxURLField* pUrlField = dynamic_cast<const SvxURLField*>( pField ) )
+ {
+ // convert URL field to string representation
+ aXclPortionText = pLinkHelper ?
+ pLinkHelper->ProcessUrlField( *pUrlField ) :
+ lclGetUrlRepresentation( *pUrlField );
+ bIsHyperlink = true;
+ }
+ else
+ {
+ OSL_FAIL( "lclCreateFormattedString - unknown text field" );
+ aXclPortionText.clear();
+ }
+ }
+ }
+
+ // Excel start position of this portion
+ sal_Int32 nXclPortionStart = xString->Len();
+ // add portion text to Excel string
+ XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
+ if( (nXclPortionStart < xString->Len()) || (aParaText.isEmpty()) )
+ {
+ /* Construct font from current edit engine text portion. Edit engine
+ creates different portions for different script types, no need to loop. */
+ sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
+ if( nScript == ApiScriptType::WEAK )
+ nScript = nLastScript;
+ SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, aItemSet, nScript ) );
+ nLastScript = nScript;
+
+ // add escapement
+ aFont.SetEscapement( nEsc );
+ // modify automatic font color for hyperlinks
+ if( bIsHyperlink && aItemSet.Get( ATTR_FONT_COLOR ).GetValue() == COL_AUTO)
+ aFont.SetColor( COL_LIGHTBLUE );
+
+ // insert font into buffer
+ sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
+ // insert font index into format run vector
+ xString->AppendFormat( nXclPortionStart, nFontIdx );
+ }
+
+ aSel.nStartPos = aSel.nEndPos;
+ }
+
+ // add trailing newline (important for correct character index calculation)
+ if( nPara + 1 < nParaCount )
+ XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
+ }
+
+ return xString;
+}
+
+} // namespace
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString = std::make_shared<XclExpString>();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ xString->Assign( rString, nFlags, nMaxLen );
+ else
+ xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString = CreateString( rRoot, OUString(), nFlags, nMaxLen );
+ AppendChar( *xString, rRoot, cChar );
+ return xString;
+}
+
+void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, std::u16string_view rString )
+{
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ rXclString.Append( rString );
+ else
+ rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
+}
+
+void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
+{
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ rXclString.Append( rtl::OUStringChar(cChar) );
+ else
+ rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
+}
+
+XclExpStringRef XclExpStringHelper::CreateCellString(
+ const XclExpRoot& rRoot, const OUString& rString, const ScPatternAttr* pCellAttr,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ return lclCreateFormattedString(rRoot, rString, pCellAttr, nFlags, nMaxLen);
+}
+
+XclExpStringRef XclExpStringHelper::CreateCellString(
+ const XclExpRoot& rRoot, const EditTextObject& rEditText, const ScPatternAttr* pCellAttr,
+ XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ XclExpStringRef xString;
+
+ // formatted cell
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+
+ // default items
+ const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
+ auto pEEItemSet = std::make_unique<SfxItemSet>( rEE.GetEmptyItemSet() );
+ ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
+ rEE.SetDefaults( std::move(pEEItemSet) ); // edit engine takes ownership
+
+ // create the string
+ rEE.SetTextCurrentDefaults(rEditText);
+ xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
+ XclStrFlags nFlags )
+{
+ XclExpStringRef xString;
+ if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
+ {
+ EditEngine& rEE = rRoot.GetDrawEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+ // create the string
+ rEE.SetText( pParaObj->GetTextObject() );
+ xString = lclCreateFormattedString( rRoot, rEE, nullptr, nFlags, EXC_STR_MAXLEN );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+ // limit formats - TODO: BIFF dependent
+ if( !xString->IsEmpty() )
+ {
+ xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
+ xString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
+ // create BIFF dependent empty Excel string
+ xString = CreateString( rRoot, OUString(), nFlags );
+ }
+ return xString;
+}
+
+XclExpStringRef XclExpStringHelper::CreateString(
+ const XclExpRoot& rRoot, const EditTextObject& rEditObj,
+ XclStrFlags nFlags )
+{
+ XclExpStringRef xString;
+ EditEngine& rEE = rRoot.GetDrawEditEngine();
+ bool bOldUpdateMode = rEE.SetUpdateLayout( true );
+ rEE.SetText( rEditObj );
+ xString = lclCreateFormattedString( rRoot, rEE, nullptr, nFlags, EXC_STR_MAXLEN );
+ rEE.SetUpdateLayout( bOldUpdateMode );
+ // limit formats - TODO: BIFF dependent
+ if( !xString->IsEmpty() )
+ {
+ xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
+ xString->AppendTrailingFormat( EXC_FONT_APP );
+ }
+ return xString;
+}
+
+sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const OUString& rString )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
+ sal_Int32 nStrPos = 0;
+ sal_Int32 nStrLen = rString.getLength();
+ sal_Int16 nScript = ApiScriptType::WEAK;
+ while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
+ {
+ nScript = xBreakIt->getScriptType( rString, nStrPos );
+ nStrPos = xBreakIt->endOfScript( rString, nStrPos, nScript );
+ }
+ return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
+}
+
+// Header/footer conversion ===================================================
+
+XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mrEE( rRoot.GetHFEditEngine() ),
+ mnTotalHeight( 0 )
+{
+}
+
+void XclExpHFConverter::GenerateString(
+ const EditTextObject* pLeftObj,
+ const EditTextObject* pCenterObj,
+ const EditTextObject* pRightObj )
+{
+ maHFString.clear();
+ mnTotalHeight = 0;
+ AppendPortion( pLeftObj, 'L' );
+ AppendPortion( pCenterObj, 'C' );
+ AppendPortion( pRightObj, 'R' );
+}
+
+void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
+{
+ if( !pTextObj ) return;
+
+ OUString aText;
+ sal_Int32 nHeight = 0;
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *GetDoc().GetPool() );
+
+ // edit engine
+ bool bOldUpdateMode = mrEE.SetUpdateLayout( true );
+ mrEE.SetText( *pTextObj );
+
+ // font information
+ XclFontData aFontData, aNewData;
+ if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
+ {
+ aFontData = pFirstFont->GetFontData();
+ aFontData.mnHeight = (aFontData.mnHeight + 10) / 20; // using pt here, not twips
+ }
+ else
+ aFontData.mnHeight = 10;
+
+ const FontList* pFontList = nullptr;
+ if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
+ pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
+ pFontList = pInfoItem->GetFontList();
+ }
+
+ sal_Int32 nParaCount = mrEE.GetParagraphCount();
+ for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
+ {
+ ESelection aSel( nPara, 0 );
+ OUStringBuffer aParaText;
+ sal_Int32 nParaHeight = 0;
+ std::vector<sal_Int32> aPosList;
+ mrEE.GetPortions( nPara, aPosList );
+
+ for( const auto& rPos : aPosList )
+ {
+ aSel.nEndPos = rPos;
+ if( aSel.nStartPos < aSel.nEndPos )
+ {
+
+// --- font attributes ---
+
+ vcl::Font aFont;
+ aItemSet.ClearItem();
+ SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
+ ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
+ ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW );
+
+ // font name and style
+ aNewData.maName = XclTools::GetXclFontName( aFont.GetFamilyName() );
+ aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
+ aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
+ bool bNewFont = (aFontData.maName != aNewData.maName);
+ bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
+ (aFontData.mbItalic != aNewData.mbItalic);
+ if( bNewFont || (bNewStyle && pFontList) )
+ {
+ aParaText.append("&\"" + aNewData.maName);
+ if( pFontList )
+ {
+ FontMetric aFontMetric( pFontList->Get(
+ aNewData.maName,
+ (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
+ aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
+ aNewData.maStyle = pFontList->GetStyleName( aFontMetric );
+ if( !aNewData.maStyle.isEmpty() )
+ aParaText.append("," + aNewData.maStyle);
+ }
+ aParaText.append("\"");
+ }
+
+ // height
+ // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
+ // -> get it directly from edit engine item set
+ aNewData.mnHeight = ulimit_cast< sal_uInt16 >( aEditSet.Get( EE_CHAR_FONTHEIGHT ).GetHeight() );
+ aNewData.mnHeight = (aNewData.mnHeight + 10) / 20;
+ bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
+ if( bFontHtChanged )
+ aParaText.append("&" + OUString::number(aNewData.mnHeight));
+ // update maximum paragraph height, convert to twips
+ nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
+
+ // underline
+ aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
+ switch( aFont.GetUnderline() )
+ {
+ case LINESTYLE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case LINESTYLE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
+ case LINESTYLE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+ if( aFontData.mnUnderline != aNewData.mnUnderline )
+ {
+ sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
+ aFontData.mnUnderline : aNewData.mnUnderline;
+ (nTmpUnderl == EXC_FONTUNDERL_SINGLE)? aParaText.append("&U") : aParaText.append("&E");
+ }
+
+ // font color
+ aNewData.maColor = aFont.GetColor();
+ if ( !aFontData.maColor.IsRGBEqual( aNewData.maColor ) )
+ {
+ aParaText.append("&K" + aNewData.maColor.AsRGBHexString());
+ }
+
+ // strikeout
+ aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
+ if( aFontData.mbStrikeout != aNewData.mbStrikeout )
+ aParaText.append("&S");
+
+ // super/sub script
+ const SvxEscapementItem& rEscapeItem = aEditSet.Get( EE_CHAR_ESCAPEMENT );
+ aNewData.SetScEscapement( rEscapeItem.GetEsc() );
+ if( aFontData.mnEscapem != aNewData.mnEscapem )
+ {
+ switch(aNewData.mnEscapem)
+ {
+ // close the previous super/sub script.
+ case EXC_FONTESC_NONE: (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? aParaText.append("&X") : aParaText.append("&Y"); break;
+ case EXC_FONTESC_SUPER: aParaText.append("&X"); break;
+ case EXC_FONTESC_SUB: aParaText.append("&Y"); break;
+ default: break;
+ }
+ }
+
+ aFontData = aNewData;
+
+// --- text content or text fields ---
+
+ const SvxFieldItem* pItem;
+ if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
+ (pItem = aEditSet.GetItemIfSet( EE_FEATURE_FIELD, false )) )
+ {
+ if( const SvxFieldData* pFieldData = pItem->GetField() )
+ {
+ if( dynamic_cast<const SvxPageField*>( pFieldData) != nullptr )
+ aParaText.append("&P");
+ else if( dynamic_cast<const SvxPagesField*>( pFieldData) != nullptr )
+ aParaText.append("&N");
+ else if( dynamic_cast<const SvxDateField*>( pFieldData) != nullptr )
+ aParaText.append("&D");
+ else if( dynamic_cast<const SvxTimeField*>( pFieldData) != nullptr || dynamic_cast<const SvxExtTimeField*>( pFieldData) != nullptr )
+ aParaText.append("&T");
+ else if( dynamic_cast<const SvxTableField*>( pFieldData) != nullptr )
+ aParaText.append("&A");
+ else if( dynamic_cast<const SvxFileField*>( pFieldData) != nullptr ) // title -> file name
+ aParaText.append("&F");
+ else if( const SvxExtFileField* pFileField = dynamic_cast<const SvxExtFileField*>( pFieldData ) )
+ {
+ switch( pFileField->GetFormat() )
+ {
+ case SvxFileFormat::NameAndExt:
+ case SvxFileFormat::NameOnly:
+ aParaText.append("&F");
+ break;
+ case SvxFileFormat::PathOnly:
+ aParaText.append("&Z");
+ break;
+ case SvxFileFormat::PathFull:
+ aParaText.append("&Z&F");
+ break;
+ default:
+ OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
+ }
+ }
+ }
+ }
+ else
+ {
+ OUString aPortionText( mrEE.GetText( aSel ) );
+ aPortionText = aPortionText.replaceAll( "&", "&&" );
+ // #i17440# space between font height and numbers in text
+ if( bFontHtChanged && aParaText.getLength() && !aPortionText.isEmpty() )
+ {
+ sal_Unicode cLast = aParaText[ aParaText.getLength() - 1 ];
+ sal_Unicode cFirst = aPortionText[0];
+ if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
+ aParaText.append(" ");
+ }
+ aParaText.append(aPortionText);
+ }
+ }
+
+ aSel.nStartPos = aSel.nEndPos;
+ }
+
+ aText = ScGlobal::addToken( aText, aParaText, '\n' );
+ aParaText.setLength(0);
+ if( nParaHeight == 0 )
+ nParaHeight = aFontData.mnHeight * 20; // points -> twips
+ nHeight += nParaHeight;
+ }
+
+ mrEE.SetUpdateLayout( bOldUpdateMode );
+
+ if( !aText.isEmpty() )
+ {
+ maHFString += "&" + OUStringChar(cPortionCode) + aText;
+ mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
+ }
+}
+
+// URL conversion =============================================================
+
+namespace {
+
+/** Encodes special parts of the URL, i.e. directory separators and volume names.
+ @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
+OUString lclEncodeDosUrl(
+ XclBiff eBiff, const OUString& rUrl, std::u16string_view rBase, const OUString* pTableName)
+{
+ OUStringBuffer aBuf;
+
+ if (!rUrl.isEmpty())
+ {
+ OUString aOldUrl = rUrl;
+ aBuf.append(EXC_URLSTART_ENCODED);
+
+ if ( aOldUrl.getLength() > 2 && aOldUrl.startsWith("\\\\") )
+ {
+ // UNC
+ aBuf.append(EXC_URL_DOSDRIVE).append('@');
+ aOldUrl = aOldUrl.copy(2);
+ }
+ else if ( aOldUrl.getLength() > 2 && aOldUrl.match(":\\", 1) )
+ {
+ // drive letter
+ sal_Unicode cThisDrive = rBase.empty() ? ' ' : rBase[0];
+ sal_Unicode cDrive = aOldUrl[0];
+ if (cThisDrive == cDrive)
+ // This document and the referenced document are under the same drive.
+ aBuf.append(EXC_URL_DRIVEROOT);
+ else
+ aBuf.append(EXC_URL_DOSDRIVE).append(cDrive);
+ aOldUrl = aOldUrl.copy(3);
+ }
+ else
+ {
+ // URL probably points to a document on a Unix-like file system
+ aBuf.append(EXC_URL_DRIVEROOT);
+ }
+
+ // directories
+ sal_Int32 nPos = -1;
+ while((nPos = aOldUrl.indexOf('\\')) != -1)
+ {
+ if ( aOldUrl.startsWith("..") )
+ // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
+ // Excel seems confused by this token).
+ aBuf.append(EXC_URL_PARENTDIR);
+ else
+ aBuf.append(aOldUrl.subView(0,nPos)).append(EXC_URL_SUBDIR);
+
+ aOldUrl = aOldUrl.copy(nPos + 1);
+ }
+
+ // file name
+ if (pTableName) // enclose file name in brackets if table name follows
+ aBuf.append('[').append(aOldUrl).append(']');
+ else
+ aBuf.append(aOldUrl);
+ }
+ else // empty URL -> self reference
+ {
+ switch( eBiff )
+ {
+ case EXC_BIFF5:
+ aBuf.append(pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF);
+ break;
+ case EXC_BIFF8:
+ DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
+ aBuf.append(EXC_URLSTART_SELF);
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+
+ // table name
+ if (pTableName)
+ aBuf.append(*pTableName);
+
+ // VirtualPath must be shorter than 255 chars ([MS-XLS].pdf 2.5.277)
+ // It's better to truncate, than generate invalid file that Excel cannot open.
+ if (aBuf.getLength() > 255)
+ aBuf.setLength(255);
+
+ return aBuf.makeStringAndClear();
+}
+
+} // namespace
+
+OUString XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, std::u16string_view rAbsUrl, const OUString* pTableName )
+{
+ OUString aDosUrl = INetURLObject(rAbsUrl).getFSysPath(FSysStyle::Dos);
+ OUString aDosBase = INetURLObject(rRoot.GetBasePath()).getFSysPath(FSysStyle::Dos);
+ return lclEncodeDosUrl(rRoot.GetBiff(), aDosUrl, aDosBase, pTableName);
+}
+
+OUString XclExpUrlHelper::EncodeDde( std::u16string_view rApplic, std::u16string_view rTopic )
+{
+ return rApplic + OUStringChar(EXC_DDE_DELIM) + rTopic;
+}
+
+// Cached Value Lists =========================================================
+
+XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
+ : mrMatrix( rMatrix )
+{
+ mrMatrix.IncRef();
+}
+XclExpCachedMatrix::~XclExpCachedMatrix()
+{
+ mrMatrix.DecRef();
+}
+
+void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
+{
+ mrMatrix.GetDimensions( nCols, nRows );
+
+ OSL_ENSURE( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
+ OSL_ENSURE( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
+}
+
+std::size_t XclExpCachedMatrix::GetSize() const
+{
+ SCSIZE nCols, nRows;
+
+ GetDimensions( nCols, nRows );
+
+ /* The returned size may be wrong if the matrix contains strings. The only
+ effect is that the export stream has to update a wrong record size which is
+ faster than to iterate through all cached values and calculate their sizes. */
+ return 3 + 9 * (nCols * nRows);
+}
+
+void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
+{
+ SCSIZE nCols, nRows;
+
+ GetDimensions( nCols, nRows );
+
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ // in BIFF2-BIFF7: 256 columns represented by 0 columns
+ rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
+ else
+ // in BIFF8: columns and rows decreased by 1
+ rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
+
+ for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
+ {
+ for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
+ {
+ ScMatrixValue nMatVal = mrMatrix.Get( nCol, nRow );
+
+ FormulaError nScError;
+ if( ScMatValType::Empty == nMatVal.nType )
+ {
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_EMPTY;
+ rStrm.WriteZeroBytes( 8 );
+ }
+ else if( ScMatrix::IsNonValueType( nMatVal.nType ) )
+ {
+ XclExpString aStr( nMatVal.GetString().getString(), XclStrFlags::NONE );
+ rStrm.SetSliceSize( 6 );
+ rStrm << EXC_CACHEDVAL_STRING << aStr;
+ }
+ else if( ScMatValType::Boolean == nMatVal.nType )
+ {
+ sal_Int8 nBool = sal_Int8(nMatVal.GetBoolean());
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_BOOL << nBool;
+ rStrm.WriteZeroBytes( 7 );
+ }
+ else if( (nScError = nMatVal.GetError()) != FormulaError::NONE )
+ {
+ sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_ERROR << nError;
+ rStrm.WriteZeroBytes( 7 );
+ }
+ else
+ {
+ rStrm.SetSliceSize( 9 );
+ rStrm << EXC_CACHEDVAL_DOUBLE << nMatVal.fVal;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xelink.cxx b/sc/source/filter/excel/xelink.cxx
new file mode 100644
index 000000000..4bdc24335
--- /dev/null
+++ b/sc/source/filter/excel/xelink.cxx
@@ -0,0 +1,2660 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xelink.hxx>
+
+#include <algorithm>
+#include <formula/errorcodes.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sal/log.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <externalrefmgr.hxx>
+#include <tokenarray.hxx>
+#include <xecontent.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xllink.hxx>
+#include <xltools.hxx>
+
+#include <vector>
+#include <memory>
+#include <string_view>
+
+using ::std::unique_ptr;
+using ::std::vector;
+using ::com::sun::star::uno::Any;
+
+using namespace oox;
+
+// *** Helper classes ***
+
+// External names =============================================================
+
+namespace {
+
+/** This is a base class for any external name (i.e. add-in names or DDE links).
+ @descr Derived classes implement creation and export of the external names. */
+class XclExpExtNameBase : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** @param nFlags The flags to export. */
+ explicit XclExpExtNameBase( const XclExpRoot& rRoot,
+ const OUString& rName, sal_uInt16 nFlags = 0 );
+
+ /** Returns the name string of the external name. */
+ const OUString& GetName() const { return maName; }
+
+private:
+ /** Writes the start of the record that is equal in all EXTERNNAME records and calls WriteAddData(). */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ /** Called to write additional data following the common record contents.
+ @descr Derived classes should overwrite this function to write their data. */
+ virtual void WriteAddData( XclExpStream& rStrm );
+
+protected:
+ OUString maName; /// Calc name (title) of the external name.
+ XclExpStringRef mxName; /// Excel name (title) of the external name.
+ sal_uInt16 mnFlags; /// Flags for record export.
+};
+
+/** Represents an EXTERNNAME record for an add-in function name. */
+class XclExpExtNameAddIn : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtNameAddIn( const XclExpRoot& rRoot, const OUString& rName );
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+};
+
+/** Represents an EXTERNNAME record for a DDE link. */
+class XclExpExtNameDde : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtNameDde( const XclExpRoot& rRoot, const OUString& rName,
+ sal_uInt16 nFlags, const ScMatrix* pResults = nullptr );
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpCachedMatrix > XclExpCachedMatRef;
+ XclExpCachedMatRef mxMatrix; /// Cached results of the DDE link.
+};
+
+class XclExpSupbook;
+
+class XclExpExtName : public XclExpExtNameBase
+{
+public:
+ explicit XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook, const OUString& rName,
+ const ScExternalRefCache::TokenArrayRef& rArray );
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+private:
+ /** Writes additional record contents. */
+ virtual void WriteAddData( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpSupbook& mrSupbook;
+ unique_ptr<ScTokenArray> mpArray;
+};
+
+// List of external names =====================================================
+
+/** List of all external names of a sheet. */
+class XclExpExtNameBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtNameBuffer( const XclExpRoot& rRoot );
+
+ /** Inserts an add-in function name
+ @return The 1-based (Excel-like) list index of the name. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+ /** InsertEuroTool */
+ sal_uInt16 InsertEuroTool( const OUString& rName );
+ /** Inserts a DDE link.
+ @return The 1-based (Excel-like) list index of the DDE link. */
+ sal_uInt16 InsertDde( std::u16string_view rApplic, std::u16string_view rTopic, const OUString& rItem );
+
+ sal_uInt16 InsertExtName( const XclExpSupbook& rSupbook, const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ /** Writes the EXTERNNAME record list. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+private:
+ /** Returns the 1-based (Excel-like) list index of the external name or 0, if not found. */
+ sal_uInt16 GetIndex( std::u16string_view rName ) const;
+ /** Appends the passed newly crested external name.
+ @return The 1-based (Excel-like) list index of the appended name. */
+ sal_uInt16 AppendNew( XclExpExtNameBase* pExtName );
+
+ XclExpRecordList< XclExpExtNameBase > maNameList; /// The list with all EXTERNNAME records.
+};
+
+// Cached external cells ======================================================
+
+/** Stores the contents of a consecutive row of external cells (record CRN). */
+class XclExpCrn : public XclExpRecord
+{
+public:
+ explicit XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+
+ /** Returns true, if the passed value could be appended to this record. */
+ bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+
+ /** Writes the row and child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+ static void WriteBool( XclExpStream& rStrm, bool bValue );
+ static void WriteDouble( XclExpStream& rStrm, double fValue );
+ static void WriteString( XclExpStream& rStrm, const OUString& rValue );
+ static void WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode );
+ static void WriteEmpty( XclExpStream& rStrm );
+
+private:
+ typedef ::std::vector< Any > CachedValues;
+
+ CachedValues maValues; /// All cached values.
+ SCCOL mnScCol; /// Column index of the first external cell.
+ SCROW mnScRow; /// Row index of the external cells.
+};
+
+class XclExpCrnList;
+
+/** Represents the record XCT which is the header record of a CRN record list.
+ */
+class XclExpXct : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpXct( const XclExpRoot& rRoot,
+ const OUString& rTabName, sal_uInt16 nSBTab,
+ ScExternalRefCache::TableTypeRef const & xCacheTable );
+
+ /** Returns the external sheet name. */
+ const XclExpString& GetTabName() const { return maTabName; }
+
+ /** Stores all cells in the given range in the CRN list. */
+ void StoreCellRange( const ScRange& rRange );
+
+ void StoreCell_( const ScAddress& rCell );
+ void StoreCellRange_( const ScRange& rRange );
+
+ /** Writes the XCT and all CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes the sheetDataSet and child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ ScExternalRefCache::TableTypeRef mxCacheTable;
+ ScMarkData maUsedCells; /// Contains addresses of all stored cells.
+ ScRange maBoundRange; /// Bounding box of maUsedCells.
+ XclExpString maTabName; /// Sheet name of the external sheet.
+ sal_uInt16 mnSBTab; /// Referred sheet index in SUPBOOK record.
+
+ /** Build the internal representation of records to be saved as BIFF or OOXML. */
+ bool BuildCrnList( XclExpCrnList& rCrnRecs );
+};
+
+// External documents (EXTERNSHEET/SUPBOOK), base class =======================
+
+/** Base class for records representing external sheets/documents.
+
+ In BIFF5/BIFF7, this record is the EXTERNSHEET record containing one sheet
+ of the own or an external document. In BIFF8, this record is the SUPBOOK
+ record representing the entire own or external document with all referenced
+ sheets.
+ */
+class XclExpExternSheetBase : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpExternSheetBase( const XclExpRoot& rRoot,
+ sal_uInt16 nRecId, sal_uInt32 nRecSize = 0 );
+
+protected:
+ /** Creates and returns the list of EXTERNNAME records. */
+ XclExpExtNameBuffer& GetExtNameBuffer();
+ /** Writes the list of EXTERNNAME records. */
+ void WriteExtNameBuffer( XclExpStream& rStrm );
+ /** Writes the list of externalName elements. */
+ void WriteExtNameBufferXml( XclExpXmlStream& rStrm );
+
+protected:
+ typedef std::shared_ptr< XclExpExtNameBuffer > XclExpExtNameBfrRef;
+ XclExpExtNameBfrRef mxExtNameBfr; /// List of EXTERNNAME records.
+};
+
+// External documents (EXTERNSHEET, BIFF5/BIFF7) ==============================
+
+/** Represents an EXTERNSHEET record containing the URL and sheet name of a sheet.
+ @descr This class is used up to BIFF7 only, writing a BIFF8 EXTERNSHEET
+ record is implemented directly in the link manager. */
+class XclExpExternSheet : public XclExpExternSheetBase
+{
+public:
+ /** Creates an EXTERNSHEET record containing a special code (i.e. own document or sheet). */
+ explicit XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode );
+ /** Creates an EXTERNSHEET record referring to an internal sheet. */
+ explicit XclExpExternSheet( const XclExpRoot& rRoot, std::u16string_view rTabName );
+
+ /** Finds or inserts an EXTERNNAME record for add-ins.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+
+ /** Writes the EXTERNSHEET and all EXTERNNAME, XCT and CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Initializes the record data with the passed encoded URL. */
+ void Init( std::u16string_view rEncUrl );
+ /** Writes the contents of the EXTERNSHEET record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpString maTabName; /// The name of the sheet.
+};
+
+// External documents (SUPBOOK, BIFF8) ========================================
+
+/** The SUPBOOK record contains data for an external document (URL, sheet names, external values). */
+class XclExpSupbook : public XclExpExternSheetBase
+{
+public:
+ /** Creates a SUPBOOK record for internal references. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount );
+ /** Creates a SUPBOOK record for add-in functions. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot );
+ /** EUROTOOL SUPBOOK */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, XclSupbookType );
+ /** Creates a SUPBOOK record for an external document. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl );
+ /** Creates a SUPBOOK record for a DDE link. */
+ explicit XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, const OUString& rTopic );
+
+ /** Returns true, if this SUPBOOK contains the passed URL of an external document. */
+ bool IsUrlLink( std::u16string_view rUrl ) const;
+ /** Returns true, if this SUPBOOK contains the passed DDE link. */
+ bool IsDdeLink( std::u16string_view rApplic, std::u16string_view rTopic ) const;
+ /** Fills the passed reference log entry with the URL and sheet names. */
+ void FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry,
+ sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const;
+
+ /** Stores all cells in the given range in the CRN list of the specified SUPBOOK sheet. */
+ void StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab );
+
+ void StoreCell_( sal_uInt16 nSBTab, const ScAddress& rCell );
+ void StoreCellRange_( sal_uInt16 nSBTab, const ScRange& rRange );
+
+ sal_uInt16 GetTabIndex( const OUString& rTabName ) const;
+ sal_uInt16 GetTabCount() const;
+
+ /** Inserts a new sheet name into the SUPBOOK and returns the SUPBOOK internal sheet index. */
+ sal_uInt16 InsertTabName( const OUString& rTabName, ScExternalRefCache::TableTypeRef const & xCacheTable );
+ /** Finds or inserts an EXTERNNAME record for add-ins.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertAddIn( const OUString& rName );
+ /** InsertEuroTool */
+ sal_uInt16 InsertEuroTool( const OUString& rName );
+ /** Finds or inserts an EXTERNNAME record for DDE links.
+ @return The 1-based EXTERNNAME record index; or 0, if the record list is full. */
+ sal_uInt16 InsertDde( const OUString& rItem );
+
+ sal_uInt16 InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ /** Get the type of record. */
+ XclSupbookType GetType() const;
+
+ /** For references to an external document, 1-based OOXML file ID. */
+ sal_uInt16 GetFileId() const;
+
+ /** For references to an external document. */
+ const OUString& GetUrl() const;
+
+ /** Writes the SUPBOOK and all EXTERNNAME, XCT and CRN records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes the externalBook and all child elements. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Returns the sheet name inside of this SUPBOOK. */
+ const XclExpString* GetTabName( sal_uInt16 nSBTab ) const;
+
+ /** Writes the SUPBOOK record contents. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpXct > XclExpXctList;
+ typedef XclExpXctList::RecordRefType XclExpXctRef;
+
+ XclExpXctList maXctList; /// List of XCT records (which contain CRN records).
+ OUString maUrl; /// URL of the external document or application name for DDE.
+ OUString maDdeTopic; /// Topic of a DDE link.
+ XclExpString maUrlEncoded; /// Document name encoded for Excel.
+ XclSupbookType meType; /// Type of this SUPBOOK record.
+ sal_uInt16 mnXclTabCount; /// Number of internal sheets.
+ sal_uInt16 mnFileId; /// 1-based external reference file ID for OOXML
+};
+
+// All SUPBOOKS in a document =================================================
+
+/** This struct contains a sheet index range for 3D references.
+ @descr This reference consists of an index to a SUPBOOK record and indexes
+ to SUPBOOK sheet names. */
+struct XclExpXti
+{
+ sal_uInt16 mnSupbook; /// Index to SUPBOOK record.
+ sal_uInt16 mnFirstSBTab; /// Index to the first sheet of the range in the SUPBOOK.
+ sal_uInt16 mnLastSBTab; /// Index to the last sheet of the range in the SUPBOOK.
+
+ explicit XclExpXti() : mnSupbook( 0 ), mnFirstSBTab( 0 ), mnLastSBTab( 0 ) {}
+ explicit XclExpXti( sal_uInt16 nSupbook, sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) :
+ mnSupbook( nSupbook ), mnFirstSBTab( nFirstSBTab ), mnLastSBTab( nLastSBTab ) {}
+
+ /** Writes this XTI structure (inside of the EXTERNSHEET record). */
+ void Save( XclExpStream& rStrm ) const
+ { rStrm << mnSupbook << mnFirstSBTab << mnLastSBTab; }
+};
+
+bool operator==( const XclExpXti& rLeft, const XclExpXti& rRight )
+{
+ return
+ (rLeft.mnSupbook == rRight.mnSupbook) &&
+ (rLeft.mnFirstSBTab == rRight.mnFirstSBTab) &&
+ (rLeft.mnLastSBTab == rRight.mnLastSBTab);
+}
+
+/** Contains a list of all SUPBOOK records and index arrays of external sheets. */
+class XclExpSupbookBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpSupbookBuffer( const XclExpRoot& rRoot );
+
+ /** Finds SUPBOOK index and SUPBOOK sheet range from given Excel sheet range.
+ @return An XTI structure containing SUPBOOK and sheet indexes. */
+ XclExpXti GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab,
+ XclExpRefLogEntry* pRefLogEntry = nullptr ) const;
+
+ /** Stores all cells in the given range in a CRN record list. */
+ void StoreCellRange( const ScRange& rRange );
+
+ void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell );
+ void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange );
+
+ /** Finds or inserts an EXTERNNAME record for an add-in function name.
+ @param rnSupbook Returns the index of the SUPBOOK record which contains the add-in function name.
+ @param rnExtName Returns the 1-based EXTERNNAME record index. */
+ bool InsertAddIn(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** InsertEuroTool */
+ bool InsertEuroTool(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** Finds or inserts an EXTERNNAME record for DDE links.
+ @param rnSupbook Returns the index of the SUPBOOK record which contains the DDE link.
+ @param rnExtName Returns the 1-based EXTERNNAME record index. */
+ bool InsertDde(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem );
+
+ bool InsertExtName(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ XclExpXti GetXti( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ XclExpRefLogEntry* pRefLogEntry );
+
+ /** Writes all SUPBOOK records with their sub records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes all externalBook elements with their child elements to OOXML. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ /** Whether we need to write externalReferences or not. */
+ bool HasExternalReferences() const;
+
+ struct XclExpSBIndex
+ {
+ sal_uInt16 mnSupbook; /// SUPBOOK index for an Excel sheet.
+ sal_uInt16 mnSBTab; /// Sheet name index in SUPBOOK for an Excel sheet.
+ void Set( sal_uInt16 nSupbook, sal_uInt16 nSBTab )
+ { mnSupbook = nSupbook; mnSBTab = nSBTab; }
+ };
+
+private:
+ typedef XclExpRecordList< XclExpSupbook > XclExpSupbookList;
+ typedef XclExpSupbookList::RecordRefType XclExpSupbookRef;
+
+private:
+ /** Searches for the SUPBOOK record containing the passed document URL.
+ @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0.
+ @param rnIndex (out-param) Returns the list index, if the SUPBOOK exists.
+ @return True, if the SUPBOOK record exists (out-parameters are valid). */
+ bool GetSupbookUrl( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex,
+ std::u16string_view rUrl ) const;
+ /** Searches for the SUPBOOK record containing the passed DDE link.
+ @param rxSupbook (out-param) Returns a reference to the SUPBOOK record, or 0.
+ @param rnIndex (out-param) Returns the list index, if the SUPBOOK exists.
+ @return True, if the SUPBOOK record exists (out-parameters are valid). */
+ bool GetSupbookDde( XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex,
+ std::u16string_view rApplic, std::u16string_view rTopic ) const;
+
+ /** Appends a new SUPBOOK to the list.
+ @return The list index of the SUPBOOK record. */
+ sal_uInt16 Append( XclExpSupbookRef const & xSupbook );
+
+private:
+ XclExpSupbookList maSupbookList; /// List of all SUPBOOK records.
+ std::vector< XclExpSBIndex >
+ maSBIndexVec; /// SUPBOOK and sheet name index for each Excel sheet.
+ sal_uInt16 mnOwnDocSB; /// Index to SUPBOOK for own document.
+ sal_uInt16 mnAddInSB; /// Index to add-in SUPBOOK.
+};
+
+}
+
+// Export link manager ========================================================
+
+/** Abstract base class for implementation classes of the link manager. */
+class XclExpLinkManagerImpl : protected XclExpRoot
+{
+public:
+ /** Derived classes search for an EXTSHEET structure for the given Calc sheet range. */
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) = 0;
+ /** Derived classes search for a special EXTERNSHEET index for the own document. */
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) = 0;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) = 0;
+
+ /** Derived classes store all cells in the given range in a CRN record list. */
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) = 0;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) = 0;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) = 0;
+
+ /** Derived classes find or insert an EXTERNNAME record for an add-in function name. */
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) = 0;
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) = 0;
+
+ /** Derived classes find or insert an EXTERNNAME record for DDE links. */
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) = 0;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) = 0;
+
+ /** Derived classes write the entire link table to the passed stream. */
+ virtual void Save( XclExpStream& rStrm ) = 0;
+
+ /** Derived classes write the entire link table to the passed OOXML stream. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) = 0;
+
+protected:
+ explicit XclExpLinkManagerImpl( const XclExpRoot& rRoot );
+};
+
+namespace {
+
+/** Implementation of the link manager for BIFF5/BIFF7. */
+class XclExpLinkManagerImpl5 : public XclExpLinkManagerImpl
+{
+public:
+ explicit XclExpLinkManagerImpl5( const XclExpRoot& rRoot );
+
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) override;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) override;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) override;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) override;
+
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) override;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpExternSheet > XclExpExtSheetList;
+ typedef XclExpExtSheetList::RecordRefType XclExpExtSheetRef;
+ typedef ::std::map< SCTAB, sal_uInt16 > XclExpIntTabMap;
+ typedef ::std::map< sal_Unicode, sal_uInt16 > XclExpCodeMap;
+
+private:
+ /** Returns the number of EXTERNSHEET records. */
+ sal_uInt16 GetExtSheetCount() const;
+
+ /** Appends an internal EXTERNSHEET record and returns the one-based index. */
+ sal_uInt16 AppendInternal( XclExpExtSheetRef const & xExtSheet );
+ /** Creates all EXTERNSHEET records for internal sheets on first call. */
+ void CreateInternal();
+
+ /** Returns the specified internal EXTERNSHEET record. */
+ XclExpExtSheetRef GetInternal( sal_uInt16 nExtSheet );
+ /** Returns the EXTERNSHEET index of an internal Calc sheet, or a deleted reference. */
+ XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab );
+ /** Finds or creates the EXTERNSHEET index of an internal special EXTERNSHEET. */
+ XclExpExtSheetRef FindInternal( sal_uInt16& rnExtSheet, sal_Unicode cCode );
+
+private:
+ XclExpExtSheetList maExtSheetList; /// List with EXTERNSHEET records.
+ XclExpIntTabMap maIntTabMap; /// Maps internal Calc sheets to EXTERNSHEET records.
+ XclExpCodeMap maCodeMap; /// Maps special external codes to EXTERNSHEET records.
+};
+
+/** Implementation of the link manager for BIFF8 and OOXML. */
+class XclExpLinkManagerImpl8 : public XclExpLinkManagerImpl
+{
+public:
+ explicit XclExpLinkManagerImpl8( const XclExpRoot& rRoot );
+
+ virtual void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+ virtual sal_uInt16 FindExtSheet( sal_Unicode cCode ) override;
+
+ virtual void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry ) override;
+
+ virtual void StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos ) override;
+
+ virtual void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos ) override;
+ virtual void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange ) override;
+
+ virtual bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+ /** InsertEuroTool */
+ virtual bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName ) override;
+
+ virtual bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem ) override;
+
+ virtual bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Searches for or inserts a new XTI structure.
+ @return The 0-based list index of the XTI structure. */
+ sal_uInt16 InsertXti( const XclExpXti& rXti );
+
+private:
+
+ XclExpSupbookBuffer maSBBuffer; /// List of all SUPBOOK records.
+ std::vector< XclExpXti > maXtiVec; /// List of XTI structures for the EXTERNSHEET record.
+};
+
+}
+
+// *** Implementation ***
+
+// Excel sheet indexes ========================================================
+
+
+XclExpTabInfo::XclExpTabInfo( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnScCnt( 0 ),
+ mnXclCnt( 0 ),
+ mnXclExtCnt( 0 ),
+ mnXclSelCnt( 0 ),
+ mnDisplXclTab( 0 ),
+ mnFirstVisXclTab( 0 )
+{
+ ScDocument& rDoc = GetDoc();
+ ScExtDocOptions& rDocOpt = GetExtDocOptions();
+
+ mnScCnt = rDoc.GetTableCount();
+
+ SCTAB nScTab;
+ SCTAB nFirstVisScTab = SCTAB_INVALID; // first visible sheet
+ SCTAB nFirstExpScTab = SCTAB_INVALID; // first exported sheet
+
+ // --- initialize the flags in the index buffer ---
+
+ maTabInfoVec.resize( mnScCnt );
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ // ignored sheets (skipped by export, with invalid Excel sheet index)
+ if( rDoc.IsScenario( nScTab ) )
+ {
+ SetFlag( nScTab, ExcTabBufFlags::Ignore );
+ }
+
+ // external sheets (skipped, but with valid Excel sheet index for ref's)
+ else if( rDoc.GetLinkMode( nScTab ) == ScLinkMode::VALUE )
+ {
+ SetFlag( nScTab, ExcTabBufFlags::Extern );
+ }
+
+ // exported sheets
+ else
+ {
+ // sheet name
+ rDoc.GetName( nScTab, maTabInfoVec[ nScTab ].maScName );
+
+ // remember first exported sheet
+ if( nFirstExpScTab == SCTAB_INVALID )
+ nFirstExpScTab = nScTab;
+ // remember first visible exported sheet
+ if( (nFirstVisScTab == SCTAB_INVALID) && rDoc.IsVisible( nScTab ) )
+ nFirstVisScTab = nScTab;
+
+ // sheet visible (only exported sheets)
+ SetFlag( nScTab, ExcTabBufFlags::Visible, rDoc.IsVisible( nScTab ) );
+
+ // sheet selected (only exported sheets)
+ if( const ScExtTabSettings* pTabSett = rDocOpt.GetTabSettings( nScTab ) )
+ SetFlag( nScTab, ExcTabBufFlags::Selected, pTabSett->mbSelected );
+
+ // sheet mirrored (only exported sheets)
+ SetFlag( nScTab, ExcTabBufFlags::Mirrored, rDoc.IsLayoutRTL( nScTab ) );
+ }
+ }
+
+ // --- visible/selected sheets ---
+
+ SCTAB nDisplScTab = rDocOpt.GetDocSettings().mnDisplTab;
+
+ // missing viewdata at embedded XLSX OLE objects
+ if (nDisplScTab == -1 )
+ nDisplScTab = rDoc.GetVisibleTab();
+
+ // find first visible exported sheet
+ if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) )
+ {
+ // no exportable visible sheet -> use first exportable sheet
+ nFirstVisScTab = nFirstExpScTab;
+ if( (nFirstVisScTab == SCTAB_INVALID) || !IsExportTab( nFirstVisScTab ) )
+ {
+ // no exportable sheet at all -> use active sheet and export it
+ nFirstVisScTab = nDisplScTab;
+ SetFlag( nFirstVisScTab, ExcTabBufFlags::SkipMask, false ); // clear skip flags
+ }
+ SetFlag( nFirstVisScTab, ExcTabBufFlags::Visible ); // must be visible, even if originally hidden
+ }
+
+ // find currently displayed sheet
+ if( !IsExportTab( nDisplScTab ) ) // selected sheet not exported (i.e. scenario) -> use first visible
+ nDisplScTab = nFirstVisScTab;
+ SetFlag( nDisplScTab, ExcTabBufFlags::Visible | ExcTabBufFlags::Selected );
+
+ // number of selected sheets
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ if( IsSelectedTab( nScTab ) )
+ ++mnXclSelCnt;
+
+ // --- calculate resulting Excel sheet indexes ---
+
+ CalcXclIndexes();
+ mnFirstVisXclTab = GetXclTab( nFirstVisScTab );
+ mnDisplXclTab = GetXclTab( nDisplScTab );
+
+ // --- sorted vectors for index lookup ---
+
+ CalcSortedIndexes();
+}
+
+bool XclExpTabInfo::IsExportTab( SCTAB nScTab ) const
+{
+ /* Check sheet index before to avoid assertion in GetFlag(). */
+ return (nScTab < mnScCnt && nScTab >= 0) && !GetFlag( nScTab, ExcTabBufFlags::SkipMask );
+}
+
+bool XclExpTabInfo::IsExternalTab( SCTAB nScTab ) const
+{
+ /* Check sheet index before to avoid assertion (called from formula
+ compiler also for deleted references). */
+ return (nScTab < mnScCnt && nScTab >= 0) && GetFlag( nScTab, ExcTabBufFlags::Extern );
+}
+
+bool XclExpTabInfo::IsVisibleTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Visible );
+}
+
+bool XclExpTabInfo::IsSelectedTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Selected );
+}
+
+bool XclExpTabInfo::IsDisplayedTab( SCTAB nScTab ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::IsActiveTab - sheet out of range" );
+ return GetXclTab( nScTab ) == mnDisplXclTab;
+}
+
+bool XclExpTabInfo::IsMirroredTab( SCTAB nScTab ) const
+{
+ return GetFlag( nScTab, ExcTabBufFlags::Mirrored );
+}
+
+OUString XclExpTabInfo::GetScTabName( SCTAB nScTab ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::IsActiveTab - sheet out of range" );
+ return (nScTab < mnScCnt && nScTab >= 0) ? maTabInfoVec[ nScTab ].maScName : OUString();
+}
+
+sal_uInt16 XclExpTabInfo::GetXclTab( SCTAB nScTab ) const
+{
+ return (nScTab < mnScCnt && nScTab >= 0) ? maTabInfoVec[ nScTab ].mnXclTab : EXC_TAB_DELETED;
+}
+
+SCTAB XclExpTabInfo::GetRealScTab( SCTAB nSortedScTab ) const
+{
+ OSL_ENSURE( nSortedScTab < mnScCnt && nSortedScTab >= 0, "XclExpTabInfo::GetRealScTab - sheet out of range" );
+ return (nSortedScTab < mnScCnt && nSortedScTab >= 0) ? maFromSortedVec[ nSortedScTab ] : SCTAB_INVALID;
+}
+
+bool XclExpTabInfo::GetFlag( SCTAB nScTab, ExcTabBufFlags nFlags ) const
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::GetFlag - sheet out of range" );
+ return (nScTab < mnScCnt && nScTab >= 0) && (maTabInfoVec[ nScTab ].mnFlags & nFlags);
+}
+
+void XclExpTabInfo::SetFlag( SCTAB nScTab, ExcTabBufFlags nFlags, bool bSet )
+{
+ OSL_ENSURE( nScTab < mnScCnt && nScTab >= 0, "XclExpTabInfo::SetFlag - sheet out of range" );
+ if( nScTab < mnScCnt && nScTab >= 0 )
+ {
+ if (bSet)
+ maTabInfoVec[ nScTab ].mnFlags |= nFlags;
+ else
+ maTabInfoVec[ nScTab ].mnFlags &= ~nFlags;
+ }
+}
+
+void XclExpTabInfo::CalcXclIndexes()
+{
+ sal_uInt16 nXclTab = 0;
+ SCTAB nScTab = 0;
+
+ // --- pass 1: process regular sheets ---
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ if( IsExportTab( nScTab ) )
+ {
+ maTabInfoVec[ nScTab ].mnXclTab = nXclTab;
+ ++nXclTab;
+ }
+ else
+ maTabInfoVec[ nScTab ].mnXclTab = EXC_TAB_DELETED;
+ }
+ mnXclCnt = nXclTab;
+
+ // --- pass 2: process external sheets (nXclTab continues) ---
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ if( IsExternalTab( nScTab ) )
+ {
+ maTabInfoVec[ nScTab ].mnXclTab = nXclTab;
+ ++nXclTab;
+ ++mnXclExtCnt;
+ }
+ }
+
+ // result: first occur all exported sheets, followed by all external sheets
+}
+
+typedef ::std::pair< OUString, SCTAB > XclExpTabName;
+
+namespace {
+
+struct XclExpTabNameSort {
+ bool operator ()( const XclExpTabName& rArg1, const XclExpTabName& rArg2 )
+ {
+ // compare the sheet names only
+ return ScGlobal::GetCollator().compareString( rArg1.first, rArg2.first ) < 0;
+ }
+};
+
+}
+
+void XclExpTabInfo::CalcSortedIndexes()
+{
+ ScDocument& rDoc = GetDoc();
+ ::std::vector< XclExpTabName > aVec( mnScCnt );
+ SCTAB nScTab;
+
+ // fill with sheet names
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ rDoc.GetName( nScTab, aVec[ nScTab ].first );
+ aVec[ nScTab ].second = nScTab;
+ }
+ ::std::sort( aVec.begin(), aVec.end(), XclExpTabNameSort() );
+
+ // fill index vectors from sorted sheet name vector
+ maFromSortedVec.resize( mnScCnt );
+ maToSortedVec.resize( mnScCnt );
+ for( nScTab = 0; nScTab < mnScCnt; ++nScTab )
+ {
+ maFromSortedVec[ nScTab ] = aVec[ nScTab ].second;
+ maToSortedVec[ aVec[ nScTab ].second ] = nScTab;
+ }
+}
+
+// External names =============================================================
+
+XclExpExtNameBase::XclExpExtNameBase(
+ const XclExpRoot& rRoot, const OUString& rName, sal_uInt16 nFlags ) :
+ XclExpRecord( EXC_ID_EXTERNNAME ),
+ XclExpRoot( rRoot ),
+ maName( rName ),
+ mxName( XclExpStringHelper::CreateString( rRoot, rName, XclStrFlags::EightBitLength ) ),
+ mnFlags( nFlags )
+{
+ OSL_ENSURE( maName.getLength() <= 255, "XclExpExtNameBase::XclExpExtNameBase - string too long" );
+ SetRecSize( 6 + mxName->GetSize() );
+}
+
+void XclExpExtNameBase::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnFlags
+ << sal_uInt32( 0 )
+ << *mxName;
+ WriteAddData( rStrm );
+}
+
+void XclExpExtNameBase::WriteAddData( XclExpStream& /*rStrm*/ )
+{
+}
+
+XclExpExtNameAddIn::XclExpExtNameAddIn( const XclExpRoot& rRoot, const OUString& rName ) :
+ XclExpExtNameBase( rRoot, rName )
+{
+ AddRecSize( 4 );
+}
+
+void XclExpExtNameAddIn::WriteAddData( XclExpStream& rStrm )
+{
+ // write a #REF! error formula
+ rStrm << sal_uInt16( 2 ) << EXC_TOKID_ERR << EXC_ERR_REF;
+}
+
+XclExpExtNameDde::XclExpExtNameDde( const XclExpRoot& rRoot,
+ const OUString& rName, sal_uInt16 nFlags, const ScMatrix* pResults ) :
+ XclExpExtNameBase( rRoot, rName, nFlags )
+{
+ if( pResults )
+ {
+ mxMatrix = std::make_shared<XclExpCachedMatrix>( *pResults );
+ AddRecSize( mxMatrix->GetSize() );
+ }
+}
+
+void XclExpExtNameDde::WriteAddData( XclExpStream& rStrm )
+{
+ if( mxMatrix )
+ mxMatrix->Save( rStrm );
+}
+
+XclExpExtName::XclExpExtName( const XclExpRoot& rRoot, const XclExpSupbook& rSupbook,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray ) :
+ XclExpExtNameBase( rRoot, rName ),
+ mrSupbook(rSupbook),
+ mpArray(rArray->Clone())
+{
+}
+
+void XclExpExtName::WriteAddData( XclExpStream& rStrm )
+{
+ // Write only if it only has a single token that is either a cell or cell
+ // range address. Excel just writes '02 00 1C 17' for all the other types
+ // of external names.
+
+ using namespace ::formula;
+ do
+ {
+ if (mpArray->GetLen() != 1)
+ break;
+
+ const formula::FormulaToken* p = mpArray->FirstToken();
+ if (!p->IsExternalRef())
+ break;
+
+ switch (p->GetType())
+ {
+ case svExternalSingleRef:
+ {
+ const ScSingleRefData& rRef = *p->GetSingleRef();
+ if (rRef.IsTabRel())
+ break;
+
+ bool bColRel = rRef.IsColRel();
+ bool bRowRel = rRef.IsRowRel();
+ sal_uInt16 nCol = static_cast<sal_uInt16>(rRef.Col());
+ sal_uInt16 nRow = static_cast<sal_uInt16>(rRef.Row());
+ if (bColRel) nCol |= 0x4000;
+ if (bRowRel) nCol |= 0x8000;
+
+ OUString aTabName = p->GetString().getString();
+ sal_uInt16 nSBTab = mrSupbook.GetTabIndex(aTabName);
+
+ // size is always 9
+ rStrm << static_cast<sal_uInt16>(9);
+ // operator token (3A for cell reference)
+ rStrm << static_cast<sal_uInt8>(0x3A);
+ // cell address (Excel's address has 2 sheet IDs.)
+ rStrm << nSBTab << nSBTab << nRow << nCol;
+ return;
+ }
+ case svExternalDoubleRef:
+ {
+ const ScComplexRefData& rRef = *p->GetDoubleRef();
+ const ScSingleRefData& r1 = rRef.Ref1;
+ const ScSingleRefData& r2 = rRef.Ref2;
+ if (r1.IsTabRel() || r2.IsTabRel())
+ break;
+
+ sal_uInt16 nTab1 = r1.Tab();
+ sal_uInt16 nTab2 = r2.Tab();
+ bool bCol1Rel = r1.IsColRel();
+ bool bRow1Rel = r1.IsRowRel();
+ bool bCol2Rel = r2.IsColRel();
+ bool bRow2Rel = r2.IsRowRel();
+
+ sal_uInt16 nCol1 = static_cast<sal_uInt16>(r1.Col());
+ sal_uInt16 nCol2 = static_cast<sal_uInt16>(r2.Col());
+ sal_uInt16 nRow1 = static_cast<sal_uInt16>(r1.Row());
+ sal_uInt16 nRow2 = static_cast<sal_uInt16>(r2.Row());
+ if (bCol1Rel) nCol1 |= 0x4000;
+ if (bRow1Rel) nCol1 |= 0x8000;
+ if (bCol2Rel) nCol2 |= 0x4000;
+ if (bRow2Rel) nCol2 |= 0x8000;
+
+ OUString aTabName = p->GetString().getString();
+ sal_uInt16 nSBTab = mrSupbook.GetTabIndex(aTabName);
+
+ // size is always 13 (0x0D)
+ rStrm << static_cast<sal_uInt16>(13);
+ // operator token (3B for area reference)
+ rStrm << static_cast<sal_uInt8>(0x3B);
+ // range (area) address
+ sal_uInt16 nSBTab2 = nSBTab + nTab2 - nTab1;
+ rStrm << nSBTab << nSBTab2 << nRow1 << nRow2 << nCol1 << nCol2;
+ return;
+ }
+ default:
+ ; // nothing
+ }
+ }
+ while (false);
+
+ // special value for #REF! (02 00 1C 17)
+ rStrm << static_cast<sal_uInt16>(2) << EXC_TOKID_ERR << EXC_ERR_REF;
+}
+
+void XclExpExtName::SaveXml(XclExpXmlStream& rStrm)
+{
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.GetCurrentStream();
+
+ /* TODO: mpArray contains external references. It doesn't cause any problems, but it's enough
+ to export it without the external document identifier. */
+ if (mpArray->GetLen())
+ {
+ const OUString aFormula = XclXmlUtils::ToOUString(GetCompileFormulaContext(), ScAddress(0, 0, 0), mpArray.get());
+ pExternalLink->startElement(XML_definedName,
+ XML_name, maName.toUtf8(),
+ XML_refersTo, aFormula.toUtf8(),
+ XML_sheetId, nullptr);
+ }
+ else
+ {
+ pExternalLink->startElement(XML_definedName,
+ XML_name, maName.toUtf8(),
+ XML_refersTo, nullptr,
+ XML_sheetId, nullptr);
+ }
+
+ pExternalLink->endElement(XML_definedName);
+}
+
+// List of external names =====================================================
+
+XclExpExtNameBuffer::XclExpExtNameBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertAddIn( const OUString& rName )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtNameAddIn( GetRoot(), rName ) );
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertEuroTool( const OUString& rName )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtNameBase( GetRoot(), rName ) );
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertDde(
+ std::u16string_view rApplic, std::u16string_view rTopic, const OUString& rItem )
+{
+ sal_uInt16 nIndex = GetIndex( rItem );
+ if( nIndex == 0 )
+ {
+ size_t nPos;
+ if( GetDoc().FindDdeLink( rApplic, rTopic, rItem, SC_DDE_IGNOREMODE, nPos ) )
+ {
+ // create the leading 'StdDocumentName' EXTERNNAME record
+ if( maNameList.IsEmpty() )
+ AppendNew( new XclExpExtNameDde(
+ GetRoot(), "StdDocumentName", EXC_EXTN_EXPDDE_STDDOC ) );
+
+ // try to find DDE result array, but create EXTERNNAME record without them too
+ const ScMatrix* pScMatrix = GetDoc().GetDdeLinkResultMatrix( nPos );
+ nIndex = AppendNew( new XclExpExtNameDde( GetRoot(), rItem, EXC_EXTN_EXPDDE, pScMatrix ) );
+ }
+ }
+ return nIndex;
+}
+
+sal_uInt16 XclExpExtNameBuffer::InsertExtName( const XclExpSupbook& rSupbook,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ sal_uInt16 nIndex = GetIndex( rName );
+ return nIndex ? nIndex : AppendNew( new XclExpExtName( GetRoot(), rSupbook, rName, rArray ) );
+}
+
+void XclExpExtNameBuffer::Save( XclExpStream& rStrm )
+{
+ maNameList.Save( rStrm );
+}
+
+void XclExpExtNameBuffer::SaveXml(XclExpXmlStream& rStrm)
+{
+ maNameList.SaveXml(rStrm);
+}
+
+sal_uInt16 XclExpExtNameBuffer::GetIndex( std::u16string_view rName ) const
+{
+ for( size_t nPos = 0, nSize = maNameList.GetSize(); nPos < nSize; ++nPos )
+ if( maNameList.GetRecord( nPos )->GetName() == rName )
+ return static_cast< sal_uInt16 >( nPos + 1 );
+ return 0;
+}
+
+sal_uInt16 XclExpExtNameBuffer::AppendNew( XclExpExtNameBase* pExtName )
+{
+ size_t nSize = maNameList.GetSize();
+ if( nSize < 0x7FFF )
+ {
+ maNameList.AppendRecord( pExtName );
+ return static_cast< sal_uInt16 >( nSize + 1 );
+ }
+ return 0;
+}
+
+// Cached external cells ======================================================
+
+XclExpCrn::XclExpCrn( SCCOL nScCol, SCROW nScRow, const Any& rValue ) :
+ XclExpRecord( EXC_ID_CRN, 4 ),
+ mnScCol( nScCol ),
+ mnScRow( nScRow )
+{
+ maValues.push_back( rValue );
+}
+
+bool XclExpCrn::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue )
+{
+ if( (nScRow != mnScRow) || (nScCol != static_cast< SCCOL >( mnScCol + maValues.size() )) )
+ return false;
+ maValues.push_back( rValue );
+ return true;
+}
+
+void XclExpCrn::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt8 >( mnScCol + maValues.size() - 1 )
+ << static_cast< sal_uInt8 >( mnScCol )
+ << static_cast< sal_uInt16 >( mnScRow );
+ for( const auto& rValue : maValues )
+ {
+ if( rValue.has< bool >() )
+ WriteBool( rStrm, rValue.get< bool >() );
+ else if( rValue.has< double >() )
+ WriteDouble( rStrm, rValue.get< double >() );
+ else if( rValue.has< OUString >() )
+ WriteString( rStrm, rValue.get< OUString >() );
+ else
+ WriteEmpty( rStrm );
+ }
+}
+
+void XclExpCrn::WriteBool( XclExpStream& rStrm, bool bValue )
+{
+ rStrm << EXC_CACHEDVAL_BOOL << sal_uInt8( bValue ? 1 : 0);
+ rStrm.WriteZeroBytes( 7 );
+}
+
+void XclExpCrn::WriteDouble( XclExpStream& rStrm, double fValue )
+{
+ if( !std::isfinite( fValue ) )
+ {
+ FormulaError nScError = GetDoubleErrorValue(fValue);
+ WriteError( rStrm, XclTools::GetXclErrorCode( nScError ) );
+ }
+ else
+ {
+ rStrm << EXC_CACHEDVAL_DOUBLE << fValue;
+ }
+}
+
+void XclExpCrn::WriteString( XclExpStream& rStrm, const OUString& rValue )
+{
+ rStrm << EXC_CACHEDVAL_STRING << XclExpString( rValue );
+}
+
+void XclExpCrn::WriteError( XclExpStream& rStrm, sal_uInt8 nErrCode )
+{
+ rStrm << EXC_CACHEDVAL_ERROR << nErrCode;
+ rStrm.WriteZeroBytes( 7 );
+}
+
+void XclExpCrn::WriteEmpty( XclExpStream& rStrm )
+{
+ rStrm << EXC_CACHEDVAL_EMPTY;
+ rStrm.WriteZeroBytes( 8 );
+}
+
+void XclExpCrn::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream();
+
+ pFS->startElement(XML_row, XML_r, OString::number(mnScRow + 1));
+
+ ScAddress aAdr( mnScCol, mnScRow, 0); // Tab number doesn't matter
+ for( const auto& rValue : maValues )
+ {
+ bool bCloseCell = true;
+ if( rValue.has< double >() )
+ {
+ double fVal = rValue.get< double >();
+ if (std::isfinite( fVal))
+ {
+ // t='n' is omitted
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr));
+ pFS->startElement(XML_v);
+ pFS->write( fVal );
+ }
+ else
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "e");
+ pFS->startElement(XML_v);
+ pFS->write( "#VALUE!" ); // OOXTODO: support other error values
+ }
+ }
+ else if( rValue.has< OUString >() )
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "str");
+ pFS->startElement(XML_v);
+ pFS->write( rValue.get< OUString >() );
+ }
+ else if( rValue.has< bool >() )
+ {
+ pFS->startElement(XML_cell, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aAdr), XML_t, "b");
+ pFS->startElement(XML_v);
+ pFS->write( rValue.get< bool >() ? "1" : "0" );
+ }
+ // OOXTODO: error type cell t='e'
+ else
+ {
+ // Empty/blank cell not stored, only aAdr is incremented.
+ bCloseCell = false;
+ }
+ if (bCloseCell)
+ {
+ pFS->endElement(XML_v);
+ pFS->endElement(XML_cell);
+ }
+ aAdr.IncCol();
+ }
+
+ pFS->endElement( XML_row);
+}
+
+// Cached cells of a sheet ====================================================
+
+XclExpXct::XclExpXct( const XclExpRoot& rRoot, const OUString& rTabName,
+ sal_uInt16 nSBTab, ScExternalRefCache::TableTypeRef const & xCacheTable ) :
+ XclExpRoot( rRoot ),
+ mxCacheTable( xCacheTable ),
+ maUsedCells( rRoot.GetDoc().GetSheetLimits() ),
+ maBoundRange( ScAddress::INITIALIZE_INVALID ),
+ maTabName( rTabName ),
+ mnSBTab( nSBTab )
+{
+}
+
+void XclExpXct::StoreCellRange( const ScRange& rRange )
+{
+ // #i70418# restrict size of external range to prevent memory overflow
+ if( (rRange.aEnd.Col() - rRange.aStart.Col()) * (rRange.aEnd.Row() - rRange.aStart.Row()) > 1024 )
+ return;
+
+ maUsedCells.SetMultiMarkArea( rRange );
+ maBoundRange.ExtendTo( rRange );
+}
+
+void XclExpXct::StoreCell_( const ScAddress& rCell )
+{
+ maUsedCells.SetMultiMarkArea( ScRange( rCell ) );
+ maBoundRange.ExtendTo( ScRange( rCell ) );
+}
+
+void XclExpXct::StoreCellRange_( const ScRange& rRange )
+{
+ maUsedCells.SetMultiMarkArea( rRange );
+ maBoundRange.ExtendTo( rRange );
+}
+
+namespace {
+
+class XclExpCrnList : public XclExpRecordList< XclExpCrn >
+{
+public:
+ /** Inserts the passed value into an existing or new CRN record.
+ @return True = value inserted successfully, false = CRN list is full. */
+ bool InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue );
+};
+
+bool XclExpCrnList::InsertValue( SCCOL nScCol, SCROW nScRow, const Any& rValue )
+{
+ RecordRefType xLastRec = GetLastRecord();
+ if( xLastRec && xLastRec->InsertValue( nScCol, nScRow, rValue ) )
+ return true;
+ if( GetSize() == SAL_MAX_UINT16 )
+ return false;
+ AppendNewRecord( new XclExpCrn( nScCol, nScRow, rValue ) );
+ return true;
+}
+
+} // namespace
+
+bool XclExpXct::BuildCrnList( XclExpCrnList& rCrnRecs )
+{
+ if( !mxCacheTable )
+ return false;
+
+ /* Get the range of used rows in the cache table. This may help to
+ optimize building the CRN record list if the cache table does not
+ contain all referred cells, e.g. if big empty ranges are used in the
+ formulas. */
+ ::std::pair< SCROW, SCROW > aRowRange = mxCacheTable->getRowRange();
+ if( aRowRange.first >= aRowRange.second )
+ return false;
+
+ /* Crop the bounding range of used cells in this table to Excel limits.
+ Return if there is no external cell inside these limits. */
+ if( !GetAddressConverter().ValidateRange( maBoundRange, false ) )
+ return false;
+
+ /* Find the resulting row range that needs to be processed. */
+ SCROW nScRow1 = ::std::max( aRowRange.first, maBoundRange.aStart.Row() );
+ SCROW nScRow2 = ::std::min( aRowRange.second - 1, maBoundRange.aEnd.Row() );
+ if( nScRow1 > nScRow2 )
+ return false;
+
+ /* Build and collect all CRN records before writing the XCT record. This
+ is needed to determine the total number of CRN records which must be
+ known when writing the XCT record (possibly encrypted, so seeking the
+ output stream back after writing the CRN records is not an option). */
+ SvNumberFormatter& rFormatter = GetFormatter();
+ bool bValid = true;
+ for( SCROW nScRow = nScRow1; bValid && (nScRow <= nScRow2); ++nScRow )
+ {
+ ::std::pair< SCCOL, SCCOL > aColRange = mxCacheTable->getColRange( nScRow );
+ const SCCOL nScEnd = ::std::min( aColRange.second, GetDoc().GetSheetLimits().GetMaxColCount() );
+ for( SCCOL nScCol = aColRange.first; bValid && (nScCol < nScEnd); ++nScCol )
+ {
+ if( maUsedCells.IsCellMarked( nScCol, nScRow, true ) )
+ {
+ sal_uInt32 nScNumFmt = 0;
+ ScExternalRefCache::TokenRef xToken = mxCacheTable->getCell( nScCol, nScRow, &nScNumFmt );
+ using namespace ::formula;
+ if( xToken )
+ switch( xToken->GetType() )
+ {
+ case svDouble:
+ bValid = (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) ?
+ rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() != 0 ) ) :
+ rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetDouble() ) );
+ break;
+ case svString:
+ // do not save empty strings (empty cells) to cache
+ if( !xToken->GetString().isEmpty() )
+ bValid = rCrnRecs.InsertValue( nScCol, nScRow, Any( xToken->GetString().getString() ) );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void XclExpXct::Save( XclExpStream& rStrm )
+{
+ XclExpCrnList aCrnRecs;
+ if (!BuildCrnList( aCrnRecs))
+ return;
+
+ // write the XCT record and the list of CRN records
+ rStrm.StartRecord( EXC_ID_XCT, 4 );
+ rStrm << static_cast< sal_uInt16 >( aCrnRecs.GetSize() ) << mnSBTab;
+ rStrm.EndRecord();
+ aCrnRecs.Save( rStrm );
+}
+
+void XclExpXct::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpCrnList aCrnRecs;
+
+ sax_fastparser::FSHelperPtr pFS = rStrm.GetCurrentStream();
+
+ bool bValid = BuildCrnList( aCrnRecs);
+ pFS->startElement(XML_sheetData, XML_sheetId, OString::number(mnSBTab));
+ if (bValid)
+ {
+ // row elements
+ aCrnRecs.SaveXml( rStrm );
+ }
+ pFS->endElement( XML_sheetData);
+}
+
+// External documents (EXTERNSHEET/SUPBOOK), base class =======================
+
+XclExpExternSheetBase::XclExpExternSheetBase( const XclExpRoot& rRoot, sal_uInt16 nRecId, sal_uInt32 nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpExtNameBuffer& XclExpExternSheetBase::GetExtNameBuffer()
+{
+ if( !mxExtNameBfr )
+ mxExtNameBfr = std::make_shared<XclExpExtNameBuffer>( GetRoot() );
+ return *mxExtNameBfr;
+}
+
+void XclExpExternSheetBase::WriteExtNameBuffer( XclExpStream& rStrm )
+{
+ if( mxExtNameBfr )
+ mxExtNameBfr->Save( rStrm );
+}
+
+void XclExpExternSheetBase::WriteExtNameBufferXml( XclExpXmlStream& rStrm )
+{
+ if( mxExtNameBfr )
+ mxExtNameBfr->SaveXml( rStrm );
+}
+
+// External documents (EXTERNSHEET, BIFF5/BIFF7) ==============================
+
+XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, sal_Unicode cCode ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET )
+{
+ Init( OUStringChar(cCode) );
+}
+
+XclExpExternSheet::XclExpExternSheet( const XclExpRoot& rRoot, std::u16string_view rTabName ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_EXTERNSHEET )
+{
+ // reference to own sheet: \03<sheetname>
+ Init(OUStringConcatenation(OUStringChar(EXC_EXTSH_TABNAME) + rTabName));
+}
+
+void XclExpExternSheet::Save( XclExpStream& rStrm )
+{
+ // EXTERNSHEET record
+ XclExpRecord::Save( rStrm );
+ // EXTERNNAME records
+ WriteExtNameBuffer( rStrm );
+}
+
+void XclExpExternSheet::Init( std::u16string_view rEncUrl )
+{
+ OSL_ENSURE_BIFF( GetBiff() <= EXC_BIFF5 );
+ maTabName.AssignByte( rEncUrl, GetTextEncoding(), XclStrFlags::EightBitLength );
+ SetRecSize( maTabName.GetSize() );
+}
+
+sal_uInt16 XclExpExternSheet::InsertAddIn( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertAddIn( rName );
+}
+
+void XclExpExternSheet::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt8 nNameSize = static_cast< sal_uInt8 >( maTabName.Len() );
+ // special case: reference to own sheet (starting with '\03') needs wrong string length
+ if( maTabName.GetChar( 0 ) == EXC_EXTSH_TABNAME )
+ --nNameSize;
+ rStrm << nNameSize;
+ maTabName.WriteBuffer( rStrm );
+}
+
+// External document (SUPBOOK, BIFF8) =========================================
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, sal_uInt16 nXclTabCount ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ meType( XclSupbookType::Self ),
+ mnXclTabCount( nXclTabCount ),
+ mnFileId( 0 )
+{
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ meType( XclSupbookType::Addin ),
+ mnXclTabCount( 1 ),
+ mnFileId( 0 )
+{
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl, XclSupbookType ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ),
+ maUrl( rUrl ),
+ maUrlEncoded( rUrl ),
+ meType( XclSupbookType::Eurotool ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rUrl ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK ),
+ maUrl( rUrl ),
+ maUrlEncoded( XclExpUrlHelper::EncodeUrl( rRoot, rUrl ) ),
+ meType( XclSupbookType::Extern ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+
+ // We need to create all tables up front to ensure the correct table order.
+ ScExternalRefManager* pRefMgr = rRoot.GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId( rUrl );
+ mnFileId = nFileId + 1;
+ ScfStringVec aTabNames;
+ pRefMgr->getAllCachedTableNames( nFileId, aTabNames );
+ size_t nTabIndex = 0;
+ for( const auto& rTabName : aTabNames )
+ {
+ InsertTabName( rTabName, pRefMgr->getCacheTable( nFileId, nTabIndex ) );
+ ++nTabIndex;
+ }
+}
+
+XclExpSupbook::XclExpSupbook( const XclExpRoot& rRoot, const OUString& rApplic, const OUString& rTopic ) :
+ XclExpExternSheetBase( rRoot, EXC_ID_SUPBOOK, 4 ),
+ maUrl( rApplic ),
+ maDdeTopic( rTopic ),
+ maUrlEncoded( XclExpUrlHelper::EncodeDde( rApplic, rTopic ) ),
+ meType( XclSupbookType::Special ),
+ mnXclTabCount( 0 ),
+ mnFileId( 0 )
+{
+ SetRecSize( 2 + maUrlEncoded.GetSize() );
+}
+
+bool XclExpSupbook::IsUrlLink( std::u16string_view rUrl ) const
+{
+ return (meType == XclSupbookType::Extern || meType == XclSupbookType::Eurotool) && (maUrl == rUrl);
+}
+
+bool XclExpSupbook::IsDdeLink( std::u16string_view rApplic, std::u16string_view rTopic ) const
+{
+ return (meType == XclSupbookType::Special) && (maUrl == rApplic) && (maDdeTopic == rTopic);
+}
+
+void XclExpSupbook::FillRefLogEntry( XclExpRefLogEntry& rRefLogEntry,
+ sal_uInt16 nFirstSBTab, sal_uInt16 nLastSBTab ) const
+{
+ rRefLogEntry.mpUrl = maUrlEncoded.IsEmpty() ? nullptr : &maUrlEncoded;
+ rRefLogEntry.mpFirstTab = GetTabName( nFirstSBTab );
+ rRefLogEntry.mpLastTab = GetTabName( nLastSBTab );
+}
+
+void XclExpSupbook::StoreCellRange( const ScRange& rRange, sal_uInt16 nSBTab )
+{
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCellRange( rRange );
+}
+
+void XclExpSupbook::StoreCell_( sal_uInt16 nSBTab, const ScAddress& rCell )
+{
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCell_( rCell );
+}
+
+void XclExpSupbook::StoreCellRange_( sal_uInt16 nSBTab, const ScRange& rRange )
+{
+ // multi-table range is not allowed!
+ if( rRange.aStart.Tab() == rRange.aEnd.Tab() )
+ if( XclExpXct* pXct = maXctList.GetRecord( nSBTab ) )
+ pXct->StoreCellRange_( rRange );
+}
+
+sal_uInt16 XclExpSupbook::GetTabIndex( const OUString& rTabName ) const
+{
+ XclExpString aXclName(rTabName);
+ size_t nSize = maXctList.GetSize();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ XclExpXctRef aRec = maXctList.GetRecord(i);
+ if (aXclName == aRec->GetTabName())
+ return ulimit_cast<sal_uInt16>(i);
+ }
+ return EXC_NOTAB;
+}
+
+sal_uInt16 XclExpSupbook::GetTabCount() const
+{
+ return ulimit_cast<sal_uInt16>(maXctList.GetSize());
+}
+
+sal_uInt16 XclExpSupbook::InsertTabName( const OUString& rTabName, ScExternalRefCache::TableTypeRef const & xCacheTable )
+{
+ SAL_WARN_IF( meType != XclSupbookType::Extern, "sc.filter", "Don't insert sheet names here" );
+ sal_uInt16 nSBTab = ulimit_cast< sal_uInt16 >( maXctList.GetSize() );
+ XclExpXctRef xXct = new XclExpXct( GetRoot(), rTabName, nSBTab, xCacheTable );
+ AddRecSize( xXct->GetTabName().GetSize() );
+ maXctList.AppendRecord( xXct );
+ return nSBTab;
+}
+
+sal_uInt16 XclExpSupbook::InsertAddIn( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertAddIn( rName );
+}
+
+sal_uInt16 XclExpSupbook::InsertEuroTool( const OUString& rName )
+{
+ return GetExtNameBuffer().InsertEuroTool( rName );
+}
+
+sal_uInt16 XclExpSupbook::InsertDde( const OUString& rItem )
+{
+ return GetExtNameBuffer().InsertDde( maUrl, maDdeTopic, rItem );
+}
+
+sal_uInt16 XclExpSupbook::InsertExtName( const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ return GetExtNameBuffer().InsertExtName(*this, rName, rArray);
+}
+
+XclSupbookType XclExpSupbook::GetType() const
+{
+ return meType;
+}
+
+sal_uInt16 XclExpSupbook::GetFileId() const
+{
+ return mnFileId;
+}
+
+const OUString& XclExpSupbook::GetUrl() const
+{
+ return maUrl;
+}
+
+void XclExpSupbook::Save( XclExpStream& rStrm )
+{
+ // SUPBOOK record
+ XclExpRecord::Save( rStrm );
+ // XCT record, CRN records
+ maXctList.Save( rStrm );
+ // EXTERNNAME records
+ WriteExtNameBuffer( rStrm );
+}
+
+void XclExpSupbook::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.GetCurrentStream();
+
+ // Add relation for this stream, e.g. xl/externalLinks/_rels/externalLink1.xml.rels
+ sal_uInt16 nLevel = 0;
+ bool bRel = true;
+
+ // BuildFileName delete ../ and convert them to nLevel
+ // but addrelation needs ../ instead of nLevel, so we have to convert it back
+ OUString sFile = XclExpHyperlink::BuildFileName(nLevel, bRel, maUrl, GetRoot(), true);
+ while (nLevel-- > 0)
+ sFile = "../" + sFile;
+
+ OUString sId = rStrm.addRelation( pExternalLink->getOutputStream(),
+ oox::getRelationship(Relationship::EXTERNALLINKPATH), sFile, true );
+
+ pExternalLink->startElement( XML_externalLink,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8());
+
+ pExternalLink->startElement( XML_externalBook,
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ FSNS(XML_r, XML_id), sId.toUtf8());
+
+ if (!maXctList.IsEmpty())
+ {
+ pExternalLink->startElement(XML_sheetNames);
+ for (size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos)
+ {
+ pExternalLink->singleElement(XML_sheetName,
+ XML_val, XclXmlUtils::ToOString(maXctList.GetRecord(nPos)->GetTabName()));
+ }
+ pExternalLink->endElement( XML_sheetNames);
+
+ }
+
+ if (mxExtNameBfr)
+ {
+ pExternalLink->startElement(XML_definedNames);
+ // externalName elements
+ WriteExtNameBufferXml( rStrm );
+ pExternalLink->endElement(XML_definedNames);
+ }
+
+ if (!maXctList.IsEmpty())
+ {
+ pExternalLink->startElement(XML_sheetDataSet);
+
+ // sheetData elements
+ maXctList.SaveXml( rStrm );
+
+ pExternalLink->endElement( XML_sheetDataSet);
+
+ }
+ pExternalLink->endElement( XML_externalBook);
+ pExternalLink->endElement( XML_externalLink);
+}
+
+const XclExpString* XclExpSupbook::GetTabName( sal_uInt16 nSBTab ) const
+{
+ XclExpXctRef xXct = maXctList.GetRecord( nSBTab );
+ return xXct ? &xXct->GetTabName() : nullptr;
+}
+
+void XclExpSupbook::WriteBody( XclExpStream& rStrm )
+{
+ switch( meType )
+ {
+ case XclSupbookType::Self:
+ rStrm << mnXclTabCount << EXC_SUPB_SELF;
+ break;
+ case XclSupbookType::Extern:
+ case XclSupbookType::Special:
+ case XclSupbookType::Eurotool:
+ {
+ sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXctList.GetSize() );
+ rStrm << nCount << maUrlEncoded;
+
+ for( size_t nPos = 0, nSize = maXctList.GetSize(); nPos < nSize; ++nPos )
+ rStrm << maXctList.GetRecord( nPos )->GetTabName();
+ }
+ break;
+ case XclSupbookType::Addin:
+ rStrm << mnXclTabCount << EXC_SUPB_ADDIN;
+ break;
+ default:
+ SAL_WARN( "sc.filter", "Unhandled SUPBOOK type " << meType);
+ }
+}
+
+// All SUPBOOKS in a document =================================================
+
+XclExpSupbookBuffer::XclExpSupbookBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnOwnDocSB( SAL_MAX_UINT16 ),
+ mnAddInSB( SAL_MAX_UINT16 )
+{
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ sal_uInt16 nXclCnt = rTabInfo.GetXclTabCount();
+ sal_uInt16 nCodeCnt = static_cast< sal_uInt16 >( GetExtDocOptions().GetCodeNameCount() );
+ size_t nCount = nXclCnt + rTabInfo.GetXclExtTabCount();
+
+ OSL_ENSURE( nCount > 0, "XclExpSupbookBuffer::XclExpSupbookBuffer - no sheets to export" );
+ if( nCount )
+ {
+ maSBIndexVec.resize( nCount );
+
+ // self-ref SUPBOOK first of list
+ XclExpSupbookRef xSupbook = new XclExpSupbook( GetRoot(), ::std::max( nXclCnt, nCodeCnt ) );
+ mnOwnDocSB = Append( xSupbook );
+ for( sal_uInt16 nXclTab = 0; nXclTab < nXclCnt; ++nXclTab )
+ maSBIndexVec[ nXclTab ].Set( mnOwnDocSB, nXclTab );
+ }
+}
+
+XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFirstXclTab, sal_uInt16 nLastXclTab,
+ XclExpRefLogEntry* pRefLogEntry ) const
+{
+ XclExpXti aXti;
+ size_t nSize = maSBIndexVec.size();
+ if( (nFirstXclTab < nSize) && (nLastXclTab < nSize) )
+ {
+ // index of the SUPBOOK record
+ aXti.mnSupbook = maSBIndexVec[ nFirstXclTab ].mnSupbook;
+
+ // all sheets in the same supbook?
+ bool bSameSB = true;
+ for( sal_uInt16 nXclTab = nFirstXclTab + 1; bSameSB && (nXclTab <= nLastXclTab); ++nXclTab )
+ {
+ bSameSB = maSBIndexVec[ nXclTab ].mnSupbook == aXti.mnSupbook;
+ if( !bSameSB )
+ nLastXclTab = nXclTab - 1;
+ }
+ aXti.mnFirstSBTab = maSBIndexVec[ nFirstXclTab ].mnSBTab;
+ aXti.mnLastSBTab = maSBIndexVec[ nLastXclTab ].mnSBTab;
+
+ // fill external reference log entry (for change tracking)
+ if( pRefLogEntry )
+ {
+ pRefLogEntry->mnFirstXclTab = nFirstXclTab;
+ pRefLogEntry->mnLastXclTab = nLastXclTab;
+ XclExpSupbookRef xSupbook = maSupbookList.GetRecord( aXti.mnSupbook );
+ if( xSupbook )
+ xSupbook->FillRefLogEntry( *pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab );
+ }
+ }
+ else
+ {
+ // special range, i.e. for deleted sheets or add-ins
+ aXti.mnSupbook = mnOwnDocSB;
+ aXti.mnFirstSBTab = nFirstXclTab;
+ aXti.mnLastSBTab = nLastXclTab;
+ }
+
+ return aXti;
+}
+
+void XclExpSupbookBuffer::StoreCellRange( const ScRange& rRange )
+{
+ sal_uInt16 nXclTab = GetTabInfo().GetXclTab( rRange.aStart.Tab() );
+ if( nXclTab < maSBIndexVec.size() )
+ {
+ const XclExpSBIndex& rSBIndex = maSBIndexVec[ nXclTab ];
+ XclExpSupbookRef xSupbook = maSupbookList.GetRecord( rSBIndex.mnSupbook );
+ OSL_ENSURE( xSupbook , "XclExpSupbookBuffer::StoreCellRange - missing SUPBOOK record" );
+ if( xSupbook )
+ xSupbook->StoreCellRange( rRange, rSBIndex.mnSBTab );
+ }
+}
+
+namespace {
+
+class FindSBIndexEntry
+{
+public:
+ explicit FindSBIndexEntry(sal_uInt16 nSupbookId, sal_uInt16 nTabId) :
+ mnSupbookId(nSupbookId), mnTabId(nTabId) {}
+
+ bool operator()(const XclExpSupbookBuffer::XclExpSBIndex& r) const
+ {
+ return mnSupbookId == r.mnSupbook && mnTabId == r.mnSBTab;
+ }
+
+private:
+ sal_uInt16 mnSupbookId;
+ sal_uInt16 mnTabId;
+};
+
+}
+
+void XclExpSupbookBuffer::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rCell )
+{
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+
+ sal_uInt16 nSheetId = xSupbook->GetTabIndex(rTabName);
+ if (nSheetId == EXC_NOTAB)
+ // specified table name not found in this SUPBOOK.
+ return;
+
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+
+ xSupbook->StoreCell_(nSheetId, rCell);
+}
+
+void XclExpSupbookBuffer::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+
+ SCTAB nTabCount = rRange.aEnd.Tab() - rRange.aStart.Tab() + 1;
+
+ // If this is a multi-table range, get token for each table.
+ using namespace ::formula;
+ SCTAB aMatrixListSize = 0;
+
+ // This is a new'ed instance, so we must manage its life cycle here.
+ ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(nFileId, rTabName, rRange, nullptr);
+ if (!pArray)
+ return;
+
+ FormulaTokenArrayPlainIterator aIter(*pArray);
+ for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ if (p->GetType() == svMatrix)
+ ++aMatrixListSize;
+ else if (p->GetOpCode() != ocSep)
+ {
+ // This is supposed to be ocSep!!!
+ return;
+ }
+ }
+
+ if (aMatrixListSize != nTabCount)
+ {
+ // matrix size mismatch!
+ return;
+ }
+
+ sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName);
+
+ ScRange aRange(rRange);
+ aRange.aStart.SetTab(0);
+ aRange.aEnd.SetTab(0);
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ sal_uInt16 nSheetId = nFirstSheetId + static_cast<sal_uInt16>(nTab);
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+
+ xSupbook->StoreCellRange_(nSheetId, aRange);
+ }
+}
+
+bool XclExpSupbookBuffer::InsertAddIn(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpSupbookRef xSupbook;
+ if( mnAddInSB == SAL_MAX_UINT16 )
+ {
+ xSupbook = new XclExpSupbook( GetRoot() );
+ mnAddInSB = Append( xSupbook );
+ }
+ else
+ xSupbook = maSupbookList.GetRecord( mnAddInSB );
+ OSL_ENSURE( xSupbook, "XclExpSupbookBuffer::InsertAddin - missing add-in supbook" );
+ rnSupbook = mnAddInSB;
+ rnExtName = xSupbook->InsertAddIn( rName );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertEuroTool(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpSupbookRef xSupbook;
+ OUString aUrl( "\001\010EUROTOOL.XLA" );
+ if( !GetSupbookUrl( xSupbook, rnSupbook, aUrl ) )
+ {
+ xSupbook = new XclExpSupbook( GetRoot(), aUrl, XclSupbookType::Eurotool );
+ rnSupbook = Append( xSupbook );
+ }
+ rnExtName = xSupbook->InsertEuroTool( rName );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertDde(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ XclExpSupbookRef xSupbook;
+ if( !GetSupbookDde( xSupbook, rnSupbook, rApplic, rTopic ) )
+ {
+ xSupbook = new XclExpSupbook( GetRoot(), rApplic, rTopic );
+ rnSupbook = Append( xSupbook );
+ }
+ rnExtName = xSupbook->InsertDde( rItem );
+ return rnExtName > 0;
+}
+
+bool XclExpSupbookBuffer::InsertExtName(
+ sal_uInt16& rnSupbook, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ XclExpSupbookRef xSupbook;
+ if (!GetSupbookUrl(xSupbook, rnSupbook, rUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), rUrl);
+ rnSupbook = Append(xSupbook);
+ }
+ rnExtName = xSupbook->InsertExtName(rName, rArray);
+ return rnExtName > 0;
+}
+
+XclExpXti XclExpSupbookBuffer::GetXti( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpXti aXti(0, EXC_NOTAB, EXC_NOTAB);
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ return aXti;
+
+ XclExpSupbookRef xSupbook;
+ sal_uInt16 nSupbookId;
+ if (!GetSupbookUrl(xSupbook, nSupbookId, *pUrl))
+ {
+ xSupbook = new XclExpSupbook(GetRoot(), *pUrl);
+ nSupbookId = Append(xSupbook);
+ }
+ aXti.mnSupbook = nSupbookId;
+
+ sal_uInt16 nFirstSheetId = xSupbook->GetTabIndex(rTabName);
+ if (nFirstSheetId == EXC_NOTAB)
+ {
+ // first sheet not found in SUPBOOK.
+ return aXti;
+ }
+ sal_uInt16 nSheetCount = xSupbook->GetTabCount();
+ for (sal_uInt16 i = 0; i < nXclTabSpan; ++i)
+ {
+ sal_uInt16 nSheetId = nFirstSheetId + i;
+ if (nSheetId >= nSheetCount)
+ return aXti;
+
+ FindSBIndexEntry f(nSupbookId, nSheetId);
+ if (::std::none_of(maSBIndexVec.begin(), maSBIndexVec.end(), f))
+ {
+ maSBIndexVec.emplace_back();
+ XclExpSBIndex& r = maSBIndexVec.back();
+ r.mnSupbook = nSupbookId;
+ r.mnSBTab = nSheetId;
+ }
+ if (i == 0)
+ aXti.mnFirstSBTab = nSheetId;
+ if (i == nXclTabSpan - 1)
+ aXti.mnLastSBTab = nSheetId;
+ }
+
+ if (pRefLogEntry)
+ {
+ pRefLogEntry->mnFirstXclTab = 0;
+ pRefLogEntry->mnLastXclTab = 0;
+ if (xSupbook)
+ xSupbook->FillRefLogEntry(*pRefLogEntry, aXti.mnFirstSBTab, aXti.mnLastSBTab);
+ }
+
+ return aXti;
+}
+
+void XclExpSupbookBuffer::Save( XclExpStream& rStrm )
+{
+ maSupbookList.Save( rStrm );
+}
+
+void XclExpSupbookBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ // Unused external references are not saved, only kept in memory.
+ // Those that are saved must be indexed from 1, so indexes must be reordered
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ vector<sal_uInt16> aExternFileIds;
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ XclExpSupbookRef xRef(maSupbookList.GetRecord(nPos));
+ // fileIDs are indexed from 1 in xlsx, and from 0 in ScExternalRefManager
+ // converting between them require a -1 or +1
+ if (xRef->GetType() == XclSupbookType::Extern)
+ aExternFileIds.push_back(xRef->GetFileId() - 1);
+ }
+ if (aExternFileIds.size() > 0)
+ pRefMgr->setSkipUnusedFileIds(aExternFileIds);
+
+ ::std::map< sal_uInt16, OUString > aMap;
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ XclExpSupbookRef xRef( maSupbookList.GetRecord( nPos));
+ if (xRef->GetType() != XclSupbookType::Extern)
+ continue; // handle only external reference (for now?)
+
+ sal_uInt16 nId = xRef->GetFileId();
+ sal_uInt16 nUsedId = pRefMgr->convertFileIdToUsedFileId(nId - 1) + 1;
+ const OUString& rUrl = xRef->GetUrl();
+ ::std::pair< ::std::map< sal_uInt16, OUString >::iterator, bool > aInsert(
+ aMap.insert( ::std::make_pair( nId, rUrl)));
+ if (!aInsert.second)
+ {
+ SAL_WARN( "sc.filter", "XclExpSupbookBuffer::SaveXml: file ID already used: " << nId <<
+ " wanted for " << rUrl << " and is " << (*aInsert.first).second <<
+ (rUrl == (*aInsert.first).second ? " multiple Supbook not supported" : ""));
+ continue;
+ }
+ OUString sId;
+ sax_fastparser::FSHelperPtr pExternalLink = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "externalLinks/externalLink", nUsedId),
+ XclXmlUtils::GetStreamName( nullptr, "externalLinks/externalLink", nUsedId),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("externalLink"),
+ &sId );
+
+ // externalReference entry in workbook externalReferences
+ rStrm.GetCurrentStream()->singleElement( XML_externalReference,
+ FSNS(XML_r, XML_id), sId.toUtf8() );
+
+ // Each externalBook in a separate stream.
+ rStrm.PushStream( pExternalLink );
+ xRef->SaveXml( rStrm );
+ rStrm.PopStream();
+ }
+}
+
+bool XclExpSupbookBuffer::HasExternalReferences() const
+{
+ for (size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos)
+ {
+ if (maSupbookList.GetRecord( nPos)->GetType() == XclSupbookType::Extern)
+ return true;
+ }
+ return false;
+}
+
+bool XclExpSupbookBuffer::GetSupbookUrl(
+ XclExpSupbookRef& rxSupbook, sal_uInt16& rnIndex, std::u16string_view rUrl ) const
+{
+ for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos )
+ {
+ rxSupbook = maSupbookList.GetRecord( nPos );
+ if( rxSupbook->IsUrlLink( rUrl ) )
+ {
+ rnIndex = ulimit_cast< sal_uInt16 >( nPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+bool XclExpSupbookBuffer::GetSupbookDde( XclExpSupbookRef& rxSupbook,
+ sal_uInt16& rnIndex, std::u16string_view rApplic, std::u16string_view rTopic ) const
+{
+ for( size_t nPos = 0, nSize = maSupbookList.GetSize(); nPos < nSize; ++nPos )
+ {
+ rxSupbook = maSupbookList.GetRecord( nPos );
+ if( rxSupbook->IsDdeLink( rApplic, rTopic ) )
+ {
+ rnIndex = ulimit_cast< sal_uInt16 >( nPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+sal_uInt16 XclExpSupbookBuffer::Append( XclExpSupbookRef const & xSupbook )
+{
+ maSupbookList.AppendRecord( xSupbook );
+ return ulimit_cast< sal_uInt16 >( maSupbookList.GetSize() - 1 );
+}
+
+// Export link manager ========================================================
+
+XclExpLinkManagerImpl::XclExpLinkManagerImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpLinkManagerImpl5::XclExpLinkManagerImpl5( const XclExpRoot& rRoot ) :
+ XclExpLinkManagerImpl( rRoot )
+{
+}
+
+void XclExpLinkManagerImpl5::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ FindInternal( rnExtSheet, rnFirstXclTab, nFirstScTab );
+ if( (rnFirstXclTab == EXC_TAB_DELETED) || (nFirstScTab == nLastScTab) )
+ {
+ rnLastXclTab = rnFirstXclTab;
+ }
+ else
+ {
+ sal_uInt16 nDummyExtSheet;
+ FindInternal( nDummyExtSheet, rnLastXclTab, nLastScTab );
+ }
+
+ OSL_ENSURE( !pRefLogEntry, "XclExpLinkManagerImpl5::FindExtSheet - fill reflog entry not implemented" );
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::FindExtSheet( sal_Unicode cCode )
+{
+ sal_uInt16 nExtSheet;
+ FindInternal( nExtSheet, cCode );
+ return nExtSheet;
+}
+
+void XclExpLinkManagerImpl5::FindExtSheet(
+ sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, sal_uInt16 /*nXclTabSpan*/,
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnFirstSBTab*/, sal_uInt16& /*rnLastSBTab*/,
+ XclExpRefLogEntry* /*pRefLogEntry*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCellRange( const ScSingleRefData& /*rRef1*/, const ScSingleRefData& /*rRef2*/, const ScAddress& /*rPos*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCell( sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, const ScAddress& /*rPos*/ )
+{
+ // not implemented
+}
+
+void XclExpLinkManagerImpl5::StoreCellRange( sal_uInt16 /*nFileId*/, const OUString& /*rTabName*/, const ScRange& /*rRange*/ )
+{
+ // not implemented
+}
+
+bool XclExpLinkManagerImpl5::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ XclExpExtSheetRef xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_ADDIN );
+ if( xExtSheet )
+ {
+ rnExtName = xExtSheet->InsertAddIn( rName );
+ return rnExtName > 0;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertEuroTool(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, const OUString& /*rName*/ )
+{
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertDde(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/,
+ const OUString& /*rApplic*/, const OUString& /*rTopic*/, const OUString& /*rItem*/ )
+{
+ // not implemented
+ return false;
+}
+
+bool XclExpLinkManagerImpl5::InsertExtName(
+ sal_uInt16& /*rnExtSheet*/, sal_uInt16& /*rnExtName*/, const OUString& /*rUrl*/,
+ const OUString& /*rName*/, const ScExternalRefCache::TokenArrayRef& /*rArray*/ )
+{
+ // not implemented
+ return false;
+}
+
+void XclExpLinkManagerImpl5::Save( XclExpStream& rStrm )
+{
+ if( sal_uInt16 nExtSheetCount = GetExtSheetCount() )
+ {
+ // EXTERNCOUNT record
+ XclExpUInt16Record( EXC_ID_EXTERNCOUNT, nExtSheetCount ).Save( rStrm );
+ // list of EXTERNSHEET records with EXTERNNAME, XCT, CRN records
+ maExtSheetList.Save( rStrm );
+ }
+}
+
+void XclExpLinkManagerImpl5::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+ // not applicable
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::GetExtSheetCount() const
+{
+ return static_cast< sal_uInt16 >( maExtSheetList.GetSize() );
+}
+
+sal_uInt16 XclExpLinkManagerImpl5::AppendInternal( XclExpExtSheetRef const & xExtSheet )
+{
+ if( GetExtSheetCount() < 0x7FFF )
+ {
+ maExtSheetList.AppendRecord( xExtSheet );
+ // return negated one-based EXTERNSHEET index (i.e. 0xFFFD for 3rd record)
+ return static_cast< sal_uInt16 >( -GetExtSheetCount() );
+ }
+ return 0;
+}
+
+void XclExpLinkManagerImpl5::CreateInternal()
+{
+ if( !maIntTabMap.empty() )
+ return;
+
+ // create EXTERNSHEET records for all internal exported sheets
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ for( SCTAB nScTab = 0, nScCnt = rTabInfo.GetScTabCount(); nScTab < nScCnt; ++nScTab )
+ {
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ XclExpExtSheetRef xRec;
+ if( nScTab == GetCurrScTab() )
+ xRec = new XclExpExternSheet( GetRoot(), EXC_EXTSH_OWNTAB );
+ else
+ xRec = new XclExpExternSheet( GetRoot(), rTabInfo.GetScTabName( nScTab ) );
+ maIntTabMap[ nScTab ] = AppendInternal( xRec );
+ }
+ }
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::GetInternal( sal_uInt16 nExtSheet )
+{
+ return maExtSheetList.GetRecord( static_cast< sal_uInt16 >( -nExtSheet - 1 ) );
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab, SCTAB nScTab )
+{
+ // create internal EXTERNSHEET records on demand
+ CreateInternal();
+
+ // try to find an EXTERNSHEET record - if not, return a "deleted sheet" reference
+ XclExpExtSheetRef xExtSheet;
+ XclExpIntTabMap::const_iterator aIt = maIntTabMap.find( nScTab );
+ if( aIt == maIntTabMap.end() )
+ {
+ xExtSheet = FindInternal( rnExtSheet, EXC_EXTSH_OWNDOC );
+ rnXclTab = EXC_TAB_DELETED;
+ }
+ else
+ {
+ rnExtSheet = aIt->second;
+ xExtSheet = GetInternal( rnExtSheet );
+ rnXclTab = GetTabInfo().GetXclTab( nScTab );
+ }
+ return xExtSheet;
+}
+
+XclExpLinkManagerImpl5::XclExpExtSheetRef XclExpLinkManagerImpl5::FindInternal(
+ sal_uInt16& rnExtSheet, sal_Unicode cCode )
+{
+ XclExpExtSheetRef xExtSheet;
+ XclExpCodeMap::const_iterator aIt = maCodeMap.find( cCode );
+ if( aIt == maCodeMap.end() )
+ {
+ xExtSheet = new XclExpExternSheet( GetRoot(), cCode );
+ rnExtSheet = maCodeMap[ cCode ] = AppendInternal( xExtSheet );
+ }
+ else
+ {
+ rnExtSheet = aIt->second;
+ xExtSheet = GetInternal( rnExtSheet );
+ }
+ return xExtSheet;
+}
+
+XclExpLinkManagerImpl8::XclExpLinkManagerImpl8( const XclExpRoot& rRoot ) :
+ XclExpLinkManagerImpl( rRoot ),
+ maSBBuffer( rRoot )
+{
+}
+
+void XclExpLinkManagerImpl8::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+ rnFirstXclTab = rTabInfo.GetXclTab( nFirstScTab );
+ rnLastXclTab = rTabInfo.GetXclTab( nLastScTab );
+ rnExtSheet = InsertXti( maSBBuffer.GetXti( rnFirstXclTab, rnLastXclTab, pRefLogEntry ) );
+}
+
+sal_uInt16 XclExpLinkManagerImpl8::FindExtSheet( sal_Unicode cCode )
+{
+ OSL_ENSURE( (cCode == EXC_EXTSH_OWNDOC) || (cCode == EXC_EXTSH_ADDIN),
+ "XclExpLinkManagerImpl8::FindExtSheet - unknown externsheet code" );
+ return InsertXti( maSBBuffer.GetXti( EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+}
+
+void XclExpLinkManagerImpl8::FindExtSheet(
+ sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ XclExpXti aXti = maSBBuffer.GetXti(nFileId, rTabName, nXclTabSpan, pRefLogEntry);
+ rnExtSheet = InsertXti(aXti);
+ rnFirstSBTab = aXti.mnFirstSBTab;
+ rnLastSBTab = aXti.mnLastSBTab;
+}
+
+void XclExpLinkManagerImpl8::StoreCellRange( const ScSingleRefData& rRef1, const ScSingleRefData& rRef2, const ScAddress& rPos )
+{
+ ScAddress aAbs1 = rRef1.toAbs(GetRoot().GetDoc(), rPos);
+ ScAddress aAbs2 = rRef2.toAbs(GetRoot().GetDoc(), rPos);
+ if (!(!rRef1.IsDeleted() && !rRef2.IsDeleted() && (aAbs1.Tab() >= 0) && (aAbs2.Tab() >= 0)))
+ return;
+
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ SCTAB nFirstScTab = aAbs1.Tab();
+ SCTAB nLastScTab = aAbs2.Tab();
+ ScRange aRange(aAbs1.Col(), aAbs1.Row(), 0, aAbs2.Col(), aAbs2.Row(), 0);
+ for (SCTAB nScTab = nFirstScTab; nScTab <= nLastScTab; ++nScTab)
+ {
+ if( rTabInfo.IsExternalTab( nScTab ) )
+ {
+ aRange.aStart.SetTab( nScTab );
+ aRange.aEnd.SetTab( nScTab );
+ maSBBuffer.StoreCellRange( aRange );
+ }
+ }
+}
+
+void XclExpLinkManagerImpl8::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos )
+{
+ maSBBuffer.StoreCell(nFileId, rTabName, rPos);
+}
+
+void XclExpLinkManagerImpl8::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ maSBBuffer.StoreCellRange(nFileId, rTabName, rRange);
+}
+
+bool XclExpLinkManagerImpl8::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertAddIn( nSupbook, rnExtName, rName ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertEuroTool( nSupbook, rnExtName, rName ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertDde( nSupbook, rnExtName, rApplic, rTopic, rItem ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+bool XclExpLinkManagerImpl8::InsertExtName( sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rUrl, const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ sal_uInt16 nSupbook;
+ if( maSBBuffer.InsertExtName( nSupbook, rnExtName, rUrl, rName, rArray ) )
+ {
+ rnExtSheet = InsertXti( XclExpXti( nSupbook, EXC_TAB_EXTERNAL, EXC_TAB_EXTERNAL ) );
+ return true;
+ }
+ return false;
+}
+
+void XclExpLinkManagerImpl8::Save( XclExpStream& rStrm )
+{
+ if( maXtiVec.empty() )
+ return;
+
+ // SUPBOOKs, XCTs, CRNs, EXTERNNAMEs
+ maSBBuffer.Save( rStrm );
+
+ // EXTERNSHEET
+ sal_uInt16 nCount = ulimit_cast< sal_uInt16 >( maXtiVec.size() );
+ rStrm.StartRecord( EXC_ID_EXTERNSHEET, 2 + 6 * nCount );
+ rStrm << nCount;
+ rStrm.SetSliceSize( 6 );
+ for( const auto& rXti : maXtiVec )
+ rXti.Save( rStrm );
+ rStrm.EndRecord();
+}
+
+void XclExpLinkManagerImpl8::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (maSBBuffer.HasExternalReferences())
+ {
+ sax_fastparser::FSHelperPtr pWorkbook = rStrm.GetCurrentStream();
+ pWorkbook->startElement(XML_externalReferences);
+
+ // externalLink, externalBook, sheetNames, sheetDataSet, externalName
+ maSBBuffer.SaveXml( rStrm );
+
+ pWorkbook->endElement( XML_externalReferences);
+ }
+
+ // TODO: equivalent for EXTERNSHEET in OOXML?
+#if 0
+ if( !maXtiVec.empty() )
+ {
+ for( const auto& rXti : maXtiVec )
+ rXti.SaveXml( rStrm );
+ }
+#endif
+}
+
+sal_uInt16 XclExpLinkManagerImpl8::InsertXti( const XclExpXti& rXti )
+{
+ auto aIt = std::find(maXtiVec.begin(), maXtiVec.end(), rXti);
+ if (aIt != maXtiVec.end())
+ return ulimit_cast< sal_uInt16 >( std::distance(maXtiVec.begin(), aIt) );
+ maXtiVec.push_back( rXti );
+ return ulimit_cast< sal_uInt16 >( maXtiVec.size() - 1 );
+}
+
+XclExpLinkManager::XclExpLinkManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ mxImpl = std::make_shared<XclExpLinkManagerImpl5>( rRoot );
+ break;
+ case EXC_BIFF8:
+ mxImpl = std::make_shared<XclExpLinkManagerImpl8>( rRoot );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+XclExpLinkManager::~XclExpLinkManager()
+{
+}
+
+void XclExpLinkManager::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnXclTab,
+ SCTAB nScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( rnExtSheet, rnXclTab, rnXclTab, nScTab, nScTab, pRefLogEntry );
+}
+
+void XclExpLinkManager::FindExtSheet(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab, XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( rnExtSheet, rnFirstXclTab, rnLastXclTab, nFirstScTab, nLastScTab, pRefLogEntry );
+}
+
+sal_uInt16 XclExpLinkManager::FindExtSheet( sal_Unicode cCode )
+{
+ return mxImpl->FindExtSheet( cCode );
+}
+
+void XclExpLinkManager::FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry )
+{
+ mxImpl->FindExtSheet( nFileId, rTabName, nXclTabSpan, rnExtSheet, rnFirstSBTab, rnLastSBTab, pRefLogEntry );
+}
+
+void XclExpLinkManager::StoreCell( const ScSingleRefData& rRef, const ScAddress& rPos )
+{
+ mxImpl->StoreCellRange(rRef, rRef, rPos);
+}
+
+void XclExpLinkManager::StoreCellRange( const ScComplexRefData& rRef, const ScAddress& rPos )
+{
+ mxImpl->StoreCellRange(rRef.Ref1, rRef.Ref2, rPos);
+}
+
+void XclExpLinkManager::StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos )
+{
+ mxImpl->StoreCell(nFileId, rTabName, rPos);
+}
+
+void XclExpLinkManager::StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange )
+{
+ mxImpl->StoreCellRange(nFileId, rTabName, rRange);
+}
+
+bool XclExpLinkManager::InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ return mxImpl->InsertAddIn( rnExtSheet, rnExtName, rName );
+}
+
+bool XclExpLinkManager::InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rName )
+{
+ return mxImpl->InsertEuroTool( rnExtSheet, rnExtName, rName );
+}
+
+bool XclExpLinkManager::InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem )
+{
+ return mxImpl->InsertDde( rnExtSheet, rnExtName, rApplic, rTopic, rItem );
+}
+
+bool XclExpLinkManager::InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl, const OUString& rName,
+ const ScExternalRefCache::TokenArrayRef& rArray )
+{
+ return mxImpl->InsertExtName(rnExtSheet, rnExtName, rUrl, rName, rArray);
+}
+
+void XclExpLinkManager::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpLinkManager::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xename.cxx b/sc/source/filter/excel/xename.cxx
new file mode 100644
index 000000000..c8fd0ed37
--- /dev/null
+++ b/sc/source/filter/excel/xename.cxx
@@ -0,0 +1,870 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xename.hxx>
+
+#include <map>
+
+#include <document.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <excrecds.hxx>
+#include <xlname.hxx>
+#include <xeformula.hxx>
+#include <xestring.hxx>
+#include <xltools.hxx>
+
+#include <formula/grammar.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace ::oox;
+
+// *** Helper classes ***
+
+namespace {
+
+/** Represents an internal defined name, supports writing it to a NAME record. */
+class XclExpName : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Creates a standard defined name. */
+ explicit XclExpName( const XclExpRoot& rRoot, const OUString& rName );
+ /** Creates a built-in defined name. */
+ explicit XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn );
+
+ /** Sets a token array containing the definition of this name. */
+ void SetTokenArray( const XclTokenArrayRef& xTokArr );
+ /** Changes this defined name to be local on the specified Calc sheet. */
+ void SetLocalTab( SCTAB nScTab );
+ /** Hides or unhides the defined name. */
+ void SetHidden( bool bHidden = true );
+ /** Changes this name to be the call to a VB macro function or procedure.
+ @param bVBasic true = Visual Basic macro, false = Sheet macro.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ void SetMacroCall( bool bVBasic, bool bFunc );
+
+ /** Sets the name's symbol value
+ @param sValue the name's symbolic value */
+ void SetSymbol( const OUString& rValue );
+
+ /** Returns the original name (title) of this defined name. */
+ const OUString& GetOrigName() const { return maOrigName; }
+ /** Returns the Excel built-in name index of this defined name.
+ @return The built-in name index or EXC_BUILTIN_UNKNOWN for user-defined names. */
+ sal_Unicode GetBuiltInName() const { return mcBuiltIn; }
+
+ /** Returns the symbol value for this defined name. */
+ const OUString& GetSymbol() const { return msSymbol; }
+
+ /** Returns true, if this is a document-global defined name. */
+ bool IsGlobal() const { return mnXclTab == EXC_NAME_GLOBAL; }
+ /** Returns the Calc sheet of a local defined name. */
+ SCTAB GetScTab() const { return mnScTab; }
+
+ /** Returns true, if this defined name is volatile. */
+ bool IsVolatile() const;
+ /** Returns true, if this defined name describes a macro call.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ bool IsMacroCall( bool bVBasic, bool bFunc ) const;
+
+ /** Writes the entire NAME record to the passed stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the NAME record to the passed stream. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ /** Convert localized range separators */
+ OUString GetWithDefaultRangeSeparator( const OUString& rSymbol ) const;
+
+private:
+ OUString maOrigName; /// The original user-defined name.
+ OUString msSymbol; /// The value of the symbol
+ XclExpStringRef mxName; /// The name as Excel string object.
+ XclTokenArrayRef mxTokArr; /// The definition of the defined name.
+ sal_Unicode mcBuiltIn; /// The built-in index for built-in names.
+ SCTAB mnScTab; /// The Calc sheet index for local names.
+ sal_uInt16 mnFlags; /// Additional flags for this defined name.
+ sal_uInt16 mnExtSheet; /// The 1-based index to a global EXTERNSHEET record.
+ sal_uInt16 mnXclTab; /// The 1-based Excel sheet index for local names.
+};
+
+}
+
+/** Implementation class of the name manager. */
+class XclExpNameManagerImpl : protected XclExpRoot
+{
+public:
+ explicit XclExpNameManagerImpl( const XclExpRoot& rRoot );
+
+ /** Creates NAME records for built-in and user defined names. */
+ void Initialize();
+
+ /** Inserts the Calc name with the passed index and returns the Excel NAME index. */
+ sal_uInt16 InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab );
+
+ /** Inserts a new built-in defined name. */
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, SCTAB nScTab, const ScRangeList& aRangeList );
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, const ScRange& aRange );
+ /** Inserts a new defined name. Sets another unused name, if rName already exists. */
+ sal_uInt16 InsertUniqueName( const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab );
+ /** Returns index of an existing name, or creates a name without definition. */
+ sal_uInt16 InsertRawName( const OUString& rName );
+ /** Searches or inserts a defined name describing a macro name.
+ @param bVBasic true = Visual Basic macro; false = Sheet macro.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ sal_uInt16 InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden );
+
+ /** Returns the NAME record at the specified position or 0 on error. */
+ const XclExpName* GetName( sal_uInt16 nNameIdx ) const;
+
+ /** Writes the entire list of NAME records.
+ @descr In BIFF7 and lower, writes the entire global link table, which
+ consists of an EXTERNCOUNT record, several EXTERNSHEET records, and
+ the list of NAME records. */
+ void Save( XclExpStream& rStrm );
+
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ typedef XclExpRecordList< XclExpName > XclExpNameList;
+ typedef XclExpNameList::RecordRefType XclExpNameRef;
+
+ typedef ::std::map< ::std::pair<SCTAB, OUString>, sal_uInt16> NamedExpMap;
+
+private:
+ /**
+ * @param nTab 0-based table index, or SCTAB_GLOBAL for global names.
+ * @param nScIdx calc's name index.
+ *
+ * @return excel's name index.
+ */
+ sal_uInt16 FindNamedExp( SCTAB nTab, OUString sName );
+
+ /** Returns the index of an existing built-in NAME record with the passed definition, otherwise 0. */
+ sal_uInt16 FindBuiltInNameIdx( const OUString& rName,
+ const OUString& sSymbol ) const;
+ /** Returns an unused name for the passed name. */
+ OUString GetUnusedName( const OUString& rName ) const;
+
+ /** Appends a new NAME record to the record list.
+ @return The 1-based NAME record index used elsewhere in the Excel file. */
+ sal_uInt16 Append( XclExpName* pName );
+ sal_uInt16 Append( XclExpNameRef const & rxName ) { return Append(rxName.get()); }
+ /** Creates a new NAME record for the passed user-defined name.
+ @return The 1-based NAME record index used elsewhere in the Excel file. */
+ sal_uInt16 CreateName( SCTAB nTab, const ScRangeData& rRangeData );
+
+ /** Creates NAME records for all built-in names in the document. */
+ void CreateBuiltInNames();
+ /** Creates NAME records for all user-defined names in the document. */
+ void CreateUserNames();
+
+private:
+ /**
+ * Maps Calc's named range to Excel's NAME records. Global names use
+ * -1 as their table index, whereas sheet-local names have 0-based table
+ * index.
+ */
+ NamedExpMap maNamedExpMap;
+ XclExpNameList maNameList; /// List of NAME records.
+ size_t mnFirstUserIdx; /// List index of first user-defined NAME record.
+};
+
+// *** Implementation ***
+
+XclExpName::XclExpName( const XclExpRoot& rRoot, const OUString& rName ) :
+ XclExpRecord( EXC_ID_NAME ),
+ XclExpRoot( rRoot ),
+ maOrigName( rName ),
+ mxName( XclExpStringHelper::CreateString( rRoot, rName, XclStrFlags::EightBitLength ) ),
+ mcBuiltIn( EXC_BUILTIN_UNKNOWN ),
+ mnScTab( SCTAB_GLOBAL ),
+ mnFlags( EXC_NAME_DEFAULT ),
+ mnExtSheet( EXC_NAME_GLOBAL ),
+ mnXclTab( EXC_NAME_GLOBAL )
+{
+}
+
+XclExpName::XclExpName( const XclExpRoot& rRoot, sal_Unicode cBuiltIn ) :
+ XclExpRecord( EXC_ID_NAME ),
+ XclExpRoot( rRoot ),
+ mcBuiltIn( cBuiltIn ),
+ mnScTab( SCTAB_GLOBAL ),
+ mnFlags( EXC_NAME_DEFAULT ),
+ mnExtSheet( EXC_NAME_GLOBAL ),
+ mnXclTab( EXC_NAME_GLOBAL )
+{
+ // filter source range is hidden in Excel
+ if( cBuiltIn == EXC_BUILTIN_FILTERDATABASE )
+ SetHidden();
+
+ // special case for BIFF5/7 filter source range - name appears as plain text without built-in flag
+ if( (GetBiff() <= EXC_BIFF5) && (cBuiltIn == EXC_BUILTIN_FILTERDATABASE) )
+ {
+ OUString aName( XclTools::GetXclBuiltInDefName( EXC_BUILTIN_FILTERDATABASE ) );
+ mxName = XclExpStringHelper::CreateString( rRoot, aName, XclStrFlags::EightBitLength );
+ maOrigName = XclTools::GetXclBuiltInDefName( cBuiltIn );
+ }
+ else
+ {
+ maOrigName = XclTools::GetBuiltInDefNameXml( cBuiltIn ) ;
+ mxName = XclExpStringHelper::CreateString( rRoot, cBuiltIn, XclStrFlags::EightBitLength );
+ ::set_flag( mnFlags, EXC_NAME_BUILTIN );
+ }
+}
+
+void XclExpName::SetTokenArray( const XclTokenArrayRef& xTokArr )
+{
+ mxTokArr = xTokArr;
+}
+
+void XclExpName::SetLocalTab( SCTAB nScTab )
+{
+ OSL_ENSURE( GetTabInfo().IsExportTab( nScTab ), "XclExpName::SetLocalTab - invalid sheet index" );
+ if( !GetTabInfo().IsExportTab( nScTab ) )
+ return;
+
+ mnScTab = nScTab;
+ GetGlobalLinkManager().FindExtSheet( mnExtSheet, mnXclTab, nScTab );
+
+ // special handling for NAME record
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: // EXTERNSHEET index is positive in NAME record
+ mnExtSheet = ~mnExtSheet + 1;
+ break;
+ case EXC_BIFF8: // EXTERNSHEET index not used, but must be created in link table
+ mnExtSheet = 0;
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ // Excel sheet index is 1-based
+ ++mnXclTab;
+}
+
+void XclExpName::SetHidden( bool bHidden )
+{
+ ::set_flag( mnFlags, EXC_NAME_HIDDEN, bHidden );
+}
+
+void XclExpName::SetMacroCall( bool bVBasic, bool bFunc )
+{
+ ::set_flag( mnFlags, EXC_NAME_PROC );
+ ::set_flag( mnFlags, EXC_NAME_VB, bVBasic );
+ ::set_flag( mnFlags, EXC_NAME_FUNC, bFunc );
+}
+
+void XclExpName::SetSymbol( const OUString& rSymbol )
+{
+ msSymbol = rSymbol;
+}
+
+bool XclExpName::IsVolatile() const
+{
+ return mxTokArr && mxTokArr->IsVolatile();
+}
+
+bool XclExpName::IsMacroCall( bool bVBasic, bool bFunc ) const
+{
+ return
+ (::get_flag( mnFlags, EXC_NAME_VB ) == bVBasic) &&
+ (::get_flag( mnFlags, EXC_NAME_FUNC ) == bFunc);
+}
+
+void XclExpName::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mxName && (mxName->Len() > 0), "XclExpName::Save - missing name" );
+ OSL_ENSURE( !(IsGlobal() && ::get_flag( mnFlags, EXC_NAME_BUILTIN )), "XclExpName::Save - global built-in name" );
+ SetRecSize( 11 + mxName->GetSize() + (mxTokArr ? mxTokArr->GetSize() : 2) );
+ XclExpRecord::Save( rStrm );
+}
+
+OUString XclExpName::GetWithDefaultRangeSeparator( const OUString& rSymbol ) const
+{
+ sal_Int32 nPos = rSymbol.indexOf(';');
+ if ( nPos > -1 )
+ {
+ // convert with validation
+ ScRange aRange;
+ ScAddress::Details detailsXL( ::formula::FormulaGrammar::CONV_XL_A1 );
+ ScRefFlags nRes = aRange.Parse( rSymbol.copy(0, nPos), GetDoc(), detailsXL );
+ if ( nRes & ScRefFlags::VALID )
+ {
+ nRes = aRange.Parse( rSymbol.copy(nPos+1), GetDoc(), detailsXL );
+ if ( nRes & ScRefFlags::VALID )
+ {
+ return rSymbol.replaceFirst(";", ",");
+ }
+ }
+ }
+ return rSymbol;
+}
+
+void XclExpName::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_definedName,
+ // OOXTODO: XML_comment, "",
+ // OOXTODO: XML_customMenu, "",
+ // OOXTODO: XML_description, "",
+ XML_function, ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) ),
+ // OOXTODO: XML_functionGroupId, "",
+ // OOXTODO: XML_help, "",
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_NAME_HIDDEN ) ),
+ XML_localSheetId, mnScTab == SCTAB_GLOBAL ? nullptr : OString::number( mnScTab ).getStr(),
+ XML_name, maOrigName.toUtf8(),
+ // OOXTODO: XML_publishToServer, "",
+ // OOXTODO: XML_shortcutKey, "",
+ // OOXTODO: XML_statusBar, "",
+ XML_vbProcedure, ToPsz( ::get_flag( mnFlags, EXC_NAME_VB ) )
+ // OOXTODO: XML_workbookParameter, "",
+ // OOXTODO: XML_xlm, ""
+ );
+ rWorkbook->writeEscaped( GetWithDefaultRangeSeparator( msSymbol ) );
+ rWorkbook->endElement( XML_definedName );
+}
+
+void XclExpName::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nFmlaSize = mxTokArr ? mxTokArr->GetSize() : 0;
+
+ rStrm << mnFlags // flags
+ << sal_uInt8( 0 ); // keyboard shortcut
+ mxName->WriteLenField( rStrm ); // length of name
+ rStrm << nFmlaSize // size of token array
+ << mnExtSheet // BIFF5/7: EXTSHEET index, BIFF8: not used
+ << mnXclTab // 1-based sheet index for local names
+ << sal_uInt32( 0 ); // length of menu/descr/help/status text
+ mxName->WriteFlagField( rStrm ); // BIFF8 flag field (no-op in <=BIFF7)
+ mxName->WriteBuffer( rStrm ); // character array of the name
+ if( mxTokArr )
+ mxTokArr->WriteArray( rStrm ); // token array without size
+}
+
+/** Returns true (needed fixing) if FormulaToken was not absolute and 3D.
+ So, regardless of whether the fix was successful or not, true is still returned since a fix was required.*/
+static bool lcl_EnsureAbs3DToken( const SCTAB nTab, formula::FormulaToken* pTok, const bool bFix = true )
+{
+ bool bFixRequired = false;
+ if ( !pTok || ( pTok->GetType() != formula::svSingleRef && pTok->GetType() != formula::svDoubleRef ) )
+ return bFixRequired;
+
+ ScSingleRefData* pRef1 = pTok->GetSingleRef();
+ if ( !pRef1 )
+ return bFixRequired;
+
+ ScSingleRefData* pRef2 = nullptr;
+ if ( pTok->GetType() == formula::svDoubleRef )
+ pRef2 = pTok->GetSingleRef2();
+
+ if ( pRef1->IsTabRel() || !pRef1->IsFlag3D() )
+ {
+ bFixRequired = true;
+ if ( bFix )
+ {
+ if ( pRef1->IsTabRel() && nTab != SCTAB_GLOBAL )
+ pRef1->SetAbsTab( nTab + pRef1->Tab() ); //XLS requirement
+ if ( !pRef1->IsTabRel() )
+ {
+ pRef1->SetFlag3D( true ); //XLSX requirement
+ if ( pRef2 && !pRef2->IsTabRel() )
+ pRef2->SetFlag3D( pRef2->Tab() != pRef1->Tab() );
+ }
+ }
+ }
+
+ if ( pRef2 && pRef2->IsTabRel() && !pRef1->IsTabRel() )
+ {
+ bFixRequired = true;
+ if ( bFix && nTab != SCTAB_GLOBAL )
+ {
+ pRef2->SetAbsTab( nTab + pRef2->Tab() );
+ pRef2->SetFlag3D( pRef2->Tab() != pRef1->Tab() );
+ }
+ }
+ return bFixRequired;
+}
+
+XclExpNameManagerImpl::XclExpNameManagerImpl( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnFirstUserIdx( 0 )
+{
+}
+
+void XclExpNameManagerImpl::Initialize()
+{
+ CreateBuiltInNames();
+ mnFirstUserIdx = maNameList.GetSize();
+ CreateUserNames();
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab )
+{
+ sal_uInt16 nNameIdx = 0;
+ const ScRangeData* pData = nullptr;
+ ScRangeName* pRN = (nTab == SCTAB_GLOBAL) ? GetDoc().GetRangeName() : GetDoc().GetRangeName(nTab);
+ if (pRN)
+ pData = pRN->findByIndex(nScNameIdx);
+
+ if (pData)
+ {
+ bool bEmulateGlobalRelativeTable = false;
+ const ScTokenArray* pCode = pData->GetCode();
+ if ( pCode
+ && nTab == SCTAB_GLOBAL
+ && (pData->HasType( ScRangeData::Type::AbsPos ) || pData->HasType( ScRangeData::Type::AbsArea )) )
+ {
+ bEmulateGlobalRelativeTable = lcl_EnsureAbs3DToken( nTab, pCode->FirstToken(), /*bFix=*/false );
+ }
+ nNameIdx = FindNamedExp( bEmulateGlobalRelativeTable ? nCurrTab : nTab, pData->GetName() );
+ if (!nNameIdx)
+ nNameIdx = CreateName(nTab, *pData);
+ }
+
+ return nNameIdx;
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, const ScRange& aRange )
+{
+ XclExpNameRef xName = new XclExpName( GetRoot(), cBuiltIn );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( aRange.aStart.Tab() );
+ OUString sSymbol(aRange.Format(GetDoc(), ScRefFlags::RANGE_ABS_3D, ScAddress::Details( ::formula::FormulaGrammar::CONV_XL_A1)));
+ xName->SetSymbol( sSymbol );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertBuiltInName( sal_Unicode cBuiltIn, const XclTokenArrayRef& xTokArr, SCTAB nScTab, const ScRangeList& rRangeList )
+{
+ XclExpNameRef xName = new XclExpName( GetRoot(), cBuiltIn );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( nScTab );
+ OUString sSymbol;
+ rRangeList.Format( sSymbol, ScRefFlags::RANGE_ABS_3D, GetDoc(), ::formula::FormulaGrammar::CONV_XL_A1 );
+ xName->SetSymbol( sSymbol );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertUniqueName(
+ const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab )
+{
+ OSL_ENSURE( !rName.isEmpty(), "XclExpNameManagerImpl::InsertUniqueName - empty name" );
+ XclExpNameRef xName = new XclExpName( GetRoot(), GetUnusedName( rName ) );
+ xName->SetTokenArray( xTokArr );
+ xName->SetLocalTab( nScTab );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertRawName( const OUString& rName )
+{
+ // empty name? may occur in broken external Calc tokens
+ if( rName.isEmpty() )
+ return 0;
+
+ // try to find an existing NAME record, regardless of its type
+ for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nListIdx );
+ if( xName->IsGlobal() && (xName->GetOrigName() == rName) )
+ return static_cast< sal_uInt16 >( nListIdx + 1 );
+ }
+
+ // create a new NAME record
+ XclExpNameRef xName = new XclExpName( GetRoot(), rName );
+ return Append( xName );
+}
+
+sal_uInt16 XclExpNameManagerImpl::InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden )
+{
+ // empty name? may occur in broken external Calc tokens
+ if( rMacroName.isEmpty() )
+ return 0;
+
+ // try to find an existing NAME record
+ for( size_t nListIdx = mnFirstUserIdx, nListSize = maNameList.GetSize(); nListIdx < nListSize; ++nListIdx )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nListIdx );
+ if( xName->IsMacroCall( bVBasic, bFunc ) && (xName->GetOrigName() == rMacroName) )
+ return static_cast< sal_uInt16 >( nListIdx + 1 );
+ }
+
+ // create a new NAME record
+ XclExpNameRef xName = new XclExpName( GetRoot(), rMacroName );
+ xName->SetMacroCall( bVBasic, bFunc );
+ xName->SetHidden( bHidden );
+
+ // for sheet macros, add a #NAME! error
+ if( !bVBasic )
+ xName->SetTokenArray( GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NAME ) );
+
+ return Append( xName );
+}
+
+const XclExpName* XclExpNameManagerImpl::GetName( sal_uInt16 nNameIdx ) const
+{
+ OSL_ENSURE( maNameList.HasRecord( nNameIdx - 1 ), "XclExpNameManagerImpl::GetName - wrong record index" );
+ return maNameList.GetRecord( nNameIdx - 1 );
+}
+
+void XclExpNameManagerImpl::Save( XclExpStream& rStrm )
+{
+ maNameList.Save( rStrm );
+}
+
+void XclExpNameManagerImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maNameList.IsEmpty() )
+ return;
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement(XML_definedNames);
+ maNameList.SaveXml( rStrm );
+ rWorkbook->endElement( XML_definedNames );
+}
+
+// private --------------------------------------------------------------------
+
+sal_uInt16 XclExpNameManagerImpl::FindNamedExp( SCTAB nTab, OUString sName )
+{
+ NamedExpMap::key_type key(nTab, sName);
+ NamedExpMap::const_iterator itr = maNamedExpMap.find(key);
+ return (itr == maNamedExpMap.end()) ? 0 : itr->second;
+}
+
+sal_uInt16 XclExpNameManagerImpl::FindBuiltInNameIdx(
+ const OUString& rName, const OUString& sSymbol ) const
+{
+ /* Get built-in index from the name. Special case: the database range
+ 'unnamed' will be mapped to Excel's built-in '_FilterDatabase' name. */
+ sal_Unicode cBuiltIn = XclTools::GetBuiltInDefNameIndex( rName );
+
+ if( cBuiltIn < EXC_BUILTIN_UNKNOWN )
+ {
+ // try to find the record in existing built-in NAME record list
+ for( size_t nPos = 0; nPos < mnFirstUserIdx; ++nPos )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nPos );
+ if( xName->GetBuiltInName() == cBuiltIn && xName->GetSymbol().replace(';', ',') == sSymbol.replace(';', ',') )
+ {
+ // tdf#112567 restore the original built-in names with non-localized separators
+ // TODO: support more localizations, if needed
+ if ( xName->GetSymbol() != sSymbol )
+ {
+ xName->SetSymbol(xName->GetSymbol().replace(';', ','));
+ }
+ return static_cast< sal_uInt16 >( nPos + 1 );
+ }
+ }
+ }
+ return 0;
+}
+
+OUString XclExpNameManagerImpl::GetUnusedName( const OUString& rName ) const
+{
+ OUString aNewName( rName );
+ sal_Int32 nAppIdx = 0;
+ bool bExist = true;
+ while( bExist )
+ {
+ // search the list of user-defined names
+ bExist = false;
+ for( size_t nPos = mnFirstUserIdx, nSize = maNameList.GetSize(); !bExist && (nPos < nSize); ++nPos )
+ {
+ XclExpNameRef xName = maNameList.GetRecord( nPos );
+ bExist = xName->GetOrigName() == aNewName;
+ // name exists -> create a new name "<originalname>_<counter>"
+ if( bExist )
+ aNewName = rName + "_" + OUString::number( ++nAppIdx );
+ }
+ }
+ return aNewName;
+}
+
+sal_uInt16 XclExpNameManagerImpl::Append( XclExpName* pName )
+{
+ if( maNameList.GetSize() == 0xFFFF )
+ return 0;
+ maNameList.AppendRecord( pName );
+ return static_cast< sal_uInt16 >( maNameList.GetSize() ); // 1-based
+}
+
+sal_uInt16 XclExpNameManagerImpl::CreateName( SCTAB nTab, const ScRangeData& rRangeData )
+{
+ const OUString& rName = rRangeData.GetName();
+
+ /* #i38821# recursive names: first insert the (empty) name object,
+ otherwise a recursive call of this function from the formula compiler
+ with the same defined name will not find it and will create it again. */
+ size_t nOldListSize = maNameList.GetSize();
+ XclExpNameRef xName = new XclExpName( GetRoot(), rName );
+ if (nTab != SCTAB_GLOBAL)
+ xName->SetLocalTab(nTab);
+ sal_uInt16 nNameIdx = Append( xName );
+ // store the index of the NAME record in the lookup map
+ NamedExpMap::key_type key(nTab, rRangeData.GetName());
+ maNamedExpMap[key] = nNameIdx;
+
+ /* Create the definition formula.
+ This may cause recursive creation of other defined names. */
+ if( const ScTokenArray* pScTokArr = const_cast< ScRangeData& >( rRangeData ).GetCode() )
+ {
+ XclTokenArrayRef xTokArr;
+ OUString sSymbol;
+ // MSO requires named ranges to have absolute sheet references
+ if ( rRangeData.HasType( ScRangeData::Type::AbsPos ) || rRangeData.HasType( ScRangeData::Type::AbsArea ) )
+ {
+ // Don't modify the actual document; use a temporary copy to create the export formulas.
+ ScTokenArray aTokenCopy( pScTokArr->CloneValue() );
+ lcl_EnsureAbs3DToken(nTab, aTokenCopy.FirstToken());
+
+ xTokArr = GetFormulaCompiler().CreateFormula(EXC_FMLATYPE_NAME, aTokenCopy);
+ if ( GetOutput() != EXC_OUTPUT_BINARY )
+ {
+ ScCompiler aComp(GetDoc(), rRangeData.GetPos(), aTokenCopy,
+ formula::FormulaGrammar::GRAM_OOXML);
+ aComp.CreateStringFromTokenArray( sSymbol );
+ }
+ }
+ else
+ {
+ bool bOOXML = GetOutput() == EXC_OUTPUT_XML_2007;
+ xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, *pScTokArr, bOOXML ?
+ &rRangeData.GetPos() : nullptr );
+ sSymbol = rRangeData.GetSymbol( (GetOutput() == EXC_OUTPUT_BINARY) ?
+ formula::FormulaGrammar::GRAM_ENGLISH_XL_A1 : formula::FormulaGrammar::GRAM_OOXML);
+ }
+ xName->SetTokenArray( xTokArr );
+ xName->SetSymbol( sSymbol );
+
+ /* Try to replace by existing built-in name - complete token array is
+ needed for comparison, and due to the recursion problem above this
+ cannot be done earlier. If a built-in name is found, the created NAME
+ record for this name and all following records in the list must be
+ deleted, otherwise they may contain wrong name list indexes. */
+ sal_uInt16 nBuiltInIdx = FindBuiltInNameIdx( rName, sSymbol );
+ if( nBuiltInIdx != 0 )
+ {
+ // delete the new NAME records
+ while( maNameList.GetSize() > nOldListSize )
+ maNameList.RemoveRecord( maNameList.GetSize() - 1 );
+ // use index of the found built-in NAME record
+ key = NamedExpMap::key_type(nTab, rRangeData.GetName());
+ maNamedExpMap[key] = nNameIdx = nBuiltInIdx;
+ }
+ }
+
+ return nNameIdx;
+}
+
+void XclExpNameManagerImpl::CreateBuiltInNames()
+{
+ ScDocument& rDoc = GetDoc();
+ XclExpTabInfo& rTabInfo = GetTabInfo();
+
+ /* #i2394# built-in defined names must be sorted by the name of the
+ containing sheet. Example: SheetA!Print_Range must be stored *before*
+ SheetB!Print_Range, regardless of the position of SheetA in the document! */
+ for( SCTAB nScTabIdx = 0, nScTabCount = rTabInfo.GetScTabCount(); nScTabIdx < nScTabCount; ++nScTabIdx )
+ {
+ // find real sheet index from the nScTabIdx counter
+ SCTAB nScTab = rTabInfo.GetRealScTab( nScTabIdx );
+ // create NAME records for all built-in names of this sheet
+ if( rTabInfo.IsExportTab( nScTab ) )
+ {
+ // *** 1) print ranges *** ----------------------------------------
+
+ if( rDoc.HasPrintRange() )
+ {
+ ScRangeList aRangeList;
+ for( sal_uInt16 nIdx = 0, nCount = rDoc.GetPrintRangeCount( nScTab ); nIdx < nCount; ++nIdx )
+ {
+ const ScRange* pPrintRange = rDoc.GetPrintRange( nScTab, nIdx );
+ if (!pPrintRange)
+ continue;
+ ScRange aRange( *pPrintRange );
+ // Calc document does not care about sheet index in print ranges
+ aRange.aStart.SetTab( nScTab );
+ aRange.aEnd.SetTab( nScTab );
+ aRange.PutInOrder();
+ aRangeList.push_back( aRange );
+ }
+ // create the NAME record (do not warn if ranges are shrunken)
+ GetAddressConverter().ValidateRangeList( aRangeList, false );
+ if( !aRangeList.empty() )
+ GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTAREA, aRangeList );
+ }
+
+ // *** 2) print titles *** ----------------------------------------
+
+ ScRangeList aTitleList;
+ // repeated columns
+ if( std::optional<ScRange> oColRange = rDoc.GetRepeatColRange( nScTab ) )
+ aTitleList.push_back( ScRange(
+ oColRange->aStart.Col(), 0, nScTab,
+ oColRange->aEnd.Col(), GetXclMaxPos().Row(), nScTab ) );
+ // repeated rows
+ if( std::optional<ScRange> oRowRange = rDoc.GetRepeatRowRange( nScTab ) )
+ aTitleList.push_back( ScRange(
+ 0, oRowRange->aStart.Row(), nScTab,
+ GetXclMaxPos().Col(), oRowRange->aEnd.Row(), nScTab ) );
+ // create the NAME record
+ GetAddressConverter().ValidateRangeList( aTitleList, false );
+ if( !aTitleList.empty() )
+ GetNameManager().InsertBuiltInName( EXC_BUILTIN_PRINTTITLES, aTitleList );
+
+ // *** 3) filter ranges *** ---------------------------------------
+
+ if( GetBiff() == EXC_BIFF8 )
+ GetFilterManager().InitTabFilter( nScTab );
+ }
+ }
+}
+
+void XclExpNameManagerImpl::CreateUserNames()
+{
+ std::vector<ScRangeData*> vEmulateAsLocalRange;
+ const ScRangeName& rNamedRanges = GetNamedRanges();
+ for (const auto& rEntry : rNamedRanges)
+ {
+ // skip definitions of shared formulas
+ if (!FindNamedExp(SCTAB_GLOBAL, rEntry.second->GetName()))
+ {
+ const ScTokenArray* pCode = rEntry.second->GetCode();
+ if ( pCode
+ && (rEntry.second->HasType( ScRangeData::Type::AbsPos ) || rEntry.second->HasType( ScRangeData::Type::AbsArea ))
+ && lcl_EnsureAbs3DToken( SCTAB_GLOBAL, pCode->FirstToken(), /*bFix=*/false ) )
+ {
+ vEmulateAsLocalRange.emplace_back(rEntry.second.get());
+ }
+ else
+ CreateName(SCTAB_GLOBAL, *rEntry.second);
+ }
+ }
+ //look at sheets containing local range names
+ ScRangeName::TabNameCopyMap rLocalNames;
+ GetDoc().GetAllTabRangeNames(rLocalNames);
+ for (const auto& [rTab, pRangeName] : rLocalNames)
+ {
+ for (const auto& rEntry : *pRangeName)
+ {
+ // skip definitions of shared formulas
+ if (!FindNamedExp(rTab, rEntry.second->GetName()))
+ CreateName(rTab, *rEntry.second);
+ }
+ }
+
+ // Emulate relative global variables by creating a copy in each local range.
+ // Creating AFTER true local range names so that conflicting global names will be ignored.
+ for ( SCTAB nTab = 0; nTab < GetDoc().GetTableCount(); ++nTab )
+ {
+ for ( auto rangeDataItr : vEmulateAsLocalRange )
+ {
+ if ( !FindNamedExp(nTab, rangeDataItr->GetName()) )
+ CreateName(nTab, *rangeDataItr );
+ }
+ }
+}
+
+XclExpNameManager::XclExpNameManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxImpl( std::make_shared<XclExpNameManagerImpl>( rRoot ) )
+{
+}
+
+XclExpNameManager::~XclExpNameManager()
+{
+}
+
+void XclExpNameManager::Initialize()
+{
+ mxImpl->Initialize();
+}
+
+sal_uInt16 XclExpNameManager::InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab )
+{
+ return mxImpl->InsertName( nTab, nScNameIdx, nCurrTab );
+}
+
+sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRange& rRange )
+{
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRange );
+ return mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRange );
+}
+
+sal_uInt16 XclExpNameManager::InsertBuiltInName( sal_Unicode cBuiltIn, const ScRangeList& rRangeList )
+{
+ sal_uInt16 nNameIdx = 0;
+ if( !rRangeList.empty() )
+ {
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_NAME, rRangeList );
+ nNameIdx = mxImpl->InsertBuiltInName( cBuiltIn, xTokArr, rRangeList.front().aStart.Tab(), rRangeList );
+ }
+ return nNameIdx;
+}
+
+sal_uInt16 XclExpNameManager::InsertUniqueName(
+ const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab )
+{
+ return mxImpl->InsertUniqueName( rName, xTokArr, nScTab );
+}
+
+sal_uInt16 XclExpNameManager::InsertRawName( const OUString& rName )
+{
+ return mxImpl->InsertRawName( rName );
+}
+
+sal_uInt16 XclExpNameManager::InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden )
+{
+ return mxImpl->InsertMacroCall( rMacroName, bVBasic, bFunc, bHidden );
+}
+
+OUString XclExpNameManager::GetOrigName( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName ? pName->GetOrigName() : OUString();
+}
+
+SCTAB XclExpNameManager::GetScTab( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName ? pName->GetScTab() : SCTAB_GLOBAL;
+}
+
+bool XclExpNameManager::IsVolatile( sal_uInt16 nNameIdx ) const
+{
+ const XclExpName* pName = mxImpl->GetName( nNameIdx );
+ return pName && pName->IsVolatile();
+}
+
+void XclExpNameManager::Save( XclExpStream& rStrm )
+{
+ mxImpl->Save( rStrm );
+}
+
+void XclExpNameManager::SaveXml( XclExpXmlStream& rStrm )
+{
+ mxImpl->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepage.cxx b/sc/source/filter/excel/xepage.cxx
new file mode 100644
index 000000000..56ecd2d6b
--- /dev/null
+++ b/sc/source/filter/excel/xepage.cxx
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xepage.hxx>
+#include <svl/itemset.hxx>
+#include <scitems.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <sax/fastattribs.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <attrib.hxx>
+#include <xehelper.hxx>
+#include <xeescher.hxx>
+#include <xltools.hxx>
+
+#include <set>
+#include <limits>
+
+using namespace ::oox;
+
+using ::std::set;
+using ::std::numeric_limits;
+
+// Page settings records ======================================================
+
+// Header/footer --------------------------------------------------------------
+
+XclExpHeaderFooter::XclExpHeaderFooter( sal_uInt16 nRecId, const OUString& rHdrString ) :
+ XclExpRecord( nRecId ),
+ maHdrString( rHdrString )
+{
+}
+
+void XclExpHeaderFooter::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ sal_Int32 nElement;
+ switch(GetRecId()) {
+ case EXC_ID_HEADER_FIRST: nElement = XML_firstHeader; break;
+ case EXC_ID_FOOTER_FIRST: nElement = XML_firstFooter; break;
+ case EXC_ID_HEADER_EVEN: nElement = XML_evenHeader; break;
+ case EXC_ID_FOOTER_EVEN: nElement = XML_evenFooter; break;
+ case EXC_ID_HEADER: nElement = XML_oddHeader; break;
+ case EXC_ID_FOOTER:
+ default: nElement = XML_oddFooter;
+ }
+ rWorksheet->startElement(nElement);
+ rWorksheet->writeEscaped( maHdrString );
+ rWorksheet->endElement( nElement );
+}
+
+void XclExpHeaderFooter::WriteBody( XclExpStream& rStrm )
+{
+ if( !maHdrString.isEmpty() )
+ {
+ XclExpString aExString;
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ aExString.AssignByte( maHdrString, rStrm.GetRoot().GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aExString.Assign( maHdrString, XclStrFlags::NONE, 255 ); // 16-bit length, but max 255 chars
+ rStrm << aExString;
+ }
+}
+
+// General page settings ------------------------------------------------------
+
+XclExpSetup::XclExpSetup( const XclPageData& rPageData ) :
+ XclExpRecord( EXC_ID_SETUP, 34 ),
+ mrData( rPageData )
+{
+}
+
+void XclExpSetup::SaveXml( XclExpXmlStream& rStrm )
+{
+ rtl::Reference<sax_fastparser::FastAttributeList> pAttrList = sax_fastparser::FastSerializerHelper::createAttrList();
+ if( rStrm.getVersion() != oox::core::ISOIEC_29500_2008 ||
+ mrData.mnStrictPaperSize != EXC_PAPERSIZE_USER )
+ {
+ pAttrList->add( XML_paperSize, OString::number( mrData.mnPaperSize ).getStr() );
+ }
+ else
+ {
+ pAttrList->add( XML_paperWidth, OString::number( mrData.mnPaperWidth ) + "mm" );
+ pAttrList->add( XML_paperHeight, OString::number( mrData.mnPaperHeight ) + "mm" );
+ // pAttrList->add( XML_paperUnits, "mm" );
+ }
+ pAttrList->add( XML_scale, OString::number( mrData.mnScaling ).getStr() );
+ pAttrList->add( XML_fitToWidth, OString::number( mrData.mnFitToWidth ).getStr() );
+ pAttrList->add( XML_fitToHeight, OString::number( mrData.mnFitToHeight ).getStr() );
+ pAttrList->add( XML_pageOrder, mrData.mbPrintInRows ? "overThenDown" : "downThenOver" );
+ pAttrList->add( XML_orientation, mrData.mbPortrait ? "portrait" : "landscape" ); // OOXTODO: "default"?
+ // tdf#48767 if XML_usePrinterDefaults field is exist, then XML_orientation is always "portrait" in MS Excel
+ // To resolve that import issue, if XML_usePrinterDefaults has default value (false) then XML_usePrinterDefaults is not added.
+ if ( !mrData.mbValid )
+ pAttrList->add( XML_usePrinterDefaults, ToPsz( !mrData.mbValid ) );
+ pAttrList->add( XML_blackAndWhite, ToPsz( mrData.mbBlackWhite ) );
+ pAttrList->add( XML_draft, ToPsz( mrData.mbDraftQuality ) );
+ pAttrList->add( XML_cellComments, mrData.mbPrintNotes ? "atEnd" : "none" ); // OOXTODO: "asDisplayed"?
+
+ if ( mrData.mbManualStart )
+ {
+ pAttrList->add( XML_firstPageNumber, OString::number( mrData.mnStartPage ).getStr() );
+ pAttrList->add( XML_useFirstPageNumber, ToPsz( mrData.mbManualStart ) );
+ }
+ // OOXTODO: XML_errors, // == displayed|blank|dash|NA
+ pAttrList->add( XML_horizontalDpi, OString::number( mrData.mnHorPrintRes ).getStr() );
+ pAttrList->add( XML_verticalDpi, OString::number( mrData.mnVerPrintRes ).getStr() );
+ pAttrList->add( XML_copies, OString::number( mrData.mnCopies ).getStr() );
+ // OOXTODO: devMode settings part RelationshipId: FSNS( XML_r, XML_id ),
+
+ rStrm.GetCurrentStream()->singleElement( XML_pageSetup, pAttrList );
+}
+
+void XclExpSetup::WriteBody( XclExpStream& rStrm )
+{
+ XclBiff eBiff = rStrm.GetRoot().GetBiff();
+
+ sal_uInt16 nFlags = 0;
+ ::set_flag( nFlags, EXC_SETUP_INROWS, mrData.mbPrintInRows );
+ ::set_flag( nFlags, EXC_SETUP_PORTRAIT, mrData.mbPortrait );
+ ::set_flag( nFlags, EXC_SETUP_INVALID, !mrData.mbValid );
+ ::set_flag( nFlags, EXC_SETUP_BLACKWHITE, mrData.mbBlackWhite );
+ if( eBiff >= EXC_BIFF5 )
+ {
+ ::set_flag( nFlags, EXC_SETUP_DRAFT, mrData.mbDraftQuality );
+ /* Set the Comments/Notes to "At end of sheet" if Print Notes is true.
+ We don't currently support "as displayed on sheet". Thus this value
+ will be re-interpreted to "At end of sheet". */
+ const sal_uInt16 nNotes = EXC_SETUP_PRINTNOTES | EXC_SETUP_NOTES_END;
+ ::set_flag( nFlags, nNotes, mrData.mbPrintNotes );
+ ::set_flag( nFlags, EXC_SETUP_STARTPAGE, mrData.mbManualStart );
+ }
+
+ rStrm << mrData.mnPaperSize << mrData.mnScaling << mrData.mnStartPage
+ << mrData.mnFitToWidth << mrData.mnFitToHeight << nFlags;
+ if( eBiff >= EXC_BIFF5 )
+ {
+ rStrm << mrData.mnHorPrintRes << mrData.mnVerPrintRes
+ << mrData.mfHeaderMargin << mrData.mfFooterMargin << mrData.mnCopies;
+ }
+}
+
+// Manual page breaks ---------------------------------------------------------
+
+XclExpPageBreaks::XclExpPageBreaks( sal_uInt16 nRecId, const ScfUInt16Vec& rPageBreaks, sal_uInt16 nMaxPos ) :
+ XclExpRecord( nRecId ),
+ mrPageBreaks( rPageBreaks ),
+ mnMaxPos( nMaxPos )
+{
+}
+
+void XclExpPageBreaks::Save( XclExpStream& rStrm )
+{
+ if( !mrPageBreaks.empty() )
+ {
+ SetRecSize( 2 + ((rStrm.GetRoot().GetBiff() <= EXC_BIFF5) ? 2 : 6) * mrPageBreaks.size() );
+ XclExpRecord::Save( rStrm );
+ }
+}
+
+void XclExpPageBreaks::WriteBody( XclExpStream& rStrm )
+{
+ bool bWriteRange = (rStrm.GetRoot().GetBiff() == EXC_BIFF8);
+
+ rStrm << static_cast< sal_uInt16 >( mrPageBreaks.size() );
+ for( const auto& rPageBreak : mrPageBreaks )
+ {
+ rStrm << rPageBreak;
+ if( bWriteRange )
+ rStrm << sal_uInt16( 0 ) << mnMaxPos;
+ }
+}
+
+void XclExpPageBreaks::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mrPageBreaks.empty() )
+ return;
+
+ sal_Int32 nElement = GetRecId() == EXC_ID_HORPAGEBREAKS ? XML_rowBreaks : XML_colBreaks;
+ sax_fastparser::FSHelperPtr& pWorksheet = rStrm.GetCurrentStream();
+ OString sNumPageBreaks = OString::number( mrPageBreaks.size() );
+ pWorksheet->startElement( nElement,
+ XML_count, sNumPageBreaks,
+ XML_manualBreakCount, sNumPageBreaks );
+ for( const auto& rPageBreak : mrPageBreaks )
+ {
+ pWorksheet->singleElement( XML_brk,
+ XML_id, OString::number(rPageBreak),
+ XML_man, "true",
+ XML_max, OString::number(mnMaxPos),
+ XML_min, "0"
+ // OOXTODO: XML_pt, ""
+ );
+ }
+ pWorksheet->endElement( nElement );
+}
+
+// Page settings ==============================================================
+
+XclExpPageSettings::XclExpPageSettings( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ if( SfxStyleSheetBase* pStyleSheet = GetStyleSheetPool().Find( rDoc.GetPageStyle( nScTab ), SfxStyleFamily::Page ) )
+ {
+ const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ maData.mbValid = true;
+
+ // *** page settings ***
+
+ maData.mbPrintInRows = ! rItemSet.Get( ATTR_PAGE_TOPDOWN ).GetValue();
+ maData.mbHorCenter = rItemSet.Get( ATTR_PAGE_HORCENTER ).GetValue();
+ maData.mbVerCenter = rItemSet.Get( ATTR_PAGE_VERCENTER ).GetValue();
+ maData.mbPrintHeadings = rItemSet.Get( ATTR_PAGE_HEADERS ).GetValue();
+ maData.mbPrintGrid = rItemSet.Get( ATTR_PAGE_GRID ).GetValue();
+ maData.mbPrintNotes = rItemSet.Get( ATTR_PAGE_NOTES ).GetValue();
+
+ maData.mnStartPage = rItemSet.Get( ATTR_PAGE_FIRSTPAGENO ).GetValue();
+ maData.mbManualStart = maData.mnStartPage && (!nScTab || rDoc.NeedPageResetAfterTab( nScTab - 1 ));
+
+ const SvxLRSpaceItem& rLRItem = rItemSet.Get( ATTR_LRSPACE );
+ maData.mfLeftMargin = XclTools::GetInchFromTwips( rLRItem.GetLeft() );
+ maData.mfRightMargin = XclTools::GetInchFromTwips( rLRItem.GetRight() );
+ const SvxULSpaceItem& rULItem = rItemSet.Get( ATTR_ULSPACE );
+ maData.mfTopMargin = XclTools::GetInchFromTwips( rULItem.GetUpper() );
+ maData.mfBottomMargin = XclTools::GetInchFromTwips( rULItem.GetLower() );
+
+ const SvxPageItem& rPageItem = rItemSet.Get( ATTR_PAGE );
+ const SvxSizeItem& rSizeItem = rItemSet.Get( ATTR_PAGE_SIZE );
+ maData.SetScPaperSize( rSizeItem.GetSize(), !rPageItem.IsLandscape() );
+
+ const ScPageScaleToItem& rScaleToItem = rItemSet.Get( ATTR_PAGE_SCALETO );
+ sal_uInt16 nPages = rItemSet.Get( ATTR_PAGE_SCALETOPAGES ).GetValue();
+ sal_uInt16 nScale = rItemSet.Get( ATTR_PAGE_SCALE ).GetValue();
+
+ if( ScfTools::CheckItem( rItemSet, ATTR_PAGE_SCALETO, false ) && rScaleToItem.IsValid() )
+ {
+ maData.mnFitToWidth = rScaleToItem.GetWidth();
+ maData.mnFitToHeight = rScaleToItem.GetHeight();
+ maData.mbFitToPages = true;
+ }
+ else if( ScfTools::CheckItem( rItemSet, ATTR_PAGE_SCALETOPAGES, false ) && nPages )
+ {
+ maData.mnFitToWidth = 1;
+ maData.mnFitToHeight = nPages;
+ maData.mbFitToPages = true;
+ }
+ else if( nScale )
+ {
+ maData.mnScaling = nScale;
+ maData.mbFitToPages = false;
+ }
+
+ maData.mxBrushItem.reset( new SvxBrushItem( rItemSet.Get( ATTR_BACKGROUND ) ) );
+ maData.mbUseEvenHF = false;
+ maData.mbUseFirstHF = false;
+
+ // *** header and footer ***
+
+ XclExpHFConverter aHFConv( GetRoot() );
+
+ // header
+ const SfxItemSet& rHdrItemSet = rItemSet.Get( ATTR_PAGE_HEADERSET ).GetItemSet();
+ if( rHdrItemSet.Get( ATTR_PAGE_ON ).GetValue() )
+ {
+ const ScPageHFItem& rHFItem = rItemSet.Get( ATTR_PAGE_HEADERRIGHT );
+ aHFConv.GenerateString( rHFItem.GetLeftArea(), rHFItem.GetCenterArea(), rHFItem.GetRightArea() );
+ maData.maHeader = aHFConv.GetHFString();
+ if ( rHdrItemSet.HasItem(ATTR_PAGE_SHARED) && !rHdrItemSet.Get(ATTR_PAGE_SHARED).GetValue())
+ {
+ const ScPageHFItem& rHFItemLeft = rItemSet.Get( ATTR_PAGE_HEADERLEFT );
+ aHFConv.GenerateString( rHFItemLeft.GetLeftArea(), rHFItemLeft.GetCenterArea(), rHFItemLeft.GetRightArea() );
+ maData.maHeaderEven = aHFConv.GetHFString();
+ maData.mbUseEvenHF = true;
+ }
+ else
+ {
+ // If maData.mbUseEvenHF become true, then we will need a copy of maHeader in maHeaderEven.
+ maData.maHeaderEven = maData.maHeader;
+ }
+ if (rHdrItemSet.HasItem(ATTR_PAGE_SHARED_FIRST) && !rHdrItemSet.Get(ATTR_PAGE_SHARED_FIRST).GetValue())
+ {
+ const ScPageHFItem& rHFItemFirst = rItemSet.Get( ATTR_PAGE_HEADERFIRST );
+ aHFConv.GenerateString( rHFItemFirst.GetLeftArea(), rHFItemFirst.GetCenterArea(), rHFItemFirst.GetRightArea() );
+ maData.maHeaderFirst = aHFConv.GetHFString();
+ maData.mbUseFirstHF = true;
+ }
+ else
+ {
+ maData.maHeaderFirst = maData.maHeader;
+ }
+ // header height (Excel excludes header from top margin)
+ sal_Int32 nHdrHeight = rHdrItemSet.Get( ATTR_PAGE_DYNAMIC ).GetValue() ?
+ // dynamic height: calculate header height, add header <-> sheet area distance
+ (aHFConv.GetTotalHeight() + rHdrItemSet.Get( ATTR_ULSPACE ).GetLower()) :
+ // static height: ATTR_PAGE_SIZE already includes header <-> sheet area distance
+ static_cast< sal_Int32 >( rHdrItemSet.Get( ATTR_PAGE_SIZE ).GetSize().Height() );
+ maData.mfHeaderMargin = maData.mfTopMargin;
+ maData.mfTopMargin += XclTools::GetInchFromTwips( nHdrHeight );
+ }
+
+ // footer
+ const SfxItemSet& rFtrItemSet = rItemSet.Get( ATTR_PAGE_FOOTERSET ).GetItemSet();
+ if( rFtrItemSet.Get( ATTR_PAGE_ON ).GetValue() )
+ {
+ const ScPageHFItem& rHFItem = rItemSet.Get( ATTR_PAGE_FOOTERRIGHT );
+ aHFConv.GenerateString( rHFItem.GetLeftArea(), rHFItem.GetCenterArea(), rHFItem.GetRightArea() );
+ maData.maFooter = aHFConv.GetHFString();
+ if (rFtrItemSet.HasItem(ATTR_PAGE_SHARED) && !rFtrItemSet.Get(ATTR_PAGE_SHARED).GetValue())
+ {
+ const ScPageHFItem& rHFItemLeft = rItemSet.Get( ATTR_PAGE_FOOTERLEFT );
+ aHFConv.GenerateString( rHFItemLeft.GetLeftArea(), rHFItemLeft.GetCenterArea(), rHFItemLeft.GetRightArea() );
+ maData.maFooterEven = aHFConv.GetHFString();
+ maData.mbUseEvenHF = true;
+ }
+ else
+ {
+ maData.maFooterEven = maData.maFooter;
+ }
+ if (rFtrItemSet.HasItem(ATTR_PAGE_SHARED_FIRST) && !rFtrItemSet.Get(ATTR_PAGE_SHARED_FIRST).GetValue())
+ {
+ const ScPageHFItem& rHFItemFirst = rItemSet.Get( ATTR_PAGE_FOOTERFIRST );
+ aHFConv.GenerateString( rHFItemFirst.GetLeftArea(), rHFItemFirst.GetCenterArea(), rHFItemFirst.GetRightArea() );
+ maData.maFooterFirst = aHFConv.GetHFString();
+ maData.mbUseFirstHF = true;
+ }
+ else
+ {
+ maData.maFooterFirst = maData.maFooter;
+ }
+ // footer height (Excel excludes footer from bottom margin)
+ sal_Int32 nFtrHeight = rFtrItemSet.Get( ATTR_PAGE_DYNAMIC ).GetValue() ?
+ // dynamic height: calculate footer height, add sheet area <-> footer distance
+ (aHFConv.GetTotalHeight() + rFtrItemSet.Get( ATTR_ULSPACE ).GetUpper()) :
+ // static height: ATTR_PAGE_SIZE already includes sheet area <-> footer distance
+ static_cast< sal_Int32 >( rFtrItemSet.Get( ATTR_PAGE_SIZE ).GetSize().Height() );
+ maData.mfFooterMargin = maData.mfBottomMargin;
+ maData.mfBottomMargin += XclTools::GetInchFromTwips( nFtrHeight );
+ }
+ }
+
+ // *** page breaks ***
+
+ set<SCROW> aRowBreaks;
+ rDoc.GetAllRowBreaks(aRowBreaks, nScTab, false, true);
+
+ SCROW const nMaxRow = numeric_limits<sal_uInt16>::max();
+ for (const SCROW nRow : aRowBreaks)
+ {
+ if (nRow > nMaxRow)
+ break;
+
+ maData.maHorPageBreaks.push_back(nRow);
+ }
+
+ if (maData.maHorPageBreaks.size() > 1026)
+ {
+ // Excel allows only up to 1026 page breaks. Trim any excess page breaks.
+ ScfUInt16Vec::iterator itr = maData.maHorPageBreaks.begin();
+ ::std::advance(itr, 1026);
+ maData.maHorPageBreaks.erase(itr, maData.maHorPageBreaks.end());
+ }
+
+ set<SCCOL> aColBreaks;
+ rDoc.GetAllColBreaks(aColBreaks, nScTab, false, true);
+ for (const auto& rColBreak : aColBreaks)
+ maData.maVerPageBreaks.push_back(rColBreak);
+}
+
+namespace {
+
+class XclExpXmlStartHeaderFooterElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlStartHeaderFooterElementRecord(sal_Int32 const nElement, bool const bDifferentOddEven = false, bool const bDifferentFirst = false)
+ : XclExpXmlElementRecord(nElement), mbDifferentOddEven(bDifferentOddEven), mbDifferentFirst(bDifferentFirst) {}
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ bool mbDifferentOddEven;
+ bool mbDifferentFirst;
+};
+
+}
+
+void XclExpXmlStartHeaderFooterElementRecord::SaveXml(XclExpXmlStream& rStrm)
+{
+ // OOXTODO: we currently only emit oddHeader/oddFooter elements, and
+ // do not support the first/even/odd page distinction.
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ rStream->startElement( mnElement,
+ // OOXTODO: XML_alignWithMargins,
+ XML_differentFirst, mbDifferentFirst ? "true" : "false",
+ XML_differentOddEven, mbDifferentOddEven ? "true" : "false"
+ // OOXTODO: XML_scaleWithDoc
+ );
+}
+
+void XclExpPageSettings::Save( XclExpStream& rStrm )
+{
+ XclExpBoolRecord( EXC_ID_PRINTHEADERS, maData.mbPrintHeadings ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTGRIDLINES, maData.mbPrintGrid ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_GRIDSET, true ).Save( rStrm );
+ XclExpPageBreaks( EXC_ID_HORPAGEBREAKS, maData.maHorPageBreaks, static_cast< sal_uInt16 >( GetXclMaxPos().Col() ) ).Save( rStrm );
+ XclExpPageBreaks( EXC_ID_VERPAGEBREAKS, maData.maVerPageBreaks, static_cast< sal_uInt16 >( GetXclMaxPos().Row() ) ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_LEFTMARGIN, maData.mfLeftMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_RIGHTMARGIN, maData.mfRightMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_TOPMARGIN, maData.mfTopMargin ).Save( rStrm );
+ XclExpDoubleRecord( EXC_ID_BOTTOMMARGIN, maData.mfBottomMargin ).Save( rStrm );
+ XclExpSetup( maData ).Save( rStrm );
+
+ if( (GetBiff() == EXC_BIFF8) && maData.mxBrushItem )
+ if( const Graphic* pGraphic = maData.mxBrushItem->GetGraphic() )
+ XclExpImgData( *pGraphic, EXC_ID8_IMGDATA ).Save( rStrm );
+}
+
+void XclExpPageSettings::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpXmlStartSingleElementRecord( XML_printOptions ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTHEADERS, maData.mbPrintHeadings, XML_headings ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_PRINTGRIDLINES, maData.mbPrintGrid, XML_gridLines ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_GRIDSET, true, XML_gridLinesSet ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter, XML_horizontalCentered ).SaveXml( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter, XML_verticalCentered ).SaveXml( rStrm );
+ XclExpXmlEndSingleElementRecord().SaveXml( rStrm ); // XML_printOptions
+
+ XclExpXmlStartSingleElementRecord( XML_pageMargins ).SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_LEFTMARGIN, maData.mfLeftMargin ).SetAttribute( XML_left )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_RIGHTMARGIN, maData.mfRightMargin ).SetAttribute( XML_right )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_TOPMARGIN, maData.mfTopMargin ).SetAttribute( XML_top )->SaveXml( rStrm );
+ XclExpDoubleRecord( EXC_ID_BOTTOMMARGIN, maData.mfBottomMargin ).SetAttribute( XML_bottom )->SaveXml( rStrm );
+ XclExpDoubleRecord( 0, maData.mfHeaderMargin).SetAttribute( XML_header )->SaveXml( rStrm );
+ XclExpDoubleRecord( 0, maData.mfFooterMargin).SetAttribute( XML_footer )->SaveXml( rStrm );
+ XclExpXmlEndSingleElementRecord().SaveXml( rStrm ); // XML_pageMargins
+
+ XclExpSetup( maData ).SaveXml( rStrm );
+
+ XclExpXmlStartHeaderFooterElementRecord(XML_headerFooter, maData.mbUseEvenHF, maData.mbUseFirstHF).SaveXml(rStrm);
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).SaveXml( rStrm );
+ if (maData.mbUseEvenHF)
+ {
+ XclExpHeaderFooter( EXC_ID_HEADER_EVEN, maData.maHeaderEven ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER_EVEN, maData.maFooterEven ).SaveXml( rStrm );
+ }
+ if (maData.mbUseFirstHF)
+ {
+ XclExpHeaderFooter( EXC_ID_HEADER_FIRST, maData.maHeaderFirst ).SaveXml( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER_FIRST, maData.maFooterFirst ).SaveXml( rStrm );
+ }
+ XclExpXmlEndElementRecord( XML_headerFooter ).SaveXml( rStrm );
+
+ XclExpPageBreaks( EXC_ID_HORPAGEBREAKS, maData.maHorPageBreaks,
+ static_cast< sal_uInt16 >( GetXclMaxPos().Col() ) ).SaveXml( rStrm );
+ XclExpPageBreaks( EXC_ID_VERPAGEBREAKS, maData.maVerPageBreaks,
+ static_cast< sal_uInt16 >( GetXclMaxPos().Row() ) ).SaveXml( rStrm );
+}
+
+XclExpImgData* XclExpPageSettings::getGraphicExport()
+{
+ if( const Graphic* pGraphic = maData.mxBrushItem->GetGraphic() )
+ return new XclExpImgData( *pGraphic, EXC_ID8_IMGDATA );
+
+ return nullptr;
+}
+
+XclExpChartPageSettings::XclExpChartPageSettings( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpChartPageSettings::Save( XclExpStream& rStrm )
+{
+ XclExpHeaderFooter( EXC_ID_HEADER, maData.maHeader ).Save( rStrm );
+ XclExpHeaderFooter( EXC_ID_FOOTER, maData.maFooter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_HCENTER, maData.mbHorCenter ).Save( rStrm );
+ XclExpBoolRecord( EXC_ID_VCENTER, maData.mbVerCenter ).Save( rStrm );
+ XclExpSetup( maData ).Save( rStrm );
+ XclExpUInt16Record( EXC_ID_PRINTSIZE, EXC_PRINTSIZE_FULL ).Save( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepivot.cxx b/sc/source/filter/excel/xepivot.cxx
new file mode 100644
index 000000000..34564e30e
--- /dev/null
+++ b/sc/source/filter/excel/xepivot.cxx
@@ -0,0 +1,1702 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xepivot.hxx>
+#include <xehelper.hxx>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+
+#include <algorithm>
+#include <math.h>
+#include <string_view>
+
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <document.hxx>
+#include <dpcache.hxx>
+#include <dpgroup.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dpshttab.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xestring.hxx>
+#include <xelink.hxx>
+#include <dputil.hxx>
+#include <generalfunction.hxx>
+#include <svl/numformat.hxx>
+
+using namespace ::oox;
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
+using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
+using ::com::sun::star::sheet::DataPilotFieldSortInfo;
+using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
+using ::com::sun::star::sheet::DataPilotFieldReference;
+
+// Pivot cache
+
+namespace {
+
+// constants to track occurrence of specific data types
+const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
+const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
+const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
+const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
+
+/** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
+const sal_uInt16 spnPCItemFlags[] =
+{ // STR DBL INT DAT
+ EXC_SXFIELD_DATA_NONE,
+ EXC_SXFIELD_DATA_STR, // x
+ EXC_SXFIELD_DATA_INT, // x
+ EXC_SXFIELD_DATA_STR_INT, // x x
+ EXC_SXFIELD_DATA_DBL, // x
+ EXC_SXFIELD_DATA_STR_DBL, // x x
+ EXC_SXFIELD_DATA_INT, // x x
+ EXC_SXFIELD_DATA_STR_INT, // x x x
+ EXC_SXFIELD_DATA_DATE, // x
+ EXC_SXFIELD_DATA_DATE_STR, // x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x
+ EXC_SXFIELD_DATA_DATE_STR, // x x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x
+ EXC_SXFIELD_DATA_DATE_STR, // x x x
+ EXC_SXFIELD_DATA_DATE_NUM, // x x x
+ EXC_SXFIELD_DATA_DATE_STR // x x x x
+};
+
+} // namespace
+
+XclExpPCItem::XclExpPCItem( const OUString& rText ) :
+ XclExpRecord( (!rText.isEmpty()) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
+ mnTypeFlag( EXC_PCITEM_DATA_STRING )
+{
+ if( !rText.isEmpty() )
+ SetText( rText );
+ else
+ SetEmpty();
+}
+
+XclExpPCItem::XclExpPCItem( double fValue, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXDOUBLE, 8 )
+{
+ SetDouble( fValue, rText );
+ mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
+ EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
+}
+
+XclExpPCItem::XclExpPCItem( const DateTime& rDateTime, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXDATETIME, 8 )
+{
+ SetDateTime( rDateTime, rText );
+ mnTypeFlag = EXC_PCITEM_DATA_DATE;
+}
+
+XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
+ XclExpRecord( EXC_ID_SXINTEGER, 2 ),
+ mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
+{
+ SetInteger( nValue );
+}
+
+XclExpPCItem::XclExpPCItem( bool bValue, const OUString& rText ) :
+ XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
+ mnTypeFlag( EXC_PCITEM_DATA_STRING )
+{
+ SetBool( bValue, rText );
+}
+
+bool XclExpPCItem::EqualsText( std::u16string_view rText ) const
+{
+ return rText.empty() ? IsEmpty() : (GetText() && (*GetText() == rText));
+}
+
+bool XclExpPCItem::EqualsDouble( double fValue ) const
+{
+ return GetDouble() && (*GetDouble() == fValue);
+}
+
+bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
+{
+ return GetDateTime() && (*GetDateTime() == rDateTime);
+}
+
+bool XclExpPCItem::EqualsBool( bool bValue ) const
+{
+ return GetBool() && (*GetBool() == bValue);
+}
+
+void XclExpPCItem::WriteBody( XclExpStream& rStrm )
+{
+ if( const OUString* pText = GetText() )
+ {
+ rStrm << XclExpString( *pText );
+ }
+ else if( const double* pfValue = GetDouble() )
+ {
+ rStrm << *pfValue;
+ }
+ else if( const sal_Int16* pnValue = GetInteger() )
+ {
+ rStrm << *pnValue;
+ }
+ else if( const DateTime* pDateTime = GetDateTime() )
+ {
+ sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
+ sal_uInt16 nMonth = pDateTime->GetMonth();
+ sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
+ sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
+ sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
+ sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
+ if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
+ rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
+ }
+ else if( const bool* pbValue = GetBool() )
+ {
+ rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
+ }
+ else
+ {
+ // nothing to do for SXEMPTY
+ OSL_ENSURE( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
+ }
+}
+
+XclExpPCField::XclExpPCField(
+ const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScRange& rRange ) :
+ XclExpRecord( EXC_ID_SXFIELD ),
+ XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
+ XclExpRoot( rRoot ),
+ mnTypeFlags( 0 )
+{
+ // general settings for the standard field, insert all items from source range
+ InitStandardField( rRange );
+
+ // add special settings for inplace numeric grouping
+ if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
+ {
+ if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
+ {
+ if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
+ {
+ const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
+ const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
+ OSL_ENSURE( !rNumInfo.mbEnable || !rDateInfo.mbEnable,
+ "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
+
+ if( rNumInfo.mbEnable )
+ InitNumGroupField( rDPObj, rNumInfo );
+ else if( rDateInfo.mbEnable )
+ InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
+ }
+ }
+ }
+
+ // final settings (flags, item numbers)
+ Finalize();
+}
+
+XclExpPCField::XclExpPCField(
+ const XclExpRoot& rRoot, sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
+ XclExpRecord( EXC_ID_SXFIELD ),
+ XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
+ XclExpRoot( rRoot ),
+ mnTypeFlags( 0 )
+{
+ // add base field info (always using first base field, not predecessor of this field) ***
+ OSL_ENSURE( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
+ "XclExpPCField::FillFromGroup - wrong base cache field" );
+ maFieldInfo.maName = rGroupDim.GetGroupDimName();
+ maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
+
+ // add standard group info or date group info
+ const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
+ if( rDateInfo.mbEnable && (rGroupDim.GetDatePart() != 0) )
+ InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
+ else
+ InitStdGroupField( rBaseField, rGroupDim );
+
+ // final settings (flags, item numbers)
+ Finalize();
+}
+
+XclExpPCField::~XclExpPCField()
+{
+}
+
+void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
+{
+ OSL_ENSURE( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
+ "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+ maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
+}
+
+sal_uInt16 XclExpPCField::GetItemCount() const
+{
+ return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
+}
+
+const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return GetVisItemList().GetRecord( nItemIdx );
+}
+
+sal_uInt16 XclExpPCField::GetItemIndex( std::u16string_view rItemName ) const
+{
+ const XclExpPCItemList& rItemList = GetVisItemList();
+ for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
+ if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
+ return static_cast< sal_uInt16 >( nPos );
+ return EXC_PC_NOITEM;
+}
+
+std::size_t XclExpPCField::GetIndexSize() const
+{
+ return Has16BitIndexes() ? 2 : 1;
+}
+
+void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
+{
+ // only standard fields write item indexes
+ if( nSrcRow < maIndexVec.size() )
+ {
+ sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
+ if( Has16BitIndexes() )
+ rStrm << nIndex;
+ else
+ rStrm << static_cast< sal_uInt8 >( nIndex );
+ }
+}
+
+void XclExpPCField::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
+ // SXFIELD
+ XclExpRecord::Save( rStrm );
+ // SXFDBTYPE
+ XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
+ // list of grouping items
+ maGroupItemList.Save( rStrm );
+ // SXGROUPINFO
+ WriteSxgroupinfo( rStrm );
+ // SXNUMGROUP and additional grouping items (grouping limit settings)
+ WriteSxnumgroup( rStrm );
+ // list of original items
+ maOrigItemList.Save( rStrm );
+}
+
+// private --------------------------------------------------------------------
+
+const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
+{
+ OSL_ENSURE( IsStandardField() == maGroupItemList.IsEmpty(),
+ "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
+ return IsStandardField() ? maOrigItemList : maGroupItemList;
+}
+
+void XclExpPCField::InitStandardField( const ScRange& rRange )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
+ OSL_ENSURE( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
+
+ ScDocument& rDoc = GetDoc();
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // field name is in top cell of the range
+ ScAddress aPos( rRange.aStart );
+ maFieldInfo.maName = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
+ // #i76047# maximum field name length in pivot cache is 255
+ if (maFieldInfo.maName.getLength() > EXC_PC_MAXSTRLEN)
+ maFieldInfo.maName = maFieldInfo.maName.copy(0, EXC_PC_MAXSTRLEN);
+
+ // loop over all cells, create pivot cache items
+ for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
+ {
+ OUString aText = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
+ if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
+ {
+ double fValue = rDoc.GetValue( aPos );
+ SvNumFormatType nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( rDoc.GetNonThreadedContext(), aPos ) );
+ if( nFmtType == SvNumFormatType::LOGICAL )
+ InsertOrigBoolItem( fValue != 0, aText );
+ else if( nFmtType & SvNumFormatType::DATETIME )
+ InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ), aText );
+ else
+ InsertOrigDoubleItem( fValue, aText );
+ }
+ else
+ {
+ InsertOrigTextItem( aText );
+ }
+ }
+}
+
+void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
+{
+ OSL_ENSURE( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
+
+ maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
+ maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
+
+ // loop over all groups of this field
+ for( tools::Long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
+ {
+ const ScDPSaveGroupItem& rGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx );
+ // the index of the new item containing the grouping name
+ sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
+ // loop over all elements of one group
+ for( size_t nElemIdx = 0, nElemCount = rGroupItem.GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
+ {
+ if (const OUString* pElemName = rGroupItem.GetElementByIndex(nElemIdx))
+ {
+ // try to find the item that is part of the group in the base field
+ sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
+ if( nBaseItemIdx < maFieldInfo.mnBaseItems )
+ {
+ // add group name item only if there are any valid base items
+ if( nGroupItemIdx == EXC_PC_NOITEM )
+ nGroupItemIdx = InsertGroupItem( new XclExpPCItem( rGroupItem.GetGroupName() ) );
+ maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
+ }
+ }
+ }
+ }
+
+ // add items and base item indexes of all ungrouped elements
+ for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
+ // items that are not part of a group still have the EXC_PC_NOITEM entry
+ if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
+ // try to find the base item
+ if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
+ // create a clone of the base item, insert its index into item order list
+ maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
+}
+
+void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
+ OSL_ENSURE( rNumInfo.mbEnable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
+
+ // new field type, date type, limit settings (min/max/step/auto)
+ if( rNumInfo.mbDateValues )
+ {
+ // special case: group by days with step count
+ meFieldType = EXC_PCFIELD_DATEGROUP;
+ maNumGroupInfo.SetScDateType( css::sheet::DataPilotFieldGroupBy::DAYS );
+ SetDateGroupLimit( rNumInfo, true );
+ }
+ else
+ {
+ meFieldType = EXC_PCFIELD_NUMGROUP;
+ maNumGroupInfo.SetNumType();
+ SetNumGroupLimit( rNumInfo );
+ }
+
+ // generate visible items
+ InsertNumDateGroupItems( rDPObj, rNumInfo );
+}
+
+void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
+{
+ OSL_ENSURE( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
+ OSL_ENSURE( rDateInfo.mbEnable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
+
+ // new field type
+ meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
+
+ // date type, limit settings (min/max/step/auto)
+ maNumGroupInfo.SetScDateType( nDatePart );
+ SetDateGroupLimit( rDateInfo, false );
+
+ // generate visible items
+ InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
+}
+
+void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
+{
+ OSL_ENSURE( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
+ maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
+}
+
+void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
+{
+ size_t nItemIdx = maOrigItemList.GetSize();
+ maOrigItemList.AppendNewRecord( pNewItem );
+ InsertItemArrayIndex( nItemIdx );
+ mnTypeFlags |= pNewItem->GetTypeFlag();
+}
+
+void XclExpPCField::InsertOrigTextItem( const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ // #i76047# maximum item text length in pivot cache is 255
+ OUString aShortText = rText.copy( 0, ::std::min(rText.getLength(), EXC_PC_MAXSTRLEN ) );
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( aShortText ) );
+}
+
+void XclExpPCField::InsertOrigDoubleItem( double fValue, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( fValue, rText ) );
+}
+
+void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( rDateTime, rText ) );
+}
+
+void XclExpPCField::InsertOrigBoolItem( bool bValue, const OUString& rText )
+{
+ size_t nPos = 0;
+ bool bFound = false;
+ for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
+ if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) )
+ InsertItemArrayIndex( nPos );
+ if( !bFound )
+ InsertOrigItem( new XclExpPCItem( bValue, rText ) );
+}
+
+sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
+{
+ maGroupItemList.AppendNewRecord( pNewItem );
+ return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
+}
+
+void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
+{
+ OSL_ENSURE( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
+ const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
+ if(!pSrcDesc)
+ return;
+
+ // get the string collection with original source elements
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ const ScDPDimensionSaveData* pDimData = nullptr;
+ if (pSaveData)
+ pDimData = pSaveData->GetExistingDimensionData();
+
+ const ScDPCache* pCache = pSrcDesc->CreateCache(pDimData);
+ if (!pCache)
+ return;
+
+ ScSheetDPData aDPData(&GetDoc(), *pSrcDesc, *pCache);
+ tools::Long nDim = GetFieldIndex();
+ // get the string collection with generated grouping elements
+ ScDPNumGroupDimension aTmpDim( rNumInfo );
+ if( nDatePart != 0 )
+ aTmpDim.SetDateDimension();
+ const std::vector<SCROW>& aMemberIds = aTmpDim.GetNumEntries(
+ static_cast<SCCOL>(nDim), pCache);
+ for (SCROW nMemberId : aMemberIds)
+ {
+ const ScDPItemData* pData = aDPData.GetMemberById(nDim, nMemberId);
+ if ( pData )
+ {
+ OUString aStr = pCache->GetFormattedString(nDim, *pData, false);
+ InsertGroupItem(new XclExpPCItem(aStr));
+ }
+ }
+}
+
+void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
+{
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.mbAutoStart );
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.mbAutoEnd );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStart ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfEnd ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStep ) );
+}
+
+void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
+{
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.mbAutoStart );
+ ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.mbAutoEnd );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfStart ) ) );
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfEnd ) ) );
+ sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.mfStep, 1, SAL_MAX_INT16 ) : 1;
+ maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
+}
+
+void XclExpPCField::Finalize()
+{
+ // flags
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
+ // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
+ ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
+ /* mnTypeFlags is updated in all Insert***Item() functions. Now the flags
+ for the current combination of item types is added to the flags. */
+ ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
+
+ // item count fields
+ maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
+ maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
+ // maFieldInfo.mnBaseItems set in InitStdGroupField()
+ maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
+}
+
+void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
+{
+ if( IsNumGroupField() || IsDateGroupField() )
+ {
+ // SXNUMGROUP record
+ rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
+ rStrm << maNumGroupInfo;
+ rStrm.EndRecord();
+
+ // limits (min/max/step) for numeric grouping
+ OSL_ENSURE( maNumGroupLimits.GetSize() == 3,
+ "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
+ maNumGroupLimits.Save( rStrm );
+ }
+}
+
+void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
+{
+ OSL_ENSURE( IsStdGroupField() != maGroupOrder.empty(),
+ "XclExpPCField::WriteSxgroupinfo - missing grouping info" );
+ if( IsStdGroupField() && !maGroupOrder.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
+ for( const auto& rItem : maGroupOrder )
+ rStrm << rItem;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPCField::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maFieldInfo;
+}
+
+XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
+ XclExpRoot( rRoot ),
+ mnListIdx( nListIdx ),
+ mbValid( false )
+{
+ // source from sheet only
+ const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc();
+ if(!pSrcDesc)
+ return;
+
+ /* maOrigSrcRange: Range received from the DataPilot object.
+ maExpSrcRange: Range written to the DCONREF record.
+ maDocSrcRange: Range used to get source data from Calc document.
+ This range may be shorter than maExpSrcRange to improve export
+ performance (#i22541#). */
+ maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->GetSourceRange();
+ maSrcRangeName = pSrcDesc->GetRangeName();
+
+ // internal sheet data only
+ SCTAB nScTab = maExpSrcRange.aStart.Tab();
+ if( !((nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab )) )
+ return;
+
+ // ValidateRange() restricts source range to valid Excel limits
+ if( !GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
+ return;
+
+ // #i22541# skip empty cell areas (performance)
+ SCCOL nDocCol1, nDocCol2;
+ SCROW nDocRow1, nDocRow2;
+ GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
+ GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
+ SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
+ SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
+ SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
+ SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
+
+ // #i22541# do not store index list for too big ranges
+ if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
+ ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
+
+ // adjust row indexes, keep one row of empty area to surely have the empty cache item
+ if( nSrcRow1 < nDocRow1 )
+ nSrcRow1 = nDocRow1 - 1;
+ if( nSrcRow2 > nDocRow2 )
+ nSrcRow2 = nDocRow2 + 1;
+
+ maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
+ maDocSrcRange.aStart.SetRow( nSrcRow1 );
+ maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
+ maDocSrcRange.aEnd.SetRow( nSrcRow2 );
+
+ GetDoc().GetName( nScTab, maTabName );
+ maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
+ maPCInfo.mnStrmId = nListIdx + 1;
+ maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
+
+ AddFields( rDPObj );
+
+ mbValid = true;
+}
+
+bool XclExpPivotCache::HasItemIndexList() const
+{
+ return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
+}
+
+sal_uInt16 XclExpPivotCache::GetFieldCount() const
+{
+ return static_cast< sal_uInt16 >( maFieldList.GetSize() );
+}
+
+const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return maFieldList.GetRecord( nFieldIdx );
+}
+
+bool XclExpPivotCache::HasAddFields() const
+{
+ // pivot cache can be shared, if there are no additional cache fields
+ return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
+}
+
+bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
+{
+ /* For now, only sheet sources are supported, therefore it is enough to
+ compare the ScSheetSourceDesc. Later, there should be done more complicated
+ comparisons regarding the source type of rDPObj and this cache. */
+ if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
+ return pSrcDesc->GetSourceRange() == maOrigSrcRange;
+ return false;
+}
+
+void XclExpPivotCache::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
+ // SXIDSTM
+ XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
+ // SXVS
+ XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
+
+ if (!maSrcRangeName.isEmpty())
+ // DCONNAME
+ WriteDConName(rStrm);
+ else
+ // DCONREF
+ WriteDconref(rStrm);
+
+ // create the pivot cache storage stream
+ WriteCacheStream();
+}
+
+void XclExpPivotCache::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
+{
+ AddStdFields( rDPObj );
+ maPCInfo.mnStdFields = GetFieldCount();
+ AddGroupFields( rDPObj );
+ maPCInfo.mnTotalFields = GetFieldCount();
+};
+
+void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
+{
+ // if item index list is not written, used shortened source range (maDocSrcRange) for performance
+ const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
+ // create a standard pivot cache field for each source column
+ for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
+ {
+ ScRange aColRange( rRange );
+ aColRange.aStart.SetCol( nScCol );
+ aColRange.aEnd.SetCol( nScCol );
+ maFieldList.AppendNewRecord( new XclExpPCField(
+ GetRoot(), GetFieldCount(), rDPObj, aColRange ) );
+ }
+}
+
+void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
+{
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ if(!pSaveData)
+ return;
+ const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData();
+ if( !pSaveDimData )
+ return;
+
+ // loop over all existing standard fields to find their group fields
+ for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
+ {
+ if( XclExpPCField* pCurrStdField = maFieldList.GetRecord( nFieldIdx ) )
+ {
+ const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
+ XclExpPCField* pLastGroupField = pCurrStdField;
+ while( pGroupDim )
+ {
+ // insert the new grouping field
+ XclExpPCFieldRef xNewGroupField = new XclExpPCField(
+ GetRoot(), GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField );
+ maFieldList.AppendRecord( xNewGroupField );
+
+ // register new grouping field at current grouping field, building a chain
+ pLastGroupField->SetGroupChildField( *xNewGroupField );
+
+ // next grouping dimension
+ pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
+ pLastGroupField = xNewGroupField.get();
+ }
+ }
+ }
+}
+
+void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
+{
+ XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), u"", &maTabName ) );
+ rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
+ rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
+ << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
+ << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
+ << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
+ << aRef
+ << sal_uInt8( 0 );
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteDConName( XclExpStream& rStrm ) const
+{
+ XclExpString aName(maSrcRangeName);
+ rStrm.StartRecord(EXC_ID_DCONNAME, aName.GetSize() + 2);
+ rStrm << aName << sal_uInt16(0);
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteCacheStream()
+{
+ tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
+ if( !xSvStrm.is() )
+ return;
+
+ XclExpStream aStrm( *xSvStrm, GetRoot() );
+ // SXDB
+ WriteSxdb( aStrm );
+ // SXDBEX
+ WriteSxdbex( aStrm );
+ // field list (SXFIELD and items)
+ maFieldList.Save( aStrm );
+ // index table (list of SXINDEXLIST)
+ WriteSxindexlistList( aStrm );
+ // EOF
+ XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
+}
+
+void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXDB, 21 );
+ rStrm << maPCInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
+ rStrm << EXC_SXDBEX_CREATION_DATE
+ << sal_uInt32( 0 ); // number of SXFORMULA records
+ rStrm.EndRecord();
+}
+
+void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
+{
+ if( !HasItemIndexList() )
+ return;
+
+ std::size_t nRecSize = 0;
+ size_t nPos, nSize = maFieldList.GetSize();
+ for( nPos = 0; nPos < nSize; ++nPos )
+ nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
+
+ for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
+ {
+ rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
+ for( nPos = 0; nPos < nSize; ++nPos )
+ maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
+ rStrm.EndRecord();
+ }
+}
+
+// Pivot table
+
+namespace {
+
+/** Returns a display string for a data field containing the field name and aggregation function. */
+OUString lclGetDataFieldCaption( std::u16string_view rFieldName, ScGeneralFunction eFunc )
+{
+ OUString aCaption;
+
+ TranslateId pResIdx;
+ switch( eFunc )
+ {
+ case ScGeneralFunction::SUM: pResIdx = STR_FUN_TEXT_SUM; break;
+ case ScGeneralFunction::COUNT: pResIdx = STR_FUN_TEXT_COUNT; break;
+ case ScGeneralFunction::AVERAGE: pResIdx = STR_FUN_TEXT_AVG; break;
+ case ScGeneralFunction::MAX: pResIdx = STR_FUN_TEXT_MAX; break;
+ case ScGeneralFunction::MIN: pResIdx = STR_FUN_TEXT_MIN; break;
+ case ScGeneralFunction::PRODUCT: pResIdx = STR_FUN_TEXT_PRODUCT; break;
+ case ScGeneralFunction::COUNTNUMS: pResIdx = STR_FUN_TEXT_COUNT; break;
+ case ScGeneralFunction::STDEV: pResIdx = STR_FUN_TEXT_STDDEV; break;
+ case ScGeneralFunction::STDEVP: pResIdx = STR_FUN_TEXT_STDDEV; break;
+ case ScGeneralFunction::VAR: pResIdx = STR_FUN_TEXT_VAR; break;
+ case ScGeneralFunction::VARP: pResIdx = STR_FUN_TEXT_VAR; break;
+ default:;
+ }
+ if (pResIdx)
+ aCaption = ScResId(pResIdx) + " - ";
+ aCaption += rFieldName;
+ return aCaption;
+}
+
+} // namespace
+
+XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
+ XclExpRecord( EXC_ID_SXVI, 8 ),
+ mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
+{
+ maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
+ maItemInfo.mnCacheIdx = nCacheIdx;
+ maItemInfo.maVisName.mbUseCache = mpCacheItem != nullptr;
+}
+
+XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx ) :
+ XclExpRecord( EXC_ID_SXVI, 8 ),
+ mpCacheItem( nullptr )
+{
+ maItemInfo.mnType = nItemType;
+ maItemInfo.mnCacheIdx = nCacheIdx;
+ maItemInfo.maVisName.mbUseCache = true;
+}
+
+OUString XclExpPTItem::GetItemName() const
+{
+ return mpCacheItem ? mpCacheItem->ConvertToText() : OUString();
+}
+
+void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
+{
+ // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
+ ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
+ // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
+ ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
+
+ // visible name
+ const std::optional<OUString> & pVisName = rSaveMem.GetLayoutName();
+ if (pVisName && *pVisName != GetItemName())
+ maItemInfo.SetVisName(*pVisName);
+}
+
+void XclExpPTItem::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << maItemInfo;
+}
+
+XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
+ mrPTable( rPTable ),
+ mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
+{
+ maFieldInfo.mnCacheIdx = nCacheIdx;
+
+ // create field items
+ if( mpCacheField )
+ for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
+ maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
+ maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
+}
+
+// data access ----------------------------------------------------------------
+
+OUString XclExpPTField::GetFieldName() const
+{
+ return mpCacheField ? mpCacheField->GetFieldName() : OUString();
+}
+
+sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
+{
+ OSL_ENSURE( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
+ // will return 0xFFFF for empty vector -> ok
+ return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
+}
+
+sal_uInt16 XclExpPTField::GetItemIndex( std::u16string_view rName, sal_uInt16 nDefaultIdx ) const
+{
+ for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
+ if( maItemList.GetRecord( nPos )->GetItemName() == rName )
+ return static_cast< sal_uInt16 >( nPos );
+ return nDefaultIdx;
+}
+
+// fill data --------------------------------------------------------------
+
+/**
+ * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
+ * are not escaped at all.
+ */
+static OUString lcl_convertCalcSubtotalName(const OUString& rName)
+{
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rName.getStr();
+ sal_Int32 n = rName.getLength();
+ bool bEscaped = false;
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const sal_Unicode c = p[i];
+ if (!bEscaped && c == '\\')
+ {
+ bEscaped = true;
+ continue;
+ }
+
+ aBuf.append(c);
+ bEscaped = false;
+ }
+ return aBuf.makeStringAndClear();
+}
+
+void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ // orientation
+ DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
+ OSL_ENSURE( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
+ maFieldInfo.AddApiOrient( eOrient );
+
+ // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
+
+ // visible name
+ const std::optional<OUString> & pLayoutName = rSaveDim.GetLayoutName();
+ if (pLayoutName && *pLayoutName != GetFieldName())
+ maFieldInfo.SetVisName(*pLayoutName);
+
+ const std::optional<OUString> & pSubtotalName = rSaveDim.GetSubtotalName();
+ if (pSubtotalName)
+ {
+ OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
+ maFieldExtInfo.mpFieldTotalName = aSubName;
+ }
+
+ // subtotals
+ XclPTSubtotalVec aSubtotals;
+ aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
+ for( tools::Long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
+ aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
+ maFieldInfo.SetSubtotals( aSubtotals );
+
+ // sorting
+ if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
+ {
+ maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
+ if( pSortInfo->Mode == css::sheet::DataPilotFieldSortMode::DATA )
+ maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
+ }
+
+ // auto show
+ if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
+ {
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
+ maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
+ maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
+ maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
+ }
+
+ // layout
+ if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
+ {
+ maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
+ }
+
+ // special page field properties
+ if( eOrient == DataPilotFieldOrientation_PAGE )
+ {
+ maPageInfo.mnField = GetFieldIndex();
+ maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
+ }
+
+ // item properties
+ const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
+ for (const auto& pMember : rMembers)
+ if( XclExpPTItem* pItem = GetItemAcc( pMember->GetName() ) )
+ pItem->SetPropertiesFromMember( *pMember );
+}
+
+void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ maDataInfoVec.emplace_back( );
+ XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
+ rDataInfo.mnField = GetFieldIndex();
+
+ // orientation
+ maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
+
+ // aggregation function
+ ScGeneralFunction eFunc = rSaveDim.GetFunction();
+ rDataInfo.SetApiAggFunc( eFunc );
+
+ // visible name
+ const std::optional<OUString> & pVisName = rSaveDim.GetLayoutName();
+ if (pVisName)
+ rDataInfo.SetVisName(*pVisName);
+ else
+ rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
+
+ // result field reference
+ if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
+ {
+ rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
+ rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
+ if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
+ {
+ rDataInfo.mnRefField = pRefField->GetFieldIndex();
+ if( pFieldRef->ReferenceItemType == css::sheet::DataPilotFieldReferenceItemType::NAMED )
+ rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
+ }
+ }
+}
+
+void XclExpPTField::AppendSubtotalItems()
+{
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
+ if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
+}
+
+// records --------------------------------------------------------------------
+
+void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
+{
+ rStrm << maPageInfo;
+}
+
+void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
+{
+ OSL_ENSURE( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
+ if( nDataInfoIdx < maDataInfoVec.size() )
+ {
+ rStrm.StartRecord( EXC_ID_SXDI, 12 );
+ rStrm << maDataInfoVec[ nDataInfoIdx ];
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPTField::Save( XclExpStream& rStrm )
+{
+ // SXVD
+ WriteSxvd( rStrm );
+ // list of SXVI records
+ maItemList.Save( rStrm );
+ // SXVDEX
+ WriteSxvdex( rStrm );
+}
+
+// private --------------------------------------------------------------------
+
+XclExpPTItem* XclExpPTField::GetItemAcc( std::u16string_view rName )
+{
+ XclExpPTItem* pItem = nullptr;
+ for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
+ if( maItemList.GetRecord( nPos )->GetItemName() == rName )
+ pItem = maItemList.GetRecord( nPos );
+ return pItem;
+}
+
+void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
+{
+ maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE ) );
+ ++maFieldInfo.mnItemCount;
+}
+
+void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVD, 10 );
+ rStrm << maFieldInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
+ rStrm << maFieldExtInfo;
+ rStrm.EndRecord();
+}
+
+XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache ) :
+ XclExpRoot( rRoot ),
+ mrPCache( rPCache ),
+ maDataOrientField( *this, EXC_SXIVD_DATA ),
+ mnOutScTab( 0 ),
+ mbValid( false ),
+ mbFilterBtn( false )
+{
+ const ScRange& rOutScRange = rDPObj.GetOutRange();
+ if( !GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
+ return;
+
+ // DataPilot properties -----------------------------------------------
+
+ // pivot table properties from DP object
+ mnOutScTab = rOutScRange.aStart.Tab();
+ maPTInfo.maTableName = rDPObj.GetName();
+ maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
+
+ maPTViewEx9Info.Init( rDPObj );
+
+ const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
+ if( !pSaveData )
+ return;
+
+ // additional properties from ScDPSaveData
+ SetPropertiesFromDP( *pSaveData );
+
+ // loop over all dimensions ---------------------------------------
+
+ /* 1) Default-construct all pivot table fields for all pivot cache fields. */
+ for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
+ maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
+
+ const ScDPSaveData::DimsType& rDimList = pSaveData->GetDimensions();
+
+ /* 2) First process all data dimensions, they are needed for extended
+ settings of row/column/page fields (sorting/auto show). */
+ for (auto const& iter : rDimList)
+ {
+ if (iter->GetOrientation() == DataPilotFieldOrientation_DATA)
+ SetDataFieldPropertiesFromDim(*iter);
+ }
+
+ /* 3) Row/column/page/hidden fields. */
+ for (auto const& iter : rDimList)
+ {
+ if (iter->GetOrientation() != DataPilotFieldOrientation_DATA)
+ SetFieldPropertiesFromDim(*iter);
+ }
+
+ // Finalize -------------------------------------------------------
+
+ Finalize();
+ mbValid = true;
+}
+
+const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
+{
+ return mrPCache.GetField( nCacheIdx );
+}
+
+const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx );
+}
+
+const XclExpPTField* XclExpPivotTable::GetField( std::u16string_view rName ) const
+{
+ return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
+}
+
+sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
+{
+ auto aIt = std::find_if(maDataFields.begin(), maDataFields.end(),
+ [this, &rName](const XclPTDataFieldPos& rDataField) {
+ const XclExpPTField* pField = GetField( rDataField.first );
+ return pField && pField->GetFieldName() == rName;
+ });
+ if (aIt != maDataFields.end())
+ return static_cast< sal_uInt16 >( std::distance(maDataFields.begin(), aIt) );
+ return nDefaultIdx;
+}
+
+void XclExpPivotTable::Save( XclExpStream& rStrm )
+{
+ if( !mbValid )
+ return;
+
+ // SXVIEW
+ WriteSxview( rStrm );
+ // pivot table fields (SXVD, SXVDEX, and item records)
+ maFieldList.Save( rStrm );
+ // SXIVD records for row and column fields
+ WriteSxivd( rStrm, maRowFields );
+ WriteSxivd( rStrm, maColFields );
+ // SXPI
+ WriteSxpi( rStrm );
+ // list of SXDI records containing data field info
+ WriteSxdiList( rStrm );
+ // SXLI records
+ WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
+ WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
+ // SXEX
+ WriteSxex( rStrm );
+ // QSISXTAG
+ WriteQsiSxTag( rStrm );
+ // SXVIEWEX9
+ WriteSxViewEx9( rStrm );
+}
+
+XclExpPTField* XclExpPivotTable::GetFieldAcc( std::u16string_view rName )
+{
+ XclExpPTField* pField = nullptr;
+ for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
+ if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
+ pField = maFieldList.GetRecord( nPos );
+ return pField;
+}
+
+XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
+{
+ // data field orientation field?
+ if( rSaveDim.IsDataLayout() )
+ return &maDataOrientField;
+
+ // a real dimension
+ OUString aFieldName = ScDPUtil::getSourceDimensionName(rSaveDim.GetName());
+ return aFieldName.isEmpty() ? nullptr : GetFieldAcc(aFieldName);
+}
+
+// fill data --------------------------------------------------------------
+
+void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
+{
+ ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
+ ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
+ ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
+ mbFilterBtn = rSaveData.GetFilterButton();
+ const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
+
+ if (pDim && pDim->GetLayoutName())
+ maPTInfo.maDataName = *pDim->GetLayoutName();
+ else
+ maPTInfo.maDataName = ScResId(STR_PIVOT_DATA);
+}
+
+void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ XclExpPTField* pField = GetFieldAcc( rSaveDim );
+ if(!pField)
+ return;
+
+ // field properties
+ pField->SetPropertiesFromDim( rSaveDim );
+
+ // update the corresponding field position list
+ DataPilotFieldOrientation eOrient = rSaveDim.GetOrientation();
+ sal_uInt16 nFieldIdx = pField->GetFieldIndex();
+ bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
+ bool bMultiData = maDataFields.size() > 1;
+
+ if( bDataLayout && !bMultiData )
+ return;
+
+ switch( eOrient )
+ {
+ case DataPilotFieldOrientation_ROW:
+ maRowFields.push_back( nFieldIdx );
+ if( bDataLayout )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
+ break;
+ case DataPilotFieldOrientation_COLUMN:
+ maColFields.push_back( nFieldIdx );
+ if( bDataLayout )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
+ break;
+ case DataPilotFieldOrientation_PAGE:
+ maPageFields.push_back( nFieldIdx );
+ OSL_ENSURE( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
+ break;
+ case DataPilotFieldOrientation_DATA:
+ OSL_FAIL( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
+ break;
+ default:;
+ }
+}
+
+void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+{
+ if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
+ {
+ // field properties
+ pField->SetDataPropertiesFromDim( rSaveDim );
+ // update the data field position list
+ maDataFields.emplace_back( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() );
+ }
+}
+
+void XclExpPivotTable::Finalize()
+{
+ // field numbers
+ maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
+ maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
+ maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
+ maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
+ maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
+
+ maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
+ maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
+
+ // subtotal items
+ for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
+ maFieldList.GetRecord( nPos )->AppendSubtotalItems();
+
+ // find data field orientation field
+ maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
+ const ScfUInt16Vec* pFieldVec = nullptr;
+ switch( maPTInfo.mnDataAxis )
+ {
+ case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
+ case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
+ }
+
+ if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
+ {
+ ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
+ if( aIt != pFieldVec->end() )
+ maPTInfo.mnDataPos = static_cast< sal_uInt16 >( std::distance(pFieldVec->begin(), aIt) );
+ }
+
+ // single data field is always row oriented
+ if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
+ maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
+
+ // update output range (initialized in ctor)
+ sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
+ sal_uInt32& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
+ sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
+ sal_uInt32& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
+ // exclude page fields from output range
+ rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
+ // exclude filter button from output range
+ if( mbFilterBtn )
+ ++rnXclRow1;
+ // exclude empty row between (filter button and/or page fields) and table
+ if( mbFilterBtn || maPTInfo.mnPageFields )
+ ++rnXclRow1;
+
+ // data area
+ sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
+ sal_uInt32& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
+ rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
+ rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
+ if( maDataFields.empty() )
+ ++rnDataXclRow;
+
+ bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
+ if (bExtraHeaderRow)
+ // Insert an extra row only when there is no column field.
+ ++rnDataXclRow;
+
+ rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
+ rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
+ maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
+ maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
+
+ // first heading
+ maPTInfo.mnFirstHeadRow = rnXclRow1 + 1;
+ if (bExtraHeaderRow)
+ maPTInfo.mnFirstHeadRow += 1;
+}
+
+// records ----------------------------------------------------------------
+
+void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.getLength() + maPTInfo.maDataName.getLength() );
+ rStrm << maPTInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields )
+{
+ if( !rFields.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
+ for( const auto& rField : rFields )
+ rStrm << rField;
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
+{
+ if( !maPageFields.empty() )
+ {
+ rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
+ rStrm.SetSliceSize( 6 );
+ for( const auto& rPageField : maPageFields )
+ {
+ XclExpPTFieldRef xField = maFieldList.GetRecord( rPageField );
+ if( xField )
+ xField->WriteSxpiEntry( rStrm );
+ }
+ rStrm.EndRecord();
+ }
+}
+
+void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
+{
+ for( const auto& [rFieldIdx, rDataInfoIdx] : maDataFields )
+ {
+ XclExpPTFieldRef xField = maFieldList.GetRecord( rFieldIdx );
+ if( xField )
+ xField->WriteSxdi( rStrm, rDataInfoIdx );
+ }
+}
+
+void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount )
+{
+ if( nLineCount <= 0 )
+ return;
+
+ std::size_t nLineSize = 8 + 2 * nIndexCount;
+ rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
+
+ /* Excel expects the records to be filled completely, do not
+ set a segment size... */
+// rStrm.SetSliceSize( nLineSize );
+
+ for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ // Excel XP needs a partly initialized SXLI record
+ rStrm << sal_uInt16( 0 ) // number of equal index entries
+ << EXC_SXVI_TYPE_DATA
+ << nIndexCount
+ << EXC_SXLI_DEFAULTFLAGS;
+ rStrm.WriteZeroBytes( 2 * nIndexCount );
+ }
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( EXC_ID_SXEX, 24 );
+ rStrm << maPTExtInfo;
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
+{
+ rStrm.StartRecord( 0x0802, 32 );
+
+ sal_uInt16 const nRecordType = 0x0802;
+ sal_uInt16 const nDummyFlags = 0x0000;
+ sal_uInt16 const nTableType = 1; // 0 = query table : 1 = pivot table
+
+ rStrm << nRecordType << nDummyFlags << nTableType;
+
+ // General flags
+ sal_uInt16 const nFlags = 0x0001;
+#if 0
+ // for doc purpose
+ sal_uInt16 nFlags = 0x0000;
+ bool bEnableRefresh = true;
+ bool bPCacheInvalid = false;
+ bool bOlapPTReport = false;
+
+ if (bEnableRefresh) nFlags |= 0x0001;
+ if (bPCacheInvalid) nFlags |= 0x0002;
+ if (bOlapPTReport) nFlags |= 0x0004;
+#endif
+ rStrm << nFlags;
+
+ // Feature-specific options. The value differs depending on the table
+ // type, but we assume the table type is always pivot table.
+ sal_uInt32 const nOptions = 0x00000000;
+#if 0
+ // documentation for which bit is for what
+ bool bNoStencil = false;
+ bool bHideTotal = false;
+ bool bEmptyRows = false;
+ bool bEmptyCols = false;
+ if (bNoStencil) nOptions |= 0x00000001;
+ if (bHideTotal) nOptions |= 0x00000002;
+ if (bEmptyRows) nOptions |= 0x00000008;
+ if (bEmptyCols) nOptions |= 0x00000010;
+#endif
+ rStrm << nOptions;
+
+ sal_uInt8 eXclVer = 0; // Excel2000
+ sal_uInt8 const nOffsetBytes = 16;
+ rStrm << eXclVer // version table last refreshed
+ << eXclVer // minimum version to refresh
+ << nOffsetBytes
+ << eXclVer; // first version created
+
+ rStrm << XclExpString(maPTInfo.maTableName);
+ rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
+
+ rStrm.EndRecord();
+}
+
+void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
+{
+ // Until we sync the autoformat ids only export if using grid header layout
+ // That could only have been set via xls import so far.
+ if ( 0 == maPTViewEx9Info.mnGridLayout )
+ {
+ rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
+ rStrm << maPTViewEx9Info;
+ rStrm.EndRecord();
+ }
+}
+
+namespace {
+
+const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
+
+/** Record wrapper class to write the pivot caches or pivot tables. */
+class XclExpPivotRecWrapper : public XclExpRecordBase
+{
+public:
+ explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
+ virtual void Save( XclExpStream& rStrm ) override;
+private:
+ XclExpPivotTableManager& mrPTMgr;
+ SCTAB mnScTab;
+};
+
+XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
+ mrPTMgr( rPTMgr ),
+ mnScTab( nScTab )
+{
+}
+
+void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
+{
+ if( mnScTab == EXC_PTMGR_PIVOTCACHES )
+ mrPTMgr.WritePivotCaches( rStrm );
+ else
+ mrPTMgr.WritePivotTables( rStrm, mnScTab );
+}
+
+} // namespace
+
+XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpPivotTableManager::CreatePivotTables()
+{
+ if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
+ for( size_t nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
+ {
+ ScDPObject& rDPObj = (*pDPColl)[ nDPObj ];
+ if( const XclExpPivotCache* pPCache = CreatePivotCache( rDPObj ) )
+ maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), rDPObj, *pPCache ) );
+ }
+}
+
+XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
+{
+ return new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES );
+}
+
+XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
+{
+ return new XclExpPivotRecWrapper( *this, nScTab );
+}
+
+void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
+{
+ maPCacheList.Save( rStrm );
+}
+
+void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
+{
+ for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
+ if( xPTable->GetScTab() == nScTab )
+ xPTable->Save( rStrm );
+ }
+}
+
+const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
+{
+ // try to find a pivot cache with the same data source
+ /* #i25110# In Excel, the pivot cache contains additional fields
+ (i.e. grouping info, calculated fields). If the passed DataPilot object
+ or the found cache contains this data, do not share the cache with
+ multiple pivot tables. */
+ if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
+ {
+ const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
+ // no dimension save data at all or save data does not contain grouping info
+ if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
+ {
+ // check all existing pivot caches
+ for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpPivotCache* pPCache = maPCacheList.GetRecord( nPos );
+ // pivot cache does not have grouping info and source data is equal
+ if( !pPCache->HasAddFields() && pPCache->HasEqualDataSource( rDPObj ) )
+ return pPCache;
+ }
+ }
+ }
+
+ // create a new pivot cache
+ sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
+ XclExpPivotCacheRef xNewPCache = new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx );
+ if( xNewPCache->IsValid() )
+ {
+ maPCacheList.AppendRecord( xNewPCache.get() );
+ return xNewPCache.get();
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xepivotxml.cxx b/sc/source/filter/excel/xepivotxml.cxx
new file mode 100644
index 000000000..ecc39caae
--- /dev/null
+++ b/sc/source/filter/excel/xepivotxml.cxx
@@ -0,0 +1,1187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <xepivotxml.hxx>
+#include <dpcache.hxx>
+#include <dpdimsave.hxx>
+#include <dpitemdata.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dputil.hxx>
+#include <document.hxx>
+#include <generalfunction.hxx>
+#include <unonames.hxx>
+#include <xestyle.hxx>
+#include <xeroot.hxx>
+
+#include <o3tl/temporary.hxx>
+#include <o3tl/safeint.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <sal/log.hxx>
+#include <sax/tools/converter.hxx>
+#include <sax/fastattribs.hxx>
+#include <svl/numformat.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotOutputRangeType.hpp>
+#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+
+#include <vector>
+
+using namespace oox;
+using namespace com::sun::star;
+
+namespace {
+
+void savePivotCacheRecordsXml( XclExpXmlStream& rStrm, const ScDPCache& rCache )
+{
+ SCROW nCount = rCache.GetDataSize();
+ size_t nFieldCount = rCache.GetFieldCount();
+
+ sax_fastparser::FSHelperPtr& pRecStrm = rStrm.GetCurrentStream();
+ pRecStrm->startElement(XML_pivotCacheRecords,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ XML_count, OString::number(static_cast<tools::Long>(nCount)));
+
+ for (SCROW i = 0; i < nCount; ++i)
+ {
+ pRecStrm->startElement(XML_r);
+ for (size_t nField = 0; nField < nFieldCount; ++nField)
+ {
+ const ScDPCache::IndexArrayType* pArray = rCache.GetFieldIndexArray(nField);
+ assert(pArray);
+ assert(o3tl::make_unsigned(i) < pArray->size());
+
+ // We are using XML_x reference (like: <x v="0"/>), instead of values here (eg: <s v="No Discount"/>).
+ // That's why in SavePivotCacheXml method, we need to list all items.
+ pRecStrm->singleElement(XML_x, XML_v, OString::number((*pArray)[i]));
+ }
+ pRecStrm->endElement(XML_r);
+ }
+
+ pRecStrm->endElement(XML_pivotCacheRecords);
+}
+
+const char* toOOXMLAxisType( sheet::DataPilotFieldOrientation eOrient )
+{
+ switch (eOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ return "axisCol";
+ case sheet::DataPilotFieldOrientation_ROW:
+ return "axisRow";
+ case sheet::DataPilotFieldOrientation_PAGE:
+ return "axisPage";
+ case sheet::DataPilotFieldOrientation_DATA:
+ return "axisValues";
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ default:
+ ;
+ }
+
+ return "";
+}
+
+const char* toOOXMLSubtotalType(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM:
+ return "sum";
+ case ScGeneralFunction::COUNT:
+ return "count";
+ case ScGeneralFunction::AVERAGE:
+ return "average";
+ case ScGeneralFunction::MAX:
+ return "max";
+ case ScGeneralFunction::MIN:
+ return "min";
+ case ScGeneralFunction::PRODUCT:
+ return "product";
+ case ScGeneralFunction::COUNTNUMS:
+ return "countNums";
+ case ScGeneralFunction::STDEV:
+ return "stdDev";
+ case ScGeneralFunction::STDEVP:
+ return "stdDevp";
+ case ScGeneralFunction::VAR:
+ return "var";
+ case ScGeneralFunction::VARP:
+ return "varp";
+ default:
+ ;
+ }
+ return nullptr;
+}
+
+}
+
+XclExpXmlPivotCaches::XclExpXmlPivotCaches( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot) {}
+
+void XclExpXmlPivotCaches::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pWorkbookStrm = rStrm.GetCurrentStream();
+ pWorkbookStrm->startElement(XML_pivotCaches);
+
+ for (size_t i = 0, n = maCaches.size(); i < n; ++i)
+ {
+ const Entry& rEntry = maCaches[i];
+
+ sal_Int32 nCacheId = i + 1;
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pPCStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheDefinition", nCacheId),
+ XclXmlUtils::GetStreamName(nullptr, "pivotCache/pivotCacheDefinition", nCacheId),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotCacheDefinition"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
+ &aRelId);
+
+ pWorkbookStrm->singleElement(XML_pivotCache,
+ XML_cacheId, OString::number(nCacheId),
+ FSNS(XML_r, XML_id), aRelId.toUtf8());
+
+ rStrm.PushStream(pPCStrm);
+ SavePivotCacheXml(rStrm, rEntry, nCacheId);
+ rStrm.PopStream();
+ }
+
+ pWorkbookStrm->endElement(XML_pivotCaches);
+}
+
+void XclExpXmlPivotCaches::SetCaches( std::vector<Entry>&& rCaches )
+{
+ maCaches = std::move(rCaches);
+}
+
+bool XclExpXmlPivotCaches::HasCaches() const
+{
+ return !maCaches.empty();
+}
+
+const XclExpXmlPivotCaches::Entry* XclExpXmlPivotCaches::GetCache( sal_Int32 nCacheId ) const
+{
+ if (nCacheId <= 0)
+ // cache ID is 1-based.
+ return nullptr;
+
+ size_t nPos = nCacheId - 1;
+ if (nPos >= maCaches.size())
+ return nullptr;
+
+ return &maCaches[nPos];
+}
+
+namespace {
+/**
+ * Create combined date and time string according the requirements of Excel.
+ * A single point in time can be represented by concatenating a complete date expression,
+ * the letter T as a delimiter, and a valid time expression. For example, "2007-04-05T14:30".
+ *
+ * fSerialDateTime - a number representing the number of days since 1900-Jan-0 (integer portion of the number),
+ * plus a fractional portion of a 24 hour day (fractional portion of the number).
+ */
+OUString GetExcelFormattedDate( double fSerialDateTime, const SvNumberFormatter& rFormatter )
+{
+ // tdf#125055: properly round the value to seconds when truncating nanoseconds below
+ constexpr double fHalfSecond = 1 / 86400.0 * 0.5;
+ css::util::DateTime aUDateTime
+ = (DateTime(rFormatter.GetNullDate()) + fSerialDateTime + fHalfSecond).GetUNODateTime();
+ // We need to reset nanoseconds, to avoid string like: "1982-02-18T16:04:47.999999849"
+ aUDateTime.NanoSeconds = 0;
+ OUStringBuffer sBuf;
+ ::sax::Converter::convertDateTime(sBuf, aUDateTime, nullptr, true);
+ return sBuf.makeStringAndClear();
+}
+
+// Excel seems to expect different order of group item values; we need to rearrange elements
+// to output "<date1" first, then elements, then ">date2" last.
+// Since ScDPItemData::DateFirst is -1, ScDPItemData::DateLast is 10000, and other date group
+// items would fit between those in order (like 0 = Jan, 1 = Feb, etc.), we can simply sort
+// the items by value.
+std::vector<OUString> SortGroupItems(const ScDPCache& rCache, tools::Long nDim)
+{
+ struct ItemData
+ {
+ sal_Int32 nVal;
+ const ScDPItemData* pData;
+ };
+ std::vector<ItemData> aDataToSort;
+ ScfInt32Vec aGIIds;
+ rCache.GetGroupDimMemberIds(nDim, aGIIds);
+ for (sal_Int32 id : aGIIds)
+ {
+ const ScDPItemData* pGIData = rCache.GetItemDataById(nDim, id);
+ if (pGIData->GetType() == ScDPItemData::GroupValue)
+ {
+ auto aGroupVal = pGIData->GetGroupValue();
+ aDataToSort.push_back({ aGroupVal.mnValue, pGIData });
+ }
+ }
+ std::sort(aDataToSort.begin(), aDataToSort.end(),
+ [](const ItemData& a, const ItemData& b) { return a.nVal < b.nVal; });
+
+ std::vector<OUString> aSortedResult;
+ for (const auto& el : aDataToSort)
+ {
+ aSortedResult.push_back(rCache.GetFormattedString(nDim, *el.pData, false));
+ }
+ return aSortedResult;
+}
+} // namespace
+
+void XclExpXmlPivotCaches::SavePivotCacheXml( XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCounter )
+{
+ assert(rEntry.mpCache);
+ const ScDPCache& rCache = *rEntry.mpCache;
+
+ sax_fastparser::FSHelperPtr& pDefStrm = rStrm.GetCurrentStream();
+
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pRecStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotCache/", "pivotCacheRecords", nCounter),
+ XclXmlUtils::GetStreamName(nullptr, "pivotCacheRecords", nCounter),
+ pDefStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotCacheRecords"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheRecords"),
+ &aRelId);
+
+ rStrm.PushStream(pRecStrm);
+ savePivotCacheRecordsXml(rStrm, rCache);
+ rStrm.PopStream();
+
+ pDefStrm->startElement(XML_pivotCacheDefinition,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ FSNS(XML_r, XML_id), aRelId.toUtf8(),
+ XML_recordCount, OString::number(rEntry.mpCache->GetDataSize()),
+ XML_createdVersion, "3"); // MS Excel 2007, tdf#112936: setting version number makes MSO to handle the pivot table differently
+
+ pDefStrm->startElement(XML_cacheSource, XML_type, "worksheet");
+
+ OUString aSheetName;
+ GetDoc().GetName(rEntry.maSrcRange.aStart.Tab(), aSheetName);
+ pDefStrm->singleElement(XML_worksheetSource,
+ XML_ref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rEntry.maSrcRange),
+ XML_sheet, aSheetName.toUtf8());
+
+ pDefStrm->endElement(XML_cacheSource);
+
+ size_t nCount = rCache.GetFieldCount();
+ const size_t nGroupFieldCount = rCache.GetGroupFieldCount();
+ pDefStrm->startElement(XML_cacheFields,
+ XML_count, OString::number(static_cast<tools::Long>(nCount + nGroupFieldCount)));
+
+ auto WriteFieldGroup = [this, &rCache, pDefStrm](size_t i, size_t base) {
+ const sal_Int32 nDatePart = rCache.GetGroupType(i);
+ if (!nDatePart)
+ return;
+ OString sGroupBy;
+ switch (nDatePart)
+ {
+ case sheet::DataPilotFieldGroupBy::SECONDS:
+ sGroupBy = "seconds";
+ break;
+ case sheet::DataPilotFieldGroupBy::MINUTES:
+ sGroupBy = "minutes";
+ break;
+ case sheet::DataPilotFieldGroupBy::HOURS:
+ sGroupBy = "hours";
+ break;
+ case sheet::DataPilotFieldGroupBy::DAYS:
+ sGroupBy = "days";
+ break;
+ case sheet::DataPilotFieldGroupBy::MONTHS:
+ sGroupBy = "months";
+ break;
+ case sheet::DataPilotFieldGroupBy::QUARTERS:
+ sGroupBy = "quarters";
+ break;
+ case sheet::DataPilotFieldGroupBy::YEARS:
+ sGroupBy = "years";
+ break;
+ }
+
+ // fieldGroup element
+ pDefStrm->startElement(XML_fieldGroup, XML_base, OString::number(base));
+
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // rangePr element
+ const ScDPNumGroupInfo* pGI = rCache.GetNumGroupInfo(i);
+ auto pGroupAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pGroupAttList->add(XML_groupBy, sGroupBy);
+ // Possible TODO: find out when to write autoStart attribute for years grouping
+ pGroupAttList->add(XML_startDate, GetExcelFormattedDate(pGI->mfStart, rFormatter).toUtf8());
+ pGroupAttList->add(XML_endDate, GetExcelFormattedDate(pGI->mfEnd, rFormatter).toUtf8());
+ if (pGI->mfStep)
+ pGroupAttList->add(XML_groupInterval, OString::number(pGI->mfStep));
+ pDefStrm->singleElement(XML_rangePr, pGroupAttList);
+
+ // groupItems element
+ auto aElemVec = SortGroupItems(rCache, i);
+ pDefStrm->startElement(XML_groupItems, XML_count, OString::number(aElemVec.size()));
+ for (const auto& sElem : aElemVec)
+ {
+ pDefStrm->singleElement(XML_s, XML_v, sElem.toUtf8());
+ }
+ pDefStrm->endElement(XML_groupItems);
+ pDefStrm->endElement(XML_fieldGroup);
+ };
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ OUString aName = rCache.GetDimensionName(i);
+
+ pDefStrm->startElement(XML_cacheField,
+ XML_name, aName.toUtf8(),
+ XML_numFmtId, OString::number(0));
+
+ const ScDPCache::ScDPItemDataVec& rFieldItems = rCache.GetDimMemberValues(i);
+
+ std::set<ScDPItemData::Type> aDPTypes;
+ double fMin = std::numeric_limits<double>::infinity(), fMax = -std::numeric_limits<double>::infinity();
+ bool isValueInteger = true;
+ bool isContainsDate = rCache.IsDateDimension(i);
+ bool isLongText = false;
+ for (const auto& rFieldItem : rFieldItems)
+ {
+ ScDPItemData::Type eType = rFieldItem.GetType();
+ // tdf#123939 : error and string are same for cache; if both are present, keep only one
+ if (eType == ScDPItemData::Error)
+ eType = ScDPItemData::String;
+ aDPTypes.insert(eType);
+ if (eType == ScDPItemData::Value)
+ {
+ double fVal = rFieldItem.GetValue();
+ fMin = std::min(fMin, fVal);
+ fMax = std::max(fMax, fVal);
+
+ // Check if all values are integers
+ if (isValueInteger && (modf(fVal, &o3tl::temporary(double())) != 0.0))
+ {
+ isValueInteger = false;
+ }
+ }
+ else if (eType == ScDPItemData::String && !isLongText)
+ {
+ isLongText = rFieldItem.GetString().getLength() > 255;
+ }
+ }
+
+ auto pAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ // TODO In same cases, disable listing of items, as it is done in MS Excel.
+ // Exporting savePivotCacheRecordsXml method needs to be updated accordingly
+ //bool bListItems = true;
+
+ std::set<ScDPItemData::Type> aDPTypesWithoutBlank = aDPTypes;
+ aDPTypesWithoutBlank.erase(ScDPItemData::Empty);
+
+ const bool isContainsString = aDPTypesWithoutBlank.count(ScDPItemData::String) > 0;
+ const bool isContainsBlank = aDPTypes.count(ScDPItemData::Empty) > 0;
+ const bool isContainsNumber
+ = !isContainsDate && aDPTypesWithoutBlank.count(ScDPItemData::Value) > 0;
+ bool isContainsNonDate = !(isContainsDate && aDPTypesWithoutBlank.size() <= 1);
+
+ // XML_containsSemiMixedTypes possible values:
+ // 1 - (Default) at least one text value, or can also contain a mix of other data types and blank values,
+ // or blank values only
+ // 0 - the field does not have a mix of text and other values
+ if (!(isContainsString || (aDPTypes.size() > 1) || (isContainsBlank && aDPTypesWithoutBlank.empty())))
+ pAttList->add(XML_containsSemiMixedTypes, ToPsz10(false));
+
+ if (!isContainsNonDate)
+ pAttList->add(XML_containsNonDate, ToPsz10(false));
+
+ if (isContainsDate)
+ pAttList->add(XML_containsDate, ToPsz10(true));
+
+ // default for containsString field is true, so we are writing only when is false
+ if (!isContainsString)
+ pAttList->add(XML_containsString, ToPsz10(false));
+
+ if (isContainsBlank)
+ pAttList->add(XML_containsBlank, ToPsz10(true));
+
+ // XML_containsMixedType possible values:
+ // 1 - field contains more than one data type
+ // 0 - (Default) only one data type. The field can still contain blank values (that's why we are using aDPTypesWithoutBlank)
+ if (aDPTypesWithoutBlank.size() > 1)
+ pAttList->add(XML_containsMixedTypes, ToPsz10(true));
+
+ // If field contain mixed types (Date and Numbers), MS Excel is saving only "minDate" and "maxDate" and not "minValue" and "maxValue"
+ // Example how Excel is saving mixed Date and Numbers:
+ // <sharedItems containsSemiMixedTypes="0" containsDate="1" containsString="0" containsMixedTypes="1" minDate="1900-01-03T22:26:04" maxDate="1900-01-07T14:02:04" />
+ // Example how Excel is saving Dates only:
+ // <sharedItems containsSemiMixedTypes="0" containsNonDate="0" containsDate="1" containsString="0" minDate="1903-08-24T07:40:48" maxDate="2024-05-23T07:12:00"/>
+ if (isContainsNumber)
+ pAttList->add(XML_containsNumber, ToPsz10(true));
+
+ if (isValueInteger && isContainsNumber)
+ pAttList->add(XML_containsInteger, ToPsz10(true));
+
+
+ // Number type fields could be mixed with blank types, and it shouldn't be treated as listed items.
+ // Example:
+ // <cacheField name="employeeID" numFmtId="0">
+ // <sharedItems containsString="0" containsBlank="1" containsNumber="1" containsInteger="1" minValue="35" maxValue="89"/>
+ // </cacheField>
+ if (isContainsNumber)
+ {
+ pAttList->add(XML_minValue, OString::number(fMin));
+ pAttList->add(XML_maxValue, OString::number(fMax));
+ }
+
+ if (isContainsDate)
+ {
+ pAttList->add(XML_minDate, GetExcelFormattedDate(fMin, GetFormatter()).toUtf8());
+ pAttList->add(XML_maxDate, GetExcelFormattedDate(fMax, GetFormatter()).toUtf8());
+ }
+
+ //if (bListItems) // see TODO above
+ {
+ pAttList->add(XML_count, OString::number(static_cast<tools::Long>(rFieldItems.size())));
+ }
+
+ if (isLongText)
+ {
+ pAttList->add(XML_longText, ToPsz10(true));
+ }
+
+ pDefStrm->startElement(XML_sharedItems, pAttList);
+
+ //if (bListItems) // see TODO above
+ {
+ for (const ScDPItemData& rItem : rFieldItems)
+ {
+ switch (rItem.GetType())
+ {
+ case ScDPItemData::String:
+ pDefStrm->singleElement(XML_s, XML_v, rItem.GetString().toUtf8());
+ break;
+ case ScDPItemData::Value:
+ if (isContainsDate)
+ {
+ pDefStrm->singleElement(XML_d,
+ XML_v, GetExcelFormattedDate(rItem.GetValue(), GetFormatter()).toUtf8());
+ }
+ else
+ pDefStrm->singleElement(XML_n,
+ XML_v, OString::number(rItem.GetValue()));
+ break;
+ case ScDPItemData::Empty:
+ pDefStrm->singleElement(XML_m);
+ break;
+ case ScDPItemData::Error:
+ pDefStrm->singleElement(XML_e,
+ XML_v, rItem.GetString().toUtf8());
+ break;
+ case ScDPItemData::GroupValue: // Should not happen here!
+ case ScDPItemData::RangeStart:
+ // TODO : What do we do with these types?
+ pDefStrm->singleElement(XML_m);
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ pDefStrm->endElement(XML_sharedItems);
+
+ WriteFieldGroup(i, i);
+
+ pDefStrm->endElement(XML_cacheField);
+ }
+
+ ScDPObject* pDPObject
+ = rCache.GetAllReferences().empty() ? nullptr : *rCache.GetAllReferences().begin();
+
+ for (size_t i = nCount; pDPObject && i < nCount + nGroupFieldCount; ++i)
+ {
+ const OUString aName = pDPObject->GetDimName(i, o3tl::temporary(bool()));
+ // tdf#126748: DPObject might not reference all group fields, when there are several
+ // DPObjects referencing this cache. Trying to get a dimension data for a field not used
+ // in a given DPObject will give nullptr, and dereferencing it then will crash. To avoid
+ // the crash, until there's a correct method to find the names of group fields in cache,
+ // just skip the fields, creating bad cache data, which is of course a temporary hack.
+ // TODO: reimplement the whole block to get the names from another source, not from first
+ // cache reference.
+ if (aName.isEmpty())
+ break;
+
+ ScDPSaveData* pSaveData = pDPObject->GetSaveData();
+ assert(pSaveData);
+
+ const ScDPSaveGroupDimension* pDim = pSaveData->GetDimensionData()->GetNamedGroupDim(aName);
+ assert(pDim);
+
+ const SCCOL nBase = rCache.GetDimensionIndex(pDim->GetSourceDimName());
+ assert(nBase >= 0);
+
+ pDefStrm->startElement(XML_cacheField, XML_name, aName.toUtf8(), XML_numFmtId,
+ OString::number(0), XML_databaseField, ToPsz10(false));
+ WriteFieldGroup(i, nBase);
+ pDefStrm->endElement(XML_cacheField);
+ }
+
+ pDefStrm->endElement(XML_cacheFields);
+
+ pDefStrm->endElement(XML_pivotCacheDefinition);
+}
+
+XclExpXmlPivotTableManager::XclExpXmlPivotTableManager( const XclExpRoot& rRoot ) :
+ XclExpRoot(rRoot), maCaches(rRoot) {}
+
+void XclExpXmlPivotTableManager::Initialize()
+{
+ ScDocument& rDoc = GetDoc();
+ if (!rDoc.HasPivotTable())
+ // No pivot table to export.
+ return;
+
+ ScDPCollection* pDPColl = rDoc.GetDPCollection();
+ if (!pDPColl)
+ return;
+
+ // Update caches from DPObject
+ for (size_t i = 0; i < pDPColl->GetCount(); ++i)
+ {
+ ScDPObject& rDPObj = (*pDPColl)[i];
+ rDPObj.SyncAllDimensionMembers();
+ (void)rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::TABLE);
+ }
+
+ // Go through the caches first.
+
+ std::vector<XclExpXmlPivotCaches::Entry> aCaches;
+ const ScDPCollection::SheetCaches& rSheetCaches = pDPColl->GetSheetCaches();
+ const std::vector<ScRange>& rRanges = rSheetCaches.getAllRanges();
+ for (const auto & rRange : rRanges)
+ {
+ const ScDPCache* pCache = rSheetCaches.getExistingCache(rRange);
+ if (!pCache)
+ continue;
+
+ // Get all pivot objects that reference this cache, and set up an
+ // object to cache ID mapping.
+ const ScDPCache::ScDPObjectSet& rRefs = pCache->GetAllReferences();
+ for (const auto& rRef : rRefs)
+ maCacheIdMap.emplace(rRef, aCaches.size()+1);
+
+ XclExpXmlPivotCaches::Entry aEntry;
+ aEntry.mpCache = pCache;
+ aEntry.maSrcRange = rRange;
+ aCaches.push_back(aEntry); // Cache ID equals position + 1.
+ }
+
+ // TODO : Handle name and database caches as well.
+
+ for (size_t i = 0, n = pDPColl->GetCount(); i < n; ++i)
+ {
+ const ScDPObject& rDPObj = (*pDPColl)[i];
+
+ // Get the cache ID for this pivot table.
+ CacheIdMapType::iterator itCache = maCacheIdMap.find(&rDPObj);
+ if (itCache == maCacheIdMap.end())
+ // No cache ID found. Something is wrong here...
+ continue;
+
+ sal_Int32 nCacheId = itCache->second;
+ SCTAB nTab = rDPObj.GetOutRange().aStart.Tab();
+
+ TablesType::iterator it = m_Tables.find(nTab);
+ if (it == m_Tables.end())
+ {
+ // Insert a new instance for this sheet index.
+ std::pair<TablesType::iterator, bool> r =
+ m_Tables.insert(std::make_pair(nTab, std::make_unique<XclExpXmlPivotTables>(GetRoot(), maCaches)));
+ it = r.first;
+ }
+
+ XclExpXmlPivotTables *const p = it->second.get();
+ p->AppendTable(&rDPObj, nCacheId, i+1);
+ }
+
+ maCaches.SetCaches(std::move(aCaches));
+}
+
+XclExpXmlPivotCaches& XclExpXmlPivotTableManager::GetCaches()
+{
+ return maCaches;
+}
+
+XclExpXmlPivotTables* XclExpXmlPivotTableManager::GetTablesBySheet( SCTAB nTab )
+{
+ TablesType::iterator const it = m_Tables.find(nTab);
+ return it == m_Tables.end() ? nullptr : it->second.get();
+}
+
+XclExpXmlPivotTables::Entry::Entry( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId ) :
+ mpTable(pTable), mnCacheId(nCacheId), mnPivotId(nPivotId) {}
+
+XclExpXmlPivotTables::XclExpXmlPivotTables( const XclExpRoot& rRoot, const XclExpXmlPivotCaches& rCaches ) :
+ XclExpRoot(rRoot), mrCaches(rCaches) {}
+
+void XclExpXmlPivotTables::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& pWSStrm = rStrm.GetCurrentStream(); // worksheet stream
+
+ for (const auto& rTable : maTables)
+ {
+ const ScDPObject& rObj = *rTable.mpTable;
+ sal_Int32 nCacheId = rTable.mnCacheId;
+ sal_Int32 nPivotId = rTable.mnPivotId;
+
+ sax_fastparser::FSHelperPtr pPivotStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/pivotTables/", "pivotTable", nPivotId),
+ XclXmlUtils::GetStreamName(nullptr, "../pivotTables/pivotTable", nPivotId),
+ pWSStrm->getOutputStream(),
+ CREATE_XL_CONTENT_TYPE("pivotTable"),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotTable"));
+
+ rStrm.PushStream(pPivotStrm);
+ SavePivotTableXml(rStrm, rObj, nCacheId);
+ rStrm.PopStream();
+ }
+}
+
+namespace {
+
+struct DataField
+{
+ tools::Long mnPos; // field index in pivot cache.
+ const ScDPSaveDimension* mpDim;
+
+ DataField( tools::Long nPos, const ScDPSaveDimension* pDim ) : mnPos(nPos), mpDim(pDim) {}
+};
+
+/** Returns an OOXML subtotal function name string. See ECMA-376-1:2016 18.18.43 */
+OString GetSubtotalFuncName(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM: return "sum";
+ case ScGeneralFunction::COUNT: return "count";
+ case ScGeneralFunction::AVERAGE: return "avg";
+ case ScGeneralFunction::MAX: return "max";
+ case ScGeneralFunction::MIN: return "min";
+ case ScGeneralFunction::PRODUCT: return "product";
+ case ScGeneralFunction::COUNTNUMS: return "countA";
+ case ScGeneralFunction::STDEV: return "stdDev";
+ case ScGeneralFunction::STDEVP: return "stdDevP";
+ case ScGeneralFunction::VAR: return "var";
+ case ScGeneralFunction::VARP: return "varP";
+ default:;
+ }
+ return "default";
+}
+
+sal_Int32 GetSubtotalAttrToken(ScGeneralFunction eFunc)
+{
+ switch (eFunc)
+ {
+ case ScGeneralFunction::SUM: return XML_sumSubtotal;
+ case ScGeneralFunction::COUNT: return XML_countSubtotal;
+ case ScGeneralFunction::AVERAGE: return XML_avgSubtotal;
+ case ScGeneralFunction::MAX: return XML_maxSubtotal;
+ case ScGeneralFunction::MIN: return XML_minSubtotal;
+ case ScGeneralFunction::PRODUCT: return XML_productSubtotal;
+ case ScGeneralFunction::COUNTNUMS: return XML_countASubtotal;
+ case ScGeneralFunction::STDEV: return XML_stdDevSubtotal;
+ case ScGeneralFunction::STDEVP: return XML_stdDevPSubtotal;
+ case ScGeneralFunction::VAR: return XML_varSubtotal;
+ case ScGeneralFunction::VARP: return XML_varPSubtotal;
+ default:;
+ }
+ return XML_defaultSubtotal;
+}
+
+// An item is expected to contain sequences of css::xml::FastAttribute and css::xml::Attribute
+void WriteGrabBagItemToStream(XclExpXmlStream& rStrm, sal_Int32 tokenId, const css::uno::Any& rItem)
+{
+ css::uno::Sequence<css::uno::Any> aSeqs;
+ if(!(rItem >>= aSeqs))
+ return;
+
+ auto& pStrm = rStrm.GetCurrentStream();
+ pStrm->write("<")->writeId(tokenId);
+
+ css::uno::Sequence<css::xml::FastAttribute> aFastSeq;
+ css::uno::Sequence<css::xml::Attribute> aUnkSeq;
+ for (const auto& a : std::as_const(aSeqs))
+ {
+ if (a >>= aFastSeq)
+ {
+ for (const auto& rAttr : std::as_const(aFastSeq))
+ rStrm.WriteAttributes(rAttr.Token, rAttr.Value);
+ }
+ else if (a >>= aUnkSeq)
+ {
+ for (const auto& rAttr : std::as_const(aUnkSeq))
+ pStrm->write(" ")
+ ->write(rAttr.Name)
+ ->write("=\"")
+ ->writeEscaped(rAttr.Value)
+ ->write("\"");
+ }
+ }
+
+ pStrm->write("/>");
+}
+}
+
+void XclExpXmlPivotTables::SavePivotTableXml( XclExpXmlStream& rStrm, const ScDPObject& rDPObj, sal_Int32 nCacheId )
+{
+ typedef std::unordered_map<OUString, long> NameToIdMapType;
+
+ const XclExpXmlPivotCaches::Entry* pCacheEntry = mrCaches.GetCache(nCacheId);
+ if (!pCacheEntry)
+ // Something is horribly wrong. Check your logic.
+ return;
+
+ const ScDPCache& rCache = *pCacheEntry->mpCache;
+
+ const ScDPSaveData& rSaveData = *rDPObj.GetSaveData();
+
+ size_t nFieldCount = rCache.GetFieldCount() + rCache.GetGroupFieldCount();
+ std::vector<const ScDPSaveDimension*> aCachedDims;
+ NameToIdMapType aNameToIdMap;
+
+ aCachedDims.reserve(nFieldCount);
+ for (size_t i = 0; i < nFieldCount; ++i)
+ {
+ OUString aName = const_cast<ScDPObject&>(rDPObj).GetDimName(i, o3tl::temporary(bool()));
+ aNameToIdMap.emplace(aName, aCachedDims.size());
+ const ScDPSaveDimension* pDim = rSaveData.GetExistingDimensionByName(aName);
+ aCachedDims.push_back(pDim);
+ }
+
+ std::vector<tools::Long> aRowFields;
+ std::vector<tools::Long> aColFields;
+ std::vector<tools::Long> aPageFields;
+ std::vector<DataField> aDataFields;
+
+ tools::Long nDataDimCount = rSaveData.GetDataDimensionCount();
+ // Use dimensions in the save data to get their correct ordering.
+ // Dimension order here is significant as they specify the order of
+ // appearance in each axis.
+ const ScDPSaveData::DimsType& rDims = rSaveData.GetDimensions();
+ bool bTabularMode = false;
+ for (const auto & i : rDims)
+ {
+ const ScDPSaveDimension& rDim = *i;
+
+ tools::Long nPos = -1; // position in cache
+ if (rDim.IsDataLayout())
+ nPos = -2; // Excel uses an index of -2 to indicate a data layout field.
+ else
+ {
+ OUString aSrcName = ScDPUtil::getSourceDimensionName(rDim.GetName());
+ NameToIdMapType::iterator it = aNameToIdMap.find(aSrcName);
+ if (it != aNameToIdMap.end())
+ nPos = it->second;
+
+ if (nPos == -1)
+ continue;
+
+ if (!aCachedDims[nPos])
+ continue;
+ }
+
+ sheet::DataPilotFieldOrientation eOrient = rDim.GetOrientation();
+
+ switch (eOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ if (nPos == -2 && nDataDimCount <= 1)
+ break;
+ aColFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ aRowFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ aPageFields.push_back(nPos);
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ aDataFields.emplace_back(nPos, &rDim);
+ break;
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ default:
+ ;
+ }
+ if(rDim.GetLayoutInfo())
+ bTabularMode |= (rDim.GetLayoutInfo()->LayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+ }
+
+ sax_fastparser::FSHelperPtr& pPivotStrm = rStrm.GetCurrentStream();
+ pPivotStrm->startElement(XML_pivotTableDefinition,
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ XML_name, rDPObj.GetName().toUtf8(),
+ XML_cacheId, OString::number(nCacheId),
+ XML_applyNumberFormats, ToPsz10(false),
+ XML_applyBorderFormats, ToPsz10(false),
+ XML_applyFontFormats, ToPsz10(false),
+ XML_applyPatternFormats, ToPsz10(false),
+ XML_applyAlignmentFormats, ToPsz10(false),
+ XML_applyWidthHeightFormats, ToPsz10(false),
+ XML_dataCaption, "Values",
+ XML_useAutoFormatting, ToPsz10(false),
+ XML_itemPrintTitles, ToPsz10(true),
+ XML_indent, ToPsz10(false),
+ XML_outline, ToPsz10(!bTabularMode),
+ XML_outlineData, ToPsz10(!bTabularMode),
+ XML_compact, ToPsz10(false),
+ XML_compactData, ToPsz10(false));
+
+ // NB: Excel's range does not include page field area (if any).
+ ScRange aOutRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::TABLE);
+
+ sal_Int32 nFirstHeaderRow = rDPObj.GetHeaderLayout() ? 2 : 1;
+ sal_Int32 nFirstDataRow = 2;
+ sal_Int32 nFirstDataCol = 1;
+ ScRange aResRange = rDPObj.GetOutputRangeByType(sheet::DataPilotOutputRangeType::RESULT);
+
+ if (!aOutRange.IsValid())
+ aOutRange = rDPObj.GetOutRange();
+
+ if (aOutRange.IsValid() && aResRange.IsValid())
+ {
+ nFirstDataRow = aResRange.aStart.Row() - aOutRange.aStart.Row();
+ nFirstDataCol = aResRange.aStart.Col() - aOutRange.aStart.Col();
+ }
+
+ pPivotStrm->write("<")->writeId(XML_location);
+ rStrm.WriteAttributes(XML_ref,
+ XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aOutRange),
+ XML_firstHeaderRow, OUString::number(nFirstHeaderRow),
+ XML_firstDataRow, OUString::number(nFirstDataRow),
+ XML_firstDataCol, OUString::number(nFirstDataCol));
+
+ if (!aPageFields.empty())
+ {
+ rStrm.WriteAttributes(XML_rowPageCount, OUString::number(static_cast<tools::Long>(aPageFields.size())));
+ rStrm.WriteAttributes(XML_colPageCount, OUString::number(1));
+ }
+
+ pPivotStrm->write("/>");
+
+ // <pivotFields> - It must contain all fields in the pivot cache even if
+ // only some of them are used in the pivot table. The order must be as
+ // they appear in the cache.
+
+ pPivotStrm->startElement(XML_pivotFields,
+ XML_count, OString::number(static_cast<tools::Long>(aCachedDims.size())));
+
+ for (size_t i = 0; i < nFieldCount; ++i)
+ {
+ const ScDPSaveDimension* pDim = aCachedDims[i];
+ if (!pDim)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ continue;
+ }
+
+ bool bDimInTabularMode = false;
+ if(pDim->GetLayoutInfo())
+ bDimInTabularMode = (pDim->GetLayoutInfo()->LayoutMode == sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT);
+
+ sheet::DataPilotFieldOrientation eOrient = pDim->GetOrientation();
+
+ if (eOrient == sheet::DataPilotFieldOrientation_HIDDEN)
+ {
+ if(bDimInTabularMode)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false),
+ XML_outline, ToPsz10(false));
+ }
+ else
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ }
+ continue;
+ }
+
+ if (eOrient == sheet::DataPilotFieldOrientation_DATA)
+ {
+ if(bDimInTabularMode)
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false),
+ XML_outline, ToPsz10(false));
+ }
+ else
+ {
+ pPivotStrm->singleElement(XML_pivotField,
+ XML_dataField, ToPsz10(true),
+ XML_compact, ToPsz10(false),
+ XML_showAll, ToPsz10(false));
+ }
+ continue;
+ }
+
+ // Dump field items.
+ std::vector<ScDPLabelData::Member> aMembers;
+ {
+ // We need to get the members in actual order, getting which requires non-const reference here
+ auto& dpo = const_cast<ScDPObject&>(rDPObj);
+ dpo.GetMembers(i, dpo.GetUsedHierarchy(i), aMembers);
+ }
+
+ std::vector<OUString> aCacheFieldItems;
+ if (i < rCache.GetFieldCount() && !rCache.GetGroupType(i))
+ {
+ for (const auto& it : rCache.GetDimMemberValues(i))
+ {
+ OUString sFormattedName;
+ if (it.HasStringData() || it.IsEmpty())
+ sFormattedName = it.GetString();
+ else
+ sFormattedName = const_cast<ScDPObject&>(rDPObj).GetFormattedString(
+ pDim->GetName(), it.GetValue());
+ aCacheFieldItems.push_back(sFormattedName);
+ }
+ }
+ else
+ {
+ aCacheFieldItems = SortGroupItems(rCache, i);
+ }
+ // The pair contains the member index in cache and if it is hidden
+ std::vector< std::pair<size_t, bool> > aMemberSequence;
+ std::set<size_t> aUsedCachePositions;
+ for (const auto & rMember : aMembers)
+ {
+ auto it = std::find(aCacheFieldItems.begin(), aCacheFieldItems.end(), rMember.maName);
+ if (it != aCacheFieldItems.end())
+ {
+ size_t nCachePos = static_cast<size_t>(std::distance(aCacheFieldItems.begin(), it));
+ auto aInserted = aUsedCachePositions.insert(nCachePos);
+ if (aInserted.second)
+ aMemberSequence.emplace_back(std::make_pair(nCachePos, !rMember.mbVisible));
+ }
+ }
+ // Now add all remaining cache items as hidden
+ for (size_t nItem = 0; nItem < aCacheFieldItems.size(); ++nItem)
+ {
+ if (aUsedCachePositions.find(nItem) == aUsedCachePositions.end())
+ aMemberSequence.emplace_back(nItem, true);
+ }
+
+ // tdf#125086: check if this field *also* appears in Data region
+ bool bAppearsInData = false;
+ {
+ OUString aSrcName = ScDPUtil::getSourceDimensionName(pDim->GetName());
+ const auto it = std::find_if(
+ aDataFields.begin(), aDataFields.end(), [&aSrcName](const DataField& rDataField) {
+ OUString aThisName
+ = ScDPUtil::getSourceDimensionName(rDataField.mpDim->GetName());
+ return aThisName == aSrcName;
+ });
+ if (it != aDataFields.end())
+ bAppearsInData = true;
+ }
+
+ auto pAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pAttList->add(XML_axis, toOOXMLAxisType(eOrient));
+ if (bAppearsInData)
+ pAttList->add(XML_dataField, ToPsz10(true));
+ pAttList->add(XML_compact, ToPsz10(false));
+ pAttList->add(XML_showAll, ToPsz10(false));
+
+ tools::Long nSubTotalCount = pDim->GetSubTotalsCount();
+ std::vector<OString> aSubtotalSequence;
+ bool bHasDefaultSubtotal = false;
+ for (tools::Long nSubTotal = 0; nSubTotal < nSubTotalCount; ++nSubTotal)
+ {
+ ScGeneralFunction eFunc = pDim->GetSubTotalFunc(nSubTotal);
+ aSubtotalSequence.push_back(GetSubtotalFuncName(eFunc));
+ sal_Int32 nAttToken = GetSubtotalAttrToken(eFunc);
+ if (nAttToken == XML_defaultSubtotal)
+ bHasDefaultSubtotal = true;
+ else if (!pAttList->hasAttribute(nAttToken))
+ pAttList->add(nAttToken, ToPsz10(true));
+ }
+ // XML_defaultSubtotal is true by default; only write it if it's false
+ if (!bHasDefaultSubtotal)
+ pAttList->add(XML_defaultSubtotal, ToPsz10(false));
+
+ if(bDimInTabularMode)
+ pAttList->add( XML_outline, ToPsz10(false));
+ pPivotStrm->startElement(XML_pivotField, pAttList);
+
+ pPivotStrm->startElement(XML_items,
+ XML_count, OString::number(static_cast<tools::Long>(aMemberSequence.size() + aSubtotalSequence.size())));
+
+ for (const auto & nMember : aMemberSequence)
+ {
+ auto pItemAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ if (nMember.second)
+ pItemAttList->add(XML_h, ToPsz10(true));
+ pItemAttList->add(XML_x, OString::number(static_cast<tools::Long>(nMember.first)));
+ pPivotStrm->singleElement(XML_item, pItemAttList);
+ }
+
+ for (const OString& sSubtotal : aSubtotalSequence)
+ {
+ pPivotStrm->singleElement(XML_item, XML_t, sSubtotal);
+ }
+
+ pPivotStrm->endElement(XML_items);
+ pPivotStrm->endElement(XML_pivotField);
+ }
+
+ pPivotStrm->endElement(XML_pivotFields);
+
+ // <rowFields>
+
+ if (!aRowFields.empty())
+ {
+ pPivotStrm->startElement(XML_rowFields,
+ XML_count, OString::number(static_cast<tools::Long>(aRowFields.size())));
+
+ for (const auto& rRowField : aRowFields)
+ {
+ pPivotStrm->singleElement(XML_field, XML_x, OString::number(rRowField));
+ }
+
+ pPivotStrm->endElement(XML_rowFields);
+ }
+
+ // <rowItems>
+
+ // <colFields>
+
+ if (!aColFields.empty())
+ {
+ pPivotStrm->startElement(XML_colFields,
+ XML_count, OString::number(static_cast<tools::Long>(aColFields.size())));
+
+ for (const auto& rColField : aColFields)
+ {
+ pPivotStrm->singleElement(XML_field, XML_x, OString::number(rColField));
+ }
+
+ pPivotStrm->endElement(XML_colFields);
+ }
+
+ // <colItems>
+
+ // <pageFields>
+
+ if (!aPageFields.empty())
+ {
+ pPivotStrm->startElement(XML_pageFields,
+ XML_count, OString::number(static_cast<tools::Long>(aPageFields.size())));
+
+ for (const auto& rPageField : aPageFields)
+ {
+ pPivotStrm->singleElement(XML_pageField,
+ XML_fld, OString::number(rPageField),
+ XML_hier, OString::number(-1)); // TODO : handle this correctly.
+ }
+
+ pPivotStrm->endElement(XML_pageFields);
+ }
+
+ // <dataFields>
+
+ if (!aDataFields.empty())
+ {
+ css::uno::Reference<css::container::XNameAccess> xDimsByName;
+ if (auto xDimSupplier = const_cast<ScDPObject&>(rDPObj).GetSource())
+ xDimsByName = xDimSupplier->getDimensions();
+
+ pPivotStrm->startElement(XML_dataFields,
+ XML_count, OString::number(static_cast<tools::Long>(aDataFields.size())));
+
+ for (const auto& rDataField : aDataFields)
+ {
+ tools::Long nDimIdx = rDataField.mnPos;
+ assert(nDimIdx == -2 || aCachedDims[nDimIdx]); // the loop above should have screened for NULL's, skip check for -2 "data field"
+ const ScDPSaveDimension& rDim = *rDataField.mpDim;
+ std::optional<OUString> pName = rDim.GetLayoutName();
+ // tdf#124651: despite being optional in CT_DataField according to ECMA-376 Part 1,
+ // Excel (at least 2016) seems to insist on the presence of "name" attribute in
+ // dataField element.
+ // tdf#124881: try to create a meaningful name; don't use empty string.
+ if (!pName)
+ pName = ScDPUtil::getDisplayedMeasureName(
+ rDim.GetName(), ScDPUtil::toSubTotalFunc(rDim.GetFunction()));
+ auto pItemAttList = sax_fastparser::FastSerializerHelper::createAttrList();
+ pItemAttList->add(XML_name, pName->toUtf8());
+ pItemAttList->add(XML_fld, OString::number(nDimIdx));
+ const char* pSubtotal = toOOXMLSubtotalType(rDim.GetFunction());
+ if (pSubtotal)
+ pItemAttList->add(XML_subtotal, pSubtotal);
+ if (xDimsByName)
+ {
+ try
+ {
+ css::uno::Reference<css::beans::XPropertySet> xDimProps(
+ xDimsByName->getByName(rDim.GetName()), uno::UNO_QUERY_THROW);
+ css::uno::Any aVal = xDimProps->getPropertyValue(SC_UNONAME_NUMFMT);
+ sal_uInt32 nScNumFmt = aVal.get<sal_uInt32>();
+ sal_uInt16 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
+ pItemAttList->add(XML_numFmtId, OString::number(nXclNumFmt));
+ }
+ catch (uno::Exception&)
+ {
+ SAL_WARN("sc.filter",
+ "Couldn't get number format for data field " << rDim.GetName());
+ // Just skip exporting number format
+ }
+ }
+ pPivotStrm->singleElement(XML_dataField, pItemAttList);
+ }
+
+ pPivotStrm->endElement(XML_dataFields);
+ }
+
+ // Now add style info (use grab bag, or just a set which is default on Excel 2007 through 2016)
+ if (const auto [bHas, aVal] = rDPObj.GetInteropGrabBagValue("pivotTableStyleInfo"); bHas)
+ WriteGrabBagItemToStream(rStrm, XML_pivotTableStyleInfo, aVal);
+ else
+ pPivotStrm->singleElement(XML_pivotTableStyleInfo, XML_name, "PivotStyleLight16",
+ XML_showRowHeaders, "1", XML_showColHeaders, "1",
+ XML_showRowStripes, "0", XML_showColStripes, "0",
+ XML_showLastColumn, "1");
+
+ OUString aBuf = "../pivotCache/pivotCacheDefinition" +
+ OUString::number(nCacheId) +
+ ".xml";
+
+ rStrm.addRelation(
+ pPivotStrm->getOutputStream(),
+ CREATE_OFFICEDOC_RELATION_TYPE("pivotCacheDefinition"),
+ aBuf);
+
+ pPivotStrm->endElement(XML_pivotTableDefinition);
+}
+
+void XclExpXmlPivotTables::AppendTable( const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId )
+{
+ maTables.emplace_back(pTable, nCacheId, nPivotId);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xerecord.cxx b/sc/source/filter/excel/xerecord.cxx
new file mode 100644
index 000000000..8778d75b5
--- /dev/null
+++ b/sc/source/filter/excel/xerecord.cxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xerecord.hxx>
+#include <xeroot.hxx>
+#include <xltools.hxx>
+
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::oox;
+
+// Base classes to export Excel records =======================================
+
+XclExpRecordBase::~XclExpRecordBase()
+{
+}
+
+void XclExpRecordBase::Save( XclExpStream& /*rStrm*/ )
+{
+}
+
+void XclExpRecordBase::SaveXml( XclExpXmlStream& /*rStrm*/ )
+{
+}
+
+XclExpDelegatingRecord::XclExpDelegatingRecord( XclExpRecordBase* pRecord ) :
+ mpRecord( pRecord )
+{
+}
+
+XclExpDelegatingRecord::~XclExpDelegatingRecord()
+{
+ // Do Nothing; we use Delegating Record for other objects we "know" will
+ // survive...
+}
+
+void XclExpDelegatingRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mpRecord )
+ mpRecord->SaveXml( rStrm );
+}
+
+XclExpXmlElementRecord::XclExpXmlElementRecord(sal_Int32 const nElement)
+ : mnElement( nElement )
+{
+}
+
+XclExpXmlElementRecord::~XclExpXmlElementRecord()
+{
+}
+
+XclExpXmlStartElementRecord::XclExpXmlStartElementRecord(sal_Int32 const nElement)
+ : XclExpXmlElementRecord(nElement)
+{
+}
+
+XclExpXmlStartElementRecord::~XclExpXmlStartElementRecord()
+{
+}
+
+void XclExpXmlStartElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ // TODO: no generic way to add attributes here, but it appears to
+ // not be needed yet
+ rStream->startElement(mnElement);
+}
+
+XclExpXmlEndElementRecord::XclExpXmlEndElementRecord( sal_Int32 nElement )
+ : XclExpXmlElementRecord( nElement )
+{
+}
+
+XclExpXmlEndElementRecord::~XclExpXmlEndElementRecord()
+{
+}
+
+void XclExpXmlEndElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->endElement( mnElement );
+}
+
+XclExpXmlStartSingleElementRecord::XclExpXmlStartSingleElementRecord(
+ sal_Int32 const nElement)
+ : XclExpXmlElementRecord( nElement )
+{
+}
+
+XclExpXmlStartSingleElementRecord::~XclExpXmlStartSingleElementRecord()
+{
+}
+
+void XclExpXmlStartSingleElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStream = rStrm.GetCurrentStream();
+ rStream->write( "<" )->writeId( mnElement );
+}
+
+XclExpXmlEndSingleElementRecord::XclExpXmlEndSingleElementRecord()
+{
+}
+
+XclExpXmlEndSingleElementRecord::~XclExpXmlEndSingleElementRecord()
+{
+}
+
+void XclExpXmlEndSingleElementRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->write( "/>" );
+}
+
+XclExpRecord::XclExpRecord( sal_uInt16 nRecId, std::size_t nRecSize ) :
+ mnRecSize( nRecSize ),
+ mnRecId( nRecId )
+{
+}
+
+XclExpRecord::~XclExpRecord()
+{
+}
+
+void XclExpRecord::SetRecHeader( sal_uInt16 nRecId, std::size_t nRecSize )
+{
+ SetRecId( nRecId );
+ SetRecSize( nRecSize );
+}
+
+void XclExpRecord::WriteBody( XclExpStream& /*rStrm*/ )
+{
+}
+
+void XclExpRecord::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mnRecId != EXC_ID_UNKNOWN, "XclExpRecord::Save - record ID uninitialized" );
+ rStrm.StartRecord( mnRecId, mnRecSize );
+ WriteBody( rStrm );
+ rStrm.EndRecord();
+}
+
+template<>
+void XclExpValueRecord<double>::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mnAttribute == -1 )
+ return;
+ rStrm.WriteAttributes(mnAttribute, OUString::number(maValue));
+}
+
+void XclExpBoolRecord::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >( mbValue ? 1 : 0 );
+}
+
+void XclExpBoolRecord::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mnAttribute == -1 )
+ return;
+
+ rStrm.WriteAttributes(
+ // HACK: HIDEOBJ (excdoc.cxx) should be its own object to handle XML_showObjects
+ mnAttribute, mnAttribute == XML_showObjects ? "all" : ToPsz( mbValue ));
+}
+
+XclExpDummyRecord::XclExpDummyRecord( sal_uInt16 nRecId, const void* pRecData, std::size_t nRecSize ) :
+ XclExpRecord( nRecId )
+{
+ SetData( pRecData, nRecSize );
+}
+
+void XclExpDummyRecord::SetData( const void* pRecData, std::size_t nRecSize )
+{
+ mpData = pRecData;
+ SetRecSize( pRecData ? nRecSize : 0 );
+}
+
+void XclExpDummyRecord::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.Write( mpData, GetRecSize() );
+}
+
+// Future records =============================================================
+
+XclExpFutureRecord::XclExpFutureRecord( XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize ) :
+ XclExpRecord( nRecId, nRecSize ),
+ meRecType( eRecType )
+{
+}
+
+void XclExpFutureRecord::Save( XclExpStream& rStrm )
+{
+ rStrm.StartRecord( GetRecId(), GetRecSize() + ((meRecType == EXC_FUTUREREC_UNUSEDREF) ? 12 : 4) );
+ rStrm << GetRecId() << sal_uInt16( 0 );
+ if( meRecType == EXC_FUTUREREC_UNUSEDREF )
+ rStrm.WriteZeroBytes( 8 );
+ WriteBody( rStrm );
+ rStrm.EndRecord();
+}
+
+XclExpSubStream::XclExpSubStream( sal_uInt16 nSubStrmType ) :
+ mnSubStrmType( nSubStrmType )
+{
+}
+
+void XclExpSubStream::Save( XclExpStream& rStrm )
+{
+ // BOF record
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ rStrm.StartRecord( EXC_ID2_BOF, 4 );
+ rStrm << sal_uInt16( 7 ) << mnSubStrmType;
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF3:
+ rStrm.StartRecord( EXC_ID3_BOF, 6 );
+ rStrm << sal_uInt16( 0 ) << mnSubStrmType << sal_uInt16( 2104 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF4:
+ rStrm.StartRecord( EXC_ID4_BOF, 6 );
+ rStrm << sal_uInt16( 0 ) << mnSubStrmType << sal_uInt16( 1705 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF5:
+ rStrm.StartRecord( EXC_ID5_BOF, 8 );
+ rStrm << EXC_BOF_BIFF5 << mnSubStrmType << sal_uInt16( 4915 ) << sal_uInt16( 1994 );
+ rStrm.EndRecord();
+ break;
+ case EXC_BIFF8:
+ rStrm.StartRecord( EXC_ID5_BOF, 16 );
+ rStrm << EXC_BOF_BIFF8 << mnSubStrmType << sal_uInt16( 3612 ) << sal_uInt16( 1996 );
+ rStrm << sal_uInt32( 1 ) << sal_uInt32( 6 );
+ rStrm.EndRecord();
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+
+ // substream records
+ XclExpRecordList<>::Save( rStrm );
+
+ // EOF record
+ rStrm.StartRecord( EXC_ID_EOF, 0 );
+ rStrm.EndRecord();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeroot.cxx b/sc/source/filter/excel/xeroot.cxx
new file mode 100644
index 000000000..3bb35d301
--- /dev/null
+++ b/sc/source/filter/excel/xeroot.cxx
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <officecfg/Office/Common.hxx>
+#include <rtl/random.h>
+#include <sal/log.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xename.hxx>
+#include <xepivot.hxx>
+#include <xestyle.hxx>
+#include <xeroot.hxx>
+#include <xepivotxml.hxx>
+#include <xedbdata.hxx>
+#include <xlcontent.hxx>
+#include <xlname.hxx>
+#include <xllink.hxx>
+
+#include <excrecds.hxx>
+#include <tabprotection.hxx>
+#include <document.hxx>
+
+#include <formulabase.hxx>
+#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+using namespace ::com::sun::star;
+
+// Global data ================================================================
+
+XclExpRootData::XclExpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc ) :
+ XclRootData( eBiff, rMedium, xRootStrg, rDoc, eTextEnc, true )
+{
+ mbRelUrl = mrMedium.IsRemote()
+ ? officecfg::Office::Common::Save::URL::Internet::get()
+ : officecfg::Office::Common::Save::URL::FileSystem::get();
+ maStringBuf.setLength(0);
+}
+
+XclExpRootData::~XclExpRootData()
+{
+}
+
+XclExpRoot::XclExpRoot( XclExpRootData& rExpRootData ) :
+ XclRoot( rExpRootData ),
+ mrExpData( rExpRootData )
+{
+}
+
+XclExpTabInfo& XclExpRoot::GetTabInfo() const
+{
+ OSL_ENSURE( mrExpData.mxTabInfo, "XclExpRoot::GetTabInfo - missing object (wrong BIFF?)" );
+ return *mrExpData.mxTabInfo;
+}
+
+XclExpAddressConverter& XclExpRoot::GetAddressConverter() const
+{
+ OSL_ENSURE( mrExpData.mxAddrConv, "XclExpRoot::GetAddressConverter - missing object (wrong BIFF?)" );
+ return *mrExpData.mxAddrConv;
+}
+
+XclExpFormulaCompiler& XclExpRoot::GetFormulaCompiler() const
+{
+ OSL_ENSURE( mrExpData.mxFmlaComp, "XclExpRoot::GetFormulaCompiler - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFmlaComp;
+}
+
+XclExpProgressBar& XclExpRoot::GetProgressBar() const
+{
+ OSL_ENSURE( mrExpData.mxProgress, "XclExpRoot::GetProgressBar - missing object (wrong BIFF?)" );
+ return *mrExpData.mxProgress;
+}
+
+XclExpSst& XclExpRoot::GetSst() const
+{
+ OSL_ENSURE( mrExpData.mxSst, "XclExpRoot::GetSst - missing object (wrong BIFF?)" );
+ return *mrExpData.mxSst;
+}
+
+XclExpPalette& XclExpRoot::GetPalette() const
+{
+ OSL_ENSURE( mrExpData.mxPalette, "XclExpRoot::GetPalette - missing object (wrong BIFF?)" );
+ return *mrExpData.mxPalette;
+}
+
+XclExpFontBuffer& XclExpRoot::GetFontBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxFontBfr, "XclExpRoot::GetFontBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFontBfr;
+}
+
+XclExpNumFmtBuffer& XclExpRoot::GetNumFmtBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxNumFmtBfr, "XclExpRoot::GetNumFmtBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxNumFmtBfr;
+}
+
+XclExpXFBuffer& XclExpRoot::GetXFBuffer() const
+{
+ OSL_ENSURE( mrExpData.mxXFBfr, "XclExpRoot::GetXFBuffer - missing object (wrong BIFF?)" );
+ return *mrExpData.mxXFBfr;
+}
+
+XclExpLinkManager& XclExpRoot::GetGlobalLinkManager() const
+{
+ OSL_ENSURE( mrExpData.mxGlobLinkMgr, "XclExpRoot::GetGlobalLinkManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxGlobLinkMgr;
+}
+
+XclExpLinkManager& XclExpRoot::GetLocalLinkManager() const
+{
+ OSL_ENSURE( GetLocalLinkMgrRef(), "XclExpRoot::GetLocalLinkManager - missing object (wrong BIFF?)" );
+ return *GetLocalLinkMgrRef();
+}
+
+XclExpNameManager& XclExpRoot::GetNameManager() const
+{
+ OSL_ENSURE( mrExpData.mxNameMgr, "XclExpRoot::GetNameManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxNameMgr;
+}
+
+XclExpObjectManager& XclExpRoot::GetObjectManager() const
+{
+ OSL_ENSURE( mrExpData.mxObjMgr, "XclExpRoot::GetObjectManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxObjMgr;
+}
+
+XclExpFilterManager& XclExpRoot::GetFilterManager() const
+{
+ OSL_ENSURE( mrExpData.mxFilterMgr, "XclExpRoot::GetFilterManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxFilterMgr;
+}
+
+XclExpDxfs& XclExpRoot::GetDxfs() const
+{
+ OSL_ENSURE( mrExpData.mxDxfs, "XclExpRoot::GetDxfs - missing object ( wrong BIFF?)" );
+ return *mrExpData.mxDxfs;
+}
+
+XclExpPivotTableManager& XclExpRoot::GetPivotTableManager() const
+{
+ OSL_ENSURE( mrExpData.mxPTableMgr, "XclExpRoot::GetPivotTableManager - missing object (wrong BIFF?)" );
+ return *mrExpData.mxPTableMgr;
+}
+
+XclExpXmlPivotTableManager& XclExpRoot::GetXmlPivotTableManager()
+{
+ assert(mrExpData.mxXmlPTableMgr);
+ return *mrExpData.mxXmlPTableMgr;
+}
+
+XclExpTablesManager& XclExpRoot::GetTablesManager()
+{
+ assert(mrExpData.mxTablesMgr);
+ return *mrExpData.mxTablesMgr;
+}
+
+void XclExpRoot::InitializeConvert()
+{
+ mrExpData.mxTabInfo = std::make_shared<XclExpTabInfo>( GetRoot() );
+ mrExpData.mxAddrConv = std::make_shared<XclExpAddressConverter>( GetRoot() );
+ mrExpData.mxFmlaComp = std::make_shared<XclExpFormulaCompiler>( GetRoot() );
+ mrExpData.mxProgress = std::make_shared<XclExpProgressBar>( GetRoot() );
+
+ GetProgressBar().Initialize();
+}
+
+void XclExpRoot::InitializeGlobals()
+{
+ SetCurrScTab( SCTAB_GLOBAL );
+
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ mrExpData.mxPalette = new XclExpPalette( GetRoot() );
+ mrExpData.mxFontBfr = new XclExpFontBuffer( GetRoot() );
+ mrExpData.mxNumFmtBfr = new XclExpNumFmtBuffer( GetRoot() );
+ mrExpData.mxXFBfr = new XclExpXFBuffer( GetRoot() );
+ mrExpData.mxGlobLinkMgr = new XclExpLinkManager( GetRoot() );
+ mrExpData.mxNameMgr = new XclExpNameManager( GetRoot() );
+ }
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mrExpData.mxSst = new XclExpSst();
+ mrExpData.mxObjMgr = std::make_shared<XclExpObjectManager>( GetRoot() );
+ mrExpData.mxFilterMgr = std::make_shared<XclExpFilterManager>( GetRoot() );
+ mrExpData.mxPTableMgr = std::make_shared<XclExpPivotTableManager>( GetRoot() );
+ // BIFF8: only one link manager for all sheets
+ mrExpData.mxLocLinkMgr = mrExpData.mxGlobLinkMgr;
+ mrExpData.mxDxfs = new XclExpDxfs( GetRoot() );
+ }
+
+ if( GetOutput() == EXC_OUTPUT_XML_2007 )
+ {
+ mrExpData.mxXmlPTableMgr = std::make_shared<XclExpXmlPivotTableManager>(GetRoot());
+ mrExpData.mxTablesMgr = std::make_shared<XclExpTablesManager>(GetRoot());
+
+ do
+ {
+ ScDocument& rDoc = GetDoc();
+ // Pass the model factory to OpCodeProvider, not the process
+ // service factory, otherwise a FormulaOpCodeMapperObj would be
+ // instantiated instead of a ScFormulaOpCodeMapperObj and the
+ // ScCompiler virtuals not be called! Which would be the case with
+ // the current (2013-01-24) rDoc.GetServiceManager()
+ const SfxObjectShell* pShell = rDoc.GetDocumentShell();
+ if (!pShell)
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no object shell");
+ break;
+ }
+ uno::Reference< lang::XComponent > xComponent = pShell->GetModel();
+ if (!xComponent.is())
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no component");
+ break;
+ }
+ uno::Reference< lang::XMultiServiceFactory > xModelFactory( xComponent, uno::UNO_QUERY);
+ oox::xls::OpCodeProvider aOpCodeProvider(xModelFactory, false);
+ // Compiler mocks about non-matching ctor or conversion from
+ // Sequence<...> to Sequence<const ...> if directly created or passed,
+ // conversion through Any works around.
+ uno::Any aAny( aOpCodeProvider.getOoxParserMap());
+ uno::Sequence< const sheet::FormulaOpCodeMapEntry > aOpCodeMapping;
+ if (!(aAny >>= aOpCodeMapping))
+ {
+ SAL_WARN( "sc", "XclExpRoot::InitializeGlobals - no OpCodeMap");
+ break;
+ }
+ ScCompiler aCompiler( rDoc, ScAddress(), rDoc.GetGrammar());
+ mrExpData.mxOpCodeMap = formula::FormulaCompiler::CreateOpCodeMap( aOpCodeMapping, true);
+ } while(false);
+ }
+
+ GetXFBuffer().Initialize();
+ GetNameManager().Initialize();
+}
+
+void XclExpRoot::InitializeTable( SCTAB nScTab )
+{
+ SetCurrScTab( nScTab );
+ if( GetBiff() == EXC_BIFF5 )
+ {
+ // local link manager per sheet
+ mrExpData.mxLocLinkMgr = new XclExpLinkManager( GetRoot() );
+ }
+}
+
+void XclExpRoot::InitializeSave()
+{
+ GetPalette().Finalize();
+ GetXFBuffer().Finalize();
+}
+
+XclExpRecordRef XclExpRoot::CreateRecord( sal_uInt16 nRecId ) const
+{
+ XclExpRecordRef xRec;
+ switch( nRecId )
+ {
+ case EXC_ID_PALETTE: xRec = mrExpData.mxPalette; break;
+ case EXC_ID_FONTLIST: xRec = mrExpData.mxFontBfr; break;
+ case EXC_ID_FORMATLIST: xRec = mrExpData.mxNumFmtBfr; break;
+ case EXC_ID_XFLIST: xRec = mrExpData.mxXFBfr; break;
+ case EXC_ID_SST: xRec = mrExpData.mxSst; break;
+ case EXC_ID_EXTERNSHEET: xRec = GetLocalLinkMgrRef(); break;
+ case EXC_ID_NAME: xRec = mrExpData.mxNameMgr; break;
+ case EXC_ID_DXFS: xRec = mrExpData.mxDxfs; break;
+ }
+ OSL_ENSURE( xRec, "XclExpRoot::CreateRecord - unknown record ID or missing object" );
+ return xRec;
+}
+
+bool XclExpRoot::IsDocumentEncrypted() const
+{
+ // We need to encrypt the content when the document structure is protected.
+ const ScDocProtection* pDocProt = GetDoc().GetDocProtection();
+ if (pDocProt && pDocProt->isProtected() && pDocProt->isOptionEnabled(ScDocProtection::STRUCTURE))
+ return true;
+
+ // Whether password is entered directly into the save dialog.
+ return GetEncryptionData().hasElements();
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GenerateEncryptionData( const OUString& aPass )
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+
+ if ( !aPass.isEmpty() && aPass.getLength() < 16 )
+ {
+ rtlRandomPool aRandomPool = rtl_random_createPool ();
+ sal_uInt8 pnDocId[16];
+ rtl_random_getBytes( aRandomPool, pnDocId, 16 );
+
+ rtl_random_destroyPool( aRandomPool );
+
+ sal_uInt16 pnPasswd[16] = {};
+ for( sal_Int32 nChar = 0; nChar < aPass.getLength(); ++nChar )
+ pnPasswd[nChar] = aPass[nChar];
+
+ ::msfilter::MSCodec_Std97 aCodec;
+ aCodec.InitKey( pnPasswd, pnDocId );
+ aEncryptionData = aCodec.GetEncryptionData();
+ }
+
+ return aEncryptionData;
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GetEncryptionData() const
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(GetMedium().GetItemSet(), SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem )
+ pEncryptionDataItem->GetValue() >>= aEncryptionData;
+ else
+ {
+ // try to get the encryption data from the password
+ const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(GetMedium().GetItemSet(), SID_PASSWORD, false);
+ if ( pPasswordItem && !pPasswordItem->GetValue().isEmpty() )
+ aEncryptionData = GenerateEncryptionData( pPasswordItem->GetValue() );
+ }
+
+ return aEncryptionData;
+}
+
+uno::Sequence< beans::NamedValue > XclExpRoot::GenerateDefaultEncryptionData()
+{
+ return GenerateEncryptionData( GetDefaultPassword() );
+}
+
+XclExpRootData::XclExpLinkMgrRef const & XclExpRoot::GetLocalLinkMgrRef() const
+{
+ return IsInGlobals() ? mrExpData.mxGlobLinkMgr : mrExpData.mxLocLinkMgr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestream.cxx b/sc/source/filter/excel/xestream.cxx
new file mode 100644
index 000000000..f0486cc37
--- /dev/null
+++ b/sc/source/filter/excel/xestream.cxx
@@ -0,0 +1,1259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <utility>
+
+#include <filter/msfilter/util.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/random.h>
+#include <sax/fshelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sot/storage.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+#include <docuno.hxx>
+#include <xestream.hxx>
+#include <xladdress.hxx>
+#include <xlstring.hxx>
+#include <xltools.hxx>
+#include <xeroot.hxx>
+#include <xestring.hxx>
+#include <xlstyle.hxx>
+#include <rangelst.hxx>
+#include <compiler.hxx>
+#include <formulacell.hxx>
+#include <tokenarray.hxx>
+#include <tokenstringcontext.hxx>
+#include <refreshtimerprotector.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <root.hxx>
+#include <sfx2/app.hxx>
+
+#include <docsh.hxx>
+#include <viewdata.hxx>
+#include <excdoc.hxx>
+
+#include <oox/token/tokens.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/drawingml.hxx>
+#include <oox/export/utils.hxx>
+#include <formula/grammar.hxx>
+#include <oox/ole/vbaexport.hxx>
+#include <excelvbaproject.hxx>
+
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <memory>
+#include <comphelper/storagehelper.hxx>
+
+#include <externalrefmgr.hxx>
+
+#define DEBUG_XL_ENCRYPTION 0
+
+using ::com::sun::star::uno::XInterface;
+using ::std::vector;
+
+using namespace com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::formula;
+using namespace ::oox;
+
+XclExpStream::XclExpStream( SvStream& rOutStrm, const XclExpRoot& rRoot, sal_uInt16 nMaxRecSize ) :
+ mrStrm( rOutStrm ),
+ mrRoot( rRoot ),
+ mbUseEncrypter( false ),
+ mnMaxRecSize( nMaxRecSize ),
+ mnCurrMaxSize( 0 ),
+ mnMaxSliceSize( 0 ),
+ mnHeaderSize( 0 ),
+ mnCurrSize( 0 ),
+ mnSliceSize( 0 ),
+ mnPredictSize( 0 ),
+ mnLastSizePos( 0 ),
+ mbInRec( false )
+{
+ if( mnMaxRecSize == 0 )
+ mnMaxRecSize = (mrRoot.GetBiff() <= EXC_BIFF5) ? EXC_MAXRECSIZE_BIFF5 : EXC_MAXRECSIZE_BIFF8;
+ mnMaxContSize = mnMaxRecSize;
+}
+
+XclExpStream::~XclExpStream()
+{
+ mrStrm.FlushBuffer();
+}
+
+void XclExpStream::StartRecord( sal_uInt16 nRecId, std::size_t nRecSize )
+{
+ OSL_ENSURE( !mbInRec, "XclExpStream::StartRecord - another record still open" );
+ DisableEncryption();
+ mnMaxContSize = mnCurrMaxSize = mnMaxRecSize;
+ mnPredictSize = nRecSize;
+ mbInRec = true;
+ InitRecord( nRecId );
+ SetSliceSize( 0 );
+ EnableEncryption();
+}
+
+void XclExpStream::EndRecord()
+{
+ OSL_ENSURE( mbInRec, "XclExpStream::EndRecord - no record open" );
+ DisableEncryption();
+ UpdateRecSize();
+ mrStrm.Seek( STREAM_SEEK_TO_END );
+ mbInRec = false;
+}
+
+void XclExpStream::SetSliceSize( sal_uInt16 nSize )
+{
+ mnMaxSliceSize = nSize;
+ mnSliceSize = 0;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int8 nValue )
+{
+ PrepareWrite( 1 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteSChar( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt8 nValue )
+{
+ PrepareWrite( 1 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUChar( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int16 nValue )
+{
+ PrepareWrite( 2 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteInt16( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt16 nValue )
+{
+ PrepareWrite( 2 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUInt16( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_Int32 nValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteInt32( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( sal_uInt32 nValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, nValue);
+ else
+ mrStrm.WriteUInt32( nValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( float fValue )
+{
+ PrepareWrite( 4 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, fValue);
+ else
+ mrStrm.WriteFloat( fValue );
+ return *this;
+}
+
+XclExpStream& XclExpStream::operator<<( double fValue )
+{
+ PrepareWrite( 8 );
+ if (mbUseEncrypter && HasValidEncrypter())
+ mxEncrypter->Encrypt(mrStrm, fValue);
+ else
+ mrStrm.WriteDouble( fValue );
+ return *this;
+}
+
+std::size_t XclExpStream::Write( const void* pData, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if( pData && (nBytes > 0) )
+ {
+ if( mbInRec )
+ {
+ const sal_uInt8* pBuffer = static_cast< const sal_uInt8* >( pData );
+ std::size_t nBytesLeft = nBytes;
+ bool bValid = true;
+
+ while( bValid && (nBytesLeft > 0) )
+ {
+ std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
+ std::size_t nWriteRet = nWriteLen;
+ if (mbUseEncrypter && HasValidEncrypter())
+ {
+ OSL_ENSURE(nWriteLen > 0, "XclExpStream::Write: write length is 0!");
+ vector<sal_uInt8> aBytes(nWriteLen);
+ memcpy(aBytes.data(), pBuffer, nWriteLen);
+ mxEncrypter->EncryptBytes(mrStrm, aBytes);
+ // TODO: How do I check if all the bytes have been successfully written ?
+ }
+ else
+ {
+ nWriteRet = mrStrm.WriteBytes(pBuffer, nWriteLen);
+ bValid = (nWriteLen == nWriteRet);
+ OSL_ENSURE( bValid, "XclExpStream::Write - stream write error" );
+ }
+ pBuffer += nWriteRet;
+ nRet += nWriteRet;
+ nBytesLeft -= nWriteRet;
+ UpdateSizeVars( nWriteRet );
+ }
+ }
+ else
+ nRet = mrStrm.WriteBytes(pData, nBytes);
+ }
+ return nRet;
+}
+
+void XclExpStream::WriteZeroBytes( std::size_t nBytes )
+{
+ if( mbInRec )
+ {
+ std::size_t nBytesLeft = nBytes;
+ while( nBytesLeft > 0 )
+ {
+ std::size_t nWriteLen = ::std::min< std::size_t >( PrepareWrite(), nBytesLeft );
+ WriteRawZeroBytes( nWriteLen );
+ nBytesLeft -= nWriteLen;
+ UpdateSizeVars( nWriteLen );
+ }
+ }
+ else
+ WriteRawZeroBytes( nBytes );
+}
+
+void XclExpStream::WriteZeroBytesToRecord( std::size_t nBytes )
+{
+ if (!mbInRec)
+ // not in record.
+ return;
+
+ for (std::size_t i = 0; i < nBytes; ++i)
+ *this << sal_uInt8(0)/*nZero*/;
+}
+
+void XclExpStream::CopyFromStream(SvStream& rInStrm, sal_uInt64 const nBytes)
+{
+ sal_uInt64 const nRemaining(rInStrm.remainingSize());
+ sal_uInt64 nBytesLeft = ::std::min(nBytes, nRemaining);
+ if( nBytesLeft <= 0 )
+ return;
+
+ const std::size_t nMaxBuffer = 4096;
+ std::unique_ptr<sal_uInt8[]> pBuffer(
+ new sal_uInt8[ ::std::min<std::size_t>(nBytesLeft, nMaxBuffer) ]);
+ bool bValid = true;
+
+ while( bValid && (nBytesLeft > 0) )
+ {
+ std::size_t nWriteLen = ::std::min<std::size_t>(nBytesLeft, nMaxBuffer);
+ rInStrm.ReadBytes(pBuffer.get(), nWriteLen);
+ std::size_t nWriteRet = Write( pBuffer.get(), nWriteLen );
+ bValid = (nWriteLen == nWriteRet);
+ nBytesLeft -= nWriteRet;
+ }
+}
+
+void XclExpStream::WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags )
+{
+ SetSliceSize( 0 );
+ nFlags &= EXC_STRF_16BIT; // repeat only 16bit flag
+ sal_uInt16 nCharLen = nFlags ? 2 : 1;
+
+ for( const auto& rItem : rBuffer )
+ {
+ if( mbInRec && (mnCurrSize + nCharLen > mnCurrMaxSize) )
+ {
+ StartContinue();
+ operator<<( nFlags );
+ }
+ if( nCharLen == 2 )
+ operator<<( rItem );
+ else
+ operator<<( static_cast< sal_uInt8 >( rItem ) );
+ }
+}
+
+// Xcl has an obscure sense of whether starting a new record or not,
+// and crashes if it encounters the string header at the very end of a record.
+// Thus we add 1 to give some room, seems like they do it that way but with another count (10?)
+void XclExpStream::WriteByteString( const OString& rString )
+{
+ SetSliceSize( 0 );
+ std::size_t nLen = ::std::min< std::size_t >( rString.getLength(), 0x00FF );
+ nLen = ::std::min< std::size_t >( nLen, 0xFF );
+
+ sal_uInt16 nLeft = PrepareWrite();
+ if( mbInRec && (nLeft <= 1) )
+ StartContinue();
+
+ operator<<( static_cast< sal_uInt8 >( nLen ) );
+ Write( rString.getStr(), nLen );
+}
+
+void XclExpStream::WriteCharBuffer( const ScfUInt8Vec& rBuffer )
+{
+ SetSliceSize( 0 );
+ Write( rBuffer.data(), rBuffer.size() );
+}
+
+void XclExpStream::SetEncrypter( XclExpEncrypterRef const & xEncrypter )
+{
+ mxEncrypter = xEncrypter;
+}
+
+bool XclExpStream::HasValidEncrypter() const
+{
+ return mxEncrypter && mxEncrypter->IsValid();
+}
+
+void XclExpStream::EnableEncryption( bool bEnable )
+{
+ mbUseEncrypter = bEnable && HasValidEncrypter();
+}
+
+void XclExpStream::DisableEncryption()
+{
+ EnableEncryption(false);
+}
+
+void XclExpStream::SetSvStreamPos(sal_uInt64 const nPos)
+{
+ OSL_ENSURE( !mbInRec, "XclExpStream::SetSvStreamPos - not allowed inside of a record" );
+ mbInRec ? 0 : mrStrm.Seek( nPos );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpStream::InitRecord( sal_uInt16 nRecId )
+{
+ mrStrm.Seek( STREAM_SEEK_TO_END );
+ mrStrm.WriteUInt16( nRecId );
+
+ mnLastSizePos = mrStrm.Tell();
+ mnHeaderSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( mnPredictSize, mnCurrMaxSize ) );
+ mrStrm.WriteUInt16( mnHeaderSize );
+ mnCurrSize = mnSliceSize = 0;
+}
+
+void XclExpStream::UpdateRecSize()
+{
+ if( mnCurrSize != mnHeaderSize )
+ {
+ mrStrm.Seek( mnLastSizePos );
+ mrStrm.WriteUInt16( mnCurrSize );
+ }
+}
+
+void XclExpStream::UpdateSizeVars( std::size_t nSize )
+{
+ OSL_ENSURE( mnCurrSize + nSize <= mnCurrMaxSize, "XclExpStream::UpdateSizeVars - record overwritten" );
+ mnCurrSize = mnCurrSize + static_cast< sal_uInt16 >( nSize );
+
+ if( mnMaxSliceSize > 0 )
+ {
+ OSL_ENSURE( mnSliceSize + nSize <= mnMaxSliceSize, "XclExpStream::UpdateSizeVars - slice overwritten" );
+ mnSliceSize = mnSliceSize + static_cast< sal_uInt16 >( nSize );
+ if( mnSliceSize >= mnMaxSliceSize )
+ mnSliceSize = 0;
+ }
+}
+
+void XclExpStream::StartContinue()
+{
+ UpdateRecSize();
+ mnCurrMaxSize = mnMaxContSize;
+ mnPredictSize -= mnCurrSize;
+ InitRecord( EXC_ID_CONT );
+}
+
+void XclExpStream::PrepareWrite( sal_uInt16 nSize )
+{
+ if( mbInRec )
+ {
+ if( (mnCurrSize + nSize > mnCurrMaxSize) ||
+ ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
+ StartContinue();
+ UpdateSizeVars( nSize );
+ }
+}
+
+sal_uInt16 XclExpStream::PrepareWrite()
+{
+ sal_uInt16 nRet = 0;
+ if( mbInRec )
+ {
+ if( (mnCurrSize >= mnCurrMaxSize) ||
+ ((mnMaxSliceSize > 0) && (mnSliceSize == 0) && (mnCurrSize + mnMaxSliceSize > mnCurrMaxSize)) )
+ StartContinue();
+ UpdateSizeVars( 0 );
+
+ nRet = (mnMaxSliceSize > 0) ? (mnMaxSliceSize - mnSliceSize) : (mnCurrMaxSize - mnCurrSize);
+ }
+ return nRet;
+}
+
+void XclExpStream::WriteRawZeroBytes( std::size_t nBytes )
+{
+ const sal_uInt32 nData = 0;
+ std::size_t nBytesLeft = nBytes;
+ while( nBytesLeft >= sizeof( nData ) )
+ {
+ mrStrm.WriteUInt32( nData );
+ nBytesLeft -= sizeof( nData );
+ }
+ if( nBytesLeft )
+ mrStrm.WriteBytes(&nData, nBytesLeft);
+}
+
+XclExpBiff8Encrypter::XclExpBiff8Encrypter( const XclExpRoot& rRoot ) :
+ mnOldPos(STREAM_SEEK_TO_END),
+ mbValid(false)
+{
+ Sequence< NamedValue > aEncryptionData = rRoot.GetEncryptionData();
+ if( !aEncryptionData.hasElements() )
+ // Empty password. Get the default biff8 password.
+ aEncryptionData = XclExpRoot::GenerateDefaultEncryptionData();
+ Init( aEncryptionData );
+}
+
+XclExpBiff8Encrypter::~XclExpBiff8Encrypter()
+{
+}
+
+void XclExpBiff8Encrypter::GetSaltDigest( sal_uInt8 pnSaltDigest[16] ) const
+{
+ if ( sizeof( mpnSaltDigest ) == 16 )
+ memcpy( pnSaltDigest, mpnSaltDigest, 16 );
+}
+
+void XclExpBiff8Encrypter::GetSalt( sal_uInt8 pnSalt[16] ) const
+{
+ if ( sizeof( mpnSalt ) == 16 )
+ memcpy( pnSalt, mpnSalt, 16 );
+}
+
+void XclExpBiff8Encrypter::GetDocId( sal_uInt8 pnDocId[16] ) const
+{
+ if ( sizeof( mpnDocId ) == 16 )
+ memcpy( pnDocId, mpnDocId, 16 );
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt8 nData )
+{
+ vector<sal_uInt8> aByte { nData };
+ EncryptBytes(rStrm, aByte);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt16 nData )
+{
+ ::std::vector<sal_uInt8> pnBytes
+ {
+ o3tl::narrowing<sal_uInt8>(nData & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 8) & 0xFF)
+ };
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_uInt32 nData )
+{
+ ::std::vector<sal_uInt8> pnBytes
+ {
+ o3tl::narrowing<sal_uInt8>(nData & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 8) & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 16) & 0xFF),
+ o3tl::narrowing<sal_uInt8>((nData >> 24) & 0xFF)
+ };
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, float fValue )
+{
+ ::std::vector<sal_uInt8> pnBytes(4);
+ memcpy(pnBytes.data(), &fValue, 4);
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, double fValue )
+{
+ ::std::vector<sal_uInt8> pnBytes(8);
+ memcpy(pnBytes.data(), &fValue, 8);
+ EncryptBytes(rStrm, pnBytes);
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int8 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt8>(nData));
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int16 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt16>(nData));
+}
+
+void XclExpBiff8Encrypter::Encrypt( SvStream& rStrm, sal_Int32 nData )
+{
+ Encrypt(rStrm, static_cast<sal_uInt32>(nData));
+}
+
+void XclExpBiff8Encrypter::Init( const Sequence< NamedValue >& rEncryptionData )
+{
+ mbValid = false;
+
+ if( !maCodec.InitCodec( rEncryptionData ) )
+ return;
+
+ maCodec.GetDocId( mpnDocId );
+
+ // generate the salt here
+ rtlRandomPool aRandomPool = rtl_random_createPool ();
+ rtl_random_getBytes( aRandomPool, mpnSalt, 16 );
+ rtl_random_destroyPool( aRandomPool );
+
+ memset( mpnSaltDigest, 0, sizeof( mpnSaltDigest ) );
+
+ // generate salt hash.
+ ::msfilter::MSCodec_Std97 aCodec;
+ aCodec.InitCodec( rEncryptionData );
+ aCodec.CreateSaltDigest( mpnSalt, mpnSaltDigest );
+
+ // verify to make sure it's in good shape.
+ mbValid = maCodec.VerifyKey( mpnSalt, mpnSaltDigest );
+}
+
+sal_uInt32 XclExpBiff8Encrypter::GetBlockPos( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
+}
+
+sal_uInt16 XclExpBiff8Encrypter::GetOffsetInBlock( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
+}
+
+void XclExpBiff8Encrypter::EncryptBytes( SvStream& rStrm, vector<sal_uInt8>& aBytes )
+{
+ sal_uInt64 nStrmPos = rStrm.Tell();
+ sal_uInt16 nBlockOffset = GetOffsetInBlock(nStrmPos);
+ sal_uInt32 nBlockPos = GetBlockPos(nStrmPos);
+
+#if DEBUG_XL_ENCRYPTION
+ fprintf(stdout, "XclExpBiff8Encrypter::EncryptBytes: stream pos = %ld offset in block = %d block pos = %ld\n",
+ nStrmPos, nBlockOffset, nBlockPos);
+#endif
+
+ sal_uInt16 nSize = static_cast< sal_uInt16 >( aBytes.size() );
+ if (nSize == 0)
+ return;
+
+#if DEBUG_XL_ENCRYPTION
+ fprintf(stdout, "RAW: ");
+ for (sal_uInt16 i = 0; i < nSize; ++i)
+ fprintf(stdout, "%2.2X ", aBytes[i]);
+ fprintf(stdout, "\n");
+#endif
+
+ if (mnOldPos != nStrmPos)
+ {
+ sal_uInt16 nOldOffset = GetOffsetInBlock(mnOldPos);
+ sal_uInt32 nOldBlockPos = GetBlockPos(mnOldPos);
+
+ if ( (nBlockPos != nOldBlockPos) || (nBlockOffset < nOldOffset) )
+ {
+ maCodec.InitCipher(nBlockPos);
+ nOldOffset = 0;
+ }
+
+ if (nBlockOffset > nOldOffset)
+ maCodec.Skip(nBlockOffset - nOldOffset);
+ }
+
+ sal_uInt16 nBytesLeft = nSize;
+ sal_uInt16 nPos = 0;
+ while (nBytesLeft > 0)
+ {
+ sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - nBlockOffset;
+ sal_uInt16 nEncBytes = ::std::min(nBlockLeft, nBytesLeft);
+
+ bool bRet = maCodec.Encode(&aBytes[nPos], nEncBytes, &aBytes[nPos], nEncBytes);
+ OSL_ENSURE(bRet, "XclExpBiff8Encrypter::EncryptBytes: encryption failed!!");
+
+ std::size_t nRet = rStrm.WriteBytes(&aBytes[nPos], nEncBytes);
+ OSL_ENSURE(nRet == nEncBytes, "XclExpBiff8Encrypter::EncryptBytes: fail to write to stream!!");
+
+ nStrmPos = rStrm.Tell();
+ nBlockOffset = GetOffsetInBlock(nStrmPos);
+ nBlockPos = GetBlockPos(nStrmPos);
+ if (nBlockOffset == 0)
+ maCodec.InitCipher(nBlockPos);
+
+ nBytesLeft -= nEncBytes;
+ nPos += nEncBytes;
+ }
+ mnOldPos = nStrmPos;
+}
+
+static const char* lcl_GetErrorString( FormulaError nScErrCode )
+{
+ sal_uInt8 nXclErrCode = XclTools::GetXclErrorCode( nScErrCode );
+ switch( nXclErrCode )
+ {
+ case EXC_ERR_NULL: return "#NULL!";
+ case EXC_ERR_DIV0: return "#DIV/0!";
+ case EXC_ERR_VALUE: return "#VALUE!";
+ case EXC_ERR_REF: return "#REF!";
+ case EXC_ERR_NAME: return "#NAME?";
+ case EXC_ERR_NUM: return "#NUM!";
+ case EXC_ERR_NA:
+ default: return "#N/A";
+ }
+}
+
+void XclXmlUtils::GetFormulaTypeAndValue( ScFormulaCell& rCell, const char*& rsType, OUString& rsValue )
+{
+ sc::FormulaResultValue aResValue = rCell.GetResult();
+
+ switch (aResValue.meType)
+ {
+ case sc::FormulaResultValue::Error:
+ rsType = "e";
+ rsValue = ToOUString(lcl_GetErrorString(aResValue.mnError));
+ break;
+ case sc::FormulaResultValue::Value:
+ rsType = rCell.GetFormatType() == SvNumFormatType::LOGICAL
+ && (aResValue.mfValue == 0.0 || aResValue.mfValue == 1.0)
+ ? "b"
+ : "n";
+ rsValue = OUString::number(aResValue.mfValue);
+ break;
+ case sc::FormulaResultValue::String:
+ rsType = "str";
+ rsValue = rCell.GetString().getString();
+ break;
+ case sc::FormulaResultValue::Invalid:
+ default:
+ // TODO : double-check this to see if this is correct.
+ rsType = "inlineStr";
+ rsValue = rCell.GetString().getString();
+ }
+}
+
+OUString XclXmlUtils::GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId )
+{
+ OUStringBuffer sBuf;
+ if( sStreamDir )
+ sBuf.appendAscii( sStreamDir );
+ sBuf.appendAscii( sStream );
+ if( nId )
+ sBuf.append( nId );
+ if( strstr(sStream, "vml") )
+ sBuf.append( ".vml" );
+ else
+ sBuf.append( ".xml" );
+ return sBuf.makeStringAndClear();
+}
+
+OString XclXmlUtils::ToOString( const Color& rColor )
+{
+ char buf[9];
+ sprintf( buf, "%.2X%.2X%.2X%.2X", rColor.GetAlpha(), rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
+ buf[8] = '\0';
+ return OString(buf);
+}
+
+OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const ScAddress& rAddress )
+{
+ rAddress.Format(s, ScRefFlags::VALID, nullptr, ScAddress::Details( FormulaGrammar::CONV_XL_A1));
+ return s;
+}
+
+OString XclXmlUtils::ToOString( const ScfUInt16Vec& rBuffer )
+{
+ if(rBuffer.empty())
+ return OString();
+
+ const sal_uInt16* pBuffer = rBuffer.data();
+ return OString(
+ reinterpret_cast<sal_Unicode const *>(pBuffer), rBuffer.size(),
+ RTL_TEXTENCODING_UTF8);
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRange& rRange, bool bFullAddressNotation )
+{
+ OUString sRange(rRange.Format( rDoc, ScRefFlags::VALID,
+ ScAddress::Details( FormulaGrammar::CONV_XL_A1 ),
+ bFullAddressNotation ) );
+ return sRange.toUtf8();
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRangeList& rRangeList )
+{
+ OUString s;
+ rRangeList.Format(s, ScRefFlags::VALID, rDoc, FormulaGrammar::CONV_XL_OOX, ' ');
+ return s.toUtf8();
+}
+
+static ScAddress lcl_ToAddress( const XclAddress& rAddress )
+{
+ return ScAddress( rAddress.mnCol, rAddress.mnRow, 0 );
+}
+
+OStringBuffer& XclXmlUtils::ToOString( OStringBuffer& s, const XclAddress& rAddress )
+{
+ return ToOString( s, lcl_ToAddress( rAddress ));
+}
+
+OString XclXmlUtils::ToOString( const XclExpString& s )
+{
+ OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
+ return ToOString( s.GetUnicodeBuffer() );
+}
+
+static ScRange lcl_ToRange( const XclRange& rRange )
+{
+ ScRange aRange;
+
+ aRange.aStart = lcl_ToAddress( rRange.maFirst );
+ aRange.aEnd = lcl_ToAddress( rRange.maLast );
+
+ return aRange;
+}
+
+OString XclXmlUtils::ToOString( const ScDocument& rDoc, const XclRangeList& rRanges )
+{
+ ScRangeList aRanges;
+ for( const auto& rRange : rRanges )
+ {
+ aRanges.push_back( lcl_ToRange( rRange ) );
+ }
+ return ToOString( rDoc, aRanges );
+}
+
+OUString XclXmlUtils::ToOUString( const char* s )
+{
+ return OUString( s, static_cast<sal_Int32>(strlen( s )), RTL_TEXTENCODING_ASCII_US );
+}
+
+OUString XclXmlUtils::ToOUString( const ScfUInt16Vec& rBuf, sal_Int32 nStart, sal_Int32 nLength )
+{
+ if( nLength == -1 || ( nLength > (static_cast<sal_Int32>(rBuf.size()) - nStart) ) )
+ nLength = (rBuf.size() - nStart);
+
+ return nLength > 0
+ ? OUString(
+ reinterpret_cast<sal_Unicode const *>(&rBuf[nStart]), nLength)
+ : OUString();
+}
+
+OUString XclXmlUtils::ToOUString(
+ sc::CompileFormulaContext& rCtx, const ScAddress& rAddress, const ScTokenArray* pTokenArray,
+ FormulaError nErrCode )
+{
+ ScCompiler aCompiler( rCtx, rAddress, const_cast<ScTokenArray&>(*pTokenArray));
+
+ /* TODO: isn't this the same as passed in rCtx and thus superfluous? */
+ aCompiler.SetGrammar(FormulaGrammar::GRAM_OOXML);
+
+ sal_Int32 nLen = pTokenArray->GetLen();
+ OUStringBuffer aBuffer( nLen ? (nLen * 5) : 8 );
+ if (nLen)
+ aCompiler.CreateStringFromTokenArray( aBuffer );
+ else
+ {
+ if (nErrCode != FormulaError::NONE)
+ aCompiler.AppendErrorConstant( aBuffer, nErrCode);
+ else
+ {
+ // No code SHOULD be an "error cell", assert caller thought of that
+ // and it really is.
+ assert(!"No code and no error.");
+ }
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+OUString XclXmlUtils::ToOUString( const XclExpString& s )
+{
+ OSL_ENSURE( !s.IsRich(), "XclXmlUtils::ToOString(XclExpString): rich text string found!" );
+ return ToOUString( s.GetUnicodeBuffer() );
+}
+
+static void lcl_WriteValue( const sax_fastparser::FSHelperPtr& rStream, sal_Int32 nElement, const char* pValue )
+{
+ if( !pValue )
+ return;
+ rStream->singleElement(nElement, XML_val, pValue);
+}
+
+static const char* lcl_GetUnderlineStyle( FontLineStyle eUnderline, bool& bHaveUnderline )
+{
+ bHaveUnderline = true;
+ switch( eUnderline )
+ {
+ // OOXTODO: doubleAccounting, singleAccounting
+ // OOXTODO: what should be done with the other FontLineStyle values?
+ case LINESTYLE_SINGLE: return "single";
+ case LINESTYLE_DOUBLE: return "double";
+ case LINESTYLE_NONE:
+ default: bHaveUnderline = false; return "none";
+ }
+}
+
+static const char* lcl_ToVerticalAlignmentRun( SvxEscapement eEscapement, bool& bHaveAlignment )
+{
+ bHaveAlignment = true;
+ switch( eEscapement )
+ {
+ case SvxEscapement::Superscript: return "superscript";
+ case SvxEscapement::Subscript: return "subscript";
+ case SvxEscapement::Off:
+ default: bHaveAlignment = false; return "baseline";
+ }
+}
+
+sax_fastparser::FSHelperPtr XclXmlUtils::WriteFontData( sax_fastparser::FSHelperPtr pStream, const XclFontData& rFontData, sal_Int32 nFontId )
+{
+ bool bHaveUnderline, bHaveVertAlign;
+ const char* pUnderline = lcl_GetUnderlineStyle( rFontData.GetScUnderline(), bHaveUnderline );
+ const char* pVertAlign = lcl_ToVerticalAlignmentRun( rFontData.GetScEscapement(), bHaveVertAlign );
+
+ lcl_WriteValue( pStream, XML_b, rFontData.mnWeight > 400 ? ToPsz( true ) : nullptr );
+ lcl_WriteValue( pStream, XML_i, rFontData.mbItalic ? ToPsz( true ) : nullptr );
+ lcl_WriteValue( pStream, XML_strike, rFontData.mbStrikeout ? ToPsz( true ) : nullptr );
+ // OOXTODO: lcl_WriteValue( rStream, XML_condense, ); // mac compatibility setting
+ // OOXTODO: lcl_WriteValue( rStream, XML_extend, ); // compatibility setting
+ lcl_WriteValue( pStream, XML_outline, rFontData.mbOutline ? ToPsz( true ) : nullptr );
+ lcl_WriteValue( pStream, XML_shadow, rFontData.mbShadow ? ToPsz( true ) : nullptr );
+ lcl_WriteValue( pStream, XML_u, bHaveUnderline ? pUnderline : nullptr );
+ lcl_WriteValue( pStream, XML_vertAlign, bHaveVertAlign ? pVertAlign : nullptr );
+ lcl_WriteValue( pStream, XML_sz, OString::number( rFontData.mnHeight / 20.0 ).getStr() ); // Twips->Pt
+ if( rFontData.maColor != Color( ColorAlpha, 0, 0xFF, 0xFF, 0xFF ) )
+ pStream->singleElement( XML_color,
+ // OOXTODO: XML_auto, bool
+ // OOXTODO: XML_indexed, uint
+ XML_rgb, XclXmlUtils::ToOString(rFontData.maColor)
+ // OOXTODO: XML_theme, index into <clrScheme/>
+ // OOXTODO: XML_tint, double
+ );
+ lcl_WriteValue( pStream, nFontId, rFontData.maName.toUtf8().getStr() );
+ lcl_WriteValue( pStream, XML_family, OString::number( rFontData.mnFamily ).getStr() );
+ lcl_WriteValue( pStream, XML_charset, rFontData.mnCharSet != 0 ? OString::number( rFontData.mnCharSet ).getStr() : nullptr );
+
+ return pStream;
+}
+
+XclExpXmlStream::XclExpXmlStream( const uno::Reference< XComponentContext >& rCC, bool bExportVBA, bool bExportTemplate )
+ : XmlFilterBase( rCC ),
+ mpRoot( nullptr ),
+ mbExportVBA(bExportVBA),
+ mbExportTemplate(bExportTemplate)
+{
+}
+
+XclExpXmlStream::~XclExpXmlStream()
+{
+ assert(maStreams.empty() && "Forgotten PopStream()?");
+}
+
+sax_fastparser::FSHelperPtr& XclExpXmlStream::GetCurrentStream()
+{
+ OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::GetCurrentStream - no current stream" );
+ return maStreams.top();
+}
+
+void XclExpXmlStream::PushStream( sax_fastparser::FSHelperPtr const & aStream )
+{
+ maStreams.push( aStream );
+}
+
+void XclExpXmlStream::PopStream()
+{
+ OSL_ENSURE( !maStreams.empty(), "XclExpXmlStream::PopStream - stack is empty!" );
+ maStreams.pop();
+}
+
+sax_fastparser::FSHelperPtr XclExpXmlStream::GetStreamForPath( const OUString& sPath )
+{
+ if( maOpenedStreamMap.find( sPath ) == maOpenedStreamMap.end() )
+ return sax_fastparser::FSHelperPtr();
+ return maOpenedStreamMap[ sPath ].second;
+}
+
+void XclExpXmlStream::WriteAttribute(sal_Int32 nAttr, std::u16string_view sVal)
+{
+ GetCurrentStream()->write(" ")->writeId(nAttr)->write("=\"")->writeEscaped(sVal)->write("\"");
+}
+
+sax_fastparser::FSHelperPtr XclExpXmlStream::CreateOutputStream (
+ const OUString& sFullStream,
+ std::u16string_view sRelativeStream,
+ const uno::Reference< XOutputStream >& xParentRelation,
+ const char* sContentType,
+ std::u16string_view sRelationshipType,
+ OUString* pRelationshipId )
+{
+ OUString sRelationshipId;
+ if (xParentRelation.is())
+ sRelationshipId = addRelation( xParentRelation, OUString(sRelationshipType), sRelativeStream );
+ else
+ sRelationshipId = addRelation( OUString(sRelationshipType), sRelativeStream );
+
+ if( pRelationshipId )
+ *pRelationshipId = sRelationshipId;
+
+ sax_fastparser::FSHelperPtr p = openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
+
+ maOpenedStreamMap[ sFullStream ] = std::make_pair( sRelationshipId, p );
+
+ return p;
+}
+
+bool XclExpXmlStream::importDocument() noexcept
+{
+ return false;
+}
+
+oox::vml::Drawing* XclExpXmlStream::getVmlDrawing()
+{
+ return nullptr;
+}
+
+const oox::drawingml::Theme* XclExpXmlStream::getCurrentTheme() const
+{
+ return nullptr;
+}
+
+oox::drawingml::table::TableStyleListPtr XclExpXmlStream::getTableStyles()
+{
+ return oox::drawingml::table::TableStyleListPtr();
+}
+
+oox::drawingml::chart::ChartConverter* XclExpXmlStream::getChartConverter()
+{
+ // DO NOT CALL
+ return nullptr;
+}
+
+ScDocShell* XclExpXmlStream::getDocShell()
+{
+ uno::Reference< XInterface > xModel( getModel(), UNO_QUERY );
+
+ ScModelObj *pObj = dynamic_cast < ScModelObj* >( xModel.get() );
+
+ if ( pObj )
+ return static_cast < ScDocShell* >( pObj->GetEmbeddedObject() );
+
+ return nullptr;
+}
+
+bool XclExpXmlStream::exportDocument()
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+ ScRefreshTimerProtector aProt(rDoc.GetRefreshTimerControlAddress());
+
+ const bool bValidateTabNames = officecfg::Office::Calc::Filter::Export::MS_Excel::TruncateLongSheetNames::get();
+ std::vector<OUString> aOriginalTabNames;
+ if (bValidateTabNames)
+ {
+ validateTabNames(aOriginalTabNames);
+ }
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator = getStatusIndicator();
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->start(ScResId(STR_SAVE_DOC), 100);
+
+ // NOTE: Don't use SotStorage or SvStream any more, and never call
+ // SfxMedium::GetOutStream() anywhere in the xlsx export filter code!
+ // Instead, write via XOutputStream instance.
+ tools::SvRef<SotStorage> rStorage = static_cast<SotStorage*>(nullptr);
+ drawingml::DrawingML::ResetMlCounters();
+ drawingml::DrawingML::PushExportGraphics();
+
+ XclExpRootData aData(
+ EXC_BIFF8, *pShell->GetMedium (), rStorage, rDoc,
+ msfilter::util::getBestTextEncodingFromLocale(
+ Application::GetSettings().GetLanguageTag().getLocale()));
+ aData.meOutput = EXC_OUTPUT_XML_2007;
+ aData.maXclMaxPos.Set( EXC_MAXCOL_XML_2007, EXC_MAXROW_XML_2007, EXC_MAXTAB_XML_2007 );
+ aData.maMaxPos.SetCol( ::std::min( aData.maScMaxPos.Col(), aData.maXclMaxPos.Col() ) );
+ aData.maMaxPos.SetRow( ::std::min( aData.maScMaxPos.Row(), aData.maXclMaxPos.Row() ) );
+ aData.maMaxPos.SetTab( ::std::min( aData.maScMaxPos.Tab(), aData.maXclMaxPos.Tab() ) );
+ aData.mpCompileFormulaCxt = std::make_shared<sc::CompileFormulaContext>(rDoc);
+ // set target path to get correct relative links to target document, not source
+ INetURLObject aPath(getFileUrl());
+ aData.maBasePath = OUString("file:///" + aPath.GetPath() + "\\").replace('\\', '/')
+ // fix for Linux
+ .replaceFirst("file:////", "file:///");
+
+ XclExpRoot aRoot( aData );
+
+ mpRoot = &aRoot;
+ aRoot.GetOldRoot().pER = &aRoot;
+ aRoot.GetOldRoot().eDateiTyp = Biff8;
+ // Get the viewsettings before processing
+ if (ScViewData* pViewData = ScDocShell::GetViewData())
+ pViewData->WriteExtOptions( mpRoot->GetExtDocOptions() );
+
+ OUString const workbook = "xl/workbook.xml";
+ const char* pWorkbookContentType = nullptr;
+ if (mbExportVBA)
+ {
+ if (mbExportTemplate)
+ {
+ pWorkbookContentType = "application/vnd.ms-excel.template.macroEnabled.main+xml";
+ }
+ else
+ {
+ pWorkbookContentType = "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
+ }
+ }
+ else
+ {
+ if (mbExportTemplate)
+ {
+ pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
+ }
+ else
+ {
+ pWorkbookContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
+ }
+ }
+
+ PushStream( CreateOutputStream( workbook, workbook,
+ uno::Reference <XOutputStream>(),
+ pWorkbookContentType,
+ oox::getRelationship(Relationship::OFFICEDOCUMENT) ) );
+
+ if (mbExportVBA)
+ {
+ VbaExport aExport(getModel());
+ if (aExport.containsVBAProject())
+ {
+ SvMemoryStream aVbaStream(4096, 4096);
+ tools::SvRef<SotStorage> pVBAStorage(new SotStorage(aVbaStream));
+ aExport.exportVBA( pVBAStorage.get() );
+ aVbaStream.Seek(0);
+ css::uno::Reference<css::io::XInputStream> xVBAStream(
+ new utl::OInputStreamWrapper(aVbaStream));
+ css::uno::Reference<css::io::XOutputStream> xVBAOutput =
+ openFragmentStream("xl/vbaProject.bin", "application/vnd.ms-office.vbaProject");
+ comphelper::OStorageHelper::CopyInputToOutput(xVBAStream, xVBAOutput);
+
+ addRelation(GetCurrentStream()->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin");
+ }
+ }
+
+ // destruct at the end of the block
+ {
+ ExcDocument aDocRoot( aRoot );
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(10);
+ aDocRoot.ReadDoc();
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(40);
+ aDocRoot.WriteXml( *this );
+ rDoc.GetExternalRefManager()->disableSkipUnusedFileIds();
+ }
+
+ PopStream();
+ // Free all FSHelperPtr, to flush data before committing storage
+ maOpenedStreamMap.clear();
+
+ commitStorage();
+
+ if (bValidateTabNames)
+ {
+ restoreTabNames(aOriginalTabNames);
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ mpRoot = nullptr;
+
+ drawingml::DrawingML::PopExportGraphics();
+
+ return true;
+}
+
+::oox::ole::VbaProject* XclExpXmlStream::implCreateVbaProject() const
+{
+ return new ::oox::xls::ExcelVbaProject( getComponentContext(), uno::Reference< XSpreadsheetDocument >( getModel(), UNO_QUERY ) );
+}
+
+OUString XclExpXmlStream::getImplementationName()
+{
+ return "TODO";
+}
+
+void XclExpXmlStream::validateTabNames(std::vector<OUString>& aOriginalTabNames)
+{
+ const int MAX_TAB_NAME_LENGTH = 31;
+
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ // get original names
+ aOriginalTabNames.resize(rDoc.GetTableCount());
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ rDoc.GetName(nTab, aOriginalTabNames[nTab]);
+ }
+
+ // new tab names
+ std::vector<OUString> aNewTabNames;
+ aNewTabNames.reserve(rDoc.GetTableCount());
+
+ // check and rename
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ const OUString& rOriginalName = aOriginalTabNames[nTab];
+ if (rOriginalName.getLength() > MAX_TAB_NAME_LENGTH)
+ {
+ OUString aNewName;
+
+ // let's try just truncate "<first 31 chars>"
+ if (aNewName.isEmpty())
+ {
+ aNewName = rOriginalName.copy(0, MAX_TAB_NAME_LENGTH);
+ if (aNewTabNames.end() != std::find(aNewTabNames.begin(), aNewTabNames.end(), aNewName) ||
+ aOriginalTabNames.end() != std::find(aOriginalTabNames.begin(), aOriginalTabNames.end(), aNewName))
+ {
+ // was found => let's use another tab name
+ aNewName.clear();
+ }
+ }
+
+ // let's try "<first N chars>-XXX" template
+ for (int digits=1; digits<10 && aNewName.isEmpty(); digits++)
+ {
+ const int rangeStart = pow(10, digits - 1);
+ const int rangeEnd = pow(10, digits);
+
+ for (int i=rangeStart; i<rangeEnd && aNewName.isEmpty(); i++)
+ {
+ aNewName = OUString::Concat(rOriginalName.subView(0, MAX_TAB_NAME_LENGTH - 1 - digits)) + "-" + OUString::number(i);
+ if (aNewTabNames.end() != std::find(aNewTabNames.begin(), aNewTabNames.end(), aNewName) ||
+ aOriginalTabNames.end() != std::find(aOriginalTabNames.begin(), aOriginalTabNames.end(), aNewName))
+ {
+ // was found => let's use another tab name
+ aNewName.clear();
+ }
+ }
+ }
+
+ if (!aNewName.isEmpty())
+ {
+ // new name was created => rename
+ renameTab(nTab, aNewName);
+ aNewTabNames.push_back(aNewName);
+ }
+ else
+ {
+ // default: do not rename
+ aNewTabNames.push_back(rOriginalName);
+ }
+ }
+ else
+ {
+ // default: do not rename
+ aNewTabNames.push_back(rOriginalName);
+ }
+ }
+}
+
+void XclExpXmlStream::restoreTabNames(const std::vector<OUString>& aOriginalTabNames)
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ for (SCTAB nTab=0; nTab < rDoc.GetTableCount(); nTab++)
+ {
+ const OUString& rOriginalName = aOriginalTabNames[nTab];
+
+ OUString rModifiedName;
+ rDoc.GetName(nTab, rModifiedName);
+
+ if (rOriginalName != rModifiedName)
+ {
+ renameTab(nTab, rOriginalName);
+ }
+ }
+}
+
+void XclExpXmlStream::renameTab(SCTAB aTab, OUString aNewName)
+{
+ ScDocShell* pShell = getDocShell();
+ ScDocument& rDoc = pShell->GetDocument();
+
+ bool bAutoCalcShellDisabled = rDoc.IsAutoCalcShellDisabled();
+ bool bIdleEnabled = rDoc.IsIdleEnabled();
+
+ rDoc.SetAutoCalcShellDisabled( true );
+ rDoc.EnableIdle(false);
+
+ if (rDoc.RenameTab(aTab, aNewName))
+ {
+ SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScTablesChanged));
+ }
+
+ rDoc.SetAutoCalcShellDisabled( bAutoCalcShellDisabled );
+ rDoc.EnableIdle(bIdleEnabled);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestring.cxx b/sc/source/filter/excel/xestring.cxx
new file mode 100644
index 000000000..295f37709
--- /dev/null
+++ b/sc/source/filter/excel/xestring.cxx
@@ -0,0 +1,565 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <cassert>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <tools/solar.h>
+#include <xlstyle.hxx>
+#include <xestyle.hxx>
+#include <xestream.hxx>
+#include <xestring.hxx>
+#include <oox/token/tokens.hxx>
+
+using namespace ::oox;
+
+namespace {
+
+// compare vectors
+
+/** Compares two vectors.
+ @return A negative value, if rLeft<rRight; or a positive value, if rLeft>rRight;
+ or 0, if rLeft==rRight. */
+template< typename Type >
+int lclCompareVectors( const ::std::vector< Type >& rLeft, const ::std::vector< Type >& rRight )
+{
+ int nResult = 0;
+
+ // 1st: compare all elements of the vectors
+ auto [aItL, aItR] = std::mismatch(rLeft.begin(), rLeft.end(), rRight.begin(), rRight.end());
+ if ((aItL != rLeft.end()) && (aItR != rRight.end()))
+ nResult = static_cast< int >( *aItL ) - static_cast< int >( *aItR );
+ else
+ // 2nd: compare the vector sizes. Shorter vector is less
+ nResult = static_cast< int >( rLeft.size() ) - static_cast< int >( rRight.size() );
+
+ return nResult;
+}
+
+// hashing helpers
+
+/** Base class for value hashers.
+ @descr These function objects are used to hash any value to a sal_uInt32 value. */
+template< typename Type >
+struct XclHasher {};
+
+template< typename Type >
+struct XclDirectHasher : public XclHasher< Type >
+{
+ sal_uInt32 operator()( Type nVal ) const { return nVal; }
+};
+
+struct XclFormatRunHasher : public XclHasher< const XclFormatRun& >
+{
+ sal_uInt32 operator()( const XclFormatRun& rRun ) const
+ { return (rRun.mnChar << 8) ^ rRun.mnFontIdx; }
+};
+
+/** Calculates a hash value from a vector.
+ @descr Uses the passed hasher function object to calculate hash values from
+ all vector elements. */
+template< typename Type, typename ValueHasher >
+sal_uInt16 lclHashVector( const ::std::vector< Type >& rVec, const ValueHasher& rHasher )
+{
+ sal_uInt32 nHash = rVec.size();
+ for( const auto& rItem : rVec )
+ nHash = (nHash * 31) + rHasher( rItem );
+ return static_cast< sal_uInt16 >( nHash ^ (nHash >> 16) );
+}
+
+/** Calculates a hash value from a vector. Uses XclDirectHasher to hash the vector elements. */
+template< typename Type >
+sal_uInt16 lclHashVector( const ::std::vector< Type >& rVec )
+{
+ return lclHashVector( rVec, XclDirectHasher< Type >() );
+}
+
+} // namespace
+
+// constructors ---------------------------------------------------------------
+
+XclExpString::XclExpString( XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( 0, nFlags, nMaxLen, true );
+}
+
+XclExpString::XclExpString( const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Assign( rString, nFlags, nMaxLen );
+}
+
+// assign ---------------------------------------------------------------------
+
+void XclExpString::Assign( const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Build( rString.getStr(), rString.getLength(), nFlags, nMaxLen );
+}
+
+void XclExpString::Assign( sal_Unicode cChar )
+{
+ Build( &cChar, 1, XclStrFlags::NONE, EXC_STR_MAXLEN );
+}
+
+void XclExpString::AssignByte(
+ std::u16string_view rString, rtl_TextEncoding eTextEnc, XclStrFlags nFlags,
+ sal_uInt16 nMaxLen )
+{
+ // length may differ from length of rString
+ OString aByteStr(OUStringToOString(rString, eTextEnc));
+ Build(aByteStr.getStr(), aByteStr.getLength(), nFlags, nMaxLen);
+}
+
+// append ---------------------------------------------------------------------
+
+void XclExpString::Append( std::u16string_view rString )
+{
+ BuildAppend( rString );
+}
+
+void XclExpString::AppendByte( std::u16string_view rString, rtl_TextEncoding eTextEnc )
+{
+ if (!rString.empty())
+ {
+ // length may differ from length of rString
+ OString aByteStr(OUStringToOString(rString, eTextEnc));
+ BuildAppend(aByteStr);
+ }
+}
+
+void XclExpString::AppendByte( sal_Unicode cChar, rtl_TextEncoding eTextEnc )
+{
+ if( !cChar )
+ {
+ char cByteChar = 0;
+ BuildAppend( std::string_view(&cByteChar, 1) );
+ }
+ else
+ {
+ OString aByteStr( &cChar, 1, eTextEnc ); // length may be >1
+ BuildAppend( aByteStr );
+ }
+}
+
+// formatting runs ------------------------------------------------------------
+
+void XclExpString::AppendFormat( sal_uInt16 nChar, sal_uInt16 nFontIdx, bool bDropDuplicate )
+{
+ OSL_ENSURE( maFormats.empty() || (maFormats.back().mnChar < nChar), "XclExpString::AppendFormat - invalid char index" );
+ size_t nMaxSize = static_cast< size_t >( mbIsBiff8 ? EXC_STR_MAXLEN : EXC_STR_MAXLEN_8BIT );
+ if( maFormats.empty() || ((maFormats.size() < nMaxSize) && (!bDropDuplicate || (maFormats.back().mnFontIdx != nFontIdx))) )
+ maFormats.emplace_back( nChar, nFontIdx );
+}
+
+void XclExpString::AppendTrailingFormat( sal_uInt16 nFontIdx )
+{
+ AppendFormat( mnLen, nFontIdx, false );
+}
+
+void XclExpString::LimitFormatCount( sal_uInt16 nMaxCount )
+{
+ if( maFormats.size() > nMaxCount )
+ maFormats.erase( maFormats.begin() + nMaxCount, maFormats.end() );
+}
+
+sal_uInt16 XclExpString::GetLeadingFont()
+{
+ sal_uInt16 nFontIdx = EXC_FONT_NOTFOUND;
+ if( !maFormats.empty() && (maFormats.front().mnChar == 0) )
+ {
+ nFontIdx = maFormats.front().mnFontIdx;
+ }
+ return nFontIdx;
+}
+
+sal_uInt16 XclExpString::RemoveLeadingFont()
+{
+ sal_uInt16 nFontIdx = GetLeadingFont();
+ if( nFontIdx != EXC_FONT_NOTFOUND )
+ {
+ maFormats.erase( maFormats.begin() );
+ }
+ return nFontIdx;
+}
+
+bool XclExpString::IsEqual( const XclExpString& rCmp ) const
+{
+ return
+ (mnLen == rCmp.mnLen) &&
+ (mbIsBiff8 == rCmp.mbIsBiff8) &&
+ (mbIsUnicode == rCmp.mbIsUnicode) &&
+ (mbWrapped == rCmp.mbWrapped) &&
+ (
+ ( mbIsBiff8 && (maUniBuffer == rCmp.maUniBuffer)) ||
+ (!mbIsBiff8 && (maCharBuffer == rCmp.maCharBuffer))
+ ) &&
+ (maFormats == rCmp.maFormats);
+}
+
+bool XclExpString::IsLessThan( const XclExpString& rCmp ) const
+{
+ int nResult = mbIsBiff8 ?
+ lclCompareVectors( maUniBuffer, rCmp.maUniBuffer ) :
+ lclCompareVectors( maCharBuffer, rCmp.maCharBuffer );
+ return (nResult != 0) ? (nResult < 0) : (maFormats < rCmp.maFormats);
+}
+
+// get data -------------------------------------------------------------------
+
+sal_uInt16 XclExpString::GetFormatsCount() const
+{
+ return static_cast< sal_uInt16 >( maFormats.size() );
+}
+
+sal_uInt8 XclExpString::GetFlagField() const
+{
+ return (mbIsUnicode ? EXC_STRF_16BIT : 0) | (IsWriteFormats() ? EXC_STRF_RICH : 0);
+}
+
+sal_uInt16 XclExpString::GetHeaderSize() const
+{
+ return
+ (mb8BitLen ? 1 : 2) + // length field
+ (IsWriteFlags() ? 1 : 0) + // flag field
+ (IsWriteFormats() ? 2 : 0); // richtext formatting count
+}
+
+std::size_t XclExpString::GetBufferSize() const
+{
+ return static_cast<std::size_t>(mnLen) * (mbIsUnicode ? 2 : 1);
+}
+
+std::size_t XclExpString::GetSize() const
+{
+ return
+ GetHeaderSize() + // header
+ GetBufferSize() + // character buffer
+ (IsWriteFormats() ? (4 * GetFormatsCount()) : 0); // richtext formatting
+}
+
+sal_uInt16 XclExpString::GetChar( sal_uInt16 nCharIdx ) const
+{
+ OSL_ENSURE( nCharIdx < Len(), "XclExpString::GetChar - invalid character index" );
+ return static_cast< sal_uInt16 >( mbIsBiff8 ? maUniBuffer[ nCharIdx ] : maCharBuffer[ nCharIdx ] );
+}
+
+sal_uInt16 XclExpString::GetHash() const
+{
+ return
+ (mbIsBiff8 ? lclHashVector( maUniBuffer ) : lclHashVector( maCharBuffer )) ^
+ lclHashVector( maFormats, XclFormatRunHasher() );
+}
+
+// streaming ------------------------------------------------------------------
+
+void XclExpString::WriteLenField( XclExpStream& rStrm ) const
+{
+ if( mb8BitLen )
+ rStrm << static_cast< sal_uInt8 >( mnLen );
+ else
+ rStrm << mnLen;
+}
+
+void XclExpString::WriteFlagField( XclExpStream& rStrm ) const
+{
+ if( mbIsBiff8 )
+ {
+ PrepareWrite( rStrm, 1 );
+ rStrm << GetFlagField();
+ rStrm.SetSliceSize( 0 );
+ }
+}
+
+void XclExpString::WriteHeader( XclExpStream& rStrm ) const
+{
+ OSL_ENSURE( !mb8BitLen || (mnLen < 256), "XclExpString::WriteHeader - string too long" );
+ PrepareWrite( rStrm, GetHeaderSize() );
+ // length
+ WriteLenField( rStrm );
+ // flag field
+ if( IsWriteFlags() )
+ rStrm << GetFlagField();
+ // format run count
+ if( IsWriteFormats() )
+ rStrm << GetFormatsCount();
+ rStrm.SetSliceSize( 0 );
+}
+
+void XclExpString::WriteBuffer( XclExpStream& rStrm ) const
+{
+ if( mbIsBiff8 )
+ rStrm.WriteUnicodeBuffer( maUniBuffer, GetFlagField() );
+ else
+ rStrm.WriteCharBuffer( maCharBuffer );
+}
+
+void XclExpString::WriteFormats( XclExpStream& rStrm, bool bWriteSize ) const
+{
+ if( !IsRich() )
+ return;
+
+ if( mbIsBiff8 )
+ {
+ if( bWriteSize )
+ rStrm << GetFormatsCount();
+ rStrm.SetSliceSize( 4 );
+ for( const auto& rFormat : maFormats )
+ rStrm << rFormat.mnChar << rFormat.mnFontIdx;
+ }
+ else
+ {
+ if( bWriteSize )
+ rStrm << static_cast< sal_uInt8 >( GetFormatsCount() );
+ rStrm.SetSliceSize( 2 );
+ for( const auto& rFormat : maFormats )
+ rStrm << static_cast< sal_uInt8 >( rFormat.mnChar ) << static_cast< sal_uInt8 >( rFormat.mnFontIdx );
+ }
+ rStrm.SetSliceSize( 0 );
+}
+
+void XclExpString::Write( XclExpStream& rStrm ) const
+{
+ if (!mbSkipHeader)
+ WriteHeader( rStrm );
+ WriteBuffer( rStrm );
+ if( IsWriteFormats() ) // only in BIFF8 included in string
+ WriteFormats( rStrm );
+}
+
+void XclExpString::WriteHeaderToMem( sal_uInt8* pnMem ) const
+{
+ assert(pnMem);
+ OSL_ENSURE( !mb8BitLen || (mnLen < 256), "XclExpString::WriteHeaderToMem - string too long" );
+ OSL_ENSURE( !IsWriteFormats(), "XclExpString::WriteHeaderToMem - formatted strings not supported" );
+ // length
+ if( mb8BitLen )
+ {
+ *pnMem = static_cast< sal_uInt8 >( mnLen );
+ ++pnMem;
+ }
+ else
+ {
+ ShortToSVBT16( mnLen, pnMem );
+ pnMem += 2;
+ }
+ // flag field
+ if( IsWriteFlags() )
+ *pnMem = GetFlagField();
+}
+
+void XclExpString::WriteBufferToMem( sal_uInt8* pnMem ) const
+{
+ assert(pnMem);
+ if( IsEmpty() )
+ return;
+
+ if( mbIsBiff8 )
+ {
+ for( const sal_uInt16 nChar : maUniBuffer )
+ {
+ *pnMem = static_cast< sal_uInt8 >( nChar );
+ ++pnMem;
+ if( mbIsUnicode )
+ {
+ *pnMem = static_cast< sal_uInt8 >( nChar >> 8 );
+ ++pnMem;
+ }
+ }
+ }
+ else
+ memcpy( pnMem, maCharBuffer.data(), mnLen );
+}
+
+void XclExpString::WriteToMem( sal_uInt8* pnMem ) const
+{
+ WriteHeaderToMem( pnMem );
+ WriteBufferToMem( pnMem + GetHeaderSize() );
+}
+
+static sal_uInt16 lcl_WriteRun( XclExpXmlStream& rStrm, const ScfUInt16Vec& rBuffer, sal_uInt16 nStart, sal_Int32 nLength, const XclExpFont* pFont )
+{
+ if( nLength == 0 )
+ return nStart;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(XML_r);
+ if( pFont )
+ {
+ const XclFontData& rFontData = pFont->GetFontData();
+ rWorksheet->startElement(XML_rPr);
+ XclXmlUtils::WriteFontData( rWorksheet, rFontData, XML_rFont );
+ rWorksheet->endElement( XML_rPr );
+ }
+ rWorksheet->startElement(XML_t, FSNS(XML_xml, XML_space), "preserve");
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString( rBuffer, nStart, nLength ) );
+ rWorksheet->endElement( XML_t );
+ rWorksheet->endElement( XML_r );
+ return nStart + nLength;
+}
+
+void XclExpString::WriteXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr rWorksheet = rStrm.GetCurrentStream();
+
+ if( !IsWriteFormats() )
+ {
+ rWorksheet->startElement(XML_t, FSNS(XML_xml, XML_space), "preserve");
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString( *this ) );
+ rWorksheet->endElement( XML_t );
+ }
+ else
+ {
+ XclExpFontBuffer& rFonts = rStrm.GetRoot().GetFontBuffer();
+
+ sal_uInt16 nStart = 0;
+ const XclExpFont* pFont = nullptr;
+ for ( const auto& rFormat : maFormats )
+ {
+ nStart = lcl_WriteRun( rStrm, GetUnicodeBuffer(),
+ nStart, rFormat.mnChar-nStart, pFont );
+ pFont = rFonts.GetFont( rFormat.mnFontIdx );
+ }
+ lcl_WriteRun( rStrm, GetUnicodeBuffer(),
+ nStart, GetUnicodeBuffer().size() - nStart, pFont );
+ }
+}
+
+bool XclExpString::IsWriteFlags() const
+{
+ return mbIsBiff8 && (!IsEmpty() || !mbSmartFlags);
+}
+
+bool XclExpString::IsWriteFormats() const
+{
+ return mbIsBiff8 && !mbSkipFormats && IsRich();
+}
+
+void XclExpString::SetStrLen( sal_Int32 nNewLen )
+{
+ sal_uInt16 nAllowedLen = (mb8BitLen && (mnMaxLen > 255)) ? 255 : mnMaxLen;
+ mnLen = limit_cast< sal_uInt16 >( nNewLen, 0, nAllowedLen );
+}
+
+void XclExpString::CharsToBuffer( const sal_Unicode* pcSource, sal_Int32 nBegin, sal_Int32 nLen )
+{
+ OSL_ENSURE( maUniBuffer.size() >= o3tl::make_unsigned( nBegin + nLen ),
+ "XclExpString::CharsToBuffer - char buffer invalid" );
+ ScfUInt16Vec::iterator aBeg = maUniBuffer.begin() + nBegin;
+ ScfUInt16Vec::iterator aEnd = aBeg + nLen;
+ const sal_Unicode* pcSrcChar = pcSource;
+ for( ScfUInt16Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar )
+ {
+ *aIt = static_cast< sal_uInt16 >( *pcSrcChar );
+ if( *aIt & 0xFF00 )
+ mbIsUnicode = true;
+ }
+ if( !mbWrapped )
+ mbWrapped = ::std::find( aBeg, aEnd, EXC_LF ) != aEnd;
+}
+
+void XclExpString::CharsToBuffer( const char* pcSource, sal_Int32 nBegin, sal_Int32 nLen )
+{
+ OSL_ENSURE( maCharBuffer.size() >= o3tl::make_unsigned( nBegin + nLen ),
+ "XclExpString::CharsToBuffer - char buffer invalid" );
+ ScfUInt8Vec::iterator aBeg = maCharBuffer.begin() + nBegin;
+ ScfUInt8Vec::iterator aEnd = aBeg + nLen;
+ const char* pcSrcChar = pcSource;
+ for( ScfUInt8Vec::iterator aIt = aBeg; aIt != aEnd; ++aIt, ++pcSrcChar )
+ *aIt = static_cast< sal_uInt8 >( *pcSrcChar );
+ mbIsUnicode = false;
+ if( !mbWrapped )
+ mbWrapped = ::std::find( aBeg, aEnd, EXC_LF_C ) != aEnd;
+}
+
+void XclExpString::Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen, bool bBiff8 )
+{
+ mbIsBiff8 = bBiff8;
+ mbIsUnicode = bBiff8 && ( nFlags & XclStrFlags::ForceUnicode );
+ mb8BitLen = bool( nFlags & XclStrFlags::EightBitLength );
+ mbSmartFlags = bBiff8 && ( nFlags & XclStrFlags::SmartFlags );
+ mbSkipFormats = bool( nFlags & XclStrFlags::SeparateFormats );
+ mbWrapped = false;
+ mbSkipHeader = bool( nFlags & XclStrFlags::NoHeader );
+ mnMaxLen = nMaxLen;
+ SetStrLen( nCurrLen );
+
+ maFormats.clear();
+ if( mbIsBiff8 )
+ {
+ maCharBuffer.clear();
+ maUniBuffer.resize( mnLen );
+ }
+ else
+ {
+ maUniBuffer.clear();
+ maCharBuffer.resize( mnLen );
+ }
+}
+
+void XclExpString::Build( const sal_Unicode* pcSource, sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( nCurrLen, nFlags, nMaxLen, true );
+ CharsToBuffer( pcSource, 0, mnLen );
+}
+
+void XclExpString::Build( const char* pcSource, sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen )
+{
+ Init( nCurrLen, nFlags, nMaxLen, false );
+ CharsToBuffer( pcSource, 0, mnLen );
+}
+
+void XclExpString::InitAppend( sal_Int32 nAddLen )
+{
+ SetStrLen( static_cast< sal_Int32 >( mnLen ) + nAddLen );
+ if( mbIsBiff8 )
+ maUniBuffer.resize( mnLen );
+ else
+ maCharBuffer.resize( mnLen );
+}
+
+void XclExpString::BuildAppend( std::u16string_view rSource )
+{
+ OSL_ENSURE( mbIsBiff8, "XclExpString::BuildAppend - must not be called at byte strings" );
+ if( mbIsBiff8 )
+ {
+ sal_uInt16 nOldLen = mnLen;
+ InitAppend( rSource.size() );
+ CharsToBuffer( rSource.data(), nOldLen, mnLen - nOldLen );
+ }
+}
+
+void XclExpString::BuildAppend( std::string_view rSource )
+{
+ OSL_ENSURE( !mbIsBiff8, "XclExpString::BuildAppend - must not be called at unicode strings" );
+ if( !mbIsBiff8 )
+ {
+ sal_uInt16 nOldLen = mnLen;
+ InitAppend( rSource.size() );
+ CharsToBuffer( rSource.data(), nOldLen, mnLen - nOldLen );
+ }
+}
+
+void XclExpString::PrepareWrite( XclExpStream& rStrm, sal_uInt16 nBytes ) const
+{
+ rStrm.SetSliceSize( nBytes + (mbIsUnicode ? 2 : 1) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xestyle.cxx b/sc/source/filter/excel/xestyle.cxx
new file mode 100644
index 000000000..1e9c426a3
--- /dev/null
+++ b/sc/source/filter/excel/xestyle.cxx
@@ -0,0 +1,3306 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xestyle.hxx>
+
+#include <algorithm>
+#include <iterator>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <comphelper/processfactory.hxx>
+#include <rtl/tencinfo.h>
+#include <vcl/font.hxx>
+#include <svl/languageoptions.hxx>
+#include <scitems.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/langitem.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xestring.hxx>
+#include <xltools.hxx>
+#include <conditio.hxx>
+#include <dbdata.hxx>
+#include <filterentries.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <svl/numformat.hxx>
+
+using namespace ::com::sun::star;
+using namespace oox;
+
+// PALETTE record - color information =========================================
+
+namespace {
+
+sal_uInt32 lclGetWeighting( XclExpColorType eType )
+{
+ switch( eType )
+ {
+ case EXC_COLOR_CHARTLINE: return 1;
+ case EXC_COLOR_CELLBORDER:
+ case EXC_COLOR_CHARTAREA: return 2;
+ case EXC_COLOR_CELLTEXT:
+ case EXC_COLOR_CHARTTEXT:
+ case EXC_COLOR_CTRLTEXT: return 10;
+ case EXC_COLOR_TABBG:
+ case EXC_COLOR_CELLAREA: return 20;
+ case EXC_COLOR_GRID: return 50;
+ default: OSL_FAIL( "lclGetWeighting - unknown color type" );
+ }
+ return 1;
+}
+
+sal_Int32 lclGetColorDistance( const Color& rColor1, const Color& rColor2 )
+{
+ sal_Int32 nDist = rColor1.GetRed() - rColor2.GetRed();
+ nDist *= nDist * 77;
+ sal_Int32 nDummy = rColor1.GetGreen() - rColor2.GetGreen();
+ nDist += nDummy * nDummy * 151;
+ nDummy = rColor1.GetBlue() - rColor2.GetBlue();
+ nDist += nDummy * nDummy * 28;
+ return nDist;
+}
+
+sal_uInt8 lclGetMergedColorComp( sal_uInt8 nComp1, sal_uInt32 nWeight1, sal_uInt8 nComp2, sal_uInt32 nWeight2 )
+{
+ sal_uInt8 nComp1Dist = ::std::min< sal_uInt8 >( nComp1, 0xFF - nComp1 );
+ sal_uInt8 nComp2Dist = ::std::min< sal_uInt8 >( nComp2, 0xFF - nComp2 );
+ if( nComp1Dist != nComp2Dist )
+ {
+ /* #i36945# One of the passed RGB components is nearer at the limits (0x00 or 0xFF).
+ Increase its weighting to prevent fading of the colors during reduction. */
+ const sal_uInt8& rnCompNearer = (nComp1Dist < nComp2Dist) ? nComp1 : nComp2;
+ sal_uInt32& rnWeight = (nComp1Dist < nComp2Dist) ? nWeight1 : nWeight2;
+ rnWeight *= ((rnCompNearer - 0x80L) * (rnCompNearer - 0x7FL) / 0x1000L + 1);
+ }
+ sal_uInt32 nWSum = nWeight1 + nWeight2;
+ return static_cast< sal_uInt8 >( (nComp1 * nWeight1 + nComp2 * nWeight2 + nWSum / 2) / nWSum );
+}
+
+void lclSetMixedColor( Color& rDest, const Color& rSrc1, const Color& rSrc2 )
+{
+ rDest.SetRed( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetRed() ) + rSrc2.GetRed()) / 2 ) );
+ rDest.SetGreen( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetGreen() ) + rSrc2.GetGreen()) / 2 ) );
+ rDest.SetBlue( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetBlue() ) + rSrc2.GetBlue()) / 2 ) );
+}
+
+} // namespace
+
+// additional classes for color reduction -------------------------------------
+
+namespace {
+
+/** Represents an entry in a color list.
+
+ The color stores a weighting value, which increases the more the color is
+ used in the document. Heavy-weighted colors will change less than others on
+ color reduction.
+ */
+class XclListColor
+{
+private:
+ Color maColor; /// The color value of this palette entry.
+ sal_uInt32 mnColorId; /// Unique color ID for color reduction.
+ sal_uInt32 mnWeight; /// Weighting for color reduction.
+ bool mbBaseColor; /// true = Handle as base color, (don't remove/merge).
+
+public:
+ explicit XclListColor( const Color& rColor, sal_uInt32 nColorId );
+
+ /** Returns the RGB color value of the color. */
+ const Color& GetColor() const { return maColor; }
+ /** Returns the unique ID of the color. */
+ sal_uInt32 GetColorId() const { return mnColorId; }
+ /** Returns the current weighting of the color. */
+ sal_uInt32 GetWeighting() const { return mnWeight; }
+ /** Returns true, if this color is a base color, i.e. it will not be removed or merged. */
+ bool IsBaseColor() const { return mbBaseColor; }
+
+ /** Adds the passed weighting to this color. */
+ void AddWeighting( sal_uInt32 nWeight ) { mnWeight += nWeight; }
+ /** Merges this color with rColor, regarding weighting settings. */
+ void Merge( const XclListColor& rColor );
+};
+
+XclListColor::XclListColor( const Color& rColor, sal_uInt32 nColorId ) :
+ maColor( rColor ),
+ mnColorId( nColorId ),
+ mnWeight( 0 )
+{
+ mbBaseColor =
+ ((rColor.GetRed() == 0x00) || (rColor.GetRed() == 0xFF)) &&
+ ((rColor.GetGreen() == 0x00) || (rColor.GetGreen() == 0xFF)) &&
+ ((rColor.GetBlue() == 0x00) || (rColor.GetBlue() == 0xFF));
+}
+
+void XclListColor::Merge( const XclListColor& rColor )
+{
+ sal_uInt32 nWeight2 = rColor.GetWeighting();
+ // do not change RGB value of base colors
+ if( !mbBaseColor )
+ {
+ maColor.SetRed( lclGetMergedColorComp( maColor.GetRed(), mnWeight, rColor.maColor.GetRed(), nWeight2 ) );
+ maColor.SetGreen( lclGetMergedColorComp( maColor.GetGreen(), mnWeight, rColor.maColor.GetGreen(), nWeight2 ) );
+ maColor.SetBlue( lclGetMergedColorComp( maColor.GetBlue(), mnWeight, rColor.maColor.GetBlue(), nWeight2 ) );
+ }
+ AddWeighting( nWeight2 );
+}
+
+/** Data for each inserted original color, represented by a color ID. */
+struct XclColorIdData
+{
+ Color maColor; /// The original inserted color.
+ sal_uInt32 mnIndex; /// Maps current color ID to color list or export color vector.
+ /** Sets the contents of this struct. */
+ void Set( const Color& rColor, sal_uInt32 nIndex ) { maColor = rColor; mnIndex = nIndex; }
+};
+
+/** A color that will be written to the Excel file. */
+struct XclPaletteColor
+{
+ Color maColor; /// Resulting color to export.
+ bool mbUsed; /// true = Entry is used in the document.
+
+ explicit XclPaletteColor( const Color& rColor ) : maColor( rColor ), mbUsed( false ) {}
+ void SetColor( const Color& rColor ) { maColor = rColor; mbUsed = true; }
+};
+
+/** Maps a color list index to a palette index.
+ @descr Used to remap the color ID data vector from list indexes to palette indexes. */
+struct XclRemap
+{
+ sal_uInt32 mnPalIndex; /// Index to palette.
+ bool mbProcessed; /// true = List color already processed.
+
+ explicit XclRemap() : mnPalIndex( 0 ), mbProcessed( false ) {}
+ void SetIndex( sal_uInt32 nPalIndex )
+ { mnPalIndex = nPalIndex; mbProcessed = true; }
+};
+
+/** Stores the nearest palette color index of a list color. */
+struct XclNearest
+{
+ sal_uInt32 mnPalIndex; /// Index to nearest palette color.
+ sal_Int32 mnDist; /// Distance to palette color.
+
+ explicit XclNearest() : mnPalIndex( 0 ), mnDist( 0 ) {}
+};
+
+} // namespace
+
+class XclExpPaletteImpl
+{
+public:
+ explicit XclExpPaletteImpl( const XclDefaultPalette& rDefPal );
+
+ /** Inserts the color into the list and updates weighting.
+ @param nAutoDefault The Excel palette index for automatic color.
+ @return A unique ID for this color. */
+ sal_uInt32 InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault = 0 );
+ /** Returns the color ID representing a fixed Excel palette index (i.e. for auto colors). */
+ static sal_uInt32 GetColorIdFromIndex( sal_uInt16 nIndex );
+
+ /** Reduces the color list to the maximum count of the current BIFF version. */
+ void Finalize();
+
+ /** Returns the Excel palette index of the color with passed color ID. */
+ sal_uInt16 GetColorIndex( sal_uInt32 nColorId ) const;
+
+ /** Returns a foreground and background color for the two passed color IDs.
+ @descr If rnXclPattern contains a solid pattern, this function tries to find
+ the two best fitting colors and a mix pattern (25%, 50% or 75%) for nForeColorId.
+ This will result in a better approximation to the passed foreground color. */
+ void GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const;
+
+ /** Returns the RGB color for a (non-zero-based) Excel palette entry.
+ @return The color from current or default palette or COL_AUTO, if nothing else found. */
+ Color GetColor( sal_uInt16 nXclIndex ) const;
+
+ /** Returns true, if all colors of the palette are equal to default palette colors. */
+ bool IsDefaultPalette() const;
+ /** Writes the color list (contents of the palette record) to the passed stream. */
+ void WriteBody( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ /** Returns the Excel index of a 0-based color index. */
+ static sal_uInt16 GetXclIndex( sal_uInt32 nIndex )
+ { return static_cast< sal_uInt16 >( nIndex + EXC_COLOR_USEROFFSET ); }
+
+ /** Returns the original inserted color represented by the color ID nColorId. */
+ const Color& GetOriginalColor( sal_uInt32 nColorId ) const;
+
+ /** Searches for rColor, returns the ordered insertion index for rColor in rnIndex. */
+ XclListColor* SearchListEntry( const Color& rColor, sal_uInt32& rnIndex );
+ /** Creates and inserts a new color list entry at the specified list position. */
+ XclListColor* CreateListEntry( const Color& rColor, sal_uInt32 nIndex );
+
+ /** Raw and fast reduction of the palette. */
+ void RawReducePalette( sal_uInt32 nPass );
+ /** Reduction of one color using advanced color merging based on color weighting. */
+ void ReduceLeastUsedColor();
+
+ /** Finds the least used color and returns its current list index. */
+ sal_uInt32 GetLeastUsedListColor() const;
+ /** Returns the list index of the color nearest to rColor.
+ @param nIgnore List index of a color which will be ignored.
+ @return The list index of the found color. */
+ sal_uInt32 GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const;
+ /** Returns the list index of the color nearest to the color with list index nIndex. */
+ sal_uInt32 GetNearestListColor( sal_uInt32 nIndex ) const;
+
+ /** Returns in rnIndex the palette index of the color nearest to rColor.
+ Searches for default colors only (colors never replaced).
+ @return The distance from passed color to found color. */
+ sal_Int32 GetNearestPaletteColor(
+ sal_uInt32& rnIndex,
+ const Color& rColor ) const;
+ /** Returns in rnFirst and rnSecond the palette indexes of the two colors nearest to rColor.
+ @return The minimum distance from passed color to found colors. */
+ sal_Int32 GetNearPaletteColors(
+ sal_uInt32& rnFirst, sal_uInt32& rnSecond,
+ const Color& rColor ) const;
+
+private:
+ typedef std::vector< std::unique_ptr<XclListColor> > XclListColorList;
+ typedef std::shared_ptr< XclListColorList > XclListColorListRef;
+
+ const XclDefaultPalette& mrDefPal; /// The default palette for the current BIFF version.
+ XclListColorListRef mxColorList; /// Working color list.
+ std::vector< XclColorIdData >
+ maColorIdDataVec; /// Data of all CIDs.
+ std::vector< XclPaletteColor >
+ maPalette; /// Contains resulting colors to export.
+ sal_uInt32 mnLastIdx; /// Last insertion index for search opt.
+};
+
+const sal_uInt32 EXC_PAL_INDEXBASE = 0xFFFF0000;
+const sal_uInt32 EXC_PAL_MAXRAWSIZE = 1024;
+
+XclExpPaletteImpl::XclExpPaletteImpl( const XclDefaultPalette& rDefPal ) :
+ mrDefPal( rDefPal ),
+ mxColorList( std::make_shared<XclListColorList>() ),
+ mnLastIdx( 0 )
+{
+ // initialize maPalette with default colors
+ sal_uInt16 nCount = static_cast< sal_uInt16 >( mrDefPal.GetColorCount() );
+ maPalette.reserve( nCount );
+ for( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
+ maPalette.emplace_back( mrDefPal.GetDefColor( GetXclIndex( nIdx ) ) );
+
+ InsertColor( COL_BLACK, EXC_COLOR_CELLTEXT );
+}
+
+sal_uInt32 XclExpPaletteImpl::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault )
+{
+ if( rColor == COL_AUTO )
+ return GetColorIdFromIndex( nAutoDefault );
+
+ sal_uInt32 nFoundIdx = 0;
+ XclListColor* pEntry = SearchListEntry( rColor, nFoundIdx );
+ if( !pEntry || (pEntry->GetColor() != rColor) )
+ pEntry = CreateListEntry( rColor, nFoundIdx );
+ pEntry->AddWeighting( lclGetWeighting( eType ) );
+
+ return pEntry->GetColorId();
+}
+
+sal_uInt32 XclExpPaletteImpl::GetColorIdFromIndex( sal_uInt16 nIndex )
+{
+ return EXC_PAL_INDEXBASE | nIndex;
+}
+
+void XclExpPaletteImpl::Finalize()
+{
+// --- build initial color ID data vector (maColorIdDataVec) ---
+
+ sal_uInt32 nCount = mxColorList->size();
+ maColorIdDataVec.resize( nCount );
+ for( sal_uInt32 nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ const XclListColor& listColor = *mxColorList->at( nIdx );
+ maColorIdDataVec[ listColor.GetColorId() ].Set( listColor.GetColor(), nIdx );
+ }
+
+// --- loop as long as current color count does not fit into palette of current BIFF ---
+
+ // phase 1: raw reduction (performance reasons, #i36945#)
+ sal_uInt32 nPass = 0;
+ while( mxColorList->size() > EXC_PAL_MAXRAWSIZE )
+ RawReducePalette( nPass++ );
+
+ // phase 2: precise reduction using advanced color merging based on color weighting
+ while( mxColorList->size() > mrDefPal.GetColorCount() )
+ ReduceLeastUsedColor();
+
+// --- use default palette and replace colors with nearest used colors ---
+
+ nCount = mxColorList->size();
+ std::vector< XclRemap > aRemapVec( nCount );
+ std::vector< XclNearest > aNearestVec( nCount );
+
+ // in each run: search the best fitting color and replace a default color with it
+ for( sal_uInt32 nRun = 0; nRun < nCount; ++nRun )
+ {
+ sal_uInt32 nIndex;
+ // find nearest unused default color for each unprocessed list color
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ aNearestVec[ nIndex ].mnDist = aRemapVec[ nIndex ].mbProcessed ? SAL_MAX_INT32 :
+ GetNearestPaletteColor( aNearestVec[ nIndex ].mnPalIndex, mxColorList->at( nIndex )->GetColor() );
+ // find the list color which is nearest to a default color
+ sal_uInt32 nFound = 0;
+ for( nIndex = 1; nIndex < nCount; ++nIndex )
+ if( aNearestVec[ nIndex ].mnDist < aNearestVec[ nFound ].mnDist )
+ nFound = nIndex;
+ // replace default color with list color
+ sal_uInt32 nNearest = aNearestVec[ nFound ].mnPalIndex;
+ OSL_ENSURE( nNearest < maPalette.size(), "XclExpPaletteImpl::Finalize - algorithm error" );
+ maPalette[ nNearest ].SetColor( mxColorList->at( nFound )->GetColor() );
+ aRemapVec[ nFound ].SetIndex( nNearest );
+ }
+
+ // remap color ID data map (maColorIdDataVec) from list indexes to palette indexes
+ for( auto& rColorIdData : maColorIdDataVec )
+ rColorIdData.mnIndex = aRemapVec[ rColorIdData.mnIndex ].mnPalIndex;
+}
+
+sal_uInt16 XclExpPaletteImpl::GetColorIndex( sal_uInt32 nColorId ) const
+{
+ sal_uInt16 nRet = 0;
+ if( nColorId >= EXC_PAL_INDEXBASE )
+ nRet = static_cast< sal_uInt16 >( nColorId & ~EXC_PAL_INDEXBASE );
+ else if( nColorId < maColorIdDataVec.size() )
+ nRet = GetXclIndex( maColorIdDataVec[ nColorId ].mnIndex );
+ return nRet;
+}
+
+void XclExpPaletteImpl::GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const
+{
+ rnXclForeIx = GetColorIndex( nForeColorId );
+ rnXclBackIx = GetColorIndex( nBackColorId );
+ if( (rnXclPattern != EXC_PATT_SOLID) || (nForeColorId >= maColorIdDataVec.size()) )
+ return;
+
+ // now we have solid pattern, and a defined foreground (background doesn't care for solid pattern)
+
+ sal_uInt32 nIndex1, nIndex2;
+ Color aForeColor( GetOriginalColor( nForeColorId ) );
+ sal_Int32 nFirstDist = GetNearPaletteColors( nIndex1, nIndex2, aForeColor );
+ if( (nIndex1 >= maPalette.size()) || (nIndex2 >= maPalette.size()) )
+ return;
+
+ Color aColorArr[ 5 ];
+ aColorArr[ 0 ] = maPalette[ nIndex1 ].maColor;
+ aColorArr[ 4 ] = maPalette[ nIndex2 ].maColor;
+ lclSetMixedColor( aColorArr[ 2 ], aColorArr[ 0 ], aColorArr[ 4 ] );
+ lclSetMixedColor( aColorArr[ 1 ], aColorArr[ 0 ], aColorArr[ 2 ] );
+ lclSetMixedColor( aColorArr[ 3 ], aColorArr[ 2 ], aColorArr[ 4 ] );
+
+ sal_Int32 nMinDist = nFirstDist;
+ sal_uInt32 nMinIndex = 0;
+ for( sal_uInt32 nCnt = 1; nCnt < 4; ++nCnt )
+ {
+ sal_Int32 nDist = lclGetColorDistance( aForeColor, aColorArr[ nCnt ] );
+ if( nDist < nMinDist )
+ {
+ nMinDist = nDist;
+ nMinIndex = nCnt;
+ }
+ }
+ rnXclForeIx = GetXclIndex( nIndex1 );
+ rnXclBackIx = GetXclIndex( nIndex2 );
+ if( nMinDist < nFirstDist )
+ {
+ switch( nMinIndex )
+ {
+ case 1: rnXclPattern = EXC_PATT_75_PERC; break;
+ case 2: rnXclPattern = EXC_PATT_50_PERC; break;
+ case 3: rnXclPattern = EXC_PATT_25_PERC; break;
+ }
+ }
+}
+
+Color XclExpPaletteImpl::GetColor( sal_uInt16 nXclIndex ) const
+{
+ if( nXclIndex >= EXC_COLOR_USEROFFSET )
+ {
+ sal_uInt32 nIdx = nXclIndex - EXC_COLOR_USEROFFSET;
+ if( nIdx < maPalette.size() )
+ return maPalette[ nIdx ].maColor;
+ }
+ return mrDefPal.GetDefColor( nXclIndex );
+}
+
+bool XclExpPaletteImpl::IsDefaultPalette() const
+{
+ bool bDefault = true;
+ for( sal_uInt32 nIdx = 0, nSize = static_cast< sal_uInt32 >( maPalette.size() ); bDefault && (nIdx < nSize); ++nIdx )
+ bDefault = maPalette[ nIdx ].maColor == mrDefPal.GetDefColor( GetXclIndex( nIdx ) );
+ return bDefault;
+}
+
+void XclExpPaletteImpl::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >( maPalette.size() );
+ for( const auto& rColor : maPalette )
+ rStrm << rColor.maColor;
+}
+
+void XclExpPaletteImpl::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maPalette.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_colors);
+ rStyleSheet->startElement(XML_indexedColors);
+ for( const auto& rColor : maPalette )
+ rStyleSheet->singleElement(XML_rgbColor, XML_rgb, XclXmlUtils::ToOString(rColor.maColor));
+ rStyleSheet->endElement( XML_indexedColors );
+ rStyleSheet->endElement( XML_colors );
+}
+
+const Color& XclExpPaletteImpl::GetOriginalColor( sal_uInt32 nColorId ) const
+{
+ if( nColorId < maColorIdDataVec.size() )
+ return maColorIdDataVec[ nColorId ].maColor;
+ return maPalette[ 0 ].maColor;
+}
+
+XclListColor* XclExpPaletteImpl::SearchListEntry( const Color& rColor, sal_uInt32& rnIndex )
+{
+ rnIndex = 0;
+
+ if (mxColorList->empty())
+ return nullptr;
+
+ XclListColor* pEntry = nullptr;
+
+ // search optimization for equal-colored objects occurring repeatedly
+ if (mnLastIdx < mxColorList->size())
+ {
+ pEntry = (*mxColorList)[mnLastIdx].get();
+ if( pEntry->GetColor() == rColor )
+ {
+ rnIndex = mnLastIdx;
+ return pEntry;
+ }
+ }
+
+ // binary search for color
+ sal_uInt32 nBegIdx = 0;
+ sal_uInt32 nEndIdx = mxColorList->size();
+ bool bFound = false;
+ while( !bFound && (nBegIdx < nEndIdx) )
+ {
+ rnIndex = (nBegIdx + nEndIdx) / 2;
+ pEntry = (*mxColorList)[rnIndex].get();
+ bFound = pEntry->GetColor() == rColor;
+ if( !bFound )
+ {
+ if( pEntry->GetColor() < rColor )
+ nBegIdx = rnIndex + 1;
+ else
+ nEndIdx = rnIndex;
+ }
+ }
+
+ // not found - use end of range as new insertion position
+ if( !bFound )
+ rnIndex = nEndIdx;
+
+ mnLastIdx = rnIndex;
+ return pEntry;
+}
+
+XclListColor* XclExpPaletteImpl::CreateListEntry( const Color& rColor, sal_uInt32 nIndex )
+{
+ XclListColor* pEntry = new XclListColor( rColor, mxColorList->size() );
+ mxColorList->insert(mxColorList->begin() + nIndex, std::unique_ptr<XclListColor>(pEntry));
+ return pEntry;
+}
+
+void XclExpPaletteImpl::RawReducePalette( sal_uInt32 nPass )
+{
+ /* Fast palette reduction - in each call of this function one RGB component
+ of each color is reduced to a lower number of distinct values.
+ Pass 0: Blue is reduced to 128 distinct values.
+ Pass 1: Red is reduced to 128 distinct values.
+ Pass 2: Green is reduced to 128 distinct values.
+ Pass 3: Blue is reduced to 64 distinct values.
+ Pass 4: Red is reduced to 64 distinct values.
+ Pass 5: Green is reduced to 64 distinct values.
+ And so on...
+ */
+
+ XclListColorListRef xOldList = mxColorList;
+ mxColorList = std::make_shared<XclListColorList>();
+
+ // maps old list indexes to new list indexes, used to update maColorIdDataVec
+ ScfUInt32Vec aListIndexMap;
+ aListIndexMap.reserve( xOldList->size() );
+
+ // preparations
+ sal_uInt8 nR, nG, nB;
+ sal_uInt8& rnComp = ((nPass % 3 == 0) ? nB : ((nPass % 3 == 1) ? nR : nG));
+ nPass /= 3;
+ OSL_ENSURE( nPass < 7, "XclExpPaletteImpl::RawReducePalette - reduction not terminated" );
+
+ static const sal_uInt8 spnFactor2[] = { 0x81, 0x82, 0x84, 0x88, 0x92, 0xAA, 0xFF };
+ sal_uInt8 nFactor1 = static_cast< sal_uInt8 >( 0x02 << nPass );
+ sal_uInt8 nFactor2 = spnFactor2[ nPass ];
+ sal_uInt8 nFactor3 = static_cast< sal_uInt8 >( 0x40 >> nPass );
+
+ // process each color in the old color list
+ for(const std::unique_ptr<XclListColor> & pOldColor : *xOldList)
+ {
+ // get the old list entry
+ const XclListColor* pOldEntry = pOldColor.get();
+ nR = pOldEntry->GetColor().GetRed();
+ nG = pOldEntry->GetColor().GetGreen();
+ nB = pOldEntry->GetColor().GetBlue();
+
+ /* Calculate the new RGB component (rnComp points to one of nR, nG, nB).
+ Using integer arithmetic with its rounding errors, the results of
+ this calculation are always exactly in the range 0x00 to 0xFF
+ (simply cutting the lower bits would darken the colors slightly). */
+ sal_uInt32 nNewComp = rnComp;
+ nNewComp /= nFactor1;
+ nNewComp *= nFactor2;
+ nNewComp /= nFactor3;
+ rnComp = static_cast< sal_uInt8 >( nNewComp );
+ Color aNewColor( nR, nG, nB );
+
+ // find or insert the new color
+ sal_uInt32 nFoundIdx = 0;
+ XclListColor* pNewEntry = SearchListEntry( aNewColor, nFoundIdx );
+ if( !pNewEntry || (pNewEntry->GetColor() != aNewColor) )
+ pNewEntry = CreateListEntry( aNewColor, nFoundIdx );
+ pNewEntry->AddWeighting( pOldEntry->GetWeighting() );
+ aListIndexMap.push_back( nFoundIdx );
+ }
+
+ // update color ID data map (maps color IDs to color list indexes), replace old by new list indexes
+ for( auto& rColorIdData : maColorIdDataVec )
+ rColorIdData.mnIndex = aListIndexMap[ rColorIdData.mnIndex ];
+}
+
+void XclExpPaletteImpl::ReduceLeastUsedColor()
+{
+ // find a list color to remove
+ sal_uInt32 nRemove = GetLeastUsedListColor();
+ // find its nearest neighbor
+ sal_uInt32 nKeep = GetNearestListColor( nRemove );
+
+ // merge both colors to one color, remove one color from list
+ XclListColor* pKeepEntry = mxColorList->at(nKeep).get();
+ XclListColor* pRemoveEntry = mxColorList->at(nRemove).get();
+ if( !(pKeepEntry && pRemoveEntry) )
+ return;
+
+ // merge both colors (if pKeepEntry is a base color, it will not change)
+ pKeepEntry->Merge( *pRemoveEntry );
+ // remove the less used color, adjust nKeep index if kept color follows removed color
+ XclListColorList::iterator itr = mxColorList->begin();
+ ::std::advance(itr, nRemove);
+ mxColorList->erase(itr);
+ if( nKeep > nRemove ) --nKeep;
+
+ // recalculate color ID data map (maps color IDs to color list indexes)
+ for( auto& rColorIdData : maColorIdDataVec )
+ {
+ if( rColorIdData.mnIndex > nRemove )
+ --rColorIdData.mnIndex;
+ else if( rColorIdData.mnIndex == nRemove )
+ rColorIdData.mnIndex = nKeep;
+ }
+}
+
+sal_uInt32 XclExpPaletteImpl::GetLeastUsedListColor() const
+{
+ sal_uInt32 nFound = 0;
+ sal_uInt32 nMinW = SAL_MAX_UINT32;
+
+ for( sal_uInt32 nIdx = 0, nCount = mxColorList->size(); nIdx < nCount; ++nIdx )
+ {
+ XclListColor& rEntry = *mxColorList->at( nIdx );
+ // ignore the base colors
+ if( !rEntry.IsBaseColor() && (rEntry.GetWeighting() < nMinW) )
+ {
+ nFound = nIdx;
+ nMinW = rEntry.GetWeighting();
+ }
+ }
+ return nFound;
+}
+
+sal_uInt32 XclExpPaletteImpl::GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const
+{
+ sal_uInt32 nFound = 0;
+ sal_Int32 nMinD = SAL_MAX_INT32;
+
+ for( sal_uInt32 nIdx = 0, nCount = mxColorList->size(); nIdx < nCount; ++nIdx )
+ {
+ if( nIdx != nIgnore )
+ {
+ if( XclListColor* pEntry = mxColorList->at(nIdx).get() )
+ {
+ sal_Int32 nDist = lclGetColorDistance( rColor, pEntry->GetColor() );
+ if( nDist < nMinD )
+ {
+ nFound = nIdx;
+ nMinD = nDist;
+ }
+ }
+ }
+ }
+ return nFound;
+}
+
+sal_uInt32 XclExpPaletteImpl::GetNearestListColor( sal_uInt32 nIndex ) const
+{
+ if (nIndex >= mxColorList->size())
+ return 0;
+ XclListColor* pEntry = mxColorList->at(nIndex).get();
+ return GetNearestListColor( pEntry->GetColor(), nIndex );
+}
+
+sal_Int32 XclExpPaletteImpl::GetNearestPaletteColor(
+ sal_uInt32& rnIndex, const Color& rColor ) const
+{
+ rnIndex = 0;
+ sal_Int32 nDist = SAL_MAX_INT32;
+
+ sal_uInt32 nPaletteIndex = 0;
+ for( const auto& rPaletteColor : maPalette )
+ {
+ if( !rPaletteColor.mbUsed )
+ {
+ sal_Int32 nCurrDist = lclGetColorDistance( rColor, rPaletteColor.maColor );
+ if( nCurrDist < nDist )
+ {
+ rnIndex = nPaletteIndex;
+ nDist = nCurrDist;
+ }
+ }
+ ++nPaletteIndex;
+ }
+ return nDist;
+}
+
+sal_Int32 XclExpPaletteImpl::GetNearPaletteColors(
+ sal_uInt32& rnFirst, sal_uInt32& rnSecond, const Color& rColor ) const
+{
+ rnFirst = rnSecond = 0;
+ sal_Int32 nDist1 = SAL_MAX_INT32;
+ sal_Int32 nDist2 = SAL_MAX_INT32;
+
+ sal_uInt32 nPaletteIndex = 0;
+ for( const auto& rPaletteColor : maPalette )
+ {
+ sal_Int32 nCurrDist = lclGetColorDistance( rColor, rPaletteColor.maColor );
+ if( nCurrDist < nDist1 )
+ {
+ rnSecond = rnFirst;
+ nDist2 = nDist1;
+ rnFirst = nPaletteIndex;
+ nDist1 = nCurrDist;
+ }
+ else if( nCurrDist < nDist2 )
+ {
+ rnSecond = nPaletteIndex;
+ nDist2 = nCurrDist;
+ }
+ ++nPaletteIndex;
+ }
+ return nDist1;
+}
+
+XclExpPalette::XclExpPalette( const XclExpRoot& rRoot ) :
+ XclDefaultPalette( rRoot ),
+ XclExpRecord( EXC_ID_PALETTE )
+{
+ mxImpl = std::make_shared<XclExpPaletteImpl>( *this );
+ SetRecSize( GetColorCount() * 4 + 2 );
+}
+
+XclExpPalette::~XclExpPalette()
+{
+}
+
+sal_uInt32 XclExpPalette::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault )
+{
+ return mxImpl->InsertColor( rColor, eType, nAutoDefault );
+}
+
+sal_uInt32 XclExpPalette::GetColorIdFromIndex( sal_uInt16 nIndex )
+{
+ return XclExpPaletteImpl::GetColorIdFromIndex( nIndex );
+}
+
+void XclExpPalette::Finalize()
+{
+ mxImpl->Finalize();
+}
+
+sal_uInt16 XclExpPalette::GetColorIndex( sal_uInt32 nColorId ) const
+{
+ return mxImpl->GetColorIndex( nColorId );
+}
+
+void XclExpPalette::GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const
+{
+ return mxImpl->GetMixedColors( rnXclForeIx, rnXclBackIx, rnXclPattern, nForeColorId, nBackColorId );
+}
+
+Color XclExpPalette::GetColor( sal_uInt16 nXclIndex ) const
+{
+ return mxImpl->GetColor( nXclIndex );
+}
+
+void XclExpPalette::Save( XclExpStream& rStrm )
+{
+ if( !mxImpl->IsDefaultPalette() )
+ XclExpRecord::Save( rStrm );
+}
+
+void XclExpPalette::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !mxImpl->IsDefaultPalette() )
+ mxImpl->SaveXml( rStrm );
+}
+
+void XclExpPalette::WriteBody( XclExpStream& rStrm )
+{
+ mxImpl->WriteBody( rStrm );
+}
+
+// FONT record - font information =============================================
+
+namespace {
+
+typedef ::std::pair< sal_uInt16, sal_Int16 > WhichAndScript;
+
+sal_Int16 lclCheckFontItems( const SfxItemSet& rItemSet,
+ const WhichAndScript& rWAS1, const WhichAndScript& rWAS2, const WhichAndScript& rWAS3 )
+{
+ if( ScfTools::CheckItem( rItemSet, rWAS1.first, false ) ) return rWAS1.second;
+ if( ScfTools::CheckItem( rItemSet, rWAS2.first, false ) ) return rWAS2.second;
+ if( ScfTools::CheckItem( rItemSet, rWAS3.first, false ) ) return rWAS3.second;
+ return 0;
+};
+
+} // namespace
+
+sal_Int16 XclExpFontHelper::GetFirstUsedScript( const XclExpRoot& rRoot, const SfxItemSet& rItemSet )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+ /* #i17050# #i107170# We need to determine which font items are set in the
+ item set, and which script type we should prefer according to the
+ current language settings. */
+
+ static const WhichAndScript WAS_LATIN( ATTR_FONT, css::i18n::ScriptType::LATIN );
+ static const WhichAndScript WAS_ASIAN( ATTR_CJK_FONT, css::i18n::ScriptType::ASIAN );
+ static const WhichAndScript WAS_CMPLX( ATTR_CTL_FONT, css::i18n::ScriptType::COMPLEX );
+
+ /* do not let a font from a parent style override an explicit
+ cell font. */
+
+ sal_Int16 nDefScript = rRoot.GetDefApiScript();
+ sal_Int16 nScript = 0;
+ const SfxItemSet* pCurrSet = &rItemSet;
+
+ while( (nScript == 0) && pCurrSet )
+ {
+ switch( nDefScript )
+ {
+ case ApiScriptType::LATIN:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_LATIN, WAS_CMPLX, WAS_ASIAN );
+ break;
+ case ApiScriptType::ASIAN:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_ASIAN, WAS_CMPLX, WAS_LATIN );
+ break;
+ case ApiScriptType::COMPLEX:
+ nScript = lclCheckFontItems( *pCurrSet, WAS_CMPLX, WAS_ASIAN, WAS_LATIN );
+ break;
+ default:
+ OSL_FAIL( "XclExpFontHelper::GetFirstUsedScript - unknown script type" );
+ nScript = ApiScriptType::LATIN;
+ };
+ pCurrSet = pCurrSet->GetParent();
+ }
+
+ if (nScript == 0)
+ nScript = nDefScript;
+
+ if (nScript == 0)
+ {
+ OSL_FAIL( "XclExpFontHelper::GetFirstUsedScript - unknown script type" );
+ nScript = ApiScriptType::LATIN;
+ }
+
+ return nScript;
+}
+
+vcl::Font XclExpFontHelper::GetFontFromItemSet( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript )
+{
+ // if WEAK is passed, guess script type from existing items in the item set
+ if( nScript == css::i18n::ScriptType::WEAK )
+ nScript = GetFirstUsedScript( rRoot, rItemSet );
+
+ // convert to core script type constants
+ SvtScriptType nScScript = SvtLanguageOptions::FromI18NToSvtScriptType(nScript);
+
+ // fill the font object
+ vcl::Font aFont;
+ ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW, nullptr, nullptr, nullptr, nScScript );
+ return aFont;
+}
+
+ScDxfFont XclExpFontHelper::GetDxfFontFromItemSet(const XclExpRoot& rRoot, const SfxItemSet& rItemSet)
+{
+ sal_Int16 nScript = GetFirstUsedScript(rRoot, rItemSet);
+
+ // convert to core script type constants
+ SvtScriptType nScScript = SvtLanguageOptions::FromI18NToSvtScriptType(nScript);
+ return ScPatternAttr::GetDxfFont(rItemSet, nScScript);
+}
+
+bool XclExpFontHelper::CheckItems( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript, bool bDeep )
+{
+ static const sal_uInt16 pnCommonIds[] = {
+ ATTR_FONT_UNDERLINE, ATTR_FONT_CROSSEDOUT, ATTR_FONT_CONTOUR,
+ ATTR_FONT_SHADOWED, ATTR_FONT_COLOR, ATTR_FONT_LANGUAGE, 0 };
+ static const sal_uInt16 pnLatinIds[] = {
+ ATTR_FONT, ATTR_FONT_HEIGHT, ATTR_FONT_WEIGHT, ATTR_FONT_POSTURE, 0 };
+ static const sal_uInt16 pnAsianIds[] = {
+ ATTR_CJK_FONT, ATTR_CJK_FONT_HEIGHT, ATTR_CJK_FONT_WEIGHT, ATTR_CJK_FONT_POSTURE, 0 };
+ static const sal_uInt16 pnComplexIds[] = {
+ ATTR_CTL_FONT, ATTR_CTL_FONT_HEIGHT, ATTR_CTL_FONT_WEIGHT, ATTR_CTL_FONT_POSTURE, 0 };
+
+ bool bUsed = ScfTools::CheckItems( rItemSet, pnCommonIds, bDeep );
+ if( !bUsed )
+ {
+ namespace ApiScriptType = css::i18n::ScriptType;
+ // if WEAK is passed, guess script type from existing items in the item set
+ if( nScript == ApiScriptType::WEAK )
+ nScript = GetFirstUsedScript( rRoot, rItemSet );
+ // check the correct items
+ switch( nScript )
+ {
+ case ApiScriptType::LATIN: bUsed = ScfTools::CheckItems( rItemSet, pnLatinIds, bDeep ); break;
+ case ApiScriptType::ASIAN: bUsed = ScfTools::CheckItems( rItemSet, pnAsianIds, bDeep ); break;
+ case ApiScriptType::COMPLEX: bUsed = ScfTools::CheckItems( rItemSet, pnComplexIds, bDeep ); break;
+ default: OSL_FAIL( "XclExpFontHelper::CheckItems - unknown script type" );
+ }
+ }
+ return bUsed;
+}
+
+namespace {
+
+sal_uInt32 lclCalcHash( const XclFontData& rFontData )
+{
+ sal_uInt32 nHash = rFontData.maName.getLength();
+ nHash += sal_uInt32(rFontData.maColor) * 2;
+ nHash += rFontData.mnWeight * 3;
+ nHash += rFontData.mnCharSet * 5;
+ nHash += rFontData.mnFamily * 7;
+ nHash += rFontData.mnHeight * 11;
+ nHash += rFontData.mnUnderline * 13;
+ nHash += rFontData.mnEscapem * 17;
+ if( rFontData.mbItalic ) nHash += 19;
+ if( rFontData.mbStrikeout ) nHash += 23;
+ if( rFontData.mbOutline ) nHash += 29;
+ if( rFontData.mbShadow ) nHash += 31;
+ return nHash;
+}
+
+} // namespace
+
+XclExpFont::XclExpFont( const XclExpRoot& rRoot,
+ const XclFontData& rFontData, XclExpColorType eColorType ) :
+ XclExpRecord( EXC_ID2_FONT, 14 ),
+ XclExpRoot( rRoot ),
+ maData( rFontData )
+{
+ // insert font color into palette
+ mnColorId = rRoot.GetPalette().InsertColor( rFontData.maColor, eColorType, EXC_COLOR_FONTAUTO );
+ // hash value for faster comparison
+ mnHash = lclCalcHash( maData );
+ // record size
+ sal_Int32 nStrLen = maData.maName.getLength();
+ SetRecSize( ((GetBiff() == EXC_BIFF8) ? (nStrLen * 2 + 1) : nStrLen) + 15 );
+}
+
+bool XclExpFont::Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const
+{
+ return (mnHash == nHash) && (maData == rFontData);
+}
+
+void XclExpFont::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_font);
+ XclXmlUtils::WriteFontData( rStyleSheet, maData, XML_name );
+ // OOXTODO: XML_scheme; //scheme/@val values: "major", "minor", "none"
+ rStyleSheet->endElement( XML_font );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpFont::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nAttr = EXC_FONTATTR_NONE;
+ ::set_flag( nAttr, EXC_FONTATTR_ITALIC, maData.mbItalic );
+ if( maData.mnUnderline > 0 )
+ ::set_flag( nAttr, EXC_FONTATTR_UNDERLINE, true );
+ ::set_flag( nAttr, EXC_FONTATTR_STRIKEOUT, maData.mbStrikeout );
+ ::set_flag( nAttr, EXC_FONTATTR_OUTLINE, maData.mbOutline );
+ ::set_flag( nAttr, EXC_FONTATTR_SHADOW, maData.mbShadow );
+
+ OSL_ENSURE( maData.maName.getLength() < 256, "XclExpFont::WriteBody - font name too long" );
+ XclExpString aFontName;
+ if( GetBiff() <= EXC_BIFF5 )
+ aFontName.AssignByte( maData.maName, GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aFontName.Assign( maData.maName, XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength );
+
+ rStrm << maData.mnHeight
+ << nAttr
+ << GetPalette().GetColorIndex( mnColorId )
+ << maData.mnWeight
+ << maData.mnEscapem
+ << maData.mnUnderline
+ << maData.mnFamily
+ << maData.mnCharSet
+ << sal_uInt8( 0 )
+ << aFontName;
+}
+
+XclExpDxfFont::XclExpDxfFont(const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet):
+ XclExpRoot(rRoot)
+{
+ maDxfData = XclExpFontHelper::GetDxfFontFromItemSet(rRoot, rItemSet);
+}
+
+namespace {
+
+const char* getUnderlineOOXValue(FontLineStyle eUnderline)
+{
+ switch (eUnderline)
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_DONTKNOW:
+ return "none";
+ case LINESTYLE_DOUBLE:
+ case LINESTYLE_DOUBLEWAVE:
+ return "double";
+ default:
+ return "single";
+ }
+}
+
+const char* getFontFamilyOOXValue(FontFamily eValue)
+{
+ switch (eValue)
+ {
+ case FAMILY_DONTKNOW:
+ return "0";
+ case FAMILY_SWISS:
+ case FAMILY_SYSTEM:
+ return "2";
+ case FAMILY_ROMAN:
+ return "1";
+ case FAMILY_SCRIPT:
+ return "4";
+ case FAMILY_MODERN:
+ return "3";
+ case FAMILY_DECORATIVE:
+ return "5";
+ default:
+ return "0";
+ }
+}
+
+}
+
+void XclExpDxfFont::SaveXml(XclExpXmlStream& rStrm)
+{
+ if (maDxfData.isEmpty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_font);
+
+ if (maDxfData.pFontAttr)
+ {
+ OUString aFontName = (*maDxfData.pFontAttr)->GetFamilyName();
+
+ aFontName = XclTools::GetXclFontName(aFontName);
+ if (!aFontName.isEmpty())
+ {
+ rStyleSheet->singleElement(XML_name, XML_val, aFontName);
+ }
+
+ rtl_TextEncoding eTextEnc = (*maDxfData.pFontAttr)->GetCharSet();
+ sal_uInt8 nExcelCharSet = rtl_getBestWindowsCharsetFromTextEncoding(eTextEnc);
+ if (nExcelCharSet)
+ {
+ rStyleSheet->singleElement(XML_charset, XML_val, OString::number(nExcelCharSet));
+ }
+
+ FontFamily eFamily = (*maDxfData.pFontAttr)->GetFamily();
+ const char* pVal = getFontFamilyOOXValue(eFamily);
+ if (pVal)
+ {
+ rStyleSheet->singleElement(XML_family, XML_val, pVal);
+ }
+ }
+
+ if (maDxfData.eWeight)
+ {
+ rStyleSheet->singleElement(XML_b,
+ XML_val, ToPsz10(*maDxfData.eWeight != WEIGHT_NORMAL));
+ }
+
+ if (maDxfData.eItalic)
+ {
+ bool bItalic = (*maDxfData.eItalic == ITALIC_OBLIQUE) || (*maDxfData.eItalic == ITALIC_NORMAL);
+ rStyleSheet->singleElement(XML_i, XML_val, ToPsz10(bItalic));
+ }
+
+ if (maDxfData.eStrike)
+ {
+ bool bStrikeout =
+ (*maDxfData.eStrike == STRIKEOUT_SINGLE) || (*maDxfData.eStrike == STRIKEOUT_DOUBLE) ||
+ (*maDxfData.eStrike == STRIKEOUT_BOLD) || (*maDxfData.eStrike == STRIKEOUT_SLASH) ||
+ (*maDxfData.eStrike == STRIKEOUT_X);
+
+ rStyleSheet->singleElement(XML_strike, XML_val, ToPsz10(bStrikeout));
+ }
+
+ if (maDxfData.bOutline)
+ {
+ rStyleSheet->singleElement(XML_outline, XML_val, ToPsz10(*maDxfData.bOutline));
+ }
+
+ if (maDxfData.bShadow)
+ {
+ rStyleSheet->singleElement(XML_shadow, XML_val, ToPsz10(*maDxfData.bShadow));
+ }
+
+ if (maDxfData.aColor)
+ {
+ rStyleSheet->singleElement(XML_color,
+ XML_rgb, XclXmlUtils::ToOString(*maDxfData.aColor));
+ }
+
+ if (maDxfData.nFontHeight)
+ {
+ rStyleSheet->singleElement(XML_sz,
+ XML_val, OString::number(*maDxfData.nFontHeight/20));
+ }
+
+ if (maDxfData.eUnder)
+ {
+ const char* pVal = getUnderlineOOXValue(*maDxfData.eUnder);
+ rStyleSheet->singleElement(XML_u, XML_val, pVal);
+ }
+
+ rStyleSheet->endElement(XML_font);
+}
+
+XclExpBlindFont::XclExpBlindFont( const XclExpRoot& rRoot ) :
+ XclExpFont( rRoot, XclFontData(), EXC_COLOR_CELLTEXT )
+{
+}
+
+bool XclExpBlindFont::Equals( const XclFontData& /*rFontData*/, sal_uInt32 /*nHash*/ ) const
+{
+ return false;
+}
+
+void XclExpBlindFont::Save( XclExpStream& /*rStrm*/ )
+{
+ // do nothing
+}
+
+XclExpFontBuffer::XclExpFontBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mnXclMaxSize( 0 )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF4: mnXclMaxSize = EXC_FONT_MAXCOUNT4; break;
+ case EXC_BIFF5: mnXclMaxSize = EXC_FONT_MAXCOUNT5; break;
+ case EXC_BIFF8: mnXclMaxSize = EXC_FONT_MAXCOUNT8; break;
+ default: DBG_ERROR_BIFF();
+ }
+ InitDefaultFonts();
+}
+
+const XclExpFont* XclExpFontBuffer::GetFont( sal_uInt16 nXclFont ) const
+{
+ return maFontList.GetRecord( nXclFont );
+}
+
+const XclFontData& XclExpFontBuffer::GetAppFontData() const
+{
+ return maFontList.GetRecord( EXC_FONT_APP )->GetFontData(); // exists always
+}
+
+sal_uInt16 XclExpFontBuffer::Insert(
+ const XclFontData& rFontData, XclExpColorType eColorType, bool bAppFont )
+{
+ if( bAppFont )
+ {
+ XclExpFontRef xFont = new XclExpFont( GetRoot(), rFontData, eColorType );
+ maFontList.ReplaceRecord( xFont, EXC_FONT_APP );
+ // set width of '0' character for column width export
+ SetCharWidth( xFont->GetFontData() );
+ return EXC_FONT_APP;
+ }
+
+ size_t nPos = Find( rFontData );
+ if( nPos == EXC_FONTLIST_NOTFOUND )
+ {
+ // not found in buffer - create new font
+ size_t nSize = maFontList.GetSize();
+ if( nSize < mnXclMaxSize )
+ {
+ // possible to insert
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), rFontData, eColorType ) );
+ nPos = nSize; // old size is last position now
+ }
+ else
+ {
+ // buffer is full - ignore new font, use default font
+ nPos = EXC_FONT_APP;
+ }
+ }
+ return static_cast< sal_uInt16 >( nPos );
+}
+
+sal_uInt16 XclExpFontBuffer::Insert(
+ const SvxFont& rFont, XclExpColorType eColorType )
+{
+ return Insert( XclFontData( rFont ), eColorType );
+}
+
+sal_uInt16 XclExpFontBuffer::Insert( const SfxItemSet& rItemSet,
+ sal_Int16 nScript, XclExpColorType eColorType, bool bAppFont )
+{
+ // #i17050# script type now provided by caller
+ vcl::Font aFont = XclExpFontHelper::GetFontFromItemSet( GetRoot(), rItemSet, nScript );
+ return Insert( XclFontData( aFont ), eColorType, bAppFont );
+}
+
+void XclExpFontBuffer::Save( XclExpStream& rStrm )
+{
+ maFontList.Save( rStrm );
+}
+
+void XclExpFontBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFontList.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fonts, XML_count, OString::number(maFontList.GetSize()));
+
+ maFontList.SaveXml( rStrm );
+
+ rStyleSheet->endElement( XML_fonts );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpFontBuffer::InitDefaultFonts()
+{
+ XclFontData aFontData;
+ aFontData.maName = "Arial";
+ aFontData.SetScFamily( FAMILY_DONTKNOW );
+ aFontData.SetFontEncoding( ScfTools::GetSystemTextEncoding() );
+ aFontData.SetScHeight( 200 ); // 200 twips = 10 pt
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_BOLD );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+ aFontData.SetScPosture( ITALIC_NORMAL );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ aFontData.SetScWeight( WEIGHT_BOLD );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ // the blind font with index 4
+ maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) );
+ // already add the first user defined font (Excel does it too)
+ aFontData.SetScWeight( WEIGHT_NORMAL );
+ aFontData.SetScPosture( ITALIC_NONE );
+ maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
+ }
+ break;
+ case EXC_BIFF8:
+ {
+ XclExpFontRef xFont = new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ maFontList.AppendRecord( xFont );
+ if( GetOutput() == EXC_OUTPUT_BINARY )
+ // the blind font with index 4
+ maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) );
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+size_t XclExpFontBuffer::Find( const XclFontData& rFontData )
+{
+ sal_uInt32 nHash = lclCalcHash( rFontData );
+ for( size_t nPos = 0, nSize = maFontList.GetSize(); nPos < nSize; ++nPos )
+ if( maFontList.GetRecord( nPos )->Equals( rFontData, nHash ) )
+ return nPos;
+ return EXC_FONTLIST_NOTFOUND;
+}
+
+// FORMAT record - number formats =============================================
+
+namespace {
+
+/** Predicate for search algorithm. */
+struct XclExpNumFmtPred
+{
+ sal_uInt32 mnScNumFmt;
+ explicit XclExpNumFmtPred( sal_uInt32 nScNumFmt ) : mnScNumFmt( nScNumFmt ) {}
+ bool operator()( const XclExpNumFmt& rFormat ) const
+ { return rFormat.mnScNumFmt == mnScNumFmt; }
+};
+
+}
+
+void XclExpNumFmt::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->singleElement( XML_numFmt,
+ XML_numFmtId, OString::number(mnXclNumFmt),
+ XML_formatCode, maNumFmtString );
+}
+
+XclExpNumFmtBuffer::XclExpNumFmtBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ mxFormatter( new SvNumberFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ) ),
+ mpKeywordTable( new NfKeywordTable ),
+ mnStdFmt( GetFormatter().GetStandardIndex( ScGlobal::eLnge ) )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: mnXclOffset = EXC_FORMAT_OFFSET5; break;
+ case EXC_BIFF8: mnXclOffset = EXC_FORMAT_OFFSET8; break;
+ default: mnXclOffset = 0; DBG_ERROR_BIFF();
+ }
+
+ mxFormatter->FillKeywordTableForExcel( *mpKeywordTable );
+}
+
+XclExpNumFmtBuffer::~XclExpNumFmtBuffer()
+{
+}
+
+sal_uInt16 XclExpNumFmtBuffer::Insert( sal_uInt32 nScNumFmt )
+{
+ XclExpNumFmtVec::const_iterator aIt =
+ ::std::find_if( maFormatMap.begin(), maFormatMap.end(), XclExpNumFmtPred( nScNumFmt ) );
+ if( aIt != maFormatMap.end() )
+ return aIt->mnXclNumFmt;
+
+ size_t nSize = maFormatMap.size();
+ if( nSize < o3tl::make_unsigned( 0xFFFF - mnXclOffset ) )
+ {
+ sal_uInt16 nXclNumFmt = static_cast< sal_uInt16 >( nSize + mnXclOffset );
+ maFormatMap.emplace_back( nScNumFmt, nXclNumFmt, GetFormatCode( nScNumFmt ) );
+ return nXclNumFmt;
+ }
+
+ return 0;
+}
+
+void XclExpNumFmtBuffer::Save( XclExpStream& rStrm )
+{
+ for( const auto& rEntry : maFormatMap )
+ WriteFormatRecord( rStrm, rEntry );
+}
+
+void XclExpNumFmtBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maFormatMap.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_numFmts, XML_count, OString::number(maFormatMap.size()));
+ for( auto& rEntry : maFormatMap )
+ {
+ rEntry.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_numFmts );
+}
+
+void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, sal_uInt16 nXclNumFmt, const OUString& rFormatStr )
+{
+ XclExpString aExpStr;
+ if( GetBiff() <= EXC_BIFF5 )
+ aExpStr.AssignByte( rFormatStr, GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ aExpStr.Assign( rFormatStr );
+
+ rStrm.StartRecord( EXC_ID4_FORMAT, 2 + aExpStr.GetSize() );
+ rStrm << nXclNumFmt << aExpStr;
+ rStrm.EndRecord();
+}
+
+void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, const XclExpNumFmt& rFormat )
+{
+ WriteFormatRecord( rStrm, rFormat.mnXclNumFmt, GetFormatCode( rFormat.mnScNumFmt ) );
+}
+
+namespace {
+
+OUString GetNumberFormatCode(const XclRoot& rRoot, const sal_uInt32 nScNumFmt, SvNumberFormatter* pFormatter, const NfKeywordTable* pKeywordTable)
+{
+ return rRoot.GetFormatter().GetFormatStringForExcel( nScNumFmt, *pKeywordTable, *pFormatter);
+}
+
+}
+
+OUString XclExpNumFmtBuffer::GetFormatCode( sal_uInt32 nScNumFmt )
+{
+ return GetNumberFormatCode( *this, nScNumFmt, mxFormatter.get(), mpKeywordTable.get() );
+}
+
+// XF, STYLE record - Cell formatting =========================================
+
+bool XclExpCellProt::FillFromItemSet( const SfxItemSet& rItemSet, bool bStyle )
+{
+ const ScProtectionAttr& rProtItem = rItemSet.Get( ATTR_PROTECTION );
+ mbLocked = rProtItem.GetProtection();
+ mbHidden = rProtItem.GetHideFormula() || rProtItem.GetHideCell();
+ return ScfTools::CheckItem( rItemSet, ATTR_PROTECTION, bStyle );
+}
+
+void XclExpCellProt::FillToXF3( sal_uInt16& rnProt ) const
+{
+ ::set_flag( rnProt, EXC_XF_LOCKED, mbLocked );
+ ::set_flag( rnProt, EXC_XF_HIDDEN, mbHidden );
+}
+
+void XclExpCellProt::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ rStrm.GetCurrentStream()->singleElement( XML_protection,
+ XML_locked, ToPsz( mbLocked ),
+ XML_hidden, ToPsz( mbHidden ) );
+}
+
+bool XclExpCellAlign::FillFromItemSet(const XclRoot& rRoot, const SfxItemSet& rItemSet,
+ bool bForceLineBreak, XclBiff eBiff, bool bStyle)
+{
+ bool bUsed = false;
+ SvxCellHorJustify eHorAlign = rItemSet.Get( ATTR_HOR_JUSTIFY ).GetValue();
+ SvxCellVerJustify eVerAlign = rItemSet.Get( ATTR_VER_JUSTIFY ).GetValue();
+
+ switch( eBiff )
+ {
+ case EXC_BIFF8: // attributes new in BIFF8
+ {
+ // text indent
+ tools::Long nTmpIndent = rItemSet.Get( ATTR_INDENT ).GetValue(); // already in twips
+ tools::Long nSpaceWidth = rRoot.GetSpaceWidth();
+ sal_Int32 nIndent = static_cast<double>(nTmpIndent) / (3.0 * nSpaceWidth) + 0.5;
+ mnIndent = limit_cast< sal_uInt8 >( nIndent, 0, 15 );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_INDENT, bStyle );
+
+ // shrink to fit
+ mbShrink = rItemSet.Get( ATTR_SHRINKTOFIT ).GetValue();
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_SHRINKTOFIT, bStyle );
+
+ // CTL text direction
+ SetScFrameDir( rItemSet.Get( ATTR_WRITINGDIR ).GetValue() );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_WRITINGDIR, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF5: // attributes new in BIFF5
+ case EXC_BIFF4: // attributes new in BIFF4
+ {
+ // vertical alignment
+ SetScVerAlign( eVerAlign );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_VER_JUSTIFY, bStyle );
+
+ // stacked/rotation
+ bool bStacked = rItemSet.Get( ATTR_STACKED ).GetValue();
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_STACKED, bStyle );
+ if( bStacked )
+ {
+ mnRotation = EXC_ROT_STACKED;
+ }
+ else
+ {
+ // rotation
+ Degree100 nScRot = rItemSet.Get( ATTR_ROTATE_VALUE ).GetValue();
+ mnRotation = XclTools::GetXclRotation( nScRot );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_ROTATE_VALUE, bStyle );
+ }
+ mnOrient = XclTools::GetXclOrientFromRot( mnRotation );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF3: // attributes new in BIFF3
+ {
+ // text wrap
+ mbLineBreak = bForceLineBreak || rItemSet.Get( ATTR_LINEBREAK ).GetValue();
+ bUsed |= bForceLineBreak || ScfTools::CheckItem( rItemSet, ATTR_LINEBREAK, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF2: // attributes new in BIFF2
+ {
+ // horizontal alignment
+ SetScHorAlign( eHorAlign );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_HOR_JUSTIFY, bStyle );
+ }
+
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ if (eBiff == EXC_BIFF8)
+ {
+ // Adjust for distributed alignments.
+ if (eHorAlign == SvxCellHorJustify::Block)
+ {
+ SvxCellJustifyMethod eHorJustMethod =
+ rItemSet.GetItem<SvxJustifyMethodItem>(ATTR_HOR_JUSTIFY_METHOD)->GetValue();
+ if (eHorJustMethod == SvxCellJustifyMethod::Distribute)
+ mnHorAlign = EXC_XF_HOR_DISTRIB;
+ }
+
+ if (eVerAlign == SvxCellVerJustify::Block)
+ {
+ SvxCellJustifyMethod eVerJustMethod =
+ rItemSet.GetItem<SvxJustifyMethodItem>(ATTR_VER_JUSTIFY_METHOD)->GetValue();
+ if (eVerJustMethod == SvxCellJustifyMethod::Distribute)
+ mnVerAlign = EXC_XF_VER_DISTRIB;
+ }
+ }
+
+ return bUsed;
+}
+
+void XclExpCellAlign::FillToXF5( sal_uInt16& rnAlign ) const
+{
+ ::insert_value( rnAlign, mnHorAlign, 0, 3 );
+ ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak );
+ ::insert_value( rnAlign, mnVerAlign, 4, 3 );
+ ::insert_value( rnAlign, mnOrient, 8, 2 );
+}
+
+void XclExpCellAlign::FillToXF8( sal_uInt16& rnAlign, sal_uInt16& rnMiscAttrib ) const
+{
+ ::insert_value( rnAlign, mnHorAlign, 0, 3 );
+ ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak );
+ ::insert_value( rnAlign, mnVerAlign, 4, 3 );
+ ::insert_value( rnAlign, mnRotation, 8, 8 );
+ ::insert_value( rnMiscAttrib, mnIndent, 0, 4 );
+ ::set_flag( rnMiscAttrib, EXC_XF8_SHRINK, mbShrink );
+ ::insert_value( rnMiscAttrib, mnTextDir, 6, 2 );
+}
+
+static const char* ToHorizontalAlignment( sal_uInt8 nHorAlign )
+{
+ switch( nHorAlign )
+ {
+ case EXC_XF_HOR_GENERAL: return "general";
+ case EXC_XF_HOR_LEFT: return "left";
+ case EXC_XF_HOR_CENTER: return "center";
+ case EXC_XF_HOR_RIGHT: return "right";
+ case EXC_XF_HOR_FILL: return "fill";
+ case EXC_XF_HOR_JUSTIFY: return "justify";
+ case EXC_XF_HOR_CENTER_AS: return "centerContinuous";
+ case EXC_XF_HOR_DISTRIB: return "distributed";
+ }
+ return "*unknown*";
+}
+
+static const char* ToVerticalAlignment( sal_uInt8 nVerAlign )
+{
+ switch( nVerAlign )
+ {
+ case EXC_XF_VER_TOP: return "top";
+ case EXC_XF_VER_CENTER: return "center";
+ case EXC_XF_VER_BOTTOM: return "bottom";
+ case EXC_XF_VER_JUSTIFY: return "justify";
+ case EXC_XF_VER_DISTRIB: return "distributed";
+ }
+ return "*unknown*";
+}
+
+void XclExpCellAlign::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ rStrm.GetCurrentStream()->singleElement( XML_alignment,
+ XML_horizontal, ToHorizontalAlignment( mnHorAlign ),
+ XML_vertical, ToVerticalAlignment( mnVerAlign ),
+ XML_textRotation, OString::number(mnRotation),
+ XML_wrapText, ToPsz( mbLineBreak ),
+ XML_indent, OString::number(mnIndent),
+ // OOXTODO: XML_relativeIndent, mnIndent?
+ // OOXTODO: XML_justifyLastLine,
+ XML_shrinkToFit, ToPsz( mbShrink ),
+ XML_readingOrder, sax_fastparser::UseIf(OString::number(mnTextDir), mnTextDir != EXC_XF_TEXTDIR_CONTEXT) );
+}
+
+namespace {
+
+void lclGetBorderLine(
+ sal_uInt8& rnXclLine, sal_uInt32& rnColorId,
+ const ::editeng::SvxBorderLine* pLine, XclExpPalette& rPalette, XclBiff eBiff )
+{
+ // Document: sc/qa/unit/data/README.cellborders
+
+ enum CalcLineIndex{Idx_None, Idx_Solid, Idx_Dotted, Idx_Dashed, Idx_FineDashed, Idx_DashDot, Idx_DashDotDot, Idx_DoubleThin, Idx_Last};
+ enum ExcelWidthIndex{Width_Hair, Width_Thin, Width_Medium, Width_Thick, Width_Last};
+ static sal_uInt8 Map_LineLO_toMS[Idx_Last][Width_Last] =
+ {
+ // 0,05 - 0,74 0,75 - 1,49 1,50 - 2,49 2,50 - 9,00 Width Range [pt]
+ // EXC_BORDER_HAIR EXC_BORDER_THIN EXC_BORDER_MEDIUM EXC_BORDER_THICK MS Width
+ {EXC_LINE_NONE , EXC_LINE_NONE , EXC_LINE_NONE , EXC_LINE_NONE }, // 0 BorderLineStyle::NONE
+ {EXC_LINE_HAIR , EXC_LINE_THIN , EXC_LINE_MEDIUM , EXC_LINE_THICK }, // 1 BorderLineStyle::SOLID
+ {EXC_LINE_DOTTED , EXC_LINE_DOTTED , EXC_LINE_MEDIUM_SLANT_DASHDOT, EXC_LINE_MEDIUM_SLANT_DASHDOT}, // 2 BorderLineStyle::DOTTED
+ {EXC_LINE_DOTTED , EXC_LINE_DASHED , EXC_LINE_MEDIUM_DASHED , EXC_LINE_MEDIUM_DASHED }, // 3 BorderLineStyle::DASHED
+ {EXC_LINE_DASHED , EXC_LINE_DASHED , EXC_LINE_MEDIUM_SLANT_DASHDOT, EXC_LINE_MEDIUM_SLANT_DASHDOT}, // 4 BorderLineStyle::FINE_DASHED
+ {EXC_LINE_DASHED , EXC_LINE_THIN_DASHDOT , EXC_LINE_MEDIUM_DASHDOT , EXC_LINE_MEDIUM_DASHDOT }, // 5 BorderLineStyle::DASH_DOT
+ {EXC_LINE_DASHED , EXC_LINE_THIN_DASHDOTDOT , EXC_LINE_MEDIUM_DASHDOTDOT , EXC_LINE_MEDIUM_DASHDOTDOT }, // 6 BorderLineStyle::DASH_DOT_DOT
+ {EXC_LINE_DOUBLE , EXC_LINE_DOUBLE , EXC_LINE_DOUBLE , EXC_LINE_DOUBLE } // 7 BorderLineStyle::DOUBLE_THIN
+ }; // Line Name
+
+ rnXclLine = EXC_LINE_NONE;
+ if( pLine )
+ {
+ sal_uInt16 nOuterWidth = pLine->GetOutWidth();
+ ExcelWidthIndex nOuterWidthIndx;
+ CalcLineIndex nStyleIndex;
+
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::NONE:
+ nStyleIndex = Idx_None;
+ break;
+ case SvxBorderLineStyle::SOLID:
+ nStyleIndex = Idx_Solid;
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ nStyleIndex = Idx_Dotted;
+ break;
+ case SvxBorderLineStyle::DASHED:
+ nStyleIndex = Idx_Dashed;
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ nStyleIndex = Idx_FineDashed;
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ nStyleIndex = Idx_DashDot;
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ nStyleIndex = Idx_DashDotDot;
+ break;
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ // the "nOuterWidth" is not right for this line type
+ // but at the moment width it not important for that
+ // the right function is nOuterWidth = (sal_uInt16) pLine->GetWidth();
+ nStyleIndex = Idx_DoubleThin;
+ break;
+ default:
+ nStyleIndex = Idx_Solid;
+ }
+
+ if( nOuterWidth >= EXC_BORDER_THICK )
+ nOuterWidthIndx = Width_Thick;
+ else if( nOuterWidth >= EXC_BORDER_MEDIUM )
+ nOuterWidthIndx = Width_Medium;
+ else if( nOuterWidth >= EXC_BORDER_THIN )
+ nOuterWidthIndx = Width_Thin;
+ else if ( nOuterWidth >= EXC_BORDER_HAIR )
+ nOuterWidthIndx = Width_Hair;
+ else
+ nOuterWidthIndx = Width_Thin;
+
+ rnXclLine = Map_LineLO_toMS[nStyleIndex][nOuterWidthIndx];
+ }
+
+ if( (eBiff == EXC_BIFF2) && (rnXclLine != EXC_LINE_NONE) )
+ rnXclLine = EXC_LINE_THIN;
+
+ rnColorId = (pLine && (rnXclLine != EXC_LINE_NONE)) ?
+ rPalette.InsertColor( pLine->GetColor(), EXC_COLOR_CELLBORDER ) :
+ XclExpPalette::GetColorIdFromIndex( 0 );
+}
+
+} // namespace
+
+XclExpCellBorder::XclExpCellBorder() :
+ mnLeftColorId( XclExpPalette::GetColorIdFromIndex( mnLeftColor ) ),
+ mnRightColorId( XclExpPalette::GetColorIdFromIndex( mnRightColor ) ),
+ mnTopColorId( XclExpPalette::GetColorIdFromIndex( mnTopColor ) ),
+ mnBottomColorId( XclExpPalette::GetColorIdFromIndex( mnBottomColor ) ),
+ mnDiagColorId( XclExpPalette::GetColorIdFromIndex( mnDiagColor ) )
+{
+}
+
+bool XclExpCellBorder::FillFromItemSet(
+ const SfxItemSet& rItemSet, XclExpPalette& rPalette, XclBiff eBiff, bool bStyle )
+{
+ bool bUsed = false;
+
+ switch( eBiff )
+ {
+ case EXC_BIFF8: // attributes new in BIFF8
+ {
+ const SvxLineItem& rTLBRItem = rItemSet.Get( ATTR_BORDER_TLBR );
+ sal_uInt8 nTLBRLine;
+ sal_uInt32 nTLBRColorId;
+ lclGetBorderLine( nTLBRLine, nTLBRColorId, rTLBRItem.GetLine(), rPalette, eBiff );
+ mbDiagTLtoBR = (nTLBRLine != EXC_LINE_NONE);
+
+ const SvxLineItem& rBLTRItem = rItemSet.Get( ATTR_BORDER_BLTR );
+ sal_uInt8 nBLTRLine;
+ sal_uInt32 nBLTRColorId;
+ lclGetBorderLine( nBLTRLine, nBLTRColorId, rBLTRItem.GetLine(), rPalette, eBiff );
+ mbDiagBLtoTR = (nBLTRLine != EXC_LINE_NONE);
+
+ if( ::ScHasPriority( rTLBRItem.GetLine(), rBLTRItem.GetLine() ) )
+ {
+ mnDiagLine = nTLBRLine;
+ mnDiagColorId = nTLBRColorId;
+ }
+ else
+ {
+ mnDiagLine = nBLTRLine;
+ mnDiagColorId = nBLTRColorId;
+ }
+
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER_TLBR, bStyle ) ||
+ ScfTools::CheckItem( rItemSet, ATTR_BORDER_BLTR, bStyle );
+
+ [[fallthrough]];
+ }
+
+ case EXC_BIFF5:
+ case EXC_BIFF4:
+ case EXC_BIFF3:
+ case EXC_BIFF2:
+ {
+ const SvxBoxItem& rBoxItem = rItemSet.Get( ATTR_BORDER );
+ lclGetBorderLine( mnLeftLine, mnLeftColorId, rBoxItem.GetLeft(), rPalette, eBiff );
+ lclGetBorderLine( mnRightLine, mnRightColorId, rBoxItem.GetRight(), rPalette, eBiff );
+ lclGetBorderLine( mnTopLine, mnTopColorId, rBoxItem.GetTop(), rPalette, eBiff );
+ lclGetBorderLine( mnBottomLine, mnBottomColorId, rBoxItem.GetBottom(), rPalette, eBiff );
+ bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER, bStyle );
+ }
+
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+
+ return bUsed;
+}
+
+void XclExpCellBorder::SetFinalColors( const XclExpPalette& rPalette )
+{
+ mnLeftColor = rPalette.GetColorIndex( mnLeftColorId );
+ mnRightColor = rPalette.GetColorIndex( mnRightColorId );
+ mnTopColor = rPalette.GetColorIndex( mnTopColorId );
+ mnBottomColor = rPalette.GetColorIndex( mnBottomColorId );
+ mnDiagColor = rPalette.GetColorIndex( mnDiagColorId );
+}
+
+void XclExpCellBorder::FillToXF5( sal_uInt32& rnBorder, sal_uInt32& rnArea ) const
+{
+ ::insert_value( rnBorder, mnTopLine, 0, 3 );
+ ::insert_value( rnBorder, mnLeftLine, 3, 3 );
+ ::insert_value( rnArea, mnBottomLine, 22, 3 );
+ ::insert_value( rnBorder, mnRightLine, 6, 3 );
+ ::insert_value( rnBorder, mnTopColor, 9, 7 );
+ ::insert_value( rnBorder, mnLeftColor, 16, 7 );
+ ::insert_value( rnArea, mnBottomColor, 25, 7 );
+ ::insert_value( rnBorder, mnRightColor, 23, 7 );
+}
+
+void XclExpCellBorder::FillToXF8( sal_uInt32& rnBorder1, sal_uInt32& rnBorder2 ) const
+{
+ ::insert_value( rnBorder1, mnLeftLine, 0, 4 );
+ ::insert_value( rnBorder1, mnRightLine, 4, 4 );
+ ::insert_value( rnBorder1, mnTopLine, 8, 4 );
+ ::insert_value( rnBorder1, mnBottomLine, 12, 4 );
+ ::insert_value( rnBorder1, mnLeftColor, 16, 7 );
+ ::insert_value( rnBorder1, mnRightColor, 23, 7 );
+ ::insert_value( rnBorder2, mnTopColor, 0, 7 );
+ ::insert_value( rnBorder2, mnBottomColor, 7, 7 );
+ ::insert_value( rnBorder2, mnDiagColor, 14, 7 );
+ ::insert_value( rnBorder2, mnDiagLine, 21, 4 );
+ ::set_flag( rnBorder1, EXC_XF_DIAGONAL_TL_TO_BR, mbDiagTLtoBR );
+ ::set_flag( rnBorder1, EXC_XF_DIAGONAL_BL_TO_TR, mbDiagBLtoTR );
+}
+
+void XclExpCellBorder::FillToCF8( sal_uInt16& rnLine, sal_uInt32& rnColor ) const
+{
+ ::insert_value( rnLine, mnLeftLine, 0, 4 );
+ ::insert_value( rnLine, mnRightLine, 4, 4 );
+ ::insert_value( rnLine, mnTopLine, 8, 4 );
+ ::insert_value( rnLine, mnBottomLine, 12, 4 );
+ ::insert_value( rnColor, mnLeftColor, 0, 7 );
+ ::insert_value( rnColor, mnRightColor, 7, 7 );
+ ::insert_value( rnColor, mnTopColor, 16, 7 );
+ ::insert_value( rnColor, mnBottomColor, 23, 7 );
+}
+
+static const char* ToLineStyle( sal_uInt8 nLineStyle )
+{
+ switch( nLineStyle )
+ {
+ case EXC_LINE_NONE: return "none";
+ case EXC_LINE_THIN: return "thin";
+ case EXC_LINE_MEDIUM: return "medium";
+ case EXC_LINE_THICK: return "thick";
+ case EXC_LINE_DOUBLE: return "double";
+ case EXC_LINE_HAIR: return "hair";
+ case EXC_LINE_DOTTED: return "dotted";
+ case EXC_LINE_DASHED: return "dashed";
+ case EXC_LINE_MEDIUM_DASHED: return "mediumDashed";
+ case EXC_LINE_THIN_DASHDOT: return "dashDot";
+ case EXC_LINE_THIN_DASHDOTDOT: return "dashDotDot";
+ case EXC_LINE_MEDIUM_DASHDOT: return "mediumDashDot";
+ case EXC_LINE_MEDIUM_DASHDOTDOT: return "mediumDashDotDot";
+ case EXC_LINE_MEDIUM_SLANT_DASHDOT: return "slantDashDot";
+ }
+ return "*unknown*";
+}
+
+static void lcl_WriteBorder( XclExpXmlStream& rStrm, sal_Int32 nElement, sal_uInt8 nLineStyle, const Color& rColor )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ if( nLineStyle == EXC_LINE_NONE )
+ rStyleSheet->singleElement(nElement);
+ else if( rColor == Color( 0, 0, 0 ) )
+ rStyleSheet->singleElement(nElement, XML_style, ToLineStyle(nLineStyle));
+ else
+ {
+ rStyleSheet->startElement(nElement, XML_style, ToLineStyle(nLineStyle));
+ rStyleSheet->singleElement(XML_color, XML_rgb, XclXmlUtils::ToOString(rColor));
+ rStyleSheet->endElement( nElement );
+ }
+}
+
+void XclExpCellBorder::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+
+ XclExpPalette& rPalette = rStrm.GetRoot().GetPalette();
+
+ rStyleSheet->startElement( XML_border,
+ XML_diagonalUp, ToPsz( mbDiagBLtoTR ),
+ XML_diagonalDown, ToPsz( mbDiagTLtoBR )
+ // OOXTODO: XML_outline
+ );
+ lcl_WriteBorder( rStrm, XML_left, mnLeftLine, rPalette.GetColor( mnLeftColor ) );
+ lcl_WriteBorder( rStrm, XML_right, mnRightLine, rPalette.GetColor( mnRightColor ) );
+ lcl_WriteBorder( rStrm, XML_top, mnTopLine, rPalette.GetColor( mnTopColor ) );
+ lcl_WriteBorder( rStrm, XML_bottom, mnBottomLine, rPalette.GetColor( mnBottomColor ) );
+ lcl_WriteBorder( rStrm, XML_diagonal, mnDiagLine, rPalette.GetColor( mnDiagColor ) );
+ // OOXTODO: XML_vertical, XML_horizontal
+ rStyleSheet->endElement( XML_border );
+}
+
+XclExpCellArea::XclExpCellArea() :
+ mnForeColorId( XclExpPalette::GetColorIdFromIndex( mnForeColor ) ),
+ mnBackColorId( XclExpPalette::GetColorIdFromIndex( mnBackColor ) ),
+ maForeColor(0),
+ maBackColor(0)
+{
+}
+
+XclExpCellArea::XclExpCellArea(Color aForeColor, Color aBackColor)
+ : XclCellArea(EXC_PATT_SOLID)
+ , mnForeColorId(0)
+ , mnBackColorId(0)
+ , maForeColor(aForeColor)
+ , maBackColor(aBackColor)
+{
+}
+
+bool XclExpCellArea::FillFromItemSet( const SfxItemSet& rItemSet, XclExpPalette& rPalette, bool bStyle )
+{
+ const SvxBrushItem& rBrushItem = rItemSet.Get( ATTR_BACKGROUND );
+ if( rBrushItem.GetColor().IsTransparent() )
+ {
+ mnPattern = EXC_PATT_NONE;
+ mnForeColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT );
+ mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWBACK );
+ }
+ else
+ {
+ mnPattern = EXC_PATT_SOLID;
+ mnForeColorId = rPalette.InsertColor( rBrushItem.GetColor(), EXC_COLOR_CELLAREA );
+ mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT );
+ }
+ return ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, bStyle );
+}
+
+void XclExpCellArea::SetFinalColors( const XclExpPalette& rPalette )
+{
+ rPalette.GetMixedColors( mnForeColor, mnBackColor, mnPattern, mnForeColorId, mnBackColorId );
+}
+
+void XclExpCellArea::FillToXF5( sal_uInt32& rnArea ) const
+{
+ ::insert_value( rnArea, mnPattern, 16, 6 );
+ ::insert_value( rnArea, mnForeColor, 0, 7 );
+ ::insert_value( rnArea, mnBackColor, 7, 7 );
+}
+
+void XclExpCellArea::FillToXF8( sal_uInt32& rnBorder2, sal_uInt16& rnArea ) const
+{
+ ::insert_value( rnBorder2, mnPattern, 26, 6 );
+ ::insert_value( rnArea, mnForeColor, 0, 7 );
+ ::insert_value( rnArea, mnBackColor, 7, 7 );
+}
+
+void XclExpCellArea::FillToCF8( sal_uInt16& rnPattern, sal_uInt16& rnColor ) const
+{
+ XclCellArea aTmp( *this );
+ if( !aTmp.IsTransparent() && (aTmp.mnBackColor == EXC_COLOR_WINDOWTEXT) )
+ aTmp.mnBackColor = 0;
+ if( aTmp.mnPattern == EXC_PATT_SOLID )
+ ::std::swap( aTmp.mnForeColor, aTmp.mnBackColor );
+ ::insert_value( rnColor, aTmp.mnForeColor, 0, 7 );
+ ::insert_value( rnColor, aTmp.mnBackColor, 7, 7 );
+ ::insert_value( rnPattern, aTmp.mnPattern, 10, 6 );
+}
+
+static const char* ToPatternType( sal_uInt8 nPattern )
+{
+ switch( nPattern )
+ {
+ case EXC_PATT_NONE: return "none";
+ case EXC_PATT_SOLID: return "solid";
+ case EXC_PATT_50_PERC: return "mediumGray";
+ case EXC_PATT_75_PERC: return "darkGray";
+ case EXC_PATT_25_PERC: return "lightGray";
+ case EXC_PATT_12_5_PERC: return "gray125";
+ case EXC_PATT_6_25_PERC: return "gray0625";
+ }
+ return "*unknown*";
+}
+
+void XclExpCellArea::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fill);
+
+ // OOXTODO: XML_gradientFill
+
+ XclExpPalette& rPalette = rStrm.GetRoot().GetPalette();
+
+ if (mnPattern == EXC_PATT_NONE
+ || (mnForeColor == 0 && mnBackColor == 0 && maForeColor == 0 && maBackColor == 0))
+ {
+ rStyleSheet->singleElement(XML_patternFill, XML_patternType, ToPatternType(mnPattern));
+ }
+ else
+ {
+ rStyleSheet->startElement(XML_patternFill, XML_patternType, ToPatternType(mnPattern));
+ if (maForeColor != 0 || maBackColor != 0)
+ {
+ if (maForeColor != 0)
+ {
+ rStyleSheet->singleElement(XML_fgColor, XML_rgb,
+ XclXmlUtils::ToOString(maForeColor));
+ }
+
+ if (maBackColor != 0)
+ {
+ rStyleSheet->singleElement(XML_bgColor, XML_rgb,
+ XclXmlUtils::ToOString(maBackColor));
+ }
+ }
+ else
+ {
+ if (mnForeColor != 0)
+ {
+ rStyleSheet->singleElement(XML_fgColor, XML_rgb,
+ XclXmlUtils::ToOString(rPalette.GetColor(mnForeColor)));
+ }
+ if (mnBackColor != 0)
+ {
+ rStyleSheet->singleElement(XML_bgColor, XML_rgb,
+ XclXmlUtils::ToOString(rPalette.GetColor(mnBackColor)));
+ }
+ }
+
+ rStyleSheet->endElement( XML_patternFill );
+ }
+
+ rStyleSheet->endElement( XML_fill );
+}
+
+bool XclExpColor::FillFromItemSet( const SfxItemSet& rItemSet )
+{
+ if( !ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true ) )
+ return false;
+
+ const SvxBrushItem& rBrushItem = rItemSet.Get( ATTR_BACKGROUND );
+ maColor = rBrushItem.GetColor();
+
+ return true;
+}
+
+void XclExpColor::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_fill);
+ rStyleSheet->startElement(XML_patternFill);
+ rStyleSheet->singleElement(XML_bgColor, XML_rgb, XclXmlUtils::ToOString(maColor));
+
+ rStyleSheet->endElement( XML_patternFill );
+ rStyleSheet->endElement( XML_fill );
+}
+
+XclExpXFId::XclExpXFId() :
+ mnXFId( XclExpXFBuffer::GetDefCellXFId() ),
+ mnXFIndex( EXC_XF_DEFAULTCELL )
+{
+}
+
+void XclExpXFId::ConvertXFIndex( const XclExpRoot& rRoot )
+{
+ mnXFIndex = rRoot.GetXFBuffer().GetXFIndex( mnXFId );
+}
+
+XclExpXF::XclExpXF(
+ const XclExpRoot& rRoot, const ScPatternAttr& rPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) :
+ XclXFBase( true ),
+ XclExpRoot( rRoot )
+{
+ mnParentXFId = GetXFBuffer().InsertStyle( rPattern.GetStyleSheet() );
+ Init( rPattern.GetItemSet(), nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak, false );
+}
+
+XclExpXF::XclExpXF( const XclExpRoot& rRoot, const SfxStyleSheetBase& rStyleSheet ) :
+ XclXFBase( false ),
+ XclExpRoot( rRoot ),
+ mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) )
+{
+ bool bDefStyle = (rStyleSheet.GetName() == ScResId( STR_STYLENAME_STANDARD ));
+ sal_Int16 nScript = bDefStyle ? GetDefApiScript() : css::i18n::ScriptType::WEAK;
+ Init( const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet(), nScript,
+ NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false, bDefStyle );
+}
+
+XclExpXF::XclExpXF( const XclExpRoot& rRoot, bool bCellXF ) :
+ XclXFBase( bCellXF ),
+ XclExpRoot( rRoot ),
+ mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) )
+{
+ InitDefault();
+}
+
+bool XclExpXF::Equals( const ScPatternAttr& rPattern,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
+{
+ return IsCellXF() && (mpItemSet == &rPattern.GetItemSet()) &&
+ (!bForceLineBreak || maAlignment.mbLineBreak) &&
+ ((nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) || (mnScNumFmt == nForceScNumFmt)) &&
+ ((nForceXclFont == EXC_FONT_NOTFOUND) || (mnXclFont == nForceXclFont));
+}
+
+bool XclExpXF::Equals( const SfxStyleSheetBase& rStyleSheet ) const
+{
+ return IsStyleXF() && (mpItemSet == &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet());
+}
+
+void XclExpXF::SetFinalColors()
+{
+ maBorder.SetFinalColors( GetPalette() );
+ maArea.SetFinalColors( GetPalette() );
+}
+
+bool XclExpXF::Equals( const XclExpXF& rCmpXF ) const
+{
+ return XclXFBase::Equals( rCmpXF ) &&
+ (maProtection == rCmpXF.maProtection) && (maAlignment == rCmpXF.maAlignment) &&
+ (maBorder == rCmpXF.maBorder) && (maArea == rCmpXF.maArea) &&
+ (mnXclFont == rCmpXF.mnXclFont) && (mnXclNumFmt == rCmpXF.mnXclNumFmt) &&
+ (mnParentXFId == rCmpXF.mnParentXFId);
+}
+
+void XclExpXF::InitDefault()
+{
+ SetRecHeader( EXC_ID5_XF, (GetBiff() == EXC_BIFF8) ? 20 : 16 );
+ mpItemSet = nullptr;
+ mnScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ mnXclFont = mnXclNumFmt = 0;
+ SetXmlIds(0, 0);
+}
+
+void XclExpXF::Init( const SfxItemSet& rItemSet, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak, bool bDefStyle )
+{
+ InitDefault();
+ mpItemSet = &rItemSet;
+
+ // cell protection
+ mbProtUsed = maProtection.FillFromItemSet( rItemSet, IsStyleXF() );
+
+ // font
+ if( nForceXclFont == EXC_FONT_NOTFOUND )
+ {
+ mnXclFont = GetFontBuffer().Insert( rItemSet, nScript, EXC_COLOR_CELLTEXT, bDefStyle );
+ mbFontUsed = XclExpFontHelper::CheckItems( GetRoot(), rItemSet, nScript, IsStyleXF() );
+ }
+ else
+ {
+ mnXclFont = nForceXclFont;
+ mbFontUsed = true;
+ }
+
+ // number format
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ mnXclNumFmt = nForceScNumFmt;
+ else
+ {
+ // Built-in formats of dedicated languages may be attributed using the
+ // system language (or even other?) format with a language attribute,
+ // obtain the "real" format key.
+ mnScNumFmt = rItemSet.Get( ATTR_VALUE_FORMAT ).GetValue();
+ LanguageType nLang = rItemSet.Get( ATTR_LANGUAGE_FORMAT).GetLanguage();
+ if (mnScNumFmt >= SV_COUNTRY_LANGUAGE_OFFSET || nLang != LANGUAGE_SYSTEM)
+ mnScNumFmt = GetFormatter().GetFormatForLanguageIfBuiltIn( mnScNumFmt, nLang);
+ }
+ mnXclNumFmt = GetNumFmtBuffer().Insert( mnScNumFmt );
+ mbFmtUsed = ScfTools::CheckItem( rItemSet, ATTR_VALUE_FORMAT, IsStyleXF() );
+
+ // alignment
+ mbAlignUsed = maAlignment.FillFromItemSet(*this, rItemSet, bForceLineBreak, GetBiff(), IsStyleXF());
+
+ // cell border
+ mbBorderUsed = maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff(), IsStyleXF() );
+
+ // background area
+ mbAreaUsed = maArea.FillFromItemSet( rItemSet, GetPalette(), IsStyleXF() );
+
+ // set all b***Used flags to true in "Default"/"Normal" style
+ if( bDefStyle )
+ SetAllUsedFlags( true );
+}
+
+sal_uInt8 XclExpXF::GetUsedFlags() const
+{
+ sal_uInt8 nUsedFlags = 0;
+ /* In cell XFs a set bit means a used attribute, in style XFs a cleared bit.
+ "mbCellXF == mb***Used" evaluates to correct value in cell and style XFs. */
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_PROT, mbCellXF == mbProtUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_FONT, mbCellXF == mbFontUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_VALFMT, mbCellXF == mbFmtUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_ALIGN, mbCellXF == mbAlignUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_BORDER, mbCellXF == mbBorderUsed );
+ ::set_flag( nUsedFlags, EXC_XF_DIFF_AREA, mbCellXF == mbAreaUsed );
+ return nUsedFlags;
+}
+
+void XclExpXF::WriteBody5( XclExpStream& rStrm )
+{
+ sal_uInt16 nTypeProt = 0, nAlign = 0;
+ sal_uInt32 nArea = 0, nBorder = 0;
+
+ ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() );
+ ::insert_value( nTypeProt, mnParent, 4, 12 );
+ ::insert_value( nAlign, GetUsedFlags(), 10, 6 );
+
+ maProtection.FillToXF3( nTypeProt );
+ maAlignment.FillToXF5( nAlign );
+ maBorder.FillToXF5( nBorder, nArea );
+ maArea.FillToXF5( nArea );
+
+ rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nArea << nBorder;
+}
+
+void XclExpXF::WriteBody8( XclExpStream& rStrm )
+{
+ sal_uInt16 nTypeProt = 0, nAlign = 0, nMiscAttrib = 0, nArea = 0;
+ sal_uInt32 nBorder1 = 0, nBorder2 = 0;
+
+ ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() );
+ ::insert_value( nTypeProt, mnParent, 4, 12 );
+ ::insert_value( nMiscAttrib, GetUsedFlags(), 10, 6 );
+
+ maProtection.FillToXF3( nTypeProt );
+ maAlignment.FillToXF8( nAlign, nMiscAttrib );
+ maBorder.FillToXF8( nBorder1, nBorder2 );
+ maArea.FillToXF8( nBorder2, nArea );
+
+ rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nMiscAttrib << nBorder1 << nBorder2 << nArea;
+}
+
+void XclExpXF::WriteBody( XclExpStream& rStrm )
+{
+ XclExpXFId aParentId( mnParentXFId );
+ aParentId.ConvertXFIndex( GetRoot() );
+ mnParent = aParentId.mnXFIndex;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5: WriteBody5( rStrm ); break;
+ case EXC_BIFF8: WriteBody8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpXF::SetXmlIds( sal_uInt32 nBorderId, sal_uInt32 nFillId )
+{
+ mnBorderId = nBorderId;
+ mnFillId = nFillId;
+}
+
+void XclExpXF::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+
+ sal_Int32 nXfId = 0;
+ const XclExpXF* pStyleXF = nullptr;
+ if( IsCellXF() )
+ {
+ sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( mnParentXFId );
+ nXfId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFIndex );
+ pStyleXF = rStrm.GetRoot().GetXFBuffer().GetXFById( mnParentXFId );
+ }
+
+ rStyleSheet->startElement( XML_xf,
+ XML_numFmtId, OString::number(mnXclNumFmt),
+ XML_fontId, OString::number(mnXclFont),
+ XML_fillId, OString::number(mnFillId),
+ XML_borderId, OString::number(mnBorderId),
+ XML_xfId, sax_fastparser::UseIf(OString::number(nXfId), !IsStyleXF()),
+ // OOXTODO: XML_quotePrefix,
+ // OOXTODO: XML_pivotButton,
+ // OOXTODO: XML_applyNumberFormat, ;
+ XML_applyFont, ToPsz( mbFontUsed ),
+ // OOXTODO: XML_applyFill,
+ XML_applyBorder, ToPsz( mbBorderUsed ),
+ XML_applyAlignment, ToPsz( mbAlignUsed ),
+ XML_applyProtection, ToPsz( mbProtUsed ) );
+ if( mbAlignUsed )
+ maAlignment.SaveXml( rStrm );
+ else if ( pStyleXF )
+ pStyleXF->GetAlignmentData().SaveXml( rStrm );
+ if( mbProtUsed )
+ maProtection.SaveXml( rStrm );
+ else if ( pStyleXF )
+ pStyleXF->GetProtectionData().SaveXml( rStrm );
+
+ // OOXTODO: XML_extLst
+ rStyleSheet->endElement( XML_xf );
+}
+
+XclExpDefaultXF::XclExpDefaultXF( const XclExpRoot& rRoot, bool bCellXF ) :
+ XclExpXF( rRoot, bCellXF )
+{
+}
+
+void XclExpDefaultXF::SetFont( sal_uInt16 nXclFont )
+{
+ mnXclFont = nXclFont;
+ mbFontUsed = true;
+}
+
+void XclExpDefaultXF::SetNumFmt( sal_uInt16 nXclNumFmt )
+{
+ mnXclNumFmt = nXclNumFmt;
+ mbFmtUsed = true;
+}
+
+XclExpStyle::XclExpStyle( sal_uInt32 nXFId, const OUString& rStyleName ) :
+ XclExpRecord( EXC_ID_STYLE, 4 ),
+ maName( rStyleName ),
+ maXFId( nXFId ),
+ mnStyleId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL )
+{
+ OSL_ENSURE( !maName.isEmpty(), "XclExpStyle::XclExpStyle - empty style name" );
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt8 nStyleId, nLevel; // do not use members for debug tests
+ OSL_ENSURE( !XclTools::GetBuiltInStyleId( nStyleId, nLevel, maName ),
+ "XclExpStyle::XclExpStyle - this is a built-in style" );
+#endif
+}
+
+XclExpStyle::XclExpStyle( sal_uInt32 nXFId, sal_uInt8 nStyleId, sal_uInt8 nLevel ) :
+ XclExpRecord( EXC_ID_STYLE, 4 ),
+ maXFId( nXFId ),
+ mnStyleId( nStyleId ),
+ mnLevel( nLevel )
+{
+}
+
+void XclExpStyle::WriteBody( XclExpStream& rStrm )
+{
+ maXFId.ConvertXFIndex( rStrm.GetRoot() );
+ ::set_flag( maXFId.mnXFIndex, EXC_STYLE_BUILTIN, IsBuiltIn() );
+ rStrm << maXFId.mnXFIndex;
+
+ if( IsBuiltIn() )
+ {
+ rStrm << mnStyleId << mnLevel;
+ }
+ else
+ {
+ XclExpString aNameEx;
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ aNameEx.Assign( maName );
+ else
+ aNameEx.AssignByte( maName, rStrm.GetRoot().GetTextEncoding(), XclStrFlags::EightBitLength );
+ rStrm << aNameEx;
+ }
+}
+
+static const char* lcl_StyleNameFromId( sal_Int32 nStyleId )
+{
+ switch( nStyleId )
+ {
+ case 0: return "Normal";
+ case 3: return "Comma";
+ case 4: return "Currency";
+ case 5: return "Percent";
+ case 6: return "Comma [0]";
+ case 7: return "Currency [0]";
+ }
+ return "*unknown*";
+}
+
+void XclExpStyle::SaveXml( XclExpXmlStream& rStrm )
+{
+ constexpr sal_Int32 CELL_STYLE_MAX_BUILTIN_ID = 54;
+ OString sName;
+ OString sBuiltinId;
+ const char* pBuiltinId = nullptr;
+ if( IsBuiltIn() )
+ {
+ sName = OString( lcl_StyleNameFromId( mnStyleId ) );
+ sBuiltinId = OString::number( std::min( static_cast<sal_Int32>( CELL_STYLE_MAX_BUILTIN_ID - 1 ), static_cast <sal_Int32>( mnStyleId ) ) );
+ pBuiltinId = sBuiltinId.getStr();
+ }
+ else
+ sName = maName.toUtf8();
+
+ // get the index in sortedlist associated with the mnXId
+ sal_Int32 nXFId = rStrm.GetRoot().GetXFBuffer().GetXFIndex( maXFId.mnXFId );
+ // get the style index associated with index into sortedlist
+ nXFId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFId );
+ rStrm.GetCurrentStream()->singleElement( XML_cellStyle,
+ XML_name, sName,
+ XML_xfId, OString::number(nXFId),
+// builtinId of 54 or above is invalid according to OpenXML SDK validator.
+ XML_builtinId, pBuiltinId
+ // OOXTODO: XML_iLevel,
+ // OOXTODO: XML_hidden,
+ // XML_customBuiltin, ToPsz( ! IsBuiltIn() )
+ );
+ // OOXTODO: XML_extLst
+}
+
+namespace {
+
+const sal_uInt32 EXC_XFLIST_INDEXBASE = 0xFFFE0000;
+/** Maximum count of XF records to store in the XF list (performance). */
+const sal_uInt32 EXC_XFLIST_HARDLIMIT = 256 * 1024;
+
+bool lclIsBuiltInStyle( const OUString& rStyleName )
+{
+ return
+ XclTools::IsBuiltInStyleName( rStyleName ) ||
+ XclTools::IsCondFormatStyleName( rStyleName );
+}
+
+} // namespace
+
+XclExpXFBuffer::XclExpBuiltInInfo::XclExpBuiltInInfo() :
+ mnStyleId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL ),
+ mbPredefined( true ),
+ mbHasStyleRec( false )
+{
+}
+
+namespace {
+
+/** Predicate for search algorithm. */
+struct XclExpBorderPred
+{
+ const XclExpCellBorder&
+ mrBorder;
+ explicit XclExpBorderPred( const XclExpCellBorder& rBorder ) : mrBorder( rBorder ) {}
+ bool operator()( const XclExpCellBorder& rBorder ) const;
+};
+
+}
+
+bool XclExpBorderPred::operator()( const XclExpCellBorder& rBorder ) const
+{
+ return
+ mrBorder.mnLeftColor == rBorder.mnLeftColor &&
+ mrBorder.mnRightColor == rBorder.mnRightColor &&
+ mrBorder.mnTopColor == rBorder.mnTopColor &&
+ mrBorder.mnBottomColor == rBorder.mnBottomColor &&
+ mrBorder.mnDiagColor == rBorder.mnDiagColor &&
+ mrBorder.mnLeftLine == rBorder.mnLeftLine &&
+ mrBorder.mnRightLine == rBorder.mnRightLine &&
+ mrBorder.mnTopLine == rBorder.mnTopLine &&
+ mrBorder.mnBottomLine == rBorder.mnBottomLine &&
+ mrBorder.mnDiagLine == rBorder.mnDiagLine &&
+ mrBorder.mbDiagTLtoBR == rBorder.mbDiagTLtoBR &&
+ mrBorder.mbDiagBLtoTR == rBorder.mbDiagBLtoTR &&
+ mrBorder.mnLeftColorId == rBorder.mnLeftColorId &&
+ mrBorder.mnRightColorId == rBorder.mnRightColorId &&
+ mrBorder.mnTopColorId == rBorder.mnTopColorId &&
+ mrBorder.mnBottomColorId == rBorder.mnBottomColorId &&
+ mrBorder.mnDiagColorId == rBorder.mnDiagColorId;
+}
+
+namespace {
+
+struct XclExpFillPred
+{
+ const XclExpCellArea&
+ mrFill;
+ explicit XclExpFillPred( const XclExpCellArea& rFill ) : mrFill( rFill ) {}
+ bool operator()( const XclExpCellArea& rFill ) const;
+};
+
+}
+
+bool XclExpFillPred::operator()( const XclExpCellArea& rFill ) const
+{
+ return
+ mrFill.mnForeColor == rFill.mnForeColor &&
+ mrFill.mnBackColor == rFill.mnBackColor &&
+ mrFill.mnPattern == rFill.mnPattern &&
+ mrFill.mnForeColorId == rFill.mnForeColorId &&
+ mrFill.mnBackColorId == rFill.mnBackColorId;
+}
+
+XclExpXFBuffer::XclExpXFBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+void XclExpXFBuffer::Initialize()
+{
+ InsertDefaultRecords();
+ InsertUserStyles();
+}
+
+sal_uInt32 XclExpXFBuffer::Insert( const ScPatternAttr* pPattern, sal_Int16 nScript )
+{
+ return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertWithFont( const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt16 nForceXclFont, bool bForceLineBreak )
+{
+ return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, nForceXclFont, bForceLineBreak );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertWithNumFmt( const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForceScNumFmt, bool bForceLineBreak )
+{
+ return InsertCellXF( pPattern, nScript, nForceScNumFmt, EXC_FONT_NOTFOUND, bForceLineBreak );
+}
+
+sal_uInt32 XclExpXFBuffer::InsertStyle( const SfxStyleSheetBase* pStyleSheet )
+{
+ return pStyleSheet ? InsertStyleXF( *pStyleSheet ) : GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE );
+}
+
+sal_uInt32 XclExpXFBuffer::GetXFIdFromIndex( sal_uInt16 nXFIndex )
+{
+ return EXC_XFLIST_INDEXBASE | nXFIndex;
+}
+
+sal_uInt32 XclExpXFBuffer::GetDefCellXFId()
+{
+ return GetXFIdFromIndex( EXC_XF_DEFAULTCELL );
+}
+
+const XclExpXF* XclExpXFBuffer::GetXFById( sal_uInt32 nXFId ) const
+{
+ return maXFList.GetRecord( nXFId );
+}
+
+void XclExpXFBuffer::Finalize()
+{
+ for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos )
+ maXFList.GetRecord( nPos )->SetFinalColors();
+
+ sal_uInt32 nTotalCount = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ sal_uInt32 nId;
+ maXFIndexVec.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+ maStyleIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+ maCellIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL );
+
+ XclExpBuiltInMap::const_iterator aBuiltInEnd = maBuiltInMap.end();
+ /* nMaxBuiltInXFId used to decide faster whether an XF record is
+ user-defined. If the current XF ID is greater than this value,
+ maBuiltInMap doesn't need to be searched. */
+ sal_uInt32 nMaxBuiltInXFId = maBuiltInMap.empty() ? 0 : maBuiltInMap.rbegin()->first;
+
+ // *** map all built-in XF records (cell and style) *** -------------------
+
+ // do not change XF order -> std::map<> iterates elements in ascending order
+ for( const auto& rEntry : maBuiltInMap )
+ AppendXFIndex( rEntry.first );
+
+ // *** insert all user-defined style XF records, without reduce *** -------
+
+ sal_uInt32 nStyleXFCount = 0; // counts up to EXC_XF_MAXSTYLECOUNT limit
+
+ for( nId = 0; nId < nTotalCount; ++nId )
+ {
+ XclExpXFRef xXF = maXFList.GetRecord( nId );
+ if( xXF->IsStyleXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) )
+ {
+ if( nStyleXFCount < EXC_XF_MAXSTYLECOUNT )
+ {
+ // maximum count of styles not reached
+ AppendXFIndex( nId );
+ ++nStyleXFCount;
+ }
+ else
+ {
+ /* Maximum count of styles reached - do not append more
+ pointers to XFs; use default style XF instead; do not break
+ the loop to initialize all maXFIndexVec elements. */
+ maXFIndexVec[ nId ] = EXC_XF_DEFAULTSTYLE;
+ }
+ }
+ }
+
+ // *** insert all cell XF records *** -------------------------------------
+
+ // start position to search for equal inserted XF records
+ size_t nSearchStart = maSortedXFList.GetSize();
+
+ // break the loop if XF limit reached - maXFIndexVec is already initialized with default index
+ XclExpXFRef xDefCellXF = maXFList.GetRecord( EXC_XF_DEFAULTCELL );
+ for( nId = 0; (nId < nTotalCount) && (maSortedXFList.GetSize() < EXC_XF_MAXCOUNT); ++nId )
+ {
+ XclExpXFRef xXF = maXFList.GetRecord( nId );
+ if( xXF->IsCellXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) )
+ {
+ // try to find an XF record equal to *xXF, which is already inserted
+ sal_uInt16 nFoundIndex = EXC_XF_NOTFOUND;
+
+ // first try if it is equal to the default cell XF
+ if( xDefCellXF->Equals( *xXF ) )
+ {
+ nFoundIndex = EXC_XF_DEFAULTCELL;
+ }
+ else for( size_t nSearchPos = nSearchStart, nSearchEnd = maSortedXFList.GetSize();
+ (nSearchPos < nSearchEnd) && (nFoundIndex == EXC_XF_NOTFOUND); ++nSearchPos )
+ {
+ if( maSortedXFList.GetRecord( nSearchPos )->Equals( *xXF ) )
+ nFoundIndex = static_cast< sal_uInt16 >( nSearchPos );
+ }
+
+ if( nFoundIndex != EXC_XF_NOTFOUND )
+ // equal XF already in the list, use its resulting XF index
+ maXFIndexVec[ nId ] = nFoundIndex;
+ else
+ AppendXFIndex( nId );
+ }
+ }
+
+ sal_uInt16 nXmlStyleIndex = 0;
+ sal_uInt16 nXmlCellIndex = 0;
+
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( xXF->IsStyleXF() )
+ maStyleIndexes[ i ] = nXmlStyleIndex++;
+ else
+ maCellIndexes[ i ] = nXmlCellIndex++;
+ }
+}
+
+sal_uInt16 XclExpXFBuffer::GetXFIndex( sal_uInt32 nXFId ) const
+{
+ sal_uInt16 nXFIndex = EXC_XF_DEFAULTSTYLE;
+ if( nXFId >= EXC_XFLIST_INDEXBASE )
+ nXFIndex = static_cast< sal_uInt16 >( nXFId & ~EXC_XFLIST_INDEXBASE );
+ else if( nXFId < maXFIndexVec.size() )
+ nXFIndex = maXFIndexVec[ nXFId ];
+ return nXFIndex;
+}
+
+sal_Int32 XclExpXFBuffer::GetXmlStyleIndex( sal_uInt32 nXFIndex ) const
+{
+ OSL_ENSURE( nXFIndex < maStyleIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" );
+ if( nXFIndex >= maStyleIndexes.size() )
+ return 0; // should be caught/debugged via above assert; return "valid" index.
+ return maStyleIndexes[ nXFIndex ];
+}
+
+sal_Int32 XclExpXFBuffer::GetXmlCellIndex( sal_uInt32 nXFIndex ) const
+{
+ OSL_ENSURE( nXFIndex < maCellIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" );
+ if( nXFIndex >= maCellIndexes.size() )
+ return 0; // should be caught/debugged via above assert; return "valid" index.
+ return maCellIndexes[ nXFIndex ];
+}
+
+void XclExpXFBuffer::Save( XclExpStream& rStrm )
+{
+ // save all XF records contained in the maSortedXFList vector (sorted by XF index)
+ maSortedXFList.Save( rStrm );
+ // save all STYLE records
+ maStyleList.Save( rStrm );
+}
+
+static void lcl_GetCellCounts( const XclExpRecordList< XclExpXF >& rXFList, sal_Int32& rCells, sal_Int32& rStyles )
+{
+ rCells = 0;
+ rStyles = 0;
+ size_t nXFCount = rXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpRecordList< XclExpXF >::RecordRefType xXF = rXFList.GetRecord( i );
+ if( xXF->IsCellXF() )
+ ++rCells;
+ else if( xXF->IsStyleXF() )
+ ++rStyles;
+ }
+}
+
+void XclExpXFBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+
+ rStyleSheet->startElement(XML_fills, XML_count, OString::number(maFills.size()));
+ for( const auto& rFill : maFills )
+ {
+ rFill.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_fills );
+
+ rStyleSheet->startElement(XML_borders, XML_count, OString::number(maBorders.size()));
+ for( const auto& rBorder : maBorders )
+ {
+ rBorder.SaveXml( rStrm );
+ }
+ rStyleSheet->endElement( XML_borders );
+
+ // save all XF records contained in the maSortedXFList vector (sorted by XF index)
+ sal_Int32 nCells, nStyles;
+ lcl_GetCellCounts( maSortedXFList, nCells, nStyles );
+
+ if( nStyles > 0 )
+ {
+ rStyleSheet->startElement(XML_cellStyleXfs, XML_count, OString::number(nStyles));
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( ! xXF->IsStyleXF() )
+ continue;
+ SaveXFXml( rStrm, *xXF );
+ }
+ rStyleSheet->endElement( XML_cellStyleXfs );
+ }
+
+ if( nCells > 0 )
+ {
+ rStyleSheet->startElement(XML_cellXfs, XML_count, OString::number(nCells));
+ size_t nXFCount = maSortedXFList.GetSize();
+ for( size_t i = 0; i < nXFCount; ++i )
+ {
+ XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
+ if( ! xXF->IsCellXF() )
+ continue;
+ SaveXFXml( rStrm, *xXF );
+ }
+ rStyleSheet->endElement( XML_cellXfs );
+ }
+
+ // save all STYLE records
+ rStyleSheet->startElement(XML_cellStyles, XML_count, OString::number(maStyleList.GetSize()));
+ maStyleList.SaveXml( rStrm );
+ rStyleSheet->endElement( XML_cellStyles );
+}
+
+void XclExpXFBuffer::SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF )
+{
+ XclExpBorderList::iterator aBorderPos =
+ std::find_if( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) );
+ OSL_ENSURE( aBorderPos != maBorders.end(), "XclExpXFBuffer::SaveXml - Invalid @borderId!" );
+ XclExpFillList::iterator aFillPos =
+ std::find_if( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) );
+ OSL_ENSURE( aFillPos != maFills.end(), "XclExpXFBuffer::SaveXml - Invalid @fillId!" );
+
+ sal_Int32 nBorderId = 0, nFillId = 0;
+ if( aBorderPos != maBorders.end() )
+ nBorderId = std::distance( maBorders.begin(), aBorderPos );
+ if( aFillPos != maFills.end() )
+ nFillId = std::distance( maFills.begin(), aFillPos );
+
+ rXF.SetXmlIds( nBorderId, nFillId );
+ rXF.SaveXml( rStrm );
+}
+
+sal_uInt32 XclExpXFBuffer::FindXF( const ScPatternAttr& rPattern,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
+{
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND && nForceXclFont == EXC_FONT_NOTFOUND)
+ {
+ FindKey key1 { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, 0 };
+ FindKey key2 { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ if (it1 != maXFFindMap.end())
+ {
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ }
+ else if (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND || nForceXclFont == EXC_FONT_NOTFOUND)
+ {
+ FindKey key1 { /*mbCellXF*/true, &rPattern.GetItemSet(), 0, 0 };
+ FindKey key2 { /*mbCellXF*/true, &rPattern.GetItemSet(), NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ if (it1 != maXFFindMap.end())
+ {
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ }
+ else
+ {
+ FindKey key { /*mbCellXF*/true, &rPattern.GetItemSet(), nForceScNumFmt, nForceXclFont };
+ auto it = maXFFindMap.find(key);
+ if (it == maXFFindMap.end())
+ return EXC_XFID_NOTFOUND;
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
+ return nPos;
+ }
+ return EXC_XFID_NOTFOUND;
+}
+
+sal_uInt32 XclExpXFBuffer::FindXF( const SfxStyleSheetBase& rStyleSheet ) const
+{
+ const SfxItemSet* pItemSet = &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet();
+ FindKey key1 { /*mbCellXF*/false, pItemSet, 0, 0 };
+ FindKey key2 { /*mbCellXF*/false, pItemSet, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND };
+ auto it1 = maXFFindMap.lower_bound(key1);
+ auto it2 = maXFFindMap.upper_bound(key2);
+ for (auto it = it1; it != it2; ++it)
+ for (auto const & nPos : it->second)
+ if( maXFList.GetRecord( nPos )->Equals( rStyleSheet ) )
+ return nPos;
+ return EXC_XFID_NOTFOUND;
+}
+
+sal_uInt32 XclExpXFBuffer::FindBuiltInXF( sal_uInt8 nStyleId, sal_uInt8 nLevel ) const
+{
+ auto aIt = std::find_if(maBuiltInMap.begin(), maBuiltInMap.end(),
+ [&nStyleId, nLevel](const XclExpBuiltInMap::value_type& rEntry) {
+ return (rEntry.second.mnStyleId == nStyleId) && (rEntry.second.mnLevel == nLevel);
+ });
+ if (aIt != maBuiltInMap.end())
+ return aIt->first;
+ return EXC_XFID_NOTFOUND;
+}
+
+XclExpXFBuffer::FindKey XclExpXFBuffer::ToFindKey(XclExpXF const & rRec)
+{
+ return { rRec.IsCellXF(), rRec.GetItemSet(), rRec.GetScNumFmt(), rRec.GetXclFont() };
+}
+
+sal_uInt32 XclExpXFBuffer::InsertCellXF( const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak )
+{
+ const ScPatternAttr* pDefPattern = GetDoc().GetDefPattern();
+ if( !pPattern )
+ pPattern = pDefPattern;
+
+ // special handling for default cell formatting
+ if( (pPattern == pDefPattern) && !bForceLineBreak &&
+ (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) &&
+ (nForceXclFont == EXC_FONT_NOTFOUND) )
+ {
+ // Is it the first try to insert the default cell format?
+ bool& rbPredefined = maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined;
+ if( rbPredefined )
+ {
+ // remove old entry in find-map
+ auto & rPositions = maXFFindMap[ToFindKey(*maXFList.GetRecord(EXC_XF_DEFAULTCELL))];
+ auto it = std::find(rPositions.begin(), rPositions.end(), EXC_XF_DEFAULTCELL);
+ rPositions.erase(it);
+ // replace default cell pattern
+ XclExpXFRef xNewXF = new XclExpXF( GetRoot(), *pPattern, nScript );
+ maXFList.ReplaceRecord( xNewXF, EXC_XF_DEFAULTCELL );
+ // and add new entry in find-map
+ maXFFindMap[ToFindKey(*xNewXF)].push_back(EXC_XF_DEFAULTCELL);
+ rbPredefined = false;
+ }
+ return GetDefCellXFId();
+ }
+
+ sal_uInt32 nXFId = FindXF( *pPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // not found - insert new cell XF
+ if( maXFList.GetSize() < EXC_XFLIST_HARDLIMIT )
+ {
+ auto pNewExp = new XclExpXF(
+ GetRoot(), *pPattern, nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak );
+ maXFList.AppendNewRecord( pNewExp );
+ // do not set nXFId before the AppendNewRecord() call - it may insert 2 XFs (style+cell)
+ nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() - 1 );
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ }
+ else
+ {
+ // list full - fall back to default cell XF
+ nXFId = GetDefCellXFId();
+ }
+ }
+ return nXFId;
+}
+
+sal_uInt32 XclExpXFBuffer::InsertStyleXF( const SfxStyleSheetBase& rStyleSheet )
+{
+ // *** try, if it is a built-in style - create new XF or replace existing predefined XF ***
+
+ sal_uInt8 nStyleId, nLevel;
+ if( XclTools::GetBuiltInStyleId( nStyleId, nLevel, rStyleSheet.GetName() ) )
+ {
+ // try to find the built-in XF record (if already created in InsertDefaultRecords())
+ sal_uInt32 nXFId = FindBuiltInXF( nStyleId, nLevel );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // built-in style XF not yet created - do it now
+ XclExpXFRef xXF = new XclExpXF( GetRoot(), rStyleSheet );
+ nXFId = AppendBuiltInXFWithStyle( xXF, nStyleId, nLevel );
+ // this new XF record is not predefined
+ maBuiltInMap[ nXFId ].mbPredefined = false;
+ }
+ else
+ {
+ OSL_ENSURE( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::InsertStyleXF - built-in XF not found" );
+ // XF record still predefined? -> Replace with real XF
+ bool& rbPredefined = maBuiltInMap[ nXFId ].mbPredefined;
+ if( rbPredefined )
+ {
+ // remove old entry in find-map
+ auto & rPositions = maXFFindMap[ToFindKey(*maXFList.GetRecord(nXFId))];
+ auto it = std::find(rPositions.begin(), rPositions.end(), nXFId);
+ rPositions.erase(it);
+ // replace predefined built-in style (ReplaceRecord() deletes old record)
+ XclExpXFRef pNewExp = new XclExpXF( GetRoot(), rStyleSheet );
+ maXFList.ReplaceRecord( pNewExp, nXFId );
+ // and add new entry in find-map
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ rbPredefined = false;
+ }
+ }
+
+ // STYLE already inserted? (may be not, i.e. for RowLevel/ColLevel or Hyperlink styles)
+ bool& rbHasStyleRec = maBuiltInMap[ nXFId ].mbHasStyleRec;
+ if( !rbHasStyleRec )
+ {
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) );
+ rbHasStyleRec = true;
+ }
+
+ return nXFId;
+ }
+
+ // *** try to find the XF record of a user-defined style ***
+
+ sal_uInt32 nXFId = FindXF( rStyleSheet );
+ if( nXFId == EXC_XFID_NOTFOUND )
+ {
+ // not found - insert new style XF and STYLE
+ nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ if( nXFId < EXC_XFLIST_HARDLIMIT )
+ {
+ auto pNewExp = new XclExpXF( GetRoot(), rStyleSheet );
+ maXFList.AppendNewRecord( pNewExp );
+ // create the STYLE record
+ if( !rStyleSheet.GetName().isEmpty() )
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, rStyleSheet.GetName() ) );
+ maXFFindMap[ToFindKey(*pNewExp)].push_back(nXFId);
+ }
+ else
+ // list full - fall back to default style XF
+ nXFId = GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE );
+ }
+ return nXFId;
+}
+
+void XclExpXFBuffer::InsertUserStyles()
+{
+ SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SfxStyleFamily::Para );
+ for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() )
+ if( pStyleSheet->IsUserDefined() && !lclIsBuiltInStyle( pStyleSheet->GetName() ) )
+ InsertStyleXF( *pStyleSheet );
+}
+
+sal_uInt32 XclExpXFBuffer::AppendBuiltInXF( XclExpXFRef const & xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel )
+{
+ sal_uInt32 nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
+ maXFList.AppendRecord( xXF );
+ maXFFindMap[ToFindKey(*xXF)].push_back(nXFId);
+ XclExpBuiltInInfo& rInfo = maBuiltInMap[ nXFId ];
+ rInfo.mnStyleId = nStyleId;
+ rInfo.mnLevel = nLevel;
+ rInfo.mbPredefined = true;
+ return nXFId;
+}
+
+sal_uInt32 XclExpXFBuffer::AppendBuiltInXFWithStyle( XclExpXFRef const & xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel )
+{
+ sal_uInt32 nXFId = AppendBuiltInXF( xXF, nStyleId, nLevel );
+ maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) );
+ maBuiltInMap[ nXFId ].mbHasStyleRec = true; // mark existing STYLE record
+ return nXFId;
+}
+
+static XclExpCellArea lcl_GetPatternFill_None()
+{
+ XclExpCellArea aFill;
+ aFill.mnPattern = EXC_PATT_NONE;
+ return aFill;
+}
+
+static XclExpCellArea lcl_GetPatternFill_Gray125()
+{
+ XclExpCellArea aFill;
+ aFill.mnPattern = EXC_PATT_12_5_PERC;
+ aFill.mnForeColor = 0;
+ aFill.mnBackColor = 0;
+ return aFill;
+}
+
+void XclExpXFBuffer::InsertDefaultRecords()
+{
+ maFills.push_back( lcl_GetPatternFill_None() );
+ maFills.push_back( lcl_GetPatternFill_Gray125() );
+
+ // index 0: default style
+ if( SfxStyleSheetBase* pDefStyleSheet = GetStyleSheetPool().Find( ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) )
+ {
+ XclExpXFRef xDefStyle = new XclExpXF( GetRoot(), *pDefStyleSheet );
+ sal_uInt32 nXFId = AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL );
+ // mark this XF as not predefined, prevents overwriting
+ maBuiltInMap[ nXFId ].mbPredefined = false;
+ }
+ else
+ {
+ OSL_FAIL( "XclExpXFBuffer::InsertDefaultRecords - default style not found" );
+ XclExpXFRef xDefStyle = new XclExpDefaultXF( GetRoot(), false );
+ xDefStyle->SetAllUsedFlags( true );
+ AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL );
+ }
+
+ // index 1-14: RowLevel and ColLevel styles (without STYLE records)
+ XclExpDefaultXF aLevelStyle( GetRoot(), false );
+ // RowLevel_1, ColLevel_1
+ aLevelStyle.SetFont( 1 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, 0 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, 0 );
+ // RowLevel_2, ColLevel_2
+ aLevelStyle.SetFont( 2 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, 1 );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, 1 );
+ // RowLevel_3, ColLevel_3 ... RowLevel_7, ColLevel_7
+ aLevelStyle.SetFont( 0 );
+ for( sal_uInt8 nLevel = 2; nLevel < EXC_STYLE_LEVELCOUNT; ++nLevel )
+ {
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_ROWLEVEL, nLevel );
+ AppendBuiltInXF( new XclExpDefaultXF( aLevelStyle ), EXC_STYLE_COLLEVEL, nLevel );
+ }
+
+ // index 15: default hard cell format, placeholder to be able to add more built-in styles
+ maXFList.AppendNewRecord( new XclExpDefaultXF( GetRoot(), true ) );
+ maXFFindMap[ToFindKey(*maXFList.GetRecord(maXFList.GetSize()-1))].push_back(maXFList.GetSize()-1);
+ maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined = true;
+
+ // index 16-20: other built-in styles
+ XclExpDefaultXF aFormatStyle( GetRoot(), false );
+ aFormatStyle.SetFont( 1 );
+ aFormatStyle.SetNumFmt( 43 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_COMMA );
+ aFormatStyle.SetNumFmt( 41 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_COMMA_0 );
+ aFormatStyle.SetNumFmt( 44 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_CURRENCY );
+ aFormatStyle.SetNumFmt( 42 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_CURRENCY_0 );
+ aFormatStyle.SetNumFmt( 9 );
+ AppendBuiltInXFWithStyle( new XclExpDefaultXF( aFormatStyle ), EXC_STYLE_PERCENT );
+
+ // other built-in style XF records (i.e. Hyperlink styles) are created on demand
+
+ /* Insert the real default hard cell format -> 0 is document default pattern.
+ Do it here (and not already above) to really have all built-in styles. */
+ Insert( nullptr, GetDefApiScript() );
+}
+
+void XclExpXFBuffer::AppendXFIndex( sal_uInt32 nXFId )
+{
+ OSL_ENSURE( nXFId < maXFIndexVec.size(), "XclExpXFBuffer::AppendXFIndex - XF ID out of range" );
+ maXFIndexVec[ nXFId ] = static_cast< sal_uInt16 >( maSortedXFList.GetSize() );
+ XclExpXFRef xXF = maXFList.GetRecord( nXFId );
+ AddBorderAndFill( *xXF );
+ maSortedXFList.AppendRecord( xXF );
+ OSL_ENSURE( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::AppendXFIndex - XF not found" );
+}
+
+void XclExpXFBuffer::AddBorderAndFill( const XclExpXF& rXF )
+{
+ if( std::none_of( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) ) )
+ {
+ maBorders.push_back( rXF.GetBorderData() );
+ }
+
+ if( std::none_of( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) ) )
+ {
+ maFills.push_back( rXF.GetAreaData() );
+ }
+}
+
+XclExpDxfs::XclExpDxfs( const XclExpRoot& rRoot )
+ : XclExpRoot( rRoot ),
+ mpKeywordTable( new NfKeywordTable )
+{
+ // Special number formatter for conversion.
+ SvNumberFormatterPtr xFormatter(new SvNumberFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ));
+ xFormatter->FillKeywordTableForExcel( *mpKeywordTable );
+
+ SCTAB nTables = rRoot.GetDoc().GetTableCount();
+ sal_Int32 nDxfId = 0;
+ for(SCTAB nTab = 0; nTab < nTables; ++nTab)
+ {
+ // Color filters
+ std::vector<ScDBData*> pDBData = rRoot.GetDoc().GetDBCollection()->GetAllDBsFromTab(nTab);
+ for (auto& pData : pDBData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+ for (auto nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); nCol++)
+ {
+ ScFilterEntries aFilterEntries;
+ rRoot.GetDoc().GetFilterEntriesArea(nCol, aRange.aStart.Row(),
+ aRange.aEnd.Row(), nTab, true, aFilterEntries);
+
+ // Excel has all filter values stored as foreground colors
+ // Does not matter it is text color or cell background color
+ for (auto& rColor : aFilterEntries.getBackgroundColors())
+ {
+ if (!maColorToDxfId.emplace(rColor, nDxfId).second)
+ continue;
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(rColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(rRoot, std::move(pExpCellArea)));
+ nDxfId++;
+ }
+ for (auto& rColor : aFilterEntries.getTextColors())
+ {
+ if (!maColorToDxfId.emplace(rColor, nDxfId).second)
+ continue;
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(rColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(rRoot, std::move(pExpCellArea)));
+ nDxfId++;
+ }
+ }
+ }
+
+ // Conditional formatting
+ ScConditionalFormatList* pList = rRoot.GetDoc().GetCondFormList(nTab);
+ if (pList)
+ {
+ for (const auto& rxItem : *pList)
+ {
+ size_t nEntryCount = rxItem->size();
+ for (size_t nFormatEntry = 0; nFormatEntry < nEntryCount; ++nFormatEntry)
+ {
+ const ScFormatEntry* pFormatEntry = rxItem->GetEntry(nFormatEntry);
+ if (!pFormatEntry
+ || (pFormatEntry->GetType() != ScFormatEntry::Type::Condition
+ && pFormatEntry->GetType() != ScFormatEntry::Type::Date
+ && pFormatEntry->GetType() != ScFormatEntry::Type::ExtCondition))
+ continue;
+
+ OUString aStyleName;
+ if (pFormatEntry->GetType() == ScFormatEntry::Type::Condition
+ || pFormatEntry->GetType() == ScFormatEntry::Type::ExtCondition)
+ {
+ const ScCondFormatEntry* pEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+ aStyleName= pEntry->GetStyle();
+ }
+ else
+ {
+ const ScCondDateFormatEntry* pEntry = static_cast<const ScCondDateFormatEntry*>(pFormatEntry);
+ aStyleName = pEntry->GetStyleName();
+ }
+
+ if (maStyleNameToDxfId.emplace(aStyleName, nDxfId).second)
+ {
+ SfxStyleSheetBase* pStyle = rRoot.GetDoc().GetStyleSheetPool()->Find(aStyleName, SfxStyleFamily::Para);
+ if(!pStyle)
+ continue;
+
+ SfxItemSet& rSet = pStyle->GetItemSet();
+
+ std::unique_ptr<XclExpCellBorder> pBorder(new XclExpCellBorder);
+ if (!pBorder->FillFromItemSet( rSet, GetPalette(), GetBiff()) )
+ {
+ pBorder.reset();
+ }
+
+ std::unique_ptr<XclExpCellAlign> pAlign(new XclExpCellAlign);
+ if (!pAlign->FillFromItemSet(rRoot, rSet, false, GetBiff()))
+ {
+ pAlign.reset();
+ }
+
+ std::unique_ptr<XclExpCellProt> pCellProt(new XclExpCellProt);
+ if (!pCellProt->FillFromItemSet( rSet ))
+ {
+ pCellProt.reset();
+ }
+
+ std::unique_ptr<XclExpColor> pColor(new XclExpColor);
+ if(!pColor->FillFromItemSet( rSet ))
+ {
+ pColor.reset();
+ }
+
+ std::unique_ptr<XclExpDxfFont> pFont(new XclExpDxfFont(rRoot, rSet));
+
+ std::unique_ptr<XclExpNumFmt> pNumFormat;
+ if( const SfxUInt32Item *pPoolItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT ) )
+ {
+ sal_uInt32 nScNumFmt = pPoolItem->GetValue();
+ sal_Int32 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
+ pNumFormat.reset(new XclExpNumFmt( nScNumFmt, nXclNumFmt, GetNumberFormatCode( *this, nScNumFmt, xFormatter.get(), mpKeywordTable.get() )));
+ }
+
+ maDxf.push_back(std::make_unique<XclExpDxf>( rRoot, std::move(pAlign), std::move(pBorder),
+ std::move(pFont), std::move(pNumFormat), std::move(pCellProt), std::move(pColor) ));
+ ++nDxfId;
+ }
+
+ }
+ }
+ }
+ }
+}
+
+sal_Int32 XclExpDxfs::GetDxfId( const OUString& rStyleName ) const
+{
+ std::map<OUString, sal_Int32>::const_iterator itr = maStyleNameToDxfId.find(rStyleName);
+ if(itr!= maStyleNameToDxfId.end())
+ return itr->second;
+ return -1;
+}
+
+sal_Int32 XclExpDxfs::GetDxfByColor(Color aColor) const
+{
+ std::map<Color, sal_Int32>::const_iterator itr = maColorToDxfId.find(aColor);
+ if (itr != maColorToDxfId.end())
+ return itr->second;
+ return -1;
+}
+
+void XclExpDxfs::AddColor(Color aColor)
+{
+ maColorToDxfId.emplace(aColor, maDxf.size());
+
+ std::unique_ptr<XclExpCellArea> pExpCellArea(new XclExpCellArea(aColor, 0));
+ maDxf.push_back(std::make_unique<XclExpDxf>(GetRoot(), std::move(pExpCellArea)));
+}
+
+void XclExpDxfs::SaveXml( XclExpXmlStream& rStrm )
+{
+ if(maDxf.empty())
+ return;
+
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_dxfs, XML_count, OString::number(maDxf.size()));
+
+ for ( auto& rxDxf : maDxf )
+ {
+ rxDxf->SaveXml( rStrm );
+ }
+
+ rStyleSheet->endElement( XML_dxfs );
+}
+
+XclExpDxf::XclExpDxf( const XclExpRoot& rRoot, std::unique_ptr<XclExpCellAlign> pAlign, std::unique_ptr<XclExpCellBorder> pBorder,
+ std::unique_ptr<XclExpDxfFont> pFont, std::unique_ptr<XclExpNumFmt> pNumberFmt, std::unique_ptr<XclExpCellProt> pProt,
+ std::unique_ptr<XclExpColor> pColor)
+ : XclExpRoot( rRoot ),
+ mpAlign(std::move(pAlign)),
+ mpBorder(std::move(pBorder)),
+ mpFont(std::move(pFont)),
+ mpNumberFmt(std::move(pNumberFmt)),
+ mpProt(std::move(pProt)),
+ mpColor(std::move(pColor))
+{
+}
+
+XclExpDxf::XclExpDxf(const XclExpRoot& rRoot, std::unique_ptr<XclExpCellArea> pCellArea)
+ : XclExpRoot(rRoot)
+ , mpCellArea(std::move(pCellArea))
+{
+}
+
+XclExpDxf::~XclExpDxf()
+{
+}
+
+void XclExpDxf::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElement(XML_dxf);
+
+ if (mpFont)
+ mpFont->SaveXml(rStrm);
+ if (mpNumberFmt)
+ mpNumberFmt->SaveXml(rStrm);
+ if (mpColor)
+ mpColor->SaveXml(rStrm);
+ if (mpAlign)
+ mpAlign->SaveXml(rStrm);
+ if (mpBorder)
+ mpBorder->SaveXml(rStrm);
+ if (mpProt)
+ mpProt->SaveXml(rStrm);
+ if (mpCellArea)
+ mpCellArea->SaveXml(rStrm);
+ rStyleSheet->endElement( XML_dxf );
+}
+
+void XclExpDxf::SaveXmlExt( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
+ rStyleSheet->startElementNS( XML_x14, XML_dxf );
+
+ if (mpFont)
+ mpFont->SaveXml(rStrm);
+ if (mpNumberFmt)
+ mpNumberFmt->SaveXml(rStrm);
+ if (mpColor)
+ mpColor->SaveXml(rStrm);
+ if (mpAlign)
+ mpAlign->SaveXml(rStrm);
+ if (mpBorder)
+ mpBorder->SaveXml(rStrm);
+ if (mpProt)
+ mpProt->SaveXml(rStrm);
+ rStyleSheet->endElementNS( XML_x14, XML_dxf );
+}
+
+
+XclExpXmlStyleSheet::XclExpXmlStyleSheet( const XclExpRoot& rRoot )
+ : XclExpRoot( rRoot )
+{
+}
+
+void XclExpXmlStyleSheet::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr aStyleSheet = rStrm.CreateOutputStream(
+ "xl/styles.xml",
+ u"styles.xml",
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
+ oox::getRelationship(Relationship::STYLES));
+ rStrm.PushStream( aStyleSheet );
+
+ aStyleSheet->startElement(XML_styleSheet, XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)));
+
+ CreateRecord( EXC_ID_FORMATLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_FONTLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_XFLIST )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_DXFS )->SaveXml( rStrm );
+ CreateRecord( EXC_ID_PALETTE )->SaveXml( rStrm );
+
+ aStyleSheet->endElement( XML_styleSheet );
+
+ rStrm.PopStream();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xetable.cxx b/sc/source/filter/excel/xetable.cxx
new file mode 100644
index 000000000..728ea2339
--- /dev/null
+++ b/sc/source/filter/excel/xetable.cxx
@@ -0,0 +1,2841 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xetable.hxx>
+
+#include <map>
+#include <numeric>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <scitems.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <tools/UnitConversion.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <dociter.hxx>
+#include <olinetab.hxx>
+#include <formulacell.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <xehelper.hxx>
+#include <xecontent.hxx>
+#include <xeescher.hxx>
+#include <xeextlst.hxx>
+#include <xeformula.hxx>
+#include <xlcontent.hxx>
+#include <xltools.hxx>
+#include <tokenarray.hxx>
+#include <formula/errorcodes.hxx>
+#include <comphelper/threadpool.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+
+using namespace ::oox;
+
+namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+// Helper records for cell records
+
+XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const OUString& rResult ) :
+ XclExpRecord( EXC_ID3_STRING ),
+ mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
+{
+ OSL_ENSURE( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
+ "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
+ SetRecSize( mxResult->GetSize() );
+}
+
+void XclExpStringRec::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << *mxResult;
+}
+
+// Additional records for special formula ranges ==============================
+
+XclExpRangeFmlaBase::XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
+ XclExpRecord( nRecId, nRecSize ),
+ maXclRange( ScAddress::UNINITIALIZED ),
+ maBaseXclPos( ScAddress::UNINITIALIZED )
+{
+ maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
+ maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
+}
+
+XclExpRangeFmlaBase::XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
+ XclExpRecord( nRecId, nRecSize ),
+ maXclRange( ScAddress::UNINITIALIZED ),
+ maBaseXclPos( ScAddress::UNINITIALIZED )
+{
+ maXclRange.Set(
+ static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
+ static_cast< sal_uInt16 >( rScRange.aStart.Row() ),
+ static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
+ static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) );
+ maBaseXclPos = maXclRange.maFirst;
+}
+
+bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const
+{
+ return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
+}
+
+void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
+ sal_uInt32 nXclRow = static_cast< sal_uInt32 >( rScPos.Row() );
+ maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
+ maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
+ maXclRange.maLast.mnCol = ::std::max( maXclRange.maLast.mnCol, nXclCol );
+ maXclRange.maLast.mnRow = ::std::max( maXclRange.maLast.mnRow, nXclRow );
+}
+
+void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
+{
+ maXclRange.Write( rStrm, false );
+}
+
+// Array formulas =============================================================
+
+XclExpArray::XclExpArray( const XclTokenArrayRef& xTokArr, const ScRange& rScRange ) :
+ XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
+ mxTokArr( xTokArr )
+{
+}
+
+XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
+}
+
+bool XclExpArray::IsVolatile() const
+{
+ return mxTokArr->IsVolatile();
+}
+
+void XclExpArray::WriteBody( XclExpStream& rStrm )
+{
+ WriteRangeAddress( rStrm );
+ sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
+ rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
+}
+
+XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
+{
+ const ScAddress& rScPos = rScRange.aStart;
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
+
+ OSL_ENSURE( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
+ XclExpArrayRef& rxRec = maRecMap[ rScPos ];
+ rxRec = new XclExpArray( xTokArr, rScRange );
+ return rxRec;
+}
+
+XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr, const ScAddress& rBasePos ) const
+{
+ XclExpArrayRef xRec;
+ // try to extract a matrix reference token
+ if (rScTokArr.GetLen() != 1)
+ // Must consist of a single reference token.
+ return xRec;
+
+ const formula::FormulaToken* pToken = rScTokArr.GetArray()[0];
+ if (!pToken || pToken->GetOpCode() != ocMatRef)
+ // not a matrix reference token.
+ return xRec;
+
+ const ScSingleRefData& rRef = *pToken->GetSingleRef();
+ ScAddress aAbsPos = rRef.toAbs(GetRoot().GetDoc(), rBasePos);
+ XclExpArrayMap::const_iterator it = maRecMap.find(aAbsPos);
+
+ if (it != maRecMap.end())
+ xRec = it->second;
+ return xRec;
+}
+
+// Shared formulas ============================================================
+
+XclExpShrfmla::XclExpShrfmla( const XclTokenArrayRef& xTokArr, const ScAddress& rScPos ) :
+ XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
+ mxTokArr( xTokArr ),
+ mnUsedCount( 1 )
+{
+}
+
+void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
+{
+ Extend( rScPos );
+ ++mnUsedCount;
+}
+
+XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
+}
+
+bool XclExpShrfmla::IsVolatile() const
+{
+ return mxTokArr->IsVolatile();
+}
+
+void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
+{
+ WriteRangeAddress( rStrm );
+ rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
+}
+
+XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+bool XclExpShrfmlaBuffer::IsValidTokenArray( const ScTokenArray& rArray ) const
+{
+ using namespace formula;
+
+ FormulaToken** pTokens = rArray.GetArray();
+ sal_uInt16 nLen = rArray.GetLen();
+ for (sal_uInt16 i = 0; i < nLen; ++i)
+ {
+ const FormulaToken* p = pTokens[i];
+ switch (p->GetType())
+ {
+ case svSingleRef:
+ {
+ const ScSingleRefData& rRefData = *p->GetSingleRef();
+ if (!GetFormulaCompiler().IsRef2D(rRefData))
+ // Excel's shared formula cannot include 3D reference.
+ return false;
+ }
+ break;
+ case svDoubleRef:
+ {
+ const ScComplexRefData& rRefData = *p->GetDoubleRef();
+ if (!GetFormulaCompiler().IsRef2D(rRefData))
+ // Excel's shared formula cannot include 3D reference.
+ return false;
+ }
+ break;
+ case svExternalSingleRef:
+ case svExternalDoubleRef:
+ case svExternalName:
+ // External references aren't allowed.
+ return false;
+ default:
+ ;
+ }
+ }
+ return true;
+}
+
+XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
+ const ScFormulaCell& rScCell, const ScAddress& rScPos )
+{
+ XclExpShrfmlaRef xRec;
+ const ScTokenArray* pShrdScTokArr = rScCell.GetSharedCode();
+ if (!pShrdScTokArr)
+ // This formula cell is not shared formula cell.
+ return xRec;
+
+ // Check to see if this shared formula contains any tokens that Excel's shared formula cannot handle.
+ if (maBadTokens.count(pShrdScTokArr) > 0)
+ // Already on the black list. Skip it.
+ return xRec;
+
+ if (!IsValidTokenArray(*pShrdScTokArr))
+ {
+ // We can't export this as shared formula.
+ maBadTokens.insert(pShrdScTokArr);
+ return xRec;
+ }
+
+ TokensType::iterator aIt = maRecMap.find(pShrdScTokArr);
+ if( aIt == maRecMap.end() )
+ {
+ // create a new record
+ XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
+ xRec = new XclExpShrfmla( xTokArr, rScPos );
+ maRecMap[ pShrdScTokArr ] = xRec;
+ }
+ else
+ {
+ // extend existing record
+ OSL_ENSURE( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
+ xRec = aIt->second;
+ xRec->ExtendRange( rScPos );
+ }
+
+ return xRec;
+}
+
+// Multiple operations ========================================================
+
+XclExpTableop::XclExpTableop( const ScAddress& rScPos,
+ const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
+ XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
+ mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
+ mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
+ mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ),
+ mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
+ mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ),
+ mnScMode( nScMode ),
+ mbValid( false )
+{
+}
+
+bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
+{
+ sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
+ sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
+
+ bool bOk = IsAppendable( nXclCol, nXclRow );
+ if( bOk )
+ {
+ SCCOL nFirstScCol = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
+ SCROW nFirstScRow = static_cast< SCROW >( maXclRange.maFirst.mnRow );
+ SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
+ SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
+ SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
+ SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
+
+ bOk = ((mnScMode == 2) == rRefs.mbDblRefMode) &&
+ (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
+ (nColInpScCol == rRefs.maColFirstScPos.Col()) &&
+ (nColInpScRow == rRefs.maColFirstScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColRelScPos.Tab());
+
+ if( bOk ) switch( mnScMode )
+ {
+ case 0:
+ bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
+ (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
+ (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row());
+ break;
+ case 1:
+ bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
+ (nFirstScRow == rRefs.maColRelScPos.Row() + 1);
+ break;
+ case 2:
+ bOk = (nFirstScCol == rRefs.maFmlaScPos.Col() + 1) &&
+ (nFirstScRow == rRefs.maFmlaScPos.Row() + 1) &&
+ (nFirstScCol == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
+ (nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
+ (nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
+ (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
+ (nFirstScRow == rRefs.maRowRelScPos.Row() + 1) &&
+ (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
+ break;
+ default:
+ bOk = false;
+ }
+
+ if( bOk )
+ {
+ // extend the cell range
+ OSL_ENSURE( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
+ Extend( rScPos );
+ mnLastAppXclCol = nXclCol;
+ }
+ }
+
+ return bOk;
+}
+
+void XclExpTableop::Finalize()
+{
+ // is the range complete? (last appended cell is in last column)
+ mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
+ // if last row is incomplete, try to shorten the used range
+ if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
+ {
+ --maXclRange.maLast.mnRow;
+ mbValid = true;
+ }
+
+ // check if referred cells are outside of own range
+ if( !mbValid )
+ return;
+
+ switch( mnScMode )
+ {
+ case 0:
+ mbValid = (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
+ break;
+ case 1:
+ mbValid = (mnColInpXclCol < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
+ break;
+ case 2:
+ mbValid = ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
+ ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
+ (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
+ break;
+ }
+}
+
+XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
+{
+ XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
+ return mbValid ?
+ rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
+ rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
+}
+
+bool XclExpTableop::IsVolatile() const
+{
+ return true;
+}
+
+void XclExpTableop::Save( XclExpStream& rStrm )
+{
+ if( mbValid )
+ XclExpRangeFmlaBase::Save( rStrm );
+}
+
+bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
+{
+ return ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
+ ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
+ ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
+}
+
+void XclExpTableop::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
+ switch( mnScMode )
+ {
+ case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break;
+ case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
+ }
+
+ WriteRangeAddress( rStrm );
+ rStrm << nFlags;
+ if( mnScMode == 2 )
+ rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
+ else
+ rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
+}
+
+XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+}
+
+XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
+ const ScTokenArray& rScTokArr, const ScAddress& rScPos )
+{
+ XclExpTableopRef xRec;
+
+ // try to extract cell references of a multiple operations formula
+ XclMultipleOpRefs aRefs;
+ if (XclTokenArrayHelper::GetMultipleOpRefs(GetDoc(), aRefs, rScTokArr, rScPos))
+ {
+ // try to find an existing TABLEOP record for this cell position
+ for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
+ {
+ XclExpTableop* xTempRec = maTableopList.GetRecord( nPos );
+ if( xTempRec->TryExtend( rScPos, aRefs ) )
+ xRec = xTempRec;
+ }
+
+ // no record found, or found record not extensible
+ if( !xRec )
+ xRec = TryCreate( rScPos, aRefs );
+ }
+
+ return xRec;
+}
+
+void XclExpTableopBuffer::Finalize()
+{
+ for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
+ maTableopList.GetRecord( nPos )->Finalize();
+}
+
+XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
+{
+ sal_uInt8 nScMode = 0;
+ bool bOk = (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
+ (rScPos.Tab() == rRefs.maColRelScPos.Tab());
+
+ if( bOk )
+ {
+ if( rRefs.mbDblRefMode )
+ {
+ nScMode = 2;
+ bOk = (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
+ (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
+ (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
+ (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
+ (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
+ }
+ else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row()) )
+ {
+ nScMode = 0;
+ }
+ else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
+ (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
+ (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
+ (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
+ {
+ nScMode = 1;
+ }
+ else
+ {
+ bOk = false;
+ }
+ }
+
+ XclExpTableopRef xRec;
+ if( bOk )
+ {
+ xRec = new XclExpTableop( rScPos, rRefs, nScMode );
+ maTableopList.AppendRecord( xRec );
+ }
+
+ return xRec;
+}
+
+// Cell records
+
+XclExpCellBase::XclExpCellBase(
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
+ XclExpRecord( nRecId, nContSize + 4 ),
+ maXclPos( rXclPos )
+{
+}
+
+bool XclExpCellBase::IsMultiLineText() const
+{
+ return false;
+}
+
+bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
+{
+ return false;
+}
+
+void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
+{
+ // default: do nothing
+}
+
+void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/, size_t /*nStartAllNotFound*/ )
+{
+ // default: do nothing
+}
+
+// Single cell records ========================================================
+
+XclExpSingleCellBase::XclExpSingleCellBase(
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
+ XclExpCellBase( nRecId, 2, rXclPos ),
+ maXFId( nXFId ),
+ mnContSize( nContSize )
+{
+}
+
+XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
+ XclExpCellBase( nRecId, 2, rXclPos ),
+ maXFId( nForcedXFId ),
+ mnContSize( nContSize )
+{
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
+}
+
+sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
+{
+ return GetXclCol();
+}
+
+sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
+{
+ return GetXFId();
+}
+
+bool XclExpSingleCellBase::IsEmpty() const
+{
+ return false;
+}
+
+void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
+{
+ maXFId.ConvertXFIndex( rRoot );
+}
+
+void XclExpSingleCellBase::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+ AddRecSize( mnContSize );
+ XclExpCellBase::Save( rStrm );
+}
+
+void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16> (GetXclRow()) << GetXclCol() << maXFId.mnXFIndex;
+ WriteContents( rStrm );
+}
+
+XclExpNumberCell::XclExpNumberCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
+ // #i41210# always use latin script for number cells - may look wrong for special number formats...
+ XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
+ mfValue( fValue )
+{
+}
+
+static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
+{
+ return OString::number( rStrm.GetRoot().GetXFBuffer()
+ .GetXmlCellIndex( nXFIndex ) );
+}
+
+static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
+{
+ sal_uInt32 nXFId = rCell.GetFirstXFId();
+ sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
+ return lcl_GetStyleId( rStrm, nXFIndex );
+}
+
+void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "n"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement(XML_v);
+ rWorksheet->write( mfValue );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
+{
+ rStrm << mfValue;
+}
+
+XclExpBooleanCell::XclExpBooleanCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
+ // #i41210# always use latin script for boolean cells
+ XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
+ mbValue( bValue )
+{
+}
+
+void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "b"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( mbValue ? "1" : "0" );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
+}
+
+XclExpLabelCell::XclExpLabelCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const OUString& rStr ) :
+ XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
+{
+ sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
+ XclExpStringRef xText = XclExpStringHelper::CreateCellString(
+ rRoot, rStr, pPattern, XclStrFlags::NONE, nMaxLen);
+ Init( rRoot, pPattern, xText );
+}
+
+XclExpLabelCell::XclExpLabelCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const EditTextObject* pEditText, XclExpHyperlinkHelper& rLinkHelper ) :
+ XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
+{
+ sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
+
+ XclExpStringRef xText;
+ if (pEditText)
+ xText = XclExpStringHelper::CreateCellString(
+ rRoot, *pEditText, pPattern, rLinkHelper, XclStrFlags::NONE, nMaxLen);
+ else
+ xText = XclExpStringHelper::CreateCellString(
+ rRoot, OUString(), pPattern, XclStrFlags::NONE, nMaxLen);
+
+ Init( rRoot, pPattern, xText );
+}
+
+bool XclExpLabelCell::IsMultiLineText() const
+{
+ return mbLineBreak || mxText->IsWrapped();
+}
+
+void XclExpLabelCell::Init( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, XclExpStringRef const & xText )
+{
+ OSL_ENSURE( xText && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
+ mxText = xText;
+ mnSstIndex = 0;
+
+ const XclFormatRunVec& rFormats = mxText->GetFormats();
+ // remove formatting of the leading run if the entire string
+ // is equally formatted
+ sal_uInt16 nXclFont = EXC_FONT_NOTFOUND;
+ if( rFormats.size() == 1 )
+ nXclFont = mxText->RemoveLeadingFont();
+ else
+ nXclFont = mxText->GetLeadingFont();
+
+ // create cell format
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ {
+ OSL_ENSURE( nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found" );
+ bool bForceLineBreak = mxText->IsWrapped();
+ SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
+ }
+
+ // get auto-wrap attribute from cell format
+ const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
+ mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
+
+ // initialize the record contents
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF5:
+ // BIFF5-BIFF7: create a LABEL or RSTRING record
+ OSL_ENSURE( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
+ SetContSize( mxText->GetSize() );
+ // formatted string is exported in an RSTRING record
+ if( mxText->IsRich() )
+ {
+ OSL_ENSURE( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
+ mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
+ SetRecId( EXC_ID_RSTRING );
+ SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
+ }
+ break;
+ case EXC_BIFF8:
+ // BIFF8+: create a LABELSST record
+ mnSstIndex = rRoot.GetSst().Insert( xText );
+ SetRecId( EXC_ID_LABELSST );
+ SetContSize( 4 );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, "s"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( static_cast<sal_Int32>(mnSstIndex) );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
+{
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF5:
+ rStrm << *mxText;
+ if( mxText->IsRich() )
+ {
+ rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
+ mxText->WriteFormats( rStrm );
+ }
+ break;
+ case EXC_BIFF8:
+ rStrm << mnSstIndex;
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+XclExpFormulaCell::XclExpFormulaCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const ScFormulaCell& rScFmlaCell,
+ XclExpArrayBuffer& rArrayBfr,
+ XclExpShrfmlaBuffer& rShrfmlaBfr,
+ XclExpTableopBuffer& rTableopBfr ) :
+ XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
+ mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
+{
+ // *** Find result number format overwriting cell number format *** -------
+
+ if( GetXFId() == EXC_XFID_NOTFOUND )
+ {
+ SvNumberFormatter& rFormatter = rRoot.GetFormatter();
+ XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
+
+ // current cell number format
+ sal_uInt32 nScNumFmt = pPattern ?
+ pPattern->GetItemSet().Get( ATTR_VALUE_FORMAT ).GetValue() :
+ rNumFmtBfr.GetStandardFormat();
+
+ // alternative number format passed to XF buffer
+ sal_uInt32 nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ /* Xcl doesn't know Boolean number formats, we write
+ "TRUE";"FALSE" (language dependent). Don't do it for automatic
+ formula formats, because Excel gets them right. */
+ /* #i8640# Don't set text format, if we have string results. */
+ SvNumFormatType nFormatType = mrScFmlaCell.GetFormatType();
+ if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
+ (nFormatType != SvNumFormatType::LOGICAL) &&
+ (nFormatType != SvNumFormatType::TEXT) )
+ nAltScNumFmt = nScNumFmt;
+ /* If cell number format is Boolean and automatic formula
+ format is Boolean don't write that ugly special format. */
+ else if( (nFormatType == SvNumFormatType::LOGICAL) &&
+ (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) )
+ nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
+
+ // #i41420# find script type according to result type (always latin for numeric results)
+ sal_Int16 nScript = ApiScriptType::LATIN;
+ bool bForceLineBreak = false;
+ if( nFormatType == SvNumFormatType::TEXT )
+ {
+ OUString aResult = mrScFmlaCell.GetString().getString();
+ bForceLineBreak = mrScFmlaCell.IsMultilineResult();
+ nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
+ }
+ SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
+ }
+
+ // *** Convert the formula token array *** --------------------------------
+
+ ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
+ const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
+
+ // first try to create multiple operations
+ mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
+
+ // no multiple operation found - try to create matrix formula
+ if( !mxAddRec )
+ switch( mrScFmlaCell.GetMatrixFlag() )
+ {
+ case ScMatrixMode::Formula:
+ {
+ // origin of the matrix - find the used matrix range
+ SCCOL nMatWidth;
+ SCROW nMatHeight;
+ mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
+ OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
+ ScRange aMatScRange( aScPos );
+ ScAddress& rMatEnd = aMatScRange.aEnd;
+ rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
+ rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
+ // reduce to valid range (range keeps valid, because start position IS valid)
+ rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
+ // create the ARRAY record
+ mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
+ }
+ break;
+ case ScMatrixMode::Reference:
+ {
+ // other formula cell covered by a matrix - find the ARRAY record
+ mxAddRec = rArrayBfr.FindArray(rScTokArr, aScPos);
+ // should always be found, if Calc document is not broken
+ OSL_ENSURE( mxAddRec, "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
+ }
+ break;
+ default:;
+ }
+
+ // no matrix found - try to create shared formula
+ if( !mxAddRec )
+ mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla(mrScFmlaCell, aScPos);
+
+ // no shared formula found - create a simple cell formula
+ if( !mxAddRec )
+ mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
+}
+
+void XclExpFormulaCell::Save( XclExpStream& rStrm )
+{
+ // create token array for FORMULA cells with additional record
+ if( mxAddRec )
+ mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
+
+ // FORMULA record itself
+ OSL_ENSURE( mxTokArr, "XclExpFormulaCell::Save - missing token array" );
+ if( !mxTokArr )
+ mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
+ SetContSize( 16 + mxTokArr->GetSize() );
+ XclExpSingleCellBase::Save( rStrm );
+
+ // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
+ if( mxAddRec && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
+ mxAddRec->Save( rStrm );
+
+ // STRING record for string result
+ if( mxStringRec )
+ mxStringRec->Save( rStrm );
+}
+
+void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
+{
+ const char* sType = nullptr;
+ OUString sValue;
+ XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell, sType, sValue );
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, *this),
+ XML_t, sType
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+
+ bool bWriteFormula = true;
+ bool bTagStarted = false;
+ ScAddress aScPos( static_cast< SCCOL >( GetXclPos().mnCol ),
+ static_cast< SCROW >( GetXclPos().mnRow ), rStrm.GetRoot().GetCurrScTab() );
+
+ switch (mrScFmlaCell.GetMatrixFlag())
+ {
+ case ScMatrixMode::NONE:
+ break;
+ case ScMatrixMode::Reference:
+ bWriteFormula = false;
+ break;
+ case ScMatrixMode::Formula:
+ {
+ // origin of the matrix - find the used matrix range
+ SCCOL nMatWidth;
+ SCROW nMatHeight;
+ mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
+ OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
+ ScRange aMatScRange( aScPos );
+ ScAddress& rMatEnd = aMatScRange.aEnd;
+ rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
+ rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
+ // reduce to valid range (range keeps valid, because start position IS valid
+ rStrm.GetRoot().GetAddressConverter().ValidateRange( aMatScRange, true );
+
+ OStringBuffer sFmlaCellRange;
+ if (rStrm.GetRoot().GetDoc().ValidRange(aMatScRange))
+ {
+ // calculate the cell range.
+ sFmlaCellRange.append( XclXmlUtils::ToOString(
+ rStrm.GetRoot().GetStringBuf(), aMatScRange.aStart ).getStr());
+ sFmlaCellRange.append(":");
+ sFmlaCellRange.append( XclXmlUtils::ToOString(
+ rStrm.GetRoot().GetStringBuf(), aMatScRange.aEnd ).getStr());
+ }
+
+ if ( aMatScRange.aStart.Col() == GetXclPos().mnCol &&
+ aMatScRange.aStart.Row() == static_cast<SCROW>(GetXclPos().mnRow))
+ {
+ rWorksheet->startElement( XML_f,
+ XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
+ (mxAddRec && mxAddRec->IsVolatile())),
+ XML_t, mxAddRec ? "array" : nullptr,
+ XML_ref, !sFmlaCellRange.isEmpty()? sFmlaCellRange.getStr() : nullptr
+ // OOXTODO: XML_dt2D, bool
+ // OOXTODO: XML_dtr, bool
+ // OOXTODO: XML_del1, bool
+ // OOXTODO: XML_del2, bool
+ // OOXTODO: XML_r1, ST_CellRef
+ // OOXTODO: XML_r2, ST_CellRef
+ // OOXTODO: XML_ca, bool
+ // OOXTODO: XML_si, uint
+ // OOXTODO: XML_bx bool
+ );
+ bTagStarted = true;
+ }
+ }
+ break;
+ }
+
+ if (bWriteFormula)
+ {
+ if (!bTagStarted)
+ {
+ rWorksheet->startElement( XML_f,
+ XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
+ (mxAddRec && mxAddRec->IsVolatile()) ) );
+ }
+ rWorksheet->writeEscaped( XclXmlUtils::ToOUString(
+ rStrm.GetRoot().GetCompileFormulaContext(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode(),
+ mrScFmlaCell.GetErrCode()));
+ rWorksheet->endElement( XML_f );
+ }
+
+ if( strcmp( sType, "inlineStr" ) == 0 )
+ {
+ rWorksheet->startElement(XML_is);
+ rWorksheet->startElement(XML_t);
+ rWorksheet->writeEscaped( sValue );
+ rWorksheet->endElement( XML_t );
+ rWorksheet->endElement( XML_is );
+ }
+ else
+ {
+ rWorksheet->startElement(XML_v);
+ rWorksheet->writeEscaped( sValue );
+ rWorksheet->endElement( XML_v );
+ }
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
+{
+ FormulaError nScErrCode = mrScFmlaCell.GetErrCode();
+ if( nScErrCode != FormulaError::NONE )
+ {
+ rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
+ << XclTools::GetXclErrorCode( nScErrCode )
+ << sal_uInt8( 0 ) << sal_uInt16( 0 )
+ << sal_uInt16( 0xFFFF );
+ }
+ else
+ {
+ // result of the formula
+ switch( mrScFmlaCell.GetFormatType() )
+ {
+ case SvNumFormatType::NUMBER:
+ {
+ // either value or error code
+ rStrm << mrScFmlaCell.GetValue();
+ }
+ break;
+
+ case SvNumFormatType::TEXT:
+ {
+ OUString aResult = mrScFmlaCell.GetString().getString();
+ if( !aResult.isEmpty() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
+ {
+ rStrm << EXC_FORMULA_RES_STRING;
+ mxStringRec = new XclExpStringRec( rStrm.GetRoot(), aResult );
+ }
+ else
+ rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only
+ rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
+ }
+ break;
+
+ case SvNumFormatType::LOGICAL:
+ {
+ sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
+ rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
+ << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
+ << sal_uInt16( 0xFFFF );
+ }
+ break;
+
+ default:
+ rStrm << mrScFmlaCell.GetValue();
+ }
+ }
+
+ // flags and formula token array
+ sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
+ ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec && mxAddRec->IsVolatile()) );
+ ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
+ rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
+}
+
+// Multiple cell records ======================================================
+
+XclExpMultiCellBase::XclExpMultiCellBase(
+ sal_uInt16 nRecId, sal_uInt16 nMulRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
+ XclExpCellBase( nRecId, 0, rXclPos ),
+ mnMulRecId( nMulRecId ),
+ mnContSize( nContSize )
+{
+}
+
+sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
+{
+ return GetXclCol() + GetCellCount() - 1;
+}
+
+sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
+{
+ return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
+}
+
+bool XclExpMultiCellBase::IsEmpty() const
+{
+ return maXFIds.empty();
+}
+
+void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
+{
+ for( auto& rXFId : maXFIds )
+ rXFId.ConvertXFIndex( rRoot );
+}
+
+void XclExpMultiCellBase::Save( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+
+ XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
+ XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
+ XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
+ sal_uInt16 nBegXclCol = GetXclCol();
+ sal_uInt16 nEndXclCol = nBegXclCol;
+
+ while( aRangeEnd != aEnd )
+ {
+ // find begin of next used XF range
+ aRangeBeg = aRangeEnd;
+ nBegXclCol = nEndXclCol;
+ while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
+ ++aRangeBeg;
+ }
+ // find end of next used XF range
+ aRangeEnd = aRangeBeg;
+ nEndXclCol = nBegXclCol;
+ while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
+ {
+ nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
+ ++aRangeEnd;
+ }
+
+ // export this range as a record
+ if( aRangeBeg != aRangeEnd )
+ {
+ sal_uInt16 nCount = nEndXclCol - nBegXclCol;
+ bool bIsMulti = nCount > 1;
+ std::size_t nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
+ if( bIsMulti ) nTotalSize += 2;
+
+ rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
+ rStrm << static_cast<sal_uInt16> (GetXclRow()) << nBegXclCol;
+
+ sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
+ for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
+ {
+ rStrm << aIt->mnXFIndex;
+ WriteContents( rStrm, nRelCol );
+ ++nRelCol;
+ }
+ }
+ if( bIsMulti )
+ rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
+ rStrm.EndRecord();
+ }
+ }
+}
+
+void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
+{
+ XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
+ XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
+ XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
+ sal_uInt16 nBegXclCol = GetXclCol();
+ sal_uInt16 nEndXclCol = nBegXclCol;
+
+ while( aRangeEnd != aEnd )
+ {
+ // find begin of next used XF range
+ aRangeBeg = aRangeEnd;
+ nBegXclCol = nEndXclCol;
+ while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
+ ++aRangeBeg;
+ }
+ // find end of next used XF range
+ aRangeEnd = aRangeBeg;
+ nEndXclCol = nBegXclCol;
+ while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
+ {
+ nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
+ ++aRangeEnd;
+ }
+
+ // export this range as a record
+ if( aRangeBeg != aRangeEnd )
+ {
+ sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
+ sal_Int32 nRelCol = 0;
+ for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
+ {
+ WriteXmlContents(
+ rStrm,
+ XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
+ aIt->mnXFIndex,
+ nRelColIdx );
+ ++nRelCol;
+ ++nRelColIdx;
+ }
+ }
+ }
+ }
+}
+
+sal_uInt16 XclExpMultiCellBase::GetCellCount() const
+{
+ return std::accumulate(maXFIds.begin(), maXFIds.end(), sal_uInt16(0),
+ [](const sal_uInt16& rSum, const XclExpMultiXFId& rXFId) { return rSum + rXFId.mnCount; });
+}
+
+void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
+{
+ if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
+ maXFIds.push_back( rXFId );
+ else
+ maXFIds.back().mnCount += rXFId.mnCount;
+}
+
+void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
+{
+ sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
+ rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
+ AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
+}
+
+bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
+{
+ if( GetLastXclCol() + 1 == rCell.GetXclCol() )
+ {
+ maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
+ return true;
+ }
+ return false;
+}
+
+void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
+{
+ OSL_ENSURE( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
+ ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
+ for( const auto& rXFId : maXFIds )
+ {
+ ::std::fill( aDestIt, aDestIt + rXFId.mnCount, rXFId.mnXFIndex );
+ aDestIt += rXFId.mnCount;
+ }
+}
+
+void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
+{
+ // save last column before calling maXFIds.clear()
+ sal_uInt16 nLastXclCol = GetLastXclCol();
+ OSL_ENSURE( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
+
+ // build new XF index vector, containing passed XF indexes
+ maXFIds.clear();
+ // Process only all that possibly are not EXC_XF_NOTFOUND.
+ size_t nEnd = std::min<size_t>(nLastXclCol + 1, nStartAllNotFound);
+ for( size_t i = GetXclCol(); i < nEnd; ++i )
+ {
+ XclExpMultiXFId aXFId( 0 );
+ // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
+ aXFId.mnXFId = aXFId.mnXFIndex = rXFIndexes[ i ];
+ AppendXFId( aXFId );
+ }
+
+ // remove leading and trailing unused XF indexes
+ if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
+ {
+ SetXclCol( GetXclCol() + maXFIds.front().mnCount );
+ maXFIds.erase(maXFIds.begin(), maXFIds.begin() + 1);
+ }
+ if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
+ maXFIds.pop_back();
+
+ // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
+}
+
+sal_uInt16 XclExpMultiCellBase::GetStartColAllDefaultCell() const
+{
+ sal_uInt16 col = GetXclCol();
+ sal_uInt16 nMaxNonDefCol = col;
+ for( const auto& rXFId : maXFIds )
+ {
+ col += rXFId.mnCount;
+ if (rXFId.mnXFIndex != EXC_XF_DEFAULTCELL)
+ nMaxNonDefCol = col;
+ }
+ return nMaxNonDefCol;
+}
+
+XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
+ XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
+{
+ OSL_ENSURE( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
+ AppendXFId( rXFId );
+}
+
+XclExpBlankCell::XclExpBlankCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
+ XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
+{
+ OSL_ENSURE( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
+ // #i46627# use default script type instead of ApiScriptType::WEAK
+ AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
+}
+
+bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
+{
+ const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
+ return pBlankCell && TryMergeXFIds( *pBlankCell );
+}
+
+void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
+{
+ GetXFIndexes( rXFIndexes );
+}
+
+void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
+{
+ RemoveUnusedXFIndexes( rXFIndexes, nStartAllNotFound );
+}
+
+void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
+{
+}
+
+void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->singleElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, nXFId) );
+}
+
+XclExpRkCell::XclExpRkCell(
+ const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
+ XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
+{
+ // #i41210# always use latin script for number cells - may look wrong for special number formats...
+ AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
+ maRkValues.push_back( nRkValue );
+}
+
+bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
+{
+ const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
+ if( pRkCell && TryMergeXFIds( *pRkCell ) )
+ {
+ maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
+ return true;
+ }
+ return false;
+}
+
+void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_c,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
+ XML_s, lcl_GetStyleId(rStrm, nXFId),
+ XML_t, "n"
+ // OOXTODO: XML_cm, XML_vm, XML_ph
+ );
+ rWorksheet->startElement( XML_v );
+ rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
+ rWorksheet->endElement( XML_v );
+ rWorksheet->endElement( XML_c );
+}
+
+void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
+{
+ OSL_ENSURE( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
+ rStrm << maRkValues[ nRelCol ];
+}
+
+// Rows and Columns
+
+XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
+ mpScOLArray( nullptr ),
+ maLevelInfos( SC_OL_MAXDEPTH ),
+ mnCurrLevel( 0 ),
+ mbCurrCollapse( false )
+{
+ if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
+ mpScOLArray = &(bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray());
+
+ if( mpScOLArray )
+ for( size_t nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
+ if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
+ maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
+}
+
+void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
+{
+ if( !mpScOLArray )
+ return;
+
+ // find open level index for passed position
+ size_t nNewOpenScLevel = 0; // new open level (0-based Calc index)
+ sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index)
+
+ if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
+ nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
+ // else nNewLevel keeps 0 to show that there are no groups
+
+ mbCurrCollapse = false;
+ if( nNewLevel >= mnCurrLevel )
+ {
+ // new level(s) opened, or no level closed - update all level infos
+ for( size_t nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
+ {
+ /* In each level: check if a new group is started (there may be
+ neighbored groups without gap - therefore check ALL levels). */
+ if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
+ {
+ if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
+ {
+ maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
+ maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
+ }
+ }
+ }
+ }
+ else
+ {
+ // level(s) closed - check if any of the closed levels are collapsed
+ // Calc uses 0-based level indexes
+ sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
+ for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
+ mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
+ }
+
+ // cache new opened level
+ mnCurrLevel = nNewLevel;
+}
+
+XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
+ XclExpRecord( EXC_ID_GUTS, 8 ),
+ mnColLevels( 0 ),
+ mnColWidth( 0 ),
+ mnRowLevels( 0 ),
+ mnRowWidth( 0 )
+{
+ const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() );
+ if(!pOutlineTable)
+ return;
+
+ // column outline groups
+ const ScOutlineArray& rColArray = pOutlineTable->GetColArray();
+ mnColLevels = ulimit_cast< sal_uInt16 >( rColArray.GetDepth(), EXC_OUTLINE_MAX );
+ if( mnColLevels )
+ {
+ ++mnColLevels;
+ mnColWidth = 12 * mnColLevels + 5;
+ }
+
+ // row outline groups
+ const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
+ mnRowLevels = ulimit_cast< sal_uInt16 >( rRowArray.GetDepth(), EXC_OUTLINE_MAX );
+ if( mnRowLevels )
+ {
+ ++mnRowLevels;
+ mnRowWidth = 12 * mnRowLevels + 5;
+ }
+}
+
+void XclExpGuts::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
+}
+
+XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
+ mrRoot(rRoot),
+ mnFirstUsedXclRow( 0 ),
+ mnFirstFreeXclRow( 0 ),
+ mnFirstUsedXclCol( 0 ),
+ mnFirstFreeXclCol( 0 )
+{
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
+ case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+void XclExpDimensions::SetDimensions(
+ sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
+ sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
+{
+ mnFirstUsedXclRow = nFirstUsedXclRow;
+ mnFirstFreeXclRow = nFirstFreeXclRow;
+ mnFirstUsedXclCol = nFirstUsedXclCol;
+ mnFirstFreeXclCol = nFirstFreeXclCol;
+}
+
+void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
+{
+ ScRange aRange;
+ aRange.aStart.SetRow( static_cast<SCROW>(mnFirstUsedXclRow) );
+ aRange.aStart.SetCol( static_cast<SCCOL>(mnFirstUsedXclCol) );
+
+ if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
+ {
+ aRange.aEnd.SetRow( static_cast<SCROW>(mnFirstFreeXclRow-1) );
+ aRange.aEnd.SetCol( static_cast<SCCOL>(mnFirstFreeXclCol-1) );
+ }
+
+ aRange.PutInOrder();
+ rStrm.GetCurrentStream()->singleElement( XML_dimension,
+ // To be compatible with MS Office 2007,
+ // we need full address notation format
+ // e.g. "A1:AMJ177" and not partial like: "1:177".
+ XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), aRange, true) );
+}
+
+void XclExpDimensions::WriteBody( XclExpStream& rStrm )
+{
+ XclBiff eBiff = rStrm.GetRoot().GetBiff();
+ if( eBiff == EXC_BIFF8 )
+ rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
+ else
+ rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
+ rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
+ if( eBiff >= EXC_BIFF3 )
+ rStrm << sal_uInt16( 0 );
+}
+
+namespace {
+
+double lclGetCChCorrection(const XclExpRoot& rRoot)
+{
+ // Convert the correction from 1/256ths of a character size to count of chars
+ // TODO: make to fit ECMA-376-1:2016 18.3.1.81 sheetFormatPr (Sheet Format Properties):
+ // 5 pixels are added to the base width: 2 for margin padding on each side, plus 1 for gridline
+ // So this should depend on rRoot.GetCharWidth(), not on font height
+
+ tools::Long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
+ return XclTools::GetXclDefColWidthCorrection(nFontHt) / 256.0;
+}
+
+} // namespace
+
+XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
+ XclExpDoubleRecord(EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF + lclGetCChCorrection(rRoot)),
+ XclExpRoot( rRoot )
+{
+}
+
+bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
+{
+ // This formula is taking number of characters with GetValue()
+ // and it is translating it into default column width.
+ // https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.column.aspx
+ double defaultColumnWidth = 256.0 * GetValue();
+
+ // exactly matched, if difference is less than 1/16 of a character to the left or to the right
+ return std::abs(defaultColumnWidth - nXclColWidth) < 256.0 * 1.0 / 16.0;
+}
+
+void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS )
+{
+ double fCCh = nXclColWidth / 256.0;
+ if (bXLS)
+ {
+ const double fCorrection = lclGetCChCorrection(GetRoot());
+ const double fCorrectedCCh = fCCh - fCorrection;
+ // Now get the value which would be stored in XLS DefColWidth struct
+ double fCChRound = std::round(fCorrectedCCh);
+ // If default width was set to a value that is not representable as integral CCh between 0
+ // and 255, then just ignore that value, and use an arbitrary default. That way, the stored
+ // default might not represent the most used column width (or any used column width), but
+ // that's OK, and it just means that those columns will explicitly store their width in
+ // 1/256ths of char, and have fUserSet in their ColInfo records.
+ if (fCChRound < 0.0 || fCChRound > 255.0 || std::abs(fCChRound - fCorrectedCCh) > 1.0 / 512)
+ fCChRound = 8.0;
+ fCCh = fCChRound + fCorrection;
+ }
+
+ SetValue(fCCh);
+}
+
+void XclExpDefcolwidth::Save(XclExpStream& rStrm)
+{
+ double fCorrectedCCh = GetValue() - lclGetCChCorrection(GetRoot());
+ // Convert double to sal_uInt16
+ XclExpUInt16Record aUInt16Rec(GetRecId(),
+ static_cast<sal_uInt16>(std::round(fCorrectedCCh)));
+ aUInt16Rec.Save(rStrm);
+}
+
+XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
+ SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
+ XclExpRecord( EXC_ID_COLINFO, 12 ),
+ XclExpRoot( rRoot ),
+ mbCustomWidth( false ),
+ mnWidth( 0 ),
+ mnScWidth( 0 ),
+ mnFlags( 0 ),
+ mnOutlineLevel( 0 ),
+ mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
+ mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // column default format
+ maXFId.mnXFId = GetXFBuffer().Insert(
+ rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
+
+ // column width. If column is hidden then we should return real value (not zero)
+ sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab, false );
+ mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
+ mnScWidth = convertTwipToMm100(nScWidth);
+
+ // column flags
+ ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
+
+ // outline data
+ rOutlineBfr.Update( nScCol );
+ ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
+ ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
+ mnOutlineLevel = rOutlineBfr.GetLevel();
+}
+
+void XclExpColinfo::ConvertXFIndexes()
+{
+ maXFId.ConvertXFIndex( GetRoot() );
+}
+
+bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth )
+{
+ mbCustomWidth = !rDefColWidth.IsDefWidth(mnWidth);
+ return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) &&
+ (mnFlags == 0) &&
+ (mnOutlineLevel == 0) &&
+ !mbCustomWidth;
+}
+
+bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
+{
+ if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
+ (mnWidth == rColInfo.mnWidth) &&
+ (mnFlags == rColInfo.mnFlags) &&
+ (mnOutlineLevel == rColInfo.mnOutlineLevel) &&
+ (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
+ {
+ mnLastXclCol = rColInfo.mnLastXclCol;
+ return true;
+ }
+ return false;
+}
+
+void XclExpColinfo::WriteBody( XclExpStream& rStrm )
+{
+ // if last column is equal to last possible column, Excel adds one more
+ sal_uInt16 nLastXclCol = mnLastXclCol;
+ if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
+ ++nLastXclCol;
+
+ rStrm << mnFirstXclCol
+ << nLastXclCol
+ << mnWidth
+ << maXFId.mnXFIndex
+ << mnFlags
+ << sal_uInt16( 0 );
+}
+
+void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
+{
+ const double nExcelColumnWidth = mnScWidth / convertTwipToMm100<double>(GetCharWidth());
+
+ // tdf#101363 In MS specification the output value is set with double precision after delimiter:
+ // =Truncate(({width in pixels} - 5)/{Maximum Digit Width} * 100 + 0.5)/100
+ // Explanation of magic numbers:
+ // 5 number - are 4 pixels of margin padding (two on each side), plus 1 pixel padding for the gridlines.
+ // It is unknown if it should be applied during LibreOffice export
+ // 100 number - used to limit precision to 0.01 with formula =Truncate( {value}*100+0.5 ) / 100
+ // 0.5 number (0.005 to output value) - used to increase value before truncating,
+ // to avoid situation when 2.997 will be truncated to 2.99 and not to 3.00
+ const double nTruncatedExcelColumnWidth = std::trunc( nExcelColumnWidth * 100.0 + 0.5 ) / 100.0;
+ rStrm.GetCurrentStream()->singleElement( XML_col,
+ // OOXTODO: XML_bestFit,
+ XML_collapsed, ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
+ XML_customWidth, ToPsz( mbCustomWidth ),
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
+ XML_outlineLevel, OString::number(mnOutlineLevel),
+ XML_max, OString::number(mnLastXclCol + 1),
+ XML_min, OString::number(mnFirstXclCol + 1),
+ // OOXTODO: XML_phonetic,
+ XML_style, lcl_GetStyleId(rStrm, maXFId.mnXFIndex),
+ XML_width, OString::number(nTruncatedExcelColumnWidth) );
+}
+
+XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maDefcolwidth( rRoot ),
+ maOutlineBfr( rRoot ),
+ mnHighestOutlineLevel( 0 )
+{
+}
+
+void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
+{
+
+ for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
+ {
+ maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
+ if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
+ {
+ mnHighestOutlineLevel = maOutlineBfr.GetLevel();
+ }
+ }
+}
+
+void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS )
+{
+ rXFIndexes.clear();
+ rXFIndexes.reserve( maColInfos.GetSize() );
+
+ if( !maColInfos.IsEmpty())
+ {
+ XclExpColinfo* xPrevRec = maColInfos.GetRecord( 0 );
+ xPrevRec->ConvertXFIndexes();
+ for( size_t nPos = 1; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ xRec->ConvertXFIndexes();
+
+ // try to merge with previous record
+ if( xPrevRec->TryMerge( *xRec ) )
+ maColInfos.InvalidateRecord( nPos );
+ else
+ xPrevRec = xRec;
+ }
+ maColInfos.RemoveInvalidatedRecords();
+ }
+
+ // put XF indexes into passed vector, collect use count of all different widths
+ std::unordered_map< sal_uInt16, sal_uInt16 > aWidthMap;
+ sal_uInt16 nMaxColCount = 0;
+ sal_uInt16 nMaxUsedWidth = 0;
+ for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ const XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ sal_uInt16 nColCount = xRec->GetColCount();
+
+ // add XF index to passed vector
+ rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
+
+ // collect use count of column width
+ sal_uInt16 nWidth = xRec->GetColWidth();
+ sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
+ rnMapCount = rnMapCount + nColCount;
+ if( rnMapCount > nMaxColCount )
+ {
+ nMaxColCount = rnMapCount;
+ nMaxUsedWidth = nWidth;
+ }
+ }
+ maDefcolwidth.SetDefWidth( nMaxUsedWidth, bXLS );
+
+ // remove all default COLINFO records
+ for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
+ {
+ XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
+ if( xRec->IsDefault( maDefcolwidth ) )
+ maColInfos.InvalidateRecord( nPos );
+ }
+ maColInfos.RemoveInvalidatedRecords();
+}
+
+void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
+{
+ // DEFCOLWIDTH
+ maDefcolwidth.Save( rStrm );
+ // COLINFO records
+ maColInfos.Save( rStrm );
+}
+
+void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( maColInfos.IsEmpty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_cols);
+ maColInfos.SaveXml( rStrm );
+ rWorksheet->endElement( XML_cols );
+}
+
+XclExpDefaultRowData::XclExpDefaultRowData() :
+ mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
+{
+}
+
+XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
+ mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
+ mnHeight( rRow.GetHeight() )
+{
+ ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
+ ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
+}
+
+static bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
+{
+ return (rLeft.mnHeight < rRight.mnHeight) ||
+ ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
+}
+
+XclExpDefrowheight::XclExpDefrowheight() :
+ XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
+{
+}
+
+void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
+{
+ maDefData = rDefData;
+}
+
+void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
+ rStrm << maDefData.mnFlags << maDefData.mnHeight;
+}
+
+XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
+ XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty, bool bHidden, sal_uInt16 nHeight ) :
+ XclExpRecord( EXC_ID3_ROW, 16 ),
+ XclExpRoot( rRoot ),
+ mnXclRow( nXclRow ),
+ mnHeight( nHeight ),
+ mnFlags( EXC_ROW_DEFAULTFLAGS ),
+ mnXFIndex( EXC_XF_DEFAULTCELL ),
+ mnOutlineLevel( 0 ),
+ mnXclRowRpt( 1 ),
+ mnCurrentRow( nXclRow ),
+ mbAlwaysEmpty( bAlwaysEmpty ),
+ mbEnabled( true )
+{
+ SCTAB nScTab = GetCurrScTab();
+ SCROW nScRow = static_cast< SCROW >( mnXclRow );
+
+ // *** Row flags *** ------------------------------------------------------
+
+ CRFlags nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
+ bool bUserHeight( nRowFlags & CRFlags::ManualSize );
+ ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
+ ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
+
+ // *** Outline data *** ---------------------------------------------------
+
+ rOutlineBfr.Update( nScRow );
+ ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
+ ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
+ mnOutlineLevel = rOutlineBfr.GetLevel();
+
+ // *** Progress bar *** ---------------------------------------------------
+
+ XclExpProgressBar& rProgress = GetProgressBar();
+ rProgress.IncRowRecordCount();
+ rProgress.Progress();
+}
+
+static size_t findFirstAllSameUntilEnd( const ScfUInt16Vec& rIndexes, sal_uInt16 value,
+ size_t searchStart = std::numeric_limits<size_t>::max())
+{
+ for( size_t i = std::min(rIndexes.size(), searchStart); i >= 1; --i )
+ {
+ if( rIndexes[ i - 1 ] != value )
+ return i;
+ }
+ return 0;
+}
+
+void XclExpRow::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
+{
+ OSL_ENSURE( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
+ // try to merge with last existing cell
+ InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
+}
+
+void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes, ScfUInt16Vec& aXFIndexes, size_t nStartColAllDefault, bool bProgress )
+{
+ size_t nPos, nSize;
+
+ // *** Convert XF identifiers *** -----------------------------------------
+
+ // additionally collect the blank XF indexes
+ size_t nColCount = GetMaxPos().Col() + 1;
+ OSL_ENSURE( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
+
+ // The vector should be preset to all items being EXC_XF_NOTFOUND, to avoid repeated allocations
+ // and clearing.
+ assert( aXFIndexes.size() == nColCount );
+ assert( aXFIndexes.front() == EXC_XF_NOTFOUND );
+ assert( aXFIndexes.back() == EXC_XF_NOTFOUND );
+ for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
+ {
+ XclExpCellBase* pCell = maCellList.GetRecord( nPos );
+ pCell->ConvertXFIndexes( GetRoot() );
+ pCell->GetBlankXFIndexes( aXFIndexes );
+ }
+
+ // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
+
+ /* This is needed because nonexistent cells in Calc are not formatted at all,
+ but in Excel they would have the column default format. Blank cells that
+ are equal to the respective column default are removed later in this function. */
+ if( !mbAlwaysEmpty )
+ {
+ // XF identifier representing default cell XF
+ XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
+ aXFId.ConvertXFIndex( GetRoot() );
+
+ nPos = 0;
+ while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop
+ {
+ // get column index that follows previous cell
+ sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
+ // get own column index
+ sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
+
+ // is there a gap?
+ if( nFirstFreeXclCol < nNextUsedXclCol )
+ {
+ aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
+ XclExpCellRef xNewCell = new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId );
+ // insert the cell, InsertCell() may merge it with existing BLANK records
+ InsertCell( xNewCell, nPos, false );
+ // insert default XF indexes into aXFIndexes
+ for( size_t i = nFirstFreeXclCol; i < nNextUsedXclCol; ++i )
+ aXFIndexes[ i ] = aXFId.mnXFIndex;
+ // don't step forward with nPos, InsertCell() may remove records
+ }
+ else
+ ++nPos;
+ }
+ }
+
+ // *** Find default row format *** ----------------------------------------
+
+ // Often there will be many EXC_XF_DEFAULTCELL at the end, optimize by ignoring them.
+ size_t nStartSearchAllDefault = aXFIndexes.size();
+ if( !maCellList.IsEmpty() && dynamic_cast< const XclExpBlankCell* >( maCellList.GetLastRecord()))
+ {
+ const XclExpBlankCell* pLastBlank = static_cast< const XclExpBlankCell* >( maCellList.GetLastRecord());
+ assert(pLastBlank->GetLastXclCol() == aXFIndexes.size() - 1);
+ nStartSearchAllDefault = pLastBlank->GetStartColAllDefaultCell();
+ }
+ size_t nStartAllDefault = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_DEFAULTCELL, nStartSearchAllDefault);
+
+ // find most used XF index in the row
+ sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
+ const size_t nHalfIndexes = aXFIndexes.size() / 2;
+ if( nStartAllDefault > nHalfIndexes ) // Otherwise most are EXC_XF_DEFAULTCELL.
+ {
+ // Very likely the most common one is going to be the last one.
+ nRowXFIndex = aXFIndexes.back();
+ size_t nStartLastSame = findFirstAllSameUntilEnd( aXFIndexes, nRowXFIndex );
+ if( nStartLastSame > nHalfIndexes ) // No, find out the most used one by counting.
+ {
+ std::unordered_map< sal_uInt16, size_t > aIndexMap;
+ size_t nMaxXFCount = 0;
+ for( const auto& rXFIndex : aXFIndexes )
+ {
+ if( rXFIndex != EXC_XF_NOTFOUND )
+ {
+ size_t& rnCount = aIndexMap[ rXFIndex ];
+ ++rnCount;
+ if( rnCount > nMaxXFCount )
+ {
+ nRowXFIndex = rXFIndex;
+ nMaxXFCount = rnCount;
+ if (nMaxXFCount > nHalfIndexes)
+ {
+ // No other XF index can have a greater usage count, we
+ // don't need to loop through the remaining cells.
+ // Specifically for the tail of unused default
+ // cells/columns this makes a difference.
+ break; // for
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // decide whether to use the row default XF index or column default XF indexes
+ bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
+ if( !bUseColDefXFs )
+ {
+ // count needed XF indexes for blank cells with and without row default XF index
+ size_t nXFCountWithRowDefXF = 0;
+ size_t nXFCountWithoutRowDefXF = 0;
+ ScfUInt16Vec::const_iterator aColIt = rColXFIndexes.begin();
+ for( const auto& rXFIndex : aXFIndexes )
+ {
+ sal_uInt16 nXFIndex = rXFIndex;
+ if( nXFIndex != EXC_XF_NOTFOUND )
+ {
+ if( nXFIndex != nRowXFIndex )
+ ++nXFCountWithRowDefXF; // with row default XF index
+ if( nXFIndex != *aColIt )
+ ++nXFCountWithoutRowDefXF; // without row default XF index
+ }
+ ++aColIt;
+ }
+
+ // use column XF indexes if this would cause less or equal number of BLANK records
+ bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
+ }
+
+ // *** Remove unused BLANK cell records *** -------------------------------
+
+ size_t maxStartAllNotFound;
+ if( bUseColDefXFs )
+ {
+ size_t maxStartAllDefault = std::max( nStartAllDefault, nStartColAllDefault );
+ // use column default XF indexes
+ // #i194#: remove cell XF indexes equal to column default XF indexes
+ for( size_t i = 0; i < maxStartAllDefault; ++i )
+ {
+ if( aXFIndexes[ i ] == rColXFIndexes[ i ] )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+ }
+ // They can differ only up to maxNonDefault, in the rest they are the same.
+ for( size_t i = maxStartAllDefault; i < aXFIndexes.size(); ++i )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+ maxStartAllNotFound = maxStartAllDefault;
+ }
+ else
+ {
+ // use row default XF index
+ mnXFIndex = nRowXFIndex;
+ ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
+ // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
+ for( auto& rXFIndex : aXFIndexes )
+ if( rXFIndex == nRowXFIndex )
+ rXFIndex = EXC_XF_NOTFOUND;
+ maxStartAllNotFound = aXFIndexes.size();
+ }
+
+ // remove unused parts of BLANK/MULBLANK cell records
+ size_t nStartAllNotFound = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_NOTFOUND, maxStartAllNotFound );
+ nPos = 0;
+ while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop
+ {
+ XclExpCellBase* xCell = maCellList.GetRecord( nPos );
+ xCell->RemoveUnusedBlankCells( aXFIndexes, nStartAllNotFound );
+ if( xCell->IsEmpty() )
+ maCellList.RemoveRecord( nPos );
+ else
+ ++nPos;
+ }
+ // Ensure it's all EXC_XF_NOTFOUND again for next reuse.
+ for( size_t i = 0; i < nStartAllNotFound; ++i )
+ aXFIndexes[ i ] = EXC_XF_NOTFOUND;
+
+ // progress bar includes disabled rows; only update it in the lead thread.
+ if (bProgress)
+ GetProgressBar().Progress();
+}
+sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
+{
+ return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
+}
+
+sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
+{
+ return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
+}
+
+bool XclExpRow::IsDefaultable() const
+{
+ const sal_uInt16 nFlagsAlwaysMarkedAsDefault = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
+ return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nFlagsAlwaysMarkedAsDefault ) ) &&
+ IsEmpty();
+}
+
+void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
+{
+ mbEnabled = !IsDefaultable() ||
+ (mnHeight != rDefRowData.mnHeight) ||
+ (IsHidden() != rDefRowData.IsHidden()) ||
+ (IsUnsynced() != rDefRowData.IsUnsynced());
+}
+
+void XclExpRow::WriteCellList( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
+ maCellList.Save( rStrm );
+}
+
+void XclExpRow::Save( XclExpStream& rStrm )
+{
+ if( mbEnabled )
+ {
+ mnCurrentRow = mnXclRow;
+ for ( sal_uInt32 i = 0; i < mnXclRowRpt; ++i, ++mnCurrentRow )
+ XclExpRecord::Save( rStrm );
+ }
+}
+
+void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
+{
+ OSL_ENSURE( xCell, "XclExpRow::InsertCell - missing cell" );
+
+ /* If we have a multi-line text in a merged cell, and the resulting
+ row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
+ flag to be true to ensure Excel works correctly. */
+ if( bIsMergedBase && xCell->IsMultiLineText() )
+ ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
+
+ // try to merge with previous cell, insert the new cell if not successful
+ XclExpCellBase* xPrevCell = maCellList.GetRecord( nPos - 1 );
+ if( xPrevCell && xPrevCell->TryMerge( *xCell ) )
+ xCell = xPrevCell;
+ else
+ maCellList.InsertRecord( xCell, nPos++ );
+ // nPos points now to following cell
+
+ // try to merge with following cell, remove it if successful
+ XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
+ if( xNextCell && xCell->TryMerge( *xNextCell ) )
+ maCellList.RemoveRecord( nPos );
+}
+
+void XclExpRow::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast< sal_uInt16 >(mnCurrentRow)
+ << GetFirstUsedXclCol()
+ << GetFirstFreeXclCol()
+ << mnHeight
+ << sal_uInt32( 0 )
+ << mnFlags
+ << mnXFIndex;
+}
+
+void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( !mbEnabled )
+ return;
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
+ mnCurrentRow = mnXclRow + 1;
+ for ( sal_uInt32 i=0; i<mnXclRowRpt; ++i )
+ {
+ rWorksheet->startElement( XML_row,
+ XML_r, OString::number(mnCurrentRow++),
+ // OOXTODO: XML_spans, optional
+ XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : nullptr,
+ XML_customFormat, ToPsz( haveFormat ),
+ XML_ht, OString::number(static_cast<double>(mnHeight) / 20.0),
+ XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
+ XML_customHeight, ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
+ XML_outlineLevel, OString::number(mnOutlineLevel),
+ XML_collapsed, ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) )
+ // OOXTODO: XML_thickTop, bool
+ // OOXTODO: XML_thickBot, bool
+ // OOXTODO: XML_ph, bool
+ );
+ // OOXTODO: XML_extLst
+ maCellList.SaveXml( rStrm );
+ rWorksheet->endElement( XML_row );
+ }
+}
+
+XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maOutlineBfr( rRoot ),
+ maDimensions( rRoot ),
+ mnHighestOutlineLevel( 0 )
+{
+}
+
+void XclExpRowBuffer::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
+{
+ OSL_ENSURE( xCell, "XclExpRowBuffer::AppendCell - missing cell" );
+ GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
+}
+
+void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
+{
+ if( nFirstFreeScRow > 0 )
+ GetOrCreateRow( ::std::max ( nFirstFreeScRow - 1, GetMaxPos().Row() ), true );
+}
+
+namespace {
+
+class RowFinalizeTask : public comphelper::ThreadTask
+{
+ bool mbProgress;
+ const ScfUInt16Vec& mrColXFIndexes;
+ size_t mnStartColAllDefault;
+ std::vector< XclExpRow * > maRows;
+public:
+ RowFinalizeTask( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
+ const ScfUInt16Vec& rColXFIndexes,
+ size_t nStartColAllDefault,
+ bool bProgress ) :
+ comphelper::ThreadTask( pTag ),
+ mbProgress( bProgress ),
+ mrColXFIndexes( rColXFIndexes ),
+ mnStartColAllDefault( nStartColAllDefault )
+ {}
+
+ void push_back( XclExpRow *pRow ) { maRows.push_back( pRow ); }
+ virtual void doWork() override
+ {
+ ScfUInt16Vec aXFIndexes( mrColXFIndexes.size(), EXC_XF_NOTFOUND );
+ for (XclExpRow* p : maRows)
+ p->Finalize( mrColXFIndexes, aXFIndexes, mnStartColAllDefault, mbProgress );
+ }
+};
+
+}
+
+void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData,
+ const ScfUInt16Vec& rColXFIndexes,
+ size_t nStartColAllDefault )
+{
+ // *** Finalize all rows *** ----------------------------------------------
+
+ GetProgressBar().ActivateFinalRowsSegment();
+
+#if 1
+ // This is staggeringly slow, and each element operates only
+ // on its own data.
+ const size_t nRows = maRowMap.size();
+ const size_t nThreads = nRows < 128 ? 1 : comphelper::ThreadPool::getPreferredConcurrency();
+#else
+ const size_t nThreads = 1; // globally disable multi-threading for now.
+#endif
+ if (nThreads == 1)
+ {
+ ScfUInt16Vec aXFIndexes( rColXFIndexes.size(), EXC_XF_NOTFOUND );
+ for (auto& rEntry : maRowMap)
+ rEntry.second->Finalize( rColXFIndexes, aXFIndexes, nStartColAllDefault, true );
+ }
+ else
+ {
+ comphelper::ThreadPool &rPool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+ std::vector<std::unique_ptr<RowFinalizeTask>> aTasks(nThreads);
+ for ( size_t i = 0; i < nThreads; i++ )
+ aTasks[ i ].reset( new RowFinalizeTask( pTag, rColXFIndexes, nStartColAllDefault, i == 0 ) );
+
+ size_t nIdx = 0;
+ for ( const auto& rEntry : maRowMap )
+ {
+ aTasks[ nIdx % nThreads ]->push_back( rEntry.second.get() );
+ ++nIdx;
+ }
+
+ for ( size_t i = 1; i < nThreads; i++ )
+ rPool.pushTask( std::move(aTasks[ i ]) );
+
+ // Progress bar updates must be synchronous to avoid deadlock
+ aTasks[0]->doWork();
+
+ rPool.waitUntilDone(pTag);
+ }
+
+ // *** Default row format *** ---------------------------------------------
+
+ std::map< XclExpDefaultRowData, size_t > aDefRowMap;
+
+ XclExpDefaultRowData aMaxDefData;
+ size_t nMaxDefCount = 0;
+ // only look for default format in existing rows, if there are more than unused
+ // if the row is hidden, then row xml must be created even if it not contain cells
+ XclExpRow* pPrev = nullptr;
+ std::vector< XclExpRow* > aRepeated;
+ for (const auto& rEntry : maRowMap)
+ {
+ const RowRef& rRow = rEntry.second;
+ if ( rRow->IsDefaultable() )
+ {
+ XclExpDefaultRowData aDefData( *rRow );
+ size_t& rnDefCount = aDefRowMap[ aDefData ];
+ ++rnDefCount;
+ if( rnDefCount > nMaxDefCount )
+ {
+ nMaxDefCount = rnDefCount;
+ aMaxDefData = aDefData;
+ }
+ }
+ if ( pPrev )
+ {
+ if ( pPrev->IsDefaultable() )
+ {
+ // if the previous row we processed is not
+ // defaultable then afaict the rows in between are
+ // not used ( and not repeatable )
+ sal_uInt32 nRpt = rRow->GetXclRow() - pPrev->GetXclRow();
+ if ( nRpt > 1 )
+ aRepeated.push_back( pPrev );
+ pPrev->SetXclRowRpt( nRpt );
+ XclExpDefaultRowData aDefData( *pPrev );
+ size_t& rnDefCount = aDefRowMap[ aDefData ];
+ rnDefCount += ( pPrev->GetXclRowRpt() - 1 );
+ if( rnDefCount > nMaxDefCount )
+ {
+ nMaxDefCount = rnDefCount;
+ aMaxDefData = aDefData;
+ }
+ }
+ }
+ pPrev = rRow.get();
+ }
+ // return the default row format to caller
+ rDefRowData = aMaxDefData;
+
+ // now disable repeating extra (empty) rows that are equal to the default row
+ for (auto& rpRow : aRepeated)
+ {
+ if ( rpRow->GetXclRowRpt() > 1
+ && rpRow->GetHeight() == rDefRowData.mnHeight
+ && rpRow->IsHidden() == rDefRowData.IsHidden() )
+ {
+ rpRow->SetXclRowRpt( 1 );
+ }
+ }
+
+ // *** Disable unused ROW records, find used area *** ---------------------
+
+ sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
+ sal_uInt16 nFirstFreeXclCol = 0;
+ sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
+ sal_uInt32 nFirstFreeXclRow = 0;
+
+ for (const auto& rEntry : maRowMap)
+ {
+ const RowRef& rRow = rEntry.second;
+ // disable unused rows
+ rRow->DisableIfDefault( aMaxDefData );
+
+ // find used column range
+ if( !rRow->IsEmpty() ) // empty rows return (0...0) as used range
+ {
+ nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, rRow->GetFirstUsedXclCol() );
+ nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, rRow->GetFirstFreeXclCol() );
+ }
+
+ // find used row range
+ if( rRow->IsEnabled() )
+ {
+ sal_uInt32 nXclRow = rRow->GetXclRow();
+ nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
+ nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
+ }
+ }
+
+ // adjust start position, if there are no or only empty/disabled ROW records
+ nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
+ nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
+
+ // initialize the DIMENSIONS record
+ maDimensions.SetDimensions(
+ nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
+}
+
+void XclExpRowBuffer::Save( XclExpStream& rStrm )
+{
+ // DIMENSIONS record
+ maDimensions.Save( rStrm );
+
+ // save in blocks of 32 rows, each block contains first all ROWs, then all cells
+ size_t nSize = maRowMap.size();
+ RowMap::iterator itr = maRowMap.begin(), itrEnd = maRowMap.end();
+ RowMap::iterator itrBlkStart = maRowMap.begin(), itrBlkEnd = maRowMap.begin();
+ sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : itr->second->GetXclRow();
+
+ for (; itr != itrEnd; ++itr)
+ {
+ // find end of row block
+ itrBlkEnd = std::find_if_not(itrBlkEnd, itrEnd,
+ [&nStartXclRow](const RowMap::value_type& rRow) { return rRow.second->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE; });
+
+ // write the ROW records
+ std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->Save( rStrm ); });
+
+ // write the cell records
+ std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->WriteCellList( rStrm ); });
+
+ itrBlkStart = (itrBlkEnd == itrEnd) ? itrBlkEnd : itrBlkEnd++;
+ nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
+ }
+}
+
+void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
+{
+ if (std::none_of(maRowMap.begin(), maRowMap.end(), [](const RowMap::value_type& rRow) { return rRow.second->IsEnabled(); }))
+ {
+ rStrm.GetCurrentStream()->singleElement(XML_sheetData);
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_sheetData);
+ for (const auto& rEntry : maRowMap)
+ rEntry.second->SaveXml(rStrm);
+ rWorksheet->endElement( XML_sheetData );
+}
+
+XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty )
+{
+ // This is called rather often, so optimize for the most common case of saving row by row
+ // (so the requested row is often the last one in the map or belongs after the last one).
+ RowMap::iterator itr;
+ if(maRowMap.empty())
+ itr = maRowMap.end();
+ else
+ {
+ RowMap::reverse_iterator last = maRowMap.rbegin();
+ if( last->first == nXclRow )
+ return *last->second;
+ if( nXclRow > last->first )
+ itr = maRowMap.end();
+ else
+ itr = maRowMap.lower_bound( nXclRow );
+ }
+ const bool bFound = itr != maRowMap.end();
+ // bFoundHigher: nXclRow was identical to the previous entry, so not explicitly created earlier
+ // coverity[deref_iterator : FALSE] - clearly itr if only derefed if bFound which checks for valid itr
+ const bool bFoundHigher = bFound && itr->first != nXclRow;
+ if( bFound && !bFoundHigher )
+ return *itr->second;
+
+ size_t nFrom = 0;
+ RowRef pPrevEntry;
+ if( itr != maRowMap.begin() )
+ {
+ --itr;
+ pPrevEntry = itr->second;
+ if( bFoundHigher )
+ nFrom = nXclRow;
+ else
+ nFrom = itr->first + 1;
+ }
+
+ const ScDocument& rDoc = GetRoot().GetDoc();
+ const SCTAB nScTab = GetRoot().GetCurrScTab();
+ // Do not repeatedly call RowHidden() / GetRowHeight() for same values.
+ bool bHidden = false;
+ SCROW lastSameHiddenRow = -1;
+ sal_uInt16 nHeight = 0;
+ SCROW lastSameHeightRow = -1;
+ // create the missing rows first
+ while( nFrom <= nXclRow )
+ {
+ // only create RowMap entries if it is first row in spreadsheet,
+ // if it is the desired row, or for rows that differ from previous.
+ if( static_cast<SCROW>(nFrom) > lastSameHiddenRow )
+ bHidden = rDoc.RowHidden(nFrom, nScTab, nullptr, &lastSameHiddenRow);
+ // Always get the actual row height even if the manual size flag is
+ // not set, to correctly export the heights of rows with wrapped
+ // texts.
+ if( static_cast<SCROW>(nFrom) > lastSameHeightRow )
+ nHeight = rDoc.GetRowHeight(nFrom, nScTab, nullptr, &lastSameHeightRow, false);
+ if ( !pPrevEntry || ( nFrom == nXclRow ) ||
+ ( maOutlineBfr.IsCollapsed() ) ||
+ ( maOutlineBfr.GetLevel() != 0 ) ||
+ ( bRowAlwaysEmpty && !pPrevEntry->IsEmpty() ) ||
+ ( bHidden != pPrevEntry->IsHidden() ) ||
+ ( nHeight != pPrevEntry->GetHeight() ) )
+ {
+ if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
+ {
+ mnHighestOutlineLevel = maOutlineBfr.GetLevel();
+ }
+ RowRef p = std::make_shared<XclExpRow>(GetRoot(), nFrom, maOutlineBfr, bRowAlwaysEmpty, bHidden, nHeight);
+ maRowMap.emplace(nFrom, p);
+ pPrevEntry = p;
+ }
+ ++nFrom;
+ }
+ itr = maRowMap.find(nXclRow);
+ return *itr->second;
+}
+
+// Cell Table
+
+XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ maColInfoBfr( rRoot ),
+ maRowBfr( rRoot ),
+ maArrayBfr( rRoot ),
+ maShrfmlaBfr( rRoot ),
+ maTableopBfr( rRoot ),
+ mxDefrowheight( new XclExpDefrowheight() ),
+ mxGuts( new XclExpGuts( rRoot ) ),
+ mxNoteList( new XclExpNoteList ),
+ mxMergedcells( new XclExpMergedcells( rRoot ) ),
+ mxHyperlinkList( new XclExpHyperlinkList ),
+ mxDval( new XclExpDval( rRoot ) ),
+ mxExtLst( new XclExtLst( rRoot ) )
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+ SvNumberFormatter& rFormatter = GetFormatter();
+
+ // maximum sheet limits
+ SCCOL nMaxScCol = GetMaxPos().Col();
+ SCROW nMaxScRow = GetMaxPos().Row();
+
+ // find used area (non-empty cells)
+ SCCOL nLastUsedScCol;
+ SCROW nLastUsedScRow;
+ rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
+
+ if(nLastUsedScCol > nMaxScCol)
+ nLastUsedScCol = nMaxScCol;
+
+ // check extra blank rows to avoid of losing their not default settings (workaround for tdf#41425)
+ nLastUsedScRow += 1000;
+
+ if(nLastUsedScRow > nMaxScRow)
+ nLastUsedScRow = nMaxScRow;
+
+ ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
+ GetAddressConverter().ValidateRange( aUsedRange, true );
+ nLastUsedScRow = aUsedRange.aEnd.Row();
+
+ // first row without any set attributes (height/hidden/...)
+ SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
+
+ // find range of outlines
+ SCROW nFirstUngroupedScRow = 0;
+ if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
+ {
+ SCCOLROW nScStartPos, nScEndPos;
+ const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
+ rRowArray.GetRange( nScStartPos, nScEndPos );
+ // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
+ nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
+ }
+
+ // column settings
+ /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
+ formatting cause big Excel files, because all rows from row 1 to row
+ 32000 are exported. Now, if the used area goes exactly to row 32000,
+ use this row as default and ignore all rows >32000.
+ #i59220# Tolerance of +-128 rows for inserted/removed rows. */
+ if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
+ nMaxScRow = nLastUsedScRow;
+ maColInfoBfr.Initialize( nMaxScRow );
+
+ // range for cell iterator
+ SCCOL nLastIterScCol = nMaxScCol;
+ SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow, nMaxScRow );
+ ScUsedAreaIterator aIt( rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
+
+ // activate the correct segment and sub segment at the progress bar
+ GetProgressBar().ActivateCreateRowsSegment();
+
+ for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
+ {
+ SCCOL nScCol = aIt.GetStartCol();
+ SCROW nScRow = aIt.GetRow();
+ SCCOL nLastScCol = aIt.GetEndCol();
+ ScAddress aScPos( nScCol, nScRow, nScTab );
+
+ XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt32 >( nScRow ) );
+ sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
+
+ const ScRefCellValue& rScCell = aIt.GetCell();
+ XclExpCellRef xCell;
+
+ const ScPatternAttr* pPattern = aIt.GetPattern();
+
+ // handle overlapped merged cells before creating the cell record
+ sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
+ bool bIsMergedBase = false;
+ if( pPattern )
+ {
+ const SfxItemSet& rItemSet = pPattern->GetItemSet();
+ // base cell in a merged range
+ const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
+ bIsMergedBase = rMergeItem.IsMerged();
+ /* overlapped cell in a merged range; in Excel all merged cells
+ must contain same XF index, for correct border */
+ const ScMergeFlagAttr& rMergeFlagItem = rItemSet.Get( ATTR_MERGE_FLAG );
+ if( rMergeFlagItem.IsOverlapped() )
+ nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
+ }
+
+ OUString aAddNoteText; // additional text to be appended to a note
+
+ switch (rScCell.meType)
+ {
+ case CELLTYPE_VALUE:
+ {
+ double fValue = rScCell.mfValue;
+
+ if (pPattern)
+ {
+ OUString aUrl = pPattern->GetItemSet().Get(ATTR_HYPERLINK).GetValue();
+ if (!aUrl.isEmpty())
+ {
+ rtl::Reference<XclExpHyperlink> aLink =
+ new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
+ mxHyperlinkList->AppendRecord(aLink);
+ }
+ }
+
+ // try to create a Boolean cell
+ if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
+ {
+ sal_uInt32 nScNumFmt = pPattern->GetItemSet().Get( ATTR_VALUE_FORMAT ).GetValue();
+ if( rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL )
+ xCell = new XclExpBooleanCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 );
+ }
+
+ // try to create an RK value (compressed floating-point number)
+ sal_Int32 nRkValue;
+ if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
+ xCell = new XclExpRkCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue );
+
+ // else: simple floating-point number cell
+ if( !xCell )
+ xCell = new XclExpNumberCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue );
+ }
+ break;
+
+ case CELLTYPE_STRING:
+ {
+ xCell = new XclExpLabelCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.mpString->getString());
+ }
+ break;
+
+ case CELLTYPE_EDIT:
+ {
+ XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
+ xCell = new XclExpLabelCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.mpEditText, aLinkHelper);
+
+ // add a single created HLINK record to the record list
+ if( aLinkHelper.HasLinkRecord() )
+ mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
+ // add list of multiple URLs to the additional cell note text
+ if( aLinkHelper.HasMultipleUrls() )
+ aAddNoteText = ScGlobal::addToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
+ }
+ break;
+
+ case CELLTYPE_FORMULA:
+ {
+ if (pPattern)
+ {
+ OUString aUrl = pPattern->GetItemSet().Get(ATTR_HYPERLINK).GetValue();
+ if (!aUrl.isEmpty())
+ {
+ rtl::Reference<XclExpHyperlink> aLink =
+ new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
+ mxHyperlinkList->AppendRecord(aLink);
+ }
+ }
+
+ xCell = new XclExpFormulaCell(
+ GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
+ *rScCell.mpFormula, maArrayBfr, maShrfmlaBfr, maTableopBfr);
+ }
+ break;
+
+ default:
+ OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
+ [[fallthrough]];
+ case CELLTYPE_NONE:
+ {
+ xCell = new XclExpBlankCell(
+ GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId );
+ }
+ break;
+ }
+
+ assert(xCell && "can only reach here with xCell set");
+
+ // insert the cell into the current row
+ maRowBfr.AppendCell( xCell, bIsMergedBase );
+
+ if ( !aAddNoteText.isEmpty() )
+ mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, nullptr, aAddNoteText ) );
+
+ // other sheet contents
+ if( pPattern )
+ {
+ const SfxItemSet& rItemSet = pPattern->GetItemSet();
+
+ // base cell in a merged range
+ if( bIsMergedBase )
+ {
+ const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
+ ScRange aScRange( aScPos );
+ aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
+ aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
+ sal_uInt32 nXFId = xCell->GetFirstXFId();
+ // blank cells merged vertically may occur repeatedly
+ OSL_ENSURE( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
+ "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
+ for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
+ {
+ mxMergedcells->AppendRange( aScRange, nXFId );
+ aScRange.aStart.IncCol();
+ aScRange.aEnd.IncCol();
+ }
+ }
+
+ // data validation
+ if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
+ {
+ sal_uLong nScHandle = rItemSet.Get( ATTR_VALIDDATA ).GetValue();
+ ScRange aScRange( aScPos );
+ aScRange.aEnd.SetCol( nLastScCol );
+ mxDval->InsertCellRange( aScRange, nScHandle );
+ }
+ }
+ }
+
+ // create missing row settings for rows anyhow flagged or with outlines
+ maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
+}
+
+void XclExpCellTable::Finalize(bool bXLS)
+{
+ // Finalize multiple operations.
+ maTableopBfr.Finalize();
+
+ /* Finalize column buffer. This calculates column default XF indexes from
+ the XF identifiers and fills a vector with these XF indexes. */
+ ScfUInt16Vec aColXFIndexes;
+ maColInfoBfr.Finalize( aColXFIndexes, bXLS );
+
+ // Usually many indexes towards the end will be EXC_XF_DEFAULTCELL, find
+ // the index that starts all EXC_XF_DEFAULTCELL until the end.
+ size_t nStartColAllDefault = findFirstAllSameUntilEnd( aColXFIndexes, EXC_XF_DEFAULTCELL );
+
+ /* Finalize row buffer. This calculates all cell XF indexes from the XF
+ identifiers. Then the XF index vector aColXFIndexes (filled above) is
+ used to calculate the row default formats. With this, all unneeded blank
+ cell records (equal to row default or column default) will be removed.
+ The function returns the (most used) default row format in aDefRowData. */
+ XclExpDefaultRowData aDefRowData;
+ maRowBfr.Finalize( aDefRowData, aColXFIndexes, nStartColAllDefault );
+
+ // Initialize the DEFROWHEIGHT record.
+ mxDefrowheight->SetDefaultData( aDefRowData );
+}
+
+XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
+{
+ XclExpRecordRef xRec;
+ switch( nRecId )
+ {
+ case EXC_ID3_DIMENSIONS: xRec = new XclExpDelegatingRecord( &const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ); break;
+ case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break;
+ case EXC_ID_GUTS: xRec = mxGuts; break;
+ case EXC_ID_NOTE: xRec = mxNoteList; break;
+ case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break;
+ case EXC_ID_HLINK: xRec = mxHyperlinkList; break;
+ case EXC_ID_DVAL: xRec = mxDval; break;
+ case EXC_ID_EXTLST: xRec = mxExtLst; break;
+ default: OSL_FAIL( "XclExpCellTable::CreateRecord - unknown record id" );
+ }
+ return xRec;
+}
+
+void XclExpCellTable::Save( XclExpStream& rStrm )
+{
+ // DEFCOLWIDTH and COLINFOs
+ maColInfoBfr.Save( rStrm );
+ // ROWs and cell records
+ maRowBfr.Save( rStrm );
+}
+
+void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
+{
+ // DEFAULT row height
+ XclExpDefaultRowData& rDefData = mxDefrowheight->GetDefaultData();
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement( XML_sheetFormatPr,
+ // OOXTODO: XML_baseColWidth
+ XML_defaultColWidth, OString::number(maColInfoBfr.GetDefColWidth()),
+ // OOXTODO: XML_customHeight
+ // OOXTODO: XML_thickTop
+ // OOXTODO: XML_thickBottom
+ XML_defaultRowHeight, OString::number(static_cast<double> (rDefData.mnHeight) / 20.0),
+ XML_zeroHeight, ToPsz( rDefData.IsHidden() ),
+ XML_outlineLevelRow, OString::number(maRowBfr.GetHighestOutlineLevel()),
+ XML_outlineLevelCol, OString::number(maColInfoBfr.GetHighestOutlineLevel()) );
+ rWorksheet->endElement( XML_sheetFormatPr );
+
+ maColInfoBfr.SaveXml( rStrm );
+ maRowBfr.SaveXml( rStrm );
+ mxExtLst->SaveXml( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xeview.cxx b/sc/source/filter/excel/xeview.cxx
new file mode 100644
index 000000000..d94a94407
--- /dev/null
+++ b/sc/source/filter/excel/xeview.cxx
@@ -0,0 +1,537 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xeview.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <viewopti.hxx>
+#include <xelink.hxx>
+#include <xestyle.hxx>
+#include <xehelper.hxx>
+#include <xltools.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/export/utils.hxx>
+
+using namespace ::oox;
+
+// Workbook view settings records =============================================
+
+XclExpWindow1::XclExpWindow1( const XclExpRoot& rRoot )
+ : XclExpRecord(EXC_ID_WINDOW1, 18)
+ , mnFlags(0)
+ , mnTabBarSize(600)
+{
+ const ScViewOptions& rViewOpt = rRoot.GetDoc().GetViewOptions();
+ ::set_flag( mnFlags, EXC_WIN1_HOR_SCROLLBAR, rViewOpt.GetOption( VOPT_HSCROLL ) );
+ ::set_flag( mnFlags, EXC_WIN1_VER_SCROLLBAR, rViewOpt.GetOption( VOPT_VSCROLL ) );
+ ::set_flag( mnFlags, EXC_WIN1_TABBAR, rViewOpt.GetOption( VOPT_TABCONTROLS ) );
+
+ double fTabBarWidth = rRoot.GetExtDocOptions().GetDocSettings().mfTabBarWidth;
+ if( (0.0 <= fTabBarWidth) && (fTabBarWidth <= 1.0) )
+ mnTabBarSize = static_cast< sal_uInt16 >( fTabBarWidth * 1000.0 + 0.5 );
+}
+
+void XclExpWindow1::SaveXml( XclExpXmlStream& rStrm )
+{
+ const XclExpTabInfo& rTabInfo = rStrm.GetRoot().GetTabInfo();
+
+ rStrm.GetCurrentStream()->singleElement( XML_workbookView,
+ // OOXTODO: XML_visibility, // ST_visibility
+ // OOXTODO: XML_minimized, // bool
+ XML_showHorizontalScroll, ToPsz( ::get_flag( mnFlags, EXC_WIN1_HOR_SCROLLBAR ) ),
+ XML_showVerticalScroll, ToPsz( ::get_flag( mnFlags, EXC_WIN1_VER_SCROLLBAR ) ),
+ XML_showSheetTabs, ToPsz( ::get_flag( mnFlags, EXC_WIN1_TABBAR ) ),
+ XML_xWindow, "0",
+ XML_yWindow, "0",
+ XML_windowWidth, OString::number(0x4000),
+ XML_windowHeight, OString::number(0x2000),
+ XML_tabRatio, OString::number(mnTabBarSize),
+ XML_firstSheet, OString::number(rTabInfo.GetFirstVisXclTab()),
+ XML_activeTab, OString::number(rTabInfo.GetDisplayedXclTab())
+ // OOXTODO: XML_autoFilterDateGrouping, // bool; AUTOFILTER12? 87Eh
+ );
+}
+
+void XclExpWindow1::WriteBody( XclExpStream& rStrm )
+{
+ const XclExpTabInfo& rTabInfo = rStrm.GetRoot().GetTabInfo();
+
+ rStrm << sal_uInt16( 0 ) // X position of the window
+ << sal_uInt16( 0 ) // Y position of the window
+ << sal_uInt16( 0x4000 ) // width of the window
+ << sal_uInt16( 0x2000 ) // height of the window
+ << mnFlags
+ << rTabInfo.GetDisplayedXclTab()
+ << rTabInfo.GetFirstVisXclTab()
+ << rTabInfo.GetXclSelectedCount()
+ << mnTabBarSize;
+}
+
+// Sheet view settings records ================================================
+
+XclExpWindow2::XclExpWindow2( const XclExpRoot& rRoot,
+ const XclTabViewData& rData, sal_uInt32 nGridColorId ) :
+ XclExpRecord( EXC_ID_WINDOW2, (rRoot.GetBiff() == EXC_BIFF8) ? 18 : 10 ),
+ maGridColor( rData.maGridColor ),
+ mnGridColorId( nGridColorId ),
+ mnFlags( 0 ),
+ maFirstXclPos( rData.maFirstXclPos ),
+ mnNormalZoom( rData.mnNormalZoom ),
+ mnPageZoom( rData.mnPageZoom )
+{
+ ::set_flag( mnFlags, EXC_WIN2_SHOWFORMULAS, rData.mbShowFormulas );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWGRID, rData.mbShowGrid );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWHEADINGS, rData.mbShowHeadings );
+ ::set_flag( mnFlags, EXC_WIN2_FROZEN, rData.mbFrozenPanes );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWZEROS, rData.mbShowZeros );
+ ::set_flag( mnFlags, EXC_WIN2_DEFGRIDCOLOR, rData.mbDefGridColor );
+ ::set_flag( mnFlags, EXC_WIN2_MIRRORED, rData.mbMirrored );
+ ::set_flag( mnFlags, EXC_WIN2_SHOWOUTLINE, rData.mbShowOutline );
+ ::set_flag( mnFlags, EXC_WIN2_FROZENNOSPLIT, rData.mbFrozenPanes );
+ ::set_flag( mnFlags, EXC_WIN2_SELECTED, rData.mbSelected );
+ ::set_flag( mnFlags, EXC_WIN2_DISPLAYED, rData.mbDisplayed );
+ ::set_flag( mnFlags, EXC_WIN2_PAGEBREAKMODE, rData.mbPageMode );
+}
+
+void XclExpWindow2::WriteBody( XclExpStream& rStrm )
+{
+ const XclExpRoot& rRoot = rStrm.GetRoot();
+
+ rStrm << mnFlags
+ << maFirstXclPos;
+
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ rStrm << maGridColor;
+ break;
+ case EXC_BIFF8:
+ rStrm << rRoot.GetPalette().GetColorIndex( mnGridColorId )
+ << sal_uInt16( 0 )
+ << mnPageZoom
+ << mnNormalZoom
+ << sal_uInt32( 0 );
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+XclExpScl::XclExpScl( sal_uInt16 nZoom ) :
+ XclExpRecord( EXC_ID_SCL, 4 ),
+ mnNum( nZoom ),
+ mnDenom( 100 )
+{
+ Shorten( 2 );
+ Shorten( 5 );
+}
+
+void XclExpScl::Shorten( sal_uInt16 nFactor )
+{
+ while( (mnNum % nFactor == 0) && (mnDenom % nFactor == 0) )
+ {
+ mnNum = mnNum / nFactor;
+ mnDenom = mnDenom / nFactor;
+ }
+}
+
+void XclExpScl::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF4 );
+ rStrm << mnNum << mnDenom;
+}
+
+XclExpPane::XclExpPane( const XclTabViewData& rData ) :
+ XclExpRecord( EXC_ID_PANE, 10 ),
+ mnSplitX( rData.mnSplitX ),
+ mnSplitY( rData.mnSplitY ),
+ maSecondXclPos( rData.maSecondXclPos ),
+ mnActivePane( rData.mnActivePane ),
+ mbFrozenPanes( rData.mbFrozenPanes )
+{
+ OSL_ENSURE( rData.IsSplit(), "XclExpPane::XclExpPane - no PANE record for unsplit view" );
+}
+
+static const char* lcl_GetActivePane( sal_uInt8 nActivePane )
+{
+ switch( nActivePane )
+ {
+ case EXC_PANE_TOPLEFT: return "topLeft";
+ case EXC_PANE_TOPRIGHT: return "topRight";
+ case EXC_PANE_BOTTOMLEFT: return "bottomLeft";
+ case EXC_PANE_BOTTOMRIGHT: return "bottomRight";
+ }
+ return "**error: lcl_GetActivePane";
+}
+
+void XclExpPane::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElement( XML_pane,
+ XML_xSplit, OString::number(mnSplitX),
+ XML_ySplit, OString::number(mnSplitY),
+ XML_topLeftCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maSecondXclPos ).getStr(),
+ XML_activePane, lcl_GetActivePane( mnActivePane ),
+ XML_state, mbFrozenPanes ? "frozen" : "split" );
+}
+
+void XclExpPane::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnSplitX
+ << static_cast<sal_uInt16>( mnSplitY )
+ << maSecondXclPos
+ << mnActivePane;
+ if( rStrm.GetRoot().GetBiff() >= EXC_BIFF5 )
+ rStrm << sal_uInt8( 0 );
+}
+
+XclExpSelection::XclExpSelection( const XclTabViewData& rData, sal_uInt8 nPane ) :
+ XclExpRecord( EXC_ID_SELECTION, 15 ),
+ mnPane( nPane )
+{
+ if( const XclSelectionData* pSelData = rData.GetSelectionData( nPane ) )
+ maSelData = *pSelData;
+
+ // find the cursor position in the selection list (or add it)
+ XclRangeList& rXclSel = maSelData.maXclSelection;
+ auto aIt = std::find_if(rXclSel.begin(), rXclSel.end(),
+ [this](const XclRange& rRange) { return rRange.Contains(maSelData.maXclCursor); });
+ if (aIt != rXclSel.end())
+ {
+ maSelData.mnCursorIdx = static_cast< sal_uInt16 >( std::distance(rXclSel.begin(), aIt) );
+ }
+ else
+ {
+ /* Cursor cell not found in list? (e.g. inactive pane, or removed in
+ ConvertRangeList(), because Calc cursor on invalid pos)
+ -> insert the valid Excel cursor. */
+ maSelData.mnCursorIdx = static_cast< sal_uInt16 >( rXclSel.size() );
+ rXclSel.push_back( XclRange( maSelData.maXclCursor ) );
+ }
+}
+
+void XclExpSelection::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.GetCurrentStream()->singleElement( XML_selection,
+ XML_pane, lcl_GetActivePane( mnPane ),
+ XML_activeCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maSelData.maXclCursor ).getStr(),
+ XML_activeCellId, OString::number(maSelData.mnCursorIdx),
+ XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), maSelData.maXclSelection) );
+}
+
+void XclExpSelection::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << mnPane // pane for this selection
+ << maSelData.maXclCursor // cell cursor
+ << maSelData.mnCursorIdx; // index to range containing cursor
+ maSelData.maXclSelection.Write( rStrm, false );
+}
+
+XclExpTabBgColor::XclExpTabBgColor( const XclTabViewData& rTabViewData ) :
+ XclExpRecord( EXC_ID_SHEETEXT, 18 ),
+ mrTabViewData( rTabViewData )
+{
+}
+//TODO Fix savexml...
+/*void XclExpTabBgColor::SaveXml( XclExpXmlStream& rStrm )
+{
+}*/
+
+void XclExpTabBgColor::WriteBody( XclExpStream& rStrm )
+{
+ if ( mrTabViewData.IsDefaultTabBgColor() )
+ return;
+ sal_uInt16 const rt = 0x0862; //rt
+ sal_uInt16 const grbitFrt = 0x0000; //grbit must be set to 0
+ sal_uInt32 unused = 0x00000000; //Use twice...
+ sal_uInt32 const cb = 0x00000014; // Record Size, may be larger in future...
+ sal_uInt16 const reserved = 0x0000; //trailing bits are 0
+ sal_uInt16 TabBgColorIndex;
+ XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
+ TabBgColorIndex = rPal.GetColorIndex(mrTabViewData.mnTabBgColorId);
+ if (TabBgColorIndex < 8 || TabBgColorIndex > 63 ) // only numbers 8 - 63 are valid numbers
+ TabBgColorIndex = 127; //Excel specs: 127 makes excel ignore tab color information.
+ rStrm << rt << grbitFrt << unused << unused << cb << TabBgColorIndex << reserved;
+}
+
+// Sheet view settings ========================================================
+
+namespace {
+
+/** Converts a Calc zoom factor into an Excel zoom factor. Returns 0 for a default zoom value. */
+sal_uInt16 lclGetXclZoom( tools::Long nScZoom, sal_uInt16 nDefXclZoom )
+{
+ sal_uInt16 nXclZoom = limit_cast< sal_uInt16 >( nScZoom, EXC_ZOOM_MIN, EXC_ZOOM_MAX );
+ return (nXclZoom == nDefXclZoom) ? 0 : nXclZoom;
+}
+
+} // namespace
+
+XclExpTabViewSettings::XclExpTabViewSettings( const XclExpRoot& rRoot, SCTAB nScTab ) :
+ XclExpRoot( rRoot ),
+ mnGridColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ) ),
+ mbHasTabSettings(false)
+{
+ // *** sheet flags ***
+
+ const XclExpTabInfo& rTabInfo = GetTabInfo();
+ maData.mbSelected = rTabInfo.IsSelectedTab( nScTab );
+ maData.mbDisplayed = rTabInfo.IsDisplayedTab( nScTab );
+ maData.mbMirrored = rTabInfo.IsMirroredTab( nScTab );
+
+ const ScViewOptions& rViewOpt = GetDoc().GetViewOptions();
+ maData.mbShowFormulas = rViewOpt.GetOption( VOPT_FORMULAS );
+ maData.mbShowHeadings = rViewOpt.GetOption( VOPT_HEADER );
+ maData.mbShowZeros = rViewOpt.GetOption( VOPT_NULLVALS );
+ maData.mbShowOutline = rViewOpt.GetOption( VOPT_OUTLINER );
+
+ // *** sheet options: cursor, selection, splits, grid color, zoom ***
+
+ if( const ScExtTabSettings* pTabSett = GetExtDocOptions().GetTabSettings( nScTab ) )
+ {
+ mbHasTabSettings = true;
+ const ScExtTabSettings& rTabSett = *pTabSett;
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+
+ // first visible cell in top-left pane
+ if( (rTabSett.maFirstVis.Col() >= 0) && (rTabSett.maFirstVis.Row() >= 0) )
+ maData.maFirstXclPos = rAddrConv.CreateValidAddress( rTabSett.maFirstVis, false );
+
+ // first visible cell in additional pane(s)
+ if( (rTabSett.maSecondVis.Col() >= 0) && (rTabSett.maSecondVis.Row() >= 0) )
+ maData.maSecondXclPos = rAddrConv.CreateValidAddress( rTabSett.maSecondVis, false );
+
+ // active pane
+ switch( rTabSett.meActivePane )
+ {
+ case SCEXT_PANE_TOPLEFT: maData.mnActivePane = EXC_PANE_TOPLEFT; break;
+ case SCEXT_PANE_TOPRIGHT: maData.mnActivePane = EXC_PANE_TOPRIGHT; break;
+ case SCEXT_PANE_BOTTOMLEFT: maData.mnActivePane = EXC_PANE_BOTTOMLEFT; break;
+ case SCEXT_PANE_BOTTOMRIGHT: maData.mnActivePane = EXC_PANE_BOTTOMRIGHT; break;
+ }
+
+ // freeze/split position
+ maData.mbFrozenPanes = rTabSett.mbFrozenPanes;
+ if( maData.mbFrozenPanes )
+ {
+ /* Frozen panes: handle split position as row/column positions.
+ #i35812# Excel uses number of visible rows/columns, Calc uses position of freeze. */
+ SCCOL nFreezeScCol = rTabSett.maFreezePos.Col();
+ if( (0 < nFreezeScCol) && (nFreezeScCol <= GetXclMaxPos().Col()) )
+ maData.mnSplitX = static_cast< sal_uInt16 >( nFreezeScCol ) - maData.maFirstXclPos.mnCol;
+ SCROW nFreezeScRow = rTabSett.maFreezePos.Row();
+ if( (0 < nFreezeScRow) && (nFreezeScRow <= GetXclMaxPos().Row()) )
+ maData.mnSplitY = static_cast< sal_uInt32 >( nFreezeScRow ) - maData.maFirstXclPos.mnRow;
+ // if both splits are left out (address overflow), remove the frozen flag
+ maData.mbFrozenPanes = maData.IsSplit();
+
+ // #i20671# frozen panes: mostright/mostbottom pane is active regardless of cursor position
+ if( maData.HasPane( EXC_PANE_BOTTOMRIGHT ) )
+ maData.mnActivePane = EXC_PANE_BOTTOMRIGHT;
+ else if( maData.HasPane( EXC_PANE_TOPRIGHT ) )
+ maData.mnActivePane = EXC_PANE_TOPRIGHT;
+ else if( maData.HasPane( EXC_PANE_BOTTOMLEFT ) )
+ maData.mnActivePane = EXC_PANE_BOTTOMLEFT;
+ }
+ else
+ {
+ // split window: position is in twips
+ maData.mnSplitX = static_cast<sal_uInt16>(rTabSett.maSplitPos.X());
+ maData.mnSplitY = static_cast<sal_uInt32>(rTabSett.maSplitPos.Y());
+ }
+
+ // selection
+ CreateSelectionData( EXC_PANE_TOPLEFT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_TOPRIGHT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_BOTTOMLEFT, rTabSett.maCursor, rTabSett.maSelection );
+ CreateSelectionData( EXC_PANE_BOTTOMRIGHT, rTabSett.maCursor, rTabSett.maSelection );
+
+ // grid color
+ const Color& rGridColor = rTabSett.maGridColor;
+ maData.mbDefGridColor = rGridColor == COL_AUTO;
+ if( !maData.mbDefGridColor )
+ {
+ if( GetBiff() == EXC_BIFF8 )
+ mnGridColorId = GetPalette().InsertColor( rGridColor, EXC_COLOR_GRID );
+ else
+ maData.maGridColor = rGridColor;
+ }
+ maData.mbShowGrid = rTabSett.mbShowGrid;
+
+ // view mode and zoom
+ maData.mbPageMode = (GetBiff() == EXC_BIFF8) && rTabSett.mbPageMode;
+ maData.mnNormalZoom = lclGetXclZoom( rTabSett.mnNormalZoom, EXC_WIN2_NORMALZOOM_DEF );
+ maData.mnPageZoom = lclGetXclZoom( rTabSett.mnPageZoom, EXC_WIN2_PAGEZOOM_DEF );
+ maData.mnCurrentZoom = maData.mbPageMode ? maData.mnPageZoom : maData.mnNormalZoom;
+ }
+
+ // Tab Bg Color
+ if ( GetBiff() == EXC_BIFF8 && !GetDoc().IsDefaultTabBgColor(nScTab) )
+ {
+ XclExpPalette& rPal = GetPalette();
+ maData.maTabBgColor = GetDoc().GetTabBgColor(nScTab);
+ maData.mnTabBgColorId = rPal.InsertColor(maData.maTabBgColor, EXC_COLOR_TABBG, EXC_COLOR_NOTABBG );
+ }
+}
+
+void XclExpTabViewSettings::Save( XclExpStream& rStrm )
+{
+ WriteWindow2( rStrm );
+ WriteScl( rStrm );
+ WritePane( rStrm );
+ WriteSelection( rStrm, EXC_PANE_TOPLEFT );
+ WriteSelection( rStrm, EXC_PANE_TOPRIGHT );
+ WriteSelection( rStrm, EXC_PANE_BOTTOMLEFT );
+ WriteSelection( rStrm, EXC_PANE_BOTTOMRIGHT );
+ WriteTabBgColor( rStrm );
+}
+
+static void lcl_WriteSelection( XclExpXmlStream& rStrm, const XclTabViewData& rData, sal_uInt8 nPane )
+{
+ if( rData.HasPane( nPane ) )
+ XclExpSelection( rData, nPane ).SaveXml( rStrm );
+}
+
+static OString lcl_GetZoom( sal_uInt16 nZoom )
+{
+ if( nZoom )
+ return OString::number( nZoom );
+ return "100";
+}
+
+void XclExpTabViewSettings::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+ rWorksheet->startElement(XML_sheetViews);
+
+ // handle missing viewdata at embedded XLSX OLE objects
+ if( !mbHasTabSettings && maData.mbSelected )
+ {
+ SCCOL nPosLeft = rStrm.GetRoot().GetDoc().GetPosLeft();
+ SCROW nPosTop = rStrm.GetRoot().GetDoc().GetPosTop();
+ if (nPosLeft > 0 || nPosTop > 0)
+ {
+ ScAddress aLeftTop(nPosLeft, nPosTop, 0);
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ maData.maFirstXclPos = rAddrConv.CreateValidAddress( aLeftTop, false );
+ }
+ }
+
+ rWorksheet->startElement( XML_sheetView,
+ // OOXTODO: XML_windowProtection,
+ XML_showFormulas, ToPsz( maData.mbShowFormulas ),
+ XML_showGridLines, ToPsz( maData.mbShowGrid ),
+ XML_showRowColHeaders, ToPsz( maData.mbShowHeadings ),
+ XML_showZeros, ToPsz( maData.mbShowZeros ),
+ XML_rightToLeft, ToPsz( maData.mbMirrored ),
+ XML_tabSelected, ToPsz( maData.mbSelected ),
+ // OOXTODO: XML_showRuler,
+ XML_showOutlineSymbols, ToPsz( maData.mbShowOutline ),
+ XML_defaultGridColor, mnGridColorId == XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT ) ? "true" : "false",
+ // OOXTODO: XML_showWhiteSpace,
+ XML_view, maData.mbPageMode ? "pageBreakPreview" : "normal", // OOXTODO: pageLayout
+ XML_topLeftCell, XclXmlUtils::ToOString( rStrm.GetRoot().GetStringBuf(), maData.maFirstXclPos ).getStr(),
+ XML_colorId, OString::number(rStrm.GetRoot().GetPalette().GetColorIndex(mnGridColorId)),
+ XML_zoomScale, lcl_GetZoom(maData.mnCurrentZoom),
+ XML_zoomScaleNormal, lcl_GetZoom(maData.mnNormalZoom),
+ // OOXTODO: XML_zoomScaleSheetLayoutView,
+ XML_zoomScalePageLayoutView, lcl_GetZoom(maData.mnPageZoom),
+ XML_workbookViewId, "0" // OOXTODO? 0-based index of document(xl/workbook.xml)/workbook/bookviews/workbookView
+ // should always be 0, as we only generate 1 such element.
+ );
+ if( maData.IsSplit() )
+ {
+ XclExpPane aPane( maData );
+ aPane.SaveXml( rStrm );
+ }
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_TOPLEFT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_TOPRIGHT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_BOTTOMLEFT );
+ lcl_WriteSelection( rStrm, maData, EXC_PANE_BOTTOMRIGHT );
+ rWorksheet->endElement( XML_sheetView );
+ // OOXTODO: XML_extLst
+ rWorksheet->endElement( XML_sheetViews );
+}
+
+// private --------------------------------------------------------------------
+
+void XclExpTabViewSettings::CreateSelectionData( sal_uInt8 nPane,
+ const ScAddress& rCursor, const ScRangeList& rSelection )
+{
+ if( !maData.HasPane( nPane ) )
+ return;
+
+ XclSelectionData& rSelData = maData.CreateSelectionData( nPane );
+
+ // first step: use top-left visible cell as cursor
+ rSelData.maXclCursor.mnCol = ((nPane == EXC_PANE_TOPLEFT) || (nPane == EXC_PANE_BOTTOMLEFT)) ?
+ maData.maFirstXclPos.mnCol : maData.maSecondXclPos.mnCol;
+ rSelData.maXclCursor.mnRow = ((nPane == EXC_PANE_TOPLEFT) || (nPane == EXC_PANE_TOPRIGHT)) ?
+ maData.maFirstXclPos.mnRow : maData.maSecondXclPos.mnRow;
+
+ // second step, active pane: create actual selection data with current cursor position
+ if( nPane == maData.mnActivePane )
+ {
+ XclExpAddressConverter& rAddrConv = GetAddressConverter();
+ // cursor position (keep top-left pane position from above, if rCursor is invalid)
+ if( (rCursor.Col() >= 0) && (rCursor.Row() >= 0) )
+ rSelData.maXclCursor = rAddrConv.CreateValidAddress( rCursor, false );
+ // selection
+ rAddrConv.ConvertRangeList( rSelData.maXclSelection, rSelection, false );
+ }
+}
+
+void XclExpTabViewSettings::WriteWindow2( XclExpStream& rStrm ) const
+{
+// #i43553# GCC 3.3 parse error
+// XclExpWindow2( GetRoot(), maData, mnGridColorId ).Save( rStrm );
+ XclExpWindow2 aWindow2( GetRoot(), maData, mnGridColorId );
+ aWindow2.Save( rStrm );
+}
+
+void XclExpTabViewSettings::WriteScl( XclExpStream& rStrm ) const
+{
+ if( maData.mnCurrentZoom != 0 )
+ XclExpScl( maData.mnCurrentZoom ).Save( rStrm );
+}
+
+void XclExpTabViewSettings::WritePane( XclExpStream& rStrm ) const
+{
+ if( maData.IsSplit() )
+// #i43553# GCC 3.3 parse error
+// XclExpPane( GetRoot(), maData ).Save( rStrm );
+ {
+ XclExpPane aPane( maData );
+ aPane.Save( rStrm );
+ }
+}
+
+void XclExpTabViewSettings::WriteSelection( XclExpStream& rStrm, sal_uInt8 nPane ) const
+{
+ if( maData.HasPane( nPane ) )
+ XclExpSelection( maData, nPane ).Save( rStrm );
+}
+
+void XclExpTabViewSettings::WriteTabBgColor( XclExpStream& rStrm ) const
+{
+ if ( !maData.IsDefaultTabBgColor() )
+ XclExpTabBgColor( maData ).Save( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xichart.cxx b/sc/source/filter/excel/xichart.cxx
new file mode 100644
index 000000000..dc14076b7
--- /dev/null
+++ b/sc/source/filter/excel/xichart.cxx
@@ -0,0 +1,4415 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xichart.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp>
+#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
+#include <com/sun/star/chart/ChartAxisMarkPosition.hpp>
+#include <com/sun/star/chart/ChartAxisPosition.hpp>
+#include <com/sun/star/chart/ChartLegendExpansion.hpp>
+#include <com/sun/star/chart/TimeInterval.hpp>
+#include <com/sun/star/chart/TimeUnit.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/XDiagramPositioning.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/ErrorBarStyle.hpp>
+#include <com/sun/star/chart/MissingValueTreatment.hpp>
+#include <com/sun/star/chart2/LinearRegressionCurve.hpp>
+#include <com/sun/star/chart2/ExponentialRegressionCurve.hpp>
+#include <com/sun/star/chart2/LogarithmicRegressionCurve.hpp>
+#include <com/sun/star/chart2/PotentialRegressionCurve.hpp>
+#include <com/sun/star/chart2/PolynomialRegressionCurve.hpp>
+#include <com/sun/star/chart2/MovingAverageRegressionCurve.hpp>
+#include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
+#include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
+#include <com/sun/star/chart2/FormattedString.hpp>
+#include <com/sun/star/chart2/LogarithmicScaling.hpp>
+#include <com/sun/star/chart2/LinearScaling.hpp>
+#include <com/sun/star/chart2/PolarCoordinateSystem2d.hpp>
+#include <com/sun/star/chart2/PolarCoordinateSystem3d.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/XDiagram.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
+#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
+#include <com/sun/star/chart2/XTitled.hpp>
+#include <com/sun/star/chart2/AxisType.hpp>
+#include <com/sun/star/chart2/CurveStyle.hpp>
+#include <com/sun/star/chart2/DataPointGeometry3D.hpp>
+#include <com/sun/star/chart2/DataPointLabel.hpp>
+#include <com/sun/star/chart2/LegendPosition.hpp>
+#include <com/sun/star/chart2/StackingDirection.hpp>
+#include <com/sun/star/chart2/TickmarkStyle.hpp>
+#include <com/sun/star/chart2/RelativePosition.hpp>
+#include <com/sun/star/chart2/RelativeSize.hpp>
+#include <com/sun/star/chart2/data/XDataProvider.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/chart2/data/XDataSink.hpp>
+#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/numeric.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unoapi.hxx>
+#include <sal/log.hxx>
+#include <tools/helpers.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <tokenarray.hxx>
+#include <compiler.hxx>
+#include <reftokenhelper.hxx>
+#include <chartlis.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xistream.hxx>
+#include <xiformula.hxx>
+#include <xistyle.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::util::XNumberFormatsSupplier;
+using ::com::sun::star::drawing::XDrawPage;
+using ::com::sun::star::drawing::XDrawPageSupplier;
+using ::com::sun::star::drawing::XShape;
+
+using namespace ::com::sun::star::chart2;
+
+using ::com::sun::star::chart2::data::XDataProvider;
+using ::com::sun::star::chart2::data::XDataReceiver;
+using ::com::sun::star::chart2::data::XDataSequence;
+using ::com::sun::star::chart2::data::XDataSink;
+using ::com::sun::star::chart2::data::XLabeledDataSequence;
+using ::com::sun::star::chart2::data::LabeledDataSequence;
+
+using ::formula::FormulaToken;
+using ::formula::FormulaTokenArrayPlainIterator;
+using ::std::unique_ptr;
+
+namespace cssc = ::com::sun::star::chart;
+namespace cssc2 = ::com::sun::star::chart2;
+
+// Helpers ====================================================================
+
+namespace {
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclChRectangle& rRect )
+{
+ rRect.mnX = rStrm.ReadInt32();
+ rRect.mnY = rStrm.ReadInt32();
+ rRect.mnWidth = rStrm.ReadInt32();
+ rRect.mnHeight = rStrm.ReadInt32();
+ return rStrm;
+}
+
+void lclSetValueOrClearAny( Any& rAny, double fValue, bool bClear )
+{
+ if( bClear )
+ rAny.clear();
+ else
+ rAny <<= fValue;
+}
+
+void lclSetExpValueOrClearAny( Any& rAny, double fValue, bool bLogScale, bool bClear )
+{
+ if( !bClear && bLogScale )
+ fValue = pow( 10.0, fValue );
+ lclSetValueOrClearAny( rAny, fValue, bClear );
+}
+
+double lclGetSerialDay( const XclImpRoot& rRoot, sal_uInt16 nValue, sal_uInt16 nTimeUnit )
+{
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS:
+ return nValue;
+ case EXC_CHDATERANGE_MONTHS:
+ return rRoot.GetDoubleFromDateTime( Date( 1, static_cast< sal_uInt16 >( 1 + nValue % 12 ), static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue / 12 ) ) );
+ case EXC_CHDATERANGE_YEARS:
+ return rRoot.GetDoubleFromDateTime( Date( 1, 1, static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue ) ) );
+ default:
+ OSL_ENSURE( false, "lclGetSerialDay - unexpected time unit" );
+ }
+ return nValue;
+}
+
+void lclConvertTimeValue( const XclImpRoot& rRoot, Any& rAny, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
+{
+ if( bAuto )
+ rAny.clear();
+ else
+ rAny <<= lclGetSerialDay( rRoot, nValue, nTimeUnit );
+}
+
+sal_Int32 lclGetApiTimeUnit( sal_uInt16 nTimeUnit )
+{
+ switch( nTimeUnit )
+ {
+ case EXC_CHDATERANGE_DAYS: return cssc::TimeUnit::DAY;
+ case EXC_CHDATERANGE_MONTHS: return cssc::TimeUnit::MONTH;
+ case EXC_CHDATERANGE_YEARS: return cssc::TimeUnit::YEAR;
+ default: OSL_ENSURE( false, "lclGetApiTimeUnit - unexpected time unit" );
+ }
+ return cssc::TimeUnit::DAY;
+}
+
+void lclConvertTimeInterval( Any& rInterval, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
+{
+ if( bAuto || (nValue == 0) )
+ rInterval.clear();
+ else
+ rInterval <<= cssc::TimeInterval( nValue, lclGetApiTimeUnit( nTimeUnit ) );
+}
+
+} // namespace
+
+// Common =====================================================================
+
+/** Stores global data needed in various classes of the Chart import filter. */
+struct XclImpChRootData : public XclChRootData
+{
+ XclImpChChart& mrChartData; /// The chart data object.
+
+ explicit XclImpChRootData( XclImpChChart& rChartData ) : mrChartData( rChartData ) {}
+};
+
+XclImpChRoot::XclImpChRoot( const XclImpRoot& rRoot, XclImpChChart& rChartData ) :
+ XclImpRoot( rRoot ),
+ mxChData( std::make_shared<XclImpChRootData>( rChartData ) )
+{
+}
+
+XclImpChRoot::~XclImpChRoot()
+{
+}
+
+XclImpChChart& XclImpChRoot::GetChartData() const
+{
+ return mxChData->mrChartData;
+}
+
+const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( XclChTypeId eType ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
+}
+
+const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( sal_uInt16 nRecId ) const
+{
+ return mxChData->mxTypeInfoProv->GetTypeInfoFromRecId( nRecId );
+}
+
+const XclChFormatInfo& XclImpChRoot::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
+}
+
+Color XclImpChRoot::GetFontAutoColor() const
+{
+ return GetPalette().GetColor( EXC_COLOR_CHWINDOWTEXT );
+}
+
+Color XclImpChRoot::GetSeriesLineAutoColor( sal_uInt16 nFormatIdx ) const
+{
+ return GetPalette().GetColor( XclChartHelper::GetSeriesLineAutoColorIdx( nFormatIdx ) );
+}
+
+Color XclImpChRoot::GetSeriesFillAutoColor( sal_uInt16 nFormatIdx ) const
+{
+ const XclImpPalette& rPal = GetPalette();
+ Color aColor = rPal.GetColor( XclChartHelper::GetSeriesFillAutoColorIdx( nFormatIdx ) );
+ sal_uInt8 nTrans = XclChartHelper::GetSeriesFillAutoTransp( nFormatIdx );
+ return ScfTools::GetMixedColor( aColor, rPal.GetColor( EXC_COLOR_CHWINDOWBACK ), nTrans );
+}
+
+void XclImpChRoot::InitConversion( const Reference<XChartDocument>& xChartDoc, const tools::Rectangle& rChartRect ) const
+{
+ // create formatting object tables
+ mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
+
+ // lock the model to suppress any internal updates
+ if( xChartDoc.is() )
+ xChartDoc->lockControllers();
+
+ SfxObjectShell* pDocShell = GetDocShell();
+ Reference< XDataReceiver > xDataRec( xChartDoc, UNO_QUERY );
+ if( pDocShell && xDataRec.is() )
+ {
+ // create and register a data provider
+ Reference< XDataProvider > xDataProv(
+ ScfApiHelper::CreateInstance( pDocShell, SERVICE_CHART2_DATAPROVIDER ), UNO_QUERY );
+ if( xDataProv.is() )
+ xDataRec->attachDataProvider( xDataProv );
+ // attach the number formatter
+ Reference< XNumberFormatsSupplier > xNumFmtSupp( pDocShell->GetModel(), UNO_QUERY );
+ if( xNumFmtSupp.is() )
+ xDataRec->attachNumberFormatsSupplier( xNumFmtSupp );
+ }
+}
+
+void XclImpChRoot::FinishConversion( XclImpDffConverter& rDffConv ) const
+{
+ rDffConv.Progress( EXC_CHART_PROGRESS_SIZE );
+ // unlock the model
+ Reference< XModel > xModel = mxChData->mxChartDoc;
+ if( xModel.is() )
+ xModel->unlockControllers();
+ rDffConv.Progress( EXC_CHART_PROGRESS_SIZE );
+
+ mxChData->FinishConversion();
+}
+
+Reference< XDataProvider > XclImpChRoot::GetDataProvider() const
+{
+ return mxChData->mxChartDoc->getDataProvider();
+}
+
+Reference< XShape > XclImpChRoot::GetTitleShape( const XclChTextKey& rTitleKey ) const
+{
+ return mxChData->GetTitleShape( rTitleKey );
+}
+
+sal_Int32 XclImpChRoot::CalcHmmFromChartX( sal_Int32 nPosX ) const
+{
+ return static_cast< sal_Int32 >( mxChData->mfUnitSizeX * nPosX + mxChData->mnBorderGapX + 0.5 );
+}
+
+sal_Int32 XclImpChRoot::CalcHmmFromChartY( sal_Int32 nPosY ) const
+{
+ return static_cast< sal_Int32 >( mxChData->mfUnitSizeY * nPosY + mxChData->mnBorderGapY + 0.5 );
+}
+
+css::awt::Rectangle XclImpChRoot::CalcHmmFromChartRect( const XclChRectangle& rRect ) const
+{
+ return css::awt::Rectangle(
+ CalcHmmFromChartX( rRect.mnX ),
+ CalcHmmFromChartY( rRect.mnY ),
+ CalcHmmFromChartX( rRect.mnWidth ),
+ CalcHmmFromChartY( rRect.mnHeight ) );
+}
+
+double XclImpChRoot::CalcRelativeFromHmmX( sal_Int32 nPosX ) const
+{
+ const tools::Long nWidth = mxChData->maChartRect.GetWidth();
+ if (!nWidth)
+ throw o3tl::divide_by_zero();
+ return static_cast<double>(nPosX) / nWidth;
+}
+
+double XclImpChRoot::CalcRelativeFromHmmY( sal_Int32 nPosY ) const
+{
+ const tools::Long nHeight = mxChData->maChartRect.GetHeight();
+ if (!nHeight)
+ throw o3tl::divide_by_zero();
+ return static_cast<double >(nPosY) / nHeight;
+}
+
+double XclImpChRoot::CalcRelativeFromChartX( sal_Int32 nPosX ) const
+{
+ return CalcRelativeFromHmmX( CalcHmmFromChartX( nPosX ) );
+}
+
+double XclImpChRoot::CalcRelativeFromChartY( sal_Int32 nPosY ) const
+{
+ return CalcRelativeFromHmmY( CalcHmmFromChartY( nPosY ) );
+}
+
+void XclImpChRoot::ConvertLineFormat( ScfPropertySet& rPropSet,
+ const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteLineProperties(
+ rPropSet, *mxChData->mxLineDashTable, rLineFmt, ePropMode );
+}
+
+void XclImpChRoot::ConvertAreaFormat( ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteAreaProperties( rPropSet, rAreaFmt, ePropMode );
+}
+
+void XclImpChRoot::ConvertEscherFormat( ScfPropertySet& rPropSet,
+ const XclChEscherFormat& rEscherFmt, const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType, XclChPropertyMode ePropMode ) const
+{
+ GetChartPropSetHelper().WriteEscherProperties( rPropSet,
+ *mxChData->mxGradientTable, *mxChData->mxBitmapTable,
+ rEscherFmt, pPicFmt, nDffFillType, ePropMode );
+}
+
+void XclImpChRoot::ConvertFont( ScfPropertySet& rPropSet,
+ sal_uInt16 nFontIdx, const Color* pFontColor ) const
+{
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CHART, nFontIdx, pFontColor );
+}
+
+void XclImpChRoot::ConvertPieRotation( ScfPropertySet& rPropSet, sal_uInt16 nAngle )
+{
+ sal_Int32 nApiRot = (450 - (nAngle % 360)) % 360;
+ rPropSet.SetProperty( EXC_CHPROP_STARTINGANGLE, nApiRot );
+}
+
+XclImpChGroupBase::~XclImpChGroupBase()
+{
+}
+
+void XclImpChGroupBase::ReadRecordGroup( XclImpStream& rStrm )
+{
+ // read contents of the header record
+ ReadHeaderRecord( rStrm );
+
+ // only read sub records, if the next record is a CHBEGIN
+ if( rStrm.GetNextRecId() != EXC_ID_CHBEGIN )
+ return;
+
+ // read the CHBEGIN record, may be used for special initial processing
+ rStrm.StartNextRecord();
+ ReadSubRecord( rStrm );
+
+ // read the nested records
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_CHEND;
+ // skip unsupported nested blocks
+ if( nRecId == EXC_ID_CHBEGIN )
+ SkipBlock( rStrm );
+ else
+ ReadSubRecord( rStrm );
+ }
+ /* Returns with current CHEND record or unchanged stream, if no record
+ group present. In every case another call to StartNextRecord() will go
+ to next record of interest. */
+}
+
+void XclImpChGroupBase::SkipBlock( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecId() == EXC_ID_CHBEGIN, "XclImpChGroupBase::SkipBlock - no CHBEGIN record" );
+ // do nothing if current record is not CHBEGIN
+ bool bLoop = rStrm.GetRecId() == EXC_ID_CHBEGIN;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_CHEND;
+ // skip nested record groups
+ if( nRecId == EXC_ID_CHBEGIN )
+ SkipBlock( rStrm );
+ }
+}
+
+// Frame formatting ===========================================================
+
+void XclImpChFramePos::ReadChFramePos( XclImpStream& rStrm )
+{
+ maData.mnTLMode = rStrm.ReaduInt16();
+ maData.mnBRMode = rStrm.ReaduInt16();
+ /* According to the spec, the upper 16 bits of all members in the
+ rectangle are unused and may contain garbage. */
+ maData.maRect.mnX = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnY = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnWidth = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+ maData.maRect.mnHeight = rStrm.ReadInt16(); rStrm.Ignore( 2 );
+}
+
+void XclImpChLineFormat::ReadChLineFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maColor;
+ maData.mnPattern = rStrm.ReaduInt16();
+ maData.mnWeight = rStrm.ReadInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ // BIFF8: index into palette used instead of RGB data
+ maData.maColor = rRoot.GetPalette().GetColor( rStrm.ReaduInt16() );
+}
+
+void XclImpChLineFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( IsAuto() )
+ {
+ XclChLineFormat aLineFmt;
+ aLineFmt.maColor = (eObjType == EXC_CHOBJTYPE_LINEARSERIES) ?
+ rRoot.GetSeriesLineAutoColor( nFormatIdx ) :
+ rRoot.GetPalette().GetColor( rFmtInfo.mnAutoLineColorIdx );
+ aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ aLineFmt.mnWeight = rFmtInfo.mnAutoLineWeight;
+ rRoot.ConvertLineFormat( rPropSet, aLineFmt, rFmtInfo.mePropMode );
+ }
+ else
+ {
+ rRoot.ConvertLineFormat( rPropSet, maData, rFmtInfo.mePropMode );
+ }
+}
+
+void XclImpChAreaFormat::ReadChAreaFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maPattColor >> maData.maBackColor;
+ maData.mnPattern = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ const XclImpPalette& rPal = rRoot.GetPalette();
+ maData.maPattColor = rPal.GetColor( rStrm.ReaduInt16() );
+ maData.maBackColor = rPal.GetColor( rStrm.ReaduInt16());
+ }
+}
+
+void XclImpChAreaFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ if( IsAuto() )
+ {
+ XclChAreaFormat aAreaFmt;
+ aAreaFmt.maPattColor = (eObjType == EXC_CHOBJTYPE_FILLEDSERIES) ?
+ rRoot.GetSeriesFillAutoColor( nFormatIdx ) :
+ rRoot.GetPalette().GetColor( rFmtInfo.mnAutoPattColorIdx );
+ aAreaFmt.mnPattern = EXC_PATT_SOLID;
+ rRoot.ConvertAreaFormat( rPropSet, aAreaFmt, rFmtInfo.mePropMode );
+ }
+ else
+ {
+ rRoot.ConvertAreaFormat( rPropSet, maData, rFmtInfo.mePropMode );
+ }
+}
+
+XclImpChEscherFormat::XclImpChEscherFormat( const XclImpRoot& rRoot ) :
+ mnDffFillType( mso_fillSolid )
+{
+ maData.mxItemSet =
+ std::make_shared<SfxItemSet>( rRoot.GetDoc().GetDrawLayer()->GetItemPool() );
+}
+
+void XclImpChEscherFormat::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ // read from stream - CHESCHERFORMAT uses own ID for record continuation
+ XclImpDffPropSet aPropSet( rStrm.GetRoot() );
+ rStrm.ResetRecord( true, rStrm.GetRecId() );
+ rStrm >> aPropSet;
+ // get the data
+ aPropSet.FillToItemSet( *maData.mxItemSet );
+ // get fill type from DFF property set
+ mnDffFillType = aPropSet.GetPropertyValue( DFF_Prop_fillType );
+}
+
+void XclImpChEscherFormat::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHPICFORMAT:
+ maPicFmt.mnBmpMode = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ maPicFmt.mnFlags = rStrm.ReaduInt16();
+ maPicFmt.mfScale = rStrm.ReadDouble();
+ break;
+ }
+}
+
+void XclImpChEscherFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, bool bUsePicFmt ) const
+{
+ const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
+ rRoot.ConvertEscherFormat( rPropSet, maData, bUsePicFmt ? &maPicFmt : nullptr, mnDffFillType, rFmtInfo.mePropMode );
+}
+
+XclImpChFrameBase::XclImpChFrameBase( const XclChFormatInfo& rFmtInfo )
+{
+ if( !rFmtInfo.mbCreateDefFrame )
+ return;
+
+ switch( rFmtInfo.meDefFrameType )
+ {
+ case EXC_CHFRAMETYPE_AUTO:
+ mxLineFmt = new XclImpChLineFormat();
+ if( rFmtInfo.mbIsFrame )
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ break;
+ case EXC_CHFRAMETYPE_INVISIBLE:
+ {
+ XclChLineFormat aLineFmt;
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false );
+ aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ mxLineFmt = new XclImpChLineFormat( aLineFmt );
+ if( rFmtInfo.mbIsFrame )
+ {
+ XclChAreaFormat aAreaFmt;
+ ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false );
+ aAreaFmt.mnPattern = EXC_PATT_NONE;
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>( aAreaFmt );
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpChFrameBase::XclImpChFrameBase - unknown frame type" );
+ }
+}
+
+void XclImpChFrameBase::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHLINEFORMAT:
+ mxLineFmt = new XclImpChLineFormat();
+ mxLineFmt->ReadChLineFormat( rStrm );
+ break;
+ case EXC_ID_CHAREAFORMAT:
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ mxAreaFmt->ReadChAreaFormat( rStrm );
+ break;
+ case EXC_ID_CHESCHERFORMAT:
+ mxEscherFmt = std::make_shared<XclImpChEscherFormat>( rStrm.GetRoot() );
+ mxEscherFmt->ReadRecordGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChFrameBase::ConvertLineBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
+{
+ if( mxLineFmt )
+ mxLineFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
+}
+
+void XclImpChFrameBase::ConvertAreaBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
+{
+ if( rRoot.GetFormatInfo( eObjType ).mbIsFrame )
+ {
+ // CHESCHERFORMAT overrides CHAREAFORMAT (even if it is auto)
+ if( mxEscherFmt )
+ mxEscherFmt->Convert( rRoot, rPropSet, eObjType, bUsePicFmt );
+ else if( mxAreaFmt )
+ mxAreaFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
+ }
+}
+
+void XclImpChFrameBase::ConvertFrameBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
+{
+ ConvertLineBase( rRoot, rPropSet, eObjType, nFormatIdx );
+ ConvertAreaBase( rRoot, rPropSet, eObjType, nFormatIdx, bUsePicFmt );
+}
+
+XclImpChFrame::XclImpChFrame( const XclImpChRoot& rRoot, XclChObjectType eObjType ) :
+ XclImpChFrameBase( rRoot.GetFormatInfo( eObjType ) ),
+ XclImpChRoot( rRoot ),
+ meObjType( eObjType )
+{
+}
+
+void XclImpChFrame::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnFormat = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChFrame::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ const XclImpPalette& rPal = GetPalette();
+
+ if( rLineData.IsVisible() && (!mxLineFmt || !mxLineFmt->HasLine()) )
+ {
+ // line formatting
+ XclChLineFormat aLineFmt;
+ aLineFmt.maColor = rPal.GetColor( rLineData.mnColorIdx );
+ switch( rLineData.mnStyle )
+ {
+ case EXC_OBJ_LINE_SOLID: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; break;
+ case EXC_OBJ_LINE_DASH: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASH; break;
+ case EXC_OBJ_LINE_DOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DOT; break;
+ case EXC_OBJ_LINE_DASHDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOT; break;
+ case EXC_OBJ_LINE_DASHDOTDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOTDOT; break;
+ case EXC_OBJ_LINE_MEDTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS; break;
+ case EXC_OBJ_LINE_DARKTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS; break;
+ case EXC_OBJ_LINE_LIGHTTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS; break;
+ case EXC_OBJ_LINE_NONE: aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; break;
+ default: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ }
+ switch( rLineData.mnWidth )
+ {
+ case EXC_OBJ_LINE_HAIR: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR; break;
+ case EXC_OBJ_LINE_THIN: aLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE; break;
+ case EXC_OBJ_LINE_MEDIUM: aLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE; break;
+ case EXC_OBJ_LINE_THICK: aLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE; break;
+ default: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR;
+ }
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, rLineData.IsAuto() );
+ mxLineFmt = new XclImpChLineFormat( aLineFmt );
+ }
+
+ if( rFillData.IsFilled() && (!mxAreaFmt || !mxAreaFmt->HasArea()) && !mxEscherFmt )
+ {
+ // area formatting
+ XclChAreaFormat aAreaFmt;
+ aAreaFmt.maPattColor = rPal.GetColor( rFillData.mnPattColorIdx );
+ aAreaFmt.maBackColor = rPal.GetColor( rFillData.mnBackColorIdx );
+ aAreaFmt.mnPattern = rFillData.mnPattern;
+ ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, rFillData.IsAuto() );
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>( aAreaFmt );
+ }
+}
+
+void XclImpChFrame::Convert( ScfPropertySet& rPropSet, bool bUsePicFmt ) const
+{
+ ConvertFrameBase( GetChRoot(), rPropSet, meObjType, EXC_CHDATAFORMAT_UNKNOWN, bUsePicFmt );
+}
+
+// Source links ===============================================================
+
+namespace {
+
+/** Creates a labeled data sequence object, adds link for series title if present. */
+Reference< XLabeledDataSequence > lclCreateLabeledDataSequence(
+ const XclImpChSourceLinkRef& xValueLink, const OUString& rValueRole,
+ const XclImpChSourceLink* pTitleLink = nullptr )
+{
+ // create data sequence for values and title
+ Reference< XDataSequence > xValueSeq;
+ if( xValueLink )
+ xValueSeq = xValueLink->CreateDataSequence( rValueRole );
+ Reference< XDataSequence > xTitleSeq;
+ if( pTitleLink )
+ xTitleSeq = pTitleLink->CreateDataSequence( EXC_CHPROP_ROLE_LABEL );
+
+ // create the labeled data sequence, if values or title are present
+ Reference< XLabeledDataSequence > xLabeledSeq;
+ if( xValueSeq.is() || xTitleSeq.is() )
+ xLabeledSeq = LabeledDataSequence::create(comphelper::getProcessComponentContext());
+ if( xLabeledSeq.is() )
+ {
+ if( xValueSeq.is() )
+ xLabeledSeq->setValues( xValueSeq );
+ if( xTitleSeq.is() )
+ xLabeledSeq->setLabel( xTitleSeq );
+ }
+ return xLabeledSeq;
+}
+
+} // namespace
+
+XclImpChSourceLink::XclImpChSourceLink( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+XclImpChSourceLink::~XclImpChSourceLink()
+{
+}
+
+void XclImpChSourceLink::ReadChSourceLink( XclImpStream& rStrm )
+{
+ maData.mnDestType = rStrm.ReaduInt8();
+ maData.mnLinkType = rStrm.ReaduInt8();
+ maData.mnFlags = rStrm.ReaduInt16();
+ maData.mnNumFmtIdx = rStrm.ReaduInt16();
+
+ mxTokenArray.reset();
+ if( GetLinkType() == EXC_CHSRCLINK_WORKSHEET )
+ {
+ // read token array
+ XclTokenArray aXclTokArr;
+ rStrm >> aXclTokArr;
+
+ // convert BIFF formula tokens to Calc token array
+ if( std::unique_ptr<ScTokenArray> pTokens = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aXclTokArr ) )
+ mxTokenArray = std::move( pTokens );
+ }
+
+ // try to read a following CHSTRING record
+ if( (rStrm.GetNextRecId() == EXC_ID_CHSTRING) && rStrm.StartNextRecord() )
+ {
+ mxString = std::make_shared<XclImpString>();
+ rStrm.Ignore( 2 );
+ mxString->Read( rStrm, XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
+ }
+}
+
+void XclImpChSourceLink::SetString( const OUString& rString )
+{
+ if( !mxString )
+ mxString = std::make_shared<XclImpString>();
+ mxString->SetText( rString );
+}
+
+void XclImpChSourceLink::SetTextFormats( XclFormatRunVec&& rFormats )
+{
+ if( mxString )
+ mxString->SetFormats( std::move(rFormats) );
+}
+
+sal_uInt16 XclImpChSourceLink::GetCellCount() const
+{
+ sal_uInt32 nCellCount = 0;
+ if( mxTokenArray )
+ {
+ FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
+ for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
+ {
+ switch( pToken->GetType() )
+ {
+ case ::formula::svSingleRef:
+ case ::formula::svExternalSingleRef:
+ // single cell
+ ++nCellCount;
+ break;
+ case ::formula::svDoubleRef:
+ case ::formula::svExternalDoubleRef:
+ {
+ // cell range
+ const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
+ ScAddress aAbs1 = rComplexRef.Ref1.toAbs(GetRoot().GetDoc(), ScAddress());
+ ScAddress aAbs2 = rComplexRef.Ref2.toAbs(GetRoot().GetDoc(), ScAddress());
+ sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
+ sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
+ sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
+ nCellCount += nCols * nRows * nTabs;
+ }
+ break;
+ default: ;
+ }
+ }
+ }
+ return limit_cast< sal_uInt16 >( nCellCount );
+}
+
+void XclImpChSourceLink::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
+{
+ bool bLinkToSource = ::get_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
+ sal_uInt32 nScNumFmt = bLinkToSource ? GetNumFmtBuffer().GetScFormat( maData.mnNumFmtIdx ) : NUMBERFORMAT_ENTRY_NOT_FOUND;
+ OUString aPropName = bPercent ? OUString( EXC_CHPROP_PERCENTAGENUMFMT ) : OUString( EXC_CHPROP_NUMBERFORMAT );
+ if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
+ rPropSet.SetProperty( aPropName, static_cast< sal_Int32 >( nScNumFmt ) );
+ else
+ // restore 'link to source' at data point (series may contain manual number format)
+ rPropSet.SetAnyProperty( aPropName, Any() );
+}
+
+Reference< XDataSequence > XclImpChSourceLink::CreateDataSequence( const OUString& rRole ) const
+{
+ Reference< XDataSequence > xDataSeq;
+ Reference< XDataProvider > xDataProv = GetDataProvider();
+ if( xDataProv.is() )
+ {
+ if ( mxTokenArray )
+ {
+ ScCompiler aComp( GetDoc(), ScAddress(), *mxTokenArray, GetDoc().GetGrammar() );
+ OUStringBuffer aRangeRep;
+ aComp.CreateStringFromTokenArray( aRangeRep );
+ try
+ {
+ xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aRangeRep.makeStringAndClear() );
+ // set sequence role
+ ScfPropertySet aSeqProp( xDataSeq );
+ aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
+ }
+ catch( Exception& )
+ {
+ // OSL_FAIL( "XclImpChSourceLink::CreateDataSequence - cannot create data sequence" );
+ }
+ }
+ else if( rRole == EXC_CHPROP_ROLE_LABEL && mxString && !mxString->GetText().isEmpty() )
+ {
+ try
+ {
+ OUString aString("\"");
+ xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aString + mxString->GetText() + aString );
+ // set sequence role
+ ScfPropertySet aSeqProp( xDataSeq );
+ aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
+ }
+ catch( Exception& ) { }
+ }
+ }
+ return xDataSeq;
+}
+
+Sequence< Reference< XFormattedString > > XclImpChSourceLink::CreateStringSequence(
+ const XclImpChRoot& rRoot, sal_uInt16 nLeadFontIdx, const Color& rLeadFontColor ) const
+{
+ ::std::vector< Reference< XFormattedString > > aStringVec;
+ if( mxString )
+ {
+ for( XclImpStringIterator aIt( *mxString ); aIt.Is(); ++aIt )
+ {
+ Reference< css::chart2::XFormattedString2 > xFmtStr = css::chart2::FormattedString::create( comphelper::getProcessComponentContext() );
+ // set text data
+ xFmtStr->setString( aIt.GetPortionText() );
+
+ // set font formatting and font color
+ ScfPropertySet aStringProp( xFmtStr );
+ sal_uInt16 nFontIdx = aIt.GetPortionFont();
+ if( (nFontIdx == EXC_FONT_NOTFOUND) && (aIt.GetPortionIndex() == 0) )
+ // leading unformatted portion - use passed font settings
+ rRoot.ConvertFont( aStringProp, nLeadFontIdx, &rLeadFontColor );
+ else
+ rRoot.ConvertFont( aStringProp, nFontIdx );
+
+ // add string to vector of strings
+ aStringVec.emplace_back(xFmtStr );
+ }
+ }
+ return ScfApiHelper::VectorToSequence( aStringVec );
+}
+
+void XclImpChSourceLink::FillSourceLink( ::std::vector< ScTokenRef >& rTokens ) const
+{
+ if( !mxTokenArray )
+ // no links to fill.
+ return;
+
+ FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
+ for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
+ {
+ ScTokenRef pToken(p->Clone());
+ if (ScRefTokenHelper::isRef(pToken))
+ // This is a reference token. Store it.
+ ScRefTokenHelper::join(&GetRoot().GetDoc(), rTokens, pToken, ScAddress());
+ }
+}
+
+// Text =======================================================================
+
+XclImpChFontBase::~XclImpChFontBase()
+{
+}
+
+void XclImpChFontBase::ConvertFontBase( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
+{
+ Color aFontColor = GetFontColor();
+ rRoot.ConvertFont( rPropSet, GetFontIndex(), &aFontColor );
+}
+
+void XclImpChFontBase::ConvertRotationBase( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
+{
+ XclChPropSetHelper::WriteRotationProperties( rPropSet, GetRotation(), bSupportsStacked );
+}
+
+XclImpChFont::XclImpChFont() :
+ mnFontIdx( EXC_FONT_NOTFOUND )
+{
+}
+
+void XclImpChFont::ReadChFont( XclImpStream& rStrm )
+{
+ mnFontIdx = rStrm.ReaduInt16();
+}
+
+XclImpChText::XclImpChText( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChText::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnHAlign = rStrm.ReaduInt8();
+ maData.mnVAlign = rStrm.ReaduInt8();
+ maData.mnBackMode = rStrm.ReaduInt16();
+ rStrm >> maData.maTextColor
+ >> maData.maRect;
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() );
+ // placement and rotation
+ maData.mnFlags2 = rStrm.ReaduInt16();
+ maData.mnRotation = rStrm.ReaduInt16();
+ }
+ else
+ {
+ // BIFF2-BIFF7: get rotation from text orientation
+ sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 8, 3 );
+ maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient );
+ }
+}
+
+void XclImpChText::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHFONT:
+ mxFont = std::make_shared<XclImpChFont>();
+ mxFont->ReadChFont( rStrm );
+ break;
+ case EXC_ID_CHFORMATRUNS:
+ if( GetBiff() == EXC_BIFF8 )
+ XclImpString::ReadFormats( rStrm, maFormats );
+ break;
+ case EXC_ID_CHSOURCELINK:
+ mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ mxSrcLink->ReadChSourceLink( rStrm );
+ break;
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_TEXT );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHOBJECTLINK:
+ maObjLink.mnTarget = rStrm.ReaduInt16();
+ maObjLink.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
+ maObjLink.maPointPos.mnPointIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHFRLABELPROPS:
+ ReadChFrLabelProps( rStrm );
+ break;
+ case EXC_ID_CHEND:
+ if( mxSrcLink && !maFormats.empty() )
+ mxSrcLink->SetTextFormats( std::vector(maFormats) );
+ break;
+ }
+}
+
+sal_uInt16 XclImpChText::GetFontIndex() const
+{
+ return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+Color XclImpChText::GetFontColor() const
+{
+ return ::get_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR ) ? GetFontAutoColor() : maData.maTextColor;
+}
+
+sal_uInt16 XclImpChText::GetRotation() const
+{
+ return maData.mnRotation;
+}
+
+void XclImpChText::SetString( const OUString& rString )
+{
+ if( !mxSrcLink )
+ mxSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ mxSrcLink->SetString( rString );
+}
+
+void XclImpChText::UpdateText( const XclImpChText* pParentText )
+{
+ if( !pParentText )
+ return;
+
+ // update missing members
+ if( !mxFrame )
+ mxFrame = pParentText->mxFrame;
+ if( !mxFont )
+ {
+ mxFont = pParentText->mxFont;
+ // text color is taken from CHTEXT record, not from font in CHFONT
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, ::get_flag( pParentText->maData.mnFlags, EXC_CHTEXT_AUTOCOLOR ) );
+ maData.maTextColor = pParentText->maData.maTextColor;
+ }
+}
+
+void XclImpChText::UpdateDataLabel( bool bCateg, bool bValue, bool bPercent )
+{
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bCateg );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bValue );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bCateg && bPercent );
+ ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bCateg && !bValue && !bPercent );
+}
+
+void XclImpChText::ConvertFont( ScfPropertySet& rPropSet ) const
+{
+ ConvertFontBase( GetChRoot(), rPropSet );
+}
+
+void XclImpChText::ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
+{
+ ConvertRotationBase( rPropSet, bSupportsStacked );
+}
+
+void XclImpChText::ConvertFrame( ScfPropertySet& rPropSet ) const
+{
+ if( mxFrame )
+ mxFrame->Convert( rPropSet );
+}
+
+void XclImpChText::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
+{
+ if( mxSrcLink )
+ mxSrcLink->ConvertNumFmt( rPropSet, bPercent );
+}
+
+void XclImpChText::ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
+{
+ // existing CHFRLABELPROPS record wins over flags from CHTEXT
+ sal_uInt16 nShowFlags = mxLabelProps ? mxLabelProps->mnFlags : maData.mnFlags;
+ sal_uInt16 SHOWANYCATEG = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWCATEG : (EXC_CHTEXT_SHOWCATEGPERC | EXC_CHTEXT_SHOWCATEG);
+ sal_uInt16 SHOWANYVALUE = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWVALUE : EXC_CHTEXT_SHOWVALUE;
+ sal_uInt16 SHOWANYPERCENT = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWPERCENT : (EXC_CHTEXT_SHOWPERCENT | EXC_CHTEXT_SHOWCATEGPERC);
+ sal_uInt16 SHOWANYBUBBLE = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWBUBBLE : EXC_CHTEXT_SHOWBUBBLE;
+
+ // get raw flags for label values
+ bool bShowNone = IsDeleted();
+ bool bShowCateg = !bShowNone && ::get_flag( nShowFlags, SHOWANYCATEG );
+ bool bShowPercent = !bShowNone && ::get_flag( nShowFlags, SHOWANYPERCENT );
+ bool bShowValue = !bShowNone && ::get_flag( nShowFlags, SHOWANYVALUE );
+ bool bShowBubble = !bShowNone && ::get_flag( nShowFlags, SHOWANYBUBBLE );
+
+ // adjust to Chart2 behaviour
+ if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
+ bShowValue = bShowBubble; // Chart2 bubble charts show bubble size if 'ShowValue' is set
+
+ // other flags
+ bool bShowAny = bShowValue || bShowPercent || bShowCateg;
+ bool bShowSymbol = bShowAny && ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL );
+
+ // create API struct for label values, set API label separator
+ cssc2::DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol, false, false );
+ rPropSet.SetProperty( EXC_CHPROP_LABEL, aPointLabel );
+ OUString aSep = mxLabelProps ? mxLabelProps->maSeparator : OUString('\n');
+ if( aSep.isEmpty() )
+ aSep = "; ";
+ rPropSet.SetStringProperty( EXC_CHPROP_LABELSEPARATOR, aSep );
+
+ // text properties of attached label
+ if( !bShowAny )
+ return;
+
+ ConvertFont( rPropSet );
+ ConvertRotation( rPropSet, false );
+ // label placement
+ using namespace cssc::DataLabelPlacement;
+ sal_Int32 nPlacement = rTypeInfo.mnDefaultLabelPos;
+ switch( ::extract_value< sal_uInt16 >( maData.mnFlags2, 0, 4 ) )
+ {
+ case EXC_CHTEXT_POS_DEFAULT: nPlacement = rTypeInfo.mnDefaultLabelPos; break;
+ case EXC_CHTEXT_POS_OUTSIDE: nPlacement = OUTSIDE; break;
+ case EXC_CHTEXT_POS_INSIDE: nPlacement = INSIDE; break;
+ case EXC_CHTEXT_POS_CENTER: nPlacement = CENTER; break;
+ case EXC_CHTEXT_POS_AXIS: nPlacement = NEAR_ORIGIN; break;
+ case EXC_CHTEXT_POS_ABOVE: nPlacement = TOP; break;
+ case EXC_CHTEXT_POS_BELOW: nPlacement = BOTTOM; break;
+ case EXC_CHTEXT_POS_LEFT: nPlacement = LEFT; break;
+ case EXC_CHTEXT_POS_RIGHT: nPlacement = RIGHT; break;
+ case EXC_CHTEXT_POS_AUTO: nPlacement = AVOID_OVERLAP; break;
+ }
+ sal_Int32 nGlobalPlacement = 0;
+ if ( ( nPlacement == rTypeInfo.mnDefaultLabelPos ) && pGlobalPropSet &&
+ pGlobalPropSet->GetProperty( nGlobalPlacement, EXC_CHPROP_LABELPLACEMENT ) )
+ nPlacement = nGlobalPlacement;
+
+ rPropSet.SetProperty( EXC_CHPROP_LABELPLACEMENT, nPlacement );
+ // label number format (percentage format wins over value format)
+ if( bShowPercent || bShowValue )
+ ConvertNumFmt( rPropSet, bShowPercent );
+}
+
+Reference< XTitle > XclImpChText::CreateTitle() const
+{
+ Reference< XTitle > xTitle;
+ if( mxSrcLink && mxSrcLink->HasString() )
+ {
+ // create the formatted strings
+ Sequence< Reference< XFormattedString > > aStringSeq(
+ mxSrcLink->CreateStringSequence( GetChRoot(), GetFontIndex(), GetFontColor() ) );
+ if( aStringSeq.hasElements() )
+ {
+ // create the title object
+ xTitle.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_TITLE ), UNO_QUERY );
+ if( xTitle.is() )
+ {
+ // set the formatted strings
+ xTitle->setText( aStringSeq );
+ // more title formatting properties
+ ScfPropertySet aTitleProp( xTitle );
+ ConvertFrame( aTitleProp );
+ ConvertRotation( aTitleProp, true );
+ }
+ }
+ }
+ return xTitle;
+}
+
+void XclImpChText::ConvertTitlePosition( const XclChTextKey& rTitleKey ) const
+{
+ if( !mxFramePos ) return;
+
+ const XclChFramePos& rPosData = mxFramePos->GetFramePosData();
+ OSL_ENSURE( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rPosData.mnBRMode == EXC_CHFRAMEPOS_PARENT),
+ "XclImpChText::ConvertTitlePosition - unexpected frame position mode" );
+
+ /* Check if title is moved manually. To get the actual position of the
+ title, we do some kind of hack and use the values from the CHTEXT
+ record, effectively ignoring the contents of the CHFRAMEPOS record
+ which contains the position relative to the default title position
+ (according to the spec, the CHFRAMEPOS supersedes the CHTEXT record).
+ Especially when it comes to axis titles, things would become very
+ complicated here, because the relative title position is stored in a
+ measurement unit that is dependent on the size of the inner plot area,
+ the interpretation of the X and Y coordinate is dependent on the
+ direction of the axis, and in 3D charts, and the title default
+ positions are dependent on the 3D view settings (rotation, elevation,
+ and perspective). Thus, it is easier to assume that the creator has
+ written out the correct absolute position and size of the title in the
+ CHTEXT record. This is assured by checking that the shape size stored
+ in the CHTEXT record is non-zero. */
+ if( !((rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) &&
+ ((rPosData.maRect.mnX != 0) || (rPosData.maRect.mnY != 0)) &&
+ (maData.maRect.mnWidth > 0) && (maData.maRect.mnHeight > 0)) )
+ return;
+
+ try
+ {
+ Reference< XShape > xTitleShape( GetTitleShape( rTitleKey ), UNO_SET_THROW );
+ // the call to XShape.getSize() may recalc the chart view
+ css::awt::Size aTitleSize = xTitleShape->getSize();
+ // rotated titles need special handling...
+ Degree100 nScRot = XclTools::GetScRotation( GetRotation(), 0_deg100 );
+ double fRad = toRadians(nScRot);
+ double fSin = fabs( sin( fRad ) );
+ // calculate the title position from the values in the CHTEXT record
+ css::awt::Point aTitlePos(
+ CalcHmmFromChartX( maData.maRect.mnX ),
+ CalcHmmFromChartY( maData.maRect.mnY ) );
+ // add part of height to X direction, if title is rotated down (clockwise)
+ if( nScRot > 18000_deg100 )
+ aTitlePos.X += static_cast< sal_Int32 >( fSin * aTitleSize.Height + 0.5 );
+ // add part of width to Y direction, if title is rotated up (counterclockwise)
+ else if( nScRot > 0_deg100 )
+ aTitlePos.Y += static_cast< sal_Int32 >( fSin * aTitleSize.Width + 0.5 );
+ // set the resulting position at the title shape
+ xTitleShape->setPosition( aTitlePos );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void XclImpChText::ReadChFrLabelProps( XclImpStream& rStrm )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mxLabelProps = std::make_shared<XclChFrLabelProps>();
+ sal_uInt16 nSepLen;
+ rStrm.Ignore( 12 );
+ mxLabelProps->mnFlags = rStrm.ReaduInt16();
+ nSepLen = rStrm.ReaduInt16();
+ if( nSepLen > 0 )
+ mxLabelProps->maSeparator = rStrm.ReadUniString( nSepLen );
+ }
+}
+
+namespace {
+
+void lclUpdateText( XclImpChTextRef& rxText, const XclImpChText* xDefText )
+{
+ if (rxText)
+ rxText->UpdateText( xDefText );
+ else if (xDefText)
+ {
+ rxText = std::make_shared<XclImpChText>(*xDefText);
+ }
+}
+
+void lclFinalizeTitle( XclImpChTextRef& rxTitle, const XclImpChText* pDefText, const OUString& rAutoTitle )
+{
+ /* Do not update a title, if it is not visible (if rxTitle is null).
+ Existing reference indicates enabled title. */
+ if( rxTitle )
+ {
+ if( !rxTitle->HasString() )
+ rxTitle->SetString( rAutoTitle );
+ if( rxTitle->HasString() )
+ rxTitle->UpdateText(pDefText);
+ else
+ rxTitle.reset();
+ }
+}
+
+} // namespace
+
+// Data series ================================================================
+
+void XclImpChMarkerFormat::ReadChMarkerFormat( XclImpStream& rStrm )
+{
+ rStrm >> maData.maLineColor >> maData.maFillColor;
+ maData.mnMarkerType = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ if( rRoot.GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ const XclImpPalette& rPal = rRoot.GetPalette();
+ maData.maLineColor = rPal.GetColor( rStrm.ReaduInt16() );
+ maData.maFillColor = rPal.GetColor( rStrm.ReaduInt16() );
+ // marker size
+ maData.mnMarkerSize = rStrm.ReaduInt32();
+ }
+}
+
+void XclImpChMarkerFormat::Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const
+{
+ if( IsAuto() )
+ {
+ XclChMarkerFormat aMarkerFmt;
+ // line and fill color of the symbol are equal to series line color
+ //TODO: Excel sets no fill color for specific symbols (e.g. cross)
+ aMarkerFmt.maLineColor = aMarkerFmt.maFillColor = rRoot.GetSeriesLineAutoColor( nFormatIdx );
+ switch( nLineWeight )
+ {
+ case EXC_CHLINEFORMAT_HAIR: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_HAIRSIZE; break;
+ case EXC_CHLINEFORMAT_SINGLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE; break;
+ case EXC_CHLINEFORMAT_DOUBLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE; break;
+ case EXC_CHLINEFORMAT_TRIPLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_TRIPLESIZE; break;
+ default: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE;
+ }
+ aMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ XclChPropSetHelper::WriteMarkerProperties( rPropSet, aMarkerFmt );
+ }
+ else
+ {
+ XclChPropSetHelper::WriteMarkerProperties( rPropSet, maData );
+ }
+}
+
+void XclImpChMarkerFormat::ConvertColor( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
+{
+ Color aLineColor = IsAuto() ? rRoot.GetSeriesLineAutoColor( nFormatIdx ) : maData.maFillColor;
+ rPropSet.SetColorProperty( EXC_CHPROP_COLOR, aLineColor );
+}
+
+XclImpChPieFormat::XclImpChPieFormat() :
+ mnPieDist( 0 )
+{
+}
+
+void XclImpChPieFormat::ReadChPieFormat( XclImpStream& rStrm )
+{
+ mnPieDist = rStrm.ReaduInt16();
+}
+
+void XclImpChPieFormat::Convert( ScfPropertySet& rPropSet ) const
+{
+ double fApiDist = ::std::min< double >( mnPieDist / 100.0, 1.0 );
+ rPropSet.SetProperty( EXC_CHPROP_OFFSET, fApiDist );
+}
+
+XclImpChSeriesFormat::XclImpChSeriesFormat() :
+ mnFlags( 0 )
+{
+}
+
+void XclImpChSeriesFormat::ReadChSeriesFormat( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpCh3dDataFormat::ReadCh3dDataFormat( XclImpStream& rStrm )
+{
+ maData.mnBase = rStrm.ReaduInt8();
+ maData.mnTop = rStrm.ReaduInt8();
+}
+
+void XclImpCh3dDataFormat::Convert( ScfPropertySet& rPropSet ) const
+{
+ using namespace ::com::sun::star::chart2::DataPointGeometry3D;
+ sal_Int32 nApiType = (maData.mnBase == EXC_CH3DDATAFORMAT_RECT) ?
+ ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CUBOID : PYRAMID) :
+ ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CYLINDER : CONE);
+ rPropSet.SetProperty( EXC_CHPROP_GEOMETRY3D, nApiType );
+}
+
+XclImpChAttachedLabel::XclImpChAttachedLabel( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ mnFlags( 0 )
+{
+}
+
+void XclImpChAttachedLabel::ReadChAttachedLabel( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+}
+
+XclImpChTextRef XclImpChAttachedLabel::CreateDataLabel( const XclImpChText* pParent ) const
+{
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYVALUE = EXC_CHATTLABEL_SHOWVALUE;
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYPERCENT = EXC_CHATTLABEL_SHOWPERCENT | EXC_CHATTLABEL_SHOWCATEGPERC;
+ const sal_uInt16 EXC_CHATTLABEL_SHOWANYCATEG = EXC_CHATTLABEL_SHOWCATEG | EXC_CHATTLABEL_SHOWCATEGPERC;
+
+ XclImpChTextRef xLabel;
+ if ( pParent )
+ xLabel = std::make_shared<XclImpChText>( *pParent );
+ else
+ xLabel = std::make_shared<XclImpChText>( GetChRoot() );
+ xLabel->UpdateDataLabel(
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYCATEG ),
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYVALUE ),
+ ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYPERCENT ) );
+ return xLabel;
+}
+
+XclImpChDataFormat::XclImpChDataFormat( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChDataFormat::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.maPointPos.mnPointIdx = rStrm.ReaduInt16();
+ maData.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
+ maData.mnFormatIdx = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChDataFormat::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHMARKERFORMAT:
+ mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
+ mxMarkerFmt->ReadChMarkerFormat( rStrm );
+ break;
+ case EXC_ID_CHPIEFORMAT:
+ mxPieFmt = std::make_shared<XclImpChPieFormat>();
+ mxPieFmt->ReadChPieFormat( rStrm );
+ break;
+ case EXC_ID_CHSERIESFORMAT:
+ mxSeriesFmt = std::make_shared<XclImpChSeriesFormat>();
+ mxSeriesFmt->ReadChSeriesFormat( rStrm );
+ break;
+ case EXC_ID_CH3DDATAFORMAT:
+ mx3dDataFmt = std::make_shared<XclImpCh3dDataFormat>();
+ mx3dDataFmt->ReadCh3dDataFormat( rStrm );
+ break;
+ case EXC_ID_CHATTACHEDLABEL:
+ mxAttLabel = std::make_shared<XclImpChAttachedLabel>( GetChRoot() );
+ mxAttLabel->ReadChAttachedLabel( rStrm );
+ break;
+ default:
+ XclImpChFrameBase::ReadSubRecord( rStrm );
+ }
+}
+
+void XclImpChDataFormat::SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx )
+{
+ maData.maPointPos = rPointPos;
+ maData.mnFormatIdx = nFormatIdx;
+}
+
+void XclImpChDataFormat::UpdateGroupFormat( const XclChExtTypeInfo& rTypeInfo )
+{
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+}
+
+void XclImpChDataFormat::UpdateSeriesFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pGroupFmt )
+{
+ // update missing formats from passed chart type group format
+ if( pGroupFmt )
+ {
+ if( !mxLineFmt )
+ mxLineFmt = pGroupFmt->mxLineFmt;
+ if( !mxAreaFmt && !mxEscherFmt )
+ {
+ mxAreaFmt = pGroupFmt->mxAreaFmt;
+ mxEscherFmt = pGroupFmt->mxEscherFmt;
+ }
+ if( !mxMarkerFmt )
+ mxMarkerFmt = pGroupFmt->mxMarkerFmt;
+ if( !mxPieFmt )
+ mxPieFmt = pGroupFmt->mxPieFmt;
+ if( !mxSeriesFmt )
+ mxSeriesFmt = pGroupFmt->mxSeriesFmt;
+ if( !mx3dDataFmt )
+ mx3dDataFmt = pGroupFmt->mx3dDataFmt;
+ if( !mxAttLabel )
+ mxAttLabel = pGroupFmt->mxAttLabel;
+ }
+
+ /* Create missing but required formats. Existing line, area, and marker
+ format objects are needed to create automatic series formatting. */
+ if( !mxLineFmt )
+ mxLineFmt = new XclImpChLineFormat();
+ if( !mxAreaFmt && !mxEscherFmt )
+ mxAreaFmt = std::make_shared<XclImpChAreaFormat>();
+ if( !mxMarkerFmt )
+ mxMarkerFmt = std::make_shared<XclImpChMarkerFormat>();
+
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+ // update data label
+ UpdateDataLabel( pGroupFmt );
+}
+
+void XclImpChDataFormat::UpdatePointFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pSeriesFmt )
+{
+ // remove formats if they are automatic in this and in the passed series format
+ if( pSeriesFmt )
+ {
+ if( IsAutoLine() && pSeriesFmt->IsAutoLine() )
+ mxLineFmt.clear();
+ if( IsAutoArea() && pSeriesFmt->IsAutoArea() )
+ mxAreaFmt.reset();
+ if( IsAutoMarker() && pSeriesFmt->IsAutoMarker() )
+ mxMarkerFmt.reset();
+ mxSeriesFmt.reset();
+ }
+
+ // Excel ignores 3D bar format for single data points
+ mx3dDataFmt.reset();
+ // remove point line formats for linear chart types, TODO: implement in OOChart
+ if( !rTypeInfo.IsSeriesFrameFormat() )
+ mxLineFmt.clear();
+
+ // remove formats not used for the current chart type
+ RemoveUnusedFormats( rTypeInfo );
+ // update data label
+ UpdateDataLabel( pSeriesFmt );
+}
+
+void XclImpChDataFormat::UpdateTrendLineFormat()
+{
+ if( !mxLineFmt )
+ mxLineFmt = new XclImpChLineFormat();
+ mxAreaFmt.reset();
+ mxEscherFmt.reset();
+ mxMarkerFmt.reset();
+ mxPieFmt.reset();
+ mxSeriesFmt.reset();
+ mx3dDataFmt.reset();
+ mxAttLabel.reset();
+ // update data label
+ UpdateDataLabel( nullptr );
+}
+
+void XclImpChDataFormat::Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
+{
+ /* Line and area format.
+ #i71810# If the data points are filled with bitmaps, textures, or
+ patterns, then only bar charts will use the CHPICFORMAT record to
+ determine stacking/stretching mode. All other chart types ignore this
+ record and always use the property 'fill-type' from the DFF property
+ set (stretched for bitmaps, and stacked for textures and patterns). */
+ bool bUsePicFmt = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR;
+ ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType(), maData.mnFormatIdx, bUsePicFmt );
+
+ // #i83151# only hair lines in 3D charts with filled data points
+ if( rTypeInfo.mb3dChart && rTypeInfo.IsSeriesFrameFormat() && mxLineFmt && mxLineFmt->HasLine() )
+ rPropSet.SetProperty< sal_Int32 >( "BorderWidth", 0 );
+
+ // other formatting
+ if( mxMarkerFmt )
+ mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx, GetLineWeight() );
+ if( mxPieFmt )
+ mxPieFmt->Convert( rPropSet );
+ if( mx3dDataFmt )
+ mx3dDataFmt->Convert( rPropSet );
+ if( mxLabel )
+ mxLabel->ConvertDataLabel( rPropSet, rTypeInfo, pGlobalPropSet );
+
+ // 3D settings
+ rPropSet.SetProperty< sal_Int16 >( EXC_CHPROP_PERCENTDIAGONAL, 0 );
+
+ /* Special case: set marker color as line color, if series line is not
+ visible. This makes the color visible in the marker area.
+ TODO: remove this if OOChart supports own colors in markers. */
+ if( !rTypeInfo.IsSeriesFrameFormat() && !HasLine() && mxMarkerFmt )
+ mxMarkerFmt->ConvertColor( GetChRoot(), rPropSet, maData.mnFormatIdx );
+}
+
+void XclImpChDataFormat::ConvertLine( ScfPropertySet& rPropSet, XclChObjectType eObjType ) const
+{
+ ConvertLineBase( GetChRoot(), rPropSet, eObjType );
+}
+
+void XclImpChDataFormat::ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
+{
+ ConvertAreaBase( GetChRoot(), rPropSet, EXC_CHOBJTYPE_FILLEDSERIES, nFormatIdx );
+}
+
+void XclImpChDataFormat::RemoveUnusedFormats( const XclChExtTypeInfo& rTypeInfo )
+{
+ // data point marker only in linear 2D charts
+ if( rTypeInfo.IsSeriesFrameFormat() )
+ mxMarkerFmt.reset();
+ // pie format only in pie/donut charts
+ if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
+ mxPieFmt.reset();
+ // 3D format only in 3D bar charts
+ if( !rTypeInfo.mb3dChart || (rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
+ mx3dDataFmt.reset();
+}
+
+void XclImpChDataFormat::UpdateDataLabel( const XclImpChDataFormat* pParentFmt )
+{
+ /* CHTEXT groups linked to data labels override existing CHATTACHEDLABEL
+ records. Only if there is a CHATTACHEDLABEL record without a CHTEXT
+ group, the contents of the CHATTACHEDLABEL record are used. In this
+ case a new CHTEXT group is created and filled with the settings from
+ the CHATTACHEDLABEL record. */
+ const XclImpChText* pDefText = nullptr;
+ if (pParentFmt)
+ pDefText = pParentFmt->GetDataLabel();
+ if (!pDefText)
+ pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_DATALABEL );
+ if (mxLabel)
+ mxLabel->UpdateText(pDefText);
+ else if (mxAttLabel)
+ mxLabel = mxAttLabel->CreateDataLabel( pDefText );
+}
+
+XclImpChSerTrendLine::XclImpChSerTrendLine( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChSerTrendLine::ReadChSerTrendLine( XclImpStream& rStrm )
+{
+ maData.mnLineType = rStrm.ReaduInt8();
+ maData.mnOrder = rStrm.ReaduInt8();
+ maData.mfIntercept = rStrm.ReadDouble();
+ maData.mnShowEquation = rStrm.ReaduInt8();
+ maData.mnShowRSquared = rStrm.ReaduInt8();
+ maData.mfForecastFor = rStrm.ReadDouble();
+ maData.mfForecastBack = rStrm.ReadDouble();
+}
+
+Reference< XRegressionCurve > XclImpChSerTrendLine::CreateRegressionCurve() const
+{
+ // trend line type
+ Reference< XRegressionCurve > xRegCurve;
+ switch( maData.mnLineType )
+ {
+ case EXC_CHSERTREND_POLYNOMIAL:
+ if( maData.mnOrder == 1 )
+ {
+ xRegCurve = LinearRegressionCurve::create( comphelper::getProcessComponentContext() );
+ } else {
+ xRegCurve = PolynomialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ }
+ break;
+ case EXC_CHSERTREND_EXPONENTIAL:
+ xRegCurve = ExponentialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_LOGARITHMIC:
+ xRegCurve = LogarithmicRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_POWER:
+ xRegCurve = PotentialRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ case EXC_CHSERTREND_MOVING_AVG:
+ xRegCurve = MovingAverageRegressionCurve::create( comphelper::getProcessComponentContext() );
+ break;
+ }
+
+ // trend line formatting
+ if( xRegCurve.is() && mxDataFmt )
+ {
+ ScfPropertySet aPropSet( xRegCurve );
+ mxDataFmt->ConvertLine( aPropSet, EXC_CHOBJTYPE_TRENDLINE );
+
+ aPropSet.SetProperty(EXC_CHPROP_CURVENAME, maTrendLineName);
+ aPropSet.SetProperty(EXC_CHPROP_POLYNOMIAL_DEGREE, static_cast<sal_Int32> (maData.mnOrder) );
+ aPropSet.SetProperty(EXC_CHPROP_MOVING_AVERAGE_PERIOD, static_cast<sal_Int32> (maData.mnOrder) );
+ aPropSet.SetProperty(EXC_CHPROP_EXTRAPOLATE_FORWARD, maData.mfForecastFor);
+ aPropSet.SetProperty(EXC_CHPROP_EXTRAPOLATE_BACKWARD, maData.mfForecastBack);
+
+ bool bForceIntercept = std::isfinite(maData.mfIntercept);
+ aPropSet.SetProperty(EXC_CHPROP_FORCE_INTERCEPT, bForceIntercept);
+ if (bForceIntercept)
+ {
+ aPropSet.SetProperty(EXC_CHPROP_INTERCEPT_VALUE, maData.mfIntercept);
+ }
+
+ // #i83100# show equation and correlation coefficient
+ ScfPropertySet aLabelProp( xRegCurve->getEquationProperties() );
+ aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWEQUATION, maData.mnShowEquation != 0 );
+ aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWCORRELATION, maData.mnShowRSquared != 0 );
+
+ // #i83100# formatting of the equation text box
+ if (const XclImpChText* pLabel = mxDataFmt->GetDataLabel())
+ {
+ pLabel->ConvertFont( aLabelProp );
+ pLabel->ConvertFrame( aLabelProp );
+ pLabel->ConvertNumFmt( aLabelProp, false );
+ }
+ }
+
+ return xRegCurve;
+}
+
+XclImpChSerErrorBar::XclImpChSerErrorBar( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChSerErrorBar::ReadChSerErrorBar( XclImpStream& rStrm )
+{
+ maData.mnBarType = rStrm.ReaduInt8();
+ maData.mnSourceType = rStrm.ReaduInt8();
+ maData.mnLineEnd = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ maData.mfValue = rStrm.ReadDouble();
+ maData.mnValueCount = rStrm.ReaduInt16();
+}
+
+void XclImpChSerErrorBar::SetSeriesData( XclImpChSourceLinkRef const & xValueLink, XclImpChDataFormatRef const & xDataFmt )
+{
+ mxValueLink = xValueLink;
+ mxDataFmt = xDataFmt;
+}
+
+Reference< XLabeledDataSequence > XclImpChSerErrorBar::CreateValueSequence() const
+{
+ return lclCreateLabeledDataSequence( mxValueLink, XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ) );
+}
+
+Reference< XPropertySet > XclImpChSerErrorBar::CreateErrorBar( const XclImpChSerErrorBar* pPosBar, const XclImpChSerErrorBar* pNegBar )
+{
+ Reference< XPropertySet > xErrorBar;
+
+ if( const XclImpChSerErrorBar* pPrimaryBar = pPosBar ? pPosBar : pNegBar )
+ {
+ xErrorBar.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_ERRORBAR ), UNO_QUERY );
+ ScfPropertySet aBarProp( xErrorBar );
+
+ // plus/minus bars visible?
+ aBarProp.SetBoolProperty( EXC_CHPROP_SHOWPOSITIVEERROR, pPosBar != nullptr );
+ aBarProp.SetBoolProperty( EXC_CHPROP_SHOWNEGATIVEERROR, pNegBar != nullptr );
+
+ // type of displayed error
+ switch( pPrimaryBar->maData.mnSourceType )
+ {
+ case EXC_CHSERERR_PERCENT:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::RELATIVE );
+ aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
+ aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_FIXED:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::ABSOLUTE );
+ aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
+ aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_STDDEV:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_DEVIATION );
+ aBarProp.SetProperty( EXC_CHPROP_WEIGHT, pPrimaryBar->maData.mfValue );
+ break;
+ case EXC_CHSERERR_STDERR:
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_ERROR );
+ break;
+ case EXC_CHSERERR_CUSTOM:
+ {
+ aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::FROM_DATA );
+ // attach data sequences to error bar
+ Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY );
+ if( xDataSink.is() )
+ {
+ // create vector of all value sequences
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ // add positive values
+ if( pPosBar )
+ {
+ Reference< XLabeledDataSequence > xValueSeq = pPosBar->CreateValueSequence();
+ if( xValueSeq.is() )
+ aLabeledSeqVec.push_back( xValueSeq );
+ }
+ // add negative values
+ if( pNegBar )
+ {
+ Reference< XLabeledDataSequence > xValueSeq = pNegBar->CreateValueSequence();
+ if( xValueSeq.is() )
+ aLabeledSeqVec.push_back( xValueSeq );
+ }
+ // attach labeled data sequences to series
+ if( aLabeledSeqVec.empty() )
+ xErrorBar.clear();
+ else
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+ }
+ }
+ break;
+ default:
+ xErrorBar.clear();
+ }
+
+ // error bar formatting
+ if( pPrimaryBar->mxDataFmt && xErrorBar.is() )
+ pPrimaryBar->mxDataFmt->ConvertLine( aBarProp, EXC_CHOBJTYPE_ERRORBAR );
+ }
+
+ return xErrorBar;
+}
+
+XclImpChSeries::XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
+ XclImpChRoot( rRoot ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE ),
+ mnSeriesIdx( nSeriesIdx ),
+ mnParentIdx( EXC_CHSERIES_INVALID ),
+ mbLabelDeleted( false )
+{
+}
+
+void XclImpChSeries::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnCategType = rStrm.ReaduInt16();
+ maData.mnValueType = rStrm.ReaduInt16();
+ maData.mnCategCount = rStrm.ReaduInt16();
+ maData.mnValueCount = rStrm.ReaduInt16();
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maData.mnBubbleType = rStrm.ReaduInt16();
+ maData.mnBubbleCount = rStrm.ReaduInt16();
+ }
+}
+
+void XclImpChSeries::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHSOURCELINK:
+ ReadChSourceLink( rStrm );
+ break;
+ case EXC_ID_CHDATAFORMAT:
+ ReadChDataFormat( rStrm );
+ break;
+ case EXC_ID_CHSERGROUP:
+ mnGroupIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHSERPARENT:
+ ReadChSerParent( rStrm );
+ break;
+ case EXC_ID_CHSERTRENDLINE:
+ ReadChSerTrendLine( rStrm );
+ break;
+ case EXC_ID_CHSERERRORBAR:
+ ReadChSerErrorBar( rStrm );
+ break;
+ case EXC_ID_CHLEGENDEXCEPTION:
+ ReadChLegendException( rStrm );
+ break;
+ }
+}
+
+void XclImpChSeries::SetDataFormat( const XclImpChDataFormatRef& xDataFmt )
+{
+ if (!xDataFmt)
+ return;
+
+ sal_uInt16 nPointIdx = xDataFmt->GetPointPos().mnPointIdx;
+ if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
+ {
+ if (mxSeriesFmt)
+ // Don't overwrite the existing format.
+ return;
+
+ mxSeriesFmt = xDataFmt;
+ if (HasParentSeries())
+ return;
+
+ XclImpChTypeGroupRef pTypeGroup = GetChartData().GetTypeGroup(mnGroupIdx);
+ if (pTypeGroup)
+ pTypeGroup->SetUsedFormatIndex(xDataFmt->GetFormatIdx());
+
+ return;
+ }
+
+ if (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT)
+ // Above the max point count. Bail out.
+ return;
+
+ XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
+ if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert it.
+ itr = maPointFmts.insert(itr, XclImpChDataFormatMap::value_type(nPointIdx, xDataFmt));
+ }
+}
+
+void XclImpChSeries::SetDataLabel( const XclImpChTextRef& xLabel )
+{
+ if (!xLabel)
+ return;
+
+ sal_uInt16 nPointIdx = xLabel->GetPointPos().mnPointIdx;
+ if ((nPointIdx != EXC_CHDATAFORMAT_ALLPOINTS) && (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT))
+ // Above the maximum allowed data points. Bail out.
+ return;
+
+ XclImpChTextMap::iterator itr = maLabels.lower_bound(nPointIdx);
+ if (itr == maLabels.end() || maLabels.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert it.
+ itr = maLabels.insert(itr, XclImpChTextMap::value_type(nPointIdx, xLabel));
+ }
+}
+
+void XclImpChSeries::AddChildSeries( const XclImpChSeries& rSeries )
+{
+ OSL_ENSURE( !HasParentSeries(), "XclImpChSeries::AddChildSeries - not allowed for child series" );
+ if (&rSeries == this)
+ {
+ SAL_WARN("sc.filter", "self add attempt");
+ return;
+ }
+
+ /* In Excel, trend lines and error bars are stored as own series. In Calc,
+ these are properties of the parent series. This function adds the
+ settings of the passed series to this series. */
+ maTrendLines.insert( maTrendLines.end(), rSeries.maTrendLines.begin(), rSeries.maTrendLines.end() );
+ for (auto const& it : rSeries.m_ErrorBars)
+ {
+ m_ErrorBars.insert(std::make_pair(it.first, std::make_unique<XclImpChSerErrorBar>(*it.second)));
+ }
+}
+
+void XclImpChSeries::FinalizeDataFormats()
+{
+ if( HasParentSeries() )
+ {
+ // *** series is a child series, e.g. trend line or error bar ***
+
+ // create missing series format
+ if( !mxSeriesFmt )
+ mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, 0 );
+
+ if( mxSeriesFmt )
+ {
+ // #i83100# set text label format, e.g. for trend line equations
+ XclImpChTextRef xLabel;
+ XclImpChTextMap::iterator itr = maLabels.find(EXC_CHDATAFORMAT_ALLPOINTS);
+ if (itr != maLabels.end())
+ xLabel = itr->second;
+ mxSeriesFmt->SetDataLabel(xLabel);
+ // create missing automatic formats
+ mxSeriesFmt->UpdateTrendLineFormat();
+ }
+
+ // copy series formatting to child objects
+ for (auto const& trendLine : maTrendLines)
+ {
+ trendLine->SetDataFormat(mxSeriesFmt);
+ if (mxTitleLink && mxTitleLink->HasString())
+ {
+ trendLine->SetTrendlineName(mxTitleLink->GetString());
+ }
+ }
+ for (auto const& it : m_ErrorBars)
+ {
+ it.second->SetSeriesData( mxValueLink, mxSeriesFmt );
+ }
+ }
+ else if( XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
+ {
+ // *** series is a regular data series ***
+
+ // create missing series format
+ if( !mxSeriesFmt )
+ {
+ // #i51639# use a new unused format index to create series default format
+ sal_uInt16 nFormatIdx = pTypeGroup->PopUnusedFormatIndex();
+ mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, nFormatIdx );
+ }
+
+ // set text labels to data formats
+ for (auto const& label : maLabels)
+ {
+ sal_uInt16 nPointIdx = label.first;
+ if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
+ {
+ if (!mxSeriesFmt)
+ mxSeriesFmt = CreateDataFormat(nPointIdx, EXC_CHDATAFORMAT_DEFAULT);
+ mxSeriesFmt->SetDataLabel(label.second);
+ }
+ else if (nPointIdx < EXC_CHDATAFORMAT_MAXPOINTCOUNT)
+ {
+ XclImpChDataFormatRef p;
+ XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
+ if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
+ {
+ // No object exists at this point index position. Insert
+ // a new one.
+ p = CreateDataFormat(nPointIdx, EXC_CHDATAFORMAT_DEFAULT);
+ itr = maPointFmts.insert(
+ itr, XclImpChDataFormatMap::value_type(nPointIdx, p));
+ }
+ else
+ p = itr->second;
+ p->SetDataLabel(label.second);
+ }
+ }
+
+ // update series format (copy missing formatting from group default format)
+ if( mxSeriesFmt )
+ mxSeriesFmt->UpdateSeriesFormat( pTypeGroup->GetTypeInfo(), pTypeGroup->GetGroupFormat().get() );
+
+ // update data point formats (removes unchanged automatic formatting)
+ for (auto const& pointFormat : maPointFmts)
+ pointFormat.second->UpdatePointFormat( pTypeGroup->GetTypeInfo(), mxSeriesFmt.get() );
+ }
+}
+
+namespace {
+
+/** Returns the property set of the specified data point. */
+ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_uInt16 nPointIdx )
+{
+ ScfPropertySet aPropSet;
+ try
+ {
+ aPropSet.Set( xDataSeries->getDataPointByIndex( static_cast< sal_Int32 >( nPointIdx ) ) );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "lclGetPointPropSet - no data point property set" );
+ }
+ return aPropSet;
+}
+
+} // namespace
+
+Reference< XLabeledDataSequence > XclImpChSeries::CreateValueSequence( const OUString& rValueRole ) const
+{
+ return lclCreateLabeledDataSequence( mxValueLink, rValueRole, mxTitleLink.get() );
+}
+
+Reference< XLabeledDataSequence > XclImpChSeries::CreateCategSequence( const OUString& rCategRole ) const
+{
+ return lclCreateLabeledDataSequence( mxCategLink, rCategRole );
+}
+
+Reference< XDataSeries > XclImpChSeries::CreateDataSeries() const
+{
+ Reference< XDataSeries > xDataSeries;
+ if( const XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
+ {
+ const XclChExtTypeInfo& rTypeInfo = pTypeGroup->GetTypeInfo();
+
+ // create the data series object
+ xDataSeries.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
+
+ // attach data and title sequences to series
+ Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
+ if( xDataSink.is() )
+ {
+ // create vector of all value sequences
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ // add Y values
+ Reference< XLabeledDataSequence > xYValueSeq =
+ CreateValueSequence( EXC_CHPROP_ROLE_YVALUES );
+ if( xYValueSeq.is() )
+ aLabeledSeqVec.push_back( xYValueSeq );
+ // add X values
+ if( !rTypeInfo.mbCategoryAxis )
+ {
+ Reference< XLabeledDataSequence > xXValueSeq =
+ CreateCategSequence( EXC_CHPROP_ROLE_XVALUES );
+ if( xXValueSeq.is() )
+ aLabeledSeqVec.push_back( xXValueSeq );
+ // add size values of bubble charts
+ if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
+ {
+ Reference< XLabeledDataSequence > xSizeValueSeq =
+ lclCreateLabeledDataSequence( mxBubbleLink, EXC_CHPROP_ROLE_SIZEVALUES, mxTitleLink.get() );
+ if( xSizeValueSeq.is() )
+ aLabeledSeqVec.push_back( xSizeValueSeq );
+ }
+ }
+ // attach labeled data sequences to series
+ if( !aLabeledSeqVec.empty() )
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+ }
+
+ // series formatting
+ ScfPropertySet aSeriesProp( xDataSeries );
+ if( mxSeriesFmt )
+ mxSeriesFmt->Convert( aSeriesProp, rTypeInfo );
+
+ if (mbLabelDeleted)
+ aSeriesProp.SetProperty(EXC_CHPROP_SHOWLEGENDENTRY, false);
+
+ // trend lines
+ ConvertTrendLines( xDataSeries );
+
+ // error bars
+ Reference< XPropertySet > xErrorBarX = CreateErrorBar( EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
+ if( xErrorBarX.is() )
+ aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARX, xErrorBarX );
+ Reference< XPropertySet > xErrorBarY = CreateErrorBar( EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
+ if( xErrorBarY.is() )
+ aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARY, xErrorBarY );
+
+ // own area formatting for every data point (TODO: varying line color not supported)
+ bool bVarPointFmt = pTypeGroup->HasVarPointFormat() && rTypeInfo.IsSeriesFrameFormat();
+ aSeriesProp.SetBoolProperty( EXC_CHPROP_VARYCOLORSBY, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
+ // #i91271# always set area formatting for every point in pie/doughnut charts
+ if (mxSeriesFmt && mxValueLink && ((bVarPointFmt && mxSeriesFmt->IsAutoArea()) || (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE)))
+ {
+ for( sal_uInt16 nPointIdx = 0, nPointCount = mxValueLink->GetCellCount(); nPointIdx < nPointCount; ++nPointIdx )
+ {
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
+ mxSeriesFmt->ConvertArea( aPointProp, bVarPointFmt ? nPointIdx : mnSeriesIdx );
+ }
+ }
+
+ // data point formatting
+ for (auto const& pointFormat : maPointFmts)
+ {
+ ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, pointFormat.first );
+ pointFormat.second->Convert( aPointProp, rTypeInfo, &aSeriesProp );
+ }
+ }
+ return xDataSeries;
+}
+
+void XclImpChSeries::FillAllSourceLinks( ::std::vector< ScTokenRef >& rTokens ) const
+{
+ if( mxValueLink )
+ mxValueLink->FillSourceLink( rTokens );
+ if( mxCategLink )
+ mxCategLink->FillSourceLink( rTokens );
+ if( mxTitleLink )
+ mxTitleLink->FillSourceLink( rTokens );
+ if( mxBubbleLink )
+ mxBubbleLink->FillSourceLink( rTokens );
+}
+
+void XclImpChSeries::ReadChSourceLink( XclImpStream& rStrm )
+{
+ XclImpChSourceLinkRef xSrcLink = std::make_shared<XclImpChSourceLink>( GetChRoot() );
+ xSrcLink->ReadChSourceLink( rStrm );
+ switch( xSrcLink->GetDestType() )
+ {
+ case EXC_CHSRCLINK_TITLE: mxTitleLink = xSrcLink; break;
+ case EXC_CHSRCLINK_VALUES: mxValueLink = xSrcLink; break;
+ case EXC_CHSRCLINK_CATEGORY: mxCategLink = xSrcLink; break;
+ case EXC_CHSRCLINK_BUBBLES: mxBubbleLink = xSrcLink; break;
+ }
+}
+
+void XclImpChSeries::ReadChDataFormat( XclImpStream& rStrm )
+{
+ // #i51639# chart stores all data formats and assigns them later to the series
+ GetChartData().ReadChDataFormat( rStrm );
+}
+
+void XclImpChSeries::ReadChSerParent( XclImpStream& rStrm )
+{
+ mnParentIdx = rStrm.ReaduInt16();
+ // index to parent series is 1-based, convert it to 0-based
+ if( mnParentIdx > 0 )
+ --mnParentIdx;
+ else
+ mnParentIdx = EXC_CHSERIES_INVALID;
+}
+
+void XclImpChSeries::ReadChSerTrendLine( XclImpStream& rStrm )
+{
+ XclImpChSerTrendLineRef xTrendLine = std::make_shared<XclImpChSerTrendLine>( GetChRoot() );
+ xTrendLine->ReadChSerTrendLine( rStrm );
+ maTrendLines.push_back( xTrendLine );
+}
+
+void XclImpChSeries::ReadChSerErrorBar( XclImpStream& rStrm )
+{
+ unique_ptr<XclImpChSerErrorBar> pErrorBar(new XclImpChSerErrorBar(GetChRoot()));
+ pErrorBar->ReadChSerErrorBar(rStrm);
+ sal_uInt8 nBarType = pErrorBar->GetBarType();
+ m_ErrorBars.insert(std::make_pair(nBarType, std::move(pErrorBar)));
+}
+
+XclImpChDataFormatRef XclImpChSeries::CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx )
+{
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->SetPointPos( XclChDataPointPos( mnSeriesIdx, nPointIdx ), nFormatIdx );
+ return xDataFmt;
+}
+
+void XclImpChSeries::ConvertTrendLines( Reference< XDataSeries > const & xDataSeries ) const
+{
+ Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
+ if( !xRegCurveCont.is() )
+ return;
+
+ for (auto const& trendLine : maTrendLines)
+ {
+ try
+ {
+ Reference< XRegressionCurve > xRegCurve = trendLine->CreateRegressionCurve();
+ if( xRegCurve.is() )
+ {
+ xRegCurveCont->addRegressionCurve( xRegCurve );
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChSeries::ConvertTrendLines - cannot add regression curve" );
+ }
+ }
+}
+
+Reference< XPropertySet > XclImpChSeries::CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const
+{
+ XclImpChSerErrorBarMap::const_iterator itrPosBar = m_ErrorBars.find(nPosBarId);
+ XclImpChSerErrorBarMap::const_iterator itrNegBar = m_ErrorBars.find(nNegBarId);
+ XclImpChSerErrorBarMap::const_iterator itrEnd = m_ErrorBars.end();
+ if (itrPosBar == itrEnd || itrNegBar == itrEnd)
+ return Reference<XPropertySet>();
+
+ return XclImpChSerErrorBar::CreateErrorBar(itrPosBar->second.get(), itrNegBar->second.get());
+}
+
+void XclImpChSeries::ReadChLegendException(XclImpStream& rStrm)
+{
+ rStrm.Ignore(2);
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ mbLabelDeleted = (nFlags & EXC_CHLEGENDEXCEPTION_DELETED);
+}
+
+// Chart type groups ==========================================================
+
+XclImpChType::XclImpChType( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ mnRecId( EXC_ID_CHUNKNOWN ),
+ maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
+{
+}
+
+void XclImpChType::ReadChType( XclImpStream& rStrm )
+{
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bool bKnownType = true;
+
+ switch( nRecId )
+ {
+ case EXC_ID_CHBAR:
+ maData.mnOverlap = rStrm.ReadInt16();
+ maData.mnGap = rStrm.ReadInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ case EXC_ID_CHLINE:
+ case EXC_ID_CHAREA:
+ case EXC_ID_CHRADARLINE:
+ case EXC_ID_CHRADARAREA:
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ case EXC_ID_CHPIE:
+ maData.mnRotation = rStrm.ReaduInt16();
+ maData.mnPieHole = rStrm.ReaduInt16();
+ if( GetBiff() == EXC_BIFF8 )
+ maData.mnFlags = rStrm.ReaduInt16();
+ else
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHPIEEXT:
+ maData.mnRotation = 0;
+ maData.mnPieHole = 0;
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHSCATTER:
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ maData.mnBubbleSize = rStrm.ReaduInt16();
+ maData.mnBubbleType = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ }
+ else
+ maData.mnFlags = 0;
+ break;
+
+ case EXC_ID_CHSURFACE:
+ maData.mnFlags = rStrm.ReaduInt16();
+ break;
+
+ default:
+ bKnownType = false;
+ }
+
+ if( bKnownType )
+ mnRecId = nRecId;
+}
+
+void XclImpChType::Finalize( bool bStockChart )
+{
+ switch( mnRecId )
+ {
+ case EXC_ID_CHLINE:
+ maTypeInfo = GetChartTypeInfo( bStockChart ?
+ EXC_CHTYPEID_STOCK : EXC_CHTYPEID_LINE );
+ break;
+ case EXC_ID_CHBAR:
+ maTypeInfo = GetChartTypeInfo( ::get_flagvalue(
+ maData.mnFlags, EXC_CHBAR_HORIZONTAL,
+ EXC_CHTYPEID_HORBAR, EXC_CHTYPEID_BAR ) );
+ break;
+ case EXC_ID_CHPIE:
+ maTypeInfo = GetChartTypeInfo( (maData.mnPieHole > 0) ?
+ EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
+ break;
+ case EXC_ID_CHSCATTER:
+ maTypeInfo = GetChartTypeInfo( ::get_flagvalue(
+ maData.mnFlags, EXC_CHSCATTER_BUBBLES,
+ EXC_CHTYPEID_BUBBLES, EXC_CHTYPEID_SCATTER ) );
+ break;
+ default:
+ maTypeInfo = GetChartTypeInfo( mnRecId );
+ }
+
+ switch( maTypeInfo.meTypeId )
+ {
+ case EXC_CHTYPEID_PIEEXT:
+ case EXC_CHTYPEID_BUBBLES:
+ case EXC_CHTYPEID_SURFACE:
+ case EXC_CHTYPEID_UNKNOWN:
+ GetTracer().TraceChartUnKnownType();
+ break;
+ default:;
+ }
+}
+
+bool XclImpChType::IsStacked() const
+{
+ bool bStacked = false;
+ if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ bStacked =
+ ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) &&
+ !::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ bStacked =
+ ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) &&
+ !::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT );
+ break;
+ default:;
+ }
+ return bStacked;
+}
+
+bool XclImpChType::IsPercent() const
+{
+ bool bPercent = false;
+ if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_LINE:
+ bPercent =
+ ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) &&
+ ::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT );
+ break;
+ case EXC_CHTYPECATEG_BAR:
+ bPercent =
+ ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) &&
+ ::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT );
+ break;
+ default:;
+ }
+ return bPercent;
+}
+
+bool XclImpChType::HasCategoryLabels() const
+{
+ // radar charts disable category labels in chart type, not in CHTICK of X axis
+ return (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) || ::get_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS );
+}
+
+Reference< XCoordinateSystem > XclImpChType::CreateCoordSystem( bool b3dChart ) const
+{
+ // create the coordinate system object
+ Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ Reference< XCoordinateSystem > xCoordSystem;
+ if( maTypeInfo.mbPolarCoordSystem )
+ {
+ if( b3dChart )
+ xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
+ else
+ xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
+ }
+ else
+ {
+ if( b3dChart )
+ xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
+ else
+ xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
+ }
+
+ // swap X and Y axis
+ if( maTypeInfo.mbSwappedAxesSet )
+ {
+ ScfPropertySet aCoordSysProp( xCoordSystem );
+ aCoordSysProp.SetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS, true );
+ }
+
+ return xCoordSystem;
+}
+
+Reference< XChartType > XclImpChType::CreateChartType( Reference< XDiagram > const & xDiagram, bool b3dChart ) const
+{
+ OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
+ Reference< XChartType > xChartType( ScfApiHelper::CreateInstance( aService ), UNO_QUERY );
+
+ // additional properties
+ switch( maTypeInfo.meTypeCateg )
+ {
+ case EXC_CHTYPECATEG_BAR:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ Sequence< sal_Int32 > aInt32Seq{ -maData.mnOverlap, -maData.mnOverlap };
+ aTypeProp.SetProperty( EXC_CHPROP_OVERLAPSEQ, aInt32Seq );
+ aInt32Seq = { maData.mnGap, maData.mnGap };
+ aTypeProp.SetProperty( EXC_CHPROP_GAPWIDTHSEQ, aInt32Seq );
+ }
+ break;
+ case EXC_CHTYPECATEG_PIE:
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_USERINGS, maTypeInfo.meTypeId == EXC_CHTYPEID_DONUT );
+ /* #i85166# starting angle of first pie slice. 3D pie charts use Y
+ rotation setting in view3D element. Of-pie charts do not
+ support pie rotation. */
+ if( !b3dChart && (maTypeInfo.meTypeId != EXC_CHTYPEID_PIEEXT) )
+ {
+ ScfPropertySet aDiaProp( xDiagram );
+ XclImpChRoot::ConvertPieRotation( aDiaProp, maData.mnRotation );
+ }
+ }
+ break;
+ default:;
+ }
+
+ return xChartType;
+}
+
+void XclImpChChart3d::ReadChChart3d( XclImpStream& rStrm )
+{
+ maData.mnRotation = rStrm.ReaduInt16();
+ maData.mnElevation = rStrm.ReadInt16();
+ maData.mnEyeDist = rStrm.ReaduInt16();
+ maData.mnRelHeight = rStrm.ReaduInt16();
+ maData.mnRelDepth = rStrm.ReaduInt16();
+ maData.mnDepthGap = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChChart3d::Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const
+{
+ namespace cssd = ::com::sun::star::drawing;
+
+// #i104057# do not assert this, written by broken external generators
+// OSL_ENSURE( ::get_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ) == b3dWallChart, "XclImpChChart3d::Convert - wrong wall flag" );
+
+ sal_Int32 nRotationY = 0;
+ sal_Int32 nRotationX = 0;
+ sal_Int32 nPerspective = 15;
+ bool bRightAngled = false;
+ cssd::ProjectionMode eProjMode = cssd::ProjectionMode_PERSPECTIVE;
+ Color aAmbientColor, aLightColor;
+
+ if( b3dWallChart )
+ {
+ // Y rotation (Excel [0..359], Chart2 [-179,180])
+ nRotationY = NormAngle180<sal_Int32>(maData.mnRotation);
+ // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
+ nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, -90, 90 );
+ // perspective (Excel and Chart2 [0,100])
+ nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
+ // right-angled axes
+ bRightAngled = !::get_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D );
+ // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
+ bool bParallel = bRightAngled || (nPerspective == 0);
+ eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
+ // ambient color (Gray 20%)
+ aAmbientColor = Color( 204, 204, 204 );
+ // light color (Gray 60%)
+ aLightColor = Color( 102, 102, 102 );
+ }
+ else
+ {
+ // Y rotation not used in pie charts, but 'first pie slice angle'
+ nRotationY = 0;
+ XclImpChRoot::ConvertPieRotation( rPropSet, maData.mnRotation );
+ // X rotation a.k.a. elevation (map Excel [10..80] to Chart2 [-80,-10])
+ nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, 10, 80 ) - 90;
+ // perspective (Excel and Chart2 [0,100])
+ nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
+ // no right-angled axes in pie charts, but parallel projection
+ bRightAngled = false;
+ eProjMode = cssd::ProjectionMode_PARALLEL;
+ // ambient color (Gray 30%)
+ aAmbientColor = Color( 179, 179, 179 );
+ // light color (Gray 70%)
+ aLightColor = Color( 76, 76, 76 );
+ }
+
+ // properties
+ rPropSet.SetProperty( EXC_CHPROP_3DRELATIVEHEIGHT, static_cast<sal_Int32>(maData.mnRelHeight / 2)); // seems to be 200%, change to 100%
+ rPropSet.SetProperty( EXC_CHPROP_ROTATIONVERTICAL, nRotationY );
+ rPropSet.SetProperty( EXC_CHPROP_ROTATIONHORIZONTAL, nRotationX );
+ rPropSet.SetProperty( EXC_CHPROP_PERSPECTIVE, nPerspective );
+ rPropSet.SetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES, bRightAngled );
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENEPERSPECTIVE, eProjMode );
+
+ // light settings
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENESHADEMODE, cssd::ShadeMode_FLAT );
+ rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENEAMBIENTCOLOR, aAmbientColor );
+ rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON1, false );
+ rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON2, true );
+ rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENELIGHTCOLOR2, aLightColor );
+ rPropSet.SetProperty( EXC_CHPROP_D3DSCENELIGHTDIR2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
+}
+
+XclImpChLegend::XclImpChLegend( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChLegend::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ rStrm >> maData.maRect;
+ maData.mnDockMode = rStrm.ReaduInt8();
+ maData.mnSpacing = rStrm.ReaduInt8();
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ // trace unsupported features
+ if( GetTracer().IsEnabled() )
+ {
+ if( maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
+ GetTracer().TraceChartLegendPosition();
+ if( ::get_flag( maData.mnFlags, EXC_CHLEGEND_DATATABLE ) )
+ GetTracer().TraceChartDataTable();
+ }
+}
+
+void XclImpChLegend::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ mxText = std::make_shared<XclImpChText>( GetChRoot() );
+ mxText->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChLegend::Finalize()
+{
+ // legend default formatting differs in OOChart and Excel, missing frame means automatic
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
+ // Update text formatting. If mxText is empty, the passed default text is used.
+ lclUpdateText( mxText, GetChartData().GetDefaultText( EXC_CHTEXTTYPE_LEGEND ) );
+}
+
+Reference< XLegend > XclImpChLegend::CreateLegend() const
+{
+ Reference< XLegend > xLegend( ScfApiHelper::CreateInstance( SERVICE_CHART2_LEGEND ), UNO_QUERY );
+ if( xLegend.is() )
+ {
+ ScfPropertySet aLegendProp( xLegend );
+ aLegendProp.SetBoolProperty( EXC_CHPROP_SHOW, true );
+
+ // frame properties
+ if( mxFrame )
+ mxFrame->Convert( aLegendProp );
+ // text properties
+ if( mxText )
+ mxText->ConvertFont( aLegendProp );
+
+ /* Legend position and size. Default positions are used only if the
+ plot area is positioned automatically (Excel sets the plot area to
+ manual mode, if the legend is moved or resized). With manual plot
+ areas, Excel ignores the value in maData.mnDockMode completely. */
+ cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
+ cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ if( !GetChartData().IsManualPlotArea() ) switch( maData.mnDockMode )
+ {
+ case EXC_CHLEGEND_LEFT:
+ eApiPos = cssc2::LegendPosition_LINE_START;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ break;
+ case EXC_CHLEGEND_RIGHT:
+ // top-right not supported
+ case EXC_CHLEGEND_CORNER:
+ eApiPos = cssc2::LegendPosition_LINE_END;
+ eApiExpand = cssc::ChartLegendExpansion_HIGH;
+ break;
+ case EXC_CHLEGEND_TOP:
+ eApiPos = cssc2::LegendPosition_PAGE_START;
+ eApiExpand = cssc::ChartLegendExpansion_WIDE;
+ break;
+ case EXC_CHLEGEND_BOTTOM:
+ eApiPos = cssc2::LegendPosition_PAGE_END;
+ eApiExpand = cssc::ChartLegendExpansion_WIDE;
+ break;
+ }
+
+ // no automatic position/size: try to find the correct position and size
+ if( GetChartData().IsManualPlotArea() || maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
+ {
+ const XclChFramePos* pFramePos = mxFramePos ? &mxFramePos->GetFramePosData() : nullptr;
+
+ /* Legend position. Only the settings from the CHFRAMEPOS record
+ are used by Excel, the position in the CHLEGEND record will be
+ ignored. */
+ if( pFramePos )
+ {
+ RelativePosition aRelPos(
+ CalcRelativeFromChartX( pFramePos->maRect.mnX ),
+ CalcRelativeFromChartY( pFramePos->maRect.mnY ),
+ css::drawing::Alignment_TOP_LEFT );
+ aLegendProp.SetProperty( EXC_CHPROP_RELATIVEPOSITION, aRelPos );
+ }
+ else
+ {
+ // no manual position/size found, just go for the default
+ eApiPos = cssc2::LegendPosition_LINE_END;
+ }
+
+ /* Legend size. The member mnBRMode specifies whether size is
+ automatic or changes manually. Manual size is given in points,
+ not in chart units. */
+ if( pFramePos && (pFramePos->mnBRMode == EXC_CHFRAMEPOS_ABSSIZE_POINTS) &&
+ (pFramePos->maRect.mnWidth > 0) && (pFramePos->maRect.mnHeight > 0) )
+ {
+ eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
+ sal_Int32 nWidthHmm = o3tl::convert(pFramePos->maRect.mnWidth, o3tl::Length::pt, o3tl::Length::mm100);
+ sal_Int32 nHeightHmm = o3tl::convert(pFramePos->maRect.mnHeight, o3tl::Length::pt, o3tl::Length::mm100);
+ RelativeSize aRelSize( CalcRelativeFromHmmX( nWidthHmm ), CalcRelativeFromHmmY( nHeightHmm ) );
+ aLegendProp.SetProperty( EXC_CHPROP_RELATIVESIZE, aRelSize );
+ }
+ else
+ {
+ // automatic size: determine entry direction from flags
+ eApiExpand = ::get_flagvalue( maData.mnFlags, EXC_CHLEGEND_STACKED,
+ cssc::ChartLegendExpansion_HIGH, cssc::ChartLegendExpansion_WIDE );
+ }
+ }
+ aLegendProp.SetProperty( EXC_CHPROP_ANCHORPOSITION, eApiPos );
+ aLegendProp.SetProperty( EXC_CHPROP_EXPANSION, eApiExpand );
+ }
+ return xLegend;
+}
+
+XclImpChDropBar::XclImpChDropBar( sal_uInt16 nDropBar ) :
+ mnDropBar( nDropBar ),
+ mnBarDist( 0 )
+{
+}
+
+void XclImpChDropBar::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ mnBarDist = rStrm.ReaduInt16();
+}
+
+void XclImpChDropBar::Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
+{
+ XclChObjectType eObjType = EXC_CHOBJTYPE_BACKGROUND;
+ switch( mnDropBar )
+ {
+ case EXC_CHDROPBAR_UP: eObjType = EXC_CHOBJTYPE_WHITEDROPBAR; break;
+ case EXC_CHDROPBAR_DOWN: eObjType = EXC_CHOBJTYPE_BLACKDROPBAR; break;
+ }
+ ConvertFrameBase( rRoot, rPropSet, eObjType );
+}
+
+XclImpChTypeGroup::XclImpChTypeGroup( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot ),
+ maType( rRoot ),
+ maTypeInfo( maType.GetTypeInfo() )
+{
+ // Initialize unused format indexes set. At this time, all formats are unused.
+ for( sal_uInt16 nFormatIdx = 0; nFormatIdx <= EXC_CHSERIES_MAXSERIES; ++nFormatIdx )
+ maUnusedFormats.insert( maUnusedFormats.end(), nFormatIdx );
+}
+
+void XclImpChTypeGroup::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 16 );
+ maData.mnFlags = rStrm.ReaduInt16();
+ maData.mnGroupIdx = rStrm.ReaduInt16();
+}
+
+void XclImpChTypeGroup::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHCHART3D:
+ mxChart3d = std::make_shared<XclImpChChart3d>();
+ mxChart3d->ReadChChart3d( rStrm );
+ break;
+ case EXC_ID_CHLEGEND:
+ mxLegend = std::make_shared<XclImpChLegend>( GetChRoot() );
+ mxLegend->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHDEFAULTTEXT:
+ GetChartData().ReadChDefaultText( rStrm );
+ break;
+ case EXC_ID_CHDROPBAR:
+ ReadChDropBar( rStrm );
+ break;
+ case EXC_ID_CHCHARTLINE:
+ ReadChChartLine( rStrm );
+ break;
+ case EXC_ID_CHDATAFORMAT:
+ ReadChDataFormat( rStrm );
+ break;
+ default:
+ maType.ReadChType( rStrm );
+ }
+}
+
+void XclImpChTypeGroup::Finalize()
+{
+ // check and set valid chart type
+ bool bStockChart =
+ (maType.GetRecId() == EXC_ID_CHLINE) && // must be a line chart
+ !mxChart3d && // must be a 2d chart
+ m_ChartLines.find(EXC_CHCHARTLINE_HILO) != m_ChartLines.end() && // must contain hi-lo lines
+ (maSeries.size() == static_cast<XclImpChSeriesVec::size_type>(HasDropBars() ? 4 : 3)); // correct series count
+ maType.Finalize( bStockChart );
+
+ // extended type info
+ maTypeInfo.Set( maType.GetTypeInfo(), static_cast< bool >(mxChart3d), false );
+
+ // reverse series order for some unstacked 2D chart types
+ if( maTypeInfo.mbReverseSeries && !Is3dChart() && !maType.IsStacked() && !maType.IsPercent() )
+ ::std::reverse( maSeries.begin(), maSeries.end() );
+
+ // update chart type group format, may depend on chart type finalized above
+ if( mxGroupFmt )
+ mxGroupFmt->UpdateGroupFormat( maTypeInfo );
+}
+
+void XclImpChTypeGroup::AddSeries( XclImpChSeriesRef const & xSeries )
+{
+ if( xSeries )
+ maSeries.push_back( xSeries );
+ // store first inserted series separately, series order may be reversed later
+ if( !mxFirstSeries )
+ mxFirstSeries = xSeries;
+}
+
+void XclImpChTypeGroup::SetUsedFormatIndex( sal_uInt16 nFormatIdx )
+{
+ maUnusedFormats.erase( nFormatIdx );
+}
+
+sal_uInt16 XclImpChTypeGroup::PopUnusedFormatIndex()
+{
+ OSL_ENSURE( !maUnusedFormats.empty(), "XclImpChTypeGroup::PopUnusedFormatIndex - no more format indexes available" );
+ sal_uInt16 nFormatIdx = maUnusedFormats.empty() ? 0 : *maUnusedFormats.begin();
+ SetUsedFormatIndex( nFormatIdx );
+ return nFormatIdx;
+}
+
+bool XclImpChTypeGroup::HasVarPointFormat() const
+{
+ return ::get_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS ) &&
+ ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_MULTI) || // multiple series allowed
+ ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_SINGLE) && // or exactly 1 series?
+ (maSeries.size() == 1)));
+}
+
+bool XclImpChTypeGroup::HasConnectorLines() const
+{
+ // existence of connector lines (only in stacked bar charts)
+ if ( !(maType.IsStacked() || maType.IsPercent()) || (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
+ return false;
+ XclImpChLineFormatMap::const_iterator aConLine = m_ChartLines.find(EXC_CHCHARTLINE_CONNECT);
+ return (aConLine != m_ChartLines.end() && aConLine->second.HasLine());
+}
+
+OUString XclImpChTypeGroup::GetSingleSeriesTitle() const
+{
+ // no automatic title for series with trendlines or error bars
+ // pie charts always show an automatic title, even if more series exist
+ return (mxFirstSeries && !mxFirstSeries->HasChildSeries() && (maTypeInfo.mbSingleSeriesVis || (maSeries.size() == 1))) ?
+ mxFirstSeries->GetTitle() : OUString();
+}
+
+void XclImpChTypeGroup::ConvertChart3d( ScfPropertySet& rPropSet ) const
+{
+ if( mxChart3d )
+ mxChart3d->Convert( rPropSet, Is3dWallChart() );
+}
+
+Reference< XCoordinateSystem > XclImpChTypeGroup::CreateCoordSystem() const
+{
+ return maType.CreateCoordSystem( Is3dChart() );
+}
+
+Reference< XChartType > XclImpChTypeGroup::CreateChartType( Reference< XDiagram > const & xDiagram, sal_Int32 nApiAxesSetIdx ) const
+{
+ OSL_ENSURE( IsValidGroup(), "XclImpChTypeGroup::CreateChartType - type group without series" );
+
+ // create the chart type object
+ Reference< XChartType > xChartType = maType.CreateChartType( xDiagram, Is3dChart() );
+
+ // bar chart connector lines
+ if( HasConnectorLines() )
+ {
+ ScfPropertySet aDiaProp( xDiagram );
+ aDiaProp.SetBoolProperty( EXC_CHPROP_CONNECTBARS, true );
+ }
+
+ /* Stock chart needs special processing. Create one 'big' series with
+ data sequences of different roles. */
+ if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
+ CreateStockSeries( xChartType, nApiAxesSetIdx );
+ else
+ CreateDataSeries( xChartType, nApiAxesSetIdx );
+
+ return xChartType;
+}
+
+Reference< XLabeledDataSequence > XclImpChTypeGroup::CreateCategSequence() const
+{
+ Reference< XLabeledDataSequence > xLabeledSeq;
+ // create category sequence from first visible series
+ if( mxFirstSeries )
+ xLabeledSeq = mxFirstSeries->CreateCategSequence( EXC_CHPROP_ROLE_CATEG );
+ return xLabeledSeq;
+}
+
+void XclImpChTypeGroup::ReadChDropBar( XclImpStream& rStrm )
+{
+ if (m_DropBars.find(EXC_CHDROPBAR_UP) == m_DropBars.end())
+ {
+ unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_UP));
+ p->ReadRecordGroup(rStrm);
+ m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_UP, std::move(p)));
+ }
+ else if (m_DropBars.find(EXC_CHDROPBAR_DOWN) == m_DropBars.end())
+ {
+ unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_DOWN));
+ p->ReadRecordGroup(rStrm);
+ m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_DOWN, std::move(p)));
+ }
+}
+
+void XclImpChTypeGroup::ReadChChartLine( XclImpStream& rStrm )
+{
+ sal_uInt16 nLineId = rStrm.ReaduInt16();
+ if( (rStrm.GetNextRecId() == EXC_ID_CHLINEFORMAT) && rStrm.StartNextRecord() )
+ {
+ XclImpChLineFormat aLineFmt;
+ aLineFmt.ReadChLineFormat( rStrm );
+ m_ChartLines[ nLineId ] = aLineFmt;
+ }
+}
+
+void XclImpChTypeGroup::ReadChDataFormat( XclImpStream& rStrm )
+{
+ // global series and data point format
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->ReadRecordGroup( rStrm );
+ const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
+ if( (rPos.mnSeriesIdx == 0) && (rPos.mnPointIdx == 0) &&
+ (xDataFmt->GetFormatIdx() == EXC_CHDATAFORMAT_DEFAULT) )
+ mxGroupFmt = xDataFmt;
+}
+
+void XclImpChTypeGroup::InsertDataSeries( Reference< XChartType > const & xChartType,
+ Reference< XDataSeries > const & xSeries, sal_Int32 nApiAxesSetIdx ) const
+{
+ Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
+ if( !(xSeriesCont.is() && xSeries.is()) )
+ return;
+
+ // series stacking mode
+ cssc2::StackingDirection eStacking = cssc2::StackingDirection_NO_STACKING;
+ // stacked overrides deep-3d
+ if( maType.IsStacked() || maType.IsPercent() )
+ eStacking = cssc2::StackingDirection_Y_STACKING;
+ else if( Is3dDeepChart() )
+ eStacking = cssc2::StackingDirection_Z_STACKING;
+
+ // additional series properties
+ ScfPropertySet aSeriesProp( xSeries );
+ aSeriesProp.SetProperty( EXC_CHPROP_STACKINGDIR, eStacking );
+ aSeriesProp.SetProperty( EXC_CHPROP_ATTAXISINDEX, nApiAxesSetIdx );
+
+ // insert series into container
+ try
+ {
+ xSeriesCont->addDataSeries( xSeries );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChTypeGroup::InsertDataSeries - cannot add data series" );
+ }
+}
+
+void XclImpChTypeGroup::CreateDataSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
+{
+ bool bSpline = false;
+ for (auto const& elem : maSeries)
+ {
+ Reference< XDataSeries > xDataSeries = elem->CreateDataSeries();
+ InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
+ bSpline |= elem->HasSpline();
+ }
+ // spline - TODO: set at single series (#i66858#)
+ if( bSpline && !maTypeInfo.IsSeriesFrameFormat() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) )
+ {
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetProperty( EXC_CHPROP_CURVESTYLE, css::chart2::CurveStyle_CUBIC_SPLINES );
+ }
+}
+
+void XclImpChTypeGroup::CreateStockSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
+{
+ // create the data series object
+ Reference< XDataSeries > xDataSeries( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
+ Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
+ if( !xDataSink.is() )
+ return;
+
+ // create a list of data sequences from all series
+ ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
+ OSL_ENSURE( maSeries.size() >= 3, "XclImpChTypeGroup::CreateChartType - missing stock series" );
+ int nRoleIdx = (maSeries.size() == 3) ? 1 : 0;
+ for( const auto& rxSeries : maSeries )
+ {
+ // create a data sequence with a specific role
+ OUString aRole;
+ switch( nRoleIdx )
+ {
+ case 0: aRole = EXC_CHPROP_ROLE_OPENVALUES; break;
+ case 1: aRole = EXC_CHPROP_ROLE_HIGHVALUES; break;
+ case 2: aRole = EXC_CHPROP_ROLE_LOWVALUES; break;
+ case 3: aRole = EXC_CHPROP_ROLE_CLOSEVALUES; break;
+ }
+ Reference< XLabeledDataSequence > xDataSeq = rxSeries->CreateValueSequence( aRole );
+ if( xDataSeq.is() )
+ aLabeledSeqVec.push_back( xDataSeq );
+ ++nRoleIdx;
+ if (nRoleIdx >= 4)
+ break;
+ }
+
+ // attach labeled data sequences to series and insert series into chart type
+ xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
+
+ // formatting of special stock chart elements
+ ScfPropertySet aTypeProp( xChartType );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_JAPANESE, HasDropBars() );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWFIRST, HasDropBars() );
+ aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWHIGHLOW, true );
+ // hi-lo line format
+ XclImpChLineFormatMap::const_iterator aHiLoLine = m_ChartLines.find( EXC_CHCHARTLINE_HILO );
+ if (aHiLoLine != m_ChartLines.end())
+ {
+ ScfPropertySet aSeriesProp( xDataSeries );
+ aHiLoLine->second.Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
+ }
+ // white dropbar format
+ XclImpChDropBarMap::const_iterator itr = m_DropBars.find(EXC_CHDROPBAR_UP);
+ Reference<XPropertySet> xWhitePropSet;
+ if (itr != m_DropBars.end() && aTypeProp.GetProperty(xWhitePropSet, EXC_CHPROP_WHITEDAY))
+ {
+ ScfPropertySet aBarProp( xWhitePropSet );
+ itr->second->Convert(GetChRoot(), aBarProp);
+ }
+ // black dropbar format
+ itr = m_DropBars.find(EXC_CHDROPBAR_DOWN);
+ Reference<XPropertySet> xBlackPropSet;
+ if (itr != m_DropBars.end() && aTypeProp.GetProperty(xBlackPropSet, EXC_CHPROP_BLACKDAY))
+ {
+ ScfPropertySet aBarProp( xBlackPropSet );
+ itr->second->Convert(GetChRoot(), aBarProp);
+ }
+
+ // insert the series into the chart type object
+ InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
+}
+
+// Axes =======================================================================
+
+XclImpChLabelRange::XclImpChLabelRange( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChLabelRange::ReadChLabelRange( XclImpStream& rStrm )
+{
+ maLabelData.mnCross = rStrm.ReaduInt16();
+ maLabelData.mnLabelFreq = rStrm.ReaduInt16();
+ maLabelData.mnTickFreq = rStrm.ReaduInt16();
+ maLabelData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChLabelRange::ReadChDateRange( XclImpStream& rStrm )
+{
+ maDateData.mnMinDate = rStrm.ReaduInt16();
+ maDateData.mnMaxDate = rStrm.ReaduInt16();
+ maDateData.mnMajorStep = rStrm.ReaduInt16();
+ maDateData.mnMajorUnit = rStrm.ReaduInt16();
+ maDateData.mnMinorStep = rStrm.ReaduInt16();
+ maDateData.mnMinorUnit = rStrm.ReaduInt16();
+ maDateData.mnBaseUnit = rStrm.ReaduInt16();
+ maDateData.mnCross = rStrm.ReaduInt16();
+ maDateData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChLabelRange::Convert( ScfPropertySet& rPropSet, ScaleData& rScaleData, bool bMirrorOrient ) const
+{
+ // automatic axis type detection
+ rScaleData.AutoDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE );
+
+ // the flag EXC_CHDATERANGE_DATEAXIS specifies whether this is a date axis
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS ) )
+ {
+ /* Chart2 requires axis type CATEGORY for automatic category/date axis
+ (even if it is a date axis currently). */
+ rScaleData.AxisType = rScaleData.AutoDateAxis ? cssc2::AxisType::CATEGORY : cssc2::AxisType::DATE;
+ rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
+ /* Min/max values depend on base time unit, they specify the number of
+ days, months, or years starting from null date. */
+ lclConvertTimeValue( GetRoot(), rScaleData.Minimum, maDateData.mnMinDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN ), maDateData.mnBaseUnit );
+ lclConvertTimeValue( GetRoot(), rScaleData.Maximum, maDateData.mnMaxDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX ), maDateData.mnBaseUnit );
+ // increment
+ cssc::TimeIncrement& rTimeIncrement = rScaleData.TimeIncrement;
+ lclConvertTimeInterval( rTimeIncrement.MajorTimeInterval, maDateData.mnMajorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR ), maDateData.mnMajorUnit );
+ lclConvertTimeInterval( rTimeIncrement.MinorTimeInterval, maDateData.mnMinorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR ), maDateData.mnMinorUnit );
+ // base unit
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE ) )
+ rTimeIncrement.TimeResolution.clear();
+ else
+ rTimeIncrement.TimeResolution <<= lclGetApiTimeUnit( maDateData.mnBaseUnit );
+ }
+ else
+ {
+ // do not overlap text unless all labels are visible
+ rPropSet.SetBoolProperty( EXC_CHPROP_TEXTOVERLAP, maLabelData.mnLabelFreq == 1 );
+ // do not break text into several lines unless all labels are visible
+ rPropSet.SetBoolProperty( EXC_CHPROP_TEXTBREAK, maLabelData.mnLabelFreq == 1 );
+ // do not stagger labels in two lines
+ rPropSet.SetProperty( EXC_CHPROP_ARRANGEORDER, cssc::ChartAxisArrangeOrderType_SIDE_BY_SIDE );
+ }
+
+ // reverse order
+ bool bReverse = ::get_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE ) != bMirrorOrient;
+ rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
+
+ //TODO #i58731# show n-th category
+}
+
+void XclImpChLabelRange::ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const
+{
+ /* Crossing mode (max-cross flag overrides other crossing settings). Excel
+ does not move the Y axis in 3D charts, regardless of actual settings.
+ But: the Y axis has to be moved to "end", if the X axis is mirrored,
+ to keep it at the left end of the chart. */
+ bool bMaxCross = ::get_flag( maLabelData.mnFlags, b3dChart ? EXC_CHLABELRANGE_REVERSE : EXC_CHLABELRANGE_MAXCROSS );
+ cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
+
+ // crossing position (depending on axis type text/date)
+ if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS ) )
+ {
+ bool bAutoCross = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
+ /* Crossing position value depends on base time unit, it specifies the
+ number of days, months, or years from null date. Note that Excel
+ 2007/2010 write broken BIFF8 files, they always stores the number
+ of days regardless of the base time unit (and they are reading it
+ the same way, thus wrongly displaying files written by Excel
+ 97-2003). This filter sticks to the correct behaviour of Excel
+ 97-2003. */
+ double fCrossingPos = bAutoCross ? 1.0 : lclGetSerialDay( GetRoot(), maDateData.mnCross, maDateData.mnBaseUnit );
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+ }
+ else
+ {
+ double fCrossingPos = b3dChart ? 1.0 : maLabelData.mnCross;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+ }
+}
+
+XclImpChValueRange::XclImpChValueRange( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChValueRange::ReadChValueRange( XclImpStream& rStrm )
+{
+ maData.mfMin = rStrm.ReadDouble();
+ maData.mfMax = rStrm.ReadDouble();
+ maData.mfMajorStep = rStrm.ReadDouble();
+ maData.mfMinorStep = rStrm.ReadDouble();
+ maData.mfCross = rStrm.ReadDouble();
+ maData.mnFlags = rStrm.ReaduInt16();
+}
+
+void XclImpChValueRange::Convert( ScaleData& rScaleData, bool bMirrorOrient ) const
+{
+ // scaling algorithm
+ const bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
+ if( bLogScale )
+ rScaleData.Scaling = css::chart2::LogarithmicScaling::create( comphelper::getProcessComponentContext() );
+ else
+ rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
+
+ // min/max
+ lclSetExpValueOrClearAny( rScaleData.Minimum, maData.mfMin, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN ) );
+ lclSetExpValueOrClearAny( rScaleData.Maximum, maData.mfMax, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX ) );
+
+ // increment
+ bool bAutoMajor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR );
+ bool bAutoMinor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR );
+ // major increment
+ IncrementData& rIncrementData = rScaleData.IncrementData;
+ lclSetValueOrClearAny( rIncrementData.Distance, maData.mfMajorStep, bAutoMajor );
+ // minor increment
+ Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
+ rSubIncrementSeq.realloc( 1 );
+ Any& rIntervalCount = rSubIncrementSeq.getArray()[ 0 ].IntervalCount;
+ rIntervalCount.clear();
+ if( bLogScale )
+ {
+ if( !bAutoMinor )
+ rIntervalCount <<= sal_Int32( 9 );
+ }
+ else if( !bAutoMajor && !bAutoMinor && (0.0 < maData.mfMinorStep) && (maData.mfMinorStep <= maData.mfMajorStep) )
+ {
+ double fCount = maData.mfMajorStep / maData.mfMinorStep + 0.5;
+ if( (1.0 <= fCount) && (fCount < 1001.0) )
+ rIntervalCount <<= static_cast< sal_Int32 >( fCount );
+ }
+ else if( bAutoMinor )
+ {
+ // tdf#114168 If minor unit is not set then set interval to 5, as MS Excel do.
+ rIntervalCount <<= static_cast< sal_Int32 >( 5 );
+ }
+
+ // reverse order
+ bool bReverse = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE ) != bMirrorOrient;
+ rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
+}
+
+void XclImpChValueRange::ConvertAxisPosition( ScfPropertySet& rPropSet ) const
+{
+ bool bMaxCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS );
+ bool bAutoCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
+ bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
+
+ // crossing mode (max-cross flag overrides other crossing settings)
+ cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
+
+ // crossing position
+ double fCrossingPos = bAutoCross ? 0.0 : maData.mfCross;
+ if( bLogScale ) fCrossingPos = pow( 10.0, fCrossingPos );
+ rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
+}
+
+namespace {
+
+sal_Int32 lclGetApiTickmarks( sal_uInt8 nXclTickPos )
+{
+ using namespace ::com::sun::star::chart2::TickmarkStyle;
+ sal_Int32 nApiTickmarks = css::chart2::TickmarkStyle::NONE;
+ ::set_flag( nApiTickmarks, INNER, ::get_flag( nXclTickPos, EXC_CHTICK_INSIDE ) );
+ ::set_flag( nApiTickmarks, OUTER, ::get_flag( nXclTickPos, EXC_CHTICK_OUTSIDE ) );
+ return nApiTickmarks;
+}
+
+cssc::ChartAxisLabelPosition lclGetApiLabelPosition( sal_Int8 nXclLabelPos )
+{
+ using namespace ::com::sun::star::chart;
+ switch( nXclLabelPos )
+ {
+ case EXC_CHTICK_LOW: return ChartAxisLabelPosition_OUTSIDE_START;
+ case EXC_CHTICK_HIGH: return ChartAxisLabelPosition_OUTSIDE_END;
+ case EXC_CHTICK_NEXT: return ChartAxisLabelPosition_NEAR_AXIS;
+ }
+ return ChartAxisLabelPosition_NEAR_AXIS;
+}
+
+} // namespace
+
+XclImpChTick::XclImpChTick( const XclImpChRoot& rRoot ) :
+ XclImpChRoot( rRoot )
+{
+}
+
+void XclImpChTick::ReadChTick( XclImpStream& rStrm )
+{
+ maData.mnMajor = rStrm.ReaduInt8();
+ maData.mnMinor = rStrm.ReaduInt8();
+ maData.mnLabelPos = rStrm.ReaduInt8();
+ maData.mnBackMode = rStrm.ReaduInt8();
+ rStrm.Ignore( 16 );
+ rStrm >> maData.maTextColor;
+ maData.mnFlags = rStrm.ReaduInt16();
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ // BIFF8: index into palette used instead of RGB data
+ maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() );
+ // rotation
+ maData.mnRotation = rStrm.ReaduInt16();
+ }
+ else
+ {
+ // BIFF2-BIFF7: get rotation from text orientation
+ sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 2, 3 );
+ maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient );
+ }
+}
+
+Color XclImpChTick::GetFontColor() const
+{
+ return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR ) ? GetFontAutoColor() : maData.maTextColor;
+}
+
+sal_uInt16 XclImpChTick::GetRotation() const
+{
+ /* n#720443: Ignore auto-rotation if there is a suggested rotation.
+ * Better fix would be to improve our axis auto rotation algorithm.
+ */
+ if( maData.mnRotation != EXC_ROT_NONE )
+ return maData.mnRotation;
+ return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOROT ) ? EXC_CHART_AUTOROTATION : maData.mnRotation;
+}
+
+void XclImpChTick::Convert( ScfPropertySet& rPropSet ) const
+{
+ rPropSet.SetProperty( EXC_CHPROP_MAJORTICKS, lclGetApiTickmarks( maData.mnMajor ) );
+ rPropSet.SetProperty( EXC_CHPROP_MINORTICKS, lclGetApiTickmarks( maData.mnMinor ) );
+ rPropSet.SetProperty( EXC_CHPROP_LABELPOSITION, lclGetApiLabelPosition( maData.mnLabelPos ) );
+ rPropSet.SetProperty( EXC_CHPROP_MARKPOSITION, cssc::ChartAxisMarkPosition_AT_AXIS );
+}
+
+XclImpChAxis::XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType ) :
+ XclImpChRoot( rRoot ),
+ mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
+{
+ maData.mnType = nAxisType;
+}
+
+void XclImpChAxis::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnType = rStrm.ReaduInt16();
+}
+
+void XclImpChAxis::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHLABELRANGE:
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ mxLabelRange->ReadChLabelRange( rStrm );
+ break;
+ case EXC_ID_CHDATERANGE:
+ if( !mxLabelRange )
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ mxLabelRange->ReadChDateRange( rStrm );
+ break;
+ case EXC_ID_CHVALUERANGE:
+ mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
+ mxValueRange->ReadChValueRange( rStrm );
+ break;
+ case EXC_ID_CHFORMAT:
+ mnNumFmtIdx = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_CHTICK:
+ mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
+ mxTick->ReadChTick( rStrm );
+ break;
+ case EXC_ID_CHFONT:
+ mxFont = std::make_shared<XclImpChFont>();
+ mxFont->ReadChFont( rStrm );
+ break;
+ case EXC_ID_CHAXISLINE:
+ ReadChAxisLine( rStrm );
+ break;
+ }
+}
+
+void XclImpChAxis::Finalize()
+{
+ // add default scaling, needed e.g. to adjust rotation direction of pie and radar charts
+ if( !mxLabelRange )
+ mxLabelRange = std::make_shared<XclImpChLabelRange>( GetChRoot() );
+ if( !mxValueRange )
+ mxValueRange = std::make_shared<XclImpChValueRange>( GetChRoot() );
+ // remove invisible grid lines completely
+ if( mxMajorGrid && !mxMajorGrid->HasLine() )
+ mxMajorGrid.clear();
+ if( mxMinorGrid && !mxMinorGrid->HasLine() )
+ mxMinorGrid.clear();
+ // default tick settings different in OOChart and Excel
+ if( !mxTick )
+ mxTick = std::make_shared<XclImpChTick>( GetChRoot() );
+ // #i4140# different default axis line color
+ if( !mxAxisLine )
+ {
+ XclChLineFormat aLineFmt;
+ // set "show axis" flag, default if line format record is missing
+ ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS );
+ mxAxisLine = new XclImpChLineFormat( aLineFmt );
+ }
+ // add wall/floor frame for 3d charts
+ if( !mxWallFrame )
+ CreateWallFrame();
+}
+
+sal_uInt16 XclImpChAxis::GetFontIndex() const
+{
+ return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+Color XclImpChAxis::GetFontColor() const
+{
+ return mxTick ? mxTick->GetFontColor() : GetFontAutoColor();
+}
+
+sal_uInt16 XclImpChAxis::GetRotation() const
+{
+ return mxTick ? mxTick->GetRotation() : EXC_CHART_AUTOROTATION;
+}
+
+Reference< XAxis > XclImpChAxis::CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const
+{
+ // create the axis object (always)
+ Reference< XAxis > xAxis( ScfApiHelper::CreateInstance( SERVICE_CHART2_AXIS ), UNO_QUERY );
+ if( xAxis.is() )
+ {
+ ScfPropertySet aAxisProp( xAxis );
+ // #i58688# axis enabled
+ aAxisProp.SetBoolProperty( EXC_CHPROP_SHOW, !mxAxisLine || mxAxisLine->IsShowAxis() );
+
+ // axis line properties
+ if( mxAxisLine )
+ mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
+ // axis ticks properties
+ if( mxTick )
+ mxTick->Convert( aAxisProp );
+
+ // axis caption text --------------------------------------------------
+
+ // radar charts disable their category labels via chart type, not via axis
+ bool bHasLabels = (!mxTick || mxTick->HasLabels()) &&
+ ((GetAxisType() != EXC_CHAXIS_X) || rTypeGroup.HasCategoryLabels());
+ aAxisProp.SetBoolProperty( EXC_CHPROP_DISPLAYLABELS, bHasLabels );
+ if( bHasLabels )
+ {
+ // font settings from CHFONT record or from default text
+ if( mxFont )
+ ConvertFontBase( GetChRoot(), aAxisProp );
+ else if( const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISLABEL ) )
+ pDefText->ConvertFont( aAxisProp );
+ // label text rotation
+ ConvertRotationBase( aAxisProp, true );
+ // number format
+ bool bLinkNumberFmtToSource = true;
+ if ( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
+ {
+ sal_uInt32 nScNumFmt = GetNumFmtBuffer().GetScFormat( mnNumFmtIdx );
+ if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT, static_cast< sal_Int32 >( nScNumFmt ) );
+ bLinkNumberFmtToSource = false;
+ }
+ }
+
+ aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT_LINKSRC, bLinkNumberFmtToSource );
+ }
+
+ // axis scaling and increment -----------------------------------------
+
+ const XclChExtTypeInfo& rTypeInfo = rTypeGroup.GetTypeInfo();
+ ScaleData aScaleData = xAxis->getScaleData();
+ // set axis type
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ if( rTypeInfo.mbCategoryAxis )
+ {
+ aScaleData.AxisType = cssc2::AxisType::CATEGORY;
+ aScaleData.Categories = rTypeGroup.CreateCategSequence();
+ }
+ else
+ aScaleData.AxisType = cssc2::AxisType::REALNUMBER;
+ break;
+ case EXC_CHAXIS_Y:
+ aScaleData.AxisType = rTypeGroup.IsPercent() ?
+ cssc2::AxisType::PERCENT : cssc2::AxisType::REALNUMBER;
+ break;
+ case EXC_CHAXIS_Z:
+ aScaleData.AxisType = cssc2::AxisType::SERIES;
+ break;
+ }
+ // axis scaling settings, dependent on axis type
+ switch( aScaleData.AxisType )
+ {
+ case cssc2::AxisType::CATEGORY:
+ case cssc2::AxisType::SERIES:
+ // #i71684# radar charts have reversed rotation direction
+ if (mxLabelRange)
+ mxLabelRange->Convert( aAxisProp, aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR );
+ else
+ SAL_WARN("sc.filter", "missing LabelRange");
+ break;
+ case cssc2::AxisType::REALNUMBER:
+ case cssc2::AxisType::PERCENT:
+ // #i85167# pie/donut charts have reversed rotation direction (at Y axis!)
+ if (mxValueRange)
+ mxValueRange->Convert( aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
+ else
+ SAL_WARN("sc.filter", "missing ValueRange");
+ break;
+ default:
+ OSL_FAIL( "XclImpChAxis::CreateAxis - unknown axis type" );
+ }
+
+ /* Do not set a value to the Origin member anymore (will be done via
+ new axis properties 'CrossoverPosition' and 'CrossoverValue'). */
+ aScaleData.Origin.clear();
+
+ // write back
+ xAxis->setScaleData( aScaleData );
+
+ // grid ---------------------------------------------------------------
+
+ // main grid
+ ScfPropertySet aGridProp( xAxis->getGridProperties() );
+ aGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMajorGrid) );
+ if( mxMajorGrid )
+ mxMajorGrid->Convert( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ // sub grid
+ Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
+ if( aSubGridPropSeq.hasElements() )
+ {
+ ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
+ aSubGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMinorGrid) );
+ if( mxMinorGrid )
+ mxMinorGrid->Convert( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
+ }
+
+ // position of crossing axis ------------------------------------------
+
+ if( pCrossingAxis )
+ pCrossingAxis->ConvertAxisPosition( aAxisProp, rTypeGroup );
+ }
+ return xAxis;
+}
+
+void XclImpChAxis::ConvertWall( ScfPropertySet& rPropSet ) const
+{
+ // #i71810# walls and floor in 3D charts use the CHPICFORMAT record for bitmap mode
+ if( mxWallFrame )
+ mxWallFrame->Convert( rPropSet, true );
+}
+
+void XclImpChAxis::ConvertAxisPosition( ScfPropertySet& rPropSet, const XclImpChTypeGroup& rTypeGroup ) const
+{
+ if( ((GetAxisType() == EXC_CHAXIS_X) && rTypeGroup.GetTypeInfo().mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z) )
+ {
+ if (mxLabelRange)
+ mxLabelRange->ConvertAxisPosition( rPropSet, rTypeGroup.Is3dChart() );
+ else
+ SAL_WARN("sc.filter", "missing LabelRange");
+ }
+ else
+ {
+ if (mxValueRange)
+ mxValueRange->ConvertAxisPosition( rPropSet );
+ else
+ SAL_WARN("sc.filter", "missing ValueRange");
+ }
+}
+
+void XclImpChAxis::ReadChAxisLine( XclImpStream& rStrm )
+{
+ XclImpChLineFormatRef* pxLineFmt = nullptr;
+ bool bWallFrame = false;
+ switch( rStrm.ReaduInt16() )
+ {
+ case EXC_CHAXISLINE_AXISLINE: pxLineFmt = &mxAxisLine; break;
+ case EXC_CHAXISLINE_MAJORGRID: pxLineFmt = &mxMajorGrid; break;
+ case EXC_CHAXISLINE_MINORGRID: pxLineFmt = &mxMinorGrid; break;
+ case EXC_CHAXISLINE_WALLS: bWallFrame = true; break;
+ }
+ if( bWallFrame )
+ CreateWallFrame();
+
+ bool bLoop = pxLineFmt || bWallFrame;
+ while( bLoop )
+ {
+ sal_uInt16 nRecId = rStrm.GetNextRecId();
+ bLoop = ((nRecId == EXC_ID_CHLINEFORMAT) ||
+ (nRecId == EXC_ID_CHAREAFORMAT) ||
+ (nRecId == EXC_ID_CHESCHERFORMAT))
+ && rStrm.StartNextRecord();
+ if( bLoop )
+ {
+ if( pxLineFmt && (nRecId == EXC_ID_CHLINEFORMAT) )
+ {
+ (*pxLineFmt) = new XclImpChLineFormat();
+ (*pxLineFmt)->ReadChLineFormat( rStrm );
+ }
+ else if( bWallFrame && mxWallFrame )
+ {
+ mxWallFrame->ReadSubRecord( rStrm );
+ }
+ }
+ }
+}
+
+void XclImpChAxis::CreateWallFrame()
+{
+ switch( GetAxisType() )
+ {
+ case EXC_CHAXIS_X:
+ mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_WALL3D );
+ break;
+ case EXC_CHAXIS_Y:
+ mxWallFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_FLOOR3D );
+ break;
+ default:
+ mxWallFrame.reset();
+ }
+}
+
+XclImpChAxesSet::XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
+ XclImpChRoot( rRoot )
+{
+ maData.mnAxesSetId = nAxesSetId;
+}
+
+void XclImpChAxesSet::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ maData.mnAxesSetId = rStrm.ReaduInt16();
+ rStrm >> maData.maRect;
+}
+
+void XclImpChAxesSet::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAMEPOS:
+ mxFramePos = std::make_shared<XclImpChFramePos>();
+ mxFramePos->ReadChFramePos( rStrm );
+ break;
+ case EXC_ID_CHAXIS:
+ ReadChAxis( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ ReadChText( rStrm );
+ break;
+ case EXC_ID_CHPLOTFRAME:
+ ReadChPlotFrame( rStrm );
+ break;
+ case EXC_ID_CHTYPEGROUP:
+ ReadChTypeGroup( rStrm );
+ break;
+ }
+}
+
+void XclImpChAxesSet::Finalize()
+{
+ if( IsValidAxesSet() )
+ {
+ // finalize chart type groups, erase empty groups without series
+ XclImpChTypeGroupMap aValidGroups;
+ for (auto const& typeGroup : maTypeGroups)
+ {
+ XclImpChTypeGroupRef xTypeGroup = typeGroup.second;
+ xTypeGroup->Finalize();
+ if( xTypeGroup->IsValidGroup() )
+ aValidGroups.emplace(typeGroup.first, xTypeGroup);
+ }
+ maTypeGroups.swap( aValidGroups );
+ }
+
+ // invalid chart type groups are deleted now, check again with IsValidAxesSet()
+ if( !IsValidAxesSet() )
+ return;
+
+ // always create missing axis objects
+ if( !mxXAxis )
+ mxXAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_X );
+ if( !mxYAxis )
+ mxYAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Y );
+ if( !mxZAxis && GetFirstTypeGroup()->Is3dDeepChart() )
+ mxZAxis = std::make_shared<XclImpChAxis>( GetChRoot(), EXC_CHAXIS_Z );
+
+ // finalize axes
+ if( mxXAxis ) mxXAxis->Finalize();
+ if( mxYAxis ) mxYAxis->Finalize();
+ if( mxZAxis ) mxZAxis->Finalize();
+
+ // finalize axis titles
+ const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISTITLE );
+ OUString aAutoTitle(ScResId(STR_AXISTITLE));
+ lclFinalizeTitle( mxXAxisTitle, pDefText, aAutoTitle );
+ lclFinalizeTitle( mxYAxisTitle, pDefText, aAutoTitle );
+ lclFinalizeTitle( mxZAxisTitle, pDefText, aAutoTitle );
+
+ // #i47745# missing plot frame -> invisible border and area
+ if( !mxPlotFrame )
+ mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
+}
+
+XclImpChTypeGroupRef XclImpChAxesSet::GetTypeGroup( sal_uInt16 nGroupIdx ) const
+{
+ XclImpChTypeGroupMap::const_iterator itr = maTypeGroups.find(nGroupIdx);
+ return itr == maTypeGroups.end() ? XclImpChTypeGroupRef() : itr->second;
+}
+
+XclImpChTypeGroupRef XclImpChAxesSet::GetFirstTypeGroup() const
+{
+ XclImpChTypeGroupRef xTypeGroup;
+ if( !maTypeGroups.empty() )
+ xTypeGroup = maTypeGroups.begin()->second;
+ return xTypeGroup;
+}
+
+XclImpChLegendRef XclImpChAxesSet::GetLegend() const
+{
+ XclImpChLegendRef xLegend;
+ for( const auto& rEntry : maTypeGroups )
+ {
+ xLegend = rEntry.second->GetLegend();
+ if (xLegend)
+ break;
+ }
+ return xLegend;
+}
+
+OUString XclImpChAxesSet::GetSingleSeriesTitle() const
+{
+ return (maTypeGroups.size() == 1) ? maTypeGroups.begin()->second->GetSingleSeriesTitle() : OUString();
+}
+
+void XclImpChAxesSet::Convert( Reference< XDiagram > const & xDiagram ) const
+{
+ if( !(IsValidAxesSet() && xDiagram.is()) )
+ return;
+
+ // diagram background formatting
+ if( GetAxesSetId() == EXC_CHAXESSET_PRIMARY )
+ ConvertBackground( xDiagram );
+
+ // create the coordinate system, this inserts all chart types and series
+ Reference< XCoordinateSystem > xCoordSystem = CreateCoordSystem( xDiagram );
+ if( !xCoordSystem.is() )
+ return;
+
+ // insert coordinate system, if not already done
+ try
+ {
+ Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY_THROW );
+ Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
+ if( !aCoordSystems.hasElements() )
+ xCoordSystemCont->addCoordinateSystem( xCoordSystem );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::Convert - cannot insert coordinate system" );
+ }
+
+ // create the axes with grids and axis titles and insert them into the diagram
+ ConvertAxis( mxXAxis, mxXAxisTitle, xCoordSystem, mxYAxis.get() );
+ ConvertAxis( mxYAxis, mxYAxisTitle, xCoordSystem, mxXAxis.get() );
+ ConvertAxis( mxZAxis, mxZAxisTitle, xCoordSystem, nullptr );
+}
+
+void XclImpChAxesSet::ConvertTitlePositions() const
+{
+ if( mxXAxisTitle )
+ mxXAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_X ) );
+ if( mxYAxisTitle )
+ mxYAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Y ) );
+ if( mxZAxisTitle )
+ mxZAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Z ) );
+}
+
+void XclImpChAxesSet::ReadChAxis( XclImpStream& rStrm )
+{
+ XclImpChAxisRef xAxis = std::make_shared<XclImpChAxis>( GetChRoot() );
+ xAxis->ReadRecordGroup( rStrm );
+
+ switch( xAxis->GetAxisType() )
+ {
+ case EXC_CHAXIS_X: mxXAxis = xAxis; break;
+ case EXC_CHAXIS_Y: mxYAxis = xAxis; break;
+ case EXC_CHAXIS_Z: mxZAxis = xAxis; break;
+ }
+}
+
+void XclImpChAxesSet::ReadChText( XclImpStream& rStrm )
+{
+ XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
+ xText->ReadRecordGroup( rStrm );
+
+ switch( xText->GetLinkTarget() )
+ {
+ case EXC_CHOBJLINK_XAXIS: mxXAxisTitle = xText; break;
+ case EXC_CHOBJLINK_YAXIS: mxYAxisTitle = xText; break;
+ case EXC_CHOBJLINK_ZAXIS: mxZAxisTitle = xText; break;
+ }
+}
+
+void XclImpChAxesSet::ReadChPlotFrame( XclImpStream& rStrm )
+{
+ if( (rStrm.GetNextRecId() == EXC_ID_CHFRAME) && rStrm.StartNextRecord() )
+ {
+ mxPlotFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME );
+ mxPlotFrame->ReadRecordGroup( rStrm );
+ }
+}
+
+void XclImpChAxesSet::ReadChTypeGroup( XclImpStream& rStrm )
+{
+ XclImpChTypeGroupRef xTypeGroup = std::make_shared<XclImpChTypeGroup>( GetChRoot() );
+ xTypeGroup->ReadRecordGroup( rStrm );
+ sal_uInt16 nGroupIdx = xTypeGroup->GetGroupIdx();
+ XclImpChTypeGroupMap::iterator itr = maTypeGroups.lower_bound(nGroupIdx);
+ if (itr != maTypeGroups.end() && !maTypeGroups.key_comp()(nGroupIdx, itr->first))
+ // Overwrite the existing element.
+ itr->second = xTypeGroup;
+ else
+ maTypeGroups.insert(
+ itr, XclImpChTypeGroupMap::value_type(nGroupIdx, xTypeGroup));
+}
+
+Reference< XCoordinateSystem > XclImpChAxesSet::CreateCoordSystem( Reference< XDiagram > const & xDiagram ) const
+{
+ Reference< XCoordinateSystem > xCoordSystem;
+
+ /* Try to get existing coordinate system. For now, all series from primary
+ and secondary axes sets are inserted into one coordinate system. Later,
+ this should be changed to use one coordinate system for each axes set. */
+ Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY );
+ if( xCoordSystemCont.is() )
+ {
+ Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
+ OSL_ENSURE( aCoordSystems.getLength() <= 1, "XclImpChAxesSet::CreateCoordSystem - too many existing coordinate systems" );
+ if( aCoordSystems.hasElements() )
+ xCoordSystem = aCoordSystems[ 0 ];
+ }
+
+ // create the coordinate system according to the first chart type
+ if( !xCoordSystem.is() )
+ {
+ XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup )
+ {
+ xCoordSystem = xTypeGroup->CreateCoordSystem();
+ // convert 3d chart settings
+ ScfPropertySet aDiaProp( xDiagram );
+ xTypeGroup->ConvertChart3d( aDiaProp );
+ }
+ }
+
+ /* Create XChartType objects for all chart type groups. Each group will
+ add its series to the data provider attached to the chart document. */
+ Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
+ if( xChartTypeCont.is() )
+ {
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ for( const auto& rEntry : maTypeGroups )
+ {
+ try
+ {
+ Reference< XChartType > xChartType = rEntry.second->CreateChartType( xDiagram, nApiAxesSetIdx );
+ if( xChartType.is() )
+ xChartTypeCont->addChartType( xChartType );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::CreateCoordSystem - cannot add chart type" );
+ }
+ }
+ }
+
+ return xCoordSystem;
+}
+
+void XclImpChAxesSet::ConvertAxis(
+ XclImpChAxisRef const & xChAxis, XclImpChTextRef const & xChAxisTitle,
+ Reference< XCoordinateSystem > const & xCoordSystem, const XclImpChAxis* pCrossingAxis ) const
+{
+ if( !xChAxis )
+ return;
+
+ // create and attach the axis object
+ Reference< XAxis > xAxis = CreateAxis( *xChAxis, pCrossingAxis );
+ if( !xAxis.is() )
+ return;
+
+ // create and attach the axis title
+ if( xChAxisTitle ) try
+ {
+ Reference< XTitled > xTitled( xAxis, UNO_QUERY_THROW );
+ Reference< XTitle > xTitle( xChAxisTitle->CreateTitle(), UNO_SET_THROW );
+ xTitled->setTitleObject( xTitle );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis title" );
+ }
+
+ // insert axis into coordinate system
+ try
+ {
+ sal_Int32 nApiAxisDim = xChAxis->GetApiAxisDimension();
+ sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
+ xCoordSystem->setAxisByDimension( nApiAxisDim, xAxis, nApiAxesSetIdx );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis" );
+ }
+}
+
+Reference< XAxis > XclImpChAxesSet::CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const
+{
+ Reference< XAxis > xAxis;
+ if( const XclImpChTypeGroup* pTypeGroup = GetFirstTypeGroup().get() )
+ xAxis = rChAxis.CreateAxis( *pTypeGroup, pCrossingAxis );
+ return xAxis;
+}
+
+void XclImpChAxesSet::ConvertBackground( Reference< XDiagram > const & xDiagram ) const
+{
+ XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
+ if( xTypeGroup && xTypeGroup->Is3dWallChart() )
+ {
+ // wall/floor formatting (3D charts)
+ if( mxXAxis )
+ {
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxXAxis->ConvertWall( aWallProp );
+ }
+ if( mxYAxis )
+ {
+ ScfPropertySet aFloorProp( xDiagram->getFloor() );
+ mxYAxis->ConvertWall( aFloorProp );
+ }
+ }
+ else if( mxPlotFrame )
+ {
+ // diagram background formatting
+ ScfPropertySet aWallProp( xDiagram->getWall() );
+ mxPlotFrame->Convert( aWallProp );
+ }
+}
+
+// The chart object ===========================================================
+
+XclImpChChart::XclImpChChart( const XclImpRoot& rRoot ) :
+ XclImpChRoot( rRoot, *this )
+{
+ mxPrimAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
+ mxSecnAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
+}
+
+XclImpChChart::~XclImpChChart()
+{
+}
+
+void XclImpChChart::ReadHeaderRecord( XclImpStream& rStrm )
+{
+ // coordinates are stored as 16.16 fixed point
+ rStrm >> maRect;
+}
+
+void XclImpChChart::ReadSubRecord( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_CHFRAME:
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ mxFrame->ReadRecordGroup( rStrm );
+ break;
+ case EXC_ID_CHSERIES:
+ ReadChSeries( rStrm );
+ break;
+ case EXC_ID_CHPROPERTIES:
+ ReadChProperties( rStrm );
+ break;
+ case EXC_ID_CHDEFAULTTEXT:
+ ReadChDefaultText( rStrm );
+ break;
+ case EXC_ID_CHAXESSET:
+ ReadChAxesSet( rStrm );
+ break;
+ case EXC_ID_CHTEXT:
+ ReadChText( rStrm );
+ break;
+ case EXC_ID_CHEND:
+ Finalize(); // finalize the entire chart object
+ break;
+ }
+}
+
+void XclImpChChart::ReadChDefaultText( XclImpStream& rStrm )
+{
+ sal_uInt16 nTextId = rStrm.ReaduInt16();
+ if( (rStrm.GetNextRecId() == EXC_ID_CHTEXT) && rStrm.StartNextRecord() )
+ {
+ unique_ptr<XclImpChText> pText(new XclImpChText(GetChRoot()));
+ pText->ReadRecordGroup(rStrm);
+ m_DefTexts.insert(std::make_pair(nTextId, std::move(pText)));
+ }
+}
+
+void XclImpChChart::ReadChDataFormat( XclImpStream& rStrm )
+{
+ XclImpChDataFormatRef xDataFmt = std::make_shared<XclImpChDataFormat>( GetChRoot() );
+ xDataFmt->ReadRecordGroup( rStrm );
+ if( xDataFmt->GetPointPos().mnSeriesIdx <= EXC_CHSERIES_MAXSERIES )
+ {
+ const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
+ XclImpChDataFormatMap::iterator itr = maDataFmts.lower_bound(rPos);
+ if (itr == maDataFmts.end() || maDataFmts.key_comp()(rPos, itr->first))
+ // No element exists for this data point. Insert it.
+ maDataFmts.insert(
+ itr, XclImpChDataFormatMap::value_type(rPos, xDataFmt));
+
+ /* Do not overwrite existing data format group, Excel always uses the
+ first data format group occurring in any CHSERIES group. */
+ }
+}
+
+void XclImpChChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ mxFrame->UpdateObjFrame( rLineData, rFillData );
+}
+
+XclImpChTypeGroupRef XclImpChChart::GetTypeGroup( sal_uInt16 nGroupIdx ) const
+{
+ XclImpChTypeGroupRef xTypeGroup = mxPrimAxesSet->GetTypeGroup( nGroupIdx );
+ if( !xTypeGroup ) xTypeGroup = mxSecnAxesSet->GetTypeGroup( nGroupIdx );
+ if( !xTypeGroup ) xTypeGroup = mxPrimAxesSet->GetFirstTypeGroup();
+ return xTypeGroup;
+}
+
+const XclImpChText* XclImpChChart::GetDefaultText( XclChTextType eTextType ) const
+{
+ sal_uInt16 nDefTextId = EXC_CHDEFTEXT_GLOBAL;
+ bool bBiff8 = GetBiff() == EXC_BIFF8;
+ switch( eTextType )
+ {
+ case EXC_CHTEXTTYPE_TITLE: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_LEGEND: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_AXISTITLE: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_AXISLABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ case EXC_CHTEXTTYPE_DATALABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
+ }
+
+ XclImpChTextMap::const_iterator const itr = m_DefTexts.find(nDefTextId);
+ return itr == m_DefTexts.end() ? nullptr : itr->second.get();
+}
+
+bool XclImpChChart::IsManualPlotArea() const
+{
+ // there is no real automatic mode in BIFF5 charts
+ return (GetBiff() <= EXC_BIFF5) || ::get_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
+}
+
+void XclImpChChart::Convert( const Reference<XChartDocument>& xChartDoc,
+ XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
+{
+ // initialize conversion (locks the model to suppress any internal updates)
+ InitConversion( xChartDoc, rChartRect );
+
+ // chart frame formatting
+ if( mxFrame )
+ {
+ ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
+ mxFrame->Convert( aFrameProp );
+ }
+
+ // chart title
+ if( mxTitle ) try
+ {
+ Reference< XTitled > xTitled( xChartDoc, UNO_QUERY_THROW );
+ Reference< XTitle > xTitle( mxTitle->CreateTitle(), UNO_SET_THROW );
+ xTitled->setTitleObject( xTitle );
+ }
+ catch( Exception& )
+ {
+ }
+
+ /* Create the diagram object and attach it to the chart document. Currently,
+ one diagram is used to carry all coordinate systems and data series. */
+ Reference< XDiagram > xDiagram = CreateDiagram();
+ xChartDoc->setFirstDiagram( xDiagram );
+
+ // coordinate systems and chart types, convert axis settings
+ mxPrimAxesSet->Convert( xDiagram );
+ mxSecnAxesSet->Convert( xDiagram );
+
+ // legend
+ if( xDiagram.is() && mxLegend )
+ xDiagram->setLegend( mxLegend->CreateLegend() );
+
+ /* Following all conversions needing the old Chart1 API that involves full
+ initialization of the chart view. */
+ Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY );
+ if( xChart1Doc.is() )
+ {
+ Reference< cssc::XDiagram > xDiagram1 = xChart1Doc->getDiagram();
+
+ /* Set the 'IncludeHiddenCells' property via the old API as only this
+ ensures that the data provider and all created sequences get this
+ flag correctly. */
+ ScfPropertySet aDiaProp( xDiagram1 );
+ bool bShowVisCells = ::get_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY );
+ aDiaProp.SetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS, !bShowVisCells );
+
+ // plot area position and size (there is no real automatic mode in BIFF5 charts)
+ XclImpChFramePosRef xPlotAreaPos = mxPrimAxesSet->GetPlotAreaFramePos();
+ if( IsManualPlotArea() && xPlotAreaPos ) try
+ {
+ const XclChFramePos& rFramePos = xPlotAreaPos->GetFramePosData();
+ if( (rFramePos.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rFramePos.mnBRMode == EXC_CHFRAMEPOS_PARENT) )
+ {
+ Reference< cssc::XDiagramPositioning > xPositioning( xDiagram1, UNO_QUERY_THROW );
+ css::awt::Rectangle aDiagramRect = CalcHmmFromChartRect( rFramePos.maRect );
+ // for pie charts, always set inner plot area size to exclude the data labels as Excel does
+ const XclImpChTypeGroup* pFirstTypeGroup = mxPrimAxesSet->GetFirstTypeGroup().get();
+ if( pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE) )
+ xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
+ else if( pFirstTypeGroup && pFirstTypeGroup->Is3dChart() )
+ xPositioning->setDiagramPositionIncludingAxesAndAxisTitles( aDiagramRect );
+ else
+ xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ // positions of all title objects
+ if( mxTitle )
+ mxTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_TITLE ) );
+ mxPrimAxesSet->ConvertTitlePositions();
+ mxSecnAxesSet->ConvertTitlePositions();
+ }
+
+ // unlock the model
+ FinishConversion( rDffConv );
+
+ // start listening to this chart
+ ScDocument& rDoc = GetRoot().GetDoc();
+ ScChartListenerCollection* pChartCollection = rDoc.GetChartListenerCollection();
+ if(!pChartCollection)
+ return;
+
+ std::vector< ScTokenRef > aRefTokens;
+ for( const auto& rxSeries : maSeries )
+ rxSeries->FillAllSourceLinks( aRefTokens );
+ if( !aRefTokens.empty() )
+ {
+ ::std::unique_ptr< ScChartListener > xListener( new ScChartListener( rObjName, rDoc, std::move(aRefTokens) ) );
+ xListener->SetUsed( true );
+ xListener->StartListeningTo();
+ pChartCollection->insert( xListener.release() );
+ }
+}
+
+void XclImpChChart::ReadChSeries( XclImpStream& rStrm )
+{
+ sal_uInt16 nNewSeriesIdx = static_cast< sal_uInt16 >( maSeries.size() );
+ XclImpChSeriesRef xSeries = std::make_shared<XclImpChSeries>( GetChRoot(), nNewSeriesIdx );
+ xSeries->ReadRecordGroup( rStrm );
+ maSeries.push_back( xSeries );
+}
+
+void XclImpChChart::ReadChProperties( XclImpStream& rStrm )
+{
+ maProps.mnFlags = rStrm.ReaduInt16();
+ maProps.mnEmptyMode = rStrm.ReaduInt8();
+}
+
+void XclImpChChart::ReadChAxesSet( XclImpStream& rStrm )
+{
+ XclImpChAxesSetRef xAxesSet = std::make_shared<XclImpChAxesSet>( GetChRoot(), EXC_CHAXESSET_NONE );
+ xAxesSet->ReadRecordGroup( rStrm );
+ switch( xAxesSet->GetAxesSetId() )
+ {
+ case EXC_CHAXESSET_PRIMARY: mxPrimAxesSet = xAxesSet; break;
+ case EXC_CHAXESSET_SECONDARY: mxSecnAxesSet = xAxesSet; break;
+ }
+}
+
+void XclImpChChart::ReadChText( XclImpStream& rStrm )
+{
+ XclImpChTextRef xText = std::make_shared<XclImpChText>( GetChRoot() );
+ xText->ReadRecordGroup( rStrm );
+ switch( xText->GetLinkTarget() )
+ {
+ case EXC_CHOBJLINK_TITLE:
+ mxTitle = xText;
+ break;
+ case EXC_CHOBJLINK_DATA:
+ {
+ sal_uInt16 nSeriesIdx = xText->GetPointPos().mnSeriesIdx;
+ if( nSeriesIdx < maSeries.size() )
+ maSeries[ nSeriesIdx ]->SetDataLabel( xText );
+ }
+ break;
+ }
+}
+
+void XclImpChChart::Finalize()
+{
+ // finalize series (must be done first)
+ FinalizeSeries();
+ // #i49218# legend may be attached to primary or secondary axes set
+ mxLegend = mxPrimAxesSet->GetLegend();
+ if( !mxLegend )
+ mxLegend = mxSecnAxesSet->GetLegend();
+ if( mxLegend )
+ mxLegend->Finalize();
+ // axes sets, updates chart type group default formats -> must be called before FinalizeDataFormats()
+ mxPrimAxesSet->Finalize();
+ mxSecnAxesSet->Finalize();
+ // formatting of all series
+ FinalizeDataFormats();
+ // #i47745# missing frame -> invisible border and area
+ if( !mxFrame )
+ mxFrame = std::make_shared<XclImpChFrame>( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND );
+ // chart title
+ FinalizeTitle();
+}
+
+void XclImpChChart::FinalizeSeries()
+{
+ for( const XclImpChSeriesRef& xSeries : maSeries )
+ {
+ if( xSeries->HasParentSeries() )
+ {
+ /* Process child series (trend lines and error bars). Data of
+ child series will be set at the connected parent series. */
+ if( xSeries->GetParentIdx() < maSeries.size() )
+ maSeries[ xSeries->GetParentIdx() ]->AddChildSeries( *xSeries );
+ }
+ else
+ {
+ // insert the series into the related chart type group
+ if( XclImpChTypeGroup* pTypeGroup = GetTypeGroup( xSeries->GetGroupIdx() ).get() )
+ pTypeGroup->AddSeries( xSeries );
+ }
+ }
+}
+
+void XclImpChChart::FinalizeDataFormats()
+{
+ /* #i51639# (part 1): CHDATAFORMAT groups are part of CHSERIES groups.
+ Each CHDATAFORMAT group specifies the series and data point it is
+ assigned to. This makes it possible to have a data format that is
+ related to another series, e.g. a CHDATAFORMAT group for series 2 is
+ part of a CHSERIES group that describes series 1. Therefore the chart
+ itself has collected all CHDATAFORMAT groups to be able to store data
+ format groups for series that have not been imported at that time. This
+ loop finally assigns these groups to the related series. */
+ for( const auto& [rPos, rDataFmt] : maDataFmts )
+ {
+ sal_uInt16 nSeriesIdx = rPos.mnSeriesIdx;
+ if( nSeriesIdx < maSeries.size() )
+ maSeries[ nSeriesIdx ]->SetDataFormat( rDataFmt );
+ }
+
+ /* #i51639# (part 2): Finalize data formats of all series. This adds for
+ example missing CHDATAFORMAT groups for entire series that are needed
+ for automatic colors of lines and areas. */
+ for( auto& rxSeries : maSeries )
+ rxSeries->FinalizeDataFormats();
+}
+
+void XclImpChChart::FinalizeTitle()
+{
+ // special handling for auto-generated title
+ OUString aAutoTitle;
+ if( !mxTitle || (!mxTitle->IsDeleted() && !mxTitle->HasString()) )
+ {
+ // automatic title from first series name (if there are no series on secondary axes set)
+ if( !mxSecnAxesSet->IsValidAxesSet() )
+ aAutoTitle = mxPrimAxesSet->GetSingleSeriesTitle();
+ if( mxTitle || (!aAutoTitle.isEmpty()) )
+ {
+ if( !mxTitle )
+ mxTitle = std::make_shared<XclImpChText>( GetChRoot() );
+ if( aAutoTitle.isEmpty() )
+ aAutoTitle = ScResId(STR_CHARTTITLE);
+ }
+ }
+
+ // will reset mxTitle, if it does not contain a string and no auto title exists
+ lclFinalizeTitle( mxTitle, GetDefaultText( EXC_CHTEXTTYPE_TITLE ), aAutoTitle );
+}
+
+Reference< XDiagram > XclImpChChart::CreateDiagram() const
+{
+ // create a diagram object
+ Reference< XDiagram > xDiagram( ScfApiHelper::CreateInstance( SERVICE_CHART2_DIAGRAM ), UNO_QUERY );
+
+ // convert global chart settings
+ ScfPropertySet aDiaProp( xDiagram );
+
+ // treatment of missing values
+ using namespace cssc::MissingValueTreatment;
+ sal_Int32 nMissingValues = LEAVE_GAP;
+ switch( maProps.mnEmptyMode )
+ {
+ case EXC_CHPROPS_EMPTY_SKIP: nMissingValues = LEAVE_GAP; break;
+ case EXC_CHPROPS_EMPTY_ZERO: nMissingValues = USE_ZERO; break;
+ case EXC_CHPROPS_EMPTY_INTERPOLATE: nMissingValues = CONTINUE; break;
+ }
+ aDiaProp.SetProperty( EXC_CHPROP_MISSINGVALUETREATMENT, nMissingValues );
+
+ return xDiagram;
+}
+
+XclImpChartDrawing::XclImpChartDrawing( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpDrawing( rRoot, bOwnTab ), // sheet charts may contain OLE objects
+ mnScTab( rRoot.GetCurrScTab() ),
+ mbOwnTab( bOwnTab )
+{
+}
+
+void XclImpChartDrawing::ConvertObjects( XclImpDffConverter& rDffConv,
+ const Reference< XModel >& rxModel, const tools::Rectangle& rChartRect )
+{
+ maChartRect = rChartRect; // needed in CalcAnchorRect() callback
+
+ SdrModel* pSdrModel = nullptr;
+ SdrPage* pSdrPage = nullptr;
+ if( mbOwnTab )
+ {
+ // chart sheet: insert all shapes into the sheet, not into the chart object
+ pSdrModel = GetDoc().GetDrawLayer();
+ pSdrPage = GetSdrPage( mnScTab );
+ }
+ else
+ {
+ // embedded chart object: insert all shapes into the chart
+ try
+ {
+ Reference< XDrawPageSupplier > xDrawPageSupp( rxModel, UNO_QUERY_THROW );
+ Reference< XDrawPage > xDrawPage( xDrawPageSupp->getDrawPage(), UNO_SET_THROW );
+ pSdrPage = ::GetSdrPageFromXDrawPage( xDrawPage );
+ pSdrModel = pSdrPage ? &pSdrPage->getSdrModelFromSdrPage() : nullptr;
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ if( pSdrModel && pSdrPage )
+ ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
+}
+
+tools::Rectangle XclImpChartDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const
+{
+ /* In objects with DFF client anchor, the position of the shape is stored
+ in the cell address components of the client anchor. In old BIFF3-BIFF5
+ objects, the position is stored in the offset components of the anchor. */
+ tools::Rectangle aRect(
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnCol : rAnchor.mnLX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnRow : rAnchor.mnTY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnCol : rAnchor.mnRX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
+ static_cast< tools::Long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnRow : rAnchor.mnBY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ) );
+ aRect.Justify();
+ // move shapes into chart area for sheet charts
+ if( mbOwnTab )
+ aRect.Move( maChartRect.Left(), maChartRect.Top() );
+ return aRect;
+}
+
+void XclImpChartDrawing::OnObjectInserted( const XclImpDrawObjBase& )
+{
+}
+
+XclImpChart::XclImpChart( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpRoot( rRoot ),
+ mbOwnTab( bOwnTab ),
+ mbIsPivotChart( false )
+{
+}
+
+XclImpChart::~XclImpChart()
+{
+}
+
+void XclImpChart::ReadChartSubStream( XclImpStream& rStrm )
+{
+ XclImpPageSettings& rPageSett = GetPageSettings();
+ XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
+
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ // page settings - only for charts in entire sheet
+ if( mbOwnTab ) switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HORPAGEBREAKS:
+ case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( rStrm ); break;
+ case EXC_ID_HEADER:
+ case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( rStrm ); break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( rStrm ); break;
+ case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( rStrm ); break;
+ case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( rStrm ); break;
+ case EXC_ID_HCENTER:
+ case EXC_ID_VCENTER: rPageSett.ReadCenter( rStrm ); break;
+ case EXC_ID_SETUP: rPageSett.ReadSetup( rStrm ); break;
+ case EXC_ID8_IMGDATA: rPageSett.ReadImgData( rStrm ); break;
+
+ case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( rStrm, true );break;
+ case EXC_ID_SCL: rTabViewSett.ReadScl( rStrm ); break;
+
+ case EXC_ID_SHEETEXT: //0x0862
+ {
+ // FIXME: do not need to pass palette, XclImpTabVieSettings is derived from root
+ XclImpPalette& rPal = GetPalette();
+ rTabViewSett.ReadTabBgColor( rStrm, rPal);
+ }
+ break;
+
+ case EXC_ID_CODENAME: ReadCodeName( rStrm, false ); break;
+ }
+
+ // common records
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_EOF: bLoop = false; break;
+
+ // #i31882# ignore embedded chart objects
+ case EXC_ID2_BOF:
+ case EXC_ID3_BOF:
+ case EXC_ID4_BOF:
+ case EXC_ID5_BOF: XclTools::SkipSubStream( rStrm ); break;
+
+ case EXC_ID_CHCHART: ReadChChart( rStrm ); break;
+
+ case EXC_ID8_CHPIVOTREF:
+ GetTracer().TracePivotChartExists();
+ mbIsPivotChart = true;
+ break;
+
+ // BIFF specific records
+ default: switch( GetBiff() )
+ {
+ case EXC_BIFF5: switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
+ }
+ break;
+ case EXC_BIFF8: switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_MSODRAWING: GetChartDrawing().ReadMsoDrawing( rStrm ); break;
+ // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
+ case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
+ }
+ break;
+ default:;
+ }
+ }
+ }
+}
+
+void XclImpChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
+{
+ if( !mxChartData )
+ mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
+ mxChartData->UpdateObjFrame( rLineData, rFillData );
+}
+
+std::size_t XclImpChart::GetProgressSize() const
+{
+ return
+ (mxChartData ? XclImpChChart::GetProgressSize() : 0) +
+ (mxChartDrawing ? mxChartDrawing->GetProgressSize() : 0);
+}
+
+void XclImpChart::Convert( Reference< XModel > const & xModel, XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
+{
+ Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
+ if( xChartDoc.is() )
+ {
+ if( mxChartData )
+ mxChartData->Convert( xChartDoc, rDffConv, rObjName, rChartRect );
+ if( mxChartDrawing )
+ mxChartDrawing->ConvertObjects( rDffConv, xModel, rChartRect );
+ }
+}
+
+XclImpChartDrawing& XclImpChart::GetChartDrawing()
+{
+ if( !mxChartDrawing )
+ mxChartDrawing = std::make_shared<XclImpChartDrawing>( GetRoot(), mbOwnTab );
+ return *mxChartDrawing;
+}
+
+void XclImpChart::ReadChChart( XclImpStream& rStrm )
+{
+ mxChartData = std::make_shared<XclImpChChart>( GetRoot() );
+ mxChartData->ReadRecordGroup( rStrm );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xicontent.cxx b/sc/source/filter/excel/xicontent.cxx
new file mode 100644
index 000000000..872632a1c
--- /dev/null
+++ b/sc/source/filter/excel/xicontent.cxx
@@ -0,0 +1,1442 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <xicontent.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <svl/itemset.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/editobj.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <stringutil.hxx>
+#include <cellform.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+#include <validat.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <rangenam.hxx>
+#include <arealink.hxx>
+#include <stlsheet.hxx>
+#include <xlcontent.hxx>
+#include <xlformula.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xistyle.hxx>
+#include <xiescher.hxx>
+#include <xiname.hxx>
+
+#include <excform.hxx>
+#include <tabprotection.hxx>
+#include <documentimport.hxx>
+
+#include <memory>
+#include <utility>
+#include <oox/helper/helper.hxx>
+#include <sal/log.hxx>
+
+using ::com::sun::star::uno::Sequence;
+using ::std::unique_ptr;
+
+// Shared string table ========================================================
+
+XclImpSst::XclImpSst( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpSst::ReadSst( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 4 );
+ sal_uInt32 nStrCount = rStrm.ReaduInt32();
+ auto nBytesAvailable = rStrm.GetRecLeft();
+ if (nStrCount > nBytesAvailable)
+ {
+ SAL_WARN("sc.filter", "xls claimed to have " << nStrCount << " strings, but only " << nBytesAvailable << " bytes available, truncating");
+ nStrCount = nBytesAvailable;
+ }
+ maStrings.clear();
+ maStrings.reserve(nStrCount);
+ while( (nStrCount > 0) && rStrm.IsValid() )
+ {
+ XclImpString aString;
+ aString.Read( rStrm );
+ maStrings.push_back( aString );
+ --nStrCount;
+ }
+}
+
+const XclImpString* XclImpSst::GetString( sal_uInt32 nSstIndex ) const
+{
+ return (nSstIndex < maStrings.size()) ? &maStrings[ nSstIndex ] : nullptr;
+}
+
+// Hyperlinks =================================================================
+
+namespace {
+
+/** Reads character array and stores it into rString.
+ @param nChars Number of following characters (not byte count!).
+ @param b16Bit true = 16-bit characters, false = 8-bit characters. */
+void lclAppendString32( OUString& rString, XclImpStream& rStrm, sal_uInt32 nChars, bool b16Bit )
+{
+ sal_uInt16 nReadChars = ulimit_cast< sal_uInt16 >( nChars );
+ rString += rStrm.ReadRawUniString( nReadChars, b16Bit );
+ // ignore remaining chars
+ std::size_t nIgnore = nChars - nReadChars;
+ if( b16Bit )
+ nIgnore *= 2;
+ rStrm.Ignore( nIgnore );
+}
+
+/** Reads 32-bit string length and the character array and stores it into rString.
+ @param b16Bit true = 16-bit characters, false = 8-bit characters. */
+void lclAppendString32( OUString& rString, XclImpStream& rStrm, bool b16Bit )
+{
+ lclAppendString32( rString, rStrm, rStrm.ReaduInt32(), b16Bit );
+}
+
+/** Reads 32-bit string length and ignores following 16-bit character array. */
+void lclIgnoreString32( XclImpStream& rStrm )
+{
+ sal_uInt32 nChars = rStrm.ReaduInt32();
+ nChars *= 2;
+ rStrm.Ignore( nChars );
+}
+
+/** Converts a path to an absolute path.
+ @param rPath The source path. The resulting path is returned here.
+ @param nLevel Number of parent directories to add in front of the path. */
+void lclGetAbsPath( OUString& rPath, sal_uInt16 nLevel, const SfxObjectShell* pDocShell )
+{
+ OUStringBuffer aTmpStr;
+ while( nLevel )
+ {
+ aTmpStr.append( "../" );
+ --nLevel;
+ }
+ aTmpStr.append( rPath );
+
+ if( pDocShell )
+ {
+ bool bWasAbs = false;
+ rPath = pDocShell->GetMedium()->GetURLObject().smartRel2Abs( aTmpStr.makeStringAndClear(), bWasAbs ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ // full path as stored in SvxURLField must be encoded
+ }
+ else
+ rPath = aTmpStr.makeStringAndClear();
+}
+
+/** Inserts the URL into a text cell. Does not modify value or formula cells. */
+void lclInsertUrl( XclImpRoot& rRoot, const OUString& rUrl, SCCOL nScCol, SCROW nScRow, SCTAB nScTab )
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ ScAddress aScPos( nScCol, nScRow, nScTab );
+ ScRefCellValue aCell(rDoc.getDoc(), aScPos);
+ switch( aCell.meType )
+ {
+ // #i54261# hyperlinks in string cells
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ sal_uInt32 nNumFmt = rDoc.getDoc().GetNumberFormat(rDoc.getDoc().GetNonThreadedContext(), aScPos);
+ SvNumberFormatter* pFormatter = rDoc.getDoc().GetFormatTable();
+ const Color* pColor;
+ OUString aDisplText = ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pFormatter, rDoc.getDoc());
+ if (aDisplText.isEmpty())
+ aDisplText = rUrl;
+
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ SvxURLField aUrlField( rUrl, aDisplText, SvxURLFormat::AppDefault );
+
+ if( aCell.meType == CELLTYPE_EDIT )
+ {
+ const EditTextObject* pEditObj = aCell.mpEditText;
+ rEE.SetTextCurrentDefaults( *pEditObj );
+ rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0, 0, EE_PARA_ALL, 0 ) );
+ }
+ else
+ {
+ rEE.SetTextCurrentDefaults( OUString() );
+ rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection() );
+ if( const ScPatternAttr* pPattern = rDoc.getDoc().GetPattern( aScPos.Col(), aScPos.Row(), nScTab ) )
+ {
+ SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
+ pPattern->FillEditItemSet( &aItemSet );
+ rEE.QuickSetAttribs( aItemSet, ESelection( 0, 0, EE_PARA_ALL, 0 ) );
+ }
+ }
+
+ // The cell will own the text object instance.
+ rDoc.setEditCell(aScPos, rEE.CreateTextObject());
+ }
+ break;
+
+ default:
+ // Handle other cell types e.g. formulas ( and ? ) that have associated
+ // hyperlinks.
+ // Ideally all hyperlinks should be treated as below. For the moment,
+ // given the current absence of ods support lets just handle what we
+ // previously didn't handle the new way.
+ // Unfortunately we won't be able to preserve such hyperlinks when
+ // saving to ods. Note: when we are able to save such hyperlinks to ods
+ // we should handle *all* imported hyperlinks as below ( e.g. as cell
+ // attribute ) for better interoperability.
+ {
+ SfxStringItem aItem( ATTR_HYPERLINK, rUrl );
+ rDoc.getDoc().ApplyAttr(nScCol, nScRow, nScTab, aItem);
+ break;
+ }
+ }
+}
+
+} // namespace
+
+void XclImpHyperlink::ReadHlink( XclImpStream& rStrm )
+{
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ rStrm >> aXclRange;
+ // #i80006# Excel silently ignores invalid hi-byte of column index (TODO: everywhere?)
+ aXclRange.maFirst.mnCol &= 0xFF;
+ aXclRange.maLast.mnCol &= 0xFF;
+ OUString aString = ReadEmbeddedData( rStrm );
+ if ( !aString.isEmpty() )
+ rStrm.GetRoot().GetXFRangeBuffer().SetHyperlink( aXclRange, aString );
+}
+
+OUString XclImpHyperlink::ReadEmbeddedData( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ SfxObjectShell* pDocShell = rRoot.GetDocShell();
+
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ XclGuid aGuid;
+ rStrm >> aGuid;
+ rStrm.Ignore( 4 );
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+
+ OSL_ENSURE( aGuid == XclTools::maGuidStdLink, "XclImpHyperlink::ReadEmbeddedData - unknown header GUID" );
+
+ ::std::unique_ptr< OUString > xLongName; // link / file name
+ ::std::unique_ptr< OUString > xShortName; // 8.3-representation of file name
+ ::std::unique_ptr< OUString > xTextMark; // text mark
+
+ // description (ignore)
+ if( ::get_flag( nFlags, EXC_HLINK_DESCR ) )
+ lclIgnoreString32( rStrm );
+ // target frame (ignore) !! DESCR/FRAME - is this the right order? (never seen them together)
+ if( ::get_flag( nFlags, EXC_HLINK_FRAME ) )
+ lclIgnoreString32( rStrm );
+
+ // URL fields are zero-terminated - do not let the stream replace them
+ // in the lclAppendString32() with the '?' character.
+ rStrm.SetNulSubstChar( '\0' );
+
+ // UNC path
+ if( ::get_flag( nFlags, EXC_HLINK_UNC ) )
+ {
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, true );
+ lclGetAbsPath( *xLongName, 0, pDocShell );
+ }
+ // file link or URL
+ else if( ::get_flag( nFlags, EXC_HLINK_BODY ) )
+ {
+ rStrm >> aGuid;
+
+ if( aGuid == XclTools::maGuidFileMoniker )
+ {
+ sal_uInt16 nLevel = rStrm.ReaduInt16(); // counter for level to climb down in path
+ xShortName.reset( new OUString );
+ lclAppendString32( *xShortName, rStrm, false );
+ rStrm.Ignore( 24 );
+
+ sal_uInt32 nStrLen = rStrm.ReaduInt32();
+ if( nStrLen )
+ {
+ nStrLen = rStrm.ReaduInt32();
+ nStrLen /= 2; // it's byte count here...
+ rStrm.Ignore( 2 );
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, nStrLen, true );
+ lclGetAbsPath( *xLongName, nLevel, pDocShell );
+ }
+ else
+ lclGetAbsPath( *xShortName, nLevel, pDocShell );
+ }
+ else if( aGuid == XclTools::maGuidUrlMoniker )
+ {
+ sal_uInt32 nStrLen = rStrm.ReaduInt32();
+ nStrLen /= 2; // it's byte count here...
+ xLongName.reset( new OUString );
+ lclAppendString32( *xLongName, rStrm, nStrLen, true );
+ if( !::get_flag( nFlags, EXC_HLINK_ABS ) )
+ lclGetAbsPath( *xLongName, 0, pDocShell );
+ }
+ else
+ {
+ OSL_FAIL( "XclImpHyperlink::ReadEmbeddedData - unknown content GUID" );
+ }
+ }
+
+ // text mark
+ if( ::get_flag( nFlags, EXC_HLINK_MARK ) )
+ {
+ xTextMark.reset( new OUString );
+ lclAppendString32( *xTextMark, rStrm, true );
+ }
+
+ rStrm.SetNulSubstChar(); // back to default
+
+ OSL_ENSURE( rStrm.GetRecLeft() == 0, "XclImpHyperlink::ReadEmbeddedData - record size mismatch" );
+
+ if (!xLongName && xShortName)
+ xLongName = std::move(xShortName);
+ else if (!xLongName && xTextMark)
+ xLongName.reset( new OUString );
+
+ if (xLongName)
+ {
+ if (xTextMark)
+ {
+ if( xLongName->isEmpty() )
+ {
+ sal_Int32 nSepPos = xTextMark->lastIndexOf( '!' );
+ if( nSepPos > 0 )
+ {
+ // Do not attempt to blindly convert '#SheetName!A1' to
+ // '#SheetName.A1', it can be #SheetName!R1C1 as well.
+ // Hyperlink handler has to handle all, but prefer
+ // '#SheetName.A1' if possible.
+ if (nSepPos < xTextMark->getLength() - 1)
+ {
+ ScDocument& rDoc = rRoot.GetDoc();
+ ScRange aRange;
+ if ((aRange.ParseAny( xTextMark->copy( nSepPos + 1 ), rDoc, formula::FormulaGrammar::CONV_XL_R1C1)
+ & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ xTextMark.reset( new OUString( xTextMark->replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ))));
+ }
+ }
+ }
+ xLongName.reset( new OUString( *xLongName + "#" + *xTextMark ) );
+ }
+ return( *xLongName );
+ }
+ return( OUString() );
+}
+
+void XclImpHyperlink::ConvertToValidTabName(OUString& rUrl)
+{
+ sal_Int32 n = rUrl.getLength();
+ if (n < 4)
+ // Needs at least 4 characters.
+ return;
+
+ if (rUrl[0] != '#')
+ // the 1st character must be '#'.
+ return;
+
+ OUStringBuffer aNewUrl("#");
+ OUStringBuffer aTabName;
+
+ bool bInQuote = false;
+ bool bQuoteTabName = false;
+ for( sal_Int32 i = 1; i < n; ++i )
+ {
+ sal_Unicode c = rUrl[i];
+ if (c == '\'')
+ {
+ if (bInQuote && i+1 < n && rUrl[i+1] == '\'')
+ {
+ // Two consecutive single quotes ('') signify a single literal
+ // quite. When this occurs, the whole table name needs to be
+ // quoted.
+ bQuoteTabName = true;
+ aTabName.append(c).append(c);
+ ++i;
+ continue;
+ }
+
+ bInQuote = !bInQuote;
+ if (!bInQuote && !aTabName.isEmpty())
+ {
+ if (bQuoteTabName)
+ aNewUrl.append("'");
+ aNewUrl.append(aTabName);
+ if (bQuoteTabName)
+ aNewUrl.append("'");
+ }
+ }
+ else if (bInQuote)
+ aTabName.append(c);
+ else
+ aNewUrl.append(c);
+ }
+
+ if (bInQuote)
+ // It should be outside the quotes!
+ return;
+
+ // All is good. Pass the new URL.
+ rUrl = aNewUrl.makeStringAndClear();
+}
+
+void XclImpHyperlink::InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl )
+{
+ OUString aUrl(rUrl);
+ ConvertToValidTabName(aUrl);
+
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( rRoot.GetAddressConverter().ConvertRange( aScRange, rXclRange, nScTab, nScTab, true ) )
+ {
+ SCCOL nScCol1, nScCol2;
+ SCROW nScRow1, nScRow2;
+ aScRange.GetVars( nScCol1, nScRow1, nScTab, nScCol2, nScRow2, nScTab );
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ SCROW nRows = nScRow2 - nScRow1;
+ if (nRows > 1024)
+ {
+ SAL_WARN("sc.filter", "for fuzzing performance, clamped hyperlink apply range end row from " << nScRow2 << " to " << nScRow1 + 1024);
+ nScRow2 = nScRow1 + 1024;
+ }
+ }
+
+ for( SCCOL nScCol = nScCol1; nScCol <= nScCol2; ++nScCol )
+ for( SCROW nScRow = nScRow1; nScRow <= nScRow2; ++nScRow )
+ lclInsertUrl( rRoot, aUrl, nScCol, nScRow, nScTab );
+ }
+}
+
+// Label ranges ===============================================================
+
+void XclImpLabelranges::ReadLabelranges( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ ScDocument& rDoc = rRoot.GetDoc();
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ XclImpAddressConverter& rAddrConv = rRoot.GetAddressConverter();
+ ScRangePairListRef xLabelRangesRef;
+
+ XclRangeList aRowXclRanges, aColXclRanges;
+ rStrm >> aRowXclRanges >> aColXclRanges;
+
+ // row label ranges
+ ScRangeList aRowScRanges;
+ rAddrConv.ConvertRangeList( aRowScRanges, aRowXclRanges, nScTab, false );
+ xLabelRangesRef = rDoc.GetRowNameRangesRef();
+ for ( size_t i = 0, nRanges = aRowScRanges.size(); i < nRanges; ++i )
+ {
+ const ScRange & rScRange = aRowScRanges[ i ];
+ ScRange aDataRange( rScRange );
+ if( aDataRange.aEnd.Col() < rDoc.MaxCol() )
+ {
+ aDataRange.aStart.SetCol( aDataRange.aEnd.Col() + 1 );
+ aDataRange.aEnd.SetCol( rDoc.MaxCol() );
+ }
+ else if( aDataRange.aStart.Col() > 0 )
+ {
+ aDataRange.aEnd.SetCol( aDataRange.aStart.Col() - 1 );
+ aDataRange.aStart.SetCol( 0 );
+ }
+ xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
+ }
+
+ // column label ranges
+ ScRangeList aColScRanges;
+ rAddrConv.ConvertRangeList( aColScRanges, aColXclRanges, nScTab, false );
+ xLabelRangesRef = rDoc.GetColNameRangesRef();
+
+ for ( size_t i = 0, nRanges = aColScRanges.size(); i < nRanges; ++i )
+ {
+ const ScRange & rScRange = aColScRanges[ i ];
+ ScRange aDataRange( rScRange );
+ if( aDataRange.aEnd.Row() < rDoc.MaxRow() )
+ {
+ aDataRange.aStart.SetRow( aDataRange.aEnd.Row() + 1 );
+ aDataRange.aEnd.SetRow( rDoc.MaxRow() );
+ }
+ else if( aDataRange.aStart.Row() > 0 )
+ {
+ aDataRange.aEnd.SetRow( aDataRange.aStart.Row() - 1 );
+ aDataRange.aStart.SetRow( 0 );
+ }
+ xLabelRangesRef->Append( ScRangePair( rScRange, aDataRange ) );
+ }
+}
+
+// Conditional formatting =====================================================
+
+XclImpCondFormat::XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex ) :
+ XclImpRoot( rRoot ),
+ mnFormatIndex( nFormatIndex ),
+ mnCondCount( 0 ),
+ mnCondIndex( 0 )
+{
+}
+
+XclImpCondFormat::~XclImpCondFormat()
+{
+}
+
+void XclImpCondFormat::ReadCondfmt( XclImpStream& rStrm )
+{
+ OSL_ENSURE( !mnCondCount, "XclImpCondFormat::ReadCondfmt - already initialized" );
+ XclRangeList aXclRanges;
+ mnCondCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ rStrm >> aXclRanges;
+ GetAddressConverter().ConvertRangeList( maRanges, aXclRanges, GetCurrScTab(), true );
+}
+
+void XclImpCondFormat::ReadCF( XclImpStream& rStrm )
+{
+ if( mnCondIndex >= mnCondCount )
+ {
+ OSL_FAIL( "XclImpCondFormat::ReadCF - CF without leading CONDFMT" );
+ return;
+ }
+
+ // entire conditional format outside of valid range?
+ if( maRanges.empty() )
+ return;
+
+ sal_uInt8 nType = rStrm.ReaduInt8();
+ sal_uInt8 nOperator = rStrm.ReaduInt8();
+ sal_uInt16 nFmlaSize1 = rStrm.ReaduInt16();
+ sal_uInt16 nFmlaSize2 = rStrm.ReaduInt16();
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+ rStrm.Ignore( 2 ); //nFlagsExtended
+
+ // *** mode and comparison operator ***
+
+ ScConditionMode eMode = ScConditionMode::NONE;
+ switch( nType )
+ {
+ case EXC_CF_TYPE_CELL:
+ {
+ switch( nOperator )
+ {
+ case EXC_CF_CMP_BETWEEN: eMode = ScConditionMode::Between; break;
+ case EXC_CF_CMP_NOT_BETWEEN: eMode = ScConditionMode::NotBetween; break;
+ case EXC_CF_CMP_EQUAL: eMode = ScConditionMode::Equal; break;
+ case EXC_CF_CMP_NOT_EQUAL: eMode = ScConditionMode::NotEqual; break;
+ case EXC_CF_CMP_GREATER: eMode = ScConditionMode::Greater; break;
+ case EXC_CF_CMP_LESS: eMode = ScConditionMode::Less; break;
+ case EXC_CF_CMP_GREATER_EQUAL: eMode = ScConditionMode::EqGreater; break;
+ case EXC_CF_CMP_LESS_EQUAL: eMode = ScConditionMode::EqLess; break;
+ default:
+ SAL_INFO(
+ "sc.filter", "unknown CF comparison " << nOperator);
+ }
+ }
+ break;
+
+ case EXC_CF_TYPE_FMLA:
+ eMode = ScConditionMode::Direct;
+ break;
+
+ default:
+ SAL_INFO("sc.filter", "unknown CF mode " << nType);
+ return;
+ }
+
+ // *** create style sheet ***
+
+ OUString aStyleName( XclTools::GetCondFormatStyleName( GetCurrScTab(), mnFormatIndex, mnCondIndex ) );
+ SfxItemSet& rStyleItemSet = ScfTools::MakeCellStyleSheet( GetStyleSheetPool(), aStyleName, true ).GetItemSet();
+
+ const XclImpPalette& rPalette = GetPalette();
+
+ // number format
+
+ if( get_flag( nFlags, EXC_CF_BLOCK_NUMFMT ) )
+ {
+ XclImpNumFmtBuffer& rNumFmtBuffer = GetRoot().GetNumFmtBuffer();
+ bool bIFmt = get_flag( nFlags, EXC_CF_IFMT_USER );
+ sal_uInt16 nFormat = rNumFmtBuffer.ReadCFFormat( rStrm, bIFmt );
+ rNumFmtBuffer.FillToItemSet( rStyleItemSet, nFormat );
+ }
+
+ // *** font block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_FONT ) )
+ {
+ XclImpFont aFont( GetRoot() );
+ aFont.ReadCFFontBlock( rStrm );
+ aFont.FillToItemSet( rStyleItemSet, XclFontItemType::Cell );
+ }
+
+ // alignment
+ if( get_flag( nFlags, EXC_CF_BLOCK_ALIGNMENT ) )
+ {
+ XclImpCellAlign aAlign;
+ sal_uInt16 nAlign(0);
+ sal_uInt16 nAlignMisc(0);
+ nAlign = rStrm.ReaduInt16();
+ nAlignMisc = rStrm.ReaduInt16();
+ aAlign.FillFromCF( nAlign, nAlignMisc );
+ aAlign.FillToItemSet( rStyleItemSet, nullptr );
+ rStrm.Ignore(4);
+ }
+
+ // *** border block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_BORDER ) )
+ {
+ sal_uInt16 nLineStyle(0);
+ sal_uInt32 nLineColor(0);
+ nLineStyle = rStrm.ReaduInt16();
+ nLineColor = rStrm.ReaduInt32();
+ rStrm.Ignore( 2 );
+
+ XclImpCellBorder aBorder;
+ aBorder.FillFromCF8( nLineStyle, nLineColor, nFlags );
+ aBorder.FillToItemSet( rStyleItemSet, rPalette );
+ }
+
+ // *** pattern block ***
+
+ if( ::get_flag( nFlags, EXC_CF_BLOCK_AREA ) )
+ {
+ sal_uInt16 nPattern(0), nColor(0);
+ nPattern = rStrm.ReaduInt16();
+ nColor = rStrm.ReaduInt16();
+
+ XclImpCellArea aArea;
+ aArea.FillFromCF8( nPattern, nColor, nFlags );
+ aArea.FillToItemSet( rStyleItemSet, rPalette );
+ }
+
+ if( get_flag( nFlags, EXC_CF_BLOCK_PROTECTION ) )
+ {
+ sal_uInt16 nCellProt;
+ nCellProt = rStrm.ReaduInt16();
+ XclImpCellProt aCellProt;
+ aCellProt.FillFromXF3(nCellProt);
+ aCellProt.FillToItemSet( rStyleItemSet );
+ }
+
+ // *** formulas ***
+
+ const ScAddress& rPos = maRanges.front().aStart; // assured above that maRanges is not empty
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+
+ ::std::unique_ptr< ScTokenArray > xTokArr1;
+ if( nFmlaSize1 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset( rPos );
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize1, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ {
+ xTokArr1 = std::move( pTokArr );
+ GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr1);
+ }
+ }
+
+ ::std::unique_ptr< ScTokenArray > xTokArr2;
+ if( nFmlaSize2 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset( rPos );
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize2, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ {
+ xTokArr2 = std::move( pTokArr );
+ GetDoc().CheckLinkFormulaNeedingCheck( *xTokArr2);
+ }
+ }
+
+ // *** create the Calc conditional formatting ***
+
+ const ScAddress aPos(rPos); //in case maRanges.Join invalidates it
+
+ if( !mxScCondFmt )
+ {
+ mxScCondFmt.reset( new ScConditionalFormat( 0/*nKey*/, &GetDoc() ) );
+ if(maRanges.size() > 1)
+ maRanges.Join(maRanges[0], true);
+ mxScCondFmt->SetRange(maRanges);
+ }
+
+ ScCondFormatEntry* pEntry = new ScCondFormatEntry(eMode, xTokArr1.get(), xTokArr2.get(), GetDoc(), aPos, aStyleName);
+ mxScCondFmt->AddEntry( pEntry );
+ ++mnCondIndex;
+}
+
+void XclImpCondFormat::Apply()
+{
+ if( mxScCondFmt )
+ {
+ ScDocument& rDoc = GetDoc();
+
+ SCTAB nTab = maRanges.front().aStart.Tab();
+ sal_uLong nKey = rDoc.AddCondFormat( mxScCondFmt->Clone(), nTab );
+
+ rDoc.AddCondFormatData( maRanges, nTab, nKey );
+ }
+}
+
+XclImpCondFormatManager::XclImpCondFormatManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpCondFormatManager::ReadCondfmt( XclImpStream& rStrm )
+{
+ XclImpCondFormat* pFmt = new XclImpCondFormat( GetRoot(), maCondFmtList.size() );
+ pFmt->ReadCondfmt( rStrm );
+ maCondFmtList.push_back( std::unique_ptr<XclImpCondFormat>(pFmt) );
+}
+
+void XclImpCondFormatManager::ReadCF( XclImpStream& rStrm )
+{
+ OSL_ENSURE( !maCondFmtList.empty(), "XclImpCondFormatManager::ReadCF - CF without leading CONDFMT" );
+ if( !maCondFmtList.empty() )
+ maCondFmtList.back()->ReadCF( rStrm );
+}
+
+void XclImpCondFormatManager::Apply()
+{
+ for( auto& rxFmt : maCondFmtList )
+ rxFmt->Apply();
+ maCondFmtList.clear();
+}
+
+// Data Validation ============================================================
+
+XclImpValidationManager::DVItem::DVItem( const ScRangeList& rRanges, const ScValidationData& rValidData ) :
+ maRanges(rRanges), maValidData(rValidData) {}
+
+XclImpValidationManager::XclImpValidationManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpValidationManager::ReadDval( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ sal_uInt32 nObjId(0);
+ rStrm.Ignore( 10 );
+ nObjId = rStrm.ReaduInt32();
+ if( nObjId != EXC_DVAL_NOOBJ )
+ {
+ OSL_ENSURE( nObjId <= 0xFFFF, "XclImpValidation::ReadDval - invalid object ID" );
+ rRoot.GetCurrSheetDrawing().SetSkipObj( static_cast< sal_uInt16 >( nObjId ) );
+ }
+}
+
+void XclImpValidationManager::ReadDV( XclImpStream& rStrm )
+{
+ const XclImpRoot& rRoot = rStrm.GetRoot();
+ OSL_ENSURE_BIFF( rRoot.GetBiff() == EXC_BIFF8 );
+
+ ScDocument& rDoc = rRoot.GetDoc();
+ SCTAB nScTab = rRoot.GetCurrScTab();
+ ExcelToSc& rFmlaConv = rRoot.GetOldFmlaConverter();
+
+ // flags
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+
+ // message strings
+ /* Empty strings are single NUL characters in Excel (string length is 1).
+ -> Do not let the stream replace them with '?' characters. */
+ rStrm.SetNulSubstChar( '\0' );
+ OUString aPromptTitle( rStrm.ReadUniString() );
+ OUString aErrorTitle( rStrm.ReadUniString() );
+ OUString aPromptMessage( rStrm.ReadUniString() );
+ OUString aErrorMessage( rStrm.ReadUniString() );
+ rStrm.SetNulSubstChar(); // back to default
+
+ // formula(s)
+ if ( rStrm.GetRecLeft() <= 8 )
+ // Not enough bytes left in the record. Bail out.
+ return;
+
+ // first formula
+ // string list is single tStr token with NUL separators -> replace them with LF
+ rStrm.SetNulSubstChar( '\n' );
+ ::std::unique_ptr< ScTokenArray > xTokArr1;
+
+ // We can't import the formula directly because we need the range
+ sal_uInt16 nLenFormula1 = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ XclImpStreamPos aPosFormula1;
+ rStrm.StorePosition(aPosFormula1);
+ rStrm.Ignore(nLenFormula1);
+
+ // second formula
+ ::std::unique_ptr< ScTokenArray > xTokArr2;
+
+ sal_uInt16 nLenFormula2 = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ XclImpStreamPos aPosFormula2;
+ rStrm.StorePosition(aPosFormula2);
+ rStrm.Ignore(nLenFormula2);
+
+ // read all cell ranges
+ XclRangeList aXclRanges;
+ rStrm >> aXclRanges;
+
+ // convert to Calc range list
+ ScRangeList aScRanges;
+ rRoot.GetAddressConverter().ConvertRangeList( aScRanges, aXclRanges, nScTab, true );
+
+ // only continue if there are valid ranges
+ if ( aScRanges.empty() )
+ return;
+
+ ScRange aCombinedRange = aScRanges.Combine();
+
+ XclImpStreamPos aCurrentPos;
+ rStrm.StorePosition(aCurrentPos);
+ rStrm.RestorePosition(aPosFormula1);
+ if( nLenFormula1 > 0 )
+ {
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset(aCombinedRange.aStart);
+ rFmlaConv.Convert( pTokArr, rStrm, nLenFormula1, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ xTokArr1 = std::move( pTokArr );
+ }
+ rStrm.SetNulSubstChar(); // back to default
+ if (nLenFormula2 > 0)
+ {
+ rStrm.RestorePosition(aPosFormula2);
+ std::unique_ptr<ScTokenArray> pTokArr;
+ rFmlaConv.Reset(aCombinedRange.aStart);
+ rFmlaConv.Convert( pTokArr, rStrm, nLenFormula2, false, FT_CondFormat );
+ // formula converter owns pTokArr -> create a copy of the token array
+ if( pTokArr )
+ xTokArr2 = std::move( pTokArr );
+ }
+
+ rStrm.RestorePosition(aCurrentPos);
+
+ bool bIsValid = true; // valid settings in flags field
+
+ ScValidationMode eValMode = SC_VALID_ANY;
+ switch( nFlags & EXC_DV_MODE_MASK )
+ {
+ case EXC_DV_MODE_ANY: eValMode = SC_VALID_ANY; break;
+ case EXC_DV_MODE_WHOLE: eValMode = SC_VALID_WHOLE; break;
+ case EXC_DV_MODE_DECIMAL: eValMode = SC_VALID_DECIMAL; break;
+ case EXC_DV_MODE_LIST: eValMode = SC_VALID_LIST; break;
+ case EXC_DV_MODE_DATE: eValMode = SC_VALID_DATE; break;
+ case EXC_DV_MODE_TIME: eValMode = SC_VALID_TIME; break;
+ case EXC_DV_MODE_TEXTLEN: eValMode = SC_VALID_TEXTLEN; break;
+ case EXC_DV_MODE_CUSTOM: eValMode = SC_VALID_CUSTOM; break;
+ default: bIsValid = false;
+ }
+ rRoot.GetTracer().TraceDVType(eValMode == SC_VALID_CUSTOM);
+
+ ScConditionMode eCondMode = ScConditionMode::Between;
+ switch( nFlags & EXC_DV_COND_MASK )
+ {
+ case EXC_DV_COND_BETWEEN: eCondMode = ScConditionMode::Between; break;
+ case EXC_DV_COND_NOTBETWEEN:eCondMode = ScConditionMode::NotBetween; break;
+ case EXC_DV_COND_EQUAL: eCondMode = ScConditionMode::Equal; break;
+ case EXC_DV_COND_NOTEQUAL: eCondMode = ScConditionMode::NotEqual; break;
+ case EXC_DV_COND_GREATER: eCondMode = ScConditionMode::Greater; break;
+ case EXC_DV_COND_LESS: eCondMode = ScConditionMode::Less; break;
+ case EXC_DV_COND_EQGREATER: eCondMode = ScConditionMode::EqGreater; break;
+ case EXC_DV_COND_EQLESS: eCondMode = ScConditionMode::EqLess; break;
+ default: bIsValid = false;
+ }
+
+ if ( !bIsValid )
+ // No valid validation found. Bail out.
+ return;
+
+ // The default value for comparison is _BETWEEN. However, custom
+ // rules are a formula, and thus the comparator should be ignored
+ // and only a true or false from the formula is evaluated. In Calc,
+ // formulas use comparison SC_COND_DIRECT.
+ if( eValMode == SC_VALID_CUSTOM )
+ {
+ eCondMode = ScConditionMode::Direct;
+ }
+
+ // first range for base address for relative references
+ const ScRange& rScRange = aScRanges.front(); // aScRanges is not empty
+
+ // process string list of a list validity (convert to list of string tokens)
+ if( xTokArr1 && (eValMode == SC_VALID_LIST) && ::get_flag( nFlags, EXC_DV_STRINGLIST ) )
+ XclTokenArrayHelper::ConvertStringToList(*xTokArr1, rDoc.GetSharedStringPool(), '\n');
+
+ maDVItems.push_back(
+ std::make_unique<DVItem>(aScRanges, ScValidationData(eValMode, eCondMode, xTokArr1.get(), xTokArr2.get(), rDoc, rScRange.aStart)));
+ DVItem& rItem = *maDVItems.back();
+
+ rItem.maValidData.SetIgnoreBlank( ::get_flag( nFlags, EXC_DV_IGNOREBLANK ) );
+ rItem.maValidData.SetListType( ::get_flagvalue( nFlags, EXC_DV_SUPPRESSDROPDOWN, css::sheet::TableValidationVisibility::INVISIBLE, css::sheet::TableValidationVisibility::UNSORTED ) );
+
+ // *** prompt box ***
+ if( !aPromptTitle.isEmpty() || !aPromptMessage.isEmpty() )
+ {
+ // set any text stored in the record
+ rItem.maValidData.SetInput( aPromptTitle, aPromptMessage );
+ if( !::get_flag( nFlags, EXC_DV_SHOWPROMPT ) )
+ rItem.maValidData.ResetInput();
+ }
+
+ // *** error box ***
+ ScValidErrorStyle eErrStyle = SC_VALERR_STOP;
+ switch( nFlags & EXC_DV_ERROR_MASK )
+ {
+ case EXC_DV_ERROR_WARNING: eErrStyle = SC_VALERR_WARNING; break;
+ case EXC_DV_ERROR_INFO: eErrStyle = SC_VALERR_INFO; break;
+ }
+ // set texts and error style
+ rItem.maValidData.SetError( aErrorTitle, aErrorMessage, eErrStyle );
+ if( !::get_flag( nFlags, EXC_DV_SHOWERROR ) )
+ rItem.maValidData.ResetError();
+}
+
+void XclImpValidationManager::Apply()
+{
+ ScDocument& rDoc = GetRoot().GetDoc();
+ for (const auto& rxDVItem : maDVItems)
+ {
+ DVItem& rItem = *rxDVItem;
+ // set the handle ID
+ sal_uLong nHandle = rDoc.AddValidationEntry( rItem.maValidData );
+ ScPatternAttr aPattern( rDoc.GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nHandle ) );
+
+ // apply all ranges
+ for ( size_t i = 0, nRanges = rItem.maRanges.size(); i < nRanges; ++i )
+ {
+ const ScRange & rScRange = rItem.maRanges[ i ];
+ rDoc.ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aEnd.Col(), rScRange.aEnd.Row(), rScRange.aStart.Tab(), aPattern );
+ }
+ }
+ maDVItems.clear();
+}
+
+// Web queries ================================================================
+
+XclImpWebQuery::XclImpWebQuery( const ScRange& rDestRange ) :
+ maDestRange( rDestRange ),
+ meMode( xlWQUnknown ),
+ mnRefresh( 0 )
+{
+}
+
+void XclImpWebQuery::ReadParamqry( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ sal_uInt16 nType = ::extract_value< sal_uInt16 >( nFlags, 0, 3 );
+ if( !((nType == EXC_PQRYTYPE_WEBQUERY) && ::get_flag( nFlags, EXC_PQRY_WEBQUERY )) )
+ return;
+
+ if( ::get_flag( nFlags, EXC_PQRY_TABLES ) )
+ {
+ meMode = xlWQAllTables;
+ maTables = ScfTools::GetHTMLTablesName();
+ }
+ else
+ {
+ meMode = xlWQDocument;
+ maTables = ScfTools::GetHTMLDocName();
+ }
+}
+
+void XclImpWebQuery::ReadWqstring( XclImpStream& rStrm )
+{
+ maURL = rStrm.ReadUniString();
+}
+
+void XclImpWebQuery::ReadWqsettings( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 10 );
+ sal_uInt16 nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnRefresh = rStrm.ReaduInt16();
+
+ if( ::get_flag( nFlags, EXC_WQSETT_SPECTABLES ) && (meMode == xlWQAllTables) )
+ meMode = xlWQSpecTables;
+}
+
+void XclImpWebQuery::ReadWqtables( XclImpStream& rStrm )
+{
+ if( meMode != xlWQSpecTables )
+ return;
+
+ rStrm.Ignore( 4 );
+ OUString aTables( rStrm.ReadUniString() );
+
+ const sal_Unicode cSep = ';';
+ static const OUStringLiteral aQuotedPairs( u"\"\"" );
+ maTables.clear();
+ for ( sal_Int32 nStringIx {aTables.isEmpty() ? -1 : 0}; nStringIx>=0; )
+ {
+ OUString aToken( ScStringUtil::GetQuotedToken( aTables, 0, aQuotedPairs, ',', nStringIx ) );
+ sal_Int32 nTabNum = CharClass::isAsciiNumeric( aToken ) ? aToken.toInt32() : 0;
+ if( nTabNum > 0 )
+ maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLIndex( static_cast< sal_uInt32 >( nTabNum ) ), cSep );
+ else
+ {
+ ScGlobal::EraseQuotes( aToken, '"', false );
+ if( !aToken.isEmpty() )
+ maTables = ScGlobal::addToken( maTables, ScfTools::GetNameFromHTMLName( aToken ), cSep );
+ }
+ }
+}
+
+void XclImpWebQuery::Apply( ScDocument& rDoc, const OUString& rFilterName )
+{
+ if( !maURL.isEmpty() && (meMode != xlWQUnknown) && rDoc.GetDocumentShell() )
+ {
+ ScAreaLink* pLink = new ScAreaLink( rDoc.GetDocumentShell(),
+ maURL, rFilterName, OUString(), maTables, maDestRange, mnRefresh * 60UL );
+ rDoc.GetLinkManager()->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile,
+ maURL, &rFilterName, &maTables );
+ }
+}
+
+XclImpWebQueryBuffer::XclImpWebQueryBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpWebQueryBuffer::ReadQsi( XclImpStream& rStrm )
+{
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ rStrm.Ignore( 10 );
+ OUString aXclName( rStrm.ReadUniString() );
+
+ // #i64794# Excel replaces spaces with underscores
+ aXclName = aXclName.replaceAll( " ", "_" );
+
+ // find the defined name used in Calc
+ if( const XclImpName* pName = GetNameManager().FindName( aXclName, GetCurrScTab() ) )
+ {
+ if( const ScRangeData* pRangeData = pName->GetScRangeData() )
+ {
+ ScRange aRange;
+ if( pRangeData->IsReference( aRange ) )
+ maWQList.emplace_back( aRange );
+ }
+ }
+ }
+ else
+ {
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpWebQueryBuffer::ReadParamqry( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadParamqry( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqstring( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqstring( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqsettings( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqsettings( rStrm );
+}
+
+void XclImpWebQueryBuffer::ReadWqtables( XclImpStream& rStrm )
+{
+ if (!maWQList.empty())
+ maWQList.back().ReadWqtables( rStrm );
+}
+
+void XclImpWebQueryBuffer::Apply()
+{
+ ScDocument& rDoc = GetDoc();
+ for( auto& rQuery : maWQList )
+ rQuery.Apply( rDoc, EXC_WEBQRY_FILTER );
+}
+
+// Decryption =================================================================
+
+namespace {
+
+XclImpDecrypterRef lclReadFilepass5( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ OSL_ENSURE( rStrm.GetRecLeft() == 4, "lclReadFilepass5 - wrong record size" );
+ if( rStrm.GetRecLeft() == 4 )
+ {
+ sal_uInt16 nKey(0), nHash(0);
+ nKey = rStrm.ReaduInt16();
+ nHash = rStrm.ReaduInt16();
+ xDecr = std::make_shared<XclImpBiff5Decrypter>( nKey, nHash );
+ }
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8_Standard( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ OSL_ENSURE( rStrm.GetRecLeft() == 48, "lclReadFilepass8 - wrong record size" );
+ if( rStrm.GetRecLeft() == 48 )
+ {
+ std::vector<sal_uInt8> aSalt(16);
+ std::vector<sal_uInt8> aVerifier(16);
+ std::vector<sal_uInt8> aVerifierHash(16);
+ rStrm.Read(aSalt.data(), 16);
+ rStrm.Read(aVerifier.data(), 16);
+ rStrm.Read(aVerifierHash.data(), 16);
+ xDecr = std::make_shared<XclImpBiff8StdDecrypter>(std::move(aSalt), std::move(aVerifier), std::move(aVerifierHash));
+ }
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8_Strong(XclImpStream& rStream)
+{
+ //It is possible there are other variants in existence but these
+ //are the defaults I get with Excel 2013
+ XclImpDecrypterRef xDecr;
+
+ msfilter::RC4EncryptionInfo info;
+
+ info.header.flags = rStream.ReaduInt32();
+ if (oox::getFlag( info.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
+ return xDecr;
+
+ sal_uInt32 nHeaderSize = rStream.ReaduInt32();
+ sal_uInt32 actualHeaderSize = sizeof(info.header);
+
+ if( nHeaderSize < actualHeaderSize )
+ return xDecr;
+
+ info.header.flags = rStream.ReaduInt32();
+ info.header.sizeExtra = rStream.ReaduInt32();
+ info.header.algId = rStream.ReaduInt32();
+ info.header.algIdHash = rStream.ReaduInt32();
+ info.header.keyBits = rStream.ReaduInt32();
+ info.header.providedType = rStream.ReaduInt32();
+ info.header.reserved1 = rStream.ReaduInt32();
+ info.header.reserved2 = rStream.ReaduInt32();
+
+ rStream.Ignore(nHeaderSize - actualHeaderSize);
+
+ info.verifier.saltSize = rStream.ReaduInt32();
+ if (info.verifier.saltSize != msfilter::SALT_LENGTH)
+ return xDecr;
+ rStream.Read(&info.verifier.salt, sizeof(info.verifier.salt));
+ rStream.Read(&info.verifier.encryptedVerifier, sizeof(info.verifier.encryptedVerifier));
+
+ info.verifier.encryptedVerifierHashSize = rStream.ReaduInt32();
+ if (info.verifier.encryptedVerifierHashSize != RTL_DIGEST_LENGTH_SHA1)
+ return xDecr;
+ rStream.Read(&info.verifier.encryptedVerifierHash, info.verifier.encryptedVerifierHashSize);
+
+ // check flags and algorithm IDs, required are AES128 and SHA-1
+ if (!oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
+ return xDecr;
+
+ if (oox::getFlag(info.header.flags, msfilter::ENCRYPTINFO_AES))
+ return xDecr;
+
+ if (info.header.algId != msfilter::ENCRYPT_ALGO_RC4)
+ return xDecr;
+
+ // hash algorithm ID 0 defaults to SHA-1 too
+ if (info.header.algIdHash != 0 && info.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
+ return xDecr;
+
+ xDecr = std::make_shared<XclImpBiff8CryptoAPIDecrypter>(
+ std::vector<sal_uInt8>(info.verifier.salt,
+ info.verifier.salt + SAL_N_ELEMENTS(info.verifier.salt)),
+ std::vector<sal_uInt8>(info.verifier.encryptedVerifier,
+ info.verifier.encryptedVerifier + SAL_N_ELEMENTS(info.verifier.encryptedVerifier)),
+ std::vector<sal_uInt8>(info.verifier.encryptedVerifierHash,
+ info.verifier.encryptedVerifierHash + SAL_N_ELEMENTS(info.verifier.encryptedVerifierHash)));
+
+ return xDecr;
+}
+
+XclImpDecrypterRef lclReadFilepass8( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+
+ sal_uInt16 nMode = rStrm.ReaduInt16();
+ switch( nMode )
+ {
+ case EXC_FILEPASS_BIFF5:
+ xDecr = lclReadFilepass5( rStrm );
+ break;
+
+ case EXC_FILEPASS_BIFF8:
+ {
+ sal_uInt32 nVersion = rStrm.ReaduInt32();
+ if (nVersion == msfilter::VERSION_INFO_1997_FORMAT)
+ {
+ //A Version structure where Version.vMajor MUST be 0x0001,
+ //and Version.vMinor MUST be 0x0001.
+ xDecr = lclReadFilepass8_Standard(rStrm);
+ }
+ else if (nVersion == msfilter::VERSION_INFO_2007_FORMAT ||
+ nVersion == msfilter::VERSION_INFO_2007_FORMAT_SP2)
+ {
+ //Version.vMajor MUST be 0x0002, 0x0003 or 0x0004 and
+ //Version.vMinor MUST be 0x0002.
+ xDecr = lclReadFilepass8_Strong(rStrm);
+ }
+ else
+ OSL_FAIL("lclReadFilepass8 - unknown BIFF8 encryption sub mode");
+ }
+ break;
+
+ default:
+ OSL_FAIL( "lclReadFilepass8 - unknown encryption mode" );
+ }
+
+ return xDecr;
+}
+
+} // namespace
+
+const ErrCode& XclImpDecryptHelper::ReadFilepass( XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xDecr;
+ rStrm.DisableDecryption();
+
+ // read the FILEPASS record and create a new decrypter object
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5: xDecr = lclReadFilepass5( rStrm ); break;
+ case EXC_BIFF8: xDecr = lclReadFilepass8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ };
+
+ // set decrypter at import stream
+ rStrm.SetDecrypter( xDecr );
+
+ // request and verify a password (decrypter implements IDocPasswordVerifier)
+ if( xDecr )
+ rStrm.GetRoot().RequestEncryptionData( *xDecr );
+
+ // return error code (success, wrong password, etc.)
+ return xDecr ? xDecr->GetError() : EXC_ENCR_ERROR_UNSUPP_CRYPT;
+}
+
+// Document protection ========================================================
+
+XclImpDocProtectBuffer::XclImpDocProtectBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnPassHash(0x0000),
+ mbDocProtect(false),
+ mbWinProtect(false)
+{
+}
+
+void XclImpDocProtectBuffer::ReadDocProtect( XclImpStream& rStrm )
+{
+ mbDocProtect = rStrm.ReaduInt16() != 0;
+}
+
+void XclImpDocProtectBuffer::ReadWinProtect( XclImpStream& rStrm )
+{
+ mbWinProtect = rStrm.ReaduInt16() != 0;
+}
+
+void XclImpDocProtectBuffer::ReadPasswordHash( XclImpStream& rStrm )
+{
+ rStrm.EnableDecryption();
+ mnPassHash = rStrm.ReaduInt16();
+}
+
+void XclImpDocProtectBuffer::Apply() const
+{
+ if (!mbDocProtect && !mbWinProtect)
+ // Excel requires either the structure or windows protection is set.
+ // If neither is set then the document is not protected at all.
+ return;
+
+ unique_ptr<ScDocProtection> pProtect(new ScDocProtection);
+ pProtect->setProtected(true);
+
+ if (mnPassHash)
+ {
+ // 16-bit password hash.
+ Sequence<sal_Int8> aPass{sal_Int8(mnPassHash >> 8), sal_Int8(mnPassHash & 0xFF)};
+ pProtect->setPasswordHash(aPass, PASSHASH_XL);
+ }
+
+ // document protection options
+ pProtect->setOption(ScDocProtection::STRUCTURE, mbDocProtect);
+ pProtect->setOption(ScDocProtection::WINDOWS, mbWinProtect);
+
+ GetDoc().SetDocProtection(pProtect.get());
+}
+
+// Sheet Protection ===========================================================
+
+XclImpSheetProtectBuffer::Sheet::Sheet() :
+ mbProtected(false),
+ mnPasswordHash(0x0000),
+ mnOptions(0x4400)
+{
+}
+
+XclImpSheetProtectBuffer::Sheet::Sheet(const Sheet& r) :
+ mbProtected(r.mbProtected),
+ mnPasswordHash(r.mnPasswordHash),
+ mnOptions(r.mnOptions)
+{
+}
+
+XclImpSheetProtectBuffer::XclImpSheetProtectBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpSheetProtectBuffer::ReadProtect( XclImpStream& rStrm, SCTAB nTab )
+{
+ if ( rStrm.ReaduInt16() )
+ {
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mbProtected = true;
+ }
+}
+
+void XclImpSheetProtectBuffer::ReadOptions( XclImpStream& rStrm, SCTAB nTab )
+{
+ // The flag size specifies the size of bytes that follows that stores
+ // feature data. If -1 it depends on the feature type imported earlier.
+ // For enhanced protection data, the size is always 4. For the most xls
+ // documents out there this value is almost always -1.
+ sal_Int32 nFlagSize = rStrm.ReadInt32();
+ if (nFlagSize != -1)
+ return;
+
+ // There are actually 4 bytes to read, but the upper 2 bytes currently
+ // don't store any bits.
+ sal_uInt16 nOptions = rStrm.ReaduInt16();
+
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mnOptions = nOptions;
+}
+
+void XclImpSheetProtectBuffer::AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab )
+{
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->maEnhancedProtections.push_back( rProt);
+}
+
+void XclImpSheetProtectBuffer::ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab )
+{
+ sal_uInt16 nHash = rStrm.ReaduInt16();
+ Sheet* pSheet = GetSheetItem(nTab);
+ if (pSheet)
+ pSheet->mnPasswordHash = nHash;
+}
+
+void XclImpSheetProtectBuffer::Apply() const
+{
+ for (const auto& [rTab, rSheet] : maProtectedSheets)
+ {
+ if (!rSheet.mbProtected)
+ // This sheet is (for whatever reason) not protected.
+ continue;
+
+ unique_ptr<ScTableProtection> pProtect(new ScTableProtection);
+ pProtect->setProtected(true);
+
+ // 16-bit hash password
+ const sal_uInt16 nHash = rSheet.mnPasswordHash;
+ if (nHash)
+ {
+ Sequence<sal_Int8> aPass{sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)};
+ pProtect->setPasswordHash(aPass, PASSHASH_XL);
+ }
+
+ // sheet protection options
+ const sal_uInt16 nOptions = rSheet.mnOptions;
+ pProtect->setOption( ScTableProtection::OBJECTS, (nOptions & 0x0001) );
+ pProtect->setOption( ScTableProtection::SCENARIOS, (nOptions & 0x0002) );
+ pProtect->setOption( ScTableProtection::FORMAT_CELLS, (nOptions & 0x0004) );
+ pProtect->setOption( ScTableProtection::FORMAT_COLUMNS, (nOptions & 0x0008) );
+ pProtect->setOption( ScTableProtection::FORMAT_ROWS, (nOptions & 0x0010) );
+ pProtect->setOption( ScTableProtection::INSERT_COLUMNS, (nOptions & 0x0020) );
+ pProtect->setOption( ScTableProtection::INSERT_ROWS, (nOptions & 0x0040) );
+ pProtect->setOption( ScTableProtection::INSERT_HYPERLINKS, (nOptions & 0x0080) );
+ pProtect->setOption( ScTableProtection::DELETE_COLUMNS, (nOptions & 0x0100) );
+ pProtect->setOption( ScTableProtection::DELETE_ROWS, (nOptions & 0x0200) );
+ pProtect->setOption( ScTableProtection::SELECT_LOCKED_CELLS, (nOptions & 0x0400) );
+ pProtect->setOption( ScTableProtection::SORT, (nOptions & 0x0800) );
+ pProtect->setOption( ScTableProtection::AUTOFILTER, (nOptions & 0x1000) );
+ pProtect->setOption( ScTableProtection::PIVOT_TABLES, (nOptions & 0x2000) );
+ pProtect->setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, (nOptions & 0x4000) );
+
+ // Enhanced protection containing editable ranges and permissions.
+ pProtect->setEnhancedProtection( std::vector(rSheet.maEnhancedProtections) );
+
+ // all done. now commit.
+ GetDoc().SetTabProtection(rTab, pProtect.get());
+ }
+}
+
+XclImpSheetProtectBuffer::Sheet* XclImpSheetProtectBuffer::GetSheetItem( SCTAB nTab )
+{
+ ProtectedSheetMap::iterator itr = maProtectedSheets.find(nTab);
+ if (itr == maProtectedSheets.end())
+ {
+ // new sheet
+ if ( !maProtectedSheets.emplace( nTab, Sheet() ).second )
+ return nullptr;
+
+ itr = maProtectedSheets.find(nTab);
+ }
+
+ return &itr->second;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiescher.cxx b/sc/source/filter/excel/xiescher.cxx
new file mode 100644
index 000000000..1de9da95d
--- /dev/null
+++ b/sc/source/filter/excel/xiescher.cxx
@@ -0,0 +1,4453 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiescher.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/awt/PushButtonType.hpp>
+#include <com/sun/star/awt/ScrollBarOrientation.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/form/binding/XListEntrySource.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <sfx2/objsh.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/wmf.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/documentinfo.hxx>
+#include <o3tl/safeint.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <sal/log.hxx>
+
+#include <svx/svdopath.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svditer.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnedcit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/sdasitm.hxx>
+#include <svx/sdshcitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtditm.hxx>
+
+#include <editeng/eeitem.hxx>
+#include <svx/xflclit.hxx>
+#include <sal/macros.h>
+#include <editeng/adjustitem.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xbitmap.hxx>
+#include <svtools/embedhlp.hxx>
+#include <sot/storage.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <userdat.hxx>
+#include <unonames.hxx>
+#include <convuno.hxx>
+#include <postit.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <fprogressbar.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiformula.hxx>
+#include <xilink.hxx>
+#include <xistyle.hxx>
+#include <xipage.hxx>
+#include <xichart.hxx>
+#include <xicontent.hxx>
+#include <scextopt.hxx>
+
+#include <namebuff.hxx>
+#include <sfx2/docfile.hxx>
+#include <memory>
+#include <numeric>
+#include <string_view>
+#include <utility>
+
+using namespace com::sun::star;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::beans::NamedValue;
+using ::com::sun::star::lang::XMultiServiceFactory;
+using ::com::sun::star::container::XIndexContainer;
+using ::com::sun::star::container::XNameContainer;
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::embed::XEmbeddedObject;
+using ::com::sun::star::embed::XEmbedPersist;
+using ::com::sun::star::drawing::XControlShape;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::form::XFormComponent;
+using ::com::sun::star::form::XFormsSupplier;
+using ::com::sun::star::form::binding::XBindableValue;
+using ::com::sun::star::form::binding::XValueBinding;
+using ::com::sun::star::form::binding::XListEntrySink;
+using ::com::sun::star::form::binding::XListEntrySource;
+using ::com::sun::star::script::ScriptEventDescriptor;
+using ::com::sun::star::script::XEventAttacherManager;
+using ::com::sun::star::table::CellAddress;
+using ::com::sun::star::table::CellRangeAddress;
+
+// Drawing objects ============================================================
+
+XclImpDrawObjBase::XclImpDrawObjBase( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnObjId( EXC_OBJ_INVALID_ID ),
+ mnTab( 0 ),
+ mnObjType( EXC_OBJTYPE_UNKNOWN ),
+ mnDffShapeId( 0 ),
+ mnDffFlags( ShapeFlag::NONE ),
+ mbHasAnchor( false ),
+ mbHidden( false ),
+ mbVisible( true ),
+ mbPrintable( true ),
+ mbAreaObj( false ),
+ mbAutoMargin( true ),
+ mbSimpleMacro( true ),
+ mbProcessSdr( true ),
+ mbInsertSdr( true ),
+ mbCustomDff( false ),
+ mbNotifyMacroEventRead( false )
+{
+}
+
+XclImpDrawObjBase::~XclImpDrawObjBase()
+{
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj3( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 30 )
+ {
+ sal_uInt16 nObjType;
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj= std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj= std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj= std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj= std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj= std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj= std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj= std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj= std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj= std::make_shared<XclImpPictureObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj3 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+
+ if (!xDrawObj)
+ {
+ xDrawObj = std::make_shared<XclImpPhObj>(rRoot);
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj3( rStrm );
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj4( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 30 )
+ {
+ sal_uInt16 nObjType;
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj = std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj = std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj = std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj = std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_POLYGON: xDrawObj = std::make_shared<XclImpPolygonObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj4 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+
+ if (!xDrawObj)
+ {
+ xDrawObj = std::make_shared<XclImpPhObj>(rRoot);
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj4( rStrm );
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj5( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 34 )
+ {
+ sal_uInt16 nObjType(EXC_OBJTYPE_UNKNOWN);
+ rStrm.Ignore( 4 );
+ nObjType = rStrm.ReaduInt16();
+ switch( nObjType )
+ {
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_LINE: xDrawObj = std::make_shared<XclImpLineObj>( rRoot ); break;
+ case EXC_OBJTYPE_RECTANGLE: xDrawObj = std::make_shared<XclImpRectObj>( rRoot ); break;
+ case EXC_OBJTYPE_OVAL: xDrawObj = std::make_shared<XclImpOvalObj>( rRoot ); break;
+ case EXC_OBJTYPE_ARC: xDrawObj = std::make_shared<XclImpArcObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_TEXT: xDrawObj = std::make_shared<XclImpTextObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_POLYGON: xDrawObj = std::make_shared<XclImpPolygonObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHECKBOX: xDrawObj = std::make_shared<XclImpCheckBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj = std::make_shared<XclImpOptionButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_EDIT: xDrawObj = std::make_shared<XclImpEditObj>( rRoot ); break;
+ case EXC_OBJTYPE_LABEL: xDrawObj = std::make_shared<XclImpLabelObj>( rRoot ); break;
+ case EXC_OBJTYPE_DIALOG: xDrawObj = std::make_shared<XclImpDialogObj>( rRoot ); break;
+ case EXC_OBJTYPE_SPIN: xDrawObj = std::make_shared<XclImpSpinButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_SCROLLBAR: xDrawObj = std::make_shared<XclImpScrollBarObj>( rRoot ); break;
+ case EXC_OBJTYPE_LISTBOX: xDrawObj = std::make_shared<XclImpListBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_GROUPBOX: xDrawObj = std::make_shared<XclImpGroupBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_DROPDOWN: xDrawObj = std::make_shared<XclImpDropDownObj>( rRoot ); break;
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj5 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ xDrawObj = std::make_shared<XclImpPhObj>( rRoot );
+ }
+ }
+
+ OSL_ENSURE(xDrawObj, "object import failed");
+
+ if (xDrawObj)
+ {
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj5( rStrm );
+ }
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawObjBase::ReadObj8( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ if( rStrm.GetRecLeft() >= 10 )
+ {
+ sal_uInt16 nSubRecId(0), nSubRecSize(0), nObjType(0);
+ nSubRecId = rStrm.ReaduInt16();
+ nSubRecSize = rStrm.ReaduInt16();
+ nObjType = rStrm.ReaduInt16();
+ OSL_ENSURE( nSubRecId == EXC_ID_OBJCMO, "XclImpDrawObjBase::ReadObj8 - OBJCMO subrecord expected" );
+ if( (nSubRecId == EXC_ID_OBJCMO) && (nSubRecSize >= 6) )
+ {
+ switch( nObjType )
+ {
+ // in BIFF8, all simple objects support text
+ case EXC_OBJTYPE_LINE:
+ case EXC_OBJTYPE_ARC:
+ xDrawObj = std::make_shared<XclImpTextObj>( rRoot );
+ // lines and arcs may be 2-dimensional
+ xDrawObj->SetAreaObj( false );
+ break;
+
+ // in BIFF8, all simple objects support text
+ case EXC_OBJTYPE_RECTANGLE:
+ case EXC_OBJTYPE_OVAL:
+ case EXC_OBJTYPE_POLYGON:
+ case EXC_OBJTYPE_DRAWING:
+ case EXC_OBJTYPE_TEXT:
+ xDrawObj = std::make_shared<XclImpTextObj>( rRoot );
+ break;
+
+ case EXC_OBJTYPE_GROUP: xDrawObj = std::make_shared<XclImpGroupObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHART: xDrawObj = std::make_shared<XclImpChartObj>( rRoot ); break;
+ case EXC_OBJTYPE_BUTTON: xDrawObj = std::make_shared<XclImpButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_PICTURE: xDrawObj = std::make_shared<XclImpPictureObj>( rRoot ); break;
+ case EXC_OBJTYPE_CHECKBOX: xDrawObj = std::make_shared<XclImpCheckBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_OPTIONBUTTON: xDrawObj = std::make_shared<XclImpOptionButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_EDIT: xDrawObj = std::make_shared<XclImpEditObj>( rRoot ); break;
+ case EXC_OBJTYPE_LABEL: xDrawObj = std::make_shared<XclImpLabelObj>( rRoot ); break;
+ case EXC_OBJTYPE_DIALOG: xDrawObj = std::make_shared<XclImpDialogObj>( rRoot ); break;
+ case EXC_OBJTYPE_SPIN: xDrawObj = std::make_shared<XclImpSpinButtonObj>( rRoot ); break;
+ case EXC_OBJTYPE_SCROLLBAR: xDrawObj = std::make_shared<XclImpScrollBarObj>( rRoot ); break;
+ case EXC_OBJTYPE_LISTBOX: xDrawObj = std::make_shared<XclImpListBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_GROUPBOX: xDrawObj = std::make_shared<XclImpGroupBoxObj>( rRoot ); break;
+ case EXC_OBJTYPE_DROPDOWN: xDrawObj = std::make_shared<XclImpDropDownObj>( rRoot ); break;
+ case EXC_OBJTYPE_NOTE: xDrawObj = std::make_shared<XclImpNoteObj>( rRoot ); break;
+
+ default:
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::ReadObj8 - unknown object type 0x" << std::hex << nObjType );
+ rRoot.GetTracer().TraceUnsupportedObjects();
+ }
+ }
+ }
+
+ if (!xDrawObj) //ensure placeholder for unknown or broken records
+ {
+ SAL_WARN( "sc.filter", "XclImpDrawObjBase::ReadObj8 import failed, substituting placeholder");
+ xDrawObj = std::make_shared<XclImpPhObj>( rRoot );
+ }
+
+ xDrawObj->mnTab = rRoot.GetCurrScTab();
+ xDrawObj->ImplReadObj8( rStrm );
+ return xDrawObj;
+}
+
+void XclImpDrawObjBase::SetAnchor( const XclObjAnchor& rAnchor )
+{
+ maAnchor = rAnchor;
+ mbHasAnchor = true;
+}
+
+void XclImpDrawObjBase::SetDffData(
+ const DffObjData& rDffObjData, const OUString& rObjName, const OUString& rHyperlink,
+ bool bVisible, bool bAutoMargin )
+{
+ mnDffShapeId = rDffObjData.nShapeId;
+ mnDffFlags = rDffObjData.nSpFlags;
+ maObjName = rObjName;
+ maHyperlink = rHyperlink;
+ mbVisible = bVisible;
+ mbAutoMargin = bAutoMargin;
+}
+
+OUString XclImpDrawObjBase::GetObjName() const
+{
+ /* #i51348# Always return a non-empty name. Create English
+ default names depending on the object type. This is not implemented as
+ virtual functions in derived classes, as class type and object type may
+ not match. */
+ return maObjName.isEmpty() ? GetObjectManager().GetDefaultObjName(*this) : maObjName;
+}
+
+const XclObjAnchor* XclImpDrawObjBase::GetAnchor() const
+{
+ return mbHasAnchor ? &maAnchor : nullptr;
+}
+
+bool XclImpDrawObjBase::IsValidSize( const tools::Rectangle& rAnchorRect ) const
+{
+ // XclObjAnchor rounds up the width, width of 3 is the result of an Excel width of 0
+ return mbAreaObj ?
+ ((rAnchorRect.GetWidth() > 3) && (rAnchorRect.GetHeight() > 1)) :
+ ((rAnchorRect.GetWidth() > 3) || (rAnchorRect.GetHeight() > 1));
+}
+
+ScRange XclImpDrawObjBase::GetUsedArea( SCTAB nScTab ) const
+{
+ ScRange aScUsedArea( ScAddress::INITIALIZE_INVALID );
+ // #i44077# object inserted -> update used area for OLE object import
+ if( mbHasAnchor && GetAddressConverter().ConvertRange( aScUsedArea, maAnchor, nScTab, nScTab, false ) )
+ {
+ // reduce range, if object ends directly on borders between two columns or rows
+ if( (maAnchor.mnRX == 0) && (aScUsedArea.aStart.Col() < aScUsedArea.aEnd.Col()) )
+ aScUsedArea.aEnd.IncCol( -1 );
+ if( (maAnchor.mnBY == 0) && (aScUsedArea.aStart.Row() < aScUsedArea.aEnd.Row()) )
+ aScUsedArea.aEnd.IncRow( -1 );
+ }
+ return aScUsedArea;
+}
+
+std::size_t XclImpDrawObjBase::GetProgressSize() const
+{
+ return DoGetProgressSize();
+}
+
+SdrObjectUniquePtr XclImpDrawObjBase::CreateSdrObject( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect, bool bIsDff ) const
+{
+ SdrObjectUniquePtr xSdrObj;
+ if( bIsDff && !mbCustomDff )
+ {
+ rDffConv.Progress( GetProgressSize() );
+ }
+ else
+ {
+ xSdrObj = DoCreateSdrObj( rDffConv, rAnchorRect );
+
+ //added for exporting OCX control
+ /* mnObjType value set should be as below table:
+ 0x0000 Group 0x0001 Line
+ 0x0002 Rectangle 0x0003 Oval
+ 0x0004 Arc 0x0005 Chart
+ 0x0006 Text 0x0009 Polygon
+ +-----------------------------------------------------+
+ OCX ==>| 0x0008 Picture |
+ +-----------------------------------------------------+
+ | 0x0007 Button |
+ | 0x000B Checkbox 0x000C Radio button |
+ | 0x000D Edit box 0x000E Label |
+ TBX ==> | 0x000F Dialog box 0x0010 Spin control |
+ | 0x0011 Scrollbar 0x0012 List |
+ | 0x0013 Group box 0x0014 Dropdown list |
+ +-----------------------------------------------------+
+ 0x0019 Note 0x001E OfficeArt object
+ */
+ if( xSdrObj && xSdrObj->IsUnoObj() &&
+ ( (mnObjType < 25 && mnObjType > 10) || mnObjType == 7 || mnObjType == 8 ) )
+ {
+ SdrUnoObj* pSdrUnoObj = dynamic_cast< SdrUnoObj* >( xSdrObj.get() );
+ if( pSdrUnoObj != nullptr )
+ {
+ const Reference< XControlModel >& xCtrlModel = pSdrUnoObj->GetUnoControlModel();
+ Reference< XPropertySet > xPropSet(xCtrlModel,UNO_QUERY);
+ static constexpr OUStringLiteral sPropertyName(u"ControlTypeinMSO");
+
+ enum { eCreateFromOffice = 0, eCreateFromMSTBXControl, eCreateFromMSOCXControl };
+
+ if( mnObjType == 7 || (mnObjType < 25 && mnObjType > 10) )//TBX
+ {
+ try
+ {
+ //Need summary type for export. Detail type(checkbox, button ...) has been contained by mnObjType
+ const sal_Int16 nTBXControlType = eCreateFromMSTBXControl ;
+ xPropSet->setPropertyValue(sPropertyName, Any(nTBXControlType));
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::CreateSdrObject, this control can't be set the property ControlTypeinMSO!");
+ }
+ }
+ if( mnObjType == 8 )//OCX
+ {
+ //Need summary type for export
+ static constexpr OUStringLiteral sObjIdPropertyName(u"ObjIDinMSO");
+ const XclImpPictureObj* const pObj = dynamic_cast< const XclImpPictureObj* const >(this);
+ if( pObj != nullptr && pObj->IsOcxControl() )
+ {
+ try
+ {
+ const sal_Int16 nOCXControlType = eCreateFromMSOCXControl;
+ xPropSet->setPropertyValue(sPropertyName, Any(nOCXControlType));
+ //Detail type(checkbox, button ...)
+ xPropSet->setPropertyValue(sObjIdPropertyName, Any(sal_uInt16(mnObjId)));
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc.filter", "XclImpDrawObjBase::CreateSdrObject, this control can't be set the property ObjIDinMSO!");
+ }
+ }
+ }
+
+ }
+ }
+ }
+ return xSdrObj;
+}
+
+void XclImpDrawObjBase::NotifyMacroEventRead()
+{
+ if (mbNotifyMacroEventRead)
+ return;
+ SfxObjectShell* pDocShell = GetDocShell();
+ if (!pDocShell)
+ return;
+ comphelper::DocumentInfo::notifyMacroEventRead(pDocShell->GetModel());
+ mbNotifyMacroEventRead = true;
+}
+
+void XclImpDrawObjBase::PreProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj )
+{
+ // default: front layer, derived classes may have to set other layer in DoPreProcessSdrObj()
+ rSdrObj.NbcSetLayer( SC_LAYER_FRONT );
+
+ // set object name (GetObjName() will always return a non-empty name)
+ rSdrObj.SetName( GetObjName() );
+
+ // #i39167# full width for all objects regardless of horizontal alignment
+ rSdrObj.SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) );
+
+ // automatic text margin
+ if( mbAutoMargin )
+ {
+ sal_Int32 nMargin = rDffConv.GetDefaultTextMargin();
+ rSdrObj.SetMergedItem( makeSdrTextLeftDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextRightDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextUpperDistItem( nMargin ) );
+ rSdrObj.SetMergedItem( makeSdrTextLowerDistItem( nMargin ) );
+ }
+
+ // macro and hyperlink
+ // removed oracle/sun check for mbSimpleMacro ( no idea what its for )
+ if (!maMacroName.isEmpty())
+ {
+ if( ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo( &rSdrObj, true ) )
+ {
+ OUString sMacro = XclTools::GetSbMacroUrl(maMacroName, GetDocShell());
+ if (!sMacro.isEmpty())
+ NotifyMacroEventRead();
+ pInfo->SetMacro(sMacro);
+ }
+ }
+ if (!maHyperlink.isEmpty())
+ rSdrObj.setHyperlink(maHyperlink);
+
+ // call virtual function for object type specific processing
+ DoPreProcessSdrObj( rDffConv, rSdrObj );
+}
+
+void XclImpDrawObjBase::PostProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // call virtual function for object type specific processing
+ DoPostProcessSdrObj( rDffConv, rSdrObj );
+}
+
+// protected ------------------------------------------------------------------
+
+void XclImpDrawObjBase::ReadName5( XclImpStream& rStrm, sal_uInt16 nNameLen )
+{
+ maObjName.clear();
+ if( nNameLen > 0 )
+ {
+ // name length field is repeated before the name
+ maObjName = rStrm.ReadByteString( false );
+ // skip padding byte for word boundaries
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+ }
+}
+
+void XclImpDrawObjBase::ReadMacro3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+ // skip padding byte for word boundaries, not contained in nMacroSize
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+}
+
+void XclImpDrawObjBase::ReadMacro4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+}
+
+void XclImpDrawObjBase::ReadMacro5( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ maMacroName.clear();
+ rStrm.Ignore( nMacroSize );
+}
+
+void XclImpDrawObjBase::ReadMacro8( XclImpStream& rStrm )
+{
+ maMacroName.clear();
+ if( rStrm.GetRecLeft() <= 6 )
+ return;
+
+ // macro is stored in a tNameXR token containing a link to a defined name
+ sal_uInt16 nFmlaSize;
+ nFmlaSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ OSL_ENSURE( nFmlaSize == 7, "XclImpDrawObjBase::ReadMacro - unexpected formula size" );
+ if( nFmlaSize == 7 )
+ {
+ sal_uInt8 nTokenId;
+ sal_uInt16 nExtSheet, nExtName;
+ nTokenId = rStrm.ReaduInt8();
+ nExtSheet = rStrm.ReaduInt16();
+ nExtName = rStrm.ReaduInt16();
+ OSL_ENSURE( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ),
+ "XclImpDrawObjBase::ReadMacro - tNameXR token expected" );
+ if( nTokenId == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) )
+ maMacroName = GetLinkManager().GetMacroName( nExtSheet, nExtName );
+ }
+}
+
+void XclImpDrawObjBase::ConvertLineStyle( SdrObject& rSdrObj, const XclObjLineData& rLineData ) const
+{
+ if( rLineData.IsAuto() )
+ {
+ XclObjLineData aAutoData;
+ aAutoData.mnAuto = 0;
+ ConvertLineStyle( rSdrObj, aAutoData );
+ }
+ else
+ {
+ tools::Long nLineWidth = 35 * ::std::min( rLineData.mnWidth, EXC_OBJ_LINE_THICK );
+ rSdrObj.SetMergedItem( XLineWidthItem( nLineWidth ) );
+ rSdrObj.SetMergedItem( XLineColorItem( OUString(), GetPalette().GetColor( rLineData.mnColorIdx ) ) );
+ rSdrObj.SetMergedItem( XLineJointItem( css::drawing::LineJoint_MITER ) );
+
+ sal_uLong nDotLen = ::std::max< sal_uLong >( 70 * rLineData.mnWidth, 35 );
+ sal_uLong nDashLen = 3 * nDotLen;
+ sal_uLong nDist = 2 * nDotLen;
+
+ switch( rLineData.mnStyle )
+ {
+ default:
+ case EXC_OBJ_LINE_SOLID:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ break;
+ case EXC_OBJ_LINE_DASH:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 0, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 1, nDotLen, 0, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DASHDOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 1, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_DASHDOTDOT:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) );
+ rSdrObj.SetMergedItem( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECT, 2, nDotLen, 1, nDashLen, nDist ) ) );
+ break;
+ case EXC_OBJ_LINE_MEDTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 50 ) );
+ break;
+ case EXC_OBJ_LINE_DARKTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 25 ) );
+ break;
+ case EXC_OBJ_LINE_LIGHTTRANS:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XLineTransparenceItem( 75 ) );
+ break;
+ case EXC_OBJ_LINE_NONE:
+ rSdrObj.SetMergedItem( XLineStyleItem( drawing::LineStyle_NONE ) );
+ break;
+ }
+ }
+}
+
+void XclImpDrawObjBase::ConvertFillStyle( SdrObject& rSdrObj, const XclObjFillData& rFillData ) const
+{
+ if( rFillData.IsAuto() )
+ {
+ XclObjFillData aAutoData;
+ aAutoData.mnAuto = 0;
+ ConvertFillStyle( rSdrObj, aAutoData );
+ }
+ else if( rFillData.mnPattern == EXC_PATT_NONE )
+ {
+ rSdrObj.SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) );
+ }
+ else
+ {
+ Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx );
+ Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx );
+ if( (rFillData.mnPattern == EXC_PATT_SOLID) || (aPattColor == aBackColor) )
+ {
+ rSdrObj.SetMergedItem( XFillStyleItem( drawing::FillStyle_SOLID ) );
+ rSdrObj.SetMergedItem( XFillColorItem( OUString(), aPattColor ) );
+ }
+ else
+ {
+ static const sal_uInt8 sppnPatterns[][ 8 ] =
+ {
+ { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 },
+ { 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD },
+ { 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 },
+ { 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 },
+ { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC },
+ { 0x33, 0x66, 0xCC, 0x99, 0x33, 0x66, 0xCC, 0x99 },
+ { 0xCC, 0x66, 0x33, 0x99, 0xCC, 0x66, 0x33, 0x99 },
+ { 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33 },
+ { 0xCC, 0xFF, 0x33, 0xFF, 0xCC, 0xFF, 0x33, 0xFF },
+ { 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 },
+ { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88 },
+ { 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88 },
+ { 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11 },
+ { 0xFF, 0x11, 0x11, 0x11, 0xFF, 0x11, 0x11, 0x11 },
+ { 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11 },
+ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
+ { 0x80, 0x00, 0x08, 0x00, 0x80, 0x00, 0x08, 0x00 }
+ };
+ const sal_uInt8* const pnPattern = sppnPatterns[std::min<size_t>(rFillData.mnPattern - 2, SAL_N_ELEMENTS(sppnPatterns) - 1)];
+ // create 2-colored 8x8 DIB
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt32( 12 ).WriteInt16( 8 ).WriteInt16( 8 ).WriteUInt16( 1 ).WriteUInt16( 1 );
+ aMemStrm.WriteUChar( 0xFF ).WriteUChar( 0xFF ).WriteUChar( 0xFF );
+ aMemStrm.WriteUChar( 0x00 ).WriteUChar( 0x00 ).WriteUChar( 0x00 );
+ for( size_t nIdx = 0; nIdx < 8; ++nIdx )
+ aMemStrm.WriteUInt32( pnPattern[ nIdx ] ); // 32-bit little-endian
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ Bitmap aBitmap;
+ (void)ReadDIB(aBitmap, aMemStrm, false);
+
+ XOBitmap aXOBitmap(( BitmapEx(aBitmap) ));
+ aXOBitmap.Bitmap2Array();
+ if( aXOBitmap.GetBackgroundColor() == COL_BLACK )
+ ::std::swap( aPattColor, aBackColor );
+ aXOBitmap.SetPixelColor( aPattColor );
+ aXOBitmap.SetBackgroundColor( aBackColor );
+ aXOBitmap.Array2Bitmap();
+ aBitmap = aXOBitmap.GetBitmap().GetBitmap();
+
+ rSdrObj.SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ rSdrObj.SetMergedItem(XFillBitmapItem(OUString(), Graphic(BitmapEx(aBitmap))));
+ }
+ }
+}
+
+void XclImpDrawObjBase::ConvertFrameStyle( SdrObject& rSdrObj, sal_uInt16 nFrameFlags ) const
+{
+ if( ::get_flag( nFrameFlags, EXC_OBJ_FRAME_SHADOW ) )
+ {
+ rSdrObj.SetMergedItem( makeSdrShadowItem( true ) );
+ rSdrObj.SetMergedItem( makeSdrShadowXDistItem( 35 ) );
+ rSdrObj.SetMergedItem( makeSdrShadowYDistItem( 35 ) );
+ rSdrObj.SetMergedItem( makeSdrShadowColorItem( GetPalette().GetColor( EXC_COLOR_WINDOWTEXT ) ) );
+ }
+}
+
+Color XclImpDrawObjBase::GetSolidLineColor( const XclObjLineData& rLineData ) const
+{
+ Color aColor( COL_TRANSPARENT );
+ if( rLineData.IsAuto() )
+ {
+ XclObjLineData aAutoData;
+ aAutoData.mnAuto = 0;
+ aColor = GetSolidLineColor( aAutoData );
+ }
+ else if( rLineData.mnStyle != EXC_OBJ_LINE_NONE )
+ {
+ aColor = GetPalette().GetColor( rLineData.mnColorIdx );
+ }
+ return aColor;
+}
+
+Color XclImpDrawObjBase::GetSolidFillColor( const XclObjFillData& rFillData ) const
+{
+ Color aColor( COL_TRANSPARENT );
+ if( rFillData.IsAuto() )
+ {
+ XclObjFillData aAutoData;
+ aAutoData.mnAuto = 0;
+ aColor = GetSolidFillColor( aAutoData );
+ }
+ else if( rFillData.mnPattern != EXC_PATT_NONE )
+ {
+ Color aPattColor = GetPalette().GetColor( rFillData.mnPattColorIdx );
+ Color aBackColor = GetPalette().GetColor( rFillData.mnBackColorIdx );
+ aColor = XclTools::GetPatternColor( aPattColor, aBackColor, rFillData.mnPattern );
+ }
+ return aColor;
+}
+
+void XclImpDrawObjBase::DoReadObj3( XclImpStream&, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj4( XclImpStream&, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj5( XclImpStream&, sal_uInt16, sal_uInt16 )
+{
+}
+
+void XclImpDrawObjBase::DoReadObj8SubRec( XclImpStream&, sal_uInt16, sal_uInt16 )
+{
+}
+
+std::size_t XclImpDrawObjBase::DoGetProgressSize() const
+{
+ return 1;
+}
+
+SdrObjectUniquePtr XclImpDrawObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& ) const
+{
+ rDffConv.Progress( GetProgressSize() );
+ return nullptr;
+}
+
+void XclImpDrawObjBase::DoPreProcessSdrObj( XclImpDffConverter&, SdrObject& ) const
+{
+ // trace if object is not printable
+ if( !IsPrintable() )
+ GetTracer().TraceObjectNotPrintable();
+}
+
+void XclImpDrawObjBase::DoPostProcessSdrObj( XclImpDffConverter&, SdrObject& ) const
+{
+}
+
+void XclImpDrawObjBase::ImplReadObj3( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ DoReadObj3( rStrm, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj4( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE );
+ DoReadObj4( rStrm, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj5( XclImpStream& rStrm )
+{
+ // back to offset 4 (ignore object count field)
+ rStrm.Seek( 4 );
+
+ sal_uInt16 nObjFlags, nMacroSize, nNameLen;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16();
+ nObjFlags = rStrm.ReaduInt16();
+ rStrm >> maAnchor;
+ nMacroSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ nNameLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+
+ mbHasAnchor = true;
+ mbHidden = ::get_flag( nObjFlags, EXC_OBJ_HIDDEN );
+ mbVisible = ::get_flag( nObjFlags, EXC_OBJ_VISIBLE );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJ_PRINTABLE );
+ DoReadObj5( rStrm, nNameLen, nMacroSize );
+}
+
+void XclImpDrawObjBase::ImplReadObj8( XclImpStream& rStrm )
+{
+ // back to beginning
+ rStrm.Seek( EXC_REC_SEEK_TO_BEGIN );
+
+ bool bLoop = true;
+ while (bLoop)
+ {
+ if (rStrm.GetRecLeft() < 4)
+ break;
+
+ sal_uInt16 nSubRecId = rStrm.ReaduInt16();
+ sal_uInt16 nSubRecSize = rStrm.ReaduInt16();
+ rStrm.PushPosition();
+ // sometimes the last subrecord has an invalid length (OBJLBSDATA) -> min()
+ nSubRecSize = static_cast< sal_uInt16 >( ::std::min< std::size_t >( nSubRecSize, rStrm.GetRecLeft() ) );
+
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJCMO:
+ OSL_ENSURE( rStrm.GetRecPos() == 4, "XclImpDrawObjBase::ImplReadObj8 - unexpected OBJCMO subrecord" );
+ if( (rStrm.GetRecPos() == 4) && (nSubRecSize >= 6) )
+ {
+ sal_uInt16 nObjFlags;
+ mnObjType = rStrm.ReaduInt16();
+ mnObjId = rStrm.ReaduInt16( );
+ nObjFlags = rStrm.ReaduInt16( );
+ mbPrintable = ::get_flag( nObjFlags, EXC_OBJCMO_PRINTABLE );
+ }
+ break;
+ case EXC_ID_OBJMACRO:
+ ReadMacro8( rStrm );
+ break;
+ case EXC_ID_OBJEND:
+ bLoop = false;
+ break;
+ default:
+ DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+
+ rStrm.PopPosition();
+ rStrm.Ignore( nSubRecSize );
+ }
+
+ /* Call DoReadObj8SubRec() with EXC_ID_OBJEND for further stream
+ processing (e.g. charts), even if the OBJEND subrecord is missing. */
+ DoReadObj8SubRec( rStrm, EXC_ID_OBJEND, 0 );
+
+ /* Pictures that Excel reads from BIFF5 and writes to BIFF8 still have the
+ IMGDATA record following the OBJ record (but they use the image data
+ stored in DFF). The IMGDATA record may be continued by several CONTINUE
+ records. But the last CONTINUE record may be in fact an MSODRAWING
+ record that contains the DFF data of the next drawing object! So we
+ have to skip just enough CONTINUE records to look at the next
+ MSODRAWING/CONTINUE record. */
+ if( !((rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord()) )
+ return;
+
+ rStrm.Ignore( 4 );
+ sal_uInt32 nDataSize = rStrm.ReaduInt32();
+ nDataSize -= rStrm.GetRecLeft();
+ // skip following CONTINUE records until IMGDATA ends
+ while (true)
+ {
+ if (!nDataSize)
+ break;
+ if (rStrm.GetNextRecId() != EXC_ID_CONT)
+ break;
+ if (!rStrm.StartNextRecord())
+ break;
+ OSL_ENSURE( nDataSize >= rStrm.GetRecLeft(), "XclImpDrawObjBase::ImplReadObj8 - CONTINUE too long" );
+ nDataSize -= ::std::min< sal_uInt32 >( rStrm.GetRecLeft(), nDataSize );
+ }
+ OSL_ENSURE( nDataSize == 0, "XclImpDrawObjBase::ImplReadObj8 - missing CONTINUE records" );
+ // next record may be MSODRAWING or CONTINUE or anything else
+}
+
+void XclImpDrawObjVector::InsertGrouped( XclImpDrawObjRef const & xDrawObj )
+{
+ if( !mObjs.empty() )
+ if( XclImpGroupObj* pGroupObj = dynamic_cast< XclImpGroupObj* >( mObjs.back().get() ) )
+ if( pGroupObj->TryInsert( xDrawObj ) )
+ return;
+ mObjs.push_back( xDrawObj );
+}
+
+std::size_t XclImpDrawObjVector::GetProgressSize() const
+{
+ return std::accumulate(mObjs.begin(), mObjs.end(), std::size_t(0),
+ [](const std::size_t& rSum, const XclImpDrawObjRef& rxObj) { return rSum + rxObj->GetProgressSize(); });
+}
+
+XclImpPhObj::XclImpPhObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot )
+{
+ SetProcessSdrObj( false );
+}
+
+XclImpGroupObj::XclImpGroupObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnFirstUngrouped( 0 )
+{
+}
+
+bool XclImpGroupObj::TryInsert( XclImpDrawObjRef const & xDrawObj )
+{
+ if( xDrawObj->GetObjId() == mnFirstUngrouped )
+ return false;
+ // insert into own list or into nested group
+ maChildren.InsertGrouped( xDrawObj );
+ return true;
+}
+
+void XclImpGroupObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpGroupObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpGroupObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm.Ignore( 4 );
+ mnFirstUngrouped = rStrm.ReaduInt16();
+ rStrm.Ignore( 16 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+std::size_t XclImpGroupObj::DoGetProgressSize() const
+{
+ return XclImpDrawObjBase::DoGetProgressSize() + maChildren.GetProgressSize();
+}
+
+SdrObjectUniquePtr XclImpGroupObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& /*rAnchorRect*/ ) const
+{
+ std::unique_ptr<SdrObjGroup, SdrObjectFreeOp> xSdrObj(
+ new SdrObjGroup(
+ *GetDoc().GetDrawLayer()));
+ // child objects in BIFF2-BIFF5 have absolute size, not needed to pass own anchor rectangle
+ SdrObjList& rObjList = *xSdrObj->GetSubList(); // SdrObjGroup always returns existing sublist
+ for( const auto& rxChild : maChildren )
+ rDffConv.ProcessObject( rObjList, *rxChild );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpLineObj::XclImpLineObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnArrows( 0 ),
+ mnStartPoint( EXC_OBJ_LINE_TL )
+{
+ SetAreaObj( false );
+}
+
+void XclImpLineObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpLineObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpLineObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm >> maLineData;
+ mnArrows = rStrm.ReaduInt16();
+ mnStartPoint = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+SdrObjectUniquePtr XclImpLineObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ ::basegfx::B2DPolygon aB2DPolygon;
+ switch( mnStartPoint )
+ {
+ default:
+ case EXC_OBJ_LINE_TL:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) );
+ break;
+ case EXC_OBJ_LINE_TR:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) );
+ break;
+ case EXC_OBJ_LINE_BR:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Bottom() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Top() ) );
+ break;
+ case EXC_OBJ_LINE_BL:
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Left(), rAnchorRect.Bottom() ) );
+ aB2DPolygon.append( ::basegfx::B2DPoint( rAnchorRect.Right(), rAnchorRect.Top() ) );
+ break;
+ }
+ SdrObjectUniquePtr xSdrObj(
+ new SdrPathObj(
+ *GetDoc().GetDrawLayer(),
+ SdrObjKind::Line,
+ ::basegfx::B2DPolyPolygon(aB2DPolygon)));
+ ConvertLineStyle( *xSdrObj, maLineData );
+
+ // line ends
+ sal_uInt8 nArrowType = ::extract_value< sal_uInt8 >( mnArrows, 0, 4 );
+ bool bLineStart = false;
+ bool bLineEnd = false;
+ bool bFilled = false;
+ switch( nArrowType )
+ {
+ case EXC_OBJ_ARROW_OPEN: bLineStart = false; bLineEnd = true; bFilled = false; break;
+ case EXC_OBJ_ARROW_OPENBOTH: bLineStart = true; bLineEnd = true; bFilled = false; break;
+ case EXC_OBJ_ARROW_FILLED: bLineStart = false; bLineEnd = true; bFilled = true; break;
+ case EXC_OBJ_ARROW_FILLEDBOTH: bLineStart = true; bLineEnd = true; bFilled = true; break;
+ }
+ if( bLineStart || bLineEnd )
+ {
+ sal_uInt8 nArrowWidth = ::extract_value< sal_uInt8 >( mnArrows, 4, 4 );
+ double fArrowWidth = 3.0;
+ switch( nArrowWidth )
+ {
+ case EXC_OBJ_ARROW_NARROW: fArrowWidth = 2.0; break;
+ case EXC_OBJ_ARROW_MEDIUM: fArrowWidth = 3.0; break;
+ case EXC_OBJ_ARROW_WIDE: fArrowWidth = 5.0; break;
+ }
+
+ sal_uInt8 nArrowLength = ::extract_value< sal_uInt8 >( mnArrows, 8, 4 );
+ double fArrowLength = 3.0;
+ switch( nArrowLength )
+ {
+ case EXC_OBJ_ARROW_NARROW: fArrowLength = 2.5; break;
+ case EXC_OBJ_ARROW_MEDIUM: fArrowLength = 3.5; break;
+ case EXC_OBJ_ARROW_WIDE: fArrowLength = 6.0; break;
+ }
+
+ ::basegfx::B2DPolygon aArrowPoly;
+#define EXC_ARROW_POINT( x, y ) ::basegfx::B2DPoint( fArrowWidth * (x), fArrowLength * (y) )
+ if( bFilled )
+ {
+ aArrowPoly.append( EXC_ARROW_POINT( 0, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100, 100 ) );
+ }
+ else
+ {
+ sal_uInt8 nLineWidth = ::limit_cast< sal_uInt8 >( maLineData.mnWidth, EXC_OBJ_LINE_THIN, EXC_OBJ_LINE_THICK );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 0 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100, 100 - 3 * nLineWidth ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 100 - 5 * nLineWidth, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 50, 12 * nLineWidth ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 5 * nLineWidth, 100 ) );
+ aArrowPoly.append( EXC_ARROW_POINT( 0, 100 - 3 * nLineWidth ) );
+ }
+#undef EXC_ARROW_POINT
+
+ ::basegfx::B2DPolyPolygon aArrowPolyPoly( aArrowPoly );
+ tools::Long nWidth = static_cast< tools::Long >( 125 * fArrowWidth );
+ if( bLineStart )
+ {
+ xSdrObj->SetMergedItem( XLineStartItem( OUString(), aArrowPolyPoly ) );
+ xSdrObj->SetMergedItem( XLineStartWidthItem( nWidth ) );
+ xSdrObj->SetMergedItem( XLineStartCenterItem( false ) );
+ }
+ if( bLineEnd )
+ {
+ xSdrObj->SetMergedItem( XLineEndItem( OUString(), aArrowPolyPoly ) );
+ xSdrObj->SetMergedItem( XLineEndWidthItem( nWidth ) );
+ xSdrObj->SetMergedItem( XLineEndCenterItem( false ) );
+ }
+ }
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpRectObj::XclImpRectObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnFrameFlags( 0 )
+{
+ SetAreaObj( true );
+}
+
+void XclImpRectObj::ReadFrameData( XclImpStream& rStrm )
+{
+ rStrm >> maFillData >> maLineData;
+ mnFrameFlags = rStrm.ReaduInt16();
+}
+
+void XclImpRectObj::ConvertRectStyle( SdrObject& rSdrObj ) const
+{
+ ConvertLineStyle( rSdrObj, maLineData );
+ ConvertFillStyle( rSdrObj, maFillData );
+ ConvertFrameStyle( rSdrObj, mnFrameFlags );
+}
+
+void XclImpRectObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpRectObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpRectObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+SdrObjectUniquePtr XclImpRectObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj(
+ new SdrRectObj(
+ *GetDoc().GetDrawLayer(),
+ rAnchorRect));
+ ConvertRectStyle( *xSdrObj );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpOvalObj::XclImpOvalObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot )
+{
+}
+
+SdrObjectUniquePtr XclImpOvalObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj(
+ new SdrCircObj(
+ *GetDoc().GetDrawLayer(),
+ SdrCircKind::Full,
+ rAnchorRect));
+ ConvertRectStyle( *xSdrObj );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpArcObj::XclImpArcObj( const XclImpRoot& rRoot ) :
+ XclImpDrawObjBase( rRoot ),
+ mnQuadrant( EXC_OBJ_ARC_TR )
+{
+ SetAreaObj( false ); // arc may be 2-dimensional
+}
+
+void XclImpArcObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro3( rStrm, nMacroSize );
+}
+
+void XclImpArcObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadMacro4( rStrm, nMacroSize );
+}
+
+void XclImpArcObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ rStrm >> maFillData >> maLineData;
+ mnQuadrant = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+}
+
+SdrObjectUniquePtr XclImpArcObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ tools::Rectangle aNewRect = rAnchorRect;
+ Degree100 nStartAngle;
+ Degree100 nEndAngle;
+ switch( mnQuadrant )
+ {
+ default:
+ case EXC_OBJ_ARC_TR:
+ nStartAngle = 0_deg100;
+ nEndAngle = 9000_deg100;
+ aNewRect.AdjustLeft( -(rAnchorRect.GetWidth()) );
+ aNewRect.AdjustBottom(rAnchorRect.GetHeight() );
+ break;
+ case EXC_OBJ_ARC_TL:
+ nStartAngle = 9000_deg100;
+ nEndAngle = 18000_deg100;
+ aNewRect.AdjustRight(rAnchorRect.GetWidth() );
+ aNewRect.AdjustBottom(rAnchorRect.GetHeight() );
+ break;
+ case EXC_OBJ_ARC_BL:
+ nStartAngle = 18000_deg100;
+ nEndAngle = 27000_deg100;
+ aNewRect.AdjustRight(rAnchorRect.GetWidth() );
+ aNewRect.AdjustTop( -(rAnchorRect.GetHeight()) );
+ break;
+ case EXC_OBJ_ARC_BR:
+ nStartAngle = 27000_deg100;
+ nEndAngle = 0_deg100;
+ aNewRect.AdjustLeft( -(rAnchorRect.GetWidth()) );
+ aNewRect.AdjustTop( -(rAnchorRect.GetHeight()) );
+ break;
+ }
+ SdrCircKind eObjKind = maFillData.IsFilled() ? SdrCircKind::Section : SdrCircKind::Arc;
+ SdrObjectUniquePtr xSdrObj(
+ new SdrCircObj(
+ *GetDoc().GetDrawLayer(),
+ eObjKind,
+ aNewRect,
+ nStartAngle,
+ nEndAngle));
+ ConvertFillStyle( *xSdrObj, maFillData );
+ ConvertLineStyle( *xSdrObj, maLineData );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+XclImpPolygonObj::XclImpPolygonObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot ),
+ mnPolyFlags( 0 ),
+ mnPointCount( 0 )
+{
+ SetAreaObj( false ); // polygon may be 2-dimensional
+}
+
+void XclImpPolygonObj::ReadCoordList( XclImpStream& rStrm )
+{
+ if( (rStrm.GetNextRecId() == EXC_ID_COORDLIST) && rStrm.StartNextRecord() )
+ {
+ OSL_ENSURE( rStrm.GetRecLeft() / 4 == mnPointCount, "XclImpPolygonObj::ReadCoordList - wrong polygon point count" );
+ while (true)
+ {
+ if (rStrm.GetRecLeft() < 4)
+ break;
+ sal_uInt16 nX = rStrm.ReaduInt16();
+ sal_uInt16 nY = rStrm.ReaduInt16();
+ maCoords.emplace_back( nX, nY );
+ }
+ }
+}
+
+void XclImpPolygonObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ mnPolyFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnPointCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+ ReadMacro4( rStrm, nMacroSize );
+ ReadCoordList( rStrm );
+}
+
+void XclImpPolygonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ mnPolyFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 10 );
+ mnPointCount = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadCoordList( rStrm );
+}
+
+namespace {
+
+::basegfx::B2DPoint lclGetPolyPoint( const tools::Rectangle& rAnchorRect, const Point& rPoint )
+{
+ return ::basegfx::B2DPoint(
+ rAnchorRect.Left() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.X(), 16384.0 ) / 16384.0 * rAnchorRect.GetWidth() + 0.5 ),
+ rAnchorRect.Top() + static_cast< sal_Int32 >( ::std::min< double >( rPoint.Y(), 16384.0 ) / 16384.0 * rAnchorRect.GetHeight() + 0.5 ) );
+}
+
+} // namespace
+
+SdrObjectUniquePtr XclImpPolygonObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj;
+ if( maCoords.size() >= 2 )
+ {
+ // create the polygon
+ ::basegfx::B2DPolygon aB2DPolygon;
+ for( const auto& rCoord : maCoords )
+ aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, rCoord ) );
+ // close polygon if specified
+ if( ::get_flag( mnPolyFlags, EXC_OBJ_POLY_CLOSED ) && (maCoords.front() != maCoords.back()) )
+ aB2DPolygon.append( lclGetPolyPoint( rAnchorRect, maCoords.front() ) );
+ // create the SdrObject
+ SdrObjKind eObjKind = maFillData.IsFilled() ? SdrObjKind::PathPoly : SdrObjKind::PathPolyLine;
+ xSdrObj.reset(
+ new SdrPathObj(
+ *GetDoc().GetDrawLayer(),
+ eObjKind,
+ ::basegfx::B2DPolyPolygon(aB2DPolygon)));
+ ConvertRectStyle( *xSdrObj );
+ }
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpObjTextData::ReadByteString( XclImpStream& rStrm )
+{
+ mxString.reset();
+ if( maData.mnTextLen > 0 )
+ {
+ mxString = std::make_shared<XclImpString>( rStrm.ReadRawByteString( maData.mnTextLen ) );
+ // skip padding byte for word boundaries
+ if( rStrm.GetRecPos() & 1 ) rStrm.Ignore( 1 );
+ }
+}
+
+void XclImpObjTextData::ReadFormats( XclImpStream& rStrm )
+{
+ if( mxString )
+ mxString->ReadObjFormats( rStrm, maData.mnFormatSize );
+ else
+ rStrm.Ignore( maData.mnFormatSize );
+}
+
+XclImpTextObj::XclImpTextObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot )
+{
+}
+
+void XclImpTextObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj3( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ maTextData.ReadFormats( rStrm );
+}
+
+void XclImpTextObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj3( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ maTextData.ReadFormats( rStrm );
+}
+
+void XclImpTextObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ ReadFrameData( rStrm );
+ maTextData.maData.ReadObj5( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ maTextData.ReadByteString( rStrm );
+ rStrm.Ignore( maTextData.maData.mnLinkSize ); // ignore text link formula
+ maTextData.ReadFormats( rStrm );
+}
+
+SdrObjectUniquePtr XclImpTextObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ std::unique_ptr<SdrObjCustomShape, SdrObjectFreeOp> xSdrObj(
+ new SdrObjCustomShape(
+ *GetDoc().GetDrawLayer()));
+ xSdrObj->NbcSetSnapRect( rAnchorRect );
+ OUString aRectType = "rectangle";
+ xSdrObj->MergeDefaultAttributes( &aRectType );
+ ConvertRectStyle( *xSdrObj );
+ bool bAutoSize = ::get_flag( maTextData.maData.mnFlags, EXC_OBJ_TEXT_AUTOSIZE );
+ xSdrObj->SetMergedItem( makeSdrTextAutoGrowWidthItem( bAutoSize ) );
+ xSdrObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( bAutoSize ) );
+ xSdrObj->SetMergedItem( makeSdrTextWordWrapItem( true ) );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpTextObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // set text data
+ if( SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( &rSdrObj ) )
+ {
+ if( maTextData.mxString )
+ {
+ if( maTextData.mxString->IsRich() )
+ {
+ if (maTextData.mxString->GetText().getLength() > 1024 && utl::ConfigManager::IsFuzzing())
+ {
+ SAL_WARN("sc.filter", "truncating slow long rich text for fuzzing performance");
+ maTextData.mxString->SetText(maTextData.mxString->GetText().copy(0, 1024));
+ }
+
+ // rich text
+ std::unique_ptr< EditTextObject > xEditObj(
+ XclImpStringHelper::CreateTextObject( GetRoot(), *maTextData.mxString ) );
+ OutlinerParaObject aOutlineObj(std::move(xEditObj));
+ aOutlineObj.SetOutlinerMode( OutlinerMode::TextObject );
+ pTextObj->NbcSetOutlinerParaObject( std::move(aOutlineObj) );
+ }
+ else
+ {
+ // plain text
+ pTextObj->NbcSetText( maTextData.mxString->GetText() );
+ }
+
+ /* #i96858# Do not apply any formatting if there is no text.
+ SdrObjCustomShape::SetVerticalWriting (initiated from
+ SetMergedItem) calls SdrTextObj::ForceOutlinerParaObject which
+ ensures that we can erroneously write a ClientTextbox record
+ (with no content) while exporting to XLS, which can cause a
+ corrupted exported document. */
+
+ SvxAdjust eHorAlign = SvxAdjust::Left;
+ SdrTextVertAdjust eVerAlign = SDRTEXTVERTADJUST_TOP;
+
+ // orientation (this is only a fake, drawing does not support real text orientation)
+ namespace csst = ::com::sun::star::text;
+ csst::WritingMode eWriteMode = csst::WritingMode_LR_TB;
+ switch( maTextData.maData.mnOrient )
+ {
+ default:
+ case EXC_OBJ_ORIENT_NONE:
+ {
+ eWriteMode = csst::WritingMode_LR_TB;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_HOR_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_HOR_RIGHT: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_HOR_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_VER_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_VER_BOTTOM: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_VER_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ }
+ break;
+
+ case EXC_OBJ_ORIENT_90CCW:
+ {
+ if( SdrObjCustomShape* pObjCustomShape = dynamic_cast< SdrObjCustomShape* >( &rSdrObj ) )
+ {
+ css::beans::PropertyValue aTextRotateAngle;
+ aTextRotateAngle.Name = "TextRotateAngle";
+ aTextRotateAngle.Value <<= 180.0;
+ SdrCustomShapeGeometryItem aGeometryItem(pObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ aGeometryItem.SetPropertyValue( aTextRotateAngle );
+ pObjCustomShape->SetMergedItem( aGeometryItem );
+ }
+ eWriteMode = csst::WritingMode_TB_RL;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ MSO_Anchor eTextAnchor = static_cast<MSO_Anchor>(rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ));
+ switch( eTextAnchor )
+ {
+ case mso_anchorTopCentered :
+ case mso_anchorMiddleCentered :
+ case mso_anchorBottomCentered :
+ {
+ eHorAlign = SvxAdjust::Center;
+ }
+ break;
+
+ default:
+ {
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_VER_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_VER_BOTTOM: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_VER_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ }
+ }
+ }
+ break;
+
+ case EXC_OBJ_ORIENT_STACKED:
+ {
+ // sj: STACKED is not supported, maybe it can be optimized here a bit
+ [[fallthrough]];
+ }
+ case EXC_OBJ_ORIENT_90CW:
+ {
+ eWriteMode = csst::WritingMode_TB_RL;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: eVerAlign = SDRTEXTVERTADJUST_BOTTOM; break;
+ case EXC_OBJ_HOR_CENTER: eVerAlign = SDRTEXTVERTADJUST_CENTER; break;
+ case EXC_OBJ_HOR_RIGHT: eVerAlign = SDRTEXTVERTADJUST_TOP; break;
+ case EXC_OBJ_HOR_JUSTIFY: eVerAlign = SDRTEXTVERTADJUST_BLOCK; break;
+ }
+ MSO_Anchor eTextAnchor = static_cast<MSO_Anchor>(rDffConv.GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ));
+ switch ( eTextAnchor )
+ {
+ case mso_anchorTopCentered :
+ case mso_anchorMiddleCentered :
+ case mso_anchorBottomCentered :
+ {
+ eHorAlign = SvxAdjust::Center;
+ }
+ break;
+
+ default:
+ {
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eHorAlign = SvxAdjust::Left; break;
+ case EXC_OBJ_VER_CENTER: eHorAlign = SvxAdjust::Center; break;
+ case EXC_OBJ_VER_BOTTOM: eHorAlign = SvxAdjust::Right; break;
+ case EXC_OBJ_VER_JUSTIFY: eHorAlign = SvxAdjust::Block; break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ rSdrObj.SetMergedItem( SvxAdjustItem( eHorAlign, EE_PARA_JUST ) );
+ rSdrObj.SetMergedItem( SdrTextVertAdjustItem( eVerAlign ) );
+ rSdrObj.SetMergedItem( SvxWritingModeItem( eWriteMode, SDRATTR_TEXTDIRECTION ) );
+ }
+ }
+ // base class processing
+ XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+}
+
+XclImpChartObj::XclImpChartObj( const XclImpRoot& rRoot, bool bOwnTab ) :
+ XclImpRectObj( rRoot ),
+ mbOwnTab( bOwnTab )
+{
+ SetSimpleMacro( false );
+ SetCustomDffObj( true );
+}
+
+void XclImpChartObj::ReadChartSubStream( XclImpStream& rStrm )
+{
+ /* If chart is read from a chartsheet (mbOwnTab == true), the BOF record
+ has already been read. If chart is embedded as object, the next record
+ has to be the BOF record. */
+ if( mbOwnTab )
+ {
+ /* #i109800# The input stream may point somewhere inside the chart
+ substream and not exactly to the leading BOF record. To read this
+ record correctly in the following, the stream has to rewind it, so
+ that the next call to StartNextRecord() will find it correctly. */
+ if( rStrm.GetRecId() != EXC_ID5_BOF )
+ rStrm.RewindRecord();
+ }
+ else
+ {
+ if( (rStrm.GetNextRecId() == EXC_ID5_BOF) && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nBofType;
+ rStrm.Seek( 2 );
+ nBofType = rStrm.ReaduInt16();
+ SAL_WARN_IF( nBofType != EXC_BOF_CHART, "sc.filter", "XclImpChartObj::ReadChartSubStream - no chart BOF record" );
+ }
+ else
+ {
+ SAL_INFO("sc.filter", "XclImpChartObj::ReadChartSubStream - missing chart substream");
+ return;
+ }
+ }
+
+ // read chart, even if BOF record contains wrong substream identifier
+ mxChart = std::make_shared<XclImpChart>( GetRoot(), mbOwnTab );
+ mxChart->ReadChartSubStream( rStrm );
+ if( mbOwnTab )
+ FinalizeTabChart();
+}
+
+void XclImpChartObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadMacro3( rStrm, nMacroSize );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadMacro4( rStrm, nMacroSize );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ // read OBJ record and the following chart substream
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 18 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadChartSubStream( rStrm );
+ // set frame format from OBJ record, it is used if chart itself is transparent
+ if( mxChart )
+ mxChart->UpdateObjFrame( maLineData, maFillData );
+}
+
+void XclImpChartObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 /*nSubRecSize*/ )
+{
+ // read the following chart substream
+ if( nSubRecId == EXC_ID_OBJEND )
+ {
+ // enable CONTINUE handling for the entire chart substream
+ rStrm.ResetRecord( true );
+ ReadChartSubStream( rStrm );
+ /* disable CONTINUE handling again to be able to read
+ following CONTINUE records as MSODRAWING records. */
+ rStrm.ResetRecord( false );
+ }
+}
+
+std::size_t XclImpChartObj::DoGetProgressSize() const
+{
+ return mxChart ? mxChart->GetProgressSize() : 1;
+}
+
+SdrObjectUniquePtr XclImpChartObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj;
+ SfxObjectShell* pDocShell = GetDocShell();
+ if( rDffConv.SupportsOleObjects() && SvtModuleOptions().IsChart() && pDocShell && mxChart && !mxChart->IsPivotChart() )
+ {
+ // create embedded chart object
+ OUString aEmbObjName;
+ OUString sBaseURL(GetRoot().GetMedium().GetBaseURL());
+ Reference< XEmbeddedObject > xEmbObj = pDocShell->GetEmbeddedObjectContainer().
+ CreateEmbeddedObject( SvGlobalName( SO3_SCH_CLASSID ).GetByteSequence(), aEmbObjName, &sBaseURL );
+
+ if (!xEmbObj)
+ return xSdrObj;
+
+ /* Set the size to the embedded object, this prevents that font sizes
+ of text objects are changed in the chart when the object is
+ inserted into the draw page. */
+ sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT;
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xEmbObj->getMapUnit( nAspect ) );
+ Size aSize( OutputDevice::LogicToLogic( rAnchorRect.GetSize(), MapMode( MapUnit::Map100thMM ), MapMode( aUnit ) ) );
+ css::awt::Size aAwtSize( aSize.Width(), aSize.Height() );
+ xEmbObj->setVisualAreaSize( nAspect, aAwtSize );
+
+ // #i121334# This call will change the chart's default background fill from white to transparent.
+ // Add here again if this is wanted (see task description for details)
+ // ChartHelper::AdaptDefaultsForChart( xEmbObj );
+
+ // create the container OLE object
+ xSdrObj.reset(
+ new SdrOle2Obj(
+ *GetDoc().GetDrawLayer(),
+ svt::EmbeddedObjectRef(xEmbObj, nAspect),
+ aEmbObjName,
+ rAnchorRect));
+ }
+
+ return xSdrObj;
+}
+
+void XclImpChartObj::DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ const SdrOle2Obj* pSdrOleObj = dynamic_cast< const SdrOle2Obj* >( &rSdrObj );
+ if( !(mxChart && pSdrOleObj) )
+ return;
+
+ const Reference< XEmbeddedObject >& xEmbObj = pSdrOleObj->GetObjRef();
+ if( xEmbObj.is() && ::svt::EmbeddedObjectRef::TryRunningState( xEmbObj ) ) try
+ {
+ Reference< XEmbedPersist > xPersist( xEmbObj, UNO_QUERY_THROW );
+ Reference< XModel > xModel( xEmbObj->getComponent(), UNO_QUERY_THROW );
+ mxChart->Convert( xModel, rDffConv, xPersist->getEntryName(), rSdrObj.GetLogicRect() );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+void XclImpChartObj::FinalizeTabChart()
+{
+ /* #i44077# Calculate and store DFF anchor for sheet charts.
+ Needed to get used area if this chart is inserted as OLE object. */
+ OSL_ENSURE( mbOwnTab, "XclImpChartObj::FinalizeTabChart - not allowed for embedded chart objects" );
+
+ // set uninitialized page to landscape
+ if( !GetPageSettings().GetPageData().mbValid )
+ GetPageSettings().SetPaperSize( EXC_PAPERSIZE_DEFAULT, false );
+
+ // calculate size of the chart object
+ const XclPageData& rPageData = GetPageSettings().GetPageData();
+ Size aPaperSize = rPageData.GetScPaperSize();
+
+ tools::Long nWidth = XclTools::GetHmmFromTwips( aPaperSize.Width() );
+ tools::Long nHeight = XclTools::GetHmmFromTwips( aPaperSize.Height() );
+
+ // subtract page margins, give some more extra space
+ nWidth -= o3tl::saturating_add(XclTools::GetHmmFromInch(rPageData.mfLeftMargin + rPageData.mfRightMargin), static_cast<sal_Int32>(2000));
+ nHeight -= o3tl::saturating_add(XclTools::GetHmmFromInch(rPageData.mfTopMargin + rPageData.mfBottomMargin), static_cast<sal_Int32>(1000));
+
+ // print column/row headers?
+ if( rPageData.mbPrintHeadings )
+ {
+ nWidth -= 2000;
+ nHeight -= 1000;
+ }
+
+ // create the object anchor
+ XclObjAnchor aAnchor;
+ aAnchor.SetRect( GetRoot(), GetCurrScTab(), tools::Rectangle( 1000, 500, nWidth, nHeight ), MapUnit::Map100thMM );
+ SetAnchor( aAnchor );
+}
+
+XclImpNoteObj::XclImpNoteObj( const XclImpRoot& rRoot ) :
+ XclImpTextObj( rRoot ),
+ maScPos( ScAddress::INITIALIZE_INVALID ),
+ mnNoteFlags( 0 )
+{
+ SetSimpleMacro( false );
+ // caption object will be created manually
+ SetInsertSdrObj( false );
+}
+
+void XclImpNoteObj::SetNoteData( const ScAddress& rScPos, sal_uInt16 nNoteFlags )
+{
+ maScPos = rScPos;
+ mnNoteFlags = nNoteFlags;
+}
+
+void XclImpNoteObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ // create formatted text
+ XclImpTextObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+ OutlinerParaObject* pOutlinerObj = rSdrObj.GetOutlinerParaObject();
+ if( maScPos.IsValid() && pOutlinerObj )
+ {
+ // create cell note with all data from drawing object
+ ScNoteUtil::CreateNoteFromObjectData(
+ GetDoc(), maScPos,
+ rSdrObj.GetMergedItemSet().CloneAsValue(), // new object on heap expected
+ *pOutlinerObj,
+ rSdrObj.GetLogicRect(),
+ ::get_flag( mnNoteFlags, EXC_NOTE_VISIBLE ) );
+ }
+}
+
+XclImpControlHelper::XclImpControlHelper( const XclImpRoot& rRoot, XclCtrlBindMode eBindMode ) :
+ mrRoot( rRoot ),
+ meBindMode( eBindMode )
+{
+}
+
+XclImpControlHelper::~XclImpControlHelper()
+{
+}
+
+SdrObjectUniquePtr XclImpControlHelper::CreateSdrObjectFromShape(
+ const Reference< XShape >& rxShape, const tools::Rectangle& rAnchorRect ) const
+{
+ mxShape = rxShape;
+ SdrObjectUniquePtr xSdrObj( SdrObject::getSdrObjectFromXShape( rxShape ) );
+ if( xSdrObj )
+ {
+ xSdrObj->NbcSetSnapRect( rAnchorRect );
+ // #i30543# insert into control layer
+ xSdrObj->NbcSetLayer( SC_LAYER_CONTROLS );
+ }
+ return xSdrObj;
+}
+
+void XclImpControlHelper::ApplySheetLinkProps() const
+{
+
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ // sheet links
+ SfxObjectShell* pDocShell = mrRoot.GetDocShell();
+ if(!pDocShell)
+ return;
+
+ Reference< XMultiServiceFactory > xFactory( pDocShell->GetModel(), UNO_QUERY );
+ if( !xFactory.is() )
+ return;
+
+ // cell link
+ if( mxCellLink ) try
+ {
+ Reference< XBindableValue > xBindable( xCtrlModel, UNO_QUERY_THROW );
+
+ // create argument sequence for createInstanceWithArguments()
+ CellAddress aApiAddress;
+ ScUnoConversion::FillApiAddress( aApiAddress, *mxCellLink );
+
+ NamedValue aValue;
+ aValue.Name = SC_UNONAME_BOUNDCELL;
+ aValue.Value <<= aApiAddress;
+
+ Sequence< Any > aArgs{ Any(aValue) };
+
+ // create the CellValueBinding instance and set at the control model
+ OUString aServiceName;
+ switch( meBindMode )
+ {
+ case EXC_CTRL_BINDCONTENT: aServiceName = SC_SERVICENAME_VALBIND; break;
+ case EXC_CTRL_BINDPOSITION: aServiceName = SC_SERVICENAME_LISTCELLBIND; break;
+ }
+ Reference< XValueBinding > xBinding(
+ xFactory->createInstanceWithArguments( aServiceName, aArgs ), UNO_QUERY_THROW );
+ xBindable->setValueBinding( xBinding );
+ }
+ catch( const Exception& )
+ {
+ }
+
+ // source range
+ if( !mxSrcRange )
+ return;
+
+ try
+ {
+ Reference< XListEntrySink > xEntrySink( xCtrlModel, UNO_QUERY_THROW );
+
+ // create argument sequence for createInstanceWithArguments()
+ CellRangeAddress aApiRange;
+ ScUnoConversion::FillApiRange( aApiRange, *mxSrcRange );
+
+ NamedValue aValue;
+ aValue.Name = SC_UNONAME_CELLRANGE;
+ aValue.Value <<= aApiRange;
+
+ Sequence< Any > aArgs{ Any(aValue) };
+
+ // create the EntrySource instance and set at the control model
+ Reference< XListEntrySource > xEntrySource( xFactory->createInstanceWithArguments(
+ SC_SERVICENAME_LISTSOURCE, aArgs ), UNO_QUERY_THROW );
+ xEntrySink->setListEntrySource( xEntrySource );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+void XclImpControlHelper::ProcessControl( const XclImpDrawObjBase& rDrawObj ) const
+{
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( mxShape );
+ if( !xCtrlModel.is() )
+ return;
+
+ ApplySheetLinkProps();
+
+ ScfPropertySet aPropSet( xCtrlModel );
+
+ // #i51348# set object name at control model
+ aPropSet.SetStringProperty( "Name", rDrawObj.GetObjName() );
+
+ // control visible and printable?
+ aPropSet.SetBoolProperty( "EnableVisible", rDrawObj.IsVisible() );
+ aPropSet.SetBoolProperty( "Printable", rDrawObj.IsPrintable() );
+
+ // virtual call for type specific processing
+ DoProcessControl( aPropSet );
+}
+
+void XclImpControlHelper::ReadCellLinkFormula( XclImpStream& rStrm, bool bWithBoundSize )
+{
+ ScRangeList aScRanges;
+ ReadRangeList( aScRanges, rStrm, bWithBoundSize );
+ // Use first cell of first range
+ if ( !aScRanges.empty() )
+ {
+ const ScRange & rScRange = aScRanges.front();
+ mxCellLink = std::make_shared<ScAddress>( rScRange.aStart );
+ }
+}
+
+void XclImpControlHelper::ReadSourceRangeFormula( XclImpStream& rStrm, bool bWithBoundSize )
+{
+ ScRangeList aScRanges;
+ ReadRangeList( aScRanges, rStrm, bWithBoundSize );
+ // Use first range
+ if ( !aScRanges.empty() )
+ {
+ const ScRange & rScRange = aScRanges.front();
+ mxSrcRange = std::make_shared<ScRange>( rScRange );
+ }
+}
+
+void XclImpControlHelper::DoProcessControl( ScfPropertySet& ) const
+{
+}
+
+void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm )
+{
+ XclTokenArray aXclTokArr;
+ sal_uInt16 nSize = XclTokenArray::ReadSize(rStrm);
+ rStrm.Ignore( 4 );
+ aXclTokArr.ReadArray(nSize, rStrm);
+ mrRoot.GetFormulaCompiler().CreateRangeList( rScRanges, EXC_FMLATYPE_CONTROL, aXclTokArr, rStrm );
+}
+
+void XclImpControlHelper::ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm, bool bWithBoundSize )
+{
+ if( bWithBoundSize )
+ {
+ sal_uInt16 nSize;
+ nSize = rStrm.ReaduInt16();
+ if( nSize > 0 )
+ {
+ rStrm.PushPosition();
+ ReadRangeList( rScRanges, rStrm );
+ rStrm.PopPosition();
+ rStrm.Ignore( nSize );
+ }
+ }
+ else
+ {
+ ReadRangeList( rScRanges, rStrm );
+ }
+}
+
+XclImpTbxObjBase::XclImpTbxObjBase( const XclImpRoot& rRoot ) :
+ XclImpTextObj( rRoot ),
+ XclImpControlHelper( rRoot, EXC_CTRL_BINDPOSITION )
+{
+ SetSimpleMacro( false );
+ SetCustomDffObj( true );
+}
+
+namespace {
+
+void lclExtractColor( sal_uInt8& rnColorIdx, const DffPropSet& rDffPropSet, sal_uInt32 nPropId )
+{
+ if( rDffPropSet.IsProperty( nPropId ) )
+ {
+ sal_uInt32 nColor = rDffPropSet.GetPropertyValue( nPropId, 0 );
+ if( (nColor & 0xFF000000) == 0x08000000 )
+ rnColorIdx = ::extract_value< sal_uInt8 >( nColor, 0, 8 );
+ }
+}
+
+} // namespace
+
+void XclImpTbxObjBase::SetDffProperties( const DffPropSet& rDffPropSet )
+{
+ maFillData.mnPattern = rDffPropSet.GetPropertyBool( DFF_Prop_fFilled ) ? EXC_PATT_SOLID : EXC_PATT_NONE;
+ lclExtractColor( maFillData.mnBackColorIdx, rDffPropSet, DFF_Prop_fillBackColor );
+ lclExtractColor( maFillData.mnPattColorIdx, rDffPropSet, DFF_Prop_fillColor );
+ ::set_flag( maFillData.mnAuto, EXC_OBJ_LINE_AUTO, false );
+
+ maLineData.mnStyle = rDffPropSet.GetPropertyBool( DFF_Prop_fLine ) ? EXC_OBJ_LINE_SOLID : EXC_OBJ_LINE_NONE;
+ lclExtractColor( maLineData.mnColorIdx, rDffPropSet, DFF_Prop_lineColor );
+ ::set_flag( maLineData.mnAuto, EXC_OBJ_FILL_AUTO, false );
+}
+
+bool XclImpTbxObjBase::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor ) const
+{
+ return XclControlHelper::FillMacroDescriptor( rDescriptor, DoGetEventType(), GetMacroName(), GetDocShell() );
+}
+
+void XclImpTbxObjBase::ConvertFont( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ const XclFormatRunVec& rFormatRuns = maTextData.mxString->GetFormats();
+ if( rFormatRuns.empty() )
+ GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet );
+ else
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, rFormatRuns.front().mnFontIdx );
+ }
+}
+
+void XclImpTbxObjBase::ConvertLabel( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ OUString aLabel = maTextData.mxString->GetText();
+ if( maTextData.maData.mnShortcut > 0 )
+ {
+ sal_Int32 nPos = aLabel.indexOf( static_cast< sal_Unicode >( maTextData.maData.mnShortcut ) );
+ if( nPos != -1 )
+ aLabel = aLabel.replaceAt( nPos, 0, u"~" );
+ }
+ rPropSet.SetStringProperty( "Label", aLabel );
+
+ //Excel Alt text <==> Aoo description
+ //For TBX control, if user does not operate alt text, alt text will be set label text as default value in Excel.
+ //In this case, DFF_Prop_wzDescription will not be set in excel file.
+ //So In the end of SvxMSDffManager::ImportShape, description will not be set. But actually in excel,
+ //the alt text is the label value. So here set description as label text first which is called before ImportShape.
+ Reference< css::beans::XPropertySet > xPropset( mxShape, UNO_QUERY );
+ try{
+ if(xPropset.is())
+ xPropset->setPropertyValue( "Description", Any(aLabel) );
+ }catch( ... )
+ {
+ SAL_WARN("sc.filter", "Can't set a default text for TBX Control ");
+ }
+ }
+ ConvertFont( rPropSet );
+}
+
+SdrObjectUniquePtr XclImpTbxObjBase::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ SdrObjectUniquePtr xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) );
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+void XclImpTbxObjBase::DoPreProcessSdrObj( XclImpDffConverter& /*rDffConv*/, SdrObject& /*rSdrObj*/ ) const
+{
+ // do not call DoPreProcessSdrObj() from base class (to skip text processing)
+ ProcessControl( *this );
+}
+
+XclImpButtonObj::XclImpButtonObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ /* Horizontal text alignment. For unknown reason, the property type is a
+ simple sal_Int16 and not a com.sun.star.style.HorizontalAlignment. */
+ sal_Int16 nHorAlign = 1;
+ switch( maTextData.maData.GetHorAlign() )
+ {
+ case EXC_OBJ_HOR_LEFT: nHorAlign = 0; break;
+ case EXC_OBJ_HOR_CENTER: nHorAlign = 1; break;
+ case EXC_OBJ_HOR_RIGHT: nHorAlign = 2; break;
+ }
+ rPropSet.SetProperty( "Align", nHorAlign );
+
+ // vertical text alignment
+ namespace csss = ::com::sun::star::style;
+ csss::VerticalAlignment eVerAlign = csss::VerticalAlignment_MIDDLE;
+ switch( maTextData.maData.GetVerAlign() )
+ {
+ case EXC_OBJ_VER_TOP: eVerAlign = csss::VerticalAlignment_TOP; break;
+ case EXC_OBJ_VER_CENTER: eVerAlign = csss::VerticalAlignment_MIDDLE; break;
+ case EXC_OBJ_VER_BOTTOM: eVerAlign = csss::VerticalAlignment_BOTTOM; break;
+ }
+ rPropSet.SetProperty( "VerticalAlign", eVerAlign );
+
+ // always wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", true );
+
+ // default button
+ bool bDefButton = ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_DEFAULT );
+ rPropSet.SetBoolProperty( "DefaultButton", bDefButton );
+
+ // button type (flags cannot be combined in OOo)
+ namespace cssa = ::com::sun::star::awt;
+ cssa::PushButtonType eButtonType = cssa::PushButtonType_STANDARD;
+ if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CLOSE ) )
+ eButtonType = cssa::PushButtonType_OK;
+ else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_CANCEL ) )
+ eButtonType = cssa::PushButtonType_CANCEL;
+ else if( ::get_flag( maTextData.maData.mnButtonFlags, EXC_OBJ_BUTTON_HELP ) )
+ eButtonType = cssa::PushButtonType_HELP;
+ // property type is short, not enum
+ rPropSet.SetProperty( "PushButtonType", sal_Int16( eButtonType ) );
+}
+
+OUString XclImpButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.CommandButton";
+}
+
+XclTbxEventType XclImpButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+XclImpCheckBoxObj::XclImpCheckBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnState( EXC_OBJ_CHECKBOX_UNCHECKED ),
+ mnCheckBoxFlags( 0 )
+{
+}
+
+void XclImpCheckBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 20 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnState = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+}
+
+void XclImpCheckBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJCBLS:
+ // do not read EXC_ID_OBJCBLSDATA, not written by OOo Excel export
+ mnState = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+ break;
+ case EXC_ID_OBJCBLSFMLA:
+ ReadCellLinkFormula( rStrm, false );
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpCheckBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ // state
+ bool bSupportsTristate = GetObjType() == EXC_OBJTYPE_CHECKBOX;
+ sal_Int16 nApiState = 0;
+ switch( mnState )
+ {
+ case EXC_OBJ_CHECKBOX_UNCHECKED: nApiState = 0; break;
+ case EXC_OBJ_CHECKBOX_CHECKED: nApiState = 1; break;
+ case EXC_OBJ_CHECKBOX_TRISTATE: nApiState = bSupportsTristate ? 2 : 1; break;
+ }
+ if( bSupportsTristate )
+ rPropSet.SetBoolProperty( "TriState", nApiState == 2 );
+ rPropSet.SetProperty( "DefaultState", nApiState );
+
+ // box style
+ namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect;
+ sal_Int16 nEffect = ::get_flagvalue( mnCheckBoxFlags, EXC_OBJ_CHECKBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D );
+ rPropSet.SetProperty( "VisualEffect", nEffect );
+
+ // do not wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", false );
+
+ // #i40279# always centered vertically
+ namespace csss = ::com::sun::star::style;
+ rPropSet.SetProperty( "VerticalAlign", csss::VerticalAlignment_MIDDLE );
+
+ // background color
+ if( maFillData.IsFilled() )
+ {
+ sal_Int32 nColor = static_cast< sal_Int32 >( GetSolidFillColor( maFillData ) );
+ rPropSet.SetProperty( "BackgroundColor", nColor );
+ }
+}
+
+OUString XclImpCheckBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.CheckBox";
+}
+
+XclTbxEventType XclImpCheckBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+XclImpOptionButtonObj::XclImpOptionButtonObj( const XclImpRoot& rRoot ) :
+ XclImpCheckBoxObj( rRoot ),
+ mnNextInGroup( 0 ),
+ mnFirstInGroup( 1 )
+{
+}
+
+void XclImpOptionButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 32 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnState = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnCheckBoxFlags = rStrm.ReaduInt16();
+ mnNextInGroup = rStrm.ReaduInt16();
+ mnFirstInGroup = rStrm.ReaduInt16();
+}
+
+void XclImpOptionButtonObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJRBODATA:
+ mnNextInGroup = rStrm.ReaduInt16();
+ mnFirstInGroup = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpCheckBoxObj::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpOptionButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ XclImpCheckBoxObj::DoProcessControl( rPropSet );
+ // TODO: grouping
+ XclImpOptionButtonObj* pTbxObj = dynamic_cast< XclImpOptionButtonObj* >( GetObjectManager().GetSheetDrawing( GetTab() ).FindDrawObj( mnNextInGroup ).get() );
+ if ( pTbxObj && pTbxObj->mnFirstInGroup )
+ {
+ // Group has terminated
+ // traverse each RadioButton in group and
+ // a) apply the groupname
+ // b) propagate the linked cell from the lead radiobutton
+ // c) apply the correct Ref value
+ XclImpOptionButtonObj* pLeader = pTbxObj;
+
+ sal_Int32 nRefVal = 1;
+ do
+ {
+
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( pTbxObj->mxShape );
+ if ( xCtrlModel.is() )
+ {
+ ScfPropertySet aProps( xCtrlModel );
+ OUString sGroupName = OUString::number( pLeader->GetDffShapeId() );
+
+ aProps.SetStringProperty( "GroupName", sGroupName );
+ aProps.SetStringProperty( "RefValue", OUString::number( nRefVal++ ) );
+ if ( pLeader->HasCellLink() && !pTbxObj->HasCellLink() )
+ {
+ // propagate cell link info
+ pTbxObj->mxCellLink = std::make_shared<ScAddress>( *pLeader->mxCellLink );
+ pTbxObj->ApplySheetLinkProps();
+ }
+ pTbxObj = dynamic_cast< XclImpOptionButtonObj* >( GetObjectManager().GetSheetDrawing( GetTab() ).FindDrawObj( pTbxObj->mnNextInGroup ).get() );
+ }
+ else
+ pTbxObj = nullptr;
+ } while ( pTbxObj && ( pTbxObj->mnFirstInGroup != 1 ) );
+ }
+ else
+ {
+ // not the leader? try and find it
+ }
+}
+
+OUString XclImpOptionButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.RadioButton";
+}
+
+XclTbxEventType XclImpOptionButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_ACTION;
+}
+
+XclImpLabelObj::XclImpLabelObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpLabelObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+
+ // text alignment (always top/left aligned)
+ rPropSet.SetProperty( "Align", sal_Int16( 0 ) );
+ namespace csss = ::com::sun::star::style;
+ rPropSet.SetProperty( "VerticalAlign", csss::VerticalAlignment_TOP );
+
+ // always wrap text automatically
+ rPropSet.SetBoolProperty( "MultiLine", true );
+}
+
+OUString XclImpLabelObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.FixedText";
+}
+
+XclTbxEventType XclImpLabelObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpGroupBoxObj::XclImpGroupBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnGroupBoxFlags( 0 )
+{
+}
+
+void XclImpGroupBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 26 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16( );
+ mnGroupBoxFlags = rStrm.ReaduInt16();
+}
+
+void XclImpGroupBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJGBODATA:
+ maTextData.maData.mnShortcut = rStrm.ReaduInt16();
+ maTextData.maData.mnShortcutEA = rStrm.ReaduInt16();
+ mnGroupBoxFlags = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpGroupBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+}
+
+OUString XclImpGroupBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.GroupBox";
+}
+
+XclTbxEventType XclImpGroupBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpDialogObj::XclImpDialogObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot )
+{
+}
+
+void XclImpDialogObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // label and text formatting
+ ConvertLabel( rPropSet );
+}
+
+OUString XclImpDialogObj::DoGetServiceName() const
+{
+ // dialog frame faked by a groupbox
+ return "com.sun.star.form.component.GroupBox";
+}
+
+XclTbxEventType XclImpDialogObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_MOUSE;
+}
+
+XclImpEditObj::XclImpEditObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnContentType( EXC_OBJ_EDIT_TEXT ),
+ mnMultiLine( 0 ),
+ mnScrollBar( 0 ),
+ mnListBoxObjId( 0 )
+{
+}
+
+bool XclImpEditObj::IsNumeric() const
+{
+ return (mnContentType == EXC_OBJ_EDIT_INTEGER) || (mnContentType == EXC_OBJ_EDIT_DOUBLE);
+}
+
+void XclImpEditObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 10 );
+ maTextData.maData.mnFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 14 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ mnContentType = rStrm.ReaduInt16();
+ mnMultiLine = rStrm.ReaduInt16();
+ mnScrollBar = rStrm.ReaduInt16();
+ mnListBoxObjId = rStrm.ReaduInt16();
+}
+
+void XclImpEditObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJEDODATA:
+ mnContentType = rStrm.ReaduInt16();
+ mnMultiLine = rStrm.ReaduInt16();
+ mnScrollBar = rStrm.ReaduInt16();
+ mnListBoxObjId = rStrm.ReaduInt16();
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpEditObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ if( maTextData.mxString )
+ {
+ OUString aText = maTextData.mxString->GetText();
+ if( IsNumeric() )
+ {
+ // TODO: OUString::toDouble() does not handle local decimal separator
+ rPropSet.SetProperty( "DefaultValue", aText.toDouble() );
+ rPropSet.SetBoolProperty( "Spin", mnScrollBar != 0 );
+ }
+ else
+ {
+ rPropSet.SetProperty( "DefaultText", aText );
+ rPropSet.SetBoolProperty( "MultiLine", mnMultiLine != 0 );
+ rPropSet.SetBoolProperty( "VScroll", mnScrollBar != 0 );
+ }
+ }
+ ConvertFont( rPropSet );
+}
+
+OUString XclImpEditObj::DoGetServiceName() const
+{
+ return IsNumeric() ?
+ OUString( "com.sun.star.form.component.NumericField" ) :
+ OUString( "com.sun.star.form.component.TextField" );
+}
+
+XclTbxEventType XclImpEditObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_TEXT;
+}
+
+XclImpTbxObjScrollableBase::XclImpTbxObjScrollableBase( const XclImpRoot& rRoot ) :
+ XclImpTbxObjBase( rRoot ),
+ mnValue( 0 ),
+ mnMin( 0 ),
+ mnMax( 100 ),
+ mnStep( 1 ),
+ mnPageStep( 10 ),
+ mnOrient( 0 ),
+ mnThumbWidth( 1 ),
+ mnScrollFlags( 0 )
+{
+}
+
+void XclImpTbxObjScrollableBase::ReadSbs( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 4 );
+ mnValue = rStrm.ReaduInt16();
+ mnMin = rStrm.ReaduInt16();
+ mnMax = rStrm.ReaduInt16();
+ mnStep = rStrm.ReaduInt16();
+ mnPageStep = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ mnThumbWidth = rStrm.ReaduInt16();
+ mnScrollFlags = rStrm.ReaduInt16();
+}
+
+void XclImpTbxObjScrollableBase::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJSBS:
+ ReadSbs( rStrm );
+ break;
+ case EXC_ID_OBJSBSFMLA:
+ ReadCellLinkFormula( rStrm, false );
+ break;
+ default:
+ XclImpTbxObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+XclImpSpinButtonObj::XclImpSpinButtonObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot )
+{
+}
+
+void XclImpSpinButtonObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+}
+
+void XclImpSpinButtonObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#)
+ rPropSet.SetProperty( "Border", css::awt::VisualEffect::NONE );
+ rPropSet.SetProperty< sal_Int32 >( "DefaultSpinValue", mnValue );
+ rPropSet.SetProperty< sal_Int32 >( "SpinValueMin", mnMin );
+ rPropSet.SetProperty< sal_Int32 >( "SpinValueMax", mnMax );
+ rPropSet.SetProperty< sal_Int32 >( "SpinIncrement", mnStep );
+
+ // Excel spin buttons always vertical
+ rPropSet.SetProperty( "Orientation", css::awt::ScrollBarOrientation::VERTICAL );
+}
+
+OUString XclImpSpinButtonObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.SpinButton";
+}
+
+XclTbxEventType XclImpSpinButtonObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_VALUE;
+}
+
+XclImpScrollBarObj::XclImpScrollBarObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot )
+{
+}
+
+void XclImpScrollBarObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+}
+
+void XclImpScrollBarObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // Calc's "Border" property is not the 3D/flat style effect in Excel (#i34712#)
+ rPropSet.SetProperty( "Border", css::awt::VisualEffect::NONE );
+ rPropSet.SetProperty< sal_Int32 >( "DefaultScrollValue", mnValue );
+ rPropSet.SetProperty< sal_Int32 >( "ScrollValueMin", mnMin );
+ rPropSet.SetProperty< sal_Int32 >( "ScrollValueMax", mnMax );
+ rPropSet.SetProperty< sal_Int32 >( "LineIncrement", mnStep );
+ rPropSet.SetProperty< sal_Int32 >( "BlockIncrement", mnPageStep );
+ rPropSet.SetProperty( "VisibleSize", ::std::min< sal_Int32 >( mnPageStep, 1 ) );
+
+ namespace AwtScrollOrient = ::com::sun::star::awt::ScrollBarOrientation;
+ sal_Int32 nApiOrient = ::get_flagvalue( mnOrient, EXC_OBJ_SCROLLBAR_HOR, AwtScrollOrient::HORIZONTAL, AwtScrollOrient::VERTICAL );
+ rPropSet.SetProperty( "Orientation", nApiOrient );
+}
+
+OUString XclImpScrollBarObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.ScrollBar";
+}
+
+XclTbxEventType XclImpScrollBarObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_VALUE;
+}
+
+XclImpTbxObjListBase::XclImpTbxObjListBase( const XclImpRoot& rRoot ) :
+ XclImpTbxObjScrollableBase( rRoot ),
+ mnEntryCount( 0 ),
+ mnSelEntry( 0 ),
+ mnListFlags( 0 ),
+ mnEditObjId( 0 ),
+ mbHasDefFontIdx( false )
+{
+}
+
+void XclImpTbxObjListBase::ReadLbsData( XclImpStream& rStrm )
+{
+ ReadSourceRangeFormula( rStrm, true );
+ mnEntryCount = rStrm.ReaduInt16();
+ mnSelEntry = rStrm.ReaduInt16();
+ mnListFlags = rStrm.ReaduInt16();
+ mnEditObjId = rStrm.ReaduInt16();
+}
+
+void XclImpTbxObjListBase::SetBoxFormatting( ScfPropertySet& rPropSet ) const
+{
+ // border style
+ namespace AwtVisualEffect = ::com::sun::star::awt::VisualEffect;
+ sal_Int16 nApiBorder = ::get_flagvalue( mnListFlags, EXC_OBJ_LISTBOX_FLAT, AwtVisualEffect::FLAT, AwtVisualEffect::LOOK3D );
+ rPropSet.SetProperty( "Border", nApiBorder );
+
+ // font formatting
+ if( mbHasDefFontIdx )
+ GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL, maTextData.maData.mnDefFontIdx );
+ else
+ GetFontBuffer().WriteDefaultCtrlFontProperties( rPropSet );
+}
+
+XclImpListBoxObj::XclImpListBoxObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjListBase( rRoot )
+{
+}
+
+void XclImpListBoxObj::ReadFullLbsData( XclImpStream& rStrm, std::size_t nRecLeft )
+{
+ std::size_t nRecEnd = rStrm.GetRecPos() + nRecLeft;
+ ReadLbsData( rStrm );
+ OSL_ENSURE( (rStrm.GetRecPos() == nRecEnd) || (rStrm.GetRecPos() + mnEntryCount == nRecEnd),
+ "XclImpListBoxObj::ReadFullLbsData - invalid size of OBJLBSDATA record" );
+ while (rStrm.IsValid())
+ {
+ if (rStrm.GetRecPos() >= nRecEnd)
+ break;
+ maSelection.push_back( rStrm.ReaduInt8() );
+ }
+}
+
+void XclImpListBoxObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ rStrm.Ignore( 18 );
+ maTextData.maData.mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ ReadFullLbsData( rStrm, rStrm.GetRecLeft() );
+ mbHasDefFontIdx = true;
+}
+
+void XclImpListBoxObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJLBSDATA:
+ ReadFullLbsData( rStrm, nSubRecSize );
+ break;
+ default:
+ XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpListBoxObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // listbox formatting
+ SetBoxFormatting( rPropSet );
+
+ // selection type
+ sal_uInt8 nSelType = ::extract_value< sal_uInt8 >( mnListFlags, 4, 2 );
+ bool bMultiSel = nSelType != EXC_OBJ_LISTBOX_SINGLE;
+ rPropSet.SetBoolProperty( "MultiSelection", bMultiSel );
+
+ // selection (do not set, if listbox is linked to a cell)
+ if( HasCellLink() )
+ return;
+
+ ScfInt16Vec aSelVec;
+
+ // multi selection: API expects sequence of list entry indexes
+ if( bMultiSel )
+ {
+ sal_Int16 nIndex = 0;
+ for( const auto& rItem : maSelection )
+ {
+ if( rItem != 0 )
+ aSelVec.push_back( nIndex );
+ ++nIndex;
+ }
+ }
+ // single selection: mnSelEntry is one-based, API expects zero-based
+ else if( mnSelEntry > 0 )
+ aSelVec.push_back( static_cast< sal_Int16 >( mnSelEntry - 1 ) );
+
+ if( !aSelVec.empty() )
+ {
+ Sequence<sal_Int16> aSelSeq(aSelVec.data(), static_cast<sal_Int32>(aSelVec.size()));
+ rPropSet.SetProperty( "DefaultSelection", aSelSeq );
+ }
+}
+
+OUString XclImpListBoxObj::DoGetServiceName() const
+{
+ return "com.sun.star.form.component.ListBox";
+}
+
+XclTbxEventType XclImpListBoxObj::DoGetEventType() const
+{
+ return EXC_TBX_EVENT_CHANGE;
+}
+
+XclImpDropDownObj::XclImpDropDownObj( const XclImpRoot& rRoot ) :
+ XclImpTbxObjListBase( rRoot ),
+ mnLeft( 0 ),
+ mnTop( 0 ),
+ mnRight( 0 ),
+ mnBottom( 0 ),
+ mnDropDownFlags( 0 ),
+ mnLineCount( 0 ),
+ mnMinWidth( 0 )
+{
+}
+
+sal_uInt16 XclImpDropDownObj::GetDropDownType() const
+{
+ return ::extract_value< sal_uInt8 >( mnDropDownFlags, 0, 2 );
+}
+
+void XclImpDropDownObj::ReadFullLbsData( XclImpStream& rStrm )
+{
+ ReadLbsData( rStrm );
+ mnDropDownFlags = rStrm.ReaduInt16();
+ mnLineCount = rStrm.ReaduInt16();
+ mnMinWidth = rStrm.ReaduInt16();
+ maTextData.maData.mnTextLen = rStrm.ReaduInt16();
+ maTextData.ReadByteString( rStrm );
+ // dropdowns of auto-filters have 'simple' style, they don't have a text area
+ if( GetDropDownType() == EXC_OBJ_DROPDOWN_SIMPLE )
+ SetProcessSdrObj( false );
+}
+
+void XclImpDropDownObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 /*nMacroSize*/ )
+{
+ ReadFrameData( rStrm );
+ ReadSbs( rStrm );
+ rStrm.Ignore( 18 );
+ maTextData.maData.mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 14 );
+ mnLeft = rStrm.ReaduInt16();
+ mnTop = rStrm.ReaduInt16();
+ mnRight = rStrm.ReaduInt16();
+ mnBottom = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, rStrm.ReaduInt16() ); // first macro size invalid and unused
+ ReadCellLinkFormula( rStrm, true );
+ ReadFullLbsData( rStrm );
+ mbHasDefFontIdx = true;
+}
+
+void XclImpDropDownObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJLBSDATA:
+ ReadFullLbsData( rStrm );
+ break;
+ default:
+ XclImpTbxObjListBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+void XclImpDropDownObj::DoProcessControl( ScfPropertySet& rPropSet ) const
+{
+ // dropdown listbox formatting
+ SetBoxFormatting( rPropSet );
+ // enable dropdown button
+ rPropSet.SetBoolProperty( "Dropdown", true );
+ // dropdown line count
+ rPropSet.SetProperty( "LineCount", mnLineCount );
+
+ if( GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX )
+ {
+ // text of editable combobox
+ if( maTextData.mxString )
+ rPropSet.SetStringProperty( "DefaultText", maTextData.mxString->GetText() );
+ }
+ else
+ {
+ // selection (do not set, if dropdown is linked to a cell)
+ if( !HasCellLink() && (mnSelEntry > 0) )
+ {
+ Sequence< sal_Int16 > aSelSeq{ o3tl::narrowing<sal_Int16>(mnSelEntry - 1) };
+ rPropSet.SetProperty( "DefaultSelection", aSelSeq );
+ }
+ }
+}
+
+OUString XclImpDropDownObj::DoGetServiceName() const
+{
+ return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ?
+ OUString( "com.sun.star.form.component.ComboBox" ) :
+ OUString( "com.sun.star.form.component.ListBox" );
+}
+
+XclTbxEventType XclImpDropDownObj::DoGetEventType() const
+{
+ return (GetDropDownType() == EXC_OBJ_DROPDOWN_COMBOBOX) ? EXC_TBX_EVENT_TEXT : EXC_TBX_EVENT_CHANGE;
+}
+
+XclImpPictureObj::XclImpPictureObj( const XclImpRoot& rRoot ) :
+ XclImpRectObj( rRoot ),
+ XclImpControlHelper( rRoot, EXC_CTRL_BINDCONTENT ),
+ mnStorageId( 0 ),
+ mnCtlsStrmPos( 0 ),
+ mnCtlsStrmSize( 0 ),
+ mbEmbedded( false ),
+ mbLinked( false ),
+ mbSymbol( false ),
+ mbControl( false ),
+ mbUseCtlsStrm( false )
+{
+ SetAreaObj( true );
+ SetSimpleMacro( true );
+ SetCustomDffObj( true );
+}
+
+OUString XclImpPictureObj::GetOleStorageName() const
+{
+ OUStringBuffer aStrgName;
+ if( (mbEmbedded || mbLinked) && !mbControl && (mnStorageId > 0) )
+ {
+ aStrgName = mbEmbedded ? std::u16string_view(u"" EXC_STORAGE_OLE_EMBEDDED) : std::u16string_view(u"" EXC_STORAGE_OLE_LINKED);
+ static const char spcHexChars[] = "0123456789ABCDEF";
+ for( sal_uInt8 nIndex = 32; nIndex > 0; nIndex -= 4 )
+ aStrgName.append(OUStringChar( spcHexChars[ ::extract_value< sal_uInt8 >( mnStorageId, nIndex - 4, 4 ) ] ));
+ }
+ return aStrgName.makeStringAndClear();
+}
+
+void XclImpPictureObj::DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ ReadMacro3( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+}
+
+void XclImpPictureObj::DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ ReadMacro4( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+}
+
+void XclImpPictureObj::DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize )
+{
+ sal_uInt16 nLinkSize;
+ ReadFrameData( rStrm );
+ rStrm.Ignore( 6 );
+ nLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ ReadFlags3( rStrm );
+ rStrm.Ignore( 4 );
+ ReadName5( rStrm, nNameLen );
+ ReadMacro5( rStrm, nMacroSize );
+ ReadPictFmla( rStrm, nLinkSize );
+
+ if( (rStrm.GetNextRecId() == EXC_ID3_IMGDATA) && rStrm.StartNextRecord() )
+ {
+ // page background is stored as hidden picture with name "__BkgndObj"
+ if ( IsHidden() && (GetObjName() == "__BkgndObj") )
+ GetPageSettings().ReadImgData( rStrm );
+ else
+ maGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+ }
+}
+
+void XclImpPictureObj::DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize )
+{
+ switch( nSubRecId )
+ {
+ case EXC_ID_OBJFLAGS:
+ ReadFlags8( rStrm );
+ break;
+ case EXC_ID_OBJPICTFMLA:
+ ReadPictFmla( rStrm, rStrm.ReaduInt16() );
+ break;
+ default:
+ XclImpDrawObjBase::DoReadObj8SubRec( rStrm, nSubRecId, nSubRecSize );
+ }
+}
+
+SdrObjectUniquePtr XclImpPictureObj::DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const
+{
+ // try to create an OLE object or form control
+ SdrObjectUniquePtr xSdrObj( rDffConv.CreateSdrObject( *this, rAnchorRect ) );
+
+ // insert a graphic replacement for unsupported ole object ( if none already
+ // exists ) Hmm ok, it's possibly that there has been some imported
+ // graphic at a base level but unlikely, normally controls have a valid
+ // preview in the IMGDATA record ( see below )
+ // It might be possible to push such an imported graphic up to this
+ // XclImpPictureObj instance but there are so many layers of indirection I
+ // don't see an easy way. This way at least ensures that we can
+ // avoid a 'blank' shape that can result from a failed control import
+ if ( !xSdrObj && IsOcxControl() && maGraphic.GetType() == GraphicType::NONE )
+ {
+ const_cast< XclImpPictureObj* >( this )->maGraphic =
+ SdrOle2Obj::GetEmptyOLEReplacementGraphic();
+ }
+ // no OLE - create a plain picture from IMGDATA record data
+ if( !xSdrObj && (maGraphic.GetType() != GraphicType::NONE) )
+ {
+ xSdrObj.reset(
+ new SdrGrafObj(
+ *GetDoc().GetDrawLayer(),
+ maGraphic,
+ rAnchorRect));
+ ConvertRectStyle( *xSdrObj );
+ }
+
+ rDffConv.Progress();
+ return xSdrObj;
+}
+
+OUString XclImpPictureObj::GetObjName() const
+{
+ if( IsOcxControl() )
+ {
+ OUString sName( GetObjectManager().GetOleNameOverride( GetTab(), GetObjId() ) );
+ if (!sName.isEmpty())
+ return sName;
+ }
+ return XclImpDrawObjBase::GetObjName();
+}
+
+void XclImpPictureObj::DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const
+{
+ if( IsOcxControl() )
+ {
+ // do not call XclImpRectObj::DoPreProcessSdrObj(), it would trace missing "printable" feature
+ ProcessControl( *this );
+ }
+ else if( mbEmbedded || mbLinked )
+ {
+ // trace missing "printable" feature
+ XclImpRectObj::DoPreProcessSdrObj( rDffConv, rSdrObj );
+
+ SfxObjectShell* pDocShell = GetDocShell();
+ SdrOle2Obj* pOleSdrObj = dynamic_cast< SdrOle2Obj* >( &rSdrObj );
+ if( pOleSdrObj && pDocShell )
+ {
+ comphelper::EmbeddedObjectContainer& rEmbObjCont = pDocShell->GetEmbeddedObjectContainer();
+ Reference< XEmbeddedObject > xEmbObj = pOleSdrObj->GetObjRef();
+ OUString aOldName( pOleSdrObj->GetPersistName() );
+
+ /* The object persistence should be already in the storage, but
+ the object still might not be inserted into the container. */
+ if( rEmbObjCont.HasEmbeddedObject( aOldName ) )
+ {
+ if( !rEmbObjCont.HasEmbeddedObject( xEmbObj ) )
+ // filter code is allowed to call the following method
+ rEmbObjCont.AddEmbeddedObject( xEmbObj, aOldName );
+ }
+ else
+ {
+ /* If the object is still not in container it must be inserted
+ there, the name must be generated in this case. */
+ OUString aNewName;
+ rEmbObjCont.InsertEmbeddedObject( xEmbObj, aNewName );
+ if( aOldName != aNewName )
+ // SetPersistName, not SetName
+ pOleSdrObj->SetPersistName( aNewName );
+ }
+ }
+ }
+}
+
+void XclImpPictureObj::ReadFlags3( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL );
+}
+
+void XclImpPictureObj::ReadFlags8( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ mbSymbol = ::get_flag( nFlags, EXC_OBJ_PIC_SYMBOL );
+ mbControl = ::get_flag( nFlags, EXC_OBJ_PIC_CONTROL );
+ mbUseCtlsStrm = ::get_flag( nFlags, EXC_OBJ_PIC_CTLSSTREAM );
+ OSL_ENSURE( mbControl || !mbUseCtlsStrm, "XclImpPictureObj::ReadFlags8 - CTLS stream for controls only" );
+ SetProcessSdrObj( mbControl || !mbUseCtlsStrm );
+}
+
+void XclImpPictureObj::ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nLinkSize )
+{
+ std::size_t nLinkEnd = rStrm.GetRecPos() + nLinkSize;
+ if( nLinkSize >= 6 )
+ {
+ sal_uInt16 nFmlaSize;
+ nFmlaSize = rStrm.ReaduInt16();
+ OSL_ENSURE( nFmlaSize > 0, "XclImpPictureObj::ReadPictFmla - missing link formula" );
+ // BIFF3/BIFF4 do not support storages, nothing to do here
+ if( (nFmlaSize > 0) && (GetBiff() >= EXC_BIFF5) )
+ {
+ rStrm.Ignore( 4 );
+ sal_uInt8 nToken;
+ nToken = rStrm.ReaduInt8();
+
+ // different processing for linked vs. embedded OLE objects
+ if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_NAMEX, EXC_TOKCLASS_REF ) )
+ {
+ mbLinked = true;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF5:
+ {
+ sal_Int16 nRefIdx;
+ sal_uInt16 nNameIdx;
+ nRefIdx = rStrm.ReadInt16();
+ rStrm.Ignore( 8 );
+ nNameIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 12 );
+ const ExtName* pExtName = GetOldRoot().pExtNameBuff->GetNameByIndex( nRefIdx, nNameIdx );
+ if( pExtName && pExtName->IsOLE() )
+ mnStorageId = pExtName->nStorageId;
+ }
+ break;
+ case EXC_BIFF8:
+ {
+ sal_uInt16 nXti, nExtName;
+ nXti = rStrm.ReaduInt16();
+ nExtName = rStrm.ReaduInt16();
+ const XclImpExtName* pExtName = GetLinkManager().GetExternName( nXti, nExtName );
+ if( pExtName && (pExtName->GetType() == xlExtOLE) )
+ mnStorageId = pExtName->GetStorageId();
+ }
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ }
+ else if( nToken == XclTokenArrayHelper::GetTokenId( EXC_TOKID_TBL, EXC_TOKCLASS_NONE ) )
+ {
+ mbEmbedded = true;
+ OSL_ENSURE( nFmlaSize == 5, "XclImpPictureObj::ReadPictFmla - unexpected formula size" );
+ rStrm.Ignore( nFmlaSize - 1 ); // token ID already read
+ if( nFmlaSize & 1 )
+ rStrm.Ignore( 1 ); // padding byte
+
+ // a class name may follow inside the picture link
+ if( rStrm.GetRecPos() + 2 <= nLinkEnd )
+ {
+ sal_uInt16 nLen = rStrm.ReaduInt16();
+ if( nLen > 0 )
+ maClassName = (GetBiff() == EXC_BIFF8) ? rStrm.ReadUniString( nLen ) : rStrm.ReadRawByteString( nLen );
+ }
+ }
+ // else: ignore other formulas, e.g. pictures linked to cell ranges
+ }
+ }
+
+ // seek behind picture link data
+ rStrm.Seek( nLinkEnd );
+
+ // read additional data for embedded OLE objects following the picture link
+ if( IsOcxControl() )
+ {
+ // #i26521# form controls to be ignored
+ if( maClassName == "Forms.HTML:Hidden.1" )
+ {
+ SetProcessSdrObj( false );
+ return;
+ }
+
+ if( rStrm.GetRecLeft() <= 8 ) return;
+
+ // position and size of control data in 'Ctls' stream
+ mnCtlsStrmPos = static_cast< std::size_t >( rStrm.ReaduInt32() );
+ mnCtlsStrmSize = static_cast< std::size_t >( rStrm.ReaduInt32() );
+
+ if( rStrm.GetRecLeft() <= 8 ) return;
+
+ // additional string (16-bit characters), e.g. for progress bar control
+ sal_uInt32 nAddStrSize;
+ nAddStrSize = rStrm.ReaduInt32();
+ OSL_ENSURE( rStrm.GetRecLeft() >= nAddStrSize + 4, "XclImpPictureObj::ReadPictFmla - missing data" );
+ if( rStrm.GetRecLeft() >= nAddStrSize + 4 )
+ {
+ rStrm.Ignore( nAddStrSize );
+ // cell link and source range
+ ReadCellLinkFormula( rStrm, true );
+ ReadSourceRangeFormula( rStrm, true );
+ }
+ }
+ else if( mbEmbedded && (rStrm.GetRecLeft() >= 4) )
+ {
+ mnStorageId = rStrm.ReaduInt32();
+ }
+}
+
+// DFF stream conversion ======================================================
+
+void XclImpSolverContainer::InsertSdrObjectInfo( SdrObject& rSdrObj, sal_uInt32 nDffShapeId, ShapeFlag nDffFlags )
+{
+ if( nDffShapeId > 0 )
+ {
+ maSdrInfoMap[ nDffShapeId ].Set( &rSdrObj, nDffFlags );
+ maSdrObjMap[ &rSdrObj ] = nDffShapeId;
+ }
+}
+
+void XclImpSolverContainer::RemoveSdrObjectInfo( SdrObject& rSdrObj )
+{
+ // remove info of passed object from the maps
+ XclImpSdrObjMap::iterator aIt = maSdrObjMap.find( &rSdrObj );
+ if( aIt != maSdrObjMap.end() )
+ {
+ maSdrInfoMap.erase( aIt->second );
+ maSdrObjMap.erase( aIt );
+ }
+
+ // remove info of all child objects of a group object
+ if( SdrObjGroup* pGroupObj = dynamic_cast< SdrObjGroup* >( &rSdrObj ) )
+ {
+ if( SdrObjList* pSubList = pGroupObj->GetSubList() )
+ {
+ // iterate flat over the list because this function already works recursively
+ SdrObjListIter aObjIt( pSubList, SdrIterMode::Flat );
+ for( SdrObject* pChildObj = aObjIt.Next(); pChildObj; pChildObj = aObjIt.Next() )
+ RemoveSdrObjectInfo( *pChildObj );
+ }
+ }
+}
+
+void XclImpSolverContainer::UpdateConnectorRules()
+{
+ for (auto const & pRule : aCList)
+ {
+ UpdateConnection( pRule->nShapeA, pRule->pAObj, &pRule->nSpFlagsA );
+ UpdateConnection( pRule->nShapeB, pRule->pBObj, &pRule->nSpFlagsB );
+ UpdateConnection( pRule->nShapeC, pRule->pCObj );
+ }
+}
+
+void XclImpSolverContainer::RemoveConnectorRules()
+{
+ aCList.clear();
+ maSdrInfoMap.clear();
+ maSdrObjMap.clear();
+}
+
+void XclImpSolverContainer::UpdateConnection( sal_uInt32 nDffShapeId, SdrObject*& rpSdrObj, ShapeFlag* pnDffFlags )
+{
+ XclImpSdrInfoMap::const_iterator aIt = maSdrInfoMap.find( nDffShapeId );
+ if( aIt != maSdrInfoMap.end() )
+ {
+ rpSdrObj = aIt->second.mpSdrObj;
+ if( pnDffFlags )
+ *pnDffFlags = aIt->second.mnDffFlags;
+ }
+}
+
+XclImpSimpleDffConverter::XclImpSimpleDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) :
+ SvxMSDffManager( rDffStrm, rRoot.GetBasePath(), 0, nullptr, rRoot.GetDoc().GetDrawLayer(), 1440, COL_DEFAULT, nullptr ),
+ XclImpRoot( rRoot )
+{
+ SetSvxMSDffSettings( SVXMSDFF_SETTINGS_CROP_BITMAPS | SVXMSDFF_SETTINGS_IMPORT_EXCEL );
+}
+
+XclImpSimpleDffConverter::~XclImpSimpleDffConverter()
+{
+}
+
+bool XclImpSimpleDffConverter::GetColorFromPalette( sal_uInt16 nIndex, Color& rColor ) const
+{
+ Color nColor = GetPalette().GetColor( nIndex );
+
+ if( nColor == COL_AUTO )
+ return false;
+
+ rColor = nColor;
+ return true;
+}
+
+XclImpDffConverter::XclImpDffConvData::XclImpDffConvData(
+ XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage ) :
+ mrDrawing( rDrawing ),
+ mrSdrModel( rSdrModel ),
+ mrSdrPage( rSdrPage ),
+ mnLastCtrlIndex( -1 ),
+ mbHasCtrlForm( false )
+{
+}
+
+constexpr OUStringLiteral gaStdFormName( u"Standard" ); /// Standard name of control forms.
+
+XclImpDffConverter::XclImpDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm ) :
+ XclImpSimpleDffConverter( rRoot, rDffStrm ),
+ oox::ole::MSConvertOCXControls( rRoot.GetDocShell()->GetModel() ),
+ mnOleImpFlags( 0 ),
+ mbNotifyMacroEventRead(false)
+{
+ const SvtFilterOptions& rFilterOpt = SvtFilterOptions::Get();
+ if( rFilterOpt.IsMathType2Math() )
+ mnOleImpFlags |= OLE_MATHTYPE_2_STARMATH;
+ if( rFilterOpt.IsWinWord2Writer() )
+ mnOleImpFlags |= OLE_WINWORD_2_STARWRITER;
+ if( rFilterOpt.IsPowerPoint2Impress() )
+ mnOleImpFlags |= OLE_POWERPOINT_2_STARIMPRESS;
+
+ // try to open the 'Ctls' storage stream containing OCX control properties
+ mxCtlsStrm = OpenStream( EXC_STREAM_CTLS );
+
+ // default text margin (convert EMU to drawing layer units)
+ mnDefTextMargin = EXC_OBJ_TEXT_MARGIN;
+ ScaleEmu( mnDefTextMargin );
+}
+
+XclImpDffConverter::~XclImpDffConverter()
+{
+}
+
+OUString XclImpObjectManager::GetOleNameOverride( SCTAB nTab, sal_uInt16 nObjId )
+{
+ OUString sOleName;
+ OUString sCodeName = GetExtDocOptions().GetCodeName( nTab );
+
+ if (mxOleCtrlNameOverride.is() && mxOleCtrlNameOverride->hasByName(sCodeName))
+ {
+ Reference< XIndexContainer > xIdToOleName;
+ mxOleCtrlNameOverride->getByName( sCodeName ) >>= xIdToOleName;
+ xIdToOleName->getByIndex( nObjId ) >>= sOleName;
+ }
+
+ return sOleName;
+}
+
+void XclImpDffConverter::StartProgressBar( std::size_t nProgressSize )
+{
+ mxProgress = std::make_shared<ScfProgressBar>( GetDocShell(), STR_PROGRESS_CALCULATING );
+ mxProgress->AddSegment( nProgressSize );
+ mxProgress->Activate();
+}
+
+void XclImpDffConverter::Progress( std::size_t nDelta )
+{
+ OSL_ENSURE( mxProgress, "XclImpDffConverter::Progress - invalid call, no progress bar" );
+ mxProgress->Progress( nDelta );
+}
+
+void XclImpDffConverter::InitializeDrawing( XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage )
+{
+ XclImpDffConvDataRef xConvData = std::make_shared<XclImpDffConvData>( rDrawing, rSdrModel, rSdrPage );
+ maDataStack.push_back( xConvData );
+ SetModel( &xConvData->mrSdrModel, 1440 );
+}
+
+void XclImpDffConverter::ProcessObject( SdrObjList& rObjList, XclImpDrawObjBase& rDrawObj )
+{
+ if( !rDrawObj.IsProcessSdrObj() )
+ return;
+
+ const XclObjAnchor* pAnchor = rDrawObj.GetAnchor();
+ if(!pAnchor)
+ return;
+
+ tools::Rectangle aAnchorRect = GetConvData().mrDrawing.CalcAnchorRect( *pAnchor, false );
+ if( rDrawObj.IsValidSize( aAnchorRect ) )
+ {
+ // CreateSdrObject() recursively creates embedded child objects
+ SdrObjectUniquePtr xSdrObj( rDrawObj.CreateSdrObject( *this, aAnchorRect, false ) );
+ if( xSdrObj )
+ rDrawObj.PreProcessSdrObject( *this, *xSdrObj );
+ // call InsertSdrObject() also, if SdrObject is missing
+ InsertSdrObject( rObjList, rDrawObj, xSdrObj.release() );
+ }
+}
+
+void XclImpDffConverter::ProcessDrawing( const XclImpDrawObjVector& rDrawObjs )
+{
+ SdrPage& rSdrPage = GetConvData().mrSdrPage;
+ for( const auto& rxDrawObj : rDrawObjs )
+ ProcessObject( rSdrPage, *rxDrawObj );
+}
+
+void XclImpDffConverter::ProcessDrawing( SvStream& rDffStrm )
+{
+ if( rDffStrm.TellEnd() > 0 )
+ {
+ rDffStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ OSL_ENSURE( aHeader.nRecType == DFF_msofbtDgContainer, "XclImpDffConverter::ProcessDrawing - unexpected record" );
+ if( aHeader.nRecType == DFF_msofbtDgContainer )
+ ProcessDgContainer( rDffStrm, aHeader );
+ }
+}
+
+void XclImpDffConverter::FinalizeDrawing()
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::FinalizeDrawing - no drawing manager on stack" );
+ maDataStack.pop_back();
+ // restore previous model at core DFF converter
+ if( !maDataStack.empty() )
+ SetModel( &maDataStack.back()->mrSdrModel, 1440 );
+}
+
+void XclImpDffConverter::NotifyMacroEventRead()
+{
+ if (mbNotifyMacroEventRead)
+ return;
+ comphelper::DocumentInfo::notifyMacroEventRead(mxModel);
+ mbNotifyMacroEventRead = true;
+}
+
+SdrObjectUniquePtr XclImpDffConverter::CreateSdrObject( const XclImpTbxObjBase& rTbxObj, const tools::Rectangle& rAnchorRect )
+{
+ SdrObjectUniquePtr xSdrObj;
+
+ OUString aServiceName = rTbxObj.GetServiceName();
+ if( SupportsOleObjects() && !aServiceName.isEmpty() ) try
+ {
+ // create the form control from scratch
+ Reference< XFormComponent > xFormComp( ScfApiHelper::CreateInstance( GetDocShell(), aServiceName ), UNO_QUERY_THROW );
+ // set controls form, needed in virtual function InsertControl()
+ InitControlForm();
+ // try to insert the control into the form
+ css::awt::Size aDummySize;
+ Reference< XShape > xShape;
+ XclImpDffConvData& rConvData = GetConvData();
+ if( rConvData.mxCtrlForm.is() && InsertControl( xFormComp, aDummySize, &xShape, true ) )
+ {
+ xSdrObj = rTbxObj.CreateSdrObjectFromShape( xShape, rAnchorRect );
+ // try to attach a macro to the control
+ ScriptEventDescriptor aDescriptor;
+ if( (rConvData.mnLastCtrlIndex >= 0) && rTbxObj.FillMacroDescriptor( aDescriptor ) )
+ {
+ NotifyMacroEventRead();
+ Reference< XEventAttacherManager > xEventMgr( rConvData.mxCtrlForm, UNO_QUERY_THROW );
+ xEventMgr->registerScriptEvent( rConvData.mnLastCtrlIndex, aDescriptor );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+
+ return xSdrObj;
+}
+
+SdrObjectUniquePtr XclImpDffConverter::CreateSdrObject( const XclImpPictureObj& rPicObj, const tools::Rectangle& rAnchorRect )
+{
+ SdrObjectUniquePtr xSdrObj;
+
+ if( SupportsOleObjects() )
+ {
+ if( rPicObj.IsOcxControl() )
+ {
+ if( mxCtlsStrm.is() ) try
+ {
+ /* set controls form, needed in virtual function InsertControl()
+ called from ReadOCXExcelKludgeStream() */
+ InitControlForm();
+
+ // read from mxCtlsStrm into xShape, insert the control model into the form
+ Reference< XShape > xShape;
+ if( GetConvData().mxCtrlForm.is() )
+ {
+ Reference< XFormComponent > xFComp;
+ ReadOCXCtlsStream( mxCtlsStrm, xFComp, rPicObj.GetCtlsStreamPos(), rPicObj.GetCtlsStreamSize() );
+ // recreate the method formerly known as ReadOCXExcelKludgeStream()
+ if ( xFComp.is() )
+ {
+ css::awt::Size aSz; // not used in import
+ ScfPropertySet aPropSet( xFComp );
+ aPropSet.SetStringProperty( "Name", rPicObj.GetObjName() );
+ InsertControl( xFComp, aSz,&xShape,true);
+ xSdrObj = rPicObj.CreateSdrObjectFromShape( xShape, rAnchorRect );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+ }
+ else
+ {
+ SfxObjectShell* pDocShell = GetDocShell();
+ tools::SvRef<SotStorage> xSrcStrg = GetRootStorage();
+ OUString aStrgName = rPicObj.GetOleStorageName();
+ if( pDocShell && xSrcStrg.is() && (!aStrgName.isEmpty()) )
+ {
+ // first try to resolve graphic from DFF storage
+ Graphic aGraphic;
+ tools::Rectangle aVisArea;
+ if( !GetBLIP( GetPropertyValue( DFF_Prop_pib, 0 ), aGraphic, &aVisArea ) )
+ {
+ // if not found, use graphic from object (imported from IMGDATA record)
+ aGraphic = rPicObj.GetGraphic();
+ }
+ if( aGraphic.GetType() != GraphicType::NONE )
+ {
+ ErrCode nError = ERRCODE_NONE;
+ namespace cssea = ::com::sun::star::embed::Aspects;
+ sal_Int64 nAspects = rPicObj.IsSymbol() ? cssea::MSOLE_ICON : cssea::MSOLE_CONTENT;
+ xSdrObj.reset(
+ CreateSdrOLEFromStorage(
+ GetConvData().mrSdrModel,
+ aStrgName,
+ xSrcStrg,
+ pDocShell->GetStorage(),
+ aGraphic,
+ rAnchorRect,
+ aVisArea,
+ nullptr,
+ nError,
+ mnOleImpFlags,
+ nAspects,
+ GetRoot().GetMedium().GetBaseURL()));
+ }
+ }
+ }
+ }
+
+ return xSdrObj;
+}
+
+bool XclImpDffConverter::SupportsOleObjects() const
+{
+ return GetConvData().mrDrawing.SupportsOleObjects();
+}
+
+// virtual functions ----------------------------------------------------------
+
+void XclImpDffConverter::ProcessClientAnchor2( SvStream& rDffStrm,
+ DffRecordHeader& rHeader, DffObjData& rObjData )
+{
+ // find the OBJ record data related to the processed shape
+ XclImpDffConvData& rConvData = GetConvData();
+ XclImpDrawObjBase* pDrawObj = rConvData.mrDrawing.FindDrawObj( rObjData.rSpHd ).get();
+ if(!pDrawObj)
+ return;
+
+ OSL_ENSURE( rHeader.nRecType == DFF_msofbtClientAnchor, "XclImpDffConverter::ProcessClientAnchor2 - no client anchor record" );
+ XclObjAnchor aAnchor;
+ rHeader.SeekToContent( rDffStrm );
+ sal_uInt8 nFlags(0);
+ rDffStrm.ReadUChar( nFlags );
+ rDffStrm.SeekRel( 1 ); // flags
+ rDffStrm >> aAnchor; // anchor format equal to BIFF5 OBJ records
+
+ if (!rDffStrm.good())
+ {
+ SAL_WARN("sc.filter", "ProcessClientAnchor2 short read");
+ return;
+ }
+
+ pDrawObj->SetAnchor( aAnchor );
+ rObjData.aChildAnchor = rConvData.mrDrawing.CalcAnchorRect( aAnchor, true );
+ rObjData.bChildAnchor = true;
+ // page anchoring is the best approximation we have if mbMove
+ // is set
+ rObjData.bPageAnchor = ( nFlags & 0x1 );
+}
+
+namespace {
+
+struct XclImpDrawObjClientData : public SvxMSDffClientData
+{
+ const XclImpDrawObjBase* m_pTopLevelObj;
+
+ XclImpDrawObjClientData()
+ : m_pTopLevelObj(nullptr)
+ {
+ }
+ virtual void NotifyFreeObj(SdrObject*) override {}
+};
+
+}
+
+SdrObject* XclImpDffConverter::ProcessObj( SvStream& rDffStrm, DffObjData& rDffObjData,
+ SvxMSDffClientData& rClientData, tools::Rectangle& /*rTextRect*/, SdrObject* pOldSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+
+ /* pOldSdrObj passes a generated SdrObject. This function owns this object
+ and can modify it. The function has either to return it back to caller
+ or to delete it by itself. */
+ SdrObjectUniquePtr xSdrObj( pOldSdrObj );
+
+ // find the OBJ record data related to the processed shape
+ XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd );
+ const tools::Rectangle& rAnchorRect = rDffObjData.aChildAnchor;
+
+ // Do not process the global page group shape
+ bool bGlobalPageGroup( rDffObjData.nSpFlags & ShapeFlag::Patriarch );
+ if( !xDrawObj || !xDrawObj->IsProcessSdrObj() || bGlobalPageGroup )
+ return nullptr; // simply return, xSdrObj will be destroyed
+
+ /* Pass pointer to top-level object back to caller. If the processed
+ object is embedded in a group, the pointer is already set to the
+ top-level parent object. */
+ XclImpDrawObjClientData& rDrawObjClientData = static_cast<XclImpDrawObjClientData&>(rClientData);
+ const bool bIsTopLevel = !rDrawObjClientData.m_pTopLevelObj;
+ if (bIsTopLevel )
+ rDrawObjClientData.m_pTopLevelObj = xDrawObj.get();
+
+ // connectors don't have to be area objects
+ if( dynamic_cast< SdrEdgeObj* >( xSdrObj.get() ) )
+ xDrawObj->SetAreaObj( false );
+
+ /* Check for valid size for all objects. Needed to ignore lots of invisible
+ phantom objects from deleted rows or columns (for performance reasons).
+ #i30816# Include objects embedded in groups.
+ #i58780# Ignore group shapes, size is not initialized. */
+ bool bEmbeddedGroup = !bIsTopLevel && dynamic_cast< SdrObjGroup* >( xSdrObj.get() );
+ if( !bEmbeddedGroup && !xDrawObj->IsValidSize( rAnchorRect ) )
+ return nullptr; // simply return, xSdrObj will be destroyed
+
+ // set shape information from DFF stream
+ OUString aObjName = GetPropertyString( DFF_Prop_wzName, rDffStrm );
+ OUString aHyperlink = ReadHlinkProperty( rDffStrm );
+ bool bVisible = !GetPropertyBool( DFF_Prop_fHidden );
+ bool bAutoMargin = GetPropertyBool( DFF_Prop_AutoTextMargin );
+ xDrawObj->SetDffData( rDffObjData, aObjName, aHyperlink, bVisible, bAutoMargin );
+
+ /* Connect textbox data (string, alignment, text orientation) to object.
+ don't ask for a text-ID, DFF export doesn't set one. */
+ if( XclImpTextObj* pTextObj = dynamic_cast< XclImpTextObj* >( xDrawObj.get() ) )
+ if( const XclImpObjTextData* pTextData = rConvData.mrDrawing.FindTextData( rDffObjData.rSpHd ) )
+ pTextObj->SetTextData( *pTextData );
+
+ // copy line and fill formatting of TBX form controls from DFF properties
+ if( XclImpTbxObjBase* pTbxObj = dynamic_cast< XclImpTbxObjBase* >( xDrawObj.get() ) )
+ pTbxObj->SetDffProperties( *this );
+
+ // try to create a custom SdrObject that overwrites the passed object
+ SdrObjectUniquePtr xNewSdrObj( xDrawObj->CreateSdrObject( *this, rAnchorRect, true ) );
+ if( xNewSdrObj )
+ xSdrObj = std::move( xNewSdrObj );
+
+ // process the SdrObject
+ if( xSdrObj )
+ {
+ // filled without color -> set system window color
+ if( GetPropertyBool( DFF_Prop_fFilled ) && !IsProperty( DFF_Prop_fillColor ) )
+ xSdrObj->SetMergedItem( XFillColorItem( OUString(), GetPalette().GetColor( EXC_COLOR_WINDOWBACK ) ) );
+
+ // additional processing on the SdrObject
+ xDrawObj->PreProcessSdrObject( *this, *xSdrObj );
+
+ /* If the SdrObject will not be inserted into the draw page, delete it
+ here. Happens e.g. for notes: The PreProcessSdrObject() call above
+ has inserted the note into the document, and the SdrObject is not
+ needed anymore. */
+ if( !xDrawObj->IsInsertSdrObj() )
+ xSdrObj.reset();
+ }
+
+ if( xSdrObj )
+ {
+ /* Store the relation between shape ID and SdrObject for connectors.
+ Must be done here (and not in InsertSdrObject() function),
+ otherwise all SdrObjects embedded in groups would be lost. */
+ rConvData.maSolverCont.InsertSdrObjectInfo( *xSdrObj, xDrawObj->GetDffShapeId(), xDrawObj->GetDffFlags() );
+
+ /* If the drawing object is embedded in a group object, call
+ PostProcessSdrObject() here. For top-level objects this will be
+ done automatically in InsertSdrObject() but grouped shapes are
+ inserted into their groups somewhere in the SvxMSDffManager base
+ class without chance of notification. Unfortunately, now this is
+ called before the object is really inserted into its group object,
+ but that should not have any effect for grouped objects. */
+ if( !bIsTopLevel )
+ xDrawObj->PostProcessSdrObject( *this, *xSdrObj );
+ }
+
+ return xSdrObj.release();
+}
+
+SdrObject* XclImpDffConverter::FinalizeObj(DffObjData& rDffObjData, SdrObject* pOldSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+
+ /* pOldSdrObj passes a generated SdrObject. This function owns this object
+ and can modify it. The function has either to return it back to caller
+ or to delete it by itself. */
+ SdrObjectUniquePtr xSdrObj( pOldSdrObj );
+
+ // find the OBJ record data related to the processed shape
+ XclImpDrawObjRef xDrawObj = rConvData.mrDrawing.FindDrawObj( rDffObjData.rSpHd );
+
+ if( xSdrObj && xDrawObj )
+ {
+ // cell anchoring
+ if ( !rDffObjData.bPageAnchor )
+ ScDrawLayer::SetCellAnchoredFromPosition( *xSdrObj, GetDoc(), xDrawObj->GetTab(), false );
+ }
+
+ return xSdrObj.release();
+}
+
+bool XclImpDffConverter::InsertControl( const Reference< XFormComponent >& rxFormComp,
+ const css::awt::Size& /*rSize*/, Reference< XShape >* pxShape,
+ bool /*bFloatingCtrl*/ )
+{
+ if( GetDocShell() ) try
+ {
+ XclImpDffConvData& rConvData = GetConvData();
+ Reference< XIndexContainer > xFormIC( rConvData.mxCtrlForm, UNO_QUERY_THROW );
+ Reference< XControlModel > xCtrlModel( rxFormComp, UNO_QUERY_THROW );
+
+ // create the control shape
+ Reference< XShape > xShape( ScfApiHelper::CreateInstance( GetDocShell(), "com.sun.star.drawing.ControlShape" ), UNO_QUERY_THROW );
+ Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY_THROW );
+
+ // insert the new control into the form
+ sal_Int32 nNewIndex = xFormIC->getCount();
+ xFormIC->insertByIndex( nNewIndex, Any( rxFormComp ) );
+ // on success: store new index of the control for later use (macro events)
+ rConvData.mnLastCtrlIndex = nNewIndex;
+
+ // set control model at control shape and pass back shape to caller
+ xCtrlShape->setControl( xCtrlModel );
+ if( pxShape ) *pxShape = xShape;
+ return true;
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "XclImpDffConverter::InsertControl - cannot create form control" );
+ }
+
+ return false;
+}
+
+// private --------------------------------------------------------------------
+
+XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData()
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" );
+ return *maDataStack.back();
+}
+
+const XclImpDffConverter::XclImpDffConvData& XclImpDffConverter::GetConvData() const
+{
+ OSL_ENSURE( !maDataStack.empty(), "XclImpDffConverter::GetConvData - no drawing manager on stack" );
+ return *maDataStack.back();
+}
+
+OUString XclImpDffConverter::ReadHlinkProperty( SvStream& rDffStrm ) const
+{
+ /* Reads hyperlink data from a complex DFF property. Contents of this
+ property are equal to the HLINK record, import of this record is
+ implemented in class XclImpHyperlink. This function has to create an
+ instance of the XclImpStream class to be able to reuse the
+ functionality of XclImpHyperlink. */
+ OUString aString;
+ sal_uInt32 nBufferSize = GetPropertyValue( DFF_Prop_pihlShape, 0 );
+ if( (0 < nBufferSize) && (nBufferSize <= 0xFFFF) && SeekToContent( DFF_Prop_pihlShape, rDffStrm ) )
+ {
+ // create a faked BIFF record that can be read by XclImpStream class
+ SvMemoryStream aMemStream;
+ aMemStream.WriteUInt16( 0 ).WriteUInt16( nBufferSize );
+
+ // copy from DFF stream to memory stream
+ ::std::vector< sal_uInt8 > aBuffer( nBufferSize );
+ sal_uInt8* pnData = aBuffer.data();
+ if (rDffStrm.ReadBytes(pnData, nBufferSize) == nBufferSize)
+ {
+ aMemStream.WriteBytes(pnData, nBufferSize);
+
+ // create BIFF import stream to be able to use XclImpHyperlink class
+ XclImpStream aXclStrm( aMemStream, GetRoot() );
+ if( aXclStrm.StartNextRecord() )
+ aString = XclImpHyperlink::ReadEmbeddedData( aXclStrm );
+ }
+ }
+ return aString;
+}
+
+bool XclImpDffConverter::ProcessDgContainer( SvStream& rDffStrm, const DffRecordHeader& rDgHeader )
+{
+ std::size_t nEndPos = rDgHeader.GetRecEndFilePos();
+ bool isBreak(false);
+ while (!isBreak && rDffStrm.good() && rDffStrm.Tell() < nEndPos)
+ {
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ switch( aHeader.nRecType )
+ {
+ case DFF_msofbtSolverContainer:
+ isBreak = !ProcessSolverContainer( rDffStrm, aHeader );
+ break;
+ case DFF_msofbtSpgrContainer:
+ isBreak = !ProcessShGrContainer( rDffStrm, aHeader );
+ break;
+ default:
+ isBreak = !aHeader.SeekToEndOfRecord( rDffStrm );
+ }
+ }
+ // seek to end of drawing page container
+ isBreak = !rDgHeader.SeekToEndOfRecord( rDffStrm );
+
+ // #i12638# #i37900# connector rules
+ XclImpSolverContainer& rSolverCont = GetConvData().maSolverCont;
+ rSolverCont.UpdateConnectorRules();
+ SolveSolver( rSolverCont );
+ rSolverCont.RemoveConnectorRules();
+ return !isBreak;
+}
+
+bool XclImpDffConverter::ProcessShGrContainer( SvStream& rDffStrm, const DffRecordHeader& rShGrHeader )
+{
+ std::size_t nEndPos = rShGrHeader.GetRecEndFilePos();
+ bool isBreak(false);
+ while (!isBreak && rDffStrm.good() && rDffStrm.Tell() < nEndPos)
+ {
+ DffRecordHeader aHeader;
+ ReadDffRecordHeader( rDffStrm, aHeader );
+ switch( aHeader.nRecType )
+ {
+ case DFF_msofbtSpgrContainer:
+ case DFF_msofbtSpContainer:
+ isBreak = !ProcessShContainer( rDffStrm, aHeader );
+ break;
+ default:
+ isBreak = !aHeader.SeekToEndOfRecord( rDffStrm );
+ }
+ }
+ // seek to end of shape group container
+ return rShGrHeader.SeekToEndOfRecord( rDffStrm ) && !isBreak;
+}
+
+bool XclImpDffConverter::ProcessSolverContainer( SvStream& rDffStrm, const DffRecordHeader& rSolverHeader )
+{
+ // solver container wants to read the solver container header again
+ rSolverHeader.SeekToBegOfRecord( rDffStrm );
+ // read the entire solver container
+ ReadSvxMSDffSolverContainer( rDffStrm, GetConvData().maSolverCont );
+ // seek to end of solver container
+ return rSolverHeader.SeekToEndOfRecord( rDffStrm );
+}
+
+bool XclImpDffConverter::ProcessShContainer( SvStream& rDffStrm, const DffRecordHeader& rShHeader )
+{
+ rShHeader.SeekToBegOfRecord( rDffStrm );
+ tools::Rectangle aDummy;
+ XclImpDrawObjClientData aDrawObjClientData;
+ /* The call to ImportObj() creates and returns a new SdrObject for the
+ processed shape. We take ownership of the returned object here. If the
+ shape is a group object, all embedded objects are created recursively,
+ and the returned group object contains them all. ImportObj() calls the
+ virtual functions ProcessClientAnchor2() and ProcessObj() and writes
+ the pointer to the related draw object data (OBJ record) into aDrawObjClientData. */
+ SdrObjectUniquePtr xSdrObj( ImportObj( rDffStrm, aDrawObjClientData, aDummy, aDummy, /*nCalledByGroup*/0, /*pShapeId*/nullptr ) );
+ if (aDrawObjClientData.m_pTopLevelObj && xSdrObj )
+ InsertSdrObject( GetConvData().mrSdrPage, *aDrawObjClientData.m_pTopLevelObj, xSdrObj.release() );
+ return rShHeader.SeekToEndOfRecord( rDffStrm );
+}
+
+void XclImpDffConverter::InsertSdrObject( SdrObjList& rObjList, const XclImpDrawObjBase& rDrawObj, SdrObject* pSdrObj )
+{
+ XclImpDffConvData& rConvData = GetConvData();
+ /* Take ownership of the passed object. If insertion fails (e.g. rDrawObj
+ states to skip insertion), the object is automatically deleted. */
+ SdrObjectUniquePtr xSdrObj( pSdrObj );
+ if( xSdrObj && rDrawObj.IsInsertSdrObj() )
+ {
+ rObjList.NbcInsertObject( xSdrObj.release() );
+ // callback to drawing manager for e.g. tracking of used sheet area
+ rConvData.mrDrawing.OnObjectInserted( rDrawObj );
+ // callback to drawing object for post processing (use pSdrObj, xSdrObj already released)
+ rDrawObj.PostProcessSdrObject( *this, *pSdrObj );
+ }
+ /* SdrObject still here? Insertion failed, remove data from shape ID map.
+ The SdrObject will be destructed then. */
+ if( xSdrObj )
+ rConvData.maSolverCont.RemoveSdrObjectInfo( *xSdrObj );
+}
+
+void XclImpDffConverter::InitControlForm()
+{
+ XclImpDffConvData& rConvData = GetConvData();
+ if( rConvData.mbHasCtrlForm )
+ return;
+
+ rConvData.mbHasCtrlForm = true;
+ if( !SupportsOleObjects() )
+ return;
+
+ try
+ {
+ Reference< XFormsSupplier > xFormsSupplier( rConvData.mrSdrPage.getUnoPage(), UNO_QUERY_THROW );
+ Reference< XNameContainer > xFormsNC( xFormsSupplier->getForms(), UNO_SET_THROW );
+ // find or create the Standard form used to insert the imported controls
+ if( xFormsNC->hasByName( gaStdFormName ) )
+ {
+ xFormsNC->getByName( gaStdFormName ) >>= rConvData.mxCtrlForm;
+ }
+ else if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ rConvData.mxCtrlForm.set( ScfApiHelper::CreateInstance( pDocShell, "com.sun.star.form.component.Form" ), UNO_QUERY_THROW );
+ xFormsNC->insertByName( gaStdFormName, Any( rConvData.mxCtrlForm ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+// Drawing manager ============================================================
+
+XclImpDrawing::XclImpDrawing( const XclImpRoot& rRoot, bool bOleObjects ) :
+ XclImpRoot( rRoot ),
+ mbOleObjs( bOleObjects )
+{
+}
+
+XclImpDrawing::~XclImpDrawing()
+{
+}
+
+Graphic XclImpDrawing::ReadImgData( const XclImpRoot& rRoot, XclImpStream& rStrm )
+{
+ Graphic aGraphic;
+ sal_uInt16 nFormat = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );//nEnv
+ sal_uInt32 nDataSize = rStrm.ReaduInt32();
+ if( nDataSize <= rStrm.GetRecLeft() )
+ {
+ switch( nFormat )
+ {
+ case EXC_IMGDATA_WMF: ReadWmf( aGraphic, rStrm ); break;
+ case EXC_IMGDATA_BMP: ReadBmp( aGraphic, rRoot, rStrm ); break;
+ default: OSL_FAIL( "XclImpDrawing::ReadImgData - unknown image format" );
+ }
+ }
+ return aGraphic;
+}
+
+void XclImpDrawing::ReadObj( XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj;
+
+ /* #i61786# In BIFF8 streams, OBJ records may occur without MSODRAWING
+ records. In this case, the OBJ records are in BIFF5 format. Do a sanity
+ check here that there is no DFF data loaded before. */
+ OSL_ENSURE( maDffStrm.Tell() == 0, "XclImpDrawing::ReadObj - unexpected DFF stream data, OBJ will be ignored" );
+ if( maDffStrm.Tell() == 0 ) switch( GetBiff() )
+ {
+ case EXC_BIFF3:
+ xDrawObj = XclImpDrawObjBase::ReadObj3( GetRoot(), rStrm );
+ break;
+ case EXC_BIFF4:
+ xDrawObj = XclImpDrawObjBase::ReadObj4( GetRoot(), rStrm );
+ break;
+ case EXC_BIFF5:
+ case EXC_BIFF8:
+ xDrawObj = XclImpDrawObjBase::ReadObj5( GetRoot(), rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+
+ if( xDrawObj )
+ {
+ // insert into maRawObjs or into the last open group object
+ maRawObjs.InsertGrouped( xDrawObj );
+ // to be able to find objects by ID
+ maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj;
+ }
+}
+
+void XclImpDrawing::ReadMsoDrawing( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ // disable internal CONTINUE handling
+ rStrm.ResetRecord( false );
+ // read leading MSODRAWING record
+ ReadDffRecord( rStrm );
+
+ // read following drawing records, but do not start following unrelated record
+ bool bLoop = true;
+ while( bLoop ) switch( rStrm.GetNextRecId() )
+ {
+ case EXC_ID_MSODRAWING:
+ case EXC_ID_MSODRAWINGSEL:
+ case EXC_ID_CONT:
+ rStrm.StartNextRecord();
+ ReadDffRecord( rStrm );
+ break;
+ case EXC_ID_OBJ:
+ rStrm.StartNextRecord();
+ ReadObj8( rStrm );
+ break;
+ case EXC_ID_TXO:
+ rStrm.StartNextRecord();
+ ReadTxo( rStrm );
+ break;
+ default:
+ bLoop = false;
+ }
+
+ // re-enable internal CONTINUE handling
+ rStrm.ResetRecord( true );
+}
+
+XclImpDrawObjRef XclImpDrawing::FindDrawObj( const DffRecordHeader& rHeader ) const
+{
+ /* maObjMap stores objects by position of the client data (OBJ record) in
+ the DFF stream, which is always behind shape start position of the
+ passed header. The function upper_bound() finds the first element in
+ the map whose key is greater than the start position of the header. Its
+ end position is used to test whether the found object is really related
+ to the shape. */
+ XclImpDrawObjRef xDrawObj;
+ XclImpObjMap::const_iterator aIt = maObjMap.upper_bound( rHeader.GetRecBegFilePos() );
+ if( (aIt != maObjMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) )
+ xDrawObj = aIt->second;
+ return xDrawObj;
+}
+
+XclImpDrawObjRef XclImpDrawing::FindDrawObj( sal_uInt16 nObjId ) const
+{
+ XclImpDrawObjRef xDrawObj;
+ XclImpObjMapById::const_iterator aIt = maObjMapId.find( nObjId );
+ if( aIt != maObjMapId.end() )
+ xDrawObj = aIt->second;
+ return xDrawObj;
+}
+
+const XclImpObjTextData* XclImpDrawing::FindTextData( const DffRecordHeader& rHeader ) const
+{
+ /* maTextMap stores textbox data by position of the client data (TXO
+ record) in the DFF stream, which is always behind shape start position
+ of the passed header. The function upper_bound() finds the first
+ element in the map whose key is greater than the start position of the
+ header. Its end position is used to test whether the found object is
+ really related to the shape. */
+ XclImpObjTextMap::const_iterator aIt = maTextMap.upper_bound( rHeader.GetRecBegFilePos() );
+ if( (aIt != maTextMap.end()) && (aIt->first <= rHeader.GetRecEndFilePos()) )
+ return aIt->second.get();
+ return nullptr;
+}
+
+void XclImpDrawing::SetSkipObj( sal_uInt16 nObjId )
+{
+ maSkipObjs.push_back( nObjId );
+}
+
+std::size_t XclImpDrawing::GetProgressSize() const
+{
+ return std::accumulate(maObjMap.begin(), maObjMap.end(), maRawObjs.GetProgressSize(),
+ [](const std::size_t& rSum, const XclImpObjMap::value_type& rEntry) { return rSum + rEntry.second->GetProgressSize(); });
+}
+
+void XclImpDrawing::ImplConvertObjects( XclImpDffConverter& rDffConv, SdrModel& rSdrModel, SdrPage& rSdrPage )
+{
+ //rhbz#636521, disable undo during conversion. faster, smaller and stops
+ //temp objects being inserted into the undo list
+ bool bOrigUndoStatus = rSdrModel.IsUndoEnabled();
+ rSdrModel.EnableUndo(false);
+ // register this drawing manager at the passed (global) DFF manager
+ rDffConv.InitializeDrawing( *this, rSdrModel, rSdrPage );
+ // process list of objects to be skipped
+ for( const auto& rSkipObj : maSkipObjs )
+ if( XclImpDrawObjBase* pDrawObj = FindDrawObj( rSkipObj ).get() )
+ pDrawObj->SetProcessSdrObj( false );
+ // process drawing objects without DFF data
+ rDffConv.ProcessDrawing( maRawObjs );
+ // process all objects in the DFF stream
+ rDffConv.ProcessDrawing( maDffStrm );
+ // unregister this drawing manager at the passed (global) DFF manager
+ rDffConv.FinalizeDrawing();
+ rSdrModel.EnableUndo(bOrigUndoStatus);
+}
+
+// protected ------------------------------------------------------------------
+
+void XclImpDrawing::AppendRawObject( const XclImpDrawObjRef& rxDrawObj )
+{
+ OSL_ENSURE( rxDrawObj, "XclImpDrawing::AppendRawObject - unexpected empty reference" );
+ maRawObjs.push_back( rxDrawObj );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpDrawing::ReadWmf( Graphic& rGraphic, XclImpStream& rStrm ) // static helper
+{
+ // extract graphic data from IMGDATA and following CONTINUE records
+ rStrm.Ignore( 8 );
+ SvMemoryStream aMemStrm;
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ // import the graphic from memory stream
+ GDIMetaFile aGDIMetaFile;
+ if( ::ReadWindowMetafile( aMemStrm, aGDIMetaFile ) )
+ rGraphic = aGDIMetaFile;
+}
+
+void XclImpDrawing::ReadBmp( Graphic& rGraphic, const XclImpRoot& rRoot, XclImpStream& rStrm ) // static helper
+{
+ // extract graphic data from IMGDATA and following CONTINUE records
+ SvMemoryStream aMemStrm;
+
+ /* Excel 3 and 4 seem to write broken BMP data. Usually they write a
+ DIBCOREHEADER (12 bytes) containing width, height, planes = 1, and
+ pixel depth = 32 bit. After that, 3 unused bytes are added before the
+ actual pixel data. This does even confuse Excel 5 and later, which
+ cannot read the image data correctly. */
+ if( rRoot.GetBiff() <= EXC_BIFF4 )
+ {
+ rStrm.PushPosition();
+ sal_uInt32 nHdrSize;
+ sal_uInt16 nWidth, nHeight, nPlanes, nDepth;
+ nHdrSize = rStrm.ReaduInt32();
+ nWidth = rStrm.ReaduInt16();
+ nHeight = rStrm.ReaduInt16();
+ nPlanes = rStrm.ReaduInt16();
+ nDepth = rStrm.ReaduInt16();
+ if( (nHdrSize == 12) && (nPlanes == 1) && (nDepth == 32) )
+ {
+ rStrm.Ignore( 3 );
+ aMemStrm.SetEndian( SvStreamEndian::LITTLE );
+ aMemStrm.WriteUInt32( nHdrSize ).WriteUInt16( nWidth ).WriteUInt16( nHeight ).WriteUInt16( nPlanes ).WriteUInt16( nDepth );
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+ }
+ rStrm.PopPosition();
+ }
+
+ // no special handling above -> just copy the remaining record data
+ if( aMemStrm.Tell() == 0 )
+ rStrm.CopyToStream( aMemStrm, rStrm.GetRecLeft() );
+
+ // import the graphic from memory stream
+ aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ Bitmap aBitmap;
+ if( ReadDIB(aBitmap, aMemStrm, false) ) // read DIB without file header
+ rGraphic = BitmapEx(aBitmap);
+}
+
+void XclImpDrawing::ReadDffRecord( XclImpStream& rStrm )
+{
+ maDffStrm.Seek( STREAM_SEEK_TO_END );
+ rStrm.CopyRecordToStream( maDffStrm );
+}
+
+void XclImpDrawing::ReadObj8( XclImpStream& rStrm )
+{
+ XclImpDrawObjRef xDrawObj = XclImpDrawObjBase::ReadObj8( GetRoot(), rStrm );
+ // store the new object in the internal containers
+ maObjMap[ maDffStrm.Tell() ] = xDrawObj;
+ maObjMapId[ xDrawObj->GetObjId() ] = xDrawObj;
+}
+
+void XclImpDrawing::ReadTxo( XclImpStream& rStrm )
+{
+ XclImpObjTextRef xTextData = std::make_shared<XclImpObjTextData>();
+ maTextMap[ maDffStrm.Tell() ] = xTextData;
+
+ // 1) read the TXO record
+ xTextData->maData.ReadTxo8( rStrm );
+
+ // 2) first CONTINUE with string
+ xTextData->mxString.reset();
+ bool bValid = true;
+ if( xTextData->maData.mnTextLen > 0 )
+ {
+ bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord();
+ OSL_ENSURE( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" );
+ if( bValid )
+ xTextData->mxString = std::make_shared<XclImpString>( rStrm.ReadUniString( xTextData->maData.mnTextLen ) );
+ }
+
+ // 3) second CONTINUE with formatting runs
+ if( xTextData->maData.mnFormatSize > 0 )
+ {
+ bValid = (rStrm.GetNextRecId() == EXC_ID_CONT) && rStrm.StartNextRecord();
+ OSL_ENSURE( bValid, "XclImpDrawing::ReadTxo - missing CONTINUE record" );
+ if( bValid )
+ xTextData->ReadFormats( rStrm );
+ }
+}
+
+XclImpSheetDrawing::XclImpSheetDrawing( const XclImpRoot& rRoot, SCTAB nScTab ) :
+ XclImpDrawing( rRoot, true ),
+ maScUsedArea( ScAddress::INITIALIZE_INVALID )
+{
+ maScUsedArea.aStart.SetTab( nScTab );
+ maScUsedArea.aEnd.SetTab( nScTab );
+}
+
+void XclImpSheetDrawing::ReadNote( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ ReadNote3( rStrm );
+ break;
+ case EXC_BIFF8:
+ ReadNote8( rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpSheetDrawing::ReadTabChart( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF5 );
+ auto xChartObj = std::make_shared<XclImpChartObj>( GetRoot(), true );
+ xChartObj->ReadChartSubStream( rStrm );
+ // insert the chart as raw object without connected DFF data
+ AppendRawObject( xChartObj );
+}
+
+void XclImpSheetDrawing::ConvertObjects( XclImpDffConverter& rDffConv )
+{
+ if( SdrModel* pSdrModel = GetDoc().GetDrawLayer() )
+ if( SdrPage* pSdrPage = GetSdrPage( maScUsedArea.aStart.Tab() ) )
+ ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
+}
+
+tools::Rectangle XclImpSheetDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool /*bDffAnchor*/ ) const
+{
+ return rAnchor.GetRect( GetRoot(), maScUsedArea.aStart.Tab(), MapUnit::Map100thMM );
+}
+
+void XclImpSheetDrawing::OnObjectInserted( const XclImpDrawObjBase& rDrawObj )
+{
+ ScRange aScObjArea = rDrawObj.GetUsedArea( maScUsedArea.aStart.Tab() );
+ if( aScObjArea.IsValid() )
+ maScUsedArea.ExtendTo( aScObjArea );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpSheetDrawing::ReadNote3( XclImpStream& rStrm )
+{
+ XclAddress aXclPos;
+ rStrm >> aXclPos;
+ sal_uInt16 nTotalLen = rStrm.ReaduInt16();
+
+ ScAddress aScNotePos( ScAddress::UNINITIALIZED );
+ if( !GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) )
+ return;
+
+ sal_uInt16 nPartLen = ::std::min( nTotalLen, static_cast< sal_uInt16 >( rStrm.GetRecLeft() ) );
+ OUStringBuffer aNoteText(rStrm.ReadRawByteString( nPartLen ));
+ nTotalLen = nTotalLen - nPartLen;
+ while (true)
+ {
+ if (!nTotalLen)
+ break;
+ if (rStrm.GetNextRecId() != EXC_ID_NOTE)
+ break;
+ if (!rStrm.StartNextRecord())
+ break;
+ rStrm >> aXclPos;
+ nPartLen = rStrm.ReaduInt16();
+ OSL_ENSURE( aXclPos.mnRow == 0xFFFF, "XclImpObjectManager::ReadNote3 - missing continuation NOTE record" );
+ if( aXclPos.mnRow == 0xFFFF )
+ {
+ OSL_ENSURE( nPartLen <= nTotalLen, "XclImpObjectManager::ReadNote3 - string too long" );
+ aNoteText.append(rStrm.ReadRawByteString( nPartLen ));
+ nTotalLen = nTotalLen - ::std::min( nTotalLen, nPartLen );
+ }
+ else
+ {
+ // seems to be a new note, record already started -> load the note
+ rStrm.Seek( EXC_REC_SEEK_TO_BEGIN );
+ ReadNote( rStrm );
+ nTotalLen = 0;
+ }
+ }
+ ScNoteUtil::CreateNoteFromString( GetDoc(), aScNotePos, aNoteText.makeStringAndClear(), false, false );
+}
+
+void XclImpSheetDrawing::ReadNote8( XclImpStream& rStrm )
+{
+ XclAddress aXclPos;
+ sal_uInt16 nFlags, nObjId;
+ rStrm >> aXclPos;
+ nFlags = rStrm.ReaduInt16();
+ nObjId = rStrm.ReaduInt16();
+
+ ScAddress aScNotePos( ScAddress::UNINITIALIZED );
+ if( GetAddressConverter().ConvertAddress( aScNotePos, aXclPos, maScUsedArea.aStart.Tab(), true ) )
+ if( nObjId != EXC_OBJ_INVALID_ID )
+ if( XclImpNoteObj* pNoteObj = dynamic_cast< XclImpNoteObj* >( FindDrawObj( nObjId ).get() ) )
+ pNoteObj->SetNoteData( aScNotePos, nFlags );
+}
+
+// The object manager =========================================================
+
+XclImpObjectManager::XclImpObjectManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ maDefObjNames[ EXC_OBJTYPE_GROUP ] = "Group";
+ maDefObjNames[ EXC_OBJTYPE_LINE ] = ScResId( STR_SHAPE_LINE );
+ maDefObjNames[ EXC_OBJTYPE_RECTANGLE ] = ScResId( STR_SHAPE_RECTANGLE );
+ maDefObjNames[ EXC_OBJTYPE_OVAL ] = ScResId( STR_SHAPE_OVAL );
+ maDefObjNames[ EXC_OBJTYPE_ARC ] = "Arc";
+ maDefObjNames[ EXC_OBJTYPE_CHART ] = "Chart";
+ maDefObjNames[ EXC_OBJTYPE_TEXT ] = "Text";
+ maDefObjNames[ EXC_OBJTYPE_BUTTON ] = ScResId( STR_FORM_BUTTON );
+ maDefObjNames[ EXC_OBJTYPE_PICTURE ] = "Picture";
+ maDefObjNames[ EXC_OBJTYPE_POLYGON ] = "Freeform";
+ maDefObjNames[ EXC_OBJTYPE_CHECKBOX ] = ScResId( STR_FORM_CHECKBOX );
+ maDefObjNames[ EXC_OBJTYPE_OPTIONBUTTON ] = ScResId( STR_FORM_OPTIONBUTTON );
+ maDefObjNames[ EXC_OBJTYPE_EDIT ] = "Edit Box";
+ maDefObjNames[ EXC_OBJTYPE_LABEL ] = ScResId( STR_FORM_LABEL );
+ maDefObjNames[ EXC_OBJTYPE_DIALOG ] = "Dialog Frame";
+ maDefObjNames[ EXC_OBJTYPE_SPIN ] = ScResId( STR_FORM_SPINNER );
+ maDefObjNames[ EXC_OBJTYPE_SCROLLBAR ] = ScResId( STR_FORM_SCROLLBAR );
+ maDefObjNames[ EXC_OBJTYPE_LISTBOX ] = ScResId( STR_FORM_LISTBOX );
+ maDefObjNames[ EXC_OBJTYPE_GROUPBOX ] = ScResId( STR_FORM_GROUPBOX );
+ maDefObjNames[ EXC_OBJTYPE_DROPDOWN ] = ScResId( STR_FORM_DROPDOWN );
+ maDefObjNames[ EXC_OBJTYPE_NOTE ] = "Comment";
+ maDefObjNames[ EXC_OBJTYPE_DRAWING ] = ScResId( STR_SHAPE_AUTOSHAPE );
+}
+
+XclImpObjectManager::~XclImpObjectManager()
+{
+}
+
+void XclImpObjectManager::ReadMsoDrawingGroup( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ // Excel continues this record with MSODRAWINGGROUP and CONTINUE records, hmm.
+ rStrm.ResetRecord( true, EXC_ID_MSODRAWINGGROUP );
+ maDggStrm.Seek( STREAM_SEEK_TO_END );
+ rStrm.CopyRecordToStream( maDggStrm );
+}
+
+XclImpSheetDrawing& XclImpObjectManager::GetSheetDrawing( SCTAB nScTab )
+{
+ XclImpSheetDrawingRef& rxDrawing = maSheetDrawings[ nScTab ];
+ if( !rxDrawing )
+ rxDrawing = std::make_shared<XclImpSheetDrawing>( GetRoot(), nScTab );
+ return *rxDrawing;
+}
+
+void XclImpObjectManager::ConvertObjects()
+{
+ // do nothing if the document does not contain a drawing layer
+ if( !GetDoc().GetDrawLayer() )
+ return;
+
+ // get total progress bar size for all sheet drawing managers
+ std::size_t nProgressSize = std::accumulate(maSheetDrawings.begin(), maSheetDrawings.end(), std::size_t(0),
+ [](const std::size_t& rSum, const XclImpSheetDrawingMap::value_type& rEntry) { return rSum + rEntry.second->GetProgressSize(); });
+ // nothing to do if progress bar is zero (no objects present)
+ if( nProgressSize == 0 )
+ return;
+
+ XclImpDffConverter aDffConv( GetRoot(), maDggStrm );
+ aDffConv.StartProgressBar( nProgressSize );
+ for( auto& rEntry : maSheetDrawings )
+ rEntry.second->ConvertObjects( aDffConv );
+
+ // #i112436# don't call ScChartListenerCollection::SetDirty here,
+ // instead use InterpretDirtyCells in ScDocument::CalcAfterLoad.
+}
+
+OUString XclImpObjectManager::GetDefaultObjName( const XclImpDrawObjBase& rDrawObj ) const
+{
+ OUStringBuffer aDefName;
+ DefObjNameMap::const_iterator aIt = maDefObjNames.find( rDrawObj.GetObjType() );
+ if( aIt != maDefObjNames.end() )
+ aDefName.append(aIt->second);
+ return aDefName.append(' ').append(static_cast<sal_Int32>(rDrawObj.GetObjId())).makeStringAndClear();
+}
+
+ScRange XclImpObjectManager::GetUsedArea( SCTAB nScTab ) const
+{
+ XclImpSheetDrawingMap::const_iterator aIt = maSheetDrawings.find( nScTab );
+ if( aIt != maSheetDrawings.end() )
+ return aIt->second->GetUsedArea();
+ return ScRange( ScAddress::INITIALIZE_INVALID );
+}
+
+// DFF property set helper ====================================================
+
+XclImpDffPropSet::XclImpDffPropSet( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maDffConv( rRoot, maDummyStrm )
+{
+}
+
+void XclImpDffPropSet::Read( XclImpStream& rStrm )
+{
+ sal_uInt32 nPropSetSize;
+
+ rStrm.PushPosition();
+ rStrm.Ignore( 4 );
+ nPropSetSize = rStrm.ReaduInt32();
+ rStrm.PopPosition();
+
+ mxMemStrm.reset( new SvMemoryStream );
+ rStrm.CopyToStream( *mxMemStrm, 8 + nPropSetSize );
+ mxMemStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ maDffConv.ReadPropSet( *mxMemStrm, nullptr );
+}
+
+sal_uInt32 XclImpDffPropSet::GetPropertyValue( sal_uInt16 nPropId ) const
+{
+ return maDffConv.GetPropertyValue( nPropId, 0 );
+}
+
+void XclImpDffPropSet::FillToItemSet( SfxItemSet& rItemSet ) const
+{
+ if( mxMemStrm )
+ maDffConv.ApplyAttributes( *mxMemStrm, rItemSet );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclImpDffPropSet& rPropSet )
+{
+ rPropSet.Read( rStrm );
+ return rStrm;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiformula.cxx b/sc/source/filter/excel/xiformula.cxx
new file mode 100644
index 000000000..a5f4d7864
--- /dev/null
+++ b/sc/source/filter/excel/xiformula.cxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiformula.hxx>
+#include <rangelst.hxx>
+#include <xistream.hxx>
+
+#include <excform.hxx>
+
+// Formula compiler ===========================================================
+
+/** Implementation class of the export formula compiler. */
+class XclImpFmlaCompImpl : protected XclImpRoot, protected XclTokenArrayHelper
+{
+public:
+ explicit XclImpFmlaCompImpl( const XclImpRoot& rRoot );
+
+ /** Creates a range list from the passed Excel token array. */
+ void CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType eType,
+ const XclTokenArray& rXclTokArr, XclImpStream& rStrm );
+
+ std::unique_ptr<ScTokenArray> CreateFormula( XclFormulaType eType, const XclTokenArray& rXclTokArr );
+
+};
+
+XclImpFmlaCompImpl::XclImpFmlaCompImpl( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpFmlaCompImpl::CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType /*eType*/,
+ const XclTokenArray& rXclTokArr, XclImpStream& /*rStrm*/ )
+{
+ rScRanges.RemoveAll();
+
+ //FIXME: evil hack, using old formula import :-)
+ if( !rXclTokArr.Empty() )
+ {
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt16( EXC_ID_EOF ).WriteUInt16( rXclTokArr.GetSize() );
+ aMemStrm.WriteBytes(rXclTokArr.GetData(), rXclTokArr.GetSize());
+ XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
+ aFmlaStrm.StartNextRecord();
+ GetOldFmlaConverter().GetAbsRefs( rScRanges, aFmlaStrm, aFmlaStrm.GetRecSize() );
+ }
+}
+
+std::unique_ptr<ScTokenArray> XclImpFmlaCompImpl::CreateFormula(
+ XclFormulaType /*eType*/, const XclTokenArray& rXclTokArr )
+{
+ if (rXclTokArr.Empty())
+ return nullptr;
+
+ // evil hack! are we trying to phase out the old style formula converter ?
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt16( EXC_ID_EOF ).WriteUInt16( rXclTokArr.GetSize() );
+ aMemStrm.WriteBytes(rXclTokArr.GetData(), rXclTokArr.GetSize());
+ XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
+ aFmlaStrm.StartNextRecord();
+ std::unique_ptr<ScTokenArray> pArray;
+ GetOldFmlaConverter().Reset();
+ GetOldFmlaConverter().Convert(pArray, aFmlaStrm, aFmlaStrm.GetRecSize(), true);
+ return pArray;
+}
+
+XclImpFormulaCompiler::XclImpFormulaCompiler( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mxImpl( std::make_shared<XclImpFmlaCompImpl>( rRoot ) )
+{
+}
+
+XclImpFormulaCompiler::~XclImpFormulaCompiler()
+{
+}
+
+void XclImpFormulaCompiler::CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType eType,
+ const XclTokenArray& rXclTokArr, XclImpStream& rStrm )
+{
+ mxImpl->CreateRangeList( rScRanges, eType, rXclTokArr, rStrm );
+}
+
+std::unique_ptr<ScTokenArray> XclImpFormulaCompiler::CreateFormula(
+ XclFormulaType eType, const XclTokenArray& rXclTokArr )
+{
+ return mxImpl->CreateFormula(eType, rXclTokArr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xihelper.cxx b/sc/source/filter/excel/xihelper.cxx
new file mode 100644
index 000000000..ef38c5b65
--- /dev/null
+++ b/sc/source/filter/excel/xihelper.cxx
@@ -0,0 +1,896 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xihelper.hxx>
+#include <svl/itemset.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <editeng/editobj.hxx>
+#include <tools/urlobj.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <document.hxx>
+#include <rangelst.hxx>
+#include <editutil.hxx>
+#include <attrib.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xistring.hxx>
+#include <xistyle.hxx>
+#include <excform.hxx>
+#include <scmatrix.hxx>
+#include <documentimport.hxx>
+#include <sal/log.hxx>
+
+// Excel->Calc cell address/range conversion ==================================
+
+namespace {
+
+/** Fills the passed Calc address with the passed Excel cell coordinates without checking any limits. */
+void lclFillAddress( ScAddress& rScPos, sal_uInt16 nXclCol, sal_uInt32 nXclRow, SCTAB nScTab )
+{
+ rScPos.SetCol( static_cast< SCCOL >( nXclCol ) );
+ rScPos.SetRow( static_cast< SCROW >( nXclRow ) );
+ rScPos.SetTab( nScTab );
+}
+
+} // namespace
+
+XclImpAddressConverter::XclImpAddressConverter( const XclImpRoot& rRoot ) :
+ XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetScMaxPos() )
+{
+}
+
+// cell address ---------------------------------------------------------------
+
+bool XclImpAddressConverter::CheckAddress( const XclAddress& rXclPos, bool bWarn )
+{
+ bool bValidCol = rXclPos.mnCol <= mnMaxCol;
+ bool bValidRow = rXclPos.mnRow <= mnMaxRow;
+ bool bValid = bValidCol && bValidRow;
+ if( !bValid && bWarn )
+ {
+ mbColTrunc |= !bValidCol;
+ mbRowTrunc |= !bValidRow;
+ mrTracer.TraceInvalidAddress( ScAddress(
+ static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), 0 ), maMaxPos );
+ }
+ return bValid;
+}
+
+bool XclImpAddressConverter::ConvertAddress( ScAddress& rScPos,
+ const XclAddress& rXclPos, SCTAB nScTab, bool bWarn )
+{
+ bool bValid = CheckAddress( rXclPos, bWarn );
+ if( bValid )
+ lclFillAddress( rScPos, rXclPos.mnCol, rXclPos.mnRow, nScTab );
+ return bValid;
+}
+
+ScAddress XclImpAddressConverter::CreateValidAddress(
+ const XclAddress& rXclPos, SCTAB nScTab, bool bWarn )
+{
+ ScAddress aScPos( ScAddress::UNINITIALIZED );
+ if( !ConvertAddress( aScPos, rXclPos, nScTab, bWarn ) )
+ {
+ aScPos.SetCol( static_cast< SCCOL >( ::std::min( rXclPos.mnCol, mnMaxCol ) ) );
+ aScPos.SetRow( static_cast< SCROW >( ::std::min( rXclPos.mnRow, mnMaxRow ) ) );
+ aScPos.SetTab( limit_cast< SCTAB >( nScTab, 0, maMaxPos.Tab() ) );
+ }
+ return aScPos;
+}
+
+// cell range -----------------------------------------------------------------
+
+bool XclImpAddressConverter::ConvertRange( ScRange& rScRange,
+ const XclRange& rXclRange, SCTAB nScTab1, SCTAB nScTab2, bool bWarn )
+{
+ // check start position
+ bool bValidStart = CheckAddress( rXclRange.maFirst, bWarn );
+ if( bValidStart )
+ {
+ lclFillAddress( rScRange.aStart, rXclRange.maFirst.mnCol, rXclRange.maFirst.mnRow, nScTab1 );
+
+ // check & correct end position
+ sal_uInt16 nXclCol2 = rXclRange.maLast.mnCol;
+ sal_uInt32 nXclRow2 = rXclRange.maLast.mnRow;
+ if( !CheckAddress( rXclRange.maLast, bWarn ) )
+ {
+ nXclCol2 = ::std::min( nXclCol2, mnMaxCol );
+ nXclRow2 = ::std::min( nXclRow2, mnMaxRow );
+ }
+ lclFillAddress( rScRange.aEnd, nXclCol2, nXclRow2, nScTab2 );
+ }
+ return bValidStart;
+}
+
+// cell range list ------------------------------------------------------------
+
+void XclImpAddressConverter::ConvertRangeList( ScRangeList& rScRanges,
+ const XclRangeList& rXclRanges, SCTAB nScTab, bool bWarn )
+{
+ rScRanges.RemoveAll();
+ for( const auto& rXclRange : rXclRanges )
+ {
+ ScRange aScRange( ScAddress::UNINITIALIZED );
+ if( ConvertRange( aScRange, rXclRange, nScTab, nScTab, bWarn ) )
+ rScRanges.push_back( aScRange );
+ }
+}
+
+// String->EditEngine conversion ==============================================
+
+namespace {
+
+std::unique_ptr<EditTextObject> lclCreateTextObject( const XclImpRoot& rRoot,
+ const XclImpString& rString, XclFontItemType eType, sal_uInt16 nXFIndex )
+{
+ std::unique_ptr<EditTextObject> pTextObj;
+
+ const XclImpXFBuffer& rXFBuffer = rRoot.GetXFBuffer();
+ const XclImpFont* pFirstFont = rXFBuffer.GetFont( nXFIndex );
+ bool bFirstEscaped = pFirstFont && pFirstFont->HasEscapement();
+
+ if( rString.IsRich() || bFirstEscaped )
+ {
+ const XclImpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
+ const XclFormatRunVec& rFormats = rString.GetFormats();
+
+ ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
+ rEE.SetTextCurrentDefaults( rString.GetText() );
+
+ SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
+ if( bFirstEscaped )
+ rFontBuffer.FillToItemSet( aItemSet, eType, rXFBuffer.GetFontIndex( nXFIndex ) );
+ ESelection aSelection;
+
+ XclFormatRun aNextRun;
+ XclFormatRunVec::const_iterator aIt = rFormats.begin();
+ XclFormatRunVec::const_iterator aEnd = rFormats.end();
+
+ if( aIt != aEnd )
+ aNextRun = *aIt++;
+ else
+ aNextRun.mnChar = 0xFFFF;
+
+ sal_Int32 nLen = rString.GetText().getLength();
+ for( sal_Int32 nChar = 0; nChar < nLen; ++nChar )
+ {
+ // reached new different formatted text portion
+ if( nChar >= aNextRun.mnChar )
+ {
+ // send items to edit engine
+ rEE.QuickSetAttribs( aItemSet, aSelection );
+
+ // start new item set
+ aItemSet.ClearItem();
+ rFontBuffer.FillToItemSet( aItemSet, eType, aNextRun.mnFontIdx );
+
+ // read new formatting information
+ if( aIt != aEnd )
+ aNextRun = *aIt++;
+ else
+ aNextRun.mnChar = 0xFFFF;
+
+ // reset selection start to current position
+ aSelection.nStartPara = aSelection.nEndPara;
+ aSelection.nStartPos = aSelection.nEndPos;
+ }
+
+ // set end of selection to current position
+ if( rString.GetText()[ nChar ] == '\n' )
+ {
+ ++aSelection.nEndPara;
+ aSelection.nEndPos = 0;
+ }
+ else
+ ++aSelection.nEndPos;
+ }
+
+ // send items of last text portion to edit engine
+ rEE.QuickSetAttribs( aItemSet, aSelection );
+
+ pTextObj = rEE.CreateTextObject();
+ }
+
+ return pTextObj;
+}
+
+} // namespace
+
+std::unique_ptr<EditTextObject> XclImpStringHelper::CreateTextObject(
+ const XclImpRoot& rRoot, const XclImpString& rString )
+{
+ return lclCreateTextObject( rRoot, rString, XclFontItemType::Editeng, 0 );
+}
+
+void XclImpStringHelper::SetToDocument(
+ ScDocumentImport& rDoc, const ScAddress& rPos, const XclImpRoot& rRoot,
+ const XclImpString& rString, sal_uInt16 nXFIndex )
+{
+ if (rString.GetText().isEmpty())
+ return;
+
+ ::std::unique_ptr< EditTextObject > pTextObj( lclCreateTextObject( rRoot, rString, XclFontItemType::Editeng, nXFIndex ) );
+
+ if (pTextObj)
+ {
+ rDoc.setEditCell(rPos, std::move(pTextObj));
+ }
+ else
+ {
+ const OUString& aStr = rString.GetText();
+ if (aStr.indexOf('\n') != -1 || aStr.indexOf('\r') != -1)
+ {
+ // Multiline content.
+ ScFieldEditEngine& rEngine = rDoc.getDoc().GetEditEngine();
+ rEngine.SetTextCurrentDefaults(aStr);
+ rDoc.setEditCell(rPos, rEngine.CreateTextObject());
+ }
+ else
+ {
+ // Normal text cell.
+ rDoc.setStringCell(rPos, aStr);
+ }
+ }
+}
+
+// Header/footer conversion ===================================================
+
+XclImpHFConverter::XclImpHFPortionInfo::XclImpHFPortionInfo() :
+ mnHeight( 0 ),
+ mnMaxLineHt( 0 )
+{
+ maSel.nStartPara = maSel.nEndPara = 0;
+ maSel.nStartPos = maSel.nEndPos = 0;
+}
+
+XclImpHFConverter::XclImpHFConverter( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mrEE( rRoot.GetHFEditEngine() ),
+ mxFontData( new XclFontData ),
+ meCurrObj( EXC_HF_CENTER )
+{
+}
+
+XclImpHFConverter::~XclImpHFConverter()
+{
+}
+
+void XclImpHFConverter::ParseString( const OUString& rHFString )
+{
+ // edit engine objects
+ mrEE.SetText( OUString() );
+ maInfos.clear();
+ maInfos.resize( EXC_HF_PORTION_COUNT );
+ meCurrObj = EXC_HF_CENTER;
+
+ // parser temporaries
+ maCurrText.truncate();
+ OUStringBuffer aReadFont; // current font name
+ OUStringBuffer aReadStyle; // current font style
+ sal_uInt16 nReadHeight = 0; // current font height
+ ResetFontData();
+
+ /** State of the parser. */
+ enum XclHFParserState
+ {
+ xlPSText, /// Read text, search for functions.
+ xlPSFunc, /// Read function (token following a '&').
+ xlPSFont, /// Read font name ('&' is followed by '"', reads until next '"' or ',').
+ xlPSFontStyle, /// Read font style name (font part after ',', reads until next '"').
+ xlPSHeight /// Read font height ('&' is followed by num. digits, reads until non-digit).
+ } eState = xlPSText;
+
+ const sal_Unicode* pChar = rHFString.getStr();
+ const sal_Unicode* pNull = pChar + rHFString.getLength(); // pointer to terminating null char
+ while( *pChar )
+ {
+ switch( eState )
+ {
+
+// --- read text character ---
+
+ case xlPSText:
+ {
+ switch( *pChar )
+ {
+ case '&': // new command
+ InsertText();
+ eState = xlPSFunc;
+ break;
+ case '\n': // line break
+ InsertText();
+ InsertLineBreak();
+ break;
+ default:
+ maCurrText.append(OUStringChar(*pChar));
+ }
+ }
+ break;
+
+// --- read control sequence ---
+
+ case xlPSFunc:
+ {
+ eState = xlPSText;
+ switch( *pChar )
+ {
+ case '&': maCurrText.append("&"); break; // the '&' character
+
+ case 'L': SetNewPortion( EXC_HF_LEFT ); break; // Left portion
+ case 'C': SetNewPortion( EXC_HF_CENTER ); break; // Center portion
+ case 'R': SetNewPortion( EXC_HF_RIGHT ); break; // Right portion
+
+ case 'P': InsertField( SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD ) ); break; // page
+ case 'N': InsertField( SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD ) ); break; // page count
+ case 'D': InsertField( SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD ) ); break; // date
+ case 'T': InsertField( SvxFieldItem( SvxTimeField(), EE_FEATURE_FIELD ) ); break; // time
+ case 'A': InsertField( SvxFieldItem( SvxTableField(), EE_FEATURE_FIELD ) ); break; // table name
+
+ case 'Z': // file path
+ InsertField( SvxFieldItem( SvxExtFileField(), EE_FEATURE_FIELD ) ); // convert to full name
+ if( (pNull - pChar >= 2) && (*(pChar + 1) == '&') && (*(pChar + 2) == 'F') )
+ {
+ // &Z&F found - ignore the &F part
+ pChar += 2;
+ }
+ break;
+ case 'F': // file name
+ InsertField( SvxFieldItem( SvxExtFileField( OUString(), SvxFileType::Var, SvxFileFormat::NameAndExt ), EE_FEATURE_FIELD ) );
+ break;
+
+ case 'U': // underline
+ SetAttribs();
+ mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_SINGLE) ?
+ EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_SINGLE;
+ break;
+ case 'E': // double underline
+ SetAttribs();
+ mxFontData->mnUnderline = (mxFontData->mnUnderline == EXC_FONTUNDERL_DOUBLE) ?
+ EXC_FONTUNDERL_NONE : EXC_FONTUNDERL_DOUBLE;
+ break;
+ case 'S': // strikeout
+ SetAttribs();
+ mxFontData->mbStrikeout = !mxFontData->mbStrikeout;
+ break;
+ case 'X': // superscript
+ SetAttribs();
+ mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUPER) ?
+ EXC_FONTESC_NONE : EXC_FONTESC_SUPER;
+ break;
+ case 'Y': // subscript
+ SetAttribs();
+ mxFontData->mnEscapem = (mxFontData->mnEscapem == EXC_FONTESC_SUB) ?
+ EXC_FONTESC_NONE : EXC_FONTESC_SUB;
+ break;
+
+ case '\"': // font name
+ aReadFont.setLength(0);
+ aReadStyle.setLength(0);
+ eState = xlPSFont;
+ break;
+ default:
+ if( ('0' <= *pChar) && (*pChar <= '9') ) // font size
+ {
+ nReadHeight = *pChar - '0';
+ eState = xlPSHeight;
+ }
+ }
+ }
+ break;
+
+// --- read font name ---
+
+ case xlPSFont:
+ {
+ switch( *pChar )
+ {
+ case '\"':
+ --pChar;
+ [[fallthrough]];
+ case ',':
+ eState = xlPSFontStyle;
+ break;
+ default:
+ aReadFont.append(*pChar);
+ }
+ }
+ break;
+
+// --- read font style ---
+
+ case xlPSFontStyle:
+ {
+ switch( *pChar )
+ {
+ case '\"':
+ SetAttribs();
+ if( !aReadFont.isEmpty() )
+ mxFontData->maName = aReadFont.toString();
+ mxFontData->maStyle = aReadStyle.toString();
+ eState = xlPSText;
+ break;
+ default:
+ aReadStyle.append(*pChar);
+ }
+ }
+ break;
+
+// --- read font height ---
+
+ case xlPSHeight:
+ {
+ if( ('0' <= *pChar) && (*pChar <= '9') )
+ {
+ if( nReadHeight != 0xFFFF )
+ {
+ nReadHeight *= 10;
+ nReadHeight += (*pChar - '0');
+ if( nReadHeight > 1600 ) // max 1600pt = 32000twips
+ nReadHeight = 0xFFFF;
+ }
+ }
+ else
+ {
+ if( (nReadHeight != 0) && (nReadHeight != 0xFFFF) )
+ {
+ SetAttribs();
+ mxFontData->mnHeight = nReadHeight * 20;
+ }
+ --pChar;
+ eState = xlPSText;
+ }
+ }
+ break;
+ }
+ ++pChar;
+ }
+
+ // finalize
+ CreateCurrObject();
+ maInfos[ EXC_HF_LEFT ].mnHeight += GetMaxLineHeight( EXC_HF_LEFT );
+ maInfos[ EXC_HF_CENTER ].mnHeight += GetMaxLineHeight( EXC_HF_CENTER );
+ maInfos[ EXC_HF_RIGHT ].mnHeight += GetMaxLineHeight( EXC_HF_RIGHT );
+}
+
+void XclImpHFConverter::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nWhichId ) const
+{
+ ScPageHFItem aHFItem( nWhichId );
+ if( maInfos[ EXC_HF_LEFT ].mxObj )
+ aHFItem.SetLeftArea( *maInfos[ EXC_HF_LEFT ].mxObj );
+ if( maInfos[ EXC_HF_CENTER ].mxObj )
+ aHFItem.SetCenterArea( *maInfos[ EXC_HF_CENTER ].mxObj );
+ if( maInfos[ EXC_HF_RIGHT ].mxObj )
+ aHFItem.SetRightArea( *maInfos[ EXC_HF_RIGHT ].mxObj );
+ rItemSet.Put( aHFItem );
+}
+
+sal_Int32 XclImpHFConverter::GetTotalHeight() const
+{
+ return ::std::max( maInfos[ EXC_HF_LEFT ].mnHeight,
+ ::std::max( maInfos[ EXC_HF_CENTER ].mnHeight, maInfos[ EXC_HF_RIGHT ].mnHeight ) );
+}
+
+// private --------------------------------------------------------------------
+
+sal_uInt16 XclImpHFConverter::GetMaxLineHeight( XclImpHFPortion ePortion ) const
+{
+ sal_uInt16 nMaxHt = maInfos[ ePortion ].mnMaxLineHt;
+ return (nMaxHt == 0) ? mxFontData->mnHeight : nMaxHt;
+}
+
+void XclImpHFConverter::UpdateMaxLineHeight( XclImpHFPortion ePortion )
+{
+ sal_uInt16& rnMaxHt = maInfos[ ePortion ].mnMaxLineHt;
+ rnMaxHt = ::std::max( rnMaxHt, mxFontData->mnHeight );
+}
+
+void XclImpHFConverter::UpdateCurrMaxLineHeight()
+{
+ UpdateMaxLineHeight( meCurrObj );
+}
+
+void XclImpHFConverter::SetAttribs()
+{
+ ESelection& rSel = GetCurrSel();
+ if( (rSel.nStartPara != rSel.nEndPara) || (rSel.nStartPos != rSel.nEndPos) )
+ {
+ SfxItemSet aItemSet( mrEE.GetEmptyItemSet() );
+ XclImpFont aFont( GetRoot(), *mxFontData );
+ aFont.FillToItemSet( aItemSet, XclFontItemType::HeaderFooter );
+ mrEE.QuickSetAttribs( aItemSet, rSel );
+ rSel.nStartPara = rSel.nEndPara;
+ rSel.nStartPos = rSel.nEndPos;
+ }
+}
+
+void XclImpHFConverter::ResetFontData()
+{
+ if( const XclImpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
+ *mxFontData = pFirstFont->GetFontData();
+ else
+ {
+ mxFontData->Clear();
+ mxFontData->mnHeight = 200;
+ }
+}
+
+void XclImpHFConverter::InsertText()
+{
+ if( !maCurrText.isEmpty() )
+ {
+ ESelection& rSel = GetCurrSel();
+ OUString sString(maCurrText.makeStringAndClear());
+ mrEE.QuickInsertText( sString, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ rSel.nEndPos = rSel.nEndPos + sString.getLength();
+ UpdateCurrMaxLineHeight();
+ }
+}
+
+void XclImpHFConverter::InsertField( const SvxFieldItem& rFieldItem )
+{
+ ESelection& rSel = GetCurrSel();
+ mrEE.QuickInsertField( rFieldItem, ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ ++rSel.nEndPos;
+ UpdateCurrMaxLineHeight();
+}
+
+void XclImpHFConverter::InsertLineBreak()
+{
+ ESelection& rSel = GetCurrSel();
+ mrEE.QuickInsertText( OUString('\n'), ESelection( rSel.nEndPara, rSel.nEndPos, rSel.nEndPara, rSel.nEndPos ) );
+ ++rSel.nEndPara;
+ rSel.nEndPos = 0;
+ GetCurrInfo().mnHeight += GetMaxLineHeight( meCurrObj );
+ GetCurrInfo().mnMaxLineHt = 0;
+}
+
+void XclImpHFConverter::CreateCurrObject()
+{
+ InsertText();
+ SetAttribs();
+ GetCurrObj() = mrEE.CreateTextObject();
+}
+
+void XclImpHFConverter::SetNewPortion( XclImpHFPortion eNew )
+{
+ if( eNew != meCurrObj )
+ {
+ CreateCurrObject();
+ meCurrObj = eNew;
+ if( GetCurrObj() )
+ mrEE.SetText( *GetCurrObj() );
+ else
+ mrEE.SetText( OUString() );
+ ResetFontData();
+ }
+}
+
+// URL conversion =============================================================
+
+namespace {
+
+void lclAppendUrlChar( OUString& rUrl, sal_Unicode cChar )
+{
+ // encode special characters
+ switch( cChar )
+ {
+ case '#': rUrl += "%23"; break;
+ case '%': rUrl += "%25"; break;
+ default: rUrl += OUStringChar( cChar );
+ }
+}
+
+} // namespace
+
+void XclImpUrlHelper::DecodeUrl(
+ OUString& rUrl, OUString& rTabName, bool& rbSameWb,
+ const XclImpRoot& rRoot, const OUString& rEncodedUrl )
+{
+ enum
+ {
+ xlUrlInit, /// Initial state, read string mode character.
+ xlUrlPath, /// Read URL path.
+ xlUrlFileName, /// Read file name.
+ xlUrlSheetName, /// Read sheet name.
+ xlUrlRaw /// Raw mode. No control characters will occur.
+ } eState = xlUrlInit;
+
+ bool bEncoded = true;
+ rbSameWb = false;
+
+ sal_Unicode cCurrDrive = 0;
+ OUString aDosBase( INetURLObject( rRoot.GetBasePath() ).getFSysPath( FSysStyle::Dos ) );
+ if (!aDosBase.isEmpty() && aDosBase.match(":\\", 1))
+ cCurrDrive = aDosBase[0];
+
+ const sal_Unicode* pChar = rEncodedUrl.getStr();
+ while( *pChar )
+ {
+ switch( eState )
+ {
+
+// --- first character ---
+
+ case xlUrlInit:
+ {
+ switch( *pChar )
+ {
+ case EXC_URLSTART_ENCODED:
+ eState = xlUrlPath;
+ break;
+ case EXC_URLSTART_SELF:
+ case EXC_URLSTART_SELFENCODED:
+ rbSameWb = true;
+ eState = xlUrlSheetName;
+ break;
+ case '[':
+ bEncoded = false;
+ eState = xlUrlFileName;
+ break;
+ default:
+ bEncoded = false;
+ lclAppendUrlChar( rUrl, *pChar );
+ eState = xlUrlPath;
+ }
+ }
+ break;
+
+// --- URL path ---
+
+ case xlUrlPath:
+ {
+ switch( *pChar )
+ {
+ case EXC_URL_DOSDRIVE:
+ {
+ if( *(pChar + 1) )
+ {
+ ++pChar;
+ if( *pChar == '@' )
+ rUrl += "\\\\";
+ else
+ {
+ lclAppendUrlChar( rUrl, *pChar );
+ rUrl += ":\\";
+ }
+ }
+ else
+ rUrl += "<NULL-DRIVE!>";
+ }
+ break;
+ case EXC_URL_DRIVEROOT:
+ if( cCurrDrive )
+ {
+ lclAppendUrlChar( rUrl, cCurrDrive );
+ rUrl += ":";
+ }
+ [[fallthrough]];
+ case EXC_URL_SUBDIR:
+ if( bEncoded )
+ rUrl += "\\";
+ else // control character in raw name -> DDE link
+ {
+ rUrl += OUStringChar(EXC_DDE_DELIM);
+ eState = xlUrlRaw;
+ }
+ break;
+ case EXC_URL_PARENTDIR:
+ rUrl += "..\\";
+ break;
+ case EXC_URL_RAW:
+ {
+ if( *(pChar + 1) )
+ {
+ sal_Int32 nLen = *++pChar;
+ for( sal_Int32 nChar = 0; (nChar < nLen) && *(pChar + 1); ++nChar )
+ lclAppendUrlChar( rUrl, *++pChar );
+// rUrl.Append( ':' );
+ }
+ }
+ break;
+ case '[':
+ eState = xlUrlFileName;
+ break;
+ default:
+ lclAppendUrlChar( rUrl, *pChar );
+ }
+ }
+ break;
+
+// --- file name ---
+
+ case xlUrlFileName:
+ {
+ switch( *pChar )
+ {
+ case ']': eState = xlUrlSheetName; break;
+ default: lclAppendUrlChar( rUrl, *pChar );
+ }
+ }
+ break;
+
+// --- sheet name ---
+
+ case xlUrlSheetName:
+ rTabName += OUStringChar( *pChar );
+ break;
+
+// --- raw read mode ---
+
+ case xlUrlRaw:
+ lclAppendUrlChar( rUrl, *pChar );
+ break;
+ }
+
+ ++pChar;
+ }
+}
+
+void XclImpUrlHelper::DecodeUrl(
+ OUString& rUrl, bool& rbSameWb, const XclImpRoot& rRoot, const OUString& rEncodedUrl )
+{
+ OUString aTabName;
+ OUString aUrl;
+ DecodeUrl( aUrl, aTabName, rbSameWb, rRoot, rEncodedUrl );
+ rUrl = aUrl;
+ OSL_ENSURE( aTabName.isEmpty(), "XclImpUrlHelper::DecodeUrl - sheet name ignored" );
+}
+
+bool XclImpUrlHelper::DecodeLink( OUString& rApplic, OUString& rTopic, const OUString& rEncUrl )
+{
+ sal_Int32 nPos = rEncUrl.indexOf( EXC_DDE_DELIM );
+ if( (nPos > 0) && (nPos + 1 < rEncUrl.getLength()) )
+ {
+ rApplic = rEncUrl.copy( 0, nPos );
+ rTopic = rEncUrl.copy( nPos + 1 );
+ return true;
+ }
+ return false;
+}
+
+// Cached Values ==============================================================
+
+XclImpCachedValue::XclImpCachedValue( XclImpStream& rStrm ) :
+ mfValue( 0.0 ),
+ mnBoolErr( 0 )
+{
+ mnType = rStrm.ReaduInt8();
+ switch( mnType )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ rStrm.Ignore( 8 );
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ mfValue = rStrm.ReadDouble();
+ break;
+ case EXC_CACHEDVAL_STRING:
+ maStr = rStrm.ReadUniString();
+ break;
+ case EXC_CACHEDVAL_BOOL:
+ case EXC_CACHEDVAL_ERROR:
+ {
+ double fVal;
+ mnBoolErr = rStrm.ReaduInt8();
+ rStrm.Ignore( 7 );
+
+ std::unique_ptr<ScTokenArray> pScTokArr = rStrm.GetRoot().GetOldFmlaConverter().GetBoolErr(
+ XclTools::ErrorToEnum( fVal, mnType == EXC_CACHEDVAL_ERROR, mnBoolErr ) );
+ if( pScTokArr )
+ mxTokArr = std::move( pScTokArr );
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpCachedValue::XclImpCachedValue - unknown data type" );
+ }
+}
+
+XclImpCachedValue::~XclImpCachedValue()
+{
+}
+
+FormulaError XclImpCachedValue::GetScError() const
+{
+ return (mnType == EXC_CACHEDVAL_ERROR) ? XclTools::GetScErrorCode( mnBoolErr ) : FormulaError::NONE;
+}
+
+// Matrix Cached Values ==============================================================
+
+XclImpCachedMatrix::XclImpCachedMatrix( XclImpStream& rStrm ) :
+ mnScCols( 0 ),
+ mnScRows( 0 )
+{
+ mnScCols = rStrm.ReaduInt8();
+ mnScRows = rStrm.ReaduInt16();
+
+ if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
+ {
+ // in BIFF2-BIFF7: 256 columns represented by 0 columns
+ if( mnScCols == 0 )
+ mnScCols = 256;
+ }
+ else
+ {
+ // in BIFF8: columns and rows decreased by 1
+ ++mnScCols;
+ ++mnScRows;
+ }
+
+ //assuming worst case scenario of unknown types
+ const size_t nMinRecordSize = 1;
+ const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * mnScCols);
+ if (mnScRows > nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << mnScRows << " claimed, truncating");
+ mnScRows = nMaxRows;
+ }
+
+ for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow )
+ for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol )
+ maValueList.push_back( std::make_unique<XclImpCachedValue>( rStrm ) );
+}
+
+XclImpCachedMatrix::~XclImpCachedMatrix()
+{
+}
+
+ScMatrixRef XclImpCachedMatrix::CreateScMatrix( svl::SharedStringPool& rPool ) const
+{
+ ScMatrixRef xScMatrix;
+ OSL_ENSURE( mnScCols * mnScRows == maValueList.size(), "XclImpCachedMatrix::CreateScMatrix - element count mismatch" );
+ if( mnScCols && mnScRows && static_cast< sal_uLong >( mnScCols * mnScRows ) <= maValueList.size() )
+ {
+ xScMatrix = new ScMatrix(mnScCols, mnScRows, 0.0);
+ XclImpValueList::const_iterator itValue = maValueList.begin();
+ for( SCSIZE nScRow = 0; nScRow < mnScRows; ++nScRow )
+ {
+ for( SCSIZE nScCol = 0; nScCol < mnScCols; ++nScCol )
+ {
+ switch( (*itValue)->GetType() )
+ {
+ case EXC_CACHEDVAL_EMPTY:
+ // Excel shows 0.0 here, not an empty cell
+ xScMatrix->PutEmpty( nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ xScMatrix->PutDouble( (*itValue)->GetValue(), nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_STRING:
+ xScMatrix->PutString(rPool.intern((*itValue)->GetString()), nScCol, nScRow);
+ break;
+ case EXC_CACHEDVAL_BOOL:
+ xScMatrix->PutBoolean( (*itValue)->GetBool(), nScCol, nScRow );
+ break;
+ case EXC_CACHEDVAL_ERROR:
+ xScMatrix->PutError( (*itValue)->GetScError(), nScCol, nScRow );
+ break;
+ default:
+ OSL_FAIL( "XclImpCachedMatrix::CreateScMatrix - unknown value type" );
+ xScMatrix->PutEmpty( nScCol, nScRow );
+ }
+ ++itValue;
+ }
+ }
+ }
+ return xScMatrix;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xilink.cxx b/sc/source/filter/excel/xilink.cxx
new file mode 100644
index 000000000..e9fea951e
--- /dev/null
+++ b/sc/source/filter/excel/xilink.cxx
@@ -0,0 +1,962 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xilink.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiname.hxx>
+#include <xltools.hxx>
+#include <excform.hxx>
+#include <tokenarray.hxx>
+#include <externalrefmgr.hxx>
+#include <scmatrix.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+
+#include <vector>
+#include <memory>
+
+// *** Helper classes ***
+
+// Cached external cells ======================================================
+
+namespace {
+
+/**
+ * Contains the address and value of an external referenced cell.
+ * Note that this is non-copyable, so cannot be used in most stl/boost containers.
+ */
+class XclImpCrn : public XclImpCachedValue
+{
+public:
+ /** Reads a cached value and stores it with its cell address. */
+ explicit XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos );
+
+ const XclAddress& GetAddress() const { return maXclPos;}
+
+private:
+ XclAddress maXclPos; /// Excel position of the cached cell.
+};
+
+// Sheet in an external document ==============================================
+
+/** Contains the name and sheet index of one sheet in an external document. */
+class XclImpSupbookTab
+{
+public:
+ /** Stores the sheet name and marks the sheet index as invalid.
+ The sheet index is set while creating the Calc sheet with CreateTable(). */
+ explicit XclImpSupbookTab( const OUString& rTabName );
+
+ const OUString& GetTabName() const { return maTabName; }
+
+ /** Reads a CRN record (external referenced cell) at the specified address. */
+ void ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos );
+
+ void LoadCachedValues( const ScExternalRefCache::TableTypeRef& pCacheTable,
+ svl::SharedStringPool& rPool );
+
+private:
+ typedef std::shared_ptr< XclImpCrn > XclImpCrnRef;
+
+ std::vector< XclImpCrnRef > maCrnList; /// List of CRN records (cached cell values).
+ OUString maTabName; /// Name of the external sheet.
+};
+
+}
+
+// External document (SUPBOOK) ================================================
+
+/** This class represents an external linked document (record SUPBOOK).
+ @descr Contains a list of all referenced sheets in the document. */
+class XclImpSupbook : protected XclImpRoot
+{
+public:
+ /** Reads the SUPBOOK record from stream. */
+ explicit XclImpSupbook( XclImpStream& rStrm );
+
+ /** Reads an XCT record (count of following CRNs and current sheet). */
+ void ReadXct( XclImpStream& rStrm );
+ /** Reads a CRN record (external referenced cell). */
+ void ReadCrn( XclImpStream& rStrm );
+ /** Reads an EXTERNNAME record. */
+ void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv );
+
+ /** Returns the SUPBOOK record type. */
+ XclSupbookType GetType() const { return meType; }
+
+ /** Returns the URL of the external document. */
+ const OUString& GetXclUrl() const { return maXclUrl; }
+
+ /** Returns the external name specified by an index from the Excel document (one-based). */
+ const XclImpExtName* GetExternName( sal_uInt16 nXclIndex ) const;
+ /** Tries to decode the URL to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ bool GetLinkData( OUString& rApplic, OUString& rDoc ) const;
+ /** Returns the specified macro name (1-based) or an empty string on error. */
+ OUString GetMacroName( sal_uInt16 nXclNameIdx ) const;
+
+ OUString GetTabName( sal_uInt16 nXtiTab ) const;
+
+ sal_uInt16 GetTabCount() const;
+
+ void LoadCachedValues();
+
+ svl::SharedStringPool& GetSharedStringPool();
+
+private:
+
+ std::vector< std::unique_ptr<XclImpSupbookTab> >
+ maSupbTabList; /// All sheet names of the document.
+ std::vector< std::unique_ptr<XclImpExtName> >
+ maExtNameList; /// All external names of the document.
+ OUString maXclUrl; /// URL of the external document (Excel mode).
+ XclSupbookType meType; /// Type of the supbook record.
+ sal_uInt16 mnSBTab; /// Current Excel sheet index from SUPBOOK for XCT/CRN records.
+};
+
+// Import link manager ========================================================
+
+namespace {
+
+/** Contains the SUPBOOK index and sheet indexes of an external link.
+ @descr It is possible to enter a formula like =SUM(Sheet1:Sheet3!A1),
+ therefore here occurs a sheet range. */
+struct XclImpXti
+{
+ sal_uInt16 mnSupbook; /// Index to SUPBOOK record.
+ sal_uInt16 mnSBTabFirst; /// Index to the first sheet of the range in the SUPBOOK.
+ sal_uInt16 mnSBTabLast; /// Index to the last sheet of the range in the SUPBOOK.
+ explicit XclImpXti() : mnSupbook( SAL_MAX_UINT16 ), mnSBTabFirst( SAL_MAX_UINT16 ), mnSBTabLast( SAL_MAX_UINT16 ) {}
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclImpXti& rXti )
+{
+ rXti.mnSupbook = rStrm.ReaduInt16();
+ rXti.mnSBTabFirst = rStrm.ReaduInt16();
+ rXti.mnSBTabLast = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+}
+
+/** Implementation of the link manager. */
+class XclImpLinkManagerImpl : protected XclImpRoot
+{
+public:
+ explicit XclImpLinkManagerImpl( const XclImpRoot& rRoot );
+
+ /** Reads the EXTERNSHEET record. */
+ void ReadExternsheet( XclImpStream& rStrm );
+ /** Reads a SUPBOOK record. */
+ void ReadSupbook( XclImpStream& rStrm );
+ /** Reads an XCT record and appends it to the current SUPBOOK. */
+ void ReadXct( XclImpStream& rStrm );
+ /** Reads a CRN record and appends it to the current SUPBOOK. */
+ void ReadCrn( XclImpStream& rStrm );
+ /** Reads an EXTERNNAME record and appends it to the current SUPBOOK. */
+ void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv );
+
+ /** Returns true, if the specified XTI entry contains an internal reference. */
+ bool IsSelfRef( sal_uInt16 nXtiIndex ) const;
+ /** Returns the Calc sheet index range of the specified XTI entry.
+ @return true = XTI data found, returned sheet index range is valid. */
+ bool GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab,
+ sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified external name or 0 on error. */
+ const XclImpExtName* GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const;
+
+ /** Returns the absolute file URL of a supporting workbook specified by
+ the index. */
+ const OUString* GetSupbookUrl( sal_uInt16 nXtiIndex ) const;
+
+ OUString GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const;
+
+ /** Tries to decode the URL of the specified XTI entry to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ bool GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified macro name or an empty string on error. */
+ OUString GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const;
+
+private:
+ /** Returns the specified XTI (link entry from BIFF8 EXTERNSHEET record). */
+ const XclImpXti* GetXti( sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified SUPBOOK (external document). */
+ const XclImpSupbook* GetSupbook( sal_uInt16 nXtiIndex ) const;
+
+ void LoadCachedValues();
+
+private:
+ typedef std::vector< XclImpXti > XclImpXtiVector;
+
+ XclImpXtiVector maXtiList; /// List of all XTI structures.
+ std::vector< std::unique_ptr<XclImpSupbook> >
+ maSupbookList; /// List of external documents.
+};
+
+// *** Implementation ***
+
+// Excel sheet indexes ========================================================
+
+// original Excel sheet names -------------------------------------------------
+
+void XclImpTabInfo::AppendXclTabName( const OUString& rXclTabName, SCTAB nScTab )
+{
+ maTabNames[ rXclTabName ] = nScTab;
+}
+
+void XclImpTabInfo::InsertScTab( SCTAB nScTab )
+{
+ for( auto& rEntry : maTabNames )
+ if( rEntry.second >= nScTab )
+ ++rEntry.second;
+}
+
+SCTAB XclImpTabInfo::GetScTabFromXclName( const OUString& rXclTabName ) const
+{
+ XclTabNameMap::const_iterator aIt = maTabNames.find( rXclTabName );
+ return (aIt != maTabNames.end()) ? aIt->second : SCTAB_INVALID;
+}
+
+// record creation order - TABID record ---------------------------------------
+
+void XclImpTabInfo::ReadTabid( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() == EXC_BIFF8 );
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ rStrm.EnableDecryption();
+ std::size_t nReadCount = rStrm.GetRecLeft() / 2;
+ OSL_ENSURE( nReadCount <= 0xFFFF, "XclImpTabInfo::ReadTabid - record too long" );
+ maTabIdVec.clear();
+ maTabIdVec.reserve( nReadCount );
+ for( std::size_t nIndex = 0; rStrm.IsValid() && (nIndex < nReadCount); ++nIndex )
+ // zero index is not allowed in BIFF8, but it seems that it occurs in real life
+ maTabIdVec.push_back( rStrm.ReaduInt16() );
+ }
+}
+
+sal_uInt16 XclImpTabInfo::GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMaxTabId ) const
+{
+ sal_uInt16 nReturn = 0;
+ for( sal_uInt16 nValue : maTabIdVec )
+ {
+ if( nValue == nCreatedId )
+ return nReturn;
+ if( nValue <= nMaxTabId )
+ ++nReturn;
+ }
+ return 0;
+}
+
+// External names =============================================================
+
+XclImpExtName::MOper::MOper(svl::SharedStringPool& rPool, XclImpStream& rStrm) :
+ mxCached(new ScMatrix(0,0))
+{
+ SCSIZE nLastCol = rStrm.ReaduInt8();
+ SCSIZE nLastRow = rStrm.ReaduInt16();
+
+ //assuming worst case scenario of nOp + one byte unistring len
+ const size_t nMinRecordSize = 2;
+ const size_t nMaxRows = rStrm.GetRecLeft() / (nMinRecordSize * (nLastCol+1));
+ if (nLastRow >= nMaxRows)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRows <<
+ " max possible rows, but " << nLastRow << " index claimed, truncating");
+ if (nMaxRows > 0)
+ nLastRow = nMaxRows-1;
+ else
+ return;
+ }
+
+ mxCached->Resize(nLastCol+1, nLastRow+1);
+ for (SCSIZE nRow = 0; nRow <= nLastRow; ++nRow)
+ {
+ for (SCSIZE nCol = 0; nCol <= nLastCol; ++nCol)
+ {
+ sal_uInt8 nOp;
+ nOp = rStrm.ReaduInt8();
+ switch (nOp)
+ {
+ case 0x01:
+ {
+ double fVal = rStrm.ReadDouble();
+ mxCached->PutDouble(fVal, nCol, nRow);
+ }
+ break;
+ case 0x02:
+ {
+ OUString aStr = rStrm.ReadUniString();
+ mxCached->PutString(rPool.intern(aStr), nCol, nRow);
+ }
+ break;
+ case 0x04:
+ {
+ bool bVal = rStrm.ReaduInt8();
+ mxCached->PutBoolean(bVal, nCol, nRow);
+ rStrm.Ignore(7);
+ }
+ break;
+ case 0x10:
+ {
+ sal_uInt8 nErr = rStrm.ReaduInt8();
+ // Map the error code from xls to calc.
+ mxCached->PutError(XclTools::GetScErrorCode(nErr), nCol, nRow);
+ rStrm.Ignore(7);
+ }
+ break;
+ default:
+ rStrm.Ignore(8);
+ }
+ }
+ }
+}
+
+const ScMatrix& XclImpExtName::MOper::GetCache() const
+{
+ return *mxCached;
+}
+
+XclImpExtName::XclImpExtName( XclImpSupbook& rSupbook, XclImpStream& rStrm, XclSupbookType eSubType, ExcelToSc* pFormulaConv )
+ : mnStorageId(0)
+{
+ sal_uInt16 nFlags(0);
+ sal_uInt8 nLen(0);
+
+ nFlags = rStrm.ReaduInt16();
+ mnStorageId = rStrm.ReaduInt32();
+ nLen = rStrm.ReaduInt8();
+ maName = rStrm.ReadUniString( nLen );
+ if( ::get_flag( nFlags, EXC_EXTN_BUILTIN ) || !::get_flag( nFlags, EXC_EXTN_OLE_OR_DDE ) )
+ {
+ if( eSubType == XclSupbookType::Addin )
+ {
+ meType = xlExtAddIn;
+ maName = XclImpRoot::GetScAddInName( maName );
+ }
+ else if ( (eSubType == XclSupbookType::Eurotool) &&
+ maName.equalsIgnoreAsciiCase( "EUROCONVERT" ) )
+ meType = xlExtEuroConvert;
+ else
+ {
+ meType = xlExtName;
+ maName = ScfTools::ConvertToScDefinedName( maName );
+ }
+ }
+ else
+ {
+ meType = ::get_flagvalue( nFlags, EXC_EXTN_OLE, xlExtOLE, xlExtDDE );
+ }
+
+ switch (meType)
+ {
+ case xlExtDDE:
+ if (rStrm.GetRecLeft() > 1)
+ mxDdeMatrix.reset(new XclImpCachedMatrix(rStrm));
+ break;
+ case xlExtName:
+ // TODO: For now, only global external names are supported. In future
+ // we should extend this to supporting per-sheet external names.
+ if (mnStorageId == 0 && pFormulaConv)
+ {
+ std::unique_ptr<ScTokenArray> pArray;
+ sal_uInt16 nFmlaLen;
+ nFmlaLen = rStrm.ReaduInt16();
+ std::vector<OUString> aTabNames;
+ sal_uInt16 nCount = rSupbook.GetTabCount();
+ aTabNames.reserve(nCount);
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ aTabNames.push_back(rSupbook.GetTabName(i));
+
+ pFormulaConv->ConvertExternName(pArray, rStrm, nFmlaLen, rSupbook.GetXclUrl(), aTabNames);
+ if (pArray)
+ mxArray = std::move( pArray );
+ }
+ break;
+ case xlExtOLE:
+ mpMOper.reset( new MOper(rSupbook.GetSharedStringPool(), rStrm) );
+ break;
+ default:
+ ;
+ }
+}
+
+XclImpExtName::~XclImpExtName()
+{
+}
+
+void XclImpExtName::CreateDdeData( ScDocument& rDoc, const OUString& rApplic, const OUString& rTopic ) const
+{
+ ScMatrixRef xResults;
+ if( mxDdeMatrix )
+ xResults = mxDdeMatrix->CreateScMatrix(rDoc.GetSharedStringPool());
+ rDoc.CreateDdeLink( rApplic, rTopic, maName, SC_DDE_DEFAULT, xResults );
+}
+
+void XclImpExtName::CreateExtNameData( const ScDocument& rDoc, sal_uInt16 nFileId ) const
+{
+ if (!mxArray)
+ return;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ pRefMgr->storeRangeNameTokens(nFileId, maName, *mxArray);
+}
+
+namespace {
+
+/**
+ * Decompose the name into sheet name and range name. An OLE link name is
+ * always formatted like this [ !Sheet1!R1C1:R5C2 ] and it always uses R1C1
+ * notation.
+ */
+bool extractSheetAndRange(const OUString& rName, OUString& rSheet, OUString& rRange)
+{
+ sal_Int32 n = rName.getLength();
+ const sal_Unicode* p = rName.getStr();
+ OUStringBuffer aBuf;
+ bool bInSheet = true;
+ for (sal_Int32 i = 0; i < n; ++i, ++p)
+ {
+ if (i == 0)
+ {
+ // first character must be '!'.
+ if (*p != '!')
+ return false;
+ continue;
+ }
+
+ if (*p == '!')
+ {
+ // sheet name to range separator.
+ if (!bInSheet)
+ return false;
+ rSheet = aBuf.makeStringAndClear();
+ bInSheet = false;
+ continue;
+ }
+
+ aBuf.append(*p);
+ }
+
+ rRange = aBuf.makeStringAndClear();
+ return true;
+}
+
+}
+
+bool XclImpExtName::CreateOleData(const ScDocument& rDoc, const OUString& rUrl,
+ sal_uInt16& rFileId, OUString& rTabName, ScRange& rRange) const
+{
+ if (!mpMOper)
+ return false;
+
+ OUString aSheet, aRangeStr;
+ if (!extractSheetAndRange(maName, aSheet, aRangeStr))
+ return false;
+
+ ScRange aRange;
+ ScRefFlags nRes = aRange.ParseAny(aRangeStr, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
+ if ((nRes & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ return false;
+
+ if (aRange.aStart.Tab() != aRange.aEnd.Tab())
+ // We don't support multi-sheet range for this.
+ return false;
+
+ const ScMatrix& rCache = mpMOper->GetCache();
+ SCSIZE nC, nR;
+ rCache.GetDimensions(nC, nR);
+ if (!nC || !nR)
+ // cache matrix is empty.
+ return false;
+
+ ScExternalRefManager* pRefMgr = rDoc.GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(rUrl);
+ ScExternalRefCache::TableTypeRef xTab = pRefMgr->getCacheTable(nFileId, aSheet, true);
+ if (!xTab)
+ // cache table creation failed.
+ return false;
+
+ xTab->setWholeTableCached();
+ for (SCSIZE i = 0; i < nR; ++i)
+ {
+ for (SCSIZE j = 0; j < nC; ++j)
+ {
+ SCCOL nCol = aRange.aStart.Col() + j;
+ SCROW nRow = aRange.aStart.Row() + i;
+
+ ScMatrixValue aVal = rCache.Get(j, i);
+ switch (aVal.nType)
+ {
+ case ScMatValType::Boolean:
+ {
+ bool b = aVal.GetBoolean();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ case ScMatValType::Value:
+ {
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(aVal.fVal));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ case ScMatValType::String:
+ {
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken(aVal.GetString()));
+ xTab->setCell(nCol, nRow, pToken, 0, false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ rFileId = nFileId;
+ rTabName = aSheet;
+ rRange = aRange;
+ return true;
+}
+
+bool XclImpExtName::HasFormulaTokens() const
+{
+ return bool(mxArray);
+}
+
+// Cached external cells ======================================================
+
+XclImpCrn::XclImpCrn( XclImpStream& rStrm, const XclAddress& rXclPos ) :
+ XclImpCachedValue( rStrm ),
+ maXclPos( rXclPos )
+{
+}
+
+// Sheet in an external document ==============================================
+
+XclImpSupbookTab::XclImpSupbookTab( const OUString& rTabName ) :
+ maTabName( rTabName )
+{
+}
+
+void XclImpSupbookTab::ReadCrn( XclImpStream& rStrm, const XclAddress& rXclPos )
+{
+ XclImpCrnRef crnRef = std::make_shared<XclImpCrn>(rStrm, rXclPos);
+ maCrnList.push_back( crnRef );
+}
+
+void XclImpSupbookTab::LoadCachedValues( const ScExternalRefCache::TableTypeRef& pCacheTable,
+ svl::SharedStringPool& rPool )
+{
+ if (maCrnList.empty())
+ return;
+
+ for (const auto& rxCrn : maCrnList)
+ {
+ const XclImpCrn* const pCrn = rxCrn.get();
+ const XclAddress& rAddr = pCrn->GetAddress();
+ switch (pCrn->GetType())
+ {
+ case EXC_CACHEDVAL_BOOL:
+ {
+ bool b = pCrn->GetBool();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(b ? 1.0 : 0.0));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_DOUBLE:
+ {
+ double f = pCrn->GetValue();
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(f));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_ERROR:
+ {
+ double fError = XclTools::ErrorToDouble( pCrn->GetXclError() );
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaDoubleToken(fError));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ case EXC_CACHEDVAL_STRING:
+ {
+ svl::SharedString aSS( rPool.intern( pCrn->GetString()));
+ ScExternalRefCache::TokenRef pToken(new formula::FormulaStringToken( aSS));
+ pCacheTable->setCell(rAddr.mnCol, rAddr.mnRow, pToken, 0, false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+// External document (SUPBOOK) ================================================
+
+XclImpSupbook::XclImpSupbook( XclImpStream& rStrm ) :
+ XclImpRoot( rStrm.GetRoot() ),
+ meType( XclSupbookType::Unknown ),
+ mnSBTab( EXC_TAB_DELETED )
+{
+ sal_uInt16 nSBTabCnt;
+ nSBTabCnt = rStrm.ReaduInt16();
+
+ if( rStrm.GetRecLeft() == 2 )
+ {
+ switch( rStrm.ReaduInt16() )
+ {
+ case EXC_SUPB_SELF: meType = XclSupbookType::Self; break;
+ case EXC_SUPB_ADDIN: meType = XclSupbookType::Addin; break;
+ default: OSL_FAIL( "XclImpSupbook::XclImpSupbook - unknown special SUPBOOK type" );
+ }
+ return;
+ }
+
+ OUString aEncUrl( rStrm.ReadUniString() );
+ bool bSelf = false;
+ XclImpUrlHelper::DecodeUrl( maXclUrl, bSelf, GetRoot(), aEncUrl );
+
+ if( maXclUrl.equalsIgnoreAsciiCase( "\010EUROTOOL.XLA" ) )
+ {
+ meType = XclSupbookType::Eurotool;
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( maXclUrl ) );
+ }
+ else if( nSBTabCnt )
+ {
+ meType = XclSupbookType::Extern;
+
+ //assuming all empty strings with just len header of 0
+ const size_t nMinRecordSize = sizeof(sal_Int16);
+ const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize;
+ if (nSBTabCnt > nMaxRecords)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nSBTabCnt << " claimed, truncating");
+ nSBTabCnt = nMaxRecords;
+ }
+
+ for( sal_uInt16 nSBTab = 0; nSBTab < nSBTabCnt; ++nSBTab )
+ {
+ OUString aTabName( rStrm.ReadUniString() );
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( aTabName ) );
+ }
+ }
+ else
+ {
+ meType = XclSupbookType::Special;
+ // create dummy list entry
+ maSupbTabList.push_back( std::make_unique<XclImpSupbookTab>( maXclUrl ) );
+ }
+}
+
+void XclImpSupbook::ReadXct( XclImpStream& rStrm )
+{
+ rStrm.Ignore( 2 );
+ mnSBTab = rStrm.ReaduInt16();
+}
+
+void XclImpSupbook::ReadCrn( XclImpStream& rStrm )
+{
+ if (mnSBTab >= maSupbTabList.size())
+ return;
+ XclImpSupbookTab& rSbTab = *maSupbTabList[mnSBTab];
+ sal_uInt8 nXclColLast, nXclColFirst;
+ sal_uInt16 nXclRow;
+ nXclColLast = rStrm.ReaduInt8();
+ nXclColFirst = rStrm.ReaduInt8();
+ nXclRow = rStrm.ReaduInt16();
+
+ for( sal_uInt8 nXclCol = nXclColFirst; (nXclCol <= nXclColLast) && (rStrm.GetRecLeft() > 1); ++nXclCol )
+ rSbTab.ReadCrn( rStrm, XclAddress( nXclCol, nXclRow ) );
+}
+
+void XclImpSupbook::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ maExtNameList.push_back( std::make_unique<XclImpExtName>( *this, rStrm, meType, pFormulaConv ) );
+}
+
+const XclImpExtName* XclImpSupbook::GetExternName( sal_uInt16 nXclIndex ) const
+{
+ if (nXclIndex == 0)
+ {
+ SAL_WARN("sc", "XclImpSupbook::GetExternName - index must be >0");
+ return nullptr;
+ }
+ if (meType == XclSupbookType::Self || nXclIndex > maExtNameList.size())
+ return nullptr;
+ return maExtNameList[nXclIndex-1].get();
+}
+
+bool XclImpSupbook::GetLinkData( OUString& rApplic, OUString& rTopic ) const
+{
+ return (meType == XclSupbookType::Special) && XclImpUrlHelper::DecodeLink( rApplic, rTopic, maXclUrl );
+}
+
+OUString XclImpSupbook::GetMacroName( sal_uInt16 nXclNameIdx ) const
+{
+ OSL_ENSURE( nXclNameIdx > 0, "XclImpSupbook::GetMacroName - index must be >0" );
+ const XclImpName* pName = (meType == XclSupbookType::Self) ? GetNameManager().GetName( nXclNameIdx ) : nullptr;
+ return (pName && pName->IsVBName()) ? pName->GetScName() : OUString();
+}
+
+OUString XclImpSupbook::GetTabName( sal_uInt16 nXtiTab ) const
+{
+ if (nXtiTab >= maSupbTabList.size())
+ return OUString();
+ return maSupbTabList[nXtiTab]->GetTabName();
+}
+
+sal_uInt16 XclImpSupbook::GetTabCount() const
+{
+ return ulimit_cast<sal_uInt16>(maSupbTabList.size());
+}
+
+void XclImpSupbook::LoadCachedValues()
+{
+ if (meType != XclSupbookType::Extern || GetExtDocOptions().GetDocSettings().mnLinkCnt > 0 || !GetDocShell())
+ return;
+
+ OUString aAbsUrl( ScGlobal::GetAbsDocName(maXclUrl, GetDocShell()) );
+
+ ScExternalRefManager* pRefMgr = GetRoot().GetDoc().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(aAbsUrl);
+
+ for (auto& rxTab : maSupbTabList)
+ {
+ const OUString& rTabName = rxTab->GetTabName();
+ ScExternalRefCache::TableTypeRef pCacheTable = pRefMgr->getCacheTable(nFileId, rTabName, true);
+ rxTab->LoadCachedValues( pCacheTable, GetSharedStringPool());
+ pCacheTable->setWholeTableCached();
+ }
+}
+
+svl::SharedStringPool& XclImpSupbook::GetSharedStringPool()
+{
+ return GetDoc().GetSharedStringPool();
+}
+
+// Import link manager ========================================================
+
+XclImpLinkManagerImpl::XclImpLinkManagerImpl( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpLinkManagerImpl::ReadExternsheet( XclImpStream& rStrm )
+{
+ sal_uInt16 nXtiCount;
+ nXtiCount = rStrm.ReaduInt16();
+ OSL_ENSURE( static_cast< std::size_t >( nXtiCount * 6 ) == rStrm.GetRecLeft(), "XclImpLinkManagerImpl::ReadExternsheet - invalid count" );
+ nXtiCount = static_cast< sal_uInt16 >( ::std::min< std::size_t >( nXtiCount, rStrm.GetRecLeft() / 6 ) );
+
+ /* #i104057# A weird external XLS generator writes multiple EXTERNSHEET
+ records instead of only one as expected. Surprisingly, Excel seems to
+ insert the entries of the second record before the entries of the first
+ record. */
+ XclImpXtiVector aNewEntries( nXtiCount );
+ for( auto& rNewEntry : aNewEntries )
+ {
+ if (!rStrm.IsValid())
+ break;
+ rStrm >> rNewEntry;
+ }
+ maXtiList.insert( maXtiList.begin(), aNewEntries.begin(), aNewEntries.end() );
+
+ LoadCachedValues();
+}
+
+void XclImpLinkManagerImpl::ReadSupbook( XclImpStream& rStrm )
+{
+ maSupbookList.push_back( std::make_unique<XclImpSupbook>( rStrm ) );
+}
+
+void XclImpLinkManagerImpl::ReadXct( XclImpStream& rStrm )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadXct( rStrm );
+}
+
+void XclImpLinkManagerImpl::ReadCrn( XclImpStream& rStrm )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadCrn( rStrm );
+}
+
+void XclImpLinkManagerImpl::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ if( !maSupbookList.empty() )
+ maSupbookList.back()->ReadExternname( rStrm, pFormulaConv );
+}
+
+bool XclImpLinkManagerImpl::IsSelfRef( sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook && (pSupbook->GetType() == XclSupbookType::Self);
+}
+
+bool XclImpLinkManagerImpl::GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const
+{
+ if( const XclImpXti* pXti = GetXti( nXtiIndex ) )
+ {
+ if (!maSupbookList.empty() && (pXti->mnSupbook < maSupbookList.size()) )
+ {
+ rnFirstScTab = pXti->mnSBTabFirst;
+ rnLastScTab = pXti->mnSBTabLast;
+ return true;
+ }
+ }
+ return false;
+}
+
+const XclImpExtName* XclImpLinkManagerImpl::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook ? pSupbook->GetExternName( nExtName ) : nullptr;
+}
+
+const OUString* XclImpLinkManagerImpl::GetSupbookUrl( sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* p = GetSupbook( nXtiIndex );
+ if (!p)
+ return nullptr;
+ return &p->GetXclUrl();
+}
+
+OUString XclImpLinkManagerImpl::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const
+{
+ const XclImpSupbook* p = GetSupbook(nXti);
+ return p ? p->GetTabName(nXtiTab) : OUString();
+}
+
+bool XclImpLinkManagerImpl::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nXtiIndex );
+ return pSupbook && pSupbook->GetLinkData( rApplic, rTopic );
+}
+
+OUString XclImpLinkManagerImpl::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const
+{
+ const XclImpSupbook* pSupbook = GetSupbook( nExtSheet );
+ return pSupbook ? pSupbook->GetMacroName( nExtName ) : OUString();
+}
+
+const XclImpXti* XclImpLinkManagerImpl::GetXti( sal_uInt16 nXtiIndex ) const
+{
+ return (nXtiIndex < maXtiList.size()) ? &maXtiList[ nXtiIndex ] : nullptr;
+}
+
+const XclImpSupbook* XclImpLinkManagerImpl::GetSupbook( sal_uInt16 nXtiIndex ) const
+{
+ if ( maSupbookList.empty() )
+ return nullptr;
+ const XclImpXti* pXti = GetXti( nXtiIndex );
+ if (!pXti || pXti->mnSupbook >= maSupbookList.size())
+ return nullptr;
+ return maSupbookList.at( pXti->mnSupbook ).get();
+}
+
+void XclImpLinkManagerImpl::LoadCachedValues()
+{
+ // Read all CRN records which can be accessed via XclImpSupbook, and store
+ // the cached values to the external reference manager.
+ for (auto& rxSupbook : maSupbookList)
+ rxSupbook->LoadCachedValues();
+}
+
+XclImpLinkManager::XclImpLinkManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mxImpl( new XclImpLinkManagerImpl( rRoot ) )
+{
+}
+
+XclImpLinkManager::~XclImpLinkManager()
+{
+}
+
+void XclImpLinkManager::ReadExternsheet( XclImpStream& rStrm )
+{
+ mxImpl->ReadExternsheet( rStrm );
+}
+
+void XclImpLinkManager::ReadSupbook( XclImpStream& rStrm )
+{
+ mxImpl->ReadSupbook( rStrm );
+}
+
+void XclImpLinkManager::ReadXct( XclImpStream& rStrm )
+{
+ mxImpl->ReadXct( rStrm );
+}
+
+void XclImpLinkManager::ReadCrn( XclImpStream& rStrm )
+{
+ mxImpl->ReadCrn( rStrm );
+}
+
+void XclImpLinkManager::ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv )
+{
+ mxImpl->ReadExternname( rStrm, pFormulaConv );
+}
+
+bool XclImpLinkManager::IsSelfRef( sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->IsSelfRef( nXtiIndex );
+}
+
+bool XclImpLinkManager::GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab, sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetScTabRange( rnFirstScTab, rnLastScTab, nXtiIndex );
+}
+
+const XclImpExtName* XclImpLinkManager::GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const
+{
+ return mxImpl->GetExternName( nXtiIndex, nExtName );
+}
+
+const OUString* XclImpLinkManager::GetSupbookUrl( sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetSupbookUrl(nXtiIndex);
+}
+
+OUString XclImpLinkManager::GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const
+{
+ return mxImpl->GetSupbookTabName(nXti, nXtiTab);
+}
+
+bool XclImpLinkManager::GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const
+{
+ return mxImpl->GetLinkData( rApplic, rTopic, nXtiIndex );
+}
+
+OUString XclImpLinkManager::GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const
+{
+ return mxImpl->GetMacroName( nExtSheet, nExtName );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiname.cxx b/sc/source/filter/excel/xiname.cxx
new file mode 100644
index 000000000..d498dfba4
--- /dev/null
+++ b/sc/source/filter/excel/xiname.cxx
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiname.hxx>
+#include <xlname.hxx>
+#include <rangenam.hxx>
+#include <xistream.hxx>
+#include <excform.hxx>
+#include <excimp8.hxx>
+#include <scextopt.hxx>
+#include <document.hxx>
+
+// *** Implementation ***
+
+XclImpName::TokenStrmData::TokenStrmData( XclImpStream& rStrm ) :
+ mrStrm(rStrm), mnStrmPos(0), mnStrmSize(0) {}
+
+XclImpName::XclImpName( XclImpStream& rStrm, sal_uInt16 nXclNameIdx ) :
+ XclImpRoot( rStrm.GetRoot() ),
+ mpScData( nullptr ),
+ mnScTab( SCTAB_MAX ),
+ meNameType( ScRangeData::Type::Name ),
+ mnXclTab( EXC_NAME_GLOBAL ),
+ mnNameIndex( nXclNameIdx ),
+ mbVBName( false ),
+ mbMacro( false )
+{
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+
+ // 1) *** read data from stream *** ---------------------------------------
+
+ sal_uInt16 nFlags = 0, nFmlaSize = 0, nExtSheet = EXC_NAME_GLOBAL;
+ sal_uInt8 nNameLen = 0;
+ sal_Unicode cBuiltIn(EXC_BUILTIN_UNKNOWN); /// Excel built-in name index.
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ {
+ sal_uInt8 nFlagsBiff2;
+ nFlagsBiff2 = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt8();
+ ::set_flag( nFlags, EXC_NAME_FUNC, ::get_flag( nFlagsBiff2, EXC_NAME2_FUNC ) );
+ }
+ break;
+
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ {
+ nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt16();
+ }
+ break;
+
+ case EXC_BIFF5:
+ case EXC_BIFF8:
+ {
+ nFlags = rStrm.ReaduInt16();
+ rStrm.Ignore( 1 ); //nShortCut
+ nNameLen = rStrm.ReaduInt8();
+ nFmlaSize = rStrm.ReaduInt16();
+ nExtSheet = rStrm.ReaduInt16();
+ mnXclTab = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ }
+ break;
+
+ default: DBG_ERROR_BIFF();
+ }
+
+ if( GetBiff() <= EXC_BIFF5 )
+ maXclName = rStrm.ReadRawByteString( nNameLen );
+ else
+ maXclName = rStrm.ReadUniString( nNameLen );
+
+ // 2) *** convert sheet index and name *** --------------------------------
+
+ // functions and VBA
+ bool bFunction = ::get_flag( nFlags, EXC_NAME_FUNC );
+ mbVBName = ::get_flag( nFlags, EXC_NAME_VB );
+ mbMacro = ::get_flag( nFlags, EXC_NAME_PROC );
+
+ // get built-in name, or convert characters invalid in Calc
+ bool bBuiltIn = ::get_flag( nFlags, EXC_NAME_BUILTIN );
+
+ // special case for BIFF5 filter range - name appears as plain text without built-in flag
+ if( (GetBiff() == EXC_BIFF5) && (maXclName == XclTools::GetXclBuiltInDefName(EXC_BUILTIN_FILTERDATABASE)) )
+ {
+ bBuiltIn = true;
+ maXclName = OUStringChar(EXC_BUILTIN_FILTERDATABASE);
+ }
+
+ // convert Excel name to Calc name
+ if( mbVBName )
+ {
+ // VB macro name
+ maScName = maXclName;
+ }
+ else if( bBuiltIn )
+ {
+ // built-in name
+ if( !maXclName.isEmpty() )
+ cBuiltIn = maXclName[0];
+ if( cBuiltIn == '?' ) // NUL character is imported as '?'
+ cBuiltIn = '\0';
+ maScName = XclTools::GetBuiltInDefName( cBuiltIn );
+ }
+ else
+ {
+ // any other name
+ maScName = ScfTools::ConvertToScDefinedName( maXclName );
+ }
+
+ // add index for local names
+ if( mnXclTab != EXC_NAME_GLOBAL )
+ {
+ sal_uInt16 nUsedTab = (GetBiff() == EXC_BIFF8) ? mnXclTab : nExtSheet;
+ // TODO: may not work for BIFF5, handle skipped sheets (all BIFF)
+ mnScTab = static_cast< SCTAB >( nUsedTab - 1 );
+ }
+
+ // 3) *** convert the name definition formula *** -------------------------
+
+ rFmlaConv.Reset();
+ std::unique_ptr<ScTokenArray> pTokArr;
+
+ if( ::get_flag( nFlags, EXC_NAME_BIG ) )
+ {
+ // special, unsupported name
+ pTokArr = rFmlaConv.GetDummy();
+ }
+ else if( bBuiltIn )
+ {
+ SCTAB const nLocalTab = (mnXclTab == EXC_NAME_GLOBAL) ? SCTAB_MAX : (mnXclTab - 1);
+
+ // --- print ranges or title ranges ---
+ rStrm.PushPosition();
+ switch( cBuiltIn )
+ {
+ case EXC_BUILTIN_PRINTAREA:
+ if( rFmlaConv.Convert( GetPrintAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvErr::OK )
+ meNameType |= ScRangeData::Type::PrintArea;
+ break;
+ case EXC_BUILTIN_PRINTTITLES:
+ if( rFmlaConv.Convert( GetTitleAreaBuffer(), rStrm, nFmlaSize, nLocalTab, FT_RangeName ) == ConvErr::OK )
+ meNameType |= ScRangeData::Type::ColHeader | ScRangeData::Type::RowHeader;
+ break;
+ }
+ rStrm.PopPosition();
+
+ // --- name formula ---
+ // JEG : double check this. It is clearly false for normal names
+ // but some of the builtins (sheettitle?) might be able to handle arrays
+ rFmlaConv.Convert( pTokArr, rStrm, nFmlaSize, false, FT_RangeName );
+
+ // --- auto or advanced filter ---
+ if ((GetBiff() == EXC_BIFF8) && pTokArr)
+ {
+ ScRange aRange;
+ if (pTokArr->IsReference(aRange, ScAddress()))
+ {
+ switch( cBuiltIn )
+ {
+ case EXC_BUILTIN_FILTERDATABASE:
+ GetFilterManager().Insert( &GetOldRoot(), aRange);
+ break;
+ case EXC_BUILTIN_CRITERIA:
+ GetFilterManager().AddAdvancedRange( aRange );
+ meNameType |= ScRangeData::Type::Criteria;
+ break;
+ case EXC_BUILTIN_EXTRACT:
+ if (pTokArr->IsValidReference(aRange, ScAddress()))
+ GetFilterManager().AddExtractPos( aRange );
+ break;
+ }
+ }
+ }
+ }
+ else if( nFmlaSize > 0 )
+ {
+ // Regular defined name. We need to convert the tokens after all the
+ // names have been registered (for cross-referenced names).
+ mpTokensData.reset(new TokenStrmData(rStrm));
+ mpTokensData->mnStrmPos = rStrm.GetSvStreamPos();
+ rStrm.StorePosition(mpTokensData->maStrmPos);
+ mpTokensData->mnStrmSize = nFmlaSize;
+ }
+
+ if (pTokArr && !bFunction && !mbVBName)
+ InsertName(pTokArr.get());
+}
+
+void XclImpName::ConvertTokens()
+{
+ if (!mpTokensData)
+ return;
+
+ ExcelToSc& rFmlaConv = GetOldFmlaConverter();
+ rFmlaConv.Reset();
+ std::unique_ptr<ScTokenArray> pArray;
+
+ XclImpStreamPos aOldPos;
+ XclImpStream& rStrm = mpTokensData->mrStrm;
+ rStrm.StorePosition(aOldPos);
+ rStrm.RestorePosition(mpTokensData->maStrmPos);
+ rFmlaConv.Convert(pArray, rStrm, mpTokensData->mnStrmSize, true, FT_RangeName);
+ rStrm.RestorePosition(aOldPos);
+
+ if (pArray)
+ InsertName(pArray.get());
+
+ mpTokensData.reset();
+}
+
+void XclImpName::InsertName(const ScTokenArray* pArray)
+{
+ // create the Calc name data
+ ScRangeData* pData = new ScRangeData(GetDoc(), maScName, *pArray, ScAddress(), meNameType);
+ pData->GuessPosition(); // calculate base position for relative refs
+ pData->SetIndex( mnNameIndex ); // used as unique identifier in formulas
+ if (mnXclTab == EXC_NAME_GLOBAL)
+ {
+ if (!GetDoc().GetRangeName()->insert(pData))
+ pData = nullptr;
+ }
+ else
+ {
+ ScRangeName* pLocalNames = GetDoc().GetRangeName(mnScTab);
+ if (pLocalNames)
+ {
+ if (!pLocalNames->insert(pData))
+ pData = nullptr;
+ }
+ else
+ {
+ delete pData;
+ pData = nullptr;
+ }
+
+ if (GetBiff() == EXC_BIFF8 && pData)
+ {
+ ScRange aRange;
+ // discard deleted ranges ( for the moment at least )
+ if ( pData->IsValidReference( aRange ) )
+ {
+ GetExtDocOptions().GetOrCreateTabSettings( mnXclTab );
+ }
+ }
+ }
+ if (pData)
+ {
+ GetDoc().CheckLinkFormulaNeedingCheck( *pData->GetCode());
+ mpScData = pData; // cache for later use
+ }
+}
+
+XclImpNameManager::XclImpNameManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpNameManager::ReadName( XclImpStream& rStrm )
+{
+ size_t nCount = maNameList.size();
+ if( nCount < 0xFFFF )
+ maNameList.push_back( std::make_unique<XclImpName>( rStrm, static_cast< sal_uInt16 >( nCount + 1 ) ) );
+}
+
+const XclImpName* XclImpNameManager::FindName( std::u16string_view rXclName, SCTAB nScTab ) const
+{
+ const XclImpName* pGlobalName = nullptr; // a found global name
+ const XclImpName* pLocalName = nullptr; // a found local name
+ for( const auto& rxName : maNameList )
+ {
+ if( rxName->GetXclName() == rXclName )
+ {
+ if( rxName->GetScTab() == nScTab )
+ pLocalName = rxName.get();
+ else if( rxName->IsGlobal() )
+ pGlobalName = rxName.get();
+ }
+
+ if (pLocalName)
+ break;
+ }
+ return pLocalName ? pLocalName : pGlobalName;
+}
+
+const XclImpName* XclImpNameManager::GetName( sal_uInt16 nXclNameIdx ) const
+{
+ OSL_ENSURE( nXclNameIdx > 0, "XclImpNameManager::GetName - index must be >0" );
+ return ( nXclNameIdx <= 0 || nXclNameIdx > maNameList.size() ) ? nullptr : maNameList.at( nXclNameIdx - 1 ).get();
+}
+
+void XclImpNameManager::ConvertAllTokens()
+{
+ for (auto& rxName : maNameList)
+ rxName->ConvertTokens();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xipage.cxx b/sc/source/filter/excel/xipage.cxx
new file mode 100644
index 000000000..c06308ba7
--- /dev/null
+++ b/sc/source/filter/excel/xipage.cxx
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xipage.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/graph.hxx>
+#include <scitems.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <document.hxx>
+#include <stlsheet.hxx>
+#include <attrib.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xiescher.hxx>
+
+// Page settings ==============================================================
+
+XclImpPageSettings::XclImpPageSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ Initialize();
+}
+
+void XclImpPageSettings::Initialize()
+{
+ maData.SetDefaults();
+ mbValidPaper = false;
+}
+
+void XclImpPageSettings::ReadSetup( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF4 );
+ if( GetBiff() < EXC_BIFF4 )
+ return;
+
+ // BIFF4 - BIFF8
+ sal_uInt16 nFlags;
+ maData.mnPaperSize = rStrm.ReaduInt16();
+ maData.mnScaling = rStrm.ReaduInt16();
+ maData.mnStartPage = rStrm.ReaduInt16();
+ maData.mnFitToWidth = rStrm.ReaduInt16();
+ maData.mnFitToHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ mbValidPaper = maData.mbValid = !::get_flag( nFlags, EXC_SETUP_INVALID );
+ maData.mbPrintInRows = ::get_flag( nFlags, EXC_SETUP_INROWS );
+ maData.mbPortrait = ::get_flag( nFlags, EXC_SETUP_PORTRAIT );
+ maData.mbBlackWhite = ::get_flag( nFlags, EXC_SETUP_BLACKWHITE );
+ maData.mbManualStart = true;
+
+ // new in BIFF5 - BIFF8
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ maData.mnHorPrintRes = rStrm.ReaduInt16();
+ maData.mnVerPrintRes = rStrm.ReaduInt16();
+ maData.mfHeaderMargin = rStrm.ReadDouble();
+ maData.mfFooterMargin = rStrm.ReadDouble();
+ maData.mnCopies = rStrm.ReaduInt16();
+
+ maData.mbDraftQuality = ::get_flag( nFlags, EXC_SETUP_DRAFT );
+ maData.mbPrintNotes = ::get_flag( nFlags, EXC_SETUP_PRINTNOTES );
+ maData.mbManualStart = ::get_flag( nFlags, EXC_SETUP_STARTPAGE );
+ }
+}
+
+void XclImpPageSettings::ReadMargin( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_LEFTMARGIN: maData.mfLeftMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_RIGHTMARGIN: maData.mfRightMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_TOPMARGIN: maData.mfTopMargin = rStrm.ReadDouble(); break;
+ case EXC_ID_BOTTOMMARGIN: maData.mfBottomMargin = rStrm.ReadDouble(); break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadMargin - unknown record" );
+ }
+}
+
+void XclImpPageSettings::ReadCenter( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF3 ); // read it anyway
+ bool bCenter = (rStrm.ReaduInt16() != 0);
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HCENTER: maData.mbHorCenter = bCenter; break;
+ case EXC_ID_VCENTER: maData.mbVerCenter = bCenter; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadCenter - unknown record" );
+ }
+}
+
+void XclImpPageSettings::ReadHeaderFooter( XclImpStream& rStrm )
+{
+ OUString aString;
+ if( rStrm.GetRecLeft() )
+ aString = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString();
+
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HEADER: maData.maHeader = aString; break;
+ case EXC_ID_FOOTER: maData.maFooter = aString; break;
+ case EXC_ID_HEADER_EVEN: maData.maHeaderEven = aString; break;
+ case EXC_ID_FOOTER_EVEN: maData.maFooterEven = aString; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadHeaderFooter - unknown record" );
+ }
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ if (maData.maHeader.getLength() > 10)
+ maData.maHeader = maData.maHeader.copy(0, 10);
+ if (maData.maFooter.getLength() > 10)
+ maData.maFooter = maData.maFooter.copy(0, 10);
+ if (maData.maHeaderEven.getLength() > 10)
+ maData.maHeaderEven = maData.maHeaderEven.copy(0, 10);
+ if (maData.maFooterEven.getLength() > 10)
+ maData.maFooterEven = maData.maFooterEven.copy(0, 10);
+ }
+}
+
+void XclImpPageSettings::ReadPageBreaks( XclImpStream& rStrm )
+{
+ ScfUInt16Vec* pVec = nullptr;
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_HORPAGEBREAKS: pVec = &maData.maHorPageBreaks; break;
+ case EXC_ID_VERPAGEBREAKS: pVec = &maData.maVerPageBreaks; break;
+ default: OSL_FAIL( "XclImpPageSettings::ReadPageBreaks - unknown record" );
+ }
+
+ if( !pVec )
+ return;
+
+ bool bIgnore = GetBiff() == EXC_BIFF8; // ignore start/end columns or rows in BIFF8
+
+ sal_uInt16 nCount, nBreak;
+ nCount = rStrm.ReaduInt16();
+ pVec->clear();
+ pVec->reserve( nCount );
+
+ while( nCount-- )
+ {
+ nBreak = rStrm.ReaduInt16();
+ if( nBreak )
+ pVec->push_back( nBreak );
+ if( bIgnore )
+ rStrm.Ignore( 4 );
+ }
+}
+
+void XclImpPageSettings::ReadPrintHeaders( XclImpStream& rStrm )
+{
+ maData.mbPrintHeadings = (rStrm.ReaduInt16() != 0);
+}
+
+void XclImpPageSettings::ReadPrintGridLines( XclImpStream& rStrm )
+{
+ maData.mbPrintGrid = (rStrm.ReaduInt16() != 0);
+}
+
+void XclImpPageSettings::ReadImgData( XclImpStream& rStrm )
+{
+ Graphic aGraphic = XclImpDrawing::ReadImgData( GetRoot(), rStrm );
+ if( aGraphic.GetType() != GraphicType::NONE )
+ maData.mxBrushItem.reset( new SvxBrushItem( aGraphic, GPOS_TILED, ATTR_BACKGROUND ) );
+}
+
+void XclImpPageSettings::SetPaperSize( sal_uInt16 nXclPaperSize, bool bPortrait )
+{
+ maData.mnPaperSize = nXclPaperSize;
+ maData.mbPortrait = bPortrait;
+ mbValidPaper = true;
+}
+
+namespace {
+
+void lclPutMarginItem( SfxItemSet& rItemSet, sal_uInt16 nRecId, double fMarginInch )
+{
+ sal_uInt16 nMarginTwips = XclTools::GetTwipsFromInch( fMarginInch );
+ switch( nRecId )
+ {
+ case EXC_ID_TOPMARGIN:
+ case EXC_ID_BOTTOMMARGIN:
+ {
+ SvxULSpaceItem aItem( rItemSet.Get( ATTR_ULSPACE ) );
+ if( nRecId == EXC_ID_TOPMARGIN )
+ aItem.SetUpperValue( nMarginTwips );
+ else
+ aItem.SetLowerValue( nMarginTwips );
+ rItemSet.Put( aItem );
+ }
+ break;
+ case EXC_ID_LEFTMARGIN:
+ case EXC_ID_RIGHTMARGIN:
+ {
+ SvxLRSpaceItem aItem( rItemSet.Get( ATTR_LRSPACE ) );
+ if( nRecId == EXC_ID_LEFTMARGIN )
+ aItem.SetLeftValue( nMarginTwips );
+ else
+ aItem.SetRightValue( nMarginTwips );
+ rItemSet.Put( aItem );
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpPageSettings::SetMarginItem - unknown record id" );
+ }
+}
+
+} // namespace
+
+void XclImpPageSettings::Finalize()
+{
+ ScDocument& rDoc = GetDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // *** create page style sheet ***
+
+ OUString aStyleName;
+ OUString aTableName;
+ if( GetDoc().GetName( nScTab, aTableName ) )
+ aStyleName = "PageStyle_" + aTableName;
+ else
+ aStyleName = "PageStyle_" + OUString::number(static_cast<sal_Int32>(nScTab+1));
+
+ ScStyleSheet& rStyleSheet = ScfTools::MakePageStyleSheet(
+ GetStyleSheetPool(), aStyleName, false);
+
+ SfxItemSet& rItemSet = rStyleSheet.GetItemSet();
+
+ // *** page settings ***
+
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_TOPDOWN, !maData.mbPrintInRows ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_HORCENTER, maData.mbHorCenter ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_VERCENTER, maData.mbVerCenter ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_HEADERS, maData.mbPrintHeadings ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_GRID, maData.mbPrintGrid ), true );
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_PAGE_NOTES, maData.mbPrintNotes ), true );
+
+ sal_uInt16 nStartPage = maData.mbManualStart ? maData.mnStartPage : 0;
+ ScfTools::PutItem( rItemSet, SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, nStartPage ), true );
+
+ if( maData.mxBrushItem )
+ rItemSet.Put( *maData.mxBrushItem );
+
+ if( mbValidPaper )
+ {
+ SvxPageItem aPageItem( rItemSet.Get( ATTR_PAGE ) );
+ aPageItem.SetLandscape( !maData.mbPortrait );
+ rItemSet.Put( aPageItem );
+ ScfTools::PutItem( rItemSet, SvxSizeItem( ATTR_PAGE_SIZE, maData.GetScPaperSize() ), true );
+ }
+
+ if( maData.mbFitToPages )
+ rItemSet.Put( ScPageScaleToItem( maData.mnFitToWidth, maData.mnFitToHeight ) );
+ else if( maData.mbValid )
+ rItemSet.Put( SfxUInt16Item( ATTR_PAGE_SCALE, maData.mnScaling ) );
+
+ // *** margin preparations ***
+
+ double fLeftMargin = maData.mfLeftMargin;
+ double fRightMargin = maData.mfRightMargin;
+ double fTopMargin = maData.mfTopMargin;
+ double fBottomMargin = maData.mfBottomMargin;
+ // distances between header/footer and page area
+ double fHeaderHeight = 0.0;
+ double fHeaderDist = 0.0;
+ double fFooterHeight = 0.0;
+ double fFooterDist = 0.0;
+ // in Calc, "header/footer left/right margin" is X distance between header/footer and page margin
+ double fHdrLeftMargin = maData.mfHdrLeftMargin - maData.mfLeftMargin;
+ double fHdrRightMargin = maData.mfHdrRightMargin - maData.mfRightMargin;
+ double fFtrLeftMargin = maData.mfFtrLeftMargin - maData.mfLeftMargin;
+ double fFtrRightMargin = maData.mfFtrRightMargin - maData.mfRightMargin;
+
+ // *** header and footer ***
+
+ XclImpHFConverter aHFConv( GetRoot() );
+
+ // header
+ bool bHasHeader = !maData.maHeader.isEmpty();
+ SvxSetItem aHdrSetItem( rItemSet.Get( ATTR_PAGE_HEADERSET ) );
+ SfxItemSet& rHdrItemSet = aHdrSetItem.GetItemSet();
+ rHdrItemSet.Put( SfxBoolItem( ATTR_PAGE_ON, bHasHeader ) );
+ if( bHasHeader )
+ {
+ aHFConv.ParseString( maData.maHeader );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERLEFT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERRIGHT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_HEADERFIRST );
+ // #i23296# In Calc, "top margin" is distance to header
+ fTopMargin = maData.mfHeaderMargin;
+ // Calc uses distance between header and sheet data area
+ fHeaderHeight = XclTools::GetInchFromTwips( aHFConv.GetTotalHeight() );
+ fHeaderDist = maData.mfTopMargin - maData.mfHeaderMargin - fHeaderHeight;
+ }
+ if( fHeaderDist < 0.0 )
+ {
+ /* #i23296# Header overlays sheet data:
+ -> set fixed header height to get correct sheet data position. */
+ ScfTools::PutItem( rHdrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, false ), true );
+ // shrink header height
+ tools::Long nHdrHeight = XclTools::GetTwipsFromInch( fHeaderHeight + fHeaderDist );
+ ScfTools::PutItem( rHdrItemSet, SvxSizeItem( ATTR_PAGE_SIZE, Size( 0, nHdrHeight ) ), true );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_BOTTOMMARGIN, 0.0 );
+ }
+ else
+ {
+ // use dynamic header height
+ ScfTools::PutItem( rHdrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, true ), true );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_BOTTOMMARGIN, fHeaderDist );
+ }
+ lclPutMarginItem( rHdrItemSet, EXC_ID_LEFTMARGIN, fHdrLeftMargin );
+ lclPutMarginItem( rHdrItemSet, EXC_ID_RIGHTMARGIN, fHdrRightMargin );
+ rItemSet.Put( aHdrSetItem );
+
+ // footer
+ bool bHasFooter = !maData.maFooter.isEmpty();
+ SvxSetItem aFtrSetItem( rItemSet.Get( ATTR_PAGE_FOOTERSET ) );
+ SfxItemSet& rFtrItemSet = aFtrSetItem.GetItemSet();
+ rFtrItemSet.Put( SfxBoolItem( ATTR_PAGE_ON, bHasFooter ) );
+ if( bHasFooter )
+ {
+ aHFConv.ParseString( maData.maFooter );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERLEFT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERRIGHT );
+ aHFConv.FillToItemSet( rItemSet, ATTR_PAGE_FOOTERFIRST );
+ // #i23296# In Calc, "bottom margin" is distance to footer
+ fBottomMargin = maData.mfFooterMargin;
+ // Calc uses distance between footer and sheet data area
+ fFooterHeight = XclTools::GetInchFromTwips( aHFConv.GetTotalHeight() );
+ fFooterDist = maData.mfBottomMargin - maData.mfFooterMargin - fFooterHeight;
+ }
+ if( fFooterDist < 0.0 )
+ {
+ /* #i23296# Footer overlays sheet data:
+ -> set fixed footer height to get correct sheet data end position. */
+ ScfTools::PutItem( rFtrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, false ), true );
+ // shrink footer height
+ tools::Long nFtrHeight = XclTools::GetTwipsFromInch( fFooterHeight + fFooterDist );
+ ScfTools::PutItem( rFtrItemSet, SvxSizeItem( ATTR_PAGE_SIZE, Size( 0, nFtrHeight ) ), true );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_TOPMARGIN, 0.0 );
+ }
+ else
+ {
+ // use dynamic footer height
+ ScfTools::PutItem( rFtrItemSet, SfxBoolItem( ATTR_PAGE_DYNAMIC, true ), true );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_TOPMARGIN, fFooterDist );
+ }
+ lclPutMarginItem( rFtrItemSet, EXC_ID_LEFTMARGIN, fFtrLeftMargin );
+ lclPutMarginItem( rFtrItemSet, EXC_ID_RIGHTMARGIN, fFtrRightMargin );
+ rItemSet.Put( aFtrSetItem );
+
+ // *** set final margins ***
+
+ lclPutMarginItem( rItemSet, EXC_ID_LEFTMARGIN, fLeftMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_RIGHTMARGIN, fRightMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_TOPMARGIN, fTopMargin );
+ lclPutMarginItem( rItemSet, EXC_ID_BOTTOMMARGIN, fBottomMargin );
+
+ // *** put style sheet into document ***
+
+ rDoc.SetPageStyle( nScTab, rStyleSheet.GetName() );
+
+ // *** page breaks ***
+
+ for( const auto& rHorPageBreak : maData.maHorPageBreaks )
+ {
+ SCROW nScRow = static_cast< SCROW >( rHorPageBreak );
+ if( nScRow <= rDoc.MaxRow() )
+ rDoc.SetRowBreak(nScRow, nScTab, false, true);
+ }
+
+ for( const auto& rVerPageBreak : maData.maVerPageBreaks )
+ {
+ SCCOL nScCol = static_cast< SCCOL >( rVerPageBreak );
+ if( nScCol <= rDoc.MaxCol() )
+ rDoc.SetColBreak(nScCol, nScTab, false, true);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xipivot.cxx b/sc/source/filter/excel/xipivot.cxx
new file mode 100644
index 000000000..d8d4eaa63
--- /dev/null
+++ b/sc/source/filter/excel/xipivot.cxx
@@ -0,0 +1,1737 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xipivot.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+
+#include <tools/datetime.hxx>
+#include <svl/intitem.hxx>
+#include <svl/numformat.hxx>
+#include <sal/log.hxx>
+#include <sot/storage.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <dpobject.hxx>
+#include <dpshttab.hxx>
+#include <dpoutputgeometry.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+
+#include <xltracer.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xilink.hxx>
+#include <xiescher.hxx>
+
+//TODO ExcelToSc usage
+#include <excform.hxx>
+#include <documentimport.hxx>
+
+#include <vector>
+
+using namespace com::sun::star;
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
+using ::com::sun::star::sheet::DataPilotFieldSortInfo;
+using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
+using ::com::sun::star::sheet::DataPilotFieldReference;
+using ::std::vector;
+
+// Pivot cache
+
+XclImpPCItem::XclImpPCItem( XclImpStream& rStrm )
+{
+ switch( rStrm.GetRecId() )
+ {
+ case EXC_ID_SXDOUBLE: ReadSxdouble( rStrm ); break;
+ case EXC_ID_SXBOOLEAN: ReadSxboolean( rStrm ); break;
+ case EXC_ID_SXERROR: ReadSxerror( rStrm ); break;
+ case EXC_ID_SXINTEGER: ReadSxinteger( rStrm ); break;
+ case EXC_ID_SXSTRING: ReadSxstring( rStrm ); break;
+ case EXC_ID_SXDATETIME: ReadSxdatetime( rStrm ); break;
+ case EXC_ID_SXEMPTY: ReadSxempty( rStrm ); break;
+ default: OSL_FAIL( "XclImpPCItem::XclImpPCItem - unknown record id" );
+ }
+}
+
+namespace {
+
+void lclSetValue( XclImpRoot& rRoot, const ScAddress& rScPos, double fValue, SvNumFormatType nFormatType )
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ rDoc.setNumericCell(rScPos, fValue);
+ sal_uInt32 nScNumFmt = rRoot.GetFormatter().GetStandardFormat( nFormatType, rRoot.GetDocLanguage() );
+ rDoc.getDoc().ApplyAttr(
+ rScPos.Col(), rScPos.Row(), rScPos.Tab(), SfxUInt32Item(ATTR_VALUE_FORMAT, nScNumFmt));
+}
+
+} // namespace
+
+void XclImpPCItem::WriteToSource( XclImpRoot& rRoot, const ScAddress& rScPos ) const
+{
+ ScDocumentImport& rDoc = rRoot.GetDocImport();
+ if( const OUString* pText = GetText() )
+ rDoc.setStringCell(rScPos, *pText);
+ else if( const double* pfValue = GetDouble() )
+ rDoc.setNumericCell(rScPos, *pfValue);
+ else if( const sal_Int16* pnValue = GetInteger() )
+ rDoc.setNumericCell(rScPos, *pnValue);
+ else if( const bool* pbValue = GetBool() )
+ lclSetValue( rRoot, rScPos, *pbValue ? 1.0 : 0.0, SvNumFormatType::LOGICAL );
+ else if( const DateTime* pDateTime = GetDateTime() )
+ {
+ // set number format date, time, or date/time, depending on the value
+ double fValue = rRoot.GetDoubleFromDateTime( *pDateTime );
+ double fInt = 0.0;
+ double fFrac = modf( fValue, &fInt );
+ SvNumFormatType nFormatType = ((fFrac == 0.0) && (fInt != 0.0)) ? SvNumFormatType::DATE :
+ ((fInt == 0.0) ? SvNumFormatType::TIME : SvNumFormatType::DATETIME);
+ lclSetValue( rRoot, rScPos, fValue, nFormatType );
+ }
+ else if( const sal_uInt16* pnError = GetError() )
+ {
+ double fValue;
+ sal_uInt8 nErrCode = static_cast< sal_uInt8 >( *pnError );
+ std::unique_ptr<ScTokenArray> pScTokArr = rRoot.GetOldFmlaConverter().GetBoolErr(
+ XclTools::ErrorToEnum( fValue, true, nErrCode ) );
+ ScFormulaCell* pCell = pScTokArr
+ ? new ScFormulaCell(rDoc.getDoc(), rScPos, std::move(pScTokArr))
+ : new ScFormulaCell(rDoc.getDoc(), rScPos);
+ pCell->SetHybridDouble( fValue );
+ rDoc.setFormulaCell(rScPos, pCell);
+ }
+}
+
+void XclImpPCItem::ReadSxdouble( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdouble - wrong record size" );
+ SetDouble( rStrm.ReadDouble() );
+}
+
+void XclImpPCItem::ReadSxboolean( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxboolean - wrong record size" );
+ SetBool( rStrm.ReaduInt16() != 0 );
+}
+
+void XclImpPCItem::ReadSxerror( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxerror - wrong record size" );
+ SetError( rStrm.ReaduInt16() );
+}
+
+void XclImpPCItem::ReadSxinteger( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 2, "XclImpPCItem::ReadSxinteger - wrong record size" );
+ SetInteger( rStrm.ReadInt16() );
+}
+
+void XclImpPCItem::ReadSxstring( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() >= 3, "XclImpPCItem::ReadSxstring - wrong record size" );
+ SetText( rStrm.ReadUniString() );
+}
+
+void XclImpPCItem::ReadSxdatetime( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 8, "XclImpPCItem::ReadSxdatetime - wrong record size" );
+ sal_uInt16 nYear, nMonth;
+ sal_uInt8 nDay, nHour, nMin, nSec;
+ nYear = rStrm.ReaduInt16();
+ nMonth = rStrm.ReaduInt16();
+ nDay = rStrm.ReaduInt8();
+ nHour = rStrm.ReaduInt8();
+ nMin = rStrm.ReaduInt8();
+ nSec = rStrm.ReaduInt8();
+ SetDateTime( DateTime( Date( nDay, nMonth, nYear ), tools::Time( nHour, nMin, nSec ) ) );
+}
+
+void XclImpPCItem::ReadSxempty( XclImpStream& rStrm )
+{
+ OSL_ENSURE( rStrm.GetRecSize() == 0, "XclImpPCItem::ReadSxempty - wrong record size" );
+ SetEmpty();
+}
+
+XclImpPCField::XclImpPCField( const XclImpRoot& rRoot, XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx ) :
+ XclPCField( EXC_PCFIELD_UNKNOWN, nFieldIdx ),
+ XclImpRoot( rRoot ),
+ mrPCache( rPCache ),
+ mnSourceScCol( -1 ),
+ mbNumGroupInfoRead( false )
+{
+}
+
+XclImpPCField::~XclImpPCField()
+{
+}
+
+// general field/item access --------------------------------------------------
+
+const OUString& XclImpPCField::GetFieldName( const ScfStringVec& rVisNames ) const
+{
+ if( IsGroupChildField() && (mnFieldIdx < rVisNames.size()) )
+ {
+ const OUString& rVisName = rVisNames[ mnFieldIdx ];
+ if (!rVisName.isEmpty())
+ return rVisName;
+ }
+ return maFieldInfo.maName;
+}
+
+const XclImpPCField* XclImpPCField::GetGroupBaseField() const
+{
+ OSL_ENSURE( IsGroupChildField(), "XclImpPCField::GetGroupBaseField - this field type does not have a base field" );
+ return IsGroupChildField() ? mrPCache.GetField( maFieldInfo.mnGroupBase ) : nullptr;
+}
+
+const XclImpPCItem* XclImpPCField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
+}
+
+const XclImpPCItem* XclImpPCField::GetLimitItem( sal_uInt16 nItemIdx ) const
+{
+ OSL_ENSURE( nItemIdx < 3, "XclImpPCField::GetLimitItem - invalid item index" );
+ OSL_ENSURE( nItemIdx < maNumGroupItems.size(), "XclImpPCField::GetLimitItem - no item found" );
+ return (nItemIdx < maNumGroupItems.size()) ? maNumGroupItems[ nItemIdx ].get() : nullptr;
+}
+
+void XclImpPCField::WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab )
+{
+ OSL_ENSURE( HasOrigItems(), "XclImpPCField::WriteFieldNameToSource - only for standard fields" );
+ GetDocImport().setStringCell(ScAddress(nScCol, 0, nScTab), maFieldInfo.maName);
+ mnSourceScCol = nScCol;
+}
+
+void XclImpPCField::WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx )
+{
+ if( nItemIdx < maOrigItems.size() )
+ maOrigItems[ nItemIdx ]->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
+}
+
+void XclImpPCField::WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab )
+{
+ if( !maOrigItems.empty() )
+ maOrigItems.back()->WriteToSource( GetRoot(), ScAddress( mnSourceScCol, nScRow, nScTab ) );
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPCField::ReadSxfield( XclImpStream& rStrm )
+{
+ rStrm >> maFieldInfo;
+
+ /* Detect the type of this field. This is done very restrictive to detect
+ any unexpected state. */
+ meFieldType = EXC_PCFIELD_UNKNOWN;
+
+ bool bItems = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS );
+ bool bPostp = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
+ bool bCalced = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_CALCED );
+ bool bChild = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+ bool bNum = ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP );
+
+ sal_uInt16 nVisC = maFieldInfo.mnVisItems;
+ sal_uInt16 nGroupC = maFieldInfo.mnGroupItems;
+ sal_uInt16 nBaseC = maFieldInfo.mnBaseItems;
+ sal_uInt16 nOrigC = maFieldInfo.mnOrigItems;
+ OSL_ENSURE( nVisC > 0, "XclImpPCField::ReadSxfield - field without visible items" );
+
+ sal_uInt16 nType = maFieldInfo.mnFlags & EXC_SXFIELD_DATA_MASK;
+ bool bType =
+ (nType == EXC_SXFIELD_DATA_STR) ||
+ (nType == EXC_SXFIELD_DATA_INT) ||
+ (nType == EXC_SXFIELD_DATA_DBL) ||
+ (nType == EXC_SXFIELD_DATA_STR_INT) ||
+ (nType == EXC_SXFIELD_DATA_STR_DBL) ||
+ (nType == EXC_SXFIELD_DATA_DATE) ||
+ (nType == EXC_SXFIELD_DATA_DATE_EMP) ||
+ (nType == EXC_SXFIELD_DATA_DATE_NUM) ||
+ (nType == EXC_SXFIELD_DATA_DATE_STR);
+ bool bTypeNone =
+ (nType == EXC_SXFIELD_DATA_NONE);
+ // for now, ignore data type of calculated fields
+ OSL_ENSURE( bCalced || bType || bTypeNone, "XclImpPCField::ReadSxfield - unknown item data type" );
+
+ if( !(nVisC > 0 || bPostp) )
+ return;
+
+ if( bItems && !bPostp )
+ {
+ if( !bCalced )
+ {
+ // 1) standard fields and standard grouping fields
+ if( !bNum )
+ {
+ // 1a) standard field without grouping
+ if( bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == nVisC) )
+ meFieldType = EXC_PCFIELD_STANDARD;
+
+ // 1b) standard grouping field
+ else if( bTypeNone && (nGroupC == nVisC) && (nBaseC > 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_STDGROUP;
+ }
+ // 2) numerical grouping fields
+ else if( (nGroupC == nVisC) && (nBaseC == 0) )
+ {
+ // 2a) single num/date grouping field without child grouping field
+ if( !bChild && bType && (nOrigC > 0) )
+ {
+ switch( nType )
+ {
+ case EXC_SXFIELD_DATA_INT:
+ case EXC_SXFIELD_DATA_DBL: meFieldType = EXC_PCFIELD_NUMGROUP; break;
+ case EXC_SXFIELD_DATA_DATE: meFieldType = EXC_PCFIELD_DATEGROUP; break;
+ default: OSL_FAIL( "XclImpPCField::ReadSxfield - numeric group with wrong data type" );
+ }
+ }
+
+ // 2b) first date grouping field with child grouping field
+ else if( bChild && (nType == EXC_SXFIELD_DATA_DATE) && (nOrigC > 0) )
+ meFieldType = EXC_PCFIELD_DATEGROUP;
+
+ // 2c) additional date grouping field
+ else if( bTypeNone && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_DATECHILD;
+ }
+ OSL_ENSURE( meFieldType != EXC_PCFIELD_UNKNOWN, "XclImpPCField::ReadSxfield - invalid standard or grouped field" );
+ }
+
+ // 3) calculated field
+ else
+ {
+ if( !bChild && !bNum && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_CALCED;
+ OSL_ENSURE( meFieldType == EXC_PCFIELD_CALCED, "XclImpPCField::ReadSxfield - invalid calculated field" );
+ }
+ }
+
+ else if( !bItems && bPostp )
+ {
+ // 4) standard field with postponed items
+ if( !bCalced && !bChild && !bNum && bType && (nGroupC == 0) && (nBaseC == 0) && (nOrigC == 0) )
+ meFieldType = EXC_PCFIELD_STANDARD;
+ OSL_ENSURE( meFieldType == EXC_PCFIELD_STANDARD, "XclImpPCField::ReadSxfield - invalid postponed field" );
+ }
+}
+
+void XclImpPCField::ReadItem( XclImpStream& rStrm )
+{
+ OSL_ENSURE( HasInlineItems() || HasPostponedItems(), "XclImpPCField::ReadItem - field does not expect items" );
+
+ // read the item
+ XclImpPCItemRef xItem = std::make_shared<XclImpPCItem>( rStrm );
+
+ // try to insert into an item list
+ if( mbNumGroupInfoRead )
+ {
+ // there are 3 items after SXNUMGROUP that contain grouping limits and step count
+ if( maNumGroupItems.size() < 3 )
+ maNumGroupItems.push_back( xItem );
+ else
+ maOrigItems.push_back( xItem );
+ }
+ else if( HasInlineItems() || HasPostponedItems() )
+ {
+ maItems.push_back( xItem );
+ // visible item is original item in standard fields
+ if( IsStandardField() )
+ maOrigItems.push_back( xItem );
+ }
+}
+
+void XclImpPCField::ReadSxnumgroup( XclImpStream& rStrm )
+{
+ OSL_ENSURE( IsNumGroupField() || IsDateGroupField(), "XclImpPCField::ReadSxnumgroup - SXNUMGROUP outside numeric grouping field" );
+ OSL_ENSURE( !mbNumGroupInfoRead, "XclImpPCField::ReadSxnumgroup - multiple SXNUMGROUP records" );
+ OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxnumgroup - SXNUMGROUP out of record order" );
+ rStrm >> maNumGroupInfo;
+ mbNumGroupInfoRead = IsNumGroupField() || IsDateGroupField();
+}
+
+void XclImpPCField::ReadSxgroupinfo( XclImpStream& rStrm )
+{
+ OSL_ENSURE( IsStdGroupField(), "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO outside grouping field" );
+ OSL_ENSURE( maGroupOrder.empty(), "XclImpPCField::ReadSxgroupinfo - multiple SXGROUPINFO records" );
+ OSL_ENSURE( maItems.size() == maFieldInfo.mnGroupItems, "XclImpPCField::ReadSxgroupinfo - SXGROUPINFO out of record order" );
+ OSL_ENSURE( (rStrm.GetRecLeft() / 2) == maFieldInfo.mnBaseItems, "XclImpPCField::ReadSxgroupinfo - wrong SXGROUPINFO size" );
+ maGroupOrder.clear();
+ size_t nSize = rStrm.GetRecLeft() / 2;
+ maGroupOrder.resize( nSize, 0 );
+ for( size_t nIdx = 0; nIdx < nSize; ++nIdx )
+ maGroupOrder[ nIdx ] = rStrm.ReaduInt16();
+}
+
+// grouping -------------------------------------------------------------------
+
+void XclImpPCField::ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ if (!GetFieldName(rVisNames).isEmpty())
+ {
+ if( IsStdGroupField() )
+ ConvertStdGroupField( rSaveData, rVisNames );
+ else if( IsNumGroupField() )
+ ConvertNumGroupField( rSaveData, rVisNames );
+ else if( IsDateGroupField() )
+ ConvertDateGroupField( rSaveData, rVisNames );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpPCField::ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ const XclImpPCField* pBaseField = GetGroupBaseField();
+ if(!pBaseField)
+ return;
+
+ const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
+ if( rBaseFieldName.isEmpty() )
+ return;
+
+ // *** create a ScDPSaveGroupItem for each own item, they collect base item names ***
+ ScDPSaveGroupItemVec aGroupItems;
+ aGroupItems.reserve( maItems.size() );
+ // initialize with own item names
+ for( const auto& rxItem : maItems )
+ aGroupItems.emplace_back( rxItem->ConvertToText() );
+
+ // *** iterate over all base items, set their names at corresponding own items ***
+ for( sal_uInt16 nItemIdx = 0, nItemCount = static_cast< sal_uInt16 >( maGroupOrder.size() ); nItemIdx < nItemCount; ++nItemIdx )
+ if( maGroupOrder[ nItemIdx ] < aGroupItems.size() )
+ if( const XclImpPCItem* pBaseItem = pBaseField->GetItem( nItemIdx ) )
+ if( const XclImpPCItem* pGroupItem = GetItem( maGroupOrder[ nItemIdx ] ) )
+ if( *pBaseItem != *pGroupItem )
+ aGroupItems[ maGroupOrder[ nItemIdx ] ].AddElement( pBaseItem->ConvertToText() );
+
+ // *** create the ScDPSaveGroupDimension object, fill with grouping info ***
+ ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
+ for( const auto& rGroupItem : aGroupItems )
+ if( !rGroupItem.IsEmpty() )
+ aGroupDim.AddGroupItem( rGroupItem );
+ rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
+}
+
+void XclImpPCField::ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ ScDPNumGroupInfo aNumInfo( GetScNumGroupInfo() );
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aNumInfo );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+}
+
+void XclImpPCField::ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const
+{
+ ScDPNumGroupInfo aDateInfo( GetScDateGroupInfo() );
+ sal_Int32 nScDateType = maNumGroupInfo.GetScDateType();
+
+ switch( meFieldType )
+ {
+ case EXC_PCFIELD_DATEGROUP:
+ {
+ if( aDateInfo.mbDateValues )
+ {
+ // special case for days only with step value - create numeric grouping
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), aDateInfo );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+ }
+ else
+ {
+ ScDPSaveNumGroupDimension aNumGroupDim( GetFieldName( rVisNames ), ScDPNumGroupInfo() );
+ aNumGroupDim.SetDateInfo( aDateInfo, nScDateType );
+ rSaveData.GetDimensionData()->AddNumGroupDimension( aNumGroupDim );
+ }
+ }
+ break;
+
+ case EXC_PCFIELD_DATECHILD:
+ {
+ if( const XclImpPCField* pBaseField = GetGroupBaseField() )
+ {
+ const OUString& rBaseFieldName = pBaseField->GetFieldName( rVisNames );
+ if( !rBaseFieldName.isEmpty() )
+ {
+ ScDPSaveGroupDimension aGroupDim( rBaseFieldName, GetFieldName( rVisNames ) );
+ aGroupDim.SetDateInfo( aDateInfo, nScDateType );
+ rSaveData.GetDimensionData()->AddGroupDimension( aGroupDim );
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "XclImpPCField::ConvertDateGroupField - unknown date field type" );
+ }
+}
+
+ScDPNumGroupInfo XclImpPCField::GetScNumGroupInfo() const
+{
+ ScDPNumGroupInfo aNumInfo;
+ aNumInfo.mbEnable = true;
+ aNumInfo.mbDateValues = false;
+ aNumInfo.mbAutoStart = true;
+ aNumInfo.mbAutoEnd = true;
+
+ if( const double* pfMinValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
+ {
+ aNumInfo.mfStart = *pfMinValue;
+ aNumInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
+ }
+ if( const double* pfMaxValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
+ {
+ aNumInfo.mfEnd = *pfMaxValue;
+ aNumInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
+ }
+ if( const double* pfStepValue = GetNumGroupLimit( EXC_SXFIELD_INDEX_STEP ) )
+ aNumInfo.mfStep = *pfStepValue;
+
+ return aNumInfo;
+}
+
+ScDPNumGroupInfo XclImpPCField::GetScDateGroupInfo() const
+{
+ ScDPNumGroupInfo aDateInfo;
+ aDateInfo.mbEnable = true;
+ aDateInfo.mbDateValues = false;
+ aDateInfo.mbAutoStart = true;
+ aDateInfo.mbAutoEnd = true;
+
+ if( const DateTime* pMinDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MIN ) )
+ {
+ aDateInfo.mfStart = GetDoubleFromDateTime( *pMinDate );
+ aDateInfo.mbAutoStart = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN );
+ }
+ if( const DateTime* pMaxDate = GetDateGroupLimit( EXC_SXFIELD_INDEX_MAX ) )
+ {
+ aDateInfo.mfEnd = GetDoubleFromDateTime( *pMaxDate );
+ aDateInfo.mbAutoEnd = ::get_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX );
+ }
+ // GetDateGroupStep() returns a value for date type "day" in single date groups only
+ if( const sal_Int16* pnStepValue = GetDateGroupStep() )
+ {
+ aDateInfo.mfStep = *pnStepValue;
+ aDateInfo.mbDateValues = true;
+ }
+
+ return aDateInfo;
+}
+
+const double* XclImpPCField::GetNumGroupLimit( sal_uInt16 nLimitIdx ) const
+{
+ OSL_ENSURE( IsNumGroupField(), "XclImpPCField::GetNumGroupLimit - only for numeric grouping fields" );
+ if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
+ {
+ OSL_ENSURE( pItem->GetDouble(), "XclImpPCField::GetNumGroupLimit - SXDOUBLE item expected" );
+ return pItem->GetDouble();
+ }
+ return nullptr;
+}
+
+const DateTime* XclImpPCField::GetDateGroupLimit( sal_uInt16 nLimitIdx ) const
+{
+ OSL_ENSURE( IsDateGroupField(), "XclImpPCField::GetDateGroupLimit - only for date grouping fields" );
+ if( const XclImpPCItem* pItem = GetLimitItem( nLimitIdx ) )
+ {
+ OSL_ENSURE( pItem->GetDateTime(), "XclImpPCField::GetDateGroupLimit - SXDATETIME item expected" );
+ return pItem->GetDateTime();
+ }
+ return nullptr;
+}
+
+const sal_Int16* XclImpPCField::GetDateGroupStep() const
+{
+ // only for single date grouping fields, not for grouping chains
+ if( !IsGroupBaseField() && !IsGroupChildField() )
+ {
+ // only days may have a step value, return 0 for all other date types
+ if( maNumGroupInfo.GetXclDataType() == EXC_SXNUMGROUP_TYPE_DAY )
+ {
+ if( const XclImpPCItem* pItem = GetLimitItem( EXC_SXFIELD_INDEX_STEP ) )
+ {
+ OSL_ENSURE( pItem->GetInteger(), "XclImpPCField::GetDateGroupStep - SXINTEGER item expected" );
+ if( const sal_Int16* pnStep = pItem->GetInteger() )
+ {
+ OSL_ENSURE( *pnStep > 0, "XclImpPCField::GetDateGroupStep - invalid step count" );
+ // return nothing for step count 1 - this is also a standard date group in Excel
+ return (*pnStep > 1) ? pnStep : nullptr;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+XclImpPivotCache::XclImpPivotCache( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maSrcRange( ScAddress::INITIALIZE_INVALID ),
+ mnStrmId( 0 ),
+ mnSrcType( EXC_SXVS_UNKNOWN ),
+ mbSelfRef( false )
+{
+}
+
+XclImpPivotCache::~XclImpPivotCache()
+{
+}
+
+// data access ----------------------------------------------------------------
+
+const XclImpPCField* XclImpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPivotCache::ReadSxidstm( XclImpStream& rStrm )
+{
+ mnStrmId = rStrm.ReaduInt16();
+}
+
+void XclImpPivotCache::ReadSxvs( XclImpStream& rStrm )
+{
+ mnSrcType = rStrm.ReaduInt16();
+ GetTracer().TracePivotDataSource( mnSrcType != EXC_SXVS_SHEET );
+}
+
+void XclImpPivotCache::ReadDconref( XclImpStream& rStrm )
+{
+ /* Read DCONREF only once (by checking maTabName), there may be other
+ DCONREF records in another context. Read reference only if a leading
+ SXVS record is present (by checking mnSrcType). */
+ if( !maTabName.isEmpty() || (mnSrcType != EXC_SXVS_SHEET) )
+ return;
+
+ XclRange aXclRange( ScAddress::UNINITIALIZED );
+ aXclRange.Read( rStrm, false );
+ OUString aEncUrl = rStrm.ReadUniString();
+
+ XclImpUrlHelper::DecodeUrl( maUrl, maTabName, mbSelfRef, GetRoot(), aEncUrl );
+
+ /* Do not convert maTabName to Calc sheet name -> original name is used to
+ find the sheet in the document. Sheet index of source range will be
+ found later in XclImpPivotCache::ReadPivotCacheStream(), because sheet
+ may not exist yet. */
+ if( mbSelfRef )
+ GetAddressConverter().ConvertRange( maSrcRange, aXclRange, 0, 0, true );
+}
+
+void XclImpPivotCache::ReadDConName( XclImpStream& rStrm )
+{
+ maSrcRangeName = rStrm.ReadUniString();
+
+ // This 2-byte value equals the length of string that follows, or if 0 it
+ // indicates that the name has a workbook scope. For now, we only support
+ // internal defined name with a workbook scope.
+ sal_uInt16 nFlag;
+ nFlag = rStrm.ReaduInt16();
+ mbSelfRef = (nFlag == 0);
+
+ if (!mbSelfRef)
+ // External name is not supported yet.
+ maSrcRangeName.clear();
+}
+
+void XclImpPivotCache::ReadPivotCacheStream( const XclImpStream& rStrm )
+{
+ if( (mnSrcType != EXC_SXVS_SHEET) && (mnSrcType != EXC_SXVS_EXTERN) )
+ return;
+
+ ScDocument& rDoc = GetDoc();
+ SCCOL nFieldScCol = 0; // column index of source data for next field
+ SCROW nItemScRow = 0; // row index of source data for current items
+ SCTAB nScTab = 0; // sheet index of source data
+ bool bGenerateSource = false; // true = write source data from cache to dummy table
+
+ if( mbSelfRef )
+ {
+ if (maSrcRangeName.isEmpty())
+ {
+ // try to find internal sheet containing the source data
+ nScTab = GetTabInfo().GetScTabFromXclName( maTabName );
+ if( rDoc.HasTable( nScTab ) )
+ {
+ // set sheet index to source range
+ maSrcRange.aStart.SetTab( nScTab );
+ maSrcRange.aEnd.SetTab( nScTab );
+ }
+ else
+ {
+ // create dummy sheet for deleted internal sheet
+ bGenerateSource = true;
+ }
+ }
+ }
+ else
+ {
+ // create dummy sheet for external sheet
+ bGenerateSource = true;
+ }
+
+ // create dummy sheet for source data from external or deleted sheet
+ if( bGenerateSource )
+ {
+ if( rDoc.GetTableCount() >= MAXTABCOUNT )
+ // cannot create more sheets -> exit
+ return;
+
+ nScTab = rDoc.GetTableCount();
+ rDoc.MakeTable( nScTab );
+ OUStringBuffer aDummyName("DPCache");
+ if( maTabName.getLength() > 0 )
+ aDummyName.append( '_' ).append( maTabName );
+ OUString aName = aDummyName.makeStringAndClear();
+ rDoc.CreateValidTabName( aName );
+ rDoc.RenameTab( nScTab, aName );
+ // set sheet index to source range
+ maSrcRange.aStart.SetTab( nScTab );
+ maSrcRange.aEnd.SetTab( nScTab );
+ }
+
+ // open pivot cache storage stream
+ tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( mnStrmId ) );
+ if( !xSvStrm.is() )
+ return;
+
+ // create Excel record stream object
+ XclImpStream aPCStrm( *xSvStrm, GetRoot() );
+ aPCStrm.CopyDecrypterFrom( rStrm ); // pivot cache streams are encrypted
+
+ XclImpPCFieldRef xCurrField; // current field for new items
+ XclImpPCFieldVec aOrigFields; // all standard fields with inline original items
+ XclImpPCFieldVec aPostpFields; // all standard fields with postponed original items
+ size_t nPostpIdx = 0; // index to current field with postponed items
+ bool bLoop = true; // true = continue loop
+
+ while( bLoop && aPCStrm.StartNextRecord() )
+ {
+ switch( aPCStrm.GetRecId() )
+ {
+ case EXC_ID_EOF:
+ bLoop = false;
+ break;
+
+ case EXC_ID_SXDB:
+ aPCStrm >> maPCInfo;
+ break;
+
+ case EXC_ID_SXFIELD:
+ {
+ xCurrField.reset();
+ sal_uInt16 nNewFieldIdx = static_cast< sal_uInt16 >( maFields.size() );
+ if( nNewFieldIdx < EXC_PC_MAXFIELDCOUNT )
+ {
+ xCurrField = std::make_shared<XclImpPCField>( GetRoot(), *this, nNewFieldIdx );
+ maFields.push_back( xCurrField );
+ xCurrField->ReadSxfield( aPCStrm );
+ if( xCurrField->HasOrigItems() )
+ {
+ if( xCurrField->HasPostponedItems() )
+ aPostpFields.push_back( xCurrField );
+ else
+ aOrigFields.push_back( xCurrField );
+ // insert field name into generated source data, field remembers its column index
+ if( bGenerateSource && (nFieldScCol <= rDoc.MaxCol()) )
+ xCurrField->WriteFieldNameToSource( nFieldScCol++, nScTab );
+ }
+ // do not read items into invalid/postponed fields
+ if( !xCurrField->HasInlineItems() )
+ xCurrField.reset();
+ }
+ }
+ break;
+
+ case EXC_ID_SXINDEXLIST:
+ // read index list and insert all items into generated source data
+ if( bGenerateSource && (nItemScRow <= rDoc.MaxRow()) && (++nItemScRow <= rDoc.MaxRow()) )
+ {
+ for( const auto& rxOrigField : aOrigFields )
+ {
+ sal_uInt16 nItemIdx = rxOrigField->Has16BitIndexes() ? aPCStrm.ReaduInt16() : aPCStrm.ReaduInt8();
+ rxOrigField->WriteOrigItemToSource( nItemScRow, nScTab, nItemIdx );
+ }
+ }
+ xCurrField.reset();
+ break;
+
+ case EXC_ID_SXDOUBLE:
+ case EXC_ID_SXBOOLEAN:
+ case EXC_ID_SXERROR:
+ case EXC_ID_SXINTEGER:
+ case EXC_ID_SXSTRING:
+ case EXC_ID_SXDATETIME:
+ case EXC_ID_SXEMPTY:
+ if( xCurrField ) // inline items
+ {
+ xCurrField->ReadItem( aPCStrm );
+ }
+ else if( !aPostpFields.empty() ) // postponed items
+ {
+ // read postponed item
+ aPostpFields[ nPostpIdx ]->ReadItem( aPCStrm );
+ // write item to source
+ if( bGenerateSource && (nItemScRow <= rDoc.MaxRow()) )
+ {
+ // start new row, if there are only postponed fields
+ if( aOrigFields.empty() && (nPostpIdx == 0) )
+ ++nItemScRow;
+ if( nItemScRow <= rDoc.MaxRow() )
+ aPostpFields[ nPostpIdx ]->WriteLastOrigItemToSource( nItemScRow, nScTab );
+ }
+ // get index of next postponed field
+ ++nPostpIdx;
+ if( nPostpIdx >= aPostpFields.size() )
+ nPostpIdx = 0;
+ }
+ break;
+
+ case EXC_ID_SXNUMGROUP:
+ if( xCurrField )
+ xCurrField->ReadSxnumgroup( aPCStrm );
+ break;
+
+ case EXC_ID_SXGROUPINFO:
+ if( xCurrField )
+ xCurrField->ReadSxgroupinfo( aPCStrm );
+ break;
+
+ // known but ignored records
+ case EXC_ID_SXRULE:
+ case EXC_ID_SXFILT:
+ case EXC_ID_00F5:
+ case EXC_ID_SXNAME:
+ case EXC_ID_SXPAIR:
+ case EXC_ID_SXFMLA:
+ case EXC_ID_SXFORMULA:
+ case EXC_ID_SXDBEX:
+ case EXC_ID_SXFDBTYPE:
+ break;
+
+ default:
+ SAL_WARN("sc.filter", "XclImpPivotCache::ReadPivotCacheStream - unknown record 0x" << std::hex << aPCStrm.GetRecId() );
+ }
+ }
+
+ OSL_ENSURE( maPCInfo.mnTotalFields == maFields.size(),
+ "XclImpPivotCache::ReadPivotCacheStream - field count mismatch" );
+
+ if (static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_SAVEDATA))
+ {
+ SCROW nNewEnd = maSrcRange.aStart.Row() + maPCInfo.mnSrcRecs;
+ maSrcRange.aEnd.SetRow(nNewEnd);
+ }
+
+ // set source range for external source data
+ if( bGenerateSource && (nFieldScCol > 0) )
+ {
+ maSrcRange.aStart.SetCol( 0 );
+ maSrcRange.aStart.SetRow( 0 );
+ // nFieldScCol points to first unused column
+ maSrcRange.aEnd.SetCol( nFieldScCol - 1 );
+ // nItemScRow points to last used row
+ maSrcRange.aEnd.SetRow( nItemScRow );
+ }
+}
+
+bool XclImpPivotCache::IsRefreshOnLoad() const
+{
+ return static_cast<bool>(maPCInfo.mnFlags & EXC_SXDB_REFRESH_LOAD);
+}
+
+bool XclImpPivotCache::IsValid() const
+{
+ if (!maSrcRangeName.isEmpty())
+ return true;
+
+ return maSrcRange.IsValid();
+}
+
+// Pivot table
+
+XclImpPTItem::XclImpPTItem( const XclImpPCField* pCacheField ) :
+ mpCacheField( pCacheField )
+{
+}
+
+const OUString* XclImpPTItem::GetItemName() const
+{
+ if( mpCacheField )
+ if( const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx ) )
+ //TODO: use XclImpPCItem::ConvertToText(), if all conversions are available
+ return pCacheItem->IsEmpty() ? nullptr : pCacheItem->GetText();
+ return nullptr;
+}
+
+std::pair<bool, OUString> XclImpPTItem::GetItemName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot) const
+{
+ if(!mpCacheField)
+ return std::pair<bool, OUString>(false, OUString());
+
+ const XclImpPCItem* pCacheItem = mpCacheField->GetItem( maItemInfo.mnCacheIdx );
+ if(!pCacheItem)
+ return std::pair<bool, OUString>(false, OUString());
+
+ OUString sItemName;
+ if(pCacheItem->GetType() == EXC_PCITEM_TEXT || pCacheItem->GetType() == EXC_PCITEM_ERROR)
+ {
+ const OUString* pItemName = pCacheItem->GetText();
+ if(!pItemName)
+ return std::pair<bool, OUString>(false, OUString());
+ sItemName = *pItemName;
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_DOUBLE)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), *pCacheItem->GetDouble());
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_INTEGER)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(*pCacheItem->GetInteger()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_BOOL)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(*pCacheItem->GetBool()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_DATETIME)
+ {
+ sItemName = pObj->GetFormattedString(rSaveDim.GetName(), rRoot.GetDoubleFromDateTime(*pCacheItem->GetDateTime()));
+ }
+ else if (pCacheItem->GetType() == EXC_PCITEM_EMPTY)
+ {
+ // sItemName is an empty string
+ }
+ else // EXC_PCITEM_INVALID
+ return std::pair<bool, OUString>(false, OUString());
+
+ return std::pair<bool, OUString>(true, sItemName);
+}
+
+void XclImpPTItem::ReadSxvi( XclImpStream& rStrm )
+{
+ rStrm >> maItemInfo;
+}
+
+void XclImpPTItem::ConvertItem( ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot ) const
+{
+ // Find member and set properties
+ std::pair<bool, OUString> aReturnedName = GetItemName(rSaveDim, pObj, rRoot);
+ if(aReturnedName.first)
+ {
+ ScDPSaveMember* pMember = rSaveDim.GetExistingMemberByName(aReturnedName.second);
+ if(pMember)
+ {
+ pMember->SetIsVisible( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN ) );
+ pMember->SetShowDetails( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL ) );
+ if (maItemInfo.HasVisName())
+ pMember->SetLayoutName(*maItemInfo.GetVisName());
+ }
+ }
+}
+
+XclImpPTField::XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
+ mrPTable( rPTable )
+{
+ maFieldInfo.mnCacheIdx = nCacheIdx;
+}
+
+// general field/item access --------------------------------------------------
+
+const XclImpPCField* XclImpPTField::GetCacheField() const
+{
+ XclImpPivotCacheRef xPCache = mrPTable.GetPivotCache();
+ return xPCache ? xPCache->GetField( maFieldInfo.mnCacheIdx ) : nullptr;
+}
+
+OUString XclImpPTField::GetFieldName() const
+{
+ const XclImpPCField* pField = GetCacheField();
+ return pField ? pField->GetFieldName( mrPTable.GetVisFieldNames() ) : OUString();
+}
+
+OUString XclImpPTField::GetVisFieldName() const
+{
+ const OUString* pVisName = maFieldInfo.GetVisName();
+ return pVisName ? *pVisName : OUString();
+}
+
+const XclImpPTItem* XclImpPTField::GetItem( sal_uInt16 nItemIdx ) const
+{
+ return (nItemIdx < maItems.size()) ? maItems[ nItemIdx ].get() : nullptr;
+}
+
+const OUString* XclImpPTField::GetItemName( sal_uInt16 nItemIdx ) const
+{
+ const XclImpPTItem* pItem = GetItem( nItemIdx );
+ return pItem ? pItem->GetItemName() : nullptr;
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPTField::ReadSxvd( XclImpStream& rStrm )
+{
+ rStrm >> maFieldInfo;
+}
+
+void XclImpPTField::ReadSxvdex( XclImpStream& rStrm )
+{
+ rStrm >> maFieldExtInfo;
+}
+
+void XclImpPTField::ReadSxvi( XclImpStream& rStrm )
+{
+ XclImpPTItemRef xItem = std::make_shared<XclImpPTItem>( GetCacheField() );
+ maItems.push_back( xItem );
+ xItem->ReadSxvi( rStrm );
+}
+
+// row/column fields ----------------------------------------------------------
+
+void XclImpPTField::ConvertRowColField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOL, "XclImpPTField::ConvertRowColField - no row/column field" );
+ // special data orientation field?
+ if( maFieldInfo.mnCacheIdx == EXC_SXIVD_DATA )
+ rSaveData.GetDataLayoutDimension()->SetOrientation( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOL ) );
+ else
+ ConvertRCPField( rSaveData );
+}
+
+// page fields ----------------------------------------------------------------
+
+void XclImpPTField::SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo )
+{
+ maPageInfo = rPageInfo;
+}
+
+void XclImpPTField::ConvertPageField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_PAGE, "XclImpPTField::ConvertPageField - no page field" );
+ ConvertRCPField( rSaveData );
+}
+
+// hidden fields --------------------------------------------------------------
+
+void XclImpPTField::ConvertHiddenField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( (maFieldInfo.mnAxes & EXC_SXVD_AXIS_ROWCOLPAGE) == 0, "XclImpPTField::ConvertHiddenField - field not hidden" );
+ ConvertRCPField( rSaveData );
+}
+
+// data fields ----------------------------------------------------------------
+
+bool XclImpPTField::HasDataFieldInfo() const
+{
+ return !maDataInfoVector.empty();
+}
+
+void XclImpPTField::AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo )
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::AddDataFieldInfo - no data field" );
+ maDataInfoVector.push_back( rDataInfo );
+}
+
+void XclImpPTField::ConvertDataField( ScDPSaveData& rSaveData ) const
+{
+ OSL_ENSURE( maFieldInfo.mnAxes & EXC_SXVD_AXIS_DATA, "XclImpPTField::ConvertDataField - no data field" );
+ OSL_ENSURE( !maDataInfoVector.empty(), "XclImpPTField::ConvertDataField - no data field info" );
+ if (maDataInfoVector.empty())
+ return;
+
+ OUString aFieldName = GetFieldName();
+ if (aFieldName.isEmpty())
+ return;
+
+ ScDPSaveDimension* pSaveDim = rSaveData.GetNewDimensionByName(aFieldName);
+ if (!pSaveDim)
+ {
+ SAL_WARN("sc.filter","XclImpPTField::ConvertDataField - field name not found: " << aFieldName);
+ return;
+ }
+
+ auto aIt = maDataInfoVector.begin(), aEnd = maDataInfoVector.end();
+
+ ConvertDataField( *pSaveDim, *aIt );
+
+ // multiple data fields -> clone dimension
+ for( ++aIt; aIt != aEnd; ++aIt )
+ {
+ ScDPSaveDimension& rDupDim = rSaveData.DuplicateDimension( *pSaveDim );
+ ConvertDataFieldInfo( rDupDim, *aIt );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+/**
+ * Convert Excel-encoded subtotal name to a Calc-encoded one.
+ */
+static OUString lcl_convertExcelSubtotalName(const OUString& rName)
+{
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rName.getStr();
+ sal_Int32 n = rName.getLength();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ const sal_Unicode c = p[i];
+ if (c == '\\')
+ {
+ aBuf.append(c);
+ aBuf.append(c);
+ }
+ else
+ aBuf.append(c);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+void XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) const
+{
+ const OUString& rFieldName = GetFieldName();
+ if( rFieldName.isEmpty() )
+ return;
+
+ const XclImpPCField* pCacheField = GetCacheField();
+ if( !pCacheField || !pCacheField->IsSupportedField() )
+ return;
+
+ ScDPSaveDimension* pTest = rSaveData.GetNewDimensionByName(rFieldName);
+ if (!pTest)
+ return;
+
+ ScDPSaveDimension& rSaveDim = *pTest;
+
+ // orientation
+ rSaveDim.SetOrientation( maFieldInfo.GetApiOrient( EXC_SXVD_AXIS_ROWCOLPAGE ) );
+
+ // visible name
+ if (const OUString* pVisName = maFieldInfo.GetVisName())
+ if (!pVisName->isEmpty())
+ rSaveDim.SetLayoutName( *pVisName );
+
+ // subtotal function(s)
+ XclPTSubtotalVec aSubtotalVec;
+ maFieldInfo.GetSubtotals( aSubtotalVec );
+ if( !aSubtotalVec.empty() )
+ rSaveDim.SetSubTotals( std::move(aSubtotalVec) );
+
+ // sorting
+ DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.Field = mrPTable.GetDataFieldName( maFieldExtInfo.mnSortField );
+ aSortInfo.IsAscending = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC );
+ aSortInfo.Mode = maFieldExtInfo.GetApiSortMode();
+ rSaveDim.SetSortInfo( &aSortInfo );
+
+ // auto show
+ DataPilotFieldAutoShowInfo aShowInfo;
+ aShowInfo.IsEnabled = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW );
+ aShowInfo.ShowItemsMode = maFieldExtInfo.GetApiAutoShowMode();
+ aShowInfo.ItemCount = maFieldExtInfo.GetApiAutoShowCount();
+ aShowInfo.DataField = mrPTable.GetDataFieldName( maFieldExtInfo.mnShowField );
+ rSaveDim.SetAutoShowInfo( &aShowInfo );
+
+ // layout
+ DataPilotFieldLayoutInfo aLayoutInfo;
+ aLayoutInfo.LayoutMode = maFieldExtInfo.GetApiLayoutMode();
+ aLayoutInfo.AddEmptyLines = ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK );
+ rSaveDim.SetLayoutInfo( &aLayoutInfo );
+
+ // grouping info
+ pCacheField->ConvertGroupField( rSaveData, mrPTable.GetVisFieldNames() );
+
+ // custom subtotal name
+ if (maFieldExtInfo.mpFieldTotalName)
+ {
+ OUString aSubName = lcl_convertExcelSubtotalName(*maFieldExtInfo.mpFieldTotalName);
+ rSaveDim.SetSubtotalName(aSubName);
+ }
+}
+
+void XclImpPTField::ConvertFieldInfo( const ScDPSaveData& rSaveData, ScDPObject* pObj, const XclImpRoot& rRoot, bool bPageField ) const
+{
+ const OUString& rFieldName = GetFieldName();
+ if( rFieldName.isEmpty() )
+ return;
+
+ const XclImpPCField* pCacheField = GetCacheField();
+ if( !pCacheField || !pCacheField->IsSupportedField() )
+ return;
+
+ ScDPSaveDimension* pSaveDim = rSaveData.GetExistingDimensionByName(rFieldName);
+ if (!pSaveDim)
+ return;
+
+ pSaveDim->SetShowEmpty( ::get_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL ) );
+ for( const auto& rxItem : maItems )
+ rxItem->ConvertItem( *pSaveDim, pObj, rRoot );
+
+ if(bPageField && maPageInfo.mnSelItem != EXC_SXPI_ALLITEMS)
+ {
+ const XclImpPTItem* pItem = GetItem( maPageInfo.mnSelItem );
+ if(pItem)
+ {
+ std::pair<bool, OUString> aReturnedName = pItem->GetItemName(*pSaveDim, pObj, rRoot);
+ if(aReturnedName.first)
+ pSaveDim->SetCurrentPage(&aReturnedName.second);
+ }
+ }
+}
+
+void XclImpPTField::ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
+{
+ // orientation
+ rSaveDim.SetOrientation( DataPilotFieldOrientation_DATA );
+ // extended data field info
+ ConvertDataFieldInfo( rSaveDim, rDataInfo );
+}
+
+void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const
+{
+ // visible name
+ const OUString* pVisName = rDataInfo.GetVisName();
+ if (pVisName && !pVisName->isEmpty())
+ rSaveDim.SetLayoutName(*pVisName);
+
+ // aggregation function
+ rSaveDim.SetFunction( rDataInfo.GetApiAggFunc() );
+
+ // result field reference
+ sal_Int32 nRefType = rDataInfo.GetApiRefType();
+ DataPilotFieldReference aFieldRef;
+ aFieldRef.ReferenceType = nRefType;
+ const XclImpPTField* pRefField = mrPTable.GetField(rDataInfo.mnRefField);
+ if (pRefField)
+ {
+ aFieldRef.ReferenceField = pRefField->GetFieldName();
+ aFieldRef.ReferenceItemType = rDataInfo.GetApiRefItemType();
+ if (aFieldRef.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::NAMED)
+ {
+ const OUString* pRefItemName = pRefField->GetItemName(rDataInfo.mnRefItem);
+ if (pRefItemName)
+ aFieldRef.ReferenceItemName = *pRefItemName;
+ }
+ }
+
+ rSaveDim.SetReferenceValue(&aFieldRef);
+}
+
+XclImpPivotTable::XclImpPivotTable( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maDataOrientField( *this, EXC_SXIVD_DATA ),
+ mpDPObj(nullptr)
+{
+}
+
+XclImpPivotTable::~XclImpPivotTable()
+{
+}
+
+// cache/field access, misc. --------------------------------------------------
+
+sal_uInt16 XclImpPivotTable::GetFieldCount() const
+{
+ return static_cast< sal_uInt16 >( maFields.size() );
+}
+
+const XclImpPTField* XclImpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
+{
+ return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField :
+ ((nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr);
+}
+
+XclImpPTField* XclImpPivotTable::GetFieldAcc( sal_uInt16 nFieldIdx )
+{
+ // do not return maDataOrientField
+ return (nFieldIdx < maFields.size()) ? maFields[ nFieldIdx ].get() : nullptr;
+}
+
+const XclImpPTField* XclImpPivotTable::GetDataField( sal_uInt16 nDataFieldIdx ) const
+{
+ if( nDataFieldIdx < maOrigDataFields.size() )
+ return GetField( maOrigDataFields[ nDataFieldIdx ] );
+ return nullptr;
+}
+
+OUString XclImpPivotTable::GetDataFieldName( sal_uInt16 nDataFieldIdx ) const
+{
+ if( const XclImpPTField* pField = GetDataField( nDataFieldIdx ) )
+ return pField->GetFieldName();
+ return OUString();
+}
+
+// records --------------------------------------------------------------------
+
+void XclImpPivotTable::ReadSxview( XclImpStream& rStrm )
+{
+ rStrm >> maPTInfo;
+
+ GetAddressConverter().ConvertRange(
+ maOutScRange, maPTInfo.maOutXclRange, GetCurrScTab(), GetCurrScTab(), true );
+
+ mxPCache = GetPivotTableManager().GetPivotCache( maPTInfo.mnCacheIdx );
+ mxCurrField.reset();
+}
+
+void XclImpPivotTable::ReadSxvd( XclImpStream& rStrm )
+{
+ sal_uInt16 nFieldCount = GetFieldCount();
+ if( nFieldCount < EXC_PT_MAXFIELDCOUNT )
+ {
+ // cache index for the field is equal to the SXVD record index
+ mxCurrField = std::make_shared<XclImpPTField>( *this, nFieldCount );
+ maFields.push_back( mxCurrField );
+ mxCurrField->ReadSxvd( rStrm );
+ // add visible name of new field to list of visible names
+ maVisFieldNames.push_back( mxCurrField->GetVisFieldName() );
+ OSL_ENSURE( maFields.size() == maVisFieldNames.size(),
+ "XclImpPivotTable::ReadSxvd - wrong size of visible name array" );
+ }
+ else
+ mxCurrField.reset();
+}
+
+void XclImpPivotTable::ReadSxvi( XclImpStream& rStrm )
+{
+ if( mxCurrField )
+ mxCurrField->ReadSxvi( rStrm );
+}
+
+void XclImpPivotTable::ReadSxvdex( XclImpStream& rStrm )
+{
+ if( mxCurrField )
+ mxCurrField->ReadSxvdex( rStrm );
+}
+
+void XclImpPivotTable::ReadSxivd( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ // find the index vector to fill (row SXIVD doesn't exist without row fields)
+ ScfUInt16Vec* pFieldVec = nullptr;
+ if( maRowFields.empty() && (maPTInfo.mnRowFields > 0) )
+ pFieldVec = &maRowFields;
+ else if( maColFields.empty() && (maPTInfo.mnColFields > 0) )
+ pFieldVec = &maColFields;
+
+ // fill the vector from record data
+ if( !pFieldVec )
+ return;
+
+ sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 2, EXC_PT_MAXROWCOLCOUNT );
+ pFieldVec->reserve( nSize );
+ for( sal_uInt16 nIdx = 0; nIdx < nSize; ++nIdx )
+ {
+ sal_uInt16 nFieldIdx;
+ nFieldIdx = rStrm.ReaduInt16();
+ pFieldVec->push_back( nFieldIdx );
+
+ // set orientation at special data orientation field
+ if( nFieldIdx == EXC_SXIVD_DATA )
+ {
+ sal_uInt16 nAxis = (pFieldVec == &maRowFields) ? EXC_SXVD_AXIS_ROW : EXC_SXVD_AXIS_COL;
+ maDataOrientField.SetAxes( nAxis );
+ }
+ }
+}
+
+void XclImpPivotTable::ReadSxpi( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ sal_uInt16 nSize = ulimit_cast< sal_uInt16 >( rStrm.GetRecSize() / 6 );
+ for( sal_uInt16 nEntry = 0; nEntry < nSize; ++nEntry )
+ {
+ XclPTPageFieldInfo aPageInfo;
+ rStrm >> aPageInfo;
+ if( XclImpPTField* pField = GetFieldAcc( aPageInfo.mnField ) )
+ {
+ maPageFields.push_back( aPageInfo.mnField );
+ pField->SetPageFieldInfo( aPageInfo );
+ }
+ GetCurrSheetDrawing().SetSkipObj( aPageInfo.mnObjId );
+ }
+}
+
+void XclImpPivotTable::ReadSxdi( XclImpStream& rStrm )
+{
+ mxCurrField.reset();
+
+ XclPTDataFieldInfo aDataInfo;
+ rStrm >> aDataInfo;
+ if( XclImpPTField* pField = GetFieldAcc( aDataInfo.mnField ) )
+ {
+ maOrigDataFields.push_back( aDataInfo.mnField );
+ // DataPilot does not support double data fields -> add first appearance to index list only
+ if( !pField->HasDataFieldInfo() )
+ maFiltDataFields.push_back( aDataInfo.mnField );
+ pField->AddDataFieldInfo( aDataInfo );
+ }
+}
+
+void XclImpPivotTable::ReadSxex( XclImpStream& rStrm )
+{
+ rStrm >> maPTExtInfo;
+}
+
+void XclImpPivotTable::ReadSxViewEx9( XclImpStream& rStrm )
+{
+ rStrm >> maPTViewEx9Info;
+}
+
+void XclImpPivotTable::ReadSxAddl( XclImpStream& rStrm )
+{
+ rStrm >> maPTAddlInfo;
+}
+
+void XclImpPivotTable::Convert()
+{
+ if( !mxPCache || !mxPCache->IsValid() )
+ return;
+
+ if (utl::ConfigManager::IsFuzzing()) //just too slow
+ return;
+
+ ScDPSaveData aSaveData;
+
+ // *** global settings ***
+
+ aSaveData.SetRowGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND ) );
+ aSaveData.SetColumnGrand( ::get_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND ) );
+ aSaveData.SetFilterButton( false );
+ aSaveData.SetDrillDown( ::get_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN ) );
+ aSaveData.SetIgnoreEmptyRows( false );
+ aSaveData.SetRepeatIfEmpty( false );
+
+ // *** fields ***
+
+ // row fields
+ for( const auto& rRowField : maRowFields )
+ if( const XclImpPTField* pField = GetField( rRowField ) )
+ pField->ConvertRowColField( aSaveData );
+
+ // column fields
+ for( const auto& rColField : maColFields )
+ if( const XclImpPTField* pField = GetField( rColField ) )
+ pField->ConvertRowColField( aSaveData );
+
+ // page fields
+ for( const auto& rPageField : maPageFields )
+ if( const XclImpPTField* pField = GetField( rPageField ) )
+ pField->ConvertPageField( aSaveData );
+
+ // We need to import hidden fields because hidden fields may contain
+ // special settings for subtotals (aggregation function, filters, custom
+ // name etc.) and members (hidden, custom name etc.).
+
+ // hidden fields
+ for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
+ if( const XclImpPTField* pField = GetField( nField ) )
+ if (!pField->GetAxes())
+ pField->ConvertHiddenField( aSaveData );
+
+ // data fields
+ for( const auto& rFiltDataField : maFiltDataFields )
+ if( const XclImpPTField* pField = GetField( rFiltDataField ) )
+ pField->ConvertDataField( aSaveData );
+
+ // *** insert into Calc document ***
+
+ // create source descriptor
+ ScSheetSourceDesc aDesc(&GetDoc());
+ const OUString& rSrcName = mxPCache->GetSourceRangeName();
+ if (!rSrcName.isEmpty())
+ // Range name is the data source.
+ aDesc.SetRangeName(rSrcName);
+ else
+ // Normal cell range.
+ aDesc.SetSourceRange(mxPCache->GetSourceRange());
+
+ // adjust output range to include the page fields
+ ScRange aOutRange( maOutScRange );
+ if( !maPageFields.empty() )
+ {
+ SCROW nDecRows = ::std::min< SCROW >( aOutRange.aStart.Row(), maPageFields.size() + 1 );
+ aOutRange.aStart.IncRow( -nDecRows );
+ }
+
+ // create the DataPilot
+ std::unique_ptr<ScDPObject> pDPObj(new ScDPObject( &GetDoc() ));
+ pDPObj->SetName( maPTInfo.maTableName );
+ if (!maPTInfo.maDataName.isEmpty())
+ aSaveData.GetDataLayoutDimension()->SetLayoutName(maPTInfo.maDataName);
+
+ if (!maPTViewEx9Info.maGrandTotalName.isEmpty())
+ aSaveData.SetGrandTotalName(maPTViewEx9Info.maGrandTotalName);
+
+ pDPObj->SetSaveData( aSaveData );
+ pDPObj->SetSheetDesc( aDesc );
+ pDPObj->SetOutRange( aOutRange );
+ pDPObj->SetHeaderLayout( maPTViewEx9Info.mnGridLayout == 0 );
+
+ mpDPObj = GetDoc().GetDPCollection()->InsertNewTable(std::move(pDPObj));
+
+ ApplyFieldInfo();
+ ApplyMergeFlags(aOutRange, aSaveData);
+}
+
+void XclImpPivotTable::MaybeRefresh()
+{
+ if (mpDPObj && mxPCache->IsRefreshOnLoad())
+ {
+ // 'refresh table on load' flag is set. Refresh the table now. Some
+ // Excel files contain partial table output when this flag is set.
+ ScRange aOutRange = mpDPObj->GetOutRange();
+ mpDPObj->Output(aOutRange.aStart);
+ }
+}
+
+void XclImpPivotTable::ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData)
+{
+ // Apply merge flags for various datapilot controls.
+
+ ScDPOutputGeometry aGeometry(rOutRange, false);
+ aGeometry.setColumnFieldCount(maPTInfo.mnColFields);
+ aGeometry.setPageFieldCount(maPTInfo.mnPageFields);
+ aGeometry.setDataFieldCount(maPTInfo.mnDataFields);
+ aGeometry.setRowFieldCount(maPTInfo.mnRowFields);
+
+ // Make sure we set headerlayout when input file has additional raw header
+ if(maPTInfo.mnColFields == 0)
+ {
+ mpDPObj->SetHeaderLayout( maPTInfo.mnFirstHeadRow - 2 == static_cast<sal_uInt16>(aGeometry.getRowFieldHeaderRow()) );
+ }
+ aGeometry.setHeaderLayout(mpDPObj->GetHeaderLayout());
+ aGeometry.setCompactMode(maPTAddlInfo.mbCompactMode);
+
+ ScDocument& rDoc = GetDoc();
+
+ vector<const ScDPSaveDimension*> aFieldDims;
+ vector<ScAddress> aFieldBtns;
+
+ aGeometry.getPageFieldPositions(aFieldBtns);
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), ScMF::Button);
+
+ ScMF nMFlag = ScMF::ButtonPopup;
+ OUString aName = rDoc.GetString(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab());
+ if (rSaveData.HasInvisibleMember(aName))
+ nMFlag |= ScMF::HiddenMember;
+
+ rDoc.ApplyFlagsTab(rFieldBtn.Col()+1, rFieldBtn.Row(), rFieldBtn.Col()+1, rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ }
+
+ aGeometry.getColumnFieldPositions(aFieldBtns);
+ rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aFieldDims);
+ if (aFieldBtns.size() == aFieldDims.size())
+ {
+ vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ ScMF nMFlag = ScMF::Button;
+ const ScDPSaveDimension* pDim = *itDim;
+ if (pDim->HasInvisibleMember())
+ nMFlag |= ScMF::HiddenMember;
+ if (!pDim->IsDataLayout())
+ nMFlag |= ScMF::ButtonPopup;
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ ++itDim;
+ }
+ }
+
+ aGeometry.getRowFieldPositions(aFieldBtns);
+ rSaveData.GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aFieldDims);
+ if (!((aFieldBtns.size() == aFieldDims.size()) || (maPTAddlInfo.mbCompactMode && aFieldBtns.size() == 1)))
+ return;
+
+ vector<const ScDPSaveDimension*>::const_iterator itDim = aFieldDims.begin();
+ for (const auto& rFieldBtn : aFieldBtns)
+ {
+ ScMF nMFlag = ScMF::Button;
+ const ScDPSaveDimension* pDim = itDim != aFieldDims.end() ? *itDim++ : nullptr;
+ if (pDim && pDim->HasInvisibleMember())
+ nMFlag |= ScMF::HiddenMember;
+ if (!pDim || !pDim->IsDataLayout())
+ nMFlag |= ScMF::ButtonPopup;
+ rDoc.ApplyFlagsTab(rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Col(), rFieldBtn.Row(), rFieldBtn.Tab(), nMFlag);
+ }
+}
+
+
+void XclImpPivotTable::ApplyFieldInfo()
+{
+ mpDPObj->BuildAllDimensionMembers();
+ ScDPSaveData& rSaveData = *mpDPObj->GetSaveData();
+
+ // row fields
+ for( const auto& rRowField : maRowFields )
+ if( const XclImpPTField* pField = GetField( rRowField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+
+ // column fields
+ for( const auto& rColField : maColFields )
+ if( const XclImpPTField* pField = GetField( rColField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+
+ // page fields
+ for( const auto& rPageField : maPageFields )
+ if( const XclImpPTField* pField = GetField( rPageField ) )
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this, true );
+
+ // hidden fields
+ for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
+ if( const XclImpPTField* pField = GetField( nField ) )
+ if (!pField->GetAxes())
+ pField->ConvertFieldInfo( rSaveData, mpDPObj, *this );
+}
+
+XclImpPivotTableManager::XclImpPivotTableManager( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+XclImpPivotTableManager::~XclImpPivotTableManager()
+{
+}
+
+// pivot cache records --------------------------------------------------------
+
+XclImpPivotCacheRef XclImpPivotTableManager::GetPivotCache( sal_uInt16 nCacheIdx )
+{
+ XclImpPivotCacheRef xPCache;
+ if( nCacheIdx < maPCaches.size() )
+ xPCache = maPCaches[ nCacheIdx ];
+ return xPCache;
+}
+
+void XclImpPivotTableManager::ReadSxidstm( XclImpStream& rStrm )
+{
+ XclImpPivotCacheRef xPCache = std::make_shared<XclImpPivotCache>( GetRoot() );
+ maPCaches.push_back( xPCache );
+ xPCache->ReadSxidstm( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvs( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadSxvs( rStrm );
+}
+
+void XclImpPivotTableManager::ReadDconref( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadDconref( rStrm );
+}
+
+void XclImpPivotTableManager::ReadDConName( XclImpStream& rStrm )
+{
+ if( !maPCaches.empty() )
+ maPCaches.back()->ReadDConName( rStrm );
+}
+
+// pivot table records --------------------------------------------------------
+
+void XclImpPivotTableManager::ReadSxview( XclImpStream& rStrm )
+{
+ XclImpPivotTableRef xPTable = std::make_shared<XclImpPivotTable>( GetRoot() );
+ maPTables.push_back( xPTable );
+ xPTable->ReadSxview( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvd( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvd( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvdex( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvdex( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxivd( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxivd( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxpi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxpi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxdi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxdi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxvi( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxvi( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxex( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxex( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxViewEx9( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxViewEx9( rStrm );
+}
+
+void XclImpPivotTableManager::ReadSxAddl( XclImpStream& rStrm )
+{
+ if( !maPTables.empty() )
+ maPTables.back()->ReadSxAddl( rStrm );
+}
+
+void XclImpPivotTableManager::ReadPivotCaches( const XclImpStream& rStrm )
+{
+ for( auto& rxPCache : maPCaches )
+ rxPCache->ReadPivotCacheStream( rStrm );
+}
+
+void XclImpPivotTableManager::ConvertPivotTables()
+{
+ for( auto& rxPTable : maPTables )
+ rxPTable->Convert();
+}
+
+void XclImpPivotTableManager::MaybeRefreshPivotTables()
+{
+ for( auto& rxPTable : maPTables )
+ rxPTable->MaybeRefresh();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiroot.cxx b/sc/source/filter/excel/xiroot.cxx
new file mode 100644
index 000000000..af04654de
--- /dev/null
+++ b/sc/source/filter/excel/xiroot.cxx
@@ -0,0 +1,298 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xiroot.hxx>
+#include <addincol.hxx>
+#include <colrowst.hxx>
+#include <document.hxx>
+#include <formel.hxx>
+#include <scextopt.hxx>
+#include <xihelper.hxx>
+#include <xiformula.hxx>
+#include <xilink.hxx>
+#include <xiname.hxx>
+#include <xistyle.hxx>
+#include <xicontent.hxx>
+#include <xiescher.hxx>
+#include <xipivot.hxx>
+#include <xipage.hxx>
+#include <xiview.hxx>
+
+#include <root.hxx>
+#include <excimp8.hxx>
+#include <documentimport.hxx>
+
+// Global data ================================================================
+
+XclImpRootData::XclImpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc ) :
+ XclRootData( eBiff, rMedium, xRootStrg, rDoc, eTextEnc, false ),
+ mxDocImport(std::make_shared<ScDocumentImport>(rDoc)),
+ mbHasCodePage( false ),
+ mbHasBasic( false )
+{
+}
+
+XclImpRootData::~XclImpRootData()
+{
+}
+
+XclImpRoot::XclImpRoot( XclImpRootData& rImpRootData ) :
+ XclRoot( rImpRootData ),
+ mrImpData( rImpRootData )
+{
+ mrImpData.mxAddrConv = std::make_shared<XclImpAddressConverter>( GetRoot() );
+ mrImpData.mxFmlaComp = std::make_shared<XclImpFormulaCompiler>( GetRoot() );
+ mrImpData.mxPalette = std::make_shared<XclImpPalette>( GetRoot() );
+ mrImpData.mxFontBfr = std::make_shared<XclImpFontBuffer>( GetRoot() );
+ mrImpData.mxNumFmtBfr = std::make_shared<XclImpNumFmtBuffer>( GetRoot() );
+ mrImpData.mpXFBfr = std::make_shared<XclImpXFBuffer>( GetRoot() );
+ mrImpData.mxXFRangeBfr = std::make_shared<XclImpXFRangeBuffer>( GetRoot() );
+ mrImpData.mxTabInfo = std::make_shared<XclImpTabInfo>();
+ mrImpData.mxNameMgr = std::make_shared<XclImpNameManager>( GetRoot() );
+ mrImpData.mxObjMgr = std::make_shared<XclImpObjectManager>( GetRoot() );
+
+ if( GetBiff() == EXC_BIFF8 )
+ {
+ mrImpData.mxLinkMgr = std::make_shared<XclImpLinkManager>( GetRoot() );
+ mrImpData.mxSst = std::make_shared<XclImpSst>( GetRoot() );
+ mrImpData.mxCondFmtMgr = std::make_shared<XclImpCondFormatManager>( GetRoot() );
+ mrImpData.mxValidMgr = std::make_shared<XclImpValidationManager>( GetRoot() );
+ // TODO still in old RootData (deleted by RootData)
+ GetOldRoot().pAutoFilterBuffer.reset( new XclImpAutoFilterBuffer );
+ mrImpData.mxWebQueryBfr = std::make_shared<XclImpWebQueryBuffer>( GetRoot() );
+ mrImpData.mxPTableMgr = std::make_shared<XclImpPivotTableManager>( GetRoot() );
+ mrImpData.mxTabProtect = std::make_shared<XclImpSheetProtectBuffer>( GetRoot() );
+ mrImpData.mxDocProtect = std::make_shared<XclImpDocProtectBuffer>( GetRoot() );
+ }
+
+ mrImpData.mxPageSett = std::make_shared<XclImpPageSettings>( GetRoot() );
+ mrImpData.mxDocViewSett = std::make_shared<XclImpDocViewSettings>( GetRoot() );
+ mrImpData.mxTabViewSett = std::make_shared<XclImpTabViewSettings>( GetRoot() );
+ mrImpData.mpPrintRanges = std::make_unique<ScRangeListTabs>( GetRoot() );
+ mrImpData.mpPrintTitles = std::make_unique<ScRangeListTabs>( GetRoot() );
+}
+
+void XclImpRoot::SetCodePage( sal_uInt16 nCodePage )
+{
+ SetTextEncoding( XclTools::GetTextEncoding( nCodePage ) );
+ mrImpData.mbHasCodePage = true;
+}
+
+void XclImpRoot::InitializeTable( SCTAB nScTab )
+{
+ if( GetBiff() <= EXC_BIFF4 )
+ {
+ GetPalette().Initialize();
+ GetFontBuffer().Initialize();
+ GetNumFmtBuffer().Initialize();
+ GetXFBuffer().Initialize();
+ }
+ GetXFRangeBuffer().Initialize();
+ GetPageSettings().Initialize();
+ GetTabViewSettings().Initialize();
+ // delete the automatically generated codename
+ GetDoc().SetCodeName( nScTab, OUString() );
+}
+
+void XclImpRoot::FinalizeTable()
+{
+ GetXFRangeBuffer().Finalize();
+ GetOldRoot().pColRowBuff->Convert( GetCurrScTab() );
+ GetPageSettings().Finalize();
+ GetTabViewSettings().Finalize();
+}
+
+XclImpAddressConverter& XclImpRoot::GetAddressConverter() const
+{
+ return *mrImpData.mxAddrConv;
+}
+
+XclImpFormulaCompiler& XclImpRoot::GetFormulaCompiler() const
+{
+ return *mrImpData.mxFmlaComp;
+}
+
+ExcelToSc& XclImpRoot::GetOldFmlaConverter() const
+{
+ // TODO still in old RootData
+ return *GetOldRoot().pFmlaConverter;
+}
+
+XclImpSst& XclImpRoot::GetSst() const
+{
+ assert(mrImpData.mxSst && "XclImpRoot::GetSst - invalid call, wrong BIFF");
+ return *mrImpData.mxSst;
+}
+
+XclImpPalette& XclImpRoot::GetPalette() const
+{
+ return *mrImpData.mxPalette;
+}
+
+XclImpFontBuffer& XclImpRoot::GetFontBuffer() const
+{
+ return *mrImpData.mxFontBfr;
+}
+
+XclImpNumFmtBuffer& XclImpRoot::GetNumFmtBuffer() const
+{
+ return *mrImpData.mxNumFmtBfr;
+}
+
+XclImpXFBuffer& XclImpRoot::GetXFBuffer() const
+{
+ return *mrImpData.mpXFBfr;
+}
+
+XclImpXFRangeBuffer& XclImpRoot::GetXFRangeBuffer() const
+{
+ return *mrImpData.mxXFRangeBfr;
+}
+
+ScRangeListTabs& XclImpRoot::GetPrintAreaBuffer() const
+{
+ return *mrImpData.mpPrintRanges;
+}
+
+ScRangeListTabs& XclImpRoot::GetTitleAreaBuffer() const
+{
+ return *mrImpData.mpPrintTitles;
+}
+
+XclImpTabInfo& XclImpRoot::GetTabInfo() const
+{
+ return *mrImpData.mxTabInfo;
+}
+
+XclImpNameManager& XclImpRoot::GetNameManager() const
+{
+ return *mrImpData.mxNameMgr;
+}
+
+XclImpLinkManager& XclImpRoot::GetLinkManager() const
+{
+ assert(mrImpData.mxLinkMgr && "XclImpRoot::GetLinkManager - invalid call, wrong BIFF");
+ return *mrImpData.mxLinkMgr;
+}
+
+XclImpObjectManager& XclImpRoot::GetObjectManager() const
+{
+ return *mrImpData.mxObjMgr;
+}
+
+XclImpSheetDrawing& XclImpRoot::GetCurrSheetDrawing() const
+{
+ OSL_ENSURE( !IsInGlobals(), "XclImpRoot::GetCurrSheetDrawing - must not be called from workbook globals" );
+ return mrImpData.mxObjMgr->GetSheetDrawing( GetCurrScTab() );
+}
+
+XclImpCondFormatManager& XclImpRoot::GetCondFormatManager() const
+{
+ assert(mrImpData.mxCondFmtMgr && "XclImpRoot::GetCondFormatManager - invalid call, wrong BIFF");
+ return *mrImpData.mxCondFmtMgr;
+}
+
+XclImpValidationManager& XclImpRoot::GetValidationManager() const
+{
+ assert(mrImpData.mxValidMgr && "XclImpRoot::GetValidationManager - invalid call, wrong BIFF");
+ return *mrImpData.mxValidMgr;
+}
+
+XclImpAutoFilterBuffer& XclImpRoot::GetFilterManager() const
+{
+ // TODO still in old RootData
+ assert(GetOldRoot().pAutoFilterBuffer && "XclImpRoot::GetFilterManager - invalid call, wrong BIFF");
+ return *GetOldRoot().pAutoFilterBuffer;
+}
+
+XclImpWebQueryBuffer& XclImpRoot::GetWebQueryBuffer() const
+{
+ assert(mrImpData.mxWebQueryBfr && "XclImpRoot::GetWebQueryBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxWebQueryBfr;
+}
+
+XclImpPivotTableManager& XclImpRoot::GetPivotTableManager() const
+{
+ assert(mrImpData.mxPTableMgr && "XclImpRoot::GetPivotTableManager - invalid call, wrong BIFF");
+ return *mrImpData.mxPTableMgr;
+}
+
+XclImpSheetProtectBuffer& XclImpRoot::GetSheetProtectBuffer() const
+{
+ assert(mrImpData.mxTabProtect && "XclImpRoot::GetSheetProtectBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxTabProtect;
+}
+
+XclImpDocProtectBuffer& XclImpRoot::GetDocProtectBuffer() const
+{
+ assert(mrImpData.mxDocProtect && "XclImpRoot::GetDocProtectBuffer - invalid call, wrong BIFF");
+ return *mrImpData.mxDocProtect;
+}
+
+XclImpPageSettings& XclImpRoot::GetPageSettings() const
+{
+ return *mrImpData.mxPageSett;
+}
+
+XclImpDocViewSettings& XclImpRoot::GetDocViewSettings() const
+{
+ return *mrImpData.mxDocViewSett;
+}
+
+XclImpTabViewSettings& XclImpRoot::GetTabViewSettings() const
+{
+ return *mrImpData.mxTabViewSett;
+}
+
+OUString XclImpRoot::GetScAddInName( const OUString& rXclName )
+{
+ OUString aScName;
+ if( ScGlobal::GetAddInCollection()->GetCalcName( rXclName, aScName ) )
+ return aScName;
+ return rXclName;
+}
+
+void XclImpRoot::ReadCodeName( XclImpStream& rStrm, bool bGlobals )
+{
+ if( !(mrImpData.mbHasBasic && (GetBiff() == EXC_BIFF8)) )
+ return;
+
+ OUString aName = rStrm.ReadUniString();
+ if( aName.isEmpty() )
+ return;
+
+ if( bGlobals )
+ {
+ GetExtDocOptions().GetDocSettings().maGlobCodeName = aName;
+ GetDoc().SetCodeName( aName );
+ }
+ else
+ {
+ GetExtDocOptions().SetCodeName( GetCurrScTab(), aName );
+ GetDoc().SetCodeName( GetCurrScTab(), aName );
+ }
+}
+
+ScDocumentImport& XclImpRoot::GetDocImport()
+{
+ return *mrImpData.mxDocImport;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistream.cxx b/sc/source/filter/excel/xistream.cxx
new file mode 100644
index 000000000..0a6c24aca
--- /dev/null
+++ b/sc/source/filter/excel/xistream.cxx
@@ -0,0 +1,1084 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/solar.h>
+#include <ftools.hxx>
+#include <xistream.hxx>
+#include <xlstring.hxx>
+#include <xiroot.hxx>
+
+#include <vector>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+// Decryption
+XclImpDecrypter::XclImpDecrypter() :
+ mnError( EXC_ENCR_ERROR_UNSUPP_CRYPT ),
+ mnOldPos( STREAM_SEEK_TO_END ),
+ mnRecSize( 0 )
+{
+}
+
+XclImpDecrypter::XclImpDecrypter( const XclImpDecrypter& rSrc ) :
+ ::comphelper::IDocPasswordVerifier(),
+ mnError( rSrc.mnError ),
+ mnOldPos( STREAM_SEEK_TO_END ),
+ mnRecSize( 0 )
+{
+}
+
+XclImpDecrypter::~XclImpDecrypter()
+{
+}
+
+XclImpDecrypterRef XclImpDecrypter::Clone() const
+{
+ XclImpDecrypterRef xNewDecr;
+ if( IsValid() )
+ xNewDecr.reset( OnClone() );
+ return xNewDecr;
+}
+
+::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyPassword( const OUString& rPassword, uno::Sequence< beans::NamedValue >& o_rEncryptionData )
+{
+ o_rEncryptionData = OnVerifyPassword( rPassword );
+ mnError = o_rEncryptionData.hasElements() ? ERRCODE_NONE : ERRCODE_ABORT;
+ return o_rEncryptionData.hasElements() ? ::comphelper::DocPasswordVerifierResult::OK : ::comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+::comphelper::DocPasswordVerifierResult XclImpDecrypter::verifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ bool bValid = OnVerifyEncryptionData( rEncryptionData );
+ mnError = bValid ? ERRCODE_NONE : ERRCODE_ABORT;
+ return bValid ? ::comphelper::DocPasswordVerifierResult::OK : ::comphelper::DocPasswordVerifierResult::WrongPassword;
+}
+
+void XclImpDecrypter::Update( const SvStream& rStrm, sal_uInt16 nRecSize )
+{
+ if( IsValid() )
+ {
+ sal_uInt64 const nNewPos = rStrm.Tell();
+ if( (mnOldPos != nNewPos) || (mnRecSize != nRecSize) )
+ {
+ OnUpdate( mnOldPos, nNewPos, nRecSize );
+ mnOldPos = nNewPos;
+ mnRecSize = nRecSize;
+ }
+ }
+}
+
+sal_uInt16 XclImpDecrypter::Read( SvStream& rStrm, void* pData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = 0;
+ if( pData && nBytes )
+ {
+ if( IsValid() )
+ {
+ Update( rStrm, mnRecSize );
+ nRet = OnRead( rStrm, static_cast< sal_uInt8* >( pData ), nBytes );
+ mnOldPos = rStrm.Tell();
+ }
+ else
+ nRet = static_cast<sal_uInt16>(rStrm.ReadBytes(pData, nBytes));
+ }
+ return nRet;
+}
+
+XclImpBiff5Decrypter::XclImpBiff5Decrypter( sal_uInt16 nKey, sal_uInt16 nHash ) :
+ mnKey( nKey ),
+ mnHash( nHash )
+{
+}
+
+XclImpBiff5Decrypter::XclImpBiff5Decrypter( const XclImpBiff5Decrypter& rSrc ) :
+ XclImpDecrypter( rSrc ),
+ maEncryptionData( rSrc.maEncryptionData ),
+ mnKey( rSrc.mnKey ),
+ mnHash( rSrc.mnHash )
+{
+ if( IsValid() )
+ maCodec.InitCodec( maEncryptionData );
+}
+
+XclImpBiff5Decrypter* XclImpBiff5Decrypter::OnClone() const
+{
+ return new XclImpBiff5Decrypter( *this );
+}
+
+uno::Sequence< beans::NamedValue > XclImpBiff5Decrypter::OnVerifyPassword( const OUString& rPassword )
+{
+ maEncryptionData.realloc( 0 );
+
+ /* Convert password to a byte string. TODO: this needs some fine tuning
+ according to the spec... */
+ OString aBytePassword = OUStringToOString( rPassword, osl_getThreadTextEncoding() );
+ sal_Int32 nLen = aBytePassword.getLength();
+ if( (0 < nLen) && (nLen < 16) )
+ {
+ // init codec
+ maCodec.InitKey( reinterpret_cast<sal_uInt8 const *>(aBytePassword.getStr()) );
+
+ if ( maCodec.VerifyKey( mnKey, mnHash ) )
+ {
+ maEncryptionData = maCodec.GetEncryptionData();
+
+ // since the export uses Std97 encryption always we have to request it here
+ ::std::vector< sal_uInt16 > aPassVect( 16 );
+ sal_Int32 nInd = 0;
+ std::for_each(aPassVect.begin(), aPassVect.begin() + nLen,
+ [&rPassword, &nInd](sal_uInt16& rPass) {
+ rPass = static_cast< sal_uInt16 >( rPassword[nInd] );
+ ++nInd;
+ });
+
+ uno::Sequence< sal_Int8 > aDocId = ::comphelper::DocPasswordHelper::GenerateRandomByteSequence( 16 );
+ OSL_ENSURE( aDocId.getLength() == 16, "Unexpected length of the sequence!" );
+
+ ::msfilter::MSCodec_Std97 aCodec97;
+ aCodec97.InitKey(aPassVect.data(), reinterpret_cast<sal_uInt8 const *>(aDocId.getConstArray()));
+
+ // merge the EncryptionData, there should be no conflicts
+ ::comphelper::SequenceAsHashMap aEncryptionHash( maEncryptionData );
+ aEncryptionHash.update( ::comphelper::SequenceAsHashMap( aCodec97.GetEncryptionData() ) );
+ aEncryptionHash >> maEncryptionData;
+ }
+ }
+
+ return maEncryptionData;
+}
+
+bool XclImpBiff5Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ maEncryptionData.realloc( 0 );
+
+ if( rEncryptionData.hasElements() )
+ {
+ // init codec
+ maCodec.InitCodec( rEncryptionData );
+
+ if ( maCodec.VerifyKey( mnKey, mnHash ) )
+ maEncryptionData = rEncryptionData;
+ }
+
+ return maEncryptionData.hasElements();
+}
+
+void XclImpBiff5Decrypter::OnUpdate( std::size_t /*nOldStrmPos*/, std::size_t nNewStrmPos, sal_uInt16 nRecSize )
+{
+ maCodec.InitCipher();
+ maCodec.Skip( (nNewStrmPos + nRecSize) & 0x0F );
+}
+
+sal_uInt16 XclImpBiff5Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = static_cast<sal_uInt16>(rStrm.ReadBytes(pnData, nBytes));
+ maCodec.Decode( pnData, nRet );
+ return nRet;
+}
+
+XclImpBiff8Decrypter::XclImpBiff8Decrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash)
+ : maSalt(std::move(rSalt))
+ , maVerifier(std::move(rVerifier))
+ , maVerifierHash(std::move(rVerifierHash))
+ , mpCodec(nullptr)
+{
+}
+
+XclImpBiff8Decrypter::XclImpBiff8Decrypter(const XclImpBiff8Decrypter& rSrc)
+ : XclImpDecrypter(rSrc)
+ , maEncryptionData(rSrc.maEncryptionData)
+ , maSalt(rSrc.maSalt)
+ , maVerifier(rSrc.maVerifier)
+ , maVerifierHash(rSrc.maVerifierHash)
+ , mpCodec(nullptr)
+{
+}
+
+XclImpBiff8StdDecrypter::XclImpBiff8StdDecrypter(const XclImpBiff8StdDecrypter& rSrc)
+ : XclImpBiff8Decrypter(rSrc)
+{
+ mpCodec = &maCodec;
+ if (IsValid())
+ maCodec.InitCodec(maEncryptionData);
+}
+
+XclImpBiff8StdDecrypter* XclImpBiff8StdDecrypter::OnClone() const
+{
+ return new XclImpBiff8StdDecrypter(*this);
+}
+
+XclImpBiff8CryptoAPIDecrypter::XclImpBiff8CryptoAPIDecrypter(const XclImpBiff8CryptoAPIDecrypter& rSrc)
+ : XclImpBiff8Decrypter(rSrc)
+{
+ mpCodec = &maCodec;
+ if (IsValid())
+ maCodec.InitCodec(maEncryptionData);
+}
+
+XclImpBiff8CryptoAPIDecrypter* XclImpBiff8CryptoAPIDecrypter::OnClone() const
+{
+ return new XclImpBiff8CryptoAPIDecrypter(*this);
+}
+
+uno::Sequence< beans::NamedValue > XclImpBiff8Decrypter::OnVerifyPassword( const OUString& rPassword )
+{
+ maEncryptionData.realloc( 0 );
+
+ sal_Int32 nLen = rPassword.getLength();
+ if( (0 < nLen) && (nLen < 16) )
+ {
+ // copy string to sal_uInt16 array
+ ::std::vector< sal_uInt16 > aPassVect( 16 );
+ const sal_Unicode* pcChar = rPassword.getStr();
+ std::for_each(aPassVect.begin(), aPassVect.begin() + nLen,
+ [&pcChar](sal_uInt16& rPass) {
+ rPass = static_cast< sal_uInt16 >( *pcChar );
+ ++pcChar;
+ });
+
+ // init codec
+ mpCodec->InitKey(aPassVect.data(), maSalt.data());
+ if (mpCodec->VerifyKey(maVerifier.data(), maVerifierHash.data()))
+ maEncryptionData = mpCodec->GetEncryptionData();
+ }
+
+ return maEncryptionData;
+}
+
+bool XclImpBiff8Decrypter::OnVerifyEncryptionData( const uno::Sequence< beans::NamedValue >& rEncryptionData )
+{
+ maEncryptionData.realloc( 0 );
+
+ if( rEncryptionData.hasElements() )
+ {
+ // init codec
+ mpCodec->InitCodec( rEncryptionData );
+
+ if (mpCodec->VerifyKey(maVerifier.data(), maVerifierHash.data()))
+ maEncryptionData = rEncryptionData;
+ }
+
+ return maEncryptionData.hasElements();
+}
+
+void XclImpBiff8Decrypter::OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 /*nRecSize*/ )
+{
+ if( nNewStrmPos == nOldStrmPos )
+ return;
+
+ sal_uInt32 nOldBlock = GetBlock( nOldStrmPos );
+ sal_uInt16 nOldOffset = GetOffset( nOldStrmPos );
+
+ sal_uInt32 nNewBlock = GetBlock( nNewStrmPos );
+ sal_uInt16 nNewOffset = GetOffset( nNewStrmPos );
+
+ /* Rekey cipher, if block changed or if previous offset in same block. */
+ if( (nNewBlock != nOldBlock) || (nNewOffset < nOldOffset) )
+ {
+ mpCodec->InitCipher( nNewBlock );
+ nOldOffset = 0; // reset nOldOffset for next if() statement
+ }
+
+ /* Seek to correct offset. */
+ if( nNewOffset > nOldOffset )
+ mpCodec->Skip( nNewOffset - nOldOffset );
+}
+
+sal_uInt16 XclImpBiff8Decrypter::OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes )
+{
+ sal_uInt16 nRet = 0;
+
+ sal_uInt8* pnCurrData = pnData;
+ sal_uInt16 nBytesLeft = nBytes;
+ while( nBytesLeft )
+ {
+ sal_uInt16 nBlockLeft = EXC_ENCR_BLOCKSIZE - GetOffset( rStrm.Tell() );
+ sal_uInt16 nDecBytes = ::std::min< sal_uInt16 >( nBytesLeft, nBlockLeft );
+
+ // read the block from stream
+ nRet = nRet + static_cast<sal_uInt16>(rStrm.ReadBytes(pnCurrData, nDecBytes));
+ // decode the block inplace
+ mpCodec->Decode( pnCurrData, nDecBytes, pnCurrData, nDecBytes );
+ if( GetOffset( rStrm.Tell() ) == 0 )
+ mpCodec->InitCipher( GetBlock( rStrm.Tell() ) );
+
+ pnCurrData += nDecBytes;
+ nBytesLeft = nBytesLeft - nDecBytes;
+ }
+
+ return nRet;
+}
+
+sal_uInt32 XclImpBiff8Decrypter::GetBlock( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt32 >( nStrmPos / EXC_ENCR_BLOCKSIZE );
+}
+
+sal_uInt16 XclImpBiff8Decrypter::GetOffset( std::size_t nStrmPos )
+{
+ return static_cast< sal_uInt16 >( nStrmPos % EXC_ENCR_BLOCKSIZE );
+}
+
+// Stream
+XclImpStreamPos::XclImpStreamPos() :
+ mnPos( STREAM_SEEK_TO_BEGIN ),
+ mnNextPos( STREAM_SEEK_TO_BEGIN ),
+ mnCurrSize( 0 ),
+ mnRawRecId( EXC_ID_UNKNOWN ),
+ mnRawRecSize( 0 ),
+ mnRawRecLeft( 0 ),
+ mbValid( false )
+{
+}
+
+void XclImpStreamPos::Set(
+ const SvStream& rStrm, std::size_t nNextPos, std::size_t nCurrSize,
+ sal_uInt16 nRawRecId, sal_uInt16 nRawRecSize, sal_uInt16 nRawRecLeft,
+ bool bValid )
+{
+ mnPos = rStrm.Tell();
+ mnNextPos = nNextPos;
+ mnCurrSize = nCurrSize;
+ mnRawRecId = nRawRecId;
+ mnRawRecSize = nRawRecSize;
+ mnRawRecLeft = nRawRecLeft;
+ mbValid = bValid;
+}
+
+void XclImpStreamPos::Get(
+ SvStream& rStrm, std::size_t& rnNextPos, std::size_t& rnCurrSize,
+ sal_uInt16& rnRawRecId, sal_uInt16& rnRawRecSize, sal_uInt16& rnRawRecLeft,
+ bool& rbValid ) const
+{
+ rStrm.Seek( mnPos );
+ rnNextPos = mnNextPos;
+ rnCurrSize = mnCurrSize;
+ rnRawRecId = mnRawRecId;
+ rnRawRecSize = mnRawRecSize;
+ rnRawRecLeft = mnRawRecLeft;
+ rbValid = mbValid;
+}
+
+XclBiff XclImpStream::DetectBiffVersion( SvStream& rStrm )
+{
+ XclBiff eBiff = EXC_BIFF_UNKNOWN;
+
+ rStrm.Seek( STREAM_SEEK_TO_BEGIN );
+ sal_uInt16 nBofId(0), nBofSize(0);
+ rStrm.ReadUInt16( nBofId ).ReadUInt16( nBofSize );
+
+ if (rStrm.good() && 4 <= nBofSize && nBofSize <= 16) switch( nBofId )
+ {
+ case EXC_ID2_BOF:
+ eBiff = EXC_BIFF2;
+ break;
+ case EXC_ID3_BOF:
+ eBiff = EXC_BIFF3;
+ break;
+ case EXC_ID4_BOF:
+ eBiff = EXC_BIFF4;
+ break;
+ case EXC_ID5_BOF:
+ {
+ sal_uInt16 nVersion(0);
+ rStrm.ReadUInt16( nVersion );
+ // #i23425# #i44031# #i62752# there are some *really* broken documents out there...
+ switch( nVersion & 0xFF00 )
+ {
+ case 0: eBiff = EXC_BIFF5; break; // #i44031# #i62752#
+ case EXC_BOF_BIFF2: eBiff = EXC_BIFF2; break;
+ case EXC_BOF_BIFF3: eBiff = EXC_BIFF3; break;
+ case EXC_BOF_BIFF4: eBiff = EXC_BIFF4; break;
+ case EXC_BOF_BIFF5: eBiff = EXC_BIFF5; break;
+ case EXC_BOF_BIFF8: eBiff = EXC_BIFF8; break;
+ default: SAL_WARN("sc", "XclImpStream::DetectBiffVersion - unknown BIFF version: 0x" << std::hex << nVersion );
+ }
+ }
+ break;
+ }
+ return eBiff;
+}
+
+XclImpStream::XclImpStream( SvStream& rInStrm, const XclImpRoot& rRoot ) :
+ mrStrm( rInStrm ),
+ mrRoot( rRoot ),
+ mnGlobRecId( EXC_ID_UNKNOWN ),
+ mbGlobValidRec( false ),
+ mbHasGlobPos( false ),
+ mnNextRecPos( STREAM_SEEK_TO_BEGIN ),
+ mnCurrRecSize( 0 ),
+ mnComplRecSize( 0 ),
+ mbHasComplRec( false ),
+ mnRecId( EXC_ID_UNKNOWN ),
+ mnAltContId( EXC_ID_UNKNOWN ),
+ mnRawRecId( EXC_ID_UNKNOWN ),
+ mnRawRecSize( 0 ),
+ mnRawRecLeft( 0 ),
+ mcNulSubst( '?' ),
+ mbCont( true ),
+ mbUseDecr( false ),
+ mbValidRec( false ),
+ mbValid( false )
+{
+ mnStreamSize = mrStrm.TellEnd();
+ mrStrm.Seek( STREAM_SEEK_TO_BEGIN );
+}
+
+XclImpStream::~XclImpStream()
+{
+}
+
+bool XclImpStream::StartNextRecord()
+{
+ maPosStack.clear();
+
+ /* #i4266# Counter to ignore zero records (id==len==0) (i.e. the application
+ "Crystal Report" writes zero records between other records) */
+ std::size_t nZeroRecCount = 5;
+ bool bIsZeroRec = false;
+
+ do
+ {
+ mbValidRec = ReadNextRawRecHeader();
+ bIsZeroRec = (mnRawRecId == 0) && (mnRawRecSize == 0);
+ if( bIsZeroRec ) --nZeroRecCount;
+ mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
+ }
+ while( mbValidRec && ((mbCont && IsContinueId( mnRawRecId )) || (bIsZeroRec && nZeroRecCount)) );
+
+ mbValidRec = mbValidRec && !bIsZeroRec;
+ mbValid = mbValidRec;
+ SetupRecord();
+
+ return mbValidRec;
+}
+
+bool XclImpStream::StartNextRecord( std::size_t nNextRecPos )
+{
+ mnNextRecPos = nNextRecPos;
+ return StartNextRecord();
+}
+
+void XclImpStream::ResetRecord( bool bContLookup, sal_uInt16 nAltContId )
+{
+ if( mbValidRec )
+ {
+ maPosStack.clear();
+ RestorePosition( maFirstRec );
+ mnCurrRecSize = mnComplRecSize = mnRawRecSize;
+ mbHasComplRec = !bContLookup;
+ mbCont = bContLookup;
+ mnAltContId = nAltContId;
+ EnableDecryption();
+ }
+}
+
+void XclImpStream::RewindRecord()
+{
+ mnNextRecPos = maFirstRec.GetPos();
+ mbValid = mbValidRec = false;
+}
+
+void XclImpStream::SetDecrypter( XclImpDecrypterRef const & xDecrypter )
+{
+ mxDecrypter = xDecrypter;
+ EnableDecryption();
+ SetupDecrypter();
+}
+
+void XclImpStream::CopyDecrypterFrom( const XclImpStream& rStrm )
+{
+ XclImpDecrypterRef xNewDecr;
+ if( rStrm.mxDecrypter )
+ xNewDecr = rStrm.mxDecrypter->Clone();
+ SetDecrypter( xNewDecr );
+}
+
+void XclImpStream::EnableDecryption( bool bEnable )
+{
+ mbUseDecr = bEnable && mxDecrypter && mxDecrypter->IsValid();
+}
+
+void XclImpStream::PushPosition()
+{
+ maPosStack.emplace_back( );
+ StorePosition( maPosStack.back() );
+}
+
+void XclImpStream::PopPosition()
+{
+ OSL_ENSURE( !maPosStack.empty(), "XclImpStream::PopPosition - stack empty" );
+ if( !maPosStack.empty() )
+ {
+ RestorePosition( maPosStack.back() );
+ maPosStack.pop_back();
+ }
+}
+
+void XclImpStream::StoreGlobalPosition()
+{
+ StorePosition( maGlobPos );
+ mnGlobRecId = mnRecId;
+ mbGlobValidRec = mbValidRec;
+ mbHasGlobPos = true;
+}
+
+void XclImpStream::SeekGlobalPosition()
+{
+ OSL_ENSURE( mbHasGlobPos, "XclImpStream::SeekGlobalPosition - no position stored" );
+ if( mbHasGlobPos )
+ {
+ RestorePosition( maGlobPos );
+ mnRecId = mnGlobRecId;
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = !mbCont;
+ mbValidRec = mbGlobValidRec;
+ }
+}
+
+std::size_t XclImpStream::GetRecPos() const
+{
+ return mbValid ? (mnCurrRecSize - mnRawRecLeft) : EXC_REC_SEEK_TO_END;
+}
+
+std::size_t XclImpStream::GetRecSize()
+{
+ if( !mbHasComplRec )
+ {
+ PushPosition();
+ while( JumpToNextContinue() ) ; // JumpToNextContinue() adds up mnCurrRecSize
+ mnComplRecSize = mnCurrRecSize;
+ mbHasComplRec = true;
+ PopPosition();
+ }
+ return mnComplRecSize;
+}
+
+std::size_t XclImpStream::GetRecLeft()
+{
+ return mbValid ? (GetRecSize() - GetRecPos()) : 0;
+}
+
+sal_uInt16 XclImpStream::GetNextRecId()
+{
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN;
+ if( mbValidRec )
+ {
+ PushPosition();
+ while( JumpToNextContinue() ) ; // skip following CONTINUE records
+ if( mnNextRecPos < mnStreamSize )
+ {
+ mrStrm.Seek( mnNextRecPos );
+ mrStrm.ReadUInt16( nRecId );
+ }
+ PopPosition();
+ }
+ return nRecId;
+}
+
+sal_uInt16 XclImpStream::PeekRecId( std::size_t nPos )
+{
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN;
+ if (mbValidRec && nPos < mnStreamSize)
+ {
+ sal_uInt64 const nCurPos = mrStrm.Tell();
+ mrStrm.Seek(nPos);
+ mrStrm.ReadUInt16( nRecId );
+ mrStrm.Seek(nCurPos);
+ }
+ return nRecId;
+}
+
+sal_uInt8 XclImpStream::ReaduInt8()
+{
+ sal_uInt8 nValue = 0;
+ if( EnsureRawReadSize( 1 ) )
+ {
+ if( mbUseDecr )
+ mxDecrypter->Read( mrStrm, &nValue, 1 );
+ else
+ mrStrm.ReadUChar( nValue );
+ --mnRawRecLeft;
+ }
+ return nValue;
+}
+
+sal_Int16 XclImpStream::ReadInt16()
+{
+ sal_Int16 nValue = 0;
+ if( EnsureRawReadSize( 2 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT16 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 2 );
+ nValue = static_cast< sal_Int16 >( SVBT16ToUInt16( pnBuffer ) );
+ }
+ else
+ mrStrm.ReadInt16( nValue );
+ mnRawRecLeft -= 2;
+ }
+ return nValue;
+}
+
+sal_uInt16 XclImpStream::ReaduInt16()
+{
+ sal_uInt16 nValue = 0;
+ if( EnsureRawReadSize( 2 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT16 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 2 );
+ nValue = SVBT16ToUInt16( pnBuffer );
+ }
+ else
+ mrStrm.ReadUInt16( nValue );
+ mnRawRecLeft -= 2;
+ }
+ return nValue;
+}
+
+sal_Int32 XclImpStream::ReadInt32()
+{
+ sal_Int32 nValue = 0;
+ if( EnsureRawReadSize( 4 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT32 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 4 );
+ nValue = static_cast< sal_Int32 >( SVBT32ToUInt32( pnBuffer ) );
+ }
+ else
+ mrStrm.ReadInt32( nValue );
+ mnRawRecLeft -= 4;
+ }
+ return nValue;
+}
+
+sal_uInt32 XclImpStream::ReaduInt32()
+{
+ sal_uInt32 nValue = 0;
+ if( EnsureRawReadSize( 4 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT32 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 4 );
+ nValue = SVBT32ToUInt32( pnBuffer );
+ }
+ else
+ mrStrm.ReadUInt32( nValue );
+ mnRawRecLeft -= 4;
+ }
+ return nValue;
+}
+
+double XclImpStream::ReadDouble()
+{
+ double nValue = 0;
+ if( EnsureRawReadSize( 8 ) )
+ {
+ if( mbUseDecr )
+ {
+ SVBT64 pnBuffer{0};
+ mxDecrypter->Read( mrStrm, pnBuffer, 8 );
+ nValue = SVBT64ToDouble( pnBuffer );
+ }
+ else
+ mrStrm.ReadDouble( nValue );
+ mnRawRecLeft -= 8;
+ }
+ return nValue;
+}
+
+std::size_t XclImpStream::Read( void* pData, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if( mbValid && pData && (nBytes > 0) )
+ {
+ sal_uInt8* pnBuffer = static_cast< sal_uInt8* >( pData );
+ std::size_t nBytesLeft = nBytes;
+
+ while( mbValid && (nBytesLeft > 0) )
+ {
+ sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
+ sal_uInt16 nReadRet = ReadRawData( pnBuffer, nReadSize );
+ nRet += nReadRet;
+ mbValid = (nReadSize == nReadRet);
+ OSL_ENSURE( mbValid, "XclImpStream::Read - stream read error" );
+ pnBuffer += nReadRet;
+ nBytesLeft -= nReadRet;
+ if( mbValid && (nBytesLeft > 0) )
+ JumpToNextContinue();
+ OSL_ENSURE( mbValid, "XclImpStream::Read - record overread" );
+ }
+ }
+ return nRet;
+}
+
+std::size_t XclImpStream::CopyToStream( SvStream& rOutStrm, std::size_t nBytes )
+{
+ std::size_t nRet = 0;
+ if (mbValid && nBytes)
+ {
+ const std::size_t nMaxBuffer = 4096;
+ std::vector<sal_uInt8> aBuffer(o3tl::sanitizing_min(nBytes, nMaxBuffer));
+ std::size_t nBytesLeft = nBytes;
+
+ while (mbValid)
+ {
+ if (!nBytesLeft)
+ break;
+ std::size_t nReadSize = o3tl::sanitizing_min(nBytesLeft, nMaxBuffer);
+ nRet += Read(aBuffer.data(), nReadSize);
+ // writing more bytes than read results in invalid memory access
+ SAL_WARN_IF(nRet != nReadSize, "sc", "read less bytes than requested");
+ rOutStrm.WriteBytes(aBuffer.data(), nReadSize);
+ nBytesLeft -= nReadSize;
+ }
+ }
+ return nRet;
+}
+
+void XclImpStream::CopyRecordToStream( SvStream& rOutStrm )
+{
+ if( mbValidRec )
+ {
+ PushPosition();
+ RestorePosition( maFirstRec );
+ CopyToStream( rOutStrm, GetRecSize() );
+ PopPosition();
+ }
+}
+
+void XclImpStream::Seek( std::size_t nPos )
+{
+ if( !mbValidRec )
+ return;
+
+ std::size_t nCurrPos = GetRecPos();
+ if( !mbValid || (nPos < nCurrPos) ) // from invalid state or backward
+ {
+ RestorePosition( maFirstRec );
+ Ignore( nPos );
+ }
+ else if( nPos > nCurrPos ) // forward
+ {
+ Ignore( nPos - nCurrPos );
+ }
+}
+
+void XclImpStream::Ignore( std::size_t nBytes )
+{
+ // implementation similar to Read(), but without really reading anything
+ std::size_t nBytesLeft = nBytes;
+ while (mbValid)
+ {
+ if (!nBytesLeft)
+ break;
+ sal_uInt16 nReadSize = GetMaxRawReadSize( nBytesLeft );
+ mbValid = checkSeek(mrStrm, mrStrm.Tell() + nReadSize);
+ mnRawRecLeft = mnRawRecLeft - nReadSize;
+ nBytesLeft -= nReadSize;
+ if (mbValid && nBytesLeft > 0)
+ JumpToNextContinue();
+ OSL_ENSURE( mbValid, "XclImpStream::Ignore - record overread" );
+ }
+}
+
+std::size_t XclImpStream::ReadUniStringExtHeader(
+ bool& rb16Bit, bool& rbRich, bool& rbFareast,
+ sal_uInt16& rnFormatRuns, sal_uInt32& rnExtInf, sal_uInt8 nFlags )
+{
+ OSL_ENSURE( !::get_flag( nFlags, EXC_STRF_UNKNOWN ), "XclImpStream::ReadUniStringExt - unknown flags" );
+ rb16Bit = ::get_flag( nFlags, EXC_STRF_16BIT );
+ rbRich = ::get_flag( nFlags, EXC_STRF_RICH );
+ rbFareast = ::get_flag( nFlags, EXC_STRF_FAREAST );
+ rnFormatRuns = rbRich ? ReaduInt16() : 0;
+ rnExtInf = rbFareast ? ReaduInt32() : 0;
+ return rnExtInf + 4 * rnFormatRuns;
+}
+
+std::size_t XclImpStream::ReadUniStringExtHeader( bool& rb16Bit, sal_uInt8 nFlags )
+{
+ bool bRich, bFareast;
+ sal_uInt16 nCrun;
+ sal_uInt32 nExtInf;
+ return ReadUniStringExtHeader( rb16Bit, bRich, bFareast, nCrun, nExtInf, nFlags );
+}
+
+OUString XclImpStream::ReadRawUniString( sal_uInt16 nChars, bool b16Bit )
+{
+ OUStringBuffer aRet(o3tl::sanitizing_min<sal_uInt16>(nChars, mnRawRecLeft / (b16Bit ? 2 : 1)));
+ sal_uInt16 nCharsLeft = nChars;
+ sal_uInt16 nReadSize;
+
+ while (IsValid() && nCharsLeft)
+ {
+ if( b16Bit )
+ {
+ nReadSize = o3tl::sanitizing_min<sal_uInt16>(nCharsLeft, mnRawRecLeft / 2);
+ OSL_ENSURE( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
+ "XclImpStream::ReadRawUniString - missing a byte" );
+ }
+ else
+ nReadSize = GetMaxRawReadSize( nCharsLeft );
+
+ std::unique_ptr<sal_Unicode[]> pcBuffer(new sal_Unicode[nReadSize + 1]);
+
+ sal_Unicode* pcUniChar = pcBuffer.get();
+ sal_Unicode* pcEndChar = pcBuffer.get() + nReadSize;
+
+ if( b16Bit )
+ {
+ sal_uInt16 nReadChar;
+ for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
+ {
+ nReadChar = ReaduInt16();
+ (*pcUniChar) = (nReadChar == EXC_NUL) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
+ }
+ }
+ else
+ {
+ sal_uInt8 nReadChar;
+ for( ; IsValid() && (pcUniChar < pcEndChar); ++pcUniChar )
+ {
+ nReadChar = ReaduInt8();
+ (*pcUniChar) = (nReadChar == EXC_NUL_C) ? mcNulSubst : static_cast< sal_Unicode >( nReadChar );
+ }
+ }
+
+ *pcEndChar = '\0';
+ // this has the side-effect of only copying as far as the first null, which appears to be intentional. e.g.
+ // see tdf#124318
+ aRet.append( pcBuffer.get() );
+
+ nCharsLeft = nCharsLeft - nReadSize;
+ if( nCharsLeft > 0 )
+ JumpToNextStringContinue( b16Bit );
+ }
+
+ return aRet.makeStringAndClear();
+}
+
+OUString XclImpStream::ReadUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
+{
+ bool b16Bit;
+ std::size_t nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
+ OUString aRet( ReadRawUniString( nChars, b16Bit ) );
+ Ignore( nExtSize );
+ return aRet;
+}
+
+OUString XclImpStream::ReadUniString( sal_uInt16 nChars )
+{
+ return ReadUniString( nChars, ReaduInt8() );
+}
+
+OUString XclImpStream::ReadUniString()
+{
+ return ReadUniString( ReaduInt16() );
+}
+
+void XclImpStream::IgnoreRawUniString( sal_uInt16 nChars, bool b16Bit )
+{
+ sal_uInt16 nCharsLeft = nChars;
+ sal_uInt16 nReadSize;
+
+ while( IsValid() && (nCharsLeft > 0) )
+ {
+ if( b16Bit )
+ {
+ nReadSize = o3tl::sanitizing_min<sal_uInt16>(nCharsLeft, mnRawRecLeft / 2);
+ OSL_ENSURE( (nReadSize <= nCharsLeft) || !(mnRawRecLeft & 0x1),
+ "XclImpStream::IgnoreRawUniString - missing a byte" );
+ Ignore( nReadSize * 2 );
+ }
+ else
+ {
+ nReadSize = GetMaxRawReadSize( nCharsLeft );
+ Ignore( nReadSize );
+ }
+
+ nCharsLeft = nCharsLeft - nReadSize;
+ if( nCharsLeft > 0 )
+ JumpToNextStringContinue( b16Bit );
+ }
+}
+
+void XclImpStream::IgnoreUniString( sal_uInt16 nChars, sal_uInt8 nFlags )
+{
+ bool b16Bit;
+ std::size_t nExtSize = ReadUniStringExtHeader( b16Bit, nFlags );
+ IgnoreRawUniString( nChars, b16Bit );
+ Ignore( nExtSize );
+}
+
+void XclImpStream::IgnoreUniString( sal_uInt16 nChars )
+{
+ IgnoreUniString( nChars, ReaduInt8() );
+}
+
+OUString XclImpStream::ReadRawByteString( sal_uInt16 nChars )
+{
+ nChars = GetMaxRawReadSize(nChars);
+ std::unique_ptr<char[]> pcBuffer(new char[ nChars + 1 ]);
+ sal_uInt16 nCharsRead = ReadRawData( pcBuffer.get(), nChars );
+ pcBuffer[ nCharsRead ] = '\0';
+ OUString aRet( pcBuffer.get(), strlen(pcBuffer.get()), mrRoot.GetTextEncoding() );
+ return aRet;
+}
+
+OUString XclImpStream::ReadByteString( bool b16BitLen )
+{
+ return ReadRawByteString( b16BitLen ? ReaduInt16() : ReaduInt8() );
+}
+
+// private --------------------------------------------------------------------
+
+void XclImpStream::StorePosition( XclImpStreamPos& rPos )
+{
+ rPos.Set( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
+}
+
+void XclImpStream::RestorePosition( const XclImpStreamPos& rPos )
+{
+ rPos.Get( mrStrm, mnNextRecPos, mnCurrRecSize, mnRawRecId, mnRawRecSize, mnRawRecLeft, mbValid );
+ SetupDecrypter();
+}
+
+bool XclImpStream::ReadNextRawRecHeader()
+{
+ bool bRet = checkSeek(mrStrm, mnNextRecPos) && (mnNextRecPos + 4 <= mnStreamSize);
+ if (bRet)
+ {
+ mrStrm.ReadUInt16( mnRawRecId ).ReadUInt16( mnRawRecSize );
+ bRet = mrStrm.good();
+ }
+ return bRet;
+}
+
+void XclImpStream::SetupDecrypter()
+{
+ if( mxDecrypter )
+ mxDecrypter->Update( mrStrm, mnRawRecSize );
+}
+
+void XclImpStream::SetupRawRecord()
+{
+ // pre: mnRawRecSize contains current raw record size
+ // pre: mrStrm points to start of raw record data
+ mnNextRecPos = mrStrm.Tell() + mnRawRecSize;
+ mnRawRecLeft = mnRawRecSize;
+ mnCurrRecSize += mnRawRecSize;
+ SetupDecrypter(); // decrypter works on raw record level
+}
+
+void XclImpStream::SetupRecord()
+{
+ mnRecId = mnRawRecId;
+ mnAltContId = EXC_ID_UNKNOWN;
+ mnCurrRecSize = 0;
+ mnComplRecSize = mnRawRecSize;
+ mbHasComplRec = !mbCont;
+ SetupRawRecord();
+ SetNulSubstChar();
+ EnableDecryption();
+ StorePosition( maFirstRec );
+}
+
+bool XclImpStream::IsContinueId( sal_uInt16 nRecId ) const
+{
+ return (nRecId == EXC_ID_CONT) || (nRecId == mnAltContId);
+}
+
+bool XclImpStream::JumpToNextContinue()
+{
+ mbValid = mbValid && mbCont && ReadNextRawRecHeader() && IsContinueId( mnRawRecId );
+ if( mbValid ) // do not setup a following non-CONTINUE record
+ SetupRawRecord();
+ return mbValid;
+}
+
+bool XclImpStream::JumpToNextStringContinue( bool& rb16Bit )
+{
+ OSL_ENSURE( mnRawRecLeft == 0, "XclImpStream::JumpToNextStringContinue - unexpected garbage" );
+
+ if( mbCont && (GetRecLeft() > 0) )
+ {
+ JumpToNextContinue();
+ }
+ else if( mnRecId == EXC_ID_CONT )
+ {
+ // CONTINUE handling is off, but we have started reading in a CONTINUE record
+ // -> start next CONTINUE for TXO import
+ mbValidRec = ReadNextRawRecHeader() && ((mnRawRecId != 0) || (mnRawRecSize > 0));
+ mbValid = mbValidRec && (mnRawRecId == EXC_ID_CONT);
+ // we really start a new record here - no chance to return to string origin
+ if( mbValid )
+ SetupRecord();
+ }
+ else
+ mbValid = false;
+
+ if( mbValid )
+ rb16Bit = ::get_flag( ReaduInt8(), EXC_STRF_16BIT );
+ return mbValid;
+}
+
+bool XclImpStream::EnsureRawReadSize( sal_uInt16 nBytes )
+{
+ if( mbValid && nBytes )
+ {
+ while( mbValid && !mnRawRecLeft ) JumpToNextContinue();
+ mbValid = mbValid && (nBytes <= mnRawRecLeft);
+ OSL_ENSURE( mbValid, "XclImpStream::EnsureRawReadSize - record overread" );
+ }
+ return mbValid;
+}
+
+sal_uInt16 XclImpStream::GetMaxRawReadSize( std::size_t nBytes ) const
+{
+ return static_cast<sal_uInt16>(o3tl::sanitizing_min<std::size_t>(nBytes, mnRawRecLeft));
+}
+
+sal_uInt16 XclImpStream::ReadRawData( void* pData, sal_uInt16 nBytes )
+{
+ OSL_ENSURE( (nBytes <= mnRawRecLeft), "XclImpStream::ReadRawData - record overread" );
+ sal_uInt16 nRet = 0;
+ if( mbUseDecr )
+ nRet = mxDecrypter->Read( mrStrm, pData, nBytes );
+ else
+ nRet = static_cast<sal_uInt16>(mrStrm.ReadBytes(pData, nBytes));
+ mnRawRecLeft = mnRawRecLeft - nRet;
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistring.cxx b/sc/source/filter/excel/xistring.cxx
new file mode 100644
index 000000000..f0878a617
--- /dev/null
+++ b/sc/source/filter/excel/xistring.cxx
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xistring.hxx>
+#include <xlstyle.hxx>
+#include <xistream.hxx>
+#include <xiroot.hxx>
+#include <xltools.hxx>
+#include <sal/log.hxx>
+
+// Byte/Unicode strings =======================================================
+
+/** All allowed flags for import. */
+const XclStrFlags nAllowedFlags = XclStrFlags::EightBitLength | XclStrFlags::SmartFlags | XclStrFlags::SeparateFormats;
+
+XclImpString::XclImpString()
+{
+}
+
+XclImpString::XclImpString( const OUString& rString ) :
+ maString( rString )
+{
+}
+
+void XclImpString::Read( XclImpStream& rStrm, XclStrFlags nFlags )
+{
+ if( !( nFlags & XclStrFlags::SeparateFormats ) )
+ maFormats.clear();
+
+ SAL_WARN_IF(
+ nFlags & ~nAllowedFlags, "sc.filter",
+ "XclImpString::Read - unknown flag");
+ bool b16BitLen = !( nFlags & XclStrFlags::EightBitLength );
+
+ switch( rStrm.GetRoot().GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ // no integrated formatting in BIFF2-BIFF7
+ maString = rStrm.ReadByteString( b16BitLen );
+ break;
+
+ case EXC_BIFF8:
+ {
+ // --- string header ---
+ sal_uInt16 nChars = b16BitLen ? rStrm.ReaduInt16() : rStrm.ReaduInt8();
+ sal_uInt8 nFlagField = 0;
+ if( nChars || !( nFlags & XclStrFlags::SmartFlags ) )
+ nFlagField = rStrm.ReaduInt8();
+
+ bool b16Bit, bRich, bFarEast;
+ sal_uInt16 nRunCount;
+ sal_uInt32 nExtInf;
+ rStrm.ReadUniStringExtHeader( b16Bit, bRich, bFarEast, nRunCount, nExtInf, nFlagField );
+ // ignore the flags, they may be wrong
+
+ // --- character array ---
+ maString = rStrm.ReadRawUniString( nChars, b16Bit );
+
+ // --- formatting ---
+ if (nRunCount)
+ ReadFormats( rStrm, maFormats, nRunCount );
+
+ // --- extended (FarEast) information ---
+ rStrm.Ignore( nExtInf );
+ }
+ break;
+
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+void XclImpString::AppendFormat( XclFormatRunVec& rFormats, sal_uInt16 nChar, sal_uInt16 nFontIdx )
+{
+ // #i33341# real life -- same character index may occur several times
+ OSL_ENSURE( rFormats.empty() || (rFormats.back().mnChar <= nChar), "XclImpString::AppendFormat - wrong char order" );
+ if( rFormats.empty() || (rFormats.back().mnChar < nChar) )
+ rFormats.emplace_back( nChar, nFontIdx );
+ else
+ rFormats.back().mnFontIdx = nFontIdx;
+}
+
+void XclImpString::ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats )
+{
+ bool bBiff8 = rStrm.GetRoot().GetBiff() == EXC_BIFF8;
+ sal_uInt16 nRunCount = bBiff8 ? rStrm.ReaduInt16() : rStrm.ReaduInt8();
+ ReadFormats( rStrm, rFormats, nRunCount );
+}
+
+void XclImpString::ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nRunCount )
+{
+ rFormats.clear();
+
+ size_t nElementSize = rStrm.GetRoot().GetBiff() == EXC_BIFF8 ? 4 : 2;
+ size_t nAvailableBytes = rStrm.GetRecLeft();
+ size_t nMaxElements = nAvailableBytes / nElementSize;
+ if (nRunCount > nMaxElements)
+ {
+ SAL_WARN("sc.filter", "XclImpString::ReadFormats - more formats claimed than stream could contain");
+ rStrm.SetSvStreamError(SVSTREAM_FILEFORMAT_ERROR);
+ return;
+ }
+
+ rFormats.reserve( nRunCount );
+ /* #i33341# real life -- same character index may occur several times
+ -> use AppendFormat() to validate formats */
+ if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt16 nChar = rStrm.ReaduInt16();
+ sal_uInt16 nFontIdx = rStrm.ReaduInt16();
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+ }
+ else
+ {
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt8 nChar = rStrm.ReaduInt8();
+ sal_uInt8 nFontIdx = rStrm.ReaduInt8();
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+ }
+}
+
+void XclImpString::ReadObjFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nFormatSize )
+{
+ // number of formatting runs, each takes 8 bytes
+ sal_uInt16 nRunCount = nFormatSize / 8;
+ rFormats.clear();
+ rFormats.reserve( nRunCount );
+ for( sal_uInt16 nIdx = 0; nIdx < nRunCount; ++nIdx )
+ {
+ sal_uInt16 nChar = rStrm.ReaduInt16();
+ sal_uInt16 nFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 4 );
+ AppendFormat( rFormats, nChar, nFontIdx );
+ }
+}
+
+// String iterator ============================================================
+
+XclImpStringIterator::XclImpStringIterator( const XclImpString& rString ) :
+ mrText( rString.GetText() ),
+ mrFormats( rString.GetFormats() ),
+ mnPortion( 0 ),
+ mnTextBeg( 0 ),
+ mnTextEnd( 0 ),
+ mnFormatsBeg( 0 ),
+ mnFormatsEnd( 0 )
+{
+ // first portion is formatted, adjust vector index to next portion
+ if( !mrFormats.empty() && (mrFormats.front().mnChar == 0) )
+ ++mnFormatsEnd;
+ // find end position of the first portion
+ mnTextEnd = (mnFormatsEnd < mrFormats.size() ?
+ mrFormats[ mnFormatsEnd ].mnChar : mrText.getLength() );
+}
+
+OUString XclImpStringIterator::GetPortionText() const
+{
+ return mrText.copy( mnTextBeg, mnTextEnd - mnTextBeg );
+}
+
+sal_uInt16 XclImpStringIterator::GetPortionFont() const
+{
+ return (mnFormatsBeg < mnFormatsEnd) ? mrFormats[ mnFormatsBeg ].mnFontIdx : EXC_FONT_NOTFOUND;
+}
+
+XclImpStringIterator& XclImpStringIterator::operator++()
+{
+ if( Is() )
+ {
+ ++mnPortion;
+ do
+ {
+ // indexes into vector of formatting runs
+ if( mnFormatsBeg < mnFormatsEnd )
+ ++mnFormatsBeg;
+ if( mnFormatsEnd < mrFormats.size() )
+ ++mnFormatsEnd;
+ // character positions of next portion
+ mnTextBeg = mnTextEnd;
+ mnTextEnd = (mnFormatsEnd < mrFormats.size()) ?
+ mrFormats[ mnFormatsEnd ].mnChar : mrText.getLength();
+ }
+ while( Is() && (mnTextBeg == mnTextEnd) );
+ }
+ return *this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xistyle.cxx b/sc/source/filter/excel/xistyle.cxx
new file mode 100644
index 000000000..7361c7ee6
--- /dev/null
+++ b/sc/source/filter/excel/xistyle.cxx
@@ -0,0 +1,2084 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xistyle.hxx>
+#include <sfx2/objsh.hxx>
+#include <svtools/ctrltool.hxx>
+#include <editeng/editobj.hxx>
+#include <scitems.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/editids.hrc>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/outdev.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <docpool.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <attarray.hxx>
+#include <xladdress.hxx>
+#include <xlcontent.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <xistream.hxx>
+#include <xicontent.hxx>
+
+#include <root.hxx>
+#include <colrowst.hxx>
+
+#include <string_view>
+#include <vector>
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <svl/numformat.hxx>
+#include <o3tl/string_view.hxx>
+
+using ::std::vector;
+using namespace ::com::sun::star;
+
+typedef ::cppu::WeakImplHelper< container::XIndexAccess > XIndexAccess_BASE;
+typedef ::std::vector< Color > ColorVec;
+
+namespace {
+
+class PaletteIndex : public XIndexAccess_BASE
+{
+public:
+ explicit PaletteIndex( ColorVec&& rColorTable ) : maColor( std::move(rColorTable) ) {}
+
+ // Methods XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount() override
+ {
+ return maColor.size();
+ }
+
+ virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
+ {
+ //--Index; // apparently the palette is already 1 based
+ return uno::Any( sal_Int32( maColor[ Index ] ) );
+ }
+
+ // Methods XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override
+ {
+ return ::cppu::UnoType<sal_Int32>::get();
+ }
+ virtual sal_Bool SAL_CALL hasElements() override
+ {
+ return (!maColor.empty());
+ }
+
+private:
+ ColorVec maColor;
+};
+
+}
+
+void
+XclImpPalette::ExportPalette()
+{
+ SfxObjectShell* pDocShell = mrRoot.GetDocShell();
+ if(!pDocShell)
+ return;
+
+ // copy values in color palette
+ sal_Int16 nColors = maColorTable.size();
+ ColorVec aColors;
+ aColors.resize( nColors );
+ for( sal_uInt16 nIndex = 0; nIndex < nColors; ++nIndex )
+ aColors[ nIndex ] = GetColor( nIndex );
+
+ uno::Reference< beans::XPropertySet > xProps( pDocShell->GetModel(), uno::UNO_QUERY );
+ if ( xProps.is() )
+ {
+ uno::Reference< container::XIndexAccess > xIndex( new PaletteIndex( std::move(aColors) ) );
+ xProps->setPropertyValue( "ColorPalette", uno::Any( xIndex ) );
+ }
+
+}
+// PALETTE record - color information =========================================
+
+XclImpPalette::XclImpPalette( const XclImpRoot& rRoot ) :
+ XclDefaultPalette( rRoot ), mrRoot( rRoot )
+{
+}
+
+void XclImpPalette::Initialize()
+{
+ maColorTable.clear();
+}
+
+Color XclImpPalette::GetColor( sal_uInt16 nXclIndex ) const
+{
+ if( nXclIndex >= EXC_COLOR_USEROFFSET )
+ {
+ sal_uInt32 nIx = nXclIndex - EXC_COLOR_USEROFFSET;
+ if( nIx < maColorTable.size() )
+ return maColorTable[ nIx ];
+ }
+ return GetDefColor( nXclIndex );
+}
+
+void XclImpPalette::ReadPalette( XclImpStream& rStrm )
+{
+ sal_uInt16 nCount;
+ nCount = rStrm.ReaduInt16();
+
+ const size_t nMinRecordSize = 4;
+ const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize;
+ if (nCount > nMaxRecords)
+ {
+ SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nCount << " claimed, truncating");
+ nCount = nMaxRecords;
+ }
+
+ maColorTable.resize( nCount );
+ Color aColor;
+ for( sal_uInt16 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ rStrm >> aColor;
+ maColorTable[ nIndex ] = aColor;
+ }
+ ExportPalette();
+}
+
+// FONT record - font information =============================================
+XclImpFont::XclImpFont( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mbHasCharSet( false ),
+ mbHasWstrn( true ),
+ mbHasAsian( false ),
+ mbHasCmplx( false )
+{
+ SetAllUsedFlags( false );
+}
+
+XclImpFont::XclImpFont( const XclImpRoot& rRoot, const XclFontData& rFontData ) :
+ XclImpRoot( rRoot )
+{
+ SetFontData( rFontData, false );
+}
+
+void XclImpFont::SetAllUsedFlags( bool bUsed )
+{
+ mbFontNameUsed = mbHeightUsed = mbColorUsed = mbWeightUsed = mbEscapemUsed =
+ mbUnderlUsed = mbItalicUsed = mbStrikeUsed = mbOutlineUsed = mbShadowUsed = bUsed;
+}
+
+void XclImpFont::SetFontData( const XclFontData& rFontData, bool bHasCharSet )
+{
+ maData = rFontData;
+ mbHasCharSet = bHasCharSet;
+ if( !maData.maStyle.isEmpty() )
+ {
+ if( SfxObjectShell* pDocShell = GetDocShell() )
+ {
+ if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
+ pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
+ {
+ if( const FontList* pFontList = pInfoItem->GetFontList() )
+ {
+ FontMetric aFontMetric( pFontList->Get( maData.maName, maData.maStyle ) );
+ maData.SetScWeight( aFontMetric.GetWeight() );
+ maData.SetScPosture( aFontMetric.GetItalic() );
+ }
+ }
+ }
+ maData.maStyle.clear();
+ }
+ GuessScriptType();
+ SetAllUsedFlags( true );
+}
+
+rtl_TextEncoding XclImpFont::GetFontEncoding() const
+{
+ // #i63105# use text encoding from FONT record
+ // #i67768# BIFF2-BIFF4 FONT records do not contain character set
+ rtl_TextEncoding eFontEnc = mbHasCharSet ? maData.GetFontEncoding() : GetTextEncoding();
+ return (eFontEnc == RTL_TEXTENCODING_DONTKNOW) ? GetTextEncoding() : eFontEnc;
+}
+
+void XclImpFont::ReadFont( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ ReadFontData2( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ ReadFontData2( rStrm );
+ ReadFontColor( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF5:
+ ReadFontData5( rStrm );
+ ReadFontName2( rStrm );
+ break;
+ case EXC_BIFF8:
+ ReadFontData5( rStrm );
+ ReadFontName8( rStrm );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ return;
+ }
+ GuessScriptType();
+ SetAllUsedFlags( true );
+}
+
+void XclImpFont::ReadEfont( XclImpStream& rStrm )
+{
+ ReadFontColor( rStrm );
+}
+
+void XclImpFont::ReadCFFontBlock( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
+ if( GetBiff() != EXC_BIFF8 )
+ return;
+
+ rStrm.Ignore( 64 );
+ sal_uInt32 nHeight = rStrm.ReaduInt32();
+ sal_uInt32 nStyle = rStrm.ReaduInt32();
+ sal_uInt16 nWeight = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 ); //nEscapem
+ sal_uInt8 nUnderl = rStrm.ReaduInt8();
+ rStrm.Ignore( 3 );
+ sal_uInt32 nColor = rStrm.ReaduInt32();
+ rStrm.Ignore( 4 );
+ sal_uInt32 nFontFlags1 = rStrm.ReaduInt32();
+ rStrm.Ignore( 4 ); //nFontFlags2
+ sal_uInt32 nFontFlags3 = rStrm.ReaduInt32();
+ rStrm.Ignore( 18 );
+
+ if( (mbHeightUsed = (nHeight <= 0x7FFF)) )
+ maData.mnHeight = static_cast< sal_uInt16 >( nHeight );
+ if( (mbWeightUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE ) && (nWeight < 0x7FFF)) )
+ maData.mnWeight = nWeight;
+ if( (mbItalicUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE )) )
+ maData.mbItalic = ::get_flag( nStyle, EXC_CF_FONT_STYLE );
+ if( (mbUnderlUsed = !::get_flag( nFontFlags3, EXC_CF_FONT_UNDERL ) && (nUnderl <= 0x7F)) )
+ maData.mnUnderline = nUnderl;
+ if( (mbColorUsed = (nColor <= 0x7FFF)) )
+ maData.maColor = GetPalette().GetColor( static_cast< sal_uInt16 >( nColor ) );
+ if( (mbStrikeUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT )) )
+ maData.mbStrikeout = ::get_flag( nStyle, EXC_CF_FONT_STRIKEOUT );
+}
+
+void XclImpFont::FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType, bool bSkipPoolDefs ) const
+{
+ // true = edit engine Which-IDs (EE_CHAR_*); false = Calc Which-IDs (ATTR_*)
+ bool bEE = eType != XclFontItemType::Cell;
+
+// item = the item to put into the item set
+// sc_which = the Calc Which-ID of the item
+// ee_which = the edit engine Which-ID of the item
+#define PUTITEM( item, sc_which, ee_which ) \
+ ScfTools::PutItem( rItemSet, item, (bEE ? (static_cast<sal_uInt16>(ee_which)) : (sc_which)), bSkipPoolDefs )
+
+// Font item
+ if( mbFontNameUsed )
+ {
+ rtl_TextEncoding eFontEnc = maData.GetFontEncoding();
+ rtl_TextEncoding eTempTextEnc = (bEE && (eFontEnc == GetTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+
+ //add corresponding pitch for FontFamily
+ FontPitch ePitch = PITCH_DONTKNOW;
+ FontFamily eFtFamily = maData.GetScFamily( GetTextEncoding() );
+ switch( eFtFamily ) //refer http://msdn.microsoft.com/en-us/library/aa246306(v=VS.60).aspx
+ {
+ case FAMILY_ROMAN: ePitch = PITCH_VARIABLE; break;
+ case FAMILY_SWISS: ePitch = PITCH_VARIABLE; break;
+ case FAMILY_MODERN: ePitch = PITCH_FIXED; break;
+ default: break;
+ }
+ SvxFontItem aFontItem( eFtFamily , maData.maName, OUString(), ePitch, eTempTextEnc, ATTR_FONT );
+
+ // set only for valid script types
+ if( mbHasWstrn )
+ PUTITEM( aFontItem, ATTR_FONT, EE_CHAR_FONTINFO );
+ if( mbHasAsian )
+ PUTITEM( aFontItem, ATTR_CJK_FONT, EE_CHAR_FONTINFO_CJK );
+ if( mbHasCmplx )
+ PUTITEM( aFontItem, ATTR_CTL_FONT, EE_CHAR_FONTINFO_CTL );
+ }
+
+// Font height (for all script types)
+ if( mbHeightUsed )
+ {
+ sal_Int32 nHeight = maData.mnHeight;
+ if( bEE && (eType != XclFontItemType::HeaderFooter) ) // do not convert header/footer height
+ nHeight = convertTwipToMm100(nHeight);
+
+ SvxFontHeightItem aHeightItem( nHeight, 100, ATTR_FONT_HEIGHT );
+ PUTITEM( aHeightItem, ATTR_FONT_HEIGHT, EE_CHAR_FONTHEIGHT );
+ PUTITEM( aHeightItem, ATTR_CJK_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CJK );
+ PUTITEM( aHeightItem, ATTR_CTL_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CTL );
+ }
+
+// Font color - pass AUTO_COL to item
+ if( mbColorUsed )
+ PUTITEM( SvxColorItem( maData.maColor, ATTR_FONT_COLOR ), ATTR_FONT_COLOR, EE_CHAR_COLOR );
+
+// Font weight (for all script types)
+ if( mbWeightUsed )
+ {
+ SvxWeightItem aWeightItem( maData.GetScWeight(), ATTR_FONT_WEIGHT );
+ PUTITEM( aWeightItem, ATTR_FONT_WEIGHT, EE_CHAR_WEIGHT );
+ PUTITEM( aWeightItem, ATTR_CJK_FONT_WEIGHT, EE_CHAR_WEIGHT_CJK );
+ PUTITEM( aWeightItem, ATTR_CTL_FONT_WEIGHT, EE_CHAR_WEIGHT_CTL );
+ }
+
+// Font underline
+ if( mbUnderlUsed )
+ {
+ SvxUnderlineItem aUnderlItem( maData.GetScUnderline(), ATTR_FONT_UNDERLINE );
+ PUTITEM( aUnderlItem, ATTR_FONT_UNDERLINE, EE_CHAR_UNDERLINE );
+ }
+
+// Font posture (for all script types)
+ if( mbItalicUsed )
+ {
+ SvxPostureItem aPostItem( maData.GetScPosture(), ATTR_FONT_POSTURE );
+ PUTITEM( aPostItem, ATTR_FONT_POSTURE, EE_CHAR_ITALIC );
+ PUTITEM( aPostItem, ATTR_CJK_FONT_POSTURE, EE_CHAR_ITALIC_CJK );
+ PUTITEM( aPostItem, ATTR_CTL_FONT_POSTURE, EE_CHAR_ITALIC_CTL );
+ }
+
+// Boolean attributes crossed out, contoured, shadowed
+ if( mbStrikeUsed )
+ PUTITEM( SvxCrossedOutItem( maData.GetScStrikeout(), ATTR_FONT_CROSSEDOUT ), ATTR_FONT_CROSSEDOUT, EE_CHAR_STRIKEOUT );
+ if( mbOutlineUsed )
+ PUTITEM( SvxContourItem( maData.mbOutline, ATTR_FONT_CONTOUR ), ATTR_FONT_CONTOUR, EE_CHAR_OUTLINE );
+ if( mbShadowUsed )
+ PUTITEM( SvxShadowedItem( maData.mbShadow, ATTR_FONT_SHADOWED ), ATTR_FONT_SHADOWED, EE_CHAR_SHADOW );
+
+// Super-/subscript: only on edit engine objects
+ if( mbEscapemUsed && bEE )
+ rItemSet.Put( SvxEscapementItem( maData.GetScEscapement(), EE_CHAR_ESCAPEMENT ) );
+
+#undef PUTITEM
+}
+
+void XclImpFont::WriteFontProperties( ScfPropertySet& rPropSet,
+ XclFontPropSetType eType, const Color* pFontColor ) const
+{
+ GetFontPropSetHelper().WriteFontProperties(
+ rPropSet, eType, maData, mbHasWstrn, mbHasAsian, mbHasCmplx, pFontColor );
+}
+
+void XclImpFont::ReadFontData2( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ maData.mnHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+
+ maData.mnWeight = ::get_flagvalue( nFlags, EXC_FONTATTR_BOLD, EXC_FONTWGHT_BOLD, EXC_FONTWGHT_NORMAL );
+ maData.mnUnderline = ::get_flagvalue( nFlags, EXC_FONTATTR_UNDERLINE, EXC_FONTUNDERL_SINGLE, EXC_FONTUNDERL_NONE );
+ maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC );
+ maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT );
+ maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE );
+ maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW );
+ mbHasCharSet = false;
+}
+
+void XclImpFont::ReadFontData5( XclImpStream& rStrm )
+{
+ sal_uInt16 nFlags;
+
+ maData.mnHeight = rStrm.ReaduInt16();
+ nFlags = rStrm.ReaduInt16();
+ ReadFontColor( rStrm );
+ maData.mnWeight = rStrm.ReaduInt16();
+ maData.mnEscapem = rStrm.ReaduInt16();
+ maData.mnUnderline = rStrm.ReaduInt8();
+ maData.mnFamily = rStrm.ReaduInt8();
+ maData.mnCharSet = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+
+ maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC );
+ maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT );
+ maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE );
+ maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW );
+ mbHasCharSet = maData.mnCharSet != 0;
+}
+
+void XclImpFont::ReadFontColor( XclImpStream& rStrm )
+{
+ maData.maColor = GetPalette().GetColor( rStrm.ReaduInt16() );
+}
+
+void XclImpFont::ReadFontName2( XclImpStream& rStrm )
+{
+ maData.maName = rStrm.ReadByteString( false );
+}
+
+void XclImpFont::ReadFontName8( XclImpStream& rStrm )
+{
+ maData.maName = rStrm.ReadUniString( rStrm.ReaduInt8() );
+}
+
+void XclImpFont::GuessScriptType()
+{
+ mbHasWstrn = true;
+ mbHasAsian = mbHasCmplx = false;
+
+ // find the script types for which the font contains characters
+ OutputDevice* pPrinter = GetPrinter();
+ if(!pPrinter)
+ return;
+
+ vcl::Font aFont( maData.maName, Size( 0, 10 ) );
+ FontCharMapRef xFontCharMap;
+
+ pPrinter->SetFont( aFont );
+ if( !pPrinter->GetFontCharMap( xFontCharMap ) )
+ return;
+
+ // CJK fonts
+ mbHasAsian =
+ xFontCharMap->HasChar( 0x3041 ) || // 3040-309F: Hiragana
+ xFontCharMap->HasChar( 0x30A1 ) || // 30A0-30FF: Katakana
+ xFontCharMap->HasChar( 0x3111 ) || // 3100-312F: Bopomofo
+ xFontCharMap->HasChar( 0x3131 ) || // 3130-318F: Hangul Compatibility Jamo
+ xFontCharMap->HasChar( 0x3301 ) || // 3300-33FF: CJK Compatibility
+ xFontCharMap->HasChar( 0x3401 ) || // 3400-4DBF: CJK Unified Ideographs Extension A
+ xFontCharMap->HasChar( 0x4E01 ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFontCharMap->HasChar( 0x7E01 ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFontCharMap->HasChar( 0xA001 ) || // A001-A48F: Yi Syllables
+ xFontCharMap->HasChar( 0xAC01 ) || // AC00-D7AF: Hangul Syllables
+ xFontCharMap->HasChar( 0xCC01 ) || // AC00-D7AF: Hangul Syllables
+ xFontCharMap->HasChar( 0xF901 ) || // F900-FAFF: CJK Compatibility Ideographs
+ xFontCharMap->HasChar( 0xFF71 ); // FF00-FFEF: Halfwidth/Fullwidth Forms
+ // CTL fonts
+ mbHasCmplx =
+ xFontCharMap->HasChar( 0x05D1 ) || // 0590-05FF: Hebrew
+ xFontCharMap->HasChar( 0x0631 ) || // 0600-06FF: Arabic
+ xFontCharMap->HasChar( 0x0721 ) || // 0700-074F: Syriac
+ xFontCharMap->HasChar( 0x0911 ) || // 0900-0DFF: Indic scripts
+ xFontCharMap->HasChar( 0x0E01 ) || // 0E00-0E7F: Thai
+ xFontCharMap->HasChar( 0xFB21 ) || // FB1D-FB4F: Hebrew Presentation Forms
+ xFontCharMap->HasChar( 0xFB51 ) || // FB50-FDFF: Arabic Presentation Forms-A
+ xFontCharMap->HasChar( 0xFE71 ); // FE70-FEFF: Arabic Presentation Forms-B
+ // Western fonts
+ mbHasWstrn = (!mbHasAsian && !mbHasCmplx) || xFontCharMap->HasChar( 'A' );
+}
+
+XclImpFontBuffer::XclImpFontBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ maFont4( rRoot ),
+ maCtrlFont( rRoot )
+{
+ Initialize();
+
+ // default font for form controls without own font information
+ XclFontData aCtrlFontData;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ aCtrlFontData.maName = "Helv";
+ aCtrlFontData.mnHeight = 160;
+ aCtrlFontData.mnWeight = EXC_FONTWGHT_BOLD;
+ break;
+ case EXC_BIFF8:
+ aCtrlFontData.maName = "Tahoma";
+ aCtrlFontData.mnHeight = 160;
+ aCtrlFontData.mnWeight = EXC_FONTWGHT_NORMAL;
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+ maCtrlFont.SetFontData( aCtrlFontData, false );
+}
+
+void XclImpFontBuffer::Initialize()
+{
+ maFontList.clear();
+
+ // application font for column width calculation, later filled with first font from font list
+ XclFontData aAppFontData;
+ aAppFontData.maName = "Arial";
+ aAppFontData.mnHeight = 200;
+ aAppFontData.mnWeight = EXC_FONTWGHT_NORMAL;
+ UpdateAppFont( aAppFontData, false );
+}
+
+const XclImpFont* XclImpFontBuffer::GetFont( sal_uInt16 nFontIndex ) const
+{
+ /* Font with index 4 is not stored in an Excel file, but used e.g. by
+ BIFF5 form pushbutton objects. It is the bold default font.
+ This also means that entries above 4 are out by one in the list. */
+
+ if (nFontIndex == 4)
+ return &maFont4;
+
+ if (nFontIndex < 4)
+ {
+ // Font ID is zero-based when it's less than 4.
+ return nFontIndex >= maFontList.size() ? nullptr : &maFontList[nFontIndex];
+ }
+
+ // Font ID is greater than 4. It is now 1-based.
+ return nFontIndex > maFontList.size() ? nullptr : &maFontList[nFontIndex-1];
+}
+
+void XclImpFontBuffer::ReadFont( XclImpStream& rStrm )
+{
+ maFontList.emplace_back( GetRoot() );
+ XclImpFont& rFont = maFontList.back();
+ rFont.ReadFont( rStrm );
+
+ if( maFontList.size() == 1 )
+ {
+ UpdateAppFont( rFont.GetFontData(), rFont.HasCharSet() );
+ }
+}
+
+void XclImpFontBuffer::ReadEfont( XclImpStream& rStrm )
+{
+ if( !maFontList.empty() )
+ maFontList.back().ReadEfont( rStrm );
+}
+
+void XclImpFontBuffer::FillToItemSet(
+ SfxItemSet& rItemSet, XclFontItemType eType,
+ sal_uInt16 nFontIdx, bool bSkipPoolDefs ) const
+{
+ if( const XclImpFont* pFont = GetFont( nFontIdx ) )
+ pFont->FillToItemSet( rItemSet, eType, bSkipPoolDefs );
+}
+
+void XclImpFontBuffer::WriteFontProperties( ScfPropertySet& rPropSet,
+ XclFontPropSetType eType, sal_uInt16 nFontIdx, const Color* pFontColor ) const
+{
+ if( const XclImpFont* pFont = GetFont( nFontIdx ) )
+ pFont->WriteFontProperties( rPropSet, eType, pFontColor );
+}
+
+void XclImpFontBuffer::WriteDefaultCtrlFontProperties( ScfPropertySet& rPropSet ) const
+{
+ maCtrlFont.WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL );
+}
+
+void XclImpFontBuffer::UpdateAppFont( const XclFontData& rFontData, bool bHasCharSet )
+{
+ maAppFont = rFontData;
+ // #i3006# Calculate the width of '0' from first font and current printer.
+ SetCharWidth( maAppFont );
+
+ // font 4 is bold font 0
+ XclFontData aFont4Data( maAppFont );
+ aFont4Data.mnWeight = EXC_FONTWGHT_BOLD;
+ maFont4.SetFontData( aFont4Data, bHasCharSet );
+}
+
+// FORMAT record - number formats =============================================
+
+XclImpNumFmtBuffer::XclImpNumFmtBuffer( const XclImpRoot& rRoot ) :
+ XclNumFmtBuffer( rRoot ),
+ XclImpRoot( rRoot ),
+ mnNextXclIdx( 0 )
+{
+}
+
+void XclImpNumFmtBuffer::Initialize()
+{
+ maIndexMap.clear();
+ mnNextXclIdx = 0;
+ InitializeImport(); // base class
+}
+
+void XclImpNumFmtBuffer::ReadFormat( XclImpStream& rStrm )
+{
+ OUString aFormat;
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2:
+ case EXC_BIFF3:
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF4:
+ rStrm.Ignore( 2 ); // in BIFF4 the index field exists, but is undefined
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF5:
+ mnNextXclIdx = rStrm.ReaduInt16();
+ aFormat = rStrm.ReadByteString( false );
+ break;
+
+ case EXC_BIFF8:
+ mnNextXclIdx = rStrm.ReaduInt16();
+ aFormat = rStrm.ReadUniString();
+ break;
+
+ default:
+ DBG_ERROR_BIFF();
+ return;
+ }
+
+ if( mnNextXclIdx < 0xFFFF )
+ {
+ InsertFormat( mnNextXclIdx, aFormat );
+ ++mnNextXclIdx;
+ }
+}
+
+sal_uInt16 XclImpNumFmtBuffer::ReadCFFormat( XclImpStream& rStrm, bool bIFmt )
+{
+ // internal number format ?
+ if(bIFmt)
+ {
+ rStrm.Ignore(1);
+ sal_uInt8 nIndex;
+ nIndex = rStrm.ReaduInt8();
+ return nIndex;
+ }
+ else
+ {
+ OUString aFormat = rStrm.ReadUniString();
+ InsertFormat( mnNextXclIdx, aFormat );
+ ++mnNextXclIdx;
+ return mnNextXclIdx - 1;
+ }
+}
+
+void XclImpNumFmtBuffer::CreateScFormats()
+{
+ OSL_ENSURE( maIndexMap.empty(), "XclImpNumFmtBuffer::CreateScFormats - already created" );
+
+ SvNumberFormatter& rFormatter = GetFormatter();
+ for( const auto& [rXclNumFmt, rNumFmt] : GetFormatMap() )
+ {
+ // insert/convert the Excel number format
+ sal_uInt32 nKey;
+ if( !rNumFmt.maFormat.isEmpty() )
+ {
+ OUString aFormat( rNumFmt.maFormat );
+ sal_Int32 nCheckPos;
+ SvNumFormatType nType = SvNumFormatType::DEFINED;
+ rFormatter.PutandConvertEntry( aFormat, nCheckPos,
+ nType, nKey, LANGUAGE_ENGLISH_US, rNumFmt.meLanguage, false);
+ }
+ else
+ nKey = rFormatter.GetFormatIndex( rNumFmt.meOffset, rNumFmt.meLanguage );
+
+ // insert the resulting format key into the Excel->Calc index map
+ maIndexMap[ rXclNumFmt ] = nKey;
+ }
+}
+
+sal_uInt32 XclImpNumFmtBuffer::GetScFormat( sal_uInt16 nXclNumFmt ) const
+{
+ XclImpIndexMap::const_iterator aIt = maIndexMap.find( nXclNumFmt );
+ return (aIt != maIndexMap.end()) ? aIt->second : NUMBERFORMAT_ENTRY_NOT_FOUND;
+}
+
+void XclImpNumFmtBuffer::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nXclNumFmt, bool bSkipPoolDefs ) const
+{
+ sal_uInt32 nScNumFmt = GetScFormat( nXclNumFmt );
+ if( nScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND )
+ nScNumFmt = GetStdScNumFmt();
+ FillScFmtToItemSet( rItemSet, nScNumFmt, bSkipPoolDefs );
+}
+
+void XclImpNumFmtBuffer::FillScFmtToItemSet( SfxItemSet& rItemSet, sal_uInt32 nScNumFmt, bool bSkipPoolDefs ) const
+{
+ OSL_ENSURE( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND, "XclImpNumFmtBuffer::FillScFmtToItemSet - invalid number format" );
+ ScfTools::PutItem( rItemSet, SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ), bSkipPoolDefs );
+ if( rItemSet.GetItemState( ATTR_VALUE_FORMAT, false ) == SfxItemState::SET )
+ ScGlobal::AddLanguage( rItemSet, GetFormatter() );
+}
+
+// XF, STYLE record - Cell formatting =========================================
+
+void XclImpCellProt::FillFromXF2( sal_uInt8 nNumFmt )
+{
+ mbLocked = ::get_flag( nNumFmt, EXC_XF2_LOCKED );
+ mbHidden = ::get_flag( nNumFmt, EXC_XF2_HIDDEN );
+}
+
+void XclImpCellProt::FillFromXF3( sal_uInt16 nProt )
+{
+ mbLocked = ::get_flag( nProt, EXC_XF_LOCKED );
+ mbHidden = ::get_flag( nProt, EXC_XF_HIDDEN );
+}
+
+void XclImpCellProt::FillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ ScfTools::PutItem( rItemSet, ScProtectionAttr( mbLocked, mbHidden ), bSkipPoolDefs );
+}
+
+void XclImpCellAlign::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nFlags, 0, 3 );
+}
+
+void XclImpCellAlign::FillFromXF3( sal_uInt16 nAlign )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK ); // new in BIFF3
+}
+
+void XclImpCellAlign::FillFromXF4( sal_uInt16 nAlign )
+{
+ FillFromXF3( nAlign );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 2 ); // new in BIFF4
+ mnOrient = ::extract_value< sal_uInt8 >( nAlign, 6, 2 ); // new in BIFF4
+}
+
+void XclImpCellAlign::FillFromXF5( sal_uInt16 nAlign )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK );
+ mnOrient = ::extract_value< sal_uInt8 >( nAlign, 8, 2 );
+}
+
+void XclImpCellAlign::FillFromXF8( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib )
+{
+ mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK );
+ mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 ); // new in BIFF8
+ mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 ); // new in BIFF8
+ mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK ); // new in BIFF8
+ mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 ); // new in BIFF8
+}
+
+void XclImpCellAlign::FillFromCF( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib )
+{
+ mnHorAlign = extract_value< sal_uInt8 >( nAlign, 0, 3 );
+ mbLineBreak = get_flag< sal_uInt8 >( nAlign, EXC_XF_LINEBREAK );
+ mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
+ mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 );
+ mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 );
+ mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK );
+ mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 );
+}
+
+void XclImpCellAlign::FillToItemSet( SfxItemSet& rItemSet, const XclImpFont* pFont, bool bSkipPoolDefs ) const
+{
+ // horizontal alignment
+ ScfTools::PutItem( rItemSet, SvxHorJustifyItem( GetScHorAlign(), ATTR_HOR_JUSTIFY ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( GetScHorJustifyMethod(), ATTR_HOR_JUSTIFY_METHOD ), bSkipPoolDefs );
+
+ // text wrap (#i74508# always if vertical alignment is justified or distributed)
+ bool bLineBreak = mbLineBreak || (mnVerAlign == EXC_XF_VER_JUSTIFY) || (mnVerAlign == EXC_XF_VER_DISTRIB);
+ ScfTools::PutItem( rItemSet, ScLineBreakCell( bLineBreak ), bSkipPoolDefs );
+
+ // vertical alignment
+ ScfTools::PutItem( rItemSet, SvxVerJustifyItem( GetScVerAlign(), ATTR_VER_JUSTIFY ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( GetScVerJustifyMethod(), ATTR_VER_JUSTIFY_METHOD ), bSkipPoolDefs );
+
+ // indent
+ sal_uInt16 nScIndent = mnIndent * 200; // 1 Excel unit == 10 pt == 200 twips
+ ScfTools::PutItem( rItemSet, ScIndentItem( nScIndent ), bSkipPoolDefs );
+
+ // shrink to fit
+ ScfTools::PutItem( rItemSet, ScShrinkToFitCell( mbShrink ), bSkipPoolDefs );
+
+ // text orientation/rotation (BIFF2-BIFF7 sets mnOrient)
+ sal_uInt8 nXclRot = (mnOrient == EXC_ORIENT_NONE) ? mnRotation : XclTools::GetXclRotFromOrient( mnOrient );
+ bool bStacked = (nXclRot == EXC_ROT_STACKED);
+ ScfTools::PutItem( rItemSet, ScVerticalStackCell( bStacked ), bSkipPoolDefs );
+ // set an angle in the range from -90 to 90 degrees
+ Degree100 nAngle = XclTools::GetScRotation( nXclRot, 0_deg100 );
+ ScfTools::PutItem( rItemSet, ScRotateValueItem( nAngle ), bSkipPoolDefs );
+ // set "Use asian vertical layout", if cell is stacked and font contains CKJ characters
+ bool bAsianVert = bStacked && pFont && pFont->HasAsianChars();
+ ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_VERTICAL_ASIAN, bAsianVert ), bSkipPoolDefs );
+
+ // CTL text direction
+ ScfTools::PutItem( rItemSet, SvxFrameDirectionItem( GetScFrameDir(), ATTR_WRITINGDIR ), bSkipPoolDefs );
+}
+
+XclImpCellBorder::XclImpCellBorder()
+{
+ SetUsedFlags( false, false );
+}
+
+void XclImpCellBorder::SetUsedFlags( bool bOuterUsed, bool bDiagUsed )
+{
+ mbLeftUsed = mbRightUsed = mbTopUsed = mbBottomUsed = bOuterUsed;
+ mbDiagUsed = bDiagUsed;
+}
+
+void XclImpCellBorder::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnLeftLine = ::get_flagvalue( nFlags, EXC_XF2_LEFTLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnRightLine = ::get_flagvalue( nFlags, EXC_XF2_RIGHTLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnTopLine = ::get_flagvalue( nFlags, EXC_XF2_TOPLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnBottomLine = ::get_flagvalue( nFlags, EXC_XF2_BOTTOMLINE, EXC_LINE_THIN, EXC_LINE_NONE );
+ mnLeftColor = mnRightColor = mnTopColor = mnBottomColor = EXC_COLOR_BIFF2_BLACK;
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF3( sal_uInt32 nBorder )
+{
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 );
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 8, 3 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nBorder, 16, 3 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 24, 3 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 3, 5 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 11, 5 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nBorder, 19, 5 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 27, 5 );
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF5( sal_uInt32 nBorder, sal_uInt32 nArea )
+{
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 );
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 3, 3 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nArea, 22, 3 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 6, 3 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 9, 7 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 16, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nArea, 25, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 23, 7 );
+ SetUsedFlags( true, false );
+}
+
+void XclImpCellBorder::FillFromXF8( sal_uInt32 nBorder1, sal_uInt32 nBorder2 )
+{
+ mnLeftLine = ::extract_value< sal_uInt8 >( nBorder1, 0, 4 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nBorder1, 4, 4 );
+ mnTopLine = ::extract_value< sal_uInt8 >( nBorder1, 8, 4 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nBorder1, 12, 4 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nBorder1, 16, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nBorder1, 23, 7 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nBorder2, 0, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nBorder2, 7, 7 );
+ mbDiagTLtoBR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_TL_TO_BR );
+ mbDiagBLtoTR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_BL_TO_TR );
+ if( mbDiagTLtoBR || mbDiagBLtoTR )
+ {
+ mnDiagLine = ::extract_value< sal_uInt8 >( nBorder2, 21, 4 );
+ mnDiagColor = ::extract_value< sal_uInt16 >( nBorder2, 14, 7 );
+ }
+ SetUsedFlags( true, true );
+}
+
+void XclImpCellBorder::FillFromCF8( sal_uInt16 nLineStyle, sal_uInt32 nLineColor, sal_uInt32 nFlags )
+{
+ mnLeftLine = ::extract_value< sal_uInt8 >( nLineStyle, 0, 4 );
+ mnRightLine = ::extract_value< sal_uInt8 >( nLineStyle, 4, 4 );
+ mnTopLine = ::extract_value< sal_uInt8 >( nLineStyle, 8, 4 );
+ mnBottomLine = ::extract_value< sal_uInt8 >( nLineStyle, 12, 4 );
+ mnLeftColor = ::extract_value< sal_uInt16 >( nLineColor, 0, 7 );
+ mnRightColor = ::extract_value< sal_uInt16 >( nLineColor, 7, 7 );
+ mnTopColor = ::extract_value< sal_uInt16 >( nLineColor, 16, 7 );
+ mnBottomColor = ::extract_value< sal_uInt16 >( nLineColor, 23, 7 );
+ mbLeftUsed = !::get_flag( nFlags, EXC_CF_BORDER_LEFT );
+ mbRightUsed = !::get_flag( nFlags, EXC_CF_BORDER_RIGHT );
+ mbTopUsed = !::get_flag( nFlags, EXC_CF_BORDER_TOP );
+ mbBottomUsed = !::get_flag( nFlags, EXC_CF_BORDER_BOTTOM );
+ mbDiagUsed = false;
+}
+
+bool XclImpCellBorder::HasAnyOuterBorder() const
+{
+ return
+ (mbLeftUsed && (mnLeftLine != EXC_LINE_NONE)) ||
+ (mbRightUsed && (mnRightLine != EXC_LINE_NONE)) ||
+ (mbTopUsed && (mnTopLine != EXC_LINE_NONE)) ||
+ (mbBottomUsed && (mnBottomLine != EXC_LINE_NONE));
+}
+
+namespace {
+
+/** Converts the passed line style to a ::editeng::SvxBorderLine, or returns false, if style is "no line". */
+bool lclConvertBorderLine( ::editeng::SvxBorderLine& rLine, const XclImpPalette& rPalette, sal_uInt8 nXclLine, sal_uInt16 nXclColor )
+{
+ static const sal_uInt16 ppnLineParam[][ 4 ] =
+ {
+ // outer width, type
+ { 0, table::BorderLineStyle::SOLID }, // 0 = none
+ { EXC_BORDER_THIN, table::BorderLineStyle::SOLID }, // 1 = thin
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::SOLID }, // 2 = medium
+ { EXC_BORDER_THIN, table::BorderLineStyle::FINE_DASHED }, // 3 = dashed
+ { EXC_BORDER_THIN, table::BorderLineStyle::DOTTED }, // 4 = dotted
+ { EXC_BORDER_THICK, table::BorderLineStyle::SOLID }, // 5 = thick
+ { EXC_BORDER_THICK, table::BorderLineStyle::DOUBLE_THIN }, // 6 = double
+ { EXC_BORDER_HAIR, table::BorderLineStyle::SOLID }, // 7 = hair
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASHED }, // 8 = med dash
+ { EXC_BORDER_THIN, table::BorderLineStyle::DASH_DOT }, // 9 = thin dashdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT }, // A = med dashdot
+ { EXC_BORDER_THIN, table::BorderLineStyle::DASH_DOT_DOT }, // B = thin dashdotdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT_DOT }, // C = med dashdotdot
+ { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT } // D = med slant dashdot
+ };
+
+ if( nXclLine == EXC_LINE_NONE )
+ return false;
+ if( nXclLine >= SAL_N_ELEMENTS( ppnLineParam ) )
+ nXclLine = EXC_LINE_THIN;
+
+ rLine.SetColor( rPalette.GetColor( nXclColor ) );
+ rLine.SetWidth( ppnLineParam[ nXclLine ][ 0 ] );
+ rLine.SetBorderLineStyle( static_cast< SvxBorderLineStyle>(
+ ppnLineParam[ nXclLine ][ 1 ]) );
+ return true;
+}
+
+} // namespace
+
+void XclImpCellBorder::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const
+{
+ if( mbLeftUsed || mbRightUsed || mbTopUsed || mbBottomUsed )
+ {
+ SvxBoxItem aBoxItem( ATTR_BORDER );
+ ::editeng::SvxBorderLine aLine;
+ if( mbLeftUsed && lclConvertBorderLine( aLine, rPalette, mnLeftLine, mnLeftColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ if( mbRightUsed && lclConvertBorderLine( aLine, rPalette, mnRightLine, mnRightColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ if( mbTopUsed && lclConvertBorderLine( aLine, rPalette, mnTopLine, mnTopColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::TOP );
+ if( mbBottomUsed && lclConvertBorderLine( aLine, rPalette, mnBottomLine, mnBottomColor ) )
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ ScfTools::PutItem( rItemSet, aBoxItem, bSkipPoolDefs );
+ }
+ if( !mbDiagUsed )
+ return;
+
+ SvxLineItem aTLBRItem( ATTR_BORDER_TLBR );
+ SvxLineItem aBLTRItem( ATTR_BORDER_BLTR );
+ ::editeng::SvxBorderLine aLine;
+ if( lclConvertBorderLine( aLine, rPalette, mnDiagLine, mnDiagColor ) )
+ {
+ if( mbDiagTLtoBR )
+ aTLBRItem.SetLine( &aLine );
+ if( mbDiagBLtoTR )
+ aBLTRItem.SetLine( &aLine );
+ }
+ ScfTools::PutItem( rItemSet, aTLBRItem, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aBLTRItem, bSkipPoolDefs );
+}
+
+XclImpCellArea::XclImpCellArea()
+{
+ SetUsedFlags( false );
+}
+
+void XclImpCellArea::SetUsedFlags( bool bUsed )
+{
+ mbForeUsed = mbBackUsed = mbPattUsed = bUsed;
+}
+
+void XclImpCellArea::FillFromXF2( sal_uInt8 nFlags )
+{
+ mnPattern = ::get_flagvalue( nFlags, EXC_XF2_BACKGROUND, EXC_PATT_12_5_PERC, EXC_PATT_NONE );
+ mnForeColor = EXC_COLOR_BIFF2_BLACK;
+ mnBackColor = EXC_COLOR_BIFF2_WHITE;
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF3( sal_uInt16 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nArea, 0, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 6, 5 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 11, 5 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF5( sal_uInt32 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nArea, 16, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromXF8( sal_uInt32 nBorder2, sal_uInt16 nArea )
+{
+ mnPattern = ::extract_value< sal_uInt8 >( nBorder2, 26, 6 );
+ mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 );
+ SetUsedFlags( true );
+}
+
+void XclImpCellArea::FillFromCF8( sal_uInt16 nPattern, sal_uInt16 nColor, sal_uInt32 nFlags )
+{
+ mnForeColor = ::extract_value< sal_uInt16 >( nColor, 0, 7 );
+ mnBackColor = ::extract_value< sal_uInt16 >( nColor, 7, 7 );
+ mnPattern = ::extract_value< sal_uInt8 >( nPattern, 10, 6 );
+ mbForeUsed = !::get_flag( nFlags, EXC_CF_AREA_FGCOLOR );
+ mbBackUsed = !::get_flag( nFlags, EXC_CF_AREA_BGCOLOR );
+ mbPattUsed = !::get_flag( nFlags, EXC_CF_AREA_PATTERN );
+
+ if( mbBackUsed && (!mbPattUsed || (mnPattern == EXC_PATT_SOLID)) )
+ {
+ mnForeColor = mnBackColor;
+ mnPattern = EXC_PATT_SOLID;
+ mbForeUsed = mbPattUsed = true;
+ }
+ else if( !mbBackUsed && mbPattUsed && (mnPattern == EXC_PATT_SOLID) )
+ {
+ mbPattUsed = false;
+ }
+}
+
+void XclImpCellArea::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const
+{
+ if( !mbPattUsed ) // colors may be both unused in cond. formats
+ return;
+
+ SvxBrushItem aBrushItem( ATTR_BACKGROUND );
+
+ // do not use IsTransparent() - old Calc filter writes transparency with different color indexes
+ if( mnPattern == EXC_PATT_NONE )
+ {
+ aBrushItem.SetColor( COL_TRANSPARENT );
+ }
+ else
+ {
+ Color aFore( rPalette.GetColor( mbForeUsed ? mnForeColor : EXC_COLOR_WINDOWTEXT ) );
+ Color aBack( rPalette.GetColor( mbBackUsed ? mnBackColor : EXC_COLOR_WINDOWBACK ) );
+ aBrushItem.SetColor( XclTools::GetPatternColor( aFore, aBack, mnPattern ) );
+ }
+
+ ScfTools::PutItem( rItemSet, aBrushItem, bSkipPoolDefs );
+}
+
+XclImpXF::XclImpXF( const XclImpRoot& rRoot ) :
+ XclXFBase( true ), // default is cell XF
+ XclImpRoot( rRoot ),
+ mpStyleSheet( nullptr ),
+ mnXclNumFmt( 0 ),
+ mnXclFont( 0 )
+{
+}
+
+XclImpXF::~XclImpXF()
+{
+}
+
+void XclImpXF::ReadXF2( XclImpStream& rStrm )
+{
+ sal_uInt8 nReadFont, nReadNumFmt, nFlags;
+ nReadFont = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );
+ nReadNumFmt = rStrm.ReaduInt8();
+ nFlags = rStrm.ReaduInt8();
+
+ // XF type always cell, no parent, used flags always true
+ SetAllUsedFlags( true );
+
+ // attributes
+ maProtection.FillFromXF2( nReadNumFmt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt & EXC_XF2_VALFMT_MASK;
+ maAlignment.FillFromXF2( nFlags );
+ maBorder.FillFromXF2( nFlags );
+ maArea.FillFromXF2( nFlags );
+}
+
+void XclImpXF::ReadXF3( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder;
+ sal_uInt16 nTypeProt, nAlign, nArea;
+ sal_uInt8 nReadFont, nReadNumFmt;
+ nReadFont = rStrm.ReaduInt8();
+ nReadNumFmt = rStrm.ReaduInt8();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt16();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE ); // new in BIFF3
+ mnParent = ::extract_value< sal_uInt16 >( nAlign, 4, 12 ); // new in BIFF3
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nTypeProt, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt;
+ maAlignment.FillFromXF3( nAlign );
+ maBorder.FillFromXF3( nBorder );
+ maArea.FillFromXF3( nArea ); // new in BIFF3
+}
+
+void XclImpXF::ReadXF4( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder;
+ sal_uInt16 nTypeProt, nAlign, nArea;
+ sal_uInt8 nReadFont, nReadNumFmt;
+ nReadFont = rStrm.ReaduInt8();
+ nReadNumFmt = rStrm.ReaduInt8();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt16();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ mnXclFont = nReadFont;
+ mnXclNumFmt = nReadNumFmt;
+ maAlignment.FillFromXF4( nAlign );
+ maBorder.FillFromXF3( nBorder );
+ maArea.FillFromXF3( nArea );
+}
+
+void XclImpXF::ReadXF5( XclImpStream& rStrm )
+{
+ sal_uInt32 nArea, nBorder;
+ sal_uInt16 nTypeProt, nAlign;
+ mnXclFont = rStrm.ReaduInt16();
+ mnXclNumFmt = rStrm.ReaduInt16();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nArea = rStrm.ReaduInt32();
+ nBorder = rStrm.ReaduInt32();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ maAlignment.FillFromXF5( nAlign );
+ maBorder.FillFromXF5( nBorder, nArea );
+ maArea.FillFromXF5( nArea );
+}
+
+void XclImpXF::ReadXF8( XclImpStream& rStrm )
+{
+ sal_uInt32 nBorder1, nBorder2;
+ sal_uInt16 nTypeProt, nAlign, nMiscAttrib, nArea;
+ mnXclFont = rStrm.ReaduInt16();
+ mnXclNumFmt = rStrm.ReaduInt16();
+ nTypeProt = rStrm.ReaduInt16();
+ nAlign = rStrm.ReaduInt16();
+ nMiscAttrib = rStrm.ReaduInt16();
+ nBorder1 = rStrm.ReaduInt32();
+ nBorder2 = rStrm.ReaduInt32( );
+ nArea = rStrm.ReaduInt16();
+
+ // XF type/parent, attribute used flags
+ mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
+ mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
+ SetUsedFlags( ::extract_value< sal_uInt8 >( nMiscAttrib, 10, 6 ) );
+
+ // attributes
+ maProtection.FillFromXF3( nTypeProt );
+ maAlignment.FillFromXF8( nAlign, nMiscAttrib );
+ maBorder.FillFromXF8( nBorder1, nBorder2 );
+ maArea.FillFromXF8( nBorder2, nArea );
+}
+
+void XclImpXF::ReadXF( XclImpStream& rStrm )
+{
+ switch( GetBiff() )
+ {
+ case EXC_BIFF2: ReadXF2( rStrm ); break;
+ case EXC_BIFF3: ReadXF3( rStrm ); break;
+ case EXC_BIFF4: ReadXF4( rStrm ); break;
+ case EXC_BIFF5: ReadXF5( rStrm ); break;
+ case EXC_BIFF8: ReadXF8( rStrm ); break;
+ default: DBG_ERROR_BIFF();
+ }
+}
+
+const ScPatternAttr& XclImpXF::CreatePattern( bool bSkipPoolDefs )
+{
+ if( mpPattern )
+ return *mpPattern;
+
+ // create new pattern attribute set
+ mpPattern.reset( new ScPatternAttr( GetDoc().GetPool() ) );
+ SfxItemSet& rItemSet = mpPattern->GetItemSet();
+ XclImpXF* pParentXF = IsCellXF() ? GetXFBuffer().GetXF( mnParent ) : nullptr;
+
+ // parent cell style
+ if( IsCellXF() && !mpStyleSheet )
+ {
+ mpStyleSheet = GetXFBuffer().CreateStyleSheet( mnParent );
+
+ /* Enables mb***Used flags, if the formatting attributes differ from
+ the passed XF record. In cell XFs Excel uses the cell attributes,
+ if they differ from the parent style XF.
+ ...or if the respective flag is not set in parent style XF. */
+ if( pParentXF )
+ {
+ if( !mbProtUsed )
+ mbProtUsed = !pParentXF->mbProtUsed || !(maProtection == pParentXF->maProtection);
+ if( !mbFontUsed )
+ mbFontUsed = !pParentXF->mbFontUsed || (mnXclFont != pParentXF->mnXclFont);
+ if( !mbFmtUsed )
+ mbFmtUsed = !pParentXF->mbFmtUsed || (mnXclNumFmt != pParentXF->mnXclNumFmt);
+ if( !mbAlignUsed )
+ mbAlignUsed = !pParentXF->mbAlignUsed || !(maAlignment == pParentXF->maAlignment);
+ if( !mbBorderUsed )
+ mbBorderUsed = !pParentXF->mbBorderUsed || !(maBorder == pParentXF->maBorder);
+ if( !mbAreaUsed )
+ mbAreaUsed = !pParentXF->mbAreaUsed || !(maArea == pParentXF->maArea);
+ }
+ }
+
+ // cell protection
+ if( mbProtUsed )
+ maProtection.FillToItemSet( rItemSet, bSkipPoolDefs );
+
+ // font
+ if( mbFontUsed )
+ GetFontBuffer().FillToItemSet( rItemSet, XclFontItemType::Cell, mnXclFont, bSkipPoolDefs );
+
+ // value format
+ if( mbFmtUsed )
+ {
+ GetNumFmtBuffer().FillToItemSet( rItemSet, mnXclNumFmt, bSkipPoolDefs );
+ // Trace occurrences of Windows date formats
+ GetTracer().TraceDates( mnXclNumFmt );
+ }
+
+ // alignment
+ if( mbAlignUsed )
+ maAlignment.FillToItemSet( rItemSet, GetFontBuffer().GetFont( mnXclFont ), bSkipPoolDefs );
+
+ // border
+ if( mbBorderUsed )
+ {
+ maBorder.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs );
+ GetTracer().TraceBorderLineStyle(maBorder.mnLeftLine > EXC_LINE_HAIR ||
+ maBorder.mnRightLine > EXC_LINE_HAIR || maBorder.mnTopLine > EXC_LINE_HAIR ||
+ maBorder.mnBottomLine > EXC_LINE_HAIR );
+ }
+
+ // area
+ if( mbAreaUsed )
+ {
+ maArea.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs );
+ GetTracer().TraceFillPattern(maArea.mnPattern != EXC_PATT_NONE &&
+ maArea.mnPattern != EXC_PATT_SOLID);
+ }
+
+ /* #i38709# Decide which rotation reference mode to use. If any outer
+ border line of the cell is set (either explicitly or via cell style),
+ and the cell contents are rotated, set rotation reference to bottom of
+ cell. This causes the borders to be painted rotated with the text. */
+ if( mbAlignUsed || mbBorderUsed )
+ {
+ SvxRotateMode eRotateMode = SVX_ROTATE_MODE_STANDARD;
+ const XclImpCellAlign* pAlign = mbAlignUsed ? &maAlignment : (pParentXF ? &pParentXF->maAlignment : nullptr);
+ const XclImpCellBorder* pBorder = mbBorderUsed ? &maBorder : (pParentXF ? &pParentXF->maBorder : nullptr);
+ if( pAlign && pBorder && (0 < pAlign->mnRotation) && (pAlign->mnRotation <= 180) && pBorder->HasAnyOuterBorder() )
+ eRotateMode = SVX_ROTATE_MODE_BOTTOM;
+ ScfTools::PutItem( rItemSet, SvxRotateModeItem( eRotateMode, ATTR_ROTATE_MODE ), bSkipPoolDefs );
+ }
+
+ // Excel's cell margins are different from Calc's default margins.
+ SvxMarginItem aItem(40, 40, 40, 40, ATTR_MARGIN);
+ ScfTools::PutItem(rItemSet, aItem, bSkipPoolDefs);
+
+ return *mpPattern;
+}
+
+void XclImpXF::ApplyPatternToAttrVector(
+ std::vector<ScAttrEntry>& rAttrs, SCROW nRow1, SCROW nRow2, sal_uInt32 nForceScNumFmt)
+{
+ // force creation of cell style and hard formatting, do it here to have mpStyleSheet
+ CreatePattern();
+ ScPatternAttr& rPat = *mpPattern;
+
+ // insert into document
+ ScDocument& rDoc = GetDoc();
+
+ if (IsCellXF())
+ {
+ if (mpStyleSheet)
+ {
+ // Apply style sheet. Don't clear the direct formats.
+ rPat.SetStyleSheet(mpStyleSheet, false);
+ }
+ else
+ {
+ // When the cell format is not associated with any style, use the
+ // 'Default' style. Some buggy XLS docs generated by apps other
+ // than Excel (such as 1C) may not have any built-in styles at
+ // all.
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ if (pStylePool)
+ {
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(
+ pStylePool->Find(
+ ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ rPat.SetStyleSheet(pStyleSheet, false);
+ }
+
+ }
+ }
+
+ if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ ScPatternAttr aNumPat(rDoc.GetPool());
+ GetNumFmtBuffer().FillScFmtToItemSet(aNumPat.GetItemSet(), nForceScNumFmt);
+ rPat.GetItemSet().Put(aNumPat.GetItemSet());
+ }
+
+ // Make sure we skip unnamed styles.
+ if (!rPat.GetStyleName())
+ return;
+
+ // Check for a gap between the last entry and this one.
+ bool bHasGap = false;
+ if (rAttrs.empty() && nRow1 > 0)
+ // First attribute range doesn't start at row 0.
+ bHasGap = true;
+
+ if (!rAttrs.empty() && rAttrs.back().nEndRow + 1 < nRow1)
+ bHasGap = true;
+
+ if (bHasGap)
+ {
+ // Fill this gap with the default pattern.
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow1 - 1;
+ aEntry.pPattern = rDoc.GetDefPattern();
+ rAttrs.push_back(aEntry);
+ }
+
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow2;
+ aEntry.pPattern = &rDoc.GetPool()->Put(rPat);
+ rAttrs.push_back(aEntry);
+}
+
+void XclImpXF::ApplyPattern(
+ SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2,
+ SCTAB nScTab )
+{
+ // force creation of cell style and hard formatting, do it here to have mpStyleSheet
+ const ScPatternAttr& rPattern = CreatePattern();
+
+ // insert into document
+ ScDocument& rDoc = GetDoc();
+ if( IsCellXF() && mpStyleSheet )
+ rDoc.ApplyStyleAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, *mpStyleSheet );
+ if( HasUsedFlags() )
+ rDoc.ApplyPatternAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, rPattern );
+
+}
+
+/*static*/ void XclImpXF::ApplyPatternForBiff2CellFormat( const XclImpRoot& rRoot,
+ const ScAddress& rScPos, sal_uInt8 nFlags1, sal_uInt8 nFlags2, sal_uInt8 nFlags3 )
+{
+ /* Create an XF object and let it do the work. We will have access to its
+ private members here. */
+ XclImpXF aXF( rRoot );
+
+ // no used flags available in BIFF2 (always true)
+ aXF.SetAllUsedFlags( true );
+
+ // set the attributes
+ aXF.maProtection.FillFromXF2( nFlags1 );
+ aXF.maAlignment.FillFromXF2( nFlags3 );
+ aXF.maBorder.FillFromXF2( nFlags3 );
+ aXF.maArea.FillFromXF2( nFlags3 );
+ aXF.mnXclNumFmt = ::extract_value< sal_uInt16 >( nFlags2, 0, 6 );
+ aXF.mnXclFont = ::extract_value< sal_uInt16 >( nFlags2, 6, 2 );
+
+ // write the attributes to the cell
+ aXF.ApplyPattern( rScPos.Col(), rScPos.Row(), rScPos.Col(), rScPos.Row(), rScPos.Tab() );
+}
+
+void XclImpXF::SetUsedFlags( sal_uInt8 nUsedFlags )
+{
+ /* Notes about finding the mb***Used flags:
+ - In cell XFs a *set* bit means a used attribute.
+ - In style XFs a *cleared* bit means a used attribute.
+ The mb***Used members always store true, if the attribute is used.
+ The "mbCellXF == ::get_flag(...)" construct evaluates to true in
+ both mentioned cases: cell XF and set bit; or style XF and cleared bit.
+ */
+ mbProtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_PROT ));
+ mbFontUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_FONT ));
+ mbFmtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_VALFMT ));
+ mbAlignUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_ALIGN ));
+ mbBorderUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_BORDER ));
+ mbAreaUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_AREA ));
+}
+
+XclImpStyle::XclImpStyle( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+ mnXfId( EXC_XF_NOTFOUND ),
+ mnBuiltinId( EXC_STYLE_USERDEF ),
+ mnLevel( EXC_STYLE_NOLEVEL ),
+ mbBuiltin( false ),
+ mbCustom( false ),
+ mbHidden( false ),
+ mpStyleSheet( nullptr )
+{
+}
+
+void XclImpStyle::ReadStyle( XclImpStream& rStrm )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF3 );
+
+ sal_uInt16 nXFIndex;
+ nXFIndex = rStrm.ReaduInt16();
+ mnXfId = nXFIndex & EXC_STYLE_XFMASK;
+ mbBuiltin = ::get_flag( nXFIndex, EXC_STYLE_BUILTIN );
+
+ if( mbBuiltin )
+ {
+ mnBuiltinId = rStrm.ReaduInt8();
+ mnLevel = rStrm.ReaduInt8();
+ }
+ else
+ {
+ maName = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString();
+ // #i103281# check if this is a new built-in style introduced in XL2007
+ if( (GetBiff() == EXC_BIFF8) && (rStrm.GetNextRecId() == EXC_ID_STYLEEXT) && rStrm.StartNextRecord() )
+ {
+ sal_uInt8 nExtFlags;
+ rStrm.Ignore( 12 );
+ nExtFlags = rStrm.ReaduInt8();
+ mbBuiltin = ::get_flag( nExtFlags, EXC_STYLEEXT_BUILTIN );
+ mbCustom = ::get_flag( nExtFlags, EXC_STYLEEXT_CUSTOM );
+ mbHidden = ::get_flag( nExtFlags, EXC_STYLEEXT_HIDDEN );
+ if( mbBuiltin )
+ {
+ rStrm.Ignore( 1 ); // category
+ mnBuiltinId = rStrm.ReaduInt8();
+ mnLevel = rStrm.ReaduInt8();
+ }
+ }
+ }
+}
+
+ScStyleSheet* XclImpStyle::CreateStyleSheet()
+{
+ // #i1624# #i1768# ignore unnamed user styles
+ if( !mpStyleSheet && (!maFinalName.isEmpty()) )
+ {
+ bool bCreatePattern = false;
+ XclImpXF* pXF = GetXFBuffer().GetXF( mnXfId );
+
+ bool bDefStyle = mbBuiltin && (mnBuiltinId == EXC_STYLE_NORMAL);
+ if( bDefStyle )
+ {
+ // set all flags to true to get all items in XclImpXF::CreatePattern()
+ if( pXF ) pXF->SetAllUsedFlags( true );
+ // use existing "Default" style sheet
+ mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find(
+ ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) );
+ OSL_ENSURE( mpStyleSheet, "XclImpStyle::CreateStyleSheet - Default style not found" );
+ bCreatePattern = true;
+ }
+ else
+ {
+ /* #i103281# do not create another style sheet of the same name,
+ if it exists already. This is needed to prevent that styles
+ pasted from clipboard get duplicated over and over. */
+ mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find( maFinalName, SfxStyleFamily::Para ) );
+ if( !mpStyleSheet )
+ {
+ mpStyleSheet = &static_cast< ScStyleSheet& >( GetStyleSheetPool().Make( maFinalName, SfxStyleFamily::Para, SfxStyleSearchBits::UserDefined ) );
+ bCreatePattern = true;
+ }
+ }
+
+ // bDefStyle==true omits default pool items in CreatePattern()
+ if( bCreatePattern && mpStyleSheet && pXF )
+ mpStyleSheet->GetItemSet().Put( pXF->CreatePattern( bDefStyle ).GetItemSet() );
+ }
+ return mpStyleSheet;
+}
+
+void XclImpStyle::CreateUserStyle( const OUString& rFinalName )
+{
+ maFinalName = rFinalName;
+ if( !IsBuiltin() || mbCustom )
+ CreateStyleSheet();
+}
+
+XclImpXFBuffer::XclImpXFBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpXFBuffer::Initialize()
+{
+ maXFList.clear();
+ maBuiltinStyles.clear();
+ maUserStyles.clear();
+ maStylesByXf.clear();
+}
+
+void XclImpXFBuffer::ReadXF( XclImpStream& rStrm )
+{
+ std::unique_ptr<XclImpXF> xXF = std::make_unique<XclImpXF>(GetRoot());
+ xXF->ReadXF(rStrm);
+ maXFList.emplace_back(std::move(xXF));
+}
+
+void XclImpXFBuffer::ReadStyle( XclImpStream& rStrm )
+{
+ std::unique_ptr<XclImpStyle> xStyle(std::make_unique<XclImpStyle>(GetRoot()));
+ xStyle->ReadStyle(rStrm);
+ XclImpStyleList& rStyleList = (xStyle->IsBuiltin() ? maBuiltinStyles : maUserStyles);
+ rStyleList.emplace_back(std::move(xStyle));
+ XclImpStyle* pStyle = rStyleList.back().get();
+ OSL_ENSURE( maStylesByXf.count( pStyle->GetXfId() ) == 0, "XclImpXFBuffer::ReadStyle - multiple styles with equal XF identifier" );
+ maStylesByXf[ pStyle->GetXfId() ] = pStyle;
+}
+
+sal_uInt16 XclImpXFBuffer::GetFontIndex( sal_uInt16 nXFIndex ) const
+{
+ const XclImpXF* pXF = GetXF( nXFIndex );
+ return pXF ? pXF->GetFontIndex() : EXC_FONT_NOTFOUND;
+}
+
+const XclImpFont* XclImpXFBuffer::GetFont( sal_uInt16 nXFIndex ) const
+{
+ return GetFontBuffer().GetFont( GetFontIndex( nXFIndex ) );
+}
+
+namespace {
+
+/** Functor for case-insensitive string comparison, usable in maps etc. */
+struct IgnoreCaseCompare
+{
+ bool operator()( std::u16string_view rName1, std::u16string_view rName2 ) const
+ { return o3tl::compareToIgnoreAsciiCase( rName1, rName2 ) < 0; }
+};
+
+} // namespace
+
+void XclImpXFBuffer::CreateUserStyles()
+{
+ // calculate final names of all styles
+ std::map< OUString, XclImpStyle*, IgnoreCaseCompare > aCellStyles;
+ std::vector< XclImpStyle* > aConflictNameStyles;
+
+ /* First, reserve style names that are built-in in Calc. This causes that
+ imported cell styles get different unused names and thus do not try to
+ overwrite these built-in styles. For BIFF4 workbooks (which contain a
+ separate list of cell styles per sheet), reserve all existing styles if
+ current sheet is not the first sheet (this styles buffer will be
+ initialized again for every new sheet). This will create unique names
+ for styles in different sheets with the same name. Assuming that the
+ BIFF4W import filter is never used to import from clipboard... */
+ bool bReserveAll = (GetBiff() == EXC_BIFF4) && (GetCurrScTab() > 0);
+ SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SfxStyleFamily::Para );
+ OUString aStandardName = ScResId( STR_STYLENAME_STANDARD );
+ for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() )
+ if( (pStyleSheet->GetName() != aStandardName) && (bReserveAll || !pStyleSheet->IsUserDefined()) )
+ if( aCellStyles.count( pStyleSheet->GetName() ) == 0 )
+ aCellStyles[ pStyleSheet->GetName() ] = nullptr;
+
+ /* Calculate names of built-in styles. Store styles with reserved names
+ in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maBuiltinStyles )
+ {
+ OUString aStyleName = XclTools::GetBuiltInStyleName( rxStyle->GetBuiltinId(), rxStyle->GetName(), rxStyle->GetLevel() );
+ OSL_ENSURE( bReserveAll || (aCellStyles.count( aStyleName ) == 0),
+ "XclImpXFBuffer::CreateUserStyles - multiple styles with equal built-in identifier" );
+ if( aCellStyles.count( aStyleName ) > 0 )
+ aConflictNameStyles.push_back( rxStyle.get() );
+ else
+ aCellStyles[ aStyleName ] = rxStyle.get();
+ }
+
+ /* Calculate names of user defined styles. Store styles with reserved
+ names in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maUserStyles )
+ {
+ // #i1624# #i1768# ignore unnamed user styles
+ if( !rxStyle->GetName().isEmpty() )
+ {
+ if( aCellStyles.count( rxStyle->GetName() ) > 0 )
+ aConflictNameStyles.push_back( rxStyle.get() );
+ else
+ aCellStyles[ rxStyle->GetName() ] = rxStyle.get();
+ }
+ }
+
+ // find unused names for all styles with conflicting names
+ for( XclImpStyle* pStyle : aConflictNameStyles )
+ {
+ OUString aUnusedName;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ aUnusedName = pStyle->GetName() + " " + OUString::number( ++nIndex );
+ }
+ while( aCellStyles.count( aUnusedName ) > 0 );
+ aCellStyles[ aUnusedName ] = pStyle;
+ }
+
+ // set final names and create user-defined and modified built-in cell styles
+ for( auto& [rStyleName, rpStyle] : aCellStyles )
+ if( rpStyle )
+ rpStyle->CreateUserStyle( rStyleName );
+}
+
+ScStyleSheet* XclImpXFBuffer::CreateStyleSheet( sal_uInt16 nXFIndex )
+{
+ XclImpStyleMap::iterator aIt = maStylesByXf.find( nXFIndex );
+ return (aIt == maStylesByXf.end()) ? nullptr : aIt->second->CreateStyleSheet();
+}
+
+// Buffer for XF indexes in cells =============================================
+
+bool XclImpXFRange::Expand( SCROW nScRow, const XclImpXFIndex& rXFIndex )
+{
+ if( maXFIndex != rXFIndex )
+ return false;
+
+ if( mnScRow2 + 1 == nScRow )
+ {
+ ++mnScRow2;
+ return true;
+ }
+ if( mnScRow1 > 0 && (mnScRow1 - 1 == nScRow) )
+ {
+ --mnScRow1;
+ return true;
+ }
+
+ return false;
+}
+
+bool XclImpXFRange::Expand( const XclImpXFRange& rNextRange )
+{
+ OSL_ENSURE( mnScRow2 < rNextRange.mnScRow1, "XclImpXFRange::Expand - rows out of order" );
+ if( (maXFIndex == rNextRange.maXFIndex) && (mnScRow2 + 1 == rNextRange.mnScRow1) )
+ {
+ mnScRow2 = rNextRange.mnScRow2;
+ return true;
+ }
+ return false;
+}
+
+void XclImpXFRangeColumn::SetDefaultXF( const XclImpXFIndex& rXFIndex, const XclImpRoot& rRoot )
+{
+ // List should be empty when inserting the default column format.
+ // Later explicit SetXF() calls will break up this range.
+ OSL_ENSURE( maIndexList.empty(), "XclImpXFRangeColumn::SetDefaultXF - Setting Default Column XF is not empty" );
+
+ // insert a complete row range with one insert.
+ maIndexList.push_back( std::make_unique<XclImpXFRange>( 0, rRoot.GetDoc().MaxRow(), rXFIndex ) );
+}
+
+void XclImpXFRangeColumn::SetXF( SCROW nScRow, const XclImpXFIndex& rXFIndex )
+{
+ XclImpXFRange* pPrevRange;
+ XclImpXFRange* pNextRange;
+ sal_uLong nNextIndex;
+
+ Find( pPrevRange, pNextRange, nNextIndex, nScRow );
+
+ // previous range:
+ // try to overwrite XF (if row is contained in) or try to expand range
+ if( pPrevRange )
+ {
+ if( pPrevRange->Contains( nScRow ) ) // overwrite old XF
+ {
+ if( rXFIndex == pPrevRange->maXFIndex )
+ return;
+
+ SCROW nFirstScRow = pPrevRange->mnScRow1;
+ SCROW nLastScRow = pPrevRange->mnScRow2;
+ sal_uLong nIndex = nNextIndex - 1;
+ XclImpXFRange* pThisRange = pPrevRange;
+ pPrevRange = (nIndex > 0 && nIndex <= maIndexList.size()) ? maIndexList[ nIndex - 1 ].get() : nullptr;
+
+ if( nFirstScRow == nLastScRow ) // replace solely XF
+ {
+ pThisRange->maXFIndex = rXFIndex;
+ TryConcatPrev( nNextIndex ); // try to concat. next with this
+ TryConcatPrev( nIndex ); // try to concat. this with previous
+ }
+ else if( nFirstScRow == nScRow ) // replace first XF
+ {
+ ++(pThisRange->mnScRow1);
+ // try to concatenate with previous of this
+ if( !pPrevRange || !pPrevRange->Expand( nScRow, rXFIndex ) )
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nIndex );
+ }
+ else if( nLastScRow == nScRow ) // replace last XF
+ {
+ --(pThisRange->mnScRow2);
+ if( !pNextRange || !pNextRange->Expand( nScRow, rXFIndex ) )
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nNextIndex );
+ }
+ else // insert in the middle of the range
+ {
+ pThisRange->mnScRow1 = nScRow + 1;
+ // List::Insert() moves entries towards end of list, so insert twice at nIndex
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nIndex );
+ Insert( new XclImpXFRange( nFirstScRow, nScRow - 1, pThisRange->maXFIndex ), nIndex );
+ }
+ return;
+ }
+ else if( pPrevRange->Expand( nScRow, rXFIndex ) ) // try to expand
+ {
+ TryConcatPrev( nNextIndex ); // try to concatenate next with expanded
+ return;
+ }
+ }
+
+ // try to expand next range
+ if( pNextRange && pNextRange->Expand( nScRow, rXFIndex ) )
+ return;
+
+ // create new range
+ Insert( new XclImpXFRange( nScRow, rXFIndex ), nNextIndex );
+}
+
+void XclImpXFRangeColumn::Insert(XclImpXFRange* pXFRange, sal_uLong nIndex)
+{
+ maIndexList.insert( maIndexList.begin() + nIndex, std::unique_ptr<XclImpXFRange>(pXFRange) );
+}
+
+void XclImpXFRangeColumn::Find(
+ XclImpXFRange*& rpPrevRange, XclImpXFRange*& rpNextRange,
+ sal_uLong& rnNextIndex, SCROW nScRow )
+{
+
+ // test whether list is empty
+ if( maIndexList.empty() )
+ {
+ rpPrevRange = rpNextRange = nullptr;
+ rnNextIndex = 0;
+ return;
+ }
+
+ rpPrevRange = maIndexList.front().get();
+ rpNextRange = maIndexList.back().get();
+
+ // test whether row is at end of list (contained in or behind last range)
+ // rpPrevRange will contain a possible existing row
+ if( rpNextRange->mnScRow1 <= nScRow )
+ {
+ rpPrevRange = rpNextRange;
+ rpNextRange = nullptr;
+ rnNextIndex = maIndexList.size();
+ return;
+ }
+
+ // test whether row is at beginning of list (really before first range)
+ if( nScRow < rpPrevRange->mnScRow1 )
+ {
+ rpNextRange = rpPrevRange;
+ rpPrevRange = nullptr;
+ rnNextIndex = 0;
+ return;
+ }
+
+ // loop: find range entries before and after new row
+ // break the loop if there is no more range between first and last -or-
+ // if rpPrevRange contains nScRow (rpNextRange will never contain nScRow)
+ sal_uLong nPrevIndex = 0;
+ sal_uLong nMidIndex;
+ rnNextIndex = maIndexList.size() - 1;
+ XclImpXFRange* pMidRange;
+ while( ((rnNextIndex - nPrevIndex) > 1) && (rpPrevRange->mnScRow2 < nScRow) )
+ {
+ nMidIndex = (nPrevIndex + rnNextIndex) / 2;
+ pMidRange = maIndexList[nMidIndex].get();
+ OSL_ENSURE( pMidRange, "XclImpXFRangeColumn::Find - missing XF index range" );
+ if( nScRow < pMidRange->mnScRow1 ) // row is really before pMidRange
+ {
+ rpNextRange = pMidRange;
+ rnNextIndex = nMidIndex;
+ }
+ else // row is in or after pMidRange
+ {
+ rpPrevRange = pMidRange;
+ nPrevIndex = nMidIndex;
+ }
+ }
+
+ // find next rpNextRange if rpPrevRange contains nScRow
+ if( nScRow <= rpPrevRange->mnScRow2 )
+ {
+ rnNextIndex = nPrevIndex + 1;
+ rpNextRange = maIndexList[rnNextIndex].get();
+ }
+}
+
+void XclImpXFRangeColumn::TryConcatPrev( sal_uLong nIndex )
+{
+ if( !nIndex || nIndex >= maIndexList.size() )
+ return;
+
+ XclImpXFRange& prevRange = *maIndexList[ nIndex - 1 ];
+ XclImpXFRange& nextRange = *maIndexList[ nIndex ];
+
+ if( prevRange.Expand( nextRange ) )
+ maIndexList.erase( maIndexList.begin() + nIndex );
+}
+
+XclImpXFRangeBuffer::XclImpXFRangeBuffer( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+XclImpXFRangeBuffer::~XclImpXFRangeBuffer()
+{
+}
+
+void XclImpXFRangeBuffer::Initialize()
+{
+ maColumns.clear();
+ maHyperlinks.clear();
+ maMergeList.RemoveAll();
+}
+
+void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex, XclImpXFInsertMode eMode )
+{
+ SCCOL nScCol = rScPos.Col();
+ SCROW nScRow = rScPos.Row();
+
+ // set cell XF's
+ size_t nIndex = static_cast< size_t >( nScCol );
+ if( maColumns.size() <= nIndex )
+ maColumns.resize( nIndex + 1 );
+ if( !maColumns[ nIndex ] )
+ maColumns[ nIndex ] = std::make_shared<XclImpXFRangeColumn>();
+ // remember all Boolean cells, they will get 'Standard' number format
+ maColumns[ nIndex ]->SetXF( nScRow, XclImpXFIndex( nXFIndex, eMode == xlXFModeBoolCell ) );
+
+ // set "center across selection" and "fill" attribute for all following empty cells
+ // ignore it on row default XFs
+ if( eMode == xlXFModeRow )
+ return;
+
+ const XclImpXF* pXF = GetXFBuffer().GetXF( nXFIndex );
+ if( pXF && ((pXF->GetHorAlign() == EXC_XF_HOR_CENTER_AS) || (pXF->GetHorAlign() == EXC_XF_HOR_FILL)) )
+ {
+ // expand last merged range if this attribute is set repeatedly
+ ScRange* pRange = maMergeList.empty() ? nullptr : &maMergeList.back();
+ if (pRange && (pRange->aEnd.Row() == nScRow) && (pRange->aEnd.Col() + 1 == nScCol) && (eMode == xlXFModeBlank))
+ pRange->aEnd.IncCol();
+ else if( eMode != xlXFModeBlank ) // do not merge empty cells
+ maMergeList.push_back( ScRange( nScCol, nScRow, 0 ) );
+ }
+}
+
+void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeCell );
+}
+
+void XclImpXFRangeBuffer::SetBlankXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeBlank );
+}
+
+void XclImpXFRangeBuffer::SetBoolXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
+{
+ SetXF( rScPos, nXFIndex, xlXFModeBoolCell );
+}
+
+void XclImpXFRangeBuffer::SetRowDefXF( SCROW nScRow, sal_uInt16 nXFIndex )
+{
+ for( SCCOL nScCol = 0; nScCol <= GetDoc().MaxCol(); ++nScCol )
+ SetXF( ScAddress( nScCol, nScRow, 0 ), nXFIndex, xlXFModeRow );
+}
+
+void XclImpXFRangeBuffer::SetColumnDefXF( SCCOL nScCol, sal_uInt16 nXFIndex )
+{
+ // our array should not have values when creating the default column format.
+ size_t nIndex = static_cast< size_t >( nScCol );
+ if( maColumns.size() <= nIndex )
+ maColumns.resize( nIndex + 1 );
+ OSL_ENSURE( !maColumns[ nIndex ], "XclImpXFRangeBuffer::SetColumnDefXF - default column of XFs already has values" );
+ maColumns[ nIndex ] = std::make_shared<XclImpXFRangeColumn>();
+ maColumns[ nIndex ]->SetDefaultXF( XclImpXFIndex( nXFIndex ), GetRoot());
+}
+
+void XclImpXFRangeBuffer::SetBorderLine( const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
+{
+ SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
+ SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
+ ScDocument& rDoc = GetDoc();
+
+ const SvxBoxItem* pFromItem =
+ rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
+ const SvxBoxItem* pToItem =
+ rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
+
+ SvxBoxItem aNewItem( *pToItem );
+ aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
+ rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
+}
+
+void XclImpXFRangeBuffer::SetHyperlink( const XclRange& rXclRange, const OUString& rUrl )
+{
+ maHyperlinks.emplace_back( rXclRange, rUrl );
+}
+
+void XclImpXFRangeBuffer::SetMerge( SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2 )
+{
+ if( (nScCol1 < nScCol2) || (nScRow1 < nScRow2) )
+ maMergeList.push_back( ScRange( nScCol1, nScRow1, 0, nScCol2, nScRow2, 0 ) );
+}
+
+void XclImpXFRangeBuffer::Finalize()
+{
+ ScDocumentImport& rDocImport = GetDocImport();
+ ScDocument& rDoc = rDocImport.getDoc();
+ SCTAB nScTab = GetCurrScTab();
+
+ // apply patterns
+ XclImpXFBuffer& rXFBuffer = GetXFBuffer();
+ ScDocumentImport::Attrs aPendingAttrParam;
+ SCCOL pendingColStart = -1;
+ SCCOL pendingColEnd = -1;
+ SCCOL nScCol = 0;
+ for( const auto& rxColumn : maColumns )
+ {
+ // apply all cell styles of an existing column
+ if( rxColumn )
+ {
+ XclImpXFRangeColumn& rColumn = *rxColumn;
+ std::vector<ScAttrEntry> aAttrs;
+ aAttrs.reserve(rColumn.end() - rColumn.begin());
+
+ for (const auto& rxStyle : rColumn)
+ {
+ XclImpXFRange& rStyle = *rxStyle;
+ const XclImpXFIndex& rXFIndex = rStyle.maXFIndex;
+ XclImpXF* pXF = rXFBuffer.GetXF( rXFIndex.GetXFIndex() );
+ if (!pXF)
+ continue;
+
+ sal_uInt32 nForceScNumFmt = rXFIndex.IsBoolCell() ?
+ GetNumFmtBuffer().GetStdScNumFmt() : NUMBERFORMAT_ENTRY_NOT_FOUND;
+
+ pXF->ApplyPatternToAttrVector(aAttrs, rStyle.mnScRow1, rStyle.mnScRow2, nForceScNumFmt);
+ }
+
+ if (aAttrs.empty() || aAttrs.back().nEndRow != rDoc.MaxRow())
+ {
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = rDoc.MaxRow();
+ aEntry.pPattern = rDoc.GetDefPattern();
+ aAttrs.push_back(aEntry);
+ }
+
+ aAttrs.shrink_to_fit();
+ assert(aAttrs.size() > 0);
+ ScDocumentImport::Attrs aAttrParam;
+ aAttrParam.mvData.swap(aAttrs);
+ aAttrParam.mbLatinNumFmtOnly = false; // when unsure, set it to false.
+
+ // Compress setting the attributes, set the same set in one call.
+ if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
+ ++pendingColEnd;
+ else
+ {
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+ pendingColStart = pendingColEnd = nScCol;
+ aPendingAttrParam = std::move( aAttrParam );
+ }
+ }
+ ++nScCol;
+ }
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+
+ // insert hyperlink cells
+ for( const auto& [rXclRange, rUrl] : maHyperlinks )
+ XclImpHyperlink::InsertUrl( GetRoot(), rXclRange, rUrl );
+
+ // apply cell merging
+ for ( size_t i = 0, nRange = maMergeList.size(); i < nRange; ++i )
+ {
+ const ScRange & rRange = maMergeList[ i ];
+ const ScAddress& rStart = rRange.aStart;
+ const ScAddress& rEnd = rRange.aEnd;
+ bool bMultiCol = rStart.Col() != rEnd.Col();
+ bool bMultiRow = rStart.Row() != rEnd.Row();
+ // set correct right border
+ if( bMultiCol )
+ SetBorderLine( rRange, nScTab, SvxBoxItemLine::RIGHT );
+ // set correct lower border
+ if( bMultiRow )
+ SetBorderLine( rRange, nScTab, SvxBoxItemLine::BOTTOM );
+ // do merge
+ if( bMultiCol || bMultiRow )
+ rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), nScTab );
+ // #i93609# merged range in a single row: test if manual row height is needed
+ if( !bMultiRow )
+ {
+ bool bTextWrap = rDoc.GetAttr( rStart, ATTR_LINEBREAK )->GetValue();
+ if( !bTextWrap && (rDoc.GetCellType( rStart ) == CELLTYPE_EDIT) )
+ if (const EditTextObject* pEditObj = rDoc.GetEditText(rStart))
+ bTextWrap = pEditObj->GetParagraphCount() > 1;
+ if( bTextWrap )
+ GetOldRoot().pColRowBuff->SetManualRowHeight( rStart.Row() );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xiview.cxx b/sc/source/filter/excel/xiview.cxx
new file mode 100644
index 000000000..49878de6e
--- /dev/null
+++ b/sc/source/filter/excel/xiview.cxx
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+
+#include <xiview.hxx>
+#include <document.hxx>
+#include <scextopt.hxx>
+#include <viewopti.hxx>
+#include <xistream.hxx>
+#include <xihelper.hxx>
+#include <xistyle.hxx>
+
+// Document view settings =====================================================
+
+XclImpDocViewSettings::XclImpDocViewSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+}
+
+void XclImpDocViewSettings::ReadWindow1( XclImpStream& rStrm )
+{
+ maData.mnWinX = rStrm.ReaduInt16();
+ maData.mnWinY = rStrm.ReaduInt16();
+ maData.mnWinWidth = rStrm.ReaduInt16();
+ maData.mnWinHeight = rStrm.ReaduInt16();
+ maData.mnFlags = rStrm.ReaduInt16();
+ if( GetBiff() >= EXC_BIFF5 )
+ {
+ maData.mnDisplXclTab = rStrm.ReaduInt16();
+ maData.mnFirstVisXclTab = rStrm.ReaduInt16();
+ maData.mnXclSelectCnt = rStrm.ReaduInt16();
+ maData.mnTabBarWidth = rStrm.ReaduInt16();
+ }
+}
+
+SCTAB XclImpDocViewSettings::GetDisplScTab() const
+{
+ /* Simply cast Excel index to Calc index.
+ TODO: This may fail if the document contains scenarios. */
+ sal_uInt16 nMaxXclTab = static_cast< sal_uInt16 >( GetMaxPos().Tab() );
+ return static_cast< SCTAB >( (maData.mnDisplXclTab <= nMaxXclTab) ? maData.mnDisplXclTab : 0 );
+}
+
+void XclImpDocViewSettings::Finalize()
+{
+ ScViewOptions aViewOpt( GetDoc().GetViewOptions() );
+ aViewOpt.SetOption( VOPT_HSCROLL, ::get_flag( maData.mnFlags, EXC_WIN1_HOR_SCROLLBAR ) );
+ aViewOpt.SetOption( VOPT_VSCROLL, ::get_flag( maData.mnFlags, EXC_WIN1_VER_SCROLLBAR ) );
+ aViewOpt.SetOption( VOPT_TABCONTROLS, ::get_flag( maData.mnFlags, EXC_WIN1_TABBAR ) );
+ GetDoc().SetViewOptions( aViewOpt );
+
+ // displayed sheet
+ GetExtDocOptions().GetDocSettings().mnDisplTab = GetDisplScTab();
+
+ // width of the tabbar with sheet names
+ if( maData.mnTabBarWidth <= 1000 )
+ GetExtDocOptions().GetDocSettings().mfTabBarWidth = static_cast< double >( maData.mnTabBarWidth ) / 1000.0;
+}
+
+// Sheet view settings ========================================================
+
+namespace {
+
+tools::Long lclGetScZoom( sal_uInt16 nXclZoom, sal_uInt16 nDefZoom )
+{
+ return static_cast< tools::Long >( nXclZoom ? nXclZoom : nDefZoom );
+}
+
+} // namespace
+
+XclImpTabViewSettings::XclImpTabViewSettings( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot )
+{
+ Initialize();
+}
+
+void XclImpTabViewSettings::Initialize()
+{
+ maData.SetDefaults();
+}
+
+void XclImpTabViewSettings::ReadTabBgColor( XclImpStream& rStrm, const XclImpPalette& rPal )
+{
+ OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF8 );
+ if( GetBiff() < EXC_BIFF8 )
+ return;
+
+ sal_uInt8 ColorIndex;
+
+ rStrm.Ignore( 16 );
+ ColorIndex = rStrm.ReaduInt8() & EXC_SHEETEXT_TABCOLOR; //0x7F
+ if ( ColorIndex >= 8 && ColorIndex <= 63 ) //only accept valid index values
+ {
+ maData.maTabBgColor = rPal.GetColor( ColorIndex );
+ }
+}
+
+void XclImpTabViewSettings::ReadWindow2( XclImpStream& rStrm, bool bChart )
+{
+ if( GetBiff() == EXC_BIFF2 )
+ {
+ maData.mbShowFormulas = rStrm.ReaduInt8() != 0;
+ maData.mbShowGrid = rStrm.ReaduInt8() != 0;
+ maData.mbShowHeadings = rStrm.ReaduInt8() != 0;
+ maData.mbFrozenPanes = rStrm.ReaduInt8() != 0;
+ maData.mbShowZeros = rStrm.ReaduInt8() != 0;
+ rStrm >> maData.maFirstXclPos;
+ maData.mbDefGridColor = rStrm.ReaduInt8() != 0;
+ rStrm >> maData.maGridColor;
+ }
+ else
+ {
+ sal_uInt16 nFlags;
+ nFlags = rStrm.ReaduInt16();
+ rStrm >> maData.maFirstXclPos;
+
+ // #i59590# real life: Excel ignores some view settings in chart sheets
+ maData.mbSelected = ::get_flag( nFlags, EXC_WIN2_SELECTED );
+ maData.mbDisplayed = ::get_flag( nFlags, EXC_WIN2_DISPLAYED );
+ maData.mbMirrored = !bChart && ::get_flag( nFlags, EXC_WIN2_MIRRORED );
+ maData.mbFrozenPanes = !bChart && ::get_flag( nFlags, EXC_WIN2_FROZEN );
+ maData.mbPageMode = !bChart && ::get_flag( nFlags, EXC_WIN2_PAGEBREAKMODE );
+ maData.mbDefGridColor = bChart || ::get_flag( nFlags, EXC_WIN2_DEFGRIDCOLOR );
+ maData.mbShowFormulas = !bChart && ::get_flag( nFlags, EXC_WIN2_SHOWFORMULAS );
+ maData.mbShowGrid = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWGRID );
+ maData.mbShowHeadings = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWHEADINGS );
+ maData.mbShowZeros = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWZEROS );
+ maData.mbShowOutline = bChart || ::get_flag( nFlags, EXC_WIN2_SHOWOUTLINE );
+
+ switch( GetBiff() )
+ {
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ case EXC_BIFF5:
+ rStrm >> maData.maGridColor;
+ break;
+ case EXC_BIFF8:
+ {
+ sal_uInt16 nGridColorIdx;
+ nGridColorIdx = rStrm.ReaduInt16();
+ // zoom data not included in chart sheets
+ if( rStrm.GetRecLeft() >= 6 )
+ {
+ rStrm.Ignore( 2 );
+ maData.mnPageZoom = rStrm.ReaduInt16();
+ maData.mnNormalZoom = rStrm.ReaduInt16();
+ }
+
+ if( !maData.mbDefGridColor )
+ maData.maGridColor = GetPalette().GetColor( nGridColorIdx );
+ }
+ break;
+ default: DBG_ERROR_BIFF();
+ }
+ }
+
+ // do not scroll chart sheets
+ if( bChart )
+ maData.maFirstXclPos.Set( 0, 0 );
+}
+
+void XclImpTabViewSettings::ReadScl( XclImpStream& rStrm )
+{
+ sal_uInt16 nNum, nDenom;
+ nNum = rStrm.ReaduInt16();
+ nDenom = rStrm.ReaduInt16();
+ OSL_ENSURE( nDenom > 0, "XclImpPageSettings::ReadScl - invalid denominator" );
+ if( nDenom > 0 )
+ maData.mnCurrentZoom = limit_cast< sal_uInt16 >( (nNum * 100) / nDenom );
+}
+
+void XclImpTabViewSettings::ReadPane( XclImpStream& rStrm )
+{
+ maData.mnSplitX = rStrm.ReaduInt16();
+ maData.mnSplitY = rStrm.ReaduInt16();
+
+ rStrm >> maData.maSecondXclPos;
+ maData.mnActivePane = rStrm.ReaduInt8();
+}
+
+void XclImpTabViewSettings::ReadSelection( XclImpStream& rStrm )
+{
+ // pane of this selection
+ sal_uInt8 nPane;
+ nPane = rStrm.ReaduInt8();
+ XclSelectionData& rSelData = maData.CreateSelectionData( nPane );
+ // cursor position and selection
+ rStrm >> rSelData.maXclCursor;
+ rSelData.mnCursorIdx = rStrm.ReaduInt16();
+ rSelData.maXclSelection.Read( rStrm, false );
+}
+
+void XclImpTabViewSettings::Finalize()
+{
+ SCTAB nScTab = GetCurrScTab();
+ ScDocument& rDoc = GetDoc();
+ XclImpAddressConverter& rAddrConv = GetAddressConverter();
+ ScExtTabSettings& rTabSett = GetExtDocOptions().GetOrCreateTabSettings( nScTab );
+ bool bDisplayed = GetDocViewSettings().GetDisplScTab() == nScTab;
+
+ // *** sheet options: cursor, selection, splits, zoom ***
+
+ // sheet flags
+ if( maData.mbMirrored )
+ // do not call this function with sal_False, it would mirror away all drawing objects
+ rDoc.SetLayoutRTL( nScTab, true );
+ rTabSett.mbSelected = maData.mbSelected || bDisplayed;
+
+ // first visible cell in top-left pane and in additional pane(s)
+ rTabSett.maFirstVis = rAddrConv.CreateValidAddress( maData.maFirstXclPos, nScTab, false );
+ rTabSett.maSecondVis = rAddrConv.CreateValidAddress( maData.maSecondXclPos, nScTab, false );
+
+ // cursor position and selection
+ if( const XclSelectionData* pSelData = maData.GetSelectionData( maData.mnActivePane ) )
+ {
+ rTabSett.maCursor = rAddrConv.CreateValidAddress( pSelData->maXclCursor, nScTab, false );
+ rAddrConv.ConvertRangeList( rTabSett.maSelection, pSelData->maXclSelection, nScTab, false );
+ }
+
+ // active pane
+ switch( maData.mnActivePane )
+ {
+ case EXC_PANE_TOPLEFT: rTabSett.meActivePane = SCEXT_PANE_TOPLEFT; break;
+ case EXC_PANE_TOPRIGHT: rTabSett.meActivePane = SCEXT_PANE_TOPRIGHT; break;
+ case EXC_PANE_BOTTOMLEFT: rTabSett.meActivePane = SCEXT_PANE_BOTTOMLEFT; break;
+ case EXC_PANE_BOTTOMRIGHT: rTabSett.meActivePane = SCEXT_PANE_BOTTOMRIGHT; break;
+ }
+
+ // freeze/split position
+ rTabSett.mbFrozenPanes = maData.mbFrozenPanes;
+ if( maData.mbFrozenPanes )
+ {
+ /* Frozen panes: handle split position as row/column positions.
+ #i35812# Excel uses number of visible rows/columns, Calc uses position of freeze. */
+ if( (maData.mnSplitX > 0) && (maData.maFirstXclPos.mnCol + maData.mnSplitX <= GetScMaxPos().Col()) )
+ rTabSett.maFreezePos.SetCol( static_cast< SCCOL >( maData.maFirstXclPos.mnCol + maData.mnSplitX ) );
+ if( (maData.mnSplitY > 0) && (maData.maFirstXclPos.mnRow + maData.mnSplitY <= o3tl::make_unsigned(GetScMaxPos().Row())) )
+ rTabSett.maFreezePos.SetRow( static_cast< SCROW >( maData.maFirstXclPos.mnRow + maData.mnSplitY ) );
+ }
+ else
+ {
+ // split window: position is in twips
+ rTabSett.maSplitPos.setX( static_cast< tools::Long >( maData.mnSplitX ) );
+ rTabSett.maSplitPos.setY( static_cast< tools::Long >( maData.mnSplitY ) );
+ }
+
+ // grid color
+ if( maData.mbDefGridColor )
+ rTabSett.maGridColor = COL_AUTO;
+ else
+ rTabSett.maGridColor = maData.maGridColor;
+
+ // show grid option
+ rTabSett.mbShowGrid = maData.mbShowGrid;
+
+ // view mode and zoom
+ if( maData.mnCurrentZoom != 0 )
+ (maData.mbPageMode ? maData.mnPageZoom : maData.mnNormalZoom) = maData.mnCurrentZoom;
+ rTabSett.mbPageMode = maData.mbPageMode;
+ rTabSett.mnNormalZoom = lclGetScZoom( maData.mnNormalZoom, EXC_WIN2_NORMALZOOM_DEF );
+ rTabSett.mnPageZoom = lclGetScZoom( maData.mnPageZoom, EXC_WIN2_PAGEZOOM_DEF );
+
+ // *** additional handling for displayed sheet ***
+
+ if( bDisplayed )
+ {
+ // set Excel sheet settings globally at Calc document, take settings from displayed sheet
+ ScViewOptions aViewOpt( rDoc.GetViewOptions() );
+ aViewOpt.SetOption( VOPT_FORMULAS, maData.mbShowFormulas );
+ aViewOpt.SetOption( VOPT_HEADER, maData.mbShowHeadings );
+ aViewOpt.SetOption( VOPT_NULLVALS, maData.mbShowZeros );
+ aViewOpt.SetOption( VOPT_OUTLINER, maData.mbShowOutline );
+ rDoc.SetViewOptions( aViewOpt );
+ }
+
+ // *** set tab bg color
+ if ( !maData.IsDefaultTabBgColor() )
+ rDoc.SetTabBgColor(nScTab, maData.maTabBgColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xladdress.cxx b/sc/source/filter/excel/xladdress.cxx
new file mode 100644
index 000000000..20ef4fa7f
--- /dev/null
+++ b/sc/source/filter/excel/xladdress.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xladdress.hxx>
+#include <xestream.hxx>
+#include <xltracer.hxx>
+#include <xistream.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+void XclAddress::Read( XclImpStream& rStrm )
+{
+ mnRow = rStrm.ReaduInt16();
+ mnCol = rStrm.ReaduInt16();
+}
+
+void XclAddress::Write( XclExpStream& rStrm ) const
+{
+ rStrm << static_cast<sal_uInt16> (mnRow);
+ rStrm << mnCol;
+}
+
+bool XclRange::Contains( const XclAddress& rPos ) const
+{
+ return (maFirst.mnCol <= rPos.mnCol) && (rPos.mnCol <= maLast.mnCol) &&
+ (maFirst.mnRow <= rPos.mnRow) && (rPos.mnRow <= maLast.mnRow);
+}
+
+void XclRange::Read( XclImpStream& rStrm, bool bCol16Bit )
+{
+ maFirst.mnRow = rStrm.ReaduInt16();
+ maLast.mnRow = rStrm.ReaduInt16();
+
+ if( bCol16Bit )
+ {
+ maFirst.mnCol = rStrm.ReaduInt16();
+ maLast.mnCol = rStrm.ReaduInt16();
+ }
+ else
+ {
+ maFirst.mnCol = rStrm.ReaduInt8();
+ maLast.mnCol = rStrm.ReaduInt8();
+ }
+}
+
+void XclRange::Write( XclExpStream& rStrm, bool bCol16Bit ) const
+{
+ rStrm << static_cast<sal_uInt16>(maFirst.mnRow) << static_cast<sal_uInt16>(maLast.mnRow);
+ if( bCol16Bit )
+ rStrm << maFirst.mnCol << maLast.mnCol;
+ else
+ rStrm << static_cast< sal_uInt8 >( maFirst.mnCol ) << static_cast< sal_uInt8 >( maLast.mnCol );
+}
+
+XclRange XclRangeList::GetEnclosingRange() const
+{
+ XclRange aXclRange;
+ if( !mRanges.empty() )
+ {
+ XclRangeVector::const_iterator aIt = mRanges.begin(), aEnd = mRanges.end();
+ aXclRange = *aIt;
+ for( ++aIt; aIt != aEnd; ++aIt )
+ {
+ aXclRange.maFirst.mnCol = ::std::min( aXclRange.maFirst.mnCol, aIt->maFirst.mnCol );
+ aXclRange.maFirst.mnRow = ::std::min( aXclRange.maFirst.mnRow, aIt->maFirst.mnRow );
+ aXclRange.maLast.mnCol = ::std::max( aXclRange.maLast.mnCol, aIt->maLast.mnCol );
+ aXclRange.maLast.mnRow = ::std::max( aXclRange.maLast.mnRow, aIt->maLast.mnRow );
+ }
+ }
+ return aXclRange;
+}
+
+void XclRangeList::Read( XclImpStream& rStrm, bool bCol16Bit, sal_uInt16 nCountInStream )
+{
+ sal_uInt16 nCount;
+ if (nCountInStream)
+ nCount = nCountInStream;
+ else
+ nCount = rStrm.ReaduInt16();
+
+ if (!nCount)
+ return;
+
+ XclRange aRange;
+ while (true)
+ {
+ aRange.Read(rStrm, bCol16Bit);
+ if (!rStrm.IsValid())
+ break;
+ mRanges.emplace_back(aRange);
+ --nCount;
+ if (!nCount)
+ break;
+ }
+}
+
+void XclRangeList::Write( XclExpStream& rStrm, bool bCol16Bit, sal_uInt16 nCountInStream ) const
+{
+ WriteSubList( rStrm, 0, mRanges.size(), bCol16Bit, nCountInStream );
+}
+
+void XclRangeList::WriteSubList( XclExpStream& rStrm, size_t nBegin, size_t nCount, bool bCol16Bit,
+ sal_uInt16 nCountInStream ) const
+{
+ OSL_ENSURE( nBegin <= mRanges.size(), "XclRangeList::WriteSubList - invalid start position" );
+ size_t nEnd = ::std::min< size_t >( nBegin + nCount, mRanges.size() );
+ if (!nCountInStream)
+ {
+ sal_uInt16 nXclCount = ulimit_cast< sal_uInt16 >( nEnd - nBegin );
+ rStrm << nXclCount;
+ }
+ rStrm.SetSliceSize( bCol16Bit ? 8 : 6 );
+ std::for_each(mRanges.begin() + nBegin, mRanges.begin() + nEnd,
+ [&rStrm, &bCol16Bit](const XclRange& rRange) { rRange.Write(rStrm, bCol16Bit); });
+}
+
+XclAddressConverterBase::XclAddressConverterBase( XclTracer& rTracer, const ScAddress& rMaxPos ) :
+ mrTracer( rTracer ),
+ maMaxPos( rMaxPos ),
+ mnMaxCol( static_cast< sal_uInt16 >( rMaxPos.Col() ) ),
+ mnMaxRow( static_cast< sal_uInt16 >( rMaxPos.Row() ) ),
+ mbColTrunc( false ),
+ mbRowTrunc( false ),
+ mbTabTrunc( false )
+{
+ OSL_ENSURE( o3tl::make_unsigned( rMaxPos.Col() ) <= SAL_MAX_UINT16, "XclAddressConverterBase::XclAddressConverterBase - invalid max column" );
+ OSL_ENSURE( o3tl::make_unsigned( rMaxPos.Row() ) <= SAL_MAX_UINT32, "XclAddressConverterBase::XclAddressConverterBase - invalid max row" );
+}
+
+XclAddressConverterBase::~XclAddressConverterBase()
+{
+}
+
+void XclAddressConverterBase::CheckScTab( SCTAB nScTab )
+{
+ bool bValid = (0 <= nScTab) && (nScTab <= maMaxPos.Tab());
+ if( !bValid )
+ {
+ mbTabTrunc |= (nScTab > maMaxPos.Tab()); // do not warn for deleted refs
+ mrTracer.TraceInvalidTab( nScTab, maMaxPos.Tab() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlchart.cxx b/sc/source/filter/excel/xlchart.cxx
new file mode 100644
index 000000000..3547dad16
--- /dev/null
+++ b/sc/source/filter/excel/xlchart.cxx
@@ -0,0 +1,1283 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlchart.hxx>
+
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/chart/DataLabelPlacement.hpp>
+#include <com/sun/star/chart/XAxisXSupplier.hpp>
+#include <com/sun/star/chart/XAxisYSupplier.hpp>
+#include <com/sun/star/chart/XAxisZSupplier.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
+#include <com/sun/star/chart2/Symbol.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <o3tl/string_view.hxx>
+#include <sal/macros.h>
+#include <sal/mathconf.h>
+#include <svl/itemset.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/unomid.hxx>
+#include <filter/msfilter/escherex.hxx>
+#include <xlroot.hxx>
+#include <xlstyle.hxx>
+#include <xltools.hxx>
+
+using namespace css;
+
+// Common =====================================================================
+
+XclChRectangle::XclChRectangle() :
+ mnX( 0 ),
+ mnY( 0 ),
+ mnWidth( 0 ),
+ mnHeight( 0 )
+{
+}
+
+XclChDataPointPos::XclChDataPointPos( sal_uInt16 nSeriesIdx, sal_uInt16 nPointIdx ) :
+ mnSeriesIdx( nSeriesIdx ),
+ mnPointIdx( nPointIdx )
+{
+}
+
+bool operator<( const XclChDataPointPos& rL, const XclChDataPointPos& rR )
+{
+ return (rL.mnSeriesIdx < rR.mnSeriesIdx) ||
+ ((rL.mnSeriesIdx == rR.mnSeriesIdx) && (rL.mnPointIdx < rR.mnPointIdx));
+}
+
+XclChFrBlock::XclChFrBlock( sal_uInt16 nType ) :
+ mnType( nType ),
+ mnContext( 0 ),
+ mnValue1( 0 ),
+ mnValue2( 0 )
+{
+}
+
+// Frame formatting ===========================================================
+
+XclChFramePos::XclChFramePos() :
+ mnTLMode( EXC_CHFRAMEPOS_PARENT ),
+ mnBRMode( EXC_CHFRAMEPOS_PARENT )
+{
+}
+
+XclChLineFormat::XclChLineFormat() :
+ maColor( COL_BLACK ),
+ mnPattern( EXC_CHLINEFORMAT_SOLID ),
+ mnWeight( EXC_CHLINEFORMAT_SINGLE ),
+ mnFlags( EXC_CHLINEFORMAT_AUTO )
+{
+}
+
+XclChAreaFormat::XclChAreaFormat() :
+ maPattColor( COL_WHITE ),
+ maBackColor( COL_BLACK ),
+ mnPattern( EXC_PATT_SOLID ),
+ mnFlags( EXC_CHAREAFORMAT_AUTO )
+{
+}
+
+XclChEscherFormat::XclChEscherFormat()
+{
+}
+
+XclChEscherFormat::~XclChEscherFormat()
+{
+}
+
+XclChPicFormat::XclChPicFormat() :
+ mnBmpMode( EXC_CHPICFORMAT_NONE ),
+ mnFlags( EXC_CHPICFORMAT_TOPBOTTOM | EXC_CHPICFORMAT_FRONTBACK | EXC_CHPICFORMAT_LEFTRIGHT ),
+ mfScale( 0.5 )
+{
+}
+
+XclChFrame::XclChFrame() :
+ mnFormat( EXC_CHFRAME_STANDARD ),
+ mnFlags( EXC_CHFRAME_AUTOSIZE | EXC_CHFRAME_AUTOPOS )
+{
+}
+
+// Source links ===============================================================
+
+XclChSourceLink::XclChSourceLink() :
+ mnDestType( EXC_CHSRCLINK_TITLE ),
+ mnLinkType( EXC_CHSRCLINK_DEFAULT ),
+ mnFlags( 0 ),
+ mnNumFmtIdx( 0 )
+{
+}
+
+// Text =======================================================================
+
+XclChObjectLink::XclChObjectLink() :
+ mnTarget( EXC_CHOBJLINK_NONE )
+{
+}
+
+XclChFrLabelProps::XclChFrLabelProps() :
+ mnFlags( 0 )
+{
+}
+
+XclChText::XclChText() :
+ maTextColor( COL_BLACK ),
+ mnHAlign( EXC_CHTEXT_ALIGN_CENTER ),
+ mnVAlign( EXC_CHTEXT_ALIGN_CENTER ),
+ mnBackMode( EXC_CHTEXT_TRANSPARENT ),
+ mnFlags( EXC_CHTEXT_AUTOCOLOR | EXC_CHTEXT_AUTOFILL ),
+ mnFlags2( EXC_CHTEXT_POS_DEFAULT ),
+ mnRotation( EXC_ROT_NONE )
+{
+}
+
+// Data series ================================================================
+
+XclChMarkerFormat::XclChMarkerFormat() :
+ maLineColor( COL_BLACK ),
+ maFillColor( COL_WHITE ),
+ mnMarkerSize( EXC_CHMARKERFORMAT_SINGLESIZE ),
+ mnMarkerType( EXC_CHMARKERFORMAT_NOSYMBOL ),
+ mnFlags( EXC_CHMARKERFORMAT_AUTO )
+{
+};
+
+XclCh3dDataFormat::XclCh3dDataFormat() :
+ mnBase( EXC_CH3DDATAFORMAT_RECT ),
+ mnTop( EXC_CH3DDATAFORMAT_STRAIGHT )
+{
+}
+
+XclChDataFormat::XclChDataFormat() :
+ mnFormatIdx( EXC_CHDATAFORMAT_DEFAULT ),
+ mnFlags( 0 )
+{
+}
+
+XclChSerTrendLine::XclChSerTrendLine() :
+ mfForecastFor( 0.0 ),
+ mfForecastBack( 0.0 ),
+ mnLineType( EXC_CHSERTREND_POLYNOMIAL ),
+ mnOrder( 1 ),
+ mnShowEquation( 0 ),
+ mnShowRSquared( 0 )
+{
+ /* Set all bits in mfIntercept to 1 (that is -1.#NAN) to indicate that
+ there is no interception point. Cannot use ::rtl::math::setNan() here
+ cause it misses the sign bit. */
+ sal_math_Double* pDouble = reinterpret_cast< sal_math_Double* >( &mfIntercept );
+ pDouble->w32_parts.msw = pDouble->w32_parts.lsw = 0xFFFFFFFF;
+}
+
+XclChSerErrorBar::XclChSerErrorBar() :
+ mfValue( 0.0 ),
+ mnValueCount( 1 ),
+ mnBarType( EXC_CHSERERR_NONE ),
+ mnSourceType( EXC_CHSERERR_FIXED ),
+ mnLineEnd( EXC_CHSERERR_END_TSHAPE )
+{
+}
+
+XclChSeries::XclChSeries() :
+ mnCategType( EXC_CHSERIES_NUMERIC ),
+ mnValueType( EXC_CHSERIES_NUMERIC ),
+ mnBubbleType( EXC_CHSERIES_NUMERIC ),
+ mnCategCount( 0 ),
+ mnValueCount( 0 ),
+ mnBubbleCount( 0 )
+{
+}
+
+// Chart type groups ==========================================================
+
+XclChType::XclChType() :
+ mnOverlap( 0 ),
+ mnGap( 150 ),
+ mnRotation( 0 ),
+ mnPieHole( 0 ),
+ mnBubbleSize( 100 ),
+ mnBubbleType( EXC_CHSCATTER_AREA ),
+ mnFlags( 0 )
+{
+}
+
+XclChChart3d::XclChChart3d() :
+ mnRotation( 20 ),
+ mnElevation( 15 ),
+ mnEyeDist( 30 ),
+ mnRelHeight( 100 ),
+ mnRelDepth( 100 ),
+ mnDepthGap( 150 ),
+ mnFlags( EXC_CHCHART3D_AUTOHEIGHT )
+{
+}
+
+XclChLegend::XclChLegend() :
+ mnDockMode( EXC_CHLEGEND_RIGHT ),
+ mnSpacing( EXC_CHLEGEND_MEDIUM ),
+ mnFlags( EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOSERIES |
+ EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY | EXC_CHLEGEND_STACKED )
+{
+}
+
+XclChTypeGroup::XclChTypeGroup() :
+ mnFlags( 0 ),
+ mnGroupIdx( EXC_CHSERGROUP_NONE )
+{
+}
+
+XclChProperties::XclChProperties() :
+ mnFlags( 0 ),
+ mnEmptyMode( EXC_CHPROPS_EMPTY_SKIP )
+{
+}
+
+// Axes =======================================================================
+
+XclChLabelRange::XclChLabelRange() :
+ mnCross( 1 ),
+ mnLabelFreq( 1 ),
+ mnTickFreq( 1 ),
+ mnFlags( 0 )
+{
+}
+
+XclChDateRange::XclChDateRange() :
+ mnMinDate( 0 ),
+ mnMaxDate( 0 ),
+ mnMajorStep( 0 ),
+ mnMajorUnit( EXC_CHDATERANGE_DAYS ),
+ mnMinorStep( 0 ),
+ mnMinorUnit( EXC_CHDATERANGE_DAYS ),
+ mnBaseUnit( EXC_CHDATERANGE_DAYS ),
+ mnCross( 0 ),
+ mnFlags( EXC_CHDATERANGE_AUTOMIN | EXC_CHDATERANGE_AUTOMAX |
+ EXC_CHDATERANGE_AUTOMAJOR | EXC_CHDATERANGE_AUTOMINOR |
+ EXC_CHDATERANGE_AUTOBASE | EXC_CHDATERANGE_AUTOCROSS |
+ EXC_CHDATERANGE_AUTODATE )
+{
+}
+
+XclChValueRange::XclChValueRange() :
+ mfMin( 0.0 ),
+ mfMax( 0.0 ),
+ mfMajorStep( 0.0 ),
+ mfMinorStep( 0.0 ),
+ mfCross( 0.0 ),
+ mnFlags( EXC_CHVALUERANGE_AUTOMIN | EXC_CHVALUERANGE_AUTOMAX |
+ EXC_CHVALUERANGE_AUTOMAJOR | EXC_CHVALUERANGE_AUTOMINOR |
+ EXC_CHVALUERANGE_AUTOCROSS | EXC_CHVALUERANGE_BIT8 )
+{
+}
+
+XclChTick::XclChTick() :
+ maTextColor( COL_BLACK ),
+ mnMajor( EXC_CHTICK_INSIDE | EXC_CHTICK_OUTSIDE ),
+ mnMinor( 0 ),
+ mnLabelPos( EXC_CHTICK_NEXT ),
+ mnBackMode( EXC_CHTICK_TRANSPARENT ),
+ mnFlags( EXC_CHTICK_AUTOCOLOR | EXC_CHTICK_AUTOROT ),
+ mnRotation( EXC_ROT_NONE )
+{
+}
+
+XclChAxis::XclChAxis() :
+ mnType( EXC_CHAXIS_NONE )
+{
+}
+
+sal_Int32 XclChAxis::GetApiAxisDimension() const
+{
+ sal_Int32 nApiAxisDim = EXC_CHART_AXIS_NONE;
+ switch( mnType )
+ {
+ case EXC_CHAXIS_X: nApiAxisDim = EXC_CHART_AXIS_X; break;
+ case EXC_CHAXIS_Y: nApiAxisDim = EXC_CHART_AXIS_Y; break;
+ case EXC_CHAXIS_Z: nApiAxisDim = EXC_CHART_AXIS_Z; break;
+ }
+ return nApiAxisDim;
+}
+
+XclChAxesSet::XclChAxesSet() :
+ mnAxesSetId( EXC_CHAXESSET_PRIMARY )
+{
+}
+
+sal_Int32 XclChAxesSet::GetApiAxesSetIndex() const
+{
+ sal_Int32 nApiAxesSetIdx = EXC_CHART_AXESSET_NONE;
+ switch( mnAxesSetId )
+ {
+ case EXC_CHAXESSET_PRIMARY: nApiAxesSetIdx = EXC_CHART_AXESSET_PRIMARY; break;
+ case EXC_CHAXESSET_SECONDARY: nApiAxesSetIdx = EXC_CHART_AXESSET_SECONDARY; break;
+ }
+ return nApiAxesSetIdx;
+}
+
+// Static helper functions ====================================================
+
+sal_uInt16 XclChartHelper::GetSeriesLineAutoColorIdx( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnLineColors[] =
+ {
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 63
+ };
+ return spnLineColors[ nFormatIdx % SAL_N_ELEMENTS( spnLineColors ) ];
+}
+
+sal_uInt16 XclChartHelper::GetSeriesFillAutoColorIdx( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnFillColors[] =
+ {
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23
+ };
+ return spnFillColors[ nFormatIdx % SAL_N_ELEMENTS( spnFillColors ) ];
+}
+
+sal_uInt8 XclChartHelper::GetSeriesFillAutoTransp( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt8 spnTrans[] = { 0x00, 0x40, 0x20, 0x60, 0x70 };
+ return spnTrans[ (nFormatIdx / 56) % SAL_N_ELEMENTS( spnTrans ) ];
+}
+
+sal_uInt16 XclChartHelper::GetAutoMarkerType( sal_uInt16 nFormatIdx )
+{
+ static const sal_uInt16 spnSymbols[] = {
+ EXC_CHMARKERFORMAT_DIAMOND, EXC_CHMARKERFORMAT_SQUARE, EXC_CHMARKERFORMAT_TRIANGLE,
+ EXC_CHMARKERFORMAT_CROSS, EXC_CHMARKERFORMAT_STAR, EXC_CHMARKERFORMAT_CIRCLE,
+ EXC_CHMARKERFORMAT_PLUS, EXC_CHMARKERFORMAT_DOWJ, EXC_CHMARKERFORMAT_STDDEV };
+ return spnSymbols[ nFormatIdx % SAL_N_ELEMENTS( spnSymbols ) ];
+}
+
+bool XclChartHelper::HasMarkerFillColor( sal_uInt16 nMarkerType )
+{
+ static const bool spbFilled[] = {
+ false, true, true, true, false, false, false, false, true, false };
+ return (nMarkerType < SAL_N_ELEMENTS( spbFilled )) && spbFilled[ nMarkerType ];
+}
+
+OUString XclChartHelper::GetErrorBarValuesRole( sal_uInt8 nBarType )
+{
+ switch( nBarType )
+ {
+ case EXC_CHSERERR_XPLUS: return EXC_CHPROP_ROLE_ERRORBARS_POSX;
+ case EXC_CHSERERR_XMINUS: return EXC_CHPROP_ROLE_ERRORBARS_NEGX;
+ case EXC_CHSERERR_YPLUS: return EXC_CHPROP_ROLE_ERRORBARS_POSY;
+ case EXC_CHSERERR_YMINUS: return EXC_CHPROP_ROLE_ERRORBARS_NEGY;
+ default: OSL_FAIL( "XclChartHelper::GetErrorBarValuesRole - unknown bar type" );
+ }
+ return OUString();
+}
+
+// Chart formatting info provider =============================================
+
+namespace {
+
+const XclChFormatInfo spFmtInfos[] =
+{
+ // object type property mode auto line color auto line weight auto pattern color missing frame type create delete isframe
+ { EXC_CHOBJTYPE_BACKGROUND, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, true, true, true },
+ { EXC_CHOBJTYPE_PLOTFRAME, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, true, true, true },
+ { EXC_CHOBJTYPE_WALL3D, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, true, false, true },
+ { EXC_CHOBJTYPE_FLOOR3D, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, 23, EXC_CHFRAMETYPE_AUTO, true, false, true },
+ { EXC_CHOBJTYPE_TEXT, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, true, true },
+ { EXC_CHOBJTYPE_LEGEND, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, true, true, true },
+ { EXC_CHOBJTYPE_LINEARSERIES, EXC_CHPROPMODE_LINEARSERIES, 0xFFFF, EXC_CHLINEFORMAT_SINGLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, false, false, false },
+ { EXC_CHOBJTYPE_FILLEDSERIES, EXC_CHPROPMODE_FILLEDSERIES, EXC_COLOR_CHBORDERAUTO, EXC_CHLINEFORMAT_SINGLE, 0xFFFF, EXC_CHFRAMETYPE_AUTO, false, false, true },
+ { EXC_CHOBJTYPE_AXISLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_AUTO, false, false, false },
+ { EXC_CHOBJTYPE_GRIDLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, true, false },
+ { EXC_CHOBJTYPE_TRENDLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_DOUBLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_ERRORBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_SINGLE, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_CONNECTLINE, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_HILOLINE, EXC_CHPROPMODE_LINEARSERIES, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, false },
+ { EXC_CHOBJTYPE_WHITEDROPBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWBACK, EXC_CHFRAMETYPE_INVISIBLE, false, false, true },
+ { EXC_CHOBJTYPE_BLACKDROPBAR, EXC_CHPROPMODE_COMMON, EXC_COLOR_CHWINDOWTEXT, EXC_CHLINEFORMAT_HAIR, EXC_COLOR_CHWINDOWTEXT, EXC_CHFRAMETYPE_INVISIBLE, false, false, true }
+};
+
+}
+
+XclChFormatInfoProvider::XclChFormatInfoProvider()
+{
+ for(auto const &rIt : spFmtInfos)
+ maInfoMap[ rIt.meObjType ] = &rIt;
+}
+
+const XclChFormatInfo& XclChFormatInfoProvider::GetFormatInfo( XclChObjectType eObjType ) const
+{
+ XclFmtInfoMap::const_iterator aIt = maInfoMap.find( eObjType );
+ OSL_ENSURE( aIt != maInfoMap.end(), "XclChFormatInfoProvider::GetFormatInfo - unknown object type" );
+ return (aIt == maInfoMap.end()) ? *spFmtInfos : *aIt->second;
+}
+
+// Chart type info provider ===================================================
+
+namespace {
+
+// chart type service names
+const char SERVICE_CHART2_AREA[] = "com.sun.star.chart2.AreaChartType";
+const char SERVICE_CHART2_CANDLE[] = "com.sun.star.chart2.CandleStickChartType";
+const char SERVICE_CHART2_COLUMN[] = "com.sun.star.chart2.ColumnChartType";
+const char SERVICE_CHART2_LINE[] = "com.sun.star.chart2.LineChartType";
+const char SERVICE_CHART2_NET[] = "com.sun.star.chart2.NetChartType";
+const char SERVICE_CHART2_FILLEDNET[] = "com.sun.star.chart2.FilledNetChartType";
+const char SERVICE_CHART2_PIE[] = "com.sun.star.chart2.PieChartType";
+const char SERVICE_CHART2_SCATTER[] = "com.sun.star.chart2.ScatterChartType";
+const char SERVICE_CHART2_BUBBLE[] = "com.sun.star.chart2.BubbleChartType";
+const char SERVICE_CHART2_SURFACE[] = "com.sun.star.chart2.ColumnChartType"; // Todo
+
+namespace csscd = css::chart::DataLabelPlacement;
+
+const XclChTypeInfo spTypeInfos[] =
+{
+ // chart type chart type category record id service varied point color def label pos comb2d 3d polar area2d area3d 1stvis xcateg swap stack revers betw
+ { EXC_CHTYPEID_BAR, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, true, true, false, true, true, false, true, false, true, false, true },
+ { EXC_CHTYPEID_HORBAR, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, false, true, false, true, true, false, true, true, true, false, true },
+ { EXC_CHTYPEID_LINE, EXC_CHTYPECATEG_LINE, EXC_ID_CHLINE, SERVICE_CHART2_LINE, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, true, true, false, false, true, false, true, false, true, false, false },
+ { EXC_CHTYPEID_AREA, EXC_CHTYPECATEG_LINE, EXC_ID_CHAREA, SERVICE_CHART2_AREA, EXC_CHVARPOINT_NONE, csscd::CENTER, true, true, false, true, true, false, true, false, true, true, false },
+ { EXC_CHTYPEID_STOCK, EXC_CHTYPECATEG_LINE, EXC_ID_CHLINE, SERVICE_CHART2_CANDLE, EXC_CHVARPOINT_NONE, csscd::RIGHT, true, false, false, false, false, false, true, false, true, false, false },
+ { EXC_CHTYPEID_RADARLINE, EXC_CHTYPECATEG_RADAR, EXC_ID_CHRADARLINE, SERVICE_CHART2_NET, EXC_CHVARPOINT_SINGLE, csscd::TOP, false, false, true, false, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_RADARAREA, EXC_CHTYPECATEG_RADAR, EXC_ID_CHRADARAREA, SERVICE_CHART2_FILLEDNET, EXC_CHVARPOINT_NONE, csscd::TOP, false, false, true, true, true, false, true, false, false, true, false },
+ { EXC_CHTYPEID_PIE, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIE, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, true, true, true, true, true, true, false, false, false, false },
+ { EXC_CHTYPEID_DONUT, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIE, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, true, true, true, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_PIEEXT, EXC_CHTYPECATEG_PIE, EXC_ID_CHPIEEXT, SERVICE_CHART2_PIE, EXC_CHVARPOINT_MULTI, csscd::AVOID_OVERLAP, false, false, true, true, true, true, true, false, false, false, false },
+ { EXC_CHTYPEID_SCATTER, EXC_CHTYPECATEG_SCATTER, EXC_ID_CHSCATTER, SERVICE_CHART2_SCATTER, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, true, false, false, false, true, false, false, false, false, false, false },
+ { EXC_CHTYPEID_BUBBLES, EXC_CHTYPECATEG_SCATTER, EXC_ID_CHSCATTER, SERVICE_CHART2_BUBBLE, EXC_CHVARPOINT_SINGLE, csscd::RIGHT, false, false, false, true, true, false, false, false, false, false, false },
+ { EXC_CHTYPEID_SURFACE, EXC_CHTYPECATEG_SURFACE, EXC_ID_CHSURFACE, SERVICE_CHART2_SURFACE, EXC_CHVARPOINT_NONE, csscd::RIGHT, false, true, false, true, true, false, true, false, false, false, false },
+ { EXC_CHTYPEID_UNKNOWN, EXC_CHTYPECATEG_BAR, EXC_ID_CHBAR, SERVICE_CHART2_COLUMN, EXC_CHVARPOINT_SINGLE, csscd::OUTSIDE, true, true, false, true, true, false, true, false, true, false, true }
+};
+
+} // namespace
+
+XclChExtTypeInfo::XclChExtTypeInfo( const XclChTypeInfo& rTypeInfo ) :
+ XclChTypeInfo( rTypeInfo ),
+ mb3dChart( false ),
+ mbSpline( false )
+{
+}
+
+void XclChExtTypeInfo::Set( const XclChTypeInfo& rTypeInfo, bool b3dChart, bool bSpline )
+{
+ static_cast< XclChTypeInfo& >( *this ) = rTypeInfo;
+ mb3dChart = mbSupports3d && b3dChart;
+ mbSpline = bSpline;
+}
+
+XclChTypeInfoProvider::XclChTypeInfoProvider()
+{
+ for(const auto &rIt : spTypeInfos)
+ maInfoMap[ rIt.meTypeId ] = &rIt;
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfo( XclChTypeId eTypeId ) const
+{
+ XclChTypeInfoMap::const_iterator aIt = maInfoMap.find( eTypeId );
+ OSL_ENSURE( aIt != maInfoMap.end(), "XclChTypeInfoProvider::GetTypeInfo - unknown chart type" );
+ return (aIt == maInfoMap.end()) ? *maInfoMap.rbegin()->second : *aIt->second;
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfoFromRecId( sal_uInt16 nRecId ) const
+{
+ for(const auto &rIt : spTypeInfos)
+ {
+ if(rIt.mnRecId == nRecId)
+ return rIt;
+ }
+ OSL_FAIL( "XclChTypeInfoProvider::GetTypeInfoFromRecId - unknown record id" );
+ return GetTypeInfo( EXC_CHTYPEID_UNKNOWN );
+}
+
+const XclChTypeInfo& XclChTypeInfoProvider::GetTypeInfoFromService( std::u16string_view rServiceName ) const
+{
+ for(auto const &rIt : spTypeInfos)
+ if( o3tl::equalsAscii( rServiceName, rIt.mpcServiceName ) )
+ return rIt;
+ OSL_FAIL( "XclChTypeInfoProvider::GetTypeInfoFromService - unknown service name" );
+ return GetTypeInfo( EXC_CHTYPEID_UNKNOWN );
+}
+
+// Property helpers ===========================================================
+
+XclChObjectTable::XclChObjectTable(uno::Reference<lang::XMultiServiceFactory> const & xFactory,
+ const OUString& rServiceName, const OUString& rObjNameBase ) :
+ mxFactory( xFactory ),
+ maServiceName( rServiceName ),
+ maObjNameBase( rObjNameBase ),
+ mnIndex( 0 )
+{
+}
+
+uno::Any XclChObjectTable::GetObject( const OUString& rObjName )
+{
+ // get object table
+ if( !mxContainer.is() )
+ mxContainer.set(ScfApiHelper::CreateInstance( mxFactory, maServiceName ), uno::UNO_QUERY);
+ OSL_ENSURE( mxContainer.is(), "XclChObjectTable::GetObject - container not found" );
+
+ uno::Any aObj;
+ if( mxContainer.is() )
+ {
+ // get object from container
+ try
+ {
+ aObj = mxContainer->getByName( rObjName );
+ }
+ catch (uno::Exception &)
+ {
+ OSL_FAIL( "XclChObjectTable::GetObject - object not found" );
+ }
+ }
+ return aObj;
+}
+
+OUString XclChObjectTable::InsertObject(const uno::Any& rObj)
+{
+
+ // create object table
+ if( !mxContainer.is() )
+ mxContainer.set(ScfApiHelper::CreateInstance( mxFactory, maServiceName ), uno::UNO_QUERY);
+ OSL_ENSURE( mxContainer.is(), "XclChObjectTable::InsertObject - container not found" );
+
+ OUString aObjName;
+ if( mxContainer.is() )
+ {
+ // create new unused identifier
+ do
+ {
+ aObjName = maObjNameBase + OUString::number( ++mnIndex );
+ }
+ while( mxContainer->hasByName( aObjName ) );
+
+ // insert object
+ try
+ {
+ mxContainer->insertByName( aObjName, rObj );
+ }
+ catch (uno::Exception &)
+ {
+ OSL_FAIL( "XclChObjectTable::InsertObject - cannot insert object" );
+ aObjName.clear();
+ }
+ }
+ return aObjName;
+}
+
+// Property names -------------------------------------------------------------
+
+namespace {
+
+/** Property names for line style in common objects. */
+const char* const sppcLineNamesCommon[] =
+ { "LineStyle", "LineWidth", "LineColor", "LineTransparence", "LineDashName", nullptr };
+/** Property names for line style in linear series objects. */
+const char* const sppcLineNamesLinear[] =
+ { "LineStyle", "LineWidth", "Color", "Transparency", "LineDashName", nullptr };
+/** Property names for line style in filled series objects. */
+const char* const sppcLineNamesFilled[] =
+ { "BorderStyle", "BorderWidth", "BorderColor", "BorderTransparency", "BorderDashName", nullptr };
+
+/** Property names for solid area style in common objects. */
+const char* const sppcAreaNamesCommon[] = { "FillStyle", "FillColor", "FillTransparence", nullptr };
+/** Property names for solid area style in filled series objects. */
+const char* const sppcAreaNamesFilled[] = { "FillStyle", "Color", "Transparency", nullptr };
+/** Property names for gradient area style in common objects. */
+const char* const sppcGradNamesCommon[] = { "FillStyle", "FillGradientName", nullptr };
+/** Property names for gradient area style in filled series objects. */
+const char* const sppcGradNamesFilled[] = { "FillStyle", "GradientName", nullptr };
+/** Property names for hatch area style in common objects. */
+const char* const sppcHatchNamesCommon[] = { "FillStyle", "FillHatchName", "FillColor", "FillBackground", nullptr };
+/** Property names for hatch area style in filled series objects. */
+const char* const sppcHatchNamesFilled[] = { "FillStyle", "HatchName", "Color", "FillBackground", nullptr };
+/** Property names for bitmap area style. */
+const char* const sppcBitmapNames[] = { "FillStyle", "FillBitmapName", "FillBitmapMode", nullptr };
+
+} // namespace
+
+XclChPropSetHelper::XclChPropSetHelper() :
+ maLineHlpCommon( sppcLineNamesCommon ),
+ maLineHlpLinear( sppcLineNamesLinear ),
+ maLineHlpFilled( sppcLineNamesFilled ),
+ maAreaHlpCommon( sppcAreaNamesCommon ),
+ maAreaHlpFilled( sppcAreaNamesFilled ),
+ maGradHlpCommon( sppcGradNamesCommon ),
+ maGradHlpFilled( sppcGradNamesFilled ),
+ maHatchHlpCommon( sppcHatchNamesCommon ),
+ maHatchHlpFilled( sppcHatchNamesFilled ),
+ maBitmapHlp( sppcBitmapNames )
+{
+}
+
+// read properties ------------------------------------------------------------
+
+void XclChPropSetHelper::ReadLineProperties(
+ XclChLineFormat& rLineFmt, XclChObjectTable& rDashTable,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read properties from property set
+ drawing::LineStyle eApiStyle = drawing::LineStyle_NONE;
+ sal_Int32 nApiWidth = 0;
+ sal_Int16 nApiTrans = 0;
+ uno::Any aDashNameAny;
+
+ ScfPropSetHelper& rLineHlp = GetLineHelper( ePropMode );
+ rLineHlp.ReadFromPropertySet( rPropSet );
+ rLineHlp >> eApiStyle >> nApiWidth >> rLineFmt.maColor >> nApiTrans >> aDashNameAny;
+
+ // clear automatic flag
+ ::set_flag( rLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false );
+
+ // line width
+ if( nApiWidth <= 0 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR;
+ else if( nApiWidth <= 35 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE;
+ else if( nApiWidth <= 70 ) rLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE;
+ else rLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE;
+
+ // line style
+ switch( eApiStyle )
+ {
+ case drawing::LineStyle_NONE:
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ break;
+ case drawing::LineStyle_SOLID:
+ {
+ if( nApiTrans < 13 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ else if( nApiTrans < 38 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS;
+ else if( nApiTrans < 63 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS;
+ else if( nApiTrans < 100 ) rLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS;
+ else rLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
+ }
+ break;
+ case drawing::LineStyle_DASH:
+ {
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ OUString aDashName;
+ drawing::LineDash aApiDash;
+ if( (aDashNameAny >>= aDashName) && (rDashTable.GetObject( aDashName ) >>= aApiDash) )
+ {
+ // reorder dashes that are shorter than dots
+ if( (aApiDash.Dashes == 0) || (aApiDash.DashLen < aApiDash.DotLen) )
+ {
+ ::std::swap( aApiDash.Dashes, aApiDash.Dots );
+ ::std::swap( aApiDash.DashLen, aApiDash.DotLen );
+ }
+ // ignore dots that are nearly equal to dashes
+ if( aApiDash.DotLen * 3 > aApiDash.DashLen * 2 )
+ aApiDash.Dots = 0;
+
+ // convert line dash to predefined Excel dash types
+ if( (aApiDash.Dashes == 1) && (aApiDash.Dots >= 1) )
+ // one dash and one or more dots
+ rLineFmt.mnPattern = (aApiDash.Dots == 1) ?
+ EXC_CHLINEFORMAT_DASHDOT : EXC_CHLINEFORMAT_DASHDOTDOT;
+ else if( aApiDash.Dashes >= 1 )
+ // one or more dashes and no dots (also: dash-dash-dot)
+ rLineFmt.mnPattern = (aApiDash.DashLen < 250) ?
+ EXC_CHLINEFORMAT_DOT : EXC_CHLINEFORMAT_DASH;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::ReadLineProperties - unknown line style" );
+ rLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
+ }
+}
+
+bool XclChPropSetHelper::ReadAreaProperties( XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read properties from property set
+ drawing::FillStyle eApiStyle = drawing::FillStyle_NONE;
+ sal_Int16 nTransparency = 0;
+
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.ReadFromPropertySet( rPropSet );
+ rAreaHlp >> eApiStyle >> rAreaFmt.maPattColor >> nTransparency;
+
+ // clear automatic flag
+ ::set_flag( rAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false );
+
+ // set fill style transparent or solid (set solid for anything but transparent)
+ rAreaFmt.mnPattern = (eApiStyle == drawing::FillStyle_NONE) ? EXC_PATT_NONE : EXC_PATT_SOLID;
+
+ // return true to indicate complex fill (gradient, bitmap, solid transparency)
+ return (eApiStyle != drawing::FillStyle_NONE) && ((eApiStyle != drawing::FillStyle_SOLID) || (nTransparency > 0));
+}
+
+void XclChPropSetHelper::ReadEscherProperties(
+ XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt,
+ XclChObjectTable& rGradientTable, XclChObjectTable& rHatchTable, XclChObjectTable& rBitmapTable,
+ const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode )
+{
+ // read style and transparency properties from property set
+ drawing::FillStyle eApiStyle = drawing::FillStyle_NONE;
+ Color aColor;
+ sal_Int16 nTransparency = 0;
+
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.ReadFromPropertySet( rPropSet );
+ rAreaHlp >> eApiStyle >> aColor >> nTransparency;
+
+ switch( eApiStyle )
+ {
+ case drawing::FillStyle_SOLID:
+ {
+ OSL_ENSURE( nTransparency > 0, "XclChPropSetHelper::ReadEscherProperties - unexpected solid area without transparency" );
+ if( (0 < nTransparency) && (nTransparency <= 100) )
+ {
+ // convert to Escher properties
+ sal_uInt32 nEscherColor = 0x02000000;
+ ::insert_value( nEscherColor, aColor.GetBlue(), 16, 8 );
+ ::insert_value( nEscherColor, aColor.GetGreen(), 8, 8 );
+ ::insert_value( nEscherColor, aColor.GetRed(), 0, 8 );
+ sal_uInt32 nEscherOpacity = static_cast< sal_uInt32 >( (100 - nTransparency) * 655.36 );
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, nEscherColor );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillOpacity, nEscherOpacity );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x02FFFFFF );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fillBackOpacity, 0x00010000 );
+ rEscherFmt.mxEscherSet->AddOpt( ESCHER_Prop_fNoFillHitTest, 0x001F001C );
+ }
+ }
+ break;
+ case drawing::FillStyle_GRADIENT:
+ {
+ // extract gradient from global gradient table
+ OUString aGradientName;
+ ScfPropSetHelper& rGradHlp = GetGradientHelper( ePropMode );
+ rGradHlp.ReadFromPropertySet( rPropSet );
+ rGradHlp >> eApiStyle >> aGradientName;
+ awt::Gradient aGradient;
+ if( rGradientTable.GetObject( aGradientName ) >>= aGradient )
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateGradientProperties( aGradient );
+ }
+ }
+ break;
+ case drawing::FillStyle_HATCH:
+ {
+ // extract hatch from global hatch table
+ OUString aHatchName;
+ bool bFillBackground;
+ ScfPropSetHelper& rHatchHlp = GetHatchHelper( ePropMode );
+ rHatchHlp.ReadFromPropertySet( rPropSet );
+ rHatchHlp >> eApiStyle >> aHatchName >> aColor >> bFillBackground;
+ drawing::Hatch aHatch;
+ if( rHatchTable.GetObject( aHatchName ) >>= aHatch )
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateEmbeddedHatchProperties( aHatch, aColor, bFillBackground );
+ rPicFmt.mnBmpMode = EXC_CHPICFORMAT_STACK;
+ }
+ }
+ break;
+ case drawing::FillStyle_BITMAP:
+ {
+ // extract bitmap URL from global bitmap table
+ OUString aBitmapName;
+ drawing::BitmapMode eApiBmpMode;
+ maBitmapHlp.ReadFromPropertySet( rPropSet );
+ maBitmapHlp >> eApiStyle >> aBitmapName >> eApiBmpMode;
+ uno::Reference<awt::XBitmap> xBitmap;
+ if (rBitmapTable.GetObject( aBitmapName ) >>= xBitmap)
+ {
+ // convert to Escher properties
+ rEscherFmt.mxEscherSet = std::make_shared<EscherPropertyContainer>();
+ rEscherFmt.mxEscherSet->CreateEmbeddedBitmapProperties( xBitmap, eApiBmpMode );
+ rPicFmt.mnBmpMode = (eApiBmpMode == drawing::BitmapMode_REPEAT) ?
+ EXC_CHPICFORMAT_STACK : EXC_CHPICFORMAT_STRETCH;
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::ReadEscherProperties - unknown fill style" );
+ }
+}
+
+void XclChPropSetHelper::ReadMarkerProperties(
+ XclChMarkerFormat& rMarkerFmt, const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx )
+{
+ chart2::Symbol aApiSymbol;
+ if( !rPropSet.GetProperty( aApiSymbol, EXC_CHPROP_SYMBOL ) )
+ return;
+
+ // clear automatic flag
+ ::set_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_AUTO, false );
+
+ // symbol style
+ switch( aApiSymbol.Style )
+ {
+ case chart2::SymbolStyle_NONE:
+ rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL;
+ break;
+ case chart2::SymbolStyle_STANDARD:
+ switch( aApiSymbol.StandardSymbol )
+ {
+ case 0: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_SQUARE; break; // square
+ case 1: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DIAMOND; break; // diamond
+ case 2: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STDDEV; break; // arrow down
+ case 3: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_TRIANGLE; break; // arrow up
+ case 4: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ; break; // arrow right, same as import
+ case 5: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_PLUS; break; // arrow left
+ case 6: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CROSS; break; // bow tie
+ case 7: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // sand glass
+ case 8: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CIRCLE; break; // circle new in LibO3.5
+ case 9: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_DIAMOND; break; // star new in LibO3.5
+ case 10: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_CROSS; break; // X new in LibO3.5
+ case 11: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_PLUS; break; // plus new in LibO3.5
+ case 12: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // asterisk new in LibO3.5
+ case 13: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STDDEV; break; // horizontal bar new in LibO3.5
+ case 14: rMarkerFmt.mnMarkerType = EXC_CHMARKERFORMAT_STAR; break; // vertical bar new in LibO3.5
+ default: rMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ }
+ break;
+ default:
+ rMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
+ }
+ bool bHasFillColor = XclChartHelper::HasMarkerFillColor( rMarkerFmt.mnMarkerType );
+ ::set_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_NOFILL, !bHasFillColor );
+
+ // symbol size
+ sal_Int32 nApiSize = (aApiSymbol.Size.Width + aApiSymbol.Size.Height + 1) / 2;
+ rMarkerFmt.mnMarkerSize = XclTools::GetTwipsFromHmm( nApiSize );
+
+ // symbol colors
+ rMarkerFmt.maLineColor = Color( ColorTransparency, aApiSymbol.BorderColor );
+ rMarkerFmt.maFillColor = Color( ColorTransparency, aApiSymbol.FillColor );
+}
+
+sal_uInt16 XclChPropSetHelper::ReadRotationProperties( const ScfPropertySet& rPropSet, bool bSupportsStacked )
+{
+ // chart2 handles rotation as double in the range [0,360)
+ double fAngle = 0.0;
+ rPropSet.GetProperty( fAngle, EXC_CHPROP_TEXTROTATION );
+ bool bStacked = bSupportsStacked && rPropSet.GetBoolProperty( EXC_CHPROP_STACKCHARACTERS );
+ return bStacked ? EXC_ROT_STACKED :
+ XclTools::GetXclRotation( Degree100(static_cast< sal_Int32 >( fAngle * 100.0 + 0.5 )) );
+}
+
+// write properties -----------------------------------------------------------
+
+void XclChPropSetHelper::WriteLineProperties(
+ ScfPropertySet& rPropSet, XclChObjectTable& rDashTable,
+ const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode )
+{
+ // line width
+ sal_Int32 nApiWidth = 0; // 0 is the width of a hair line
+ switch( rLineFmt.mnWeight )
+ {
+ case EXC_CHLINEFORMAT_SINGLE: nApiWidth = 35; break;
+ case EXC_CHLINEFORMAT_DOUBLE: nApiWidth = 70; break;
+ case EXC_CHLINEFORMAT_TRIPLE: nApiWidth = 105; break;
+ }
+
+ // line style
+ drawing::LineStyle eApiStyle = drawing::LineStyle_NONE;
+ sal_Int16 nApiTrans = 0;
+ sal_Int32 nDotLen = ::std::min< sal_Int32 >( rLineFmt.mnWeight + 105, 210 );
+ drawing::LineDash aApiDash( drawing::DashStyle_RECT, 0, nDotLen, 0, 4 * nDotLen, nDotLen );
+
+ switch( rLineFmt.mnPattern )
+ {
+ case EXC_CHLINEFORMAT_SOLID:
+ eApiStyle = drawing::LineStyle_SOLID;
+ break;
+ case EXC_CHLINEFORMAT_DARKTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 25;
+ break;
+ case EXC_CHLINEFORMAT_MEDTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 50;
+ break;
+ case EXC_CHLINEFORMAT_LIGHTTRANS:
+ eApiStyle = drawing::LineStyle_SOLID; nApiTrans = 75;
+ break;
+ case EXC_CHLINEFORMAT_DASH:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = 1;
+ break;
+ case EXC_CHLINEFORMAT_DOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dots = 1;
+ break;
+ case EXC_CHLINEFORMAT_DASHDOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = aApiDash.Dots = 1;
+ break;
+ case EXC_CHLINEFORMAT_DASHDOTDOT:
+ eApiStyle = drawing::LineStyle_DASH; aApiDash.Dashes = 1; aApiDash.Dots = 2;
+ break;
+ }
+
+ // line color
+ sal_Int32 nApiColor = sal_Int32( rLineFmt.maColor );
+
+ // try to insert the dash style and receive its name
+ uno::Any aDashNameAny;
+ if( eApiStyle == drawing::LineStyle_DASH )
+ {
+ OUString aDashName = rDashTable.InsertObject( uno::Any( aApiDash ) );
+ if( !aDashName.isEmpty() )
+ aDashNameAny <<= aDashName;
+ }
+
+ // write the properties
+ ScfPropSetHelper& rLineHlp = GetLineHelper( ePropMode );
+ rLineHlp.InitializeWrite();
+ rLineHlp << eApiStyle << nApiWidth << nApiColor << nApiTrans << aDashNameAny;
+ rLineHlp.WriteToPropertySet( rPropSet );
+}
+
+void XclChPropSetHelper::WriteAreaProperties( ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode )
+{
+ drawing::FillStyle eFillStyle = drawing::FillStyle_NONE;
+ Color aColor;
+
+ // fill color
+ if( rAreaFmt.mnPattern != EXC_PATT_NONE )
+ {
+ eFillStyle = drawing::FillStyle_SOLID;
+ aColor = XclTools::GetPatternColor( rAreaFmt.maPattColor, rAreaFmt.maBackColor, rAreaFmt.mnPattern );
+ }
+
+ // write the properties
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.InitializeWrite();
+ rAreaHlp << eFillStyle << aColor << sal_Int16(0)/*nTransparency*/;
+ rAreaHlp.WriteToPropertySet( rPropSet );
+}
+
+void XclChPropSetHelper::WriteEscherProperties( ScfPropertySet& rPropSet,
+ XclChObjectTable& rGradientTable, XclChObjectTable& rBitmapTable,
+ const XclChEscherFormat& rEscherFmt, const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType, XclChPropertyMode ePropMode )
+{
+ if( !rEscherFmt.mxItemSet )
+ return;
+
+ const XFillStyleItem* pStyleItem = rEscherFmt.mxItemSet->GetItem<XFillStyleItem>( XATTR_FILLSTYLE, false );
+ if( !pStyleItem )
+ return;
+
+ switch( pStyleItem->GetValue() )
+ {
+ case drawing::FillStyle_SOLID:
+ // #i84812# Excel 2007 writes Escher properties for solid fill
+ if( const XFillColorItem* pColorItem = rEscherFmt.mxItemSet->GetItem<XFillColorItem>( XATTR_FILLCOLOR, false ) )
+ {
+ // get solid transparence too
+ const XFillTransparenceItem* pTranspItem = rEscherFmt.mxItemSet->GetItem<XFillTransparenceItem>( XATTR_FILLTRANSPARENCE, false );
+ sal_uInt16 nTransp = pTranspItem ? pTranspItem->GetValue() : 0;
+ ScfPropSetHelper& rAreaHlp = GetAreaHelper( ePropMode );
+ rAreaHlp.InitializeWrite();
+ rAreaHlp << drawing::FillStyle_SOLID << pColorItem->GetColorValue() << static_cast< sal_Int16 >( nTransp );
+ rAreaHlp.WriteToPropertySet( rPropSet );
+ }
+ break;
+ case drawing::FillStyle_GRADIENT:
+ if( const XFillGradientItem* pGradItem = rEscherFmt.mxItemSet->GetItem<XFillGradientItem>( XATTR_FILLGRADIENT, false ) )
+ {
+ uno::Any aGradientAny;
+ if( pGradItem->QueryValue( aGradientAny, MID_FILLGRADIENT ) )
+ {
+ OUString aGradName = rGradientTable.InsertObject( aGradientAny );
+ if( !aGradName.isEmpty() )
+ {
+ ScfPropSetHelper& rGradHlp = GetGradientHelper( ePropMode );
+ rGradHlp.InitializeWrite();
+ rGradHlp << drawing::FillStyle_GRADIENT << aGradName;
+ rGradHlp.WriteToPropertySet( rPropSet );
+ }
+ }
+ }
+ break;
+ case drawing::FillStyle_BITMAP:
+ if( const XFillBitmapItem* pBmpItem = rEscherFmt.mxItemSet->GetItem<XFillBitmapItem>( XATTR_FILLBITMAP, false ) )
+ {
+ uno::Any aBitmapAny;
+ if (pBmpItem->QueryValue(aBitmapAny, MID_BITMAP))
+ {
+ OUString aBmpName = rBitmapTable.InsertObject( aBitmapAny );
+ if( !aBmpName.isEmpty() )
+ {
+ /* #i71810# Caller decides whether to use a CHPICFORMAT record for bitmap mode.
+ If not passed, detect fill mode from the DFF property 'fill-type'. */
+ bool bStretch = pPicFmt ? (pPicFmt->mnBmpMode == EXC_CHPICFORMAT_STRETCH) : (nDffFillType == mso_fillPicture);
+ drawing::BitmapMode eApiBmpMode = bStretch ? drawing::BitmapMode_STRETCH : drawing::BitmapMode_REPEAT;
+ maBitmapHlp.InitializeWrite();
+ maBitmapHlp << drawing::FillStyle_BITMAP << aBmpName << eApiBmpMode;
+ maBitmapHlp.WriteToPropertySet( rPropSet );
+ }
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclChPropSetHelper::WriteEscherProperties - unknown fill mode" );
+ }
+}
+
+void XclChPropSetHelper::WriteMarkerProperties(
+ ScfPropertySet& rPropSet, const XclChMarkerFormat& rMarkerFmt )
+{
+ // symbol style
+ chart2::Symbol aApiSymbol;
+ aApiSymbol.Style = chart2::SymbolStyle_STANDARD;
+ switch( rMarkerFmt.mnMarkerType )
+ {
+ case EXC_CHMARKERFORMAT_NOSYMBOL: aApiSymbol.Style = chart2::SymbolStyle_NONE; break;
+ case EXC_CHMARKERFORMAT_SQUARE: aApiSymbol.StandardSymbol = 0; break; // square
+ case EXC_CHMARKERFORMAT_DIAMOND: aApiSymbol.StandardSymbol = 1; break; // diamond
+ case EXC_CHMARKERFORMAT_TRIANGLE: aApiSymbol.StandardSymbol = 3; break; // arrow up
+ case EXC_CHMARKERFORMAT_CROSS: aApiSymbol.StandardSymbol = 10; break; // X, legacy bow tie
+ case EXC_CHMARKERFORMAT_STAR: aApiSymbol.StandardSymbol = 12; break; // asterisk, legacy sand glass
+ case EXC_CHMARKERFORMAT_DOWJ: aApiSymbol.StandardSymbol = 4; break; // arrow right, same as export
+ case EXC_CHMARKERFORMAT_STDDEV: aApiSymbol.StandardSymbol = 13; break; // horizontal bar, legacy arrow down
+ case EXC_CHMARKERFORMAT_CIRCLE: aApiSymbol.StandardSymbol = 8; break; // circle, legacy arrow right
+ case EXC_CHMARKERFORMAT_PLUS: aApiSymbol.StandardSymbol = 11; break; // plus, legacy arrow left
+ default: break;
+ }
+
+ // symbol size
+ sal_Int32 nApiSize = XclTools::GetHmmFromTwips( rMarkerFmt.mnMarkerSize );
+ aApiSymbol.Size = awt::Size( nApiSize, nApiSize );
+
+ // symbol colors
+ aApiSymbol.FillColor = sal_Int32( rMarkerFmt.maFillColor );
+ aApiSymbol.BorderColor = ::get_flag( rMarkerFmt.mnFlags, EXC_CHMARKERFORMAT_NOLINE ) ?
+ aApiSymbol.FillColor : sal_Int32( rMarkerFmt.maLineColor );
+
+ // set the property
+ rPropSet.SetProperty( EXC_CHPROP_SYMBOL, aApiSymbol );
+}
+
+void XclChPropSetHelper::WriteRotationProperties(
+ ScfPropertySet& rPropSet, sal_uInt16 nRotation, bool bSupportsStacked )
+{
+ if( nRotation != EXC_CHART_AUTOROTATION )
+ {
+ // chart2 handles rotation as double in the range [0,360)
+ double fAngle = XclTools::GetScRotation( nRotation, 0_deg100 ).get() / 100.0;
+ rPropSet.SetProperty( EXC_CHPROP_TEXTROTATION, fAngle );
+ if( bSupportsStacked )
+ rPropSet.SetProperty( EXC_CHPROP_STACKCHARACTERS, nRotation == EXC_ROT_STACKED );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+ScfPropSetHelper& XclChPropSetHelper::GetLineHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maLineHlpCommon;
+ case EXC_CHPROPMODE_LINEARSERIES: return maLineHlpLinear;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maLineHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetLineHelper - unknown property mode" );
+ }
+ return maLineHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetAreaHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maAreaHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maAreaHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetAreaHelper - unknown property mode" );
+ }
+ return maAreaHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetGradientHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maGradHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maGradHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetGradientHelper - unknown property mode" );
+ }
+ return maGradHlpCommon;
+}
+
+ScfPropSetHelper& XclChPropSetHelper::GetHatchHelper( XclChPropertyMode ePropMode )
+{
+ switch( ePropMode )
+ {
+ case EXC_CHPROPMODE_COMMON: return maHatchHlpCommon;
+ case EXC_CHPROPMODE_FILLEDSERIES: return maHatchHlpFilled;
+ default: OSL_FAIL( "XclChPropSetHelper::GetHatchHelper - unknown property mode" );
+ }
+ return maHatchHlpCommon;
+}
+
+namespace {
+
+/* The following local functions implement getting the XShape interface of all
+ supported title objects (chart and axes). This needs some effort due to the
+ design of the old Chart1 API used to access these objects. */
+
+/** Returns the drawing shape of the main title, if existing. */
+uno::Reference<drawing::XShape> lclGetMainTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ ScfPropertySet aPropSet(rxChart1Doc);
+ if (rxChart1Doc.is() && aPropSet.GetBoolProperty("HasMainTitle"))
+ return rxChart1Doc->getTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetXAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XAxisXSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasXAxisTitle"))
+ return xAxisSupp->getXAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetYAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc )
+{
+ uno::Reference<chart::XAxisYSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasYAxisTitle"))
+ return xAxisSupp->getYAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetZAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc )
+{
+ uno::Reference<chart::XAxisZSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasZAxisTitle"))
+ return xAxisSupp->getZAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetSecXAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XSecondAxisTitleSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasSecondaryXAxisTitle"))
+ return xAxisSupp->getSecondXAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+uno::Reference<drawing::XShape> lclGetSecYAxisTitleShape(const uno::Reference<chart::XChartDocument> & rxChart1Doc)
+{
+ uno::Reference<chart::XSecondAxisTitleSupplier> xAxisSupp(rxChart1Doc->getDiagram(), uno::UNO_QUERY);
+ ScfPropertySet aPropSet(xAxisSupp);
+ if (xAxisSupp.is() && aPropSet.GetBoolProperty("HasSecondaryYAxisTitle"))
+ return xAxisSupp->getSecondYAxisTitle();
+ return uno::Reference<drawing::XShape>();
+}
+
+} // namespace
+
+XclChRootData::XclChRootData()
+ : mxTypeInfoProv(std::make_shared<XclChTypeInfoProvider>())
+ , mxFmtInfoProv(std::make_shared<XclChFormatInfoProvider>())
+ , mnBorderGapX(0)
+ , mnBorderGapY(0)
+ , mfUnitSizeX(0.0)
+ , mfUnitSizeY(0.0)
+{
+ // remember some title shape getter functions
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_TITLE ) ] = lclGetMainTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_X ) ] = lclGetXAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_Y ) ] = lclGetYAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_PRIMARY, EXC_CHAXIS_Z ) ] = lclGetZAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_SECONDARY, EXC_CHAXIS_X ) ] = lclGetSecXAxisTitleShape;
+ maGetShapeFuncs[ XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, EXC_CHAXESSET_SECONDARY, EXC_CHAXIS_Y ) ] = lclGetSecYAxisTitleShape;
+}
+
+XclChRootData::~XclChRootData()
+{
+}
+
+void XclChRootData::InitConversion(const XclRoot& rRoot, const uno::Reference<chart2::XChartDocument> & rxChartDoc, const tools::Rectangle& rChartRect)
+{
+ // remember chart document reference and chart shape position/size
+ OSL_ENSURE( rxChartDoc.is(), "XclChRootData::InitConversion - missing chart document" );
+ mxChartDoc = rxChartDoc;
+ maChartRect = rChartRect;
+
+ // Excel excludes a border of 5 pixels in each direction from chart area
+ mnBorderGapX = rRoot.GetHmmFromPixelX( 5.0 );
+ mnBorderGapY = rRoot.GetHmmFromPixelY( 5.0 );
+
+ // size of a chart unit in 1/100 mm
+ mfUnitSizeX = std::max<double>( maChartRect.GetWidth() - 2 * mnBorderGapX, mnBorderGapX ) / EXC_CHART_TOTALUNITS;
+ mfUnitSizeY = std::max<double>( maChartRect.GetHeight() - 2 * mnBorderGapY, mnBorderGapY ) / EXC_CHART_TOTALUNITS;
+
+ // create object tables
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxChartDoc, uno::UNO_QUERY);
+ mxLineDashTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_DASHTABLE, "Excel line dash ");
+ mxGradientTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_GRADIENTTABLE, "Excel gradient ");
+ mxHatchTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_HATCHTABLE, "Excel hatch ");
+ mxBitmapTable = std::make_shared<XclChObjectTable>(xFactory, SERVICE_DRAWING_BITMAPTABLE, "Excel bitmap ");
+}
+
+void XclChRootData::FinishConversion()
+{
+ // forget formatting object tables
+ mxBitmapTable.reset();
+ mxHatchTable.reset();
+ mxGradientTable.reset();
+ mxLineDashTable.reset();
+ // forget chart document reference
+ mxChartDoc.clear();
+}
+
+uno::Reference<drawing::XShape> XclChRootData::GetTitleShape(const XclChTextKey& rTitleKey) const
+{
+ XclChGetShapeFuncMap::const_iterator aIt = maGetShapeFuncs.find( rTitleKey );
+ OSL_ENSURE( aIt != maGetShapeFuncs.end(), "XclChRootData::GetTitleShape - invalid title key" );
+ uno::Reference<chart::XChartDocument> xChart1Doc( mxChartDoc, uno::UNO_QUERY );
+ uno::Reference<drawing::XShape> xTitleShape;
+ if (xChart1Doc.is() && (aIt != maGetShapeFuncs.end()))
+ xTitleShape = (aIt->second)(xChart1Doc);
+ return xTitleShape;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlescher.cxx b/sc/source/filter/excel/xlescher.cxx
new file mode 100644
index 000000000..8ae663cdd
--- /dev/null
+++ b/sc/source/filter/excel/xlescher.cxx
@@ -0,0 +1,335 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <xlescher.hxx>
+
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <tools/UnitConversion.hxx>
+#include <document.hxx>
+#include <xistream.hxx>
+#include <xlroot.hxx>
+#include <xltools.hxx>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::drawing::XControlShape;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::script::ScriptEventDescriptor;
+
+namespace {
+
+/** Returns the scaling factor to calculate coordinates from twips. */
+double lclGetTwipsScale( MapUnit eMapUnit )
+{
+ double fScale = 1.0;
+ if (const auto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid)
+ fScale = o3tl::convert(1.0, o3tl::Length::twip, eTo);
+ else
+ OSL_FAIL("lclGetTwipsScale - map unit not implemented");
+ return fScale;
+}
+
+/** Calculates a drawing layer X position (in twips) from an object column position. */
+tools::Long lclGetXFromCol( const ScDocument& rDoc, SCTAB nScTab, sal_uInt16 nXclCol, sal_uInt16 nOffset, double fScale )
+{
+ SCCOL nScCol = static_cast< SCCOL >( nXclCol );
+ return static_cast< tools::Long >( fScale * (rDoc.GetColOffset( nScCol, nScTab ) +
+ ::std::min( nOffset / 1024.0, 1.0 ) * rDoc.GetColWidth( nScCol, nScTab )) + 0.5 );
+}
+
+/** Calculates a drawing layer Y position (in twips) from an object row position. */
+tools::Long lclGetYFromRow( const ScDocument& rDoc, SCTAB nScTab, sal_uInt16 nXclRow, sal_uInt16 nOffset, double fScale )
+{
+ SCROW nScRow = static_cast< SCROW >( nXclRow );
+ return static_cast< tools::Long >( fScale * (rDoc.GetRowOffset( nScRow, nScTab ) +
+ ::std::min( nOffset / 256.0, 1.0 ) * rDoc.GetRowHeight( nScRow, nScTab )) + 0.5 );
+}
+
+/** Calculates an object column position from a drawing layer X position (in twips). */
+void lclGetColFromX(
+ const ScDocument& rDoc, SCTAB nScTab, sal_uInt16& rnXclCol,
+ sal_uInt16& rnOffset, sal_uInt16 nXclStartCol, sal_uInt16 nXclMaxCol,
+ tools::Long& rnStartW, tools::Long nX, double fScale )
+{
+ // rnStartW in conjunction with nXclStartCol is used as buffer for previously calculated width
+ tools::Long nTwipsX = static_cast< tools::Long >( nX / fScale + 0.5 );
+ tools::Long nColW = 0;
+ for( rnXclCol = nXclStartCol; rnXclCol <= nXclMaxCol; ++rnXclCol )
+ {
+ nColW = rDoc.GetColWidth( static_cast< SCCOL >( rnXclCol ), nScTab );
+ if( rnStartW + nColW > nTwipsX )
+ break;
+ rnStartW += nColW;
+ }
+ rnOffset = nColW ? static_cast< sal_uInt16 >( (nTwipsX - rnStartW) * 1024.0 / nColW + 0.5 ) : 0;
+}
+
+/** Calculates an object row position from a drawing layer Y position (in twips). */
+void lclGetRowFromY(
+ const ScDocument& rDoc, SCTAB nScTab, sal_uInt32& rnXclRow,
+ sal_uInt32& rnOffset, sal_uInt32 nXclStartRow, sal_uInt32 nXclMaxRow,
+ tools::Long& rnStartH, tools::Long nY, double fScale )
+{
+ // rnStartH in conjunction with nXclStartRow is used as buffer for previously calculated height
+ tools::Long nTwipsY = static_cast< tools::Long >( nY / fScale + 0.5 );
+ tools::Long nRowH = 0;
+ bool bFound = false;
+ for( sal_uInt32 nRow = nXclStartRow; nRow <= nXclMaxRow; ++nRow )
+ {
+ nRowH = rDoc.GetRowHeight( nRow, nScTab );
+ if( rnStartH + nRowH > nTwipsY )
+ {
+ rnXclRow = nRow;
+ bFound = true;
+ break;
+ }
+ rnStartH += nRowH;
+ }
+ if( !bFound )
+ rnXclRow = nXclMaxRow;
+ rnOffset = static_cast< sal_uInt32 >( nRowH ? std::max((nTwipsY - rnStartH) * 256.0 / nRowH + 0.5, 0.0) : 0 );
+}
+
+/** Mirrors a rectangle (from LTR to RTL layout or vice versa). */
+void lclMirrorRectangle( tools::Rectangle& rRect )
+{
+ tools::Long nLeft = rRect.Left();
+ rRect.SetLeft( -rRect.Right() );
+ rRect.SetRight( -nLeft );
+}
+
+sal_uInt16 lclGetEmbeddedScale( tools::Long nPageSize, sal_Int32 nPageScale, tools::Long nPos, double fPosScale )
+{
+ return static_cast< sal_uInt16 >( nPos * fPosScale / nPageSize * nPageScale + 0.5 );
+}
+
+} // namespace
+
+XclObjAnchor::XclObjAnchor() :
+ mnLX( 0 ),
+ mnTY( 0 ),
+ mnRX( 0 ),
+ mnBY( 0 )
+{
+}
+
+tools::Rectangle XclObjAnchor::GetRect( const XclRoot& rRoot, SCTAB nScTab, MapUnit eMapUnit ) const
+{
+ ScDocument& rDoc = rRoot.GetDoc();
+ double fScale = lclGetTwipsScale( eMapUnit );
+ tools::Rectangle aRect(
+ lclGetXFromCol(rDoc, nScTab, std::min<SCCOL>(maFirst.mnCol, rDoc.MaxCol()), mnLX, fScale),
+ lclGetYFromRow(rDoc, nScTab, std::min<SCROW>(maFirst.mnRow, rDoc.MaxRow()), mnTY, fScale),
+ lclGetXFromCol(rDoc, nScTab, std::min<SCCOL>(maLast.mnCol, rDoc.MaxCol()), mnRX + 1, fScale),
+ lclGetYFromRow(rDoc, nScTab, std::min<SCROW>(maLast.mnRow, rDoc.MaxRow()), mnBY, fScale));
+
+ // adjust coordinates in mirrored sheets
+ if( rDoc.IsLayoutRTL( nScTab ) )
+ lclMirrorRectangle( aRect );
+ return aRect;
+}
+
+void XclObjAnchor::SetRect( const XclRoot& rRoot, SCTAB nScTab, const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ ScDocument& rDoc = rRoot.GetDoc();
+ sal_uInt16 nXclMaxCol = rRoot.GetXclMaxPos().Col();
+ sal_uInt16 nXclMaxRow = static_cast<sal_uInt16>( rRoot.GetXclMaxPos().Row());
+
+ // adjust coordinates in mirrored sheets
+ tools::Rectangle aRect( rRect );
+ if( rDoc.IsLayoutRTL( nScTab ) )
+ lclMirrorRectangle( aRect );
+
+ double fScale = lclGetTwipsScale( eMapUnit );
+ tools::Long nDummy = 0;
+ lclGetColFromX( rDoc, nScTab, maFirst.mnCol, mnLX, 0, nXclMaxCol, nDummy, aRect.Left(), fScale );
+ lclGetColFromX( rDoc, nScTab, maLast.mnCol, mnRX, maFirst.mnCol, nXclMaxCol, nDummy, aRect.Right(), fScale );
+ nDummy = 0;
+ lclGetRowFromY( rDoc, nScTab, maFirst.mnRow, mnTY, 0, nXclMaxRow, nDummy, aRect.Top(), fScale );
+ lclGetRowFromY( rDoc, nScTab, maLast.mnRow, mnBY, maFirst.mnRow, nXclMaxRow, nDummy, aRect.Bottom(), fScale );
+}
+
+void XclObjAnchor::SetRect( const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY,
+ const tools::Rectangle& rRect, MapUnit eMapUnit )
+{
+ double fScale = 1.0;
+ if (const auto eFrom = MapToO3tlLength(eMapUnit); eFrom != o3tl::Length::invalid)
+ fScale = o3tl::convert(1.0, eFrom, o3tl::Length::mm100);
+ else
+ OSL_FAIL("XclObjAnchor::SetRect - map unit not implemented");
+
+ /* In objects with DFF client anchor, the position of the shape is stored
+ in the cell address components of the client anchor. In old BIFF3-BIFF5
+ objects, the position is stored in the offset components of the anchor. */
+ maFirst.mnCol = lclGetEmbeddedScale( rPageSize.Width(), nScaleX, rRect.Left(), fScale );
+ maFirst.mnRow = lclGetEmbeddedScale( rPageSize.Height(), nScaleY, rRect.Top(), fScale );
+ maLast.mnCol = lclGetEmbeddedScale( rPageSize.Width(), nScaleX, rRect.Right(), fScale );
+ maLast.mnRow = lclGetEmbeddedScale( rPageSize.Height(), nScaleY, rRect.Bottom(), fScale );
+
+ // for safety, clear the other members
+ mnLX = mnTY = mnRX = mnBY = 0;
+}
+
+XclObjLineData::XclObjLineData() :
+ mnColorIdx( EXC_OBJ_LINE_AUTOCOLOR ),
+ mnStyle( EXC_OBJ_LINE_SOLID ),
+ mnWidth( EXC_OBJ_LINE_HAIR ),
+ mnAuto( EXC_OBJ_LINE_AUTO )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjLineData& rLineData )
+{
+ rLineData.mnColorIdx = rStrm.ReaduInt8();
+ rLineData.mnStyle = rStrm.ReaduInt8();
+ rLineData.mnWidth = rStrm.ReaduInt8();
+ rLineData.mnAuto = rStrm.ReaduInt8();
+ return rStrm;
+}
+
+XclObjFillData::XclObjFillData() :
+ mnBackColorIdx( EXC_OBJ_LINE_AUTOCOLOR ),
+ mnPattColorIdx( EXC_OBJ_FILL_AUTOCOLOR ),
+ mnPattern( EXC_PATT_SOLID ),
+ mnAuto( EXC_OBJ_FILL_AUTO )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjFillData& rFillData )
+{
+ rFillData.mnBackColorIdx = rStrm.ReaduInt8();
+ rFillData.mnPattColorIdx = rStrm.ReaduInt8();
+ rFillData.mnPattern = rStrm.ReaduInt8();
+ rFillData.mnAuto = rStrm.ReaduInt8();
+ return rStrm;
+}
+
+XclObjTextData::XclObjTextData() :
+ mnTextLen( 0 ),
+ mnFormatSize( 0 ),
+ mnLinkSize( 0 ),
+ mnDefFontIdx( EXC_FONT_APP ),
+ mnFlags( 0 ),
+ mnOrient( EXC_OBJ_ORIENT_NONE ),
+ mnButtonFlags( 0 ),
+ mnShortcut( 0 ),
+ mnShortcutEA( 0 )
+{
+}
+
+void XclObjTextData::ReadObj3( XclImpStream& rStrm )
+{
+ mnTextLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFormatSize = rStrm.ReaduInt16();
+ mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ rStrm.Ignore( 8 );
+}
+
+void XclObjTextData::ReadObj5( XclImpStream& rStrm )
+{
+ mnTextLen = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFormatSize = rStrm.ReaduInt16();
+ mnDefFontIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnLinkSize = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ mnButtonFlags = rStrm.ReaduInt16();
+ mnShortcut = rStrm.ReaduInt16();
+ mnShortcutEA = rStrm.ReaduInt16();
+}
+
+void XclObjTextData::ReadTxo8( XclImpStream& rStrm )
+{
+ mnFlags = rStrm.ReaduInt16();
+ mnOrient = rStrm.ReaduInt16();
+ mnButtonFlags = rStrm.ReaduInt16();
+ mnShortcut = rStrm.ReaduInt16();
+ mnShortcutEA = rStrm.ReaduInt16();
+ mnTextLen = rStrm.ReaduInt16();
+ mnFormatSize = rStrm.ReaduInt16();
+}
+
+Reference< XControlModel > XclControlHelper::GetControlModel( Reference< XShape > const & xShape )
+{
+ Reference< XControlModel > xCtrlModel;
+ Reference< XControlShape > xCtrlShape( xShape, UNO_QUERY );
+ if( xCtrlShape.is() )
+ xCtrlModel = xCtrlShape->getControl();
+ return xCtrlModel;
+}
+
+namespace {
+
+const struct
+{
+ const char* mpcListenerType;
+ const char* mpcEventMethod;
+}
+spTbxListenerData[] =
+{
+ // Attention: MUST be in order of the XclTbxEventType enum!
+ /*EXC_TBX_EVENT_ACTION*/ { "XActionListener", "actionPerformed" },
+ /*EXC_TBX_EVENT_MOUSE*/ { "XMouseListener", "mouseReleased" },
+ /*EXC_TBX_EVENT_TEXT*/ { "XTextListener", "textChanged" },
+ /*EXC_TBX_EVENT_VALUE*/ { "XAdjustmentListener", "adjustmentValueChanged" },
+ /*EXC_TBX_EVENT_CHANGE*/ { "XChangeListener", "changed" }
+};
+
+} // namespace
+
+bool XclControlHelper::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor,
+ XclTbxEventType eEventType, const OUString& rXclMacroName, SfxObjectShell* pDocShell )
+{
+ if( !rXclMacroName.isEmpty() )
+ {
+ rDescriptor.ListenerType = OUString::createFromAscii( spTbxListenerData[ eEventType ].mpcListenerType );
+ rDescriptor.EventMethod = OUString::createFromAscii( spTbxListenerData[ eEventType ].mpcEventMethod );
+ rDescriptor.ScriptType = "Script";
+ rDescriptor.ScriptCode = XclTools::GetSbMacroUrl( rXclMacroName, pDocShell );
+ return true;
+ }
+ return false;
+}
+
+OUString XclControlHelper::ExtractFromMacroDescriptor(
+ const ScriptEventDescriptor& rDescriptor, XclTbxEventType eEventType )
+{
+ if( (!rDescriptor.ScriptCode.isEmpty()) &&
+ rDescriptor.ScriptType.equalsIgnoreAsciiCase("Script") &&
+ rDescriptor.ListenerType.equalsAscii( spTbxListenerData[ eEventType ].mpcListenerType ) &&
+ rDescriptor.EventMethod.equalsAscii( spTbxListenerData[ eEventType ].mpcEventMethod ) )
+ return XclTools::GetXclMacroName( rDescriptor.ScriptCode );
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlformula.cxx b/sc/source/filter/excel/xlformula.cxx
new file mode 100644
index 000000000..e2e082ac2
--- /dev/null
+++ b/sc/source/filter/excel/xlformula.cxx
@@ -0,0 +1,1022 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlformula.hxx>
+
+#include <refdata.hxx>
+#include <tokenarray.hxx>
+#include <xestream.hxx>
+#include <xistream.hxx>
+#include <xlroot.hxx>
+
+#include <comphelper/string.hxx>
+#include <svl/sharedstringpool.hxx>
+
+using namespace ::formula;
+
+// Function data ==============================================================
+
+OUString XclFunctionInfo::GetMacroFuncName() const
+{
+ if( IsMacroFunc() )
+ return OUString( mpcMacroName, strlen(mpcMacroName), RTL_TEXTENCODING_UTF8 );
+ return OUString();
+}
+
+OUString XclFunctionInfo::GetAddInEquivalentFuncName() const
+{
+ if( IsAddInEquivalent() )
+ return OUString( mpcMacroName, strlen(mpcMacroName), RTL_TEXTENCODING_UTF8 );
+ return OUString();
+}
+
+// abbreviations for function return token class
+const sal_uInt8 R = EXC_TOKCLASS_REF;
+const sal_uInt8 V = EXC_TOKCLASS_VAL;
+const sal_uInt8 A = EXC_TOKCLASS_ARR;
+
+// abbreviations for parameter infos
+#define RO { EXC_PARAM_REGULAR, EXC_PARAMCONV_ORG, false }
+#define RA { EXC_PARAM_REGULAR, EXC_PARAMCONV_ARR, false }
+#define RR { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPT, false }
+#define RX { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPX, false }
+#define VO { EXC_PARAM_REGULAR, EXC_PARAMCONV_ORG, true }
+#define VV { EXC_PARAM_REGULAR, EXC_PARAMCONV_VAL, true }
+#define VA { EXC_PARAM_REGULAR, EXC_PARAMCONV_ARR, true }
+#define VR { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPT, true }
+#define VX { EXC_PARAM_REGULAR, EXC_PARAMCONV_RPX, true }
+#define RO_E { EXC_PARAM_EXCELONLY, EXC_PARAMCONV_ORG, false }
+#define VR_E { EXC_PARAM_EXCELONLY, EXC_PARAMCONV_RPT, true }
+#define C { EXC_PARAM_CALCONLY, EXC_PARAMCONV_ORG, false }
+
+const sal_uInt16 NOID = SAL_MAX_UINT16; /// No BIFF/OOBIN function identifier available.
+const sal_uInt8 MX = 30; /// Maximum parameter count.
+
+#define EXC_FUNCNAME( ascii ) "_xlfn." ascii
+#define EXC_FUNCNAME_ODF( ascii ) "_xlfnodf." ascii
+#define EXC_FUNCNAME_ADDIN( ascii ) "com.sun.star.sheet.addin." ascii
+
+/** Functions new in BIFF2. */
+const XclFunctionInfo saFuncTable_2[] =
+{
+ { ocCount, 0, 0, MX, V, { RX }, 0, nullptr },
+ { ocIf, 1, 2, 3, R, { VO, RO }, 0, nullptr },
+ { ocIsNA, 2, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsError, 3, 1, 1, V, { VR }, 0, nullptr },
+ { ocSum, 4, 0, MX, V, { RX }, 0, nullptr },
+ { ocAverage, 5, 1, MX, V, { RX }, 0, nullptr },
+ { ocMin, 6, 1, MX, V, { RX }, 0, nullptr },
+ { ocMax, 7, 1, MX, V, { RX }, 0, nullptr },
+ { ocRow, 8, 0, 1, V, { RO }, 0, nullptr },
+ { ocColumn, 9, 0, 1, V, { RO }, 0, nullptr },
+ { ocNotAvail, 10, 0, 0, V, {}, 0, nullptr },
+ { ocNPV, 11, 2, MX, V, { VR, RX }, 0, nullptr },
+ { ocStDev, 12, 1, MX, V, { RX }, 0, nullptr },
+ { ocCurrency, 13, 1, 2, V, { VR }, 0, nullptr },
+ { ocFixed, 14, 1, 2, V, { VR, VR, C }, 0, nullptr },
+ { ocSin, 15, 1, 1, V, { VR }, 0, nullptr },
+ { ocCosecant, 15, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocCos, 16, 1, 1, V, { VR }, 0, nullptr },
+ { ocSecant, 16, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocTan, 17, 1, 1, V, { VR }, 0, nullptr },
+ { ocCot, 17, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocArcTan, 18, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCot, 18, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocPi, 19, 0, 0, V, {}, 0, nullptr },
+ { ocSqrt, 20, 1, 1, V, { VR }, 0, nullptr },
+ { ocExp, 21, 1, 1, V, { VR }, 0, nullptr },
+ { ocLn, 22, 1, 1, V, { VR }, 0, nullptr },
+ { ocLog10, 23, 1, 1, V, { VR }, 0, nullptr },
+ { ocAbs, 24, 1, 1, V, { VR }, 0, nullptr },
+ { ocInt, 25, 1, 1, V, { VR }, 0, nullptr },
+ { ocPlusMinus, 26, 1, 1, V, { VR }, 0, nullptr },
+ { ocRound, 27, 2, 2, V, { VR }, 0, nullptr },
+ { ocLookup, 28, 2, 3, V, { VR, RA }, 0, nullptr },
+ { ocIndex, 29, 2, 4, R, { RA, VV }, 0, nullptr },
+ { ocRept, 30, 2, 2, V, { VR }, 0, nullptr },
+ { ocMid, 31, 3, 3, V, { VR }, 0, nullptr },
+ { ocLen, 32, 1, 1, V, { VR }, 0, nullptr },
+ { ocValue, 33, 1, 1, V, { VR }, 0, nullptr },
+ { ocTrue, 34, 0, 0, V, {}, 0, nullptr },
+ { ocFalse, 35, 0, 0, V, {}, 0, nullptr },
+ { ocAnd, 36, 1, MX, V, { RX }, 0, nullptr },
+ { ocOr, 37, 1, MX, V, { RX }, 0, nullptr },
+ { ocNot, 38, 1, 1, V, { VR }, 0, nullptr },
+ { ocMod, 39, 2, 2, V, { VR }, 0, nullptr },
+ { ocDBCount, 40, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBSum, 41, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBAverage, 42, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBMin, 43, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBMax, 44, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBStdDev, 45, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocVar, 46, 1, MX, V, { RX }, 0, nullptr },
+ { ocDBVar, 47, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocText, 48, 2, 2, V, { VR }, 0, nullptr },
+ { ocLinest, 49, 1, 2, A, { RA, RA, C, C }, 0, nullptr },
+ { ocTrend, 50, 1, 3, A, { RA, RA, RA, C }, 0, nullptr },
+ { ocLogest, 51, 1, 2, A, { RA, RA, C, C }, 0, nullptr },
+ { ocGrowth, 52, 1, 3, A, { RA, RA, RA, C }, 0, nullptr },
+ { ocPV, 56, 3, 5, V, { VR }, 0, nullptr },
+ { ocFV, 57, 3, 5, V, { VR }, 0, nullptr },
+ { ocNper, 58, 3, 5, V, { VR }, 0, nullptr },
+ { ocPMT, 59, 3, 5, V, { VR }, 0, nullptr },
+ { ocRate, 60, 3, 6, V, { VR }, 0, nullptr },
+ { ocMIRR, 61, 3, 3, V, { RA, VR }, 0, nullptr },
+ { ocIRR, 62, 1, 2, V, { RA, VR }, 0, nullptr },
+ { ocRandom, 63, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocMatch, 64, 2, 3, V, { VR, RX, RR }, 0, nullptr },
+ { ocGetDate, 65, 3, 3, V, { VR }, 0, nullptr },
+ { ocGetTime, 66, 3, 3, V, { VR }, 0, nullptr },
+ { ocGetDay, 67, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetMonth, 68, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetYear, 69, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetDayOfWeek, 70, 1, 1, V, { VR, C }, 0, nullptr },
+ { ocGetHour, 71, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetMin, 72, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetSec, 73, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetActTime, 74, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocAreas, 75, 1, 1, V, { RO }, 0, nullptr },
+ { ocRows, 76, 1, 1, V, { RO }, 0, nullptr },
+ { ocColumns, 77, 1, 1, V, { RO }, 0, nullptr },
+ { ocOffset, 78, 3, 5, R, { RO, VR }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocSearch, 82, 2, 3, V, { VR }, 0, nullptr },
+ { ocMatTrans, 83, 1, 1, A, { VO }, 0, nullptr },
+ { ocType, 86, 1, 1, V, { VX }, 0, nullptr },
+ { ocArcTan2, 97, 2, 2, V, { VR }, 0, nullptr },
+ { ocArcSin, 98, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCos, 99, 1, 1, V, { VR }, 0, nullptr },
+ { ocChoose, 100, 2, MX, R, { VO, RO }, 0, nullptr },
+ { ocHLookup, 101, 3, 3, V, { VV, RO, RO, C }, 0, nullptr },
+ { ocVLookup, 102, 3, 3, V, { VV, RO, RO, C }, 0, nullptr },
+ { ocIsRef, 105, 1, 1, V, { RX }, 0, nullptr },
+ { ocLog, 109, 1, 2, V, { VR }, 0, nullptr },
+ { ocChar, 111, 1, 1, V, { VR }, 0, nullptr },
+ { ocLower, 112, 1, 1, V, { VR }, 0, nullptr },
+ { ocUpper, 113, 1, 1, V, { VR }, 0, nullptr },
+ { ocProper, 114, 1, 1, V, { VR }, 0, nullptr },
+ { ocLeft, 115, 1, 2, V, { VR }, 0, nullptr },
+ { ocRight, 116, 1, 2, V, { VR }, 0, nullptr },
+ { ocExact, 117, 2, 2, V, { VR }, 0, nullptr },
+ { ocTrim, 118, 1, 1, V, { VR }, 0, nullptr },
+ { ocReplace, 119, 4, 4, V, { VR }, 0, nullptr },
+ { ocSubstitute, 120, 3, 4, V, { VR }, 0, nullptr },
+ { ocCode, 121, 1, 1, V, { VR }, 0, nullptr },
+ { ocFind, 124, 2, 3, V, { VR }, 0, nullptr },
+ { ocCell, 125, 1, 2, V, { VV, RO }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocIsErr, 126, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsString, 127, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsValue, 128, 1, 1, V, { VR }, 0, nullptr },
+ { ocIsEmpty, 129, 1, 1, V, { VR }, 0, nullptr },
+ { ocT, 130, 1, 1, V, { RO }, 0, nullptr },
+ { ocN, 131, 1, 1, V, { RO }, 0, nullptr },
+ { ocGetDateValue, 140, 1, 1, V, { VR }, 0, nullptr },
+ { ocGetTimeValue, 141, 1, 1, V, { VR }, 0, nullptr },
+ { ocSLN, 142, 3, 3, V, { VR }, 0, nullptr },
+ { ocSYD, 143, 4, 4, V, { VR }, 0, nullptr },
+ { ocDDB, 144, 4, 5, V, { VR }, 0, nullptr },
+ { ocIndirect, 148, 1, 2, R, { VR }, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocClean, 162, 1, 1, V, { VR }, 0, nullptr },
+ { ocMatDet, 163, 1, 1, V, { VA }, 0, nullptr },
+ { ocMatInv, 164, 1, 1, A, { VA }, 0, nullptr },
+ { ocMatMult, 165, 2, 2, A, { VA }, 0, nullptr },
+ { ocIpmt, 167, 4, 6, V, { VR }, 0, nullptr },
+ { ocPpmt, 168, 4, 6, V, { VR }, 0, nullptr },
+ { ocCount2, 169, 0, MX, V, { RX }, 0, nullptr },
+ { ocProduct, 183, 0, MX, V, { RX }, 0, nullptr },
+ { ocFact, 184, 1, 1, V, { VR }, 0, nullptr },
+ { ocDBProduct, 189, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocIsNonString, 190, 1, 1, V, { VR }, 0, nullptr },
+ { ocStDevP, 193, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarP, 194, 1, MX, V, { RX }, 0, nullptr },
+ { ocDBStdDevP, 195, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocDBVarP, 196, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocTrunc, 197, 1, 1, V, { VR, C }, 0, nullptr },
+ { ocIsLogical, 198, 1, 1, V, { VR }, 0, nullptr },
+ { ocDBCount2, 199, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocCurrency, 204, 1, 2, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr },
+ { ocFindB, 205, 2, 3, V, { VR }, 0, nullptr },
+ { ocSearchB, 206, 2, 3, V, { VR }, 0, nullptr },
+ { ocReplaceB, 207, 4, 4, V, { VR }, 0, nullptr },
+ { ocLeftB, 208, 1, 2, V, { VR }, 0, nullptr },
+ { ocRightB, 209, 1, 2, V, { VR }, 0, nullptr },
+ { ocMidB, 210, 3, 3, V, { VR }, 0, nullptr },
+ { ocLenB, 211, 1, 1, V, { VR }, 0, nullptr },
+ { ocRoundUp, 212, 2, 2, V, { VR }, 0, nullptr },
+ { ocRoundDown, 213, 2, 2, V, { VR }, 0, nullptr },
+ { ocExternal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_IMPORTONLY, nullptr }
+};
+
+/** Functions new in BIFF3. */
+const XclFunctionInfo saFuncTable_3[] =
+{
+ { ocLinest, 49, 1, 4, A, { RA, RA, VV }, 0, nullptr }, // BIFF2: 1-2, BIFF3: 1-4
+ { ocTrend, 50, 1, 4, A, { RA, RA, RA, VV }, 0, nullptr }, // BIFF2: 1-3, BIFF3: 1-4
+ { ocLogest, 51, 1, 4, A, { RA, RA, VV }, 0, nullptr }, // BIFF2: 1-2, BIFF3: 1-4
+ { ocGrowth, 52, 1, 4, A, { RA, RA, RA, VV }, 0, nullptr }, // BIFF2: 1-3, BIFF3: 1-4
+ { ocTrunc, 197, 1, 2, V, { VR }, 0, nullptr }, // BIFF2: 1, BIFF3: 1-2
+ { ocAddress, 219, 2, 5, V, { VR }, 0, nullptr },
+ { ocGetDiffDate360, 220, 2, 2, V, { VR, VR, C }, 0, nullptr },
+ { ocGetActDate, 221, 0, 0, V, {}, EXC_FUNCFLAG_VOLATILE, nullptr },
+ { ocVBD, 222, 5, 7, V, { VR }, 0, nullptr },
+ { ocMedian, 227, 1, MX, V, { RX }, 0, nullptr },
+ { ocSumProduct, 228, 1, MX, V, { VA }, 0, nullptr },
+ { ocSinHyp, 229, 1, 1, V, { VR }, 0, nullptr },
+ { ocCosecantHyp, 229, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocCosHyp, 230, 1, 1, V, { VR }, 0, nullptr },
+ { ocSecantHyp, 230, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocTanHyp, 231, 1, 1, V, { VR }, 0, nullptr },
+ { ocCotHyp, 231, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocArcSinHyp, 232, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCosHyp, 233, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcTanHyp, 234, 1, 1, V, { VR }, 0, nullptr },
+ { ocArcCotHyp, 234, 1, 1, V, { VR }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocDBGet, 235, 3, 3, V, { RO, RR }, 0, nullptr },
+ { ocInfo, 244, 1, 1, V, { VR }, EXC_FUNCFLAG_VOLATILE, nullptr }
+};
+
+/** Functions new in BIFF4. */
+const XclFunctionInfo saFuncTable_4[] =
+{
+ { ocFixed, 14, 1, 3, V, { VR }, 0, nullptr }, // BIFF2-3: 1-2, BIFF4: 1-3
+ { ocAsc, 214, 1, 1, V, { VR }, 0, nullptr },
+ { ocJis, 215, 1, 1, V, { VR }, 0, nullptr },
+ { ocRank, 216, 2, 3, V, { VR, RO, VR }, 0, nullptr },
+ { ocDB, 247, 4, 5, V, { VR }, 0, nullptr },
+ { ocFrequency, 252, 2, 2, A, { RA }, 0, nullptr },
+ { ocErrorType_ODF, 261, 1, 1, V, { VR }, 0, nullptr },
+ { ocAveDev, 269, 1, MX, V, { RX }, 0, nullptr },
+ { ocBetaDist, 270, 3, 5, V, { VR }, 0, nullptr },
+ { ocGammaLn, 271, 1, 1, V, { VR }, 0, nullptr },
+ { ocBetaInv, 272, 3, 5, V, { VR }, 0, nullptr },
+ { ocBinomDist, 273, 4, 4, V, { VR }, 0, nullptr },
+ { ocChiDist, 274, 2, 2, V, { VR }, 0, nullptr },
+ { ocChiInv, 275, 2, 2, V, { VR }, 0, nullptr },
+ { ocCombin, 276, 2, 2, V, { VR }, 0, nullptr },
+ { ocConfidence, 277, 3, 3, V, { VR }, 0, nullptr },
+ { ocCritBinom, 278, 3, 3, V, { VR }, 0, nullptr },
+ { ocEven, 279, 1, 1, V, { VR }, 0, nullptr },
+ { ocExpDist, 280, 3, 3, V, { VR }, 0, nullptr },
+ { ocFDist, 281, 3, 3, V, { VR }, 0, nullptr },
+ { ocFInv, 282, 3, 3, V, { VR }, 0, nullptr },
+ { ocFisher, 283, 1, 1, V, { VR }, 0, nullptr },
+ { ocFisherInv, 284, 1, 1, V, { VR }, 0, nullptr },
+ { ocFloor_MS, 285, 2, 2, V, { VR }, 0, nullptr },
+ { ocGammaDist, 286, 4, 4, V, { VR }, 0, nullptr },
+ { ocGammaInv, 287, 3, 3, V, { VR }, 0, nullptr },
+ { ocCeil_MS, 288, 2, 2, V, { VR }, 0, nullptr },
+ { ocHypGeomDist, 289, 4, 4, V, { VR }, 0, nullptr },
+ { ocLogNormDist, 290, 3, 3, V, { VR }, 0, nullptr },
+ { ocLogInv, 291, 3, 3, V, { VR }, 0, nullptr },
+ { ocNegBinomVert, 292, 3, 3, V, { VR }, 0, nullptr },
+ { ocNormDist, 293, 4, 4, V, { VR }, 0, nullptr },
+ { ocStdNormDist, 294, 1, 1, V, { VR }, 0, nullptr },
+ { ocNormInv, 295, 3, 3, V, { VR }, 0, nullptr },
+ { ocSNormInv, 296, 1, 1, V, { VR }, 0, nullptr },
+ { ocStandard, 297, 3, 3, V, { VR }, 0, nullptr },
+ { ocOdd, 298, 1, 1, V, { VR }, 0, nullptr },
+ { ocPermut, 299, 2, 2, V, { VR }, 0, nullptr },
+ { ocPoissonDist, 300, 3, 3, V, { VR }, 0, nullptr },
+ { ocTDist, 301, 3, 3, V, { VR }, 0, nullptr },
+ { ocWeibull, 302, 4, 4, V, { VR }, 0, nullptr },
+ { ocSumXMY2, 303, 2, 2, V, { VA }, 0, nullptr },
+ { ocSumX2MY2, 304, 2, 2, V, { VA }, 0, nullptr },
+ { ocSumX2DY2, 305, 2, 2, V, { VA }, 0, nullptr },
+ { ocChiTest, 306, 2, 2, V, { VA }, 0, nullptr },
+ { ocCorrel, 307, 2, 2, V, { VA }, 0, nullptr },
+ { ocCovar, 308, 2, 2, V, { VA }, 0, nullptr },
+ { ocForecast, 309, 3, 3, V, { VR, VA }, 0, nullptr },
+ { ocFTest, 310, 2, 2, V, { VA }, 0, nullptr },
+ { ocIntercept, 311, 2, 2, V, { VA }, 0, nullptr },
+ { ocPearson, 312, 2, 2, V, { VA }, 0, nullptr },
+ { ocRSQ, 313, 2, 2, V, { VA }, 0, nullptr },
+ { ocSTEYX, 314, 2, 2, V, { VA }, 0, nullptr },
+ { ocSlope, 315, 2, 2, V, { VA }, 0, nullptr },
+ { ocTTest, 316, 4, 4, V, { VA, VA, VR }, 0, nullptr },
+ { ocProb, 317, 3, 4, V, { VA, VA, VR }, 0, nullptr },
+ { ocDevSq, 318, 1, MX, V, { RX }, 0, nullptr },
+ { ocGeoMean, 319, 1, MX, V, { RX }, 0, nullptr },
+ { ocHarMean, 320, 1, MX, V, { RX }, 0, nullptr },
+ { ocSumSQ, 321, 0, MX, V, { RX }, 0, nullptr },
+ { ocKurt, 322, 1, MX, V, { RX }, 0, nullptr },
+ { ocSkew, 323, 1, MX, V, { RX }, 0, nullptr },
+ { ocZTest, 324, 2, 3, V, { RX, VR }, 0, nullptr },
+ { ocLarge, 325, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocSmall, 326, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocQuartile, 327, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocPercentile, 328, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocPercentrank, 329, 2, 3, V, { RX, VR, VR_E }, 0, nullptr },
+ { ocModalValue, 330, 1, MX, V, { VA }, 0, nullptr },
+ { ocTrimMean, 331, 2, 2, V, { RX, VR }, 0, nullptr },
+ { ocTInv, 332, 2, 2, V, { VR }, 0, nullptr },
+ // Functions equivalent to add-in functions, use same parameters as
+ // ocExternal but add programmatical function name (here without
+ // "com.sun.star.sheet.addin.") so it can be looked up and stored as
+ // add-in, as older Excel versions only know them as add-in.
+ { ocIsEven, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getIseven" ) },
+ { ocIsOdd, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getIsodd" ) },
+ { ocGCD, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getGcd" ) },
+ { ocLCM, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getLcm" ) },
+ { ocEffect, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getEffect" ) },
+ { ocCumPrinc, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getCumprinc" ) },
+ { ocCumIpmt, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getCumipmt" ) },
+ { ocNominal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getNominal" ) },
+ { ocNetWorkdays, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY | EXC_FUNCFLAG_ADDINEQUIV, EXC_FUNCNAME_ADDIN( "Analysis.getNetworkdays" ) }
+};
+
+/** Functions new in BIFF5/BIFF7. Unsupported functions: DATESTRING, NUMBERSTRING. */
+const XclFunctionInfo saFuncTable_5[] =
+{
+ { ocGetDayOfWeek, 70, 1, 2, V, { VR }, 0, nullptr }, // BIFF2-4: 1, BIFF5: 1-2
+ { ocHLookup, 101, 3, 4, V, { VV, RO, RO, VV }, 0, nullptr }, // BIFF2-4: 3, BIFF5: 3-4
+ { ocVLookup, 102, 3, 4, V, { VV, RO, RO, VV }, 0, nullptr }, // BIFF2-4: 3, BIFF5: 3-4
+ { ocGetDiffDate360, 220, 2, 3, V, { VR }, 0, nullptr }, // BIFF3-4: 2, BIFF5: 2-3
+ { ocMacro, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocExternal, 255, 1, MX, R, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, nullptr },
+ { ocConcat, 336, 0, MX, V, { VR }, 0, nullptr },
+ { ocPower, 337, 2, 2, V, { VR }, 0, nullptr },
+ { ocRad, 342, 1, 1, V, { VR }, 0, nullptr },
+ { ocDeg, 343, 1, 1, V, { VR }, 0, nullptr },
+ { ocSubTotal, 344, 2, MX, V, { VR, RO }, 0, nullptr },
+ { ocSumIf, 345, 2, 3, V, { RO, VR, RO }, 0, nullptr },
+ { ocCountIf, 346, 2, 2, V, { RO, VR }, 0, nullptr },
+ { ocCountEmptyCells, 347, 1, 1, V, { RO }, 0, nullptr },
+ { ocISPMT, 350, 4, 4, V, { VR }, 0, nullptr },
+ { ocGetDateDif, 351, 3, 3, V, { VR }, 0, nullptr },
+ { ocNoName, 352, 1, 1, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // DATESTRING
+ { ocNoName, 353, 2, 2, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // NUMBERSTRING
+ { ocRoman, 354, 1, 2, V, { VR }, 0, nullptr }
+};
+
+/** Functions new in BIFF8. Unsupported functions: PHONETIC. */
+const XclFunctionInfo saFuncTable_8[] =
+{
+ { ocGetPivotData, 358, 2, MX, V, { RR, RR, VR }, 0, nullptr },
+ { ocHyperLink, 359, 1, 2, V, { VV, VO }, 0, nullptr },
+ { ocNoName, 360, 1, 1, V, { RO }, EXC_FUNCFLAG_IMPORTONLY, nullptr }, // PHONETIC
+ { ocAverageA, 361, 1, MX, V, { RX }, 0, nullptr },
+ { ocMaxA, 362, 1, MX, V, { RX }, 0, nullptr },
+ { ocMinA, 363, 1, MX, V, { RX }, 0, nullptr },
+ { ocStDevPA, 364, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarPA, 365, 1, MX, V, { RX }, 0, nullptr },
+ { ocStDevA, 366, 1, MX, V, { RX }, 0, nullptr },
+ { ocVarA, 367, 1, MX, V, { RX }, 0, nullptr },
+ { ocBahtText, 368, 1, 1, V, { VR }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "BAHTTEXT" ) },
+ { ocBahtText, 255, 2, 2, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "BAHTTEXT" ) },
+ { ocEuroConvert, 255, 4, 6, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY, "EUROCONVERT" }
+};
+
+#define EXC_FUNCENTRY_V_VR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+/** Functions new in OOXML. */
+const XclFunctionInfo saFuncTable_Oox[] =
+{
+ { ocCountIfs, NOID, 2, MX, V, { RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "COUNTIFS" ) },
+ { ocCountIfs, 255, 3, MX, V, { RO_E, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "COUNTIFS" ) },
+ { ocSumIfs, NOID, 3, MX, V, { RO, RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "SUMIFS" ) },
+ { ocSumIfs, 255, 4, MX, V, { RO_E, RO, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "SUMIFS" ) },
+ { ocAverageIf, NOID, 2, 3, V, { RO, VR, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "AVERAGEIF" ) },
+ { ocAverageIf, 255, 3, 4, V, { RO_E, RO, VR, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "AVERAGEIF" ) },
+ { ocAverageIfs, NOID, 3, MX, V, { RO, RO, VR }, EXC_FUNCFLAG_IMPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) },
+ { ocAverageIfs, 255, 4, MX, V, { RO_E, RO, RO, VR }, EXC_FUNCFLAG_EXPORTONLY|EXC_FUNCFLAG_PARAMPAIRS, EXC_FUNCNAME( "AVERAGEIFS" ) },
+ { ocIfError, NOID, 2, 2, V, { VO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "IFERROR" ) },
+ { ocIfError, 255, 3, 3, V, { RO_E, VO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "IFERROR" ) },
+ { ocNetWorkdays_MS, NOID, 2, 4, V, { VR, VR, RO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "NETWORKDAYS.INTL" ) },
+ { ocNetWorkdays_MS, 255, 3, 5, V, { RO_E, VR, VR, RO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "NETWORKDAYS.INTL" ) },
+ { ocWorkday_MS, NOID, 2, 4, V, { VR, VR, VR, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "WORKDAY.INTL" ) },
+ { ocWorkday_MS, 255, 3, 5, V, { RO_E, VR, VR, VR, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "WORKDAY.INTL" ) },
+ EXC_FUNCENTRY_V_VR( ocCeil_ISO, 1, 2, 0, "ISO.CEILING" )
+};
+
+#define EXC_FUNCENTRY_V_VR_IMPORT( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_RO_EXPORT( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_A_VR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, A, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, A, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_RO( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { RO }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+// implicit maxparam=MX
+#define EXC_FUNCENTRY_V_RX( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, MX, V, { RX }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, MX, V, { RO_E, RX }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+#define EXC_FUNCENTRY_V_VA( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VA }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+/** Functions new in Excel 2010.
+
+ See http://office.microsoft.com/en-us/excel-help/what-s-new-changes-made-to-excel-functions-HA010355760.aspx
+ A lot of statistical functions have been renamed (the 'old' function names still exist).
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2010 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2010[] =
+{
+ EXC_FUNCENTRY_V_VA( ocCovarianceP, 2, 2, 0, "COVARIANCE.P" ),
+ EXC_FUNCENTRY_V_VA( ocCovarianceS, 2, 2, 0, "COVARIANCE.S" ),
+ EXC_FUNCENTRY_V_RX( ocStDevP_MS, 1, MX, 0, "STDEV.P" ),
+ EXC_FUNCENTRY_V_RX( ocStDevS, 1, MX, 0, "STDEV.S" ),
+ EXC_FUNCENTRY_V_RX( ocVarP_MS, 1, MX, 0, "VAR.P" ),
+ EXC_FUNCENTRY_V_RX( ocVarS, 1, MX, 0, "VAR.S" ),
+ EXC_FUNCENTRY_V_VR( ocBetaDist_MS, 4, 6, 0, "BETA.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocBetaInv_MS, 3, 5, 0, "BETA.INV" ),
+ EXC_FUNCENTRY_V_VR( ocBinomDist_MS, 4, 4, 0, "BINOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocBinomInv, 3, 3, 0, "BINOM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocChiSqDist_MS, 3, 3, 0, "CHISQ.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocChiSqInv_MS, 2, 2, 0, "CHISQ.INV" ),
+ EXC_FUNCENTRY_V_VR( ocChiDist_MS, 2, 2, 0, "CHISQ.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocChiInv_MS, 2, 2, 0, "CHISQ.INV.RT" ),
+ EXC_FUNCENTRY_V_VR( ocChiTest_MS, 2, 2, 0, "CHISQ.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocConfidence_N, 3, 3, 0, "CONFIDENCE.NORM" ),
+ EXC_FUNCENTRY_V_VR( ocConfidence_T, 3, 3, 0, "CONFIDENCE.T" ),
+ EXC_FUNCENTRY_V_VR( ocFDist_LT, 4, 4, 0, "F.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocFDist_RT, 3, 3, 0, "F.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocFInv_LT, 3, 3, 0, "F.INV" ),
+ EXC_FUNCENTRY_V_VR( ocFInv_RT, 3, 3, 0, "F.INV.RT" ),
+ EXC_FUNCENTRY_V_VR( ocFTest_MS, 2, 2, 0, "F.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocExpDist_MS, 3, 3, 0, "EXPON.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocHypGeomDist_MS, 5, 5, 0, "HYPGEOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocPoissonDist_MS, 3, 3, 0, "POISSON.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocWeibull_MS, 4, 4, 0, "WEIBULL.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocGammaDist_MS, 4, 4, 0, "GAMMA.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocGammaInv_MS, 3, 3, 0, "GAMMA.INV" ),
+ EXC_FUNCENTRY_V_VR( ocGammaLn_MS, 1, 1, 0, "GAMMALN.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocLogNormDist_MS, 4, 4, 0, "LOGNORM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocLogInv_MS, 3, 3, 0, "LOGNORM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocNormDist_MS, 4, 4, 0, "NORM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocNormInv_MS, 3, 3, 0, "NORM.INV" ),
+ EXC_FUNCENTRY_V_VR( ocStdNormDist_MS, 2, 2, 0, "NORM.S.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocSNormInv_MS, 1, 1, 0, "NORM.S.INV" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_2T, 2, 2, 0, "T.DIST.2T" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_MS, 3, 3, 0, "T.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocTDist_RT, 2, 2, 0, "T.DIST.RT" ),
+ EXC_FUNCENTRY_V_VR( ocTInv_2T, 2, 2, 0, "T.INV.2T" ),
+ EXC_FUNCENTRY_V_VR( ocTInv_MS, 2, 2, 0, "T.INV" ),
+ EXC_FUNCENTRY_V_VR( ocTTest_MS, 4, 4, 0, "T.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocPercentile_Inc, 2, 2, 0, "PERCENTILE.INC" ),
+ EXC_FUNCENTRY_V_VR( ocPercentrank_Inc, 2, 3, 0, "PERCENTRANK.INC" ),
+ EXC_FUNCENTRY_V_VR( ocQuartile_Inc, 2, 2, 0, "QUARTILE.INC" ),
+ EXC_FUNCENTRY_V_VR( ocRank_Eq, 2, 3, 0, "RANK.EQ" ),
+ EXC_FUNCENTRY_V_VR( ocPercentile_Exc, 2, 2, 0, "PERCENTILE.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocPercentrank_Exc, 2, 3, 0, "PERCENTRANK.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocQuartile_Exc, 2, 2, 0, "QUARTILE.EXC" ),
+ EXC_FUNCENTRY_V_VR( ocRank_Avg, 2, 3, 0, "RANK.AVG" ),
+ EXC_FUNCENTRY_V_RX( ocModalValue_MS, 1, MX, 0, "MODE.SNGL" ),
+ EXC_FUNCENTRY_V_RX( ocModalValue_Multi, 1, MX, 0, "MODE.MULT" ),
+ EXC_FUNCENTRY_V_VR( ocNegBinomDist_MS, 4, 4, 0, "NEGBINOM.DIST" ),
+ EXC_FUNCENTRY_V_VR( ocZTest_MS, 2, 3, 0, "Z.TEST" ),
+ EXC_FUNCENTRY_V_VR( ocCeil_Precise, 1, 2, 0, "CEILING.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocFloor_Precise, 1, 2, 0, "FLOOR.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocErf_MS, 1, 1, 0, "ERF.PRECISE" ),
+ EXC_FUNCENTRY_V_VR( ocErfc_MS, 1, 1, 0, "ERFC.PRECISE" ),
+ EXC_FUNCENTRY_V_RX( ocAggregate, 3, MX, 0, "AGGREGATE" ),
+};
+
+/** Functions new in Excel 2013.
+
+ See http://office.microsoft.com/en-us/excel-help/new-functions-in-excel-2013-HA103980604.aspx
+ Most functions apparently were added for ODF1.2 ODFF / OpenFormula
+ compatibility.
+
+ Functions with EXC_FUNCENTRY_V_VR_IMPORT are rewritten in
+ sc/source/filter/excel/xeformula.cxx during export for BIFF, OOXML export
+ uses a different mapping but still uses this mapping here to determine the
+ feature set.
+
+ FIXME: either have the exporter determine the feature set from the active
+ mapping, preferred, or enhance this mapping here such that for OOXML the
+ rewrite can be overridden.
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2013 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2013[] =
+{
+ EXC_FUNCENTRY_V_VR_IMPORT( ocArcCot, 1, 1, 0, "ACOT" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocArcCotHyp, 1, 1, 0, "ACOTH" ),
+ EXC_FUNCENTRY_V_VR( ocArabic, 1, 1, 0, "ARABIC" ),
+ EXC_FUNCENTRY_V_VR( ocBase, 2, 3, 0, "BASE" ),
+ EXC_FUNCENTRY_V_VR( ocB, 3, 4, 0, "BINOM.DIST.RANGE" ),
+ EXC_FUNCENTRY_V_VR( ocBitAnd, 2, 2, 0, "BITAND" ),
+ EXC_FUNCENTRY_V_VR( ocBitLshift, 2, 2, 0, "BITLSHIFT" ),
+ EXC_FUNCENTRY_V_VR( ocBitOr, 2, 2, 0, "BITOR" ),
+ EXC_FUNCENTRY_V_VR( ocBitRshift, 2, 2, 0, "BITRSHIFT" ),
+ EXC_FUNCENTRY_V_VR( ocBitXor, 2, 2, 0, "BITXOR" ),
+ EXC_FUNCENTRY_V_VR( ocCeil_Math, 1, 3, 0, "CEILING.MATH" ),
+ EXC_FUNCENTRY_V_RO_EXPORT( ocCeil, 1, 3, 0, "CEILING.MATH" ),
+ EXC_FUNCENTRY_V_VR( ocCombinA, 2, 2, 0, "COMBINA" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCot, 1, 1, 0, "COT" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCotHyp, 1, 1, 0, "COTH" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCosecant, 1, 1, 0, "CSC" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocCosecantHyp, 1, 1, 0, "CSCH" ),
+ EXC_FUNCENTRY_V_VR( ocGetDiffDate, 2, 2, 0, "DAYS" ),
+ EXC_FUNCENTRY_V_VR( ocDecimal, 2, 2, 0, "DECIMAL" ),
+ EXC_FUNCENTRY_V_VR( ocEncodeURL, 1, 1, 0, "ENCODEURL" ),
+ // NOTE: this FDIST is not our LEGACY.FDIST
+ EXC_FUNCENTRY_V_VR( ocNoName, 3, 4, 0, "FDIST" ),
+ // NOTE: this FINV is not our LEGACY.FINV
+ EXC_FUNCENTRY_V_VR( ocNoName, 3, 3, 0, "FINV" ),
+ EXC_FUNCENTRY_V_VR( ocFilterXML, 2, 2, 0, "FILTERXML" ),
+ EXC_FUNCENTRY_V_VR( ocFloor_Math, 1, 3, 0, "FLOOR.MATH" ),
+ EXC_FUNCENTRY_V_RO_EXPORT( ocFloor, 1, 3, 0, "FLOOR.MATH" ),
+ EXC_FUNCENTRY_V_RO( ocFormula, 1, 1, 0, "FORMULATEXT" ),
+ EXC_FUNCENTRY_V_VR( ocGamma, 1, 1, 0, "GAMMA" ),
+ EXC_FUNCENTRY_V_VR( ocGauss, 1, 1, 0, "GAUSS" ),
+ { ocIfNA, NOID, 2, 2, V, { VO, RO }, EXC_FUNCFLAG_IMPORTONLY, EXC_FUNCNAME( "IFNA" ) },
+ { ocIfNA, 255, 3, 3, V, { RO_E, VO, RO }, EXC_FUNCFLAG_EXPORTONLY, EXC_FUNCNAME( "IFNA" ) },
+ // IMCOSH, IMCOT, IMCSC, IMCSCH, IMSEC, IMSECH, IMSINH and IMTAN are
+ // implemented in the Analysis Add-In.
+ EXC_FUNCENTRY_V_RO( ocIsFormula, 1, 1, 0, "ISFORMULA" ),
+ EXC_FUNCENTRY_V_VR( ocWeek, 1, 2, 0, "WEEKNUM" ),
+ EXC_FUNCENTRY_V_VR( ocIsoWeeknum, 1, 1, 0, "ISOWEEKNUM" ),
+ EXC_FUNCENTRY_A_VR( ocMatrixUnit, 1, 1, 0, "MUNIT" ),
+ EXC_FUNCENTRY_V_VR( ocNumberValue, 1, 3, 0, "NUMBERVALUE" ),
+ EXC_FUNCENTRY_V_VR( ocPDuration, 3, 3, 0, "PDURATION" ),
+ EXC_FUNCENTRY_V_VR( ocPermutationA, 2, 2, 0, "PERMUTATIONA" ),
+ EXC_FUNCENTRY_V_VR( ocPhi, 1, 1, 0, "PHI" ),
+ EXC_FUNCENTRY_V_VR( ocRRI, 3, 3, 0, "RRI" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocSecant, 1, 1, 0, "SEC" ),
+ EXC_FUNCENTRY_V_VR_IMPORT( ocSecantHyp, 1, 1, 0, "SECH" ),
+ EXC_FUNCENTRY_V_RO( ocSheet, 0, 1, 0, "SHEET" ),
+ EXC_FUNCENTRY_V_RO( ocSheets, 0, 1, 0, "SHEETS" ),
+ EXC_FUNCENTRY_V_RX( ocSkewp, 1, MX, 0, "SKEW.P" ),
+ EXC_FUNCENTRY_V_VR( ocUnichar, 1, 1, 0, "UNICHAR" ),
+ EXC_FUNCENTRY_V_VR( ocUnicode, 1, 1, 0, "UNICODE" ),
+ EXC_FUNCENTRY_V_VR( ocWebservice, 1, 1, 0, "WEBSERVICE" ),
+ EXC_FUNCENTRY_V_RX( ocXor, 1, MX, 0, "XOR" ),
+ EXC_FUNCENTRY_V_VR( ocErrorType_ODF, 1, 1, 0, "ERROR.TYPE" )
+};
+
+/** Functions new in Excel 2016.
+
+ See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets
+ and https://support.office.com/en-us/article/What-s-New-and-Improved-in-Office-2016-for-Office-365-95c8d81d-08ba-42c1-914f-bca4603e1426?ui=en-US&rs=en-US&ad=US
+
+ @See sc/source/filter/oox/formulabase.cxx saFuncTable2016 for V,VR,RO,...
+ */
+const XclFunctionInfo saFuncTable_2016[] =
+{
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_ADD, 3, 6, 0, "FORECAST.ETS" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_PIA, 3, 7, 0, "FORECAST.ETS.CONFINT" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_SEA, 2, 4, 0, "FORECAST.ETS.SEASONALITY" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_ETS_STA, 3, 6, 0, "FORECAST.ETS.STAT" ),
+ EXC_FUNCENTRY_V_VR( ocForecast_LIN, 3, 3, 0, "FORECAST.LINEAR" ),
+ EXC_FUNCENTRY_V_VR( ocConcat_MS, 1, MX, 0, "CONCAT" ),
+ EXC_FUNCENTRY_V_VR( ocTextJoin_MS, 3, MX, 0, "TEXTJOIN" ),
+ EXC_FUNCENTRY_V_VR( ocIfs_MS, 2, MX, 0, "IFS" ),
+ EXC_FUNCENTRY_V_VR( ocSwitch_MS, 3, MX, 0, "SWITCH" ),
+ EXC_FUNCENTRY_V_VR( ocMinIfs_MS, 3, MX, 0, "MINIFS" ),
+ EXC_FUNCENTRY_V_VR( ocMaxIfs_MS, 3, MX, 0, "MAXIFS" )
+};
+
+#define EXC_FUNCENTRY_ODF( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME_ODF( asciiname ) }
+
+/** Functions defined by OpenFormula, but not supported by Calc (ocNoName) or by Excel (defined op-code). */
+const XclFunctionInfo saFuncTable_Odf[] =
+{
+ EXC_FUNCENTRY_ODF( ocChiSqDist, 2, 3, 0, "CHISQDIST" ),
+ EXC_FUNCENTRY_ODF( ocChiSqInv, 2, 2, 0, "CHISQINV" )
+};
+
+#undef EXC_FUNCENTRY_ODF
+
+#define EXC_FUNCENTRY_OOO( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }, \
+ { opcode, 255, (minparam)+1, (maxparam)+1, V, { RO_E, RO }, EXC_FUNCFLAG_EXPORTONLY|(flags), EXC_FUNCNAME( asciiname ) }
+
+// Import Broken Raw ... even without leading _xlfn.
+#define EXC_FUNCENTRY_OOO_IBR( opcode, minparam, maxparam, flags, asciiname ) \
+ { opcode, NOID, minparam, maxparam, V, { VR }, EXC_FUNCFLAG_IMPORTONLY|(flags), asciiname }
+
+/** Functions defined by Calc, but not in OpenFormula nor supported by Excel. */
+const XclFunctionInfo saFuncTable_OOoLO[] =
+{
+ EXC_FUNCENTRY_OOO( ocErrorType, 1, 1, 0, "ORG.OPENOFFICE.ERRORTYPE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocErrorType, 1, 1, 0, "ERRORTYPE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocMultiArea, 1, MX, 0, "ORG.OPENOFFICE.MULTIRANGE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocMultiArea, 1, MX, 0, "MULTIRANGE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocBackSolver, 3, 3, 0, "ORG.OPENOFFICE.GOALSEEK" ),
+ EXC_FUNCENTRY_OOO_IBR( ocBackSolver,3, 3, 0, "GOALSEEK" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocEasterSunday, 1, 1, 0, "ORG.OPENOFFICE.EASTERSUNDAY" ),
+ EXC_FUNCENTRY_OOO_IBR( ocEasterSunday,1,1, 0, "EASTERSUNDAY" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocCurrent, 0, 0, 0, "ORG.OPENOFFICE.CURRENT" ),
+ EXC_FUNCENTRY_OOO_IBR( ocCurrent, 0, 0, 0, "CURRENT" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocStyle, 1, 3, 0, "ORG.OPENOFFICE.STYLE" ),
+ EXC_FUNCENTRY_OOO_IBR( ocStyle, 1, 3, 0, "STYLE" ), // was written wrongly, read it
+ EXC_FUNCENTRY_OOO( ocConvertOOo, 3, 3, 0, "ORG.OPENOFFICE.CONVERT" ),
+ EXC_FUNCENTRY_OOO( ocColor, 3, 4, 0, "ORG.LIBREOFFICE.COLOR" ),
+ EXC_FUNCENTRY_OOO( ocRawSubtract, 2, MX, 0, "ORG.LIBREOFFICE.RAWSUBTRACT" ),
+ EXC_FUNCENTRY_OOO( ocWeeknumOOo, 2, 2, 0, "ORG.LIBREOFFICE.WEEKNUM_OOO" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_MUL, 3, 6, 0, "ORG.LIBREOFFICE.FORECAST.ETS.MULT" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_PIM, 3, 7, 0, "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT" ),
+ EXC_FUNCENTRY_OOO( ocForecast_ETS_STM, 3, 6, 0, "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT" ),
+ EXC_FUNCENTRY_OOO( ocRoundSig, 2, 2, 0, "ORG.LIBREOFFICE.ROUNDSIG" ),
+ EXC_FUNCENTRY_OOO( ocRegex, 2, 4, 0, "ORG.LIBREOFFICE.REGEX" ),
+ EXC_FUNCENTRY_OOO( ocFourier, 2, 5, 0, "ORG.LIBREOFFICE.FOURIER" ),
+ EXC_FUNCENTRY_OOO( ocRandomNV, 0, 0, 0, "ORG.LIBREOFFICE.RAND.NV" ),
+ EXC_FUNCENTRY_OOO( ocRandbetweenNV, 2, 2, 0, "ORG.LIBREOFFICE.RANDBETWEEN.NV" )
+};
+
+#undef EXC_FUNCENTRY_OOO_IBR
+#undef EXC_FUNCENTRY_OOO
+
+XclFunctionProvider::XclFunctionProvider( const XclRoot& rRoot )
+{
+ void (XclFunctionProvider::*pFillFunc)( const XclFunctionInfo*, const XclFunctionInfo* ) =
+ rRoot.IsImport() ? &XclFunctionProvider::FillXclFuncMap : &XclFunctionProvider::FillScFuncMap;
+
+ /* Only read/write functions supported in the current BIFF version.
+ Function tables from later BIFF versions may overwrite single functions
+ from earlier tables. */
+ XclBiff eBiff = rRoot.GetBiff();
+ if( eBiff >= EXC_BIFF2 )
+ (this->*pFillFunc)(saFuncTable_2, saFuncTable_2 + SAL_N_ELEMENTS(saFuncTable_2));
+ if( eBiff >= EXC_BIFF3 )
+ (this->*pFillFunc)(saFuncTable_3, saFuncTable_3 + SAL_N_ELEMENTS(saFuncTable_3));
+ if( eBiff >= EXC_BIFF4 )
+ (this->*pFillFunc)(saFuncTable_4, saFuncTable_4 + SAL_N_ELEMENTS(saFuncTable_4));
+ if( eBiff >= EXC_BIFF5 )
+ (this->*pFillFunc)(saFuncTable_5, saFuncTable_5 + SAL_N_ELEMENTS(saFuncTable_5));
+ if( eBiff >= EXC_BIFF8 )
+ (this->*pFillFunc)(saFuncTable_8, saFuncTable_8 + SAL_N_ELEMENTS(saFuncTable_8));
+ (this->*pFillFunc)(saFuncTable_Oox, saFuncTable_Oox + SAL_N_ELEMENTS(saFuncTable_Oox));
+ (this->*pFillFunc)(saFuncTable_2010, saFuncTable_2010 + SAL_N_ELEMENTS(saFuncTable_2010));
+ (this->*pFillFunc)(saFuncTable_2013, saFuncTable_2013 + SAL_N_ELEMENTS(saFuncTable_2013));
+ (this->*pFillFunc)(saFuncTable_2016, saFuncTable_2016 + SAL_N_ELEMENTS(saFuncTable_2016));
+ (this->*pFillFunc)(saFuncTable_Odf, saFuncTable_Odf + SAL_N_ELEMENTS(saFuncTable_Odf));
+ (this->*pFillFunc)(saFuncTable_OOoLO, saFuncTable_OOoLO + SAL_N_ELEMENTS(saFuncTable_OOoLO));
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromXclFunc( sal_uInt16 nXclFunc ) const
+{
+ // only in import filter allowed
+ OSL_ENSURE( !maXclFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromXclFunc - wrong filter" );
+ XclFuncMap::const_iterator aIt = maXclFuncMap.find( nXclFunc );
+ return (aIt == maXclFuncMap.end()) ? nullptr : aIt->second;
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromXclMacroName( const OUString& rXclMacroName ) const
+{
+ // only in import filter allowed, but do not test maXclMacroNameMap, it may be empty for old BIFF versions
+ OSL_ENSURE( !maXclFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromXclMacroName - wrong filter" );
+ XclMacroNameMap::const_iterator aIt = maXclMacroNameMap.find( rXclMacroName );
+ return (aIt == maXclMacroNameMap.end()) ? nullptr : aIt->second;
+}
+
+const XclFunctionInfo* XclFunctionProvider::GetFuncInfoFromOpCode( OpCode eOpCode ) const
+{
+ // only in export filter allowed
+ OSL_ENSURE( !maScFuncMap.empty(), "XclFunctionProvider::GetFuncInfoFromOpCode - wrong filter" );
+ ScFuncMap::const_iterator aIt = maScFuncMap.find( eOpCode );
+ return (aIt == maScFuncMap.end()) ? nullptr : aIt->second;
+}
+
+void XclFunctionProvider::FillXclFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd )
+{
+ for( const XclFunctionInfo* pIt = pBeg; pIt != pEnd; ++pIt )
+ {
+ if( !::get_flag( pIt->mnFlags, EXC_FUNCFLAG_EXPORTONLY ) )
+ {
+ if( pIt->mnXclFunc != NOID )
+ maXclFuncMap[ pIt->mnXclFunc ] = pIt;
+ if( pIt->IsMacroFunc() )
+ maXclMacroNameMap[ pIt->GetMacroFuncName() ] = pIt;
+ }
+ }
+}
+
+void XclFunctionProvider::FillScFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd )
+{
+ for( const XclFunctionInfo* pIt = pBeg; pIt != pEnd; ++pIt )
+ if( !::get_flag( pIt->mnFlags, EXC_FUNCFLAG_IMPORTONLY ) )
+ maScFuncMap[ pIt->meOpCode ] = pIt;
+}
+
+// Token array ================================================================
+
+XclTokenArray::XclTokenArray( bool bVolatile ) :
+ mbVolatile( bVolatile )
+{
+}
+
+XclTokenArray::XclTokenArray( ScfUInt8Vec& rTokVec, ScfUInt8Vec& rExtDataVec, bool bVolatile ) :
+ mbVolatile( bVolatile )
+{
+ maTokVec.swap( rTokVec );
+ maExtDataVec.swap( rExtDataVec );
+}
+
+sal_uInt16 XclTokenArray::GetSize() const
+{
+ OSL_ENSURE( maTokVec.size() <= 0xFFFF, "XclTokenArray::GetSize - array too long" );
+ return limit_cast< sal_uInt16 >( maTokVec.size() );
+}
+
+sal_uInt16 XclTokenArray::ReadSize(XclImpStream& rStrm)
+{
+ return rStrm.ReaduInt16();
+}
+
+void XclTokenArray::ReadArray(sal_uInt16 nSize, XclImpStream& rStrm)
+{
+ maTokVec.resize(0);
+
+ const std::size_t nMaxBuffer = 4096;
+ std::size_t nBytesLeft = nSize;
+ std::size_t nTotalRead = 0;
+
+ while (true)
+ {
+ if (!nBytesLeft)
+ break;
+ std::size_t nReadRequest = o3tl::sanitizing_min(nBytesLeft, nMaxBuffer);
+ maTokVec.resize(maTokVec.size() + nReadRequest);
+ auto nRead = rStrm.Read(maTokVec.data() + nTotalRead, nReadRequest);
+ nTotalRead += nRead;
+ if (nRead != nReadRequest)
+ {
+ maTokVec.resize(nTotalRead);
+ break;
+ }
+ nBytesLeft -= nRead;
+ }
+}
+
+void XclTokenArray::Read( XclImpStream& rStrm )
+{
+ ReadArray(ReadSize(rStrm), rStrm);
+}
+
+void XclTokenArray::WriteSize( XclExpStream& rStrm ) const
+{
+ rStrm << GetSize();
+}
+
+void XclTokenArray::WriteArray( XclExpStream& rStrm ) const
+{
+ if( !maTokVec.empty() )
+ rStrm.Write(maTokVec.data(), GetSize());
+ if( !maExtDataVec.empty() )
+ rStrm.Write(maExtDataVec.data(), maExtDataVec.size());
+}
+
+void XclTokenArray::Write( XclExpStream& rStrm ) const
+{
+ WriteSize( rStrm );
+ WriteArray( rStrm );
+}
+
+bool XclTokenArray::operator==( const XclTokenArray& rTokArr ) const
+{
+ return (mbVolatile == rTokArr.mbVolatile) && (maTokVec == rTokArr.maTokVec) && (maExtDataVec == rTokArr.maExtDataVec);
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclTokenArray& rTokArr )
+{
+ rTokArr.Read( rStrm );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArray& rTokArr )
+{
+ rTokArr.Write( rStrm );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArrayRef& rxTokArr )
+{
+ if( rxTokArr )
+ rxTokArr->Write( rStrm );
+ else
+ rStrm << sal_uInt16( 0 );
+ return rStrm;
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator() :
+ mppScTokenBeg( nullptr ),
+ mppScTokenEnd( nullptr ),
+ mppScToken( nullptr ),
+ mbSkipSpaces( false )
+{
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator( const ScTokenArray& rScTokArr, bool bSkipSpaces )
+{
+ Init( rScTokArr, bSkipSpaces );
+}
+
+XclTokenArrayIterator::XclTokenArrayIterator( const XclTokenArrayIterator& rTokArrIt, bool bSkipSpaces ) :
+ mppScTokenBeg( rTokArrIt.mppScTokenBeg ),
+ mppScTokenEnd( rTokArrIt.mppScTokenEnd ),
+ mppScToken( rTokArrIt.mppScToken ),
+ mbSkipSpaces( bSkipSpaces )
+{
+ SkipSpaces();
+}
+
+void XclTokenArrayIterator::Init( const ScTokenArray& rScTokArr, bool bSkipSpaces )
+{
+ sal_uInt16 nTokArrLen = rScTokArr.GetLen();
+ mppScTokenBeg = static_cast< const FormulaToken* const* >( nTokArrLen ? rScTokArr.GetArray() : nullptr );
+ mppScTokenEnd = mppScTokenBeg ? (mppScTokenBeg + nTokArrLen) : nullptr;
+ mppScToken = (mppScTokenBeg != mppScTokenEnd) ? mppScTokenBeg : nullptr;
+ mbSkipSpaces = bSkipSpaces;
+ SkipSpaces();
+}
+
+XclTokenArrayIterator& XclTokenArrayIterator::operator++()
+{
+ NextRawToken();
+ SkipSpaces();
+ return *this;
+}
+
+void XclTokenArrayIterator::NextRawToken()
+{
+ if( mppScToken )
+ if( (++mppScToken == mppScTokenEnd) || !*mppScToken )
+ mppScToken = nullptr;
+}
+
+void XclTokenArrayIterator::SkipSpaces()
+{
+ if( mbSkipSpaces )
+ {
+ OpCode eOp;
+ while( Is() && (((eOp = (*this)->GetOpCode()) == ocSpaces) || eOp == ocWhitespace) )
+ NextRawToken();
+ }
+}
+
+// strings and string lists ---------------------------------------------------
+
+bool XclTokenArrayHelper::GetTokenString( OUString& rString, const FormulaToken& rScToken )
+{
+ bool bIsStr = (rScToken.GetType() == svString) && (rScToken.GetOpCode() == ocPush);
+ if( bIsStr ) rString = rScToken.GetString().getString();
+ return bIsStr;
+}
+
+bool XclTokenArrayHelper::GetString( OUString& rString, const ScTokenArray& rScTokArr )
+{
+ XclTokenArrayIterator aIt( rScTokArr, true );
+ // something is following the string token -> error
+ return aIt.Is() && GetTokenString( rString, *aIt ) && !++aIt;
+}
+
+bool XclTokenArrayHelper::GetStringList( OUString& rStringList, const ScTokenArray& rScTokArr, sal_Unicode cSep )
+{
+ bool bRet = true;
+ XclTokenArrayIterator aIt( rScTokArr, true );
+ enum { STATE_START, STATE_STR, STATE_SEP, STATE_END } eState = STATE_START;
+ while( eState != STATE_END ) switch( eState )
+ {
+ case STATE_START:
+ eState = aIt.Is() ? STATE_STR : STATE_END;
+ break;
+ case STATE_STR:
+ {
+ OUString aString;
+ bRet = GetTokenString( aString, *aIt );
+ if( bRet ) rStringList += aString ;
+ eState = (bRet && (++aIt).Is()) ? STATE_SEP : STATE_END;
+ break;
+ }
+ case STATE_SEP:
+ bRet = aIt->GetOpCode() == ocSep;
+ if( bRet ) rStringList += OUStringChar(cSep);
+ eState = (bRet && (++aIt).Is()) ? STATE_STR : STATE_END;
+ break;
+ default:;
+ }
+ return bRet;
+}
+
+void XclTokenArrayHelper::ConvertStringToList(
+ ScTokenArray& rScTokArr, svl::SharedStringPool& rSPool, sal_Unicode cStringSep )
+{
+ OUString aString;
+ if( !GetString( aString, rScTokArr ) )
+ return;
+
+ rScTokArr.Clear();
+ if (aString.isEmpty())
+ return;
+ sal_Int32 nStringIx = 0;
+ for (;;)
+ {
+ OUString aToken( aString.getToken( 0, cStringSep, nStringIx ) );
+ rScTokArr.AddString(rSPool.intern(comphelper::string::stripStart(aToken, ' ')));
+ if (nStringIx<0)
+ break;
+ rScTokArr.AddOpCode( ocSep );
+ }
+}
+
+// multiple operations --------------------------------------------------------
+
+namespace {
+
+bool lclGetAddress( const ScDocument& rDoc, ScAddress& rAddress, const FormulaToken& rToken, const ScAddress& rPos )
+{
+ OpCode eOpCode = rToken.GetOpCode();
+ bool bIsSingleRef = (eOpCode == ocPush) && (rToken.GetType() == svSingleRef);
+ if( bIsSingleRef )
+ {
+ const ScSingleRefData& rRef = *rToken.GetSingleRef();
+ rAddress = rRef.toAbs(rDoc, rPos);
+ bIsSingleRef = !rRef.IsDeleted();
+ }
+ return bIsSingleRef;
+}
+
+} // namespace
+
+bool XclTokenArrayHelper::GetMultipleOpRefs(
+ const ScDocument& rDoc,
+ XclMultipleOpRefs& rRefs, const ScTokenArray& rScTokArr, const ScAddress& rScPos )
+{
+ rRefs.mbDblRefMode = false;
+ enum
+ {
+ stBegin, stTableOp, stOpen, stFormula, stFormulaSep,
+ stColFirst, stColFirstSep, stColRel, stColRelSep,
+ stRowFirst, stRowFirstSep, stRowRel, stClose, stError
+ } eState = stBegin; // last read token
+ for( XclTokenArrayIterator aIt( rScTokArr, true ); aIt.Is() && (eState != stError); ++aIt )
+ {
+ OpCode eOpCode = aIt->GetOpCode();
+ bool bIsSep = eOpCode == ocSep;
+ switch( eState )
+ {
+ case stBegin:
+ eState = (eOpCode == ocTableOp) ? stTableOp : stError;
+ break;
+ case stTableOp:
+ eState = (eOpCode == ocOpen) ? stOpen : stError;
+ break;
+ case stOpen:
+ eState = lclGetAddress(rDoc, rRefs.maFmlaScPos, *aIt, rScPos) ? stFormula : stError;
+ break;
+ case stFormula:
+ eState = bIsSep ? stFormulaSep : stError;
+ break;
+ case stFormulaSep:
+ eState = lclGetAddress(rDoc, rRefs.maColFirstScPos, *aIt, rScPos) ? stColFirst : stError;
+ break;
+ case stColFirst:
+ eState = bIsSep ? stColFirstSep : stError;
+ break;
+ case stColFirstSep:
+ eState = lclGetAddress(rDoc, rRefs.maColRelScPos, *aIt, rScPos) ? stColRel : stError;
+ break;
+ case stColRel:
+ eState = bIsSep ? stColRelSep : ((eOpCode == ocClose) ? stClose : stError);
+ break;
+ case stColRelSep:
+ eState = lclGetAddress(rDoc, rRefs.maRowFirstScPos, *aIt, rScPos) ? stRowFirst : stError;
+ rRefs.mbDblRefMode = true;
+ break;
+ case stRowFirst:
+ eState = bIsSep ? stRowFirstSep : stError;
+ break;
+ case stRowFirstSep:
+ eState = lclGetAddress(rDoc, rRefs.maRowRelScPos, *aIt, rScPos) ? stRowRel : stError;
+ break;
+ case stRowRel:
+ eState = (eOpCode == ocClose) ? stClose : stError;
+ break;
+ default:
+ eState = stError;
+ }
+ }
+ return eState == stClose;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlpage.cxx b/sc/source/filter/excel/xlpage.cxx
new file mode 100644
index 000000000..937aa9427
--- /dev/null
+++ b/sc/source/filter/excel/xlpage.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlpage.hxx>
+#include <xltools.hxx>
+#include <editeng/paperinf.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <sal/macros.h>
+#include <editeng/brushitem.hxx>
+
+namespace{
+
+struct XclPaperSize
+{
+ Paper mePaper; /// SVX paper size identifier.
+ tools::Long mnWidth; /// Paper width in twips.
+ tools::Long mnHeight; /// Paper height in twips.
+};
+
+constexpr tools::Long in2twips(double n_inch)
+{
+ return o3tl::convert(n_inch, o3tl::Length::in, o3tl::Length::twip) + 0.5;
+}
+constexpr tools::Long mm2twips(double n_mm)
+{
+ return o3tl::convert(n_mm, o3tl::Length::mm, o3tl::Length::twip) + 0.5;
+}
+constexpr tools::Long twips2mm(tools::Long n_twips)
+{
+ return o3tl::convert(n_twips, o3tl::Length::twip, o3tl::Length::mm);
+}
+
+// see ApiPaperSize spPaperSizeTable in filter and aDinTab in i18nutil
+constexpr XclPaperSize pPaperSizeTable[] =
+{
+/* 0*/ { PAPER_USER, 0, 0 }, // undefined
+ { PAPER_LETTER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter Small
+ { PAPER_TABLOID, in2twips( 11 ), in2twips( 17 ) }, // Tabloid
+ { PAPER_LEDGER, in2twips( 17 ), in2twips( 11 ) }, // Ledger
+/* 5*/ { PAPER_LEGAL, in2twips( 8.5 ), in2twips( 14 ) }, // Legal
+ { PAPER_STATEMENT, in2twips( 5.5 ), in2twips( 8.5 ) }, // Statement
+ { PAPER_EXECUTIVE, in2twips( 7.25 ), in2twips( 10.5 ) }, // Executive
+ { PAPER_A3, mm2twips( 297 ), mm2twips( 420 ) }, // A3
+ { PAPER_A4, mm2twips( 210 ), mm2twips( 297 ) }, // A4
+/* 10*/ { PAPER_USER, mm2twips( 210 ), mm2twips( 297 ) }, // A4 Small
+ { PAPER_A5, mm2twips( 148 ), mm2twips( 210 ) }, // A5
+ /* for JIS vs ISO B confusion see:
+ https://docs.microsoft.com/en-us/windows/win32/intl/paper-sizes
+ http://wiki.openoffice.org/wiki/DefaultPaperSize comments
+ http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf */
+ { PAPER_B4_JIS, mm2twips( 257 ), mm2twips( 364 ) }, // B4 (JIS)
+ { PAPER_B5_JIS, mm2twips( 182 ), mm2twips( 257 ) }, // B5 (JIS)
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 13 ) }, // Folio
+/* 15*/ { PAPER_QUARTO, mm2twips( 215 ), mm2twips( 275 ) }, // Quarto
+ { PAPER_10x14, in2twips( 10 ), in2twips( 14 ) }, // 10x14
+ { PAPER_USER, in2twips( 11 ), in2twips( 17 ) }, // 11x17
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Note
+ { PAPER_ENV_9, in2twips( 3.875 ), in2twips( 8.875 ) }, // Envelope #9
+/* 20*/ { PAPER_ENV_10, in2twips( 4.125 ), in2twips( 9.5 ) }, // Envelope #10
+ { PAPER_ENV_11, in2twips( 4.5 ), in2twips( 10.375 ) }, // Envelope #11
+ { PAPER_ENV_12, in2twips( 4.75 ), in2twips( 11 ) }, // Envelope #12
+ { PAPER_ENV_14, in2twips( 5 ), in2twips( 11.5 ) }, // Envelope #14
+ { PAPER_C, in2twips( 17 ), in2twips( 22 ) }, // ANSI-C
+/* 25*/ { PAPER_D, in2twips( 22 ), in2twips( 34 ) }, // ANSI-D
+ { PAPER_E, in2twips( 34 ), in2twips( 44 ) }, // ANSI-E
+ { PAPER_ENV_DL, mm2twips( 110 ), mm2twips( 220 ) }, // Envelope DL
+ { PAPER_ENV_C5, mm2twips( 162 ), mm2twips( 229 ) }, // Envelope C5
+ { PAPER_ENV_C3, mm2twips( 324 ), mm2twips( 458 ) }, // Envelope C3
+/* 30*/ { PAPER_ENV_C4, mm2twips( 229 ), mm2twips( 324 ) }, // Envelope C4
+ { PAPER_ENV_C6, mm2twips( 114 ), mm2twips( 162 ) }, // Envelope C6
+ { PAPER_ENV_C65, mm2twips( 114 ), mm2twips( 229 ) }, // Envelope C65
+ { PAPER_B4_ISO, mm2twips( 250 ), mm2twips( 353 ) }, // B4 (ISO)
+ { PAPER_B5_ISO, mm2twips( 176 ), mm2twips( 250 ) }, // B5 (ISO)
+/* 35*/ { PAPER_B6_ISO, mm2twips( 125 ), mm2twips( 176 ) }, // B6 (ISO)
+ { PAPER_ENV_ITALY, mm2twips( 110 ), mm2twips( 230 ) }, // Envelope Italy
+ { PAPER_ENV_MONARCH, in2twips( 3.875 ), in2twips( 7.5 ) }, // Envelope Monarch
+ { PAPER_ENV_PERSONAL, in2twips( 3.625 ), in2twips( 6.5 ) }, // 6 3/4 Envelope
+ { PAPER_FANFOLD_US, in2twips( 14.875 ), in2twips( 11 ) }, // US Std Fanfold
+/* 40*/ { PAPER_FANFOLD_DE, in2twips( 8.5 ), in2twips( 12 ) }, // German Std Fanfold
+ { PAPER_FANFOLD_LEGAL_DE, in2twips( 8.5 ), in2twips( 13 ) }, // German Legal Fanfold
+ { PAPER_B4_ISO, mm2twips( 250 ), mm2twips( 353 ) }, // B4 (ISO)
+ { PAPER_POSTCARD_JP,mm2twips( 100 ), mm2twips( 148 ) }, // Japanese Postcard
+ { PAPER_9x11, in2twips( 9 ), in2twips( 11 ) }, // 9x11
+/* 45*/ { PAPER_10x11, in2twips( 10 ), in2twips( 11 ) }, // 10x11
+ { PAPER_15x11, in2twips( 15 ), in2twips( 11 ) }, // 15x11
+ { PAPER_ENV_INVITE, mm2twips( 220 ), mm2twips( 220 ) }, // Envelope Invite
+ { PAPER_USER, 0, 0 }, // undefined
+ { PAPER_USER, 0, 0 }, // undefined
+ /* See: https://docs.microsoft.com/en-us/windows/win32/intl/paper-sizes */
+/* 50*/ { PAPER_USER, in2twips( 9.5 ), in2twips( 12 ) }, // Letter Extra
+ { PAPER_USER, in2twips( 9.5 ), in2twips( 15 ) }, // Legal Extra
+ { PAPER_USER, in2twips( 11.69 ), in2twips( 18 ) }, // Tabloid Extra
+ { PAPER_USER, mm2twips( 235 ), mm2twips( 322 ) }, // A4 Extra
+ { PAPER_USER, in2twips( 8.5 ), in2twips( 11 ) }, // Letter Transverse
+/* 55*/ { PAPER_USER, mm2twips( 210 ), mm2twips( 297 ) }, // A4 Transverse
+ { PAPER_USER, in2twips( 9.5 ), in2twips( 12 ) }, // Letter Extra Transverse
+ { PAPER_A_PLUS, mm2twips( 227 ), mm2twips( 356 ) }, // Super A/A4
+ { PAPER_B_PLUS, mm2twips( 305 ), mm2twips( 487 ) }, // Super B/A3
+ { PAPER_LETTER_PLUS,in2twips( 8.5 ), in2twips( 12.69 ) }, // Letter Plus
+/* 60*/ { PAPER_A4_PLUS, mm2twips( 210 ), mm2twips( 330 ) }, // A4 Plus
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 210 ) }, // A5 Transverse
+ { PAPER_USER, mm2twips( 182 ), mm2twips( 257 ) }, // B5 (JIS) Transverse
+ { PAPER_USER, mm2twips( 322 ), mm2twips( 445 ) }, // A3 Extra
+ { PAPER_USER, mm2twips( 174 ), mm2twips( 235 ) }, // A5 Extra
+/* 65*/ { PAPER_USER, mm2twips( 201 ), mm2twips( 276 ) }, // B5 (ISO) Extra
+ { PAPER_A2, mm2twips( 420 ), mm2twips( 594 ) }, // A2
+ { PAPER_USER, mm2twips( 297 ), mm2twips( 420 ) }, // A3 Transverse
+ { PAPER_USER, mm2twips( 322 ), mm2twips( 445 ) }, // A3 Extra Transverse
+ { PAPER_DOUBLEPOSTCARD_JP, mm2twips( 200 ), mm2twips( 148 ) }, // Double Japanese Postcard
+/* 70*/ { PAPER_A6, mm2twips( 105 ), mm2twips( 148 ) }, // A6
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #2
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #3
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #3
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #4
+/* 75*/ { PAPER_USER, in2twips( 11 ), in2twips( 8.5 ) }, // Letter Rotated
+ { PAPER_USER, mm2twips( 420 ), mm2twips( 297 ) }, // A3 Rotated
+ { PAPER_USER, mm2twips( 297 ), mm2twips( 210 ) }, // A4 Rotated
+ { PAPER_USER, mm2twips( 210 ), mm2twips( 148 ) }, // A5 Rotated
+ { PAPER_USER, mm2twips( 364 ), mm2twips( 257 ) }, // B4 (JIS) Rotated
+/* 80*/ { PAPER_USER, mm2twips( 257 ), mm2twips( 182 ) }, // B5 (JIS) Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 100 ) }, // Japanese Postcard Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 200 ) }, // Double Japanese Postcard Rotated
+ { PAPER_USER, mm2twips( 148 ), mm2twips( 105 ) }, // A6 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #2 Rotated
+/* 85*/ { PAPER_USER, 0, 0 }, // Japanese Envelope Kaku #3 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #3 Rotated
+ { PAPER_USER, 0, 0 }, // Japanese Envelope Chou #4 Rotated
+ { PAPER_B6_JIS, mm2twips( 128 ), mm2twips( 182 ) }, // B6 (JIS)
+ { PAPER_USER, mm2twips( 182 ), mm2twips( 128 ) }, // B6 (JIS) Rotated
+/* 90*/ { PAPER_12x11, in2twips( 12 ), in2twips( 11 ) } // 12x11
+};
+
+} //namespace
+
+// Page settings ==============================================================
+
+XclPageData::XclPageData()
+{
+ SetDefaults();
+}
+
+XclPageData::~XclPageData()
+{
+ // SvxBrushItem incomplete in header
+}
+
+void XclPageData::SetDefaults()
+{
+ maHorPageBreaks.clear();
+ maVerPageBreaks.clear();
+ mxBrushItem.reset();
+ maHeader.clear();
+ maFooter.clear();
+ maHeaderEven.clear();
+ maFooterEven.clear();
+ mfLeftMargin = mfRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_LR );
+ mfTopMargin = mfBottomMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_TB );
+ mfHeaderMargin = mfFooterMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_HF );
+ mfHdrLeftMargin = mfHdrRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_HLR );
+ mfFtrLeftMargin = mfFtrRightMargin = XclTools::GetInchFromHmm( EXC_MARGIN_DEFAULT_FLR );
+ mnStrictPaperSize = mnPaperSize = EXC_PAPERSIZE_DEFAULT;
+ mnPaperWidth = 0;
+ mnPaperHeight = 0;
+ mnCopies = 1;
+ mnStartPage = 0;
+ mnScaling = 100;
+ mnFitToWidth = mnFitToHeight = 1;
+ mnHorPrintRes = mnVerPrintRes = 300;
+ mbUseEvenHF = mbUseFirstHF = false;
+ mbValid = false;
+ mbPortrait = true;
+ mbPrintInRows = mbBlackWhite = mbDraftQuality = mbPrintNotes = mbManualStart = mbFitToPages = false;
+ mbHorCenter = mbVerCenter = mbPrintHeadings = mbPrintGrid = false;
+}
+
+Size XclPageData::GetScPaperSize() const
+{
+ const XclPaperSize* pEntry = pPaperSizeTable;
+ if( mnPaperSize < SAL_N_ELEMENTS( pPaperSizeTable ) )
+ pEntry += mnPaperSize;
+
+ Size aSize;
+ if( pEntry->mePaper == PAPER_USER )
+ aSize = Size( pEntry->mnWidth, pEntry->mnHeight );
+ else
+ aSize = SvxPaperInfo::GetPaperSize( pEntry->mePaper );
+
+ // invalid size -> back to default
+ if( !aSize.Width() || !aSize.Height() )
+ aSize = SvxPaperInfo::GetDefaultPaperSize();
+
+ if( !mbPortrait )
+ {
+ // swap width and height
+ tools::Long n = aSize.Width();
+ aSize.setWidth(aSize.Height());
+ aSize.setHeight(n);
+ }
+
+ return aSize;
+}
+
+void XclPageData::SetScPaperSize( const Size& rSize, bool bPortrait, bool bStrictSize )
+{
+ mbPortrait = bPortrait;
+ mnPaperSize = 0;
+ tools::Long nWidth = bPortrait ? rSize.Width() : rSize.Height();
+ tools::Long nHeight = bPortrait ? rSize.Height() : rSize.Width();
+ tools::Long nMaxWDiff = 80;
+ tools::Long nMaxHDiff = 50;
+
+ mnPaperWidth = twips2mm( nWidth );
+ mnPaperHeight = twips2mm( nHeight );
+ if( bStrictSize )
+ {
+ nMaxWDiff = 5;
+ nMaxHDiff = 5;
+ mnStrictPaperSize = EXC_PAPERSIZE_USER;
+ }
+ else
+ {
+ mnPaperSize = EXC_PAPERSIZE_DEFAULT;
+ }
+
+ for( const auto &rEntry : pPaperSizeTable)
+ {
+ tools::Long nWDiff = std::abs( rEntry.mnWidth - nWidth );
+ tools::Long nHDiff = std::abs( rEntry.mnHeight - nHeight );
+ if( ((nWDiff <= nMaxWDiff) && (nHDiff < nMaxHDiff)) ||
+ ((nWDiff < nMaxWDiff) && (nHDiff <= nMaxHDiff)) )
+ {
+ sal_uInt16 nIndex = static_cast< sal_uInt16 >( &rEntry - pPaperSizeTable );
+ mnPaperSize = nIndex;
+ if( bStrictSize )
+ mnStrictPaperSize = nIndex;
+
+ nMaxWDiff = nWDiff;
+ nMaxHDiff = nHDiff;
+ }
+ }
+ if( !bStrictSize )
+ SetScPaperSize( rSize, bPortrait, true );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlpivot.cxx b/sc/source/filter/excel/xlpivot.cxx
new file mode 100644
index 000000000..d18ab7416
--- /dev/null
+++ b/sc/source/filter/excel/xlpivot.cxx
@@ -0,0 +1,1043 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dpsave.hxx>
+#include <xestream.hxx>
+#include <xistream.hxx>
+#include <xestring.hxx>
+#include <xlpivot.hxx>
+#include <generalfunction.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+
+using ::com::sun::star::sheet::DataPilotFieldOrientation;
+
+namespace ScDPSortMode = ::com::sun::star::sheet::DataPilotFieldSortMode;
+namespace ScDPShowItemsMode = ::com::sun::star::sheet::DataPilotFieldShowItemsMode;
+namespace ScDPLayoutMode = ::com::sun::star::sheet::DataPilotFieldLayoutMode;
+namespace ScDPRefItemType = ::com::sun::star::sheet::DataPilotFieldReferenceItemType;
+namespace ScDPGroupBy = ::com::sun::star::sheet::DataPilotFieldGroupBy;
+
+// Pivot cache
+
+XclPCItem::XclPCItem() :
+ meType( EXC_PCITEM_INVALID ),
+ maDateTime( DateTime::EMPTY )
+{
+}
+
+XclPCItem::~XclPCItem()
+{
+}
+
+void XclPCItem::SetEmpty()
+{
+ meType = EXC_PCITEM_EMPTY;
+ maText.clear();
+}
+
+void XclPCItem::SetText( const OUString& rText )
+{
+ meType = EXC_PCITEM_TEXT;
+ maText = rText;
+}
+
+void XclPCItem::SetDouble( double fValue, const OUString& rText )
+{
+ meType = EXC_PCITEM_DOUBLE;
+ maText = rText;
+ mfValue = fValue;
+}
+
+void XclPCItem::SetDateTime( const DateTime& rDateTime, const OUString& rText )
+{
+ meType = EXC_PCITEM_DATETIME;
+ maText = rText;
+ maDateTime = rDateTime;
+}
+
+void XclPCItem::SetInteger( sal_Int16 nValue )
+{
+ meType = EXC_PCITEM_INTEGER;
+ maText = OUString::number(nValue);
+ mnValue = nValue;
+}
+
+void XclPCItem::SetError( sal_uInt16 nError )
+{
+ meType = EXC_PCITEM_ERROR;
+ maText.clear();
+ mnError = nError;
+ switch( nError )
+ {
+ case 0x00: maText = "#nullptr!"; break;
+ case 0x07: maText = "#DIV/0!"; break;
+ case 0x0F: maText = "#VALUE!"; break;
+ case 0x17: maText = "#REF!"; break;
+ case 0x1D: maText = "#NAME?"; break;
+ case 0x24: maText = "#NUM!"; break;
+ case 0x2A: maText = "#N/A"; break;
+ default: break;
+ }
+}
+
+void XclPCItem::SetBool( bool bValue, const OUString& rText )
+{
+ meType = EXC_PCITEM_BOOL;
+ maText = rText;
+ mbValue = bValue;
+}
+
+bool XclPCItem::IsEqual( const XclPCItem& rItem ) const
+{
+ if( meType == rItem.meType ) switch( meType )
+ {
+ case EXC_PCITEM_INVALID: return true;
+ case EXC_PCITEM_EMPTY: return true;
+ case EXC_PCITEM_TEXT: return maText == rItem.maText;
+ case EXC_PCITEM_DOUBLE: return mfValue == rItem.mfValue;
+ case EXC_PCITEM_DATETIME: return maDateTime == rItem.maDateTime;
+ case EXC_PCITEM_INTEGER: return mnValue == rItem.mnValue;
+ case EXC_PCITEM_BOOL: return mbValue == rItem.mbValue;
+ case EXC_PCITEM_ERROR: return mnError == rItem.mnError;
+ default: OSL_FAIL( "XclPCItem::IsEqual - unknown pivot cache item type" );
+ }
+ return false;
+}
+
+bool XclPCItem::IsEmpty() const
+{
+ return meType == EXC_PCITEM_EMPTY;
+}
+
+const OUString* XclPCItem::GetText() const
+{
+ return (meType == EXC_PCITEM_TEXT || meType == EXC_PCITEM_ERROR) ? &maText : nullptr;
+}
+
+const double* XclPCItem::GetDouble() const
+{
+ return (meType == EXC_PCITEM_DOUBLE) ? &mfValue : nullptr;
+}
+
+const DateTime* XclPCItem::GetDateTime() const
+{
+ return (meType == EXC_PCITEM_DATETIME) ? &maDateTime : nullptr;
+}
+
+const sal_Int16* XclPCItem::GetInteger() const
+{
+ return (meType == EXC_PCITEM_INTEGER) ? &mnValue : nullptr;
+}
+
+const sal_uInt16* XclPCItem::GetError() const
+{
+ return (meType == EXC_PCITEM_ERROR) ? &mnError : nullptr;
+}
+
+const bool* XclPCItem::GetBool() const
+{
+ return (meType == EXC_PCITEM_BOOL) ? &mbValue : nullptr;
+}
+
+XclPCItemType XclPCItem::GetType() const
+{
+ return meType;
+}
+
+// Field settings =============================================================
+
+XclPCFieldInfo::XclPCFieldInfo() :
+ mnFlags( 0 ),
+ mnGroupChild( 0 ),
+ mnGroupBase( 0 ),
+ mnVisItems( 0 ),
+ mnGroupItems( 0 ),
+ mnBaseItems( 0 ),
+ mnOrigItems( 0 )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCFieldInfo& rInfo )
+{
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnGroupChild = rStrm.ReaduInt16();
+ rInfo.mnGroupBase = rStrm.ReaduInt16();
+ rInfo.mnVisItems = rStrm.ReaduInt16();
+ rInfo.mnGroupItems = rStrm.ReaduInt16();
+ rInfo.mnBaseItems = rStrm.ReaduInt16();
+ rInfo.mnOrigItems = rStrm.ReaduInt16();
+ if( rStrm.GetRecLeft() >= 3 )
+ rInfo.maName = rStrm.ReadUniString();
+ else
+ rInfo.maName.clear();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnFlags
+ << rInfo.mnGroupChild
+ << rInfo.mnGroupBase
+ << rInfo.mnVisItems
+ << rInfo.mnGroupItems
+ << rInfo.mnBaseItems
+ << rInfo.mnOrigItems
+ << XclExpString( rInfo.maName );
+}
+
+// Numeric grouping field settings ============================================
+
+XclPCNumGroupInfo::XclPCNumGroupInfo() :
+ mnFlags( EXC_SXNUMGROUP_AUTOMIN | EXC_SXNUMGROUP_AUTOMAX )
+{
+ SetNumType();
+}
+
+void XclPCNumGroupInfo::SetNumType()
+{
+ SetXclDataType( EXC_SXNUMGROUP_TYPE_NUM );
+}
+
+sal_Int32 XclPCNumGroupInfo::GetScDateType() const
+{
+ sal_Int32 nScType = 0;
+ switch( GetXclDataType() )
+ {
+ case EXC_SXNUMGROUP_TYPE_SEC: nScType = ScDPGroupBy::SECONDS; break;
+ case EXC_SXNUMGROUP_TYPE_MIN: nScType = ScDPGroupBy::MINUTES; break;
+ case EXC_SXNUMGROUP_TYPE_HOUR: nScType = ScDPGroupBy::HOURS; break;
+ case EXC_SXNUMGROUP_TYPE_DAY: nScType = ScDPGroupBy::DAYS; break;
+ case EXC_SXNUMGROUP_TYPE_MONTH: nScType = ScDPGroupBy::MONTHS; break;
+ case EXC_SXNUMGROUP_TYPE_QUART: nScType = ScDPGroupBy::QUARTERS; break;
+ case EXC_SXNUMGROUP_TYPE_YEAR: nScType = ScDPGroupBy::YEARS; break;
+ default: SAL_WARN("sc.filter", "XclPCNumGroupInfo::GetScDateType - unexpected date type " << GetXclDataType() );
+ }
+ return nScType;
+}
+
+void XclPCNumGroupInfo::SetScDateType( sal_Int32 nScType )
+{
+ sal_uInt16 nXclType = EXC_SXNUMGROUP_TYPE_NUM;
+ switch( nScType )
+ {
+ case ScDPGroupBy::SECONDS: nXclType = EXC_SXNUMGROUP_TYPE_SEC; break;
+ case ScDPGroupBy::MINUTES: nXclType = EXC_SXNUMGROUP_TYPE_MIN; break;
+ case ScDPGroupBy::HOURS: nXclType = EXC_SXNUMGROUP_TYPE_HOUR; break;
+ case ScDPGroupBy::DAYS: nXclType = EXC_SXNUMGROUP_TYPE_DAY; break;
+ case ScDPGroupBy::MONTHS: nXclType = EXC_SXNUMGROUP_TYPE_MONTH; break;
+ case ScDPGroupBy::QUARTERS: nXclType = EXC_SXNUMGROUP_TYPE_QUART; break;
+ case ScDPGroupBy::YEARS: nXclType = EXC_SXNUMGROUP_TYPE_YEAR; break;
+ default:
+ SAL_INFO("sc.filter", "unexpected date type " << nScType);
+ }
+ SetXclDataType( nXclType );
+}
+
+sal_uInt16 XclPCNumGroupInfo::GetXclDataType() const
+{
+ return ::extract_value< sal_uInt16 >( mnFlags, 2, 4 );
+}
+
+void XclPCNumGroupInfo::SetXclDataType( sal_uInt16 nXclType )
+{
+ ::insert_value( mnFlags, nXclType, 2, 4 );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCNumGroupInfo& rInfo )
+{
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCNumGroupInfo& rInfo )
+{
+ return rStrm << rInfo.mnFlags;
+}
+
+// Base class for pivot cache fields ==========================================
+
+XclPCField::XclPCField( XclPCFieldType eFieldType, sal_uInt16 nFieldIdx ) :
+ meFieldType( eFieldType ),
+ mnFieldIdx( nFieldIdx )
+{
+}
+
+XclPCField::~XclPCField()
+{
+}
+
+bool XclPCField::IsSupportedField() const
+{
+ return (meFieldType != EXC_PCFIELD_CALCED) && (meFieldType != EXC_PCFIELD_UNKNOWN);
+}
+
+bool XclPCField::IsStandardField() const
+{
+ return meFieldType == EXC_PCFIELD_STANDARD;
+}
+
+bool XclPCField::IsStdGroupField() const
+{
+ return meFieldType == EXC_PCFIELD_STDGROUP;
+}
+
+bool XclPCField::IsNumGroupField() const
+{
+ return meFieldType == EXC_PCFIELD_NUMGROUP;
+}
+
+bool XclPCField::IsDateGroupField() const
+{
+ return (meFieldType == EXC_PCFIELD_DATEGROUP) || (meFieldType == EXC_PCFIELD_DATECHILD);
+}
+
+bool XclPCField::IsGroupField() const
+{
+ return IsStdGroupField() || IsNumGroupField() || IsDateGroupField();
+}
+
+bool XclPCField::IsGroupBaseField() const
+{
+ return ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
+}
+
+bool XclPCField::IsGroupChildField() const
+{
+ return (meFieldType == EXC_PCFIELD_STDGROUP) || (meFieldType == EXC_PCFIELD_DATECHILD);
+}
+
+bool XclPCField::HasOrigItems() const
+{
+ return IsSupportedField() && ((maFieldInfo.mnOrigItems > 0) || HasPostponedItems());
+}
+
+bool XclPCField::HasInlineItems() const
+{
+ return (IsStandardField() || IsGroupField()) && ((maFieldInfo.mnGroupItems > 0) || (maFieldInfo.mnOrigItems > 0));
+}
+
+bool XclPCField::HasPostponedItems() const
+{
+ return IsStandardField() && ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_POSTPONE );
+}
+
+bool XclPCField::Has16BitIndexes() const
+{
+ return IsStandardField() && ::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT );
+}
+
+// Pivot cache settings =======================================================
+
+/** Contains data for a pivot cache (SXDB record). */
+XclPCInfo::XclPCInfo() :
+ mnSrcRecs( 0 ),
+ mnStrmId( 0xFFFF ),
+ mnFlags( EXC_SXDB_DEFAULTFLAGS ),
+ mnBlockRecs( EXC_SXDB_BLOCKRECS ),
+ mnStdFields( 0 ),
+ mnTotalFields( 0 ),
+ mnSrcType( EXC_SXDB_SRC_SHEET )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCInfo& rInfo )
+{
+ rInfo.mnSrcRecs = rStrm.ReaduInt32();
+ rInfo.mnStrmId = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnBlockRecs = rStrm.ReaduInt16();
+ rInfo.mnStdFields = rStrm.ReaduInt16();
+ rInfo.mnTotalFields = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ rInfo.mnSrcType = rStrm.ReaduInt16();
+ rInfo.maUserName = rStrm.ReadUniString();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnSrcRecs
+ << rInfo.mnStrmId
+ << rInfo.mnFlags
+ << rInfo.mnBlockRecs
+ << rInfo.mnStdFields
+ << rInfo.mnTotalFields
+ << sal_uInt16( 0 )
+ << rInfo.mnSrcType
+ << XclExpString( rInfo.maUserName );
+}
+
+// Pivot table
+
+// cached name ================================================================
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTCachedName& rCachedName )
+{
+ sal_uInt16 nStrLen;
+ nStrLen = rStrm.ReaduInt16();
+ rCachedName.mbUseCache = nStrLen == EXC_PT_NOSTRING;
+ if( rCachedName.mbUseCache )
+ rCachedName.maName.clear();
+ else
+ rCachedName.maName = rStrm.ReadUniString( nStrLen );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTCachedName& rCachedName )
+{
+ if( rCachedName.mbUseCache )
+ rStrm << EXC_PT_NOSTRING;
+ else
+ rStrm << XclExpString( rCachedName.maName, XclStrFlags::NONE, EXC_PT_MAXSTRLEN );
+ return rStrm;
+}
+
+const OUString* XclPTVisNameInfo::GetVisName() const
+{
+ return HasVisName() ? &maVisName.maName : nullptr;
+}
+
+void XclPTVisNameInfo::SetVisName( const OUString& rName )
+{
+ maVisName.maName = rName;
+ maVisName.mbUseCache = rName.isEmpty();
+}
+
+// Field item settings ========================================================
+
+XclPTItemInfo::XclPTItemInfo() :
+ mnType( EXC_SXVI_TYPE_DATA ),
+ mnFlags( EXC_SXVI_DEFAULTFLAGS ),
+ mnCacheIdx( EXC_SXVI_DEFAULT_CACHE )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTItemInfo& rInfo )
+{
+ rInfo.mnType = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnCacheIdx = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTItemInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnType
+ << rInfo.mnFlags
+ << rInfo.mnCacheIdx
+ << rInfo.maVisName;
+}
+
+// General field settings =====================================================
+
+XclPTFieldInfo::XclPTFieldInfo() :
+ mnAxes( EXC_SXVD_AXIS_NONE ),
+ mnSubtCount( 1 ),
+ mnSubtotals( EXC_SXVD_SUBT_DEFAULT ),
+ mnItemCount( 0 ),
+ mnCacheIdx( EXC_SXVD_DEFAULT_CACHE )
+{
+}
+
+DataPilotFieldOrientation XclPTFieldInfo::GetApiOrient( sal_uInt16 nMask ) const
+{
+ using namespace ::com::sun::star::sheet;
+ DataPilotFieldOrientation eOrient = DataPilotFieldOrientation_HIDDEN;
+ sal_uInt16 nUsedAxes = mnAxes & nMask;
+ if( nUsedAxes & EXC_SXVD_AXIS_ROW )
+ eOrient = DataPilotFieldOrientation_ROW;
+ else if( nUsedAxes & EXC_SXVD_AXIS_COL )
+ eOrient = DataPilotFieldOrientation_COLUMN;
+ else if( nUsedAxes & EXC_SXVD_AXIS_PAGE )
+ eOrient = DataPilotFieldOrientation_PAGE;
+ else if( nUsedAxes & EXC_SXVD_AXIS_DATA )
+ eOrient = DataPilotFieldOrientation_DATA;
+ return eOrient;
+}
+
+void XclPTFieldInfo::AddApiOrient( DataPilotFieldOrientation eOrient )
+{
+ using namespace ::com::sun::star::sheet;
+ switch( eOrient )
+ {
+ case DataPilotFieldOrientation_ROW: mnAxes |= EXC_SXVD_AXIS_ROW; break;
+ case DataPilotFieldOrientation_COLUMN: mnAxes |= EXC_SXVD_AXIS_COL; break;
+ case DataPilotFieldOrientation_PAGE: mnAxes |= EXC_SXVD_AXIS_PAGE; break;
+ case DataPilotFieldOrientation_DATA: mnAxes |= EXC_SXVD_AXIS_DATA; break;
+ default:;
+ }
+}
+
+//TODO: should be a Sequence<GeneralFunction> in ScDPSaveData
+void XclPTFieldInfo::GetSubtotals( XclPTSubtotalVec& rSubtotals ) const
+{
+ rSubtotals.clear();
+ rSubtotals.reserve( 16 );
+
+ if( mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) rSubtotals.push_back( ScGeneralFunction::AUTO );
+ if( mnSubtotals & EXC_SXVD_SUBT_SUM ) rSubtotals.push_back( ScGeneralFunction::SUM );
+ if( mnSubtotals & EXC_SXVD_SUBT_COUNT ) rSubtotals.push_back( ScGeneralFunction::COUNT );
+ if( mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) rSubtotals.push_back( ScGeneralFunction::AVERAGE );
+ if( mnSubtotals & EXC_SXVD_SUBT_MAX ) rSubtotals.push_back( ScGeneralFunction::MAX );
+ if( mnSubtotals & EXC_SXVD_SUBT_MIN ) rSubtotals.push_back( ScGeneralFunction::MIN );
+ if( mnSubtotals & EXC_SXVD_SUBT_PROD ) rSubtotals.push_back( ScGeneralFunction::PRODUCT );
+ if( mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) rSubtotals.push_back( ScGeneralFunction::COUNTNUMS );
+ if( mnSubtotals & EXC_SXVD_SUBT_STDDEV ) rSubtotals.push_back( ScGeneralFunction::STDEV );
+ if( mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) rSubtotals.push_back( ScGeneralFunction::STDEVP );
+ if( mnSubtotals & EXC_SXVD_SUBT_VAR ) rSubtotals.push_back( ScGeneralFunction::VAR );
+ if( mnSubtotals & EXC_SXVD_SUBT_VARP ) rSubtotals.push_back( ScGeneralFunction::VARP );
+}
+
+void XclPTFieldInfo::SetSubtotals( const XclPTSubtotalVec& rSubtotals )
+{
+ mnSubtotals = EXC_SXVD_SUBT_NONE;
+ for( const auto& rSubtotal : rSubtotals )
+ {
+ switch( rSubtotal )
+ {
+ case ScGeneralFunction::AUTO: mnSubtotals |= EXC_SXVD_SUBT_DEFAULT; break;
+ case ScGeneralFunction::SUM: mnSubtotals |= EXC_SXVD_SUBT_SUM; break;
+ case ScGeneralFunction::COUNT: mnSubtotals |= EXC_SXVD_SUBT_COUNT; break;
+ case ScGeneralFunction::AVERAGE: mnSubtotals |= EXC_SXVD_SUBT_AVERAGE; break;
+ case ScGeneralFunction::MAX: mnSubtotals |= EXC_SXVD_SUBT_MAX; break;
+ case ScGeneralFunction::MIN: mnSubtotals |= EXC_SXVD_SUBT_MIN; break;
+ case ScGeneralFunction::PRODUCT: mnSubtotals |= EXC_SXVD_SUBT_PROD; break;
+ case ScGeneralFunction::COUNTNUMS: mnSubtotals |= EXC_SXVD_SUBT_COUNTNUM; break;
+ case ScGeneralFunction::STDEV: mnSubtotals |= EXC_SXVD_SUBT_STDDEV; break;
+ case ScGeneralFunction::STDEVP: mnSubtotals |= EXC_SXVD_SUBT_STDDEVP; break;
+ case ScGeneralFunction::VAR: mnSubtotals |= EXC_SXVD_SUBT_VAR; break;
+ case ScGeneralFunction::VARP: mnSubtotals |= EXC_SXVD_SUBT_VARP; break;
+ default: break;
+ }
+ }
+
+ mnSubtCount = 0;
+ for( sal_uInt16 nMask = 0x8000; nMask; nMask >>= 1 )
+ if( mnSubtotals & nMask )
+ ++mnSubtCount;
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldInfo& rInfo )
+{
+ // rInfo.mnCacheIdx is not part of the SXVD record
+ rInfo.mnAxes = rStrm.ReaduInt16();
+ rInfo.mnSubtCount = rStrm.ReaduInt16();
+ rInfo.mnSubtotals = rStrm.ReaduInt16();
+ rInfo.mnItemCount = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldInfo& rInfo )
+{
+ // rInfo.mnCacheIdx is not part of the SXVD record
+ return rStrm
+ << rInfo.mnAxes
+ << rInfo.mnSubtCount
+ << rInfo.mnSubtotals
+ << rInfo.mnItemCount
+ << rInfo.maVisName;
+}
+
+// Extended field settings ====================================================
+
+XclPTFieldExtInfo::XclPTFieldExtInfo() :
+ mnFlags( EXC_SXVDEX_DEFAULTFLAGS ),
+ mnSortField( EXC_SXVDEX_SORT_OWN ),
+ mnShowField( EXC_SXVDEX_SHOW_NONE ),
+ mnNumFmt(0)
+{
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiSortMode() const
+{
+ sal_Int32 nSortMode = ScDPSortMode::MANUAL;
+ if( ::get_flag( mnFlags, EXC_SXVDEX_SORT ) )
+ nSortMode = (mnSortField == EXC_SXVDEX_SORT_OWN) ? ScDPSortMode::NAME : ScDPSortMode::DATA;
+ return nSortMode;
+}
+
+void XclPTFieldExtInfo::SetApiSortMode( sal_Int32 nSortMode )
+{
+ bool bSort = (nSortMode == ScDPSortMode::NAME) || (nSortMode == ScDPSortMode::DATA);
+ ::set_flag( mnFlags, EXC_SXVDEX_SORT, bSort );
+ if( nSortMode == ScDPSortMode::NAME )
+ mnSortField = EXC_SXVDEX_SORT_OWN; // otherwise sort field has to be set by caller
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiAutoShowMode() const
+{
+ return ::get_flagvalue( mnFlags, EXC_SXVDEX_AUTOSHOW_ASC,
+ ScDPShowItemsMode::FROM_TOP, ScDPShowItemsMode::FROM_BOTTOM );
+}
+
+void XclPTFieldExtInfo::SetApiAutoShowMode( sal_Int32 nShowMode )
+{
+ ::set_flag( mnFlags, EXC_SXVDEX_AUTOSHOW_ASC, nShowMode == ScDPShowItemsMode::FROM_TOP );
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiAutoShowCount() const
+{
+ return ::extract_value< sal_Int32 >( mnFlags, 24, 8 );
+}
+
+void XclPTFieldExtInfo::SetApiAutoShowCount( sal_Int32 nShowCount )
+{
+ ::insert_value( mnFlags, limit_cast< sal_uInt8 >( nShowCount ), 24, 8 );
+}
+
+sal_Int32 XclPTFieldExtInfo::GetApiLayoutMode() const
+{
+ sal_Int32 nLayoutMode = ScDPLayoutMode::TABULAR_LAYOUT;
+ if( ::get_flag( mnFlags, EXC_SXVDEX_LAYOUT_REPORT ) )
+ nLayoutMode = ::get_flag( mnFlags, EXC_SXVDEX_LAYOUT_TOP ) ?
+ ScDPLayoutMode::OUTLINE_SUBTOTALS_TOP : ScDPLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ return nLayoutMode;
+}
+
+void XclPTFieldExtInfo::SetApiLayoutMode( sal_Int32 nLayoutMode )
+{
+ ::set_flag( mnFlags, EXC_SXVDEX_LAYOUT_REPORT, nLayoutMode != ScDPLayoutMode::TABULAR_LAYOUT );
+ ::set_flag( mnFlags, EXC_SXVDEX_LAYOUT_TOP, nLayoutMode == ScDPLayoutMode::OUTLINE_SUBTOTALS_TOP );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldExtInfo& rInfo )
+{
+ sal_uInt8 nNameLen = 0;
+ rInfo.mnFlags = rStrm.ReaduInt32();
+ rInfo.mnSortField = rStrm.ReaduInt16();
+ rInfo.mnShowField = rStrm.ReaduInt16();
+ rInfo.mnNumFmt = rStrm.ReaduInt16();
+ nNameLen = rStrm.ReaduInt8();
+
+ rStrm.Ignore(10);
+ if (nNameLen != 0xFF)
+ // Custom field total name is used. Pick it up.
+ rInfo.mpFieldTotalName = rStrm.ReadUniString(nNameLen, 0);
+
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldExtInfo& rInfo )
+{
+ rStrm << rInfo.mnFlags
+ << rInfo.mnSortField
+ << rInfo.mnShowField
+ << EXC_SXVDEX_FORMAT_NONE;
+
+ if (rInfo.mpFieldTotalName && !rInfo.mpFieldTotalName->isEmpty())
+ {
+ OUString aFinalName = *rInfo.mpFieldTotalName;
+ if (aFinalName.getLength() >= 254)
+ aFinalName = aFinalName.copy(0, 254);
+ sal_uInt8 nNameLen = static_cast<sal_uInt8>(aFinalName.getLength());
+ rStrm << nNameLen;
+ rStrm.WriteZeroBytes(10);
+ rStrm << XclExpString(aFinalName, XclStrFlags::NoHeader);
+ }
+ else
+ {
+ rStrm << sal_uInt16(0xFFFF);
+ rStrm.WriteZeroBytes(8);
+ }
+ return rStrm;
+}
+
+// Page field settings ========================================================
+
+XclPTPageFieldInfo::XclPTPageFieldInfo() :
+ mnField( 0 ),
+ mnSelItem( EXC_SXPI_ALLITEMS ),
+ mnObjId( 0xFFFF )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTPageFieldInfo& rInfo )
+{
+ rInfo.mnField = rStrm.ReaduInt16();
+ rInfo.mnSelItem = rStrm.ReaduInt16();
+ rInfo.mnObjId = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTPageFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnField
+ << rInfo.mnSelItem
+ << rInfo.mnObjId;
+}
+
+// Data field settings ========================================================
+
+XclPTDataFieldInfo::XclPTDataFieldInfo() :
+ mnField( 0 ),
+ mnAggFunc( EXC_SXDI_FUNC_SUM ),
+ mnRefType( EXC_SXDI_REF_NORMAL ),
+ mnRefField( 0 ),
+ mnRefItem( 0 ),
+ mnNumFmt( 0 )
+{
+}
+
+ScGeneralFunction XclPTDataFieldInfo::GetApiAggFunc() const
+{
+ ScGeneralFunction eAggFunc;
+ switch( mnAggFunc )
+ {
+ case EXC_SXDI_FUNC_SUM: eAggFunc = ScGeneralFunction::SUM; break;
+ case EXC_SXDI_FUNC_COUNT: eAggFunc = ScGeneralFunction::COUNT; break;
+ case EXC_SXDI_FUNC_AVERAGE: eAggFunc = ScGeneralFunction::AVERAGE; break;
+ case EXC_SXDI_FUNC_MAX: eAggFunc = ScGeneralFunction::MAX; break;
+ case EXC_SXDI_FUNC_MIN: eAggFunc = ScGeneralFunction::MIN; break;
+ case EXC_SXDI_FUNC_PRODUCT: eAggFunc = ScGeneralFunction::PRODUCT; break;
+ case EXC_SXDI_FUNC_COUNTNUM: eAggFunc = ScGeneralFunction::COUNTNUMS; break;
+ case EXC_SXDI_FUNC_STDDEV: eAggFunc = ScGeneralFunction::STDEV; break;
+ case EXC_SXDI_FUNC_STDDEVP: eAggFunc = ScGeneralFunction::STDEVP; break;
+ case EXC_SXDI_FUNC_VAR: eAggFunc = ScGeneralFunction::VAR; break;
+ case EXC_SXDI_FUNC_VARP: eAggFunc = ScGeneralFunction::VARP; break;
+ default: eAggFunc = ScGeneralFunction::SUM;
+ }
+ return eAggFunc;
+}
+
+void XclPTDataFieldInfo::SetApiAggFunc( ScGeneralFunction eAggFunc )
+{
+ switch( eAggFunc )
+ {
+ case ScGeneralFunction::SUM: mnAggFunc = EXC_SXDI_FUNC_SUM; break;
+ case ScGeneralFunction::COUNT: mnAggFunc = EXC_SXDI_FUNC_COUNT; break;
+ case ScGeneralFunction::AVERAGE: mnAggFunc = EXC_SXDI_FUNC_AVERAGE; break;
+ case ScGeneralFunction::MAX: mnAggFunc = EXC_SXDI_FUNC_MAX; break;
+ case ScGeneralFunction::MIN: mnAggFunc = EXC_SXDI_FUNC_MIN; break;
+ case ScGeneralFunction::PRODUCT: mnAggFunc = EXC_SXDI_FUNC_PRODUCT; break;
+ case ScGeneralFunction::COUNTNUMS: mnAggFunc = EXC_SXDI_FUNC_COUNTNUM; break;
+ case ScGeneralFunction::STDEV: mnAggFunc = EXC_SXDI_FUNC_STDDEV; break;
+ case ScGeneralFunction::STDEVP: mnAggFunc = EXC_SXDI_FUNC_STDDEVP; break;
+ case ScGeneralFunction::VAR: mnAggFunc = EXC_SXDI_FUNC_VAR; break;
+ case ScGeneralFunction::VARP: mnAggFunc = EXC_SXDI_FUNC_VARP; break;
+ default: mnAggFunc = EXC_SXDI_FUNC_SUM;
+ }
+}
+
+sal_Int32 XclPTDataFieldInfo::GetApiRefType() const
+{
+ namespace ScDPRefType = ::com::sun::star::sheet::DataPilotFieldReferenceType;
+ sal_Int32 nRefType;
+ switch( mnRefType )
+ {
+ case EXC_SXDI_REF_DIFF: nRefType = ScDPRefType::ITEM_DIFFERENCE; break;
+ case EXC_SXDI_REF_PERC: nRefType = ScDPRefType::ITEM_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_DIFF: nRefType = ScDPRefType::ITEM_PERCENTAGE_DIFFERENCE; break;
+ case EXC_SXDI_REF_RUN_TOTAL: nRefType = ScDPRefType::RUNNING_TOTAL; break;
+ case EXC_SXDI_REF_PERC_ROW: nRefType = ScDPRefType::ROW_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_COL: nRefType = ScDPRefType::COLUMN_PERCENTAGE; break;
+ case EXC_SXDI_REF_PERC_TOTAL: nRefType = ScDPRefType::TOTAL_PERCENTAGE; break;
+ case EXC_SXDI_REF_INDEX: nRefType = ScDPRefType::INDEX; break;
+ default: nRefType = ScDPRefType::NONE;
+ }
+ return nRefType;
+}
+
+void XclPTDataFieldInfo::SetApiRefType( sal_Int32 nRefType )
+{
+ namespace ScDPRefType = ::com::sun::star::sheet::DataPilotFieldReferenceType;
+ switch( nRefType )
+ {
+ case ScDPRefType::ITEM_DIFFERENCE: mnRefType = EXC_SXDI_REF_DIFF; break;
+ case ScDPRefType::ITEM_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC; break;
+ case ScDPRefType::ITEM_PERCENTAGE_DIFFERENCE: mnRefType = EXC_SXDI_REF_PERC_DIFF; break;
+ case ScDPRefType::RUNNING_TOTAL: mnRefType = EXC_SXDI_REF_RUN_TOTAL; break;
+ case ScDPRefType::ROW_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_ROW; break;
+ case ScDPRefType::COLUMN_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_COL; break;
+ case ScDPRefType::TOTAL_PERCENTAGE: mnRefType = EXC_SXDI_REF_PERC_TOTAL;break;
+ case ScDPRefType::INDEX: mnRefType = EXC_SXDI_REF_INDEX; break;
+ default: mnRefType = EXC_SXDI_REF_NORMAL;
+ }
+}
+
+sal_Int32 XclPTDataFieldInfo::GetApiRefItemType() const
+{
+ sal_Int32 nRefItemType;
+ switch( mnRefItem )
+ {
+ case EXC_SXDI_PREVITEM: nRefItemType = ScDPRefItemType::PREVIOUS; break;
+ case EXC_SXDI_NEXTITEM: nRefItemType = ScDPRefItemType::NEXT; break;
+ default: nRefItemType = ScDPRefItemType::NAMED;
+ }
+ return nRefItemType;
+}
+
+void XclPTDataFieldInfo::SetApiRefItemType( sal_Int32 nRefItemType )
+{
+ switch( nRefItemType )
+ {
+ case ScDPRefItemType::PREVIOUS: mnRefItem = EXC_SXDI_PREVITEM; break;
+ case ScDPRefItemType::NEXT: mnRefItem = EXC_SXDI_NEXTITEM; break;
+ // nothing for named item reference
+ }
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTDataFieldInfo& rInfo )
+{
+ rInfo.mnField = rStrm.ReaduInt16();
+ rInfo.mnAggFunc = rStrm.ReaduInt16();
+ rInfo.mnRefType = rStrm.ReaduInt16();
+ rInfo.mnRefField = rStrm.ReaduInt16();
+ rInfo.mnRefItem = rStrm.ReaduInt16();
+ rInfo.mnNumFmt = rStrm.ReaduInt16();
+ rStrm >> rInfo.maVisName;
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTDataFieldInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnField
+ << rInfo.mnAggFunc
+ << rInfo.mnRefType
+ << rInfo.mnRefField
+ << rInfo.mnRefItem
+ << rInfo.mnNumFmt
+ << rInfo.maVisName;
+}
+
+// Pivot table settings =======================================================
+
+XclPTInfo::XclPTInfo() :
+ mnFirstHeadRow( 0 ),
+ mnCacheIdx( 0xFFFF ),
+ mnDataAxis( EXC_SXVD_AXIS_NONE ),
+ mnDataPos( EXC_SXVIEW_DATALAST ),
+ mnFields( 0 ),
+ mnRowFields( 0 ),
+ mnColFields( 0 ),
+ mnPageFields( 0 ),
+ mnDataFields( 0 ),
+ mnDataRows( 0 ),
+ mnDataCols( 0 ),
+ mnFlags( EXC_SXVIEW_DEFAULTFLAGS ),
+ mnAutoFmtIdx( EXC_SXVIEW_AUTOFMT )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTInfo& rInfo )
+{
+ sal_uInt16 nTabLen, nDataLen;
+
+ rStrm >> rInfo.maOutXclRange;
+ rInfo.mnFirstHeadRow = rStrm.ReaduInt16();
+ rStrm >> rInfo.maDataXclPos;
+ rInfo.mnCacheIdx = rStrm.ReaduInt16();
+ rStrm.Ignore( 2 );
+ rInfo.mnDataAxis = rStrm.ReaduInt16();
+ rInfo.mnDataPos = rStrm.ReaduInt16();
+ rInfo.mnFields = rStrm.ReaduInt16();
+ rInfo.mnRowFields = rStrm.ReaduInt16();
+ rInfo.mnColFields = rStrm.ReaduInt16();
+ rInfo.mnPageFields = rStrm.ReaduInt16();
+ rInfo.mnDataFields = rStrm.ReaduInt16();
+ rInfo.mnDataRows = rStrm.ReaduInt16();
+ rInfo.mnDataCols = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt16();
+ rInfo.mnAutoFmtIdx = rStrm.ReaduInt16();
+ nTabLen = rStrm.ReaduInt16();
+ nDataLen = rStrm.ReaduInt16();
+ rInfo.maTableName = rStrm.ReadUniString( nTabLen );
+ rInfo.maDataName = rStrm.ReadUniString( nDataLen );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTInfo& rInfo )
+{
+ XclExpString aXclTableName( rInfo.maTableName );
+ XclExpString aXclDataName( rInfo.maDataName );
+
+ rStrm << rInfo.maOutXclRange
+ << rInfo.mnFirstHeadRow
+ << rInfo.maDataXclPos
+ << rInfo.mnCacheIdx
+ << sal_uInt16( 0 )
+ << rInfo.mnDataAxis << rInfo.mnDataPos
+ << rInfo.mnFields
+ << rInfo.mnRowFields << rInfo.mnColFields
+ << rInfo.mnPageFields << rInfo.mnDataFields
+ << rInfo.mnDataRows << rInfo.mnDataCols
+ << rInfo.mnFlags
+ << rInfo.mnAutoFmtIdx
+ << aXclTableName.Len() << aXclDataName.Len();
+ aXclTableName.WriteFlagField( rStrm );
+ aXclTableName.WriteBuffer( rStrm );
+ aXclDataName.WriteFlagField( rStrm );
+ aXclDataName.WriteBuffer( rStrm );
+ return rStrm;
+}
+
+// Extended pivot table settings ==============================================
+
+XclPTExtInfo::XclPTExtInfo() :
+ mnSxformulaRecs( 0 ),
+ mnSxselectRecs( 0 ),
+ mnPagePerRow( 0 ),
+ mnPagePerCol( 0 ),
+ mnFlags( EXC_SXEX_DEFAULTFLAGS )
+{
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTExtInfo& rInfo )
+{
+ rInfo.mnSxformulaRecs = rStrm.ReaduInt16();
+ rStrm.Ignore( 6 );
+ rInfo.mnSxselectRecs = rStrm.ReaduInt16();
+ rInfo.mnPagePerRow = rStrm.ReaduInt16();
+ rInfo.mnPagePerCol = rStrm.ReaduInt16();
+ rInfo.mnFlags = rStrm.ReaduInt32();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTExtInfo& rInfo )
+{
+ return rStrm
+ << rInfo.mnSxformulaRecs
+ << EXC_PT_NOSTRING // length of alt. error text
+ << EXC_PT_NOSTRING // length of alt. empty text
+ << EXC_PT_NOSTRING // length of tag
+ << rInfo.mnSxselectRecs
+ << rInfo.mnPagePerRow
+ << rInfo.mnPagePerCol
+ << rInfo.mnFlags
+ << EXC_PT_NOSTRING // length of page field style name
+ << EXC_PT_NOSTRING // length of table style name
+ << EXC_PT_NOSTRING; // length of vacate style name
+}
+
+// Pivot table autoformat settings ============================================
+
+/**
+classic : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
+default : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
+report01 : 10 08 02 00 00 00 00 00 20 00 00 00 00 10 00 00 00
+report02 : 10 08 02 00 00 00 00 00 20 00 00 00 01 10 00 00 00
+report03 : 10 08 02 00 00 00 00 00 20 00 00 00 02 10 00 00 00
+report04 : 10 08 02 00 00 00 00 00 20 00 00 00 03 10 00 00 00
+report05 : 10 08 02 00 00 00 00 00 20 00 00 00 04 10 00 00 00
+report06 : 10 08 02 00 00 00 00 00 20 00 00 00 05 10 00 00 00
+report07 : 10 08 02 00 00 00 00 00 20 00 00 00 06 10 00 00 00
+report08 : 10 08 02 00 00 00 00 00 20 00 00 00 07 10 00 00 00
+report09 : 10 08 02 00 00 00 00 00 20 00 00 00 08 10 00 00 00
+report10 : 10 08 02 00 00 00 00 00 20 00 00 00 09 10 00 00 00
+table01 : 10 08 00 00 00 00 00 00 20 00 00 00 0a 10 00 00 00
+table02 : 10 08 00 00 00 00 00 00 20 00 00 00 0b 10 00 00 00
+table03 : 10 08 00 00 00 00 00 00 20 00 00 00 0c 10 00 00 00
+table04 : 10 08 00 00 00 00 00 00 20 00 00 00 0d 10 00 00 00
+table05 : 10 08 00 00 00 00 00 00 20 00 00 00 0e 10 00 00 00
+table06 : 10 08 00 00 00 00 00 00 20 00 00 00 0f 10 00 00 00
+table07 : 10 08 00 00 00 00 00 00 20 00 00 00 10 10 00 00 00
+table08 : 10 08 00 00 00 00 00 00 20 00 00 00 11 10 00 00 00
+table09 : 10 08 00 00 00 00 00 00 20 00 00 00 12 10 00 00 00
+table10 : 10 08 00 00 00 00 00 00 20 00 00 00 13 10 00 00 00
+none : 10 08 00 00 00 00 00 00 20 00 00 00 15 10 00 00 00
+**/
+
+XclPTViewEx9Info::XclPTViewEx9Info() :
+ mbReport( 0 ),
+ mnAutoFormat( 0 ),
+ mnGridLayout( 0x10 )
+{
+}
+
+void XclPTViewEx9Info::Init( const ScDPObject& rDPObj )
+{
+ if( rDPObj.GetHeaderLayout() )
+ {
+ mbReport = 0;
+ mnAutoFormat = 1;
+ mnGridLayout = 0;
+ }
+ else
+ {
+ // Report1 for now
+ // TODO : sync with autoformat indices
+ mbReport = 2;
+ mnAutoFormat = 1;
+ mnGridLayout = 0x10;
+ }
+
+ const ScDPSaveData* pData = rDPObj.GetSaveData();
+ if (pData)
+ {
+ const std::optional<OUString> & pGrandTotal = pData->GetGrandTotalName();
+ if (pGrandTotal)
+ maGrandTotalName = *pGrandTotal;
+ }
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTViewEx9Info& rInfo )
+{
+ rStrm.Ignore( 2 );
+ rInfo.mbReport = rStrm.ReaduInt32(); /// 2 for report* fmts ?
+ rStrm.Ignore( 6 );
+ rInfo.mnAutoFormat = rStrm.ReaduInt8();
+ rInfo.mnGridLayout = rStrm.ReaduInt8();
+ rInfo.maGrandTotalName = rStrm.ReadUniString();
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTViewEx9Info& rInfo )
+{
+ return rStrm
+ << EXC_PT_AUTOFMT_HEADER
+ << rInfo.mbReport
+ << EXC_PT_AUTOFMT_ZERO
+ << EXC_PT_AUTOFMT_FLAGS
+ << rInfo.mnAutoFormat
+ << rInfo.mnGridLayout
+ << XclExpString(rInfo.maGrandTotalName, XclStrFlags::NONE, EXC_PT_MAXSTRLEN);
+}
+
+XclPTAddl::XclPTAddl() :
+ mbCompactMode(false)
+{
+}
+
+XclImpStream& operator>>(XclImpStream& rStrm, XclPTAddl& rInfo)
+{
+ rStrm.Ignore(4);
+ sal_uInt8 sxc = rStrm.ReaduInt8();
+ sal_uInt8 sxd = rStrm.ReaduInt8();
+ if(sxc == 0x00 && sxd == 0x19) // SxcView / sxdVer12Info
+ {
+ sal_uInt32 nFlags = rStrm.ReaduInt32();
+ rInfo.mbCompactMode = ((nFlags & 0x00000008) != 0);
+ }
+ return rStrm;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlroot.cxx b/sc/source/filter/excel/xlroot.cxx
new file mode 100644
index 000000000..bac3ca1b3
--- /dev/null
+++ b/sc/source/filter/excel/xlroot.cxx
@@ -0,0 +1,438 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <xlroot.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <sot/storage.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/numformat.hxx>
+#include <svl/stritem.hxx>
+#include <svl/languageoptions.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <vcl/font.hxx>
+#include <vcl/settings.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <editeng/editstat.hxx>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <docuno.hxx>
+#include <editutil.hxx>
+#include <drwlayer.hxx>
+#include <scextopt.hxx>
+#include <patattr.hxx>
+#include <fapihelper.hxx>
+#include <xlconst.hxx>
+#include <xlstyle.hxx>
+#include <xlchart.hxx>
+#include <xltracer.hxx>
+#include <xltools.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/useroptions.hxx>
+#include <root.hxx>
+
+namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::UNO_SET_THROW;
+using ::com::sun::star::awt::XDevice;
+using ::com::sun::star::awt::DeviceInfo;
+using ::com::sun::star::frame::XFrame;
+
+using namespace ::com::sun::star;
+
+// Global data ================================================================
+
+#ifdef DBG_UTIL
+XclDebugObjCounter::~XclDebugObjCounter()
+{
+ OSL_ENSURE( mnObjCnt == 0, "XclDebugObjCounter::~XclDebugObjCounter - wrong root object count" );
+}
+#endif
+
+XclRootData::XclRootData( XclBiff eBiff, SfxMedium& rMedium,
+ tools::SvRef<SotStorage> const & xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc, bool bExport ) :
+ meBiff( eBiff ),
+ meOutput( EXC_OUTPUT_BINARY ),
+ mrMedium( rMedium ),
+ mxRootStrg( xRootStrg ),
+ mrDoc( rDoc ),
+ meTextEnc( eTextEnc ),
+ meSysLang( Application::GetSettings().GetLanguageTag().getLanguageType() ),
+ meDocLang( Application::GetSettings().GetLanguageTag().getLanguageType() ),
+ meUILang( Application::GetSettings().GetUILanguageTag().getLanguageType() ),
+ mnDefApiScript( ApiScriptType::LATIN ),
+ maScMaxPos( mrDoc.MaxCol(), mrDoc.MaxRow(), MAXTAB ),
+ maXclMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ),
+ maMaxPos( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ),
+ mxFontPropSetHlp( std::make_shared<XclFontPropSetHelper>() ),
+ mxChPropSetHlp( std::make_shared<XclChPropSetHelper>() ),
+ mxRD( std::make_shared<RootData>() ),
+ mfScreenPixelX( 50.0 ),
+ mfScreenPixelY( 50.0 ),
+ mnCharWidth( 110 ),
+ mnSpaceWidth(45),
+ mnScTab( 0 ),
+ mbExport( bExport )
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ maUserName = SvtUserOptions().GetLastName();
+ if (maUserName.isEmpty())
+ maUserName = "Calc";
+
+ switch( ScGlobal::GetDefaultScriptType() )
+ {
+ case SvtScriptType::LATIN: mnDefApiScript = ApiScriptType::LATIN; break;
+ case SvtScriptType::ASIAN: mnDefApiScript = ApiScriptType::ASIAN; break;
+ case SvtScriptType::COMPLEX: mnDefApiScript = ApiScriptType::COMPLEX; break;
+ default: SAL_WARN( "sc", "XclRootData::XclRootData - unknown script type" );
+ }
+
+ // maximum cell position
+ switch( meBiff )
+ {
+ case EXC_BIFF2: maXclMaxPos.Set( EXC_MAXCOL2, EXC_MAXROW2, EXC_MAXTAB2 ); break;
+ case EXC_BIFF3: maXclMaxPos.Set( EXC_MAXCOL3, EXC_MAXROW3, EXC_MAXTAB3 ); break;
+ case EXC_BIFF4: maXclMaxPos.Set( EXC_MAXCOL4, EXC_MAXROW4, EXC_MAXTAB4 ); break;
+ case EXC_BIFF5: maXclMaxPos.Set( EXC_MAXCOL5, EXC_MAXROW5, EXC_MAXTAB5 ); break;
+ case EXC_BIFF8: maXclMaxPos.Set( EXC_MAXCOL8, EXC_MAXROW8, EXC_MAXTAB8 ); break;
+ default: DBG_ERROR_BIFF();
+ }
+ maMaxPos.SetCol( ::std::min( maScMaxPos.Col(), maXclMaxPos.Col() ) );
+ maMaxPos.SetRow( ::std::min( maScMaxPos.Row(), maXclMaxPos.Row() ) );
+ maMaxPos.SetTab( ::std::min( maScMaxPos.Tab(), maXclMaxPos.Tab() ) );
+
+ // document URL and path
+ if( const SfxItemSet* pItemSet = mrMedium.GetItemSet() )
+ if( const SfxStringItem* pItem = pItemSet->GetItem<SfxStringItem>( SID_FILE_NAME ) )
+ maDocUrl = pItem->GetValue();
+ maBasePath = maDocUrl.copy( 0, maDocUrl.lastIndexOf( '/' ) + 1 );
+
+ // extended document options - always own object, try to copy existing data from document
+ if( const ScExtDocOptions* pOldDocOpt = mrDoc.GetExtDocOptions() )
+ mxExtDocOpt = std::make_shared<ScExtDocOptions>( *pOldDocOpt );
+ else
+ mxExtDocOpt = std::make_shared<ScExtDocOptions>();
+
+ // screen pixel size
+ try
+ {
+ Reference< frame::XDesktop2 > xFramesSupp = frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XFrame > xFrame( xFramesSupp->getActiveFrame(), UNO_SET_THROW );
+ Reference< XDevice > xDevice( xFrame->getContainerWindow(), UNO_QUERY_THROW );
+ DeviceInfo aDeviceInfo = xDevice->getInfo();
+ mfScreenPixelX = (aDeviceInfo.PixelPerMeterX > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterX) : 50.0;
+ mfScreenPixelY = (aDeviceInfo.PixelPerMeterY > 0) ? (100000.0 / aDeviceInfo.PixelPerMeterY) : 50.0;
+ }
+ catch( const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "XclRootData::XclRootData - cannot get output device info");
+ }
+}
+
+XclRootData::~XclRootData()
+{
+}
+
+XclRoot::XclRoot( XclRootData& rRootData ) :
+ mrData( rRootData )
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ ++mrData.mnObjCnt;
+#endif
+
+ // filter tracer
+ mrData.mxTracer = std::make_shared<XclTracer>( GetDocUrl() );
+}
+
+XclRoot::XclRoot( const XclRoot& rRoot ) :
+ mrData( rRoot.mrData )
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ ++mrData.mnObjCnt;
+#endif
+}
+
+XclRoot::~XclRoot()
+{
+#if defined(DBG_UTIL) && OSL_DEBUG_LEVEL > 0
+ --mrData.mnObjCnt;
+#endif
+}
+
+XclRoot& XclRoot::operator=( const XclRoot& rRoot )
+{
+ (void)rRoot; // avoid compiler warning
+ // allowed for assignment in derived classes - but test if the same root data is used
+ OSL_ENSURE( &mrData == &rRoot.mrData, "XclRoot::operator= - incompatible root data" );
+ return *this;
+}
+
+void XclRoot::SetTextEncoding( rtl_TextEncoding eTextEnc )
+{
+ if( eTextEnc != RTL_TEXTENCODING_DONTKNOW )
+ mrData.meTextEnc = eTextEnc;
+}
+
+void XclRoot::SetCharWidth( const XclFontData& rFontData )
+{
+ mrData.mnCharWidth = 0;
+ if( OutputDevice* pPrinter = GetPrinter() )
+ {
+ vcl::Font aFont( rFontData.maName, Size( 0, rFontData.mnHeight ) );
+ aFont.SetFamily( rFontData.GetScFamily( GetTextEncoding() ) );
+ aFont.SetCharSet( rFontData.GetFontEncoding() );
+ aFont.SetWeight( rFontData.GetScWeight() );
+ pPrinter->SetFont( aFont );
+ // Usually digits have the same width, but in some fonts they don't ...
+ // Match the import in sc/source/filter/oox/unitconverter.cxx
+ // UnitConverter::finalizeImport()
+ for (sal_Unicode cChar = '0'; cChar <= '9'; ++cChar)
+ mrData.mnCharWidth = std::max( pPrinter->GetTextWidth( OUString(cChar)), mrData.mnCharWidth);
+
+ // Set the width of space ' ' character.
+ mrData.mnSpaceWidth = pPrinter->GetTextWidth(OUString(' '));
+ }
+ if( mrData.mnCharWidth <= 0 )
+ {
+ // #i48717# Win98 with HP LaserJet returns 0
+ SAL_WARN( "sc", "XclRoot::SetCharWidth - invalid character width (no printer?)" );
+ mrData.mnCharWidth = 11 * rFontData.mnHeight / 20;
+ }
+ if (mrData.mnSpaceWidth <= 0)
+ {
+ SAL_WARN( "sc", "XclRoot::SetCharWidth - invalid character width (no printer?)" );
+ mrData.mnSpaceWidth = 45;
+ }
+}
+
+sal_Int32 XclRoot::GetHmmFromPixelX( double fPixelX ) const
+{
+ return static_cast< sal_Int32 >( fPixelX * mrData.mfScreenPixelX + 0.5 );
+}
+
+sal_Int32 XclRoot::GetHmmFromPixelY( double fPixelY ) const
+{
+ return static_cast< sal_Int32 >( fPixelY * mrData.mfScreenPixelY + 0.5 );
+}
+
+uno::Sequence< beans::NamedValue > XclRoot::RequestEncryptionData( ::comphelper::IDocPasswordVerifier& rVerifier ) const
+{
+ ::std::vector< OUString > aDefaultPasswords { XclRootData::gaDefPassword };
+ return ScfApiHelper::QueryEncryptionDataForMedium( mrData.mrMedium, rVerifier, &aDefaultPasswords );
+}
+
+bool XclRoot::HasVbaStorage() const
+{
+ tools::SvRef<SotStorage> xRootStrg = GetRootStorage();
+ return xRootStrg.is() && xRootStrg->IsContained( EXC_STORAGE_VBA_PROJECT );
+}
+
+tools::SvRef<SotStorage> XclRoot::OpenStorage( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName ) const
+{
+ return mrData.mbExport ?
+ ScfTools::OpenStorageWrite( xStrg, rStrgName ) :
+ ScfTools::OpenStorageRead( xStrg, rStrgName );
+}
+
+tools::SvRef<SotStorage> XclRoot::OpenStorage( const OUString& rStrgName ) const
+{
+ return OpenStorage( GetRootStorage(), rStrgName );
+}
+
+tools::SvRef<SotStorageStream> XclRoot::OpenStream( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName ) const
+{
+ return mrData.mbExport ?
+ ScfTools::OpenStorageStreamWrite( xStrg, rStrmName ) :
+ ScfTools::OpenStorageStreamRead( xStrg, rStrmName );
+}
+
+tools::SvRef<SotStorageStream> XclRoot::OpenStream( const OUString& rStrmName ) const
+{
+ return OpenStream( GetRootStorage(), rStrmName );
+}
+
+ScDocument& XclRoot::GetDoc() const
+{
+ return mrData.mrDoc;
+}
+
+SfxObjectShell* XclRoot::GetDocShell() const
+{
+ return GetDoc().GetDocumentShell();
+}
+
+ScModelObj* XclRoot::GetDocModelObj() const
+{
+ SfxObjectShell* pDocShell = GetDocShell();
+ return pDocShell ? comphelper::getFromUnoTunnel<ScModelObj>( pDocShell->GetModel() ) : nullptr;
+}
+
+OutputDevice* XclRoot::GetPrinter() const
+{
+ return GetDoc().GetRefDevice();
+}
+
+ScStyleSheetPool& XclRoot::GetStyleSheetPool() const
+{
+ return *GetDoc().GetStyleSheetPool();
+}
+
+ScRangeName& XclRoot::GetNamedRanges() const
+{
+ return *GetDoc().GetRangeName();
+}
+
+SdrPage* XclRoot::GetSdrPage( SCTAB nScTab ) const
+{
+ return ((nScTab >= 0) && GetDoc().GetDrawLayer()) ?
+ GetDoc().GetDrawLayer()->GetPage( static_cast< sal_uInt16 >( nScTab ) ) : nullptr;
+}
+
+SvNumberFormatter& XclRoot::GetFormatter() const
+{
+ return *GetDoc().GetFormatTable();
+}
+
+DateTime XclRoot::GetNullDate() const
+{
+ return GetFormatter().GetNullDate();
+}
+
+sal_uInt16 XclRoot::GetBaseYear() const
+{
+ // return 1904 for 1904-01-01, and 1900 for 1899-12-30
+ return (GetNullDate().GetYear() == 1904) ? 1904 : 1900;
+}
+
+const DateTime theOurCompatNullDate( Date( 30, 12, 1899 ));
+const DateTime theExcelCutOverDate( Date( 1, 3, 1900 ));
+
+double XclRoot::GetDoubleFromDateTime( const DateTime& rDateTime ) const
+{
+ double fValue = rDateTime - GetNullDate();
+ // adjust dates before 1900-03-01 to get correct time values in the range [0.0,1.0)
+ /* XXX: this is only used when reading BIFF, otherwise we'd have to check
+ * for dateCompatibility==true as mentioned below. */
+ if( rDateTime < theExcelCutOverDate && GetNullDate() == theOurCompatNullDate )
+ fValue -= 1.0;
+ return fValue;
+}
+
+DateTime XclRoot::GetDateTimeFromDouble( double fValue ) const
+{
+ DateTime aDateTime = GetNullDate() + fValue;
+ // adjust dates before 1900-03-01 to get correct time values
+ /* FIXME: correction should only be done when writing BIFF or OOXML
+ * transitional with dateCompatibility==true (or absent for default true),
+ * but not if strict ISO/IEC 29500 which does not have the Excel error
+ * compatibility and the null date is the same 1899-12-30 as ours. */
+ if( aDateTime < theExcelCutOverDate && GetNullDate() == theOurCompatNullDate )
+ aDateTime.AddDays(1);
+ return aDateTime;
+}
+
+ScEditEngineDefaulter& XclRoot::GetEditEngine() const
+{
+ if( !mrData.mxEditEngine )
+ {
+ mrData.mxEditEngine = std::make_shared<ScEditEngineDefaulter>( GetDoc().GetEnginePool() );
+ ScEditEngineDefaulter& rEE = *mrData.mxEditEngine;
+ rEE.SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ rEE.SetEditTextObjectPool( GetDoc().GetEditPool() );
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+ }
+ return *mrData.mxEditEngine;
+}
+
+ScHeaderEditEngine& XclRoot::GetHFEditEngine() const
+{
+ if( !mrData.mxHFEditEngine )
+ {
+ mrData.mxHFEditEngine = std::make_shared<ScHeaderEditEngine>( EditEngine::CreatePool().get() );
+ ScHeaderEditEngine& rEE = *mrData.mxHFEditEngine;
+ rEE.SetRefMapMode(MapMode(MapUnit::MapTwip)); // headers/footers use twips as default metric
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+
+ // set Calc header/footer defaults
+ auto pEditSet = std::make_unique<SfxItemSet>( rEE.GetEmptyItemSet() );
+ SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END> aItemSet( *GetDoc().GetPool() );
+ ScPatternAttr::FillToEditItemSet( *pEditSet, aItemSet );
+ // FillToEditItemSet() adjusts font height to 1/100th mm, we need twips
+ pEditSet->Put( aItemSet.Get( ATTR_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ pEditSet->Put( aItemSet.Get( ATTR_CJK_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
+ pEditSet->Put( aItemSet.Get( ATTR_CTL_FONT_HEIGHT ).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ rEE.SetDefaults( std::move(pEditSet) ); // takes ownership
+ }
+ return *mrData.mxHFEditEngine;
+}
+
+EditEngine& XclRoot::GetDrawEditEngine() const
+{
+ if( !mrData.mxDrawEditEng )
+ {
+ mrData.mxDrawEditEng = std::make_shared<EditEngine>( &GetDoc().GetDrawLayer()->GetItemPool() );
+ EditEngine& rEE = *mrData.mxDrawEditEng;
+ rEE.SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ rEE.SetUpdateLayout( false );
+ rEE.EnableUndo( false );
+ rEE.SetControlWord( rEE.GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+ }
+ return *mrData.mxDrawEditEng;
+}
+
+XclFontPropSetHelper& XclRoot::GetFontPropSetHelper() const
+{
+ return *mrData.mxFontPropSetHlp;
+}
+
+XclChPropSetHelper& XclRoot::GetChartPropSetHelper() const
+{
+ return *mrData.mxChPropSetHlp;
+}
+
+ScExtDocOptions& XclRoot::GetExtDocOptions() const
+{
+ return *mrData.mxExtDocOpt;
+}
+
+XclTracer& XclRoot::GetTracer() const
+{
+ return *mrData.mxTracer;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlstyle.cxx b/sc/source/filter/excel/xlstyle.cxx
new file mode 100644
index 000000000..ae4a6f1f6
--- /dev/null
+++ b/sc/source/filter/excel/xlstyle.cxx
@@ -0,0 +1,1744 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlstyle.hxx>
+#include <com/sun/star/awt/FontFamily.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/font.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <rtl/tencinfo.h>
+#include <svl/numformat.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/unohelp.hxx>
+#include <editeng/svxfont.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <global.hxx>
+#include <xlroot.hxx>
+#include <xltools.hxx>
+// Color data =================================================================
+
+/** Standard EGA colors, bright. */
+#define EXC_PALETTE_EGA_COLORS_LIGHT \
+ Color(0x000000), Color(0xFFFFFF), Color(0xFF0000), Color(0x00FF00), Color(0x0000FF), Color(0xFFFF00), Color(0xFF00FF), Color(0x00FFFF)
+/** Standard EGA colors, dark. */
+#define EXC_PALETTE_EGA_COLORS_DARK \
+ Color(0x800000), Color(0x008000), Color(0x000080), Color(0x808000), Color(0x800080), Color(0x008080), Color(0xC0C0C0), Color(0x808080)
+
+/** Default color table for BIFF2. */
+const Color spnDefColorTable2[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT
+};
+
+/** Default color table for BIFF3/BIFF4. */
+const Color spnDefColorTable3[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK
+};
+
+/** Default color table for BIFF5/BIFF7. */
+const Color spnDefColorTable5[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK,
+/* 24 */ Color(0x8080FF), Color(0x802060), Color(0xFFFFC0), Color(0xA0E0E0), Color(0x600080), Color(0xFF8080), Color(0x0080C0), Color(0xC0C0FF),
+/* 32 */ Color(0x000080), Color(0xFF00FF), Color(0xFFFF00), Color(0x00FFFF), Color(0x800080), Color(0x800000), Color(0x008080), Color(0x0000FF),
+/* 40 */ Color(0x00CFFF), Color(0x69FFFF), Color(0xE0FFE0), Color(0xFFFF80), Color(0xA6CAF0), Color(0xDD9CB3), Color(0xB38FEE), Color(0xE3E3E3),
+/* 48 */ Color(0x2A6FF9), Color(0x3FB8CD), Color(0x488436), Color(0x958C41), Color(0x8E5E42), Color(0xA0627A), Color(0x624FAC), Color(0x969696),
+/* 56 */ Color(0x1D2FBE), Color(0x286676), Color(0x004500), Color(0x453E01), Color(0x6A2813), Color(0x85396A), Color(0x4A3285), Color(0x424242)
+};
+
+/** Default color table for BIFF8. */
+const Color spnDefColorTable8[] =
+{
+/* 0 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ EXC_PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ EXC_PALETTE_EGA_COLORS_DARK,
+/* 24 */ Color(0x9999FF), Color(0x993366), Color(0xFFFFCC), Color(0xCCFFFF), Color(0x660066), Color(0xFF8080), Color(0x0066CC), Color(0xCCCCFF),
+/* 32 */ Color(0x000080), Color(0xFF00FF), Color(0xFFFF00), Color(0x00FFFF), Color(0x800080), Color(0x800000), Color(0x008080), Color(0x0000FF),
+/* 40 */ Color(0x00CCFF), Color(0xCCFFFF), Color(0xCCFFCC), Color(0xFFFF99), Color(0x99CCFF), Color(0xFF99CC), Color(0xCC99FF), Color(0xFFCC99),
+/* 48 */ Color(0x3366FF), Color(0x33CCCC), Color(0x99CC00), Color(0xFFCC00), Color(0xFF9900), Color(0xFF6600), Color(0x666699), Color(0x969696),
+/* 56 */ Color(0x003366), Color(0x339966), Color(0x003300), Color(0x333300), Color(0x993300), Color(0x993366), Color(0x333399), Color(0x333333)
+};
+
+#undef EXC_PALETTE_EGA_COLORS_LIGHT
+#undef EXC_PALETTE_EGA_COLORS_DARK
+
+XclDefaultPalette::XclDefaultPalette( const XclRoot& rRoot ) :
+ mpnColorTable( nullptr ),
+ mnTableSize( 0 )
+{
+ const StyleSettings& rSett = Application::GetSettings().GetStyleSettings();
+ mnFaceColor = rSett.GetFaceColor();
+ // Don't use the system HelpBack and HelpText colours as it causes problems
+ // with modern gnome. This is because mnNoteText and mnNoteBack are used
+ // when colour indices ( instead of real colours ) are specified.
+ // Note: That this it is not an unusual scenario that we get the Note
+ // background specified as a real colour and the text specified as a
+ // colour index. That means the text colour would be picked from
+ // the system where the note background would be picked from a real colour.
+ // Previously the note text colour was picked from the system tooltip
+ // text colour, on modern gnome(e.g. 3) that tends to be 'white' with the
+ // default theme.
+ // Using the Libreoffice defaults ( instead of system specific colours
+ // ) lessens the chance of the one colour being an unsuitable combination
+ // because by default the note text is black and the note background is
+ // a light yellow colour ( very similar to Excel's normal defaults )
+ mnNoteText = svtools::ColorConfig::GetDefaultColor( svtools::FONTCOLOR );
+ mnNoteBack = svtools::ColorConfig::GetDefaultColor( svtools::CALCNOTESBACKGROUND );
+
+ // default colors
+ switch( rRoot.GetBiff() )
+ {
+ case EXC_BIFF2:
+ mpnColorTable = spnDefColorTable2;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable2 );
+ break;
+ case EXC_BIFF3:
+ case EXC_BIFF4:
+ mpnColorTable = spnDefColorTable3;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable3 );
+ break;
+ case EXC_BIFF5:
+ mpnColorTable = spnDefColorTable5;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable5 );
+ break;
+ case EXC_BIFF8:
+ mpnColorTable = spnDefColorTable8;
+ mnTableSize = SAL_N_ELEMENTS( spnDefColorTable8 );
+ break;
+ default:
+ DBG_ERROR_BIFF();
+ }
+}
+
+Color XclDefaultPalette::GetDefColor( sal_uInt16 nXclIndex ) const
+{
+ Color nColor;
+ if( nXclIndex < mnTableSize )
+ nColor = mpnColorTable[ nXclIndex ];
+ else switch( nXclIndex )
+ {
+ case EXC_COLOR_WINDOWTEXT3:
+ case EXC_COLOR_WINDOWTEXT:
+ case EXC_COLOR_CHWINDOWTEXT: nColor = COL_BLACK; break;
+ case EXC_COLOR_WINDOWBACK3:
+ case EXC_COLOR_WINDOWBACK:
+ case EXC_COLOR_CHWINDOWBACK: nColor = COL_WHITE; break;
+ case EXC_COLOR_BUTTONBACK: nColor = mnFaceColor; break;
+ case EXC_COLOR_CHBORDERAUTO: nColor = COL_BLACK; break; // TODO: really always black?
+ case EXC_COLOR_NOTEBACK: nColor = mnNoteBack; break;
+ case EXC_COLOR_NOTETEXT: nColor = mnNoteText; break;
+ case EXC_COLOR_FONTAUTO: nColor = COL_AUTO; break;
+ default:
+ SAL_WARN("sc", "XclDefaultPalette::GetDefColor - unknown default color index: " << nXclIndex );
+ nColor = COL_AUTO;
+ }
+ return nColor;
+}
+
+// Font Data ==================================================================
+
+namespace Awt = ::com::sun::star::awt;
+namespace AwtFontFamily = Awt::FontFamily;
+namespace AwtFontLineStyle = Awt::FontUnderline;
+namespace AwtFontStrikeout = Awt::FontStrikeout;
+
+XclFontData::XclFontData()
+{
+ Clear();
+}
+
+XclFontData::XclFontData( const vcl::Font& rFont )
+{
+ Clear();
+ FillFromVclFont( rFont );
+}
+
+XclFontData::XclFontData( const SvxFont& rFont )
+{
+ FillFromSvxFont( rFont );
+}
+
+void XclFontData::Clear()
+{
+ maName.clear();
+ maStyle.clear();
+ maColor = COL_AUTO;
+ mnHeight = 0;
+ mnWeight = EXC_FONTWGHT_DONTKNOW;
+ mnEscapem = EXC_FONTESC_NONE;
+ mnFamily = EXC_FONTFAM_SYSTEM;
+ mnCharSet = EXC_FONTCSET_ANSI_LATIN;
+ mnUnderline = EXC_FONTUNDERL_NONE;
+ mbItalic = mbStrikeout = mbOutline = mbShadow = false;
+}
+
+void XclFontData::FillFromVclFont( const vcl::Font& rFont )
+{
+ maName = XclTools::GetXclFontName( rFont.GetFamilyName() ); // substitute with MS fonts
+ maStyle.clear();
+ maColor = rFont.GetColor();
+ SetScUnderline( rFont.GetUnderline() );
+ mnEscapem = EXC_FONTESC_NONE;
+ SetScHeight( rFont.GetFontSize().Height() );
+ SetScWeight( rFont.GetWeight() );
+ SetScFamily( rFont.GetFamilyType() );
+ SetFontEncoding( rFont.GetCharSet() );
+ SetScPosture( rFont.GetItalic() );
+ SetScStrikeout( rFont.GetStrikeout() );
+ mbOutline = rFont.IsOutline();
+ mbShadow = rFont.IsShadow();
+}
+
+void XclFontData::FillFromSvxFont( const SvxFont& rFont )
+{
+ FillFromVclFont( rFont );
+ SetScEscapement( rFont.GetEscapement() );
+}
+
+// *** conversion of VCL/SVX constants *** ------------------------------------
+
+FontFamily XclFontData::GetScFamily( rtl_TextEncoding eDefTextEnc ) const
+{
+ FontFamily eScFamily;
+ // ! format differs from Windows documentation: family is in lower nibble, pitch unknown
+ switch( mnFamily & 0x0F )
+ {
+ case EXC_FONTFAM_ROMAN: eScFamily = FAMILY_ROMAN; break;
+ case EXC_FONTFAM_SWISS: eScFamily = FAMILY_SWISS; break;
+ case EXC_FONTFAM_MODERN: eScFamily = FAMILY_MODERN; break;
+ case EXC_FONTFAM_SCRIPT: eScFamily = FAMILY_SCRIPT; break;
+ case EXC_FONTFAM_DECORATIVE: eScFamily = FAMILY_DECORATIVE; break;
+ default:
+ eScFamily =
+ ((eDefTextEnc == RTL_TEXTENCODING_APPLE_ROMAN) &&
+ (maName.equalsIgnoreAsciiCase( "Geneva" ) || maName.equalsIgnoreAsciiCase( "Chicago" ))) ?
+ FAMILY_SWISS : FAMILY_DONTKNOW;
+ }
+ return eScFamily;
+}
+
+rtl_TextEncoding XclFontData::GetFontEncoding() const
+{
+ // convert Windows character set to text encoding identifier
+ return rtl_getTextEncodingFromWindowsCharset( mnCharSet );
+}
+
+FontItalic XclFontData::GetScPosture() const
+{
+ return mbItalic ? ITALIC_NORMAL : ITALIC_NONE;
+}
+
+FontWeight XclFontData::GetScWeight() const
+{
+ FontWeight eScWeight;
+
+ if( !mnWeight ) eScWeight = WEIGHT_DONTKNOW;
+ else if( mnWeight < 150 ) eScWeight = WEIGHT_THIN;
+ else if( mnWeight < 250 ) eScWeight = WEIGHT_ULTRALIGHT;
+ else if( mnWeight < 325 ) eScWeight = WEIGHT_LIGHT;
+ else if( mnWeight < 375 ) eScWeight = WEIGHT_SEMILIGHT;
+ else if( mnWeight < 450 ) eScWeight = WEIGHT_NORMAL;
+ else if( mnWeight < 550 ) eScWeight = WEIGHT_MEDIUM;
+ else if( mnWeight < 650 ) eScWeight = WEIGHT_SEMIBOLD;
+ else if( mnWeight < 750 ) eScWeight = WEIGHT_BOLD;
+ else if( mnWeight < 850 ) eScWeight = WEIGHT_ULTRABOLD;
+ else eScWeight = WEIGHT_BLACK;
+
+ return eScWeight;
+}
+
+FontLineStyle XclFontData::GetScUnderline() const
+{
+ FontLineStyle eScUnderl = LINESTYLE_NONE;
+ switch( mnUnderline )
+ {
+ case EXC_FONTUNDERL_SINGLE:
+ case EXC_FONTUNDERL_SINGLE_ACC: eScUnderl = LINESTYLE_SINGLE; break;
+ case EXC_FONTUNDERL_DOUBLE:
+ case EXC_FONTUNDERL_DOUBLE_ACC: eScUnderl = LINESTYLE_DOUBLE; break;
+ }
+ return eScUnderl;
+}
+
+SvxEscapement XclFontData::GetScEscapement() const
+{
+ SvxEscapement eScEscapem = SvxEscapement::Off;
+ switch( mnEscapem )
+ {
+ case EXC_FONTESC_SUPER: eScEscapem = SvxEscapement::Superscript; break;
+ case EXC_FONTESC_SUB: eScEscapem = SvxEscapement::Subscript; break;
+ }
+ return eScEscapem;
+}
+
+FontStrikeout XclFontData::GetScStrikeout() const
+{
+ return mbStrikeout ? STRIKEOUT_SINGLE : STRIKEOUT_NONE;
+}
+
+void XclFontData::SetScHeight( sal_Int32 nTwips )
+{
+ mnHeight = static_cast< sal_uInt16 >( ::std::min( nTwips, static_cast<sal_Int32>(0x7FFFL) ) );
+}
+
+void XclFontData::SetScFamily( FontFamily eScFamily )
+{
+ switch( eScFamily )
+ {
+ case FAMILY_DONTKNOW: mnFamily = EXC_FONTFAM_DONTKNOW; break;
+ case FAMILY_DECORATIVE: mnFamily = EXC_FONTFAM_DECORATIVE; break;
+ case FAMILY_MODERN: mnFamily = EXC_FONTFAM_MODERN; break;
+ case FAMILY_ROMAN: mnFamily = EXC_FONTFAM_ROMAN; break;
+ case FAMILY_SCRIPT: mnFamily = EXC_FONTFAM_SCRIPT; break;
+ case FAMILY_SWISS: mnFamily = EXC_FONTFAM_SWISS; break;
+ case FAMILY_SYSTEM: mnFamily = EXC_FONTFAM_SYSTEM; break;
+ default:
+ OSL_FAIL( "XclFontData::SetScFamily - unknown font family" );
+ mnFamily = EXC_FONTFAM_DONTKNOW;
+ }
+}
+
+void XclFontData::SetFontEncoding( rtl_TextEncoding eFontEnc )
+{
+ // convert text encoding identifier to Windows character set
+ mnCharSet = rtl_getBestWindowsCharsetFromTextEncoding( eFontEnc );
+}
+
+void XclFontData::SetScPosture( FontItalic eScPosture )
+{
+ mbItalic = (eScPosture == ITALIC_OBLIQUE) || (eScPosture == ITALIC_NORMAL);
+}
+
+void XclFontData::SetScWeight( FontWeight eScWeight )
+{
+ switch( eScWeight )
+ {
+ case WEIGHT_DONTKNOW: mnWeight = EXC_FONTWGHT_DONTKNOW; break;
+ case WEIGHT_THIN: mnWeight = EXC_FONTWGHT_THIN; break;
+ case WEIGHT_ULTRALIGHT: mnWeight = EXC_FONTWGHT_ULTRALIGHT; break;
+ case WEIGHT_LIGHT: mnWeight = EXC_FONTWGHT_LIGHT; break;
+ case WEIGHT_SEMILIGHT: mnWeight = EXC_FONTWGHT_SEMILIGHT; break;
+ case WEIGHT_NORMAL: mnWeight = EXC_FONTWGHT_NORMAL; break;
+ case WEIGHT_MEDIUM: mnWeight = EXC_FONTWGHT_MEDIUM; break;
+ case WEIGHT_SEMIBOLD: mnWeight = EXC_FONTWGHT_SEMIBOLD; break;
+ case WEIGHT_BOLD: mnWeight = EXC_FONTWGHT_BOLD; break;
+ case WEIGHT_ULTRABOLD: mnWeight = EXC_FONTWGHT_ULTRABOLD; break;
+ case WEIGHT_BLACK: mnWeight = EXC_FONTWGHT_BLACK; break;
+ default: mnWeight = EXC_FONTWGHT_NORMAL;
+ }
+}
+
+void XclFontData::SetScUnderline( FontLineStyle eScUnderl )
+{
+ switch( eScUnderl )
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_DONTKNOW: mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case LINESTYLE_DOUBLE:
+ case LINESTYLE_DOUBLEWAVE: mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+}
+
+void XclFontData::SetScEscapement( short nScEscapem )
+{
+ if( nScEscapem > 0 )
+ mnEscapem = EXC_FONTESC_SUPER;
+ else if( nScEscapem < 0 )
+ mnEscapem = EXC_FONTESC_SUB;
+ else
+ mnEscapem = EXC_FONTESC_NONE;
+}
+
+void XclFontData::SetScStrikeout( FontStrikeout eScStrikeout )
+{
+ mbStrikeout =
+ (eScStrikeout == STRIKEOUT_SINGLE) || (eScStrikeout == STRIKEOUT_DOUBLE) ||
+ (eScStrikeout == STRIKEOUT_BOLD) || (eScStrikeout == STRIKEOUT_SLASH) ||
+ (eScStrikeout == STRIKEOUT_X);
+}
+
+// *** conversion of API constants *** ----------------------------------------
+
+float XclFontData::GetApiHeight() const
+{
+ return o3tl::convert<double>(mnHeight, o3tl::Length::twip, o3tl::Length::pt);
+}
+
+sal_Int16 XclFontData::GetApiFamily() const
+{
+ sal_Int16 nApiFamily = AwtFontFamily::DONTKNOW;
+ switch( mnFamily )
+ {
+ case FAMILY_DECORATIVE: nApiFamily = AwtFontFamily::DECORATIVE; break;
+ case FAMILY_MODERN: nApiFamily = AwtFontFamily::MODERN; break;
+ case FAMILY_ROMAN: nApiFamily = AwtFontFamily::ROMAN; break;
+ case FAMILY_SCRIPT: nApiFamily = AwtFontFamily::SCRIPT; break;
+ case FAMILY_SWISS: nApiFamily = AwtFontFamily::SWISS; break;
+ case FAMILY_SYSTEM: nApiFamily = AwtFontFamily::SYSTEM; break;
+ }
+ return nApiFamily;
+}
+
+sal_Int16 XclFontData::GetApiFontEncoding() const
+{
+ // API constants are equal to rtl_TextEncoding constants
+ return static_cast< sal_Int16 >( GetFontEncoding() );
+}
+
+Awt::FontSlant XclFontData::GetApiPosture() const
+{
+ return mbItalic ? Awt::FontSlant_ITALIC : Awt::FontSlant_NONE;
+}
+
+float XclFontData::GetApiWeight() const
+{
+ return vcl::unohelper::ConvertFontWeight( GetScWeight() );
+}
+
+sal_Int16 XclFontData::GetApiUnderline() const
+{
+ sal_Int16 nApiUnderl = AwtFontLineStyle::NONE;
+ switch( mnUnderline )
+ {
+ case EXC_FONTUNDERL_SINGLE:
+ case EXC_FONTUNDERL_SINGLE_ACC: nApiUnderl = AwtFontLineStyle::SINGLE; break;
+ case EXC_FONTUNDERL_DOUBLE:
+ case EXC_FONTUNDERL_DOUBLE_ACC: nApiUnderl = AwtFontLineStyle::DOUBLE; break;
+ }
+ return nApiUnderl;
+}
+
+sal_Int16 XclFontData::GetApiEscapement() const
+{
+ sal_Int16 nApiEscapem = 0;
+ switch( mnEscapem )
+ {
+ case EXC_FONTESC_SUPER: nApiEscapem = 33; break;
+ case EXC_FONTESC_SUB: nApiEscapem = -33; break;
+ }
+ return nApiEscapem;
+}
+
+sal_Int16 XclFontData::GetApiStrikeout() const
+{
+ return mbStrikeout ? AwtFontStrikeout::SINGLE : AwtFontStrikeout::NONE;
+}
+
+void XclFontData::SetApiHeight( float fPoint )
+{
+ mnHeight = std::min(o3tl::convert(fPoint, o3tl::Length::pt, o3tl::Length::twip) + 0.5, 32767.0);
+}
+
+void XclFontData::SetApiFamily( sal_Int16 nApiFamily )
+{
+ switch( nApiFamily )
+ {
+ case AwtFontFamily::DECORATIVE: mnFamily = FAMILY_DECORATIVE; break;
+ case AwtFontFamily::MODERN: mnFamily = FAMILY_MODERN; break;
+ case AwtFontFamily::ROMAN: mnFamily = FAMILY_ROMAN; break;
+ case AwtFontFamily::SCRIPT: mnFamily = FAMILY_SCRIPT; break;
+ case AwtFontFamily::SWISS: mnFamily = FAMILY_SWISS; break;
+ case AwtFontFamily::SYSTEM: mnFamily = FAMILY_SYSTEM; break;
+ default: mnFamily = FAMILY_DONTKNOW;
+ }
+}
+
+void XclFontData::SetApiPosture( Awt::FontSlant eApiPosture )
+{
+ mbItalic =
+ (eApiPosture == Awt::FontSlant_OBLIQUE) ||
+ (eApiPosture == Awt::FontSlant_ITALIC) ||
+ (eApiPosture == Awt::FontSlant_REVERSE_OBLIQUE) ||
+ (eApiPosture == Awt::FontSlant_REVERSE_ITALIC);
+}
+
+void XclFontData::SetApiWeight( float fApiWeight )
+{
+ SetScWeight( vcl::unohelper::ConvertFontWeight( fApiWeight ) );
+}
+
+void XclFontData::SetApiUnderline( sal_Int16 nApiUnderl )
+{
+ switch( nApiUnderl )
+ {
+ case AwtFontLineStyle::NONE:
+ case AwtFontLineStyle::DONTKNOW: mnUnderline = EXC_FONTUNDERL_NONE; break;
+ case AwtFontLineStyle::DOUBLE:
+ case AwtFontLineStyle::DOUBLEWAVE: mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
+ default: mnUnderline = EXC_FONTUNDERL_SINGLE;
+ }
+}
+
+void XclFontData::SetApiEscapement( sal_Int16 nApiEscapem )
+{
+ if( nApiEscapem > 0 )
+ mnEscapem = EXC_FONTESC_SUPER;
+ else if( nApiEscapem < 0 )
+ mnEscapem = EXC_FONTESC_SUB;
+ else
+ mnEscapem = EXC_FONTESC_NONE;
+}
+
+void XclFontData::SetApiStrikeout( sal_Int16 nApiStrikeout )
+{
+ mbStrikeout =
+ (nApiStrikeout != AwtFontStrikeout::NONE) &&
+ (nApiStrikeout != AwtFontStrikeout::DONTKNOW);
+}
+
+bool operator==( const XclFontData& rLeft, const XclFontData& rRight )
+{
+ return
+ (rLeft.mnHeight == rRight.mnHeight) &&
+ (rLeft.mnWeight == rRight.mnWeight) &&
+ (rLeft.mnUnderline == rRight.mnUnderline) &&
+ (rLeft.maColor == rRight.maColor) &&
+ (rLeft.mnEscapem == rRight.mnEscapem) &&
+ (rLeft.mnFamily == rRight.mnFamily) &&
+ (rLeft.mnCharSet == rRight.mnCharSet) &&
+ (rLeft.mbItalic == rRight.mbItalic) &&
+ (rLeft.mbStrikeout == rRight.mbStrikeout) &&
+ (rLeft.mbOutline == rRight.mbOutline) &&
+ (rLeft.mbShadow == rRight.mbShadow) &&
+ (rLeft.maName == rRight.maName);
+}
+
+namespace {
+
+/** Property names for common font settings. */
+const char *const sppcPropNamesChCommon[] =
+{
+ "CharUnderline", "CharStrikeout", "CharColor", "CharContoured", "CharShadowed", nullptr
+};
+/** Property names for Western font settings. */
+const char *const sppcPropNamesChWstrn[] =
+{
+ "CharFontName", "CharHeight", "CharPosture", "CharWeight", nullptr
+};
+/** Property names for Asian font settings. */
+const char *const sppcPropNamesChAsian[] =
+{
+ "CharFontNameAsian", "CharHeightAsian", "CharPostureAsian", "CharWeightAsian", nullptr
+};
+/** Property names for Complex font settings. */
+const char *const sppcPropNamesChCmplx[] =
+{
+ "CharFontNameComplex", "CharHeightComplex", "CharPostureComplex", "CharWeightComplex", nullptr
+};
+/** Property names for escapement. */
+const char *const sppcPropNamesChEscapement[] =
+{
+ "CharEscapement", "CharEscapementHeight", nullptr
+};
+const sal_Int8 EXC_API_ESC_HEIGHT = 58; /// Default escapement font height.
+
+/** Property names for Western font settings without font name. */
+const char *const *const sppcPropNamesChWstrnNoName = sppcPropNamesChWstrn + 1;
+/** Property names for Asian font settings without font name. */
+const char *const *const sppcPropNamesChAsianNoName = sppcPropNamesChAsian + 1;
+/** Property names for Complex font settings without font name. */
+const char *const *const sppcPropNamesChCmplxNoName = sppcPropNamesChCmplx + 1;
+
+/** Property names for font settings in form controls. */
+const char *const sppcPropNamesControl[] =
+{
+ "FontName", "FontFamily", "FontCharset", "FontHeight", "FontSlant",
+ "FontWeight", "FontLineStyle", "FontStrikeout", "TextColor", nullptr
+};
+
+/** Inserts all passed API font settings into the font data object. */
+void lclSetApiFontSettings( XclFontData& rFontData,
+ const OUString& rApiFontName, float fApiHeight, float fApiWeight,
+ Awt::FontSlant eApiPosture, sal_Int16 nApiUnderl, sal_Int16 nApiStrikeout )
+{
+ rFontData.maName = XclTools::GetXclFontName( rApiFontName );
+ rFontData.SetApiHeight( fApiHeight );
+ rFontData.SetApiWeight( fApiWeight );
+ rFontData.SetApiPosture( eApiPosture );
+ rFontData.SetApiUnderline( nApiUnderl );
+ rFontData.SetApiStrikeout( nApiStrikeout );
+}
+
+/** Writes script dependent properties to a font property set helper. */
+void lclWriteChartFont( ScfPropertySet& rPropSet,
+ ScfPropSetHelper& rHlpName, ScfPropSetHelper& rHlpNoName,
+ const XclFontData& rFontData, bool bHasFontName )
+{
+ // select the font helper
+ ScfPropSetHelper& rPropSetHlp = bHasFontName ? rHlpName : rHlpNoName;
+ // initialize the font helper (must be called before writing any properties)
+ rPropSetHlp.InitializeWrite();
+ // write font name
+ if( bHasFontName )
+ rPropSetHlp << rFontData.maName;
+ // write remaining properties
+ rPropSetHlp << rFontData.GetApiHeight() << rFontData.GetApiPosture() << rFontData.GetApiWeight();
+ // write properties to property set
+ rPropSetHlp.WriteToPropertySet( rPropSet );
+}
+
+} // namespace
+
+XclFontPropSetHelper::XclFontPropSetHelper() :
+ maHlpChCommon( sppcPropNamesChCommon ),
+ maHlpChWstrn( sppcPropNamesChWstrn ),
+ maHlpChAsian( sppcPropNamesChAsian ),
+ maHlpChCmplx( sppcPropNamesChCmplx ),
+ maHlpChWstrnNoName( sppcPropNamesChWstrnNoName ),
+ maHlpChAsianNoName( sppcPropNamesChAsianNoName ),
+ maHlpChCmplxNoName( sppcPropNamesChCmplxNoName ),
+ maHlpChEscapement( sppcPropNamesChEscapement ),
+ maHlpControl( sppcPropNamesControl )
+{
+}
+
+void XclFontPropSetHelper::ReadFontProperties( XclFontData& rFontData,
+ const ScfPropertySet& rPropSet, XclFontPropSetType eType, sal_Int16 nScript )
+{
+ switch( eType )
+ {
+ case EXC_FONTPROPSET_CHART:
+ {
+ OUString aApiFontName;
+ float fApiHeight, fApiWeight;
+ sal_Int16 nApiUnderl = 0, nApiStrikeout = 0;
+ Awt::FontSlant eApiPosture;
+
+ // read script type dependent properties
+ ScfPropSetHelper& rPropSetHlp = GetChartHelper( nScript );
+ rPropSetHlp.ReadFromPropertySet( rPropSet );
+ rPropSetHlp >> aApiFontName >> fApiHeight >> eApiPosture >> fApiWeight;
+ // read common properties
+ maHlpChCommon.ReadFromPropertySet( rPropSet );
+ maHlpChCommon >> nApiUnderl
+ >> nApiStrikeout
+ >> rFontData.maColor
+ >> rFontData.mbOutline
+ >> rFontData.mbShadow;
+
+ // convert API property values to Excel settings
+ lclSetApiFontSettings( rFontData, aApiFontName,
+ fApiHeight, fApiWeight, eApiPosture, nApiUnderl, nApiStrikeout );
+
+ // font escapement
+ sal_Int16 nApiEscapement = 0;
+ sal_Int8 nApiEscHeight = 0;
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement.ReadFromPropertySet( rPropSet );
+ maHlpChEscapement >> nApiEscapement >> nApiEscHeight;
+ rFontData.SetApiEscapement( nApiEscapement );
+ }
+ break;
+
+ case EXC_FONTPROPSET_CONTROL:
+ {
+ OUString aApiFontName;
+ float fApiHeight(0.0), fApiWeight(0.0);
+ sal_Int16 nApiFamily(0), nApiCharSet(0), nApiPosture(0), nApiUnderl(0), nApiStrikeout(0);
+
+ // read font properties
+ maHlpControl.ReadFromPropertySet( rPropSet );
+ maHlpControl >> aApiFontName
+ >> nApiFamily
+ >> nApiCharSet
+ >> fApiHeight
+ >> nApiPosture
+ >> fApiWeight
+ >> nApiUnderl
+ >> nApiStrikeout
+ >> rFontData.maColor;
+
+ // convert API property values to Excel settings
+ Awt::FontSlant eApiPosture = static_cast< Awt::FontSlant >( nApiPosture );
+ lclSetApiFontSettings( rFontData, aApiFontName,
+ fApiHeight, fApiWeight, eApiPosture, nApiUnderl, nApiStrikeout );
+ rFontData.SetApiFamily( nApiFamily );
+ rFontData.SetFontEncoding( nApiCharSet );
+ }
+ break;
+ }
+}
+
+void XclFontPropSetHelper::WriteFontProperties(
+ ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ const XclFontData& rFontData, bool bHasWstrn, bool bHasAsian, bool bHasCmplx,
+ const Color* pFontColor )
+{
+ switch( eType )
+ {
+ case EXC_FONTPROPSET_CHART:
+ {
+ // write common properties
+ maHlpChCommon.InitializeWrite();
+ const Color& rColor = pFontColor ? *pFontColor : rFontData.maColor;
+ maHlpChCommon << rFontData.GetApiUnderline()
+ << rFontData.GetApiStrikeout()
+ << rColor
+ << rFontData.mbOutline
+ << rFontData.mbShadow;
+ maHlpChCommon.WriteToPropertySet( rPropSet );
+
+ // write script type dependent properties
+ lclWriteChartFont( rPropSet, maHlpChWstrn, maHlpChWstrnNoName, rFontData, bHasWstrn );
+ lclWriteChartFont( rPropSet, maHlpChAsian, maHlpChAsianNoName, rFontData, bHasAsian );
+ lclWriteChartFont( rPropSet, maHlpChCmplx, maHlpChCmplxNoName, rFontData, bHasCmplx );
+
+ // font escapement
+ if( rFontData.GetScEscapement() != SvxEscapement::Off )
+ {
+ maHlpChEscapement.InitializeWrite();
+ maHlpChEscapement << rFontData.GetApiEscapement() << EXC_API_ESC_HEIGHT;
+ maHlpChEscapement.WriteToPropertySet( rPropSet );
+ }
+ }
+ break;
+
+ case EXC_FONTPROPSET_CONTROL:
+ {
+ maHlpControl.InitializeWrite();
+ maHlpControl << rFontData.maName
+ << rFontData.GetApiFamily()
+ << rFontData.GetApiFontEncoding()
+ << static_cast< sal_Int16 >( rFontData.GetApiHeight() + 0.5 )
+ << rFontData.GetApiPosture()
+ << rFontData.GetApiWeight()
+ << rFontData.GetApiUnderline()
+ << rFontData.GetApiStrikeout()
+ << rFontData.maColor;
+ maHlpControl.WriteToPropertySet( rPropSet );
+ }
+ break;
+ }
+}
+
+ScfPropSetHelper& XclFontPropSetHelper::GetChartHelper( sal_Int16 nScript )
+{
+ namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
+ switch( nScript )
+ {
+ case ApiScriptType::LATIN: return maHlpChWstrn;
+ case ApiScriptType::ASIAN: return maHlpChAsian;
+ case ApiScriptType::COMPLEX: return maHlpChCmplx;
+ default: OSL_FAIL( "XclFontPropSetHelper::GetChartHelper - unknown script type" );
+ }
+ return maHlpChWstrn;
+}
+
+// Number formats =============================================================
+
+namespace {
+
+/** Special number format index describing a reused format. */
+const NfIndexTableOffset PRV_NF_INDEX_REUSE = NF_INDEX_TABLE_ENTRIES;
+
+/** German primary language not defined, LANGUAGE_GERMAN belongs to Germany. */
+constexpr LanguageType PRV_LANGUAGE_GERMAN_PRIM = primary(LANGUAGE_GERMAN);
+/** French primary language not defined, LANGUAGE_FRENCH belongs to France. */
+constexpr LanguageType PRV_LANGUAGE_FRENCH_PRIM = primary(LANGUAGE_FRENCH);
+/** Parent language identifier for Asian languages. */
+constexpr LanguageType PRV_LANGUAGE_ASIAN_PRIM = primary(LANGUAGE_CHINESE);
+
+/** Stores the number format used in Calc for an Excel built-in number format. */
+struct XclBuiltInFormat
+{
+ sal_uInt16 mnXclNumFmt; /// Excel built-in index.
+ const char* mpFormat; /// Format string, may be 0 (meOffset used then).
+ NfIndexTableOffset meOffset; /// SvNumberFormatter format index, if mpFormat==0.
+ sal_uInt16 mnXclReuseFmt; /// Use this Excel format, if meOffset==PRV_NF_INDEX_REUSE.
+};
+
+/** Defines a literal Excel built-in number format. */
+#define EXC_NUMFMT_STRING( nXclNumFmt, pcUtf8 ) \
+ { nXclNumFmt, pcUtf8, NF_NUMBER_STANDARD, 0 }
+
+/** Defines an Excel built-in number format that maps to an own built-in format. */
+#define EXC_NUMFMT_OFFSET( nXclNumFmt, eOffset ) \
+ { nXclNumFmt, nullptr, eOffset, 0 }
+
+/** Defines an Excel built-in number format that is the same as the specified. */
+#define EXC_NUMFMT_REUSE( nXclNumFmt, nXclReuse ) \
+ { nXclNumFmt, nullptr, PRV_NF_INDEX_REUSE, nXclReuse }
+
+/** Terminates an Excel built-in number format table. */
+#define EXC_NUMFMT_ENDTABLE() \
+ { EXC_FORMAT_NOTFOUND, nullptr, NF_NUMBER_STANDARD, 0 }
+
+// Currency unit characters
+#define UTF8_BAHT "\340\270\277"
+#define UTF8_EURO "\342\202\254"
+#define UTF8_POUND_UK "\302\243"
+#define UTF8_SHEQEL "\342\202\252"
+#define UTF8_WON "\357\277\246"
+#define UTF8_YEN_CS "\357\277\245"
+#define UTF8_YEN_JP "\302\245"
+
+// Japanese/Chinese date/time characters
+#define UTF8_CJ_YEAR "\345\271\264"
+#define UTF8_CJ_MON "\346\234\210"
+#define UTF8_CJ_DAY "\346\227\245"
+#define UTF8_CJ_HOUR "\346\231\202"
+#define UTF8_CJ_MIN "\345\210\206"
+#define UTF8_CJ_SEC "\347\247\222"
+
+// Chinese Simplified date/time characters
+#define UTF8_CS_HOUR "\346\227\266"
+
+// Korean date/time characters
+#define UTF8_KO_YEAR "\353\205\204"
+#define UTF8_KO_MON "\354\233\224"
+#define UTF8_KO_DAY "\354\235\274"
+#define UTF8_KO_HOUR "\354\213\234"
+#define UTF8_KO_MIN "\353\266\204"
+#define UTF8_KO_SEC "\354\264\210"
+
+/** Default number format table. Last parent of all other tables, used for unknown languages. */
+const XclBuiltInFormat spBuiltInFormats_DONTKNOW[] =
+{
+ EXC_NUMFMT_OFFSET( 0, NF_NUMBER_STANDARD ), // General
+ EXC_NUMFMT_OFFSET( 1, NF_NUMBER_INT ), // 0
+ EXC_NUMFMT_OFFSET( 2, NF_NUMBER_DEC2 ), // 0.00
+ EXC_NUMFMT_OFFSET( 3, NF_NUMBER_1000INT ), // #,##0
+ EXC_NUMFMT_OFFSET( 4, NF_NUMBER_1000DEC2 ), // #,##0.00
+ // 5...8 contained in file
+ EXC_NUMFMT_OFFSET( 9, NF_PERCENT_INT ), // 0%
+ EXC_NUMFMT_OFFSET( 10, NF_PERCENT_DEC2 ), // 0.00%
+ EXC_NUMFMT_OFFSET( 11, NF_SCIENTIFIC_000E00 ), // 0.00E+00
+ EXC_NUMFMT_OFFSET( 12, NF_FRACTION_1D ), // # ?/?
+ EXC_NUMFMT_OFFSET( 13, NF_FRACTION_2D ), // # ??/??
+
+ // 14...22 date and time formats
+ EXC_NUMFMT_OFFSET( 14, NF_DATE_SYS_DDMMYYYY ),
+ EXC_NUMFMT_OFFSET( 15, NF_DATE_SYS_DMMMYY ),
+ EXC_NUMFMT_OFFSET( 16, NF_DATE_SYS_DDMMM ),
+ EXC_NUMFMT_OFFSET( 17, NF_DATE_SYS_MMYY ),
+ EXC_NUMFMT_OFFSET( 18, NF_TIME_HHMMAMPM ),
+ EXC_NUMFMT_OFFSET( 19, NF_TIME_HHMMSSAMPM ),
+ EXC_NUMFMT_OFFSET( 20, NF_TIME_HHMM ),
+ EXC_NUMFMT_OFFSET( 21, NF_TIME_HHMMSS ),
+ EXC_NUMFMT_OFFSET( 22, NF_DATETIME_SYSTEM_SHORT_HHMM ),
+
+ // 23...36 international formats
+ EXC_NUMFMT_REUSE( 23, 0 ),
+ EXC_NUMFMT_REUSE( 24, 0 ),
+ EXC_NUMFMT_REUSE( 25, 0 ),
+ EXC_NUMFMT_REUSE( 26, 0 ),
+ EXC_NUMFMT_REUSE( 27, 14 ),
+ EXC_NUMFMT_REUSE( 28, 14 ),
+ EXC_NUMFMT_REUSE( 29, 14 ),
+ EXC_NUMFMT_REUSE( 30, 14 ),
+ EXC_NUMFMT_REUSE( 31, 14 ),
+ EXC_NUMFMT_REUSE( 32, 21 ),
+ EXC_NUMFMT_REUSE( 33, 21 ),
+ EXC_NUMFMT_REUSE( 34, 21 ),
+ EXC_NUMFMT_REUSE( 35, 21 ),
+ EXC_NUMFMT_REUSE( 36, 14 ),
+
+ // 37...44 accounting formats
+ // 41...44 contained in file
+ EXC_NUMFMT_STRING( 37, "#,##0;-#,##0" ),
+ EXC_NUMFMT_STRING( 38, "#,##0;[RED]-#,##0" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00;-#,##0.00" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00;[RED]-#,##0.00" ),
+
+ // 45...49 more special formats
+ EXC_NUMFMT_STRING( 45, "mm:ss" ),
+ EXC_NUMFMT_STRING( 46, "[h]:mm:ss" ),
+ EXC_NUMFMT_STRING( 47, "mm:ss.0" ),
+ EXC_NUMFMT_STRING( 48, "##0.0E+0" ),
+ EXC_NUMFMT_OFFSET( 49, NF_TEXT ),
+
+ // 50...81 international formats
+ EXC_NUMFMT_REUSE( 50, 14 ),
+ EXC_NUMFMT_REUSE( 51, 14 ),
+ EXC_NUMFMT_REUSE( 52, 14 ),
+ EXC_NUMFMT_REUSE( 53, 14 ),
+ EXC_NUMFMT_REUSE( 54, 14 ),
+ EXC_NUMFMT_REUSE( 55, 14 ),
+ EXC_NUMFMT_REUSE( 56, 14 ),
+ EXC_NUMFMT_REUSE( 57, 14 ),
+ EXC_NUMFMT_REUSE( 58, 14 ),
+ EXC_NUMFMT_REUSE( 59, 1 ),
+ EXC_NUMFMT_REUSE( 60, 2 ),
+ EXC_NUMFMT_REUSE( 61, 3 ),
+ EXC_NUMFMT_REUSE( 62, 4 ),
+ EXC_NUMFMT_REUSE( 67, 9 ),
+ EXC_NUMFMT_REUSE( 68, 10 ),
+ EXC_NUMFMT_REUSE( 69, 12 ),
+ EXC_NUMFMT_REUSE( 70, 13 ),
+ EXC_NUMFMT_REUSE( 71, 14 ),
+ EXC_NUMFMT_REUSE( 72, 14 ),
+ EXC_NUMFMT_REUSE( 73, 15 ),
+ EXC_NUMFMT_REUSE( 74, 16 ),
+ EXC_NUMFMT_REUSE( 75, 17 ),
+ EXC_NUMFMT_REUSE( 76, 20 ),
+ EXC_NUMFMT_REUSE( 77, 21 ),
+ EXC_NUMFMT_REUSE( 78, 22 ),
+ EXC_NUMFMT_REUSE( 79, 45 ),
+ EXC_NUMFMT_REUSE( 80, 46 ),
+ EXC_NUMFMT_REUSE( 81, 47 ),
+
+ // 82...163 not used, must not occur in a file (Excel may crash)
+
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ENGLISH --------------------------------------------------------------------
+
+/** Base table for English locales. */
+const XclBuiltInFormat spBuiltInFormats_ENGLISH[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY hh:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_UK[] =
+{
+ EXC_NUMFMT_STRING( 63, UTF8_POUND_UK "#,##0;-" UTF8_POUND_UK "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_POUND_UK "#,##0;[RED]-" UTF8_POUND_UK "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_POUND_UK "#,##0.00;-" UTF8_POUND_UK "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_POUND_UK "#,##0.00;[RED]-" UTF8_POUND_UK "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_EIRE[] =
+{
+ EXC_NUMFMT_STRING( 63, UTF8_EURO "#,##0;-" UTF8_EURO "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO "#,##0;[RED]-" UTF8_EURO "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO "#,##0.00;-" UTF8_EURO "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO "#,##0.00;[RED]-" UTF8_EURO "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_US[] =
+{
+ EXC_NUMFMT_STRING( 14, "M/D/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "M/D/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0_);(#,##0)" ),
+ EXC_NUMFMT_STRING( 38, "#,##0_);[RED](#,##0)" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00_);(#,##0.00)" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00_);[RED](#,##0.00)" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0_);($#,##0)" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0_);[RED]($#,##0)" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00_);($#,##0.00)" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00_);[RED]($#,##0.00)" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_CAN[] =
+{
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_AUS[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/MM/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "D/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ENGLISH_SAFRICA[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY/MM/DD" ),
+ EXC_NUMFMT_OFFSET( 18, NF_TIME_HHMMAMPM ),
+ EXC_NUMFMT_OFFSET( 19, NF_TIME_HHMMSSAMPM ),
+ EXC_NUMFMT_STRING( 22, "YYYY/MM/DD hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\\R #,##0;\\R -#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\\R #,##0;[RED]\\R -#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\\R #,##0.00;\\R -#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\\R #,##0.00;[RED]\\R -#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// FRENCH ---------------------------------------------------------------------
+
+/** Base table for French locales. */
+const XclBuiltInFormat spBuiltInFormats_FRENCH[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_FRANCE[] =
+{
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0\\ _" UTF8_EURO ";-#,##0\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0\\ _" UTF8_EURO ";[RED]-#,##0\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00\\ _" UTF8_EURO ";-#,##0.00\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00\\ _" UTF8_EURO ";[RED]-#,##0.00\\ _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0\\ " UTF8_EURO ";-#,##0\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0\\ " UTF8_EURO ";[RED]-#,##0\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00\\ " UTF8_EURO ";-#,##0.00\\ " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00\\ " UTF8_EURO ";[RED]-#,##0.00\\ " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_CANADIAN[] =
+{
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0\\ _$_-;#,##0\\ _$-" ),
+ EXC_NUMFMT_STRING( 38, "#,##0\\ _$_-;[RED]#,##0\\ _$-" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00\\ _$_-;#,##0.00\\ _$-" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00\\ _$_-;[RED]#,##0.00\\ _$-" ),
+ EXC_NUMFMT_STRING( 63, "#,##0\\ $_-;#,##0\\ $-" ),
+ EXC_NUMFMT_STRING( 64, "#,##0\\ $_-;[RED]#,##0\\ $-" ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00\\ $_-;#,##0.00\\ $-" ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00\\ $_-;[RED]#,##0.00\\ $-" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_FRENCH_BELGIAN[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/MM/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "D/MM/YYYY h:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// GERMAN ---------------------------------------------------------------------
+
+/** Base table for German locales. */
+const XclBuiltInFormat spBuiltInFormats_GERMAN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD. MMM YY" ),
+ EXC_NUMFMT_STRING( 16, "DD. MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_GERMANY[] =
+{
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_AUSTRIAN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 63, UTF8_EURO " #,##0;-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO " #,##0;[RED]-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO " #,##0.00;-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO " #,##0.00;[RED]-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_LUXEMBOURG[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_GERMAN_LIECHTENSTEIN[] =
+{
+ EXC_NUMFMT_STRING( 63, "\"CHF \"#,##0;\"CHF \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"CHF \"#,##0;[RED]\"CHF \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"CHF \"#,##0.00;\"CHF \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"CHF \"#,##0.00;[RED]\"CHF \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ITALIAN --------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_ITALIAN_ITALY[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 22, "DD/MM/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 63, UTF8_EURO " #,##0;-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_EURO " #,##0;[RED]-" UTF8_EURO " #,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_EURO " #,##0.00;-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_EURO " #,##0.00;[RED]-" UTF8_EURO " #,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_ITALIAN_SWISS[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "DD.MM.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 63, "\"SFr. \"#,##0;\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 64, "\"SFr. \"#,##0;[RED]\"SFr. \"-#,##0" ),
+ EXC_NUMFMT_STRING( 65, "\"SFr. \"#,##0.00;\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "\"SFr. \"#,##0.00;[RED]\"SFr. \"-#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// SWEDISH --------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_SWEDISH_SWEDEN[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _k_r;-#,##0 _k_r" ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _k_r;[RED]-#,##0 _k_r" ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _k_r;-#,##0.00 _k_r" ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _k_r;[RED]-#,##0.00 _k_r" ),
+ EXC_NUMFMT_STRING( 63, "#,##0 \"kr\";-#,##0 \"kr\"" ),
+ EXC_NUMFMT_STRING( 64, "#,##0 \"kr\";[RED]-#,##0 \"kr\"" ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 \"kr\";-#,##0.00 \"kr\"" ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 \"kr\";[RED]-#,##0.00 \"kr\"" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_SWEDISH_FINLAND[] =
+{
+ EXC_NUMFMT_STRING( 9, "0 %" ),
+ EXC_NUMFMT_STRING( 10, "0.00 %" ),
+ EXC_NUMFMT_STRING( 15, "DD.MMM.YY" ),
+ EXC_NUMFMT_STRING( 16, "DD.MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM.YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "D.M.YYYY hh:mm" ),
+ EXC_NUMFMT_STRING( 37, "#,##0 _" UTF8_EURO ";-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 38, "#,##0 _" UTF8_EURO ";[RED]-#,##0 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 39, "#,##0.00 _" UTF8_EURO ";-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 40, "#,##0.00 _" UTF8_EURO ";[RED]-#,##0.00 _" UTF8_EURO ),
+ EXC_NUMFMT_STRING( 63, "#,##0 " UTF8_EURO ";-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 64, "#,##0 " UTF8_EURO ";[RED]-#,##0 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 65, "#,##0.00 " UTF8_EURO ";-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_STRING( 66, "#,##0.00 " UTF8_EURO ";[RED]-#,##0.00 " UTF8_EURO ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// ASIAN ----------------------------------------------------------------------
+
+/** Base table for Asian locales. */
+const XclBuiltInFormat spBuiltInFormats_ASIAN[] =
+{
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 20, "h:mm" ),
+ EXC_NUMFMT_STRING( 21, "h:mm:ss" ),
+ EXC_NUMFMT_STRING( 23, "$#,##0_);($#,##0)" ),
+ EXC_NUMFMT_STRING( 24, "$#,##0_);[RED]($#,##0)" ),
+ EXC_NUMFMT_STRING( 25, "$#,##0.00_);($#,##0.00)" ),
+ EXC_NUMFMT_STRING( 26, "$#,##0.00_);[RED]($#,##0.00)" ),
+ EXC_NUMFMT_REUSE( 29, 28 ),
+ EXC_NUMFMT_REUSE( 36, 27 ),
+ EXC_NUMFMT_REUSE( 50, 27 ),
+ EXC_NUMFMT_REUSE( 51, 28 ),
+ EXC_NUMFMT_REUSE( 52, 34 ),
+ EXC_NUMFMT_REUSE( 53, 35 ),
+ EXC_NUMFMT_REUSE( 54, 28 ),
+ EXC_NUMFMT_REUSE( 55, 34 ),
+ EXC_NUMFMT_REUSE( 56, 35 ),
+ EXC_NUMFMT_REUSE( 57, 27 ),
+ EXC_NUMFMT_REUSE( 58, 28 ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_JAPANESE[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY/M/D" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY/M/D h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0411]GE.M.D" ),
+ EXC_NUMFMT_STRING( 28, "[$-0411]GGGE" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0411]M/D/YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0411]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0411]h" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0411]h" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0411]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON ),
+ EXC_NUMFMT_STRING( 35, "[$-0411]M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 63, UTF8_YEN_JP "#,##0;-" UTF8_YEN_JP "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_YEN_JP "#,##0;[RED]-" UTF8_YEN_JP "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_YEN_JP "#,##0.00;-" UTF8_YEN_JP "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_YEN_JP "#,##0.00;[RED]-" UTF8_YEN_JP "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_KOREAN[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY-MM-DD" ),
+ EXC_NUMFMT_STRING( 15, "DD-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-MM-DD h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0412]YYYY" UTF8_CJ_YEAR " MM" UTF8_CJ_MON " DD" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 28, "[$-0412]MM-DD" ),
+ EXC_NUMFMT_STRING( 30, "[$-0412]MM-DD-YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0412]YYYY" UTF8_KO_YEAR " MM" UTF8_KO_MON " DD" UTF8_KO_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0412]h" UTF8_KO_HOUR " mm" UTF8_KO_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0412]h" UTF8_KO_HOUR " mm" UTF8_KO_MIN " ss" UTF8_KO_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0412]YYYY\"/\"MM\"/\"DD" ),
+ EXC_NUMFMT_STRING( 35, "[$-0412]YYYY-MM-DD" ),
+ EXC_NUMFMT_STRING( 63, UTF8_WON "#,##0;-" UTF8_WON "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_WON "#,##0;[RED]-" UTF8_WON "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_WON "#,##0.00;-" UTF8_WON "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_WON "#,##0.00;[RED]-" UTF8_WON "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_CHINESE_SIMPLIFIED[] =
+{
+ EXC_NUMFMT_STRING( 14, "YYYY-M-D" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 22, "YYYY-M-D h:mm" ),
+ EXC_NUMFMT_STRING( 27, "[$-0804]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON ),
+ EXC_NUMFMT_STRING( 28, "[$-0804]M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0804]M-D-YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0804]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0804]h" UTF8_CS_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0804]h" UTF8_CS_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0804]AM/PMh" UTF8_CS_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 35, "[$-0804]AM/PMh" UTF8_CS_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_REUSE( 52, 27 ),
+ EXC_NUMFMT_REUSE( 53, 28 ),
+ EXC_NUMFMT_STRING( 63, UTF8_YEN_CS "#,##0;-" UTF8_YEN_CS "#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_YEN_CS "#,##0;[RED]-" UTF8_YEN_CS "#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_YEN_CS "#,##0.00;-" UTF8_YEN_CS "#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_YEN_CS "#,##0.00;[RED]-" UTF8_YEN_CS "#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_CHINESE_TRADITIONAL[] =
+{
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "hh:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "hh:mm:ss AM/PM" ),
+ EXC_NUMFMT_OFFSET( 20, NF_TIME_HHMM ),
+ EXC_NUMFMT_OFFSET( 21, NF_TIME_HHMMSS ),
+ EXC_NUMFMT_STRING( 22, "YYYY/M/D hh:mm" ),
+ EXC_NUMFMT_STRING( 23, "US$#,##0_);(US$#,##0)" ),
+ EXC_NUMFMT_STRING( 24, "US$#,##0_);[RED](US$#,##0)" ),
+ EXC_NUMFMT_STRING( 25, "US$#,##0.00_);(US$#,##0.00)" ),
+ EXC_NUMFMT_STRING( 26, "US$#,##0.00_);[RED](US$#,##0.00)" ),
+ EXC_NUMFMT_STRING( 27, "[$-0404]E/M/D" ),
+ EXC_NUMFMT_STRING( 28, "[$-0404]E" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 30, "[$-0404]M/D/YY" ),
+ EXC_NUMFMT_STRING( 31, "[$-0404]YYYY" UTF8_CJ_YEAR "M" UTF8_CJ_MON "D" UTF8_CJ_DAY ),
+ EXC_NUMFMT_STRING( 32, "[$-0404]hh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 33, "[$-0404]hh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 34, "[$-0404]AM/PMhh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN ),
+ EXC_NUMFMT_STRING( 35, "[$-0404]AM/PMhh" UTF8_CJ_HOUR "mm" UTF8_CJ_MIN "ss" UTF8_CJ_SEC ),
+ EXC_NUMFMT_STRING( 63, "$#,##0;-$#,##0" ),
+ EXC_NUMFMT_STRING( 64, "$#,##0;[RED]-$#,##0" ),
+ EXC_NUMFMT_STRING( 65, "$#,##0.00;-$#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, "$#,##0.00;[RED]-$#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+// OTHER ----------------------------------------------------------------------
+
+const XclBuiltInFormat spBuiltInFormats_HEBREW[] =
+{
+ EXC_NUMFMT_STRING( 15, "DD-MMMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "DD-MMMM" ),
+ EXC_NUMFMT_STRING( 17, "MMMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 63, UTF8_SHEQEL " #,##0;" UTF8_SHEQEL " -#,##0" ),
+ EXC_NUMFMT_STRING( 64, UTF8_SHEQEL " #,##0;[RED]" UTF8_SHEQEL " -#,##0" ),
+ EXC_NUMFMT_STRING( 65, UTF8_SHEQEL " #,##0.00;" UTF8_SHEQEL " -#,##0.00" ),
+ EXC_NUMFMT_STRING( 66, UTF8_SHEQEL " #,##0.00;[RED]" UTF8_SHEQEL " -#,##0.00" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+const XclBuiltInFormat spBuiltInFormats_THAI[] =
+{
+ EXC_NUMFMT_STRING( 14, "D/M/YYYY" ),
+ EXC_NUMFMT_STRING( 15, "D-MMM-YY" ),
+ EXC_NUMFMT_STRING( 16, "D-MMM" ),
+ EXC_NUMFMT_STRING( 17, "MMM-YY" ),
+ EXC_NUMFMT_STRING( 18, "h:mm AM/PM" ),
+ EXC_NUMFMT_STRING( 19, "h:mm:ss AM/PM" ),
+ EXC_NUMFMT_STRING( 22, "D/M/YYYY h:mm" ),
+ EXC_NUMFMT_STRING( 59, "t0" ),
+ EXC_NUMFMT_STRING( 60, "t0.00" ),
+ EXC_NUMFMT_STRING( 61, "t#,##0" ),
+ EXC_NUMFMT_STRING( 62, "t#,##0.00" ),
+ EXC_NUMFMT_STRING( 63, "t" UTF8_BAHT "#,##0_);t(" UTF8_BAHT "#,##0)" ),
+ EXC_NUMFMT_STRING( 64, "t" UTF8_BAHT "#,##0_);[RED]t(" UTF8_BAHT "#,##0)" ),
+ EXC_NUMFMT_STRING( 65, "t" UTF8_BAHT "#,##0.00_);t(" UTF8_BAHT "#,##0.00)" ),
+ EXC_NUMFMT_STRING( 66, "t" UTF8_BAHT "#,##0.00_);[RED]t(" UTF8_BAHT "#,##0.00)" ),
+ EXC_NUMFMT_STRING( 67, "t0%" ),
+ EXC_NUMFMT_STRING( 68, "t0.00%" ),
+ EXC_NUMFMT_STRING( 69, "t# ?/?" ),
+ EXC_NUMFMT_STRING( 70, "t# ?\?/?\?" ),
+ EXC_NUMFMT_STRING( 71, "tD/M/EE" ),
+ EXC_NUMFMT_STRING( 72, "tD-MMM-E" ),
+ EXC_NUMFMT_STRING( 73, "tD-MMM" ),
+ EXC_NUMFMT_STRING( 74, "tMMM-E" ),
+ EXC_NUMFMT_STRING( 75, "th:mm" ),
+ EXC_NUMFMT_STRING( 76, "th:mm:ss" ),
+ EXC_NUMFMT_STRING( 77, "tD/M/EE h:mm" ),
+ EXC_NUMFMT_STRING( 78, "tmm:ss" ),
+ EXC_NUMFMT_STRING( 79, "t[h]:mm:ss" ),
+ EXC_NUMFMT_STRING( 80, "tmm:ss.0" ),
+ EXC_NUMFMT_STRING( 81, "D/M/E" ),
+ EXC_NUMFMT_ENDTABLE()
+};
+
+#undef EXC_NUMFMT_ENDTABLE
+#undef EXC_NUMFMT_REUSE
+#undef EXC_NUMFMT_OFFSET
+#undef EXC_NUMFMT_STRING
+
+/** Specifies a number format table for a specific language. */
+struct XclBuiltInFormatTable
+{
+ LanguageType meLanguage; /// The language of this table.
+ LanguageType meParentLang; /// The language of the parent table.
+ const XclBuiltInFormat* mpFormats; /// The number format table.
+};
+
+const XclBuiltInFormatTable spBuiltInFormatTables[] =
+{ // language parent language format table
+ { LANGUAGE_DONTKNOW, LANGUAGE_NONE, spBuiltInFormats_DONTKNOW },
+
+ { LANGUAGE_ENGLISH, LANGUAGE_DONTKNOW, spBuiltInFormats_ENGLISH },
+ { LANGUAGE_ENGLISH_UK, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_UK },
+ { LANGUAGE_ENGLISH_EIRE, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_EIRE },
+ { LANGUAGE_ENGLISH_US, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_US },
+ { LANGUAGE_ENGLISH_CAN, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_CAN },
+ { LANGUAGE_ENGLISH_AUS, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_AUS },
+ { LANGUAGE_ENGLISH_SAFRICA, LANGUAGE_ENGLISH, spBuiltInFormats_ENGLISH_SAFRICA },
+ { LANGUAGE_ENGLISH_NZ, LANGUAGE_ENGLISH_AUS, nullptr },
+
+ { PRV_LANGUAGE_FRENCH_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_FRENCH },
+ { LANGUAGE_FRENCH, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_FRANCE },
+ { LANGUAGE_FRENCH_CANADIAN, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_CANADIAN },
+ { LANGUAGE_FRENCH_SWISS, PRV_LANGUAGE_FRENCH_PRIM, spBuiltInFormats_FRENCH_SWISS },
+ { LANGUAGE_FRENCH_BELGIAN, LANGUAGE_FRENCH, spBuiltInFormats_FRENCH_BELGIAN },
+ { LANGUAGE_FRENCH_LUXEMBOURG, LANGUAGE_FRENCH, nullptr },
+ { LANGUAGE_FRENCH_MONACO, LANGUAGE_FRENCH, nullptr },
+
+ { PRV_LANGUAGE_GERMAN_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_GERMAN },
+ { LANGUAGE_GERMAN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_GERMANY },
+ { LANGUAGE_GERMAN_AUSTRIAN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_AUSTRIAN },
+ { LANGUAGE_GERMAN_SWISS, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_SWISS },
+ { LANGUAGE_GERMAN_LUXEMBOURG, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_LUXEMBOURG },
+ { LANGUAGE_GERMAN_LIECHTENSTEIN, PRV_LANGUAGE_GERMAN_PRIM, spBuiltInFormats_GERMAN_LIECHTENSTEIN },
+
+ { LANGUAGE_ITALIAN, LANGUAGE_DONTKNOW, spBuiltInFormats_ITALIAN_ITALY },
+ { LANGUAGE_ITALIAN_SWISS, LANGUAGE_DONTKNOW, spBuiltInFormats_ITALIAN_SWISS },
+
+ { LANGUAGE_SWEDISH, LANGUAGE_DONTKNOW, spBuiltInFormats_SWEDISH_SWEDEN },
+ { LANGUAGE_SWEDISH_FINLAND, LANGUAGE_DONTKNOW, spBuiltInFormats_SWEDISH_FINLAND },
+
+ { PRV_LANGUAGE_ASIAN_PRIM, LANGUAGE_DONTKNOW, spBuiltInFormats_ASIAN },
+ { LANGUAGE_JAPANESE, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_JAPANESE },
+ { LANGUAGE_KOREAN, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_KOREAN },
+ { LANGUAGE_CHINESE_SIMPLIFIED, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_CHINESE_SIMPLIFIED },
+ { LANGUAGE_CHINESE_TRADITIONAL, PRV_LANGUAGE_ASIAN_PRIM, spBuiltInFormats_CHINESE_TRADITIONAL },
+
+ { LANGUAGE_HEBREW, LANGUAGE_DONTKNOW, spBuiltInFormats_HEBREW },
+ { LANGUAGE_THAI, LANGUAGE_DONTKNOW, spBuiltInFormats_THAI }
+};
+
+} // namespace
+
+XclNumFmtBuffer::XclNumFmtBuffer( const XclRoot& rRoot ) :
+ meSysLang( rRoot.GetSysLanguage() ),
+ mnStdScNumFmt( rRoot.GetFormatter().GetStandardIndex( ScGlobal::eLnge ) )
+{
+ // *** insert default formats (BIFF5+ only)***
+
+ if( rRoot.GetBiff() >= EXC_BIFF5 )
+ InsertBuiltinFormats();
+}
+
+void XclNumFmtBuffer::InitializeImport()
+{
+ maFmtMap.clear();
+}
+
+void XclNumFmtBuffer::InsertFormat( sal_uInt16 nXclNumFmt, const OUString& rFormat )
+{
+ XclNumFmt& rNumFmt = maFmtMap[ nXclNumFmt ];
+ rNumFmt.maFormat = rFormat;
+ // #i62053# rFormat may be an empty string, meOffset must be initialized
+ rNumFmt.meOffset = NF_NUMBER_STANDARD;
+ rNumFmt.meLanguage = LANGUAGE_SYSTEM;
+}
+
+void XclNumFmtBuffer::InsertBuiltinFormats()
+{
+ // build a map containing tables for all languages
+ typedef ::std::map< LanguageType, const XclBuiltInFormatTable* > XclBuiltInMap;
+ XclBuiltInMap aBuiltInMap;
+ for(const auto &rTable : spBuiltInFormatTables)
+ aBuiltInMap[ rTable.meLanguage ] = &rTable;
+
+ // build a list of table pointers for the current language, with all parent tables
+ typedef ::std::vector< const XclBuiltInFormatTable* > XclBuiltInVec;
+ XclBuiltInVec aBuiltInVec;
+ for( XclBuiltInMap::const_iterator aMIt = aBuiltInMap.find( meSysLang ), aMEnd = aBuiltInMap.end();
+ aMIt != aMEnd; aMIt = aBuiltInMap.find( aMIt->second->meParentLang ) )
+ aBuiltInVec.push_back( aMIt->second );
+ // language not supported
+ if( aBuiltInVec.empty() )
+ {
+ SAL_WARN("sc", "XclNumFmtBuffer::InsertBuiltinFormats - language not supported (#i29949#) 0x" << std::hex << meSysLang );
+ XclBuiltInMap::const_iterator aMIt = aBuiltInMap.find( LANGUAGE_DONTKNOW );
+ OSL_ENSURE( aMIt != aBuiltInMap.end(), "XclNumFmtBuffer::InsertBuiltinFormats - default map not found" );
+ if( aMIt != aBuiltInMap.end() )
+ aBuiltInVec.push_back( aMIt->second );
+ }
+
+ // insert the default formats in the format map, from root parent to system language
+ std::map< sal_uInt16, sal_uInt16 > aReuseMap;
+ for( XclBuiltInVec::reverse_iterator aVIt = aBuiltInVec.rbegin(), aVEnd = aBuiltInVec.rend(); aVIt != aVEnd; ++aVIt )
+ {
+ // put LANGUAGE_SYSTEM for all entries in default table
+ LanguageType eLang = ((*aVIt)->meLanguage == LANGUAGE_DONTKNOW) ? LANGUAGE_SYSTEM : meSysLang;
+ for( const XclBuiltInFormat* pBuiltIn = (*aVIt)->mpFormats; pBuiltIn && (pBuiltIn->mnXclNumFmt != EXC_FORMAT_NOTFOUND); ++pBuiltIn )
+ {
+ XclNumFmt& rNumFmt = maFmtMap[ pBuiltIn->mnXclNumFmt ];
+
+ rNumFmt.meOffset = pBuiltIn->meOffset;
+ rNumFmt.meLanguage = eLang;
+
+ if( pBuiltIn->mpFormat )
+ rNumFmt.maFormat = OUString( pBuiltIn->mpFormat, strlen(pBuiltIn->mpFormat), RTL_TEXTENCODING_UTF8 );
+ else
+ rNumFmt.maFormat.clear();
+
+ if( pBuiltIn->meOffset == PRV_NF_INDEX_REUSE )
+ aReuseMap[ pBuiltIn->mnXclNumFmt ] = pBuiltIn->mnXclReuseFmt;
+ else
+ aReuseMap.erase( pBuiltIn->mnXclNumFmt );
+ }
+ }
+
+ // copy reused number formats
+ for( const auto& [rXclNumFmt, rXclReuseFmt] : aReuseMap )
+ maFmtMap[ rXclNumFmt ] = maFmtMap[ rXclReuseFmt ];
+}
+
+// Cell formatting data (XF) ==================================================
+
+XclCellProt::XclCellProt() :
+ mbLocked( true ), // default in Excel and Calc
+ mbHidden( false )
+{
+}
+
+bool operator==( const XclCellProt& rLeft, const XclCellProt& rRight )
+{
+ return (rLeft.mbLocked == rRight.mbLocked) && (rLeft.mbHidden == rRight.mbHidden);
+}
+
+XclCellAlign::XclCellAlign() :
+ mnHorAlign( EXC_XF_HOR_GENERAL ),
+ mnVerAlign( EXC_XF_VER_BOTTOM ),
+ mnOrient( EXC_ORIENT_NONE ),
+ mnTextDir( EXC_XF_TEXTDIR_CONTEXT ),
+ mnRotation( EXC_ROT_NONE ),
+ mnIndent( 0 ),
+ mbLineBreak( false ),
+ mbShrink( false )
+{
+}
+
+SvxCellHorJustify XclCellAlign::GetScHorAlign() const
+{
+ SvxCellHorJustify eHorJust = SvxCellHorJustify::Standard;
+ switch( mnHorAlign )
+ {
+ case EXC_XF_HOR_GENERAL: eHorJust = SvxCellHorJustify::Standard; break;
+ case EXC_XF_HOR_LEFT: eHorJust = SvxCellHorJustify::Left; break;
+ case EXC_XF_HOR_CENTER_AS:
+ case EXC_XF_HOR_CENTER: eHorJust = SvxCellHorJustify::Center; break;
+ case EXC_XF_HOR_RIGHT: eHorJust = SvxCellHorJustify::Right; break;
+ case EXC_XF_HOR_FILL: eHorJust = SvxCellHorJustify::Repeat; break;
+ case EXC_XF_HOR_JUSTIFY:
+ case EXC_XF_HOR_DISTRIB: eHorJust = SvxCellHorJustify::Block; break;
+ default: OSL_FAIL( "XclCellAlign::GetScHorAlign - unknown horizontal alignment" );
+ }
+ return eHorJust;
+}
+
+SvxCellJustifyMethod XclCellAlign::GetScHorJustifyMethod() const
+{
+ return (mnHorAlign == EXC_XF_HOR_DISTRIB) ? SvxCellJustifyMethod::Distribute : SvxCellJustifyMethod::Auto;
+}
+
+SvxCellVerJustify XclCellAlign::GetScVerAlign() const
+{
+ SvxCellVerJustify eVerJust = SvxCellVerJustify::Standard;
+ switch( mnVerAlign )
+ {
+ case EXC_XF_VER_TOP: eVerJust = SvxCellVerJustify::Top; break;
+ case EXC_XF_VER_CENTER: eVerJust = SvxCellVerJustify::Center; break;
+ case EXC_XF_VER_BOTTOM: eVerJust = SvxCellVerJustify::Standard; break;
+ case EXC_XF_VER_JUSTIFY:
+ case EXC_XF_VER_DISTRIB: eVerJust = SvxCellVerJustify::Block; break;
+ default: OSL_FAIL( "XclCellAlign::GetScVerAlign - unknown vertical alignment" );
+ }
+ return eVerJust;
+}
+
+SvxCellJustifyMethod XclCellAlign::GetScVerJustifyMethod() const
+{
+ return (mnVerAlign == EXC_XF_VER_DISTRIB) ? SvxCellJustifyMethod::Distribute : SvxCellJustifyMethod::Auto;
+}
+
+SvxFrameDirection XclCellAlign::GetScFrameDir() const
+{
+ SvxFrameDirection eFrameDir = SvxFrameDirection::Environment;
+ switch( mnTextDir )
+ {
+ case EXC_XF_TEXTDIR_CONTEXT: eFrameDir = SvxFrameDirection::Environment; break;
+ case EXC_XF_TEXTDIR_LTR: eFrameDir = SvxFrameDirection::Horizontal_LR_TB; break;
+ case EXC_XF_TEXTDIR_RTL: eFrameDir = SvxFrameDirection::Horizontal_RL_TB; break;
+ default: OSL_FAIL( "XclCellAlign::GetScFrameDir - unknown CTL text direction" );
+ }
+ return eFrameDir;
+}
+
+void XclCellAlign::SetScHorAlign( SvxCellHorJustify eHorJust )
+{
+ switch( eHorJust )
+ {
+ case SvxCellHorJustify::Standard: mnHorAlign = EXC_XF_HOR_GENERAL; break;
+ case SvxCellHorJustify::Left: mnHorAlign = EXC_XF_HOR_LEFT; break;
+ case SvxCellHorJustify::Center: mnHorAlign = EXC_XF_HOR_CENTER; break;
+ case SvxCellHorJustify::Right: mnHorAlign = EXC_XF_HOR_RIGHT; break;
+ case SvxCellHorJustify::Block: mnHorAlign = EXC_XF_HOR_JUSTIFY; break;
+ case SvxCellHorJustify::Repeat: mnHorAlign = EXC_XF_HOR_FILL; break;
+ default: mnHorAlign = EXC_XF_HOR_GENERAL;
+ OSL_FAIL( "XclCellAlign::SetScHorAlign - unknown horizontal alignment" );
+ }
+}
+
+void XclCellAlign::SetScVerAlign( SvxCellVerJustify eVerJust )
+{
+ switch( eVerJust )
+ {
+ case SvxCellVerJustify::Standard: mnVerAlign = EXC_XF_VER_BOTTOM; break;
+ case SvxCellVerJustify::Top: mnVerAlign = EXC_XF_VER_TOP; break;
+ case SvxCellVerJustify::Center: mnVerAlign = EXC_XF_VER_CENTER; break;
+ case SvxCellVerJustify::Bottom: mnVerAlign = EXC_XF_VER_BOTTOM; break;
+ default: mnVerAlign = EXC_XF_VER_BOTTOM;
+ OSL_FAIL( "XclCellAlign::SetScVerAlign - unknown vertical alignment" );
+ }
+}
+
+void XclCellAlign::SetScFrameDir( SvxFrameDirection eFrameDir )
+{
+ switch( eFrameDir )
+ {
+ case SvxFrameDirection::Environment: mnTextDir = EXC_XF_TEXTDIR_CONTEXT; break;
+ case SvxFrameDirection::Horizontal_LR_TB: mnTextDir = EXC_XF_TEXTDIR_LTR; break;
+ case SvxFrameDirection::Horizontal_RL_TB: mnTextDir = EXC_XF_TEXTDIR_RTL; break;
+ default: mnTextDir = EXC_XF_TEXTDIR_CONTEXT;
+ OSL_FAIL( "XclCellAlign::SetScFrameDir - unknown CTL text direction" );
+ }
+}
+
+bool operator==( const XclCellAlign& rLeft, const XclCellAlign& rRight )
+{
+ return
+ (rLeft.mnHorAlign == rRight.mnHorAlign) && (rLeft.mnVerAlign == rRight.mnVerAlign) &&
+ (rLeft.mnTextDir == rRight.mnTextDir) && (rLeft.mnOrient == rRight.mnOrient) &&
+ (rLeft.mnRotation == rRight.mnRotation) && (rLeft.mnIndent == rRight.mnIndent) &&
+ (rLeft.mbLineBreak == rRight.mbLineBreak) && (rLeft.mbShrink == rRight.mbShrink);
+}
+
+XclCellBorder::XclCellBorder() :
+ mnLeftColor( 0 ),
+ mnRightColor( 0 ),
+ mnTopColor( 0 ),
+ mnBottomColor( 0 ),
+ mnDiagColor( 0 ),
+ mnLeftLine( EXC_LINE_NONE ),
+ mnRightLine( EXC_LINE_NONE ),
+ mnTopLine( EXC_LINE_NONE ),
+ mnBottomLine( EXC_LINE_NONE ),
+ mnDiagLine( EXC_LINE_NONE ),
+ mbDiagTLtoBR( false ),
+ mbDiagBLtoTR( false )
+{
+}
+
+bool operator==( const XclCellBorder& rLeft, const XclCellBorder& rRight )
+{
+ return
+ (rLeft.mnLeftColor == rRight.mnLeftColor) && (rLeft.mnRightColor == rRight.mnRightColor) &&
+ (rLeft.mnTopColor == rRight.mnTopColor) && (rLeft.mnBottomColor == rRight.mnBottomColor) &&
+ (rLeft.mnLeftLine == rRight.mnLeftLine) && (rLeft.mnRightLine == rRight.mnRightLine) &&
+ (rLeft.mnTopLine == rRight.mnTopLine) && (rLeft.mnBottomLine == rRight.mnBottomLine) &&
+ (rLeft.mnDiagColor == rRight.mnDiagColor) && (rLeft.mnDiagLine == rRight.mnDiagLine) &&
+ (rLeft.mbDiagTLtoBR == rRight.mbDiagTLtoBR) && (rLeft.mbDiagBLtoTR == rRight.mbDiagBLtoTR);
+}
+
+XclCellArea::XclCellArea() :
+ mnForeColor( EXC_COLOR_WINDOWTEXT ),
+ mnBackColor( EXC_COLOR_WINDOWBACK ),
+ mnPattern( EXC_PATT_NONE )
+{
+}
+
+XclCellArea::XclCellArea(sal_uInt8 nPattern) :
+ mnForeColor( EXC_COLOR_WINDOWTEXT ),
+ mnBackColor( EXC_COLOR_WINDOWBACK ),
+ mnPattern( nPattern )
+{
+}
+
+bool XclCellArea::IsTransparent() const
+{
+ return (mnPattern == EXC_PATT_NONE) && (mnBackColor == EXC_COLOR_WINDOWBACK);
+}
+
+bool operator==( const XclCellArea& rLeft, const XclCellArea& rRight )
+{
+ return
+ (rLeft.mnForeColor == rRight.mnForeColor) && (rLeft.mnBackColor == rRight.mnBackColor) &&
+ (rLeft.mnPattern == rRight.mnPattern);
+}
+
+XclXFBase::XclXFBase( bool bCellXF ) :
+ mnParent( bCellXF ? EXC_XF_DEFAULTSTYLE : EXC_XF_STYLEPARENT ),
+ mbCellXF( bCellXF )
+{
+ SetAllUsedFlags( false );
+}
+
+XclXFBase::~XclXFBase()
+{
+}
+
+void XclXFBase::SetAllUsedFlags( bool bUsed )
+{
+ mbProtUsed = mbFontUsed = mbFmtUsed = mbAlignUsed = mbBorderUsed = mbAreaUsed = bUsed;
+}
+
+bool XclXFBase::HasUsedFlags() const
+{
+ return mbProtUsed || mbFontUsed || mbFmtUsed || mbAlignUsed || mbBorderUsed || mbAreaUsed;
+}
+
+bool XclXFBase::Equals( const XclXFBase& rCmp ) const
+{
+ return
+ (mbCellXF == rCmp.mbCellXF) && (mnParent == rCmp.mnParent) &&
+ (mbProtUsed == rCmp.mbProtUsed) && (mbFontUsed == rCmp.mbFontUsed) &&
+ (mbFmtUsed == rCmp.mbFmtUsed) && (mbAlignUsed == rCmp.mbAlignUsed) &&
+ (mbBorderUsed == rCmp.mbBorderUsed) && (mbAreaUsed == rCmp.mbAreaUsed);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltoolbar.cxx b/sc/source/filter/excel/xltoolbar.cxx
new file mode 100644
index 000000000..efb54925b
--- /dev/null
+++ b/sc/source/filter/excel/xltoolbar.cxx
@@ -0,0 +1,439 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#include "xltoolbar.hxx"
+#include <sal/log.hxx>
+#include <o3tl/safeint.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/ui/XUIConfigurationPersistence.hpp>
+#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <rtl/ref.hxx>
+#include <map>
+
+using namespace com::sun::star;
+
+typedef std::map< sal_Int16, OUString > IdToString;
+
+namespace {
+
+class MSOExcelCommandConvertor : public MSOCommandConvertor
+{
+ IdToString msoToOOcmd;
+ IdToString tcidToOOcmd;
+public:
+ MSOExcelCommandConvertor();
+ virtual OUString MSOCommandToOOCommand( sal_Int16 msoCmd ) override;
+ virtual OUString MSOTCIDToOOCommand( sal_Int16 key ) override;
+};
+
+}
+
+MSOExcelCommandConvertor::MSOExcelCommandConvertor()
+{
+/*
+ // mso command id to ooo command string
+ // #FIXME and *HUNDREDS* of id's to added here
+ msoToOOcmd[ 0x20b ] = ".uno:CloseDoc";
+ msoToOOcmd[ 0x50 ] = ".uno:Open";
+
+ // mso tcid to ooo command string
+ // #FIXME and *HUNDREDS* of id's to added here
+ tcidToOOcmd[ 0x9d9 ] = ".uno:Print";
+*/
+}
+
+OUString MSOExcelCommandConvertor::MSOCommandToOOCommand( sal_Int16 key )
+{
+ OUString sResult;
+ IdToString::iterator it = msoToOOcmd.find( key );
+ if ( it != msoToOOcmd.end() )
+ sResult = it->second;
+ return sResult;
+}
+
+OUString MSOExcelCommandConvertor::MSOTCIDToOOCommand( sal_Int16 key )
+{
+ OUString sResult;
+ IdToString::iterator it = tcidToOOcmd.find( key );
+ if ( it != tcidToOOcmd.end() )
+ sResult = it->second;
+ return sResult;
+}
+
+CTBS::CTBS() : bSignature(0), bVersion(0), reserved1(0), reserved2(0), reserved3(0), ctb(0), ctbViews(0), ictbView(0)
+{
+}
+
+ScCTB::ScCTB(sal_uInt16 nNum ) : nViews( nNum ), ectbid(0)
+{
+}
+
+bool ScCTB::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ tb.Read( rS );
+
+ {
+ const size_t nMinRecordSize = 20; // TBVisualData reads 20 bytes
+ const size_t nMaxPossibleRecords = rS.remainingSize() / nMinRecordSize;
+ if (nViews > nMaxPossibleRecords)
+ {
+ SAL_WARN("sc.filter", "ScCTB::Read more entries claimed than stream could contain");
+ return false;
+ }
+ }
+
+ for ( sal_uInt16 index = 0; index < nViews; ++index )
+ {
+ TBVisualData aVisData;
+ aVisData.Read( rS );
+ rVisualData.push_back( aVisData );
+ }
+ rS.ReadUInt32( ectbid );
+
+ sal_Int16 nCL = tb.getcCL();
+ if (nCL > 0)
+ {
+ auto nIndexes = o3tl::make_unsigned(nCL);
+
+ const size_t nMinRecordSize = 11; // ScTBC's TBCHeader reads min 11 bytes
+ const size_t nMaxPossibleRecords = rS.remainingSize() / nMinRecordSize;
+ if (nIndexes > nMaxPossibleRecords)
+ {
+ SAL_WARN("sc.filter", "ScCTB::Read more entries claimed than stream could contain");
+ return false;
+ }
+
+ for (decltype(nIndexes) index = 0; index < nIndexes; ++index)
+ {
+ ScTBC aTBC;
+ aTBC.Read( rS );
+ rTBC.push_back( aTBC );
+ }
+ }
+
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void ScCTB::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScCTB -- dump\n", nOffSet );
+ indent_printf( fp, " nViews 0x%x\n", nViews);
+ tb.Print( fp );
+
+ sal_Int32 counter = 0;
+ for ( auto& rItem : rVisualData )
+ {
+ indent_printf( fp, " TBVisualData [%d]\n", counter++ );
+ Indent b;
+ rItem.Print( fp );
+ }
+ indent_printf( fp, " ectbid 0x%x\n", ectbid);
+ counter = 0;
+ for ( auto& rItem : rTBC )
+ {
+ indent_printf( fp, " ScTBC [%d]\n", counter++);
+ Indent c;
+ rItem.Print( fp );
+ }
+}
+#endif
+
+bool ScCTB::IsMenuToolbar() const
+{
+ return tb.IsMenuToolbar();
+}
+
+bool ScCTB::ImportMenuTB( ScCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& xMenuDesc, CustomToolBarImportHelper& helper )
+{
+ for ( auto& rItem : rTBC )
+ {
+ if ( !rItem.ImportToolBarControl( rWrapper, xMenuDesc, helper, IsMenuToolbar() ) )
+ return false;
+ }
+ return true;
+}
+
+bool ScCTB::ImportCustomToolBar( ScCTBWrapper& rWrapper, CustomToolBarImportHelper& helper )
+{
+
+ bool bRes = false;
+ try
+ {
+ if ( !tb.IsEnabled() )
+ return true; // didn't fail, just ignoring
+
+ // Create default setting
+ uno::Reference< container::XIndexContainer > xIndexContainer( helper.getCfgManager()->createSettings(), uno::UNO_SET_THROW );
+ uno::Reference< container::XIndexAccess > xIndexAccess( xIndexContainer, uno::UNO_QUERY_THROW );
+ uno::Reference< beans::XPropertySet > xProps( xIndexContainer, uno::UNO_QUERY_THROW );
+ WString& name = tb.getName();
+ // set UI name for toolbar
+ xProps->setPropertyValue("UIName", uno::Any( name.getString() ) );
+
+ OUString sToolBarName = "private:resource/toolbar/custom_" + name.getString();
+ for ( auto& rItem : rTBC )
+ {
+ if ( !rItem.ImportToolBarControl( rWrapper, xIndexContainer, helper, IsMenuToolbar() ) )
+ return false;
+ }
+
+ helper.getCfgManager()->insertSettings( sToolBarName, xIndexAccess );
+ helper.applyIcons();
+
+ uno::Reference< ui::XUIConfigurationPersistence > xPersistence( helper.getCfgManager()->getImageManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+
+ xPersistence.set( helper.getCfgManager(), uno::UNO_QUERY_THROW );
+ xPersistence->store();
+
+ bRes = true;
+ }
+ catch( uno::Exception& )
+ {
+ bRes = false;
+ }
+ return bRes;
+}
+bool CTBS::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ rS.ReadUChar( bSignature ).ReadUChar( bVersion ).ReadUInt16( reserved1 ).ReadUInt16( reserved2 ).ReadUInt16( reserved3 ).ReadUInt16( ctb ).ReadUInt16( ctbViews ).ReadUInt16( ictbView );
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void CTBS::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] CTBS -- dump\n", nOffSet );
+
+ indent_printf( fp, " bSignature 0x%x\n", bSignature);
+ indent_printf( fp, " bVersion 0x%x\n", bVersion);
+
+ indent_printf( fp, " reserved1 0x%x\n", reserved1 );
+ indent_printf( fp, " reserved2 0x%x\n", reserved2 );
+ indent_printf( fp, " reserved3 0x%x\n", reserved3 );
+
+ indent_printf( fp, " ctb 0x%x\n", ctb );
+ indent_printf( fp, " ctbViews 0x%x\n", ctbViews );
+ indent_printf( fp, " ictbView 0x%x\n", ictbView );
+}
+#endif
+
+ScTBC::ScTBC()
+{
+}
+
+bool
+ScTBC::Read(SvStream &rS)
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ if ( !tbch.Read( rS ) )
+ return false;
+ sal_uInt16 tcid = tbch.getTcID();
+ sal_uInt8 tct = tbch.getTct();
+ if ( ( tcid != 0x0001 && tcid != 0x06CC && tcid != 0x03D8 && tcid != 0x03EC && tcid != 0x1051 ) && ( ( tct > 0 && tct < 0x0B ) || ( ( tct > 0x0B && tct < 0x10 ) || tct == 0x15 ) ) )
+ {
+ tbcCmd = std::make_shared<TBCCmd>();
+ if ( ! tbcCmd->Read( rS ) )
+ return false;
+ }
+ if ( tct != 0x16 )
+ {
+ tbcd = std::make_shared<TBCData>( tbch );
+ if ( !tbcd->Read( rS ) )
+ return false;
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+ScTBC::Print(FILE* fp)
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScTBC -- dump\n", nOffSet );
+ tbch.Print( fp );
+ if ( tbcCmd.get() )
+ tbcCmd->Print( fp );
+ if ( tbcd.get() )
+ tbcd->Print( fp );
+}
+#endif
+
+bool ScTBC::ImportToolBarControl( ScCTBWrapper& rWrapper, const css::uno::Reference< css::container::XIndexContainer >& toolbarcontainer, CustomToolBarImportHelper& helper, bool bIsMenuToolbar )
+{
+ // how to identify built-in-command ?
+// bool bBuiltin = false;
+ if ( tbcd )
+ {
+ std::vector< css::beans::PropertyValue > props;
+ bool bBeginGroup = false;
+ tbcd->ImportToolBarControl( helper, props, bBeginGroup, bIsMenuToolbar );
+ TBCMenuSpecific* pMenu = tbcd->getMenuSpecific();
+ if ( pMenu )
+ {
+ // search for ScCTB with the appropriate name ( it contains the
+ // menu items, although we cannot import ( or create ) a menu on
+ // a custom toolbar we can import the menu items in a separate
+ // toolbar ( better than nothing )
+ ScCTB* pCustTB = rWrapper.GetCustomizationData( pMenu->Name() );
+ if ( pCustTB )
+ {
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xMenuDesc = new comphelper::IndexedPropertyValuesContainer();
+ if ( !pCustTB->ImportMenuTB( rWrapper, xMenuDesc, helper ) )
+ return false;
+ if ( !bIsMenuToolbar )
+ {
+ if ( !helper.createMenu( pMenu->Name(), xMenuDesc ) )
+ return false;
+ }
+ else
+ {
+ beans::PropertyValue aProp;
+ aProp.Name = "ItemDescriptorContainer";
+ aProp.Value <<= uno::Reference< container::XIndexContainer >(xMenuDesc);
+ props.push_back( aProp );
+ }
+ }
+ }
+
+ if ( bBeginGroup )
+ {
+ // insert spacer
+ uno::Sequence sProps{ comphelper::makePropertyValue("Type",
+ ui::ItemType::SEPARATOR_LINE) };
+ toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::Any( sProps ) );
+ }
+ toolbarcontainer->insertByIndex( toolbarcontainer->getCount(), uno::Any( comphelper::containerToSequence(props) ) );
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+TBCCmd::Print(FILE* fp)
+{
+ Indent a;
+ indent_printf( fp, " TBCCmd -- dump\n" );
+ indent_printf( fp, " cmdID 0x%x\n", cmdID );
+ indent_printf( fp, " A ( fHideDrawing ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " B ( reserved - ignored ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " cmdType 0x%x\n", cmdType );
+ indent_printf( fp, " C ( reserved - ignored ) %s\n", A ? "true" : "false" );
+ indent_printf( fp, " reserved3 0x%x\n", reserved3 );
+}
+#endif
+
+bool TBCCmd::Read( SvStream &rS )
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ rS.ReadUInt16( cmdID );
+ sal_uInt16 temp;
+ rS.ReadUInt16( temp );
+ A = (temp & 0x8000 ) == 0x8000;
+ B = (temp & 0x4000) == 0x4000;
+ cmdType = ( temp & 0x3E00 ) >> 9;
+ C = ( temp & 0x100 ) == 0x100;
+ reserved3 = ( temp & 0xFF );
+ return true;
+}
+
+ScCTBWrapper::ScCTBWrapper()
+{
+}
+
+ScCTBWrapper::~ScCTBWrapper()
+{
+}
+
+bool
+ScCTBWrapper::Read( SvStream &rS)
+{
+ SAL_INFO("sc.filter", "stream pos " << rS.Tell());
+ nOffSet = rS.Tell();
+ if (!ctbSet.Read(rS))
+ return false;
+
+ //ScCTB is 1 TB which is min 15bytes, nViews TBVisualData which is min 20bytes
+ //and one 32bit number (4 bytes)
+ const size_t nMinRecordSize = 19 + o3tl::sanitizing_min(ctbSet.ctbViews * 20, 0);
+ const size_t nMaxPossibleRecords = rS.remainingSize()/nMinRecordSize;
+ if (ctbSet.ctb > nMaxPossibleRecords)
+ return false;
+
+ for ( sal_uInt16 index = 0; index < ctbSet.ctb; ++index )
+ {
+ ScCTB aCTB( ctbSet.ctbViews );
+ if ( !aCTB.Read( rS ) )
+ return false;
+ rCTB.push_back( aCTB );
+ }
+ return true;
+}
+
+#ifdef DEBUG_SC_EXCEL
+void
+ScCTBWrapper::Print( FILE* fp )
+{
+ Indent a;
+ indent_printf( fp, "[ 0x%x ] ScCTBWrapper -- dump\n", nOffSet );
+ ctbSet.Print( fp );
+ for ( auto& rItem : rCTB )
+ {
+ Indent b;
+ rItem.Print( fp );
+ }
+}
+#endif
+
+ScCTB* ScCTBWrapper::GetCustomizationData( const OUString& sTBName )
+{
+ ScCTB* pCTB = nullptr;
+ auto it = std::find_if(rCTB.begin(), rCTB.end(), [&sTBName](ScCTB& rItem) { return rItem.GetName() == sTBName; });
+ if (it != rCTB.end())
+ pCTB = &(*it);
+ return pCTB;
+}
+
+void ScCTBWrapper::ImportCustomToolBar( SfxObjectShell& rDocSh )
+{
+ if(rCTB.empty())
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference< ui::XModuleUIConfigurationManagerSupplier > xAppCfgSupp( ui::theModuleUIConfigurationManagerSupplier::get(xContext) );
+
+ for ( auto& rItem : rCTB )
+ {
+ // for each customtoolbar
+ CustomToolBarImportHelper helper( rDocSh, xAppCfgSupp->getUIConfigurationManager( "com.sun.star.sheet.SpreadsheetDocument" ) );
+ helper.setMSOCommandMap( new MSOExcelCommandConvertor() );
+ // Ignore menu toolbars, excel doesn't ( afaics ) store
+ // menu customizations ( but you can have menus in a customtoolbar
+ // such menus will be dealt with when they are encountered
+ // as part of importing the appropriate MenuSpecific toolbar control )
+
+ if ( !rItem.IsMenuToolbar() && !rItem.ImportCustomToolBar( *this, helper ) )
+ return;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltoolbar.hxx b/sc/source/filter/excel/xltoolbar.hxx
new file mode 100644
index 000000000..f7da78b2f
--- /dev/null
+++ b/sc/source/filter/excel/xltoolbar.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <filter/msfilter/mstoolbar.hxx>
+
+namespace com::sun::star::container { class XIndexContainer; }
+
+class ScCTBWrapper;
+// hmm I don't normally use these packed structures
+// but... hey always good to do something different
+class TBCCmd : public TBBase
+{
+public:
+ TBCCmd() : cmdID(0), A(false), B(false), cmdType(0), C(false), reserved3(0)
+ {}
+ sal_uInt16 cmdID;
+ bool A:1;
+ bool B:1;
+ sal_uInt16 cmdType:5;
+ bool C:1;
+ sal_uInt16 reserved3:8;
+ bool Read( SvStream& rS ) override;
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print(FILE* fp) override;
+#endif
+};
+
+class ScTBC : public TBBase
+{
+ TBCHeader tbch;
+ std::shared_ptr<TBCCmd> tbcCmd; // optional
+ std::shared_ptr<TBCData> tbcd;
+public:
+ ScTBC();
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+ bool ImportToolBarControl( ScCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >& toolbarcontainer, CustomToolBarImportHelper& helper, bool bIsMenuBar );
+};
+
+class ScCTB : public TBBase
+{
+ sal_uInt16 nViews;
+ TB tb;
+ std::vector<TBVisualData> rVisualData;
+ sal_uInt32 ectbid;
+ std::vector< ScTBC > rTBC;
+public:
+ explicit ScCTB(sal_uInt16);
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+ bool IsMenuToolbar() const;
+ bool ImportCustomToolBar( ScCTBWrapper&, CustomToolBarImportHelper& );
+ bool ImportMenuTB( ScCTBWrapper&, const css::uno::Reference< css::container::XIndexContainer >&, CustomToolBarImportHelper& );
+ const OUString& GetName() { return tb.getName().getString(); }
+
+};
+
+class CTBS : public TBBase
+{
+public:
+ sal_uInt8 bSignature;
+ sal_uInt8 bVersion;
+ sal_uInt16 reserved1;
+ sal_uInt16 reserved2;
+ sal_uInt16 reserved3;
+ sal_uInt16 ctb;
+ sal_uInt16 ctbViews;
+ sal_uInt16 ictbView;
+ CTBS(const CTBS&);
+ CTBS& operator = ( const CTBS&);
+ CTBS();
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ bool Read(SvStream &rS) override;
+};
+
+class ScCTBWrapper : public TBBase
+{
+ CTBS ctbSet;
+
+ std::vector< ScCTB > rCTB;
+
+public:
+ ScCTBWrapper();
+ virtual ~ScCTBWrapper() override;
+ bool Read(SvStream &rS) override;
+#ifdef DEBUG_SC_EXCEL
+ virtual void Print( FILE* ) override;
+#endif
+ void ImportCustomToolBar( SfxObjectShell& rDocSh );
+ ScCTB* GetCustomizationData( const OUString& name );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltools.cxx b/sc/source/filter/excel/xltools.cxx
new file mode 100644
index 000000000..3a69e7da0
--- /dev/null
+++ b/sc/source/filter/excel/xltools.cxx
@@ -0,0 +1,744 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+#include <math.h>
+#include <string_view>
+
+#include <o3tl/unit_conversion.hxx>
+#include <sal/mathconf.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <tools/solar.h>
+#include <o3tl/string_view.hxx>
+#include <unotools/fontdefs.hxx>
+#include <filter/msfilter/msvbahelper.hxx>
+#include <xestream.hxx>
+#include <formula/errorcodes.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <xlstyle.hxx>
+#include <xlname.hxx>
+#include <xistream.hxx>
+#include <xltools.hxx>
+
+// GUID import/export
+
+XclGuid::XclGuid()
+ : mpnData{}
+{
+}
+
+XclGuid::XclGuid(
+ sal_uInt32 nData1, sal_uInt16 nData2, sal_uInt16 nData3,
+ sal_uInt8 nData41, sal_uInt8 nData42, sal_uInt8 nData43, sal_uInt8 nData44,
+ sal_uInt8 nData45, sal_uInt8 nData46, sal_uInt8 nData47, sal_uInt8 nData48 )
+{
+ // convert to little endian -> makes streaming easy
+ UInt32ToSVBT32( nData1, mpnData );
+ ShortToSVBT16( nData2, mpnData + 4 );
+ ShortToSVBT16( nData3, mpnData + 6 );
+ mpnData[ 8 ] = nData41;
+ mpnData[ 9 ] = nData42;
+ mpnData[ 10 ] = nData43;
+ mpnData[ 11 ] = nData44;
+ mpnData[ 12 ] = nData45;
+ mpnData[ 13 ] = nData46;
+ mpnData[ 14 ] = nData47;
+ mpnData[ 15 ] = nData48;
+}
+
+bool operator==( const XclGuid& rCmp1, const XclGuid& rCmp2 )
+{
+ return ::std::equal( rCmp1.mpnData, std::end( rCmp1.mpnData ), rCmp2.mpnData );
+}
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclGuid& rGuid )
+{
+ rStrm.Read( rGuid.mpnData, 16 ); // mpnData always in little endian
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const XclGuid& rGuid )
+{
+ rStrm.Write( rGuid.mpnData, 16 ); // mpnData already in little endian
+ return rStrm;
+}
+
+// Excel Tools
+
+// GUID's
+const XclGuid XclTools::maGuidStdLink(
+ 0x79EAC9D0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B );
+
+const XclGuid XclTools::maGuidUrlMoniker(
+ 0x79EAC9E0, 0xBAF9, 0x11CE, 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B );
+
+const XclGuid XclTools::maGuidFileMoniker(
+ 0x00000303, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 );
+
+// numeric conversion
+
+double XclTools::GetDoubleFromRK( sal_Int32 nRKValue )
+{
+ union
+ {
+ double fVal;
+ sal_math_Double smD;
+ };
+ fVal = 0.0;
+
+ if( ::get_flag( nRKValue, EXC_RK_INTFLAG ) )
+ {
+ sal_Int32 nTemp = nRKValue >> 2;
+ ::set_flag< sal_Int32 >( nTemp, 0xE0000000, nRKValue < 0 );
+ fVal = nTemp;
+ }
+ else
+ {
+ smD.w32_parts.msw = nRKValue & EXC_RK_VALUEMASK;
+ }
+
+ if( ::get_flag( nRKValue, EXC_RK_100FLAG ) )
+ fVal /= 100.0;
+
+ return fVal;
+}
+
+bool XclTools::GetRKFromDouble( sal_Int32& rnRKValue, double fValue )
+{
+ double fFrac, fInt;
+
+ // integer
+ fFrac = modf( fValue, &fInt );
+ if( (fFrac == 0.0) && (fInt >= -536870912.0) && (fInt <= 536870911.0) ) // 2^29
+ {
+ rnRKValue
+ = static_cast<sal_Int32>(
+ static_cast<sal_uInt32>(static_cast<sal_Int32>(fInt)) << 2)
+ | EXC_RK_INT;
+ return true;
+ }
+
+ // integer/100
+ fFrac = modf( fValue * 100.0, &fInt );
+ if( (fFrac == 0.0) && (fInt >= -536870912.0) && (fInt <= 536870911.0) )
+ {
+ rnRKValue
+ = static_cast<sal_Int32>(
+ static_cast<sal_uInt32>(static_cast<sal_Int32>(fInt)) << 2)
+ | EXC_RK_INT100;
+ return true;
+ }
+
+ // double
+ return false;
+}
+
+Degree100 XclTools::GetScRotation( sal_uInt16 nXclRot, Degree100 nRotForStacked )
+{
+ if( nXclRot == EXC_ROT_STACKED )
+ return nRotForStacked;
+ OSL_ENSURE( nXclRot <= 180, "XclTools::GetScRotation - illegal rotation angle" );
+ return Degree100(static_cast< sal_Int32 >( (nXclRot <= 180) ? (100 * ((nXclRot > 90) ? (450 - nXclRot) : nXclRot)) : 0 ));
+}
+
+sal_uInt8 XclTools::GetXclRotation( Degree100 nScRot )
+{
+ sal_Int32 nXclRot = nScRot.get() / 100;
+ if( (0 <= nXclRot) && (nXclRot <= 90) )
+ return static_cast< sal_uInt8 >( nXclRot );
+ if( nXclRot < 180 )
+ return static_cast< sal_uInt8 >( 270 - nXclRot );
+ if( nXclRot < 270 )
+ return static_cast< sal_uInt8 >( nXclRot - 180 );
+ if( nXclRot < 360 )
+ return static_cast< sal_uInt8 >( 450 - nXclRot );
+ return 0;
+}
+
+sal_uInt8 XclTools::GetXclRotFromOrient( sal_uInt8 nXclOrient )
+{
+ switch( nXclOrient )
+ {
+ case EXC_ORIENT_NONE: return EXC_ROT_NONE;
+ case EXC_ORIENT_STACKED: return EXC_ROT_STACKED;
+ case EXC_ORIENT_90CCW: return EXC_ROT_90CCW;
+ case EXC_ORIENT_90CW: return EXC_ROT_90CW;
+ default: OSL_FAIL( "XclTools::GetXclRotFromOrient - unknown text orientation" );
+ }
+ return EXC_ROT_NONE;
+}
+
+sal_uInt8 XclTools::GetXclOrientFromRot( sal_uInt16 nXclRot )
+{
+ if( nXclRot == EXC_ROT_STACKED )
+ return EXC_ORIENT_STACKED;
+ OSL_ENSURE( nXclRot <= 180, "XclTools::GetXclOrientFromRot - unknown text rotation" );
+ if( (45 < nXclRot) && (nXclRot <= 90) )
+ return EXC_ORIENT_90CCW;
+ if( (135 < nXclRot) && (nXclRot <= 180) )
+ return EXC_ORIENT_90CW;
+ return EXC_ORIENT_NONE;
+}
+
+sal_uInt8 XclTools::GetXclErrorCode( FormulaError nScError )
+{
+ switch( nScError )
+ {
+ case FormulaError::IllegalArgument: return EXC_ERR_VALUE;
+ case FormulaError::IllegalFPOperation: return EXC_ERR_NUM; // maybe DIV/0 or NUM...
+ case FormulaError::DivisionByZero: return EXC_ERR_DIV0;
+ case FormulaError::IllegalParameter: return EXC_ERR_VALUE;
+ case FormulaError::PairExpected: return EXC_ERR_VALUE;
+ case FormulaError::OperatorExpected: return EXC_ERR_VALUE;
+ case FormulaError::VariableExpected: return EXC_ERR_VALUE;
+ case FormulaError::ParameterExpected: return EXC_ERR_VALUE;
+ case FormulaError::NoValue: return EXC_ERR_VALUE;
+ case FormulaError::CircularReference: return EXC_ERR_VALUE;
+ case FormulaError::NoCode: return EXC_ERR_NULL;
+ case FormulaError::NoRef: return EXC_ERR_REF;
+ case FormulaError::NoName: return EXC_ERR_NAME;
+ case FormulaError::NoAddin: return EXC_ERR_NAME;
+ case FormulaError::NoMacro: return EXC_ERR_NAME;
+ case FormulaError::NotAvailable: return EXC_ERR_NA;
+ default: break;
+ }
+ return EXC_ERR_NA;
+}
+
+FormulaError XclTools::GetScErrorCode( sal_uInt8 nXclError )
+{
+ switch( nXclError )
+ {
+ case EXC_ERR_NULL: return FormulaError::NoCode;
+ case EXC_ERR_DIV0: return FormulaError::DivisionByZero;
+ case EXC_ERR_VALUE: return FormulaError::NoValue;
+ case EXC_ERR_REF: return FormulaError::NoRef;
+ case EXC_ERR_NAME: return FormulaError::NoName;
+ case EXC_ERR_NUM: return FormulaError::IllegalFPOperation;
+ case EXC_ERR_NA: return FormulaError::NotAvailable;
+ default: OSL_FAIL( "XclTools::GetScErrorCode - unknown error code" );
+ }
+ return FormulaError::NotAvailable;
+}
+
+double XclTools::ErrorToDouble( sal_uInt8 nXclError )
+{
+ return CreateDoubleError(GetScErrorCode( nXclError ));
+}
+
+XclBoolError XclTools::ErrorToEnum( double& rfDblValue, bool bErrOrBool, sal_uInt8 nValue )
+{
+ XclBoolError eType;
+ if( bErrOrBool )
+ {
+ // error value
+ switch( nValue )
+ {
+ case EXC_ERR_NULL: eType = xlErrNull; break;
+ case EXC_ERR_DIV0: eType = xlErrDiv0; break;
+ case EXC_ERR_VALUE: eType = xlErrValue; break;
+ case EXC_ERR_REF: eType = xlErrRef; break;
+ case EXC_ERR_NAME: eType = xlErrName; break;
+ case EXC_ERR_NUM: eType = xlErrNum; break;
+ case EXC_ERR_NA: eType = xlErrNA; break;
+ default: eType = xlErrUnknown;
+ }
+ rfDblValue = 0.0;
+ }
+ else
+ {
+ // Boolean value
+ eType = nValue ? xlErrTrue : xlErrFalse;
+ rfDblValue = nValue ? 1.0 : 0.0;
+ }
+ return eType;
+}
+
+template <typename N> static N to(double f) { return limit_cast<N>(f + 0.5); }
+
+sal_uInt16 XclTools::GetTwipsFromInch( double fInches )
+{
+ return to<sal_uInt16>(o3tl::convert(fInches, o3tl::Length::in, o3tl::Length::twip));
+}
+
+sal_uInt16 XclTools::GetTwipsFromHmm( sal_Int32 nHmm )
+{
+ return limit_cast<sal_uInt16>(o3tl::convert(nHmm, o3tl::Length::mm100, o3tl::Length::twip));
+}
+
+double XclTools::GetInchFromTwips( sal_Int32 nTwips )
+{
+ return o3tl::convert<double>(nTwips, o3tl::Length::twip, o3tl::Length::in);
+}
+
+double XclTools::GetInchFromHmm( sal_Int32 nHmm )
+{
+ return o3tl::convert<double>(nHmm, o3tl::Length::mm100, o3tl::Length::in);
+}
+
+sal_Int32 XclTools::GetHmmFromInch( double fInches )
+{
+ return to<sal_Int32>(o3tl::convert(fInches, o3tl::Length::in, o3tl::Length::mm100));
+}
+
+sal_Int32 XclTools::GetHmmFromTwips( sal_Int32 nTwips )
+{
+ return limit_cast<sal_Int32>(o3tl::convert(nTwips, o3tl::Length::twip, o3tl::Length::mm100));
+}
+
+sal_uInt16 XclTools::GetScColumnWidth( sal_uInt16 nXclWidth, tools::Long nScCharWidth )
+{
+ double fScWidth = static_cast< double >( nXclWidth ) / 256.0 * nScCharWidth - 0.5;
+ return limit_cast< sal_uInt16 >( fScWidth );
+}
+
+sal_uInt16 XclTools::GetXclColumnWidth( sal_uInt16 nScWidth, tools::Long nScCharWidth )
+{
+ double fXclWidth = ( static_cast< double >( nScWidth ) + 0.5 ) * 256.0 / nScCharWidth;
+ return limit_cast< sal_uInt16 >( fXclWidth );
+}
+
+// takes font height in twips (1/20 pt = 1/1440 in)
+// returns correction value in 1/256th of *digit width* of default font
+double XclTools::GetXclDefColWidthCorrection( tools::Long nXclDefFontHeight )
+{
+ // Excel uses *max digit width of default font* (W) as cell width unit. Also it has 5-pixel
+ // "correction" to cell widths (ECMA-376-1:2016 18.3.1.81): each cell has 1-pixel padding, then
+ // 3 pixels for the border (which may be 1-pixel - hairline - then it will have 2 additional
+ // 1-pixel spacings from each side; or e.g. 2 hairlines with 1-pixel spacing in the middle; or
+ // thick 3-pixel). Obviously, correction size entirely depends on pixel size (and it is actually
+ // different in Excel on monitors with different resolution). Thus actual (displayed/printed)
+ // cell widths consist of X*W+5px; stored in file is the X (or X*256 if 1/256th of digit width
+ // units are used) value.
+ // This formula apparently converts this 5-pixel correction to 1/256th of digit width units.
+ // Looks like it is created from
+ //
+ // 5 * 256 * 1440 * 2.1333 / (96 * max(N-15, 60)) + 50.0
+ //
+ // where 5 - pixel correction; 256 - used to produce 1/256th of digit width; 1440 - used to
+ // convert font height N (in twips) to inches; 2.1333 - an (empirical?) quotient to convert
+ // font *height* into digit *width*; 96 - "standard" monitor resolution (DPI).
+ // Additionally the formula uses 15 (of unknown origin), 60 (minimal font height 3 pt), and
+ // 50.0 (also of unknown origin).
+ //
+ // TODO: convert this to take font digit width directly (and possibly DPI?), to avoid guessing
+ // the digit width and pixel size. Or DPI might stay 96, to not follow Excel dependency on DPI
+ // in addition to used font, and have absolute size of the correction fixed 5/96 in.
+ return 40960.0 / ::std::max( nXclDefFontHeight - 15, tools::Long(60) ) + 50.0;
+}
+
+// formatting
+
+Color XclTools::GetPatternColor( const Color& rPattColor, const Color& rBackColor, sal_uInt16 nXclPattern )
+{
+ // 0x00 == 0% transparence (full rPattColor)
+ // 0x80 == 100% transparence (full rBackColor)
+ static const sal_uInt8 pnRatioTable[] =
+ {
+ 0x80, 0x00, 0x40, 0x20, 0x60, 0x40, 0x40, 0x40, // 00 - 07
+ 0x40, 0x40, 0x20, 0x60, 0x60, 0x60, 0x60, 0x48, // 08 - 15
+ 0x50, 0x70, 0x78 // 16 - 18
+ };
+ return (nXclPattern < SAL_N_ELEMENTS( pnRatioTable )) ?
+ ScfTools::GetMixedColor( rPattColor, rBackColor, pnRatioTable[ nXclPattern ] ) : rPattColor;
+}
+
+// text encoding
+
+namespace {
+
+const struct XclCodePageEntry
+{
+ sal_uInt16 mnCodePage;
+ rtl_TextEncoding meTextEnc;
+}
+pCodePageTable[] =
+{
+ { 437, RTL_TEXTENCODING_IBM_437 }, // OEM US
+// { 720, RTL_TEXTENCODING_IBM_720 }, // OEM Arabic
+ { 737, RTL_TEXTENCODING_IBM_737 }, // OEM Greek
+ { 775, RTL_TEXTENCODING_IBM_775 }, // OEM Baltic
+ { 850, RTL_TEXTENCODING_IBM_850 }, // OEM Latin I
+ { 852, RTL_TEXTENCODING_IBM_852 }, // OEM Latin II (Central European)
+ { 855, RTL_TEXTENCODING_IBM_855 }, // OEM Cyrillic
+ { 857, RTL_TEXTENCODING_IBM_857 }, // OEM Turkish
+// { 858, RTL_TEXTENCODING_IBM_858 }, // OEM Multilingual Latin I with Euro
+ { 860, RTL_TEXTENCODING_IBM_860 }, // OEM Portuguese
+ { 861, RTL_TEXTENCODING_IBM_861 }, // OEM Icelandic
+ { 862, RTL_TEXTENCODING_IBM_862 }, // OEM Hebrew
+ { 863, RTL_TEXTENCODING_IBM_863 }, // OEM Canadian (French)
+ { 864, RTL_TEXTENCODING_IBM_864 }, // OEM Arabic
+ { 865, RTL_TEXTENCODING_IBM_865 }, // OEM Nordic
+ { 866, RTL_TEXTENCODING_IBM_866 }, // OEM Cyrillic (Russian)
+ { 869, RTL_TEXTENCODING_IBM_869 }, // OEM Greek (Modern)
+ { 874, RTL_TEXTENCODING_MS_874 }, // MS Windows Thai
+ { 932, RTL_TEXTENCODING_MS_932 }, // MS Windows Japanese Shift-JIS
+ { 936, RTL_TEXTENCODING_MS_936 }, // MS Windows Chinese Simplified GBK
+ { 949, RTL_TEXTENCODING_MS_949 }, // MS Windows Korean (Wansung)
+ { 950, RTL_TEXTENCODING_MS_950 }, // MS Windows Chinese Traditional BIG5
+ { 1200, RTL_TEXTENCODING_DONTKNOW }, // Unicode (BIFF8) - return *_DONTKNOW to preserve old code page
+ { 1250, RTL_TEXTENCODING_MS_1250 }, // MS Windows Latin II (Central European)
+ { 1251, RTL_TEXTENCODING_MS_1251 }, // MS Windows Cyrillic
+ { 1252, RTL_TEXTENCODING_MS_1252 }, // MS Windows Latin I (BIFF4-BIFF8)
+ { 1253, RTL_TEXTENCODING_MS_1253 }, // MS Windows Greek
+ { 1254, RTL_TEXTENCODING_MS_1254 }, // MS Windows Turkish
+ { 1255, RTL_TEXTENCODING_MS_1255 }, // MS Windows Hebrew
+ { 1256, RTL_TEXTENCODING_MS_1256 }, // MS Windows Arabic
+ { 1257, RTL_TEXTENCODING_MS_1257 }, // MS Windows Baltic
+ { 1258, RTL_TEXTENCODING_MS_1258 }, // MS Windows Vietnamese
+ { 1361, RTL_TEXTENCODING_MS_1361 }, // MS Windows Korean (Johab)
+ { 10000, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman
+ { 32768, RTL_TEXTENCODING_APPLE_ROMAN }, // Apple Roman
+ { 32769, RTL_TEXTENCODING_MS_1252 } // MS Windows Latin I (BIFF2-BIFF3)
+};
+const XclCodePageEntry* const pCodePageTableEnd = std::end(pCodePageTable);
+
+struct XclCodePageEntry_CPPred
+{
+ explicit XclCodePageEntry_CPPred( sal_uInt16 nCodePage ) : mnCodePage( nCodePage ) {}
+ bool operator()( const XclCodePageEntry& rEntry ) const { return rEntry.mnCodePage == mnCodePage; }
+ sal_uInt16 mnCodePage;
+};
+
+struct XclCodePageEntry_TEPred
+{
+ explicit XclCodePageEntry_TEPred( rtl_TextEncoding eTextEnc ) : meTextEnc( eTextEnc ) {}
+ bool operator()( const XclCodePageEntry& rEntry ) const { return rEntry.meTextEnc == meTextEnc; }
+ rtl_TextEncoding meTextEnc;
+};
+
+} // namespace
+
+rtl_TextEncoding XclTools::GetTextEncoding( sal_uInt16 nCodePage )
+{
+ const XclCodePageEntry* pEntry = ::std::find_if( pCodePageTable, pCodePageTableEnd, XclCodePageEntry_CPPred( nCodePage ) );
+ if( pEntry == pCodePageTableEnd )
+ {
+ SAL_WARN("sc", "XclTools::GetTextEncoding - unknown code page: 0x" << std::hex << nCodePage );
+ return RTL_TEXTENCODING_DONTKNOW;
+ }
+ return pEntry->meTextEnc;
+}
+
+sal_uInt16 XclTools::GetXclCodePage( rtl_TextEncoding eTextEnc )
+{
+ if( eTextEnc == RTL_TEXTENCODING_UNICODE )
+ return 1200; // for BIFF8
+
+ const XclCodePageEntry* pEntry = ::std::find_if( pCodePageTable, pCodePageTableEnd, XclCodePageEntry_TEPred( eTextEnc ) );
+ if( pEntry == pCodePageTableEnd )
+ {
+ SAL_WARN("sc", "XclTools::GetXclCodePage - unsupported text encoding: 0x" << std::hex << eTextEnc );
+ return 1252;
+ }
+ return pEntry->mnCodePage;
+}
+
+OUString XclTools::GetXclFontName( const OUString& rFontName )
+{
+ // substitute with MS fonts
+ OUString aNewName = GetSubsFontName(rFontName, SubsFontFlags::ONLYONE | SubsFontFlags::MS);
+ return aNewName.isEmpty() ? rFontName : aNewName;
+}
+
+// built-in defined names
+const char maDefNamePrefix[] = "Excel_BuiltIn_"; /// Prefix for built-in defined names.
+const char maDefNamePrefixXml[] = "_xlnm."; /// Prefix for built-in defined names for OOX
+
+const char* const ppcDefNames[] =
+{
+ "Consolidate_Area",
+ "Auto_Open",
+ "Auto_Close",
+ "Extract",
+ "Database",
+ "Criteria",
+ "Print_Area",
+ "Print_Titles",
+ "Recorder",
+ "Data_Form",
+ "Auto_Activate",
+ "Auto_Deactivate",
+ "Sheet_Title",
+ "_FilterDatabase"
+};
+
+OUString XclTools::GetXclBuiltInDefName( sal_Unicode cBuiltIn )
+{
+ OSL_ENSURE( SAL_N_ELEMENTS( ppcDefNames ) == EXC_BUILTIN_UNKNOWN,
+ "XclTools::GetXclBuiltInDefName - built-in defined name list modified" );
+
+ if( cBuiltIn < SAL_N_ELEMENTS( ppcDefNames ) )
+ return OUString::createFromAscii(ppcDefNames[cBuiltIn]);
+ else
+ return OUString::number(cBuiltIn);
+}
+
+OUString XclTools::GetBuiltInDefName( sal_Unicode cBuiltIn )
+{
+ return maDefNamePrefix + GetXclBuiltInDefName(cBuiltIn);
+}
+
+OUString XclTools::GetBuiltInDefNameXml( sal_Unicode cBuiltIn )
+{
+ return maDefNamePrefixXml + GetXclBuiltInDefName(cBuiltIn);
+}
+
+sal_Unicode XclTools::GetBuiltInDefNameIndex( const OUString& rDefName )
+{
+ sal_Int32 nPrefixLen = 0;
+ if( rDefName.startsWithIgnoreAsciiCase( maDefNamePrefix ) )
+ nPrefixLen = strlen(maDefNamePrefix);
+ else if( rDefName.startsWithIgnoreAsciiCase( maDefNamePrefixXml ) )
+ nPrefixLen = strlen(maDefNamePrefixXml);
+ if( nPrefixLen > 0 )
+ {
+ for( sal_Unicode cBuiltIn = 0; cBuiltIn < EXC_BUILTIN_UNKNOWN; ++cBuiltIn )
+ {
+ OUString aBuiltInName(GetXclBuiltInDefName(cBuiltIn));
+ sal_Int32 nBuiltInLen = aBuiltInName.getLength();
+ if( rDefName.matchIgnoreAsciiCase( aBuiltInName, nPrefixLen ) )
+ {
+ // name can be followed by underline or space character
+ sal_Int32 nNextCharPos = nPrefixLen + nBuiltInLen;
+ sal_Unicode cNextChar = (rDefName.getLength() > nNextCharPos) ? rDefName[nNextCharPos] : '\0';
+ if( (cNextChar == '\0') || (cNextChar == ' ') || (cNextChar == '_') )
+ return cBuiltIn;
+ }
+ }
+ }
+ return EXC_BUILTIN_UNKNOWN;
+}
+
+// built-in style names
+
+const char maStyleNamePrefix1[] = "Excel_BuiltIn_"; /// Prefix for built-in cell style names.
+const char maStyleNamePrefix2[] = "Excel Built-in "; /// Prefix for built-in cell style names from OOX filter.
+
+const char* const ppcStyleNames[] =
+{
+ "", // "Normal" not used directly, but localized "Default"
+ "RowLevel_", // outline level will be appended
+ "ColumnLevel_", // outline level will be appended
+ "Comma",
+ "Currency",
+ "Percent",
+ "Comma_0",
+ "Currency_0",
+ "Hyperlink",
+ "Followed_Hyperlink"
+};
+
+OUString XclTools::GetBuiltInStyleName( sal_uInt8 nStyleId, std::u16string_view rName, sal_uInt8 nLevel )
+{
+ OUString aStyleName;
+
+ if( nStyleId == EXC_STYLE_NORMAL ) // "Normal" becomes "Default" style
+ {
+ aStyleName = ScResId( STR_STYLENAME_STANDARD );
+ }
+ else
+ {
+ OUStringBuffer aBuf(maStyleNamePrefix1);
+ if( nStyleId < SAL_N_ELEMENTS( ppcStyleNames ) )
+ aBuf.appendAscii(ppcStyleNames[nStyleId]);
+ else if (!rName.empty())
+ aBuf.append(rName);
+ else
+ aBuf.append(static_cast<sal_Int32>(nStyleId));
+
+ if( (nStyleId == EXC_STYLE_ROWLEVEL) || (nStyleId == EXC_STYLE_COLLEVEL) )
+ aBuf.append(static_cast<sal_Int32>(nLevel+1));
+
+ aStyleName = aBuf.makeStringAndClear();
+ }
+
+ return aStyleName;
+}
+
+bool XclTools::IsBuiltInStyleName( const OUString& rStyleName, sal_uInt8* pnStyleId, sal_Int32* pnNextChar )
+{
+ // "Default" becomes "Normal"
+ if (rStyleName == ScResId(STR_STYLENAME_STANDARD))
+ {
+ if( pnStyleId ) *pnStyleId = EXC_STYLE_NORMAL;
+ if( pnNextChar ) *pnNextChar = rStyleName.getLength();
+ return true;
+ }
+
+ // try the other built-in styles
+ sal_uInt8 nFoundId = 0;
+ sal_Int32 nNextChar = 0;
+
+ sal_Int32 nPrefixLen = 0;
+ if( rStyleName.startsWithIgnoreAsciiCase( maStyleNamePrefix1 ) )
+ nPrefixLen = strlen(maStyleNamePrefix1);
+ else if( rStyleName.startsWithIgnoreAsciiCase( maStyleNamePrefix2 ) )
+ nPrefixLen = strlen(maStyleNamePrefix2);
+ if( nPrefixLen > 0 )
+ {
+ for( sal_uInt8 nId = 0; nId < SAL_N_ELEMENTS( ppcStyleNames ); ++nId )
+ {
+ if( nId != EXC_STYLE_NORMAL )
+ {
+ OUString aShortName = OUString::createFromAscii(ppcStyleNames[nId]);
+ if( rStyleName.matchIgnoreAsciiCase( aShortName, nPrefixLen ) &&
+ (nNextChar < nPrefixLen + aShortName.getLength()))
+ {
+ nFoundId = nId;
+ nNextChar = nPrefixLen + aShortName.getLength();
+ }
+ }
+ }
+ }
+
+ if( nNextChar > 0 )
+ {
+ if( pnStyleId ) *pnStyleId = nFoundId;
+ if( pnNextChar ) *pnNextChar = nNextChar;
+ return true;
+ }
+
+ if( pnStyleId ) *pnStyleId = EXC_STYLE_USERDEF;
+ if( pnNextChar ) *pnNextChar = 0;
+ return nPrefixLen > 0; // also return true for unknown built-in styles
+}
+
+bool XclTools::GetBuiltInStyleId( sal_uInt8& rnStyleId, sal_uInt8& rnLevel, const OUString& rStyleName )
+{
+ sal_uInt8 nStyleId;
+ sal_Int32 nNextChar;
+ if( IsBuiltInStyleName( rStyleName, &nStyleId, &nNextChar ) && (nStyleId != EXC_STYLE_USERDEF) )
+ {
+ if( (nStyleId == EXC_STYLE_ROWLEVEL) || (nStyleId == EXC_STYLE_COLLEVEL) )
+ {
+ std::u16string_view aLevel = rStyleName.subView(nNextChar);
+ sal_Int32 nLevel = o3tl::toInt32(aLevel);
+ if (std::u16string_view(OUString::number(nLevel)) == aLevel
+ && nLevel > 0 && nLevel <= EXC_STYLE_LEVELCOUNT)
+ {
+ rnStyleId = nStyleId;
+ rnLevel = static_cast< sal_uInt8 >( nLevel - 1 );
+ return true;
+ }
+ }
+ else if( rStyleName.getLength() == nNextChar )
+ {
+ rnStyleId = nStyleId;
+ rnLevel = EXC_STYLE_NOLEVEL;
+ return true;
+ }
+ }
+
+ rnStyleId = EXC_STYLE_USERDEF;
+ rnLevel = EXC_STYLE_NOLEVEL;
+ return false;
+}
+
+// conditional formatting style names
+
+const char maCFStyleNamePrefix1[] = "Excel_CondFormat_"; /// Prefix for cond. formatting style names.
+const char maCFStyleNamePrefix2[] = "ConditionalStyle_"; /// Prefix for cond. formatting style names from OOX filter.
+const char maCFStyleNamePrefix3[] = "ExtConditionalStyle_";
+
+OUString XclTools::GetCondFormatStyleName( SCTAB nScTab, sal_Int32 nFormat, sal_uInt16 nCondition )
+{
+ return maCFStyleNamePrefix1 +
+ OUString::number(static_cast<sal_Int32>(nScTab+1)) +
+ "_" +
+ OUString::number(static_cast<sal_Int32>(nFormat+1)) +
+ "_" +
+ OUString::number(static_cast<sal_Int32>(nCondition+1));
+}
+
+bool XclTools::IsCondFormatStyleName( const OUString& rStyleName )
+{
+ if( rStyleName.startsWithIgnoreAsciiCase( maCFStyleNamePrefix1 ) )
+ return true;
+
+ if( rStyleName.startsWithIgnoreAsciiCase( maCFStyleNamePrefix2 ) )
+ return true;
+
+ if (rStyleName.startsWithIgnoreAsciiCase(maCFStyleNamePrefix3))
+ return true;
+
+ return false;
+}
+
+// stream handling
+
+void XclTools::SkipSubStream( XclImpStream& rStrm )
+{
+ bool bLoop = true;
+ while( bLoop && rStrm.StartNextRecord() )
+ {
+ sal_uInt16 nRecId = rStrm.GetRecId();
+ bLoop = nRecId != EXC_ID_EOF;
+ if( (nRecId == EXC_ID2_BOF) || (nRecId == EXC_ID3_BOF) || (nRecId == EXC_ID4_BOF) || (nRecId == EXC_ID5_BOF) )
+ SkipSubStream( rStrm );
+ }
+}
+
+// Basic macro names
+
+const char maSbMacroPrefix[] = "vnd.sun.star.script:"; /// Prefix for StarBasic macros.
+const char maSbMacroSuffix[] = "?language=Basic&location=document"; /// Suffix for StarBasic macros.
+
+OUString XclTools::GetSbMacroUrl( const OUString& rMacroName, SfxObjectShell* pDocShell )
+{
+ OSL_ENSURE( !rMacroName.isEmpty(), "XclTools::GetSbMacroUrl - macro name is empty" );
+ ::ooo::vba::MacroResolvedInfo aMacroInfo = ::ooo::vba::resolveVBAMacro( pDocShell, rMacroName );
+ if( aMacroInfo.mbFound )
+ return ::ooo::vba::makeMacroURL( aMacroInfo.msResolvedMacro );
+ return OUString();
+}
+
+OUString XclTools::GetXclMacroName( const OUString& rSbMacroUrl )
+{
+ sal_Int32 nSbMacroUrlLen = rSbMacroUrl.getLength();
+ sal_Int32 nMacroNameLen = nSbMacroUrlLen - strlen(maSbMacroPrefix) - strlen(maSbMacroSuffix);
+ if( (nMacroNameLen > 0) && rSbMacroUrl.startsWithIgnoreAsciiCase( maSbMacroPrefix ) &&
+ rSbMacroUrl.endsWithIgnoreAsciiCase( maSbMacroSuffix ) )
+ {
+ sal_Int32 nPrjDot = rSbMacroUrl.indexOf( '.', strlen(maSbMacroPrefix) ) + 1;
+ return rSbMacroUrl.copy( nPrjDot, nSbMacroUrlLen - nPrjDot - strlen(maSbMacroSuffix) );
+ }
+ return OUString();
+}
+
+// read/write colors
+
+XclImpStream& operator>>( XclImpStream& rStrm, Color& rColor )
+{
+ sal_uInt8 nR = rStrm.ReaduInt8();
+ sal_uInt8 nG = rStrm.ReaduInt8();
+ sal_uInt8 nB = rStrm.ReaduInt8();
+ rStrm.Ignore( 1 );//nD
+ rColor = Color( nR, nG, nB );
+ return rStrm;
+}
+
+XclExpStream& operator<<( XclExpStream& rStrm, const Color& rColor )
+{
+ return rStrm << rColor.GetRed() << rColor.GetGreen() << rColor.GetBlue() << sal_uInt8( 0 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xltracer.cxx b/sc/source/filter/excel/xltracer.cxx
new file mode 100644
index 000000000..c6931dbf0
--- /dev/null
+++ b/sc/source/filter/excel/xltracer.cxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xltracer.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <address.hxx>
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::beans::PropertyValue;
+
+XclTracer::XclTracer(std::u16string_view /*rDocUrl*/)
+ : mbEnabled(false)
+ , maFirstTimes(eTraceLength, true)
+{
+}
+
+XclTracer::~XclTracer() {}
+
+void XclTracer::ProcessTraceOnce(XclTracerId eProblem)
+{
+ if (mbEnabled && maFirstTimes[eProblem])
+ {
+ maFirstTimes[eProblem] = false;
+ }
+}
+
+void XclTracer::TraceInvalidAddress(const ScAddress& rPos, const ScAddress& rMaxPos)
+{
+ TraceInvalidRow(rPos.Row(), rMaxPos.Row());
+ TraceInvalidTab(rPos.Tab(), rMaxPos.Tab());
+}
+
+void XclTracer::TraceInvalidRow(sal_uInt32 nRow, sal_uInt32 nMaxRow)
+{
+ if (nRow > nMaxRow)
+ ProcessTraceOnce(eRowLimitExceeded);
+}
+
+void XclTracer::TraceInvalidTab(SCTAB nTab, SCTAB nMaxTab)
+{
+ if (nTab > nMaxTab)
+ ProcessTraceOnce(eTabLimitExceeded);
+}
+
+void XclTracer::TracePrintRange() { ProcessTraceOnce(ePrintRange); }
+
+void XclTracer::TraceDates(sal_uInt16 nNumFmt)
+{
+ // Short Date = 14 and Short Date+Time = 22
+ if (nNumFmt == 14 || nNumFmt == 22)
+ ProcessTraceOnce(eShortDate);
+}
+
+void XclTracer::TraceBorderLineStyle(bool bBorderLineStyle)
+{
+ if (bBorderLineStyle)
+ ProcessTraceOnce(eBorderLineStyle);
+}
+
+void XclTracer::TraceFillPattern(bool bFillPattern)
+{
+ if (bFillPattern)
+ ProcessTraceOnce(eFillPattern);
+}
+
+void XclTracer::TraceFormulaMissingArg()
+{
+ // missing parameter in Formula record
+ ProcessTraceOnce(eFormulaMissingArg);
+}
+
+void XclTracer::TracePivotDataSource(bool bExternal)
+{
+ if (bExternal)
+ ProcessTraceOnce(ePivotDataSource);
+}
+
+void XclTracer::TracePivotChartExists()
+{
+ // Pivot Charts not currently displayed.
+ ProcessTraceOnce(ePivotChartExists);
+}
+
+void XclTracer::TraceChartUnKnownType() { ProcessTraceOnce(eChartUnKnownType); }
+
+void XclTracer::TraceChartOnlySheet() { ProcessTraceOnce(eChartOnlySheet); }
+
+void XclTracer::TraceChartDataTable()
+{
+ // Data table is not supported.
+ ProcessTraceOnce(eChartDataTable);
+}
+
+void XclTracer::TraceChartLegendPosition()
+{
+ // If position is set to "not docked or inside the plot area" then
+ // we cannot guarantee the legend position.
+ ProcessTraceOnce(eChartLegendPosition);
+}
+
+void XclTracer::TraceUnsupportedObjects()
+{
+ // Called from Excel 5.0 - limited Graphical object support.
+ ProcessTraceOnce(eUnsupportedObject);
+}
+
+void XclTracer::TraceObjectNotPrintable() { ProcessTraceOnce(eObjectNotPrintable); }
+
+void XclTracer::TraceDVType(bool bType)
+{
+ // Custom types work if 'Data->validity dialog' is not OKed.
+ if (bType)
+ ProcessTraceOnce(eDVType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/excel/xlview.cxx b/sc/source/filter/excel/xlview.cxx
new file mode 100644
index 000000000..b5768e2df
--- /dev/null
+++ b/sc/source/filter/excel/xlview.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xlview.hxx>
+#include <osl/diagnose.h>
+
+// Structs ====================================================================
+
+XclDocViewData::XclDocViewData() :
+ mnWinX( 0 ),
+ mnWinY( 0 ),
+ mnWinWidth( 0 ),
+ mnWinHeight( 0 ),
+ mnFlags( EXC_WIN1_HOR_SCROLLBAR | EXC_WIN1_VER_SCROLLBAR | EXC_WIN1_TABBAR ),
+ mnDisplXclTab( 0 ),
+ mnFirstVisXclTab( 0 ),
+ mnXclSelectCnt( 1 ),
+ mnTabBarWidth( 600 )
+{
+}
+
+XclTabViewData::XclTabViewData() :
+ maFirstXclPos( ScAddress::UNINITIALIZED ),
+ maSecondXclPos( ScAddress::UNINITIALIZED )
+{
+ SetDefaults();
+}
+
+XclTabViewData::~XclTabViewData()
+{
+}
+
+void XclTabViewData::SetDefaults()
+{
+ maSelMap.clear();
+ maGridColor = COL_AUTO;
+ maFirstXclPos.Set( 0, 0 );
+ maSecondXclPos.Set( 0, 0 );
+ mnSplitX = mnSplitY = 0;
+ mnNormalZoom = EXC_WIN2_NORMALZOOM_DEF;
+ mnPageZoom = EXC_WIN2_PAGEZOOM_DEF;
+ mnCurrentZoom = 0; // default to mnNormalZoom or mnPageZoom
+ mnActivePane = EXC_PANE_TOPLEFT;
+ mbSelected = mbDisplayed = false;
+ mbMirrored = false;
+ mbFrozenPanes = false;
+ mbPageMode = false;
+ mbDefGridColor = true;
+ mbShowFormulas = false;
+ mbShowGrid = mbShowHeadings = mbShowZeros = mbShowOutline = true;
+ maTabBgColor = COL_AUTO;
+ mnTabBgColorId = 0;
+}
+
+bool XclTabViewData::IsSplit() const
+{
+ return (mnSplitX > 0) || (mnSplitY > 0);
+}
+
+bool XclTabViewData::HasPane( sal_uInt8 nPaneId ) const
+{
+ switch( nPaneId )
+ {
+ case EXC_PANE_BOTTOMRIGHT: return (mnSplitX > 0) && (mnSplitY > 0);
+ case EXC_PANE_TOPRIGHT: return mnSplitX > 0;
+ case EXC_PANE_BOTTOMLEFT: return mnSplitY > 0;
+ case EXC_PANE_TOPLEFT: return true;
+ }
+ OSL_FAIL( "XclExpPane::HasPane - wrong pane ID" );
+ return false;
+}
+
+const XclSelectionData* XclTabViewData::GetSelectionData( sal_uInt8 nPane ) const
+{
+ XclSelectionMap::const_iterator aIt = maSelMap.find( nPane );
+ return (aIt == maSelMap.end()) ? nullptr : aIt->second.get();
+}
+
+XclSelectionData& XclTabViewData::CreateSelectionData( sal_uInt8 nPane )
+{
+ XclSelectionDataRef& rxSelData = maSelMap[ nPane ];
+ if( !rxSelData )
+ rxSelData = std::make_shared<XclSelectionData>();
+ return *rxSelData;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/fapihelper.cxx b/sc/source/filter/ftools/fapihelper.cxx
new file mode 100644
index 000000000..4abfc79be
--- /dev/null
+++ b/sc/source/filter/ftools/fapihelper.cxx
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fapihelper.hxx>
+
+#include <algorithm>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/beans/XPropertySetOption.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <comphelper/docpasswordhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sal/log.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/stritem.hxx>
+#include <svl/itemset.hxx>
+#include <miscuno.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::beans::XPropertyState;
+using ::com::sun::star::lang::XServiceName;
+using ::com::sun::star::lang::XMultiServiceFactory;
+
+using namespace ::com::sun::star;
+
+// Static helper functions ====================================================
+
+OUString ScfApiHelper::GetServiceName( const Reference< XInterface >& xInt )
+{
+ OUString aService;
+ Reference< XServiceName > xServiceName( xInt, UNO_QUERY );
+ if( xServiceName.is() )
+ aService = xServiceName->getServiceName();
+ return aService;
+}
+
+Reference< XMultiServiceFactory > ScfApiHelper::GetServiceFactory( const SfxObjectShell* pShell )
+{
+ Reference< XMultiServiceFactory > xFactory;
+ if( pShell )
+ xFactory.set( pShell->GetModel(), UNO_QUERY );
+ return xFactory;
+}
+
+Reference< XInterface > ScfApiHelper::CreateInstance(
+ const Reference< XMultiServiceFactory >& xFactory, const OUString& rServiceName )
+{
+ Reference< XInterface > xInt;
+ if( xFactory.is() )
+ {
+ try
+ {
+ xInt = xFactory->createInstance( rServiceName );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "ScfApiHelper::CreateInstance - cannot create instance" );
+ }
+ }
+ return xInt;
+}
+
+Reference< XInterface > ScfApiHelper::CreateInstance( const SfxObjectShell* pShell, const OUString& rServiceName )
+{
+ return CreateInstance( GetServiceFactory( pShell ), rServiceName );
+}
+
+Reference< XInterface > ScfApiHelper::CreateInstance( const OUString& rServiceName )
+{
+ return CreateInstance( ::comphelper::getProcessServiceFactory(), rServiceName );
+}
+
+uno::Sequence< beans::NamedValue > ScfApiHelper::QueryEncryptionDataForMedium( SfxMedium& rMedium,
+ ::comphelper::IDocPasswordVerifier& rVerifier, const ::std::vector< OUString >* pDefaultPasswords )
+{
+ uno::Sequence< beans::NamedValue > aEncryptionData;
+ const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem<SfxUnoAnyItem>(rMedium.GetItemSet(), SID_ENCRYPTIONDATA, false);
+ if ( pEncryptionDataItem )
+ pEncryptionDataItem->GetValue() >>= aEncryptionData;
+
+ OUString aPassword;
+ const SfxStringItem* pPasswordItem = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_PASSWORD, false);
+ if ( pPasswordItem )
+ aPassword = pPasswordItem->GetValue();
+
+ bool bIsDefaultPassword = false;
+ aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
+ rVerifier, aEncryptionData, aPassword, rMedium.GetInteractionHandler(), rMedium.GetOrigURL(),
+ ::comphelper::DocPasswordRequestType::MS, pDefaultPasswords, &bIsDefaultPassword );
+
+ rMedium.GetItemSet()->ClearItem( SID_PASSWORD );
+ rMedium.GetItemSet()->ClearItem( SID_ENCRYPTIONDATA );
+
+ if( !bIsDefaultPassword && aEncryptionData.hasElements() )
+ rMedium.GetItemSet()->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, uno::Any( aEncryptionData ) ) );
+
+ return aEncryptionData;
+}
+
+// Property sets ==============================================================
+
+ScfPropertySet::~ScfPropertySet()
+{
+ Reference<beans::XPropertySetOption> xPropSetOpt(mxPropSet, UNO_QUERY);
+ if (xPropSetOpt.is())
+ // Turn the property value change notification back on when finished.
+ xPropSetOpt->enableChangeListenerNotification(true);
+}
+
+void ScfPropertySet::Set( Reference< XPropertySet > const & xPropSet )
+{
+ mxPropSet = xPropSet;
+ mxMultiPropSet.set( mxPropSet, UNO_QUERY );
+ Reference<beans::XPropertySetOption> xPropSetOpt(mxPropSet, UNO_QUERY);
+ if (xPropSetOpt.is())
+ // We don't want to broadcast property value changes during import to
+ // improve performance.
+ xPropSetOpt->enableChangeListenerNotification(false);
+}
+
+OUString ScfPropertySet::GetServiceName() const
+{
+ return ScfApiHelper::GetServiceName( mxPropSet );
+}
+
+// Get properties -------------------------------------------------------------
+
+bool ScfPropertySet::HasProperty( const OUString& rPropName ) const
+{
+ bool bHasProp = false;
+ try
+ {
+ Reference< XPropertyState > xPropState( mxPropSet, UNO_QUERY_THROW );
+ bHasProp = xPropState->getPropertyState( rPropName ) == css::beans::PropertyState_DIRECT_VALUE;
+ }
+ catch( Exception& )
+ {
+ }
+ return bHasProp;
+}
+
+bool ScfPropertySet::GetAnyProperty( Any& rValue, const OUString& rPropName ) const
+{
+ bool bHasValue = false;
+ try
+ {
+ if( mxPropSet.is() )
+ {
+ rValue = mxPropSet->getPropertyValue( rPropName );
+ bHasValue = true;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ return bHasValue;
+}
+
+bool ScfPropertySet::GetBoolProperty( const OUString& rPropName ) const
+{
+ Any aAny;
+ return GetAnyProperty( aAny, rPropName ) && ScUnoHelpFunctions::GetBoolFromAny( aAny );
+}
+
+OUString ScfPropertySet::GetStringProperty( const OUString& rPropName ) const
+{
+ OUString aOUString;
+ GetProperty( aOUString, rPropName );
+ return aOUString;
+}
+
+bool ScfPropertySet::GetColorProperty( Color& rColor, const OUString& rPropName ) const
+{
+ sal_Int32 nApiColor = 0;
+ bool bRet = GetProperty( nApiColor, rPropName );
+ rColor = Color( ColorTransparency, nApiColor );
+ return bRet;
+}
+
+void ScfPropertySet::GetProperties( Sequence< Any >& rValues, const Sequence< OUString >& rPropNames ) const
+{
+ try
+ {
+ OSL_ENSURE( mxMultiPropSet.is(), "ScfPropertySet::GetProperties - multi property set not available" );
+ if( mxMultiPropSet.is() ) // first try the XMultiPropertySet
+ {
+ rValues = mxMultiPropSet->getPropertyValues( rPropNames );
+ }
+ else if( mxPropSet.is() )
+ {
+ sal_Int32 nLen = rPropNames.getLength();
+ rValues.realloc( nLen );
+ std::transform(rPropNames.begin(), rPropNames.end(), rValues.getArray(),
+ [this](const OUString& rPropName) -> Any { return mxPropSet->getPropertyValue(rPropName); });
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+// Set properties -------------------------------------------------------------
+
+void ScfPropertySet::SetAnyProperty( const OUString& rPropName, const Any& rValue )
+{
+ try
+ {
+ if( mxPropSet.is() )
+ mxPropSet->setPropertyValue( rPropName, rValue );
+ }
+ catch (const Exception&)
+ {
+ SAL_WARN("sc", "ScfPropertySet::SetAnyProperty - cannot set property \"" + rPropName + "\"");
+ }
+}
+
+void ScfPropertySet::SetProperties( const Sequence< OUString >& rPropNames, const Sequence< Any >& rValues )
+{
+ OSL_ENSURE( rPropNames.getLength() == rValues.getLength(), "ScfPropertySet::SetProperties - length of sequences different" );
+ try
+ {
+ if( mxMultiPropSet.is() ) // first try the XMultiPropertySet
+ {
+ mxMultiPropSet->setPropertyValues( rPropNames, rValues );
+ }
+ else if( mxPropSet.is() )
+ {
+ OSL_FAIL( "ScfPropertySet::SetProperties - multi property set not available" );
+ const OUString* pPropName = rPropNames.getConstArray();
+ const OUString* pPropNameEnd = pPropName + rPropNames.getLength();
+ const Any* pValue = rValues.getConstArray();
+ for( ; pPropName != pPropNameEnd; ++pPropName, ++pValue )
+ mxPropSet->setPropertyValue( *pPropName, *pValue );
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "ScfPropertySet::SetAnyProperty - cannot set multiple properties" );
+ }
+}
+
+ScfPropSetHelper::ScfPropSetHelper( const char* const* ppcPropNames ) :
+ mnNextIdx( 0 )
+{
+ OSL_ENSURE( ppcPropNames, "ScfPropSetHelper::ScfPropSetHelper - no strings found" );
+
+ // create OUStrings from ASCII property names
+ typedef ::std::pair< OUString, size_t > IndexedOUString;
+ std::vector<IndexedOUString> aPropNameVec;
+ for( size_t nVecIdx = 0; *ppcPropNames; ++ppcPropNames, ++nVecIdx )
+ {
+ OUString aPropName = OUString::createFromAscii( *ppcPropNames );
+ aPropNameVec.emplace_back( aPropName, nVecIdx );
+ }
+
+ // sorts the pairs, which will be sorted by first component (the property name)
+ ::std::sort( aPropNameVec.begin(), aPropNameVec.end() );
+
+ // resize member sequences
+ size_t nSize = aPropNameVec.size();
+ maNameSeq.realloc( static_cast< sal_Int32 >( nSize ) );
+ auto pNameSeq = maNameSeq.getArray();
+ maValueSeq.realloc( static_cast< sal_Int32 >( nSize ) );
+ maNameOrder.resize( nSize );
+
+ // fill the property name sequence and store original sort order
+ sal_Int32 nSeqIdx = 0;
+ for( auto& aPropName : aPropNameVec )
+ {
+ pNameSeq[ nSeqIdx ] = aPropName.first;
+ maNameOrder[ aPropName.second ] = nSeqIdx;
+ ++nSeqIdx;
+ }
+}
+
+// read properties ------------------------------------------------------------
+
+void ScfPropSetHelper::ReadFromPropertySet( const ScfPropertySet& rPropSet )
+{
+ rPropSet.GetProperties( maValueSeq, maNameSeq );
+ mnNextIdx = 0;
+}
+
+void ScfPropSetHelper::ReadValue( Any& rAny )
+{
+ Any* pAny = GetNextAny();
+ if( pAny )
+ rAny = *pAny;
+}
+
+void ScfPropSetHelper::ReadValue( Color& rColor )
+{
+ sal_Int32 nApiColor(0);
+ ReadValue( nApiColor );
+ rColor = Color( ColorTransparency, nApiColor );
+}
+
+void ScfPropSetHelper::ReadValue( bool& rbValue )
+{
+ Any aAny;
+ ReadValue( aAny );
+ rbValue = ScUnoHelpFunctions::GetBoolFromAny( aAny );
+}
+
+// write properties -----------------------------------------------------------
+
+void ScfPropSetHelper::InitializeWrite()
+{
+ mnNextIdx = 0;
+}
+
+void ScfPropSetHelper::WriteValue( const Any& rAny )
+{
+ if( Any* pAny = GetNextAny() )
+ *pAny = rAny;
+}
+
+void ScfPropSetHelper::WriteValue( bool rbValue )
+{
+ if( Any* pAny = GetNextAny() )
+ *pAny <<= rbValue;
+}
+
+void ScfPropSetHelper::WriteToPropertySet( ScfPropertySet& rPropSet ) const
+{
+ rPropSet.SetProperties( maNameSeq, maValueSeq );
+}
+
+// private --------------------------------------------------------------------
+
+Any* ScfPropSetHelper::GetNextAny()
+{
+ OSL_ENSURE( mnNextIdx < maNameOrder.size(), "ScfPropSetHelper::GetNextAny - sequence overflow" );
+ Any* pAny = nullptr;
+ if( mnNextIdx < maNameOrder.size() )
+ pAny = &maValueSeq.getArray()[ maNameOrder[ mnNextIdx++ ] ];
+ return pAny;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/fprogressbar.cxx b/sc/source/filter/ftools/fprogressbar.cxx
new file mode 100644
index 000000000..3b84e8492
--- /dev/null
+++ b/sc/source/filter/ftools/fprogressbar.cxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fprogressbar.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <progress.hxx>
+#include <osl/diagnose.h>
+#include <tools/stream.hxx>
+
+#include <limits>
+
+ScfProgressBar::ScfProgressSegment::ScfProgressSegment( std::size_t nSize ) :
+ mnSize( nSize ),
+ mnPos( 0 )
+{
+}
+
+ScfProgressBar::ScfProgressSegment::~ScfProgressSegment()
+{
+}
+
+ScfProgressBar::ScfProgressBar( SfxObjectShell* pDocShell, const OUString& rText ) :
+ maText( rText )
+{
+ Init( pDocShell );
+}
+
+ScfProgressBar::ScfProgressBar(SfxObjectShell* pDocShell, TranslateId pResId)
+ : maText(ScResId(pResId))
+{
+ Init( pDocShell );
+}
+
+ScfProgressBar::ScfProgressBar( ScfProgressBar& rParProgress, ScfProgressSegment* pParSegment )
+{
+ Init( rParProgress.mpDocShell );
+ mpParentProgress = &rParProgress;
+ mpParentSegment = pParSegment;
+}
+
+ScfProgressBar::~ScfProgressBar()
+{
+}
+
+void ScfProgressBar::Init( SfxObjectShell* pDocShell )
+{
+ mpDocShell = pDocShell;
+ mpParentProgress = nullptr;
+ mpParentSegment = mpCurrSegment = nullptr;
+ mnTotalSize = mnTotalPos = mnUnitSize = mnNextUnitPos = 0;
+ mnSysProgressScale = 1; // used to workaround the SfxProgress sal_uInt32 limit
+ mbInProgress = false;
+}
+
+ScfProgressBar::ScfProgressSegment* ScfProgressBar::GetSegment( sal_Int32 nSegment )
+{
+ if( nSegment < 0 )
+ return nullptr;
+ return maSegments.at( nSegment ).get();
+}
+
+void ScfProgressBar::SetCurrSegment( ScfProgressSegment* pSegment )
+{
+ if( mpCurrSegment == pSegment )
+ return;
+
+ mpCurrSegment = pSegment;
+
+ if( mpParentProgress && mpParentSegment )
+ {
+ mpParentProgress->SetCurrSegment( mpParentSegment );
+ }
+ else if( !mxSysProgress && (mnTotalSize > 0) )
+ {
+ // SfxProgress has a limit of sal_uInt32.
+ mnSysProgressScale = 1;
+ std::size_t nSysTotalSize = mnTotalSize;
+ while( nSysTotalSize > std::numeric_limits<sal_uInt32>::max() )
+ {
+ nSysTotalSize /= 2;
+ mnSysProgressScale *= 2;
+ }
+ mxSysProgress.reset( new ScProgress( mpDocShell, maText, nSysTotalSize, true ) );
+ }
+
+ if( !mbInProgress && mpCurrSegment && (mnTotalSize > 0) )
+ {
+ mnUnitSize = mnTotalSize / 256 + 1; // at most 256 calls of system progress
+ mnNextUnitPos = 0;
+ mbInProgress = true;
+ }
+}
+
+void ScfProgressBar::IncreaseProgressBar( std::size_t nDelta )
+{
+ std::size_t nNewPos = mnTotalPos + nDelta;
+
+ // call back to parent progress bar
+ if( mpParentProgress && mpParentSegment )
+ {
+ // calculate new position of parent progress bar
+ std::size_t nParentPos = static_cast< std::size_t >(
+ static_cast< double >( nNewPos ) * mpParentSegment->mnSize / mnTotalSize );
+ mpParentProgress->ProgressAbs( nParentPos );
+ }
+ // modify system progress bar
+ else if( mxSysProgress )
+ {
+ if( nNewPos >= mnNextUnitPos )
+ {
+ mnNextUnitPos = nNewPos + mnUnitSize;
+ mxSysProgress->SetState( static_cast< sal_uLong >( nNewPos / mnSysProgressScale ) );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "ScfProgressBar::IncreaseProgressBar - no progress bar found" );
+ }
+
+ mnTotalPos = nNewPos;
+}
+
+sal_Int32 ScfProgressBar::AddSegment( std::size_t nSize )
+{
+ OSL_ENSURE( !mbInProgress, "ScfProgressBar::AddSegment - already in progress mode" );
+ if( nSize == 0 )
+ return SCF_INV_SEGMENT;
+
+ maSegments.push_back( std::make_unique<ScfProgressSegment>( nSize ) );
+ mnTotalSize += nSize;
+ return static_cast< sal_Int32 >( maSegments.size() - 1 );
+}
+
+ScfProgressBar& ScfProgressBar::GetSegmentProgressBar( sal_Int32 nSegment )
+{
+ ScfProgressSegment* pSegment = GetSegment( nSegment );
+ OSL_ENSURE( !pSegment || (pSegment->mnPos == 0), "ScfProgressBar::GetSegmentProgressBar - segment already started" );
+ if( pSegment && (pSegment->mnPos == 0) )
+ {
+ if( !pSegment->mxProgress )
+ pSegment->mxProgress.reset( new ScfProgressBar( *this, pSegment ) );
+ return *pSegment->mxProgress;
+ }
+ return *this;
+}
+
+bool ScfProgressBar::IsFull() const
+{
+ OSL_ENSURE( mbInProgress && mpCurrSegment, "ScfProgressBar::IsFull - no segment started" );
+ return mpCurrSegment && (mpCurrSegment->mnPos >= mpCurrSegment->mnSize);
+}
+
+void ScfProgressBar::ActivateSegment( sal_Int32 nSegment )
+{
+ OSL_ENSURE( mnTotalSize > 0, "ScfProgressBar::ActivateSegment - progress range is zero" );
+ if( mnTotalSize > 0 )
+ SetCurrSegment( GetSegment( nSegment ) );
+}
+
+void ScfProgressBar::ProgressAbs( std::size_t nPos )
+{
+ OSL_ENSURE( mbInProgress && mpCurrSegment, "ScfProgressBar::ProgressAbs - no segment started" );
+ if( mpCurrSegment )
+ {
+ OSL_ENSURE( mpCurrSegment->mnPos <= nPos, "ScfProgressBar::ProgressAbs - delta pos < 0" );
+ OSL_ENSURE( nPos <= mpCurrSegment->mnSize, "ScfProgressBar::ProgressAbs - segment overflow" );
+ if( (mpCurrSegment->mnPos < nPos) && (nPos <= mpCurrSegment->mnSize) )
+ {
+ IncreaseProgressBar( nPos - mpCurrSegment->mnPos );
+ mpCurrSegment->mnPos = nPos;
+ }
+ }
+}
+
+void ScfProgressBar::Progress( std::size_t nDelta )
+{
+ ProgressAbs( mpCurrSegment ? (mpCurrSegment->mnPos + nDelta) : 0 );
+}
+
+ScfSimpleProgressBar::ScfSimpleProgressBar( std::size_t nSize, SfxObjectShell* pDocShell, const OUString& rText ) :
+ maProgress( pDocShell, rText )
+{
+ Init( nSize );
+}
+
+ScfSimpleProgressBar::ScfSimpleProgressBar(std::size_t nSize, SfxObjectShell* pDocShell, TranslateId pResId)
+ : maProgress(pDocShell, pResId)
+{
+ Init( nSize );
+}
+
+void ScfSimpleProgressBar::Init( std::size_t nSize )
+{
+ sal_Int32 nSegment = maProgress.AddSegment( nSize );
+ if( nSegment >= 0 )
+ maProgress.ActivateSegment( nSegment );
+}
+
+ScfStreamProgressBar::ScfStreamProgressBar( SvStream& rStrm, SfxObjectShell* pDocShell ) :
+ mrStrm( rStrm )
+{
+ Init( pDocShell, ScResId( STR_LOAD_DOC ) );
+}
+
+void ScfStreamProgressBar::Progress()
+{
+ mxProgress->ProgressAbs( mrStrm.Tell() );
+}
+
+void ScfStreamProgressBar::Init( SfxObjectShell* pDocShell, const OUString& rText )
+{
+ sal_uInt64 const nSize = mrStrm.TellEnd();
+ mxProgress.reset( new ScfSimpleProgressBar( nSize, pDocShell, rText ) );
+ Progress();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/ftools.cxx b/sc/source/filter/ftools/ftools.cxx
new file mode 100644
index 000000000..5c219e3c3
--- /dev/null
+++ b/sc/source/filter/ftools/ftools.cxx
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ftools.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <tools/color.hxx>
+#include <unotools/charclass.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/poolitem.hxx>
+#include <sot/storage.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <math.h>
+#include <global.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <compiler.hxx>
+
+#include <orcusfiltersimpl.hxx>
+
+
+// ScFilterTools::ReadLongDouble()
+
+void ScfTools::ReadLongDouble(SvStream& rStrm, double& fResult)
+
+#ifdef __SIMPLE_FUNC // for <=VC 1.5
+{
+ long double fRet;
+ bool bOk = 10 == rStrm.Read(&fRet, 10);
+ if (!bOk)
+ return;
+ fResult = static_cast<double>(fRet);
+}
+#undef __SIMPLE_FUNC
+
+#else // detailed for all others
+{
+
+/*
+"Mapping - Guide" 10-Byte Intel
+
+77777777 77666666 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 x10
+98765432 10987654 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 Bit-# total
+9 9 8 8 7 7 6 6 5 5 4 4 3 3 2 2 1 1 0 0 Byte-#
+76543210 76543210 76543210 76543210 76543210 76543210 76543210 76543210 76543210 76543210 Bit-# in Byte
+SEEEEEEE EEEEEEEE IMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM Group
+01111110 00000000 06665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 x10
+14321098 76543210 02109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 Bit in Group
+*/
+
+ long double lfDouble;
+ long double lfFactor = 256.0;
+ sal_uInt8 pDouble10[ 10 ];
+
+ bool bOk = 10 == rStrm.ReadBytes(pDouble10, 10); // Intel-10 in pDouble10
+ if (!bOk)
+ return;
+
+ lfDouble = static_cast< long double >( pDouble10[ 7 ] ); // Byte 7
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 6 ] ); // Byte 6
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 5 ] ); // Byte 5
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 4 ] ); // Byte 4
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 3 ] ); // Byte 3
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 2 ] ); // Byte 2
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 1 ] ); // Byte 1
+ lfDouble *= lfFactor;
+ lfDouble += static_cast< long double >( pDouble10[ 0 ] ); // Byte 0
+
+ // For value 0.0 all bits are zero; pow(2.0,-16446) does not work with CSet compilers
+ if( lfDouble != 0.0 )
+ {
+ // exponent
+ sal_Int32 nExp;
+ nExp = pDouble10[ 9 ] & 0x7F;
+ nExp <<= 8;
+ nExp += pDouble10[ 8 ];
+ nExp -= 16446;
+
+ lfDouble *= pow( 2.0, static_cast< double >( nExp ) );
+ }
+
+ // sign
+ if( pDouble10[ 9 ] & 0x80 )
+ lfDouble *= static_cast< long double >( -1.0 );
+
+ fResult = static_cast<double>(lfDouble);
+}
+#endif
+
+// *** common methods *** -----------------------------------------------------
+
+rtl_TextEncoding ScfTools::GetSystemTextEncoding()
+{
+ return osl_getThreadTextEncoding();
+}
+
+OUString ScfTools::GetHexStr( sal_uInt16 nValue )
+{
+ const char pHex[] = "0123456789ABCDEF";
+ OUString aStr = OUStringChar( pHex[ nValue >> 12 ] )
+ + OUStringChar( pHex[ (nValue >> 8) & 0x000F ] )
+ + OUStringChar( pHex[ (nValue >> 4) & 0x000F ] )
+ + OUStringChar( pHex[ nValue & 0x000F ] );
+ return aStr;
+}
+
+sal_uInt8 ScfTools::GetMixedColorComp( sal_uInt8 nFore, sal_uInt8 nBack, sal_uInt8 nTrans )
+{
+ sal_Int32 nTemp = ((static_cast< sal_Int32 >( nBack ) - nFore) * nTrans) / 0x80 + nFore;
+ return static_cast< sal_uInt8 >( nTemp );
+}
+
+Color ScfTools::GetMixedColor( const Color& rFore, const Color& rBack, sal_uInt8 nTrans )
+{
+ return Color(
+ GetMixedColorComp( rFore.GetRed(), rBack.GetRed(), nTrans ),
+ GetMixedColorComp( rFore.GetGreen(), rBack.GetGreen(), nTrans ),
+ GetMixedColorComp( rFore.GetBlue(), rBack.GetBlue(), nTrans ) );
+}
+
+// *** conversion of names *** ------------------------------------------------
+
+/* XXX As in sc/source/core/tool/rangenam.cxx ScRangeData::IsValidName() */
+
+OUString ScfTools::ConvertToScDefinedName(const OUString& rName )
+{
+ //fdo#37872: we don't allow points in range names any more
+ OUString sName = rName.replace(u'.',
+ u'_');
+ sal_Int32 nLen = sName.getLength();
+ if( nLen && !ScCompiler::IsCharFlagAllConventions( sName, 0, ScCharFlags::CharName ) )
+ sName = sName.replaceAt( 0, 1, u"_" );
+ for( sal_Int32 nPos = 1; nPos < nLen; ++nPos )
+ if( !ScCompiler::IsCharFlagAllConventions( sName, nPos, ScCharFlags::Name ) )
+ sName = sName.replaceAt( nPos, 1, u"_" );
+ return sName;
+}
+
+// *** streams and storages *** -----------------------------------------------
+
+tools::SvRef<SotStorage> ScfTools::OpenStorageRead( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName )
+{
+ tools::SvRef<SotStorage> xSubStrg;
+ if( xStrg.is() && xStrg->IsContained( rStrgName ) )
+ xSubStrg = xStrg->OpenSotStorage( rStrgName, StreamMode::STD_READ );
+ return xSubStrg;
+}
+
+tools::SvRef<SotStorage> ScfTools::OpenStorageWrite( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName )
+{
+ tools::SvRef<SotStorage> xSubStrg;
+ if( xStrg.is() )
+ xSubStrg = xStrg->OpenSotStorage( rStrgName, StreamMode::STD_WRITE );
+ return xSubStrg;
+}
+
+tools::SvRef<SotStorageStream> ScfTools::OpenStorageStreamRead( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName )
+{
+ tools::SvRef<SotStorageStream> xStrm;
+ if( xStrg.is() && xStrg->IsContained( rStrmName ) && xStrg->IsStream( rStrmName ) )
+ xStrm = xStrg->OpenSotStream( rStrmName, StreamMode::STD_READ );
+ return xStrm;
+}
+
+tools::SvRef<SotStorageStream> ScfTools::OpenStorageStreamWrite( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName )
+{
+ OSL_ENSURE( !xStrg.is() || !xStrg->IsContained( rStrmName ), "ScfTools::OpenStorageStreamWrite - stream exists already" );
+ tools::SvRef<SotStorageStream> xStrm;
+ if( xStrg.is() )
+ xStrm = xStrg->OpenSotStream( rStrmName, StreamMode::STD_WRITE | StreamMode::TRUNC );
+ return xStrm;
+}
+
+// *** item handling *** ------------------------------------------------------
+
+bool ScfTools::CheckItem( const SfxItemSet& rItemSet, sal_uInt16 nWhichId, bool bDeep )
+{
+ return rItemSet.GetItemState( nWhichId, bDeep ) == SfxItemState::SET;
+}
+
+bool ScfTools::CheckItems( const SfxItemSet& rItemSet, const sal_uInt16* pnWhichIds, bool bDeep )
+{
+ OSL_ENSURE( pnWhichIds, "ScfTools::CheckItems - no which id list" );
+ for( const sal_uInt16* pnWhichId = pnWhichIds; *pnWhichId != 0; ++pnWhichId )
+ if( CheckItem( rItemSet, *pnWhichId, bDeep ) )
+ return true;
+ return false;
+}
+
+void ScfTools::PutItem( SfxItemSet& rItemSet, const SfxPoolItem& rItem, sal_uInt16 nWhichId, bool bSkipPoolDef )
+{
+ if( !bSkipPoolDef || (rItem != rItemSet.GetPool()->GetDefaultItem( nWhichId )) )
+ {
+ rItemSet.Put( rItem.CloneSetWhich(nWhichId) );
+ }
+}
+
+void ScfTools::PutItem( SfxItemSet& rItemSet, const SfxPoolItem& rItem, bool bSkipPoolDef )
+{
+ PutItem( rItemSet, rItem, rItem.Which(), bSkipPoolDef );
+}
+
+// *** style sheet handling *** -----------------------------------------------
+
+namespace {
+
+ScStyleSheet& lclMakeStyleSheet( ScStyleSheetPool& rPool, const OUString& rStyleName, SfxStyleFamily eFamily, bool bForceName )
+{
+ // find an unused name
+ OUString aNewName( rStyleName );
+ sal_Int32 nIndex = 0;
+ SfxStyleSheetBase* pOldStyleSheet = nullptr;
+ while( SfxStyleSheetBase* pStyleSheet = rPool.Find( aNewName, eFamily ) )
+ {
+ if( !pOldStyleSheet )
+ pOldStyleSheet = pStyleSheet;
+ aNewName = rStyleName + " " + OUString::number( ++nIndex );
+ }
+
+ // rename existing style
+ if( pOldStyleSheet && bForceName )
+ {
+ pOldStyleSheet->SetName( aNewName );
+ aNewName = rStyleName;
+ }
+
+ // create new style sheet
+ return static_cast< ScStyleSheet& >( rPool.Make( aNewName, eFamily, SfxStyleSearchBits::UserDefined ) );
+}
+
+} // namespace
+
+ScStyleSheet& ScfTools::MakeCellStyleSheet( ScStyleSheetPool& rPool, const OUString& rStyleName, bool bForceName )
+{
+ return lclMakeStyleSheet( rPool, rStyleName, SfxStyleFamily::Para, bForceName );
+}
+
+ScStyleSheet& ScfTools::MakePageStyleSheet( ScStyleSheetPool& rPool, const OUString& rStyleName, bool bForceName )
+{
+ return lclMakeStyleSheet( rPool, rStyleName, SfxStyleFamily::Page, bForceName );
+}
+
+// *** byte string import operations *** --------------------------------------
+
+OString ScfTools::read_zeroTerminated_uInt8s_ToOString(SvStream& rStrm, sal_Int32& rnBytesLeft)
+{
+ OString aRet(::read_zeroTerminated_uInt8s_ToOString(rStrm));
+ rnBytesLeft -= aRet.getLength(); //we read this number of bytes anyway
+ if (rStrm.good()) //if the stream is happy we read the null terminator as well
+ --rnBytesLeft;
+ return aRet;
+}
+
+void ScfTools::AppendCString( SvStream& rStrm, OUString& rString, rtl_TextEncoding eTextEnc )
+{
+ rString += ::read_zeroTerminated_uInt8s_ToOUString(rStrm, eTextEnc);
+}
+
+// *** HTML table names <-> named range names *** -----------------------------
+
+const OUString& ScfTools::GetHTMLDocName()
+{
+ static const OUString saHTMLDoc( "HTML_all" );
+ return saHTMLDoc;
+}
+
+const OUString& ScfTools::GetHTMLTablesName()
+{
+ static const OUString saHTMLTables( "HTML_tables" );
+ return saHTMLTables;
+}
+
+const OUString& ScfTools::GetHTMLIndexPrefix()
+{
+ static const OUString saHTMLIndexPrefix( "HTML_" );
+ return saHTMLIndexPrefix;
+
+}
+
+const OUString& ScfTools::GetHTMLNamePrefix()
+{
+ static const OUString saHTMLNamePrefix( "HTML__" );
+ return saHTMLNamePrefix;
+}
+
+OUString ScfTools::GetNameFromHTMLIndex( sal_uInt32 nIndex )
+{
+ OUString aName = GetHTMLIndexPrefix() +
+ OUString::number( static_cast< sal_Int32 >( nIndex ) );
+ return aName;
+}
+
+OUString ScfTools::GetNameFromHTMLName( std::u16string_view rTabName )
+{
+ return GetHTMLNamePrefix() + rTabName;
+}
+
+bool ScfTools::IsHTMLDocName( std::u16string_view rSource )
+{
+ return o3tl::equalsIgnoreAsciiCase( rSource, GetHTMLDocName() );
+}
+
+bool ScfTools::IsHTMLTablesName( std::u16string_view rSource )
+{
+ return o3tl::equalsIgnoreAsciiCase( rSource, GetHTMLTablesName() );
+}
+
+bool ScfTools::GetHTMLNameFromName( const OUString& rSource, OUString& rName )
+{
+ rName.clear();
+ if( rSource.startsWithIgnoreAsciiCase( GetHTMLNamePrefix() ) )
+ {
+ rName = rSource.copy( GetHTMLNamePrefix().getLength() );
+ ScGlobal::AddQuotes( rName, '"', false );
+ }
+ else if( rSource.startsWithIgnoreAsciiCase( GetHTMLIndexPrefix() ) )
+ {
+ OUString aIndex( rSource.copy( GetHTMLIndexPrefix().getLength() ) );
+ if( CharClass::isAsciiNumeric( aIndex ) && (aIndex.toInt32() > 0) )
+ rName = aIndex;
+ }
+ return !rName.isEmpty();
+}
+
+ScFormatFilterPluginImpl::ScFormatFilterPluginImpl() {}
+ScFormatFilterPluginImpl::~ScFormatFilterPluginImpl() {}
+
+ScOrcusFilters* ScFormatFilterPluginImpl::GetOrcusFilters()
+{
+ static ScOrcusFiltersImpl aImpl;
+ return &aImpl;
+}
+
+ScFormatFilterPlugin * ScFilterCreate()
+{
+ return new ScFormatFilterPluginImpl();
+}
+
+// implementation class inside the filters
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/sharedformulagroups.cxx b/sc/source/filter/ftools/sharedformulagroups.cxx
new file mode 100644
index 000000000..9b028d9eb
--- /dev/null
+++ b/sc/source/filter/ftools/sharedformulagroups.cxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <sharedformulagroups.hxx>
+#include <tokenarray.hxx>
+
+namespace sc {
+
+void SharedFormulaGroups::set( size_t nSharedId, std::unique_ptr<ScTokenArray> pArray )
+{
+ m_Store.try_emplace(nSharedId, std::move(pArray), ScAddress(ScAddress::INITIALIZE_INVALID));
+}
+
+void SharedFormulaGroups::set( size_t nSharedId, std::unique_ptr<ScTokenArray> pArray, const ScAddress& rOrigin )
+{
+ m_Store.try_emplace(nSharedId, std::move(pArray), rOrigin);
+}
+
+const ScTokenArray* SharedFormulaGroups::get( size_t nSharedId ) const
+{
+ StoreType::const_iterator const it = m_Store.find(nSharedId);
+ return it == m_Store.end() ? nullptr : it->second.getTokenArray();
+}
+
+const SharedFormulaGroupEntry* SharedFormulaGroups::getEntry( size_t nSharedId ) const
+{
+ StoreType::const_iterator const it = m_Store.find(nSharedId);
+ return it == m_Store.end() ? nullptr : &(it->second);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/html/htmlexp.cxx b/sc/source/filter/html/htmlexp.cxx
new file mode 100644
index 000000000..9c9f8745f
--- /dev/null
+++ b/sc/source/filter/html/htmlexp.cxx
@@ -0,0 +1,1378 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <vcl/svapp.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svx/xoutbmp.hxx>
+#include <editeng/editeng.hxx>
+#include <svtools/htmlcfg.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frmhtmlw.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/urihelper.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmlout.hxx>
+#include <svtools/parhtml.hxx>
+#include <vcl/outdev.hxx>
+#include <stdio.h>
+#include <osl/diagnose.h>
+#include <o3tl/unit_conversion.hxx>
+
+#include <htmlexp.hxx>
+#include <global.hxx>
+#include <postit.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <stlpool.hxx>
+#include <scresid.hxx>
+#include <formulacell.hxx>
+#include <cellform.hxx>
+#include <docoptio.hxx>
+#include <editutil.hxx>
+#include <ftools.hxx>
+#include <cellvalue.hxx>
+#include <mtvelements.hxx>
+
+#include <editeng/flditem.hxx>
+#include <editeng/borderline.hxx>
+
+// Without strings.hrc: error C2679: binary '=' : no operator defined which takes a
+// right-hand operand of type 'const class String (__stdcall *)(class ScResId)'
+// at
+// const String aStrTable( ScResId( SCSTR_TABLE ) ); aStrOut = aStrTable;
+// ?!???
+#include <strings.hrc>
+#include <globstr.hrc>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <rtl/strbuf.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+const char sMyBegComment[] = "<!-- ";
+const char sMyEndComment[] = " -->";
+const char sDisplay[] = "display:";
+const char sBorder[] = "border:";
+const char sBackground[] = "background:";
+
+const sal_uInt16 ScHTMLExport::nDefaultFontSize[SC_HTML_FONTSIZES] =
+{
+ HTMLFONTSZ1_DFLT, HTMLFONTSZ2_DFLT, HTMLFONTSZ3_DFLT, HTMLFONTSZ4_DFLT,
+ HTMLFONTSZ5_DFLT, HTMLFONTSZ6_DFLT, HTMLFONTSZ7_DFLT
+};
+
+sal_uInt16 ScHTMLExport::nFontSize[SC_HTML_FONTSIZES] = { 0 };
+
+const char* ScHTMLExport::pFontSizeCss[SC_HTML_FONTSIZES] =
+{
+ "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large"
+};
+
+const sal_uInt16 ScHTMLExport::nCellSpacing = 0;
+const char ScHTMLExport::sIndentSource[nIndentMax+1] =
+ "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+
+// Macros for HTML export
+
+#define TAG_ON( tag ) HTMLOutFuncs::Out_AsciiTag( rStrm, tag )
+#define TAG_OFF( tag ) HTMLOutFuncs::Out_AsciiTag( rStrm, tag, false )
+#define OUT_STR( str ) HTMLOutFuncs::Out_String( rStrm, str, &aNonConvertibleChars )
+#define OUT_LF() rStrm.WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( GetIndentStr() )
+#define TAG_ON_LF( tag ) (TAG_ON( tag ).WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( GetIndentStr() ))
+#define TAG_OFF_LF( tag ) (TAG_OFF( tag ).WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( GetIndentStr() ))
+#define OUT_HR() TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_horzrule )
+#define OUT_COMMENT( comment ) (rStrm.WriteCharPtr( sMyBegComment ), OUT_STR( comment ) \
+ .WriteCharPtr( sMyEndComment ).WriteCharPtr( SAL_NEWLINE_STRING ) \
+ .WriteCharPtr( GetIndentStr() ))
+
+#define OUT_SP_CSTR_ASS( s ) rStrm.WriteChar( ' ').WriteCharPtr( s ).WriteChar( '=' )
+
+#define GLOBSTR(id) ScResId( id )
+
+void ScFormatFilterPluginImpl::ScExportHTML( SvStream& rStrm, const OUString& rBaseURL, ScDocument* pDoc,
+ const ScRange& rRange, const rtl_TextEncoding /*eNach*/, bool bAll,
+ const OUString& rStreamPath, OUString& rNonConvertibleChars, const OUString& rFilterOptions )
+{
+ ScHTMLExport aEx( rStrm, rBaseURL, pDoc, rRange, bAll, rStreamPath, rFilterOptions );
+ aEx.Write();
+ rNonConvertibleChars = aEx.GetNonConvertibleChars();
+}
+
+static OString lcl_getColGroupString(sal_Int32 nSpan, sal_Int32 nWidth)
+{
+ OStringBuffer aByteStr(OOO_STRING_SVTOOLS_HTML_colgroup);
+ aByteStr.append(' ');
+ if( nSpan > 1 )
+ {
+ aByteStr.append(OOO_STRING_SVTOOLS_HTML_O_span);
+ aByteStr.append("=\"");
+ aByteStr.append(nSpan);
+ aByteStr.append("\" ");
+ }
+ aByteStr.append(OOO_STRING_SVTOOLS_HTML_O_width);
+ aByteStr.append("=\"");
+ aByteStr.append(nWidth);
+ aByteStr.append('"');
+ return aByteStr.makeStringAndClear();
+}
+
+static void lcl_AddStamp( OUString& rStr, std::u16string_view rName,
+ const css::util::DateTime& rDateTime,
+ const LocaleDataWrapper& rLoc )
+{
+ Date aD(rDateTime.Day, rDateTime.Month, rDateTime.Year);
+ tools::Time aT(rDateTime.Hours, rDateTime.Minutes, rDateTime.Seconds,
+ rDateTime.NanoSeconds);
+ DateTime aDateTime(aD,aT);
+
+ OUString aStrDate = rLoc.getDate( aDateTime );
+ OUString aStrTime = rLoc.getTime( aDateTime );
+
+ rStr += GLOBSTR( STR_BY ) + " ";
+ if (!rName.empty())
+ rStr += rName;
+ else
+ rStr += "???";
+ rStr += " " + GLOBSTR( STR_ON ) + " ";
+ if (!aStrDate.isEmpty())
+ rStr += aStrDate;
+ else
+ rStr += "???";
+ rStr += ", ";
+ if (!aStrTime.isEmpty())
+ rStr += aStrTime;
+ else
+ rStr += "???";
+}
+
+static OString lcl_makeHTMLColorTriplet(const Color& rColor)
+{
+ char buf[24];
+
+ // <font COLOR="#00FF40">hello</font>
+ snprintf( buf, 24, "\"#%02X%02X%02X\"", rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
+
+ return OString(buf);
+}
+
+ScHTMLExport::ScHTMLExport( SvStream& rStrmP, const OUString& rBaseURL, ScDocument* pDocP,
+ const ScRange& rRangeP, bool bAllP,
+ const OUString& rStreamPathP, std::u16string_view rFilterOptions ) :
+ ScExportBase( rStrmP, pDocP, rRangeP ),
+ aBaseURL( rBaseURL ),
+ aStreamPath( rStreamPathP ),
+ pAppWin( Application::GetDefaultDevice() ),
+ nUsedTables( 0 ),
+ nIndent( 0 ),
+ bAll( bAllP ),
+ bTabHasGraphics( false ),
+ bTabAlignedLeft( false ),
+ bCalcAsShown( pDocP->GetDocOptions().IsCalcAsShown() ),
+ bTableDataHeight( true ),
+ mbSkipImages ( false ),
+ mbSkipHeaderFooter( false )
+{
+ strcpy( sIndent, sIndentSource );
+ sIndent[0] = 0;
+
+ // set HTML configuration
+ bCopyLocalFileToINet = officecfg::Office::Common::Filter::HTML::Export::LocalGraphic::get();
+
+ if (rFilterOptions == u"SkipImages")
+ {
+ mbSkipImages = true;
+ }
+ else if (rFilterOptions == u"SkipHeaderFooter")
+ {
+ mbSkipHeaderFooter = true;
+ }
+
+ for ( sal_uInt16 j=0; j < SC_HTML_FONTSIZES; j++ )
+ {
+ sal_uInt16 nSize = SvxHtmlOptions::GetFontSize( j );
+ // remember in Twips, like our SvxFontHeightItem
+ if ( nSize )
+ nFontSize[j] = nSize * 20;
+ else
+ nFontSize[j] = nDefaultFontSize[j] * 20;
+ }
+
+ const SCTAB nCount = pDoc->GetTableCount();
+ for ( SCTAB nTab = 0; nTab < nCount; nTab++ )
+ {
+ if ( !IsEmptyTable( nTab ) )
+ nUsedTables++;
+ }
+}
+
+ScHTMLExport::~ScHTMLExport()
+{
+ aGraphList.clear();
+}
+
+sal_uInt16 ScHTMLExport::GetFontSizeNumber( sal_uInt16 nHeight )
+{
+ sal_uInt16 nSize = 1;
+ for ( sal_uInt16 j=SC_HTML_FONTSIZES-1; j>0; j-- )
+ {
+ if( nHeight > (nFontSize[j] + nFontSize[j-1]) / 2 )
+ { // The one next to it
+ nSize = j+1;
+ break;
+ }
+ }
+ return nSize;
+}
+
+const char* ScHTMLExport::GetFontSizeCss( sal_uInt16 nHeight )
+{
+ sal_uInt16 nSize = GetFontSizeNumber( nHeight );
+ return pFontSizeCss[ nSize-1 ];
+}
+
+sal_uInt16 ScHTMLExport::ToPixel( sal_uInt16 nVal )
+{
+ if( nVal )
+ {
+ nVal = static_cast<sal_uInt16>(pAppWin->LogicToPixel(
+ Size( nVal, nVal ), MapMode( MapUnit::MapTwip ) ).Width());
+ if( !nVal ) // If there's a Twip there should also be a Pixel
+ nVal = 1;
+ }
+ return nVal;
+}
+
+Size ScHTMLExport::MMToPixel( const Size& rSize )
+{
+ Size aSize = pAppWin->LogicToPixel( rSize, MapMode( MapUnit::Map100thMM ) );
+ // If there's something there should also be a Pixel
+ if ( !aSize.Width() && rSize.Width() )
+ aSize.setWidth( 1 );
+ if ( !aSize.Height() && rSize.Height() )
+ aSize.setHeight( 1 );
+ return aSize;
+}
+
+void ScHTMLExport::Write()
+{
+ if (!mbSkipHeaderFooter)
+ {
+ rStrm.WriteChar( '<' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_doctype ).WriteChar( ' ' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_doctype5 ).WriteChar( '>' )
+ .WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( SAL_NEWLINE_STRING );
+ TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_html );
+ WriteHeader();
+ OUT_LF();
+ }
+ WriteBody();
+ OUT_LF();
+ if (!mbSkipHeaderFooter)
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_html );
+}
+
+void ScHTMLExport::WriteHeader()
+{
+ IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_head );
+
+ if ( pDoc->IsClipOrUndo() )
+ { // no real DocInfo available, but some META information like charset needed
+ SfxFrameHTMLWriter::Out_DocInfo( rStrm, aBaseURL, nullptr, sIndent, &aNonConvertibleChars );
+ }
+ else
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps
+ = xDPS->getDocumentProperties();
+ SfxFrameHTMLWriter::Out_DocInfo( rStrm, aBaseURL, xDocProps,
+ sIndent, &aNonConvertibleChars );
+ OUT_LF();
+
+ if (!xDocProps->getPrintedBy().isEmpty())
+ {
+ OUT_COMMENT( GLOBSTR( STR_DOC_INFO ) );
+ OUString aStrOut = GLOBSTR( STR_DOC_PRINTED ) + ": ";
+ lcl_AddStamp( aStrOut, xDocProps->getPrintedBy(),
+ xDocProps->getPrintDate(), ScGlobal::getLocaleData() );
+ OUT_COMMENT( aStrOut );
+ }
+
+ }
+ OUT_LF();
+
+ // CSS1 StyleSheet
+ PageDefaults( bAll ? 0 : aRange.aStart.Tab() );
+ IncIndent(1);
+ rStrm.WriteCharPtr( "<" ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_style ).WriteCharPtr( " " ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_type ).WriteCharPtr( "=\"text/css\">" );
+
+ OUT_LF();
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_body);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_division);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_table);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_thead);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tbody);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tfoot);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tablerow);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tableheader);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_tabledata);
+ rStrm.WriteCharPtr(",");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_parabreak);
+ rStrm.WriteCharPtr(" { ");
+ rStrm.WriteCharPtr("font-family:");
+
+ if (!aHTMLStyle.aFontFamilyName.isEmpty())
+ {
+ const OUString& rList = aHTMLStyle.aFontFamilyName;
+ for(sal_Int32 nPos {0};;)
+ {
+ rStrm.WriteChar( '\"' );
+ OUT_STR( rList.getToken( 0, ';', nPos ) );
+ rStrm.WriteChar( '\"' );
+ if (nPos<0)
+ break;
+ rStrm.WriteCharPtr( ", " );
+ }
+ }
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr("font-size:");
+ rStrm.WriteCharPtr(GetFontSizeCss(static_cast<sal_uInt16>(aHTMLStyle.nFontHeight)));
+ rStrm.WriteCharPtr(" }");
+
+ OUT_LF();
+
+ // write the style for the comments to make them stand out from normal cell content
+ // this is done through only showing the cell contents when the custom indicator is hovered
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_anchor);
+ rStrm.WriteCharPtr(".comment-indicator:hover");
+ rStrm.WriteCharPtr(" + ");
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_comment2);
+ rStrm.WriteCharPtr(" { ");
+ rStrm.WriteCharPtr(sBackground);
+ rStrm.WriteCharPtr("#ffd");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr("position:");
+ rStrm.WriteCharPtr("absolute");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr(sDisplay);
+ rStrm.WriteCharPtr("block");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr(sBorder);
+ rStrm.WriteCharPtr("1px solid black");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr("padding:");
+ rStrm.WriteCharPtr("0.5em");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr(" } ");
+
+ OUT_LF();
+
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_anchor);
+ rStrm.WriteCharPtr(".comment-indicator");
+ rStrm.WriteCharPtr(" { ");
+ rStrm.WriteCharPtr(sBackground);
+ rStrm.WriteCharPtr("red");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr(sDisplay);
+ rStrm.WriteCharPtr("inline-block");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr(sBorder);
+ rStrm.WriteCharPtr("1px solid black");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr("width:");
+ rStrm.WriteCharPtr("0.5em");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr("height:");
+ rStrm.WriteCharPtr("0.5em");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr(" } ");
+
+ OUT_LF();
+
+ rStrm.WriteCharPtr(OOO_STRING_SVTOOLS_HTML_comment2);
+ rStrm.WriteCharPtr(" { ");
+ rStrm.WriteCharPtr(sDisplay);
+ rStrm.WriteCharPtr("none");
+ rStrm.WriteCharPtr("; ");
+ rStrm.WriteCharPtr(" } ");
+
+
+ IncIndent(-1);
+ OUT_LF();
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_style );
+
+ IncIndent(-1);
+ OUT_LF();
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_head );
+}
+
+void ScHTMLExport::WriteOverview()
+{
+ if ( nUsedTables <= 1 )
+ return;
+
+ IncIndent(1);
+ OUT_HR();
+ IncIndent(1); TAG_ON( OOO_STRING_SVTOOLS_HTML_parabreak ); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_center );
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_head1 );
+ OUT_STR( ScResId( STR_OVERVIEW ) );
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_head1 );
+
+ OUString aStr;
+
+ const SCTAB nCount = pDoc->GetTableCount();
+ for ( SCTAB nTab = 0; nTab < nCount; nTab++ )
+ {
+ if ( !IsEmptyTable( nTab ) )
+ {
+ pDoc->GetName( nTab, aStr );
+ rStrm.WriteCharPtr( "<A HREF=\"#table" )
+ .WriteOString( OString::number(nTab) )
+ .WriteCharPtr( "\">" );
+ OUT_STR( aStr );
+ rStrm.WriteCharPtr( "</A>" );
+ TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_linebreak );
+ }
+ }
+
+ IncIndent(-1); OUT_LF();
+ IncIndent(-1); TAG_OFF( OOO_STRING_SVTOOLS_HTML_center ); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_parabreak );
+}
+
+const SfxItemSet& ScHTMLExport::PageDefaults( SCTAB nTab )
+{
+ SfxStyleSheetBasePool* pStylePool = pDoc->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyleSheet = nullptr;
+ OSL_ENSURE( pStylePool, "StylePool not found! :-(" );
+
+ // remember defaults for compare in WriteCell
+ if ( !aHTMLStyle.bInitialized )
+ {
+ pStyleSheet = pStylePool->Find(
+ ScResId(STR_STYLENAME_STANDARD),
+ SfxStyleFamily::Para );
+ OSL_ENSURE( pStyleSheet, "ParaStyle not found! :-(" );
+ if (!pStyleSheet)
+ pStyleSheet = pStylePool->First(SfxStyleFamily::Para);
+ const SfxItemSet& rSetPara = pStyleSheet->GetItemSet();
+
+ aHTMLStyle.nDefaultScriptType = ScGlobal::GetDefaultScriptType();
+ aHTMLStyle.aFontFamilyName = static_cast<const SvxFontItem&>((rSetPara.Get(
+ ScGlobal::GetScriptedWhichID(
+ aHTMLStyle.nDefaultScriptType, ATTR_FONT
+ )))).GetFamilyName();
+ aHTMLStyle.nFontHeight = static_cast<const SvxFontHeightItem&>((rSetPara.Get(
+ ScGlobal::GetScriptedWhichID(
+ aHTMLStyle.nDefaultScriptType, ATTR_FONT_HEIGHT
+ )))).GetHeight();
+ aHTMLStyle.nFontSizeNumber = GetFontSizeNumber( static_cast< sal_uInt16 >( aHTMLStyle.nFontHeight ) );
+ }
+
+ // Page style sheet printer settings, e.g. for background graphics.
+ // There's only one background graphic in HTML!
+ pStyleSheet = pStylePool->Find( pDoc->GetPageStyle( nTab ), SfxStyleFamily::Page );
+ OSL_ENSURE( pStyleSheet, "PageStyle not found! :-(" );
+ if (!pStyleSheet)
+ pStyleSheet = pStylePool->First(SfxStyleFamily::Page);
+ const SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ if ( !aHTMLStyle.bInitialized )
+ {
+ const SvxBrushItem* pBrushItem = &rSet.Get( ATTR_BACKGROUND );
+ aHTMLStyle.aBackgroundColor = pBrushItem->GetColor();
+ aHTMLStyle.bInitialized = true;
+ }
+ return rSet;
+}
+
+OString ScHTMLExport::BorderToStyle(const char* pBorderName,
+ const SvxBorderLine* pLine, bool& bInsertSemicolon)
+{
+ OStringBuffer aOut;
+
+ if ( pLine )
+ {
+ if ( bInsertSemicolon )
+ aOut.append("; ");
+
+ // which border
+ aOut.append(OString::Concat("border-") + pBorderName + ": ");
+
+ // thickness
+ int nWidth = pLine->GetWidth();
+ int nPxWidth = (nWidth > 0) ?
+ std::max(o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px), sal_Int64(1)) : 0;
+ aOut.append(OString::number(nPxWidth) + "px ");
+ switch (pLine->GetBorderLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ aOut.append("solid");
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ aOut.append("dotted");
+ break;
+ case SvxBorderLineStyle::DASHED:
+ case SvxBorderLineStyle::DASH_DOT:
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ aOut.append("dashed");
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ aOut.append("double");
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ aOut.append("ridge");
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ aOut.append("groove");
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ aOut.append("outset");
+ break;
+ case SvxBorderLineStyle::INSET:
+ aOut.append("inset");
+ break;
+ default:
+ aOut.append("hidden");
+ }
+ aOut.append(" #");
+
+ // color
+ char hex[7];
+ snprintf( hex, 7, "%06" SAL_PRIxUINT32, static_cast<sal_uInt32>( pLine->GetColor().GetRGBColor() ) );
+ hex[6] = 0;
+
+ aOut.append(hex);
+
+ bInsertSemicolon = true;
+ }
+
+ return aOut.makeStringAndClear();
+}
+
+void ScHTMLExport::WriteBody()
+{
+ const SfxItemSet& rSet = PageDefaults( bAll ? 0 : aRange.aStart.Tab() );
+ const SvxBrushItem* pBrushItem = &rSet.Get( ATTR_BACKGROUND );
+
+ // default text color black
+ if (!mbSkipHeaderFooter)
+ {
+ rStrm.WriteChar( '<' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_body );
+
+ if (!mbSkipImages)
+ {
+ if ( bAll && GPOS_NONE != pBrushItem->GetGraphicPos() )
+ {
+ OUString aLink = pBrushItem->GetGraphicLink();
+ OUString aGrfNm;
+
+ // Embedded graphic -> write using WriteGraphic
+ if( aLink.isEmpty() )
+ {
+ const Graphic* pGrf = pBrushItem->GetGraphic();
+ if( pGrf )
+ {
+ // Save graphic as (JPG) file
+ aGrfNm = aStreamPath;
+ ErrCode nErr = XOutBitmap::WriteGraphic( *pGrf, aGrfNm,
+ "JPG", XOutFlags::UseNativeIfPossible );
+ if( !nErr ) // Contains errors, as we have nothing to output
+ {
+ aGrfNm = URIHelper::SmartRel2Abs(
+ INetURLObject(aBaseURL),
+ aGrfNm, URIHelper::GetMaybeFileHdl());
+ aLink = aGrfNm;
+ }
+ }
+ }
+ else
+ {
+ aGrfNm = aLink;
+ if( bCopyLocalFileToINet )
+ {
+ CopyLocalFileToINet( aGrfNm, aStreamPath );
+ }
+ else
+ aGrfNm = URIHelper::SmartRel2Abs(
+ INetURLObject(aBaseURL),
+ aGrfNm, URIHelper::GetMaybeFileHdl());
+ aLink = aGrfNm;
+ }
+ if( !aLink.isEmpty() )
+ {
+ rStrm.WriteChar( ' ' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_background ).WriteCharPtr( "=\"" );
+ OUT_STR( URIHelper::simpleNormalizedMakeRelative(
+ aBaseURL,
+ aLink ) ).WriteChar( '\"' );
+ }
+ }
+ }
+ if ( !aHTMLStyle.aBackgroundColor.IsTransparent() )
+ { // A transparent background color should always result in default
+ // background of the browser. Also, HTMLOutFuncs::Out_Color() writes
+ // black #000000 for COL_AUTO which is the same as white #ffffff with
+ // transparency set to 0xff, our default background.
+ OUT_SP_CSTR_ASS( OOO_STRING_SVTOOLS_HTML_O_bgcolor );
+ HTMLOutFuncs::Out_Color( rStrm, aHTMLStyle.aBackgroundColor );
+ }
+
+ rStrm.WriteChar( '>' ); OUT_LF();
+ }
+
+ if ( bAll )
+ WriteOverview();
+
+ WriteTables();
+
+ if (!mbSkipHeaderFooter)
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_body );
+}
+
+void ScHTMLExport::WriteTables()
+{
+ const SCTAB nTabCount = pDoc->GetTableCount();
+ const OUString aStrTable( ScResId( SCSTR_TABLE ) );
+ OUString aStr;
+ OUString aStrOut;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCTAB nStartTab;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ SCTAB nEndTab;
+ SCCOL nStartColFix = 0;
+ SCROW nStartRowFix = 0;
+ SCCOL nEndColFix = 0;
+ SCROW nEndRowFix = 0;
+ ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
+ if ( bAll )
+ {
+ nStartTab = 0;
+ nEndTab = nTabCount - 1;
+ }
+ else
+ {
+ nStartCol = nStartColFix = aRange.aStart.Col();
+ nStartRow = nStartRowFix = aRange.aStart.Row();
+ nStartTab = aRange.aStart.Tab();
+ nEndCol = nEndColFix = aRange.aEnd.Col();
+ nEndRow = nEndRowFix = aRange.aEnd.Row();
+ nEndTab = aRange.aEnd.Tab();
+ }
+ SCTAB nTableStrNum = 1;
+ for ( SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++ )
+ {
+ if ( !pDoc->IsVisible( nTab ) )
+ continue; // for
+
+ if ( bAll )
+ {
+ if ( !GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ) )
+ continue; // for
+
+ if ( nUsedTables > 1 )
+ {
+ aStrOut = aStrTable + " " + OUString::number( nTableStrNum++ ) + ": ";
+
+ OUT_HR();
+
+ // Write anchor
+ rStrm.WriteCharPtr( "<A NAME=\"table" )
+ .WriteOString( OString::number(nTab) )
+ .WriteCharPtr( "\">" );
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_head1 );
+ OUT_STR( aStrOut );
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_emphasis );
+
+ pDoc->GetName( nTab, aStr );
+ OUT_STR( aStr );
+
+ TAG_OFF( OOO_STRING_SVTOOLS_HTML_emphasis );
+ TAG_OFF( OOO_STRING_SVTOOLS_HTML_head1 );
+ rStrm.WriteCharPtr( "</A>" ); OUT_LF();
+ }
+ }
+ else
+ {
+ nStartCol = nStartColFix;
+ nStartRow = nStartRowFix;
+ nEndCol = nEndColFix;
+ nEndRow = nEndRowFix;
+ if ( !TrimDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ) )
+ continue; // for
+ }
+
+ // <TABLE ...>
+ OStringBuffer aByteStrOut(OOO_STRING_SVTOOLS_HTML_table);
+
+ bTabHasGraphics = bTabAlignedLeft = false;
+ if ( bAll && pDrawLayer )
+ PrepareGraphics( pDrawLayer, nTab, nStartCol, nStartRow,
+ nEndCol, nEndRow );
+
+ // more <TABLE ...>
+ if ( bTabAlignedLeft )
+ {
+ aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_align
+ "=\""
+ OOO_STRING_SVTOOLS_HTML_AL_left "\"");
+ }
+ // ALIGN=LEFT allow text and graphics to flow around
+ // CELLSPACING
+ aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_cellspacing
+ "=\"" +
+ OString::number(nCellSpacing) + "\"");
+
+ // BORDER=0, we do the styling of the cells in <TD>
+ aByteStrOut.append(" " OOO_STRING_SVTOOLS_HTML_O_border "=\"0\"");
+ IncIndent(1); TAG_ON_LF( aByteStrOut.makeStringAndClear().getStr() );
+
+ // --- <COLGROUP> ----
+ {
+ SCCOL nCol = nStartCol;
+ sal_Int32 nWidth = 0;
+ sal_Int32 nSpan = 0;
+ while( nCol <= nEndCol )
+ {
+ if( pDoc->ColHidden(nCol, nTab) )
+ {
+ ++nCol;
+ continue;
+ }
+
+ if( nWidth != ToPixel( pDoc->GetColWidth( nCol, nTab ) ) )
+ {
+ if( nSpan != 0 )
+ {
+ TAG_ON(lcl_getColGroupString(nSpan, nWidth).getStr());
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_colgroup );
+ }
+ nWidth = ToPixel( pDoc->GetColWidth( nCol, nTab ) );
+ nSpan = 1;
+ }
+ else
+ nSpan++;
+ nCol++;
+ }
+ if( nSpan )
+ {
+ TAG_ON(lcl_getColGroupString(nSpan, nWidth).getStr());
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_colgroup );
+ }
+ }
+
+ // <TBODY> // Re-enable only when THEAD and TFOOT are exported
+ // IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_tbody );
+ // At least old (3.x, 4.x?) Netscape doesn't follow <TABLE COLS=n> and
+ // <COL WIDTH=x> specified, but needs a width at every column.
+ bool bHasHiddenRows = pDoc->HasHiddenRows(nStartRow, nEndRow, nTab);
+ // We need to cache sc::ColumnBlockPosition per each column.
+ std::vector< sc::ColumnBlockPosition > blockPos( nEndCol - nStartCol + 1 );
+ for( SCCOL i = nStartCol; i <= nEndCol; ++i )
+ pDoc->InitColumnBlockPosition( blockPos[ i - nStartCol ], nTab, i );
+ for ( SCROW nRow=nStartRow; nRow<=nEndRow; nRow++ )
+ {
+ if ( bHasHiddenRows && pDoc->RowHidden(nRow, nTab) )
+ {
+ nRow = pDoc->FirstVisibleRow(nRow+1, nEndRow, nTab);
+ --nRow;
+ continue; // for
+ }
+
+ IncIndent(1); TAG_ON_LF( OOO_STRING_SVTOOLS_HTML_tablerow );
+ bTableDataHeight = true; // height at every first cell of each row
+ for ( SCCOL nCol2=nStartCol; nCol2<=nEndCol; nCol2++ )
+ {
+ if ( pDoc->ColHidden(nCol2, nTab) )
+ continue; // for
+
+ if ( nCol2 == nEndCol )
+ IncIndent(-1);
+ WriteCell( blockPos[ nCol2 - nStartCol ], nCol2, nRow, nTab );
+ bTableDataHeight = false;
+ }
+
+ if ( nRow == nEndRow )
+ IncIndent(-1);
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tablerow );
+ }
+ // TODO: Uncomment later
+ // IncIndent(-1); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tbody );
+
+ IncIndent(-1); TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_table );
+
+ if ( bTabHasGraphics && !mbSkipImages )
+ {
+ // the rest that is not in a cell
+ size_t ListSize = aGraphList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScHTMLGraphEntry* pE = &aGraphList[ i ];
+ if ( !pE->bWritten )
+ WriteGraphEntry( pE );
+ }
+ aGraphList.clear();
+ if ( bTabAlignedLeft )
+ {
+ // clear <TABLE ALIGN=LEFT> with <BR CLEAR=LEFT>
+ aByteStrOut.append(OOO_STRING_SVTOOLS_HTML_linebreak);
+ aByteStrOut.append(' ').
+ append(OOO_STRING_SVTOOLS_HTML_O_clear).append('=').
+ append(OOO_STRING_SVTOOLS_HTML_AL_left);
+ TAG_ON_LF( aByteStrOut.makeStringAndClear().getStr() );
+ }
+ }
+
+ if ( bAll )
+ OUT_COMMENT( "**************************************************************************" );
+ }
+}
+
+void ScHTMLExport::WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ ScAddress aPos( nCol, nRow, nTab );
+ ScRefCellValue aCell(*pDoc, aPos, rBlockPos);
+ const ScPatternAttr* pAttr = pDoc->GetPattern( nCol, nRow, nTab );
+ const SfxItemSet* pCondItemSet = pDoc->GetCondResult( nCol, nRow, nTab, &aCell );
+
+ const ScMergeFlagAttr& rMergeFlagAttr = pAttr->GetItem( ATTR_MERGE_FLAG, pCondItemSet );
+ if ( rMergeFlagAttr.IsOverlapped() )
+ return ;
+
+ ScHTMLGraphEntry* pGraphEntry = nullptr;
+ if ( bTabHasGraphics && !mbSkipImages )
+ {
+ size_t ListSize = aGraphList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScHTMLGraphEntry* pE = &aGraphList[ i ];
+ if ( pE->bInCell && pE->aRange.Contains( aPos ) )
+ {
+ if ( pE->aRange.aStart == aPos )
+ {
+ pGraphEntry = pE;
+ break; // for
+ }
+ else
+ return ; // Is a Col/RowSpan, Overlapped
+ }
+ }
+ }
+
+ sal_uInt32 nFormat = pAttr->GetNumberFormat( pFormatter );
+ bool bValueData = aCell.hasNumeric();
+ SvtScriptType nScriptType = SvtScriptType::NONE;
+ if (!aCell.isEmpty())
+ nScriptType = pDoc->GetScriptType(nCol, nRow, nTab, &aCell);
+
+ if ( nScriptType == SvtScriptType::NONE )
+ nScriptType = aHTMLStyle.nDefaultScriptType;
+
+ OStringBuffer aStrTD(OOO_STRING_SVTOOLS_HTML_tabledata);
+
+ // border of the cells
+ const SvxBoxItem* pBorder = pDoc->GetAttr( nCol, nRow, nTab, ATTR_BORDER );
+ if ( pBorder && (pBorder->GetTop() || pBorder->GetBottom() || pBorder->GetLeft() || pBorder->GetRight()) )
+ {
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_style "=\"");
+
+ bool bInsertSemicolon = false;
+ aStrTD.append(BorderToStyle("top", pBorder->GetTop(),
+ bInsertSemicolon));
+ aStrTD.append(BorderToStyle("bottom", pBorder->GetBottom(),
+ bInsertSemicolon));
+ aStrTD.append(BorderToStyle("left", pBorder->GetLeft(),
+ bInsertSemicolon));
+ aStrTD.append(BorderToStyle("right", pBorder->GetRight(),
+ bInsertSemicolon));
+
+ aStrTD.append('"');
+ }
+
+ const char* pChar;
+ sal_uInt16 nHeightPixel;
+
+ const ScMergeAttr& rMergeAttr = pAttr->GetItem( ATTR_MERGE, pCondItemSet );
+ if ( pGraphEntry || rMergeAttr.IsMerged() )
+ {
+ SCCOL nC, jC;
+ SCROW nR;
+ tools::Long v;
+ if ( pGraphEntry )
+ nC = std::max( SCCOL(pGraphEntry->aRange.aEnd.Col() - nCol + 1),
+ rMergeAttr.GetColMerge() );
+ else
+ nC = rMergeAttr.GetColMerge();
+ if ( nC > 1 )
+ {
+ aStrTD.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_colspan).
+ append('=').append(static_cast<sal_Int32>(nC));
+ nC = nC + nCol;
+ for ( jC=nCol, v=0; jC<nC; jC++ )
+ v += pDoc->GetColWidth( jC, nTab );
+ }
+
+ if ( pGraphEntry )
+ nR = std::max( SCROW(pGraphEntry->aRange.aEnd.Row() - nRow + 1),
+ rMergeAttr.GetRowMerge() );
+ else
+ nR = rMergeAttr.GetRowMerge();
+ if ( nR > 1 )
+ {
+ aStrTD.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_rowspan).
+ append('=').append(static_cast<sal_Int32>(nR));
+ nR += nRow;
+ v = pDoc->GetRowHeight( nRow, nR-1, nTab );
+ nHeightPixel = ToPixel( static_cast< sal_uInt16 >( v ) );
+ }
+ else
+ nHeightPixel = ToPixel( pDoc->GetRowHeight( nRow, nTab ) );
+ }
+ else
+ nHeightPixel = ToPixel( pDoc->GetRowHeight( nRow, nTab ) );
+
+ if ( bTableDataHeight )
+ {
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_height "=\"" +
+ OString::number(nHeightPixel) + "\"");
+ }
+
+ const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>( pAttr->GetItem(
+ ScGlobal::GetScriptedWhichID( nScriptType, ATTR_FONT),
+ pCondItemSet) );
+
+ const SvxFontHeightItem& rFontHeightItem = static_cast<const SvxFontHeightItem&>(
+ pAttr->GetItem( ScGlobal::GetScriptedWhichID( nScriptType,
+ ATTR_FONT_HEIGHT), pCondItemSet) );
+
+ const SvxWeightItem& rWeightItem = static_cast<const SvxWeightItem&>( pAttr->GetItem(
+ ScGlobal::GetScriptedWhichID( nScriptType, ATTR_FONT_WEIGHT),
+ pCondItemSet) );
+
+ const SvxPostureItem& rPostureItem = static_cast<const SvxPostureItem&>(
+ pAttr->GetItem( ScGlobal::GetScriptedWhichID( nScriptType,
+ ATTR_FONT_POSTURE), pCondItemSet) );
+
+ const SvxUnderlineItem& rUnderlineItem =
+ pAttr->GetItem( ATTR_FONT_UNDERLINE, pCondItemSet );
+
+ const SvxCrossedOutItem& rCrossedOutItem =
+ pAttr->GetItem( ATTR_FONT_CROSSEDOUT, pCondItemSet );
+
+ const SvxColorItem& rColorItem = pAttr->GetItem(
+ ATTR_FONT_COLOR, pCondItemSet );
+
+ const SvxHorJustifyItem& rHorJustifyItem =
+ pAttr->GetItem( ATTR_HOR_JUSTIFY, pCondItemSet );
+
+ const SvxVerJustifyItem& rVerJustifyItem =
+ pAttr->GetItem( ATTR_VER_JUSTIFY, pCondItemSet );
+
+ const SvxBrushItem& rBrushItem = pAttr->GetItem(
+ ATTR_BACKGROUND, pCondItemSet );
+
+ Color aBgColor;
+ if ( rBrushItem.GetColor().GetAlpha() == 0 )
+ aBgColor = aHTMLStyle.aBackgroundColor; // No unwanted background color
+ else
+ aBgColor = rBrushItem.GetColor();
+
+ bool bBold = ( WEIGHT_BOLD <= rWeightItem.GetWeight() );
+ bool bItalic = ( ITALIC_NONE != rPostureItem.GetPosture() );
+ bool bUnderline = ( LINESTYLE_NONE != rUnderlineItem.GetLineStyle() );
+ bool bCrossedOut = ( STRIKEOUT_SINGLE <= rCrossedOutItem.GetStrikeout() );
+ bool bSetFontColor = ( COL_AUTO != rColorItem.GetValue() ); // default is AUTO now
+ bool bSetFontName = ( aHTMLStyle.aFontFamilyName != rFontItem.GetFamilyName() );
+ sal_uInt16 nSetFontSizeNumber = 0;
+ sal_uInt32 nFontHeight = rFontHeightItem.GetHeight();
+ if ( nFontHeight != aHTMLStyle.nFontHeight )
+ {
+ nSetFontSizeNumber = GetFontSizeNumber( static_cast<sal_uInt16>(nFontHeight) );
+ if ( nSetFontSizeNumber == aHTMLStyle.nFontSizeNumber )
+ nSetFontSizeNumber = 0; // no difference, don't set
+ }
+
+ bool bSetFont = (bSetFontColor || bSetFontName || nSetFontSizeNumber);
+
+ //! TODO: we could entirely use CSS1 here instead, but that would exclude
+ //! Netscape 3.0 and Netscape 4.x without JavaScript enabled.
+ //! Do we want that?
+
+ switch( rHorJustifyItem.GetValue() )
+ {
+ case SvxCellHorJustify::Standard:
+ pChar = (bValueData ? OOO_STRING_SVTOOLS_HTML_AL_right : OOO_STRING_SVTOOLS_HTML_AL_left);
+ break;
+ case SvxCellHorJustify::Center: pChar = OOO_STRING_SVTOOLS_HTML_AL_center; break;
+ case SvxCellHorJustify::Block: pChar = OOO_STRING_SVTOOLS_HTML_AL_justify; break;
+ case SvxCellHorJustify::Right: pChar = OOO_STRING_SVTOOLS_HTML_AL_right; break;
+ case SvxCellHorJustify::Left:
+ case SvxCellHorJustify::Repeat:
+ default: pChar = OOO_STRING_SVTOOLS_HTML_AL_left; break;
+ }
+
+ aStrTD.append(" " OOO_STRING_SVTOOLS_HTML_O_align "=\"" +
+ OString::Concat(pChar) + "\"");
+
+ switch( rVerJustifyItem.GetValue() )
+ {
+ case SvxCellVerJustify::Top: pChar = OOO_STRING_SVTOOLS_HTML_VA_top; break;
+ case SvxCellVerJustify::Center: pChar = OOO_STRING_SVTOOLS_HTML_VA_middle; break;
+ case SvxCellVerJustify::Bottom: pChar = OOO_STRING_SVTOOLS_HTML_VA_bottom; break;
+ case SvxCellVerJustify::Standard:
+ default: pChar = nullptr;
+ }
+ if ( pChar )
+ {
+ aStrTD.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_valign).
+ append('=').append(pChar);
+ }
+
+ if ( aHTMLStyle.aBackgroundColor != aBgColor )
+ {
+ aStrTD.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_bgcolor).
+ append('=');
+ aStrTD.append(lcl_makeHTMLColorTriplet(aBgColor));
+ }
+
+ double fVal = 0.0;
+ if ( bValueData )
+ {
+ switch (aCell.meType)
+ {
+ case CELLTYPE_VALUE:
+ fVal = aCell.mfValue;
+ if ( bCalcAsShown && fVal != 0.0 )
+ fVal = pDoc->RoundValueAsShown( fVal, nFormat );
+ break;
+ case CELLTYPE_FORMULA:
+ fVal = aCell.mpFormula->GetValue();
+ break;
+ default:
+ OSL_FAIL( "value data with unsupported cell type" );
+ }
+ }
+
+ aStrTD.append(HTMLOutFuncs::CreateTableDataOptionsValNum(bValueData, fVal,
+ nFormat, *pFormatter, &aNonConvertibleChars));
+
+ TAG_ON(aStrTD.makeStringAndClear().getStr());
+
+ //write the note for this as the first thing in the tag
+ ScPostIt* pNote = pDoc->HasNote(aPos) ? pDoc->GetNote(aPos) : nullptr;
+ if (pNote)
+ {
+ //create the comment indicator
+ OString aStr = OOO_STRING_SVTOOLS_HTML_anchor " "
+ OOO_STRING_SVTOOLS_HTML_O_class "=\"comment-indicator\"";
+ TAG_ON(aStr.getStr());
+ TAG_OFF(OOO_STRING_SVTOOLS_HTML_anchor);
+ OUT_LF();
+
+ //create the element holding the contents
+ //this is a bit naive, since it doesn't separate
+ //lines into html breaklines yet
+ TAG_ON(OOO_STRING_SVTOOLS_HTML_comment2);
+ OUT_STR( pNote->GetText() );
+ TAG_OFF(OOO_STRING_SVTOOLS_HTML_comment2);
+ OUT_LF();
+ }
+
+ if ( bBold ) TAG_ON( OOO_STRING_SVTOOLS_HTML_bold );
+ if ( bItalic ) TAG_ON( OOO_STRING_SVTOOLS_HTML_italic );
+ if ( bUnderline ) TAG_ON( OOO_STRING_SVTOOLS_HTML_underline );
+ if ( bCrossedOut ) TAG_ON( OOO_STRING_SVTOOLS_HTML_strikethrough );
+
+ if ( bSetFont )
+ {
+ OStringBuffer aStr(OOO_STRING_SVTOOLS_HTML_font);
+ if ( bSetFontName )
+ {
+ aStr.append(" " OOO_STRING_SVTOOLS_HTML_O_face "=\"");
+
+ if (!rFontItem.GetFamilyName().isEmpty())
+ {
+ const OUString& rList = rFontItem.GetFamilyName();
+ for (sal_Int32 nPos {0};;)
+ {
+ OString aTmpStr = HTMLOutFuncs::ConvertStringToHTML(
+ rList.getToken( 0, ';', nPos ),
+ &aNonConvertibleChars);
+ aStr.append(aTmpStr);
+ if (nPos<0)
+ break;
+ aStr.append(',');
+ }
+ }
+
+ aStr.append('\"');
+ }
+ if ( nSetFontSizeNumber )
+ {
+ aStr.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_size).
+ append('=').append(static_cast<sal_Int32>(nSetFontSizeNumber));
+ }
+ if ( bSetFontColor )
+ {
+ Color aColor = rColorItem.GetValue();
+
+ // always export automatic text color as black
+ if ( aColor == COL_AUTO )
+ aColor = COL_BLACK;
+
+ aStr.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_color).
+ append('=').append(lcl_makeHTMLColorTriplet(aColor));
+ }
+ TAG_ON(aStr.makeStringAndClear().getStr());
+ }
+
+ OUString aURL;
+ bool bWriteHyperLink(false);
+ if (aCell.meType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = aCell.mpFormula;
+ if (pFCell->IsHyperLinkCell())
+ {
+ OUString aCellText;
+ pFCell->GetURLResult(aURL, aCellText);
+ bWriteHyperLink = true;
+ }
+ }
+
+ if (bWriteHyperLink)
+ {
+ OString aURLStr = HTMLOutFuncs::ConvertStringToHTML(aURL, &aNonConvertibleChars);
+ OString aStr = OOO_STRING_SVTOOLS_HTML_anchor " " OOO_STRING_SVTOOLS_HTML_O_href "=\"" + aURLStr + "\"";
+ TAG_ON(aStr.getStr());
+ }
+
+ OUString aStrOut;
+ bool bFieldText = false;
+
+ const Color* pColor;
+ switch (aCell.meType)
+ {
+ case CELLTYPE_EDIT :
+ bFieldText = WriteFieldText(aCell.mpEditText);
+ if ( bFieldText )
+ break;
+ [[fallthrough]];
+ default:
+ aStrOut = ScCellFormat::GetString(aCell, nFormat, &pColor, *pFormatter, *pDoc);
+ }
+
+ if ( !bFieldText )
+ {
+ if ( aStrOut.isEmpty() )
+ {
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak ); // No completely empty line
+ }
+ else
+ {
+ sal_Int32 nPos = aStrOut.indexOf( '\n' );
+ if ( nPos == -1 )
+ {
+ OUT_STR( aStrOut );
+ }
+ else
+ {
+ sal_Int32 nStartPos = 0;
+ do
+ {
+ OUString aSingleLine = aStrOut.copy( nStartPos, nPos - nStartPos );
+ OUT_STR( aSingleLine );
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
+ nStartPos = nPos + 1;
+ }
+ while( ( nPos = aStrOut.indexOf( '\n', nStartPos ) ) != -1 );
+ OUString aSingleLine = aStrOut.copy( nStartPos );
+ OUT_STR( aSingleLine );
+ }
+ }
+ }
+ if ( pGraphEntry )
+ WriteGraphEntry( pGraphEntry );
+
+ if (bWriteHyperLink) { TAG_OFF(OOO_STRING_SVTOOLS_HTML_anchor); }
+
+ if ( bSetFont ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_font );
+ if ( bCrossedOut ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_strikethrough );
+ if ( bUnderline ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_underline );
+ if ( bItalic ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_italic );
+ if ( bBold ) TAG_OFF( OOO_STRING_SVTOOLS_HTML_bold );
+
+ TAG_OFF_LF( OOO_STRING_SVTOOLS_HTML_tabledata );
+}
+
+bool ScHTMLExport::WriteFieldText( const EditTextObject* pData )
+{
+ bool bFields = false;
+ // text and anchor of URL fields, Doc-Engine is a ScFieldEditEngine
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText( *pData );
+ sal_Int32 nParas = rEngine.GetParagraphCount();
+ if ( nParas )
+ {
+ ESelection aSel( 0, 0, nParas-1, rEngine.GetTextLen( nParas-1 ) );
+ SfxItemSet aSet( rEngine.GetAttribs( aSel ) );
+ SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
+ if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
+ bFields = true;
+ }
+ if ( bFields )
+ {
+ bool bOldUpdateMode = rEngine.SetUpdateLayout( true ); // no portions if not formatted
+ for ( sal_Int32 nPar=0; nPar < nParas; nPar++ )
+ {
+ if ( nPar > 0 )
+ TAG_ON( OOO_STRING_SVTOOLS_HTML_linebreak );
+ std::vector<sal_Int32> aPortions;
+ rEngine.GetPortions( nPar, aPortions );
+ sal_Int32 nStart = 0;
+ for ( const sal_Int32 nEnd : aPortions )
+ {
+ ESelection aSel( nPar, nStart, nPar, nEnd );
+ bool bUrl = false;
+ // fields are single characters
+ if ( nEnd == nStart+1 )
+ {
+ SfxItemSet aSet = rEngine.GetAttribs( aSel );
+ if ( const SvxFieldItem* pFieldItem = aSet.GetItemIfSet( EE_FEATURE_FIELD, false ) )
+ {
+ const SvxFieldData* pField = pFieldItem->GetField();
+ if (const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(pField))
+ {
+ bUrl = true;
+ rStrm.WriteChar( '<' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_anchor ).WriteChar( ' ' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_href ).WriteCharPtr( "=\"" );
+ OUT_STR( pURLField->GetURL() );
+ rStrm.WriteCharPtr( "\">" );
+ OUT_STR( pURLField->GetRepresentation() );
+ rStrm.WriteCharPtr( "</" ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_anchor ).WriteChar( '>' );
+ }
+ }
+ }
+ if ( !bUrl )
+ OUT_STR( rEngine.GetText( aSel ) );
+ nStart = nEnd;
+ }
+ }
+ rEngine.SetUpdateLayout( bOldUpdateMode );
+ }
+ return bFields;
+}
+
+void ScHTMLExport::CopyLocalFileToINet( OUString& rFileNm,
+ std::u16string_view rTargetNm )
+{
+ INetURLObject aFileUrl, aTargetUrl;
+ aFileUrl.SetSmartURL( rFileNm );
+ aTargetUrl.SetSmartURL( rTargetNm );
+ if( !(INetProtocol::File == aFileUrl.GetProtocol() &&
+ ( INetProtocol::File != aTargetUrl.GetProtocol() &&
+ INetProtocol::Ftp <= aTargetUrl.GetProtocol() &&
+ INetProtocol::Javascript >= aTargetUrl.GetProtocol())) )
+ return;
+
+ if( pFileNameMap )
+ {
+ // Did we already move the file?
+ std::map<OUString, OUString>::iterator it = pFileNameMap->find( rFileNm );
+ if( it != pFileNameMap->end() )
+ {
+ rFileNm = it->second;
+ return;
+ }
+ }
+ else
+ {
+ pFileNameMap.reset( new std::map<OUString, OUString> );
+ }
+
+ bool bRet = false;
+ SvFileStream aTmp( aFileUrl.PathToFileName(), StreamMode::READ );
+
+ OUString aSrc = rFileNm;
+ OUString aDest = aTargetUrl.GetPartBeforeLastName() + aFileUrl.GetLastName();
+
+ SfxMedium aMedium( aDest, StreamMode::WRITE | StreamMode::SHARE_DENYNONE );
+
+ {
+ SvFileStream aCpy( aMedium.GetPhysicalName(), StreamMode::WRITE );
+ aCpy.WriteStream( aTmp );
+ }
+
+ // Take over
+ aMedium.Close();
+ aMedium.Commit();
+
+ bRet = ERRCODE_NONE == aMedium.GetError();
+
+ if( bRet )
+ {
+ pFileNameMap->insert( std::make_pair( aSrc, aDest ) );
+ rFileNm = aDest;
+ }
+}
+
+void ScHTMLExport::IncIndent( short nVal )
+{
+ sIndent[nIndent] = '\t';
+ nIndent = nIndent + nVal;
+ if ( nIndent < 0 )
+ nIndent = 0;
+ else if ( nIndent > nIndentMax )
+ nIndent = nIndentMax;
+ sIndent[nIndent] = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/html/htmlexp2.cxx b/sc/source/filter/html/htmlexp2.cxx
new file mode 100644
index 000000000..475fea96c
--- /dev/null
+++ b/sc/source/filter/html/htmlexp2.cxx
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/xoutbmp.hxx>
+#include <svx/svdxcgv.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmlout.hxx>
+#include <svl/urihelper.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+
+#include <htmlexp.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <rtl/strbuf.hxx>
+
+using namespace com::sun::star;
+
+void ScHTMLExport::PrepareGraphics( ScDrawLayer* pDrawLayer, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ if ( !pDrawLayer->HasObjectsInRows( nTab, nStartRow, nEndRow ) )
+ return;
+
+ SdrPage* pDrawPage = pDrawLayer->GetPage( static_cast<sal_uInt16>(nTab) );
+ if ( !pDrawPage )
+ return;
+
+ bTabHasGraphics = true;
+ FillGraphList( pDrawPage, nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+ size_t ListSize = aGraphList.size();
+ for ( size_t i = 0; i < ListSize; ++i )
+ {
+ ScHTMLGraphEntry* pE = &aGraphList[ i ];
+ if ( !pE->bInCell )
+ { // not all cells: table next to some
+ bTabAlignedLeft = true;
+ break;
+ }
+ }
+}
+
+void ScHTMLExport::FillGraphList( const SdrPage* pPage, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow )
+{
+ if ( !pPage->GetObjCount() )
+ return;
+
+ tools::Rectangle aRect;
+ if ( !bAll )
+ aRect = pDoc->GetMMRect( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
+ SdrObjListIter aIter( pPage, SdrIterMode::Flat );
+ SdrObject* pObject = aIter.Next();
+ while ( pObject )
+ {
+ tools::Rectangle aObjRect = pObject->GetCurrentBoundRect();
+ if ( (bAll || aRect.Contains( aObjRect )) && !ScDrawLayer::IsNoteCaption(pObject) )
+ {
+ Size aSpace;
+ ScRange aR = pDoc->GetRange( nTab, aObjRect );
+ // Rectangle in mm/100
+ Size aSize( MMToPixel( aObjRect.GetSize() ) );
+ // If the image is somewhere in a merged range we must
+ // move the anchor to the upper left (THE span cell).
+ pDoc->ExtendOverlapped( aR );
+ SCCOL nCol1 = aR.aStart.Col();
+ SCROW nRow1 = aR.aStart.Row();
+ SCCOL nCol2 = aR.aEnd.Col();
+ SCROW nRow2 = aR.aEnd.Row();
+ // All cells empty under object?
+ bool bInCell = pDoc->IsEmptyData( nCol1, nRow1, nCol2, nRow2, nTab );
+ if ( bInCell )
+ { // Spacing in spanning cell
+ tools::Rectangle aCellRect = pDoc->GetMMRect(
+ nCol1, nRow1, nCol2, nRow2, nTab );
+ aSpace = MMToPixel( Size(
+ aCellRect.GetWidth() - aObjRect.GetWidth(),
+ aCellRect.GetHeight() - aObjRect.GetHeight() ));
+ aSpace.AdjustWidth((nCol2-nCol1) * (nCellSpacing+1) );
+ aSpace.AdjustHeight((nRow2-nRow1) * (nCellSpacing+1) );
+ aSpace.setWidth( aSpace.Width() / 2 );
+ aSpace.setHeight( aSpace.Height() / 2 );
+ }
+ aGraphList.emplace_back( pObject,
+ aR, aSize, bInCell, aSpace );
+ }
+ pObject = aIter.Next();
+ }
+}
+
+void ScHTMLExport::WriteGraphEntry( ScHTMLGraphEntry* pE )
+{
+ SdrObject* pObject = pE->pObject;
+ OStringBuffer aBuf;
+ aBuf.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_width).append('=').
+ append(static_cast<sal_Int32>(pE->aSize.Width()));
+ aBuf.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_height).append('=').
+ append(static_cast<sal_Int32>(pE->aSize.Height()));
+ if ( pE->bInCell )
+ {
+ aBuf.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_hspace).append('=').
+ append(static_cast<sal_Int32>(pE->aSpace.Width()));
+ aBuf.append(' ').append(OOO_STRING_SVTOOLS_HTML_O_vspace).append('=').
+ append(static_cast<sal_Int32>(pE->aSpace.Height()));
+ }
+ OString aOpt = aBuf.makeStringAndClear();
+ switch ( pObject->GetObjIdentifier() )
+ {
+ case SdrObjKind::Graphic:
+ {
+ const SdrGrafObj* pSGO = static_cast<SdrGrafObj*>(pObject);
+ std::unique_ptr<SdrGrafObjGeoData> pGeo(static_cast<SdrGrafObjGeoData*>(pSGO->GetGeoData().release()));
+ sal_uInt16 nMirrorCase = (pGeo->maGeo.nRotationAngle == 18000_deg100 ?
+ ( pGeo->bMirrored ? 3 : 4 ) : ( pGeo->bMirrored ? 2 : 1 ));
+ bool bHMirr = ( ( nMirrorCase == 2 ) || ( nMirrorCase == 4 ) );
+ bool bVMirr = ( ( nMirrorCase == 3 ) || ( nMirrorCase == 4 ) );
+ XOutFlags nXOutFlags = XOutFlags::NONE;
+ if ( bHMirr )
+ nXOutFlags |= XOutFlags::MirrorHorz;
+ if ( bVMirr )
+ nXOutFlags |= XOutFlags::MirrorVert;
+ OUString aLinkName;
+ if ( pSGO->IsLinkedGraphic() )
+ aLinkName = pSGO->GetFileName();
+ WriteImage( aLinkName, pSGO->GetGraphic(), aOpt, nXOutFlags );
+ pE->bWritten = true;
+ }
+ break;
+ case SdrObjKind::OLE2:
+ {
+ const Graphic* pGraphic = static_cast<SdrOle2Obj*>(pObject)->GetGraphic();
+ if ( pGraphic )
+ {
+ OUString aLinkName;
+ WriteImage( aLinkName, *pGraphic, aOpt );
+ pE->bWritten = true;
+ }
+ }
+ break;
+ default:
+ {
+ Graphic aGraph(SdrExchangeView::GetObjGraphic(*pObject));
+ OUString aLinkName;
+ WriteImage( aLinkName, aGraph, aOpt );
+ pE->bWritten = true;
+ }
+ }
+}
+
+void ScHTMLExport::WriteImage( OUString& rLinkName, const Graphic& rGrf,
+ std::string_view rImgOptions, XOutFlags nXOutFlags )
+{
+ // Embedded graphic -> create an image file
+ if( rLinkName.isEmpty() )
+ {
+ if( !aStreamPath.isEmpty() )
+ {
+ // Save as a PNG
+ OUString aGrfNm( aStreamPath );
+ nXOutFlags |= XOutFlags::UseNativeIfPossible;
+ ErrCode nErr = XOutBitmap::WriteGraphic( rGrf, aGrfNm,
+ "PNG", nXOutFlags );
+
+ // If it worked, create a URL for the IMG tag
+ if( !nErr )
+ {
+ rLinkName = URIHelper::SmartRel2Abs(
+ INetURLObject(aBaseURL),
+ aGrfNm,
+ URIHelper::GetMaybeFileHdl());
+ }
+ }
+ }
+ else
+ {
+ // Linked graphic - figure out the URL for the IMG tag
+ if( bCopyLocalFileToINet )
+ {
+ CopyLocalFileToINet( rLinkName, aStreamPath );
+ }
+ else
+ rLinkName = URIHelper::SmartRel2Abs(
+ INetURLObject(aBaseURL),
+ rLinkName,
+ URIHelper::GetMaybeFileHdl());
+ }
+
+ // If a URL was set, output the IMG tag.
+ // <IMG SRC="..."[ rImgOptions]>
+ if( !rLinkName.isEmpty() )
+ {
+ rStrm.WriteChar( '<' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_image ).WriteChar( ' ' ).WriteCharPtr( OOO_STRING_SVTOOLS_HTML_O_src ).WriteCharPtr( "=\"" );
+ HTMLOutFuncs::Out_String( rStrm, URIHelper::simpleNormalizedMakeRelative(
+ aBaseURL,
+ rLinkName ) ).WriteChar( '\"' );
+ if ( !rImgOptions.empty() )
+ rStrm.WriteOString( rImgOptions );
+ rStrm.WriteChar( '>' ).WriteCharPtr( SAL_NEWLINE_STRING ).WriteCharPtr( GetIndentStr() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/html/htmlimp.cxx b/sc/source/filter/html/htmlimp.cxx
new file mode 100644
index 000000000..12e98a9ef
--- /dev/null
+++ b/sc/source/filter/html/htmlimp.cxx
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+
+#include <editeng/lrspitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <htmlimp.hxx>
+#include <htmlpars.hxx>
+#include <filter.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <refdata.hxx>
+#include <rangenam.hxx>
+#include <attrib.hxx>
+#include <ftools.hxx>
+#include <tokenarray.hxx>
+
+ErrCode ScFormatFilterPluginImpl::ScImportHTML( SvStream &rStream, const OUString& rBaseURL, ScDocument *pDoc,
+ ScRange& rRange, double nOutputFactor, bool bCalcWidthHeight, SvNumberFormatter* pFormatter,
+ bool bConvertDate )
+{
+ ScHTMLImport aImp( pDoc, rBaseURL, rRange, bCalcWidthHeight );
+ ErrCode nErr = aImp.Read( rStream, rBaseURL );
+ ScRange aR = aImp.GetRange();
+ rRange.aEnd = aR.aEnd;
+ aImp.WriteToDocument( true, nOutputFactor, pFormatter, bConvertDate );
+ return nErr;
+}
+
+std::unique_ptr<ScEEAbsImport> ScFormatFilterPluginImpl::CreateHTMLImport( ScDocument* pDocP, const OUString& rBaseURL, const ScRange& rRange )
+{
+ return std::make_unique<ScHTMLImport>( pDocP, rBaseURL, rRange, true/*bCalcWidthHeight*/ );
+}
+
+ScHTMLImport::ScHTMLImport( ScDocument* pDocP, const OUString& rBaseURL, const ScRange& rRange, bool bCalcWidthHeight ) :
+ ScEEImport( pDocP, rRange )
+{
+ Size aPageSize;
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ const OUString& aPageStyle = mpDoc->GetPageStyle( rRange.aStart.Tab() );
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(mpDoc->
+ GetStyleSheetPool()->Find( aPageStyle, SfxStyleFamily::Page ));
+ if ( pStyleSheet )
+ {
+ const SfxItemSet& rSet = pStyleSheet->GetItemSet();
+ const SvxLRSpaceItem* pLRItem = &rSet.Get( ATTR_LRSPACE );
+ tools::Long nLeftMargin = pLRItem->GetLeft();
+ tools::Long nRightMargin = pLRItem->GetRight();
+ const SvxULSpaceItem* pULItem = &rSet.Get( ATTR_ULSPACE );
+ tools::Long nTopMargin = pULItem->GetUpper();
+ tools::Long nBottomMargin = pULItem->GetLower();
+ aPageSize = rSet.Get(ATTR_PAGE_SIZE).GetSize();
+ if ( !aPageSize.Width() || !aPageSize.Height() )
+ {
+ OSL_FAIL("PageSize Null ?!?!?");
+ aPageSize = SvxPaperInfo::GetPaperSize( PAPER_A4 );
+ }
+ aPageSize.AdjustWidth( -(nLeftMargin + nRightMargin) );
+ aPageSize.AdjustHeight( -(nTopMargin + nBottomMargin) );
+ aPageSize = pDefaultDev->LogicToPixel( aPageSize, MapMode( MapUnit::MapTwip ) );
+ }
+ else
+ {
+ OSL_FAIL("no StyleSheet?!?");
+ aPageSize = pDefaultDev->LogicToPixel(
+ SvxPaperInfo::GetPaperSize( PAPER_A4 ), MapMode( MapUnit::MapTwip ) );
+ }
+ if( bCalcWidthHeight )
+ mpParser.reset( new ScHTMLLayoutParser( mpEngine.get(), rBaseURL, aPageSize, pDocP ));
+ else
+ mpParser.reset( new ScHTMLQueryParser( mpEngine.get(), pDocP ));
+}
+
+void ScHTMLImport::InsertRangeName( ScDocument& rDoc, const OUString& rName, const ScRange& rRange )
+{
+ ScComplexRefData aRefData;
+ aRefData.InitRange( rRange );
+ aRefData.Ref1.SetFlag3D( true );
+ aRefData.Ref2.SetFlag3D( aRefData.Ref2.Tab() != aRefData.Ref1.Tab() );
+ ScTokenArray aTokArray(rDoc);
+ aTokArray.AddDoubleReference( aRefData );
+ ScRangeData* pRangeData = new ScRangeData( rDoc, rName, aTokArray );
+ rDoc.GetRangeName()->insert( pRangeData );
+}
+
+void ScHTMLImport::WriteToDocument(
+ bool bSizeColsRows, double nOutputFactor, SvNumberFormatter* pFormatter, bool bConvertDate )
+{
+ ScEEImport::WriteToDocument( bSizeColsRows, nOutputFactor, pFormatter, bConvertDate );
+
+ const ScHTMLParser* pParser = static_cast<ScHTMLParser*>(mpParser.get());
+ const ScHTMLTable* pGlobTable = pParser->GetGlobalTable();
+ if( !pGlobTable )
+ return;
+
+ // set cell borders for HTML table cells
+ pGlobTable->ApplyCellBorders( mpDoc, maRange.aStart );
+
+ // correct cell borders for merged cells
+ for ( size_t i = 0, n = pParser->ListSize(); i < n; ++i )
+ {
+ const ScEEParseEntry* pEntry = pParser->ListEntry( i );
+ if( (pEntry->nColOverlap > 1) || (pEntry->nRowOverlap > 1) )
+ {
+ SCTAB nTab = maRange.aStart.Tab();
+ const ScMergeAttr* pItem = mpDoc->GetAttr( pEntry->nCol, pEntry->nRow, nTab, ATTR_MERGE );
+ if( pItem->IsMerged() )
+ {
+ SCCOL nColMerge = pItem->GetColMerge();
+ SCROW nRowMerge = pItem->GetRowMerge();
+
+ const SvxBoxItem* pToItem = mpDoc->GetAttr( pEntry->nCol, pEntry->nRow, nTab, ATTR_BORDER );
+ SvxBoxItem aNewItem( *pToItem );
+ if( nColMerge > 1 )
+ {
+ const SvxBoxItem* pFromItem =
+ mpDoc->GetAttr( pEntry->nCol + nColMerge - 1, pEntry->nRow, nTab, ATTR_BORDER );
+ aNewItem.SetLine( pFromItem->GetLine( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::RIGHT );
+ }
+ if( nRowMerge > 1 )
+ {
+ const SvxBoxItem* pFromItem =
+ mpDoc->GetAttr( pEntry->nCol, pEntry->nRow + nRowMerge - 1, nTab, ATTR_BORDER );
+ aNewItem.SetLine( pFromItem->GetLine( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::BOTTOM );
+ }
+ mpDoc->ApplyAttr( pEntry->nCol, pEntry->nRow, nTab, aNewItem );
+ }
+ }
+ }
+
+ // create ranges for HTML tables
+ // 1 - entire document
+ ScRange aNewRange( maRange.aStart );
+ aNewRange.aEnd.IncCol( static_cast<SCCOL>(pGlobTable->GetDocSize( tdCol )) - 1 );
+ aNewRange.aEnd.IncRow( pGlobTable->GetDocSize( tdRow ) - 1 );
+ InsertRangeName( *mpDoc, ScfTools::GetHTMLDocName(), aNewRange );
+
+ // 2 - all tables
+ InsertRangeName( *mpDoc, ScfTools::GetHTMLTablesName(), ScRange( maRange.aStart ) );
+
+ // 3 - single tables
+ SCCOL nColDiff = maRange.aStart.Col();
+ SCROW nRowDiff = maRange.aStart.Row();
+ SCTAB nTabDiff = maRange.aStart.Tab();
+
+ ScHTMLTable* pTable = nullptr;
+ ScHTMLTableId nTableId = SC_HTML_GLOBAL_TABLE;
+ ScRange aErrorRange( ScAddress::UNINITIALIZED );
+ while( (pTable = pGlobTable->FindNestedTable( ++nTableId )) != nullptr )
+ {
+ pTable->GetDocRange( aNewRange );
+ if (!aNewRange.Move( nColDiff, nRowDiff, nTabDiff, aErrorRange, *mpDoc ))
+ {
+ assert(!"can't move");
+ }
+ // insert table number as name
+ OUStringBuffer aName(ScfTools::GetNameFromHTMLIndex(nTableId));
+ // insert table id as name
+ if (!pTable->GetTableName().isEmpty())
+ aName.append(" - " + pTable->GetTableName());
+ // insert table caption as name
+ if (!pTable->GetTableCaption().isEmpty())
+ aName.append(" - " + pTable->GetTableCaption());
+ const OUString sName(aName.makeStringAndClear());
+ if (!mpDoc->GetRangeName()->findByUpperName(ScGlobal::getCharClass().uppercase(sName)))
+ InsertRangeName(*mpDoc, sName, aNewRange);
+ }
+}
+
+OUString ScFormatFilterPluginImpl::GetHTMLRangeNameList( ScDocument& rDoc, const OUString& rOrigName )
+{
+ return ScHTMLImport::GetHTMLRangeNameList( rDoc, rOrigName );
+}
+
+OUString ScHTMLImport::GetHTMLRangeNameList( const ScDocument& rDoc, std::u16string_view rOrigName )
+{
+ if (rOrigName.empty())
+ return OUString();
+
+ OUString aNewName;
+ ScRangeName* pRangeNames = rDoc.GetRangeName();
+ ScRangeList aRangeList;
+ sal_Int32 nStringIx = 0;
+ do
+ {
+ OUString aToken( o3tl::getToken(rOrigName, 0, ';', nStringIx ) );
+ if( pRangeNames && ScfTools::IsHTMLTablesName( aToken ) )
+ { // build list with all HTML tables
+ sal_uLong nIndex = 1;
+ for(;;)
+ {
+ aToken = ScfTools::GetNameFromHTMLIndex( nIndex++ );
+ const ScRangeData* pRangeData = pRangeNames->findByUpperName(ScGlobal::getCharClass().uppercase(aToken));
+ if (!pRangeData)
+ break;
+ ScRange aRange;
+ if( pRangeData->IsReference( aRange ) && !aRangeList.Contains( aRange ) )
+ {
+ aNewName = ScGlobal::addToken(aNewName, aToken, ';');
+ aRangeList.push_back( aRange );
+ }
+ }
+ }
+ else
+ aNewName = ScGlobal::addToken(aNewName, aToken, ';');
+ }
+ while (nStringIx>0);
+ return aNewName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/html/htmlpars.cxx b/sc/source/filter/html/htmlpars.cxx
new file mode 100644
index 000000000..d6dbb6f8f
--- /dev/null
+++ b/sc/source/filter/html/htmlpars.cxx
@@ -0,0 +1,3168 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+
+#include <comphelper/string.hxx>
+
+#include <scitems.hxx>
+
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <sal/log.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/numformat.hxx>
+#include <svl/intitem.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svtools/parhtml.hxx>
+#include <svtools/htmlkywd.hxx>
+#include <svtools/htmltokn.h>
+
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <rtl/tencinfo.h>
+
+#include <attrib.hxx>
+#include <htmlpars.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <rangelst.hxx>
+
+#include <orcus/css_parser.hpp>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <numeric>
+#include <utility>
+#include <officecfg/Office/Common.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+ScHTMLStyles::ScHTMLStyles() : maEmpty() {}
+
+void ScHTMLStyles::add(const char* pElemName, size_t nElemName, const char* pClassName, size_t nClassName,
+ const OUString& aProp, const OUString& aValue)
+{
+ if (pElemName)
+ {
+ OUString aElem(pElemName, nElemName, RTL_TEXTENCODING_UTF8);
+ aElem = aElem.toAsciiLowerCase();
+ if (pClassName)
+ {
+ // Both element and class names given.
+ ElemsType::iterator itrElem = m_ElemProps.find(aElem);
+ if (itrElem == m_ElemProps.end())
+ {
+ // new element
+ std::pair<ElemsType::iterator, bool> r =
+ m_ElemProps.insert(std::make_pair(aElem, NamePropsType()));
+ if (!r.second)
+ // insertion failed.
+ return;
+ itrElem = r.first;
+ }
+
+ NamePropsType& rClsProps = itrElem->second;
+ OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
+ aClass = aClass.toAsciiLowerCase();
+ insertProp(rClsProps, aClass, aProp, aValue);
+ }
+ else
+ {
+ // Element name only. Add it to the element global.
+ insertProp(m_ElemGlobalProps, aElem, aProp, aValue);
+ }
+ }
+ else
+ {
+ if (pClassName)
+ {
+ // Class name only. Add it to the global.
+ OUString aClass(pClassName, nClassName, RTL_TEXTENCODING_UTF8);
+ aClass = aClass.toAsciiLowerCase();
+ insertProp(m_GlobalProps, aClass, aProp, aValue);
+ }
+ }
+}
+
+const OUString& ScHTMLStyles::getPropertyValue(
+ const OUString& rElem, const OUString& rClass, const OUString& rPropName) const
+{
+ // First, look into the element-class storage.
+ {
+ auto const itr = m_ElemProps.find(rElem);
+ if (itr != m_ElemProps.end())
+ {
+ const NamePropsType& rClasses = itr->second;
+ NamePropsType::const_iterator itr2 = rClasses.find(rClass);
+ if (itr2 != rClasses.end())
+ {
+ const PropsType& rProps = itr2->second;
+ PropsType::const_iterator itr3 = rProps.find(rPropName);
+ if (itr3 != rProps.end())
+ return itr3->second;
+ }
+ }
+ }
+ // Next, look into the class global storage.
+ {
+ auto const itr = m_GlobalProps.find(rClass);
+ if (itr != m_GlobalProps.end())
+ {
+ const PropsType& rProps = itr->second;
+ PropsType::const_iterator itr2 = rProps.find(rPropName);
+ if (itr2 != rProps.end())
+ return itr2->second;
+ }
+ }
+ // As the last resort, look into the element global storage.
+ {
+ auto const itr = m_ElemGlobalProps.find(rClass);
+ if (itr != m_ElemGlobalProps.end())
+ {
+ const PropsType& rProps = itr->second;
+ PropsType::const_iterator itr2 = rProps.find(rPropName);
+ if (itr2 != rProps.end())
+ return itr2->second;
+ }
+ }
+
+ return maEmpty; // nothing found.
+}
+
+void ScHTMLStyles::insertProp(
+ NamePropsType& rStore, const OUString& aName,
+ const OUString& aProp, const OUString& aValue)
+{
+ NamePropsType::iterator itr = rStore.find(aName);
+ if (itr == rStore.end())
+ {
+ // new element
+ std::pair<NamePropsType::iterator, bool> r =
+ rStore.insert(std::make_pair(aName, PropsType()));
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itr = r.first;
+ }
+
+ PropsType& rProps = itr->second;
+ rProps.emplace(aProp, aValue);
+}
+
+// BASE class for HTML parser classes
+
+ScHTMLParser::ScHTMLParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
+ ScEEParser( pEditEngine ),
+ mpDoc( pDoc )
+{
+ maFontHeights[0] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_1::get() * 20;
+ maFontHeights[1] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_2::get() * 20;
+ maFontHeights[2] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_3::get() * 20;
+ maFontHeights[3] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_4::get() * 20;
+ maFontHeights[4] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_5::get() * 20;
+ maFontHeights[5] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_6::get() * 20;
+ maFontHeights[6] = officecfg::Office::Common::Filter::HTML::Import::FontSize::Size_7::get() * 20;
+}
+
+ScHTMLParser::~ScHTMLParser()
+{
+}
+
+ScHTMLLayoutParser::ScHTMLLayoutParser(
+ EditEngine* pEditP, const OUString& rBaseURL, const Size& aPageSizeP,
+ ScDocument* pDocP ) :
+ ScHTMLParser( pEditP, pDocP ),
+ aPageSize( aPageSizeP ),
+ aBaseURL( rBaseURL ),
+ xLockedList( new ScRangeList ),
+ pLocalColOffset( new ScHTMLColOffset ),
+ nFirstTableCell(0),
+ nTableLevel(0),
+ nTable(0),
+ nMaxTable(0),
+ nColCntStart(0),
+ nMaxCol(0),
+ nTableWidth(0),
+ nColOffset(0),
+ nColOffsetStart(0),
+ nOffsetTolerance( SC_HTML_OFFSET_TOLERANCE_SMALL ),
+ bFirstRow( true ),
+ bTabInTabCell( false ),
+ bInCell( false ),
+ bInTitle( false )
+{
+ MakeColNoRef( pLocalColOffset, 0, 0, 0, 0 );
+ MakeColNoRef( &maColOffset, 0, 0, 0, 0 );
+}
+
+ScHTMLLayoutParser::~ScHTMLLayoutParser()
+{
+ while ( !aTableStack.empty() )
+ {
+ ScHTMLTableStackEntry * pS = aTableStack.top().get();
+ if ( pS->pLocalColOffset != pLocalColOffset )
+ delete pS->pLocalColOffset;
+ aTableStack.pop();
+ }
+ delete pLocalColOffset;
+ if ( pTables )
+ {
+ for( const auto& rEntry : *pTables)
+ delete rEntry.second;
+ pTables.reset();
+ }
+}
+
+ErrCode ScHTMLLayoutParser::Read( SvStream& rStream, const OUString& rBaseURL )
+{
+ Link<HtmlImportInfo&,void> aOldLink = pEdit->GetHtmlImportHdl();
+ pEdit->SetHtmlImportHdl( LINK( this, ScHTMLLayoutParser, HTMLImportHdl ) );
+
+ SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
+ bool bLoading = pObjSh && pObjSh->IsLoading();
+
+ SvKeyValueIteratorRef xValues;
+ SvKeyValueIterator* pAttributes = nullptr;
+ if ( bLoading )
+ pAttributes = pObjSh->GetHeaderAttributes();
+ else
+ {
+ // When not loading, set up fake http headers to force the SfxHTMLParser to use UTF8
+ // (used when pasting from clipboard)
+ const char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
+ if( pCharSet )
+ {
+ OUString aContentType = "text/html; charset=" +
+ OUString::createFromAscii( pCharSet );
+
+ xValues = new SvKeyValueIterator;
+ xValues->Append( SvKeyValue( OOO_STRING_SVTOOLS_HTML_META_content_type, aContentType ) );
+ pAttributes = xValues.get();
+ }
+ }
+
+ ErrCode nErr = pEdit->Read( rStream, rBaseURL, EETextFormat::Html, pAttributes );
+
+ pEdit->SetHtmlImportHdl( aOldLink );
+ // Create column width
+ Adjust();
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ sal_uInt16 nCount = maColOffset.size();
+ sal_uLong nOff = maColOffset[0];
+ Size aSize;
+ for ( sal_uInt16 j = 1; j < nCount; j++ )
+ {
+ aSize.setWidth( maColOffset[j] - nOff );
+ aSize = pDefaultDev->PixelToLogic( aSize, MapMode( MapUnit::MapTwip ) );
+ maColWidths[ j-1 ] = aSize.Width();
+ nOff = maColOffset[j];
+ }
+ return nErr;
+}
+
+const ScHTMLTable* ScHTMLLayoutParser::GetGlobalTable() const
+{
+ return nullptr;
+}
+
+void ScHTMLLayoutParser::NewActEntry( const ScEEParseEntry* pE )
+{
+ ScEEParser::NewActEntry( pE );
+ if ( pE )
+ {
+ if ( !pE->aSel.HasRange() )
+ { // Completely empty, following text ends up in the same paragraph!
+ mxActEntry->aSel.nStartPara = pE->aSel.nEndPara;
+ mxActEntry->aSel.nStartPos = pE->aSel.nEndPos;
+ }
+ }
+ mxActEntry->aSel.nEndPara = mxActEntry->aSel.nStartPara;
+ mxActEntry->aSel.nEndPos = mxActEntry->aSel.nStartPos;
+}
+
+void ScHTMLLayoutParser::EntryEnd( ScEEParseEntry* pE, const ESelection& rSel )
+{
+ if ( rSel.nEndPara >= pE->aSel.nStartPara )
+ {
+ pE->aSel.nEndPara = rSel.nEndPara;
+ pE->aSel.nEndPos = rSel.nEndPos;
+ }
+ else if ( rSel.nStartPara == pE->aSel.nStartPara - 1 && !pE->aSel.HasRange() )
+ { // Did not attach a paragraph, but empty, do nothing
+ }
+ else
+ {
+ OSL_FAIL( "EntryEnd: EditEngine ESelection End < Start" );
+ }
+}
+
+void ScHTMLLayoutParser::NextRow( const HtmlImportInfo* pInfo )
+{
+ if ( bInCell )
+ CloseEntry( pInfo );
+ if ( nRowMax < ++nRowCnt )
+ nRowMax = nRowCnt;
+ nColCnt = nColCntStart;
+ nColOffset = nColOffsetStart;
+ bFirstRow = false;
+}
+
+bool ScHTMLLayoutParser::SeekOffset( const ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
+ SCCOL* pCol, sal_uInt16 nOffsetTol )
+{
+ OSL_ENSURE( pOffset, "ScHTMLLayoutParser::SeekOffset - illegal call" );
+ ScHTMLColOffset::const_iterator it = pOffset->find( nOffset );
+ bool bFound = it != pOffset->end();
+ sal_uInt16 nPos = it - pOffset->begin();
+ *pCol = static_cast<SCCOL>(nPos);
+ if ( bFound )
+ return true;
+ sal_uInt16 nCount = pOffset->size();
+ if ( !nCount )
+ return false;
+ // nPos is the position of insertion, that's where the next higher one is (or isn't)
+ if ( nPos < nCount && (((*pOffset)[nPos] - nOffsetTol) <= nOffset) )
+ return true;
+ // Not smaller than everything else? Then compare with the next lower one
+ else if ( nPos && (((*pOffset)[nPos-1] + nOffsetTol) >= nOffset) )
+ {
+ (*pCol)--;
+ return true;
+ }
+ return false;
+}
+
+void ScHTMLLayoutParser::MakeCol( ScHTMLColOffset* pOffset, sal_uInt16& nOffset,
+ sal_uInt16& nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
+{
+ OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeCol - illegal call" );
+ SCCOL nPos;
+ if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
+ nOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
+ else
+ pOffset->insert( nOffset );
+ if ( nWidth )
+ {
+ if ( SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
+ nWidth = static_cast<sal_uInt16>((*pOffset)[nPos]) - nOffset;
+ else
+ pOffset->insert( nOffset + nWidth );
+ }
+}
+
+void ScHTMLLayoutParser::MakeColNoRef( ScHTMLColOffset* pOffset, sal_uInt16 nOffset,
+ sal_uInt16 nWidth, sal_uInt16 nOffsetTol, sal_uInt16 nWidthTol )
+{
+ OSL_ENSURE( pOffset, "ScHTMLLayoutParser::MakeColNoRef - illegal call" );
+ SCCOL nPos;
+ if ( SeekOffset( pOffset, nOffset, &nPos, nOffsetTol ) )
+ nOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
+ else
+ pOffset->insert( nOffset );
+ if ( nWidth )
+ {
+ if ( !SeekOffset( pOffset, nOffset + nWidth, &nPos, nWidthTol ) )
+ pOffset->insert( nOffset + nWidth );
+ }
+}
+
+void ScHTMLLayoutParser::ModifyOffset( ScHTMLColOffset* pOffset, sal_uInt16& nOldOffset,
+ sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol )
+{
+ OSL_ENSURE( pOffset, "ScHTMLLayoutParser::ModifyOffset - illegal call" );
+ SCCOL nPos;
+ if ( !SeekOffset( pOffset, nOldOffset, &nPos, nOffsetTol ) )
+ {
+ if ( SeekOffset( pOffset, nNewOffset, &nPos, nOffsetTol ) )
+ nNewOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
+ else
+ pOffset->insert( nNewOffset );
+ return ;
+ }
+ nOldOffset = static_cast<sal_uInt16>((*pOffset)[nPos]);
+ SCCOL nPos2;
+ if ( SeekOffset( pOffset, nNewOffset, &nPos2, nOffsetTol ) )
+ {
+ nNewOffset = static_cast<sal_uInt16>((*pOffset)[nPos2]);
+ return ;
+ }
+ tools::Long nDiff = nNewOffset - nOldOffset;
+ if ( nDiff < 0 )
+ {
+ do
+ {
+ const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
+ } while ( nPos-- );
+ }
+ else
+ {
+ do
+ {
+ const_cast<sal_uLong&>((*pOffset)[nPos]) += nDiff;
+ } while ( ++nPos < static_cast<sal_uInt16>(pOffset->size()) );
+ }
+}
+
+void ScHTMLLayoutParser::SkipLocked( ScEEParseEntry* pE, bool bJoin )
+{
+ if ( !mpDoc->ValidCol(pE->nCol) )
+ return;
+
+// Or else this would create a wrong value at ScAddress (chance for an infinite loop)!
+ bool bBadCol = false;
+ bool bAgain;
+ ScRange aRange( pE->nCol, pE->nRow, 0,
+ pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 );
+ do
+ {
+ bAgain = false;
+ for ( size_t i = 0, nRanges = xLockedList->size(); i < nRanges; ++i )
+ {
+ ScRange & rR = (*xLockedList)[i];
+ if ( rR.Intersects( aRange ) )
+ {
+ pE->nCol = rR.aEnd.Col() + 1;
+ SCCOL nTmp = pE->nCol + pE->nColOverlap - 1;
+ if ( pE->nCol > mpDoc->MaxCol() || nTmp > mpDoc->MaxCol() )
+ bBadCol = true;
+ else
+ {
+ bAgain = true;
+ aRange.aStart.SetCol( pE->nCol );
+ aRange.aEnd.SetCol( nTmp );
+ }
+ break;
+ }
+ }
+ } while ( bAgain );
+ if ( bJoin && !bBadCol )
+ xLockedList->Join( aRange );
+}
+
+void ScHTMLLayoutParser::Adjust()
+{
+ xLockedList->RemoveAll();
+
+ std::stack< std::unique_ptr<ScHTMLAdjustStackEntry> > aStack;
+ sal_uInt16 nTab = 0;
+ SCCOL nLastCol = SCCOL_MAX;
+ SCROW nNextRow = 0;
+ SCROW nCurRow = 0;
+ sal_uInt16 nPageWidth = static_cast<sal_uInt16>(aPageSize.Width());
+ InnerMap* pTab = nullptr;
+ for (auto& pE : maList)
+ {
+ if ( pE->nTab < nTab )
+ { // Table finished
+ if ( !aStack.empty() )
+ {
+ std::unique_ptr<ScHTMLAdjustStackEntry> pS = std::move(aStack.top());
+ aStack.pop();
+
+ nLastCol = pS->nLastCol;
+ nNextRow = pS->nNextRow;
+ nCurRow = pS->nCurRow;
+ }
+ nTab = pE->nTab;
+ if (pTables)
+ {
+ OuterMap::const_iterator it = pTables->find( nTab );
+ if ( it != pTables->end() )
+ pTab = it->second;
+ }
+
+ }
+ SCROW nRow = pE->nRow;
+ if ( pE->nCol <= nLastCol )
+ { // Next row
+ if ( pE->nRow < nNextRow )
+ pE->nRow = nCurRow = nNextRow;
+ else
+ nCurRow = nNextRow = pE->nRow;
+ SCROW nR = 0;
+ if ( pTab )
+ {
+ InnerMap::const_iterator it = pTab->find( nCurRow );
+ if ( it != pTab->end() )
+ nR = it->second;
+ }
+ if ( nR )
+ nNextRow += nR;
+ else
+ nNextRow++;
+ }
+ else
+ pE->nRow = nCurRow;
+ nLastCol = pE->nCol; // Read column
+ if ( pE->nTab > nTab )
+ { // New table
+ aStack.push( std::make_unique<ScHTMLAdjustStackEntry>(
+ nLastCol, nNextRow, nCurRow ) );
+ nTab = pE->nTab;
+ if ( pTables )
+ {
+ OuterMap::const_iterator it = pTables->find( nTab );
+ if ( it != pTables->end() )
+ pTab = it->second;
+ }
+ // New line spacing
+ SCROW nR = 0;
+ if ( pTab )
+ {
+ InnerMap::const_iterator it = pTab->find( nCurRow );
+ if ( it != pTab->end() )
+ nR = it->second;
+ }
+ if ( nR )
+ nNextRow = nCurRow + nR;
+ else
+ nNextRow = nCurRow + 1;
+ }
+ if ( nTab == 0 )
+ pE->nWidth = nPageWidth;
+ else
+ { // Real table, no paragraphs on the field
+ if ( pTab )
+ {
+ SCROW nRowSpan = pE->nRowOverlap;
+ for ( SCROW j=0; j < nRowSpan; j++ )
+ { // RowSpan resulting from merged rows
+ SCROW nRows = 0;
+ InnerMap::const_iterator it = pTab->find( nRow+j );
+ if ( it != pTab->end() )
+ nRows = it->second;
+ if ( nRows > 1 )
+ {
+ pE->nRowOverlap += nRows - 1;
+ if ( j == 0 )
+ { // Merged rows move the next row
+ SCROW nTmp = nCurRow + nRows;
+ if ( nNextRow < nTmp )
+ nNextRow = nTmp;
+ }
+ }
+ }
+ }
+ }
+ // Real column
+ (void)SeekOffset( &maColOffset, pE->nOffset, &pE->nCol, nOffsetTolerance );
+ SCCOL nColBeforeSkip = pE->nCol;
+ SkipLocked(pE.get(), false);
+ if ( pE->nCol != nColBeforeSkip )
+ {
+ SCCOL nCount = static_cast<SCCOL>(maColOffset.size());
+ if ( nCount <= pE->nCol )
+ {
+ pE->nOffset = static_cast<sal_uInt16>(maColOffset[nCount-1]);
+ MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
+ }
+ else
+ {
+ pE->nOffset = static_cast<sal_uInt16>(maColOffset[pE->nCol]);
+ }
+ }
+ SCCOL nPos;
+ if ( pE->nWidth && SeekOffset( &maColOffset, pE->nOffset + pE->nWidth, &nPos, nOffsetTolerance ) )
+ pE->nColOverlap = (nPos > pE->nCol ? nPos - pE->nCol : 1);
+ else
+ {
+ //FIXME: This may not be correct, but works anyway ...
+ pE->nColOverlap = 1;
+ }
+ xLockedList->Join( ScRange( pE->nCol, pE->nRow, 0,
+ pE->nCol + pE->nColOverlap - 1, pE->nRow + pE->nRowOverlap - 1, 0 ) );
+ // Take over MaxDimensions
+ SCCOL nColTmp = pE->nCol + pE->nColOverlap;
+ if ( nColMax < nColTmp )
+ nColMax = nColTmp;
+ SCROW nRowTmp = pE->nRow + pE->nRowOverlap;
+ if ( nRowMax < nRowTmp )
+ nRowMax = nRowTmp;
+ }
+}
+
+sal_uInt16 ScHTMLLayoutParser::GetWidth( const ScEEParseEntry* pE )
+{
+ if ( pE->nWidth )
+ return pE->nWidth;
+ sal_Int32 nTmp = std::min( static_cast<sal_Int32>( pE->nCol -
+ nColCntStart + pE->nColOverlap),
+ static_cast<sal_Int32>( pLocalColOffset->size() - 1));
+ SCCOL nPos = (nTmp < 0 ? 0 : static_cast<SCCOL>(nTmp));
+ sal_uInt16 nOff2 = static_cast<sal_uInt16>((*pLocalColOffset)[nPos]);
+ if ( pE->nOffset < nOff2 )
+ return nOff2 - pE->nOffset;
+ return 0;
+}
+
+void ScHTMLLayoutParser::SetWidths()
+{
+ SCCOL nCol;
+ if ( !nTableWidth )
+ nTableWidth = static_cast<sal_uInt16>(aPageSize.Width());
+ SCCOL nColsPerRow = nMaxCol - nColCntStart;
+ if ( nColsPerRow <= 0 )
+ nColsPerRow = 1;
+ if ( pLocalColOffset->size() <= 2 )
+ { // Only PageSize, there was no width setting
+ sal_uInt16 nWidth = nTableWidth / static_cast<sal_uInt16>(nColsPerRow);
+ sal_uInt16 nOff = nColOffsetStart;
+ pLocalColOffset->clear();
+ for ( nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth )
+ {
+ MakeColNoRef( pLocalColOffset, nOff, 0, 0, 0 );
+ }
+ nTableWidth = static_cast<sal_uInt16>(pLocalColOffset->back() - pLocalColOffset->front());
+ for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
+ {
+ auto& pE = maList[ i ];
+ if ( pE->nTab == nTable )
+ {
+ pE->nOffset = static_cast<sal_uInt16>((*pLocalColOffset)[pE->nCol - nColCntStart]);
+ pE->nWidth = 0; // to be recalculated later
+ }
+ }
+ }
+ else
+ { // Some without width
+ // Why actually no pE?
+ if ( nFirstTableCell < maList.size() )
+ {
+ std::unique_ptr<sal_uInt16[]> pOffsets(new sal_uInt16[ nColsPerRow+1 ]);
+ memset( pOffsets.get(), 0, (nColsPerRow+1) * sizeof(sal_uInt16) );
+ std::unique_ptr<sal_uInt16[]> pWidths(new sal_uInt16[ nColsPerRow ]);
+ memset( pWidths.get(), 0, nColsPerRow * sizeof(sal_uInt16) );
+ pOffsets[0] = nColOffsetStart;
+ for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
+ {
+ auto& pE = maList[ i ];
+ if ( pE->nTab == nTable && pE->nWidth )
+ {
+ nCol = pE->nCol - nColCntStart;
+ if ( nCol < nColsPerRow )
+ {
+ if ( pE->nColOverlap == 1 )
+ {
+ if ( pWidths[nCol] < pE->nWidth )
+ pWidths[nCol] = pE->nWidth;
+ }
+ else
+ { // try to find a single undefined width
+ sal_uInt16 nTotal = 0;
+ bool bFound = false;
+ SCCOL nHere = 0;
+ SCCOL nStop = std::min( static_cast<SCCOL>(nCol + pE->nColOverlap), nColsPerRow );
+ for ( ; nCol < nStop; nCol++ )
+ {
+ if ( pWidths[nCol] )
+ nTotal = nTotal + pWidths[nCol];
+ else
+ {
+ if ( bFound )
+ {
+ bFound = false;
+ break; // for
+ }
+ bFound = true;
+ nHere = nCol;
+ }
+ }
+ if ( bFound && pE->nWidth > nTotal )
+ pWidths[nHere] = pE->nWidth - nTotal;
+ }
+ }
+ }
+ }
+ sal_uInt16 nWidths = 0;
+ sal_uInt16 nUnknown = 0;
+ for ( nCol = 0; nCol < nColsPerRow; nCol++ )
+ {
+ if ( pWidths[nCol] )
+ nWidths = nWidths + pWidths[nCol];
+ else
+ nUnknown++;
+ }
+ if ( nUnknown )
+ {
+ sal_uInt16 nW = ((nWidths < nTableWidth) ?
+ ((nTableWidth - nWidths) / nUnknown) :
+ (nTableWidth / nUnknown));
+ for ( nCol = 0; nCol < nColsPerRow; nCol++ )
+ {
+ if ( !pWidths[nCol] )
+ pWidths[nCol] = nW;
+ }
+ }
+ for ( nCol = 1; nCol <= nColsPerRow; nCol++ )
+ {
+ pOffsets[nCol] = pOffsets[nCol-1] + pWidths[nCol-1];
+ }
+ pLocalColOffset->clear();
+ for ( nCol = 0; nCol <= nColsPerRow; nCol++ )
+ {
+ MakeColNoRef( pLocalColOffset, pOffsets[nCol], 0, 0, 0 );
+ }
+ nTableWidth = pOffsets[nColsPerRow] - pOffsets[0];
+
+ for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
+ {
+ auto& pE = maList[ i ];
+ if ( pE->nTab == nTable )
+ {
+ nCol = pE->nCol - nColCntStart;
+ OSL_ENSURE( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" );
+ if ( nCol < nColsPerRow )
+ {
+ pE->nOffset = pOffsets[nCol];
+ nCol = nCol + pE->nColOverlap;
+ if ( nCol > nColsPerRow )
+ nCol = nColsPerRow;
+ pE->nWidth = pOffsets[nCol] - pE->nOffset;
+ }
+ }
+ }
+ }
+ }
+ if ( !pLocalColOffset->empty() )
+ {
+ sal_uInt16 nMax = static_cast<sal_uInt16>(pLocalColOffset->back());
+ if ( aPageSize.Width() < nMax )
+ aPageSize.setWidth( nMax );
+ if (nTableLevel == 0)
+ {
+ // Local table is very outer table, create missing offsets.
+ for (auto it = pLocalColOffset->begin(); it != pLocalColOffset->end(); ++it)
+ {
+ // Only exact offsets, do not use MakeColNoRef().
+ if (maColOffset.find(*it) == maColOffset.end())
+ maColOffset.insert(*it);
+ }
+ }
+ }
+ for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i )
+ {
+ auto& pE = maList[ i ];
+ if ( pE->nTab == nTable )
+ {
+ if ( !pE->nWidth )
+ {
+ pE->nWidth = GetWidth(pE.get());
+ OSL_ENSURE( pE->nWidth, "SetWidths: pE->nWidth == 0" );
+ }
+ MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance );
+ }
+ }
+}
+
+void ScHTMLLayoutParser::Colonize( ScEEParseEntry* pE )
+{
+ if ( pE->nCol == SCCOL_MAX )
+ pE->nCol = nColCnt;
+ if ( pE->nRow == SCROW_MAX )
+ pE->nRow = nRowCnt;
+ SCCOL nCol = pE->nCol;
+ SkipLocked( pE ); // Change of columns to the right
+
+ if ( nCol < pE->nCol )
+ { // Replaced
+ nCol = pE->nCol - nColCntStart;
+ SCCOL nCount = static_cast<SCCOL>(pLocalColOffset->size());
+ if ( nCol < nCount )
+ nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCol]);
+ else
+ nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCount - 1]);
+ }
+ pE->nOffset = nColOffset;
+ sal_uInt16 nWidth = GetWidth( pE );
+ MakeCol( pLocalColOffset, pE->nOffset, nWidth, nOffsetTolerance, nOffsetTolerance );
+ if ( pE->nWidth )
+ pE->nWidth = nWidth;
+ nColOffset = pE->nOffset + nWidth;
+ if ( nTableWidth < nColOffset - nColOffsetStart )
+ nTableWidth = nColOffset - nColOffsetStart;
+}
+
+void ScHTMLLayoutParser::CloseEntry( const HtmlImportInfo* pInfo )
+{
+ bInCell = false;
+ if ( bTabInTabCell )
+ { // From the stack in TableOff
+ bTabInTabCell = false;
+ NewActEntry(maList.back().get()); // New free flying mxActEntry
+ return ;
+ }
+ if (mxActEntry->nTab == 0)
+ mxActEntry->nWidth = static_cast<sal_uInt16>(aPageSize.Width());
+ Colonize(mxActEntry.get());
+ nColCnt = mxActEntry->nCol + mxActEntry->nColOverlap;
+ if ( nMaxCol < nColCnt )
+ nMaxCol = nColCnt; // TableStack MaxCol
+ if ( nColMax < nColCnt )
+ nColMax = nColCnt; // Global MaxCol for ScEEParser GetDimensions!
+ EntryEnd(mxActEntry.get(), pInfo->aSelection);
+ ESelection& rSel = mxActEntry->aSel;
+ while ( rSel.nStartPara < rSel.nEndPara
+ && pEdit->GetTextLen( rSel.nStartPara ) == 0 )
+ { // Strip preceding empty paragraphs
+ rSel.nStartPara++;
+ }
+ while ( rSel.nEndPos == 0 && rSel.nEndPara > rSel.nStartPara )
+ { // Strip successive empty paragraphs
+ rSel.nEndPara--;
+ rSel.nEndPos = pEdit->GetTextLen( rSel.nEndPara );
+ }
+ if ( rSel.nStartPara > rSel.nEndPara )
+ { // Gives GPF in CreateTextObject
+ OSL_FAIL( "CloseEntry: EditEngine ESelection Start > End" );
+ rSel.nEndPara = rSel.nStartPara;
+ }
+ if ( rSel.HasRange() )
+ mxActEntry->aItemSet.Put( ScLineBreakCell(true) );
+ maList.push_back(mxActEntry);
+ NewActEntry(mxActEntry.get()); // New free flying mxActEntry
+}
+
+IMPL_LINK( ScHTMLLayoutParser, HTMLImportHdl, HtmlImportInfo&, rInfo, void )
+{
+ switch ( rInfo.eState )
+ {
+ case HtmlImportState::NextToken:
+ ProcToken( &rInfo );
+ break;
+ case HtmlImportState::Start:
+ break;
+ case HtmlImportState::End:
+ if ( rInfo.aSelection.nEndPos )
+ {
+ // If text remains: create paragraph, without calling CloseEntry().
+ if( bInCell ) // ...but only in opened table cells.
+ {
+ bInCell = false;
+ NextRow( &rInfo );
+ bInCell = true;
+ }
+ CloseEntry( &rInfo );
+ }
+ while ( nTableLevel > 0 )
+ TableOff( &rInfo ); // close tables, if </TABLE> missing
+ break;
+ case HtmlImportState::SetAttr:
+ break;
+ case HtmlImportState::InsertText:
+ break;
+ case HtmlImportState::InsertPara:
+ if ( nTableLevel < 1 )
+ {
+ CloseEntry( &rInfo );
+ NextRow( &rInfo );
+ }
+ break;
+ case HtmlImportState::InsertField:
+ break;
+ default:
+ OSL_FAIL("HTMLImportHdl: unknown ImportInfo.eState");
+ }
+}
+
+void ScHTMLLayoutParser::TableDataOn( HtmlImportInfo* pInfo )
+{
+ if ( bInCell )
+ CloseEntry( pInfo );
+ if ( !nTableLevel )
+ {
+ OSL_FAIL( "dumbo doc! <TH> or <TD> without previous <TABLE>" );
+ TableOn( pInfo );
+ }
+ bInCell = true;
+ bool bHorJustifyCenterTH = (pInfo->nToken == HtmlTokenId::TABLEHEADER_ON);
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::COLSPAN:
+ {
+ mxActEntry->nColOverlap = static_cast<SCCOL>(rOption.GetString().toInt32());
+ }
+ break;
+ case HtmlOptionId::ROWSPAN:
+ {
+ mxActEntry->nRowOverlap = static_cast<SCROW>(rOption.GetString().toInt32());
+ }
+ break;
+ case HtmlOptionId::ALIGN:
+ {
+ bHorJustifyCenterTH = false;
+ SvxCellHorJustify eVal;
+ const OUString& rOptVal = rOption.GetString();
+ if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
+ eVal = SvxCellHorJustify::Right;
+ else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
+ eVal = SvxCellHorJustify::Center;
+ else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
+ eVal = SvxCellHorJustify::Left;
+ else
+ eVal = SvxCellHorJustify::Standard;
+ if ( eVal != SvxCellHorJustify::Standard )
+ mxActEntry->aItemSet.Put(SvxHorJustifyItem(eVal, ATTR_HOR_JUSTIFY));
+ }
+ break;
+ case HtmlOptionId::VALIGN:
+ {
+ SvxCellVerJustify eVal;
+ const OUString& rOptVal = rOption.GetString();
+ if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
+ eVal = SvxCellVerJustify::Top;
+ else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
+ eVal = SvxCellVerJustify::Center;
+ else if ( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
+ eVal = SvxCellVerJustify::Bottom;
+ else
+ eVal = SvxCellVerJustify::Standard;
+ mxActEntry->aItemSet.Put(SvxVerJustifyItem(eVal, ATTR_VER_JUSTIFY));
+ }
+ break;
+ case HtmlOptionId::WIDTH:
+ {
+ mxActEntry->nWidth = GetWidthPixel(rOption);
+ }
+ break;
+ case HtmlOptionId::BGCOLOR:
+ {
+ Color aColor;
+ rOption.GetColor( aColor );
+ mxActEntry->aItemSet.Put(SvxBrushItem(aColor, ATTR_BACKGROUND));
+ }
+ break;
+ case HtmlOptionId::SDVAL:
+ {
+ mxActEntry->pValStr = rOption.GetString();
+ }
+ break;
+ case HtmlOptionId::SDNUM:
+ {
+ mxActEntry->pNumStr = rOption.GetString();
+ }
+ break;
+ default: break;
+ }
+ }
+
+ mxActEntry->nCol = nColCnt;
+ mxActEntry->nRow = nRowCnt;
+ mxActEntry->nTab = nTable;
+
+ if ( bHorJustifyCenterTH )
+ mxActEntry->aItemSet.Put(
+ SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY) );
+}
+
+void ScHTMLLayoutParser::TableRowOn( const HtmlImportInfo* pInfo )
+{
+ if ( nColCnt > nColCntStart )
+ NextRow( pInfo ); // The optional TableRowOff wasn't there
+ nColOffset = nColOffsetStart;
+}
+
+void ScHTMLLayoutParser::TableRowOff( const HtmlImportInfo* pInfo )
+{
+ NextRow( pInfo );
+}
+
+void ScHTMLLayoutParser::TableDataOff( const HtmlImportInfo* pInfo )
+{
+ if ( bInCell )
+ CloseEntry( pInfo ); // Only if it really was one
+}
+
+void ScHTMLLayoutParser::TableOn( HtmlImportInfo* pInfo )
+{
+ if ( ++nTableLevel > 1 )
+ { // Table in Table
+ sal_uInt16 nTmpColOffset = nColOffset; // Will be changed in Colonize()
+ Colonize(mxActEntry.get());
+ aTableStack.push( std::make_unique<ScHTMLTableStackEntry>(
+ mxActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
+ nRowCnt, nColCntStart, nMaxCol, nTable,
+ nTableWidth, nColOffset, nColOffsetStart,
+ bFirstRow ) );
+ sal_uInt16 nLastWidth = nTableWidth;
+ nTableWidth = GetWidth(mxActEntry.get());
+ if ( nTableWidth == nLastWidth && nMaxCol - nColCntStart > 1 )
+ { // There must be more than one, so this one cannot be enough
+ nTableWidth = nLastWidth / static_cast<sal_uInt16>((nMaxCol - nColCntStart));
+ }
+ nLastWidth = nTableWidth;
+ if ( pInfo->nToken == HtmlTokenId::TABLE_ON )
+ { // It can still be TD or TH, if we didn't have a TABLE earlier
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::WIDTH:
+ { // Percent: of document width or outer cell
+ nTableWidth = GetWidthPixel( rOption );
+ }
+ break;
+ case HtmlOptionId::BORDER:
+ // Border is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
+ break;
+ default: break;
+ }
+ }
+ }
+ bInCell = false;
+ if ( bTabInTabCell && (nTableWidth >= nLastWidth) )
+ { // Multiple tables in one cell, underneath each other
+ bTabInTabCell = false;
+ NextRow( pInfo );
+ }
+ else
+ { // It start's in this cell or next to each other
+ bTabInTabCell = false;
+ nColCntStart = nColCnt;
+ nColOffset = nTmpColOffset;
+ nColOffsetStart = nColOffset;
+ }
+
+ NewActEntry(!maList.empty() ? maList.back().get() : nullptr); // New free flying mxActEntry
+ xLockedList = new ScRangeList;
+ }
+ else
+ { // Simple table at the document level
+ EntryEnd(mxActEntry.get(), pInfo->aSelection);
+ if (mxActEntry->aSel.HasRange())
+ { // Flying text left
+ CloseEntry( pInfo );
+ NextRow( pInfo );
+ }
+ aTableStack.push( std::make_unique<ScHTMLTableStackEntry>(
+ mxActEntry, xLockedList, pLocalColOffset, nFirstTableCell,
+ nRowCnt, nColCntStart, nMaxCol, nTable,
+ nTableWidth, nColOffset, nColOffsetStart,
+ bFirstRow ) );
+ // As soon as we have multiple tables we need to be tolerant with the offsets.
+ if (nMaxTable > 0)
+ nOffsetTolerance = SC_HTML_OFFSET_TOLERANCE_LARGE;
+ nTableWidth = 0;
+ if ( pInfo->nToken == HtmlTokenId::TABLE_ON )
+ {
+ // It can still be TD or TH, if we didn't have a TABLE earlier
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::WIDTH:
+ { // Percent: of document width or outer cell
+ nTableWidth = GetWidthPixel( rOption );
+ }
+ break;
+ case HtmlOptionId::BORDER:
+ //BorderOn is: ((pOption->GetString().Len() == 0) || (pOption->GetNumber() != 0));
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+ nTable = ++nMaxTable;
+ bFirstRow = true;
+ nFirstTableCell = maList.size();
+
+ pLocalColOffset = new ScHTMLColOffset;
+ MakeColNoRef( pLocalColOffset, nColOffsetStart, 0, 0, 0 );
+}
+
+void ScHTMLLayoutParser::TableOff( const HtmlImportInfo* pInfo )
+{
+ if ( bInCell )
+ CloseEntry( pInfo );
+ if ( nColCnt > nColCntStart )
+ TableRowOff( pInfo ); // The optional TableRowOff wasn't
+ if ( !nTableLevel )
+ {
+ OSL_FAIL( "dumbo doc! </TABLE> without opening <TABLE>" );
+ return ;
+ }
+ if ( --nTableLevel > 0 )
+ { // Table in Table done
+ if ( !aTableStack.empty() )
+ {
+ std::unique_ptr<ScHTMLTableStackEntry> pS = std::move(aTableStack.top());
+ aTableStack.pop();
+
+ auto& pE = pS->xCellEntry;
+ SCROW nRows = nRowCnt - pS->nRowCnt;
+ if ( nRows > 1 )
+ { // Insert size of table at this position
+ SCROW nRow = pS->nRowCnt;
+ sal_uInt16 nTab = pS->nTable;
+ if ( !pTables )
+ pTables.reset( new OuterMap );
+ // Height of outer table
+ OuterMap::const_iterator it = pTables->find( nTab );
+ InnerMap* pTab1;
+ if ( it == pTables->end() )
+ {
+ pTab1 = new InnerMap;
+ (*pTables)[ nTab ] = pTab1;
+ }
+ else
+ pTab1 = it->second;
+ SCROW nRowSpan = pE->nRowOverlap;
+ SCROW nRowKGV;
+ SCROW nRowsPerRow1; // Outer table
+ SCROW nRowsPerRow2; // Inner table
+ if ( nRowSpan > 1 )
+ { // LCM to which we can map the inner and outer rows
+ nRowKGV = std::lcm( nRowSpan, nRows );
+ nRowsPerRow1 = nRowKGV / nRowSpan;
+ nRowsPerRow2 = nRowKGV / nRows;
+ }
+ else
+ {
+ nRowKGV = nRowsPerRow1 = nRows;
+ nRowsPerRow2 = 1;
+ }
+ InnerMap* pTab2 = nullptr;
+ if ( nRowsPerRow2 > 1 )
+ { // Height of the inner table
+ pTab2 = new InnerMap;
+ (*pTables)[ nTable ] = pTab2;
+ }
+ // Abuse void* Data entry of the Table class for height mapping
+ if ( nRowKGV > 1 )
+ {
+ if ( nRowsPerRow1 > 1 )
+ { // Outer
+ for ( SCROW j=0; j < nRowSpan; j++ )
+ {
+ sal_uLong nRowKey = nRow + j;
+ SCROW nR = (*pTab1)[ nRowKey ];
+ if ( !nR )
+ (*pTab1)[ nRowKey ] = nRowsPerRow1;
+ else if ( nRowsPerRow1 > nR )
+ (*pTab1)[ nRowKey ] = nRowsPerRow1;
+ //TODO: How can we improve on this?
+ else if ( nRowsPerRow1 < nR && nRowSpan == 1
+ && nTable == nMaxTable )
+ { // Still some space left, merge in a better way (if possible)
+ SCROW nAdd = nRowsPerRow1 - (nR % nRowsPerRow1);
+ nR += nAdd;
+ if ( (nR % nRows) == 0 )
+ { // Only if representable
+ SCROW nR2 = (*pTab1)[ nRowKey+1 ];
+ if ( nR2 > nAdd )
+ { // Only if we really have enough space
+ (*pTab1)[ nRowKey ] = nR;
+ (*pTab1)[ nRowKey+1 ] = nR2 - nAdd;
+ nRowsPerRow2 = nR / nRows;
+ }
+ }
+ }
+ }
+ }
+ if ( nRowsPerRow2 > 1 )
+ { // Inner
+ if ( !pTab2 )
+ { // nRowsPerRow2 could be've been incremented
+ pTab2 = new InnerMap;
+ (*pTables)[ nTable ] = pTab2;
+ }
+ for ( SCROW j=0; j < nRows; j++ )
+ {
+ sal_uLong nRowKey = nRow + j;
+ (*pTab2)[ nRowKey ] = nRowsPerRow2;
+ }
+ }
+ }
+ }
+
+ SetWidths();
+
+ if ( !pE->nWidth )
+ pE->nWidth = nTableWidth;
+ else if ( pE->nWidth < nTableWidth )
+ {
+ sal_uInt16 nOldOffset = pE->nOffset + pE->nWidth;
+ sal_uInt16 nNewOffset = pE->nOffset + nTableWidth;
+ ModifyOffset( pS->pLocalColOffset, nOldOffset, nNewOffset, nOffsetTolerance );
+ sal_uInt16 nTmp = nNewOffset - pE->nOffset - pE->nWidth;
+ pE->nWidth = nNewOffset - pE->nOffset;
+ pS->nTableWidth = pS->nTableWidth + nTmp;
+ if ( pS->nColOffset >= nOldOffset )
+ pS->nColOffset = pS->nColOffset + nTmp;
+ }
+
+ nColCnt = pE->nCol + pE->nColOverlap;
+ nRowCnt = pS->nRowCnt;
+ nColCntStart = pS->nColCntStart;
+ nMaxCol = pS->nMaxCol;
+ nTable = pS->nTable;
+ nTableWidth = pS->nTableWidth;
+ nFirstTableCell = pS->nFirstTableCell;
+ nColOffset = pS->nColOffset;
+ nColOffsetStart = pS->nColOffsetStart;
+ bFirstRow = pS->bFirstRow;
+ xLockedList = pS->xLockedList;
+ pLocalColOffset = pS->pLocalColOffset;
+ // mxActEntry is kept around if a table is started in the same row
+ // (anything's possible in HTML); will be deleted by CloseEntry
+ mxActEntry = pE;
+ }
+ bTabInTabCell = true;
+ bInCell = true;
+ }
+ else
+ { // Simple table finished
+ SetWidths();
+ nMaxCol = 0;
+ nTable = 0;
+ if ( !aTableStack.empty() )
+ {
+ ScHTMLTableStackEntry* pS = aTableStack.top().get();
+ delete pLocalColOffset;
+ pLocalColOffset = pS->pLocalColOffset;
+ aTableStack.pop();
+ }
+ }
+}
+
+void ScHTMLLayoutParser::Image( HtmlImportInfo* pInfo )
+{
+ mxActEntry->maImageList.push_back(std::make_unique<ScHTMLImage>());
+ ScHTMLImage* pImage = mxActEntry->maImageList.back().get();
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::SRC:
+ {
+ pImage->aURL = INetURLObject::GetAbsURL( aBaseURL, rOption.GetString() );
+ }
+ break;
+ case HtmlOptionId::ALT:
+ {
+ if (!mxActEntry->bHasGraphic)
+ { // ALT text only if not any image loaded
+ if (!mxActEntry->aAltText.isEmpty())
+ mxActEntry->aAltText += "; ";
+
+ mxActEntry->aAltText += rOption.GetString();
+ }
+ }
+ break;
+ case HtmlOptionId::WIDTH:
+ {
+ pImage->aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ case HtmlOptionId::HEIGHT:
+ {
+ pImage->aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ case HtmlOptionId::HSPACE:
+ {
+ pImage->aSpace.setX( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ case HtmlOptionId::VSPACE:
+ {
+ pImage->aSpace.setY( static_cast<tools::Long>(rOption.GetNumber()) );
+ }
+ break;
+ default: break;
+ }
+ }
+ if (pImage->aURL.isEmpty())
+ {
+ OSL_FAIL( "Image: graphic without URL ?!?" );
+ return ;
+ }
+
+ sal_uInt16 nFormat;
+ std::unique_ptr<Graphic> pGraphic(new Graphic);
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ if ( ERRCODE_NONE != GraphicFilter::LoadGraphic( pImage->aURL, pImage->aFilterName,
+ *pGraphic, &rFilter, &nFormat ) )
+ {
+ return ; // Bad luck
+ }
+ if (!mxActEntry->bHasGraphic)
+ { // discard any ALT text in this cell if we have any image
+ mxActEntry->bHasGraphic = true;
+ mxActEntry->aAltText.clear();
+ }
+ pImage->aFilterName = rFilter.GetImportFormatName( nFormat );
+ pImage->pGraphic = std::move( pGraphic );
+ if ( !(pImage->aSize.Width() && pImage->aSize.Height()) )
+ {
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ pImage->aSize = pDefaultDev->LogicToPixel( pImage->pGraphic->GetPrefSize(),
+ pImage->pGraphic->GetPrefMapMode() );
+ }
+ if (mxActEntry->maImageList.empty())
+ return;
+
+ tools::Long nWidth = 0;
+ for (const std::unique_ptr<ScHTMLImage> & pI : mxActEntry->maImageList)
+ {
+ if ( pI->nDir & nHorizontal )
+ nWidth += pI->aSize.Width() + 2 * pI->aSpace.X();
+ else
+ nWidth = 0;
+ }
+ if ( mxActEntry->nWidth
+ && (nWidth + pImage->aSize.Width() + 2 * pImage->aSpace.X()
+ >= mxActEntry->nWidth) )
+ mxActEntry->maImageList.back()->nDir = nVertical;
+}
+
+void ScHTMLLayoutParser::ColOn( HtmlImportInfo* pInfo )
+{
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ if( rOption.GetToken() == HtmlOptionId::WIDTH )
+ {
+ sal_uInt16 nVal = GetWidthPixel( rOption );
+ MakeCol( pLocalColOffset, nColOffset, nVal, 0, 0 );
+ nColOffset = nColOffset + nVal;
+ }
+ }
+}
+
+sal_uInt16 ScHTMLLayoutParser::GetWidthPixel( const HTMLOption& rOption )
+{
+ const OUString& rOptVal = rOption.GetString();
+ if ( rOptVal.indexOf('%') != -1 )
+ { // Percent
+ sal_uInt16 nW = (nTableWidth ? nTableWidth : static_cast<sal_uInt16>(aPageSize.Width()));
+ return static_cast<sal_uInt16>((rOption.GetNumber() * nW) / 100);
+ }
+ else
+ {
+ if ( rOptVal.indexOf('*') != -1 )
+ { // Relative to what?
+ // TODO: Collect all relative values in ColArray and then MakeCol
+ return 0;
+ }
+ else
+ return static_cast<sal_uInt16>(rOption.GetNumber()); // Pixel
+ }
+}
+
+void ScHTMLLayoutParser::AnchorOn( HtmlImportInfo* pInfo )
+{
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ if( rOption.GetToken() == HtmlOptionId::NAME )
+ mxActEntry->pName = rOption.GetString();
+ }
+}
+
+bool ScHTMLLayoutParser::IsAtBeginningOfText( const HtmlImportInfo* pInfo )
+{
+ ESelection& rSel = mxActEntry->aSel;
+ return rSel.nStartPara == rSel.nEndPara &&
+ rSel.nStartPara <= pInfo->aSelection.nEndPara &&
+ pEdit->GetTextLen( rSel.nStartPara ) == 0;
+}
+
+void ScHTMLLayoutParser::FontOn( HtmlImportInfo* pInfo )
+{
+ if ( !IsAtBeginningOfText( pInfo ) )
+ return;
+
+// Only at the start of the text; applies to whole line
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(pInfo->pParser)->GetOptions();
+ for (const auto & rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::FACE :
+ {
+ const OUString& rFace = rOption.GetString();
+ OUStringBuffer aFontName;
+ sal_Int32 nPos = 0;
+ while( nPos != -1 )
+ {
+ // Font list, VCL uses the semicolon as separator
+ // HTML uses the comma
+ std::u16string_view aFName = o3tl::getToken(rFace, 0, ',', nPos );
+ aFName = comphelper::string::strip(aFName, ' ');
+ if( !aFontName.isEmpty() )
+ aFontName.append(";");
+ aFontName.append(aFName);
+ }
+ if ( !aFontName.isEmpty() )
+ mxActEntry->aItemSet.Put( SvxFontItem( FAMILY_DONTKNOW,
+ aFontName.makeStringAndClear(), OUString(), PITCH_DONTKNOW,
+ RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
+ }
+ break;
+ case HtmlOptionId::SIZE :
+ {
+ sal_uInt16 nSize = static_cast<sal_uInt16>(rOption.GetNumber());
+ if ( nSize == 0 )
+ nSize = 1;
+ else if ( nSize > SC_HTML_FONTSIZES )
+ nSize = SC_HTML_FONTSIZES;
+ mxActEntry->aItemSet.Put( SvxFontHeightItem(
+ maFontHeights[nSize-1], 100, ATTR_FONT_HEIGHT ) );
+ }
+ break;
+ case HtmlOptionId::COLOR :
+ {
+ Color aColor;
+ rOption.GetColor( aColor );
+ mxActEntry->aItemSet.Put( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void ScHTMLLayoutParser::ProcToken( HtmlImportInfo* pInfo )
+{
+ switch ( pInfo->nToken )
+ {
+ case HtmlTokenId::META:
+ {
+ HTMLParser* pParser = static_cast<HTMLParser*>(pInfo->pParser);
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
+ pParser->ParseMetaOptions(
+ xDPS->getDocumentProperties(),
+ mpDoc->GetDocumentShell()->GetHeaderAttributes() );
+ }
+ break;
+ case HtmlTokenId::TITLE_ON:
+ {
+ bInTitle = true;
+ aString.clear();
+ }
+ break;
+ case HtmlTokenId::TITLE_OFF:
+ {
+ if ( bInTitle && !aString.isEmpty() )
+ {
+ // Remove blanks from line breaks
+ aString = aString.trim();
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ mpDoc->GetDocumentShell()->GetModel(),
+ uno::UNO_QUERY_THROW);
+ xDPS->getDocumentProperties()->setTitle(aString);
+ }
+ bInTitle = false;
+ }
+ break;
+ case HtmlTokenId::TABLE_ON:
+ {
+ TableOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::COL_ON:
+ {
+ ColOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLEHEADER_ON: // Opens row
+ {
+ if ( bInCell )
+ CloseEntry( pInfo );
+ // Do not set bInCell to true, TableDataOn does that
+ mxActEntry->aItemSet.Put(
+ SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT) );
+ [[fallthrough]];
+ }
+ case HtmlTokenId::TABLEDATA_ON: // Opens cell
+ {
+ TableDataOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLEHEADER_OFF:
+ case HtmlTokenId::TABLEDATA_OFF: // Closes cell
+ {
+ TableDataOff( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLEROW_ON: // Before first cell in row
+ {
+ TableRowOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLEROW_OFF: // After last cell in row
+ {
+ TableRowOff( pInfo );
+ }
+ break;
+ case HtmlTokenId::TABLE_OFF:
+ {
+ TableOff( pInfo );
+ }
+ break;
+ case HtmlTokenId::IMAGE:
+ {
+ Image( pInfo );
+ }
+ break;
+ case HtmlTokenId::PARABREAK_OFF:
+ { // We continue vertically after an image
+ if (!mxActEntry->maImageList.empty())
+ mxActEntry->maImageList.back()->nDir = nVertical;
+ }
+ break;
+ case HtmlTokenId::ANCHOR_ON:
+ {
+ AnchorOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::FONT_ON :
+ {
+ FontOn( pInfo );
+ }
+ break;
+ case HtmlTokenId::BIGPRINT_ON :
+ {
+ // TODO: Remember current font size and increase by 1
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxFontHeightItem(
+ maFontHeights[3], 100, ATTR_FONT_HEIGHT ) );
+ }
+ break;
+ case HtmlTokenId::SMALLPRINT_ON :
+ {
+ // TODO: Remember current font size and decrease by 1
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxFontHeightItem(
+ maFontHeights[0], 100, ATTR_FONT_HEIGHT ) );
+ }
+ break;
+ case HtmlTokenId::BOLD_ON :
+ case HtmlTokenId::STRONG_ON :
+ {
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
+ ATTR_FONT_WEIGHT ) );
+ }
+ break;
+ case HtmlTokenId::ITALIC_ON :
+ case HtmlTokenId::EMPHASIS_ON :
+ case HtmlTokenId::ADDRESS_ON :
+ case HtmlTokenId::BLOCKQUOTE_ON :
+ case HtmlTokenId::BLOCKQUOTE30_ON :
+ case HtmlTokenId::CITATION_ON :
+ case HtmlTokenId::VARIABLE_ON :
+ {
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
+ ATTR_FONT_POSTURE ) );
+ }
+ break;
+ case HtmlTokenId::DEFINSTANCE_ON :
+ {
+ if ( IsAtBeginningOfText( pInfo ) )
+ {
+ mxActEntry->aItemSet.Put( SvxWeightItem( WEIGHT_BOLD,
+ ATTR_FONT_WEIGHT ) );
+ mxActEntry->aItemSet.Put( SvxPostureItem( ITALIC_NORMAL,
+ ATTR_FONT_POSTURE ) );
+ }
+ }
+ break;
+ case HtmlTokenId::UNDERLINE_ON :
+ {
+ if ( IsAtBeginningOfText( pInfo ) )
+ mxActEntry->aItemSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE,
+ ATTR_FONT_UNDERLINE ) );
+ }
+ break;
+ case HtmlTokenId::TEXTTOKEN:
+ {
+ if ( bInTitle )
+ aString += pInfo->aText;
+ }
+ break;
+ default: ;
+ }
+}
+
+// HTML DATA QUERY PARSER
+
+template< typename Type >
+static Type getLimitedValue( const Type& rValue, const Type& rMin, const Type& rMax )
+{ return std::clamp( rValue, rMin, rMax ); }
+
+ScHTMLEntry::ScHTMLEntry( const SfxItemSet& rItemSet, ScHTMLTableId nTableId ) :
+ ScEEParseEntry( rItemSet ),
+ mbImportAlways( false )
+{
+ nTab = nTableId;
+ bEntirePara = false;
+}
+
+bool ScHTMLEntry::HasContents() const
+{
+ return mbImportAlways || aSel.HasRange() || !aAltText.isEmpty() || IsTable();
+}
+
+void ScHTMLEntry::AdjustStart( const HtmlImportInfo& rInfo )
+{
+ // set start position
+ aSel.nStartPara = rInfo.aSelection.nStartPara;
+ aSel.nStartPos = rInfo.aSelection.nStartPos;
+ // adjust end position
+ if( (aSel.nEndPara < aSel.nStartPara) || ((aSel.nEndPara == aSel.nStartPara) && (aSel.nEndPos < aSel.nStartPos)) )
+ {
+ aSel.nEndPara = aSel.nStartPara;
+ aSel.nEndPos = aSel.nStartPos;
+ }
+}
+
+void ScHTMLEntry::AdjustEnd( const HtmlImportInfo& rInfo )
+{
+ OSL_ENSURE( (aSel.nEndPara < rInfo.aSelection.nEndPara) ||
+ ((aSel.nEndPara == rInfo.aSelection.nEndPara) && (aSel.nEndPos <= rInfo.aSelection.nEndPos)),
+ "ScHTMLQueryParser::AdjustEntryEnd - invalid end position" );
+ // set end position
+ aSel.nEndPara = rInfo.aSelection.nEndPara;
+ aSel.nEndPos = rInfo.aSelection.nEndPos;
+}
+
+void ScHTMLEntry::Strip( const EditEngine& rEditEngine )
+{
+ // strip leading empty paragraphs
+ while( (aSel.nStartPara < aSel.nEndPara) && (rEditEngine.GetTextLen( aSel.nStartPara ) <= aSel.nStartPos) )
+ {
+ ++aSel.nStartPara;
+ aSel.nStartPos = 0;
+ }
+ // strip trailing empty paragraphs
+ while( (aSel.nStartPara < aSel.nEndPara) && (aSel.nEndPos == 0) )
+ {
+ --aSel.nEndPara;
+ aSel.nEndPos = rEditEngine.GetTextLen( aSel.nEndPara );
+ }
+}
+
+/** A map of ScHTMLTable objects.
+
+ Organizes the tables with a unique table key. Stores nested tables inside
+ the parent table and forms in this way a tree structure of tables. An
+ instance of this class owns the contained table objects and deletes them
+ on destruction.
+ */
+class ScHTMLTableMap final
+{
+private:
+ typedef std::shared_ptr< ScHTMLTable > ScHTMLTablePtr;
+ typedef std::map< ScHTMLTableId, ScHTMLTablePtr > ScHTMLTableStdMap;
+
+public:
+ typedef ScHTMLTableStdMap::iterator iterator;
+ typedef ScHTMLTableStdMap::const_iterator const_iterator;
+
+private:
+ ScHTMLTable& mrParentTable; /// Reference to parent table.
+ ScHTMLTableStdMap maTables; /// Container for all table objects.
+ mutable ScHTMLTable* mpCurrTable; /// Current table, used for fast search.
+
+public:
+ explicit ScHTMLTableMap( ScHTMLTable& rParentTable );
+
+ const_iterator begin() const { return maTables.begin(); }
+ const_iterator end() const { return maTables.end(); }
+
+ /** Returns the specified table.
+ @param nTableId Unique identifier of the table.
+ @param bDeep true = searches deep in all nested table; false = only in this container. */
+ ScHTMLTable* FindTable( ScHTMLTableId nTableId, bool bDeep = true ) const;
+
+ /** Inserts a new table into the container. This container owns the created table.
+ @param bPreFormText true = New table is based on preformatted text (<pre> tag). */
+ ScHTMLTable* CreateTable( const HtmlImportInfo& rInfo, bool bPreFormText, const ScDocument& rDoc );
+
+private:
+ /** Sets a working table with its index for search optimization. */
+ void SetCurrTable( ScHTMLTable* pTable ) const
+ { if( pTable ) mpCurrTable = pTable; }
+};
+
+ScHTMLTableMap::ScHTMLTableMap( ScHTMLTable& rParentTable ) :
+ mrParentTable(rParentTable),
+ mpCurrTable(nullptr)
+{
+}
+
+ScHTMLTable* ScHTMLTableMap::FindTable( ScHTMLTableId nTableId, bool bDeep ) const
+{
+ ScHTMLTable* pResult = nullptr;
+ if( mpCurrTable && (nTableId == mpCurrTable->GetTableId()) )
+ pResult = mpCurrTable; // cached table
+ else
+ {
+ const_iterator aFind = maTables.find( nTableId );
+ if( aFind != maTables.end() )
+ pResult = aFind->second.get(); // table from this container
+ }
+
+ // not found -> search deep in nested tables
+ if( !pResult && bDeep )
+ for( const_iterator aIter = begin(), aEnd = end(); !pResult && (aIter != aEnd); ++aIter )
+ pResult = aIter->second->FindNestedTable( nTableId );
+
+ SetCurrTable( pResult );
+ return pResult;
+}
+
+ScHTMLTable* ScHTMLTableMap::CreateTable( const HtmlImportInfo& rInfo, bool bPreFormText, const ScDocument& rDoc )
+{
+ ScHTMLTable* pTable = new ScHTMLTable( mrParentTable, rInfo, bPreFormText, rDoc );
+ maTables[ pTable->GetTableId() ].reset( pTable );
+ SetCurrTable( pTable );
+ return pTable;
+}
+
+namespace {
+
+/** Simplified forward iterator for convenience.
+
+ Before the iterator can be dereferenced, it must be tested with the is()
+ method. The iterator may be invalid directly after construction (e.g. empty
+ container).
+ */
+class ScHTMLTableIterator
+{
+public:
+ /** Constructs the iterator for the passed table map.
+ @param pTableMap Pointer to the table map (is allowed to be NULL). */
+ explicit ScHTMLTableIterator( const ScHTMLTableMap* pTableMap );
+
+ bool is() const { return mpTableMap && maIter != maEnd; }
+ ScHTMLTable* operator->() { return maIter->second.get(); }
+ ScHTMLTableIterator& operator++() { ++maIter; return *this; }
+
+private:
+ ScHTMLTableMap::const_iterator maIter;
+ ScHTMLTableMap::const_iterator maEnd;
+ const ScHTMLTableMap* mpTableMap;
+};
+
+}
+
+ScHTMLTableIterator::ScHTMLTableIterator( const ScHTMLTableMap* pTableMap ) :
+ mpTableMap(pTableMap)
+{
+ if( pTableMap )
+ {
+ maIter = pTableMap->begin();
+ maEnd = pTableMap->end();
+ }
+}
+
+ScHTMLTableAutoId::ScHTMLTableAutoId( ScHTMLTableId& rnUnusedId ) :
+ mnTableId( rnUnusedId ),
+ mrnUnusedId( rnUnusedId )
+{
+ ++mrnUnusedId;
+}
+
+ScHTMLTable::ScHTMLTable( ScHTMLTable& rParentTable, const HtmlImportInfo& rInfo, bool bPreFormText, const ScDocument& rDoc ) :
+ mpParentTable( &rParentTable ),
+ maTableId( rParentTable.maTableId.mrnUnusedId ),
+ maTableItemSet( rParentTable.GetCurrItemSet() ),
+ mrEditEngine( rParentTable.mrEditEngine ),
+ mrEEParseList( rParentTable.mrEEParseList ),
+ mpCurrEntryVector( nullptr ),
+ maSize( 1, 1 ),
+ mpParser(rParentTable.mpParser),
+ mrDoc(rDoc),
+ mbBorderOn( false ),
+ mbPreFormText( bPreFormText ),
+ mbRowOn( false ),
+ mbDataOn( false ),
+ mbPushEmptyLine( false ),
+ mbCaptionOn ( false )
+{
+ if( mbPreFormText )
+ {
+ ImplRowOn();
+ ImplDataOn( ScHTMLSize( 1, 1 ) );
+ }
+ else
+ {
+ ProcessFormatOptions( maTableItemSet, rInfo );
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
+ for (const auto& rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::BORDER:
+ mbBorderOn = rOption.GetString().isEmpty() || (rOption.GetNumber() != 0);
+ break;
+ case HtmlOptionId::ID:
+ maTableName = rOption.GetString();
+ break;
+ default: break;
+ }
+ }
+ }
+
+ CreateNewEntry( rInfo );
+}
+
+ScHTMLTable::ScHTMLTable(
+ SfxItemPool& rPool,
+ EditEngine& rEditEngine,
+ std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseList,
+ ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser, const ScDocument& rDoc
+) :
+ mpParentTable( nullptr ),
+ maTableId( rnUnusedId ),
+ maTableItemSet( rPool ),
+ mrEditEngine( rEditEngine ),
+ mrEEParseList( rEEParseList ),
+ mpCurrEntryVector( nullptr ),
+ maSize( 1, 1 ),
+ mpParser(pParser),
+ mrDoc(rDoc),
+ mbBorderOn( false ),
+ mbPreFormText( false ),
+ mbRowOn( false ),
+ mbDataOn( false ),
+ mbPushEmptyLine( false ),
+ mbCaptionOn ( false )
+{
+ // open the first "cell" of the document
+ ImplRowOn();
+ ImplDataOn( ScHTMLSize( 1, 1 ) );
+ mxCurrEntry = CreateEntry();
+}
+
+ScHTMLTable::~ScHTMLTable()
+{
+}
+
+const SfxItemSet& ScHTMLTable::GetCurrItemSet() const
+{
+ // first try cell item set, then row item set, then table item set
+ return moDataItemSet ? *moDataItemSet : (moRowItemSet ? *moRowItemSet : maTableItemSet);
+}
+
+ScHTMLSize ScHTMLTable::GetSpan( const ScHTMLPos& rCellPos ) const
+{
+ ScHTMLSize aSpan( 1, 1 );
+ const ScRange* pRange = maVMergedCells.Find( rCellPos.MakeAddr() );
+ if (!pRange)
+ pRange = maHMergedCells.Find( rCellPos.MakeAddr() );
+ if (pRange)
+ aSpan.Set( pRange->aEnd.Col() - pRange->aStart.Col() + 1, pRange->aEnd.Row() - pRange->aStart.Row() + 1 );
+ return aSpan;
+}
+
+ScHTMLTable* ScHTMLTable::FindNestedTable( ScHTMLTableId nTableId ) const
+{
+ return mxNestedTables ? mxNestedTables->FindTable( nTableId ) : nullptr;
+}
+
+void ScHTMLTable::PutItem( const SfxPoolItem& rItem )
+{
+ OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PutItem - no current entry" );
+ if( mxCurrEntry && mxCurrEntry->IsEmpty() )
+ mxCurrEntry->GetItemSet().Put( rItem );
+}
+
+void ScHTMLTable::PutText( const HtmlImportInfo& rInfo )
+{
+ OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PutText - no current entry" );
+ if( mxCurrEntry )
+ {
+ if( !mxCurrEntry->HasContents() && IsSpaceCharInfo( rInfo ) )
+ mxCurrEntry->AdjustStart( rInfo );
+ else
+ mxCurrEntry->AdjustEnd( rInfo );
+ if (mbCaptionOn)
+ maCaptionBuffer.append(rInfo.aText);
+
+ }
+}
+
+void ScHTMLTable::InsertPara( const HtmlImportInfo& rInfo )
+{
+ if( mxCurrEntry && mbDataOn && !IsEmptyCell() )
+ mxCurrEntry->SetImportAlways();
+ PushEntry( rInfo );
+ CreateNewEntry( rInfo );
+ InsertLeadingEmptyLine();
+}
+
+void ScHTMLTable::BreakOn()
+{
+ // empty line, if <br> is at start of cell
+ mbPushEmptyLine = !mbPreFormText && mbDataOn && IsEmptyCell();
+}
+
+void ScHTMLTable::HeadingOn()
+{
+ // call directly, InsertPara() has not been called before
+ InsertLeadingEmptyLine();
+}
+
+void ScHTMLTable::InsertLeadingEmptyLine()
+{
+ // empty line, if <p>, </p>, <h?>, or </h*> are not at start of cell
+ mbPushEmptyLine = !mbPreFormText && mbDataOn && !IsEmptyCell();
+}
+
+void ScHTMLTable::AnchorOn()
+{
+ OSL_ENSURE( mxCurrEntry, "ScHTMLTable::AnchorOn - no current entry" );
+ // don't skip entries with single hyperlinks
+ if( mxCurrEntry )
+ mxCurrEntry->SetImportAlways();
+}
+
+ScHTMLTable* ScHTMLTable::TableOn( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo );
+ return InsertNestedTable( rInfo, false );
+}
+
+ScHTMLTable* ScHTMLTable::TableOff( const HtmlImportInfo& rInfo )
+{
+ return mbPreFormText ? this : CloseTable( rInfo );
+}
+
+void ScHTMLTable::CaptionOn()
+{
+ mbCaptionOn = true;
+ maCaptionBuffer.setLength(0);
+}
+
+void ScHTMLTable::CaptionOff()
+{
+ if (!mbCaptionOn)
+ return;
+ maCaption = maCaptionBuffer.makeStringAndClear().trim();
+ mbCaptionOn = false;
+}
+
+ScHTMLTable* ScHTMLTable::PreOn( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo );
+ return InsertNestedTable( rInfo, true );
+}
+
+ScHTMLTable* ScHTMLTable::PreOff( const HtmlImportInfo& rInfo )
+{
+ return mbPreFormText ? CloseTable( rInfo ) : this;
+}
+
+void ScHTMLTable::RowOn( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo, true );
+ if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
+ {
+ ImplRowOn();
+ ProcessFormatOptions( *moRowItemSet, rInfo );
+ }
+ CreateNewEntry( rInfo );
+}
+
+void ScHTMLTable::RowOff( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo, true );
+ if( mpParentTable && !mbPreFormText ) // no rows allowed in global and preformatted tables
+ ImplRowOff();
+ CreateNewEntry( rInfo );
+}
+
+namespace {
+
+/**
+ * Decode a number format string stored in Excel-generated HTML's CSS
+ * region.
+ */
+OUString decodeNumberFormat(const OUString& rFmt)
+{
+ OUStringBuffer aBuf;
+ const sal_Unicode* p = rFmt.getStr();
+ sal_Int32 n = rFmt.getLength();
+ for (sal_Int32 i = 0; i < n; ++i, ++p)
+ {
+ if (*p == '\\')
+ {
+ // Skip '\'.
+ ++i;
+ ++p;
+
+ // Parse all subsequent digits until first non-digit is found.
+ sal_Int32 nDigitCount = 0;
+ const sal_Unicode* p1 = p;
+ for (; i < n; ++i, ++p, ++nDigitCount)
+ {
+ if (*p < '0' || '9' < *p)
+ {
+ --i;
+ --p;
+ break;
+ }
+
+ }
+ if (nDigitCount)
+ {
+ // Hex-encoded character found. Decode it back into its
+ // original character. An example of number format with
+ // hex-encoded chars: "\0022$\0022\#\,\#\#0\.00"
+ sal_uInt32 nVal = OUString(p1, nDigitCount).toUInt32(16);
+ aBuf.append(static_cast<sal_Unicode>(nVal));
+ }
+ }
+ else
+ aBuf.append(*p);
+ }
+ return aBuf.makeStringAndClear();
+}
+
+}
+
+void ScHTMLTable::DataOn( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo, true );
+ if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
+ {
+ // read needed options from the <td> tag
+ ScHTMLSize aSpanSize( 1, 1 );
+ std::optional<OUString> pValStr, pNumStr;
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
+ sal_uInt32 nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ for (const auto& rOption : rOptions)
+ {
+ switch (rOption.GetToken())
+ {
+ case HtmlOptionId::COLSPAN:
+ aSpanSize.mnCols = static_cast<SCCOL>( getLimitedValue<sal_Int32>( rOption.GetString().toInt32(), 1, 256 ) );
+ break;
+ case HtmlOptionId::ROWSPAN:
+ aSpanSize.mnRows = static_cast<SCROW>( getLimitedValue<sal_Int32>( rOption.GetString().toInt32(), 1, 256 ) );
+ break;
+ case HtmlOptionId::SDVAL:
+ pValStr = rOption.GetString();
+ break;
+ case HtmlOptionId::SDNUM:
+ pNumStr = rOption.GetString();
+ break;
+ case HtmlOptionId::CLASS:
+ {
+ // Pick up the number format associated with this class (if
+ // any).
+ OUString aClass = rOption.GetString();
+ const ScHTMLStyles& rStyles = mpParser->GetStyles();
+ const OUString& rVal = rStyles.getPropertyValue("td", aClass, "mso-number-format");
+ if (!rVal.isEmpty())
+ {
+ OUString aNumFmt = decodeNumberFormat(rVal);
+
+ nNumberFormat = GetFormatTable()->GetEntryKey(aNumFmt);
+ if (nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_Int32 nErrPos = 0;
+ SvNumFormatType nDummy;
+ bool bValidFmt = GetFormatTable()->PutEntry(aNumFmt, nErrPos, nDummy, nNumberFormat);
+ if (!bValidFmt)
+ nNumberFormat = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+
+ ImplDataOn( aSpanSize );
+
+ if (nNumberFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ moDataItemSet->Put( SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat) );
+
+ ProcessFormatOptions( *moDataItemSet, rInfo );
+ CreateNewEntry( rInfo );
+ mxCurrEntry->pValStr = std::move(pValStr);
+ mxCurrEntry->pNumStr = std::move(pNumStr);
+ }
+ else
+ CreateNewEntry( rInfo );
+}
+
+void ScHTMLTable::DataOff( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo, true );
+ if( mpParentTable && !mbPreFormText ) // no cells allowed in global and preformatted tables
+ ImplDataOff();
+ CreateNewEntry( rInfo );
+}
+
+void ScHTMLTable::BodyOn( const HtmlImportInfo& rInfo )
+{
+ bool bPushed = PushEntry( rInfo );
+ if( !mpParentTable )
+ {
+ // do not start new row, if nothing (no title) precedes the body.
+ if( bPushed || !mbRowOn )
+ ImplRowOn();
+ if( bPushed || !mbDataOn )
+ ImplDataOn( ScHTMLSize( 1, 1 ) );
+ ProcessFormatOptions( *moDataItemSet, rInfo );
+ }
+ CreateNewEntry( rInfo );
+}
+
+void ScHTMLTable::BodyOff( const HtmlImportInfo& rInfo )
+{
+ PushEntry( rInfo );
+ if( !mpParentTable )
+ {
+ ImplDataOff();
+ ImplRowOff();
+ }
+ CreateNewEntry( rInfo );
+}
+
+ScHTMLTable* ScHTMLTable::CloseTable( const HtmlImportInfo& rInfo )
+{
+ if( mpParentTable ) // not allowed to close global table
+ {
+ PushEntry( rInfo, mbDataOn );
+ ImplDataOff();
+ ImplRowOff();
+ mpParentTable->PushTableEntry( GetTableId() );
+ mpParentTable->CreateNewEntry( rInfo );
+ if( mbPreFormText ) // enclose preformatted table with empty lines in parent table
+ mpParentTable->InsertLeadingEmptyLine();
+ return mpParentTable;
+ }
+ return this;
+}
+
+SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
+{
+ const ScSizeVec& rSizes = maCumSizes[ eOrient ];
+ size_t nIndex = static_cast< size_t >( nCellPos );
+ if( nIndex >= rSizes.size() ) return 0;
+ return (nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]);
+}
+
+SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellBegin, SCCOLROW nCellEnd ) const
+{
+ const ScSizeVec& rSizes = maCumSizes[ eOrient ];
+ size_t nBeginIdx = static_cast< size_t >( std::max< SCCOLROW >( nCellBegin, 0 ) );
+ size_t nEndIdx = static_cast< size_t >( std::min< SCCOLROW >( nCellEnd, static_cast< SCCOLROW >( rSizes.size() ) ) );
+ if (nBeginIdx >= nEndIdx ) return 0;
+ return rSizes[ nEndIdx - 1 ] - ((nBeginIdx == 0) ? 0 : rSizes[ nBeginIdx - 1 ]);
+}
+
+SCCOLROW ScHTMLTable::GetDocSize( ScHTMLOrient eOrient ) const
+{
+ const ScSizeVec& rSizes = maCumSizes[ eOrient ];
+ return rSizes.empty() ? 0 : rSizes.back();
+}
+
+ScHTMLSize ScHTMLTable::GetDocSize( const ScHTMLPos& rCellPos ) const
+{
+ ScHTMLSize aCellSpan = GetSpan( rCellPos );
+ return ScHTMLSize(
+ static_cast< SCCOL >( GetDocSize( tdCol, rCellPos.mnCol, rCellPos.mnCol + aCellSpan.mnCols ) ),
+ static_cast< SCROW >( GetDocSize( tdRow, rCellPos.mnRow, rCellPos.mnRow + aCellSpan.mnRows ) ) );
+}
+
+SCCOLROW ScHTMLTable::GetDocPos( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const
+{
+ return maDocBasePos.Get( eOrient ) + GetDocSize( eOrient, 0, nCellPos );
+}
+
+ScHTMLPos ScHTMLTable::GetDocPos( const ScHTMLPos& rCellPos ) const
+{
+ return ScHTMLPos(
+ static_cast< SCCOL >( GetDocPos( tdCol, rCellPos.mnCol ) ),
+ static_cast< SCROW >( GetDocPos( tdRow, rCellPos.mnRow ) ) );
+}
+
+void ScHTMLTable::GetDocRange( ScRange& rRange ) const
+{
+ rRange.aStart = rRange.aEnd = maDocBasePos.MakeAddr();
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!rRange.aEnd.Move( static_cast< SCCOL >( GetDocSize( tdCol ) ) - 1,
+ static_cast< SCROW >( GetDocSize( tdRow ) ) - 1, 0, aErrorPos, mrDoc ))
+ {
+ assert(!"can't move");
+ }
+}
+
+void ScHTMLTable::ApplyCellBorders( ScDocument* pDoc, const ScAddress& rFirstPos ) const
+{
+ OSL_ENSURE( pDoc, "ScHTMLTable::ApplyCellBorders - no document" );
+ if( pDoc && mbBorderOn )
+ {
+ const SCCOL nLastCol = maSize.mnCols - 1;
+ const SCROW nLastRow = maSize.mnRows - 1;
+ const tools::Long nOuterLine = SvxBorderLineWidth::Medium;
+ const tools::Long nInnerLine = SvxBorderLineWidth::Hairline;
+ SvxBorderLine aOuterLine(nullptr, nOuterLine, SvxBorderLineStyle::SOLID);
+ SvxBorderLine aInnerLine(nullptr, nInnerLine, SvxBorderLineStyle::SOLID);
+ SvxBoxItem aBorderItem( ATTR_BORDER );
+
+ for( SCCOL nCol = 0; nCol <= nLastCol; ++nCol )
+ {
+ SvxBorderLine* pLeftLine = (nCol == 0) ? &aOuterLine : &aInnerLine;
+ SvxBorderLine* pRightLine = (nCol == nLastCol) ? &aOuterLine : &aInnerLine;
+ SCCOL nCellCol1 = static_cast< SCCOL >( GetDocPos( tdCol, nCol ) ) + rFirstPos.Col();
+ SCCOL nCellCol2 = nCellCol1 + static_cast< SCCOL >( GetDocSize( tdCol, nCol ) ) - 1;
+ for( SCROW nRow = 0; nRow <= nLastRow; ++nRow )
+ {
+ SvxBorderLine* pTopLine = (nRow == 0) ? &aOuterLine : &aInnerLine;
+ SvxBorderLine* pBottomLine = (nRow == nLastRow) ? &aOuterLine : &aInnerLine;
+ SCROW nCellRow1 = GetDocPos( tdRow, nRow ) + rFirstPos.Row();
+ SCROW nCellRow2 = nCellRow1 + GetDocSize( tdRow, nRow ) - 1;
+ for( SCCOL nCellCol = nCellCol1; nCellCol <= nCellCol2; ++nCellCol )
+ {
+ aBorderItem.SetLine( (nCellCol == nCellCol1) ? pLeftLine : nullptr, SvxBoxItemLine::LEFT );
+ aBorderItem.SetLine( (nCellCol == nCellCol2) ? pRightLine : nullptr, SvxBoxItemLine::RIGHT );
+ for( SCROW nCellRow = nCellRow1; nCellRow <= nCellRow2; ++nCellRow )
+ {
+ aBorderItem.SetLine( (nCellRow == nCellRow1) ? pTopLine : nullptr, SvxBoxItemLine::TOP );
+ aBorderItem.SetLine( (nCellRow == nCellRow2) ? pBottomLine : nullptr, SvxBoxItemLine::BOTTOM );
+ pDoc->ApplyAttr( nCellCol, nCellRow, rFirstPos.Tab(), aBorderItem );
+ }
+ }
+ }
+ }
+ }
+
+ for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
+ aIter->ApplyCellBorders( pDoc, rFirstPos );
+}
+
+SvNumberFormatter* ScHTMLTable::GetFormatTable()
+{
+ return mpParser->GetDoc().GetFormatTable();
+}
+
+bool ScHTMLTable::IsEmptyCell() const
+{
+ return mpCurrEntryVector && mpCurrEntryVector->empty();
+}
+
+bool ScHTMLTable::IsSpaceCharInfo( const HtmlImportInfo& rInfo )
+{
+ return (rInfo.nToken == HtmlTokenId::TEXTTOKEN) && (rInfo.aText.getLength() == 1) && (rInfo.aText[ 0 ] == ' ');
+}
+
+ScHTMLTable::ScHTMLEntryPtr ScHTMLTable::CreateEntry() const
+{
+ return std::make_unique<ScHTMLEntry>( GetCurrItemSet() );
+}
+
+void ScHTMLTable::CreateNewEntry( const HtmlImportInfo& rInfo )
+{
+ OSL_ENSURE( !mxCurrEntry, "ScHTMLTable::CreateNewEntry - old entry still present" );
+ mxCurrEntry = CreateEntry();
+ mxCurrEntry->aSel = rInfo.aSelection;
+}
+
+void ScHTMLTable::ImplPushEntryToVector( ScHTMLEntryVector& rEntryVector, ScHTMLEntryPtr& rxEntry )
+{
+ // HTML entry list does not own the entries
+ rEntryVector.push_back( rxEntry.get() );
+ // mrEEParseList (reference to member of ScEEParser) owns the entries
+ mrEEParseList.push_back(std::shared_ptr<ScEEParseEntry>(rxEntry.release()));
+}
+
+bool ScHTMLTable::PushEntry( ScHTMLEntryPtr& rxEntry )
+{
+ bool bPushed = false;
+ if( rxEntry && rxEntry->HasContents() )
+ {
+ if( mpCurrEntryVector )
+ {
+ if( mbPushEmptyLine )
+ {
+ ScHTMLEntryPtr xEmptyEntry = CreateEntry();
+ ImplPushEntryToVector( *mpCurrEntryVector, xEmptyEntry );
+ mbPushEmptyLine = false;
+ }
+ ImplPushEntryToVector( *mpCurrEntryVector, rxEntry );
+ bPushed = true;
+ }
+ else if( mpParentTable )
+ {
+ bPushed = mpParentTable->PushEntry( rxEntry );
+ }
+ else
+ {
+ OSL_FAIL( "ScHTMLTable::PushEntry - cannot push entry, no parent found" );
+ }
+ }
+ return bPushed;
+}
+
+bool ScHTMLTable::PushEntry( const HtmlImportInfo& rInfo, bool bLastInCell )
+{
+ OSL_ENSURE( mxCurrEntry, "ScHTMLTable::PushEntry - no current entry" );
+ bool bPushed = false;
+ if( mxCurrEntry )
+ {
+ mxCurrEntry->AdjustEnd( rInfo );
+ mxCurrEntry->Strip( mrEditEngine );
+
+ // import entry always, if it is the last in cell, and cell is still empty
+ if( bLastInCell && IsEmptyCell() )
+ {
+ mxCurrEntry->SetImportAlways();
+ // don't insert empty lines before single empty entries
+ if( mxCurrEntry->IsEmpty() )
+ mbPushEmptyLine = false;
+ }
+
+ bPushed = PushEntry( mxCurrEntry );
+ mxCurrEntry.reset();
+ }
+ return bPushed;
+}
+
+void ScHTMLTable::PushTableEntry( ScHTMLTableId nTableId )
+{
+ OSL_ENSURE( nTableId != SC_HTML_GLOBAL_TABLE, "ScHTMLTable::PushTableEntry - cannot push global table" );
+ if( nTableId != SC_HTML_GLOBAL_TABLE )
+ {
+ ScHTMLEntryPtr xEntry( new ScHTMLEntry( maTableItemSet, nTableId ) );
+ PushEntry( xEntry );
+ }
+}
+
+ScHTMLTable* ScHTMLTable::GetExistingTable( ScHTMLTableId nTableId ) const
+{
+ ScHTMLTable* pTable = ((nTableId != SC_HTML_GLOBAL_TABLE) && mxNestedTables) ?
+ mxNestedTables->FindTable( nTableId, false ) : nullptr;
+ OSL_ENSURE( pTable || (nTableId == SC_HTML_GLOBAL_TABLE), "ScHTMLTable::GetExistingTable - table not found" );
+ return pTable;
+}
+
+ScHTMLTable* ScHTMLTable::InsertNestedTable( const HtmlImportInfo& rInfo, bool bPreFormText )
+{
+ if( !mxNestedTables )
+ mxNestedTables.reset( new ScHTMLTableMap( *this ) );
+ if( bPreFormText ) // enclose new preformatted table with empty lines
+ InsertLeadingEmptyLine();
+ return mxNestedTables->CreateTable( rInfo, bPreFormText, mrDoc );
+}
+
+void ScHTMLTable::InsertNewCell( const ScHTMLSize& rSpanSize )
+{
+ ScRange* pRange;
+
+ /* Find an unused cell by skipping all merged ranges that cover the
+ current cell position stored in maCurrCell. */
+ for (;;)
+ {
+ pRange = maVMergedCells.Find( maCurrCell.MakeAddr() );
+ if (!pRange)
+ pRange = maHMergedCells.Find( maCurrCell.MakeAddr() );
+ if (!pRange)
+ break;
+ maCurrCell.mnCol = pRange->aEnd.Col() + 1;
+ }
+ mpCurrEntryVector = &maEntryMap[ maCurrCell ];
+
+ /* If the new cell is merged horizontally, try to find collisions with
+ other vertically merged ranges. In this case, shrink existing
+ vertically merged ranges (do not shrink the new cell). */
+ SCCOL nColEnd = maCurrCell.mnCol + rSpanSize.mnCols;
+ for( ScAddress aAddr( maCurrCell.MakeAddr() ); aAddr.Col() < nColEnd; aAddr.IncCol() )
+ if( (pRange = maVMergedCells.Find( aAddr )) != nullptr )
+ pRange->aEnd.SetRow( maCurrCell.mnRow - 1 );
+
+ // insert the new range into the cell lists
+ ScRange aNewRange( maCurrCell.MakeAddr() );
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ if (!aNewRange.aEnd.Move( rSpanSize.mnCols - 1, rSpanSize.mnRows - 1, 0, aErrorPos, mrDoc ))
+ {
+ assert(!"can't move");
+ }
+ if( rSpanSize.mnRows > 1 )
+ {
+ maVMergedCells.push_back( aNewRange );
+ /* Do not insert vertically merged ranges into maUsedCells yet,
+ because they may be shrunken (see above). The final vertically
+ merged ranges are inserted in FillEmptyCells(). */
+ }
+ else
+ {
+ if( rSpanSize.mnCols > 1 )
+ maHMergedCells.push_back( aNewRange );
+ /* Insert horizontally merged ranges and single cells into
+ maUsedCells, they will not be changed anymore. */
+ maUsedCells.Join( aNewRange );
+ }
+
+ // adjust table size
+ maSize.mnCols = std::max< SCCOL >( maSize.mnCols, aNewRange.aEnd.Col() + 1 );
+ maSize.mnRows = std::max< SCROW >( maSize.mnRows, aNewRange.aEnd.Row() + 1 );
+}
+
+void ScHTMLTable::ImplRowOn()
+{
+ if( mbRowOn )
+ ImplRowOff();
+ moRowItemSet.emplace( maTableItemSet );
+ maCurrCell.mnCol = 0;
+ mbRowOn = true;
+ mbDataOn = false;
+}
+
+void ScHTMLTable::ImplRowOff()
+{
+ if( mbDataOn )
+ ImplDataOff();
+ if( mbRowOn )
+ {
+ moRowItemSet.reset();
+ ++maCurrCell.mnRow;
+ mbRowOn = mbDataOn = false;
+ }
+}
+
+void ScHTMLTable::ImplDataOn( const ScHTMLSize& rSpanSize )
+{
+ if( mbDataOn )
+ ImplDataOff();
+ if( !mbRowOn )
+ ImplRowOn();
+ moDataItemSet.emplace( *moRowItemSet );
+ InsertNewCell( rSpanSize );
+ mbDataOn = true;
+ mbPushEmptyLine = false;
+}
+
+void ScHTMLTable::ImplDataOff()
+{
+ if( mbDataOn )
+ {
+ moDataItemSet.reset();
+ ++maCurrCell.mnCol;
+ mpCurrEntryVector = nullptr;
+ mbDataOn = false;
+ }
+}
+
+void ScHTMLTable::ProcessFormatOptions( SfxItemSet& rItemSet, const HtmlImportInfo& rInfo )
+{
+ // special handling for table header cells
+ if( rInfo.nToken == HtmlTokenId::TABLEHEADER_ON )
+ {
+ rItemSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ rItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) );
+ }
+
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
+ for (const auto& rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::ALIGN:
+ {
+ SvxCellHorJustify eVal = SvxCellHorJustify::Standard;
+ const OUString& rOptVal = rOption.GetString();
+ if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
+ eVal = SvxCellHorJustify::Right;
+ else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_center ) )
+ eVal = SvxCellHorJustify::Center;
+ else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
+ eVal = SvxCellHorJustify::Left;
+ if( eVal != SvxCellHorJustify::Standard )
+ rItemSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY ) );
+ }
+ break;
+
+ case HtmlOptionId::VALIGN:
+ {
+ SvxCellVerJustify eVal = SvxCellVerJustify::Standard;
+ const OUString& rOptVal = rOption.GetString();
+ if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_top ) )
+ eVal = SvxCellVerJustify::Top;
+ else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_middle ) )
+ eVal = SvxCellVerJustify::Center;
+ else if( rOptVal.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_VA_bottom ) )
+ eVal = SvxCellVerJustify::Bottom;
+ if( eVal != SvxCellVerJustify::Standard )
+ rItemSet.Put( SvxVerJustifyItem( eVal, ATTR_VER_JUSTIFY ) );
+ }
+ break;
+
+ case HtmlOptionId::BGCOLOR:
+ {
+ Color aColor;
+ rOption.GetColor( aColor );
+ rItemSet.Put( SvxBrushItem( aColor, ATTR_BACKGROUND ) );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void ScHTMLTable::SetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nSize )
+{
+ OSL_ENSURE( nCellPos >= 0, "ScHTMLTable::SetDocSize - unexpected negative position" );
+ ScSizeVec& rSizes = maCumSizes[ eOrient ];
+ size_t nIndex = static_cast< size_t >( nCellPos );
+ // expand with height/width == 1
+ while( nIndex >= rSizes.size() )
+ rSizes.push_back( rSizes.empty() ? 1 : (rSizes.back() + 1) );
+ // update size of passed position and all following
+ // #i109987# only grow, don't shrink - use the largest needed size
+ SCCOLROW nDiff = nSize - ((nIndex == 0) ? rSizes.front() : (rSizes[ nIndex ] - rSizes[ nIndex - 1 ]));
+ if( nDiff > 0 )
+ std::for_each(rSizes.begin() + nIndex, rSizes.end(), [&nDiff](SCCOLROW& rSize) { rSize += nDiff; });
+}
+
+void ScHTMLTable::CalcNeededDocSize(
+ ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nCellSpan, SCCOLROW nRealDocSize )
+{
+ SCCOLROW nDiffSize = 0;
+ // in merged columns/rows: reduce needed size by size of leading columns
+ while( nCellSpan > 1 )
+ {
+ nDiffSize += GetDocSize( eOrient, nCellPos );
+ --nCellSpan;
+ ++nCellPos;
+ }
+ // set remaining needed size to last column/row
+ nRealDocSize -= std::min< SCCOLROW >( nRealDocSize - 1, nDiffSize );
+ SetDocSize( eOrient, nCellPos, nRealDocSize );
+}
+
+void ScHTMLTable::FillEmptyCells()
+{
+ for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
+ aIter->FillEmptyCells();
+
+ // insert the final vertically merged ranges into maUsedCells
+ for ( size_t i = 0, nRanges = maVMergedCells.size(); i < nRanges; ++i )
+ {
+ ScRange & rRange = maVMergedCells[ i ];
+ maUsedCells.Join( rRange );
+ }
+
+ for( ScAddress aAddr; aAddr.Row() < maSize.mnRows; aAddr.IncRow() )
+ {
+ for( aAddr.SetCol( 0 ); aAddr.Col() < maSize.mnCols; aAddr.IncCol() )
+ {
+ if( !maUsedCells.Find( aAddr ) )
+ {
+ // create a range for the lock list (used to calc. cell span)
+ ScRange aRange( aAddr );
+ do
+ {
+ aRange.aEnd.IncCol();
+ }
+ while( (aRange.aEnd.Col() < maSize.mnCols) && !maUsedCells.Find( aRange.aEnd ) );
+ aRange.aEnd.IncCol( -1 );
+ maUsedCells.Join( aRange );
+
+ // insert a dummy entry
+ ScHTMLEntryPtr xEntry = CreateEntry();
+ ImplPushEntryToVector( maEntryMap[ ScHTMLPos( aAddr ) ], xEntry );
+ }
+ }
+ }
+}
+
+void ScHTMLTable::RecalcDocSize()
+{
+ // recalc table sizes recursively from inner to outer
+ for( ScHTMLTableIterator aIter( mxNestedTables.get() ); aIter.is(); ++aIter )
+ aIter->RecalcDocSize();
+
+ /* Two passes: first calculates the sizes of single columns/rows, then
+ the sizes of spanned columns/rows. This allows to fill nested tables
+ into merged cells optimally. */
+ static const sal_uInt16 PASS_SINGLE = 0;
+ static const sal_uInt16 PASS_SPANNED = 1;
+ for( sal_uInt16 nPass = PASS_SINGLE; nPass <= PASS_SPANNED; ++nPass )
+ {
+ // iterate through every table cell
+ for( const auto& [rCellPos, rEntryVector] : maEntryMap )
+ {
+ ScHTMLSize aCellSpan = GetSpan( rCellPos );
+
+ // process the dimension of the current cell in this pass?
+ // (pass is single and span is 1) or (pass is not single and span is not 1)
+ bool bProcessColWidth = ((nPass == PASS_SINGLE) == (aCellSpan.mnCols == 1));
+ bool bProcessRowHeight = ((nPass == PASS_SINGLE) == (aCellSpan.mnRows == 1));
+ if( bProcessColWidth || bProcessRowHeight )
+ {
+ ScHTMLSize aDocSize( 1, 0 ); // resulting size of the cell in document
+
+ // expand the cell size for each cell parse entry
+ for( const auto& rpEntry : rEntryVector )
+ {
+ ScHTMLTable* pTable = GetExistingTable( rpEntry->GetTableId() );
+ // find entry with maximum width
+ if( bProcessColWidth && pTable )
+ aDocSize.mnCols = std::max( aDocSize.mnCols, static_cast< SCCOL >( pTable->GetDocSize( tdCol ) ) );
+ // add up height of each entry
+ if( bProcessRowHeight )
+ aDocSize.mnRows += pTable ? pTable->GetDocSize( tdRow ) : 1;
+ }
+ if( !aDocSize.mnRows )
+ aDocSize.mnRows = 1;
+
+ if( bProcessColWidth )
+ CalcNeededDocSize( tdCol, rCellPos.mnCol, aCellSpan.mnCols, aDocSize.mnCols );
+ if( bProcessRowHeight )
+ CalcNeededDocSize( tdRow, rCellPos.mnRow, aCellSpan.mnRows, aDocSize.mnRows );
+ }
+ }
+ }
+}
+
+void ScHTMLTable::RecalcDocPos( const ScHTMLPos& rBasePos )
+{
+ maDocBasePos = rBasePos;
+ // after the previous assignment it is allowed to call GetDocPos() methods
+
+ // iterate through every table cell
+ for( auto& [rCellPos, rEntryVector] : maEntryMap )
+ {
+ // fixed doc position of the entire cell (first entry)
+ const ScHTMLPos aCellDocPos( GetDocPos( rCellPos ) );
+ // fixed doc size of the entire cell
+ const ScHTMLSize aCellDocSize( GetDocSize( rCellPos ) );
+
+ // running doc position for single entries
+ ScHTMLPos aEntryDocPos( aCellDocPos );
+
+ ScHTMLEntry* pEntry = nullptr;
+ for( const auto& rpEntry : rEntryVector )
+ {
+ pEntry = rpEntry;
+ if( ScHTMLTable* pTable = GetExistingTable( pEntry->GetTableId() ) )
+ {
+ pTable->RecalcDocPos( aEntryDocPos ); // recalc nested table
+ pEntry->nCol = SCCOL_MAX;
+ pEntry->nRow = SCROW_MAX;
+ SCROW nTableRows = static_cast< SCROW >( pTable->GetDocSize( tdRow ) );
+
+ // use this entry to pad empty space right of table
+ if( mpParentTable ) // ... but not in global table
+ {
+ SCCOL nStartCol = aEntryDocPos.mnCol + static_cast< SCCOL >( pTable->GetDocSize( tdCol ) );
+ SCCOL nNextCol = aEntryDocPos.mnCol + aCellDocSize.mnCols;
+ if( nStartCol < nNextCol )
+ {
+ pEntry->nCol = nStartCol;
+ pEntry->nRow = aEntryDocPos.mnRow;
+ pEntry->nColOverlap = nNextCol - nStartCol;
+ pEntry->nRowOverlap = nTableRows;
+ }
+ }
+ aEntryDocPos.mnRow += nTableRows;
+ }
+ else
+ {
+ pEntry->nCol = aEntryDocPos.mnCol;
+ pEntry->nRow = aEntryDocPos.mnRow;
+ if( mpParentTable ) // do not merge in global table
+ pEntry->nColOverlap = aCellDocSize.mnCols;
+ ++aEntryDocPos.mnRow;
+ }
+ }
+
+ // pEntry points now to last entry.
+ if( pEntry )
+ {
+ if( (pEntry == rEntryVector.front()) && (pEntry->GetTableId() == SC_HTML_NO_TABLE) )
+ {
+ // pEntry is the only entry in this cell - merge rows of cell with single non-table entry.
+ pEntry->nRowOverlap = aCellDocSize.mnRows;
+ }
+ else
+ {
+ // fill up incomplete entry lists
+ SCROW nFirstUnusedRow = aCellDocPos.mnRow + aCellDocSize.mnRows;
+ while( aEntryDocPos.mnRow < nFirstUnusedRow )
+ {
+ ScHTMLEntryPtr xDummyEntry( new ScHTMLEntry( pEntry->GetItemSet() ) );
+ xDummyEntry->nCol = aEntryDocPos.mnCol;
+ xDummyEntry->nRow = aEntryDocPos.mnRow;
+ xDummyEntry->nColOverlap = aCellDocSize.mnCols;
+ ImplPushEntryToVector( rEntryVector, xDummyEntry );
+ ++aEntryDocPos.mnRow;
+ }
+ }
+ }
+ }
+}
+
+ScHTMLGlobalTable::ScHTMLGlobalTable(
+ SfxItemPool& rPool,
+ EditEngine& rEditEngine,
+ std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseVector,
+ ScHTMLTableId& rnUnusedId,
+ ScHTMLParser* pParser,
+ const ScDocument& rDoc
+) :
+ ScHTMLTable( rPool, rEditEngine, rEEParseVector, rnUnusedId, pParser, rDoc )
+{
+}
+
+ScHTMLGlobalTable::~ScHTMLGlobalTable()
+{
+}
+
+void ScHTMLGlobalTable::Recalc()
+{
+ // Fills up empty cells with a dummy entry. */
+ FillEmptyCells();
+ // recalc table sizes of all nested tables and this table
+ RecalcDocSize();
+ // recalc document positions of all entries in this table and in nested tables
+ RecalcDocPos( GetDocPos() );
+}
+
+ScHTMLQueryParser::ScHTMLQueryParser( EditEngine* pEditEngine, ScDocument* pDoc ) :
+ ScHTMLParser( pEditEngine, pDoc ),
+ mnUnusedId( SC_HTML_GLOBAL_TABLE ),
+ mbTitleOn( false )
+{
+ mxGlobTable.reset(
+ new ScHTMLGlobalTable(*pPool, *pEdit, maList, mnUnusedId, this, *pDoc));
+ mpCurrTable = mxGlobTable.get();
+}
+
+ScHTMLQueryParser::~ScHTMLQueryParser()
+{
+}
+
+ErrCode ScHTMLQueryParser::Read( SvStream& rStrm, const OUString& rBaseURL )
+{
+ SvKeyValueIteratorRef xValues;
+ SvKeyValueIterator* pAttributes = nullptr;
+
+ SfxObjectShell* pObjSh = mpDoc->GetDocumentShell();
+ if( pObjSh && pObjSh->IsLoading() )
+ {
+ pAttributes = pObjSh->GetHeaderAttributes();
+ }
+ else
+ {
+ /* When not loading, set up fake HTTP headers to force the SfxHTMLParser
+ to use UTF8 (used when pasting from clipboard) */
+ const char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 );
+ if( pCharSet )
+ {
+ OUString aContentType = "text/html; charset=" +
+ OUString::createFromAscii( pCharSet );
+
+ xValues = new SvKeyValueIterator;
+ xValues->Append( SvKeyValue( OOO_STRING_SVTOOLS_HTML_META_content_type, aContentType ) );
+ pAttributes = xValues.get();
+ }
+ }
+
+ Link<HtmlImportInfo&,void> aOldLink = pEdit->GetHtmlImportHdl();
+ pEdit->SetHtmlImportHdl( LINK( this, ScHTMLQueryParser, HTMLImportHdl ) );
+ ErrCode nErr = pEdit->Read( rStrm, rBaseURL, EETextFormat::Html, pAttributes );
+ pEdit->SetHtmlImportHdl( aOldLink );
+
+ mxGlobTable->Recalc();
+ nColMax = static_cast< SCCOL >( mxGlobTable->GetDocSize( tdCol ) - 1 );
+ nRowMax = static_cast< SCROW >( mxGlobTable->GetDocSize( tdRow ) - 1 );
+
+ return nErr;
+}
+
+const ScHTMLTable* ScHTMLQueryParser::GetGlobalTable() const
+{
+ return mxGlobTable.get();
+}
+
+void ScHTMLQueryParser::ProcessToken( const HtmlImportInfo& rInfo )
+{
+ switch( rInfo.nToken )
+ {
+// --- meta data ---
+ case HtmlTokenId::META: MetaOn( rInfo ); break; // <meta>
+
+// --- title handling ---
+ case HtmlTokenId::TITLE_ON: TitleOn(); break; // <title>
+ case HtmlTokenId::TITLE_OFF: TitleOff( rInfo ); break; // </title>
+
+ case HtmlTokenId::STYLE_ON: break;
+ case HtmlTokenId::STYLE_OFF: ParseStyle(rInfo.aText); break;
+
+// --- body handling ---
+ case HtmlTokenId::BODY_ON: mpCurrTable->BodyOn( rInfo ); break; // <body>
+ case HtmlTokenId::BODY_OFF: mpCurrTable->BodyOff( rInfo ); break; // </body>
+
+// --- insert text ---
+ case HtmlTokenId::TEXTTOKEN: InsertText( rInfo ); break; // any text
+ case HtmlTokenId::LINEBREAK: mpCurrTable->BreakOn(); break; // <br>
+ case HtmlTokenId::HEAD1_ON: // <h1>
+ case HtmlTokenId::HEAD2_ON: // <h2>
+ case HtmlTokenId::HEAD3_ON: // <h3>
+ case HtmlTokenId::HEAD4_ON: // <h4>
+ case HtmlTokenId::HEAD5_ON: // <h5>
+ case HtmlTokenId::HEAD6_ON: // <h6>
+ case HtmlTokenId::PARABREAK_ON: mpCurrTable->HeadingOn(); break; // <p>
+
+// --- misc. contents ---
+ case HtmlTokenId::ANCHOR_ON: mpCurrTable->AnchorOn(); break; // <a>
+
+// --- table handling ---
+ case HtmlTokenId::TABLE_ON: TableOn( rInfo ); break; // <table>
+ case HtmlTokenId::TABLE_OFF: TableOff( rInfo ); break; // </table>
+ case HtmlTokenId::CAPTION_ON: mpCurrTable->CaptionOn(); break; // <caption>
+ case HtmlTokenId::CAPTION_OFF: mpCurrTable->CaptionOff(); break; // </caption>
+ case HtmlTokenId::TABLEROW_ON: mpCurrTable->RowOn( rInfo ); break; // <tr>
+ case HtmlTokenId::TABLEROW_OFF: mpCurrTable->RowOff( rInfo ); break; // </tr>
+ case HtmlTokenId::TABLEHEADER_ON: // <th>
+ case HtmlTokenId::TABLEDATA_ON: mpCurrTable->DataOn( rInfo ); break; // <td>
+ case HtmlTokenId::TABLEHEADER_OFF: // </th>
+ case HtmlTokenId::TABLEDATA_OFF: mpCurrTable->DataOff( rInfo ); break; // </td>
+ case HtmlTokenId::PREFORMTXT_ON: PreOn( rInfo ); break; // <pre>
+ case HtmlTokenId::PREFORMTXT_OFF: PreOff( rInfo ); break; // </pre>
+
+// --- formatting ---
+ case HtmlTokenId::FONT_ON: FontOn( rInfo ); break; // <font>
+
+ case HtmlTokenId::BIGPRINT_ON: // <big>
+ //! TODO: store current font size, use following size
+ mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 3 ], 100, ATTR_FONT_HEIGHT ) );
+ break;
+ case HtmlTokenId::SMALLPRINT_ON: // <small>
+ //! TODO: store current font size, use preceding size
+ mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ 0 ], 100, ATTR_FONT_HEIGHT ) );
+ break;
+
+ case HtmlTokenId::BOLD_ON: // <b>
+ case HtmlTokenId::STRONG_ON: // <strong>
+ mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ break;
+
+ case HtmlTokenId::ITALIC_ON: // <i>
+ case HtmlTokenId::EMPHASIS_ON: // <em>
+ case HtmlTokenId::ADDRESS_ON: // <address>
+ case HtmlTokenId::BLOCKQUOTE_ON: // <blockquote>
+ case HtmlTokenId::BLOCKQUOTE30_ON: // <bq>
+ case HtmlTokenId::CITATION_ON: // <cite>
+ case HtmlTokenId::VARIABLE_ON: // <var>
+ mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
+ break;
+
+ case HtmlTokenId::DEFINSTANCE_ON: // <dfn>
+ mpCurrTable->PutItem( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) );
+ mpCurrTable->PutItem( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
+ break;
+
+ case HtmlTokenId::UNDERLINE_ON: // <u>
+ mpCurrTable->PutItem( SvxUnderlineItem( LINESTYLE_SINGLE, ATTR_FONT_UNDERLINE ) );
+ break;
+ default: break;
+ }
+}
+
+void ScHTMLQueryParser::InsertText( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable->PutText( rInfo );
+ if( mbTitleOn )
+ maTitle.append(rInfo.aText);
+}
+
+void ScHTMLQueryParser::FontOn( const HtmlImportInfo& rInfo )
+{
+ const HTMLOptions& rOptions = static_cast<HTMLParser*>(rInfo.pParser)->GetOptions();
+ for (const auto& rOption : rOptions)
+ {
+ switch( rOption.GetToken() )
+ {
+ case HtmlOptionId::FACE :
+ {
+ const OUString& rFace = rOption.GetString();
+ OUString aFontName;
+ sal_Int32 nPos = 0;
+ while( nPos != -1 )
+ {
+ // font list separator: VCL = ';' HTML = ','
+ std::u16string_view aFName = comphelper::string::strip(o3tl::getToken(rFace, 0, ',', nPos), ' ');
+ aFontName = ScGlobal::addToken(aFontName, aFName, ';');
+ }
+ if ( !aFontName.isEmpty() )
+ mpCurrTable->PutItem( SvxFontItem( FAMILY_DONTKNOW,
+ aFontName, OUString(), PITCH_DONTKNOW,
+ RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
+ }
+ break;
+ case HtmlOptionId::SIZE :
+ {
+ sal_uInt32 nSize = getLimitedValue< sal_uInt32 >( rOption.GetNumber(), 1, SC_HTML_FONTSIZES );
+ mpCurrTable->PutItem( SvxFontHeightItem( maFontHeights[ nSize - 1 ], 100, ATTR_FONT_HEIGHT ) );
+ }
+ break;
+ case HtmlOptionId::COLOR :
+ {
+ Color aColor;
+ rOption.GetColor( aColor );
+ mpCurrTable->PutItem( SvxColorItem( aColor, ATTR_FONT_COLOR ) );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void ScHTMLQueryParser::MetaOn( const HtmlImportInfo& rInfo )
+{
+ if( mpDoc->GetDocumentShell() )
+ {
+ HTMLParser* pParser = static_cast< HTMLParser* >( rInfo.pParser );
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
+ pParser->ParseMetaOptions(
+ xDPS->getDocumentProperties(),
+ mpDoc->GetDocumentShell()->GetHeaderAttributes() );
+ }
+}
+
+void ScHTMLQueryParser::TitleOn()
+{
+ mbTitleOn = true;
+ maTitle.setLength(0);
+}
+
+void ScHTMLQueryParser::TitleOff( const HtmlImportInfo& rInfo )
+{
+ if( !mbTitleOn )
+ return;
+
+ OUString aTitle = maTitle.makeStringAndClear().trim();
+ if (!aTitle.isEmpty() && mpDoc->GetDocumentShell())
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ mpDoc->GetDocumentShell()->GetModel(), uno::UNO_QUERY_THROW);
+
+ xDPS->getDocumentProperties()->setTitle(aTitle);
+ }
+ InsertText( rInfo );
+ mbTitleOn = false;
+}
+
+void ScHTMLQueryParser::TableOn( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->TableOn( rInfo );
+}
+
+void ScHTMLQueryParser::TableOff( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->TableOff( rInfo );
+}
+
+void ScHTMLQueryParser::PreOn( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->PreOn( rInfo );
+}
+
+void ScHTMLQueryParser::PreOff( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->PreOff( rInfo );
+}
+
+void ScHTMLQueryParser::CloseTable( const HtmlImportInfo& rInfo )
+{
+ mpCurrTable = mpCurrTable->CloseTable( rInfo );
+}
+
+namespace {
+
+/**
+ * Handler class for the CSS parser.
+ */
+class CSSHandler: public orcus::css_handler
+{
+ struct MemStr
+ {
+ const char* mp;
+ size_t mn;
+
+ MemStr() : mp(nullptr), mn(0) {}
+ MemStr(const char* p, size_t n) : mp(p), mn(n) {}
+ MemStr(const MemStr& r) : mp(r.mp), mn(r.mn) {}
+ MemStr& operator=(const MemStr& r) = default;
+ };
+
+ typedef std::pair<MemStr, MemStr> SelectorName; // element : class
+ typedef std::vector<SelectorName> SelectorNames;
+
+ SelectorNames maSelectorNames; // current selector names
+ MemStr maPropName; // current property name.
+ MemStr maPropValue; // current property value.
+ ScHTMLStyles& mrStyles;
+
+public:
+ explicit CSSHandler(ScHTMLStyles& rStyles):
+ maPropName(),
+ maPropValue(),
+ mrStyles(rStyles)
+ {}
+
+ // selector name starting with "@"
+ static void at_rule_name(const char* /*p*/, size_t /*n*/)
+ {
+ // TODO: For now, we ignore at-rule properties
+ }
+
+ // selector name not starting with "." or "#" (i.e. element selectors)
+ void simple_selector_type(const char* pElem, size_t nElem)
+ {
+ MemStr aElem(pElem, nElem); // element given
+ MemStr aClass(nullptr, 0); // class name not given - to be added in the "element global" storage
+ SelectorName aName(aElem, aClass);
+
+ maSelectorNames.push_back(aName);
+ }
+
+ // selector names starting with a "." (i.e. class selector)
+ void simple_selector_class(const char* pClassName, size_t nClassName)
+ {
+ MemStr aElem(nullptr, 0); // no element given - should be added in the "global" storage
+ MemStr aClass(pClassName, nClassName);
+ SelectorName aName(aElem, aClass);
+
+ maSelectorNames.push_back(aName);
+ }
+
+ // TODO: Add other selectors
+
+ void property_name(const char* p, size_t n)
+ {
+ maPropName = MemStr(p, n);
+ }
+
+ void value(const char* p, size_t n)
+ {
+ maPropValue = MemStr(p, n);
+ }
+
+ void end_block() {
+ maSelectorNames.clear();
+ }
+
+ void end_property()
+ {
+ SelectorNames::const_iterator itr = maSelectorNames.begin(), itrEnd = maSelectorNames.end();
+ for (; itr != itrEnd; ++itr)
+ {
+ // Add this property to the collection for each selector.
+ const SelectorName& rSelName = *itr;
+ const MemStr& rElem = rSelName.first;
+ const MemStr& rClass = rSelName.second;
+ OUString aName(maPropName.mp, maPropName.mn, RTL_TEXTENCODING_UTF8);
+ OUString aValue(maPropValue.mp, maPropValue.mn, RTL_TEXTENCODING_UTF8);
+ mrStyles.add(rElem.mp, rElem.mn, rClass.mp, rClass.mn, aName, aValue);
+ }
+ maPropName = MemStr();
+ maPropValue = MemStr();
+ }
+
+};
+
+}
+
+void ScHTMLQueryParser::ParseStyle(std::u16string_view rStrm)
+{
+ OString aStr = OUStringToOString(rStrm, RTL_TEXTENCODING_UTF8);
+ CSSHandler aHdl(GetStyles());
+ orcus::css_parser<CSSHandler> aParser(aStr.getStr(), aStr.getLength(), aHdl);
+ try
+ {
+ aParser.parse();
+ }
+ catch (const orcus::css::parse_error& rOrcusParseError)
+ {
+ SAL_WARN("sc", "ScHTMLQueryParser::ParseStyle: " << rOrcusParseError.what());
+ // TODO: Parsing of CSS failed. Do nothing for now.
+ }
+}
+
+IMPL_LINK( ScHTMLQueryParser, HTMLImportHdl, HtmlImportInfo&, rInfo, void )
+{
+ switch( rInfo.eState )
+ {
+ case HtmlImportState::Start:
+ break;
+
+ case HtmlImportState::NextToken:
+ ProcessToken( rInfo );
+ break;
+
+ case HtmlImportState::InsertPara:
+ mpCurrTable->InsertPara( rInfo );
+ break;
+
+ case HtmlImportState::SetAttr:
+ case HtmlImportState::InsertText:
+ case HtmlImportState::InsertField:
+ break;
+
+ case HtmlImportState::End:
+ while( mpCurrTable->GetTableId() != SC_HTML_GLOBAL_TABLE )
+ CloseTable( rInfo );
+ break;
+
+ default:
+ OSL_FAIL( "ScHTMLQueryParser::HTMLImportHdl - unknown ImportInfo::eState" );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/importfilterdata.cxx b/sc/source/filter/importfilterdata.cxx
new file mode 100644
index 000000000..100188928
--- /dev/null
+++ b/sc/source/filter/importfilterdata.cxx
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <importfilterdata.hxx>
+
+namespace sc {
+
+ImportPostProcessData::DataStream::DataStream() :
+ mbRefreshOnEmpty(false), meInsertPos(InsertBottom) {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/SparklineFragment.hxx b/sc/source/filter/inc/SparklineFragment.hxx
new file mode 100644
index 000000000..0d4e76e6b
--- /dev/null
+++ b/sc/source/filter/inc/SparklineFragment.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include <oox/core/contexthandler.hxx>
+#include <SparklineGroup.hxx>
+
+#include <vector>
+#include <memory>
+
+namespace oox
+{
+class AttributeList;
+}
+
+namespace oox::xls
+{
+/** Transitional sparkline data */
+class Sparkline
+{
+public:
+ ScRangeList m_aInputRange;
+ ScRangeList m_aTargetRange;
+ Sparkline() {}
+};
+
+/** Transitional sparkline group data */
+class SparklineGroup
+{
+private:
+ std::vector<Sparkline> m_aSparklines;
+
+ std::shared_ptr<sc::SparklineGroup> m_pSparklineGroup;
+
+public:
+ SparklineGroup()
+ : m_pSparklineGroup(new sc::SparklineGroup)
+ {
+ }
+
+ const std::shared_ptr<sc::SparklineGroup>& getSparklineGroup() { return m_pSparklineGroup; }
+
+ std::vector<Sparkline>& getSparklines() { return m_aSparklines; }
+};
+
+/** Handle import of the sparkline, sparkline group and attributes */
+class SparklineGroupsContext : public WorksheetContextBase
+{
+private:
+ std::vector<SparklineGroup> m_aSparklineGroups;
+
+public:
+ explicit SparklineGroupsContext(WorksheetContextBase& rFragment);
+
+ oox::core::ContextHandlerRef onCreateContext(sal_Int32 nElement,
+ const AttributeList& rAttribs) override;
+ void onStartElement(const AttributeList& rAttribs) override;
+ void onCharacters(const OUString& rCharacters) override;
+ void onEndElement() override;
+
+ void insertSparkline(SparklineGroup& rSparklineGroup, Sparkline& rSparkline);
+};
+
+} //namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/XclExpChangeTrack.hxx b/sc/source/filter/inc/XclExpChangeTrack.hxx
new file mode 100644
index 000000000..48e34e2e3
--- /dev/null
+++ b/sc/source/filter/inc/XclExpChangeTrack.hxx
@@ -0,0 +1,610 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <stack>
+#include <tools/datetime.hxx>
+#include <chgtrack.hxx>
+#include <document.hxx>
+#include "xelink.hxx"
+#include "xestring.hxx"
+#include "excrecds.hxx"
+#include "xlformula.hxx"
+#include "xeformula.hxx"
+
+class ExcXmlRecord : public ExcRecord
+{
+public:
+ virtual std::size_t GetLen() const override;
+ virtual sal_uInt16 GetNum() const override;
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// XclExpUserBView - one UserBView record for each user
+
+class XclExpUserBView : public ExcRecord
+{
+private:
+ XclExpString sUsername;
+ sal_uInt8 aGUID[ 16 ];
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpUserBView( const OUString& rUsername, const sal_uInt8* pGUID );
+
+ const sal_uInt8* GetGUID() const { return aGUID; }
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// XclExpUserBViewList - list of UserBView records
+
+class XclExpUserBViewList : public ExcEmptyRec
+{
+private:
+ std::vector<XclExpUserBView> aViews;
+
+public:
+
+ typedef std::vector<XclExpUserBView>::const_iterator const_iterator;
+
+ XclExpUserBViewList( const ScChangeTrack& rChangeTrack );
+ virtual ~XclExpUserBViewList() override;
+
+ const_iterator cbegin () { return aViews.cbegin(); }
+ const_iterator cend () { return aViews.cend(); }
+
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// XclExpUsersViewBegin - begin of view block (one per sheet)
+
+class XclExpUsersViewBegin : public ExcRecord
+{
+private:
+ sal_uInt8 aGUID[ 16 ];
+ sal_uInt32 nCurrTab;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpUsersViewBegin( const sal_uInt8* pGUID, sal_uInt32 nTab );
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// XclExpUsersViewEnd - end of view block (one per sheet)
+
+class XclExpUsersViewEnd : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "User Names" stream
+
+class XclExpChTr0x0191 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "User Names" stream
+
+class XclExpChTr0x0198 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "User Names" stream
+
+class XclExpChTr0x0192 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "User Names" stream
+
+class XclExpChTr0x0197 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record without content
+
+class XclExpChTrEmpty : public ExcRecord
+{
+private:
+ sal_uInt16 nRecNum;
+
+public:
+ XclExpChTrEmpty( sal_uInt16 nNum ) : nRecNum( nNum ) {}
+ virtual ~XclExpChTrEmpty() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "Revision Log" stream
+
+class XclExpChTr0x0195 : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ virtual ~XclExpChTr0x0195() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// dummy record for "Revision Log" stream
+
+class XclExpChTr0x0194 : public ExcRecord
+{
+private:
+ XclExpString sUsername;
+ DateTime aDateTime;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ inline XclExpChTr0x0194( const ScChangeTrack& rChangeTrack );
+ virtual ~XclExpChTr0x0194() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+inline XclExpChTr0x0194::XclExpChTr0x0194( const ScChangeTrack& rChangeTrack ) :
+ sUsername( rChangeTrack.GetUser() ),
+ aDateTime( rChangeTrack.GetFixDateTime() )
+{
+}
+
+// XclExpChTrHeader - header record, includes action count
+
+class XclExpChTrHeader : public ExcRecord
+{
+private:
+ sal_uInt8 aGUID[ 16 ];
+ sal_uInt32 nCount;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpChTrHeader() : nCount( 0 ) {}
+ virtual ~XclExpChTrHeader() override;
+
+ void SetGUID( const sal_uInt8* pGUID ) { memcpy( aGUID, pGUID, 16 ); }
+ void SetCount( sal_uInt32 nNew ) { nCount = nNew; }
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpXmlChTrHeaders : public ExcXmlRecord
+{
+ sal_uInt8 maGUID[16];
+public:
+ void SetGUID( const sal_uInt8* pGUID );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpChTrTabIdBuffer;
+class XclExpChTrAction;
+
+class XclExpXmlChTrHeader : public ExcXmlRecord
+{
+ OUString maUserName;
+ DateTime maDateTime;
+ sal_uInt8 maGUID[16];
+ sal_Int32 mnLogNumber;
+ sal_uInt32 mnMinAction;
+ sal_uInt32 mnMaxAction;
+
+ std::vector<sal_uInt16> maTabBuffer;
+ std::vector<std::unique_ptr<XclExpChTrAction>> maActions;
+
+public:
+ XclExpXmlChTrHeader(
+ const OUString& rUserName, const DateTime& rDateTime, const sal_uInt8* pGUID,
+ sal_Int32 nLogNumber, const XclExpChTrTabIdBuffer& rBuf );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ void AppendAction( std::unique_ptr<XclExpChTrAction> pAction );
+};
+
+// XclExpChTrInfo - header of action group of a user
+
+class XclExpChTrInfo : public ExcRecord
+{
+private:
+ XclExpString sUsername;
+ DateTime aDateTime;
+ sal_uInt8 aGUID[ 16 ];
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime,
+ const sal_uInt8* pGUID );
+
+ virtual ~XclExpChTrInfo() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// XclExpChTrTabIdBuffer - buffer for tab id's
+
+class XclExpChTrTabIdBuffer
+{
+private:
+ std::unique_ptr<sal_uInt16[]>
+ pBuffer;
+ sal_uInt16* pLast;
+ sal_uInt16 nBufSize;
+ sal_uInt16 nLastId;
+
+public:
+ XclExpChTrTabIdBuffer( sal_uInt16 nCount );
+ XclExpChTrTabIdBuffer( const XclExpChTrTabIdBuffer& rCopy );
+ ~XclExpChTrTabIdBuffer();
+
+ void InitFill( sal_uInt16 nIndex );
+ void InitFillup();
+
+ sal_uInt16 GetId( sal_uInt16 nIndex ) const;
+ void Remove();
+
+ sal_uInt16 GetBufferCount() const
+ { return static_cast< sal_uInt16 >( (pLast - pBuffer.get()) + 1 ); }
+ void GetBufferCopy( sal_uInt16* pDest ) const
+ { memcpy( pDest, pBuffer.get(), sizeof(sal_uInt16) * GetBufferCount() ); }
+};
+
+// XclExpChTrTabId - tab id record
+
+class XclExpChTrTabId : public ExcRecord
+{
+private:
+ std::unique_ptr<sal_uInt16[]> pBuffer;
+ sal_uInt16 nTabCount;
+
+ void Clear() { pBuffer.reset(); }
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ XclExpChTrTabId( sal_uInt16 nCount ) : nTabCount( nCount ) {}
+ XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer );
+ virtual ~XclExpChTrTabId() override;
+
+ void Copy( const XclExpChTrTabIdBuffer& rBuffer );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// XclExpChTrAction - base class for action records
+
+class XclExpChTrAction : public ExcRecord
+{
+private:
+ OUString sUsername;
+ DateTime aDateTime;
+ sal_uInt32 nIndex; // action number
+ std::unique_ptr<XclExpChTrAction>
+ pAddAction; // additional record for this action
+ bool bAccepted;
+
+protected:
+ const XclExpTabInfo& rTabInfo; // for table num export (sc num -> xcl num)
+ const XclExpChTrTabIdBuffer& rIdBuffer; // for table num export (xcl num -> tab id)
+ sal_uInt32 nLength; // this is not the record size
+ sal_uInt16 nOpCode; // EXC_CHTR_OP_***
+ bool bForceInfo;
+
+ XclExpChTrAction( const XclExpChTrAction& rCopy );
+
+ void SetAddAction( XclExpChTrAction* pAction );
+ void AddDependentContents(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const ScChangeTrack& rChangeTrack );
+
+ static inline void Write2DAddress( XclExpStream& rStrm, const ScAddress& rAddress );
+ static inline void Write2DRange( XclExpStream& rStrm, const ScRange& rRange );
+ inline sal_uInt16 GetTabId( SCTAB nTabId ) const;
+ inline void WriteTabId( XclExpStream& rStrm, SCTAB nTabId ) const;
+
+ // save header data, call SaveActionData()
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+ static std::size_t GetHeaderByteCount() { return 12; }
+
+ // override to save action data without header, called by SaveCont()
+ virtual void SaveActionData( XclExpStream& rStrm ) const = 0;
+ // override to get action size without header, called by GetLen()
+ virtual std::size_t GetActionByteCount() const = 0;
+
+ // do something before writing the record
+ virtual void PrepareSaveAction( XclExpStream& rStrm ) const;
+ // do something after writing the record
+ virtual void CompleteSaveAction( XclExpStream& rStrm ) const;
+
+ bool GetAccepted() const { return bAccepted; }
+
+public:
+ XclExpChTrAction(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ sal_uInt16 nNewOpCode = EXC_CHTR_OP_UNKNOWN );
+ virtual ~XclExpChTrAction() override;
+
+ const OUString& GetUsername() const { return sUsername; }
+ const DateTime& GetDateTime() const { return aDateTime; }
+ const XclExpChTrTabIdBuffer& GetTabIdBuffer() const { return rIdBuffer; }
+ bool ForceInfoRecord() const { return bForceInfo; }
+
+ // set own index & return new index
+ // could override to use more indexes per action
+ void SetIndex( sal_uInt32& rIndex );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual std::size_t GetLen() const override;
+
+ XclExpChTrAction* GetAddAction() { return pAddAction.get(); }
+ sal_uInt32 GetActionNumber() const { return nIndex; }
+};
+
+inline void XclExpChTrAction::Write2DAddress( XclExpStream& rStrm, const ScAddress& rAddress )
+{
+ rStrm << static_cast<sal_uInt16>(rAddress.Row())
+ << static_cast<sal_uInt16>(rAddress.Col());
+}
+
+inline void XclExpChTrAction::Write2DRange( XclExpStream& rStrm, const ScRange& rRange )
+{
+ rStrm << static_cast<sal_uInt16>(rRange.aStart.Row())
+ << static_cast<sal_uInt16>(rRange.aEnd.Row())
+ << static_cast<sal_uInt16>(rRange.aStart.Col())
+ << static_cast<sal_uInt16>(rRange.aEnd.Col());
+}
+
+inline sal_uInt16 XclExpChTrAction::GetTabId( SCTAB nTab ) const
+{
+ return rIdBuffer.GetId( rTabInfo.GetXclTab( nTab ) );
+}
+
+inline void XclExpChTrAction::WriteTabId( XclExpStream& rStrm, SCTAB nTab ) const
+{
+ rStrm << GetTabId( nTab );
+}
+
+// XclExpChTrData - cell content itself
+
+struct XclExpChTrData
+{
+ std::unique_ptr<XclExpString> pString;
+ XclExpStringRef mpFormattedString;
+ const ScFormulaCell* mpFormulaCell;
+ XclTokenArrayRef mxTokArr;
+ XclExpRefLog maRefLog;
+ double fValue;
+ sal_Int32 nRKValue;
+ sal_uInt16 nType;
+ std::size_t nSize;
+
+ XclExpChTrData();
+ ~XclExpChTrData();
+ void Clear();
+
+ void WriteFormula(
+ XclExpStream& rStrm,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer );
+ void Write(
+ XclExpStream& rStrm,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer );
+};
+
+// XclExpChTrCellContent - changed cell content
+
+class XclExpChTrCellContent final : public XclExpChTrAction, protected XclExpRoot
+{
+ std::unique_ptr<XclExpChTrData> pOldData;
+ std::unique_ptr<XclExpChTrData> pNewData;
+ sal_uInt16 nOldLength; // this is not the record size
+ ScAddress aPosition;
+
+ static void MakeEmptyChTrData( std::unique_ptr<XclExpChTrData>& rpData );
+
+ void GetCellData(
+ const XclExpRoot& rRoot, const ScCellValue& rScCell, std::unique_ptr<XclExpChTrData>& rpData,
+ sal_uInt32& rXclLength1, sal_uInt16& rXclLength2 );
+
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTrCellContent(
+ const ScChangeActionContent& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer );
+ virtual ~XclExpChTrCellContent() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChTrInsert - insert/delete columns/rows
+
+class XclExpChTrInsert : public XclExpChTrAction
+{
+ bool mbEndOfList;
+
+protected:
+ ScRange aRange;
+
+ XclExpChTrInsert( const XclExpChTrInsert& rCopy );
+
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+ virtual void PrepareSaveAction( XclExpStream& rStrm ) const override;
+ virtual void CompleteSaveAction( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTrInsert(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ const ScChangeTrack& rChangeTrack );
+ virtual ~XclExpChTrInsert() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChTrInsertTab - insert table
+
+class XclExpChTrInsertTab : public XclExpChTrAction, protected XclExpRoot
+{
+private:
+ SCTAB nTab;
+
+protected:
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTrInsertTab(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer );
+ virtual ~XclExpChTrInsertTab() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChTrMoveRange - move cell range
+
+class XclExpChTrMoveRange final : public XclExpChTrAction
+{
+ ScRange aSourceRange;
+ ScRange aDestRange;
+
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+ virtual void PrepareSaveAction( XclExpStream& rStrm ) const override;
+ virtual void CompleteSaveAction( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTrMoveRange(
+ const ScChangeActionMove& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ const ScChangeTrack& rChangeTrack );
+ virtual ~XclExpChTrMoveRange() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChTr0x019A - additional data for delete action
+
+class XclExpChTr0x014A : public XclExpChTrInsert
+{
+protected:
+ virtual void SaveActionData( XclExpStream& rStrm ) const override;
+
+public:
+ XclExpChTr0x014A( const XclExpChTrInsert& rAction );
+ virtual ~XclExpChTr0x014A() override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetActionByteCount() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// XclExpChangeTrack - exports the "Revision Log" stream
+
+class XclExpChangeTrack : protected XclExpRoot
+{
+ typedef std::vector<std::unique_ptr<ExcRecord>> RecListType;
+ RecListType maRecList; // list of "Revision Log" stream records
+ std::stack<XclExpChTrAction*> aActionStack;
+ XclExpChTrTabIdBuffer* pTabIdBuffer;
+ std::vector<std::unique_ptr<XclExpChTrTabIdBuffer>>
+ maBuffers;
+
+ ScDocumentUniquePtr xTempDoc; // empty document
+
+ ScChangeTrack* CreateTempChangeTrack();
+ void PushActionRecord( const ScChangeAction& rAction );
+
+ bool WriteUserNamesStream();
+
+public:
+ XclExpChangeTrack( const XclExpRoot& rRoot );
+ virtual ~XclExpChangeTrack() override;
+
+ void Write();
+ void WriteXml( XclExpXmlStream& rStrm );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/XclImpChangeTrack.hxx b/sc/source/filter/inc/XclImpChangeTrack.hxx
new file mode 100644
index 000000000..532cc9e32
--- /dev/null
+++ b/sc/source/filter/inc/XclImpChangeTrack.hxx
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xiroot.hxx"
+#include "xistream.hxx"
+#include "excform.hxx"
+
+struct ScCellValue;
+class ScChangeAction;
+class ScChangeTrack;
+class DateTime;
+
+struct XclImpChTrRecHeader
+{
+ sal_uInt32 nSize;
+ sal_uInt32 nIndex;
+ sal_uInt16 nOpCode;
+ sal_uInt16 nAccept;
+};
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclImpChTrRecHeader& rRecHeader )
+{
+ rRecHeader.nSize = rStrm.ReaduInt32();
+ rRecHeader.nIndex = rStrm.ReaduInt32();
+ rRecHeader.nOpCode = rStrm.ReaduInt16();
+ rRecHeader.nAccept = rStrm.ReaduInt16();
+ return rStrm;
+}
+
+class XclImpChangeTrack : protected XclImpRoot
+{
+private:
+ XclImpChTrRecHeader aRecHeader;
+ OUString sOldUsername;
+
+ std::unique_ptr<ScChangeTrack> pChangeTrack;
+ tools::SvRef<SotStorageStream> xInStrm; // input stream
+ std::unique_ptr<XclImpStream> pStrm; // stream import class
+ sal_uInt16 nTabIdCount;
+ bool bGlobExit; // global exit loop
+
+ enum { nmBase, nmFound, nmNested }
+ eNestedMode; // action with nested content actions
+
+ bool FoundNestedMode() { return eNestedMode == nmFound; }
+
+ void DoAcceptRejectAction( ScChangeAction* pAction );
+ void DoAcceptRejectAction( sal_uInt32 nFirst, sal_uInt32 nLast );
+
+ void DoInsertRange( const ScRange& rRange, bool bEndOfList );
+ void DoDeleteRange( const ScRange& rRange );
+
+ inline sal_uInt8 LookAtuInt8();
+ inline void Read2DAddress( ScAddress& rAddress );
+ inline void Read2DRange( ScRange& rRange );
+ SCTAB ReadTabNum();
+ void ReadDateTime( DateTime& rDateTime );
+
+ bool CheckRecord( sal_uInt16 nOpCode );
+
+ void ReadFormula(
+ std::unique_ptr<ScTokenArray>& rpTokenArray,
+ const ScAddress& rPosition );
+ void ReadCell( ScCellValue& rCell, sal_uInt32& rFormat, sal_uInt16 nFlags, const ScAddress& rPosition );
+
+ void ReadChTrInsert(); // 0x0137
+ void ReadChTrInfo(); // 0x0138
+ void ReadChTrCellContent(); // 0x013B
+ void ReadChTrTabId(); // 0x013D
+ void ReadChTrMoveRange(); // 0x0140
+ void ReadChTrInsertTab(); // 0x014D
+ void InitNestedMode(); // 0x014E, 0x0150
+ void ReadNestedRecords();
+ bool EndNestedMode(); // 0x014F, 0x0151
+
+ void ReadRecords();
+
+public:
+ XclImpChangeTrack( const XclImpRoot& rRoot, const XclImpStream& rBookStrm );
+ virtual ~XclImpChangeTrack() override;
+
+ // reads extended 3D ref info following the formulas, returns sc tab nums
+ // ( called by XclImpChTrFmlConverter::Read3DTabReference() )
+ void Read3DTabRefInfo( SCTAB& rFirstTab, SCTAB& rLastTab, ExcelToSc8::ExternalTabInfo& rExtInfo );
+
+ void Apply();
+};
+
+inline sal_uInt8 XclImpChangeTrack::LookAtuInt8()
+{
+ pStrm->PushPosition();
+ sal_uInt8 nValue;
+ nValue = pStrm->ReaduInt8();
+ pStrm->PopPosition();
+ return nValue;
+}
+
+inline void XclImpChangeTrack::Read2DAddress( ScAddress& rAddress )
+{
+ rAddress.SetRow( static_cast<SCROW>(pStrm->ReaduInt16()) );
+ rAddress.SetCol( static_cast<SCCOL>(pStrm->ReaduInt16()) );
+}
+
+inline void XclImpChangeTrack::Read2DRange( ScRange& rRange )
+{
+ rRange.aStart.SetRow( static_cast<SCROW>(pStrm->ReaduInt16()) );
+ rRange.aEnd.SetRow( static_cast<SCROW>(pStrm->ReaduInt16()) );
+ rRange.aStart.SetCol( static_cast<SCCOL>(pStrm->ReaduInt16()) );
+ rRange.aEnd.SetCol( static_cast<SCCOL>(pStrm->ReaduInt16()) );
+}
+
+// derived class for special 3D ref handling
+
+class XclImpChTrFmlConverter : public ExcelToSc8
+{
+private:
+ XclImpChangeTrack& rChangeTrack;
+
+ virtual bool Read3DTabReference( sal_uInt16 nIxti, SCTAB& rFirstTab, SCTAB& rLastTab, ExternalTabInfo& rExtInfo ) override;
+
+public:
+ XclImpChTrFmlConverter( XclImpRoot& rRoot, XclImpChangeTrack& rXclChTr );
+ virtual ~XclImpChTrFmlConverter() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/addressconverter.hxx b/sc/source/filter/inc/addressconverter.hxx
new file mode 100644
index 000000000..af1bc340c
--- /dev/null
+++ b/sc/source/filter/inc/addressconverter.hxx
@@ -0,0 +1,505 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <rangelst.hxx>
+#include "workbookhelper.hxx"
+#include <com/sun/star/uno/Sequence.h>
+
+namespace com::sun::star::table { struct CellRangeAddress; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox {
+namespace xls {
+
+/** A 2D cell address struct for binary filters. */
+struct BinAddress
+{
+ sal_Int32 mnCol;
+ sal_Int32 mnRow;
+
+ explicit BinAddress() : mnCol( 0 ), mnRow( 0 ) {}
+ explicit BinAddress( sal_Int32 nCol, sal_Int32 nRow ) : mnCol( nCol ), mnRow( nRow ) {}
+ explicit BinAddress( const ScAddress& rAddr ) : mnCol( rAddr.Col() ), mnRow( rAddr.Row() ) {}
+
+ void read( SequenceInputStream& rStrm );
+};
+
+inline bool operator<( const BinAddress& rL, const BinAddress& rR )
+{
+ return (rL.mnCol < rR.mnCol) || ((rL.mnCol == rR.mnCol) && (rL.mnRow < rR.mnRow));
+}
+
+inline SequenceInputStream& operator>>( SequenceInputStream& rStrm, BinAddress& orPos )
+{
+ orPos.read( rStrm );
+ return rStrm;
+}
+
+/** A 2D cell range address struct for binary filters. */
+struct BinRange
+{
+ BinAddress maFirst;
+ BinAddress maLast;
+
+ void read( SequenceInputStream& rStrm );
+};
+
+inline SequenceInputStream& operator>>( SequenceInputStream& rStrm, BinRange& orRange )
+{
+ orRange.read( rStrm );
+ return rStrm;
+}
+
+/** A 2D cell range address list for binary filters. */
+class BinRangeList
+{
+public:
+ explicit BinRangeList() : mvRanges() {}
+
+ ::std::vector< BinRange >::const_iterator begin() const { return mvRanges.begin(); }
+ ::std::vector< BinRange >::const_iterator end() const { return mvRanges.end(); }
+
+ void read( SequenceInputStream& rStrm );
+
+private:
+ ::std::vector< BinRange > mvRanges;
+};
+
+inline SequenceInputStream& operator>>( SequenceInputStream& rStrm, BinRangeList& orRanges )
+{
+ orRanges.read( rStrm );
+ return rStrm;
+}
+
+/** Converter for cell addresses and cell ranges for OOXML and BIFF filters.
+ */
+class AddressConverter final : public WorkbookHelper
+{
+public:
+ explicit AddressConverter( const WorkbookHelper& rHelper );
+
+ /** Tries to parse the passed string for a 2d cell address in A1 notation.
+
+ This function accepts all strings that match the regular expression
+ "[a-zA-Z]{1,6}0*[1-9][0-9]{0,8}" (without quotes), i.e. 1 to 6 letters
+ for the column index (translated to 0-based column indexes from 0 to
+ 321,272,405), and 1 to 9 digits for the 1-based row index (translated
+ to 0-based row indexes from 0 to 999,999,998). The row number part may
+ contain leading zeros, they will be ignored. It is up to the caller to
+ handle cell addresses outside of a specific valid range (e.g. the
+ entire spreadsheet).
+
+ @param ornColumn (out-parameter) Returns the converted column index.
+ @param ornRow (out-parameter) returns the converted row index.
+ @param rString The string containing the cell address.
+ @param nStart Start index of string part in rString to be parsed.
+ @param nLength Length of string part in rString to be parsed.
+
+ @return true = Parsed string was valid, returned values can be used.
+ */
+ static bool parseOoxAddress2d(
+ sal_Int32& ornColumn, sal_Int32& ornRow,
+ const OUString& rString,
+ sal_Int32 nStart = 0,
+ sal_Int32 nLength = SAL_MAX_INT32 );
+
+ static bool parseOoxAddress2d(
+ sal_Int32& ornColumn, sal_Int32& ornRow, const char* pStr );
+
+ /** Tries to parse the passed string for a 2d cell range in A1 notation.
+
+ This function accepts all strings that match the regular expression
+ "ADDR(:ADDR)?" (without quotes), where ADDR is a cell address accepted
+ by the parseOoxAddress2d() function of this class. It is up to the
+ caller to handle cell ranges outside of a specific valid range (e.g.
+ the entire spreadsheet).
+
+ @param ornStartColumn (out-parameter) Returns the converted start column index.
+ @param ornStartRow (out-parameter) returns the converted start row index.
+ @param ornEndColumn (out-parameter) Returns the converted end column index.
+ @param ornEndRow (out-parameter) returns the converted end row index.
+ @param rString The string containing the cell address.
+ @param nStart Start index of string part in rString to be parsed.
+
+ @return true = Parsed string was valid, returned values can be used.
+ */
+ static bool parseOoxRange2d(
+ sal_Int32& ornStartColumn, sal_Int32& ornStartRow,
+ sal_Int32& ornEndColumn, sal_Int32& ornEndRow,
+ const OUString& rString,
+ sal_Int32 nStart = 0 );
+
+ /** Returns the biggest valid cell address in the own Calc document. */
+ const ScAddress&
+ getMaxApiAddress() const { return maMaxApiPos; }
+
+ /** Returns the biggest valid cell address in the imported/exported
+ Excel document. */
+ const ScAddress&
+ getMaxXlsAddress() const { return maMaxXlsPos; }
+
+ /** Returns the biggest valid cell address in both Calc and the
+ imported/exported Excel document. */
+ const ScAddress&
+ getMaxAddress() const { return maMaxPos; }
+
+ /** Checks if the passed column index is valid.
+
+ @param nCol The column index to check.
+ @param bTrackOverflow true = Update the internal overflow flag, if the
+ column index is outside of the supported limits.
+ @return true = Passed column index is valid (no index overflow).
+ */
+ bool checkCol( sal_Int32 nCol, bool bTrackOverflow );
+
+ /** Checks if the passed row index is valid.
+
+ @param nRow The row index to check.
+ @param bTrackOverflow true = Update the internal overflow flag, if the
+ row index is outside of the supported limits.
+ @return true = Passed row index is valid (no index overflow).
+ */
+ bool checkRow( sal_Int32 nRow, bool bTrackOverflow );
+
+ /** Checks if the passed sheet index is valid.
+
+ @param nSheet The sheet index to check.
+ @param bTrackOverflow true = Update the internal overflow flag, if the
+ sheet index is outside of the supported limits.
+ @return true = Passed sheet index is valid (no index overflow).
+ */
+ bool checkTab( sal_Int16 nSheet, bool bTrackOverflow );
+
+ /** Checks the passed cell address if it fits into the spreadsheet limits.
+
+ @param rAddress The cell address to be checked.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return true = Passed address is valid (no index overflow).
+ */
+ bool checkCellAddress(
+ const ScAddress& rAddress,
+ bool bTrackOverflow );
+
+ /** Converts the passed string to a single cell address, without checking
+ any sheet limits.
+
+ @param orAddress (out-parameter) Returns the converted cell address.
+ @param rString Cell address string in A1 notation.
+ @param nSheet Sheet index to be inserted into orAddress.
+ @return true = Cell address could be parsed from the passed string.
+ */
+ static bool convertToCellAddressUnchecked(
+ ScAddress& orAddress,
+ const OUString& rString,
+ sal_Int16 nSheet );
+
+ static bool convertToCellAddressUnchecked(
+ ScAddress& orAddress, const char* pStr, sal_Int16 nSheet );
+
+ /** Tries to convert the passed string to a single cell address.
+
+ @param orAddress (out-parameter) Returns the converted cell address.
+ @param rString Cell address string in A1 notation.
+ @param nSheet Sheet index to be inserted into orAddress (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return true = Converted address is valid (no index overflow).
+ */
+ bool convertToCellAddress(
+ ScAddress& orAddress,
+ const OUString& rString,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ bool convertToCellAddress(
+ ScAddress& rAddress,
+ const char* pStr, sal_Int16 nSheet, bool bTrackOverflow );
+
+ /** Returns a valid cell address by moving it into allowed dimensions.
+
+ @param rString Cell address string in A1 notation.
+ @param nSheet Sheet index for the returned address (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return A valid cell address struct. */
+ ScAddress createValidCellAddress(
+ const OUString& rString,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Converts the passed address to a single cell address, without checking
+ any sheet limits.
+
+ @param orAddress (out-parameter) Returns the converted cell address.
+ @param rBinAddress Binary cell address struct.
+ @param nSheet Sheet index to be inserted into orAddress.
+ */
+ static void convertToCellAddressUnchecked(
+ ScAddress& orAddress,
+ const BinAddress& rBinAddress,
+ sal_Int16 nSheet );
+
+ /** Tries to convert the passed address to a single cell address.
+
+ @param orAddress (out-parameter) Returns the converted cell address.
+ @param rBinAddress Binary cell address struct.
+ @param nSheet Sheet index to be inserted into orAddress (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return true = Converted address is valid (no index overflow).
+ */
+ bool convertToCellAddress(
+ ScAddress& orAddress,
+ const BinAddress& rBinAddress,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Returns a valid cell address by moving it into allowed dimensions.
+
+ @param rBinAddress Binary cell address struct.
+ @param nSheet Sheet index for the returned address (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the address is outside of the supported sheet limits.
+ @return A valid cell address struct. */
+ ScAddress createValidCellAddress(
+ const BinAddress& rBinAddress,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Checks the passed cell range if it fits into the spreadsheet limits.
+
+ @param rRange The cell range address to be checked.
+ @param bAllowOverflow true = Allow ranges that start inside the
+ supported sheet limits but may end outside of these limits.
+ false = Do not allow ranges that overflow the supported limits.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the passed range contains cells outside of the supported sheet
+ limits.
+ @return true = Cell range is valid. This function returns also true,
+ if only parts of the range are outside the current sheet limits and
+ such an overflow is allowed via parameter bAllowOverflow. Returns
+ false, if the entire range is outside the sheet limits, or if
+ overflow is not allowed via parameter bAllowOverflow.
+ */
+ bool checkCellRange(
+ const ScRange& rRange,
+ bool bAllowOverflow, bool bTrackOverflow );
+
+ /** Checks the passed cell range, may try to fit it to current sheet limits.
+
+ First, this function reorders the column and row indexes so that the
+ starting indexes are less than or equal to the end indexes. Then,
+ depending on the parameter bAllowOverflow, the range is just checked or
+ cropped to the current sheet limits.
+
+ @param orRange (in-out-parameter) Converts the passed cell range
+ into a valid cell range address. If the passed range contains cells
+ outside the currently supported spreadsheet limits, it will be
+ cropped to these limits.
+ @param bAllowOverflow true = Allow ranges that start inside the
+ supported sheet limits but may end outside of these limits. The
+ cell range returned in orRange will be cropped to these limits.
+ false = Do not allow ranges that overflow the supported limits. The
+ function will return false when the range overflows the sheet limits.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original range contains cells outside of the supported sheet
+ limits.
+ @return true = Converted range address is valid. This function
+ returns also true, if overflowing ranges are allowed via parameter
+ bAllowOverflow and the range has been cropped, but still contains
+ cells inside the current sheet limits. Returns false, if the entire
+ range is outside the sheet limits or overflowing ranges are not
+ allowed via parameter bAllowOverflow.
+ */
+ bool validateCellRange(
+ ScRange& orRange,
+ bool bAllowOverflow, bool bTrackOverflow );
+
+ /** Converts the passed string to a cell range address, without checking
+ any sheet limits.
+
+ @param orRange (out-parameter) Returns the converted range address.
+ @param rString Cell range string in A1 notation.
+ @param nSheet Sheet index to be inserted into orRange.
+ @return true = Range address could be parsed from the passed string.
+ */
+ static bool convertToCellRangeUnchecked(
+ ScRange& orRange,
+ const OUString& rString,
+ sal_Int16 nSheet );
+
+ /** Tries to convert the passed string to a cell range address.
+
+ @param orRange (out-parameter) Returns the converted cell range
+ address. If the original range in the passed string contains cells
+ outside the currently supported spreadsheet limits, and parameter
+ bAllowOverflow is set to true, the range will be cropped to these
+ limits. Example: the range string "A1:ZZ100000" may be converted to
+ the range A1:IV65536.
+ @param rString Cell range string in A1 notation.
+ @param nSheet Sheet index to be inserted into orRange (will be checked).
+ @param bAllowOverflow true = Allow ranges that start inside the
+ supported sheet limits but may end outside of these limits. The
+ cell range returned in orRange will be cropped to these limits.
+ false = Do not allow ranges that overflow the supported limits.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original range contains cells outside of the supported sheet
+ limits.
+ @return true = Converted and returned range is valid. This function
+ returns also true, if overflowing ranges are allowed via parameter
+ bAllowOverflow and the range has been cropped, but still contains
+ cells inside the current sheet limits. Returns false, if the entire
+ range is outside the sheet limits or overflowing ranges are not
+ allowed via parameter bAllowOverflow.
+ */
+ bool convertToCellRange(
+ ScRange& orRange,
+ const OUString& rString,
+ sal_Int16 nSheet,
+ bool bAllowOverflow, bool bTrackOverflow );
+
+ /** Converts the passed range to a cell range address, without checking any
+ sheet limits.
+
+ @param orRange (out-parameter) Returns the converted range address.
+ @param rBinRange Binary cell range struct.
+ @param nSheet Sheet index to be inserted into orRange.
+ */
+ static void convertToCellRangeUnchecked(
+ ScRange& orRange,
+ const BinRange& rBinRange,
+ sal_Int16 nSheet );
+
+ /** Tries to convert the passed range to a cell range address.
+
+ @param orRange (out-parameter) Returns the converted cell range
+ address. If the passed original range contains cells outside the
+ currently supported spreadsheet limits, and parameter bAllowOverflow
+ is set to true, the range will be cropped to these limits.
+ @param rBinRange Binary cell range struct.
+ @param nSheet Sheet index to be inserted into orRange (will be checked).
+ @param bAllowOverflow true = Allow ranges that start inside the
+ supported sheet limits but may end outside of these limits. The
+ cell range returned in orRange will be cropped to these limits.
+ false = Do not allow ranges that overflow the supported limits.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original range contains cells outside of the supported sheet
+ limits.
+ @return true = Converted and returned range is valid. This function
+ returns also true, if overflowing ranges are allowed via parameter
+ bAllowOverflow and the range has been cropped, but still contains
+ cells inside the current sheet limits. Returns false, if the entire
+ range is outside the sheet limits or if overflowing ranges are not
+ allowed via parameter bAllowOverflow.
+ */
+ bool convertToCellRange(
+ ScRange& orRange,
+ const BinRange& rBinRange,
+ sal_Int16 nSheet,
+ bool bAllowOverflow, bool bTrackOverflow );
+
+
+ /** Tries to restrict the passed cell range list to current sheet limits.
+
+ @param orRanges (in-out-parameter) Restricts the cell range addresses
+ in the passed list to the current sheet limits and removes invalid
+ ranges from the list.
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original ranges contain cells outside of the supported sheet
+ limits.
+ */
+ void validateCellRangeList(
+ ScRangeList& orRanges,
+ bool bTrackOverflow );
+
+ /** Tries to convert the passed string to a cell range list.
+
+ @param orRanges (out-parameter) Returns the converted cell range
+ addresses. If a range in the passed string contains cells outside
+ the currently supported spreadsheet limits, it will be cropped to
+ these limits. Example: the range string "A1:ZZ100000" may be
+ converted to the range A1:IV65536. If a range is completely outside
+ the limits, it will be omitted.
+ @param rString Cell range list string in A1 notation, space separated.
+ @param nSheet Sheet index to be inserted into orRanges (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original ranges contain cells outside of the supported sheet
+ limits.
+ */
+ void convertToCellRangeList(
+ ScRangeList& orRanges,
+ const OUString& rString,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Tries to convert the passed range list to a cell range list.
+
+ @param orRanges (out-parameter) Returns the converted cell range
+ addresses. If a range in the passed string contains cells outside
+ the currently supported spreadsheet limits, it will be cropped to
+ these limits. Example: the range string "A1:ZZ100000" may be
+ converted to the range A1:IV65536. If a range is completely outside
+ the limits, it will be omitted.
+ @param rBinRanges List of binary cell range objects.
+ @param nSheet Sheet index to be inserted into orRanges (will be checked).
+ @param bTrackOverflow true = Update the internal overflow flags, if
+ the original ranges contain cells outside of the supported sheet
+ limits.
+ */
+ void convertToCellRangeList(
+ ScRangeList& orRanges,
+ const BinRangeList& rBinRanges,
+ sal_Int16 nSheet,
+ bool bTrackOverflow );
+
+ /** Converts the passed range list to a sequence of cell range addresses.
+
+ @param orRanges List of range objects.
+ @return A uno sequence of cell range addresses as used in API calls.
+ Does not check ranges for supported sheet limits.
+ */
+ static css::uno::Sequence<css::table::CellRangeAddress>
+ toApiSequence(const ScRangeList& orRanges);
+
+ bool isColOverflow() const { return mbColOverflow; }
+ bool isRowOverflow() const { return mbRowOverflow; }
+ bool isTabOverflow() const { return mbTabOverflow; }
+
+private:
+ void initializeMaxPos(
+ sal_Int16 nMaxXlsTab, sal_Int32 nMaxXlsCol, sal_Int32 nMaxXlsRow );
+
+private:
+ ScAddress maMaxApiPos; /// Maximum valid cell address in Calc.
+ ScAddress maMaxXlsPos; /// Maximum valid cell address in Excel.
+ ScAddress maMaxPos; /// Maximum valid cell address in Calc/Excel.
+ bool mbColOverflow; /// Flag for "columns overflow".
+ bool mbRowOverflow; /// Flag for "rows overflow".
+ bool mbTabOverflow; /// Flag for "tables overflow".
+};
+
+} // namespace xls
+} // namespace oox
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/autofilterbuffer.hxx b/sc/source/filter/inc/autofilterbuffer.hxx
new file mode 100644
index 000000000..788be9561
--- /dev/null
+++ b/sc/source/filter/inc/autofilterbuffer.hxx
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/helper/helper.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+#include <com/sun/star/sheet/TableFilterField3.hpp>
+#include <com/sun/star/util/Color.hpp>
+
+namespace com::sun::star {
+ namespace sheet { class XDatabaseRange; }
+ namespace sheet { class XSheetFilterDescriptor3; }
+}
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox {
+namespace xls {
+
+/** Contains UNO API filter settings for a column in a filtered range. */
+struct ApiFilterSettings
+{
+ typedef ::std::vector<css::sheet::TableFilterField3> FilterFieldVector;
+
+ FilterFieldVector maFilterFields; /// List of UNO API filter settings.
+ OptValue< bool > mobNeedsRegExp; /// If set, requires regular expressions to be enabled/disabled.
+
+ explicit ApiFilterSettings();
+
+ void appendField( bool bAnd, sal_Int32 nOperator, double fValue );
+ void appendField( bool bAnd, sal_Int32 nOperator, const OUString& rValue );
+ void appendField( bool bAnd, css::util::Color aColor, bool bIsBackgroundColor );
+ void appendField( bool bAnd, const std::vector<std::pair<OUString, bool>>& rValues );
+};
+
+/** Base class for specific filter settings for a column in a filtered range.
+ */
+class FilterSettingsBase : public WorkbookHelper
+{
+public:
+ explicit FilterSettingsBase( const WorkbookHelper& rHelper );
+
+ /** Derived classes import filter settings from the passed attribute list. */
+ virtual void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Derived classes import filter settings from the passed record. */
+ virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm );
+
+ /** Derived classes return converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport();
+};
+
+
+/** Settings for a discrete filter, specifying a list of values to be shown in
+ the filtered range.
+ */
+class DiscreteFilter final : public FilterSettingsBase
+{
+public:
+ explicit DiscreteFilter( const WorkbookHelper& rHelper );
+
+ /** Imports filter settings from the filters and filter elements. */
+ virtual void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ /** Imports filter settings from the FILTERS and FILTER records. */
+ virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ /** Returns converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport() override;
+
+private:
+
+ std::vector<std::pair<OUString, bool>> maValues; // first->values, second->bDatefFormat
+ sal_Int32 mnCalendarType;
+ bool mbShowBlank;
+};
+
+/** Settings for a top-10 filter. */
+class Top10Filter final : public FilterSettingsBase
+{
+public:
+ explicit Top10Filter( const WorkbookHelper& rHelper );
+
+ /** Imports filter settings from the filters and filter elements. */
+ virtual void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ /** Imports filter settings from the FILTERS and FILTER records. */
+ virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ /** Returns converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport() override;
+
+private:
+ double mfValue; /// Number of items or percentage.
+ bool mbTop; /// True = show top (greatest) items/percentage.
+ bool mbPercent; /// True = percentage, false = number of items.
+};
+
+/** Settings for a color filter. */
+class ColorFilter final : public FilterSettingsBase
+{
+public:
+ explicit ColorFilter(const WorkbookHelper& rHelper);
+
+ /** Imports filter settings from the filters and filter elements. */
+ virtual void importAttribs(sal_Int32 nElement, const AttributeList& rAttribs) override;
+ /** Imports filter settings from the FILTERS and FILTER records. */
+ virtual void importRecord(sal_Int32 nRecId, SequenceInputStream& rStrm) override;
+
+ /** Returns converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport() override;
+
+private:
+ /// Whether we are dealing with the background color (vs. text color)
+ bool mbIsBackgroundColor;
+ /// Style name to retrieve the color from
+ OUString msStyleName;
+};
+
+/** A filter criterion for a custom filter. */
+struct FilterCriterionModel
+{
+ css::uno::Any maValue; /// Comparison operand.
+ sal_Int32 mnOperator; /// Comparison operator.
+ sal_uInt8 mnDataType; /// Operand data type (BIFF only).
+
+ explicit FilterCriterionModel();
+
+ /** Sets the passed BIFF operator constant. */
+ void setBiffOperator( sal_uInt8 nOperator );
+
+ /** Imports the criterion model from the passed BIFF12 stream. */
+ void readBiffData( SequenceInputStream& rStrm );
+};
+
+/** Settings for a custom filter, specifying one or two comparison operators
+ associated with some values.
+ */
+class CustomFilter final : public FilterSettingsBase
+{
+public:
+ explicit CustomFilter( const WorkbookHelper& rHelper );
+
+ /** Imports filter settings from the filters and filter elements. */
+ virtual void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ /** Imports filter settings from the FILTERS and FILTER records. */
+ virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ /** Returns converted UNO API filter settings representing all filter settings. */
+ virtual ApiFilterSettings finalizeImport() override;
+
+private:
+ /** Appends the passed filter criterion, if it contains valid settings. */
+ void appendCriterion( const FilterCriterionModel& rCriterion );
+
+private:
+ typedef ::std::vector< FilterCriterionModel > FilterCriterionVector;
+
+ FilterCriterionVector maCriteria;
+ bool mbAnd;
+};
+
+/** A column in a filtered range. Contains an object with specific filter
+ settings for the cells in the column.
+ */
+class FilterColumn final : public WorkbookHelper
+{
+public:
+ explicit FilterColumn( const WorkbookHelper& rHelper );
+
+ /** Imports auto filter column settings from the filterColumn element. */
+ void importFilterColumn( const AttributeList& rAttribs );
+ /** Imports auto filter column settings from the FILTERCOLUMN record. */
+ void importFilterColumn( SequenceInputStream& rStrm );
+
+ /** Creates and returns the specified filter settings object. */
+ template< typename FilterSettingsType >
+ FilterSettingsBase& createFilterSettings()
+ { mxSettings = std::make_shared<FilterSettingsType>( *this ); return *mxSettings; }
+
+ /** Returns converted UNO API filter settings representing all filter
+ settings of this column. */
+ ApiFilterSettings finalizeImport();
+
+private:
+ std::shared_ptr< FilterSettingsBase >
+ mxSettings;
+ sal_Int32 mnColId;
+ bool mbHiddenButton;
+ bool mbShowButton;
+};
+
+// class SortCondition
+
+class SortCondition final : public WorkbookHelper
+{
+public:
+ explicit SortCondition( const WorkbookHelper& rHelper );
+
+ void importSortCondition( const AttributeList& rAttribs, sal_Int16 nSheet );
+
+ ScRange maRange; // Column/Row that this sort condition applies to.
+ OUString maSortCustomList; // Sort by a custom list.
+ bool mbDescending;
+};
+
+// class AutoFilter
+
+class AutoFilter final : public WorkbookHelper
+{
+public:
+ explicit AutoFilter( const WorkbookHelper& rHelper );
+
+ /** Imports auto filter settings from the autoFilter element. */
+ void importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet );
+ /** Imports auto filter settings from the AUTOFILTER record. */
+ void importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet );
+
+ void importSortState( const AttributeList& rAttribs, sal_Int16 nSheet );
+
+ /** Creates a new auto filter column and stores it internally. */
+ FilterColumn& createFilterColumn();
+
+ SortCondition& createSortCondition();
+
+ /** Applies the filter to the passed filter descriptor. */
+ void finalizeImport( const css::uno::Reference< css::sheet::XDatabaseRange >& rxDatabaseRange,
+ sal_Int16 nSheet );
+
+private:
+ typedef RefVector< FilterColumn > FilterColumnVector;
+
+ FilterColumnVector maFilterColumns;
+ ScRange maRange;
+
+ ScRange maSortRange; // The whole range of data to sort (not just the sort-by column).
+ typedef RefVector< SortCondition > SortConditionVector;
+ SortConditionVector maSortConditions;
+};
+
+class AutoFilterBuffer final : public WorkbookHelper
+{
+public:
+ explicit AutoFilterBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new auto filter and stores it internally. */
+ AutoFilter& createAutoFilter();
+
+ /** Applies filter settings to a new database range object (used for sheet
+ autofilter or advanced filter as specified by built-in defined names). */
+ void finalizeImport( sal_Int16 nSheet );
+
+ /** Applies the filters to the passed database range object.
+ @return True = this buffer contains valid auto filter settings. */
+ bool finalizeImport( const css::uno::Reference< css::sheet::XDatabaseRange >& rxDatabaseRange,
+ sal_Int16 nSheet );
+
+private:
+ /** Returns the auto filter object used to perform auto filtering. */
+ AutoFilter* getActiveAutoFilter();
+
+private:
+ typedef RefVector< AutoFilter > AutoFilterVector;
+ AutoFilterVector maAutoFilters;
+};
+
+} // namespace xls
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/autofiltercontext.hxx b/sc/source/filter/inc/autofiltercontext.hxx
new file mode 100644
index 000000000..f6eadab80
--- /dev/null
+++ b/sc/source/filter/inc/autofiltercontext.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "autofilterbuffer.hxx"
+
+namespace oox::xls {
+
+class AutoFilter;
+class FilterColumn;
+class FilterSettingsBase;
+
+class FilterSettingsContext final : public WorksheetContextBase
+{
+public:
+ explicit FilterSettingsContext( WorksheetContextBase& rParent, FilterSettingsBase& rFilterSettings );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ FilterSettingsBase& mrFilterSettings;
+};
+
+class FilterColumnContext final : public WorksheetContextBase
+{
+public:
+ explicit FilterColumnContext( WorksheetContextBase& rParent, FilterColumn& rFilterColumn );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ FilterColumn& mrFilterColumn;
+};
+
+// class SortConditionContext
+
+class SortConditionContext final : public WorksheetContextBase
+{
+public:
+ explicit SortConditionContext( WorksheetContextBase& rFragment, SortCondition& rSortCondition );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ SortCondition& mrSortCondition;
+};
+
+// class SortStateContext
+
+class SortStateContext final : public WorksheetContextBase
+{
+public:
+ explicit SortStateContext( WorksheetContextBase& rFragment, AutoFilter& rAutoFilter );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ AutoFilter& mrAutoFilter;
+};
+
+// class AutoFilterContext
+
+class AutoFilterContext final : public WorksheetContextBase
+{
+public:
+ explicit AutoFilterContext( WorksheetFragmentBase& rFragment, AutoFilter& rAutoFilter );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ AutoFilter& mrAutoFilter;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/biffhelper.hxx b/sc/source/filter/inc/biffhelper.hxx
new file mode 100644
index 000000000..1cf3ed9c6
--- /dev/null
+++ b/sc/source/filter/inc/biffhelper.hxx
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+// BIFF12 record identifiers ==================================================
+
+const sal_Int32 BIFF12_ID_ARRAY = 0x01AA;
+const sal_Int32 BIFF12_ID_AUTOFILTER = 0x00A1;
+const sal_Int32 BIFF12_ID_AUTOSORTSCOPE = 0x01CB;
+const sal_Int32 BIFF12_ID_BINARYINDEXBLOCK = 0x002A;
+const sal_Int32 BIFF12_ID_BINARYINDEXROWS = 0x0028;
+const sal_Int32 BIFF12_ID_BOOKVIEWS = 0x0087;
+const sal_Int32 BIFF12_ID_BORDER = 0x002E;
+const sal_Int32 BIFF12_ID_BORDERS = 0x0265;
+const sal_Int32 BIFF12_ID_BRK = 0x018C;
+const sal_Int32 BIFF12_ID_CALCPR = 0x009D;
+const sal_Int32 BIFF12_ID_CELL_BLANK = 0x0001;
+const sal_Int32 BIFF12_ID_CELL_BOOL = 0x0004;
+const sal_Int32 BIFF12_ID_CELL_DOUBLE = 0x0005;
+const sal_Int32 BIFF12_ID_CELL_ERROR = 0x0003;
+const sal_Int32 BIFF12_ID_CELL_RK = 0x0002;
+const sal_Int32 BIFF12_ID_CELL_RSTRING = 0x003E;
+const sal_Int32 BIFF12_ID_CELL_SI = 0x0007;
+const sal_Int32 BIFF12_ID_CELL_STRING = 0x0006;
+const sal_Int32 BIFF12_ID_CELLSTYLE = 0x0030;
+const sal_Int32 BIFF12_ID_CELLSTYLES = 0x026B;
+const sal_Int32 BIFF12_ID_CELLSTYLEXFS = 0x0272;
+const sal_Int32 BIFF12_ID_CELLXFS = 0x0269;
+const sal_Int32 BIFF12_ID_CFCOLOR = 0x0234;
+const sal_Int32 BIFF12_ID_CFRULE = 0x01CF;
+const sal_Int32 BIFF12_ID_CHARTPAGESETUP = 0x028C;
+const sal_Int32 BIFF12_ID_CHARTPROTECTION = 0x029D;
+const sal_Int32 BIFF12_ID_CHARTSHEETPR = 0x028B;
+const sal_Int32 BIFF12_ID_CHARTSHEETVIEW = 0x008D;
+const sal_Int32 BIFF12_ID_CHARTSHEETVIEWS = 0x008B;
+const sal_Int32 BIFF12_ID_COL = 0x003C;
+const sal_Int32 BIFF12_ID_COLBREAKS = 0x018A;
+const sal_Int32 BIFF12_ID_COLOR = 0x023C;
+const sal_Int32 BIFF12_ID_COLORS = 0x01D9;
+const sal_Int32 BIFF12_ID_COLORSCALE = 0x01D5;
+const sal_Int32 BIFF12_ID_COLS = 0x0186;
+const sal_Int32 BIFF12_ID_COMMENT = 0x027B;
+const sal_Int32 BIFF12_ID_COMMENTAUTHOR = 0x0278;
+const sal_Int32 BIFF12_ID_COMMENTAUTHORS = 0x0276;
+const sal_Int32 BIFF12_ID_COMMENTLIST = 0x0279;
+const sal_Int32 BIFF12_ID_COMMENTS = 0x0274;
+const sal_Int32 BIFF12_ID_COMMENTTEXT = 0x027D;
+const sal_Int32 BIFF12_ID_CONDFORMATTING = 0x01CD;
+const sal_Int32 BIFF12_ID_CONNECTION = 0x00C9;
+const sal_Int32 BIFF12_ID_CONNECTIONS = 0x01AD;
+const sal_Int32 BIFF12_ID_CONTROL = 0x0284;
+const sal_Int32 BIFF12_ID_CONTROLS = 0x0283;
+const sal_Int32 BIFF12_ID_CUSTOMCHARTVIEW = 0x028F;
+const sal_Int32 BIFF12_ID_CUSTOMCHARTVIEWS = 0x028D;
+const sal_Int32 BIFF12_ID_CUSTOMFILTER = 0x00AE;
+const sal_Int32 BIFF12_ID_CUSTOMFILTERS = 0x00AC;
+const sal_Int32 BIFF12_ID_CUSTOMSHEETVIEW = 0x01A7;
+const sal_Int32 BIFF12_ID_CUSTOMSHEETVIEWS = 0x01A6;
+const sal_Int32 BIFF12_ID_CUSTOMWORKBOOKVIEW= 0x018D;
+const sal_Int32 BIFF12_ID_DATABAR = 0x01D3;
+const sal_Int32 BIFF12_ID_DATATABLE = 0x01AC;
+const sal_Int32 BIFF12_ID_DATAVALIDATION = 0x0040;
+const sal_Int32 BIFF12_ID_DATAVALIDATIONS = 0x023D;
+const sal_Int32 BIFF12_ID_DDEITEMVALUES = 0x0242;
+const sal_Int32 BIFF12_ID_DDEITEM_BOOL = 0x0248;
+const sal_Int32 BIFF12_ID_DDEITEM_DOUBLE = 0x0244;
+const sal_Int32 BIFF12_ID_DDEITEM_ERROR = 0x0245;
+const sal_Int32 BIFF12_ID_DDEITEM_STRING = 0x0246;
+const sal_Int32 BIFF12_ID_DEFINEDNAME = 0x0027;
+const sal_Int32 BIFF12_ID_DIMENSION = 0x0094;
+const sal_Int32 BIFF12_ID_DISCRETEFILTER = 0x00A7;
+const sal_Int32 BIFF12_ID_DISCRETEFILTERS = 0x00A5;
+const sal_Int32 BIFF12_ID_DRAWING = 0x0226;
+const sal_Int32 BIFF12_ID_DXF = 0x01FB;
+const sal_Int32 BIFF12_ID_DXFS = 0x01F9;
+const sal_Int32 BIFF12_ID_EXTCELL_BLANK = 0x016F;
+const sal_Int32 BIFF12_ID_EXTCELL_BOOL = 0x0171;
+const sal_Int32 BIFF12_ID_EXTCELL_DOUBLE = 0x0170;
+const sal_Int32 BIFF12_ID_EXTCELL_ERROR = 0x0172;
+const sal_Int32 BIFF12_ID_EXTCELL_STRING = 0x0173;
+const sal_Int32 BIFF12_ID_EXTERNALADDIN = 0x029B;
+const sal_Int32 BIFF12_ID_EXTERNALBOOK = 0x0168;
+const sal_Int32 BIFF12_ID_EXTERNALNAME = 0x0241;
+const sal_Int32 BIFF12_ID_EXTERNALREF = 0x0163;
+const sal_Int32 BIFF12_ID_EXTERNALREFS = 0x0161;
+const sal_Int32 BIFF12_ID_EXTERNALSELF = 0x0165;
+const sal_Int32 BIFF12_ID_EXTERNALSAME = 0x0166;
+const sal_Int32 BIFF12_ID_EXTERNALSHEETS = 0x016A;
+const sal_Int32 BIFF12_ID_EXTROW = 0x016E;
+const sal_Int32 BIFF12_ID_EXTSHEETDATA = 0x016B;
+const sal_Int32 BIFF12_ID_EXTERNALNAMEFLAGS = 0x024A;
+const sal_Int32 BIFF12_ID_EXTSHEETNAMES = 0x0167;
+const sal_Int32 BIFF12_ID_FILESHARING = 0x0224;
+const sal_Int32 BIFF12_ID_FILEVERSION = 0x0080;
+const sal_Int32 BIFF12_ID_FILL = 0x002D;
+const sal_Int32 BIFF12_ID_FILLS = 0x025B;
+const sal_Int32 BIFF12_ID_FILTERCOLUMN = 0x00A3;
+const sal_Int32 BIFF12_ID_FONT = 0x002B;
+const sal_Int32 BIFF12_ID_FONTS = 0x0263;
+const sal_Int32 BIFF12_ID_FORMULA_STRING = 0x0008;
+const sal_Int32 BIFF12_ID_FORMULA_DOUBLE = 0x0009;
+const sal_Int32 BIFF12_ID_FORMULA_BOOL = 0x000A;
+const sal_Int32 BIFF12_ID_FORMULA_ERROR = 0x000B;
+const sal_Int32 BIFF12_ID_FUNCTIONGROUP = 0x0299;
+const sal_Int32 BIFF12_ID_FUNCTIONGROUPS = 0x0298;
+const sal_Int32 BIFF12_ID_HEADERFOOTER = 0x01DF;
+const sal_Int32 BIFF12_ID_HYPERLINK = 0x01EE;
+const sal_Int32 BIFF12_ID_ICONSET = 0x01D1;
+const sal_Int32 BIFF12_ID_INDEXEDCOLORS = 0x0235;
+const sal_Int32 BIFF12_ID_INPUTCELLS = 0x01F8;
+const sal_Int32 BIFF12_ID_LEGACYDRAWING = 0x0227;
+const sal_Int32 BIFF12_ID_MERGECELL = 0x00B0;
+const sal_Int32 BIFF12_ID_MERGECELLS = 0x00B1;
+const sal_Int32 BIFF12_ID_MRUCOLORS = 0x0239;
+const sal_Int32 BIFF12_ID_MULTCELL_BLANK = 0x000C;
+const sal_Int32 BIFF12_ID_MULTCELL_BOOL = 0x000F;
+const sal_Int32 BIFF12_ID_MULTCELL_DOUBLE = 0x0010;
+const sal_Int32 BIFF12_ID_MULTCELL_ERROR = 0x000E;
+const sal_Int32 BIFF12_ID_MULTCELL_RK = 0x000D;
+const sal_Int32 BIFF12_ID_MULTCELL_RSTRING = 0x003D;
+const sal_Int32 BIFF12_ID_MULTCELL_SI = 0x0012;
+const sal_Int32 BIFF12_ID_MULTCELL_STRING = 0x0011;
+const sal_Int32 BIFF12_ID_NUMFMT = 0x002C;
+const sal_Int32 BIFF12_ID_NUMFMTS = 0x0267;
+const sal_Int32 BIFF12_ID_OLEOBJECT = 0x027F;
+const sal_Int32 BIFF12_ID_OLEOBJECTS = 0x027E;
+const sal_Int32 BIFF12_ID_OLESIZE = 0x0225;
+const sal_Int32 BIFF12_ID_PAGEMARGINS = 0x01DC;
+const sal_Int32 BIFF12_ID_PAGESETUP = 0x01DE;
+const sal_Int32 BIFF12_ID_PANE = 0x0097;
+const sal_Int32 BIFF12_ID_PCDEFINITION = 0x00B3;
+const sal_Int32 BIFF12_ID_PCDFDISCRETEPR = 0x00E1;
+const sal_Int32 BIFF12_ID_PCDFGROUPITEMS = 0x00DD;
+const sal_Int32 BIFF12_ID_PCDFIELD = 0x00B7;
+const sal_Int32 BIFF12_ID_PCDFIELDGROUP = 0x00DB;
+const sal_Int32 BIFF12_ID_PCDFIELDS = 0x00B5;
+const sal_Int32 BIFF12_ID_PCDFRANGEPR = 0x00DF;
+const sal_Int32 BIFF12_ID_PCDFSHAREDITEMS = 0x00BD;
+const sal_Int32 BIFF12_ID_PCDSHEETSOURCE = 0x00BB;
+const sal_Int32 BIFF12_ID_PCDSOURCE = 0x00B9;
+const sal_Int32 BIFF12_ID_PCITEM_ARRAY = 0x00BF;
+const sal_Int32 BIFF12_ID_PCITEM_BOOL = 0x0016;
+const sal_Int32 BIFF12_ID_PCITEM_DATE = 0x0019;
+const sal_Int32 BIFF12_ID_PCITEM_DOUBLE = 0x0015;
+const sal_Int32 BIFF12_ID_PCITEM_ERROR = 0x0017;
+const sal_Int32 BIFF12_ID_PCITEM_INDEX = 0x001A;
+const sal_Int32 BIFF12_ID_PCITEM_MISSING = 0x0014;
+const sal_Int32 BIFF12_ID_PCITEM_STRING = 0x0018;
+const sal_Int32 BIFF12_ID_PCITEMA_BOOL = 0x001D;
+const sal_Int32 BIFF12_ID_PCITEMA_DATE = 0x0020;
+const sal_Int32 BIFF12_ID_PCITEMA_DOUBLE = 0x001C;
+const sal_Int32 BIFF12_ID_PCITEMA_ERROR = 0x001E;
+const sal_Int32 BIFF12_ID_PCITEMA_MISSING = 0x001B;
+const sal_Int32 BIFF12_ID_PCITEMA_STRING = 0x001F;
+const sal_Int32 BIFF12_ID_PCRECORD = 0x0021;
+const sal_Int32 BIFF12_ID_PCRECORDDT = 0x0022;
+const sal_Int32 BIFF12_ID_PCRECORDS = 0x00C1;
+const sal_Int32 BIFF12_ID_PHONETICPR = 0x0219;
+const sal_Int32 BIFF12_ID_PICTURE = 0x0232;
+const sal_Int32 BIFF12_ID_PIVOTAREA = 0x00F7;
+const sal_Int32 BIFF12_ID_PIVOTCACHE = 0x0182;
+const sal_Int32 BIFF12_ID_PIVOTCACHES = 0x0180;
+const sal_Int32 BIFF12_ID_PRINTOPTIONS = 0x01DD;
+const sal_Int32 BIFF12_ID_PTCOLFIELDS = 0x0137;
+const sal_Int32 BIFF12_ID_PTDATAFIELD = 0x0125;
+const sal_Int32 BIFF12_ID_PTDATAFIELDS = 0x0127;
+const sal_Int32 BIFF12_ID_PTDEFINITION = 0x0118;
+const sal_Int32 BIFF12_ID_PTFIELD = 0x011D;
+const sal_Int32 BIFF12_ID_PTFIELDS = 0x011F;
+const sal_Int32 BIFF12_ID_PTFILTER = 0x0259;
+const sal_Int32 BIFF12_ID_PTFILTERS = 0x0257;
+const sal_Int32 BIFF12_ID_PTFITEM = 0x011A;
+const sal_Int32 BIFF12_ID_PTFITEMS = 0x011B;
+const sal_Int32 BIFF12_ID_PTLOCATION = 0x013A;
+const sal_Int32 BIFF12_ID_PTPAGEFIELD = 0x0121;
+const sal_Int32 BIFF12_ID_PTPAGEFIELDS = 0x0123;
+const sal_Int32 BIFF12_ID_PTREFERENCE = 0x00FB;
+const sal_Int32 BIFF12_ID_PTREFERENCEITEM = 0x017E;
+const sal_Int32 BIFF12_ID_PTREFERENCES = 0x00F9;
+const sal_Int32 BIFF12_ID_PTROWFIELDS = 0x0135;
+const sal_Int32 BIFF12_ID_QUERYTABLE = 0x01BF;
+const sal_Int32 BIFF12_ID_QUERYTABLEREFRESH = 0x01C1;
+const sal_Int32 BIFF12_ID_RGBCOLOR = 0x01DB;
+const sal_Int32 BIFF12_ID_ROW = 0x0000;
+const sal_Int32 BIFF12_ID_ROWBREAKS = 0x0188;
+const sal_Int32 BIFF12_ID_SCENARIO = 0x01F6;
+const sal_Int32 BIFF12_ID_SCENARIOS = 0x01F4;
+const sal_Int32 BIFF12_ID_SELECTION = 0x0098;
+const sal_Int32 BIFF12_ID_SHAREDFMLA = 0x01AB;
+const sal_Int32 BIFF12_ID_SHEET = 0x009C;
+const sal_Int32 BIFF12_ID_SHEETDATA = 0x0091;
+const sal_Int32 BIFF12_ID_SHEETFORMATPR = 0x01E5;
+const sal_Int32 BIFF12_ID_SHEETPR = 0x0093;
+const sal_Int32 BIFF12_ID_SHEETPROTECTION = 0x0217;
+const sal_Int32 BIFF12_ID_SHEETS = 0x008F;
+const sal_Int32 BIFF12_ID_SHEETVIEW = 0x0089;
+const sal_Int32 BIFF12_ID_SHEETVIEWS = 0x0085;
+const sal_Int32 BIFF12_ID_SI = 0x0013;
+const sal_Int32 BIFF12_ID_SST = 0x009F;
+const sal_Int32 BIFF12_ID_STYLESHEET = 0x0116;
+const sal_Int32 BIFF12_ID_TABLE = 0x0157;
+const sal_Int32 BIFF12_ID_TABLEPART = 0x0295;
+const sal_Int32 BIFF12_ID_TABLEPARTS = 0x0294;
+const sal_Int32 BIFF12_ID_TABLESTYLEINFO = 0x0201;
+const sal_Int32 BIFF12_ID_TABLESTYLES = 0x01FC;
+const sal_Int32 BIFF12_ID_TOP10FILTER = 0x00AA;
+const sal_Int32 BIFF12_ID_VOLTYPE = 0x0204;
+const sal_Int32 BIFF12_ID_VOLTYPEMAIN = 0x0206;
+const sal_Int32 BIFF12_ID_VOLTYPES = 0x0202;
+const sal_Int32 BIFF12_ID_VOLTYPESTP = 0x020A;
+const sal_Int32 BIFF12_ID_VOLTYPETR = 0x020B;
+const sal_Int32 BIFF12_ID_WEBPR = 0x0105;
+const sal_Int32 BIFF12_ID_WEBPRTABLES = 0x0107;
+const sal_Int32 BIFF12_ID_WORKBOOK = 0x0083;
+const sal_Int32 BIFF12_ID_WORKBOOKPR = 0x0099;
+const sal_Int32 BIFF12_ID_WORKBOOKVIEW = 0x009E;
+const sal_Int32 BIFF12_ID_WORKSHEET = 0x0081;
+const sal_Int32 BIFF12_ID_XF = 0x002F;
+
+// BIFF2-BIFF8 record identifiers =============================================
+
+/** all binary Excel file format types (BIFF types).
+ BIFF2 /// MS Excel 2.1.
+ BIFF3 /// MS Excel 3.0.
+ BIFF4 /// MS Excel 4.0.
+ BIFF5 /// MS Excel 5.0, MS Excel 7.0 (95).
+ BIFF8 /// MS Excel 8.0 (97), 9.0 (2000), 10.0 (XP), 11.0 (2003).
+ BIFF_UNKNOWN /// Unknown BIFF version.
+*/
+
+//const sal_uInt16 BIFF2_MAXRECSIZE = 2080;
+//const sal_uInt16 BIFF8_MAXRECSIZE = 8224;
+
+// record identifiers ---------------------------------------------------------
+
+const sal_uInt16 BIFF2_ID_BOF = 0x0009;
+const sal_uInt16 BIFF3_ID_BOF = 0x0209;
+const sal_uInt16 BIFF4_ID_BOF = 0x0409;
+const sal_uInt16 BIFF5_ID_BOF = 0x0809;
+const sal_uInt16 BIFF_ID_CONT = 0x003C;
+const sal_uInt16 BIFF_ID_EOF = 0x000A;
+const sal_uInt16 BIFF_ID_PCDEFINITION = 0x00C6;
+const sal_uInt16 BIFF_ID_PCDEFINITION2 = 0x0122;
+const sal_uInt16 BIFF_ID_PCDFDISCRETEPR = 0x00D9;
+const sal_uInt16 BIFF_ID_PCDFIELD = 0x00C7;
+const sal_uInt16 BIFF_ID_PCDFRANGEPR = 0x00D8;
+const sal_uInt16 BIFF_ID_PCDFSQLTYPE = 0x01BB;
+const sal_uInt16 BIFF_ID_PCITEM_BOOL = 0x00CA;
+const sal_uInt16 BIFF_ID_PCITEM_DATE = 0x00CE;
+const sal_uInt16 BIFF_ID_PCITEM_DOUBLE = 0x00C9;
+const sal_uInt16 BIFF_ID_PCITEM_ERROR = 0x00CB;
+const sal_uInt16 BIFF_ID_PCITEM_INDEXLIST = 0x00C8;
+const sal_uInt16 BIFF_ID_PCITEM_INTEGER = 0x00CC;
+const sal_uInt16 BIFF_ID_PCITEM_MISSING = 0x00CF;
+const sal_uInt16 BIFF_ID_PCITEM_STRING = 0x00CD;
+
+const sal_uInt16 BIFF_ID_UNKNOWN = SAL_MAX_UINT16;
+
+/* Many of these constants might be unused, but please keep for documentation. If you notice
+ * hardcoded numbers in the code that actually correspond in meaning in the context (not just value)
+ * to one of the named constants, feel free to change it to use the constant instead, of course.
+ */
+const sal_uInt16 BIFF2_ID_ARRAY = 0x0021;
+const sal_uInt16 BIFF3_ID_ARRAY = 0x0221;
+const sal_uInt16 BIFF_ID_AUTOFILTER = 0x009D;
+const sal_uInt16 BIFF2_ID_BLANK = 0x0001;
+const sal_uInt16 BIFF3_ID_BLANK = 0x0201;
+const sal_uInt16 BIFF_ID_BOOKBOOL = 0x00DA;
+const sal_uInt16 BIFF_ID_BOOKEXT = 0x0863;
+const sal_uInt16 BIFF2_ID_BOOLERR = 0x0005;
+const sal_uInt16 BIFF3_ID_BOOLERR = 0x0205;
+const sal_uInt16 BIFF_ID_BOTTOMMARGIN = 0x0029;
+const sal_uInt16 BIFF_ID_CALCCOUNT = 0x000C;
+const sal_uInt16 BIFF_ID_CALCMODE = 0x000D;
+const sal_uInt16 BIFF_ID_CFHEADER = 0x01B0;
+const sal_uInt16 BIFF_ID_CFRULE = 0x01B1;
+const sal_uInt16 BIFF_ID_CFRULE12 = 0x087A;
+const sal_uInt16 BIFF_ID_CFRULEEXT = 0x087B;
+const sal_uInt16 BIFF_ID_CH3DDATAFORMAT = 0x105F;
+const sal_uInt16 BIFF_ID_CHAREA = 0x101A;
+const sal_uInt16 BIFF_ID_CHAREAFORMAT = 0x100A;
+const sal_uInt16 BIFF_ID_CHATTACHEDLABEL = 0x100C;
+const sal_uInt16 BIFF_ID_CHAXESSET = 0x1041;
+const sal_uInt16 BIFF_ID_CHAXIS = 0x101D;
+const sal_uInt16 BIFF_ID_CHAXISLINE = 0x1021;
+const sal_uInt16 BIFF_ID_CHBAR = 0x1017;
+const sal_uInt16 BIFF_ID_CHBEGIN = 0x1033;
+const sal_uInt16 BIFF_ID_CHCHART = 0x1002;
+const sal_uInt16 BIFF_ID_CHCHART3D = 0x103A;
+const sal_uInt16 BIFF_ID_CHCHARTLINE = 0x101C;
+const sal_uInt16 BIFF_ID_CHDATAFORMAT = 0x1006;
+const sal_uInt16 BIFF_ID_CHDATERANGE = 0x1062;
+const sal_uInt16 BIFF_ID_CHDEFAULTTEXT = 0x1024;
+const sal_uInt16 BIFF_ID_CHDROPBAR = 0x103D;
+const sal_uInt16 BIFF_ID_CHECKCOMPAT = 0x088C;
+const sal_uInt16 BIFF_ID_CHEND = 0x1034;
+const sal_uInt16 BIFF_ID_CHESCHERFORMAT = 0x1066;
+const sal_uInt16 BIFF_ID_CHFONT = 0x1026;
+const sal_uInt16 BIFF_ID_CHFORMAT = 0x104E;
+const sal_uInt16 BIFF_ID_CHFORMATRUNS = 0x1050;
+const sal_uInt16 BIFF_ID_CHFRAME = 0x1032;
+const sal_uInt16 BIFF_ID_CHFRAMEPOS = 0x104F;
+const sal_uInt16 BIFF_ID_CHFRBLOCKBEGIN = 0x0852;
+const sal_uInt16 BIFF_ID_CHFRBLOCKEND = 0x0853;
+const sal_uInt16 BIFF_ID_CHFRCATEGORYPROPS = 0x0856;
+const sal_uInt16 BIFF_ID_CHFREXTPROPS = 0x089E;
+const sal_uInt16 BIFF_ID_CHFREXTPROPSCONT = 0x089F;
+const sal_uInt16 BIFF_ID_CHFRINFO = 0x0850;
+const sal_uInt16 BIFF_ID_CHFRLABELPROPS = 0x086B;
+const sal_uInt16 BIFF_ID_CHFRLAYOUT = 0x089D;
+const sal_uInt16 BIFF_ID_CHFRPLOTAREALAYOUT = 0x08A7;
+const sal_uInt16 BIFF_ID_CHFRSHAPEPROPS = 0x08A4;
+const sal_uInt16 BIFF_ID_CHFRTEXTPROPS = 0x08A5;
+const sal_uInt16 BIFF_ID_CHFRUNITPROPS = 0x0857;
+const sal_uInt16 BIFF_ID_CHFRWRAPPER = 0x0851;
+const sal_uInt16 BIFF_ID_CHLABELRANGE = 0x1020;
+const sal_uInt16 BIFF_ID_CHLEGEND = 0x1015;
+const sal_uInt16 BIFF_ID_CHLINE = 0x1018;
+const sal_uInt16 BIFF_ID_CHLINEFORMAT = 0x1007;
+const sal_uInt16 BIFF_ID_CHMARKERFORMAT = 0x1009;
+const sal_uInt16 BIFF_ID_CHOBJECTLINK = 0x1027;
+const sal_uInt16 BIFF_ID_CHPICFORMAT = 0x103C;
+const sal_uInt16 BIFF_ID_CHPIE = 0x1019;
+const sal_uInt16 BIFF_ID_CHPIEEXT = 0x1061;
+const sal_uInt16 BIFF_ID_CHPIEFORMAT = 0x100B;
+const sal_uInt16 BIFF_ID_CHPIVOTFLAGS = 0x0859;
+const sal_uInt16 BIFF5_ID_CHPIVOTREF = 0x1048;
+const sal_uInt16 BIFF8_ID_CHPIVOTREF = 0x0858;
+const sal_uInt16 BIFF_ID_CHPLOTFRAME = 0x1035;
+const sal_uInt16 BIFF_ID_CHPLOTGROWTH = 0x1064;
+const sal_uInt16 BIFF_ID_CHPROPERTIES = 0x1044;
+const sal_uInt16 BIFF_ID_CHRADARLINE = 0x103E;
+const sal_uInt16 BIFF_ID_CHRADARAREA = 0x1040;
+const sal_uInt16 BIFF_ID_CHSCATTER = 0x101B;
+const sal_uInt16 BIFF_ID_CHSERERRORBAR = 0x105B;
+const sal_uInt16 BIFF_ID_CHSERGROUP = 0x1045;
+const sal_uInt16 BIFF_ID_CHSERIES = 0x1003;
+const sal_uInt16 BIFF_ID_CHSERIESFORMAT = 0x105D;
+const sal_uInt16 BIFF_ID_CHSERPARENT = 0x104A;
+const sal_uInt16 BIFF_ID_CHSERTRENDLINE = 0x104B;
+const sal_uInt16 BIFF_ID_CHSOURCELINK = 0x1051;
+const sal_uInt16 BIFF_ID_CHSTRING = 0x100D;
+const sal_uInt16 BIFF_ID_CHSURFACE = 0x103F;
+const sal_uInt16 BIFF_ID_CHTEXT = 0x1025;
+const sal_uInt16 BIFF_ID_CHTICK = 0x101E;
+const sal_uInt16 BIFF_ID_CHTYPEGROUP = 0x1014;
+const sal_uInt16 BIFF_ID_CHVALUERANGE = 0x101F;
+const sal_uInt16 BIFF_ID_CODENAME = 0x01BA;
+const sal_uInt16 BIFF_ID_CODEPAGE = 0x0042;
+const sal_uInt16 BIFF_ID_COLINFO = 0x007D;
+const sal_uInt16 BIFF_ID_COLUMNDEFAULT = 0x0020;
+const sal_uInt16 BIFF_ID_COLWIDTH = 0x0024;
+const sal_uInt16 BIFF_ID_COMPRESSPICS = 0x089B;
+const sal_uInt16 BIFF_ID_CONNECTION = 0x0876;
+const sal_uInt16 BIFF_ID_COORDLIST = 0x00A9;
+const sal_uInt16 BIFF_ID_COUNTRY = 0x008C;
+const sal_uInt16 BIFF_ID_CRN = 0x005A;
+const sal_uInt16 BIFF2_ID_DATATABLE = 0x0036;
+const sal_uInt16 BIFF3_ID_DATATABLE = 0x0236;
+const sal_uInt16 BIFF2_ID_DATATABLE2 = 0x0037;
+const sal_uInt16 BIFF_ID_DATAVALIDATION = 0x01BE;
+const sal_uInt16 BIFF_ID_DATAVALIDATIONS = 0x01B2;
+const sal_uInt16 BIFF_ID_DATEMODE = 0x0022;
+const sal_uInt16 BIFF_ID_DBCELL = 0x00D7;
+const sal_uInt16 BIFF_ID_DBQUERY = 0x00DC;
+const sal_uInt16 BIFF_ID_DCONBINAME = 0x01B5;
+const sal_uInt16 BIFF_ID_DCONNAME = 0x0052;
+const sal_uInt16 BIFF_ID_DCONREF = 0x0051;
+const sal_uInt16 BIFF_ID_DEFCOLWIDTH = 0x0055;
+const sal_uInt16 BIFF2_ID_DEFINEDNAME = 0x0018;
+const sal_uInt16 BIFF3_ID_DEFINEDNAME = 0x0218;
+const sal_uInt16 BIFF5_ID_DEFINEDNAME = 0x0018;
+const sal_uInt16 BIFF2_ID_DEFROWHEIGHT = 0x0025;
+const sal_uInt16 BIFF3_ID_DEFROWHEIGHT = 0x0225;
+const sal_uInt16 BIFF_ID_DELTA = 0x0010;
+const sal_uInt16 BIFF2_ID_DIMENSION = 0x0000;
+const sal_uInt16 BIFF3_ID_DIMENSION = 0x0200;
+const sal_uInt16 BIFF_ID_DXF = 0x088D;
+const sal_uInt16 BIFF_ID_EXTERNALBOOK = 0x01AE;
+const sal_uInt16 BIFF2_ID_EXTERNALNAME = 0x0023;
+const sal_uInt16 BIFF3_ID_EXTERNALNAME = 0x0223;
+const sal_uInt16 BIFF5_ID_EXTERNALNAME = 0x0023;
+const sal_uInt16 BIFF_ID_EXTERNSHEET = 0x0017;
+const sal_uInt16 BIFF_ID_EXTSST = 0x00FF;
+const sal_uInt16 BIFF_ID_FILEPASS = 0x002F;
+const sal_uInt16 BIFF_ID_FILESHARING = 0x005B;
+const sal_uInt16 BIFF_ID_FILTERCOLUMN = 0x009E;
+const sal_uInt16 BIFF_ID_FILTERMODE = 0x009B;
+const sal_uInt16 BIFF2_ID_FONT = 0x0031;
+const sal_uInt16 BIFF3_ID_FONT = 0x0231;
+const sal_uInt16 BIFF5_ID_FONT = 0x0031;
+const sal_uInt16 BIFF_ID_FONTCOLOR = 0x0045;
+const sal_uInt16 BIFF_ID_FOOTER = 0x0015;
+const sal_uInt16 BIFF_ID_FORCEFULLCALC = 0x08A3;
+const sal_uInt16 BIFF2_ID_FORMAT = 0x001E;
+const sal_uInt16 BIFF4_ID_FORMAT = 0x041E;
+const sal_uInt16 BIFF2_ID_FORMULA = 0x0006;
+const sal_uInt16 BIFF3_ID_FORMULA = 0x0206;
+const sal_uInt16 BIFF4_ID_FORMULA = 0x0406;
+const sal_uInt16 BIFF5_ID_FORMULA = 0x0006;
+const sal_uInt16 BIFF_ID_GUTS = 0x0080;
+const sal_uInt16 BIFF_ID_HCENTER = 0x0083;
+const sal_uInt16 BIFF_ID_HEADER = 0x0014;
+const sal_uInt16 BIFF_ID_HEADERFOOTER = 0x089C;
+const sal_uInt16 BIFF_ID_HIDEOBJ = 0x008D;
+const sal_uInt16 BIFF_ID_HORPAGEBREAKS = 0x001B;
+const sal_uInt16 BIFF_ID_HYPERLINK = 0x01B8;
+const sal_uInt16 BIFF3_ID_IMGDATA = 0x007F;
+const sal_uInt16 BIFF8_ID_IMGDATA = 0x00E9;
+const sal_uInt16 BIFF2_ID_INDEX = 0x000B;
+const sal_uInt16 BIFF3_ID_INDEX = 0x020B;
+const sal_uInt16 BIFF2_ID_INTEGER = 0x0002;
+const sal_uInt16 BIFF_ID_INTERFACEHDR = 0x00E1;
+const sal_uInt16 BIFF_ID_ITERATION = 0x0011;
+const sal_uInt16 BIFF_ID_IXFE = 0x0044;
+const sal_uInt16 BIFF2_ID_LABEL = 0x0004;
+const sal_uInt16 BIFF3_ID_LABEL = 0x0204;
+const sal_uInt16 BIFF_ID_LABELRANGES = 0x015F;
+const sal_uInt16 BIFF_ID_LABELSST = 0x00FD;
+const sal_uInt16 BIFF_ID_LEFTMARGIN = 0x0026;
+const sal_uInt16 BIFF_ID_MERGEDCELLS = 0x00E5;
+const sal_uInt16 BIFF_ID_MSODRAWING = 0x00EC;
+const sal_uInt16 BIFF_ID_MSODRAWINGGROUP = 0x00EB;
+const sal_uInt16 BIFF_ID_MSODRAWINGSEL = 0x00ED;
+const sal_uInt16 BIFF_ID_MTHREADSETTINGS = 0x089A;
+const sal_uInt16 BIFF_ID_MULTBLANK = 0x00BE;
+const sal_uInt16 BIFF_ID_MULTRK = 0x00BD;
+const sal_uInt16 BIFF_ID_NOTE = 0x001C;
+const sal_uInt16 BIFF_ID_NOTESOUND = 0x0096;
+const sal_uInt16 BIFF2_ID_NUMBER = 0x0003;
+const sal_uInt16 BIFF3_ID_NUMBER = 0x0203;
+const sal_uInt16 BIFF_ID_OBJ = 0x005D;
+const sal_uInt16 BIFF_ID_OBJECTPROTECT = 0x0063;
+const sal_uInt16 BIFF_ID_OLESIZE = 0x00DE;
+const sal_uInt16 BIFF_ID_PAGELAYOUTVIEW = 0x088B;
+const sal_uInt16 BIFF_ID_PAGESETUP = 0x00A1;
+const sal_uInt16 BIFF_ID_PALETTE = 0x0092;
+const sal_uInt16 BIFF_ID_PANE = 0x0041;
+const sal_uInt16 BIFF_ID_PARAMQUERY = 0x00DC;
+const sal_uInt16 BIFF_ID_PASSWORD = 0x0013;
+const sal_uInt16 BIFF_ID_PCDFIELDINDEX = 0x0103;
+const sal_uInt16 BIFF_ID_PCDFORMULAFIELD = 0x00F9;
+const sal_uInt16 BIFF_ID_PCDSOURCE = 0x00E3;
+const sal_uInt16 BIFF_ID_PHONETICPR = 0x00EF;
+const sal_uInt16 BIFF_ID_PICTURE = 0x00E9;
+const sal_uInt16 BIFF_ID_PIVOTCACHE = 0x00D5;
+const sal_uInt16 BIFF_ID_PRECISION = 0x000E;
+const sal_uInt16 BIFF_ID_PRINTGRIDLINES = 0x002B;
+const sal_uInt16 BIFF_ID_PRINTHEADERS = 0x002A;
+const sal_uInt16 BIFF_ID_PROJEXTSHEET = 0x00A3;
+const sal_uInt16 BIFF_ID_PROTECT = 0x0012;
+const sal_uInt16 BIFF_ID_PTDATAFIELD = 0x00C5;
+const sal_uInt16 BIFF_ID_PTDEFINITION = 0x00B0;
+const sal_uInt16 BIFF_ID_PTDEFINITION2 = 0x00F1;
+const sal_uInt16 BIFF_ID_PTFIELD = 0x00B1;
+const sal_uInt16 BIFF_ID_PTFIELD2 = 0x0100;
+const sal_uInt16 BIFF_ID_PTFITEM = 0x00B2;
+const sal_uInt16 BIFF_ID_PTPAGEFIELDS = 0x00B6;
+const sal_uInt16 BIFF_ID_PTROWCOLFIELDS = 0x00B4;
+const sal_uInt16 BIFF_ID_PTROWCOLITEMS = 0x00B5;
+const sal_uInt16 BIFF_ID_QUERYTABLE = 0x01AD;
+const sal_uInt16 BIFF_ID_QUERYTABLEREFRESH = 0x0802;
+const sal_uInt16 BIFF_ID_QUERYTABLESETTINGS = 0x0803;
+const sal_uInt16 BIFF_ID_QUERYTABLESTRING = 0x0804;
+const sal_uInt16 BIFF_ID_RECALCID = 0x01C1;
+const sal_uInt16 BIFF_ID_REFMODE = 0x000F;
+const sal_uInt16 BIFF_ID_RIGHTMARGIN = 0x0027;
+const sal_uInt16 BIFF_ID_RK = 0x027E;
+const sal_uInt16 BIFF2_ID_ROW = 0x0008;
+const sal_uInt16 BIFF3_ID_ROW = 0x0208;
+const sal_uInt16 BIFF_ID_RSTRING = 0x00D6;
+const sal_uInt16 BIFF_ID_SAVERECALC = 0x005F;
+const sal_uInt16 BIFF_ID_SCENARIO = 0x00AF;
+const sal_uInt16 BIFF_ID_SCENARIOS = 0x00AE;
+const sal_uInt16 BIFF_ID_SCL = 0x00A0;
+const sal_uInt16 BIFF_ID_SCENPROTECT = 0x00DD;
+const sal_uInt16 BIFF_ID_SCREENTIP = 0x0800;
+const sal_uInt16 BIFF_ID_SELECTION = 0x001D;
+const sal_uInt16 BIFF_ID_SHAREDFEATHEAD = 0x0867;
+const sal_uInt16 BIFF_ID_SHAREDFMLA = 0x04BC;
+const sal_uInt16 BIFF_ID_SHEET = 0x0085;
+const sal_uInt16 BIFF_ID_SHEETEXT = 0x0862;
+const sal_uInt16 BIFF_ID_SHEETHEADER = 0x008F;
+const sal_uInt16 BIFF_ID_SHEETPR = 0x0081;
+const sal_uInt16 BIFF_ID_SST = 0x00FC;
+const sal_uInt16 BIFF_ID_STANDARDWIDTH = 0x0099;
+const sal_uInt16 BIFF2_ID_STRING = 0x0007;
+const sal_uInt16 BIFF3_ID_STRING = 0x0207;
+const sal_uInt16 BIFF_ID_STYLE = 0x0293;
+const sal_uInt16 BIFF_ID_STYLEEXT = 0x0892;
+const sal_uInt16 BIFF_ID_TABLESTYLES = 0x088E;
+const sal_uInt16 BIFF_ID_THEME = 0x0896;
+const sal_uInt16 BIFF_ID_TOPMARGIN = 0x0028;
+const sal_uInt16 BIFF_ID_TXO = 0x01B6;
+const sal_uInt16 BIFF_ID_UNCALCED = 0x005E;
+const sal_uInt16 BIFF_ID_USESELFS = 0x0160;
+const sal_uInt16 BIFF_ID_VBAPROJECT = 0x00D3;
+const sal_uInt16 BIFF_ID_VBAPROJECTEMPTY = 0x01BD;
+const sal_uInt16 BIFF_ID_VCENTER = 0x0084;
+const sal_uInt16 BIFF_ID_VERPAGEBREAKS = 0x001A;
+const sal_uInt16 BIFF_ID_WINDOW1 = 0x003D;
+const sal_uInt16 BIFF2_ID_WINDOW2 = 0x003E;
+const sal_uInt16 BIFF3_ID_WINDOW2 = 0x023E;
+const sal_uInt16 BIFF_ID_WRITEACCESS = 0x005C;
+const sal_uInt16 BIFF_ID_XCT = 0x0059;
+const sal_uInt16 BIFF2_ID_XF = 0x0043;
+const sal_uInt16 BIFF3_ID_XF = 0x0243;
+const sal_uInt16 BIFF4_ID_XF = 0x0443;
+const sal_uInt16 BIFF5_ID_XF = 0x00E0;
+const sal_uInt16 BIFF_ID_XFCRC = 0x087C;
+const sal_uInt16 BIFF_ID_XFEXT = 0x087D;
+
+// OBJ subrecord identifiers --------------------------------------------------
+const sal_uInt16 BIFF_ID_OBJEND = 0x0000; /// End of OBJ.
+const sal_uInt16 BIFF_ID_OBJMACRO = 0x0004; /// Macro link.
+const sal_uInt16 BIFF_ID_OBJBUTTON = 0x0005; /// Button data.
+const sal_uInt16 BIFF_ID_OBJGMO = 0x0006; /// Group marker.
+const sal_uInt16 BIFF_ID_OBJCF = 0x0007; /// Clipboard format.
+const sal_uInt16 BIFF_ID_OBJFLAGS = 0x0008; /// Option flags.
+const sal_uInt16 BIFF_ID_OBJPICTFMLA = 0x0009; /// OLE link formula.
+const sal_uInt16 BIFF_ID_OBJCBLS = 0x000A; /// Check box/radio button data.
+const sal_uInt16 BIFF_ID_OBJRBO = 0x000B; /// Radio button group data.
+const sal_uInt16 BIFF_ID_OBJSBS = 0x000C; /// Scroll bar data.
+const sal_uInt16 BIFF_ID_OBJNTS = 0x000C; /// Note data.
+const sal_uInt16 BIFF_ID_OBJSBSFMLA = 0x000E; /// Scroll bar/list box/combo box cell link.
+const sal_uInt16 BIFF_ID_OBJGBODATA = 0x000F; /// Group box data.
+const sal_uInt16 BIFF_ID_OBJEDODATA = 0x0010; /// Edit box data.
+const sal_uInt16 BIFF_ID_OBJRBODATA = 0x0011; /// Radio button group data.
+const sal_uInt16 BIFF_ID_OBJCBLSDATA = 0x0012; /// Check box/radio button data.
+const sal_uInt16 BIFF_ID_OBJLBSDATA = 0x0013; /// List box/combo box data.
+const sal_uInt16 BIFF_ID_OBJCBLSFMLA = 0x0014; /// Check box/radio button cell link.
+const sal_uInt16 BIFF_ID_OBJCMO = 0x0015; /// Common object settings.
+
+// record constants -----------------------------------------------------------
+
+const sal_uInt8 BIFF_ERR_NULL = 0x00;
+const sal_uInt8 BIFF_ERR_DIV0 = 0x07;
+const sal_uInt8 BIFF_ERR_VALUE = 0x0F;
+const sal_uInt8 BIFF_ERR_REF = 0x17;
+const sal_uInt8 BIFF_ERR_NAME = 0x1D;
+const sal_uInt8 BIFF_ERR_NUM = 0x24;
+const sal_uInt8 BIFF_ERR_NA = 0x2A;
+
+const sal_uInt16 BIFF_BOF_BIFF2 = 0x0200;
+const sal_uInt16 BIFF_BOF_BIFF3 = 0x0300;
+const sal_uInt16 BIFF_BOF_BIFF4 = 0x0400;
+const sal_uInt16 BIFF_BOF_BIFF5 = 0x0500;
+const sal_uInt16 BIFF_BOF_BIFF8 = 0x0600;
+
+const sal_uInt8 BIFF_DATATYPE_EMPTY = 0;
+const sal_uInt8 BIFF_DATATYPE_DOUBLE = 1;
+const sal_uInt8 BIFF_DATATYPE_STRING = 2;
+const sal_uInt8 BIFF_DATATYPE_BOOL = 4;
+const sal_uInt8 BIFF_DATATYPE_ERROR = 16;
+
+const sal_uInt8 BIFF_BOOLERR_BOOL = 0;
+const sal_uInt8 BIFF_BOOLERR_ERROR = 1;
+
+// BIFF8 unicode strings ------------------------------------------------------
+
+const sal_uInt8 BIFF_STRF_16BIT = 0x01;
+const sal_uInt8 BIFF_STRF_PHONETIC = 0x04;
+const sal_uInt8 BIFF_STRF_RICH = 0x08;
+const sal_uInt8 BIFF_STRF_UNKNOWN = 0xF2;
+
+/** Static helper functions for BIFF filters. */
+class BiffHelper
+{
+public:
+ // conversion -------------------------------------------------------------
+
+ /** Converts the passed packed number to a double. */
+ static double calcDoubleFromRk( sal_Int32 nRkValue );
+
+ /** Converts the passed BIFF error to a double containing the respective Calc error code. */
+ static double calcDoubleFromError( sal_uInt8 nErrorCode );
+
+ // BIFF12 import ----------------------------------------------------------
+
+ /** Reads a BIFF12 string with leading 16-bit or 32-bit length field. */
+ static OUString readString( SequenceInputStream& rStrm, bool b32BitLen = true );
+
+private:
+ BiffHelper() = delete;
+ ~BiffHelper() = delete;
+};
+
+/** BIFF12 stream operator for an OUString, reads 32-bit string length and Unicode array. */
+inline SequenceInputStream& operator>>( SequenceInputStream& rStrm, OUString& orString )
+{
+ orString = BiffHelper::readString( rStrm );
+ return rStrm;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/chartsheetfragment.hxx b/sc/source/filter/inc/chartsheetfragment.hxx
new file mode 100644
index 000000000..c2cce27c2
--- /dev/null
+++ b/sc/source/filter/inc/chartsheetfragment.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class ChartsheetFragment final : public WorksheetFragmentBase
+{
+public:
+ explicit ChartsheetFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void initializeImport() override;
+ virtual void finalizeImport() override;
+
+ /** Imports the relation identifier for the DrawingML part. */
+ void importDrawing( const AttributeList& rAttribs );
+ /** Imports the DRAWING record containing the relation identifier for the DrawingML part. */
+ void importDrawing( SequenceInputStream& rStrm );
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/colrowst.hxx b/sc/source/filter/inc/colrowst.hxx
new file mode 100644
index 000000000..963ea9682
--- /dev/null
+++ b/sc/source/filter/inc/colrowst.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xiroot.hxx"
+#include <mdds/flat_segment_tree.hpp>
+#include <o3tl/typed_flags_set.hxx>
+
+enum class ExcColRowFlags : sal_uInt8 {
+ NONE = 0x00,
+ Used = 0x01,
+ Default = 0x02,
+ Hidden = 0x04,
+ Man = 0x08,
+};
+namespace o3tl {
+ template<> struct typed_flags<ExcColRowFlags> : is_typed_flags<ExcColRowFlags, 0x0f> {};
+}
+
+
+class XclImpColRowSettings final : protected XclImpRoot
+{
+public:
+ explicit XclImpColRowSettings( const XclImpRoot& rRoot );
+ virtual ~XclImpColRowSettings() override;
+
+ void SetDefWidth( sal_uInt16 nDefWidth, bool bStdWidthRec = false );
+ void SetWidthRange( SCCOL nCol1, SCCOL nCol2, sal_uInt16 nWidth );
+ void HideCol( SCCOL nCol );
+ void HideColRange( SCCOL nCol1, SCCOL nCol2 );
+
+ void SetDefHeight( sal_uInt16 nDefHeight, sal_uInt16 nFlags );
+ void SetHeight( SCROW nRow, sal_uInt16 nHeight );
+ void SetRowSettings( SCROW nRow, sal_uInt16 nHeight, sal_uInt16 nFlags );
+ void SetManualRowHeight( SCROW nScRow );
+
+ void SetDefaultXF( SCCOL nScCol1, SCCOL nScCol2, sal_uInt16 nXFIndex );
+ /** Inserts all column and row settings of the specified sheet, except the hidden flags. */
+ void Convert( SCTAB nScTab );
+ /** Sets the HIDDEN flags at all hidden columns and rows in the specified sheet. */
+ void ConvertHiddenFlags( SCTAB nScTab );
+
+private:
+ void ApplyColFlag(SCCOL nCol, ExcColRowFlags nNewVal);
+ bool GetColFlag(SCCOL nCol, ExcColRowFlags nMask) const;
+
+private:
+ typedef ::mdds::flat_segment_tree<SCROW, sal_uInt16> WidthHeightStoreType;
+ typedef ::mdds::flat_segment_tree<SCROW, ExcColRowFlags> ColRowFlagsType;
+ typedef ::mdds::flat_segment_tree<SCROW, bool> RowHiddenType;
+
+ WidthHeightStoreType maColWidths;
+ ColRowFlagsType maColFlags;
+ WidthHeightStoreType maRowHeights;
+ ColRowFlagsType maRowFlags;
+ RowHiddenType maHiddenRows;
+
+ SCROW mnLastScRow;
+
+ sal_uInt16 mnDefWidth; /// Default width from DEFCOLWIDTH or STANDARDWIDTH record.
+ sal_uInt16 mnDefHeight; /// Default height from DEFAULTROWHEIGHT record.
+ sal_uInt16 mnDefRowFlags; /// Default row flags from DEFAULTROWHEIGHT record.
+
+ bool mbHasStdWidthRec; /// true = Width from STANDARDWIDTH (overrides DEFCOLWIDTH record).
+ bool mbHasDefHeight; /// true = mnDefHeight and mnDefRowFlags are valid.
+ bool mbDirty;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/commentsbuffer.hxx b/sc/source/filter/inc/commentsbuffer.hxx
new file mode 100644
index 000000000..c30d6765d
--- /dev/null
+++ b/sc/source/filter/inc/commentsbuffer.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "richstring.hxx"
+#include "worksheethelper.hxx"
+#include <com/sun/star/awt/Rectangle.hpp>
+
+namespace oox::xls {
+
+struct CommentModel
+{
+ ScRange maRange; /// Position of the comment in the worksheet.
+ RichStringRef mxText; /// Formatted text of the comment (not used in BIFF8).
+ sal_Int32 mnAuthorId; /// Identifier of the comment's author (OOXML and BIFF12 only).
+ bool mbAutoFill; /// Auto Selection of comment object's fill style
+ bool mbAutoScale; /// Auto Scale comment text
+ bool mbColHidden; /// Comment cell's Column is Hidden
+ bool mbLocked; /// Comment changes Locked
+ bool mbRowHidden; /// Comment cell's Row is Hidden
+ sal_Int32 mnTHA; /// Horizontal Alignment
+ sal_Int32 mnTVA; /// Vertical Alignment
+ css::awt::Rectangle maAnchor; /// Anchor parameters
+
+ explicit CommentModel();
+};
+
+class Comment final : public WorksheetHelper
+{
+public:
+ explicit Comment( const WorksheetHelper& rHelper );
+
+ /** Imports a cell comment from the passed attributes of the comment element. */
+ void importComment( const AttributeList& rAttribs );
+ /** Imports a cell comment Properties from the passed attributes of the comment element. */
+ void importCommentPr( const AttributeList& rAttribs );
+ /** Imports a cell comment from the passed stream of a COMMENT record. */
+ void importComment( SequenceInputStream& rStrm );
+
+ /** Creates and returns a new rich-string object for the comment text. */
+ RichStringRef const & createText();
+
+ /** Finalizes the formatted string of the comment. */
+ void finalizeImport();
+
+private:
+ CommentModel maModel;
+};
+
+typedef std::shared_ptr< Comment > CommentRef;
+
+class CommentsBuffer final : public WorksheetHelper
+{
+public:
+ explicit CommentsBuffer( const WorksheetHelper& rHelper );
+
+ /** Appends a new author to the list of comment authors. */
+ void appendAuthor( const OUString& rAuthor );
+ /** Creates and returns a new comment. */
+ CommentRef createComment();
+
+ /** Finalizes the formatted string of all comments. */
+ void finalizeImport();
+
+private:
+ typedef RefVector< Comment > CommentVector;
+
+ std::vector< OUString > maAuthors;
+ CommentVector maComments;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/commentsfragment.hxx b/sc/source/filter/inc/commentsfragment.hxx
new file mode 100644
index 000000000..4acf4b0d8
--- /dev/null
+++ b/sc/source/filter/inc/commentsfragment.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "commentsbuffer.hxx"
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class CommentsFragment final : public WorksheetFragmentBase
+{
+public:
+ explicit CommentsFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onEndRecord() override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+ /** Imports comment data from the comment element. */
+ void importComment( const AttributeList& rAttribs );
+ /** Imports comment data from the COMMENT record. */
+ void importComment( SequenceInputStream& rStrm );
+
+ CommentRef mxComment;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/condformatbuffer.hxx b/sc/source/filter/inc/condformatbuffer.hxx
new file mode 100644
index 000000000..92c7463a5
--- /dev/null
+++ b/sc/source/filter/inc/condformatbuffer.hxx
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "worksheethelper.hxx"
+#include <tools/color.hxx>
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <rangelst.hxx>
+
+#include <memory>
+#include <vector>
+
+class ScColorScaleFormat;
+class ScDataBarFormat;
+struct ScDataBarFormatData;
+class ScIconSetFormat;
+struct ScIconSetFormatData;
+
+namespace oox { class AttributeList; }
+
+namespace oox::xls {
+
+class CondFormat;
+
+/** Model for a single rule in a conditional formatting. */
+struct CondFormatRuleModel
+{
+ typedef ::std::vector< ApiTokenSequence > ApiTokenSequenceVector;
+
+ ApiTokenSequenceVector maFormulas; /// Formulas for rule conditions.
+ OUString maText; /// Text for 'contains' rules.
+ sal_Int32 mnPriority; /// Priority of this rule.
+ sal_Int32 mnType; /// Type of the rule.
+ sal_Int32 mnOperator; /// In cell-is rules: Comparison operator.
+ sal_Int32 mnTimePeriod; /// In time-period rules: Type of time period.
+ sal_Int32 mnRank; /// In top-10 rules: True = bottom, false = top.
+ sal_Int32 mnStdDev; /// In average rules: Number of std deviations.
+ sal_Int32 mnDxfId; /// Differential formatting identifier.
+ bool mbStopIfTrue; /// True = stop evaluating rules, if this rule is true.
+ bool mbBottom; /// In top-10 rules: True = bottom, false = top.
+ bool mbPercent; /// In top-10 rules: True = percent, false = rank.
+ bool mbAboveAverage; /// In average rules: True = above average, false = below.
+ bool mbEqualAverage; /// In average rules: True = include average, false = exclude.
+
+ explicit CondFormatRuleModel();
+
+ /** Sets the passed BIFF operator for condition type cellIs. */
+ void setBiffOperator( sal_Int32 nOperator );
+
+ /** Sets the passed BIFF12 text comparison type and operator. */
+ void setBiff12TextType( sal_Int32 nOperator );
+};
+
+struct ColorScaleRuleModelEntry
+{
+ ::Color maColor;
+ double mnVal;
+
+ bool mbMin;
+ bool mbMax;
+ bool mbPercent;
+ bool mbPercentile;
+ bool mbNum;
+ OUString maFormula;
+
+ ColorScaleRuleModelEntry():
+ maColor(),
+ mnVal(0),
+ mbMin(false),
+ mbMax(false),
+ mbPercent(false),
+ mbPercentile(false),
+ mbNum(false) {}
+};
+
+class ColorScaleRule final : public WorksheetHelper
+{
+public:
+ ColorScaleRule( const CondFormat& rFormat );
+
+ void importCfvo( const AttributeList& rAttribs );
+ void importColor( const AttributeList& rAttribs );
+
+ void AddEntries( ScColorScaleFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr );
+
+private:
+ std::vector< ColorScaleRuleModelEntry > maColorScaleRuleEntries;
+
+ sal_uInt32 mnCfvo;
+ sal_uInt32 mnCol;
+};
+
+class DataBarRule final : public WorksheetHelper
+{
+public:
+ DataBarRule( const CondFormat& rFormat );
+ void importCfvo( const AttributeList& rAttribs );
+ void importColor( const AttributeList& rAttribs );
+ void importAttribs( const AttributeList& rAttribs );
+
+ void SetData( ScDataBarFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr );
+
+ ScDataBarFormatData* getDataBarFormatData() { return mxFormat.get(); }
+
+private:
+ std::unique_ptr<ScDataBarFormatData> mxFormat;
+
+ std::unique_ptr<ColorScaleRuleModelEntry> mpUpperLimit;
+ std::unique_ptr<ColorScaleRuleModelEntry> mpLowerLimit;
+};
+
+class IconSetRule final : public WorksheetHelper
+{
+public:
+ IconSetRule( const WorksheetHelper& rParent );
+ void importCfvo( const AttributeList& rAttribs );
+ void importAttribs( const AttributeList& rAttribs );
+ void importFormula(const OUString& rFormula);
+ void importIcon(const AttributeList& rAttribs);
+
+ void SetData( ScIconSetFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr );
+
+private:
+ std::vector< ColorScaleRuleModelEntry > maEntries;
+ std::unique_ptr<ScIconSetFormatData> mxFormatData;
+ OUString maIconSetType;
+ bool mbCustom;
+};
+
+/** Represents a single rule in a conditional formatting. */
+class CondFormatRule final : public WorksheetHelper
+{
+public:
+ explicit CondFormatRule( const CondFormat& rCondFormat, ScConditionalFormat* pFormat );
+
+ /** Imports rule settings from the cfRule element. */
+ void importCfRule( const AttributeList& rAttribs );
+ /** Appends a new condition formula string. */
+ void appendFormula( const OUString& rFormula );
+
+ /** Imports rule settings from a CFRULE record. */
+ void importCfRule( SequenceInputStream& rStrm );
+
+ /** Directly set a ScFormatEntry with a priority ready for finalizeImport(). */
+ void setFormatEntry(sal_Int32 nPriority, ScFormatEntry* pEntry);
+
+ /** Creates a conditional formatting rule in the Calc document. */
+ void finalizeImport();
+
+ /** Returns the priority of this rule. */
+ sal_Int32 getPriority() const { return maModel.mnPriority; }
+
+ ColorScaleRule* getColorScale();
+ DataBarRule* getDataBar();
+ IconSetRule* getIconSet();
+
+private:
+ const CondFormat& mrCondFormat;
+ CondFormatRuleModel maModel;
+ ScConditionalFormat* mpFormat;
+ ScFormatEntry* mpFormatEntry;
+ std::unique_ptr<ColorScaleRule> mpColor;
+ std::unique_ptr<DataBarRule> mpDataBar;
+ std::unique_ptr<IconSetRule> mpIconSet;
+};
+
+typedef std::shared_ptr< CondFormatRule > CondFormatRuleRef;
+
+/** Model for a conditional formatting object. */
+struct CondFormatModel
+{
+ ScRangeList maRanges; /// Cell ranges for this conditional format.
+ bool mbPivot; /// Conditional formatting belongs to pivot table.
+
+ explicit CondFormatModel();
+};
+
+class CondFormatBuffer;
+
+/** Represents a conditional formatting object with a list of affected cell ranges. */
+class CondFormat final : public WorksheetHelper
+{
+friend class CondFormatBuffer;
+public:
+ explicit CondFormat( const WorksheetHelper& rHelper );
+
+ /** Imports settings from the conditionalFormatting element. */
+ void importConditionalFormatting( const AttributeList& rAttribs );
+ /** Imports a conditional formatting rule from the cfRule element. */
+ CondFormatRuleRef importCfRule( const AttributeList& rAttribs );
+
+ /** Imports settings from the CONDFORMATTING record. */
+ void importCondFormatting( SequenceInputStream& rStrm );
+ /** Imports a conditional formatting rule from the CFRULE record. */
+ void importCfRule( SequenceInputStream& rStrm );
+
+ /** Creates the conditional formatting in the Calc document. */
+ void finalizeImport();
+
+ /** Returns the cell ranges this conditional formatting belongs to. */
+ const ScRangeList& getRanges() const { return maModel.maRanges; }
+
+ void setReadyForFinalize() { mbReadyForFinalize = true; }
+private:
+ CondFormatRuleRef createRule();
+ void insertRule( CondFormatRuleRef const & xRule );
+
+private:
+ typedef RefMap< sal_Int32, CondFormatRule > CondFormatRuleMap;
+
+ CondFormatModel maModel; /// Model of this conditional formatting.
+ CondFormatRuleMap maRules; /// Maps formatting rules by priority.
+ ScConditionalFormat* mpFormat;
+ bool mbReadyForFinalize;
+};
+
+struct ExCfRuleModel
+{
+ ExCfRuleModel() : mnAxisColor( ColorTransparency, UNSIGNED_RGB_TRANSPARENT ), mnNegativeColor( ColorTransparency, UNSIGNED_RGB_TRANSPARENT ), mbGradient( false ), mbIsLower( true ) {}
+ // AxisColor
+ ::Color mnAxisColor;
+ // NegativeFillColor
+ ::Color mnNegativeColor;
+ OUString maAxisPosition; // DataBar
+ OUString maColorScaleType; // Cfvo
+ bool mbGradient; // DataBar
+ bool mbIsLower; // Cfvo
+};
+
+class ExtCfDataBarRule : public WorksheetHelper
+{
+ enum RuleType
+ {
+ DATABAR,
+ NEGATIVEFILLCOLOR,
+ AXISCOLOR,
+ CFVO,
+ UNKNOWN,
+ };
+ ExCfRuleModel maModel;
+ RuleType mnRuleType;
+ ScDataBarFormatData* mpTarget;
+public:
+
+ ExtCfDataBarRule(ScDataBarFormatData* pTarget, const WorksheetHelper& rParent);
+ void finalizeImport();
+ void importDataBar( const AttributeList& rAttribs );
+ void importNegativeFillColor( const AttributeList& rAttribs );
+ void importAxisColor( const AttributeList& rAttribs );
+ void importCfvo( const AttributeList& rAttribs );
+ ExCfRuleModel& getModel() { return maModel; }
+};
+
+class ExtCfCondFormat
+{
+public:
+ ExtCfCondFormat(const ScRangeList& aRange, std::vector< std::unique_ptr<ScFormatEntry> >& rEntries,
+ const std::vector<sal_Int32>* pPriorities = nullptr);
+ ~ExtCfCondFormat();
+
+ const ScRangeList& getRange() const;
+ const std::vector< std::unique_ptr<ScFormatEntry> >& getEntries() const;
+ const std::vector<sal_Int32>& getPriorities() const { return maPriorities; }
+
+private:
+ std::vector< std::unique_ptr<ScFormatEntry> > maEntries;
+ std::vector<sal_Int32> maPriorities;
+ ScRangeList maRange;
+};
+
+typedef std::shared_ptr< CondFormat > CondFormatRef;
+typedef std::shared_ptr< ExtCfDataBarRule > ExtCfDataBarRuleRef;
+
+class CondFormatBuffer final : public WorksheetHelper
+{
+public:
+ explicit CondFormatBuffer( const WorksheetHelper& rHelper );
+
+ /** Imports settings from the conditionalFormatting element. */
+ CondFormatRef importConditionalFormatting( const AttributeList& rAttribs );
+ /** Imports settings from the CONDFORMATTING record. */
+ CondFormatRef importCondFormatting( SequenceInputStream& rStrm );
+ ExtCfDataBarRuleRef createExtCfDataBarRule(ScDataBarFormatData* pTarget);
+ std::vector< std::unique_ptr<ExtCfCondFormat> >& importExtCondFormat();
+
+ /** Converts an OOXML condition operator token to the API constant. */
+ static sal_Int32 convertToApiOperator( sal_Int32 nToken );
+ static ScConditionMode convertToInternalOperator( sal_Int32 nToken );
+ void finalizeImport();
+private:
+ CondFormatRef createCondFormat();
+
+private:
+ typedef RefVector< CondFormat > CondFormatVec;
+ typedef RefVector< ExtCfDataBarRule > ExtCfDataBarRuleVec;
+ CondFormatVec maCondFormats; /// All conditional formatting in a sheet.
+ ExtCfDataBarRuleVec maCfRules; /// All external conditional formatting rules in a sheet.
+ std::vector< std::unique_ptr<ExtCfCondFormat> > maExtCondFormats;
+ sal_Int32 mnNonPrioritizedRuleNextPriority = 1048576;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/condformatcontext.hxx b/sc/source/filter/inc/condformatcontext.hxx
new file mode 100644
index 000000000..4da08a5bb
--- /dev/null
+++ b/sc/source/filter/inc/condformatcontext.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "condformatbuffer.hxx"
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class CondFormatContext;
+
+class ColorScaleContext final : public WorksheetContextBase
+{
+public:
+ explicit ColorScaleContext( CondFormatContext& rFragment, CondFormatRuleRef const & xRule );
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+ CondFormatRuleRef mxRule;
+};
+
+class DataBarContext final : public WorksheetContextBase
+{
+public:
+ explicit DataBarContext( CondFormatContext& rFormat, CondFormatRuleRef const & xRule );
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+ CondFormatRuleRef mxRule;
+};
+
+class IconSetContext final : public WorksheetContextBase
+{
+public:
+ explicit IconSetContext( WorksheetContextBase& rParent, IconSetRule* pIconSet );
+
+ virtual oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters(const OUString& rChars) override;
+ virtual void onEndElement() override;
+
+private:
+ IconSetRule* mpIconSet;
+ OUString maChars;
+};
+
+class CondFormatContext final : public WorksheetContextBase
+{
+public:
+ explicit CondFormatContext( WorksheetFragmentBase& rFragment );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+ virtual void onEndRecord() override;
+
+ CondFormatRef mxCondFmt;
+ CondFormatRuleRef mxRule;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/connectionsbuffer.hxx b/sc/source/filter/inc/connectionsbuffer.hxx
new file mode 100644
index 000000000..9308da5a3
--- /dev/null
+++ b/sc/source/filter/inc/connectionsbuffer.hxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+const sal_Int32 BIFF12_CONNECTION_UNKNOWN = 0;
+const sal_Int32 BIFF12_CONNECTION_ODBC = 1;
+const sal_Int32 BIFF12_CONNECTION_DAO = 2;
+const sal_Int32 BIFF12_CONNECTION_FILE = 3;
+const sal_Int32 BIFF12_CONNECTION_HTML = 4;
+const sal_Int32 BIFF12_CONNECTION_OLEDB = 5;
+const sal_Int32 BIFF12_CONNECTION_TEXT = 6;
+const sal_Int32 BIFF12_CONNECTION_ADO = 7;
+const sal_Int32 BIFF12_CONNECTION_DSP = 8;
+
+/** Special properties for data connections representing web queries. */
+struct WebPrModel
+{
+ typedef ::std::vector< css::uno::Any > TablesVector;
+
+ TablesVector maTables; /// Names or indexes of the web query tables.
+ OUString maUrl; /// Source URL to refresh the data.
+ OUString maPostMethod; /// POST method to query data.
+ OUString maEditPage; /// Web page showing query data (for XML queries).
+ sal_Int32 mnHtmlFormat; /// Plain text, rich text, or HTML.
+ bool mbXml; /// True = XML query, false = HTML query.
+ bool mbSourceData; /// True = import XML source data referred by HTML table.
+ bool mbParsePre; /// True = parse preformatted sections (<pre> tag).
+ bool mbConsecutive; /// True = join consecutive delimiters.
+ bool mbFirstRow; /// True = use column widths of first row for entire <pre> tag.
+ bool mbXl97Created; /// True = web query created with Excel 97.
+ bool mbTextDates; /// True = read date values as text, false = parse dates.
+ bool mbXl2000Refreshed; /// True = refreshed with Excel 2000 or newer.
+ bool mbHtmlTables; /// True = HTML tables, false = entire document.
+
+ explicit WebPrModel();
+};
+
+/** Common properties of an external data connection. */
+struct ConnectionModel
+{
+ typedef ::std::unique_ptr< WebPrModel > WebPrModelPtr;
+
+ WebPrModelPtr mxWebPr; /// Special settings for web queries.
+ OUString maName; /// Unique name of this connection.
+ OUString maDescription; /// User description of this connection.
+ OUString maSourceFile; /// URL of a source data file.
+ OUString maSourceConnFile; /// URL of a source connection file.
+ OUString maSsoId; /// Single sign-on identifier.
+ sal_Int32 mnId; /// Unique connection identifier.
+ sal_Int32 mnType; /// Data source type.
+ sal_Int32 mnReconnectMethod; /// Reconnection method.
+ sal_Int32 mnCredentials; /// Credentials method.
+ sal_Int32 mnInterval; /// Refresh interval in minutes.
+ bool mbKeepAlive; /// True = keep connection open after import.
+ bool mbNew; /// True = new connection, never updated.
+ bool mbDeleted; /// True = connection has been deleted.
+ bool mbOnlyUseConnFile; /// True = use maSourceConnFile, ignore mnReconnectMethod.
+ bool mbBackground; /// True = background refresh enabled.
+ bool mbRefreshOnLoad; /// True = refresh connection on import.
+ bool mbSaveData; /// True = save cached data with connection.
+ bool mbSavePassword; /// True = save password in connection string.
+
+ explicit ConnectionModel();
+
+ WebPrModel& createWebPr();
+};
+
+/** An external data connection (database, web query, etc.). */
+class Connection final : public WorkbookHelper
+{
+public:
+ explicit Connection( const WorkbookHelper& rHelper );
+
+ /** Imports connection settings from the connection element. */
+ void importConnection( const AttributeList& rAttribs );
+ /** Imports web query settings from the webPr element. */
+ void importWebPr( const AttributeList& rAttribs );
+ /** Imports web query table settings from the tables element. */
+ void importTables();
+ /** Imports a web query table identifier from the m, s, or x element. */
+ void importTable( const AttributeList& rAttribs, sal_Int32 nElement );
+
+ /** Imports connection settings from the CONNECTION record. */
+ void importConnection( SequenceInputStream& rStrm );
+ /** Imports web query settings from the WEBPR record. */
+ void importWebPr( SequenceInputStream& rStrm );
+ /** Imports web query table settings from the WEBPRTABLES record. */
+ void importWebPrTables( SequenceInputStream& rStrm );
+ /** Imports a web query table identifier from the PCITEM_MISSING, PCITEM_STRING, or PCITEM_INDEX record. */
+ void importWebPrTable( SequenceInputStream& rStrm, sal_Int32 nRecId );
+
+ /** Returns the unique connection identifier. */
+ sal_Int32 getConnectionId() const { return maModel.mnId; }
+ /** Returns the source data type of the connection. */
+ sal_Int32 getConnectionType() const { return maModel.mnType; }
+ /** Returns read-only access to the connection model data. */
+ const ConnectionModel& getModel() const { return maModel; }
+
+private:
+ ConnectionModel maModel;
+};
+
+typedef std::shared_ptr< Connection > ConnectionRef;
+
+class ConnectionsBuffer final : public WorkbookHelper
+{
+public:
+ explicit ConnectionsBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new empty connection. */
+ Connection& createConnection();
+
+ /** Maps all connections by their identifier. */
+ void finalizeImport();
+
+ /** Returns a data connection by its unique identifier. */
+ ConnectionRef getConnection( sal_Int32 nConnId ) const;
+
+private:
+ /** Inserts the passed connection into the map according to its identifier. */
+ void insertConnectionToMap( const ConnectionRef& rxConnection );
+
+private:
+ typedef RefVector< Connection > ConnectionVector;
+ typedef RefMap< sal_Int32, Connection > ConnectionMap;
+
+ ConnectionVector maConnections;
+ ConnectionMap maConnectionsById;
+ sal_Int32 mnUnusedId;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/connectionsfragment.hxx b/sc/source/filter/inc/connectionsfragment.hxx
new file mode 100644
index 000000000..8eaec1c41
--- /dev/null
+++ b/sc/source/filter/inc/connectionsfragment.hxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class Connection;
+
+class ConnectionContext final : public WorkbookContextBase
+{
+public:
+ explicit ConnectionContext( WorkbookFragmentBase& rParent, Connection& rConnection );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+ Connection& mrConnection;
+};
+
+class ConnectionsFragment final : public WorkbookFragmentBase
+{
+public:
+ explicit ConnectionsFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/decl.h b/sc/source/filter/inc/decl.h
new file mode 100644
index 000000000..0cfbc239c
--- /dev/null
+++ b/sc/source/filter/inc/decl.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#pragma once
+
+enum WKTYP { eWK_UNKNOWN = -2, eWK_1 = 0, eWK_2, eWK3, eWK4, eWK_Error, eWK123 };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/defnamesbuffer.hxx b/sc/source/filter/inc/defnamesbuffer.hxx
new file mode 100644
index 000000000..ade123682
--- /dev/null
+++ b/sc/source/filter/inc/defnamesbuffer.hxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "workbookhelper.hxx"
+#include <oox/helper/binarystreambase.hxx>
+#include <oox/helper/refvector.hxx>
+
+#include <memory>
+
+class ScTokenArray;
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+// codes for built-in names
+const sal_Unicode BIFF_DEFNAME_CONSOLIDATEAREA = '\x00';
+const sal_Unicode BIFF_DEFNAME_AUTOOPEN = '\x01'; // Sheet macro executed when workbook is opened.
+const sal_Unicode BIFF_DEFNAME_AUTOCLOSE = '\x02'; // Sheet macro executed when workbook is closed.
+const sal_Unicode BIFF_DEFNAME_EXTRACT = '\x03'; // Filter output destination for advanced filter.
+const sal_Unicode BIFF_DEFNAME_DATABASE = '\x04';
+const sal_Unicode BIFF_DEFNAME_CRITERIA = '\x05'; // Filter criteria source range for advanced filter.
+const sal_Unicode BIFF_DEFNAME_PRINTAREA = '\x06'; // Print ranges.
+const sal_Unicode BIFF_DEFNAME_PRINTTITLES = '\x07'; // Rows/columns repeated on each page when printing.
+const sal_Unicode BIFF_DEFNAME_RECORDER = '\x08';
+const sal_Unicode BIFF_DEFNAME_DATAFORM = '\x09';
+const sal_Unicode BIFF_DEFNAME_AUTOACTIVATE = '\x0A'; // Sheet macro executed when workbook is activated.
+const sal_Unicode BIFF_DEFNAME_AUTODEACTIVATE = '\x0B'; // Sheet macro executed when workbook is deactivated.
+const sal_Unicode BIFF_DEFNAME_SHEETTITLE = '\x0C';
+const sal_Unicode BIFF_DEFNAME_FILTERDATABASE = '\x0D'; // Sheet range autofilter or advanced filter works on.
+const sal_Unicode BIFF_DEFNAME_UNKNOWN = '\x0E';
+
+struct DefinedNameModel
+{
+ OUString maName; /// The original name.
+ OUString maFormula; /// The formula string.
+ sal_Int32 mnSheet; /// Sheet index for local names.
+ sal_Int32 mnFuncGroupId; /// Function group identifier.
+ bool mbMacro; /// True = Macro name (VBA or sheet macro).
+ bool mbFunction; /// True = function, false = command.
+ bool mbVBName; /// True = VBA macro, false = sheet macro.
+ bool mbHidden; /// True = name hidden in UI.
+
+ explicit DefinedNameModel();
+};
+
+/** Base class for defined names and external names. */
+class DefinedNameBase : public WorkbookHelper
+{
+public:
+ explicit DefinedNameBase( const WorkbookHelper& rHelper );
+
+ /** Returns the original name as imported from or exported to the file. */
+ const OUString& getModelName() const { return maModel.maName; }
+ /** Returns the name as used in the Calc document. */
+ const OUString& getCalcName() const { return maCalcName; }
+
+ /** Returns the original name as imported from or exported to the file. */
+ const OUString& getUpcaseModelName() const;
+
+protected:
+ DefinedNameModel maModel; /// Model data for this defined name.
+ mutable OUString maUpModelName; /// Model name converted to uppercase ASCII.
+ OUString maCalcName; /// Final name used in the Calc document.
+};
+
+class DefinedName final : public DefinedNameBase
+{
+public:
+ explicit DefinedName( const WorkbookHelper& rHelper );
+ virtual ~DefinedName() override;
+
+ /** Sets the attributes for this defined name from the passed attribute set. */
+ void importDefinedName( const AttributeList& rAttribs );
+ /** Sets the formula string from the body of the definedName element. */
+ void setFormula( const OUString& rFormula );
+ /** Imports the defined name from a DEFINEDNAME record in the passed stream. */
+ void importDefinedName( SequenceInputStream& rStrm );
+
+ /** Creates a defined name in the Calc document. */
+ void createNameObject( sal_Int32 nIndex );
+ /** Converts the formula string or BIFF token array for this defined name. */
+ void convertFormula( const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks );
+ std::unique_ptr<ScTokenArray> getScTokens( const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks );
+ /** Returns true, if this defined name is global in the document. */
+ bool isGlobalName() const { return mnCalcSheet < 0; }
+ /** Returns true, if this defined name is a special builtin name. */
+ bool isBuiltinName() const { return mcBuiltinId != BIFF_DEFNAME_UNKNOWN; }
+ /** Returns true, if this defined name is a macro function call. */
+ bool isMacroFunction() const { return maModel.mbMacro && maModel.mbFunction; }
+ /** Returns true, if this defined name is a reference to a VBA macro. */
+ bool isVBName() const { return maModel.mbMacro && maModel.mbVBName; }
+
+ /** Returns the 0-based sheet index for local names, or -1 for global names. */
+ sal_Int16 getLocalCalcSheet() const { return mnCalcSheet; }
+ /** Returns the built-in identifier of the defined name. */
+ sal_Unicode getBuiltinId() const { return mcBuiltinId; }
+ /** Returns the token index used in API token arrays (com.sun.star.sheet.FormulaToken). */
+ sal_Int32 getTokenIndex() const { return mnTokenIndex; }
+ /** Tries to resolve the defined name to an absolute cell range. */
+ bool getAbsoluteRange( ScRange& orRange ) const;
+ bool isValid(const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks) const;
+
+private:
+ typedef ::std::unique_ptr< StreamDataSequence > StreamDataSeqPtr;
+
+ RangeDataRet maScRangeData; /// ScRangeData of the defined name.
+ sal_Int32 mnTokenIndex; /// Name index used in API token array.
+ sal_Int16 mnCalcSheet; /// Calc sheet index for sheet-local names.
+ sal_Unicode mcBuiltinId; /// Identifier for built-in defined names.
+ StreamDataSeqPtr mxFormula; /// Formula data for BIFF12 import.
+};
+
+typedef std::shared_ptr< DefinedName > DefinedNameRef;
+
+class DefinedNamesBuffer final : public WorkbookHelper
+{
+public:
+ explicit DefinedNamesBuffer( const WorkbookHelper& rHelper );
+
+ /** Imports a defined name from the passed attribute set. */
+ DefinedNameRef importDefinedName( const AttributeList& rAttribs );
+ /** Imports a defined name from a DEFINEDNAME record in the passed stream. */
+ void importDefinedName( SequenceInputStream& rStrm );
+
+ /** Creates all defined names in the document. */
+ void finalizeImport();
+
+ /** Returns a defined name by zero-based index (order of appearance). */
+ DefinedNameRef getByIndex( sal_Int32 nIndex ) const;
+ /** Returns a defined name by token index (index in XDefinedNames container). */
+ DefinedNameRef getByTokenIndex( sal_Int32 nIndex ) const;
+ /** Returns a defined name by its model name.
+ @param nSheet The sheet index for local names or -1 for global names.
+ If no local name is found, tries to find a matching global name.
+ @return Reference to the defined name or empty reference. */
+ DefinedNameRef getByModelName( const OUString& rModelName, sal_Int16 nCalcSheet = -1 ) const;
+ /** Returns a built-in defined name by its built-in identifier.
+ @param nSheet The sheet index of the built-in name.
+ @return Reference to the defined name or empty reference. */
+ DefinedNameRef getByBuiltinId( sal_Unicode cBuiltinId, sal_Int16 nCalcSheet ) const;
+
+private:
+ DefinedNameRef createDefinedName();
+
+private:
+ typedef ::std::pair< sal_Int16, OUString > SheetNameKey;
+ typedef ::std::pair< sal_Int16, sal_Unicode > BuiltinKey;
+
+ typedef RefVector< DefinedName > DefNameVector;
+ typedef RefMap< SheetNameKey, DefinedName > DefNameNameMap;
+ typedef RefMap< BuiltinKey, DefinedName > DefNameBuiltinMap;
+ typedef RefMap< sal_Int32, DefinedName > DefNameTokenIdMap;
+
+ DefNameVector maDefNames; /// List of all defined names in insertion order.
+ DefNameNameMap maModelNameMap; /// Maps all defined names by sheet index and model name.
+ DefNameBuiltinMap maBuiltinMap; /// Maps all defined names by sheet index and built-in identifier.
+ DefNameTokenIdMap maTokenIdMap; /// Maps all defined names by API token index.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/dif.hxx b/sc/source/filter/inc/dif.hxx
new file mode 100644
index 000000000..594327058
--- /dev/null
+++ b/sc/source/filter/inc/dif.hxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <types.hxx>
+
+class SvStream;
+class SvNumberFormatter;
+class ScDocument;
+
+extern const std::u16string_view pKeyTABLE;
+extern const std::u16string_view pKeyVECTORS;
+extern const std::u16string_view pKeyTUPLES;
+extern const std::u16string_view pKeyDATA;
+extern const std::u16string_view pKeyBOT;
+extern const std::u16string_view pKeyEOD;
+
+enum TOPIC
+{
+ T_UNKNOWN,
+ T_TABLE, T_VECTORS, T_TUPLES, T_DATA, T_LABEL, T_COMMENT, T_SIZE,
+ T_PERIODICITY, T_MAJORSTART, T_MINORSTART, T_TRUELENGTH, T_UINITS,
+ T_DISPLAYUNITS,
+ T_END
+};
+
+enum DATASET { D_BOT, D_EOD, D_NUMERIC, D_STRING, D_UNKNOWN, D_SYNT_ERROR };
+
+class DifParser
+{
+public:
+ OUStringBuffer m_aData;
+ double fVal;
+ sal_uInt32 nVector;
+ sal_uInt32 nVal;
+ sal_uInt32 nNumFormat;
+private:
+ SvNumberFormatter* pNumFormatter;
+ SvStream& rIn;
+ OUString aLookAheadLine;
+
+ bool ReadNextLine( OUString& rStr );
+ bool LookAhead();
+ DATASET GetNumberDataset( const sal_Unicode* pPossibleNumericData );
+ static inline bool IsBOT( const sal_Unicode* pRef );
+ static inline bool IsEOD( const sal_Unicode* pRef );
+ static inline bool Is1_0( const sal_Unicode* pRef );
+public:
+ DifParser( SvStream&, const ScDocument&, rtl_TextEncoding );
+
+ TOPIC GetNextTopic();
+
+ DATASET GetNextDataset();
+
+ static const sal_Unicode* ScanIntVal( const sal_Unicode* pStart, sal_uInt32& rRet );
+
+ static inline bool IsNumber( const sal_Unicode cChar );
+
+ static inline bool IsV( const sal_Unicode* pRef );
+};
+
+inline bool DifParser::IsBOT( const sal_Unicode* pRef )
+{
+ return pRef == pKeyBOT;
+}
+
+inline bool DifParser::IsEOD( const sal_Unicode* pRef )
+{
+ return pRef == pKeyEOD;
+}
+
+inline bool DifParser::Is1_0( const sal_Unicode* pRef )
+{
+ return pRef == std::u16string_view(u"1,0");
+}
+
+inline bool DifParser::IsV( const sal_Unicode* pRef )
+{
+ return pRef == std::u16string_view(u"V");
+}
+
+inline bool DifParser::IsNumber( const sal_Unicode cChar )
+{
+ return ( cChar >= '0' && cChar <= '9' );
+}
+
+class DifColumn
+{
+ friend class DifAttrCache;
+
+ struct ENTRY
+ {
+ sal_uInt32 nNumFormat;
+ SCROW nStart;
+ SCROW nEnd;
+ };
+
+ ENTRY *mpCurrent;
+ std::vector<ENTRY> maEntries;
+
+ DifColumn();
+
+ void SetNumFormat( const ScDocument* pDoc, SCROW nRow, const sal_uInt32 nNumFormat );
+
+ void NewEntry( const SCROW nPos, const sal_uInt32 nNumFormat );
+
+ void Apply( ScDocument &rDoc, const SCCOL nCol, const SCTAB nTab );
+};
+
+class DifAttrCache
+{
+public:
+
+ DifAttrCache();
+
+ ~DifAttrCache();
+
+ void SetNumFormat( const ScDocument* pDoc, const SCCOL nCol, const SCROW nRow, const sal_uInt32 nNumFormat );
+
+ void Apply( ScDocument&, SCTAB nTab );
+
+private:
+
+ std::map<SCCOL, std::unique_ptr<DifColumn>> maColMap;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/drawingbase.hxx b/sc/source/filter/inc/drawingbase.hxx
new file mode 100644
index 000000000..576a3619d
--- /dev/null
+++ b/sc/source/filter/inc/drawingbase.hxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/drawingml/drawingmltypes.hxx>
+#include "worksheethelper.hxx"
+
+namespace oox { class AttributeList; }
+
+namespace oox::xls {
+
+/** Absolute position in a spreadsheet (in EMUs) independent from cells. */
+struct AnchorPointModel : public ::oox::drawingml::EmuPoint
+{
+ explicit AnchorPointModel() : ::oox::drawingml::EmuPoint( -1, -1 ) {}
+ bool isValid() const { return (X >= 0) && (Y >= 0); }
+};
+
+/** Absolute size in a spreadsheet (in EMUs). */
+struct AnchorSizeModel : public ::oox::drawingml::EmuSize
+{
+ explicit AnchorSizeModel() : ::oox::drawingml::EmuSize( -1, -1 ) {}
+ bool isValid() const { return (Width >= 0) && (Height >= 0); }
+};
+
+/** Position in spreadsheet (cell position and offset inside cell). */
+struct CellAnchorModel
+{
+ sal_Int32 mnCol; /// Column index.
+ sal_Int32 mnRow; /// Row index.
+ sal_Int64 mnColOffset; /// X offset inside the column.
+ sal_Int64 mnRowOffset; /// Y offset inside the row.
+
+ explicit CellAnchorModel();
+ bool isValid() const { return (mnCol >= 0) && (mnRow >= 0); }
+};
+
+/** Application-specific client data of a shape. */
+struct AnchorClientDataModel
+{
+ bool mbLocksWithSheet;
+ bool mbPrintsWithSheet;
+
+ explicit AnchorClientDataModel();
+};
+
+/** Contains the position of a shape in the spreadsheet. Supports different
+ shape anchor modes (absolute, one-cell, two-cell). */
+class ShapeAnchor final : public WorksheetHelper
+{
+public:
+ enum AnchorType
+ {
+ ANCHOR_INVALID, /// Anchor type is unknown.
+ ANCHOR_ABSOLUTE, /// Absolute anchor (top-left corner and size in absolute units).
+ /// Matches our "Page" anchor -> ScAnchorType::SCA_PAGE
+ ANCHOR_ONECELL, /// One-cell anchor (top-left corner at cell, size in absolute units).
+ /// Matches our "Cell" anchor -> ScAnchorType::SCA_CELL
+ ANCHOR_TWOCELL, /// Two-cell anchor (top-left and bottom-right corner at cell).
+ /// Matches our "Cell (resize with cell)" anchor -> ScAnchorType::SCA_CELL_RESIZE
+ ANCHOR_VML
+ };
+ explicit ShapeAnchor( const WorksheetHelper& rHelper );
+
+ /** Imports the shape anchor (one of the elements xdr:absoluteAnchor, xdr:oneCellAnchor, xdr:twoCellAnchor). */
+ void importAnchor( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Imports the absolute anchor position from the xdr:pos element. */
+ void importPos( const AttributeList& rAttribs );
+ /** Imports the absolute anchor size from the xdr:ext element. */
+ void importExt( const AttributeList& rAttribs );
+ /** Imports the shape client data from the xdr:clientData element. */
+ void importClientData( const AttributeList& rAttribs );
+ /** Sets an attribute of the cell-dependent anchor position from xdr:from and xdr:to elements. */
+ void setCellPos( sal_Int32 nElement, sal_Int32 nParentContext, std::u16string_view rValue );
+ /** Imports the client anchor settings from a VML element. */
+ void importVmlAnchor( std::u16string_view rAnchor );
+
+ /** Checks whether the shape is visible based on the anchor
+
+ If From and To anchor has the same attribute values, the shape
+ will not have width and height and thus we can assume that
+ such kind of shape will be not be visible
+ */
+ bool isAnchorValid() const;
+
+ /** Calculates the resulting shape anchor in EMUs. */
+ ::oox::drawingml::EmuRectangle calcAnchorRectEmu( const css::awt::Size& rPageSizeHmm ) const;
+ /** Calculates the resulting shape anchor in 1/100 mm. */
+ css::awt::Rectangle calcAnchorRectHmm( const css::awt::Size& rPageSizeHmm ) const;
+ AnchorType getEditAs() const { return meEditAs; }
+private:
+ /** Converts the passed anchor to an absolute position in EMUs. */
+ ::oox::drawingml::EmuPoint calcCellAnchorEmu( const CellAnchorModel& rModel ) const;
+
+private:
+
+ /** Specifies how cell positions from CellAnchorModel have to be processed. */
+ enum class CellAnchorType
+ {
+ Emu, /// Offsets are given in EMUs.
+ Pixel, /// Offsets are given in screen pixels.
+ };
+
+ AnchorType meAnchorType; /// Type of this shape anchor.
+ CellAnchorType meCellAnchorType; /// Type of the cell anchor models.
+ AnchorPointModel maPos; /// Top-left position, if anchor is of type absolute.
+ AnchorSizeModel maSize; /// Anchor size, if anchor is not of type two-cell.
+ CellAnchorModel maFrom; /// Top-left position, if anchor is not of type absolute.
+ CellAnchorModel maTo; /// Bottom-right position, if anchor is of type two-cell.
+ AnchorClientDataModel maClientData; /// Shape client data.
+ AnchorType meEditAs; /// Anchor mode as shown in the UI.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/drawingfragment.hxx b/sc/source/filter/inc/drawingfragment.hxx
new file mode 100644
index 000000000..f93a13bc2
--- /dev/null
+++ b/sc/source/filter/inc/drawingfragment.hxx
@@ -0,0 +1,203 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <oox/drawingml/shapegroupcontext.hxx>
+#include <oox/ole/axcontrol.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <oox/ole/vbaproject.hxx>
+#include <oox/vml/vmldrawing.hxx>
+#include <oox/vml/vmldrawingfragment.hxx>
+#include <oox/vml/vmltextbox.hxx>
+#include "drawingbase.hxx"
+#include "excelhandlers.hxx"
+
+namespace oox::ole {
+ struct AxFontData;
+}
+
+namespace oox::xls {
+
+// DrawingML
+
+class ShapeMacroAttacher final : public ::oox::ole::VbaMacroAttacherBase
+{
+public:
+ explicit ShapeMacroAttacher( const OUString& rMacroName,
+ const css::uno::Reference< css::drawing::XShape >& rxShape );
+
+private:
+ virtual void attachMacro( const OUString& rMacroUrl ) override;
+
+private:
+ css::uno::Reference< css::drawing::XShape > mxShape;
+};
+
+class Shape final : public ::oox::drawingml::Shape, public WorksheetHelper
+{
+public:
+ explicit Shape(
+ const WorksheetHelper& rHelper,
+ const AttributeList& rAttribs,
+ const char* pcServiceName );
+
+private:
+ virtual void finalizeXShape(
+ ::oox::core::XmlFilterBase& rFilter,
+ const css::uno::Reference< css::drawing::XShapes >& rxShapes ) override;
+
+ OUString maMacroName;
+};
+
+/** Context handler for creation of shapes embedded in group shapes. */
+class GroupShapeContext final : public ::oox::drawingml::ShapeGroupContext, public WorksheetHelper
+{
+public:
+ explicit GroupShapeContext(
+ const ::oox::core::FragmentHandler2& rParent,
+ const WorksheetHelper& rHelper,
+ const ::oox::drawingml::ShapePtr& rxParentShape,
+ const ::oox::drawingml::ShapePtr& rxShape );
+
+ static ::oox::core::ContextHandlerRef
+ createShapeContext(
+ ::oox::core::FragmentHandler2& rParent,
+ const WorksheetHelper& rHelper,
+ sal_Int32 nElement,
+ const AttributeList& rAttribs,
+ const ::oox::drawingml::ShapePtr& rxParentShape,
+ ::oox::drawingml::ShapePtr* pxShape = nullptr );
+
+private:
+ virtual ::oox::core::ContextHandlerRef
+ onCreateContext(
+ sal_Int32 nElement,
+ const ::oox::AttributeList& rAttribs ) override;
+};
+
+/** Fragment handler for a complete sheet drawing. */
+class DrawingFragment final : public WorksheetFragmentBase
+{
+public:
+ explicit DrawingFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+private:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ typedef ::std::unique_ptr< ShapeAnchor > ShapeAnchorRef;
+
+ css::uno::Reference< css::drawing::XShapes >
+ mxDrawPage; /// Drawing page of this sheet.
+ ::oox::drawingml::ShapePtr mxShape; /// Current top-level shape.
+ ShapeAnchorRef mxAnchor; /// Current anchor of top-level shape.
+};
+
+// VML
+
+class VmlControlMacroAttacher final : public ::oox::ole::VbaMacroAttacherBase
+{
+public:
+ explicit VmlControlMacroAttacher( const OUString& rMacroName,
+ const css::uno::Reference< css::container::XIndexContainer >& rxCtrlFormIC,
+ sal_Int32 nCtrlIndex, sal_Int32 nCtrlType, sal_Int32 nDropStyle );
+
+private:
+ virtual void attachMacro( const OUString& rMacroUrl ) override;
+
+private:
+ css::uno::Reference< css::container::XIndexContainer > mxCtrlFormIC;
+ sal_Int32 mnCtrlIndex;
+ sal_Int32 mnCtrlType;
+ sal_Int32 mnDropStyle;
+};
+
+class VmlDrawing final : public ::oox::vml::Drawing, public WorksheetHelper
+{
+public:
+ explicit VmlDrawing( const WorksheetHelper& rHelper );
+
+ /** Returns the drawing shape for a cell note at the specified position. */
+ const ::oox::vml::ShapeBase* getNoteShape( const ScAddress& rPos ) const;
+
+ /** Filters cell note shapes. */
+ virtual bool isShapeSupported( const ::oox::vml::ShapeBase& rShape ) const override;
+
+ /** Returns additional base names for automatic shape name creation. */
+ virtual OUString getShapeBaseName( const ::oox::vml::ShapeBase& rShape ) const override;
+
+ /** Calculates the shape rectangle from a cell anchor string. */
+ virtual bool convertClientAnchor(
+ css::awt::Rectangle& orShapeRect,
+ const OUString& rShapeAnchor ) const override;
+
+ /** Creates a UNO control shape for legacy drawing controls. */
+ virtual css::uno::Reference< css::drawing::XShape >
+ createAndInsertClientXShape(
+ const ::oox::vml::ShapeBase& rShape,
+ const css::uno::Reference< css::drawing::XShapes >& rxShapes,
+ const css::awt::Rectangle& rShapeRect ) const override;
+
+ /** Updates the bounding box covering all shapes of this drawing. */
+ virtual void notifyXShapeInserted(
+ const css::uno::Reference< css::drawing::XShape >& rxShape,
+ const css::awt::Rectangle& rShapeRect,
+ const ::oox::vml::ShapeBase& rShape, bool bGroupChild ) override;
+
+private:
+ /** Converts the passed VML textbox text color to an OLE color. */
+ sal_uInt32 convertControlTextColor( const OUString& rTextColor ) const;
+ /** Converts the passed VML textbox font to an ActiveX form control font. */
+ void convertControlFontData(
+ ::oox::ole::AxFontData& rAxFontData, sal_uInt32& rnOleTextColor,
+ const ::oox::vml::TextFontModel& rFontModel ) const;
+ /** Converts the caption, the font settings, and the horizontal alignment
+ from the passed VML textbox to ActiveX form control settings. */
+ void convertControlText(
+ ::oox::ole::AxFontData& rAxFontData, sal_uInt32& rnOleTextColor, OUString& rCaption,
+ const ::oox::vml::TextBox* pTextBox, sal_Int32 nTextHAlign ) const;
+ /** Converts the passed VML shape background formatting to ActiveX control formatting. */
+ void convertControlBackground(
+ ::oox::ole::AxMorphDataModelBase& rAxModel,
+ const ::oox::vml::ShapeBase& rShape ) const;
+
+private:
+ ::oox::ole::ControlConverter maControlConv;
+ ::oox::vml::TextFontModel maListBoxFont;
+};
+
+class VmlDrawingFragment final : public ::oox::vml::DrawingFragment, public WorksheetHelper
+{
+public:
+ explicit VmlDrawingFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+private:
+ virtual void finalizeImport() override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/eeimport.hxx b/sc/source/filter/inc/eeimport.hxx
new file mode 100644
index 000000000..d4ddc31f4
--- /dev/null
+++ b/sc/source/filter/inc/eeimport.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <memory>
+#include <address.hxx>
+#include <filter.hxx>
+#include <map>
+
+class ScDocument;
+class ScEEParser;
+class ScTabEditEngine;
+class SvStream;
+
+struct ScEEParseEntry;
+
+typedef std::map<SCROW, long> RowHeightMap;
+
+class ScEEImport : public ScEEAbsImport
+{
+protected:
+ ScRange maRange;
+ ScDocument* mpDoc;
+ std::unique_ptr<ScTabEditEngine>
+ mpEngine;
+ std::unique_ptr<ScEEParser> // needs mpEngine
+ mpParser; // must reset before mpEngine resets
+ RowHeightMap maRowHeights;
+
+ bool GraphicSize( SCCOL nCol, SCROW nRow,
+ ScEEParseEntry* );
+ void InsertGraphic( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ ScEEParseEntry* );
+public:
+ ScEEImport( ScDocument* pDoc, const ScRange& rRange );
+ virtual ~ScEEImport() override;
+
+ virtual ErrCode Read( SvStream& rStream, const OUString& rBaseURL ) override;
+ virtual ScRange GetRange() override { return maRange; }
+ virtual void WriteToDocument( bool bSizeColsRows = false,
+ double nOutputFactor = 1.0,
+ SvNumberFormatter* pFormatter = nullptr,
+ bool bConvertDate = true ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/eeparser.hxx b/sc/source/filter/inc/eeparser.hxx
new file mode 100644
index 000000000..190caa4ec
--- /dev/null
+++ b/sc/source/filter/inc/eeparser.hxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+#include <vcl/errcode.hxx>
+#include <vcl/graph.hxx>
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+#include <editeng/editdata.hxx>
+#include <optional>
+#include <address.hxx>
+#include <memory>
+#include <vector>
+#include <map>
+
+const char nHorizontal = 1;
+const char nVertical = 2;
+
+struct ScHTMLImage
+{
+ OUString aURL;
+ Size aSize;
+ Point aSpace;
+ OUString aFilterName;
+ std::unique_ptr<Graphic>
+ pGraphic; // is taken over by WriteToDocument
+ char nDir; // 1==hori, 2==verti, 3==both
+
+ ScHTMLImage() :
+ aSize( 0, 0 ), aSpace( 0, 0 ), nDir( nHorizontal )
+ {}
+};
+
+struct ScEEParseEntry
+{
+ SfxItemSet aItemSet;
+ ESelection aSel; // Selection in EditEngine
+ std::optional<OUString>
+ pValStr; // HTML possibly SDVAL string
+ std::optional<OUString>
+ pNumStr; // HTML possibly SDNUM string
+ std::optional<OUString>
+ pName; // HTML possibly anchor/RangeName
+ OUString aAltText; // HTML IMG ALT Text
+ std::vector< std::unique_ptr<ScHTMLImage> > maImageList; // graphics in this cell
+ SCCOL nCol; // relative to the beginning of the parse
+ SCROW nRow;
+ sal_uInt16 nTab; // HTML TableInTable
+ sal_uInt16 nTwips; // RTF ColAdjust etc.
+ SCCOL nColOverlap; // merged cells if >1
+ SCROW nRowOverlap; // merged cells if >1
+ sal_uInt16 nOffset; // HTML PixelOffset
+ sal_uInt16 nWidth; // HTML PixelWidth
+ bool bHasGraphic:1; // HTML any image loaded
+ bool bEntirePara:1; // true = use entire paragraph, false = use selection
+
+ ScEEParseEntry( SfxItemPool* pPool ) :
+ aItemSet( *pPool ),
+ nCol(SCCOL_MAX), nRow(SCROW_MAX), nTab(0),
+ nTwips(0), nColOverlap(1), nRowOverlap(1),
+ nOffset(0), nWidth(0), bHasGraphic(false), bEntirePara(true)
+ {}
+
+ ScEEParseEntry( const SfxItemSet& rItemSet ) :
+ aItemSet( rItemSet ),
+ nCol(SCCOL_MAX), nRow(SCROW_MAX), nTab(0),
+ nTwips(0), nColOverlap(1), nRowOverlap(1),
+ nOffset(0), nWidth(0), bHasGraphic(false), bEntirePara(true)
+ {}
+
+ ~ScEEParseEntry()
+ {
+ maImageList.clear();
+ }
+};
+
+class EditEngine;
+
+typedef std::map<SCCOL, sal_uInt16> ColWidthsMap;
+
+class ScEEParser
+{
+protected:
+ EditEngine* pEdit;
+ rtl::Reference<SfxItemPool> pPool;
+ rtl::Reference<SfxItemPool> pDocPool;
+ std::vector<std::shared_ptr<ScEEParseEntry>> maList;
+ std::shared_ptr<ScEEParseEntry> mxActEntry;
+ ColWidthsMap maColWidths;
+ int nRtfLastToken;
+ SCCOL nColCnt;
+ SCROW nRowCnt;
+ SCCOL nColMax;
+ SCROW nRowMax;
+
+ void NewActEntry( const ScEEParseEntry* );
+
+public:
+ ScEEParser( EditEngine* );
+ virtual ~ScEEParser();
+
+ virtual ErrCode Read( SvStream&, const OUString& rBaseURL ) = 0;
+
+ const ColWidthsMap& GetColWidths() const { return maColWidths; }
+ ColWidthsMap& GetColWidths() { return maColWidths; }
+ void GetDimensions( SCCOL& nCols, SCROW& nRows ) const
+ { nCols = nColMax; nRows = nRowMax; }
+
+ size_t ListSize() const{ return maList.size(); }
+ ScEEParseEntry* ListEntry( size_t index ) { return maList[index].get(); }
+ const ScEEParseEntry* ListEntry( size_t index ) const { return maList[index].get(); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excdefs.hxx b/sc/source/filter/inc/excdefs.hxx
new file mode 100644
index 000000000..149993e1e
--- /dev/null
+++ b/sc/source/filter/inc/excdefs.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+// (0x009B, 0x009D, 0x009E) AUTOFILTER ========================================
+
+// flags
+const sal_uInt16 EXC_AFFLAG_AND = 0x0000;
+const sal_uInt16 EXC_AFFLAG_OR = 0x0001;
+const sal_uInt16 EXC_AFFLAG_ANDORMASK = 0x0003;
+const sal_uInt16 EXC_AFFLAG_SIMPLE1 = 0x0004;
+const sal_uInt16 EXC_AFFLAG_SIMPLE2 = 0x0008;
+const sal_uInt16 EXC_AFFLAG_TOP10 = 0x0010;
+const sal_uInt16 EXC_AFFLAG_TOP10TOP = 0x0020;
+const sal_uInt16 EXC_AFFLAG_TOP10PERC = 0x0040;
+
+// data types
+const sal_uInt8 EXC_AFTYPE_NOTUSED = 0x00;
+const sal_uInt8 EXC_AFTYPE_RK = 0x02;
+const sal_uInt8 EXC_AFTYPE_DOUBLE = 0x04;
+const sal_uInt8 EXC_AFTYPE_STRING = 0x06;
+const sal_uInt8 EXC_AFTYPE_BOOLERR = 0x08;
+const sal_uInt8 EXC_AFTYPE_INVALID = 0x0A;
+const sal_uInt8 EXC_AFTYPE_EMPTY = 0x0C;
+const sal_uInt8 EXC_AFTYPE_NOTEMPTY = 0x0E;
+
+// comparison operands
+const sal_uInt8 EXC_AFOPER_NONE = 0x00;
+const sal_uInt8 EXC_AFOPER_LESS = 0x01;
+const sal_uInt8 EXC_AFOPER_EQUAL = 0x02;
+const sal_uInt8 EXC_AFOPER_LESSEQUAL = 0x03;
+const sal_uInt8 EXC_AFOPER_GREATER = 0x04;
+const sal_uInt8 EXC_AFOPER_NOTEQUAL = 0x05;
+const sal_uInt8 EXC_AFOPER_GREATEREQUAL = 0x06;
+
+// (0x00AE, 0x00AF) SCENARIO, SCENMAN =========================================
+
+#define EXC_SCEN_MAXCELL 32
+
+// defines for change tracking ================================================
+
+inline constexpr OUStringLiteral EXC_STREAM_USERNAMES = u"User Names";
+inline constexpr OUStringLiteral EXC_STREAM_REVLOG = u"Revision Log";
+
+// opcodes
+#define EXC_CHTR_OP_COLFLAG 0x0001
+#define EXC_CHTR_OP_DELFLAG 0x0002
+#define EXC_CHTR_OP_INSROW 0x0000
+#define EXC_CHTR_OP_INSCOL EXC_CHTR_OP_COLFLAG
+#define EXC_CHTR_OP_DELROW EXC_CHTR_OP_DELFLAG
+#define EXC_CHTR_OP_DELCOL (EXC_CHTR_OP_COLFLAG|EXC_CHTR_OP_DELFLAG)
+#define EXC_CHTR_OP_MOVE 0x0004
+#define EXC_CHTR_OP_INSTAB 0x0005
+#define EXC_CHTR_OP_CELL 0x0008
+#define EXC_CHTR_OP_FORMAT 0x000B
+#define EXC_CHTR_OP_UNKNOWN 0xFFFF
+
+// data types
+#define EXC_CHTR_TYPE_MASK 0x0007
+#define EXC_CHTR_TYPE_FORMATMASK 0xFF00
+#define EXC_CHTR_TYPE_EMPTY 0x0000
+#define EXC_CHTR_TYPE_RK 0x0001
+#define EXC_CHTR_TYPE_DOUBLE 0x0002
+#define EXC_CHTR_TYPE_STRING 0x0003
+#define EXC_CHTR_TYPE_BOOL 0x0004
+#define EXC_CHTR_TYPE_FORMULA 0x0005
+
+// accept flags
+#define EXC_CHTR_NOTHING 0x0000
+#define EXC_CHTR_ACCEPT 0x0001
+#define EXC_CHTR_REJECT 0x0003
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excdoc.hxx b/sc/source/filter/inc/excdoc.hxx
new file mode 100644
index 000000000..40748b38f
--- /dev/null
+++ b/sc/source/filter/inc/excdoc.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xeroot.hxx"
+#include "xerecord.hxx"
+#include "excrecds.hxx"
+#include <memory>
+
+// Forwards -
+
+class SvStream;
+class XclExpNote;
+class XclExpStream;
+class XclExpXmlStream;
+class XclExpChangeTrack;
+
+
+class XclExpCellTable;
+
+class ExcTable final : public XclExpRecordBase, public XclExpRoot
+{
+private:
+ typedef XclExpRecordList< ExcBundlesheetBase > ExcBoundsheetList;
+ typedef rtl::Reference< XclExpCellTable > XclExpCellTableRef;
+ typedef XclExpRecordList< XclExpNote > XclExpNoteList;
+ typedef rtl::Reference< XclExpNoteList > XclExpNoteListRef;
+
+ XclExpRecordList<> aRecList;
+ XclExpCellTableRef mxCellTable;
+
+ SCTAB mnScTab; // table number SC document
+ sal_uInt16 nExcTab; // table number Excel document
+
+ XclExpNoteListRef mxNoteList;
+
+ // re-create and forget pRec; delete is done by ExcTable itself!
+ void Add( XclExpRecordBase* pRec );
+
+public:
+ ExcTable( const XclExpRoot& rRoot );
+ ExcTable( const XclExpRoot& rRoot, SCTAB nScTab );
+ virtual ~ExcTable() override;
+
+ void FillAsHeaderBinary( ExcBoundsheetList& rBoundsheetList );
+ void FillAsHeaderXml( ExcBoundsheetList& rBoundsheetList );
+
+ void FillAsTableBinary( SCTAB nCodeNameIdx );
+ void FillAsTableXml();
+
+ void FillAsEmptyTable( SCTAB nCodeNameIdx );
+
+ void Write( XclExpStream& );
+ void WriteXml( XclExpXmlStream& );
+};
+
+class ExcDocument final : protected XclExpRoot
+{
+friend class ExcTable;
+
+private:
+ typedef XclExpRecordList< ExcTable > ExcTableList;
+ typedef XclExpRecordList< ExcBundlesheetBase > ExcBoundsheetList;
+ typedef ExcBoundsheetList::RecordRefType ExcBoundsheetRef;
+
+ ExcTable aHeader;
+
+ ExcTableList maTableList;
+ ExcBoundsheetList maBoundsheetList;
+
+ std::unique_ptr<XclExpChangeTrack> m_xExpChangeTrack;
+
+public:
+ explicit ExcDocument( const XclExpRoot& rRoot );
+ virtual ~ExcDocument() override;
+
+ void ReadDoc();
+ void Write( SvStream& rSvStrm );
+ void WriteXml( XclExpXmlStream& );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excelchartconverter.hxx b/sc/source/filter/inc/excelchartconverter.hxx
new file mode 100644
index 000000000..363966a4c
--- /dev/null
+++ b/sc/source/filter/inc/excelchartconverter.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/drawingml/chart/chartconverter.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox::xls {
+
+class ExcelChartConverter final : public ::oox::drawingml::chart::ChartConverter, public WorkbookHelper
+{
+public:
+ explicit ExcelChartConverter( const WorkbookHelper& rHelper );
+ virtual ~ExcelChartConverter() override;
+
+ /** Creates an external data provider that is able to use spreadsheet data. */
+ virtual void createDataProvider(
+ const css::uno::Reference< css::chart2::XChartDocument >& rxChartDoc ) override;
+
+ /** Creates a data sequence from the passed formula. */
+ virtual css::uno::Reference<css::chart2::data::XDataSequence>
+ createDataSequence(
+ const css::uno::Reference<css::chart2::data::XDataProvider>& rxDataProvider,
+ const oox::drawingml::chart::DataSequenceModel& rDataSeq, const OUString& rRole,
+ const OUString& aRoleQualifier ) override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excelfilter.hxx b/sc/source/filter/inc/excelfilter.hxx
new file mode 100644
index 000000000..2aeb675cd
--- /dev/null
+++ b/sc/source/filter/inc/excelfilter.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/core/xmlfilterbase.hxx>
+
+namespace oox::xls {
+
+class WorkbookGlobals;
+
+class ExcelFilter final : public ::oox::core::XmlFilterBase
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ExcelFilter(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual ~ExcelFilter() override;
+
+ void registerWorkbookGlobals( WorkbookGlobals& rBookGlob );
+ WorkbookGlobals& getWorkbookGlobals() const;
+ void unregisterWorkbookGlobals();
+
+ virtual bool importDocument() override;
+ virtual bool exportDocument() noexcept override;
+
+ virtual const ::oox::drawingml::Theme* getCurrentTheme() const override;
+ virtual ::oox::vml::Drawing* getVmlDrawing() override;
+ virtual ::oox::drawingml::table::TableStyleListPtr getTableStyles() override;
+ virtual ::oox::drawingml::chart::ChartConverter* getChartConverter() override;
+ virtual void useInternalChartDataTable( bool bInternal ) override;
+
+ virtual sal_Bool SAL_CALL filter( const css::uno::Sequence< css::beans::PropertyValue >& rDescriptor ) override;
+
+private:
+ virtual GraphicHelper* implCreateGraphicHelper() const override;
+ virtual ::oox::ole::VbaProject* implCreateVbaProject() const override;
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ WorkbookGlobals* mpBookGlob;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excelhandlers.hxx b/sc/source/filter/inc/excelhandlers.hxx
new file mode 100644
index 000000000..fd029e995
--- /dev/null
+++ b/sc/source/filter/inc/excelhandlers.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/core/fragmenthandler2.hxx>
+#include <oox/core/contexthandler2.hxx>
+#include "workbookhelper.hxx"
+#include "worksheethelper.hxx"
+
+namespace oox::xls {
+
+/** Context handler derived from the WorkbookHelper helper class.
+
+ Used to import contexts in global workbook fragments.
+ */
+class WorkbookContextBase : public ::oox::core::ContextHandler2, public WorkbookHelper
+{
+public:
+ template< typename ParentType >
+ explicit WorkbookContextBase( ParentType& rParent ) :
+ ::oox::core::ContextHandler2( rParent ), WorkbookHelper( rParent ) {}
+};
+
+/** Context handler derived from the WorksheetHelper helper class.
+
+ Used to import contexts in sheet fragments.
+ */
+class WorksheetContextBase : public ::oox::core::ContextHandler2, public WorksheetHelper
+{
+public:
+ template< typename ParentType >
+ explicit WorksheetContextBase( ParentType& rParent ) :
+ ::oox::core::ContextHandler2( rParent ), WorksheetHelper( rParent ) {}
+};
+
+/** Fragment handler derived from the WorkbookHelper helper class.
+
+ Used to import global workbook fragments.
+ */
+class WorkbookFragmentBase : public ::oox::core::FragmentHandler2, public WorkbookHelper
+{
+public:
+ explicit WorkbookFragmentBase(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+};
+
+/** Fragment handler derived from the WorksheetHelper helper class.
+
+ Used to import sheet fragments.
+ */
+class WorksheetFragmentBase : public ::oox::core::FragmentHandler2, public WorksheetHelper
+{
+public:
+ explicit WorksheetFragmentBase(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+};
+
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excelvbaproject.hxx b/sc/source/filter/inc/excelvbaproject.hxx
new file mode 100644
index 000000000..7c1047291
--- /dev/null
+++ b/sc/source/filter/inc/excelvbaproject.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/ole/vbaproject.hxx>
+
+namespace com::sun::star {
+ namespace sheet { class XSpreadsheetDocument; }
+}
+
+namespace oox::xls {
+
+/** Special implementation of the VBA project for the Excel filters. */
+class ExcelVbaProject final : public ::oox::ole::VbaProject
+{
+public:
+ explicit ExcelVbaProject(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::sheet::XSpreadsheetDocument >& rxDocument );
+
+private:
+ /** Adds dummy modules for sheets without imported code name. */
+ virtual void prepareImport() override;
+
+ css::uno::Reference< css::sheet::XSpreadsheetDocument >
+ mxDocument;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excform.hxx b/sc/source/filter/inc/excform.hxx
new file mode 100644
index 000000000..4bc19b185
--- /dev/null
+++ b/sc/source/filter/inc/excform.hxx
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlformula.hxx"
+#include "xiroot.hxx"
+#include "xltools.hxx"
+#include "formel.hxx"
+
+#include <vector>
+
+class ScFormulaCell;
+class ScRangeList;
+
+class ExcelToSc : public ExcelConverterBase, protected XclImpRoot
+{
+protected:
+ enum ExtensionType { EXTENSION_ARRAY, EXTENSION_NLR, EXTENSION_MEMAREA };
+ typedef ::std::vector< ExtensionType > ExtensionTypeVec;
+
+ static const sal_uInt16 nRowMask;
+
+ XclFunctionProvider maFuncProv;
+ const XclBiff meBiff;
+
+ void DoMulArgs( DefTokenId eId, sal_uInt8 nNumArgs );
+
+ void ExcRelToScRel( sal_uInt16 nRow, sal_uInt8 nCol, ScSingleRefData&, const bool bName );
+
+public:
+ ExcelToSc( XclImpRoot& rRoot );
+ virtual ~ExcelToSc() override;
+ virtual ConvErr Convert( std::unique_ptr<ScTokenArray>&, XclImpStream& rStrm, std::size_t nFormulaLen,
+ bool bAllowArrays, const FORMULA_TYPE eFT = FT_CellFormula ) override;
+
+ virtual ConvErr Convert( ScRangeListTabs&, XclImpStream& rStrm, std::size_t nFormulaLen, SCTAB nTab, const FORMULA_TYPE eFT = FT_CellFormula ) override;
+
+ virtual void ConvertExternName( std::unique_ptr<ScTokenArray>& rpArray, XclImpStream& rStrm, std::size_t nFormulaLen,
+ const OUString& rUrl, const ::std::vector<OUString>& rTabNames );
+
+ virtual void GetAbsRefs( ScRangeList& rRangeList, XclImpStream& rStrm, std::size_t nLen );
+
+ std::unique_ptr<ScTokenArray> GetDummy();
+ std::unique_ptr<ScTokenArray> GetBoolErr( XclBoolError );
+
+ static bool ReadSharedFormulaPosition( XclImpStream& rStrm, SCCOL& rCol, SCROW& rRow );
+ const ScTokenArray* GetSharedFormula( const ScAddress& rRefPos ) const;
+
+ static void SetError( ScFormulaCell& rCell, const ConvErr eErr );
+
+ static inline bool IsComplColRange( const sal_uInt16 nCol1, const sal_uInt16 nCol2 );
+ static inline bool IsComplRowRange( const sal_uInt16 nRow1, const sal_uInt16 nRow2 );
+
+ void SetComplCol( ScComplexRefData& );
+ void SetComplRow( ScComplexRefData& );
+
+ void ReadExtensions( const ExtensionTypeVec& rExtensions,
+ XclImpStream& aIn );
+ void ReadExtensionArray( unsigned int n,
+ XclImpStream& aIn );
+ static void ReadExtensionNlr( XclImpStream& aIn );
+ void ReadExtensionMemArea( XclImpStream& aIn );
+};
+
+inline bool ExcelToSc::IsComplColRange( const sal_uInt16 nCol1, const sal_uInt16 nCol2 )
+{
+ return ( nCol1 == 0x00 ) && ( nCol2 == 0xFF );
+}
+
+inline bool ExcelToSc::IsComplRowRange( const sal_uInt16 nRow1, const sal_uInt16 nRow2 )
+{
+ return ( ( nRow1 & 0x3FFF ) == 0x0000 ) && ( ( nRow2 & 0x3FFF ) == 0x3FFF );
+}
+
+class XclImpLinkManager;
+class XclImpExtName;
+
+class ExcelToSc8 : public ExcelToSc
+{
+public:
+
+ struct ExternalTabInfo
+ {
+ ScRange maRange;
+ OUString maTabName;
+ sal_uInt16 mnFileId;
+ bool mbExternal;
+
+ ExternalTabInfo();
+ };
+
+private:
+ const XclImpLinkManager& rLinkMan;
+
+ void ExcRelToScRel8( sal_uInt16 nRow, sal_uInt16 nCol, ScSingleRefData&,
+ const bool bName );
+
+ bool GetExternalFileIdFromXti( sal_uInt16 nIxti, sal_uInt16& rFileId ) const;
+
+ virtual bool Read3DTabReference( sal_uInt16 nIxti, SCTAB& rFirstTab, SCTAB& rLastTab, ExternalTabInfo& rExtInfo );
+
+ bool HandleOleLink(sal_uInt16 nXtiIndex, const XclImpExtName& rExtName, ExternalTabInfo& rExtInfo);
+public:
+ ExcelToSc8( XclImpRoot& rRoot );
+ virtual ~ExcelToSc8() override;
+
+ virtual ConvErr Convert( std::unique_ptr<ScTokenArray>& rpTokArray, XclImpStream& rStrm, std::size_t nFormulaLen, bool bAllowArrays, const FORMULA_TYPE eFT = FT_CellFormula ) override;
+
+ virtual ConvErr Convert( ScRangeListTabs&, XclImpStream& rStrm, std::size_t nFormulaLen, SCTAB nTab, const FORMULA_TYPE eFT = FT_CellFormula ) override;
+
+ virtual void ConvertExternName( std::unique_ptr<ScTokenArray>& rpArray, XclImpStream& rStrm, std::size_t nFormulaLen,
+ const OUString& rUrl, const ::std::vector<OUString>& rTabNames ) override;
+
+ static inline bool IsComplRowRange( const sal_uInt16 nRow1, const sal_uInt16 nRow2 );
+
+ virtual void GetAbsRefs( ScRangeList& rRangeList, XclImpStream& rStrm, std::size_t nLen ) override;
+};
+
+inline bool ExcelToSc8::IsComplRowRange( const sal_uInt16 nRow1, const sal_uInt16 nRow2 )
+{
+ return ( nRow1 == 0x0000 ) && ( nRow2 == 0xFFFF );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excimp8.hxx b/sc/source/filter/inc/excimp8.hxx
new file mode 100644
index 000000000..4096ca068
--- /dev/null
+++ b/sc/source/filter/inc/excimp8.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "imp_op.hxx"
+#include "root.hxx"
+#include "excscen.hxx"
+#include <queryparam.hxx>
+
+class ScDBData;
+class XclImpStream;
+
+class ImportExcel8 : public ImportExcel
+{
+public:
+ ImportExcel8( XclImpRootData& rImpData, SvStream& rStrm );
+ virtual ~ImportExcel8() override;
+
+ virtual ErrCode Read() override;
+
+ void Calccount(); // 0x0C
+ void Precision(); // 0x0E
+ void Delta(); // 0x10
+ void Iteration(); // 0x11
+ void Boundsheet(); // 0x85
+ void FilterMode(); // 0x9B
+ void AutoFilterInfo(); // 0x9D
+ void AutoFilter(); // 0x9E
+ void Scenman(); // 0xAE
+ void Scenario(); // 0xAF
+ void ReadBasic(); // 0xD3
+ void Labelsst(); // 0xFD
+
+ void FeatHdr(); // 0x0867
+ void Feat(); // 0x0868
+
+ virtual void EndSheet() override;
+ virtual void PostDocLoad() override;
+
+private:
+ ExcScenarioList maScenList;
+};
+
+
+class XclImpAutoFilterData : private ExcRoot
+{
+private:
+ ScDBData* pCurrDBData;
+ ScQueryParam aParam;
+ ScRange aCriteriaRange;
+ bool bActive:1;
+ bool bCriteria:1;
+ bool bAutoOrAdvanced:1;
+
+ void SetCellAttribs();
+ void InsertQueryParam();
+
+protected:
+public:
+ XclImpAutoFilterData(
+ RootData* pRoot,
+ const ScRange& rRange);
+
+ bool IsActive() const { return bActive; }
+ bool IsFiltered() const { return bAutoOrAdvanced; }
+ SCTAB Tab() const { return aParam.nTab; }
+ SCCOL StartCol() const { return aParam.nCol1; }
+ SCROW StartRow() const { return aParam.nRow1; }
+ SCCOL EndCol() const { return aParam.nCol2; }
+ SCROW EndRow() const { return aParam.nRow2; }
+
+ void ReadAutoFilter( XclImpStream& rStrm, svl::SharedStringPool& rPool );
+
+ void Activate() { bActive = true; }
+ void SetAdvancedRange( const ScRange* pRange );
+ void SetExtractPos( const ScAddress& rAddr );
+ void SetAutoOrAdvanced() { bAutoOrAdvanced = true; }
+ void Apply();
+ void EnableRemoveFilter();
+};
+
+class XclImpAutoFilterBuffer
+{
+public:
+
+ void Insert( RootData* pRoot, const ScRange& rRange);
+ void AddAdvancedRange( const ScRange& rRange );
+ void AddExtractPos( const ScRange& rRange );
+ void Apply();
+
+ XclImpAutoFilterData* GetByTab( SCTAB nTab );
+
+private:
+ typedef std::shared_ptr<XclImpAutoFilterData> XclImpAutoFilterSharePtr;
+ std::vector<XclImpAutoFilterSharePtr> maFilters;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excrecds.hxx b/sc/source/filter/inc/excrecds.hxx
new file mode 100644
index 000000000..2e4885a88
--- /dev/null
+++ b/sc/source/filter/inc/excrecds.hxx
@@ -0,0 +1,457 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/color.hxx>
+
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+#include "excdefs.hxx"
+
+//------------------------------------------------------------------ Forwards -
+
+class ScDBData;
+struct ScQueryEntry;
+
+//----------------------------------------------------------- class ExcRecord -
+
+class ExcRecord : public XclExpRecord
+{
+public:
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ virtual sal_uInt16 GetNum() const = 0;
+ virtual std::size_t GetLen() const = 0;
+
+protected:
+ virtual void SaveCont( XclExpStream& rStrm );
+
+private:
+ /** Writes the body of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+//--------------------------------------------------------- class ExcEmptyRec -
+
+class ExcEmptyRec : public ExcRecord
+{
+private:
+protected:
+public:
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+//--------------------------------------------------------- class ExcDummyRec -
+
+class ExcDummyRec : public ExcRecord
+{
+protected:
+public:
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual sal_uInt16 GetNum() const override;
+ virtual const sal_uInt8* GetData() const = 0; // byte data must contain header and body
+};
+
+//------------------------------------------------------- class ExcBoolRecord -
+// stores bool as 16bit val ( 0x0000 | 0x0001 )
+
+class ExcBoolRecord : public ExcRecord
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+protected:
+ bool bVal;
+
+ ExcBoolRecord() : bVal( false ) {}
+
+public:
+ virtual std::size_t GetLen() const override;
+};
+
+//--------------------------------------------------------- class ExcBof_Base -
+
+class ExcBof_Base : public ExcRecord
+{
+private:
+protected:
+ sal_uInt16 nDocType;
+ sal_uInt16 nVers;
+ sal_uInt16 nRupBuild;
+ sal_uInt16 nRupYear;
+public:
+ ExcBof_Base();
+};
+
+//-------------------------------------------------------------- class ExcBof -
+// Header Record for WORKSHEETS
+
+class ExcBof : public ExcBof_Base
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ ExcBof();
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+//------------------------------------------------------------- class ExcBofW -
+// Header Record for WORKBOOKS
+
+class ExcBofW : public ExcBof_Base
+{
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ ExcBofW();
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+//-------------------------------------------------------------- class ExcEof -
+
+class ExcEof : public ExcRecord
+{
+private:
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+//--------------------------------------------------------- class ExcDummy_00 -
+// INTERFACEHDR to FNGROUPCOUNT (see excrecds.cxx)
+
+class ExcDummy_00 : public ExcDummyRec
+{
+private:
+ static const sal_uInt8 pMyData[];
+ static const std::size_t nMyLen;
+public:
+ virtual std::size_t GetLen() const override;
+ virtual const sal_uInt8* GetData() const override;
+};
+
+// EXC_ID_WINDOWPROTECTION
+class XclExpWindowProtection : public XclExpBoolRecord
+{
+ public:
+ XclExpWindowProtection(bool bValue);
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// EXC_ID_PROTECT Document Protection
+class XclExpProtection : public XclExpBoolRecord
+{
+ public:
+ XclExpProtection(bool bValue);
+};
+
+class XclExpSheetProtection : public XclExpProtection
+{
+ SCTAB mnTab;
+ public:
+ XclExpSheetProtection(bool bValue, SCTAB nTab);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpPassHash : public XclExpRecord
+{
+public:
+ XclExpPassHash(const css::uno::Sequence<sal_Int8>& aHash);
+ virtual ~XclExpPassHash() override;
+
+private:
+ virtual void WriteBody(XclExpStream& rStrm) override;
+
+private:
+ sal_uInt16 mnHash;
+};
+
+//-------------------------------------------------------- class ExcDummy_04x -
+// PASSWORD to BOOKBOOL (see excrecds.cxx), no 1904
+
+class ExcDummy_040 : public ExcDummyRec
+{
+private:
+ static const sal_uInt8 pMyData[];
+ static const std::size_t nMyLen;
+public:
+ virtual std::size_t GetLen() const override;
+ virtual const sal_uInt8* GetData() const override;
+};
+
+class ExcDummy_041 : public ExcDummyRec
+{
+private:
+ static const sal_uInt8 pMyData[];
+ static const std::size_t nMyLen;
+public:
+ virtual std::size_t GetLen() const override;
+ virtual const sal_uInt8* GetData() const override;
+};
+
+//------------------------------------------------------------- class Exc1904 -
+
+class Exc1904 : public ExcBoolRecord
+{
+public:
+ Exc1904( const ScDocument& rDoc );
+ virtual sal_uInt16 GetNum() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ bool bDateCompatibility;
+};
+
+//------------------------------------------------------ class ExcBundlesheet -
+
+class ExcBundlesheetBase : public ExcRecord
+{
+protected:
+ sal_uInt64 m_nStrPos;
+ sal_uInt64 m_nOwnPos; // Position after # and Len
+ sal_uInt16 nGrbit;
+ SCTAB nTab;
+
+ ExcBundlesheetBase();
+
+public:
+ ExcBundlesheetBase( const RootData& rRootData, SCTAB nTab );
+
+ void SetStreamPos(sal_uInt64 const nStrPos) { m_nStrPos = nStrPos; }
+ void UpdateStreamPos( XclExpStream& rStrm );
+
+ virtual sal_uInt16 GetNum() const override;
+};
+
+class ExcBundlesheet : public ExcBundlesheetBase
+{
+private:
+ OString aName;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ ExcBundlesheet( const RootData& rRootData, SCTAB nTab );
+ virtual std::size_t GetLen() const override;
+};
+
+//--------------------------------------------------------- class ExcDummy_02 -
+// sheet dummies: CALCMODE to SETUP
+
+class ExcDummy_02a : public ExcDummyRec
+{
+private:
+ static const sal_uInt8 pMyData[];
+ static const std::size_t nMyLen;
+public:
+ virtual std::size_t GetLen() const override;
+ virtual const sal_uInt8* GetData() const override;
+};
+
+/** This record contains the Windows country IDs for the UI and document language. */
+class XclExpCountry : public XclExpRecord
+{
+public:
+ explicit XclExpCountry( const XclExpRoot& rRoot );
+
+private:
+ sal_uInt16 mnUICountry; /// The UI country ID.
+ sal_uInt16 mnDocCountry; /// The document country ID.
+
+ /** Writes the body of the COUNTRY record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+// XclExpWsbool ===============================================================
+
+class XclExpWsbool : public XclExpUInt16Record
+{
+public:
+ explicit XclExpWsbool( bool bFitToPages );
+};
+
+/**
+ * Save sheetPr element and its children for xlsx export.
+ */
+class XclExpXmlSheetPr : public XclExpRecordBase
+{
+public:
+ explicit XclExpXmlSheetPr(
+ bool bFitToPages, SCTAB nScTab, const Color& rTabColor, XclExpFilterManager* pManager );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ SCTAB mnScTab;
+ XclExpFilterManager* mpManager;
+ bool mbFitToPage;
+ Color maTabColor;
+};
+
+class XclExpFiltermode : public XclExpEmptyRecord
+{
+public:
+ explicit XclExpFiltermode();
+};
+
+class XclExpAutofilterinfo : public XclExpUInt16Record
+{
+public:
+ explicit XclExpAutofilterinfo( const ScAddress& rStartPos, SCCOL nScCol );
+
+ const ScAddress& GetStartPos() const { return maStartPos; }
+ SCCOL GetColCount() const { return static_cast< SCCOL >( GetValue() ); }
+
+private:
+ ScAddress maStartPos;
+};
+
+class ExcFilterCondition
+{
+private:
+ sal_uInt8 nType;
+ sal_uInt8 nOper;
+ std::unique_ptr<XclExpString>
+ pText;
+
+protected:
+public:
+ ExcFilterCondition();
+ ~ExcFilterCondition();
+
+ bool IsEmpty() const { return (nType == EXC_AFTYPE_NOTUSED); }
+ std::size_t GetTextBytes() const;
+
+ void SetCondition( sal_uInt8 nTp, sal_uInt8 nOp, const OUString* pT );
+
+ void Save( XclExpStream& rStrm );
+ void SaveXml( XclExpXmlStream& rStrm );
+ void SaveText( XclExpStream& rStrm );
+};
+
+class XclExpAutofilter : public XclExpRecord, protected XclExpRoot
+{
+private:
+ enum FilterType
+ {
+ FilterCondition,
+ MultiValue,
+ BlankValue,
+ ColorValue
+ };
+ FilterType meType;
+ sal_uInt16 nCol;
+ sal_uInt16 nFlags;
+ bool bHasBlankValue;
+ ExcFilterCondition aCond[ 2 ];
+ std::vector<std::pair<OUString, bool>> maMultiValues; // first->values, second->bDateFormat
+ std::vector<std::pair<::Color, bool>> maColorValues; // first->Color, second->bIsBackgroundColor (vs. TextColor)
+
+ bool AddCondition( ScQueryConnect eConn, sal_uInt8 nType,
+ sal_uInt8 nOp, const OUString* pText, bool bSimple = false );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+public:
+ XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC );
+
+ sal_uInt16 GetCol() const { return nCol; }
+ bool HasTop10() const { return ::get_flag( nFlags, EXC_AFFLAG_TOP10 ); }
+
+ bool HasCondition() const;
+ bool AddEntry( const ScQueryEntry& rEntry );
+ void AddMultiValueEntry( const ScQueryEntry& rEntry );
+ void AddColorEntry( const ScQueryEntry& rEntry );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class ExcAutoFilterRecs : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** @param pDefinedData
+ If nullptr, obtain anonymous ScDBData from sheet nTab.
+ Else, use defined database range; used with XclExpTables.
+ */
+ explicit ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const ScDBData* pDefinedData );
+ virtual ~ExcAutoFilterRecs() override;
+
+ void AddObjRecs();
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ bool HasFilterMode() const;
+
+private:
+ XclExpAutofilter* GetByCol( SCCOL nCol ); // always 0-based
+ bool IsFiltered( SCCOL nCol );
+
+private:
+ typedef XclExpRecordList< XclExpAutofilter > XclExpAutofilterList;
+ typedef XclExpAutofilterList::RecordRefType XclExpAutofilterRef;
+
+ XclExpAutofilterList maFilterList;
+ rtl::Reference<XclExpFiltermode> m_pFilterMode;
+ rtl::Reference<XclExpAutofilterinfo> m_pFilterInfo;
+ ScRange maRef;
+ bool mbAutoFilter;
+
+ ScRange maSortRef; // sort area without headers
+ std::vector< std::tuple<ScRange, OUString, bool> > maSortCustomList; // sorted column with list of sorted items
+};
+
+/** Sheet filter manager. Contains auto filters or advanced filters from all sheets. */
+class XclExpFilterManager : protected XclExpRoot
+{
+public:
+ explicit XclExpFilterManager( const XclExpRoot& rRoot );
+
+ /** Creates the filter records for the specified sheet.
+ @descr Creates and inserts related built-in NAME records. Therefore this
+ function is called from the name buffer itself. */
+ void InitTabFilter( SCTAB nScTab );
+
+ /** Returns a record object containing all filter records for the specified sheet. */
+ XclExpRecordRef CreateRecord( SCTAB nScTab );
+
+ /** Returns whether or not FilterMode is present */
+ bool HasFilterMode( SCTAB nScTab );
+
+private:
+ using XclExpRoot::CreateRecord;
+
+ typedef rtl::Reference< ExcAutoFilterRecs > XclExpTabFilterRef;
+ typedef ::std::map< SCTAB, XclExpTabFilterRef > XclExpTabFilterMap;
+
+ XclExpTabFilterMap maFilterMap;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/excscen.hxx b/sc/source/filter/inc/excscen.hxx
new file mode 100644
index 000000000..81e97e1b1
--- /dev/null
+++ b/sc/source/filter/inc/excscen.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <rtl/ustring.hxx>
+
+struct RootData;
+class XclImpRoot;
+class XclImpStream;
+
+class ExcScenarioCell
+{
+private:
+ OUString aValue;
+public:
+ const sal_uInt16 nCol;
+ const sal_uInt16 nRow;
+
+ ExcScenarioCell( const sal_uInt16 nC, const sal_uInt16 nR );
+
+ void SetValue( const OUString& rVal ) { aValue = rVal; }
+
+ const OUString& GetValue() const { return aValue; }
+};
+
+class ExcScenario final
+{
+public:
+ ExcScenario( XclImpStream& rIn, const RootData& rRoot );
+
+ void Apply( const XclImpRoot& rRoot, const bool bLast );
+
+private:
+ OUString aName;
+ OUString aComment;
+ sal_uInt8 nProtected;
+ const sal_uInt16 nTab;
+ std::vector<ExcScenarioCell> aEntries;
+};
+
+struct ExcScenarioList
+{
+ ExcScenarioList () : nLastScenario(0) {}
+
+ void Apply( const XclImpRoot& rRoot );
+
+ sal_uInt16 nLastScenario;
+ std::vector< std::unique_ptr<ExcScenario> > aEntries;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/exp_op.hxx b/sc/source/filter/inc/exp_op.hxx
new file mode 100644
index 000000000..87ed95b2e
--- /dev/null
+++ b/sc/source/filter/inc/exp_op.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/stream.hxx>
+#include <memory>
+#include "xeroot.hxx"
+
+class ExcDocument;
+
+class ExportTyp
+{
+protected:
+ ~ExportTyp() {}
+
+ SvStream& aOut;
+public:
+ ExportTyp( SvStream& aStream ) : aOut( aStream ) {}
+
+ virtual ErrCode Write() = 0;
+};
+
+class ExportBiff5 : public ExportTyp, protected XclExpRoot
+{
+private:
+ std::unique_ptr<ExcDocument>
+ pExcDoc;
+
+protected:
+ RootData* pExcRoot;
+
+public:
+ ExportBiff5( XclExpRootData& rExpData, SvStream& rStrm );
+ virtual ~ExportBiff5() override;
+ ErrCode Write() override;
+};
+
+class ExportBiff8 : public ExportBiff5
+{
+public:
+ ExportBiff8( XclExpRootData& rExpData, SvStream& rStrm );
+ virtual ~ExportBiff8() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/expbase.hxx b/sc/source/filter/inc/expbase.hxx
new file mode 100644
index 000000000..84cc558cb
--- /dev/null
+++ b/sc/source/filter/inc/expbase.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <address.hxx>
+
+class SvNumberFormatter;
+class SvStream;
+class ScFieldEditEngine;
+
+class ScExportBase
+{
+protected:
+
+ SvStream& rStrm;
+ ScRange aRange;
+ ScDocument* pDoc;
+ SvNumberFormatter* pFormatter;
+ std::unique_ptr<ScFieldEditEngine>
+ pEditEngine;
+
+public:
+
+ ScExportBase( SvStream&, ScDocument*, const ScRange& );
+ virtual ~ScExportBase();
+
+ // Trim borders of hidden Cols/Rows,
+ // return: sal_True if range exists
+ // Start/End/Col/Row must have valid starting values
+ bool TrimDataArea( SCTAB nTab, SCCOL& nStartCol,
+ SCROW& nStartRow, SCCOL& nEndCol, SCROW& nEndRow ) const;
+
+ // Get Data Area of a table,
+ // adjust borders of hidden Cols/Rows,
+ // return: sal_True if range exists
+ bool GetDataArea( SCTAB nTab, SCCOL& nStartCol,
+ SCROW& nStartRow, SCCOL& nEndCol, SCROW& nEndRow ) const;
+
+ // table does not exist or is empty
+ bool IsEmptyTable( SCTAB nTab ) const;
+
+ ScFieldEditEngine& GetEditEngine() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/export/SparklineExt.hxx b/sc/source/filter/inc/export/SparklineExt.hxx
new file mode 100644
index 000000000..f2bff1c7d
--- /dev/null
+++ b/sc/source/filter/inc/export/SparklineExt.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <map>
+#include <rangelst.hxx>
+#include <Sparkline.hxx>
+#include <SparklineAttributes.hxx>
+
+#include <sax/fastattribs.hxx>
+
+#include <xerecord.hxx>
+#include <xeroot.hxx>
+#include <xeextlst.hxx>
+
+namespace xcl::exp
+{
+/** Export for sparkline type of <ext> element - top sparkline element. */
+class SparklineExt : public XclExpExt
+{
+public:
+ SparklineExt(const XclExpRoot& rRoot);
+
+ void SaveXml(XclExpXmlStream& rStream) override;
+ void addSparklineGroup(XclExpXmlStream& rStream, sc::SparklineGroup& rSparklineGroup,
+ std::vector<std::shared_ptr<sc::Sparkline>> const& rSparklines);
+
+ static void
+ addSparklineGroupAttributes(rtl::Reference<sax_fastparser::FastAttributeList>& pAttrList,
+ sc::SparklineAttributes& rSparklineAttributes);
+ static void addSparklineGroupColors(XclExpXmlStream& rStream,
+ sc::SparklineAttributes& rSparklineAttributes);
+
+ XclExpExtType GetType() override { return XclExpExtSparklineType; }
+};
+
+/** Determines if sparklines needs to be exported and initiates the export. */
+class SparklineBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit SparklineBuffer(const XclExpRoot& rRoot, const XclExtLstRef& xExtLst);
+};
+
+} // end namespace xcl::exp
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/externallinkbuffer.hxx b/sc/source/filter/inc/externallinkbuffer.hxx
new file mode 100644
index 000000000..374a54b38
--- /dev/null
+++ b/sc/source/filter/inc/externallinkbuffer.hxx
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include <com/sun/star/sheet/ExternalLinkInfo.hpp>
+#include <oox/helper/containerhelper.hxx>
+#include "defnamesbuffer.hxx"
+#include "formulabase.hxx"
+
+namespace com::sun::star {
+ namespace sheet { struct DDEItemInfo; }
+ namespace sheet { class XDDELink; }
+ namespace sheet { class XExternalDocLink; }
+ namespace sheet { class XExternalSheetCache; }
+}
+
+namespace oox::core {
+ class Relations;
+}
+
+namespace oox::xls {
+
+struct ExternalNameModel
+{
+ bool mbNotify; /// Notify application on data change.
+ bool mbPreferPic; /// Picture link.
+ bool mbStdDocName; /// Name is the StdDocumentName for DDE.
+ bool mbOleObj; /// Name is an OLE object.
+ bool mbIconified; /// Iconified object link.
+
+ explicit ExternalNameModel();
+};
+
+class ExternalLink;
+
+class ExternalName : public DefinedNameBase
+{
+public:
+ explicit ExternalName( const ExternalLink& rParentLink );
+
+ /** Appends the passed value to the result set. */
+ template< typename Type >
+ void appendResultValue( const Type& rValue )
+ { if( maCurrIt != maResults.end() ) (*maCurrIt++) <<= rValue; }
+
+ /** Imports the definedName element. */
+ void importDefinedName( const AttributeList& rAttribs );
+ /** Imports the ddeItem element describing an item of a DDE link. */
+ void importDdeItem( const AttributeList& rAttribs );
+ /** Imports the values element containing the size of the DDE result matrix. */
+ void importValues( const AttributeList& rAttribs );
+ /** Imports the oleItem element describing an object of an OLE link. */
+ void importOleItem( const AttributeList& rAttribs );
+
+ /** Imports the EXTERNALNAME record containing the name (only). */
+ void importExternalName( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALNAMEFLAGS record containing the settings of an external name. */
+ void importExternalNameFlags( SequenceInputStream& rStrm );
+ /** Imports the DDEITEMVALUES record containing the size of the DDE result matrix. */
+ void importDdeItemValues( SequenceInputStream& rStrm );
+ /** Imports the DDEITEM_BOOL record containing a boolean value in a link result. */
+ void importDdeItemBool( SequenceInputStream& rStrm );
+ /** Imports the DDEITEM_DOUBLE record containing a double value in a link result. */
+ void importDdeItemDouble( SequenceInputStream& rStrm );
+ /** Imports the DDEITEM_ERROR record containing an error code in a link result. */
+ void importDdeItemError( SequenceInputStream& rStrm );
+ /** Imports the DDEITEM_STRING record containing a string in a link result. */
+ void importDdeItemString( SequenceInputStream& rStrm );
+
+ /** Returns the DDE item info needed by the XML formula parser. */
+ bool getDdeItemInfo(
+ css::sheet::DDEItemInfo& orItemInfo ) const;
+
+ /** Returns the complete DDE link data of this DDE item. */
+ bool getDdeLinkData(
+ OUString& orDdeServer,
+ OUString& orDdeTopic,
+ OUString& orDdeItem );
+
+private:
+ /** Sets the size of the result matrix. */
+ void setResultSize( sal_Int32 nColumns, sal_Int32 nRows );
+
+private:
+ typedef Matrix< css::uno::Any > ResultMatrix;
+
+ const ExternalLink& mrParentLink; /// External link this name belongs to.
+ ExternalNameModel maExtNameModel; /// Additional name data.
+ ResultMatrix maResults; /// DDE/OLE link results.
+ ResultMatrix::iterator maCurrIt; /// Current position in result matrix.
+ css::uno::Reference< css::sheet::XDDELink >
+ mxDdeLink; /// Interface of a DDE link.
+ bool mbDdeLinkCreated; /// True = already tried to create the DDE link.
+};
+
+typedef std::shared_ptr< ExternalName > ExternalNameRef;
+
+/** Contains indexes for a range of sheets in the spreadsheet document. */
+class LinkSheetRange
+{
+public:
+ explicit LinkSheetRange() { setDeleted(); }
+
+ /** Sets this struct to deleted state. */
+ void setDeleted();
+ /** Sets this struct to "use current sheet" state. */
+ void setSameSheet();
+ /** Sets the passed absolute sheet range to the members of this struct. */
+ void setRange( sal_Int32 nFirst, sal_Int32 nLast );
+ /** Sets the passed external sheet cache range to the members of this struct. */
+ void setExternalRange( sal_Int32 nDocLink, sal_Int32 nFirst, sal_Int32 nLast );
+
+ /** Returns true, if the sheet indexes are valid and different. */
+ bool isDeleted() const { return mnFirst < 0; }
+ /** Returns true, if the sheet range points to an external document. */
+ bool isExternal() const { return !isDeleted() && (meType == LINKSHEETRANGE_EXTERNAL); }
+ /** Returns true, if the sheet indexes are valid and different. */
+ bool isSameSheet() const { return meType == LINKSHEETRANGE_SAMESHEET; }
+ /** Returns true, if the sheet indexes are valid and different. */
+ bool is3dRange() const { return (0 <= mnFirst) && (mnFirst < mnLast); }
+
+ sal_Int32 getDocLinkIndex() const { return mnDocLink; }
+ sal_Int32 getFirstSheet() const { return mnFirst; }
+ sal_Int32 getLastSheet() const { return mnLast; }
+
+private:
+ enum LinkSheetRangeType
+ {
+ LINKSHEETRANGE_INTERNAL, /// Sheet range in the own document.
+ LINKSHEETRANGE_EXTERNAL, /// Sheet range in an external document.
+ LINKSHEETRANGE_SAMESHEET /// Current sheet depending on context.
+ };
+
+ LinkSheetRangeType meType; /// Link sheet range type.
+ sal_Int32 mnDocLink; /// Document link token index for external links.
+ sal_Int32 mnFirst; /// Index of the first sheet or index of first external sheet cache.
+ sal_Int32 mnLast; /// Index of the last sheet or index of last external sheet cache.
+};
+
+enum class ExternalLinkType
+{
+ Self, /// Link refers to the current workbook.
+ Same, /// Link refers to the current sheet.
+ External, /// Link refers to an external spreadsheet document.
+ // let's ignore xlStartup and xlAlternateStartup for now
+ PathMissing, /// Just for round-tripping (FIXME: Functionality not actually implemented after all.)
+ Library, /// Link refers to an external add-in.
+ DDE, /// DDE link.
+ OLE, /// OLE link.
+ Unknown /// Unknown or unsupported link type.
+};
+
+template< typename charT, typename traits >
+inline std::basic_ostream<charT, traits> & operator <<(
+ std::basic_ostream<charT, traits> & stream, const ExternalLinkType& type )
+{
+ switch (type)
+ {
+ case ExternalLinkType::Self: return stream << "self";
+ case ExternalLinkType::Same: return stream << "same";
+ case ExternalLinkType::External: return stream << "external";
+ case ExternalLinkType::PathMissing: return stream << "pathmissing";
+ case ExternalLinkType::Library: return stream << "library";
+ case ExternalLinkType::DDE: return stream << "dde";
+ case ExternalLinkType::OLE: return stream << "ole";
+ case ExternalLinkType::Unknown: return stream << "unknown";
+ default: return stream << static_cast<int>(type);
+ }
+}
+
+class ExternalLink : public WorkbookHelper
+{
+public:
+ explicit ExternalLink( const WorkbookHelper& rHelper );
+
+ /** Imports the externalReference element containing the relation identifier. */
+ void importExternalReference( const AttributeList& rAttribs );
+ /** Imports the externalBook element describing an externally linked document. */
+ void importExternalBook( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+ /** Imports the sheetName element containing the sheet name in an externally linked document. */
+ void importSheetName( const AttributeList& rAttribs );
+ /** Imports the definedName element describing an external name. */
+ void importDefinedName( const AttributeList& rAttribs );
+ /** Imports the ddeLink element describing a DDE link. */
+ void importDdeLink( const AttributeList& rAttribs );
+ /** Imports the ddeItem element describing an item of a DDE link. */
+ ExternalNameRef importDdeItem( const AttributeList& rAttribs );
+ /** Imports the oleLink element describing an OLE link. */
+ void importOleLink( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+ /** Imports the oleItem element describing an object of an OLE link. */
+ ExternalNameRef importOleItem( const AttributeList& rAttribs );
+
+ /** Imports the EXTERNALBOOK record describing an externally linked document, DDE link, or OLE link. */
+ void importExternalBook( const ::oox::core::Relations& rRelations, SequenceInputStream& rStrm );
+ /** Imports the EXTSHEETNAMES record containing the sheet names in an externally linked document. */
+ void importExtSheetNames( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALNAME record describing an external name. */
+ ExternalNameRef importExternalName( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALREF record from the passed stream. */
+ void importExternalRef( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSELF record from the passed stream. */
+ void importExternalSelf( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSAME record from the passed stream. */
+ void importExternalSame( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALADDIN record from the passed stream. */
+ void importExternalAddin( SequenceInputStream& rStrm );
+
+ /** Sets the link type to 'self reference'. */
+ void setSelfLinkType() { meLinkType = ExternalLinkType::Self; }
+
+ /** Returns the type of this external link. */
+ ExternalLinkType getLinkType() const { return meLinkType; }
+
+ /** Returns the relation identifier for the external link fragment. */
+ const OUString& getRelId() const { return maRelId; }
+ /** Returns the class name of this external link. */
+ const OUString& getClassName() const { return maClassName; }
+ /** Returns the target URL of this external link. */
+ const OUString& getTargetUrl() const { return maTargetUrl; }
+ /** Returns the link info needed by the XML formula parser. */
+ css::sheet::ExternalLinkInfo getLinkInfo() const;
+
+ /** Returns the type of the external library if this is a library link. */
+ FunctionLibraryType getFuncLibraryType() const;
+
+ /** Returns the token index of the external document. */
+ sal_Int32 getDocumentLinkIndex() const;
+ /** Returns the external sheet cache index or for the passed sheet. */
+ sal_Int32 getSheetCacheIndex( sal_Int32 nTabId ) const;
+ /** Returns the sheet cache of the external sheet with the passed index. */
+ css::uno::Reference< css::sheet::XExternalSheetCache >
+ getSheetCache( sal_Int32 nTabId ) const;
+
+ /** Returns the internal sheet range or range of external sheet caches for the passed sheet range (BIFF only). */
+ void getSheetRange( LinkSheetRange& orSheetRange, sal_Int32 nTabId1, sal_Int32 nTabId2 ) const;
+
+ /** Returns the external name with the passed zero-based index. */
+ ExternalNameRef getNameByIndex( sal_Int32 nIndex ) const;
+
+private:
+ void setExternalTargetUrl( const OUString& rTargetUrl, const OUString& rTargetType );
+ void setDdeOleTargetUrl( const OUString& rClassName, const OUString& rTargetUrl, ExternalLinkType eLinkType );
+ void parseExternalReference( const ::oox::core::Relations& rRelations, const OUString& rRelId );
+
+ /** Creates an external document link and the sheet cache for the passed sheet name. */
+ void insertExternalSheet( const OUString& rSheetName );
+
+ ExternalNameRef createExternalName();
+
+private:
+ ExternalLinkType meLinkType; /// Type of this link object.
+ FunctionLibraryType meFuncLibType; /// Type of the function library, if link type is ExternalLinkType::Library.
+ OUString maRelId; /// Relation identifier for the external link fragment.
+ OUString maClassName; /// DDE service, OLE class name.
+ OUString maTargetUrl; /// Target link, DDE topic, OLE target.
+ css::uno::Reference< css::sheet::XExternalDocLink >
+ mxDocLink; /// Interface for an external document.
+ std::vector< sal_Int32 > maSheetCaches; /// External sheet cache indexes.
+ RefVector< ExternalName > maExtNames; /// Defined names in external document.
+};
+
+typedef std::shared_ptr< ExternalLink > ExternalLinkRef;
+
+/** Represents a REF entry in the BIFF12 EXTERNALSHEETS or in the BIFF8
+ EXTERNSHEET record.
+
+ This struct is used to map ref identifiers to external books (BIFF12:
+ EXTERNALREF records, BIFF8: EXTERNALBOOK records), and provides sheet
+ indexes into the sheet list of the external document.
+ */
+struct RefSheetsModel
+{
+ sal_Int32 mnExtRefId; /// Zero-based index into list of external documents.
+ sal_Int32 mnTabId1; /// Zero-based index to first sheet in external document.
+ sal_Int32 mnTabId2; /// Zero-based index to last sheet in external document.
+
+ explicit RefSheetsModel();
+
+ void readBiff12Data( SequenceInputStream& rStrm );
+};
+
+class ExternalLinkBuffer : public WorkbookHelper
+{
+public:
+ explicit ExternalLinkBuffer( const WorkbookHelper& rHelper );
+
+ /** Imports the externalReference element containing . */
+ ExternalLinkRef importExternalReference( const AttributeList& rAttribs );
+
+ /** Imports the EXTERNALREF record from the passed stream. */
+ ExternalLinkRef importExternalRef( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSELF record from the passed stream. */
+ void importExternalSelf( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSAME record from the passed stream. */
+ void importExternalSame( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALADDIN record from the passed stream. */
+ void importExternalAddin( SequenceInputStream& rStrm );
+ /** Imports the EXTERNALSHEETS record from the passed stream. */
+ void importExternalSheets( SequenceInputStream& rStrm );
+
+ /** Returns the sequence of link infos needed by the XML formula parser. */
+ css::uno::Sequence< css::sheet::ExternalLinkInfo >
+ getLinkInfos() const;
+
+ /** Returns the external link for the passed reference identifier. */
+ ExternalLinkRef getExternalLink( sal_Int32 nRefId, bool bUseRefSheets = true ) const;
+
+ /** Returns the sheet range for the specified reference (BIFF8 only). */
+ LinkSheetRange getSheetRange( sal_Int32 nRefId ) const;
+
+private:
+ /** Creates a new external link and inserts it into the list of links. */
+ ExternalLinkRef createExternalLink();
+
+ /** Returns the specified sheet indexes for a reference identifier. */
+ const RefSheetsModel* getRefSheets( sal_Int32 nRefId ) const;
+
+private:
+ typedef RefVector< ExternalLink > ExternalLinkVec;
+ typedef ::std::vector< RefSheetsModel > RefSheetsModelVec;
+
+ ExternalLinkRef mxSelfRef; /// Implicit self reference at index 0.
+ ExternalLinkVec maLinks; /// List of link structures for all kinds of links.
+ ExternalLinkVec maExtLinks; /// Real external links needed for formula parser.
+ RefSheetsModelVec maRefSheets; /// Sheet indexes for reference ids.
+ bool mbUseRefSheets; /// True = use maRefSheets list (BIFF12 only).
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/externallinkfragment.hxx b/sc/source/filter/inc/externallinkfragment.hxx
new file mode 100644
index 000000000..f14ba0b0a
--- /dev/null
+++ b/sc/source/filter/inc/externallinkfragment.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "externallinkbuffer.hxx"
+
+namespace oox::xls {
+
+/** This class implements importing the sheetData element in external sheets.
+
+ The sheetData element embedded in the externalBook element contains cached
+ cells from externally linked sheets.
+ */
+class ExternalSheetDataContext : public WorkbookContextBase
+{
+public:
+ explicit ExternalSheetDataContext(
+ WorkbookFragmentBase& rFragment,
+ const css::uno::Reference< css::sheet::XExternalSheetCache >& rxSheetCache );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+private:
+ /** Imports cell settings from a c element. */
+ void importCell( const AttributeList& rAttribs );
+
+ /** Imports the EXTCELL_BLANK from the passed stream. */
+ void importExtCellBlank( SequenceInputStream& rStrm );
+ /** Imports the EXTCELL_BOOL from the passed stream. */
+ void importExtCellBool( SequenceInputStream& rStrm );
+ /** Imports the EXTCELL_DOUBLE from the passed stream. */
+ void importExtCellDouble( SequenceInputStream& rStrm );
+ /** Imports the EXTCELL_ERROR from the passed stream. */
+ void importExtCellError( SequenceInputStream& rStrm );
+ /** Imports the EXTCELL_STRING from the passed stream. */
+ void importExtCellString( SequenceInputStream& rStrm );
+
+ /** Sets the passed cell value to the current position in the sheet cache. */
+ void setCellValue( const css::uno::Any& rValue );
+
+private:
+ css::uno::Reference< css::sheet::XExternalSheetCache >
+ mxSheetCache; /// The sheet cache used to store external cell values.
+ ScAddress maCurrPos; /// Position of current cell.
+ sal_Int32 mnCurrType; /// Data type of current cell.
+};
+
+class ExternalLinkFragment : public WorkbookFragmentBase
+{
+public:
+ explicit ExternalLinkFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath,
+ ExternalLink& rExtLink );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ ::oox::core::ContextHandlerRef createSheetDataContext( sal_Int32 nSheetId );
+
+private:
+ ExternalLink& mrExtLink;
+ ExternalNameRef mxExtName;
+ OUString maResultValue;
+ sal_Int32 mnResultType;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/extlstcontext.hxx b/sc/source/filter/inc/extlstcontext.hxx
new file mode 100644
index 000000000..8635c6029
--- /dev/null
+++ b/sc/source/filter/inc/extlstcontext.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include <oox/core/contexthandler.hxx>
+#include "condformatbuffer.hxx"
+
+#include <vector>
+#include <memory>
+
+extern sal_Int32 rStyleIdx; // Holds index of the <extlst> <cfRule> style (Will be reset by finalize import)
+
+struct ScDataBarFormatData;
+namespace oox { class AttributeList; }
+namespace oox::xls { class WorkbookFragment; }
+namespace oox::xls { class WorksheetFragment; }
+
+namespace oox::xls {
+
+class ExtCfRuleContext : public WorksheetContextBase
+{
+public:
+ explicit ExtCfRuleContext( WorksheetContextBase& rFragment, ScDataBarFormatData* pDataBar );
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+ ScDataBarFormatData* mpTarget;
+
+ bool mbFirstEntry;
+};
+
+struct ExtCondFormatRuleModel
+{
+ sal_Int32 nPriority;
+ ScConditionMode eOperator;
+ OUString aFormula;
+ OUString aStyle;
+};
+
+class ExtConditionalFormattingContext : public WorksheetContextBase
+{
+public:
+ explicit ExtConditionalFormattingContext(WorksheetContextBase& rFragment);
+
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters(const OUString& rCharacters) override;
+ virtual void onEndElement() override;
+
+private:
+ ExtCondFormatRuleModel maModel;
+ sal_Int32 nFormulaCount;
+ OUString aChars; // Characters of between xml elements.
+ sal_Int32 nPriority; // Priority of last cfRule element.
+ ScConditionMode eOperator; // Used only when cfRule type is "cellIs"
+ bool isPreviousElementF; // Used to distinguish alone <sqref> from <f> and <sqref>
+ std::vector<std::unique_ptr<ScFormatEntry> > maEntries;
+ std::unique_ptr<IconSetRule> mpCurrentRule;
+ std::vector<sal_Int32> maPriorities;
+ std::vector<ExtCondFormatRuleModel> maModels;
+};
+
+/**
+ * Handle ExtLst entries in xlsx. These entries are a way to extend the standard
+ * without actually changing it
+ *
+ * Needed right now for data bars
+ *
+ * ExtLstLocalContext is for the entry in the datastructure that needs to be extended
+ */
+class ExtLstLocalContext : public WorksheetContextBase
+{
+public:
+ explicit ExtLstLocalContext( WorksheetContextBase& rFragment, ScDataBarFormatData* pTarget ); // until now a ExtLst always extends an existing entry
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+private:
+ ScDataBarFormatData* mpTarget;
+};
+
+/**
+ * A single ext entry. Will be skipped until the actual entry with the correct uri is found
+ */
+class ExtGlobalContext : public WorksheetContextBase
+{
+public:
+ explicit ExtGlobalContext( WorksheetContextBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+};
+
+/**
+ * Used for the actual ExtLst containing the new extended definition.
+ * Uses the saved data from the ExtLstLocalContext
+ */
+class ExtLstGlobalContext : public WorksheetContextBase
+{
+public:
+ explicit ExtLstGlobalContext( WorksheetFragment& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+};
+
+class ExtGlobalWorkbookContext : public WorkbookContextBase
+{
+public:
+ explicit ExtGlobalWorkbookContext( WorkbookContextBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+private:
+};
+
+class ExtLstGlobalWorkbookContext : public WorkbookContextBase
+{
+public:
+ explicit ExtLstGlobalWorkbookContext( WorkbookFragment& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+};
+
+} //namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/fapihelper.hxx b/sc/source/filter/inc/fapihelper.hxx
new file mode 100644
index 000000000..7449f1193
--- /dev/null
+++ b/sc/source/filter/inc/fapihelper.hxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <osl/diagnose.h>
+#include <tools/color.hxx>
+#include "ftools.hxx"
+
+namespace com::sun::star {
+ namespace lang { class XMultiServiceFactory; }
+}
+
+namespace com::sun::star::beans { struct NamedValue; }
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::beans { class XMultiPropertySet; }
+
+namespace comphelper { class IDocPasswordVerifier; }
+
+// Static helper functions ====================================================
+
+class SfxMedium;
+class SfxObjectShell;
+
+/** Static API helper functions. */
+class ScfApiHelper
+{
+public:
+ /** Converts a non-empty vector into a UNO sequence containing elements of the same type. */
+ template< typename Type >
+ static css::uno::Sequence< Type >
+ VectorToSequence( const ::std::vector< Type >& rVector );
+
+ /** Returns the service name provided via the XServiceName interface, or an empty string on error. */
+ static OUString GetServiceName( const css::uno::Reference< css::uno::XInterface >& xInt );
+
+ /** Returns the multi service factory from a document shell. */
+ static css::uno::Reference< css::lang::XMultiServiceFactory > GetServiceFactory( const SfxObjectShell* pShell );
+
+ /** Creates an instance from the passed service name, using the passed service factory. */
+ static css::uno::Reference< css::uno::XInterface > CreateInstance(
+ const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory,
+ const OUString& rServiceName );
+
+ /** Creates an instance from the passed service name, using the service factory of the passed object. */
+ static css::uno::Reference< css::uno::XInterface > CreateInstance(
+ const SfxObjectShell* pShell,
+ const OUString& rServiceName );
+
+ /** Creates an instance from the passed service name, using the process service factory. */
+ static css::uno::Reference< css::uno::XInterface > CreateInstance( const OUString& rServiceName );
+
+ /** Opens a password dialog and returns the encryption data.
+ @return The encryption data or an empty sequence on 'Cancel' or any error. */
+ static css::uno::Sequence< css::beans::NamedValue > QueryEncryptionDataForMedium( SfxMedium& rMedium,
+ ::comphelper::IDocPasswordVerifier& rVerifier,
+ const ::std::vector< OUString >* pDefaultPasswords );
+};
+
+template< typename Type >
+css::uno::Sequence< Type > ScfApiHelper::VectorToSequence( const ::std::vector< Type >& rVector )
+{
+ OSL_ENSURE( !rVector.empty(), "ScfApiHelper::VectorToSequence - vector is empty" );
+ return css::uno::Sequence<Type>(rVector.data(), static_cast< sal_Int32 >(rVector.size()));
+}
+
+// Property sets ==============================================================
+
+/** A wrapper for a UNO property set.
+
+ This class provides functions to silently get and set properties (without
+ exceptions, without the need to check validity of the UNO property set).
+
+ An instance is constructed with the reference to a UNO property set or any
+ other interface (the constructor will query for the XPropertySet interface
+ then). The reference to the property set will be kept as long as the
+ instance of this class is alive.
+
+ The functions GetProperties() and SetProperties() try to handle all passed
+ values at once, using the XMultiPropertySet interface. If the
+ implementation does not support the XMultiPropertySet interface, all
+ properties are handled separately in a loop.
+ */
+class ScfPropertySet
+{
+public:
+ explicit ScfPropertySet() {}
+ /** Constructs a property set wrapper with the passed UNO property set. */
+ explicit ScfPropertySet( const css::uno::Reference< css::beans::XPropertySet > & xPropSet ) { Set( xPropSet ); }
+ /** Constructs a property set wrapper after querying the XPropertySet interface. */
+ template< typename InterfaceType >
+ explicit ScfPropertySet( const css::uno::Reference< InterfaceType >& xInterface ) { Set( xInterface ); }
+
+ ~ScfPropertySet();
+ //TODO:
+ ScfPropertySet(ScfPropertySet const &) = default;
+ ScfPropertySet(ScfPropertySet &&) = default;
+ ScfPropertySet & operator =(ScfPropertySet const &) = default;
+ ScfPropertySet & operator =(ScfPropertySet &&) = default;
+
+ /** Sets the passed UNO property set and releases the old UNO property set. */
+ void Set( css::uno::Reference< css::beans::XPropertySet > const & xPropSet );
+ /** Queries the passed interface for an XPropertySet and releases the old UNO property set. */
+ template< typename InterfaceType >
+ void Set( css::uno::Reference< InterfaceType > xInterface )
+ { Set( css::uno::Reference< css::beans::XPropertySet >( xInterface, css::uno::UNO_QUERY ) ); }
+
+ /** Returns true, if the contained XPropertySet interface is valid. */
+ bool Is() const { return mxPropSet.is(); }
+
+ /** Returns the contained XPropertySet interface. */
+ const css::uno::Reference< css::beans::XPropertySet >& GetApiPropertySet() const { return mxPropSet; }
+
+ /** Returns the service name provided via the XServiceName interface, or an empty string on error. */
+ OUString GetServiceName() const;
+
+ // Get properties ---------------------------------------------------------
+
+ /** Returns true, if the property set contains the specified property. */
+ bool HasProperty( const OUString& rPropName ) const;
+
+ /** Gets the specified property from the property set.
+ @return true, if the Any could be filled with the property value. */
+ bool GetAnyProperty( css::uno::Any& rValue, const OUString& rPropName ) const;
+
+ /** Gets the specified property from the property set.
+ @return true, if the passed variable could be filled with the property value. */
+ template< typename Type >
+ bool GetProperty( Type& rValue, const OUString& rPropName ) const
+ { css::uno::Any aAny; return GetAnyProperty( aAny, rPropName ) && (aAny >>= rValue); }
+
+ /** Gets the specified Boolean property from the property set.
+ @return true = property contains true; false = property contains false or error occurred. */
+ bool GetBoolProperty( const OUString& rPropName ) const;
+
+ /** Gets the specified Boolean property from the property set. */
+ OUString GetStringProperty( const OUString& rPropName ) const;
+
+ /** Gets the specified color property from the property set.
+ @return true, if the passed color variable could be filled with the property value. */
+ bool GetColorProperty( Color& rColor, const OUString& rPropName ) const;
+
+ /** Gets the specified properties from the property set. Tries to use the XMultiPropertySet interface.
+ @param rPropNames The property names. MUST be ordered alphabetically.
+ @param rValues The related property values. */
+ void GetProperties( css::uno::Sequence< css::uno::Any >& rValues, const css::uno::Sequence< OUString >& rPropNames ) const;
+
+ // Set properties ---------------------------------------------------------
+
+ /** Puts the passed Any into the property set. */
+ void SetAnyProperty( const OUString& rPropName, const css::uno::Any& rValue );
+
+ /** Puts the passed value into the property set. */
+ template< typename Type >
+ void SetProperty( const OUString& rPropName, const Type& rValue )
+ { SetAnyProperty( rPropName, css::uno::Any( rValue ) ); }
+
+ /** Puts the passed Boolean value into the property set. */
+ void SetBoolProperty( const OUString& rPropName, bool bValue )
+ { SetAnyProperty( rPropName, css::uno::Any( bValue ) ); }
+
+ /** Puts the passed string into the property set. */
+ void SetStringProperty( const OUString& rPropName, const OUString& rValue )
+ { SetProperty( rPropName, rValue ); }
+
+ /** Puts the passed color into the property set. */
+ void SetColorProperty( const OUString& rPropName, const Color& rColor )
+ { SetProperty( rPropName, sal_Int32( rColor ) ); }
+
+ /** Puts the passed properties into the property set. Tries to use the XMultiPropertySet interface.
+ @param rPropNames The property names. MUST be ordered alphabetically.
+ @param rValues The related property values. */
+ void SetProperties( const css::uno::Sequence< OUString > & rPropNames, const css::uno::Sequence< css::uno::Any >& rValues );
+
+private:
+ css::uno::Reference< css::beans::XPropertySet > mxPropSet; /// The mandatory property set interface.
+ css::uno::Reference< css::beans::XMultiPropertySet > mxMultiPropSet; /// The optional multi property set interface.
+};
+
+/** Generic helper class for reading from and writing to property sets.
+
+ Usage:
+ 1) Call the constructor with a null-terminated array of ASCII strings.
+ 2a) Read properties from a property set: Call the ReadFromPropertySet()
+ function, then get the properties with the ReadValue() functions or the
+ operator>> stream operator. The properties are returned in order of the
+ array of property names passed in the constructor.
+ 2b) Write properties to a property set: Call InitializeWrite() to start a
+ new cycle. Set the values with the WriteValue() functions or the
+ operator<< stream operator. The order of the properties is equal to the
+ array of property names passed in the constructor. Finally, call the
+ WriteToPropertySet() function.
+ */
+class ScfPropSetHelper
+{
+public:
+ /** @param ppPropNames A null-terminated array of ASCII property names. */
+ explicit ScfPropSetHelper( const char* const* ppcPropNames );
+
+ // read properties --------------------------------------------------------
+
+ /** Reads all values from the passed property set. */
+ void ReadFromPropertySet( const ScfPropertySet& rPropSet );
+
+ /** Reads the next value from the value sequence. */
+ template< typename Type >
+ void ReadValue( Type& rValue );
+ /** Reads an Any from the value sequence. */
+ void ReadValue( css::uno::Any& rAny );
+ /** Reads a color value from the value sequence. */
+ void ReadValue( Color& rColor );
+ /** Reads a C++ boolean value from the value sequence. */
+ void ReadValue( bool& rbValue );
+
+ // write properties -------------------------------------------------------
+
+ /** Must be called before reading or storing property values in the helper. */
+ void InitializeWrite();
+
+ /** Writes the next value to the value sequence. */
+ template< typename Type >
+ void WriteValue( const Type& rValue );
+ /** Writes an Any to the value sequence. */
+ void WriteValue( const css::uno::Any& rAny );
+ /** Writes a color value to the value sequence. */
+ void WriteValue( const Color& rColor )
+ { WriteValue( sal_Int32( rColor ) ); }
+ /** Writes a C++ boolean value to the value sequence. */
+ void WriteValue( bool rbValue );
+
+ /** Writes all values to the passed property set. */
+ void WriteToPropertySet( ScfPropertySet& rPropSet ) const;
+
+private:
+ /** Returns a pointer to the next Any to be written to. */
+ css::uno::Any* GetNextAny();
+
+private:
+ css::uno::Sequence< OUString > maNameSeq; /// Sequence of property names.
+ css::uno::Sequence< css::uno::Any > maValueSeq; /// Sequence of property values.
+ ScfInt32Vec maNameOrder; /// Maps initial order to alphabetical order.
+ size_t mnNextIdx; /// Counter for next Any to be processed.
+};
+
+template< typename Type >
+void ScfPropSetHelper::ReadValue( Type& rValue )
+{
+ css::uno::Any* pAny = GetNextAny();
+ if (pAny)
+ *pAny >>= rValue;
+}
+
+template< typename Type >
+void ScfPropSetHelper::WriteValue( const Type& rValue )
+{
+ css::uno::Any* pAny = GetNextAny();
+ if( pAny )
+ *pAny <<= rValue;
+}
+
+template< typename Type >
+ScfPropSetHelper& operator>>( ScfPropSetHelper& rPropSetHelper, Type& rValue )
+{
+ rPropSetHelper.ReadValue( rValue );
+ return rPropSetHelper;
+}
+
+template< typename Type >
+ScfPropSetHelper& operator<<( ScfPropSetHelper& rPropSetHelper, const Type& rValue )
+{
+ rPropSetHelper.WriteValue( rValue );
+ return rPropSetHelper;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/flttypes.hxx b/sc/source/filter/inc/flttypes.hxx
new file mode 100644
index 000000000..9863278c0
--- /dev/null
+++ b/sc/source/filter/inc/flttypes.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+enum BiffTyp
+{
+ BiffX = 0x0000,
+ Biff2 = 0x2000, Biff2M = 0x2002, Biff2C = 0x2004,
+ Biff3 = 0x3000, Biff3W = 0x3001, Biff3M = 0x3002, Biff3C = 0x3004,
+ Biff4 = 0x4000, Biff4W = 0x4001, Biff4M = 0x4002, Biff4C = 0x4004,
+ Biff5 = 0x5000, Biff5W = 0x5001, Biff5V = 0x5002, Biff5C = 0x5004, Biff5M4 = 0x5008,
+ Biff8 = 0x8000, Biff8W = 0x8001, Biff8V = 0x8002, Biff8C = 0x8004, Biff8M4 = 0x8008
+};
+
+enum class Lotus123Typ
+{
+ X,
+ WK3,
+ WK4,
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/formel.hxx b/sc/source/filter/inc/formel.hxx
new file mode 100644
index 000000000..2c01a560e
--- /dev/null
+++ b/sc/source/filter/inc/formel.hxx
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/stream.hxx>
+
+#include "tokstack.hxx"
+#include "xiroot.hxx"
+
+#include <memory>
+#include <vector>
+#include <map>
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+class XclImpStream;
+class ScTokenArray;
+
+enum class ConvErr
+{
+ OK = 0,
+ Ni, // unimplemented/unknown opcode occurred
+ Count // did not get all bytes of formula
+};
+
+enum FORMULA_TYPE
+{
+ FT_CellFormula,
+ FT_RangeName,
+ FT_SharedFormula,
+ FT_CondFormat
+};
+
+class ScRangeListTabs : protected XclImpRoot
+{
+ typedef ::std::vector<ScRange> RangeListType;
+ typedef ::std::map<SCTAB, RangeListType> TabRangeType;
+ TabRangeType m_TabRanges;
+ RangeListType::const_iterator maItrCur;
+ RangeListType::const_iterator maItrCurEnd;
+
+public:
+ ScRangeListTabs( const XclImpRoot& rRoot );
+ ~ScRangeListTabs();
+
+ void Append( const ScAddress& aSRD, SCTAB nTab );
+ void Append( const ScRange& aCRD, SCTAB nTab );
+
+ const ScRange* First ( SCTAB nTab );
+ const ScRange* Next ();
+
+ bool HasRanges () const { return !m_TabRanges.empty(); }
+};
+
+class ConverterBase
+{
+protected:
+ TokenPool aPool; // user token + predefined token
+ TokenStack aStack;
+ ScAddress aEingPos;
+
+ ConverterBase( svl::SharedStringPool& rSPool );
+ virtual ~ConverterBase();
+
+ void Reset();
+};
+
+class ExcelConverterBase : public ConverterBase
+{
+protected:
+ ExcelConverterBase( svl::SharedStringPool& rSPool );
+ virtual ~ExcelConverterBase() override;
+
+public:
+ void Reset();
+ void Reset( const ScAddress& rEingPos );
+
+ virtual ConvErr Convert( std::unique_ptr<ScTokenArray>& rpErg, XclImpStream& rStrm, std::size_t nFormulaLen,
+ bool bAllowArrays, const FORMULA_TYPE eFT = FT_CellFormula ) = 0;
+ virtual ConvErr Convert( ScRangeListTabs&, XclImpStream& rStrm, std::size_t nFormulaLen, SCTAB nTab,
+ const FORMULA_TYPE eFT = FT_CellFormula ) = 0;
+};
+
+class LotusConverterBase : public ConverterBase
+{
+protected:
+ SvStream& aIn;
+ sal_Int32 nBytesLeft;
+
+ inline void Ignore( const tools::Long nSeekRel );
+ inline void Read( sal_uInt8& nByte );
+ inline void Read( sal_uInt16& nUINT16 );
+ inline void Read( sal_Int16& nINT16 );
+ inline void Read( double& fDouble );
+ inline void Read( sal_uInt32& nUINT32 );
+
+ LotusConverterBase( SvStream& rStr, svl::SharedStringPool& rSPool );
+ virtual ~LotusConverterBase() override;
+
+public:
+ void Reset( const ScAddress& rEingPos );
+
+ virtual void Convert( std::unique_ptr<ScTokenArray>& rpErg, sal_Int32& nRest ) = 0;
+
+ bool good() const { return aIn.good(); }
+
+protected:
+ using ConverterBase::Reset;
+};
+
+inline void LotusConverterBase::Ignore( const tools::Long nSeekRel )
+{
+ aIn.SeekRel( nSeekRel );
+ nBytesLeft -= nSeekRel;
+}
+
+inline void LotusConverterBase::Read( sal_uInt8& nByte )
+{
+ aIn.ReadUChar( nByte );
+ if (aIn.good())
+ nBytesLeft--;
+ else
+ {
+ // SvStream::ReadUChar() does not init a single char on failure. This
+ // behaviour is even tested in a unit test.
+ nByte = 0;
+ nBytesLeft = -1; // bail out early
+ }
+}
+
+inline void LotusConverterBase::Read( sal_uInt16& nUINT16 )
+{
+ aIn.ReadUInt16( nUINT16 );
+ if (aIn.good())
+ nBytesLeft -= 2;
+ else
+ nBytesLeft = -1; // bail out early
+}
+
+inline void LotusConverterBase::Read( sal_Int16& nINT16 )
+{
+ aIn.ReadInt16( nINT16 );
+ if (aIn.good())
+ nBytesLeft -= 2;
+ else
+ nBytesLeft = -1; // bail out early
+}
+
+inline void LotusConverterBase::Read( double& fDouble )
+{
+ aIn.ReadDouble( fDouble );
+ if (aIn.good())
+ nBytesLeft -= 8;
+ else
+ nBytesLeft = -1; // bail out early
+}
+
+inline void LotusConverterBase::Read( sal_uInt32& nUINT32 )
+{
+ aIn.ReadUInt32( nUINT32 );
+ if (aIn.good())
+ nBytesLeft -= 4;
+ else
+ nBytesLeft = -1; // bail out early
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/formulabase.hxx b/sc/source/filter/inc/formulabase.hxx
new file mode 100644
index 000000000..aa0643f5a
--- /dev/null
+++ b/sc/source/filter/inc/formulabase.hxx
@@ -0,0 +1,780 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/beans/Pair.hpp>
+#include <com/sun/star/sheet/FormulaToken.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+#include <oox/helper/propertyset.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace com::sun::star {
+ namespace lang { class XMultiServiceFactory; }
+ namespace sheet { class XFormulaParser; }
+}
+
+namespace oox { template< typename Type > class Matrix; }
+namespace com::sun::star::sheet { struct FormulaOpCodeMapEntry; }
+namespace oox { class SequenceInputStream; }
+namespace oox::xls { struct BinAddress; }
+class ScRangeList;
+
+namespace oox::xls {
+
+// Constants ==================================================================
+
+const size_t BIFF_TOKARR_MAXLEN = 4096; /// Maximum size of a token array.
+
+// token class flags ----------------------------------------------------------
+
+const sal_uInt8 BIFF_TOKCLASS_MASK = 0x60;
+const sal_uInt8 BIFF_TOKCLASS_NONE = 0x00; /// 00-1F: Base tokens.
+const sal_uInt8 BIFF_TOKCLASS_REF = 0x20; /// 20-3F: Reference class tokens.
+const sal_uInt8 BIFF_TOKCLASS_VAL = 0x40; /// 40-5F: Value class tokens.
+const sal_uInt8 BIFF_TOKCLASS_ARR = 0x60; /// 60-7F: Array class tokens.
+
+const sal_uInt8 BIFF_TOKFLAG_INVALID = 0x80; /// This bit must be null for a valid token identifier.
+
+// base token identifiers -----------------------------------------------------
+
+const sal_uInt8 BIFF_TOKID_MASK = 0x1F;
+
+const sal_uInt8 BIFF_TOKID_NONE = 0x00; /// Placeholder for invalid token id.
+const sal_uInt8 BIFF_TOKID_EXP = 0x01; /// Array or shared formula reference.
+const sal_uInt8 BIFF_TOKID_TBL = 0x02; /// Multiple operation reference.
+const sal_uInt8 BIFF_TOKID_ADD = 0x03; /// Addition operator.
+const sal_uInt8 BIFF_TOKID_SUB = 0x04; /// Subtraction operator.
+const sal_uInt8 BIFF_TOKID_MUL = 0x05; /// Multiplication operator.
+const sal_uInt8 BIFF_TOKID_DIV = 0x06; /// Division operator.
+const sal_uInt8 BIFF_TOKID_POWER = 0x07; /// Power operator.
+const sal_uInt8 BIFF_TOKID_CONCAT = 0x08; /// String concatenation operator.
+const sal_uInt8 BIFF_TOKID_LT = 0x09; /// Less than operator.
+const sal_uInt8 BIFF_TOKID_LE = 0x0A; /// Less than or equal operator.
+const sal_uInt8 BIFF_TOKID_EQ = 0x0B; /// Equal operator.
+const sal_uInt8 BIFF_TOKID_GE = 0x0C; /// Greater than or equal operator.
+const sal_uInt8 BIFF_TOKID_GT = 0x0D; /// Greater than operator.
+const sal_uInt8 BIFF_TOKID_NE = 0x0E; /// Not equal operator.
+const sal_uInt8 BIFF_TOKID_ISECT = 0x0F; /// Intersection operator.
+const sal_uInt8 BIFF_TOKID_LIST = 0x10; /// List operator.
+const sal_uInt8 BIFF_TOKID_RANGE = 0x11; /// Range operator.
+const sal_uInt8 BIFF_TOKID_UPLUS = 0x12; /// Unary plus.
+const sal_uInt8 BIFF_TOKID_UMINUS = 0x13; /// Unary minus.
+const sal_uInt8 BIFF_TOKID_PERCENT = 0x14; /// Percent sign.
+const sal_uInt8 BIFF_TOKID_PAREN = 0x15; /// Parentheses.
+const sal_uInt8 BIFF_TOKID_MISSARG = 0x16; /// Missing argument.
+const sal_uInt8 BIFF_TOKID_STR = 0x17; /// String constant.
+const sal_uInt8 BIFF_TOKID_NLR = 0x18; /// Natural language reference (NLR).
+const sal_uInt8 BIFF_TOKID_ATTR = 0x19; /// Special attribute.
+const sal_uInt8 BIFF_TOKID_SHEET = 0x1A; /// Start of a sheet reference (BIFF2-BIFF4).
+const sal_uInt8 BIFF_TOKID_ENDSHEET = 0x1B; /// End of a sheet reference (BIFF2-BIFF4).
+const sal_uInt8 BIFF_TOKID_ERR = 0x1C; /// Error constant.
+const sal_uInt8 BIFF_TOKID_BOOL = 0x1D; /// Boolean constant.
+const sal_uInt8 BIFF_TOKID_INT = 0x1E; /// Integer constant.
+const sal_uInt8 BIFF_TOKID_NUM = 0x1F; /// Floating-point constant.
+
+// base identifiers of classified tokens --------------------------------------
+
+const sal_uInt8 BIFF_TOKID_ARRAY = 0x00; /// Array constant.
+const sal_uInt8 BIFF_TOKID_FUNC = 0x01; /// Function, fixed number of arguments.
+const sal_uInt8 BIFF_TOKID_FUNCVAR = 0x02; /// Function, variable number of arguments.
+const sal_uInt8 BIFF_TOKID_NAME = 0x03; /// Defined name.
+const sal_uInt8 BIFF_TOKID_REF = 0x04; /// 2D cell reference.
+const sal_uInt8 BIFF_TOKID_AREA = 0x05; /// 2D area reference.
+const sal_uInt8 BIFF_TOKID_MEMAREA = 0x06; /// Constant reference subexpression.
+const sal_uInt8 BIFF_TOKID_MEMERR = 0x07; /// Deleted reference subexpression.
+const sal_uInt8 BIFF_TOKID_MEMNOMEM = 0x08; /// Constant reference subexpression without result.
+const sal_uInt8 BIFF_TOKID_MEMFUNC = 0x09; /// Variable reference subexpression.
+const sal_uInt8 BIFF_TOKID_REFERR = 0x0A; /// Deleted 2D cell reference.
+const sal_uInt8 BIFF_TOKID_AREAERR = 0x0B; /// Deleted 2D area reference.
+const sal_uInt8 BIFF_TOKID_REFN = 0x0C; /// Relative 2D cell reference (in names).
+const sal_uInt8 BIFF_TOKID_AREAN = 0x0D; /// Relative 2D area reference (in names).
+const sal_uInt8 BIFF_TOKID_MEMAREAN = 0x0E; /// Reference subexpression (in names).
+const sal_uInt8 BIFF_TOKID_MEMNOMEMN = 0x0F; /// Reference subexpression (in names) without result.
+const sal_uInt8 BIFF_TOKID_FUNCCE = 0x18;
+const sal_uInt8 BIFF_TOKID_NAMEX = 0x19; /// External reference.
+const sal_uInt8 BIFF_TOKID_REF3D = 0x1A; /// 3D cell reference.
+const sal_uInt8 BIFF_TOKID_AREA3D = 0x1B; /// 3D area reference.
+const sal_uInt8 BIFF_TOKID_REFERR3D = 0x1C; /// Deleted 3D cell reference.
+const sal_uInt8 BIFF_TOKID_AREAERR3D = 0x1D; /// Deleted 3D area reference
+
+// specific token constants ---------------------------------------------------
+
+const sal_uInt8 BIFF_TOK_ARRAY_DOUBLE = 0; /// Double value in an array.
+const sal_uInt8 BIFF_TOK_ARRAY_STRING = 1; /// String value in an array.
+const sal_uInt8 BIFF_TOK_ARRAY_BOOL = 2; /// Boolean value in an array.
+const sal_uInt8 BIFF_TOK_ARRAY_ERROR = 4; /// Error code in an array.
+
+const sal_uInt8 BIFF_TOK_BOOL_FALSE = 0; /// FALSE value of a tBool token.
+const sal_uInt8 BIFF_TOK_BOOL_TRUE = 1; /// TRUE value of a tBool token.
+
+const sal_uInt8 BIFF_TOK_ATTR_VOLATILE = 0x01; /// Volatile function.
+const sal_uInt8 BIFF_TOK_ATTR_IF = 0x02; /// Start of true condition in IF function.
+const sal_uInt8 BIFF_TOK_ATTR_CHOOSE = 0x04; /// Jump array of CHOOSE function.
+const sal_uInt8 BIFF_TOK_ATTR_SKIP = 0x08; /// Skip tokens.
+const sal_uInt8 BIFF_TOK_ATTR_SUM = 0x10; /// SUM function with one parameter.
+const sal_uInt8 BIFF_TOK_ATTR_ASSIGN = 0x20; /// BASIC style assignment.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE = 0x40; /// Spaces in formula representation.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_VOLATILE = 0x41; /// Leading spaces and volatile formula.
+const sal_uInt8 BIFF_TOK_ATTR_IFERROR = 0x80; /// Start of condition in IFERROR function (BIFF12 only).
+
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_SP = 0x00; /// Spaces before next token.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_BR = 0x01; /// Line breaks before next token.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_SP_OPEN = 0x02; /// Spaces before opening parenthesis.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_BR_OPEN = 0x03; /// Line breaks before opening parenthesis.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_SP_CLOSE = 0x04; /// Spaces before closing parenthesis.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_BR_CLOSE = 0x05; /// Line breaks before closing parenthesis.
+const sal_uInt8 BIFF_TOK_ATTR_SPACE_SP_PRE = 0x06; /// Spaces before formula (BIFF3).
+
+const sal_uInt16 BIFF_TOK_FUNCVAR_CMD = 0x8000; /// Macro command.
+const sal_uInt16 BIFF_TOK_FUNCVAR_FUNCIDMASK = 0x7FFF; /// Mask for function/command index.
+const sal_uInt8 BIFF_TOK_FUNCVAR_CMDPROMPT = 0x80; /// User prompt for macro commands.
+const sal_uInt8 BIFF_TOK_FUNCVAR_COUNTMASK = 0x7F; /// Mask for parameter count.
+
+const sal_uInt16 BIFF12_TOK_REF_COLMASK = 0x3FFF; /// Mask to extract column from reference (BIFF12).
+const sal_Int32 BIFF12_TOK_REF_ROWMASK = 0xFFFFF; /// Mask to extract row from reference (BIFF12).
+const sal_uInt16 BIFF12_TOK_REF_COLREL = 0x4000; /// True = column is relative (BIFF12).
+const sal_uInt16 BIFF12_TOK_REF_ROWREL = 0x8000; /// True = row is relative (BIFF12).
+
+const sal_uInt16 BIFF_TOK_REF_COLMASK = 0x00FF; /// Mask to extract BIFF8 column from reference.
+const sal_uInt16 BIFF_TOK_REF_ROWMASK = 0x3FFF; /// Mask to extract BIFF2-BIFF5 row from reference.
+const sal_uInt16 BIFF_TOK_REF_COLREL = 0x4000; /// True = column is relative.
+const sal_uInt16 BIFF_TOK_REF_ROWREL = 0x8000; /// True = row is relative.
+
+const sal_uInt16 BIFF12_TOK_TABLE_COLUMN = 0x0001; /// Table reference: Single column.
+const sal_uInt16 BIFF12_TOK_TABLE_COLRANGE = 0x0002; /// Table reference: Range of columns.
+const sal_uInt16 BIFF12_TOK_TABLE_ALL = 0x0004; /// Table reference: Special [#All] range.
+const sal_uInt16 BIFF12_TOK_TABLE_HEADERS = 0x0008; /// Table reference: Special [#Headers] range.
+const sal_uInt16 BIFF12_TOK_TABLE_DATA = 0x0010; /// Table reference: Special [#Data] range.
+const sal_uInt16 BIFF12_TOK_TABLE_TOTALS = 0x0020; /// Table reference: Special [#Totals] range.
+const sal_uInt16 BIFF12_TOK_TABLE_THISROW = 0x0040; /// Table reference: Special [#This Row] range.
+const sal_uInt16 BIFF12_TOK_TABLE_SP_BRACKETS = 0x0080; /// Table reference: Spaces in outer brackets.
+const sal_uInt16 BIFF12_TOK_TABLE_SP_SEP = 0x0100; /// Table reference: Spaces after separators.
+const sal_uInt16 BIFF12_TOK_TABLE_ROW = 0x0200; /// Table reference: Single row.
+const sal_uInt16 BIFF12_TOK_TABLE_CELL = 0x0400; /// Table reference: Single cell.
+
+const sal_uInt8 BIFF_TOK_NLR_ERR = 0x01; /// NLR: Invalid/deleted.
+const sal_uInt8 BIFF_TOK_NLR_ROWR = 0x02; /// NLR: Row index.
+const sal_uInt8 BIFF_TOK_NLR_COLR = 0x03; /// NLR: Column index.
+const sal_uInt8 BIFF_TOK_NLR_ROWV = 0x06; /// NLR: Value in row.
+const sal_uInt8 BIFF_TOK_NLR_COLV = 0x07; /// NLR: Value in column.
+const sal_uInt8 BIFF_TOK_NLR_RANGE = 0x0A; /// NLR: Range.
+const sal_uInt8 BIFF_TOK_NLR_SRANGE = 0x0B; /// Stacked NLR: Range.
+const sal_uInt8 BIFF_TOK_NLR_SROWR = 0x0C; /// Stacked NLR: Row index.
+const sal_uInt8 BIFF_TOK_NLR_SCOLR = 0x0D; /// Stacked NLR: Column index.
+const sal_uInt8 BIFF_TOK_NLR_SROWV = 0x0E; /// Stacked NLR: Value in row.
+const sal_uInt8 BIFF_TOK_NLR_SCOLV = 0x0F; /// Stacked NLR: Value in column.
+const sal_uInt8 BIFF_TOK_NLR_RANGEERR = 0x10; /// NLR: Invalid/deleted range.
+const sal_uInt8 BIFF_TOK_NLR_SXNAME = 0x1D; /// NLR: Pivot table name.
+const sal_uInt16 BIFF_TOK_NLR_REL = 0x8000; /// True = NLR is relative.
+const sal_uInt16 BIFF_TOK_NLR_MASK = 0x3FFF; /// Mask to extract BIFF8 column from NLR.
+
+const sal_uInt32 BIFF_TOK_NLR_ADDREL = 0x80000000; /// NLR relative (in appended data).
+const sal_uInt32 BIFF_TOK_NLR_ADDMASK = 0x3FFFFFFF; /// Mask for number of appended ranges.
+
+// function constants ---------------------------------------------------------
+
+const sal_uInt8 OOX_MAX_PARAMCOUNT = 255; /// Maximum parameter count for OOXML/BIFF12 files.
+const sal_uInt8 BIFF_MAX_PARAMCOUNT = 30; /// Maximum parameter count for BIFF2-BIFF8 files.
+
+const sal_uInt16 BIFF_FUNC_IF = 1; /// Function identifier of the IF function.
+const sal_uInt16 BIFF_FUNC_SUM = 4; /// Function identifier of the SUM function.
+const sal_uInt16 BIFF_FUNC_TRUE = 34; /// Function identifier of the TRUE function.
+const sal_uInt16 BIFF_FUNC_FALSE = 35; /// Function identifier of the FALSE function.
+const sal_uInt16 BIFF_FUNC_ROWS = 76; /// Function identifier of the ROWS function.
+const sal_uInt16 BIFF_FUNC_COLUMNS = 77; /// Function identifier of the COLUMNS function.
+const sal_uInt16 BIFF_FUNC_OFFSET = 78; /// Function identifier of the OFFSET function.
+const sal_uInt16 BIFF_FUNC_EXTERNCALL = 255; /// BIFF function id of the EXTERN.CALL function.
+const sal_uInt16 BIFF_FUNC_FLOOR = 285; /// Function identifier of the FLOOR function.
+const sal_uInt16 BIFF_FUNC_CEILING = 288; /// Function identifier of the CEILING function.
+const sal_uInt16 BIFF_FUNC_HYPERLINK = 359; /// Function identifier of the HYPERLINK function.
+const sal_uInt16 BIFF_FUNC_WEEKNUM = 465; /// Function identifier of the WEEKNUM function.
+
+// Formula type ===============================================================
+
+/** Enumerates all possible types of a formula. */
+enum class FormulaType
+{
+ Cell, /// Simple cell formula, or reference to a shared formula name.
+ Array, /// Array (matrix) formula.
+ SharedFormula, /// Shared formula definition.
+ CondFormat, /// Condition of a conditional format rule.
+ Validation /// Condition of a data validation.
+};
+
+// Reference helpers ==========================================================
+
+/** A 2D formula cell reference struct with relative flags. */
+struct BinSingleRef2d
+{
+ sal_Int32 mnCol; /// Column index.
+ sal_Int32 mnRow; /// Row index.
+ bool mbColRel; /// True = relative column reference.
+ bool mbRowRel; /// True = relative row reference.
+
+ explicit BinSingleRef2d();
+
+ void setBiff12Data( sal_uInt16 nCol, sal_Int32 nRow, bool bRelativeAsOffset );
+
+ void readBiff12Data( SequenceInputStream& rStrm, bool bRelativeAsOffset );
+};
+
+/** A 2D formula cell range reference struct with relative flags. */
+struct BinComplexRef2d
+{
+ BinSingleRef2d maRef1; /// Start (top-left) cell address.
+ BinSingleRef2d maRef2; /// End (bottom-right) cell address.
+
+ void readBiff12Data( SequenceInputStream& rStrm, bool bRelativeAsOffset );
+};
+
+// Token vector, token sequence ===============================================
+
+typedef css::sheet::FormulaToken ApiToken;
+typedef css::uno::Sequence< ApiToken > ApiTokenSequence;
+
+/** Contains the base address and type of a special token representing an array
+ formula or a shared formula (sal_False), or a table operation (sal_True). */
+typedef css::beans::Pair< css::table::CellAddress, sal_Bool > ApiSpecialTokenInfo;
+
+/** A vector of formula tokens with additional convenience functions. */
+class ApiTokenVector
+{
+public:
+ explicit ApiTokenVector();
+
+ ApiToken& operator[]( size_t i ) { return mvTokens[i]; }
+
+ size_t size() const { return mvTokens.size(); }
+
+ ApiToken& back() { return mvTokens.back(); }
+ const ApiToken& back() const { return mvTokens.back(); }
+
+ void clear() { mvTokens.clear(); }
+
+ void pop_back() { mvTokens.pop_back(); }
+
+ void push_back( const ApiToken& rToken ) { mvTokens.push_back( rToken ); }
+
+ void reserve( size_t n ) { mvTokens.reserve( n ); }
+
+ void resize( size_t n ) { mvTokens.resize( n ); }
+
+ /** Appends a new token with the passed op-code, returns its data field. */
+ css::uno::Any& append( sal_Int32 nOpCode );
+
+ /** Appends a new token with the passed op-code and data. */
+ template< typename Type >
+ void append( sal_Int32 nOpCode, const Type& rData ) { append( nOpCode ) <<= rData; }
+
+ /** Converts to a sequence. */
+ ApiTokenSequence toSequence() const;
+
+private:
+ ::std::vector< ApiToken > mvTokens;
+};
+
+// Token sequence iterator ====================================================
+
+/** Token sequence iterator that is able to skip space tokens. */
+class ApiTokenIterator
+{
+public:
+ explicit ApiTokenIterator( const ApiTokenSequence& rTokens, sal_Int32 nSpacesOpCode );
+ bool is() const { return mpToken != mpTokenEnd; }
+ const ApiToken* operator->() const { return mpToken; }
+
+ ApiTokenIterator& operator++();
+
+private:
+ void skipSpaces();
+
+private:
+ const ApiToken* mpToken; /// Pointer to current token of the token sequence.
+ const ApiToken* mpTokenEnd; /// Pointer behind last token of the token sequence.
+ const sal_Int32 mnSpacesOpCode; /// Op-code for whitespace tokens.
+};
+
+// List of API op-codes =======================================================
+
+/** Contains all API op-codes needed to build formulas with tokens. */
+struct ApiOpCodes
+{
+ // special
+ sal_Int32 OPCODE_UNKNOWN; /// Internal: function name unknown to mapper.
+ sal_Int32 OPCODE_EXTERNAL; /// External function call (e.g. add-ins).
+ // formula structure
+ sal_Int32 OPCODE_PUSH; /// Op-code for common value operands.
+ sal_Int32 OPCODE_MISSING; /// Placeholder for a missing function parameter.
+ sal_Int32 OPCODE_SPACES; /// Spaces between other formula tokens.
+ sal_Int32 OPCODE_NAME; /// Index of a defined name.
+ sal_Int32 OPCODE_DBAREA; /// Index of a database area.
+ sal_Int32 OPCODE_NLR; /// Natural language reference.
+ sal_Int32 OPCODE_DDE; /// DDE link function.
+ sal_Int32 OPCODE_MACRO; /// Macro function call.
+ sal_Int32 OPCODE_BAD; /// Bad token (unknown name, formula error).
+ sal_Int32 OPCODE_NONAME; /// Function style #NAME? error.
+ // separators
+ sal_Int32 OPCODE_OPEN; /// Opening parenthesis.
+ sal_Int32 OPCODE_CLOSE; /// Closing parenthesis.
+ sal_Int32 OPCODE_SEP; /// Function parameter separator.
+ // array separators
+ sal_Int32 OPCODE_ARRAY_OPEN; /// Opening brace for constant arrays.
+ sal_Int32 OPCODE_ARRAY_CLOSE; /// Closing brace for constant arrays.
+ sal_Int32 OPCODE_ARRAY_ROWSEP; /// Row separator in constant arrays.
+ sal_Int32 OPCODE_ARRAY_COLSEP; /// Column separator in constant arrays.
+ // unary operators
+ sal_Int32 OPCODE_PLUS_SIGN; /// Unary plus sign.
+ sal_Int32 OPCODE_MINUS_SIGN; /// Unary minus sign.
+ sal_Int32 OPCODE_PERCENT; /// Percent sign.
+ // binary operators
+ sal_Int32 OPCODE_ADD; /// Addition operator.
+ sal_Int32 OPCODE_SUB; /// Subtraction operator.
+ sal_Int32 OPCODE_MULT; /// Multiplication operator.
+ sal_Int32 OPCODE_DIV; /// Division operator.
+ sal_Int32 OPCODE_POWER; /// Power operator.
+ sal_Int32 OPCODE_CONCAT; /// String concatenation operator.
+ sal_Int32 OPCODE_EQUAL; /// Compare equal operator.
+ sal_Int32 OPCODE_NOT_EQUAL; /// Compare not equal operator.
+ sal_Int32 OPCODE_LESS; /// Compare less operator.
+ sal_Int32 OPCODE_LESS_EQUAL; /// Compare less or equal operator.
+ sal_Int32 OPCODE_GREATER; /// Compare greater operator.
+ sal_Int32 OPCODE_GREATER_EQUAL; /// Compare greater or equal operator.
+ sal_Int32 OPCODE_INTERSECT; /// Range intersection operator.
+ sal_Int32 OPCODE_LIST; /// Range list operator.
+ sal_Int32 OPCODE_RANGE; /// Range operator.
+};
+
+// Function parameter info ====================================================
+
+/** Enumerates validity modes for a function parameter. */
+enum class FuncParamValidity
+{
+ Regular, /// Parameter supported by Calc and Excel.
+ CalcOnly, /// Parameter supported by Calc only.
+ ExcelOnly /// Parameter supported by Excel only.
+};
+
+/** Structure that contains all needed information for a parameter in a
+ function.
+
+ The member meValid specifies which application supports the parameter. If
+ set to CALCONLY, import filters have to insert a default value for this
+ parameter, and export filters have to skip the parameter. If set to
+ EXCELONLY, import filters have to skip the parameter, and export filters
+ have to insert a default value for this parameter.
+
+ The member mbValType specifies whether the parameter requires tokens to be
+ of value type (VAL or ARR class).
+
+ If set to false, the parameter is called to be REFTYPE. Tokens with REF
+ default class can be inserted for the parameter (e.g. tAreaR tokens).
+
+ If set to true, the parameter is called to be VALTYPE. Tokens with REF
+ class need to be converted to VAL tokens first (e.g. tAreaR will be
+ converted to tAreaV), and further conversion is done according to this
+ new token class.
+
+ The member meConv specifies how to convert the current token class of the
+ token inserted for the parameter. If the token class is still REF this
+ means that the token has default REF class and the parameter is REFTYPE
+ (see member mbValType), the token will not be converted at all and remains
+ in REF class. Otherwise, token class conversion is depending on the actual
+ token class of the return value of the function containing this parameter.
+ The function may return REF class (tFuncR, tFuncVarR, tFuncCER), or it may
+ return VAL or ARR class (tFuncV, tFuncA, tFuncVarV, tFuncVarA, tFuncCEV,
+ tFuncCEA). Even if the function is able to return REF class, it may return
+ VAL or ARR class instead due to the VALTYPE data type of the parent
+ function parameter that calls the own function. Example: The INDIRECT
+ function returns REF class by default. But if called from a VALTYPE
+ function parameter, e.g. in the formula =ABS(INDIRECT("A1")), it returns
+ VAL or ARR class instead. Additionally, the repeating conversion types RPT
+ and RPX rely on the conversion executed for the function token class.
+
+ 1) ORG:
+ Use the original class of the token (VAL or ARR), regardless of any
+ conversion done for the function return class.
+
+ 2) VAL:
+ Convert ARR tokens to VAL class, regardless of any conversion done for
+ the function return class.
+
+ 3) ARR:
+ Convert VAL tokens to ARR class, regardless of any conversion done for
+ the function return class.
+
+ 4) RPT:
+ If the own function returns REF class (thus it is called from a REFTYPE
+ parameter, see above), and the parent conversion type (for the function
+ return class) was ORG, VAL, or ARR, ignore that conversion and always
+ use VAL conversion for the own token instead. If the parent conversion
+ type was RPT or RPX, repeat the conversion that would have been used if
+ the function would return value type.
+ If the own function returns value type (VAL or ARR class, see above),
+ and the parent conversion type (for the function return class) was ORG,
+ VAL, ARR, or RPT, repeat this conversion for the own token. If the
+ parent conversion type was RPX, always use ORG conversion type for the
+ own token instead.
+
+ 5) RPX:
+ This type of conversion only occurs in functions returning VAL class by
+ default. If the own token is value type, and the VAL return class of
+ the own function has been changed to ARR class (due to direct ARR
+ conversion, or due to ARR conversion repeated by RPT or RPX), set the
+ own token to ARR type. Otherwise use the original token type (VAL
+ conversion from parent parameter will not be repeated at all). If
+ nested functions have RPT or value-type RPX parameters, they will not
+ repeat this conversion type, but will use ORG conversion instead (see
+ description of RPT above).
+
+ 6) RPO:
+ This type of conversion is only used for the operands of all operators
+ (unary and binary arithmetic operators, comparison operators, and range
+ operators). It is not used for function parameters. On conversion, it
+ will be replaced by the last conversion type that was not the RPO
+ conversion. This leads to a slightly different behaviour than the RPT
+ conversion for operands in conjunction with a parent RPX conversion.
+ */
+struct FunctionParamInfo
+{
+ FuncParamValidity meValid; /// Parameter validity.
+};
+
+// Function data ==============================================================
+
+/** This enumeration contains constants for all known external libraries
+ containing supported sheet functions. */
+enum FunctionLibraryType
+{
+ FUNCLIB_UNKNOWN = 0, /// Unknown library (must be zero).
+ FUNCLIB_EUROTOOL /// EuroTool add-in with EUROCONVERT function.
+};
+
+/** Represents information for a spreadsheet function.
+
+ The member mpParamInfos points to a C-array of type information structures
+ for all parameters of the function. The last initialized structure
+ describing a regular parameter (member meValid == FuncParamValidity::Regular) in
+ this array is used repeatedly for all following parameters supported by a
+ function.
+ */
+struct FunctionInfo
+{
+ OUString maOdfFuncName; /// ODF function name.
+ OUString maOoxFuncName; /// OOXML function name.
+ OUString maBiffMacroName; /// Expected macro name in EXTERN.CALL function.
+ OUString maExtProgName; /// Programmatic function name for external functions.
+ FunctionLibraryType meFuncLibType; /// The external library this function is part of.
+ sal_Int32 mnApiOpCode; /// API function opcode.
+ sal_uInt16 mnBiff12FuncId; /// BIFF12 function identifier.
+ sal_uInt16 mnBiffFuncId; /// BIFF2-BIFF8 function identifier.
+ sal_uInt8 mnMinParamCount; /// Minimum number of parameters.
+ sal_uInt8 mnMaxParamCount; /// Maximum number of parameters.
+ sal_uInt8 mnRetClass; /// BIFF token class of the return value.
+ const FunctionParamInfo* mpParamInfos; /// Information about all parameters.
+ bool mbParamPairs; /// True = optional parameters are expected to appear in pairs.
+ bool mbVolatile; /// True = volatile function.
+ bool mbExternal; /// True = external function in Calc.
+ bool mbInternal; /// True = internal function in Calc. (Both can be true!)
+ bool mbMacroFunc; /// True = macro sheet function or command.
+ bool mbVarParam; /// True = use a tFuncVar token, also if min/max are equal.
+};
+
+typedef RefVector< FunctionInfo > FunctionInfoVector;
+
+// Function info parameter class iterator =====================================
+
+/** Iterator working on the mpParamInfos member of the FunctionInfo struct.
+
+ This iterator can be used to iterate through the array containing the
+ token class conversion information of function parameters. This iterator
+ repeats the last valid structure in the array - it stops automatically
+ before the first empty array entry or before the end of the array, even for
+ repeated calls to the increment operator.
+ */
+class FunctionParamInfoIterator
+{
+public:
+ explicit FunctionParamInfoIterator( const FunctionInfo& rFuncInfo );
+
+ bool isCalcOnlyParam() const;
+ bool isExcelOnlyParam() const;
+ FunctionParamInfoIterator& operator++();
+
+private:
+ const FunctionParamInfo* mpParamInfo;
+ const FunctionParamInfo* mpParamInfoEnd;
+ bool mbParamPairs;
+};
+
+// Base function provider =====================================================
+
+struct FunctionProviderImpl;
+
+/** Provides access to function info structs for all available sheet functions.
+ */
+class FunctionProvider // not derived from WorkbookHelper to make it usable in file dumpers
+{
+public:
+ explicit FunctionProvider(bool bImportFilter);
+ virtual ~FunctionProvider();
+
+ FunctionProvider(FunctionProvider const &) = default;
+ FunctionProvider(FunctionProvider &&) = default;
+ FunctionProvider & operator =(FunctionProvider const &) = delete;
+ FunctionProvider & operator =(FunctionProvider &&) = delete;
+
+ /** Returns the function info for an OOXML function name, or 0 on error. */
+ const FunctionInfo* getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const;
+
+ /** Returns the function info for a BIFF12 function index, or 0 on error. */
+ const FunctionInfo* getFuncInfoFromBiff12FuncId( sal_uInt16 nFuncId ) const;
+
+ /** Returns the function info for a macro function referred by the
+ EXTERN.CALL function, or 0 on error. */
+ const FunctionInfo* getFuncInfoFromMacroName( const OUString& rFuncName ) const;
+
+ /** Returns the library type associated with the passed URL of a function
+ library (function add-in). */
+ static FunctionLibraryType getFuncLibTypeFromLibraryName( std::u16string_view rLibraryName );
+
+protected:
+ /** Returns the list of all function infos. */
+ const FunctionInfoVector& getFuncs() const;
+
+private:
+ typedef std::shared_ptr< FunctionProviderImpl > FunctionProviderImplRef;
+ FunctionProviderImplRef mxFuncImpl; /// Shared implementation between all copies of the provider.
+};
+
+// Op-code and function provider ==============================================
+
+struct OpCodeProviderImpl;
+
+/** Provides access to API op-codes for all available formula tokens and to
+ function info structs for all available sheet functions.
+ */
+class OpCodeProvider : public FunctionProvider // not derived from WorkbookHelper to make it usable as UNO service
+{
+public:
+ explicit OpCodeProvider(const css::uno::Reference<css::lang::XMultiServiceFactory>& rxModelFactory,
+ bool bImportFilter);
+ virtual ~OpCodeProvider() override;
+
+ OpCodeProvider(OpCodeProvider const &) = default;
+ OpCodeProvider(OpCodeProvider &&) = default;
+ OpCodeProvider & operator =(OpCodeProvider const &) = delete;
+ OpCodeProvider & operator =(OpCodeProvider &&) = delete;
+
+ /** Returns the structure containing all token op-codes for operators and
+ special tokens used by the Calc document and its formula parser. */
+ const ApiOpCodes& getOpCodes() const;
+
+ /** Returns the function info for an API token, or 0 on error. */
+ const FunctionInfo* getFuncInfoFromApiToken( const ApiToken& rToken ) const;
+
+ /** Returns the op-code map that is used by the OOXML formula parser. */
+ css::uno::Sequence< css::sheet::FormulaOpCodeMapEntry >
+ getOoxParserMap() const;
+
+private:
+ typedef std::shared_ptr< OpCodeProviderImpl > OpCodeProviderImplRef;
+ OpCodeProviderImplRef mxOpCodeImpl; /// Shared implementation between all copies of the provider.
+};
+
+// API formula parser wrapper =================================================
+
+/** A wrapper around the FormulaParser service provided by the Calc document. */
+class ApiParserWrapper : public OpCodeProvider
+{
+public:
+ explicit ApiParserWrapper(
+ const css::uno::Reference< css::lang::XMultiServiceFactory >& rxModelFactory,
+ const OpCodeProvider& rOpCodeProv );
+
+ /** Returns read/write access to the formula parser property set. */
+ PropertySet& getParserProperties() { return maParserProps; }
+
+ /** Calls the XFormulaParser::parseFormula() function of the API parser. */
+ ApiTokenSequence parseFormula(
+ const OUString& rFormula,
+ const ScAddress& rRefPos );
+
+private:
+ css::uno::Reference< css::sheet::XFormulaParser >
+ mxParser;
+ PropertySet maParserProps;
+};
+
+// Formula parser/printer base class for filters ==============================
+
+/** Base class for import formula parsers and export formula compilers. */
+class FormulaProcessorBase : public OpCodeProvider, protected ApiOpCodes, public WorkbookHelper
+{
+public:
+ explicit FormulaProcessorBase( const WorkbookHelper& rHelper );
+
+ /** Generates a cell address string in A1 notation from the passed cell
+ address.
+
+ @param rAddress The cell address containing column and row index.
+ @param bAbsolute True = adds dollar signs before column and row.
+ */
+ static OUString generateAddress2dString(
+ const ScAddress& rAddress,
+ bool bAbsolute );
+
+ /** Generates a cell address string in A1 notation from the passed binary
+ cell address.
+
+ @param rAddress The cell address containing column and row index.
+ @param bAbsolute True = adds dollar signs before column and row.
+ */
+ static OUString generateAddress2dString(
+ const BinAddress& rAddress,
+ bool bAbsolute );
+
+ /** Generates a string in Calc formula notation from the passed string.
+
+ @param rString The string value.
+
+ @return The string enclosed in double quotes, where all contained
+ quote characters are doubled.
+ */
+ static OUString generateApiString( const OUString& rString );
+
+ /** Generates an array string in Calc formula notation from the passed
+ matrix with Any's containing double values or strings.
+
+ @param rMatrix The matrix containing double values or strings.
+ */
+ static OUString generateApiArray( const Matrix< css::uno::Any >& rMatrix );
+
+ /** Tries to extract a single cell reference from a formula token sequence.
+
+ @param rTokens The token sequence to be parsed. Should contain exactly
+ one address token or cell range address token. The token sequence
+ may contain whitespace tokens.
+
+ @return If the token sequence is valid, this function returns an Any
+ containing a com.sun.star.sheet.SingleReference object, or a
+ com.sun.star.sheet.ComplexReference object. If the token sequence
+ contains too many, or unexpected tokens, an empty Any is returned.
+ */
+ css::uno::Any
+ extractReference( const ApiTokenSequence& rTokens ) const;
+
+ /** Tries to extract a cell range address from a formula token sequence.
+ Only real absolute references will be accepted.
+
+ @param orAddress (output parameter) If the token sequence is valid,
+ this parameter will contain the extracted cell range address. If
+ the token sequence contains unexpected tokens, nothing meaningful
+ is inserted, and the function returns false.
+
+ @param rTokens The token sequence to be parsed. Should contain exactly
+ one cell range address token. The token sequence may contain
+ whitespace tokens.
+
+ @return True, if the token sequence contains a valid cell range
+ address which has been extracted to orRange, false otherwise.
+ */
+ bool extractCellRange(
+ ScRange& orRange,
+ const ApiTokenSequence& rTokens ) const;
+
+ /** Tries to extract a cell range list from a formula token sequence.
+ Only real absolute references will be accepted.
+
+ @param orRanges (output parameter) If the token sequence is valid,
+ this parameter will contain the extracted cell range list. Deleted
+ cells or cell ranges (shown as #REF! error in a formula) will be
+ skipped. If the token sequence contains unexpected tokens, an empty
+ list is returned here.
+
+ @param rTokens The token sequence to be parsed. Should contain cell
+ address tokens or cell range address tokens, separated by the
+ standard function parameter separator token. The token sequence may
+ contain parentheses and whitespace tokens.
+
+ @param nFilterBySheet If non-negative, this function returns only cell
+ ranges located in the specified sheet, otherwise returns all cell
+ ranges contained in the token sequence.
+ */
+ void extractCellRangeList(
+ ScRangeList& orRanges,
+ const ApiTokenSequence& rTokens,
+ sal_Int32 nFilterBySheet ) const;
+
+ /** Tries to extract a string from a formula token sequence.
+
+ @param orString (output parameter) The extracted string.
+
+ @param rTokens The token sequence to be parsed. Should contain exactly
+ one string token, may contain whitespace tokens.
+
+ @return True = token sequence is valid, output parameter orString
+ contains the string extracted from the token sequence.
+ */
+ bool extractString(
+ OUString& orString,
+ const ApiTokenSequence& rTokens ) const;
+
+ /** Tries to extract information about a special token used for array
+ formulas, shared formulas, or table operations.
+
+ @param orTokenInfo (output parameter) The extracted information about
+ the token. Contains the base address and the token type (sal_False
+ for array or shared formulas, sal_True for table operations).
+
+ @param rTokens The token sequence to be parsed. If it contains exactly
+ one OPCODE_BAD token with special token information, this
+ information will be extracted.
+
+ @return True = token sequence is valid, output parameter orTokenInfo
+ contains the token information extracted from the token sequence.
+ */
+ bool extractSpecialTokenInfo(
+ ApiSpecialTokenInfo& orTokenInfo,
+ const ApiTokenSequence& rTokens ) const;
+
+ /** Converts a single string with separators in the passed formula token
+ sequence to a list of string tokens.
+
+ @param orTokens (input/output parameter) Expects a single string token
+ in this token sequence (whitespace tokens are allowed). The string
+ is split into substrings. A list of string tokens separated with
+ parameter separator tokens is returned in this parameter.
+
+ @param cStringSep The separator character used to split the input
+ string.
+
+ @param bTrimLeadingSpaces True = removes leading whitespace from all
+ substrings inserted into the formula token sequence.
+ */
+ void convertStringToStringList(
+ ApiTokenSequence& orTokens,
+ sal_Unicode cStringSep,
+ bool bTrimLeadingSpaces ) const;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/formulabuffer.hxx b/sc/source/filter/inc/formulabuffer.hxx
new file mode 100644
index 000000000..36e41c7ba
--- /dev/null
+++ b/sc/source/filter/inc/formulabuffer.hxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "workbookhelper.hxx"
+#include <mutex>
+#include <vector>
+
+namespace oox::xls {
+
+class FormulaBuffer : public WorkbookHelper
+{
+public:
+ /**
+ * Represents a shared formula definition.
+ */
+ struct SharedFormulaEntry
+ {
+ ScAddress maAddress;
+ OUString maTokenStr;
+ sal_Int32 mnSharedId;
+
+ SharedFormulaEntry(
+ const ScAddress& rAddress,
+ const OUString& rTokenStr, sal_Int32 nSharedId );
+ };
+
+ /**
+ * Represents a formula cell that uses shared formula.
+ */
+ struct SharedFormulaDesc
+ {
+ ScAddress maAddress;
+ OUString maCellValue;
+ sal_Int32 mnSharedId;
+ sal_Int32 mnValueType;
+
+ SharedFormulaDesc(
+ const ScAddress& rAddr, sal_Int32 nSharedId,
+ const OUString& rCellValue, sal_Int32 nValueType );
+ };
+
+ struct TokenAddressItem
+ {
+ OUString maTokenStr;
+ ScAddress maAddress;
+ TokenAddressItem( const OUString& rTokenStr, const ScAddress& rAddress ) : maTokenStr( rTokenStr ), maAddress( rAddress ) {}
+ };
+
+ struct TokenRangeAddressItem
+ {
+ TokenAddressItem maTokenAndAddress;
+ ScRange maRange;
+ TokenRangeAddressItem( const TokenAddressItem& rTokenAndAddress, const ScRange& rRange ) : maTokenAndAddress( rTokenAndAddress ), maRange( rRange ) {}
+ };
+
+ struct FormulaValue
+ {
+ ScAddress maAddress;
+ OUString maValueStr;
+ sal_Int32 mnCellType;
+ };
+
+ struct SheetItem
+ {
+ std::vector<TokenAddressItem>* mpCellFormulas;
+ std::vector<TokenRangeAddressItem>* mpArrayFormulas;
+ std::vector<FormulaValue>* mpCellFormulaValues;
+ std::vector<SharedFormulaEntry>* mpSharedFormulaEntries;
+ std::vector<SharedFormulaDesc>* mpSharedFormulaIDs;
+
+ SheetItem();
+ };
+
+private:
+
+ std::mutex maMtxData;
+ // Vectors indexed by SCTAB - cf. SetSheetCount
+ std::vector< std::vector<TokenAddressItem> > maCellFormulas;
+ std::vector< std::vector<TokenRangeAddressItem> > maCellArrayFormulas;
+ std::vector< std::vector<SharedFormulaEntry> > maSharedFormulas; // sheet -> stuff needed to create shared formulae
+ std::vector< std::vector<SharedFormulaDesc> > maSharedFormulaIds; // sheet -> list of shared formula descriptions
+ std::vector< std::vector<FormulaValue> > maCellFormulaValues; // sheet -> stuff needed to create shared formulae
+
+ SheetItem getSheetItem( SCTAB nTab );
+
+public:
+ explicit FormulaBuffer( const WorkbookHelper& rHelper );
+ void finalizeImport();
+ void setCellFormula( const ScAddress& rAddress, const OUString& );
+
+ void setCellFormula(
+ const ScAddress& rAddress, sal_Int32 nSharedId,
+ const OUString& rCellValue, sal_Int32 nValueType );
+
+ void setCellFormulaValue(
+ const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType );
+
+ void setCellArrayFormula( const ScRange& rRangeAddress,
+ const ScAddress& rTokenAddress,
+ const OUString& );
+
+ void createSharedFormulaMapEntry( const ScAddress& rAddress,
+ sal_Int32 nSharedId, const OUString& rTokens );
+
+ /// ensure sizes of vectors matches the number of sheets
+ void SetSheetCount( SCTAB nSheets );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/formulaparser.hxx b/sc/source/filter/inc/formulaparser.hxx
new file mode 100644
index 000000000..3a2030227
--- /dev/null
+++ b/sc/source/filter/inc/formulaparser.hxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "formulabase.hxx"
+
+namespace oox::xls {
+
+// formula finalizer ==========================================================
+
+/** A generic formula token array finalizer.
+
+ After building a formula token array from alien binary file formats, or
+ parsing an XML formula string using the com.sun.star.sheet.FormulaParser
+ service, the token array is still not ready to be put into the spreadsheet
+ document. There may be functions with a wrong number of parameters (missing
+ but required parameters, or unsupported parameters) or intermediate tokens
+ used to encode references to macro functions or add-in functions. This
+ helper processes a passed token array and builds a new compatible token
+ array.
+
+ Derived classes may add more functionality by overwriting the virtual
+ functions.
+ */
+class FormulaFinalizer : public OpCodeProvider, protected ApiOpCodes
+{
+public:
+ explicit FormulaFinalizer( const OpCodeProvider& rOpCodeProv );
+
+ /** Finalizes and returns the passed token array. */
+ ApiTokenSequence finalizeTokenArray( const ApiTokenSequence& rTokens );
+
+protected:
+ /** Derived classed may try to find a function info struct from the passed
+ string extracted from an OPCODE_BAD token.
+
+ @param rTokenData The string that has been found in an OPCODE_BAD
+ token preceding the function parentheses.
+ */
+ virtual const FunctionInfo* resolveBadFuncName( const OUString& rTokenData ) const;
+
+ /** Derived classed may try to find the name of a defined name with the
+ passed index extracted from an OPCODE_NAME token.
+
+ @param nTokenIndex The index of the defined name that has been found
+ in an OPCODE_NAME token preceding the function parentheses.
+ */
+ virtual OUString resolveDefinedName( sal_Int32 nTokenIndex ) const;
+
+private:
+ typedef ::std::vector< const ApiToken* > ParameterPosVector;
+
+ const FunctionInfo* getFunctionInfo( ApiToken& orFuncToken );
+ const FunctionInfo* getExternCallInfo( ApiToken& orFuncToken, const ApiToken& rECToken );
+
+ void processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd );
+ const ApiToken* processParameters( const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd );
+
+ bool isEmptyParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const;
+ const ApiToken* getSingleToken( const ApiToken* pToken, const ApiToken* pTokenEnd ) const;
+ const ApiToken* skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const;
+ const ApiToken* findParameters( ParameterPosVector& rParams, const ApiToken* pToken, const ApiToken* pTokenEnd ) const;
+ void appendEmptyParameter( const FunctionInfo& rFuncInfo, size_t nParam );
+ void appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam, size_t nParamCount );
+ void appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount );
+
+ bool appendFinalToken( const ApiToken& rToken );
+
+private:
+ ApiTokenVector maTokens;
+};
+
+class FormulaParserImpl;
+
+/** Import formula parser for OOXML and BIFF filters.
+
+ This class implements formula import for the OOXML and BIFF filter. One
+ instance is contained in the global filter data to prevent construction and
+ destruction of internal buffers for every imported formula.
+ */
+class FormulaParser : public FormulaProcessorBase
+{
+public:
+ explicit FormulaParser( const WorkbookHelper& rHelper );
+ virtual ~FormulaParser() override;
+
+ /** Converts an OOXML formula string. */
+ ApiTokenSequence importFormula(
+ const ScAddress& rBaseAddr,
+ const OUString& rFormulaString ) const;
+
+ /** Imports and converts a BIFF12 token array from the passed stream. */
+ ApiTokenSequence importFormula(
+ const ScAddress& rBaseAddr,
+ FormulaType eType,
+ SequenceInputStream& rStrm ) const;
+
+ /** Converts the passed XML formula to an OLE link target. */
+ OUString importOleTargetLink( const OUString& rFormulaString );
+
+ /** Imports and converts an OLE link target from the passed stream. */
+ OUString importOleTargetLink( SequenceInputStream& rStrm );
+
+ /** Converts the passed formula to a macro name for a drawing shape. */
+ OUString importMacroName( const OUString& rFormulaString );
+
+private:
+ ::std::unique_ptr< FormulaParserImpl > mxImpl;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/fprogressbar.hxx b/sc/source/filter/inc/fprogressbar.hxx
new file mode 100644
index 000000000..71baa3766
--- /dev/null
+++ b/sc/source/filter/inc/fprogressbar.hxx
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <rtl/ustring.hxx>
+#include <progress.hxx>
+#include <unotools/resmgr.hxx>
+
+class SfxObjectShell;
+class SvStream;
+
+const sal_Int32 SCF_INV_SEGMENT = -1;
+
+/** Progress bar for complex progress representation.
+
+ The progress bar contains one or more segments, each with customizable
+ size. Each segment is represented by a unique identifier. While showing the
+ progress bar, several segments can be started simultaneously. The progress
+ bar displays the sum of all started segments on screen.
+
+ It is possible to create a full featured ScfProgressBar object from
+ any segment. This sub progress bar works only on that parent segment, with
+ the effect, that if the sub progress bar reaches 100%, the parent segment is
+ filled completely.
+
+ After adding segments, the progress bar has to be activated. In this step the
+ total size of all segments is calculated. Therefore it is not possible to add
+ more segments from here.
+
+ If a sub progress bar is created from a segment, and the main progress bar
+ has been started (but not the sub progress bar), it is still possible to add
+ segments to the sub progress bar. It is not allowed to get the sub progress bar
+ of a started segment. And it is not allowed to modify the segment containing
+ a sub progress bar directly.
+
+ Following a few code examples, how to use the progress bar.
+
+ Example 1: Simple progress bar (see also ScfSimpleProgressBar below).
+
+ ScfProgressBar aProgress( ... );
+ sal_Int32 nSeg = aProgress.AddSegment( 50 ); // segment with 50 steps (1 step = 2%)
+
+ aProgress.ActivateSegment( nSeg ); // start segment nSeg
+ aProgress.Progress(); // 0->1; display: 2%
+ aProgress.ProgressAbs( 9 ); // 1->9; display: 18%
+
+ Example 2: Progress bar with 2 segments.
+
+ ScfProgressBar aProgress( ... );
+ sal_Int32 nSeg1 = aProgress.AddSegment( 70 ); // segment with 70 steps
+ sal_Int32 nSeg2 = aProgress.AddSegment( 30 ); // segment with 30 steps
+ // both segments: 100 steps (1 step = 1%)
+
+ aProgress.ActivateSegment( nSeg1 ); // start first segment
+ aProgress.Progress(); // 0->1, display: 1%
+ aProgress.Progress( 2 ); // 1->3, display: 3%
+ aProgress.ActivateSegment( nSeg2 ); // start second segment
+ aProgress.Progress( 5 ); // 0->5, display: 8% (5+3 steps)
+ aProgress.ActivateSegment( nSeg1 ); // continue with first segment
+ aProgress.Progress(); // 3->4, display: 9% (5+4 steps)
+
+ Example 3: Progress bar with 2 segments, one contains a sub progress bar.
+
+ ScfProgressBar aProgress( ... );
+ sal_Int32 nSeg1 = aProgress.AddSegment( 75 ); // segment with 75 steps
+ sal_Int32 nSeg2 = aProgress.AddSegment( 25 ); // segment with 25 steps
+ // both segments: 100 steps (1 step = 1%)
+
+ aProgress.ActivateSegment( nSeg1 ); // start first segment
+ aProgress.Progress(); // 0->1, display: 1%
+
+ ScfProgressBar& rSubProgress = aProgress.GetSegmentProgressBar( nSeg2 );
+ // sub progress bar from second segment
+ sal_Int32 nSubSeg = rSubProgress.AddSegment( 5 ); // 5 steps, mapped to second segment
+ // => 1 step = 5 steps in parent = 5%
+
+ rSubProgress.ActivateSegment( nSubSeg ); // start the segment (auto activate parent segment)
+ rSubProgress.Progress(); // 0->1 (0->5 in parent); display: 6% (1+5)
+
+ // not allowed (second segment active): aProgress.Progress();
+ // not allowed (first segment not empty): aProgress.GetSegmentProgressBar( nSeg1 );
+ */
+class ScfProgressBar final
+{
+public:
+ ScfProgressBar(const ScfProgressBar&) = delete;
+ const ScfProgressBar operator=(const ScfProgressBar&) = delete;
+
+ explicit ScfProgressBar(SfxObjectShell* pDocShell, const OUString& rText);
+ explicit ScfProgressBar(SfxObjectShell* pDocShell, TranslateId pResId);
+ ~ScfProgressBar();
+
+ /** Adds a new segment to the progress bar.
+ @return the identifier of the segment. */
+ sal_Int32 AddSegment( std::size_t nSize );
+ /** Returns a complete progress bar for the specified segment.
+ @descr The progress bar can be used to create sub segments inside of the
+ segment. Do not delete it (done by root progress bar)!
+ @return A reference to an ScfProgressBar connected to the segment. */
+ ScfProgressBar& GetSegmentProgressBar( sal_Int32 nSegment );
+
+ /** Returns true, if the current progress segment is already full. */
+ bool IsFull() const;
+
+ /** Starts the progress bar or activates another segment. */
+ void ActivateSegment( sal_Int32 nSegment );
+ /** Starts the progress bar (with first segment). */
+ void Activate() { ActivateSegment( 0 ); }
+ /** Set current segment to the specified absolute position. */
+ void ProgressAbs( std::size_t nPos );
+ /** Increase current segment by the passed value. */
+ void Progress( std::size_t nDelta = 1 );
+
+private:
+ struct ScfProgressSegment;
+
+ /** Used to create sub progress bars. */
+ explicit ScfProgressBar(
+ ScfProgressBar& rParProgress,
+ ScfProgressSegment* pParSegment );
+
+ /** Initializes all members on construction. */
+ void Init( SfxObjectShell* pDocShell );
+
+ /** Returns the segment specified by list index. */
+ ScfProgressSegment* GetSegment( sal_Int32 nSegment );
+ /** Activates progress bar and sets current segment. */
+ void SetCurrSegment( ScfProgressSegment* pSegment );
+ /** Increases mnTotalPos and calls the system progress bar. */
+ void IncreaseProgressBar( std::size_t nDelta );
+
+private:
+ /** Contains all data of a segment of the progress bar. */
+ struct ScfProgressSegment
+ {
+ typedef ::std::unique_ptr< ScfProgressBar > ScfProgressBarPtr;
+
+ ScfProgressBarPtr mxProgress; /// Pointer to sub progress bar for this segment.
+ std::size_t mnSize; /// Size of this segment.
+ std::size_t mnPos; /// Current position of this segment.
+
+ explicit ScfProgressSegment( std::size_t nSize );
+ ~ScfProgressSegment();
+ };
+
+ typedef ::std::unique_ptr< ScProgress > ScProgressPtr;
+ typedef std::vector< std::unique_ptr<ScfProgressSegment> > ScfSegmentList;
+
+ ScfSegmentList maSegments; /// List of progress segments.
+ OUString maText; /// UI string for system progress.
+
+ ScProgressPtr mxSysProgress; /// System progress bar.
+ SfxObjectShell* mpDocShell; /// The document shell for the progress bar.
+ ScfProgressBar* mpParentProgress; /// Parent progress bar, if this is a segment progress bar.
+ ScfProgressSegment* mpParentSegment; /// Parent segment, if this is a segment progress bar.
+ ScfProgressSegment* mpCurrSegment; /// Current segment for progress.
+
+ std::size_t mnTotalSize; /// Total size of all segments.
+ std::size_t mnTotalPos; /// Sum of positions of all segments.
+ std::size_t mnUnitSize; /// Size between two calls of system progress.
+ std::size_t mnNextUnitPos; /// Limit for next system progress call.
+ std::size_t mnSysProgressScale; /// Additionally scaling factor for system progress.
+ bool mbInProgress; /// true = progress bar started.
+};
+
+/** A simplified progress bar with only one segment. */
+class ScfSimpleProgressBar
+{
+public:
+ explicit ScfSimpleProgressBar(std::size_t nSize, SfxObjectShell* pDocShell, const OUString& rText);
+ explicit ScfSimpleProgressBar(std::size_t nSize, SfxObjectShell* pDocShell, TranslateId pResId);
+
+ /** Set progress bar to the specified position. */
+ void ProgressAbs( std::size_t nPos ) { maProgress.ProgressAbs( nPos ); }
+
+private:
+ /** Initializes and starts the progress bar. */
+ void Init( std::size_t nSize );
+
+private:
+ ScfProgressBar maProgress; /// The used progress bar.
+};
+
+/** A simplified progress bar based on the stream position of an existing stream. */
+class ScfStreamProgressBar
+{
+public:
+ explicit ScfStreamProgressBar( SvStream& rStrm, SfxObjectShell* pDocShell );
+
+ /** Sets the progress bar to the current stream position. */
+ void Progress();
+
+private:
+ /** Initializes and starts the progress bar. */
+ void Init( SfxObjectShell* pDocShell, const OUString& rText );
+
+private:
+ typedef ::std::unique_ptr< ScfSimpleProgressBar > ScfSimpleProgressBarPtr;
+
+ ScfSimpleProgressBarPtr mxProgress; /// The used progress bar.
+ SvStream& mrStrm; /// The used stream.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/ftools.hxx b/sc/source/filter/inc/ftools.hxx
new file mode 100644
index 000000000..1366a5197
--- /dev/null
+++ b/sc/source/filter/inc/ftools.hxx
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <algorithm>
+#include <vector>
+#include <limits>
+#include <string_view>
+
+#include <tools/ref.hxx>
+#include <filter.hxx>
+
+// Common macros ==============================================================
+
+// items and item sets --------------------------------------------------------
+
+/** Expands to the item (with type 'itemtype') with Which-ID 'which'. */
+#define GETITEM( itemset, itemtype, which ) \
+ static_cast< const itemtype & >( (itemset).Get( which ) )
+
+/** Expands to the value of the SfxBoolItem with Which-ID 'which'. */
+#define GETITEMBOOL( itemset, which ) \
+ (static_cast<const SfxBoolItem &>( (itemset).Get( which )).GetValue() )
+
+// Global static helpers ======================================================
+
+// Value range limit helpers --------------------------------------------------
+
+/** Returns the value, if it is not less than nMin, otherwise nMin. */
+template< typename ReturnType, typename Type >
+inline ReturnType llimit_cast( Type nValue, ReturnType nMin )
+{ return static_cast< ReturnType >( ::std::max< Type >( nValue, nMin ) ); }
+
+/** Returns the value, if it is not greater than nMax, otherwise nMax. */
+template< typename ReturnType, typename Type >
+inline ReturnType ulimit_cast( Type nValue, ReturnType nMax )
+{ return static_cast< ReturnType >( ::std::min< Type >( nValue, nMax ) ); }
+
+/** Returns the value, if it fits into ReturnType, otherwise the maximum value of ReturnType. */
+template< typename ReturnType, typename Type >
+inline ReturnType ulimit_cast( Type nValue )
+{ return ulimit_cast( nValue, ::std::numeric_limits< ReturnType >::max() ); }
+
+/** Returns the value, if it is not less than nMin and not greater than nMax, otherwise one of the limits. */
+template< typename ReturnType, typename Type >
+inline ReturnType limit_cast( Type nValue, ReturnType nMin, ReturnType nMax )
+{ return static_cast< ReturnType >( ::std::clamp< Type >( nValue, nMin, nMax ) ); }
+
+/** Returns the value, if it fits into ReturnType, otherwise one of the limits of ReturnType. */
+template< typename ReturnType, typename Type >
+inline ReturnType limit_cast( Type nValue )
+{ return limit_cast( nValue, ::std::numeric_limits< ReturnType >::min(), ::std::numeric_limits< ReturnType >::max() ); }
+
+// Read from bitfields --------------------------------------------------------
+
+/** Returns true, if at least one of the bits set in nMask is set in nBitField. */
+template< typename Type >
+inline bool get_flag( Type nBitField, Type nMask )
+{ return (nBitField & nMask) != 0; }
+
+/** Returns nSet, if at least one bit of nMask is set in nBitField, otherwise nUnset. */
+template< typename ReturnType, typename Type >
+inline ReturnType get_flagvalue( Type nBitField, Type nMask, ReturnType nSet, ReturnType nUnset )
+{ return ::get_flag( nBitField, nMask ) ? nSet : nUnset; }
+
+/** Extracts a value from a bit field.
+ @descr Returns in rnRet the data fragment from nBitField, that starts at bit nStartBit
+ (0-based, bit 0 is rightmost) with the width of nBitCount. rnRet will be right-aligned (normalized).
+ For instance: extract_value( n, 0x4321, 8, 4 ) stores 3 in n (value in bits 8-11). */
+template< typename ReturnType, typename Type >
+inline ReturnType extract_value( Type nBitField, sal_uInt8 nStartBit, sal_uInt8 nBitCount )
+{ return static_cast< ReturnType >( ((1UL << nBitCount) - 1) & (nBitField >> nStartBit) ); }
+
+// Write to bitfields ---------------------------------------------------------
+
+/** Sets or clears (according to bSet) all set bits of nMask in rnBitField. */
+template< typename Type >
+inline void set_flag( Type& rnBitField, Type nMask, bool bSet = true )
+{ if( bSet ) rnBitField |= nMask; else rnBitField &= ~nMask; }
+
+/** Inserts a value into a bitfield.
+ @descr Inserts the lower nBitCount bits of nValue into rnBitField, starting
+ there at bit nStartBit. Other contents of rnBitField keep unchanged. */
+template< typename Type, typename InsertType >
+void insert_value( Type& rnBitField, InsertType nValue, sal_uInt8 nStartBit, sal_uInt8 nBitCount )
+{
+ unsigned int nMask = (1U << nBitCount) - 1;
+ Type nNewValue = static_cast< Type >( nValue & nMask );
+ rnBitField = (rnBitField & ~(nMask << nStartBit)) | (nNewValue << nStartBit);
+}
+
+class Color;
+class SfxPoolItem;
+class SfxItemSet;
+class ScStyleSheet;
+class ScStyleSheetPool;
+class SvStream;
+class SotStorage;
+class SotStorageStream;
+
+/** Contains static methods used anywhere in the filters. */
+class ScfTools
+{
+public:
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static items. */
+ ScfTools() = delete;
+ ScfTools(const ScfTools&) = delete;
+ const ScfTools& operator=(const ScfTools&) = delete;
+
+// *** common methods *** -----------------------------------------------------
+
+ /** Reads a 10-byte-long-double and converts it to double. */
+ static void ReadLongDouble(SvStream& rStrm, double& fResult);
+ /** Returns system text encoding for byte string conversion. */
+ static rtl_TextEncoding GetSystemTextEncoding();
+ /** Returns a string representing the hexadecimal value of nValue. */
+ static OUString GetHexStr( sal_uInt16 nValue );
+
+ /** Mixes RGB components with given transparence.
+ @param nTrans Foreground transparence (0x00 == full nFore ... 0x80 = full nBack). */
+ static sal_uInt8 GetMixedColorComp( sal_uInt8 nFore, sal_uInt8 nBack, sal_uInt8 nTrans );
+ /** Mixes colors with given transparence.
+ @param nTrans Foreground transparence (0x00 == full rFore ... 0x80 = full rBack). */
+ static Color GetMixedColor( const Color& rFore, const Color& rBack, sal_uInt8 nTrans );
+
+// *** conversion of names *** ------------------------------------------------
+
+ /** Converts a string to a valid Calc defined name or database range name.
+ @descr Defined names in Calc may contain letters, digits (*), underscores, periods (*),
+ colons (*), question marks, and dollar signs.
+ (*) = not allowed at first position. */
+ static OUString ConvertToScDefinedName( const OUString& rName );
+
+// *** streams and storages *** -----------------------------------------------
+
+ /** Tries to open an existing storage with the specified name in the passed storage (read-only). */
+ static tools::SvRef<SotStorage> OpenStorageRead( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName );
+ /** Creates and opens a storage with the specified name in the passed storage (read/write). */
+ static tools::SvRef<SotStorage> OpenStorageWrite( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName );
+
+ /** Tries to open an existing stream with the specified name in the passed storage (read-only). */
+ static tools::SvRef<SotStorageStream> OpenStorageStreamRead( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName );
+ /** Creates and opens a stream with the specified name in the passed storage (read/write). */
+ static tools::SvRef<SotStorageStream> OpenStorageStreamWrite( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName );
+
+// *** item handling *** ------------------------------------------------------
+
+ /** Returns true, if the passed item set contains the item.
+ @param bDeep true = Searches in parent item sets too. */
+ static bool CheckItem( const SfxItemSet& rItemSet, sal_uInt16 nWhichId, bool bDeep );
+ /** Returns true, if the passed item set contains at least one of the items.
+ @param pnWhichIds Zero-terminated array of Which-IDs.
+ @param bDeep true = Searches in parent item sets too. */
+ static bool CheckItems( const SfxItemSet& rItemSet, const sal_uInt16* pnWhichIds, bool bDeep );
+
+ /** Puts the item into the passed item set.
+ @descr The item will be put into the item set, if bSkipPoolDef is false,
+ or if the item differs from the default pool item.
+ @param rItemSet The destination item set.
+ @param rItem The item to put into the item set.
+ @param nWhichId The Which-ID to set with the item.
+ @param bSkipPoolDef true = Do not put item if it is equal to pool default; false = Always put the item. */
+ static void PutItem(
+ SfxItemSet& rItemSet, const SfxPoolItem& rItem,
+ sal_uInt16 nWhichId, bool bSkipPoolDef );
+
+ /** Puts the item into the passed item set.
+ @descr The item will be put into the item set, if bSkipPoolDef is false,
+ or if the item differs from the default pool item.
+ @param rItemSet The destination item set.
+ @param rItem The item to put into the item set.
+ @param bSkipPoolDef true = Do not put item if it is equal to pool default; false = Always put the item. */
+ static void PutItem( SfxItemSet& rItemSet, const SfxPoolItem& rItem, bool bSkipPoolDef );
+
+// *** style sheet handling *** -----------------------------------------------
+
+ /** Creates and returns a cell style sheet and inserts it into the pool.
+ @descr If the style sheet is already in the pool, another unused style name is used.
+ @param bForceName Controls behaviour, if the style already exists:
+ true = Old existing style will be renamed; false = New style gets another name. */
+ static ScStyleSheet& MakeCellStyleSheet(
+ ScStyleSheetPool& rPool,
+ const OUString& rStyleName, bool bForceName );
+ /** Creates and returns a page style sheet and inserts it into the pool.
+ @descr If the style sheet is already in the pool, another unused style name is used.
+ @param bForceName Controls behaviour, if the style already exists:
+ true = Old existing style will be renamed; false = New style gets another name. */
+ static ScStyleSheet& MakePageStyleSheet(
+ ScStyleSheetPool& rPool,
+ const OUString& rStyleName, bool bForceName );
+
+// *** byte string import operations *** --------------------------------------
+
+ /** Reads and returns a zero terminated byte string and decreases a stream counter. */
+ static OString read_zeroTerminated_uInt8s_ToOString(SvStream& rStrm, sal_Int32& rnBytesLeft);
+ /** Reads and returns a zero terminated byte string and decreases a stream counter. */
+ static OUString read_zeroTerminated_uInt8s_ToOUString(SvStream& rStrm, sal_Int32& rnBytesLeft, rtl_TextEncoding eTextEnc)
+ {
+ return OStringToOUString(read_zeroTerminated_uInt8s_ToOString(rStrm, rnBytesLeft), eTextEnc);
+ }
+
+ /** Appends a zero terminated byte string. */
+ static void AppendCString( SvStream& rStrm, OUString& rString, rtl_TextEncoding eTextEnc );
+
+// *** HTML table names <-> named range names *** -----------------------------
+
+ /** Returns the built-in range name for an HTML document. */
+ static const OUString& GetHTMLDocName();
+ /** Returns the built-in range name for all HTML tables. */
+ static const OUString& GetHTMLTablesName();
+ /** Returns the built-in range name for an HTML table, specified by table index. */
+ static OUString GetNameFromHTMLIndex( sal_uInt32 nIndex );
+ /** Returns the built-in range name for an HTML table, specified by table name. */
+ static OUString GetNameFromHTMLName( std::u16string_view rTabName );
+
+ /** Returns true, if rSource is the built-in range name for an HTML document. */
+ static bool IsHTMLDocName( std::u16string_view rSource );
+ /** Returns true, if rSource is the built-in range name for all HTML tables. */
+ static bool IsHTMLTablesName( std::u16string_view rSource );
+ /** Converts a built-in range name to an HTML table name.
+ @param rSource The string to be determined.
+ @param rName The HTML table name.
+ @return true, if conversion was successful. */
+ static bool GetHTMLNameFromName( const OUString& rSource, OUString& rName );
+
+private:
+ /** Returns the prefix for table index names. */
+ static const OUString& GetHTMLIndexPrefix();
+ /** Returns the prefix for table names. */
+ static const OUString& GetHTMLNamePrefix();
+};
+
+// Containers =================================================================
+
+typedef ::std::vector< sal_uInt8 > ScfUInt8Vec;
+typedef ::std::vector< sal_Int16 > ScfInt16Vec;
+typedef ::std::vector< sal_uInt16 > ScfUInt16Vec;
+typedef ::std::vector< sal_Int32 > ScfInt32Vec;
+typedef ::std::vector< sal_uInt32 > ScfUInt32Vec;
+typedef ::std::vector< OUString > ScfStringVec;
+
+class ScFormatFilterPluginImpl : public ScFormatFilterPlugin
+{
+public:
+ ScFormatFilterPluginImpl();
+ virtual ~ScFormatFilterPluginImpl();
+ // various import filters
+ virtual ErrCode ScImportLotus123( SfxMedium&, ScDocument&, rtl_TextEncoding eSrc ) override;
+ virtual ErrCode ScImportQuattroPro(SvStream* pStream, ScDocument& rDoc) override;
+ virtual ErrCode ScImportExcel( SfxMedium&, ScDocument*, const EXCIMPFORMAT ) override;
+ // eFormat == EIF_AUTO -> matching filter is used automatically
+ // eFormat == EIF_BIFF5 -> only Biff5 stream leads to success (even in an Excel97 doc)
+ // eFormat == EIF_BIFF8 -> only Biff8 stream leads to success (only in Excel97 docs)
+ // eFormat == EIF_BIFF_LE4 -> only non-storage files _could_ lead to success
+ virtual ErrCode ScImportDif( SvStream&, ScDocument*, const ScAddress& rInsPos,
+ const rtl_TextEncoding eSrc ) override;
+ virtual ErrCode ScImportRTF( SvStream&, const OUString& rBaseURL, ScDocument*, ScRange& rRange ) override;
+ virtual ErrCode ScImportHTML( SvStream&, const OUString& rBaseURL, ScDocument*, ScRange& rRange,
+ double nOutputFactor, bool bCalcWidthHeight,
+ SvNumberFormatter* pFormatter, bool bConvertDate ) override;
+
+ virtual std::unique_ptr<ScEEAbsImport> CreateRTFImport( ScDocument* pDoc, const ScRange& rRange ) override;
+ virtual std::unique_ptr<ScEEAbsImport> CreateHTMLImport( ScDocument* pDocP, const OUString& rBaseURL, const ScRange& rRange ) override;
+ virtual OUString GetHTMLRangeNameList( ScDocument& rDoc, const OUString& rOrigName ) override;
+
+ // various export filters
+ virtual ErrCode ScExportExcel5( SfxMedium&, ScDocument*, ExportFormatExcel eFormat, rtl_TextEncoding eDest ) override;
+ virtual void ScExportDif( SvStream&, ScDocument*, const ScAddress& rOutPos, const rtl_TextEncoding eDest ) override;
+ virtual void ScExportDif( SvStream&, ScDocument*, const ScRange& rRange, const rtl_TextEncoding eDest ) override;
+ virtual void ScExportHTML( SvStream&, const OUString& rBaseURL, ScDocument*, const ScRange& rRange, const rtl_TextEncoding eDest, bool bAll,
+ const OUString& rStreamPath, OUString& rNonConvertibleChars, const OUString& rFilterOptions ) override;
+ virtual void ScExportRTF( SvStream&, ScDocument*, const ScRange& rRange, const rtl_TextEncoding eDest ) override;
+
+ virtual ScOrcusFilters* GetOrcusFilters() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/htmlexp.hxx b/sc/source/filter/inc/htmlexp.hxx
new file mode 100644
index 000000000..29d5b3b1b
--- /dev/null
+++ b/sc/source/filter/inc/htmlexp.hxx
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/textenc.h>
+#include <tools/gen.hxx>
+#include <tools/color.hxx>
+#include <vcl/vclptr.hxx>
+#include <vector>
+#include <memory>
+#include <map>
+#include <svx/xoutbmp.hxx>
+
+#include "expbase.hxx"
+
+class ScDocument;
+class SfxItemSet;
+class SdrPage;
+class Graphic;
+class SdrObject;
+class OutputDevice;
+class ScDrawLayer;
+class EditTextObject;
+enum class SvtScriptType : sal_uInt8;
+namespace editeng { class SvxBorderLine; }
+
+namespace sc {
+struct ColumnBlockPosition;
+}
+
+struct ScHTMLStyle
+{ // Defaults from stylesheet
+ Color aBackgroundColor;
+ OUString aFontFamilyName;
+ sal_uInt32 nFontHeight; // Item-Value
+ sal_uInt16 nFontSizeNumber; // HTML value 1-7
+ SvtScriptType nDefaultScriptType; // Font values are valid for the default script type
+ bool bInitialized;
+
+ ScHTMLStyle() :
+ nFontHeight(0),
+ nFontSizeNumber(2),
+ nDefaultScriptType(),
+ bInitialized(false)
+ {}
+
+ const ScHTMLStyle& operator=( const ScHTMLStyle& rScHTMLStyle )
+ {
+ aBackgroundColor = rScHTMLStyle.aBackgroundColor;
+ aFontFamilyName = rScHTMLStyle.aFontFamilyName;
+ nFontHeight = rScHTMLStyle.nFontHeight;
+ nFontSizeNumber = rScHTMLStyle.nFontSizeNumber;
+ nDefaultScriptType = rScHTMLStyle.nDefaultScriptType;
+ bInitialized = rScHTMLStyle.bInitialized;
+ return *this;
+ }
+};
+
+struct ScHTMLGraphEntry
+{
+ ScRange aRange; // mapped range
+ Size aSize; // size in pixels
+ Size aSpace; // spacing in pixels
+ SdrObject* pObject;
+ bool bInCell; // if output is in cell
+ bool bWritten;
+
+ ScHTMLGraphEntry( SdrObject* pObj, const ScRange& rRange,
+ const Size& rSize, bool bIn, const Size& rSpace ) :
+ aRange( rRange ),
+ aSize( rSize ),
+ aSpace( rSpace ),
+ pObject( pObj ),
+ bInCell( bIn ),
+ bWritten( false )
+ {}
+};
+
+#define SC_HTML_FONTSIZES 7
+const short nIndentMax = 23;
+
+class ScHTMLExport : public ScExportBase
+{
+ // default HtmlFontSz[1-7]
+ static const sal_uInt16 nDefaultFontSize[SC_HTML_FONTSIZES];
+ // HtmlFontSz[1-7] in s*3.ini [user]
+ static sal_uInt16 nFontSize[SC_HTML_FONTSIZES];
+ static const char* pFontSizeCss[SC_HTML_FONTSIZES];
+ static const sal_uInt16 nCellSpacing;
+ static const char sIndentSource[];
+
+ typedef std::unique_ptr<std::map<OUString, OUString> > FileNameMapPtr;
+ typedef std::vector<ScHTMLGraphEntry> GraphEntryList;
+
+ GraphEntryList aGraphList;
+ ScHTMLStyle aHTMLStyle;
+ OUString aBaseURL;
+ OUString aStreamPath;
+ VclPtr<OutputDevice> pAppWin; // for Pixel-work
+ FileNameMapPtr pFileNameMap; // for CopyLocalFileToINet
+ OUString aNonConvertibleChars; // collect nonconvertible characters
+ SCTAB nUsedTables;
+ short nIndent;
+ char sIndent[nIndentMax+1];
+ bool bAll; // whole document
+ bool bTabHasGraphics;
+ bool bTabAlignedLeft;
+ bool bCalcAsShown;
+ bool bCopyLocalFileToINet;
+ bool bTableDataHeight;
+ bool mbSkipImages;
+ /// If HTML header and footer should be written as well, or just the content itself.
+ bool mbSkipHeaderFooter;
+
+ const SfxItemSet& PageDefaults( SCTAB nTab );
+
+ void WriteBody();
+ void WriteHeader();
+ void WriteOverview();
+ void WriteTables();
+ void WriteCell( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow, SCTAB nTab );
+ void WriteGraphEntry( ScHTMLGraphEntry* );
+ void WriteImage( OUString& rLinkName,
+ const Graphic&, std::string_view rImgOptions,
+ XOutFlags nXOutFlags = XOutFlags::NONE );
+ // nXOutFlags for XOutBitmap::WriteGraphic
+
+ // write to stream if and only if URL fields in edit cell
+ bool WriteFieldText( const EditTextObject* pData );
+
+ // copy a local file to internet if needed
+ void CopyLocalFileToINet( OUString& rFileNm, std::u16string_view rTargetNm );
+
+ void PrepareGraphics( ScDrawLayer*, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow );
+
+ void FillGraphList( const SdrPage*, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow );
+
+ static OString BorderToStyle(const char* pBorderName,
+ const editeng::SvxBorderLine* pLine,
+ bool& bInsertSemicolon);
+
+ static sal_uInt16 GetFontSizeNumber( sal_uInt16 nHeight );
+ static const char* GetFontSizeCss( sal_uInt16 nHeight );
+ sal_uInt16 ToPixel( sal_uInt16 nTwips );
+ Size MMToPixel( const Size& r100thMMSize );
+ void IncIndent( short nVal );
+
+ const char* GetIndentStr() const
+ {
+ return sIndent;
+ }
+
+public:
+ ScHTMLExport( SvStream&, const OUString&, ScDocument*, const ScRange&,
+ bool bAll, const OUString& aStreamPath, std::u16string_view rFilterOptions );
+ virtual ~ScHTMLExport() override;
+ void Write();
+ const OUString& GetNonConvertibleChars() const
+ {
+ return aNonConvertibleChars;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/htmlimp.hxx b/sc/source/filter/inc/htmlimp.hxx
new file mode 100644
index 000000000..76acc4471
--- /dev/null
+++ b/sc/source/filter/inc/htmlimp.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "eeimport.hxx"
+
+class ScHTMLImport : public ScEEImport
+{
+private:
+ static void InsertRangeName( ScDocument& rDoc, const OUString& rName, const ScRange& rRange );
+
+public:
+ ScHTMLImport( ScDocument* pDoc, const OUString& rBaseURL, const ScRange& rRange, bool bCalcWidthHeight );
+
+ virtual void WriteToDocument( bool bSizeColsRows = false, double nOutputFactor = 1.0,
+ SvNumberFormatter* pFormatter = nullptr, bool bConvertDate = true ) override;
+
+ static OUString GetHTMLRangeNameList( const ScDocument& rDoc, std::u16string_view rOrigName );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/htmlpars.hxx b/sc/source/filter/inc/htmlpars.hxx
new file mode 100644
index 000000000..47ecc57b4
--- /dev/null
+++ b/sc/source/filter/inc/htmlpars.hxx
@@ -0,0 +1,630 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <map>
+#include <optional>
+#include <stack>
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+#include <o3tl/sorted_vector.hxx>
+
+#include <rangelst.hxx>
+#include "eeparser.hxx"
+
+const sal_uInt32 SC_HTML_FONTSIZES = 7; // like export, HTML options
+
+// Pixel tolerance for SeekOffset and related.
+const sal_uInt16 SC_HTML_OFFSET_TOLERANCE_SMALL = 1; // single table
+const sal_uInt16 SC_HTML_OFFSET_TOLERANCE_LARGE = 10; // nested
+
+// BASE class for HTML parser classes
+
+class ScHTMLTable;
+
+/**
+ * Collection of HTML style data parsed from the content of <style>
+ * elements.
+ */
+class ScHTMLStyles
+{
+ typedef std::unordered_map<OUString, OUString> PropsType;
+ typedef ::std::map<OUString, PropsType> NamePropsType;
+ typedef ::std::map<OUString, NamePropsType> ElemsType;
+
+ NamePropsType m_GlobalProps; /// global properties (for a given class for all elements)
+ NamePropsType m_ElemGlobalProps; /// element global properties (no class specified)
+ ElemsType m_ElemProps; /// element to class to properties (both element and class are given)
+ const OUString maEmpty; /// just a persistent empty string.
+public:
+ ScHTMLStyles();
+
+ void add(const char* pElemName, size_t nElemName, const char* pClassName, size_t nClassName,
+ const OUString& aProp, const OUString& aValue);
+
+ /**
+ * Find best-matching property value for given element and class names.
+ */
+ const OUString& getPropertyValue(
+ const OUString& rElem, const OUString& rClass, const OUString& rPropName) const;
+
+private:
+ static void insertProp(
+ NamePropsType& rProps, const OUString& aName,
+ const OUString& aProp, const OUString& aValue);
+};
+
+/** Base class for HTML parser classes. */
+class ScHTMLParser : public ScEEParser
+{
+ ScHTMLStyles maStyles;
+protected:
+ sal_uInt32 maFontHeights[ SC_HTML_FONTSIZES ];
+ ScDocument* mpDoc; /// The destination document.
+
+public:
+ explicit ScHTMLParser( EditEngine* pEditEngine, ScDocument* pDoc );
+ virtual ~ScHTMLParser() override;
+
+ virtual ErrCode Read( SvStream& rStrm, const OUString& rBaseURL ) override = 0;
+
+ ScHTMLStyles& GetStyles() { return maStyles;}
+ ScDocument& GetDoc() { return *mpDoc;}
+
+ /** Returns the "global table" which contains the entire HTML document. */
+ virtual const ScHTMLTable* GetGlobalTable() const = 0;
+};
+
+typedef o3tl::sorted_vector<sal_uLong> ScHTMLColOffset;
+
+struct ScHTMLTableStackEntry
+{
+ ScRangeListRef xLockedList;
+ std::shared_ptr<ScEEParseEntry> xCellEntry;
+ ScHTMLColOffset* pLocalColOffset;
+ sal_uLong nFirstTableCell;
+ SCROW nRowCnt;
+ SCCOL nColCntStart;
+ SCCOL nMaxCol;
+ sal_uInt16 nTable;
+ sal_uInt16 nTableWidth;
+ sal_uInt16 nColOffset;
+ sal_uInt16 nColOffsetStart;
+ bool bFirstRow;
+ ScHTMLTableStackEntry( const std::shared_ptr<ScEEParseEntry>& rE,
+ const ScRangeListRef& rL, ScHTMLColOffset* pTO,
+ sal_uLong nFTC,
+ SCROW nRow,
+ SCCOL nStart, SCCOL nMax, sal_uInt16 nTab,
+ sal_uInt16 nTW, sal_uInt16 nCO, sal_uInt16 nCOS,
+ bool bFR )
+ : xLockedList( rL ), xCellEntry(rE),
+ pLocalColOffset( pTO ),
+ nFirstTableCell( nFTC ),
+ nRowCnt( nRow ),
+ nColCntStart( nStart ), nMaxCol( nMax ),
+ nTable( nTab ), nTableWidth( nTW ),
+ nColOffset( nCO ), nColOffsetStart( nCOS ),
+ bFirstRow( bFR )
+ {}
+};
+
+struct ScHTMLAdjustStackEntry
+{
+ SCCOL nLastCol;
+ SCROW nNextRow;
+ SCROW nCurRow;
+ ScHTMLAdjustStackEntry( SCCOL nLCol, SCROW nNRow,
+ SCROW nCRow )
+ : nLastCol( nLCol ), nNextRow( nNRow ),
+ nCurRow( nCRow )
+ {}
+};
+
+class EditEngine;
+class ScDocument;
+class HTMLOption;
+
+// TODO these need better names
+typedef ::std::map<SCROW, SCROW> InnerMap;
+typedef ::std::map<sal_uInt16, InnerMap*> OuterMap;
+
+class ScHTMLLayoutParser : public ScHTMLParser
+{
+private:
+ Size aPageSize;
+ OUString aBaseURL;
+ ::std::stack< std::unique_ptr<ScHTMLTableStackEntry> >
+ aTableStack;
+ OUString aString;
+ ScRangeListRef xLockedList; // per table
+ std::unique_ptr<OuterMap> pTables;
+ ScHTMLColOffset maColOffset;
+ ScHTMLColOffset* pLocalColOffset; // per table
+ sal_uLong nFirstTableCell; // per table
+ short nTableLevel;
+ sal_uInt16 nTable;
+ sal_uInt16 nMaxTable;
+ SCCOL nColCntStart; // first Col per table
+ SCCOL nMaxCol; // per table
+ sal_uInt16 nTableWidth; // per table
+ sal_uInt16 nColOffset; // current, pixel
+ sal_uInt16 nColOffsetStart; // start value per table, in pixel
+ sal_uInt16 nOffsetTolerance; // for use with SeekOffset and related
+ bool bFirstRow; // per table, whether in first row
+ bool bTabInTabCell:1;
+ bool bInCell:1;
+ bool bInTitle:1;
+
+ DECL_LINK( HTMLImportHdl, HtmlImportInfo&, void );
+ void NewActEntry( const ScEEParseEntry* );
+ static void EntryEnd( ScEEParseEntry*, const ESelection& );
+ void ProcToken( HtmlImportInfo* );
+ void CloseEntry( const HtmlImportInfo* );
+ void NextRow( const HtmlImportInfo* );
+ void SkipLocked( ScEEParseEntry*, bool bJoin = true );
+ static bool SeekOffset( const ScHTMLColOffset*, sal_uInt16 nOffset,
+ SCCOL* pCol, sal_uInt16 nOffsetTol );
+ static void MakeCol( ScHTMLColOffset*, sal_uInt16& nOffset,
+ sal_uInt16& nWidth, sal_uInt16 nOffsetTol,
+ sal_uInt16 nWidthTol );
+ static void MakeColNoRef( ScHTMLColOffset*, sal_uInt16 nOffset,
+ sal_uInt16 nWidth, sal_uInt16 nOffsetTol,
+ sal_uInt16 nWidthTol );
+ static void ModifyOffset( ScHTMLColOffset*, sal_uInt16& nOldOffset,
+ sal_uInt16& nNewOffset, sal_uInt16 nOffsetTol );
+ void Colonize( ScEEParseEntry* );
+ sal_uInt16 GetWidth( const ScEEParseEntry* );
+ void SetWidths();
+ void Adjust();
+
+ sal_uInt16 GetWidthPixel( const HTMLOption& );
+ bool IsAtBeginningOfText( const HtmlImportInfo* );
+
+ void TableOn( HtmlImportInfo* );
+ void ColOn( HtmlImportInfo* );
+ void TableRowOn( const HtmlImportInfo* );
+ void TableRowOff( const HtmlImportInfo* );
+ void TableDataOn( HtmlImportInfo* );
+ void TableDataOff( const HtmlImportInfo* );
+ void TableOff( const HtmlImportInfo* );
+ void Image( HtmlImportInfo* );
+ void AnchorOn( HtmlImportInfo* );
+ void FontOn( HtmlImportInfo* );
+
+public:
+ ScHTMLLayoutParser( EditEngine*, const OUString& rBaseURL, const Size& aPageSize, ScDocument* );
+ virtual ~ScHTMLLayoutParser() override;
+ virtual ErrCode Read( SvStream&, const OUString& rBaseURL ) override;
+ virtual const ScHTMLTable* GetGlobalTable() const override;
+};
+
+// HTML DATA QUERY PARSER
+
+/** Declares the orientation in or for a table: column or row. */
+enum ScHTMLOrient { tdCol = 0 , tdRow = 1 };
+
+/** Type for a unique identifier for each table. */
+typedef sal_uInt16 ScHTMLTableId;
+/** Identifier of the "global table" (the entire HTML document). */
+const ScHTMLTableId SC_HTML_GLOBAL_TABLE = 0;
+/** Used as table index for normal (non-table) entries in ScHTMLEntry structs. */
+const ScHTMLTableId SC_HTML_NO_TABLE = 0;
+
+/** A 2D cell position in an HTML table. */
+struct ScHTMLPos
+{
+ SCCOL mnCol;
+ SCROW mnRow;
+
+ explicit ScHTMLPos() : mnCol( 0 ), mnRow( 0 ) {}
+ explicit ScHTMLPos( SCCOL nCol, SCROW nRow ) :
+ mnCol( nCol ), mnRow( nRow ) {}
+ explicit ScHTMLPos( const ScAddress& rAddr ) { Set( rAddr ); }
+
+ SCCOLROW Get( ScHTMLOrient eOrient ) const
+ { return (eOrient == tdCol) ? mnCol : mnRow; }
+ void Set( SCCOL nCol, SCROW nRow )
+ { mnCol = nCol; mnRow = nRow; }
+ void Set( const ScAddress& rAddr )
+ { Set( rAddr.Col(), rAddr.Row() ); }
+ ScAddress MakeAddr() const
+ { return ScAddress( mnCol, mnRow, 0 ); }
+};
+
+inline bool operator<( const ScHTMLPos& rPos1, const ScHTMLPos& rPos2 )
+{
+ return (rPos1.mnRow < rPos2.mnRow) || ((rPos1.mnRow == rPos2.mnRow) && (rPos1.mnCol < rPos2.mnCol));
+}
+
+/** A 2D cell size in an HTML table. */
+struct ScHTMLSize
+{
+ SCCOL mnCols;
+ SCROW mnRows;
+
+ explicit ScHTMLSize( SCCOL nCols, SCROW nRows ) :
+ mnCols( nCols ), mnRows( nRows ) {}
+ void Set( SCCOL nCols, SCROW nRows )
+ { mnCols = nCols; mnRows = nRows; }
+};
+
+/** A single entry containing a line of text or representing a table. */
+struct ScHTMLEntry : public ScEEParseEntry
+{
+public:
+ explicit ScHTMLEntry(
+ const SfxItemSet& rItemSet,
+ ScHTMLTableId nTableId = SC_HTML_NO_TABLE );
+
+ /** Returns true, if the selection of the entry is empty. */
+ bool IsEmpty() const { return !aSel.HasRange(); }
+ /** Returns true, if the entry has any content to be imported. */
+ bool HasContents() const;
+ /** Returns true, if the entry represents a table. */
+ bool IsTable() const { return nTab != SC_HTML_NO_TABLE; }
+ /** Returns true, if the entry represents a table. */
+ ScHTMLTableId GetTableId() const { return nTab; }
+
+ /** Sets or clears the import always state. */
+ void SetImportAlways() { mbImportAlways = true; }
+ /** Sets start point of the entry selection to the start of the import info object. */
+ void AdjustStart( const HtmlImportInfo& rInfo );
+ /** Sets end point of the entry selection to the end of the import info object. */
+ void AdjustEnd( const HtmlImportInfo& rInfo );
+ /** Deletes leading and trailing empty paragraphs from the entry. */
+ void Strip( const EditEngine& rEditEngine );
+
+ /** Returns read/write access to the item set of this entry. */
+ SfxItemSet& GetItemSet() { return aItemSet; }
+ /** Returns read-only access to the item set of this entry. */
+ const SfxItemSet& GetItemSet() const { return aItemSet; }
+
+private:
+ bool mbImportAlways; /// true = Always import this entry.
+};
+
+/** This struct handles creation of unique table identifiers. */
+struct ScHTMLTableAutoId
+{
+ const ScHTMLTableId mnTableId; /// The created unique table identifier.
+ ScHTMLTableId& mrnUnusedId; /// Reference to global unused identifier variable.
+
+ /** The constructor assigns an unused identifier to member mnTableId. */
+ explicit ScHTMLTableAutoId( ScHTMLTableId& rnUnusedId );
+};
+
+class ScHTMLTableMap;
+
+/** Stores data for one table in an HTML document.
+
+ This class does the main work for importing an HTML document. It manages
+ the correct insertion of parse entries into the correct cells and the
+ creation of nested tables. Recalculation of resulting document size and
+ position is done recursively in all nested tables.
+ */
+class ScHTMLTable
+{
+public:
+ /** Creates a new HTML table without content.
+ @descr Internally handles a current cell position. This position is
+ invalid until first calls of RowOn() and DataOn().
+ @param rParentTable Reference to the parent table that owns this table.
+ @param bPreFormText true = Table is based on preformatted text (<pre> tag). */
+ explicit ScHTMLTable(
+ ScHTMLTable& rParentTable,
+ const HtmlImportInfo& rInfo,
+ bool bPreFormText,
+ const ScDocument& rDoc );
+
+ virtual ~ScHTMLTable();
+
+ /** Returns the name of the table, specified in the TABLE tag. */
+ const OUString& GetTableName() const { return maTableName; }
+ /** Returns the caption of the table, specified in the <caption> tag. */
+ const OUString& GetTableCaption() const { return maCaption; }
+ /** Returns the unique identifier of the table. */
+ ScHTMLTableId GetTableId() const { return maTableId.mnTableId; }
+ /** Returns the cell spanning of the specified cell. */
+ ScHTMLSize GetSpan( const ScHTMLPos& rCellPos ) const;
+
+ /** Searches in all nested tables for the specified table.
+ @param nTableId Unique identifier of the table. */
+ ScHTMLTable* FindNestedTable( ScHTMLTableId nTableId ) const;
+
+ /** Puts the item into the item set of the current entry. */
+ void PutItem( const SfxPoolItem& rItem );
+ /** Inserts a text portion into current entry. */
+ void PutText( const HtmlImportInfo& rInfo );
+ /** Inserts a new line, if in preformatted text, else does nothing. */
+ void InsertPara( const HtmlImportInfo& rInfo );
+
+ /** Inserts a line break (<br> tag).
+ @descr Inserts the current entry regardless if it is empty. */
+ void BreakOn();
+ /** Inserts a heading line (<p> and <h*> tags). */
+ void HeadingOn();
+ /** Processes a hyperlink (<a> tag). */
+ void AnchorOn();
+
+ /** Starts a *new* table nested in this table (<table> tag).
+ @return Pointer to the new table. */
+ ScHTMLTable* TableOn( const HtmlImportInfo& rInfo );
+ /** Closes *this* table (</table> tag).
+ @return Pointer to the parent table. */
+ ScHTMLTable* TableOff( const HtmlImportInfo& rInfo );
+ /** Processes the caption of the table (<caption> tag). */
+ void CaptionOn();
+ /** Processes the caption of the table (</caption> tag). */
+ void CaptionOff();
+ /** Starts a *new* table based on preformatted text (<pre> tag).
+ @return Pointer to the new table. */
+ ScHTMLTable* PreOn( const HtmlImportInfo& rInfo );
+ /** Closes *this* table based on preformatted text (</pre> tag).
+ @return Pointer to the parent table. */
+ ScHTMLTable* PreOff( const HtmlImportInfo& rInfo );
+
+ /** Starts next row (<tr> tag).
+ @descr Cell address is invalid until first call of DataOn(). */
+ void RowOn( const HtmlImportInfo& rInfo );
+ /** Closes the current row (<tr> tag).
+ @descr Cell address is invalid until call of RowOn() and DataOn(). */
+ void RowOff( const HtmlImportInfo& rInfo );
+ /** Starts the next cell (<td> or <th> tag). */
+ void DataOn( const HtmlImportInfo& rInfo );
+ /** Closes the current cell (</td> or </th> tag).
+ @descr Cell address is invalid until next call of DataOn(). */
+ void DataOff( const HtmlImportInfo& rInfo );
+
+ /** Starts the body of the HTML document (<body> tag). */
+ void BodyOn( const HtmlImportInfo& rInfo );
+ /** Closes the body of the HTML document (</body> tag). */
+ void BodyOff( const HtmlImportInfo& rInfo );
+
+ /** Closes *this* table (</table> tag) or preformatted text (</pre> tag).
+ @descr Used to close this table object regardless on opening tag type.
+ @return Pointer to the parent table, or this, if no parent found. */
+ ScHTMLTable* CloseTable( const HtmlImportInfo& rInfo );
+
+ /** Returns the resulting document row/column count of the specified HTML row/column. */
+ SCCOLROW GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const;
+ /** Returns the resulting document row/column count in the half-open range [nCellBegin, nCellEnd). */
+ SCCOLROW GetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellBegin, SCCOLROW nCellEnd ) const;
+ /** Returns the total document row/column count in the specified direction. */
+ SCCOLROW GetDocSize( ScHTMLOrient eOrient ) const;
+ /** Returns the total document row/column count of the specified HTML cell. */
+ ScHTMLSize GetDocSize( const ScHTMLPos& rCellPos ) const;
+
+ /** Returns the resulting Calc position of the top left edge of the table. */
+ const ScHTMLPos& GetDocPos() const { return maDocBasePos; }
+ /** Calculates the resulting Calc position of the specified HTML column/row. */
+ SCCOLROW GetDocPos( ScHTMLOrient eOrient, SCCOLROW nCellPos ) const;
+ /** Calculates the resulting Calc position of the specified HTML cell. */
+ ScHTMLPos GetDocPos( const ScHTMLPos& rCellPos ) const;
+
+ /** Calculates the current Calc document area of this table. */
+ void GetDocRange( ScRange& rRange ) const;
+
+ /** Applies border formatting to the passed document. */
+ void ApplyCellBorders( ScDocument* pDoc, const ScAddress& rFirstPos ) const;
+
+ SvNumberFormatter* GetFormatTable();
+
+protected:
+ /** Creates a new HTML table without parent.
+ @descr This constructor is used to create the "global table". */
+ explicit ScHTMLTable(
+ SfxItemPool& rPool,
+ EditEngine& rEditEngine,
+ std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseList,
+ ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser,
+ const ScDocument& rDoc );
+
+ /** Fills all empty cells in this and nested tables with dummy parse entries. */
+ void FillEmptyCells();
+ /** Recalculates the size of all columns/rows in the table, regarding nested tables. */
+ void RecalcDocSize();
+ /** Recalculates the position of all cell entries and nested tables.
+ @param rBasePos The origin of the table in the Calc document. */
+ void RecalcDocPos( const ScHTMLPos& rBasePos );
+
+private:
+ typedef ::std::unique_ptr< ScHTMLTableMap > ScHTMLTableMapPtr;
+ typedef ::std::vector< SCCOLROW > ScSizeVec;
+ typedef ::std::vector< ScHTMLEntry* > ScHTMLEntryVector;
+ typedef ::std::unique_ptr< ScHTMLEntry > ScHTMLEntryPtr;
+
+ /** Returns true, if the current cell does not contain an entry yet. */
+ bool IsEmptyCell() const;
+ /** Returns the item set from cell, row, or table, depending on current state. */
+ const SfxItemSet& GetCurrItemSet() const;
+
+ /** Returns true, if import info represents a space character. */
+ static bool IsSpaceCharInfo( const HtmlImportInfo& rInfo );
+
+ /** Creates and returns a new empty flying entry at position (0,0). */
+ ScHTMLEntryPtr CreateEntry() const;
+ /** Creates a new flying entry.
+ @param rInfo Contains the initial edit engine selection for the entry. */
+ void CreateNewEntry( const HtmlImportInfo& rInfo );
+
+ /** Inserts an empty line in front of the next entry. */
+ void InsertLeadingEmptyLine();
+
+ /** Pushes the passed entry into the list of the current cell. */
+ void ImplPushEntryToVector( ScHTMLEntryVector& rEntryVector, ScHTMLEntryPtr& rxEntry );
+ /** Tries to insert the entry into the current cell.
+ @descr If insertion is not possible (i.e., currently no cell open), the
+ entry will be inserted into the parent table.
+ @return true = Entry has been pushed into the current cell; false = Entry dropped. */
+ bool PushEntry( ScHTMLEntryPtr& rxEntry );
+ /** Puts the current entry into the entry list, if it is not empty.
+ @param rInfo The import info struct containing the end position of the current entry.
+ @param bLastInCell true = If cell is still empty, put this entry always.
+ @return true = Entry as been pushed into the current cell; false = Entry dropped. */
+ bool PushEntry( const HtmlImportInfo& rInfo, bool bLastInCell = false );
+ /** Pushes a new entry into current cell which references a nested table.*/
+ void PushTableEntry( ScHTMLTableId nTableId );
+
+ /** Tries to find a table from the table container.
+ @descr Assumes that the table is located in the current container or
+ that the passed table identifier is 0.
+ @param nTableId Unique identifier of the table or 0. */
+ ScHTMLTable* GetExistingTable( ScHTMLTableId nTableId ) const;
+ /** Inserts a nested table in the current cell at the specified position.
+ @param bPreFormText true = New table is based on preformatted text (<pre> tag). */
+ ScHTMLTable* InsertNestedTable( const HtmlImportInfo& rInfo, bool bPreFormText );
+
+ /** Inserts a new cell in an unused position, starting from current cell position. */
+ void InsertNewCell( const ScHTMLSize& rSpanSize );
+
+ /** Set internal states for a new table row. */
+ void ImplRowOn();
+ /** Set internal states for leaving a table row. */
+ void ImplRowOff();
+ /** Set internal states for entering a new table cell. */
+ void ImplDataOn( const ScHTMLSize& rSpanSize );
+ /** Set internal states for leaving a table cell. */
+ void ImplDataOff();
+
+ /** Inserts additional formatting options from import info into the item set. */
+ static void ProcessFormatOptions( SfxItemSet& rItemSet, const HtmlImportInfo& rInfo );
+
+ /** Updates the document column/row size of the specified column or row.
+ @descr Only increases the present count, never decreases. */
+ void SetDocSize( ScHTMLOrient eOrient, SCCOLROW nCellPos, SCCOLROW nSize );
+ /** Calculates and sets the resulting size the cell needs in the document.
+ @descr Reduces the needed size in merged cells.
+ @param nCellPos The first column/row position of the (merged) cell.
+ @param nCellSpan The cell spanning in the specified orientation.
+ @param nRealDocSize The raw document size of all entries of the cell. */
+ void CalcNeededDocSize(
+ ScHTMLOrient eOrient, SCCOLROW nCellPos,
+ SCCOLROW nCellSpan, SCCOLROW nRealDocSize );
+
+private:
+ ScHTMLTable* mpParentTable; /// Pointer to parent table.
+ ScHTMLTableMapPtr mxNestedTables; /// Table of nested HTML tables.
+ OUString maTableName; /// Table name from <table id> option.
+ OUString maCaption; /// Caption name of the table from <caption> </caption>
+ OUStringBuffer maCaptionBuffer; /// Caption buffer of the table from <caption> </caption>
+ ScHTMLTableAutoId maTableId; /// Unique identifier of this table.
+ SfxItemSet maTableItemSet; /// Items for the entire table.
+ std::optional<SfxItemSet> moRowItemSet; /// Items for the current table row.
+ std::optional<SfxItemSet> moDataItemSet; /// Items for the current cell.
+ ScRangeList maHMergedCells; /// List of all horizontally merged cells.
+ ScRangeList maVMergedCells; /// List of all vertically merged cells.
+ ScRangeList maUsedCells; /// List of all used cells.
+ EditEngine& mrEditEngine; /// Edit engine (from ScEEParser).
+ std::vector<std::shared_ptr<ScEEParseEntry>>& mrEEParseList; /// List that owns the parse entries (from ScEEParser).
+ std::map< ScHTMLPos, ScHTMLEntryVector > maEntryMap; /// List of entries for each cell.
+ ScHTMLEntryVector* mpCurrEntryVector; /// Current entry vector from map for faster access.
+ ScHTMLEntryPtr mxCurrEntry; /// Working entry, not yet inserted in a list.
+ ScSizeVec maCumSizes[ 2 ]; /// Cumulated cell counts for each HTML table column/row.
+ ScHTMLSize maSize; /// Size of the table.
+ ScHTMLPos maCurrCell; /// Address of current cell to fill.
+ ScHTMLPos maDocBasePos; /// Resulting base address in a Calc document.
+ ScHTMLParser* mpParser;
+ const ScDocument& mrDoc;
+ bool mbBorderOn:1; /// true = Table borders on.
+ bool mbPreFormText:1; /// true = Table from preformatted text (<pre> tag).
+ bool mbRowOn:1; /// true = Inside of <tr> </tr>.
+ bool mbDataOn:1; /// true = Inside of <td> </td> or <th> </th>.
+ bool mbPushEmptyLine:1; /// true = Insert empty line before current entry.
+ bool mbCaptionOn:1; /// true = Inside of <caption> </caption>
+};
+
+/** The "global table" representing the entire HTML document. */
+class ScHTMLGlobalTable : public ScHTMLTable
+{
+public:
+ explicit ScHTMLGlobalTable(
+ SfxItemPool& rPool,
+ EditEngine& rEditEngine,
+ std::vector<std::shared_ptr<ScEEParseEntry>>& rEEParseList,
+ ScHTMLTableId& rnUnusedId, ScHTMLParser* pParser,
+ const ScDocument& rDoc );
+
+ virtual ~ScHTMLGlobalTable() override;
+
+ /** Recalculates sizes and resulting positions of all document entries. */
+ void Recalc();
+};
+
+/** The HTML parser for data queries. Focuses on data import, not on layout.
+
+ Builds the table structure correctly, ignores extended formatting like
+ pictures or column widths.
+ */
+class ScHTMLQueryParser : public ScHTMLParser
+{
+public:
+ explicit ScHTMLQueryParser( EditEngine* pEditEngine, ScDocument* pDoc );
+ virtual ~ScHTMLQueryParser() override;
+
+ virtual ErrCode Read( SvStream& rStrm, const OUString& rBaseURL ) override;
+
+ /** Returns the "global table" which contains the entire HTML document. */
+ virtual const ScHTMLTable* GetGlobalTable() const override;
+
+private:
+ /** Handles all possible tags in the HTML document. */
+ void ProcessToken( const HtmlImportInfo& rInfo );
+ /** Inserts a text portion into current entry. */
+ void InsertText( const HtmlImportInfo& rInfo );
+ /** Processes the <font> tag. */
+ void FontOn( const HtmlImportInfo& rInfo );
+
+ /** Processes the <meta> tag. */
+ void MetaOn( const HtmlImportInfo& rInfo );
+ /** Opens the title of the HTML document (<title> tag). */
+ void TitleOn();
+ /** Closes the title of the HTML document (</title> tag). */
+ void TitleOff( const HtmlImportInfo& rInfo );
+
+ /** Opens a new table at the current position. */
+ void TableOn( const HtmlImportInfo& rInfo );
+ /** Closes the current table. */
+ void TableOff( const HtmlImportInfo& rInfo );
+ /** Opens a new table based on preformatted text. */
+ void PreOn( const HtmlImportInfo& rInfo );
+ /** Closes the current preformatted text table. */
+ void PreOff( const HtmlImportInfo& rInfo );
+
+ /** Closes the current table, regardless on opening tag. */
+ void CloseTable( const HtmlImportInfo& rInfo );
+
+ void ParseStyle(std::u16string_view rStrm);
+
+ DECL_LINK( HTMLImportHdl, HtmlImportInfo&, void );
+
+private:
+ typedef ::std::unique_ptr< ScHTMLGlobalTable > ScHTMLGlobalTablePtr;
+
+ OUStringBuffer maTitle; /// The title of the document.
+ ScHTMLGlobalTablePtr mxGlobTable; /// Contains the entire imported document.
+ ScHTMLTable* mpCurrTable; /// Pointer to current table (performance).
+ ScHTMLTableId mnUnusedId; /// First unused table identifier.
+ bool mbTitleOn; /// true = Inside of <title> </title>.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/imp_op.hxx b/sc/source/filter/inc/imp_op.hxx
new file mode 100644
index 000000000..1b791bb72
--- /dev/null
+++ b/sc/source/filter/inc/imp_op.hxx
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include "xiroot.hxx"
+#include "xistream.hxx"
+#include "ftools.hxx"
+
+#include <vector>
+#include <memory>
+#include <unordered_map>
+
+class SvStream;
+
+class ScFormulaCell;
+class ScDocument;
+class ScTokenArray;
+
+class ExcelToSc;
+class XclImpOutlineBuffer;
+class XclImpColRowSettings;
+struct XclAddress;
+
+class ImportTyp
+{
+protected:
+ rtl_TextEncoding eQuellChar; // source (Quelle) character set
+ ScDocument& rD; // document
+
+public:
+ ImportTyp( ScDocument&, rtl_TextEncoding eSrc );
+ virtual ~ImportTyp();
+};
+
+class XclImpOutlineDataBuffer : protected XclImpRoot
+{
+public:
+ explicit XclImpOutlineDataBuffer( const XclImpRoot& rRoot, SCTAB nScTab );
+ virtual ~XclImpOutlineDataBuffer() override;
+
+ XclImpColRowSettings* GetColRowBuff() const { return mxColRowBuff.get(); }
+ XclImpOutlineBuffer* GetColOutline() const { return mxColOutlineBuff.get(); }
+ XclImpOutlineBuffer* GetRowOutline() const { return mxRowOutlineBuff.get(); }
+ void Convert();
+
+private:
+ typedef std::shared_ptr< XclImpOutlineBuffer > XclImpOutlineBfrRef;
+ typedef std::shared_ptr< XclImpColRowSettings > XclImpColRowSettRef;
+
+ XclImpOutlineBfrRef mxColOutlineBuff;
+ XclImpOutlineBfrRef mxRowOutlineBuff;
+ XclImpColRowSettRef mxColRowBuff;
+ SCTAB mnScTab;
+};
+
+class ImportExcel : public ImportTyp, protected XclImpRoot
+{
+protected:
+ struct LastFormula
+ {
+ sal_uInt16 mnXF;
+ SCCOL mnCol;
+ SCROW mnRow;
+ double mfValue;
+ ScFormulaCell* mpCell;
+ };
+ typedef std::unordered_map<SCCOL, LastFormula> LastFormulaMapType;
+
+ RootData* pExcRoot;
+
+ XclImpStream maStrm; // input stream
+ XclImpStream& aIn; // input stream
+
+ ScfUInt32Vec maSheetOffsets;
+ ScRange maScOleSize; /// Visible range if embedded.
+
+ std::unique_ptr<ExcelToSc> pFormConv; // formula-converter
+
+ XclImpOutlineBuffer* pColOutlineBuff;
+ XclImpOutlineBuffer* pRowOutlineBuff;
+ XclImpColRowSettings* pColRowBuff; // Col/Row settings 1 table
+
+ typedef std::vector< std::unique_ptr<XclImpOutlineDataBuffer> > XclImpOutlineListBuffer;
+ std::unique_ptr<XclImpOutlineListBuffer> pOutlineListBuffer;
+
+ LastFormulaMapType maLastFormulaCells; // Keep track of last formula cells in each column.
+ LastFormula* mpLastFormula;
+
+ sal_Int16 mnLastRefIdx;
+ sal_uInt16 mnIxfeIndex; /// Current XF identifier from IXFE record.
+ sal_uInt16 mnLastRecId;
+
+ SCTAB nBdshtTab; // Counter for Boundsheet
+
+ bool bTabTruncated; // if extended range leads to
+ // truncation of cells
+
+ bool mbBiff2HasXfs:1; /// Select XF formatting or direct formatting in BIFF2.
+ bool mbBiff2HasXfsValid:1; /// False = mbBiff2HasXfs is undetermined yet.
+
+ void SetLastFormula( SCCOL nCol, SCROW nRow, double fVal, sal_uInt16 nXF, ScFormulaCell* pCell );
+
+ // Record functions
+ void ReadFileSharing();
+
+ sal_uInt16 ReadXFIndex( const ScAddress& rScPos, bool bBiff2 );
+
+ void ReadDimensions();
+ void ReadBlank();
+ void ReadInteger();
+ void ReadNumber();
+ void ReadLabel();
+ void ReadBoolErr();
+ void ReadRk();
+
+ void Window1();
+ void Formula25(); // 0x06 -> excform.cxx
+ void Row25(); // 0x08
+ void Bof2(); // 0x09
+ void Eof(); // 0x0A
+ void DocProtect(); // 0x12
+ void SheetProtect(); // 0x12 Sheet Protection
+ void DocPassword(); // 0x13 document password
+ void SheetPassword(); // 0x13 sheet password
+ void Externsheet(); // 0x17
+ void WinProtection(); // 0x19
+ void Columndefault(); // 0x20
+ void Array25(); // 0x21
+ void Rec1904(); // 0x22
+ void Externname25(); // 0x23
+ void Colwidth(); // 0x24
+ void Defrowheight2(); // 0x25
+// void Window1(); // 0x3D
+ void Codepage(); // 0x42
+ void Ixfe(); // 0x44
+ void DefColWidth(); // 0x55
+ void Colinfo(); // 0x7D
+ void Wsbool(); // 0x81
+ void Boundsheet(); // 0x85
+ void Country(); // 0x8C
+ void Hideobj(); // 0x8D
+ void Standardwidth(); // 0x99
+ void Shrfmla(); // 0xBC
+ void Mulrk(); // 0xBD
+ void Mulblank(); // 0xBE
+ void Rstring(); // 0xD6
+ void Cellmerging(); // 0xE5
+ void Olesize(); // 0xDE
+ void ReadUsesElfs(); // 0x0160
+ void Formula3(); // 0x0206 -> excform.cxx
+ // 0x0207 -> 0x07
+ void Row34(); // 0x0208
+ void Bof3(); // 0x0209
+ void Array34(); // 0x0221
+ void Defrowheight345(); // 0x0225
+ void TableOp(); // 0x0236
+ //void Rk(); // 0x027E -> 0x7E
+ void Formula4(); // 0x0406 -> excform.cxx
+ void Bof4(); // 0x0409
+ void Bof5(); // 0x0809
+
+ void Formula(
+ const XclAddress& rXclPos, sal_uInt16 nXF, sal_uInt16 nFormLen, double fCurVal, bool bShrFmla);
+ // -> excform.cxx
+
+ virtual void EndSheet();
+ void NewTable();
+ std::unique_ptr<ScTokenArray> ErrorToFormula( bool bErrOrVal, sal_uInt8 nError,
+ double& rVal );
+
+ void AdjustRowHeight();
+ virtual void PostDocLoad();
+
+public:
+ ImportExcel( XclImpRootData& rImpData, SvStream& rStrm );
+
+ virtual ~ImportExcel() override;
+
+ virtual ErrCode Read();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotattr.hxx b/sc/source/filter/inc/lotattr.hxx
new file mode 100644
index 000000000..475d46547
--- /dev/null
+++ b/sc/source/filter/inc/lotattr.hxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+
+#include <address.hxx>
+
+class ScDocumentPool;
+class ScPatternAttr;
+class SvxColorItem;
+class Color;
+struct LotusContext;
+
+namespace editeng { class SvxBorderLine; }
+
+struct LotAttrWK3
+{
+ sal_uInt8 nFont;
+ sal_uInt8 nLineStyle;
+ sal_uInt8 nFontCol;
+ sal_uInt8 nBack;
+
+ bool HasStyles () const
+ {
+ return ( nFont || nLineStyle || nFontCol || ( nBack & 0x7F ) );
+ // !! without center bit!!
+ }
+
+ bool IsCentered () const
+ {
+ return ( nBack & 0x80 );
+ }
+};
+
+class LotAttrCache
+{
+public:
+
+ LotAttrCache(LotusContext& rContext);
+
+ ~LotAttrCache();
+
+ const ScPatternAttr& GetPattAttr( const LotAttrWK3& );
+
+private:
+
+ friend class LotAttrTable;
+
+ struct ENTRY
+ {
+ std::unique_ptr<ScPatternAttr> pPattAttr;
+ sal_uInt32 nHash0;
+
+ ENTRY(std::unique_ptr<ScPatternAttr> p);
+
+ ~ENTRY();
+ };
+
+ static void MakeHash( const LotAttrWK3& rAttr, sal_uInt32& rOut )
+ {
+ reinterpret_cast<sal_uInt8*>(&rOut)[ 0 ] = rAttr.nFont & 0x7F;
+ reinterpret_cast<sal_uInt8*>(&rOut)[ 1 ] = rAttr.nLineStyle;
+ reinterpret_cast<sal_uInt8*>(&rOut)[ 2 ] = rAttr.nFontCol;
+ reinterpret_cast<sal_uInt8*>(&rOut)[ 3 ] = rAttr.nBack;
+ }
+
+ static void LotusToScBorderLine( sal_uInt8 nLine, ::editeng::SvxBorderLine& );
+
+ const SvxColorItem& GetColorItem( const sal_uInt8 nLotIndex ) const;
+
+ const Color& GetColor( const sal_uInt8 nLotIndex ) const;
+
+ ScDocumentPool* pDocPool;
+ std::unique_ptr<SvxColorItem> ppColorItems[6]; // 0 and 7 are missing!
+ std::unique_ptr<SvxColorItem> pWhite;
+ std::unique_ptr<Color[]> pColTab;
+ std::vector< std::unique_ptr<ENTRY> > aEntries;
+
+ LotusContext& mrContext;
+};
+
+class LotAttrCol
+{
+public:
+ void SetAttr (const ScDocument* pDoc, const SCROW nRow, const ScPatternAttr&);
+
+ void Apply(LotusContext& rContext, const SCCOL nCol, const SCTAB nTab);
+private:
+
+ struct ENTRY
+ {
+ const ScPatternAttr* pPattAttr;
+ SCROW nFirstRow;
+ SCROW nLastRow;
+ };
+
+ std::vector< std::unique_ptr<ENTRY> > aEntries;
+};
+
+class LotAttrTable
+{
+public:
+ LotAttrTable(LotusContext& rContext);
+
+ void SetAttr(const LotusContext& rContext, const SCCOL nColFirst, const SCCOL nColLast, const SCROW nRow, const LotAttrWK3& );
+
+ void Apply(LotusContext& rContext, const SCTAB nTabNum);
+
+private:
+
+ LotAttrCol pCols[ MAXCOLCOUNT ];
+ LotAttrCache aAttrCache;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotfntbf.hxx b/sc/source/filter/inc/lotfntbf.hxx
new file mode 100644
index 000000000..99d1c9a9d
--- /dev/null
+++ b/sc/source/filter/inc/lotfntbf.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <optional>
+
+// Code in fontbuff.cxx (excel)
+
+class LotusFontBuffer
+{
+private:
+ struct ENTRY
+ {
+ std::optional<OUString> xTmpName;
+ std::unique_ptr<SvxFontItem> pFont;
+ std::unique_ptr<SvxFontHeightItem> pHeight;
+ sal_Int32 nType = -1; // < 0 -> undefined
+ void TmpName( const OUString &rNew )
+ {
+ xTmpName = rNew;
+ }
+ void Height( std::unique_ptr<SvxFontHeightItem> pNew )
+ {
+ pHeight = std::move(pNew);
+ }
+ void Type( const sal_uInt16 nNew ) { nType = nNew; }
+ };
+
+ static void MakeFont( ENTRY* pEntry );
+public:
+ const static sal_uInt16 nSize = 8;
+ void Fill( const sal_uInt8 nIndex, SfxItemSet& rItemSet );
+ void SetName( const sal_uInt16 nIndex, const OUString& rName );
+ void SetHeight( const sal_uInt16 nIndex, const sal_uInt16 nHeight );
+ void SetType( const sal_uInt16 nIndex, const sal_uInt16 nType );
+private:
+ ENTRY pData[ nSize ];
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotform.hxx b/sc/source/filter/inc/lotform.hxx
new file mode 100644
index 000000000..29751a2f5
--- /dev/null
+++ b/sc/source/filter/inc/lotform.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "formel.hxx"
+
+enum FUNC_TYPE
+{
+ FT_Return = 0, // End Formula
+ FT_FuncFix0, // Function, 0 Parameter
+ FT_FuncFix1, // Function, 0 Parameter
+ FT_FuncFix2, // Function, 0 Parameter
+ FT_FuncFix3, // Function, 0 Parameter
+ FT_FuncFix4, // Function, 0 Parameter
+ FT_FuncVar, // ~, var. P.
+ FT_Neg, // Negation
+ FT_Op, // Operator
+ FT_NotImpl, // not implemented
+ FT_ConstFloat, // Double (8-Byte)
+ FT_Variable, // Single Ref
+ FT_Range, // Double Ref
+ FT_Braces, // Braces
+ FT_ConstInt, // Integer
+ FT_ConstString, // String
+ FT_NOP, // nothing
+ // additionally since WK3
+ FT_Cref, // Cell Reference
+ FT_Rref, // Range Reference
+ FT_Nrref, // Named range reference
+ FT_Absnref, // Absolute named range
+ FT_Erref, // Err range reference
+ FT_Ecref, // Err cell reference
+ FT_Econstant, // Err constant
+ FT_Splfunc, // SPLfunction
+ FT_Const10Float,// Float (10-Byte)
+ FT_Snum // Const Short Num
+ // for 'Problem Cases' during Import
+};
+
+struct LotusContext;
+
+class LotusToSc : public LotusConverterBase
+{
+private:
+ LotusContext& m_rContext;
+ rtl_TextEncoding eSrcChar;
+ TokenId nAddToken; // ')+1.0'
+ TokenId nSubToken; // ~
+ TokenId n0Token; // '0.0';
+
+ static FUNC_TYPE IndexToType( sal_uInt8 );
+ static DefTokenId IndexToToken( sal_uInt8 );
+ static FUNC_TYPE IndexToTypeWK123( sal_uInt8 );
+ static DefTokenId IndexToTokenWK123( sal_uInt8 );
+ void DoFunc( DefTokenId eOc, sal_uInt8 nCnt, const char* pExtName );
+ void LotusRelToScRel(sal_uInt16 nCol, sal_uInt16 nRow, ScSingleRefData& rSRD);
+ bool bWK3; // alternative Code translation for < WK1
+ bool bWK123; // alternative for 123
+
+ void ReadSRD( const ScDocument& rDoc, ScSingleRefData& rSRD, sal_uInt8 nFlags );
+ inline void ReadCRD( const ScDocument& rDoc, ScComplexRefData& rCRD, sal_uInt8 nFlags );
+ void IncToken( TokenId &rParam );
+ // Attention: here the Token-chain is extended in Pool
+ // with '(<rParam>)+1' and finished with Store() !
+
+ void DecToken( TokenId& rParam );
+ // Attention: ~
+ void NegToken( TokenId& rParam );
+ // Attention: like ~, but with '-(<rParam>)'
+public:
+ LotusToSc(LotusContext &rContext, SvStream& aStr, svl::SharedStringPool& rSPool, rtl_TextEncoding eSrc, bool b);
+
+ virtual void Convert( std::unique_ptr<ScTokenArray>& rpErg, sal_Int32& nRest ) override;
+
+ void Reset( const ScAddress& rEingPos );
+ inline void SetWK3();
+ LotusContext& getContext() { return m_rContext; }
+
+private:
+ using LotusConverterBase::Reset;
+};
+
+inline void LotusToSc::ReadCRD( const ScDocument& rDoc, ScComplexRefData& rCRD, sal_uInt8 nRelBit )
+{
+ // 1st part
+ ReadSRD( rDoc, rCRD.Ref1, nRelBit );
+
+ // 2nd part
+ ReadSRD( rDoc, rCRD.Ref2, nRelBit >> 3 );
+}
+
+inline void LotusToSc::SetWK3()
+{
+ bWK3 = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotimpop.hxx b/sc/source/filter/inc/lotimpop.hxx
new file mode 100644
index 000000000..916cbda11
--- /dev/null
+++ b/sc/source/filter/inc/lotimpop.hxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+#include "imp_op.hxx"
+#include "ftools.hxx"
+#include "lotform.hxx"
+#include "lotattr.hxx"
+
+struct LotusContext;
+
+class ImportLotus : public ImportTyp
+{
+private:
+ SvStream* pIn; // needed due to multiple Read()!
+ LotusToSc aConv;
+ sal_uInt16 nTab; // currently handled table
+ sal_Int32 nExtTab;
+
+ // in WK?-Datei
+ void Bof(); // 0x0000 00
+ bool BofFm3(); // 0x0000 00
+ void Columnwidth( sal_uInt16 nRecLen ); // 0x0007 07
+ void Hiddencolumn( sal_uInt16 nRecLen ); // 0x0008 08
+ void Userrange(); // 0x0009 09
+ void Errcell(); // 0x0014 20
+ void Nacell(); // 0x0015 21
+ void Labelcell(); // 0x0016 22
+ void Numbercell(); // 0x0017 23
+ void Smallnumcell(); // 0x0018 24
+ void Formulacell( sal_uInt16 nRecLen ); // 0x0019 25
+ // 0x001b 27 special
+ void NamedSheet(); // 14000
+ void RowPresentation( sal_uInt16 nRecLen ); // 2007
+
+ // in FM? file
+ void Font_Face(); // 174
+ void Font_Type(); // 176
+ void Font_Ysize(); // 177
+ void Row_( const sal_uInt16 nRecLen ); // 197 ?
+
+ inline void Read( ScAddress& );
+ inline void Read( ScRange& );
+ // for addresses/ranges in the format row(16)/tab(8)/col(8)
+ inline void Read( char& );
+ inline void Read( sal_uInt8& );
+ inline void Read( sal_uInt16& );
+ inline void Read( sal_Int16& );
+ inline void Read( double& ); // read 10-byte IEEE
+ inline void Read( LotAttrWK3& );
+ void Read( OUString& ); // read in 0-terminated string
+ inline void Skip( const sal_uInt16 nNumBytes );
+
+public:
+ ImportLotus(LotusContext& rContext, SvStream&, rtl_TextEncoding eSrc);
+
+ virtual ~ImportLotus() override;
+
+ ErrCode parse(); //parse + CalcAfterLoad
+ ErrCode Read();
+ ErrCode Read( SvStream& ); // special for *.fm3 files
+};
+
+inline void ImportLotus::Read( ScAddress& rAddr )
+{
+ sal_uInt16 nRow;
+ pIn->ReadUInt16( nRow );
+ rAddr.SetRow( static_cast<SCROW>(nRow) );
+ sal_uInt8 nByte;
+ pIn->ReadUChar( nByte );
+ rAddr.SetTab( static_cast<SCTAB>(nByte) );
+ pIn->ReadUChar( nByte );
+ rAddr.SetCol( static_cast<SCCOL>(nByte) );
+}
+
+inline void ImportLotus::Read( ScRange& rRange )
+{
+ Read( rRange.aStart );
+ Read( rRange.aEnd );
+}
+
+inline void ImportLotus::Read( char& r )
+{
+ pIn->ReadChar( r );
+}
+
+inline void ImportLotus::Read( sal_uInt8& r )
+{
+ pIn->ReadUChar( r );
+}
+
+inline void ImportLotus::Read( sal_uInt16& r )
+{
+ pIn->ReadUInt16( r );
+}
+
+inline void ImportLotus::Read( sal_Int16& r )
+{
+ pIn->ReadInt16( r );
+}
+
+inline void ImportLotus::Read( double& r )
+{
+ ScfTools::ReadLongDouble(*pIn, r);
+}
+
+inline void ImportLotus::Read( LotAttrWK3& r )
+{
+ pIn->ReadUChar( r.nFont ).ReadUChar( r.nFontCol ).ReadUChar( r.nBack ).ReadUChar( r.nLineStyle );
+}
+
+inline void ImportLotus::Skip( const sal_uInt16 n )
+{
+ pIn->SeekRel( n );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/lotrange.hxx b/sc/source/filter/inc/lotrange.hxx
new file mode 100644
index 000000000..bbf98ab1f
--- /dev/null
+++ b/sc/source/filter/inc/lotrange.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <refdata.hxx>
+#include <types.hxx>
+
+#include <memory>
+
+typedef sal_uInt16 LR_ID;
+
+#define ID_FAIL 0xFFFF
+
+class LotusRange
+{
+ friend class LotusRangeList;
+private:
+ sal_uInt32 nHash;
+ SCCOL nColStart;
+ SCROW nRowStart;
+ SCCOL nColEnd;
+ SCROW nRowEnd;
+ LR_ID nId;
+ void MakeHash();
+ inline void Copy( const LotusRange& );
+ inline void SetId( LR_ID nId );
+public:
+ LotusRange( SCCOL nCol, SCROW nRow );
+ LotusRange( SCCOL nColS, SCROW nRowS, SCCOL nColE, SCROW nRowE );
+ LotusRange( const LotusRange& );
+ inline LotusRange &operator =( const LotusRange& );
+ inline bool operator ==( const LotusRange& ) const;
+ inline bool IsSingle() const;
+};
+
+inline void LotusRange::Copy( const LotusRange& rCpy )
+{
+ nHash = rCpy.nHash;
+ nColStart = rCpy.nColStart;
+ nRowStart = rCpy.nRowStart;
+ nColEnd = rCpy.nColEnd;
+ nRowEnd = rCpy.nRowEnd;
+ nId = rCpy.nId;
+}
+
+inline void LotusRange::SetId( LR_ID nNewId )
+{
+ nId = nNewId;
+}
+
+inline LotusRange &LotusRange::operator =( const LotusRange& rCpy )
+{
+ Copy( rCpy );
+ return *this;
+}
+
+inline bool LotusRange::operator ==( const LotusRange& rRef ) const
+{
+ return ( nHash == rRef.nHash && nColStart == rRef.nColStart &&
+ nRowStart == rRef.nRowStart && nColEnd == rRef.nColEnd &&
+ nRowEnd == rRef.nRowEnd );
+}
+
+inline bool LotusRange::IsSingle() const
+{
+ return ( nColStart == nColEnd && nRowStart == nRowEnd );
+}
+
+class LotusRangeList
+{
+private:
+ LR_ID nIdCnt;
+ ScComplexRefData aComplRef;
+ std::vector<std::unique_ptr<LotusRange>> maRanges;
+
+public:
+ LotusRangeList();
+ ~LotusRangeList();
+ inline LR_ID GetIndex( SCCOL nCol, SCROW nRow );
+ inline LR_ID GetIndex( SCCOL nColS, SCROW nRowS, SCCOL nColE, SCROW nRowE );
+ LR_ID GetIndex( const LotusRange& );
+ void Append( const ScDocument* pDoc, std::unique_ptr<LotusRange> pLR );
+};
+
+inline LR_ID LotusRangeList::GetIndex( SCCOL nCol, SCROW nRow )
+{
+ LotusRange aRef( nCol, nRow );
+ return GetIndex( aRef );
+}
+
+inline LR_ID LotusRangeList::GetIndex( SCCOL nColS, SCROW nRowS, SCCOL nColE, SCROW nRowE )
+{
+ LotusRange aRef( nColS, nRowS, nColE, nRowE );
+ return GetIndex( aRef );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/namebuff.hxx b/sc/source/filter/inc/namebuff.hxx
new file mode 100644
index 000000000..4ad7bc102
--- /dev/null
+++ b/sc/source/filter/inc/namebuff.hxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <map>
+#include <vector>
+#include <rtl/ustring.hxx>
+#include "root.hxx"
+#include "xiroot.hxx"
+#include <refdata.hxx>
+#include <tokenarray.hxx>
+
+#include <unordered_map>
+
+class StringHashEntry
+{
+private:
+ OUString aString;
+ sal_uInt32 nHash;
+
+ static sal_uInt32 MakeHashCode( const OUString& );
+public:
+ inline StringHashEntry( const OUString& );
+ inline bool operator ==( const StringHashEntry& ) const;
+};
+
+inline StringHashEntry::StringHashEntry( const OUString& r )
+ : aString( r )
+ , nHash( MakeHashCode(r) )
+{
+}
+
+inline bool StringHashEntry::operator ==( const StringHashEntry& r ) const
+{
+ return ( nHash == r.nHash && aString == r.aString );
+}
+
+/**
+ * Store and manage shared formula tokens.
+ */
+class SharedFormulaBuffer : public ExcRoot
+{
+ typedef std::unordered_map<ScAddress, ScTokenArray, ScAddressHashFunctor> TokenArraysType;
+ TokenArraysType maTokenArrays;
+
+public:
+ SharedFormulaBuffer( RootData* pRD );
+ virtual ~SharedFormulaBuffer();
+ void Clear();
+ void Store( const ScAddress& rPos, const ScTokenArray& rArray );
+ const ScTokenArray* Find( const ScAddress& rRefPos ) const;
+};
+
+class RangeNameBufferWK3 final
+{
+private:
+ struct Entry
+ {
+ StringHashEntry aStrHashEntry;
+ ScComplexRefData aScComplexRefDataRel;
+ sal_uInt16 nAbsInd; // == 0 -> no Abs-Name yet!
+ sal_uInt16 nRelInd;
+ bool bSingleRef;
+ Entry( const OUString& rName, const ScComplexRefData& rCRD )
+ : aStrHashEntry( rName )
+ , aScComplexRefDataRel( rCRD )
+ , nAbsInd(0)
+ , nRelInd(0)
+ , bSingleRef(false)
+ {
+ }
+ };
+
+ std::unique_ptr<ScTokenArray>
+ pScTokenArray;
+ sal_uInt16 nIntCount;
+ std::vector<Entry> maEntries;
+
+public:
+ RangeNameBufferWK3(const ScDocument& rDoc);
+ ~RangeNameBufferWK3();
+ void Add( const ScDocument& rDoc, const OUString& rName, const ScComplexRefData& rCRD );
+ inline void Add( const ScDocument& rDoc, const OUString& rName, const ScRange& aScRange );
+ bool FindRel( const OUString& rRef, sal_uInt16& rIndex );
+ bool FindAbs( std::u16string_view rRef, sal_uInt16& rIndex );
+};
+
+inline void RangeNameBufferWK3::Add( const ScDocument& rDoc, const OUString& rName, const ScRange& aScRange )
+{
+ ScComplexRefData aCRD;
+ ScSingleRefData* pSRD;
+
+ pSRD = &aCRD.Ref1;
+ pSRD->InitAddress(aScRange.aStart);
+ pSRD->SetFlag3D(true);
+
+ pSRD = &aCRD.Ref2;
+ pSRD->InitAddress(aScRange.aEnd);
+ pSRD->SetFlag3D(true);
+
+ Add( rDoc, rName, aCRD );
+}
+
+class ExtSheetBuffer : public ExcRoot
+{
+private:
+ struct Cont
+ {
+ OUString aFile;
+ OUString aTab;
+ sal_uInt16 nTabNum; // 0xFFFF -> not set yet
+ // 0xFFFE -> tried to set, but failed
+ // 0xFFFD -> should be in the same workbook, but not found
+ bool bSWB;
+ Cont( const OUString& rFilePathAndName, const OUString& rTabName,
+ const bool bSameWB ) :
+ aFile( rFilePathAndName ),
+ aTab( rTabName )
+ {
+ nTabNum = 0xFFFF; // -> table not created yet
+ bSWB = bSameWB;
+ }
+ };
+
+ std::vector<Cont> maEntries;
+
+public:
+ inline ExtSheetBuffer( RootData* );
+
+ sal_Int16 Add( const OUString& rFilePathAndName,
+ const OUString& rTabName, const bool bSameWorkbook );
+
+ bool GetScTabIndex( sal_uInt16 nExcSheetIndex, sal_uInt16& rIn_LastTab_Out_ScIndex );
+
+ void Reset();
+};
+
+inline ExtSheetBuffer::ExtSheetBuffer( RootData* p ) : ExcRoot( p )
+{
+}
+
+struct ExtName
+{
+ sal_uInt32 nStorageId;
+ sal_uInt16 nFlags;
+
+ ExtName( sal_uInt16 n ) : nStorageId( 0 ), nFlags( n ) {}
+
+ bool IsOLE() const;
+};
+
+class ExtNameBuff : protected XclImpRoot
+{
+public:
+ explicit ExtNameBuff( const XclImpRoot& rRoot );
+
+ void AddDDE( sal_Int16 nRefIdx );
+ void AddOLE( sal_Int16 nRefIdx, sal_uInt32 nStorageId );
+ void AddName( sal_Int16 nRefIdx );
+
+ const ExtName* GetNameByIndex( sal_Int16 nRefIdx, sal_uInt16 nNameIdx ) const;
+
+ void Reset();
+
+private:
+ typedef ::std::vector< ExtName > ExtNameVec;
+ typedef ::std::map< sal_Int16, ExtNameVec > ExtNameMap;
+
+ ExtNameMap maExtNames;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/numberformatsbuffer.hxx b/sc/source/filter/inc/numberformatsbuffer.hxx
new file mode 100644
index 000000000..28157acc0
--- /dev/null
+++ b/sc/source/filter/inc/numberformatsbuffer.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/Locale.hpp>
+#include "workbookhelper.hxx"
+
+namespace com::sun::star {
+ namespace util { class XNumberFormats; }
+}
+
+class SfxItemSet;
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+struct NumFmtModel
+{
+ css::lang::Locale maLocale;
+ OUString maFmtCode;
+ sal_Int16 mnPredefId;
+
+ explicit NumFmtModel();
+};
+
+/** Contains all API number format attributes. */
+struct ApiNumFmtData
+{
+ sal_Int32 mnIndex; /// API number format index.
+
+ explicit ApiNumFmtData();
+};
+
+/** Contains all data for a number format code. */
+class NumberFormat : public WorkbookHelper
+{
+public:
+ explicit NumberFormat( const WorkbookHelper& rHelper );
+
+ /** Sets the passed format code. */
+ void setFormatCode( const OUString& rFmtCode );
+ /** Sets the passed format code, encoded in UTF-8. */
+ void setFormatCode(
+ const css::lang::Locale& rLocale,
+ const char* pcFmtCode );
+ /** Sets the passed predefined format code identifier. */
+ void setPredefinedId(
+ const css::lang::Locale& rLocale,
+ sal_Int16 nPredefId );
+
+ /** Final processing after import of all style settings. Returns the API format index. */
+ void finalizeImport(
+ const css::uno::Reference< css::util::XNumberFormats >& rxNumFmts,
+ const css::lang::Locale& rFromLocale );
+ sal_uInt32 fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+
+private:
+ NumFmtModel maModel;
+ ApiNumFmtData maApiData;
+};
+
+typedef std::shared_ptr< NumberFormat > NumberFormatRef;
+
+class NumberFormatsBuffer : public WorkbookHelper
+{
+public:
+ explicit NumberFormatsBuffer( const WorkbookHelper& rHelper );
+
+ /** Inserts a new number format. */
+ NumberFormatRef createNumFmt( sal_uInt32 nNumFmtId, const OUString& rFmtCode );
+
+ /** Inserts a new number format code. */
+ NumberFormatRef importNumFmt( const AttributeList& rAttribs );
+ /** Inserts a new number format code from a NUMFMT record. */
+ void importNumFmt( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ sal_uInt32 fillToItemSet( SfxItemSet& rItemSet, sal_uInt32 nNumFmtId, bool bSkipPoolDefs ) const;
+
+ sal_uInt32 nextFreeId(){ return ++mnHighestId; }
+private:
+ /** Inserts built-in number formats for the current system language. */
+ void insertBuiltinFormats();
+
+private:
+ typedef RefMap< sal_uInt32, NumberFormat > NumberFormatMap;
+
+ NumberFormatMap maNumFmts; /// List of number formats.
+ OUString maLocaleStr; /// Current office locale.
+ sal_uInt32 mnHighestId;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/ooxformulaparser.hxx b/sc/source/filter/inc/ooxformulaparser.hxx
new file mode 100644
index 000000000..25ba76dfc
--- /dev/null
+++ b/sc/source/filter/inc/ooxformulaparser.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sheet/XFilterFormulaParser.hpp>
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::lang { class XComponent; }
+
+namespace oox::xls {
+
+class OOXMLFormulaParserImpl;
+
+typedef ::cppu::WeakImplHelper<
+ css::lang::XServiceInfo,
+ css::lang::XInitialization,
+ css::sheet::XFilterFormulaParser > OOXMLFormulaParser_BASE;
+
+/** OOXML formula parser/compiler service for usage in ODF filters. */
+class OOXMLFormulaParser : public OOXMLFormulaParser_BASE
+{
+public:
+ explicit OOXMLFormulaParser();
+ virtual ~OOXMLFormulaParser() override;
+
+ // com.sun.star.lang.XServiceInfo interface -------------------------------
+
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL
+ supportsService( const OUString& rService ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // com.sun.star.lang.XInitialization interface ----------------------------
+
+ virtual void SAL_CALL initialize(
+ const css::uno::Sequence< css::uno::Any >& rArgs ) override;
+
+ // com.sun.star.sheet.XFilterFormulaParser interface ----------------------
+
+ virtual OUString SAL_CALL
+ getSupportedNamespace() override;
+
+ // com.sun.star.sheet.XFormulaParser interface ----------------------------
+
+ virtual css::uno::Sequence< css::sheet::FormulaToken > SAL_CALL
+ parseFormula(
+ const OUString& rFormula,
+ const css::table::CellAddress& rReferencePos ) override;
+
+ virtual OUString SAL_CALL
+ printFormula(
+ const css::uno::Sequence< css::sheet::FormulaToken >& rTokens,
+ const css::table::CellAddress& rReferencePos ) override;
+
+private:
+ typedef std::shared_ptr< OOXMLFormulaParserImpl > ParserImplRef;
+
+ css::uno::Reference< css::lang::XComponent >
+ mxComponent;
+ ParserImplRef mxParserImpl; /// Implementation of import parser.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/op.h b/sc/source/filter/inc/op.h
new file mode 100644
index 000000000..13b8e7012
--- /dev/null
+++ b/sc/source/filter/inc/op.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+class SfxItemSet;
+
+// OP code functions
+class SvStream;
+struct LotusContext;
+void NI(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_BOF(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_EOF(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Integer(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Number(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Label(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Formula(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_ColumnWidth(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_NamedRange(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_SymphNamedRange(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Footer(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Header(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Margins(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_HiddenCols(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Window1(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Blank(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+// Lotus 123 bits.
+void OP_BOF123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_EOF123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Number123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Label123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_Formula123(LotusContext &rContext, SvStream &aStream, sal_uInt16 nLength );
+void OP_IEEENumber123(LotusContext &rContext,SvStream& r, sal_uInt16 n);
+void OP_Note123(LotusContext &rContext,SvStream &aStream, sal_uInt16 nLength);
+void OP_CreatePattern123(LotusContext &rContext,SvStream &aStream, sal_uInt16 nLength);
+void OP_SheetName123(LotusContext &rContext, SvStream &rStream, sal_uInt16 nLength );
+void OP_HorAlign123(LotusContext &rContext,sal_uInt8 nAlignPattern, SfxItemSet& rPattern /* const ScPatternAttr& rPattern*/ );
+void OP_VerAlign123(LotusContext &rContext,sal_uInt8 nAlignPattern, SfxItemSet& rPattern /* const ScPatternAttr& rPattern*/ );
+void OP_ApplyPatternArea123(LotusContext &rContext,SvStream& r);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/optab.h b/sc/source/filter/inc/optab.h
new file mode 100644
index 000000000..fb85d5e51
--- /dev/null
+++ b/sc/source/filter/inc/optab.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+class SvStream;
+
+struct LotusContext;
+
+typedef void (*OPCODE_FKT)(LotusContext& rContext, SvStream& rStream, sal_uInt16 nLength);
+
+#define FKT_LIMIT 101
+
+#define FKT_LIMIT123 101
+
+#define LOTUS_EOF 0x01
+
+#define LOTUS_FILEPASSWD 0x4b
+
+#define LOTUS_PATTERN 0x284
+
+#define LOTUS_FORMAT_INDEX 0x800
+
+#define LOTUS_FORMAT_INFO 0x801
+
+#define ROW_FORMAT_MARKER 0x106
+
+#define COL_FORMAT_MARKER 0x107
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/orcusfiltersimpl.hxx b/sc/source/filter/inc/orcusfiltersimpl.hxx
new file mode 100644
index 000000000..4dea6ba6d
--- /dev/null
+++ b/sc/source/filter/inc/orcusfiltersimpl.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <orcusfilters.hxx>
+
+#include <orcus/xml_namespace.hpp>
+
+class ScOrcusFiltersImpl : public ScOrcusFilters
+{
+public:
+ virtual bool importCSV(ScDocument& rDoc, SfxMedium& rMedium) const override;
+ virtual bool importGnumeric(ScDocument& rDoc, SfxMedium& rMedium) const override;
+ virtual bool importExcel2003XML(ScDocument& rDoc, SfxMedium& rMedium) const override;
+ virtual bool importXLSX(ScDocument& rDoc, SfxMedium& rMedium) const override;
+ virtual bool importODS(ScDocument& rDoc, SfxMedium& rMedium) const override;
+
+ virtual bool importODS_Styles(ScDocument& rDoc, OUString& aFileName) const override;
+
+ virtual std::unique_ptr<ScOrcusXMLContext>
+ createXMLContext(ScDocument& rDoc, const OUString& rPath) const override;
+};
+
+class ScOrcusXMLContextImpl : public ScOrcusXMLContext
+{
+ ScDocument& mrDoc;
+ OUString maPath;
+
+ orcus::xmlns_repository maNsRepo; /// XML namespace repository for this context.
+
+public:
+ ScOrcusXMLContextImpl(ScDocument& rDoc, const OUString& rPath);
+ virtual ~ScOrcusXMLContextImpl() override;
+
+ virtual void loadXMLStructure(weld::TreeView& rTreeCtrl, ScOrcusXMLTreeParam& rParam) override;
+
+ virtual void importXML(const ScOrcusImportXMLParam& rParam) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/orcusinterface.hxx b/sc/source/filter/inc/orcusinterface.hxx
new file mode 100644
index 000000000..08e9c4a56
--- /dev/null
+++ b/sc/source/filter/inc/orcusinterface.hxx
@@ -0,0 +1,656 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <documentimport.hxx>
+
+#include <tools/color.hxx>
+#include <tools/fontenum.hxx>
+#include <editeng/svxenum.hxx>
+
+#include "sharedformulagroups.hxx"
+
+#include <conditio.hxx>
+
+#include <rtl/strbuf.hxx>
+#include <editeng/borderline.hxx>
+
+#include <orcus/spreadsheet/import_interface.hpp>
+
+#include <memory>
+#include <map>
+#include <unordered_map>
+#include <vector>
+
+class ScOrcusSheet;
+class ScOrcusStyles;
+class ScOrcusFactory;
+class SfxItemSet;
+namespace com::sun::star::task { class XStatusIndicator; }
+
+class ScOrcusGlobalSettings : public orcus::spreadsheet::iface::import_global_settings
+{
+ ScDocumentImport& mrDoc;
+ formula::FormulaGrammar::Grammar meCalcGrammar;
+ orcus::spreadsheet::formula_grammar_t meOrcusGrammar;
+ rtl_TextEncoding mnTextEncoding;
+
+public:
+ ScOrcusGlobalSettings(ScDocumentImport& rDoc);
+
+ virtual void set_origin_date(int year, int month, int day) override;
+ virtual void set_character_set(orcus::character_set_t cs) override;
+
+ virtual void set_default_formula_grammar(orcus::spreadsheet::formula_grammar_t grammar) override;
+ virtual orcus::spreadsheet::formula_grammar_t get_default_formula_grammar() const override;
+
+ formula::FormulaGrammar::Grammar getCalcGrammar() const
+ {
+ return meCalcGrammar;
+ }
+
+ rtl_TextEncoding getTextEncoding() const
+ {
+ return mnTextEncoding;
+ }
+
+ ScDocumentImport& getDoc() const
+ {
+ return mrDoc;
+ }
+};
+
+class ScOrcusRefResolver : public orcus::spreadsheet::iface::import_reference_resolver
+{
+ const ScOrcusGlobalSettings& mrGlobalSettings;
+
+public:
+ ScOrcusRefResolver( const ScOrcusGlobalSettings& rGS );
+
+ orcus::spreadsheet::src_address_t resolve_address(std::string_view address) override;
+ orcus::spreadsheet::src_range_t resolve_range(std::string_view range) override;
+};
+
+class ScOrcusNamedExpression : public orcus::spreadsheet::iface::import_named_expression
+{
+ ScDocumentImport& mrDoc;
+ const ScOrcusGlobalSettings& mrGlobalSettings;
+ ScAddress maBasePos;
+ OUString maName;
+ OUString maExpr;
+ const SCTAB mnTab; //< negative if global, else >= 0 for sheet-local named expressions.
+
+public:
+ ScOrcusNamedExpression( ScDocumentImport& rDoc, const ScOrcusGlobalSettings& rGS, SCTAB nTab = -1 );
+
+ void reset();
+
+ virtual void set_base_position(const orcus::spreadsheet::src_address_t& pos) override;
+ virtual void set_named_expression(std::string_view name, std::string_view expression) override;
+ virtual void set_named_range(std::string_view name, std::string_view range) override;
+ virtual void commit() override;
+};
+
+class ScOrcusSharedStrings : public orcus::spreadsheet::iface::import_shared_strings
+{
+ ScOrcusFactory& mrFactory;
+
+ OStringBuffer maCurSegment;
+public:
+ ScOrcusSharedStrings(ScOrcusFactory& rFactory);
+
+ virtual size_t append(std::string_view s) override;
+ virtual size_t add(std::string_view s) override;
+
+ virtual void set_segment_bold(bool b) override;
+ virtual void set_segment_italic(bool b) override;
+ virtual void set_segment_font(size_t font_index) override;
+ virtual void set_segment_font_name(std::string_view s) override;
+ virtual void set_segment_font_size(double point) override;
+ virtual void set_segment_font_color(orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ virtual void append_segment(std::string_view s) override;
+
+ virtual size_t commit_segments() override;
+};
+
+class ScOrcusConditionalFormat : public orcus::spreadsheet::iface::import_conditional_format
+{
+public:
+ ScOrcusConditionalFormat(SCTAB nTab, ScDocument& rDoc);
+ virtual ~ScOrcusConditionalFormat() override;
+
+ virtual void set_color(orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue) override;
+
+ virtual void set_formula(std::string_view formula) override;
+
+ virtual void set_condition_type(orcus::spreadsheet::condition_type_t type) override;
+
+ virtual void set_date(orcus::spreadsheet::condition_date_t date) override;
+
+ virtual void commit_condition() override;
+
+ virtual void set_icon_name(std::string_view name) override;
+
+ virtual void set_databar_gradient(bool gradient) override;
+
+ virtual void set_databar_axis(orcus::spreadsheet::databar_axis_t axis) override;
+
+ virtual void set_databar_color_positive(orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue) override;
+
+ virtual void set_databar_color_negative(orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue) override;
+
+ virtual void set_min_databar_length(double length) override;
+
+ virtual void set_max_databar_length(double length) override;
+
+ virtual void set_show_value(bool show) override;
+
+ virtual void set_iconset_reverse(bool reverse) override;
+
+ virtual void set_xf_id(size_t xf) override;
+
+ virtual void set_operator(orcus::spreadsheet::condition_operator_t condition_type) override;
+
+ virtual void set_type(orcus::spreadsheet::conditional_format_t type) override;
+
+ virtual void commit_entry() override;
+
+ virtual void set_range(std::string_view range) override;
+
+ virtual void set_range(orcus::spreadsheet::row_t row_start, orcus::spreadsheet::col_t col_start,
+ orcus::spreadsheet::row_t row_end, orcus::spreadsheet::col_t col_end) override;
+
+ virtual void commit_format() override;
+
+private:
+
+ SCTAB mnTab;
+ ScDocument& mrDoc;
+
+ std::unique_ptr<ScConditionalFormat> mpCurrentFormat;
+
+ ScFormatEntry::Type meEntryType;
+};
+
+class ScOrcusAutoFilter : public orcus::spreadsheet::iface::import_auto_filter
+{
+public:
+ ScOrcusAutoFilter( const ScOrcusGlobalSettings& rGS );
+
+ virtual ~ScOrcusAutoFilter() override;
+
+ virtual void set_range(const orcus::spreadsheet::range_t& range) override;
+
+ virtual void set_column(orcus::spreadsheet::col_t col) override;
+
+ virtual void append_column_match_value(std::string_view value) override;
+
+ virtual void commit_column() override;
+
+ virtual void commit() override;
+
+private:
+ const ScOrcusGlobalSettings& mrGlobalSettings;
+ ScRange maRange;
+};
+
+class ScOrcusSheetProperties : public orcus::spreadsheet::iface::import_sheet_properties
+{
+ ScDocumentImport& mrDoc;
+ SCTAB mnTab;
+public:
+ ScOrcusSheetProperties(SCTAB nTab, ScDocumentImport& rDoc);
+ virtual ~ScOrcusSheetProperties() override;
+
+ virtual void set_column_width(orcus::spreadsheet::col_t col, double width, orcus::length_unit_t unit) override;
+
+ virtual void set_column_hidden(orcus::spreadsheet::col_t col, bool hidden) override;
+
+ virtual void set_row_height(orcus::spreadsheet::row_t row, double height, orcus::length_unit_t unit) override;
+
+ virtual void set_row_hidden(orcus::spreadsheet::row_t row, bool hidden) override;
+
+ virtual void set_merge_cell_range(const orcus::spreadsheet::range_t& range) override;
+};
+
+class ScOrcusFormula : public orcus::spreadsheet::iface::import_formula
+{
+ enum class ResultType { NotSet, String, Value, Empty };
+
+ friend class ScOrcusSheet;
+
+ ScOrcusSheet& mrSheet;
+
+ SCCOL mnCol;
+ SCROW mnRow;
+ OUString maFormula;
+ formula::FormulaGrammar::Grammar meGrammar;
+ size_t mnSharedFormulaIndex;
+ bool mbShared;
+
+ ResultType meResType;
+ OUString maResult; // result string.
+ double mfResult;
+
+ void reset();
+
+public:
+ ScOrcusFormula( ScOrcusSheet& rSheet );
+ virtual ~ScOrcusFormula() override;
+
+ virtual void set_position(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col) override;
+ virtual void set_formula(orcus::spreadsheet::formula_grammar_t grammar, std::string_view formula) override;
+ virtual void set_shared_formula_index(size_t index) override;
+ virtual void set_result_value(double value) override;
+ virtual void set_result_string(std::string_view value) override;
+ virtual void set_result_empty() override;
+ virtual void set_result_bool(bool value) override;
+ virtual void commit() override;
+};
+
+class ScOrcusArrayFormula : public orcus::spreadsheet::iface::import_array_formula
+{
+ friend class ScOrcusSheet;
+
+ ScOrcusSheet& mrSheet;
+
+ SCCOL mnCol;
+ SCROW mnRow;
+ uint32_t mnColRange;
+ uint32_t mnRowRange;
+ OUString maFormula;
+ formula::FormulaGrammar::Grammar meGrammar;
+
+ void reset();
+
+public:
+ ScOrcusArrayFormula( ScOrcusSheet& rSheet );
+ virtual ~ScOrcusArrayFormula() override;
+
+ virtual void set_range(const orcus::spreadsheet::range_t& range) override;
+ virtual void set_formula(orcus::spreadsheet::formula_grammar_t grammar, std::string_view formula) override;
+ virtual void set_result_value(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, double value) override;
+ virtual void set_result_string(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, std::string_view value) override;
+ virtual void set_result_empty(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col) override;
+ virtual void set_result_bool(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, bool value) override;
+ virtual void commit() override;
+};
+
+class ScOrcusSheet : public orcus::spreadsheet::iface::import_sheet
+{
+ friend class ScOrcusFormula;
+ friend class ScOrcusArrayFormula;
+
+ ScDocumentImport& mrDoc;
+ SCTAB mnTab;
+ ScOrcusFactory& mrFactory;
+ ScOrcusStyles& mrStyles;
+ sc::SharedFormulaGroups maFormulaGroups;
+
+ ScOrcusAutoFilter maAutoFilter;
+ ScOrcusSheetProperties maProperties;
+ ScOrcusConditionalFormat maConditionalFormat;
+ ScOrcusNamedExpression maNamedExpressions;
+ ScOrcusFormula maFormula;
+ ScOrcusArrayFormula maArrayFormula;
+
+ int mnCellCount;
+
+ void cellInserted();
+
+ ScDocumentImport& getDoc();
+
+public:
+ ScOrcusSheet(ScDocumentImport& rDoc, SCTAB nTab, ScOrcusFactory& rFactory);
+
+ virtual orcus::spreadsheet::iface::import_auto_filter* get_auto_filter() override;
+ virtual orcus::spreadsheet::iface::import_table* get_table() override;
+ virtual orcus::spreadsheet::iface::import_sheet_properties* get_sheet_properties() override;
+ virtual orcus::spreadsheet::iface::import_conditional_format* get_conditional_format() override;
+ virtual orcus::spreadsheet::iface::import_named_expression* get_named_expression() override;
+ virtual orcus::spreadsheet::iface::import_formula* get_formula() override;
+ virtual orcus::spreadsheet::iface::import_array_formula* get_array_formula() override;
+
+ // Orcus import interface
+ virtual void set_auto(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, std::string_view value) override;
+ virtual void set_string(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, orcus::spreadsheet::string_id_t sindex) override;
+ virtual void set_value(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, double value) override;
+ virtual void set_bool(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, bool value) override;
+ virtual void set_date_time(
+ orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, int year, int month, int day, int hour, int minute, double second) override;
+
+ virtual void set_format(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, size_t xf_index) override;
+ virtual void set_format(orcus::spreadsheet::row_t row_start, orcus::spreadsheet::col_t col_start,
+ orcus::spreadsheet::row_t row_end, orcus::spreadsheet::col_t col_end, size_t xf_index) override;
+
+ virtual orcus::spreadsheet::range_size_t get_sheet_size() const override;
+
+ virtual void fill_down_cells(orcus::spreadsheet::row_t row, orcus::spreadsheet::col_t col, orcus::spreadsheet::row_t range_size) override;
+
+ SCTAB getIndex() const { return mnTab; }
+
+ const sc::SharedFormulaGroups& getSharedFormulaGroups() const;
+ sc::SharedFormulaGroups& getSharedFormulaGroups();
+ ScOrcusFactory& getFactory();
+};
+
+class ScOrcusStyles : public orcus::spreadsheet::iface::import_styles
+{
+private:
+ ScOrcusFactory& mrFactory;
+
+ struct font
+ {
+ std::optional<OUString> maName;
+ std::optional<double> mnSize;
+ std::optional<Color> maColor;
+ std::optional<bool> mbBold;
+ std::optional<bool> mbItalic;
+ std::optional<FontLineStyle> meUnderline;
+ std::optional<Color> maUnderlineColor;
+ std::optional<FontStrikeout> meStrikeout;
+
+ void applyToItemSet(SfxItemSet& rSet) const;
+ };
+
+ font maCurrentFont;
+ std::vector<font> maFonts;
+
+ struct fill
+ {
+ std::optional<orcus::spreadsheet::fill_pattern_t> mePattern;
+ std::optional<Color> maFgColor;
+ std::optional<Color> maBgColor; // currently not used.
+
+ void applyToItemSet(SfxItemSet& rSet) const;
+ };
+
+ fill maCurrentFill;
+ std::vector<fill> maFills;
+
+ struct border
+ {
+ struct border_line
+ {
+ std::optional<SvxBorderLineStyle> meStyle;
+ std::optional<Color> maColor;
+ std::optional<double> mnWidth;
+ };
+
+ std::map<orcus::spreadsheet::border_direction_t, border_line> maBorders;
+
+ void applyToItemSet(SfxItemSet& rSet) const;
+ };
+
+ border maCurrentBorder;
+ std::vector<border> maBorders;
+
+ struct protection
+ {
+ std::optional<bool> mbLocked;
+ std::optional<bool> mbHidden;
+ std::optional<bool> mbPrintContent;
+ std::optional<bool> mbFormulaHidden;
+
+ void applyToItemSet(SfxItemSet& rSet) const;
+ };
+
+ protection maCurrentProtection;
+ std::vector<protection> maProtections;
+
+ struct number_format
+ {
+ std::optional<OUString> maCode;
+
+ void applyToItemSet(SfxItemSet& rSet, const ScDocument& rDoc) const;
+ };
+
+ number_format maCurrentNumberFormat;
+ std::vector<number_format> maNumberFormats;
+
+ struct xf
+ {
+ size_t mnFontId;
+ size_t mnFillId;
+ size_t mnBorderId;
+ size_t mnProtectionId;
+ size_t mnNumberFormatId;
+ size_t mnStyleXf;
+ bool mbAlignment;
+
+ SvxCellHorJustify meHorAlignment;
+ SvxCellVerJustify meVerAlignment;
+ SvxCellJustifyMethod meHorAlignMethod;
+ SvxCellJustifyMethod meVerAlignMethod;
+
+ xf();
+ };
+
+ xf maCurrentXF;
+ std::vector<xf> maCellStyleXfs;
+ std::vector<xf> maCellXfs;
+
+ struct cell_style
+ {
+ OUString maName;
+ OUString maParentName;
+ size_t mnXFId;
+ size_t mnBuiltInId;
+
+ cell_style();
+ };
+
+ cell_style maCurrentCellStyle;
+
+ void applyXfToItemSet(SfxItemSet& rSet, const xf& rXf);
+
+public:
+ ScOrcusStyles( ScOrcusFactory& rFactory, bool bSkipDefaultStyles=false );
+
+ void applyXfToItemSet(SfxItemSet& rSet, size_t xfId);
+
+ // font
+
+ virtual void set_font_count(size_t n) override;
+ virtual void set_font_bold(bool b) override;
+ virtual void set_font_italic(bool b) override;
+ virtual void set_font_name(std::string_view name) override;
+ virtual void set_font_size(double point) override;
+ virtual void set_font_underline(orcus::spreadsheet::underline_t e) override;
+ virtual void set_font_underline_width(orcus::spreadsheet::underline_width_t e) override;
+ virtual void set_font_underline_mode(orcus::spreadsheet::underline_mode_t e) override;
+ virtual void set_font_underline_type(orcus::spreadsheet::underline_type_t e) override;
+ virtual void set_font_underline_color(orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ virtual void set_font_color( orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ virtual void set_strikethrough_style(orcus::spreadsheet::strikethrough_style_t s) override;
+ virtual void set_strikethrough_type(orcus::spreadsheet::strikethrough_type_t s) override;
+ virtual void set_strikethrough_width(orcus::spreadsheet::strikethrough_width_t s) override;
+ virtual void set_strikethrough_text(orcus::spreadsheet::strikethrough_text_t s) override;
+ virtual size_t commit_font() override;
+
+ // fill
+
+ virtual void set_fill_count(size_t n) override;
+ virtual void set_fill_pattern_type(orcus::spreadsheet::fill_pattern_t fp) override;
+ virtual void set_fill_fg_color(orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red, orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue) override;
+ virtual void set_fill_bg_color(orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red, orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue) override;
+ virtual size_t commit_fill() override;
+
+ // border
+
+ virtual void set_border_count(size_t n) override;
+
+ virtual void set_border_style(orcus::spreadsheet::border_direction_t dir, orcus::spreadsheet::border_style_t style) override;
+ virtual void set_border_color(orcus::spreadsheet::border_direction_t dir,
+ orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue) override;
+ virtual void set_border_width(orcus::spreadsheet::border_direction_t dir, double val, orcus::length_unit_t unit) override;
+ virtual size_t commit_border() override;
+
+ // cell protection
+ virtual void set_cell_hidden(bool b) override;
+ virtual void set_cell_locked(bool b) override;
+ virtual void set_cell_print_content(bool b) override;
+ virtual void set_cell_formula_hidden(bool b) override;
+ virtual size_t commit_cell_protection() override;
+
+ // number format
+ virtual void set_number_format_count(size_t n) override;
+ virtual void set_number_format_identifier(size_t n) override;
+ virtual void set_number_format_code(std::string_view s) override;
+ virtual size_t commit_number_format() override;
+
+ // cell style xf
+
+ virtual void set_cell_style_xf_count(size_t n) override;
+ virtual size_t commit_cell_style_xf() override;
+
+ // cell xf
+
+ virtual void set_cell_xf_count(size_t n) override;
+ virtual size_t commit_cell_xf() override;
+
+ // dxf
+ virtual void set_dxf_count(size_t count) override;
+ virtual size_t commit_dxf() override;
+
+ // xf (cell format) - used both by cell xf and cell style xf.
+
+ virtual void set_xf_number_format(size_t index) override;
+ virtual void set_xf_font(size_t index) override;
+ virtual void set_xf_fill(size_t index) override;
+ virtual void set_xf_border(size_t index) override;
+ virtual void set_xf_protection(size_t index) override;
+ virtual void set_xf_style_xf(size_t index) override;
+ virtual void set_xf_apply_alignment(bool b) override;
+ virtual void set_xf_horizontal_alignment(orcus::spreadsheet::hor_alignment_t align) override;
+ virtual void set_xf_vertical_alignment(orcus::spreadsheet::ver_alignment_t align) override;
+
+ // cell style entry
+
+ virtual void set_cell_style_count(size_t n) override;
+ virtual void set_cell_style_name(std::string_view name) override;
+ virtual void set_cell_style_xf(size_t index) override;
+ virtual void set_cell_style_builtin(size_t index) override;
+ virtual void set_cell_style_parent_name(std::string_view name) override;
+ virtual size_t commit_cell_style() override;
+};
+
+class ScOrcusFactory : public orcus::spreadsheet::iface::import_factory
+{
+ struct CellStoreToken
+ {
+ enum class Type : sal_uInt8
+ {
+ Auto,
+ Numeric,
+ String,
+ Formula,
+ FormulaWithResult,
+ SharedFormula,
+ SharedFormulaWithResult,
+ Matrix,
+ FillDownCells
+ };
+
+
+ OUString maStr1;
+ OUString maStr2;
+ double mfValue;
+
+ ScAddress maPos;
+ Type meType;
+
+ uint32_t mnIndex1;
+ uint32_t mnIndex2;
+ formula::FormulaGrammar::Grammar meGrammar;
+
+ CellStoreToken( const ScAddress& rPos, Type eType );
+ CellStoreToken( const ScAddress& rPos, double fValue );
+ CellStoreToken( const ScAddress& rPos, uint32_t nIndex );
+ CellStoreToken( const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar );
+ };
+
+ typedef std::unordered_map<OUString, size_t> StringHashType;
+ typedef std::vector<CellStoreToken> CellStoreTokensType;
+
+ ScDocumentImport maDoc;
+
+ std::vector<OUString> maStrings;
+ StringHashType maStringHash;
+
+ CellStoreTokensType maCellStoreTokens;
+ ScOrcusGlobalSettings maGlobalSettings;
+ ScOrcusSharedStrings maSharedStrings;
+ ScOrcusNamedExpression maNamedExpressions;
+ std::vector<std::unique_ptr<ScOrcusSheet>> maSheets;
+ ScOrcusStyles maStyles;
+
+ int mnProgress;
+
+ css::uno::Reference<css::task::XStatusIndicator> mxStatusIndicator;
+
+public:
+ ScOrcusFactory(ScDocument& rDoc, bool bSkipDefaultStyles=false);
+
+ virtual orcus::spreadsheet::iface::import_sheet* append_sheet(
+ orcus::spreadsheet::sheet_t sheet_index, std::string_view sheet_name) override;
+ virtual orcus::spreadsheet::iface::import_sheet* get_sheet(std::string_view sheet_name) override;
+ virtual orcus::spreadsheet::iface::import_sheet* get_sheet(orcus::spreadsheet::sheet_t sheet_index) override;
+ virtual orcus::spreadsheet::iface::import_global_settings* get_global_settings() override;
+ virtual orcus::spreadsheet::iface::import_shared_strings* get_shared_strings() override;
+ virtual orcus::spreadsheet::iface::import_named_expression* get_named_expression() override;
+ virtual orcus::spreadsheet::iface::import_styles* get_styles() override;
+ virtual void finalize() override;
+
+ ScDocumentImport& getDoc();
+
+ size_t appendString(const OUString& rStr);
+ size_t addString(const OUString& rStr);
+ const OUString* getString(size_t nIndex) const;
+
+ void pushCellStoreAutoToken( const ScAddress& rPos, const OUString& rVal );
+ void pushCellStoreToken( const ScAddress& rPos, uint32_t nStrIndex );
+ void pushCellStoreToken( const ScAddress& rPos, double fValue );
+ void pushCellStoreToken(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar );
+ void pushFillDownCellsToken( const ScAddress& rPos, uint32_t nFillSize );
+
+ void pushSharedFormulaToken( const ScAddress& rPos, uint32_t nIndex );
+ void pushMatrixFormulaToken(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar,
+ uint32_t nRowRange, uint32_t nColRange );
+
+ void pushFormulaResult( const ScAddress& rPos, double fValue );
+ void pushFormulaResult( const ScAddress& rPos, const OUString& rValue );
+
+ void incrementProgress();
+
+ void setStatusIndicator(const css::uno::Reference<css::task::XStatusIndicator>& rIndicator);
+
+ const ScOrcusGlobalSettings& getGlobalSettings() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/otlnbuff.hxx b/sc/source/filter/inc/otlnbuff.hxx
new file mode 100644
index 000000000..288971f51
--- /dev/null
+++ b/sc/source/filter/inc/otlnbuff.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <mdds/flat_segment_tree.hpp>
+#include <set>
+#include <address.hxx>
+
+class ScOutlineArray;
+
+class XclImpOutlineBuffer
+{
+public:
+ XclImpOutlineBuffer( SCSIZE nNewSize );
+ ~XclImpOutlineBuffer();
+
+ void SetLevel( SCSIZE nIndex, sal_uInt8 nVal, bool bCollapsed );
+ void SetOutlineArray( ScOutlineArray* pOArray );
+ void MakeScOutline();
+ void SetLevelRange( SCSIZE nF, SCSIZE nL, sal_uInt8 nVal, bool bCollapsed );
+ void SetButtonMode( bool bRightOrUnder );
+
+private:
+ typedef ::mdds::flat_segment_tree<SCSIZE, sal_uInt8> OutlineLevels;
+ OutlineLevels maLevels;
+ ::std::set<SCSIZE> maCollapsedPosSet;
+ ScOutlineArray* mpOutlineArray;
+ SCSIZE mnEndPos;
+ sal_uInt8 mnMaxLevel;
+ bool mbButtonAfter:1;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pagesettings.hxx b/sc/source/filter/inc/pagesettings.hxx
new file mode 100644
index 000000000..f951f3197
--- /dev/null
+++ b/sc/source/filter/inc/pagesettings.hxx
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <string_view>
+
+#include "worksheethelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class PropertySet; }
+namespace oox::core { class Relations; }
+
+namespace oox::xls {
+
+class HeaderFooterParser;
+
+/** Holds page style data for a single sheet. */
+struct PageSettingsModel
+{
+ css::uno::Reference<css::graphic::XGraphic> mxGraphic; /// Background Graphic
+ OUString maBinSettPath; /// Relation identifier of binary printer settings.
+ OUString maOddHeader; /// Header string for odd pages.
+ OUString maOddFooter; /// Footer string for odd pages.
+ OUString maEvenHeader; /// Header string for even pages.
+ OUString maEvenFooter; /// Footer string for even pages.
+ OUString maFirstHeader; /// Header string for first page of the sheet.
+ OUString maFirstFooter; /// Footer string for first page of the sheet.
+ double mfLeftMargin; /// Margin between left edge of page and begin of sheet area.
+ double mfRightMargin; /// Margin between end of sheet area and right edge of page.
+ double mfTopMargin; /// Margin between top edge of page and begin of sheet area.
+ double mfBottomMargin; /// Margin between end of sheet area and bottom edge of page.
+ double mfHeaderMargin; /// Margin between top edge of page and begin of header.
+ double mfFooterMargin; /// Margin between end of footer and bottom edge of page.
+ sal_Int32 mnPaperSize; /// Paper size (enumeration).
+ sal_Int32 mnPaperWidth; /// Paper width in twips
+ sal_Int32 mnPaperHeight; /// Paper height in twips
+ sal_Int32 mnCopies; /// Number of copies to print.
+ sal_Int32 mnScale; /// Page scale (zoom in percent).
+ sal_Int32 mnFirstPage; /// First page number.
+ sal_Int32 mnFitToWidth; /// Fit to number of pages in horizontal direction.
+ sal_Int32 mnFitToHeight; /// Fit to number of pages in vertical direction.
+ sal_Int32 mnHorPrintRes; /// Horizontal printing resolution in DPI.
+ sal_Int32 mnVerPrintRes; /// Vertical printing resolution in DPI.
+ sal_Int32 mnOrientation; /// Landscape or portrait.
+ sal_Int32 mnPageOrder; /// Page order through sheet area (to left or down).
+ sal_Int32 mnCellComments; /// Cell comments printing mode.
+ sal_Int32 mnPrintErrors; /// Cell error printing mode.
+ bool mbUseEvenHF; /// True = use maEvenHeader/maEvenFooter.
+ bool mbUseFirstHF; /// True = use maFirstHeader/maFirstFooter.
+ bool mbValidSettings; /// True = use imported settings.
+ bool mbUseFirstPage; /// True = start page numbering with mnFirstPage.
+ bool mbBlackWhite; /// True = print black and white.
+ bool mbDraftQuality; /// True = print in draft quality.
+ bool mbFitToPages; /// True = Fit to width/height; false = scale in percent.
+ bool mbHorCenter; /// True = horizontally centered.
+ bool mbVerCenter; /// True = vertically centered.
+ bool mbPrintGrid; /// True = print grid lines.
+ bool mbPrintHeadings; /// True = print column/row headings.
+
+ explicit PageSettingsModel();
+
+ /** Sets the BIFF print errors mode. */
+ void setBiffPrintErrors( sal_uInt8 nPrintErrors );
+};
+
+class PageSettings : public WorksheetHelper
+{
+public:
+ explicit PageSettings( const WorksheetHelper& rHelper );
+
+ /** Imports printing options from a printOptions element. */
+ void importPrintOptions( const AttributeList& rAttribs );
+ /** Imports pageMarings element containing page margins. */
+ void importPageMargins( const AttributeList& rAttribs );
+ /** Imports pageSetup element for worksheets. */
+ void importPageSetup( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+ /** Imports pageSetup element for chart sheets. */
+ void importChartPageSetup( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+ /** Imports header and footer settings from a headerFooter element. */
+ void importHeaderFooter( const AttributeList& rAttribs );
+ /** Imports header/footer characters from a headerFooter element. */
+ void importHeaderFooterCharacters(
+ std::u16string_view rChars, sal_Int32 nElement );
+ /** Imports the picture element. */
+ void importPicture( const ::oox::core::Relations& rRelations, const AttributeList& rAttribs );
+
+ /** Imports the PRINTOPTIONS record from the passed stream. */
+ void importPrintOptions( SequenceInputStream& rStrm );
+ /** Imports the PAGEMARGINS record from the passed stream. */
+ void importPageMargins( SequenceInputStream& rStrm );
+ /** Imports the PAGESETUP record from the passed stream. */
+ void importPageSetup( const ::oox::core::Relations& rRelations, SequenceInputStream& rStrm );
+ /** Imports the CHARTPAGESETUP record from the passed stream. */
+ void importChartPageSetup( const ::oox::core::Relations& rRelations, SequenceInputStream& rStrm );
+ /** Imports the HEADERFOOTER record from the passed stream. */
+ void importHeaderFooter( SequenceInputStream& rStrm );
+ /** Imports the PICTURE record from the passed stream. */
+ void importPicture( const ::oox::core::Relations& rRelations, SequenceInputStream& rStrm );
+
+ /** Sets whether percentual scaling or fit to width/height scaling is used. */
+ void setFitToPagesMode( bool bFitToPages );
+
+ /** Creates a page style for the spreadsheet and sets all page properties. */
+ void finalizeImport();
+
+private:
+ /** Imports the binary picture data from the fragment with the passed identifier. */
+ void importPictureData( const ::oox::core::Relations& rRelations, const OUString& rRelId );
+
+private:
+ PageSettingsModel maModel;
+};
+
+class PageSettingsConverter : public WorkbookHelper
+{
+public:
+ explicit PageSettingsConverter( const WorkbookHelper& rHelper );
+ virtual ~PageSettingsConverter() override;
+
+ /** Writes all properties to the passed property set of a page style object. */
+ void writePageSettingsProperties(
+ PropertySet& rPropSet,
+ const PageSettingsModel& rModel,
+ WorksheetType eSheetType );
+
+private:
+ struct HFHelperData
+ {
+ sal_Int32 mnLeftPropId;
+ sal_Int32 mnRightPropId;
+ sal_Int32 mnFirstPropId;
+ sal_Int32 mnHeight;
+ sal_Int32 mnBodyDist;
+ bool mbHasContent;
+ bool mbShareOddEven;
+ bool mbShareFirst;
+ bool mbDynamicHeight;
+
+ explicit HFHelperData( sal_Int32 nLeftPropId, sal_Int32 nRightPropId, sal_Int32 nFirstPropId );
+ };
+
+private:
+ void convertHeaderFooterData(
+ PropertySet& rPropSet,
+ HFHelperData& orHFData,
+ const OUString& rOddContent,
+ const OUString& rEvenContent,
+ const OUString& rFirstContent,
+ bool bUseEvenContent,
+ bool bUseFirstContent,
+ double fPageMargin,
+ double fContentMargin );
+
+ sal_Int32 writeHeaderFooter(
+ PropertySet& rPropSet,
+ sal_Int32 nPropId,
+ const OUString& rContent );
+
+private:
+ typedef ::std::unique_ptr< HeaderFooterParser > HeaderFooterParserPtr;
+ HeaderFooterParserPtr mxHFParser;
+ HFHelperData maHeaderData;
+ HFHelperData maFooterData;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pivotcachebuffer.hxx b/sc/source/filter/inc/pivotcachebuffer.hxx
new file mode 100644
index 000000000..5997d8db3
--- /dev/null
+++ b/sc/source/filter/inc/pivotcachebuffer.hxx
@@ -0,0 +1,458 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/util/DateTime.hpp>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace com::sun::star {
+ namespace sheet { class XDataPilotField; }
+}
+
+namespace oox::core { class Relations; }
+
+class ScDPSaveDimension;
+class ScDPObject;
+class DateTime;
+
+namespace oox::xls {
+
+class WorksheetHelper;
+
+typedef ::std::pair< sal_Int32, OUString > IdCaptionPair;
+typedef ::std::vector< IdCaptionPair > IdCaptionPairList;
+
+class PivotCacheItem
+{
+public:
+ explicit PivotCacheItem();
+
+ /** Reads the string value from a pivot cache item. */
+ void readString( const AttributeList& rAttribs );
+ /** Reads the double value from a pivot cache item. */
+ void readNumeric( const AttributeList& rAttribs );
+ /** Reads the date/time value from a pivot cache item. */
+ void readDate( const AttributeList& rAttribs );
+ /** Reads the boolean value from a pivot cache item. */
+ void readBool( const AttributeList& rAttribs );
+ /** Reads the error code value from a pivot cache item. */
+ void readError( const AttributeList& rAttribs );
+ /** Reads the index of a shared item. */
+ void readIndex( const AttributeList& rAttribs );
+
+ /** Reads the string value from a pivot cache item. */
+ void readString( SequenceInputStream& rStrm );
+ /** Reads the double value from a pivot cache item. */
+ void readDouble( SequenceInputStream& rStrm );
+ /** Reads the date/time value from a pivot cache item. */
+ void readDate( SequenceInputStream& rStrm );
+ /** Reads the boolean value from a pivot cache item. */
+ void readBool( SequenceInputStream& rStrm );
+ /** Reads the error code value from a pivot cache item. */
+ void readError(SequenceInputStream& rStrm, const UnitConverter& rUnitConverter);
+ /** Reads the index of a shared item. */
+ void readIndex( SequenceInputStream& rStrm );
+
+ /** Returns the type of the item. */
+ sal_Int32 getType() const { return mnType; }
+ /** Returns the value of the item. */
+ const css::uno::Any& getValue() const { return maValue; }
+ /** Returns the string representation of the item. */
+ OUString getName() const;
+
+ /** Returns the string representation of the item, using the actual formatting. */
+ OUString getFormattedName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const DateTime& rNullDate) const;
+ /** Returns true if the item is unused. */
+ bool isUnused() const { return mbUnused; }
+
+private:
+friend class PivotCacheItemList;
+ // #FIXME hack Sets the value of this item to the given string ( and overwrites type if necessary
+ void setStringValue( const OUString& sName );
+ css::uno::Any maValue; /// Value of the item.
+ sal_Int32 mnType; /// Value type (OOXML token identifier).
+ bool mbUnused;
+};
+
+class PivotCacheItemList : public WorkbookHelper
+{
+public:
+ explicit PivotCacheItemList( const WorkbookHelper& rHelper );
+
+ /** Imports the item from the passed attribute list. */
+ void importItem( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Imports the item from the passed stream and record. */
+ void importItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+
+ /** Returns true, if this item list is empty. */
+ bool empty() const { return maItems.empty(); }
+ /** Returns the size of the item list. */
+ size_t size() const { return maItems.size(); }
+
+ /** Returns the specified item. */
+ const PivotCacheItem* getCacheItem( sal_Int32 nItemIdx ) const;
+ /** Returns the names of all items. */
+ void getCacheItemNames( ::std::vector< OUString >& orItemNames ) const;
+ void applyItemCaptions( const IdCaptionPairList& vCaptions );
+
+private:
+ /** Creates and returns a new item at the end of the items list. */
+ PivotCacheItem& createItem();
+ /** Imports an array of items from the PCITEM_ARRAY record */
+ void importArray( SequenceInputStream& rStrm );
+
+private:
+ std::vector< PivotCacheItem > maItems; /// All items of this list.
+};
+
+struct PCFieldModel
+{
+ OUString maName; /// Fixed name of the cache field.
+ OUString maCaption; /// Caption of the cache field.
+ OUString maPropertyName; /// OLAP property name.
+ OUString maFormula; /// Formula of a calculated field.
+ sal_Int32 mnNumFmtId; /// Number format for all items.
+ sal_Int32 mnSqlType; /// Data type from ODBC data source.
+ sal_Int32 mnHierarchy; /// Hierarchy this field is part of.
+ sal_Int32 mnLevel; /// Hierarchy level this field is part of.
+ sal_Int32 mnMappingCount; /// Number of property mappings.
+ bool mbDatabaseField; /// True = field from source data; false = calculated field.
+ bool mbServerField; /// True = ODBC server-based page field.
+ bool mbUniqueList; /// True = list of unique ODBC items exists.
+ bool mbMemberPropField; /// True = contains OLAP member properties.
+
+ explicit PCFieldModel();
+};
+
+struct PCSharedItemsModel
+{
+ bool mbHasSemiMixed; /// True = has (blank|string|bool|error) item(s), maybe other types.
+ bool mbHasNonDate; /// True = has non-date item(s), maybe date items.
+ bool mbHasDate; /// True = has date item(s), maybe other types.
+ bool mbHasString; /// True = has (string|bool|error) item(s), maybe other types.
+ bool mbHasBlank; /// True = has blank item(s), maybe other types.
+ bool mbHasMixed; /// True = has [(string|bool|error) and (number|date)] or (number and date).
+ bool mbIsNumeric; /// True = has numeric item(s), maybe other types except date.
+ bool mbIsInteger; /// True = has numeric item(s) with only integers, maybe other types except date.
+ bool mbHasLongText; /// True = contains strings with >255 characters.
+
+ explicit PCSharedItemsModel();
+};
+
+struct PCFieldGroupModel
+{
+ css::util::DateTime maStartDate; /// Manual or calculated start date for range grouping.
+ css::util::DateTime maEndDate; /// Manual or calculated end date for range grouping.
+ double mfStartValue; /// Manual or calculated start value for range grouping.
+ double mfEndValue; /// Manual or calculated end value for range grouping.
+ double mfInterval; /// Interval for numeric range grouping.
+ sal_Int32 mnParentField; /// Index of cache field that contains item groups based on this field.
+ sal_Int32 mnBaseField; /// Index of cache field this grouped field is based on.
+ sal_Int32 mnGroupBy; /// Type of numeric or date range grouping.
+ bool mbRangeGroup; /// True = items are grouped by numeric ranges or date ranges.
+ bool mbDateGroup; /// True = items are grouped by date ranges or by item names.
+ bool mbAutoStart; /// True = start value for range groups is calculated from source data.
+ bool mbAutoEnd; /// True = end value for range groups is calculated from source data.
+ OUString msFinalGroupName ; /// Finalized group name of this field used in internal pivot table collection.
+
+
+ explicit PCFieldGroupModel();
+
+ /** Sets the group-by value for BIFF import. */
+ void setBiffGroupBy( sal_uInt8 nGroupBy );
+};
+
+/** Helper struct for mapping original item names from/to group item names. */
+struct PivotCacheGroupItem
+{
+ OUString maOrigName;
+ OUString maGroupName;
+
+ explicit PivotCacheGroupItem( const OUString& rItemName ) :
+ maOrigName( rItemName ), maGroupName( rItemName ) {}
+};
+
+typedef ::std::vector< PivotCacheGroupItem > PivotCacheGroupItemVector;
+
+class PivotCacheField : public WorkbookHelper
+{
+public:
+ explicit PivotCacheField( const WorkbookHelper& rHelper, bool bIsDatabaseField );
+
+ /** Imports pivot cache field settings from the cacheField element. */
+ void importCacheField( const AttributeList& rAttribs );
+ /** Imports shared items settings from the sharedItems element. */
+ void importSharedItems( const AttributeList& rAttribs );
+ /** Imports a shared item from the passed element. */
+ void importSharedItem( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Imports grouping settings from the fieldGroup element. */
+ void importFieldGroup( const AttributeList& rAttribs );
+ /** Imports numeric grouping settings from the rangePr element. */
+ void importRangePr( const AttributeList& rAttribs );
+ /** Imports an item of the mapping between group items and base items from the passed element. */
+ void importDiscretePrItem( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Imports a group item from the passed element. */
+ void importGroupItem( sal_Int32 nElement, const AttributeList& rAttribs );
+
+ /** Imports pivot cache field settings from the PCDFIELD record. */
+ void importPCDField( SequenceInputStream& rStrm );
+ /** Imports shared items settings from the PCDFSHAREDITEMS record. */
+ void importPCDFSharedItems( SequenceInputStream& rStrm );
+ /** Imports one or more shared items from the passed record. */
+ void importPCDFSharedItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+ /** Imports grouping settings from the PCDFIELDGROUP record. */
+ void importPCDFieldGroup( SequenceInputStream& rStrm );
+ /** Imports numeric grouping settings from the PCDFRANGEPR record. */
+ void importPCDFRangePr( SequenceInputStream& rStrm );
+ /** Imports an item of the mapping between group items and base items from the passed record. */
+ void importPCDFDiscretePrItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+ /** Imports one or more group items from the passed record. */
+ void importPCDFGroupItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+
+ /** Apply user Captions to imported group data */
+ void applyItemCaptions( const IdCaptionPairList& vCaptions );
+
+ /** Returns true, if the field is based on source data, or false if it is grouped or calculated. */
+ bool isDatabaseField() const { return maFieldModel.mbDatabaseField; }
+
+ /** Returns true, if the field contains a list of shared items. */
+ bool hasSharedItems() const { return !maSharedItems.empty(); }
+ /** Returns true, if the field contains a list of grouping items. */
+ bool hasGroupItems() const { return !maGroupItems.empty(); }
+ /** Returns true, if the field has inplace numeric grouping settings. */
+ bool hasNumericGrouping() const { return maFieldGroupModel.mbRangeGroup && !maFieldGroupModel.mbDateGroup; }
+ /** Returns true, if the field has inplace date grouping settings. */
+ bool hasDateGrouping() const { return maFieldGroupModel.mbRangeGroup && maFieldGroupModel.mbDateGroup; }
+ /** Returns true, if the field has a parent group field that groups the items of this field. */
+ bool hasParentGrouping() const { return maFieldGroupModel.mnParentField >= 0; }
+
+ /** Returns the name of the cache field. */
+ const OUString& getName() const { return maFieldModel.maName; }
+ /** Returns the index of the parent group field that groups the items of this field. */
+ sal_Int32 getParentGroupField() const { return maFieldGroupModel.mnParentField; }
+ /** Returns the index of the base field grouping is based on. */
+ sal_Int32 getGroupBaseField() const { return maFieldGroupModel.mnBaseField; }
+ /** Returns the finalized group name of this field. */
+ const OUString& getFinalGroupName() const { return maFieldGroupModel.msFinalGroupName; }
+ /** Set the finalized group name of this field. */
+ void setFinalGroupName(const OUString& rFinalGroupName) { maFieldGroupModel.msFinalGroupName = rFinalGroupName; }
+
+ /** Returns the shared or group item with the specified index. */
+ const PivotCacheItem* getCacheItem( sal_Int32 nItemIdx ) const;
+ /** Returns the names of all shared or group items. */
+ void getCacheItemNames( ::std::vector< OUString >& orItemNames ) const;
+ /** Returns shared or group items. */
+ const PivotCacheItemList& getCacheItems() const;
+
+ /** Creates inplace numeric grouping settings. */
+ void convertNumericGrouping(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxDPField ) const;
+ /** Creates inplace date grouping settings or a new date group field. */
+ OUString createDateGroupField(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField ) const;
+ /** Creates a new grouped DataPilot field and returns its name. */
+ OUString createParentGroupField(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ const PivotCacheField& rBaseCacheField,
+ PivotCacheGroupItemVector& orItemNames ) const;
+
+ /** Writes the title of the field into the passed sheet at the passed address. */
+ void writeSourceHeaderCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow ) const;
+ /** Writes a source field item value into the passed sheet. */
+ void writeSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow,
+ const PivotCacheItem& rItem ) const;
+
+ /** Reads an item from the PCRECORD record and writes it to the passed sheet. */
+ void importPCRecordItem( SequenceInputStream& rStrm,
+ const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const;
+
+private:
+ /** Tries to write the passed value to the passed sheet position. */
+ static void writeItemToSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem );
+ /** Tries to write the value of a shared item to the passed sheet position. */
+ void writeSharedItemToSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nItemIdx ) const;
+
+private:
+ typedef ::std::vector< sal_Int32 > IndexVector;
+
+ PivotCacheItemList maSharedItems; /// All shared items of this field.
+ PivotCacheItemList maGroupItems; /// All group items of this field.
+ IndexVector maDiscreteItems; /// Mapping between group and base items.
+ PCFieldModel maFieldModel; /// Settings for this cache field.
+ PCSharedItemsModel maSharedItemsModel; /// Settings for shared items.
+ PCFieldGroupModel maFieldGroupModel; /// Settings for item grouping.
+};
+
+struct PCDefinitionModel
+{
+ OUString maRelId; /// Relation identifier for cache records fragment.
+ OUString maRefreshedBy; /// Name of user who last refreshed the cache.
+ double mfRefreshedDate; /// Date/time of last refresh.
+ sal_Int32 mnRecords; /// Number of data records in the cache.
+ sal_Int32 mnMissItemsLimit; /// Limit for discarding unused items.
+ bool mbInvalid; /// True = cache needs refresh.
+ bool mbSaveData; /// True = cached item values are present.
+ bool mbRefreshOnLoad; /// True = try to refresh cache on load.
+ bool mbOptimizeMemory; /// True = application may optimize memory usage.
+ bool mbEnableRefresh; /// True = refreshing cache is enabled in UI.
+ bool mbBackgroundQuery; /// True = application queries data asynchronously.
+ bool mbUpgradeOnRefresh; /// True = application may upgrade cache version.
+ bool mbTupleCache; /// True = cache stores OLAP functions.
+ bool mbSupportSubquery; /// True = data source supports subqueries.
+ bool mbSupportDrill; /// True = data source supports drilldown.
+
+ explicit PCDefinitionModel();
+};
+
+struct PCSourceModel
+{
+ sal_Int32 mnSourceType; /// Type of the source data (sheet, consolidation, scenario, external).
+ sal_Int32 mnConnectionId; /// Connection identifier for external data source.
+
+ explicit PCSourceModel();
+};
+
+struct PCWorksheetSourceModel
+{
+ OUString maRelId; /// Relation identifier for an external document URL.
+ OUString maSheet; /// Sheet name for cell range or sheet-local defined names.
+ OUString maDefName; /// Defined name containing a cell range if present.
+ ScRange maRange; /// Source cell range of the data.
+
+ explicit PCWorksheetSourceModel();
+};
+
+class PivotCache : public WorkbookHelper
+{
+public:
+ explicit PivotCache( const WorkbookHelper& rHelper );
+
+ /** Reads pivot cache global settings from the pivotCacheDefinition element. */
+ void importPivotCacheDefinition( const AttributeList& rAttribs );
+ /** Reads cache source settings from the cacheSource element. */
+ void importCacheSource( const AttributeList& rAttribs );
+ /** Reads sheet source settings from the worksheetSource element. */
+ void importWorksheetSource( const AttributeList& rAttribs, const ::oox::core::Relations& rRelations );
+
+ /** Reads pivot cache global settings from the PCDEFINITION record. */
+ void importPCDefinition( SequenceInputStream& rStrm );
+ /** Reads cache source settings from the PCDSOURCE record. */
+ void importPCDSource( SequenceInputStream& rStrm );
+ /** Reads sheet source settings from the PCDSHEETSOURCE record. */
+ void importPCDSheetSource( SequenceInputStream& rStrm, const ::oox::core::Relations& rRelations );
+
+ /** Creates and returns a new pivot cache field. */
+ PivotCacheField& createCacheField();
+ /** Checks validity of source data and creates a dummy data sheet for external sheet sources. */
+ void finalizeImport();
+
+ /** Returns true, if the pivot cache is based on a valid data source, so
+ that pivot tables can be created based on this pivot cache. */
+ bool isValidDataSource() const { return mbValidSource; }
+ /** Returns true, if the pivot cache is based on a dummy sheet created in finalizeImport. */
+ bool isBasedOnDummySheet() const { return mbDummySheet; }
+ /** Returns the internal cell range the cache is based on. */
+ const ScRange&
+ getSourceRange() const { return maSheetSrcModel.maRange; }
+ /** Returns the relation identifier of the pivot cache records fragment. */
+ const OUString& getRecordsRelId() const { return maDefModel.maRelId; }
+
+ /** Returns the cache field with the specified index. */
+ PivotCacheField* getCacheField( sal_Int32 nFieldIdx );
+ const PivotCacheField* getCacheField( sal_Int32 nFieldIdx ) const;
+ /** Returns the source column index of the field with the passed index. */
+ sal_Int32 getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const;
+
+ /** Writes the titles of all source fields into the passed sheet. */
+ void writeSourceHeaderCells( const WorksheetHelper& rSheetHelper ) const;
+ /** Writes a source field item value into the passed sheet. */
+ void writeSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nColIdx, sal_Int32 nRowIdx,
+ const PivotCacheItem& rItem ) const;
+
+ /** Reads a PCRECORD record and writes all item values to the passed sheet. */
+ void importPCRecord( SequenceInputStream& rStrm,
+ const WorksheetHelper& rSheetHelper, sal_Int32 nRowIdx ) const;
+
+private:
+
+ /** Finalizes the pivot cache if it is based on internal sheet data. */
+ void finalizeInternalSheetSource();
+ /** Finalizes the pivot cache if it is based on sheet data of an external spreadsheet document. */
+ void finalizeExternalSheetSource();
+ /** Creates a dummy sheet that will be filled with the pivot cache data. */
+ void prepareSourceDataSheet();
+ /** Checks, if the row index has changed since last call, and initializes the sheet data buffer. */
+ void updateSourceDataRow( const WorksheetHelper& rSheetHelper, sal_Int32 nRow ) const;
+
+private:
+ typedef RefVector< PivotCacheField > PivotCacheFieldVector;
+ typedef ::std::vector< sal_Int32 > IndexVector;
+
+ PivotCacheFieldVector maFields; /// All pivot cache fields.
+ PivotCacheFieldVector maDatabaseFields; /// All cache fields that are based on source data.
+ IndexVector maDatabaseIndexes; /// Database field index for all fields.
+ PCDefinitionModel maDefModel; /// Global pivot cache settings.
+ PCSourceModel maSourceModel; /// Pivot cache source settings.
+ PCWorksheetSourceModel maSheetSrcModel; /// Sheet source data if cache type is sheet.
+ ValueRangeSet maColSpans; /// Column spans used by SheetDataBuffer for optimized cell import.
+ OUString maTargetUrl; /// URL of an external source document.
+ mutable sal_Int32 mnCurrRow; /// Current row index in dummy sheet.
+ bool mbValidSource; /// True = pivot cache is based on supported data source.
+ bool mbDummySheet; /// True = pivot cache is based on a dummy sheet.
+};
+
+class PivotCacheBuffer : public WorkbookHelper
+{
+public:
+ explicit PivotCacheBuffer( const WorkbookHelper& rHelper );
+
+ /** Registers a pivot cache definition fragment. The fragment will be loaded on demand (OOXML/BIFF12 only). */
+ void registerPivotCacheFragment( sal_Int32 nCacheId, const OUString& rFragmentPath );
+
+ /** Imports and stores a pivot cache definition fragment on first call,
+ returns the imported cache on subsequent calls with the same identifier. */
+ PivotCache* importPivotCacheFragment( sal_Int32 nCacheId );
+
+private:
+ /** Creates and returns a new pivot cache object with the passed identifier. */
+ PivotCache& createPivotCache( sal_Int32 nCacheId );
+
+private:
+ typedef ::std::map< sal_Int32, OUString > FragmentPathMap;
+ typedef RefMap< sal_Int32, PivotCache > PivotCacheMap;
+
+ FragmentPathMap maFragmentPaths;
+ PivotCacheMap maCaches;
+ std::vector< sal_Int32 > maCacheIds;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pivotcachefragment.hxx b/sc/source/filter/inc/pivotcachefragment.hxx
new file mode 100644
index 000000000..38f1b77b1
--- /dev/null
+++ b/sc/source/filter/inc/pivotcachefragment.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class PivotCache;
+class PivotCacheField;
+
+class PivotCacheFieldContext : public WorkbookContextBase
+{
+public:
+ explicit PivotCacheFieldContext(
+ WorkbookFragmentBase& rFragment,
+ PivotCacheField& rCacheField );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ PivotCacheField& mrCacheField;
+};
+
+class PivotCacheDefinitionFragment : public WorkbookFragmentBase
+{
+public:
+ explicit PivotCacheDefinitionFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath,
+ PivotCache& rPivotCache );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+
+private:
+ PivotCache& mrPivotCache;
+};
+
+class PivotCacheRecordsFragment : public WorksheetFragmentBase
+{
+public:
+ explicit PivotCacheRecordsFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath,
+ const PivotCache& rPivotCache );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ void startCacheRecord();
+ void importPCRecord( SequenceInputStream& rStrm );
+ void importPCRecordItem( sal_Int32 nRecId, SequenceInputStream& rStrm );
+
+private:
+ const PivotCache& mrPivotCache;
+ sal_Int32 mnColIdx; /// Relative column index in source data.
+ sal_Int32 mnRowIdx; /// Relative row index in source data.
+ bool mbInRecord;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pivottablebuffer.hxx b/sc/source/filter/inc/pivottablebuffer.hxx
new file mode 100644
index 000000000..7b7f22320
--- /dev/null
+++ b/sc/source/filter/inc/pivottablebuffer.hxx
@@ -0,0 +1,404 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "pivotcachebuffer.hxx"
+#include "stylesbuffer.hxx"
+
+namespace com::sun::star {
+ namespace sheet { class XDataPilotDescriptor; }
+ namespace sheet { class XDataPilotField; }
+}
+
+class ScDPObject;
+
+namespace oox::xls {
+
+class PivotTable;
+
+struct PTFieldItemModel
+{
+ sal_Int32 mnCacheItem; /// Index to shared item in pivot cache.
+ sal_Int32 mnType; /// Type of the item.
+ OUString msCaption; /// User caption of the item
+ bool mbShowDetails; /// True = show item details (items of child fields).
+ bool mbHidden; /// True = item is hidden.
+
+ explicit PTFieldItemModel();
+
+ /** Sets item type for BIFF import. */
+ void setBiffType( sal_uInt16 nType );
+};
+
+struct PTFieldModel
+{
+ sal_Int32 mnAxis; /// Axis this field is assigned to (none, row, column, page).
+ sal_Int32 mnNumFmtId; /// Number format for field items.
+ sal_Int32 mnAutoShowItems; /// Number of items (or percent/sum) to be shown in auto show filter.
+ sal_Int32 mnAutoShowRankBy; /// Index of the data field auto show filter is based on.
+ sal_Int32 mnSortType; /// Autosorting type.
+ sal_Int32 mnSortRefField; /// Reference field for autosorting.
+ sal_Int32 mnSortRefItem; /// Item in reference field for autosorting.
+ bool mbDataField; /// True = field appears in data area.
+ bool mbDefaultSubtotal; /// True = show default subtotals.
+ bool mbSumSubtotal; /// True = show sum subtotals.
+ bool mbCountASubtotal; /// True = show count all subtotals.
+ bool mbAverageSubtotal; /// True = show average subtotals.
+ bool mbMaxSubtotal; /// True = show maximum subtotals.
+ bool mbMinSubtotal; /// True = show minimum subtotals.
+ bool mbProductSubtotal; /// True = show product subtotals.
+ bool mbCountSubtotal; /// True = show count numbers subtotals.
+ bool mbStdDevSubtotal; /// True = show standard deviation subtotals.
+ bool mbStdDevPSubtotal; /// True = show standard deviation of population subtotals.
+ bool mbVarSubtotal; /// True = show variance subtotals.
+ bool mbVarPSubtotal; /// True = show variance of population subtotals.
+ bool mbShowAll; /// True = show items without data.
+ bool mbOutline; /// True = show in outline view, false = show in tabular view.
+ bool mbSubtotalTop; /// True = show subtotals on top of items in outline or compact mode.
+ bool mbInsertBlankRow; /// True = insert blank rows after items.
+ bool mbInsertPageBreak; /// True = insert page breaks after items.
+ bool mbAutoShow; /// True = auto show (top 10) filter enabled.
+ bool mbTopAutoShow; /// True = auto show filter shows top entries, false = bottom.
+ bool mbMultiPageItems; /// True = multiple items selectable in page dimension.
+
+ explicit PTFieldModel();
+
+ /** Sets axis type for BIFF import. */
+ void setBiffAxis( sal_uInt8 nAxisFlags );
+};
+
+struct PTPageFieldModel
+{
+ OUString maName; /// Unique name of the page field.
+ sal_Int32 mnField; /// Base pivot field.
+ sal_Int32 mnItem; /// Index of field item that is shown by the page field.
+
+ explicit PTPageFieldModel();
+};
+
+struct PTDataFieldModel
+{
+ OUString maName; /// Name of the data field.
+ sal_Int32 mnField; /// Base pivot field.
+ sal_Int32 mnSubtotal; /// Subtotal aggregation function.
+ sal_Int32 mnShowDataAs; /// Show data as, based on another field.
+ sal_Int32 mnBaseField; /// Base field for 'show data as'.
+ sal_Int32 mnBaseItem; /// Base item for 'show data as'.
+ sal_Int32 mnNumFmtId; /// Number format for the result.
+
+ explicit PTDataFieldModel();
+
+ /** Sets the subtotal aggregation function for BIFF import. */
+ void setBiffSubtotal( sal_Int32 nSubtotal );
+ /** Sets the 'show data as' type for BIFF import. */
+ void setBiffShowDataAs( sal_Int32 nShowDataAs );
+};
+
+class PivotTableField : public WorkbookHelper
+{
+public:
+ explicit PivotTableField( PivotTable& rPivotTable, sal_Int32 nFieldIndex );
+
+ /** Imports pivot field settings from the pivotField element. */
+ void importPivotField( const AttributeList& rAttribs );
+ /** Imports settings of an item in this pivot field from the item element. */
+ void importItem( const AttributeList& rAttribs );
+ /** Imports pivot field reference settings from the reference element. */
+ void importReference( const AttributeList& rAttribs );
+ /** Imports pivot field item reference settings from the x element. */
+ void importReferenceItem( const AttributeList& rAttribs );
+
+ /** Imports pivot field settings from the PTFIELD record. */
+ void importPTField( SequenceInputStream& rStrm );
+ /** Imports settings of an item in this pivot field from the PTFITEM record. */
+ void importPTFItem( SequenceInputStream& rStrm );
+ /** Imports pivot field reference settings from the PTREFERENCE record. */
+ void importPTReference( SequenceInputStream& rStrm );
+ /** Imports pivot field item reference settings from the PTREFERENCEITEM record. */
+ void importPTReferenceItem( SequenceInputStream& rStrm );
+
+ /** Finalizes the field after import, creates grouping and other settings. */
+ void finalizeImport(
+ const css::uno::Reference< css::sheet::XDataPilotDescriptor >& rxDPDesc );
+ /** Finalizes the grouped date field after import. */
+ void finalizeDateGroupingImport(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ sal_Int32 nBaseFieldIdx );
+ /** Finalizes the grouped field after import. */
+ void finalizeParentGroupingImport(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ const PivotCacheField& rBaseCacheField,
+ PivotCacheGroupItemVector& orItemNames );
+ void finalizeImportBasedOnCache(
+ const css::uno::Reference< css::sheet::XDataPilotDescriptor >& rxDPDesc);
+
+ /** Returns the name of the DataPilot field in the fields collection. */
+ const OUString& getDPFieldName() const { return maDPFieldName; }
+
+ /** Converts dimension and other settings for a row field. */
+ void convertRowField();
+ /** Converts dimension and other settings for a column field. */
+ void convertColField();
+ /** Converts dimension and other settings for a hidden field. */
+ void convertHiddenField();
+ /** Converts dimension and other settings for a page field */
+ void convertPageField( const PTPageFieldModel& rPageField );
+ /** Converts dimension and other settings for a data field. */
+ void convertDataField( const PTDataFieldModel& rDataField );
+
+private:
+ /** Converts dimension and other settings for row, column, page, or hidden fields. */
+ css::uno::Reference< css::sheet::XDataPilotField >
+ convertRowColPageField( sal_Int32 nAxis );
+
+private:
+ typedef ::std::vector< PTFieldItemModel > ItemModelVector;
+
+ PivotTable& mrPivotTable; /// The parent pivot table object.
+ ItemModelVector maItems; /// All items of this field.
+ PTFieldModel maModel; /// Pivot field settings.
+ OUString maDPFieldName; /// Name of the field in DataPilot field collection.
+ sal_Int32 mnFieldIndex; /// Zero-based index of this field.
+};
+
+struct PTFilterModel
+{
+ OUString maName; /// Name of the field filter.
+ OUString maDescription; /// Description of the field filter.
+ OUString maStrValue1; /// First string value for label filter.
+ OUString maStrValue2; /// Second string value for label filter.
+ double mfValue; /// Number of items or percent or sum to be shown.
+ sal_Int32 mnField; /// Base pivot field.
+ sal_Int32 mnMemPropField; /// Member property field.
+ sal_Int32 mnType; /// Filter type.
+ sal_Int32 mnEvalOrder; /// Evaluation order index.
+ sal_Int32 mnId; /// Unique identifier.
+ sal_Int32 mnMeasureField; /// Data field for filter calculation.
+ sal_Int32 mnMeasureHier; /// Hierarchy for filter calculation.
+ bool mbTopFilter; /// True = filter shows top entries, false = bottom.
+
+ explicit PTFilterModel();
+};
+
+class PivotTableFilter : public WorkbookHelper
+{
+public:
+ explicit PivotTableFilter( const PivotTable& rPivotTable );
+
+ /** Reads the settings of a field filter from the filter element. */
+ void importFilter( const AttributeList& rAttribs );
+ /** Reads additional settings of a field filter from the top10 element. */
+ void importTop10( const AttributeList& rAttribs );
+
+ /** Reads the settings of a field filter from the PTFILTER record. */
+ void importPTFilter( SequenceInputStream& rStrm );
+ /** Reads additional settings of a field filter from the TOP10FILTER record. */
+ void importTop10Filter( SequenceInputStream& rStrm );
+
+ /** Applies the filter to the associated pivot table field if possible. */
+ void finalizeImport();
+
+private:
+ const PivotTable& mrPivotTable;
+ PTFilterModel maModel;
+};
+
+struct PTDefinitionModel : public AutoFormatModel
+{
+ OUString maName;
+ OUString maDataCaption;
+ OUString maGrandTotalCaption;
+ OUString maRowHeaderCaption;
+ OUString maColHeaderCaption;
+ OUString maErrorCaption;
+ OUString maMissingCaption;
+ OUString maPageStyle;
+ OUString maPivotTableStyle;
+ OUString maVacatedStyle;
+ OUString maTag;
+ sal_Int32 mnCacheId;
+ sal_Int32 mnDataPosition;
+ sal_Int32 mnPageWrap;
+ sal_Int32 mnIndent;
+ sal_Int32 mnChartFormat;
+ bool mbDataOnRows;
+ bool mbShowError;
+ bool mbShowMissing;
+ bool mbShowItems;
+ bool mbDisableFieldList;
+ bool mbShowCalcMembers;
+ bool mbVisualTotals;
+ bool mbShowDrill;
+ bool mbPrintDrill;
+ bool mbEnableDrill;
+ bool mbPreserveFormatting;
+ bool mbUseAutoFormat;
+ bool mbPageOverThenDown;
+ bool mbSubtotalHiddenItems;
+ bool mbRowGrandTotals;
+ bool mbColGrandTotals;
+ bool mbFieldPrintTitles;
+ bool mbItemPrintTitles;
+ bool mbMergeItem;
+ bool mbShowEmptyRow;
+ bool mbShowEmptyCol;
+ bool mbShowHeaders;
+ bool mbFieldListSortAsc;
+ bool mbCustomListSort;
+
+ explicit PTDefinitionModel();
+};
+
+struct PTLocationModel
+{
+ ScRange maRange; /// Target cell range for the pivot table.
+ sal_Int32 mnFirstHeaderRow; /// First row of header cells (relative in pivot table).
+ sal_Int32 mnFirstDataRow; /// First row of data cells (relative in pivot table).
+ sal_Int32 mnFirstDataCol; /// First column of data cells (relative in pivot table).
+ sal_Int32 mnRowPageCount; /// Number of rows in page filter area.
+ sal_Int32 mnColPageCount; /// Number of columns in page filter area.
+
+ explicit PTLocationModel();
+};
+
+class PivotTable : public WorkbookHelper
+{
+public:
+ explicit PivotTable( const WorkbookHelper& rHelper );
+
+ /** Reads global pivot table settings from the pivotTableDefinition element. */
+ void importPivotTableDefinition( const AttributeList& rAttribs );
+ /** Reads the location of the pivot table from the location element. */
+ void importLocation( const AttributeList& rAttribs, sal_Int16 nSheet );
+ /** Reads the index of a field located in the row dimension. */
+ void importRowField( const AttributeList& rAttribs );
+ /** Reads the index of a field located in the column dimension. */
+ void importColField( const AttributeList& rAttribs );
+ /** Reads the settings of a field located in the page dimension from the pageField element. */
+ void importPageField( const AttributeList& rAttribs );
+ /** Reads the settings of a field located in the data dimension from the dataField element. */
+ void importDataField( const AttributeList& rAttribs );
+ /** Puts the attributes to the named grab bag value. */
+ void putToInteropGrabBag(const OUString& sName, const AttributeList& rAttribs);
+
+ /** Reads global pivot table settings from the PTDEFINITION record. */
+ void importPTDefinition( SequenceInputStream& rStrm );
+ /** Reads the location of the pivot table from the PTLOCATION record. */
+ void importPTLocation( SequenceInputStream& rStrm, sal_Int16 nSheet );
+ /** Reads the indexes of all fields located in the row dimension from a PTROWFIELDS record. */
+ void importPTRowFields( SequenceInputStream& rStrm );
+ /** Reads the indexes of all fields located in the column dimension from a PTCOLFIELDS record. */
+ void importPTColFields( SequenceInputStream& rStrm );
+ /** Reads the settings of a field located in the page dimension from the PTPAGEFIELD record. */
+ void importPTPageField( SequenceInputStream& rStrm );
+ /** Reads the settings of a field located in the data dimension from the PTDATAFIELD record. */
+ void importPTDataField( SequenceInputStream& rStrm );
+
+ /** Creates and returns a new pivot table field. */
+ PivotTableField& createTableField();
+ /** Creates and returns a new pivot table filter. */
+ PivotTableFilter& createTableFilter();
+ /** Inserts the pivot table into the sheet. */
+ void finalizeImport();
+ /** Finalizes all fields, finds field names and creates grouping fields. */
+ void finalizeFieldsImport();
+ /** Creates all date group fields for the specified cache field after import. */
+ void finalizeDateGroupingImport(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ sal_Int32 nBaseFieldIdx );
+ /** Creates all grouped fields for the specified cache field after import. */
+ void finalizeParentGroupingImport(
+ const css::uno::Reference< css::sheet::XDataPilotField >& rxBaseDPField,
+ const PivotCacheField& rBaseCacheField,
+ PivotCacheGroupItemVector& orItemNames );
+
+ /** Returns the associated data pilot field for the specified pivot table field. */
+ css::uno::Reference< css::sheet::XDataPilotField >
+ getDataPilotField( const OUString& rFieldName ) const;
+ /** Returns the associated data pilot field for the specified pivot table field. */
+ css::uno::Reference< css::sheet::XDataPilotField >
+ getDataPilotField( sal_Int32 nFieldIdx ) const;
+ /** Returns the data layout field used to store all data fields in row/col dimension. */
+ css::uno::Reference< css::sheet::XDataPilotField >
+ getDataLayoutField() const;
+
+ /** Returns the cache field with the specified index. */
+ PivotCacheField* getCacheField( sal_Int32 nFieldIdx );
+ const PivotCacheField* getCacheField( sal_Int32 nFieldIdx ) const;
+ /** Returns the base cache field of the data field item with the specified index. */
+ const PivotCacheField* getCacheFieldOfDataField( sal_Int32 nDataItemIdx ) const;
+ /** Returns the source column index of the pivot field with the passed index, or -1. */
+ sal_Int32 getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const;
+
+ ScDPObject* getDPObject() { return mpDPObject; }
+
+private:
+ typedef RefVector< PivotTableField > PivotTableFieldVector;
+ typedef RefVector< PivotTableFilter > PivotTableFilterVector;
+ typedef ::std::vector< sal_Int32 > IndexVector;
+ typedef ::std::vector< PTPageFieldModel > PageFieldVector;
+ typedef ::std::vector< PTDataFieldModel > DataFieldVector;
+
+private:
+ /** Returns a pivot table field by its index. */
+ PivotTableField* getTableField( sal_Int32 nFieldIdx );
+
+ /** Reads a field index for the row or column dimension. */
+ static void importField( IndexVector& orFields, const AttributeList& rAttribs );
+ /** Reads an array of field indexes for the row or column dimension. */
+ static void importFields( IndexVector& orFields, SequenceInputStream& rStrm );
+
+private:
+ ScDPObject* mpDPObject;
+ PivotTableFieldVector maFields; /// All pivot table fields.
+ PivotTableField maDataField; /// Data layout field.
+ IndexVector maRowFields; /// Indexes to fields in row dimension.
+ IndexVector maColFields; /// Indexes to fields in column dimension.
+ PageFieldVector maPageFields; /// Settings for all fields in page dimension.
+ DataFieldVector maDataFields; /// Settings for all fields in data area.
+ PivotTableFilterVector maFilters; /// All field filters.
+ PTDefinitionModel maDefModel; /// Global pivot table settings.
+ PTLocationModel maLocationModel; /// Location settings of the pivot table.
+ PivotCache* mpPivotCache; /// The pivot cache this table is based on.
+ css::uno::Reference< css::sheet::XDataPilotDescriptor >
+ mxDPDescriptor; /// Descriptor of the DataPilot object.
+ std::map<OUString, css::uno::Any> maInteropGrabBag;
+
+};
+
+class PivotTableBuffer : public WorkbookHelper
+{
+public:
+ explicit PivotTableBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates and returns a new pivot table. */
+ PivotTable& createPivotTable();
+
+ /** Inserts all pivot tables into the sheet. */
+ void finalizeImport();
+
+private:
+ typedef RefVector< PivotTable > PivotTableVector;
+ PivotTableVector maTables;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/pivottablefragment.hxx b/sc/source/filter/inc/pivottablefragment.hxx
new file mode 100644
index 000000000..c6c82f6b7
--- /dev/null
+++ b/sc/source/filter/inc/pivottablefragment.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "worksheethelper.hxx"
+
+namespace oox::xls {
+
+class PivotTable;
+class PivotTableField;
+class PivotTableFilter;
+
+class PivotTableFieldContext : public WorksheetContextBase
+{
+public:
+ explicit PivotTableFieldContext(
+ WorksheetFragmentBase& rFragment,
+ PivotTableField& rTableField );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ PivotTableField& mrTableField;
+};
+
+class PivotTableFilterContext : public WorksheetContextBase
+{
+public:
+ explicit PivotTableFilterContext(
+ WorksheetFragmentBase& rFragment,
+ PivotTableFilter& rTableFilter );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ PivotTableFilter& mrTableFilter;
+};
+
+class PivotTableFragment : public WorksheetFragmentBase
+{
+public:
+ explicit PivotTableFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ PivotTable& mrPivotTable;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/qpro.hxx b/sc/source/filter/inc/qpro.hxx
new file mode 100644
index 000000000..0a4f78062
--- /dev/null
+++ b/sc/source/filter/inc/qpro.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <sal/config.h>
+#include <vcl/errcode.hxx>
+#include <types.hxx>
+
+class ScDocument;
+class SvStream;
+class ScQProStyle;
+
+// Stream wrapper class
+class ScQProReader
+{
+ sal_uInt16 mnId;
+ sal_uInt16 mnLength;
+ sal_uInt32 mnOffset;
+ SvStream* mpStream;
+ bool mbEndOfFile;
+ const SCTAB mnMaxTab;
+
+public:
+ ScQProReader(SvStream* pStream);
+ ~ScQProReader();
+
+ bool recordsLeft();
+ void SetEof(bool bValue) { mbEndOfFile = bValue; }
+ bool nextRecord();
+ sal_uInt16 getId() const { return mnId; }
+ sal_uInt16 getLength() const { return mnLength; }
+ OUString readString(sal_uInt16 nLength);
+
+ ErrCode parse(ScDocument& rDoc);
+ ErrCode import(ScDocument& rDoc); //parse + CalcAfterLoad
+ ErrCode readSheet(SCTAB nTab, ScDocument& rDoc, ScQProStyle* pStyle);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/qproform.hxx b/sc/source/filter/inc/qproform.hxx
new file mode 100644
index 000000000..bedb5b311
--- /dev/null
+++ b/sc/source/filter/inc/qproform.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+#include "formel.hxx"
+
+typedef OpCode DefTokenId;
+
+enum QPRO_FUNC_TYPE
+{
+ FT_Return,
+ FT_FuncFix0,
+ FT_FuncFix1,
+ FT_FuncFix2,
+ FT_FuncFix3,
+ FT_FuncFix4,
+ FT_FuncFix5,
+ FT_FuncFix6,
+ FT_FuncVar,
+ FT_DLL,
+ FT_Neg,
+ FT_Op,
+ FT_NotImpl,
+ FT_ConstFloat,
+ FT_Range,
+ FT_Braces,
+ FT_ConstInt,
+ FT_ConstString,
+ FT_NOP,
+ FT_Cref
+};
+
+class QProToSc : public ConverterBase
+{
+private:
+ TokenId mnAddToken;
+ SvStream& maIn;
+
+public:
+ static const size_t nBufSize = 256;
+ QProToSc(SvStream& aStr, svl::SharedStringPool& rSPool, const ScAddress& rRefPos);
+ ConvErr Convert(const ScDocument& rDoc, std::unique_ptr<ScTokenArray>& pArray);
+ void DoFunc(DefTokenId eOc, sal_uInt16 nArgs, const char* pExtString);
+ void ReadSRD(const ScDocument& rDoc, ScSingleRefData& rR, sal_Int8 nPage, sal_Int8 nCol,
+ sal_uInt16 rRel);
+ void IncToken(TokenId& aParam);
+ static DefTokenId IndexToToken(sal_uInt16 nToken);
+ static QPRO_FUNC_TYPE IndexToType(sal_uInt8 nToken);
+ static DefTokenId IndexToDLLId(sal_uInt16 nIndex);
+ static const char* getString(sal_uInt8 nIndex);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/qprostyle.hxx b/sc/source/filter/inc/qprostyle.hxx
new file mode 100644
index 000000000..708b0ae1d
--- /dev/null
+++ b/sc/source/filter/inc/qprostyle.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <sal/config.h>
+#include <types.hxx>
+
+class ScDocument;
+
+class ScQProStyle
+{
+ enum limits { maxsize = 256 };
+ sal_uInt8 maAlign[ maxsize ] = {};
+ sal_uInt8 maFont[ maxsize ] = {};
+ sal_uInt16 maFontRecord[ maxsize ] = {};
+ sal_uInt16 maFontHeight[ maxsize ] = {};
+ OUString maFontType[ maxsize ];
+
+ public:
+ ScQProStyle() = default;
+ void SetFormat( ScDocument *pDoc, sal_uInt8 nCol, sal_uInt16 nRow, SCTAB nTab, sal_uInt16 nStyle );
+ void setFontRecord(sal_uInt16 nIndex, sal_uInt16 nData, sal_uInt16 nPtSize)
+ {
+ if (nIndex < maxsize)
+ {
+ maFontRecord[ nIndex ] = nData;
+ maFontHeight[ nIndex ] = nPtSize;
+ }
+ }
+ void setFontType( sal_uInt16 nIndex, const OUString &aLabel )
+ { if (nIndex < maxsize) maFontType[ nIndex ] = aLabel; }
+ void setAlign( sal_uInt16 nIndex, sal_uInt8 nData )
+ { if (nIndex < maxsize) maAlign[ nIndex ] = nData; }
+ void setFont( sal_uInt16 nIndex, sal_uInt8 nData )
+ { if (nIndex < maxsize) maFont[ nIndex ] = nData; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/querytablebuffer.hxx b/sc/source/filter/inc/querytablebuffer.hxx
new file mode 100644
index 000000000..4bba7a68c
--- /dev/null
+++ b/sc/source/filter/inc/querytablebuffer.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "stylesbuffer.hxx"
+#include "worksheethelper.hxx"
+
+namespace oox::xls {
+
+struct QueryTableModel : public AutoFormatModel
+{
+ OUString maDefName; /// Defined name containing the target cell range.
+ sal_Int32 mnConnId; /// Identifier of the external connection used to query the data.
+ sal_Int32 mnGrowShrinkType; /// Behaviour when source data size changes.
+ bool mbHeaders; /// True = source data contains a header row.
+ bool mbRowNumbers; /// True = first column contains row numbers.
+ bool mbDisableRefresh; /// True = refreshing data disabled.
+ bool mbBackground; /// True = refresh asynchronously.
+ bool mbFirstBackground; /// True = first background refresh not yet finished.
+ bool mbRefreshOnLoad; /// True = refresh table after import.
+ bool mbFillFormulas; /// True = expand formulas next to range when source data grows.
+ bool mbRemoveDataOnSave; /// True = remove querried data before saving.
+ bool mbDisableEdit; /// True = connection locked for editing.
+ bool mbPreserveFormat; /// True = use existing formatting for new rows.
+ bool mbAdjustColWidth; /// True = adjust column widths after refresh.
+ bool mbIntermediate; /// True = query table defined but not built yet.
+
+ explicit QueryTableModel();
+};
+
+class QueryTable : public WorksheetHelper
+{
+public:
+ explicit QueryTable( const WorksheetHelper& rHelper );
+
+ /** Imports query table settings from the queryTable element. */
+ void importQueryTable( const AttributeList& rAttribs );
+ /** Imports query table settings from the QUERYTABLE record. */
+ void importQueryTable( SequenceInputStream& rStrm );
+
+ /** Inserts a web query into the sheet. */
+ void finalizeImport();
+
+private:
+ QueryTableModel maModel;
+};
+
+class QueryTableBuffer : public WorksheetHelper
+{
+public:
+ explicit QueryTableBuffer( const WorksheetHelper& rHelper );
+
+ /** Creates a new query table and stores it into the internal vector. */
+ QueryTable& createQueryTable();
+
+ /** Inserts all web queries into the sheet. */
+ void finalizeImport();
+
+private:
+ typedef RefVector< QueryTable > QueryTableVector;
+ QueryTableVector maQueryTables;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/querytablefragment.hxx b/sc/source/filter/inc/querytablefragment.hxx
new file mode 100644
index 000000000..281826178
--- /dev/null
+++ b/sc/source/filter/inc/querytablefragment.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class QueryTable;
+
+class QueryTableFragment : public WorksheetFragmentBase
+{
+public:
+ explicit QueryTableFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ QueryTable& mrQueryTable;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/revisionfragment.hxx b/sc/source/filter/inc/revisionfragment.hxx
new file mode 100644
index 000000000..30a953160
--- /dev/null
+++ b/sc/source/filter/inc/revisionfragment.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include <memory>
+
+class ScChangeTrack;
+
+namespace oox::xls {
+
+class RevisionHeadersFragment : public WorkbookFragmentBase
+{
+ struct Impl;
+ std::unique_ptr<Impl> mpImpl;
+
+public:
+ explicit RevisionHeadersFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath );
+
+ virtual ~RevisionHeadersFragment() override;
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual void finalizeImport() override;
+
+private:
+ void importHeader( const AttributeList& rAttribs );
+};
+
+class RevisionLogFragment : public WorkbookFragmentBase
+{
+ struct Impl;
+ std::unique_ptr<Impl> mpImpl;
+
+public:
+ explicit RevisionLogFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack );
+
+ virtual ~RevisionLogFragment() override;
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual void finalizeImport() override;
+
+private:
+ void importCommon( const AttributeList& rAttribs );
+ void importRcc( const AttributeList& rAttribs );
+ void importRrc( const AttributeList& rAttribs );
+
+ void pushRevision();
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sc/source/filter/inc/richstring.hxx b/sc/source/filter/inc/richstring.hxx
new file mode 100644
index 000000000..71b393c1d
--- /dev/null
+++ b/sc/source/filter/inc/richstring.hxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/helper/refvector.hxx>
+#include "stylesbuffer.hxx"
+
+class EditTextObject;
+struct ESelection;
+class ScEditEngineDefaulter;
+
+namespace com::sun::star {
+ namespace text { class XText; }
+}
+
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+/** Contains text data and font attributes for a part of a rich formatted string. */
+class RichStringPortion : public WorkbookHelper
+{
+public:
+ explicit RichStringPortion( const WorkbookHelper& rHelper );
+
+ /** Sets text data for this portion. */
+ void setText( const OUString& rText );
+ /** Creates and returns a new font formatting object. */
+ FontRef const & createFont();
+ /** Links this portion to a font object from the global font list. */
+ void setFontId( sal_Int32 nFontId );
+
+ /** Final processing after import of all strings. */
+ void finalizeImport();
+
+ /** Returns the text data of this portion. */
+ const OUString& getText() const { return maText; }
+ /** Returns true, if the portion contains font formatting. */
+ bool hasFont() const { return bool(mxFont); }
+
+ /** Converts the portion and replaces or appends to the passed XText. */
+ void convert(
+ const css::uno::Reference< css::text::XText >& rxText,
+ bool bReplace );
+ void convert( ScEditEngineDefaulter& rEE, ESelection& rSelection, const oox::xls::Font* pFont );
+
+ void writeFontProperties(
+ const css::uno::Reference< css::text::XText >& rxText ) const;
+
+private:
+ OUString maText; /// Portion text.
+ FontRef mxFont; /// Embedded portion font, may be empty.
+ sal_Int32 mnFontId; /// Link to global font list.
+ bool mbConverted; /// Without repeatedly convert
+};
+
+typedef std::shared_ptr< RichStringPortion > RichStringPortionRef;
+
+/** Represents a position in a rich-string containing current font identifier.
+
+ This object stores the position of a formatted character in a rich-string
+ and the identifier of a font from the global font list used to format this
+ and the following characters. Used in binary filters only.
+ */
+struct FontPortionModel
+{
+ sal_Int32 mnPos; /// First character in the string.
+ sal_Int32 mnFontId; /// Font identifier for the next characters.
+
+ explicit FontPortionModel() : mnPos( 0 ), mnFontId( -1 ) {}
+ explicit FontPortionModel( sal_Int32 nPos ) : mnPos( nPos ), mnFontId( -1 ) {}
+
+ void read( SequenceInputStream& rStrm );
+};
+
+/** A vector with all font portions in a rich-string. */
+class FontPortionModelList {
+ ::std::vector< FontPortionModel > mvModels;
+
+public:
+ explicit FontPortionModelList() : mvModels() {}
+
+ bool empty() const { return mvModels.empty(); }
+
+ const FontPortionModel& back() const { return mvModels.back(); }
+ const FontPortionModel& front() const { return mvModels.front(); }
+
+ void push_back(const FontPortionModel& rModel) { mvModels.push_back(rModel); }
+
+ void insert(::std::vector< FontPortionModel >::iterator it,
+ const FontPortionModel& rModel)
+ { mvModels.insert(it, rModel); }
+
+ ::std::vector< FontPortionModel >::iterator begin() { return mvModels.begin(); }
+
+ /** Appends a rich-string font identifier. */
+ void appendPortion( const FontPortionModel& rPortion );
+ /** Reads count and font identifiers from the passed stream. */
+ void importPortions( SequenceInputStream& rStrm );
+};
+
+struct PhoneticDataModel
+{
+ sal_Int32 mnFontId; /// Font identifier for text formatting.
+ sal_Int32 mnType; /// Phonetic text type.
+ sal_Int32 mnAlignment; /// Phonetic portion alignment.
+
+ explicit PhoneticDataModel();
+
+ /** Sets the passed data from binary import. */
+ void setBiffData( sal_Int32 nType, sal_Int32 nAlignment );
+};
+
+class PhoneticSettings : public WorkbookHelper
+{
+public:
+ explicit PhoneticSettings( const WorkbookHelper& rHelper );
+
+ /** Imports phonetic settings from the phoneticPr element. */
+ void importPhoneticPr( const AttributeList& rAttribs );
+ /** Imports phonetic settings from the PHONETICPR record. */
+ void importPhoneticPr( SequenceInputStream& rStrm );
+
+ /** Imports phonetic settings from a rich string. */
+ void importStringData( SequenceInputStream& rStrm );
+
+private:
+ PhoneticDataModel maModel;
+};
+
+/** Contains text data and positioning information for a phonetic text portion. */
+class RichStringPhonetic : public WorkbookHelper
+{
+public:
+ explicit RichStringPhonetic( const WorkbookHelper& rHelper );
+
+ /** Sets text data for this phonetic portion. */
+ void setText( const OUString& rText );
+ /** Imports attributes of a phonetic run (rPh element). */
+ void importPhoneticRun( const AttributeList& rAttribs );
+ /** Sets the associated range in base text for this phonetic portion. */
+ void setBaseRange( sal_Int32 nBasePos, sal_Int32 nBaseEnd );
+
+private:
+ OUString maText; /// Portion text.
+ sal_Int32 mnBasePos; /// Start position in base text.
+ sal_Int32 mnBaseEnd; /// One-past-end position in base text.
+};
+
+typedef std::shared_ptr< RichStringPhonetic > RichStringPhoneticRef;
+
+/** Represents a phonetic text portion in a rich-string with phonetic text.
+ Used in binary filters only. */
+struct PhoneticPortionModel
+{
+ sal_Int32 mnPos; /// First character in phonetic text.
+ sal_Int32 mnBasePos; /// First character in base text.
+ sal_Int32 mnBaseLen; /// Number of characters in base text.
+
+ explicit PhoneticPortionModel() : mnPos( -1 ), mnBasePos( -1 ), mnBaseLen( 0 ) {}
+ explicit PhoneticPortionModel( sal_Int32 nPos, sal_Int32 nBasePos, sal_Int32 nBaseLen ) :
+ mnPos( nPos ), mnBasePos( nBasePos ), mnBaseLen( nBaseLen ) {}
+
+ void read( SequenceInputStream& rStrm );
+};
+
+/** A vector with all phonetic portions in a rich-string. */
+class PhoneticPortionModelList
+{
+public:
+ explicit PhoneticPortionModelList() : mvModels() {}
+
+ bool empty() const { return mvModels.empty(); }
+
+ const PhoneticPortionModel& back() const { return mvModels.back(); }
+
+ void push_back(const PhoneticPortionModel& rModel) { mvModels.push_back(rModel); }
+
+ ::std::vector< PhoneticPortionModel >::const_iterator begin() const { return mvModels.begin(); }
+
+ /** Appends a rich-string phonetic portion. */
+ void appendPortion( const PhoneticPortionModel& rPortion );
+ /** Reads all phonetic portions from the passed stream. */
+ void importPortions( SequenceInputStream& rStrm );
+
+private:
+ ::std::vector< PhoneticPortionModel > mvModels;
+};
+
+/** Contains string data and a list of formatting runs for a rich formatted string. */
+class RichString : public WorkbookHelper
+{
+public:
+ explicit RichString( const WorkbookHelper& rHelper );
+
+ /** Appends and returns a portion object for a plain string (t element). */
+ RichStringPortionRef importText();
+ /** Appends and returns a portion object for a new formatting run (r element). */
+ RichStringPortionRef importRun();
+ /** Appends and returns a phonetic text object for a new phonetic run (rPh element). */
+ RichStringPhoneticRef importPhoneticRun( const AttributeList& rAttribs );
+ /** Imports phonetic settings from the rPhoneticPr element. */
+ void importPhoneticPr( const AttributeList& rAttribs );
+
+ /** Imports a Unicode rich-string from the passed record stream. */
+ void importString( SequenceInputStream& rStrm, bool bRich );
+
+ /** Final processing after import of all strings. */
+ void finalizeImport();
+
+ /** Tries to extract a plain string from this object. Returns the string,
+ if there is only one unformatted portion. */
+ bool extractPlainString(
+ OUString& orString,
+ const oox::xls::Font* pFirstPortionFont ) const;
+
+ /** Converts the string and writes it into the passed XText, replace old contents of the text object,.
+ @param rxText The XText interface of the target object.
+ */
+ void convert( const css::uno::Reference< css::text::XText >& rxText ) const;
+ std::unique_ptr<EditTextObject> convert( ScEditEngineDefaulter& rEE, const oox::xls::Font* pFont ) const;
+
+private:
+ /** Creates, appends, and returns a new empty string portion. */
+ RichStringPortionRef createPortion();
+ /** Creates, appends, and returns a new empty phonetic text portion. */
+ RichStringPhoneticRef createPhonetic();
+
+ /** Create base text portions from the passed string and character formatting. */
+ void createTextPortions( const OUString& rText, FontPortionModelList& rPortions );
+ /** Create phonetic text portions from the passed string and portion data. */
+ void createPhoneticPortions( const OUString& rText, PhoneticPortionModelList& rPortions, sal_Int32 nBaseLen );
+
+private:
+ typedef RefVector< RichStringPortion > PortionVector;
+ typedef RefVector< RichStringPhonetic > PhoneticVector;
+
+ PortionVector maTextPortions; /// String portions with font data.
+ PhoneticSettings maPhonSettings; /// Phonetic settings for this string.
+ PhoneticVector maPhonPortions; /// Phonetic text portions.
+};
+
+typedef std::shared_ptr< RichString > RichStringRef;
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/richstringcontext.hxx b/sc/source/filter/inc/richstringcontext.hxx
new file mode 100644
index 000000000..83e2fa4cc
--- /dev/null
+++ b/sc/source/filter/inc/richstringcontext.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "richstring.hxx"
+#include <osl/diagnose.h>
+
+namespace oox::xls {
+
+class RichStringContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit RichStringContext( ParentType& rParent, RichStringRef xString );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+private:
+ RichStringRef mxString; /// Processed string.
+ RichStringPortionRef mxPortion; /// Processed portion in the string.
+ RichStringPhoneticRef mxPhonetic; /// Processed phonetic text portion.
+};
+
+template< typename ParentType >
+RichStringContext::RichStringContext( ParentType& rParent, RichStringRef xString ) :
+ WorkbookContextBase( rParent ),
+ mxString( xString )
+{
+ mbEnableTrimSpace = false;
+ OSL_ENSURE( mxString, "RichStringContext::RichStringContext - missing string object" );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/root.hxx b/sc/source/filter/inc/root.hxx
new file mode 100644
index 000000000..0e4697dcd
--- /dev/null
+++ b/sc/source/filter/inc/root.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "flttypes.hxx"
+#include <memory>
+
+class ScRangeName;
+
+class RangeNameBufferWK3;
+class SharedFormulaBuffer;
+class ExtNameBuff;
+class ExtSheetBuffer;
+class ExcelToSc;
+
+class XclImpColRowSettings;
+class XclImpAutoFilterBuffer;
+class XclExpChTrTabId;
+class XclExpUserBViewList;
+
+class XclImpRoot;
+class XclExpRoot;
+
+// Excel Imp~/Exp~ -
+
+struct RootData // -> incarnation in each case in the ImportExcel object!
+{
+ BiffTyp eDateiTyp; // fine differentiation
+ std::unique_ptr<ExtSheetBuffer> pExtSheetBuff;
+ std::unique_ptr<SharedFormulaBuffer> pShrfmlaBuff;
+ std::unique_ptr<ExtNameBuff> pExtNameBuff;
+ ExcelToSc* pFmlaConverter;
+ XclImpColRowSettings* pColRowBuff; // col/row settings 1 table
+
+ // Biff8
+ std::unique_ptr<XclImpAutoFilterBuffer> pAutoFilterBuffer; // ranges for autofilter and advanced filter
+
+ // extensions for export
+ XclExpChTrTabId* pTabId; // pointer to rec list, do not destroy
+ XclExpUserBViewList* pUserBViewList; // pointer to rec list, do not destroy
+
+ XclImpRoot* pIR;
+ XclExpRoot* pER;
+
+ RootData(); // -> exctools.cxx
+ ~RootData(); // -> exctools.cxx
+};
+
+class ExcRoot
+{
+protected:
+ RootData* pExcRoot;
+ ExcRoot( RootData* pNexExcRoot ) : pExcRoot( pNexExcRoot ) {}
+ ExcRoot( const ExcRoot& rCopy ) : pExcRoot( rCopy.pExcRoot ) {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/rtfexp.hxx b/sc/source/filter/inc/rtfexp.hxx
new file mode 100644
index 000000000..0d5e69f79
--- /dev/null
+++ b/sc/source/filter/inc/rtfexp.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "expbase.hxx"
+#include <tools/solar.h>
+
+class ScRTFExport : public ScExportBase
+{
+ std::unique_ptr<sal_uLong[]> pCellX; // cumulative range in a table
+
+ void WriteTab( SCTAB nTab );
+ void WriteRow( SCTAB nTab, SCROW nRow );
+ void WriteCell( SCTAB nTab, SCROW nRow, SCCOL nCol );
+
+public:
+
+ ScRTFExport( SvStream&, ScDocument*, const ScRange& );
+ virtual ~ScRTFExport() override;
+
+ void Write();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/rtfimp.hxx b/sc/source/filter/inc/rtfimp.hxx
new file mode 100644
index 000000000..ee9fd248b
--- /dev/null
+++ b/sc/source/filter/inc/rtfimp.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "eeimport.hxx"
+
+class ScRTFImport : public ScEEImport
+{
+public:
+ ScRTFImport(ScDocument* pDoc, const ScRange& rRange);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/rtfparse.hxx b/sc/source/filter/inc/rtfparse.hxx
new file mode 100644
index 000000000..ac0f81e14
--- /dev/null
+++ b/sc/source/filter/inc/rtfparse.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "eeparser.hxx"
+
+#include <vector>
+#include <memory>
+#include <o3tl/sorted_vector.hxx>
+
+struct ScRTFCellDefault
+{
+ SfxItemSet aItemSet;
+ SCCOL nCol;
+ sal_uInt16 nTwips; // right border of cell
+ SCCOL nColOverlap; // MergeCell if >1, merged cells if 0
+
+ ScRTFCellDefault( SfxItemPool* pPool )
+ : aItemSet(*pPool)
+ , nCol(0)
+ , nTwips(0)
+ , nColOverlap(1)
+ {
+ }
+};
+
+class ScRTFColTwips : public o3tl::sorted_vector<sal_uLong> {};
+
+class EditEngine;
+
+class ScRTFParser : public ScEEParser
+{
+private:
+ typedef std::vector< std::unique_ptr<ScRTFCellDefault> > DefaultList;
+
+ DefaultList maDefaultList;
+ size_t mnCurPos;
+
+ ScRTFColTwips aColTwips;
+ std::unique_ptr<ScRTFCellDefault> pInsDefault;
+ ScRTFCellDefault* pActDefault;
+ ScRTFCellDefault* pDefMerge;
+ sal_uLong nStartAdjust;
+ sal_uInt16 nLastWidth;
+ bool bNewDef;
+
+ DECL_LINK( RTFImportHdl, RtfImportInfo&, void );
+ inline void NextRow();
+ void EntryEnd( ScEEParseEntry*, const ESelection& );
+ void ProcToken( RtfImportInfo* );
+ void ColAdjust();
+ bool SeekTwips( sal_uInt16 nTwips, SCCOL* pCol );
+ void NewCellRow();
+
+public:
+ ScRTFParser( EditEngine* );
+ virtual ~ScRTFParser() override;
+ virtual ErrCode Read( SvStream&, const OUString& rBaseURL ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/scenariobuffer.hxx b/sc/source/filter/inc/scenariobuffer.hxx
new file mode 100644
index 000000000..97ef44daa
--- /dev/null
+++ b/sc/source/filter/inc/scenariobuffer.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/helper/refmap.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace oox::xls {
+
+struct ScenarioCellModel
+{
+ ScAddress maPos;
+ OUString maValue;
+ sal_Int32 mnNumFmtId;
+ bool mbDeleted;
+
+ explicit ScenarioCellModel();
+};
+
+struct ScenarioModel
+{
+ OUString maName; /// Name of the scenario.
+ OUString maComment; /// Comment.
+ OUString maUser; /// Name of user created the scenario.
+ bool mbLocked; /// True = input cell values locked.
+ bool mbHidden; /// True = scenario is hidden.
+ bool mbActive;
+
+ explicit ScenarioModel();
+};
+
+class Scenario : public WorkbookHelper
+{
+public:
+ explicit Scenario( const WorkbookHelper& rHelper, sal_Int16 nSheet, bool bIsActive );
+
+ /** Imports a scenario definition from a scenario element. */
+ void importScenario( const AttributeList& rAttribs );
+ /** Imports a new cell for this scenario from an inputCells element. */
+ void importInputCells( const AttributeList& rAttribs );
+
+ /** Imports a scenario definition from a SCENARIO record. */
+ void importScenario( SequenceInputStream& rStrm );
+ /** Imports a new cell for this scenario from an INPUTCELLS record. */
+ void importInputCells( SequenceInputStream& rStrm );
+
+ /** Creates the scenario in the Calc document. */
+ void finalizeImport();
+
+private:
+ std::vector< ScenarioCellModel > maCells; /// Scenario cells.
+ ScenarioModel maModel; /// Scenario model data.
+ sal_Int16 mnSheet; /// Index of the sheet this scenario is based on.
+};
+
+struct SheetScenariosModel
+{
+ sal_Int32 mnCurrent; /// Selected scenario.
+ sal_Int32 mnShown; /// Visible scenario.
+
+ explicit SheetScenariosModel();
+};
+
+class SheetScenarios : public WorkbookHelper
+{
+public:
+ explicit SheetScenarios( const WorkbookHelper& rHelper, sal_Int16 nSheet );
+
+ /** Imports sheet scenario settings from a scenarios element. */
+ void importScenarios( const AttributeList& rAttribs );
+ /** Imports sheet scenario settings from a SCENARIOS record. */
+ void importScenarios( SequenceInputStream& rStrm );
+
+ /** Creates and returns a new scenario in this collection. */
+ Scenario& createScenario();
+
+ /** Creates all scenarios in the Calc sheet. */
+ void finalizeImport();
+
+private:
+ typedef RefVector< Scenario > ScenarioVector;
+ ScenarioVector maScenarios;
+ SheetScenariosModel maModel;
+ sal_Int16 mnSheet;
+};
+
+class ScenarioBuffer : public WorkbookHelper
+{
+public:
+ explicit ScenarioBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates and returns a scenario collection for the passed sheet. */
+ SheetScenarios& createSheetScenarios( sal_Int16 nSheet );
+
+ /** Creates all scenarios in the Calc document. */
+ void finalizeImport();
+
+private:
+ typedef RefMap< sal_Int16, SheetScenarios, ::std::greater< sal_Int16 > > SheetScenariosMap;
+ SheetScenariosMap maSheetScenarios;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/scenariocontext.hxx b/sc/source/filter/inc/scenariocontext.hxx
new file mode 100644
index 000000000..80368810d
--- /dev/null
+++ b/sc/source/filter/inc/scenariocontext.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class Scenario;
+class SheetScenarios;
+
+class ScenarioContext : public WorksheetContextBase
+{
+public:
+ explicit ScenarioContext( WorksheetContextBase& rParent, SheetScenarios& rSheetScenarios );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ Scenario& mrScenario;
+};
+
+class ScenariosContext : public WorksheetContextBase
+{
+public:
+ explicit ScenariosContext( WorksheetFragmentBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ SheetScenarios& mrSheetScenarios;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/scfobj.hxx b/sc/source/filter/inc/scfobj.hxx
new file mode 100644
index 000000000..1380ed1ee
--- /dev/null
+++ b/sc/source/filter/inc/scfobj.hxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+
+class ScDocument;
+namespace tools { class Rectangle; }
+
+class Sc10InsertObject
+{
+public:
+ static void InsertChart( ScDocument* pDoc, SCTAB nDestTab, const tools::Rectangle& rRect,
+ SCTAB nSrcTab, sal_uInt16 nX1, sal_uInt16 nY1, sal_uInt16 nX2, sal_uInt16 nY2 );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/scmem.h b/sc/source/filter/inc/scmem.h
new file mode 100644
index 000000000..b54b7d850
--- /dev/null
+++ b/sc/source/filter/inc/scmem.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+struct LotusContext;
+
+bool MemNew(LotusContext& rContext);
+void MemDelete(LotusContext& rContext);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sharedformulagroups.hxx b/sc/source/filter/inc/sharedformulagroups.hxx
new file mode 100644
index 000000000..e8b049285
--- /dev/null
+++ b/sc/source/filter/inc/sharedformulagroups.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <tokenarray.hxx>
+#include <memory>
+#include <map>
+class ScTokenArray;
+
+namespace sc
+{
+class SharedFormulaGroupEntry
+{
+private:
+ std::unique_ptr<ScTokenArray> mpArray;
+ ScAddress maOrigin;
+
+public:
+ SharedFormulaGroupEntry(std::unique_ptr<ScTokenArray> pArray, const ScAddress& rOrigin)
+ : mpArray(std::move(pArray))
+ , maOrigin(rOrigin)
+ {
+ }
+
+ const ScTokenArray* getTokenArray() const { return mpArray.get(); }
+ const ScAddress& getOrigin() const { return maOrigin; }
+};
+
+class SharedFormulaGroups
+{
+private:
+ typedef std::map<size_t, SharedFormulaGroupEntry> StoreType;
+ StoreType m_Store;
+
+public:
+ void set(size_t nSharedId, std::unique_ptr<ScTokenArray> pArray);
+ void set(size_t nSharedId, std::unique_ptr<ScTokenArray> pArray, const ScAddress& rOrigin);
+ const ScTokenArray* get(size_t nSharedId) const;
+ const SharedFormulaGroupEntry* getEntry(size_t nSharedId) const;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sharedstringsbuffer.hxx b/sc/source/filter/inc/sharedstringsbuffer.hxx
new file mode 100644
index 000000000..f5cc18bca
--- /dev/null
+++ b/sc/source/filter/inc/sharedstringsbuffer.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "richstring.hxx"
+
+namespace oox::xls {
+
+/** Collects all strings from the shared strings substream. */
+class SharedStringsBuffer : public WorkbookHelper
+{
+public:
+ explicit SharedStringsBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates and returns a new string entry. */
+ RichStringRef createRichString();
+
+ /** Final processing after import of all strings. */
+ void finalizeImport();
+
+ /** Returns the specified string. */
+ RichStringRef getString( sal_Int32 nStringId ) const;
+
+private:
+ typedef RefVector< RichString > StringVector;
+ StringVector maStrings;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sharedstringsfragment.hxx b/sc/source/filter/inc/sharedstringsfragment.hxx
new file mode 100644
index 000000000..791832397
--- /dev/null
+++ b/sc/source/filter/inc/sharedstringsfragment.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class SharedStringsFragment : public WorkbookFragmentBase
+{
+public:
+ explicit SharedStringsFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sheetdatabuffer.hxx b/sc/source/filter/inc/sheetdatabuffer.hxx
new file mode 100644
index 000000000..a6bec5a55
--- /dev/null
+++ b/sc/source/filter/inc/sheetdatabuffer.hxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <map>
+#include <o3tl/sorted_vector.hxx>
+
+#include "richstring.hxx"
+#include "worksheethelper.hxx"
+#include "addressconverter.hxx"
+
+namespace com::sun::star {
+ namespace util { struct DateTime; }
+}
+
+namespace oox::xls {
+
+/** Stores basic data about cell values and formatting. */
+struct CellModel
+{
+ ScAddress maCellAddr; /// The address of the current cell.
+ sal_Int32 mnCellType; /// Data type of the cell value.
+ sal_Int32 mnXfId; /// XF (cell formatting) identifier.
+ bool mbShowPhonetic; /// True = show phonetic text.
+
+ explicit CellModel();
+};
+
+/** Stores data about cell formulas. */
+struct CellFormulaModel
+{
+ ScRange maFormulaRef; /// Formula range for array/shared formulas and data tables.
+ sal_Int32 mnFormulaType; /// Type of the formula (regular, array, shared, table).
+ sal_Int32 mnSharedId; /// Identifier of a shared formula (OOXML only).
+
+ explicit CellFormulaModel();
+
+ /** Returns true, if the passed cell address is valid for an array formula. */
+ bool isValidArrayRef( const ScAddress& rCellAddr );
+ /** Returns true, if the passed cell address is valid for a shared formula. */
+ bool isValidSharedRef( const ScAddress& rCellAddr );
+};
+
+/** Stores data about table operations. */
+struct DataTableModel
+{
+ OUString maRef1; /// First reference cell for table operations.
+ OUString maRef2; /// Second reference cell for table operations.
+ bool mb2dTable; /// True = 2-dimensional data table.
+ bool mbRowTable; /// True = row oriented data table.
+ bool mbRef1Deleted; /// True = first reference cell deleted.
+ bool mbRef2Deleted; /// True = second reference cell deleted.
+
+ explicit DataTableModel();
+};
+
+/** Manages all cell blocks currently in use. */
+class CellBlockBuffer : public WorksheetHelper
+{
+public:
+ explicit CellBlockBuffer( const WorksheetHelper& rHelper );
+
+ /** Sets column span information for a row. */
+ void setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans );
+
+private:
+ typedef ::std::map< sal_Int32, ValueRangeVector > ColSpanVectorMap;
+
+ ColSpanVectorMap maColSpans; /// Buffered column spans, mapped by row index.
+ sal_Int32 mnCurrRow; /// Current row index used for buffered cell import.
+};
+
+/** Manages the cell contents and cell formatting of a sheet.
+ */
+class SheetDataBuffer : public WorksheetHelper
+{
+public:
+ explicit SheetDataBuffer( const WorksheetHelper& rHelper );
+
+ /** Sets column span information for a row. */
+ void setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans );
+
+ /** Inserts a blank cell (with formatting) into the sheet. */
+ void setBlankCell( const CellModel& rModel );
+ /** Inserts a value cell into the sheet. */
+ void setValueCell( const CellModel& rModel, double fValue );
+ /** Inserts a simple string cell into the sheet. */
+ void setStringCell( const CellModel& rModel, const OUString& rText );
+ /** Inserts a rich-string cell into the sheet. */
+ void setStringCell( const CellModel& rModel, const RichStringRef& rxString );
+ /** Inserts a shared string cell into the sheet. */
+ void setStringCell( const CellModel& rModel, sal_Int32 nStringId );
+ /** Inserts a date/time cell into the sheet and adjusts number format. */
+ void setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime );
+ /** Inserts a boolean cell into the sheet and adjusts number format. */
+ void setBooleanCell( const CellModel& rModel, bool bValue );
+ /** Inserts an error cell from the passed error code into the sheet. */
+ void setErrorCell( const CellModel& rModel, const OUString& rErrorCode );
+ /** Inserts an error cell from the passed BIFF error code into the sheet. */
+ void setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode );
+ /** Inserts a formula cell into the sheet. */
+ void setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens );
+ /** Inserts an ISO 8601 date cell into the sheet. */
+ void setDateCell( const CellModel& rModel, const OUString& rDateString );
+
+ void createSharedFormula(
+ const ScAddress& rRange,
+ const ApiTokenSequence& rTokens);
+
+ /** Inserts the passed token array as array formula. */
+ void createArrayFormula(
+ const ScRange& rRange,
+ const ApiTokenSequence& rTokens );
+ /** Sets a multiple table operation to the passed range. */
+ void createTableOperation(
+ const ScRange& rRange,
+ const DataTableModel& rModel );
+
+ /** Sets default cell formatting for the specified range of rows. */
+ void setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat );
+ /** Merges the cells in the passed cell range. */
+ void setMergedRange( const ScRange& rRange );
+
+ /** Processes the cell formatting data of the passed cell. */
+ void setCellFormat( const CellModel& rModel );
+
+ /** Final processing after the sheet has been imported. */
+ void finalizeImport();
+
+ /** Sets the passed formula token array into a cell. */
+ void setCellFormula(
+ const ScAddress& rCellAddr,
+ const ApiTokenSequence& rTokens );
+private:
+
+ /** Creates a formula token array representing the shared formula with the
+ passed identifier. */
+ ApiTokenSequence resolveSharedFormula( const ScAddress& rMapKey ) const;
+
+ /** Inserts the passed array formula into the sheet. */
+ void finalizeArrayFormula(
+ const ScRange& rRange,
+ const ApiTokenSequence& rTokens ) const;
+ /** Inserts the passed table operation into the sheet. */
+ void finalizeTableOperation(
+ const ScRange& rRange, const DataTableModel& rModel );
+
+ /** Writes all cell formatting attributes to the passed cell range list. (depreciates writeXfIdRangeProperties) */
+ void applyCellMerging( const ScRange& rRange );
+ void addColXfStyles();
+ void addColXfStyleProcessRowRanges();
+private:
+ /** Stores cell range address and formula token array of an array formula. */
+ typedef std::pair< ScRange, ApiTokenSequence > ArrayFormula;
+ typedef ::std::vector< ArrayFormula > ArrayFormulaVector;
+
+ /** Stores cell range address and settings of a table operation. */
+ typedef std::pair< ScRange, DataTableModel > TableOperation;
+
+ /** Stores information about a range of rows with equal cell formatting. */
+ struct XfIdRowRange
+ {
+ ValueRange maRowRange; /// Indexes of first and last row.
+ sal_Int32 mnXfId; /// XF identifier for the row range.
+
+ explicit XfIdRowRange();
+ void set( sal_Int32 nRow, sal_Int32 nXfId );
+ bool tryExpand( sal_Int32 nRow, sal_Int32 nXfId );
+ };
+
+ typedef ::std::pair< sal_Int32, sal_Int32 > XfIdNumFmtKey;
+
+ struct RowRangeStyle
+ {
+ sal_Int32 mnStartRow;
+ sal_Int32 mnEndRow;
+ XfIdNumFmtKey mnNumFmt;
+ };
+ struct StyleRowRangeComp
+ {
+ bool operator() (const RowRangeStyle& lhs, const RowRangeStyle& rhs) const
+ {
+ // This end-vs-start comparison is needed by the lower_bound() use
+ // in SheetDataBuffer::addColXfStyleProcessRowRanges() that searches
+ // for partially overlapping ranges. In all other places the ranges
+ // should be non-overlapping, in which case this is the same as the "normal"
+ // comparison.
+ return lhs.mnEndRow<rhs.mnStartRow;
+ }
+ };
+ typedef ::o3tl::sorted_vector< RowRangeStyle, StyleRowRangeComp > RowStyles;
+ typedef ::std::map< sal_Int32, RowStyles > ColStyles;
+ typedef ::std::vector< RowRangeStyle > TmpRowStyles;
+ typedef ::std::map< sal_Int32, TmpRowStyles > TmpColStyles;
+ /** Stores information about a merged cell range. */
+ struct MergedRange
+ {
+ ScRange maRange; /// The formatted cell range.
+ sal_Int32 mnHorAlign; /// Horizontal alignment in the range.
+
+ explicit MergedRange( const ScRange& rRange );
+ explicit MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign );
+ bool tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign );
+ };
+ typedef ::std::vector< MergedRange > MergedRangeVector;
+
+ ColStyles maStylesPerColumn; /// Stores cell styles by column ( in row ranges )
+ CellBlockBuffer maCellBlocks; /// Manages all open cell blocks.
+ ArrayFormulaVector maArrayFormulas; /// All array formulas in the sheet.
+ std::vector< TableOperation >
+ maTableOperations; /// All table operations in the sheet.
+ ::std::map< BinAddress, ApiTokenSequence >
+ maSharedFormulas; /// Maps shared formula base address to defined name token index.
+ ScAddress maSharedFmlaAddr; /// Address of a cell containing a pending shared formula.
+ ScAddress maSharedBaseAddr; /// Base address of the pending shared formula.
+ XfIdRowRange maXfIdRowRange; /// Cached XF identifier for a range of rows.
+ std::map< XfIdNumFmtKey, ScRangeList >
+ maXfIdRangeLists; /// Collected XF identifiers for cell rangelists.
+ MergedRangeVector maMergedRanges; /// Merged cell ranges.
+ MergedRangeVector maCenterFillRanges; /// Merged cell ranges from 'center across' or 'fill' alignment.
+ bool mbPendingSharedFmla; /// True = maSharedFmlaAddr and maSharedBaseAddr are valid.
+ std::map< sal_Int32, std::vector< ValueRange > > maXfIdRowRangeList; /// Cached XF identifiers for a ranges of rows, we try and process rowranges with the same XF id together
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/sheetdatacontext.hxx b/sc/source/filter/inc/sheetdatacontext.hxx
new file mode 100644
index 000000000..b413e3d92
--- /dev/null
+++ b/sc/source/filter/inc/sheetdatacontext.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "addressconverter.hxx"
+#include "excelhandlers.hxx"
+#include "richstring.hxx"
+#include "sheetdatabuffer.hxx"
+#include <vcl/svapp.hxx>
+
+#define MULTI_THREAD_SHEET_PARSING 1
+
+namespace oox::xls {
+
+/** This class implements importing the sheetData element.
+
+ The sheetData element contains all row settings and all cells in a single
+ sheet of a spreadsheet document.
+ */
+class SheetDataContext : public WorksheetContextBase
+{
+ AddressConverter& mrAddressConv; /// The address converter.
+ std::unique_ptr<FormulaParser> mxFormulaParser; /// The formula parser, different one for each SheetDataContext
+ SheetDataBuffer& mrSheetData; /// The sheet data buffer for cell content and formatting.
+ CellModel maCellData; /// Position, contents, formatting of current imported cell.
+ CellFormulaModel maFmlaData; /// Settings for a cell formula.
+ sal_Int16 mnSheet; /// Index of the current sheet.
+ // If we are doing threaded parsing, this SheetDataContext
+ // forms the inner loop for bulk data parsing, and for the
+ // duration of this we can drop the solar mutex.
+#if MULTI_THREAD_SHEET_PARSING
+ SolarMutexReleaser aReleaser;
+#endif
+
+public:
+ explicit SheetDataContext( WorksheetFragmentBase& rFragment );
+ virtual ~SheetDataContext() override;
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+private:
+ /** Different types of cell records. */
+ enum CellType { CELLTYPE_VALUE, CELLTYPE_MULTI, CELLTYPE_FORMULA };
+
+ /** Imports row settings from a row element. */
+ void importRow( const AttributeList& rAttribs );
+ /** Imports cell settings from a c element. */
+ bool importCell( const AttributeList& rAttribs );
+ /** Imports cell settings from an f element. */
+ void importFormula( const AttributeList& rAttribs );
+
+ /** Imports row settings from a ROW record. */
+ void importRow( SequenceInputStream& rStrm );
+
+ /** Reads a cell address and the following XF identifier. */
+ bool readCellHeader( SequenceInputStream& rStrm, CellType eCellType );
+ /** Reads a cell formula for the current cell. */
+ ApiTokenSequence readCellFormula( SequenceInputStream& rStrm );
+ /** Reads the formula range used by shared formulas, arrays, and data tables. */
+ bool readFormulaRef( SequenceInputStream& rStrm );
+
+ /** Imports an empty cell from a CELL_BLANK or MULTCELL_BLANK record. */
+ void importCellBlank( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a boolean cell from a CELL_BOOL, MULTCELL_BOOL, or FORMULA_BOOL record. */
+ void importCellBool( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a numeric cell from a CELL_DOUBLE, MULTCELL_DOUBLE, or FORMULA_DOUBLE record. */
+ void importCellDouble( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports an error code cell from a CELL_ERROR, MULTCELL_ERROR, or FORMULA_ERROR record. */
+ void importCellError( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports an encoded numeric cell from a CELL_RK or MULTCELL_RK record. */
+ void importCellRk( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a rich-string cell from a CELL_RSTRING or MULTCELL_RSTRING record. */
+ void importCellRString( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a string cell from a CELL_SI or MULTCELL_SI record. */
+ void importCellSi( SequenceInputStream& rStrm, CellType eCellType );
+ /** Imports a string cell from a CELL_STRING, MULTCELL_STRING, or FORMULA_STRING record. */
+ void importCellString( SequenceInputStream& rStrm, CellType eCellType );
+
+ /** Imports an array formula from an ARRAY record. */
+ void importArray( SequenceInputStream& rStrm );
+ /** Imports table operation from a DATATABLE record. */
+ void importDataTable( SequenceInputStream& rStrm );
+ /** Imports a shared formula from a SHAREDFORMULA record. */
+ void importSharedFmla( SequenceInputStream& rStrm );
+
+private:
+ OUString maCellValue; /// Cell value string (OOXML only).
+ RichStringRef mxInlineStr; /// Inline rich string (OOXML only).
+ OUString maFormulaStr;
+ DataTableModel maTableData; /// Settings for table operations.
+ BinAddress maCurrPos; /// Current cell position (BIFF12 only).
+ bool mbHasFormula; /// True = current cell has formula data (OOXML only).
+ bool mbValidRange; /// True = maFmlaData.maFormulaRef is valid (OOXML only).
+
+ sal_Int32 mnRow; /// row index (0-based)
+ sal_Int32 mnCol; /// column index (0-based)
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/stylesbuffer.hxx b/sc/source/filter/inc/stylesbuffer.hxx
new file mode 100644
index 000000000..4d9e7aeed
--- /dev/null
+++ b/sc/source/filter/inc/stylesbuffer.hxx
@@ -0,0 +1,891 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/table/CellOrientation.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/util/CellProtection.hpp>
+#include <oox/drawingml/color.hxx>
+#include <oox/helper/graphichelper.hxx>
+#include <oox/helper/refmap.hxx>
+#include <oox/helper/refvector.hxx>
+#include "numberformatsbuffer.hxx"
+#include <editeng/svxenum.hxx>
+#include <editeng/frmdir.hxx>
+#include <attarray.hxx>
+#include <vector>
+
+namespace oox { class SequenceInputStream; }
+
+namespace oox { class PropertySet;
+ class PropertyMap;
+ class AttributeList; }
+
+namespace oox::xls {
+
+const sal_Int32 OOX_COLOR_WINDOWTEXT3 = 24; /// System window text color (BIFF3-BIFF4).
+const sal_Int32 OOX_COLOR_WINDOWBACK3 = 25; /// System window background color (BIFF3-BIFF4).
+const sal_Int32 OOX_COLOR_WINDOWTEXT = 64; /// System window text color (BIFF5+).
+const sal_Int32 OOX_COLOR_WINDOWBACK = 65; /// System window background color (BIFF5+).
+const sal_Int32 OOX_COLOR_BUTTONBACK = 67; /// System button background color (face color).
+const sal_Int32 OOX_COLOR_CHWINDOWTEXT = 77; /// System window text color (BIFF8 charts).
+const sal_Int32 OOX_COLOR_CHWINDOWBACK = 78; /// System window background color (BIFF8 charts).
+const sal_Int32 OOX_COLOR_CHBORDERAUTO = 79; /// Automatic frame border (BIFF8 charts).
+const sal_Int32 OOX_COLOR_NOTEBACK = 80; /// Note background color.
+const sal_Int32 OOX_COLOR_NOTETEXT = 81; /// Note text color.
+const sal_Int32 OOX_COLOR_FONTAUTO = 0x7FFF; /// Font auto color (system window text color).
+
+const sal_Int16 API_LINE_NONE = 0;
+const sal_Int16 API_LINE_HAIR = 1;
+const sal_Int16 API_LINE_THIN = 15;
+const sal_Int16 API_LINE_MEDIUM = 35;
+const sal_Int16 API_LINE_THICK = 50;
+
+const sal_Int16 API_ESCAPE_NONE = 0; /// No escapement.
+const sal_Int16 API_ESCAPE_SUPERSCRIPT = 101; /// Superscript: raise characters automatically (magic value 101).
+const sal_Int16 API_ESCAPE_SUBSCRIPT = -101; /// Subscript: lower characters automatically (magic value -101).
+
+const sal_Int8 API_ESCAPEHEIGHT_NONE = 100; /// Relative character height if not escaped.
+const sal_Int8 API_ESCAPEHEIGHT_DEFAULT = 58; /// Relative character height if escaped.
+
+/** Special implementation of the GraphicHelper for Excel palette and scheme
+ colors.
+ */
+class ExcelGraphicHelper : public GraphicHelper, public WorkbookHelper
+{
+public:
+ explicit ExcelGraphicHelper( const WorkbookHelper& rHelper );
+
+ /** Derived classes may implement to resolve a scheme color from the passed XML token identifier. */
+ virtual ::Color getSchemeColor( sal_Int32 nToken ) const override;
+ /** Derived classes may implement to resolve a palette index to an RGB color. */
+ virtual ::Color getPaletteColor( sal_Int32 nPaletteIdx ) const override;
+};
+
+class Color : public ::oox::drawingml::Color
+{
+public:
+ /** Sets the color to automatic. */
+ void setAuto();
+ /** Sets the color to the passed RGB value. */
+ void setRgb( ::Color nRgbValue, double fTint = 0.0 );
+ /** Sets the color to the passed theme index. */
+ void setTheme( sal_Int32 nThemeIdx, double fTint = 0.0 );
+ /** Sets the color to the passed palette index. */
+ void setIndexed( sal_Int32 nPaletteIdx, double fTint = 0.0 );
+
+ /** Imports the color from the passed attribute list. */
+ void importColor( const AttributeList& rAttribs );
+
+ /** Imports a 64-bit color from the passed binary stream. */
+ void importColor( SequenceInputStream& rStrm );
+ /** Imports a 32-bit palette color identifier from the passed BIFF12 stream. */
+ void importColorId( SequenceInputStream& rStrm );
+
+ /** Returns true, if the color is set to automatic. */
+ bool isAuto() const { return isPlaceHolder(); }
+};
+
+SequenceInputStream& operator>>( SequenceInputStream& rStrm, Color& orColor );
+
+/** Stores all colors of the color palette. */
+class ColorPalette : public WorkbookHelper
+{
+public:
+ /** Constructs the color palette with predefined color values. */
+ explicit ColorPalette( const WorkbookHelper& rHelper );
+
+ /** Appends a new color from the passed attributes. */
+ void importPaletteColor( const AttributeList& rAttribs );
+ /** Appends a new color from the passed RGBCOLOR record. */
+ void importPaletteColor( SequenceInputStream& rStrm );
+
+ /** Returns the RGB value of the color with the passed index. */
+ ::Color getColor( sal_Int32 nPaletteIdx ) const;
+
+private:
+ /** Appends the passed color. */
+ void appendColor( ::Color nRGBValue );
+
+private:
+ ::std::vector< ::Color > maColors; /// List of RGB values.
+ size_t mnAppendIndex; /// Index to append a new color.
+};
+
+/** Contains all XML font attributes, e.g. from a font or rPr element. */
+struct FontModel
+{
+ OUString maName; /// Font name.
+ Color maColor; /// Font color.
+ sal_Int32 mnScheme; /// Major/minor scheme font.
+ sal_Int32 mnFamily; /// Font family.
+ sal_Int32 mnCharSet; /// Windows font character set.
+ double mfHeight; /// Font height in points.
+ sal_Int32 mnUnderline; /// Underline style.
+ sal_Int32 mnEscapement; /// Escapement style.
+ bool mbBold; /// True = bold characters.
+ bool mbItalic; /// True = italic characters.
+ bool mbStrikeout; /// True = Strike out characters.
+ bool mbOutline; /// True = outlined characters.
+ bool mbShadow; /// True = shadowed chgaracters.
+
+ explicit FontModel();
+
+ void setBiff12Scheme( sal_uInt8 nScheme );
+ void setBiffHeight( sal_uInt16 nHeight );
+ void setBiffWeight( sal_uInt16 nWeight );
+ void setBiffUnderline( sal_uInt16 nUnderline );
+ void setBiffEscapement( sal_uInt16 nEscapement );
+};
+
+/** Contains used flags for all API font attributes. */
+struct ApiFontUsedFlags
+{
+ bool mbNameUsed; /// True = font name/family/char set are used.
+ bool mbColorUsed; /// True = font color is used.
+ bool mbSchemeUsed; /// True = font scheme is used.
+ bool mbHeightUsed; /// True = font height is used.
+ bool mbUnderlineUsed; /// True = underline style is used.
+ bool mbEscapementUsed; /// True = escapement style is used.
+ bool mbWeightUsed; /// True = font weight (boldness) is used.
+ bool mbPostureUsed; /// True = font posture (italic) is used.
+ bool mbStrikeoutUsed; /// True = strike out style is used.
+ bool mbOutlineUsed; /// True = outline style is used.
+ bool mbShadowUsed; /// True = shadow style is used.
+
+ explicit ApiFontUsedFlags( bool bAllUsed );
+};
+
+/** Contains API font name, family, and charset for a script type. */
+struct ApiScriptFontName
+{
+ OUString maName; /// Font name.
+ sal_Int16 mnFamily; /// Font family.
+ sal_Int16 mnTextEnc; /// Font text encoding.
+
+ explicit ApiScriptFontName();
+};
+
+/** Contains all API font attributes. */
+struct ApiFontData
+{
+ ApiScriptFontName maLatinFont; /// Font name for latin scripts.
+ ApiScriptFontName maAsianFont; /// Font name for east-asian scripts.
+ ApiScriptFontName maCmplxFont; /// Font name for complex scripts.
+ css::awt::FontDescriptor maDesc; /// Font descriptor (height in twips, weight in %).
+ ::Color mnColor; /// Font color.
+ sal_Int16 mnEscapement; /// Escapement style.
+ sal_Int8 mnEscapeHeight; /// Escapement font height.
+ bool mbOutline; /// True = outlined characters.
+ bool mbShadow; /// True = shadowed chgaracters.
+
+ explicit ApiFontData();
+};
+
+class Font : public WorkbookHelper
+{
+public:
+ explicit Font( const WorkbookHelper& rHelper, bool bDxf );
+ explicit Font( const WorkbookHelper& rHelper, const FontModel& rModel );
+
+ /** Sets font formatting attributes for the passed element. */
+ void importAttribs( sal_Int32 nElement, const AttributeList& rAttribs );
+
+ /** Imports the FONT record from the passed stream. */
+ void importFont( SequenceInputStream& rStrm );
+ /** Imports the font name from a DXF record. */
+ void importDxfName( SequenceInputStream& rStrm );
+ /** Imports the font color from a DXF record. */
+ void importDxfColor( SequenceInputStream& rStrm );
+ /** Imports the font scheme from a DXF record. */
+ void importDxfScheme( SequenceInputStream& rStrm );
+ /** Imports the font height from a DXF record. */
+ void importDxfHeight( SequenceInputStream& rStrm );
+ /** Imports the font weight from a DXF record. */
+ void importDxfWeight( SequenceInputStream& rStrm );
+ /** Imports the font underline style from a DXF record. */
+ void importDxfUnderline( SequenceInputStream& rStrm );
+ /** Imports the font escapement style from a DXF record. */
+ void importDxfEscapement( SequenceInputStream& rStrm );
+ /** Imports a font style flag from a DXF record. */
+ void importDxfFlag( sal_Int32 nElement, SequenceInputStream& rStrm );
+
+ /** Returns the font model structure. This function can be called before
+ finalizeImport() has been called. */
+ const FontModel& getModel() const { return maModel; }
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns an API font descriptor with own font information. */
+ const css::awt::FontDescriptor& getFontDescriptor() const { return maApiData.maDesc;}
+ /** Returns true, if the font requires rich text formatting in Calc.
+ @descr Example: Font escapement is a cell attribute in Excel, but Calc
+ needs a rich text cell for this attribute. */
+ bool needsRichTextFormat() const;
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bEditEngineText, bool bSkipPoolDefs = false ) const;
+ /** Writes all font attributes to the passed property map. */
+ void writeToPropertyMap(
+ PropertyMap& rPropMap ) const;
+ /** Writes all font attributes to the passed property set. */
+ void writeToPropertySet(
+ PropertySet& rPropSet ) const;
+
+private:
+ FontModel maModel;
+ ApiFontData maApiData;
+ ApiFontUsedFlags maUsedFlags;
+ bool mbDxf;
+};
+
+typedef std::shared_ptr< Font > FontRef;
+
+/** Contains all XML cell alignment attributes, e.g. from an alignment element. */
+struct AlignmentModel
+{
+ sal_Int32 mnHorAlign; /// Horizontal alignment.
+ sal_Int32 mnVerAlign; /// Vertical alignment.
+ sal_Int32 mnTextDir; /// CTL text direction.
+ sal_Int32 mnRotation; /// Text rotation angle.
+ sal_Int32 mnIndent; /// Indentation.
+ bool mbWrapText; /// True = multi-line text.
+ bool mbShrink; /// True = shrink to fit cell size.
+ bool mbJustLastLine; /// True = justify last line in block text.
+
+ explicit AlignmentModel();
+
+ /** Sets horizontal alignment from the passed BIFF data. */
+ void setBiffHorAlign( sal_uInt8 nHorAlign );
+ /** Sets vertical alignment from the passed BIFF data. */
+ void setBiffVerAlign( sal_uInt8 nVerAlign );
+};
+
+/** Contains all API cell alignment attributes. */
+struct ApiAlignmentData
+{
+ css::table::CellHoriJustify meHorJustify; /// Horizontal alignment.
+ sal_Int32 mnHorJustifyMethod;
+ sal_Int32 mnVerJustify; /// Vertical alignment.
+ sal_Int32 mnVerJustifyMethod;
+ css::table::CellOrientation meOrientation; /// Normal or stacked text.
+ Degree100 mnRotation; /// Text rotation angle.
+ sal_Int16 mnWritingMode; /// CTL text direction.
+ sal_Int16 mnIndent; /// Indentation.
+ bool mbWrapText; /// True = multi-line text.
+ bool mbShrink; /// True = shrink to fit cell size.
+
+ explicit ApiAlignmentData();
+};
+
+bool operator==( const ApiAlignmentData& rLeft, const ApiAlignmentData& rRight );
+
+class Alignment : public WorkbookHelper
+{
+public:
+ explicit Alignment( const WorkbookHelper& rHelper );
+
+ /** Sets all attributes from the alignment element. */
+ void importAlignment( const AttributeList& rAttribs );
+
+ /** Sets the alignment attributes from the passed BIFF12 XF record data. */
+ void setBiff12Data( sal_uInt32 nFlags );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns the alignment model structure. */
+ const AlignmentModel& getModel() const { return maModel; }
+ /** Returns the converted API alignment data struct. */
+ const ApiAlignmentData& getApiData() const { return maApiData; }
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+
+private:
+ ::SvxCellHorJustify GetScHorAlign() const;
+ ::SvxCellVerJustify GetScVerAlign() const;
+ ::SvxFrameDirection GetScFrameDir() const;
+ AlignmentModel maModel; /// Alignment model data.
+ ApiAlignmentData maApiData; /// Alignment data converted to API constants.
+};
+
+/** Contains all XML cell protection attributes, e.g. from a protection element. */
+struct ProtectionModel
+{
+ bool mbLocked; /// True = locked against editing.
+ bool mbHidden; /// True = formula is hidden.
+
+ explicit ProtectionModel();
+};
+
+/** Contains all API cell protection attributes. */
+struct ApiProtectionData
+{
+ typedef css::util::CellProtection ApiCellProtection;
+
+ ApiCellProtection maCellProt;
+
+ explicit ApiProtectionData();
+};
+
+bool operator==( const ApiProtectionData& rLeft, const ApiProtectionData& rRight );
+
+class Protection : public WorkbookHelper
+{
+public:
+ explicit Protection( const WorkbookHelper& rHelper );
+
+ /** Sets all attributes from the protection element. */
+ void importProtection( const AttributeList& rAttribs );
+
+ /** Sets the protection attributes from the passed BIFF12 XF record data. */
+ void setBiff12Data( sal_uInt32 nFlags );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns the converted API protection data struct. */
+ const ApiProtectionData& getApiData() const { return maApiData; }
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+private:
+ ProtectionModel maModel; /// Protection model data.
+ ApiProtectionData maApiData; /// Protection data converted to API constants.
+};
+
+/** Contains XML attributes of a single border line. */
+struct BorderLineModel
+{
+ Color maColor; /// Borderline color.
+ sal_Int32 mnStyle; /// Border line style.
+ bool mbUsed; /// True = line format used.
+
+ explicit BorderLineModel( bool bDxf );
+
+ /** Sets the passed BIFF line style. */
+ void setBiffStyle( sal_Int32 nLineStyle );
+};
+
+/** Contains XML attributes of a complete cell border. */
+struct BorderModel
+{
+ BorderLineModel maLeft; /// Left line format.
+ BorderLineModel maRight; /// Right line format.
+ BorderLineModel maTop; /// Top line format.
+ BorderLineModel maBottom; /// Bottom line format.
+ BorderLineModel maDiagonal; /// Diagonal line format.
+ bool mbDiagTLtoBR; /// True = top-left to bottom-right on.
+ bool mbDiagBLtoTR; /// True = bottom-left to top-right on.
+
+ explicit BorderModel( bool bDxf );
+};
+
+/** Contains API attributes of a complete cell border. */
+struct ApiBorderData
+{
+ typedef css::table::BorderLine2 ApiBorderLine;
+
+ ApiBorderLine maLeft; /// Left line format
+ ApiBorderLine maRight; /// Right line format
+ ApiBorderLine maTop; /// Top line format
+ ApiBorderLine maBottom; /// Bottom line format
+ ApiBorderLine maTLtoBR; /// Diagonal top-left to bottom-right line format.
+ ApiBorderLine maBLtoTR; /// Diagonal bottom-left to top-right line format.
+ bool mbBorderUsed; /// True = left/right/top/bottom line format used.
+ bool mbDiagUsed; /// True = diagonal line format used.
+
+ explicit ApiBorderData();
+
+ /** Returns true, if any of the outer border lines is visible. */
+ bool hasAnyOuterBorder() const;
+};
+
+class Border : public WorkbookHelper
+{
+public:
+ explicit Border( const WorkbookHelper& rHelper, bool bDxf );
+
+ /** Sets global border attributes from the border element. */
+ void importBorder( const AttributeList& rAttribs );
+ /** Sets border attributes for the border line with the passed element identifier. */
+ void importStyle( sal_Int32 nElement, const AttributeList& rAttribs );
+ /** Sets color attributes for the border line with the passed element identifier. */
+ void importColor( sal_Int32 nElement, const AttributeList& rAttribs );
+
+ /** Imports the BORDER record from the passed stream. */
+ void importBorder( SequenceInputStream& rStrm );
+ /** Imports a border from a DXF record from the passed stream. */
+ void importDxfBorder( sal_Int32 nElement, SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport( bool bRTL );
+
+ /** Returns the converted API border data struct. */
+ const ApiBorderData& getApiData() const { return maApiData; }
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+
+private:
+ /** Returns the border line struct specified by the passed XML token identifier. */
+ BorderLineModel* getBorderLine( sal_Int32 nElement );
+
+ /** Converts border line data to an API struct, returns true, if the line is marked as used. */
+ bool convertBorderLine(
+ css::table::BorderLine2& rBorderLine,
+ const BorderLineModel& rModel );
+
+private:
+ BorderModel maModel;
+ ApiBorderData maApiData;
+ bool mbDxf;
+};
+
+typedef std::shared_ptr< Border > BorderRef;
+
+/** Contains XML pattern fill attributes from the patternFill element. */
+struct PatternFillModel
+{
+ Color maPatternColor; /// Pattern foreground color.
+ Color maFilterPatternColor; /// Pattern foreground for color filter.
+ Color maFillColor; /// Background fill color.
+ sal_Int32 mnPattern; /// Pattern identifier (e.g. solid).
+ bool mbPattColorUsed; /// True = pattern foreground color used.
+ bool mbFillColorUsed; /// True = background fill color used.
+ bool mbPatternUsed; /// True = pattern used.
+
+ explicit PatternFillModel( bool bDxf );
+
+ /** Sets the passed BIFF pattern identifier. */
+ void setBiffPattern( sal_Int32 nPattern );
+};
+
+/** Contains XML gradient fill attributes from the gradientFill element. */
+struct GradientFillModel
+{
+ typedef ::std::map< double, Color > ColorMap;
+
+ sal_Int32 mnType; /// Gradient type, linear or path.
+ double mfAngle; /// Rotation angle for type linear.
+ double mfLeft; /// Left convergence for type path.
+ double mfRight; /// Right convergence for type path.
+ double mfTop; /// Top convergence for type path.
+ double mfBottom; /// Bottom convergence for type path.
+ ColorMap maColors; /// Gradient colors.
+
+ explicit GradientFillModel();
+
+ /** Reads BIFF12 gradient settings from a FILL or DXF record. */
+ void readGradient( SequenceInputStream& rStrm );
+ /** Reads BIFF12 gradient stop settings from a FILL or DXF record. */
+ void readGradientStop( SequenceInputStream& rStrm, bool bDxf );
+};
+
+/** Contains API fill attributes. */
+struct ApiSolidFillData
+{
+ ::Color mnColor; /// Fill color.
+ ::Color mnFilterColor; /// Fill color filtering.
+ bool mbTransparent; /// True = transparent area.
+ bool mbUsed; /// True = fill data is valid.
+
+ explicit ApiSolidFillData();
+};
+
+/** Contains cell fill attributes, either a pattern fill or a gradient fill. */
+class Fill : public WorkbookHelper
+{
+public:
+ explicit Fill( const WorkbookHelper& rHelper, bool bDxf );
+
+ /** Sets attributes of a patternFill element. */
+ void importPatternFill( const AttributeList& rAttribs );
+ /** Sets the pattern color from the fgColor element. */
+ void importFgColor( const AttributeList& rAttribs );
+ /** Sets the background color from the bgColor element. */
+ void importBgColor( const AttributeList& rAttribs );
+ /** Sets attributes of a gradientFill element. */
+ void importGradientFill( const AttributeList& rAttribs );
+ /** Sets a color from the color element in a gradient fill. */
+ void importColor( const AttributeList& rAttribs, double fPosition );
+
+ /** Imports the FILL record from the passed stream. */
+ void importFill( SequenceInputStream& rStrm );
+ /** Imports the fill pattern from a DXF record. */
+ void importDxfPattern( SequenceInputStream& rStrm );
+ /** Imports the pattern color from a DXF record. */
+ void importDxfFgColor( SequenceInputStream& rStrm );
+ /** Imports the background color from a DXF record. */
+ void importDxfBgColor( SequenceInputStream& rStrm );
+ /** Imports gradient settings from a DXF record. */
+ void importDxfGradient( SequenceInputStream& rStrm );
+ /** Imports gradient stop settings from a DXF record. */
+ void importDxfStop( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ void fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+
+private:
+ typedef std::shared_ptr< PatternFillModel > PatternModelRef;
+ typedef std::shared_ptr< GradientFillModel > GradientModelRef;
+
+ PatternModelRef mxPatternModel;
+ GradientModelRef mxGradientModel;
+ ApiSolidFillData maApiData;
+ bool mbDxf;
+};
+
+typedef std::shared_ptr< Fill > FillRef;
+
+/** Contains all data for a cell format or cell style. */
+struct XfModel
+{
+ sal_Int32 mnStyleXfId; /// Index to parent style XF.
+ sal_Int32 mnFontId; /// Index to font data list.
+ sal_Int32 mnNumFmtId; /// Index to number format list.
+ sal_Int32 mnBorderId; /// Index to list of cell borders.
+ sal_Int32 mnFillId; /// Index to list of cell areas.
+ bool mbCellXf; /// True = cell XF, false = style XF.
+ bool mbFontUsed; /// True = font index used.
+ bool mbNumFmtUsed; /// True = number format used.
+ bool mbAlignUsed; /// True = alignment used.
+ bool mbProtUsed; /// True = cell protection used.
+ bool mbBorderUsed; /// True = border data used.
+ bool mbAreaUsed; /// True = area data used.
+
+ explicit XfModel();
+};
+
+bool operator==( const XfModel& rXfModel1, const XfModel& rXfModel2 );
+
+/** Represents a cell format or a cell style (called XF, extended format).
+
+ This class stores the type (cell/style), the index to the parent style (if
+ it is a cell format) and all "attribute used" flags, which reflect the
+ state of specific attribute groups (true = user has changed the attributes)
+ and all formatting data.
+ */
+class Xf : public WorkbookHelper
+{
+ friend bool operator==( const Xf& rXf1, const Xf& rXf2 );
+public:
+ struct AttrList
+ {
+ std::vector<ScAttrEntry> maAttrs;
+ bool mbLatinNumFmtOnly;
+ const ScPatternAttr* mpDefPattern;
+
+ AttrList(const ScPatternAttr* pDefPatternAttr);
+ };
+
+ explicit Xf( const WorkbookHelper& rHelper );
+
+ /** Sets all attributes from the xf element. */
+ void importXf( const AttributeList& rAttribs, bool bCellXf );
+ /** Sets all attributes from the alignment element. */
+ void importAlignment( const AttributeList& rAttribs );
+ /** Sets all attributes from the protection element. */
+ void importProtection( const AttributeList& rAttribs );
+
+ /** Imports the XF record from the passed stream. */
+ void importXf( SequenceInputStream& rStrm, bool bCellXf );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns true, if the XF is a cell XF, and false, if it is a style XF. */
+ bool isCellXf() const { return maModel.mbCellXf; }
+
+ /** Returns the referred font object. */
+ FontRef getFont() const;
+ /** Returns the alignment data of this style. */
+ const Alignment& getAlignment() const { return maAlignment; }
+
+ void applyPatternToAttrList(
+ AttrList& rAttrs, SCROW nRow1, SCROW nRow2, sal_Int32 nForceScNumFmt );
+
+ void writeToDoc( ScDocumentImport& rDoc, const ScRange& rRange );
+
+ const ::ScPatternAttr& createPattern( bool bSkipPoolDefs = false );
+
+private:
+ typedef ::std::unique_ptr< ::ScPatternAttr > ScPatternAttrPtr;
+
+ ScPatternAttrPtr mpPattern; /// Calc item set.
+ sal_uInt32 mnScNumFmt; /// Calc number format.
+
+ XfModel maModel; /// Cell XF or style XF model data.
+ Alignment maAlignment; /// Cell alignment data.
+ Protection maProtection; /// Cell protection data.
+ sal_Int32 meRotationRef; /// Rotation reference dependent on border.
+ ::ScStyleSheet* mpStyleSheet; /// Calc cell style sheet.
+};
+
+bool operator==( const Xf& rXf1, const Xf& rXf2 );
+
+typedef std::shared_ptr< Xf > XfRef;
+
+class Dxf : public WorkbookHelper
+{
+public:
+ explicit Dxf( const WorkbookHelper& rHelper );
+
+ /** Creates a new empty font object. */
+ FontRef const & createFont( bool bAlwaysNew = true );
+ /** Creates a new empty border object. */
+ BorderRef const & createBorder( bool bAlwaysNew = true );
+ /** Creates a new empty fill object. */
+ FillRef const & createFill( bool bAlwaysNew = true );
+
+ /** Inserts a new number format code. */
+ void importNumFmt( const AttributeList& rAttribs );
+
+ /** Imports the DXF record from the passed stream. */
+ void importDxf( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ void fillToItemSet( SfxItemSet& rSet ) const;
+
+private:
+ FontRef mxFont; /// Font data.
+ NumberFormatRef mxNumFmt; /// Number format data.
+ std::shared_ptr< Alignment >
+ mxAlignment; /// Alignment data.
+ std::shared_ptr< Protection >
+ mxProtection; /// Protection data.
+ BorderRef mxBorder; /// Border data.
+ FillRef mxFill; /// Fill data.
+};
+
+typedef std::shared_ptr< Dxf > DxfRef;
+
+/** Contains attributes of a cell style, e.g. from the cellStyle element. */
+struct CellStyleModel
+{
+ OUString maName; /// Cell style name.
+ sal_Int32 mnXfId; /// Formatting for this cell style.
+ sal_Int32 mnBuiltinId; /// Identifier for builtin styles.
+ sal_Int32 mnLevel; /// Level for builtin column/row styles.
+ bool mbBuiltin; /// True = builtin style.
+ bool mbCustom; /// True = customized builtin style.
+ bool mbHidden; /// True = style not visible in GUI.
+
+ explicit CellStyleModel();
+
+ /** Returns true, if this style is a builtin style. */
+ bool isBuiltin() const;
+ /** Returns true, if this style represents the default document cell style. */
+ bool isDefaultStyle() const;
+};
+
+class CellStyle : public WorkbookHelper
+{
+public:
+ explicit CellStyle( const WorkbookHelper& rHelper );
+
+ /** Imports passed attributes from the cellStyle element. */
+ void importCellStyle( const AttributeList& rAttribs );
+ /** Imports style settings from a CELLSTYLE record. */
+ void importCellStyle( SequenceInputStream& rStrm );
+
+ /** Creates the style sheet in the document described by this cell style object. */
+ void createCellStyle();
+ /** Stores the passed final style name and creates the cell style, if it is
+ user-defined or modified built-in. */
+ void finalizeImport( const OUString& rFinalName );
+
+ /** Returns the cell style model structure. */
+ const CellStyleModel& getModel() const { return maModel; }
+ /** Returns the final style name used in the document. */
+ const OUString& getFinalStyleName() const { return maFinalName; }
+ ::ScStyleSheet* getStyleSheet() { return mpStyleSheet; }
+private:
+ CellStyleModel maModel;
+ OUString maFinalName; /// Final style name used in API.
+ bool mbCreated; /// True = style sheet created.
+ ::ScStyleSheet* mpStyleSheet; /// Calc cell style sheet.
+
+};
+
+typedef std::shared_ptr< CellStyle > CellStyleRef;
+
+class CellStyleBuffer : public WorkbookHelper
+{
+public:
+ explicit CellStyleBuffer( const WorkbookHelper& rHelper );
+
+ /** Appends and returns a new named cell style object. */
+ CellStyleRef importCellStyle( const AttributeList& rAttribs );
+ /** Imports the CELLSTYLE record from the passed stream. */
+ CellStyleRef importCellStyle( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns the XF identifier associated to the default cell style. */
+ sal_Int32 getDefaultXfId() const;
+ /** Returns the default style sheet for unused cells. */
+ OUString getDefaultStyleName() const;
+ /** Creates the style sheet described by the style XF with the passed identifier. */
+ OUString createCellStyle( sal_Int32 nXfId ) const;
+ ::ScStyleSheet* getCellStyleSheet( sal_Int32 nXfId ) const;
+
+private:
+ /** Inserts the passed cell style object into the internal maps. */
+ void insertCellStyle( CellStyleRef const & xCellStyle );
+ /** Creates the style sheet described by the passed cell style object. */
+ static OUString createCellStyle( const CellStyleRef& rxCellStyle );
+ static ::ScStyleSheet* getCellStyleSheet( const CellStyleRef& rxCellStyle );
+
+private:
+ typedef RefVector< CellStyle > CellStyleVector;
+ typedef RefMap< sal_Int32, CellStyle > CellStyleXfIdMap;
+
+ CellStyleVector maBuiltinStyles; /// All built-in cell styles.
+ CellStyleVector maUserStyles; /// All user defined cell styles.
+ CellStyleXfIdMap maStylesByXf; /// All cell styles, mapped by XF identifier.
+ CellStyleRef mxDefStyle; /// Default cell style.
+};
+
+struct AutoFormatModel
+{
+ sal_Int32 mnAutoFormatId; /// Index of predefined autoformatting.
+ bool mbApplyNumFmt; /// True = apply number format from autoformatting.
+ bool mbApplyFont; /// True = apply font from autoformatting.
+ bool mbApplyAlignment; /// True = apply alignment from autoformatting.
+ bool mbApplyBorder; /// True = apply border from autoformatting.
+ bool mbApplyFill; /// True = apply fill from autoformatting.
+ bool mbApplyProtection; /// True = apply protection from autoformatting.
+
+ explicit AutoFormatModel();
+};
+
+class StylesBuffer : public WorkbookHelper
+{
+public:
+ explicit StylesBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new empty font object. */
+ FontRef createFont();
+ /** Creates a number format. */
+ NumberFormatRef createNumFmt( sal_Int32 nNumFmtId, const OUString& rFmtCode );
+ sal_Int32 nextFreeNumFmtId();
+ /** Creates a new empty border object. */
+ BorderRef createBorder();
+ /** Creates a new empty fill object. */
+ FillRef createFill();
+ /** Creates a new empty cell formatting object. */
+ XfRef createCellXf();
+ /** Creates a new empty style formatting object. */
+ XfRef createStyleXf();
+ /** Creates a new empty differential formatting object. */
+ DxfRef createDxf();
+ DxfRef createExtDxf();
+
+ /** Appends a new color to the color palette. */
+ void importPaletteColor( const AttributeList& rAttribs );
+ /** Inserts a new number format code. */
+ NumberFormatRef importNumFmt( const AttributeList& rAttribs );
+ /** Appends and returns a new named cell style object. */
+ CellStyleRef importCellStyle( const AttributeList& rAttribs );
+
+ /** Appends a new color to the color palette. */
+ void importPaletteColor( SequenceInputStream& rStrm );
+ /** Imports the NUMFMT record from the passed stream. */
+ void importNumFmt( SequenceInputStream& rStrm );
+ /** Imports the CELLSTYLE record from the passed stream. */
+ void importCellStyle( SequenceInputStream& rStrm );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+
+ /** Returns the palette color with the specified index. */
+ ::Color getPaletteColor( sal_Int32 nIndex ) const;
+ /** Returns the specified font object. */
+ FontRef getFont( sal_Int32 nFontId ) const;
+ /** Returns the specified border object. */
+ BorderRef getBorder( sal_Int32 nBorderId ) const;
+ /** Returns the specified cell format object. */
+ XfRef getCellXf( sal_Int32 nXfId ) const;
+ /** Returns the specified style format object. */
+ XfRef getStyleXf( sal_Int32 nXfId ) const;
+
+ /** Returns the font object of the specified cell XF. */
+ FontRef getFontFromCellXf( sal_Int32 nXfId ) const;
+ /** Returns the default application font (used in the "Normal" cell style). */
+ FontRef getDefaultFont() const;
+ /** Returns the model of the default application font (used in the "Normal" cell style). */
+ const FontModel& getDefaultFontModel() const;
+
+ /** Returns true, if the specified borders are equal. */
+ static bool equalBorders( sal_Int32 nBorderId1, sal_Int32 nBorderId2 );
+ /** Returns true, if the specified fills are equal. */
+ static bool equalFills( sal_Int32 nFillId1, sal_Int32 nFillId2 );
+
+ /** Returns the default style sheet for unused cells. */
+ OUString getDefaultStyleName() const;
+ /** Creates the style sheet described by the style XF with the passed identifier. */
+ OUString createCellStyle( sal_Int32 nXfId ) const;
+ ::ScStyleSheet* getCellStyleSheet( sal_Int32 nXfId ) const;
+ /** Creates the style sheet described by the DXF with the passed identifier. */
+ OUString createDxfStyle( sal_Int32 nDxfId ) const;
+ OUString createExtDxfStyle( sal_Int32 nDxfId ) const;
+
+ void writeFontToItemSet( SfxItemSet& rItemSet, sal_Int32 nFontId, bool bSkipPoolDefs ) const;
+ sal_uInt32 writeNumFmtToItemSet( SfxItemSet& rItemSet, sal_uInt32 nNumFmtId, bool bSkipPoolDefs ) const;
+ /** Writes the specified number format to the passed property map. */
+ void writeBorderToItemSet( SfxItemSet& rItemSet, sal_Int32 nBorderId, bool bSkipPoolDefs ) const;
+ /** Writes the fill attributes of the specified fill data to the passed property map. */
+ void writeFillToItemSet( SfxItemSet& rItemSet, sal_Int32 nFillId, bool bSkipPoolDefs ) const;
+
+ /** Writes the cell formatting attributes of the specified XF to the passed property set. */
+ void writeCellXfToDoc( ScDocumentImport& rDoc, const ScRange& rRange, sal_Int32 nXfId ) const;
+ const RefVector< Dxf >& getExtDxfs() const { return maExtDxfs; }
+
+private:
+ typedef RefVector< Font > FontVector;
+ typedef RefVector< Border > BorderVector;
+ typedef RefVector< Fill > FillVector;
+ typedef RefVector< Xf > XfVector;
+ typedef RefVector< Dxf > DxfVector;
+ typedef ::std::map< sal_Int32, OUString > DxfStyleMap;
+
+ ColorPalette maPalette; /// Color palette.
+ FontVector maFonts; /// List of font objects.
+ NumberFormatsBuffer maNumFmts; /// List of all number format codes.
+ BorderVector maBorders; /// List of cell border objects.
+ FillVector maFills; /// List of cell area fill objects.
+ XfVector maCellXfs; /// List of cell formats.
+ XfVector maStyleXfs; /// List of cell styles.
+ CellStyleBuffer maCellStyles; /// All built-in and user defined cell styles.
+ DxfVector maDxfs; /// List of differential cell styles.
+ DxfVector maExtDxfs; /// List of differential extlst cell styles.
+ mutable DxfStyleMap maDxfStyles; /// Maps DXF identifiers to Calc style sheet names.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/stylesfragment.hxx b/sc/source/filter/inc/stylesfragment.hxx
new file mode 100644
index 000000000..bd590f2a6
--- /dev/null
+++ b/sc/source/filter/inc/stylesfragment.hxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+#include "stylesbuffer.hxx"
+
+namespace oox::xls {
+
+class IndexedColorsContext : public WorkbookContextBase
+{
+public:
+ explicit IndexedColorsContext( WorkbookFragmentBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+};
+
+class FontContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit FontContext( ParentType& rParent, const FontRef& rxFont ) :
+ WorkbookContextBase( rParent ), mxFont( rxFont ) {}
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ FontRef mxFont;
+};
+
+class BorderContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit BorderContext( ParentType& rParent, const BorderRef& rxBorder ) :
+ WorkbookContextBase( rParent ), mxBorder( rxBorder ) {}
+
+protected:
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ BorderRef mxBorder;
+};
+
+class FillContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit FillContext( ParentType& rParent, const FillRef& rxFill ) :
+ WorkbookContextBase( rParent ), mxFill( rxFill ), mfGradPos( -1.0 ) {}
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ FillRef mxFill;
+ double mfGradPos; /// Gradient color position.
+};
+
+class XfContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit XfContext( ParentType& rParent, const XfRef& rxXf, bool bCellXf ) :
+ WorkbookContextBase( rParent ), mxXf( rxXf ), mbCellXf( bCellXf ) {}
+
+protected:
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ XfRef mxXf;
+ bool mbCellXf; /// True = cell XF, false = style XF.
+};
+
+class DxfContext : public WorkbookContextBase
+{
+public:
+ template< typename ParentType >
+ explicit DxfContext( ParentType& rParent, const DxfRef& rxDxf ) :
+ WorkbookContextBase( rParent ), mxDxf( rxDxf ), mxExtDxf( rxDxf ) {}
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+
+private:
+ DxfRef mxDxf;
+ DxfRef mxExtDxf;
+};
+
+class StylesFragment : public WorkbookFragmentBase
+{
+public:
+ explicit StylesFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tablebuffer.hxx b/sc/source/filter/inc/tablebuffer.hxx
new file mode 100644
index 000000000..66f6816c1
--- /dev/null
+++ b/sc/source/filter/inc/tablebuffer.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "autofilterbuffer.hxx"
+#include "tablecolumnsbuffer.hxx"
+#include "workbookhelper.hxx"
+
+namespace oox::xls {
+
+struct TableModel
+{
+ ScRange maRange; /// Original (unchecked) range of the table.
+ OUString maProgName; /// Programmatical name.
+ OUString maDisplayName; /// Display name.
+ sal_Int32 mnId; /// Unique table identifier.
+ sal_Int32 mnType; /// Table type (worksheet, query, etc.).
+ sal_Int32 mnHeaderRows; /// Number of header rows.
+ sal_Int32 mnTotalsRows; /// Number of totals rows.
+
+ explicit TableModel();
+};
+
+class Table : public WorkbookHelper
+{
+public:
+ explicit Table( const WorkbookHelper& rHelper );
+
+ /** Imports a table definition from the passed attributes. */
+ void importTable( const AttributeList& rAttribs, sal_Int16 nSheet );
+ /** Imports a table definition from a TABLE record. */
+ void importTable( SequenceInputStream& rStrm, sal_Int16 nSheet );
+ /** Creates a new auto filter and stores it internally. */
+ AutoFilter& createAutoFilter() { return maAutoFilters.createAutoFilter(); }
+ /** Creates a new tableColumns handler and stores it internally. */
+ TableColumns& createTableColumns() { return maTableColumns.createTableColumns(); }
+
+ /** Creates a database range from this tables. */
+ void finalizeImport();
+ void applyAutoFilters();
+ void applyTableColumns();
+
+ /** Returns the unique table identifier. */
+ sal_Int32 getTableId() const { return maModel.mnId; }
+ /** Returns the token index used in API token arrays (com.sun.star.sheet.FormulaToken). */
+ sal_Int32 getTokenIndex() const { return mnTokenIndex; }
+ /** Returns the original display name of the table. */
+ const OUString& getDisplayName() const { return maModel.maDisplayName; }
+
+ /** Returns the original (unchecked) total range of the table. */
+ const ScRange& getOriginalRange() const { return maModel.maRange; }
+ /** Returns the cell range of this table. */
+ const ScRange& getRange() const { return maDestRange; }
+ /** Returns the number of columns of this table. */
+ SCCOL getWidth() const { return maDestRange.aEnd.Col() - maDestRange.aStart.Col() + 1; }
+ /** Returns the number of rows of this table. */
+ SCROW getHeight() const { return maDestRange.aEnd.Row() - maDestRange.aStart.Row() + 1; }
+ /** Returns the number of header rows in the table range. */
+ sal_Int32 getHeaderRows() const { return maModel.mnHeaderRows; }
+ /** Returns the number of totals rows in the table range. */
+ sal_Int32 getTotalsRows() const { return maModel.mnTotalsRows; }
+
+private:
+ TableModel maModel;
+ AutoFilterBuffer maAutoFilters; /// Filter settings for this table.
+ TableColumnsBuffer maTableColumns; /// Column names of this table.
+ OUString maDBRangeName; /// Name of the database range in the Calc document.
+ ScRange maDestRange; /// Validated range of the table in the worksheet.
+ sal_Int32 mnTokenIndex; /// Token index used in API token array.
+};
+
+typedef std::shared_ptr< Table > TableRef;
+
+class TableBuffer : public WorkbookHelper
+{
+public:
+ explicit TableBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new empty table. */
+ Table& createTable();
+
+ /** Creates database ranges from all imported tables. */
+ void finalizeImport();
+ /** Applies autofilters from created database range ( requires finalizeImport to have run before being called */
+ void applyAutoFilters();
+ /** Applies columns names from created database range ( requires finalizeImport to have run before being called */
+ void applyTableColumns();
+ /** Returns a table by its identifier. */
+ TableRef getTable( sal_Int32 nTableId ) const;
+ /** Returns a table by its display name. */
+ TableRef getTable( const OUString& rDispName ) const;
+
+private:
+ /** Inserts the passed table into the maps according to its identifier and name. */
+ void insertTableToMaps( const TableRef& rxTable );
+
+private:
+ typedef RefVector< Table > TableVector;
+
+ TableVector maTables;
+ RefMap< sal_Int32, Table > maIdTables;
+ RefMap< OUString, Table > maNameTables;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tablecolumnsbuffer.hxx b/sc/source/filter/inc/tablecolumnsbuffer.hxx
new file mode 100644
index 000000000..e1e340cbe
--- /dev/null
+++ b/sc/source/filter/inc/tablecolumnsbuffer.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+class ScDBData;
+
+namespace oox::xls {
+
+/** A column in a table (database range).
+ */
+class TableColumn : public WorkbookHelper
+{
+public:
+ explicit TableColumn( const WorkbookHelper& rHelper );
+
+ /** Imports table column settings from the tableColumn element. */
+ void importTableColumn( const AttributeList& rAttribs );
+ /** Imports table column settings from the TABLECOLUMN (?) record. */
+ void importTableColumn( SequenceInputStream& rStrm );
+ /** Gets the name of this column. */
+ const OUString& getName() const;
+
+private:
+ OUString maName;
+ sal_Int32 mnId;
+ sal_Int32 mnDataDxfId;
+};
+
+class TableColumns : public WorkbookHelper
+{
+public:
+ explicit TableColumns( const WorkbookHelper& rHelper );
+
+ /** Imports settings from the tableColumns element. */
+ void importTableColumns( const AttributeList& rAttribs );
+ /** Imports settings from the TABLECOLUMNS (?) record. */
+ void importTableColumns( SequenceInputStream& rStrm );
+
+ /** Creates a new table column and stores it internally. */
+ TableColumn& createTableColumn();
+
+ /** Applies the columns to the passed database range. */
+ bool finalizeImport( ScDBData* pDBData );
+
+private:
+ typedef RefVector< TableColumn > TableColumnVector;
+
+ TableColumnVector maTableColumnVector;
+ sal_Int32 mnCount;
+};
+
+class TableColumnsBuffer : public WorkbookHelper
+{
+public:
+ explicit TableColumnsBuffer( const WorkbookHelper& rHelper );
+
+ /** Creates a new table columns object and stores it internally. */
+ TableColumns& createTableColumns();
+
+ /** Applies the table columns to the passed database range. */
+ void finalizeImport( ScDBData* pDBData );
+
+private:
+ /** Returns the table columns object used. */
+ TableColumns* getActiveTableColumns();
+
+private:
+ typedef RefVector< TableColumns > TableColumnsVector;
+ TableColumnsVector maTableColumnsVector;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tablecolumnscontext.hxx b/sc/source/filter/inc/tablecolumnscontext.hxx
new file mode 100644
index 000000000..1f5a5ba04
--- /dev/null
+++ b/sc/source/filter/inc/tablecolumnscontext.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class TableColumn;
+class TableColumns;
+
+class TableColumnContext : public WorksheetContextBase
+{
+public:
+ explicit TableColumnContext( WorksheetContextBase& rParent, TableColumn& rTableColumn );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ TableColumn& mrTableColumn;
+};
+
+class TableColumnsContext : public WorksheetContextBase
+{
+public:
+ explicit TableColumnsContext( WorksheetFragmentBase& rFragment, TableColumns& rTableColumns );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onStartElement( const AttributeList& rAttribs ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+ virtual void onStartRecord( SequenceInputStream& rStrm ) override;
+
+private:
+ TableColumns& mrTableColumns;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tablefragment.hxx b/sc/source/filter/inc/tablefragment.hxx
new file mode 100644
index 000000000..7aad73e03
--- /dev/null
+++ b/sc/source/filter/inc/tablefragment.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class Table;
+
+class TableFragment : public WorksheetFragmentBase
+{
+public:
+ explicit TableFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+
+private:
+ Table& mrTable;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/themebuffer.hxx b/sc/source/filter/inc/themebuffer.hxx
new file mode 100644
index 000000000..f1443a073
--- /dev/null
+++ b/sc/source/filter/inc/themebuffer.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <oox/drawingml/theme.hxx>
+#include "workbookhelper.hxx"
+#include "stylesbuffer.hxx"
+
+namespace oox::xls {
+
+class ThemeBuffer : public ::oox::drawingml::Theme, public WorkbookHelper
+{
+public:
+ explicit ThemeBuffer( const WorkbookHelper& rHelper );
+ virtual ~ThemeBuffer() override;
+
+ /** Returns the theme color with the specified token identifier. */
+ ::Color getColorByToken( sal_Int32 nToken ) const;
+
+ ::Color getColorByIndex(size_t nIndex) const;
+
+ /** Returns the default font data for the current file type. */
+ const FontModel& getDefaultFontModel() const { return *mxDefFontModel; }
+
+private:
+ typedef ::std::unique_ptr< FontModel > FontModelPtr;
+ FontModelPtr mxDefFontModel;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tokstack.hxx b/sc/source/filter/inc/tokstack.hxx
new file mode 100644
index 000000000..03301d510
--- /dev/null
+++ b/sc/source/filter/inc/tokstack.hxx
@@ -0,0 +1,426 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tokenarray.hxx>
+#include <refdata.hxx>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <vector>
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+typedef OpCode DefTokenId;
+// in PRODUCT version: ambiguity between OpCode (being sal_uInt16) and UINT16
+// Unfortunately a typedef is just a dumb alias and not a real type ...
+//typedef sal_uInt16 TokenId;
+struct TokenId
+{
+ sal_uInt16 nId;
+
+ TokenId() : nId( 0 ) {}
+ TokenId( sal_uInt16 n ) : nId( n ) {}
+ TokenId( const TokenId& r ) : nId( r.nId ) {}
+ TokenId& operator =( const TokenId& r ) { nId = r.nId; return *this; }
+ TokenId& operator =( sal_uInt16 n ) { nId = n; return *this; }
+ operator const sal_uInt16&() const { return nId; }
+};
+
+class TokenStack;
+
+enum E_TYPE
+{
+ T_Id, // Id-Folge
+ T_Str, // String
+ T_D, // Double
+ T_Err, // Error code
+ T_RefC, // Cell Reference
+ T_RefA, // Area Reference
+ T_RN, // Range Name
+ T_Ext, // something unknown with function name
+ T_Nlf, // token for natural language formula
+ T_Matrix, // token for inline arrays
+ T_ExtName, // token for external names
+ T_ExtRefC,
+ T_ExtRefA
+};
+
+template<typename T, int InitialCapacity>
+struct TokenPoolPool
+{
+ std::unique_ptr<T[]> ppP_Str;
+ sal_uInt16 m_capacity;
+ sal_uInt16 m_writemark;
+
+ TokenPoolPool() :
+ ppP_Str( new T[InitialCapacity] ),
+ m_capacity(InitialCapacity),
+ m_writemark(0)
+ {
+ }
+ bool Grow(sal_uInt16 nByMin = 1)
+ {
+ sal_uInt16 nP_StrNew = lcl_canGrow(m_capacity, nByMin);
+ if (!nP_StrNew)
+ return false;
+
+ T* ppP_StrNew = new T[ nP_StrNew ];
+
+ for( sal_uInt16 i = 0 ; i < m_capacity ; i++ )
+ ppP_StrNew[ i ] = std::move(ppP_Str[ i ]);
+
+ m_capacity = nP_StrNew;
+
+ ppP_Str.reset( ppP_StrNew );
+ return true;
+ }
+ /** Returns the new number of elements, or 0 if overflow. */
+ static sal_uInt16 lcl_canGrow( sal_uInt16 nOld, sal_uInt16 nByMin )
+ {
+ if (!nOld)
+ return nByMin ? nByMin : 1;
+ if (nOld == SAL_MAX_UINT16)
+ return 0;
+ sal_uInt32 nNew = ::std::max( static_cast<sal_uInt32>(nOld) * 2,
+ static_cast<sal_uInt32>(nOld) + nByMin);
+ if (nNew > SAL_MAX_UINT16)
+ nNew = SAL_MAX_UINT16;
+ if (nNew - nByMin < nOld)
+ nNew = 0;
+ return static_cast<sal_uInt16>(nNew);
+ }
+ T* getIfInRange(sal_uInt16 n) const
+ {
+ return ( n < m_capacity ) ? &ppP_Str[ n ] : nullptr;
+ }
+ T const & operator[](sal_uInt16 n) const
+ {
+ return ppP_Str[ n ];
+ }
+ T & operator[](sal_uInt16 n)
+ {
+ return ppP_Str[ n ];
+ }
+};
+
+class TokenPool
+{
+ // !ATTENTION!: external Id-Basis is 1, internal 0!
+ // return Id = 0 -> Error
+private:
+ svl::SharedStringPool& mrStringPool;
+
+ TokenPoolPool<std::unique_ptr<OUString>, 4>
+ ppP_Str; // Pool for Strings
+
+ TokenPoolPool<double, 8> pP_Dbl; // Pool for Doubles
+
+ TokenPoolPool<sal_uInt16, 8>
+ pP_Err; // Pool for error codes
+
+ TokenPoolPool<std::unique_ptr<ScSingleRefData>, 32>
+ ppP_RefTr; // Pool for References
+ std::unique_ptr<sal_uInt16[]> pP_Id; // Pool for Id-sets
+ sal_uInt16 nP_Id;
+ sal_uInt16 nP_IdCurrent;
+ sal_uInt16 nP_IdLast; // last set-start
+
+ struct EXTCONT
+ {
+ DefTokenId eId;
+ OUString aText;
+ EXTCONT( const DefTokenId e, const OUString& r ) :
+ eId( e ), aText( r ){}
+ };
+ TokenPoolPool<std::unique_ptr<EXTCONT>, 32>
+ ppP_Ext;
+
+ TokenPoolPool<std::unique_ptr<ScSingleRefData>, 16>
+ ppP_Nlf;
+
+ std::unique_ptr<ScMatrix*[]> ppP_Matrix; // Pool for Matrices
+ sal_uInt16 nP_Matrix;
+ sal_uInt16 nP_MatrixCurrent;
+
+ /** for storage of named ranges */
+ struct RangeName
+ {
+ sal_uInt16 mnIndex;
+ sal_Int16 mnSheet;
+ };
+ ::std::vector<RangeName> maRangeNames;
+
+ /** for storage of external names */
+ struct ExtName
+ {
+ sal_uInt16 mnFileId;
+ OUString maName;
+ };
+ ::std::vector<ExtName> maExtNames;
+
+ /** for storage of external cell references */
+ struct ExtCellRef
+ {
+ OUString maTabName;
+ ScSingleRefData maRef;
+ sal_uInt16 mnFileId;
+ };
+ ::std::vector<ExtCellRef> maExtCellRefs;
+
+ /** for storage of external area references */
+ struct ExtAreaRef
+ {
+ OUString maTabName;
+ ScComplexRefData maRef;
+ sal_uInt16 mnFileId;
+ };
+ ::std::vector<ExtAreaRef> maExtAreaRefs;
+
+ std::unique_ptr<sal_uInt16[]> pElement; // Array with Indices for elements
+ std::unique_ptr<E_TYPE[]> pType; // ...with Type-Info
+ std::unique_ptr<sal_uInt16[]> pSize; // ...with size
+ sal_uInt16 nElement;
+ sal_uInt16 nElementCurrent;
+
+ static const sal_uInt16 nScTokenOff;// Offset for SC-Token
+#ifdef DBG_UTIL
+ sal_uInt16 m_nRek; // recursion counter
+#endif
+
+ bool GrowTripel( sal_uInt16 nByMin );
+ bool GrowId();
+ bool GrowElement();
+ bool GrowMatrix();
+ /** @return false means nElementCurrent range
+ below nScTokenOff would overflow or
+ further allocation is not possible, no
+ new ID available other than
+ nElementCurrent+1.
+ */
+ bool CheckElementOrGrow();
+ bool GetElement( const sal_uInt16 nId, ScTokenArray* pScToken );
+ bool GetElementRek( const sal_uInt16 nId, ScTokenArray* pScToken );
+ void ClearMatrix();
+public:
+ TokenPool( svl::SharedStringPool& rSPool );
+ ~TokenPool();
+ inline TokenPool& operator <<( const TokenId& rId );
+ inline TokenPool& operator <<( const DefTokenId eId );
+ inline TokenPool& operator <<( TokenStack& rStack );
+ void operator >>( TokenId& rId );
+ inline void operator >>( TokenStack& rStack );
+ inline TokenId Store();
+ TokenId Store( const double& rDouble );
+
+ // only for Range-Names
+ TokenId Store( const sal_uInt16 nIndex );
+
+ TokenId Store( const OUString& rString );
+ TokenId Store( const ScSingleRefData& rTr );
+ TokenId Store( const ScComplexRefData& rTr );
+
+ TokenId Store( const DefTokenId eId, const OUString& rName );
+ // 4 externals (e.g. AddIns, Macros...)
+ TokenId StoreNlf( const ScSingleRefData& rTr );
+ TokenId StoreMatrix();
+ TokenId StoreName( sal_uInt16 nIndex, sal_Int16 nSheet );
+ TokenId StoreExtName( sal_uInt16 nFileId, const OUString& rName );
+ TokenId StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef );
+ TokenId StoreExtRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef );
+
+ std::unique_ptr<ScTokenArray> GetTokenArray( const ScDocument& rDoc, const TokenId& rId );
+ void Reset();
+ bool IsSingleOp( const TokenId& rId, const DefTokenId eId ) const;
+ const OUString* GetExternal( const TokenId& rId ) const;
+ ScMatrix* GetMatrix( unsigned int n ) const;
+};
+
+class TokenStack
+ // Stack for Token-Ids: reserve Id=0 for error; e.g. Get() returns 0 on error
+
+{
+ private:
+ std::unique_ptr<TokenId[]> pStack; // Stack as Array
+ sal_uInt16 nPos; // Write-mark
+ static const sal_uInt16 nSize = 1024; // first Index outside of stack
+ public:
+ TokenStack();
+ ~TokenStack();
+ inline TokenStack& operator <<( const TokenId& rNewId );
+ inline void operator >>( TokenId &rId );
+
+ inline void Reset();
+
+ bool HasMoreTokens() const { return nPos > 0; }
+ inline TokenId Get();
+};
+
+inline TokenId TokenStack::Get()
+{
+ TokenId nRet;
+
+ if( nPos == 0 )
+ {
+ SAL_WARN("sc.filter", "*TokenStack::Get(): is empty, is empty, ...");
+ nRet = 0;
+ }
+ else
+ {
+ nPos--;
+ nRet = pStack[ nPos ];
+ }
+
+ return nRet;
+}
+
+inline TokenStack &TokenStack::operator <<( const TokenId& rNewId )
+{// Element on Stack
+ if( nPos < nSize )
+ {
+ pStack[ nPos ] = rNewId;
+ nPos++;
+ }
+ else
+ {
+ SAL_WARN("sc.filter", "*TokenStack::<<(): Stack overflow for " << static_cast<sal_uInt16>(rNewId));
+ }
+
+ return *this;
+}
+
+inline void TokenStack::operator >>( TokenId& rId )
+{// Element of Stack
+ if( nPos > 0 )
+ {
+ nPos--;
+ rId = pStack[ nPos ];
+ }
+ else
+ {
+ SAL_WARN("sc.filter", "*TokenStack::>>(): is empty, is empty, ...");
+ rId = 0;
+ }
+}
+
+inline void TokenStack::Reset()
+{
+ nPos = 0;
+}
+
+inline TokenPool& TokenPool::operator <<( const TokenId& rId )
+{
+ // POST: rId's are stored consecutively in Pool under a new Id;
+ // finalize with >> or Store()
+ // rId -> ( sal_uInt16 ) rId - 1;
+ sal_uInt16 nId = static_cast<sal_uInt16>(rId);
+ if (nId == 0)
+ {
+ // This would result in nId-1==0xffff, create error.
+ SAL_WARN("sc.filter", "-TokenPool::operator <<: TokenId 0");
+ nId = static_cast<sal_uInt16>(ocErrNull) + nScTokenOff + 1;
+ }
+ else if (nId >= nScTokenOff)
+ {
+ SAL_WARN("sc.filter", "-TokenPool::operator <<: TokenId in DefToken-Range! " << static_cast<sal_uInt16>(rId));
+
+ // Do not "invent" OpCode values by arbitrarily mapping into the Calc
+ // space. This badly smells like an overflow or binary garbage, so
+ // treat as error.
+ nId = static_cast<sal_uInt16>(ocErrNull) + nScTokenOff + 1;
+ }
+
+ if( nP_IdCurrent >= nP_Id && !GrowId())
+ return *this;
+
+ pP_Id[ nP_IdCurrent ] = nId - 1;
+ nP_IdCurrent++;
+
+ return *this;
+}
+
+inline TokenPool& TokenPool::operator <<( const DefTokenId eId )
+{
+ if (static_cast<sal_uInt32>(eId) + nScTokenOff >= 0xFFFF)
+ {
+ SAL_WARN("sc.filter", "-TokenPool::operator<<: enum too large! " << static_cast<sal_uInt32>(eId));
+ }
+
+ if( nP_IdCurrent >= nP_Id && !GrowId())
+ return *this;
+
+ pP_Id[ nP_IdCurrent ] = static_cast<sal_uInt16>(eId) + nScTokenOff;
+ nP_IdCurrent++;
+
+ return *this;
+}
+
+inline TokenPool& TokenPool::operator <<( TokenStack& rStack )
+{
+ if( nP_IdCurrent >= nP_Id && !GrowId())
+ return *this;
+
+ sal_uInt16 nId = static_cast<sal_uInt16>(rStack.Get());
+ if (nId == 0)
+ {
+ // Indicates error, so generate one. Empty stack, overflow, ...
+ nId = static_cast<sal_uInt16>(ocErrNull) + nScTokenOff + 1;
+ }
+ pP_Id[ nP_IdCurrent ] = nId - 1;
+ nP_IdCurrent++;
+
+ return *this;
+}
+
+inline void TokenPool::operator >>( TokenStack& rStack )
+{
+ TokenId nId;
+ *this >> nId;
+ rStack << nId;
+}
+
+inline TokenId TokenPool::Store()
+{
+ TokenId nId;
+ *this >> nId;
+ return nId;
+}
+
+inline std::unique_ptr<ScTokenArray> TokenPool::GetTokenArray( const ScDocument& rDoc, const TokenId& rId )
+{
+ std::unique_ptr<ScTokenArray> pScToken( new ScTokenArray(rDoc) );
+
+ if( rId )
+ {//...only if rId > 0!
+#ifdef DBG_UTIL
+ m_nRek = 0;
+#endif
+ GetElement( static_cast<sal_uInt16>(rId) - 1, pScToken.get());
+ }
+
+ return pScToken;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/tool.h b/sc/source/filter/inc/tool.h
new file mode 100644
index 000000000..786e7f669
--- /dev/null
+++ b/sc/source/filter/inc/tool.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <document.hxx>
+
+#include <i18nlangtag/lang.h>
+#include <svl/intitem.hxx>
+#include <types.hxx>
+#include <osl/diagnose.h>
+
+// Default values
+const sal_uInt8 nFractionalStd = 0; // Number of digits in fractional part for standard cells
+const sal_uInt8 nFractionalFloat = 2; // " " " " float cells
+
+struct LotusContext;
+
+void PutFormString(LotusContext& rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, char *pString);
+
+void SetFormat(LotusContext& rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt8 nFormat, sal_uInt8 nSt);
+
+double SnumToDouble( sal_Int16 nVal );
+
+double Snum32ToDouble( sal_uInt32 nValue );
+
+typedef sal_uInt16 StampTyp;
+
+#define MAKE_STAMP(nF,nS) ((nS&0x0F)+((nF&0x7F)*16))
+ // Bit 0...3 = Bit 0...3 of number of digits
+ // Bit 4...10 = Bit 0...6 of Formatbyte
+
+class FormIdent
+{
+private:
+ StampTyp nStamp; // ID key
+ std::unique_ptr<SfxUInt32Item> pAttr; // associated attribute
+public:
+ FormIdent( void )
+ {
+ nStamp = 0;
+ pAttr = nullptr;
+ }
+
+ FormIdent( sal_uInt8 nFormat, sal_uInt8 nSt, SfxUInt32Item& rAttr )
+ {
+ nStamp = MAKE_STAMP( nFormat, nSt );
+ pAttr.reset(&rAttr);
+ }
+
+ StampTyp GetStamp( void ) const
+ {
+ return nStamp;
+ }
+
+ SfxUInt32Item* GetAttr( void )
+ {
+ return pAttr.get();
+ }
+
+ void SetStamp( sal_uInt8 nFormat, sal_uInt8 nSt )
+ {
+ nStamp = MAKE_STAMP( nFormat, nSt );
+ }
+};
+
+
+#define nSize_ 2048
+
+
+class FormCache
+{
+private:
+ FormIdent aIdents[ nSize_ ]; //buffered formats
+ bool bValid[ nSize_ ];
+ FormIdent aCompareIdent; // for comparing
+ SvNumberFormatter* pFormTable; // value format table anchor
+ StampTyp nIndex;
+ LanguageType eLanguage; // System language
+
+ SfxUInt32Item* NewAttr( sal_uInt8 nFormat, sal_uInt8 nSt );
+public:
+ FormCache( const ScDocument* );
+ ~FormCache();
+
+ inline const SfxUInt32Item* GetAttr( sal_uInt8 nFormat, sal_uInt8 nSt );
+};
+
+
+inline const SfxUInt32Item* FormCache::GetAttr( sal_uInt8 nFormat, sal_uInt8 nSt )
+{
+ // PREC: nFormat = Lotus format byte
+ // nSt = Number of digit
+ // POST: return = SC-format fitting nFormat and nSt
+ SfxUInt32Item* pAttr;
+ SfxUInt32Item* pRet;
+
+ aCompareIdent.SetStamp( nFormat, nSt );
+ nIndex = aCompareIdent.GetStamp();
+ OSL_ENSURE( nIndex < nSize_, "FormCache::GetAttr(): Oups... not this way!" );
+ if( bValid[ nIndex ] )
+ pRet = aIdents[ nIndex ].GetAttr();
+ else
+ {
+ // create new attribute
+ pAttr = NewAttr( nFormat, nSt );
+ OSL_ENSURE( pAttr, "FormCache::GetAttr(): Nothing to save" );
+
+ aIdents[ nIndex ] = FormIdent( nFormat, nSt, *pAttr );
+ bValid[ nIndex ] = true;
+
+ pRet = pAttr;
+ }
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/unitconverter.hxx b/sc/source/filter/inc/unitconverter.hxx
new file mode 100644
index 000000000..f418eaaf0
--- /dev/null
+++ b/sc/source/filter/inc/unitconverter.hxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <vector>
+#include <o3tl/enumarray.hxx>
+#include "workbookhelper.hxx"
+
+namespace com::sun::star {
+ namespace util { struct Date; struct DateTime; }
+}
+
+namespace oox::xls {
+
+/** Units supported by the UnitConverter class. */
+enum class Unit
+{
+ Twip, /// Twips (1/20 point).
+ Emu, /// English Metric Unit (1/360,000 cm).
+ ScreenX, /// Horizontal screen pixels.
+ ScreenY, /// Vertical screen pixels.
+ Digit, /// Digit width of document default font.
+ Space, /// Space character width of document default font.
+ LAST
+};
+
+/** Helper class that provides functions to convert values from and to
+ different units.
+
+ Provides functions to calculate the width of certain characters of the
+ default font of the imported/exported document. The default font is always
+ the first font in the styles font list, and is always referenced by the
+ default cell style ("Normal" style in Excel) which is used by all empty
+ unformatted cells in the document. To be able to calculate the character
+ width correctly, the default font must be known, which is the case after
+ the finalizeImport() or finalizeExport() functions have been called. Caller
+ must make sure to not call the character width conversion functions before.
+ */
+class UnitConverter : public WorkbookHelper
+{
+public:
+ explicit UnitConverter( const WorkbookHelper& rHelper );
+
+ /** Final processing after import of all style settings. */
+ void finalizeImport();
+ /** Updates internal nulldate for date/serial conversion. */
+ void finalizeNullDate( const css::util::Date& rNullDate );
+
+ /** Converts the passed value between the passed units. */
+ double scaleValue( double fValue, Unit eFromUnit, Unit eToUnit ) const;
+
+ /** Returns the serial value of the passed datetime, based on current nulldate. */
+ double calcSerialFromDateTime( const css::util::DateTime& rDateTime ) const;
+ /** Returns the datetime of the passed serial value, based on current nulldate. */
+ css::util::DateTime calcDateTimeFromSerial( double fSerial ) const;
+
+ /** Returns a BIFF error code from the passed error string. */
+ sal_uInt8 calcBiffErrorCode( const OUString& rErrorCode ) const;
+
+ /** Returns an error string from the passed BIFF error code. */
+ OUString calcErrorString( sal_uInt8 nErrorCode ) const;
+
+private:
+ /** Adds an error code to the internal maps. */
+ void addErrorCode( sal_uInt8 nErrorCode, const OUString& rErrorCode );
+
+ /** Returns the conversion coefficient for the passed unit. */
+ double getCoefficient( Unit eUnit ) const;
+
+private:
+ o3tl::enumarray<Unit, double> maCoeffs; /// Coefficients for unit conversion.
+ std::map<OUString, sal_uInt8> maOoxErrCodes; /// Maps error code strings to BIFF error constants.
+ sal_Int32 mnNullDate; /// Nulldate of this workbook (number of days since 0000-01-01).
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/viewsettings.hxx b/sc/source/filter/inc/viewsettings.hxx
new file mode 100644
index 000000000..2382f5b54
--- /dev/null
+++ b/sc/source/filter/inc/viewsettings.hxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rangelst.hxx>
+#include "stylesbuffer.hxx"
+#include "worksheethelper.hxx"
+#include "workbookhelper.hxx"
+
+namespace oox::xls {
+
+/** Contains all settings for a selection in a single pane of a sheet. */
+struct PaneSelectionModel
+{
+ ScAddress maActiveCell; /// Position of active cell (cursor).
+ ScRangeList maSelection; /// Selected cell ranges.
+ sal_Int32 mnActiveCellId; /// Index of active cell in selection list.
+
+ explicit PaneSelectionModel();
+};
+
+/** Contains all view settings for a single sheet. */
+struct SheetViewModel
+{
+ typedef RefMap< sal_Int32, PaneSelectionModel > PaneSelectionModelMap;
+
+ PaneSelectionModelMap maPaneSelMap; /// Selections of all panes.
+ Color maGridColor; /// Grid color.
+ ScAddress maFirstPos; /// First visible cell.
+ ScAddress maSecondPos; /// First visible cell in additional panes.
+ sal_Int32 mnWorkbookViewId; /// Index into list of workbookView elements.
+ sal_Int32 mnViewType; /// View type (normal, page break, layout).
+ sal_Int32 mnActivePaneId; /// Active pane (with cell cursor).
+ sal_Int32 mnPaneState; /// Pane state (frozen, split).
+ double mfSplitX; /// Split X position (twips), or number of frozen columns.
+ double mfSplitY; /// Split Y position (twips), or number of frozen rows.
+ sal_Int32 mnCurrentZoom; /// Zoom factor for current view.
+ sal_Int32 mnNormalZoom; /// Zoom factor for normal view.
+ sal_Int32 mnSheetLayoutZoom; /// Zoom factor for pagebreak preview.
+ sal_Int32 mnPageLayoutZoom; /// Zoom factor for page layout view.
+ bool mbSelected; /// True = sheet is selected.
+ bool mbRightToLeft; /// True = sheet in right-to-left mode.
+ bool mbDefGridColor; /// True = default grid color.
+ bool mbShowFormulas; /// True = show formulas instead of results.
+ bool mbShowGrid; /// True = show cell grid.
+ bool mbShowHeadings; /// True = show column/row headings.
+ bool mbShowZeros; /// True = show zero value zells.
+ bool mbShowOutline; /// True = show outlines.
+ bool mbZoomToFit; /// True = zoom chart sheet to fit window.
+
+ explicit SheetViewModel();
+
+ /** Returns true, if page break preview is active. */
+ bool isPageBreakPreview() const;
+ /** Returns the zoom in normal view (returns default, if current value is 0). */
+ sal_Int32 getNormalZoom() const;
+ /** Returns the zoom in pagebreak preview (returns default, if current value is 0). */
+ sal_Int32 getPageBreakZoom() const;
+ /** Returns the grid color as RGB value. */
+ ::Color getGridColor( const ::oox::core::FilterBase& rFilter ) const;
+
+ /** Returns the selection data of the active pane. */
+ const PaneSelectionModel* getActiveSelection() const;
+ /** Returns read/write access to the selection data of the specified pane. */
+ PaneSelectionModel& createPaneSelection( sal_Int32 nPaneId );
+};
+
+typedef std::shared_ptr< SheetViewModel > SheetViewModelRef;
+
+class SheetViewSettings : public WorksheetHelper
+{
+public:
+ explicit SheetViewSettings( const WorksheetHelper& rHelper );
+
+ /** Imports the sheetView element containing sheet view settings. */
+ void importSheetView( const AttributeList& rAttribs );
+ /** Imports the pane element containing sheet pane settings. */
+ void importPane( const AttributeList& rAttribs );
+ /** Imports the selection element containing selection settings for a pane. */
+ void importSelection( const AttributeList& rAttribs );
+ /** Imports the sheetView element containing view settings of a chart sheet. */
+ void importChartSheetView( const AttributeList& rAttribs );
+
+ /** Imports the SHEETVIEW record containing sheet view settings. */
+ void importSheetView( SequenceInputStream& rStrm );
+ /** Imports the PANE record containing sheet pane settings. */
+ void importPane( SequenceInputStream& rStrm );
+ /** Imports the SELECTION record containing selection settings for a pane. */
+ void importSelection( SequenceInputStream& rStrm );
+ /** Imports the CHARTSHEETVIEW record containing view settings of a chart sheet. */
+ void importChartSheetView( SequenceInputStream& rStrm );
+
+ /** Converts all imported sheet view settings. */
+ void finalizeImport();
+
+ /** Returns true, if the sheet layout is set to right-to-left. */
+ bool isSheetRightToLeft() const;
+
+private:
+ SheetViewModelRef createSheetView();
+
+private:
+ typedef RefVector< SheetViewModel > SheetViewModelVec;
+ SheetViewModelVec maSheetViews;
+};
+
+/** Contains all view settings for the entire document. */
+struct WorkbookViewModel
+{
+ sal_Int32 mnWinX; /// X position of the workbook window (twips).
+ sal_Int32 mnWinY; /// Y position of the workbook window (twips).
+ sal_Int32 mnWinWidth; /// Width of the workbook window (twips).
+ sal_Int32 mnWinHeight; /// Height of the workbook window (twips).
+ sal_Int32 mnActiveSheet; /// Displayed (active) sheet.
+ sal_Int32 mnFirstVisSheet; /// First visible sheet in sheet tabbar.
+ sal_Int32 mnTabBarWidth; /// Width of sheet tabbar (1/1000 of window width).
+ sal_Int32 mnVisibility; /// Visibility state of workbook window.
+ bool mbShowTabBar; /// True = show sheet tabbar.
+ bool mbShowHorScroll; /// True = show horizontal sheet scrollbars.
+ bool mbShowVerScroll; /// True = show vertical sheet scrollbars.
+ bool mbMinimized; /// True = workbook window is minimized.
+
+ explicit WorkbookViewModel();
+};
+
+typedef std::shared_ptr< WorkbookViewModel > WorkbookViewModelRef;
+
+class ViewSettings : public WorkbookHelper
+{
+public:
+ explicit ViewSettings( const WorkbookHelper& rHelper );
+
+ /** Imports the workbookView element containing workbook view settings. */
+ void importWorkbookView( const AttributeList& rAttribs );
+ /** Imports the oleSize element containing the visible size of the workbook. */
+ void importOleSize( const AttributeList& rAttribs );
+ /** Imports the WORKBOOKVIEW record containing workbook view settings. */
+ void importWorkbookView( SequenceInputStream& rStrm );
+ /** Imports the OLESIZE record containing the visible size of the workbook. */
+ void importOleSize( SequenceInputStream& rStrm );
+
+ /** Stores converted view settings for a specific worksheet. */
+ void setSheetViewSettings( sal_Int16 nSheet,
+ const SheetViewModelRef& rxSheetView,
+ const css::uno::Any& rProperties );
+ /** Stores the used area for a specific worksheet. */
+ void setSheetUsedArea( const ScRange& rUsedArea );
+
+ /** Converts all imported document view settings. */
+ void finalizeImport();
+
+ /** Returns the Calc index of the active sheet. */
+ sal_Int16 getActiveCalcSheet() const;
+
+private:
+ WorkbookViewModel& createWorkbookView();
+
+private:
+ typedef RefVector< WorkbookViewModel > WorkbookViewModelVec;
+ typedef RefMap< sal_Int16, SheetViewModel > SheetViewModelMap;
+
+ WorkbookViewModelVec maBookViews; /// Workbook view models.
+ SheetViewModelMap maSheetViews; /// Active view model for each sheet.
+ std::map< sal_Int16, css::uno::Any > maSheetProps; /// Converted property sequences for each sheet.
+ std::map< sal_Int16, ScRange > maSheetUsedAreas; /// Used area (cell range) of every sheet.
+ ScRange maOleSize; /// Visible area if this is an embedded OLE object.
+ bool mbValidOleSize; /// True = imported OLE size is a valid cell range.
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/workbookfragment.hxx b/sc/source/filter/inc/workbookfragment.hxx
new file mode 100644
index 000000000..f2556678b
--- /dev/null
+++ b/sc/source/filter/inc/workbookfragment.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "defnamesbuffer.hxx"
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class ExternalLink;
+
+class WorkbookFragment : public WorkbookFragmentBase
+{
+public:
+ explicit WorkbookFragment(
+ const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void finalizeImport() override;
+
+private:
+ void importExternalReference( const AttributeList& rAttribs );
+ void importDefinedName( const AttributeList& rAttribs );
+ void importPivotCache( const AttributeList& rAttribs );
+
+ void importExternalRef( SequenceInputStream& rStrm );
+ void importPivotCache( SequenceInputStream& rStrm );
+
+ void importExternalLinkFragment( ExternalLink& rExtLink );
+ void importPivotCacheDefFragment( const OUString& rRelId, sal_Int32 nCacheId );
+
+ void recalcFormulaCells();
+
+private:
+ DefinedNameRef mxCurrName;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/workbookhelper.hxx b/sc/source/filter/inc/workbookhelper.hxx
new file mode 100644
index 000000000..0234c5025
--- /dev/null
+++ b/sc/source/filter/inc/workbookhelper.hxx
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <string_view>
+
+#include <oox/helper/storagebase.hxx>
+#include <address.hxx>
+
+namespace oox::drawingml::chart { class ChartConverter; }
+namespace rtl { template <class reference_type> class Reference; }
+
+namespace com::sun::star {
+ namespace container { class XNameContainer; }
+ namespace sheet { class XDatabaseRange; }
+ namespace sheet { class XSpreadsheet; }
+ namespace sheet { class XSpreadsheetDocument; }
+ namespace sheet { struct FormulaToken; }
+ namespace style { class XStyle; }
+ namespace table { class XCellRange; }
+}
+
+namespace oox {
+ class SegmentProgressBar;
+}
+
+namespace oox::core {
+ class FilterBase;
+ class FragmentHandler;
+ class XmlFilterBase;
+ class FastParser;
+}
+
+class ScDocument;
+class ScDocumentImport;
+class ScEditEngineDefaulter;
+class ScDBData;
+class ScRangeData;
+
+namespace oox::xls {
+
+class ExcelFilter;
+
+/** Functor for case-insensitive string comparison, usable in maps etc. */
+struct IgnoreCaseCompare
+{
+ bool operator()( std::u16string_view rName1, std::u16string_view rName2 ) const;
+};
+
+class AddressConverter;
+class ConnectionsBuffer;
+class DefinedNamesBuffer;
+class ExternalLinkBuffer;
+class FormulaParser;
+class PageSettingsConverter;
+class PivotCacheBuffer;
+class PivotTableBuffer;
+class ScenarioBuffer;
+class SharedStringsBuffer;
+class StylesBuffer;
+class TableBuffer;
+class ThemeBuffer;
+class UnitConverter;
+class ViewSettings;
+class WorkbookSettings;
+class WorksheetBuffer;
+class FormulaBuffer;
+
+class WorkbookGlobals;
+typedef std::shared_ptr< WorkbookGlobals > WorkbookGlobalsRef;
+
+/** Helper class to provide access to global workbook data.
+
+ All classes derived from this helper class will have access to a singleton
+ object of type WorkbookGlobals containing global workbook settings,
+ buffers, converters, etc. Nearly all classes in this filter implementation
+ are derived directly or indirectly from this class.
+
+ This class contains just a simple reference to the WorkbookGlobals object
+ to prevent circular references, as the WorkbookGlobals object contains a
+ lot of objects derived from this class.
+ */
+class WorkbookHelper
+{
+public:
+ /*implicit*/ WorkbookHelper( WorkbookGlobals& rBookGlob ) : mrBookGlob( rBookGlob ) {}
+ virtual ~WorkbookHelper();
+
+ WorkbookHelper(WorkbookHelper const &) = default;
+ WorkbookHelper(WorkbookHelper &&) = default;
+ WorkbookHelper & operator =(WorkbookHelper const &) = delete; // due to mrBookGlob
+ WorkbookHelper & operator =(WorkbookHelper &&) = delete; // due to mrBookGlob
+
+ static WorkbookGlobalsRef constructGlobals( ExcelFilter& rFilter );
+
+ // filter -----------------------------------------------------------------
+
+ /** Returns the base filter object (base class of all filters). */
+ ::oox::core::FilterBase& getBaseFilter() const;
+ /** Returns the filter progress bar. */
+ SegmentProgressBar& getProgressBar() const;
+ /** Returns the index of the current Calc sheet, if filter currently processes a sheet. */
+ sal_Int16 getCurrentSheetIndex() const;
+ /** Returns true when reading a file generated by a known good generator. */
+ bool isGeneratorKnownGood() const;
+
+ /** Sets the VBA project storage used to import VBA source code and forms. */
+ void setVbaProjectStorage( const StorageRef& rxVbaPrjStrg );
+ /** Sets the index of the current Calc sheet, if filter currently processes a sheet. */
+ void setCurrentSheetIndex( SCTAB nSheet );
+ /** Final conversion after importing the workbook. */
+ void finalizeWorkbookImport();
+ void useInternalChartDataTable( bool bInternal );
+
+ // document model ---------------------------------------------------------
+ ScDocument& getScDocument();
+ const ScDocument& getScDocument() const;
+
+ ScDocumentImport& getDocImport();
+ const ScDocumentImport& getDocImport() const;
+
+ ScEditEngineDefaulter& getEditEngine() const;
+ /** Returns a reference to the source/target spreadsheet document model. */
+ const css::uno::Reference< css::sheet::XSpreadsheetDocument >&
+ getDocument() const;
+
+ /** Returns a reference to the specified spreadsheet in the document model. */
+ css::uno::Reference< css::sheet::XSpreadsheet >
+ getSheetFromDoc( sal_Int32 nSheet ) const;
+ /** Returns a reference to the specified spreadsheet in the document model. */
+ css::uno::Reference< css::sheet::XSpreadsheet >
+ getSheetFromDoc( const OUString& rSheet ) const;
+
+ /** Returns the XCellRange interface for the passed cell range address. */
+ css::uno::Reference< css::table::XCellRange >
+ getCellRangeFromDoc( const ScRange& rRange ) const;
+
+ /** Returns the cell styles container from the Calc document. */
+ css::uno::Reference< css::container::XNameContainer >
+ getCellStyleFamily() const;
+ /** Returns the specified cell or page style from the Calc document. */
+ css::uno::Reference< css::style::XStyle >
+ getStyleObject( const OUString& rStyleName, bool bPageStyle ) const;
+
+ // second is true if ownership belongs to the caller
+ typedef std::pair<ScRangeData*, bool> RangeDataRet;
+
+ /** Creates and returns a defined name on-the-fly in the Calc document.
+ The name will not be buffered in the global defined names buffer.
+ @param orName (in/out-parameter) Returns the resulting used name. */
+ RangeDataRet createNamedRangeObject(
+ OUString& orName,
+ const css::uno::Sequence< css::sheet::FormulaToken>& rTokens,
+ sal_Int32 nIndex,
+ sal_Int32 nNameFlags, bool bHidden ) const;
+
+ /** Creates and returns a defined name on-the-fly in the sheet.
+ The name will not be buffered in the global defined names buffer.
+ @param orName (in/out-parameter) Returns the resulting used name. */
+ RangeDataRet createLocalNamedRangeObject(
+ OUString& orName,
+ const css::uno::Sequence< css::sheet::FormulaToken>& rTokens,
+ sal_Int32 nIndex,
+ sal_Int32 nNameFlags, sal_Int32 nTab, bool bHidden ) const;
+
+ /** Creates and returns a database range on-the-fly in the Calc document.
+ The range will not be buffered in the global table buffer.
+ @param orName (in/out-parameter) Returns the resulting used name. */
+ css::uno::Reference< css::sheet::XDatabaseRange >
+ createDatabaseRangeObject(
+ OUString& orName,
+ const ScRange& rRangeAddr ) const;
+
+ /** Creates and returns an unnamed database range on-the-fly in the Calc document.
+ The range will not be buffered in the global table buffer. */
+ css::uno::Reference< css::sheet::XDatabaseRange >
+ createUnnamedDatabaseRangeObject(
+ const ScRange& rRangeAddr ) const;
+
+ /** Finds the (already existing) database range of the given formula token index. */
+ ScDBData* findDatabaseRangeByIndex( sal_uInt16 nIndex ) const;
+
+ /** Creates and returns a com.sun.star.style.Style object for cells or pages. */
+ css::uno::Reference< css::style::XStyle >
+ createStyleObject(
+ OUString& orStyleName,
+ bool bPageStyle ) const;
+
+ // buffers ----------------------------------------------------------------
+
+ FormulaBuffer& getFormulaBuffer() const;
+ /** Returns the global workbook settings object. */
+ WorkbookSettings& getWorkbookSettings() const;
+ /** Returns the workbook and sheet view settings object. */
+ ViewSettings& getViewSettings() const;
+ /** Returns the worksheet buffer containing sheet names and properties. */
+ WorksheetBuffer& getWorksheets() const;
+ /** Returns the office theme object read from the theme substorage. */
+ ThemeBuffer& getTheme() const;
+ /** Returns all cell formatting objects read from the styles substream. */
+ StylesBuffer& getStyles() const;
+ /** Returns the shared strings read from the shared strings substream. */
+ SharedStringsBuffer& getSharedStrings() const;
+ /** Returns the external links read from the external links substream. */
+ ExternalLinkBuffer& getExternalLinks() const;
+ /** Returns the defined names read from the workbook globals. */
+ DefinedNamesBuffer& getDefinedNames() const;
+ /** Returns the tables collection (equivalent to Calc's database ranges). */
+ TableBuffer& getTables() const;
+ /** Returns the scenarios collection. */
+ ScenarioBuffer& getScenarios() const;
+ /** Returns the collection of external data connections. */
+ ConnectionsBuffer& getConnections() const;
+ /** Returns the collection of pivot caches. */
+ PivotCacheBuffer& getPivotCaches() const;
+ /** Returns the collection of pivot tables. */
+ PivotTableBuffer& getPivotTables() const;
+
+ // converters -------------------------------------------------------------
+
+ /** Returns a shared import formula parser (import filter only!). */
+ FormulaParser& getFormulaParser() const;
+ /** Returns an unshared import formula parser (import filter only!). */
+ FormulaParser* createFormulaParser() const;
+ /** Returns the measurement unit converter. */
+ UnitConverter& getUnitConverter() const;
+ /** Returns the converter for string to cell address/range conversion. */
+ AddressConverter& getAddressConverter() const;
+ /** Returns the chart object converter. */
+ oox::drawingml::chart::ChartConverter* getChartConverter() const;
+ /** Returns the page and print settings converter. */
+ PageSettingsConverter& getPageSettingsConverter() const;
+
+ // OOXML/BIFF12 specific (MUST NOT be called in BIFF filter) --------------
+
+ /** Returns the base OOXML/BIFF12 filter object.
+ Must not be called, if current filter is not the OOXML/BIFF12 filter. */
+ ::oox::core::XmlFilterBase& getOoxFilter() const;
+
+ /** Imports a fragment using the passed fragment handler, which contains
+ the full path to the fragment stream. */
+ bool importOoxFragment( const rtl::Reference<oox::core::FragmentHandler>& rxHandler );
+
+ bool importOoxFragment( const rtl::Reference<oox::core::FragmentHandler>& rxHandler, oox::core::FastParser& rParser );
+
+ // BIFF2-BIFF8 specific (MUST NOT be called in OOXML/BIFF12 filter) -------
+
+ /** Returns the text encoding used to import/export byte strings. */
+ rtl_TextEncoding getTextEncoding() const;
+
+private:
+ WorkbookGlobals& mrBookGlob;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/workbooksettings.hxx b/sc/source/filter/inc/workbooksettings.hxx
new file mode 100644
index 000000000..1078b3e6c
--- /dev/null
+++ b/sc/source/filter/inc/workbooksettings.hxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "workbookhelper.hxx"
+
+namespace oox { class AttributeList; }
+namespace oox { class SequenceInputStream; }
+
+namespace com::sun::star::util { struct Date; }
+
+namespace oox::xls {
+
+/** Settings for workbook write protection. */
+struct FileSharingModel
+{
+ OUString maUserName; /// User who added the write protection password.
+ OUString maAlgorithmName; /// Algorithm name, "SHA-512", "SHA-1", ...
+ OUString maHashValue; /// Hash value computed by the algorithm, base-64 encoded
+ OUString maSaltValue; /// Salt value to be prepended to the password, base-64 encoded
+ sal_uInt32 mnSpinCount; /// Spin count, iterations to run algorithm
+ sal_uInt16 mnPasswordHash; /// Hash value of the write protection password. (unrelated to the above)
+ bool mbRecommendReadOnly; /// True = recommend read-only mode on opening.
+
+ explicit FileSharingModel();
+};
+
+/** Global workbook settings. */
+struct WorkbookSettingsModel
+{
+ OUString maCodeName; /// VBA codename for the workbook.
+ sal_Int32 mnShowObjectMode; /// Specifies how objects are shown.
+ sal_Int32 mnUpdateLinksMode; /// Specifies how external links are updated.
+ sal_Int32 mnDefaultThemeVer; /// Default theme version.
+ bool mbDateMode1904; /// True = null date is 1904-01-01.
+ bool mbDateCompatibility; /// False = null date is 1899-12-30.
+ bool mbSaveExtLinkValues; /// True = save cached cell values for external links.
+
+ explicit WorkbookSettingsModel();
+
+ /** Sets BIFF object visibility mode. */
+ void setBiffObjectMode( sal_uInt16 nObjMode );
+};
+
+/** Workbook calculation settings. */
+struct CalcSettingsModel
+{
+ double mfIterateDelta; /// Minimum change in circular references.
+ sal_Int32 mnCalcId; /// Calculation engine identifier.
+ sal_Int32 mnRefMode; /// Cell reference mode: A1 or R1C1.
+ sal_Int32 mnCalcMode; /// Automatic or manual recalculation.
+ sal_Int32 mnIterateCount; /// Number of iterations in circular references.
+ sal_Int32 mnProcCount; /// Number of processors for concurrent calculation.
+ bool mbCalcOnSave; /// True = always recalculate formulas before save.
+ bool mbCalcCompleted; /// True = formulas have been recalculated before save.
+ bool mbFullPrecision; /// True = use full precision on calculation.
+ bool mbIterate; /// True = allow circular references.
+ bool mbConcurrent; /// True = concurrent calculation enabled.
+
+ explicit CalcSettingsModel();
+};
+
+class WorkbookSettings : public WorkbookHelper
+{
+public:
+ explicit WorkbookSettings( const WorkbookHelper& rHelper );
+
+ /** Imports the fileSharing element containing write protection settings. */
+ void importFileSharing( const AttributeList& rAttribs );
+ /** Imports the workbookPr element containing global workbook settings. */
+ void importWorkbookPr( const AttributeList& rAttribs );
+ /** Imports the calcPr element containing workbook calculation settings. */
+ void importCalcPr( const AttributeList& rAttribs );
+
+ /** Imports the FILESHARING record containing write protection settings. */
+ void importFileSharing( SequenceInputStream& rStrm );
+ /** Imports the WORKBOOKPR record containing global workbook settings. */
+ void importWorkbookPr( SequenceInputStream& rStrm );
+ /** Imports the CALCPR record containing workbook calculation settings. */
+ void importCalcPr( SequenceInputStream& rStrm );
+
+ /** Converts the imported workbook settings. */
+ void finalizeImport();
+
+ /** Returns the show objects mode (considered a view setting in Calc). */
+ sal_Int16 getApiShowObjectMode() const;
+ /** Returns the nulldate of this workbook. */
+ css::util::Date const & getNullDate() const;
+
+private:
+ /** Updates date mode and unit converter nulldate. */
+ void setDateMode( bool bDateMode1904, bool bDateCompatibility=true );
+
+private:
+ FileSharingModel maFileSharing;
+ WorkbookSettingsModel maBookSettings;
+ CalcSettingsModel maCalcSettings;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/worksheetbuffer.hxx b/sc/source/filter/inc/worksheetbuffer.hxx
new file mode 100644
index 000000000..98a71e617
--- /dev/null
+++ b/sc/source/filter/inc/worksheetbuffer.hxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <utility>
+#include <oox/helper/refmap.hxx>
+#include <oox/helper/refvector.hxx>
+#include "workbookhelper.hxx"
+
+namespace oox { class SequenceInputStream; }
+namespace oox { class AttributeList; }
+
+namespace oox::xls {
+
+/** Contains data from the 'sheet' element describing a sheet in the workbook. */
+struct SheetInfoModel
+{
+ OUString maRelId; /// Relation identifier for the sheet substream.
+ OUString maName; /// Original name of the sheet.
+ sal_Int32 mnSheetId; /// Sheet identifier.
+ sal_Int32 mnState; /// Visibility state.
+
+ explicit SheetInfoModel();
+};
+
+/** Stores information about all sheets in a spreadsheet document.
+
+ Information about sheets includes the sheet name, the visibility state, and
+ for the OOXML filter, the relation identifier of the sheet used to obtain
+ the related worksheet fragment.
+ */
+class WorksheetBuffer : public WorkbookHelper
+{
+public:
+ explicit WorksheetBuffer( const WorkbookHelper& rHelper );
+
+ /** Imports the attributes of a sheet element. */
+ void importSheet( const AttributeList& rAttribs );
+ /** Imports the SHEET record from the passed BIFF12 stream. */
+ void importSheet( SequenceInputStream& rStrm );
+ /** Inserts a new empty sheet into the document. Looks for an unused name.
+ @return Index of the new sheet in the Calc document. */
+ sal_Int16 insertEmptySheet( const OUString& rPreferredName );
+
+ /** Returns the number of original sheets contained in the workbook. */
+ sal_Int32 getWorksheetCount() const;
+ /** Returns the number of all sheets, workbook + dummy ones (pivot table cache records ) */
+ sal_Int32 getAllSheetCount() const;
+ /** Returns the OOXML relation identifier of the specified worksheet. */
+ OUString getWorksheetRelId( sal_Int32 nWorksheet ) const;
+
+ /** Returns the Calc index of the specified worksheet. */
+ sal_Int16 getCalcSheetIndex( sal_Int32 nWorksheet ) const;
+ /** Returns the finalized name of the specified worksheet. */
+ OUString getCalcSheetName( sal_Int32 nWorksheet ) const;
+
+ /** Returns the Calc index of the sheet with the passed original worksheet name. */
+ sal_Int16 getCalcSheetIndex( const OUString& rWorksheetName ) const;
+ /** Returns the finalized name of the sheet with the passed worksheet name. */
+ OUString getCalcSheetName( const OUString& rWorksheetName ) const;
+ /** Converts sSheetNameRef (e.g. '#SheetName!A1' to '#SheetName.A1' )
+ if sSheetNameRef doesn't start with '#' it is ignored and not modified
+ */
+ void convertSheetNameRef( OUString& sSheetNameRef ) const;
+ void finalizeImport( sal_Int16 nActiveSheet );
+
+private:
+ struct SheetInfo : public SheetInfoModel
+ {
+ OUString maCalcName;
+ OUString maCalcQuotedName;
+ sal_Int16 mnCalcSheet;
+
+ explicit SheetInfo( const SheetInfoModel& rModel, sal_Int16 nCalcSheet, const OUString& rCalcName );
+ };
+
+ typedef ::std::pair< sal_Int16, OUString > IndexNamePair;
+
+ /** Creates a new sheet in the Calc document. Does not insert anything in the own lists. */
+ IndexNamePair createSheet( const OUString& rPreferredName, sal_Int32 nSheetPos );
+ /** Creates a new sheet in the Calc document and inserts the related SheetInfo. */
+ void insertSheet( const SheetInfoModel& rModel );
+
+private:
+ RefVector< SheetInfo > maSheetInfos;
+ RefMap< OUString, SheetInfo, IgnoreCaseCompare > maSheetInfosByName;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/worksheetfragment.hxx b/sc/source/filter/inc/worksheetfragment.hxx
new file mode 100644
index 000000000..05ec7d02f
--- /dev/null
+++ b/sc/source/filter/inc/worksheetfragment.hxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "excelhandlers.hxx"
+
+namespace oox::xls {
+
+class DataValidationsContextBase
+{
+public:
+ void SetSqref( const OUString& rChars ) { maSqref = rChars; }
+ void SetFormula1( const OUString& rChars ) { maFormula1 = rChars; }
+ void SetFormula2( const OUString& rChars ) { maFormula2 = rChars; }
+ void SetValidation( WorksheetHelper& rTarget );
+ /** Imports the dataValidation element containing data validation settings. */
+ void importDataValidation( const AttributeList& rAttribs );
+ /** Imports the DATAVALIDATION record containing data validation settings. */
+ static void importDataValidation( SequenceInputStream& rStrm, WorksheetHelper& rTarget );
+ bool isFormula1Set() const { return !maFormula1.isEmpty(); }
+ bool isFormula2Set() const { return !maFormula2.isEmpty(); }
+
+private:
+ std::unique_ptr< ValidationModel > mxValModel;
+
+ OUString maSqref;
+ OUString maFormula1;
+ OUString maFormula2;
+};
+
+// For following types of validations:
+//
+// <dataValidations count="1">
+// <dataValidation allowBlank="true" operator="equal" showDropDown="false" showErrorMessage="true" showInputMessage="false" sqref="C1:C5" type="list">
+// <formula1>Sheet1!$A$1:$A$5</formula1>
+// <formula2>0</formula2>
+// </dataValidation>
+// </dataValidations>
+//
+// or
+//
+// <dataValidations count="1">
+// <dataValidation type="list" operator="equal" allowBlank="1" showErrorMessage="1" sqref="A1">
+// <mc:AlternateContent xmlns:x12ac="http://schemas.microsoft.com/office/spreadsheetml/2011/1/ac" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
+// <mc:Choice Requires="x12ac">
+// <x12ac:list>1,"2,3",4</x12ac:list>
+// </mc:Choice>
+// <mc:Fallback>
+// <formula1>"1,2,3,4"</formula1>
+// </mc:Fallback>
+// </mc:AlternateContent>
+// </dataValidation>
+// </dataValidations>
+
+class DataValidationsContext : public WorksheetContextBase, private DataValidationsContextBase
+{
+public:
+ explicit DataValidationsContext( WorksheetFragmentBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+};
+
+// For following types of validations:
+//
+// <extLst>
+// <ext uri="{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main">
+// <x14:dataValidations count="1" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
+// <x14:dataValidation type="list" allowBlank="1" showInputMessage="1" showErrorMessage="1">
+// <x14:formula1>
+// <xm:f>Sheet1!$A$2:$A$272</xm:f>
+// </x14:formula1>
+// <xm:sqref>A6:A22</xm:sqref>
+// </x14:dataValidation>
+// </x14:dataValidations>
+// </ext>
+// </extLst>
+
+class ExtDataValidationsContext : public WorksheetContextBase, private DataValidationsContextBase
+{
+public:
+ explicit ExtDataValidationsContext( WorksheetContextBase& rFragment );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+ virtual void onEndElement() override;
+private:
+ sal_Int32 mCurrFormula;
+};
+
+class WorksheetFragment : public WorksheetFragmentBase
+{
+public:
+ explicit WorksheetFragment(
+ const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath );
+
+protected:
+ virtual ::oox::core::ContextHandlerRef onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs ) override;
+ virtual void onCharacters( const OUString& rChars ) override;
+
+ virtual ::oox::core::ContextHandlerRef onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
+
+ virtual const ::oox::core::RecordInfo* getRecordInfos() const override;
+ virtual void initializeImport() override;
+ virtual void finalizeImport() override;
+
+private:
+ /** Imports page settings from a pageSetUpPr element. */
+ void importPageSetUpPr( const AttributeList& rAttribs );
+ /** Imports the dimension element containing the used area of the sheet. */
+ void importDimension( const AttributeList& rAttribs );
+ /** Imports sheet format properties from a sheetFormatPr element. */
+ void importSheetFormatPr( const AttributeList& rAttribs );
+ /** Imports column settings from a col element. */
+ void importCol( const AttributeList& rAttribs );
+ /** Imports a merged cell range from a mergeCell element. */
+ void importMergeCell( const AttributeList& rAttribs );
+ /** Imports the hyperlink element containing a hyperlink for a cell range. */
+ void importHyperlink( const AttributeList& rAttribs );
+ /** Imports individual break that is either within row or column break context. */
+ void importBrk( const AttributeList& rAttribs, bool bRowBreak );
+ /** Imports the relation identifier for the DrawingML part. */
+ void importDrawing( const AttributeList& rAttribs );
+ /** Imports the relation identifier for the legacy VML drawing part. */
+ void importLegacyDrawing( const AttributeList& rAttribs );
+ /** Imports additional data for an OLE object. */
+ void importOleObject( const AttributeList& rAttribs );
+ /** Imports additional data for an OCX form control. */
+ void importControl( const AttributeList& rAttribs );
+
+ /** Imports the DIMENSION record containing the used area of the sheet. */
+ void importDimension( SequenceInputStream& rStrm );
+ /** Imports sheet format properties from a SHEETFORMATPR record. */
+ void importSheetFormatPr( SequenceInputStream& rStrm );
+ /** Imports column settings from a COL record. */
+ void importCol( SequenceInputStream& rStrm );
+ /** Imports a merged cell range from a MERGECELL record. */
+ void importMergeCell( SequenceInputStream& rStrm );
+ /** Imports a hyperlink for a cell range from a HYPERLINK record. */
+ void importHyperlink( SequenceInputStream& rStrm );
+ /** Imports the BRK record for an individual row or column page break. */
+ void importBrk( SequenceInputStream& rStrm, bool bRowBreak );
+ /** Imports the DRAWING record containing the relation identifier for the DrawingML part. */
+ void importDrawing( SequenceInputStream& rStrm );
+ /** Imports the LEGACYDRAWING record containing the relation identifier for the VML drawing part. */
+ void importLegacyDrawing( SequenceInputStream& rStrm );
+ /** Imports additional data for an OLE object. */
+ void importOleObject( SequenceInputStream& rStrm );
+ /** Imports additional data for an OCX form control. */
+ void importControl( SequenceInputStream& rStrm );
+
+ /** Imports the binary data of an embedded OLE object from the fragment with the passed ID. */
+ void importEmbeddedOleData( StreamDataSequence& orEmbeddedData, const OUString& rRelId );
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/worksheethelper.hxx b/sc/source/filter/inc/worksheethelper.hxx
new file mode 100644
index 000000000..7beacf7c2
--- /dev/null
+++ b/sc/source/filter/inc/worksheethelper.hxx
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/progressbar.hxx>
+#include <oox/ole/olehelper.hxx>
+#include <rangelst.hxx>
+#include "formulabase.hxx"
+
+struct ScDataBarFormatData;
+class ScDocument;
+
+namespace com::sun::star {
+ namespace awt { struct Point; }
+ namespace awt { struct Rectangle; }
+ namespace awt { struct Size; }
+ namespace drawing { class XDrawPage; }
+ namespace sheet { class XSpreadsheet; }
+ namespace table { class XCell; }
+ namespace table { class XCellRange; }
+}
+
+namespace oox::xls {
+
+class AutoFilterBuffer;
+class CommentsBuffer;
+class CondFormatBuffer;
+class Font;
+class PageSettings;
+class QueryTableBuffer;
+class RichString;
+class SheetDataBuffer;
+class SheetViewSettings;
+class VmlDrawing;
+class WorksheetSettings;
+
+typedef ::std::map< OUString, ScDataBarFormatData* > ExtLst;
+
+/** An enumeration for all types of sheets in a workbook. */
+enum class WorksheetType
+{
+ Work, /// Worksheet.
+ Chart, /// Chart sheet.
+ Macro, /// Macro sheet.
+ Dialog, /// Dialog sheet (BIFF5+).
+ Empty /// Other (unsupported) sheet type.
+};
+
+/** Stores settings and formatting data about a range of sheet columns. */
+struct ColumnModel
+{
+ ValueRange maRange; /// 1-based (!) range of the described columns.
+ double mfWidth; /// Column width in number of characters.
+ sal_Int32 mnXfId; /// Column default formatting.
+ sal_Int32 mnLevel; /// Column outline level.
+ bool mbShowPhonetic:1; /// True = cells in column show phonetic settings.
+ bool mbHidden:1; /// True = column is hidden.
+ bool mbCollapsed:1; /// True = column outline is collapsed.
+
+ explicit ColumnModel();
+
+ /** Returns true, if this entry can be merged with the passed column range (column settings are equal). */
+ bool isMergeable( const ColumnModel& rModel ) const;
+};
+
+/** Stores settings and formatting data about a sheet row. */
+struct RowModel
+{
+ sal_Int32 mnRow; /// 1-based (!) index of the described row.
+ ValueRangeSet maColSpans; /// 0-based (!) column ranges of used cells.
+ double mfHeight; /// Row height in points.
+ sal_Int32 mnXfId; /// Row default formatting (see mbIsFormatted).
+ sal_Int32 mnLevel; /// Row outline level.
+ bool mbCustomHeight:1; /// True = row has custom height.
+ bool mbCustomFormat:1; /// True = cells in row have explicit formatting.
+ bool mbShowPhonetic:1; /// True = cells in row show phonetic settings.
+ bool mbHidden:1; /// True = row is hidden.
+ bool mbCollapsed:1; /// True = row outline is collapsed.
+ bool mbThickTop:1; /// True = row has extra space above text.
+ bool mbThickBottom:1; /// True = row has extra space below text.
+
+ explicit RowModel();
+
+ /** Inserts the passed column span into the row model. */
+ void insertColSpan( const ValueRange& rColSpan );
+ /** Returns true, if this entry can be merged with the passed row range (row settings are equal). */
+ bool isMergeable( const RowModel& rModel ) const;
+};
+
+/** Stores formatting data about a page break. */
+struct PageBreakModel
+{
+ sal_Int32 mnColRow; /// 0-based (!) index of column/row.
+ sal_Int32 mnMin; /// Start of limited break.
+ sal_Int32 mnMax; /// End of limited break.
+ bool mbManual; /// True = manual page break.
+
+ explicit PageBreakModel();
+};
+
+/** Stores data about a hyperlink range. */
+struct HyperlinkModel : public ::oox::ole::StdHlinkInfo
+{
+ ScRange maRange; /// The cell area containing the hyperlink.
+ OUString maTooltip; /// Additional tooltip text.
+
+ explicit HyperlinkModel();
+};
+
+/** Stores data about ranges with data validation settings. */
+struct ValidationModel
+{
+ ScRangeList maRanges;
+ ApiTokenSequence maTokens1;
+ ApiTokenSequence maTokens2;
+ OUString msRef;
+ OUString maInputTitle;
+ OUString maInputMessage;
+ OUString maErrorTitle;
+ OUString maErrorMessage;
+ sal_Int32 mnType;
+ sal_Int32 mnOperator;
+ sal_Int32 mnErrorStyle;
+ bool mbShowInputMsg:1;
+ bool mbShowErrorMsg:1;
+ bool mbNoDropDown:1;
+ bool mbAllowBlank:1;
+
+ explicit ValidationModel();
+
+ /** Sets the passed BIFF validation type. */
+ void setBiffType( sal_uInt8 nType );
+ /** Sets the passed BIFF operator. */
+ void setBiffOperator( sal_uInt8 nOperator );
+ /** Sets the passed BIFF error style. */
+ void setBiffErrorStyle( sal_uInt8 nErrorStyle );
+};
+
+class WorksheetGlobals;
+typedef std::shared_ptr< WorksheetGlobals > WorksheetGlobalsRef;
+
+class IWorksheetProgress {
+public:
+ virtual ~IWorksheetProgress() {}
+ virtual ISegmentProgressBarRef getRowProgress() = 0;
+ virtual void setCustomRowProgress(
+ const ISegmentProgressBarRef &rxRowProgress ) = 0;
+};
+
+class WorksheetHelper : public WorkbookHelper
+{
+public:
+ /*implicit*/ WorksheetHelper( WorksheetGlobals& rSheetGlob );
+
+ static WorksheetGlobalsRef constructGlobals(
+ const WorkbookHelper& rHelper,
+ const ISegmentProgressBarRef& rxProgressBar,
+ WorksheetType eSheetType,
+ SCTAB nSheet );
+
+ // horrible accessor for hidden WorksheetGlobals ...
+ static IWorksheetProgress *getWorksheetInterface( const WorksheetGlobalsRef &xRef );
+
+ /** Returns the type of this sheet. */
+ WorksheetType getSheetType() const;
+ /** Returns the index of the current sheet. */
+ SCTAB getSheetIndex() const;
+ /** Returns the XSpreadsheet interface of the current sheet. */
+ const css::uno::Reference< css::sheet::XSpreadsheet >&
+ getSheet() const;
+
+ /** Returns the XCell interface for the passed cell address. */
+ css::uno::Reference< css::table::XCell >
+ getCell( const ScAddress& rAddress ) const;
+ /** Returns the XCellRange interface for the passed cell range address. */
+ css::uno::Reference< css::table::XCellRange >
+ getCellRange( const ScRange& rRange ) const;
+
+ /** Returns the XDrawPage interface of the draw page of the current sheet. */
+ css::uno::Reference< css::drawing::XDrawPage >
+ getDrawPage() const;
+
+ /** Returns the absolute cell position in 1/100 mm. */
+ css::awt::Point getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const;
+ /** Returns the size of the entire drawing page in 1/100 mm. */
+ const css::awt::Size& getDrawPageSize() const;
+
+ /** Returns the buffer for cell contents and cell formatting. */
+ SheetDataBuffer& getSheetData() const;
+ /** Returns the conditional formatting in this sheet. */
+ CondFormatBuffer& getCondFormats() const;
+ /** Returns the buffer for all cell comments in this sheet. */
+ CommentsBuffer& getComments() const;
+ /** Returns the auto filters for the sheet. */
+ AutoFilterBuffer& getAutoFilters() const;
+ /** Returns the buffer for all web query tables in this sheet. */
+ QueryTableBuffer& getQueryTables() const;
+ /** Returns the worksheet settings object. */
+ WorksheetSettings& getWorksheetSettings() const;
+ /** Returns the page/print settings for this sheet. */
+ PageSettings& getPageSettings() const;
+ /** Returns the view settings for this sheet. */
+ SheetViewSettings& getSheetViewSettings() const;
+ /** Returns the VML drawing page for this sheet (OOXML/BIFF12 only). */
+ VmlDrawing& getVmlDrawing() const;
+
+ ExtLst& getExtLst() const;
+
+ /** Sets a column or row page break described in the passed struct. */
+ void setPageBreak( const PageBreakModel& rModel, bool bRowBreak );
+ /** Inserts the hyperlink URL into the spreadsheet. */
+ void setHyperlink( const HyperlinkModel& rModel );
+ /** Inserts the data validation settings into the spreadsheet. */
+ void setValidation( const ValidationModel& rModel );
+ /** Sets the path to the DrawingML fragment of this sheet. */
+ void setDrawingPath( const OUString& rDrawingPath );
+ /** Sets the path to the legacy VML drawing fragment of this sheet. */
+ void setVmlDrawingPath( const OUString& rVmlDrawingPath );
+
+ /** Extends the used area of this sheet by the passed cell position. */
+ void extendUsedArea( const ScAddress& rAddress );
+ /** Extends the used area of this sheet by the passed cell range. */
+ void extendUsedArea( const ScRange& rRange );
+ /** Extends the shape bounding box by the position and size of the passed rectangle (in 1/100 mm). */
+ void extendShapeBoundingBox( const css::awt::Rectangle& rShapeRect );
+
+ /** Sets base width for all columns (without padding pixels). This value
+ is only used, if width has not been set with setDefaultColumnWidth(). */
+ void setBaseColumnWidth( sal_Int32 nWidth );
+ /** Sets default width for all columns. This function overrides the base
+ width set with the setBaseColumnWidth() function. */
+ void setDefaultColumnWidth( double fWidth );
+ /** Sets column settings for a specific range of columns.
+ @descr Column default formatting is converted directly, other settings
+ are cached and converted in the finalizeWorksheetImport() call. */
+ void setColumnModel( const ColumnModel& rModel );
+
+ /** Sets default height and hidden state for all unused rows in the sheet. */
+ void setDefaultRowSettings(
+ double fHeight, bool bCustomHeight,
+ bool bHidden, bool bThickTop, bool bThickBottom );
+ /** Sets row settings for a specific range of rows.
+ @descr Row default formatting is converted directly, other settings
+ are cached and converted in the finalizeWorksheetImport() call. */
+ void setRowModel( const RowModel& rModel );
+
+ /** Inserts a rich-string cell directly into the Calc sheet. */
+ void putRichString(
+ const ScAddress& rAddress,
+ const RichString& rString, const oox::xls::Font* pFirstPortionFont );
+
+ /** Inserts a formula cell directly into the Calc sheet. */
+ void putFormulaTokens(
+ const ScAddress& rAddress, const ApiTokenSequence& rTokens );
+
+ /** Initial conversion before importing the worksheet. */
+ void initializeWorksheetImport();
+ /** Final conversion after importing the worksheet. */
+ void finalizeWorksheetImport();
+ /** Final import of drawing objects. Has to be called after all content has been imported */
+ void finalizeDrawingImport();
+
+ void setCellFormula( const ScAddress& rTokenAddress, const OUString& );
+
+ void setCellFormula(
+ const ScAddress& rAddr, sal_Int32 nSharedId,
+ const OUString& rCellValue, sal_Int32 nValueType );
+
+ void setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr );
+
+ void createSharedFormulaMapEntry(
+ const ScAddress& rAddress,
+ sal_Int32 nSharedId, const OUString& rTokens );
+
+ void setCellFormulaValue(
+ const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType );
+
+ ScDocument& getScDocument();
+
+
+private:
+ WorksheetGlobals& mrSheetGlob;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/worksheetsettings.hxx b/sc/source/filter/inc/worksheetsettings.hxx
new file mode 100644
index 000000000..a785145bf
--- /dev/null
+++ b/sc/source/filter/inc/worksheetsettings.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "richstring.hxx"
+#include "worksheethelper.hxx"
+#include <tabprotection.hxx>
+
+namespace oox::xls {
+
+/** Sheet and outline settings. */
+struct SheetSettingsModel
+{
+ OUString maCodeName; /// VBA module codename.
+ Color maTabColor; /// Sheet tab color.
+ bool mbFilterMode; /// True = sheet contains active filter.
+ bool mbApplyStyles; /// True = automatic styles when creating outlines.
+ bool mbSummaryBelow; /// True = row outline symbols below group.
+ bool mbSummaryRight; /// True = column outline symbols right of group.
+
+ explicit SheetSettingsModel();
+};
+
+/** Sheet protection settings. */
+struct SheetProtectionModel
+{
+ OUString maAlgorithmName; /// Algorithm name, "SHA-512", "SHA-1", ...
+ OUString maHashValue; /// Hash value computed by the algorithm, base-64 encoded
+ OUString maSaltValue; /// Salt value to be prepended to the password, base-64 encoded
+ sal_uInt32 mnSpinCount; /// Spin count, iterations to run algorithm
+ sal_uInt16 mnPasswordHash; /// Hash value from sheet protection password. (unrelated to the above)
+ bool mbSheet; /// True = sheet protection enabled, locked cells are protected.
+ bool mbObjects; /// True = objects locked.
+ bool mbScenarios; /// True = scenarios locked.
+ bool mbFormatCells; /// True = format cells locked.
+ bool mbFormatColumns; /// True = format columns locked.
+ bool mbFormatRows; /// True = format rows locked.
+ bool mbInsertColumns; /// True = insert columns locked.
+ bool mbInsertRows; /// True = insert rows locked.
+ bool mbInsertHyperlinks; /// True = insert hyperlinks locked.
+ bool mbDeleteColumns; /// True = delete columns locked.
+ bool mbDeleteRows; /// True = delete rows locked.
+ bool mbSelectLocked; /// True = select locked cells locked.
+ bool mbSort; /// True = sorting locked.
+ bool mbAutoFilter; /// True = autofilters locked.
+ bool mbPivotTables; /// True = pivot tables locked.
+ bool mbSelectUnlocked; /// True = select unlocked cells locked.
+
+ ::std::vector< ScEnhancedProtection > maEnhancedProtections;
+
+ explicit SheetProtectionModel();
+};
+
+class WorksheetSettings : public WorksheetHelper
+{
+public:
+ explicit WorksheetSettings( const WorksheetHelper& rHelper );
+
+ /** Imports sheet settings from the sheetPr element. */
+ void importSheetPr( const AttributeList& rAttribs );
+ /** Imports chart sheet settings from the sheetPr element. */
+ void importChartSheetPr( const AttributeList& rAttribs );
+ /** Imports the sheet tab color from the tabColor element. */
+ void importTabColor( const AttributeList& rAttribs );
+ /** Imports outline settings from the outlinePr element. */
+ void importOutlinePr( const AttributeList& rAttribs );
+ /** Imports protection settings from the sheetProtection element. */
+ void importSheetProtection( const AttributeList& rAttribs );
+ /** Imports enhanced protection settings from the protectedRange element. */
+ void importProtectedRange( const AttributeList& rAttribs );
+ /** Imports protection settings from the sheetProtection element of a chart sheet. */
+ void importChartProtection( const AttributeList& rAttribs );
+ /** Imports phonetic settings from the phoneticPr element. */
+ void importPhoneticPr( const AttributeList& rAttribs );
+
+ /** Imports sheet properties from the SHEETPR record. */
+ void importSheetPr( SequenceInputStream& rStrm );
+ /** Imports sheet properties from the CHARTSHEETPR record. */
+ void importChartSheetPr( SequenceInputStream& rStrm );
+ /** Imports sheet protection settings from the SHEETPROTECTION record. */
+ void importSheetProtection( SequenceInputStream& rStrm );
+ /** Imports chart sheet protection settings from the CHARTPROTECTION record. */
+ void importChartProtection( SequenceInputStream& rStrm );
+ /** Imports phonetic settings from the PHONETICPR record. */
+ void importPhoneticPr( SequenceInputStream& rStrm );
+
+ /** Converts the imported worksheet settings. */
+ void finalizeImport();
+
+private:
+ PhoneticSettings maPhoneticSett;
+ SheetSettingsModel maSheetSettings;
+ SheetProtectionModel maSheetProt;
+};
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xcl97esc.hxx b/sc/source/filter/inc/xcl97esc.hxx
new file mode 100644
index 000000000..6ae5c8762
--- /dev/null
+++ b/sc/source/filter/inc/xcl97esc.hxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <stack>
+#include <filter/msfilter/escherex.hxx>
+#include "xeroot.hxx"
+#include <unotools/tempfile.hxx>
+
+namespace com::sun::star::awt { class XControlModel; }
+
+class XclEscherExGlobal : public EscherExGlobal, protected XclExpRoot
+{
+public:
+ explicit XclEscherExGlobal( const XclExpRoot& rRoot );
+
+private:
+ /** Override to create a new temporary file and return its stream. */
+ virtual SvStream* ImplQueryPictureStream() override;
+
+private:
+ ::std::unique_ptr< ::utl::TempFile > mxPicTempFile;
+ ::std::unique_ptr< SvStream > mxPicStrm;
+};
+
+class XclObj;
+class XclExpDffAnchorBase;
+class XclEscherHostAppData;
+class XclEscherClientData;
+class XclEscherClientTextbox;
+class XclExpOcxControlObj;
+class XclExpTbxControlObj;
+class XclExpShapeObj;
+class ShapeInteractionHelper
+{
+public:
+ static XclExpShapeObj* CreateShapeObj( XclExpObjectManager& rObjMgr, const css::uno::Reference<
+ css::drawing::XShape >& xShape, ScDocument* pDoc );
+ static void PopulateShapeInteractionInfo( const XclExpObjectManager& rObjMgr, const css::uno::Reference< css::drawing::XShape >& xShape, EscherExHostAppData& rHostAppData );
+};
+
+class XclEscherEx : public EscherEx, protected XclExpRoot
+{
+public:
+ explicit XclEscherEx(
+ const XclExpRoot& rRoot,
+ XclExpObjectManager& rObjMgr,
+ SvStream& rStrm,
+ const XclEscherEx* pParent = nullptr );
+ virtual ~XclEscherEx() override;
+
+ /** Called by MSODRAWING record constructors to initialize the DFF stream
+ fragment they will own. returns the DFF fragment identifier. */
+ sal_uInt32 InitNextDffFragment();
+ /** Called after some data has been written to the DFF stream, to update
+ the end position of the DFF fragment owned by an MSODRAWING record. */
+ void UpdateDffFragmentEnd();
+
+ /** Returns the position of the specified DFF stream fragment. */
+ sal_uInt32 GetDffFragmentPos( sal_uInt32 nFragmentKey );
+ /** Returns the size of the specified DFF stream fragment. */
+ sal_uInt32 GetDffFragmentSize( sal_uInt32 nFragmentKey );
+ /** Returns true, if there is more data left in the DFF stream than owned
+ by the last MSODRAWING record. */
+ bool HasPendingDffData();
+
+ /** Creates a new DFF client anchor object and calculates the anchor
+ position of the passed object. Caller takes ownership! */
+ XclExpDffAnchorBase* CreateDffAnchor( const SdrObject& rSdrObj ) const;
+
+ virtual EscherExHostAppData* StartShape(
+ const css::uno::Reference< css::drawing::XShape>& rxShape,
+ const tools::Rectangle* pChildAnchor ) override;
+ virtual void EndShape( sal_uInt16 nShapeType, sal_uInt32 nShapeID ) override;
+ virtual EscherExHostAppData* EnterAdditionalTextGroup() override;
+
+ /// Flush and merge PicStream into EscherStream
+ void EndDocument();
+ /** Creates an OCX form control OBJ record from the passed form control.
+ @descr Writes the form control data to the 'Ctls' stream. */
+ std::unique_ptr<XclExpOcxControlObj> CreateOCXCtrlObj(
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor );
+
+private:
+ tools::SvRef<SotStorageStream> mxCtlsStrm; /// The 'Ctls' stream.
+ /** Creates a TBX form control OBJ record from the passed form control. */
+ std::unique_ptr<XclExpTbxControlObj> CreateTBXCtrlObj(
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor );
+
+private:
+ /** Tries to get the name of a Basic macro from a control. */
+ void ConvertTbxMacro(
+ XclExpTbxControlObj& rTbxCtrlObj,
+ css::uno::Reference< css::awt::XControlModel > const & xCtrlModel );
+
+ void DeleteCurrAppData();
+
+private:
+ XclExpObjectManager& mrObjMgr;
+ std::stack< std::pair< XclObj*, std::unique_ptr<XclEscherHostAppData> > > aStack;
+ XclObj* pCurrXclObj;
+ std::unique_ptr<XclEscherHostAppData> pCurrAppData;
+ std::unique_ptr<XclEscherClientData> pTheClientData; // always the same
+ XclEscherClientTextbox* pAdditionalText;
+ sal_uInt16 nAdditionalText;
+ sal_uInt32 mnNextKey;
+ bool mbIsRootDff;
+};
+
+// --- class XclEscherHostAppData ------------------------------------
+
+class XclEscherHostAppData : public EscherExHostAppData
+{
+private:
+ bool bStackedGroup;
+
+public:
+ XclEscherHostAppData() : bStackedGroup( false )
+ {}
+ void SetStackedGroup( bool b ) { bStackedGroup = b; }
+ bool IsStackedGroup() const { return bStackedGroup; }
+};
+
+// --- class XclEscherClientData -------------------------------------
+
+class XclEscherClientData : public EscherExClientRecord_Base
+{
+public:
+ XclEscherClientData() {}
+ virtual void WriteData( EscherEx& rEx ) const override;
+};
+
+// --- class XclEscherClientTextbox ----------------------------------
+
+class SdrTextObj;
+
+class XclEscherClientTextbox : public EscherExClientRecord_Base, protected XclExpRoot
+{
+private:
+ const SdrTextObj& rTextObj;
+ XclObj* pXclObj;
+
+public:
+ XclEscherClientTextbox(
+ const XclExpRoot& rRoot,
+ const SdrTextObj& rObj,
+ XclObj* pObj );
+
+ //! ONLY for the AdditionalText mimic
+ void SetXclObj( XclObj* p ) { pXclObj = p; }
+
+ virtual void WriteData( EscherEx& rEx ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xcl97rec.hxx b/sc/source/filter/inc/xcl97rec.hxx
new file mode 100644
index 000000000..eab23db6a
--- /dev/null
+++ b/sc/source/filter/inc/xcl97rec.hxx
@@ -0,0 +1,600 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include "excrecds.hxx"
+#include "xlescher.hxx"
+#include "xestring.hxx"
+#include <tabprotection.hxx>
+#include <svx/svdobj.hxx>
+#include <oox/export/drawingml.hxx>
+
+class XclObj;
+class XclExpMsoDrawing;
+class SdrCaptionObj;
+class SdrTextObj;
+class XclTxo;
+class XclEscherEx;
+
+class ScURLTransformer : public oox::drawingml::URLTransformer
+{
+public:
+ explicit ScURLTransformer(ScDocument& rDoc);
+
+ virtual OUString getTransformedString(const OUString& rURL) const override;
+
+ virtual bool isExternalURL(const OUString& rURL) const override;
+
+private:
+ ScDocument& mrDoc;
+};
+
+class XclExpObjList : public ExcEmptyRec, protected XclExpRoot
+{
+public:
+
+ typedef std::vector<std::unique_ptr<XclObj>>::iterator iterator;
+
+ explicit XclExpObjList( const XclExpRoot& rRoot, XclEscherEx& rEscherEx );
+ virtual ~XclExpObjList() override;
+
+ /// return: 1-based ObjId
+ ///! count>=0xFFFF: Obj will be deleted, return 0
+ sal_uInt16 Add( std::unique_ptr<XclObj> );
+
+ /**
+ * @brief Remove last element in the list.
+ */
+ std::unique_ptr<XclObj> pop_back ();
+
+ bool empty () const { return maObjs.empty(); }
+
+ size_t size () const { return maObjs.size(); }
+
+ iterator begin () { return maObjs.begin(); }
+
+ iterator end () { return maObjs.end(); }
+
+ XclExpMsoDrawing* GetMsodrawingPerSheet() { return pMsodrawingPerSheet.get(); }
+
+ /// close groups and DgContainer opened in ctor
+ void EndSheet();
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ SCTAB mnScTab;
+
+ XclEscherEx& mrEscherEx;
+ std::unique_ptr<XclExpMsoDrawing> pMsodrawingPerSheet;
+ std::unique_ptr<XclExpMsoDrawing> pSolverContainer;
+
+ std::vector<std::unique_ptr<XclObj>> maObjs;
+};
+
+// --- class XclObj --------------------------------------------------
+
+class XclObj : public XclExpRecord
+{
+protected:
+ XclEscherEx& mrEscherEx;
+ XclExpMsoDrawing* pMsodrawing;
+ std::unique_ptr<XclExpMsoDrawing> pClientTextbox;
+ std::unique_ptr<XclTxo> pTxo;
+ sal_uInt16 mnObjType;
+ sal_uInt16 nObjId;
+ sal_uInt16 nGrbit;
+ SCTAB mnScTab;
+ bool bFirstOnSheet;
+
+ bool mbOwnEscher; /// true = Escher part created on the fly.
+
+ /** @param bOwnEscher If set to true, this object will create its escher data.
+ See SetOwnEscher() for details. */
+ explicit XclObj( XclExpObjectManager& rObjMgr, sal_uInt16 nObjType, bool bOwnEscher = false );
+
+ void ImplWriteAnchor( const SdrObject* pSdrObj, const tools::Rectangle* pChildAnchor );
+
+ // overwritten for writing MSODRAWING record
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ virtual void WriteSubRecs( XclExpStream& rStrm );
+ void SaveTextRecs( XclExpStream& rStrm );
+
+public:
+ virtual ~XclObj() override;
+
+ sal_uInt16 GetObjType() const { return mnObjType; }
+
+ void SetId( sal_uInt16 nId ) { nObjId = nId; }
+
+ void SetTab( SCTAB nScTab ) { mnScTab = nScTab; }
+ SCTAB GetTab() const { return mnScTab; }
+
+ void SetLocked( bool b ) { SetGrBit(b, 0x0001); }
+ void SetPrintable( bool b ) { SetGrBit(b, 0x0010); }
+ void SetAutoFill( bool b ) { SetGrBit(b, 0x2000); }
+ void SetAutoLine( bool b ) { SetGrBit(b, 0x4000); }
+ void SetGrBit( bool b, int f )
+ {
+ if (b)
+ nGrbit |= f;
+ else
+ nGrbit &= ~f;
+ }
+
+ // set corresponding Excel object type in OBJ/ftCmo
+ void SetEscherShapeType( sal_uInt16 nType );
+ void SetEscherShapeTypeGroup() { mnObjType = EXC_OBJTYPE_GROUP; }
+
+ /** If set to true, this object has created its own escher data.
+ @descr This causes the function EscherEx::EndShape() to not post process
+ this object. This is used i.e. for form controls. They are not handled in
+ the svx base code, so the XclExpEscherOcxCtrl c'tor creates the escher data
+ itself. The svx base code does not receive the correct shape ID after the
+ EscherEx::StartShape() call, which would result in deleting the object in
+ EscherEx::EndShape(). */
+ /** Returns true, if the object has created the escher data itself.
+ @descr See SetOwnEscher() for details. */
+ bool IsOwnEscher() const { return mbOwnEscher; }
+
+ //! actually writes ESCHER_ClientTextbox
+ void SetText( const XclExpRoot& rRoot, const SdrTextObj& rObj );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// --- class XclObjComment -------------------------------------------
+
+class XclObjComment : public XclObj
+{
+ ScAddress maScPos;
+
+ // no need to use std::unique_ptr< SdrCaptionObj, SdrObjectFreeOp >
+ SdrCaptionObj* mpCaption;
+
+ bool mbVisible;
+ tools::Rectangle maFrom;
+ tools::Rectangle maTo;
+
+public:
+ XclObjComment( XclExpObjectManager& rObjMgr,
+ const tools::Rectangle& rRect, const EditTextObject& rEditObj, SdrCaptionObj* pCaption, bool bVisible, const ScAddress& rAddress, const tools::Rectangle &rFrom, const tools::Rectangle &To );
+ virtual ~XclObjComment() override;
+
+ /** c'tor process for formatted text objects above .
+ @descr used to construct the MSODRAWING Escher object properties. */
+ void ProcessEscherObj( const XclExpRoot& rRoot,
+ const tools::Rectangle& rRect, SdrObject* pCaption, bool bVisible );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// --- class XclObjDropDown ------------------------------------------
+
+class XclObjDropDown : public XclObj
+{
+private:
+ bool bIsFiltered;
+
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+protected:
+public:
+ XclObjDropDown( XclExpObjectManager& rObjMgr, const ScAddress& rPos, bool bFilt );
+ virtual ~XclObjDropDown() override;
+};
+
+// --- class XclTxo --------------------------------------------------
+
+class XclTxo : public ExcRecord
+{
+public:
+ XclTxo( const OUString& rString, sal_uInt16 nFontIx );
+ XclTxo( const XclExpRoot& rRoot, const SdrTextObj& rEditObj );
+ XclTxo( const XclExpRoot& rRoot, const EditTextObject& rEditObj, SdrObject* pCaption );
+
+ void SetHorAlign( sal_uInt8 nHorAlign ) { mnHorAlign = nHorAlign; }
+ void SetVerAlign( sal_uInt8 nVerAlign ) { mnVerAlign = nVerAlign; }
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+private:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+private:
+ XclExpStringRef mpString; /// Text and formatting data.
+ sal_uInt16 mnRotation; /// Text rotation.
+ sal_uInt8 mnHorAlign; /// Horizontal alignment.
+ sal_uInt8 mnVerAlign; /// Vertical alignment.
+};
+
+// --- class XclObjOle -----------------------------------------------
+
+class XclObjOle : public XclObj
+{
+private:
+
+ const SdrObject& rOleObj;
+ SotStorage* pRootStorage;
+
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+public:
+ XclObjOle( XclExpObjectManager& rObjMgr, const SdrObject& rObj );
+ virtual ~XclObjOle() override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// --- class XclObjAny -----------------------------------------------
+
+class XclObjAny : public XclObj
+{
+protected:
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+public:
+ XclObjAny( XclExpObjectManager& rObjMgr,
+ const css::uno::Reference< css::drawing::XShape >& rShape,
+ ScDocument* pDoc);
+ virtual ~XclObjAny() override;
+
+ const css::uno::Reference< css::drawing::XShape >&
+ GetShape() const { return mxShape; }
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+ static void WriteFromTo( XclExpXmlStream& rStrm, const XclObjAny& rObj );
+ static void WriteFromTo( XclExpXmlStream& rStrm, const css::uno::Reference< css::drawing::XShape >& rShape, SCTAB nTab );
+
+private:
+ css::uno::Reference< css::drawing::XShape >
+ mxShape;
+ ScDocument* mpDoc;
+};
+
+// --- class ExcBof8_Base --------------------------------------------
+
+class ExcBof8_Base : public ExcBof_Base
+{
+protected:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ ExcBof8_Base();
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// --- class ExcBofW8 ------------------------------------------------
+// Header Record for WORKBOOKS
+
+class ExcBofW8 : public ExcBof8_Base
+{
+public:
+ ExcBofW8();
+};
+
+// --- class ExcBof8 -------------------------------------------------
+// Header Record for WORKSHEETS
+
+class ExcBof8 : public ExcBof8_Base
+{
+public:
+ ExcBof8();
+};
+
+// --- class ExcBundlesheet8 -----------------------------------------
+
+class ExcBundlesheet8 : public ExcBundlesheetBase
+{
+private:
+ OUString sUnicodeName;
+ XclExpString GetName() const { return XclExpString( sUnicodeName, XclStrFlags::EightBitLength );}
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+public:
+ ExcBundlesheet8( const RootData& rRootData, SCTAB nTab );
+ ExcBundlesheet8( const OUString& rString );
+
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+// --- class XclObproj -----------------------------------------------
+
+class XclObproj : public ExcRecord
+{
+public:
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// ---- class XclCodename --------------------------------------------
+
+class XclCodename : public ExcRecord
+{
+private:
+ XclExpString aName;
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ XclCodename( const OUString& );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+// ---- Scenarios ----------------------------------------------------
+// - ExcEScenarioCell a cell of a scenario range
+// - ExcEScenario all ranges of a scenario table
+// - ExcEScenarioManager list of scenario tables
+
+class ExcEScenarioCell
+{
+private:
+ sal_uInt16 nCol;
+ sal_uInt16 nRow;
+ XclExpString sText;
+
+protected:
+public:
+ ExcEScenarioCell( sal_uInt16 nC, sal_uInt16 nR, const OUString& rTxt );
+
+ std::size_t GetStringBytes() const
+ { return sText.GetSize(); }
+
+ void WriteAddress( XclExpStream& rStrm ) const ;
+ void WriteText( XclExpStream& rStrm ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+class ExcEScenario : public ExcRecord
+{
+private:
+ std::size_t nRecLen;
+ XclExpString sName;
+ XclExpString sComment;
+ XclExpString sUserName;
+ bool bProtected;
+
+ std::vector<ExcEScenarioCell> aCells;
+
+ bool Append( sal_uInt16 nCol, sal_uInt16 nRow, const OUString& rTxt );
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+protected:
+public:
+ ExcEScenario( const XclExpRoot& rRoot, SCTAB nTab );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class ExcEScenarioManager : public ExcRecord
+{
+private:
+ sal_uInt16 nActive;
+ std::vector<ExcEScenario> aScenes;
+
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+
+protected:
+public:
+ ExcEScenarioManager( const XclExpRoot& rRoot, SCTAB nTab );
+ virtual ~ExcEScenarioManager() override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+};
+
+/** Represents a FEATHDR (SHEETPROTECTION) record that stores sheet protection
+ options. Note that a sheet still needs to save its sheet protection
+ options even when it's not protected. */
+class XclExpSheetProtectOptions : public XclExpRecord
+{
+public:
+ explicit XclExpSheetProtectOptions( const XclExpRoot& rRoot, SCTAB nTab );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnOptions; /// Encoded sheet protection options.
+};
+
+/** Represents one EnhancedProtection feature in a FEAT record.
+ To be written only if such feature exists. */
+class XclExpSheetEnhancedProtection : public XclExpRecord
+{
+public:
+ explicit XclExpSheetEnhancedProtection( const XclExpRoot& rRoot, const ScEnhancedProtection& rProt );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpRoot& mrRoot;
+ ScEnhancedProtection maEnhancedProtection;
+};
+
+class XclCalccount : public ExcRecord
+{
+private:
+ sal_uInt16 nCount;
+protected:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ XclCalccount( const ScDocument& );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclIteration : public ExcRecord
+{
+private:
+ sal_uInt16 nIter;
+protected:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ XclIteration( const ScDocument& );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclDelta : public ExcRecord
+{
+private:
+ double fDelta;
+protected:
+ virtual void SaveCont( XclExpStream& rStrm ) override;
+public:
+ XclDelta( const ScDocument& );
+
+ virtual sal_uInt16 GetNum() const override;
+ virtual std::size_t GetLen() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclRefmode : public XclExpBoolRecord
+{
+public:
+ XclRefmode( const ScDocument& );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpFileEncryption : public XclExpRecord
+{
+public:
+ explicit XclExpFileEncryption( const XclExpRoot& rRoot );
+ virtual ~XclExpFileEncryption() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpRoot& mrRoot;
+};
+
+/** Beginning of User Interface Records */
+class XclExpInterfaceHdr : public XclExpUInt16Record
+{
+public:
+ explicit XclExpInterfaceHdr( sal_uInt16 nCodePage );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+/** End of User Interface Records */
+class XclExpInterfaceEnd : public XclExpRecord
+{
+public:
+ explicit XclExpInterfaceEnd();
+ virtual ~XclExpInterfaceEnd() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+/** Write Access User Name - This record contains the user name, which is
+ the name you type when you install Excel. */
+class XclExpWriteAccess : public XclExpRecord
+{
+public:
+ explicit XclExpWriteAccess();
+ virtual ~XclExpWriteAccess() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+class XclExpFileSharing : public XclExpRecord
+{
+public:
+ explicit XclExpFileSharing( const XclExpRoot& rRoot, sal_uInt16 nPasswordHash, bool bRecommendReadOnly );
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpString maUserName;
+ sal_uInt16 mnPasswordHash;
+ bool mbRecommendReadOnly;
+};
+
+class XclExpProt4Rev : public XclExpRecord
+{
+public:
+ explicit XclExpProt4Rev();
+ virtual ~XclExpProt4Rev() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+class XclExpProt4RevPass : public XclExpRecord
+{
+public:
+ explicit XclExpProt4RevPass();
+ virtual ~XclExpProt4RevPass() override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+class XclExpRecalcId : public XclExpDummyRecord
+{
+public:
+ explicit XclExpRecalcId();
+};
+
+class XclExpBookExt : public XclExpDummyRecord
+{
+public:
+ explicit XclExpBookExt();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xechart.hxx b/sc/source/filter/inc/xechart.hxx
new file mode 100644
index 000000000..ff7dcc909
--- /dev/null
+++ b/sc/source/filter/inc/xechart.hxx
@@ -0,0 +1,1191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xlchart.hxx"
+#include "xlformula.hxx"
+#include "xlstyle.hxx"
+#include "xeroot.hxx"
+#include "xestring.hxx"
+
+#include <memory>
+#include <map>
+
+class Size;
+namespace tools { class Rectangle; }
+
+namespace com::sun::star {
+ namespace awt
+ {
+ struct Rectangle;
+ }
+ namespace frame
+ {
+ class XModel;
+ }
+ namespace chart
+ {
+ class XAxis;
+ }
+ namespace chart2
+ {
+ struct ScaleData;
+ class XChartDocument;
+ class XDiagram;
+ class XCoordinateSystem;
+ class XChartType;
+ class XDataSeries;
+ class XAxis;
+ class XTitle;
+ class XFormattedString;
+ class XRegressionCurve;
+ namespace data
+ {
+ class XDataSequence;
+ class XLabeledDataSequence;
+ }
+ }
+}
+
+// Common =====================================================================
+
+struct XclExpChRootData;
+class XclExpChChart;
+
+/** Base class for complex chart classes, provides access to other components
+ of the chart.
+
+ Keeps also track of future record levels and writes the needed future
+ records on demand.
+ */
+class XclExpChRoot : public XclExpRoot
+{
+public:
+ explicit XclExpChRoot( const XclExpRoot& rRoot, XclExpChChart& rChartData );
+ virtual ~XclExpChRoot() override;
+
+ XclExpChRoot(XclExpChRoot const &) = default;
+ XclExpChRoot(XclExpChRoot &&) = default;
+ XclExpChRoot & operator =(XclExpChRoot const &) = delete; // due to XclExpRoot
+ XclExpChRoot & operator =(XclExpChRoot &&) = delete; // due to XclExpRoot
+
+ /** Returns this root instance - for code readability in derived classes. */
+ const XclExpChRoot& GetChRoot() const { return *this; }
+ /** Returns the API Chart document model. */
+ css::uno::Reference< css::chart2::XChartDocument > const &
+ GetChartDocument() const;
+ /** Returns a reference to the parent chart data object. */
+ XclExpChChart& GetChartData() const;
+ /** Returns chart type info for a unique chart type identifier. */
+ const XclChTypeInfo& GetChartTypeInfo( XclChTypeId eType ) const;
+ /** Returns the first fitting chart type info for the passed service name. */
+ const XclChTypeInfo& GetChartTypeInfo( std::u16string_view rServiceName ) const;
+
+ /** Returns an info struct about auto formatting for the passed object type. */
+ const XclChFormatInfo& GetFormatInfo( XclChObjectType eObjType ) const;
+
+ /** Starts the API chart document conversion. Must be called once before all API conversion. */
+ void InitConversion( css::uno::Reference< css::chart2::XChartDocument > const & xChartDoc,
+ const tools::Rectangle& rChartRect ) const;
+ /** Finishes the API chart document conversion. Must be called once after all API conversion. */
+ void FinishConversion() const;
+
+ /** Returns true, if the passed color equals to the specified system color. */
+ bool IsSystemColor( const Color& rColor, sal_uInt16 nSysColorIdx ) const;
+ /** Sets a system color and the respective color identifier. */
+ void SetSystemColor( Color& rColor, sal_uInt32& rnColorId, sal_uInt16 nSysColorIdx ) const;
+
+ /** Converts the passed horizontal coordinate from 1/100 mm to Excel chart units. */
+ sal_Int32 CalcChartXFromHmm( sal_Int32 nPosX ) const;
+ /** Converts the passed vertical coordinate from 1/100 mm to Excel chart units. */
+ sal_Int32 CalcChartYFromHmm( sal_Int32 nPosY ) const;
+ /** Converts the passed rectangle from 1/100 mm to Excel chart units. */
+ XclChRectangle CalcChartRectFromHmm( const css::awt::Rectangle& rRect ) const;
+
+ /** Reads all line properties from the passed property set. */
+ void ConvertLineFormat(
+ XclChLineFormat& rLineFmt,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode ) const;
+ /** Reads solid area properties from the passed property set.
+ @return true = object contains complex fill properties. */
+ bool ConvertAreaFormat(
+ XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode ) const;
+ /** Reads gradient or bitmap area properties from the passed property set. */
+ void ConvertEscherFormat(
+ XclChEscherFormat& rEscherFmt,
+ XclChPicFormat& rPicFmt,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode ) const;
+ /** Reads font properties from the passed property set. */
+ sal_uInt16 ConvertFont(
+ const ScfPropertySet& rPropSet,
+ sal_Int16 nScript ) const;
+
+ /** Reads the pie rotation property and returns the converted angle. */
+ static sal_uInt16 ConvertPieRotation( const ScfPropertySet& rPropSet );
+
+protected:
+ /** Called from XclExpChGroupBase::Save, registers a new future record level. */
+ void RegisterFutureRecBlock( const XclChFrBlock& rFrBlock );
+ /** Called from XclExpChFutureRecordBase::Save, Initializes the current future record level. */
+ void InitializeFutureRecBlock( XclExpStream& rStrm );
+ /** Called from XclExpChGroupBase::Save, finalizes the current future record level. */
+ void FinalizeFutureRecBlock( XclExpStream& rStrm );
+
+private:
+ typedef std::shared_ptr< XclExpChRootData > XclExpChRootDataRef;
+ XclExpChRootDataRef mxChData; /// Reference to the root data object.
+};
+
+/** Base class for chart record groups. Provides helper functions to write sub records.
+
+ A chart record group consists of a header record, followed by a CHBEGIN
+ record, followed by group sub records, and finished with a CHEND record.
+ */
+class XclExpChGroupBase : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChGroupBase(
+ const XclExpChRoot& rRoot, sal_uInt16 nFrType,
+ sal_uInt16 nRecId, std::size_t nRecSize = 0 );
+ virtual ~XclExpChGroupBase() override;
+
+ /** Saves the header record. Calls WriteSubRecords() to let derived classes write sub records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ /** Derived classes return whether there are any records embedded in this group. */
+ virtual bool HasSubRecords() const;
+ /** Derived classes implement writing any records embedded in this group. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) = 0;
+
+protected:
+ /** Sets context information for future record blocks. */
+ void SetFutureRecordContext( sal_uInt16 nFrContext,
+ sal_uInt16 nFrValue1 = 0, sal_uInt16 nFrValue2 = 0 );
+
+private:
+ XclChFrBlock maFrBlock; /// Future records block settings.
+};
+
+/** Base class for chart future records. On saving, the record writes missing
+ CHFRBLOCKBEGIN records automatically.
+ */
+class XclExpChFutureRecordBase : public XclExpFutureRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChFutureRecordBase( const XclExpChRoot& rRoot,
+ XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize );
+
+ /** Writes missing CHFRBLOCKBEGIN records and this record. */
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+// Frame formatting ===========================================================
+
+class XclExpChFramePos : public XclExpRecord
+{
+public:
+ explicit XclExpChFramePos( sal_uInt16 nTLMode );
+
+ /** Returns read/write access to the frame position data. */
+ XclChFramePos& GetFramePosData() { return maData; }
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChFramePos maData; /// Position of the frame.
+};
+
+typedef rtl::Reference< XclExpChFramePos > XclExpChFramePosRef;
+
+class XclExpChLineFormat : public XclExpRecord
+{
+public:
+ explicit XclExpChLineFormat( const XclExpChRoot& rRoot );
+
+ /** Converts line formatting properties from the passed property set. */
+ void Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+ /** Sets or clears the automatic flag. */
+ void SetAuto( bool bAuto ) { ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO, bAuto ); }
+ /** Sets flag to show or hide an axis. */
+ void SetShowAxis( bool bShowAxis )
+ { ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS, bShowAxis ); }
+ /** Sets the line format to the specified default type. */
+ void SetDefault( XclChFrameType eDefFrameType );
+
+ /** Returns true, if the line format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO ); }
+ /** Returns true, if the line style is set to something visible. */
+ bool HasLine() const { return maData.mnPattern != EXC_CHLINEFORMAT_NONE; }
+ /** Returns true, if the line contains default formatting according to the passed frame type. */
+ bool IsDefault( XclChFrameType eDefFrameType ) const;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChLineFormat maData; /// Contents of the CHLINEFORMAT record.
+ sal_uInt32 mnColorId; /// Line color identifier.
+};
+
+typedef rtl::Reference< XclExpChLineFormat > XclExpChLineFormatRef;
+
+class XclExpChAreaFormat : public XclExpRecord
+{
+public:
+ explicit XclExpChAreaFormat( const XclExpChRoot& rRoot );
+
+ /** Converts area formatting properties from the passed property set.
+ @return true = object contains complex fill properties. */
+ bool Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+ /** Sets or clears the automatic flag. */
+ void SetAuto( bool bAuto ) { ::set_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO, bAuto ); }
+ /** Sets the area format to the specified default type. */
+ void SetDefault( XclChFrameType eDefFrameType );
+
+ /** Returns true, if the area format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO ); }
+ /** Returns true, if the area style is set to something visible. */
+ bool HasArea() const { return maData.mnPattern != EXC_PATT_NONE; }
+ /** Returns true, if the area contains default formatting according to the passed frame type. */
+ bool IsDefault( XclChFrameType eDefFrameType ) const;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChAreaFormat maData; /// Contents of the CHAREAFORMAT record.
+ sal_uInt32 mnPattColorId; /// Pattern color identifier.
+ sal_uInt32 mnBackColorId; /// Pattern background color identifier.
+};
+
+typedef rtl::Reference< XclExpChAreaFormat > XclExpChAreaFormatRef;
+
+class XclExpChEscherFormat : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChEscherFormat( const XclExpChRoot& rRoot );
+
+ /** Converts complex area formatting from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+
+ /** Returns true, if the object contains valid formatting data. */
+ bool IsValid() const;
+
+ /** Writes the CHESCHERFORMAT record group to the stream, if complex formatting is extant. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ /** Returns true, if this record group contains a CHPICFORMAT record. */
+ virtual bool HasSubRecords() const override;
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ /** Inserts a color from the contained Escher property set into the color palette. */
+ sal_uInt32 RegisterColor( sal_uInt16 nPropId );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChEscherFormat maData; /// Fill properties for complex areas (CHESCHERFORMAT record).
+ XclChPicFormat maPicFmt; /// Image options, e.g. stretched, stacked (CHPICFORMAT record).
+ sal_uInt32 mnColor1Id; /// First fill color identifier.
+ sal_uInt32 mnColor2Id; /// Second fill color identifier.
+};
+
+typedef rtl::Reference< XclExpChEscherFormat > XclExpChEscherFormatRef;
+
+/** Base class for record groups containing frame formatting.
+
+ Frame formatting can be part of several record groups, e.g. CHFRAME,
+ CHDATAFORMAT, CHDROPBAR. It consists of CHLINEFORMAT, CHAREAFORMAT, and
+ CHESCHERFORMAT group.
+ */
+class XclExpChFrameBase
+{
+public:
+ explicit XclExpChFrameBase();
+ virtual ~XclExpChFrameBase();
+
+protected:
+ /** Converts frame formatting properties from the passed property set. */
+ void ConvertFrameBase( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+ /** Sets the frame formatting to the specified default type. */
+ void SetDefaultFrameBase( const XclExpChRoot& rRoot,
+ XclChFrameType eDefFrameType, bool bIsFrame );
+
+ /** Returns true, if the frame contains default formatting (as if the frame is missing). */
+ bool IsDefaultFrameBase( XclChFrameType eDefFrameType ) const;
+
+ /** Writes all contained frame records to the passed stream. */
+ void WriteFrameRecords( XclExpStream& rStrm );
+
+private:
+ XclExpChLineFormatRef mxLineFmt; /// Line format (CHLINEFORMAT record).
+ XclExpChAreaFormatRef mxAreaFmt; /// Area format (CHAREAFORMAT record).
+ XclExpChEscherFormatRef mxEscherFmt; /// Complex area format (CHESCHERFORMAT record).
+};
+
+/** Represents the CHFRAME record group containing object frame properties.
+
+ The CHFRAME group consists of: CHFRAME, CHBEGIN, CHLINEFORMAT,
+ CHAREAFORMAT, CHESCHERFORMAT group, CHEND.
+ */
+class XclExpChFrame : public XclExpChGroupBase, public XclExpChFrameBase
+{
+public:
+ explicit XclExpChFrame( const XclExpChRoot& rRoot, XclChObjectType eObjType );
+
+ /** Converts frame formatting properties from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+ /** Sets the specified automatic flags. */
+ void SetAutoFlags( bool bAutoPos, bool bAutoSize );
+
+ /** Returns true, if the frame object contains default formats. */
+ bool IsDefault() const;
+ /** Returns true, if the frame object can be deleted because it contains default formats. */
+ bool IsDeleteable() const;
+
+ /** Writes the entire record group. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChFrame maData; /// Contents of the CHFRAME record.
+ XclChObjectType meObjType; /// Type of the represented object.
+};
+
+typedef rtl::Reference< XclExpChFrame > XclExpChFrameRef;
+
+// Source links ===============================================================
+
+class XclExpChSourceLink : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChSourceLink( const XclExpChRoot& rRoot, sal_uInt8 nDestType );
+
+ void ConvertString( const OUString& aString );
+ /** Converts the passed source link, returns the number of linked values. */
+ sal_uInt16 ConvertDataSequence( css::uno::Reference< css::chart2::data::XDataSequence > const & xDataSeq,
+ bool bSplitToColumns, sal_uInt16 nDefCount = 0 );
+ /** Converts the passed sequence of formatted string objects, returns leading font index. */
+ sal_uInt16 ConvertStringSequence( const css::uno::Sequence< css::uno::Reference< css::chart2::XFormattedString > >& rStringSeq );
+ /** Converts the number format from the passed property set. */
+ void ConvertNumFmt( const ScfPropertySet& rPropSet, bool bPercent );
+
+ void AppendString( std::u16string_view rStr );
+
+ /** Returns true, if this source link contains explicit string data. */
+ bool HasString() const { return mxString && !mxString->IsEmpty(); }
+
+ /** Writes the CHSOURCELINK record and optionally a CHSTRING record with explicit string data. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChSourceLink maData; /// Contents of the CHSOURCELINK record.
+ XclTokenArrayRef mxLinkFmla; /// Formula with link to source data.
+ XclExpStringRef mxString; /// Text data (CHSTRING record).
+};
+
+typedef rtl::Reference< XclExpChSourceLink > XclExpChSourceLinkRef;
+
+// Text =======================================================================
+
+/** The CHFONT record containing a font index for text objects. */
+class XclExpChFont : public XclExpUInt16Record
+{
+public:
+ explicit XclExpChFont( sal_uInt16 nFontIdx );
+};
+
+typedef rtl::Reference< XclExpChFont > XclExpChFontRef;
+
+/** The CHOBJECTLINK record linking a text object to a specific chart object. */
+class XclExpChObjectLink : public XclExpRecord
+{
+public:
+ explicit XclExpChObjectLink( sal_uInt16 nLinkTarget, const XclChDataPointPos& rPointPos );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChObjectLink maData; /// Contents of the CHOBJECTLINK record.
+};
+
+typedef rtl::Reference< XclExpChObjectLink > XclExpChObjectLinkRef;
+
+/** Additional data label settings in the future record CHFRLABELPROPS. */
+class XclExpChFrLabelProps : public XclExpChFutureRecordBase
+{
+public:
+ explicit XclExpChFrLabelProps( const XclExpChRoot& rRoot );
+
+ /** Converts separator and the passed data label flags. */
+ void Convert(
+ const ScfPropertySet& rPropSet,
+ bool bShowCateg, bool bShowValue,
+ bool bShowPercent, bool bShowBubble );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChFrLabelProps maData; /// Contents of the CHFRLABELPROPS record.
+};
+
+typedef rtl::Reference< XclExpChFrLabelProps > XclExpChFrLabelPropsRef;
+
+/** Base class for objects with font settings. Provides font conversion helper functions. */
+class XclExpChFontBase
+{
+public:
+ virtual ~XclExpChFontBase();
+
+ /** Derived classes set font color and color identifier to internal data structures. */
+ virtual void SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId ) = 0;
+ /** Derived classes set text rotation to internal data structures. */
+ virtual void SetRotation( sal_uInt16 nRotation ) = 0;
+
+ /** Creates a CHFONT record from the passed font index, calls virtual function SetFont(). */
+ void ConvertFontBase( const XclExpChRoot& rRoot, sal_uInt16 nFontIdx );
+ /** Creates a CHFONT record from the passed font index, calls virtual function SetFont(). */
+ void ConvertFontBase( const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet );
+ /** Converts rotation settings, calls virtual function SetRotation(). */
+ void ConvertRotationBase( const ScfPropertySet& rPropSet, bool bSupportsStacked );
+};
+
+/** Represents the CHTEXT record group containing text object properties.
+
+ The CHTEXT group consists of: CHTEXT, CHBEGIN, CHFRAMEPOS, CHFONT,
+ CHFORMATRUNS, CHSOURCELINK, CHSTRING, CHFRAME group, CHOBJECTLINK, and CHEND.
+ */
+class XclExpChText : public XclExpChGroupBase, public XclExpChFontBase
+{
+public:
+ explicit XclExpChText( const XclExpChRoot& rRoot );
+
+ /** Sets font color and color identifier to internal data structures. */
+ virtual void SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId ) override;
+ /** Sets text rotation to internal data structures. */
+ virtual void SetRotation( sal_uInt16 nRotation ) override;
+
+ /** Converts all text settings of the passed title text object. */
+ void ConvertTitle( css::uno::Reference< css::chart2::XTitle > const & xTitle, sal_uInt16 nTarget, const OUString* pSubTitle );
+ /** Converts all text settings of the passed legend. */
+ void ConvertLegend( const ScfPropertySet& rPropSet );
+ /** Converts all settings of the passed data point caption text object. */
+ bool ConvertDataLabel( const ScfPropertySet& rPropSet,
+ const XclChTypeInfo& rTypeInfo, const XclChDataPointPos& rPointPos );
+ /** Converts all settings of the passed trend line equation box. */
+ void ConvertTrendLineEquation( const ScfPropertySet& rPropSet, const XclChDataPointPos& rPointPos );
+
+ /** Returns true, if the string object does not contain any text data. */
+ bool HasString() const { return mxSrcLink && mxSrcLink->HasString(); }
+ /** Returns the flags needed for the CHATTACHEDLABEL record. */
+ sal_uInt16 GetAttLabelFlags() const;
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChText maData; /// Contents of the CHTEXT record.
+ XclExpChFramePosRef mxFramePos; /// Relative text frame position (CHFRAMEPOS record).
+ XclExpChSourceLinkRef mxSrcLink; /// Linked data (CHSOURCELINK with CHSTRING record).
+ XclExpChFrameRef mxFrame; /// Text object frame properties (CHFRAME group).
+ XclExpChFontRef mxFont; /// Index into font buffer (CHFONT record).
+ XclExpChObjectLinkRef mxObjLink; /// Link target for this text object.
+ XclExpChFrLabelPropsRef mxLabelProps; /// Extended data label properties (CHFRLABELPROPS record).
+ sal_uInt32 mnTextColorId; /// Text color identifier.
+};
+
+typedef rtl::Reference< XclExpChText > XclExpChTextRef;
+
+// Data series ================================================================
+
+/** The CHMARKERFORMAT record containing data point marker formatting data. */
+class XclExpChMarkerFormat : public XclExpRecord
+{
+public:
+ explicit XclExpChMarkerFormat( const XclExpChRoot& rRoot );
+
+ /** Converts symbol properties from the passed property set. */
+ void Convert( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx );
+ /** Converts symbol properties for stock charts from the passed property set. */
+ void ConvertStockSymbol( const XclExpChRoot& rRoot,
+ const ScfPropertySet& rPropSet, bool bCloseSymbol );
+
+ /** Returns true, if markers are enabled. */
+ bool HasMarker() const { return maData.mnMarkerType != EXC_CHMARKERFORMAT_NOSYMBOL; }
+ /** Returns true, if border line of markers is visible. */
+ bool HasLineColor() const { return !::get_flag( maData.mnFlags, EXC_CHMARKERFORMAT_NOLINE ); }
+ /** Returns true, if fill area of markers is visible. */
+ bool HasFillColor() const { return !::get_flag( maData.mnFlags, EXC_CHMARKERFORMAT_NOFILL ); }
+
+private:
+ /** Registers marker colors in palette and stores color identifiers. */
+ void RegisterColors( const XclExpChRoot& rRoot );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChMarkerFormat maData; /// Contents of the CHMARKERFORMAT record.
+ sal_uInt32 mnLineColorId; /// Border line color identifier.
+ sal_uInt32 mnFillColorId; /// Fill color identifier.
+};
+
+typedef rtl::Reference< XclExpChMarkerFormat > XclExpChMarkerFormatRef;
+
+/** The CHPIEFORMAT record containing data point formatting data for pie segments. */
+class XclExpChPieFormat : public XclExpUInt16Record
+{
+public:
+ explicit XclExpChPieFormat();
+
+ /** Sets pie segment properties from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+};
+
+typedef rtl::Reference< XclExpChPieFormat > XclExpChPieFormatRef;
+
+/** The CH3DDATAFORMAT record containing the bar type in 3D bar charts. */
+class XclExpCh3dDataFormat : public XclExpRecord
+{
+public:
+ explicit XclExpCh3dDataFormat();
+
+ /** Sets 3d bar properties from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclCh3dDataFormat maData; /// Contents of the CH3DDATAFORMAT record.
+};
+
+typedef rtl::Reference< XclExpCh3dDataFormat > XclExpCh3dDataFormatRef;
+
+/** The CHATTACHEDLABEL record that contains the type of a data point label. */
+class XclExpChAttachedLabel : public XclExpUInt16Record
+{
+public:
+ explicit XclExpChAttachedLabel( sal_uInt16 nFlags );
+};
+
+typedef rtl::Reference< XclExpChAttachedLabel > XclExpChAttLabelRef;
+
+/** Represents the CHDATAFORMAT record group containing data point properties.
+
+ The CHDATAFORMAT group consists of: CHDATAFORMAT, CHBEGIN, CHFRAME group,
+ CHMARKERFORMAT, CHPIEFORMAT, CH3DDATAFORMAT, CHSERIESFORMAT,
+ CHATTACHEDLABEL, CHEND.
+ */
+class XclExpChDataFormat : public XclExpChGroupBase, public XclExpChFrameBase
+{
+public:
+ explicit XclExpChDataFormat( const XclExpChRoot& rRoot,
+ const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx );
+
+ /** Converts the passed data series or data point formatting. */
+ void ConvertDataSeries( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo );
+ /** Sets default formatting for a series in a stock chart. */
+ void ConvertStockSeries( const ScfPropertySet& rPropSet, bool bCloseSymbol );
+ /** Converts line formatting for the specified object (e.g. trend lines, error bars). */
+ void ConvertLine( const ScfPropertySet& rPropSet, XclChObjectType eObjType );
+
+ /** Returns true, if this objects describes the formatting of an entire series. */
+ bool IsSeriesFormat() const { return maData.maPointPos.mnPointIdx == EXC_CHDATAFORMAT_ALLPOINTS; }
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChDataFormat maData; /// Contents of the CHDATAFORMAT record.
+ XclExpChMarkerFormatRef mxMarkerFmt; /// Data point marker (CHMARKERFORMAT record).
+ XclExpChPieFormatRef mxPieFmt; /// Pie segment format (CHPIEFORMAT record).
+ XclExpRecordRef mxSeriesFmt; /// Series properties (CHSERIESFORMAT record).
+ XclExpCh3dDataFormatRef mx3dDataFmt; /// 3D bar format (CH3DDATAFORMAT record).
+ XclExpChAttLabelRef mxAttLabel; /// Data point label type (CHATTACHEDLABEL record).
+};
+
+typedef rtl::Reference< XclExpChDataFormat > XclExpChDataFormatRef;
+
+/** Represents the CHSERTRENDLINE record containing settings for a trend line. */
+class XclExpChSerTrendLine : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChSerTrendLine( const XclExpChRoot& rRoot );
+
+ /** Converts the passed trend line, returns true if trend line type is supported. */
+ bool Convert( css::uno::Reference< css::chart2::XRegressionCurve > const & xRegCurve,
+ sal_uInt16 nSeriesIdx );
+
+ /** Returns formatting information of the trend line, created in Convert(). */
+ const XclExpChDataFormatRef& GetDataFormat() const { return mxDataFmt; }
+ /** Returns formatting of the equation text box, created in Convert(). */
+ const XclExpChTextRef& GetDataLabel() const { return mxLabel; }
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChSerTrendLine maData; /// Contents of the CHSERTRENDLINE record.
+ XclExpChDataFormatRef mxDataFmt; /// Formatting settings of the trend line.
+ XclExpChTextRef mxLabel; /// Formatting of the equation text box.
+};
+
+typedef rtl::Reference< XclExpChSerTrendLine > XclExpChSerTrendLineRef;
+
+/** Represents the CHSERERRORBAR record containing settings for error bars. */
+class XclExpChSerErrorBar : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChSerErrorBar( const XclExpChRoot& rRoot, sal_uInt8 nBarType );
+
+ /** Converts the passed error bar settings, returns true if error bar type is supported. */
+ bool Convert( XclExpChSourceLink& rValueLink, sal_uInt16& rnValueCount, const ScfPropertySet& rPropSet );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChSerErrorBar maData; /// Contents of the CHSERERRORBAR record.
+};
+
+typedef rtl::Reference< XclExpChSerErrorBar > XclExpChSerErrorBarRef;
+
+/** Represents the CHSERIES record group describing a data series in a chart.
+
+ The CHSERIES group consists of: CHSERIES, CHBEGIN, CHSOURCELINK groups,
+ CHDATAFORMAT groups, CHSERGROUP, CHSERPARENT, CHSERERRORBAR,
+ CHSERTRENDLINE, CHEND.
+ */
+class XclExpChSeries : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChSeries( const XclExpChRoot& rRoot, sal_uInt16 nSeriesIdx );
+
+ /** Converts the passed data series (source links and formatting). */
+ bool ConvertDataSeries(
+ css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ const XclChExtTypeInfo& rTypeInfo,
+ sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx );
+ /** Converts the passed data series for stock charts. */
+ bool ConvertStockSeries(
+ css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole,
+ sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx, bool bCloseSymbol );
+ /** Converts the passed error bar settings (called at trend line child series). */
+ bool ConvertTrendLine( const XclExpChSeries& rParent,
+ css::uno::Reference< css::chart2::XRegressionCurve > const & xRegCurve );
+ /** Converts the passed error bar settings (called at error bar child series). */
+ bool ConvertErrorBar( const XclExpChSeries& rParent, const ScfPropertySet& rPropSet, sal_uInt8 nBarId );
+ /** Converts and inserts category ranges for all inserted series. */
+ void ConvertCategSequence( css::uno::Reference< css::chart2::data::XLabeledDataSequence > const & xCategSeq );
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ /** Initializes members of this series to represent a child of the passed series. */
+ void InitFromParent( const XclExpChSeries& rParent );
+ /** Tries to create trend line series objects (called at parent series). */
+ void CreateTrendLines( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries );
+ /** Tries to create positive and negative error bar series objects (called at parent series). */
+ void CreateErrorBars( const ScfPropertySet& rPropSet,
+ const OUString& rBarPropName,
+ sal_uInt8 nPosBarId, sal_uInt8 nNegBarId );
+ /** Tries to create an error bar series object (called at parent series). */
+ void CreateErrorBar( const ScfPropertySet& rPropSet,
+ const OUString& rShowPropName, sal_uInt8 nBarId );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+
+private:
+ XclChSeries maData; /// Contents of the CHSERIES record.
+ XclExpChSourceLinkRef mxTitleLink; /// Link data for series title.
+ XclExpChSourceLinkRef mxValueLink; /// Link data for series values.
+ XclExpChSourceLinkRef mxCategLink; /// Link data for series category names.
+ XclExpChSourceLinkRef mxBubbleLink; /// Link data for series bubble sizes.
+ XclExpChDataFormatRef mxSeriesFmt; /// CHDATAFORMAT group for series format.
+ XclExpRecordList< XclExpChDataFormat >
+ maPointFmts; /// CHDATAFORMAT groups for data point formats.
+ XclExpChSerTrendLineRef mxTrendLine; /// Trend line settings (CHSERTRENDLINE record).
+ XclExpChSerErrorBarRef mxErrorBar; /// Error bar settings (CHSERERRORBAR record).
+ sal_uInt16 mnGroupIdx; /// Chart type group (CHTYPEGROUP group) this series is assigned to.
+ sal_uInt16 mnSeriesIdx; /// 0-based series index.
+ sal_uInt16 mnParentIdx; /// 0-based index of parent series (trend lines and error bars).
+};
+
+typedef rtl::Reference< XclExpChSeries > XclExpChSeriesRef;
+
+// Chart type groups ==========================================================
+
+/** Represents the chart type record for all supported chart types. */
+class XclExpChType : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChType( const XclExpChRoot& rRoot );
+
+ /** Converts the passed chart type and the contained data series. */
+ void Convert( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool bSwappedAxesSet, bool bHasXLabels );
+ /** Sets stacking mode (standard or percent) for the series in this chart type group. */
+ void SetStacked( bool bPercent );
+
+ /** Returns true, if this is object represents a valid chart type. */
+ bool IsValidType() const { return maTypeInfo.meTypeId != EXC_CHTYPEID_UNKNOWN; }
+ /** Returns the chart type info struct for the contained chart type. */
+ const XclChTypeInfo& GetTypeInfo() const { return maTypeInfo; }
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChType maData; /// Contents of the chart type record.
+ XclChTypeInfo maTypeInfo; /// Chart type info for the contained type.
+};
+
+/** Represents the CHCHART3D record that contains 3D view settings. */
+class XclExpChChart3d : public XclExpRecord
+{
+public:
+ explicit XclExpChChart3d();
+
+ /** Converts 3d settings for the passed chart type. */
+ void Convert( const ScfPropertySet& rPropSet, bool b3dWallChart );
+ /** Sets flag that the data points are clustered on the X axis. */
+ void SetClustered() { ::set_flag( maData.mnFlags, EXC_CHCHART3D_CLUSTER ); }
+
+ /** Returns true, if the data points are clustered on the X axis. */
+ bool IsClustered() const { return ::get_flag( maData.mnFlags, EXC_CHCHART3D_CLUSTER ); }
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChChart3d maData; /// Contents of the CHCHART3D record.
+};
+
+typedef rtl::Reference< XclExpChChart3d > XclExpChChart3dRef;
+
+/** Represents the CHLEGEND record group describing the chart legend.
+
+ The CHLEGEND group consists of: CHLEGEND, CHBEGIN, CHFRAMEPOS, CHFRAME
+ group, CHTEXT group, CHEND.
+ */
+class XclExpChLegend : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChLegend( const XclExpChRoot& rRoot );
+
+ /** Converts all legend settings from the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChLegend maData; /// Contents of the CHLEGEND record.
+ XclExpChFramePosRef mxFramePos; /// Legend frame position (CHFRAMEPOS record).
+ XclExpChTextRef mxText; /// Legend text format (CHTEXT group).
+ XclExpChFrameRef mxFrame; /// Legend frame format (CHFRAME group).
+};
+
+typedef rtl::Reference< XclExpChLegend > XclExpChLegendRef;
+
+/** Represents the CHDROPBAR record group describing pos/neg bars in line charts.
+
+ The CHDROPBAR group consists of: CHDROPBAR, CHBEGIN, CHLINEFORMAT,
+ CHAREAFORMAT, CHESCHERFORMAT group, CHEND.
+ */
+class XclExpChDropBar : public XclExpChGroupBase, public XclExpChFrameBase
+{
+public:
+ explicit XclExpChDropBar( const XclExpChRoot& rRoot, XclChObjectType eObjType );
+
+ /** Converts and writes the contained frame data to the passed property set. */
+ void Convert( const ScfPropertySet& rPropSet );
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChObjectType meObjType; /// Type of the dropbar.
+};
+
+typedef rtl::Reference< XclExpChDropBar > XclExpChDropBarRef;
+
+/** Represents the CHTYPEGROUP record group describing a group of series.
+
+ The CHTYPEGROUP group consists of: CHTYPEGROUP, CHBEGIN, a chart type
+ record (e.g. CHBAR, CHLINE, CHAREA, CHPIE, ...), CHCHART3D, CHLEGEND group,
+ CHDROPBAR groups, CHCHARTLINE groups (CHCHARTLINE with CHLINEFORMAT),
+ CHDATAFORMAT group, CHEND.
+ */
+class XclExpChTypeGroup : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChTypeGroup( const XclExpChRoot& rRoot, sal_uInt16 nGroupIdx );
+
+ /** Converts the passed chart type to Excel type settings. */
+ void ConvertType( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx, bool b3dChart, bool bSwappedAxesSet, bool bHasXLabels );
+ /** Converts and inserts all series from the passed chart type. */
+ void ConvertSeries( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nGroupAxesSetIdx, bool bPercent, bool bConnectorLines );
+ /** Converts and inserts category ranges for all inserted series. */
+ void ConvertCategSequence( css::uno::Reference< css::chart2::data::XLabeledDataSequence > const & xCategSeq );
+ /** Creates a legend object and converts all legend settings. */
+ void ConvertLegend( const ScfPropertySet& rPropSet );
+
+ /** Returns true, if this chart type group contains at least one valid series. */
+ bool IsValidGroup() const { return !maSeries.IsEmpty() && maType.IsValidType(); }
+ /** Returns the index of this chart type group format. */
+ sal_uInt16 GetGroupIdx() const { return maData.mnGroupIdx; }
+ /** Returns the chart type info struct for the contained chart type. */
+ const XclChExtTypeInfo& GetTypeInfo() const { return maTypeInfo; }
+ /** Returns true, if the chart is three-dimensional. */
+ bool Is3dChart() const { return maTypeInfo.mb3dChart; }
+ /** Returns true, if chart type supports wall and floor format. */
+ bool Is3dWallChart() const { return Is3dChart() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE); }
+ /** Returns true, if the series in this chart type group are ordered on the Z axis. */
+ bool Is3dDeepChart() const { return Is3dWallChart() && mxChart3d && !mxChart3d->IsClustered(); }
+ /** Returns true, if this chart type can be combined with other types. */
+ bool IsCombinable2d() const { return !Is3dChart() && maTypeInfo.mbCombinable2d; }
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns an unused format index to be used for the next created series. */
+ sal_uInt16 GetFreeFormatIdx() const;
+ /** Creates all data series of any chart type except stock charts. */
+ void CreateDataSeries( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries );
+ /** Creates all data series of a stock chart. */
+ void CreateAllStockSeries( css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries );
+ /** Creates a single data series of a stock chart. */
+ bool CreateStockSeries( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
+ std::u16string_view rValueRole, bool bCloseSymbol );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpChSeries > XclExpChSeriesList;
+
+ XclChTypeGroup maData; /// Contents of the CHTYPEGROUP record.
+ XclExpChType maType; /// Chart type (e.g. CHBAR, CHLINE, ...).
+ XclChExtTypeInfo maTypeInfo; /// Extended chart type info.
+ XclExpChSeriesList maSeries; /// List of series data (CHSERIES groups).
+ XclExpChChart3dRef mxChart3d; /// 3D settings (CHCHART3D record).
+ XclExpChLegendRef mxLegend; /// Chart legend (CHLEGEND group).
+ XclExpChDropBarRef mxUpBar; /// White dropbars (CHDROPBAR group).
+ XclExpChDropBarRef mxDownBar; /// Black dropbars (CHDROPBAR group).
+ std::map<sal_uInt16, std::unique_ptr<XclExpChLineFormat>>
+ m_ChartLines; /// Global line formats (CHCHARTLINE group).
+};
+
+typedef rtl::Reference< XclExpChTypeGroup > XclExpChTypeGroupRef;
+
+// Axes =======================================================================
+
+class XclExpChLabelRange : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChLabelRange( const XclExpChRoot& rRoot );
+
+ /** Converts category axis scaling settings. */
+ void Convert( const css::chart2::ScaleData& rScaleData,
+ const ScfPropertySet& rChart1Axis, bool bMirrorOrient );
+ /** Converts position settings of a crossing axis at this axis. */
+ void ConvertAxisPosition( const ScfPropertySet& rPropSet );
+ /** Sets flag for tickmark position between categories or on categories. */
+ void SetTicksBetweenCateg( bool bTicksBetween )
+ { ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_BETWEEN, bTicksBetween ); }
+
+private:
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChLabelRange maLabelData; /// Contents of the CHLABELRANGE record.
+ XclChDateRange maDateData; /// Contents of the CHDATERANGE record.
+};
+
+typedef rtl::Reference< XclExpChLabelRange > XclExpChLabelRangeRef;
+
+class XclExpChValueRange : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChValueRange( const XclExpChRoot& rRoot );
+
+ /** Converts value axis scaling settings. */
+ void Convert( const css::chart2::ScaleData& rScaleData );
+ /** Converts position settings of a crossing axis at this axis. */
+ void ConvertAxisPosition( const ScfPropertySet& rPropSet );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChValueRange maData; /// Contents of the CHVALUERANGE record.
+};
+
+typedef rtl::Reference< XclExpChValueRange > XclExpChValueRangeRef;
+
+class XclExpChTick : public XclExpRecord, protected XclExpChRoot
+{
+public:
+ explicit XclExpChTick( const XclExpChRoot& rRoot );
+
+ /** Converts axis tick mark settings. */
+ void Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType );
+ /** Sets font color and color identifier to internal data structures. */
+ void SetFontColor( const Color& rColor, sal_uInt32 nColorId );
+ /** Sets text rotation to internal data structures. */
+ void SetRotation( sal_uInt16 nRotation );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChTick maData; /// Contents of the CHTICK record.
+ sal_uInt32 mnTextColorId; /// Axis labels text color identifier.
+};
+
+typedef rtl::Reference< XclExpChTick > XclExpChTickRef;
+
+/** Represents the CHAXIS record group describing an entire chart axis.
+
+ The CHAXIS group consists of: CHAXIS, CHBEGIN, CHLABELRANGE, CHEXTRANGE,
+ CHVALUERANGE, CHFORMAT, CHTICK, CHFONT, CHAXISLINE groups (CHAXISLINE with
+ CHLINEFORMAT, CHAREAFORMAT, and CHESCHERFORMAT group), CHEND.
+ */
+class XclExpChAxis : public XclExpChGroupBase, public XclExpChFontBase
+{
+public:
+ explicit XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType );
+
+ /** Sets font color and color identifier to internal data structures. */
+ virtual void SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId ) override;
+ /** Sets text rotation to internal data structures. */
+ virtual void SetRotation( sal_uInt16 nRotation ) override;
+
+ /** Converts formatting and scaling settings from the passed axis. */
+ void Convert( css::uno::Reference< css::chart2::XAxis > const & xAxis,
+ css::uno::Reference< css::chart2::XAxis > const & xCrossingAxis,
+ css::uno::Reference< css::chart::XAxis > const & xChart1Axis,
+ const XclChExtTypeInfo& rTypeInfo );
+ /** Converts and writes 3D wall/floor properties from the passed diagram. */
+ void ConvertWall( css::uno::Reference< css::chart2::XDiagram > const & xDiagram );
+
+ /** Returns the type of this axis. */
+ sal_uInt16 GetAxisType() const { return maData.mnType; }
+ /** Returns the axis dimension index used by the chart API. */
+ sal_Int32 GetApiAxisDimension() const { return maData.GetApiAxisDimension(); }
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChAxis maData; /// Contents of the CHAXIS record.
+ XclExpChLabelRangeRef mxLabelRange; /// Category scaling (CHLABELRANGE record).
+ XclExpChValueRangeRef mxValueRange; /// Value scaling (CHVALUERANGE record).
+ XclExpChTickRef mxTick; /// Axis ticks (CHTICK record).
+ XclExpChFontRef mxFont; /// Index into font buffer (CHFONT record).
+ XclExpChLineFormatRef mxAxisLine; /// Axis line format (CHLINEFORMAT record).
+ XclExpChLineFormatRef mxMajorGrid; /// Major grid line format (CHLINEFORMAT record).
+ XclExpChLineFormatRef mxMinorGrid; /// Minor grid line format (CHLINEFORMAT record).
+ XclExpChFrameRef mxWallFrame; /// Wall/floor format (sub records of CHFRAME group).
+ sal_uInt16 mnNumFmtIdx; /// Index into number format buffer (CHFORMAT record).
+};
+
+typedef rtl::Reference< XclExpChAxis > XclExpChAxisRef;
+
+/** Represents the CHAXESSET record group describing an axes set (X/Y/Z axes).
+
+ The CHAXESSET group consists of: CHAXESSET, CHBEGIN, CHFRAMEPOS, CHAXIS
+ groups, CHTEXT groups, CHPLOTFRAME group (CHPLOTFRAME with CHFRAME group),
+ CHTYPEGROUP group, CHEND.
+ */
+class XclExpChAxesSet : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId );
+
+ /** Converts the passed diagram to chart record data.
+ @return First unused chart type group index. */
+ sal_uInt16 Convert( css::uno::Reference< css::chart2::XDiagram > const & xDiagram,
+ sal_uInt16 nFirstGroupIdx );
+
+ /** Returns true, if this axes set exists (returns false if this is a dummy object). */
+ bool IsValidAxesSet() const { return !maTypeGroups.IsEmpty(); }
+ /** Returns the index of the axes set (primary/secondary). */
+ sal_uInt16 GetAxesSetId() const { return maData.mnAxesSetId; }
+ /** Returns the axes set index used by the chart API. */
+ sal_Int32 GetApiAxesSetIndex() const { return maData.GetApiAxesSetIndex(); }
+ /** Returns true, if the chart is three-dimensional. */
+ bool Is3dChart() const;
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns first inserted chart type group. */
+ XclExpChTypeGroupRef GetFirstTypeGroup() const;
+ /** Returns last inserted chart type group. */
+ XclExpChTypeGroupRef GetLastTypeGroup() const;
+
+ /** Converts a complete axis object including axis title. */
+ void ConvertAxis( XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType,
+ XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget,
+ css::uno::Reference< css::chart2::XCoordinateSystem > const & xCoordSystem,
+ const XclChExtTypeInfo& rTypeInfo,
+ sal_Int32 nCrossingAxisDim );
+
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclChAxesSet maData; /// Contents of the CHAXESSET record.
+ XclExpChFramePosRef mxFramePos; /// Outer plot area position (CHFRAMEPOS record).
+ XclExpChAxisRef mxXAxis; /// The X axis (CHAXIS group).
+ XclExpChAxisRef mxYAxis; /// The Y axis (CHAXIS group).
+ XclExpChAxisRef mxZAxis; /// The Z axis (CHAXIS group).
+ XclExpChTextRef mxXAxisTitle; /// The X axis title (CHTEXT group).
+ XclExpChTextRef mxYAxisTitle; /// The Y axis title (CHTEXT group).
+ XclExpChTextRef mxZAxisTitle; /// The Z axis title (CHTEXT group).
+ XclExpChFrameRef mxPlotFrame; /// Plot area (CHPLOTFRAME group).
+ XclExpRecordList< XclExpChTypeGroup >
+ maTypeGroups; /// Chart type groups (CHTYPEGROUP group).
+};
+
+typedef std::shared_ptr< XclExpChAxesSet > XclExpChAxesSetRef;
+
+// The chart object ===========================================================
+
+/** Represents the CHCHART record group describing the chart contents.
+
+ The CHCHART group consists of: CHCHART, CHBEGIN, SCL, CHPLOTGROWTH, CHFRAME
+ group, CHSERIES groups, CHPROPERTIES, CHDEFAULTTEXT groups (CHDEFAULTTEXT
+ with CHTEXT groups), CHUSEDAXESSETS, CHAXESSET groups, CHTEXT groups, CHEND.
+ */
+class XclExpChChart : public XclExpChGroupBase
+{
+public:
+ explicit XclExpChChart( const XclExpRoot& rRoot,
+ css::uno::Reference< css::chart2::XChartDocument > const & xChartDoc,
+ const tools::Rectangle& rChartRect );
+
+ /** Creates, registers and returns a new data series object. */
+ XclExpChSeriesRef CreateSeries();
+ /** Removes the last created data series object from the series list. */
+ void RemoveLastSeries();
+ /** Stores a CHTEXT group that describes a data point label. */
+ void SetDataLabel( XclExpChTextRef const & xText );
+ /** Sets the plot area position and size to manual mode. */
+ void SetManualPlotArea();
+
+ /** Writes all embedded records. */
+ virtual void WriteSubRecords( XclExpStream& rStrm ) override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpChSeries > XclExpChSeriesList;
+
+ XclChRectangle maRect; /// Position of the chart on the sheet (CHCHART record).
+ XclExpChSeriesList maSeries; /// List of series data (CHSERIES groups).
+ XclExpChFrameRef mxFrame; /// Chart frame format (CHFRAME group).
+ XclChProperties maProps; /// Chart properties (CHPROPERTIES record).
+ XclExpChAxesSetRef mxPrimAxesSet; /// Primary axes set (CHAXESSET group).
+ XclExpChAxesSetRef mxSecnAxesSet; /// Secondary axes set (CHAXESSET group).
+ XclExpChTextRef mxTitle; /// Chart title (CHTEXT group).
+ XclExpRecordList< XclExpChText >
+ maLabels; /// Data point labels (CHTEXT groups).
+};
+
+/** Represents the group of DFF and OBJ records containing all drawing shapes
+ embedded in the chart object.
+ */
+class XclExpChartDrawing : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpChartDrawing(
+ const XclExpRoot& rRoot,
+ const css::uno::Reference< css::frame::XModel >& rxModel,
+ const Size& rChartSize );
+ virtual ~XclExpChartDrawing() override;
+
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ std::shared_ptr< XclExpObjectManager > mxObjMgr;
+ rtl::Reference< XclExpRecordBase > mxObjRecs;
+};
+
+/** Represents the entire chart substream (all records in BOF/EOF block). */
+class XclExpChart : public XclExpSubStream, protected XclExpRoot
+{
+public:
+ explicit XclExpChart( const XclExpRoot& rRoot,
+ css::uno::Reference< css::frame::XModel > const & xModel,
+ const tools::Rectangle& rChartRect );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xecontent.hxx b/sc/source/filter/inc/xecontent.hxx
new file mode 100644
index 000000000..6eb4c77de
--- /dev/null
+++ b/sc/source/filter/inc/xecontent.hxx
@@ -0,0 +1,416 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <rangelst.hxx>
+#include "xladdress.hxx"
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+#include "xestring.hxx"
+#include "xeextlst.hxx"
+#include "xlformula.hxx"
+
+#include <colorscale.hxx>
+
+/* ============================================================================
+Classes to export the big Excel document contents (related to several cells or
+globals for the sheet or document).
+- Shared string table
+- Merged cells
+- Hyperlinks
+- Label ranges
+- Conditional formatting
+- Data validation
+- Web Queries
+============================================================================ */
+
+// Shared string table ========================================================
+
+class XclExpSstImpl;
+
+/** Provides export of the SST (shared string table) record.
+ @descr Contains all strings in the document and writes the SST. */
+class XclExpSst : public XclExpRecordBase
+{
+public:
+ explicit XclExpSst();
+ virtual ~XclExpSst() override;
+
+ /** Inserts a new string into the table.
+ @return The index of the string in the SST, used in other records. */
+ sal_uInt32 Insert( const XclExpStringRef& xString );
+
+ /** Writes the complete SST and EXTSST records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef std::unique_ptr< XclExpSstImpl > XclExpSstImplPtr;
+ XclExpSstImplPtr mxImpl;
+};
+
+// Merged cells ===============================================================
+
+/** Represents a MERGEDCELLS record containing all merged cell ranges in a sheet. */
+class XclExpMergedcells : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpMergedcells( const XclExpRoot& rRoot );
+
+ /** Appends a new range to the list of merged cell ranges. */
+ void AppendRange( const ScRange& rRange, sal_uInt32 nBaseXFId );
+ /** Returns the XF identifier of the top-left cell in a merged range. */
+ sal_uInt32 GetBaseXFId( const ScAddress& rPos ) const;
+
+ /** Writes the record, if it contains at least one merged cell range. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ ScRangeList maMergedRanges; /// All merged cell ranges of the sheet.
+ ScfUInt32Vec maBaseXFIds; /// The XF identifiers of the top-left cells.
+};
+
+// Hyperlinks =================================================================
+
+class SvxURLField;
+
+/** Provides export of hyperlink data. */
+class XclExpHyperlink : public XclExpRecord
+{
+public:
+ /** Constructs the HLINK record from a URL text field. */
+ explicit XclExpHyperlink( const XclExpRoot& rRoot,
+ const SvxURLField& rUrlField, const ScAddress& rScPos );
+ virtual ~XclExpHyperlink() override;
+
+ /** Returns the cell representation text or 0, if not available. */
+ const OUString* GetRepr() const { return m_Repr.isEmpty() ? nullptr : &m_Repr; }
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ void WriteEmbeddedData( XclExpStream& rStrm );
+
+ /** Builds file name from the passed file URL. Tries to convert to relative file name.
+ @param rnLevel (out-param) The parent directory level.
+ @param rbRel (out-param) true = path is relative.
+ @param bEncoded if true return an IURI encoded name, not a DOS name. */
+ static OUString BuildFileName(
+ sal_uInt16& rnLevel, bool& rbRel,
+ const OUString& rUrl, const XclExpRoot& rRoot, bool bEncoded );
+private:
+
+ /** Writes the body of the HLINK record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::unique_ptr< SvStream > SvStreamPtr;
+
+ ScAddress maScPos; /// Position of the hyperlink.
+ OUString m_Repr; /// Cell representation text.
+ SvStreamPtr mxVarData; /// Buffer stream with variable data.
+ sal_uInt32 mnFlags; /// Option flags.
+ XclExpStringRef mxTextMark; /// Location within m_Repr
+ OUString msTarget; /// Target URL
+};
+
+typedef XclExpRecordList< XclExpHyperlink > XclExpHyperlinkList;
+
+// Label ranges ===============================================================
+
+/** Provides export of the row/column label range list of a sheet. */
+class XclExpLabelranges : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Fills the cell range lists with all ranges of the current sheet. */
+ explicit XclExpLabelranges( const XclExpRoot& rRoot );
+
+ /** Writes the LABELRANGES record if it contains at least one range. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Fills the specified range list with all label headers of the current sheet.
+ @param rRanges The cell range list to fill.
+ @param xLabelRangesRef The core range list with all ranges.
+ @param nScTab The current Calc sheet index. */
+ static void FillRangeList( ScRangeList& rScRanges,
+ const ScRangePairListRef& xLabelRangesRef, SCTAB nScTab );
+
+private:
+ ScRangeList maRowRanges; /// Cell range list for row labels.
+ ScRangeList maColRanges; /// Cell range list for column labels.
+};
+
+// Conditional formatting =====================================================
+
+class XclExpCFImpl;
+
+/** Represents a CF record that contains one condition of a conditional format. */
+class XclExpCF : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormatEntry, sal_Int32 nPriority, ScAddress aOrigin );
+ virtual ~XclExpCF() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the CF record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::unique_ptr< XclExpCFImpl > XclExpCFImplPtr;
+ XclExpCFImplPtr mxImpl;
+};
+
+class XclExpDateFormat : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDateFormat( const XclExpRoot& rRoot, const ScCondDateFormatEntry& rFormatEntry, sal_Int32 nPriority );
+ virtual ~XclExpDateFormat() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ const ScCondDateFormatEntry& mrFormatEntry;
+ sal_Int32 mnPriority;
+};
+
+class XclExpCfvo : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpCfvo( const XclExpRoot& rRoot, const ScColorScaleEntry& rFormatEntry, const ScAddress& rPos, bool bFirst = true);
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ const ScColorScaleEntry& mrEntry;
+ ScAddress maSrcPos;
+ bool mbFirst;
+};
+
+class XclExpColScaleCol : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpColScaleCol( const XclExpRoot& rRoot, const Color& rColor);
+ virtual ~XclExpColScaleCol() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ const Color& mrColor;
+};
+
+/** Represents a CONDFMT record that contains all conditions of a conditional format.
+ @descr Contains the conditions which are stored in CF records. */
+class XclExpCondfmt : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpCondfmt( const XclExpRoot& rRoot, const ScConditionalFormat& rCondFormat, const XclExtLstRef& xExtLst, sal_Int32& rIndex );
+ virtual ~XclExpCondfmt() override;
+
+ /** Returns true, if this conditional format contains at least one cell range and CF record. */
+ bool IsValidForBinary() const;
+ bool IsValidForXml() const;
+
+ /** Writes the CONDFMT record with following CF records, if there is valid data. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the CONDFMT record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpRecordList< XclExpRecord >
+ maCFList; /// List of CF records.
+ XclRangeList maXclRanges; /// Cell ranges for this conditional format.
+ OUString msSeqRef; /// OOXML Sequence of References
+};
+
+class XclExpColorScale: public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpColorScale( const XclExpRoot& rRoot, const ScColorScaleFormat& rFormat, sal_Int32 nPriority );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ typedef XclExpRecordList< XclExpCfvo > XclExpCfvoList;
+ typedef XclExpRecordList< XclExpColScaleCol > XclExpColScaleColList;
+
+ XclExpCfvoList maCfvoList;
+ XclExpColScaleColList maColList;
+ sal_Int32 mnPriority;
+};
+
+class XclExpDataBar : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, sal_Int32 nPriority, const OString& rGUID);
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ std::unique_ptr<XclExpCfvo> mpCfvoLowerLimit;
+ std::unique_ptr<XclExpCfvo> mpCfvoUpperLimit;
+ std::unique_ptr<XclExpColScaleCol> mpCol;
+
+ const ScDataBarFormat& mrFormat;
+ sal_Int32 mnPriority;
+ OString maGUID;
+};
+
+class XclExpIconSet : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpIconSet( const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, sal_Int32 nPriority );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ typedef XclExpRecordList< XclExpCfvo > XclExpCfvoList;
+
+ XclExpCfvoList maCfvoList;
+ const ScIconSetFormat& mrFormat;
+ sal_Int32 mnPriority;
+};
+
+/** Contains all conditional formats of a specific sheet. */
+class XclExpCondFormatBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Constructs CONDFMT and CF records containing the conditional formats of the current sheet. */
+ explicit XclExpCondFormatBuffer( const XclExpRoot& rRoot, const XclExtLstRef& xExtLst );
+
+ /** Writes all contained CONDFMT records with their CF records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpCondfmt > XclExpCondfmtList;
+ XclExpCondfmtList maCondfmtList; /// List of CONDFMT records.
+};
+
+// Data Validation ============================================================
+
+/** Provides export of the data of a DV record.
+ @descr This record contains the settings for a data validation. In detail
+ this is a pointer to the core validation data and a cell range list with all
+ affected cells. The handle index is used to optimize list search algorithm. */
+class XclExpDV : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDV( const XclExpRoot& rRoot, sal_uLong nScHandle );
+ virtual ~XclExpDV() override;
+
+ /** Returns the core handle of the validation data. */
+ sal_uLong GetScHandle() const { return mnScHandle; }
+
+ /** Inserts a new cell range into the cell range list. */
+ void InsertCellRange( const ScRange& rPos );
+ /** Converts the Calc range list to the Excel range list.
+ @return false = Resulting range list empty - do not write this record. */
+ bool Finalize();
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the DV record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ ScRangeList maScRanges; /// Calc range list with all affected cells.
+ XclRangeList maXclRanges; /// Excel range list with all affected cells.
+ XclExpString maPromptTitle; /// The prompt title.
+ XclExpString maPromptText; /// The prompt text.
+ XclExpString maErrorTitle; /// The error title.
+ XclExpString maErrorText; /// The error text.
+ XclExpStringRef mxString1; /// String for first condition formula.
+ XclTokenArrayRef mxTokArr1; /// Formula for first condition.
+ OUString msFormula1; /// OOXML Formula for first condition.
+ OUString msList; /// x12ac:list for first condition.
+ XclTokenArrayRef mxTokArr2; /// Formula for second condition.
+ OUString msFormula2; /// OOXML Formula for second condition.
+ sal_uInt32 mnFlags; /// Miscellaneous flags.
+ sal_uLong mnScHandle; /// The core handle for quick list search.
+};
+
+/** This class contains the DV record list following the DVAL record. */
+class XclExpDval : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDval( const XclExpRoot& rRoot );
+ virtual ~XclExpDval() override;
+
+ /** Inserts the cell range into the range list of the DV record with the specified handle. */
+ void InsertCellRange( const ScRange& rRange, sal_uLong nScHandle );
+
+ /** Writes the DVAL record and the DV record list. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Searches for or creates a XclExpDV record object with the specified handle. */
+ XclExpDV& SearchOrCreateDv( sal_uLong nScHandle );
+
+ /** Writes the body of the DVAL record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpDV > XclExpDVList;
+ typedef XclExpDVList::RecordRefType XclExpDVRef;
+
+ XclExpDVList maDVList; /// List of DV records
+ XclExpDVRef mxLastFoundDV; /// For search optimization.
+};
+
+// Web Queries ================================================================
+
+/** Contains all records for a web query (linked tables in an HTML document).
+ @descr mode 1 (entire document): mpQryTables==0, mbEntireDoc==true;
+ mode 2 (all tables): mpQryTables==0, mbEntireDoc==false;
+ mode 3 (custom range list): mpQryTables!=0, mbEntireDoc==false. */
+class XclExpWebQuery : public XclExpRecordBase
+{
+public:
+ /** Constructs a web query record container with settings from Calc. */
+ explicit XclExpWebQuery(
+ const OUString& rRangeName,
+ const OUString& rUrl,
+ std::u16string_view rSource,
+ sal_Int32 nRefrSecs );
+ virtual ~XclExpWebQuery() override;
+
+ /** Writes all needed records for this web query. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ XclExpString maDestRange; /// Destination range.
+ XclExpString maUrl; /// Source document URL.
+ XclExpStringRef mxQryTables; /// List of source range names.
+ sal_Int16 mnRefresh; /// Refresh time in minutes.
+ bool mbEntireDoc; /// true = entire document.
+};
+
+/** Contains all web query records for this document. */
+class XclExpWebQueryBuffer : public XclExpRecordList< XclExpWebQuery >
+{
+public:
+ explicit XclExpWebQueryBuffer( const XclExpRoot& rRoot );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xedbdata.hxx b/sc/source/filter/inc/xedbdata.hxx
new file mode 100644
index 000000000..a7fddfe04
--- /dev/null
+++ b/sc/source/filter/inc/xedbdata.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xeroot.hxx"
+#include "xerecord.hxx"
+
+class ScDBData;
+
+class XclExpTables : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpTables( const XclExpRoot& rRoot );
+ virtual ~XclExpTables() override;
+
+ void AppendTable( const ScDBData* pData, sal_Int32 nTableId );
+
+protected:
+ struct Entry
+ {
+ const ScDBData* mpData;
+ sal_Int32 mnTableId; /// used as [n] in table[n].xml part name.
+
+ Entry( const ScDBData* pData, sal_Int32 nTableId );
+ };
+
+ typedef ::std::vector<Entry> TablesType;
+ TablesType maTables;
+
+ static void SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry );
+};
+
+/** Stores all data for database ranges (tables in Excel speak).
+ Only OOXML export, BIFF not implemented.*/
+class XclExpTablesManager : protected XclExpRoot
+{
+public:
+ explicit XclExpTablesManager( const XclExpRoot& rRoot );
+ virtual ~XclExpTablesManager() override;
+
+ void Initialize();
+ rtl::Reference< XclExpTables > GetTablesBySheet( SCTAB nTab );
+
+private:
+ typedef ::std::map< SCTAB, rtl::Reference< XclExpTables > > TablesMapType;
+ TablesMapType maTablesMap;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeescher.hxx b/sc/source/filter/inc/xeescher.hxx
new file mode 100644
index 000000000..00d405767
--- /dev/null
+++ b/sc/source/filter/inc/xeescher.hxx
@@ -0,0 +1,464 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/graph.hxx>
+#include <filter/msfilter/escherex.hxx>
+#include "xcl97rec.hxx"
+#include "xlescher.hxx"
+#include "xlformula.hxx"
+#include <svx/sdtaitm.hxx>
+#include <rtl/ustring.hxx>
+#include <memory>
+
+class ScPostIt;
+
+namespace utl { class TempFile; }
+namespace com::sun::star::chart { class XChartDocument; }
+namespace com::sun::star::script { struct ScriptEventDescriptor; }
+
+
+// DFF client anchor ==========================================================
+
+/** Base class for DFF client anchor atoms used in spreadsheets. */
+class XclExpDffAnchorBase : public EscherExClientAnchor_Base, protected XclExpRoot
+{
+public:
+ /** Constructs a dummy client anchor. */
+ explicit XclExpDffAnchorBase( const XclExpRoot& rRoot, sal_uInt16 nFlags = 0 );
+
+ /** Sets the flags according to the passed SdrObject. */
+ void SetFlags( const SdrObject& rSdrObj );
+ /** Sets the anchor position and flags according to the passed SdrObject. */
+ void SetSdrObject( const SdrObject& rSdrObj );
+
+ /** Writes the DFF client anchor structure with the current anchor position. */
+ void WriteDffData( EscherEx& rEscherEx ) const;
+
+ /** Called from SVX DFF converter.
+ @param rRect The object anchor rectangle to be exported (in twips). */
+ virtual void WriteData( EscherEx& rEscherEx, const tools::Rectangle& rRect ) override;
+
+private:
+ virtual void ImplSetFlags( const SdrObject& rSdrObj );
+ virtual void ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit );
+
+protected: // for access in derived classes
+ XclObjAnchor maAnchor; /// The client anchor data.
+ sal_uInt16 mnFlags; /// Flags for DFF stream export.
+};
+
+/** Represents the position (anchor) of an object in a Calc sheet. */
+class XclExpDffSheetAnchor : public XclExpDffAnchorBase
+{
+public:
+ explicit XclExpDffSheetAnchor( const XclExpRoot& rRoot );
+
+private:
+ virtual void ImplSetFlags( const SdrObject& rSdrObj ) override;
+ virtual void ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit ) override;
+
+private:
+ SCTAB mnScTab; /// Calc sheet index.
+};
+
+/** Represents the position (anchor) of a shape in an embedded draw page. */
+class XclExpDffEmbeddedAnchor : public XclExpDffAnchorBase
+{
+public:
+ explicit XclExpDffEmbeddedAnchor( const XclExpRoot& rRoot,
+ const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY );
+
+private:
+ virtual void ImplSetFlags( const SdrObject& rSdrObj ) override;
+ virtual void ImplCalcAnchorRect( const tools::Rectangle& rRect, MapUnit eMapUnit ) override;
+
+private:
+ Size maPageSize;
+ sal_Int32 mnScaleX;
+ sal_Int32 mnScaleY;
+};
+
+/** Represents the position (anchor) of a note object. */
+class XclExpDffNoteAnchor : public XclExpDffAnchorBase
+{
+public:
+ explicit XclExpDffNoteAnchor( const XclExpRoot& rRoot, const tools::Rectangle& rRect );
+};
+
+/** Represents the position (anchor) of a cell dropdown object. */
+class XclExpDffDropDownAnchor : public XclExpDffAnchorBase
+{
+public:
+ explicit XclExpDffDropDownAnchor( const XclExpRoot& rRoot, const ScAddress& rScPos );
+};
+
+// MSODRAWING* records ========================================================
+
+/** Base class for records holding DFF stream fragments. */
+class XclExpMsoDrawingBase : public XclExpRecord
+{
+public:
+ explicit XclExpMsoDrawingBase( XclEscherEx& rEscherEx, sal_uInt16 nRecId );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+protected:
+ XclEscherEx& mrEscherEx; /// Reference to the DFF converter containing the DFF stream.
+ sal_uInt32 mnFragmentKey; /// The key of the DFF stream fragment to be written by this record.
+};
+
+/** The MSODRAWINGGROUP record contains the DGGCONTAINER with global DFF data
+ such as the picture container.
+ */
+class XclExpMsoDrawingGroup : public XclExpMsoDrawingBase
+{
+public:
+ explicit XclExpMsoDrawingGroup( XclEscherEx& rEscherEx );
+};
+
+/** One or more MSODRAWING records contain the DFF stream data for a drawing
+ shape.
+ */
+class XclExpMsoDrawing : public XclExpMsoDrawingBase
+{
+public:
+ explicit XclExpMsoDrawing( XclEscherEx& rEscherEx );
+};
+
+/** Provides export of bitmap data to an IMGDATA record. */
+class XclExpImgData : public XclExpRecordBase
+{
+public:
+ explicit XclExpImgData( const Graphic& rGraphic, sal_uInt16 nRecId );
+
+ /** Writes the BITMAP record. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ Graphic maGraphic; /// The VCL graphic.
+ sal_uInt16 mnRecId; /// Record identifier for the IMGDATA record.
+};
+
+/** Helper class for form controls to manage spreadsheet links . */
+class XclExpControlHelper : protected XclExpRoot
+{
+public:
+ explicit XclExpControlHelper( const XclExpRoot& rRoot );
+ virtual ~XclExpControlHelper() override;
+
+protected:
+ /** Tries to get spreadsheet cell link and source range link from the passed shape. */
+ void ConvertSheetLinks(
+ css::uno::Reference< css::drawing::XShape > const & xShape );
+
+ /** Returns the Excel token array of the cell link, or 0, if no link present. */
+ const XclTokenArray* GetCellLinkTokArr() const { return mxCellLink.get(); }
+ /** Returns the Excel token array of the source range, or 0, if no link present. */
+ const XclTokenArray* GetSourceRangeTokArr() const { return mxSrcRange.get(); }
+ /** Returns the number of entries in the source range, or 0, if no source set. */
+ sal_uInt16 GetSourceEntryCount() const { return mnEntryCount; }
+
+ /** Writes a formula with special style only valid in OBJ records. */
+ static void WriteFormula( XclExpStream& rStrm, const XclTokenArray& rTokArr );
+ /** Writes a formula subrecord with special style only valid in OBJ records. */
+ static void WriteFormulaSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId, const XclTokenArray& rTokArr );
+
+private:
+ XclTokenArrayRef mxCellLink; /// Formula for linked cell.
+ XclTokenArrayRef mxSrcRange; /// Formula for source data range.
+ sal_uInt16 mnEntryCount; /// Number of entries in source range.
+protected:
+ ScAddress mxCellLinkAddress;
+};
+
+class XclMacroHelper : public XclExpControlHelper
+{
+ XclTokenArrayRef mxMacroLink; /// Token array containing a link to an attached macro.
+ OUString maMacroName;
+
+public:
+ explicit XclMacroHelper( const XclExpRoot& rRoot );
+ virtual ~XclMacroHelper() override;
+ /** Writes an ftMacro subrecord containing a macro link, or nothing, if no macro present. */
+ void WriteMacroSubRec( XclExpStream& rStrm );
+ /** Sets the name of a macro for object of passed type
+ @return true = The passed event descriptor was valid, macro name has been found. */
+ bool SetMacroLink( const css::script::ScriptEventDescriptor& rEvent, const XclTbxEventType& nEventType );
+
+ /** Sets the name of a macro
+ @return true = The passed macro name has been found. */
+ bool SetMacroLink( const OUString& rMacro );
+ const OUString& GetMacroName() const;
+};
+
+class XclExpShapeObj : public XclObjAny, public XclMacroHelper
+{
+public:
+ explicit XclExpShapeObj( XclExpObjectManager& rRoot, css::uno::Reference< css::drawing::XShape > const & xShape, ScDocument* pDoc );
+ virtual ~XclExpShapeObj() override;
+private:
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+};
+
+//delete for exporting OCX
+//#if EXC_EXP_OCX_CTRL
+
+/** Represents an OBJ record for an OCX form control. */
+class XclExpOcxControlObj : public XclObj, public XclExpControlHelper
+{
+public:
+ explicit XclExpOcxControlObj(
+ XclExpObjectManager& rObjMgr,
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor,
+ const OUString& rClassName,
+ sal_uInt32 nStrmStart, sal_uInt32 nStrmSize );
+
+private:
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+private:
+ OUString maClassName; /// Class name of the control.
+ sal_uInt32 mnStrmStart; /// Start position in 'Ctls' stream.
+ sal_uInt32 mnStrmSize; /// Size in 'Ctls' stream.
+};
+
+//#else
+
+/** Represents an OBJ record for a TBX form control. */
+class XclExpTbxControlObj : public XclObj, public XclMacroHelper
+{
+public:
+ explicit XclExpTbxControlObj(
+ XclExpObjectManager& rObjMgr,
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor );
+
+ /** Sets the name of a macro attached to this control.
+ @return true = The passed event descriptor was valid, macro name has been found. */
+ bool SetMacroLink( const css::script::ScriptEventDescriptor& rEvent );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ void SaveVml(XclExpXmlStream& rStrm);
+
+ OUString SaveControlPropertiesXml(XclExpXmlStream& rStrm) const;
+ void SaveSheetXml(XclExpXmlStream& rStrm, const OUString& aIdFormControlPr) const;
+
+ void setShapeId(sal_Int32 aShapeId);
+
+private:
+ virtual void WriteSubRecs( XclExpStream& rStrm ) override;
+
+ /** Writes a subrecord containing a cell link, or nothing, if no link present. */
+ void WriteCellLinkSubRec( XclExpStream& rStrm, sal_uInt16 nSubRecId );
+ /** Writes the ftSbs sub structure containing scrollbar data. */
+ void WriteSbs( XclExpStream& rStrm );
+
+private:
+ const css::uno::Reference< css::drawing::XShape > mxShape;
+ ScfInt16Vec maMultiSel; /// Indexes of all selected entries in a multi selection.
+ XclTbxEventType meEventType; /// Type of supported macro event.
+ sal_Int32 mnHeight; /// Height of the control.
+ sal_uInt16 mnState; /// Checked/unchecked state.
+ sal_Int16 mnLineCount; /// Combobox dropdown line count.
+ sal_Int16 mnSelEntry; /// Selected entry in combobox (1-based).
+ sal_uInt16 mnScrollValue; /// Scrollbar: Current value.
+ sal_uInt16 mnScrollMin; /// Scrollbar: Minimum value.
+ sal_uInt16 mnScrollMax; /// Scrollbar: Maximum value.
+ sal_uInt16 mnScrollStep; /// Scrollbar: Single step.
+ sal_uInt16 mnScrollPage; /// Scrollbar: Page step.
+ bool mbFlatButton; /// False = 3D button style; True = Flat button style.
+ bool mbFlatBorder; /// False = 3D border style; True = Flat border style.
+ bool mbMultiSel; /// true = Multi selection in listbox.
+ bool mbScrollHor; /// Scrollbar: true = horizontal.
+ bool mbPrint;
+ bool mbVisible;
+ OUString msCtrlName;
+ OUString msLabel;
+ sal_Int32 mnShapeId;
+ tools::Rectangle maAreaFrom;
+ tools::Rectangle maAreaTo;
+ XclExpObjectManager& mrRoot;
+};
+
+//#endif
+
+class XclExpChart;
+
+/** A chart object. This is the drawing object wrapper for the chart data. */
+class XclExpChartObj : public XclObj, protected XclExpRoot
+{
+public:
+ explicit XclExpChartObj(
+ XclExpObjectManager& rObjMgr,
+ css::uno::Reference< css::drawing::XShape > const & xShape,
+ const tools::Rectangle* pChildAnchor,
+ ScDocument* pDoc );
+ virtual ~XclExpChartObj() override;
+
+ /** Writes the OBJ record and the entire chart substream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ css::uno::Reference<css::chart::XChartDocument> GetChartDoc() const;
+
+private:
+ typedef std::shared_ptr< XclExpChart > XclExpChartRef;
+ XclExpChartRef mxChart; /// The chart itself (BOF/EOF substream data).
+ css::uno::Reference< css::drawing::XShape > mxShape;
+ ScDocument* mpDoc;
+};
+
+/** Represents a NOTE record containing the relevant data of a cell note.
+
+ NOTE records differ significantly in various BIFF versions. This class
+ encapsulates all needed actions for each supported BIFF version.
+ BIFF5/BIFF7: Stores the note text and generates a single or multiple NOTE
+ records on saving.
+ BIFF8: Creates the Escher object containing the drawing information and the
+ note text.
+ */
+class XclExpNote : public XclExpRecord
+{
+public:
+ /** Constructs a NOTE record from the passed note object and/or the text.
+ @descr The additional text will be separated from the note text with
+ an empty line.
+ @param rScPos The Calc cell address of the note.
+ @param pScNote The Calc note object. May be 0 to create a note from rAddText only.
+ @param rAddText Additional text appended to the note text. */
+ explicit XclExpNote(
+ const XclExpRoot& rRoot,
+ const ScAddress& rScPos,
+ const ScPostIt* pScNote,
+ std::u16string_view rAddText );
+
+ /** Writes the NOTE record, if the respective Escher object is present. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ void WriteXml( sal_Int32 nAuthorId, XclExpXmlStream& rStrm );
+
+ const XclExpString& GetAuthor() const { return maAuthor; }
+private:
+ /** Writes the body of the NOTE record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpRoot& mrRoot;
+ XclExpString maAuthor; /// Name of the author.
+ OString maNoteText; /// Main text of the note (<=BIFF7).
+ XclExpStringRef mpNoteContents; /// Text and formatting data (OOXML)
+ ScAddress maScPos; /// Calc cell address of the note.
+ sal_uInt16 mnObjId; /// Escher object ID (BIFF8).
+ bool mbVisible; /// true = permanently visible.
+ SdrTextHorzAdjust meTHA; /// text horizontal adjust
+ SdrTextVertAdjust meTVA; /// text vertical adjust
+ bool mbAutoScale; /// Auto scale text
+ bool mbLocked; /// Position & Size locked
+ bool mbAutoFill; /// Auto Fill Style
+ bool mbColHidden; /// Column containing the comment is hidden
+ bool mbRowHidden; /// Row containing the comment is hidden
+ tools::Rectangle maCommentFrom; /// From and From Offset
+ tools::Rectangle maCommentTo; /// To and To Offsets
+};
+
+class XclExpComments : public XclExpRecord
+{
+public:
+ XclExpComments( SCTAB nTab, XclExpRecordList< XclExpNote >& rNotes );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ SCTAB mnTab;
+ XclExpRecordList< XclExpNote >& mrNotes;
+};
+
+// object manager =============================================================
+
+class XclExpObjectManager : public XclExpRoot
+{
+public:
+ explicit XclExpObjectManager( const XclExpRoot& rRoot );
+ virtual ~XclExpObjectManager() override;
+
+ /** Creates a new DFF client anchor object. Caller takes ownership! May be
+ overwritten in derived classes. */
+ virtual XclExpDffAnchorBase* CreateDffAnchor() const;
+
+ /** Creates and returns the MSODRAWINGGROUP record containing global DFF
+ data in the DGGCONTAINER. */
+ rtl::Reference< XclExpRecordBase > CreateDrawingGroup();
+
+ /** Initializes the object manager for a new sheet. */
+ void StartSheet();
+
+ /** Processes a drawing page and returns the record block containing all
+ related records (MSODRAWING, OBJ, TXO, charts, etc.). */
+ rtl::Reference< XclExpRecordBase > ProcessDrawing( const SdrPage* pSdrPage );
+ /** Processes a collection of UNO shapes and returns the record block
+ containing all related records (MSODRAWING, OBJ, TXO, charts, etc.). */
+ rtl::Reference< XclExpRecordBase > ProcessDrawing( const css::uno::Reference< css::drawing::XShapes >& rxShapes );
+
+ /** Finalizes the object manager after conversion of all sheets. */
+ void EndDocument();
+
+ XclEscherEx& GetEscherEx() { return *mxEscherEx; }
+ XclExpMsoDrawing* GetMsodrawingPerSheet();
+ bool HasObj() const;
+ sal_uInt16 AddObj( std::unique_ptr<XclObj> pObjRec );
+ std::unique_ptr<XclObj> RemoveLastObj();
+
+protected:
+ explicit XclExpObjectManager( const XclExpObjectManager& rParent );
+
+private:
+ void InitStream( bool bTempFile );
+
+private:
+ std::shared_ptr< ::utl::TempFile > mxTempFile;
+ std::unique_ptr< SvStream > mxDffStrm;
+ std::shared_ptr< XclEscherEx > mxEscherEx;
+ rtl::Reference< XclExpObjList > mxObjList;
+};
+
+class XclExpEmbeddedObjectManager : public XclExpObjectManager
+{
+public:
+ explicit XclExpEmbeddedObjectManager(
+ const XclExpObjectManager& rParent,
+ const Size& rPageSize,
+ sal_Int32 nScaleX, sal_Int32 nScaleY );
+
+ /** Creates a new DFF client anchor object for embedded objects according
+ to the scaling data passed to the constructor. Caller takes ownership! */
+ virtual XclExpDffAnchorBase* CreateDffAnchor() const override;
+
+private:
+ Size maPageSize;
+ sal_Int32 mnScaleX;
+ sal_Int32 mnScaleY;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeextlst.hxx b/sc/source/filter/inc/xeextlst.hxx
new file mode 100644
index 000000000..1770f9af1
--- /dev/null
+++ b/sc/source/filter/inc/xeextlst.hxx
@@ -0,0 +1,208 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+
+#include <colorscale.hxx>
+
+#include <memory>
+
+enum XclExpExtType
+{
+ XclExpExtDataBarType,
+ XclExpExtDataFooType,
+ XclExpExtSparklineType,
+};
+
+struct XclExpExtCondFormatData
+{
+ // -1 means don't write priority
+ sal_Int32 nPriority;
+ OString aGUID;
+ const ScFormatEntry* pEntry;
+};
+
+/**
+ * Base class for ext entries. Extend this class to provide the needed functionality
+ */
+class XclExpExt : public XclExpRecordBase, public XclExpRoot
+{
+public:
+ explicit XclExpExt( const XclExpRoot& rRoot );
+ virtual XclExpExtType GetType() = 0;
+
+protected:
+ OString maURI;
+};
+
+class XclExpExtCfvo : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpExtCfvo( const XclExpRoot& rRoot, const ScColorScaleEntry& rEntry, const ScAddress& rPos, bool bFirst );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ ScColorScaleEntryType meType;
+ OString maValue;
+ bool mbFirst;
+};
+
+class XclExpExtNegativeColor
+{
+public:
+ XclExpExtNegativeColor( const Color& rColor );
+ void SaveXml( XclExpXmlStream& rStrm);
+
+private:
+ Color maColor;
+};
+
+class XclExpExtAxisColor
+{
+public:
+ XclExpExtAxisColor( const Color& maColor );
+ void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ Color maAxisColor;
+};
+
+class XclExpExtIcon : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtIcon( const XclExpRoot& rRoot, const std::pair<ScIconSetType, sal_Int32>& rCustomEntry);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ const char* pIconSetName;
+ sal_Int32 nIndex;
+};
+
+class XclExpExtCF : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtCF( const XclExpRoot& rRoot, const ScCondFormatEntry& rFormat );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ OUString aFormula;
+ ScCondFormatEntry mrFormat;
+};
+
+class XclExpExtDataBar : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtDataBar( const XclExpRoot& rRoot, const ScDataBarFormat& rFormat, const ScAddress& rPos );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ databar::ScAxisPosition meAxisPosition;
+ bool mbGradient;
+ double mnMinLength;
+ double mnMaxLength;
+
+ std::unique_ptr<XclExpExtCfvo> mpLowerLimit;
+ std::unique_ptr<XclExpExtCfvo> mpUpperLimit;
+ std::unique_ptr<XclExpExtNegativeColor> mpNegativeColor;
+ std::unique_ptr<XclExpExtAxisColor> mpAxisColor;
+
+};
+
+class XclExpExtIconSet : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtIconSet(const XclExpRoot& rRoot, const ScIconSetFormat& rFormat, const ScAddress& rPos);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ XclExpRecordList<XclExpExtCfvo> maCfvos;
+ XclExpRecordList<XclExpExtIcon> maCustom;
+ bool mbCustom;
+ bool mbReverse;
+ bool mbShowValue;
+ const char* mpIconSetName;
+};
+
+
+class XclExpExtCfRule : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpExtCfRule( const XclExpRoot& rRoot, const ScFormatEntry& rFormat, const ScAddress& rPos, const OString& rId, sal_Int32 nPriority );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ XclExpRecordRef mxEntry;
+ OString maId;
+ const char* pType;
+ sal_Int32 mnPriority;
+ const char* mOperator;
+};
+
+typedef rtl::Reference<XclExpExt> XclExpExtRef;
+
+class XclExpExtConditionalFormatting : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpExtConditionalFormatting( const XclExpRoot& rRoot, std::vector<XclExpExtCondFormatData>& rData, const ScRangeList& rRange);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ XclExpRecordList<XclExpExtCfRule> maCfRules;
+ ScRangeList maRange;
+};
+
+typedef rtl::Reference<XclExpExtConditionalFormatting> XclExpExtConditionalFormattingRef;
+
+class XclExpExtCondFormat : public XclExpExt
+{
+public:
+ XclExpExtCondFormat( const XclExpRoot& rRoot );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ virtual XclExpExtType GetType() override { return XclExpExtDataBarType; }
+
+ void AddRecord( XclExpExtConditionalFormatting* pFormat );
+
+private:
+ XclExpRecordList< XclExpExtConditionalFormatting > maCF;
+};
+
+class XclExpExtCalcPr : public XclExpExt
+{
+public:
+ XclExpExtCalcPr( const XclExpRoot& rRoot, formula::FormulaGrammar::AddressConvention eConv );
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ virtual XclExpExtType GetType() override { return XclExpExtDataFooType; }
+
+private:
+ OString maSyntax;
+};
+
+class XclExtLst : public XclExpRecordBase, public XclExpRoot
+{
+public:
+ explicit XclExtLst( const XclExpRoot& rRoot);
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ void AddRecord( XclExpExt* pEntry );
+ void AddRecord( const XclExpExtRef& aEntry ) { AddRecord(aEntry.get()); }
+
+ XclExpExt* GetItem( XclExpExtType eType );
+
+private:
+ XclExpRecordList< XclExpExt > maExtEntries;
+};
+
+typedef rtl::Reference< XclExtLst > XclExtLstRef;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeformula.hxx b/sc/source/filter/inc/xeformula.hxx
new file mode 100644
index 000000000..d050b2217
--- /dev/null
+++ b/sc/source/filter/inc/xeformula.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlformula.hxx"
+#include "xeroot.hxx"
+#include <memory>
+
+struct XclAddress;
+
+// External reference log =====================================================
+
+/** Log entry for external references in a formula, used i.e. in change tracking. */
+struct XclExpRefLogEntry
+{
+ const XclExpString* mpUrl; /// URL of the document containing the first sheet.
+ const XclExpString* mpFirstTab; /// Name of the first sheet.
+ const XclExpString* mpLastTab; /// Name of the last sheet.
+ sal_uInt16 mnFirstXclTab; /// Calc index of the first sheet.
+ sal_uInt16 mnLastXclTab; /// Calc index of the last sheet.
+
+ explicit XclExpRefLogEntry();
+};
+
+/** Vector containing a log for all external references in a formula, used i.e. in change tracking. */
+typedef ::std::vector< XclExpRefLogEntry > XclExpRefLog;
+
+// Formula compiler ===========================================================
+
+class ScRangeList;
+class XclExpFmlaCompImpl;
+
+/** The formula compiler to create Excel token arrays from Calc token arrays. */
+class XclExpFormulaCompiler : protected XclExpRoot
+{
+public:
+ explicit XclExpFormulaCompiler( const XclExpRoot& rRoot );
+ virtual ~XclExpFormulaCompiler() override;
+
+ /** Creates and returns the token array of a formula. */
+ XclTokenArrayRef CreateFormula(
+ XclFormulaType eType, const ScTokenArray& rScTokArr,
+ const ScAddress* pScBasePos = nullptr, XclExpRefLog* pRefLog = nullptr );
+
+ /** Creates and returns a token array containing a single cell address. */
+ XclTokenArrayRef CreateFormula( XclFormulaType eType, const ScAddress& rScPos );
+
+ /** Creates and returns a token array containing a single cell range address. */
+ XclTokenArrayRef CreateFormula( XclFormulaType eType, const ScRange& rScRange );
+
+ /** Creates and returns the token array for a cell range list. */
+ XclTokenArrayRef CreateFormula( XclFormulaType eType, const ScRangeList& rScRanges );
+
+ /** Creates a single error token containing the passed error code. */
+ XclTokenArrayRef CreateErrorFormula( sal_uInt8 nErrCode );
+
+ /** Creates a single token for a special cell reference.
+ @descr This is used for array formulas and shared formulas (token tExp),
+ and multiple operation tables (token tTbl). */
+ XclTokenArrayRef CreateSpecialRefFormula( sal_uInt8 nTokenId, const XclAddress& rXclPos );
+
+ /** Creates a single tNameXR token for a reference to an external name.
+ @descr This is used i.e. for linked macros in push buttons. */
+ XclTokenArrayRef CreateNameXFormula( sal_uInt16 nExtSheet, sal_uInt16 nExtName );
+
+ bool IsRef2D( const ScSingleRefData& rRefData ) const;
+ bool IsRef2D( const ScComplexRefData& rRefData ) const;
+
+private:
+ typedef std::shared_ptr< XclExpFmlaCompImpl > XclExpFmlaCompImplRef;
+ XclExpFmlaCompImplRef mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xehelper.hxx b/sc/source/filter/inc/xehelper.hxx
new file mode 100644
index 000000000..fac6b3bb2
--- /dev/null
+++ b/sc/source/filter/inc/xehelper.hxx
@@ -0,0 +1,438 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <string_view>
+
+#include "ftools.hxx"
+#include <rangelst.hxx>
+#include "xladdress.hxx"
+#include "xeroot.hxx"
+#include "xlstring.hxx"
+
+// Export progress bar ========================================================
+
+class ScfProgressBar;
+
+/** The main progress bar for the export filter.
+
+ This class encapsulates creation and initialization of sub progress
+ segments. The Activate***Segment() functions activate a specific segment
+ of the main progress bar. The implementation of these functions contain the
+ calculation of the needed size of the segment. Following calls of the
+ Progress() function increase the currently activated sub segment.
+ */
+class XclExpProgressBar : protected XclExpRoot
+{
+public:
+ explicit XclExpProgressBar( const XclExpRoot& rRoot );
+ virtual ~XclExpProgressBar() override;
+
+ /** Initializes all segments and sub progress bars. */
+ void Initialize();
+
+ /** Increases the number of existing ROW records by 1. */
+ void IncRowRecordCount();
+
+ /** Activates the progress segment to create ROW records. */
+ void ActivateCreateRowsSegment();
+ /** Activates the progress segment to finalize ROW records. */
+ void ActivateFinalRowsSegment();
+
+ /** Increases the currently activated (sub) progress bar by 1 step. */
+ void Progress();
+
+private:
+ typedef std::unique_ptr< ScfProgressBar > ScfProgressBarPtr;
+
+ ScfProgressBarPtr mxProgress; /// Progress bar implementation.
+ ScfProgressBar* mpSubProgress; /// Current sub progress bar.
+
+ ScfProgressBar* mpSubRowCreate; /// Sub progress bar for creating table rows.
+ ScfInt32Vec maSubSegRowCreate; /// Segment ID's for all sheets in sub progress bar.
+
+ ScfProgressBar* mpSubRowFinal; /// Sub progress bar for finalizing ROW records.
+ sal_Int32 mnSegRowFinal; /// Progress segment for finalizing ROW records.
+
+ std::size_t mnRowCount; /// Number of created ROW records.
+};
+
+// Calc->Excel cell address/range conversion ==================================
+
+/** Provides functions to convert Calc cell addresses to Excel cell addresses. */
+class XclExpAddressConverter : public XclAddressConverterBase
+{
+public:
+ explicit XclExpAddressConverter( const XclExpRoot& rRoot );
+
+ // cell address -----------------------------------------------------------
+
+ /** Checks if the passed Calc cell address is valid.
+ @param rScPos The Calc cell address to check.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is not valid.
+ @return true = Cell address in rScPos is valid. */
+ bool CheckAddress( const ScAddress& rScPos, bool bWarn );
+
+ /** Converts the passed Calc cell address to an Excel cell address.
+ @param rXclPos (Out) The converted Excel cell address, if valid.
+ @param rScPos The Calc cell address to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is not valid.
+ @return true = Cell address returned in rXclPos is valid. */
+ bool ConvertAddress( XclAddress& rXclPos,
+ const ScAddress& rScPos, bool bWarn );
+
+ /** Returns a valid cell address by moving it into allowed dimensions.
+ @param rScPos The Calc cell address to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is invalid.
+ @return The converted Excel cell address. */
+ XclAddress CreateValidAddress( const ScAddress& rScPos, bool bWarn );
+
+ // cell range -------------------------------------------------------------
+
+ /** Checks if the passed cell range is valid (checks start and end position).
+ @param rScRange The Calc cell range to check.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell range is not valid.
+ @return true = Cell range in rScRange is valid. */
+ bool CheckRange( const ScRange& rScRange, bool bWarn );
+
+ /** Checks and eventually crops the cell range to valid dimensions.
+ @descr The start position of the range will not be modified.
+ @param rScRange (In/out) The cell range to validate.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell range contains invalid
+ cells. If the range is partly valid, this function sets the warning
+ flag, corrects the range and returns true.
+ @return true = Cell range in rScRange is valid (original or cropped). */
+ bool ValidateRange( ScRange& rScRange, bool bWarn );
+
+ /** Converts the passed Calc cell range to an Excel cell range.
+ @param rXclRange (Out) The converted Excel cell range, if valid.
+ @param rScRange The Calc cell range to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell range contains invalid cells.
+ @return true = Cell range returned in rXclRange is valid (original or cropped). */
+ bool ConvertRange( XclRange& rXclRange, const ScRange& rScRange, bool bWarn );
+
+ // cell range list --------------------------------------------------------
+
+ /** Checks and eventually crops the cell ranges to valid dimensions.
+ @descr The start position of the ranges will not be modified. Cell
+ ranges that fit partly into valid dimensions are cropped
+ accordingly. Cell ranges that do not fit at all, are removed from
+ the cell range list.
+ @param rScRanges (In/out) The cell range list to check.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if at least one of the cell ranges
+ contains invalid cells. */
+ void ValidateRangeList( ScRangeList& rScRanges, bool bWarn );
+
+ /** Converts the passed Calc cell range list to an Excel cell range list.
+ @descr The start position of the ranges will not be modified. Cell
+ ranges that fit partly into valid dimensions are cropped
+ accordingly. Cell ranges that do not fit at all, are not inserted
+ into the Excel cell range list.
+ @param rXclRanges (Out) The converted Excel cell range list.
+ @param rScRanges The Calc cell range list to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if at least one of the cell ranges
+ contains invalid cells. */
+ void ConvertRangeList( XclRangeList& rXclRanges,
+ const ScRangeList& rScRanges, bool bWarn );
+};
+
+// EditEngine->String conversion ==============================================
+
+class SvxURLField;
+class XclExpHyperlink;
+
+/** Helper to create HLINK records during creation of formatted cell strings.
+
+ In Excel it is not possible to have more than one hyperlink in a cell. This
+ helper detects multiple occurrences of hyperlinks and fills a string which
+ is used to create a cell note containing all URLs. Only cells containing
+ one hyperlink are exported as hyperlink cells.
+ */
+class XclExpHyperlinkHelper : protected XclExpRoot
+{
+public:
+ typedef rtl::Reference< XclExpHyperlink > XclExpHyperlinkRef;
+
+ explicit XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos );
+ virtual ~XclExpHyperlinkHelper() override;
+
+ /** Processes the passed URL field (tries to create a HLINK record).
+ @return The representation string of the URL field. */
+ OUString ProcessUrlField( const SvxURLField& rUrlField );
+
+ /** Returns true, if a single HLINK record has been created. */
+ bool HasLinkRecord() const;
+ /** Returns the created single HLINk record, or an empty reference. */
+ XclExpHyperlinkRef GetLinkRecord() const;
+
+ /** Returns true, if multiple URLs have been processed. */
+ bool HasMultipleUrls() const { return mbMultipleUrls; }
+ /** Returns a string containing all processed URLs. */
+ const OUString& GetUrlList() const { return maUrlList; }
+
+private:
+ XclExpHyperlinkRef mxLinkRec; /// Created HLINK record.
+ ScAddress maScPos; /// Cell position to set at the HLINK record.
+ OUString maUrlList; /// List with all processed URLs.
+ bool mbMultipleUrls; /// true = Multiple URL fields processed.
+};
+
+class EditEngine;
+class EditTextObject;
+class SdrTextObj;
+class ScPatternAttr;
+
+/** This class provides methods to create an XclExpString.
+ @descr The string can be created from an edit engine text object or
+ directly from a Calc edit cell. */
+class XclExpStringHelper
+{
+public:
+ /** removes copy constructor */
+ XclExpStringHelper(const XclExpStringHelper &) = delete;
+ /** remove copy-assignment operator */
+ const XclExpStringHelper& operator=(const XclExpStringHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods */
+ XclExpStringHelper() = delete;
+
+ /** Creates a new unformatted string from the passed string.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ @param rString The source string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateString(
+ const XclExpRoot& rRoot,
+ const OUString& rString,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Creates a new unformatted string from the passed character.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ @param cChar The source character. The NUL character is explicitly allowed.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateString(
+ const XclExpRoot& rRoot,
+ sal_Unicode cChar,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Appends an unformatted string to an Excel string object.
+ @descr Selects the correct Append() function depending on the current
+ BIFF version contained in the passed XclExpRoot object.
+ @param rXclString The Excel string object.
+ @param rString The source string. */
+ static void AppendString(
+ XclExpString& rXclString,
+ const XclExpRoot& rRoot,
+ std::u16string_view rString);
+
+ /** Appends a character to an Excel string object.
+ @descr Selects the correct Append() function depending on the current
+ BIFF version contained in the passed XclExpRoot object.
+ @param rXclString The Excel string object.
+ @param rString The source string. */
+ static void AppendChar(
+ XclExpString& rXclString,
+ const XclExpRoot& rRoot,
+ sal_Unicode cChar );
+
+ /** Creates a new formatted string from a Calc string cell.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ May create a formatted string object, if the cell text contains
+ different script types.
+ @param rStringCell The Calc string cell object.
+ @param pCellAttr The set item containing the cell formatting.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateCellString(
+ const XclExpRoot& rRoot,
+ const OUString& rString,
+ const ScPatternAttr* pCellAttr,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Creates a new formatted string from a Calc edit cell.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ @param rEditCell The Calc edit cell object.
+ @param pCellAttr The set item containing the cell formatting.
+ @param rLinkHelper Helper object for hyperlink conversion.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateCellString(
+ const XclExpRoot& rRoot,
+ const EditTextObject& rEditText,
+ const ScPatternAttr* pCellAttr,
+ XclExpHyperlinkHelper& rLinkHelper,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Creates a new formatted string from a drawing text box.
+ @descr Creates a Unicode string or a byte string, depending on the
+ current BIFF version contained in the passed XclExpRoot object.
+ @param rTextObj The text box object.
+ @param nFlags Modifiers for string export.
+ @return The new string object (shared pointer). */
+ static XclExpStringRef CreateString(
+ const XclExpRoot& rRoot,
+ const SdrTextObj& rTextObj,
+ XclStrFlags nFlags = XclStrFlags::NONE );
+
+ /** Creates a new formatted string from an edit text string.
+ @param rEditObj The edittext object.
+ @param nFlags Modifiers for string export.
+ @return The new string object. */
+ static XclExpStringRef CreateString(
+ const XclExpRoot& rRoot,
+ const EditTextObject& rEditObj,
+ XclStrFlags nFlags = XclStrFlags::NONE );
+
+ /** Returns the script type first text portion different to WEAK, or the system
+ default script type, if there is only weak script in the passed string. */
+ static sal_Int16 GetLeadingScriptType( const XclExpRoot& rRoot, const OUString& rString );
+};
+
+// Header/footer conversion ===================================================
+
+/** Converts edit engine text objects to an Excel header/footer string.
+ @descr Header/footer content is divided into three parts: Left, center and
+ right portion. All formatting information will be encoded in the Excel string
+ using special character sequences. A control sequence starts with the ampersand
+ character.
+
+ Supported control sequences:
+ &L start of left portion
+ &C start of center portion
+ &R start of right portion
+ &P current page number
+ &N page count
+ &D current date
+ &T current time
+ &A table name
+ &F file name without path
+ &Z file path without file name
+ &Z&F file path and name
+ &U underlining on/off
+ &E double underlining on/off
+ &S strikeout characters on/off
+ &X superscript on/off
+ &Y subscript on/off
+ &"fontname,fontstyle" use font with name 'fontname' and style 'fontstyle'
+ &fontheight set font height in points ('fontheight' is a decimal value)
+
+ Known but unsupported control sequences:
+ &G picture
+ */
+class XclExpHFConverter : protected XclExpRoot
+{
+public:
+ /** delete copy constructor */
+ XclExpHFConverter(const XclExpHFConverter&) = delete;
+ /** delete copy-assignment operator */
+ const XclExpHFConverter& operator=(const XclExpHFConverter&) = delete;
+
+ explicit XclExpHFConverter( const XclExpRoot& rRoot );
+
+ /** Generates the header/footer string from the passed edit engine text objects. */
+ void GenerateString(
+ const EditTextObject* pLeftObj,
+ const EditTextObject* pCenterObj,
+ const EditTextObject* pRightObj );
+
+ /** Returns the last generated header/footer string. */
+ const OUString& GetHFString() const { return maHFString; }
+ /** Returns the total height of the last generated header/footer in twips. */
+ sal_Int32 GetTotalHeight() const { return mnTotalHeight; }
+
+private:
+ /** Converts the text object contents and stores it in the passed string. */
+ void AppendPortion(
+ const EditTextObject* pTextObj,
+ sal_Unicode cPortionCode );
+
+private:
+ EditEngine& mrEE; /// The header/footer edit engine.
+ OUString maHFString; /// The last generated header/footer string.
+ sal_Int32 mnTotalHeight; /// Total height of the last header/footer (twips).
+};
+
+// URL conversion =============================================================
+
+/** This class contains static methods to encode a file URL.
+ @descr Excel stores URLs in a format that contains special control characters,
+ i.e. for directory separators or volume names. */
+class XclExpUrlHelper
+{
+public:
+ /** delete copy constructor */
+ XclExpUrlHelper(const XclExpUrlHelper&) = delete;
+ /** delete copy-assignment operator */
+ const XclExpUrlHelper& operator=(const XclExpUrlHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclExpUrlHelper() = delete;
+
+ /** Encodes and returns the URL passed in rAbsUrl to an Excel like URL.
+ @param pTableName Optional pointer to a table name to be encoded in this URL. */
+ static OUString EncodeUrl( const XclExpRoot& rRoot, std::u16string_view rAbsUrl, const OUString* pTableName = nullptr );
+ /** Encodes and returns the passed DDE link to an Excel like DDE link. */
+ static OUString EncodeDde( std::u16string_view rApplic, std::u16string_view rTopic );
+};
+
+class ScMatrix;
+
+/** Contains cached values in a 2-dimensional array. */
+class XclExpCachedMatrix
+{
+ void GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const;
+public:
+ /** Constructs and fills a new matrix.
+ @param rMatrix The Calc value matrix. */
+ explicit XclExpCachedMatrix( const ScMatrix& rMatrix );
+ ~XclExpCachedMatrix();
+
+ /** Returns the byte count of all contained data. */
+ std::size_t GetSize() const;
+ /** Writes the complete matrix to stream. */
+ void Save( XclExpStream& rStrm ) const;
+
+private:
+ const ScMatrix& mrMatrix;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xelink.hxx b/sc/source/filter/inc/xelink.hxx
new file mode 100644
index 000000000..1c816d21f
--- /dev/null
+++ b/sc/source/filter/inc/xelink.hxx
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+#include <externalrefmgr.hxx>
+#include <memory>
+#include <o3tl/typed_flags_set.hxx>
+
+struct ScSingleRefData;
+struct ScComplexRefData;
+struct XclExpRefLogEntry;
+
+/* ============================================================================
+Classes for export of different kinds of internal/external references.
+- 3D cell and cell range links
+- External cell and cell range links
+- External defined names
+- Macro calls
+- Add-in functions
+- DDE links
+- OLE object links
+============================================================================ */
+
+// Excel sheet indexes ========================================================
+
+enum class ExcTabBufFlags : sal_uInt8 {
+ NONE = 0x00,
+ Ignore = 0x01, /// Sheet will be ignored completely.
+ Extern = 0x02, /// Sheet is linked externally.
+ SkipMask = 0x03, /// Sheet will be skipped, if any flag is set.
+ Visible = 0x10, /// Sheet is visible.
+ Selected = 0x20, /// Sheet is selected.
+ Mirrored = 0x40 /// Sheet is mirrored (right-to-left).
+};
+namespace o3tl {
+ template<> struct typed_flags<ExcTabBufFlags> : is_typed_flags<ExcTabBufFlags, 0x73> {};
+}
+
+/** Stores the correct Excel sheet index for each Calc sheet.
+ @descr The class knows all sheets which will not exported
+ (i.e. external link sheets, scenario sheets). */
+class XclExpTabInfo : protected XclExpRoot
+{
+public:
+ /** Initializes the complete buffer from the current exported document. */
+ explicit XclExpTabInfo( const XclExpRoot& rRoot );
+
+ /** Returns true, if the specified Calc sheet will be exported. */
+ bool IsExportTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is used to store external cell contents. */
+ bool IsExternalTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is visible and will be exported. */
+ bool IsVisibleTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is selected and will be exported. */
+ bool IsSelectedTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is the displayed (active) sheet. */
+ bool IsDisplayedTab( SCTAB nScTab ) const;
+ /** Returns true, if the specified Calc sheet is displayed in right-to-left mode. */
+ bool IsMirroredTab( SCTAB nScTab ) const;
+ /** Returns the Calc name of the specified sheet. */
+ OUString GetScTabName( SCTAB nScTab ) const;
+
+ /** Returns the Excel sheet index for a given Calc sheet. */
+ sal_uInt16 GetXclTab( SCTAB nScTab ) const;
+
+ /** Returns the Calc sheet index of the nSortedTab-th entry in the sorted sheet names list. */
+ SCTAB GetRealScTab( SCTAB nSortedScTab ) const;
+
+ /** Returns the number of Calc sheets. */
+ SCTAB GetScTabCount() const { return mnScCnt; }
+
+ /** Returns the number of Excel sheets to be exported. */
+ sal_uInt16 GetXclTabCount() const { return mnXclCnt; }
+ /** Returns the number of external linked sheets. */
+ sal_uInt16 GetXclExtTabCount() const { return mnXclExtCnt; }
+ /** Returns the number of exported selected sheets. */
+ sal_uInt16 GetXclSelectedCount() const { return mnXclSelCnt; }
+
+ /** Returns the Excel index of the active, displayed sheet. */
+ sal_uInt16 GetDisplayedXclTab() const { return mnDisplXclTab; }
+ /** Returns the Excel index of the first visible sheet. */
+ sal_uInt16 GetFirstVisXclTab() const { return mnFirstVisXclTab; }
+
+private:
+ /** Returns true, if any of the passed flags is set for the specified Calc sheet. */
+ bool GetFlag( SCTAB nScTab, ExcTabBufFlags nFlags ) const;
+ /** Sets or clears (depending on bSet) all passed flags for the specified Calc sheet. */
+ void SetFlag( SCTAB nScTab, ExcTabBufFlags nFlags, bool bSet = true );
+
+ /** Searches for sheets not to be exported. */
+ void CalcXclIndexes();
+ /** Sorts the names of all tables and stores the indexes of the sorted indexes. */
+ void CalcSortedIndexes();
+
+private:
+ /** Data structure with information about one Calc sheet. */
+ struct XclExpTabInfoEntry
+ {
+ OUString maScName;
+ sal_uInt16 mnXclTab;
+ ExcTabBufFlags mnFlags;
+ explicit XclExpTabInfoEntry() : mnXclTab( 0 ), mnFlags( ExcTabBufFlags::NONE ) {}
+ };
+
+ typedef ::std::vector< SCTAB > ScTabVec;
+
+ std::vector< XclExpTabInfoEntry >
+ maTabInfoVec; /// Array of Calc sheet index information.
+
+ SCTAB mnScCnt; /// Count of Calc sheets.
+ sal_uInt16 mnXclCnt; /// Count of Excel sheets to be exported.
+ sal_uInt16 mnXclExtCnt; /// Count of external link sheets.
+ sal_uInt16 mnXclSelCnt; /// Count of selected and exported sheets.
+ sal_uInt16 mnDisplXclTab; /// Displayed (active) sheet.
+ sal_uInt16 mnFirstVisXclTab; /// First visible sheet.
+
+ ScTabVec maFromSortedVec; /// Sorted Calc sheet index -> real Calc sheet index.
+ ScTabVec maToSortedVec; /// Real Calc sheet index -> sorted Calc sheet index.
+};
+
+// Export link manager ========================================================
+
+class XclExpLinkManagerImpl;
+
+/** Stores all data for internal/external references (the link table). */
+class XclExpLinkManager : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpLinkManager( const XclExpRoot& rRoot );
+ virtual ~XclExpLinkManager() override;
+
+ /** Searches for an EXTERNSHEET index for the given Calc sheet.
+ @descr See above for the meaning of EXTERNSHEET indexes.
+ @param rnExtSheet (out-param) Returns the EXTERNSHEET index.
+ @param rnXclTab (out-param) Returns the Excel sheet index.
+ @param nScTab The Calc sheet index to process.
+ param pRefLogEntry If not 0, data about the external link is stored here. */
+ void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnXclTab, SCTAB nScTab,
+ XclExpRefLogEntry* pRefLogEntry = nullptr );
+ /** Searches for an EXTERNSHEET index for the given Calc sheet range.
+ @descr See above for the meaning of EXTERNSHEET indexes.
+ @param rnExtSheet (out-param) Returns the EXTERNSHEET index.
+ @param rnFirstXclTab (out-param) Returns the Excel sheet index of the first sheet.
+ @param rnXclTab (out-param) Returns the Excel sheet index of the last sheet.
+ @param nFirstScTab The first Calc sheet index to process.
+ @param nLastScTab The last Calc sheet index to process.
+ param pRefLogEntry If not 0, data about the external link is stored here. */
+ void FindExtSheet( sal_uInt16& rnExtSheet,
+ sal_uInt16& rnFirstXclTab, sal_uInt16& rnLastXclTab,
+ SCTAB nFirstScTab, SCTAB nLastScTab,
+ XclExpRefLogEntry* pRefLogEntry );
+ /** Searches for a special EXTERNSHEET index for the own document. */
+ sal_uInt16 FindExtSheet( sal_Unicode cCode );
+
+ void FindExtSheet( sal_uInt16 nFileId, const OUString& rTabName, sal_uInt16 nXclTabSpan,
+ sal_uInt16& rnExtSheet, sal_uInt16& rnFirstSBTab, sal_uInt16& rnLastSBTab,
+ XclExpRefLogEntry* pRefLogEntry );
+
+ /** Stores the cell with the given address in a CRN record list. */
+ void StoreCell( const ScSingleRefData& rRef, const ScAddress& rPos );
+ /** Stores all cells in the given range in a CRN record list. */
+ void StoreCellRange( const ScComplexRefData& rRef, const ScAddress& rPos );
+
+ void StoreCell( sal_uInt16 nFileId, const OUString& rTabName, const ScAddress& rPos );
+
+ void StoreCellRange( sal_uInt16 nFileId, const OUString& rTabName, const ScRange& rRange );
+
+ /** Finds or inserts an EXTERNNAME record for an add-in function name.
+ @param rnExtSheet (out-param) Returns the index of the EXTSHEET structure for the add-in function name.
+ @param rnExtName (out-param) Returns the 1-based EXTERNNAME record index.
+ @return true = add-in function inserted; false = error (i.e. not supported in current BIFF). */
+ bool InsertAddIn(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** InsertEuroTool */
+ bool InsertEuroTool(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rName );
+ /** Finds or inserts an EXTERNNAME record for DDE links.
+ @param rnExtSheet (out-param) Returns the index of the EXTSHEET structure for the DDE link.
+ @param rnExtName (out-param) Returns the 1-based EXTERNNAME record index.
+ @return true = DDE link inserted; false = error (i.e. not supported in current BIFF). */
+ bool InsertDde(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName,
+ const OUString& rApplic, const OUString& rTopic, const OUString& rItem );
+
+ bool InsertExtName(
+ sal_uInt16& rnExtSheet, sal_uInt16& rnExtName, const OUString& rUrl,
+ const OUString& rName, const ScExternalRefCache::TokenArrayRef& rArray );
+
+ /** Writes the entire Link table. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+ /** Writes the entire Link table to OOXML. */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpLinkManagerImpl > XclExpLinkMgrImplPtr;
+ XclExpLinkMgrImplPtr mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xename.hxx b/sc/source/filter/inc/xename.hxx
new file mode 100644
index 000000000..a81a12804
--- /dev/null
+++ b/sc/source/filter/inc/xename.hxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xlformula.hxx"
+#include "xeroot.hxx"
+#include <memory>
+
+class ScRangeList;
+class XclExpNameManagerImpl;
+
+/** Manager that stores all internal defined names (NAME records) of the document. */
+class XclExpNameManager : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpNameManager( const XclExpRoot& rRoot );
+ virtual ~XclExpNameManager() override;
+
+ /** Creates NAME records for built-in and user defined names. */
+ void Initialize();
+
+ /** Inserts the Calc name with the passed index and returns the Excel NAME index. */
+ sal_uInt16 InsertName( SCTAB nTab, sal_uInt16 nScNameIdx, SCTAB nCurrTab );
+
+ /** Inserts a new built-in defined name, referring to the passed sheet range. */
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const ScRange& rRange );
+ /** Inserts a new built-in defined name, referring to the passed sheet range list. */
+ sal_uInt16 InsertBuiltInName( sal_Unicode cBuiltIn, const ScRangeList& rRangeList );
+
+ /** Inserts a new defined name. Sets another unused name, if rName already exists. */
+ sal_uInt16 InsertUniqueName( const OUString& rName, const XclTokenArrayRef& xTokArr, SCTAB nScTab );
+ /** Returns index of an existing name, or creates a name without definition. */
+ sal_uInt16 InsertRawName( const OUString& rName );
+ /** Searches or inserts a defined name describing a macro name.
+ @param bVBasic true = Visual Basic macro, false = Sheet macro.
+ @param bFunc true = Macro function; false = Macro procedure. */
+ sal_uInt16 InsertMacroCall( const OUString& rMacroName, bool bVBasic, bool bFunc, bool bHidden = false );
+
+ /** Returns the Calc sheet of a local defined name, or SCTAB_GLOBAL for global defined names. */
+ OUString GetOrigName( sal_uInt16 nNameIdx ) const;
+ /** Returns the Calc sheet of a local defined name, or SCTAB_GLOBAL for global defined names. */
+ SCTAB GetScTab( sal_uInt16 nNameIdx ) const;
+ /** Returns true, if the specified defined name is volatile. */
+ bool IsVolatile( sal_uInt16 nNameIdx ) const;
+
+ /** Writes the entire list of NAME records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpNameManagerImpl > XclExpNameMgrImplRef;
+ XclExpNameMgrImplRef mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xepage.hxx b/sc/source/filter/inc/xepage.hxx
new file mode 100644
index 000000000..7d5a57bfc
--- /dev/null
+++ b/sc/source/filter/inc/xepage.hxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xlpage.hxx"
+#include "xeroot.hxx"
+
+class XclExpImgData;
+
+// Page settings records ======================================================
+
+// Header/footer --------------------------------------------------------------
+
+/** Represents a HEADER or FOOTER record. */
+class XclExpHeaderFooter : public XclExpRecord
+{
+public:
+ explicit XclExpHeaderFooter( sal_uInt16 nRecId, const OUString& rHdrString );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Writes the header or footer string. Writes an empty record, if no header/footer present. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ OUString maHdrString; /// Header or footer contents.
+};
+
+// General page settings ------------------------------------------------------
+
+/** Represents a SETUP record that contains common page settings. */
+class XclExpSetup : public XclExpRecord
+{
+public:
+ explicit XclExpSetup( const XclPageData& rPageData );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Writes the contents of the SETUP record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclPageData& mrData; /// Page settings data of current sheet.
+};
+
+// Manual page breaks ---------------------------------------------------------
+
+/** Stores an array of manual page breaks for columns or rows. */
+class XclExpPageBreaks : public XclExpRecord
+{
+public:
+ explicit XclExpPageBreaks(
+ sal_uInt16 nRecId,
+ const ScfUInt16Vec& rPageBreaks,
+ sal_uInt16 nMaxPos );
+
+ /** Writes the record, if the list is not empty. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the page break list. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const ScfUInt16Vec& mrPageBreaks; /// Page settings data of current sheet.
+ sal_uInt16 mnMaxPos; /// Maximum row/column for BIFF8 page breaks.
+};
+
+// Page settings ==============================================================
+
+/** Contains all page (print) settings records for a single sheet. */
+class XclExpPageSettings : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Creates all records containing the current page settings. */
+ explicit XclExpPageSettings( const XclExpRoot& rRoot );
+
+ /** Returns read-only access to the page data. */
+ const XclPageData& GetPageData() const { return maData; }
+
+ /** Writes all page settings records to the stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ XclExpImgData* getGraphicExport();
+
+private:
+ XclPageData maData; /// Page settings data.
+};
+
+/** Contains all page (print) settings records for a chart object. */
+class XclExpChartPageSettings : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Creates all records containing the current page settings. */
+ explicit XclExpChartPageSettings( const XclExpRoot& rRoot );
+
+ /** Writes all page settings records to the stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ XclPageData maData; /// Page settings data.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xepivot.hxx b/sc/source/filter/inc/xepivot.hxx
new file mode 100644
index 000000000..1fb61696c
--- /dev/null
+++ b/sc/source/filter/inc/xepivot.hxx
@@ -0,0 +1,436 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xlpivot.hxx"
+#include "xeroot.hxx"
+
+class ScDPObject;
+class ScDPSaveData;
+class ScDPSaveDimension;
+class ScDPSaveMember;
+class ScDPSaveGroupDimension;
+struct ScDPNumGroupInfo;
+
+// Pivot cache
+
+/** Represents a data item in a pivot cache containing data of any type. */
+class XclExpPCItem : public XclExpRecord, public XclPCItem
+{
+public:
+ explicit XclExpPCItem( const OUString& rText );
+ explicit XclExpPCItem( double fValue, const OUString& rText = OUString() );
+ explicit XclExpPCItem( const DateTime& rDateTime, const OUString& rText = OUString() );
+ explicit XclExpPCItem( sal_Int16 nValue );
+ explicit XclExpPCItem( bool bValue, const OUString& rText );
+
+ sal_uInt16 GetTypeFlag() const { return mnTypeFlag; }
+
+ bool EqualsText( std::u16string_view rText ) const;
+ bool EqualsDouble( double fValue ) const;
+ bool EqualsDateTime( const DateTime& rDateTime ) const;
+ bool EqualsBool( bool bValue ) const;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnTypeFlag; /// Data type flag.
+};
+
+class XclExpPCField : public XclExpRecord, public XclPCField, protected XclExpRoot
+{
+public:
+ /** Creates a standard pivot cache field, filled from sheet source data. */
+ explicit XclExpPCField( const XclExpRoot& rRoot,
+ sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScRange& rRange );
+ /** Creates a child grouping pivot cache field, filled from the passed grouping info. */
+ explicit XclExpPCField( const XclExpRoot& rRoot,
+ sal_uInt16 nFieldIdx,
+ const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim,
+ const XclExpPCField& rBaseField );
+ virtual ~XclExpPCField() override;
+
+ /** Sets the passed field as direct grouping child field of this field. */
+ void SetGroupChildField( const XclExpPCField& rChildField );
+
+ /** Returns the name of this cache field. */
+ const OUString& GetFieldName() const { return maFieldInfo.maName; }
+
+ /** Returns the number of visible items of this field. */
+ sal_uInt16 GetItemCount() const;
+ /** Returns the specified pivot cache item (returns visible items in groupings). */
+ const XclExpPCItem* GetItem( sal_uInt16 nItemIdx ) const;
+ /** Returns the index of a pivot cache item, or EXC_PC_NOITEM on error. */
+ sal_uInt16 GetItemIndex( std::u16string_view rItemName ) const;
+
+ /** Returns the size an item index needs to write out. */
+ std::size_t GetIndexSize() const;
+ /** Writes the item index at the passed source row position as part of the SXINDEXLIST record. */
+ void WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const;
+
+ /** Writes the pivot cache field and all items and other related records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpPCItem > XclExpPCItemList;
+
+ /** Returns the item list that contains the visible items.
+ @descr Visible items are equal to source items in standard fields,
+ but are generated items in grouping and calculated fields. */
+ const XclExpPCItemList& GetVisItemList() const;
+
+ /** Initializes a standard field. Inserts all original source items. */
+ void InitStandardField( const ScRange& rRange );
+ /** Initializes a standard grouping field. Inserts all visible grouping items. */
+ void InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim );
+ /** Initializes a numeric grouping field. Inserts all visible grouping items and the limit settings. */
+ void InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo );
+ /** Initializes a date grouping field. Inserts all visible grouping items and the limit settings. */
+ void InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart );
+
+ /** Inserts the passed index into the item index array of original items. */
+ void InsertItemArrayIndex( size_t nListPos );
+ /** Inserts an original source item. Updates item index array. */
+ void InsertOrigItem( XclExpPCItem* pNewItem );
+ /** Inserts an original text item, if it is not contained already. */
+ void InsertOrigTextItem( const OUString& rText );
+ /** Inserts an original value item, if it is not contained already. */
+ void InsertOrigDoubleItem( double fValue, const OUString& rText );
+ /** Inserts an original date/time item, if it is not contained already. */
+ void InsertOrigDateTimeItem( const DateTime& rDateTime, const OUString& rText );
+ /** Inserts an original boolean item, if it is not contained already. */
+ void InsertOrigBoolItem( bool bValue, const OUString& rText );
+
+ /** Inserts an item into the grouping item list. Does not change anything else.
+ @return The list index of the new item. */
+ sal_uInt16 InsertGroupItem( XclExpPCItem* pNewItem );
+ /** Generates and inserts all visible items for numeric or date grouping. */
+ void InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart = 0 );
+
+ /** Inserts the SXDOUBLE items that specify the limits for a numeric grouping. */
+ void SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo );
+ /** Inserts the SXDATETIME/SXINTEGER items that specify the limits for a date grouping.
+ @param bUseStep true = Insert the passed step value; false = always insert 1. */
+ void SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep );
+
+ /** Initializes flags and item count fields. */
+ void Finalize();
+
+ /** Writes an SXNUMGROUP record and the additional items for a numeric grouping field. */
+ void WriteSxnumgroup( XclExpStream& rStrm );
+ /** Writes an SXGROUPINFO record describing the item order in grouping fields. */
+ void WriteSxgroupinfo( XclExpStream& rStrm );
+
+ /** Writes the contents of the SXFIELD record for this field. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpPCItemList maOrigItemList; /// List with original items.
+ XclExpPCItemList maGroupItemList; /// List with grouping items.
+ ScfUInt16Vec maIndexVec; /// Indexes into maItemList.
+ XclExpPCItemList maNumGroupLimits; /// List with limit values for numeric grouping.
+ sal_uInt16 mnTypeFlags; /// Collected item data type flags.
+};
+
+class XclExpPivotCache : public salhelper::SimpleReferenceObject, protected XclExpRoot
+{
+public:
+ explicit XclExpPivotCache( const XclExpRoot& rRoot,
+ const ScDPObject& rDPObj, sal_uInt16 nListIdx );
+
+ /** Returns true, if the cache has been constructed successfully. */
+ bool IsValid() const { return mbValid; }
+ /** Returns true, if the item index list will be written. */
+ bool HasItemIndexList() const;
+
+ /** Returns the list index of the cache used in pivot table records. */
+ sal_uInt16 GetCacheIndex() const { return mnListIdx; }
+
+ /** Returns the number of pivot cache fields. */
+ sal_uInt16 GetFieldCount() const;
+ /** Returns the specified pivot cache field. */
+ const XclExpPCField* GetField( sal_uInt16 nFieldIdx ) const;
+ /** Returns true, if this pivot cache contains non-standard fields (e.g. grouping fields). */
+ bool HasAddFields() const;
+
+ /** Returns true, if the passed DP object has the same data source as this cache. */
+ bool HasEqualDataSource( const ScDPObject& rDPObj ) const;
+
+ /** Writes related records into Workbook stream and creates the pivot cache storage stream. */
+ void Save( XclExpStream& rStrm );
+ static void SaveXml( XclExpXmlStream& rStrm );
+
+private:
+ /** Adds all pivot cache fields. */
+ void AddFields( const ScDPObject& rDPObj );
+
+ /** Adds all standard pivot cache fields based on source data. */
+ void AddStdFields( const ScDPObject& rDPObj );
+ /** Adds all grouping pivot cache fields. */
+ void AddGroupFields( const ScDPObject& rDPObj );
+
+ /** Writes the DCONREF record containing the source range. */
+ void WriteDconref( XclExpStream& rStrm ) const;
+ /** DCONNAME record contains range name source. */
+ void WriteDConName( XclExpStream& rStrm ) const;
+
+ /** Creates the pivot cache storage stream and writes the cache. */
+ void WriteCacheStream();
+ /** Writes the SXDB record. */
+ void WriteSxdb( XclExpStream& rStrm ) const;
+ /** Writes the SXDBEX record. */
+ static void WriteSxdbex( XclExpStream& rStrm );
+ /** Writes the SXINDEXLIST record list containing the item index table. */
+ void WriteSxindexlistList( XclExpStream& rStrm ) const;
+
+private:
+ typedef XclExpRecordList< XclExpPCField > XclExpPCFieldList;
+ typedef XclExpPCFieldList::RecordRefType XclExpPCFieldRef;
+
+ XclPCInfo maPCInfo; /// Pivot cache settings (SXDB record).
+ XclExpPCFieldList maFieldList; /// List of all pivot cache fields.
+ OUString maTabName; /// Name of source data sheet.
+ OUString maSrcRangeName; /// Range name for source data.
+ ScRange maOrigSrcRange; /// The original sheet source range.
+ ScRange maExpSrcRange; /// The exported sheet source range.
+ ScRange maDocSrcRange; /// The range used to build the cache fields and items.
+ sal_uInt16 mnListIdx; /// List index in pivot cache buffer.
+ bool mbValid; /// true = The cache is valid for export.
+};
+
+typedef rtl::Reference<XclExpPivotCache> XclExpPivotCacheRef;
+
+// Pivot table
+
+class XclExpPivotTable;
+
+/** Data field position specifying the pivot table field index (first) and data info index (second). */
+typedef ::std::pair< sal_uInt16, sal_uInt16 > XclPTDataFieldPos;
+
+class XclExpPTItem : public XclExpRecord
+{
+public:
+ explicit XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx );
+ explicit XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx );
+
+ /** Returns the internal name of this item. */
+ OUString GetItemName() const;
+
+ /** Fills this item with properties from the passed save member. */
+ void SetPropertiesFromMember( const ScDPSaveMember& rSaveMem );
+
+private:
+ /** Writes the SXVI record body describing the pivot table item. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpPCItem* mpCacheItem; /// The referred pivot cache item.
+ XclPTItemInfo maItemInfo; /// General data for this item.
+};
+
+class XclExpPTField : public XclExpRecordBase
+{
+public:
+ explicit XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx );
+
+ // data access ------------------------------------------------------------
+
+ /** Returns the name of this field. */
+ OUString GetFieldName() const;
+ /** Returns the pivot table field list index of this field.
+ * The field index is always equal to cache index.
+ */
+ sal_uInt16 GetFieldIndex() const { return maFieldInfo.mnCacheIdx; }
+
+ /** Returns the index of the last inserted data info struct. */
+ sal_uInt16 GetLastDataInfoIndex() const;
+
+ /** Returns the list index of an item by its name.
+ @param nDefaultIdx This value will be returned, if the item could not be found. */
+ sal_uInt16 GetItemIndex( std::u16string_view rName, sal_uInt16 nDefaultIdx ) const;
+
+ // fill data --------------------------------------------------------------
+
+ /** Fills this field with row/column/page properties from the passed save dimension. */
+ void SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim );
+ /** Fills this field with data field properties from the passed save dimension. */
+ void SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim );
+
+ /** Appends special items describing the field subtotal entries. */
+ void AppendSubtotalItems();
+
+ // records ----------------------------------------------------------------
+
+ /** Writes an entry for an SXPI record containing own page field info. */
+ void WriteSxpiEntry( XclExpStream& rStrm ) const;
+ /** Writes an SXDI records containing info about a data field. */
+ void WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const;
+
+ /** Writes the entire pivot table field. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns an item by its name. */
+ XclExpPTItem* GetItemAcc( std::u16string_view rName );
+
+ /** Appends a special item describing a field subtotal entry. */
+ void AppendSubtotalItem( sal_uInt16 nItemType );
+
+ /** Writes the SXVD record introducing the field. */
+ void WriteSxvd( XclExpStream& rStrm ) const;
+ /** Writes the SXVDEX record containing additional settings. */
+ void WriteSxvdex( XclExpStream& rStrm ) const;
+
+private:
+
+ const XclExpPivotTable& mrPTable; /// Parent pivot table containing this field.
+ const XclExpPCField* mpCacheField; /// The referred pivot cache field.
+ XclPTFieldInfo maFieldInfo; /// General field info (SXVD record).
+ XclPTFieldExtInfo maFieldExtInfo; /// Extended field info (SXVDEX record).
+ XclPTPageFieldInfo maPageInfo; /// Page field info (entry in SXPI record).
+ std::vector< XclPTDataFieldInfo >
+ maDataInfoVec; /// List of extended data field info (SXDI records).
+ XclExpRecordList< XclExpPTItem >
+ maItemList; /// List of all items of this field.
+};
+
+class XclExpPivotTable : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpPivotTable( const XclExpRoot& rRoot,
+ const ScDPObject& rDPObj, const XclExpPivotCache& rPCache );
+
+ /** Returns a pivot cache field. */
+ const XclExpPCField* GetCacheField( sal_uInt16 nCacheIdx ) const;
+
+ /** Returns the output range of the pivot table. */
+ SCTAB GetScTab() const { return mnOutScTab; }
+
+ /** Returns a pivot table field by its name. */
+ const XclExpPTField* GetField( sal_uInt16 nFieldIdx ) const;
+ /** Returns a pivot table field by its name. */
+ const XclExpPTField* GetField( std::u16string_view rName ) const;
+
+ /** Returns the data-field-only index of the first data field with the passed name.
+ @param nDefaultIdx This value will be returned, if the field could not be found. */
+ sal_uInt16 GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const;
+
+ /** Writes the entire pivot table. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns a pivot table field by its name. */
+ XclExpPTField* GetFieldAcc( std::u16string_view rName );
+ /** Returns a pivot table field corresponding to the passed save dimension. */
+ XclExpPTField* GetFieldAcc( const ScDPSaveDimension& rSaveDim );
+
+ // fill data --------------------------------------------------------------
+
+ /** Fills internal members with all properties from the passed save data. */
+ void SetPropertiesFromDP( const ScDPSaveData& rSaveData );
+ /** Fills a pivot table field with all properties from the passed save dimension. */
+ void SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim );
+ /** Fills a pivot table data field with all properties from the passed save dimension. */
+ void SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim );
+
+ /** Initializes any data after processing the entire source DataPilot. */
+ void Finalize();
+
+ // records ----------------------------------------------------------------
+
+ /** Writes the SXVIEW record starting the pivot table. */
+ void WriteSxview( XclExpStream& rStrm ) const;
+ /** Writes an SXIVD record for row field or column field order. */
+ static void WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields );
+ /** Writes the SXPI record containing page field info. */
+ void WriteSxpi( XclExpStream& rStrm ) const;
+ /** Writes all SXDI records containing info about the data fields. */
+ void WriteSxdiList( XclExpStream& rStrm ) const;
+ /** Writes a dummy SXLI records containing item layout info. */
+ static void WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount );
+ /** Writes the SXEX records containing additional pivot table info. */
+ void WriteSxex( XclExpStream& rStrm ) const;
+
+ void WriteQsiSxTag( XclExpStream& rStrm ) const;
+ /** Writes the SX_AUTOFORMAT records with the autoformat id and header layout */
+ void WriteSxViewEx9( XclExpStream& rStrm ) const;
+
+private:
+ typedef XclExpRecordList< XclExpPTField > XclExpPTFieldList;
+ typedef XclExpPTFieldList::RecordRefType XclExpPTFieldRef;
+
+ const XclExpPivotCache& mrPCache; /// The pivot cache this pivot table bases on.
+ XclPTInfo maPTInfo; /// Info about the pivot table (SXVIEW record).
+ XclPTExtInfo maPTExtInfo; /// Extended info about the pivot table (SXEX record).
+ XclPTViewEx9Info maPTViewEx9Info; /// The selected autoformat (SXVIEWEX9)
+ XclExpPTFieldList maFieldList; /// All fields in pivot cache order.
+ ScfUInt16Vec maRowFields; /// Row field indexes.
+ ScfUInt16Vec maColFields; /// Column field indexes.
+ ScfUInt16Vec maPageFields; /// Page field indexes.
+ std::vector< XclPTDataFieldPos >
+ maDataFields; /// Data field indexes.
+ XclExpPTField maDataOrientField; /// Special data field orientation field.
+ SCTAB mnOutScTab; /// Sheet index of the output range.
+ bool mbValid; /// true = The pivot table is valid for export.
+ bool mbFilterBtn; /// true = DataPilot has filter button.
+};
+
+/** The main class for pivot table export.
+
+ This class contains all pivot caches and pivot tables in a Calc document.
+ It creates the pivot cache streams and pivot table records in the main
+ workbook stream. It supports sharing of pivot caches between multiple pivot
+ tables to decrease file size.
+ */
+class XclExpPivotTableManager : protected XclExpRoot
+{
+public:
+ explicit XclExpPivotTableManager( const XclExpRoot& rRoot );
+
+ /** Creates all pivot tables and caches from the Calc DataPilot objects. */
+ void CreatePivotTables();
+
+ /** Creates a record wrapper for exporting all pivot caches. */
+ XclExpRecordRef CreatePivotCachesRecord();
+ /** Creates a record wrapper for exporting all pivot tables of the specified sheet. */
+ XclExpRecordRef CreatePivotTablesRecord( SCTAB nScTab );
+
+ /** Writes all pivot caches (all Workbook records and cache streams). */
+ void WritePivotCaches( XclExpStream& rStrm );
+ /** Writes all pivot tables of the specified Calc sheet. */
+ void WritePivotTables( XclExpStream& rStrm, SCTAB nScTab );
+
+private:
+ /** Finds an existing (if enabled in mbShareCaches) or creates a new pivot cache.
+ @return Pointer to the pivot cache or 0, if the passed source range was invalid. */
+ const XclExpPivotCache* CreatePivotCache( const ScDPObject& rDPObj );
+
+private:
+ typedef XclExpRecordList< XclExpPivotTable > XclExpPivotTableList;
+ typedef XclExpPivotTableList::RecordRefType XclExpPivotTableRef;
+
+ XclExpRecordList< XclExpPivotCache > maPCacheList; /// List of all pivot caches.
+ XclExpPivotTableList maPTableList; /// List of all pivot tables.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xepivotxml.hxx b/sc/source/filter/inc/xepivotxml.hxx
new file mode 100644
index 000000000..30fb25a30
--- /dev/null
+++ b/sc/source/filter/inc/xepivotxml.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xeroot.hxx"
+
+#include <memory>
+#include <map>
+#include <unordered_map>
+
+class ScDPCache;
+class ScDPObject;
+
+class XclExpXmlPivotCaches : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ struct Entry
+ {
+ const ScDPCache* mpCache;
+ ScRange maSrcRange;
+ };
+
+ XclExpXmlPivotCaches(const XclExpRoot& rRoot);
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+ void SetCaches(std::vector<Entry>&& rCaches);
+ bool HasCaches() const;
+ const Entry* GetCache(sal_Int32 nCacheId) const;
+
+private:
+ void SavePivotCacheXml(XclExpXmlStream& rStrm, const Entry& rEntry, sal_Int32 nCacheId);
+
+private:
+ std::vector<Entry> maCaches;
+};
+
+class XclExpXmlPivotTables : public XclExpRecordBase, protected XclExpRoot
+{
+ struct Entry
+ {
+ const ScDPObject* mpTable;
+ sal_Int32 mnCacheId;
+ sal_Int32 mnPivotId; /// used as [n] in pivotTable[n].xml part name.
+
+ Entry(const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId);
+ };
+
+ const XclExpXmlPivotCaches& mrCaches;
+ typedef std::vector<Entry> TablesType;
+ TablesType maTables;
+
+public:
+ XclExpXmlPivotTables(const XclExpRoot& rRoot, const XclExpXmlPivotCaches& rCaches);
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+
+ void AppendTable(const ScDPObject* pTable, sal_Int32 nCacheId, sal_Int32 nPivotId);
+
+private:
+ void SavePivotTableXml(XclExpXmlStream& rStrm, const ScDPObject& rObj, sal_Int32 nCacheId);
+};
+
+class XclExpXmlPivotTableManager : protected XclExpRoot
+{
+ typedef std::map<SCTAB, std::unique_ptr<XclExpXmlPivotTables>> TablesType;
+ typedef std::unordered_map<const ScDPObject*, sal_Int32> CacheIdMapType;
+
+public:
+ XclExpXmlPivotTableManager(const XclExpRoot& rRoot);
+
+ void Initialize();
+
+ XclExpXmlPivotCaches& GetCaches();
+ XclExpXmlPivotTables* GetTablesBySheet(SCTAB nTab);
+
+private:
+ XclExpXmlPivotCaches maCaches;
+ TablesType m_Tables;
+ CacheIdMapType maCacheIdMap;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xerecord.hxx b/sc/source/filter/inc/xerecord.hxx
new file mode 100644
index 000000000..c6901e7fb
--- /dev/null
+++ b/sc/source/filter/inc/xerecord.hxx
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlconst.hxx"
+#include "xestream.hxx"
+#include "xlstream.hxx"
+#include <salhelper/simplereferenceobject.hxx>
+#include <rtl/ref.hxx>
+#include <algorithm>
+
+// Base classes to export Excel records =======================================
+
+/** Base class for all Excel records.
+
+ Derive from this class to implement any functionality performed during
+ saving the records - except really writing a record (i.e. write a list of
+ records contained in the class). Derive from XclExpRecord (instead from
+ this class) to write common records.
+ */
+class XclExpRecordBase : public salhelper::SimpleReferenceObject
+{
+public:
+ XclExpRecordBase() {}
+
+ // this class is stored both ref-counted and by value
+ XclExpRecordBase(XclExpRecordBase const &)
+ : salhelper::SimpleReferenceObject() {}
+ XclExpRecordBase(XclExpRecordBase &&)
+ : salhelper::SimpleReferenceObject() {}
+ XclExpRecordBase& operator=(XclExpRecordBase const &)
+ { return *this; }
+ XclExpRecordBase& operator=(XclExpRecordBase &&) noexcept
+ { return *this; }
+
+ virtual ~XclExpRecordBase();
+
+ /** Overwrite this method to do any operation while saving the record. */
+ virtual void Save( XclExpStream& rStrm );
+ virtual void SaveXml( XclExpXmlStream& rStrm );
+};
+
+/*namespace std
+{
+ template<typename T,
+ typename = typename std::enable_if<
+ std::is_base_of<XclExpRecordBase,T>::value
+ >::type>
+ class shared_ptr
+ {
+ shared_ptr() {}
+ };
+};*/
+
+class XclExpDelegatingRecord : public XclExpRecordBase
+{
+public:
+ XclExpDelegatingRecord( XclExpRecordBase* pRecord );
+ virtual ~XclExpDelegatingRecord() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ XclExpRecordBase* mpRecord;
+};
+
+class XclExpXmlElementRecord : public XclExpRecordBase
+{
+public:
+ explicit XclExpXmlElementRecord(sal_Int32 nElement);
+ virtual ~XclExpXmlElementRecord() override;
+
+protected:
+ sal_Int32 mnElement;
+};
+
+class XclExpXmlStartElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlStartElementRecord(sal_Int32 nElement);
+ virtual ~XclExpXmlStartElementRecord() override;
+
+ /** Starts the element nElement */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpXmlEndElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlEndElementRecord(sal_Int32 nElement);
+ virtual ~XclExpXmlEndElementRecord() override;
+
+ /** Ends the element nElement */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpXmlStartSingleElementRecord : public XclExpXmlElementRecord
+{
+public:
+ explicit XclExpXmlStartSingleElementRecord(sal_Int32 nElement);
+ virtual ~XclExpXmlStartSingleElementRecord() override;
+
+ /** Starts the single element nElement */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+class XclExpXmlEndSingleElementRecord : public XclExpRecordBase
+{
+public:
+ XclExpXmlEndSingleElementRecord();
+ virtual ~XclExpXmlEndSingleElementRecord() override;
+
+ /** Ends the single element nElement */
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+/** Base class for single records with any content.
+
+ This class handles writing the record header. Derived classes only have to
+ write the record body. Calculating the record size before saving optimizes
+ the write process (the stream does not have to seek back and update the
+ written record size). But it is not required to calculate a valid size
+ (maybe it would be too complex or just impossible until the record is
+ really written).
+ */
+class XclExpRecord : public XclExpRecordBase
+{
+public:
+ /** @param nRecId The record ID of this record. May be set later with SetRecId().
+ @param nRecSize The predicted record size. May be set later with SetRecSize(). */
+ explicit XclExpRecord(
+ sal_uInt16 nRecId = EXC_ID_UNKNOWN,
+ std::size_t nRecSize = 0 );
+
+ XclExpRecord(XclExpRecord const &) = default;
+
+ virtual ~XclExpRecord() override;
+
+ /** Returns the current record ID. */
+ sal_uInt16 GetRecId() const { return mnRecId; }
+ /** Returns the current record size prediction. */
+ std::size_t GetRecSize() const { return mnRecSize; }
+
+ /** Sets a new record ID. */
+ void SetRecId( sal_uInt16 nRecId ) { mnRecId = nRecId; }
+ /** Sets a new record size prediction. */
+ void SetRecSize( std::size_t nRecSize ) { mnRecSize = nRecSize; }
+ /** Adds a size value to the record size prediction. */
+ void AddRecSize( std::size_t nRecSize ) { mnRecSize += nRecSize; }
+ /** Sets record ID and size with one call. */
+ void SetRecHeader( sal_uInt16 nRecId, std::size_t nRecSize );
+
+ /** Writes the record header and calls WriteBody(). */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+protected:
+ /** Writes the body of the record (without record header).
+ @descr Usually this method will be overwritten by derived classes. */
+ virtual void WriteBody( XclExpStream& rStrm );
+
+private:
+ std::size_t mnRecSize; /// The predicted record size.
+ sal_uInt16 mnRecId; /// The record ID.
+};
+
+/** A record without body. Only the record ID and the size 0 will be written. */
+class XclExpEmptyRecord : public XclExpRecord
+{
+public:
+ /** @param nRecId The record ID of this record. */
+ inline explicit XclExpEmptyRecord( sal_uInt16 nRecId );
+};
+
+inline XclExpEmptyRecord::XclExpEmptyRecord( sal_uInt16 nRecId ) :
+ XclExpRecord( nRecId, 0 )
+{
+}
+
+/** A record with a single value of type Type.
+ @descr Requires operator<<( XclExpStream&, const Type& ). */
+template< typename Type >
+class XclExpValueRecord : public XclExpRecord
+{
+public:
+ /** @param nRecId The record ID of this record.
+ @param rValue The value for the record body.
+ @param nSize Record size. Uses sizeof( Type ), if this parameter is omitted. */
+ explicit XclExpValueRecord( sal_uInt16 nRecId, const Type& rValue, std::size_t nSize = sizeof( Type ) ) :
+ XclExpRecord( nRecId, nSize ), maValue( rValue ), mnAttribute( -1 ) {}
+
+ /** Returns the value of the record. */
+ const Type& GetValue() const { return maValue; }
+ /** Sets a new record value. */
+ void SetValue( const Type& rValue ) { maValue = rValue; }
+
+ /** Sets the OOXML attribute this record corresponds to */
+ XclExpValueRecord* SetAttribute( sal_Int32 nId );
+
+ /** Write the OOXML attribute and its value */
+ void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override { rStrm << maValue; }
+ // inlining prevents warning in wntmsci10
+
+private:
+ Type maValue; /// The record data.
+ sal_Int32 mnAttribute; /// The OOXML attribute Id
+};
+
+template< typename Type >
+void XclExpValueRecord< Type >::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( mnAttribute == -1 )
+ return;
+ rStrm.WriteAttributes(mnAttribute, OUString::number(maValue));
+}
+
+template<>
+void XclExpValueRecord<double>::SaveXml( XclExpXmlStream& rStrm );
+
+template< typename Type >
+XclExpValueRecord< Type >* XclExpValueRecord< Type >::SetAttribute( sal_Int32 nId )
+{
+ mnAttribute = nId;
+ return this;
+}
+
+/** A record containing an unsigned 16-bit value. */
+typedef XclExpValueRecord< sal_uInt16 > XclExpUInt16Record;
+
+/** A record containing a double value. */
+typedef XclExpValueRecord< double > XclExpDoubleRecord;
+
+/** Record which contains a Boolean value.
+ @descr The value is stored as 16-bit value: 0x0000 = sal_False, 0x0001 = TRUE. */
+class XclExpBoolRecord : public XclExpRecord
+{
+public:
+ /** @param nRecId The record ID of this record.
+ @param nValue The value for the record body. */
+ explicit XclExpBoolRecord( sal_uInt16 nRecId, bool bValue, sal_Int32 nAttribute = -1 ) :
+ XclExpRecord( nRecId, 2 ), mbValue( bValue ), mnAttribute( nAttribute ) {}
+
+ /** Returns the Boolean value of the record. */
+ bool GetBool() const { return mbValue; }
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the body of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ bool mbValue; /// The record data.
+ sal_Int32 mnAttribute; /// The attribute to generate within SaveXml()
+};
+
+/** Record which exports a memory data array. */
+class XclExpDummyRecord : public XclExpRecord
+{
+public:
+ /** @param nRecId The record ID of this record.
+ @param pRecData Pointer to the data array representing the record body.
+ @param nRecSize Size of the data array. */
+ explicit XclExpDummyRecord(
+ sal_uInt16 nRecId, const void* pRecData, std::size_t nRecSize );
+
+ /** Sets a data array. */
+ void SetData( const void* pRecData, std::size_t nRecSize );
+
+private:
+ /** Writes the body of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const void* mpData; /// The record data.
+};
+
+// Future records =============================================================
+
+class XclExpFutureRecord : public XclExpRecord
+{
+public:
+ explicit XclExpFutureRecord( XclFutureRecType eRecType,
+ sal_uInt16 nRecId, std::size_t nRecSize );
+
+ /** Writes the extended record header and calls WriteBody(). */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ XclFutureRecType meRecType;
+};
+
+// List of records ============================================================
+
+/** A list of Excel record objects.
+
+ Provides saving the compete list. This class is derived from
+ XclExpRecordBase, so it can be used as record in another record list.
+ Requires RecType::Save( XclExpStream& ).
+ */
+template< typename RecType = XclExpRecordBase >
+class XclExpRecordList : public XclExpRecordBase
+{
+public:
+ typedef rtl::Reference< RecType > RecordRefType;
+
+ bool IsEmpty() const { return maRecs.empty(); }
+ size_t GetSize() const { return maRecs.size(); }
+
+ /** Returns true, if the passed index points to an exiting record. */
+ bool HasRecord( size_t nPos ) const
+ { return nPos < maRecs.size(); }
+ /** Returns reference to an existing record or empty reference on error. */
+ RecType* GetRecord( size_t nPos ) const
+ { return nPos < maRecs.size() ? maRecs[ nPos ].get() : nullptr; }
+ /** Returns reference to the first existing record or empty reference, if list is empty. */
+ RecType* GetFirstRecord() const
+ { return maRecs.empty() ? nullptr : maRecs.front().get(); }
+ /** Returns reference to the last existing record or empty reference, if list is empty. */
+ RecType* GetLastRecord() const
+ { return maRecs.empty() ? nullptr : maRecs.back().get(); }
+
+ /** Inserts a record at the specified position into the list. */
+ void InsertRecord( RecType* pRec, size_t nPos )
+ { assert(pRec); maRecs.insert( maRecs.begin() + ::std::min( nPos, maRecs.size() ), pRec ); }
+ void InsertRecord( RecordRefType pRec, size_t nPos )
+ { assert(pRec); maRecs.insert( maRecs.begin() + ::std::min( nPos, maRecs.size() ), std::move(pRec) ); }
+ /** Appends a record to the list. */
+ void AppendRecord( RecType* pRec )
+ { if (pRec) maRecs.push_back( pRec ); }
+ void AppendRecord( const RecordRefType& xRec )
+ { if (xRec) maRecs.push_back( xRec.get() ); }
+ void AppendRecord( RecordRefType xRec ) &&
+ { if (xRec) maRecs.push_back( std::move(xRec) ); }
+ /** Replaces the record at the specified position from the list with the passed record. */
+ void ReplaceRecord( RecType* pRec, size_t nPos )
+ { if (pRec) maRecs[nPos] = pRec; else RemoveRecord( nPos ); }
+ void ReplaceRecord( RecordRefType const & xRec, size_t nPos )
+ { ReplaceRecord(xRec.get(), nPos); }
+
+ /** Appends a newly created record to the list. */
+ void AppendNewRecord( RecType* pRec )
+ { assert(pRec); maRecs.push_back( pRec ); }
+ void AppendNewRecord( RecordRefType const & xRec )
+ { AppendNewRecord(xRec.get()); }
+ void AppendNewRecord( RecordRefType xRec ) &&
+ { assert(xRec); maRecs.append(std::move(xRec)); }
+
+ /** Removes the record at the specified position from the list. */
+ void RemoveRecord( size_t nPos )
+ { maRecs.erase( maRecs.begin() + nPos ); }
+ /** Removes all records from the list. */
+ void RemoveAllRecords() { maRecs.clear(); }
+
+ /** Writes the complete record list. */
+ virtual void Save( XclExpStream& rStrm ) override
+ {
+ // inlining prevents warning in wntmsci10
+ for( typename RecordVec::iterator aIt = maRecs.begin(), aEnd = maRecs.end(); aIt != aEnd; ++aIt )
+ (*aIt)->Save( rStrm );
+ }
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override
+ {
+ // inlining prevents warning in wntmsci10
+ for( typename RecordVec::iterator aIt = maRecs.begin(), aEnd = maRecs.end(); aIt != aEnd; ++aIt )
+ (*aIt)->SaveXml( rStrm );
+ }
+
+ /**
+ Optimization for repeated removal. Since this is internally a vector, repeated RemoveRecord()
+ would repeatedly move all items after the removed position. Instead it's possible to invalidate
+ a record and then call RemoveInvalidatedRecords() at the end (which is necessary, the list is generally
+ not allowed to contain invalid entries).
+ */
+ void InvalidateRecord( size_t nPos ) { maRecs[ nPos ] = nullptr; }
+ void RemoveInvalidatedRecords()
+ {
+ maRecs.erase(
+ std::remove_if( maRecs.begin(), maRecs.end(), [](const RecordRefType& xRec) { return xRec == nullptr; } ),
+ maRecs.end());
+ }
+
+private:
+ typedef ::std::vector< RecordRefType > RecordVec;
+ RecordVec maRecs;
+};
+
+/** Represents a complete substream of records enclosed into a pair of BOF/EOF records. */
+class XclExpSubStream : public XclExpRecordList<>
+{
+public:
+ explicit XclExpSubStream( sal_uInt16 nSubStrmType );
+
+ /** Writes the complete substream, including leading BOF and trailing EOF. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnSubStrmType; /// Substream type, stored in leading BOF record.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeroot.hxx b/sc/source/filter/inc/xeroot.hxx
new file mode 100644
index 000000000..ea65aba7e
--- /dev/null
+++ b/sc/source/filter/inc/xeroot.hxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlroot.hxx"
+#include <compiler.hxx>
+#include <memory>
+#include <rtl/ref.hxx>
+
+// Forward declarations of objects in public use ==============================
+
+namespace com::sun::star::beans { struct NamedValue; }
+
+class XclExpRecordBase;
+class XclExpString;
+
+typedef rtl::Reference< XclExpRecordBase > XclExpRecordRef;
+typedef std::shared_ptr< XclExpString > XclExpStringRef;
+
+// Global data ================================================================
+
+class XclExpTabInfo;
+class XclExpAddressConverter;
+class XclExpFormulaCompiler;
+class XclExpProgressBar;
+class XclExpSst;
+class XclExpPalette;
+class XclExpFontBuffer;
+class XclExpNumFmtBuffer;
+class XclExpXFBuffer;
+class XclExpLinkManager;
+class XclExpNameManager;
+class XclExpObjectManager;
+class XclExpFilterManager;
+class XclExpPivotTableManager;
+class XclExpDxfs;
+class XclExpXmlPivotTableManager;
+class XclExpTablesManager;
+namespace sc { class CompileFormulaContext; }
+
+/** Stores global buffers and data needed for Excel export filter. */
+struct XclExpRootData : public XclRootData
+{
+ typedef std::shared_ptr< XclExpTabInfo > XclExpTabInfoRef;
+ typedef std::shared_ptr< XclExpAddressConverter > XclExpAddrConvRef;
+ typedef std::shared_ptr< XclExpFormulaCompiler > XclExpFmlaCompRef;
+ typedef std::shared_ptr< XclExpProgressBar > XclExpProgressRef;
+
+ typedef rtl::Reference< XclExpSst > XclExpSstRef;
+ typedef rtl::Reference< XclExpPalette > XclExpPaletteRef;
+ typedef rtl::Reference< XclExpFontBuffer > XclExpFontBfrRef;
+ typedef rtl::Reference< XclExpNumFmtBuffer > XclExpNumFmtBfrRef;
+ typedef rtl::Reference< XclExpXFBuffer > XclExpXFBfrRef;
+ typedef rtl::Reference< XclExpNameManager > XclExpNameMgrRef;
+ typedef rtl::Reference< XclExpLinkManager > XclExpLinkMgrRef;
+ typedef std::shared_ptr< XclExpObjectManager > XclExpObjectMgrRef;
+ typedef std::shared_ptr< XclExpFilterManager > XclExpFilterMgrRef;
+ typedef std::shared_ptr< XclExpPivotTableManager > XclExpPTableMgrRef;
+ typedef rtl::Reference< XclExpDxfs > XclExpDxfsRef;
+
+ XclExpTabInfoRef mxTabInfo; /// Calc->Excel sheet index conversion.
+ XclExpAddrConvRef mxAddrConv; /// The address converter.
+ XclExpFmlaCompRef mxFmlaComp; /// The formula compiler.
+ XclExpProgressRef mxProgress; /// The export progress bar.
+
+ XclExpSstRef mxSst; /// The shared string table.
+ XclExpPaletteRef mxPalette; /// The color buffer.
+ XclExpFontBfrRef mxFontBfr; /// All fonts in the file.
+ XclExpNumFmtBfrRef mxNumFmtBfr; /// All number formats in the file.
+ XclExpXFBfrRef mxXFBfr; /// All XF records in the file.
+ XclExpNameMgrRef mxNameMgr; /// Internal defined names.
+ XclExpLinkMgrRef mxGlobLinkMgr; /// Global link manager for defined names.
+ XclExpLinkMgrRef mxLocLinkMgr; /// Local link manager for a sheet.
+ XclExpObjectMgrRef mxObjMgr; /// All drawing objects.
+ XclExpFilterMgrRef mxFilterMgr; /// Manager for filtered areas in all sheets.
+ XclExpPTableMgrRef mxPTableMgr; /// All pivot tables and pivot caches.
+ XclExpDxfsRef mxDxfs; /// All delta formatting entries
+
+ std::shared_ptr<XclExpXmlPivotTableManager> mxXmlPTableMgr;
+ std::shared_ptr<XclExpTablesManager> mxTablesMgr;
+ std::shared_ptr<sc::CompileFormulaContext> mpCompileFormulaCxt;
+
+ ScCompiler::OpCodeMapPtr mxOpCodeMap; /// mapping between op-codes and names
+
+ bool mbRelUrl; /// true = Store URLs relative.
+
+ OStringBuffer maStringBuf; /// buffer to avoid massive OUString allocations
+
+ explicit XclExpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc );
+ virtual ~XclExpRootData() override;
+};
+
+/** Access to global data from other classes. */
+class XclExpRoot : public XclRoot
+{
+public:
+ explicit XclExpRoot( XclExpRootData& rExpRootData );
+
+ /** Returns this root instance - for code readability in derived classes. */
+ const XclExpRoot& GetRoot() const { return *this; }
+ /** Returns true, if URLs should be stored relative to the document location. */
+ bool IsRelUrl() const { return mrExpData.mbRelUrl; }
+ sc::CompileFormulaContext& GetCompileFormulaContext() const { return *mrExpData.mpCompileFormulaCxt; }
+
+ /** Returns the buffer for Calc->Excel sheet index conversion. */
+ XclExpTabInfo& GetTabInfo() const;
+ /** Returns the address converter. */
+ XclExpAddressConverter& GetAddressConverter() const;
+ /** Returns the formula compiler to produce formula token arrays. */
+ XclExpFormulaCompiler& GetFormulaCompiler() const;
+ /** Returns the export progress bar. */
+ XclExpProgressBar& GetProgressBar() const;
+
+ /** Returns the shared string table. */
+ XclExpSst& GetSst() const;
+ /** Returns the color buffer. */
+ XclExpPalette& GetPalette() const;
+ /** Returns the font buffer. */
+ XclExpFontBuffer& GetFontBuffer() const;
+ /** Returns the number format buffer. */
+ XclExpNumFmtBuffer& GetNumFmtBuffer() const;
+ /** Returns the cell formatting attributes buffer. */
+ XclExpXFBuffer& GetXFBuffer() const;
+ /** Returns the global link manager for defined names. */
+ XclExpLinkManager& GetGlobalLinkManager() const;
+ /** Returns the local link manager for the current sheet. */
+ XclExpLinkManager& GetLocalLinkManager() const;
+ /** Returns the buffer that contains internal defined names. */
+ XclExpNameManager& GetNameManager() const;
+ /** Returns the drawing object manager. */
+ XclExpObjectManager& GetObjectManager() const;
+ /** Returns the filter manager. */
+ XclExpFilterManager& GetFilterManager() const;
+ /** Returns the pivot table manager. */
+ XclExpPivotTableManager& GetPivotTableManager() const;
+ /** Returns the differential formatting list */
+ XclExpDxfs& GetDxfs() const;
+
+ /** Clean and return the OStringBuffer */
+ OStringBuffer& GetStringBuf() const { mrExpData.maStringBuf.setLength(0); return mrExpData.maStringBuf; }
+
+ XclExpXmlPivotTableManager& GetXmlPivotTableManager();
+
+ XclExpTablesManager& GetTablesManager();
+
+ /** Is called when export filter starts to create the Excel document (all BIFF versions). */
+ void InitializeConvert();
+ /** Is called when export filter starts to create the workbook global data (>=BIFF5). */
+ void InitializeGlobals();
+ /** Is called when export filter starts to create data for a single sheet (all BIFF versions). */
+ void InitializeTable( SCTAB nScTab );
+ /** Is called before export filter starts to write the records to the stream. */
+ void InitializeSave();
+ /** Returns the reference to a record (or record list) representing a root object.
+ @param nRecId Identifier that specifies which record is returned. */
+ XclExpRecordRef CreateRecord( sal_uInt16 nRecId ) const;
+
+ bool IsDocumentEncrypted() const;
+
+ static css::uno::Sequence< css::beans::NamedValue > GenerateEncryptionData( const OUString& aPass );
+ css::uno::Sequence< css::beans::NamedValue > GetEncryptionData() const;
+ static css::uno::Sequence< css::beans::NamedValue > GenerateDefaultEncryptionData();
+
+private:
+
+ /** Returns the local or global link manager, depending on current context. */
+ XclExpRootData::XclExpLinkMgrRef const & GetLocalLinkMgrRef() const;
+
+private:
+ XclExpRootData& mrExpData; /// Reference to the global export data struct.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xestream.hxx b/sc/source/filter/inc/xestream.hxx
new file mode 100644
index 000000000..669dadaec
--- /dev/null
+++ b/sc/source/filter/inc/xestream.hxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <stack>
+#include <string_view>
+
+#include <rtl/strbuf.hxx>
+
+#include <oox/core/xmlfilterbase.hxx>
+#include <sax/fshelper.hxx>
+#include <tools/stream.hxx>
+#include <formula/errorcodes.hxx>
+#include "ftools.hxx"
+#include <types.hxx>
+
+#include <filter/msfilter/mscodec.hxx>
+#include <vector>
+
+namespace com::sun::star::beans { struct NamedValue; }
+
+/* ============================================================================
+Output stream class for Excel export
+- CONTINUE record handling
+============================================================================ */
+
+class XclExpString;
+class XclExpRoot;
+class XclExpBiff8Encrypter;
+typedef std::shared_ptr< XclExpBiff8Encrypter > XclExpEncrypterRef;
+
+/** This class is used to export Excel record streams.
+ @descr An instance is constructed with an SvStream and the maximum size of Excel
+ record contents (in BIFF5: 2080 bytes, in BIFF8: 8224 bytes).
+
+ To start writing a record call StartRecord(). Parameters are the record identifier
+ and any calculated record size. This is for optimizing the write process: if the real
+ written data has the same size as the calculated, the stream will not seek back and
+ update the record size field. But it is not mandatory to calculate a size. Each
+ record must be closed by calling EndRecord(). This will check (and update) the record
+ size field.
+
+ If some data exceeds the record size limit, a CONTINUE record is started automatically
+ and the new data will be written to this record.
+
+ If specific data pieces must not be split, use SetSliceSize(). For instance:
+ To write a sequence of 16-bit values, where 4 values form a unit and cannot be
+ split, call SetSliceSize( 8 ) first (4*2 bytes == 8).
+
+ To write unicode character arrays, call WriteUnicodeBuffer(). It creates CONTINUE
+ records and repeats the unicode string flag byte automatically. This function is used
+ for instance from the class XclExpString which can write complete unicode strings.
+*/
+class XclExpStream
+{
+public:
+ /** Constructs the Excel record export stream.
+ @param rOutStrm The system output stream to write to.
+ @param nMaxRecSize The maximum allowed size of record content (depending on BIFF type).
+ If 0 is passed, the record size will be set automatically, depending on the current BIFF type. */
+ XclExpStream(
+ SvStream& rOutStrm,
+ const XclExpRoot& rRoot,
+ sal_uInt16 nMaxRecSize = 0 );
+
+ ~XclExpStream();
+
+ /** Returns the filter root data. */
+ const XclExpRoot& GetRoot() const { return mrRoot; }
+
+ /** Starts a new record: writes header data, stores calculated record size. */
+ void StartRecord( sal_uInt16 nRecId, std::size_t nRecSize );
+ /** Checks and corrects real record length. Must be called every time a record is finished. */
+ void EndRecord();
+
+ /** Returns the position inside of current record (starts by 0 in every CONTINUE). */
+ sal_uInt16 GetRawRecPos() const { return mnCurrSize; }
+
+ /** Sets data slice length. 0 = no slices. */
+ void SetSliceSize( sal_uInt16 nSize );
+
+ XclExpStream& operator<<( sal_Int8 nValue );
+ XclExpStream& operator<<( sal_uInt8 nValue );
+ XclExpStream& operator<<( sal_Int16 nValue );
+ XclExpStream& operator<<( sal_uInt16 nValue );
+ XclExpStream& operator<<( sal_Int32 nValue );
+ XclExpStream& operator<<( sal_uInt32 nValue );
+ XclExpStream& operator<<( float fValue );
+ XclExpStream& operator<<( double fValue );
+
+ /** Writes nBytes bytes from memory. */
+ std::size_t Write( const void* pData, std::size_t nBytes );
+ /** Writes a sequence of nBytes zero bytes (respects slice setting). */
+ void WriteZeroBytes( std::size_t nBytes );
+
+ void WriteZeroBytesToRecord( std::size_t nBytes );
+
+ /** Copies nBytes bytes from current position of the stream rInStrm.
+ @descr Omitting the second parameter means: read to end of stream. */
+ void CopyFromStream( SvStream& rInStrm, sal_uInt64 nBytes = STREAM_SEEK_TO_END );
+
+ // *** unicode string export is realized with helper class XclExpString ***
+ // (slice length setting has no effect here -> disabled automatically)
+
+ /** Writes Unicode buffer as 8/16 bit, repeats nFlags at start of a CONTINUE record. */
+ void WriteUnicodeBuffer( const ScfUInt16Vec& rBuffer, sal_uInt8 nFlags );
+
+ // *** write 8-bit-strings ***
+ // (slice length setting has no effect here -> disabled automatically)
+
+ /** Writes string length field and OString buffer. */
+ void WriteByteString( const OString& rString );
+
+ /** Writes 8-bit character buffer. */
+ void WriteCharBuffer( const ScfUInt8Vec& rBuffer );
+
+ // *** SvStream access ***
+
+ /** Sets position of system stream (only allowed outside of records). */
+ void SetSvStreamPos(sal_uInt64 nPos);
+ /** Returns the absolute position of the system stream. */
+ sal_uInt64 GetSvStreamPos() const { return mrStrm.Tell(); }
+
+ void SetEncrypter( XclExpEncrypterRef const & xEncrypter );
+
+ bool HasValidEncrypter() const;
+
+ void EnableEncryption( bool bEnable = true );
+
+ void DisableEncryption();
+
+private:
+ /** Writes header data, internal setup. */
+ void InitRecord( sal_uInt16 nRecId );
+ /** Rewrites correct record length, if different from calculated. */
+ void UpdateRecSize();
+ /** Recalculates mnCurrSize and mnSliceSize. */
+ void UpdateSizeVars( std::size_t nSize );
+ /** Writes CONTINUE header, internal setup. */
+ void StartContinue();
+ /** Refreshes counter vars, creates CONTINUE records. */
+ void PrepareWrite( sal_uInt16 nSize );
+ /** Creates CONTINUE record at end of record.
+ @return Maximum data block size remaining. */
+ sal_uInt16 PrepareWrite();
+
+ /** Writes a raw sequence of zero bytes. */
+ void WriteRawZeroBytes( std::size_t nBytes );
+
+private:
+ SvStream& mrStrm; /// Reference to the system output stream.
+ const XclExpRoot& mrRoot; /// Filter root data.
+
+ bool mbUseEncrypter;
+ XclExpEncrypterRef mxEncrypter;
+
+ // length data
+ sal_uInt16 mnMaxRecSize; /// Maximum size of record content.
+ sal_uInt16 mnMaxContSize; /// Maximum size of CONTINUE content.
+ sal_uInt16 mnCurrMaxSize; /// Current maximum, either mnMaxRecSize or mnMaxContSize.
+ sal_uInt16 mnMaxSliceSize; /// Maximum size of data slices (parts that cannot be split).
+ sal_uInt16 mnHeaderSize; /// Record size written in last record header.
+ sal_uInt16 mnCurrSize; /// Count of bytes already written in current record.
+ sal_uInt16 mnSliceSize; /// Count of bytes already written in current slice.
+ std::size_t mnPredictSize; /// Predicted size received from calling function.
+
+ // stream position data
+ std::size_t mnLastSizePos; /// Stream position of size field in current header.
+ bool mbInRec; /// true = currently writing inside of a record.
+};
+
+class XclExpBiff8Encrypter
+{
+public:
+ explicit XclExpBiff8Encrypter( const XclExpRoot& rRoot );
+ ~XclExpBiff8Encrypter();
+
+ bool IsValid() const { return mbValid; }
+
+ void GetSaltDigest( sal_uInt8 pnSaltDigest[16] ) const;
+ void GetSalt( sal_uInt8 pnSalt[16] ) const;
+ void GetDocId( sal_uInt8 pnDocId[16] ) const;
+
+ void Encrypt( SvStream& rStrm, sal_uInt8 nData );
+ void Encrypt( SvStream& rStrm, sal_uInt16 nData );
+ void Encrypt( SvStream& rStrm, sal_uInt32 nData );
+
+ void Encrypt( SvStream& rStrm, sal_Int8 nData );
+ void Encrypt( SvStream& rStrm, sal_Int16 nData );
+ void Encrypt( SvStream& rStrm, sal_Int32 nData );
+
+ void Encrypt( SvStream& rStrm, float fValue );
+ void Encrypt( SvStream& rStrm, double fValue );
+
+ void EncryptBytes( SvStream& rStrm, ::std::vector<sal_uInt8>& aBytes );
+
+private:
+ void Init( const css::uno::Sequence< css::beans::NamedValue >& aEncryptionData );
+
+ static sal_uInt32 GetBlockPos( std::size_t nStrmPos );
+ static sal_uInt16 GetOffsetInBlock( std::size_t nStrmPos );
+
+private:
+ ::msfilter::MSCodec_Std97 maCodec; /// Crypto algorithm implementation.
+ sal_uInt8 mpnDocId[16];
+ sal_uInt8 mpnSalt[16];
+ sal_uInt8 mpnSaltDigest[16];
+
+ sal_uInt64 mnOldPos; /// Last known stream position
+ bool mbValid;
+};
+
+// `s.GetChar(0) != 0` needed because some strings on export only contain NULL.
+#define XESTRING_TO_PSZ(s) \
+ (s.Len() && s.GetChar( 0 ) != 0 ? XclXmlUtils::ToOString( s ).getStr() : nullptr)
+
+class ScAddress;
+class ScDocShell;
+class ScFormulaCell;
+class ScRange;
+class ScRangeList;
+class ScTokenArray;
+struct XclAddress;
+struct XclFontData;
+class XclRangeList;
+namespace sc { class CompileFormulaContext; }
+
+class XclXmlUtils
+{
+public:
+ XclXmlUtils() = delete;
+ ~XclXmlUtils() = delete;
+ XclXmlUtils(const XclXmlUtils&) = delete;
+ XclXmlUtils& operator=(const XclXmlUtils&) = delete;
+
+ static void GetFormulaTypeAndValue( ScFormulaCell& rCell, const char*& sType, OUString& rValue);
+ static OUString GetStreamName( const char* sStreamDir, const char* sStream, sal_Int32 nId );
+
+ static OString ToOString( const Color& rColor );
+ static OString ToOString( const ScfUInt16Vec& rBuffer );
+ static OStringBuffer& ToOString( OStringBuffer& s, const ScAddress& rRange );
+ static OString ToOString( const ScDocument& rDoc, const ScRange& rRange, bool bFullAddressNotation = false );
+ static OString ToOString( const ScDocument& rDoc, const ScRangeList& rRangeList );
+ static OStringBuffer& ToOString( OStringBuffer& s, const XclAddress& rAddress );
+ static OString ToOString( const XclExpString& s );
+ static OString ToOString( const ScDocument& rDoc, const XclRangeList& rRangeList );
+
+ static OUString ToOUString( const char* s );
+ static OUString ToOUString( const ScfUInt16Vec& rBuffer, sal_Int32 nStart = 0, sal_Int32 nLength = -1 );
+ static OUString ToOUString( sc::CompileFormulaContext& rCtx, const ScAddress& rAddress,
+ const ScTokenArray* pTokenArray, FormulaError nErrCode = FormulaError::NONE );
+ static OUString ToOUString( const XclExpString& s );
+
+ template <class T>
+ static sax_fastparser::FSHelperPtr WriteElement(sax_fastparser::FSHelperPtr pStream, sal_Int32 nElement, const T& value)
+ {
+ pStream->startElement(nElement);
+ pStream->write(value);
+ pStream->endElement(nElement);
+
+ return pStream;
+ }
+ static sax_fastparser::FSHelperPtr WriteFontData( sax_fastparser::FSHelperPtr pStream, const XclFontData& rFontData, sal_Int32 nNameId );
+};
+
+class XclExpXmlStream : public oox::core::XmlFilterBase
+{
+public:
+ XclExpXmlStream( const css::uno::Reference< css::uno::XComponentContext >& rCC, bool bExportVBA, bool bExportTemplate );
+ virtual ~XclExpXmlStream() override;
+
+ /** Returns the filter root data. */
+ const XclExpRoot& GetRoot() const { return *mpRoot; }
+
+ sax_fastparser::FSHelperPtr& GetCurrentStream();
+ void PushStream( sax_fastparser::FSHelperPtr const & aStream );
+ void PopStream();
+
+ sax_fastparser::FSHelperPtr GetStreamForPath( const OUString& rPath );
+
+ template <typename Str, typename... Args>
+ void WriteAttributes(sal_Int32 nAttribute, Str&& value, Args&&... rest)
+ {
+ WriteAttribute(nAttribute, std::forward<Str>(value));
+ if constexpr(sizeof...(rest) > 0)
+ {
+ // coverity[stray_semicolon : FALSE] - coverity parse error
+ WriteAttributes(std::forward<Args>(rest)...);
+ }
+ }
+
+ sax_fastparser::FSHelperPtr CreateOutputStream (
+ const OUString& sFullStream,
+ std::u16string_view sRelativeStream,
+ const css::uno::Reference< css::io::XOutputStream >& xParentRelation,
+ const char* sContentType,
+ std::u16string_view sRelationshipType,
+ OUString* pRelationshipId = nullptr );
+
+ // ignore
+ virtual bool exportDocument() override;
+
+ // only needed for import; ignore
+ virtual bool importDocument() noexcept override;
+ virtual oox::vml::Drawing* getVmlDrawing() override;
+ virtual const oox::drawingml::Theme* getCurrentTheme() const override;
+ virtual oox::drawingml::table::TableStyleListPtr getTableStyles() override;
+ virtual oox::drawingml::chart::ChartConverter* getChartConverter() override;
+
+private:
+ virtual ::oox::ole::VbaProject* implCreateVbaProject() const override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ ScDocShell *getDocShell();
+ void WriteAttribute(sal_Int32 nAttr, std::u16string_view sVal);
+ void WriteAttribute(sal_Int32 nAttr, std::string_view sVal)
+ {
+ WriteAttribute(nAttr, OStringToOUString(sVal, RTL_TEXTENCODING_UTF8));
+ }
+ void WriteAttribute(sal_Int32 nAttr, const char* sVal)
+ {
+ if (sVal)
+ WriteAttribute(nAttr, OUString(sVal, strlen(sVal), RTL_TEXTENCODING_UTF8));
+ }
+
+ void validateTabNames(std::vector<OUString>& aOriginalTabNames);
+ void restoreTabNames(const std::vector<OUString>& aOriginalTabNames);
+ void renameTab(SCTAB aTab, OUString aNewName);
+
+ typedef std::map< OUString,
+ std::pair< OUString,
+ sax_fastparser::FSHelperPtr > > XclExpXmlPathToStateMap;
+
+ const XclExpRoot* mpRoot;
+ std::stack< sax_fastparser::FSHelperPtr > maStreams;
+ XclExpXmlPathToStateMap maOpenedStreamMap;
+
+ bool mbExportVBA;
+ bool mbExportTemplate;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xestring.hxx b/sc/source/filter/inc/xestring.hxx
new file mode 100644
index 000000000..a61e563f0
--- /dev/null
+++ b/sc/source/filter/inc/xestring.hxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "xlstring.hxx"
+#include "ftools.hxx"
+
+class XclExpStream;
+class XclExpXmlStream;
+
+/** This class stores an unformatted or formatted string for Excel export.
+
+ The class supports two completely different types of Excel strings:
+ 1) BIFF2-BIFF7 byte strings: The text is encoded as a 8-bit character
+ array. The strings cannot contain any character formatting.
+ 2) BIFF8 Unicode strings: The text may be stored as UCS-2 character array,
+ or compressed to an 8-bit array, if all characters are less than
+ U+0100. Unicode strings may contain a formatting array, that specifies
+ the used FONT record for different ranges of characters.
+
+ The class provides full support for NUL characters in strings. On
+ construction or assignment the passed flags specify the behaviour of the
+ string while it is written to a stream (the 'Write' functions and
+ 'operator<<').
+ */
+class XclExpString
+{
+public:
+ // constructors -----------------------------------------------------------
+
+ /** Constructs an empty BIFF8 Unicode string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string. */
+ explicit XclExpString(
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Constructs an unformatted BIFF8 Unicode string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string. */
+ explicit XclExpString(
+ const OUString& rString,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ // assign -----------------------------------------------------------------
+
+ /** Assigns an unformatted string, converts this object to a BIFF8 Unicode string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string. */
+ void Assign(
+ const OUString& rString,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ /** Assigns a Unicode character, converts this object to a BIFF8 Unicode string. */
+ void Assign( sal_Unicode cChar );
+
+ /** Assigns an unformatted string, converts this object to a BIFF2-BIFF7 byte string.
+ @param nFlags Modifiers for string export.
+ @param nMaxLen The maximum number of characters to store in this string. */
+ void AssignByte(
+ std::u16string_view rString,
+ rtl_TextEncoding eTextEnc,
+ XclStrFlags nFlags = XclStrFlags::NONE,
+ sal_uInt16 nMaxLen = EXC_STR_MAXLEN );
+
+ // append -----------------------------------------------------------------
+
+ /** Appends a string. Uses the string flags used in constructor or last Assign().
+ @descr This object must be a BIFF8 Unicode string. */
+ void Append( std::u16string_view rString );
+
+ /** Appends a string. Uses the string flags used in constructor or last Assign().
+ @descr This object must be a BIFF2-BIFF7 byte string. */
+ void AppendByte( std::u16string_view rString, rtl_TextEncoding eTextEnc );
+ /** Appends a character. Uses the string flags used in constructor or last Assign().
+ @descr This object must be a BIFF2-BIFF7 byte string. */
+ void AppendByte( sal_Unicode cChar, rtl_TextEncoding eTextEnc );
+
+ // formatting runs --------------------------------------------------------
+
+ /** Appends a formatting run. nChar must be greater than last contained character index. */
+ void AppendFormat( sal_uInt16 nChar, sal_uInt16 nFontIdx, bool bDropDuplicate = true );
+ /** Appends a trailing formatting run with the passed font index. */
+ void AppendTrailingFormat( sal_uInt16 nFontIdx );
+ /** Removes formatting runs at the end, if the string contains too much. */
+ void LimitFormatCount( sal_uInt16 nMaxCount );
+ /** Returns the font index of the first char in the formatting run, or EXC_FONT_NOTFOUND. */
+ sal_uInt16 GetLeadingFont();
+ /** The same as above + additionally remove the given font from the formatting run*/
+ sal_uInt16 RemoveLeadingFont();
+
+ // get data ---------------------------------------------------------------
+
+ /** Returns the character count of the string. */
+ sal_uInt16 Len() const { return mnLen; }
+ /** Returns true, if the string is empty. */
+ bool IsEmpty() const { return mnLen == 0; }
+ /** Returns true, if the string contains line breaks. */
+ bool IsWrapped() const { return mbWrapped; }
+ /** Returns true, if this string is equal to the passed string. */
+ bool IsEqual( const XclExpString& rCmp ) const;
+ /** Returns true, if this string is less than the passed string. */
+ bool IsLessThan( const XclExpString& rCmp ) const;
+
+ /** Returns true, if the string contains formatting information. */
+ bool IsRich() const { return !maFormats.empty(); }
+ /** Returns the current count of formatting runs for rich strings. */
+ sal_uInt16 GetFormatsCount() const;
+ /** Returns the vector with all formatting runs. */
+ const XclFormatRunVec& GetFormats() const { return maFormats; }
+
+ /** Returns the current string flags field to export. */
+ sal_uInt8 GetFlagField() const;
+ /** Returns the byte count the header will take on export. */
+ sal_uInt16 GetHeaderSize() const;
+ /** Returns the byte count the character buffer will take on export. */
+ std::size_t GetBufferSize() const;
+ /** Returns the byte count the whole string will take on export. */
+ std::size_t GetSize() const;
+
+ /** Returns the specified character from the (already encoded) string. */
+ sal_uInt16 GetChar( sal_uInt16 nCharIdx ) const;
+ /** Returns a hash value for the string. */
+ sal_uInt16 GetHash() const;
+
+ const ScfUInt16Vec& GetUnicodeBuffer() const { return maUniBuffer; }
+
+ // streaming --------------------------------------------------------------
+
+ /** Writes the string length field (1 byte or 2 bytes). */
+ void WriteLenField( XclExpStream& rStrm ) const;
+ /** Writes the string flags field (1 byte). */
+ void WriteFlagField( XclExpStream& rStrm ) const;
+ /** Writes 8-bit or 16-bit length field and string flags field. */
+ void WriteHeader( XclExpStream& rStrm ) const;
+ /** Writes the raw character buffer. */
+ void WriteBuffer( XclExpStream& rStrm ) const;
+ /** Writes the raw formatting run buffer. */
+ void WriteFormats( XclExpStream& rStrm, bool bWriteSize = false ) const;
+ /** Writes the complete Unicode string. */
+ void Write( XclExpStream& rStrm ) const;
+
+ /** Writes the string header to memory. */
+ void WriteHeaderToMem( sal_uInt8* pnMem ) const;
+ /** Writes the raw character buffer to memory (8-bit or 16-bit little-endian). */
+ void WriteBufferToMem( sal_uInt8* pnMem ) const;
+ /** Writes the entire string to memory. */
+ void WriteToMem( sal_uInt8* pnMem ) const;
+
+ void WriteXml( XclExpXmlStream& rStrm ) const;
+
+private:
+ /** Returns true, if the flag field should be written. */
+ bool IsWriteFlags() const;
+ /** Returns true, if the formatting run vector should be written. */
+ bool IsWriteFormats() const;
+
+ /** Sets the string length but regards the limit given in mnMaxLen. */
+ void SetStrLen( sal_Int32 nNewLen );
+ /** Inserts the passed character array into the internal character buffer.
+ @param nBegin First index in internal buffer to fill.
+ @param nLen Number of characters to insert. */
+ void CharsToBuffer( const sal_Unicode* pcSource, sal_Int32 nBegin, sal_Int32 nLen );
+ /** Inserts the passed character array into the internal character buffer.
+ @param nBegin First index in internal buffer to fill.
+ @param nLen Number of characters to insert. */
+ void CharsToBuffer( const char* pcSource, sal_Int32 nBegin, sal_Int32 nLen );
+
+ /** Initializes flags, string length, and resizes character buffer.
+ @param nFlags Modifiers for string export.
+ @param nCurrLen The requested number of characters for the string.
+ @param nMaxLen The maximum length allowed of the resulting string.
+ @param bBiff8 true = BIFF8 Unicode string; false = BIFF2-BIFF7 byte string. */
+ void Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMaxLen, bool bBiff8 );
+ /** Creates the character buffer from the given Unicode array.
+ @param pcSource The source character buffer. Trailing NUL character is not necessary.
+ @param nFlags Modifiers for string export.
+ @param nCurrLen The real count of characters contained in the passed buffer.
+ @param nMaxLen The maximum length allowed of the resulting string. */
+ void Build(
+ const sal_Unicode* pcSource, sal_Int32 nCurrLen,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen );
+ /** Creates the character buffer from the given character array.
+ @param pcSource The source character buffer. Trailing NUL character is not necessary.
+ @param nFlags Modifiers for string export.
+ @param nCurrLen The real count of characters contained in the passed buffer.
+ @param nMaxLen The maximum length allowed of the resulting string. */
+ void Build(
+ const char* pcSource, sal_Int32 nCurrLen,
+ XclStrFlags nFlags, sal_uInt16 nMaxLen );
+
+ /** Initializes string length and resizes character buffers for appending operation.
+ @param nAddLen The number of characters to be appended. */
+ void InitAppend( sal_Int32 nAddLen );
+ /** Appends the given Unicode array to the character buffer.
+ @param pcSource The source character buffer. Trailing NUL character is not necessary. */
+ void BuildAppend( std::u16string_view );
+ /** Appends the given character array to the character buffer.
+ @param pcSource The source character buffer. Trailing NUL character is not necessary. */
+ void BuildAppend( std::string_view );
+
+ /** Initializes write process on stream. */
+ void PrepareWrite( XclExpStream& rStrm, sal_uInt16 nBytes ) const;
+
+private:
+ ScfUInt16Vec maUniBuffer; /// The Unicode character buffer.
+ ScfUInt8Vec maCharBuffer; /// The byte character buffer.
+ XclFormatRunVec maFormats; /// All formatting runs.
+ sal_uInt16 mnLen; /// Character count to export.
+ sal_uInt16 mnMaxLen; /// Maximum allowed number of characters.
+ bool mbIsBiff8; /// true = BIFF8 Unicode string, false = BIFF2-7 bytestring.
+ bool mbIsUnicode; /// true, if at least one character is >0xFF.
+ bool mb8BitLen; /// true = write 8-bit string length; false = 16-bit.
+ bool mbSmartFlags; /// true = omit flags on empty string; false = always write flags.
+ bool mbSkipFormats; /// true = skip formats on export; false = write complete formatted string.
+ bool mbWrapped; /// true = text contains several paragraphs.
+ bool mbSkipHeader; /// true = skip length and flags when writing string bytes.
+};
+
+inline bool operator==( const XclExpString& rLeft, const XclExpString& rRight )
+{
+ return rLeft.IsEqual( rRight );
+}
+
+inline bool operator!=( const XclExpString& rLeft, const XclExpString& rRight )
+{
+ return !(rLeft == rRight);
+}
+
+inline bool operator<( const XclExpString& rLeft, const XclExpString& rRight )
+{
+ return rLeft.IsLessThan( rRight );
+}
+
+inline XclExpStream& operator<<( XclExpStream& rStrm, const XclExpString& rString )
+{
+ rString.Write( rStrm );
+ return rStrm;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xestyle.hxx b/sc/source/filter/inc/xestyle.hxx
new file mode 100644
index 000000000..26ba9fcf8
--- /dev/null
+++ b/sc/source/filter/inc/xestyle.hxx
@@ -0,0 +1,773 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <svl/zforlist.hxx>
+#include <svl/nfkeytab.hxx>
+#include <editeng/svxfont.hxx>
+#include "xerecord.hxx"
+#include "xlstyle.hxx"
+#include "xeroot.hxx"
+#include <fonthelper.hxx>
+#include <memory>
+#include <vector>
+
+/* ============================================================================
+- Buffers for style records (PALETTE, FONT, FORMAT, XF, STYLE).
+============================================================================ */
+
+const sal_uInt16 EXC_ID_FONTLIST = 0x8031; /// For internal use only.
+const sal_uInt16 EXC_ID_FORMATLIST = 0x801E; /// For internal use only.
+const sal_uInt16 EXC_ID_XFLIST = 0x8043; /// For internal use only.
+const sal_uInt16 EXC_ID_DXFS = 0x9999; /// For internal use only. TODO:moggi: find a better/correct value
+
+// PALETTE record - color information =========================================
+
+/** Different types of colors in a document. */
+enum XclExpColorType
+{
+ EXC_COLOR_CELLTEXT, /// Text in a cell.
+ EXC_COLOR_CELLBORDER, /// Border of a cell.
+ EXC_COLOR_CELLAREA, /// Background area of a cell.
+ EXC_COLOR_CHARTTEXT, /// Text color in a chart.
+ EXC_COLOR_CHARTLINE, /// Line in a chart.
+ EXC_COLOR_CHARTAREA, /// Area in a chart.
+ EXC_COLOR_CTRLTEXT, /// Text color in a form control.
+ EXC_COLOR_GRID, /// Spreadsheet grid color.
+ EXC_COLOR_TABBG /// Spreadsheet tab bg color.
+};
+
+class XclExpPaletteImpl;
+
+/** Stores all used colors in the document.
+
+ Supports color reduction to the maximum count of the current BIFF version.
+ An instance of this class collects all colors in the conversion phase of
+ the export, using the InsertColor() function. It returns a unique
+ identifier for each passed color.
+
+ After the entire document is converted, the Finalize() function will reduce
+ the palette to the number of colors supported by the current BIFF version.
+
+ Then, in the streaming phase, the functions GetColorIndex() and
+ GetMixedColors() return the real Excel palette index for all color
+ identifiers.
+ */
+class XclExpPalette : public XclDefaultPalette, public XclExpRecord
+{
+public:
+ explicit XclExpPalette( const XclExpRoot& rRoot );
+ virtual ~XclExpPalette() override;
+
+ /** Inserts the color into the list and updates weighting.
+ @param nAutoDefault The Excel palette index for automatic color.
+ @return A unique ID for this color. */
+ sal_uInt32 InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault = 0 );
+ /** Returns the color ID representing a fixed Excel palette index (i.e. for auto colors). */
+ static sal_uInt32 GetColorIdFromIndex( sal_uInt16 nIndex );
+
+ /** Reduces the color list to the maximum count of the current BIFF version. */
+ void Finalize();
+
+ /** Returns the Excel palette index of the color with passed color ID. */
+ sal_uInt16 GetColorIndex( sal_uInt32 nColorId ) const;
+
+ /** Returns a foreground and background color for the two passed color IDs.
+ @descr If rnXclPattern contains a solid pattern, this function tries to find
+ the two best fitting colors and a mix pattern (25%, 50% or 75%) for nForeColorId.
+ This will result in a better approximation to the passed foreground color. */
+ void GetMixedColors(
+ sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
+ sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const;
+
+ /** Returns the color for a (non-zero-based) Excel palette entry.
+ @return The color from current or default palette or COL_AUTO, if nothing else found. */
+ Color GetColor( sal_uInt16 nXclIndex ) const;
+
+ /** Saves the PALETTE record, if it differs from the default palette. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the PALETTE record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ typedef std::shared_ptr< XclExpPaletteImpl > XclExpPaletteImplRef;
+ XclExpPaletteImplRef mxImpl;
+};
+
+// FONT record - font information =============================================
+
+const size_t EXC_FONTLIST_NOTFOUND = static_cast< size_t >( -1 );
+
+/** Helper functions for font export. */
+namespace XclExpFontHelper
+{
+ /** Returns the script type of the first font item found in the item set and its parents. */
+ sal_Int16 GetFirstUsedScript(
+ const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet );
+
+ /** Returns a VCL font object filled from the passed item set. */
+ vcl::Font GetFontFromItemSet(
+ const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet,
+ sal_Int16 nScript );
+
+ /**
+ * Get a dxf related font object from the item set.
+ * Only items that are explicitly set in the item set
+ * are also set in the returned object.
+ */
+ ScDxfFont GetDxfFontFromItemSet(const XclExpRoot& rRoot, const SfxItemSet& rSet);
+
+ /** Returns true, if at least one font related item is set in the passed item set.
+ @param bDeep true = Searches in parent item sets too. */
+ bool CheckItems(
+ const XclExpRoot& rRoot,
+ const SfxItemSet& rItemSet,
+ sal_Int16 nScript,
+ bool bDeep );
+}
+
+/** Stores all data of an Excel font and provides export of FONT records. */
+class XclExpFont : public XclExpRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpFont( const XclExpRoot& rRoot,
+ const XclFontData& rFontData, XclExpColorType eColorType );
+
+ /** Returns read-only access to font data. */
+ const XclFontData& GetFontData() const { return maData; }
+ /** Returns the font color identifier. */
+ sal_uInt32 GetFontColorId() const { return mnColorId; }
+ /** Compares this font with the passed font data.
+ @param nHash The hash value calculated from the font data. */
+ virtual bool Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the FONT record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclFontData maData; /// All font attributes.
+ sal_uInt32 mnColorId; /// Unique color ID for text color.
+ sal_uInt32 mnHash; /// Hash value for fast comparison.
+};
+
+class XclExpDxfFont : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpDxfFont(const XclExpRoot& rRoot, const SfxItemSet& rItemSet);
+
+ virtual void SaveXml(XclExpXmlStream& rStrm) override;
+private:
+
+ ScDxfFont maDxfData;
+};
+
+/** Used as placeholder for font index 4, which is not used in Excel. */
+class XclExpBlindFont : public XclExpFont
+{
+public:
+ explicit XclExpBlindFont( const XclExpRoot& rRoot );
+
+ /** Returns always false to never find this font while searching the font list. */
+ virtual bool Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const override;
+
+ /** Skips writing this record. */
+ virtual void Save( XclExpStream& rStrm ) override;
+};
+
+class ScPatternAttr;
+
+/** Stores the data of all fonts used in the document. */
+class XclExpFontBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpFontBuffer( const XclExpRoot& rRoot );
+
+ /** Returns the specified font from font list. */
+ const XclExpFont* GetFont( sal_uInt16 nXclFont ) const;
+ /** Returns the application font data of this file, needed e.g. for column width. */
+ const XclFontData& GetAppFontData() const;
+
+ /** Inserts a new font with the passed font data into the buffer if not present.
+ @param bAppFont true = Sets the application font; false = Inserts a new font.
+ @return The resulting Excel font index. */
+ sal_uInt16 Insert( const XclFontData& rFontData,
+ XclExpColorType eColorType, bool bAppFont = false );
+ /** Inserts the SvxFont into the buffer if not present, e.g. where escapements are used.
+ @return The resulting Excel font index. */
+ sal_uInt16 Insert( const SvxFont& rFont,
+ XclExpColorType eColorType );
+ /** Inserts the font contained in the passed item set into the buffer, if not present.
+ @param nScript The script type of the font properties to be used.
+ @param bAppFont true = Sets the application font; false = Inserts a new font.
+ @return The resulting Excel font index. */
+ sal_uInt16 Insert( const SfxItemSet& rItemSet, sal_Int16 nScript,
+ XclExpColorType eColorType, bool bAppFont );
+
+ /** Writes all FONT records contained in this buffer. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Initializes the default fonts for the current BIFF version. */
+ void InitDefaultFonts();
+ /** Tries to find the passed font and returns the current list index. */
+ size_t Find( const XclFontData& rFontData );
+
+private:
+ typedef XclExpRecordList< XclExpFont > XclExpFontList;
+ typedef XclExpFontList::RecordRefType XclExpFontRef;
+
+ XclExpFontList maFontList; /// List of all FONT records.
+ size_t mnXclMaxSize; /// Maximum number of fonts.
+};
+
+// FORMAT record - number formats =============================================
+
+/** Stores a core number format index with corresponding Excel format index. */
+struct XclExpNumFmt
+{
+ sal_uInt32 mnScNumFmt; /// Core index of the number format.
+ sal_uInt16 mnXclNumFmt; /// Resulting Excel format index.
+ OUString maNumFmtString; /// format string
+
+ explicit XclExpNumFmt( sal_uInt32 nScNumFmt, sal_uInt16 nXclNumFmt, const OUString& rFrmt ) :
+ mnScNumFmt( nScNumFmt ), mnXclNumFmt( nXclNumFmt ), maNumFmtString( rFrmt ) {}
+
+ void SaveXml( XclExpXmlStream& rStrm );
+};
+
+typedef ::std::unique_ptr< SvNumberFormatter > SvNumberFormatterPtr;
+
+/** Stores all number formats used in the document. */
+class XclExpNumFmtBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpNumFmtBuffer( const XclExpRoot& rRoot );
+ virtual ~XclExpNumFmtBuffer() override;
+
+ /** Returns the core index of the current standard number format. */
+ sal_uInt32 GetStandardFormat() const { return mnStdFmt; }
+
+ /** Inserts a number format into the format buffer.
+ @param nScNumFmt The core index of the number format.
+ @return The resulting Excel format index. */
+ sal_uInt16 Insert( sal_uInt32 nScNumFmt );
+
+ /** Writes all FORMAT records contained in this buffer. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ OUString GetFormatCode ( sal_uInt32 nScNumFmt );
+
+private:
+ /** Writes the FORMAT record with index nXclIx and format string rFormatStr. */
+ void WriteFormatRecord( XclExpStream& rStrm, sal_uInt16 nXclNumFmt, const OUString& rFormatStr );
+ /** Writes the FORMAT record represented by rFormat. */
+ void WriteFormatRecord( XclExpStream& rStrm, const XclExpNumFmt& rFormat );
+
+private:
+ typedef ::std::vector< XclExpNumFmt > XclExpNumFmtVec;
+
+ SvNumberFormatterPtr mxFormatter; /// Special number formatter for conversion.
+ XclExpNumFmtVec maFormatMap; /// Maps core formats to Excel indexes.
+ std::unique_ptr<NfKeywordTable> mpKeywordTable; /// Replacement table.
+ sal_uInt32 mnStdFmt; /// Key for standard number format.
+ sal_uInt16 mnXclOffset; /// Offset to first user defined format.
+};
+
+// XF, STYLE record - Cell formatting =========================================
+
+/** Extends the XclCellProt struct for export.
+ @descr Provides functions to fill from item sets and to fill to Excel record data. */
+struct XclExpCellProt : public XclCellProt
+{
+ /** Fills the protection attributes from the passed item set.
+ @return true = At least one protection item is set. */
+ bool FillFromItemSet( const SfxItemSet& rItemSet, bool bStyle = false );
+
+ /** Fills the data to the passed fields of a BIFF3-BIFF8 XF record. */
+ void FillToXF3( sal_uInt16& rnProt ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+/** Extends the XclCellAlign struct for export.
+ @descr Provides functions to fill from item sets and to fill to Excel record data. */
+struct XclExpCellAlign : public XclCellAlign
+{
+ /** Fills the alignment attributes from the passed item set.
+ @descr Fills only the attributes exported in the passed BIFF version.
+ @param bForceLineBreak true = Set line break flag unconditionally.
+ @return true = At least one alignment item is set. */
+ bool FillFromItemSet(const XclRoot& rRoot, const SfxItemSet& rItemSet,
+ bool bForceLineBreak, XclBiff eBiff, bool bStyle = false );
+
+ /** Fills the data to the passed fields of a BIFF5/BIFF7 XF record. */
+ void FillToXF5( sal_uInt16& rnAlign ) const;
+ /** Fills the data to the passed fields of a BIFF8 XF record. */
+ void FillToXF8( sal_uInt16& rnAlign, sal_uInt16& rnMiscAttrib ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+/** Extends the XclCellBorder struct for export.
+ @descr Provides functions to fill from item sets and to fill to Excel record data. */
+struct XclExpCellBorder : public XclCellBorder
+{
+ sal_uInt32 mnLeftColorId; /// Color ID for left line.
+ sal_uInt32 mnRightColorId; /// Color ID for right line.
+ sal_uInt32 mnTopColorId; /// Color ID for top line.
+ sal_uInt32 mnBottomColorId; /// Color ID for bottom line.
+ sal_uInt32 mnDiagColorId; /// Color ID for diagonal line(s).
+
+ explicit XclExpCellBorder();
+
+ /** Fills the border attributes from the passed item set.
+ @descr Fills only the attributes exported in the passed BIFF version.
+ @return true = At least one border item is set. */
+ bool FillFromItemSet( const SfxItemSet& rItemSet,
+ XclExpPalette& rPalette, XclBiff eBiff, bool bStyle = false );
+ /** Fills the mn***Color base members from the mn***ColorId members. */
+ void SetFinalColors( const XclExpPalette& rPalette );
+
+ /** Fills the data to the passed fields of a BIFF5/BIFF7 XF record. */
+ void FillToXF5( sal_uInt32& rnBorder, sal_uInt32& rnArea ) const;
+ /** Fills the data to the passed fields of a BIFF8 XF record. */
+ void FillToXF8( sal_uInt32& rnBorder1, sal_uInt32& rnBorder2 ) const;
+
+ /** Fills the data to the passed fields of a BIFF8 CF (conditional format) record. */
+ void FillToCF8( sal_uInt16& rnLine, sal_uInt32& rnColor ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+/** Extends the XclCellArea struct for export.
+ @descr Provides functions to fill from item sets and to fill to Excel record data. */
+struct XclExpCellArea : public XclCellArea
+{
+ sal_uInt32 mnForeColorId; /// Foreground color ID.
+ sal_uInt32 mnBackColorId; /// Background color ID.
+ Color maForeColor; // Actual foreground color
+ Color maBackColor; // Actual background color
+
+ explicit XclExpCellArea();
+ explicit XclExpCellArea(Color aForeColor, Color aBackColor);
+
+ /** Fills the area attributes from the passed item set.
+ @return true = At least one area item is set. */
+ bool FillFromItemSet(
+ const SfxItemSet& rItemSet, XclExpPalette& rPalette,
+ bool bStyle );
+ /** Fills the mn***Color base members from the mn***ColorId members. */
+ void SetFinalColors( const XclExpPalette& rPalette );
+
+ /** Fills the data to the passed fields of a BIFF5/BIFF7 XF record. */
+ void FillToXF5( sal_uInt32& rnArea ) const;
+ /** Fills the data to the passed fields of a BIFF8 XF record. */
+ void FillToXF8( sal_uInt32& rnBorder2, sal_uInt16& rnArea ) const;
+
+ /** Fills the data to the passed fields of a BIFF8 CF (conditional format) record. */
+ void FillToCF8( sal_uInt16& rnPattern, sal_uInt16& rnColor ) const;
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+struct XclExpColor
+{
+ Color maColor;
+
+ bool FillFromItemSet( const SfxItemSet& rItemSet );
+
+ void SaveXml( XclExpXmlStream& rStrm ) const;
+};
+
+/** A combination of unique XF identifier with real Excel XF index. */
+struct XclExpXFId
+{
+ sal_uInt32 mnXFId; /// Temporary XF identifier.
+ sal_uInt16 mnXFIndex; /// Real Excel XF index.
+
+ explicit XclExpXFId();
+ explicit XclExpXFId( sal_uInt32 nXFId )
+ : mnXFId( nXFId ), mnXFIndex( EXC_XF_DEFAULTCELL ) {}
+
+ /** Converts the XF identifier in mnXFId to an Excel XF index and stores it in mnXFIndex. */
+ void ConvertXFIndex( const XclExpRoot& rRoot );
+};
+
+class SfxStyleSheetBase;
+
+/** Represents an XF record which contains all formatting data of a cell or cell style. */
+class XclExpXF : public XclXFBase, public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Constructs a cell XF record from the passed Calc cell formatting. */
+ explicit XclExpXF(
+ const XclExpRoot& rRoot,
+ const ScPatternAttr& rPattern,
+ sal_Int16 nScript,
+ sal_uInt32 nScForceNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND,
+ sal_uInt16 nForceXclFont = EXC_FONT_NOTFOUND,
+ bool bForceLineBreak = false );
+ /** Constructs a style XF record from the passed cell style sheet. */
+ explicit XclExpXF(
+ const XclExpRoot& rRoot,
+ const SfxStyleSheetBase& rStyleSheet );
+
+ /** Returns the cell protection settings of this XF. */
+ const XclExpCellProt& GetProtectionData() const { return maProtection; }
+ /** Returns the alignment settings of this XF. */
+ const XclExpCellAlign& GetAlignmentData() const { return maAlignment; }
+ /** Returns the cell border settings of this XF. */
+ const XclExpCellBorder& GetBorderData() const { return maBorder; }
+ /** Returns the cell fill settings of this XF. */
+ const XclExpCellArea& GetAreaData() const { return maArea; }
+
+ /** Returns true, if this XF record represents the passed cell formatting.
+ @descr Searches for cell XFs only. */
+ bool Equals(
+ const ScPatternAttr& rPattern,
+ sal_uInt32 nScForceNumFmt,
+ sal_uInt16 nForceXclFont,
+ bool bForceLineBreak ) const;
+
+ /** Returns true, if this XF record represents the passed style.
+ @descr Searches for style XFs only. */
+ bool Equals( const SfxStyleSheetBase& rStyleSheet ) const;
+
+ /** Sets the resulting Excel palette index from all used color IDs (border and area). */
+ void SetFinalColors();
+
+ /** Returns true, if this XF record is completely equal to the passed. */
+ bool Equals( const XclExpXF& rCmpXF ) const;
+
+ void SetXmlIds( sal_uInt32 nBorderId, sal_uInt32 nFillId );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ const SfxItemSet* GetItemSet() const { return mpItemSet; }
+
+ sal_uInt32 GetScNumFmt() const { return mnScNumFmt; }
+ sal_uInt16 GetXclFont() const { return mnXclFont; }
+
+protected:
+ explicit XclExpXF( const XclExpRoot& rRoot, bool bCellXF );
+
+protected: // access for XclExpDefaultXF
+ const SfxItemSet* mpItemSet; /// Pointer to the item set (we do not own it).
+
+ XclExpCellProt maProtection; /// Cell protection flags.
+ XclExpCellAlign maAlignment; /// All alignment attributes.
+ XclExpCellBorder maBorder; /// Border line style.
+ XclExpCellArea maArea; /// Background area style.
+ sal_uInt32 mnParentXFId; /// XF ID of parent XF record.
+ sal_uInt32 mnScNumFmt; /// Calc number format index.
+ sal_uInt16 mnXclFont; /// Excel font index.
+ sal_uInt16 mnXclNumFmt; /// Excel number format index.
+ sal_Int32 mnBorderId; /// OOXML Border Index
+ sal_Int32 mnFillId; /// OOXML Fill Index
+
+private:
+ using XclXFBase::Equals;
+
+ /** Initializes with default values. */
+ void InitDefault();
+ /** Fills all members from the passed item set.
+ @param bDefStyle true = This is the "Default"/"Normal" style - needs special handling. */
+ void Init(
+ const SfxItemSet& rItemSet,
+ sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt,
+ sal_uInt16 nForceXclFont,
+ bool bForceLineBreak,
+ bool bDefStyle );
+
+ /** Returns the bits specifying the used attributes.
+ @descr In cell XFs a set bit means a used attribute, in style XF a cleared
+ bit means a used attribute. This method regards the cell/style state.
+ @return The mask based on bit 0 (not yet bit-shifted as needed for export). */
+ sal_uInt8 GetUsedFlags() const;
+
+ void WriteBody5( XclExpStream& rStrm );
+ void WriteBody8( XclExpStream& rStrm );
+
+ /** Writes the contents of the XF record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+};
+
+/** Represents a default XF record. Supports methods to set attributes directly. */
+class XclExpDefaultXF : public XclExpXF
+{
+public:
+ explicit XclExpDefaultXF( const XclExpRoot& rRoot, bool bCellXF );
+
+ /** Sets the Excel font index. */
+ void SetFont( sal_uInt16 nXclFont );
+ /** Sets the Excel number format index. */
+ void SetNumFmt( sal_uInt16 nXclNumFmt );
+};
+
+/** Represents a STYLE record containing the data of a cell style.
+ @descr The class is able to store built-in and user-defined styles. */
+class XclExpStyle : public XclExpRecord
+{
+public:
+ explicit XclExpStyle( sal_uInt32 nXFId, const OUString& rStyleName );
+ explicit XclExpStyle( sal_uInt32 nXFId, sal_uInt8 nStyleId, sal_uInt8 nLevel );
+
+ /** Returns true, if this record represents an Excel built-in style. */
+ bool IsBuiltIn() const { return mnStyleId != EXC_STYLE_USERDEF; }
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the STYLE record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ OUString maName; /// Name of the cell style.
+ XclExpXFId maXFId; /// XF identifier for style formatting.
+ sal_uInt8 mnStyleId; /// Built-in style identifier.
+ sal_uInt8 mnLevel; /// Outline level for RowLevel and ColLevel styles.
+};
+
+/** Stores all XF records (cell formats and cell styles) in the document.
+
+ Stores also the names of user defined cell styles (STYLE records). Supports
+ reduction to the maximum count of XF records of the current BIFF version.
+
+ An instance of this class collects all XF records in the conversion phase
+ of the export, using the Insert() and InsertStyle() functions. It returns a
+ unique identifier for each XF record.
+
+ After the entire document is converted, the Finalize() function will reduce
+ the list to the number of XF records supported by the current BIFF version.
+
+ Then, in the streaming phase, the function GetXFIndex() returns the real
+ Excel XF index for all XF identifiers.
+ */
+class XclExpXFBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpXFBuffer( const XclExpRoot& rRoot );
+
+ /** Inserts predefined built-in styles and user-defined styles. */
+ void Initialize();
+
+ /** Finds or creates a cell XF record for the passed item set.
+ @return A unique XF record ID. */
+ sal_uInt32 Insert( const ScPatternAttr* pPattern, sal_Int16 nScript );
+ /** Finds or creates a cell XF record for the passed item set.
+ @param nForceXclFont The font to be exported. If not equal to EXC_FONT_NOTFOUND,
+ this font index will be used unconditionally and the cell font will be ignored.
+ @param bForceLineBreak true = Set line break flag unconditionally.
+ This is required for cells that contain multi-line text.
+ @return A unique XF record ID. */
+ sal_uInt32 InsertWithFont(
+ const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt16 nForceXclFont,
+ bool bForceLineBreak );
+ /** Finds or creates a cell XF record for the passed item set, with custom number format.
+ @param nXFFlags Additional flags allowing to control the creation of an XF.
+ @param nForceScNumFmt The number format to be exported, e.g. formula
+ result type. This format will always overwrite the cell's number format.
+ @param bForceLineBreak true = Set line break flag unconditionally.
+ This is required for cells that contain multi-line text.
+ @return A unique XF record ID. */
+ sal_uInt32 InsertWithNumFmt(
+ const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt,
+ bool bForceLineBreak );
+ /** Inserts the passed cell style. Creates a style XF record and a STYLE record.
+ @return A unique XF record ID. */
+ sal_uInt32 InsertStyle( const SfxStyleSheetBase* pStyleSheet );
+ /** Returns the XF identifier representing a fixed Excel XF index (e.g. for built-in XFs). */
+ static sal_uInt32 GetXFIdFromIndex( sal_uInt16 nXFIndex );
+ /** Returns the XF identifier representing the default cell XF. */
+ static sal_uInt32 GetDefCellXFId();
+
+ /** Returns an XF record by its unique identifier. */
+ const XclExpXF* GetXFById( sal_uInt32 nXFId ) const;
+
+ /** Reduces the XF record list to the maximum allowed number of records. */
+ void Finalize();
+
+ /** Returns the Excel XF index of the XF record with passed XF ID. */
+ sal_uInt16 GetXFIndex( sal_uInt32 nXFId ) const;
+
+ sal_Int32 GetXmlStyleIndex( sal_uInt32 nXFId ) const;
+ sal_Int32 GetXmlCellIndex( sal_uInt32 nXFId ) const;
+
+ /** Writes all XF records contained in this buffer. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpXF > XclExpXFList;
+ typedef XclExpXFList::RecordRefType XclExpXFRef;
+
+private:
+ /** Returns the XF ID of the cell XF containing the passed format. */
+ sal_uInt32 FindXF( const ScPatternAttr& rPattern, sal_uInt32 nForceScNumFmt,
+ sal_uInt16 nForceXclFont, bool bForceLineBreak ) const;
+ /** Returns the XF ID of the style XF containing the passed style. */
+ sal_uInt32 FindXF( const SfxStyleSheetBase& rStyleSheet ) const;
+
+ /** Returns the XF ID of a built-in style XF, searches by style identifier. */
+ sal_uInt32 FindBuiltInXF( sal_uInt8 nStyleId, sal_uInt8 nLevel ) const;
+
+ /** Tries to find the XF record containing the passed format or inserts a new record.
+ @return The XF record ID. */
+ sal_uInt32 InsertCellXF( const ScPatternAttr* pPattern, sal_Int16 nScript,
+ sal_uInt32 nForceScNumFmt,
+ sal_uInt16 nForceXclFont, bool bForceLineBreak );
+ /** Inserts the passed cell style. Creates a style XF record and a STYLE record.
+ @return The XF record ID. */
+ sal_uInt32 InsertStyleXF( const SfxStyleSheetBase& rStyleSheet );
+
+ /** Inserts an XF and a STYLE record for all user defined style sheets. */
+ void InsertUserStyles();
+
+ /** Inserts a built-in XF record without a STYLE record and returns the XF ID.
+ @param bCreateStyleRec true = Creates the related STYLE record. */
+ sal_uInt32 AppendBuiltInXF( XclExpXFRef const & xXF,
+ sal_uInt8 nStyleId, sal_uInt8 nLevel = EXC_STYLE_NOLEVEL );
+ /** Inserts a built-in XF and STYLE record and returns the XF ID.
+ @param bCreateStyleRec true = Creates the related STYLE record. */
+ sal_uInt32 AppendBuiltInXFWithStyle( XclExpXFRef const & xXF,
+ sal_uInt8 nStyleId, sal_uInt8 nLevel = EXC_STYLE_NOLEVEL );
+
+ /** Inserts all default XF and STYLE records. */
+ void InsertDefaultRecords();
+
+ /** Appends a XF index to the internal ID<->index maps. */
+ void AppendXFIndex( sal_uInt32 nXFId );
+
+ void AddBorderAndFill( const XclExpXF& rXF );
+ void SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF );
+
+private:
+ /** Extended info about a built-in XF. */
+ struct XclExpBuiltInInfo
+ {
+ sal_uInt8 mnStyleId; /// Built-in style identifier.
+ sal_uInt8 mnLevel; /// Level for RowLevel/ColLevel styles.
+ bool mbPredefined; /// true = XF still predefined.
+ bool mbHasStyleRec; /// true = STYLE record created.
+ explicit XclExpBuiltInInfo();
+ };
+ typedef ::std::map< sal_uInt32, XclExpBuiltInInfo > XclExpBuiltInMap;
+ typedef ::std::vector< XclExpCellBorder > XclExpBorderList;
+ typedef ::std::vector< XclExpCellArea > XclExpFillList;
+
+ /** composite key for the find-map, so we can do partial key searching */
+ struct FindKey
+ {
+ bool mbCellXF; // is this a hard cell format, or a cell style
+ const SfxItemSet* mpItemSet;
+ sal_uInt32 mnScNumFmt;
+ sal_uInt16 mnXclFont;
+
+ bool operator<(const FindKey& other) const
+ {
+ if (mbCellXF != other.mbCellXF)
+ return mbCellXF < other.mbCellXF;
+ if (mpItemSet != other.mpItemSet)
+ return mpItemSet < other.mpItemSet;
+ if (mnScNumFmt != other.mnScNumFmt)
+ return mnScNumFmt < other.mnScNumFmt;
+ return mnXclFont < other.mnXclFont;
+ }
+ };
+ static FindKey ToFindKey(XclExpXF const &);
+
+ XclExpXFList maXFList; /// List of all XF records.
+ std::map<FindKey, std::vector<sal_uInt32>>
+ maXFFindMap; /// map of itemset to vector of positions, to speed up find
+ XclExpRecordList< XclExpStyle >
+ maStyleList; /// List of all STYLE records.
+ XclExpBuiltInMap maBuiltInMap; /// Contained elements describe built-in XFs.
+ ScfUInt16Vec maXFIndexVec; /// Maps XF IDs to XF indexes.
+ ScfUInt16Vec maStyleIndexes; /// Maps XF IDs to OOXML Style indexes
+ ScfUInt16Vec maCellIndexes; /// Maps XF IDs to OOXML Cell indexes
+ XclExpXFList maSortedXFList; /// List of XF records in XF index order.
+ XclExpBorderList maBorders; /// List of borders used by XF records
+ XclExpFillList maFills; /// List of fills used by XF records
+
+};
+
+class XclExpDxf : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpDxf( const XclExpRoot& rRoot, std::unique_ptr<XclExpCellAlign> pAlign, std::unique_ptr<XclExpCellBorder> pBorder,
+ std::unique_ptr<XclExpDxfFont> pFont, std::unique_ptr<XclExpNumFmt> pNumberFmt,
+ std::unique_ptr<XclExpCellProt> pProt, std::unique_ptr<XclExpColor> pColor);
+ XclExpDxf( const XclExpRoot& rRoot, std::unique_ptr<XclExpCellArea> pCellArea);
+ virtual ~XclExpDxf() override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+ void SaveXmlExt( XclExpXmlStream& rStrm);
+
+private:
+ std::unique_ptr<XclExpCellAlign> mpAlign;
+ std::unique_ptr<XclExpCellBorder> mpBorder;
+ std::unique_ptr<XclExpDxfFont> mpFont;
+ std::unique_ptr<XclExpNumFmt> mpNumberFmt;
+ std::unique_ptr<XclExpCellProt> mpProt;
+ std::unique_ptr<XclExpColor> mpColor;
+ std::unique_ptr<XclExpCellArea> mpCellArea;
+};
+
+class XclExpDxfs : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ XclExpDxfs( const XclExpRoot& rRoot );
+
+ sal_Int32 GetDxfId(const OUString& rName) const;
+ sal_Int32 GetDxfByColor(Color aColor) const;
+ void AddColor(Color aColor);
+
+ virtual void SaveXml( XclExpXmlStream& rStrm) override;
+private:
+ typedef std::vector< std::unique_ptr<XclExpDxf> > DxfContainer;
+ std::map<OUString, sal_Int32> maStyleNameToDxfId;
+ std::map<Color, sal_Int32> maColorToDxfId;
+ DxfContainer maDxf;
+ std::unique_ptr<NfKeywordTable> mpKeywordTable; /// Replacement table.
+};
+
+class XclExpXmlStyleSheet : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpXmlStyleSheet( const XclExpRoot& rRoot );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xetable.hxx b/sc/source/filter/inc/xetable.hxx
new file mode 100644
index 000000000..ed16140f6
--- /dev/null
+++ b/sc/source/filter/inc/xetable.hxx
@@ -0,0 +1,1028 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xltable.hxx"
+
+#include <vector>
+#include "xladdress.hxx"
+#include "xecontent.hxx"
+#include "xerecord.hxx"
+#include "xestyle.hxx"
+#include "xlformula.hxx"
+
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <o3tl/sorted_vector.hxx>
+
+class XclExtLst;
+
+/* ============================================================================
+Export of cell tables including row and column description.
+- Managing all used and formatted cells in a sheet.
+- Row and column properties, i.e. width/height, visibility.
+- Find default row formatting and default column formatting.
+- Merged cell ranges.
+============================================================================ */
+
+// Helper records for cell records
+
+/** Represents a STRING record that contains the result of a string formula. */
+class XclExpStringRec : public XclExpRecord
+{
+public:
+ explicit XclExpStringRec( const XclExpRoot& rRoot, const OUString& rResult );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpStringRef mxResult;
+};
+
+// Additional records for special formula ranges
+
+/** Base record for additional range formula records (i.e. ARRAY, SHRFMLA). */
+class XclExpRangeFmlaBase : public XclExpRecord
+{
+public:
+ /** Returns true, if the passed cell position is equal to own base position. */
+ bool IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const;
+
+ /** Derived classes create the token array for a corresponding FORMULA cell record. */
+ virtual XclTokenArrayRef CreateCellTokenArray( const XclExpRoot& rRoot ) const = 0;
+ /** Derived classes return true, if the own formula contains volatile functions. */
+ virtual bool IsVolatile() const = 0;
+
+protected:
+ /** Constructs the record with a single cell. */
+ explicit XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos );
+ /** Constructs the record with a cell range. */
+ explicit XclExpRangeFmlaBase(
+ sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange );
+
+ /** Extends the cell range to include the passed cell address. */
+ void Extend( const ScAddress& rScPos );
+
+ /** Writes the range address covered by this record. */
+ void WriteRangeAddress( XclExpStream& rStrm ) const;
+
+protected:
+ XclRange maXclRange; /// Range described by this record.
+ XclAddress maBaseXclPos; /// Address of base cell (first FORMULA record).
+};
+
+typedef rtl::Reference< XclExpRangeFmlaBase > XclExpRangeFmlaRef;
+
+// Array formulas =============================================================
+
+class ScTokenArray;
+
+/** Represents an ARRAY record that contains the token array of a matrix formula.
+
+ An ARRAY record is stored following the first FORMULA record that is part
+ of a matrix formula. All FORMULA records of a matrix formula contain a
+ reference to the ARRAY record, while the ARRAY record contains the formula
+ token array used by all formulas.
+ */
+class XclExpArray : public XclExpRangeFmlaBase
+{
+public:
+ explicit XclExpArray( const XclTokenArrayRef& xTokArr, const ScRange& rScRange );
+
+ /** Creates and returns the token array for a corresponding FORMULA cell record. */
+ virtual XclTokenArrayRef CreateCellTokenArray( const XclExpRoot& rRoot ) const override;
+ /** Returns true, if the array formula contains volatile functions. */
+ virtual bool IsVolatile() const override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclTokenArrayRef mxTokArr; /// The token array of a matrix formula.
+};
+
+typedef rtl::Reference< XclExpArray > XclExpArrayRef;
+
+/** Caches all ARRAY records. */
+class XclExpArrayBuffer : protected XclExpRoot
+{
+public:
+ explicit XclExpArrayBuffer( const XclExpRoot& rRoot );
+
+ /** Inserts a new ARRAY record into the buffer and returns it. */
+ XclExpArrayRef CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange );
+ /** Tries to find an ARRAY record that corresponds to an ocMatRef token. */
+ XclExpArrayRef FindArray( const ScTokenArray& rScTokArr, const ScAddress& rBasePos ) const;
+
+private:
+ typedef ::std::map< ScAddress, XclExpArrayRef > XclExpArrayMap;
+ XclExpArrayMap maRecMap; /// Map containing the ARRAY records.
+};
+
+// Shared formulas ============================================================
+
+/** Represents a SHRFMLA record that contains the token array of a shared formula.
+
+ A SHRFMLA record is stored following the first FORMULA record that is part
+ of a shared formula. All FORMULA records of a shared formula contain a
+ reference to the SHRFMLA record, while the SHRFMLA record contains the
+ formula token array used by all formulas.
+ */
+class XclExpShrfmla : public XclExpRangeFmlaBase
+{
+public:
+ /** Creates a SHRFMLA record that consists of the passed cell address only. */
+ explicit XclExpShrfmla( const XclTokenArrayRef& xTokArr, const ScAddress& rScPos );
+
+ /** Extends the cell range to include the passed cell address. */
+ void ExtendRange( const ScAddress& rScPos );
+
+ /** Creates and returns the token array for a corresponding FORMULA cell record. */
+ virtual XclTokenArrayRef CreateCellTokenArray( const XclExpRoot& rRoot ) const override;
+ /** Returns true, if the shared formula contains volatile functions. */
+ virtual bool IsVolatile() const override;
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclTokenArrayRef mxTokArr; /// The token array of a shared formula.
+ sal_uInt8 mnUsedCount; /// Number of FORMULA records referring to this record.
+};
+
+typedef rtl::Reference< XclExpShrfmla > XclExpShrfmlaRef;
+
+/** Caches all SHRFMLA records and provides functions to update their ranges. */
+class XclExpShrfmlaBuffer : protected XclExpRoot
+{
+public:
+ explicit XclExpShrfmlaBuffer( const XclExpRoot& rRoot );
+
+ /** Tries to create a new or to update an existing SHRFMLA record.
+ @return An empty reference, if the passed token array does not contain
+ a shared formula. If the token array is a shared formula, this
+ function updates its cell range to include the passed cell position,
+ if there is a SHRFMLA record for the passed token array; otherwise
+ this function creates and returns a new SHRFMLA record. */
+ XclExpShrfmlaRef CreateOrExtendShrfmla( const ScFormulaCell& rScCell, const ScAddress& rScPos );
+
+private:
+ /**
+ * Check for presence of token that's not allowed in Excel's shared
+ * formula. Refer to the "SharedParsedFormula" section of [MS-XLS] spec
+ * for more info.
+ */
+ bool IsValidTokenArray( const ScTokenArray& rArray ) const;
+
+ typedef std::unordered_map<const ScTokenArray*, XclExpShrfmlaRef> TokensType;
+ typedef o3tl::sorted_vector<const ScTokenArray*> BadTokenArraysType;
+
+ TokensType maRecMap; /// Map containing the SHRFMLA records.
+ BadTokenArraysType maBadTokens; /// shared tokens we should *not* export as SHRFMLA
+};
+
+// Multiple operations ========================================================
+
+/** Represents a TABLEOP record for a multiple operations range. */
+class XclExpTableop : public XclExpRangeFmlaBase
+{
+public:
+ explicit XclExpTableop( const ScAddress& rScPos,
+ const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode );
+
+ /** Returns true, if the cell range has been extended to the passed position.
+ @descr All references passed in rRefs must fit the ranges passed in the constructor. */
+ bool TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs );
+
+ /** Finalizes the record. Tests on valid cell range and reference addresses. */
+ void Finalize();
+
+ /** Creates and returns the token array for a corresponding FORMULA cell record. */
+ virtual XclTokenArrayRef CreateCellTokenArray( const XclExpRoot& rRoot ) const override;
+ /** Returns true, if the multiple operations range is volatile. */
+ virtual bool IsVolatile() const override;
+ /** Writes the record if it is valid. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+private:
+ /** Returns true, if the passed cell position can be appended to this record. */
+ bool IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const;
+
+ /** Writes the contents of the TABLEOP record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnLastAppXclCol;/// Column index of last appended cell.
+ sal_uInt16 mnColInpXclCol; /// Column index of column input cell.
+ sal_uInt32 mnColInpXclRow; /// Row index of column input cell.
+ sal_uInt16 mnRowInpXclCol; /// Column index of row input cell.
+ sal_uInt32 mnRowInpXclRow; /// Row index of row input cell.
+ sal_uInt8 mnScMode; /// Type of the multiple operation (Calc constant).
+ bool mbValid; /// true = Contains valid references.
+};
+
+typedef rtl::Reference< XclExpTableop > XclExpTableopRef;
+
+/** Contains all created TABLEOP records and supports creating or updating them. */
+class XclExpTableopBuffer : protected XclExpRoot
+{
+public:
+ explicit XclExpTableopBuffer( const XclExpRoot& rRoot );
+
+ /** Tries to update an existing or to create a new TABLEOP record.
+ @return Reference to the TABLEOP record for this cell (existing or new),
+ or an empty reference, if rScTokArr does not contain a multiple
+ operations formula. */
+ XclExpTableopRef CreateOrExtendTableop(
+ const ScTokenArray& rScTokArr, const ScAddress& rScPos );
+
+ /** Finalizes all contained TABLEOP records. */
+ void Finalize();
+
+private:
+ /** Tries to create a new TABLEOP record, if rRefs contains valid references. */
+ XclExpTableopRef TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs );
+
+private:
+ XclExpRecordList< XclExpTableop > maTableopList; /// List of all TABLEOP records.
+};
+
+// Cell records
+
+/** The base class of all cell records. */
+class XclExpCellBase : public XclExpRecord
+{
+public:
+ /** Returns the (first) address of the cell(s). */
+ const XclAddress& GetXclPos() const { return maXclPos; }
+ /** Returns the (first) Excel column index of the cell(s). */
+ sal_uInt16 GetXclCol() const { return maXclPos.mnCol; }
+ /** Returns the Excel row index of the cell. */
+ sal_uInt32 GetXclRow() const { return maXclPos.mnRow; }
+
+ /** Derived classes return the column index of the last contained cell. */
+ virtual sal_uInt16 GetLastXclCol() const = 0;
+ /** Derived classes return the XF identifier of the first contained cell. */
+ virtual sal_uInt32 GetFirstXFId() const = 0;
+ /** Derived classes return true, if this record does not contain at least one valid cell. */
+ virtual bool IsEmpty() const = 0;
+ /** Derived classes return whether the cell contains multi-line text. */
+ virtual bool IsMultiLineText() const;
+
+ /** Derived classes try to merge the contents of the passed cell to own data. */
+ virtual bool TryMerge( const XclExpCellBase& rCell );
+ /** Derived classes convert the XF identifier(s) into the Excel XF index(es).
+ @param rXFIndexes The converted XF index(es) are inserted here. */
+ virtual void ConvertXFIndexes( const XclExpRoot& rRoot ) = 0;
+ /** Derived classes for blank cells insert the Excel XF index(es) into the passed vector. */
+ virtual void GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const;
+ /** Derived classes for blank cells remove unused Excel XF index(es). */
+ virtual void RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound );
+
+protected:
+ explicit XclExpCellBase(
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos );
+
+ /** Sets this record to a new column position. */
+ void SetXclCol( sal_uInt16 nXclCol ) { maXclPos.mnCol = nXclCol; }
+
+private:
+ XclAddress maXclPos; /// Address of the cell.
+};
+
+typedef rtl::Reference< XclExpCellBase > XclExpCellRef;
+
+// Single cell records ========================================================
+
+/** Base class for all cell records not supporting multiple contents. */
+class XclExpSingleCellBase : public XclExpCellBase
+{
+public:
+ /** Returns the last column, which is equal to the first column for single cells. */
+ virtual sal_uInt16 GetLastXclCol() const override;
+ /** Return the XF identifier of the cell. */
+ virtual sal_uInt32 GetFirstXFId() const override;
+ /** Returns true, if this record does not contain at least one valid cell. */
+ virtual bool IsEmpty() const override;
+ /** Converts the XF identifier into the Excel XF index. */
+ virtual void ConvertXFIndexes( const XclExpRoot& rRoot ) override;
+ /** Writes cell address, XF index, and calls WriteContents() for each cell. */
+ virtual void Save( XclExpStream& rStrm ) override;
+
+protected:
+ explicit XclExpSingleCellBase( sal_uInt16 nRecId, std::size_t nContSize,
+ const XclAddress& rXclPos, sal_uInt32 nXFId );
+
+ explicit XclExpSingleCellBase( const XclExpRoot& rRoot,
+ sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId );
+
+ void SetContSize( std::size_t nContSize ) { mnContSize = nContSize; }
+ std::size_t GetContSize() const { return mnContSize; }
+
+ void SetXFId( sal_uInt32 nXFId ) { maXFId.mnXFId = nXFId; }
+ sal_uInt32 GetXFId() const { return maXFId.mnXFId; }
+
+private:
+ /** Writes cell address, XF index, and calls WriteContents() for each cell. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+ /** Derived classes write the contents of the specified cell (without XF index). */
+ virtual void WriteContents( XclExpStream& rStrm ) = 0;
+
+private:
+ XclExpXFId maXFId; /// The XF identifier of the cell formatting.
+ std::size_t mnContSize; /// The size of the cell contents.
+};
+
+/** Represents a NUMBER record that describes a cell with a double value. */
+class XclExpNumberCell : public XclExpSingleCellBase
+{
+public:
+ explicit XclExpNumberCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ double fValue );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ virtual void WriteContents( XclExpStream& rStrm ) override;
+
+private:
+ double mfValue; /// The cell value.
+};
+
+/** Represents a BOOLERR record that describes a cell with a Boolean value. */
+class XclExpBooleanCell : public XclExpSingleCellBase
+{
+public:
+ explicit XclExpBooleanCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ bool bValue );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ virtual void WriteContents( XclExpStream& rStrm ) override;
+
+private:
+ bool mbValue; /// The cell value.
+};
+
+class XclExpHyperlinkHelper;
+class EditTextObject;
+
+/** Represents a text cell record.
+
+ May contain a BIFF2-BIFF7 LABEL record for a simple string, or a BIFF2-BIFF7
+ RSTRING record for a formatted string, or a BIFF8 LABELSST string for any
+ string (simply stores a reference to the Shared String Table).
+ */
+class XclExpLabelCell : public XclExpSingleCellBase
+{
+public:
+ /** Constructs the record from an unformatted Calc string cell. */
+ explicit XclExpLabelCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const OUString& rStr );
+
+ /** Constructs the record from a formatted Calc edit cell. */
+ explicit XclExpLabelCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const EditTextObject* pEditText, XclExpHyperlinkHelper& rHlinkHelper );
+
+ /** Returns true if the cell contains multi-line text. */
+ virtual bool IsMultiLineText() const override;
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Initializes the record contents. Called from constructors. */
+ void Init( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, XclExpStringRef const & xText );
+
+ virtual void WriteContents( XclExpStream& rStrm ) override;
+
+private:
+ XclExpStringRef mxText; /// The cell text.
+ sal_uInt32 mnSstIndex; /// Index into Shared String Table (only used for BIFF8).
+ bool mbLineBreak; /// True = cell has automatic linebreaks enabled.
+};
+
+class ScFormulaCell;
+
+/** Represents a FORMULA record that describes a cell with a formula. */
+class XclExpFormulaCell : public XclExpSingleCellBase
+{
+public:
+ explicit XclExpFormulaCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ const ScFormulaCell& rScFmlaCell,
+ XclExpArrayBuffer& rArrayBfr,
+ XclExpShrfmlaBuffer& rShrfmlaBfr,
+ XclExpTableopBuffer& rTableopBfr );
+
+ /** Writes the FORMULA record and additional records related to the formula. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ virtual void WriteContents( XclExpStream& rStrm ) override;
+
+private:
+ ScFormulaCell& mrScFmlaCell; /// The Calc formula cell.
+ XclTokenArrayRef mxTokArr; /// The token array of the formula.
+ XclExpRangeFmlaRef mxAddRec; /// Additional record for matrix/shared formulas.
+ XclExpRecordRef mxStringRec; /// STRING record for string result.
+};
+
+// Multiple cell records ======================================================
+
+struct XclExpMultiXFId : public XclExpXFId
+{
+ sal_uInt16 mnCount; /// Number of XF identifiers.
+
+ explicit XclExpMultiXFId( sal_uInt32 nXFId, sal_uInt16 nCount = 1 ) :
+ XclExpXFId( nXFId ), mnCount( nCount ) {}
+};
+
+/** Base class for all cell records supporting multiple contents. */
+class XclExpMultiCellBase : public XclExpCellBase
+{
+public:
+ /** Returns the column index of the last cell this record describes. */
+ virtual sal_uInt16 GetLastXclCol() const override;
+ /** Return the XF identifier of the first contained cell. */
+ virtual sal_uInt32 GetFirstXFId() const override;
+ /** Returns true, if this record does not contain at least one valid cell. */
+ virtual bool IsEmpty() const override;
+
+ /** Convert all XF identifiers into the Excel XF indexes. */
+ virtual void ConvertXFIndexes( const XclExpRoot& rRoot ) override;
+ /** Writes the record, calls WriteContents() for each contained cell.
+ @descr May write several records, if unused XF indexes are contained. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+protected:
+ explicit XclExpMultiCellBase( sal_uInt16 nRecId, sal_uInt16 nMulRecId,
+ std::size_t nContSize, const XclAddress& rXclPos );
+
+ /** Returns the number of cells this record represents. */
+ sal_uInt16 GetCellCount() const;
+
+ /** Appends the passed XF identifier nCount times to the list of XF identifiers. */
+ void AppendXFId( const XclExpMultiXFId& rXFId );
+ /** Appends the passed cell format nCount times to the list of XF identifiers. */
+ void AppendXFId( const XclExpRoot& rRoot,
+ const ScPatternAttr* pPattern, sal_uInt16 nScript,
+ sal_uInt32 nForcedXFId, sal_uInt16 nCount = 1 );
+
+ /** Tries to merge the XF ID list of the passed cell with the own list. */
+ bool TryMergeXFIds( const XclExpMultiCellBase& rCell );
+ /** Inserts the Excel XF index(es) into the passed vector. */
+ void GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const;
+
+ /** Removes unused Excel XF index(es).
+ @param rXFIndexes Specifies which XF indexes are used.
+ @param nStartAllNotFound Index in rXFIndexes which starts EXC_XF_NOTFOUND until the end.
+ */
+ void RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound );
+
+ /** Return starting column at which all indexes until the end are EXC_XF_DEFAULTCELL .*/
+ sal_uInt16 GetStartColAllDefaultCell() const;
+
+private:
+ /** Derived classes write the remaining contents of the specified cell (without XF index).
+ @param nRelCol Relative column index (starts with 0 for first cell of this record). */
+ virtual void WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol ) = 0;
+ virtual void WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol ) = 0;
+
+private:
+ typedef ::std::vector< XclExpMultiXFId > XclExpMultiXFIdDeq;
+
+ sal_uInt16 mnMulRecId; /// Record ID for multiple record variant.
+ std::size_t mnContSize; /// Data size of contents for one cell
+ XclExpMultiXFIdDeq maXFIds; /// The XF identifiers of the cell formatting.
+};
+
+/** Represents a BLANK or MULBLANK record that describes empty but formatted cells. */
+class XclExpBlankCell : public XclExpMultiCellBase
+{
+public:
+ explicit XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId );
+
+ explicit XclExpBlankCell( const XclExpRoot& rRoot,
+ const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId );
+
+ /** Tries to merge the contents of the passed cell to own data. */
+ virtual bool TryMerge( const XclExpCellBase& rCell ) override;
+ /** Inserts the Excel XF index(es) into the passed vector. */
+ virtual void GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const override;
+ /** Tries to remove unused Excel XF index(es). */
+ virtual void RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound ) override;
+
+ using XclExpMultiCellBase::GetStartColAllDefaultCell;
+
+private:
+ /** Writes the remaining contents of the specified cell (without XF index). */
+ virtual void WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol ) override;
+ virtual void WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol ) override;
+};
+
+/** Represents an RK or MULRK record that describes cells with a compressed double values. */
+class XclExpRkCell : public XclExpMultiCellBase
+{
+public:
+ explicit XclExpRkCell( const XclExpRoot& rRoot, const XclAddress& rXclPos,
+ const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
+ sal_Int32 nRkValue );
+
+ /** Tries to merge the contents of the passed cell to own data. */
+ virtual bool TryMerge( const XclExpCellBase& rCell ) override;
+
+private:
+ /** Writes the remaining contents of the specified cell (without XF index). */
+ virtual void WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol ) override;
+ virtual void WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol ) override;
+
+private:
+ ScfInt32Vec maRkValues; /// The cell values.
+};
+
+// Rows and Columns
+
+class ScOutlineArray;
+
+/** Base class for buffers containing row or column outline data. */
+class XclExpOutlineBuffer
+{
+public:
+ /** Returns true, if a collapsed group ends at the last processed position. */
+ bool IsCollapsed() const { return mbCurrCollapse; }
+ /** Returns the highest level of an open group at the last processed position. */
+ sal_uInt8 GetLevel() const { return ::std::min( mnCurrLevel, EXC_OUTLINE_MAX ); }
+
+protected:
+ /** Constructs the outline buffer.
+ @param bRows true = Process row outline array; false = Process column outline array. */
+ explicit XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows );
+
+ /** Updates the current state by processing the settings at the passed Calc position. */
+ void UpdateColRow( SCCOLROW nScPos );
+
+private:
+ /** Data about an outline level. */
+ struct XclExpLevelInfo
+ {
+ SCCOLROW mnScEndPos; /// The end position of a group in a level.
+ bool mbHidden; /// true = Group in this level is hidden.
+ explicit XclExpLevelInfo() : mnScEndPos( 0 ), mbHidden( false ) {}
+ };
+
+ const ScOutlineArray* mpScOLArray; /// Pointer to Calc outline array.
+ std::vector< XclExpLevelInfo >
+ maLevelInfos; /// Info for current row and all levels.
+ sal_uInt8 mnCurrLevel; /// Highest level of an open group for current position.
+ bool mbCurrCollapse; /// true = Collapsed group ends at current position.
+};
+
+/** The outline buffer for column outlines. */
+class XclExpColOutlineBuffer : public XclExpOutlineBuffer
+{
+public:
+ explicit XclExpColOutlineBuffer( const XclExpRoot& rRoot ) :
+ XclExpOutlineBuffer( rRoot, false ) {}
+
+ /** Updates the current state by processing the settings of the passed Calc column. */
+ void Update( SCCOL nScCol )
+ { UpdateColRow( static_cast< SCCOLROW >( nScCol ) ); }
+};
+
+/** The outline buffer for row outlines. */
+class XclExpRowOutlineBuffer : public XclExpOutlineBuffer
+{
+public:
+ explicit XclExpRowOutlineBuffer( const XclExpRoot& rRoot ) :
+ XclExpOutlineBuffer( rRoot, true ) {}
+
+ /** Updates the current state by processing the settings of the passed Calc row. */
+ void Update( SCROW nScRow )
+ { UpdateColRow( static_cast< SCCOLROW >( nScRow ) ); }
+};
+
+/** Represents a GUTS record containing the level count of row and column outlines. */
+class XclExpGuts : public XclExpRecord
+{
+public:
+ explicit XclExpGuts( const XclExpRoot& rRoot );
+
+private:
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnColLevels; /// Number of visible column outline levels.
+ sal_uInt16 mnColWidth; /// Width of column outline area (pixels).
+ sal_uInt16 mnRowLevels; /// Number of visible row outline levels.
+ sal_uInt16 mnRowWidth; /// Width of row outline area (pixels).
+};
+
+/** Represents a DIMENSIONS record containing the used area of a sheet. */
+class XclExpDimensions : public XclExpRecord
+{
+public:
+ explicit XclExpDimensions( const XclExpRoot& rRoot );
+
+ /** Sets the used area to the record. */
+ void SetDimensions(
+ sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
+ sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Writes the contents of the DIMENSIONS record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclExpRoot& mrRoot;
+ sal_uInt32 mnFirstUsedXclRow; /// First used row.
+ sal_uInt32 mnFirstFreeXclRow; /// First unused row after used area.
+ sal_uInt16 mnFirstUsedXclCol; /// First used column.
+ sal_uInt16 mnFirstFreeXclCol; /// First free column after used area.
+};
+
+/** Represents the DEFCOLWIDTH record containing the default column width of a sheet.
+
+ Excel stores the default column width in entire character widths of the '0'
+ character using the application default font (i.e. the default width is 10,
+ if the '0' character fits 10 times into a cell in a column with default
+ width.
+
+ Half of character width is reserved for non character display.
+ It is margin padding (two on each side) and padding for the gridlines.
+
+ The IsDefWidth() function returns true, if the passed width (measured in
+ 1/256 of the width of the '0' character) could be converted exactly to the
+ default width. If the passed width is rounded up or down to get the default
+ width, the function returns false.
+ */
+class XclExpDefcolwidth : public XclExpDoubleRecord, protected XclExpRoot
+{
+public:
+ explicit XclExpDefcolwidth( const XclExpRoot& rRoot );
+
+ /** Returns true, if the own default width exactly matches the passed width. */
+ bool IsDefWidth( sal_uInt16 nXclColWidth ) const;
+
+ /** Sets the passed column width (in 1/256 character width) as default width. */
+ void SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS );
+
+ virtual void Save(XclExpStream& rStrm) override;
+};
+
+/** Contains the column settings for a range of columns.
+
+ After construction the record contains a temporary XF identifier returned
+ from the XF buffer. After creating the entire Excel document in memory, the
+ ConvertXFIndexes() function converts it into the real Excel XF index.
+ */
+class XclExpColinfo : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Constructs the record with the settings in the Calc document. */
+ explicit XclExpColinfo( const XclExpRoot& rRoot,
+ SCCOL nScCol, SCROW nLastScRow,
+ XclExpColOutlineBuffer& rOutlineBfr );
+
+ /** Converts the XF identifier into the Excel XF index. */
+ void ConvertXFIndexes();
+
+ /** Tries to merge this record with the passed record.
+ @descr Possible, if passed record directly follows this record and has equal contents.
+ @return true = This record is equal to passed record and has been updated. */
+ bool TryMerge( const XclExpColinfo& rColInfo );
+
+ /** Returns the Excel width of the column(s). */
+ sal_uInt16 GetColWidth() const { return mnWidth; }
+ /** Returns the final Excel XF index of the column(s). */
+ sal_uInt16 GetXFIndex() const { return maXFId.mnXFIndex; }
+ /** Returns the number of columns represented by this record. */
+ sal_uInt16 GetColCount() const { return mnLastXclCol - mnFirstXclCol + 1; }
+
+ /** Returns true, if the column has default format and width. Also sets mbCustomWidth */
+ bool IsDefault( const XclExpDefcolwidth& rDefColWidth );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of this COLINFO record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpXFId maXFId; /// The XF identifier for column default format.
+ bool mbCustomWidth; /// True = Column width is different from default width
+ sal_uInt16 mnWidth; /// Excel width of the column.
+ sal_uInt16 mnScWidth; /// Calc width of the column.
+ sal_uInt16 mnFlags; /// Additional column flags.
+ sal_uInt8 mnOutlineLevel; /// Outline Level of column (for OOXML)
+ sal_uInt16 mnFirstXclCol; /// Index to first column.
+ sal_uInt16 mnLastXclCol; /// Index to last column.
+};
+
+/** Contains COLINFO records for all columns of a Calc sheet.
+
+ On construction one COLINFO record per column is created. After creating
+ the entire Excel document in memory, the ConvertXFIndexes() function converts
+ all temporary XF identifiers into real Excel XF indexes and merges all equal
+ COLINFO records together.
+ */
+class XclExpColinfoBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpColinfoBuffer( const XclExpRoot& rRoot );
+
+ /** Initializes the buffer: finds settings and formatting of all columns.
+ @param nLastScRow Last row used to find default formatting. */
+ void Initialize( SCROW nLastScRow );
+ /** Converts the XF identifiers into the Excel XF indexes and merges equal columns.
+ @param rXFIndexes Returns the final XF indexes of all columns. */
+ void Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS );
+
+ /** Writes all COLINFO records of this buffer. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+ sal_uInt8 GetHighestOutlineLevel() const { return mnHighestOutlineLevel; }
+ double GetDefColWidth() const { return maDefcolwidth.GetValue(); }
+
+private:
+ typedef XclExpRecordList< XclExpColinfo > XclExpColinfoList;
+ typedef XclExpColinfoList::RecordRefType XclExpColinfoRef;
+
+ XclExpColinfoList maColInfos; /// List of COLINFO records.
+ XclExpDefcolwidth maDefcolwidth; /// The DEFCOLWIDTH record.
+ XclExpColOutlineBuffer maOutlineBfr; /// Buffer for column outline groups.
+ sal_uInt8 mnHighestOutlineLevel; /// Highest number of outline levels for columns in sheet.
+};
+
+class XclExpRow;
+
+/** Contains all possible default row settings. */
+struct XclExpDefaultRowData
+{
+ sal_uInt16 mnFlags; /// Default flags for unspecified rows.
+ sal_uInt16 mnHeight; /// Default height for unspecified rows.
+
+ explicit XclExpDefaultRowData();
+ explicit XclExpDefaultRowData( const XclExpRow& rRow );
+
+ /** Returns true, if rows are hidden by default. */
+ bool IsHidden() const { return ::get_flag( mnFlags, EXC_DEFROW_HIDDEN ); }
+ /** Returns true, if the rows have a manually set height by default. */
+ bool IsUnsynced() const { return ::get_flag( mnFlags, EXC_DEFROW_UNSYNCED ); }
+};
+
+/** Represents a DEFROWHEIGHT record containing default format for unused rows. */
+class XclExpDefrowheight : public XclExpRecord
+{
+public:
+ explicit XclExpDefrowheight();
+
+ /** Sets the passed default data as current record contents. */
+ void SetDefaultData( const XclExpDefaultRowData& rDefData );
+ XclExpDefaultRowData& GetDefaultData() { return maDefData; }
+private:
+ /** Writes the contents of the record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpDefaultRowData maDefData; /// Record data.
+};
+
+/** Represents a ROW record and additionally contains all cells records of a row.
+
+ This class contains all cell records of a row in a spreadsheet. There are 2
+ cell records in Excel that support storing a range of cells in one record
+ (MULBLANK for multiple blank cells, and MULRK for multiple RK values). The
+ insertion functions try to merge a new inserted cell with existing
+ neighbors, if this is supported by the current type of cell record.
+
+ The Finalize() function converts the XF identifiers of all cell records to
+ the final Excel XF indexes. Then a default
+ */
+class XclExpRow : public XclExpRecord, protected XclExpRoot
+{
+public:
+ /** Constructs the ROW record and converts the Calc row settings.
+ @param bAlwaysEmpty true = This row will not be filled with blank cells
+ in the Finalize() function. */
+ explicit XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
+ XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty, bool bHidden, sal_uInt16 nHeight );
+
+ /** Returns the excel row index of this ROW record. */
+ sal_uInt32 GetXclRow() const { return mnXclRow; }
+ /** Returns the height of the row in twips. */
+ sal_uInt16 GetHeight() const { return mnHeight; }
+ /** Returns true, if this row does not contain at least one valid cell. */
+ bool IsEmpty() const { return maCellList.IsEmpty(); }
+ /** Returns true, if this row is hidden. */
+ bool IsHidden() const { return ::get_flag( mnFlags, EXC_ROW_HIDDEN ); }
+ /** Returns true, if this row contains a manually set height. */
+ bool IsUnsynced() const { return ::get_flag( mnFlags, EXC_ROW_UNSYNCED ); }
+ /** Returns true, if this row is enabled (will be exported). */
+ bool IsEnabled() const { return mbEnabled; }
+
+ /** Appends the passed cell object to this row. */
+ void AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase );
+
+ /** Converts all XF identifiers into the Excel XF indexes. */
+ void Finalize( const ScfUInt16Vec& rColXFIndexes,
+ ScfUInt16Vec& aXFIndexes,
+ size_t nStartColAllDefault,
+ bool bUpdateProgress );
+
+ /** Returns the column index of the first used cell in this row.
+ @descr This function can only be called after Finalize(). */
+ sal_uInt16 GetFirstUsedXclCol() const;
+ /** Returns the column index of the first unused cell following all used cells in this row.
+ @descr This function can only be called after Finalize(). */
+ sal_uInt16 GetFirstFreeXclCol() const;
+
+ /** Returns true, if this row may be omitted by using the DEFROWHEIGHT record.
+ @descr A row may be omitted, if it does not contain any cell or
+ explicit default cell formatting, and is not part of an outline.
+ This function can only be called after Finalize(). */
+ bool IsDefaultable() const;
+ /** Disables this row, if it is defaultable and has the passed default format.
+ @descr Disabled rows will not be saved.
+ This function can only be called after Finalize(). */
+ void DisableIfDefault( const XclExpDefaultRowData& rDefRowData );
+
+ /** Writes all cell records of this row. */
+ void WriteCellList( XclExpStream& rStrm );
+
+ /** Writes the ROW record if the row is not disabled (see DisableIfDefault() function). */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ sal_uInt32 GetXclRowRpt() const { return mnXclRowRpt; }
+ void SetXclRowRpt( sal_uInt32 nRpt ){ mnXclRowRpt = nRpt; }
+private:
+ /** Inserts a cell at the specified list position, tries to merge with neighbors. */
+ void InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase );
+
+ /** Writes the contents of the ROW record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclExpRecordList< XclExpCellBase >
+ maCellList; /// List of cell records for this row.
+ sal_uInt32 mnXclRow; /// Excel row index of this row.
+ sal_uInt16 mnHeight; /// Row height in twips.
+ sal_uInt16 mnFlags; /// Flags for the ROW record.
+ sal_uInt16 mnXFIndex; /// Default row formatting.
+ sal_uInt8 mnOutlineLevel; /// Outline Level of row (for OOXML)
+ sal_uInt32 mnXclRowRpt;
+ sal_uInt32 mnCurrentRow;
+ bool mbAlwaysEmpty; /// true = Do not add blank cells in Finalize().
+ bool mbEnabled; /// true = Write this ROW record.
+};
+
+/** Collects all rows which contain all cells of a sheet.
+
+ This row buffer automatically creates ROW records when cells are inserted
+ with the AppendCell() function. It is possible to force creation of more
+ ROW records with the CreateRows() function. In both cases, all preceding
+ missing ROW records are inserted too.
+ */
+class XclExpRowBuffer : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpRowBuffer( const XclExpRoot& rRoot );
+
+ /** Appends the passed cell object to the row that the cell specifies. */
+ void AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase );
+ /** Forces insertion of all ROW records before the passed row. */
+ void CreateRows( SCROW nFirstFreeScRow );
+
+ /** Converts all XF identifiers into the Excel XF indexes and calculates default formats.
+ @param rDefRowData (out-param) The default row format is returned here.
+ @param rColXFIndexes The column default XF indexes.
+ @param nStartColAllDefault Index in rColXFIndexes which starts EXC_XF_DEFAULTCELL until the end.
+ */
+ void Finalize( XclExpDefaultRowData& rDefRowData, const ScfUInt16Vec& rColXFIndexes,
+ size_t nStartColAllDefault );
+
+ /** Writes the DIMENSIONS record, all ROW records and all cell records. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+ XclExpDimensions& GetDimensions() { return maDimensions; }
+ sal_uInt8 GetHighestOutlineLevel() const { return mnHighestOutlineLevel; }
+
+private:
+ /** Returns access to the specified ROW record. Inserts preceding missing ROW records.
+ @param bRowAlwaysEmpty true = Created rows will not be filled with blank cells
+ in the XclExpRow::Finalize() function. */
+ XclExpRow& GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty );
+
+private:
+ typedef std::shared_ptr<XclExpRow> RowRef;
+ typedef ::std::map<sal_uInt32, RowRef> RowMap;
+
+ RowMap maRowMap;
+ XclExpRowOutlineBuffer maOutlineBfr; /// Buffer for row outline groups.
+ XclExpDimensions maDimensions; /// DIMENSIONS record for used area.
+ sal_uInt8 mnHighestOutlineLevel; /// Highest number of outline levels for rows in sheet.
+};
+
+// Cell Table
+
+class XclExpNote;
+class XclExpMergedcells;
+class XclExpHyperlink;
+class XclExpDval;
+
+/** This class contains the cell contents and more of an entire sheet.
+
+ The cell table includes the settings and default formatting of all columns,
+ the settings and default formatting of all used rows, and the contents of
+ all cells of one sheet in a spreadsheet document.
+
+ The constructor does all the work creating the cell table. It reads the
+ Calc sheet and converts all columns, rows, and cells to Excel record data.
+ Additionally, hyperlink records, note records, additional records for
+ formula cells, data validation records, and outline records are created.
+
+ The Finalize() function does even more work. It calculates default column
+ settings and removes column records that are equal to this default. The
+ same happens with rows: A default format is calculated for each row, and
+ all blank cells in this row that have the same format are removed. Then,
+ the most used row settings are calculated, and all empty rows that have the
+ same settings are removed too.
+
+ Records that are not stored inside the cell table area in an Excel file
+ (i.e. DEFROWHEIGHT record, NOTE records, MERGEDCELLS record, HLINK records,
+ DVAL and DV records for data validation) can be accessed with the function
+ CreateRecord(). It returns the reference to the respective record (or
+ record list) which can be inserted into a record list.
+ */
+class XclExpCellTable : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ explicit XclExpCellTable( const XclExpRoot& rRoot );
+
+ /** Converts all XF identifiers into the Excel XF indexes and calculates default formats. */
+ void Finalize(bool bXLS);
+
+ /** Returns the reference to an internal record specified by the passed record id.
+ @param nRecId The record identifier that specifies which record is
+ returned. Possible values are: EXC_ID_DEFROWHEIGHT, EXC_ID_NOTE,
+ EXC_ID_MERGEDCELLS, EXC_ID_HLINK, EXC_ID_DVAL. */
+ XclExpRecordRef CreateRecord( sal_uInt16 nRecId ) const;
+ /** Saves the entire cell table. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ typedef XclExpRecordList< XclExpNote > XclExpNoteList;
+ typedef XclExpRecordList< XclExpHyperlink > XclExpHyperlinkList;
+
+ typedef rtl::Reference< XclExpDefrowheight > XclExpDefrowhRef;
+ typedef rtl::Reference< XclExpNoteList > XclExpNoteListRef;
+ typedef rtl::Reference< XclExpMergedcells > XclExpMergedcellsRef;
+ typedef rtl::Reference< XclExpHyperlinkList > XclExpHyperlinkRef;
+ typedef rtl::Reference< XclExpDval > XclExpDvalRef;
+ typedef rtl::Reference< XclExtLst > XclExtLstRef;
+
+ XclExpColinfoBuffer maColInfoBfr; /// Buffer for column formatting.
+ XclExpRowBuffer maRowBfr; /// Rows and cell records.
+ XclExpArrayBuffer maArrayBfr; /// Buffer for ARRAY records.
+ XclExpShrfmlaBuffer maShrfmlaBfr; /// Buffer for SHRFMLA records.
+ XclExpTableopBuffer maTableopBfr; /// Buffer for TABLEOP records.
+ XclExpDefrowhRef mxDefrowheight; /// DEFROWHEIGHT record for default row format.
+ XclExpRecordRef mxGuts; /// GUTS record for outline areas.
+ XclExpNoteListRef mxNoteList; /// List of NOTE records.
+ XclExpMergedcellsRef mxMergedcells; /// MERGEDCELLS record for merged cell ranges.
+ XclExpHyperlinkRef mxHyperlinkList; /// List of HLINK records.
+ XclExpDvalRef mxDval; /// Data validation with DVAL and DV records.
+ XclExtLstRef mxExtLst;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xeview.hxx b/sc/source/filter/inc/xeview.hxx
new file mode 100644
index 000000000..6b7980ca8
--- /dev/null
+++ b/sc/source/filter/inc/xeview.hxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xerecord.hxx"
+#include "xlview.hxx"
+#include "xeroot.hxx"
+
+// Workbook view settings records =============================================
+
+/** Represents the WINDOW1 record containing global workbook view settings. */
+class XclExpWindow1 : public XclExpRecord
+{
+public:
+ explicit XclExpWindow1( const XclExpRoot& rRoot );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the WINDOW1 record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnFlags; /// Option flags.
+ sal_uInt16 mnTabBarSize; /// Size of tabbar relative to window width (per mill).
+};
+
+// Sheet view settings records ================================================
+
+/** Represents a WINDOW2 record with general view settings for a sheet. */
+class XclExpWindow2 : public XclExpRecord
+{
+public:
+ explicit XclExpWindow2( const XclExpRoot& rRoot,
+ const XclTabViewData& rData, sal_uInt32 nGridColorId );
+
+private:
+ /** Writes the contents of the WINDOW2 record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ Color maGridColor; /// Grid color (<=BIFF5).
+ sal_uInt32 mnGridColorId; /// Color ID of grid color (>=BIFF8).
+ sal_uInt16 mnFlags; /// Option flags.
+ XclAddress maFirstXclPos; /// First visible cell.
+ sal_uInt16 mnNormalZoom; /// Zoom factor for normal view.
+ sal_uInt16 mnPageZoom; /// Zoom factor for pagebreak preview.
+};
+
+/** Represents an SCL record for the zoom factor of the current view of a sheet. */
+class XclExpScl : public XclExpRecord
+{
+public:
+ explicit XclExpScl( sal_uInt16 nZoom );
+
+private:
+ /** Tries to shorten numerator and denominator by the passed value. */
+ void Shorten( sal_uInt16 nFactor );
+ /** Writes the contents of the SCL record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnNum; /// Numerator of the zoom factor.
+ sal_uInt16 mnDenom; /// Denominator of the zoom factor.
+};
+
+/** Represents a PANE record containing settings for split/frozen windows. */
+class XclExpPane : public XclExpRecord
+{
+public:
+ explicit XclExpPane( const XclTabViewData& rData );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Writes the contents of the PANE record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ sal_uInt16 mnSplitX; /// Split X position, or frozen column.
+ sal_uInt32 mnSplitY; /// Split Y position, or frozen row.
+ XclAddress maSecondXclPos; /// First visible cell in additional panes.
+ sal_uInt8 mnActivePane; /// Active pane (with cell cursor).
+ bool mbFrozenPanes; /// true = "frozen" panes; false = "split" window.
+};
+
+/** Represents a SELECTION record with selection data for a pane. */
+class XclExpSelection : public XclExpRecord
+{
+public:
+ explicit XclExpSelection( const XclTabViewData& rData, sal_uInt8 nPane );
+
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+private:
+ /** Writes the contents of the SELECTION record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ XclSelectionData maSelData; /// Selection data.
+ sal_uInt8 mnPane; /// Pane identifier of this selection.
+};
+
+class XclExpTabBgColor : public XclExpRecord
+{
+public:
+ explicit XclExpTabBgColor( const XclTabViewData& rTabViewData );
+
+ /* virtual void SaveXml( XclExpXmlStream& rStrm ); TODO Fix XML Saving Stream */
+private:
+ /** Writes the contents of the SHEETEXT record. */
+ virtual void WriteBody( XclExpStream& rStrm ) override;
+
+private:
+ const XclTabViewData& mrTabViewData; /// view settings data of current sheet.
+};
+
+// View settings ==============================================================
+
+/** Contains all view settings records for a single sheet. */
+class XclExpTabViewSettings : public XclExpRecordBase, protected XclExpRoot
+{
+public:
+ /** Creates all records containing the view settings of the specified sheet. */
+ explicit XclExpTabViewSettings( const XclExpRoot& rRoot, SCTAB nScTab );
+
+ /** Writes all view settings records to the stream. */
+ virtual void Save( XclExpStream& rStrm ) override;
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+
+private:
+ /** Creates selection data for the specified pane. */
+ void CreateSelectionData( sal_uInt8 nPane,
+ const ScAddress& rCursor, const ScRangeList& rSelection );
+
+ void WriteWindow2( XclExpStream& rStrm ) const;
+ void WriteScl( XclExpStream& rStrm ) const;
+ void WritePane( XclExpStream& rStrm ) const;
+ void WriteSelection( XclExpStream& rStrm, sal_uInt8 nPane ) const;
+ void WriteTabBgColor( XclExpStream& rStrm ) const;
+
+private:
+ XclTabViewData maData; /// All view settings for a sheet.
+ sal_uInt32 mnGridColorId; /// Color identifier for grid color.
+ bool mbHasTabSettings; /// It's false for embedded OLE spreadsheets.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xichart.hxx b/sc/source/filter/inc/xichart.hxx
new file mode 100644
index 000000000..e26ed4a0c
--- /dev/null
+++ b/sc/source/filter/inc/xichart.hxx
@@ -0,0 +1,1433 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <salhelper/simplereferenceobject.hxx>
+#include <set>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <types.hxx>
+#include "xlchart.hxx"
+#include "xlstyle.hxx"
+#include "xiescher.hxx"
+#include "xistring.hxx"
+
+namespace com::sun::star {
+ namespace awt
+ {
+ struct Rectangle;
+ }
+ namespace frame
+ {
+ class XModel;
+ }
+ namespace drawing
+ {
+ class XShape;
+ }
+ namespace chart2
+ {
+ struct ScaleData;
+ class XChartDocument;
+ class XDiagram;
+ class XCoordinateSystem;
+ class XChartType;
+ class XDataSeries;
+ class XRegressionCurve;
+ class XAxis;
+ class XLegend;
+ class XTitle;
+ class XFormattedString;
+ namespace data
+ {
+ class XDataProvider;
+ class XDataSequence;
+ class XLabeledDataSequence;
+ }
+ }
+}
+
+struct XclObjLineData;
+struct XclObjFillData;
+
+// Common =====================================================================
+
+struct XclImpChRootData;
+class XclImpChChart;
+class ScTokenArray;
+
+/** Base class for complex chart classes, provides access to other components of the chart. */
+class XclImpChRoot : public XclImpRoot
+{
+public:
+ explicit XclImpChRoot( const XclImpRoot& rRoot, XclImpChChart& rChartData );
+ virtual ~XclImpChRoot() override;
+
+ XclImpChRoot(XclImpChRoot const &) = default;
+ XclImpChRoot(XclImpChRoot &&) = default;
+ XclImpChRoot & operator =(XclImpChRoot const &) = delete; // due to XclImpRoot
+ XclImpChRoot & operator =(XclImpChRoot &&) = delete; // due to XclImpRoot
+
+ /** Returns this root instance - for code readability in derived classes. */
+ const XclImpChRoot& GetChRoot() const { return *this; }
+ /** Returns a reference to the parent chart data object. */
+ XclImpChChart& GetChartData() const;
+ /** Returns chart type info for a unique chart type identifier. */
+ const XclChTypeInfo& GetChartTypeInfo( XclChTypeId eType ) const;
+ /** Returns the first fitting chart type info for an Excel chart type record identifier. */
+ const XclChTypeInfo& GetChartTypeInfo( sal_uInt16 nRecId ) const;
+ /** Returns an info struct about auto formatting for the passed object type. */
+ const XclChFormatInfo& GetFormatInfo( XclChObjectType eObjType ) const;
+
+ /** Returns the default text color for charts. */
+ Color GetFontAutoColor() const;
+ /** Returns the automatic line color of linear series. */
+ Color GetSeriesLineAutoColor( sal_uInt16 nFormatIdx ) const;
+ /** Returns the automatic fill color of filled series. */
+ Color GetSeriesFillAutoColor( sal_uInt16 nFormatIdx ) const;
+
+ /** Starts the API chart document conversion. Must be called once before all API conversion. */
+ void InitConversion(
+ const css::uno::Reference< css::chart2::XChartDocument>& xChartDoc,
+ const tools::Rectangle& rChartRect ) const;
+
+ /** Finishes the API chart document conversion. Must be called once after all API conversion. */
+ void FinishConversion( XclImpDffConverter& rDffConv ) const;
+
+ /** Returns the data provider for the chart document. */
+ css::uno::Reference< css::chart2::data::XDataProvider >
+ GetDataProvider() const;
+ /** Returns the drawing shape interface of the specified title object. */
+ css::uno::Reference< css::drawing::XShape >
+ GetTitleShape( const XclChTextKey& rTitleKey ) const;
+
+ /** Converts the passed horizontal coordinate from Excel chart units into 1/100 mm. */
+ sal_Int32 CalcHmmFromChartX( sal_Int32 nPosX ) const;
+ /** Converts the passed vertical coordinate from Excel chart units into 1/100 mm. */
+ sal_Int32 CalcHmmFromChartY( sal_Int32 nPosY ) const;
+ /** Converts the passed rectangle from Excel chart units into 1/100 mm. */
+ css::awt::Rectangle CalcHmmFromChartRect( const XclChRectangle& rRect ) const;
+
+ /** Converts the passed horizontal coordinate from 1/100 mm into a relative position. */
+ double CalcRelativeFromHmmX( sal_Int32 nPosX ) const;
+ /** Converts the passed vertical coordinate from 1/100 mm into a relative position. */
+ double CalcRelativeFromHmmY( sal_Int32 nPosY ) const;
+
+ /** Converts the passed horizontal coordinate from Excel chart units into a relative position. */
+ double CalcRelativeFromChartX( sal_Int32 nPosX ) const;
+ /** Converts the passed vertical coordinate from Excel chart units into a relative position. */
+ double CalcRelativeFromChartY( sal_Int32 nPosY ) const;
+
+ /** Writes all line properties to the passed property set. */
+ void ConvertLineFormat(
+ ScfPropertySet& rPropSet,
+ const XclChLineFormat& rLineFmt,
+ XclChPropertyMode ePropMode ) const;
+ /** Writes solid area properties to the passed property set. */
+ void ConvertAreaFormat(
+ ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt,
+ XclChPropertyMode ePropMode ) const;
+ /** Writes gradient or bitmap area properties to the passed property set. */
+ void ConvertEscherFormat(
+ ScfPropertySet& rPropSet,
+ const XclChEscherFormat& rEscherFmt,
+ const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType,
+ XclChPropertyMode ePropMode ) const;
+ /** Writes font properties to the passed property set. */
+ void ConvertFont(
+ ScfPropertySet& rPropSet,
+ sal_uInt16 nFontIdx,
+ const Color* pFontColor = nullptr ) const;
+
+ /** Writes the pie rotation property for the passed angle. */
+ static void ConvertPieRotation(
+ ScfPropertySet& rPropSet,
+ sal_uInt16 nAngle );
+
+private:
+ typedef std::shared_ptr< XclImpChRootData > XclImpChRootDataRef;
+ XclImpChRootDataRef mxChData; /// Reference to the root data object.
+};
+
+/** Base class for chart record groups. Provides helper functions to read sub records.
+
+ A chart record group consists of a header record, followed by a CHBEGIN
+ record, followed by group sub records, and finished with a CHEND record.
+ */
+class XclImpChGroupBase
+{
+public:
+ XclImpChGroupBase() = default;
+ XclImpChGroupBase(XclImpChGroupBase const &) = default;
+ XclImpChGroupBase(XclImpChGroupBase &&) = default;
+ XclImpChGroupBase & operator =(XclImpChGroupBase const &) = default;
+ XclImpChGroupBase & operator =(XclImpChGroupBase &&) = default;
+
+ virtual ~XclImpChGroupBase();
+
+ /** Reads the entire record group.
+ @descr First calls ReadHeaderRecord() to read the contents of the
+ header record. Then tries to read the sub records. If next record
+ is a CHBEGIN record, ReadSubRecord() is called for each following
+ record until a CHEND record is found. */
+ void ReadRecordGroup( XclImpStream& rStrm );
+
+ /** Helper to skip a CHBEGIN/CHEND block, includes nested blocks. */
+ static void SkipBlock( XclImpStream& rStrm );
+
+ /** Derived classes implement to read the group header record. */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) = 0;
+ /** Derived classes implement to read a record from the group. */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) = 0;
+};
+
+// Frame formatting ===========================================================
+
+class XclImpChFramePos
+{
+public:
+ /** Reads the CHFRAMEPOS record (frame position and size). */
+ void ReadChFramePos( XclImpStream& rStrm );
+
+ /** Returns read-only access to the imported frame position data. */
+ const XclChFramePos& GetFramePosData() const { return maData; }
+
+private:
+ XclChFramePos maData; /// Position of the frame.
+};
+
+typedef std::shared_ptr< XclImpChFramePos > XclImpChFramePosRef;
+
+/** The CHLINEFORMAT record containing line formatting data. */
+class XclImpChLineFormat : public salhelper::SimpleReferenceObject
+{
+public:
+ /** Creates a new line format object with automatic formatting. */
+ explicit XclImpChLineFormat() {}
+ /** Creates a new line format object with the passed formatting. */
+ explicit XclImpChLineFormat( const XclChLineFormat& rLineFmt ) : maData( rLineFmt ) {}
+
+ // this class is stored both ref-counted and by value
+ XclImpChLineFormat(XclImpChLineFormat const & rOther)
+ : salhelper::SimpleReferenceObject(), maData(rOther.maData) {}
+ XclImpChLineFormat(XclImpChLineFormat && rOther)
+ : salhelper::SimpleReferenceObject(), maData(std::move(rOther.maData)) {}
+ XclImpChLineFormat& operator=(XclImpChLineFormat const & rOther)
+ { maData = rOther.maData; return *this; }
+ XclImpChLineFormat& operator=(XclImpChLineFormat && rOther) noexcept
+ { maData = std::move(rOther.maData); return *this; }
+
+ /** Reads the CHLINEFORMAT record (basic line properties). */
+ void ReadChLineFormat( XclImpStream& rStrm );
+
+ /** Returns true, if the line format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO ); }
+ /** Returns true, if the line style is set to something visible. */
+ bool HasLine() const { return IsAuto() || (maData.mnPattern != EXC_CHLINEFORMAT_NONE); }
+ /** Returns the line width of this line format (returns 'single', if the line is invisible). */
+ sal_Int16 GetWeight() const { return (IsAuto() || !HasLine()) ? EXC_CHLINEFORMAT_SINGLE : maData.mnWeight; }
+ /** Returns true, if the "show axis" flag is set. */
+ bool IsShowAxis() const { return ::get_flag( maData.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS ); }
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx = EXC_CHDATAFORMAT_UNKNOWN ) const;
+
+private:
+ XclChLineFormat maData; /// Contents of the CHLINEFORMAT record.
+};
+
+typedef rtl::Reference< XclImpChLineFormat > XclImpChLineFormatRef;
+
+/** The CHAREAFORMAT record containing simple area formatting data (solid or patterns). */
+class XclImpChAreaFormat
+{
+public:
+ /** Creates a new area format object with automatic formatting. */
+ explicit XclImpChAreaFormat() {}
+ /** Creates a new area format object with the passed formatting. */
+ explicit XclImpChAreaFormat( const XclChAreaFormat& rAreaFmt ) : maData( rAreaFmt ) {}
+
+ /** Reads the CHAREAFORMAT record (basic fill properties, e.g. transparent or colored). */
+ void ReadChAreaFormat( XclImpStream& rStrm );
+
+ /** Returns true, if the area format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO ); }
+ /** Returns true, if the area style is set to something visible. */
+ bool HasArea() const { return IsAuto() || (maData.mnPattern != EXC_PATT_NONE); }
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx ) const;
+
+private:
+ XclChAreaFormat maData; /// Contents of the CHAREAFORMAT record.
+};
+
+typedef std::shared_ptr< XclImpChAreaFormat > XclImpChAreaFormatRef;
+
+/** The CHESCHERFORMAT record containing complex area formatting data (bitmaps, hatches). */
+class XclImpChEscherFormat : public XclImpChGroupBase
+{
+public:
+ explicit XclImpChEscherFormat( const XclImpRoot& rRoot );
+
+ /** Reads the CHESCHERFORMAT record (complex fill data) (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHESCHERFORMAT group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet,
+ XclChObjectType eObjType, bool bUsePicFmt ) const;
+
+private:
+ XclChEscherFormat maData; /// Fill properties for complex areas (CHESCHERFORMAT record).
+ XclChPicFormat maPicFmt; /// Image options, e.g. stretched, stacked (CHPICFORMAT record).
+ sal_uInt32 mnDffFillType; /// Fill type imported from the DFF property set.
+};
+
+typedef std::shared_ptr< XclImpChEscherFormat > XclImpChEscherFormatRef;
+
+/** Base class for record groups containing frame formatting.
+
+ Frame formatting can be part of several record groups, e.g. CHFRAME,
+ CHDATAFORMAT, CHDROPBAR. It consists of CHLINEFORMAT, CHAREAFORMAT, and
+ CHESCHERFORMAT group.
+ */
+class XclImpChFrameBase : public XclImpChGroupBase
+{
+public:
+ /** Creates a new frame object without internal formatting objects. */
+ explicit XclImpChFrameBase() {}
+ /** Creates a new frame object with specific default formatting. */
+ explicit XclImpChFrameBase( const XclChFormatInfo& rFmtInfo );
+
+ /** Reads a frame formatting record (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Returns true, if the line format is set to automatic. */
+ bool IsAutoLine() const { return !mxLineFmt || mxLineFmt->IsAuto(); }
+ /** Returns true, if the line style is set to something visible. */
+ bool HasLine() const { return IsAutoLine() || mxLineFmt->HasLine(); }
+ /** Returns the line weight used for this frame. */
+ sal_Int16 GetLineWeight() const { return mxLineFmt ? mxLineFmt->GetWeight() : EXC_CHLINEFORMAT_SINGLE; }
+
+ /** Returns true, if the area format is set to automatic. */
+ bool IsAutoArea() const { return !mxEscherFmt && (!mxAreaFmt || mxAreaFmt->IsAuto()); }
+
+protected:
+ /** Converts and writes the contained line formatting to the passed property set. */
+ void ConvertLineBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx = EXC_CHDATAFORMAT_UNKNOWN ) const;
+ /** Converts and writes the contained area formatting to the passed property set. */
+ void ConvertAreaBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx = EXC_CHDATAFORMAT_UNKNOWN, bool bUsePicFmt = false ) const;
+ /** Converts and writes the contained data to the passed property set. */
+ void ConvertFrameBase( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, XclChObjectType eObjType,
+ sal_uInt16 nFormatIdx = EXC_CHDATAFORMAT_UNKNOWN, bool bUsePicFmt = false ) const;
+
+protected:
+ XclImpChLineFormatRef mxLineFmt; /// Line format (CHLINEFORMAT record).
+ XclImpChAreaFormatRef mxAreaFmt; /// Area format (CHAREAFORMAT record).
+ XclImpChEscherFormatRef mxEscherFmt; /// Complex area format (CHESCHERFORMAT record).
+};
+
+/** Represents the CHFRAME record group containing object frame properties.
+
+ The CHFRAME group consists of: CHFRAME, CHBEGIN, CHLINEFORMAT,
+ CHAREAFORMAT, CHESCHERFORMAT group, CHEND.
+ */
+class XclImpChFrame : public XclImpChFrameBase, protected XclImpChRoot
+{
+public:
+ /** Creates a new frame object with specific default formatting. */
+ explicit XclImpChFrame(
+ const XclImpChRoot& rRoot,
+ XclChObjectType eObjType );
+
+ /** Reads the CHFRAME record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+
+ /** Sets formatting from BIFF3-BIFF5 OBJ record, if own formatting is invisible. */
+ void UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData );
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet, bool bUsePicFmt = false ) const;
+
+private:
+ XclChFrame maData; /// Contents of the CHFRAME record.
+ XclChObjectType meObjType; /// Type of the represented object.
+};
+
+typedef std::shared_ptr< XclImpChFrame > XclImpChFrameRef;
+
+// Source links ===============================================================
+
+class XclImpChSourceLink : protected XclImpChRoot
+{
+public:
+ explicit XclImpChSourceLink( const XclImpChRoot& rRoot );
+ virtual ~XclImpChSourceLink() override;
+
+ /** Reads the CHSOURCELINK record (link to source data). */
+ void ReadChSourceLink( XclImpStream& rStrm );
+ /** Sets explicit string data for this text object. */
+ void SetString( const OUString& rString );
+ /** Sets formatting runs read from a CHFORMATRUNS record. */
+ void SetTextFormats( XclFormatRunVec&& rFormats );
+
+ /** Returns the destination object (title, values, category, ...). */
+ sal_uInt8 GetDestType() const { return maData.mnDestType; }
+ /** Returns the link type (to worksheet, directly, default, ...). */
+ sal_uInt8 GetLinkType() const { return maData.mnLinkType; }
+
+ /** Returns true, if the source link contains explicit string data. */
+ bool HasString() const { return mxString && !mxString->IsEmpty(); }
+ /** Returns explicit string data or an empty string. */
+ OUString GetString() const {
+ if (mxString) return mxString->GetText();
+ return OUString();
+ }
+ /** Returns the number of data points of this source link. */
+ sal_uInt16 GetCellCount() const;
+
+ /** Converts and writes the contained number format to the passed property set. */
+ void ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const;
+
+ /** Creates a data sequence containing the link into the Calc document. */
+ css::uno::Reference< css::chart2::data::XDataSequence >
+ CreateDataSequence( const OUString& rRole ) const;
+ /** Creates a sequence of formatted string objects. */
+ css::uno::Sequence< css::uno::Reference< css::chart2::XFormattedString > >
+ CreateStringSequence( const XclImpChRoot& rRoot,
+ sal_uInt16 nLeadFontIdx, const Color& rLeadFontColor ) const;
+
+ void FillSourceLink(::std::vector<ScTokenRef>& rTokens) const;
+
+private:
+ XclChSourceLink maData; /// Contents of the CHSOURCELINK record.
+ XclImpStringRef mxString; /// Text data (CHSTRING record).
+ std::shared_ptr< ScTokenArray> mxTokenArray; /// Token array representing the data ranges.
+};
+
+typedef std::shared_ptr< XclImpChSourceLink > XclImpChSourceLinkRef;
+
+// Text =======================================================================
+
+/** Base class for objects with font settings. Provides font conversion helper functions. */
+class XclImpChFontBase
+{
+public:
+ XclImpChFontBase() = default;
+ XclImpChFontBase(XclImpChFontBase const &) = default;
+ XclImpChFontBase(XclImpChFontBase &&) = default;
+ XclImpChFontBase & operator =(XclImpChFontBase const &) = default;
+ XclImpChFontBase & operator =(XclImpChFontBase &&) = default;
+
+ virtual ~XclImpChFontBase();
+
+ /** Derived classes return the leading font index for the text object. */
+ virtual sal_uInt16 GetFontIndex() const = 0;
+ /** Derived classes return the leading font color for the text object. */
+ virtual Color GetFontColor() const = 0;
+ /** Derived classes return the rotation value for the text object. */
+ virtual sal_uInt16 GetRotation() const = 0;
+
+ /** Converts and writes the contained font settings to the passed property set. */
+ void ConvertFontBase( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const;
+ /** Converts and writes the contained rotation settings to the passed property set. */
+ void ConvertRotationBase( ScfPropertySet& rPropSet, bool bSupportsStacked ) const;
+};
+
+/** The CHFONT record containing a font index for text objects. */
+class XclImpChFont
+{
+public:
+ explicit XclImpChFont();
+ /** Reads the CHFONT record (font index). */
+ void ReadChFont( XclImpStream& rStrm );
+
+ /** Returns the contained font index. */
+ sal_uInt16 GetFontIndex() const { return mnFontIdx; }
+
+private:
+ sal_uInt16 mnFontIdx; /// Index into font buffer.
+};
+
+typedef std::shared_ptr< XclImpChFont > XclImpChFontRef;
+
+/** Represents the CHTEXT record group containing text object properties.
+
+ The CHTEXT group consists of: CHTEXT, CHBEGIN, CHFRAMEPOS, CHFONT,
+ CHFORMATRUNS, CHSOURCELINK, CHSTRING, CHFRAME group, CHOBJECTLINK, and CHEND.
+ */
+class XclImpChText : public XclImpChGroupBase, public XclImpChFontBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChText( const XclImpChRoot& rRoot );
+
+ /** Reads the CHTEXT record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHTEXT group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Returns the leading font index for the text object. */
+ virtual sal_uInt16 GetFontIndex() const override;
+ /** Returns the leading font color for the text object. */
+ virtual Color GetFontColor() const override;
+ /** Returns the rotation value for the text object. */
+ virtual sal_uInt16 GetRotation() const override;
+
+ /** Sets explicit string data for this text object. */
+ void SetString( const OUString& rString );
+ /** Updates missing parts of this text object from the passed object. */
+ void UpdateText( const XclImpChText* pParentText );
+ /** Updates display type of this data point label text object. */
+ void UpdateDataLabel( bool bCateg, bool bValue, bool bPercent );
+
+ /** Returns the target object this text is linked to. */
+ sal_uInt16 GetLinkTarget() const { return maObjLink.mnTarget; }
+ /** Returns the position of the data point label this text is linked to. */
+ const XclChDataPointPos& GetPointPos() const { return maObjLink.maPointPos; }
+ /** Returns true, if this text group contains string data. */
+ bool HasString() const { return mxSrcLink && mxSrcLink->HasString(); }
+ /** Returns true, if the text object is marked as deleted. */
+ bool IsDeleted() const { return ::get_flag( maData.mnFlags, EXC_CHTEXT_DELETED ); }
+
+ /** Converts and writes the contained font settings to the passed property set. */
+ void ConvertFont( ScfPropertySet& rPropSet ) const;
+ /** Converts and writes the contained rotation settings to the passed property set. */
+ void ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const;
+ /** Converts and writes the contained frame data to the passed property set. */
+ void ConvertFrame( ScfPropertySet& rPropSet ) const;
+ /** Converts and writes the contained number format to the passed property set. */
+ void ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const;
+ /** Converts and writes all contained data to the passed data point label property set. */
+ void ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const;
+ /** Creates a title text object. */
+ css::uno::Reference< css::chart2::XTitle >
+ CreateTitle() const;
+ /** Converts the manual position of the specified title */
+ void ConvertTitlePosition( const XclChTextKey& rTitleKey ) const;
+
+private:
+ using XclImpChRoot::ConvertFont;
+
+ /** Reads a CHFRLABELPROPS record. */
+ void ReadChFrLabelProps( XclImpStream& rStrm );
+
+private:
+ typedef std::shared_ptr< XclChFrLabelProps > XclChFrLabelPropsRef;
+
+ XclChText maData; /// Contents of the CHTEXT record.
+ XclChObjectLink maObjLink; /// Link target for this text object.
+ XclFormatRunVec maFormats; /// Formatting runs (CHFORMATRUNS record).
+ XclImpChFramePosRef mxFramePos; /// Relative text frame position (CHFRAMEPOS record).
+ XclImpChSourceLinkRef mxSrcLink; /// Linked data (CHSOURCELINK with CHSTRING record).
+ XclImpChFrameRef mxFrame; /// Text object frame properties (CHFRAME group).
+ XclImpChFontRef mxFont; /// Index into font buffer (CHFONT record).
+ XclChFrLabelPropsRef mxLabelProps; /// Extended data label properties (CHFRLABELPROPS record).
+};
+
+typedef std::shared_ptr< XclImpChText > XclImpChTextRef;
+
+// Data series ================================================================
+
+/** The CHMARKERFORMAT record containing data point marker formatting data. */
+class XclImpChMarkerFormat
+{
+public:
+ /** Reads the CHMARKERFORMAT record (data point marker properties). */
+ void ReadChMarkerFormat( XclImpStream& rStrm );
+
+ /** Returns true, if the marker format is set to automatic. */
+ bool IsAuto() const { return ::get_flag( maData.mnFlags, EXC_CHMARKERFORMAT_AUTO ); }
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet,
+ sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const;
+ /** Sets the marker fill color as main color to the passed property set. */
+ void ConvertColor( const XclImpChRoot& rRoot,
+ ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const;
+
+private:
+ XclChMarkerFormat maData; /// Contents of the CHMARKERFORMAT record.
+};
+
+typedef std::shared_ptr< XclImpChMarkerFormat > XclImpChMarkerFormatRef;
+
+/** The CHPIEFORMAT record containing data point formatting data for pie segments. */
+class XclImpChPieFormat
+{
+public:
+ explicit XclImpChPieFormat();
+ /** Reads the CHPIEFORMAT record (pie segment properties). */
+ void ReadChPieFormat( XclImpStream& rStrm );
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet ) const;
+
+private:
+ sal_uInt16 mnPieDist; /// Pie distance to diagram center.
+};
+
+typedef std::shared_ptr< XclImpChPieFormat > XclImpChPieFormatRef;
+
+/** The CHSERIESFORMAT record containing additional settings for a data series. */
+class XclImpChSeriesFormat
+{
+public:
+ explicit XclImpChSeriesFormat();
+ /** Reads the CHSERIESFORMAT record (additional settings for a series). */
+ void ReadChSeriesFormat( XclImpStream& rStrm );
+ /** Returns true, if the series line is smoothed. */
+ bool HasSpline() const { return ::get_flag( mnFlags, EXC_CHSERIESFORMAT_SMOOTHED ); }
+
+private:
+ sal_uInt16 mnFlags; /// Additional flags.
+};
+
+typedef std::shared_ptr< XclImpChSeriesFormat > XclImpChSeriesFormatRef;
+
+/** The CH3DDATAFORMAT record containing the bar type in 3D bar charts. */
+class XclImpCh3dDataFormat
+{
+public:
+ /** Reads the CH3DDATAFORMAT record (3D bar properties). */
+ void ReadCh3dDataFormat( XclImpStream& rStrm );
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet ) const;
+
+private:
+ XclCh3dDataFormat maData; /// Contents of the CH3DDATAFORMAT record.
+};
+
+typedef std::shared_ptr< XclImpCh3dDataFormat > XclImpCh3dDataFormatRef;
+
+/** The CHATTACHEDLABEL record that contains the type of a data point label. */
+class XclImpChAttachedLabel : protected XclImpChRoot
+{
+public:
+ explicit XclImpChAttachedLabel( const XclImpChRoot& rRoot );
+ /** Reads the CHATTACHEDLABEL record (data series/point labels). */
+ void ReadChAttachedLabel( XclImpStream& rStrm );
+ /** Creates a CHTEXT group for the label. Clones xParentText and sets additional label settings */
+ XclImpChTextRef CreateDataLabel( const XclImpChText* pParent ) const;
+
+private:
+ sal_uInt16 mnFlags; /// Additional flags.
+};
+
+typedef std::shared_ptr< XclImpChAttachedLabel > XclImpChAttLabelRef;
+
+/** Represents the CHDATAFORMAT record group containing data point properties.
+
+ The CHDATAFORMAT group consists of: CHDATAFORMAT, CHBEGIN, CHFRAME group,
+ CHMARKERFORMAT, CHPIEFORMAT, CH3DDATAFORMAT, CHSERIESFORMAT,
+ CHATTACHEDLABEL, CHEND.
+ */
+class XclImpChDataFormat : public XclImpChFrameBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChDataFormat( const XclImpChRoot& rRoot );
+
+ /** Reads the CHDATAFORMAT record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHDATAFORMAT group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Sets this object to the specified data point position. */
+ void SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx );
+ /** Sets type and text formatting for a data point label (CHTEXT group). */
+ void SetDataLabel( XclImpChTextRef xLabel ) { mxLabel = xLabel; }
+
+ /** Updates default data format for series group. */
+ void UpdateGroupFormat( const XclChExtTypeInfo& rTypeInfo );
+ /** Updates missing series settings from the passed chart type group data format. */
+ void UpdateSeriesFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pGroupFmt );
+ /** Updates missing data point settings from the passed series format. */
+ void UpdatePointFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pSeriesFmt );
+ /** Updates default data format for trend lines. */
+ void UpdateTrendLineFormat();
+
+ /** Returns the position of the data point described by this group. */
+ const XclChDataPointPos& GetPointPos() const { return maData.maPointPos; }
+ /** Returns the format index of the data point described by this group. */
+ sal_uInt16 GetFormatIdx() const { return maData.mnFormatIdx; }
+ /** Returns true, if markers are set to automatic format. */
+ bool IsAutoMarker() const { return !mxMarkerFmt || mxMarkerFmt->IsAuto(); }
+ /** Returns true, if the series line is smoothed. */
+ bool HasSpline() const { return mxSeriesFmt && mxSeriesFmt->HasSpline(); }
+ /** Returns the data label text object. */
+ const XclImpChText* GetDataLabel() const { return mxLabel.get(); }
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet = nullptr ) const;
+ /** Writes the line format only, e.g. for trend lines or error bars. */
+ void ConvertLine( ScfPropertySet& rPropSet, XclChObjectType eObjType ) const;
+ /** Writes the area format only for the series or a data point. */
+ void ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const;
+
+private:
+ /** Removes unused formatting (e.g. pie distance in a bar chart). */
+ void RemoveUnusedFormats( const XclChExtTypeInfo& rTypeInfo );
+ /** Updates or creates the data point label. */
+ void UpdateDataLabel( const XclImpChDataFormat* pParentFmt );
+
+private:
+ XclChDataFormat maData; /// Contents of the CHDATAFORMAT record.
+ XclImpChMarkerFormatRef mxMarkerFmt; /// Data point marker (CHMARKERFORMAT record).
+ XclImpChPieFormatRef mxPieFmt; /// Pie segment format (CHPIEFORMAT record).
+ XclImpChSeriesFormatRef mxSeriesFmt; /// Series properties (CHSERIESFORMAT record).
+ XclImpCh3dDataFormatRef mx3dDataFmt; /// 3D bar format (CH3DDATAFORMAT record).
+ XclImpChAttLabelRef mxAttLabel; /// Data point label type (CHATTACHEDLABEL record).
+ XclImpChTextRef mxLabel; /// Data point label formatting (CHTEXT group).
+};
+
+typedef std::shared_ptr< XclImpChDataFormat > XclImpChDataFormatRef;
+
+/** Represents the CHSERTRENDLINE record containing settings for a trend line. */
+class XclImpChSerTrendLine : protected XclImpChRoot
+{
+public:
+ explicit XclImpChSerTrendLine( const XclImpChRoot& rRoot );
+
+ /** Reads the CHSERTRENDLINE record. */
+ void ReadChSerTrendLine( XclImpStream& rStrm );
+ /** Sets formatting information for the trend line. */
+ void SetDataFormat( XclImpChDataFormatRef xDataFmt ) { mxDataFmt = xDataFmt; }
+
+ void SetTrendlineName( const OUString& aTrendlineName) { maTrendLineName = aTrendlineName; }
+
+ /** Creates an API object representing this trend line. */
+ css::uno::Reference< css::chart2::XRegressionCurve >
+ CreateRegressionCurve() const;
+
+private:
+ OUString maTrendLineName;
+ XclChSerTrendLine maData; /// Contents of the CHSERTRENDLINE record.
+ XclImpChDataFormatRef mxDataFmt; /// Formatting settings of the trend line.
+};
+
+typedef std::shared_ptr< XclImpChSerTrendLine > XclImpChSerTrendLineRef;
+
+/** Represents the CHSERERRORBAR record containing settings for error bars. */
+class XclImpChSerErrorBar : protected XclImpChRoot
+{
+public:
+ explicit XclImpChSerErrorBar( const XclImpChRoot& rRoot );
+
+ /** Reads the CHSERERRORBAR record. */
+ void ReadChSerErrorBar( XclImpStream& rStrm );
+ /** Sets link and formatting information for the error bars. */
+ void SetSeriesData(
+ XclImpChSourceLinkRef const & xValueLink,
+ XclImpChDataFormatRef const & xDataFmt );
+
+ /** Returns the type of this error bar (X/Y, plus/minus). */
+ sal_uInt8 GetBarType() const { return maData.mnBarType; }
+ /** Creates a labeled data sequence object from value data link. */
+ css::uno::Reference< css::chart2::data::XLabeledDataSequence >
+ CreateValueSequence() const;
+
+ /** Tries to create an error bar API object from the specified Excel error bars. */
+ static css::uno::Reference< css::beans::XPropertySet >
+ CreateErrorBar(
+ const XclImpChSerErrorBar* pPosBar,
+ const XclImpChSerErrorBar* pNegBar );
+
+private:
+ XclChSerErrorBar maData; /// Contents of the CHSERERRORBAR record.
+ XclImpChSourceLinkRef mxValueLink; /// Link data for manual error bar values.
+ XclImpChDataFormatRef mxDataFmt; /// Formatting settings of the error bars.
+};
+
+
+/** Represents the CHSERIES record group describing a data series in a chart.
+
+ The CHSERIES group consists of: CHSERIES, CHBEGIN, CHSOURCELINK groups,
+ CHDATAFORMAT groups, CHSERGROUP, CHSERPARENT, CHSERERRORBAR,
+ CHSERTRENDLINE, CHEND.
+ */
+class XclImpChSeries : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx );
+
+ /** Reads the CHSERIES record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHSERIES group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+
+ /** Sets a data point or series format (CHDATAFORMAT group) for this series. */
+ void SetDataFormat( const XclImpChDataFormatRef& xDataFmt );
+ /** Sets a label text (CHTEXT group) attached to a series or data point. */
+ void SetDataLabel( const XclImpChTextRef& xLabel );
+ /** Adds error bar settings from the passed series to the own series. */
+ void AddChildSeries( const XclImpChSeries& rSeries );
+ /** Updates missing series formatting by using default formatting from axes sets. */
+ void FinalizeDataFormats();
+
+ /** Returns the axes set identifier this series is assigned to (primary/secondary). */
+ sal_uInt16 GetGroupIdx() const { return mnGroupIdx; }
+ /** Returns the 0-based index of the parent series (e.g. of a trend line). */
+ sal_uInt16 GetParentIdx() const { return mnParentIdx; }
+ /** Returns true, if the series is child of another series (e.g. trend line). */
+ bool HasParentSeries() const { return mnParentIdx != EXC_CHSERIES_INVALID; }
+ /** Returns true, if the series contains child series (e.g. trend lines). */
+ bool HasChildSeries() const { return !maTrendLines.empty() || !m_ErrorBars.empty(); }
+ /** Returns series title or an empty string, if the series does not contain a title. */
+ OUString GetTitle() const { return mxTitleLink ? mxTitleLink->GetString() : OUString(); }
+
+ /** Returns true, if the series line is smoothed. */
+ bool HasSpline() const { return mxSeriesFmt && mxSeriesFmt->HasSpline(); }
+
+ /** Creates a labeled data sequence object from value data link. */
+ css::uno::Reference< css::chart2::data::XLabeledDataSequence >
+ CreateValueSequence( const OUString& rValueRole ) const;
+ /** Creates a labeled data sequence object from category data link. */
+ css::uno::Reference< css::chart2::data::XLabeledDataSequence >
+ CreateCategSequence( const OUString& rCategRole ) const;
+ /** Creates a data series object with initialized source links. */
+ css::uno::Reference< css::chart2::XDataSeries >
+ CreateDataSeries() const;
+
+ void FillAllSourceLinks(::std::vector<ScTokenRef>& rTokens) const;
+
+private:
+ /** Reads a CHSOURCELINK record. */
+ void ReadChSourceLink( XclImpStream& rStrm );
+ /** Reads a CHDATAFORMAT group containing series and point formatting. */
+ void ReadChDataFormat( XclImpStream& rStrm );
+ /** Reads a CHSERPARENT record specifying the parent series of this series. */
+ void ReadChSerParent( XclImpStream& rStrm );
+ /** Reads a CHSERTRENDLINE record containing trend line settings. */
+ void ReadChSerTrendLine( XclImpStream& rStrm );
+ /** Reads a CHSERERRORBAR record containing error bar settings. */
+ void ReadChSerErrorBar( XclImpStream& rStrm );
+
+ void ReadChLegendException( XclImpStream& rStrm );
+ /** Creates a new CHDATAFORMAT group with the specified point index. */
+ XclImpChDataFormatRef CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx );
+
+ /** Converts all trend lines and inserts them into the passed API data series object. */
+ void ConvertTrendLines( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries ) const;
+ /** Tries to create an error bar API object from the specified Excel error bars. */
+ css::uno::Reference< css::beans::XPropertySet >
+ CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const;
+
+private:
+ typedef ::std::map<sal_uInt16, XclImpChDataFormatRef> XclImpChDataFormatMap;
+ typedef ::std::map<sal_uInt16, XclImpChTextRef> XclImpChTextMap;
+ typedef ::std::map<sal_uInt8, std::unique_ptr<XclImpChSerErrorBar>> XclImpChSerErrorBarMap;
+
+ XclChSeries maData; /// Contents of the CHSERIES record.
+ XclImpChSourceLinkRef mxValueLink; /// Link data for series values.
+ XclImpChSourceLinkRef mxCategLink; /// Link data for series category names.
+ XclImpChSourceLinkRef mxTitleLink; /// Link data for series title.
+ XclImpChSourceLinkRef mxBubbleLink; /// Link data for series bubble sizes.
+ XclImpChDataFormatRef mxSeriesFmt; /// CHDATAFORMAT group for series format.
+ XclImpChDataFormatMap maPointFmts; /// CHDATAFORMAT groups for data point formats.
+ XclImpChTextMap maLabels; /// Data point labels (CHTEXT groups).
+ std::vector< XclImpChSerTrendLineRef > maTrendLines; /// Trend line settings (CHSERTRENDLINE records).
+ XclImpChSerErrorBarMap m_ErrorBars; /// Error bar settings (CHSERERRORBAR records).
+ sal_uInt16 mnGroupIdx; /// Chart type group (CHTYPEGROUP group) this series is assigned to.
+ sal_uInt16 mnSeriesIdx; /// 0-based series index.
+ sal_uInt16 mnParentIdx; /// 0-based index of parent series (trend lines and error bars).
+ bool mbLabelDeleted; /// Legend label deleted
+};
+
+typedef std::shared_ptr< XclImpChSeries > XclImpChSeriesRef;
+
+// Chart type groups ==========================================================
+
+class XclImpChType : protected XclImpChRoot
+{
+public:
+ explicit XclImpChType( const XclImpChRoot& rRoot );
+
+ /** Reads a chart type record (e.g. CHBAR, CHLINE, CHPIE, ...). */
+ void ReadChType( XclImpStream& rStrm );
+ /** Final processing after reading the entire chart. */
+ void Finalize( bool bStockChart );
+
+ /** Returns the record identifier of the chart type record. */
+ sal_uInt16 GetRecId() const { return mnRecId; }
+ /** Returns the chart type info struct for the contained chart type. */
+ const XclChTypeInfo& GetTypeInfo() const { return maTypeInfo; }
+ /** Returns true, if the series in this chart type group are stacked on each other (no percentage). */
+ bool IsStacked() const;
+ /** Returns true, if the series in this chart type group are stacked on each other as percentage. */
+ bool IsPercent() const;
+ /** Returns true, if chart type has category labels enabled (may be disabled in radar charts). */
+ bool HasCategoryLabels() const;
+
+ /** Creates a coordinate system according to the contained chart type. */
+ css::uno::Reference< css::chart2::XCoordinateSystem >
+ CreateCoordSystem( bool b3dChart ) const;
+ /** Creates and returns an object that represents the contained chart type. */
+ css::uno::Reference< css::chart2::XChartType >
+ CreateChartType( css::uno::Reference< css::chart2::XDiagram > const & xDiagram, bool b3dChart ) const;
+
+private:
+ XclChType maData; /// Contents of the chart type record.
+ sal_uInt16 mnRecId; /// Record identifier for chart type.
+ XclChTypeInfo maTypeInfo; /// Chart type info for the contained type.
+};
+
+/** Represents the CHCHART3D record that contains 3D view settings. */
+class XclImpChChart3d
+{
+public:
+ /** Reads the CHCHART3D record (properties for 3D charts). */
+ void ReadChChart3d( XclImpStream& rStrm );
+ /** Returns true, if the data points are clustered on the X axis. */
+ bool IsClustered() const { return ::get_flag( maData.mnFlags, EXC_CHCHART3D_CLUSTER ); }
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const;
+
+private:
+ XclChChart3d maData; /// Contents of the CHCHART3D record.
+};
+
+typedef std::shared_ptr< XclImpChChart3d > XclImpChChart3dRef;
+
+/** Represents the CHLEGEND record group describing the chart legend.
+
+ The CHLEGEND group consists of: CHLEGEND, CHBEGIN, CHFRAMEPOS, CHFRAME
+ group, CHTEXT group, CHEND.
+ */
+class XclImpChLegend : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChLegend( const XclImpChRoot& rRoot );
+
+ /** Reads the CHLEGEND record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHLEGEND group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Final processing after reading the entire chart. */
+ void Finalize();
+
+ /** Creates a new legend object. */
+ css::uno::Reference< css::chart2::XLegend >
+ CreateLegend() const;
+
+private:
+ XclChLegend maData; /// Contents of the CHLEGEND record.
+ XclImpChFramePosRef mxFramePos; /// Legend frame position (CHFRAMEPOS record).
+ XclImpChTextRef mxText; /// Legend text format (CHTEXT group).
+ XclImpChFrameRef mxFrame; /// Legend frame format (CHFRAME group).
+};
+
+typedef std::shared_ptr< XclImpChLegend > XclImpChLegendRef;
+
+/** Represents the CHDROPBAR record group describing pos/neg bars in line charts.
+
+ The CHDROPBAR group consists of: CHDROPBAR, CHBEGIN, CHLINEFORMAT,
+ CHAREAFORMAT, CHESCHERFORMAT group, CHEND.
+ */
+class XclImpChDropBar : public XclImpChFrameBase
+{
+public:
+ explicit XclImpChDropBar( sal_uInt16 nDropBar );
+
+ /** Reads the CHDROPBAR record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+
+ /** Converts and writes the contained frame data to the passed property set. */
+ void Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const;
+
+private:
+ sal_uInt16 mnDropBar; /// Drop bar identifier, needed for auto format.
+ sal_uInt16 mnBarDist; /// Distance between bars (CHDROPBAR record).
+};
+
+
+/** Represents the CHTYPEGROUP record group describing a group of series.
+
+ The CHTYPEGROUP group consists of: CHTYPEGROUP, CHBEGIN, a chart type
+ record (e.g. CHBAR, CHLINE, CHAREA, CHPIE, ...), CHCHART3D, CHLEGEND group,
+ CHDEFAULTTEXT groups (CHDEFAULTTEXT with CHTEXT groups), CHDROPBAR groups,
+ CHCHARTLINE groups (CHCHARTLINE with CHLINEFORMAT), CHDATAFORMAT group,
+ CHEND.
+ */
+class XclImpChTypeGroup : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChTypeGroup( const XclImpChRoot& rRoot );
+
+ /** Reads the CHTYPEGROUP record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHTYPEGROUP group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Final processing after reading the entire chart. */
+ void Finalize();
+
+ /** Inserts a series attached to this chart type group.*/
+ void AddSeries( XclImpChSeriesRef const & xSeries );
+ /** Marks the passed format index as used. PopUnusedFormatIndex() will not return this index. */
+ void SetUsedFormatIndex( sal_uInt16 nFormatIdx );
+ /** Returns the next unused format index and marks it as used. */
+ sal_uInt16 PopUnusedFormatIndex();
+
+ /** Returns the index of this chart type group. */
+ sal_uInt16 GetGroupIdx() const { return maData.mnGroupIdx; }
+ /** Returns the chart type info struct for the contained chart type. */
+ const XclChExtTypeInfo& GetTypeInfo() const { return maTypeInfo; }
+ /** Returns true, if this chart type group contains at least one valid series. */
+ bool IsValidGroup() const { return !maSeries.empty(); }
+ /** Returns true, if the series in this chart type group are stacked on each other as percentage. */
+ bool IsPercent() const { return maType.IsPercent(); }
+ /** Returns true, if the chart is three-dimensional. */
+ bool Is3dChart() const { return mxChart3d && maTypeInfo.mbSupports3d; }
+ /** Returns true, if chart type supports wall and floor format in 3d mode. */
+ bool Is3dWallChart() const { return Is3dChart() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE); }
+ /** Returns true, if the series in this chart type group are ordered on the Z axis. */
+ bool Is3dDeepChart() const { return Is3dWallChart() && mxChart3d && !mxChart3d->IsClustered(); }
+ /** Returns true, if category (X axis) labels are enabled (may be disabled in radar charts). */
+ bool HasCategoryLabels() const { return maType.HasCategoryLabels(); }
+ /** Returns true, if points of a series show varying automatic area format. */
+ bool HasVarPointFormat() const;
+ /** Returns true, if bars are connected with lines (stacked bar charts only). */
+ bool HasConnectorLines() const;
+
+ /** Returns the legend object. */
+ const XclImpChLegendRef& GetLegend() const { return mxLegend; }
+ /** Returns the default series data format. */
+ const XclImpChDataFormatRef& GetGroupFormat() const { return mxGroupFmt; }
+ /** Returns series title, if the chart type group contains only one single series. */
+ OUString GetSingleSeriesTitle() const;
+
+ /** Converts and writes all 3D settings to the passed diagram. */
+ void ConvertChart3d( ScfPropertySet& rPropSet ) const;
+ /** Creates a coordinate system according to the contained chart type. */
+ css::uno::Reference< css::chart2::XCoordinateSystem >
+ CreateCoordSystem() const;
+ /** Creates and returns an object that represents the contained chart type. */
+ css::uno::Reference< css::chart2::XChartType >
+ CreateChartType( css::uno::Reference< css::chart2::XDiagram > const & xDiagram, sal_Int32 nApiAxesSetIdx ) const;
+ /** Creates a labeled data sequence object for axis categories. */
+ css::uno::Reference< css::chart2::data::XLabeledDataSequence >
+ CreateCategSequence() const;
+
+private:
+ /** Reads a CHDROPBAR record group. */
+ void ReadChDropBar( XclImpStream& rStrm );
+ /** Reads a CHCHARTLINE record group. */
+ void ReadChChartLine( XclImpStream& rStrm );
+ /** Reads a CHDATAFORMAT record group (default series format). */
+ void ReadChDataFormat( XclImpStream& rStrm );
+
+ /** Returns true, if the chart type group contains drop bar formats. */
+ bool HasDropBars() const { return !m_DropBars.empty(); }
+
+ /** Inserts the passed series into the chart type. Adds additional properties to the series. */
+ void InsertDataSeries( css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ css::uno::Reference< css::chart2::XDataSeries > const & xSeries,
+ sal_Int32 nApiAxesSetIdx ) const;
+ /** Creates all data series of any chart type except stock charts. */
+ void CreateDataSeries( css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx ) const;
+ /** Creates all data series of a stock chart. */
+ void CreateStockSeries( css::uno::Reference< css::chart2::XChartType > const & xChartType,
+ sal_Int32 nApiAxesSetIdx ) const;
+
+private:
+ typedef ::std::vector< XclImpChSeriesRef > XclImpChSeriesVec;
+ typedef ::std::map<sal_uInt16, std::unique_ptr<XclImpChDropBar>> XclImpChDropBarMap;
+ typedef ::std::map<sal_uInt16, XclImpChLineFormat> XclImpChLineFormatMap;
+
+ XclChTypeGroup maData; /// Contents of the CHTYPEGROUP record.
+ XclImpChType maType; /// Chart type (e.g. CHBAR, CHLINE, ...).
+ XclChExtTypeInfo maTypeInfo; /// Extended chart type info.
+ XclImpChSeriesVec maSeries; /// Series attached to this chart type group (CHSERIES groups).
+ XclImpChSeriesRef mxFirstSeries; /// First series in this chart type group (CHSERIES groups).
+ XclImpChChart3dRef mxChart3d; /// 3D settings (CHCHART3D record).
+ XclImpChLegendRef mxLegend; /// Chart legend (CHLEGEND group).
+ XclImpChDropBarMap m_DropBars; /// Dropbars (CHDROPBAR group).
+ XclImpChLineFormatMap m_ChartLines; /// Global line formats (CHCHARTLINE group).
+ XclImpChDataFormatRef mxGroupFmt; /// Default format for all series (CHDATAFORMAT group).
+ std::set< sal_uInt16 >
+ maUnusedFormats; /// Contains unused format indexes for automatic colors.
+};
+
+typedef std::shared_ptr< XclImpChTypeGroup > XclImpChTypeGroupRef;
+
+// Axes =======================================================================
+
+class XclImpChLabelRange : protected XclImpChRoot
+{
+public:
+ explicit XclImpChLabelRange( const XclImpChRoot& rRoot );
+ /** Reads the CHLABELRANGE record (category axis scaling properties). */
+ void ReadChLabelRange( XclImpStream& rStrm );
+ /** Reads the CHDATERANGE record (date axis scaling properties). */
+ void ReadChDateRange( XclImpStream& rStrm );
+ /** Converts category axis scaling settings. */
+ void Convert( ScfPropertySet& rPropSet, css::chart2::ScaleData& rScaleData, bool bMirrorOrient ) const;
+ /** Converts position settings of this axis at a crossing axis. */
+ void ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const;
+
+private:
+ XclChLabelRange maLabelData; /// Contents of the CHLABELRANGE record.
+ XclChDateRange maDateData; /// Contents of the CHDATERANGE record.
+};
+
+typedef std::shared_ptr< XclImpChLabelRange > XclImpChLabelRangeRef;
+
+class XclImpChValueRange : protected XclImpChRoot
+{
+public:
+ explicit XclImpChValueRange( const XclImpChRoot& rRoot );
+ /** Reads the CHVALUERANGE record (numeric axis scaling properties). */
+ void ReadChValueRange( XclImpStream& rStrm );
+ /** Converts value axis scaling settings. */
+ void Convert( css::chart2::ScaleData& rScaleData, bool bMirrorOrient ) const;
+ /** Converts position settings of this axis at a crossing axis. */
+ void ConvertAxisPosition( ScfPropertySet& rPropSet ) const;
+
+private:
+ XclChValueRange maData; /// Contents of the CHVALUERANGE record.
+};
+
+typedef std::shared_ptr< XclImpChValueRange > XclImpChValueRangeRef;
+
+class XclImpChTick : protected XclImpChRoot
+{
+public:
+ explicit XclImpChTick( const XclImpChRoot& rRoot );
+ /** Reads the CHTICK record (axis ticks properties). */
+ void ReadChTick( XclImpStream& rStrm );
+
+ /** Returns true, if the axis shows attached labels. */
+ bool HasLabels() const { return maData.mnLabelPos != EXC_CHTICK_NOLABEL; }
+ /** Returns the leading font color for the axis labels. */
+ Color GetFontColor() const;
+ /** Returns the rotation value for the axis labels. */
+ sal_uInt16 GetRotation() const;
+
+ /** Converts and writes the contained data to the passed property set. */
+ void Convert( ScfPropertySet& rPropSet ) const;
+
+private:
+ XclChTick maData; /// Contents of the CHTICK record.
+};
+
+typedef std::shared_ptr< XclImpChTick > XclImpChTickRef;
+
+/** Represents the CHAXIS record group describing an entire chart axis.
+
+ The CHAXIS group consists of: CHAXIS, CHBEGIN, CHLABELRANGE, CHEXTRANGE,
+ CHVALUERANGE, CHFORMAT, CHTICK, CHFONT, CHAXISLINE groups (CHAXISLINE with
+ CHLINEFORMAT, CHAREAFORMAT, and CHESCHERFORMAT group), CHEND.
+ */
+class XclImpChAxis : public XclImpChGroupBase, public XclImpChFontBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType = EXC_CHAXIS_NONE );
+
+ /** Reads the CHAXIS record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHAXIS group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Final processing after reading the entire chart. */
+ void Finalize();
+
+ /** Returns the font index for the axis labels. */
+ virtual sal_uInt16 GetFontIndex() const override;
+ /** Returns the font color for the axis labels. */
+ virtual Color GetFontColor() const override;
+ /** Returns the rotation value for axis labels. */
+ virtual sal_uInt16 GetRotation() const override;
+
+ /** Returns the type of this axis. */
+ sal_uInt16 GetAxisType() const { return maData.mnType; }
+ /** Returns the axis dimension index used by the chart API. */
+ sal_Int32 GetApiAxisDimension() const { return maData.GetApiAxisDimension(); }
+
+ /** Creates an API axis object. */
+ css::uno::Reference< css::chart2::XAxis >
+ CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const;
+ /** Converts and writes 3D wall/floor properties to the passed property set. */
+ void ConvertWall( ScfPropertySet& rPropSet ) const;
+ /** Converts position settings of this axis at a crossing axis. */
+ void ConvertAxisPosition( ScfPropertySet& rPropSet, const XclImpChTypeGroup& rTypeGroup ) const;
+
+private:
+ /** Reads a CHAXISLINE record specifying the target for following line properties. */
+ void ReadChAxisLine( XclImpStream& rStrm );
+ /** Creates a CHFRAME object and stores it into the mxWallFrame member. */
+ void CreateWallFrame();
+
+private:
+ XclChAxis maData; /// Contents of the CHAXIS record.
+ XclImpChLabelRangeRef mxLabelRange; /// Category scaling (CHLABELRANGE record).
+ XclImpChValueRangeRef mxValueRange; /// Value scaling (CHVALUERANGE record).
+ XclImpChTickRef mxTick; /// Axis ticks (CHTICK record).
+ XclImpChFontRef mxFont; /// Index into font buffer (CHFONT record).
+ XclImpChLineFormatRef mxAxisLine; /// Axis line format (CHLINEFORMAT record).
+ XclImpChLineFormatRef mxMajorGrid; /// Major grid line format (CHLINEFORMAT record).
+ XclImpChLineFormatRef mxMinorGrid; /// Minor grid line format (CHLINEFORMAT record).
+ XclImpChFrameRef mxWallFrame; /// Wall/floor format (sub records of CHFRAME group).
+ sal_uInt16 mnNumFmtIdx; /// Index into number format buffer (CHFORMAT record).
+};
+
+typedef std::shared_ptr< XclImpChAxis > XclImpChAxisRef;
+
+/** Represents the CHAXESSET record group describing an axes set (X/Y/Z axes).
+
+ The CHAXESSET group consists of: CHAXESSET, CHBEGIN, CHFRAMEPOS, CHAXIS
+ groups, CHTEXT groups, CHPLOTFRAME group (CHPLOTFRAME with CHFRAME group),
+ CHTYPEGROUP group, CHEND.
+ */
+class XclImpChAxesSet : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId );
+
+ /** Reads the CHAXESSET record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHAXESSET group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Final processing after reading the entire chart. */
+ void Finalize();
+
+ /** Returns true, if this axes set exists (returns false if this is a dummy object). */
+ bool IsValidAxesSet() const { return !maTypeGroups.empty(); }
+ /** Returns the index of the axes set (primary/secondary). */
+ sal_uInt16 GetAxesSetId() const { return maData.mnAxesSetId; }
+ /** Returns the axes set index used by the chart API. */
+ sal_Int32 GetApiAxesSetIndex() const { return maData.GetApiAxesSetIndex(); }
+
+ /** Returns the outer plot area position, if existing. */
+ const XclImpChFramePosRef& GetPlotAreaFramePos() const { return mxFramePos; }
+ /** Returns the specified chart type group. */
+ XclImpChTypeGroupRef GetTypeGroup( sal_uInt16 nGroupIdx ) const;
+ /** Returns the first chart type group. */
+ XclImpChTypeGroupRef GetFirstTypeGroup() const;
+ /** Looks for a legend in all chart type groups and returns it. */
+ XclImpChLegendRef GetLegend() const;
+ /** Returns series title, if the axes set contains only one single series. */
+ OUString GetSingleSeriesTitle() const;
+
+ /** Creates a coordinate system and converts all series and axis settings. */
+ void Convert( css::uno::Reference< css::chart2::XDiagram > const & xDiagram ) const;
+ /** Converts the manual positions of all axis titles. */
+ void ConvertTitlePositions() const;
+
+private:
+ /** Reads a CHAXIS record group containing a single axis. */
+ void ReadChAxis( XclImpStream& rStrm );
+ /** Reads a CHTEXT record group containing an axis title. */
+ void ReadChText( XclImpStream& rStrm );
+ /** Reads the CHPLOTFRAME record group containing diagram area formatting. */
+ void ReadChPlotFrame( XclImpStream& rStrm );
+ /** Reads a CHTYPEGROUP record group containing chart type and chart settings. */
+ void ReadChTypeGroup( XclImpStream& rStrm );
+
+ /** Creates a coordinate system that contains all chart types for this axes set. */
+ css::uno::Reference< css::chart2::XCoordinateSystem >
+ CreateCoordSystem( css::uno::Reference< css::chart2::XDiagram > const & xDiagram ) const;
+ /** Creates and inserts an axis into the container and registers the coordinate system. */
+ void ConvertAxis( XclImpChAxisRef const & xChAxis, XclImpChTextRef const & xChAxisTitle,
+ css::uno::Reference< css::chart2::XCoordinateSystem > const & xCoordSystem,
+ const XclImpChAxis* pCrossingAxis ) const;
+ /** Creates and returns an API axis object. */
+ css::uno::Reference< css::chart2::XAxis >
+ CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const;
+ /** Writes all properties of the background area to the passed diagram. */
+ void ConvertBackground( css::uno::Reference< css::chart2::XDiagram > const & xDiagram ) const;
+
+private:
+ typedef ::std::map<sal_uInt16, XclImpChTypeGroupRef> XclImpChTypeGroupMap;
+
+ XclChAxesSet maData; /// Contents of the CHAXESSET record.
+ XclImpChFramePosRef mxFramePos; /// Outer plot area position (CHFRAMEPOS record).
+ XclImpChAxisRef mxXAxis; /// The X axis (CHAXIS group).
+ XclImpChAxisRef mxYAxis; /// The Y axis (CHAXIS group).
+ XclImpChAxisRef mxZAxis; /// The Z axis (CHAXIS group).
+ XclImpChTextRef mxXAxisTitle; /// The X axis title (CHTEXT group).
+ XclImpChTextRef mxYAxisTitle; /// The Y axis title (CHTEXT group).
+ XclImpChTextRef mxZAxisTitle; /// The Z axis title (CHTEXT group).
+ XclImpChFrameRef mxPlotFrame; /// Plot area (CHPLOTFRAME group).
+ XclImpChTypeGroupMap maTypeGroups; /// Chart type groups (CHTYPEGROUP group).
+};
+
+typedef std::shared_ptr< XclImpChAxesSet > XclImpChAxesSetRef;
+
+// The chart object ===========================================================
+
+/** Represents the CHCHART record group describing the chart contents.
+
+ The CHCHART group consists of: CHCHART, CHBEGIN, SCL, CHPLOTGROWTH, CHFRAME
+ group, CHSERIES groups, CHPROPERTIES, CHDEFAULTTEXT groups (CHDEFAULTTEXT
+ with CHTEXT groups), CHUSEDAXESSETS, CHAXESSET groups, CHTEXT groups, CHEND.
+ */
+class XclImpChChart : public XclImpChGroupBase, protected XclImpChRoot
+{
+public:
+ explicit XclImpChChart( const XclImpRoot& rRoot );
+ virtual ~XclImpChChart() override;
+
+ /** Reads the CHCHART record (called by base class). */
+ virtual void ReadHeaderRecord( XclImpStream& rStrm ) override;
+ /** Reads a record from the CHCHART group (called by base class). */
+ virtual void ReadSubRecord( XclImpStream& rStrm ) override;
+ /** Reads a CHDEFAULTTEXT group (default text formats). */
+ void ReadChDefaultText( XclImpStream& rStrm );
+ /** Reads a CHDATAFORMAT group describing a series format or a data point format. */
+ void ReadChDataFormat( XclImpStream& rStrm );
+
+ /** Sets formatting from BIFF3-BIFF5 OBJ record, if own formatting is invisible. */
+ void UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData );
+
+ /** Returns the specified chart type group. */
+ XclImpChTypeGroupRef GetTypeGroup( sal_uInt16 nGroupIdx ) const;
+ /** Returns the specified default text. */
+ const XclImpChText* GetDefaultText( XclChTextType eTextType ) const;
+ /** Returns true, if the plot area has benn moved and/or resized manually. */
+ bool IsManualPlotArea() const;
+ /** Returns the number of units on the progress bar needed for the chart. */
+ static std::size_t GetProgressSize() { return 2 * EXC_CHART_PROGRESS_SIZE; }
+
+ /** Converts and writes all properties to the passed chart. */
+ void Convert(
+ const css::uno::Reference< css::chart2::XChartDocument>& xChartDoc,
+ XclImpDffConverter& rDffConv,
+ const OUString& rObjName,
+ const tools::Rectangle& rChartRect ) const;
+
+private:
+ /** Reads a CHSERIES group (data series source and formatting). */
+ void ReadChSeries( XclImpStream& rStrm );
+ /** Reads a CHPROPERTIES record (global chart properties). */
+ void ReadChProperties( XclImpStream& rStrm );
+ /** Reads a CHAXESSET group (primary/secondary axes set). */
+ void ReadChAxesSet( XclImpStream& rStrm );
+ /** Reads a CHTEXT group (chart title and series/point captions). */
+ void ReadChText( XclImpStream& rStrm );
+
+ /** Final processing after reading the entire chart data. */
+ void Finalize();
+ /** Finalizes series list, assigns child series to parent series. */
+ void FinalizeSeries();
+ /** Assigns all imported CHDATAFORMAT groups to the respective series. */
+ void FinalizeDataFormats();
+ /** Finalizes chart title, tries to detect title auto-generated from series name. */
+ void FinalizeTitle();
+
+ /** Creates and returns a new diagram object and converts global chart settings. */
+ css::uno::Reference<css::chart2::XDiagram>
+ CreateDiagram() const;
+
+private:
+ typedef ::std::vector< XclImpChSeriesRef > XclImpChSeriesVec;
+ typedef ::std::map<XclChDataPointPos, XclImpChDataFormatRef> XclImpChDataFormatMap;
+ typedef ::std::map<sal_uInt16, std::unique_ptr<XclImpChText>> XclImpChTextMap;
+
+ XclChRectangle maRect; /// Position of the chart on the sheet (CHCHART record).
+ XclImpChSeriesVec maSeries; /// List of series data (CHSERIES groups).
+ XclImpChDataFormatMap maDataFmts; /// All series and point formats (CHDATAFORMAT groups).
+ XclImpChFrameRef mxFrame; /// Chart frame format (CHFRAME group).
+ XclChProperties maProps; /// Chart properties (CHPROPERTIES record).
+ XclImpChTextMap m_DefTexts; /// Default text objects (CHDEFAULTTEXT groups).
+ XclImpChAxesSetRef mxPrimAxesSet; /// Primary axes set (CHAXESSET group).
+ XclImpChAxesSetRef mxSecnAxesSet; /// Secondary axes set (CHAXESSET group).
+ XclImpChTextRef mxTitle; /// Chart title (CHTEXT group).
+ XclImpChLegendRef mxLegend; /// Chart legend (CHLEGEND group).
+};
+
+typedef std::shared_ptr< XclImpChChart > XclImpChChartRef;
+
+/** Drawing container of a chart. */
+class XclImpChartDrawing : public XclImpDrawing
+{
+public:
+ explicit XclImpChartDrawing( const XclImpRoot& rRoot, bool bOwnTab );
+
+ /** Converts all objects and inserts them into the chart drawing page. */
+ void ConvertObjects(
+ XclImpDffConverter& rDffConv,
+ const css::uno::Reference< css::frame::XModel >& rxModel,
+ const tools::Rectangle& rChartRect );
+
+ /** Calculate the resulting rectangle of the passed anchor. */
+ virtual tools::Rectangle CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const override;
+ /** Called whenever an object has been inserted into the draw page. */
+ virtual void OnObjectInserted( const XclImpDrawObjBase& rDrawObj ) override;
+
+private:
+ tools::Rectangle maChartRect; /// Position and size of the chart shape in 1/100 mm.
+ SCTAB mnScTab; /// Index of the sheet that contains the chart.
+ bool mbOwnTab; /// True = own sheet, false = embedded object.
+};
+
+/** Represents the entire chart substream (all records in BOF/EOF block). */
+class XclImpChart : protected XclImpRoot
+{
+public:
+ /** Constructs a new chart object.
+ @param bOwnTab True = chart is on an own sheet; false = chart is an embedded object. */
+ explicit XclImpChart( const XclImpRoot& rRoot, bool bOwnTab );
+ virtual ~XclImpChart() override;
+
+ /** Reads the complete chart substream (BOF/EOF block).
+ @descr The passed stream must be located in the BOF record of the chart substream. */
+ void ReadChartSubStream( XclImpStream& rStrm );
+ /** Sets formatting from BIFF3-BIFF5 OBJ record, if own formatting is invisible. */
+ void UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData );
+
+ /** Returns the number of units on the progress bar needed for the chart. */
+ std::size_t GetProgressSize() const;
+ /** Returns true, if the chart is based on a pivot table. */
+ bool IsPivotChart() const { return mbIsPivotChart; }
+
+ /** Creates the chart object in the passed component. */
+ void Convert( css::uno::Reference< css::frame::XModel > const & xModel,
+ XclImpDffConverter& rDffConv,
+ const OUString& rObjName,
+ const tools::Rectangle& rChartRect ) const;
+
+private:
+ /** Returns (initially creates) the drawing container for embedded shapes. **/
+ XclImpChartDrawing& GetChartDrawing();
+ /** Reads the CHCHART group (entire chart data). */
+ void ReadChChart( XclImpStream& rStrm );
+
+private:
+ typedef std::shared_ptr< XclImpChartDrawing > XclImpChartDrawingRef;
+
+ XclImpChChartRef mxChartData; /// The chart data (CHCHART group).
+ XclImpChartDrawingRef mxChartDrawing; /// Drawing container for embedded shapes.
+ bool mbOwnTab; /// true = own sheet; false = embedded object.
+ bool mbIsPivotChart; /// true = chart is based on a pivot table.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xicontent.hxx b/sc/source/filter/inc/xicontent.hxx
new file mode 100644
index 000000000..d7cdb5dd6
--- /dev/null
+++ b/sc/source/filter/inc/xicontent.hxx
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rangelst.hxx>
+#include "xistring.hxx"
+#include "xiroot.hxx"
+#include <validat.hxx>
+#include <tabprotection.hxx>
+
+#include <map>
+#include <vector>
+#include <memory>
+
+class ErrCode;
+struct XclRange;
+
+/* ============================================================================
+Classes to import the big Excel document contents (related to several cells or
+globals for the document).
+- Shared
+ tables
+- Hyperlinks
+- Label ranges
+- Conditional formatting
+- Data validation
+- Web queries
+- Stream decryption
+============================================================================ */
+
+// Shared string table ========================================================
+
+/** The SST (shared string table) contains all strings used in a BIFF8 file.
+
+ This class loads the SST, provides access to the strings, and is able to
+ create Calc string or edit cells.
+ */
+class XclImpSst : protected XclImpRoot
+{
+public:
+ explicit XclImpSst( const XclImpRoot& rRoot );
+
+ /** Reads the entire SST record.
+ @descr Import stream must be located at start of a SST record. */
+ void ReadSst( XclImpStream& rStrm );
+
+ /** Returns a pointer to the string with the passed index. */
+ const XclImpString* GetString( sal_uInt32 nSstIndex ) const;
+
+private:
+ typedef ::std::vector< XclImpString > XclImpStringVec;
+ XclImpStringVec maStrings; /// List with all strings in the SST.
+};
+
+// Hyperlinks =================================================================
+
+/** Provides importing hyperlinks and inserting them into a document. */
+class XclImpHyperlink
+{
+public:
+ /** delete copy constructor */
+ XclImpHyperlink(const XclImpHyperlink&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpHyperlink& operator=(const XclImpHyperlink&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpHyperlink() = delete;
+
+ /** Reads a HLINK record and inserts it into the document.
+ @descr Import stream must be located at start of a HLINK record. */
+ static void ReadHlink( XclImpStream& rStrm );
+
+ /** Reads the (undocumented) embedded hyperlink data and returns the URL. */
+ static OUString ReadEmbeddedData( XclImpStream& rStrm );
+
+ /** Inserts the URL into a range of cells. Does not modify value or formula cells. */
+ static void InsertUrl( XclImpRoot& rRoot, const XclRange& rXclRange, const OUString& rUrl );
+
+ /** Convert the sheet name with invalid character(s) in URL when the URL is
+ to a location within the same document (e.g. #'Sheet&Name'.A1). */
+ static void ConvertToValidTabName(OUString& rName);
+};
+
+// Label ranges ===============================================================
+
+/** Provides importing label ranges and inserting them into a document. */
+class XclImpLabelranges
+{
+public:
+ /** delete copy constructor */
+ XclImpLabelranges(const XclImpLabelranges&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpLabelranges& operator=(const XclImpLabelranges&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpLabelranges() = delete;
+ /** Reads a LABELRANGES record and inserts the label ranges into the document.
+ @descr Import stream must be located at start of a LABELRANGES record. */
+ static void ReadLabelranges( XclImpStream& rStrm );
+};
+
+// Conditional formatting =====================================================
+
+/** Represents a conditional format with condition formulas, and formatting attributes. */
+class XclImpCondFormat : protected XclImpRoot
+{
+public:
+ explicit XclImpCondFormat( const XclImpRoot& rRoot, sal_uInt32 nFormatIndex );
+ virtual ~XclImpCondFormat() override;
+
+ /** Reads a CONDFMT record and initializes this conditional format. */
+ void ReadCondfmt( XclImpStream& rStrm );
+ /** Reads a CF record and adds a new condition and the formatting attributes. */
+ void ReadCF( XclImpStream& rStrm );
+
+ /** Inserts this conditional format into the document. */
+ void Apply();
+
+private:
+ typedef ::std::unique_ptr< ScConditionalFormat > ScCondFmtPtr;
+
+ ScRangeList maRanges; /// Destination cell ranges.
+ ScCondFmtPtr mxScCondFmt; /// Calc conditional format.
+ sal_uInt32 mnFormatIndex; /// Index of this conditional format in list.
+ sal_uInt16 mnCondCount; /// Number of conditions to be inserted.
+ sal_uInt16 mnCondIndex; /// Condition index to be inserted next.
+};
+
+/** Imports and collects all conditional formatting of a sheet. */
+class XclImpCondFormatManager : protected XclImpRoot
+{
+public:
+ explicit XclImpCondFormatManager( const XclImpRoot& rRoot );
+
+ /** Reads a CONDFMT record and starts a new conditional format to be filled from CF records. */
+ void ReadCondfmt( XclImpStream& rStrm );
+ /** Reads a CF record and inserts the formatting data to the current conditional format. */
+ void ReadCF( XclImpStream& rStrm );
+
+ /** Inserts the conditional formatting into the document. */
+ void Apply();
+
+private:
+ std::vector< std::unique_ptr<XclImpCondFormat> >
+ maCondFmtList; /// List with all conditional formatting.
+};
+
+// Data Validation ============================================================
+
+/** Imports validation data. */
+class XclImpValidationManager : protected XclImpRoot
+{
+public:
+ explicit XclImpValidationManager( const XclImpRoot& rRoot );
+
+ /** Reads a DVAL record and sets marks the dropdown arrow control to be ignored. */
+ static void ReadDval( XclImpStream& rStrm );
+ /** Reads a DV record and inserts validation data into the document. */
+ void ReadDV( XclImpStream& rStrm );
+
+ void Apply();
+private:
+ struct DVItem
+ {
+ ScRangeList maRanges;
+ ScValidationData maValidData;
+
+ explicit DVItem ( const ScRangeList& rRanges, const ScValidationData& rValidData );
+ };
+
+ std::vector< std::unique_ptr<DVItem> > maDVItems;
+};
+
+// Web queries ================================================================
+
+/** Stores the data of one web query. */
+class XclImpWebQuery
+{
+public:
+ explicit XclImpWebQuery( const ScRange& rDestRange );
+
+ /** Reads a PARAMQRY record and sets data to the web query. */
+ void ReadParamqry( XclImpStream& rStrm );
+ /** Reads a WQSTRING record and sets URL. */
+ void ReadWqstring( XclImpStream& rStrm );
+ /** Reads a WEBQRYSETTINGS record and sets refresh rate. */
+ void ReadWqsettings( XclImpStream& rStrm );
+ /** Reads a WEBQRYTABLES record and sets source range list. */
+ void ReadWqtables( XclImpStream& rStrm );
+
+ /** Inserts the web query into the document. */
+ void Apply( ScDocument& rDoc, const OUString& rFilterName );
+
+private:
+ /** Specifies the type of the web query (which ranges are imported). */
+ enum XclImpWebQueryMode
+ {
+ xlWQUnknown, /// Not specified.
+ xlWQDocument, /// Entire document.
+ xlWQAllTables, /// All tables.
+ xlWQSpecTables /// Specific tables.
+ };
+
+ OUString maURL; /// Source document URL.
+ OUString maTables; /// List of source range names.
+ ScRange maDestRange; /// Destination range.
+ XclImpWebQueryMode meMode; /// Current mode of the web query.
+ sal_uInt16 mnRefresh; /// Refresh time in minutes.
+};
+
+class XclImpWebQueryBuffer : protected XclImpRoot
+{
+public:
+ explicit XclImpWebQueryBuffer( const XclImpRoot& rRoot );
+
+ /** Reads the QSI record and creates a new web query in the buffer. */
+ void ReadQsi( XclImpStream& rStrm );
+ /** Reads a PARAMQRY record and sets data to the current web query. */
+ void ReadParamqry( XclImpStream& rStrm );
+ /** Reads a WQSTRING record and sets URL to the current web query. */
+ void ReadWqstring( XclImpStream& rStrm );
+ /** Reads a WEBQRYSETTINGS record and sets refresh rate to the current web query. */
+ void ReadWqsettings( XclImpStream& rStrm );
+ /** Reads a WEBQRYTABLES record and sets source range list to the current web query. */
+ void ReadWqtables( XclImpStream& rStrm );
+
+ /** Inserts all web queries into the document. */
+ void Apply();
+
+private:
+ typedef std::vector< XclImpWebQuery > XclImpWebQueryList;
+ XclImpWebQueryList maWQList; /// List of the web query objects.
+};
+
+// Decryption =================================================================
+
+/** Provides static functions to import stream decryption settings. */
+class XclImpDecryptHelper
+{
+public:
+ /** delete copy constructor */
+ XclImpDecryptHelper(const XclImpDecryptHelper&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpDecryptHelper& operator=(const XclImpDecryptHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpDecryptHelper() = delete;
+
+ /** Reads the FILEPASS record, queries a password and sets decryption algorithm.
+ @return Error code that may cause an error message after import. */
+ static const ErrCode& ReadFilepass( XclImpStream& rStrm );
+};
+
+// Document protection ========================================================
+
+class XclImpDocProtectBuffer : protected XclImpRoot
+{
+public:
+ explicit XclImpDocProtectBuffer( const XclImpRoot& rRoot );
+
+ /** document structure protection flag */
+ void ReadDocProtect( XclImpStream& rStrm );
+
+ /** document windows properties protection flag */
+ void ReadWinProtect( XclImpStream& rStrm );
+
+ void ReadPasswordHash( XclImpStream& rStrm );
+
+ void Apply() const;
+
+private:
+ sal_uInt16 mnPassHash;
+ bool mbDocProtect:1;
+ bool mbWinProtect:1;
+};
+
+// Sheet protection ===========================================================
+
+class XclImpSheetProtectBuffer : protected XclImpRoot
+{
+public:
+ explicit XclImpSheetProtectBuffer( const XclImpRoot& rRoot );
+
+ void ReadProtect( XclImpStream& rStrm, SCTAB nTab );
+
+ void ReadOptions( XclImpStream& rStrm, SCTAB nTab );
+
+ void AppendEnhancedProtection( const ScEnhancedProtection & rProt, SCTAB nTab );
+
+ void ReadPasswordHash( XclImpStream& rStrm, SCTAB nTab );
+
+ void Apply() const;
+
+private:
+ struct Sheet
+ {
+ bool mbProtected;
+ sal_uInt16 mnPasswordHash;
+ sal_uInt16 mnOptions;
+ ::std::vector< ScEnhancedProtection > maEnhancedProtections;
+
+ Sheet();
+ Sheet(const Sheet& r);
+ };
+
+ Sheet* GetSheetItem( SCTAB nTab );
+
+private:
+ typedef ::std::map<SCTAB, Sheet> ProtectedSheetMap;
+ ProtectedSheetMap maProtectedSheets;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiescher.hxx b/sc/source/filter/inc/xiescher.hxx
new file mode 100644
index 000000000..cc7360576
--- /dev/null
+++ b/sc/source/filter/inc/xiescher.hxx
@@ -0,0 +1,1212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <filter/msfilter/msdffimp.hxx>
+#include <vcl/graph.hxx>
+#include "xlescher.hxx"
+#include "xiroot.hxx"
+#include <oox/ole/olehelper.hxx>
+#include <rtl/ustring.hxx>
+#include <svx/svdobj.hxx>
+#include <map>
+#include <memory>
+#include <vector>
+
+namespace com::sun::star {
+ namespace drawing { class XShape; }
+ namespace form { class XForm; }
+}
+
+namespace com::sun::star::container { class XNameContainer; }
+
+class SdrObjList;
+class ScfProgressBar;
+class ScfPropertySet;
+class ScRangeList;
+class XclImpChart;
+class XclImpDffConverter;
+class XclImpDrawing;
+
+// Drawing objects ============================================================
+
+class XclImpDrawObjBase;
+typedef std::shared_ptr< XclImpDrawObjBase > XclImpDrawObjRef;
+
+/** Base class for drawing objects (OBJ records). */
+class XclImpDrawObjBase : protected XclImpRoot
+{
+public:
+ explicit XclImpDrawObjBase( const XclImpRoot& rRoot );
+ virtual ~XclImpDrawObjBase() override;
+
+ /** Reads the BIFF3 OBJ record, returns a new drawing object. */
+ static XclImpDrawObjRef ReadObj3( const XclImpRoot& rRoot, XclImpStream& rStrm );
+ /** Reads the BIFF4 OBJ record, returns a new drawing object. */
+ static XclImpDrawObjRef ReadObj4( const XclImpRoot& rRoot, XclImpStream& rStrm );
+ /** Reads the BIFF5 OBJ record, returns a new drawing object. */
+ static XclImpDrawObjRef ReadObj5( const XclImpRoot& rRoot, XclImpStream& rStrm );
+ /** Reads the BIFF8 OBJ record, returns a new drawing object. */
+ static XclImpDrawObjRef ReadObj8( const XclImpRoot& rRoot, XclImpStream& rStrm );
+
+ /** Sets whether this is an area object (then its width and height must be greater than 0). */
+ void SetAreaObj( bool bAreaObj ) { mbAreaObj = bAreaObj; }
+ /** If set to true, a new SdrObject will be created while in DFF import. */
+ void SetSimpleMacro( bool bMacro ) { mbSimpleMacro = bMacro; }
+
+ /** Sets the object anchor explicitly. */
+ void SetAnchor( const XclObjAnchor& rAnchor );
+ /** Sets shape data from DFF stream. */
+ void SetDffData(
+ const DffObjData& rDffObjData, const OUString& rObjName, const OUString& rHyperlink,
+ bool bVisible, bool bAutoMargin );
+
+ /** If set to false, the SdrObject will not be created, processed, or inserted into the draw page. */
+ void SetProcessSdrObj( bool bProcess ) { mbProcessSdr = bProcess; }
+ /** If set to false, the SdrObject will be created or processed, but not be inserted into the draw page. */
+ void SetInsertSdrObj( bool bInsert ) { mbInsertSdr = bInsert; }
+ /** If set to true, a new SdrObject will be created while in DFF import. */
+ void SetCustomDffObj( bool bCustom ) { mbCustomDff = bCustom; }
+
+ /** Returns the sheet index and Excel object identifier from OBJ record. */
+ sal_uInt16 GetObjId() const { return mnObjId; }
+ /** Returns the Excel object type from OBJ record. */
+ sal_uInt16 GetObjType() const { return mnObjType; }
+ /** Returns the name of this object, may generate a default name. */
+ virtual OUString GetObjName() const;
+ /** Returns associated macro name, if set, otherwise zero length string. */
+ const OUString& GetMacroName() const { return maMacroName; }
+
+ /** Returns the shape identifier used in the DFF stream. */
+ sal_uInt32 GetDffShapeId() const { return mnDffShapeId; }
+ /** Returns the shape flags from the DFF stream. */
+ ShapeFlag GetDffFlags() const { return mnDffFlags; }
+
+ /** Returns true, if the object is hidden. */
+ bool IsHidden() const { return mbHidden; }
+ /** Returns true, if the object is visible. */
+ bool IsVisible() const { return mbVisible; }
+ /** Returns true, if the object is printable. */
+ bool IsPrintable() const { return mbPrintable; }
+
+ /** Returns the object anchor if existing, null otherwise. */
+ const XclObjAnchor* GetAnchor() const;
+ /** Returns true, if the passed size is valid for this object. */
+ bool IsValidSize( const tools::Rectangle& rAnchorRect ) const;
+ /** Returns the range in the sheet covered by this object. */
+ ScRange GetUsedArea( SCTAB nScTab ) const;
+
+ /** Returns true, if the object is valid and will be processed. */
+ bool IsProcessSdrObj() const { return mbProcessSdr && !mbHidden; }
+ /** Returns true, if the SdrObject will be created or processed, but not be inserted into the draw page. */
+ bool IsInsertSdrObj() const { return mbInsertSdr; }
+
+ /** Returns the needed size on the progress bar (calls virtual DoGetProgressSize() function). */
+ std::size_t GetProgressSize() const;
+ /** Creates and returns an SdrObject from the contained data. Caller takes ownership! */
+ SdrObjectUniquePtr CreateSdrObject( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect, bool bIsDff ) const;
+ /** Additional processing for the passed SdrObject before insertion into
+ the drawing page (calls virtual DoPreProcessSdrObj() function). */
+ void PreProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj );
+ /** Additional processing for the passed SdrObject after insertion into the
+ drawing page (calls virtual DoPostProcessSdrObj() function). */
+ void PostProcessSdrObject( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const;
+ SCTAB GetTab() const { return mnTab; }
+
+protected:
+ /** Reads the object name in a BIFF5 OBJ record. */
+ void ReadName5( XclImpStream& rStrm, sal_uInt16 nNameLen );
+ /** Reads the macro link in a BIFF3 OBJ record. */
+ void ReadMacro3( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Reads the macro link in a BIFF4 OBJ record. */
+ void ReadMacro4( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Reads the macro link in a BIFF5 OBJ record. */
+ void ReadMacro5( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Reads the contents of the ftMacro sub structure in an OBJ record. */
+ void ReadMacro8( XclImpStream& rStrm );
+
+ /** Converts the passed line formatting to the passed SdrObject. */
+ void ConvertLineStyle( SdrObject& rSdrObj, const XclObjLineData& rLineData ) const;
+ /** Converts the passed fill formatting to the passed SdrObject. */
+ void ConvertFillStyle( SdrObject& rSdrObj, const XclObjFillData& rFillData ) const;
+ /** Converts the passed frame flags to the passed SdrObject. */
+ void ConvertFrameStyle( SdrObject& rSdrObj, sal_uInt16 nFrameFlags ) const;
+
+ /** Returns a solid line color from the passed line data struct. */
+ Color GetSolidLineColor( const XclObjLineData& rLineData ) const;
+ /** Returns a solid fill color from the passed fill data struct. */
+ Color GetSolidFillColor( const XclObjFillData& rFillData ) const;
+
+ /** Derived classes read the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Derived classes read the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize );
+ /** Derived classes read the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize );
+ /** Derived classes read the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize );
+
+ /** Derived classes may return a progress bar size different from 1. */
+ virtual std::size_t DoGetProgressSize() const;
+ /** Derived classes create and return a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const;
+ /** Derived classes may perform additional processing for the passed SdrObject before insertion. */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const;
+ /** Derived classes may perform additional processing for the passed SdrObject after insertion. */
+ virtual void DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const;
+
+ /** Notify that the document contains a macro event handler */
+ void NotifyMacroEventRead();
+private:
+ /** Reads the contents of a BIFF3 OBJ record. */
+ void ImplReadObj3( XclImpStream& rStrm );
+ /** Reads the contents of a BIFF4 OBJ record. */
+ void ImplReadObj4( XclImpStream& rStrm );
+ /** Reads the contents of a BIFF5 OBJ record. */
+ void ImplReadObj5( XclImpStream& rStrm );
+ /** Reads the contents of a BIFF8 OBJ record. */
+ void ImplReadObj8( XclImpStream& rStrm );
+
+private:
+ XclObjAnchor maAnchor; /// The position of the object in its parent.
+ sal_uInt16 mnObjId; /// The object identifier (unique per drawing).
+ SCTAB mnTab; /// Location of object
+ sal_uInt16 mnObjType; /// The Excel object type from OBJ record.
+ sal_uInt32 mnDffShapeId; /// Shape ID from DFF stream.
+ ShapeFlag mnDffFlags; /// Shape flags from DFF stream.
+ OUString maObjName; /// Name of the object.
+ OUString maMacroName; /// Name of an attached macro.
+ OUString maHyperlink; /// On-click hyperlink URL.
+ bool mbHasAnchor; /// true = maAnchor is initialized.
+ bool mbHidden; /// true = Object is hidden.
+ bool mbVisible; /// true = Object is visible.
+ bool mbPrintable; /// true = Object is printable.
+ bool mbAreaObj; /// true = Width and height must be greater than 0.
+ bool mbAutoMargin; /// true = Set automatic text margin.
+ bool mbSimpleMacro; /// true = Create simple macro link and hyperlink.
+ bool mbProcessSdr; /// true = Object is valid, do processing and insertion.
+ bool mbInsertSdr; /// true = Insert the SdrObject into draw page.
+ bool mbCustomDff; /// true = Recreate SdrObject in DFF import.
+ bool mbNotifyMacroEventRead; /// true == If we have already seen a macro event
+};
+
+class XclImpDrawObjVector
+{
+private:
+ std::vector< XclImpDrawObjRef > mObjs;
+
+public:
+ explicit XclImpDrawObjVector() : mObjs() {}
+
+ std::vector< XclImpDrawObjRef >::const_iterator begin() const { return mObjs.begin(); }
+ std::vector< XclImpDrawObjRef >::const_iterator end() const { return mObjs.end(); }
+ void push_back(const XclImpDrawObjRef& rObj) { mObjs.push_back(rObj); }
+
+ /** Tries to insert the passed object into the last group or appends it. */
+ void InsertGrouped( XclImpDrawObjRef const & xDrawObj );
+
+ /** Returns the needed size on the progress bar for all contained objects. */
+ std::size_t GetProgressSize() const;
+};
+
+/** A placeholder object for unknown object types. */
+class XclImpPhObj : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpPhObj( const XclImpRoot& rRoot );
+};
+
+/** A group object. */
+class XclImpGroupObj final : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpGroupObj( const XclImpRoot& rRoot );
+
+ /** Tries to insert the drawing object into this or a nested group. */
+ bool TryInsert( XclImpDrawObjRef const & xDrawObj );
+
+private:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Returns a progress bar size that takes all group children into account. */
+ virtual std::size_t DoGetProgressSize() const override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+ XclImpDrawObjVector maChildren; /// Grouped objects.
+ sal_uInt16 mnFirstUngrouped; /// Object identifier of first object not grouped into this group.
+};
+
+/** A line object. */
+class XclImpLineObj final : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpLineObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+ XclObjLineData maLineData; /// BIFF5 line formatting.
+ sal_uInt16 mnArrows; /// Line arrows.
+ sal_uInt8 mnStartPoint; /// Starting point.
+};
+
+/** A rectangle or oval object. */
+class XclImpRectObj : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpRectObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads fil data, line data, and frame flags. */
+ void ReadFrameData( XclImpStream& rStrm );
+
+ /** Converts fill formatting, line formatting, and frame style. */
+ void ConvertRectStyle( SdrObject& rSdrObj ) const;
+
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+protected:
+ XclObjFillData maFillData; /// BIFF5 fill formatting.
+ XclObjLineData maLineData; /// BIFF5 line formatting.
+ sal_uInt16 mnFrameFlags; /// Additional flags.
+};
+
+/** An oval object. */
+class XclImpOvalObj : public XclImpRectObj
+{
+public:
+ explicit XclImpOvalObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+};
+
+/** An arc object. */
+class XclImpArcObj final : public XclImpDrawObjBase
+{
+public:
+ explicit XclImpArcObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+ XclObjFillData maFillData; /// BIFF5 fill formatting.
+ XclObjLineData maLineData; /// BIFF5 line formatting.
+ sal_uInt8 mnQuadrant; /// Visible quadrant of the circle.
+};
+
+/** A polygon object. */
+class XclImpPolygonObj final : public XclImpRectObj
+{
+public:
+ explicit XclImpPolygonObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the COORDLIST record following the OBJ record. */
+ void ReadCoordList( XclImpStream& rStrm );
+
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+
+ typedef std::vector< Point > PointVector;
+ PointVector maCoords; /// Coordinates relative to bounding rectangle.
+ sal_uInt16 mnPolyFlags; /// Additional flags.
+ sal_uInt16 mnPointCount; /// Polygon point count.
+};
+
+struct XclImpObjTextData
+{
+ XclObjTextData maData; /// BIFF5 text data.
+ XclImpStringRef mxString; /// Plain or rich string.
+
+ /** Reads a byte string from the passed stream. */
+ void ReadByteString( XclImpStream& rStrm );
+ /** Reads text formatting from the passed stream. */
+ void ReadFormats( XclImpStream& rStrm );
+};
+
+/** A drawing object supporting text contents. Used for all simple objects in BIFF8. */
+class XclImpTextObj : public XclImpRectObj
+{
+public:
+ explicit XclImpTextObj( const XclImpRoot& rRoot );
+
+ /** Stores the passed textbox data. */
+ void SetTextData( const XclImpObjTextData& rTextData ) { maTextData = rTextData; }
+
+protected:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+ /** Inserts the contained text data at the passed object. */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+protected:
+ XclImpObjTextData maTextData; /// Textbox data from BIFF5 OBJ or BIFF8 TXO record.
+};
+
+/** A chart object. This is the drawing object wrapper for the chart data. */
+class XclImpChartObj : public XclImpRectObj
+{
+public:
+ /** @param bOwnTab True = chart is on an own sheet; false = chart is an embedded object. */
+ explicit XclImpChartObj( const XclImpRoot& rRoot, bool bOwnTab = false );
+
+ /** Reads the complete chart substream (BOF/EOF block). */
+ void ReadChartSubStream( XclImpStream& rStrm );
+
+protected:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Returns the needed size on the progress bar. */
+ virtual std::size_t DoGetProgressSize() const override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+ /** Converts the chart document. */
+ virtual void DoPostProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+private:
+ /** Calculates the object anchor of a sheet chart (chart fills one page). */
+ void FinalizeTabChart();
+
+private:
+ typedef std::shared_ptr< XclImpChart > XclImpChartRef;
+
+ XclImpChartRef mxChart; /// The chart itself (BOF/EOF substream data).
+ bool mbOwnTab; /// true = own sheet; false = embedded object.
+};
+
+/** A note object, which is a specialized text box object. */
+class XclImpNoteObj : public XclImpTextObj
+{
+public:
+ explicit XclImpNoteObj( const XclImpRoot& rRoot );
+
+ /** Sets note flags and the note position in the Calc sheet. */
+ void SetNoteData( const ScAddress& rScPos, sal_uInt16 nNoteFlags );
+
+protected:
+ /** Inserts the note into the document, sets visibility. */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+private:
+ ScAddress maScPos; /// Cell position of the note object.
+ sal_uInt16 mnNoteFlags; /// Flags from NOTE record.
+};
+
+/** Helper base class for TBX and OCX form controls to manage spreadsheet links. */
+class XclImpControlHelper
+{
+public:
+ explicit XclImpControlHelper( const XclImpRoot& rRoot, XclCtrlBindMode eBindMode );
+ virtual ~XclImpControlHelper();
+
+ /** Returns true, if a linked cell address is present. */
+ bool HasCellLink() const { return mxCellLink != nullptr; }
+
+ /** Returns the SdrObject from the passed control shape and sets the bounding rectangle. */
+ SdrObjectUniquePtr CreateSdrObjectFromShape(
+ const css::uno::Reference< css::drawing::XShape >& rxShape,
+ const tools::Rectangle& rAnchorRect ) const;
+
+ /** Sets additional properties to the form control model, calls virtual DoProcessControl(). */
+ void ProcessControl( const XclImpDrawObjBase& rDrawObj ) const;
+
+protected:
+ /** Reads the formula for the linked cell from the current position of the stream. */
+ void ReadCellLinkFormula( XclImpStream& rStrm, bool bWithBoundSize );
+ /** Reads the formula for the source range from the current position of the stream. */
+ void ReadSourceRangeFormula( XclImpStream& rStrm, bool bWithBoundSize );
+
+ /** Derived classes will set additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const;
+
+ void ApplySheetLinkProps() const;
+ mutable css::uno::Reference< css::drawing::XShape >
+ mxShape; /// The UNO wrapper of the control shape.
+ std::shared_ptr< ScAddress > mxCellLink; /// Linked cell in the Calc document.
+private:
+ /** Reads a list of cell ranges from a formula at the current stream position. */
+ void ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm );
+ /** Reads leading formula size and a list of cell ranges from a formula if the leading size is not zero. */
+ void ReadRangeList( ScRangeList& rScRanges, XclImpStream& rStrm, bool bWithBoundSize );
+
+private:
+ const XclImpRoot& mrRoot; /// Not derived from XclImpRoot to allow multiple inheritance.
+ std::shared_ptr< ScRange > mxSrcRange; /// Source data range in the Calc document.
+ XclCtrlBindMode meBindMode; /// Value binding mode.
+};
+
+/** Base class for textbox based form controls. */
+class XclImpTbxObjBase : public XclImpTextObj, public XclImpControlHelper
+{
+public:
+ explicit XclImpTbxObjBase( const XclImpRoot& rRoot );
+
+ /** Sets line and fill formatting from the passed DFF property set. */
+ void SetDffProperties( const DffPropSet& rDffPropSet );
+
+ /** Returns the service name of the control component to be created. */
+ OUString GetServiceName() const { return DoGetServiceName(); }
+ /** Fills the passed macro event descriptor. */
+ bool FillMacroDescriptor(
+ css::script::ScriptEventDescriptor& rDescriptor ) const;
+
+protected:
+ /** Sets control text formatting. */
+ void ConvertFont( ScfPropertySet& rPropSet ) const;
+ /** Sets control label and text formatting. */
+ void ConvertLabel( ScfPropertySet& rPropSet ) const;
+
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+ /** Additional processing on the SdrObject, calls new virtual function DoProcessControl(). */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+ /** Derived classes return the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const = 0;
+ /** Derived classes return the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const = 0;
+};
+
+/** A button control. */
+class XclImpButtonObj : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpButtonObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** A checkbox control. */
+class XclImpCheckBoxObj : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpCheckBoxObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+protected:
+ sal_uInt16 mnState;
+ sal_uInt16 mnCheckBoxFlags;
+};
+
+/** An option button control. */
+class XclImpOptionButtonObj final : public XclImpCheckBoxObj
+{
+public:
+ explicit XclImpOptionButtonObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ sal_uInt16 mnNextInGroup; /// Next option button in a group.
+ sal_uInt16 mnFirstInGroup; /// 1 = Button is the first in a group.
+};
+
+/** A label control. */
+class XclImpLabelObj : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpLabelObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** A groupbox control. */
+class XclImpGroupBoxObj final : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpGroupBoxObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ sal_uInt16 mnGroupBoxFlags;
+};
+
+/** A dialog control. */
+class XclImpDialogObj : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpDialogObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** An edit control. */
+class XclImpEditObj final : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpEditObj( const XclImpRoot& rRoot );
+
+private:
+ /** REturns true, if the field type is numeric. */
+ bool IsNumeric() const;
+
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ sal_uInt16 mnContentType;
+ sal_uInt16 mnMultiLine;
+ sal_uInt16 mnScrollBar;
+ sal_uInt16 mnListBoxObjId;
+};
+
+/** Base class of scrollable form controls (spin button, scrollbar, listbox, dropdown). */
+class XclImpTbxObjScrollableBase : public XclImpTbxObjBase
+{
+public:
+ explicit XclImpTbxObjScrollableBase( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads scrollbar data. */
+ void ReadSbs( XclImpStream& rStrm );
+
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+
+protected:
+ sal_uInt16 mnValue;
+ sal_uInt16 mnMin;
+ sal_uInt16 mnMax;
+ sal_uInt16 mnStep;
+ sal_uInt16 mnPageStep;
+ sal_uInt16 mnOrient;
+ sal_uInt16 mnThumbWidth;
+ sal_uInt16 mnScrollFlags;
+};
+
+/** A spinbutton control. */
+class XclImpSpinButtonObj : public XclImpTbxObjScrollableBase
+{
+public:
+ explicit XclImpSpinButtonObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** A scrollbar control. */
+class XclImpScrollBarObj : public XclImpTbxObjScrollableBase
+{
+public:
+ explicit XclImpScrollBarObj( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+};
+
+/** Base class for list controls (listbox, dropdown). */
+class XclImpTbxObjListBase : public XclImpTbxObjScrollableBase
+{
+public:
+ explicit XclImpTbxObjListBase( const XclImpRoot& rRoot );
+
+protected:
+ /** Reads common listbox settings. */
+ void ReadLbsData( XclImpStream& rStrm );
+ /** Sets common listbox/dropdown formatting attributes. */
+ void SetBoxFormatting( ScfPropertySet& rPropSet ) const;
+
+protected:
+ sal_uInt16 mnEntryCount;
+ sal_uInt16 mnSelEntry;
+ sal_uInt16 mnListFlags;
+ sal_uInt16 mnEditObjId;
+ bool mbHasDefFontIdx;
+};
+
+/** A listbox control. */
+class XclImpListBoxObj final : public XclImpTbxObjListBase
+{
+public:
+ explicit XclImpListBoxObj( const XclImpRoot& rRoot );
+
+private:
+ /** Reads listbox settings and selection. */
+ void ReadFullLbsData( XclImpStream& rStrm, std::size_t nRecLeft );
+
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ ScfUInt8Vec maSelection;
+};
+
+/** A dropdown listbox control. */
+class XclImpDropDownObj final : public XclImpTbxObjListBase
+{
+public:
+ explicit XclImpDropDownObj( const XclImpRoot& rRoot );
+
+private:
+ /** Returns the type of the dropdown control. */
+ sal_uInt16 GetDropDownType() const;
+
+ /** Reads dropdown box settings. */
+ void ReadFullLbsData( XclImpStream& rStrm );
+
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Sets additional properties for the current form control. */
+ virtual void DoProcessControl( ScfPropertySet& rPropSet ) const override;
+ /** Returns the service name of the control component to be created. */
+ virtual OUString DoGetServiceName() const override;
+ /** Returns the type of the macro event to be created. */
+ virtual XclTbxEventType DoGetEventType() const override;
+
+ sal_uInt16 mnLeft;
+ sal_uInt16 mnTop;
+ sal_uInt16 mnRight;
+ sal_uInt16 mnBottom;
+ sal_uInt16 mnDropDownFlags;
+ sal_uInt16 mnLineCount;
+ sal_uInt16 mnMinWidth;
+};
+
+/** A picture, an embedded or linked OLE object, or an OCX form control. */
+class XclImpPictureObj : public XclImpRectObj, public XclImpControlHelper
+{
+public:
+ explicit XclImpPictureObj( const XclImpRoot& rRoot );
+ /** Returns the ObjectName - can use non-obvious lookup for override in the associated vba document module stream**/
+ virtual OUString GetObjName() const override;
+ /** Returns the graphic imported from the IMGDATA record. */
+ const Graphic& GetGraphic() const { return maGraphic; }
+
+ /** Returns true, if the OLE object will be shown as symbol. */
+ bool IsSymbol() const { return mbSymbol; }
+ /** Returns the storage name for the OLE object. */
+ OUString GetOleStorageName() const;
+
+ /** Returns true, if this object is an OCX form control. */
+ bool IsOcxControl() const { return mbEmbedded && mbControl && mbUseCtlsStrm; }
+ /** Returns the position in the 'Ctls' stream for additional form control data. */
+ std::size_t GetCtlsStreamPos() const { return mnCtlsStrmPos; }
+ /** Returns the size in the 'Ctls' stream for additional form control data. */
+ std::size_t GetCtlsStreamSize() const { return mnCtlsStrmSize; }
+
+protected:
+ /** Reads the contents of the a BIFF3 OBJ record from the passed stream. */
+ virtual void DoReadObj3( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF4 OBJ record from the passed stream. */
+ virtual void DoReadObj4( XclImpStream& rStrm, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
+ virtual void DoReadObj5( XclImpStream& rStrm, sal_uInt16 nNameLen, sal_uInt16 nMacroSize ) override;
+ /** Reads the contents of the specified subrecord of a BIFF8 OBJ record from stream. */
+ virtual void DoReadObj8SubRec( XclImpStream& rStrm, sal_uInt16 nSubRecId, sal_uInt16 nSubRecSize ) override;
+ /** Creates and returns a new SdrObject from the contained data. Caller takes ownership! */
+ virtual SdrObjectUniquePtr DoCreateSdrObj( XclImpDffConverter& rDffConv, const tools::Rectangle& rAnchorRect ) const override;
+ /** Override to do additional processing on the SdrObject. */
+ virtual void DoPreProcessSdrObj( XclImpDffConverter& rDffConv, SdrObject& rSdrObj ) const override;
+
+private:
+ /** Reads and sets the picture flags from a BIFF3-BIFF5 OBJ picture record. */
+ void ReadFlags3( XclImpStream& rStrm );
+ /** Reads the contents of the OBJFLAGS subrecord. */
+ void ReadFlags8( XclImpStream& rStrm );
+ /** Reads the contents of the OBJPICTFMLA subrecord. */
+ void ReadPictFmla( XclImpStream& rStrm, sal_uInt16 nLinkSize );
+
+private:
+ Graphic maGraphic; /// Picture or OLE placeholder graphic.
+ OUString maClassName; /// Class name of embedded OLE object.
+ sal_uInt32 mnStorageId; /// Identifier of the storage for this object.
+ std::size_t mnCtlsStrmPos; /// Position in 'Ctls' stream for this control.
+ std::size_t mnCtlsStrmSize; /// Size in 'Ctls' stream for this control.
+ bool mbEmbedded; /// true = Embedded OLE object.
+ bool mbLinked; /// true = Linked OLE object.
+ bool mbSymbol; /// true = Show as symbol.
+ bool mbControl; /// true = Form control, false = OLE object.
+ bool mbUseCtlsStrm; /// true = Form control data in 'Ctls' stream, false = Own storage.
+};
+
+// DFF stream conversion ======================================================
+
+/** The solver container collects all connector rules for connected objects. */
+class XclImpSolverContainer : public SvxMSDffSolverContainer
+{
+public:
+
+ /** Inserts information about a new SdrObject. */
+ void InsertSdrObjectInfo( SdrObject& rSdrObj, sal_uInt32 nDffShapeId, ShapeFlag nDffFlags );
+ /** Removes information of an SdrObject (and all child objects if it is a group). */
+ void RemoveSdrObjectInfo( SdrObject& rSdrObj );
+
+ /** Inserts the SdrObject pointers into all connector rules. */
+ void UpdateConnectorRules();
+ /** Removes all contained connector rules. */
+ void RemoveConnectorRules();
+
+private:
+ /** Updates the data of a connected shape in a connector rule. */
+ void UpdateConnection( sal_uInt32 nDffShapeId, SdrObject*& rpSdrObj, ShapeFlag* pnDffFlags = nullptr );
+
+private:
+ /** Stores data about an SdrObject processed during import. */
+ struct XclImpSdrInfo
+ {
+ SdrObject* mpSdrObj; /// Pointer to an SdrObject.
+ ShapeFlag mnDffFlags; /// Shape flags from DFF stream.
+ explicit XclImpSdrInfo() : mpSdrObj( nullptr ), mnDffFlags( ShapeFlag::NONE ) {}
+ void Set( SdrObject* pSdrObj, ShapeFlag nDffFlags )
+ { mpSdrObj = pSdrObj; mnDffFlags = nDffFlags; }
+ };
+ typedef std::map< sal_uInt32, XclImpSdrInfo > XclImpSdrInfoMap;
+ typedef std::map< SdrObject*, sal_uInt32 > XclImpSdrObjMap;
+
+ XclImpSdrInfoMap maSdrInfoMap; /// Maps shape IDs to SdrObjects and flags.
+ XclImpSdrObjMap maSdrObjMap; /// Maps SdrObjects to shape IDs.
+};
+
+/** Simple implementation of the SVX DFF manager. Implements resolving palette
+ colors. Used by XclImpDffPropSet (as is), extended by XclImpDffConverter.
+ */
+class XclImpSimpleDffConverter : public SvxMSDffManager, protected XclImpRoot
+{
+public:
+ explicit XclImpSimpleDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm );
+ virtual ~XclImpSimpleDffConverter() override;
+
+protected:
+ /** Returns a color from the Excel color palette. */
+ virtual bool GetColorFromPalette( sal_uInt16 nIndex, Color& rColor ) const override;
+};
+
+/** This is the central instance for converting binary DFF data into shape
+ objects. Used for all sheet shapes and shapes embedded in chart objects.
+
+ The class derives from SvxMSDffManager and SvxMSConvertOCXControls and
+ contains core implementation of DFF stream import and OCX form control
+ import.
+ */
+class XclImpDffConverter : public XclImpSimpleDffConverter, private oox::ole::MSConvertOCXControls
+{
+public:
+ explicit XclImpDffConverter( const XclImpRoot& rRoot, SvStream& rDffStrm );
+ virtual ~XclImpDffConverter() override;
+
+ /** Initializes the internal progress bar with the passed size and starts it. */
+ void StartProgressBar( std::size_t nProgressSize );
+ /** Increase the progress bar by the passed value. */
+ void Progress( std::size_t nDelta = 1 );
+
+ /** Initially called before the objects of the passed drawing manager are converted. */
+ void InitializeDrawing( XclImpDrawing& rDrawing, SdrModel& rSdrModel, SdrPage& rSdrPage );
+ /** Processes BIFF5 drawing objects without DFF data, inserts into the passed object list. */
+ void ProcessObject( SdrObjList& rObjList, XclImpDrawObjBase& rDrawObj );
+ /** Processes all objects in the passed list. */
+ void ProcessDrawing( const XclImpDrawObjVector& rDrawObjs );
+ /** Processes a drawing container in the passed DFF stream, converts all objects. */
+ void ProcessDrawing( SvStream& rDffStrm );
+ /** Finally called after the objects of the passed drawing manager have been converted. */
+ void FinalizeDrawing();
+
+ /** Creates the SdrObject for the passed Excel TBX form control object. */
+ SdrObjectUniquePtr CreateSdrObject( const XclImpTbxObjBase& rTbxObj, const tools::Rectangle& rAnchorRect );
+ /** Creates the SdrObject for the passed Excel OLE object or OCX form control object. */
+ SdrObjectUniquePtr CreateSdrObject( const XclImpPictureObj& rPicObj, const tools::Rectangle& rAnchorRect );
+
+ /** Returns true, if the conversion of OLE objects is supported. */
+ bool SupportsOleObjects() const;
+ /** Returns the default text margin in drawing layer units. */
+ sal_Int32 GetDefaultTextMargin() const { return mnDefTextMargin; }
+
+private:
+ // virtual functions of SvxMSDffManager
+
+ /** Reads the client anchor from the DFF stream and sets it at the correct object. */
+ virtual void ProcessClientAnchor2(
+ SvStream& rDffStrm,
+ DffRecordHeader& rHeader,
+ DffObjData& rObjData ) override;
+ /** Processes a DFF object, reads properties from DFF stream. */
+ virtual SdrObject* ProcessObj(
+ SvStream& rDffStrm,
+ DffObjData& rDffObjData,
+ SvxMSDffClientData& rClientData,
+ tools::Rectangle& rTextRect,
+ SdrObject* pOldSdrObj ) override;
+
+ /** Finalize a DFF object, sets anchor after nested objs have been loaded. */
+ virtual SdrObject* FinalizeObj(
+ DffObjData& rDffObjData,
+ SdrObject* pOldSdrObj ) override;
+
+ // virtual functions of SvxMSConvertOCXControls
+
+ /** Inserts the passed control rxFComp into the form. Needs call to SetCurrentForm() before. */
+ virtual bool InsertControl(
+ const css::uno::Reference<
+ css::form::XFormComponent >& rxFormComp,
+ const css::awt::Size& rSize,
+ css::uno::Reference<
+ css::drawing::XShape >* pxShape,
+ bool bFloatingCtrl ) override;
+
+private:
+ /** Data per registered drawing manager, will be stacked for recursive calls. */
+ struct XclImpDffConvData
+ {
+ XclImpDrawing& mrDrawing; /// Current drawing container with all drawing objects.
+ SdrModel& mrSdrModel; /// The SdrModel of the drawing manager.
+ SdrPage& mrSdrPage; /// The SdrPage of the drawing manager.
+ XclImpSolverContainer maSolverCont; /// The solver container for connector rules.
+ css::uno::Reference< css::form::XForm >
+ mxCtrlForm; /// Controls form of current drawing page.
+ sal_Int32 mnLastCtrlIndex; /// Last insertion index of a form control (for macro events).
+ bool mbHasCtrlForm; /// True = mxCtrlForm is initialized (but maybe still null).
+
+ explicit XclImpDffConvData( XclImpDrawing& rDrawing,
+ SdrModel& rSdrModel, SdrPage& rSdrPage );
+ };
+
+ /** Returns the current drawing manager data struct from top of the stack. */
+ XclImpDffConvData& GetConvData();
+ /** Returns the current drawing manager data struct from top of the stack. */
+ const XclImpDffConvData& GetConvData() const;
+
+ /** Reads contents of a hyperlink property and returns the extracted URL. */
+ OUString ReadHlinkProperty( SvStream& rDffStrm ) const;
+
+ /** Processes a drawing container (all drawing data of a sheet). */
+ bool ProcessDgContainer( SvStream& rDffStrm, const DffRecordHeader& rDgHeader );
+ /** Processes the global shape group container (all shapes of a sheet). */
+ bool ProcessShGrContainer( SvStream& rDffStrm, const DffRecordHeader& rShGrHeader );
+ /** Processes the solver container (connectors of a sheet). */
+ bool ProcessSolverContainer( SvStream& rDffStrm, const DffRecordHeader& rSolverHeader );
+ /** Processes a shape or shape group container (one top-level shape). */
+ bool ProcessShContainer( SvStream& rDffStrm, const DffRecordHeader& rShHeader );
+
+ /** Inserts the passed SdrObject into the document. This function takes ownership of pSdrObj! */
+ void InsertSdrObject( SdrObjList& rObjList, const XclImpDrawObjBase& rDrawObj, SdrObject* pSdrObj );
+ /** Initializes the mxCtrlForm referring to the standard controls form. */
+ void InitControlForm();
+ /** Notify that this document contains a macro event handler */
+ void NotifyMacroEventRead();
+
+private:
+ typedef std::shared_ptr< ScfProgressBar > ScfProgressBarRef;
+ typedef std::shared_ptr< XclImpDffConvData > XclImpDffConvDataRef;
+
+ tools::SvRef<SotStorageStream> mxCtlsStrm; /// The 'Ctls' stream for OCX form controls.
+ ScfProgressBarRef mxProgress; /// The progress bar used in ProcessObj().
+ std::vector< XclImpDffConvDataRef >
+ maDataStack; /// Stack for registered drawing managers.
+ sal_uInt32 mnOleImpFlags; /// Application OLE import settings.
+ sal_Int32 mnDefTextMargin; /// Default margin in text boxes.
+ bool mbNotifyMacroEventRead; /// If we have already seen a macro event
+};
+
+// Drawing manager ============================================================
+
+/** Base class for a container for all objects on a drawing (spreadsheet or
+ embedded chart object). */
+class XclImpDrawing : protected XclImpRoot
+{
+public:
+ explicit XclImpDrawing( const XclImpRoot& rRoot, bool bOleObjects );
+ virtual ~XclImpDrawing() override;
+
+ /** Reads and returns a bitmap from the IMGDATA record. */
+ static Graphic ReadImgData( const XclImpRoot& rRoot, XclImpStream& rStrm );
+
+ /** Reads a plain OBJ record (without leading DFF data). */
+ void ReadObj( XclImpStream& rStrm );
+ /** Reads the MSODRAWING or MSODRAWINGSELECTION record. */
+ void ReadMsoDrawing( XclImpStream& rStrm );
+
+ /** Returns true, if the conversion of OLE objects is supported. */
+ bool SupportsOleObjects() const { return mbOleObjs; }
+ /** Finds the OBJ record data related to the DFF shape at the passed position. */
+ XclImpDrawObjRef FindDrawObj( const DffRecordHeader& rHeader ) const;
+ /** Finds the OBJ record data specified by the passed object identifier. */
+ XclImpDrawObjRef FindDrawObj( sal_uInt16 nObjId ) const;
+ /** Finds the textbox data related to the DFF shape at the passed position. */
+ const XclImpObjTextData* FindTextData( const DffRecordHeader& rHeader ) const;
+
+ /** Sets the object with the passed identification to be skipped on import. */
+ void SetSkipObj( sal_uInt16 nObjId );
+ /** Returns the size of the progress bar shown while processing all objects. */
+ std::size_t GetProgressSize() const;
+
+ /** Derived classes calculate the resulting rectangle of the passed anchor. */
+ virtual tools::Rectangle CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const = 0;
+ /** Called whenever an object has been inserted into the draw page. */
+ virtual void OnObjectInserted( const XclImpDrawObjBase& rDrawObj ) = 0;
+
+protected:
+ /** Appends a new drawing object to the list of raw objects (without DFF data). */
+ void AppendRawObject( const XclImpDrawObjRef& rxDrawObj );
+ /** Converts all objects and inserts them into the current drawing page. */
+ void ImplConvertObjects( XclImpDffConverter& rDffConv, SdrModel& rSdrModel, SdrPage& rSdrPage );
+
+private:
+ /** Reads and returns a bitmap from WMF/PICT format. */
+ static void ReadWmf( Graphic& rGraphic, XclImpStream& rStrm );
+ /** Reads and returns a bitmap from BMP format. */
+ static void ReadBmp( Graphic& rGraphic, const XclImpRoot& rRoot, XclImpStream& rStrm );
+
+ /** Reads contents of a DFF record and append data to internal DFF stream. */
+ void ReadDffRecord( XclImpStream& rStrm );
+ /** Reads a BIFF8 OBJ record following an MSODRAWING record. */
+ void ReadObj8( XclImpStream& rStrm );
+ /** Reads the TXO record and following CONTINUE records containing string and formatting. */
+ void ReadTxo( XclImpStream& rStrm );
+
+private:
+ typedef std::map< std::size_t, XclImpDrawObjRef > XclImpObjMap;
+ typedef std::map< sal_uInt16, XclImpDrawObjRef > XclImpObjMapById;
+ typedef std::shared_ptr< XclImpObjTextData > XclImpObjTextRef;
+ typedef std::map< std::size_t, XclImpObjTextRef > XclImpObjTextMap;
+
+ XclImpDrawObjVector maRawObjs; /// BIFF5 objects without DFF data.
+ SvMemoryStream maDffStrm; /// Copy of the DFF page stream in memory.
+ XclImpObjMap maObjMap; /// Maps BIFF8 drawing objects to DFF stream position.
+ XclImpObjMapById maObjMapId; /// Maps BIFF8 drawing objects to object ID.
+ XclImpObjTextMap maTextMap; /// Maps BIFF8 TXO textbox data to DFF stream position.
+ ScfUInt16Vec maSkipObjs; /// IDs of all objects to be skipped.
+ bool mbOleObjs; /// True = draw model supports OLE objects.
+};
+
+/** Drawing manager of a single sheet. */
+class XclImpSheetDrawing : public XclImpDrawing
+{
+public:
+ explicit XclImpSheetDrawing( const XclImpRoot& rRoot, SCTAB nScTab );
+
+ /** Reads the NOTE record. */
+ void ReadNote( XclImpStream& rStrm );
+ /** Inserts a new chart object and reads the chart substream (BOF/EOF block).
+ @descr Used to import chart sheets, which do not have a corresponding OBJ record. */
+ void ReadTabChart( XclImpStream& rStrm );
+
+ /** Returns the total cell range covered by any shapes in the sheet. */
+ const ScRange& GetUsedArea() const { return maScUsedArea; }
+ /** Converts all objects and inserts them into the sheet drawing page. */
+ void ConvertObjects( XclImpDffConverter& rDffConv );
+
+ /** Calculate the resulting rectangle of the passed anchor. */
+ virtual tools::Rectangle CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const override;
+ /** On call, updates the used area of the sheet. */
+ virtual void OnObjectInserted( const XclImpDrawObjBase& rDrawObj ) override;
+
+private:
+ /** Reads a BIFF3-BIFF5 NOTE record. */
+ void ReadNote3( XclImpStream& rStrm );
+ /** Reads a BIFF8 NOTE record. */
+ void ReadNote8( XclImpStream& rStrm );
+
+private:
+ ScRange maScUsedArea; /// Sheet index and used area in this sheet.
+};
+
+// The object manager =========================================================
+
+/** Stores all drawing and OLE objects and additional data related to these objects. */
+class XclImpObjectManager : protected XclImpRoot
+{
+public:
+ explicit XclImpObjectManager( const XclImpRoot& rRoot );
+ virtual ~XclImpObjectManager() override;
+
+ /** Reads the MSODRAWINGGROUP record. */
+ void ReadMsoDrawingGroup( XclImpStream& rStrm );
+
+ /** Returns (initially creates) the drawing manager of the specified sheet. */
+ XclImpSheetDrawing& GetSheetDrawing( SCTAB nScTab );
+ /** Inserts all objects into the Calc document. */
+ void ConvertObjects();
+
+ /** Returns the default name for the passed object. */
+ OUString GetDefaultObjName( const XclImpDrawObjBase& rDrawObj ) const;
+ /** Returns the used area in the sheet with the passed index. */
+ ScRange GetUsedArea( SCTAB nScTab ) const;
+ /** Sets the container to receive overridden shape/ctrl names from
+ the filter. */
+ void SetOleNameOverrideInfo( const css::uno::Reference< css::container::XNameContainer >& rxOverrideInfo ) { mxOleCtrlNameOverride = rxOverrideInfo; }
+ /** Returns the name of overridden name ( or zero length string ) for
+ associated object id. */
+ OUString GetOleNameOverride( SCTAB nTab, sal_uInt16 nObjId );
+
+private:
+ typedef std::map< sal_uInt16, OUString > DefObjNameMap;
+ typedef std::shared_ptr< XclImpSheetDrawing > XclImpSheetDrawingRef;
+ typedef std::map< SCTAB, XclImpSheetDrawingRef > XclImpSheetDrawingMap;
+
+ css::uno::Reference< css::container::XNameContainer > mxOleCtrlNameOverride;
+ DefObjNameMap maDefObjNames; /// Default base names for all object types.
+ SvMemoryStream maDggStrm; /// Copy of global DFF data (DGG container) in memory.
+ XclImpSheetDrawingMap maSheetDrawings; /// Drawing managers of all sheets.
+};
+
+// DFF property set helper ====================================================
+
+/** This class reads a DFF property set (msofbtOPT record).
+
+ It can return separate property values or an item set which contains items
+ translated from these properties.
+ */
+class XclImpDffPropSet : protected XclImpRoot
+{
+public:
+ explicit XclImpDffPropSet( const XclImpRoot& rRoot );
+
+ /** Reads a DFF property set from the stream.
+ @descr The stream must point to the start of a DFF record containing properties. */
+ void Read( XclImpStream& rStrm );
+
+ /** Returns the specified property or zero, if not extant. */
+ sal_uInt32 GetPropertyValue( sal_uInt16 nPropId ) const;
+
+ /** Translates the properties and fills the item set. */
+ void FillToItemSet( SfxItemSet& rItemSet ) const;
+
+private:
+ typedef std::unique_ptr<SvMemoryStream> SvMemoryStreamPtr;
+
+ SvMemoryStream maDummyStrm; /// Dummy DGG stream for DFF manager.
+ XclImpSimpleDffConverter maDffConv; /// DFF converter used to resolve palette colors.
+ SvMemoryStreamPtr mxMemStrm; /// Helper stream.
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclImpDffPropSet& rPropSet );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiformula.hxx b/sc/source/filter/inc/xiformula.hxx
new file mode 100644
index 000000000..b379766f0
--- /dev/null
+++ b/sc/source/filter/inc/xiformula.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlformula.hxx"
+#include "xiroot.hxx"
+#include <memory>
+
+// Formula compiler ===========================================================
+
+class ScRangeList;
+class XclImpFmlaCompImpl;
+
+/** The formula compiler to create Calc token arrays from Excel token arrays. */
+class XclImpFormulaCompiler : protected XclImpRoot
+{
+public:
+ explicit XclImpFormulaCompiler( const XclImpRoot& rRoot );
+ virtual ~XclImpFormulaCompiler() override;
+
+ /** Creates a range list from the passed Excel token array.
+ @param rStrm Stream pointing to additional formula data (e.g. constant array data). */
+ void CreateRangeList(
+ ScRangeList& rScRanges, XclFormulaType eType,
+ const XclTokenArray& rXclTokArr, XclImpStream& rStrm );
+
+ /**
+ * Creates a formula token array from the Excel token array.
+ */
+ std::unique_ptr<ScTokenArray> CreateFormula( XclFormulaType eType, const XclTokenArray& rXclTokArr );
+
+private:
+ typedef std::shared_ptr< XclImpFmlaCompImpl > XclImpFmlaCompImplRef;
+ XclImpFmlaCompImplRef mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xihelper.hxx b/sc/source/filter/inc/xihelper.hxx
new file mode 100644
index 000000000..dda6456d5
--- /dev/null
+++ b/sc/source/filter/inc/xihelper.hxx
@@ -0,0 +1,350 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <editeng/editdata.hxx>
+#include <types.hxx>
+#include "xladdress.hxx"
+#include "xiroot.hxx"
+#include "xltools.hxx"
+#include <memory>
+#include <vector>
+
+class ScRangeList;
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+// Excel->Calc cell address/range conversion ==================================
+
+/** Provides functions to convert Excel cell addresses to Calc cell addresses. */
+class XclImpAddressConverter : public XclAddressConverterBase
+{
+public:
+ explicit XclImpAddressConverter( const XclImpRoot& rRoot );
+
+ // cell address -----------------------------------------------------------
+
+ /** Checks if the passed Excel cell address is valid.
+ @param rXclPos The Excel cell address to check.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is not valid.
+ @return true = Cell address in rXclPos is valid. */
+ bool CheckAddress( const XclAddress& rXclPos, bool bWarn );
+
+ /** Converts the passed Excel cell address to a Calc cell address.
+ @param rScPos (Out) The converted Calc cell address, if valid.
+ @param rXclPos The Excel cell address to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is invalid.
+ @return true = Cell address returned in rScPos is valid. */
+ bool ConvertAddress( ScAddress& rScPos,
+ const XclAddress& rXclPos, SCTAB nScTab, bool bWarn );
+
+ /** Returns a valid cell address by moving it into allowed dimensions.
+ @param rXclPos The Excel cell address to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell address is invalid.
+ @return The converted Calc cell address. */
+ ScAddress CreateValidAddress( const XclAddress& rXclPos,
+ SCTAB nScTab, bool bWarn );
+
+ // cell range -------------------------------------------------------------
+
+ /** Converts the passed Excel cell range to a Calc cell range.
+ @param rScRange (Out) The converted Calc cell range, if valid.
+ @param rXclRange The Excel cell range to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if the cell range contains invalid cells.
+ @return true = Cell range returned in rScRange is valid (original or cropped). */
+ bool ConvertRange( ScRange& rScRange, const XclRange& rXclRange,
+ SCTAB nScTab1, SCTAB nScTab2, bool bWarn );
+
+ // cell range list --------------------------------------------------------
+
+ /** Converts the passed Excel cell range list to a Calc cell range list.
+ @descr The start position of the ranges will not be modified. Cell
+ ranges that fit partly into valid dimensions are cropped
+ accordingly. Cell ranges that do not fit at all, are not inserted
+ into the Calc cell range list.
+ @param rScRanges (Out) The converted Calc cell range list.
+ @param rXclRanges The Excel cell range list to convert.
+ @param bWarn true = Sets the internal flag that produces a warning box
+ after loading/saving the file, if at least one of the cell ranges
+ contains invalid cells. */
+ void ConvertRangeList( ScRangeList& rScRanges,
+ const XclRangeList& rXclRanges, SCTAB nScTab, bool bWarn );
+};
+
+// String->EditEngine conversion ==============================================
+
+class EditTextObject;
+
+/** This class provides methods to convert an XclImpString.
+ @The string can be converted to an edit engine text object or directly
+ to a Calc edit cell. */
+class XclImpStringHelper
+{
+public:
+ /** delete copy constructor */
+ XclImpStringHelper(const XclImpStringHelper&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpStringHelper& operator=(const XclImpStringHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpStringHelper() = delete;
+ /** Returns a new edit engine text object.
+ @param nXFIndex Index to XF for first text portion (for escapement). */
+ static std::unique_ptr<EditTextObject> CreateTextObject(
+ const XclImpRoot& rRoot,
+ const XclImpString& rString );
+
+ static void SetToDocument(
+ ScDocumentImport& rDoc, const ScAddress& rPos, const XclImpRoot& rRoot,
+ const XclImpString& rString, sal_uInt16 nXFIndex );
+};
+
+// Header/footer conversion ===================================================
+
+class EditEngine;
+class SfxItemSet;
+class SvxFieldItem;
+struct XclFontData;
+
+/** Converts an Excel header/footer string into three edit engine text objects.
+ @descr Header/footer content is divided into three parts: Left, center and
+ right portion. All formatting information is encoded in the Excel string
+ using special character sequences. A control sequence starts with the ampersand
+ character.
+
+ Supported control sequences:
+ &L start of left portion
+ &C start of center portion
+ &R start of right portion
+ &P current page number
+ &N page count
+ &D current date
+ &T current time
+ &A table name
+ &F file name without path (see also &Z&F)
+ &Z file path without file name (converted to full file name, see also &Z&F)
+ &Z&F file path and name
+ &U underlining on/off
+ &E double underlining on/off
+ &S strikeout characters on/off
+ &X superscript on/off
+ &Y subscript on/off
+ &"fontname,fontstyle" use font with name 'fontname' and style 'fontstyle'
+ &fontheight set font height in points ('fontheight' is a decimal value)
+
+ Known but unsupported control sequences:
+ &G picture
+ */
+class XclImpHFConverter : protected XclImpRoot
+{
+public:
+ /** delete copy constructor */
+ XclImpHFConverter(const XclImpHFConverter&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpHFConverter& operator=(const XclImpHFConverter&) = delete;
+
+ explicit XclImpHFConverter( const XclImpRoot& rRoot );
+ virtual ~XclImpHFConverter() override;
+
+ /** Parses the passed string and creates three new edit engine text objects. */
+ void ParseString( const OUString& rHFString );
+
+ /** Creates a ScPageHFItem and inserts it into the passed item set. */
+ void FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nWhichId ) const;
+ /** Returns the total height of the converted header or footer in twips. */
+ sal_Int32 GetTotalHeight() const;
+
+private: // types
+ typedef ::std::unique_ptr< XclFontData > XclFontDataPtr;
+
+ /** Enumerates the supported header/footer portions. */
+ enum XclImpHFPortion { EXC_HF_LEFT, EXC_HF_CENTER, EXC_HF_RIGHT, EXC_HF_PORTION_COUNT };
+
+ /** Contains all information about a header/footer portion. */
+ struct XclImpHFPortionInfo
+ {
+ typedef std::shared_ptr< EditTextObject > EditTextObjectRef;
+ EditTextObjectRef mxObj; /// Edit engine text object.
+ ESelection maSel; /// Edit engine selection.
+ sal_Int32 mnHeight; /// Height of previous lines in twips.
+ sal_uInt16 mnMaxLineHt; /// Maximum font height for the current text line.
+ explicit XclImpHFPortionInfo();
+ };
+
+private:
+ /** Returns the current edit engine text object. */
+ XclImpHFPortionInfo& GetCurrInfo() { return maInfos[ meCurrObj ]; }
+ /** Returns the current edit engine text object. */
+ XclImpHFPortionInfo::EditTextObjectRef& GetCurrObj() { return GetCurrInfo().mxObj; }
+ /** Returns the current selection. */
+ ESelection& GetCurrSel() { return GetCurrInfo().maSel; }
+
+ /** Returns the maximum line height of the specified portion. */
+ sal_uInt16 GetMaxLineHeight( XclImpHFPortion ePortion ) const;
+
+ /** Updates the maximum line height of the specified portion, using the current font size. */
+ void UpdateMaxLineHeight( XclImpHFPortion ePortion );
+ /** Updates the current maximum line height, using the current font size. */
+ void UpdateCurrMaxLineHeight();
+
+ /** Sets the font attributes at the current selection.
+ @descr After that, the start position of the current selection object is
+ adjusted to the end of the selection. */
+ void SetAttribs();
+ /** Resets font data to application default font. */
+ void ResetFontData();
+
+ /** Inserts maCurrText into edit engine and adjusts the current selection object.
+ @descr The text shall not contain a newline character.
+ The text will be cleared after insertion. */
+ void InsertText();
+ /** Inserts the passed text field and adjusts the current selection object. */
+ void InsertField( const SvxFieldItem& rFieldItem );
+ /** Inserts a line break and adjusts the current selection object. */
+ void InsertLineBreak();
+
+ /** Creates the edit engine text object of current portion from edit engine. */
+ void CreateCurrObject();
+ /** Changes current header/footer portion to eNew.
+ @descr Creates text object of current portion and reinitializes edit engine. */
+ void SetNewPortion( XclImpHFPortion eNew );
+
+private:
+ EditEngine& mrEE; /// The header/footer edit engine.
+ std::vector< XclImpHFPortionInfo >
+ maInfos; /// Edit engine text objects for all portions.
+ OUStringBuffer maCurrText; /// Current text to insert into edit engine.
+ XclFontDataPtr mxFontData; /// Font data of current text.
+ XclImpHFPortion meCurrObj; /// The current portion.
+};
+
+// URL conversion =============================================================
+
+/** This class contains static methods to decode a URL stored in an Excel file.
+ @descr Excel URLs can contain a sheet name, for instance: path\[test.xls]Sheet1
+ This sheet name will be extracted automatically. */
+class XclImpUrlHelper
+{
+public:
+ /** delete copy constructor */
+ XclImpUrlHelper(const XclImpUrlHelper&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpUrlHelper& operator=(const XclImpUrlHelper&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static methods. */
+ XclImpUrlHelper() = delete;
+
+ /** Decodes an encoded external document URL with optional sheet name.
+ @param rUrl Returns the decoded file name incl. path.
+ @param rTabName Returns the decoded sheet name.
+ @param rbSameWb Returns true, if the URL is a reference to the own workbook.
+ @param rEncodedUrl An encoded URL from Excel. */
+ static void DecodeUrl(
+ OUString& rUrl,
+ OUString& rTabName,
+ bool& rbSameWb,
+ const XclImpRoot& rRoot,
+ const OUString& rEncodedUrl );
+
+ /** Decodes an encoded external document URL without sheet name.
+ @param rUrl Returns the decoded file name incl. path.
+ @param rbSameWb Returns true, if the URL is a reference to the own workbook.
+ @param rEncodedUrl An encoded URL from Excel. */
+
+ static void DecodeUrl(
+ OUString& rUrl,
+ bool& rbSameWb,
+ const XclImpRoot& rRoot,
+ const OUString& rEncodedUrl );
+
+ /** Decodes the passed URL to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ static bool DecodeLink( OUString& rApplic, OUString& rTopic, const OUString& rEncUrl );
+};
+
+// Cached values ==============================================================
+
+class ScTokenArray;
+
+/** This class stores one cached value of a cached value list (used for instance in
+ CRN, EXTERNNAME, tArray). */
+class XclImpCachedValue
+{
+public:
+ /** delete copy constructor */
+ XclImpCachedValue(const XclImpCachedValue&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpCachedValue& operator=(const XclImpCachedValue&) = delete;
+ /** Creates a cached value and reads contents from stream and stores it with its array address. */
+ explicit XclImpCachedValue( XclImpStream& rStrm );
+ virtual ~XclImpCachedValue();
+
+ /** Returns the type of the cached value (EXC_CACHEDVAL_*). */
+ sal_uInt8 GetType() const { return mnType; }
+ /** Returns the cached string value, if this value is a string, else an empty string. */
+ const OUString& GetString() const { return maStr;}
+ /** Returns the cached number, if this value has number type, else 0.0. */
+ double GetValue() const { return mfValue; }
+ /** Returns the cached Boolean value, if this value has Boolean type, else false. */
+ bool GetBool() const { return (mnType == EXC_CACHEDVAL_BOOL) && (mnBoolErr != 0); }
+ /** Returns the cached Calc error code, if this value has Error type, else 0. */
+ sal_uInt8 GetXclError() const { return (mnType == EXC_CACHEDVAL_ERROR) ? mnBoolErr : EXC_ERR_NA; }
+ /** Returns the cached Calc error code, if this value has Error type, else 0. */
+ FormulaError GetScError() const;
+
+protected:
+ typedef ::std::unique_ptr< const ScTokenArray > ScTokenArrayPtr;
+
+ OUString maStr; /// Cached value is a string.
+ double mfValue; /// Cached value is a double.
+ ScTokenArrayPtr mxTokArr; /// Cached value is a formula or error code or Boolean.
+ sal_uInt8 mnBoolErr; /// Boolean value or Excel error code.
+ sal_uInt8 mnType; /// The type of the cached value (EXC_CACHEDVAL_*).
+};
+
+/** Contains cached values in a 2-dimensional array. */
+class XclImpCachedMatrix
+{
+public:
+ explicit XclImpCachedMatrix( XclImpStream& rStrm );
+ ~XclImpCachedMatrix();
+
+ /** Creates a new ScMatrix object and fills it with the contained values. */
+ ScMatrixRef CreateScMatrix( svl::SharedStringPool& rPool ) const;
+
+private:
+ typedef std::vector< std::unique_ptr<XclImpCachedValue> > XclImpValueList;
+
+ XclImpValueList maValueList; /// List of cached cell values.
+ SCSIZE mnScCols; /// Number of cached columns.
+ SCSIZE mnScRows; /// Number of cached rows.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xilink.hxx b/sc/source/filter/inc/xilink.hxx
new file mode 100644
index 000000000..cdfef4c82
--- /dev/null
+++ b/sc/source/filter/inc/xilink.hxx
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <map>
+#include "xllink.hxx"
+#include "xiroot.hxx"
+#include "ftools.hxx"
+#include <types.hxx>
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+/* ============================================================================
+Classes for import of different kinds of internal/external references.
+- 3D cell and cell range links
+- External cell and cell range links
+- External defined names
+- Add-in functions
+- DDE links
+- OLE object links
+============================================================================ */
+
+// Excel sheet indexes ========================================================
+
+/** A buffer containing information about names and creation order of sheets.
+
+ The first purpose of this buffer is to translate original Excel
+ sheet names into Calc sheet indexes. This is not trivial because the filter
+ may rename the Calc sheets during creation. This buffer stores the original
+ Excel sheet names with the corresponding Calc sheet indexes.
+
+ The second purpose is to store the creation order of all sheets inside the
+ Excel workbook. The creation order list is contained in the TABID record
+ and needed to import the change log. Example: if the list contains 3;1;2
+ this means that the second sheet in the file was created first, then the
+ third sheet in the file was created and finally the first sheet.
+ */
+class XclImpTabInfo
+{
+public:
+ // original Excel sheet names ---------------------------------------------
+
+ /** Appends an original Excel sheet name with corresponding Calc sheet index. */
+ void AppendXclTabName( const OUString& rXclTabName, SCTAB nScTab );
+ /** Inserts a Calc sheet index (increases all following sheet indexes). */
+ void InsertScTab( SCTAB nScTab );
+
+ /** Returns the Calc sheet index from the passed original Excel sheet name. */
+ SCTAB GetScTabFromXclName( const OUString& rXclTabName ) const;
+
+ // record creation order - TABID record -----------------------------------
+
+ /** Reads the TABID record. */
+ void ReadTabid( XclImpStream& rStrm );
+
+ /** Returns the current sheet index calculated from creation index.
+ @param nCreatedId The creation index of the sheet (1-based).
+ @param nMaxTabId All values greater than this parameter are not used to find the index.
+ @return The 0-based index of the sheet nCreatedId if it is contained in the list.
+ Example: The buffer is 3;5;2;4;1, nCreatedId is 1 and nMaxTabId is 3. The function will
+ return 2 which is the 0-based index of sheet 1 in the list 3;2;1. */
+ sal_uInt16 GetCurrentIndex( sal_uInt16 nCreatedId, sal_uInt16 nMaxTabId ) const;
+
+private:
+ typedef ::std::map< OUString, SCTAB > XclTabNameMap;
+
+ XclTabNameMap maTabNames; /// All Excel sheet names with Calc sheet index.
+ ScfUInt16Vec maTabIdVec; /// The vector with sheet indexes.
+};
+
+// External names =============================================================
+
+/** Type of an external name. */
+enum XclImpExtNameType
+{
+ xlExtName, /// An external defined name.
+ xlExtAddIn, /// An add-in function name.
+ xlExtDDE, /// A DDE link range.
+ xlExtOLE, /// An OLE object link.
+ xlExtEuroConvert /// An external in Excel, but internal in OO function name.
+};
+
+class XclImpCachedMatrix;
+class ScTokenArray;
+class XclImpSupbook;
+
+/** Stores contents of an external name.
+ @descr Supported: External defined names, AddIn names, DDE links and OLE objects. */
+class XclImpExtName
+{
+ /**
+ * MOper, multiple operands, stores cached values of external range
+ * specified in the record.
+ */
+ class MOper
+ {
+ public:
+ MOper(svl::SharedStringPool& rPool, XclImpStream& rStrm);
+ const ScMatrix& GetCache() const;
+ private:
+ ScMatrixRef mxCached;
+ };
+
+public:
+ /** Reads the external name from the stream. */
+ explicit XclImpExtName( XclImpSupbook& rSupbook, XclImpStream& rStrm,
+ XclSupbookType eSubType, ExcelToSc* pFormulaConv );
+ ~XclImpExtName();
+
+ /** Create and apply the cached list of this DDE Link to the document. */
+ void CreateDdeData( ScDocument& rDoc,
+ const OUString& rApplc, const OUString& rExtDoc ) const;
+
+ void CreateExtNameData( const ScDocument& rDoc, sal_uInt16 nFileId ) const;
+
+ /**
+ * Create OLE link data. OLE link data is converted to external
+ * reference, since OLE link doesn't work cross-platform, and is not very
+ * reliable even on Windows.
+ */
+ bool CreateOleData(const ScDocument& rDoc, const OUString& rUrl,
+ sal_uInt16& rFileId, OUString& rTabName, ScRange& rRange) const;
+
+ bool HasFormulaTokens() const;
+
+ XclImpExtNameType GetType() const { return meType; }
+ const OUString& GetName() const { return maName; }
+ sal_uInt32 GetStorageId() const { return mnStorageId; }
+
+private:
+ typedef ::std::unique_ptr< XclImpCachedMatrix > XclImpCachedMatrixPtr;
+ typedef ::std::unique_ptr< ScTokenArray > TokenArrayPtr;
+
+ XclImpCachedMatrixPtr mxDdeMatrix; /// Cached results of the DDE link.
+ std::unique_ptr<MOper> mpMOper; /// Cached values for OLE link
+ TokenArrayPtr mxArray; /// Formula tokens for external name.
+ OUString maName; /// The name of the external name.
+ sal_uInt32 mnStorageId; /// Storage ID for OLE object storages.
+ XclImpExtNameType meType; /// Type of the external name.
+};
+
+// Import link manager ========================================================
+
+class XclImpLinkManagerImpl;
+
+/** This is the central class for the import of all internal/external links.
+ @descr This manager stores all data about external documents with their sheets
+ and cached cell contents. Additionally it handles external names, such as add-in
+ function names, DDE links, and OLE object links.
+ File contents in BIFF8:
+ - Record SUPBOOK: Contains the name of an external document and the names of its sheets.
+ This record is optionally followed by NAME, EXTERNNAME, XCT and CRN records.
+ - Record XCT: Contains the number and sheet index of the following CRN records.
+ - Record CRN: Contains addresses (row and column) and values of external referenced cells.
+ - Record NAME: Contains defined names of the own workbook.
+ - Record EXTERNNAME: Contains external defined names, DDE links, or OLE object links.
+ - Record EXTERNSHEET: Contains indexes to URLs of external documents (SUPBOOKs)
+ and sheet indexes for each external reference used anywhere in the workbook.
+ This record follows a list of SUPBOOK records (with their attached records).
+*/
+class XclImpLinkManager : protected XclImpRoot
+{
+public:
+ explicit XclImpLinkManager( const XclImpRoot& rRoot );
+ virtual ~XclImpLinkManager() override;
+
+ /** Reads the EXTERNSHEET record. */
+ void ReadExternsheet( XclImpStream& rStrm );
+ /** Reads a SUPBOOK record. */
+ void ReadSupbook( XclImpStream& rStrm );
+ /** Reads an XCT record and appends it to the current SUPBOOK. */
+ void ReadXct( XclImpStream& rStrm );
+ /** Reads a CRN record and appends it to the current SUPBOOK. */
+ void ReadCrn( XclImpStream& rStrm );
+ /** Reads an EXTERNNAME record and appends it to the current SUPBOOK. */
+ void ReadExternname( XclImpStream& rStrm, ExcelToSc* pFormulaConv );
+
+ /** Returns true, if the specified XTI entry contains an internal reference. */
+ bool IsSelfRef( sal_uInt16 nXtiIndex ) const;
+ /** Returns the Calc sheet index range of the specified XTI entry.
+ @return true = XTI data found, returned sheet index range is valid. */
+ bool GetScTabRange(
+ SCTAB& rnFirstScTab, SCTAB& rnLastScTab,
+ sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified external name or 0 on error. */
+ const XclImpExtName* GetExternName( sal_uInt16 nXtiIndex, sal_uInt16 nExtName ) const;
+
+ const OUString* GetSupbookUrl( sal_uInt16 nXtiIndex ) const;
+
+ OUString GetSupbookTabName( sal_uInt16 nXti, sal_uInt16 nXtiTab ) const;
+
+ /** Tries to decode the URL of the specified XTI entry to OLE or DDE link components.
+ @descr For DDE links: Decodes to application name and topic.
+ For OLE object links: Decodes to class name and document URL.
+ @return true = decoding was successful, returned strings are valid (not empty). */
+ bool GetLinkData( OUString& rApplic, OUString& rTopic, sal_uInt16 nXtiIndex ) const;
+ /** Returns the specified macro name or an empty string on error. */
+ OUString GetMacroName( sal_uInt16 nExtSheet, sal_uInt16 nExtName ) const;
+
+private:
+ typedef ::std::unique_ptr< XclImpLinkManagerImpl > XclImpLinkMgrImplPtr;
+ XclImpLinkMgrImplPtr mxImpl;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiname.hxx b/sc/source/filter/inc/xiname.hxx
new file mode 100644
index 000000000..8d86909c0
--- /dev/null
+++ b/sc/source/filter/inc/xiname.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xiroot.hxx"
+#include "xistream.hxx"
+
+#include <rangenam.hxx>
+
+#include <memory>
+#include <vector>
+
+class ScTokenArray;
+
+/** Represents a defined name. It may be related to a single sheet or global. */
+class XclImpName : protected XclImpRoot
+{
+ struct TokenStrmData
+ {
+ XclImpStream& mrStrm;
+ XclImpStreamPos maStrmPos;
+ std::size_t mnStrmPos;
+ std::size_t mnStrmSize;
+
+ TokenStrmData( XclImpStream& rStrm );
+ };
+
+public:
+ XclImpName(const XclImpName&) = delete;
+ const XclImpName& operator=(const XclImpName&) = delete;
+
+ explicit XclImpName( XclImpStream& rStrm, sal_uInt16 nXclNameIdx );
+
+ const OUString& GetXclName() const { return maXclName; }
+ const OUString& GetScName() const { return maScName; }
+ SCTAB GetScTab() const { return mnScTab; }
+ const ScRangeData* GetScRangeData() const { return mpScData; }
+ bool IsGlobal() const { return mnScTab == SCTAB_MAX; }
+ bool IsVBName() const { return mbVBName; }
+ bool IsMacro() const { return mbMacro; }
+ void ConvertTokens();
+
+private:
+ void InsertName(const ScTokenArray* pArray);
+
+ OUString maXclName; /// Original name read from the file.
+ OUString maScName; /// Name inserted into the Calc document.
+ const ScRangeData* mpScData; /// Pointer to Calc defined name (no ownership).
+ SCTAB mnScTab; /// Calc sheet index of local names.
+ ScRangeData::Type meNameType;
+ sal_uInt16 mnXclTab;
+ sal_uInt16 mnNameIndex;
+ bool mbVBName:1; /// true = Visual Basic procedure or function.
+ bool mbMacro:1; /// Whether it's a user-defined macro.
+
+ std::unique_ptr<TokenStrmData> mpTokensData; /// For later conversion of token array.
+};
+
+/** This buffer contains all internal defined names of the document.
+ @descr It manages the position of the names in the document, means if they are
+ global or attached to a specific sheet. While inserting the names into the Calc
+ document this buffer resolves conflicts caused by equal names from different
+ sheets. */
+class XclImpNameManager : protected XclImpRoot
+{
+public:
+ explicit XclImpNameManager( const XclImpRoot& rRoot );
+
+ /** Reads a NAME record and creates an entry in this buffer. */
+ void ReadName( XclImpStream& rStrm );
+
+ /** Tries to find the name used in Calc, based on the original Excel defined name.
+ @param nScTab The sheet index for local names or SCTAB_MAX for global names.
+ If no local name is found, tries to find a matching global name.
+ @return Pointer to the defined name or 0 on error. */
+ const XclImpName* FindName( std::u16string_view rXclName, SCTAB nScTab ) const;
+
+ /** Returns the defined name specified by its Excel index.
+ @param nXclNameIdx The index of the internal defined name.
+ @return Pointer to the defined name or 0 on error. */
+ const XclImpName* GetName( sal_uInt16 nXclNameIdx ) const;
+
+ void ConvertAllTokens();
+
+private:
+ typedef std::vector< std::unique_ptr<XclImpName> > XclImpNameList;
+ XclImpNameList maNameList;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xipage.hxx b/sc/source/filter/inc/xipage.hxx
new file mode 100644
index 000000000..9aad4a506
--- /dev/null
+++ b/sc/source/filter/inc/xipage.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlpage.hxx"
+#include "xiroot.hxx"
+
+// Page settings ==============================================================
+
+/** Contains all page (print) settings for a single sheet.
+ @descr Supports reading all related records and creating a page style sheet. */
+class XclImpPageSettings : protected XclImpRoot
+{
+public:
+ explicit XclImpPageSettings( const XclImpRoot& rRoot );
+
+ /** Returns read-only access to the page data. */
+ const XclPageData& GetPageData() const { return maData; }
+
+ /** Initializes the object to be used for a new sheet. */
+ void Initialize();
+
+ /** Reads a SETUP record and inserts contained data. */
+ void ReadSetup( XclImpStream& rStrm );
+ /** Reads a ***MARGIN record (reads all 4 margin records). */
+ void ReadMargin( XclImpStream& rStrm );
+ /** Reads a HCENTER or VCENTER record. */
+ void ReadCenter( XclImpStream& rStrm );
+ /** Reads a HEADER or FOOTER record. */
+ void ReadHeaderFooter( XclImpStream& rStrm );
+ /** Reads a HORIZONTALPAGEBREAKS or VERTICALPAGEBREAKS record. */
+ void ReadPageBreaks( XclImpStream& rStrm );
+ /** Reads a PRINTHEADERS record. */
+ void ReadPrintHeaders( XclImpStream& rStrm );
+ /** Reads a PRINTGRIDLINES record. */
+ void ReadPrintGridLines( XclImpStream& rStrm );
+ /** Reads an IMGDATA record and creates the SvxBrushItem. */
+ void ReadImgData( XclImpStream& rStrm );
+
+ /** Overrides paper size and orientation (used in sheet-charts). */
+ void SetPaperSize( sal_uInt16 nXclPaperSize, bool bPortrait );
+ /** Sets or clears the fit-to-pages setting (contained in WSBOOL record). */
+ void SetFitToPages( bool bFitToPages ) { maData.mbFitToPages = bFitToPages; }
+
+ /** Creates a page stylesheet from current settings and sets it at current sheet. */
+ void Finalize();
+
+private:
+ XclPageData maData; /// Page settings data.
+ bool mbValidPaper; /// true = Paper size and orientation valid.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xipivot.hxx b/sc/source/filter/inc/xipivot.hxx
new file mode 100644
index 000000000..611de505e
--- /dev/null
+++ b/sc/source/filter/inc/xipivot.hxx
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlpivot.hxx"
+#include "xiroot.hxx"
+#include <vector>
+#include <memory>
+
+class ScDPSaveData;
+class ScDPSaveDimension;
+
+// Pivot cache
+
+/** Represents a data item in a pivot cache. */
+class XclImpPCItem : public XclPCItem
+{
+public:
+ explicit XclImpPCItem( XclImpStream& rStrm );
+
+ /** Inserts the item data into the passed document. */
+ void WriteToSource( XclImpRoot& rRoot, const ScAddress& rScPos ) const;
+
+private:
+ /** Reads an SXDOUBLE record describing a floating-point item. */
+ void ReadSxdouble( XclImpStream& rStrm );
+ /** Reads an SXBOOLEAN record describing a boolean item. */
+ void ReadSxboolean( XclImpStream& rStrm );
+ /** Reads an SXERROR record describing an error code item. */
+ void ReadSxerror( XclImpStream& rStrm );
+ /** Reads an SXINTEGER record describing an integer item. */
+ void ReadSxinteger( XclImpStream& rStrm );
+ /** Reads an SXSTRING record describing a text item. */
+ void ReadSxstring( XclImpStream& rStrm );
+ /** Reads an SXDATETIME record describing a date/time item. */
+ void ReadSxdatetime( XclImpStream& rStrm );
+ /** Reads an SXEMPTY record describing an empty item. */
+ void ReadSxempty( XclImpStream& rStrm );
+};
+
+typedef std::shared_ptr< XclImpPCItem > XclImpPCItemRef;
+
+struct ScDPNumGroupInfo;
+class XclImpPivotCache;
+
+/** Represents a field in a pivot cache (a column of data items in the source area). */
+class XclImpPCField : public XclPCField, protected XclImpRoot
+{
+public:
+ /** Creates a pivot cache field by reading an SXFIELD record. */
+ explicit XclImpPCField( const XclImpRoot& rRoot,
+ XclImpPivotCache& rPCache, sal_uInt16 nFieldIdx );
+ virtual ~XclImpPCField() override;
+
+ // general field/item access ----------------------------------------------
+
+ /** Returns the name of the field, uses the passed visible name if supported. */
+ const OUString& GetFieldName( const ScfStringVec& rVisNames ) const;
+
+ /** Returns the base field if this is a grouping field. */
+ const XclImpPCField* GetGroupBaseField() const;
+
+ /** Returns the item at the specified position or 0 on error. */
+ const XclImpPCItem* GetItem( sal_uInt16 nItemIdx ) const;
+ /** Returns the item representing a limit value in numeric/date/time grouping fields.
+ @param nItemIdx One of EXC_SXFIELD_INDEX_MIN, EXC_SXFIELD_INDEX_MAX, or EXC_SXFIELD_INDEX_STEP. */
+ const XclImpPCItem* GetLimitItem( sal_uInt16 nItemIdx ) const;
+
+ /** Inserts the field name into the document. */
+ void WriteFieldNameToSource( SCCOL nScCol, SCTAB nScTab );
+ /** Inserts the specified item data into the document. */
+ void WriteOrigItemToSource( SCROW nScRow, SCTAB nScTab, sal_uInt16 nItemIdx );
+ /** Inserts the data of the last inserted item into the document. */
+ void WriteLastOrigItemToSource( SCROW nScRow, SCTAB nScTab );
+
+ // records ----------------------------------------------------------------
+
+ /** Reads the SXFIELD record describing the field. */
+ void ReadSxfield( XclImpStream& rStrm );
+ /** Reads an item data record describing a new item. */
+ void ReadItem( XclImpStream& rStrm );
+ /** Reads the SXNUMGROUP record describing numeric grouping fields. */
+ void ReadSxnumgroup( XclImpStream& rStrm );
+ /** Reads the SXGROUPINFO record describing the item order in grouping fields. */
+ void ReadSxgroupinfo( XclImpStream& rStrm );
+
+ // grouping ---------------------------------------------------------------
+
+ /** Inserts grouping information of this field into the passed ScDPSaveData. */
+ void ConvertGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const;
+
+private:
+ /** Inserts standard grouping information of this field into the passed ScDPSaveData. */
+ void ConvertStdGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const;
+ /** Inserts numeric grouping information of this field into the passed ScDPSaveData. */
+ void ConvertNumGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const;
+ /** Inserts date grouping information of this field into the passed ScDPSaveData. */
+ void ConvertDateGroupField( ScDPSaveData& rSaveData, const ScfStringVec& rVisNames ) const;
+
+ /** Returns a Calc struct with numeric grouping data. */
+ ScDPNumGroupInfo GetScNumGroupInfo() const;
+ /** Returns a Calc struct with date grouping data. */
+ ScDPNumGroupInfo GetScDateGroupInfo() const;
+
+ /** Returns a limit value for numeric grouping fields. */
+ const double* GetNumGroupLimit( sal_uInt16 nLimitIdx ) const;
+ /** Returns a limit value for date grouping fields (minimum/maximum only). */
+ const DateTime* GetDateGroupLimit( sal_uInt16 nLimitIdx ) const;
+ /** Returns the step value for date grouping fields. */
+ const sal_Int16* GetDateGroupStep() const;
+
+private:
+ typedef ::std::vector< XclImpPCItemRef > XclImpPCItemVec;
+
+ XclImpPivotCache& mrPCache; /// Parent pivot cache containing this field.
+ XclImpPCItemVec maItems; /// List of all displayed data items.
+ XclImpPCItemVec maOrigItems; /// List of all source data items.
+ XclImpPCItemVec maNumGroupItems; /// List of items containing numeric grouping limits.
+ mutable SCCOL mnSourceScCol; /// Column index of source data for this field.
+ bool mbNumGroupInfoRead; /// true = Numeric grouping info read (SXNUMGROUP record).
+};
+
+typedef std::shared_ptr< XclImpPCField > XclImpPCFieldRef;
+
+class XclImpPivotCache : protected XclImpRoot
+{
+public:
+ explicit XclImpPivotCache( const XclImpRoot& rRoot );
+ virtual ~XclImpPivotCache() override;
+
+ // data access ------------------------------------------------------------
+
+ /** Returns the data source range read from the DCONREF record. */
+ const ScRange& GetSourceRange() const { return maSrcRange; }
+
+ const OUString& GetSourceRangeName() const { return maSrcRangeName; }
+
+ /** Returns read-only access to a pivot cache field. */
+ const XclImpPCField* GetField( sal_uInt16 nFieldIdx ) const;
+
+ // records ----------------------------------------------------------------
+
+ /** Reads an SXIDSTM record containing a pivot cache stream identifier and the pivot cache. */
+ void ReadSxidstm( XclImpStream& rStrm );
+ /** Reads an SXVS record containing the source type of the pivot cache. */
+ void ReadSxvs( XclImpStream& rStrm );
+ /** Reads a DCONREF record containing the source range of the pivot cache. */
+ void ReadDconref( XclImpStream& rStrm );
+ /**
+ * Read DECONNAME record which contains the defined name of the source
+ * range.
+ */
+ void ReadDConName( XclImpStream& rStrm );
+ /** Reads the entire pivot cache stream. Uses decrypter from passed stream. */
+ void ReadPivotCacheStream( const XclImpStream& rStrm );
+
+ bool IsRefreshOnLoad() const;
+ bool IsValid() const;
+
+private:
+ typedef ::std::vector< XclImpPCFieldRef > XclImpPCFieldVec;
+
+ XclPCInfo maPCInfo; /// Pivot cache settings (SXDB record).
+ XclImpPCFieldVec maFields; /// List of pivot cache fields.
+ ScRange maSrcRange; /// Source range in the spreadsheet.
+ OUString maUrl; /// URL of the source data.
+ OUString maTabName; /// Sheet name of the source data.
+ OUString maSrcRangeName; /// Name of the source data range.
+ sal_uInt16 mnStrmId; /// Pivot cache stream identifier.
+ sal_uInt16 mnSrcType; /// Source data type.
+ bool mbSelfRef; /// true = Source data from own document.
+};
+
+typedef std::shared_ptr< XclImpPivotCache > XclImpPivotCacheRef;
+
+// Pivot table
+
+class XclImpPivotTable;
+
+class XclImpPTItem
+{
+public:
+ explicit XclImpPTItem( const XclImpPCField* pCacheField );
+
+ /** Returns the internal name of the item or 0, if no name could be found. */
+ const OUString* GetItemName() const;
+ /** Returns the internal name of the item. */
+ std::pair<bool, OUString> GetItemName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot) const;
+
+ /** Reads an SXVI record containing data of this item. */
+ void ReadSxvi( XclImpStream& rStrm );
+
+ /** Inserts this item into the passed ScDPSaveDimension. */
+ void ConvertItem( ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const XclImpRoot& rRoot ) const;
+
+private:
+ XclPTItemInfo maItemInfo; /// General data for this item.
+ const XclImpPCField* mpCacheField; /// Corresponding pivot cache field.
+};
+
+typedef std::shared_ptr< XclImpPTItem > XclImpPTItemRef;
+
+class XclImpPTField
+{
+public:
+ explicit XclImpPTField( const XclImpPivotTable& rPTable, sal_uInt16 nCacheIdx );
+
+ // general field/item access ----------------------------------------------
+
+ /** Returns the corresponding pivot cache field of this field. */
+ const XclImpPCField* GetCacheField() const;
+ /** Returns the name of this field that is used to create the Calc dimensions. */
+ OUString GetFieldName() const;
+ /** Returns the internally set visible name of this field. */
+ OUString GetVisFieldName() const;
+
+ /** Returns the specified item. */
+ const XclImpPTItem* GetItem( sal_uInt16 nItemIdx ) const;
+ /** Returns the internal name of the specified item. */
+ const OUString* GetItemName( sal_uInt16 nItemIdx ) const;
+
+ /** Returns the flags of the axes this field is part of. */
+ sal_uInt16 GetAxes() const { return maFieldInfo.mnAxes; }
+ /** Sets the flags of the axes this field is part of. */
+ void SetAxes( sal_uInt16 nAxes ) { maFieldInfo.mnAxes = nAxes; }
+
+ // records ----------------------------------------------------------------
+
+ /** Reads an SXVD record describing the field. */
+ void ReadSxvd( XclImpStream& rStrm );
+ /** Reads an SXVDEX record describing extended options of the field. */
+ void ReadSxvdex( XclImpStream& rStrm );
+ /** Reads an SXVI record describing a new item of this field. */
+ void ReadSxvi( XclImpStream& rStrm );
+
+ // row/column fields ------------------------------------------------------
+
+ void ConvertRowColField( ScDPSaveData& rSaveData ) const;
+
+ // page fields ------------------------------------------------------------
+
+ void SetPageFieldInfo( const XclPTPageFieldInfo& rPageInfo );
+ void ConvertPageField( ScDPSaveData& rSaveData ) const;
+
+ // hidden fields ----------------------------------------------------------
+
+ void ConvertHiddenField( ScDPSaveData& rSaveData ) const;
+
+ // data fields ------------------------------------------------------------
+
+ bool HasDataFieldInfo() const;
+ void AddDataFieldInfo( const XclPTDataFieldInfo& rDataInfo );
+ void ConvertDataField( ScDPSaveData& rSaveData ) const;
+
+ void ConvertFieldInfo( const ScDPSaveData& rSaveData, ScDPObject* pObj, const XclImpRoot& rRoot, bool bPageField = false ) const;
+
+private:
+ void ConvertRCPField( ScDPSaveData& rSaveData ) const;
+
+ void ConvertDataField( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const;
+ void ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const XclPTDataFieldInfo& rDataInfo ) const;
+
+private:
+
+ const XclImpPivotTable& mrPTable; /// Parent pivot table containing this field.
+ XclPTFieldInfo maFieldInfo; /// General field info (SXVD record).
+ XclPTFieldExtInfo maFieldExtInfo; /// Extended field info (SXVDEX record).
+ XclPTPageFieldInfo maPageInfo; /// Page field info (entry from SXPI record).
+ std::vector< XclPTDataFieldInfo > maDataInfoVector; /// Vector of extended data field info (SXDI records).
+ std::vector< XclImpPTItemRef > maItems; /// List of all items of this field.
+};
+
+typedef std::shared_ptr< XclImpPTField > XclImpPTFieldRef;
+
+class XclImpPivotTable : protected XclImpRoot
+{
+public:
+ explicit XclImpPivotTable( const XclImpRoot& rRoot );
+ virtual ~XclImpPivotTable() override;
+
+ // cache/field access, misc. ----------------------------------------------
+
+ const XclImpPivotCacheRef& GetPivotCache() const { return mxPCache; }
+ const ScfStringVec& GetVisFieldNames() const { return maVisFieldNames; }
+
+ sal_uInt16 GetFieldCount() const;
+ const XclImpPTField* GetField( sal_uInt16 nFieldIdx ) const;
+ XclImpPTField* GetFieldAcc( sal_uInt16 nFieldIdx );
+
+ const XclImpPTField* GetDataField( sal_uInt16 nDataFieldIdx ) const;
+ OUString GetDataFieldName( sal_uInt16 nDataFieldIdx ) const;
+
+ // records ----------------------------------------------------------------
+
+ /** Reads an SXVIEW record starting a new pivot table. */
+ void ReadSxview( XclImpStream& rStrm );
+ /** Reads an SXVD record describing a new field. */
+ void ReadSxvd( XclImpStream& rStrm );
+ /** Reads an SXVI record describing a new item of the current field. */
+ void ReadSxvi( XclImpStream& rStrm );
+ /** Reads an SXVDEX record describing extended options of the current field. */
+ void ReadSxvdex( XclImpStream& rStrm );
+ /** Reads an SXIVD record containing the row field or column field order. */
+ void ReadSxivd( XclImpStream& rStrm );
+ /** Reads an SXPI record containing page field data. */
+ void ReadSxpi( XclImpStream& rStrm );
+ /** Reads an SXDI record containing data field data. */
+ void ReadSxdi( XclImpStream& rStrm );
+ /** Reads an SXEX record containing additional settings for the pivot table. */
+ void ReadSxex( XclImpStream& rStrm );
+ /** Reads an SXVIEWEX9 record that specifies the pivot tables
+ * autoformat. */
+ void ReadSxViewEx9( XclImpStream& rStrm );
+
+ /** Reads an SXADDL record that specifies additional info for pivot table. */
+ void ReadSxAddl( XclImpStream& rStrm );
+
+ /** Inserts the pivot table into the Calc document. */
+ void Convert();
+
+ void MaybeRefresh();
+
+ void ApplyMergeFlags(const ScRange& rOutRange, const ScDPSaveData& rSaveData);
+ void ApplyFieldInfo();
+
+private:
+ XclImpPivotCacheRef mxPCache; /// Pivot cache containing field/item names.
+
+ XclPTInfo maPTInfo; /// General info about the pivot table (SXVIEW record).
+ XclPTExtInfo maPTExtInfo; /// Extended info about the pivot table (SXEX record).
+ XclPTViewEx9Info maPTViewEx9Info; /// (SXVIEWEX9 record)
+ XclPTAddl maPTAddlInfo;
+ std::vector< XclImpPTFieldRef >
+ maFields; /// Vector containing all fields.
+ XclImpPTFieldRef mxCurrField; /// Current field for importing additional info.
+ ScfStringVec maVisFieldNames; /// Vector containing all visible field names.
+ ScfUInt16Vec maRowFields; /// Row field indexes.
+ ScfUInt16Vec maColFields; /// Column field indexes.
+ ScfUInt16Vec maPageFields; /// Page field indexes.
+ ScfUInt16Vec maOrigDataFields; /// Original data field indexes.
+ ScfUInt16Vec maFiltDataFields; /// Filtered data field indexes.
+ XclImpPTField maDataOrientField; /// Special data field orientation field.
+ ScRange maOutScRange; /// Output range in the Calc document.
+ ScDPObject* mpDPObj;
+};
+
+typedef std::shared_ptr< XclImpPivotTable > XclImpPivotTableRef;
+
+/** The main class for pivot table import.
+
+ This class contains functions to read all records related to pivot tables
+ and pivot caches.
+ */
+class XclImpPivotTableManager : protected XclImpRoot
+{
+public:
+ explicit XclImpPivotTableManager( const XclImpRoot& rRoot );
+ virtual ~XclImpPivotTableManager() override;
+
+ // pivot cache records ----------------------------------------------------
+
+ /** Returns the pivot cache with the specified 0-based index. */
+ XclImpPivotCacheRef GetPivotCache( sal_uInt16 nCacheIdx );
+
+ /** Reads an SXIDSTM record containing a pivot cache stream identifier and the pivot cache. */
+ void ReadSxidstm( XclImpStream& rStrm );
+ /** Reads an SXVS record containing the source type of a pivot cache. */
+ void ReadSxvs( XclImpStream& rStrm );
+ /** Reads a DCONREF record containing the source range of a pivot cache. */
+ void ReadDconref( XclImpStream& rStrm );
+ void ReadDConName( XclImpStream& rStrm );
+
+ // pivot table records ----------------------------------------------------
+
+ /** Reads an SXVIEW record describing a new pivot table. */
+ void ReadSxview( XclImpStream& rStrm );
+ /** Reads an SXVD record describing a new field. */
+ void ReadSxvd( XclImpStream& rStrm );
+ /** Reads an SXVDEX record describing extended options of a field. */
+ void ReadSxvdex( XclImpStream& rStrm );
+ /** Reads an SXIVD record containing the row field or column field order. */
+ void ReadSxivd( XclImpStream& rStrm );
+ /** Reads an SXPI record containing page field data. */
+ void ReadSxpi( XclImpStream& rStrm );
+ /** Reads an SXDI record containing data field data. */
+ void ReadSxdi( XclImpStream& rStrm );
+ /** Reads an SXVI record describing a new item of the current field. */
+ void ReadSxvi( XclImpStream& rStrm );
+ /** Reads an SXEX record containing additional settings for a pivot table. */
+ void ReadSxex( XclImpStream& rStrm );
+ /** Reads an SXVIEWEX9 record that specifies the pivot tables
+ * autoformat. */
+ void ReadSxViewEx9( XclImpStream& rStrm );
+ /** Reads an SXADDL record that specifies additional info for pivot table. */
+ void ReadSxAddl( XclImpStream& rStrm );
+
+ /** Reads all used pivot caches and creates additional sheets for external data sources. */
+ void ReadPivotCaches( const XclImpStream& rStrm );
+ /** Inserts all pivot tables into the Calc document. */
+ void ConvertPivotTables();
+
+ void MaybeRefreshPivotTables();
+
+private:
+
+ std::vector< XclImpPivotCacheRef > maPCaches; /// List of all pivot caches.
+ std::vector< XclImpPivotTableRef > maPTables; /// List of all pivot tables.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiroot.hxx b/sc/source/filter/inc/xiroot.hxx
new file mode 100644
index 000000000..d290a01db
--- /dev/null
+++ b/sc/source/filter/inc/xiroot.hxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlroot.hxx"
+#include <memory>
+
+// Forward declarations of objects in public use ==============================
+
+class XclImpStream;
+class XclImpString;
+
+typedef std::shared_ptr< XclImpString > XclImpStringRef;
+
+// Global data ================================================================
+
+class XclImpAddressConverter;
+class XclImpFormulaCompiler;
+class XclImpSst;
+class XclImpPalette;
+class XclImpFontBuffer;
+class XclImpNumFmtBuffer;
+class XclImpXFBuffer;
+class XclImpXFRangeBuffer;
+class XclImpTabInfo;
+class XclImpNameManager;
+class XclImpLinkManager;
+class XclImpObjectManager;
+class XclImpSheetDrawing;
+class XclImpCondFormatManager;
+class XclImpValidationManager;
+class XclImpAutoFilterBuffer;
+class XclImpWebQueryBuffer;
+class XclImpPivotTableManager;
+class XclImpPageSettings;
+class XclImpDocViewSettings;
+class XclImpTabViewSettings;
+class XclImpSheetProtectBuffer;
+class XclImpDocProtectBuffer;
+
+class ScRangeListTabs;
+class ExcelToSc;
+class ScDocumentImport;
+
+/** Stores global buffers and data needed for Excel import filter. */
+struct XclImpRootData : public XclRootData
+{
+ typedef std::shared_ptr< XclImpAddressConverter > XclImpAddrConvRef;
+ typedef std::shared_ptr< XclImpFormulaCompiler > XclImpFmlaCompRef;
+
+ typedef std::shared_ptr< XclImpSst > XclImpSstRef;
+ typedef std::shared_ptr< XclImpPalette > XclImpPaletteRef;
+ typedef std::shared_ptr< XclImpFontBuffer > XclImpFontBfrRef;
+ typedef std::shared_ptr< XclImpNumFmtBuffer > XclImpNumFmtBfrRef;
+ typedef std::shared_ptr< XclImpXFBuffer > XclImpXFBfrRef;
+ typedef std::shared_ptr< XclImpXFRangeBuffer > XclImpXFRangeBfrRef;
+ typedef std::shared_ptr< XclImpTabInfo > XclImpTabInfoRef;
+ typedef std::shared_ptr< XclImpNameManager > XclImpNameMgrRef;
+ typedef std::shared_ptr< XclImpLinkManager > XclImpLinkMgrRef;
+ typedef std::shared_ptr< XclImpObjectManager > XclImpObjectMgrRef;
+ typedef std::shared_ptr< XclImpCondFormatManager > XclImpCondFmtMgrRef;
+ typedef std::shared_ptr< XclImpValidationManager > XclImpValidationMgrRef;
+ typedef std::shared_ptr< XclImpWebQueryBuffer > XclImpWebQueryBfrRef;
+ typedef std::shared_ptr< XclImpPivotTableManager > XclImpPTableMgrRef;
+ typedef std::shared_ptr< XclImpPageSettings > XclImpPageSettRef;
+ typedef std::shared_ptr< XclImpDocViewSettings > XclImpDocViewSettRef;
+ typedef std::shared_ptr< XclImpTabViewSettings > XclImpTabViewSettRef;
+ typedef std::shared_ptr< XclImpSheetProtectBuffer > XclImpTabProtectRef;
+ typedef std::shared_ptr< XclImpDocProtectBuffer > XclImpDocProtectRef;
+
+ XclImpAddrConvRef mxAddrConv; /// The address converter.
+ XclImpFmlaCompRef mxFmlaComp; /// The formula compiler.
+
+ XclImpSstRef mxSst; /// The shared string table.
+ XclImpPaletteRef mxPalette; /// The color buffer.
+ XclImpFontBfrRef mxFontBfr; /// All fonts in the file.
+ XclImpNumFmtBfrRef mxNumFmtBfr; /// All number formats in the file.
+ XclImpXFBfrRef mpXFBfr; /// All XF record data in the file.
+ XclImpXFRangeBfrRef mxXFRangeBfr; /// Buffer of XF index ranges in a sheet.
+
+ XclImpTabInfoRef mxTabInfo; /// Sheet creation order list.
+ XclImpNameMgrRef mxNameMgr; /// Internal defined names.
+ XclImpLinkMgrRef mxLinkMgr; /// Manager for internal/external links.
+
+ XclImpObjectMgrRef mxObjMgr; /// All drawing objects.
+ XclImpCondFmtMgrRef mxCondFmtMgr; /// Conditional formatting.
+ XclImpValidationMgrRef mxValidMgr; /// Data validation
+ XclImpWebQueryBfrRef mxWebQueryBfr; /// All web queries.
+ XclImpPTableMgrRef mxPTableMgr; /// All pivot tables and pivot caches.
+
+ XclImpPageSettRef mxPageSett; /// Page settings for current sheet.
+ XclImpDocViewSettRef mxDocViewSett; /// View settings for entire document.
+ XclImpTabViewSettRef mxTabViewSett; /// View settings for current sheet.
+ XclImpTabProtectRef mxTabProtect; /// Sheet protection options for current sheet.
+ XclImpDocProtectRef mxDocProtect; /// Document protection options.
+
+ std::shared_ptr<ScDocumentImport> mxDocImport;
+
+ std::unique_ptr<ScRangeListTabs> mpPrintRanges;
+ std::unique_ptr<ScRangeListTabs> mpPrintTitles;
+
+ bool mbHasCodePage; /// true = CODEPAGE record exists.
+ bool mbHasBasic; /// true = document contains VB project.
+
+ explicit XclImpRootData( XclBiff eBiff, SfxMedium& rMedium,
+ const tools::SvRef<SotStorage>& xRootStrg, ScDocument& rDoc, rtl_TextEncoding eTextEnc );
+ virtual ~XclImpRootData() override;
+};
+
+/** Access to global data from other classes. */
+class XclImpRoot : public XclRoot
+{
+public:
+ explicit XclImpRoot( XclImpRootData& rImpRootData );
+
+ /** Returns this root instance - for code readability in derived classes. */
+ const XclImpRoot& GetRoot() const { return *this; }
+ XclImpRoot& GetRoot() { return *this; }
+
+ /** Sets a code page read from a CODEPAGE record for byte string import. */
+ void SetCodePage( sal_uInt16 nCodePage );
+
+ /** Is called when import filter starts importing a single sheet (all BIFF versions). */
+ void InitializeTable( SCTAB nScTab );
+ /** Is called when import filter stops importing a single sheet (all BIFF versions). */
+ void FinalizeTable();
+
+ /** Returns the address converter. */
+ XclImpAddressConverter& GetAddressConverter() const;
+ /** Returns the formula converter. */
+ XclImpFormulaCompiler& GetFormulaCompiler() const;
+ /** Returns the old formula converter. */
+ ExcelToSc& GetOldFmlaConverter() const;
+
+ /** Returns the shared string table. */
+ XclImpSst& GetSst() const;
+ /** Returns the color buffer. */
+ XclImpPalette& GetPalette() const;
+ /** Returns the font buffer. */
+ XclImpFontBuffer& GetFontBuffer() const;
+ /** Returns the number format buffer. */
+ XclImpNumFmtBuffer& GetNumFmtBuffer() const;
+ /** Returns the cell formatting attributes buffer. */
+ XclImpXFBuffer& GetXFBuffer() const;
+ /** Returns the buffer of XF index ranges for a sheet. */
+ XclImpXFRangeBuffer& GetXFRangeBuffer() const;
+
+ /** Returns the buffer that contains all print areas in the document. */
+ ScRangeListTabs& GetPrintAreaBuffer() const;
+ /** Returns the buffer that contains all print titles in the document. */
+ ScRangeListTabs& GetTitleAreaBuffer() const;
+
+ /** Returns the buffer that contains the sheet creation order. */
+ XclImpTabInfo& GetTabInfo() const;
+ /** Returns the buffer that contains internal defined names. */
+ XclImpNameManager& GetNameManager() const;
+ /** Returns the link manager. */
+ XclImpLinkManager& GetLinkManager() const;
+
+ /** Returns the drawing object manager. */
+ XclImpObjectManager& GetObjectManager() const;
+ /** Returns the drawing container of the current sheet. */
+ XclImpSheetDrawing& GetCurrSheetDrawing() const;
+ /** Returns the conditional formatting manager. */
+ XclImpCondFormatManager& GetCondFormatManager() const;
+
+ XclImpValidationManager& GetValidationManager() const;
+ /** Returns the filter manager. */
+ XclImpAutoFilterBuffer& GetFilterManager() const;
+ /** Returns the web query buffer. */
+ XclImpWebQueryBuffer& GetWebQueryBuffer() const;
+ /** Returns the pivot table manager. */
+ XclImpPivotTableManager& GetPivotTableManager() const;
+ /** Returns the sheet protection options of the current sheet. */
+ XclImpSheetProtectBuffer& GetSheetProtectBuffer() const;
+ /** Returns the document protection options. */
+ XclImpDocProtectBuffer& GetDocProtectBuffer() const;
+
+ /** Returns the page settings of the current sheet. */
+ XclImpPageSettings& GetPageSettings() const;
+ /** Returns the view settings of the entire document. */
+ XclImpDocViewSettings& GetDocViewSettings() const;
+ /** Returns the view settings of the current sheet. */
+ XclImpTabViewSettings& GetTabViewSettings() const;
+
+ /** Returns the Calc add-in function name for an Excel function name. */
+ static OUString GetScAddInName( const OUString& rXclName );
+
+ /** Returns true, if the document contains a VB project. */
+ bool HasBasic() const { return mrImpData.mbHasBasic; }
+ /** Called to indicate that the document contains a VB project. */
+ void SetHasBasic() { mrImpData.mbHasBasic = true; }
+ /** Reads the CODENAME record and inserts the codename into the document. */
+ void ReadCodeName( XclImpStream& rStrm, bool bGlobals );
+
+ ScDocumentImport& GetDocImport();
+
+private:
+ XclImpRootData& mrImpData; /// Reference to the global import data struct.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xistream.hxx b/sc/source/filter/inc/xistream.hxx
new file mode 100644
index 000000000..df440f3b9
--- /dev/null
+++ b/sc/source/filter/inc/xistream.hxx
@@ -0,0 +1,552 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <comphelper/docpasswordhelper.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <filter/msfilter/mscodec.hxx>
+#include <tools/stream.hxx>
+#include <memory>
+#include "xlstream.hxx"
+#include "xlconst.hxx"
+
+class XclImpRoot;
+
+/* ============================================================================
+Input stream class for Excel import
+- CONTINUE record handling
+- Decryption
+============================================================================ */
+
+// Decryption
+
+class XclImpDecrypter;
+typedef std::shared_ptr< XclImpDecrypter > XclImpDecrypterRef;
+
+/** Base class for BIFF stream decryption. */
+class XclImpDecrypter : public ::comphelper::IDocPasswordVerifier
+{
+public:
+ explicit XclImpDecrypter();
+ virtual ~XclImpDecrypter() override;
+
+ /** Returns the current error code of the decrypter. */
+ const ErrCode& GetError() const { return mnError; }
+ /** Returns true, if the decoder has been initialized correctly. */
+ bool IsValid() const { return mnError == ERRCODE_NONE; }
+
+ /** Creates a (ref-counted) copy of this decrypter object. */
+ XclImpDecrypterRef Clone() const;
+
+ /** Implementation of the ::comphelper::IDocPasswordVerifier interface */
+ virtual ::comphelper::DocPasswordVerifierResult verifyPassword( const OUString& rPassword, css::uno::Sequence< css::beans::NamedValue >& o_rEncryptionData ) override;
+ virtual ::comphelper::DocPasswordVerifierResult verifyEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& rEncryptionData ) override;
+
+ /** Updates the decrypter on start of a new record or after seeking stream. */
+ void Update( const SvStream& rStrm, sal_uInt16 nRecSize );
+ /** Reads and decrypts nBytes bytes and stores data into the existing(!) buffer pData.
+ @return Count of bytes really read. */
+ sal_uInt16 Read( SvStream& rStrm, void* pData, sal_uInt16 nBytes );
+
+protected:
+ /** Protected copy c'tor for OnClone(). */
+ explicit XclImpDecrypter( const XclImpDecrypter& rSrc );
+
+private:
+ /** Implementation of cloning this object. */
+ virtual XclImpDecrypter* OnClone() const = 0;
+ /** Derived classes implement password verification and initialization of
+ the decoder. */
+ virtual css::uno::Sequence< css::beans::NamedValue >
+ OnVerifyPassword( const OUString& rPassword ) = 0;
+ virtual bool OnVerifyEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& rEncryptionData ) = 0;
+
+ /** Implementation of updating the decrypter. */
+ virtual void OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 nRecSize ) = 0;
+ /** Implementation of the decryption. */
+ virtual sal_uInt16 OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes ) = 0;
+
+private:
+ ErrCode mnError; /// Decrypter error code.
+ sal_uInt64 mnOldPos; /// Last known stream position.
+ sal_uInt16 mnRecSize; /// Current record size.
+};
+
+/** Decrypts BIFF5 stream contents. */
+class XclImpBiff5Decrypter : public XclImpDecrypter
+{
+public:
+ explicit XclImpBiff5Decrypter( sal_uInt16 nKey, sal_uInt16 nHash );
+
+private:
+ /** Private copy c'tor for OnClone(). */
+ explicit XclImpBiff5Decrypter( const XclImpBiff5Decrypter& rSrc );
+
+ /** Implementation of cloning this object. */
+ virtual XclImpBiff5Decrypter* OnClone() const override;
+ /** Implements password verification and initialization of the decoder. */
+ virtual css::uno::Sequence< css::beans::NamedValue >
+ OnVerifyPassword( const OUString& rPassword ) override;
+ virtual bool OnVerifyEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& rEncryptionData ) override;
+ /** Implementation of updating the decrypter. */
+ virtual void OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 nRecSize ) override;
+ /** Implementation of the decryption. */
+ virtual sal_uInt16 OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes ) override;
+
+private:
+ ::msfilter::MSCodec_XorXLS95 maCodec; /// Crypto algorithm implementation.
+ css::uno::Sequence< css::beans::NamedValue > maEncryptionData;
+ sal_uInt16 mnKey;
+ sal_uInt16 mnHash;
+};
+
+/** Decrypts BIFF8 stream contents using the given document identifier. */
+class XclImpBiff8Decrypter : public XclImpDecrypter
+{
+private:
+ /** Implements password verification and initialization of the decoder. */
+ virtual css::uno::Sequence< css::beans::NamedValue >
+ OnVerifyPassword( const OUString& rPassword ) override;
+ virtual bool OnVerifyEncryptionData( const css::uno::Sequence< css::beans::NamedValue >& rEncryptionData ) override;
+ /** Implementation of updating the decrypter. */
+ virtual void OnUpdate( std::size_t nOldStrmPos, std::size_t nNewStrmPos, sal_uInt16 nRecSize ) override;
+ /** Implementation of the decryption. */
+ virtual sal_uInt16 OnRead( SvStream& rStrm, sal_uInt8* pnData, sal_uInt16 nBytes ) override;
+
+ /** Returns the block number corresponding to the passed stream position. */
+ static sal_uInt32 GetBlock( std::size_t nStrmPos );
+ /** Returns the block offset corresponding to the passed stream position. */
+ static sal_uInt16 GetOffset( std::size_t nStrmPos );
+
+protected:
+ explicit XclImpBiff8Decrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash);
+
+ explicit XclImpBiff8Decrypter(const XclImpBiff8Decrypter& rSrc);
+
+ css::uno::Sequence< css::beans::NamedValue > maEncryptionData;
+ std::vector< sal_uInt8 > maSalt;
+ std::vector< sal_uInt8 > maVerifier;
+ std::vector< sal_uInt8 > maVerifierHash;
+ msfilter::MSCodec97* mpCodec; /// Crypto algorithm implementation.
+};
+
+class XclImpBiff8StdDecrypter : public XclImpBiff8Decrypter
+{
+public:
+ explicit XclImpBiff8StdDecrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash)
+ : XclImpBiff8Decrypter(std::move(rSalt), std::move(rVerifier), std::move(rVerifierHash))
+ {
+ mpCodec = &maCodec;
+ }
+
+private:
+ /** Private copy c'tor for OnClone(). */
+ explicit XclImpBiff8StdDecrypter(const XclImpBiff8StdDecrypter& rSrc);
+
+ /** Implementation of cloning this object. */
+ virtual XclImpBiff8StdDecrypter* OnClone() const override;
+
+private:
+ ::msfilter::MSCodec_Std97 maCodec; /// Crypto algorithm implementation.
+};
+
+class XclImpBiff8CryptoAPIDecrypter : public XclImpBiff8Decrypter
+{
+public:
+ explicit XclImpBiff8CryptoAPIDecrypter( std::vector<sal_uInt8>&& rSalt,
+ std::vector<sal_uInt8>&& rVerifier,
+ std::vector<sal_uInt8>&& rVerifierHash)
+ : XclImpBiff8Decrypter(std::move(rSalt), std::move(rVerifier), std::move(rVerifierHash))
+ {
+ mpCodec = &maCodec;
+ }
+
+private:
+ /** Private copy c'tor for OnClone(). */
+ explicit XclImpBiff8CryptoAPIDecrypter(const XclImpBiff8CryptoAPIDecrypter& rSrc);
+
+ /** Implementation of cloning this object. */
+ virtual XclImpBiff8CryptoAPIDecrypter* OnClone() const override;
+
+private:
+ ::msfilter::MSCodec_CryptoAPI maCodec; /// Crypto algorithm implementation.
+};
+
+// Stream
+
+/** This class represents an Excel stream position.
+ @descr It contains the relevant data for a stream position inside of a record
+ (including CONTINUE records). */
+class XclImpStreamPos
+{
+public:
+ /** Constructs an invalid stream position data object. */
+ explicit XclImpStreamPos();
+
+ /** Sets the stream position data to the passed values. */
+ void Set( const SvStream& rStrm, std::size_t nNextPos, std::size_t nCurrSize,
+ sal_uInt16 nRawRecId, sal_uInt16 nRawRecSize, sal_uInt16 nRawRecLeft,
+ bool bValid );
+
+ /** Writes the contained stream position data to the given variables. */
+ void Get( SvStream& rStrm, std::size_t& rnNextPos, std::size_t& rnCurrSize,
+ sal_uInt16& rnRawRecId, sal_uInt16& rnRawRecSize, sal_uInt16& rnRawRecLeft,
+ bool& rbValid ) const;
+
+ /** Returns the stored stream position. */
+ std::size_t GetPos() const { return mnPos; }
+
+private:
+ std::size_t mnPos; /// Absolute position of the stream.
+ std::size_t mnNextPos; /// Absolute position of next record.
+ std::size_t mnCurrSize; /// Current calculated size of the record.
+ sal_uInt16 mnRawRecId; /// Current raw record ID (including CONTINUEs).
+ sal_uInt16 mnRawRecSize; /// Current raw record size (without following CONTINUEs).
+ sal_uInt16 mnRawRecLeft; /// Bytes left in current raw record (without following CONTINUEs).
+ bool mbValid; /// Read state: false = record overread.
+};
+
+/** This class is used to import record oriented streams.
+ @descr An instance is constructed with an SvStream. The SvStream stream is
+ reset to its start while constructing this stream.
+
+ To start reading a record call StartNextRecord(). Now it is possible to
+ read all contents of the record using operator>>() or any of the Read***()
+ functions. If some data exceeds the record size limit, the stream looks for
+ a following CONTINUE record and jumps automatically to it. It is NOT
+ allowed that an atomic data type is split into two records (i.e. 4 bytes of
+ a double in one record and the other 4 bytes in a following CONTINUE).
+
+ Trying to read over the record limits results in a stream error. The
+ IsValid() function indicates that with returning false. From now on it is
+ undefined what data the read functions will return. The error state will be
+ reset, if the record is reset (with the method ResetRecord()) or if the
+ next record is started.
+
+ To switch off the automatic lookup of CONTINUE records, use ResetRecord()
+ with false parameter. This is useful i.e. on import of Escher objects,
+ where sometimes solely CONTINUE records will occur. The automatic lookup
+ keeps switched off until the method ResetRecord() is called with parameter
+ true. All other settings done on the stream (i.e. alternative CONTINUE
+ record identifier, enabled decryption, NUL substitution character) will be
+ reset to default values, if a new record is started.
+
+ The import stream supports decrypting the stream data. The contents of a
+ record (not the record header) will be encrypted by Excel if the file has
+ been stored with password protection. The functions SetDecrypter(),
+ EnableDecryption(), and DisableDecryption() control the usage of the
+ decryption algorithms. SetDecrypter() sets a new decryption algorithm and
+ initially enables it. DisableDecryption() may be used to stop the usage of
+ the decryption temporarily (sometimes record contents are never encrypted,
+ i.e. all BOF records or the stream position in BOUNDSHEET). Decryption will
+ be re-enabled automatically, if a new record is started with the function
+ StartNextRecord().
+
+ It is possible to store several stream positions inside a record (including
+ its CONTINUE records). The positions are stored on a stack, which can be
+ controlled with the functions PushPosition(), PopPosition() and
+ RejectPosition(). The stack will be cleared whenever a new record is
+ started with the function StartNextRecord().
+
+ Additionally a single global stream position can be stored which keeps
+ valid during the whole import process (methods StoreGlobalPosition(),
+ SeekGlobalPosition() and DeleteGlobalPosition()). This is the only way to
+ jump back to a previous record (that is a real jump without return).
+*/
+class XclImpStream
+{
+public:
+ /** Detects the BIFF version of the passed workbook stream. */
+ static XclBiff DetectBiffVersion( SvStream& rStrm );
+
+ /** Constructs the Excel record import stream using a TOOLS stream object.
+ @param rInStrm The system input stream. Will be set to its start position.
+ Must exist as long as this object exists */
+ explicit XclImpStream(
+ SvStream& rInStrm,
+ const XclImpRoot& rRoot );
+
+ ~XclImpStream();
+
+ /** Returns the filter root data. */
+ const XclImpRoot& GetRoot() const { return mrRoot; }
+
+ /** Sets stream pointer to the start of the next record content.
+ @descr Ignores all CONTINUE records of the current record, if automatic
+ CONTINUE usage is switched on.
+ @return false = no record found (end of stream). */
+ bool StartNextRecord();
+ /** Sets stream pointer to the start of the record content for the record
+ at the passed absolute stream position.
+ @return false = no record found (end of stream). */
+ bool StartNextRecord( std::size_t nNextRecPos );
+ /** Sets stream pointer to begin of record content.
+ @param bContLookup Automatic CONTINUE lookup on/off. In difference
+ to other stream settings, this setting is persistent until next call of
+ this function (because it is wanted to receive the next CONTINUE
+ records separately).
+ @param nAltContId Sets an alternative record ID for content
+ continuation. This value is reset automatically when a new record is
+ started with StartNextRecord(). */
+ void ResetRecord( bool bContLookup,
+ sal_uInt16 nAltContId = EXC_ID_UNKNOWN );
+ /** Sets stream pointer before current record and invalidates stream.
+ @descr The next call to StartNextRecord() will start again the current
+ record. This can be used in situations where a loop or a function
+ leaves on a specific record, but the parent context expects to start
+ this record by itself. The stream is invalid as long as the first
+ record has not been started (it is not allowed to call any other stream
+ operation then). */
+ void RewindRecord();
+
+ /** Enables decryption of record contents for the rest of the stream. */
+ void SetDecrypter( XclImpDecrypterRef const & xDecrypter );
+ /** Sets decrypter from another stream. */
+ void CopyDecrypterFrom( const XclImpStream& rStrm );
+ /** Switches usage of current decryption algorithm on/off.
+ @descr Encryption is re-enabled automatically, if a new record is
+ started using the function StartNextRecord(). */
+ void EnableDecryption( bool bEnable = true );
+ /** Switches usage of current decryption algorithm off.
+ @descr This is a record-local setting. The function StartNextRecord()
+ always enables decryption. */
+ void DisableDecryption() { EnableDecryption( false ); }
+
+ /** Pushes current position on user position stack.
+ @descr This stack is emptied when starting a new record with
+ StartNextRecord(). The decryption state (enabled/disabled) is not
+ pushed onto the stack. */
+ void PushPosition();
+ /** Seeks to last position from user position stack.
+ @descr This position will be removed from the stack. */
+ void PopPosition();
+
+ /** Stores current position. This position keeps valid in all records. */
+ void StoreGlobalPosition();
+ /** Seeks to the stored global user position. */
+ void SeekGlobalPosition();
+
+ /** Returns record reading state: false = record overread. */
+ bool IsValid() const { return mbValid; }
+ /** Returns the current record ID. */
+ sal_uInt16 GetRecId() const { return mnRecId; }
+ /** Returns the position inside of the whole record content. */
+ std::size_t GetRecPos() const;
+ /** Returns the data size of the whole record without record headers. */
+ std::size_t GetRecSize();
+ /** Returns remaining data size of the whole record without record headers. */
+ std::size_t GetRecLeft();
+ /** Returns the record ID of the following record. */
+ sal_uInt16 GetNextRecId();
+
+ sal_uInt16 PeekRecId( std::size_t nPos );
+
+ [[nodiscard]]
+ sal_uInt8 ReaduInt8();
+ [[nodiscard]]
+ sal_Int16 ReadInt16();
+ [[nodiscard]]
+ sal_uInt16 ReaduInt16();
+ [[nodiscard]]
+ sal_Int32 ReadInt32();
+ [[nodiscard]]
+ sal_uInt32 ReaduInt32();
+ [[nodiscard]]
+ double ReadDouble();
+
+ /** Reads nBytes bytes to the existing(!) buffer pData.
+ @return Count of bytes really read. */
+ std::size_t Read( void* pData, std::size_t nBytes );
+ /** Copies nBytes bytes to rOutStrm.
+ @return Count of bytes really written. */
+ std::size_t CopyToStream( SvStream& rOutStrm, std::size_t nBytes );
+
+ /** Copies the entire record to rOutStrm. The current record position keeps unchanged. */
+ void CopyRecordToStream( SvStream& rOutStrm );
+
+ /** Seeks absolute in record content to the specified position.
+ @descr The value 0 means start of record, independent from physical stream position. */
+ void Seek( std::size_t nPos );
+ /** Seeks forward inside the current record. */
+ void Ignore( std::size_t nBytes );
+
+ // *** special string functions *** ---------------------------------------
+
+ // *** read/ignore unicode strings *** ------------------------------------
+ /* - look for CONTINUE records even if CONTINUE handling disabled
+ (only if inside of a CONTINUE record - for TXO import)
+ - no overread assertions (for Applix wrong string length export bug)
+
+ structure of an Excel unicode string:
+ (1) 2 byte character count
+ (2) 1 byte flags (16-bit-characters, rich string, far east string)
+ (3) [2 byte rich string format run count]
+ (4) [4 byte far east data size]
+ (5) character array
+ (6) [4 * (rich string format run count) byte]
+ (7) [(far east data size) byte]
+ header = (1), (2)
+ ext. header = (3), (4)
+ ext. data = (6), (7)
+ */
+
+ /** Reads ext. header, detects 8/16 bit mode, sets all ext. info.
+ @return Total size of ext. data. */
+ std::size_t ReadUniStringExtHeader(
+ bool& rb16Bit, bool& rbRich, bool& rbFareast,
+ sal_uInt16& rnFormatRuns, sal_uInt32& rnExtInf, sal_uInt8 nFlags );
+ /** Seeks to begin of character array, detects 8/16 bit mode.
+ @return Total size of ext. data. */
+ std::size_t ReadUniStringExtHeader( bool& rb16Bit, sal_uInt8 nFlags );
+
+ /** Sets a replacement character for NUL characters.
+ @descr NUL characters must be replaced, because Tools strings cannot
+ handle them. The substitution character is reset to '?' automatically,
+ if a new record is started using the function StartNextRecord().
+ @param cNulSubst The character to use for NUL replacement. It is
+ possible to specify NUL here. in this case strings are terminated when
+ the first NUL occurs during string import. */
+ void SetNulSubstChar( sal_Unicode cNulSubst = '?' ) { mcNulSubst = cNulSubst; }
+
+ /** Reads nChars characters and returns the string. */
+ OUString ReadRawUniString( sal_uInt16 nChars, bool b16Bit );
+ /** Reads ext. header, nChar characters, ext. data and returns the string. */
+ OUString ReadUniString( sal_uInt16 nChars, sal_uInt8 nFlags );
+ /** Reads 8 bit flags, ext. header, nChar characters, ext. data and returns the string. */
+ OUString ReadUniString( sal_uInt16 nChars );
+ /** Reads 16 bit character count, 8 bit flags, ext. header, character array,
+ ext. data and returns the string. */
+ OUString ReadUniString();
+
+ /** Ignores nChars characters. */
+ void IgnoreRawUniString( sal_uInt16 nChars, bool b16Bit );
+ /** Ignores ext. header, nChar characters, ext. data. */
+ void IgnoreUniString( sal_uInt16 nChars, sal_uInt8 nFlags );
+ /** Ignores 8 bit flags, ext. header, nChar characters, ext. data. */
+ void IgnoreUniString( sal_uInt16 nChars );
+
+ // *** read/ignore 8-bit-strings, store in String *** ---------------------
+
+ /** Reads nChar byte characters and returns the string. */
+ OUString ReadRawByteString( sal_uInt16 nChars );
+ /** Reads 8/16 bit string length, character array and returns the string. */
+ OUString ReadByteString( bool b16BitLen );
+
+ // *** SvStream functions *** ---------------------------------------------
+
+ /** Returns the absolute stream position. */
+ std::size_t GetSvStreamPos() const { return mrStrm.Tell(); }
+ /** Returns the stream size. */
+ std::size_t GetSvStreamSize() const { return mnStreamSize; }
+
+ /** Stores current stream position into rPos. */
+ void StorePosition( XclImpStreamPos& rPos );
+ /** Restores stream position contained in rPos. */
+ void RestorePosition( const XclImpStreamPos& rPos );
+
+ /** Set an SVSTREAM_..._ERROR. */
+ void SetSvStreamError( const ErrCode& rErrCode )
+ { mrStrm.SetError( rErrCode ); }
+
+private:
+ /** Seeks to next raw record header and reads record ID and size.
+ @descr This is a "raw" function, means that stream members are
+ inconsistent after return. Does only change mnRawRecId, mnRawRecSize,
+ and the base stream position, but no other members.
+ @return false = No record header found (end of stream). */
+ bool ReadNextRawRecHeader();
+
+ /** Initializes the decrypter to read a new record. */
+ void SetupDecrypter();
+ /** Initializes all members after base stream has been sought to new raw record. */
+ void SetupRawRecord();
+ /** Initializes all members after base stream has been sought to new record. */
+ void SetupRecord();
+
+ /** Returns true, if the passed ID is real or alternative continuation record ID. */
+ bool IsContinueId( sal_uInt16 nRecId ) const;
+
+ /** Goes to start of the next CONTINUE record.
+ @descr Stream must be located at the end of a raw record, and handling
+ of CONTINUE records must be enabled.
+ @return Copy of mbValid. */
+ bool JumpToNextContinue();
+ /** Goes to start of the next CONTINUE record while reading strings.
+ @descr Stream must be located at the end of a raw record. If reading
+ has been started in a CONTINUE record, jumps to an existing following
+ CONTINUE record, even if handling of CONTINUE records is disabled (This
+ is a special handling for TXO string data). Reads additional Unicode
+ flag byte at start of the new raw record and sets or resets rb16Bit.
+ @return Copy of mbValid. */
+ bool JumpToNextStringContinue( bool& rb16Bit );
+
+ /** Ensures that reading nBytes bytes is possible with next stream access.
+ @descr Stream must be located at the end of a raw record, and handling
+ of CONTINUE records must be enabled.
+ @return Copy of mbValid. */
+ bool EnsureRawReadSize( sal_uInt16 nBytes );
+ /** Returns the maximum size of raw data possible to read in one block. */
+ sal_uInt16 GetMaxRawReadSize( std::size_t nBytes ) const;
+
+ /** Reads and decrypts nBytes bytes to the existing(!) buffer pData.
+ @return Count of bytes really read. */
+ sal_uInt16 ReadRawData( void* pData, sal_uInt16 nBytes );
+
+private:
+ SvStream& mrStrm; /// Reference to the system input stream.
+ const XclImpRoot& mrRoot; /// Filter root data.
+
+ XclImpDecrypterRef mxDecrypter; /// Provides methods to decrypt data.
+
+ XclImpStreamPos maFirstRec; /// Start position of current record.
+ std::vector< XclImpStreamPos >
+ maPosStack; /// Stack for record positions.
+
+ XclImpStreamPos maGlobPos; /// User defined position elsewhere in stream.
+ sal_uInt16 mnGlobRecId; /// Record ID for user defined position.
+ bool mbGlobValidRec; /// Was user position a valid record?
+ bool mbHasGlobPos; /// Is user position defined?
+
+ std::size_t mnStreamSize; /// Size of system stream.
+ std::size_t mnNextRecPos; /// Start of next record header.
+ std::size_t mnCurrRecSize; /// Helper for record position.
+ std::size_t mnComplRecSize; /// Size of complete record data (with CONTINUEs).
+ bool mbHasComplRec; /// true = mnComplRecSize is valid.
+
+ sal_uInt16 mnRecId; /// Current record ID (not the CONTINUE ID).
+ sal_uInt16 mnAltContId; /// Alternative record ID for content continuation.
+
+ sal_uInt16 mnRawRecId; /// Current raw record ID (including CONTINUEs).
+ sal_uInt16 mnRawRecSize; /// Current raw record size (without following CONTINUEs).
+ sal_uInt16 mnRawRecLeft; /// Bytes left in current raw record (without following CONTINUEs).
+
+ sal_Unicode mcNulSubst; /// Replacement for NUL characters.
+
+ bool mbCont; /// Automatic CONTINUE lookup on/off.
+ bool mbUseDecr; /// Usage of decryption.
+ bool mbValidRec; /// false = No more records to read.
+ bool mbValid; /// false = Record overread.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xistring.hxx b/sc/source/filter/inc/xistring.hxx
new file mode 100644
index 000000000..cf534da3f
--- /dev/null
+++ b/sc/source/filter/inc/xistring.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include "xlstring.hxx"
+
+// Byte/Unicode strings =======================================================
+
+class XclImpStream;
+
+/** This class represents an unformatted or formatted string and provides importing from stream. */
+class XclImpString
+{
+public:
+ /** Constructs an empty string. */
+ explicit XclImpString();
+ /** Constructs an unformatted string. */
+ explicit XclImpString( const OUString& rString );
+
+ /** Reads a complete string from the passed stream. */
+ void Read( XclImpStream& rStrm, XclStrFlags nFlags = XclStrFlags::NONE );
+
+ /** Sets the passed string data. */
+ void SetText( const OUString& rText ) { maString = rText; }
+ /** Sets the passed formatting buffer. */
+ void SetFormats( XclFormatRunVec&& rFormats ) { maFormats = std::move(rFormats); }
+ /** Reads and appends the formatting information (run count and runs) from stream. */
+ void ReadFormats( XclImpStream& rStrm ) { ReadFormats( rStrm, maFormats ); }
+ /** Reads and appends formatting runs from an OBJ or TXO record. */
+ void ReadObjFormats( XclImpStream& rStrm, sal_uInt16 nFormatSize ) { ReadObjFormats( rStrm, maFormats, nFormatSize ); }
+
+ /** Returns true, if the string is empty. */
+ bool IsEmpty() const { return maString.isEmpty(); }
+ /** Returns the pure text data of the string. */
+ const OUString& GetText() const { return maString; }
+
+ /** Returns true, if the string contains formatting information. */
+ bool IsRich() const { return !maFormats.empty(); }
+ /** Returns the formatting run vector. */
+ const XclFormatRunVec& GetFormats() const { return maFormats; }
+
+ /** Insert a formatting run to the passed format buffer. */
+ static void AppendFormat( XclFormatRunVec& rFormats, sal_uInt16 nChar, sal_uInt16 nFontIdx );
+ /** Reads and appends the formatting information (run count and runs) from stream. */
+ static void ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats );
+ /** Reads and appends nRunCount formatting runs from stream. */
+ static void ReadFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nRunCount );
+ /** Reads and appends formatting runs from an OBJ or TXO record. */
+ static void ReadObjFormats( XclImpStream& rStrm, XclFormatRunVec& rFormats, sal_uInt16 nFormatSize );
+
+private:
+ OUString maString; /// The text data of the string.
+ XclFormatRunVec maFormats; /// All formatting runs.
+};
+
+// String iterator ============================================================
+
+/** Iterates over formatted string portions. */
+class XclImpStringIterator
+{
+public:
+ explicit XclImpStringIterator( const XclImpString& rString );
+
+ /** Returns true, if the iterator references a valid text portion. */
+ bool Is() const { return mnTextBeg < mrText.getLength(); }
+ /** Returns the index of the current text portion. */
+ size_t GetPortionIndex() const { return mnPortion; }
+ /** Returns the string of the current text portion. */
+ OUString GetPortionText() const;
+ /** Returns the font index of the current text portion. */
+ sal_uInt16 GetPortionFont() const;
+
+ /** Moves iterator to next text portion. */
+ XclImpStringIterator& operator++();
+
+private:
+ const OUString& mrText; /// The processed string.
+ const XclFormatRunVec& mrFormats; /// The vector of formatting runs.
+ size_t mnPortion; /// Current text portion.
+ sal_Int32 mnTextBeg; /// First character of current portion.
+ sal_Int32 mnTextEnd; /// First character of next portion.
+ size_t mnFormatsBeg; /// Formatting run index for current portion.
+ size_t mnFormatsEnd; /// Formatting run index for next portion.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xistyle.hxx b/sc/source/filter/inc/xistyle.hxx
new file mode 100644
index 000000000..1d0f0e04c
--- /dev/null
+++ b/sc/source/filter/inc/xistyle.hxx
@@ -0,0 +1,669 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/solar.h>
+#include <vector>
+#include <memory>
+#include <rangelst.hxx>
+#include "xlstyle.hxx"
+#include "xiroot.hxx"
+
+class ScPatternAttr;
+
+struct XclRange;
+struct ScAttrEntry;
+enum class SvxBoxItemLine;
+
+/* ============================================================================
+- Buffers for style records (PALETTE, FONT, FORMAT, XF)
+ and a container for XF indexes for every used cell in a sheet.
+============================================================================ */
+
+// PALETTE record - color information =========================================
+
+/** Stores the default colors for the current BIFF version and the contents of
+ a PALETTE record. */
+class XclImpPalette : public XclDefaultPalette
+{
+public:
+ explicit XclImpPalette( const XclImpRoot& rRoot );
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Returns the color for a (non-zero-based) Excel palette entry.
+ @descr First looks for a color read from file, then looks for a default color.
+ @return The color from current or default palette or COL_AUTO, if nothing else found. */
+ Color GetColor( sal_uInt16 nXclIndex ) const;
+
+ /** Reads a PALETTE record. */
+ void ReadPalette( XclImpStream& rStrm );
+
+private:
+ void ExportPalette();
+ typedef ::std::vector< Color > ColorVec;
+ ColorVec maColorTable; /// Colors read from file.
+ const XclImpRoot& mrRoot;
+};
+
+// FONT record - font information =============================================
+
+/** Stores all data of an Excel font and provides import of FONT records. */
+class XclImpFont : protected XclImpRoot
+{
+public:
+ explicit XclImpFont( const XclImpRoot& rRoot );
+
+ /** Constructs a font from font data.
+ @descr Special handling for font style (bold, italic) in font name,
+ overwrites settings in rFontData. */
+ explicit XclImpFont( const XclImpRoot& rRoot, const XclFontData& rFontData );
+
+ /** Sets all font attributes to used or unused. */
+ void SetAllUsedFlags( bool bUsed );
+ /** Sets the passed font data and all used flags to 'used'. */
+ void SetFontData( const XclFontData& rFontData, bool bHasCharSet );
+
+ /** Returns read-only access to font data. */
+ const XclFontData& GetFontData() const { return maData; }
+ /** Returns true, if the font character set is valid. */
+ bool HasCharSet() const { return mbHasCharSet; }
+ /** Returns true, if the font contains superscript or subscript. */
+ bool HasEscapement() const { return maData.mnEscapem != EXC_FONTESC_NONE; }
+ /** Returns the text encoding for strings used with this font. */
+ rtl_TextEncoding GetFontEncoding() const;
+
+ /** Returns true, if this font contains characters for Asian scripts (CJK). */
+ bool HasAsianChars() const { return mbHasAsian; }
+
+ /** Reads a FONT record for all BIFF versions. */
+ void ReadFont( XclImpStream& rStrm );
+ /** Reads an EFONT record (BIFF2 font color). */
+ void ReadEfont( XclImpStream& rStrm );
+ /** Reads the font block from a CF (conditional format) record. */
+ void ReadCFFontBlock( XclImpStream& rStrm );
+
+ /** Fills all font properties to the item set.
+ @param rItemSet The destination item set.
+ @param eType The type of Which-IDs.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType,
+ bool bSkipPoolDefs = false ) const;
+ /** Writes all font properties to the passed property set.
+ @param pFontColor If set, overrides internal stored font color. */
+ void WriteFontProperties( ScfPropertySet& rPropSet,
+ XclFontPropSetType eType, const Color* pFontColor = nullptr ) const;
+
+private:
+ /** Reads and sets height and flags. */
+ void ReadFontData2( XclImpStream& rStrm );
+ /** Reads and sets height, flags, color, boldness, script, family and charset. */
+ void ReadFontData5( XclImpStream& rStrm );
+
+ /** Reads and sets the font color. */
+ void ReadFontColor( XclImpStream& rStrm );
+
+ /** Reads and sets a byte string as font name. */
+ void ReadFontName2( XclImpStream& rStrm );
+ /** Reads and sets a Unicode string as font name. */
+ void ReadFontName8( XclImpStream& rStrm );
+
+ /** Tests whether the font contains CJK or CTL characters.
+ @descr This is only a weak guess using preselected characters. */
+ void GuessScriptType();
+
+private:
+ XclFontData maData; /// All font attributes.
+ bool mbHasCharSet; /// true = Font contains own character set info.
+ bool mbHasWstrn; /// true = Font contains Western script characters.
+ bool mbHasAsian; /// true = Font contains Asian script characters.
+ bool mbHasCmplx; /// true = Font contains Complex script characters.
+ bool mbFontNameUsed; /// true = Font name, family, charset used.
+ bool mbHeightUsed; /// true = Font height used.
+ bool mbColorUsed; /// true = Color used.
+ bool mbWeightUsed; /// true = Weight used.
+ bool mbEscapemUsed; /// true = Escapement type used.
+ bool mbUnderlUsed; /// true = Underline style used.
+ bool mbItalicUsed; /// true = Italic used.
+ bool mbStrikeUsed; /// true = Strikeout used.
+ bool mbOutlineUsed; /// true = Outlined used.
+ bool mbShadowUsed; /// true = Shadowed used.
+};
+
+/** Stores the data of all fonts occurred in an Excel file. */
+class XclImpFontBuffer : protected XclImpRoot
+{
+public:
+ /** delete copy constructor */
+ XclImpFontBuffer(const XclImpFontBuffer&) = delete;
+ /** delete copy-assignment operator */
+ const XclImpFontBuffer& operator=(const XclImpFontBuffer&) = delete;
+
+ explicit XclImpFontBuffer( const XclImpRoot& rRoot );
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Returns the object that stores all contents of a FONT record. */
+ const XclImpFont* GetFont( sal_uInt16 nFontIndex ) const;
+ /** Returns the application font data of this file, needed i.e. for column width. */
+ const XclFontData& GetAppFontData() const { return maAppFont; }
+
+ /** Reads a FONT record. */
+ void ReadFont( XclImpStream& rStrm );
+ /** Reads an EFONT record (BIFF2 font color). */
+ void ReadEfont( XclImpStream& rStrm );
+
+ /** Fills all font properties from a FONT record to the item set.
+ @param rItemSet The destination item set.
+ @param eType The type of Which-IDs.
+ @param nFontIdx The Excel index of the font.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType,
+ sal_uInt16 nFontIdx, bool bSkipPoolDefs = false ) const;
+ /** Writes all font properties to the passed property set.
+ @param pFontColor If set, overrides internal stored font color. */
+ void WriteFontProperties(
+ ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ sal_uInt16 nFontIdx, const Color* pFontColor = nullptr ) const;
+ /** Writes default font properties for form controls to the passed property set. */
+ void WriteDefaultCtrlFontProperties( ScfPropertySet& rPropSet ) const;
+
+private:
+ /** Updates the application default font. */
+ void UpdateAppFont( const XclFontData& rFontData, bool bHasCharSet );
+
+private:
+ std::vector< XclImpFont > maFontList; /// List of all FONT records in the Excel file.
+ XclFontData maAppFont; /// Application font (for column width).
+ XclImpFont maFont4; /// Built-in font with index 4.
+ XclImpFont maCtrlFont; /// BIFF5 default form controls font (Helv,8pt,bold).
+};
+
+// FORMAT record - number formats =============================================
+
+/** Stores all user defined number formats occurred in the file. */
+class XclImpNumFmtBuffer : public XclNumFmtBuffer, protected XclImpRoot
+{
+public:
+ explicit XclImpNumFmtBuffer( const XclImpRoot& rRoot );
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Reads a FORMAT record. */
+ void ReadFormat( XclImpStream& rStrm );
+
+ /** Read NumFmt from conditional format record */
+ sal_uInt16 ReadCFFormat( XclImpStream& rStrm, bool bIFmt );
+
+ /** Creates the number formats in the Calc document. */
+ void CreateScFormats();
+
+ /** Returns the format key with the passed Excel index or NUMBERFORMAT_ENTRY_NOT_FOUND on error. */
+ sal_uInt32 GetScFormat( sal_uInt16 nXclNumFmt ) const;
+
+ /** Fills an Excel number format to the passed item set.
+ @param rItemSet The destination item set.
+ @param nXclNumFmt The Excel number format index.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet(
+ SfxItemSet& rItemSet, sal_uInt16 nXclNumFmt,
+ bool bSkipPoolDefs = false ) const;
+ /** Fills a Calc number format to the passed item set.
+ @param rItemSet The destination item set.
+ @param nScNumFmt The Calc number formatter index of the format.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillScFmtToItemSet(
+ SfxItemSet& rItemSet, sal_uInt32 nScNumFmt,
+ bool bSkipPoolDefs = false ) const;
+
+private:
+ typedef ::std::map< sal_uInt16, sal_uLong > XclImpIndexMap;
+
+ XclImpIndexMap maIndexMap; /// Maps Excel format indexes to Calc formats.
+ sal_uInt16 mnNextXclIdx; /// Index counter for BIFF2-BIFF4.
+};
+
+// XF, STYLE record - Cell formatting =========================================
+
+/** Extends the XclCellProt struct for import.
+ @descr Provides functions to fill from Excel record data and to fill to item sets. */
+struct XclImpCellProt : public XclCellProt
+{
+ /** Fills this struct with BIFF2 XF record data. */
+ void FillFromXF2( sal_uInt8 nNumFmt );
+ /** Fills this struct with BIFF3-BIFF8 XF record data. */
+ void FillFromXF3( sal_uInt16 nProt );
+
+ /** Inserts items representing this protection style into the item set.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs = false ) const;
+};
+
+/** Extends the XclCellAlign struct for import.
+ @descr Provides functions to fill from Excel record data and to fill to item sets. */
+struct XclImpCellAlign : public XclCellAlign
+{
+ /** Fills this struct with BIFF2 XF record data. */
+ void FillFromXF2( sal_uInt8 nFlags );
+ /** Fills this struct with BIFF3 XF record data. */
+ void FillFromXF3( sal_uInt16 nAlign );
+ /** Fills this struct with BIFF4 XF record data. */
+ void FillFromXF4( sal_uInt16 nAlign );
+ /** Fills this struct with BIFF5/BIFF7 XF record data. */
+ void FillFromXF5( sal_uInt16 nAlign );
+ /** Fills this struct with BIFF8 XF record data. */
+ void FillFromXF8( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib );
+ /** Fills this struct with CF record data. */
+ void FillFromCF( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib );
+
+ /** Inserts items representing this alignment style into the item set.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet( SfxItemSet& rItemSet, const XclImpFont* pFont, bool bSkipPoolDefs = false ) const;
+};
+
+/** Extends the XclCellBorder struct for import.
+ @descr Provides functions to fill from Excel record data and to fill to item sets. */
+struct XclImpCellBorder : public XclCellBorder
+{
+ bool mbLeftUsed; /// true = Left line style used.
+ bool mbRightUsed; /// true = Right line style used.
+ bool mbTopUsed; /// true = Top line style used.
+ bool mbBottomUsed; /// true = Bottom line style used.
+ bool mbDiagUsed; /// true = Diagonal line style used.
+
+ explicit XclImpCellBorder();
+
+ /** Sets outer line states and diagonal line states to used or unused. */
+ void SetUsedFlags( bool bOuterUsed, bool bDiagUsed );
+
+ /** Fills this struct with BIFF2 XF record data. */
+ void FillFromXF2( sal_uInt8 nFlags );
+ /** Fills this struct with BIFF3/BIFF4 XF record data. */
+ void FillFromXF3( sal_uInt32 nBorder );
+ /** Fills this struct with BIFF5/BIFF7 XF record data. */
+ void FillFromXF5( sal_uInt32 nBorder, sal_uInt32 nArea );
+ /** Fills this struct with BIFF8 XF record data. */
+ void FillFromXF8( sal_uInt32 nBorder1, sal_uInt32 nBorder2 );
+
+ /** Fills this struct with BIFF8 CF (conditional format) record data. */
+ void FillFromCF8( sal_uInt16 nLineStyle, sal_uInt32 nLineColor, sal_uInt32 nFlags );
+
+ /** Returns true, if any of the outer border lines is visible. */
+ bool HasAnyOuterBorder() const;
+
+ /** Inserts a box item representing this border style into the item set.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet(
+ SfxItemSet& rItemSet,
+ const XclImpPalette& rPalette,
+ bool bSkipPoolDefs = false ) const;
+};
+
+/** Extends the XclCellArea struct for import.
+ @descr Provides functions to fill from Excel record data and to fill to item sets. */
+struct XclImpCellArea : public XclCellArea
+{
+ bool mbForeUsed; /// true = Foreground color used.
+ bool mbBackUsed; /// true = Background color used.
+ bool mbPattUsed; /// true = Pattern used.
+
+ explicit XclImpCellArea();
+
+ /** Sets colors and pattern state to used or unused. */
+ void SetUsedFlags( bool bUsed );
+
+ /** Fills this struct with BIFF2 XF record data. */
+ void FillFromXF2( sal_uInt8 nFlags );
+ /** Fills this struct with BIFF3/BIFF4 XF record data. */
+ void FillFromXF3( sal_uInt16 nArea );
+ /** Fills this struct with BIFF5/BIFF7 XF record data. */
+ void FillFromXF5( sal_uInt32 nArea );
+ /** Fills this struct with BIFF8 XF record data. */
+ void FillFromXF8( sal_uInt32 nBorder2, sal_uInt16 nArea );
+
+ /** Fills this struct with BIFF8 CF (conditional format) record data. */
+ void FillFromCF8( sal_uInt16 nPattern, sal_uInt16 nColor, sal_uInt32 nFlags );
+
+ /** Inserts a brush item representing this area style into the item set.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items. */
+ void FillToItemSet(
+ SfxItemSet& rItemSet,
+ const XclImpPalette& rPalette,
+ bool bSkipPoolDefs = false ) const;
+};
+
+/** Represents an XF record index with additional information. */
+class XclImpXFIndex
+{
+public:
+ explicit XclImpXFIndex( sal_uInt16 nXFIndex, bool bBoolCell = false ) :
+ mnXFIndex( nXFIndex ), mbBoolCell( bBoolCell ) {}
+
+ sal_uInt16 GetXFIndex() const { return mnXFIndex; }
+ bool IsBoolCell() const { return mbBoolCell; }
+
+private:
+ sal_uInt16 mnXFIndex; /// The XF record index.
+ bool mbBoolCell; /// true = A Boolean value cell.
+};
+
+inline bool operator==( const XclImpXFIndex& rLeft, const XclImpXFIndex& rRight )
+{ return (rLeft.GetXFIndex() == rRight.GetXFIndex()) && (rLeft.IsBoolCell() == rRight.IsBoolCell()); }
+
+inline bool operator!=( const XclImpXFIndex& rLeft, const XclImpXFIndex& rRight )
+{ return !(rLeft == rRight); }
+
+/** Contains all data of a XF record and a Calc item set. */
+class XclImpXF : public XclXFBase, protected XclImpRoot
+{
+public:
+ /** make noncopyable */
+ XclImpXF(const XclImpXF&) = delete;
+ const XclImpXF& operator=(const XclImpXF&) = delete;
+
+ explicit XclImpXF( const XclImpRoot& rRoot );
+ virtual ~XclImpXF() override;
+
+ /** Reads an XF record. */
+ void ReadXF( XclImpStream& rStrm );
+
+ sal_uInt8 GetHorAlign() const { return maAlignment.mnHorAlign; }
+ sal_uInt16 GetFontIndex() const { return mnXclFont; }
+
+ /** Creates a Calc item set containing an item set with all cell properties.
+ @param bSkipPoolDefs true = Do not put items equal to pool default; false = Put all items.
+ @return A read-only reference to the item set stored internally. */
+ const ScPatternAttr& CreatePattern( bool bSkipPoolDefs = false );
+
+ void ApplyPatternToAttrVector(
+ ::std::vector<ScAttrEntry>& rAttrs, SCROW nRow1, SCROW nRow2,
+ sal_uInt32 nForceScNumFmt);
+
+ /** Inserts all formatting attributes to the specified area in the Calc document.
+ @param nForcedNumFmt If not set to NUMBERFORMAT_ENTRY_NOT_FOUND, it will overwrite
+ the number format of the XF. */
+ void ApplyPattern(
+ SCCOL nScCol1, SCROW nScRow1,
+ SCCOL nScCol2, SCROW nScRow2,
+ SCTAB nScTab );
+
+ /** Converts formatting information from BIFF2 cell record data directly. */
+ static void ApplyPatternForBiff2CellFormat(
+ const XclImpRoot& rRoot, const ScAddress& rScPos,
+ sal_uInt8 nFlags1, sal_uInt8 nFlags2, sal_uInt8 nFlags3 );
+
+private:
+ void ReadXF2( XclImpStream& rStrm );
+ void ReadXF3( XclImpStream& rStrm );
+ void ReadXF4( XclImpStream& rStrm );
+ void ReadXF5( XclImpStream& rStrm );
+ void ReadXF8( XclImpStream& rStrm );
+
+ /** Sets all "attribute used" flags according to the passed mask.
+ @descr In cell XFs, a set bit represents "used", in style XFs it is a cleared bit.
+ Therefore mbCellXF must be set correctly before calling this method. */
+ void SetUsedFlags( sal_uInt8 nUsedFlags );
+
+private:
+ typedef ::std::unique_ptr< ScPatternAttr > ScPatternAttrPtr;
+
+ ScPatternAttrPtr mpPattern; /// Calc item set.
+ ScStyleSheet* mpStyleSheet; /// Calc cell style sheet.
+
+ XclImpCellProt maProtection; /// Cell protection flags.
+ XclImpCellAlign maAlignment; /// All alignment attributes.
+ XclImpCellBorder maBorder; /// Border line style.
+ XclImpCellArea maArea; /// Background area style.
+ sal_uInt16 mnXclNumFmt; /// Index to number format.
+ sal_uInt16 mnXclFont; /// Index to font record.
+};
+
+/** Contains all data of a cell style associated with an XF record. */
+class XclImpStyle : protected XclImpRoot
+{
+public:
+ explicit XclImpStyle( const XclImpRoot& rRoot );
+
+ /** Reads a STYLE record. */
+ void ReadStyle( XclImpStream& rStrm );
+
+ const OUString& GetName() const { return maName; }
+ sal_uInt16 GetXfId() const { return mnXfId; }
+ bool IsBuiltin() const { return mbBuiltin && (mnBuiltinId != EXC_STYLE_USERDEF); }
+ sal_uInt8 GetBuiltinId() const { return mnBuiltinId; }
+ sal_uInt8 GetLevel() const { return mnLevel; }
+
+ /** Creates a cell style sheet and inserts it into the Calc document.
+ @return The pointer to the cell style sheet, or 0, if there is no style sheet. */
+ ScStyleSheet* CreateStyleSheet();
+ /** Creates the Calc style sheet, if this is a user-defined style. */
+ void CreateUserStyle( const OUString& rFinalName );
+
+private:
+ OUString maName; /// Cell style name.
+ sal_uInt16 mnXfId; /// Formatting for this cell style.
+ sal_uInt8 mnBuiltinId; /// Identifier for builtin styles.
+ sal_uInt8 mnLevel; /// Level for builtin column/row styles.
+ bool mbBuiltin; /// True = builtin style.
+ bool mbCustom; /// True = customized builtin style.
+ bool mbHidden; /// True = style not visible in GUI.
+
+ OUString maFinalName; /// Final name used in the Calc document.
+ ScStyleSheet* mpStyleSheet; /// Calc cell style sheet.
+};
+
+/** Contains all XF records occurred in the file.
+ @descr This class is able to read XF records (BIFF2 - BIFF8) and STYLE records (BIFF8). */
+class XclImpXFBuffer : protected XclImpRoot
+{
+public:
+ /** make noncopyable */
+ XclImpXFBuffer(const XclImpXFBuffer&) = delete;
+ const XclImpXFBuffer& operator=(const XclImpXFBuffer&) = delete;
+
+ explicit XclImpXFBuffer( const XclImpRoot& rRoot );
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Reads an XF record. */
+ void ReadXF( XclImpStream& rStrm );
+ /** Reads a STYLE record. */
+ void ReadStyle( XclImpStream& rStrm );
+
+ /** Returns the object that stores all contents of an XF record. */
+ XclImpXF* GetXF( sal_uInt16 nXFIndex )
+ { return (nXFIndex >= maXFList.size()) ? nullptr : maXFList.at(nXFIndex).get(); }
+
+ const XclImpXF* GetXF( sal_uInt16 nXFIndex ) const
+ { return (nXFIndex >= maXFList.size()) ? nullptr : maXFList.at(nXFIndex).get(); }
+
+ /** Returns the index to the Excel font used in the specified XF record. */
+ sal_uInt16 GetFontIndex( sal_uInt16 nXFIndex ) const;
+ /** Returns the Excel font used in the specified XF record. */
+ const XclImpFont* GetFont( sal_uInt16 nXFIndex ) const;
+
+ /** Creates all user defined style sheets. */
+ void CreateUserStyles();
+ /** Creates a cell style sheet of the passed XF and inserts it into the Calc document.
+ @return The pointer to the cell style sheet, or 0, if there is no style sheet. */
+ ScStyleSheet* CreateStyleSheet( sal_uInt16 nXFIndex );
+
+private:
+ typedef std::vector< std::unique_ptr<XclImpStyle> > XclImpStyleList;
+ typedef ::std::map< sal_uInt16, XclImpStyle* > XclImpStyleMap;
+
+ std::vector< std::unique_ptr<XclImpXF> > maXFList; /// List of contents of all XF record.
+ XclImpStyleList maBuiltinStyles; /// List of built-in cell styles.
+ XclImpStyleList maUserStyles; /// List of user defined cell styles.
+ XclImpStyleMap maStylesByXf; /// Maps XF records to cell styles.
+};
+
+// Buffer for XF indexes in cells =============================================
+
+/** Contains an (encoded) XF index for a range of rows in a single column. */
+class XclImpXFRange
+{
+public:
+ SCROW mnScRow1; /// The first row of an equal-formatted range.
+ SCROW mnScRow2; /// The last row of an equal-formatted range.
+ XclImpXFIndex maXFIndex; /// Extended XF index.
+
+ inline explicit XclImpXFRange( SCROW nScRow, const XclImpXFIndex& rXFIndex );
+ inline explicit XclImpXFRange( SCROW nFirstScRow, SCROW nLastScRow, const XclImpXFIndex& rXFIndex );
+
+ /** Returns true, if nScRow is contained in own row range. */
+ inline bool Contains( SCROW nScRow ) const;
+
+ /** Returns true, if the range has been expanded. */
+ bool Expand( SCROW nScRow, const XclImpXFIndex& rXFIndex );
+ /** Returns true, if the range has been expanded. */
+ bool Expand( const XclImpXFRange& rNextRange );
+};
+
+inline XclImpXFRange::XclImpXFRange( SCROW nScRow, const XclImpXFIndex& rXFIndex ) :
+ mnScRow1( nScRow ),
+ mnScRow2( nScRow ),
+ maXFIndex( rXFIndex )
+{
+}
+
+inline XclImpXFRange::XclImpXFRange( SCROW nFirstScRow, SCROW nLastScRow, const XclImpXFIndex& rXFIndex ) :
+ mnScRow1( nFirstScRow ),
+ mnScRow2( nLastScRow ),
+ maXFIndex( rXFIndex )
+{
+}
+
+inline bool XclImpXFRange::Contains( SCROW nScRow ) const
+{
+ return (mnScRow1 <= nScRow) && (nScRow <= mnScRow2);
+}
+
+/** Contains the XF indexes for every used cell in a column. */
+class XclImpXFRangeColumn
+{
+public:
+ /** make noncopyable */
+ XclImpXFRangeColumn(const XclImpXFRangeColumn&) = delete;
+ const XclImpXFRangeColumn& operator=(const XclImpXFRangeColumn&) = delete;
+
+ typedef std::vector< std::unique_ptr<XclImpXFRange> > IndexList;
+
+ explicit XclImpXFRangeColumn() {}
+
+ IndexList::iterator begin() { return maIndexList.begin(); }
+ IndexList::iterator end() { return maIndexList.end(); }
+
+ /** Inserts a single row range into the list. */
+ void SetDefaultXF( const XclImpXFIndex& rXFIndex, const XclImpRoot& rRoot );
+
+ /** Inserts a new (encoded) XF index (first try to expand the last range). */
+ void SetXF( SCROW nScRow, const XclImpXFIndex& rXFIndex );
+
+private:
+ /** Finds the previous and next row range from row position nScRow.
+ @descr If an XF still exists, it is contained in rpPrevRange. */
+ void Find(
+ XclImpXFRange*& rpPrevRange,
+ XclImpXFRange*& rpNextRange,
+ sal_uLong& rnNextIndex,
+ SCROW nScRow );
+
+ /** Tries to concatenate a range with its predecessor.
+ @descr The ranges must have the same XF index and must not have a gap.
+ The resulting range has the index nIndex-1. */
+ void TryConcatPrev( sal_uLong nIndex );
+
+ /** Insert a range into the list at the specified index. */
+ void Insert(XclImpXFRange* pXFRange, sal_uLong nIndex);
+
+private:
+ IndexList maIndexList; /// The list of XF index range.
+};
+
+/** Contains the XF indexes for every used cell in a single sheet. */
+class XclImpXFRangeBuffer : protected XclImpRoot
+{
+public:
+ /** make noncopyable */
+ XclImpXFRangeBuffer(const XclImpXFRangeBuffer&) = delete;
+ const XclImpXFRangeBuffer& operator=(const XclImpXFRangeBuffer&) = delete;
+
+ explicit XclImpXFRangeBuffer( const XclImpRoot& rRoot );
+ virtual ~XclImpXFRangeBuffer() override;
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void Initialize();
+
+ /** Inserts a new XF index. */
+ void SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex );
+ /** Inserts a new XF index for blank cells. */
+ void SetBlankXF( const ScAddress& rScPos, sal_uInt16 nXFIndex );
+ /** Inserts a new XF index for boolean cells. */
+ void SetBoolXF( const ScAddress& rScPos, sal_uInt16 nXFIndex );
+ /** Inserts a new XF index for all cells in a row. */
+ void SetRowDefXF( SCROW nScRow, sal_uInt16 nXFIndex );
+ /** Inserts a new XF index for all cells in a column. */
+ void SetColumnDefXF( SCCOL nScCol, sal_uInt16 nXFIndex );
+
+ /** Inserts a range of hyperlink cells. */
+ void SetHyperlink( const XclRange& rXclRange, const OUString& rUrl );
+
+ /** Inserts a complete merged cell range. */
+ void SetMerge( SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2 );
+
+ /** Applies styles and cell merging to the current sheet in the document. */
+ void Finalize();
+
+private:
+ /** Insertion mode of an XF index. */
+ enum XclImpXFInsertMode
+ {
+ xlXFModeCell, /// Filled cell.
+ xlXFModeBoolCell, /// Cell with a single Boolean value.
+ xlXFModeBlank, /// Blank cell.
+ xlXFModeRow /// Row default XF.
+ };
+
+private:
+ /** Inserts a new XF index for the specified cell type. */
+ void SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex, XclImpXFInsertMode eMode );
+
+ /** Copies border of the last cell of the range to the first cell to keep it visible
+ when the range is merged.
+ @param nLine
+ SvxBoxItemLine::RIGHT = copy most-right border of top row;
+ SvxBoxItemLine::BOTTOM = copy most-bottom border of first column. */
+ void SetBorderLine( const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine );
+
+private:
+
+ std::vector< std::shared_ptr< XclImpXFRangeColumn > >
+ maColumns; /// Array of column XF index buffers.
+ std::vector< std::pair< XclRange, OUString > >
+ maHyperlinks; /// Maps URLs to hyperlink cells.
+ ScRangeList maMergeList; /// List of merged cell ranges.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xiview.hxx b/sc/source/filter/inc/xiview.hxx
new file mode 100644
index 000000000..fdf3201fa
--- /dev/null
+++ b/sc/source/filter/inc/xiview.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "xlview.hxx"
+#include "xiroot.hxx"
+
+// Document view settings =====================================================
+
+/** Contains document view settings (WINDOW1 record). */
+class XclImpDocViewSettings : protected XclImpRoot
+{
+public:
+ explicit XclImpDocViewSettings( const XclImpRoot& rRoot );
+
+ /** Reads a WINDOW1 record. */
+ void ReadWindow1( XclImpStream& rStrm );
+
+ /** Returns the Calc index of the displayed sheet. */
+ SCTAB GetDisplScTab() const;
+
+ /** Sets the view settings at the document. */
+ void Finalize();
+
+private:
+ XclDocViewData maData; /// Document view settings data.
+};
+
+// Sheet view settings ========================================================
+
+/** Contains all view settings for a single sheet.
+
+ Usage:
+ 1) When import filter starts reading a worksheet substream, initialize an
+ instance of this class with the Initialize() function. This will set
+ all view options to Excel default values.
+ 2) Read all view related records using the Read*() functions.
+ 3) When import filter ends reading a worksheet substream, call Finalize()
+ to set all view settings to the current sheet of the Calc document.
+ */
+class XclImpTabViewSettings : protected XclImpRoot
+{
+public:
+ explicit XclImpTabViewSettings( const XclImpRoot& rRoot );
+
+ /** Initializes the object to be used for a new sheet. */
+ void Initialize();
+
+ /** Reads a WINDOW2 record. */
+ void ReadWindow2( XclImpStream& rStrm, bool bChart );
+ /** Reads an SCL record. */
+ void ReadScl( XclImpStream& rStrm );
+ /** Reads a PANE record. */
+ void ReadPane( XclImpStream& rStrm );
+ /** Reads a SELECTION record. */
+ void ReadSelection( XclImpStream& rStrm );
+ /** Reads a SHEETEXT record (Tab Color). */
+ void ReadTabBgColor( XclImpStream& rStrm, const XclImpPalette& rPal );
+ /** Sets the view settings at the current sheet or the extended sheet options object. */
+ void Finalize();
+
+private:
+ XclTabViewData maData; /// Sheet view settings data.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xladdress.hxx b/sc/source/filter/inc/xladdress.hxx
new file mode 100644
index 000000000..e090d8445
--- /dev/null
+++ b/sc/source/filter/inc/xladdress.hxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <address.hxx>
+
+class XclImpStream;
+class XclExpStream;
+
+/** A 2D cell address struct with Excel column and row indexes. */
+struct XclAddress
+{
+ sal_uInt16 mnCol;
+ sal_uInt32 mnRow;
+
+ // coverity[uninit_member] - members deliberately not initialized
+ explicit XclAddress( ScAddress::Uninitialized ) {}
+ explicit XclAddress() : mnCol( 0 ), mnRow( 0 ) {}
+ explicit XclAddress( sal_uInt16 nCol, sal_uInt32 nRow ) : mnCol( nCol ), mnRow( nRow ) {}
+
+ void Set( sal_uInt16 nCol, sal_uInt32 nRow ) { mnCol = nCol; mnRow = nRow; }
+
+ void Read( XclImpStream& rStrm );
+ void Write( XclExpStream& rStrm ) const;
+};
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclAddress& rXclPos )
+{
+ rXclPos.Read( rStrm );
+ return rStrm;
+}
+
+inline XclExpStream& operator<<( XclExpStream& rStrm, const XclAddress& rXclPos )
+{
+ rXclPos.Write( rStrm );
+ return rStrm;
+}
+
+/** A 2D cell range address struct with Excel column and row indexes. */
+struct XclRange
+{
+ XclAddress maFirst;
+ XclAddress maLast;
+
+ explicit XclRange( ScAddress::Uninitialized e ) : maFirst( e ), maLast( e ) {}
+ explicit XclRange() {}
+ explicit XclRange( const XclAddress& rPos ) : maFirst( rPos ), maLast( rPos ) {}
+ explicit XclRange( sal_uInt16 nCol1, sal_uInt32 nRow1, sal_uInt16 nCol2, sal_uInt32 nRow2 ) :
+ maFirst( nCol1, nRow1 ), maLast( nCol2, nRow2 ) {}
+
+ void Set( sal_uInt16 nCol1, sal_uInt32 nRow1, sal_uInt16 nCol2, sal_uInt32 nRow2 )
+ { maFirst.Set( nCol1, nRow1 ); maLast.Set( nCol2, nRow2 ); }
+
+ sal_uInt16 GetColCount() const {
+ return maFirst.mnCol <= maLast.mnCol && maFirst.mnRow <= maLast.mnRow
+ ? maLast.mnCol - maFirst.mnCol + 1 : 0;
+ }
+ sal_uInt32 GetRowCount() const {
+ return maFirst.mnCol <= maLast.mnCol && maFirst.mnRow <= maLast.mnRow
+ ? maLast.mnRow - maFirst.mnRow + 1 : 0;
+ }
+ bool Contains( const XclAddress& rPos ) const;
+
+ void Read( XclImpStream& rStrm, bool bCol16Bit = true );
+ void Write( XclExpStream& rStrm, bool bCol16Bit = true ) const;
+};
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclRange& rXclRange )
+{
+ rXclRange.Read( rStrm );
+ return rStrm;
+}
+
+inline XclExpStream& operator<<( XclExpStream& rStrm, const XclRange& rXclRange )
+{
+ rXclRange.Write( rStrm );
+ return rStrm;
+}
+
+typedef ::std::vector< XclRange > XclRangeVector;
+
+/** A 2D cell range address list with Excel column and row indexes. */
+class XclRangeList
+{
+private:
+ XclRangeVector mRanges;
+
+public:
+ explicit XclRangeList() : mRanges() {}
+
+ size_t size() const { return mRanges.size(); }
+ bool empty() const { return mRanges.empty(); }
+ XclRangeVector::const_iterator begin() const { return mRanges.begin(); }
+ XclRangeVector::const_iterator end() const { return mRanges.end(); }
+ void clear() { mRanges.clear(); }
+ void push_back(const XclRange &rRange) { mRanges.push_back(rRange); }
+
+ XclRange GetEnclosingRange() const;
+
+ void Read( XclImpStream& rStrm, bool bCol16Bit = true, sal_uInt16 nCountInStream = 0 );
+ void Write( XclExpStream& rStrm, bool bCol16Bit = true, sal_uInt16 nCountInStream = 0 ) const;
+ void WriteSubList( XclExpStream& rStrm,
+ size_t nBegin, size_t nCount, bool bCol16Bit = true, sal_uInt16 nCountInStream = 0 ) const;
+};
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclRangeList& rXclRanges )
+{
+ rXclRanges.Read( rStrm );
+ return rStrm;
+}
+
+inline XclExpStream& operator<<( XclExpStream& rStrm, const XclRangeList& rXclRanges )
+{
+ rXclRanges.Write( rStrm );
+ return rStrm;
+}
+
+class XclTracer;
+
+/** Base class for import/export address converters. */
+class XclAddressConverterBase
+{
+public:
+ explicit XclAddressConverterBase( XclTracer& rTracer, const ScAddress& rMaxPos );
+ virtual ~XclAddressConverterBase();
+
+ /** Returns whether the "some columns have been cut" warning box should be shown. */
+ bool IsColTruncated() const { return mbColTrunc; }
+ /** Returns whether the "some rows have been cut" warning box should be shown. */
+ bool IsRowTruncated() const { return mbRowTrunc; }
+ /** Returns whether the "some sheets have been cut" warning box should be shown. */
+ bool IsTabTruncated() const { return mbTabTrunc; }
+
+ /** Checks if the passed sheet index is valid.
+ @param nScTab The sheet index to check.
+ Sets the internal flag that produces a warning box
+ after loading/saving the file, if the sheet index is not valid.
+ */
+ void CheckScTab( SCTAB nScTab );
+
+protected:
+ XclTracer& mrTracer; /// Tracer for invalid addresses.
+ ScAddress maMaxPos; /// Default maximum position.
+ sal_uInt16 mnMaxCol; /// Maximum column index, as 16-bit value.
+ sal_uInt32 mnMaxRow; /// Maximum row index.
+ bool mbColTrunc; /// Flag for "columns truncated" warning box.
+ bool mbRowTrunc; /// Flag for "rows truncated" warning box.
+ bool mbTabTrunc; /// Flag for "tables truncated" warning box.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlchart.hxx b/sc/source/filter/inc/xlchart.hxx
new file mode 100644
index 000000000..8b015e012
--- /dev/null
+++ b/sc/source/filter/inc/xlchart.hxx
@@ -0,0 +1,1437 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+#include "fapihelper.hxx"
+#include <map>
+#include <memory>
+
+namespace com::sun::star {
+ namespace container { class XNameContainer; }
+ namespace lang { class XMultiServiceFactory; }
+ namespace chart { class XChartDocument; }
+ namespace chart2 { class XChartDocument; }
+ namespace drawing { class XShape; }
+}
+
+class XclRoot;
+
+// Property names =============================================================
+
+// service names
+inline constexpr OUStringLiteral SERVICE_DRAWING_BITMAPTABLE = u"com.sun.star.drawing.BitmapTable";
+inline constexpr OUStringLiteral SERVICE_DRAWING_DASHTABLE = u"com.sun.star.drawing.DashTable";
+inline constexpr OUStringLiteral SERVICE_DRAWING_GRADIENTTABLE = u"com.sun.star.drawing.GradientTable";
+inline constexpr OUStringLiteral SERVICE_DRAWING_HATCHTABLE = u"com.sun.star.drawing.HatchTable";
+
+inline constexpr OUStringLiteral SERVICE_CHART2_AXIS = u"com.sun.star.chart2.Axis";
+inline constexpr OUStringLiteral SERVICE_CHART2_DATAPROVIDER = u"com.sun.star.chart2.data.DataProvider";
+inline constexpr OUStringLiteral SERVICE_CHART2_DATASERIES = u"com.sun.star.chart2.DataSeries";
+inline constexpr OUStringLiteral SERVICE_CHART2_DIAGRAM = u"com.sun.star.chart2.Diagram";
+inline constexpr OUStringLiteral SERVICE_CHART2_ERRORBAR = u"com.sun.star.chart2.ErrorBar";
+inline constexpr OUStringLiteral SERVICE_CHART2_LEGEND = u"com.sun.star.chart2.Legend";
+inline constexpr OUStringLiteral SERVICE_CHART2_TITLE = u"com.sun.star.chart2.Title";
+
+// property names
+inline constexpr OUStringLiteral EXC_CHPROP_ADDITIONALSHAPES = u"AdditionalShapes";
+inline constexpr OUStringLiteral EXC_CHPROP_ANCHORPOSITION = u"AnchorPosition";
+inline constexpr OUStringLiteral EXC_CHPROP_ARRANGEORDER = u"ArrangeOrder";
+inline constexpr OUStringLiteral EXC_CHPROP_ATTAXISINDEX = u"AttachedAxisIndex";
+inline constexpr OUStringLiteral EXC_CHPROP_ATTRIBDATAPOINTS = u"AttributedDataPoints";
+inline constexpr OUStringLiteral EXC_CHPROP_BLACKDAY = u"BlackDay";
+inline constexpr OUStringLiteral EXC_CHPROP_COLOR = u"Color";
+inline constexpr OUStringLiteral EXC_CHPROP_CONNECTBARS = u"ConnectBars";
+inline constexpr OUStringLiteral EXC_CHPROP_CROSSOVERPOSITION = u"CrossoverPosition";
+inline constexpr OUStringLiteral EXC_CHPROP_CROSSOVERVALUE = u"CrossoverValue";
+inline constexpr OUStringLiteral EXC_CHPROP_CURVESTYLE = u"CurveStyle";
+inline constexpr OUStringLiteral EXC_CHPROP_CURVENAME = u"CurveName";
+inline constexpr OUStringLiteral EXC_CHPROP_D3DSCENEAMBIENTCOLOR = u"D3DSceneAmbientColor";
+inline constexpr OUStringLiteral EXC_CHPROP_D3DSCENELIGHTON1 = u"D3DSceneLightOn1";
+inline constexpr OUStringLiteral EXC_CHPROP_D3DSCENELIGHTCOLOR2 = u"D3DSceneLightColor2";
+inline constexpr OUStringLiteral EXC_CHPROP_D3DSCENELIGHTDIR2 = u"D3DSceneLightDirection2";
+inline constexpr OUStringLiteral EXC_CHPROP_D3DSCENELIGHTON2 = u"D3DSceneLightOn2";
+inline constexpr OUStringLiteral EXC_CHPROP_D3DSCENEPERSPECTIVE = u"D3DScenePerspective";
+inline constexpr OUStringLiteral EXC_CHPROP_D3DSCENESHADEMODE = u"D3DSceneShadeMode";
+inline constexpr OUStringLiteral EXC_CHPROP_DISPLAYLABELS = u"DisplayLabels";
+inline constexpr OUStringLiteral EXC_CHPROP_ERRORBARSTYLE = u"ErrorBarStyle";
+inline constexpr OUStringLiteral EXC_CHPROP_ERRORBARX = u"ErrorBarX";
+inline constexpr OUStringLiteral EXC_CHPROP_ERRORBARY = u"ErrorBarY";
+inline constexpr OUStringLiteral EXC_CHPROP_EXPANSION = u"Expansion";
+inline constexpr OUStringLiteral EXC_CHPROP_EXPTIMEINCREMENT = u"ExplicitTimeIncrement";
+inline constexpr OUStringLiteral EXC_CHPROP_EXTRAPOLATE_FORWARD = u"ExtrapolateForward";
+inline constexpr OUStringLiteral EXC_CHPROP_EXTRAPOLATE_BACKWARD = u"ExtrapolateBackward";
+inline constexpr OUStringLiteral EXC_CHPROP_FORCE_INTERCEPT = u"ForceIntercept";
+inline constexpr OUStringLiteral EXC_CHPROP_GAPWIDTHSEQ = u"GapwidthSequence";
+inline constexpr OUStringLiteral EXC_CHPROP_GEOMETRY3D = u"Geometry3D";
+inline constexpr OUStringLiteral EXC_CHPROP_INCLUDEHIDDENCELLS = u"IncludeHiddenCells";
+inline constexpr OUStringLiteral EXC_CHPROP_INTERCEPT_VALUE = u"InterceptValue";
+inline constexpr OUStringLiteral EXC_CHPROP_JAPANESE = u"Japanese";
+inline constexpr OUStringLiteral EXC_CHPROP_LABEL = u"Label";
+inline constexpr OUStringLiteral EXC_CHPROP_LABELPLACEMENT = u"LabelPlacement";
+inline constexpr OUStringLiteral EXC_CHPROP_LABELPOSITION = u"LabelPosition";
+inline constexpr OUStringLiteral EXC_CHPROP_LABELSEPARATOR = u"LabelSeparator";
+inline constexpr OUStringLiteral EXC_CHPROP_SHOWLEGENDENTRY = u"ShowLegendEntry";
+inline constexpr OUStringLiteral EXC_CHPROP_MAJORTICKS = u"MajorTickmarks";
+inline constexpr OUStringLiteral EXC_CHPROP_MARKPOSITION = u"MarkPosition";
+inline constexpr OUStringLiteral EXC_CHPROP_MINORTICKS = u"MinorTickmarks";
+inline constexpr OUStringLiteral EXC_CHPROP_MISSINGVALUETREATMENT = u"MissingValueTreatment";
+inline constexpr OUStringLiteral EXC_CHPROP_MOVING_AVERAGE_PERIOD = u"MovingAveragePeriod";
+inline constexpr OUStringLiteral EXC_CHPROP_NEGATIVEERROR = u"NegativeError";
+inline constexpr OUStringLiteral EXC_CHPROP_NUMBERFORMAT = u"NumberFormat";
+inline constexpr OUStringLiteral EXC_CHPROP_NUMBERFORMAT_LINKSRC = u"LinkNumberFormatToSource";
+inline constexpr OUStringLiteral EXC_CHPROP_OFFSET = u"Offset";
+inline constexpr OUStringLiteral EXC_CHPROP_OVERLAPSEQ = u"OverlapSequence";
+inline constexpr OUStringLiteral EXC_CHPROP_PERCENTAGENUMFMT = u"PercentageNumberFormat";
+inline constexpr OUStringLiteral EXC_CHPROP_PERCENTDIAGONAL = u"PercentDiagonal";
+inline constexpr OUStringLiteral EXC_CHPROP_PERSPECTIVE = u"Perspective";
+inline constexpr OUStringLiteral EXC_CHPROP_POSITIVEERROR = u"PositiveError";
+inline constexpr OUStringLiteral EXC_CHPROP_POLYNOMIAL_DEGREE = u"PolynomialDegree";
+inline constexpr OUStringLiteral EXC_CHPROP_RELATIVEPOSITION = u"RelativePosition";
+inline constexpr OUStringLiteral EXC_CHPROP_RELATIVESIZE = u"RelativeSize";
+inline constexpr OUStringLiteral EXC_CHPROP_RIGHTANGLEDAXES = u"RightAngledAxes";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE = u"Role";
+inline constexpr OUStringLiteral EXC_CHPROP_ROTATIONHORIZONTAL = u"RotationHorizontal";
+inline constexpr OUStringLiteral EXC_CHPROP_ROTATIONVERTICAL = u"RotationVertical";
+inline constexpr OUStringLiteral EXC_CHPROP_3DRELATIVEHEIGHT = u"3DRelativeHeight";
+inline constexpr OUStringLiteral EXC_CHPROP_SHOW = u"Show";
+inline constexpr OUStringLiteral EXC_CHPROP_SHOWCORRELATION = u"ShowCorrelationCoefficient";
+inline constexpr OUStringLiteral EXC_CHPROP_SHOWEQUATION = u"ShowEquation";
+inline constexpr OUStringLiteral EXC_CHPROP_SHOWFIRST = u"ShowFirst";
+inline constexpr OUStringLiteral EXC_CHPROP_SHOWHIGHLOW = u"ShowHighLow";
+inline constexpr OUStringLiteral EXC_CHPROP_SHOWNEGATIVEERROR = u"ShowNegativeError";
+inline constexpr OUStringLiteral EXC_CHPROP_SHOWPOSITIVEERROR = u"ShowPositiveError";
+inline constexpr OUStringLiteral EXC_CHPROP_STACKCHARACTERS = u"StackCharacters";
+inline constexpr OUStringLiteral EXC_CHPROP_STACKINGDIR = u"StackingDirection";
+inline constexpr OUStringLiteral EXC_CHPROP_STARTINGANGLE = u"StartingAngle";
+inline constexpr OUStringLiteral EXC_CHPROP_SWAPXANDYAXIS = u"SwapXAndYAxis";
+inline constexpr OUStringLiteral EXC_CHPROP_SYMBOL = u"Symbol";
+inline constexpr OUStringLiteral EXC_CHPROP_TEXTBREAK = u"TextBreak";
+inline constexpr OUStringLiteral EXC_CHPROP_TEXTOVERLAP = u"TextOverlap";
+inline constexpr OUStringLiteral EXC_CHPROP_TEXTROTATION = u"TextRotation";
+inline constexpr OUStringLiteral EXC_CHPROP_USERINGS = u"UseRings";
+inline constexpr OUStringLiteral EXC_CHPROP_VARYCOLORSBY = u"VaryColorsByPoint";
+inline constexpr OUStringLiteral EXC_CHPROP_WEIGHT = u"Weight";
+inline constexpr OUStringLiteral EXC_CHPROP_WHITEDAY = u"WhiteDay";
+
+// data series roles
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_CATEG = u"categories";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_ERRORBARS_NEGX = u"error-bars-x-negative";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_ERRORBARS_NEGY = u"error-bars-y-negative";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_ERRORBARS_POSX = u"error-bars-x-positive";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_ERRORBARS_POSY = u"error-bars-y-positive";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_LABEL = u"label";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_XVALUES = u"values-x";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_YVALUES = u"values-y";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_OPENVALUES = u"values-first";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_CLOSEVALUES = u"values-last";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_LOWVALUES = u"values-min";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_HIGHVALUES = u"values-max";
+inline constexpr OUStringLiteral EXC_CHPROP_ROLE_SIZEVALUES = u"values-size";
+
+// Constants and Enumerations =================================================
+
+const std::size_t EXC_CHART_PROGRESS_SIZE = 10;
+const sal_uInt16 EXC_CHART_AUTOROTATION = 0xFFFF; /// Automatic rotation, e.g. axis labels (internal use only).
+
+const sal_Int32 EXC_CHART_AXIS_NONE = -1; /// For internal use only.
+const sal_Int32 EXC_CHART_AXIS_X = 0; /// API X axis index.
+const sal_Int32 EXC_CHART_AXIS_Y = 1; /// API Y axis index.
+const sal_Int32 EXC_CHART_AXIS_Z = 2; /// API Z axis index.
+const sal_Int32 EXC_CHART_AXESSET_NONE = -1; /// For internal use only.
+const sal_Int32 EXC_CHART_AXESSET_PRIMARY = 0; /// API primary axes set index.
+const sal_Int32 EXC_CHART_AXESSET_SECONDARY = 1; /// API secondary axes set index.
+
+const sal_Int32 EXC_CHART_TOTALUNITS = 4000; /// Most chart objects are positioned in 1/4000 of chart area.
+const sal_Int32 EXC_CHART_PLOTAREAUNITS = 1000; /// For objects that are positioned in 1/1000 of plot area.
+
+// (0x0850) CHFRINFO ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFRINFO = 0x0850;
+
+const sal_uInt8 EXC_CHFRINFO_EXCEL2000 = 9;
+const sal_uInt8 EXC_CHFRINFO_EXCELXP2003 = 10;
+const sal_uInt8 EXC_CHFRINFO_EXCEL2007 = 11;
+
+// (0x0852, 0x0853) CHFRBLOCKBEGIN, CHFRBLOCKEND ------------------------------
+
+const sal_uInt16 EXC_ID_CHFRBLOCKBEGIN = 0x0852;
+const sal_uInt16 EXC_ID_CHFRBLOCKEND = 0x0853;
+
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_AXESSET = 0;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_TEXT = 2;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_AXIS = 4;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_TYPEGROUP = 5;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_DATATABLE = 6;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_FRAME = 7;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_LEGEND = 9;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_LEGENDEX = 10;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_SERIES = 12;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_CHART = 13;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_DATAFORMAT = 14;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_DROPBAR = 15;
+const sal_uInt16 EXC_CHFRBLOCK_TYPE_UNKNOWN = 0xFFFF; /// For internal use only.
+
+const sal_uInt16 EXC_CHFRBLOCK_TEXT_TITLE = 0;
+const sal_uInt16 EXC_CHFRBLOCK_TEXT_DEFTEXT = 2;
+const sal_uInt16 EXC_CHFRBLOCK_TEXT_AXISTITLE = 4;
+const sal_uInt16 EXC_CHFRBLOCK_TEXT_DATALABEL = 5;
+
+const sal_uInt16 EXC_CHFRBLOCK_FRAME_STANDARD = 0;
+const sal_uInt16 EXC_CHFRBLOCK_FRAME_PLOTFRAME = 1;
+const sal_uInt16 EXC_CHFRBLOCK_FRAME_BACKGROUND = 2;
+
+// (0x086B) CHFRLABELPROPS ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFRLABELPROPS = 0x086B;
+
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWSERIES = 0x0001;
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWCATEG = 0x0002;
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWVALUE = 0x0004;
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWPERCENT = 0x0008;
+const sal_uInt16 EXC_CHFRLABELPROPS_SHOWBUBBLE = 0x0010;
+
+// (0x1001) CHUNITS -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHUNITS = 0x1001;
+
+const sal_uInt16 EXC_CHUNITS_TWIPS = 0;
+const sal_uInt16 EXC_CHUNITS_PIXELS = 1;
+
+// (0x1002) CHCHART -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHCHART = 0x1002;
+
+// (0x1003) CHSERIES ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERIES = 0x1003;
+
+const sal_uInt16 EXC_CHSERIES_DATE = 0;
+const sal_uInt16 EXC_CHSERIES_NUMERIC = 1;
+const sal_uInt16 EXC_CHSERIES_SEQUENCE = 2;
+const sal_uInt16 EXC_CHSERIES_TEXT = 3;
+
+const sal_uInt16 EXC_CHSERIES_MAXSERIES = 255; /// Maximum valid series index.
+const sal_uInt16 EXC_CHSERIES_INVALID = 0xFFFF; /// Invalid series index (for internal use).
+
+// (0x1006) CHDATAFORMAT ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHDATAFORMAT = 0x1006;
+
+const sal_uInt16 EXC_CHDATAFORMAT_MAXPOINTCOUNT = 32000; /// Maximum number of data points.
+const sal_uInt16 EXC_CHDATAFORMAT_DEFAULT = 0xFFFD; /// As format index: global default for an axes set.
+const sal_uInt16 EXC_CHDATAFORMAT_UNKNOWN = 0xFFFE; /// As point index: unknown format, don't use.
+const sal_uInt16 EXC_CHDATAFORMAT_ALLPOINTS = 0xFFFF; /// As point index: default for a series.
+
+const sal_uInt16 EXC_CHDATAFORMAT_OLDCOLORS = 0x0001;
+
+// (0x1007) CHLINEFORMAT ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHLINEFORMAT = 0x1007;
+
+const sal_uInt16 EXC_CHLINEFORMAT_SOLID = 0;
+const sal_uInt16 EXC_CHLINEFORMAT_DASH = 1;
+const sal_uInt16 EXC_CHLINEFORMAT_DOT = 2;
+const sal_uInt16 EXC_CHLINEFORMAT_DASHDOT = 3;
+const sal_uInt16 EXC_CHLINEFORMAT_DASHDOTDOT = 4;
+const sal_uInt16 EXC_CHLINEFORMAT_NONE = 5;
+const sal_uInt16 EXC_CHLINEFORMAT_DARKTRANS = 6;
+const sal_uInt16 EXC_CHLINEFORMAT_MEDTRANS = 7;
+const sal_uInt16 EXC_CHLINEFORMAT_LIGHTTRANS = 8;
+
+const sal_Int16 EXC_CHLINEFORMAT_HAIR = -1;
+const sal_Int16 EXC_CHLINEFORMAT_SINGLE = 0;
+const sal_Int16 EXC_CHLINEFORMAT_DOUBLE = 1;
+const sal_Int16 EXC_CHLINEFORMAT_TRIPLE = 2;
+
+const sal_uInt16 EXC_CHLINEFORMAT_AUTO = 0x0001;
+const sal_uInt16 EXC_CHLINEFORMAT_SHOWAXIS = 0x0004;
+
+// (0x1009) CHMARKERFORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHMARKERFORMAT = 0x1009;
+
+const sal_uInt16 EXC_CHMARKERFORMAT_NOSYMBOL = 0;
+const sal_uInt16 EXC_CHMARKERFORMAT_SQUARE = 1;
+const sal_uInt16 EXC_CHMARKERFORMAT_DIAMOND = 2;
+const sal_uInt16 EXC_CHMARKERFORMAT_TRIANGLE = 3;
+const sal_uInt16 EXC_CHMARKERFORMAT_CROSS = 4;
+const sal_uInt16 EXC_CHMARKERFORMAT_STAR = 5;
+const sal_uInt16 EXC_CHMARKERFORMAT_DOWJ = 6;
+const sal_uInt16 EXC_CHMARKERFORMAT_STDDEV = 7;
+const sal_uInt16 EXC_CHMARKERFORMAT_CIRCLE = 8;
+const sal_uInt16 EXC_CHMARKERFORMAT_PLUS = 9;
+
+const sal_uInt32 EXC_CHMARKERFORMAT_HAIRSIZE = 60; /// Automatic symbol size for hair lines.
+const sal_uInt32 EXC_CHMARKERFORMAT_SINGLESIZE = 100; /// Automatic symbol size for single lines.
+const sal_uInt32 EXC_CHMARKERFORMAT_DOUBLESIZE = 140; /// Automatic symbol size for double lines.
+const sal_uInt32 EXC_CHMARKERFORMAT_TRIPLESIZE = 180; /// Automatic symbol size for triple lines.
+
+const sal_uInt16 EXC_CHMARKERFORMAT_AUTO = 0x0001;
+const sal_uInt16 EXC_CHMARKERFORMAT_NOFILL = 0x0010;
+const sal_uInt16 EXC_CHMARKERFORMAT_NOLINE = 0x0020;
+
+// (0x100A) CHAREAFORMAT ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHAREAFORMAT = 0x100A;
+
+const sal_uInt16 EXC_CHAREAFORMAT_AUTO = 0x0001;
+const sal_uInt16 EXC_CHAREAFORMAT_INVERTNEG = 0x0002;
+
+// (0x100B) CHPIEFORMAT -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPIEFORMAT = 0x100B;
+
+// (0x100C) CHATTACHEDLABEL ---------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHATTACHEDLABEL = 0x100C;
+
+const sal_uInt16 EXC_CHATTLABEL_SHOWVALUE = 0x0001;
+const sal_uInt16 EXC_CHATTLABEL_SHOWPERCENT = 0x0002;
+const sal_uInt16 EXC_CHATTLABEL_SHOWCATEGPERC = 0x0004;
+const sal_uInt16 EXC_CHATTLABEL_SMOOTHED = 0x0008; /// Smoothed line.
+const sal_uInt16 EXC_CHATTLABEL_SHOWCATEG = 0x0010;
+const sal_uInt16 EXC_CHATTLABEL_SHOWBUBBLE = 0x0020;
+
+// (0x100D) CHSTRING ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSTRING = 0x100D;
+
+// (0x1014) CHTYPEGROUP -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHTYPEGROUP = 0x1014;
+
+const sal_uInt16 EXC_CHTYPEGROUP_VARIEDCOLORS = 0x0001; /// Varied colors for points.
+
+// (0x1015) CHLEGEND ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHLEGEND = 0x1015;
+
+const sal_uInt8 EXC_CHLEGEND_BOTTOM = 0;
+const sal_uInt8 EXC_CHLEGEND_CORNER = 1;
+const sal_uInt8 EXC_CHLEGEND_TOP = 2;
+const sal_uInt8 EXC_CHLEGEND_RIGHT = 3;
+const sal_uInt8 EXC_CHLEGEND_LEFT = 4;
+const sal_uInt8 EXC_CHLEGEND_NOTDOCKED = 7;
+
+const sal_uInt8 EXC_CHLEGEND_CLOSE = 0;
+const sal_uInt8 EXC_CHLEGEND_MEDIUM = 1;
+const sal_uInt8 EXC_CHLEGEND_OPEN = 2;
+
+const sal_uInt16 EXC_CHLEGEND_DOCKED = 0x0001;
+const sal_uInt16 EXC_CHLEGEND_AUTOSERIES = 0x0002;
+const sal_uInt16 EXC_CHLEGEND_AUTOPOSX = 0x0004;
+const sal_uInt16 EXC_CHLEGEND_AUTOPOSY = 0x0008;
+const sal_uInt16 EXC_CHLEGEND_STACKED = 0x0010;
+const sal_uInt16 EXC_CHLEGEND_DATATABLE = 0x0020;
+
+// (0x1017) CHBAR, CHCOLUMN ---------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHBAR = 0x1017;
+
+const sal_uInt16 EXC_CHBAR_HORIZONTAL = 0x0001;
+const sal_uInt16 EXC_CHBAR_STACKED = 0x0002;
+const sal_uInt16 EXC_CHBAR_PERCENT = 0x0004;
+const sal_uInt16 EXC_CHBAR_SHADOW = 0x0008;
+
+// (0x1018, 0x101A) CHLINE, CHAREA --------------------------------------------
+
+const sal_uInt16 EXC_ID_CHLINE = 0x1018;
+const sal_uInt16 EXC_ID_CHAREA = 0x101A;
+
+const sal_uInt16 EXC_CHLINE_STACKED = 0x0001;
+const sal_uInt16 EXC_CHLINE_PERCENT = 0x0002;
+const sal_uInt16 EXC_CHLINE_SHADOW = 0x0004;
+
+// (0x1019) CHPIE -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPIE = 0x1019;
+
+const sal_uInt16 EXC_CHPIE_SHADOW = 0x0001;
+const sal_uInt16 EXC_CHPIE_LINES = 0x0002;
+
+// (0x101B) CHSCATTER ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSCATTER = 0x101B;
+
+const sal_uInt16 EXC_CHSCATTER_AREA = 1; /// Bubble area refers to value.
+const sal_uInt16 EXC_CHSCATTER_WIDTH = 2; /// Bubble width refers to value.
+
+const sal_uInt16 EXC_CHSCATTER_BUBBLES = 0x0001;
+const sal_uInt16 EXC_CHSCATTER_SHOWNEG = 0x0002;
+const sal_uInt16 EXC_CHSCATTER_SHADOW = 0x0004;
+
+// (0x001C) CHCHARTLINE -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHCHARTLINE = 0x101C;
+
+const sal_uInt16 EXC_CHCHARTLINE_DROP = 0; /// Drop lines.
+const sal_uInt16 EXC_CHCHARTLINE_HILO = 1; /// Hi-lo lines.
+const sal_uInt16 EXC_CHCHARTLINE_CONNECT = 2; /// Connector lines in stacked bar charts.
+
+// (0x101D) CHAXIS ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHAXIS = 0x101D;
+
+const sal_uInt16 EXC_CHAXIS_X = 0;
+const sal_uInt16 EXC_CHAXIS_Y = 1;
+const sal_uInt16 EXC_CHAXIS_Z = 2;
+const sal_uInt16 EXC_CHAXIS_NONE = 0xFFFF; /// For internal use only.
+
+// (0x101E) CHTICK ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHTICK = 0x101E;
+
+const sal_uInt8 EXC_CHTICK_INSIDE = 0x01;
+const sal_uInt8 EXC_CHTICK_OUTSIDE = 0x02;
+
+const sal_uInt8 EXC_CHTICK_NOLABEL = 0;
+const sal_uInt8 EXC_CHTICK_LOW = 1; /// Below diagram/right of diagram.
+const sal_uInt8 EXC_CHTICK_HIGH = 2; /// Above diagram/left of diagram.
+const sal_uInt8 EXC_CHTICK_NEXT = 3; /// Next to axis.
+
+const sal_uInt8 EXC_CHTICK_TRANSPARENT = 1;
+const sal_uInt8 EXC_CHTICK_OPAQUE = 2;
+
+const sal_uInt16 EXC_CHTICK_AUTOCOLOR = 0x0001;
+const sal_uInt16 EXC_CHTICK_AUTOFILL = 0x0002;
+const sal_uInt16 EXC_CHTICK_AUTOROT = 0x0020;
+
+// (0x101F) CHVALUERANGE ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHVALUERANGE = 0x101F;
+
+const sal_uInt16 EXC_CHVALUERANGE_AUTOMIN = 0x0001;
+const sal_uInt16 EXC_CHVALUERANGE_AUTOMAX = 0x0002;
+const sal_uInt16 EXC_CHVALUERANGE_AUTOMAJOR = 0x0004;
+const sal_uInt16 EXC_CHVALUERANGE_AUTOMINOR = 0x0008;
+const sal_uInt16 EXC_CHVALUERANGE_AUTOCROSS = 0x0010;
+const sal_uInt16 EXC_CHVALUERANGE_LOGSCALE = 0x0020;
+const sal_uInt16 EXC_CHVALUERANGE_REVERSE = 0x0040; /// Axis direction reversed.
+const sal_uInt16 EXC_CHVALUERANGE_MAXCROSS = 0x0080; /// Other axis crosses at own maximum.
+const sal_uInt16 EXC_CHVALUERANGE_BIT8 = 0x0100; /// This bit is always set in BIFF5+.
+
+// (0x1020) CHLABELRANGE -----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHLABELRANGE = 0x1020;
+
+const sal_uInt16 EXC_CHLABELRANGE_BETWEEN = 0x0001; /// Axis between categories.
+const sal_uInt16 EXC_CHLABELRANGE_MAXCROSS = 0x0002; /// Other axis crosses at own maximum.
+const sal_uInt16 EXC_CHLABELRANGE_REVERSE = 0x0004; /// Axis direction reversed.
+
+// (0x1021) CHAXISLINE --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHAXISLINE = 0x1021;
+
+const sal_uInt16 EXC_CHAXISLINE_AXISLINE = 0; /// Axis line itself.
+const sal_uInt16 EXC_CHAXISLINE_MAJORGRID = 1; /// Major grid line.
+const sal_uInt16 EXC_CHAXISLINE_MINORGRID = 2; /// Minor grid line.
+const sal_uInt16 EXC_CHAXISLINE_WALLS = 3; /// Walls (X, Z axis), floor (Y axis).
+
+// (0x1024) CHDEFAULTTEXT -----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHDEFAULTTEXT = 0x1024;
+
+const sal_uInt16 EXC_CHDEFTEXT_TEXTLABEL = 0; /// Default for text data labels (not used?).
+const sal_uInt16 EXC_CHDEFTEXT_NUMLABEL = 1; /// Default for numeric data labels (not used?).
+const sal_uInt16 EXC_CHDEFTEXT_GLOBAL = 2; /// Default text for all chart objects.
+const sal_uInt16 EXC_CHDEFTEXT_AXESSET = 3; /// Default text for axes and data points (BIFF8 only).
+const sal_uInt16 EXC_CHDEFTEXT_NONE = 0xFFFF; /// No default text available.
+
+// (0x1025) CHTEXT ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHTEXT = 0x1025;
+
+const sal_uInt8 EXC_CHTEXT_ALIGN_TOPLEFT = 1; /// Horizontal: left, vertical: top.
+const sal_uInt8 EXC_CHTEXT_ALIGN_CENTER = 2;
+const sal_uInt8 EXC_CHTEXT_ALIGN_BOTTOMRIGHT = 3; /// Horizontal: right, vertical: bottom.
+const sal_uInt8 EXC_CHTEXT_ALIGN_JUSTIFY = 4;
+const sal_uInt8 EXC_CHTEXT_ALIGN_DISTRIBUTE = 5;
+
+const sal_uInt16 EXC_CHTEXT_TRANSPARENT = 1;
+const sal_uInt16 EXC_CHTEXT_OPAQUE = 2;
+
+const sal_uInt16 EXC_CHTEXT_AUTOCOLOR = 0x0001; /// Automatic text color.
+const sal_uInt16 EXC_CHTEXT_SHOWSYMBOL = 0x0002; /// Legend symbol for data point caption.
+const sal_uInt16 EXC_CHTEXT_SHOWVALUE = 0x0004; /// Data point caption is the value.
+const sal_uInt16 EXC_CHTEXT_VERTICAL = 0x0008;
+const sal_uInt16 EXC_CHTEXT_AUTOTEXT = 0x0010; /// Label text generated from chart data.
+const sal_uInt16 EXC_CHTEXT_AUTOGEN = 0x0020; /// Text object is inserted automatically.
+const sal_uInt16 EXC_CHTEXT_DELETED = 0x0040; /// Text object is removed.
+const sal_uInt16 EXC_CHTEXT_AUTOFILL = 0x0080; /// Automatic text background mode (transparent/opaque).
+const sal_uInt16 EXC_CHTEXT_SHOWCATEGPERC = 0x0800; /// Data point caption is category and percent.
+const sal_uInt16 EXC_CHTEXT_SHOWPERCENT = 0x1000; /// Data point caption as percent.
+const sal_uInt16 EXC_CHTEXT_SHOWBUBBLE = 0x2000; /// Show bubble size.
+const sal_uInt16 EXC_CHTEXT_SHOWCATEG = 0x4000; /// Data point caption is category name.
+
+const sal_uInt16 EXC_CHTEXT_POS_DEFAULT = 0;
+const sal_uInt16 EXC_CHTEXT_POS_OUTSIDE = 1;
+const sal_uInt16 EXC_CHTEXT_POS_INSIDE = 2;
+const sal_uInt16 EXC_CHTEXT_POS_CENTER = 3;
+const sal_uInt16 EXC_CHTEXT_POS_AXIS = 4;
+const sal_uInt16 EXC_CHTEXT_POS_ABOVE = 5;
+const sal_uInt16 EXC_CHTEXT_POS_BELOW = 6;
+const sal_uInt16 EXC_CHTEXT_POS_LEFT = 7;
+const sal_uInt16 EXC_CHTEXT_POS_RIGHT = 8;
+const sal_uInt16 EXC_CHTEXT_POS_AUTO = 9;
+const sal_uInt16 EXC_CHTEXT_POS_MOVED = 10;
+
+// (0x1026) CHFONT ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFONT = 0x1026;
+
+// (0x1027) CHOBJECTLINK ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHOBJECTLINK = 0x1027;
+
+// link targets
+const sal_uInt16 EXC_CHOBJLINK_NONE = 0; /// No link target.
+const sal_uInt16 EXC_CHOBJLINK_TITLE = 1; /// Chart title.
+const sal_uInt16 EXC_CHOBJLINK_YAXIS = 2; /// Value axis (Y axis).
+const sal_uInt16 EXC_CHOBJLINK_XAXIS = 3; /// Category axis (X axis).
+const sal_uInt16 EXC_CHOBJLINK_DATA = 4; /// Data series/point.
+const sal_uInt16 EXC_CHOBJLINK_ZAXIS = 7; /// Series axis (Z axis).
+const sal_uInt16 EXC_CHOBJLINK_AXISUNIT = 12; /// Unit name for axis labels.
+
+// (0x1032) CHFRAME -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFRAME = 0x1032;
+
+const sal_uInt16 EXC_CHFRAME_STANDARD = 0;
+const sal_uInt16 EXC_CHFRAME_SHADOW = 4;
+
+const sal_uInt16 EXC_CHFRAME_AUTOSIZE = 0x0001;
+const sal_uInt16 EXC_CHFRAME_AUTOPOS = 0x0002;
+
+// (0x1033, 0x1034) CHBEGIN, CHEND --------------------------------------------
+
+const sal_uInt16 EXC_ID_CHBEGIN = 0x1033;
+const sal_uInt16 EXC_ID_CHEND = 0x1034;
+
+// (0x1035) CHPLOTFRAME -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPLOTFRAME = 0x1035;
+
+// (0x103A) CHCHART3D ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHCHART3D = 0x103A;
+
+const sal_uInt16 EXC_CHCHART3D_REAL3D = 0x0001; /// true = real 3d perspective.
+const sal_uInt16 EXC_CHCHART3D_CLUSTER = 0x0002; /// false = Z axis, true = clustered/stacked.
+const sal_uInt16 EXC_CHCHART3D_AUTOHEIGHT = 0x0004; /// true = automatic height to width ratio.
+const sal_uInt16 EXC_CHCHART3D_HASWALLS = 0x0010; /// true = 3d chart has walls/floor.
+const sal_uInt16 EXC_CHCHART3D_2DWALLS = 0x0020; /// true = 2d wall/gridlines, no floor.
+
+// (0x103C) CHPICFORMAT -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPICFORMAT = 0x103C;
+
+const sal_uInt16 EXC_CHPICFORMAT_NONE = 0; /// For internal use only.
+const sal_uInt16 EXC_CHPICFORMAT_STRETCH = 1; /// Bitmap stretched to area.
+const sal_uInt16 EXC_CHPICFORMAT_STACK = 2; /// Bitmap stacked.
+const sal_uInt16 EXC_CHPICFORMAT_SCALE = 3; /// Bitmap scaled to axis scale.
+
+const sal_uInt16 EXC_CHPICFORMAT_TOPBOTTOM = 0x0200;
+const sal_uInt16 EXC_CHPICFORMAT_FRONTBACK = 0x0400;
+const sal_uInt16 EXC_CHPICFORMAT_LEFTRIGHT = 0x0800;
+
+// (0x103D) CHDROPBAR ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHDROPBAR = 0x103D;
+
+const sal_uInt16 EXC_CHDROPBAR_UP = 0;
+const sal_uInt16 EXC_CHDROPBAR_DOWN = 1;
+const sal_uInt16 EXC_CHDROPBAR_NONE = 0xFFFF;
+
+// (0x103E, 0x1040) CHRADARLINE, CHRADARAREA ----------------------------------
+
+const sal_uInt16 EXC_ID_CHRADARLINE = 0x103E;
+const sal_uInt16 EXC_ID_CHRADARAREA = 0x1040;
+
+const sal_uInt16 EXC_CHRADAR_AXISLABELS = 0x0001;
+const sal_uInt16 EXC_CHRADAR_SHADOW = 0x0002;
+
+// (0x103F) CHSURFACE ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSURFACE = 0x103F;
+
+const sal_uInt16 EXC_CHSURFACE_FILLED = 0x0001;
+const sal_uInt16 EXC_CHSURFACE_SHADING = 0x0002;
+
+// (0x1041) CHAXESSET ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHAXESSET = 0x1041;
+
+const sal_uInt16 EXC_CHAXESSET_PRIMARY = 0;
+const sal_uInt16 EXC_CHAXESSET_SECONDARY = 1;
+const sal_uInt16 EXC_CHAXESSET_NONE = 0xFFFF; /// For internal use.
+
+// (0x1043) LEGENDEXCEPTION
+
+const sal_uInt16 EXC_ID_CHLEGENDEXCEPTION = 0x1043;
+
+const sal_uInt16 EXC_CHLEGENDEXCEPTION_DELETED = 0x0001;
+const sal_uInt16 EXC_CHLEGENDEXCEPTION_LABEL = 0x0002;
+
+// (0x1044) CHPROPERTIES ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPROPERTIES = 0x1044;
+
+const sal_uInt16 EXC_CHPROPS_MANSERIES = 0x0001; /// Manual series allocation.
+const sal_uInt16 EXC_CHPROPS_SHOWVISIBLEONLY = 0x0002; /// Show visible cells only.
+const sal_uInt16 EXC_CHPROPS_NORESIZE = 0x0004; /// Do not resize chart with window.
+const sal_uInt16 EXC_CHPROPS_MANPLOTAREA = 0x0008; /// Manual plot area mode.
+const sal_uInt16 EXC_CHPROPS_USEMANPLOTAREA = 0x0010; /// Manual plot area layout in CHFRAMEPOS record.
+
+const sal_uInt8 EXC_CHPROPS_EMPTY_SKIP = 0; /// Skip empty values.
+const sal_uInt8 EXC_CHPROPS_EMPTY_ZERO = 1; /// Plot empty values as zero.
+const sal_uInt8 EXC_CHPROPS_EMPTY_INTERPOLATE = 2; /// Interpolate empty values.
+
+// (0x1045) CHSERGROUP --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERGROUP = 0x1045;
+
+const sal_uInt16 EXC_CHSERGROUP_NONE = 0xFFFF; /// For internal use: no chart type group.
+
+// (0x1048, 0x0858) CHPIVOTREF ------------------------------------------------
+
+const sal_uInt16 EXC_ID5_CHPIVOTREF = 0x1048;
+const sal_uInt16 EXC_ID8_CHPIVOTREF = 0x0858;
+
+// (0x104A) CHSERPARENT -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERPARENT = 0x104A;
+
+// (0x104B) CHSERTRENDLINE ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERTRENDLINE = 0x104B;
+
+const sal_uInt8 EXC_CHSERTREND_POLYNOMIAL = 0; /// If order is 1, trend line is linear.
+const sal_uInt8 EXC_CHSERTREND_EXPONENTIAL = 1;
+const sal_uInt8 EXC_CHSERTREND_LOGARITHMIC = 2;
+const sal_uInt8 EXC_CHSERTREND_POWER = 3;
+const sal_uInt8 EXC_CHSERTREND_MOVING_AVG = 4;
+
+// (0x104E) CHFORMAT ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFORMAT = 0x104E;
+
+// (0x104F) CHFRAMEPOS --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFRAMEPOS = 0x104F;
+
+const sal_uInt16 EXC_CHFRAMEPOS_POINTS = 0;
+const sal_uInt16 EXC_CHFRAMEPOS_ABSSIZE_POINTS = 1;
+const sal_uInt16 EXC_CHFRAMEPOS_PARENT = 2;
+const sal_uInt16 EXC_CHFRAMEPOS_DEFOFFSET_PLOT = 3;
+const sal_uInt16 EXC_CHFRAMEPOS_CHARTSIZE = 5;
+
+// (0x1050) CHFORMATRUNS ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHFORMATRUNS = 0x1050;
+
+// (0x1051) CHSOURCELINK ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSOURCELINK = 0x1051;
+
+const sal_uInt8 EXC_CHSRCLINK_TITLE = 0;
+const sal_uInt8 EXC_CHSRCLINK_VALUES = 1;
+const sal_uInt8 EXC_CHSRCLINK_CATEGORY = 2;
+const sal_uInt8 EXC_CHSRCLINK_BUBBLES = 3;
+
+const sal_uInt8 EXC_CHSRCLINK_DEFAULT = 0;
+const sal_uInt8 EXC_CHSRCLINK_DIRECTLY = 1;
+const sal_uInt8 EXC_CHSRCLINK_WORKSHEET = 2;
+
+const sal_uInt16 EXC_CHSRCLINK_NUMFMT = 0x0001;
+
+// (0x105B) CHSERERRORBAR -----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERERRORBAR = 0x105B;
+
+const sal_uInt8 EXC_CHSERERR_NONE = 0; /// For internal use only.
+const sal_uInt8 EXC_CHSERERR_XPLUS = 1;
+const sal_uInt8 EXC_CHSERERR_XMINUS = 2;
+const sal_uInt8 EXC_CHSERERR_YPLUS = 3;
+const sal_uInt8 EXC_CHSERERR_YMINUS = 4;
+
+const sal_uInt8 EXC_CHSERERR_PERCENT = 1;
+const sal_uInt8 EXC_CHSERERR_FIXED = 2;
+const sal_uInt8 EXC_CHSERERR_STDDEV = 3;
+const sal_uInt8 EXC_CHSERERR_CUSTOM = 4;
+const sal_uInt8 EXC_CHSERERR_STDERR = 5;
+
+const sal_uInt8 EXC_CHSERERR_END_BLANK = 0; /// Line end: blank.
+const sal_uInt8 EXC_CHSERERR_END_TSHAPE = 1; /// Line end: t-shape.
+
+// (0x105D) CHSERIESFORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHSERIESFORMAT = 0x105D;
+
+const sal_uInt16 EXC_CHSERIESFORMAT_SMOOTHED = 0x0001;
+const sal_uInt16 EXC_CHSERIESFORMAT_BUBBLE3D = 0x0002;
+const sal_uInt16 EXC_CHSERIESFORMAT_SHADOW = 0x0004;
+
+// (0x105F) CH3DDATAFORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CH3DDATAFORMAT = 0x105F;
+
+const sal_uInt8 EXC_CH3DDATAFORMAT_RECT = 0; /// Rectangular base.
+const sal_uInt8 EXC_CH3DDATAFORMAT_CIRC = 1; /// Circular base.
+
+const sal_uInt8 EXC_CH3DDATAFORMAT_STRAIGHT = 0; /// Straight to top.
+const sal_uInt8 EXC_CH3DDATAFORMAT_SHARP = 1; /// Sharp top.
+const sal_uInt8 EXC_CH3DDATAFORMAT_TRUNC = 2; /// Sharp top, truncated.
+
+// (0x1061) CHPIEEXT ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHPIEEXT = 0x1061;
+
+// (0x1062) CHDATERANGE -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHDATERANGE = 0x1062;
+
+const sal_uInt16 EXC_CHDATERANGE_AUTOMIN = 0x0001;
+const sal_uInt16 EXC_CHDATERANGE_AUTOMAX = 0x0002;
+const sal_uInt16 EXC_CHDATERANGE_AUTOMAJOR = 0x0004;
+const sal_uInt16 EXC_CHDATERANGE_AUTOMINOR = 0x0008;
+const sal_uInt16 EXC_CHDATERANGE_DATEAXIS = 0x0010;
+const sal_uInt16 EXC_CHDATERANGE_AUTOBASE = 0x0020;
+const sal_uInt16 EXC_CHDATERANGE_AUTOCROSS = 0x0040; /// Other axis crosses at own maximum.
+const sal_uInt16 EXC_CHDATERANGE_AUTODATE = 0x0080; /// Recognize date/text automatically.
+
+const sal_uInt16 EXC_CHDATERANGE_DAYS = 0;
+const sal_uInt16 EXC_CHDATERANGE_MONTHS = 1;
+const sal_uInt16 EXC_CHDATERANGE_YEARS = 2;
+
+// (0x1066) CHESCHERFORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHESCHERFORMAT = 0x1066;
+
+// Other record IDs -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_CHWRAPPEDRECORD = 0x0851;
+const sal_uInt16 EXC_ID_CHUNITPROPERTIES = 0x0857;
+const sal_uInt16 EXC_ID_CHUSEDAXESSETS = 0x1046;
+const sal_uInt16 EXC_ID_CHLABELRANGE2 = 0x1062;
+const sal_uInt16 EXC_ID_CHPLOTGROWTH = 0x1064;
+const sal_uInt16 EXC_ID_CHSERINDEX = 0x1065;
+const sal_uInt16 EXC_ID_CHUNKNOWN = 0xFFFF;
+
+// Structs and classes
+
+// Common =====================================================================
+
+struct XclChRectangle
+{
+ sal_Int32 mnX; /// X position of the object in 1/4000 of chart width.
+ sal_Int32 mnY; /// Y position of the object in 1/4000 of chart height.
+ sal_Int32 mnWidth; /// Width of the object in 1/4000 of chart width.
+ sal_Int32 mnHeight; /// Height of the object in 1/4000 of chart height.
+
+ explicit XclChRectangle();
+};
+
+/** Specifies the position of a data series or data point. */
+struct XclChDataPointPos
+{
+ sal_uInt16 mnSeriesIdx; /// Series index of series or a data point.
+ sal_uInt16 mnPointIdx; /// Index of a data point inside a series.
+
+ explicit XclChDataPointPos(
+ sal_uInt16 nSeriesIdx = EXC_CHSERIES_INVALID,
+ sal_uInt16 nPointIdx = EXC_CHDATAFORMAT_ALLPOINTS );
+};
+
+bool operator<( const XclChDataPointPos& rL, const XclChDataPointPos& rR );
+
+/** Contains the type and context of a block of future records which are
+ guarded by CHFRBLOCKBEGIN and CHFRBLOCKEND records. */
+struct XclChFrBlock
+{
+ sal_uInt16 mnType; /// Type of the future record block.
+ sal_uInt16 mnContext; /// Context dependent on type.
+ sal_uInt16 mnValue1; /// Optional primary value for current context.
+ sal_uInt16 mnValue2; /// Optional secondary value for current context.
+
+ explicit XclChFrBlock( sal_uInt16 nType );
+};
+
+// Frame formatting ===========================================================
+
+struct XclChFramePos
+{
+ XclChRectangle maRect; /// Object dependent position data.
+ sal_uInt16 mnTLMode; /// Top-left position mode.
+ sal_uInt16 mnBRMode; /// Bottom-right position mode.
+
+ explicit XclChFramePos();
+};
+
+struct XclChLineFormat
+{
+ Color maColor; /// Line color.
+ sal_uInt16 mnPattern; /// Line pattern (solid, dashed, ...).
+ sal_Int16 mnWeight; /// Line weight (hairline, single, ...).
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChLineFormat();
+};
+
+struct XclChAreaFormat
+{
+ Color maPattColor; /// Pattern color.
+ Color maBackColor; /// Pattern background color.
+ sal_uInt16 mnPattern; /// Fill pattern.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChAreaFormat();
+};
+
+class SfxItemSet;
+class EscherPropertyContainer;
+
+struct XclChEscherFormat
+{
+ typedef std::shared_ptr< SfxItemSet > SfxItemSetRef;
+ typedef std::shared_ptr< EscherPropertyContainer > EscherPropSetRef;
+
+ SfxItemSetRef mxItemSet; /// Item set for Escher properties import.
+ EscherPropSetRef mxEscherSet; /// Container for Escher properties export.
+
+ explicit XclChEscherFormat();
+ ~XclChEscherFormat();
+};
+
+struct XclChPicFormat
+{
+ sal_uInt16 mnBmpMode; /// Bitmap mode, e.g. stretched, stacked.
+ sal_uInt16 mnFlags; /// Additional flags.
+ double mfScale; /// Picture scaling (units).
+
+ explicit XclChPicFormat();
+};
+
+struct XclChFrame
+{
+ sal_uInt16 mnFormat; /// Format type of the frame.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChFrame();
+};
+
+// Source links ===============================================================
+
+struct XclChSourceLink
+{
+ sal_uInt8 mnDestType; /// Type of the destination (title, values, ...).
+ sal_uInt8 mnLinkType; /// Link type (directly, linked to worksheet, ...).
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnNumFmtIdx; /// Number format index.
+
+ explicit XclChSourceLink();
+};
+
+// Text =======================================================================
+
+struct XclChObjectLink
+{
+ XclChDataPointPos maPointPos; /// Position of the data point.
+ sal_uInt16 mnTarget; /// Target of the link.
+
+ explicit XclChObjectLink();
+};
+
+struct XclChFrLabelProps
+{
+ OUString maSeparator; /// Separator between label values.
+ sal_uInt16 mnFlags; /// Flags indicating which values to be displayed.
+
+ explicit XclChFrLabelProps();
+};
+
+struct XclChText
+{
+ XclChRectangle maRect; /// Position of the text object.
+ Color maTextColor; /// Text color.
+ sal_uInt8 mnHAlign; /// Horizontal alignment.
+ sal_uInt8 mnVAlign; /// Vertical alignment.
+ sal_uInt16 mnBackMode; /// Background mode: transparent, opaque.
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnFlags2; /// Text object placement and text direction (BIFF8+).
+ sal_uInt16 mnRotation; /// Text object rotation (BIFF8+).
+
+ explicit XclChText();
+};
+
+// Data series ================================================================
+
+struct XclChMarkerFormat
+{
+ Color maLineColor; /// Border line color.
+ Color maFillColor; /// Fill color.
+ sal_uInt32 mnMarkerSize; /// Size of a marker
+ sal_uInt16 mnMarkerType; /// Marker type (none, diamond, ...).
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChMarkerFormat();
+};
+
+struct XclCh3dDataFormat
+{
+ sal_uInt8 mnBase; /// Base form.
+ sal_uInt8 mnTop; /// Top edge mode.
+
+ explicit XclCh3dDataFormat();
+};
+
+struct XclChDataFormat
+{
+ XclChDataPointPos maPointPos; /// Position of the data point or series.
+ sal_uInt16 mnFormatIdx; /// Formatting index for automatic colors.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChDataFormat();
+};
+
+struct XclChSerTrendLine
+{
+ double mfIntercept; /// Forced intercept.
+ double mfForecastFor; /// Counter to forecast forward.
+ double mfForecastBack; /// Counter to forecast backward.
+ sal_uInt8 mnLineType; /// Type of the trend line.
+ sal_uInt8 mnOrder; /// Polynomial order or moving average counter.
+ sal_uInt8 mnShowEquation; /// 1 = Show equation.
+ sal_uInt8 mnShowRSquared; /// 1 = Show R-squared.
+
+ explicit XclChSerTrendLine();
+};
+
+struct XclChSerErrorBar
+{
+ double mfValue; /// Fixed value for several source types.
+ sal_uInt16 mnValueCount; /// Number of custom error values.
+ sal_uInt8 mnBarType; /// Type of the error bar (X/Y).
+ sal_uInt8 mnSourceType; /// Type of source values.
+ sal_uInt8 mnLineEnd; /// Type of the line ends.
+
+ explicit XclChSerErrorBar();
+};
+
+struct XclChSeries
+{
+ sal_uInt16 mnCategType; /// Data type for category entries.
+ sal_uInt16 mnValueType; /// Data type for value entries.
+ sal_uInt16 mnBubbleType; /// Data type for bubble entries.
+ sal_uInt16 mnCategCount; /// Number of category entries.
+ sal_uInt16 mnValueCount; /// Number of value entries.
+ sal_uInt16 mnBubbleCount; /// Number of bubble entries.
+
+ explicit XclChSeries();
+};
+
+// Chart type groups ==========================================================
+
+struct XclChType
+{
+ sal_Int16 mnOverlap; /// Bar overlap width (CHBAR).
+ sal_Int16 mnGap; /// Gap between bars (CHBAR).
+ sal_uInt16 mnRotation; /// Rotation angle of first pie (CHPIE).
+ sal_uInt16 mnPieHole; /// Hole size in donut chart (CHPIE).
+ sal_uInt16 mnBubbleSize; /// Bubble size in bubble chart (CHSCATTER).
+ sal_uInt16 mnBubbleType; /// Bubble type in bubble chart (CHSCATTER).
+ sal_uInt16 mnFlags; /// Additional flags (all chart types).
+
+ explicit XclChType();
+};
+
+struct XclChChart3d
+{
+ sal_uInt16 mnRotation; /// Rotation (0...359deg).
+ sal_Int16 mnElevation; /// Elevation (-90...+90deg).
+ sal_uInt16 mnEyeDist; /// Eye distance to chart (0...100).
+ sal_uInt16 mnRelHeight; /// Height relative to width.
+ sal_uInt16 mnRelDepth; /// Depth relative to width.
+ sal_uInt16 mnDepthGap; /// Space between series.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChChart3d();
+};
+
+struct XclChLegend
+{
+ XclChRectangle maRect; /// Position of the legend.
+ sal_uInt8 mnDockMode; /// Docking mode.
+ sal_uInt8 mnSpacing; /// Spacing between elements.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChLegend();
+};
+
+struct XclChTypeGroup
+{
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnGroupIdx; /// Chart type group index.
+
+ explicit XclChTypeGroup();
+};
+
+struct XclChProperties
+{
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt8 mnEmptyMode; /// Plotting mode for empty cells.
+
+ explicit XclChProperties();
+};
+
+// Axes =======================================================================
+
+struct XclChLabelRange
+{
+ sal_uInt16 mnCross; /// Crossing position of other axis.
+ sal_uInt16 mnLabelFreq; /// Frequency of labels.
+ sal_uInt16 mnTickFreq; /// Frequency of ticks.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChLabelRange();
+};
+
+struct XclChDateRange
+{
+ sal_uInt16 mnMinDate; /// Minimum value on axis.
+ sal_uInt16 mnMaxDate; /// Maximum value on axis.
+ sal_uInt16 mnMajorStep; /// Distance for major grid lines.
+ sal_uInt16 mnMajorUnit; /// Time unit for major step.
+ sal_uInt16 mnMinorStep; /// Distance for minor grid lines.
+ sal_uInt16 mnMinorUnit; /// Time unit for minor step.
+ sal_uInt16 mnBaseUnit; /// Time unit for axis values.
+ sal_uInt16 mnCross; /// Crossing position of other axis.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChDateRange();
+};
+
+struct XclChValueRange
+{
+ double mfMin; /// Minimum value on axis.
+ double mfMax; /// Maximum value on axis.
+ double mfMajorStep; /// Distance for major grid lines.
+ double mfMinorStep; /// Distance for minor grid lines.
+ double mfCross; /// Crossing position of other axis.
+ sal_uInt16 mnFlags; /// Additional flags.
+
+ explicit XclChValueRange();
+};
+
+struct XclChTick
+{
+ Color maTextColor; /// Tick labels color.
+ sal_uInt8 mnMajor; /// Type of tick marks of major grid.
+ sal_uInt8 mnMinor; /// Type of tick marks of minor grid.
+ sal_uInt8 mnLabelPos; /// Position of labels relative to axis.
+ sal_uInt8 mnBackMode; /// Background mode: transparent, opaque.
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnRotation; /// Tick labels angle (BIFF8+).
+
+ explicit XclChTick();
+};
+
+struct XclChAxis
+{
+ sal_uInt16 mnType; /// Axis type.
+
+ explicit XclChAxis();
+
+ /** Returns the axis dimension index used by the chart API. */
+ sal_Int32 GetApiAxisDimension() const;
+};
+
+struct XclChAxesSet
+{
+ XclChRectangle maRect; /// Position of the axes set (inner plot area).
+ sal_uInt16 mnAxesSetId; /// Primary/secondary axes set.
+
+ explicit XclChAxesSet();
+
+ /** Returns the axes set index used by the chart API. */
+ sal_Int32 GetApiAxesSetIndex() const;
+};
+
+// Property mode ==============================================================
+
+/** Specifies the type of a formatting. This results in different property names. */
+enum XclChPropertyMode
+{
+ EXC_CHPROPMODE_COMMON, /// Common objects, no special handling.
+ EXC_CHPROPMODE_LINEARSERIES, /// Specific to data series drawn as lines.
+ EXC_CHPROPMODE_FILLEDSERIES /// Specific to data series drawn as areas.
+};
+
+// Static helper functions ====================================================
+
+/** Contains static helper functions for the chart filters. */
+class XclChartHelper
+{
+public:
+ /** Returns a palette index for automatic series line colors. */
+ static sal_uInt16 GetSeriesLineAutoColorIdx( sal_uInt16 nFormatIdx );
+ /** Returns a palette index for automatic series fill colors. */
+ static sal_uInt16 GetSeriesFillAutoColorIdx( sal_uInt16 nFormatIdx );
+ /** Returns a transparency value for automatic series fill colors. */
+ static sal_uInt8 GetSeriesFillAutoTransp( sal_uInt16 nFormatIdx );
+ /** Returns an automatic symbol index for the passed format index. */
+ static sal_uInt16 GetAutoMarkerType( sal_uInt16 nFormatIdx );
+ /** Returns true, if the passed marker type is filled. */
+ static bool HasMarkerFillColor( sal_uInt16 nMarkerType );
+ /** Returns the role name for a manual data source for error bars. */
+ static OUString GetErrorBarValuesRole( sal_uInt8 nBarType );
+};
+
+// Chart formatting info provider =============================================
+
+/** Enumerates different object types for specific automatic formatting behaviour. */
+enum XclChObjectType
+{
+ EXC_CHOBJTYPE_BACKGROUND, /// Chart background.
+ EXC_CHOBJTYPE_PLOTFRAME, /// Wall formatting in 2d charts.
+ EXC_CHOBJTYPE_WALL3D, /// Wall formatting in 3d charts.
+ EXC_CHOBJTYPE_FLOOR3D, /// Floor formatting in 3d charts.
+ EXC_CHOBJTYPE_TEXT, /// Text boxes (titles, data point labels).
+ EXC_CHOBJTYPE_LEGEND, /// Chart legend.
+ EXC_CHOBJTYPE_LINEARSERIES, /// Series formatting in a chart supporting line formatting only.
+ EXC_CHOBJTYPE_FILLEDSERIES, /// Series formatting in a chart supporting area formatting.
+ EXC_CHOBJTYPE_AXISLINE, /// Axis line format.
+ EXC_CHOBJTYPE_GRIDLINE, /// Axis grid line format.
+ EXC_CHOBJTYPE_TRENDLINE, /// Series trend line.
+ EXC_CHOBJTYPE_ERRORBAR, /// Series error bar.
+ EXC_CHOBJTYPE_CONNECTLINE, /// Data point connector line.
+ EXC_CHOBJTYPE_HILOLINE, /// High/low lines in stock charts.
+ EXC_CHOBJTYPE_WHITEDROPBAR, /// White-day drop bar in stock charts.
+ EXC_CHOBJTYPE_BLACKDROPBAR /// Black-day drop bar in stock charts.
+};
+
+/** Enumerates different types to handle missing frame objects. */
+enum XclChFrameType
+{
+ EXC_CHFRAMETYPE_AUTO, /// Missing frame represents automatic formatting.
+ EXC_CHFRAMETYPE_INVISIBLE /// Missing frame represents invisible formatting.
+};
+
+/** Contains information about auto formatting of a specific chart object type. */
+struct XclChFormatInfo
+{
+ XclChObjectType meObjType; /// Object type for automatic format.
+ XclChPropertyMode mePropMode; /// Property mode for property set helper.
+ sal_uInt16 mnAutoLineColorIdx; /// Automatic line color index.
+ sal_Int16 mnAutoLineWeight; /// Automatic line weight (hairline, single, ...).
+ sal_uInt16 mnAutoPattColorIdx; /// Automatic fill pattern color index.
+ XclChFrameType meDefFrameType; /// Default format type for missing frame objects.
+ bool mbCreateDefFrame; /// true = Create missing frame objects on import.
+ bool mbDeleteDefFrame; /// true = Delete default frame formatting on export.
+ bool mbIsFrame; /// true = Object is a frame, false = Object is a line.
+};
+
+/** Provides access to chart auto formatting for all available object types. */
+class XclChFormatInfoProvider
+{
+public:
+ explicit XclChFormatInfoProvider();
+
+ /** Returns an info struct about auto formatting for the passed object type. */
+ const XclChFormatInfo& GetFormatInfo( XclChObjectType eObjType ) const;
+
+private:
+ typedef ::std::map< XclChObjectType, const XclChFormatInfo* > XclFmtInfoMap;
+ XclFmtInfoMap maInfoMap; /// Maps object type to formatting data.
+};
+
+// Chart type info provider ===================================================
+
+/** Enumerates all kinds of different chart types. */
+enum XclChTypeId
+{
+ EXC_CHTYPEID_BAR, /// Vertical bar chart.
+ EXC_CHTYPEID_HORBAR, /// Horizontal bar chart.
+ EXC_CHTYPEID_LINE, /// Line chart.
+ EXC_CHTYPEID_AREA, /// Area chart.
+ EXC_CHTYPEID_STOCK, /// Stock chart.
+ EXC_CHTYPEID_RADARLINE, /// Linear radar chart.
+ EXC_CHTYPEID_RADARAREA, /// Filled radar chart.
+ EXC_CHTYPEID_PIE, /// Pie chart.
+ EXC_CHTYPEID_DONUT, /// Donut chart.
+ EXC_CHTYPEID_PIEEXT, /// Pie-to-pie or pie-to-bar chart.
+ EXC_CHTYPEID_SCATTER, /// Scatter (XY) chart.
+ EXC_CHTYPEID_BUBBLES, /// Bubble chart.
+ EXC_CHTYPEID_SURFACE, /// Surface chart.
+ EXC_CHTYPEID_UNKNOWN /// Default for unknown chart types.
+};
+
+/** Enumerates different categories of similar chart types. */
+enum XclChTypeCateg
+{
+ EXC_CHTYPECATEG_BAR, /// Bar charts (horizontal or vertical).
+ EXC_CHTYPECATEG_LINE, /// Line charts (line, area, stock charts).
+ EXC_CHTYPECATEG_RADAR, /// Radar charts (linear or filled).
+ EXC_CHTYPECATEG_PIE, /// Pie and donut charts.
+ EXC_CHTYPECATEG_SCATTER, /// Scatter and bubble charts.
+ EXC_CHTYPECATEG_SURFACE /// Surface charts.
+};
+
+/** Enumerates modes for varying point colors in a series. */
+enum XclChVarPointMode
+{
+ EXC_CHVARPOINT_NONE, /// No varied colors supported.
+ EXC_CHVARPOINT_SINGLE, /// Only supported, if type group contains only one series.
+ EXC_CHVARPOINT_MULTI /// Supported for multiple series in a chart type group.
+};
+
+/** Contains information for a chart type. */
+struct XclChTypeInfo
+{
+ XclChTypeId meTypeId; /// Unique chart type identifier.
+ XclChTypeCateg meTypeCateg; /// Chart type category this type belongs to.
+ sal_uInt16 mnRecId; /// Record identifier written to the file.
+ const char* mpcServiceName; /// Service name of the type.
+ XclChVarPointMode meVarPointMode; /// Mode for varying point colors.
+ sal_Int32 mnDefaultLabelPos; /// Default data label position (API constant).
+ bool mbCombinable2d; /// true = Types can be combined in one axes set.
+ bool mbSupports3d; /// true = 3d type allowed, false = Only 2d type.
+ bool mbPolarCoordSystem; /// true = Polar, false = Cartesian.
+ bool mbSeriesIsFrame2d; /// true = Series with area formatting (2d charts).
+ bool mbSeriesIsFrame3d; /// true = Series with area formatting (3d charts).
+ bool mbSingleSeriesVis; /// true = Only first series visible.
+ bool mbCategoryAxis; /// true = X axis contains categories.
+ bool mbSwappedAxesSet; /// true = X and Y axes are swapped.
+ bool mbSupportsStacking; /// true = Series can be stacked on each other.
+ bool mbReverseSeries; /// true = Insert unstacked series in reverse order.
+ bool mbTicksBetweenCateg; /// true = X axis ticks between categories.
+};
+
+/** Extended chart type information and access functions. */
+struct XclChExtTypeInfo : public XclChTypeInfo
+{
+ bool mb3dChart; /// Chart is actually a 3D chart.
+ bool mbSpline; /// Series lines are smoothed.
+
+ explicit XclChExtTypeInfo( const XclChTypeInfo& rTypeInfo );
+
+ void Set( const XclChTypeInfo& rTypeInfo, bool b3dChart, bool bSpline );
+
+ /** Returns true, if this chart type supports area formatting for its series. */
+ bool IsSeriesFrameFormat() const
+ { return mb3dChart ? mbSeriesIsFrame3d : mbSeriesIsFrame2d; }
+ /** Returns the correct object type identifier for series and data points. */
+ XclChObjectType GetSeriesObjectType() const
+ { return IsSeriesFrameFormat() ? EXC_CHOBJTYPE_FILLEDSERIES : EXC_CHOBJTYPE_LINEARSERIES; }
+};
+
+/** Provides access to chart type info structs for all available chart types. */
+class XclChTypeInfoProvider
+{
+public:
+ explicit XclChTypeInfoProvider();
+
+ /** Returns chart type info for a unique chart type identifier. */
+ const XclChTypeInfo& GetTypeInfo( XclChTypeId eType ) const;
+ /** Returns the first fitting chart type info for an Excel chart type record identifier. */
+ const XclChTypeInfo& GetTypeInfoFromRecId( sal_uInt16 nRecId ) const;
+ /** Returns the first fitting chart type info for the passed service name. */
+ const XclChTypeInfo& GetTypeInfoFromService( std::u16string_view rServiceName ) const;
+
+private:
+ typedef ::std::map< XclChTypeId, const XclChTypeInfo* > XclChTypeInfoMap;
+ XclChTypeInfoMap maInfoMap; /// Maps chart types to type info data.
+};
+
+// Chart text and title object helpers ========================================
+
+/** Enumerates different text box types for default text formatting and title
+ positioning. */
+enum XclChTextType
+{
+ EXC_CHTEXTTYPE_TITLE, /// Chart title.
+ EXC_CHTEXTTYPE_LEGEND, /// Chart legend.
+ EXC_CHTEXTTYPE_AXISTITLE, /// Chart axis titles.
+ EXC_CHTEXTTYPE_AXISLABEL, /// Chart axis labels.
+ EXC_CHTEXTTYPE_DATALABEL /// Data point labels.
+};
+
+/** A map key for text and title objects. */
+struct XclChTextKey : public ::std::pair< XclChTextType, ::std::pair< sal_uInt16, sal_uInt16 > >
+{
+ explicit XclChTextKey( XclChTextType eTextType, sal_uInt16 nMainIdx = 0, sal_uInt16 nSubIdx = 0 )
+ { first = eTextType; second.first = nMainIdx; second.second = nSubIdx; }
+};
+
+/** Function prototype receiving a chart document and returning a title shape. */
+typedef css::uno::Reference< css::drawing::XShape >
+ (*XclChGetShapeFunc)( const css::uno::Reference< css::chart::XChartDocument >& );
+
+// Property helpers ===========================================================
+
+class XclChObjectTable
+{
+public:
+ explicit XclChObjectTable( css::uno::Reference< css::lang::XMultiServiceFactory > const & xFactory,
+ const OUString& rServiceName, const OUString& rObjNameBase );
+
+ /** Returns a named formatting object from the chart document. */
+ css::uno::Any GetObject( const OUString& rObjName );
+ /** Inserts a named formatting object into the chart document. */
+ OUString InsertObject( const css::uno::Any& rObj );
+
+private:
+ css::uno::Reference< css::lang::XMultiServiceFactory > mxFactory; /// Factory to create the container.
+ css::uno::Reference< css::container::XNameContainer > mxContainer; /// Container for the objects.
+ OUString maServiceName; /// Service name to create the container.
+ OUString maObjNameBase; /// Base of names for inserted objects.
+ sal_Int32 mnIndex; /// Index to create unique identifiers.
+};
+
+/** Helper class for usage of property sets. */
+class XclChPropSetHelper
+{
+public:
+ explicit XclChPropSetHelper();
+
+ /** Reads all line properties from the passed property set. */
+ void ReadLineProperties(
+ XclChLineFormat& rLineFmt,
+ XclChObjectTable& rDashTable,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode );
+ /** Reads solid area properties from the passed property set.
+ @return true = object contains complex fill properties. */
+ bool ReadAreaProperties(
+ XclChAreaFormat& rAreaFmt,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode );
+ /** Reads gradient or bitmap area properties from the passed property set. */
+ void ReadEscherProperties(
+ XclChEscherFormat& rEscherFmt,
+ XclChPicFormat& rPicFmt,
+ XclChObjectTable& rGradientTable,
+ XclChObjectTable& rHatchTable,
+ XclChObjectTable& rBitmapTable,
+ const ScfPropertySet& rPropSet,
+ XclChPropertyMode ePropMode );
+ /** Reads all marker properties from the passed property set. */
+ static void ReadMarkerProperties(
+ XclChMarkerFormat& rMarkerFmt,
+ const ScfPropertySet& rPropSet,
+ sal_uInt16 nFormatIdx );
+ /** Reads rotation properties from the passed property set. */
+ static sal_uInt16 ReadRotationProperties(
+ const ScfPropertySet& rPropSet,
+ bool bSupportsStacked );
+
+ /** Writes all line properties to the passed property set. */
+ void WriteLineProperties(
+ ScfPropertySet& rPropSet,
+ XclChObjectTable& rDashTable,
+ const XclChLineFormat& rLineFmt,
+ XclChPropertyMode ePropMode );
+ /** Writes solid area properties to the passed property set. */
+ void WriteAreaProperties(
+ ScfPropertySet& rPropSet,
+ const XclChAreaFormat& rAreaFmt,
+ XclChPropertyMode ePropMode );
+ /** Writes gradient or bitmap area properties to the passed property set. */
+ void WriteEscherProperties(
+ ScfPropertySet& rPropSet,
+ XclChObjectTable& rGradientTable,
+ XclChObjectTable& rBitmapTable,
+ const XclChEscherFormat& rEscherFmt,
+ const XclChPicFormat* pPicFmt,
+ sal_uInt32 nDffFillType,
+ XclChPropertyMode ePropMode );
+ /** Writes all marker properties to the passed property set. */
+ static void WriteMarkerProperties(
+ ScfPropertySet& rPropSet,
+ const XclChMarkerFormat& rMarkerFmt );
+ /** Writes rotation properties to the passed property set. */
+ static void WriteRotationProperties(
+ ScfPropertySet& rPropSet,
+ sal_uInt16 nRotation,
+ bool bSupportsStacked );
+
+private:
+ /** Returns a line property set helper according to the passed property mode. */
+ ScfPropSetHelper& GetLineHelper( XclChPropertyMode ePropMode );
+ /** Returns an area property set helper according to the passed property mode. */
+ ScfPropSetHelper& GetAreaHelper( XclChPropertyMode ePropMode );
+ /** Returns a gradient property set helper according to the passed property mode. */
+ ScfPropSetHelper& GetGradientHelper( XclChPropertyMode ePropMode );
+ /** Returns a hatch property set helper according to the passed property mode. */
+ ScfPropSetHelper& GetHatchHelper( XclChPropertyMode ePropMode );
+
+private:
+ ScfPropSetHelper maLineHlpCommon; /// Properties for lines in common objects.
+ ScfPropSetHelper maLineHlpLinear; /// Properties for lines in linear series.
+ ScfPropSetHelper maLineHlpFilled; /// Properties for lines in filled series.
+ ScfPropSetHelper maAreaHlpCommon; /// Properties for areas in common objects.
+ ScfPropSetHelper maAreaHlpFilled; /// Properties for areas in filled series.
+ ScfPropSetHelper maGradHlpCommon; /// Properties for gradients in common objects.
+ ScfPropSetHelper maGradHlpFilled; /// Properties for gradients in filled series.
+ ScfPropSetHelper maHatchHlpCommon; /// Properties for hatches in common objects.
+ ScfPropSetHelper maHatchHlpFilled; /// Properties for hatches in filled series.
+ ScfPropSetHelper maBitmapHlp; /// Properties for bitmaps.
+};
+
+/** Base struct for internal root data structs for import and export. */
+struct XclChRootData
+{
+ typedef std::shared_ptr< XclChTypeInfoProvider > XclChTypeProvRef;
+ typedef std::shared_ptr< XclChFormatInfoProvider > XclChFmtInfoProvRef;
+ typedef std::shared_ptr< XclChObjectTable > XclChObjectTableRef;
+ typedef std::map< XclChTextKey, XclChGetShapeFunc > XclChGetShapeFuncMap;
+
+ css::uno::Reference< css::chart2::XChartDocument >
+ mxChartDoc; /// The chart document.
+ tools::Rectangle maChartRect; /// Position and size of the chart shape.
+ XclChTypeProvRef mxTypeInfoProv; /// Provides info about chart types.
+ XclChFmtInfoProvRef mxFmtInfoProv; /// Provides info about auto formatting.
+ XclChObjectTableRef mxLineDashTable; /// Container for line dash styles.
+ XclChObjectTableRef mxGradientTable; /// Container for gradient fill styles.
+ XclChObjectTableRef mxHatchTable; /// Container for hatch fill styles.
+ XclChObjectTableRef mxBitmapTable; /// Container for bitmap fill styles.
+ XclChGetShapeFuncMap maGetShapeFuncs; /// Maps title shape getter functions.
+ sal_Int32 mnBorderGapX; /// Border gap to chart space in 1/100mm.
+ sal_Int32 mnBorderGapY; /// Border gap to chart space in 1/100mm.
+ double mfUnitSizeX; /// Size of a chart X unit (1/4000 of chart width) in 1/100 mm.
+ double mfUnitSizeY; /// Size of a chart Y unit (1/4000 of chart height) in 1/100 mm.
+
+ explicit XclChRootData();
+ virtual ~XclChRootData();
+
+ /** Starts the API chart document conversion. Must be called once before any API access. */
+ void InitConversion(
+ const XclRoot& rRoot,
+ const css::uno::Reference< css::chart2::XChartDocument >& rxChartDoc,
+ const tools::Rectangle& rChartRect );
+ /** Finishes the API chart document conversion. Must be called once before any API access. */
+ void FinishConversion();
+
+ /** Returns the drawing shape interface of the specified title object. */
+ css::uno::Reference< css::drawing::XShape >
+ GetTitleShape( const XclChTextKey& rTitleKey ) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlconst.hxx b/sc/source/filter/inc/xlconst.hxx
new file mode 100644
index 000000000..2d62c26ba
--- /dev/null
+++ b/sc/source/filter/inc/xlconst.hxx
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+
+// Common =====================================================================
+
+// BIFF versions --------------------------------------------------------------
+
+/** An enumeration for all Excel file format types (BIFF types). */
+enum XclBiff
+{
+ EXC_BIFF2 = 0, /// MS Excel 2.1
+ EXC_BIFF3, /// MS Excel 3.0
+ EXC_BIFF4, /// MS Excel 4.0
+ EXC_BIFF5, /// MS Excel 5.0, MS Excel 7.0 (95)
+ EXC_BIFF8, /// MS Excel 8.0 (97), 9.0 (2000), 10.0 (XP), 11.0 (2003)
+ EXC_BIFF_UNKNOWN /// Unknown BIFF version.
+};
+
+/** An enumeration for all Excel output format types. */
+enum XclOutput
+{
+ EXC_OUTPUT_BINARY, /// MS Excel binary .xls
+ EXC_OUTPUT_XML_2007, /// MS Excel 2007 .xlsx
+};
+
+// Excel sheet dimensions -----------------------------------------------------
+
+const SCCOL EXC_MAXCOL2 = 255;
+const SCROW EXC_MAXROW2 = 16383;
+const SCTAB EXC_MAXTAB2 = 0;
+
+const SCCOL EXC_MAXCOL3 = EXC_MAXCOL2;
+const SCROW EXC_MAXROW3 = EXC_MAXROW2;
+const SCTAB EXC_MAXTAB3 = EXC_MAXTAB2;
+
+const SCCOL EXC_MAXCOL4 = EXC_MAXCOL3;
+const SCROW EXC_MAXROW4 = EXC_MAXROW3;
+const SCTAB EXC_MAXTAB4 = 32767;
+
+const SCCOL EXC_MAXCOL5 = EXC_MAXCOL4;
+const SCROW EXC_MAXROW5 = EXC_MAXROW4;
+const SCTAB EXC_MAXTAB5 = EXC_MAXTAB4;
+
+const SCCOL EXC_MAXCOL8 = EXC_MAXCOL5;
+const SCROW EXC_MAXROW8 = 65535;
+const SCTAB EXC_MAXTAB8 = EXC_MAXTAB5;
+
+const SCCOL EXC_MAXCOL_XML_2007 = 16383;
+const SCROW EXC_MAXROW_XML_2007 = 1048575;
+const SCTAB EXC_MAXTAB_XML_2007 = 1023;
+
+const sal_uInt16 EXC_NOTAB = SAL_MAX_UINT16; /// An invalid Excel sheet index, for common use.
+const SCTAB SCTAB_INVALID = SCTAB_MAX; /// An invalid Calc sheet index, for common use.
+const SCTAB SCTAB_GLOBAL = SCTAB_MAX; /// A Calc sheet index for the workbook globals.
+
+// Storage/stream names -------------------------------------------------------
+
+#define EXC_STORAGE_OLE_LINKED "LNK"
+#define EXC_STORAGE_OLE_EMBEDDED "MBD"
+inline constexpr OUStringLiteral EXC_STORAGE_VBA_PROJECT = u"_VBA_PROJECT_CUR";
+
+inline constexpr OUStringLiteral EXC_STREAM_BOOK = u"Book";
+inline constexpr OUStringLiteral EXC_STREAM_WORKBOOK = u"Workbook";
+inline constexpr OUStringLiteral EXC_STREAM_CTLS = u"Ctls";
+
+// Encoded URLs ---------------------------------------------------------------
+
+const sal_Unicode EXC_URLSTART_ENCODED = '\x01'; /// Encoded URL.
+const sal_Unicode EXC_URLSTART_SELF = '\x02'; /// Reference to own workbook.
+const sal_Unicode EXC_URLSTART_SELFENCODED = '\x03'; /// Encoded self reference.
+const sal_Unicode EXC_URLSTART_OWNDOC = '\x04'; /// Reference to own workbook (BIFF5/BIFF7).
+
+const sal_Unicode EXC_URL_DOSDRIVE = '\x01'; /// DOS drive letter or UNC server name.
+const sal_Unicode EXC_URL_DRIVEROOT = '\x02'; /// Root directory of current drive.
+const sal_Unicode EXC_URL_SUBDIR = '\x03'; /// Directory name delimiter.
+const sal_Unicode EXC_URL_PARENTDIR = '\x04'; /// Parent directory.
+const sal_Unicode EXC_URL_RAW = '\x05'; /// Unencoded URL.
+const sal_Unicode EXC_URL_SHEETNAME = '\x09'; /// Sheet name starts here (BIFF4).
+
+const sal_Unicode EXC_DDE_DELIM = '\x03'; /// DDE application-topic delimiter
+
+// Error codes ----------------------------------------------------------------
+
+const sal_uInt8 EXC_ERR_NULL = 0x00;
+const sal_uInt8 EXC_ERR_DIV0 = 0x07;
+const sal_uInt8 EXC_ERR_VALUE = 0x0F;
+const sal_uInt8 EXC_ERR_REF = 0x17;
+const sal_uInt8 EXC_ERR_NAME = 0x1D;
+const sal_uInt8 EXC_ERR_NUM = 0x24;
+const sal_uInt8 EXC_ERR_NA = 0x2A;
+
+// Cached values list (EXTERNNAME, ptgArray, ...) -----------------------------
+
+const sal_uInt8 EXC_CACHEDVAL_EMPTY = 0x00;
+const sal_uInt8 EXC_CACHEDVAL_DOUBLE = 0x01;
+const sal_uInt8 EXC_CACHEDVAL_STRING = 0x02;
+const sal_uInt8 EXC_CACHEDVAL_BOOL = 0x04;
+const sal_uInt8 EXC_CACHEDVAL_ERROR = 0x10;
+
+// RK values ------------------------------------------------------------------
+
+const sal_Int32 EXC_RK_100FLAG = 0x00000001;
+const sal_Int32 EXC_RK_INTFLAG = 0x00000002;
+const sal_Int32 EXC_RK_VALUEMASK = 0xFFFFFFFC;
+
+const sal_Int32 EXC_RK_DBL = 0x00000000;
+const sal_Int32 EXC_RK_DBL100 = EXC_RK_100FLAG;
+const sal_Int32 EXC_RK_INT = EXC_RK_INTFLAG;
+const sal_Int32 EXC_RK_INT100 = EXC_RK_100FLAG | EXC_RK_INTFLAG;
+
+// Measures -------------------------------------------------------------------
+
+const sal_uInt8 EXC_ORIENT_NONE = 0; /// Text orientation: not rotated.
+const sal_uInt8 EXC_ORIENT_STACKED = 1; /// Text orientation: vertically stacked.
+const sal_uInt8 EXC_ORIENT_90CCW = 2; /// Text orientation: 90 deg counterclockwise.
+const sal_uInt8 EXC_ORIENT_90CW = 3; /// Text orientation: 90 deg clockwise.
+
+const sal_uInt8 EXC_ROT_NONE = 0; /// Text rotation: not rotated.
+const sal_uInt8 EXC_ROT_90CCW = 90; /// Text rotation: 90 deg counterclockwise.
+const sal_uInt8 EXC_ROT_90CW = 180; /// Text rotation: 90 deg clockwise.
+const sal_uInt8 EXC_ROT_STACKED = 255; /// Text rotation: vertically stacked.
+
+// Records (ordered by lowest record ID) ======================================
+
+// (0x0009, 0x0209, 0x0409, 0x0809) BOF ---------------------------------------
+
+const sal_uInt16 EXC_ID2_BOF = 0x0009;
+const sal_uInt16 EXC_ID3_BOF = 0x0209;
+const sal_uInt16 EXC_ID4_BOF = 0x0409;
+const sal_uInt16 EXC_ID5_BOF = 0x0809;
+
+const sal_uInt16 EXC_BOF_BIFF2 = 0x0200;
+const sal_uInt16 EXC_BOF_BIFF3 = 0x0300;
+const sal_uInt16 EXC_BOF_BIFF4 = 0x0400;
+const sal_uInt16 EXC_BOF_BIFF5 = 0x0500;
+const sal_uInt16 EXC_BOF_BIFF8 = 0x0600;
+
+const sal_uInt16 EXC_BOF_GLOBALS = 0x0005; /// BIFF5-BIFF8 workbook globals.
+const sal_uInt16 EXC_BOF_VBMODULE = 0x0006; /// BIFF5-BIFF8 Visual BASIC module.
+const sal_uInt16 EXC_BOF_SHEET = 0x0010; /// Regular worksheet.
+const sal_uInt16 EXC_BOF_CHART = 0x0020; /// Chart sheet.
+const sal_uInt16 EXC_BOF_MACROSHEET = 0x0040; /// Macro sheet.
+const sal_uInt16 EXC_BOF_WORKSPACE = 0x0100; /// Workspace.
+const sal_uInt16 EXC_BOF_UNKNOWN = 0xFFFF; /// Internal use only.
+
+// (0x000A) EOF ---------------------------------------------------------------
+const sal_uInt16 EXC_ID_EOF = 0x000A;
+
+// (0x0012) PROTECT -----------------------------------------------------------
+const sal_uInt16 EXC_ID_PROTECT = 0x0012;
+
+// (0x0013) PASSWORD ----------------------------------------------------------
+const sal_uInt16 EXC_ID_PASSWORD = 0x0013;
+
+// (0x0019) WINDOWPROTECT -----------------------------------------------------
+const sal_uInt16 EXC_ID_WINDOWPROTECT = 0x0019;
+
+// (0x0042) CODEPAGE ----------------------------------------------------------
+const sal_uInt16 EXC_ID_CODEPAGE = 0x0042;
+
+// (0x0081) WSBOOL ------------------------------------------------------------
+const sal_uInt16 EXC_ID_WSBOOL = 0x0081;
+
+const sal_uInt16 EXC_WSBOOL_ROWBELOW = 0x0040;
+const sal_uInt16 EXC_WSBOOL_COLBELOW = 0x0080;
+const sal_uInt16 EXC_WSBOOL_FITTOPAGE = 0x0100;
+
+const sal_uInt16 EXC_WSBOOL_DEFAULTFLAGS = 0x04C1;
+
+// (0x0086) WRITEPROT ---------------------------------------------------------
+const sal_uInt16 EXC_ID_WRITEPROT = 0x0086;
+
+// (0x008C) COUNTRY -----------------------------------------------------------
+const sal_uInt16 EXC_ID_COUNTRY = 0x008C;
+
+// (0x009B) FILTERMODE --------------------------------------------------------
+const sal_uInt16 EXC_ID_FILTERMODE = 0x009B;
+
+// (0x009C) FNGROUPCOUNT ------------------------------------------------------
+const sal_uInt16 EXC_ID_FNGROUPCOUNT = 0x009C;
+
+// (0x009D) AUTOFILTERINFO ----------------------------------------------------
+const sal_uInt16 EXC_ID_AUTOFILTERINFO = 0x009D;
+
+// (0x009E) AUTOFILTER --------------------------------------------------------
+const sal_uInt16 EXC_ID_AUTOFILTER = 0x009E;
+
+// (0x00BF, 0x00C0, 0x00C1) TOOLBARHDR, TOOLBAREND, MMS -----------------------
+const sal_uInt16 EXC_ID_TOOLBARHDR = 0x00BF;
+const sal_uInt16 EXC_ID_TOOLBAREND = 0x00C0;
+const sal_uInt16 EXC_ID_MMS = 0x00C1;
+
+// (0x00E1, 0x00E2) INTERFACEHDR, INTERFACEEND --------------------------------
+const sal_uInt16 EXC_ID_INTERFACEHDR = 0x00E1;
+const sal_uInt16 EXC_ID_INTERFACEEND = 0x00E2;
+
+// (0x0160) USESELFS ----------------------------------------------------------
+const sal_uInt16 EXC_ID_USESELFS = 0x0160;
+
+// (0x0161) DSF ---------------------------------------------------------------
+const sal_uInt16 EXC_ID_DSF = 0x0161;
+
+// (0x01AA,0x01AB) USERSVIEWBEGIN, USERSVIEWEND -------------------------------
+const sal_uInt16 EXC_ID_USERSVIEWBEGIN = 0x01AA;
+const sal_uInt16 EXC_ID_USERSVIEWEND = 0x01AB;
+
+// (0x01BA) CODENAME ----------------------------------------------------------
+const sal_uInt16 EXC_ID_CODENAME = 0x01BA;
+
+// (0x01C0) XL9FILE -----------------------------------------------------------
+const sal_uInt16 EXC_ID_XL9FILE = 0x01C0;
+
+// (0x8xx) Future records -----------------------------------------------------
+
+/** Enumerates different header types of future records. */
+enum XclFutureRecType
+{
+ EXC_FUTUREREC_SIMPLE, /// Record identifier and empty flags field.
+ EXC_FUTUREREC_UNUSEDREF /// Record identifier, empty flags field, unused range address.
+};
+
+const sal_uInt16 EXC_FUTUREREC_EMPTYFLAGS = 0x0000;
+const sal_uInt16 EXC_FUTUREREC_HASREF = 0x0001;
+const sal_uInt16 EXC_FUTUREREC_ALERT = 0x0002;
+
+// Border import/export
+
+const sal_uInt16 EXC_BORDER_THICK = 50;
+const sal_uInt16 EXC_BORDER_MEDIUM = 35;
+const sal_uInt16 EXC_BORDER_THIN = 15;
+const sal_uInt16 EXC_BORDER_HAIR = 1;
+
+// SharedFeatureType enumeration
+const sal_uInt16 EXC_ISFPROTECTION = 0x0002;
+const sal_uInt16 EXC_ISFFEC2 = 0x0003;
+const sal_uInt16 EXC_ISFFACTOID = 0x0004;
+const sal_uInt16 EXC_ISFLIST = 0x0005;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlcontent.hxx b/sc/source/filter/inc/xlcontent.hxx
new file mode 100644
index 000000000..4feb47d84
--- /dev/null
+++ b/sc/source/filter/inc/xlcontent.hxx
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+// Constants ==================================================================
+
+// (0x005B) FILESHARING -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_FILESHARING = 0x005B;
+
+// (0x00E5) MERGEDCELLS -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_MERGEDCELLS = 0x00E5;
+const sal_uInt16 EXC_MERGEDCELLS_MAXCOUNT = 1027;
+
+// (0x002F) FILEPASS ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_FILEPASS = 0x002F;
+
+const sal_uInt16 EXC_FILEPASS_BIFF5 = 0x0000;
+const sal_uInt16 EXC_FILEPASS_BIFF8 = 0x0001;
+
+// (0x00FC, 0x00FF) SST, EXTSST -----------------------------------------------
+
+const sal_uInt16 EXC_ID_SST = 0x00FC;
+const sal_uInt16 EXC_ID_EXTSST = 0x00FF;
+
+// (0x015F) LABELRANGES -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_LABELRANGES = 0x015F;
+
+// (0x01B0) CONDFMT, (0x01B1) CF ----------------------------------------------
+
+const sal_uInt16 EXC_ID_CONDFMT = 0x01B0;
+const sal_uInt16 EXC_ID_CF = 0x01B1;
+
+const sal_uInt8 EXC_CF_TYPE_NONE = 0x00;
+const sal_uInt8 EXC_CF_TYPE_CELL = 0x01;
+const sal_uInt8 EXC_CF_TYPE_FMLA = 0x02;
+
+const sal_uInt8 EXC_CF_CMP_NONE = 0x00;
+const sal_uInt8 EXC_CF_CMP_BETWEEN = 0x01;
+const sal_uInt8 EXC_CF_CMP_NOT_BETWEEN = 0x02;
+const sal_uInt8 EXC_CF_CMP_EQUAL = 0x03;
+const sal_uInt8 EXC_CF_CMP_NOT_EQUAL = 0x04;
+const sal_uInt8 EXC_CF_CMP_GREATER = 0x05;
+const sal_uInt8 EXC_CF_CMP_LESS = 0x06;
+const sal_uInt8 EXC_CF_CMP_GREATER_EQUAL = 0x07;
+const sal_uInt8 EXC_CF_CMP_LESS_EQUAL = 0x08;
+
+const sal_uInt32 EXC_CF_BORDER_LEFT = 0x00000400; /// Left border line modified?
+const sal_uInt32 EXC_CF_BORDER_RIGHT = 0x00000800; /// Right border line modified?
+const sal_uInt32 EXC_CF_BORDER_TOP = 0x00001000; /// Top border line modified?
+const sal_uInt32 EXC_CF_BORDER_BOTTOM = 0x00002000; /// Bottom border line modified?
+const sal_uInt32 EXC_CF_BORDER_ALL = 0x00003C00; /// Any border line modified?
+const sal_uInt32 EXC_CF_AREA_PATTERN = 0x00010000; /// Pattern modified?
+const sal_uInt32 EXC_CF_AREA_FGCOLOR = 0x00020000; /// Foreground color modified?
+const sal_uInt32 EXC_CF_AREA_BGCOLOR = 0x00040000; /// Background color modified?
+const sal_uInt32 EXC_CF_AREA_ALL = 0x00070000; /// Any area attribute modified?
+const sal_uInt32 EXC_CF_ALLDEFAULT = 0x003FFFFF; /// Default flags.
+const sal_uInt32 EXC_CF_BLOCK_NUMFMT = 0x02000000; /// Font block present?
+const sal_uInt32 EXC_CF_BLOCK_FONT = 0x04000000; /// Font block present?
+const sal_uInt32 EXC_CF_BLOCK_ALIGNMENT = 0x08000000; /// Alignment block present?
+const sal_uInt32 EXC_CF_BLOCK_BORDER = 0x10000000; /// Border block present?
+const sal_uInt32 EXC_CF_BLOCK_AREA = 0x20000000; /// Pattern block present?
+const sal_uInt32 EXC_CF_BLOCK_PROTECTION = 0x40000000; /// Protection block present?
+const sal_uInt32 EXC_CF_IFMT_USER = 0x1; /// NumberFormat String or Id?
+
+const sal_uInt32 EXC_CF_FONT_STYLE = 0x00000002; /// Font posture or weight modified?
+const sal_uInt32 EXC_CF_FONT_STRIKEOUT = 0x00000080; /// Font cancellation modified?
+const sal_uInt32 EXC_CF_FONT_ALLDEFAULT = 0x0000009A; /// Default flags.
+
+const sal_uInt32 EXC_CF_FONT_UNDERL = 0x00000001; /// Font underline type modified?
+const sal_uInt32 EXC_CF_FONT_ESCAPEM = 0x00000001; /// Font escapement type modified?
+
+// (0x01B2) DVAL --------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_DVAL = 0x01B2;
+const sal_uInt32 EXC_DVAL_NOOBJ = 0xFFFFFFFF;
+
+// (0x01BE) DV ----------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_DV = 0x01BE;
+
+// data validation flags
+const sal_uInt32 EXC_DV_STRINGLIST = 0x00000080;
+const sal_uInt32 EXC_DV_IGNOREBLANK = 0x00000100;
+const sal_uInt32 EXC_DV_SUPPRESSDROPDOWN = 0x00000200;
+const sal_uInt32 EXC_DV_SHOWPROMPT = 0x00040000;
+const sal_uInt32 EXC_DV_SHOWERROR = 0x00080000;
+
+// data validation data mode
+const sal_uInt32 EXC_DV_MODE_MASK = 0x0000000F;
+const sal_uInt32 EXC_DV_MODE_ANY = 0x00000000;
+const sal_uInt32 EXC_DV_MODE_WHOLE = 0x00000001;
+const sal_uInt32 EXC_DV_MODE_DECIMAL = 0x00000002;
+const sal_uInt32 EXC_DV_MODE_LIST = 0x00000003;
+const sal_uInt32 EXC_DV_MODE_DATE = 0x00000004;
+const sal_uInt32 EXC_DV_MODE_TIME = 0x00000005;
+const sal_uInt32 EXC_DV_MODE_TEXTLEN = 0x00000006;
+const sal_uInt32 EXC_DV_MODE_CUSTOM = 0x00000007;
+
+// data validation conditions
+const sal_uInt32 EXC_DV_COND_MASK = 0x00F00000;
+const sal_uInt32 EXC_DV_COND_BETWEEN = 0x00000000;
+const sal_uInt32 EXC_DV_COND_NOTBETWEEN = 0x00100000;
+const sal_uInt32 EXC_DV_COND_EQUAL = 0x00200000;
+const sal_uInt32 EXC_DV_COND_NOTEQUAL = 0x00300000;
+const sal_uInt32 EXC_DV_COND_GREATER = 0x00400000;
+const sal_uInt32 EXC_DV_COND_LESS = 0x00500000;
+const sal_uInt32 EXC_DV_COND_EQGREATER = 0x00600000;
+const sal_uInt32 EXC_DV_COND_EQLESS = 0x00700000;
+
+// data validation error style
+const sal_uInt32 EXC_DV_ERROR_MASK = 0x00000070;
+const sal_uInt32 EXC_DV_ERROR_STOP = 0x00000000;
+const sal_uInt32 EXC_DV_ERROR_WARNING = 0x00000010;
+const sal_uInt32 EXC_DV_ERROR_INFO = 0x00000020;
+
+// (0x01B8) HLINK -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_HLINK = 0x01B8;
+
+const sal_uInt32 EXC_HLINK_BODY = 0x00000001; /// Contains file link or URL.
+const sal_uInt32 EXC_HLINK_ABS = 0x00000002; /// Absolute path.
+const sal_uInt32 EXC_HLINK_DESCR = 0x00000014; /// Description.
+const sal_uInt32 EXC_HLINK_MARK = 0x00000008; /// Text mark.
+const sal_uInt32 EXC_HLINK_FRAME = 0x00000080; /// Target frame.
+const sal_uInt32 EXC_HLINK_UNC = 0x00000100; /// UNC path.
+
+// web queries ================================================================
+
+inline constexpr OUStringLiteral EXC_WEBQRY_FILTER = u"calc_HTML_WebQuery";
+
+// (0x00CD) WQSTRING
+const sal_uInt16 EXC_ID_WQSTRING = 0x00CD;
+
+// (0x00DC) PARAMQRY
+const sal_uInt16 EXC_ID_PQRY = 0x00DC;
+const sal_uInt16 EXC_PQRYTYPE_ODBC = 1; /// Source type: ODBC.
+const sal_uInt16 EXC_PQRYTYPE_WEBQUERY = 4; /// Source type: webquery.
+const sal_uInt16 EXC_PQRY_ODBC = 0x0008; /// ODBC connection.
+const sal_uInt16 EXC_PQRY_WEBQUERY = 0x0040; /// Web query.
+const sal_uInt16 EXC_PQRY_TABLES = 0x0100; /// All tables.
+
+// (0x01AD) QSI
+const sal_uInt16 EXC_ID_QSI = 0x01AD;
+const sal_uInt16 EXC_QSI_DEFAULTFLAGS = 0x0349; /// Flags for export.
+
+// (0x0802) unknown record
+const sal_uInt16 EXC_ID_0802 = 0x0802;
+
+// (0x0803) WEBQRYSETTINGS
+const sal_uInt16 EXC_ID_WQSETT = 0x0803;
+const sal_uInt16 EXC_WQSETT_ALL = 0x0000; /// All tables or entire document.
+const sal_uInt16 EXC_WQSETT_SPECTABLES = 0x0002; /// Specific tables.
+const sal_uInt16 EXC_WQSETT_DEFAULTFLAGS = 0x0023; /// Flags for export.
+const sal_uInt16 EXC_WQSETT_NOFORMAT = 0x0001;
+const sal_uInt16 EXC_WQSETT_FORMATRTF = 0x0002;
+const sal_uInt16 EXC_WQSETT_FORMATFULL = 0x0003;
+
+// (0x0804) WEBQRYTABLES
+const sal_uInt16 EXC_ID_WQTABLES = 0x0804;
+
+const sal_uInt16 EXC_ID_EXTLST = 0x9988; /// it is just a random number
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlescher.hxx b/sc/source/filter/inc/xlescher.hxx
new file mode 100644
index 000000000..913f66759
--- /dev/null
+++ b/sc/source/filter/inc/xlescher.hxx
@@ -0,0 +1,434 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+#include <tools/mapunit.hxx>
+#include "xladdress.hxx"
+#include "xlstyle.hxx"
+#include "xistream.hxx"
+
+namespace com::sun::star {
+ namespace drawing { class XShape; }
+ namespace awt { class XControlModel; }
+ namespace script { struct ScriptEventDescriptor; }
+}
+
+// Constants and Enumerations =================================================
+
+// (0x001C) NOTE --------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_NOTE = 0x001C;
+const sal_uInt16 EXC_NOTE_VISIBLE = 0x0002;
+const sal_uInt16 EXC_NOTE5_MAXLEN = 2048;
+
+// (0x005D) OBJ ---------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_OBJ = 0x005D;
+
+const sal_uInt16 EXC_OBJ_INVALID_ID = 0;
+
+// object types
+const sal_uInt16 EXC_OBJTYPE_GROUP = 0;
+const sal_uInt16 EXC_OBJTYPE_LINE = 1;
+const sal_uInt16 EXC_OBJTYPE_RECTANGLE = 2;
+const sal_uInt16 EXC_OBJTYPE_OVAL = 3;
+const sal_uInt16 EXC_OBJTYPE_ARC = 4;
+const sal_uInt16 EXC_OBJTYPE_CHART = 5;
+const sal_uInt16 EXC_OBJTYPE_TEXT = 6;
+const sal_uInt16 EXC_OBJTYPE_BUTTON = 7;
+const sal_uInt16 EXC_OBJTYPE_PICTURE = 8;
+const sal_uInt16 EXC_OBJTYPE_POLYGON = 9; // new in BIFF4
+const sal_uInt16 EXC_OBJTYPE_CHECKBOX = 11; // new in BIFF5
+const sal_uInt16 EXC_OBJTYPE_OPTIONBUTTON = 12;
+const sal_uInt16 EXC_OBJTYPE_EDIT = 13;
+const sal_uInt16 EXC_OBJTYPE_LABEL = 14;
+const sal_uInt16 EXC_OBJTYPE_DIALOG = 15;
+const sal_uInt16 EXC_OBJTYPE_SPIN = 16;
+const sal_uInt16 EXC_OBJTYPE_SCROLLBAR = 17;
+const sal_uInt16 EXC_OBJTYPE_LISTBOX = 18;
+const sal_uInt16 EXC_OBJTYPE_GROUPBOX = 19;
+const sal_uInt16 EXC_OBJTYPE_DROPDOWN = 20;
+const sal_uInt16 EXC_OBJTYPE_NOTE = 25; // new in BIFF8
+const sal_uInt16 EXC_OBJTYPE_DRAWING = 30;
+const sal_uInt16 EXC_OBJTYPE_UNKNOWN = 0xFFFF; /// For internal use only.
+
+// BIFF3-BIFF5 flags
+const sal_uInt16 EXC_OBJ_HIDDEN = 0x0100;
+const sal_uInt16 EXC_OBJ_VISIBLE = 0x0200;
+const sal_uInt16 EXC_OBJ_PRINTABLE = 0x0400;
+
+// BIFF5 line formatting
+const sal_uInt8 EXC_OBJ_LINE_AUTOCOLOR = 64;
+
+const sal_uInt8 EXC_OBJ_LINE_SOLID = 0;
+const sal_uInt8 EXC_OBJ_LINE_DASH = 1;
+const sal_uInt8 EXC_OBJ_LINE_DOT = 2;
+const sal_uInt8 EXC_OBJ_LINE_DASHDOT = 3;
+const sal_uInt8 EXC_OBJ_LINE_DASHDOTDOT = 4;
+const sal_uInt8 EXC_OBJ_LINE_MEDTRANS = 5;
+const sal_uInt8 EXC_OBJ_LINE_DARKTRANS = 6;
+const sal_uInt8 EXC_OBJ_LINE_LIGHTTRANS = 7;
+const sal_uInt8 EXC_OBJ_LINE_NONE = 255;
+
+const sal_uInt8 EXC_OBJ_LINE_HAIR = 0;
+const sal_uInt8 EXC_OBJ_LINE_THIN = 1;
+const sal_uInt8 EXC_OBJ_LINE_MEDIUM = 2;
+const sal_uInt8 EXC_OBJ_LINE_THICK = 3;
+
+const sal_uInt8 EXC_OBJ_LINE_AUTO = 0x01;
+
+const sal_uInt8 EXC_OBJ_ARROW_NONE = 0;
+const sal_uInt8 EXC_OBJ_ARROW_OPEN = 1;
+const sal_uInt8 EXC_OBJ_ARROW_FILLED = 2;
+const sal_uInt8 EXC_OBJ_ARROW_OPENBOTH = 3;
+const sal_uInt8 EXC_OBJ_ARROW_FILLEDBOTH = 4;
+
+const sal_uInt8 EXC_OBJ_ARROW_NARROW = 0;
+const sal_uInt8 EXC_OBJ_ARROW_MEDIUM = 1;
+const sal_uInt8 EXC_OBJ_ARROW_WIDE = 2;
+
+const sal_uInt8 EXC_OBJ_LINE_TL = 0;
+const sal_uInt8 EXC_OBJ_LINE_TR = 1;
+const sal_uInt8 EXC_OBJ_LINE_BR = 2;
+const sal_uInt8 EXC_OBJ_LINE_BL = 3;
+
+// BIFF5 fill formatting
+const sal_uInt8 EXC_OBJ_FILL_AUTOCOLOR = 65;
+
+const sal_uInt8 EXC_OBJ_FILL_AUTO = 0x01;
+
+// BIFF5 frame formatting
+const sal_uInt16 EXC_OBJ_FRAME_SHADOW = 0x0002;
+
+// BIFF5 text objects
+const sal_uInt8 EXC_OBJ_HOR_LEFT = 1;
+const sal_uInt8 EXC_OBJ_HOR_CENTER = 2;
+const sal_uInt8 EXC_OBJ_HOR_RIGHT = 3;
+const sal_uInt8 EXC_OBJ_HOR_JUSTIFY = 4;
+
+const sal_uInt8 EXC_OBJ_VER_TOP = 1;
+const sal_uInt8 EXC_OBJ_VER_CENTER = 2;
+const sal_uInt8 EXC_OBJ_VER_BOTTOM = 3;
+const sal_uInt8 EXC_OBJ_VER_JUSTIFY = 4;
+
+const sal_uInt16 EXC_OBJ_ORIENT_NONE = 0;
+const sal_uInt16 EXC_OBJ_ORIENT_STACKED = 1; /// Stacked top to bottom.
+const sal_uInt16 EXC_OBJ_ORIENT_90CCW = 2; /// 90 degr. counterclockwise.
+const sal_uInt16 EXC_OBJ_ORIENT_90CW = 3; /// 90 degr. clockwise.
+
+const sal_uInt16 EXC_OBJ_TEXT_AUTOSIZE = 0x0080;
+const sal_uInt16 EXC_OBJ_TEXT_LOCKED = 0x0200;
+
+const sal_Int32 EXC_OBJ_TEXT_MARGIN = 20000; /// Automatic text margin (EMUs).
+
+// BIFF5 arc objects
+const sal_uInt8 EXC_OBJ_ARC_TR = 0;
+const sal_uInt8 EXC_OBJ_ARC_TL = 1;
+const sal_uInt8 EXC_OBJ_ARC_BL = 2;
+const sal_uInt8 EXC_OBJ_ARC_BR = 3;
+
+// BIFF5 polygon objects
+const sal_uInt16 EXC_OBJ_POLY_CLOSED = 0x0100;
+
+// BIFF5 pictures/OLE objects
+const sal_uInt16 EXC_OBJ_PIC_MANUALSIZE = 0x0001;
+const sal_uInt16 EXC_OBJ_PIC_DDE = 0x0002;
+const sal_uInt16 EXC_OBJ_PIC_SYMBOL = 0x0008;
+const sal_uInt16 EXC_OBJ_PIC_CONTROL = 0x0010; /// Form control (BIFF8).
+const sal_uInt16 EXC_OBJ_PIC_CTLSSTREAM = 0x0020; /// Data in Ctls stream (BIFF8).
+const sal_uInt16 EXC_OBJ_PIC_AUTOLOAD = 0x0200; /// Auto-load form control (BIFF8).
+
+// BIFF5 button objects
+const sal_uInt16 EXC_OBJ_BUTTON_DEFAULT = 0x0001;
+const sal_uInt16 EXC_OBJ_BUTTON_HELP = 0x0002;
+const sal_uInt16 EXC_OBJ_BUTTON_CANCEL = 0x0004;
+const sal_uInt16 EXC_OBJ_BUTTON_CLOSE = 0x0008;
+
+// BIFF5 checkboxes, radio buttons
+const sal_uInt16 EXC_OBJ_CHECKBOX_UNCHECKED = 0;
+const sal_uInt16 EXC_OBJ_CHECKBOX_CHECKED = 1;
+const sal_uInt16 EXC_OBJ_CHECKBOX_TRISTATE = 2;
+const sal_uInt16 EXC_OBJ_CHECKBOX_FLAT = 0x0001;
+
+// BIFF5 editbox objects
+const sal_uInt16 EXC_OBJ_EDIT_TEXT = 0;
+const sal_uInt16 EXC_OBJ_EDIT_INTEGER = 1;
+const sal_uInt16 EXC_OBJ_EDIT_DOUBLE = 2;
+const sal_uInt16 EXC_OBJ_EDIT_REFERENCE = 3;
+const sal_uInt16 EXC_OBJ_EDIT_FORMULA = 4;
+
+// BIFF5 scrollbars/spinbuttons
+const sal_uInt16 EXC_OBJ_SCROLLBAR_MIN = 0;
+const sal_uInt16 EXC_OBJ_SCROLLBAR_MAX = 30000;
+
+const sal_uInt16 EXC_OBJ_SCROLLBAR_HOR = 0x0001;
+
+const sal_uInt16 EXC_OBJ_SCROLLBAR_DEFFLAGS = 0x0001;
+const sal_uInt16 EXC_OBJ_SCROLLBAR_FLAT = 0x0008;
+
+// BIFF5 listboxes/dropdowns
+const sal_uInt8 EXC_OBJ_LISTBOX_SINGLE = 0; /// Single selection.
+const sal_uInt8 EXC_OBJ_LISTBOX_MULTI = 1; /// Multi selection.
+const sal_uInt8 EXC_OBJ_LISTBOX_RANGE = 2; /// Range selection.
+
+const sal_uInt16 EXC_OBJ_LISTBOX_EDIT = 0x0002;
+const sal_uInt16 EXC_OBJ_LISTBOX_FLAT = 0x0008;
+
+// BIFF5 dropdown listboxes
+const sal_uInt16 EXC_OBJ_DROPDOWN_LISTBOX = 0; /// Listbox, text not editable.
+const sal_uInt16 EXC_OBJ_DROPDOWN_COMBOBOX = 1; /// Dropdown listbox with editable text.
+const sal_uInt16 EXC_OBJ_DROPDOWN_SIMPLE = 2; /// Dropdown button only, no text area.
+const sal_uInt16 EXC_OBJ_DROPDOWN_MAX = 3;
+const sal_uInt16 EXC_OBJ_DROPDOWN_FILTERED = 0x0008; /// Dropdown style: filtered.
+
+// BIFF5 groupboxes
+const sal_uInt16 EXC_OBJ_GROUPBOX_FLAT = 0x0001;
+
+// BIFF8 sub records
+const sal_uInt16 EXC_ID_OBJEND = 0x0000; /// End of OBJ.
+const sal_uInt16 EXC_ID_OBJMACRO = 0x0004; /// Macro link.
+const sal_uInt16 EXC_ID_OBJBUTTON = 0x0005; /// Button data.
+const sal_uInt16 EXC_ID_OBJGMO = 0x0006; /// Group marker.
+const sal_uInt16 EXC_ID_OBJCF = 0x0007; /// Clipboard format.
+const sal_uInt16 EXC_ID_OBJFLAGS = 0x0008; /// Option flags.
+const sal_uInt16 EXC_ID_OBJPICTFMLA = 0x0009; /// OLE link formula.
+const sal_uInt16 EXC_ID_OBJCBLS = 0x000A; /// Check box/radio button data.
+const sal_uInt16 EXC_ID_OBJRBO = 0x000B; /// Radio button group data.
+const sal_uInt16 EXC_ID_OBJSBS = 0x000C; /// Scroll bar data.
+const sal_uInt16 EXC_ID_OBJNTS = 0x000D; /// Note data.
+const sal_uInt16 EXC_ID_OBJSBSFMLA = 0x000E; /// Scroll bar/list box/combo box cell link.
+const sal_uInt16 EXC_ID_OBJGBODATA = 0x000F; /// Group box data.
+const sal_uInt16 EXC_ID_OBJEDODATA = 0x0010; /// Edit box data.
+const sal_uInt16 EXC_ID_OBJRBODATA = 0x0011; /// Radio button group data.
+const sal_uInt16 EXC_ID_OBJCBLSDATA = 0x0012; /// Check box/radio button data.
+const sal_uInt16 EXC_ID_OBJLBSDATA = 0x0013; /// List box/combo box data.
+const sal_uInt16 EXC_ID_OBJCBLSFMLA = 0x0014; /// Check box/radio button cell link.
+const sal_uInt16 EXC_ID_OBJCMO = 0x0015; /// Common object settings.
+const sal_uInt16 EXC_ID_OBJUNKNOWN = 0xFFFF; /// For internal use only.
+
+// BIFF8 OBJCMO: flags
+const sal_uInt16 EXC_OBJCMO_PRINTABLE = 0x0010; /// Object printable.
+const sal_uInt16 EXC_OBJCMO_AUTOLINE = 0x2000; /// Automatic line formatting.
+const sal_uInt16 EXC_OBJCMO_AUTOFILL = 0x4000; /// Automatic fill formatting.
+
+/** Value binding mode for cells linked to form controls. */
+enum XclCtrlBindMode
+{
+ EXC_CTRL_BINDCONTENT, /// Binds cell to content of control.
+ EXC_CTRL_BINDPOSITION /// Binds cell to position in control (e.g. listbox selection index).
+};
+
+// (0x007F) IMGDATA -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID3_IMGDATA = 0x007F;
+const sal_uInt16 EXC_ID8_IMGDATA = 0x00E9;
+
+const sal_uInt16 EXC_IMGDATA_WMF = 2;
+const sal_uInt16 EXC_IMGDATA_BMP = 9;
+
+const sal_uInt16 EXC_IMGDATA_WIN = 1;
+const sal_uInt16 EXC_IMGDATA_MAC = 2;
+
+const sal_uInt32 EXC_IMGDATA_MAXREC8 = 0x201C;
+const sal_uInt32 EXC_IMGDATA_MAXCONT8 = 0x2014;
+
+// (0x00A9) COORDLIST ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_COORDLIST = 0x00A9;
+
+// (0x00EB) MSODRAWINGGROUP ---------------------------------------------------
+
+const sal_uInt16 EXC_ID_MSODRAWINGGROUP = 0x00EB;
+
+// (0x00EC) MSODRAWING --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_MSODRAWING = 0x00EC;
+
+// additional flags not extant in svx headers
+const sal_uInt16 EXC_ESC_ANCHOR_POSLOCKED = 0x0001;
+const sal_uInt16 EXC_ESC_ANCHOR_SIZELOCKED = 0x0002;
+const sal_uInt16 EXC_ESC_ANCHOR_LOCKED = EXC_ESC_ANCHOR_POSLOCKED|EXC_ESC_ANCHOR_SIZELOCKED;
+
+// (0x00ED) MSODRAWINGSELECTION -----------------------------------------------
+
+const sal_uInt16 EXC_ID_MSODRAWINGSEL = 0x00ED;
+
+// (0x01B6) TXO ---------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_TXO = 0x01B6;
+
+// TXO constants are equal to BIFF5 OBJ text object flags
+
+// Structs and classes ========================================================
+
+/** Represents the position (anchor) of an object in a Calc document. */
+struct XclObjAnchor : public XclRange
+{
+ sal_uInt16 mnLX; /// X offset in left column (1/1024 of column width).
+ sal_uInt32 mnTY; /// Y offset in top row (1/256 of row height).
+ sal_uInt16 mnRX; /// X offset in right column (1/1024 of column width).
+ sal_uInt32 mnBY; /// Y offset in bottom row (1/256 of row height).
+
+ explicit XclObjAnchor();
+
+ /** Calculates a rectangle from the contained coordinates. */
+ tools::Rectangle GetRect( const XclRoot& rRoot, SCTAB nScTab, MapUnit eMapUnit ) const;
+ /** Initializes the anchor coordinates for a sheet. */
+ void SetRect( const XclRoot& rRoot, SCTAB nScTab, const tools::Rectangle& rRect, MapUnit eMapUnit );
+
+ /** Initializes the anchor coordinates for an embedded draw page. */
+ void SetRect( const Size& rPageSize, sal_Int32 nScaleX, sal_Int32 nScaleY,
+ const tools::Rectangle& rRect, MapUnit eMapUnit );
+};
+
+inline SvStream& operator>>( SvStream& rStrm, XclObjAnchor& rAnchor )
+{
+ sal_uInt16 tmpFirstRow, tmpTY, tmpLastRow, tmpBY;
+
+ rStrm
+ .ReadUInt16( rAnchor.maFirst.mnCol ).ReadUInt16( rAnchor.mnLX )
+ .ReadUInt16( tmpFirstRow ).ReadUInt16( tmpTY )
+ .ReadUInt16( rAnchor.maLast.mnCol ).ReadUInt16( rAnchor.mnRX )
+ .ReadUInt16( tmpLastRow ).ReadUInt16( tmpBY );
+
+ rAnchor.maFirst.mnRow = static_cast<sal_uInt32> (tmpFirstRow);
+ rAnchor.mnTY = static_cast<sal_uInt32> (tmpTY);
+ rAnchor.maLast.mnRow = static_cast<sal_uInt32> (tmpLastRow);
+ rAnchor.mnBY = static_cast<sal_uInt32> (tmpBY);
+
+ return rStrm;
+}
+
+inline XclImpStream& operator>>( XclImpStream& rStrm, XclObjAnchor& rAnchor )
+{
+ sal_uInt16 tmpFirstRow, tmpTY, tmpLastRow, tmpBY;
+
+ rAnchor.maFirst.mnCol = rStrm.ReaduInt16();
+ rAnchor.mnLX = rStrm.ReaduInt16();
+ tmpFirstRow = rStrm.ReaduInt16();
+ tmpTY = rStrm.ReaduInt16();
+ rAnchor.maLast.mnCol = rStrm.ReaduInt16();
+ rAnchor.mnRX = rStrm.ReaduInt16();
+ tmpLastRow = rStrm.ReaduInt16();
+ tmpBY = rStrm.ReaduInt16();
+
+ rAnchor.maFirst.mnRow = static_cast<sal_uInt32> (tmpFirstRow);
+ rAnchor.mnTY = static_cast<sal_uInt32> (tmpTY);
+ rAnchor.maLast.mnRow = static_cast<sal_uInt32> (tmpLastRow);
+ rAnchor.mnBY = static_cast<sal_uInt32> (tmpBY);
+
+ return rStrm;
+}
+
+inline SvStream& WriteXclObjAnchor( SvStream& rStrm, const XclObjAnchor& rAnchor )
+{
+ return rStrm
+ .WriteUInt16( rAnchor.maFirst.mnCol ).WriteUInt16( rAnchor.mnLX )
+ .WriteUInt16( rAnchor.maFirst.mnRow ).WriteUInt16( rAnchor.mnTY )
+ .WriteUInt16( rAnchor.maLast.mnCol ).WriteUInt16( rAnchor.mnRX )
+ .WriteUInt16( rAnchor.maLast.mnRow).WriteUInt16(rAnchor.mnBY);
+}
+
+struct XclObjLineData
+{
+ sal_uInt8 mnColorIdx;
+ sal_uInt8 mnStyle;
+ sal_uInt8 mnWidth;
+ sal_uInt8 mnAuto;
+
+ explicit XclObjLineData();
+
+ bool IsAuto() const { return ::get_flag( mnAuto, EXC_OBJ_LINE_AUTO ); }
+ bool IsVisible() const { return IsAuto() || (mnStyle != EXC_OBJ_LINE_NONE); }
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjLineData& rLineData );
+
+struct XclObjFillData
+{
+ sal_uInt8 mnBackColorIdx;
+ sal_uInt8 mnPattColorIdx;
+ sal_uInt8 mnPattern;
+ sal_uInt8 mnAuto;
+
+ explicit XclObjFillData();
+
+ bool IsAuto() const { return ::get_flag( mnAuto, EXC_OBJ_FILL_AUTO ); }
+ bool IsFilled() const { return IsAuto() || (mnPattern != EXC_PATT_NONE); }
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclObjFillData& rFillData );
+
+struct XclObjTextData
+{
+ sal_uInt16 mnTextLen;
+ sal_uInt16 mnFormatSize;
+ sal_uInt16 mnLinkSize;
+ sal_uInt16 mnDefFontIdx;
+ sal_uInt16 mnFlags;
+ sal_uInt16 mnOrient;
+ sal_uInt16 mnButtonFlags;
+ sal_uInt16 mnShortcut;
+ sal_uInt16 mnShortcutEA;
+
+ explicit XclObjTextData();
+
+ /** Reads text data from a BIFF3/BIFF4 OBJ record. */
+ void ReadObj3( XclImpStream& rStrm );
+ /** Reads text data from a BIFF5 OBJ record. */
+ void ReadObj5( XclImpStream& rStrm );
+ /** Reads text data from a BIFF8 TXO record. */
+ void ReadTxo8( XclImpStream& rStrm );
+
+ sal_uInt8 GetHorAlign() const { return ::extract_value< sal_uInt8 >( mnFlags, 1, 3 ); }
+ sal_uInt8 GetVerAlign() const { return ::extract_value< sal_uInt8 >( mnFlags, 4, 3 ); }
+};
+
+enum XclTbxEventType
+{
+ EXC_TBX_EVENT_ACTION, /// XActionListener.actionPerformed
+ EXC_TBX_EVENT_MOUSE, /// XMouseListener.mouseReleased
+ EXC_TBX_EVENT_TEXT, /// XTextListener.textChanged
+ EXC_TBX_EVENT_VALUE, /// XAdjustmentListener.adjustmentValueChanged
+ EXC_TBX_EVENT_CHANGE /// XChangeListener.changed
+};
+
+/** Provides static helper functions for form controls. */
+class XclControlHelper
+{
+public:
+ /** Returns the API control model from the passed API shape object. */
+ static css::uno::Reference< css::awt::XControlModel >
+ GetControlModel( css::uno::Reference< css::drawing::XShape > const & xShape );
+
+ /** Fills the macro descriptor according to the passed macro name. */
+ static bool FillMacroDescriptor(
+ css::script::ScriptEventDescriptor& rDescriptor,
+ XclTbxEventType eEventType,
+ const OUString& rXclMacroName,
+ SfxObjectShell* pDocShell );
+ /** Tries to extract an Excel macro name from the passed macro descriptor. */
+ static OUString ExtractFromMacroDescriptor(
+ const css::script::ScriptEventDescriptor& rDescriptor,
+ XclTbxEventType eEventType );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlformula.hxx b/sc/source/filter/inc/xlformula.hxx
new file mode 100644
index 000000000..43f220bd6
--- /dev/null
+++ b/sc/source/filter/inc/xlformula.hxx
@@ -0,0 +1,551 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <osl/diagnose.h>
+#include <formula/opcode.hxx>
+#include <address.hxx>
+#include "ftools.hxx"
+#include <map>
+#include <memory>
+
+namespace svl {
+
+class SharedStringPool;
+
+}
+
+// Constants ==================================================================
+
+const size_t EXC_TOKARR_MAXLEN = 4096; /// Maximum size of a token array.
+
+// Token class flags ----------------------------------------------------------
+
+const sal_uInt8 EXC_TOKCLASS_MASK = 0x60;
+const sal_uInt8 EXC_TOKCLASS_NONE = 0x00; /// 00-1F: Base tokens.
+const sal_uInt8 EXC_TOKCLASS_REF = 0x20; /// 20-3F: Reference class tokens.
+const sal_uInt8 EXC_TOKCLASS_VAL = 0x40; /// 40-5F: Value class tokens.
+const sal_uInt8 EXC_TOKCLASS_ARR = 0x60; /// 60-7F: Array class tokens.
+
+// Base tokens ----------------------------------------------------------------
+
+const sal_uInt8 EXC_TOKID_MASK = 0x1F;
+
+const sal_uInt8 EXC_TOKID_NONE = 0x00; /// Placeholder for invalid token id.
+const sal_uInt8 EXC_TOKID_EXP = 0x01; /// Array or shared formula reference.
+const sal_uInt8 EXC_TOKID_TBL = 0x02; /// Multiple operation reference.
+const sal_uInt8 EXC_TOKID_ADD = 0x03; /// Addition operator.
+const sal_uInt8 EXC_TOKID_SUB = 0x04; /// Subtraction operator.
+const sal_uInt8 EXC_TOKID_MUL = 0x05; /// Multiplication operator.
+const sal_uInt8 EXC_TOKID_DIV = 0x06; /// Division operator.
+const sal_uInt8 EXC_TOKID_POWER = 0x07; /// Power operator.
+const sal_uInt8 EXC_TOKID_CONCAT = 0x08; /// String concatenation operator.
+const sal_uInt8 EXC_TOKID_LT = 0x09; /// Less than operator.
+const sal_uInt8 EXC_TOKID_LE = 0x0A; /// Less than or equal operator.
+const sal_uInt8 EXC_TOKID_EQ = 0x0B; /// Equal operator.
+const sal_uInt8 EXC_TOKID_GE = 0x0C; /// Greater than or equal operator.
+const sal_uInt8 EXC_TOKID_GT = 0x0D; /// Greater than operator.
+const sal_uInt8 EXC_TOKID_NE = 0x0E; /// Not equal operator.
+const sal_uInt8 EXC_TOKID_ISECT = 0x0F; /// Intersection operator.
+const sal_uInt8 EXC_TOKID_LIST = 0x10; /// List operator.
+const sal_uInt8 EXC_TOKID_RANGE = 0x11; /// Range operator.
+const sal_uInt8 EXC_TOKID_UPLUS = 0x12; /// Unary plus.
+const sal_uInt8 EXC_TOKID_UMINUS = 0x13; /// Unary minus.
+const sal_uInt8 EXC_TOKID_PERCENT = 0x14; /// Percent sign.
+const sal_uInt8 EXC_TOKID_PAREN = 0x15; /// Parentheses.
+const sal_uInt8 EXC_TOKID_MISSARG = 0x16; /// Missing argument.
+const sal_uInt8 EXC_TOKID_STR = 0x17; /// String constant.
+const sal_uInt8 EXC_TOKID_NLR = 0x18; /// Natural language reference (NLR).
+const sal_uInt8 EXC_TOKID_ATTR = 0x19; /// Special attribute.
+const sal_uInt8 EXC_TOKID_SHEET = 0x1A; /// Start of a sheet reference (BIFF2-BIFF4).
+const sal_uInt8 EXC_TOKID_ENDSHEET = 0x1B; /// End of a sheet reference (BIFF2-BIFF4).
+const sal_uInt8 EXC_TOKID_ERR = 0x1C; /// Error constant.
+const sal_uInt8 EXC_TOKID_BOOL = 0x1D; /// Boolean constant.
+const sal_uInt8 EXC_TOKID_INT = 0x1E; /// Integer constant.
+const sal_uInt8 EXC_TOKID_NUM = 0x1F; /// Floating-point constant.
+
+// Base IDs of classified tokens ----------------------------------------------
+
+const sal_uInt8 EXC_TOKID_ARRAY = 0x00; /// Array constant.
+const sal_uInt8 EXC_TOKID_FUNC = 0x01; /// Function, fixed number of arguments.
+const sal_uInt8 EXC_TOKID_FUNCVAR = 0x02; /// Function, variable number of arguments.
+const sal_uInt8 EXC_TOKID_NAME = 0x03; /// Defined name.
+const sal_uInt8 EXC_TOKID_REF = 0x04; /// 2D cell reference.
+const sal_uInt8 EXC_TOKID_AREA = 0x05; /// 2D area reference.
+const sal_uInt8 EXC_TOKID_MEMAREA = 0x06; /// Constant reference subexpression.
+const sal_uInt8 EXC_TOKID_MEMERR = 0x07; /// Deleted reference subexpression.
+const sal_uInt8 EXC_TOKID_MEMNOMEM = 0x08; /// Constant reference subexpression without result.
+const sal_uInt8 EXC_TOKID_MEMFUNC = 0x09; /// Variable reference subexpression.
+const sal_uInt8 EXC_TOKID_REFERR = 0x0A; /// Deleted 2D cell reference.
+const sal_uInt8 EXC_TOKID_AREAERR = 0x0B; /// Deleted 2D area reference.
+const sal_uInt8 EXC_TOKID_REFN = 0x0C; /// Relative 2D cell reference (in names).
+const sal_uInt8 EXC_TOKID_AREAN = 0x0D; /// Relative 2D area reference (in names).
+const sal_uInt8 EXC_TOKID_MEMAREAN = 0x0E; /// Reference subexpression (in names).
+const sal_uInt8 EXC_TOKID_MEMNOMEMN = 0x0F; /// Reference subexpression (in names) without result.
+const sal_uInt8 EXC_TOKID_FUNCCE = 0x18;
+const sal_uInt8 EXC_TOKID_NAMEX = 0x19; /// External reference.
+const sal_uInt8 EXC_TOKID_REF3D = 0x1A; /// 3D cell reference.
+const sal_uInt8 EXC_TOKID_AREA3D = 0x1B; /// 3D area reference.
+const sal_uInt8 EXC_TOKID_REFERR3D = 0x1C; /// Deleted 3D cell reference.
+const sal_uInt8 EXC_TOKID_AREAERR3D = 0x1D; /// Deleted 3D area reference
+
+// specific token constants ---------------------------------------------------
+
+const sal_uInt16 EXC_TOK_STR_MAXLEN = 255; /// Maximum string length of a tStr token.
+
+const sal_uInt8 EXC_TOK_BOOL_FALSE = 0; /// sal_False value of a tBool token.
+const sal_uInt8 EXC_TOK_BOOL_TRUE = 1; /// sal_True value of a tBool token.
+
+const sal_uInt8 EXC_TOK_ATTR_VOLATILE = 0x01; /// Volatile function.
+const sal_uInt8 EXC_TOK_ATTR_IF = 0x02; /// Start of true condition in IF function.
+const sal_uInt8 EXC_TOK_ATTR_CHOOSE = 0x04; /// Jump array of CHOOSE function.
+const sal_uInt8 EXC_TOK_ATTR_GOTO = 0x08; /// Jump to token.
+const sal_uInt8 EXC_TOK_ATTR_SUM = 0x10; /// SUM function with one parameter.
+const sal_uInt8 EXC_TOK_ATTR_ASSIGN = 0x20; /// BASIC style assignment.
+const sal_uInt8 EXC_TOK_ATTR_SPACE = 0x40; /// Spaces in formula representation.
+
+const sal_uInt8 EXC_TOK_ATTR_SPACE_SP = 0x00; /// Spaces before next token.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_BR = 0x01; /// Line breaks before next token.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_SP_OPEN = 0x02; /// Spaces before opening parenthesis.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_BR_OPEN = 0x03; /// Line breaks before opening parenthesis.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_SP_CLOSE = 0x04; /// Spaces before closing parenthesis.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_BR_CLOSE = 0x05; /// Line breaks before closing parenthesis.
+const sal_uInt8 EXC_TOK_ATTR_SPACE_SP_PRE = 0x06; /// Spaces before formula (BIFF3).
+
+const sal_uInt16 EXC_TOK_FUNCVAR_CMD = 0x8000; /// Macro command.
+const sal_uInt16 EXC_TOK_FUNCVAR_INDEXMASK = 0x7FFF; /// Mask for function/command index.
+const sal_uInt8 EXC_TOK_FUNCVAR_PROMPT = 0x80; /// User prompt for macro commands.
+const sal_uInt8 EXC_TOK_FUNCVAR_COUNTMASK = 0x7F; /// Mask for parameter count.
+
+const sal_uInt16 EXC_TOK_REF_COLREL = 0x4000; /// True = Column is relative.
+const sal_uInt16 EXC_TOK_REF_ROWREL = 0x8000; /// True = Row is relative.
+
+const sal_uInt8 EXC_TOK_NLR_ERR = 0x01; /// NLR: Invalid/deleted.
+const sal_uInt8 EXC_TOK_NLR_ROWR = 0x02; /// NLR: Row index.
+const sal_uInt8 EXC_TOK_NLR_COLR = 0x03; /// NLR: Column index.
+const sal_uInt8 EXC_TOK_NLR_ROWV = 0x06; /// NLR: Value in row.
+const sal_uInt8 EXC_TOK_NLR_COLV = 0x07; /// NLR: Value in column.
+const sal_uInt8 EXC_TOK_NLR_RANGE = 0x0A; /// NLR: Range.
+const sal_uInt8 EXC_TOK_NLR_SRANGE = 0x0B; /// Stacked NLR: Range.
+const sal_uInt8 EXC_TOK_NLR_SROWR = 0x0C; /// Stacked NLR: Row index.
+const sal_uInt8 EXC_TOK_NLR_SCOLR = 0x0D; /// Stacked NLR: Column index.
+const sal_uInt8 EXC_TOK_NLR_SROWV = 0x0E; /// Stacked NLR: Value in row.
+const sal_uInt8 EXC_TOK_NLR_SCOLV = 0x0F; /// Stacked NLR: Value in column.
+const sal_uInt8 EXC_TOK_NLR_RANGEERR = 0x10; /// NLR: Invalid/deleted range.
+const sal_uInt8 EXC_TOK_NLR_SXNAME = 0x1D; /// NLR: Pivot table name.
+const sal_uInt16 EXC_TOK_NLR_REL = 0x8000; /// True = Natural language ref is relative.
+
+const sal_uInt32 EXC_TOK_NLR_ADDREL = 0x80000000; /// NLR relative (in appended data).
+const sal_uInt32 EXC_TOK_NLR_ADDMASK = 0x3FFFFFFF; /// Mask for number of appended ranges.
+
+/** Type of a formula. */
+enum XclFormulaType
+{
+ EXC_FMLATYPE_CELL, /// Simple cell formula, also used in change tracking.
+ EXC_FMLATYPE_MATRIX, /// Matrix (array) formula.
+ EXC_FMLATYPE_SHARED, /// Shared formula.
+ EXC_FMLATYPE_CONDFMT, /// Conditional format.
+ EXC_FMLATYPE_DATAVAL, /// Data validation.
+ EXC_FMLATYPE_NAME, /// Defined name.
+ EXC_FMLATYPE_CHART, /// Chart source ranges.
+ EXC_FMLATYPE_CONTROL, /// Spreadsheet links in form controls.
+ EXC_FMLATYPE_WQUERY, /// Web query source range.
+ EXC_FMLATYPE_LISTVAL /// List (cell range) validation.
+};
+
+// Function parameter info ====================================================
+
+/** Enumerates validity modes for a function parameter. */
+enum XclFuncParamValidity
+{
+ EXC_PARAM_NONE = 0, /// Default for an unspecified entry in a C-array.
+ EXC_PARAM_REGULAR, /// Parameter supported by Calc and Excel.
+ EXC_PARAM_CALCONLY, /// Parameter supported by Calc only.
+ EXC_PARAM_EXCELONLY /// Parameter supported by Excel only.
+};
+
+/** Enumerates different types of token class conversion in function parameters. */
+enum XclFuncParamConv
+{
+ EXC_PARAMCONV_ORG, /// Use original class of current token.
+ EXC_PARAMCONV_VAL, /// Convert tokens to VAL class.
+ EXC_PARAMCONV_ARR, /// Convert tokens to ARR class.
+ EXC_PARAMCONV_RPT, /// Repeat parent conversion in VALTYPE parameters.
+ EXC_PARAMCONV_RPX, /// Repeat parent conversion in REFTYPE parameters.
+ EXC_PARAMCONV_RPO /// Repeat parent conversion in operands of operators.
+};
+
+/** Structure that contains all needed information for a parameter in a
+ function.
+
+ The member meValid specifies which application supports the parameter. If
+ set to CALCONLY, import filters have to insert a default value for this
+ parameter, and export filters have to skip the parameter. If set to
+ EXCELONLY, import filters have to skip the parameter, and export filters
+ have to insert a default value for this parameter.
+
+ The member mbValType specifies whether the parameter requires tokens to be
+ of value type (VAL or ARR class).
+
+ If set to false, the parameter is called to be REFTYPE. Tokens with REF
+ default class can be inserted for the parameter (e.g. tAreaR tokens).
+
+ If set to true, the parameter is called to be VALTYPE. Tokens with REF
+ class need to be converted to VAL tokens first (e.g. tAreaR will be
+ converted to tAreaV), and further conversion is done according to this
+ new token class.
+
+ The member meConv specifies how to convert the current token class of the
+ token inserted for the parameter. If the token class is still REF this
+ means that the token has default REF class and the parameter is REFTYPE
+ (see member mbValType), the token will not be converted at all and remains
+ in REF class. Otherwise, token class conversion is depending on the actual
+ token class of the return value of the function containing this parameter.
+ The function may return REF class (tFuncR, tFuncVarR, tFuncCER), or it may
+ return VAL or ARR class (tFuncV, tFuncA, tFuncVarV, tFuncVarA, tFuncCEV,
+ tFuncCEA). Even if the function is able to return REF class, it may return
+ VAL or ARR class instead due to the VALTYPE data type of the parent
+ function parameter that calls the own function. Example: The INDIRECT
+ function returns REF class by default. But if called from a VALTYPE
+ function parameter, e.g. in the formula =ABS(INDIRECT("A1")), it returns
+ VAL or ARR class instead. Additionally, the repeating conversion types RPT
+ and RPX rely on the conversion executed for the function token class.
+
+ 1) ORG:
+ Use the original class of the token (VAL or ARR), regardless of any
+ conversion done for the function return class.
+
+ 2) VAL:
+ Convert ARR tokens to VAL class, regardless of any conversion done for
+ the function return class.
+
+ 3) ARR:
+ Convert VAL tokens to ARR class, regardless of any conversion done for
+ the function return class.
+
+ 4) RPT:
+ If the own function returns REF class (thus it is called from a REFTYPE
+ parameter, see above), and the parent conversion type (for the function
+ return class) was ORG, VAL, or ARR, ignore that conversion and always
+ use VAL conversion for the own token instead. If the parent conversion
+ type was RPT or RPX, repeat the conversion that would have been used if
+ the function would return value type.
+ If the own function returns value type (VAL or ARR class, see above),
+ and the parent conversion type (for the function return class) was ORG,
+ VAL, ARR, or RPT, repeat this conversion for the own token. If the
+ parent conversion type was RPX, always use ORG conversion type for the
+ own token instead.
+
+ 5) RPX:
+ This type of conversion only occurs in functions returning VAL class by
+ default. If the own token is value type, and the VAL return class of
+ the own function has been changed to ARR class (due to direct ARR
+ conversion, or due to ARR conversion repeated by RPT or RPX), set the
+ own token to ARR type. Otherwise use the original token type (VAL
+ conversion from parent parameter will not be repeated at all). If
+ nested functions have RPT or value-type RPX parameters, they will not
+ repeat this conversion type, but will use ORG conversion instead (see
+ description of RPT above).
+
+ 6) RPO:
+ This type of conversion is only used for the operands of all operators
+ (unary and binary arithmetic operators, comparison operators, and range
+ operators). It is not used for function parameters. On conversion, it
+ will be replaced by the last conversion type that was not the RPO
+ conversion. This leads to a slightly different behaviour than the RPT
+ conversion for operands in conjunction with a parent RPX conversion.
+ */
+struct XclFuncParamInfo
+{
+ XclFuncParamValidity meValid; /// Parameter validity.
+ XclFuncParamConv meConv; /// Token class conversion type.
+ bool mbValType; /// Data type (false = REFTYPE, true = VALTYPE).
+};
+
+// Function data ==============================================================
+
+const sal_uInt8 EXC_FUNC_MAXPARAM = 30; /// Maximum parameter count.
+
+const size_t EXC_FUNCINFO_PARAMINFO_COUNT = 5; /// Number of parameter info entries.
+
+const sal_uInt8 EXC_FUNCFLAG_VOLATILE = 0x01; /// Result is volatile (e.g. NOW() function).
+const sal_uInt8 EXC_FUNCFLAG_IMPORTONLY = 0x02; /// Only used in import filter.
+const sal_uInt8 EXC_FUNCFLAG_EXPORTONLY = 0x04; /// Only used in export filter.
+const sal_uInt8 EXC_FUNCFLAG_PARAMPAIRS = 0x08; /// Optional parameters are expected to appear in pairs.
+const sal_uInt8 EXC_FUNCFLAG_ADDINEQUIV = 0x10; /// Function is an add-in equivalent
+
+// selected function IDs
+const sal_uInt16 EXC_FUNCID_IF = 1;
+const sal_uInt16 EXC_FUNCID_SUM = 4;
+const sal_uInt16 EXC_FUNCID_AND = 36;
+const sal_uInt16 EXC_FUNCID_OR = 37;
+const sal_uInt16 EXC_FUNCID_CHOOSE = 100;
+const sal_uInt16 EXC_FUNCID_EXTERNCALL = 255;
+
+/** Represents information for a spreadsheet function for import and export.
+
+ The member mpParamInfos points to an array of type information structures
+ for all parameters of the function. The last initialized structure
+ describing a regular parameter (member meValid == EXC_PARAMVALID_ALWAYS) in
+ this array is used repeatedly for all following parameters supported by a
+ function.
+ */
+struct XclFunctionInfo
+{
+ OpCode meOpCode; /// Calc function opcode.
+ sal_uInt16 mnXclFunc; /// Excel function index.
+ sal_uInt8 mnMinParamCount; /// Minimum number of parameters.
+ sal_uInt8 mnMaxParamCount; /// Maximum number of parameters.
+ sal_uInt8 mnRetClass; /// Token class of the return value.
+ XclFuncParamInfo mpParamInfos[ EXC_FUNCINFO_PARAMINFO_COUNT ]; /// Information for all parameters.
+ sal_uInt8 mnFlags; /// Additional flags (EXC_FUNCFLAG_* constants).
+ const char* mpcMacroName; /** Function name, if simulated by
+ a macro call (UTF-8) EXC_FUNCFLAG_ADDINEQUIV is 0;
+ or programmatical add-in name
+ if stored as such and
+ EXC_FUNCFLAG_ADDINEQUIV is set. */
+
+ /** Returns true, if the function is volatile. */
+ bool IsVolatile() const { return ::get_flag( mnFlags, EXC_FUNCFLAG_VOLATILE ); }
+ /** Returns true, if optional parameters are expected to appear in pairs. */
+ bool IsParamPairs() const { return ::get_flag( mnFlags, EXC_FUNCFLAG_PARAMPAIRS ); }
+ /** Returns true, if the function parameter count is fixed. */
+ bool IsFixedParamCount() const { return (mnXclFunc != EXC_FUNCID_EXTERNCALL) && (mnMinParamCount == mnMaxParamCount); }
+ /** Returns true, if the function is simulated by a macro call. */
+ bool IsMacroFunc() const { return mpcMacroName != nullptr && !(mnFlags & EXC_FUNCFLAG_ADDINEQUIV); }
+ /** Returns true, if the function is stored as an add-in call. */
+ bool IsAddInEquivalent() const { return mpcMacroName != nullptr && (mnFlags & EXC_FUNCFLAG_ADDINEQUIV); }
+ /** Returns the name of the external function as string. */
+ OUString GetMacroFuncName() const;
+ /** Returns the programmatical name of the Add-In function as string. */
+ OUString GetAddInEquivalentFuncName() const;
+};
+
+class XclRoot;
+
+/** Provides access to function info structs for all available functions. */
+class XclFunctionProvider
+{
+public:
+ explicit XclFunctionProvider( const XclRoot& rRoot );
+
+ /** Returns the function data for an Excel function index, or 0 on error. */
+ const XclFunctionInfo* GetFuncInfoFromXclFunc( sal_uInt16 nXclFunc ) const;
+ /** Returns the function data for an Excel function simulated by a macro call, or 0 on error. */
+ const XclFunctionInfo* GetFuncInfoFromXclMacroName( const OUString& rXclMacroName ) const;
+ /** Returns the function data for a Calc opcode, or 0 on error. */
+ const XclFunctionInfo* GetFuncInfoFromOpCode( OpCode eOpCode ) const;
+
+private:
+ void FillXclFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd );
+ void FillScFuncMap( const XclFunctionInfo* pBeg, const XclFunctionInfo* pEnd );
+
+private:
+ typedef ::std::map< sal_uInt16, const XclFunctionInfo* > XclFuncMap;
+ typedef ::std::map< OUString, const XclFunctionInfo* > XclMacroNameMap;
+ typedef ::std::map< OpCode, const XclFunctionInfo* > ScFuncMap;
+
+ XclFuncMap maXclFuncMap; /// Maps Excel function indexes to function data.
+ XclMacroNameMap maXclMacroNameMap; /// Maps macro function names to function data.
+ ScFuncMap maScFuncMap; /// Maps Calc opcodes to function data.
+};
+
+// Token array ================================================================
+
+class XclImpStream;
+class XclExpStream;
+
+/** Binary representation of an Excel token array. */
+class XclTokenArray
+{
+public:
+ /** Creates an empty token array. */
+ explicit XclTokenArray( bool bVolatile = false );
+ /** Creates a token array, swaps passed token vectors into own data. */
+ explicit XclTokenArray( ScfUInt8Vec& rTokVec, ScfUInt8Vec& rExtDataVec, bool bVolatile = false );
+
+ /** Returns true, if the token array is empty. */
+ bool Empty() const { return maTokVec.empty(); }
+ /** Returns the size of the token array in bytes. */
+ sal_uInt16 GetSize() const;
+ /** Returns read-only access to the byte vector storing token data. */
+ const sal_uInt8* GetData() const { return maTokVec.empty() ? nullptr : maTokVec.data(); }
+ /** Returns true, if the formula contains a volatile function. */
+ bool IsVolatile() const { return mbVolatile; }
+
+ /** Reads the size field of the token array. */
+ static sal_uInt16 ReadSize(XclImpStream& rStrm);
+ /** Reads the tokens of the token array (without size field). */
+ void ReadArray(sal_uInt16 nSize, XclImpStream& rStrm);
+ /** Reads size field and the tokens. */
+ void Read( XclImpStream& rStrm );
+
+ /** Writes the size field of the token array. */
+ void WriteSize( XclExpStream& rStrm ) const;
+ /** Writes the tokens of the token array (without size field). */
+ void WriteArray( XclExpStream& rStrm ) const;
+ /** Writes size field and the tokens. */
+ void Write( XclExpStream& rStrm ) const;
+
+ /** Compares this token array with the passed. */
+ bool operator==( const XclTokenArray& rTokArr ) const;
+
+private:
+ ScfUInt8Vec maTokVec; /// Byte vector containing token data.
+ ScfUInt8Vec maExtDataVec; /// Byte vector containing extended data (arrays, stacked NLRs).
+ bool mbVolatile; /// True = Formula contains volatile function.
+};
+
+typedef std::shared_ptr< XclTokenArray > XclTokenArrayRef;
+
+/** Calls the Read() function at the passed token array. */
+XclImpStream& operator>>( XclImpStream& rStrm, XclTokenArray& rTokArr );
+/** Calls the Write() function at the passed token array. */
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArray& rTokArr );
+/** Calls the Write() function at the passed token array. */
+XclExpStream& operator<<( XclExpStream& rStrm, const XclTokenArrayRef& rxTokArr );
+
+namespace formula { class FormulaToken; }
+class ScTokenArray;
+
+/** Special token array iterator for the Excel filters.
+
+ Iterates over a Calc token array without modifying it (therefore the
+ iterator can be used with constant token arrays).
+
+ Usage: Construct a new iterator object and pass a Calc token array, or use
+ the Init() function to assign another Calc token array. As long as the Is()
+ function returns true, the accessor functions can be used to get the
+ current Calc token.
+ */
+class XclTokenArrayIterator
+{
+public:
+ explicit XclTokenArrayIterator();
+ explicit XclTokenArrayIterator( const ScTokenArray& rScTokArr, bool bSkipSpaces );
+ /** Copy constructor that allows to change the skip-spaces mode. */
+ explicit XclTokenArrayIterator( const XclTokenArrayIterator& rTokArrIt, bool bSkipSpaces );
+
+ void Init( const ScTokenArray& rScTokArr, bool bSkipSpaces );
+
+ bool Is() const { return mppScToken != nullptr; }
+ bool operator!() const { return !Is(); }
+ const ::formula::FormulaToken* Get() const { return mppScToken ? *mppScToken : nullptr; }
+ const ::formula::FormulaToken* operator->() const { return Get(); }
+ const ::formula::FormulaToken& operator*() const { return *Get(); }
+
+ XclTokenArrayIterator& operator++();
+
+private:
+ void NextRawToken();
+ void SkipSpaces();
+
+private:
+ const ::formula::FormulaToken*const* mppScTokenBeg; /// Pointer to first token pointer of token array.
+ const ::formula::FormulaToken*const* mppScTokenEnd; /// Pointer behind last token pointer of token array.
+ const ::formula::FormulaToken*const* mppScToken; /// Pointer to current token pointer of token array.
+ bool mbSkipSpaces; /// true = Skip whitespace tokens.
+};
+
+/** Contains all cell references that can be extracted from a multiple operations formula. */
+struct XclMultipleOpRefs
+{
+ ScAddress maFmlaScPos; /// Position of the (first) formula cell.
+ ScAddress maColFirstScPos;
+ ScAddress maColRelScPos;
+ ScAddress maRowFirstScPos;
+ ScAddress maRowRelScPos;
+ bool mbDblRefMode; /// true = One formula with row and column values.
+};
+
+/** A helper with Excel specific token array functions.
+
+ The purpose to not add these functions to ScTokenArray is to prevent code
+ changes in low-level Calc headers and to keep the Excel specific source
+ code in the filter directory. Deriving from ScTokenArray is not viable
+ because that would need expensive copy-constructions of the token arrays.
+ */
+class XclTokenArrayHelper
+{
+public:
+ // token identifiers ------------------------------------------------------
+
+ /** Returns the classified token ID from a base ID and the token class. */
+ inline static sal_uInt8 GetTokenId( sal_uInt8 nBaseId, sal_uInt8 nTokenClass );
+
+ /** Returns the token class of the passed token ID. */
+ static sal_uInt8 GetTokenClass( sal_uInt8 nTokenId ) { return nTokenId & EXC_TOKCLASS_MASK; }
+ /** Changes the token class in the passed classified token ID. */
+ inline static void ChangeTokenClass( sal_uInt8& rnTokenId, sal_uInt8 nTokenClass );
+
+ // strings and string lists -----------------------------------------------
+
+ /** Tries to extract a string from the passed token.
+ @param rString (out-parameter) The string contained in the token.
+ @return true = Passed token is a string token, rString parameter is valid. */
+ static bool GetTokenString( OUString& rString, const ::formula::FormulaToken& rScToken );
+
+ /** Parses the passed formula and tries to find a single string token, i.e. "abc".
+ @param rString (out-parameter) The string contained in the formula.
+ @return true = String token found, rString parameter is valid. */
+ static bool GetString( OUString& rString, const ScTokenArray& rScTokArr );
+
+ /** Parses the passed formula and tries to find a string token list, i.e. "abc";"def";"ghi".
+ @descr Returns the unquoted (!) strings in a single string, separated with the
+ passed character. If a comma is specified, the function will return abc,def,ghi from
+ the example above.
+ @param rStringList (out-parameter) All strings contained in the formula as list.
+ @param cSep List separator character.
+ @return true = String token list found, rString parameter is valid. */
+ static bool GetStringList( OUString& rStringList, const ScTokenArray& rScTokArr, sal_Unicode cSep );
+
+ /** Tries to convert a formula that consists of a single string token to a list of strings.
+ Removes leading spaces from each token.
+ @descr Example: The formula ="abc\ndef\nghi" will be converted to the formula
+ ="abc";"def";"ghi", if the LF character is specified as separator.
+ @param rScTokArr (in/out-parameter) The token array to modify.
+ @param cStringSep The separator in the source string. */
+ static void ConvertStringToList(
+ ScTokenArray& rScTokArr, svl::SharedStringPool& rSPool, sal_Unicode cStringSep );
+
+ // multiple operations ----------------------------------------------------
+
+ /** Parses the passed formula and tries to extract references of a multiple operation.
+ @descr Requires that the formula contains a single MULTIPLE.OPERATION function call.
+ Spaces in the formula are silently ignored.
+ @return true = Multiple operation found, and all references successfully extracted. */
+ static bool GetMultipleOpRefs( const ScDocument& rDoc, XclMultipleOpRefs& rRefs, const ScTokenArray& rScTokArr, const ScAddress& rScPos );
+};
+
+inline sal_uInt8 XclTokenArrayHelper::GetTokenId( sal_uInt8 nBaseId, sal_uInt8 nTokenClass )
+{
+ OSL_ENSURE( !::get_flag( nBaseId, static_cast< sal_uInt8 >( ~EXC_TOKID_MASK ) ), "XclTokenArrayHelper::GetTokenId - invalid token ID" );
+ OSL_ENSURE( !::get_flag( nTokenClass, static_cast< sal_uInt8 >( ~EXC_TOKCLASS_MASK ) ), "XclTokenArrayHelper::GetTokenId - invalid token class" );
+ return nBaseId | nTokenClass;
+}
+
+inline void XclTokenArrayHelper::ChangeTokenClass( sal_uInt8& rnTokenId, sal_uInt8 nTokenClass )
+{
+ OSL_ENSURE( !::get_flag( nTokenClass, static_cast< sal_uInt8 >( ~EXC_TOKCLASS_MASK ) ), "XclTokenArrayHelper::ChangeTokenClass - invalid token class" );
+ ::set_flag( rnTokenId, EXC_TOKCLASS_MASK, false );
+ ::set_flag( rnTokenId, nTokenClass );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xllink.hxx b/sc/source/filter/inc/xllink.hxx
new file mode 100644
index 000000000..f87567d09
--- /dev/null
+++ b/sc/source/filter/inc/xllink.hxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <ostream>
+
+#include <sal/types.h>
+
+// Constants and Enumerations =================================================
+
+const sal_uInt16 EXC_TAB_EXTERNAL = 0xFFFE; /// Special sheet index for external links.
+const sal_uInt16 EXC_TAB_DELETED = 0xFFFF; /// Deleted sheet in a 3D reference.
+
+// (0x0016) EXTERNCOUNT -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_EXTERNCOUNT = 0x0016;
+
+// (0x0017) EXTERNSHEET -------------------------------------------------------
+
+const sal_uInt16 EXC_ID_EXTERNSHEET = 0x0017;
+
+const sal_Unicode EXC_EXTSH_URL = '\x01';
+const sal_Unicode EXC_EXTSH_OWNTAB = '\x02';
+const sal_Unicode EXC_EXTSH_TABNAME = '\x03';
+const sal_Unicode EXC_EXTSH_OWNDOC = '\x04';
+const sal_Unicode EXC_EXTSH_ADDIN = '\x3A';
+
+// (0x0023) EXTERNNAME --------------------------------------------------------
+
+const sal_uInt16 EXC_ID_EXTERNNAME = 0x0023;
+
+const sal_uInt16 EXC_EXTN_BUILTIN = 0x0001;
+const sal_uInt16 EXC_EXTN_OLE = 0x0010;
+const sal_uInt16 EXC_EXTN_OLE_OR_DDE = 0xFFFE;
+
+const sal_uInt16 EXC_EXTN_EXPDDE_STDDOC = 0x7FEA; /// for export
+const sal_uInt16 EXC_EXTN_EXPDDE = 0x7FE2; /// for export
+
+// (0x0059, 0x005A) XCT, CRN --------------------------------------------------
+
+const sal_uInt16 EXC_ID_XCT = 0x0059;
+const sal_uInt16 EXC_ID_CRN = 0x005A;
+
+// (0x013D) TABID -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_TABID = 0x013D;
+
+// (0x01AE) SUPBOOK -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SUPBOOK = 0x01AE;
+
+const sal_uInt16 EXC_SUPB_SELF = 0x0401;
+const sal_uInt16 EXC_SUPB_ADDIN = 0x3A01;
+
+/** This enumeration specifies the type of a SUPBOOK record. */
+enum class XclSupbookType
+{
+ Unknown, /// unknown SUPBOOK record type.
+ Self, /// SUPBOOK is used for internal references.
+ Extern, /// SUPBOOK is used for external references.
+ Addin, /// SUPBOOK contains add-in functions.
+ Special, /// SUPBOOK is used for DDE or OLE links.
+ Eurotool /// SUPBOOK is used for EUROCONVERT.
+};
+
+template< typename charT, typename traits >
+inline std::basic_ostream<charT, traits> & operator <<(
+ std::basic_ostream<charT, traits> & stream, const XclSupbookType& type )
+{
+ switch (type)
+ {
+ case XclSupbookType::Unknown: return stream << "unknown";
+ case XclSupbookType::Self: return stream << "self";
+ case XclSupbookType::Extern: return stream << "extern";
+ case XclSupbookType::Addin: return stream << "addin";
+ case XclSupbookType::Special: return stream << "special";
+ case XclSupbookType::Eurotool: return stream << "eurotool";
+ default: return stream << static_cast<int>(type);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlname.hxx b/sc/source/filter/inc/xlname.hxx
new file mode 100644
index 000000000..f469919ec
--- /dev/null
+++ b/sc/source/filter/inc/xlname.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+// Constants and Enumerations =================================================
+
+// (0x0018, 0x0218) NAME ------------------------------------------------------
+
+const sal_uInt16 EXC_ID_NAME = 0x0018;
+const sal_uInt16 EXC_ID34_NAME = 0x0218;
+
+// flags
+const sal_uInt16 EXC_NAME_DEFAULT = 0x0000;
+const sal_uInt16 EXC_NAME_HIDDEN = 0x0001;
+const sal_uInt16 EXC_NAME_FUNC = 0x0002;
+const sal_uInt16 EXC_NAME_VB = 0x0004;
+const sal_uInt16 EXC_NAME_PROC = 0x0008;
+const sal_uInt16 EXC_NAME_CALCEXP = 0x0010;
+const sal_uInt16 EXC_NAME_BUILTIN = 0x0020;
+const sal_uInt16 EXC_NAME_FGROUPMASK = 0x0FC0;
+const sal_uInt16 EXC_NAME_BIG = 0x1000;
+
+const sal_uInt8 EXC_NAME2_FUNC = 0x02; /// BIFF2 function/command flag.
+
+const sal_uInt16 EXC_NAME_GLOBAL = 0; /// 0 = Globally defined name.
+
+// codes for built-in names
+const sal_Unicode EXC_BUILTIN_CONSOLIDATEAREA = '\x00';
+const sal_Unicode EXC_BUILTIN_AUTOOPEN = '\x01';
+const sal_Unicode EXC_BUILTIN_AUTOCLOSE = '\x02';
+const sal_Unicode EXC_BUILTIN_EXTRACT = '\x03';
+const sal_Unicode EXC_BUILTIN_DATABASE = '\x04';
+const sal_Unicode EXC_BUILTIN_CRITERIA = '\x05';
+const sal_Unicode EXC_BUILTIN_PRINTAREA = '\x06';
+const sal_Unicode EXC_BUILTIN_PRINTTITLES = '\x07';
+const sal_Unicode EXC_BUILTIN_RECORDER = '\x08';
+const sal_Unicode EXC_BUILTIN_DATAFORM = '\x09';
+const sal_Unicode EXC_BUILTIN_AUTOACTIVATE = '\x0A';
+const sal_Unicode EXC_BUILTIN_AUTODEACTIVATE = '\x0B';
+const sal_Unicode EXC_BUILTIN_SHEETTITLE = '\x0C';
+const sal_Unicode EXC_BUILTIN_FILTERDATABASE = '\x0D';
+const sal_Unicode EXC_BUILTIN_UNKNOWN = '\x0E';
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlpage.hxx b/sc/source/filter/inc/xlpage.hxx
new file mode 100644
index 000000000..d200e8b80
--- /dev/null
+++ b/sc/source/filter/inc/xlpage.hxx
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/gen.hxx>
+#include "ftools.hxx"
+#include <memory>
+
+// Constants and Enumerations =================================================
+
+// (0x0014, 0x0015) HEADER, FOOTER --------------------------------------------
+
+const sal_uInt16 EXC_ID_HEADER = 0x0014;
+const sal_uInt16 EXC_ID_FOOTER = 0x0015;
+
+// (0x0016, 0x0017) EVEN HEADER, EVEN FOOTER ----------------------------------
+
+const sal_uInt16 EXC_ID_HEADER_EVEN = 0x0016;
+const sal_uInt16 EXC_ID_FOOTER_EVEN = 0x0017;
+
+// (0x0018, 0x0019) FIRST HEADER, FIRST FOOTER ----------------------------------
+
+const sal_uInt16 EXC_ID_HEADER_FIRST = 0x0018;
+const sal_uInt16 EXC_ID_FOOTER_FIRST = 0x0019;
+
+// (0x001A, 0x001B) VERTICAL-, HORIZONTALPAGEBREAKS ---------------------------
+
+const sal_uInt16 EXC_ID_VERPAGEBREAKS = 0x001A;
+const sal_uInt16 EXC_ID_HORPAGEBREAKS = 0x001B;
+
+// (0x0026, 0x0027, 0x0028, 0x0029) LEFT-, RIGHT-, TOP-, BOTTOMMARGIN ---------
+
+const sal_uInt16 EXC_ID_LEFTMARGIN = 0x0026;
+const sal_uInt16 EXC_ID_RIGHTMARGIN = 0x0027;
+const sal_uInt16 EXC_ID_TOPMARGIN = 0x0028;
+const sal_uInt16 EXC_ID_BOTTOMMARGIN = 0x0029;
+
+const sal_Int32 EXC_MARGIN_DEFAULT_LR = 1900; /// Left/right default margin in 1/100mm.
+const sal_Int32 EXC_MARGIN_DEFAULT_TB = 2500; /// Top/bottom default margin in 1/100mm.
+const sal_Int32 EXC_MARGIN_DEFAULT_HF = 1300; /// Header/footer default margin in 1/100mm.
+const sal_Int32 EXC_MARGIN_DEFAULT_HLR = 1900; /// Left/right header default margin in 1/100mm.
+const sal_Int32 EXC_MARGIN_DEFAULT_FLR = 1900; /// Left/right footer default margin in 1/100mm.
+
+// (0x002A, 0x002B) PRINTHEADERS, PRINTGRIDLINES ------------------------------
+
+const sal_uInt16 EXC_ID_PRINTHEADERS = 0x002A;
+const sal_uInt16 EXC_ID_PRINTGRIDLINES = 0x002B;
+
+// (0x0033) PRINTSIZE ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_PRINTSIZE = 0x0033;
+
+const sal_uInt16 EXC_PRINTSIZE_SCREEN = 1;
+const sal_uInt16 EXC_PRINTSIZE_PAGE = 2;
+const sal_uInt16 EXC_PRINTSIZE_FULL = 3;
+
+// (0x0082, 0x0083, 0x0084) GRIDSET, HCENTER, VCENTER -------------------------
+
+const sal_uInt16 EXC_ID_GRIDSET = 0x0082;
+const sal_uInt16 EXC_ID_HCENTER = 0x0083;
+const sal_uInt16 EXC_ID_VCENTER = 0x0084;
+
+// (0x00A1) SETUP -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SETUP = 0x00A1;
+
+const sal_uInt16 EXC_SETUP_INROWS = 0x0001;
+const sal_uInt16 EXC_SETUP_PORTRAIT = 0x0002;
+const sal_uInt16 EXC_SETUP_INVALID = 0x0004;
+const sal_uInt16 EXC_SETUP_BLACKWHITE = 0x0008;
+const sal_uInt16 EXC_SETUP_DRAFT = 0x0010;
+const sal_uInt16 EXC_SETUP_PRINTNOTES = 0x0020;
+const sal_uInt16 EXC_SETUP_STARTPAGE = 0x0080;
+const sal_uInt16 EXC_SETUP_NOTES_END = 0x0200;
+
+const sal_uInt16 EXC_PAPERSIZE_DEFAULT = 0;
+const sal_uInt16 EXC_PAPERSIZE_USER = 0xFFFF;
+
+// Page settings ==============================================================
+
+class SvxBrushItem;
+
+/** Contains all page (print) settings for a single sheet. */
+struct XclPageData
+{
+ /** noncopyable */
+ XclPageData(const XclPageData&) = delete;
+ const XclPageData& operator=(const XclPageData&) = delete;
+
+ typedef std::unique_ptr< SvxBrushItem > SvxBrushItemPtr;
+
+ ScfUInt16Vec maHorPageBreaks; /// Horizontal page breaks.
+ ScfUInt16Vec maVerPageBreaks; /// Vertical page breaks.
+ SvxBrushItemPtr mxBrushItem; /// Background bitmap.
+ OUString maHeader; /// Excel header string (empty = off).
+ OUString maFooter; /// Excel footer string (empty = off).
+ OUString maHeaderEven; /// Excel header string for even pages (empty = off).
+ OUString maFooterEven; /// Excel footer string for even pages (empty = off).
+ OUString maHeaderFirst; /// Excel header string for first page (empty = off).
+ OUString maFooterFirst; /// Excel footer string for first page (empty = off).
+ double mfLeftMargin; /// Left margin in inches.
+ double mfRightMargin; /// Right margin in inches.
+ double mfTopMargin; /// Top margin in inches.
+ double mfBottomMargin; /// Bottom margin in inches.
+ double mfHeaderMargin; /// Margin main page to header.
+ double mfFooterMargin; /// Margin main page to footer.
+ double mfHdrLeftMargin; /// Left margin to header.
+ double mfHdrRightMargin; /// Right margin to header.
+ double mfFtrLeftMargin; /// Left margin to footer.
+ double mfFtrRightMargin; /// Right margin to footer.
+ sal_uInt16 mnPaperSize; /// Index into paper size table.
+ sal_uInt16 mnStrictPaperSize; /// Same as papersize - but for ooxml (considering stricter dimensions)
+ sal_uInt16 mnPaperWidth; /// Paper Width in mm
+ sal_uInt16 mnPaperHeight; /// Paper Height in mm
+ sal_uInt16 mnCopies; /// Number of copies.
+ sal_uInt16 mnStartPage; /// Start page number.
+ sal_uInt16 mnScaling; /// Scaling in percent.
+ sal_uInt16 mnFitToWidth; /// Fit to number of pages in width.
+ sal_uInt16 mnFitToHeight; /// Fit to number of pages in height.
+ sal_uInt16 mnHorPrintRes; /// Horizontal printing resolution.
+ sal_uInt16 mnVerPrintRes; /// Vertical printing resolution.
+ bool mbUseEvenHF; /// True = use maHeaderEven/maFooterEven.
+ bool mbUseFirstHF; /// True = use maHeaderFirst/maFooterFirst.
+ bool mbValid; /// false = some of the values are not valid.
+ bool mbPortrait; /// true = portrait; false = landscape.
+ bool mbPrintInRows; /// true = in rows; false = in columns.
+ bool mbBlackWhite; /// true = black/white; false = colors.
+ bool mbDraftQuality; /// true = draft; false = default quality.
+ bool mbPrintNotes; /// true = print notes.
+ bool mbManualStart; /// true = mnStartPage valid; false = automatic.
+ bool mbFitToPages; /// true = fit to pages; false = scale in percent.
+ bool mbHorCenter; /// true = centered horizontally; false = left aligned.
+ bool mbVerCenter; /// true = centered vertically; false = top aligned.
+ bool mbPrintHeadings; /// true = print column and row headings.
+ bool mbPrintGrid; /// true = print grid lines.
+
+ explicit XclPageData();
+ ~XclPageData();
+
+ /** Sets Excel default page settings. */
+ void SetDefaults();
+
+ /** Returns the real paper size (twips) from the paper size index and paper orientation. */
+ Size GetScPaperSize() const;
+ /** Sets the Excel paper size index and paper orientation from Calc paper size (twips). */
+ void SetScPaperSize( const Size& rSize, bool bPortrait, bool bStrict = false );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlpivot.hxx b/sc/source/filter/inc/xlpivot.hxx
new file mode 100644
index 000000000..aa2ef17ff
--- /dev/null
+++ b/sc/source/filter/inc/xlpivot.hxx
@@ -0,0 +1,774 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <tools/datetime.hxx>
+#include "ftools.hxx"
+#include "xladdress.hxx"
+#include <dpobject.hxx>
+
+#include <optional>
+
+class XclImpStream;
+class XclExpStream;
+enum class ScGeneralFunction;
+
+// Constants and Enumerations =================================================
+
+// misc -----------------------------------------------------------------------
+
+inline constexpr OUStringLiteral EXC_STORAGE_PTCACHE = u"_SX_DB_CUR";
+
+// strings
+const sal_uInt16 EXC_PT_NOSTRING = 0xFFFF;
+const sal_uInt16 EXC_PT_MAXSTRLEN = 0xFFFE;
+
+// pivot cache fields
+const size_t EXC_PC_MAXFIELDCOUNT = 0xFFFE;
+const sal_uInt16 EXC_PC_NOFIELD = 0xFFFF;
+const sal_Int32 EXC_PC_MAXSTRLEN = 255;
+
+// pivot cache items
+const size_t EXC_PC_MAXITEMCOUNT = 32500;
+const sal_uInt16 EXC_PC_NOITEM = 0xFFFF;
+
+// pivot table fields
+const sal_uInt16 EXC_PT_MAXFIELDCOUNT = 0xFFFE;
+const sal_uInt16 EXC_PT_MAXROWCOLCOUNT = EXC_PT_MAXFIELDCOUNT;
+const sal_uInt16 EXC_PT_MAXPAGECOUNT = 256;
+const sal_uInt16 EXC_PT_MAXDATACOUNT = 256;
+
+// pivot table items
+const sal_uInt16 EXC_PT_MAXITEMCOUNT = 32500;
+
+const sal_uInt16 EXC_PT_AUTOFMT_HEADER = 0x810;
+const sal_uInt16 EXC_PT_AUTOFMT_ZERO = 0;
+const sal_uInt32 EXC_PT_AUTOFMT_FLAGS = 0x20;
+
+/** Data type of a pivot cache item. */
+enum XclPCItemType
+{
+ EXC_PCITEM_INVALID, /// Special state, not used in Excel files.
+ EXC_PCITEM_EMPTY, /// Empty cell.
+ EXC_PCITEM_TEXT, /// String data.
+ EXC_PCITEM_DOUBLE, /// Floating-point value.
+ EXC_PCITEM_DATETIME, /// Date/time.
+ EXC_PCITEM_INTEGER, /// 16-bit integer value.
+ EXC_PCITEM_BOOL, /// Boolean value.
+ EXC_PCITEM_ERROR /// Error code.
+};
+
+/** Specifies the type of a pivot cache field. */
+enum XclPCFieldType
+{
+ EXC_PCFIELD_STANDARD, /// Standard field without grouping.
+ EXC_PCFIELD_STDGROUP, /// Standard grouping field.
+ EXC_PCFIELD_NUMGROUP, /// Numeric grouping field.
+ EXC_PCFIELD_DATEGROUP, /// First date grouping field (opt. with child grouping field).
+ EXC_PCFIELD_DATECHILD, /// Additional date grouping field.
+ EXC_PCFIELD_CALCED, /// Calculated field.
+ EXC_PCFIELD_UNKNOWN /// Unknown field state, handled like standard field.
+};
+
+// (0x0051,0x0052) DCONREF, DCONNAME ------------------------------------------
+
+const sal_uInt16 EXC_ID_DCONREF = 0x0051;
+const sal_uInt16 EXC_ID_DCONNAME = 0x0052;
+
+// (0x00B0) SXVIEW ------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SXVIEW = 0x00B0;
+
+const sal_uInt16 EXC_SXVIEW_ROWGRAND = 0x0001;
+const sal_uInt16 EXC_SXVIEW_COLGRAND = 0x0002;
+const sal_uInt16 EXC_SXVIEW_DEFAULTFLAGS = 0x0208;
+
+const sal_uInt16 EXC_SXVIEW_DATALAST = 0xFFFF;
+const sal_uInt16 EXC_SXVIEW_AUTOFMT = 0x0001;
+
+// (0x00B1) SXVD --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVD = 0x00B1;
+
+const sal_uInt16 EXC_SXVD_AXIS_NONE = 0x0000;
+const sal_uInt16 EXC_SXVD_AXIS_ROW = 0x0001;
+const sal_uInt16 EXC_SXVD_AXIS_COL = 0x0002;
+const sal_uInt16 EXC_SXVD_AXIS_PAGE = 0x0004;
+const sal_uInt16 EXC_SXVD_AXIS_DATA = 0x0008;
+const sal_uInt16 EXC_SXVD_AXIS_ROWCOL = EXC_SXVD_AXIS_ROW | EXC_SXVD_AXIS_COL;
+const sal_uInt16 EXC_SXVD_AXIS_ROWCOLPAGE = EXC_SXVD_AXIS_ROWCOL | EXC_SXVD_AXIS_PAGE;
+
+const sal_uInt16 EXC_SXVD_SUBT_NONE = 0x0000;
+const sal_uInt16 EXC_SXVD_SUBT_DEFAULT = 0x0001;
+const sal_uInt16 EXC_SXVD_SUBT_SUM = 0x0002;
+const sal_uInt16 EXC_SXVD_SUBT_COUNT = 0x0004;
+const sal_uInt16 EXC_SXVD_SUBT_AVERAGE = 0x0008;
+const sal_uInt16 EXC_SXVD_SUBT_MAX = 0x0010;
+const sal_uInt16 EXC_SXVD_SUBT_MIN = 0x0020;
+const sal_uInt16 EXC_SXVD_SUBT_PROD = 0x0040;
+const sal_uInt16 EXC_SXVD_SUBT_COUNTNUM = 0x0080;
+const sal_uInt16 EXC_SXVD_SUBT_STDDEV = 0x0100;
+const sal_uInt16 EXC_SXVD_SUBT_STDDEVP = 0x0200;
+const sal_uInt16 EXC_SXVD_SUBT_VAR = 0x0400;
+const sal_uInt16 EXC_SXVD_SUBT_VARP = 0x0800;
+
+const sal_uInt16 EXC_SXVD_DEFAULT_CACHE = EXC_PC_NOFIELD;
+
+// (0x00B2) SXVI --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVI = 0x00B2;
+
+const sal_uInt16 EXC_SXVI_TYPE_PAGE = 0x00FE;
+const sal_uInt16 EXC_SXVI_TYPE_NULL = 0x00FF;
+const sal_uInt16 EXC_SXVI_TYPE_DATA = 0x0000;
+const sal_uInt16 EXC_SXVI_TYPE_DEFAULT = 0x0001;
+const sal_uInt16 EXC_SXVI_TYPE_SUM = 0x0002;
+const sal_uInt16 EXC_SXVI_TYPE_COUNT = 0x0003;
+const sal_uInt16 EXC_SXVI_TYPE_AVERAGE = 0x0004;
+const sal_uInt16 EXC_SXVI_TYPE_MAX = 0x0005;
+const sal_uInt16 EXC_SXVI_TYPE_MIN = 0x0006;
+const sal_uInt16 EXC_SXVI_TYPE_PROD = 0x0007;
+const sal_uInt16 EXC_SXVI_TYPE_COUNTNUM = 0x0008;
+const sal_uInt16 EXC_SXVI_TYPE_STDDEV = 0x0009;
+const sal_uInt16 EXC_SXVI_TYPE_STDDEVP = 0x000A;
+const sal_uInt16 EXC_SXVI_TYPE_VAR = 0x000B;
+const sal_uInt16 EXC_SXVI_TYPE_VARP = 0x000C;
+const sal_uInt16 EXC_SXVI_TYPE_GRAND = 0x000D;
+
+const sal_uInt16 EXC_SXVI_DEFAULTFLAGS = 0x0000;
+const sal_uInt16 EXC_SXVI_HIDDEN = 0x0001;
+const sal_uInt16 EXC_SXVI_HIDEDETAIL = 0x0002;
+const sal_uInt16 EXC_SXVI_FORMULA = 0x0004;
+const sal_uInt16 EXC_SXVI_MISSING = 0x0008;
+
+const sal_uInt16 EXC_SXVI_DEFAULT_CACHE = EXC_PC_NOFIELD;
+
+// (0x00B4) SXIVD -------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXIVD = 0x00B4;
+const sal_uInt16 EXC_SXIVD_DATA = 0xFFFE;
+
+// (0x00B5) SXLI --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXLI = 0x00B5;
+const sal_uInt16 EXC_SXLI_DEFAULTFLAGS = 0x0000;
+
+// (0x00B6) SXPI --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXPI = 0x00B6;
+const sal_uInt16 EXC_SXPI_ALLITEMS = 0x7FFD;
+
+// (0x00C5) SXDI --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDI = 0x00C5;
+
+const sal_uInt16 EXC_SXDI_FUNC_SUM = 0x0000;
+const sal_uInt16 EXC_SXDI_FUNC_COUNT = 0x0001;
+const sal_uInt16 EXC_SXDI_FUNC_AVERAGE = 0x0002;
+const sal_uInt16 EXC_SXDI_FUNC_MAX = 0x0003;
+const sal_uInt16 EXC_SXDI_FUNC_MIN = 0x0004;
+const sal_uInt16 EXC_SXDI_FUNC_PRODUCT = 0x0005;
+const sal_uInt16 EXC_SXDI_FUNC_COUNTNUM = 0x0006;
+const sal_uInt16 EXC_SXDI_FUNC_STDDEV = 0x0007;
+const sal_uInt16 EXC_SXDI_FUNC_STDDEVP = 0x0008;
+const sal_uInt16 EXC_SXDI_FUNC_VAR = 0x0009;
+const sal_uInt16 EXC_SXDI_FUNC_VARP = 0x000A;
+
+const sal_uInt16 EXC_SXDI_REF_NORMAL = 0x0000;
+const sal_uInt16 EXC_SXDI_REF_DIFF = 0x0001;
+const sal_uInt16 EXC_SXDI_REF_PERC = 0x0002;
+const sal_uInt16 EXC_SXDI_REF_PERC_DIFF = 0x0003;
+const sal_uInt16 EXC_SXDI_REF_RUN_TOTAL = 0x0004;
+const sal_uInt16 EXC_SXDI_REF_PERC_ROW = 0x0005;
+const sal_uInt16 EXC_SXDI_REF_PERC_COL = 0x0006;
+const sal_uInt16 EXC_SXDI_REF_PERC_TOTAL = 0x0007;
+const sal_uInt16 EXC_SXDI_REF_INDEX = 0x0008;
+
+const sal_uInt16 EXC_SXDI_PREVITEM = 0x7FFB;
+const sal_uInt16 EXC_SXDI_NEXTITEM = 0x7FFC;
+
+// (0x00C6) SXDB --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDB = 0x00C6;
+
+const sal_uInt16 EXC_SXDB_SAVEDATA = 0x0001;
+const sal_uInt16 EXC_SXDB_INVALID = 0x0002;
+const sal_uInt16 EXC_SXDB_REFRESH_LOAD = 0x0004;
+const sal_uInt16 EXC_SXDB_OPT_CACHE = 0x0008;
+const sal_uInt16 EXC_SXDB_BG_QUERY = 0x0010;
+const sal_uInt16 EXC_SXDB_ENABLE_REFRESH = 0x0020;
+const sal_uInt16 EXC_SXDB_DEFAULTFLAGS = EXC_SXDB_SAVEDATA | EXC_SXDB_ENABLE_REFRESH;
+
+const sal_uInt16 EXC_SXDB_BLOCKRECS = 0x1FFF;
+
+const sal_uInt16 EXC_SXDB_SRC_SHEET = 0x0001;
+const sal_uInt16 EXC_SXDB_SRC_EXTERN = 0x0002;
+const sal_uInt16 EXC_SXDB_SRC_CONSOLID = 0x0004;
+const sal_uInt16 EXC_SXDB_SRC_SCENARIO = 0x0008;
+
+// (0x00C7) SXFIELD -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFIELD = 0x00C7;
+
+const sal_uInt16 EXC_SXFIELD_HASITEMS = 0x0001;
+const sal_uInt16 EXC_SXFIELD_POSTPONE = 0x0002;
+const sal_uInt16 EXC_SXFIELD_CALCED = 0x0004;
+const sal_uInt16 EXC_SXFIELD_HASCHILD = 0x0008;
+const sal_uInt16 EXC_SXFIELD_NUMGROUP = 0x0010;
+const sal_uInt16 EXC_SXFIELD_16BIT = 0x0200;
+
+const sal_uInt16 EXC_SXFIELD_DATA_MASK = 0x0DE0;
+// known data types
+const sal_uInt16 EXC_SXFIELD_DATA_NONE = 0x0000; /// Special state for groupings.
+const sal_uInt16 EXC_SXFIELD_DATA_STR = 0x0480; /// Only strings, nothing else.
+const sal_uInt16 EXC_SXFIELD_DATA_INT = 0x0520; /// Only integers, opt. with doubles.
+const sal_uInt16 EXC_SXFIELD_DATA_DBL = 0x0560; /// Only doubles, nothing else.
+const sal_uInt16 EXC_SXFIELD_DATA_STR_INT = 0x05A0; /// Only strings and integers, opt. with doubles.
+const sal_uInt16 EXC_SXFIELD_DATA_STR_DBL = 0x05E0; /// Only strings and doubles, nothing else.
+const sal_uInt16 EXC_SXFIELD_DATA_DATE = 0x0900; /// Only dates, nothing else.
+const sal_uInt16 EXC_SXFIELD_DATA_DATE_EMP = 0x0980; /// Dates and empty strings, nothing else (?).
+const sal_uInt16 EXC_SXFIELD_DATA_DATE_NUM = 0x0D00; /// Dates with integers or doubles without strings.
+const sal_uInt16 EXC_SXFIELD_DATA_DATE_STR = 0x0D80; /// Dates and strings, opt. with integers or doubles.
+
+const sal_uInt16 EXC_SXFIELD_INDEX_MIN = 0; /// List index for minimum item in groupings.
+const sal_uInt16 EXC_SXFIELD_INDEX_MAX = 1; /// List index for maximum item in groupings.
+const sal_uInt16 EXC_SXFIELD_INDEX_STEP = 2; /// List index for step item in groupings.
+
+// (0x00C8) SXINDEXLIST -------------------------------------------------------
+const sal_uInt16 EXC_ID_SXINDEXLIST = 0x00C8;
+
+// (0x00C9) SXDOUBLE ----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDOUBLE = 0x00C9;
+
+// (0x00CA) SXBOOLEAN ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXBOOLEAN = 0x00CA;
+
+// (0x00CB) SXERROR -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXERROR = 0x00CB;
+
+// (0x00CC) SXINTEGER ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXINTEGER = 0x00CC;
+
+// (0x00CD) SXSTRING ----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXSTRING = 0x00CD;
+
+// (0x00CE) SXDATETIME --------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDATETIME = 0x00CE;
+
+// (0x00CF) SXEMPTY -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXEMPTY = 0x00CF;
+
+// (0x00D5) SXIDSTM -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SXIDSTM = 0x00D5;
+
+// (0x00D8) SXNUMGROUP --------------------------------------------------------
+const sal_uInt16 EXC_ID_SXNUMGROUP = 0x00D8;
+
+const sal_uInt16 EXC_SXNUMGROUP_AUTOMIN = 0x0001;
+const sal_uInt16 EXC_SXNUMGROUP_AUTOMAX = 0x0002;
+
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_SEC = 1;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_MIN = 2;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_HOUR = 3;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_DAY = 4;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_MONTH = 5;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_QUART = 6;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_YEAR = 7;
+const sal_uInt16 EXC_SXNUMGROUP_TYPE_NUM = 8;
+
+// (0x00D9) SXGROUPINFO -------------------------------------------------------
+const sal_uInt16 EXC_ID_SXGROUPINFO = 0x00D9;
+
+// (0x00DC) SXEXT -------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXEXT = 0x00DC;
+
+// (0x00E3) SXVS --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVS = 0x00E3;
+
+const sal_uInt16 EXC_SXVS_UNKNOWN = 0x0000;
+const sal_uInt16 EXC_SXVS_SHEET = 0x0001;
+const sal_uInt16 EXC_SXVS_EXTERN = 0x0002;
+const sal_uInt16 EXC_SXVS_CONSOLID = 0x0004;
+const sal_uInt16 EXC_SXVS_PIVOTTAB = 0x0008;
+const sal_uInt16 EXC_SXVS_SCENARIO = 0x0010;
+
+// (0x00F0) SXRULE ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXRULE = 0x00F0;
+
+// (0x00F1) SXEX --------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXEX = 0x00F1;
+
+const sal_uInt32 EXC_SXEX_DRILLDOWN = 0x00020000;
+const sal_uInt32 EXC_SXEX_DEFAULTFLAGS = 0x004F0200;
+
+// (0x00F2) SXFILT ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFILT = 0x00F2;
+
+// (0x00F5) -------------------------------------------------------------------
+const sal_uInt16 EXC_ID_00F5 = 0x00F5; /// Unknown record
+
+// (0x00F6) SXNAME ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXNAME = 0x00F6;
+
+// (0x00F8) SXPAIR ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXPAIR = 0x00F8;
+
+// (0x00F9) SXFMLA ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFMLA = 0x00F9;
+
+// (0x0100) SXVDEX ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVDEX = 0x0100;
+
+const sal_uInt32 EXC_SXVDEX_SHOWALL = 0x00000001;
+const sal_uInt32 EXC_SXVDEX_SORT = 0x00000200;
+const sal_uInt32 EXC_SXVDEX_SORT_ASC = 0x00000400;
+const sal_uInt32 EXC_SXVDEX_AUTOSHOW = 0x00000800;
+const sal_uInt32 EXC_SXVDEX_AUTOSHOW_ASC = 0x00001000;
+const sal_uInt32 EXC_SXVDEX_LAYOUT_REPORT = 0x00200000;
+const sal_uInt32 EXC_SXVDEX_LAYOUT_BLANK = 0x00400000;
+const sal_uInt32 EXC_SXVDEX_LAYOUT_TOP = 0x00800000;
+const sal_uInt32 EXC_SXVDEX_DEFAULTFLAGS = 0x0A00001E | EXC_SXVDEX_SORT_ASC | EXC_SXVDEX_AUTOSHOW_ASC;
+
+const sal_uInt16 EXC_SXVDEX_SORT_OWN = 0xFFFF;
+const sal_uInt16 EXC_SXVDEX_SHOW_NONE = 0xFFFF;
+const sal_uInt16 EXC_SXVDEX_FORMAT_NONE = 0x0000;
+
+// (0x0103) SXFORMULA ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFORMULA = 0x0103;
+
+// (0x0122) SXDBEX ------------------------------------------------------------
+const sal_uInt16 EXC_ID_SXDBEX = 0x0122;
+const double EXC_SXDBEX_CREATION_DATE = 51901.029652778;
+
+// (0x01BB) SXFDBTYPE ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXFDBTYPE = 0x01BB;
+const sal_uInt16 EXC_SXFDBTYPE_DEFAULT = 0x0000;
+
+// (0x0810) SXVIEWEX9 ---------------------------------------------------------
+const sal_uInt16 EXC_ID_SXVIEWEX9 = 0x0810;
+
+// (0x0864) SXADDL ("Pivot Table Additional Info") ----------------------------
+const sal_uInt16 EXC_ID_SXADDL = 0x0864;
+
+// Pivot cache
+
+/** Represents a data item of any type in a pivot cache. Supposed as base class for import and export. */
+class XclPCItem
+{
+public:
+ explicit XclPCItem();
+ virtual ~XclPCItem();
+
+ XclPCItem(XclPCItem const &) = default;
+ XclPCItem(XclPCItem &&) = default;
+ XclPCItem & operator =(XclPCItem const &) = default;
+ XclPCItem & operator =(XclPCItem &&) = default;
+
+ /** Sets the item to 'empty' type. */
+ void SetEmpty();
+ /** Sets the item to 'text' type and adds the passed text. */
+ void SetText( const OUString& rText );
+ /** Sets the item to 'double' type and adds the passed value. */
+ void SetDouble( double fValue, const OUString& rText = OUString() );
+ /** Sets the item to 'date/time' type and adds the passed date. */
+ void SetDateTime( const DateTime& rDateTime, const OUString& rText = OUString() );
+ /** Sets the item to 'integer' type and adds the passed value. */
+ void SetInteger( sal_Int16 nValue );
+ /** Sets the item to 'error' type and adds the passed Excel error code. */
+ void SetError( sal_uInt16 nError );
+ /** Sets the item to 'boolean' type and adds the passed Boolean value. */
+ void SetBool( bool bValue, const OUString& rText = OUString() );
+
+ /** Returns the text representation of the item. */
+ const OUString& ConvertToText() const { return maText; }
+
+ /** Returns true, if the passed term equals this item. */
+ bool IsEqual( const XclPCItem& rItem ) const;
+
+ /** Returns true, if the item type is 'empty'. */
+ bool IsEmpty() const;
+ /** Returns pointer to text, if the item type is 'text', otherwise 0. */
+ const OUString* GetText() const;
+ /** Returns pointer to value, if the item type is 'double', otherwise 0. */
+ const double* GetDouble() const;
+ /** Returns pointer to date, if the item type is 'date/time', otherwise 0. */
+ const DateTime* GetDateTime() const;
+ /** Returns pointer to integer, if the item type is 'integer', otherwise 0. */
+ const sal_Int16* GetInteger() const;
+ /** Returns pointer to error code, if the item type is 'error', otherwise 0. */
+ const sal_uInt16* GetError() const;
+ /** Returns pointer to Boolean value, if the item type is 'boolean', otherwise 0. */
+ const bool* GetBool() const;
+
+ /** Returns the type of the item */
+ XclPCItemType GetType() const;
+
+private:
+ XclPCItemType meType; /// Type of the item.
+ OUString maText; /// Text representation of the item.
+ DateTime maDateTime; /// Value of a date/time item.
+ union
+ {
+ double mfValue; /// Value of a floating-point item.
+ sal_Int16 mnValue; /// Value of an integer item.
+ sal_uInt16 mnError; /// Error code of an error item.
+ bool mbValue; /// Value of a boolean item.
+ };
+};
+
+inline bool operator==( const XclPCItem& rLeft, const XclPCItem& rRight ) { return rLeft.IsEqual( rRight ); }
+inline bool operator!=( const XclPCItem& rLeft, const XclPCItem& rRight ) { return !(rLeft == rRight); }
+
+// Field settings =============================================================
+
+/** Contains data for a pivot cache field (SXFIELD record). */
+struct XclPCFieldInfo
+{
+ OUString maName; /// Name of the pivot cache field.
+ sal_uInt16 mnFlags; /// Various flags.
+ sal_uInt16 mnGroupChild; /// Field containing grouping info for this field.
+ sal_uInt16 mnGroupBase; /// Base field if this field contains grouping info.
+ sal_uInt16 mnVisItems; /// Number of visible items for this field.
+ sal_uInt16 mnGroupItems; /// Number of special items in a grouping field.
+ sal_uInt16 mnBaseItems; /// Number of items in the base field.
+ sal_uInt16 mnOrigItems; /// Number of original source data items.
+
+ explicit XclPCFieldInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCFieldInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCFieldInfo& rInfo );
+
+// Numeric grouping field settings ============================================
+
+/** Contains data for a numeric grouping field (SXNUMGROUP record). */
+struct XclPCNumGroupInfo
+{
+ sal_uInt16 mnFlags; /// Various flags.
+
+ explicit XclPCNumGroupInfo();
+
+ void SetNumType();
+
+ sal_Int32 GetScDateType() const;
+ void SetScDateType( sal_Int32 nScType );
+
+ sal_uInt16 GetXclDataType() const;
+ void SetXclDataType( sal_uInt16 nXclType );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCNumGroupInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCNumGroupInfo& rInfo );
+
+// Base class for pivot cache fields ==========================================
+
+/** Represents a field in a pivot cache. Supposed as base class for import and export. */
+class XclPCField
+{
+public:
+ explicit XclPCField( XclPCFieldType eFieldType, sal_uInt16 nFieldIdx );
+ virtual ~XclPCField();
+
+ /** Returns the index of this field in the containing pivot cache. */
+ sal_uInt16 GetFieldIndex() const { return mnFieldIdx; }
+
+ /** Returns true, if the type of the field is supported by Calc. */
+ bool IsSupportedField() const;
+
+ /** Returns true, if this is a standard field build directly from source data. */
+ bool IsStandardField() const;
+
+ /** Returns true, if this field is a grouping field. */
+ bool IsStdGroupField() const;
+ /** Returns true, if this field is a numeric grouping field. */
+ bool IsNumGroupField() const;
+ /** Returns true, if this field is a date/time grouping field. */
+ bool IsDateGroupField() const;
+ /** Returns true, if this field is a grouping field of any type. */
+ bool IsGroupField() const;
+
+ /** Returns true, if this field has a child field in a grouping. */
+ bool IsGroupBaseField() const;
+ /** Returns true, if this field is a child field in a grouping (it has a base field). */
+ bool IsGroupChildField() const;
+
+ /** Returns true, if the field is based on a column in the source data area. */
+ bool HasOrigItems() const;
+ /** Returns true, if any items are stored after the SXFIELD record. */
+ bool HasInlineItems() const;
+ /** Returns true, if the items are stored separately after the last field. */
+ bool HasPostponedItems() const;
+ /** Returns true, if the item indexes in the SXINDEXLIST record are stored as 16-bit values. */
+ bool Has16BitIndexes() const;
+
+protected:
+ XclPCFieldInfo maFieldInfo; /// Pivot cache field info (SXFIELD record).
+ XclPCFieldType meFieldType; /// Type of this pivot cache field.
+ sal_uInt16 mnFieldIdx; /// Own field index in pivot cache.
+ ScfUInt16Vec maGroupOrder; /// Order of items in a grouping field (SXGROUPINFO record).
+ XclPCNumGroupInfo maNumGroupInfo; /// Info for numeric grouping (SXNUMGROUP record).
+};
+
+// Pivot cache settings =======================================================
+
+/** Contains data for a pivot cache (SXDB record). */
+struct XclPCInfo
+{
+ sal_uInt32 mnSrcRecs; /// Records in source database.
+ sal_uInt16 mnStrmId; /// Stream identifier.
+ sal_uInt16 mnFlags; /// Flags for the cache.
+ sal_uInt16 mnBlockRecs; /// Records in a source database block.
+ sal_uInt16 mnStdFields; /// Number of standard pivot cache fields.
+ sal_uInt16 mnTotalFields; /// Number of all fields (standard, grouped, calculated).
+ sal_uInt16 mnSrcType; /// Database type.
+ OUString maUserName; /// Name of user who last modified the cache.
+
+ explicit XclPCInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPCInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPCInfo& rInfo );
+
+// Pivot table
+
+// cached name ================================================================
+
+/** A name for various pivot table info structs. Includes 'use cache' state. */
+struct XclPTCachedName
+{
+ OUString maName; /// The visible name, if used.
+ bool mbUseCache; /// true = Use name in cache instead of maName.
+
+ explicit XclPTCachedName() : mbUseCache( true ) {}
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTCachedName& rCachedName );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTCachedName& rCachedName );
+
+/** Base struct for named info structs. Supports explicit naming and using the cache. */
+struct XclPTVisNameInfo
+{
+ XclPTCachedName maVisName; /// The displayed name of the item.
+
+ /** Returns true, if the name is set explicitly (maVisName.mbUseCache is false). */
+ bool HasVisName() const { return !maVisName.mbUseCache; }
+ /** Returns the name, if set explicitly (maVisName.mbUseCache is false). */
+ const OUString* GetVisName() const;
+ /** Sets the visible name and enables usage of cache if name is empty. */
+ void SetVisName( const OUString& rName );
+};
+
+// Field item settings ========================================================
+
+/** Contains data for a pivot table data item (SXVI record). */
+struct XclPTItemInfo : public XclPTVisNameInfo
+{
+ sal_uInt16 mnType; /// Type of the item (e.g. data, function, grand total).
+ sal_uInt16 mnFlags; /// Several flags.
+ sal_uInt16 mnCacheIdx; /// Index into cache for item name.
+
+ explicit XclPTItemInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTItemInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTItemInfo& rInfo );
+
+// General field settings =====================================================
+
+typedef ::std::vector< ScGeneralFunction > XclPTSubtotalVec;
+
+/** Contains data for a pivot table field (SXVD record). */
+struct XclPTFieldInfo : public XclPTVisNameInfo
+{
+ sal_uInt16 mnAxes; /// Flags for axes this field is part of.
+ sal_uInt16 mnSubtCount; /// Number of subtotal functions.
+ sal_uInt16 mnSubtotals; /// Bitfield for subtotal functions.
+ sal_uInt16 mnItemCount; /// Number of items of this field.
+ sal_uInt16 mnCacheIdx; /// Index into cache for field name (not part of record).
+
+ explicit XclPTFieldInfo();
+
+ /** Returns the API enum representing the orientation (first of row/col/page/data).
+ @param nMask Restricts the axes taken into account.
+ @return The first found axis orientation, that is allowed in nMask parameter. */
+ css::sheet::DataPilotFieldOrientation GetApiOrient( sal_uInt16 nMask ) const;
+ /** Adds the axis orientation represented by the passed API enum. */
+ void AddApiOrient( css::sheet::DataPilotFieldOrientation eOrient );
+
+ /** Returns a vector of all set subtotal functions. */
+ void GetSubtotals( XclPTSubtotalVec& rSubtotals ) const;
+ /** Sets the subtotal functions contained in the passed sequence. */
+ void SetSubtotals( const XclPTSubtotalVec& rSubtotals );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldInfo& rInfo );
+
+// Extended field settings ====================================================
+
+/** Contains extended data for a pivot table field (SXVDEX record). */
+struct XclPTFieldExtInfo
+{
+ sal_uInt32 mnFlags; /// Several flags and number of items for AutoShow.
+ sal_uInt16 mnSortField; /// Index to data field sorting bases on.
+ sal_uInt16 mnShowField; /// Index to data field AutoShow bases on.
+ sal_uInt16 mnNumFmt;
+ std::optional<OUString> mpFieldTotalName;
+
+ explicit XclPTFieldExtInfo();
+
+ /** Returns the API constant representing the sorting mode. */
+ sal_Int32 GetApiSortMode() const;
+ /** Sets the sorting mode represented by the passed API constant. */
+ void SetApiSortMode( sal_Int32 nSortMode );
+
+ /** Returns the API constant representing the AutoShow mode. */
+ sal_Int32 GetApiAutoShowMode() const;
+ /** Sets the AutoShow mode represented by the passed API constant. */
+ void SetApiAutoShowMode( sal_Int32 nShowMode );
+
+ /** Returns the number of items to be shown in AutoShow mode. */
+ sal_Int32 GetApiAutoShowCount() const;
+ /** Sets the number of items to be shown in AutoShow mode. */
+ void SetApiAutoShowCount( sal_Int32 nShowCount );
+
+ /** Returns the API constant representing the layout mode. */
+ sal_Int32 GetApiLayoutMode() const;
+ /** Sets the layout mode represented by the passed API constant. */
+ void SetApiLayoutMode( sal_Int32 nLayoutMode );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldExtInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldExtInfo& rInfo );
+
+// Page field settings ========================================================
+
+/** Contains data for a pivot table page field (part of SXPI record). */
+struct XclPTPageFieldInfo
+{
+ sal_uInt16 mnField; /// Base field for this page info.
+ sal_uInt16 mnSelItem; /// Index to selected item.
+ sal_uInt16 mnObjId; /// Escher object ID of dropdown listbox.
+
+ explicit XclPTPageFieldInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTPageFieldInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTPageFieldInfo& rInfo );
+
+// Data field settings ========================================================
+
+/** Contains data for a pivot table data field (SXDI record). */
+struct XclPTDataFieldInfo : public XclPTVisNameInfo
+{
+ sal_uInt16 mnField; /// Base field for this data info.
+ sal_uInt16 mnAggFunc; /// Data aggregation function.
+ sal_uInt16 mnRefType; /// Result reference type.
+ sal_uInt16 mnRefField; /// Index to SXVD of referred field used for the results.
+ sal_uInt16 mnRefItem; /// Index to SXVI of referred item of the used field.
+ sal_uInt16 mnNumFmt; /// Number format of the results.
+
+ explicit XclPTDataFieldInfo();
+
+ /** Returns the API enum representing the aggregation function. */
+ ScGeneralFunction GetApiAggFunc() const;
+ /** Sets the aggregation function represented by the passed API enum. */
+ void SetApiAggFunc( ScGeneralFunction eAggFunc );
+
+ /** Returns the API constant representing the result reference type. */
+ sal_Int32 GetApiRefType() const;
+ /** Sets the result reference type represented by the passed API constant. */
+ void SetApiRefType( sal_Int32 nRefType );
+
+ /** Returns the API constant representing the result reference item type. */
+ sal_Int32 GetApiRefItemType() const;
+ /** Sets the result reference item type represented by the passed API constant. */
+ void SetApiRefItemType( sal_Int32 nRefItemType );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTDataFieldInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTDataFieldInfo& rInfo );
+
+// Pivot table settings =======================================================
+
+/** Contains data for a pivot table (SXVIEW record). */
+struct XclPTInfo
+{
+ OUString maTableName; /// The name of the pivot table.
+ OUString maDataName; /// The visible name of the data field.
+ XclRange maOutXclRange; /// Output range.
+ XclAddress maDataXclPos; /// First cell containing data.
+ sal_uInt16 mnFirstHeadRow; /// First heading row.
+ sal_uInt16 mnCacheIdx; /// 0-based index of the pivot cache.
+ sal_uInt16 mnDataAxis; /// Orientation of data fields.
+ sal_uInt16 mnDataPos; /// Position of data fields.
+ sal_uInt16 mnFields; /// Number of all fields.
+ sal_uInt16 mnRowFields; /// Number of row fields.
+ sal_uInt16 mnColFields; /// Number of column fields.
+ sal_uInt16 mnPageFields; /// Number of page fields.
+ sal_uInt16 mnDataFields; /// Number of data fields.
+ sal_uInt16 mnDataRows; /// Number of rows containing data.
+ sal_uInt16 mnDataCols; /// Number of columns containing data.
+ sal_uInt16 mnFlags; /// Flags for the entire pivot table.
+ sal_uInt16 mnAutoFmtIdx; /// Index to pivot table autoformat.
+
+ explicit XclPTInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTInfo& rInfo );
+
+// Extended pivot table settings ==============================================
+
+/** Extended information about a pivot table (SXEX record). */
+struct XclPTExtInfo
+{
+ sal_uInt16 mnSxformulaRecs; /// Number of SXFORMULA records.
+ sal_uInt16 mnSxselectRecs; /// Number of SXSELECT records.
+ sal_uInt16 mnPagePerRow; /// Number of page fields per row.
+ sal_uInt16 mnPagePerCol; /// Number of page fields per column.
+ sal_uInt32 mnFlags; /// Flags for the entire pivot table.
+
+ explicit XclPTExtInfo();
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTExtInfo& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTExtInfo& rInfo );
+
+// Pivot table autoformat settings ==============================================
+
+/** Pivot table autoformat settings (SXVIEWEX9 record). */
+struct XclPTViewEx9Info
+{
+ sal_uInt32 mbReport; /// 2 for report* fmts ?
+ sal_uInt8 mnAutoFormat; /// AutoFormat ID
+ sal_uInt8 mnGridLayout; /// 0 == gridlayout, 0x10 == modern
+ OUString maGrandTotalName;
+
+ explicit XclPTViewEx9Info();
+ void Init( const ScDPObject& rDPObj );
+};
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclPTViewEx9Info& rInfo );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclPTViewEx9Info& rInfo );
+
+/** Additional pivot table settings (SXADDL record). */
+struct XclPTAddl
+{
+ bool mbCompactMode;
+ explicit XclPTAddl();
+};
+
+XclImpStream& operator>>(XclImpStream& rStrm, XclPTAddl& rInfo);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlroot.hxx b/sc/source/filter/inc/xlroot.hxx
new file mode 100644
index 000000000..3e6db3701
--- /dev/null
+++ b/sc/source/filter/inc/xlroot.hxx
@@ -0,0 +1,269 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <i18nlangtag/lang.h>
+#include <tools/ref.hxx>
+#include <tools/long.hxx>
+#include "xlconst.hxx"
+#include <memory>
+
+namespace com::sun::star::beans { struct NamedValue; }
+namespace comphelper { class IDocPasswordVerifier; }
+
+// Forward declarations of objects in public use ==============================
+
+class DateTime;
+class SotStorage;
+class SotStorageStream;
+
+// Global data ================================================================
+
+#ifdef DBG_UTIL
+/** Counts the number of created root objects. */
+struct XclDebugObjCounter
+{
+ sal_Int32 mnObjCnt;
+ explicit XclDebugObjCounter() : mnObjCnt( 0 ) {}
+ ~XclDebugObjCounter();
+};
+#endif
+
+class SfxMedium;
+class ScEditEngineDefaulter;
+class ScHeaderEditEngine;
+class EditEngine;
+class ScExtDocOptions;
+class XclFontPropSetHelper;
+class XclChPropSetHelper;
+class XclTracer;
+
+struct RootData;//!
+
+/** Stores global buffers and data needed elsewhere in the Excel filters. */
+struct XclRootData
+#ifdef DBG_UTIL
+ : public XclDebugObjCounter
+#endif
+{
+ typedef std::shared_ptr< ScEditEngineDefaulter > ScEEDefaulterRef;
+ typedef std::shared_ptr< ScHeaderEditEngine > ScHeaderEERef;
+ typedef std::shared_ptr< EditEngine > EditEngineRef;
+ typedef std::shared_ptr< XclFontPropSetHelper > XclFontPropSetHlpRef;
+ typedef std::shared_ptr< XclChPropSetHelper > XclChPropSetHlpRef;
+ typedef std::shared_ptr< ScExtDocOptions > ScExtDocOptRef;
+ typedef std::shared_ptr< XclTracer > XclTracerRef;
+ typedef std::shared_ptr< RootData > RootDataRef;
+
+ XclBiff meBiff; /// Current BIFF version.
+ XclOutput meOutput; /// Current Output format.
+ SfxMedium& mrMedium; /// The medium to import from.
+ tools::SvRef<SotStorage> mxRootStrg; /// The root OLE storage of imported/exported file.
+ ScDocument& mrDoc; /// The source or destination document.
+ OUString maDocUrl; /// Document URL of imported/exported file.
+ OUString maBasePath; /// Base path of imported/exported file (path of maDocUrl).
+ OUString maUserName; /// Current user name.
+ static constexpr OUStringLiteral gaDefPassword = u"VelvetSweatshop"; /// The default password used for stream encryption.
+ rtl_TextEncoding meTextEnc; /// Text encoding to import/export byte strings.
+ LanguageType meSysLang; /// System language.
+ LanguageType meDocLang; /// Document language (import: from file, export: from system).
+ LanguageType meUILang; /// UI language (import: from file, export: from system).
+ sal_Int16 mnDefApiScript; /// Default script type for blank cells (API constant).
+ ScAddress maScMaxPos; /// Highest Calc cell position.
+ ScAddress maXclMaxPos; /// Highest Excel cell position.
+ ScAddress maMaxPos; /// Highest position valid in Calc and Excel.
+
+ ScEEDefaulterRef mxEditEngine; /// Edit engine for rich strings etc.
+ ScHeaderEERef mxHFEditEngine; /// Edit engine for header/footer.
+ EditEngineRef mxDrawEditEng; /// Edit engine for text boxes.
+
+ XclFontPropSetHlpRef mxFontPropSetHlp; /// Property set helper for fonts.
+ XclChPropSetHlpRef mxChPropSetHlp; /// Property set helper for chart filter.
+
+ ScExtDocOptRef mxExtDocOpt; /// Extended document options.
+ XclTracerRef mxTracer; /// Filter tracer.
+ RootDataRef mxRD; /// Old RootData struct. Will be removed.
+
+ double mfScreenPixelX; /// Width of a screen pixel (1/100 mm).
+ double mfScreenPixelY; /// Height of a screen pixel (1/100 mm).
+ tools::Long mnCharWidth; /// Width of '0' in default font (twips).
+ tools::Long mnSpaceWidth; /// Width of space char ' ' using default font.
+ SCTAB mnScTab; /// Current Calc sheet index.
+ const bool mbExport; /// false = Import, true = Export.
+
+ explicit XclRootData( XclBiff eBiff, SfxMedium& rMedium,
+ tools::SvRef<SotStorage> const & xRootStrg, ScDocument& rDoc,
+ rtl_TextEncoding eTextEnc, bool bExport );
+ virtual ~XclRootData();
+};
+
+class SfxObjectShell;
+class ScModelObj;
+class OutputDevice;
+class SvNumberFormatter;
+class SdrPage;
+class ScStyleSheetPool;
+class ScRangeName;
+struct XclFontData;
+
+/** Access to global data for a filter object (imported or exported document) from other classes. */
+class XclRoot
+{
+public:
+ explicit XclRoot( XclRootData& rRootData );
+ XclRoot( const XclRoot& rRoot );
+
+ virtual ~XclRoot();
+
+ XclRoot& operator=( const XclRoot& rRoot );
+
+ /** Returns old RootData struct. Deprecated. */
+ RootData& GetOldRoot() const { return *mrData.mxRD; }
+
+ /** Returns the current BIFF version of the importer/exporter. */
+ XclBiff GetBiff() const { return mrData.meBiff; }
+ /** Returns the current output format of the importer/exporter. */
+ XclOutput GetOutput() const { return mrData.meOutput; }
+ /** Returns true, if currently a document is imported. */
+ bool IsImport() const { return !mrData.mbExport; }
+ /** Returns the text encoding to import/export byte strings. */
+ rtl_TextEncoding GetTextEncoding() const { return mrData.meTextEnc; }
+ /** Returns the system language, i.e. for number formats. */
+ LanguageType GetSysLanguage() const { return mrData.meSysLang; }
+ /** Returns the document language. */
+ LanguageType GetDocLanguage() const { return mrData.meDocLang; }
+ /** Returns the UI language. */
+ LanguageType GetUILanguage() const { return mrData.meUILang; }
+ /** Returns the default script type, e.g. for blank cells. */
+ sal_Int16 GetDefApiScript() const { return mrData.mnDefApiScript; }
+ /** Returns the width of the '0' character (default font) for the current printer (twips). */
+ tools::Long GetCharWidth() const { return mrData.mnCharWidth; }
+ tools::Long GetSpaceWidth() const { return mrData.mnSpaceWidth; }
+ /** Returns the current Calc sheet index. */
+ bool IsInGlobals() const { return mrData.mnScTab == SCTAB_GLOBAL; }
+ /** Returns the current Calc sheet index. */
+ SCTAB GetCurrScTab() const { return mrData.mnScTab; }
+
+ /** Calculates the width of the passed number of pixels in 1/100 mm. */
+ sal_Int32 GetHmmFromPixelX( double fPixelX ) const;
+ /** Calculates the height of the passed number of pixels in 1/100 mm. */
+ sal_Int32 GetHmmFromPixelY( double fPixelY ) const;
+
+ /** Returns the medium to import from. */
+ SfxMedium& GetMedium() const { return mrData.mrMedium; }
+ /** Returns the document URL of the imported/exported file. */
+ const OUString& GetDocUrl() const { return mrData.maDocUrl; }
+ /** Returns the base path of the imported/exported file. */
+ const OUString& GetBasePath() const { return mrData.maBasePath; }
+ /** Returns the current user name. */
+ const OUString& GetUserName() const { return mrData.maUserName; }
+
+ /** Returns the default password used for stream encryption. */
+ static OUString GetDefaultPassword() { return XclRootData::gaDefPassword; }
+ /** Requests and verifies a password from the medium or the user. */
+ css::uno::Sequence< css::beans::NamedValue >
+ RequestEncryptionData( ::comphelper::IDocPasswordVerifier& rVerifier ) const;
+
+ /** Returns the OLE2 root storage of the imported/exported file.
+ @return Pointer to root storage or 0, if the file is a simple stream. */
+ const tools::SvRef<SotStorage>& GetRootStorage() const { return mrData.mxRootStrg; }
+ /** Returns true, if the document contains a VBA storage. */
+ bool HasVbaStorage() const;
+
+ /** Tries to open a storage as child of the specified storage for reading or writing. */
+ tools::SvRef<SotStorage> OpenStorage( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrgName ) const;
+ /** Tries to open a storage as child of the root storage for reading or writing. */
+ tools::SvRef<SotStorage> OpenStorage( const OUString& rStrgName ) const;
+ /** Tries to open a new stream in the specified storage for reading or writing. */
+ tools::SvRef<SotStorageStream> OpenStream( tools::SvRef<SotStorage> const & xStrg, const OUString& rStrmName ) const;
+ /** Tries to open a new stream in the root storage for reading or writing. */
+ tools::SvRef<SotStorageStream> OpenStream( const OUString& rStrmName ) const;
+
+ /** Returns reference to the destination document (import) or source document (export). */
+ ScDocument& GetDoc() const;
+
+ /** Returns the object shell of the Calc document. May be 0 (i.e. import from clipboard). */
+ SfxObjectShell* GetDocShell() const;
+ /** Returns the object model of the Calc document. */
+ ScModelObj* GetDocModelObj() const;
+ /** Returns pointer to the printer of the Calc document. */
+ OutputDevice* GetPrinter() const;
+ /** Returns the style sheet pool of the Calc document. */
+ ScStyleSheetPool& GetStyleSheetPool() const;
+ /** Returns the defined names container of the Calc document. */
+ ScRangeName& GetNamedRanges() const;
+ /** Returns the drawing layer page of the passed sheet, if present. */
+ SdrPage* GetSdrPage( SCTAB nScTab ) const;
+
+ /** Returns the number formatter of the Calc document. */
+ SvNumberFormatter& GetFormatter() const;
+ /** Returns the null date of the current number formatter. */
+ DateTime GetNullDate() const;
+ /** Returns the base year depending on the current null date (1900 or 1904). */
+ sal_uInt16 GetBaseYear() const;
+ /** Converts a date/time value to a floating-point value. */
+ double GetDoubleFromDateTime( const DateTime& rDateTime ) const;
+ /** Converts a floating-point value to a date/time value. */
+ DateTime GetDateTimeFromDouble( double fValue ) const;
+
+ /** Returns the edit engine for import/export of rich strings etc. */
+ ScEditEngineDefaulter& GetEditEngine() const;
+ /** Returns the edit engine for import/export of headers/footers. */
+ ScHeaderEditEngine& GetHFEditEngine() const;
+ /** Returns the edit engine for import/export of drawing text boxes. */
+ EditEngine& GetDrawEditEngine() const;
+
+ /** Returns the property set helper for fonts. */
+ XclFontPropSetHelper& GetFontPropSetHelper() const;
+ /** Returns the property set helper for the chart filters. */
+ XclChPropSetHelper& GetChartPropSetHelper() const;
+
+ /** Returns the extended document options. */
+ ScExtDocOptions& GetExtDocOptions() const;
+ /** Returns the filter tracer. */
+ XclTracer& GetTracer() const;
+
+ /** Returns the highest possible cell address in a Calc document. */
+ const ScAddress& GetScMaxPos() const { return mrData.maScMaxPos; }
+ /** Returns the highest possible cell address in an Excel document (using current BIFF version). */
+ const ScAddress& GetXclMaxPos() const { return mrData.maXclMaxPos; }
+ /** Returns the highest possible cell address valid in Calc and Excel (using current BIFF version). */
+ const ScAddress& GetMaxPos() const { return mrData.maMaxPos; }
+
+ /** Sets the document language. */
+ void SetDocLanguage( LanguageType eLang ) { mrData.meDocLang = eLang; }
+ /** Sets the UI language, i.e. if it has been read from a file. */
+ void SetUILanguage( LanguageType eLang ) { mrData.meUILang = eLang; }
+ /** Sets the text encoding to import/export byte strings. */
+ void SetTextEncoding( rtl_TextEncoding eTextEnc );
+ /** Sets the width of the '0' - '9' digit character as well as the ' ' space char
+ (using the default font) for the current printer (twips).
+ @param rFontData The font used for the '0' character. */
+ void SetCharWidth( const XclFontData& rFontData );
+ /** Sets the current Calc sheet index. */
+ void SetCurrScTab( SCTAB nScTab ) { mrData.mnScTab = nScTab; }
+ /** Increases the current Calc sheet index by 1. */
+ void IncCurrScTab() { ++mrData.mnScTab; }
+
+private:
+ XclRootData& mrData; /// Reference to the global data struct.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlstream.hxx b/sc/source/filter/inc/xlstream.hxx
new file mode 100644
index 000000000..6e87890a1
--- /dev/null
+++ b/sc/source/filter/inc/xlstream.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <svx/svxerr.hxx>
+
+// Constants ==================================================================
+
+const std::size_t EXC_REC_SEEK_TO_BEGIN = 0;
+const std::size_t EXC_REC_SEEK_TO_END = static_cast<std::size_t>( -1 );
+
+const sal_uInt16 EXC_MAXRECSIZE_BIFF5 = 2080;
+const sal_uInt16 EXC_MAXRECSIZE_BIFF8 = 8224;
+
+const ErrCode EXC_ENCR_ERROR_UNSUPP_CRYPT = ERRCODE_SVX_READ_FILTER_CRYPT;
+const sal_uInt16 EXC_ENCR_BLOCKSIZE = 1024;
+
+const sal_uInt16 EXC_ID_UNKNOWN = SAL_MAX_UINT16;
+const sal_uInt16 EXC_ID_CONT = 0x003C;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlstring.hxx b/sc/source/filter/inc/xlstring.hxx
new file mode 100644
index 000000000..b346953ba
--- /dev/null
+++ b/sc/source/filter/inc/xlstring.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <sal/types.h>
+#include <o3tl/typed_flags_set.hxx>
+
+// Constants and enumerations =================================================
+
+/** Flags used to specify import/export mode of strings. */
+enum class XclStrFlags : sal_uInt16 {
+ NONE = 0x0000, /// Default string settings.
+ ForceUnicode = 0x0001, /// Always use UCS-2 characters (default: try to compress). BIFF8 only.
+ EightBitLength = 0x0002, /// 8-bit string length field (default: 16-bit).
+ SmartFlags = 0x0004, /// Omit flags on empty string (default: read/write always). BIFF8 only.
+ SeparateFormats = 0x0008, /// Import: Keep old formats when reading unformatted string (default: clear formats); Export: Write unformatted string.
+ NoHeader = 0x0010, /// Export: Don't write the length and flag fields.
+};
+namespace o3tl {
+ template<> struct typed_flags<XclStrFlags> : is_typed_flags<XclStrFlags, 0x001f> {};
+}
+
+
+const sal_uInt16 EXC_STR_MAXLEN_8BIT = 0x00FF;
+const sal_uInt16 EXC_STR_MAXLEN = 0x7FFF;
+
+const sal_uInt8 EXC_STRF_16BIT = 0x01;
+const sal_uInt8 EXC_STRF_FAREAST = 0x04;
+const sal_uInt8 EXC_STRF_RICH = 0x08;
+const sal_uInt8 EXC_STRF_UNKNOWN = 0xF2;
+
+// Fixed-size characters
+const sal_uInt8 EXC_LF_C = '\x0A'; /// LF character (used for line break).
+const sal_uInt16 EXC_LF = EXC_LF_C; /// LF character (unicode).
+const sal_uInt8 EXC_NUL_C = '\x00'; /// NUL character.
+const sal_uInt16 EXC_NUL = EXC_NUL_C; /// NUL character (unicode).
+
+// Rich-string formatting runs ================================================
+
+/** Represents a formatting run for rich-strings.
+
+ An Excel formatting run stores the first formatted character in a
+ rich-string and the index of a font used to format this and the following
+ characters.
+ */
+struct XclFormatRun
+{
+ sal_uInt16 mnChar; /// First character this format applies to.
+ sal_uInt16 mnFontIdx; /// Excel font index for the next characters.
+
+ explicit XclFormatRun() : mnChar( 0 ), mnFontIdx( 0 ) {}
+ explicit XclFormatRun( sal_uInt16 nChar, sal_uInt16 nFontIdx ) :
+ mnChar( nChar ), mnFontIdx( nFontIdx ) {}
+};
+
+inline bool operator==( const XclFormatRun& rLeft, const XclFormatRun& rRight )
+{
+ return (rLeft.mnChar == rRight.mnChar) && (rLeft.mnFontIdx == rRight.mnFontIdx);
+}
+
+inline bool operator<( const XclFormatRun& rLeft, const XclFormatRun& rRight )
+{
+ return (rLeft.mnChar < rRight.mnChar) || ((rLeft.mnChar == rRight.mnChar) && (rLeft.mnFontIdx < rRight.mnFontIdx));
+}
+
+/** A vector with all formatting runs for a rich-string. */
+typedef ::std::vector< XclFormatRun > XclFormatRunVec;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlstyle.hxx b/sc/source/filter/inc/xlstyle.hxx
new file mode 100644
index 000000000..098d2d334
--- /dev/null
+++ b/sc/source/filter/inc/xlstyle.hxx
@@ -0,0 +1,596 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <tools/color.hxx>
+#include <tools/fontenum.hxx>
+#include <editeng/svxenum.hxx>
+#include <editeng/frmdir.hxx>
+#include <svl/zforlist.hxx>
+#include "fapihelper.hxx"
+
+class XclRoot;
+
+// Constants and Enumerations =================================================
+
+// Line styles ----------------------------------------------------------------
+
+const sal_uInt8 EXC_LINE_NONE = 0x00;
+const sal_uInt8 EXC_LINE_THIN = 0x01;
+const sal_uInt8 EXC_LINE_MEDIUM = 0x02;
+const sal_uInt8 EXC_LINE_DASHED = 0x03;
+const sal_uInt8 EXC_LINE_DOTTED = 0x04;
+const sal_uInt8 EXC_LINE_THICK = 0x05;
+const sal_uInt8 EXC_LINE_DOUBLE = 0x06;
+const sal_uInt8 EXC_LINE_HAIR = 0x07;
+const sal_uInt8 EXC_LINE_MEDIUM_DASHED = 0x08;
+const sal_uInt8 EXC_LINE_THIN_DASHDOT = 0x09;
+const sal_uInt8 EXC_LINE_MEDIUM_DASHDOT = 0x0A;
+const sal_uInt8 EXC_LINE_THIN_DASHDOTDOT = 0x0B;
+const sal_uInt8 EXC_LINE_MEDIUM_DASHDOTDOT = 0x0C;
+const sal_uInt8 EXC_LINE_MEDIUM_SLANT_DASHDOT = 0x0D;
+
+// Background patterns --------------------------------------------------------
+
+const sal_uInt8 EXC_PATT_NONE = 0x00;
+const sal_uInt8 EXC_PATT_SOLID = 0x01;
+const sal_uInt8 EXC_PATT_50_PERC = 0x02;
+const sal_uInt8 EXC_PATT_75_PERC = 0x03;
+const sal_uInt8 EXC_PATT_25_PERC = 0x04;
+const sal_uInt8 EXC_PATT_12_5_PERC = 0x11;
+const sal_uInt8 EXC_PATT_6_25_PERC = 0x12;
+
+// (0x001E, 0x041E) FORMAT ----------------------------------------------------
+
+const sal_uInt16 EXC_ID2_FORMAT = 0x001E;
+const sal_uInt16 EXC_ID4_FORMAT = 0x041E;
+
+const sal_uInt16 EXC_FORMAT_OFFSET5 = 164;
+const sal_uInt16 EXC_FORMAT_OFFSET8 = 164;
+const sal_uInt16 EXC_FORMAT_NOTFOUND = 0xFFFF;
+
+// (0x0031) FONT --------------------------------------------------------------
+
+const sal_uInt16 EXC_ID2_FONT = 0x0031;
+const sal_uInt16 EXC_ID3_FONT = 0x0231;
+
+const sal_uInt16 EXC_FONT_APP = 0; /// Application font index.
+const sal_uInt16 EXC_FONT_NOTFOUND = 0xFFFF;
+
+const size_t EXC_FONT_MAXCOUNT4 = 0x00FF;
+const size_t EXC_FONT_MAXCOUNT5 = 0x00FF;
+const size_t EXC_FONT_MAXCOUNT8 = 0xFFFF;
+
+// families
+const sal_uInt8 EXC_FONTFAM_DONTKNOW = 0x00;
+const sal_uInt8 EXC_FONTFAM_ROMAN = 0x01;
+const sal_uInt8 EXC_FONTFAM_SWISS = 0x02;
+const sal_uInt8 EXC_FONTFAM_SYSTEM = EXC_FONTFAM_SWISS;
+const sal_uInt8 EXC_FONTFAM_MODERN = 0x03;
+const sal_uInt8 EXC_FONTFAM_SCRIPT = 0x04;
+const sal_uInt8 EXC_FONTFAM_DECORATIVE = 0x05;
+
+// charsets
+const sal_uInt8 EXC_FONTCSET_ANSI_LATIN = 0x00;
+
+// attributes
+const sal_uInt16 EXC_FONTATTR_NONE = 0x0000;
+const sal_uInt16 EXC_FONTATTR_BOLD = 0x0001;
+const sal_uInt16 EXC_FONTATTR_ITALIC = 0x0002;
+const sal_uInt16 EXC_FONTATTR_UNDERLINE = 0x0004;
+const sal_uInt16 EXC_FONTATTR_STRIKEOUT = 0x0008;
+const sal_uInt16 EXC_FONTATTR_OUTLINE = 0x0010;
+const sal_uInt16 EXC_FONTATTR_SHADOW = 0x0020;
+
+// weight
+const sal_uInt16 EXC_FONTWGHT_DONTKNOW = 0;
+const sal_uInt16 EXC_FONTWGHT_THIN = 100;
+const sal_uInt16 EXC_FONTWGHT_ULTRALIGHT = 200;
+const sal_uInt16 EXC_FONTWGHT_LIGHT = 300;
+const sal_uInt16 EXC_FONTWGHT_SEMILIGHT = 350;
+const sal_uInt16 EXC_FONTWGHT_NORMAL = 400;
+const sal_uInt16 EXC_FONTWGHT_MEDIUM = 500;
+const sal_uInt16 EXC_FONTWGHT_SEMIBOLD = 600;
+const sal_uInt16 EXC_FONTWGHT_BOLD = 700;
+const sal_uInt16 EXC_FONTWGHT_ULTRABOLD = 800;
+const sal_uInt16 EXC_FONTWGHT_BLACK = 900;
+
+// underline
+const sal_uInt8 EXC_FONTUNDERL_NONE = 0x00;
+const sal_uInt8 EXC_FONTUNDERL_SINGLE = 0x01;
+const sal_uInt8 EXC_FONTUNDERL_DOUBLE = 0x02;
+const sal_uInt8 EXC_FONTUNDERL_SINGLE_ACC = 0x21;
+const sal_uInt8 EXC_FONTUNDERL_DOUBLE_ACC = 0x22;
+
+// escapement
+const sal_uInt16 EXC_FONTESC_NONE = 0x00;
+const sal_uInt16 EXC_FONTESC_SUPER = 0x01;
+const sal_uInt16 EXC_FONTESC_SUB = 0x02;
+
+// (0x0043, 0x0243, 0x0443, 0x00E0) XF ----------------------------------------
+
+const sal_uInt16 EXC_ID2_XF = 0x0043;
+const sal_uInt16 EXC_ID3_XF = 0x0243;
+const sal_uInt16 EXC_ID4_XF = 0x0443;
+const sal_uInt16 EXC_ID5_XF = 0x00E0;
+
+const sal_uInt32 EXC_XF_MAXCOUNT = 4050; /// Maximum number of all XF records.
+const sal_uInt32 EXC_XF_MAXSTYLECOUNT = 1536; /// Arbitrary maximum number of style XFs.
+const sal_uInt16 EXC_XF_DEFAULTSTYLE = 0; /// Excel index to default style XF.
+const sal_uInt16 EXC_XF_DEFAULTCELL = 15; /// Excel index to default cell XF.
+const sal_uInt16 EXC_XF_NOTFOUND = 0xFFFF; /// Special index for "not found" state.
+
+const sal_uInt32 EXC_XFID_NOTFOUND = 0xFFFFFFFF;
+
+const sal_uInt16 EXC_XF_LOCKED = 0x0001;
+const sal_uInt16 EXC_XF_HIDDEN = 0x0002;
+const sal_uInt16 EXC_XF_STYLE = 0x0004;
+const sal_uInt16 EXC_XF_STYLEPARENT = 0x0FFF; /// Styles don't have a parent.
+const sal_uInt16 EXC_XF_LINEBREAK = 0x0008; /// Automatic line break.
+const sal_uInt16 EXC_XF_SHRINK = 0x0010; /// Shrink to fit into cell.
+
+const sal_uInt8 EXC_XF_DIFF_VALFMT = 0x01;
+const sal_uInt8 EXC_XF_DIFF_FONT = 0x02;
+const sal_uInt8 EXC_XF_DIFF_ALIGN = 0x04;
+const sal_uInt8 EXC_XF_DIFF_BORDER = 0x08;
+const sal_uInt8 EXC_XF_DIFF_AREA = 0x10;
+const sal_uInt8 EXC_XF_DIFF_PROT = 0x20;
+
+const sal_uInt8 EXC_XF_HOR_GENERAL = 0x00;
+const sal_uInt8 EXC_XF_HOR_LEFT = 0x01;
+const sal_uInt8 EXC_XF_HOR_CENTER = 0x02;
+const sal_uInt8 EXC_XF_HOR_RIGHT = 0x03;
+const sal_uInt8 EXC_XF_HOR_FILL = 0x04;
+const sal_uInt8 EXC_XF_HOR_JUSTIFY = 0x05;
+const sal_uInt8 EXC_XF_HOR_CENTER_AS = 0x06;
+const sal_uInt8 EXC_XF_HOR_DISTRIB = 0x07;
+
+const sal_uInt8 EXC_XF_VER_TOP = 0x00;
+const sal_uInt8 EXC_XF_VER_CENTER = 0x01;
+const sal_uInt8 EXC_XF_VER_BOTTOM = 0x02;
+const sal_uInt8 EXC_XF_VER_JUSTIFY = 0x03;
+const sal_uInt8 EXC_XF_VER_DISTRIB = 0x04;
+
+const sal_uInt8 EXC_XF_TEXTDIR_CONTEXT = 0x00;
+const sal_uInt8 EXC_XF_TEXTDIR_LTR = 0x01;
+const sal_uInt8 EXC_XF_TEXTDIR_RTL = 0x02;
+
+const sal_uInt8 EXC_XF2_VALFMT_MASK = 0x3F;
+const sal_uInt8 EXC_XF2_LOCKED = 0x40;
+const sal_uInt8 EXC_XF2_HIDDEN = 0x80;
+const sal_uInt8 EXC_XF2_LEFTLINE = 0x08;
+const sal_uInt8 EXC_XF2_RIGHTLINE = 0x10;
+const sal_uInt8 EXC_XF2_TOPLINE = 0x20;
+const sal_uInt8 EXC_XF2_BOTTOMLINE = 0x40;
+const sal_uInt8 EXC_XF2_BACKGROUND = 0x80;
+
+const sal_uInt16 EXC_XF8_SHRINK = 0x0010; /// Shrink to fit into cell.
+const sal_uInt16 EXC_XF8_MERGE = 0x0020;
+
+const sal_uInt32 EXC_XF_DIAGONAL_TL_TO_BR = 0x40000000; /// Top-left to bottom-right.
+const sal_uInt32 EXC_XF_DIAGONAL_BL_TO_TR = 0x80000000; /// Bottom-left to top-right.
+const sal_uInt32 EXC_XF_DIAGONAL_BOTH = 0xC0000000; /// Both.
+
+// (0x0045) EFONT -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_EFONT = 0x0045;
+
+// (0x0092) PALETTE -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_PALETTE = 0x0092;
+
+const sal_uInt16 EXC_COLOR_BIFF2_BLACK = 0;
+const sal_uInt16 EXC_COLOR_BIFF2_WHITE = 1;
+
+const sal_uInt16 EXC_COLOR_USEROFFSET = 8; /// First user defined color.
+const sal_uInt16 EXC_COLOR_WINDOWTEXT3 = 24; /// System window text color (BIFF3-BIFF4).
+const sal_uInt16 EXC_COLOR_WINDOWBACK3 = 25; /// System window background color (BIFF3-BIFF4).
+const sal_uInt16 EXC_COLOR_WINDOWTEXT = 64; /// System window text color (>=BIFF5).
+const sal_uInt16 EXC_COLOR_WINDOWBACK = 65; /// System window background color (>=BIFF5).
+const sal_uInt16 EXC_COLOR_BUTTONBACK = 67; /// System button background color (face color).
+const sal_uInt16 EXC_COLOR_CHWINDOWTEXT = 77; /// Chart window text color (BIFF8 charts).
+const sal_uInt16 EXC_COLOR_CHWINDOWBACK = 78; /// Chart window background color (BIFF8 charts).
+const sal_uInt16 EXC_COLOR_CHBORDERAUTO = 79; /// Automatic frame border for series (BIFF8 charts).
+const sal_uInt16 EXC_COLOR_NOTEBACK = 80; /// Note background color.
+const sal_uInt16 EXC_COLOR_NOTETEXT = 81; /// Note text color.
+const sal_uInt16 EXC_COLOR_FONTAUTO = 0x7FFF; /// Font auto color (system window text color).
+
+// (0x0293) STYLE -------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_STYLE = 0x0293;
+
+const sal_uInt16 EXC_STYLE_BUILTIN = 0x8000;
+const sal_uInt16 EXC_STYLE_XFMASK = 0x0FFF;
+
+const sal_uInt8 EXC_STYLE_NORMAL = 0x00; /// "Normal" style.
+const sal_uInt8 EXC_STYLE_ROWLEVEL = 0x01; /// "RowLevel_*" styles.
+const sal_uInt8 EXC_STYLE_COLLEVEL = 0x02; /// "ColLevel_*" styles.
+const sal_uInt8 EXC_STYLE_COMMA = 0x03; /// "Comma" style.
+const sal_uInt8 EXC_STYLE_CURRENCY = 0x04; /// "Currency" style.
+const sal_uInt8 EXC_STYLE_PERCENT = 0x05; /// "Percent" style.
+const sal_uInt8 EXC_STYLE_COMMA_0 = 0x06; /// "Comma [0]" style.
+const sal_uInt8 EXC_STYLE_CURRENCY_0 = 0x07; /// "Currency [0]" style.
+const sal_uInt8 EXC_STYLE_HYPERLINK = 0x08; /// "Hyperlink" style.
+const sal_uInt8 EXC_STYLE_FOLLOWED_HYPERLINK= 0x09; /// "Followed_Hyperlink" style.
+const sal_uInt8 EXC_STYLE_USERDEF = 0xFF; /// No built-in style.
+
+const sal_uInt8 EXC_STYLE_LEVELCOUNT = 7; /// Number of outline level styles.
+const sal_uInt8 EXC_STYLE_NOLEVEL = 0xFF; /// Default value for unused level.
+
+// (0x0892) STYLEEXT ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_STYLEEXT = 0x0892;
+
+const sal_uInt8 EXC_STYLEEXT_BUILTIN = 0x01;
+const sal_uInt8 EXC_STYLEEXT_HIDDEN = 0x02;
+const sal_uInt8 EXC_STYLEEXT_CUSTOM = 0x04;
+
+// Color data =================================================================
+
+/** Stores all default colors for a specific BIFF version. */
+class XclDefaultPalette
+{
+public:
+ explicit XclDefaultPalette( const XclRoot& rRoot );
+
+ /** Returns the color count in the current palette. */
+ sal_uInt32 GetColorCount() const { return mnTableSize - EXC_COLOR_USEROFFSET; }
+
+ /** Returns the default color for a (non-zero-based) Excel color or COL_AUTO on error. */
+ Color GetDefColor( sal_uInt16 nXclIndex ) const;
+
+ /** Returns true, if the passed Excel color index is a system color. */
+ bool IsSystemColor( sal_uInt16 nXclIndex ) const { return nXclIndex >= mnTableSize; }
+
+private:
+ const Color* mpnColorTable; /// The table with RGB values.
+ Color mnFaceColor; /// System button background color.
+ Color mnNoteText; /// Note text color.
+ Color mnNoteBack; /// Note background color.
+ sal_uInt32 mnTableSize; /// The color table size.
+};
+
+// Font data ==================================================================
+
+namespace vcl { class Font; }
+class SvxFont;
+
+/** This struct helps reading and writing Excel fonts.
+
+ It stores all Excel compatible properties of a font. In detail this is the
+ name, family, character set, height, color, boldness, posture, script,
+ underline, strikeout, outline and shadow of the font.
+ */
+struct XclFontData
+{
+ OUString maName; /// Font name.
+ OUString maStyle; /// String with styles (bold, italic).
+ Color maColor; /// Font color.
+ sal_uInt16 mnHeight; /// Font height in twips (1/20 of a point).
+ sal_uInt16 mnWeight; /// Boldness: 400=normal, 700=bold.
+ sal_uInt16 mnEscapem; /// Escapement type.
+ sal_uInt8 mnFamily; /// Windows font family.
+ sal_uInt8 mnCharSet; /// Windows character set.
+ sal_uInt8 mnUnderline; /// Underline style.
+ bool mbItalic; /// true = Italic.
+ bool mbStrikeout; /// true = Struck out.
+ bool mbOutline; /// true = Outlined.
+ bool mbShadow; /// true = Shadowed.
+
+ /** Constructs an empty font data structure. */
+ explicit XclFontData();
+ /** Constructs a font data structure and fills it with the passed font attributes (except color). */
+ explicit XclFontData( const vcl::Font& rFont );
+ /** As directly above but also fills in the escapement member. */
+ explicit XclFontData( const SvxFont& rFont );
+
+ /** Resets all members to default (empty) values. */
+ void Clear();
+ /** Fills all members (except color and escapement) from the passed font. */
+ void FillFromVclFont( const vcl::Font& rFont );
+ /** Fills all members (except color) from the passed SVX font. */
+ void FillFromSvxFont( const SvxFont& rFont );
+
+// *** conversion of VCL/SVX constants *** ------------------------------------
+
+ /** Returns the Calc font family. */
+ FontFamily GetScFamily( rtl_TextEncoding eDefTextEnc ) const;
+ /** Returns the font text encoding. */
+ rtl_TextEncoding GetFontEncoding() const;
+ /** Returns the Calc font posture. */
+ FontItalic GetScPosture() const;
+ /** Returns the Calc font weight. */
+ FontWeight GetScWeight() const;
+ /** Returns the Calc font underline style. */
+ FontLineStyle GetScUnderline() const;
+ /** Returns the Calc escapement style. */
+ SvxEscapement GetScEscapement() const;
+ /** Returns the Calc strike-out style. */
+ FontStrikeout GetScStrikeout() const;
+
+ /** Sets the Calc font height (in twips). */
+ void SetScHeight( sal_Int32 nTwips );
+ /** Sets the Calc font family. */
+ void SetScFamily( FontFamily eScFamily );
+ /** Sets the font text encoding. */
+ void SetFontEncoding( rtl_TextEncoding eFontEnc );
+ /** Sets the Calc font posture. */
+ void SetScPosture( FontItalic eScPosture );
+ /** Sets the Calc font weight. */
+ void SetScWeight( FontWeight eScWeight );
+ /** Sets the Calc underline style. */
+ void SetScUnderline( FontLineStyle eScUnderl );
+ /** Sets the Calc escapement style. */
+ void SetScEscapement( short nScEscapem );
+ /** Sets the Calc strike-out style. */
+ void SetScStrikeout( FontStrikeout eScStrikeout );
+
+// *** conversion of API constants *** ----------------------------------------
+
+ /** Returns the API font height. */
+ float GetApiHeight() const;
+ /** Returns the API font family. */
+ sal_Int16 GetApiFamily() const;
+ /** Returns the API font text encoding. */
+ sal_Int16 GetApiFontEncoding() const;
+ /** Returns the API font posture. */
+ css::awt::FontSlant GetApiPosture() const;
+ /** Returns the API font weight. */
+ float GetApiWeight() const;
+ /** Returns the API font underline style. */
+ sal_Int16 GetApiUnderline() const;
+ /** Returns the API escapement style. */
+ sal_Int16 GetApiEscapement() const;
+ /** Returns the API font strike-out style. */
+ sal_Int16 GetApiStrikeout() const;
+
+ /** Sets the API font height. */
+ void SetApiHeight( float fPoint );
+ /** Sets the API font family. */
+ void SetApiFamily( sal_Int16 nApiFamily );
+ /** Sets the API font posture. */
+ void SetApiPosture( css::awt::FontSlant eApiPosture );
+ /** Sets the API font weight. */
+ void SetApiWeight( float fApiWeight );
+ /** Sets the API font underline style. */
+ void SetApiUnderline( sal_Int16 nApiUnderl );
+ /** Sets the API escapement style. */
+ void SetApiEscapement( sal_Int16 nApiEscapem );
+ /** Sets the API font strike-out style. */
+ void SetApiStrikeout( sal_Int16 nApiStrikeout );
+};
+
+bool operator==( const XclFontData& rLeft, const XclFontData& rRight );
+
+/** Enumerates different types of Which-IDs for font items. */
+enum class XclFontItemType
+{
+ Cell, /// Use Calc Which-IDs (ATTR_*).
+ Editeng, /// Use edit engine Which-IDs (EE_CHAR_*).
+ HeaderFooter /// Use header/footer edit engine Which-IDs (EE_CHAR_*).
+};
+
+/** Enumerates different types for objects with font settings (using different property names). */
+enum XclFontPropSetType
+{
+ EXC_FONTPROPSET_CHART, /// All text objects in charts.
+ EXC_FONTPROPSET_CONTROL /// Text formatting in form controls.
+};
+
+/** Helper class for usage of property sets. */
+class XclFontPropSetHelper
+{
+public:
+ explicit XclFontPropSetHelper();
+
+ /** Reads all font properties from the passed property set. */
+ void ReadFontProperties( XclFontData& rFontData,
+ const ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ sal_Int16 nScript = -1 );
+
+ /** Writes all font properties to the passed property set, uses passed color as font color. */
+ void WriteFontProperties(
+ ScfPropertySet& rPropSet, XclFontPropSetType eType,
+ const XclFontData& rFontData,
+ bool bHasWstrn, bool bHasAsian, bool bHasCmplx,
+ const Color* pFontColor );
+
+private:
+ /** Returns a chart property set helper according to the passed script type. */
+ ScfPropSetHelper& GetChartHelper( sal_Int16 nScript );
+
+private:
+ ScfPropSetHelper maHlpChCommon; /// Chart properties for all scripts.
+ ScfPropSetHelper maHlpChWstrn; /// Chart properties for Western script.
+ ScfPropSetHelper maHlpChAsian; /// Chart properties for Asian script.
+ ScfPropSetHelper maHlpChCmplx; /// Chart properties for Complex script.
+ ScfPropSetHelper maHlpChWstrnNoName; /// Chart properties for Western script, no font name.
+ ScfPropSetHelper maHlpChAsianNoName; /// Chart properties for Asian script, no font name.
+ ScfPropSetHelper maHlpChCmplxNoName; /// Chart properties for Complex script, no font name.
+ ScfPropSetHelper maHlpChEscapement; /// Chart properties for font escapement.
+ ScfPropSetHelper maHlpControl; /// Properties for form controls.
+};
+
+// Number formats =============================================================
+
+struct XclNumFmt
+{
+ OUString maFormat; /// Format string, may be empty (meOffset used then).
+ NfIndexTableOffset meOffset; /// SvNumberFormatter format index, if maFormat is empty.
+ LanguageType meLanguage; /// Language type to be set with the number format.
+};
+
+class XclNumFmtBuffer
+{
+public:
+ explicit XclNumFmtBuffer( const XclRoot& rRoot );
+
+ /** Returns the core index of the current standard number format. */
+ sal_uInt32 GetStdScNumFmt() const { return mnStdScNumFmt; }
+
+protected:
+ typedef ::std::map< sal_uInt16, XclNumFmt > XclNumFmtMap;
+
+ /** Clears all buffered data, used to set up for a new sheet. */
+ void InitializeImport();
+
+ /** Returns the current number format map. */
+ const XclNumFmtMap& GetFormatMap() const { return maFmtMap; }
+
+ /** Inserts a new number format for the specified Excel format index. */
+ void InsertFormat( sal_uInt16 nXclNumFmt, const OUString& rFormat );
+
+private:
+ /** Inserts built-in number formats for the current system language. */
+ void InsertBuiltinFormats();
+
+ XclNumFmtMap maFmtMap; /// Map containing all default and user-defined formats.
+ const LanguageType meSysLang; /// Current system language.
+ const sal_uInt32 mnStdScNumFmt; /// Calc format key for standard number format.
+};
+
+// Cell formatting data (XF) ==================================================
+
+/** Contains all cell protection attributes. */
+struct XclCellProt
+{
+ bool mbLocked; /// true = Locked against editing.
+ bool mbHidden; /// true = Formula is hidden.
+
+ explicit XclCellProt();
+};
+
+bool operator==( const XclCellProt& rLeft, const XclCellProt& rRight );
+
+/** Contains all cell alignment attributes. */
+struct XclCellAlign
+{
+ sal_uInt8 mnHorAlign; /// Horizontal alignment.
+ sal_uInt8 mnVerAlign; /// Vertical alignment.
+ sal_uInt8 mnOrient; /// Text orientation.
+ sal_uInt8 mnTextDir; /// CTL text direction.
+ sal_uInt8 mnRotation; /// Text rotation angle.
+ sal_uInt8 mnIndent; /// Indentation.
+ bool mbLineBreak; /// true = Multi-line text.
+ bool mbShrink; /// true = Shrink to fit cell size.
+
+ explicit XclCellAlign();
+
+ /** Returns the Calc horizontal alignment. */
+ SvxCellHorJustify GetScHorAlign() const;
+ /** Returns horizontal justification method as Calc's attribute. */
+ SvxCellJustifyMethod GetScHorJustifyMethod() const;
+ /** Returns the Calc vertical alignment. */
+ SvxCellVerJustify GetScVerAlign() const;
+ /** Returns vertical justification method as Calc's attribute. */
+ SvxCellJustifyMethod GetScVerJustifyMethod() const;
+ /** Returns the Calc frame direction. */
+ SvxFrameDirection GetScFrameDir() const;
+
+ /** Sets the Calc horizontal alignment. */
+ void SetScHorAlign( SvxCellHorJustify eHorJust );
+ /** Sets the Calc vertical alignment. */
+ void SetScVerAlign( SvxCellVerJustify eVerJust );
+ /** Sets the Calc frame direction. */
+ void SetScFrameDir( SvxFrameDirection eFrameDir );
+};
+
+bool operator==( const XclCellAlign& rLeft, const XclCellAlign& rRight );
+
+/** Contains color and line style for each cell border line. */
+struct XclCellBorder
+{
+ sal_uInt16 mnLeftColor; /// Palette index for left line.
+ sal_uInt16 mnRightColor; /// Palette index for right line.
+ sal_uInt16 mnTopColor; /// Palette index for top line.
+ sal_uInt16 mnBottomColor; /// Palette index for bottom line.
+ sal_uInt16 mnDiagColor; /// Palette index for diagonal line(s).
+ sal_uInt8 mnLeftLine; /// Style of left line.
+ sal_uInt8 mnRightLine; /// Style of right line.
+ sal_uInt8 mnTopLine; /// Style of top line.
+ sal_uInt8 mnBottomLine; /// Style of bottom line.
+ sal_uInt8 mnDiagLine; /// Style of diagonal line(s).
+ bool mbDiagTLtoBR; /// true = Top-left to bottom-right on.
+ bool mbDiagBLtoTR; /// true = Bottom-left to top-right on.
+
+ explicit XclCellBorder();
+};
+
+bool operator==( const XclCellBorder& rLeft, const XclCellBorder& rRight );
+
+/** Contains background colors and pattern for a cell. */
+struct XclCellArea
+{
+ sal_uInt16 mnForeColor; /// Palette index to foreground color.
+ sal_uInt16 mnBackColor; /// Palette index to background color.
+ sal_uInt8 mnPattern; /// Fill pattern.
+
+ explicit XclCellArea();
+ explicit XclCellArea(sal_uInt8 nPattern);
+
+ /** Returns true, if the area represents transparent state. */
+ bool IsTransparent() const;
+};
+
+bool operator==( const XclCellArea& rLeft, const XclCellArea& rRight );
+
+/** Contains base members for XF record import/export.
+ @descr In detail this class stores the XF type (cell/style), the index to the
+ parent style XF and all "attribute used" flags, which reflect the state of
+ specific attribute groups (true = user has changed the attributes). */
+class XclXFBase
+{
+public:
+ explicit XclXFBase( bool bCellXF );
+ virtual ~XclXFBase();
+
+ XclXFBase(XclXFBase const &) = default;
+ XclXFBase(XclXFBase &&) = default;
+ XclXFBase & operator =(XclXFBase const &) = default;
+ XclXFBase & operator =(XclXFBase &&) = default;
+
+ /** Sets all "attribute used" flags to the passed state. */
+ void SetAllUsedFlags( bool bUsed );
+ /** Returns true, if any "attribute used" flags are ste in this XF. */
+ bool HasUsedFlags() const;
+
+ /** Returns true, if this is a hard cell format. */
+ bool IsCellXF() const { return mbCellXF; }
+ /** Returns true, if this is a cell style. */
+ bool IsStyleXF() const { return !IsCellXF(); }
+
+protected:
+ /** Returns true, if this object is equal to the passed. */
+ bool Equals( const XclXFBase& rCmp ) const;
+
+protected:
+ sal_uInt16 mnParent; /// Index to parent style XF.
+ bool mbCellXF; /// true = cell XF, false = style XF.
+ bool mbProtUsed; /// true = cell protection used.
+ bool mbFontUsed; /// true = font index used.
+ bool mbFmtUsed; /// true = number format used.
+ bool mbAlignUsed; /// true = alignment used.
+ bool mbBorderUsed; /// true = border data used.
+ bool mbAreaUsed; /// true = area data used.
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xltable.hxx b/sc/source/filter/inc/xltable.hxx
new file mode 100644
index 000000000..d5e210a4a
--- /dev/null
+++ b/sc/source/filter/inc/xltable.hxx
@@ -0,0 +1,167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+// Constants and Enumerations =================================================
+
+// Specials for outlines ------------------------------------------------------
+
+const sal_uInt8 EXC_OUTLINE_MAX = 7;
+const sal_uInt8 EXC_OUTLINE_COUNT = EXC_OUTLINE_MAX + 1;
+
+// (0x0000, 0x0200) DIMENSIONS ------------------------------------------------
+const sal_uInt16 EXC_ID2_DIMENSIONS = 0x0000;
+const sal_uInt16 EXC_ID3_DIMENSIONS = 0x0200;
+
+// (0x0001, 0x0201) BLANK -----------------------------------------------------
+const sal_uInt16 EXC_ID2_BLANK = 0x0001;
+const sal_uInt16 EXC_ID3_BLANK = 0x0201;
+
+// (0x0002) INTEGER -----------------------------------------------------------
+const sal_uInt16 EXC_ID2_INTEGER = 0x0002;
+
+// (0x0003, 0x0203) NUMBER ----------------------------------------------------
+const sal_uInt16 EXC_ID2_NUMBER = 0x0003;
+const sal_uInt16 EXC_ID3_NUMBER = 0x0203;
+
+// (0x0004, 0x0204) LABEL -----------------------------------------------------
+const sal_uInt16 EXC_ID2_LABEL = 0x0004;
+const sal_uInt16 EXC_ID3_LABEL = 0x0204;
+
+const sal_uInt8 EXC_LABEL_MAXLEN = 0xFF;
+
+// (0x0005, 0x0205) BOOLERR ---------------------------------------------------
+const sal_uInt16 EXC_ID2_BOOLERR = 0x0005;
+const sal_uInt16 EXC_ID3_BOOLERR = 0x0205;
+
+const sal_uInt8 EXC_BOOLERR_BOOL = 0x00;
+const sal_uInt8 EXC_BOOLERR_ERROR = 0x01;
+
+// (0x0006, 0x0206, 0x0406) FORMULA -------------------------------------------
+const sal_uInt16 EXC_ID2_FORMULA = 0x0006;
+const sal_uInt16 EXC_ID3_FORMULA = 0x0206;
+const sal_uInt16 EXC_ID4_FORMULA = 0x0406;
+
+const sal_uInt16 EXC_FORMULA_RECALC_ALWAYS = 0x0001;
+const sal_uInt16 EXC_FORMULA_RECALC_ONLOAD = 0x0002;
+const sal_uInt16 EXC_FORMULA_SHARED = 0x0008;
+const sal_uInt16 EXC_FORMULA_DEFAULTFLAGS = 0x0000;
+
+const sal_uInt8 EXC_FORMULA_RES_STRING = 0x00; /// Result is a string.
+const sal_uInt8 EXC_FORMULA_RES_BOOL = 0x01; /// Result is Boolean value.
+const sal_uInt8 EXC_FORMULA_RES_ERROR = 0x02; /// Result is error code.
+const sal_uInt8 EXC_FORMULA_RES_EMPTY = 0x03; /// Result is empty cell (BIFF8 only).
+
+// (0x0007, 0x0207) STRING ----------------------------------------------------
+const sal_uInt16 EXC_ID2_STRING = 0x0007;
+const sal_uInt16 EXC_ID3_STRING = 0x0207;
+
+// (0x0008, 0x0208) ROW -------------------------------------------------------
+const sal_uInt16 EXC_ID2_ROW = 0x0008;
+const sal_uInt16 EXC_ID3_ROW = 0x0208;
+
+const sal_uInt16 EXC_ROW_COLLAPSED = 0x0010;
+const sal_uInt16 EXC_ROW_HIDDEN = 0x0020;
+const sal_uInt16 EXC_ROW_UNSYNCED = 0x0040;
+const sal_uInt16 EXC_ROW_USEDEFXF = 0x0080;
+const sal_uInt16 EXC_ROW_DEFAULTFLAGS = 0x0100;
+
+const sal_uInt16 EXC_ROW_XFMASK = 0x0FFF;
+
+const sal_uInt16 EXC_ROW_DEFAULTHEIGHT = 255;
+const sal_uInt16 EXC_ROW_FLAGDEFHEIGHT = 0x8000;
+const sal_uInt16 EXC_ROW_HEIGHTMASK = 0x7FFF;
+
+const sal_uInt16 EXC_ROW_ROWBLOCKSIZE = 32; /// Number of rows in a row block.
+
+// (0x0020) COLUMNDEFAULT -----------------------------------------------------
+const sal_uInt16 EXC_ID_COLUMNDEFAULT = 0x0020;
+
+// (0x0021, 0x0221) ARRAY -----------------------------------------------------
+const sal_uInt16 EXC_ID2_ARRAY = 0x0021;
+const sal_uInt16 EXC_ID3_ARRAY = 0x0221;
+
+const sal_uInt16 EXC_ARRAY_RECALC_ALWAYS = 0x0001;
+const sal_uInt16 EXC_ARRAY_RECALC_ONLOAD = 0x0002;
+const sal_uInt16 EXC_ARRAY_DEFAULTFLAGS = EXC_ARRAY_RECALC_ONLOAD;
+
+// (0x0024) COLWIDTH ----------------------------------------------------------
+const sal_uInt16 EXC_ID_COLWIDTH = 0x0024;
+
+// (0x0025, 0x0225) DEFAULTROWHEIGHT ------------------------------------------
+const sal_uInt16 EXC_ID2_DEFROWHEIGHT = 0x0025;
+const sal_uInt16 EXC_ID3_DEFROWHEIGHT = 0x0225;
+
+const sal_uInt16 EXC_DEFROW_UNSYNCED = 0x0001;
+const sal_uInt16 EXC_DEFROW_HIDDEN = 0x0002;
+const sal_uInt16 EXC_DEFROW_SPACEABOVE = 0x0004;
+const sal_uInt16 EXC_DEFROW_SPACEBELOW = 0x0008;
+const sal_uInt16 EXC_DEFROW_DEFAULTFLAGS = 0x0000;
+
+const sal_uInt16 EXC_DEFROW_DEFAULTHEIGHT = 255;
+
+// (0x0036, 0x0236) TABLEOP ---------------------------------------------------
+const sal_uInt16 EXC_ID2_TABLEOP = 0x0036;
+const sal_uInt16 EXC_ID3_TABLEOP = 0x0236;
+
+const sal_uInt16 EXC_TABLEOP_RECALC_ALWAYS = 0x0001;
+const sal_uInt16 EXC_TABLEOP_RECALC_ONLOAD = 0x0002;
+const sal_uInt16 EXC_TABLEOP_ROW = 0x0004;
+const sal_uInt16 EXC_TABLEOP_BOTH = 0x0008;
+const sal_uInt16 EXC_TABLEOP_DEFAULTFLAGS = EXC_TABLEOP_RECALC_ONLOAD;
+
+// (0x0037) TABLEOP2 ----------------------------------------------------------
+const sal_uInt16 EXC_ID2_TABLEOP2 = 0x0037;
+
+// (0x0055) DEFCOLWIDTH -------------------------------------------------------
+const sal_uInt16 EXC_ID_DEFCOLWIDTH = 0x0055;
+const sal_uInt16 EXC_DEFCOLWIDTH_DEF = 10;
+
+// (0x007D) COLINFO -----------------------------------------------------------
+const sal_uInt16 EXC_ID_COLINFO = 0x007D;
+
+const sal_uInt16 EXC_COLINFO_HIDDEN = 0x0001;
+const sal_uInt16 EXC_COLINFO_CUSTOMWIDTH = 0x0002;
+const sal_uInt16 EXC_COLINFO_COLLAPSED = 0x1000;
+
+// (0x0080) GUTS --------------------------------------------------------------
+const sal_uInt16 EXC_ID_GUTS = 0x0080;
+
+// (0x00BD) MULRK -------------------------------------------------------------
+const sal_uInt16 EXC_ID_MULRK = 0x00BD;
+
+// (0x00BE) MULBLANK ----------------------------------------------------------
+const sal_uInt16 EXC_ID_MULBLANK = 0x00BE;
+
+// (0x00D6) RSTRING -----------------------------------------------------------
+const sal_uInt16 EXC_ID_RSTRING = 0x00D6;
+
+// (0x00FD) LABELSST ----------------------------------------------------------
+const sal_uInt16 EXC_ID_LABELSST = 0x00FD;
+
+// (0x027E) RK ----------------------------------------------------------------
+const sal_uInt16 EXC_ID_RK = 0x027E;
+
+// (0x04BC) SHRFMLA -----------------------------------------------------------
+const sal_uInt16 EXC_ID_SHRFMLA = 0x04BC;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xltools.hxx b/sc/source/filter/inc/xltools.hxx
new file mode 100644
index 000000000..f67d896af
--- /dev/null
+++ b/sc/source/filter/inc/xltools.hxx
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <types.hxx>
+#include "ftools.hxx"
+
+#include <tools/long.hxx>
+#include <tools/degree.hxx>
+
+class SfxObjectShell;
+enum class FormulaError : sal_uInt16;
+
+// BIFF versions ==============================================================
+
+#define DBG_ERROR_BIFF() OSL_FAIL( "Unknown BIFF type!" )
+#define OSL_ENSURE_BIFF( c ) OSL_ENSURE( c, "Unknown BIFF type!" )
+
+// Enumerations ===============================================================
+
+/** An enumeration for all Excel error codes and the values true and false. */
+enum XclBoolError
+{
+ xlErrNull, /// The error code #NULL!
+ xlErrDiv0, /// The error code #DIV/0!
+ xlErrValue, /// The error code #VALUE!
+ xlErrRef, /// The error code #REF!
+ xlErrName, /// The error code #NAME?
+ xlErrNum, /// The error code #NUM!
+ xlErrNA, /// The error code #N/A!
+ xlErrTrue, /// The Boolean value true.
+ xlErrFalse, /// The Boolean value false.
+ xlErrUnknown /// For unknown codes and values.
+};
+
+// GUID import/export =========================================================
+
+class XclImpStream;
+class XclExpStream;
+
+/** This struct stores a GUID (class ID) and supports reading, writing and comparison. */
+struct XclGuid
+{
+ sal_uInt8 mpnData[ 16 ]; /// Stores GUID always in little endian.
+
+ explicit XclGuid();
+ explicit XclGuid(
+ sal_uInt32 nData1,
+ sal_uInt16 nData2, sal_uInt16 nData3,
+ sal_uInt8 nData41, sal_uInt8 nData42,
+ sal_uInt8 nData43, sal_uInt8 nData44,
+ sal_uInt8 nData45, sal_uInt8 nData46,
+ sal_uInt8 nData47, sal_uInt8 nData48 );
+};
+
+bool operator==( const XclGuid& rCmp1, const XclGuid& rCmp2 );
+
+XclImpStream& operator>>( XclImpStream& rStrm, XclGuid& rGuid );
+XclExpStream& operator<<( XclExpStream& rStrm, const XclGuid& rGuid );
+
+// Excel Tools ================================================================
+
+/** This class contains static helper methods for the Excel import and export filters. */
+class XclTools
+{
+public:
+ // noncopyable nonconstructable -------------------------------------------
+
+ XclTools(const XclTools&) = delete;
+ const XclTools& operator=(const XclTools&) = delete;
+ /** We don't want anybody to instantiate this class, since it is just a
+ collection of static items. */
+ XclTools() = delete;
+
+
+ // GUID's -----------------------------------------------------------------
+
+ static const XclGuid maGuidStdLink; /// GUID of StdLink (HLINK record).
+ static const XclGuid maGuidUrlMoniker; /// GUID of URL moniker (HLINK record).
+ static const XclGuid maGuidFileMoniker; /// GUID of file moniker (HLINK record).
+
+ // numeric conversion -----------------------------------------------------
+
+ /** Calculates the double value from an RK value (encoded integer or double). */
+ static double GetDoubleFromRK( sal_Int32 nRKValue );
+ /** Calculates an RK value (encoded integer or double) from a double value.
+ @param rnRKValue Returns the calculated RK value.
+ @param fValue The double value.
+ @return true = An RK value could be created. */
+ static bool GetRKFromDouble( sal_Int32& rnRKValue, double fValue );
+
+ /** Calculates an angle (in 1/100 of degrees) from an Excel angle value.
+ @param nRotForStacked This value will be returned, if nXclRot contains 'stacked'. */
+ static Degree100 GetScRotation( sal_uInt16 nXclRot, Degree100 nRotForStacked );
+ /** Calculates the Excel angle value from an angle in 1/100 of degrees. */
+ static sal_uInt8 GetXclRotation( Degree100 nScRot );
+
+ /** Calculates BIFF8 rotation angle from BIFF2-BIFF5 text orientation. */
+ static sal_uInt8 GetXclRotFromOrient( sal_uInt8 nXclOrient );
+ /** Calculates BIFF2-BIFF5 text orientation from BIFF8 rotation angle. */
+ static sal_uInt8 GetXclOrientFromRot( sal_uInt16 nXclRot );
+
+ /** Converts a Calc error code to an Excel error code. */
+ static sal_uInt8 GetXclErrorCode( FormulaError nScError );
+ /** Converts an Excel error code to a Calc error code. */
+ static FormulaError GetScErrorCode( sal_uInt8 nXclError );
+
+ /** Converts the passed BIFF error to a double containing the respective Calc error code. */
+ static double ErrorToDouble( sal_uInt8 nXclError );
+ /** Gets a translated error code or Boolean value from Excel error codes.
+ @param rfDblValue Returns 0.0 for error codes or the value of a Boolean (0.0 or 1.0).
+ @param bErrorOrBool false = nError is a Boolean value; true = is an error value.
+ @param nValue The error code or Boolean value. */
+ static XclBoolError ErrorToEnum( double& rfDblValue, bool bErrOrBool, sal_uInt8 nValue );
+
+ /** Returns the length in twips calculated from a length in inches. */
+ static sal_uInt16 GetTwipsFromInch( double fInches );
+ /** Returns the length in twips calculated from a length in 1/100 mm. */
+ static sal_uInt16 GetTwipsFromHmm( sal_Int32 nHmm );
+
+ /** Returns the length in inches calculated from a length in twips. */
+ static double GetInchFromTwips( sal_Int32 nTwips );
+ /** Returns the length in inches calculated from a length in 1/100 mm. */
+ static double GetInchFromHmm( sal_Int32 nHmm );
+
+ /** Returns the length in 1/100 mm calculated from a length in inches. */
+ static sal_Int32 GetHmmFromInch( double fInches );
+ /** Returns the length in 1/100 mm calculated from a length in twips. */
+ static sal_Int32 GetHmmFromTwips( sal_Int32 nTwips );
+
+ /** Returns the Calc column width (twips) for the passed Excel width.
+ * Excel Column width is stored as 1/256th of a character.
+ @param nScCharWidth Width of the '0' character in Calc (twips). */
+ static sal_uInt16 GetScColumnWidth( sal_uInt16 nXclWidth, tools::Long nScCharWidth );
+ /** Returns the Excel column width for the passed Calc width (twips).
+ @param nScCharWidth Width of the '0' character in Calc (twips). */
+ static sal_uInt16 GetXclColumnWidth( sal_uInt16 nScWidth, tools::Long nScCharWidth );
+
+ /** Returns a correction value to convert column widths from/to default column widths.
+ @param nXclDefFontHeight Excel height of application default font. */
+ static double GetXclDefColWidthCorrection( tools::Long nXclDefFontHeight );
+
+ // formatting -------------------------------------------------------------
+
+ /** Returns the best fitting color for an Excel pattern area format. */
+ static Color GetPatternColor( const Color& rPattColor, const Color& rBackColor, sal_uInt16 nXclPattern );
+
+ // text encoding ----------------------------------------------------------
+
+ /** Returns a text encoding from an Excel code page.
+ @return The corresponding text encoding or RTL_TEXTENCODING_DONTKNOW. */
+ static rtl_TextEncoding GetTextEncoding( sal_uInt16 nCodePage );
+
+ /** Returns an Excel code page from a text encoding. */
+ static sal_uInt16 GetXclCodePage( rtl_TextEncoding eTextEnc );
+
+ // font names -------------------------------------------------------------
+
+ /** Returns the matching Excel font name for a passed Calc font name. */
+ static OUString GetXclFontName( const OUString& rFontName );
+
+ // built-in defined names -------------------------------------------------
+
+ /** Returns the raw English UI representation of a built-in defined name used in NAME records.
+ @param cBuiltIn Excel index of the built-in name. */
+ static OUString GetXclBuiltInDefName( sal_Unicode cBuiltIn );
+ /** Returns the Calc UI representation of a built-in defined name used in NAME records.
+ @descr Adds a prefix to the representation returned by GetXclBuiltInDefName().
+ @param cBuiltIn Excel index of the built-in name. */
+ static OUString GetBuiltInDefName( sal_Unicode cBuiltIn );
+ /** Returns the Excel built-in name with OOXML prefix
+ @descr Adds the "_xlnm." prefix to the representation returned by GetXclBuiltInDefName()
+ @param cBuiltIn Excel index of the built in name.*/
+ static OUString GetBuiltInDefNameXml( sal_Unicode cBuiltIn );
+ /** Returns the Excel built-in name index of the passed defined name from Calc.
+ @descr Ignores any characters following a valid representation of a built-in name.
+ @param rDefName raw English UI representation of a built-in defined name used in NAME records.
+ @return the index of the built-in name, or EXC_BUILTIN_UNKNOWN if it is not a built-in name. */
+ static sal_Unicode GetBuiltInDefNameIndex( const OUString& rDefName );
+
+ // built-in style names ---------------------------------------------------
+
+ /** Returns the specified built-in cell style name.
+ @param nStyleId The identifier of the built-in style.
+ @param rName Default name for unknown styles.
+ @param nLevel The zero-based outline level for RowLevel and ColLevel styles.
+ @return The style name or an empty string, if the parameters are not valid. */
+ static OUString GetBuiltInStyleName( sal_uInt8 nStyleId, std::u16string_view rName, sal_uInt8 nLevel );
+
+ /** Returns true, if the passed string is a name of an Excel built-in style.
+ @param pnStyleId If not 0, the found style identifier will be returned here.
+ @param pnNextChar If not 0, the index of the char after the evaluated substring will be returned here. */
+ static bool IsBuiltInStyleName( const OUString& rStyleName, sal_uInt8* pnStyleId = nullptr, sal_Int32* pnNextChar = nullptr );
+ /** Returns the Excel built-in style identifier of a passed style name.
+ @param rnStyleId The style identifier is returned here.
+ @param rnLevel The zero-based outline level for RowLevel and ColLevel styles is returned here.
+ @param rStyleName The style name to examine.
+ @return true = passed string is a built-in style name, false = user style. */
+ static bool GetBuiltInStyleId(
+ sal_uInt8& rnStyleId, sal_uInt8& rnLevel,
+ const OUString& rStyleName );
+
+ // conditional formatting style names -------------------------------------
+
+ /** Returns the style name for a single condition of a conditional formatting.
+ @param nScTab The current Calc sheet index.
+ @param nFormat The zero-based index of the conditional formatting.
+ @param nCondition The zero-based index of the condition.
+ @return A style sheet name in the form "Excel_CondFormat_<sheet>_<format>_<condition>". */
+ static OUString GetCondFormatStyleName( SCTAB nScTab, sal_Int32 nFormat, sal_uInt16 nCondition );
+ /** Returns true, if the passed string is a name of a conditional format style created by Excel import.
+ @param pnNextChar If not 0, the index of the char after the evaluated substring will be returned here. */
+ static bool IsCondFormatStyleName( const OUString& rStyleName );
+
+ // stream handling --------------------------------------------------------
+
+ /** Skips a substream (BOF/EOF record block). Includes all embedded substreams. */
+ static void SkipSubStream( XclImpStream& rStrm );
+
+ // Basic macro names ------------------------------------------------------
+
+ /** Returns the full StarBasic macro URL from an Excel macro name. */
+ static OUString GetSbMacroUrl( const OUString& rMacroName, SfxObjectShell* pDocShell );
+ /** Returns the Excel macro name from a full StarBasic macro URL. */
+ static OUString GetXclMacroName( const OUString& rSbMacroUrl );
+
+};
+
+// read/write colors ----------------------------------------------------------
+
+/** Reads a color from the passed stream.
+ @descr The color has the format (all values 8-bit): Red, Green, Blue, 0. */
+XclImpStream& operator>>( XclImpStream& rStrm, Color& rColor );
+
+/** Reads a color to the passed stream.
+ @descr The color has the format (all values 8-bit): Red, Green, Blue, 0. */
+XclExpStream& operator<<( XclExpStream& rStrm, const Color& rColor );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xltracer.hxx b/sc/source/filter/inc/xltracer.hxx
new file mode 100644
index 000000000..eef190139
--- /dev/null
+++ b/sc/source/filter/inc/xltracer.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <vector>
+
+// As Trace features become implemented, we can safely delete the enum entry as
+// we use the member mnID to keep track of the actual trace tag ID value.
+enum XclTracerId
+{
+ eUnKnown , /// unused but allows us to set the correct index
+ eRowLimitExceeded ,
+ eTabLimitExceeded ,
+ ePrintRange ,
+ eShortDate ,
+ eBorderLineStyle ,
+ eFillPattern ,
+ eFormulaMissingArg ,
+ ePivotDataSource ,
+ ePivotChartExists ,
+ eChartUnKnownType ,
+ eChartOnlySheet ,
+ eChartDataTable,
+ eChartLegendPosition,
+ eUnsupportedObject ,
+ eObjectNotPrintable ,
+ eDVType,
+ eTraceLength /// this *should* always be the final entry
+};
+
+/** This class wraps an MSFilterTracer to create trace logs for import/export filters. */
+class XclTracer final
+{
+public:
+ explicit XclTracer( std::u16string_view rDocUrl );
+ ~XclTracer();
+
+ /** Returns true, if tracing is enabled. */
+ bool IsEnabled() const { return mbEnabled; }
+
+ /** Ensure that particular traces are logged once per document. */
+ void ProcessTraceOnce(XclTracerId eProblem);
+
+ void TraceInvalidAddress(const ScAddress& rPos, const ScAddress& rMaxPos);
+ void TraceInvalidRow( sal_uInt32 nRow, sal_uInt32 nMaxrow );
+ void TraceInvalidTab( SCTAB nTab, SCTAB nMaxTab);
+ void TracePrintRange();
+ void TraceDates(sal_uInt16 nNumFmt);
+ void TraceBorderLineStyle(bool bBorderLineStyle);
+ void TraceFillPattern(bool bFillPattern);
+ void TraceFormulaMissingArg();
+ void TracePivotDataSource(bool bExternal);
+ void TracePivotChartExists();
+ void TraceChartUnKnownType();
+ void TraceChartOnlySheet();
+ void TraceChartDataTable();
+ void TraceChartLegendPosition();
+ void TraceUnsupportedObjects();
+ void TraceObjectNotPrintable();
+ void TraceDVType(bool bType);
+
+private:
+ bool mbEnabled;
+ /** array of flags corresponding to each entry in the XclTracerDetails table. */
+ std::vector<bool> maFirstTimes;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/inc/xlview.hxx b/sc/source/filter/inc/xlview.hxx
new file mode 100644
index 000000000..9f9daa95c
--- /dev/null
+++ b/sc/source/filter/inc/xlview.hxx
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/color.hxx>
+#include "xladdress.hxx"
+#include <map>
+#include <memory>
+
+// Constants and enumerations =================================================
+
+const sal_uInt16 EXC_ZOOM_MIN = 10;
+const sal_uInt16 EXC_ZOOM_MAX = 400;
+
+// (0x001D) SELECTION ---------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SELECTION = 0x001D;
+
+// (0x003D) WINDOW1 -----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_WINDOW1 = 0x003D;
+
+const sal_uInt16 EXC_WIN1_HIDDEN = 0x0001;
+const sal_uInt16 EXC_WIN1_MINIMIZED = 0x0002;
+const sal_uInt16 EXC_WIN1_HOR_SCROLLBAR = 0x0008;
+const sal_uInt16 EXC_WIN1_VER_SCROLLBAR = 0x0010;
+const sal_uInt16 EXC_WIN1_TABBAR = 0x0020;
+
+// (0x003E, 0x023E) WINDOW2 ---------------------------------------------------
+
+const sal_uInt16 EXC_ID2_WINDOW2 = 0x003E;
+const sal_uInt16 EXC_ID_WINDOW2 = 0x023E;
+
+const sal_uInt16 EXC_WIN2_SHOWFORMULAS = 0x0001;
+const sal_uInt16 EXC_WIN2_SHOWGRID = 0x0002;
+const sal_uInt16 EXC_WIN2_SHOWHEADINGS = 0x0004;
+const sal_uInt16 EXC_WIN2_FROZEN = 0x0008;
+const sal_uInt16 EXC_WIN2_SHOWZEROS = 0x0010;
+const sal_uInt16 EXC_WIN2_DEFGRIDCOLOR = 0x0020;
+const sal_uInt16 EXC_WIN2_MIRRORED = 0x0040;
+const sal_uInt16 EXC_WIN2_SHOWOUTLINE = 0x0080;
+const sal_uInt16 EXC_WIN2_FROZENNOSPLIT = 0x0100;
+const sal_uInt16 EXC_WIN2_SELECTED = 0x0200;
+const sal_uInt16 EXC_WIN2_DISPLAYED = 0x0400;
+const sal_uInt16 EXC_WIN2_PAGEBREAKMODE = 0x0800;
+
+const sal_uInt16 EXC_WIN2_NORMALZOOM_DEF = 100; /// Default zoom for normal view.
+const sal_uInt16 EXC_WIN2_PAGEZOOM_DEF = 60; /// Default zoom for pagebreak preview.
+
+// (0x0041) PANE --------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_PANE = 0x0041;
+
+const sal_uInt8 EXC_PANE_BOTTOMRIGHT = 0; /// Bottom-right pane.
+const sal_uInt8 EXC_PANE_TOPRIGHT = 1; /// Right, or top-right pane.
+const sal_uInt8 EXC_PANE_BOTTOMLEFT = 2; /// Bottom, or bottom-left pane.
+const sal_uInt8 EXC_PANE_TOPLEFT = 3; /// Single, top, left, or top-left pane.
+
+// (0x00A0) SCL ---------------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SCL = 0x00A0;
+
+// (0x0862) SHEETEXT ----------------------------------------------------------
+
+const sal_uInt16 EXC_ID_SHEETEXT = 0x0862; /// header id for sheetext
+const sal_uInt8 EXC_SHEETEXT_TABCOLOR = 0x7F; /// mask for tab color
+const sal_uInt16 EXC_COLOR_NOTABBG = 0x7F; /// Excel ignores Tab color when set to this value...
+// Structs ====================================================================
+
+/** Contains all view settings for the entire document. */
+struct XclDocViewData
+{
+ sal_uInt16 mnWinX; /// X position of the document window (twips).
+ sal_uInt16 mnWinY; /// Y position of the document window (twips).
+ sal_uInt16 mnWinWidth; /// Width of the document window (twips).
+ sal_uInt16 mnWinHeight; /// Height of the document window (twips).
+ sal_uInt16 mnFlags; /// Additional flags.
+ sal_uInt16 mnDisplXclTab; /// Displayed (active) sheet.
+ sal_uInt16 mnFirstVisXclTab; /// First visible sheet.
+ sal_uInt16 mnXclSelectCnt; /// Number of selected sheets.
+ sal_uInt16 mnTabBarWidth; /// Width of sheet tabbar (1/1000 of window width).
+
+ explicit XclDocViewData();
+};
+
+/** Contains all settings for a selection in a single pane of a sheet. */
+struct XclSelectionData
+{
+ XclAddress maXclCursor; /// Cell cursor position.
+ XclRangeList maXclSelection; /// Selected cell ranges.
+ sal_uInt16 mnCursorIdx; /// Index of cursor in selection list.
+
+ explicit XclSelectionData() : mnCursorIdx( 0 ) {}
+};
+
+typedef std::shared_ptr< XclSelectionData > XclSelectionDataRef;
+
+/** Contains all view settings for a single sheet. */
+struct XclTabViewData
+{
+ typedef ::std::map< sal_uInt8, XclSelectionDataRef > XclSelectionMap;
+
+ XclSelectionMap maSelMap; /// Selections of all panes.
+ Color maGridColor; /// Grid color.
+ XclAddress maFirstXclPos; /// First visible cell.
+ XclAddress maSecondXclPos; /// First visible cell in additional panes.
+ sal_uInt16 mnSplitX; /// Split X position, or number of frozen columns.
+ sal_uInt32 mnSplitY; /// Split Y position, or number of frozen rows.
+ sal_uInt16 mnNormalZoom; /// Zoom factor for normal view.
+ sal_uInt16 mnPageZoom; /// Zoom factor for pagebreak preview.
+ sal_uInt16 mnCurrentZoom; /// Zoom factor for current view.
+ sal_uInt8 mnActivePane; /// Active pane (with cell cursor).
+ bool mbSelected; /// true = Sheet is selected.
+ bool mbDisplayed; /// true = Sheet is displayed (active).
+ bool mbMirrored; /// true = Mirrored (right-to-left) sheet.
+ bool mbFrozenPanes; /// true = Frozen panes; false = split window.
+ bool mbPageMode; /// true = Pagebreak preview; false = Normal view.
+ bool mbDefGridColor; /// true = Default grid color.
+ bool mbShowFormulas; /// true = Show formulas instead of results.
+ bool mbShowGrid; /// true = Show cell grid.
+ bool mbShowHeadings; /// true = Show column/row headings.
+ bool mbShowZeros; /// true = Show zero value zells.
+ bool mbShowOutline; /// true = Show outlines.
+ Color maTabBgColor; /// Tab Color default = (COL_AUTO )
+ bool IsDefaultTabBgColor() const { return maTabBgColor == COL_AUTO; };
+ sal_uInt32 mnTabBgColorId; /// palette color id
+
+ explicit XclTabViewData();
+ ~XclTabViewData();
+
+ /** Sets Excel default view settings. */
+ void SetDefaults();
+
+ /** Returns true, if the window is split in any direction. */
+ bool IsSplit() const;
+ /** Returns true, if the specified pane (EXC_PANE_*) is available. */
+ bool HasPane( sal_uInt8 nPaneId ) const;
+
+ /** Returns the selection data, if available, otherwise 0. */
+ const XclSelectionData* GetSelectionData( sal_uInt8 nPane ) const;
+ /** Returns read/write access to the selection data of the specified pane. */
+ XclSelectionData& CreateSelectionData( sal_uInt8 nPane );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/filter.cxx b/sc/source/filter/lotus/filter.cxx
new file mode 100644
index 000000000..d61015595
--- /dev/null
+++ b/sc/source/filter/lotus/filter.cxx
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// Discover WKS, WK1 and WK3; s.a op.cpp
+
+#include <map>
+
+#include <filter.hxx>
+#include <document.hxx>
+#include <scerrors.hxx>
+
+#include <optab.h>
+#include <op.h>
+#include <scmem.h>
+#include <decl.h>
+#include <fprogressbar.hxx>
+#include "lotfilter.hxx"
+#include <tools/stream.hxx>
+
+static ErrCode
+generate_Opcodes(LotusContext &rContext, SvStream& aStream,
+ ScfStreamProgressBar& aPrgrsBar)
+{
+ OPCODE_FKT *pOps = nullptr;
+ int nOps = 0;
+
+ ErrCode nErr = ERRCODE_NONE;
+
+ switch (rContext.eTyp)
+ {
+ case eWK_1:
+ case eWK_2:
+ pOps = LotusContext::pOpFkt;
+ nOps = FKT_LIMIT;
+ break;
+ case eWK123:
+ pOps = LotusContext::pOpFkt123;
+ nOps = FKT_LIMIT123;
+ break;
+ case eWK3:
+ nErr = SCERR_IMPORT_NI;
+ break;
+ case eWK_Error:
+ nErr = SCERR_IMPORT_FORMAT;
+ break;
+ default:
+ nErr = SCERR_IMPORT_UNKNOWN_WK;
+ break;
+ }
+
+ if (nErr != ERRCODE_NONE)
+ {
+ MemDelete(rContext);
+ return nErr;
+ }
+
+ // #i76299# seems that SvStream::IsEof() does not work correctly
+ sal_uInt64 const nStrmSize = aStream.TellEnd();
+ aStream.Seek( STREAM_SEEK_TO_BEGIN );
+ while (!rContext.bEOF && aStream.good() && (aStream.Tell() < nStrmSize))
+ {
+ sal_uInt16 nOpcode(LOTUS_EOF), nLength(0);
+
+ aStream.ReadUInt16(nOpcode).ReadUInt16(nLength);
+ if (!aStream.good())
+ break;
+
+ aPrgrsBar.Progress();
+ if( nOpcode == LOTUS_EOF )
+ rContext.bEOF = true;
+ else if( nOpcode == LOTUS_FILEPASSWD )
+ {
+ nErr = SCERR_IMPORT_FILEPASSWD;
+ break;
+ }
+ else if( nOpcode < nOps )
+ pOps[ nOpcode ] (rContext, aStream, nLength);
+ else if (rContext.eTyp == eWK123 && nOpcode == LOTUS_PATTERN)
+ {
+ // This is really ugly - needs re-factoring ...
+ aStream.SeekRel(nLength);
+ aStream.ReadUInt16( nOpcode ).ReadUInt16( nLength );
+ if ( nOpcode == 0x29a)
+ {
+ aStream.SeekRel(nLength);
+ aStream.ReadUInt16( nOpcode ).ReadUInt16( nLength );
+ if ( nOpcode == 0x804 )
+ {
+ aStream.SeekRel(nLength);
+ OP_ApplyPatternArea123(rContext, aStream);
+ }
+ else
+ aStream.SeekRel(nLength);
+ }
+ else
+ aStream.SeekRel(nLength);
+ }
+ else
+ aStream.SeekRel( nLength );
+ }
+
+ MemDelete(rContext);
+
+ if (!aStream.good())
+ nErr = SCERR_IMPORT_FORMAT;
+ else if (nErr == ERRCODE_NONE)
+ rContext.rDoc.CalcAfterLoad();
+
+ return nErr;
+}
+
+static WKTYP ScanVersion(SvStream& aStream)
+{
+ // PREC: pWKFile: pointer to open file
+ // POST: return: type of file
+ sal_uInt16 nOpcode(0), nVersNr(0), nRecLen(0);
+
+ // first byte has to be 0 because of BOF!
+ aStream.ReadUInt16( nOpcode );
+ if (nOpcode != LotusContext::nBOF)
+ return eWK_UNKNOWN;
+
+ aStream.ReadUInt16( nRecLen ).ReadUInt16( nVersNr );
+
+ if (!aStream.good())
+ return eWK_Error;
+
+ switch( nVersNr )
+ {
+ case 0x0404:
+ if( nRecLen == 2 )
+ return eWK_1;
+ else
+ return eWK_UNKNOWN;
+
+ case 0x0406:
+ if( nRecLen == 2 )
+ return eWK_2;
+ else
+ return eWK_UNKNOWN;
+
+ case 0x1000:
+ aStream.ReadUInt16( nVersNr );
+ if (!aStream.good())
+ return eWK_Error;
+ if( nVersNr == 0x0004 && nRecLen == 26 )
+ {
+ // 4 bytes of 26 read => skip 22 (read instead of seek to make IsEof() work just in case)
+ char aDummy[22];
+ aStream.ReadBytes(aDummy, 22);
+ return !aStream.good() ? eWK_Error : eWK3;
+ }
+ break;
+ case 0x1003:
+ if( nRecLen == 0x1a )
+ return eWK123;
+ else
+ return eWK_UNKNOWN;
+ case 0x1005:
+ if( nRecLen == 0x1a )
+ return eWK123;
+ else
+ return eWK_UNKNOWN;
+ }
+
+ return eWK_UNKNOWN;
+}
+
+ErrCode ScImportLotus123old(LotusContext& rContext, SvStream& aStream, rtl_TextEncoding eSrc )
+{
+ aStream.Seek( 0 );
+
+ // make document pointer global
+ rContext.bEOF = false;
+ rContext.eCharset = eSrc;
+
+ // allocate memory
+ if( !MemNew(rContext) )
+ return SCERR_IMPORT_OUTOFMEM;
+
+ // initialize page format (only Tab 0!)
+ // initialize page format; meaning: get defaults from SC TODO:
+ //scGetPageFormat( 0, &aPage );
+
+ // start progressbar
+ ScfStreamProgressBar aPrgrsBar( aStream, rContext.rDoc.GetDocumentShell() );
+
+ // detect file type
+ rContext.eTyp = ScanVersion(aStream);
+ rContext.aLotusPatternPool.clear();
+
+ return generate_Opcodes(rContext, aStream, aPrgrsBar);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotattr.cxx b/sc/source/filter/lotus/lotattr.cxx
new file mode 100644
index 000000000..f87984056
--- /dev/null
+++ b/sc/source/filter/lotus/lotattr.cxx
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <lotattr.hxx>
+
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <sal/log.hxx>
+
+#include <docpool.hxx>
+#include <document.hxx>
+#include <lotfntbf.hxx>
+#include "lotfilter.hxx"
+#include <patattr.hxx>
+#include <scitems.hxx>
+
+using namespace ::com::sun::star;
+
+LotAttrCache::ENTRY::ENTRY (std::unique_ptr<ScPatternAttr> p)
+ : pPattAttr(std::move(p))
+ , nHash0(0)
+{
+}
+
+LotAttrCache::ENTRY::~ENTRY ()
+{
+}
+
+LotAttrCache::LotAttrCache (LotusContext& rContext)
+ : mrContext(rContext)
+{
+ pDocPool = rContext.rDoc.GetPool();
+
+ pColTab.reset( new Color [ 8 ] );
+ pColTab[ 0 ] = COL_WHITE;
+ pColTab[ 1 ] = COL_LIGHTBLUE;
+ pColTab[ 2 ] = COL_LIGHTGREEN;
+ pColTab[ 3 ] = COL_LIGHTCYAN;
+ pColTab[ 4 ] = COL_LIGHTRED;
+ pColTab[ 5 ] = COL_LIGHTMAGENTA;
+ pColTab[ 6 ] = COL_YELLOW;
+ pColTab[ 7 ] = COL_BLACK;
+
+ ppColorItems[ 0 ].reset( new SvxColorItem( GetColor( 1 ), ATTR_FONT_COLOR ) ); // 1
+ ppColorItems[ 1 ].reset( new SvxColorItem( GetColor( 2 ), ATTR_FONT_COLOR ) );
+ ppColorItems[ 2 ].reset( new SvxColorItem( GetColor( 3 ), ATTR_FONT_COLOR ) );
+ ppColorItems[ 3 ].reset( new SvxColorItem( GetColor( 4 ), ATTR_FONT_COLOR ) );
+ ppColorItems[ 4 ].reset( new SvxColorItem( GetColor( 5 ), ATTR_FONT_COLOR ) );
+ ppColorItems[ 5 ].reset( new SvxColorItem( GetColor( 6 ), ATTR_FONT_COLOR ) ); // 6
+
+ pWhite.reset( new SvxColorItem( COL_WHITE, ATTR_FONT_COLOR ) );
+}
+
+LotAttrCache::~LotAttrCache()
+{
+}
+
+const ScPatternAttr& LotAttrCache::GetPattAttr( const LotAttrWK3& rAttr )
+{
+ sal_uInt32 nRefHash;
+ MakeHash( rAttr, nRefHash );
+
+ std::vector< std::unique_ptr<ENTRY> >::const_iterator iter
+ = std::find_if(aEntries.begin(),aEntries.end(),
+ [nRefHash] (const std::unique_ptr<ENTRY>& rEntry) { return rEntry->nHash0 == nRefHash; } );
+
+ if (iter != aEntries.end())
+ return *((*iter)->pPattAttr);
+
+ // generate new Pattern Attribute
+ ScPatternAttr* pNewPatt = new ScPatternAttr(pDocPool);
+
+ SfxItemSet& rItemSet = pNewPatt->GetItemSet();
+ ENTRY *pCurrent = new ENTRY( std::unique_ptr<ScPatternAttr>(pNewPatt) );
+
+ pCurrent->nHash0 = nRefHash;
+
+ mrContext.maFontBuff.Fill( rAttr.nFont, rItemSet );
+
+ sal_uInt8 nLine = rAttr.nLineStyle;
+ if( nLine )
+ {
+ SvxBoxItem aBox( ATTR_BORDER );
+ ::editeng::SvxBorderLine aTop, aLeft, aBottom, aRight;
+
+ LotusToScBorderLine( nLine, aLeft );
+ nLine >>= 2;
+ LotusToScBorderLine( nLine, aRight );
+ nLine >>= 2;
+ LotusToScBorderLine( nLine, aTop );
+ nLine >>= 2;
+ LotusToScBorderLine( nLine, aBottom );
+
+ aBox.SetLine( &aTop, SvxBoxItemLine::TOP );
+ aBox.SetLine( &aLeft, SvxBoxItemLine::LEFT );
+ aBox.SetLine( &aBottom, SvxBoxItemLine::BOTTOM );
+ aBox.SetLine( &aRight, SvxBoxItemLine::RIGHT );
+
+ rItemSet.Put( aBox );
+ }
+
+ sal_uInt8 nFontCol = rAttr.nFontCol & 0x07;
+ if( nFontCol )
+ {
+ // nFontCol > 0
+ if( nFontCol < 7 )
+ rItemSet.Put( GetColorItem( nFontCol ) );
+ else
+ rItemSet.Put( *pWhite );
+ }
+
+ sal_uInt8 nBack = rAttr.nBack & 0x1F;
+ if( nBack )
+ rItemSet.Put( SvxBrushItem( GetColor( nBack & 0x07 ), ATTR_BACKGROUND ) );
+
+ if( rAttr.nBack & 0x80 )
+ {
+ SvxHorJustifyItem aHorJustify(SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY );
+ rItemSet.Put( aHorJustify );
+ }
+
+ aEntries.push_back(std::unique_ptr<ENTRY>(pCurrent));
+
+ return *pNewPatt;
+}
+
+void LotAttrCache::LotusToScBorderLine( sal_uInt8 nLine, ::editeng::SvxBorderLine& aBL )
+{
+ nLine &= 0x03;
+
+ switch ( nLine )
+ {
+ case 0: aBL.SetBorderLineStyle(SvxBorderLineStyle::NONE); break;
+ case 1: aBL.SetWidth( SvxBorderLineWidth::Thin ); break;
+ case 2: aBL.SetWidth( SvxBorderLineWidth::Medium ); break;
+ case 3:
+ {
+ aBL.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE_THIN);
+ aBL.SetWidth( SvxBorderLineWidth::Thin );
+ }
+ break;
+ }
+}
+
+const SvxColorItem& LotAttrCache::GetColorItem( const sal_uInt8 nLotIndex ) const
+{
+ // *LotAttrCache::GetColorItem(): caller has to check index!
+ assert( nLotIndex > 0 && nLotIndex < 7 );
+
+ return *ppColorItems[ nLotIndex - 1 ];
+}
+
+const Color& LotAttrCache::GetColor( const sal_uInt8 nLotIndex ) const
+{
+ // color <-> index fits background, but not for fonts (0 <-> 7)!
+ // *LotAttrCache::GetColor(): Index > 7, caller hast to check index!"
+ assert( nLotIndex < 8 );
+
+ return pColTab[ nLotIndex ];
+}
+
+void LotAttrCol::SetAttr( const ScDocument* pDoc, const SCROW nRow, const ScPatternAttr& rAttr )
+{
+ // Actually with the current implementation of MAXROWCOUNT1>=64k and nRow
+ // being read as sal_uInt16 there's no chance that nRow would be invalid...
+ SAL_WARN_IF( !pDoc->ValidRow(nRow), "sc.filter", "*LotAttrCol::SetAttr(): ... and failed?!" );
+
+ std::vector<std::unique_ptr<ENTRY> >::reverse_iterator iterLast = aEntries.rbegin();
+
+ if(iterLast != aEntries.rend())
+ {
+ if( ( (*iterLast)->nLastRow == nRow - 1 ) && ( &rAttr == (*iterLast)->pPattAttr ) )
+ (*iterLast)->nLastRow = nRow;
+ else
+ {
+ ENTRY *pCurrent = new ENTRY;
+
+ pCurrent->pPattAttr = &rAttr;
+ pCurrent->nFirstRow = pCurrent->nLastRow = nRow;
+
+ aEntries.push_back(std::unique_ptr<ENTRY>(pCurrent));
+ }
+ }
+ else
+ { // first entry
+ ENTRY *pCurrent = new ENTRY;
+ pCurrent->pPattAttr = &rAttr;
+ pCurrent->nFirstRow = pCurrent->nLastRow = nRow;
+
+ aEntries.push_back(std::unique_ptr<ENTRY>(pCurrent));
+ }
+}
+
+void LotAttrCol::Apply(LotusContext& rContext, const SCCOL nColNum, const SCTAB nTabNum)
+{
+ ScDocument& rDoc = rContext.rDoc;
+
+ for (const auto& rxEntry : aEntries)
+ {
+ rDoc.ApplyPatternAreaTab(nColNum, rxEntry->nFirstRow, nColNum, rxEntry->nLastRow,
+ nTabNum, *(rxEntry->pPattAttr));
+ }
+}
+
+LotAttrTable::LotAttrTable(LotusContext& rContext):
+ aAttrCache(rContext)
+{
+}
+
+void LotAttrTable::SetAttr( const LotusContext& rContext, const SCCOL nColFirst, const SCCOL nColLast, const SCROW nRow,
+ const LotAttrWK3& rAttr )
+{
+ // With the current implementation of MAXCOLCOUNT>=1024 and nColFirst and
+ // nColLast being calculated as sal_uInt8+sal_uInt8 there's no chance that
+ // they would be invalid.
+ const ScPatternAttr &rPattAttr = aAttrCache.GetPattAttr( rAttr );
+ SCCOL nColCnt;
+
+ for( nColCnt = nColFirst ; nColCnt <= nColLast ; nColCnt++ )
+ pCols[ nColCnt ].SetAttr( &rContext.rDoc, nRow, rPattAttr );
+}
+
+void LotAttrTable::Apply(LotusContext& rContext, const SCTAB nTabNum)
+{
+ SCCOL nColCnt;
+ for( nColCnt = 0 ; nColCnt <= aAttrCache.mrContext.rDoc.MaxCol() ; nColCnt++ )
+ pCols[ nColCnt ].Apply(rContext, nColCnt, nTabNum); // does a Clear() at end
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotfilter.hxx b/sc/source/filter/lotus/lotfilter.hxx
new file mode 100644
index 000000000..596b9a9de
--- /dev/null
+++ b/sc/source/filter/lotus/lotfilter.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <map>
+#include <decl.h>
+#include <optab.h>
+#include <patattr.hxx>
+#include <lotattr.hxx>
+#include <lotrange.hxx>
+#include <lotfntbf.hxx>
+#include <flttypes.hxx>
+#include <namebuff.hxx>
+
+class SvxHorJustifyItem;
+class FormCache;
+
+struct LotusContext
+{
+ static const sal_uInt16 nBOF = 0x0000;
+ static OPCODE_FKT pOpFkt[ FKT_LIMIT ];
+ static OPCODE_FKT pOpFkt123[ FKT_LIMIT123 ]; // -> optab.cxx; table of possible Opcodes
+
+ WKTYP eTyp; // type of file being processed
+ bool bEOF; // shows end of file
+ rtl_TextEncoding eCharset;
+ ScDocument& rDoc; // reference to access document
+ std::map<sal_uInt16, ScPatternAttr> aLotusPatternPool;
+
+ std::unique_ptr<SvxHorJustifyItem> xAttrRight, xAttrLeft, xAttrCenter, xAttrRepeat, xAttrStandard;
+
+ std::unique_ptr<FormCache> xValueFormCache; // -> initialized in memory.cxx
+
+ LotusRangeList maRangeNames;
+ Lotus123Typ eFirstType;
+ Lotus123Typ eActType;
+ ScRange aActRange;
+ std::unique_ptr<RangeNameBufferWK3> pRngNmBffWK3;
+ LotusFontBuffer maFontBuff;
+ LotAttrTable maAttrTable;
+
+ LotusContext(ScDocument& rDocP, rtl_TextEncoding eQ);
+ ~LotusContext();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotform.cxx b/sc/source/filter/lotus/lotform.cxx
new file mode 100644
index 000000000..d20f0a38f
--- /dev/null
+++ b/sc/source/filter/lotus/lotform.cxx
@@ -0,0 +1,2106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <decl.h>
+#include <lotform.hxx>
+#include "lotfilter.hxx"
+#include <lotrange.hxx>
+#include <namebuff.hxx>
+#include <ftools.hxx>
+#include <tool.h>
+#include <document.hxx>
+
+#include <comphelper/string.hxx>
+#include <sal/log.hxx>
+#include <memory>
+#include <string_view>
+
+static const char* GetAddInName( const sal_uInt8 nIndex );
+
+static DefTokenId lcl_KnownAddIn(std::string_view rTest);
+
+void LotusToSc::DoFunc( DefTokenId eOc, sal_uInt8 nCnt, const char* pExtString )
+{
+ TokenId eParam[ 256 ];
+ sal_Int32 nPass;
+ TokenId nBuf0;
+
+ bool bAddIn = false;
+
+ SAL_WARN_IF( nCnt > 128, "sc.filter", "-LotusToSc::DoFunc(): Too many (128)!" );
+
+ if( eOc == ocNoName )
+ {
+ OString t;
+ if( pExtString )
+ {
+ const OString s("@<<@123>>");
+
+ t = pExtString;
+
+ sal_Int32 n = t.indexOf(s);
+ if( n != -1 )
+ t = t.copy(n + s.getLength());
+
+ t = comphelper::string::stripEnd(t, '(');
+
+ eOc = lcl_KnownAddIn( t );
+
+ if( eOc == ocNoName )
+ t = "L123_" + t;
+ }
+ else
+ t = "#UNKNOWN FUNC NAME#";
+
+ if( eOc == ocNoName )
+ {
+ bAddIn = true;
+ nBuf0 = aPool.Store(eOc, OStringToOUString(t, eSrcChar));
+
+ aPool << nBuf0;
+ }
+ }
+
+ for( nPass = 0 ; nPass < nCnt && aStack.HasMoreTokens() ; nPass++ )
+ aStack >> eParam[ nPass ];
+
+ if (nPass < nCnt)
+ // Adapt count to reality. All sort of binary crap is possible.
+ nCnt = static_cast<sal_uInt8>(nPass);
+
+ // special cases...
+ switch( eOc )
+ {
+ case ocIndex:
+ SAL_WARN_IF( nCnt < 2, "sc.filter", "+LotusToSc::DoFunc(): ocIndex needs at least 2 parameters!" );
+ nBuf0 = eParam[ 0 ];
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = nBuf0;
+ IncToken( eParam[ 0 ] );
+ IncToken( eParam[ 1 ] );
+ break;
+ case ocIRR:
+ {
+ SAL_WARN_IF( nCnt != 2, "sc.filter", "+LotusToSc::DoFunc(): ocIRR needs 2 parameters!" );
+ nBuf0 = eParam[ 0 ];
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = nBuf0;
+ }
+ break;
+ case ocGetYear:
+ {
+ nBuf0 = aPool.Store( 1900.0 );
+ aPool << ocOpen;
+ }
+ break;
+ case ocChoose:
+ {// 1. Parameter ++
+ if (nCnt >= 1)
+ IncToken( eParam[ nCnt - 1 ] );
+ }
+ break;
+ case ocFind:
+ case ocHLookup:
+ case ocVLookup:
+ {// last parameter ++
+ IncToken( eParam[ 0 ] );
+ }
+ break;
+ case ocMid:
+ case ocReplace:
+ {// 2. Parameter ++
+ if (nCnt >= 2)
+ IncToken( eParam[ nCnt - 2 ] );
+ }
+ break;
+ case ocRate:
+ {
+ // new quantity = 4!
+ SAL_WARN_IF( nCnt != 3, "sc.filter",
+ "*LotusToSc::DoFunc(): ZINS() needs 3 parameters!" );
+ nCnt = 4;
+ eParam[ 3 ] = eParam[ 0 ]; // 3. -> 1.
+ eParam[ 0 ] = eParam[ 2 ]; // 1. -> 4.
+ NegToken( eParam[ 1 ] ); // 2. -> -2. (+ 2. -> 3.)
+ eParam[ 2 ] = n0Token; // -> 2. as Default
+ }
+ break;
+ case ocNper:
+ {
+ SAL_WARN_IF( nCnt != 3, "sc.filter",
+ "*LotusToSc::DoFunc(): TERM() or CTERM() need 3 parameters!" );
+ nCnt = 4;
+ if ( pExtString == std::string_view("TERM") )
+ {
+ // @TERM(pmt,int,fv) -> NPER(int,-pmt,pv=0,fv)
+ NegToken( eParam[ 2 ] );
+ eParam[ 3 ] = eParam[ 1 ];
+ eParam[ 1 ] = aPool.Store( 0.0 );
+ }
+ else //CTERM()
+ {
+ // @CTERM(int,fv,pv) -> NPER(int,pmt=0,-pv,fv)
+ NegToken( eParam[ 0 ] );
+ nBuf0 = eParam[ 1 ];
+ eParam[ 1 ] = eParam[ 0 ];
+ eParam[ 0 ] = nBuf0;
+ eParam[ 3 ] = eParam[ 2 ];
+ eParam[ 2 ] = aPool.Store( 0.0 );
+ }
+ }
+ break;
+ case ocRoundUp:
+ case ocRoundDown:
+ {
+ // omit optional 3rd parameter
+ if ( nCnt == 3 )
+ {
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = eParam[ 2 ];
+ nCnt = 2;
+ }
+
+ }
+ break;
+ default:;
+ }
+
+ if( !bAddIn )
+ aPool << eOc;
+
+ aPool << ocOpen;
+
+ if( nCnt > 0 )
+ {
+ // ATTENTION: 0 is the last parameter, nCnt-1 the first one
+
+ sal_Int16 nLast = nCnt - 1;
+
+ if( eOc == ocPMT )
+ { // special case ocPMT, negate last parameter!
+ // additionally: 1. -> 3., 3. -> 2., 2. -> 1.
+ SAL_WARN_IF( nCnt != 3, "sc.filter", "+LotusToSc::DoFunc(): ocPMT needs 3 parameters!" );
+ // There should be at least 3 arguments, but with binary crap may not...
+ switch (nCnt)
+ {
+ case 1:
+ aPool << eParam[ 1 ];
+ break;
+ case 2:
+ aPool << eParam[ 1 ] << ocSep << eParam[ 0 ];
+ break;
+ default:
+ case 3:
+ aPool << eParam[ 1 ] << ocSep << eParam[ 0 ] << ocSep << ocNegSub << eParam[ 2 ];
+ break;
+ }
+ }
+ else
+ { // default
+ // [Parameter{;Parameter}]
+ aPool << eParam[ nLast ];
+
+ for( nPass = nLast - 1 ; nPass >= 0 ; nPass-- )
+ {
+ aPool << ocSep << eParam[nPass];
+ }
+ }
+ }
+
+ // special cases ...
+ if( eOc == ocGetYear )
+ {
+ aPool << ocClose << ocSub << nBuf0;
+ }
+ else if( eOc == ocFixed )
+ {
+ aPool << ocSep << ocTrue << ocOpen << ocClose;
+ }
+ else if( eOc == ocFind )
+ {
+ TokenId nBuf1 = aPool.Store();
+ DecToken( nBuf1 );
+ aPool << nBuf1;
+ }
+
+ aPool << ocClose;
+
+ aPool >> aStack;
+}
+
+void LotusToSc::LotusRelToScRel( sal_uInt16 nCol, sal_uInt16 nRow, ScSingleRefData& rSRD )
+{
+ // Column
+ if( nCol & 0x8000 )
+ {
+ if( nCol & 0x0080 )
+ nCol |= 0xFF00;
+ else
+ nCol &= 0x00FF;
+ // #i36252# first cast unsigned 16-bit to signed 16-bit, and then to SCCOL
+ rSRD.SetRelCol(static_cast<SCCOL>(static_cast<sal_Int16>(nCol)));
+ }
+ else
+ {
+ rSRD.SetAbsCol(static_cast<SCCOL>(nCol & 0x00FF));
+ }
+
+ // Row
+ if( nRow & 0x8000 )
+ {
+ rSRD.SetRowRel(true);
+ // sign correct extension
+ switch (m_rContext.eTyp)
+ {
+ // 5432 1098 7654 3210
+ // 8421 8421 8421 8421
+ // xxx xxxx xxxx
+ case eWK_1:
+ if( nRow & 0x0400 )
+ nRow |= 0xF800;
+ else
+ nRow &= 0x07FF;
+ break;
+ // 8421 8421 8421 8421
+ // x xxxx xxxx xxxx
+ case eWK_2:
+ if( nRow & 0x1000 )
+ nRow |= 0xE000;
+ else
+ nRow &= 0x1FFF;
+ break;
+ default:
+ SAL_WARN( "sc.filter", "*LotusToSc::LotusRelToScRel(): unhandled case? " << m_rContext.eTyp );
+ }
+ }
+ else
+ {
+ rSRD.SetRowRel(false);
+ switch (m_rContext.eTyp)
+ {
+ // 5432 1098 7654 3210
+ // 8421 8421 8421 8421
+ // xxx xxxx xxxx
+ case eWK_1:
+ nRow &= 0x07FF;
+ break;
+ // 8421 8421 8421 8421
+ // xx xxxx xxxx xxxx
+ case eWK_2:
+ nRow &= 0x3FFF;
+ break;
+ default:
+ SAL_WARN( "sc.filter", "*LotusToSc::LotusRelToScRel(): unhandled case? " << m_rContext.eTyp );
+ }
+ }
+
+ if( rSRD.IsRowRel() )
+ // #i36252# first cast unsigned 16-bit to signed 16-bit, and then to SCROW
+ rSRD.SetRelRow(static_cast<SCROW>(static_cast<sal_Int16>(nRow)));
+ else
+ rSRD.SetAbsRow(static_cast<SCROW>(nRow));
+}
+
+void LotusToSc::ReadSRD( const ScDocument& rDoc, ScSingleRefData& rSRD, sal_uInt8 nRelBit )
+{
+ sal_uInt8 nTab, nCol;
+ sal_uInt16 nRow;
+
+ Read( nRow );
+ Read( nTab );
+ Read( nCol );
+
+ if (!aIn.good())
+ {
+ SAL_WARN("sc.filter", "LotusToSc::ReadSRD short read");
+ return;
+ }
+
+ bool b3D = (static_cast<SCTAB>(nTab) != aEingPos.Tab());
+
+ rSRD.SetColRel( ( nRelBit & 0x01 ) != 0 );
+ rSRD.SetRowRel( ( nRelBit & 0x02 ) != 0 );
+ rSRD.SetTabRel( ( ( nRelBit & 0x04) != 0 ) || !b3D );
+ rSRD.SetFlag3D( b3D );
+
+ rSRD.SetAddress(rDoc.GetSheetLimits(), ScAddress(nCol, nRow, nTab), aEingPos);
+}
+
+void LotusToSc::IncToken( TokenId &rParam )
+{
+ aPool << ocOpen << rParam << nAddToken;
+ rParam = aPool.Store();
+}
+
+void LotusToSc::DecToken( TokenId &rParam )
+{
+ aPool << ocOpen << rParam << nSubToken;
+ rParam = aPool.Store();
+}
+
+void LotusToSc::NegToken( TokenId &rParam )
+{
+ aPool << ocNegSub << ocOpen << rParam << ocClose;
+ rParam = aPool.Store();
+}
+
+void LotusToSc::Reset( const ScAddress& rEingPos )
+{
+ LotusConverterBase::Reset( rEingPos );
+
+ TokenId nEins = aPool.Store( 1.0 );
+
+ aPool << ocClose << ocAdd << nEins;
+ nAddToken = aPool.Store();
+
+ aPool << ocClose << ocSub << nEins;
+ nSubToken = aPool.Store();
+
+ n0Token = aPool.Store( 0.0 );
+}
+
+LotusToSc::LotusToSc(LotusContext &rContext, SvStream &rStream, svl::SharedStringPool& rSPool,
+ rtl_TextEncoding e, bool b)
+ : LotusConverterBase(rStream, rSPool)
+ , m_rContext(rContext)
+{
+ eSrcChar = e;
+ bWK3 = false;
+ bWK123 = b;
+}
+
+typedef FUNC_TYPE ( FuncType1 ) ( sal_uInt8 );
+typedef DefTokenId ( FuncType2 ) ( sal_uInt8 );
+
+void LotusToSc::Convert( std::unique_ptr<ScTokenArray>& rpErg, sal_Int32& rRest )
+{
+ FUNC_TYPE eType = FT_NOP;
+ const char* pExtName = nullptr;
+ RangeNameBufferWK3& rRangeNameBufferWK3 = *m_rContext.pRngNmBffWK3;
+
+ ScComplexRefData aCRD;
+ aCRD.InitFlags();
+ ScSingleRefData& rR = aCRD.Ref1;
+
+ LotusRangeList& rRangeList = m_rContext.maRangeNames;
+
+ FuncType1* pIndexToType;
+ FuncType2* pIndexToToken;
+
+ if( bWK3 )
+ { // for > WK3
+ pIndexToType = IndexToTypeWK123;
+ pIndexToToken = IndexToTokenWK123;
+ }
+ else if( bWK123 )
+ {
+ pIndexToType = IndexToTypeWK123;
+ pIndexToToken = IndexToTokenWK123;
+ }
+ else
+ {
+ pIndexToType = IndexToType;
+ pIndexToToken = IndexToToken;
+
+ rR.SetRelTab(0);
+ rR.SetFlag3D( false );
+ }
+
+ aCRD.Ref2 = rR;
+
+ nBytesLeft = rRest;
+
+ while( eType ) // != FT_Return (==0)
+ {
+ sal_uInt8 nOc;
+ Read(nOc);
+
+ if( nBytesLeft < 0 )
+ {
+ rpErg = aPool.GetTokenArray(m_rContext.rDoc, aStack.Get());
+ return;
+ }
+
+ eType = pIndexToType( nOc );
+ DefTokenId eOc = pIndexToToken(nOc);
+ if( eOc == ocNoName )
+ pExtName = GetAddInName( nOc );
+
+ switch( eType )
+ {
+ case FT_Return:
+ {
+ if( bWK3 || bWK123 )
+ nBytesLeft = 0; // not used for >= WK3
+
+ rRest = nBytesLeft;
+ break;
+ }
+ case FT_NotImpl:
+ case FT_FuncFix0: DoFunc( eOc, 0, pExtName ); break;
+ case FT_FuncFix1: DoFunc( eOc, 1, pExtName ); break;
+ case FT_FuncFix2: DoFunc( eOc, 2, pExtName ); break;
+ case FT_FuncFix3: DoFunc( eOc, 3, pExtName ); break;
+ case FT_FuncFix4: DoFunc( eOc, 4, pExtName ); break;
+ case FT_FuncVar:
+ {
+ sal_uInt8 nCnt(0);
+ Read( nCnt );
+ DoFunc( eOc, nCnt, pExtName );
+ break;
+ }
+ case FT_Neg:
+ aPool << ocOpen << ocNegSub << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case FT_Op:
+ {
+ TokenId nBuf0;
+ aStack >> nBuf0;
+ aPool << aStack << eOc << nBuf0;
+ aPool >> aStack;
+ break;
+ }
+ case FT_ConstFloat:
+ {
+ double fDouble;
+ Read( fDouble );
+ aStack << aPool.Store( fDouble );
+ break;
+ }
+ case FT_Variable:
+ {
+ sal_uInt16 nCol(0), nRow(0);
+ Read( nCol );
+ Read( nRow );
+
+ LotusRelToScRel( nCol, nRow, rR );
+
+ TokenId nNewId;
+
+ if( bWK3 )
+ nNewId = aPool.Store( rR );
+ else
+ {
+ LR_ID nId = rRangeList.GetIndex(rR.Col(), rR.Row());
+ if( nId == ID_FAIL )
+ // missing range
+ nNewId = aPool.Store( rR );
+ else
+ nNewId = aPool.Store( static_cast<sal_uInt16>(nId) );
+ }
+
+ aStack << nNewId;
+ break;
+ }
+ case FT_Range:
+ {
+ sal_uInt16 nColS(0), nRowS(0), nColE(0), nRowE(0);
+ Read( nColS );
+ Read( nRowS );
+ Read( nColE );
+ Read( nRowE );
+
+ LotusRelToScRel( nColS, nRowS, rR );
+ LotusRelToScRel( nColE, nRowE, aCRD.Ref2 );
+
+ TokenId nNewId;
+
+ if( bWK3 )
+ nNewId = aPool.Store( aCRD );
+ else
+ {
+ LR_ID nId = rRangeList.GetIndex(rR.Col(), rR.Row(), aCRD.Ref2.Col(), aCRD.Ref2.Row());
+
+ if( nId == ID_FAIL )
+ // missing range
+ nNewId = aPool.Store( aCRD );
+ else
+ nNewId = aPool.Store( static_cast<sal_uInt16>(nId) );
+ }
+
+ aStack << nNewId;
+ break;
+ }
+ case FT_Braces:
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+ case FT_ConstInt:
+ {
+ sal_Int16 nVal(0);
+ Read( nVal );
+ aStack << aPool.Store( static_cast<double>(nVal) );
+ break;
+ }
+ case FT_ConstString:
+ {
+ OUString aTmp(ScfTools::read_zeroTerminated_uInt8s_ToOUString(aIn, nBytesLeft, eSrcChar));
+ aStack << aPool.Store( aTmp );
+ break;
+ }
+ case FT_NOP:
+ break;
+ // for > WK3
+ case FT_Cref:
+ {
+ sal_uInt8 nRelBits(0);
+ Read( nRelBits );
+ ReadSRD( m_rContext.rDoc, rR, nRelBits );
+ aStack << aPool.Store( rR );
+ break;
+ }
+ case FT_Rref:
+ {
+ sal_uInt8 nRelBits(0);
+ Read( nRelBits );
+ ReadCRD( m_rContext.rDoc, aCRD, nRelBits );
+ aStack << aPool.Store( aCRD );
+ break;
+ }
+ case FT_Nrref:
+ {
+ OUString aTmp(ScfTools::read_zeroTerminated_uInt8s_ToOUString(aIn, nBytesLeft, eSrcChar));
+ sal_uInt16 nRngIndex;
+ if( rRangeNameBufferWK3.FindRel( aTmp, nRngIndex ) )
+ aStack << aPool.Store( nRngIndex );
+ else
+ {
+ OUString aText = "NRREF " + aTmp;
+ aStack << aPool.Store( aText );
+ }
+ break;
+ }
+ case FT_Absnref:
+ {
+ OUString aTmp(ScfTools::read_zeroTerminated_uInt8s_ToOUString(aIn, nBytesLeft, eSrcChar));
+ sal_uInt16 nRngIndex;
+ if( rRangeNameBufferWK3.FindAbs( aTmp, nRngIndex ) )
+ aStack << aPool.Store( nRngIndex );
+ else
+ {
+ OUString aText = "ABSNREF " + aTmp;
+ aStack << aPool.Store( aText );
+ }
+ break;
+ }
+ case FT_Erref:
+ Ignore( 4 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case FT_Ecref:
+ Ignore( 5 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case FT_Econstant:
+ Ignore( 10 );
+ aPool << ocBad;
+ aPool >> aStack;
+ break;
+ case FT_Splfunc:
+ {
+ sal_uInt8 nCnt(0);
+ Read( nCnt );
+ sal_uInt16 nStrLen(0);
+ Read( nStrLen );
+
+ const size_t nMaxEntries = aIn.remainingSize();
+ if (nStrLen > nMaxEntries)
+ nStrLen = nMaxEntries;
+
+ if( nStrLen )
+ {
+ std::vector<char> aBuffer(nStrLen + 1);
+ aBuffer[aIn.ReadBytes(aBuffer.data(), nStrLen)] = 0;
+ DoFunc(ocNoName, nCnt, aBuffer.data());
+ }
+ else
+ DoFunc( ocNoName, nCnt, nullptr );
+ break;
+ }
+ case FT_Const10Float:
+ {
+ double fValue;
+ if (bWK123)
+ Read(fValue);
+ else
+ ScfTools::ReadLongDouble(aIn, fValue);
+ aStack << aPool.Store(fValue);
+ break;
+ }
+ case FT_Snum:
+ {
+ if ( bWK123 )
+ {
+ sal_uInt32 nValue(0);
+ Read(nValue);
+ double fValue = Snum32ToDouble( nValue );
+ aStack << aPool.Store( fValue );
+ }
+ else
+ {
+ sal_Int16 nVal(0);
+ Read(nVal);
+ aStack << aPool.Store( SnumToDouble( nVal ) );
+ }
+ break;
+ }
+ default:
+ SAL_WARN( "sc.filter", "*LotusToSc::Convert(): unknown enum!" );
+ }
+ }
+
+ rpErg = aPool.GetTokenArray( m_rContext.rDoc, aStack.Get());
+
+ SAL_WARN_IF( nBytesLeft < 0, "sc.filter", "*LotusToSc::Convert(): processed too much!");
+ SAL_WARN_IF( nBytesLeft > 0, "sc.filter", "*LotusToSc::Convert(): what happens with the rest?" );
+
+ if( rRest )
+ aIn.SeekRel( nBytesLeft ); // Correct any remainder/overflow
+
+ rRest = 0;
+}
+
+FUNC_TYPE LotusToSc::IndexToType( sal_uInt8 nIndex )
+{
+ static const FUNC_TYPE pType[ 256 ] =
+ { // Code Description
+ FT_ConstFloat, // 0 8-Byte-IEEE-Float
+ FT_Variable, // 1 Variable
+ FT_Range, // 2 Range
+ FT_Return, // 3 return
+ FT_Braces, // 4 Braces
+ FT_ConstInt, // 5 2-Byte integer
+ FT_ConstString, // 6 ASCII string
+ FT_NOP, // 7 NOP
+ FT_Neg, // 8 Negation
+ FT_Op, // 9 Addition
+ FT_Op, // 10 Subtraction
+ FT_Op, // 11 Multiplication
+ FT_Op, // 12 Division
+ FT_Op, // 13 Power of
+ FT_Op, // 14 equal
+ FT_Op, // 15 unequal
+ FT_Op, // 16 <=
+ FT_Op, // 17 >=
+ FT_Op, // 18 <
+ FT_Op, // 19 >
+ FT_Op, // 20 And (logic)
+ FT_Op, // 21 Or (logic)
+ FT_FuncFix1, // 22 Not (logic)
+ FT_NOP, // 23 unary Plus
+ FT_NotImpl, // 24
+ FT_NotImpl, // 25
+ FT_NotImpl, // 26
+ FT_NotImpl, // 27
+ FT_NotImpl, // 28
+ FT_NotImpl, // 29
+ FT_NotImpl, // 30
+ FT_FuncFix0, // 31 Not applicable
+ FT_FuncFix0, // 32 Error
+ FT_FuncFix1, // 33 Absolute value ABS()
+ FT_FuncFix1, // 34 Integer INT()
+ FT_FuncFix1, // 35 Square Root
+ FT_FuncFix1, // 36 Log10
+ FT_FuncFix1, // 37 Natural Logarithm
+ FT_FuncFix0, // 38 PI
+ FT_FuncFix1, // 39 Sine
+ FT_FuncFix1, // 40 Cosine
+ FT_FuncFix1, // 41 Tangens
+ FT_FuncFix2, // 42 Arctangens 2 (4.Quadrant) <- TODO: correct?
+ FT_FuncFix1, // 43 Arctangens (2.Quadrant)
+ FT_FuncFix1, // 44 Arcsine
+ FT_FuncFix1, // 45 Arccosine
+ FT_FuncFix1, // 46 Exponential function
+ FT_FuncFix2, // 47 Modulo
+ FT_FuncVar, // 48 Selection
+ FT_FuncFix1, // 49 Is not applicable?
+ FT_FuncFix1, // 50 Is Error?
+ FT_FuncFix0, // 51 FALSE
+ FT_FuncFix0, // 52 TRUE
+ FT_FuncFix0, // 53 Random number
+ FT_FuncFix3, // 54 Date
+ FT_FuncFix0, // 55 Today
+ FT_FuncFix3, // 56 Payment
+ FT_FuncFix3, // 57 Present Value
+ FT_FuncFix3, // 58 Future Value
+ FT_FuncFix3, // 59 If ... then ... else ...
+ FT_FuncFix1, // 60 Day of month
+ FT_FuncFix1, // 61 Month
+ FT_FuncFix1, // 62 Year
+ FT_FuncFix2, // 63 Round
+ FT_FuncFix3, // 64 Time
+ FT_FuncFix1, // 65 Hour
+ FT_FuncFix1, // 66 Minute
+ FT_FuncFix1, // 67 Second
+ FT_FuncFix1, // 68 Is Number?
+ FT_FuncFix1, // 69 Is Text?
+ FT_FuncFix1, // 70 Len()
+ FT_FuncFix1, // 71 Val()
+ FT_FuncFix2, // 72 String()
+ FT_FuncFix3, // 73 Mid()
+ FT_FuncFix1, // 74 Char()
+ FT_FuncFix1, // 75 Ascii()
+ FT_FuncFix3, // 76 Find()
+ FT_FuncFix1, // 77 Datevalue
+ FT_FuncFix1, // 78 Timevalue
+ FT_FuncFix1, // 79 Cellpointer
+ FT_FuncVar, // 80 Sum()
+ FT_FuncVar, // 81 Avg()
+ FT_FuncVar, // 82 Cnt()
+ FT_FuncVar, // 83 Min()
+ FT_FuncVar, // 84 Max()
+ FT_FuncFix3, // 85 Vlookup()
+ FT_FuncFix2, // 86 Npv()
+ FT_FuncVar, // 87 Var()
+ FT_FuncVar, // 88 Std()
+ FT_FuncFix2, // 89 Irr()
+ FT_FuncFix3, // 90 Hlookup()
+ FT_FuncFix3, // 91 ?
+ FT_FuncFix3, // 92 ?
+ FT_FuncFix3, // 93 ?
+ FT_FuncFix3, // 94 ?
+ FT_FuncFix3, // 95 ?
+ FT_FuncFix3, // 96 ?
+ FT_FuncFix3, // 97 ?
+ FT_FuncFix3, // 98 Index() <- TODO: correct?
+ FT_FuncFix1, // 99 Columns()
+ FT_FuncFix1, // 100 Rows()
+ FT_FuncFix2, // 101 Repeat()
+ FT_FuncFix1, // 102 Upper()
+ FT_FuncFix1, // 103 Lower()
+ FT_FuncFix2, // 104 Left()
+ FT_FuncFix2, // 105 Right()
+ FT_FuncFix4, // 106 Replace()
+ FT_FuncFix1, // 107 Proper()
+ FT_FuncFix2, // 108 Cell()
+ FT_FuncFix1, // 109 Trim()
+ FT_FuncFix1, // 110 Clean()
+ FT_FuncFix1, // 111 F()
+ FT_FuncFix1, // 112 W()
+ FT_FuncFix2, // 113 Exact()
+ FT_NotImpl, // 114 Call()
+ FT_FuncFix1, // 115 @@()
+ FT_FuncFix3, // 116 Rate()
+ FT_FuncFix3, // 117 Term()
+ FT_FuncFix3, // 118 Cterm()
+ FT_FuncFix3, // 119 Sln()
+ FT_FuncFix4, // 120 Syd(), Soy()
+ FT_FuncFix4, // 121 Ddb()
+ FT_NotImpl, // 122
+ FT_NotImpl, // 123
+ FT_NotImpl, // 124
+ FT_NotImpl, // 125
+ FT_NotImpl, // 126
+ FT_NotImpl, // 127
+ FT_NotImpl, // 128
+ FT_NotImpl, // 129
+ FT_NotImpl, // 130
+ FT_NotImpl, // 131
+ FT_NotImpl, // 132
+ FT_NotImpl, // 133
+ FT_NotImpl, // 134
+ FT_NotImpl, // 135
+ FT_NotImpl, // 136
+ FT_NotImpl, // 137
+ FT_NotImpl, // 138
+ FT_NotImpl, // 139
+ FT_NotImpl, // 140
+ FT_NotImpl, // 141
+ FT_NotImpl, // 142
+ FT_NotImpl, // 143
+ FT_NotImpl, // 144
+ FT_NotImpl, // 145
+ FT_NotImpl, // 146
+ FT_NotImpl, // 147
+ FT_NotImpl, // 148
+ FT_NotImpl, // 149
+ FT_NotImpl, // 150
+ FT_NotImpl, // 151
+ FT_NotImpl, // 152
+ FT_NotImpl, // 153
+ FT_NotImpl, // 154
+ FT_NotImpl, // 155
+ FT_FuncVar, // 156 TODO: ?
+ FT_NotImpl, // 157
+ FT_NotImpl, // 158
+ FT_NotImpl, // 159
+ FT_NotImpl, // 160
+ FT_NotImpl, // 161
+ FT_NotImpl, // 162
+ FT_NotImpl, // 163
+ FT_NotImpl, // 164
+ FT_NotImpl, // 165
+ FT_NotImpl, // 166
+ FT_NotImpl, // 167
+ FT_NotImpl, // 168
+ FT_NotImpl, // 169
+ FT_NotImpl, // 170
+ FT_NotImpl, // 171
+ FT_NotImpl, // 172
+ FT_NotImpl, // 173
+ FT_NotImpl, // 174
+ FT_NotImpl, // 175
+ FT_NotImpl, // 176
+ FT_NotImpl, // 177
+ FT_NotImpl, // 178
+ FT_NotImpl, // 179
+ FT_NotImpl, // 180
+ FT_NotImpl, // 181
+ FT_NotImpl, // 182
+ FT_NotImpl, // 183
+ FT_NotImpl, // 184
+ FT_NotImpl, // 185
+ FT_NotImpl, // 186
+ FT_NotImpl, // 187
+ FT_NotImpl, // 188
+ FT_NotImpl, // 189
+ FT_NotImpl, // 190
+ FT_NotImpl, // 191
+ FT_NotImpl, // 192
+ FT_NotImpl, // 193
+ FT_NotImpl, // 194
+ FT_NotImpl, // 195
+ FT_NotImpl, // 196
+ FT_NotImpl, // 197
+ FT_NotImpl, // 198
+ FT_NotImpl, // 199
+ FT_NotImpl, // 200
+ FT_NotImpl, // 201
+ FT_NotImpl, // 202
+ FT_NotImpl, // 203
+ FT_NotImpl, // 204
+ FT_NotImpl, // 205
+ FT_FuncVar, // 206 TODO: ?
+ FT_NotImpl, // 207
+ FT_NotImpl, // 208
+ FT_NotImpl, // 209
+ FT_NotImpl, // 210
+ FT_NotImpl, // 211
+ FT_NotImpl, // 212
+ FT_NotImpl, // 213
+ FT_NotImpl, // 214
+ FT_NotImpl, // 215
+ FT_NotImpl, // 216
+ FT_NotImpl, // 217
+ FT_NotImpl, // 218
+ FT_NotImpl, // 219
+ FT_NotImpl, // 220
+ FT_NotImpl, // 221
+ FT_NotImpl, // 222
+ FT_NotImpl, // 223
+ FT_NotImpl, // 224
+ FT_NotImpl, // 225
+ FT_NotImpl, // 226
+ FT_NotImpl, // 227
+ FT_NotImpl, // 228
+ FT_NotImpl, // 229
+ FT_NotImpl, // 230
+ FT_NotImpl, // 231
+ FT_NotImpl, // 232
+ FT_NotImpl, // 233
+ FT_NotImpl, // 234
+ FT_NotImpl, // 235
+ FT_NotImpl, // 236
+ FT_NotImpl, // 237
+ FT_NotImpl, // 238
+ FT_NotImpl, // 239
+ FT_NotImpl, // 240
+ FT_NotImpl, // 241
+ FT_NotImpl, // 242
+ FT_NotImpl, // 243
+ FT_NotImpl, // 244
+ FT_NotImpl, // 245
+ FT_NotImpl, // 246
+ FT_NotImpl, // 247
+ FT_NotImpl, // 248
+ FT_NotImpl, // 249
+ FT_NotImpl, // 250
+ FT_NotImpl, // 251
+ FT_NotImpl, // 252
+ FT_NotImpl, // 253
+ FT_NotImpl, // 254
+ FT_FuncVar, // 255 TODO: ?
+ };
+ return pType[ nIndex ];
+}
+
+DefTokenId LotusToSc::IndexToToken( sal_uInt8 nIndex )
+{
+ static const DefTokenId pToken[ 256 ] =
+ { // Code Description
+ ocPush, // 0 8-Byte-IEEE-Float
+ ocPush, // 1 Variable
+ ocPush, // 2 Range
+ ocPush, // 3 return
+ ocPush, // 4 Braces
+ ocPush, // 5 2-Byte integer
+ ocPush, // 6 ASCII string
+ ocPush, // 7 NOP
+ ocNegSub, // 8 Negation
+ ocAdd, // 9 Addition
+ ocSub, // 10 Subtraction
+ ocMul, // 11 Multiplication
+ ocDiv, // 12 Division
+ ocPow, // 13 Power of
+ ocEqual, // 14 equal
+ ocNotEqual, // 15 unequal
+ ocLessEqual, // 16 <=
+ ocGreaterEqual, // 17 >=
+ ocLess, // 18 <
+ ocGreater, // 19 >
+ ocAnd, // 20 And (logic)
+ ocOr, // 21 Or (logic)
+ ocNot, // 22 Not (logic)
+ ocPush, // 23 unary Plus
+ ocNoName, // 24
+ ocNoName, // 25
+ ocNoName, // 26
+ ocNoName, // 27
+ ocNoName, // 28
+ ocNoName, // 29
+ ocNoName, // 30
+ ocNotAvail, // 31 Not available
+ ocNoName, // 32 Error
+ ocAbs, // 33 Absolute value ABS()
+ ocInt, // 34 Integer INT()
+ ocSqrt, // 35 Square Root
+ ocLog10, // 36 Log10
+ ocLn, // 37 Natural Logarithm
+ ocPi, // 38 PI
+ ocSin, // 39 Sine
+ ocCos, // 40 Cosine
+ ocTan, // 41 Tangens
+ ocArcTan2, // 42 Arctangens 2 (4.Quadrant)
+ ocArcTan, // 43 Arctangens (2.Quadrant)
+ ocArcSin, // 44 Arcsine
+ ocArcCos, // 45 Arccosine
+ ocExp, // 46 Exponential function
+ ocMod, // 47 Modulo
+ ocChoose, // 48 Selection
+ ocIsNA, // 49 Is not available?
+ ocIsError, // 50 Is Error?
+ ocFalse, // 51 FALSE
+ ocTrue, // 52 TRUE
+ ocRandom, // 53 Random number
+ ocGetDate, // 54 Date
+ ocGetActDate, // 55 Today
+ ocPMT, // 56 Payment
+ ocPV, // 57 Present Value
+ ocFV, // 58 Future Value
+ ocIf, // 59 If ... then ... else ...
+ ocGetDay, // 60 Day of month
+ ocGetMonth, // 61 Month
+ ocGetYear, // 62 Year
+ ocRound, // 63 Round
+ ocGetTime, // 64 Time
+ ocGetHour, // 65 Hour
+ ocGetMin, // 66 Minute
+ ocGetSec, // 67 Second
+ ocIsValue, // 68 Is Number?
+ ocIsString, // 69 Is Text?
+ ocLen, // 70 Len()
+ ocValue, // 71 Val()
+ ocFixed, // 72 String() ocFixed replacement + special case
+ ocMid, // 73 Mid()
+ ocChar, // 74 Char()
+ ocCode, // 75 Ascii()
+ ocFind, // 76 Find()
+ ocGetDateValue, // 77 Datevalue
+ ocGetTimeValue, // 78 Timevalue
+ ocNoName, // 79 Cellpointer
+ ocSum, // 80 Sum()
+ ocAverageA, // 81 Avg()
+ ocCount2, // 82 Cnt()
+ ocMinA, // 83 Min()
+ ocMaxA, // 84 Max()
+ ocVLookup, // 85 Vlookup()
+ ocNPV, // 86 Npv()
+ ocVarPA, // 87 Var()
+ ocStDevPA, // 88 Std()
+ ocIRR, // 89 Irr()
+ ocHLookup, // 90 Hlookup()
+ ocDBSum, // 91 XlfDsum
+ ocDBAverage, // 92 XlfDaverage
+ ocDBCount2, // 93 XlfDcount
+ ocDBMin, // 94 XlfDmin
+ ocDBMax, // 95 XlfDmax
+ ocDBVarP, // 96 XlfDvar
+ ocDBStdDevP, // 97 XlfDstdev
+ ocIndex, // 98 Index()
+ ocColumns, // 99 Columns()
+ ocRows, // 100 Rows()
+ ocRept, // 101 Repeat()
+ ocUpper, // 102 Upper()
+ ocLower, // 103 Lower()
+ ocLeft, // 104 Left()
+ ocRight, // 105 Right()
+ ocReplace, // 106 Replace()
+ ocProper, // 107 Proper()
+ ocNoName, // 108 Cell()
+ ocTrim, // 109 Trim()
+ ocClean, // 110 Clean()
+ ocFalse, // 111 F()
+ ocTrue, // 112 W()
+ ocExact, // 113 Exact()
+ ocNoName, // 114 Call()
+ ocIndirect, // 115 @@()
+ ocRate, // 116 Rate()
+ ocNoName, // 117 Term()
+ ocNoName, // 118 Cterm()
+ ocSLN, // 119 Sln()
+ ocSYD, // 120 Syd(), Soy()
+ ocDDB, // 121 Ddb()
+ ocNoName, // 122
+ ocNoName, // 123
+ ocNoName, // 124
+ ocNoName, // 125
+ ocNoName, // 126
+ ocNoName, // 127
+ ocNoName, // 128
+ ocNoName, // 129
+ ocNoName, // 130
+ ocNoName, // 131
+ ocNoName, // 132
+ ocNoName, // 133
+ ocNoName, // 134
+ ocNoName, // 135
+ ocNoName, // 136
+ ocNoName, // 137
+ ocNoName, // 138
+ ocNoName, // 139
+ ocNoName, // 140
+ ocNoName, // 141
+ ocNoName, // 142
+ ocNoName, // 143
+ ocNoName, // 144
+ ocNoName, // 145
+ ocNoName, // 146
+ ocNoName, // 147
+ ocNoName, // 148
+ ocNoName, // 149
+ ocNoName, // 150
+ ocNoName, // 151
+ ocNoName, // 152
+ ocNoName, // 153
+ ocNoName, // 154
+ ocNoName, // 155
+ ocNoName, // 156 TODO: ?
+ ocNoName, // 157
+ ocNoName, // 158
+ ocNoName, // 159
+ ocNoName, // 160
+ ocNoName, // 161
+ ocNoName, // 162
+ ocNoName, // 163
+ ocNoName, // 164
+ ocNoName, // 165
+ ocNoName, // 166
+ ocNoName, // 167
+ ocNoName, // 168
+ ocNoName, // 169
+ ocNoName, // 170
+ ocNoName, // 171
+ ocNoName, // 172
+ ocNoName, // 173
+ ocNoName, // 174
+ ocNoName, // 175
+ ocNoName, // 176
+ ocNoName, // 177
+ ocNoName, // 178
+ ocNoName, // 179
+ ocNoName, // 180
+ ocNoName, // 181
+ ocNoName, // 182
+ ocNoName, // 183
+ ocNoName, // 184
+ ocNoName, // 185
+ ocNoName, // 186
+ ocNoName, // 187
+ ocNoName, // 188
+ ocNoName, // 189
+ ocNoName, // 190
+ ocNoName, // 191
+ ocNoName, // 192
+ ocNoName, // 193
+ ocNoName, // 194
+ ocNoName, // 195
+ ocNoName, // 196
+ ocNoName, // 197
+ ocNoName, // 198
+ ocNoName, // 199
+ ocNoName, // 200
+ ocNoName, // 201
+ ocNoName, // 202
+ ocNoName, // 203
+ ocNoName, // 204
+ ocNoName, // 205
+ ocNoName, // 206 TODO: ?
+ ocNoName, // 207
+ ocNoName, // 208
+ ocNoName, // 209
+ ocNoName, // 210
+ ocNoName, // 211
+ ocNoName, // 212
+ ocNoName, // 213
+ ocNoName, // 214
+ ocNoName, // 215
+ ocNoName, // 216
+ ocNoName, // 217
+ ocNoName, // 218
+ ocNoName, // 219
+ ocNoName, // 220
+ ocNoName, // 221
+ ocNoName, // 222
+ ocNoName, // 223
+ ocNoName, // 224
+ ocNoName, // 225
+ ocNoName, // 226
+ ocNoName, // 227
+ ocNoName, // 228
+ ocNoName, // 229
+ ocNoName, // 230
+ ocNoName, // 231
+ ocNoName, // 232
+ ocNoName, // 233
+ ocNoName, // 234
+ ocNoName, // 235
+ ocNoName, // 236
+ ocNoName, // 237
+ ocNoName, // 238
+ ocNoName, // 239
+ ocNoName, // 240
+ ocNoName, // 241
+ ocNoName, // 242
+ ocNoName, // 243
+ ocNoName, // 244
+ ocNoName, // 245
+ ocNoName, // 246
+ ocNoName, // 247
+ ocNoName, // 248
+ ocNoName, // 249
+ ocNoName, // 250
+ ocNoName, // 251
+ ocNoName, // 252
+ ocNoName, // 253
+ ocNoName, // 254
+ ocNoName // 255 TODO: ?
+ };
+
+ return pToken[ nIndex ];
+}
+
+FUNC_TYPE LotusToSc::IndexToTypeWK123( sal_uInt8 nIndex )
+{
+ static const FUNC_TYPE pType[ 256 ] =
+ { // Code Description
+ FT_Const10Float, // 0 8-Byte-IEEE-Long-Number
+ FT_Cref, // 1 Cell Reference
+ FT_Rref, // 2 Area Reference
+ FT_Return, // 3 return
+ FT_Braces, // 4 Braces
+ FT_Snum, // 5 Short number
+ FT_ConstString, // 6 ASCII string
+ FT_Nrref, // 7 Named range reference
+ FT_Absnref, // 8 Absolute named range
+ FT_Erref, // 9 Err range reference
+ FT_Ecref, // 10 Err cell reference
+ FT_Econstant, // 11 Err constant
+ FT_NotImpl, // 12
+ FT_NotImpl, // 13
+ FT_Neg, // 14 Negation
+ FT_Op, // 15 Addition
+ FT_Op, // 16 Subtraction
+ FT_Op, // 17 Multiplication
+ FT_Op, // 18 Division
+ FT_Op, // 19 Power of
+ FT_Op, // 20 equal
+ FT_Op, // 21 unequal
+ FT_Op, // 22 <=
+ FT_Op, // 23 >=
+ FT_Op, // 24 <
+ FT_Op, // 25 >
+ FT_Op, // 26 And (logical)
+ FT_Op, // 27 Or (logical)
+ FT_FuncFix1, // 28 Not (logical)
+ FT_NOP, // 29 unary plus
+ FT_Op, // 30 Concatenation
+ FT_FuncFix0, // 31 Not applicable
+ FT_FuncFix0, // 32 Error
+ FT_FuncFix1, // 33 Absolute value ABS()
+ FT_FuncFix1, // 34 Integer INT()
+ FT_FuncFix1, // 35 Square root
+ FT_FuncFix1, // 36 Log10
+ FT_FuncFix1, // 37 Natural Logarithm
+ FT_FuncFix0, // 38 PI
+ FT_FuncFix1, // 39 Sine
+ FT_FuncFix1, // 40 Cosine
+ FT_FuncFix1, // 41 Tangens
+ FT_FuncFix2, // 42 Arctangens 2 (4.Quadrant)
+ FT_FuncFix1, // 43 Arctangens (2.Quadrant)
+ FT_FuncFix1, // 44 Arcsine
+ FT_FuncFix1, // 45 Arccosine
+ FT_FuncFix1, // 46 Exponential function
+ FT_FuncFix2, // 47 Modulo
+ FT_FuncVar, // 48 Selection
+ FT_FuncFix1, // 49 Is not applicable?
+ FT_FuncFix1, // 50 Is Error?
+ FT_FuncFix0, // 51 FALSE
+ FT_FuncFix0, // 52 TRUE
+ FT_FuncFix0, // 53 Random number
+ FT_FuncFix3, // 54 Date
+ FT_FuncFix0, // 55 Today
+ FT_FuncFix3, // 56 Payment
+ FT_FuncFix3, // 57 Present Value
+ FT_FuncFix3, // 58 Future Value
+ FT_FuncFix3, // 59 If ... then ... else ...
+ FT_FuncFix1, // 60 Day of Month
+ FT_FuncFix1, // 61 Month
+ FT_FuncFix1, // 62 Year
+ FT_FuncFix2, // 63 Round
+ FT_FuncFix3, // 64 Time
+ FT_FuncFix1, // 65 Hour
+ FT_FuncFix1, // 66 Minute
+ FT_FuncFix1, // 67 Second
+ FT_FuncFix1, // 68 Is Number?
+ FT_FuncFix1, // 69 Is Text?
+ FT_FuncFix1, // 70 Len()
+ FT_FuncFix1, // 71 Val()
+ FT_FuncFix2, // 72 String()
+ FT_FuncFix3, // 73 Mid()
+ FT_FuncFix1, // 74 Char()
+ FT_FuncFix1, // 75 Ascii()
+ FT_FuncFix3, // 76 Find()
+ FT_FuncFix1, // 77 Datevalue
+ FT_FuncFix1, // 78 Timevalue
+ FT_FuncFix1, // 79 Cellpointer
+ FT_FuncVar, // 80 Sum()
+ FT_FuncVar, // 81 Avg()
+ FT_FuncVar, // 82 Cnt()
+ FT_FuncVar, // 83 Min()
+ FT_FuncVar, // 84 Max()
+ FT_FuncFix3, // 85 Vlookup()
+ FT_FuncFix2, // 86 Npv()
+ FT_FuncVar, // 87 Var()
+ FT_FuncVar, // 88 Std()
+ FT_FuncFix2, // 89 Irr()
+ FT_FuncFix3, // 90 Hlookup()
+ FT_FuncVar, // 91 Dsum <- new
+ FT_FuncVar, // 92 Davg <- new
+ FT_FuncVar, // 93 Dcnt <- new
+ FT_FuncVar, // 94 Dmin <- new
+ FT_FuncVar, // 95 Dmax <- new
+ FT_FuncVar, // 96 Dvar <- new
+ FT_FuncVar, // 97 Dstd <- new
+ FT_FuncVar, // 98 Index() <- change!
+ FT_FuncFix1, // 99 Columns() <- new
+ FT_FuncFix1, // 100 Rows() <- new
+ FT_FuncFix2, // 101 Repeat() <- new
+ FT_FuncFix1, // 102 Upper() <- new
+ FT_FuncFix1, // 103 Lower() <- new
+ FT_FuncFix2, // 104 Left() <- new
+ FT_FuncFix2, // 105 Right() <- new
+ FT_FuncFix4, // 106 Replace() <- new
+ FT_FuncFix1, // 107 Proper() <- new
+ FT_FuncFix2, // 108 Cell() <- new
+ FT_FuncFix1, // 109 Trim() <- new
+ FT_FuncFix1, // 110 Clean() <- new
+ FT_FuncFix1, // 111 S() <- change in name
+ FT_FuncFix1, // 112 N() <- change in name
+ FT_FuncFix2, // 113 Exact() <- new
+ FT_NotImpl, // 114 App <- change in name
+ FT_FuncFix1, // 115 @@() <- new
+ FT_FuncFix3, // 116 Rate() <- new
+ FT_FuncFix3, // 117 Term()
+ FT_FuncFix3, // 118 Cterm()
+ FT_FuncFix3, // 119 Sln() <- new
+ FT_FuncFix4, // 120 Syd() <- new
+ FT_FuncFix4, // 121 Ddb() <- new
+ FT_Splfunc, // 122 Splfunc <- new
+ FT_FuncFix1, // 123 Sheets <- new
+ FT_FuncFix1, // 124 Info <- new
+ FT_FuncVar, // 125 Sumproduct <- new
+ FT_FuncFix1, // 126 Isrange <- new
+ FT_FuncVar, // 127 Dget <- new
+ FT_FuncVar, // 128 Dquery <- new
+ FT_FuncFix4, // 129 Coord <- new
+ FT_NOP, // 130 Reserved (internal) <- new
+ FT_FuncFix0, // 131 Today <- new
+ FT_FuncVar, // 132 Vdb <- new
+ FT_FuncVar, // 133 Dvars <- new
+ FT_FuncVar, // 134 Dstds <- new
+ FT_FuncVar, // 135 Vars <- new
+ FT_FuncVar, // 136 Stds <- new
+ FT_FuncFix2, // 137 D360 <- new
+ FT_NOP, // 138 Reserved (internal) <- new
+ FT_FuncFix0, // 139 Isapp <- new quantity ?
+ FT_FuncVar, // 140 Isaaf <- new quantity ?
+ FT_FuncFix1, // 141 Weekday <- new
+ FT_FuncFix3, // 142 Datedif <- new
+ FT_FuncVar, // 143 Rank <- new
+ FT_FuncFix2, // 144 Numberstring <- new
+ FT_FuncFix1, // 145 Datestring <- new
+ FT_FuncFix1, // 146 Decimal <- new
+ FT_FuncFix1, // 147 Hex <- new
+ FT_FuncFix4, // 148 Db <- new
+ FT_FuncFix4, // 149 Pmti <- new
+ FT_FuncFix4, // 150 Spi <- new
+ FT_FuncFix1, // 151 Fullp <- new
+ FT_FuncFix1, // 152 Halfp <- new
+ FT_FuncVar, // 153 Pureavg <- new
+ FT_FuncVar, // 154 Purecount <- new
+ FT_FuncVar, // 155 Puremax <- new
+ FT_FuncVar, // 156 Puremin <- new
+ FT_FuncVar, // 157 Purestd <- new
+ FT_FuncVar, // 158 Purevar <- new
+ FT_FuncVar, // 159 Purestds <- new
+ FT_FuncVar, // 160 Purevars <- new
+ FT_FuncFix3, // 161 Pmt2 <- new
+ FT_FuncFix3, // 162 Pv2 <- new
+ FT_FuncFix3, // 163 Fv2 <- new
+ FT_FuncFix3, // 164 Term2 <- new
+ FT_NotImpl, // 165-- <- new quantity ?
+ FT_FuncFix2, // 166 D360 (US-Version)
+ FT_NotImpl, // 167
+ FT_NotImpl, // 168
+ FT_NotImpl, // 169
+ FT_NotImpl, // 170
+ FT_NotImpl, // 171
+ FT_NotImpl, // 172
+ FT_NotImpl, // 173
+ FT_NotImpl, // 174
+ FT_NotImpl, // 175
+ FT_NotImpl, // 176
+ FT_NotImpl, // 177
+ FT_NotImpl, // 178
+ FT_NotImpl, // 179
+ FT_NotImpl, // 180
+ FT_NotImpl, // 181
+ FT_NotImpl, // 182
+ FT_NotImpl, // 183
+ FT_NotImpl, // 184
+ FT_NotImpl, // 185
+ FT_FuncVar, // 186 Solver <- new
+ FT_NotImpl, // 187
+ FT_NotImpl, // 188
+ FT_NotImpl, // 189
+ FT_NotImpl, // 190
+ FT_NotImpl, // 191
+ FT_NotImpl, // 192
+ FT_NotImpl, // 193
+ FT_NotImpl, // 194
+ FT_NotImpl, // 195
+ FT_NotImpl, // 196
+ FT_NotImpl, // 197
+ FT_NotImpl, // 198
+ FT_NotImpl, // 199
+ FT_NotImpl, // 200
+ FT_NotImpl, // 201
+ FT_NotImpl, // 202
+ FT_NotImpl, // 203
+ FT_NotImpl, // 204
+ FT_NotImpl, // 205
+ FT_NotImpl, // 206
+ FT_NotImpl, // 207
+ FT_NotImpl, // 208
+ FT_NotImpl, // 209
+ FT_NotImpl, // 210
+ FT_NotImpl, // 211
+ FT_NotImpl, // 212
+ FT_NotImpl, // 213
+ FT_NotImpl, // 214
+ FT_NotImpl, // 215
+ FT_NotImpl, // 216
+ FT_NotImpl, // 217
+ FT_NotImpl, // 218
+ FT_NotImpl, // 219
+ FT_NotImpl, // 220
+ FT_NotImpl, // 221
+ FT_NotImpl, // 222
+ FT_NotImpl, // 223
+ FT_NotImpl, // 224
+ FT_NotImpl, // 225
+ FT_NotImpl, // 226
+ FT_NotImpl, // 227
+ FT_NotImpl, // 228
+ FT_NotImpl, // 229
+ FT_NotImpl, // 230
+ FT_NotImpl, // 231
+ FT_NotImpl, // 232
+ FT_NotImpl, // 233
+ FT_NotImpl, // 234
+ FT_NotImpl, // 235
+ FT_NotImpl, // 236
+ FT_NotImpl, // 237
+ FT_NotImpl, // 238
+ FT_NotImpl, // 239
+ FT_NotImpl, // 240
+ FT_NotImpl, // 241
+ FT_NotImpl, // 242
+ FT_NotImpl, // 243
+ FT_NotImpl, // 244
+ FT_NotImpl, // 245
+ FT_NotImpl, // 246
+ FT_NotImpl, // 247
+ FT_NotImpl, // 248
+ FT_NotImpl, // 249
+ FT_NotImpl, // 250
+ FT_NotImpl, // 251
+ FT_NotImpl, // 252
+ FT_NotImpl, // 253
+ FT_NotImpl, // 254
+ FT_NotImpl, // 255
+ };
+ return pType[ nIndex ];
+}
+
+DefTokenId LotusToSc::IndexToTokenWK123( sal_uInt8 nIndex )
+{
+ static const DefTokenId pToken[ 256 ] =
+ { // Code Description
+ ocPush, // 0 8-Byte-IEEE-Long-Numbers
+ ocPush, // 1 Variable
+ ocPush, // 2 Range
+ ocPush, // 3 return
+ ocPush, // 4 Braces
+ ocPush, // 5 Numbers
+ ocPush, // 6 ASCII string
+ ocPush, // 7 Named range reference
+ ocPush, // 8 Absolute named range
+ ocPush, // 9 Err range reference
+ ocPush, // 10 Err cell reference
+ ocPush, // 11 Err constant
+ ocPush, // 12
+ ocPush, // 13
+ ocNegSub, // 14 Negation
+ ocAdd, // 15 Addition
+ ocSub, // 16 Subtraction
+ ocMul, // 17 Multiplication
+ ocDiv, // 18 Division
+ ocPow, // 19 Power of
+ ocEqual, // 20 Equality
+ ocNotEqual, // 21 Inequality
+ ocLessEqual, // 22 <=
+ ocGreaterEqual, // 23 >=
+ ocLess, // 24 <
+ ocGreater, // 25 >
+ ocAnd, // 26 And (logic)
+ ocOr, // 27 Or (logic)
+ ocNot, // 28 Not (logic)
+ ocPush, // 29 unary Plus
+ ocAmpersand, // 30 Concatenation
+ ocNotAvail, // 31 Not available
+ ocNoName, // 32 Error
+ ocAbs, // 33 Absolute Value ABS()
+ ocInt, // 34 Integer INT()
+ ocSqrt, // 35 Square Root
+ ocLog10, // 36 log10
+ ocLn, // 37 Natural logarithm
+ ocPi, // 38 Pi
+ ocSin, // 39 Sine
+ ocCos, // 40 Cosine
+ ocTan, // 41 Tangens
+ ocArcTan2, // 42 Arctangens 2 (4.Quadrant)
+ ocArcTan, // 43 Arctangens (2.Quadrant)
+ ocArcSin, // 44 Arcsine
+ ocArcCos, // 45 Arccosine
+ ocExp, // 46 Exponential function
+ ocMod, // 47 Modulo
+ ocChoose, // 48 Selection
+ ocIsNA, // 49 Is not available?
+ ocIsError, // 50 Is Error?
+ ocFalse, // 51 FALSE
+ ocTrue, // 52 TRUE
+ ocRandom, // 53 Random number
+ ocGetDate, // 54 Date
+ ocGetActDate, // 55 Today
+ ocPMT, // 56 Payment
+ ocPV, // 57 Present Value
+ ocFV, // 58 Future Value
+ ocIf, // 59 If... then... else...
+ ocGetDay, // 60 Day of Month
+ ocGetMonth, // 61 Month
+ ocGetYear, // 62 Year
+ ocRound, // 63 Round
+ ocGetTime, // 64 Time
+ ocGetHour, // 65 Hour
+ ocGetMin, // 66 Minute
+ ocGetSec, // 67 Second
+ ocIsValue, // 68 Is number?
+ ocIsString, // 69 Is text?
+ ocLen, // 70 Len()
+ ocValue, // 71 Val()
+ ocFixed, // 72 String() ocFixed alternatively + special case
+ ocMid, // 73 Mid()
+ ocChar, // 74 Char()
+ ocCode, // 75 Ascii()
+ ocFind, // 76 Find()
+ ocGetDateValue, // 77 Datevalue
+ ocGetTimeValue, // 78 Timevalue
+ ocNoName, // 79 Cellpointer
+ ocSum, // 80 Sum()
+ ocAverageA, // 81 Avg()
+ ocCount2, // 82 Cnt()
+ ocMinA, // 83 Min()
+ ocMaxA, // 84 Max()
+ ocVLookup, // 85 Vlookup()
+ ocNPV, // 86 Npv()
+ ocVarPA, // 87 Var()
+ ocStDevPA, // 88 Std()
+ ocIRR, // 89 Irr()
+ ocHLookup, // 90 Hlookup()
+ ocDBSum, // 91 XlfDsum
+ ocDBAverage, // 92 XlfDaverage
+ ocDBCount2, // 93 XlfDcount
+ ocDBMin, // 94 XlfDmin
+ ocDBMax, // 95 XlfDmax
+ ocDBVarP, // 96 XlfDvar
+ ocDBStdDevP, // 97 XlfDstdev
+ ocIndex, // 98 Index()
+ ocColumns, // 99 Cols()
+ ocRows, // 100 Rows()
+ ocRept, // 101 Repeat()
+ ocUpper, // 102 Upper()
+ ocLower, // 103 Lower()
+ ocLeft, // 104 Left()
+ ocRight, // 105 Right()
+ ocReplace, // 106 Replace()
+ ocProper, // 107 Proper()
+ ocNoName, // 108 Cell()
+ ocTrim, // 109 Trim()
+ ocClean, // 110 Clean()
+ ocNoName, // 111 F() (Excel: T()?)
+ ocNoName, // 112 W()
+ ocExact, // 113 Exact()
+ ocNoName, // 114 Call()
+ ocIndirect, // 115 @@()
+ ocRate, // 116 Rate()
+ ocNoName, // 117 Term()
+ ocNoName, // 118 Cterm()
+ ocSLN, // 119 Sln()
+ ocSYD, // 120 Syd(), Soy()
+ ocDDB, // 121 Ddb()
+ ocNoName, // 122 Splfunc
+ ocNoName, // 123 Sheets
+ ocNoName, // 124 Info
+ ocSumProduct, // 125 Sumproduct
+ ocNoName, // 126 Isrange
+ ocDBGet, // 127 Dget
+ ocNoName, // 128 Dquery
+ ocNoName, // 129 Coord
+ ocNoName, // 130 Reserved (internal)
+ ocGetActDate, // 131 Today
+ ocNoName, // 132 Vdb
+ ocDBVar, // 133 Dvars
+ ocDBStdDev, // 134 Dstds
+ ocVarA, // 135 Vars
+ ocStDevA, // 136 Stds
+ ocGetDiffDate360, // 137 D360
+ ocNoName, // 138 Reserved (internal)
+ ocNoName, // 139 Isapp
+ ocNoName, // 140 Isaaf
+ ocGetDayOfWeek, // 141 Weekday
+ ocGetDiffDate, // 142 Datedif
+ ocRank, // 143 Rank
+ ocNoName, // 144 Numberstring
+ ocNoName, // 145 Datestring
+ ocNoName, // 146 Decimal
+ ocNoName, // 147 Hex
+ ocNoName, // 148 Db
+ ocNoName, // 149 Pmti
+ ocNoName, // 150 Spi
+ ocNoName, // 151 Fullp
+ ocNoName, // 152 Halfp
+ ocAverage, // 153 Pureavg
+ ocCount, // 154 Purecount
+ ocMax, // 155 Puremax
+ ocMin, // 156 Puremin
+ ocStDevP, // 157 Purestd
+ ocVarP, // 158 Purevar
+ ocStDev, // 159 Purestds
+ ocVar, // 160 Purevars
+ ocNoName, // 161 Pmt2
+ ocNoName, // 162 Pv2
+ ocNoName, // 163 Fv2
+ ocNoName, // 164 Term2
+ ocNoName, // 165-- <- new quantity ?
+ ocGetDiffDate360, // 166 D360 (US-Version, alternatively as other D360 function)
+ ocNoName, // 167
+ ocNoName, // 168
+ ocNoName, // 169
+ ocNoName, // 170
+ ocNoName, // 171
+ ocNoName, // 172
+ ocNoName, // 173
+ ocNoName, // 174
+ ocNoName, // 175
+ ocNoName, // 176
+ ocNoName, // 177
+ ocNoName, // 178
+ ocNoName, // 179
+ ocNoName, // 180
+ ocNoName, // 181
+ ocNoName, // 182
+ ocNoName, // 183
+ ocNoName, // 184
+ ocNoName, // 185
+ ocNoName, // 186
+ ocNoName, // 187
+ ocNoName, // 188
+ ocNoName, // 189
+ ocNoName, // 190
+ ocNoName, // 191
+ ocNoName, // 192
+ ocNoName, // 193
+ ocNoName, // 194
+ ocNoName, // 195
+ ocNoName, // 196
+ ocNoName, // 197
+ ocNoName, // 198
+ ocNoName, // 199
+ ocNoName, // 200
+ ocNoName, // 201
+ ocNoName, // 202
+ ocNoName, // 203
+ ocNoName, // 204
+ ocNoName, // 205
+ ocNoName, // 206 ?
+ ocNoName, // 207
+ ocNoName, // 208
+ ocNoName, // 209
+ ocNoName, // 210
+ ocNoName, // 211
+ ocNoName, // 212
+ ocNoName, // 213
+ ocNoName, // 214
+ ocNoName, // 215
+ ocNoName, // 216
+ ocNoName, // 217
+ ocNoName, // 218
+ ocNoName, // 219
+ ocNoName, // 220
+ ocNoName, // 221
+ ocNoName, // 222
+ ocNoName, // 223
+ ocNoName, // 224
+ ocNoName, // 225
+ ocNoName, // 226
+ ocNoName, // 227
+ ocNoName, // 228
+ ocNoName, // 229
+ ocNoName, // 230
+ ocNoName, // 231
+ ocNoName, // 232
+ ocNoName, // 233
+ ocNoName, // 234
+ ocNoName, // 235
+ ocNoName, // 236
+ ocNoName, // 237
+ ocNoName, // 238
+ ocNoName, // 239
+ ocNoName, // 240
+ ocNoName, // 241
+ ocNoName, // 242
+ ocNoName, // 243
+ ocNoName, // 244
+ ocNoName, // 245
+ ocNoName, // 246
+ ocNoName, // 247
+ ocNoName, // 248
+ ocNoName, // 249
+ ocNoName, // 250
+ ocNoName, // 251
+ ocNoName, // 252
+ ocNoName, // 253
+ ocNoName, // 254
+ ocNoName // 255 ?
+ };
+
+ return pToken[ nIndex ];
+}
+
+const char* GetAddInName( const sal_uInt8 n )
+{
+ static const char* pNames[ 256 ] =
+ {
+ nullptr, // 0 8-Byte-IEEE-Float
+ nullptr, // 1 Variable
+ nullptr, // 2 Range
+ nullptr, // 3 return
+ nullptr, // 4 Braces
+ nullptr, // 5 2-Byte integer
+ nullptr, // 6 ASCII string
+ nullptr, // 7 Named range reference
+ nullptr, // 8 Absolute named range
+ nullptr, // 9 Err range reference
+ nullptr, // 10 Err cell reference
+ nullptr, // 11 Err constant
+ nullptr, // 12
+ nullptr, // 13
+ nullptr, // 14 Negation
+ nullptr, // 15 Addition
+ nullptr, // 16 Subtraction
+ nullptr, // 17 Multiplication
+ nullptr, // 18 Division
+ nullptr, // 19 Power of
+ nullptr, // 20 equal
+ nullptr, // 21 unequal
+ nullptr, // 22 <=
+ nullptr, // 23 >=
+ nullptr, // 24 <
+ nullptr, // 25 >
+ nullptr, // 26 And (logic)
+ nullptr, // 27 Or (logic)
+ nullptr, // 28 Not (logic)
+ nullptr, // 29 unary Plus
+ nullptr, // 30 Concatenation
+ nullptr, // 31 Not applicable
+ nullptr, // 32 Error
+ nullptr, // 33 Absolute Value ABS()
+ nullptr, // 34 Integer INT()
+ nullptr, // 35 Square Root
+ nullptr, // 36 log10
+ nullptr, // 37 Natural logarithm
+ nullptr, // 38 PI
+ nullptr, // 39 Sine
+ nullptr, // 40 Cosine
+ nullptr, // 41 Tangens
+ nullptr, // 42 Arctangens 2 (4.Quadrant)
+ nullptr, // 43 Arctangens (2.Quadrant)
+ nullptr, // 44 Arcsine
+ nullptr, // 45 Arccosine
+ nullptr, // 46 Exponential function
+ nullptr, // 47 Modulo
+ nullptr, // 48 Selection
+ nullptr, // 49 Is not applicable?
+ nullptr, // 50 Is Error?
+ nullptr, // 51 FALSE
+ nullptr, // 52 TRUE
+ nullptr, // 53 Random number
+ nullptr, // 54 Date
+ nullptr, // 55 Today
+ nullptr, // 56 Payment
+ nullptr, // 57 Present Value
+ nullptr, // 58 Future Value
+ nullptr, // 59 If ... then ... else ...
+ nullptr, // 60 Day of Month
+ nullptr, // 61 Month
+ nullptr, // 62 Year
+ nullptr, // 63 Round
+ nullptr, // 64 Time
+ nullptr, // 65 Hour
+ nullptr, // 66 Minute
+ nullptr, // 67 Second
+ nullptr, // 68 Is Number?
+ nullptr, // 69 Is Text?
+ nullptr, // 70 Len()
+ nullptr, // 71 Val()
+ nullptr, // 72 String() ocFixed as substitute + special case
+ nullptr, // 73 Mid()
+ nullptr, // 74 Char()
+ nullptr, // 75 Ascii()
+ nullptr, // 76 Find()
+ nullptr, // 77 Datevalue
+ nullptr, // 78 Timevalue
+ "ZELLZEIGER", // 79 Cellpointer
+ nullptr, // 80 Sum()
+ nullptr, // 81 Avg()
+ nullptr, // 82 Cnt()
+ nullptr, // 83 Min()
+ nullptr, // 84 Max()
+ nullptr, // 85 Vlookup()
+ nullptr, // 86 Npv()
+ nullptr, // 87 Var()
+ nullptr, // 88 Std()
+ nullptr, // 89 Irr()
+ nullptr, // 90 Hlookup()
+ nullptr, // 91 XlfDsum
+ nullptr, // 92 XlfDaverage
+ nullptr, // 93 XlfDcount
+ nullptr, // 94 XlfDmin
+ nullptr, // 95 XlfDmax
+ nullptr, // 96 XlfDvar
+ nullptr, // 97 XlfDstdev
+ nullptr, // 98 Index()
+ nullptr, // 99 Cols()
+ nullptr, // 100 Rows()
+ nullptr, // 101 Repeat()
+ nullptr, // 102 Upper()
+ nullptr, // 103 Lower()
+ nullptr, // 104 Left()
+ nullptr, // 105 Right()
+ nullptr, // 106 Replace()
+ nullptr, // 107 Proper()
+ "ZELLE", // 108 Cell()
+ nullptr, // 109 Trim()
+ nullptr, // 110 Clean()
+ "F", // 111 F() (Excel: T()?)
+ "W", // 112 W()
+ nullptr, // 113 Exact()
+ nullptr, // 114 Call()
+ nullptr, // 115 @@()
+ nullptr, // 116 Rate()
+ "TERM", // 117 Term()
+ "CTERM", // 118 Cterm()
+ nullptr, // 119 Sln()
+ nullptr, // 120 Syd(), Soy()
+ nullptr, // 121 Ddb()
+ "SplFunc", // 122 Splfunc
+ "BLAETTER", // 123 Sheets
+ "INFO", // 124 Info
+ nullptr, // 125 Sumproduct
+ "ISTBEREICH", // 126 Isrange
+ nullptr, // 127 Dget
+ "DABFRAGE", // 128 Dquery
+ "KOORD", // 129 Coord
+ nullptr, // 130 Reserved (internal)
+ nullptr, // 131 Today
+ nullptr, // 132 Vdb
+ nullptr, // 133 Dvars
+ nullptr, // 134 Dstds
+ nullptr, // 135 Vars
+ nullptr, // 136 Stds
+ nullptr, // 137 D360
+ nullptr, // 138 Reserved (internal)
+ nullptr, // 139 Isapp
+ "ISTDEFZUS", // 140 Isaaf
+ nullptr, // 141 Weekday
+ nullptr, // 142 Datedif
+ nullptr, // 143 Rank
+ nullptr, // 144 Numberstring
+ "DATUMFOLGE", // 145 Datestring
+ "DEZIMAL", // 146 Decimal
+ "HEX", // 147 Hex
+ nullptr, // 148 Db
+ nullptr, // 149 Pmti
+ nullptr, // 150 Spi
+ nullptr, // 151 Fullp
+ nullptr, // 152 Halfp
+ "PURMITTELWERT", // 153 Pureavg
+ "PURquantity", // 154 Purecount
+ "PURMAX", // 155 Puremax
+ "PURMIN", // 156 Puremin
+ "PURSTDABW", // 157 Purestd
+ "PURVAR", // 158 Purevar
+ "PURSTDABWP", // 159 Purestds
+ "PURVARP", // 160 Purevars
+ nullptr, // 161 Pmt2
+ nullptr, // 162 Pv2
+ nullptr, // 163 Fv2
+ nullptr, // 164 Term2
+ nullptr, // 165-- <- new quantity ?
+ nullptr, // 166 D360 (US-Version, alternatively as in D360-function)
+ nullptr, // 167
+ nullptr, // 168
+ nullptr, // 169
+ nullptr, // 170
+ nullptr, // 171
+ nullptr, // 172
+ nullptr, // 173
+ nullptr, // 174
+ nullptr, // 175
+ nullptr, // 176
+ nullptr, // 177
+ nullptr, // 178
+ nullptr, // 179
+ nullptr, // 180
+ nullptr, // 181
+ nullptr, // 182
+ nullptr, // 183
+ nullptr, // 184
+ nullptr, // 185
+ nullptr, // 186
+ nullptr, // 187
+ nullptr, // 188
+ nullptr, // 189
+ nullptr, // 190
+ nullptr, // 191
+ nullptr, // 192
+ nullptr, // 193
+ nullptr, // 194
+ nullptr, // 195
+ nullptr, // 196
+ nullptr, // 197
+ nullptr, // 198
+ nullptr, // 199
+ nullptr, // 200
+ nullptr, // 201
+ nullptr, // 202
+ nullptr, // 203
+ nullptr, // 204
+ nullptr, // 205
+ nullptr, // 206 TODO: ?
+ nullptr, // 207
+ nullptr, // 208
+ nullptr, // 209
+ nullptr, // 210
+ nullptr, // 211
+ nullptr, // 212
+ nullptr, // 213
+ nullptr, // 214
+ nullptr, // 215
+ nullptr, // 216
+ nullptr, // 217
+ nullptr, // 218
+ nullptr, // 219
+ nullptr, // 220
+ nullptr, // 221
+ nullptr, // 222
+ nullptr, // 223
+ nullptr, // 224
+ nullptr, // 225
+ nullptr, // 226
+ nullptr, // 227
+ nullptr, // 228
+ nullptr, // 229
+ nullptr, // 230
+ nullptr, // 231
+ nullptr, // 232
+ nullptr, // 233
+ nullptr, // 234
+ nullptr, // 235
+ nullptr, // 236
+ nullptr, // 237
+ nullptr, // 238
+ nullptr, // 239
+ nullptr, // 240
+ nullptr, // 241
+ nullptr, // 242
+ nullptr, // 243
+ nullptr, // 244
+ nullptr, // 245
+ nullptr, // 246
+ nullptr, // 247
+ nullptr, // 248
+ nullptr, // 249
+ nullptr, // 250
+ nullptr, // 251
+ nullptr, // 252
+ nullptr, // 253
+ nullptr, // 254
+ nullptr // 255 TODO: ?
+ };
+
+ return pNames[ n ];
+}
+
+static DefTokenId lcl_KnownAddIn( std::string_view rTest )
+{
+ DefTokenId eId = ocNoName;
+
+ if (rTest == "FACT")
+ eId = ocFact;
+ else if (rTest == "ISEMPTY")
+ eId=ocIsEmpty;
+ else if (rTest == "DEGTORAD")
+ eId=ocRad;
+ else if (rTest == "RADTODEG")
+ eId=ocDeg;
+ else if (rTest == "SIGN")
+ eId=ocPlusMinus;
+ else if (rTest == "ACOSH")
+ eId=ocArcCosHyp;
+ else if (rTest == "ACOTH")
+ eId=ocArcCotHyp;
+ else if (rTest == "ASINH")
+ eId=ocArcSinHyp;
+ else if (rTest == "ATANH")
+ eId=ocArcTanHyp;
+ else if (rTest == "COSH")
+ eId=ocCosHyp;
+ else if (rTest == "COTH")
+ eId=ocCotHyp;
+ else if (rTest == "SINH")
+ eId=ocSinHyp;
+ else if (rTest == "TANH")
+ eId=ocTanHyp;
+ else if (rTest == "EVEN")
+ eId=ocEven;
+ else if (rTest == "ODD")
+ eId=ocOdd;
+ else if (rTest == "ACOT")
+ eId=ocArcCot;
+ else if (rTest == "COT")
+ eId=ocCot;
+ else if (rTest == "TRUNC")
+ eId=ocTrunc;
+ else if (rTest == "GEOMEAN")
+ eId=ocGeoMean;
+ else if (rTest == "HARMEAN")
+ eId=ocHarMean;
+ else if (rTest == "CORREL")
+ eId=ocCorrel;
+ else if (rTest == "MEDIAN")
+ eId=ocMedian;
+ else if (rTest == "COV")
+ eId=ocCovar;
+ else if (rTest == "SKEWNESS")
+ eId=ocSkew;
+ else if (rTest == "CHITEST")
+ eId=ocChiTest;
+ else if (rTest == "FTEST")
+ eId=ocFTest;
+ else if (rTest == "AVEDEV")
+ eId=ocAveDev;
+ else if (rTest == "PRODUCT")
+ eId=ocProduct;
+ else if (rTest == "PERMUT")
+ eId=ocPermut;
+ else if (rTest == "GAMMALN")
+ eId=ocGammaLn;
+ else if (rTest =="POISSON")
+ eId=ocPoissonDist;
+ else if (rTest == "NORMAL")
+ eId=ocNormDist;
+ else if (rTest == "CRITBINOMIAL")
+ eId=ocCritBinom;
+ else if (rTest == "TERM")
+ eId=ocNper;
+ else if (rTest == "CTERM")
+ eId=ocNper;
+ else if (rTest == "SUMIF")
+ eId=ocSumIf;
+ else if (rTest == "COUNTIF")
+ eId=ocCountIf;
+ else if (rTest == "DPURECOUNT")
+ eId=ocDBCount;
+ else if (rTest == "CSC")
+ eId=ocCosecant;
+ else if (rTest == "CSCH")
+ eId=ocCosecantHyp;
+ else if (rTest == "LARGE")
+ eId=ocLarge;
+ else if (rTest == "SMALL")
+ eId=ocSmall;
+ else if (rTest == "MODULO")
+ eId=ocMod;
+ else if (rTest == "ROUNDDOWN")
+ eId=ocRoundDown;
+ else if (rTest == "ROUNDUP")
+ eId=ocRoundUp;
+ else if (rTest == "SEC")
+ eId=ocSecant;
+ else if (rTest == "SECH")
+ eId=ocSecantHyp;
+ return eId;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotimpop.cxx b/sc/source/filter/lotus/lotimpop.cxx
new file mode 100644
index 000000000..df7f17380
--- /dev/null
+++ b/sc/source/filter/lotus/lotimpop.cxx
@@ -0,0 +1,489 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "lotfilter.hxx"
+#include <lotimpop.hxx>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <global.hxx>
+
+#include <lotfntbf.hxx>
+#include <lotform.hxx>
+#include <tool.h>
+#include <namebuff.hxx>
+#include <lotattr.hxx>
+#include <stringutil.hxx>
+#include <config_fuzzers.h>
+
+
+static osl::Mutex aLotImpSemaphore;
+
+ImportLotus::ImportLotus(LotusContext &rContext, SvStream& aStream, rtl_TextEncoding eQ)
+ : ImportTyp(rContext.rDoc, eQ)
+ , pIn(&aStream)
+ , aConv(rContext, *pIn, rContext.rDoc.GetSharedStringPool(), eQ, false)
+ , nTab(0)
+ , nExtTab(0)
+{
+ // good point to start locking of import lotus
+ aLotImpSemaphore.acquire();
+}
+
+ImportLotus::~ImportLotus()
+{
+ // no need 4 pLotusRoot anymore
+ aLotImpSemaphore.release();
+}
+
+void ImportLotus::Bof()
+{
+ sal_uInt16 nFileCode, nFileSub, nSaveCnt;
+ sal_uInt8 nMajorId, nMinorId, nFlags;
+
+ Read( nFileCode );
+ Read( nFileSub );
+ LotusContext &rContext = aConv.getContext();
+ Read( rContext.aActRange );
+ Read( nSaveCnt );
+ Read( nMajorId );
+ Read( nMinorId );
+ Skip( 1 );
+ Read( nFlags );
+
+ if (!pIn->good())
+ return;
+
+ if( nFileSub == 0x0004 )
+ {
+ if( nFileCode == 0x1000 )
+ {// <= WK3
+ rContext.eFirstType = rContext.eActType = Lotus123Typ::WK3;
+ }
+ else if( nFileCode == 0x1002 )
+ {// WK4
+ rContext.eFirstType = rContext.eActType = Lotus123Typ::WK4;
+ }
+ }
+}
+
+bool ImportLotus::BofFm3()
+{
+ sal_uInt16 nFileCode(0), nFileSub(0);
+
+ Read( nFileCode );
+ Read( nFileSub );
+
+ return ( nFileCode == 0x8007 && ( nFileSub == 0x0000 || nFileSub == 0x00001 ) );
+}
+
+void ImportLotus::Columnwidth( sal_uInt16 nRecLen )
+{
+ SAL_WARN_IF( nRecLen < 4, "sc.filter", "*ImportLotus::Columnwidth(): Record too short!" );
+
+ sal_uInt16 nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 2;
+
+ sal_uInt8 nLTab(0), nWindow2(0);
+ Read( nLTab );
+ Read( nWindow2 );
+
+ if( !rD.HasTable( static_cast<SCTAB> (nLTab) ) )
+ rD.MakeTable( static_cast<SCTAB> (nLTab) );
+
+ if( nWindow2 )
+ return;
+
+ Skip( 2 );
+
+ while (nCnt && pIn->good())
+ {
+ sal_uInt8 nCol(0), nSpaces(0);
+ Read( nCol );
+ Read( nSpaces );
+ // Attention: ambiguous Correction factor!
+ rD.SetColWidth( static_cast<SCCOL> (nCol), static_cast<SCTAB> (nLTab), static_cast<sal_uInt16>( TWIPS_PER_CHAR * 1.28 * nSpaces ) );
+
+ nCnt--;
+ }
+
+ SAL_WARN_IF(!pIn->good(), "sc.filter", "*ImportLotus::Columnwidth(): short read");
+}
+
+void ImportLotus::Hiddencolumn( sal_uInt16 nRecLen )
+{
+ SAL_WARN_IF( nRecLen < 4, "sc.filter", "*ImportLotus::Hiddencolumn(): Record too short!" );
+
+ sal_uInt16 nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 2;
+
+ sal_uInt8 nLTab(0), nWindow2(0);
+ Read( nLTab );
+ Read( nWindow2 );
+
+ if( nWindow2 )
+ return;
+
+ Skip( 2 );
+
+ while (nCnt && pIn->good())
+ {
+ sal_uInt8 nCol(0);
+ Read( nCol );
+
+ rD.SetColHidden(static_cast<SCCOL>(nCol), static_cast<SCCOL>(nCol), static_cast<SCTAB>(nLTab), true);
+ nCnt--;
+ }
+
+ SAL_WARN_IF(!pIn->good(), "sc.filter", "*ImportLotus::Hiddencolumn(): short read");
+}
+
+void ImportLotus::Userrange()
+{
+ sal_uInt16 nRangeType;
+ ScRange aScRange;
+
+ Read( nRangeType );
+
+ char aBuffer[ 17 ];
+ aBuffer[pIn->ReadBytes(aBuffer, 16)] = 0;
+ OUString aName(aBuffer, strlen(aBuffer), eQuellChar);
+
+ Read(aScRange);
+
+ if (!pIn->good())
+ {
+ SAL_WARN("sc.filter", "invalid range");
+ return;
+ }
+
+ LotusContext &rContext = aConv.getContext();
+ rContext.pRngNmBffWK3->Add( rContext.rDoc, aName, aScRange );
+}
+
+void ImportLotus::Errcell()
+{
+ ScAddress aA;
+
+ Read( aA );
+
+ if (!pIn->good() || !rD.ValidAddress(aA))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rD.EnsureTable(aA.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aA
+ rD.SetString(aA, "#ERR!", &aParam);
+}
+
+void ImportLotus::Nacell()
+{
+ ScAddress aA;
+
+ Read( aA );
+
+ if (!pIn->good() || !rD.ValidAddress(aA))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rD.EnsureTable(aA.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aA
+ rD.SetString(aA, "#NA!", &aParam);
+}
+
+void ImportLotus::Labelcell()
+{
+ ScAddress aA;
+ OUString aLabel;
+ char cAlign;
+
+ Read( aA );
+ Read( cAlign );
+ Read( aLabel );
+
+ if (!pIn->good() || !rD.ValidAddress(aA))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rD.EnsureTable(aA.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aA
+ rD.SetString(aA, aLabel, &aParam);
+}
+
+void ImportLotus::Numbercell()
+{
+ ScAddress aAddr;
+ double fVal;
+
+ Read( aAddr );
+ Read( fVal );
+
+ if (!pIn->good() || !rD.ValidAddress(aAddr))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ rD.EnsureTable(aAddr.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rD.SetValue(aAddr, fVal);
+}
+
+void ImportLotus::Smallnumcell()
+{
+ ScAddress aAddr;
+ sal_Int16 nVal;
+
+ Read( aAddr );
+ Read( nVal );
+
+ if (!pIn->good() || !rD.ValidAddress(aAddr))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ rD.EnsureTable(aAddr.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rD.SetValue(aAddr, SnumToDouble(nVal));
+}
+
+void ImportLotus::Formulacell( sal_uInt16 n )
+{
+ SAL_WARN_IF( !pIn, "sc.filter", "-ImportLotus::Formulacell(): Null-Stream!" );
+
+ ScAddress aAddr;
+
+ Read( aAddr );
+ Skip( 10 );
+
+ n -= std::min<sal_uInt16>(n, 14);
+
+ std::unique_ptr<ScTokenArray> pErg;
+ sal_Int32 nRest = n;
+
+ aConv.Reset( aAddr );
+ aConv.SetWK3();
+ aConv.Convert( pErg, nRest );
+ if (!aConv.good())
+ return;
+
+ if (!pIn->good() || !rD.ValidAddress(aAddr))
+ {
+ SAL_WARN("sc.filter", "invalid address");
+ return;
+ }
+
+ ScFormulaCell* pCell = pErg ? new ScFormulaCell(rD, aAddr, std::move(pErg)) : new ScFormulaCell(rD, aAddr);
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ rD.EnsureTable(aAddr.Tab());
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rD.SetFormulaCell(aAddr, pCell);
+}
+
+void ImportLotus::Read( OUString &r )
+{
+ ScfTools::AppendCString( *pIn, r, eQuellChar );
+}
+
+void ImportLotus::RowPresentation( sal_uInt16 nRecLen )
+{
+ SAL_WARN_IF( nRecLen < 5, "sc.filter", "*ImportLotus::RowPresentation(): Record too short!" );
+
+ sal_uInt16 nCnt = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 8;
+
+ sal_uInt8 nLTab(0);
+ Read( nLTab );
+ Skip( 1 );
+
+ while (nCnt && pIn->good())
+ {
+ sal_uInt16 nRow(0);
+ Read( nRow );
+ sal_uInt16 nHeight(0);
+ Read( nHeight );
+ Skip( 2 );
+ sal_uInt8 nFlags(0);
+ Read( nFlags );
+ Skip( 1 );
+
+ if( nFlags & 0x02 ) // Fixed / Stretch to fit fonts
+ { // fixed
+ // Height in Lotus in 1/32 Points
+ nHeight *= 20; // -> 32 * TWIPS
+ nHeight /= 32; // -> TWIPS
+
+ rD.SetRowFlags( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab), rD.GetRowFlags( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab) ) | CRFlags::ManualSize );
+
+ rD.SetRowHeight( static_cast<SCROW> (nRow), static_cast<SCTAB> (nLTab), nHeight );
+ }
+
+ nCnt--;
+ }
+}
+
+void ImportLotus::NamedSheet()
+{
+ sal_uInt16 nTmpTab(0);
+ Read(nTmpTab);
+ OUString aName;
+ Read(aName);
+
+ SCTAB nLTab(SanitizeTab(static_cast<SCTAB>(nTmpTab)));
+#if ENABLE_FUZZERS
+ //ofz#14167 arbitrary sheet limit to make fuzzing useful
+ if (nLTab > 5)
+ nLTab = 5;
+#endif
+
+ if (rD.HasTable(nLTab))
+ rD.RenameTab(nLTab, aName);
+ else
+ rD.InsertTab(nLTab, aName);
+}
+
+void ImportLotus::Font_Face()
+{
+ sal_uInt8 nNum;
+ OUString aName;
+
+ Read( nNum );
+
+ if( nNum >= LotusFontBuffer::nSize )
+ return; // nonsense
+
+ Read( aName );
+
+ LotusContext &rContext = aConv.getContext();
+ rContext.maFontBuff.SetName( nNum, aName );
+}
+
+void ImportLotus::Font_Type()
+{
+ LotusContext &rContext = aConv.getContext();
+ for( sal_uInt16 nCnt = 0 ; nCnt < LotusFontBuffer::nSize ; nCnt++ )
+ {
+ sal_uInt16 nType;
+ Read( nType );
+ rContext.maFontBuff.SetType( nCnt, nType );
+ }
+}
+
+void ImportLotus::Font_Ysize()
+{
+ LotusContext &rContext = aConv.getContext();
+ for( sal_uInt16 nCnt = 0 ; nCnt < LotusFontBuffer::nSize ; nCnt++ )
+ {
+ sal_uInt16 nSize;
+ Read( nSize );
+ rContext.maFontBuff.SetHeight( nCnt, nSize );
+ }
+}
+
+void ImportLotus::Row_( const sal_uInt16 nRecLen )
+{
+ SAL_WARN_IF( nExtTab < 0, "sc.filter", "*ImportLotus::Row_(): not possible!" );
+
+ sal_uInt16 nCntDwn = (nRecLen < 4) ? 0 : ( nRecLen - 4 ) / 5;
+ SCCOL nColCnt = 0;
+ sal_uInt8 nRepeats;
+ LotAttrWK3 aAttr;
+
+ bool bCenter = false;
+ SCCOL nCenterStart = 0, nCenterEnd = 0;
+ LotusContext &rContext = aConv.getContext();
+
+ sal_uInt16 nTmpRow(0);
+ Read(nTmpRow);
+ SCROW nRow(rContext.rDoc.SanitizeRow(static_cast<SCROW>(nTmpRow)));
+ sal_uInt16 nHeight(0);
+ Read(nHeight);
+
+ nHeight &= 0x0FFF;
+ nHeight *= 22;
+
+ SCTAB nDestTab(static_cast<SCTAB>(nExtTab));
+
+ if( nHeight )
+ rD.SetRowHeight(nRow, nDestTab, nHeight);
+
+ while( nCntDwn )
+ {
+ Read( aAttr );
+ Read( nRepeats );
+
+ if( aAttr.HasStyles() )
+ rContext.maAttrTable.SetAttr(
+ rContext, nColCnt, static_cast<SCCOL> ( nColCnt + nRepeats ), nRow, aAttr );
+
+ // Do this here and NOT in class LotAttrTable, as we only add attributes if the other
+ // attributes are set
+ // -> for Center-Attribute default is centered
+ if( aAttr.IsCentered() )
+ {
+ if( bCenter )
+ {
+ if (rD.HasData(nColCnt, nRow, nDestTab))
+ {
+ // new Center after previous Center
+ rD.DoMerge( nCenterStart, nRow, nCenterEnd, nRow, nDestTab);
+ nCenterStart = nColCnt;
+ }
+ }
+ else
+ {
+ // fully new Center
+ bCenter = true;
+ nCenterStart = nColCnt;
+ }
+ nCenterEnd = nColCnt + static_cast<SCCOL>(nRepeats);
+ }
+ else
+ {
+ if( bCenter )
+ {
+ // possibly reset old Center
+ rD.DoMerge( nCenterStart, nRow, nCenterEnd, nRow, nDestTab );
+ bCenter = false;
+ }
+ }
+
+ nColCnt = nColCnt + static_cast<SCCOL>(nRepeats);
+ nColCnt++;
+
+ nCntDwn--;
+ }
+
+ if( bCenter )
+ // possibly reset old Center
+ rD.DoMerge( nCenterStart, nRow, nCenterEnd, nRow, nDestTab );
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotread.cxx b/sc/source/filter/lotus/lotread.cxx
new file mode 100644
index 000000000..240ac323c
--- /dev/null
+++ b/sc/source/filter/lotus/lotread.cxx
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include <docoptio.hxx>
+
+#include <scdll.hxx>
+#include <scerrors.hxx>
+#include "lotfilter.hxx"
+#include <lotimpop.hxx>
+#include <lotattr.hxx>
+#include <fprogressbar.hxx>
+
+#include <sal/log.hxx>
+
+ErrCode ImportLotus::parse()
+{
+ enum STATE
+ {
+ S_START, // analyse first BOF
+ S_WK3, // in WK3-Section
+ S_WK4,
+ S_FM3,
+ S_END // Import finished
+ };
+
+ sal_uInt16 nOp;
+ sal_uInt16 nRecLen;
+ sal_uInt32 nNextRec = 0;
+ ErrCode eRet = ERRCODE_NONE;
+// ScFormulaCell *pLastFormCell;
+
+ STATE eCurrent = S_START;
+
+ nTab = 0;
+ nExtTab = -2;
+
+ pIn->Seek( nNextRec );
+
+ // start progressbar
+ ScfStreamProgressBar aPrgrsBar( *pIn, rD.GetDocumentShell() );
+ LotusContext &rContext = aConv.getContext();
+ while( eCurrent != S_END )
+ {
+ pIn->ReadUInt16( nOp ).ReadUInt16( nRecLen );
+
+ if (!pIn->good() || nNextRec > SAL_MAX_UINT32 - nRecLen - 4)
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ eCurrent = S_END;
+ if (!pIn->good())
+ break; // while
+ }
+
+ nNextRec += nRecLen + 4;
+
+ switch( eCurrent )
+ {
+
+ case S_START: // S_START
+ if( nOp )
+ {
+ eRet = SCERR_IMPORT_UNKNOWN_WK;
+ eCurrent = S_END;
+ }
+ else
+ {
+ if( nRecLen > 2 )
+ {
+ Bof();
+ switch (rContext.eFirstType)
+ {
+ case Lotus123Typ::WK3: eCurrent = S_WK3; break;
+ case Lotus123Typ::WK4: eCurrent = S_WK4; break;
+ default:
+ eRet = SCERR_IMPORT_UNKNOWN_WK;
+ eCurrent = S_END;
+ }
+ }
+ else
+ {
+ eCurrent = S_END; // TODO: add here something for <= WK1!
+ eRet = ErrCode(0xFFFFFFFF);
+ }
+ }
+ break;
+
+ case S_WK3: // S_WK3
+ case S_WK4: // S_WK4
+ switch( nOp )
+ {
+ case 0x0001: // EOF
+ eCurrent = S_FM3;
+ nTab++;
+ break;
+
+ case 0x0002: // PASSWORD
+ eRet = SCERR_IMPORT_FILEPASSWD;
+ eCurrent = S_END;
+ break;
+
+ case 0x0007: // COLUMNWIDTH
+ Columnwidth( nRecLen );
+ break;
+
+ case 0x0008: // HIDDENCOLUMN
+ Hiddencolumn( nRecLen );
+ break;
+
+ case 0x0009: // USERRANGE
+ Userrange();
+ break;
+
+ case 0x0013: // FORMAT
+
+ break;
+ case 0x0014: // ERRCELL
+ Errcell();
+ break;
+
+ case 0x0015: // NACELL
+ Nacell();
+ break;
+
+ case 0x0016: // LABELCELL
+ Labelcell();
+ break;
+
+ case 0x0017: // NUMBERCELL
+ Numbercell();
+ break;
+
+ case 0x0018: // SMALLNUMCELL
+ Smallnumcell();
+ break;
+
+ case 0x0019: // FORMULACELL
+ Formulacell( nRecLen );
+ break;
+
+ case 0x001b: // extended attributes
+ if (nRecLen > 2)
+ {
+ sal_uInt16 nSubType(0);
+ Read( nSubType );
+ nRecLen -= 2;
+ switch( nSubType )
+ {
+ case 2007: // ROW PRESENTATION
+ RowPresentation( nRecLen );
+ break;
+
+ case 14000: // NAMED SHEET
+ NamedSheet();
+ break;
+ }
+ }
+ else
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ eCurrent = S_END;
+ }
+ }
+
+ break;
+
+ case S_FM3: // S_FM3
+ break;
+
+ case S_END: // S_END
+ break;
+ }
+
+ SAL_WARN_IF( nNextRec < pIn->Tell(), "sc.filter",
+ "*ImportLotus::Read(): Read too much..." );
+
+ pIn->Seek( nNextRec );
+ aPrgrsBar.Progress();
+ }
+
+ // TODO: eliminate stupid names
+ SCTAB nTabs = rD.GetTableCount();
+ SCTAB nCnt;
+ OUString aTabName;
+ OUString aBaseName;
+ if( nTabs != 0 )
+ {
+ if( nTabs > 1 )
+ {
+ rD.GetName( 0, aBaseName );
+ aBaseName = aBaseName.copy(0, aBaseName.getLength()-1);
+ }
+ for( nCnt = 1 ; nCnt < nTabs ; nCnt++ )
+ {
+ SAL_WARN_IF( !rD.HasTable( nCnt ), "sc.filter",
+ "-ImportLotus::Read(): Where is my table?!" );
+ rD.GetName( nCnt, aTabName );
+ if( aTabName == "temp" )
+ {
+ aTabName = aBaseName;
+ rD.CreateValidTabName( aTabName );
+ rD.RenameTab( nCnt, aTabName );
+ }
+ }
+ }
+
+ return eRet;
+}
+
+ErrCode ImportLotus::Read()
+{
+ ErrCode eRet = parse();
+ rD.CalcAfterLoad();
+ return eRet;
+}
+
+ErrCode ImportLotus::Read(SvStream& rIn)
+{
+ pIn = &rIn;
+
+ bool bRead = true;
+ sal_uInt16 nOp;
+ sal_uInt16 nRecLen;
+ sal_uInt32 nNextRec = 0;
+ ErrCode eRet = ERRCODE_NONE;
+
+ nTab = 0;
+ nExtTab = -1;
+
+ pIn->Seek( nNextRec );
+
+ // start progressbar
+ ScfStreamProgressBar aPrgrsBar( *pIn, rD.GetDocumentShell() );
+ LotusContext &rContext = aConv.getContext();
+ while( bRead )
+ {
+ pIn->ReadUInt16( nOp ).ReadUInt16( nRecLen );
+
+ if (!pIn->good() || nNextRec > SAL_MAX_UINT32 - nRecLen - 4)
+ bRead = false;
+ else
+ {
+ nNextRec += nRecLen + 4;
+
+ switch( nOp )
+ {
+ case 0x0000: // BOF
+ if( nRecLen != 26 || !BofFm3() )
+ {
+ bRead = false;
+ eRet = SCERR_IMPORT_FORMAT;
+ }
+ break;
+
+ case 0x0001: // EOF
+ bRead = false;
+ SAL_WARN_IF( nTab != 0, "sc.filter",
+ "-ImportLotus::Read( SvStream& ): EOF twice!" );
+ nTab++;
+ break;
+
+ case 174: // FONT_FACE
+ Font_Face();
+ break;
+
+ case 176: // FONT_TYPE
+ Font_Type();
+ break;
+
+ case 177: // FONT_YSIZE
+ Font_Ysize();
+ break;
+
+ case 195:
+ if( nExtTab >= 0 )
+ rContext.maAttrTable.Apply(rContext, static_cast<SCTAB>(nExtTab));
+ nExtTab++;
+ break;
+ case 197:
+ Row_( nRecLen );
+ break;
+ }
+
+ SAL_WARN_IF( nNextRec < pIn->Tell(), "sc.filter",
+ "*ImportLotus::Read(): Read too much..." );
+ pIn->Seek( nNextRec );
+ aPrgrsBar.Progress();
+ }
+ }
+
+ rContext.maAttrTable.Apply(rContext, static_cast<SCTAB>(nExtTab));
+
+ return eRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportWKS(SvStream& rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+
+ LotusContext aContext(aDocument, RTL_TEXTENCODING_ASCII_US);
+ ImportLotus aLotusImport(aContext, rStream, RTL_TEXTENCODING_ASCII_US);
+
+ ErrCode eRet = aLotusImport.parse();
+ if (eRet == ErrCode(0xFFFFFFFF))
+ {
+ rStream.Seek(0);
+ eRet = ScImportLotus123old(aContext, rStream, RTL_TEXTENCODING_ASCII_US);
+ }
+
+ return eRet == ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/lotus.cxx b/sc/source/filter/lotus/lotus.cxx
new file mode 100644
index 000000000..c89717cb6
--- /dev/null
+++ b/sc/source/filter/lotus/lotus.cxx
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "lotfilter.hxx"
+#include <lotimpop.hxx>
+
+#include <editeng/justifyitem.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <scerrors.hxx>
+#include <filtopt.hxx>
+#include <ftools.hxx>
+#include <tool.h>
+
+ErrCode ScFormatFilterPluginImpl::ScImportLotus123( SfxMedium& rMedium, ScDocument& rDocument, rtl_TextEncoding eSrc )
+{
+ SvStream* pStream = rMedium.GetInStream();
+ if (!pStream)
+ return SCERR_IMPORT_OPEN;
+
+ pStream->Seek( 0 );
+
+ pStream->SetBufferSize( 32768 );
+
+ LotusContext aContext(rDocument, eSrc);
+
+ ImportLotus aLotusImport(aContext, *pStream, eSrc);
+
+ ErrCode eRet;
+ if (ScFilterOptions().GetWK3Flag())
+ eRet = aLotusImport.Read();
+ else
+ eRet = ErrCode(0xFFFFFFFF); // force WK1 /WKS
+
+ // WARNING: QUICK-HACK for WK1 / WKS <-> WK3 / WK4
+ if( eRet == ErrCode(0xFFFFFFFF) )
+ {
+ pStream->Seek( 0 );
+ pStream->SetBufferSize( 32768 );
+ assert(&rDocument == &aContext.rDoc);
+ eRet = ScImportLotus123old(aContext, *pStream, eSrc);
+ pStream->SetBufferSize( 0 );
+ return eRet;
+ }
+
+ if( eRet != ERRCODE_NONE )
+ return eRet;
+
+ if (aContext.eFirstType == Lotus123Typ::WK3)
+ {
+ // try to load *.FM3 file
+ INetURLObject aURL( rMedium.GetURLObject() );
+ aURL.setExtension( u"FM3" );
+ SfxMedium aMedium( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::STD_READ );
+ pStream = aMedium.GetInStream();
+ if ( pStream )
+ {
+ if( aLotusImport.Read( *pStream ) != ERRCODE_NONE )
+ eRet = SCWARN_IMPORT_WRONG_FM3;
+ }
+ else
+ eRet = SCWARN_IMPORT_OPEN_FM3;
+ }
+
+ return eRet;
+}
+
+LotusContext::LotusContext(ScDocument& rDocP, rtl_TextEncoding eQ)
+ : eTyp(eWK_UNKNOWN)
+ , bEOF(false)
+ , eCharset(eQ)
+ , rDoc(rDocP)
+ , eFirstType( Lotus123Typ::X)
+ , eActType( Lotus123Typ::X)
+ , pRngNmBffWK3( new RangeNameBufferWK3(rDocP) )
+ , maAttrTable( *this )
+{
+}
+
+LotusContext::~LotusContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/memory.cxx b/sc/source/filter/lotus/memory.cxx
new file mode 100644
index 000000000..97ba1654c
--- /dev/null
+++ b/sc/source/filter/lotus/memory.cxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// Remark: Variables are not declared in header file! Modules have to declare
+// their variables via extern!
+
+#include <scitems.hxx>
+#include <scmem.h>
+#include <editeng/justifyitem.hxx>
+#include "lotfilter.hxx"
+#include <tool.h>
+#include <editeng/svxenum.hxx>
+
+bool MemNew(LotusContext& rContext)
+{
+ rContext.xValueFormCache.reset(new FormCache(&rContext.rDoc));
+
+ // for tool.cxx::PutFormString()
+ rContext.xAttrRight.reset(new SvxHorJustifyItem(SvxCellHorJustify::Right, ATTR_HOR_JUSTIFY));
+ rContext.xAttrLeft.reset(new SvxHorJustifyItem(SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY));
+ rContext.xAttrCenter.reset(new SvxHorJustifyItem(SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY));
+ rContext.xAttrRepeat.reset(new SvxHorJustifyItem(SvxCellHorJustify::Repeat, ATTR_HOR_JUSTIFY));
+ rContext.xAttrStandard.reset(
+ new SvxHorJustifyItem(SvxCellHorJustify::Standard, ATTR_HOR_JUSTIFY));
+
+ return true;
+}
+
+void MemDelete(LotusContext& rContext)
+{
+ rContext.xValueFormCache.reset();
+ rContext.xAttrRight.reset();
+ rContext.xAttrLeft.reset();
+ rContext.xAttrCenter.reset();
+ rContext.xAttrRepeat.reset();
+ rContext.xAttrStandard.reset();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/op.cxx b/sc/source/filter/lotus/op.cxx
new file mode 100644
index 000000000..58007a518
--- /dev/null
+++ b/sc/source/filter/lotus/op.cxx
@@ -0,0 +1,684 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/math.hxx>
+#include <rtl/character.hxx>
+
+#include <string.h>
+
+#include <scitems.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <postit.hxx>
+
+#include <op.h>
+#include <optab.h>
+#include <tool.h>
+#include "lotfilter.hxx"
+#include <lotform.hxx>
+#include <lotrange.hxx>
+#include <ftools.hxx>
+
+#include <vector>
+#include <map>
+#include <memory>
+
+static sal_uInt16 nDefWidth = sal_uInt16( TWIPS_PER_CHAR * 10 );
+
+void NI(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( n );
+}
+
+void OP_BOF(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 /*n*/)
+{
+ r.SeekRel( 2 ); // skip version number
+}
+
+void OP_EOF(LotusContext& rContext, SvStream& /*r*/, sal_uInt16 /*n*/)
+{
+ rContext.bEOF = true;
+}
+
+void OP_Integer(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ sal_Int16 nValue(0);
+ r.ReadUChar(nFormat).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow).ReadInt16(nValue);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ ScAddress aAddr(nCol, nRow, 0);
+ if (rContext.rDoc.ValidAddress(aAddr))
+ {
+ rContext.rDoc.EnsureTable(0);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetValue(aAddr, static_cast<double>(nValue));
+
+ // 0 digits in fractional part!
+ SetFormat(rContext, nCol, nRow, 0, nFormat, 0);
+ }
+}
+
+void OP_Number(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ double fValue(0.0);
+ r.ReadUChar( nFormat ).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow).ReadDouble(fValue);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ ScAddress aAddr(nCol, nRow, 0);
+ if (rContext.rDoc.ValidAddress(aAddr))
+ {
+ fValue = ::rtl::math::round( fValue, 15 );
+ rContext.rDoc.EnsureTable(0);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetValue(aAddr, fValue);
+
+ SetFormat(rContext, nCol, nRow, 0, nFormat, nFractionalFloat);
+ }
+}
+
+void OP_Label(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ r.ReadUChar(nFormat).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ n -= std::min<sal_uInt16>(n, 5);
+
+ std::vector<char> aText(n + 1);
+ n = r.ReadBytes(aText.data(), n);
+ aText[n] = 0;
+
+ if (rContext.rDoc.ValidColRow(nCol, nRow))
+ {
+ nFormat &= 0x80; // don't change Bit 7
+ nFormat |= 0x75; // protected does not matter, special-text is set
+
+ PutFormString(rContext, nCol, nRow, 0, aText.data());
+
+ SetFormat(rContext, nCol, nRow, 0, nFormat, nFractionalStd);
+ }
+}
+
+void OP_Formula(LotusContext &rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ r.ReadUChar(nFormat).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow);
+ r.SeekRel(8); // skip result
+ sal_uInt16 nFormulaSize(0);
+ r.ReadUInt16(nFormulaSize);
+
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ std::unique_ptr<ScTokenArray> pResult;
+ sal_Int32 nBytesLeft = nFormulaSize;
+ ScAddress aAddress(nCol, nRow, 0);
+
+ svl::SharedStringPool& rSPool = rContext.rDoc.GetSharedStringPool();
+ LotusToSc aConv(rContext, r, rSPool, rContext.eCharset, false);
+ aConv.Reset( aAddress );
+ aConv.Convert( pResult, nBytesLeft );
+ if (!aConv.good())
+ return;
+
+ if (rContext.rDoc.ValidColRow(nCol, nRow))
+ {
+ ScFormulaCell* pCell = new ScFormulaCell(rContext.rDoc, aAddress, std::move(pResult));
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ rContext.rDoc.EnsureTable(0);
+ // coverity[tainted_data : FALSE] - ValidColRow has sanitized aAddr
+ rContext.rDoc.SetFormulaCell(ScAddress(nCol, nRow, 0), pCell);
+
+ // nFormat = Default -> number of digits in fractional part like Float
+ SetFormat(rContext, nCol, nRow, 0, nFormat, nFractionalFloat);
+ }
+}
+
+void OP_ColumnWidth(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt16 nTmpCol(0);
+ sal_uInt8 nWidthSpaces(0);
+ r.ReadUInt16(nTmpCol).ReadUChar(nWidthSpaces);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+
+ if (!rContext.rDoc.ValidCol(nCol))
+ return;
+
+ nCol = rContext.rDoc.SanitizeCol(nCol);
+
+ sal_uInt16 nBreite;
+ if( nWidthSpaces )
+ // assuming 10cpi character set
+ nBreite = static_cast<sal_uInt16>( TWIPS_PER_CHAR * nWidthSpaces );
+ else
+ {
+ rContext.rDoc.SetColHidden(nCol, nCol, 0, true);
+ nBreite = nDefWidth;
+ }
+
+ rContext.rDoc.SetColWidth(nCol, 0, nBreite);
+}
+
+void OP_NamedRange(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ // POST: don't save for invalid coordinates
+ sal_uInt16 nColSt, nRowSt, nColEnd, nRowEnd;
+
+ char cBuffer[ 16+1 ];
+ r.ReadBytes(cBuffer, 16);
+ cBuffer[ 16 ] = 0;
+
+ r.ReadUInt16( nColSt ).ReadUInt16( nRowSt ).ReadUInt16( nColEnd ).ReadUInt16( nRowEnd );
+
+ if (!r.good())
+ return;
+
+ if (!rContext.rDoc.ValidColRow(static_cast<SCCOL>(nColSt), nRowSt) || !rContext.rDoc.ValidColRow(static_cast<SCCOL>(nColEnd), nRowEnd))
+ return;
+
+ std::unique_ptr<LotusRange> pRange;
+
+ if( nColSt == nColEnd && nRowSt == nRowEnd )
+ pRange.reset(new LotusRange( static_cast<SCCOL> (nColSt), static_cast<SCROW> (nRowSt) ));
+ else
+ pRange.reset(new LotusRange( static_cast<SCCOL> (nColSt), static_cast<SCROW> (nRowSt),
+ static_cast<SCCOL> (nColEnd), static_cast<SCROW> (nRowEnd) ));
+
+ char cBuf[sizeof(cBuffer)+1];
+ if( rtl::isAsciiDigit( static_cast<unsigned char>(*cBuffer) ) )
+ { // first char in name is a number -> prepend 'A'
+ cBuf[0] = 'A';
+ strcpy( cBuf + 1, cBuffer ); // #100211# - checked
+ }
+ else
+ strcpy( cBuf, cBuffer ); // #100211# - checked
+
+ OUString aTmp( cBuf, strlen(cBuf), rContext.eCharset );
+
+ aTmp = ScfTools::ConvertToScDefinedName( aTmp );
+
+ rContext.maRangeNames.Append( &rContext.rDoc, std::move(pRange) );
+}
+
+void OP_SymphNamedRange(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ // POST:don't save for invalid coordinates
+ sal_uInt16 nColSt, nRowSt, nColEnd, nRowEnd;
+ sal_uInt8 nType;
+
+ char cBuffer[ 16+1 ];
+ r.ReadBytes(cBuffer, 16);
+ cBuffer[ 16 ] = 0;
+
+ r.ReadUInt16( nColSt ).ReadUInt16( nRowSt ).ReadUInt16( nColEnd ).ReadUInt16( nRowEnd ).ReadUChar( nType );
+
+ if (!r.good())
+ return;
+
+ if (!rContext.rDoc.ValidColRow(static_cast<SCCOL>(nColSt), nRowSt) || !rContext.rDoc.ValidColRow(static_cast<SCCOL>(nColEnd), nRowEnd))
+ return;
+
+ std::unique_ptr<LotusRange> pRange;
+
+ if( nType )
+ pRange.reset(new LotusRange( static_cast<SCCOL> (nColSt), static_cast<SCROW> (nRowSt) ));
+ else
+ pRange.reset(new LotusRange( static_cast<SCCOL> (nColSt), static_cast<SCROW> (nRowSt),
+ static_cast<SCCOL> (nColEnd), static_cast<SCROW> (nRowEnd) ));
+
+ char cBuf[sizeof(cBuffer)+1];
+ if( rtl::isAsciiDigit( static_cast<unsigned char>(*cBuffer) ) )
+ { // first char in name is a number -> prepend 'A'
+ cBuf[0] = 'A';
+ strcpy( cBuf + 1, cBuffer ); // #100211# - checked
+ }
+ else
+ strcpy( cBuf, cBuffer ); // #100211# - checked
+
+ OUString aTmp( cBuf, strlen(cBuf), rContext.eCharset );
+ aTmp = ScfTools::ConvertToScDefinedName( aTmp );
+
+ rContext.maRangeNames.Append( &rContext.rDoc, std::move(pRange) );
+}
+
+void OP_Footer(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( n );
+}
+
+void OP_Header(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( n );
+}
+
+void OP_Margins(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( n );
+}
+
+void OP_HiddenCols(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ SCCOL nCount = 0;
+ for (sal_uInt16 nByte = 0; nByte < 32; ++nByte) // 32 Bytes with ...
+ {
+ sal_uInt8 nCurrent(0);
+ r.ReadUChar(nCurrent);
+ for (sal_uInt16 nBit = 0; nBit < 8; ++nBit) // ...each 8 Bits = 256 Bits
+ {
+ if( nCurrent & 0x01 ) // is lowest Bit set?
+ {
+ // -> Hidden Col
+ rContext.rDoc.SetColHidden(nCount, nCount, 0, true);
+ }
+
+ nCount++;
+ nCurrent = nCurrent / 2; // the next please...
+ }
+ }
+}
+
+void OP_Window1(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ r.SeekRel( 4 ); // skip Cursor Pos
+
+ sal_uInt8 nDefaultFormat; // -> op.cpp, standard cell format
+ r.ReadUChar(nDefaultFormat);
+
+ r.SeekRel( 1 ); // skip 'unused'
+
+ r.ReadUInt16( nDefWidth );
+ if (!r.good())
+ return;
+
+ r.SeekRel( n - 8 ); // skip the rest
+
+ nDefWidth = static_cast<sal_uInt16>( TWIPS_PER_CHAR * nDefWidth );
+
+ const bool bFuzzing = utl::ConfigManager::IsFuzzing();
+
+ // instead of default, set all Cols in SC by hand
+ for (SCCOL nCol = 0 ; nCol <= rContext.rDoc.MaxCol() ; nCol++)
+ {
+ rContext.rDoc.SetColWidth( nCol, 0, nDefWidth );
+ if (bFuzzing)
+ break;
+ }
+}
+
+void OP_Blank(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt8 nFormat(0);
+ sal_uInt16 nTmpCol(0), nTmpRow(0);
+ r.ReadUChar( nFormat ).ReadUInt16(nTmpCol).ReadUInt16(nTmpRow);
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ SetFormat(rContext, nCol, nRow, 0, nFormat, nFractionalFloat);
+}
+
+void OP_BOF123(LotusContext& /*rContext*/, SvStream& r, sal_uInt16 /*n*/)
+{
+ r.SeekRel( 26 );
+}
+
+void OP_EOF123(LotusContext& rContext, SvStream& /*r*/, sal_uInt16 /*n*/)
+{
+ rContext.bEOF = true;
+}
+
+void OP_Label123(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt8 nTmpTab(0), nTmpCol(0);
+ sal_uInt16 nTmpRow(0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ n -= std::min<sal_uInt16>(n, 4);
+
+ std::unique_ptr<char[]> pText(new char[n + 1]);
+ r.ReadBytes(pText.get(), n);
+ pText[ n ] = 0;
+
+ PutFormString(rContext, nCol, nRow, nTab, pText.get());
+}
+
+void OP_Number123(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt16 nTmpRow(0);
+ sal_uInt8 nTmpCol(0), nTmpTab(0);
+ sal_uInt32 nValue(0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol).ReadUInt32(nValue);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ ScAddress aAddr(nCol, nRow, nTab);
+ if (rContext.rDoc.ValidAddress(aAddr) && nTab <= rContext.rDoc.GetMaxTableNumber())
+ {
+ double fValue = Snum32ToDouble( nValue );
+ rContext.rDoc.EnsureTable(nTab);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetValue(aAddr, fValue);
+ }
+}
+
+void OP_Formula123(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt16 nTmpRow(0);
+ sal_uInt8 nTmpCol(0), nTmpTab(0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+ r.SeekRel( 8 ); // skip Result
+
+ std::unique_ptr<ScTokenArray> pResult;
+ sal_Int32 nBytesLeft = (n > 12) ? n - 12 : 0;
+ ScAddress aAddress(nCol, nRow, nTab);
+
+ svl::SharedStringPool& rSPool = rContext.rDoc.GetSharedStringPool();
+ LotusToSc aConv(rContext, r, rSPool, rContext.eCharset, true);
+ aConv.Reset( aAddress );
+ aConv.Convert( pResult, nBytesLeft );
+ if (!aConv.good())
+ return;
+
+ if (rContext.rDoc.ValidAddress(aAddress) && nTab <= rContext.rDoc.GetMaxTableNumber())
+ {
+ ScFormulaCell* pCell = new ScFormulaCell(rContext.rDoc, aAddress, std::move(pResult));
+ pCell->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ rContext.rDoc.EnsureTable(nTab);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetFormulaCell(aAddress, pCell);
+ }
+}
+
+void OP_IEEENumber123(LotusContext& rContext, SvStream& r, sal_uInt16 /*n*/)
+{
+ sal_uInt16 nTmpRow(0);
+ sal_uInt8 nTmpCol(0), nTmpTab(0);
+ double dValue(0.0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol).ReadDouble(dValue);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ ScAddress aAddr(nCol, nRow, nTab);
+ if (rContext.rDoc.ValidAddress(aAddr) && nTab <= rContext.rDoc.GetMaxTableNumber())
+ {
+ rContext.rDoc.EnsureTable(nTab);
+ // coverity[tainted_data : FALSE] - ValidAddress has sanitized aAddr
+ rContext.rDoc.SetValue(aAddr, dValue);
+ }
+}
+
+void OP_Note123(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt16 nTmpRow(0);
+ sal_uInt8 nTmpTab(0), nTmpCol(0);
+ r.ReadUInt16(nTmpRow).ReadUChar(nTmpTab).ReadUChar(nTmpCol);
+ SCTAB nTab(static_cast<SCTAB>(nTmpTab));
+ SCCOL nCol(static_cast<SCCOL>(nTmpCol));
+ SCROW nRow(static_cast<SCROW>(nTmpRow));
+
+ n -= std::min<sal_uInt16>(n, 4);
+
+ std::unique_ptr<char[]> pText(new char[n + 1]);
+ r.ReadBytes(pText.get(), n);
+ pText[ n ] = 0;
+
+ OUString aNoteText(pText.get(), strlen(pText.get()), rContext.eCharset);
+ pText.reset();
+
+ ScAddress aPos(nCol, nRow, nTab);
+ ScNoteUtil::CreateNoteFromString( rContext.rDoc, aPos, aNoteText, false, false );
+}
+
+void OP_HorAlign123(LotusContext& /*rContext*/, sal_uInt8 nAlignPattern, SfxItemSet& rPatternItemSet)
+{
+// pre: Pattern is stored in the last 3 bites of the 21st byte
+// post: Appropriate Horizontal Alignment is set in rPattern according to the bit pattern.
+//
+// LEFT:001, RIGHT:010, CENTER:011, JUSTIFY:110,
+// LEFT-Text/RIGHT-NUMBER:100, DEFAULT:000
+
+ nAlignPattern = ( nAlignPattern & 0x07);
+
+ switch (nAlignPattern)
+ {
+ case 1:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
+ break;
+ case 2:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Right, ATTR_HOR_JUSTIFY ) );
+ break;
+ case 3:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY) );
+ break;
+ case 4:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Standard, ATTR_HOR_JUSTIFY ) );
+ break;
+ case 6:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Block, ATTR_HOR_JUSTIFY ) );
+ break;
+ default:
+ rPatternItemSet.Put( SvxHorJustifyItem( SvxCellHorJustify::Standard, ATTR_HOR_JUSTIFY ) );
+ break;
+ }
+}
+
+void OP_VerAlign123(LotusContext& /*rContext*/, sal_uInt8 nAlignPattern, SfxItemSet& rPatternItemSet)
+{
+// pre: Pattern is stored in the last 3 bites of the 22nd byte
+// post: Appropriate Vertical Alignment is set in rPattern according to the bit pattern.
+//
+// TOP:001, MIDDLE:010, DOWN:100, DEFAULT:000
+
+ nAlignPattern = ( nAlignPattern & 0x07);
+
+ switch (nAlignPattern)
+ {
+ case 0:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Standard, ATTR_VER_JUSTIFY) );
+ break;
+ case 1:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Top, ATTR_VER_JUSTIFY) );
+ break;
+ case 2:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Center, ATTR_VER_JUSTIFY) );
+ break;
+ case 4:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Bottom, ATTR_VER_JUSTIFY) );
+ break;
+ default:
+ rPatternItemSet.Put( SvxVerJustifyItem(SvxCellVerJustify::Standard, ATTR_VER_JUSTIFY) );
+ break;
+ }
+}
+
+void OP_CreatePattern123(LotusContext& rContext, SvStream& r, sal_uInt16 n)
+{
+ sal_uInt16 nCode;
+
+ ScPatternAttr aPattern(rContext.rDoc.GetPool());
+ SfxItemSet& rItemSet = aPattern.GetItemSet();
+
+ r.ReadUInt16( nCode );
+ n -= std::min<sal_uInt16>(n, 2);
+
+ if ( nCode == 0x0fd2 )
+ {
+ sal_uInt16 nPatternId;
+ r.ReadUInt16( nPatternId );
+
+ sal_uInt8 Hor_Align, Ver_Align, temp;
+ bool bIsBold,bIsUnderLine,bIsItalics;
+
+ r.SeekRel(12);
+
+ // Read 17th Byte
+ r.ReadUChar( temp );
+
+ bIsBold = (temp & 0x01);
+ bIsItalics = (temp & 0x02);
+ bIsUnderLine = (temp & 0x04);
+
+ if ( bIsBold )
+ rItemSet.Put( SvxWeightItem(WEIGHT_BOLD,ATTR_FONT_WEIGHT) );
+ if ( bIsItalics )
+ rItemSet.Put( SvxPostureItem(ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
+ if ( bIsUnderLine )
+ rItemSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, ATTR_FONT_UNDERLINE ) );
+
+ r.SeekRel(3);
+
+ // Read 21st Byte
+ r.ReadUChar( Hor_Align );
+ OP_HorAlign123(rContext, Hor_Align, rItemSet );
+
+ r.ReadUChar( Ver_Align );
+ OP_VerAlign123(rContext, Ver_Align, rItemSet );
+
+ rContext.aLotusPatternPool.emplace( nPatternId, aPattern );
+ n -= std::min<sal_uInt16>(n, 20);
+ }
+ r.SeekRel(n);
+}
+
+void OP_SheetName123(LotusContext& rContext, SvStream& rStream, sal_uInt16 nLength)
+{
+ if (nLength <= 4)
+ {
+ rStream.SeekRel(nLength);
+ return;
+ }
+
+ // B0 36 [sheet number (2 bytes?)] [sheet name (null terminated char array)]
+ rStream.SeekRel(2); // ignore the first 2 bytes (B0 36).
+ sal_uInt16 nSheetNum(0);
+ rStream.ReadUInt16(nSheetNum);
+
+ const size_t nStrLen = nLength - 4;
+ std::vector<char> sSheetName(nStrLen + 1);
+ sSheetName[rStream.ReadBytes(sSheetName.data(), nStrLen)] = 0;
+
+ if (!ValidTab(nSheetNum))
+ return;
+ // coverity[tainted_data : FALSE] - ValidTab has sanitized nSheetNum
+ rContext.rDoc.MakeTable(nSheetNum);
+ if (!sSheetName.empty())
+ {
+ OUString aName(sSheetName.data(), strlen(sSheetName.data()), rContext.eCharset);
+ rContext.rDoc.RenameTab(nSheetNum, aName);
+ }
+}
+
+void OP_ApplyPatternArea123(LotusContext& rContext, SvStream& rStream)
+{
+ sal_uInt16 nOpcode, nLength;
+ sal_uInt16 nCol = 0, nColCount = 0, nRow = 0, nRowCount = 0, nTab = 0, nData, nTabCount = 0, nLevel = 0;
+
+ do
+ {
+ rStream.ReadUInt16( nOpcode ).ReadUInt16( nLength );
+ switch ( nOpcode )
+ {
+ case ROW_FORMAT_MARKER:
+ nLevel++;
+ break;
+ case COL_FORMAT_MARKER:
+ nLevel--;
+ if( nLevel == 1 )
+ {
+ nTab = nTab + nTabCount;
+ nCol = 0; nColCount = 0;
+ nRow = 0; nRowCount = 0;
+ }
+ break;
+ case LOTUS_FORMAT_INDEX:
+ if( nLength >= 2 )
+ {
+ rStream.ReadUInt16( nData );
+ rStream.SeekRel( nLength - 2 );
+ if( nLevel == 1 )
+ nTabCount = SanitizeTab(nData);
+ else if( nLevel == 2 )
+ {
+ nCol = nCol + nColCount;
+ nColCount = nData;
+ if ( nCol > 0xff ) // 256 is the max col size supported by 123
+ nCol = 0;
+ }
+ else if( nLevel == 3 )
+ {
+ nRow = nRow + nRowCount;
+ nRowCount = nData;
+ if ( nRow > 0x1fff ) // 8192 is the max row size supported by 123
+ nRow = 0;
+ }
+ }
+ else
+ rStream.SeekRel( nLength );
+ break;
+ case LOTUS_FORMAT_INFO:
+ if( nLength >= 2 )
+ {
+ rStream.ReadUInt16( nData );
+ rStream.SeekRel( nLength - 2 );
+ std::map<sal_uInt16, ScPatternAttr>::iterator loc = rContext.aLotusPatternPool.find( nData );
+ // #126338# apparently, files with invalid index occur in the wild -> don't crash then
+ if ( loc != rContext.aLotusPatternPool.end() )
+ for( int i = 0; i < nTabCount; i++)
+ {
+ rContext.rDoc.ApplyPatternAreaTab( nCol, nRow, nCol + nColCount - 1, nRow + nRowCount - 1, static_cast< SCTAB >( nTab + i ), loc->second );
+ }
+ }
+ else
+ rStream.SeekRel( nLength );
+ break;
+ default:
+ rStream.SeekRel( nLength );
+ break;
+ }
+ }
+ while( nLevel && rStream.good() );
+
+ rContext.aLotusPatternPool.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/optab.cxx b/sc/source/filter/lotus/optab.cxx
new file mode 100644
index 000000000..b4cbaff91
--- /dev/null
+++ b/sc/source/filter/lotus/optab.cxx
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <op.h>
+#include <optab.h>
+#include "lotfilter.hxx"
+
+// edit function char *X( char * )
+OPCODE_FKT LotusContext::pOpFkt[ FKT_LIMIT ] =
+{ // Code
+ OP_BOF, // 0
+ OP_EOF, // 1
+ NI, // 2
+ NI, // 3
+ NI, // 4
+ NI, // 5
+ NI, // 6
+ OP_Window1, // 7
+ OP_ColumnWidth, // 8
+ NI, // 9
+ NI, // 10
+ OP_NamedRange, // 11
+ OP_Blank, // 12
+ OP_Integer, // 13
+ OP_Number, // 14
+ OP_Label, // 15
+ OP_Formula, // 16
+ NI, // 17
+ NI, // 18
+ NI, // 19
+ NI, // 20
+ NI, // 21
+ NI, // 22
+ NI, // 23
+ NI, // 24
+ NI, // 25
+ NI, // 26
+ NI, // 27
+ NI, // 28
+ NI, // 29
+ NI, // 30
+ NI, // 31
+ NI, // 32
+ NI, // 33
+ NI, // 34
+ NI, // 35
+ NI, // 36
+ OP_Footer, // 37
+ OP_Header, // 38
+ NI, // 39
+ OP_Margins, // 40
+ NI, // 41
+ NI, // 42
+ NI, // 43
+ NI, // 44
+ NI, // 45
+ NI, // 46
+ NI, // 47
+ NI, // 48
+ NI, // 49
+ NI, // 50
+ NI, // 51
+ NI, // 52
+ NI, // 53
+ NI, // 54
+ NI, // 55
+ NI, // 56
+ NI, // 57
+ NI, // 58
+ NI, // 59
+ NI, // 60
+ NI, // 61
+ NI, // 62
+ NI, // 63
+ NI, // 64
+ NI, // 65
+ NI, // 66
+ NI, // 67
+ NI, // 68
+ NI, // 69
+ NI, // 70
+ OP_SymphNamedRange, // 71
+ NI, // 72
+ NI, // 73
+ NI, // 74
+ NI, // 75
+ NI, // 76
+ NI, // 77
+ NI, // 78
+ NI, // 79
+ NI, // 80
+ NI, // 81
+ NI, // 82
+ NI, // 83
+ NI, // 84
+ NI, // 85
+ NI, // 86
+ NI, // 87
+ NI, // 88
+ NI, // 89
+ NI, // 90
+ NI, // 91
+ NI, // 92
+ NI, // 93
+ NI, // 94
+ NI, // 95
+ NI, // 96
+ NI, // 97
+ NI, // 98
+ NI, // 99
+ OP_HiddenCols, // 100
+};
+
+OPCODE_FKT LotusContext::pOpFkt123[ FKT_LIMIT123 ] =
+{ // Code
+ OP_BOF123, // 0
+ OP_EOF123, // 1
+ NI, // 2
+ NI, // 3
+ NI, // 4
+ NI, // 5
+ NI, // 6
+ NI, // 7
+ NI, // 8
+ NI, // 9
+ NI, // 10
+ NI, // 11
+ NI, // 12
+ NI, // 13
+ NI, // 14
+ NI, // 15
+ NI, // 16
+ NI, // 17
+ NI, // 18
+ NI, // 19
+ NI, // 20
+ NI, // 21
+ OP_Label123, // 22
+ NI, // 23
+ NI, // 24
+ NI, // 25
+ NI, // 26
+ OP_CreatePattern123, // 27
+ NI, // 28
+ NI, // 29
+ NI, // 30
+ NI, // 31
+ NI, // 32
+ NI, // 33
+ NI, // 34
+ OP_SheetName123, // 35
+ NI, // 36
+ OP_Number123, // 37
+ OP_Note123, // 38
+ OP_IEEENumber123, // 39
+ OP_Formula123, // 40
+ NI, // 41
+ NI, // 42
+ NI, // 43
+ NI, // 44
+ NI, // 45
+ NI, // 46
+ NI, // 47
+ NI, // 48
+ NI, // 49
+ NI, // 50
+ NI, // 51
+ NI, // 52
+ NI, // 53
+ NI, // 54
+ NI, // 55
+ NI, // 56
+ NI, // 57
+ NI, // 58
+ NI, // 59
+ NI, // 60
+ NI, // 61
+ NI, // 62
+ NI, // 63
+ NI, // 64
+ NI, // 65
+ NI, // 66
+ NI, // 67
+ NI, // 68
+ NI, // 69
+ NI, // 70
+ NI, // 71
+ NI, // 72
+ NI, // 73
+ NI, // 74
+ NI, // 75
+ NI, // 76
+ NI, // 77
+ NI, // 78
+ NI, // 79
+ NI, // 80
+ NI, // 81
+ NI, // 82
+ NI, // 83
+ NI, // 84
+ NI, // 85
+ NI, // 86
+ NI, // 87
+ NI, // 88
+ NI, // 89
+ NI, // 90
+ NI, // 91
+ NI, // 92
+ NI, // 93
+ NI, // 94
+ NI, // 95
+ NI, // 96
+ NI, // 97
+ NI, // 98
+ NI, // 99
+ NI // 100
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/lotus/tool.cxx b/sc/source/filter/lotus/tool.cxx
new file mode 100644
index 000000000..c3bf46b4f
--- /dev/null
+++ b/sc/source/filter/lotus/tool.cxx
@@ -0,0 +1,524 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sal/log.hxx>
+
+#include <attrib.hxx>
+#include <document.hxx>
+
+#include <tool.h>
+#include <lotrange.hxx>
+#include <namebuff.hxx>
+#include <stringutil.hxx>
+#include <tokenarray.hxx>
+#include "lotfilter.hxx"
+
+#include <math.h>
+
+void PutFormString(LotusContext& rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, char* pString)
+{
+ // evaluate Label-Format
+ SAL_WARN_IF( pString == nullptr, "sc.filter", "PutFormString(): pString == NULL" );
+ if (!pString)
+ return;
+
+ char cForm;
+ SvxHorJustifyItem* pJustify = nullptr;
+
+ cForm = *pString;
+
+ switch( cForm )
+ {
+ case '"': // align-right
+ pJustify = rContext.xAttrRight.get();
+ pString++;
+ break;
+ case '\'': // align-left
+ pJustify = rContext.xAttrLeft.get();
+ pString++;
+ break;
+ case '^': // centered
+ pJustify = rContext.xAttrCenter.get();
+ pString++;
+ break;
+ case '|': // printer command
+ pString = nullptr;
+ break;
+ case '\\': // repetition
+ pJustify = rContext.xAttrRepeat.get();
+ pString++;
+ break;
+ default: // undefined case!
+ pJustify = rContext.xAttrStandard.get();
+ }
+
+ if (!pString)
+ return;
+
+ nCol = rContext.rDoc.SanitizeCol(nCol);
+ nRow = rContext.rDoc.SanitizeRow(nRow);
+ nTab = SanitizeTab(nTab);
+
+ rContext.rDoc.ApplyAttr( nCol, nRow, nTab, *pJustify );
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ rContext.rDoc.SetString(ScAddress(nCol,nRow,nTab), OUString(pString, strlen(pString), rContext.eCharset), &aParam);
+}
+
+void SetFormat(LotusContext& rContext, SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt8 nFormat, sal_uInt8 nSt)
+{
+ nCol = rContext.rDoc.SanitizeCol(nCol);
+ nRow = rContext.rDoc.SanitizeRow(nRow);
+ nTab = SanitizeTab(nTab);
+
+ // PREC: nSt = default number of decimal places
+ rContext.rDoc.ApplyAttr(nCol, nRow, nTab, *(rContext.xValueFormCache->GetAttr(nFormat, nSt)));
+
+ ScProtectionAttr aAttr;
+
+ aAttr.SetProtection( nFormat & 0x80 );
+
+ rContext.rDoc.ApplyAttr( nCol, nRow, nTab, aAttr );
+}
+
+double SnumToDouble( sal_Int16 nVal )
+{
+ const double pFacts[ 8 ] = {
+ 5000.0,
+ 500.0,
+ 0.05,
+ 0.005,
+ 0.0005,
+ 0.00005,
+ 0.0625,
+ 0.015625 };
+
+ double fVal;
+
+ if( nVal & 0x0001 )
+ {
+ fVal = pFacts[ ( nVal >> 1 ) & 0x0007 ];
+ fVal *= static_cast<sal_Int16>( nVal >> 4 );
+ }
+ else
+ fVal = static_cast<sal_Int16>( nVal >> 1 );
+
+ return fVal;
+}
+
+double Snum32ToDouble( sal_uInt32 nValue )
+{
+ double fValue, temp;
+
+ fValue = nValue >> 6;
+ temp = nValue & 0x0f;
+ if (temp)
+ {
+ if (nValue & 0x00000010)
+ fValue /= pow(double(10), temp);
+ else
+ fValue *= pow(double(10), temp);
+ }
+
+ if (nValue & 0x00000020)
+ fValue = -fValue;
+ return fValue;
+}
+
+FormCache::FormCache( const ScDocument* pDoc1 )
+ : nIndex(0)
+{
+ pFormTable = pDoc1->GetFormatTable();
+ for(bool & rb : bValid)
+ rb = false;
+ eLanguage = ScGlobal::eLnge;
+}
+
+FormCache::~FormCache()
+{
+}
+
+SfxUInt32Item* FormCache::NewAttr( sal_uInt8 nFormat, sal_uInt8 nSt )
+{
+ // setup new Format
+ sal_uInt8 nL, nH; // Low-/High-Nibble
+ OUString aFormString;
+ SvNumFormatType eType = SvNumFormatType::ALL;
+ sal_uInt32 nIndex1;
+ sal_uInt32 nHandle;
+ NfIndexTableOffset eIndexTableOffset = NF_NUMERIC_START;
+ bool bDefault = false;
+
+ // split into Low and High byte
+ nL = nFormat & 0x0F;
+ nH = ( nFormat & 0xF0 ) / 16;
+
+ nH &= 0x07; // extract bits 4-6
+ switch( nH )
+ {
+ case 0x00: // fixed-point number
+ //fStandard;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x01: // scientific notation
+ //fExponent;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::SCIENTIFIC, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x02: // currency
+ //fMoney;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::CURRENCY, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x03: // percentage
+ //fPercent;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::PERCENT, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x04: // Decimal
+ //fStandard;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, true, false, nL);
+ break;
+ case 0x05: // unspecified
+ //fStandard;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x06: // unspecified
+ //fStandard;nL;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nL);
+ break;
+ case 0x07: // Special format
+ switch( nL )
+ {
+ case 0x00: // +/-
+ //fStandard;nSt;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, true, nSt);
+ break;
+ case 0x01: // general Format
+ //fStandard;nSt;
+ nIndex1 = pFormTable->GetStandardFormat(
+ SvNumFormatType::NUMBER, eLanguage );
+ aFormString = pFormTable->GenerateFormat(nIndex1,
+ eLanguage, false, false, nSt);
+ break;
+ case 0x02: // Date: Day, Month, Year
+ //fDate;dfDayMonthYearLong;
+ eType = SvNumFormatType::DATE;
+ eIndexTableOffset = NF_DATE_SYS_DDMMYYYY;
+ break;
+ case 0x03: // Date: Day, Month
+ //fDate;dfDayMonthLong;
+ eType = SvNumFormatType::DATE;
+ aFormString = pFormTable->GetKeyword( eLanguage, NF_KEY_DD) +
+ pFormTable->GetDateSep() + // matches last eLanguage
+ pFormTable->GetKeyword( eLanguage, NF_KEY_MMMM);
+ break;
+ case 0x04: // Date: Month, Year
+ //fDate;dfMonthYearLong;
+ eType = SvNumFormatType::DATE;
+ aFormString = pFormTable->GetKeyword( eLanguage, NF_KEY_MM) +
+ pFormTable->GetDateSep() + // matches last eLanguage
+ pFormTable->GetKeyword( eLanguage, NF_KEY_YYYY);
+ break;
+ case 0x05: // Text formats
+ //fString;nSt;
+ eType = SvNumFormatType::TEXT;
+ eIndexTableOffset = NF_TEXT;
+ break;
+ case 0x06: // hidden
+ //wFlag |= paHideAll;bSetFormat = sal_False;
+ eType = SvNumFormatType::NUMBER;
+ aFormString = "\"\"";
+ break;
+ case 0x07: // Time: hour, min, sec
+ //fTime;tfHourMinSec24;
+ eType = SvNumFormatType::TIME;
+ eIndexTableOffset = NF_TIME_HHMMSS;
+ break;
+ case 0x08: // Time: hour, min
+ //fTime;tfHourMin24;
+ eType = SvNumFormatType::TIME;
+ eIndexTableOffset = NF_TIME_HHMM;
+ break;
+ case 0x09: // Date, intern sal_Int32 1
+ //fDate;dfDayMonthYearLong;
+ eType = SvNumFormatType::DATE;
+ eIndexTableOffset = NF_DATE_SYS_DDMMYYYY;
+ break;
+ case 0x0A: // Date, intern sal_Int32 2
+ //fDate;dfDayMonthYearLong;
+ eType = SvNumFormatType::DATE;
+ eIndexTableOffset = NF_DATE_SYS_DDMMYYYY;
+ break;
+ case 0x0B: // Time, intern sal_Int32 1
+ //fTime;tfHourMinSec24;
+ eType = SvNumFormatType::TIME;
+ eIndexTableOffset = NF_TIME_HHMMSS;
+ break;
+ case 0x0C: // Time, intern sal_Int32 2
+ //fTime;tfHourMinSec24;
+ eType = SvNumFormatType::TIME;
+ eIndexTableOffset = NF_TIME_HHMMSS;
+ break;
+ case 0x0F: // Default
+ //fStandard;nSt;
+ bDefault = true;
+ break;
+ default:
+ //fStandard;nSt;
+ bDefault = true;
+ break;
+ }
+ break;
+ }
+
+ // push Format into table
+ if( bDefault )
+ nHandle = 0;
+ else if (eIndexTableOffset != NF_NUMERIC_START)
+ nHandle = pFormTable->GetFormatIndex( eIndexTableOffset, eLanguage);
+ else
+ {
+ sal_Int32 nDummy;
+ pFormTable->PutEntry( aFormString, nDummy, eType, nHandle, eLanguage );
+ }
+
+ return new SfxUInt32Item( ATTR_VALUE_FORMAT, nHandle );
+}
+
+void LotusRange::MakeHash()
+{
+ // 33222222222211111111110000000000
+ // 10987654321098765432109876543210
+ // ******** nColS
+ // ******** nColE
+ // **************** nRowS
+ // **************** nRowE
+ nHash = static_cast<sal_uInt32>(nColStart);
+ nHash += static_cast<sal_uInt32>(nColEnd) << 6;
+ nHash += static_cast<sal_uInt32>(nRowStart) << 12;
+ nHash += static_cast<sal_uInt32>(nRowEnd ) << 16;
+}
+
+LotusRange::LotusRange( SCCOL nCol, SCROW nRow )
+{
+ nColStart = nColEnd = nCol;
+ nRowStart = nRowEnd = nRow;
+ nId = ID_FAIL;
+ MakeHash();
+}
+
+LotusRange::LotusRange( SCCOL nCS, SCROW nRS, SCCOL nCE, SCROW nRE )
+{
+ nColStart = nCS;
+ nColEnd = nCE;
+ nRowStart = nRS;
+ nRowEnd = nRE;
+ nId = ID_FAIL;
+ MakeHash();
+}
+
+LotusRange::LotusRange( const LotusRange& rCpy )
+{
+ Copy( rCpy );
+}
+
+LotusRangeList::LotusRangeList()
+{
+ aComplRef.InitFlags();
+
+ ScSingleRefData* pSingRef;
+ nIdCnt = 1;
+
+ pSingRef = &aComplRef.Ref1;
+ pSingRef->SetRelTab(0);
+ pSingRef->SetColRel( false );
+ pSingRef->SetRowRel( false );
+ pSingRef->SetFlag3D( false );
+
+ pSingRef = &aComplRef.Ref2;
+ pSingRef->SetRelTab(0);
+ pSingRef->SetColRel( false );
+ pSingRef->SetRowRel( false );
+ pSingRef->SetFlag3D( false );
+}
+
+LotusRangeList::~LotusRangeList ()
+{
+}
+
+LR_ID LotusRangeList::GetIndex( const LotusRange &rRef )
+{
+ auto pIter = std::find_if(maRanges.begin(), maRanges.end(),
+ [&rRef](const std::unique_ptr<LotusRange>& pRange) { return rRef == *pRange; });
+ if (pIter != maRanges.end())
+ return (*pIter)->nId;
+
+ return ID_FAIL;
+}
+
+void LotusRangeList::Append( const ScDocument* pDoc, std::unique_ptr<LotusRange> pLR )
+{
+ assert( pLR );
+ auto pLRTmp = pLR.get();
+ maRanges.push_back(std::move(pLR));
+
+ ScTokenArray aTokArray(*pDoc);
+
+ ScSingleRefData* pSingRef = &aComplRef.Ref1;
+
+ pSingRef->SetAbsCol(pLRTmp->nColStart);
+ pSingRef->SetAbsRow(pLRTmp->nRowStart);
+
+ if( pLRTmp->IsSingle() )
+ aTokArray.AddSingleReference( *pSingRef );
+ else
+ {
+ pSingRef = &aComplRef.Ref2;
+ pSingRef->SetAbsCol(pLRTmp->nColEnd);
+ pSingRef->SetAbsRow(pLRTmp->nRowEnd);
+ aTokArray.AddDoubleReference( aComplRef );
+ }
+
+ pLRTmp->SetId( nIdCnt );
+
+ nIdCnt++;
+}
+
+RangeNameBufferWK3::RangeNameBufferWK3(const ScDocument& rDoc)
+ : pScTokenArray( new ScTokenArray(rDoc) )
+{
+ nIntCount = 1;
+}
+
+RangeNameBufferWK3::~RangeNameBufferWK3()
+{
+}
+
+void RangeNameBufferWK3::Add( const ScDocument& rDoc, const OUString& rOrgName, const ScComplexRefData& rCRD )
+{
+ Entry aInsert( rOrgName, rCRD );
+
+ pScTokenArray->Clear();
+
+ const ScSingleRefData& rRef1 = rCRD.Ref1;
+ const ScSingleRefData& rRef2 = rCRD.Ref2;
+ ScAddress aAbs1 = rRef1.toAbs(rDoc, ScAddress());
+ ScAddress aAbs2 = rRef2.toAbs(rDoc, ScAddress());
+ if (aAbs1 == aAbs2)
+ {
+ pScTokenArray->AddSingleReference( rCRD.Ref1 );
+ aInsert.bSingleRef = true;
+ }
+ else
+ {
+ pScTokenArray->AddDoubleReference( rCRD );
+ aInsert.bSingleRef = false;
+ }
+
+ aInsert.nRelInd = nIntCount;
+ nIntCount++;
+
+ maEntries.push_back( aInsert );
+}
+
+bool RangeNameBufferWK3::FindRel( const OUString& rRef, sal_uInt16& rIndex )
+{
+ StringHashEntry aRef( rRef );
+
+ std::vector<Entry>::const_iterator itr = std::find_if(maEntries.begin(), maEntries.end(),
+ [&aRef](const Entry& rEntry) { return aRef == rEntry.aStrHashEntry; });
+ if (itr != maEntries.end())
+ {
+ rIndex = itr->nRelInd;
+ return true;
+ }
+
+ return false;
+}
+
+bool RangeNameBufferWK3::FindAbs( std::u16string_view rRef, sal_uInt16& rIndex )
+{
+ if (rRef.empty())
+ return false;
+ StringHashEntry aRef(OUString(rRef.substr(1))); // search w/o '$'!
+
+ std::vector<Entry>::iterator itr = std::find_if(maEntries.begin(), maEntries.end(),
+ [&aRef](const Entry& rEntry) { return aRef == rEntry.aStrHashEntry; });
+ if (itr != maEntries.end())
+ {
+ // setup new range if needed
+ if( itr->nAbsInd )
+ rIndex = itr->nAbsInd;
+ else
+ {
+ ScSingleRefData* pRef = &itr->aScComplexRefDataRel.Ref1;
+ pScTokenArray->Clear();
+
+ pRef->SetColRel( false );
+ pRef->SetRowRel( false );
+ pRef->SetTabRel( true );
+
+ if( itr->bSingleRef )
+ pScTokenArray->AddSingleReference( *pRef );
+ else
+ {
+ pRef = &itr->aScComplexRefDataRel.Ref2;
+ pRef->SetColRel( false );
+ pRef->SetRowRel( false );
+ pRef->SetTabRel( true );
+ pScTokenArray->AddDoubleReference( itr->aScComplexRefDataRel );
+ }
+
+ rIndex = itr->nAbsInd = nIntCount;
+ nIntCount++;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/SparklineFragment.cxx b/sc/source/filter/oox/SparklineFragment.cxx
new file mode 100644
index 000000000..bb341d4a8
--- /dev/null
+++ b/sc/source/filter/oox/SparklineFragment.cxx
@@ -0,0 +1,273 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <SparklineFragment.hxx>
+#include <oox/core/contexthandler.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/helper/helper.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <Sparkline.hxx>
+#include <themebuffer.hxx>
+
+using ::oox::core::ContextHandlerRef;
+
+namespace oox::xls
+{
+namespace
+{
+// TODO: deduplicate with importOOXColor
+::Color getColor(const AttributeList& rAttribs, ThemeBuffer const& rThemeBuffer)
+{
+ if (rAttribs.hasAttribute(XML_rgb))
+ {
+ return ::Color(ColorAlpha, rAttribs.getIntegerHex(XML_rgb, sal_Int32(API_RGB_TRANSPARENT)));
+ }
+ else if (rAttribs.hasAttribute(XML_theme))
+ {
+ sal_uInt32 nThemeIndex = rAttribs.getUnsigned(XML_theme, 0);
+
+ // Excel has a bug in the mapping of index 0, 1, 2 and 3.
+ if (nThemeIndex == 0)
+ nThemeIndex = 1;
+ else if (nThemeIndex == 1)
+ nThemeIndex = 0;
+ else if (nThemeIndex == 2)
+ nThemeIndex = 3;
+ else if (nThemeIndex == 3)
+ nThemeIndex = 2;
+
+ ::Color aColor = rThemeBuffer.getColorByIndex(nThemeIndex);
+ double nTint = rAttribs.getDouble(XML_tint, 0.0);
+
+ if (nTint > 0.0)
+ aColor.ApplyTintOrShade(nTint * 10000);
+ return aColor;
+ }
+
+ return ::Color();
+}
+
+void addColorsToSparklineAttributes(sc::SparklineAttributes& rAttributes, sal_Int32 nElement,
+ const AttributeList& rAttribs, ThemeBuffer& rThemeBuffer)
+{
+ switch (nElement)
+ {
+ case XLS14_TOKEN(colorSeries):
+ rAttributes.setColorSeries(getColor(rAttribs, rThemeBuffer));
+ break;
+ case XLS14_TOKEN(colorNegative):
+ rAttributes.setColorNegative(getColor(rAttribs, rThemeBuffer));
+ break;
+ case XLS14_TOKEN(colorAxis):
+ rAttributes.setColorAxis(getColor(rAttribs, rThemeBuffer));
+ break;
+ case XLS14_TOKEN(colorMarkers):
+ rAttributes.setColorMarkers(getColor(rAttribs, rThemeBuffer));
+ break;
+ case XLS14_TOKEN(colorFirst):
+ rAttributes.setColorFirst(getColor(rAttribs, rThemeBuffer));
+ break;
+ case XLS14_TOKEN(colorLast):
+ rAttributes.setColorLast(getColor(rAttribs, rThemeBuffer));
+ break;
+ case XLS14_TOKEN(colorHigh):
+ rAttributes.setColorHigh(getColor(rAttribs, rThemeBuffer));
+ break;
+ case XLS14_TOKEN(colorLow):
+ rAttributes.setColorLow(getColor(rAttribs, rThemeBuffer));
+ break;
+ default:
+ break;
+ }
+}
+
+sc::SparklineType parseSparklineType(std::u16string_view rString)
+{
+ if (rString == u"column")
+ return sc::SparklineType::Column;
+ else if (rString == u"stacked")
+ return sc::SparklineType::Stacked;
+ return sc::SparklineType::Line;
+}
+
+sc::DisplayEmptyCellsAs parseDisplayEmptyCellsAs(std::u16string_view rString)
+{
+ if (rString == u"span")
+ return sc::DisplayEmptyCellsAs::Span;
+ else if (rString == u"gap")
+ return sc::DisplayEmptyCellsAs::Gap;
+ return sc::DisplayEmptyCellsAs::Zero;
+}
+
+sc::AxisType parseAxisType(std::u16string_view rString)
+{
+ if (rString == u"group")
+ return sc::AxisType::Group;
+ else if (rString == u"custom")
+ return sc::AxisType::Custom;
+ return sc::AxisType::Individual;
+}
+
+void addAttributesToSparklineAttributes(sc::SparklineAttributes& rSparklineAttributes,
+ const AttributeList& rAttribs)
+{
+ auto oManualMax = rAttribs.getDouble(XML_manualMax);
+ auto oManualMin = rAttribs.getDouble(XML_manualMin);
+
+ rSparklineAttributes.setLineWeight(rAttribs.getDouble(XML_lineWeight, 0.75));
+
+ OUString sType = rAttribs.getString(XML_type, "line");
+ rSparklineAttributes.setType(parseSparklineType(sType));
+
+ rSparklineAttributes.setDateAxis(rAttribs.getBool(XML_dateAxis, false));
+
+ OUString sDisplayEmptyCellsAs = rAttribs.getString(XML_displayEmptyCellsAs, "zero");
+ rSparklineAttributes.setDisplayEmptyCellsAs(parseDisplayEmptyCellsAs(sDisplayEmptyCellsAs));
+
+ rSparklineAttributes.setMarkers(rAttribs.getBool(XML_markers, false));
+ rSparklineAttributes.setHigh(rAttribs.getBool(XML_high, false));
+ rSparklineAttributes.setLow(rAttribs.getBool(XML_low, false));
+ rSparklineAttributes.setFirst(rAttribs.getBool(XML_first, false));
+ rSparklineAttributes.setLast(rAttribs.getBool(XML_last, false));
+ rSparklineAttributes.setNegative(rAttribs.getBool(XML_negative, false));
+ rSparklineAttributes.setDisplayXAxis(rAttribs.getBool(XML_displayXAxis, false));
+ rSparklineAttributes.setDisplayHidden(rAttribs.getBool(XML_displayHidden, false));
+
+ OUString sMinAxisType = rAttribs.getString(XML_minAxisType, "individual");
+ rSparklineAttributes.setMinAxisType(parseAxisType(sMinAxisType));
+
+ OUString sMaxAxisType = rAttribs.getString(XML_maxAxisType, "individual");
+ rSparklineAttributes.setMaxAxisType(parseAxisType(sMaxAxisType));
+
+ rSparklineAttributes.setRightToLeft(rAttribs.getBool(XML_rightToLeft, false));
+
+ if (rSparklineAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ rSparklineAttributes.setManualMax(oManualMax.get());
+ if (rSparklineAttributes.getMinAxisType() == sc::AxisType::Custom)
+ rSparklineAttributes.setManualMin(oManualMin.get());
+}
+
+} // end anonymous namespace
+
+SparklineGroupsContext::SparklineGroupsContext(WorksheetContextBase& rFragment)
+ : WorksheetContextBase(rFragment)
+{
+}
+
+ContextHandlerRef SparklineGroupsContext::onCreateContext(sal_Int32 nElement,
+ const AttributeList& rAttribs)
+{
+ switch (nElement)
+ {
+ case XLS14_TOKEN(sparklineGroup):
+ {
+ auto& rLastGroup = m_aSparklineGroups.emplace_back();
+ auto& rSparklineAttributes = rLastGroup.getSparklineGroup()->getAttributes();
+ addAttributesToSparklineAttributes(rSparklineAttributes, rAttribs);
+ OUString sGUID = rAttribs.getString(XR2_TOKEN(uid), OUString());
+ tools::Guid aGuid(OUStringToOString(sGUID, RTL_TEXTENCODING_ASCII_US));
+ rLastGroup.getSparklineGroup()->setID(aGuid);
+ return this;
+ }
+ case XLS14_TOKEN(colorSeries):
+ case XLS14_TOKEN(colorNegative):
+ case XLS14_TOKEN(colorAxis):
+ case XLS14_TOKEN(colorMarkers):
+ case XLS14_TOKEN(colorFirst):
+ case XLS14_TOKEN(colorLast):
+ case XLS14_TOKEN(colorHigh):
+ case XLS14_TOKEN(colorLow):
+ {
+ auto& rLastGroup = m_aSparklineGroups.back();
+ auto& rSparklineAttributes = rLastGroup.getSparklineGroup()->getAttributes();
+ addColorsToSparklineAttributes(rSparklineAttributes, nElement, rAttribs, getTheme());
+ return this;
+ }
+ case XLS14_TOKEN(sparklines):
+ {
+ return this;
+ }
+ case XLS14_TOKEN(sparkline):
+ {
+ auto& rLastGroup = m_aSparklineGroups.back();
+ rLastGroup.getSparklines().emplace_back();
+ return this;
+ }
+ }
+ return this;
+}
+
+void SparklineGroupsContext::onStartElement(const AttributeList&) {}
+
+void SparklineGroupsContext::onCharacters(const OUString& rChars)
+{
+ if (getCurrentElement() == XM_TOKEN(sqref) || getCurrentElement() == XM_TOKEN(f))
+ {
+ ScDocument& rDocument = getScDocument();
+ auto& rLastGroup = m_aSparklineGroups.back();
+ auto& rLastSparkline = rLastGroup.getSparklines().back();
+ ScRangeList aRange;
+ if (ScRangeStringConverter::GetRangeListFromString(aRange, rChars, rDocument,
+ formula::FormulaGrammar::CONV_XL_OOX))
+ {
+ if (!aRange.empty())
+ {
+ if (getCurrentElement() == XM_TOKEN(sqref))
+ {
+ rLastSparkline.m_aTargetRange = aRange;
+
+ // Need to set the current sheet index to the range as
+ // it is assumed that the address string refers to
+ // the current sheet and is not defined in the string.
+ for (auto& rRange : rLastSparkline.m_aTargetRange)
+ {
+ rRange.aStart.SetTab(getSheetIndex());
+ rRange.aEnd.SetTab(getSheetIndex());
+ }
+ }
+ else if (getCurrentElement() == XM_TOKEN(f))
+ rLastSparkline.m_aInputRange = aRange;
+ }
+ }
+ }
+}
+
+void SparklineGroupsContext::onEndElement()
+{
+ if (getCurrentElement() == XLS14_TOKEN(sparklineGroup))
+ {
+ auto& rLastGroup = m_aSparklineGroups.back();
+ for (Sparkline& rSparkline : rLastGroup.getSparklines())
+ {
+ insertSparkline(rLastGroup, rSparkline);
+ }
+ }
+}
+
+void SparklineGroupsContext::insertSparkline(SparklineGroup& rSparklineGroup, Sparkline& rSparkline)
+{
+ auto& rDocument = getScDocument();
+ if (rSparkline.m_aTargetRange.size() == 1)
+ {
+ auto& rRange = rSparkline.m_aTargetRange[0];
+ if (rRange.aStart == rRange.aEnd)
+ {
+ auto pSparklineGroup = rSparklineGroup.getSparklineGroup();
+ auto* pCreated = rDocument.CreateSparkline(rRange.aStart, pSparklineGroup);
+ pCreated->setInputRange(rSparkline.m_aInputRange);
+ }
+ }
+}
+
+} //namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/addressconverter.cxx b/sc/source/filter/oox/addressconverter.cxx
new file mode 100644
index 000000000..b3fc5b167
--- /dev/null
+++ b/sc/source/filter/oox/addressconverter.cxx
@@ -0,0 +1,483 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <addressconverter.hxx>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <convuno.hxx>
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+//! TODO: this limit may change, is there a way to obtain it via API?
+const sal_Int16 API_MAXTAB = MAXTAB;
+
+const sal_Int32 OOX_MAXCOL = static_cast< sal_Int32 >( (1 << 14) - 1 );
+const sal_Int32 OOX_MAXROW = static_cast< sal_Int32 >( (1 << 20) - 1 );
+const sal_Int16 OOX_MAXTAB = static_cast< sal_Int16 >( (1 << 15) - 1 );
+
+} // namespace
+
+
+void BinAddress::read( SequenceInputStream& rStrm )
+{
+ mnRow = rStrm.readInt32();
+ mnCol = rStrm.readInt32();
+}
+
+void BinRange::read( SequenceInputStream& rStrm )
+{
+ maFirst.mnRow = rStrm.readInt32();
+ maLast.mnRow = rStrm.readInt32();
+ maFirst.mnCol = rStrm.readInt32();
+ maLast.mnCol = rStrm.readInt32();
+}
+
+void BinRangeList::read( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCount = rStrm.readInt32();
+ mvRanges.resize( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 16 ) );
+ for( auto& rRange : mvRanges )
+ rRange.read( rStrm );
+}
+
+AddressConverter::AddressConverter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbColOverflow( false ),
+ mbRowOverflow( false ),
+ mbTabOverflow( false )
+{
+ initializeMaxPos( OOX_MAXTAB, OOX_MAXCOL, OOX_MAXROW );
+}
+
+bool AddressConverter::parseOoxAddress2d(
+ sal_Int32& ornColumn, sal_Int32& ornRow,
+ const OUString& rString, sal_Int32 nStart, sal_Int32 nLength )
+{
+ ornColumn = ornRow = 0;
+ if( (nStart < 0) || (nStart >= rString.getLength()) || (nLength < 2) )
+ return false;
+
+ const sal_Unicode* pcChar = rString.getStr() + nStart;
+ const sal_Unicode* pcEndChar = pcChar + ::std::min( nLength, rString.getLength() - nStart );
+
+ enum { STATE_COL, STATE_ROW } eState = STATE_COL;
+ while( pcChar < pcEndChar )
+ {
+ sal_Unicode cChar = *pcChar;
+ switch( eState )
+ {
+ case STATE_COL:
+ {
+ if( ('a' <= cChar) && (cChar <= 'z') )
+ cChar = (cChar - 'a') + 'A';
+ if( ('A' <= cChar) && (cChar <= 'Z') )
+ {
+ /* Return, if 1-based column index is already 6 characters
+ long (12356631 is column index for column AAAAAA). */
+ if( ornColumn >= 12356631 )
+ return false;
+ ornColumn = (ornColumn * 26) + (cChar - 'A' + 1);
+ }
+ else if( ornColumn > 0 )
+ {
+ --pcChar;
+ eState = STATE_ROW;
+ }
+ else
+ return false;
+ }
+ break;
+
+ case STATE_ROW:
+ {
+ if( ('0' <= cChar) && (cChar <= '9') )
+ {
+ // return, if 1-based row is already 9 digits long
+ if( ornRow >= 100000000 )
+ return false;
+ ornRow = (ornRow * 10) + (cChar - '0');
+ }
+ else
+ return false;
+ }
+ break;
+ }
+ ++pcChar;
+ }
+
+ --ornColumn;
+ --ornRow;
+ return (ornColumn >= 0) && (ornRow >= 0);
+}
+
+bool AddressConverter::parseOoxAddress2d( sal_Int32& ornColumn, sal_Int32& ornRow, const char* pStr )
+{
+ ornColumn = ornRow = 0;
+
+ enum { STATE_COL, STATE_ROW } eState = STATE_COL;
+
+ while (*pStr)
+ {
+ char cChar = *pStr;
+ switch( eState )
+ {
+ case STATE_COL:
+ {
+ if( ('a' <= cChar) && (cChar <= 'z') )
+ cChar = (cChar - 'a') + 'A';
+ if( ('A' <= cChar) && (cChar <= 'Z') )
+ {
+ /* Return, if 1-based column index is already 6 characters
+ long (12356631 is column index for column AAAAAA). */
+ if( ornColumn >= 12356631 )
+ return false;
+ ornColumn = (ornColumn * 26) + (cChar - 'A' + 1);
+ }
+ else if( ornColumn > 0 )
+ {
+ --pStr;
+ eState = STATE_ROW;
+ }
+ else
+ return false;
+ }
+ break;
+
+ case STATE_ROW:
+ {
+ if( ('0' <= cChar) && (cChar <= '9') )
+ {
+ // return, if 1-based row is already 9 digits long
+ if( ornRow >= 100000000 )
+ return false;
+ ornRow = (ornRow * 10) + (cChar - '0');
+ }
+ else
+ return false;
+ }
+ break;
+ }
+ ++pStr;
+ }
+
+ --ornColumn;
+ --ornRow;
+ return (ornColumn >= 0) && (ornRow >= 0);
+}
+
+bool AddressConverter::parseOoxRange2d(
+ sal_Int32& ornStartColumn, sal_Int32& ornStartRow,
+ sal_Int32& ornEndColumn, sal_Int32& ornEndRow,
+ const OUString& rString, sal_Int32 nStart )
+{
+ ornStartColumn = ornStartRow = ornEndColumn = ornEndRow = 0;
+ if( (nStart < 0) || (nStart >= rString.getLength()) )
+ return false;
+
+ sal_Int32 nEnd = nStart + ( rString.getLength() - nStart );
+ sal_Int32 nColonPos = rString.indexOf( ':', nStart );
+ if( (nStart < nColonPos) && (nColonPos + 1 < nEnd) )
+ {
+ return
+ parseOoxAddress2d( ornStartColumn, ornStartRow, rString, nStart, nColonPos - nStart ) &&
+ parseOoxAddress2d( ornEndColumn, ornEndRow, rString, nColonPos + 1, SAL_MAX_INT32 - nColonPos - 1 );
+ }
+
+ if( parseOoxAddress2d( ornStartColumn, ornStartRow, rString, nStart ) )
+ {
+ ornEndColumn = ornStartColumn;
+ ornEndRow = ornStartRow;
+ return true;
+ }
+
+ return false;
+}
+
+bool AddressConverter::checkCol( sal_Int32 nCol, bool bTrackOverflow )
+{
+ bool bValid = (0 <= nCol) && ( nCol <= maMaxPos.Col() );
+ if( !bValid && bTrackOverflow )
+ mbColOverflow = true;
+ return bValid;
+}
+
+bool AddressConverter::checkRow( sal_Int32 nRow, bool bTrackOverflow )
+{
+ bool bValid = (0 <= nRow) && ( nRow <= maMaxPos.Row() );
+ if( !bValid && bTrackOverflow )
+ mbRowOverflow = true;
+ return bValid;
+}
+
+bool AddressConverter::checkTab( sal_Int16 nSheet, bool bTrackOverflow )
+{
+ bool bValid = (0 <= nSheet) && ( nSheet <= maMaxPos.Tab() );
+ if( !bValid && bTrackOverflow )
+ mbTabOverflow |= ( nSheet > maMaxPos.Tab() ); // do not warn for deleted refs (-1)
+ return bValid;
+}
+
+bool AddressConverter::checkCellAddress( const ScAddress& rAddress, bool bTrackOverflow )
+{
+ return
+ checkTab( rAddress.Tab(), bTrackOverflow ) &&
+ checkCol( rAddress.Col(), bTrackOverflow ) &&
+ checkRow( rAddress.Row(), bTrackOverflow );
+}
+
+bool AddressConverter::convertToCellAddressUnchecked( ScAddress& orAddress,
+ const OUString& rString, sal_Int16 nSheet )
+{
+ orAddress.SetTab(nSheet);
+ sal_Int32 nCol = 0;
+ sal_Int32 nRow = 0;
+ bool bRes = parseOoxAddress2d( nCol, nRow, rString );
+ orAddress.SetRow(nRow);
+ orAddress.SetCol(nCol);
+
+ return bRes;
+}
+
+bool AddressConverter::convertToCellAddressUnchecked(
+ ScAddress& orAddress, const char* pStr, sal_Int16 nSheet )
+{
+ orAddress.SetTab(nSheet);
+ sal_Int32 nCol = 0;
+ sal_Int32 nRow = 0;
+ bool bRes = parseOoxAddress2d(nCol, nRow, pStr);
+ orAddress.SetRow(nRow);
+ orAddress.SetCol(nCol);
+
+ return bRes;
+}
+
+bool AddressConverter::convertToCellAddress( ScAddress& orAddress,
+ const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ return
+ convertToCellAddressUnchecked( orAddress, rString, nSheet ) &&
+ checkCellAddress( orAddress, bTrackOverflow );
+}
+
+bool AddressConverter::convertToCellAddress(
+ ScAddress& rAddress,
+ const char* pStr, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ if (!convertToCellAddressUnchecked(rAddress, pStr, nSheet))
+ return false;
+
+ return checkCellAddress(rAddress, bTrackOverflow);
+}
+
+ScAddress AddressConverter::createValidCellAddress(
+ const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ ScAddress aAddress( 0, 0, 0 );
+ if( !convertToCellAddress( aAddress, rString, nSheet, bTrackOverflow ) )
+ {
+ aAddress.SetTab( getLimitedValue< sal_Int16, sal_Int16 >( nSheet, 0, maMaxPos.Tab() ) );
+ aAddress.SetCol( ::std::min( aAddress.Col(), maMaxPos.Col() ) );
+ aAddress.SetRow( ::std::min( aAddress.Row(), maMaxPos.Row() ) );
+ }
+ return aAddress;
+}
+
+void AddressConverter::convertToCellAddressUnchecked( ScAddress& orAddress,
+ const BinAddress& rBinAddress, sal_Int16 nSheet )
+{
+ orAddress.SetTab(nSheet);
+ orAddress.SetCol(rBinAddress.mnCol);
+ orAddress.SetRow(rBinAddress.mnRow);
+}
+
+bool AddressConverter::convertToCellAddress( ScAddress& orAddress,
+ const BinAddress& rBinAddress, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ convertToCellAddressUnchecked( orAddress, rBinAddress, nSheet );
+ return checkCellAddress( orAddress, bTrackOverflow );
+}
+
+ScAddress AddressConverter::createValidCellAddress(
+ const BinAddress& rBinAddress, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ ScAddress aAddress ( 0, 0, 0 );
+ if( !convertToCellAddress( aAddress, rBinAddress, nSheet, bTrackOverflow ) )
+ {
+ aAddress.SetTab( getLimitedValue< sal_Int16, sal_Int16 >( nSheet, 0, maMaxPos.Tab() ) );
+ aAddress.SetCol( getLimitedValue< sal_Int32, sal_Int32 >( rBinAddress.mnCol, 0, sal_Int32( maMaxPos.Col() ) ) );
+ aAddress.SetRow( getLimitedValue< sal_Int32, sal_Int32 >( rBinAddress.mnRow, 0, sal_Int32( maMaxPos.Row() ) ) );
+ }
+ return aAddress;
+}
+
+bool AddressConverter::checkCellRange( const ScRange& rRange, bool bAllowOverflow, bool bTrackOverflow )
+{
+ return
+ (checkCol( rRange.aEnd.Col(), bTrackOverflow ) || bAllowOverflow) && // bAllowOverflow after checkCol to track overflow!
+ (checkRow( rRange.aEnd.Row(), bTrackOverflow ) || bAllowOverflow) && // bAllowOverflow after checkRow to track overflow!
+ checkTab( rRange.aStart.Tab(), bTrackOverflow ) &&
+ checkCol( rRange.aStart.Col(), bTrackOverflow ) &&
+ checkRow( rRange.aStart.Row(), bTrackOverflow );
+}
+
+bool AddressConverter::validateCellRange( ScRange& orRange, bool bAllowOverflow, bool bTrackOverflow )
+{
+ if( orRange.aStart.Col() > orRange.aEnd.Col() )
+ {
+ SCCOL nCol = orRange.aStart.Col();
+ orRange.aStart.SetCol( orRange.aEnd.Col() );
+ orRange.aEnd.SetCol( nCol );
+ }
+ if( orRange.aStart.Row() > orRange.aEnd.Row() )
+ {
+ SCROW nRow = orRange.aStart.Row();
+ orRange.aStart.SetRow( orRange.aEnd.Row() );
+ orRange.aEnd.SetRow( nRow );
+ }
+ if( !checkCellRange( orRange, bAllowOverflow, bTrackOverflow ) )
+ return false;
+ if( orRange.aEnd.Col() > maMaxPos.Col() )
+ orRange.aEnd.SetCol( maMaxPos.Col() );
+ if( orRange.aEnd.Row() > maMaxPos.Row() )
+ orRange.aEnd.SetRow( maMaxPos.Row() );
+ return true;
+}
+
+bool AddressConverter::convertToCellRangeUnchecked( ScRange& orRange,
+ const OUString& rString, sal_Int16 nSheet )
+{
+ orRange.aStart.SetTab( nSheet );
+ orRange.aEnd.SetTab( nSheet );
+ sal_Int32 aStartCol = orRange.aStart.Col();
+ sal_Int32 aStartRow = orRange.aStart.Row();
+ sal_Int32 aEndCol = orRange.aEnd.Col();
+ sal_Int32 aEndRow = orRange.aEnd.Row();
+ bool bReturnValue = parseOoxRange2d( aStartCol, aStartRow, aEndCol, aEndRow, rString );
+ orRange.aStart.SetCol( aStartCol );
+ orRange.aStart.SetRow( aStartRow );
+ orRange.aEnd.SetCol( aEndCol );
+ orRange.aEnd.SetRow( aEndRow );
+ return bReturnValue;
+}
+
+bool AddressConverter::convertToCellRange( ScRange& orRange,
+ const OUString& rString, sal_Int16 nSheet, bool bAllowOverflow, bool bTrackOverflow )
+{
+ return
+ convertToCellRangeUnchecked( orRange, rString, nSheet ) &&
+ validateCellRange( orRange, bAllowOverflow, bTrackOverflow );
+}
+
+void AddressConverter::convertToCellRangeUnchecked( ScRange& orRange,
+ const BinRange& rBinRange, sal_Int16 nSheet )
+{
+ orRange.aStart.SetTab( nSheet );
+ orRange.aStart.SetCol( rBinRange.maFirst.mnCol );
+ orRange.aStart.SetRow( rBinRange.maFirst.mnRow );
+ orRange.aEnd.SetTab( nSheet );
+ orRange.aEnd.SetCol( rBinRange.maLast.mnCol );
+ orRange.aEnd.SetRow( rBinRange.maLast.mnRow );
+}
+
+bool AddressConverter::convertToCellRange( ScRange& orRange,
+ const BinRange& rBinRange, sal_Int16 nSheet, bool bAllowOverflow, bool bTrackOverflow )
+{
+ convertToCellRangeUnchecked( orRange, rBinRange, nSheet );
+ return validateCellRange( orRange, bAllowOverflow, bTrackOverflow );
+}
+
+void AddressConverter::validateCellRangeList( ScRangeList& orRanges, bool bTrackOverflow )
+{
+ for( size_t nIndex = orRanges.size(); nIndex > 0; --nIndex )
+ if( !validateCellRange( orRanges[ nIndex - 1 ], true, bTrackOverflow ) )
+ orRanges.Remove( nIndex - 1 );
+}
+
+void AddressConverter::convertToCellRangeList( ScRangeList& orRanges,
+ const OUString& rString, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ sal_Int32 nPos = 0;
+ sal_Int32 nLen = rString.getLength();
+ ScRange aRange;
+ while( (0 <= nPos) && (nPos < nLen) )
+ {
+ OUString aToken = rString.getToken( 0, ' ', nPos );
+ if( !aToken.isEmpty() && convertToCellRange( aRange, aToken, nSheet, true, bTrackOverflow ) )
+ orRanges.push_back(aRange);
+ }
+}
+
+void AddressConverter::convertToCellRangeList( ScRangeList& orRanges,
+ const BinRangeList& rBinRanges, sal_Int16 nSheet, bool bTrackOverflow )
+{
+ ScRange aRange;
+ for( const auto& rBinRange : rBinRanges )
+ if( convertToCellRange( aRange, rBinRange, nSheet, true, bTrackOverflow ) )
+ orRanges.push_back( aRange );
+}
+
+Sequence<CellRangeAddress> AddressConverter::toApiSequence(const ScRangeList& orRanges)
+{
+ const size_t nSize = orRanges.size();
+ Sequence<CellRangeAddress> aRangeSequence(nSize);
+ CellRangeAddress* pApiRanges = aRangeSequence.getArray();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ ScUnoConversion::FillApiRange(pApiRanges[i], orRanges[i]);
+ }
+ return aRangeSequence;
+}
+
+// private --------------------------------------------------------------------
+
+void AddressConverter::initializeMaxPos(
+ sal_Int16 nMaxXlsTab, sal_Int32 nMaxXlsCol, sal_Int32 nMaxXlsRow )
+{
+ maMaxXlsPos.Set( nMaxXlsCol, nMaxXlsRow, nMaxXlsTab);
+
+ // maximum cell position in Calc
+ try
+ {
+ Reference< XIndexAccess > xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW );
+ Reference< XCellRangeAddressable > xAddressable( xSheetsIA->getByIndex( 0 ), UNO_QUERY_THROW );
+ CellRangeAddress aRange = xAddressable->getRangeAddress();
+ maMaxApiPos = ScAddress( aRange.EndColumn, aRange.EndRow, API_MAXTAB );
+ maMaxPos = getBaseFilter().isImportFilter() ? maMaxApiPos : maMaxXlsPos;
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "AddressConverter::AddressConverter - cannot get sheet limits" );
+ }
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/autofilterbuffer.cxx b/sc/source/filter/oox/autofilterbuffer.cxx
new file mode 100644
index 000000000..4fc354960
--- /dev/null
+++ b/sc/source/filter/oox/autofilterbuffer.cxx
@@ -0,0 +1,955 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <autofilterbuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/FilterFieldType.hpp>
+#include <com/sun/star/sheet/FilterConnection.hpp>
+#include <com/sun/star/sheet/FilterOperator2.hpp>
+#include <com/sun/star/sheet/TableFilterField3.hpp>
+#include <com/sun/star/sheet/XDatabaseRange.hpp>
+#include <com/sun/star/sheet/XSheetFilterDescriptor3.hpp>
+#include <com/sun/star/table/TableOrientation.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <editeng/brushitem.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <defnamesbuffer.hxx>
+#include <biffhelper.hxx>
+#include <document.hxx>
+#include <dbdata.hxx>
+#include <scitems.hxx>
+#include <sortparam.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <stylesbuffer.hxx>
+#include <userlist.hxx>
+
+namespace oox::xls {
+
+using namespace css;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt8 BIFF12_TOP10FILTER_TOP = 0x01;
+const sal_uInt8 BIFF12_TOP10FILTER_PERCENT = 0x02;
+
+const sal_uInt16 BIFF12_FILTERCOLUMN_HIDDENBUTTON = 0x0001;
+const sal_uInt16 BIFF12_FILTERCOLUMN_SHOWBUTTON = 0x0002;
+
+const sal_uInt8 BIFF_FILTER_DATATYPE_NONE = 0;
+const sal_uInt8 BIFF_FILTER_DATATYPE_DOUBLE = 4;
+const sal_uInt8 BIFF_FILTER_DATATYPE_STRING = 6;
+const sal_uInt8 BIFF_FILTER_DATATYPE_BOOLEAN = 8;
+const sal_uInt8 BIFF_FILTER_DATATYPE_EMPTY = 12;
+const sal_uInt8 BIFF_FILTER_DATATYPE_NOTEMPTY = 14;
+
+bool lclGetApiOperatorFromToken( sal_Int32& rnApiOperator, sal_Int32 nToken )
+{
+ switch( nToken )
+ {
+ case XML_lessThan: rnApiOperator = FilterOperator2::LESS; return true;
+ case XML_equal: rnApiOperator = FilterOperator2::EQUAL; return true;
+ case XML_lessThanOrEqual: rnApiOperator = FilterOperator2::LESS_EQUAL; return true;
+ case XML_greaterThan: rnApiOperator = FilterOperator2::GREATER; return true;
+ case XML_notEqual: rnApiOperator = FilterOperator2::NOT_EQUAL; return true;
+ case XML_greaterThanOrEqual: rnApiOperator = FilterOperator2::GREATER_EQUAL; return true;
+ }
+ return false;
+}
+
+/** Removes leading asterisk characters from the passed string.
+ @return True = at least one asterisk character has been removed. */
+bool lclTrimLeadingAsterisks( OUString& rValue )
+{
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 nPos = 0;
+ while( (nPos < nLength) && (rValue[ nPos ] == '*') )
+ ++nPos;
+ if( nPos > 0 )
+ {
+ rValue = rValue.copy( nPos );
+ return true;
+ }
+ return false;
+}
+
+/** Removes trailing asterisk characters from the passed string.
+ @return True = at least one asterisk character has been removed. */
+bool lclTrimTrailingAsterisks( OUString& rValue )
+{
+ sal_Int32 nLength = rValue.getLength();
+ sal_Int32 nPos = nLength;
+ while( (nPos > 0) && (rValue[ nPos - 1 ] == '*') )
+ --nPos;
+ if( nPos < nLength )
+ {
+ rValue = rValue.copy( 0, nPos );
+ return true;
+ }
+ return false;
+}
+
+/** Converts wildcard characters '*' and '?' to regular expressions and quotes
+ RE meta characters.
+ @return True = passed string has been changed (RE needs to be enabled). */
+bool lclConvertWildcardsToRegExp( OUString& rValue )
+{
+ // check existence of the wildcard characters '*' and '?'
+ if( !rValue.isEmpty() && ((rValue.indexOf( '*' ) >= 0) || (rValue.indexOf( '?' ) >= 0)) )
+ {
+ OUStringBuffer aBuffer;
+ aBuffer.ensureCapacity( rValue.getLength() + 5 );
+ const sal_Unicode* pcChar = rValue.getStr();
+ const sal_Unicode* pcEnd = pcChar + rValue.getLength();
+ for( ; pcChar < pcEnd; ++pcChar )
+ {
+ switch( *pcChar )
+ {
+ case '?':
+ aBuffer.append( '.' );
+ break;
+ case '*':
+ aBuffer.append( '.' ).append( '*' );
+ break;
+ case '\\': case '.': case '|': case '(': case ')': case '^': case '$':
+ // quote RE meta characters
+ aBuffer.append( '\\' ).append( *pcChar );
+ break;
+ default:
+ aBuffer.append( *pcChar );
+ }
+ }
+ rValue = aBuffer.makeStringAndClear();
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+ApiFilterSettings::ApiFilterSettings()
+{
+}
+
+void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, double fValue )
+{
+ maFilterFields.emplace_back();
+ TableFilterField3& rFilterField = maFilterFields.back();
+ rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
+ rFilterField.Operator = nOperator;
+ rFilterField.Values.realloc(1);
+ auto pValues = rFilterField.Values.getArray();
+ pValues[0].FilterType = FilterFieldType::NUMERIC;
+ pValues[0].NumericValue = fValue;
+}
+
+void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, const OUString& rValue )
+{
+ maFilterFields.emplace_back();
+ TableFilterField3& rFilterField = maFilterFields.back();
+ rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
+ rFilterField.Operator = nOperator;
+ rFilterField.Values.realloc(1);
+ auto pValues = rFilterField.Values.getArray();
+ pValues[0].FilterType = FilterFieldType::STRING;
+ pValues[0].StringValue = rValue;
+}
+
+void ApiFilterSettings::appendField(bool bAnd, util::Color aColor, bool bIsBackgroundColor)
+{
+ maFilterFields.emplace_back();
+ TableFilterField3& rFilterField = maFilterFields.back();
+ rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
+ rFilterField.Operator = FilterOperator2::EQUAL;
+ rFilterField.Values.realloc(1);
+ auto pValues = rFilterField.Values.getArray();
+ pValues[0].FilterType
+ = bIsBackgroundColor ? FilterFieldType::BACKGROUND_COLOR : FilterFieldType::TEXT_COLOR;
+ pValues[0].ColorValue = aColor;
+}
+
+void ApiFilterSettings::appendField( bool bAnd, const std::vector<std::pair<OUString, bool>>& rValues )
+{
+ maFilterFields.emplace_back();
+ TableFilterField3& rFilterField = maFilterFields.back();
+ rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
+ rFilterField.Operator = FilterOperator2::EQUAL;
+ rFilterField.Values.realloc(rValues.size());
+ auto pValues = rFilterField.Values.getArray();
+ size_t i = 0;
+
+ for( auto const& it : rValues )
+ {
+ pValues[i].StringValue = it.first;
+ pValues[i++].FilterType
+ = it.second ? FilterFieldType::DATE : FilterFieldType::STRING;
+ }
+}
+
+FilterSettingsBase::FilterSettingsBase( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void FilterSettingsBase::importAttribs( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
+{
+}
+
+void FilterSettingsBase::importRecord( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
+{
+}
+
+ApiFilterSettings FilterSettingsBase::finalizeImport()
+{
+ return ApiFilterSettings();
+}
+
+DiscreteFilter::DiscreteFilter( const WorkbookHelper& rHelper ) :
+ FilterSettingsBase( rHelper ),
+ mnCalendarType( XML_none ),
+ mbShowBlank( false )
+{
+}
+
+void DiscreteFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( nElement )
+ {
+ case XLS_TOKEN( filters ):
+ mnCalendarType = rAttribs.getToken( XML_calendarType, XML_none );
+ mbShowBlank = rAttribs.getBool( XML_blank, false );
+ break;
+
+ case XLS_TOKEN( filter ):
+ {
+ OUString aValue = rAttribs.getXString( XML_val, OUString() );
+ if( !aValue.isEmpty() )
+ maValues.push_back( std::make_pair(aValue, false) );
+ }
+ break;
+
+ case XLS_TOKEN( dateGroupItem ):
+ {
+ OUString aDateValue;
+ // it is just a fallback, we do not need the XML_day as default value,
+ // because if the dateGroupItem exists also XML_dateTimeGrouping exists!
+ sal_uInt16 nToken = rAttribs.getToken(XML_dateTimeGrouping, XML_day);
+ if( nToken == XML_year || nToken == XML_month || nToken == XML_day ||
+ nToken == XML_hour || nToken == XML_minute || nToken == XML_second )
+ {
+ aDateValue = rAttribs.getString(XML_year, OUString());
+
+ if( nToken == XML_month || nToken == XML_day || nToken == XML_hour ||
+ nToken == XML_minute || nToken == XML_second )
+ {
+ OUString aMonthName = rAttribs.getString(XML_month, OUString());
+ if( aMonthName.getLength() == 1 )
+ aMonthName = "0" + aMonthName;
+ aDateValue += "-" + aMonthName;
+
+ if( nToken == XML_day || nToken == XML_hour || nToken == XML_minute ||
+ nToken == XML_second )
+ {
+ OUString aDayName = rAttribs.getString(XML_day, OUString());
+ if( aDayName.getLength() == 1 )
+ aDayName = "0" + aDayName;
+ aDateValue += "-" + aDayName;
+
+ if( nToken == XML_hour || nToken == XML_minute || nToken == XML_second )
+ {
+ OUString aHourName = rAttribs.getString(XML_hour, OUString());
+ if( aHourName.getLength() == 1 )
+ aHourName = "0" + aHourName;
+ aDateValue += " " + aHourName;
+
+ if( nToken == XML_minute || nToken == XML_second )
+ {
+ OUString aMinName = rAttribs.getString(XML_minute, OUString());
+ if( aMinName.getLength() == 1 )
+ aMinName = "0" + aMinName;
+ aDateValue += ":" + aMinName;
+
+ if( nToken == XML_second )
+ {
+ OUString aSecName = rAttribs.getString(XML_second, OUString());
+ if( aSecName.getLength() == 1 )
+ aSecName = "0" + aSecName;
+ aDateValue += ":" + aSecName;
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !aDateValue.isEmpty() )
+ maValues.push_back( std::make_pair(aDateValue, true) );
+ }
+ break;
+ }
+}
+
+void DiscreteFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( nRecId )
+ {
+ case BIFF12_ID_DISCRETEFILTERS:
+ {
+ sal_Int32 nShowBlank, nCalendarType;
+ nShowBlank = rStrm.readInt32();
+ nCalendarType = rStrm.readInt32();
+
+ static const sal_Int32 spnCalendarTypes[] = {
+ XML_none, XML_gregorian, XML_gregorianUs, XML_japan, XML_taiwan, XML_korea, XML_hijri, XML_thai, XML_hebrew,
+ XML_gregorianMeFrench, XML_gregorianArabic, XML_gregorianXlitEnglish, XML_gregorianXlitFrench };
+ mnCalendarType = STATIC_ARRAY_SELECT( spnCalendarTypes, nCalendarType, XML_none );
+ mbShowBlank = nShowBlank != 0;
+ }
+ break;
+
+ case BIFF12_ID_DISCRETEFILTER:
+ {
+ OUString aValue = BiffHelper::readString( rStrm );
+ if( !aValue.isEmpty() )
+ maValues.push_back( std::make_pair(aValue, false) );
+ }
+ break;
+ }
+}
+
+ApiFilterSettings DiscreteFilter::finalizeImport()
+{
+ ApiFilterSettings aSettings;
+ aSettings.maFilterFields.reserve( maValues.size() );
+
+ // insert all filter values
+ aSettings.appendField( true, maValues );
+
+ // extra field for 'show empty'
+ if( mbShowBlank )
+ aSettings.appendField( false, FilterOperator2::EMPTY, OUString() );
+
+ /* Require disabled regular expressions, filter entries may contain
+ any RE meta characters. */
+ if( !maValues.empty() )
+ aSettings.mobNeedsRegExp = false;
+
+ return aSettings;
+}
+
+Top10Filter::Top10Filter( const WorkbookHelper& rHelper ) :
+ FilterSettingsBase( rHelper ),
+ mfValue( 0.0 ),
+ mbTop( true ),
+ mbPercent( false )
+{
+}
+
+void Top10Filter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( nElement == XLS_TOKEN( top10 ) )
+ {
+ mfValue = rAttribs.getDouble( XML_val, 0.0 );
+ mbTop = rAttribs.getBool( XML_top, true );
+ mbPercent = rAttribs.getBool( XML_percent, false );
+ }
+}
+
+void Top10Filter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ if( nRecId == BIFF12_ID_TOP10FILTER )
+ {
+ sal_uInt8 nFlags;
+ nFlags = rStrm.readuChar();
+ mfValue = rStrm.readDouble();
+ mbTop = getFlag( nFlags, BIFF12_TOP10FILTER_TOP );
+ mbPercent = getFlag( nFlags, BIFF12_TOP10FILTER_PERCENT );
+ }
+}
+
+ApiFilterSettings Top10Filter::finalizeImport()
+{
+ sal_Int32 nOperator = mbTop ?
+ (mbPercent ? FilterOperator2::TOP_PERCENT : FilterOperator2::TOP_VALUES) :
+ (mbPercent ? FilterOperator2::BOTTOM_PERCENT : FilterOperator2::BOTTOM_VALUES);
+ ApiFilterSettings aSettings;
+ aSettings.appendField( true, nOperator, mfValue );
+ return aSettings;
+}
+
+ColorFilter::ColorFilter(const WorkbookHelper& rHelper)
+ : FilterSettingsBase(rHelper)
+ , mbIsBackgroundColor(false)
+{
+}
+
+void ColorFilter::importAttribs(sal_Int32 nElement, const AttributeList& rAttribs)
+{
+ if (nElement == XLS_TOKEN(colorFilter))
+ {
+ // When cellColor attribute not found, it means cellColor = true
+ // cellColor = 0 (false) -> TextColor
+ // cellColor = 1 (true) -> BackgroundColor
+ mbIsBackgroundColor = rAttribs.getBool(XML_cellColor, true);
+ msStyleName = getStyles().createDxfStyle( rAttribs.getInteger(XML_dxfId, -1) );
+ }
+}
+
+void ColorFilter::importRecord(sal_Int32 /* nRecId */, SequenceInputStream& /* rStrm */)
+{
+ // TODO
+}
+
+ApiFilterSettings ColorFilter::finalizeImport()
+{
+ ApiFilterSettings aSettings;
+ ScDocument& rDoc = getScDocument();
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(
+ rDoc.GetStyleSheetPool()->Find(msStyleName, SfxStyleFamily::Para));
+ if (!pStyleSheet)
+ return aSettings;
+
+ const SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ // Color (whether text or background color) is always stored in ATTR_BACKGROUND
+ const SvxBrushItem* pItem = rItemSet.GetItem<SvxBrushItem>(ATTR_BACKGROUND);
+ ::Color aColor = pItem->GetFiltColor();
+ util::Color nColor(aColor);
+ aSettings.appendField(true, nColor, mbIsBackgroundColor);
+ return aSettings;
+}
+
+FilterCriterionModel::FilterCriterionModel() :
+ mnOperator( XML_equal ),
+ mnDataType( BIFF_FILTER_DATATYPE_NONE )
+{
+}
+
+void FilterCriterionModel::setBiffOperator( sal_uInt8 nOperator )
+{
+ static const sal_Int32 spnOperators[] = { XML_TOKEN_INVALID,
+ XML_lessThan, XML_equal, XML_lessThanOrEqual, XML_greaterThan, XML_notEqual, XML_greaterThanOrEqual };
+ mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
+}
+
+void FilterCriterionModel::readBiffData( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nOperator;
+ mnDataType = rStrm.readuChar();
+ nOperator = rStrm.readuChar();
+ setBiffOperator( nOperator );
+
+ switch( mnDataType )
+ {
+ case BIFF_FILTER_DATATYPE_DOUBLE:
+ maValue <<= rStrm.readDouble();
+ break;
+ case BIFF_FILTER_DATATYPE_STRING:
+ {
+ rStrm.skip( 8 );
+ OUString aValue = BiffHelper::readString( rStrm ).trim();
+ if( !aValue.isEmpty() )
+ maValue <<= aValue;
+ }
+ break;
+ case BIFF_FILTER_DATATYPE_BOOLEAN:
+ maValue <<= (rStrm.readuInt8() != 0);
+ rStrm.skip( 7 );
+ break;
+ case BIFF_FILTER_DATATYPE_EMPTY:
+ rStrm.skip( 8 );
+ if( mnOperator == XML_equal )
+ maValue <<= OUString();
+ break;
+ case BIFF_FILTER_DATATYPE_NOTEMPTY:
+ rStrm.skip( 8 );
+ if( mnOperator == XML_notEqual )
+ maValue <<= OUString();
+ break;
+ default:
+ OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" );
+ rStrm.skip( 8 );
+ }
+}
+
+CustomFilter::CustomFilter( const WorkbookHelper& rHelper ) :
+ FilterSettingsBase( rHelper ),
+ mbAnd( false )
+{
+}
+
+void CustomFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( nElement )
+ {
+ case XLS_TOKEN( customFilters ):
+ mbAnd = rAttribs.getBool( XML_and, false );
+ break;
+
+ case XLS_TOKEN( customFilter ):
+ {
+ FilterCriterionModel aCriterion;
+ aCriterion.mnOperator = rAttribs.getToken( XML_operator, XML_equal );
+ OUString aValue = rAttribs.getXString( XML_val, OUString() ).trim();
+ if( (aCriterion.mnOperator == XML_equal) || (aCriterion.mnOperator == XML_notEqual) || (!aValue.isEmpty()) )
+ aCriterion.maValue <<= aValue;
+ appendCriterion( aCriterion );
+ }
+ break;
+ }
+}
+
+void CustomFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( nRecId )
+ {
+ case BIFF12_ID_CUSTOMFILTERS:
+ mbAnd = rStrm.readInt32() == 0;
+ break;
+
+ case BIFF12_ID_CUSTOMFILTER:
+ {
+ FilterCriterionModel aCriterion;
+ aCriterion.readBiffData( rStrm );
+ appendCriterion( aCriterion );
+ }
+ break;
+ }
+}
+
+ApiFilterSettings CustomFilter::finalizeImport()
+{
+ ApiFilterSettings aSettings;
+ OSL_ENSURE( maCriteria.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" );
+ for( const auto& rCriterion : maCriteria )
+ {
+ // first extract the filter operator
+ sal_Int32 nOperator = 0;
+ bool bValidOperator = lclGetApiOperatorFromToken( nOperator, rCriterion.mnOperator );
+ if( bValidOperator )
+ {
+ if( rCriterion.maValue.has< OUString >() )
+ {
+ // string argument
+ OUString aValue;
+ rCriterion.maValue >>= aValue;
+ // check for 'empty', 'contains', 'begins with', or 'ends with' text filters
+ bool bEqual = nOperator == FilterOperator2::EQUAL;
+ bool bNotEqual = nOperator == FilterOperator2::NOT_EQUAL;
+ if( bEqual || bNotEqual )
+ {
+ if( aValue.isEmpty() )
+ {
+ // empty comparison string: create empty/not empty filters
+ nOperator = bNotEqual ? FilterOperator2::NOT_EMPTY : FilterOperator2::EMPTY;
+ }
+ else
+ {
+ // compare to something: try to find begins/ends/contains
+ bool bHasLeadingAsterisk = lclTrimLeadingAsterisks( aValue );
+ bool bHasTrailingAsterisk = lclTrimTrailingAsterisks( aValue );
+ // just '***' matches everything, do not create a filter field
+ bValidOperator = !aValue.isEmpty();
+ if( bValidOperator )
+ {
+ if( bHasLeadingAsterisk && bHasTrailingAsterisk )
+ nOperator = bNotEqual ? FilterOperator2::DOES_NOT_CONTAIN : FilterOperator2::CONTAINS;
+ else if( bHasLeadingAsterisk )
+ nOperator = bNotEqual ? FilterOperator2::DOES_NOT_END_WITH : FilterOperator2::ENDS_WITH;
+ else if( bHasTrailingAsterisk )
+ nOperator = bNotEqual ? FilterOperator2::DOES_NOT_BEGIN_WITH : FilterOperator2::BEGINS_WITH;
+ // else: no asterisks, stick to equal/not equal
+ }
+ }
+ }
+
+ if( bValidOperator )
+ {
+ // if wildcards are present, require RE mode, otherwise keep don't care state
+ if( lclConvertWildcardsToRegExp( aValue ) )
+ aSettings.mobNeedsRegExp = true;
+ // create a new UNO API filter field
+ aSettings.appendField( mbAnd, nOperator, aValue );
+ }
+ }
+ else if( rCriterion.maValue.has< double >() )
+ {
+ // floating-point argument
+ double fValue = 0.0;
+ rCriterion.maValue >>= fValue;
+ aSettings.appendField( mbAnd, nOperator, fValue );
+ }
+ }
+ }
+ return aSettings;
+}
+
+void CustomFilter::appendCriterion( const FilterCriterionModel& rCriterion )
+{
+ if( (rCriterion.mnOperator != XML_TOKEN_INVALID) && rCriterion.maValue.hasValue() )
+ maCriteria.push_back( rCriterion );
+}
+
+FilterColumn::FilterColumn( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnColId( -1 ),
+ mbHiddenButton( false ),
+ mbShowButton( true )
+{
+}
+
+void FilterColumn::importFilterColumn( const AttributeList& rAttribs )
+{
+ mnColId = rAttribs.getInteger( XML_colId, -1 );
+ mbHiddenButton = rAttribs.getBool( XML_hiddenButton, false );
+ mbShowButton = rAttribs.getBool( XML_showButton, true );
+}
+
+void FilterColumn::importFilterColumn( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ mnColId = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ mbHiddenButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_HIDDENBUTTON );
+ mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON );
+}
+
+ApiFilterSettings FilterColumn::finalizeImport()
+{
+ ApiFilterSettings aSettings;
+ if( (0 <= mnColId) && mxSettings )
+ {
+ // filter settings object creates a sequence of filter fields
+ aSettings = mxSettings->finalizeImport();
+ // add column index to all filter fields
+ for( auto& rFilterField : aSettings.maFilterFields )
+ rFilterField.Field = mnColId;
+ }
+ return aSettings;
+}
+
+// SortCondition
+
+SortCondition::SortCondition( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbDescending( false )
+{
+}
+
+void SortCondition::importSortCondition( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
+ AddressConverter::convertToCellRangeUnchecked( maRange, aRangeStr, nSheet );
+
+ maSortCustomList = rAttribs.getString( XML_customList, OUString() );
+ mbDescending = rAttribs.getBool( XML_descending, false );
+}
+
+// AutoFilter
+
+AutoFilter::AutoFilter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void AutoFilter::importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
+ AddressConverter::convertToCellRangeUnchecked( maRange, aRangeStr, nSheet );
+}
+
+void AutoFilter::importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ AddressConverter::convertToCellRangeUnchecked( maRange, aBinRange, nSheet );
+}
+
+void AutoFilter::importSortState( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
+ AddressConverter::convertToCellRangeUnchecked( maSortRange, aRangeStr, nSheet );
+}
+
+FilterColumn& AutoFilter::createFilterColumn()
+{
+ FilterColumnVector::value_type xFilterColumn = std::make_shared<FilterColumn>( *this );
+ maFilterColumns.push_back( xFilterColumn );
+ return *xFilterColumn;
+}
+
+SortCondition& AutoFilter::createSortCondition()
+{
+ SortConditionVector::value_type xSortCondition = std::make_shared<SortCondition>( *this );
+ maSortConditions.push_back( xSortCondition );
+ return *xSortCondition;
+}
+
+void AutoFilter::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange, sal_Int16 nSheet )
+{
+ // convert filter settings using the filter descriptor of the database range
+ const Reference<XSheetFilterDescriptor3> xFilterDesc( rxDatabaseRange->getFilterDescriptor(), UNO_QUERY_THROW );
+ if( !xFilterDesc.is() )
+ return;
+
+ // set some common properties for the auto filter range
+ PropertySet aDescProps( xFilterDesc );
+ aDescProps.setProperty( PROP_IsCaseSensitive, false );
+ aDescProps.setProperty( PROP_SkipDuplicates, false );
+ aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
+ aDescProps.setProperty( PROP_ContainsHeader, true );
+ aDescProps.setProperty( PROP_CopyOutputData, false );
+
+ // resulting list of all UNO API filter fields
+ ::std::vector<TableFilterField3> aFilterFields;
+
+ // track if columns require to enable or disable regular expressions
+ OptValue< bool > obNeedsRegExp;
+
+ /* Track whether the filter fields of the first filter column are
+ connected with 'or'. In this case, other filter fields cannot be
+ inserted without altering the result of the entire filter, due to
+ Calc's precedence for the 'and' connection operator. Example:
+ Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
+ B2 belong to filter column B, will be evaluated by Calc as
+ '(A1 and B1) or (B2 and C1)'. */
+ bool bHasOrConnection = false;
+
+ // process all filter column objects, exit when 'or' connection exists
+ for( const auto& rxFilterColumn : maFilterColumns )
+ {
+ // the filter settings object creates a list of filter fields
+ ApiFilterSettings aSettings = rxFilterColumn->finalizeImport();
+ ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;
+
+ /* Check whether mode for regular expressions is compatible with
+ the global mode in obNeedsRegExp. If either one is still in
+ don't-care state, all is fine. If both are set, they must be
+ equal. */
+ bool bRegExpCompatible = !obNeedsRegExp || !aSettings.mobNeedsRegExp || (obNeedsRegExp.get() == aSettings.mobNeedsRegExp.get());
+
+ // check whether fields are connected by 'or' (see comments above).
+ if( rColumnFields.size() >= 2 )
+ bHasOrConnection = std::any_of(rColumnFields.begin() + 1, rColumnFields.end(),
+ [](const css::sheet::TableFilterField3& rColumnField) { return rColumnField.Connection == FilterConnection_OR; });
+
+ /* Skip the column filter, if no filter fields have been created,
+ and if the mode for regular expressions of the
+ filter column does not fit. */
+ if( !rColumnFields.empty() && bRegExpCompatible )
+ {
+ /* Add 'and' connection to the first filter field to connect
+ it to the existing filter fields of other columns. */
+ rColumnFields[ 0 ].Connection = FilterConnection_AND;
+
+ // insert the new filter fields
+ aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() );
+
+ // update the regular expressions mode
+ obNeedsRegExp.assignIfUsed( aSettings.mobNeedsRegExp );
+ }
+
+ if( bHasOrConnection )
+ break;
+ }
+
+ // insert all filter fields to the filter descriptor
+ if( !aFilterFields.empty() )
+ xFilterDesc->setFilterFields3( comphelper::containerToSequence( aFilterFields ) );
+
+ // regular expressions
+ bool bUseRegExp = obNeedsRegExp.get( false );
+ aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp );
+
+ // sort
+ if (maSortConditions.empty())
+ return;
+
+ const SortConditionVector::value_type& xSortConditionPointer = *maSortConditions.begin();
+ const SortCondition& rSorConditionLoaded = *xSortConditionPointer;
+
+ ScSortParam aParam;
+ aParam.bUserDef = false;
+ aParam.nUserIndex = 0;
+ aParam.bByRow = false;
+
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ if (!rSorConditionLoaded.maSortCustomList.isEmpty())
+ {
+ for (size_t i=0; pUserList && i < pUserList->size(); i++)
+ {
+ const OUString aEntry((*pUserList)[i].GetString());
+ if (aEntry.equalsIgnoreAsciiCase(rSorConditionLoaded.maSortCustomList))
+ {
+ aParam.bUserDef = true;
+ aParam.nUserIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (!aParam.bUserDef)
+ {
+ pUserList->push_back(new ScUserListData(rSorConditionLoaded.maSortCustomList));
+ aParam.bUserDef = true;
+ aParam.nUserIndex = pUserList->size()-1;
+ }
+
+ // set sort parameter if we have detected it
+ if (!aParam.bUserDef)
+ return;
+
+ SCCOLROW nStartPos = aParam.bByRow ? maRange.aStart.Col() : maRange.aStart.Row();
+ if (rSorConditionLoaded.mbDescending)
+ {
+ // descending sort - need to enable 1st SortParam slot
+ assert(aParam.GetSortKeyCount() == DEFSORT);
+
+ aParam.maKeyState[0].bDoSort = true;
+ aParam.maKeyState[0].bAscending = false;
+ aParam.maKeyState[0].nField += nStartPos;
+ }
+
+ ScDocument& rDoc = getScDocument();
+ ScDBData* pDBData = rDoc.GetDBAtArea(
+ nSheet,
+ maRange.aStart.Col(), maRange.aStart.Row(),
+ maRange.aEnd.Col(), maRange.aEnd.Row());
+
+ if (pDBData)
+ pDBData->SetSortParam(aParam);
+ else
+ OSL_FAIL("AutoFilter::finalizeImport(): cannot find matching DBData");
+}
+
+AutoFilterBuffer::AutoFilterBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+AutoFilter& AutoFilterBuffer::createAutoFilter()
+{
+ AutoFilterVector::value_type xAutoFilter = std::make_shared<AutoFilter>( *this );
+ maAutoFilters.push_back( xAutoFilter );
+ return *xAutoFilter;
+}
+
+void AutoFilterBuffer::finalizeImport( sal_Int16 nSheet )
+{
+ // rely on existence of the defined name '_FilterDatabase' containing the range address of the filtered area
+ const DefinedName* pFilterDBName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_FILTERDATABASE, nSheet ).get();
+ if(!pFilterDBName)
+ return;
+
+ ScRange aFilterRange;
+ if( !(pFilterDBName->getAbsoluteRange( aFilterRange ) && (aFilterRange.aStart.Tab() == nSheet)) )
+ return;
+
+ // use the same name for the database range as used for the defined name '_FilterDatabase'
+ Reference< XDatabaseRange > xDatabaseRange = createUnnamedDatabaseRangeObject( aFilterRange );
+ // first, try to create an auto filter
+ bool bHasAutoFilter = finalizeImport( xDatabaseRange, nSheet );
+ // no success: try to create an advanced filter
+ if( bHasAutoFilter || !xDatabaseRange.is() )
+ return;
+
+ // the built-in defined name 'Criteria' must exist
+ const DefinedName* pCriteriaName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_CRITERIA, nSheet ).get();
+ if( !pCriteriaName )
+ return;
+
+ ScRange aCriteriaRange;
+ if( !pCriteriaName->getAbsoluteRange( aCriteriaRange ) )
+ return;
+
+ // set some common properties for the filter descriptor
+ PropertySet aDescProps( xDatabaseRange->getFilterDescriptor() );
+ aDescProps.setProperty( PROP_IsCaseSensitive, false );
+ aDescProps.setProperty( PROP_SkipDuplicates, false );
+ aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
+ aDescProps.setProperty( PROP_ContainsHeader, true );
+ // criteria range may contain wildcards, but these are incompatible with REs
+ aDescProps.setProperty( PROP_UseRegularExpressions, false );
+
+ // position of output data (if built-in defined name 'Extract' exists)
+ DefinedNameRef xExtractName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_EXTRACT, nSheet );
+ ScRange aOutputRange;
+ bool bHasOutputRange = xExtractName && xExtractName->getAbsoluteRange( aOutputRange );
+ aDescProps.setProperty( PROP_CopyOutputData, bHasOutputRange );
+ if( bHasOutputRange )
+ {
+ aDescProps.setProperty( PROP_SaveOutputPosition, true );
+ aDescProps.setProperty( PROP_OutputPosition, CellAddress( aOutputRange.aStart.Tab(), aOutputRange.aStart.Col(), aOutputRange.aStart.Row() ) );
+ }
+
+ /* Properties of the database range (must be set after
+ modifying properties of the filter descriptor,
+ otherwise the 'FilterCriteriaSource' property gets
+ deleted). */
+ PropertySet aRangeProps( xDatabaseRange );
+ aRangeProps.setProperty( PROP_AutoFilter, false );
+ aRangeProps.setProperty( PROP_FilterCriteriaSource,
+ CellRangeAddress( aCriteriaRange.aStart.Tab(),
+ aCriteriaRange.aStart.Col(), aCriteriaRange.aStart.Row(),
+ aCriteriaRange.aEnd.Col(), aCriteriaRange.aEnd.Row() ));
+}
+
+bool AutoFilterBuffer::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange, sal_Int16 nSheet )
+{
+ AutoFilter* pAutoFilter = getActiveAutoFilter();
+ if( pAutoFilter && rxDatabaseRange.is() ) try
+ {
+ // the property 'AutoFilter' enables the drop-down buttons
+ PropertySet aRangeProps( rxDatabaseRange );
+ aRangeProps.setProperty( PROP_AutoFilter, true );
+
+ pAutoFilter->finalizeImport( rxDatabaseRange, nSheet );
+
+ // return true to indicate enabled autofilter
+ return true;
+ }
+ catch( Exception& )
+ {
+ }
+ return false;
+}
+
+AutoFilter* AutoFilterBuffer::getActiveAutoFilter()
+{
+ // Excel expects not more than one auto filter per sheet or table
+ OSL_ENSURE( maAutoFilters.size() <= 1, "AutoFilterBuffer::getActiveAutoFilter - too many auto filters" );
+ // stick to the last imported auto filter
+ return maAutoFilters.empty() ? nullptr : maAutoFilters.back().get();
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/autofiltercontext.cxx b/sc/source/filter/oox/autofiltercontext.cxx
new file mode 100644
index 000000000..9cdbacea4
--- /dev/null
+++ b/sc/source/filter/oox/autofiltercontext.cxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <autofiltercontext.hxx>
+#include <biffhelper.hxx>
+
+#include <autofilterbuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+FilterSettingsContext::FilterSettingsContext( WorksheetContextBase& rParent, FilterSettingsBase& rFilterSettings ) :
+ WorksheetContextBase( rParent ),
+ mrFilterSettings( rFilterSettings )
+{
+}
+
+ContextHandlerRef FilterSettingsContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( filters ):
+ if( nElement == XLS_TOKEN( filter ) || nElement == XLS_TOKEN( dateGroupItem )) return this;
+ break;
+ case XLS_TOKEN( customFilters ):
+ if( nElement == XLS_TOKEN( customFilter ) ) return this;
+ break;
+ case XLS_TOKEN( colorFilter ):
+ if( nElement == XLS_TOKEN( colorFilter ) ) return this;
+ break;
+ }
+ return nullptr;
+}
+
+void FilterSettingsContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrFilterSettings.importAttribs( getCurrentElement(), rAttribs );
+}
+
+ContextHandlerRef FilterSettingsContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& /*rStrm*/ )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_DISCRETEFILTERS:
+ if( nRecId == BIFF12_ID_DISCRETEFILTER ) return this;
+ break;
+ case BIFF12_ID_CUSTOMFILTERS:
+ if( nRecId == BIFF12_ID_CUSTOMFILTER ) return this;
+ break;
+ }
+ return nullptr;
+}
+
+void FilterSettingsContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrFilterSettings.importRecord( getCurrentElement(), rStrm );
+}
+
+FilterColumnContext::FilterColumnContext( WorksheetContextBase& rParent, FilterColumn& rFilterColumn ) :
+ WorksheetContextBase( rParent ),
+ mrFilterColumn( rFilterColumn )
+{
+}
+
+ContextHandlerRef FilterColumnContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ if( getCurrentElement() == XLS_TOKEN( filterColumn ) ) switch( nElement )
+ {
+ case XLS_TOKEN( filters ):
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< DiscreteFilter >() );
+ case XLS_TOKEN( top10 ):
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< Top10Filter >() );
+ case XLS_TOKEN( customFilters ):
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< CustomFilter >() );
+ case XLS_TOKEN( colorFilter ):
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< ColorFilter >() );
+ }
+ return nullptr;
+}
+
+void FilterColumnContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrFilterColumn.importFilterColumn( rAttribs );
+}
+
+ContextHandlerRef FilterColumnContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& /*rStrm*/ )
+{
+ if( getCurrentElement() == BIFF12_ID_FILTERCOLUMN ) switch( nRecId )
+ {
+ case BIFF12_ID_DISCRETEFILTERS:
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< DiscreteFilter >() );
+ case BIFF12_ID_TOP10FILTER:
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< Top10Filter >() );
+ case BIFF12_ID_CUSTOMFILTERS:
+ return new FilterSettingsContext( *this, mrFilterColumn.createFilterSettings< CustomFilter >() );
+ }
+ return nullptr;
+}
+
+void FilterColumnContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrFilterColumn.importFilterColumn( rStrm );
+}
+
+// class SortConditionContext
+
+SortConditionContext::SortConditionContext( WorksheetContextBase& rParent, SortCondition& rSortCondition ) :
+ WorksheetContextBase( rParent ),
+ mrSortCondition( rSortCondition )
+{
+}
+
+ContextHandlerRef SortConditionContext::onCreateContext( sal_Int32 , const AttributeList& )
+{
+ return nullptr;
+}
+
+void SortConditionContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrSortCondition.importSortCondition( rAttribs, getSheetIndex() );
+}
+
+ContextHandlerRef SortConditionContext::onCreateRecordContext( sal_Int32 , SequenceInputStream& )
+{
+ return nullptr;
+}
+
+void SortConditionContext::onStartRecord( SequenceInputStream& )
+{
+}
+
+// class SortStateContext
+
+SortStateContext::SortStateContext( WorksheetContextBase& rParent, AutoFilter& rAutoFilter ) :
+ WorksheetContextBase( rParent ),
+ mrAutoFilter( rAutoFilter )
+{
+}
+
+ContextHandlerRef SortStateContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ if( getCurrentElement() == XLS_TOKEN( sortState ) ) switch( nElement )
+ {
+ case XLS_TOKEN( sortCondition ):
+ return new SortConditionContext( *this, mrAutoFilter.createSortCondition() );
+ }
+ return nullptr;
+}
+
+void SortStateContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrAutoFilter.importSortState( rAttribs, getSheetIndex() );
+}
+
+ContextHandlerRef SortStateContext::onCreateRecordContext( sal_Int32 , SequenceInputStream& )
+{
+ return nullptr;
+}
+
+void SortStateContext::onStartRecord( SequenceInputStream& )
+{
+}
+
+AutoFilterContext::AutoFilterContext( WorksheetFragmentBase& rFragment, AutoFilter& rAutoFilter ) :
+ WorksheetContextBase( rFragment ),
+ mrAutoFilter( rAutoFilter )
+{
+}
+
+ContextHandlerRef AutoFilterContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ if( getCurrentElement() == XLS_TOKEN( autoFilter ) ) switch( nElement )
+ {
+ case XLS_TOKEN( sortState ):
+ return new SortStateContext( *this, mrAutoFilter );
+ case XLS_TOKEN( filterColumn ):
+ return new FilterColumnContext( *this, mrAutoFilter.createFilterColumn() );
+ }
+ return nullptr;
+}
+
+void AutoFilterContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrAutoFilter.importAutoFilter( rAttribs, getSheetIndex() );
+}
+
+ContextHandlerRef AutoFilterContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& /*rStrm*/ )
+{
+ if( (getCurrentElement() == BIFF12_ID_AUTOFILTER) && (nRecId == BIFF12_ID_FILTERCOLUMN) )
+ return new FilterColumnContext( *this, mrAutoFilter.createFilterColumn() );
+ return nullptr;
+}
+
+void AutoFilterContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrAutoFilter.importAutoFilter( rStrm, getSheetIndex() );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/biffhelper.cxx b/sc/source/filter/oox/biffhelper.cxx
new file mode 100644
index 000000000..20a088fc7
--- /dev/null
+++ b/sc/source/filter/oox/biffhelper.cxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <biffhelper.hxx>
+
+#include <osl/diagnose.h>
+#include <oox/helper/binaryinputstream.hxx>
+
+#include <limits>
+
+namespace oox::xls {
+
+namespace {
+
+const sal_Int32 BIFF_RK_100FLAG = 0x00000001;
+const sal_Int32 BIFF_RK_INTFLAG = 0x00000002;
+const sal_Int32 BIFF_RK_VALUEMASK = 0xFFFFFFFC;
+
+} // namespace
+
+// conversion -----------------------------------------------------------------
+
+/*static*/ double BiffHelper::calcDoubleFromRk( sal_Int32 nRkValue )
+{
+ sal_math_Double aMathDouble{};
+ if( getFlag( nRkValue, BIFF_RK_INTFLAG ) )
+ {
+ sal_Int32 nTemp = nRkValue >> 2;
+ setFlag< sal_Int32 >( nTemp, 0xE0000000, nRkValue < 0 );
+ aMathDouble.value = nTemp;
+ }
+ else
+ {
+ aMathDouble.w32_parts.msw = static_cast< sal_uInt32 >( nRkValue & BIFF_RK_VALUEMASK );
+ }
+ if( getFlag( nRkValue, BIFF_RK_100FLAG ) )
+ aMathDouble.value /= 100.0;
+
+ return aMathDouble.value;
+}
+/*static*/ double BiffHelper::calcDoubleFromError( sal_uInt8 nErrorCode )
+{
+ sal_uInt16 nApiError = 0x7FFF;
+ switch( nErrorCode )
+ {
+ case BIFF_ERR_NULL: nApiError = 521; break;
+ case BIFF_ERR_DIV0: nApiError = 532; break;
+ case BIFF_ERR_VALUE: nApiError = 519; break;
+ case BIFF_ERR_REF: nApiError = 524; break;
+ case BIFF_ERR_NAME: nApiError = 525; break;
+ case BIFF_ERR_NUM: nApiError = 503; break;
+ case BIFF_ERR_NA: nApiError = 0x7FFF; break;
+ default: OSL_FAIL( "BiffHelper::calcDoubleFromError - unknown error code" );
+ }
+ sal_math_Double aMathDouble;
+ aMathDouble.value = std::numeric_limits<double>::quiet_NaN();
+ aMathDouble.nan_parts.fraction_lo = nApiError;
+ return aMathDouble.value;
+}
+
+// BIFF12 import --------------------------------------------------------------
+
+/*static*/ OUString BiffHelper::readString( SequenceInputStream& rStrm, bool b32BitLen )
+{
+ OUString aString;
+ if( !rStrm.isEof() )
+ {
+ sal_Int32 nCharCount = b32BitLen ? rStrm.readValue< sal_Int32 >() : rStrm.readValue< sal_Int16 >();
+ // string length -1 is often used to indicate a missing string
+ OSL_ENSURE( !rStrm.isEof() && (nCharCount >= -1), "BiffHelper::readString - invalid string length" );
+ if( !rStrm.isEof() && (nCharCount > 0) )
+ {
+ // SequenceInputStream always supports getRemaining()
+ nCharCount = ::std::min( nCharCount, static_cast< sal_Int32 >( rStrm.getRemaining() / 2 ) );
+ aString = rStrm.readUnicodeArray( nCharCount );
+ }
+ }
+ return aString;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/chartsheetfragment.cxx b/sc/source/filter/oox/chartsheetfragment.cxx
new file mode 100644
index 000000000..10fde3205
--- /dev/null
+++ b/sc/source/filter/oox/chartsheetfragment.cxx
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <chartsheetfragment.hxx>
+
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <pagesettings.hxx>
+#include <viewsettings.hxx>
+#include <worksheetsettings.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+ChartsheetFragment::ChartsheetFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef ChartsheetFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( chartsheet ) ) return this;
+ break;
+
+ case XLS_TOKEN( chartsheet ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheetViews ): return this;
+
+ case XLS_TOKEN( sheetPr ): getWorksheetSettings().importChartSheetPr( rAttribs ); return this;
+ case XLS_TOKEN( sheetProtection ): getWorksheetSettings().importChartProtection( rAttribs ); break;
+ case XLS_TOKEN( pageMargins ): getPageSettings().importPageMargins( rAttribs ); break;
+ case XLS_TOKEN( pageSetup ): getPageSettings().importChartPageSetup( getRelations(), rAttribs ); break;
+ case XLS_TOKEN( headerFooter ): getPageSettings().importHeaderFooter( rAttribs ); return this;
+ case XLS_TOKEN( picture ): getPageSettings().importPicture( getRelations(), rAttribs ); break;
+ case XLS_TOKEN( drawing ): importDrawing( rAttribs ); break;
+ }
+ break;
+
+ case XLS_TOKEN( sheetViews ):
+ if( nElement == XLS_TOKEN( sheetView ) ) getSheetViewSettings().importChartSheetView( rAttribs );
+ break;
+
+ case XLS_TOKEN( headerFooter ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( firstHeader ):
+ case XLS_TOKEN( firstFooter ):
+ case XLS_TOKEN( oddHeader ):
+ case XLS_TOKEN( oddFooter ):
+ case XLS_TOKEN( evenHeader ):
+ case XLS_TOKEN( evenFooter ): return this; // collect contents in onCharacters()
+ }
+ break;
+
+ case XLS_TOKEN( sheetPr ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( tabColor ): getWorksheetSettings().importTabColor( rAttribs ); break;
+ }
+ break;
+
+ }
+ return nullptr;
+}
+
+void ChartsheetFragment::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( firstHeader ):
+ case XLS_TOKEN( firstFooter ):
+ case XLS_TOKEN( oddHeader ):
+ case XLS_TOKEN( oddFooter ):
+ case XLS_TOKEN( evenHeader ):
+ case XLS_TOKEN( evenFooter ):
+ getPageSettings().importHeaderFooterCharacters( rChars, getCurrentElement() );
+ break;
+ }
+}
+
+ContextHandlerRef ChartsheetFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_WORKSHEET ) return this;
+ break;
+
+ case BIFF12_ID_WORKSHEET:
+ switch( nRecId )
+ {
+ case BIFF12_ID_CHARTSHEETVIEWS: return this;
+
+ case BIFF12_ID_CHARTSHEETPR: getWorksheetSettings().importChartSheetPr( rStrm ); break;
+ case BIFF12_ID_CHARTPROTECTION: getWorksheetSettings().importChartProtection( rStrm ); break;
+ case BIFF12_ID_PAGEMARGINS: getPageSettings().importPageMargins( rStrm ); break;
+ case BIFF12_ID_CHARTPAGESETUP: getPageSettings().importChartPageSetup( getRelations(), rStrm ); break;
+ case BIFF12_ID_HEADERFOOTER: getPageSettings().importHeaderFooter( rStrm ); break;
+ case BIFF12_ID_PICTURE: getPageSettings().importPicture( getRelations(), rStrm ); break;
+ case BIFF12_ID_DRAWING: importDrawing( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_CHARTSHEETVIEWS:
+ if( nRecId == BIFF12_ID_CHARTSHEETVIEW ) getSheetViewSettings().importChartSheetView( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* ChartsheetFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_CHARTSHEETVIEW, BIFF12_ID_CHARTSHEETVIEW + 1 },
+ { BIFF12_ID_CHARTSHEETVIEWS, BIFF12_ID_CHARTSHEETVIEWS + 1 },
+ { BIFF12_ID_CUSTOMCHARTVIEW, BIFF12_ID_CUSTOMCHARTVIEW + 1 },
+ { BIFF12_ID_CUSTOMCHARTVIEWS, BIFF12_ID_CUSTOMCHARTVIEWS + 1 },
+ { BIFF12_ID_HEADERFOOTER, BIFF12_ID_HEADERFOOTER + 1 },
+ { BIFF12_ID_WORKSHEET, BIFF12_ID_WORKSHEET + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void ChartsheetFragment::initializeImport()
+{
+ // initial processing in base class WorksheetHelper
+ initializeWorksheetImport();
+}
+
+void ChartsheetFragment::finalizeImport()
+{
+ // final processing in base class WorksheetHelper
+ finalizeWorksheetImport();
+}
+
+// private --------------------------------------------------------------------
+
+void ChartsheetFragment::importDrawing( const AttributeList& rAttribs )
+{
+ setDrawingPath( getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ) );
+}
+
+void ChartsheetFragment::importDrawing( SequenceInputStream& rStrm )
+{
+ setDrawingPath( getFragmentPathFromRelId( BiffHelper::readString( rStrm ) ) );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/commentsbuffer.cxx b/sc/source/filter/oox/commentsbuffer.cxx
new file mode 100644
index 000000000..b4e6c7f48
--- /dev/null
+++ b/sc/source/filter/oox/commentsbuffer.cxx
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <oox/token/tokens.hxx>
+
+#include <commentsbuffer.hxx>
+
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <osl/diagnose.h>
+#include <oox/helper/attributelist.hxx>
+#include <oox/vml/vmlshape.hxx>
+#include <addressconverter.hxx>
+#include <drawingfragment.hxx>
+#include <svx/sdtaitm.hxx>
+#include <svx/svdocapt.hxx>
+#include <tools/diagnose_ex.h>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <cellsuno.hxx>
+#include <docfunc.hxx>
+#include <docuno.hxx>
+#include <docsh.hxx>
+#include <postit.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+
+static sal_Int32 lcl_ToHorizAlign( sal_Int32 nAlign )
+{
+ switch( nAlign )
+ {
+ case XML_left:
+ return SDRTEXTHORZADJUST_LEFT;
+ case XML_right:
+ return SDRTEXTHORZADJUST_RIGHT;
+ case XML_center:
+ return SDRTEXTHORZADJUST_CENTER;
+ default:
+ return SDRTEXTHORZADJUST_BLOCK;
+ }
+}
+
+static sal_Int32 lcl_ToVertAlign( sal_Int32 nAlign )
+{
+ switch( nAlign )
+ {
+ case XML_top:
+ case XML_Top:
+ return SDRTEXTVERTADJUST_TOP;
+ case XML_center:
+ case XML_Center:
+ return SDRTEXTVERTADJUST_CENTER;
+ case XML_bottom:
+ case XML_Bottom:
+ return SDRTEXTVERTADJUST_BOTTOM;
+ default:
+ return SDRTEXTVERTADJUST_BLOCK;
+ }
+}
+
+static sal_Int16 lcl_ToParaAlign(sal_Int32 nAlign)
+{
+ switch ( nAlign )
+ {
+ case XML_Left:
+ return sal_Int16(css::style::ParagraphAdjust_LEFT);
+ case XML_Right:
+ return sal_Int16(css::style::ParagraphAdjust_RIGHT);
+ case XML_Center:
+ return sal_Int16(css::style::ParagraphAdjust_CENTER);
+ default:
+ return sal_Int16(css::style::ParagraphAdjust_BLOCK);
+ }
+}
+
+CommentModel::CommentModel()
+ : mnAuthorId(-1)
+ , mbAutoFill(false)
+ , mbAutoScale(false)
+ , mbColHidden(false)
+ , mbLocked(false)
+ , mbRowHidden(false)
+ , mnTHA(0)
+ , mnTVA(0)
+{
+}
+
+Comment::Comment( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void Comment::importComment( const AttributeList& rAttribs )
+{
+ maModel.mnAuthorId = rAttribs.getInteger( XML_authorId, -1 );
+ // cell range will be checked while inserting the comment into the document
+ AddressConverter::convertToCellRangeUnchecked( maModel.maRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex() );
+}
+
+void Comment::importCommentPr( const AttributeList& rAttribs )
+{
+ maModel.mbAutoFill = rAttribs.getBool( XML_autoFill, true );
+ maModel.mbAutoScale = rAttribs.getBool( XML_autoScale, false );
+ maModel.mbColHidden = rAttribs.getBool( XML_colHidden, false );
+ maModel.mbLocked = rAttribs.getBool( XML_locked, false );
+ maModel.mbRowHidden = rAttribs.getBool( XML_rowHidden, false );
+ maModel.mnTHA = rAttribs.getToken( XML_textHAlign, XML_left );
+ maModel.mnTVA = rAttribs.getToken( XML_textVAlign, XML_top );
+}
+
+void Comment::importComment( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ maModel.mnAuthorId = rStrm.readInt32();
+ rStrm >> aBinRange;
+ // cell range will be checked while inserting the comment into the document
+ AddressConverter::convertToCellRangeUnchecked( maModel.maRange, aBinRange, getSheetIndex() );
+}
+
+RichStringRef const & Comment::createText()
+{
+ maModel.mxText = std::make_shared<RichString>( *this );
+ return maModel.mxText;
+}
+
+void Comment::finalizeImport()
+{
+ // BIFF12 stores cell range instead of cell address, use first cell of this range
+ OSL_ENSURE( maModel.maRange.aStart == maModel.maRange.aEnd,
+ "Comment::finalizeImport - comment anchor should be a single cell" );
+ if( !getAddressConverter().checkCellAddress( maModel.maRange.aStart, true ) || !maModel.mxText )
+ return;
+
+ try
+ {
+ ScTableSheetObj* pAnnosSupp = static_cast<ScTableSheetObj*>(getSheet().get());
+ rtl::Reference<ScAnnotationsObj> xAnnos = static_cast<ScAnnotationsObj*>(pAnnosSupp->getAnnotations().get());
+ ScDocShell* pDocShell = xAnnos->GetDocShell();
+ // non-empty string required by note implementation (real text will be added below)
+ ScPostIt* pPostIt = pDocShell->GetDocFunc().ImportNote( maModel.maRange.aStart, OUString( ' ' ) );
+ SdrCaptionObj* pCaption = pPostIt->GetOrCreateCaption( maModel.maRange.aStart );
+
+ Reference< XShape > xAnnoShape( pCaption->getUnoShape() ); // SvxShapeText
+ // setting a property triggers expensive process, so set them all at once
+ Reference< css::beans::XMultiPropertySet > xAnnoShapeMultiPropSet(xAnnoShape, UNO_QUERY_THROW);
+
+ // Add shape formatting properties (autoFill, colHidden and rowHidden are dropped)
+ xAnnoShapeMultiPropSet->setPropertyValues(
+ Sequence<OUString> { "TextFitToSize", "MoveProtect", "TextHorizontalAdjust", "TextVerticalAdjust" },
+ Sequence<Any> { Any(maModel.mbAutoScale), Any(maModel.mbLocked),
+ Any(lcl_ToHorizAlign( maModel.mnTHA )), Any(lcl_ToVertAlign( maModel.mnTVA )) });
+ if( maModel.maAnchor.Width > 0 && maModel.maAnchor.Height > 0 )
+ {
+ xAnnoShape->setPosition( css::awt::Point( maModel.maAnchor.X, maModel.maAnchor.Y ) );
+ xAnnoShape->setSize( css::awt::Size( maModel.maAnchor.Width, maModel.maAnchor.Height ) );
+ }
+
+ // convert shape formatting and visibility
+ bool bVisible = true;
+ if( const ::oox::vml::ShapeBase* pVmlNoteShape = getVmlDrawing().getNoteShape( maModel.maRange.aStart ) )
+ {
+ // position and formatting
+ pVmlNoteShape->convertFormatting( xAnnoShape );
+ // visibility
+ bVisible = pVmlNoteShape->getTypeModel().mbVisible;
+
+ // Setting comment text alignment
+ const ::oox::vml::ClientData* xClientData = pVmlNoteShape->getClientData();
+ xAnnoShapeMultiPropSet->setPropertyValues(
+ Sequence<OUString> { "TextVerticalAdjust", "ParaAdjust" },
+ Sequence<Any> { Any(lcl_ToVertAlign( xClientData->mnTextVAlign )), Any(lcl_ToParaAlign( xClientData->mnTextHAlign )) });
+ }
+ if (bVisible)
+ pDocShell->GetDocFunc().ShowNote( maModel.maRange.aStart, bVisible );
+
+ // insert text and convert text formatting
+ maModel.mxText->finalizeImport();
+ Reference< XText > xAnnoText( xAnnoShape, UNO_QUERY_THROW );
+ Reference< css::document::XActionLockable > xAnnoLock( xAnnoShape, UNO_QUERY_THROW );
+ xAnnoLock->addActionLock();
+ maModel.mxText->convert( xAnnoText );
+ xAnnoLock->removeActionLock();
+ }
+ catch( Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("sc");
+ }
+}
+
+// private --------------------------------------------------------------------
+
+CommentsBuffer::CommentsBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void CommentsBuffer::appendAuthor( const OUString& rAuthor )
+{
+ maAuthors.push_back( rAuthor );
+}
+
+CommentRef CommentsBuffer::createComment()
+{
+ CommentRef xComment = std::make_shared<Comment>( *this );
+ maComments.push_back( xComment );
+ return xComment;
+}
+
+void CommentsBuffer::finalizeImport()
+{
+ // keep the model locked to avoid repeated reformatting in the model
+ auto pModel = getScDocument().GetDrawLayer();
+ bool bWasLocked = pModel->isLocked();
+ pModel->setLock(true);
+ maComments.forEachMem( &Comment::finalizeImport );
+ pModel->setLock(bWasLocked);
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/commentsfragment.cxx b/sc/source/filter/oox/commentsfragment.cxx
new file mode 100644
index 000000000..0d6a8b27a
--- /dev/null
+++ b/sc/source/filter/oox/commentsfragment.cxx
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <commentsfragment.hxx>
+
+#include <biffhelper.hxx>
+#include <richstringcontext.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+CommentsFragment::CommentsFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef CommentsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( comments ) ) return this;
+ break;
+ case XLS_TOKEN( comments ):
+ if( nElement == XLS_TOKEN( authors ) ) return this;
+ if( nElement == XLS_TOKEN( commentList ) ) return this;
+ break;
+ case XLS_TOKEN( authors ):
+ if( nElement == XLS_TOKEN( author ) ) return this; // collect author in onCharacters()
+ break;
+ case XLS_TOKEN( commentList ):
+ if( nElement == XLS_TOKEN( comment ) ) { importComment( rAttribs ); return this; }
+ break;
+ case XLS_TOKEN( commentPr ):
+ if( nElement == XLS_TOKEN( anchor ) )
+ return this;
+ break;
+ case XLS_TOKEN( anchor ):
+ if( nElement == XDR_TOKEN( from ) || nElement == XDR_TOKEN( to ) )
+ return this;
+ break;
+ case XDR_TOKEN( from ):
+ case XDR_TOKEN( to ):
+ return this;
+ case XLS_TOKEN( comment ):
+ if( (nElement == XLS_TOKEN( text )) && mxComment )
+ return new RichStringContext( *this, mxComment->createText() );
+ if( nElement == XLS_TOKEN( commentPr ) ) { mxComment->importCommentPr( rAttribs ); return this; }
+ break;
+ }
+ return nullptr;
+}
+
+void CommentsFragment::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( author ) ) )
+ getComments().appendAuthor( rChars );
+}
+
+void CommentsFragment::onEndElement()
+{
+ if( isCurrentElement( XLS_TOKEN( comment ) ) )
+ mxComment.reset();
+}
+
+ContextHandlerRef CommentsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_COMMENTS ) return this;
+ break;
+ case BIFF12_ID_COMMENTS:
+ if( nRecId == BIFF12_ID_COMMENTAUTHORS ) return this;
+ if( nRecId == BIFF12_ID_COMMENTLIST ) return this;
+ break;
+ case BIFF12_ID_COMMENTAUTHORS:
+ if( nRecId == BIFF12_ID_COMMENTAUTHOR ) getComments().appendAuthor( BiffHelper::readString( rStrm ) );
+ break;
+ case BIFF12_ID_COMMENTLIST:
+ if( nRecId == BIFF12_ID_COMMENT ) { importComment( rStrm ); return this; }
+ break;
+ case BIFF12_ID_COMMENT:
+ if( (nRecId == BIFF12_ID_COMMENTTEXT) && mxComment )
+ mxComment->createText()->importString( rStrm, true );
+ break;
+ }
+ return nullptr;
+}
+
+void CommentsFragment::onEndRecord()
+{
+ if( isCurrentElement( BIFF12_ID_COMMENT ) )
+ mxComment.reset();
+}
+
+const RecordInfo* CommentsFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_COMMENT, BIFF12_ID_COMMENT + 1 },
+ { BIFF12_ID_COMMENTAUTHORS, BIFF12_ID_COMMENTAUTHORS + 1 },
+ { BIFF12_ID_COMMENTLIST, BIFF12_ID_COMMENTLIST + 1 },
+ { BIFF12_ID_COMMENTS, BIFF12_ID_COMMENTS + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+// private --------------------------------------------------------------------
+
+void CommentsFragment::importComment( const AttributeList& rAttribs )
+{
+ mxComment = getComments().createComment();
+ mxComment->importComment( rAttribs );
+}
+
+void CommentsFragment::importComment( SequenceInputStream& rStrm )
+{
+ mxComment = getComments().createComment();
+ mxComment->importComment( rStrm );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/condformatbuffer.cxx b/sc/source/filter/oox/condformatbuffer.cxx
new file mode 100644
index 000000000..dbe8f626f
--- /dev/null
+++ b/sc/source/filter/oox/condformatbuffer.cxx
@@ -0,0 +1,1399 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <unordered_set>
+#include <unordered_map>
+#include <condformatbuffer.hxx>
+#include <formulaparser.hxx>
+
+#include <com/sun/star/sheet/ConditionOperator2.hpp>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <stylesbuffer.hxx>
+#include <themebuffer.hxx>
+
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <document.hxx>
+#include <tokenarray.hxx>
+#include <tokenuno.hxx>
+#include <extlstcontext.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_Int32 BIFF12_CFRULE_TYPE_CELLIS = 1;
+const sal_Int32 BIFF12_CFRULE_TYPE_EXPRESSION = 2;
+const sal_Int32 BIFF12_CFRULE_TYPE_COLORSCALE = 3;
+const sal_Int32 BIFF12_CFRULE_TYPE_DATABAR = 4;
+const sal_Int32 BIFF12_CFRULE_TYPE_TOPTEN = 5;
+const sal_Int32 BIFF12_CFRULE_TYPE_ICONSET = 6;
+
+const sal_Int32 BIFF12_CFRULE_SUB_CELLIS = 0;
+const sal_Int32 BIFF12_CFRULE_SUB_EXPRESSION = 1;
+const sal_Int32 BIFF12_CFRULE_SUB_COLORSCALE = 2;
+const sal_Int32 BIFF12_CFRULE_SUB_DATABAR = 3;
+const sal_Int32 BIFF12_CFRULE_SUB_ICONSET = 4;
+const sal_Int32 BIFF12_CFRULE_SUB_TOPTEN = 5;
+const sal_Int32 BIFF12_CFRULE_SUB_UNIQUE = 7;
+const sal_Int32 BIFF12_CFRULE_SUB_TEXT = 8;
+const sal_Int32 BIFF12_CFRULE_SUB_BLANK = 9;
+const sal_Int32 BIFF12_CFRULE_SUB_NOTBLANK = 10;
+const sal_Int32 BIFF12_CFRULE_SUB_ERROR = 11;
+const sal_Int32 BIFF12_CFRULE_SUB_NOTERROR = 12;
+const sal_Int32 BIFF12_CFRULE_SUB_TODAY = 15;
+const sal_Int32 BIFF12_CFRULE_SUB_TOMORROW = 16;
+const sal_Int32 BIFF12_CFRULE_SUB_YESTERDAY = 17;
+const sal_Int32 BIFF12_CFRULE_SUB_LAST7DAYS = 18;
+const sal_Int32 BIFF12_CFRULE_SUB_LASTMONTH = 19;
+const sal_Int32 BIFF12_CFRULE_SUB_NEXTMONTH = 20;
+const sal_Int32 BIFF12_CFRULE_SUB_THISWEEK = 21;
+const sal_Int32 BIFF12_CFRULE_SUB_NEXTWEEK = 22;
+const sal_Int32 BIFF12_CFRULE_SUB_LASTWEEK = 23;
+const sal_Int32 BIFF12_CFRULE_SUB_THISMONTH = 24;
+const sal_Int32 BIFF12_CFRULE_SUB_ABOVEAVERAGE = 25;
+const sal_Int32 BIFF12_CFRULE_SUB_BELOWAVERAGE = 26;
+const sal_Int32 BIFF12_CFRULE_SUB_DUPLICATE = 27;
+const sal_Int32 BIFF12_CFRULE_SUB_EQABOVEAVERAGE = 29;
+const sal_Int32 BIFF12_CFRULE_SUB_EQBELOWAVERAGE = 30;
+
+const sal_Int32 BIFF12_CFRULE_TIMEOP_TODAY = 0;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_YESTERDAY = 1;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_LAST7DAYS = 2;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_THISWEEK = 3;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_LASTWEEK = 4;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_LASTMONTH = 5;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_TOMORROW = 6;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_NEXTWEEK = 7;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_NEXTMONTH = 8;
+const sal_Int32 BIFF12_CFRULE_TIMEOP_THISMONTH = 9;
+
+const sal_uInt16 BIFF12_CFRULE_STOPIFTRUE = 0x0002;
+const sal_uInt16 BIFF12_CFRULE_ABOVEAVERAGE = 0x0004;
+const sal_uInt16 BIFF12_CFRULE_BOTTOM = 0x0008;
+const sal_uInt16 BIFF12_CFRULE_PERCENT = 0x0010;
+
+bool isValue(std::u16string_view rStr, double& rVal)
+{
+ sal_Int32 nEnd = -1;
+ rVal = rtl::math::stringToDouble(o3tl::trim(rStr), '.', ',', nullptr, &nEnd);
+
+ return nEnd >= static_cast<sal_Int32>(rStr.size());
+}
+
+void SetCfvoData( ColorScaleRuleModelEntry* pEntry, const AttributeList& rAttribs )
+{
+ OUString aType = rAttribs.getString( XML_type, OUString() );
+ OUString aVal = rAttribs.getString(XML_val, OUString());
+
+ double nVal = 0.0;
+ bool bVal = isValue(aVal, nVal);
+ if( !bVal || aType == "formula" )
+ {
+ pEntry->maFormula = aVal;
+ }
+ else
+ {
+ pEntry->mnVal = nVal;
+ }
+
+ if (aType == "num")
+ {
+ pEntry->mbNum = true;
+ }
+ else if( aType == "min" )
+ {
+ pEntry->mbMin = true;
+ }
+ else if( aType == "max" )
+ {
+ pEntry->mbMax = true;
+ }
+ else if( aType == "percent" )
+ {
+ pEntry->mbPercent = true;
+ }
+ else if( aType == "percentile" )
+ {
+ pEntry->mbPercentile = true;
+ }
+}
+
+}
+
+ColorScaleRule::ColorScaleRule( const CondFormat& rFormat ):
+ WorksheetHelper( rFormat ),
+ mnCfvo(0),
+ mnCol(0)
+{
+}
+
+void ColorScaleRule::importCfvo( const AttributeList& rAttribs )
+{
+ if(mnCfvo >= maColorScaleRuleEntries.size())
+ maColorScaleRuleEntries.emplace_back();
+
+ SetCfvoData( &maColorScaleRuleEntries[mnCfvo], rAttribs );
+
+ ++mnCfvo;
+}
+
+namespace {
+
+::Color importOOXColor(const AttributeList& rAttribs, const ThemeBuffer& rThemeBuffer, const GraphicHelper& rGraphicHelper)
+{
+ ::Color nColor;
+ if( rAttribs.hasAttribute( XML_rgb ) )
+ nColor = ::Color(ColorTransparency, rAttribs.getUnsignedHex( XML_rgb, UNSIGNED_RGB_TRANSPARENT ));
+ else if( rAttribs.hasAttribute( XML_theme ) )
+ {
+ sal_uInt32 nThemeIndex = rAttribs.getUnsigned( XML_theme, 0 );
+
+ // Excel has a bug in the mapping of index 0, 1, 2 and 3.
+ if (nThemeIndex == 0)
+ nThemeIndex = 1;
+ else if (nThemeIndex == 1)
+ nThemeIndex = 0;
+ else if (nThemeIndex == 2)
+ nThemeIndex = 3;
+ else if (nThemeIndex == 3)
+ nThemeIndex = 2;
+
+ nColor = rThemeBuffer.getColorByIndex( nThemeIndex );
+ }
+
+ ::Color aColor;
+ double nTint = rAttribs.getDouble(XML_tint, 0.0);
+ if (nTint != 0.0)
+ {
+ oox::drawingml::Color aDMColor;
+ aDMColor.setSrgbClr(nColor);
+ aDMColor.addExcelTintTransformation(nTint);
+ aColor = aDMColor.getColor(rGraphicHelper);
+ }
+ else
+ aColor = nColor.GetRGBColor();
+
+ return aColor;
+}
+
+}
+
+void ColorScaleRule::importColor( const AttributeList& rAttribs )
+{
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+
+ if(mnCol >= maColorScaleRuleEntries.size())
+ maColorScaleRuleEntries.emplace_back();
+
+ maColorScaleRuleEntries[mnCol].maColor = aColor;
+ ++mnCol;
+}
+
+namespace {
+
+ScColorScaleEntry* ConvertToModel( const ColorScaleRuleModelEntry& rEntry, ScDocument* pDoc, const ScAddress& rAddr )
+{
+ ScColorScaleEntry* pEntry = new ScColorScaleEntry(rEntry.mnVal, rEntry.maColor);
+
+ if(rEntry.mbMin)
+ pEntry->SetType(COLORSCALE_MIN);
+ if(rEntry.mbMax)
+ pEntry->SetType(COLORSCALE_MAX);
+ if(rEntry.mbPercent)
+ pEntry->SetType(COLORSCALE_PERCENT);
+ if(rEntry.mbPercentile)
+ pEntry->SetType(COLORSCALE_PERCENTILE);
+ if (rEntry.mbNum)
+ pEntry->SetType(COLORSCALE_VALUE);
+
+ if(!rEntry.maFormula.isEmpty())
+ {
+ pEntry->SetType(COLORSCALE_FORMULA);
+ pEntry->SetFormula(rEntry.maFormula, *pDoc, rAddr, formula::FormulaGrammar::GRAM_ENGLISH_XL_A1);
+ }
+
+ return pEntry;
+}
+
+}
+
+void ColorScaleRule::AddEntries( ScColorScaleFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr )
+{
+ for(const ColorScaleRuleModelEntry & rEntry : maColorScaleRuleEntries)
+ {
+ ScColorScaleEntry* pEntry = ConvertToModel( rEntry, pDoc, rAddr );
+
+ pFormat->AddEntry( pEntry );
+ }
+}
+
+DataBarRule::DataBarRule( const CondFormat& rFormat ):
+ WorksheetHelper( rFormat ),
+ mxFormat(new ScDataBarFormatData)
+{
+ mxFormat->meAxisPosition = databar::NONE;
+}
+
+void DataBarRule::importColor( const AttributeList& rAttribs )
+{
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+
+ mxFormat->maPositiveColor = aColor;
+}
+
+void DataBarRule::importCfvo( const AttributeList& rAttribs )
+{
+ ColorScaleRuleModelEntry* pEntry;
+ if(!mpLowerLimit)
+ {
+ mpLowerLimit.reset(new ColorScaleRuleModelEntry);
+ pEntry = mpLowerLimit.get();
+ }
+ else
+ {
+ mpUpperLimit.reset(new ColorScaleRuleModelEntry);
+ pEntry = mpUpperLimit.get();
+ }
+
+ SetCfvoData( pEntry, rAttribs );
+}
+
+void DataBarRule::importAttribs( const AttributeList& rAttribs )
+{
+ mxFormat->mbOnlyBar = !rAttribs.getBool( XML_showValue, true );
+ mxFormat->mnMinLength = rAttribs.getUnsigned( XML_minLength, 10);
+ mxFormat->mnMaxLength = rAttribs.getUnsigned( XML_maxLength, 90);
+}
+
+void DataBarRule::SetData( ScDataBarFormat* pFormat, ScDocument* pDoc, const ScAddress& rAddr )
+{
+ ScColorScaleEntry* pUpperEntry = ConvertToModel(*mpUpperLimit, pDoc, rAddr);
+ ScColorScaleEntry* pLowerEntry = ConvertToModel(*mpLowerLimit, pDoc, rAddr);
+
+ mxFormat->mpUpperLimit.reset( pUpperEntry );
+ mxFormat->mpLowerLimit.reset( pLowerEntry );
+ pFormat->SetDataBarData(mxFormat.release());
+}
+
+IconSetRule::IconSetRule( const WorksheetHelper& rParent ):
+ WorksheetHelper( rParent ),
+ mxFormatData( new ScIconSetFormatData ),
+ mbCustom(false)
+{
+}
+
+void IconSetRule::importCfvo( const AttributeList& rAttribs )
+{
+ ColorScaleRuleModelEntry aNewEntry;
+ SetCfvoData(&aNewEntry, rAttribs);
+
+ maEntries.push_back(aNewEntry);
+}
+
+void IconSetRule::importAttribs( const AttributeList& rAttribs )
+{
+ maIconSetType = rAttribs.getString( XML_iconSet, "3TrafficLights1" );
+ mxFormatData->mbShowValue = rAttribs.getBool( XML_showValue, true );
+ mxFormatData->mbReverse = rAttribs.getBool( XML_reverse, false );
+ mbCustom = rAttribs.getBool(XML_custom, false);
+}
+
+void IconSetRule::importFormula(const OUString& rFormula)
+{
+ ColorScaleRuleModelEntry& rEntry = maEntries.back();
+ double nVal = 0.0;
+ if ((rEntry.mbNum || rEntry.mbPercent || rEntry.mbPercentile) && isValue(rFormula, nVal))
+ {
+ rEntry.mnVal = nVal;
+ }
+ else if (!rFormula.isEmpty())
+ rEntry.maFormula = rFormula;
+}
+
+namespace {
+
+ScIconSetType getType(std::u16string_view rName)
+{
+ ScIconSetType eIconSetType = IconSet_3TrafficLights1;
+ const ScIconSetMap* pIconSetMap = ScIconSetFormat::g_IconSetMap;
+ for(size_t i = 0; pIconSetMap[i].pName; ++i)
+ {
+ if(OUString::createFromAscii(pIconSetMap[i].pName) == rName)
+ {
+ eIconSetType = pIconSetMap[i].eType;
+ break;
+ }
+ }
+
+ return eIconSetType;
+}
+
+}
+
+void IconSetRule::importIcon(const AttributeList& rAttribs)
+{
+ OUString aIconSet = rAttribs.getString(XML_iconSet, OUString());
+ sal_Int32 nIndex = rAttribs.getInteger(XML_iconId, -1);
+ if (aIconSet == "NoIcons")
+ {
+ nIndex = -1;
+ }
+
+ ScIconSetType eIconSetType = getType(aIconSet);
+ mxFormatData->maCustomVector.emplace_back(eIconSetType, nIndex);
+}
+
+void IconSetRule::SetData( ScIconSetFormat* pFormat, ScDocument* pDoc, const ScAddress& rPos )
+{
+ for(const ColorScaleRuleModelEntry & rEntry : maEntries)
+ {
+ ScColorScaleEntry* pModelEntry = ConvertToModel( rEntry, pDoc, rPos );
+ mxFormatData->m_Entries.emplace_back(pModelEntry);
+ }
+
+ mxFormatData->eIconSetType = getType(maIconSetType);
+ mxFormatData->mbCustom = mbCustom;
+ pFormat->SetIconSetData(mxFormatData.release());
+}
+
+CondFormatRuleModel::CondFormatRuleModel() :
+ mnPriority( -1 ),
+ mnType( XML_TOKEN_INVALID ),
+ mnOperator( XML_TOKEN_INVALID ),
+ mnTimePeriod( XML_TOKEN_INVALID ),
+ mnRank( 0 ),
+ mnStdDev( 0 ),
+ mnDxfId( -1 ),
+ mbStopIfTrue( false ),
+ mbBottom( false ),
+ mbPercent( false ),
+ mbAboveAverage( true ),
+ mbEqualAverage( false )
+{
+}
+
+void CondFormatRuleModel::setBiffOperator( sal_Int32 nOperator )
+{
+ static const sal_Int32 spnOperators[] = {
+ XML_TOKEN_INVALID, XML_between, XML_notBetween, XML_equal, XML_notEqual,
+ XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual };
+ mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
+}
+
+void CondFormatRuleModel::setBiff12TextType( sal_Int32 nOperator )
+{
+ // note: type XML_notContainsText vs. operator XML_notContains
+ static const sal_Int32 spnTypes[] = { XML_containsText, XML_notContainsText, XML_beginsWith, XML_endsWith };
+ mnType = STATIC_ARRAY_SELECT( spnTypes, nOperator, XML_TOKEN_INVALID );
+ static const sal_Int32 spnOperators[] = { XML_containsText, XML_notContains, XML_beginsWith, XML_endsWith };
+ mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
+}
+
+CondFormatRule::CondFormatRule( const CondFormat& rCondFormat, ScConditionalFormat* pFormat ) :
+ WorksheetHelper( rCondFormat ),
+ mrCondFormat( rCondFormat ),
+ mpFormat(pFormat),
+ mpFormatEntry(nullptr)
+{
+}
+
+void CondFormatRule::importCfRule( const AttributeList& rAttribs )
+{
+ maModel.maText = rAttribs.getString( XML_text, OUString() );
+ maModel.mnPriority = rAttribs.getInteger( XML_priority, -1 );
+ maModel.mnType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID );
+ maModel.mnOperator = rAttribs.getToken( XML_operator, XML_TOKEN_INVALID );
+ maModel.mnTimePeriod = rAttribs.getToken( XML_timePeriod, XML_TOKEN_INVALID );
+ maModel.mnRank = rAttribs.getInteger( XML_rank, 0 );
+ maModel.mnStdDev = rAttribs.getInteger( XML_stdDev, 0 );
+ maModel.mnDxfId = rAttribs.getInteger( XML_dxfId, -1 );
+ maModel.mbStopIfTrue = rAttribs.getBool( XML_stopIfTrue, false );
+ maModel.mbBottom = rAttribs.getBool( XML_bottom, false );
+ maModel.mbPercent = rAttribs.getBool( XML_percent, false );
+ maModel.mbAboveAverage = rAttribs.getBool( XML_aboveAverage, true );
+ maModel.mbEqualAverage = rAttribs.getBool( XML_equalAverage, false );
+
+ if(maModel.mnType == XML_colorScale)
+ {
+ //import the remaining values
+
+ }
+}
+
+void CondFormatRule::appendFormula( const OUString& rFormula )
+{
+ ScAddress aBaseAddr = mrCondFormat.getRanges().GetTopLeftCorner();
+ ApiTokenSequence aTokens = getFormulaParser().importFormula( aBaseAddr, rFormula );
+ maModel.maFormulas.push_back( aTokens );
+}
+
+void CondFormatRule::importCfRule( SequenceInputStream& rStrm )
+{
+ sal_Int32 nType, nSubType, nOperator, nFmla1Size, nFmla2Size, nFmla3Size;
+ sal_uInt16 nFlags;
+ nType = rStrm.readInt32();
+ nSubType = rStrm.readInt32();
+ maModel.mnDxfId = rStrm.readInt32();
+ maModel.mnPriority = rStrm.readInt32();
+ nOperator = rStrm.readInt32();
+ rStrm.skip( 8 );
+ nFlags = rStrm.readuInt16();
+ nFmla1Size = rStrm.readInt32();
+ nFmla2Size = rStrm.readInt32();
+ nFmla3Size = rStrm.readInt32();
+ rStrm >> maModel.maText;
+
+ /* Import the formulas. For no obvious reason, the sizes of the formulas
+ are already stored before. Nevertheless the following formulas contain
+ their own sizes. */
+
+ // first formula
+ // I am not bored enough to bother simplifying these expressions
+ SAL_WARN_IF( !( (nFmla1Size >= 0) || ((nFmla2Size == 0) && (nFmla3Size == 0)) ), "sc.filter", "CondFormatRule::importCfRule - missing first formula" );
+ SAL_WARN_IF( !( (nFmla1Size > 0) == (rStrm.getRemaining() >= 8) ), "sc.filter", "CondFormatRule::importCfRule - formula size mismatch" );
+ if( rStrm.getRemaining() >= 8 )
+ {
+ ScAddress aBaseAddr = mrCondFormat.getRanges().GetTopLeftCorner();
+ ApiTokenSequence aTokens = getFormulaParser().importFormula( aBaseAddr, FormulaType::CondFormat, rStrm );
+ maModel.maFormulas.push_back( aTokens );
+
+ // second formula
+ OSL_ENSURE( (nFmla2Size >= 0) || (nFmla3Size == 0), "CondFormatRule::importCfRule - missing second formula" );
+ OSL_ENSURE( (nFmla2Size > 0) == (rStrm.getRemaining() >= 8), "CondFormatRule::importCfRule - formula size mismatch" );
+ if( rStrm.getRemaining() >= 8 )
+ {
+ aTokens = getFormulaParser().importFormula( aBaseAddr, FormulaType::CondFormat, rStrm );
+ maModel.maFormulas.push_back( aTokens );
+
+ // third formula
+ OSL_ENSURE( (nFmla3Size > 0) == (rStrm.getRemaining() >= 8), "CondFormatRule::importCfRule - formula size mismatch" );
+ if( rStrm.getRemaining() >= 8 )
+ {
+ aTokens = getFormulaParser().importFormula( aBaseAddr, FormulaType::CondFormat, rStrm );
+ maModel.maFormulas.push_back( aTokens );
+ }
+ }
+ }
+
+ // flags
+ maModel.mbStopIfTrue = getFlag( nFlags, BIFF12_CFRULE_STOPIFTRUE );
+ maModel.mbBottom = getFlag( nFlags, BIFF12_CFRULE_BOTTOM );
+ maModel.mbPercent = getFlag( nFlags, BIFF12_CFRULE_PERCENT );
+ maModel.mbAboveAverage = getFlag( nFlags, BIFF12_CFRULE_ABOVEAVERAGE );
+ // no flag for equalAverage, must be determined from subtype below...
+
+ // Convert the type/operator settings. This is a real mess...
+ switch( nType )
+ {
+ case BIFF12_CFRULE_TYPE_CELLIS:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_CELLIS, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ maModel.mnType = XML_cellIs;
+ maModel.setBiffOperator( nOperator );
+ OSL_ENSURE( maModel.mnOperator != XML_TOKEN_INVALID, "CondFormatRule::importCfRule - unknown operator" );
+ break;
+ case BIFF12_CFRULE_TYPE_EXPRESSION:
+ // here we have to look at the subtype to find the real type...
+ switch( nSubType )
+ {
+ case BIFF12_CFRULE_SUB_EXPRESSION:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_expression;
+ break;
+ case BIFF12_CFRULE_SUB_UNIQUE:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_uniqueValues;
+ break;
+ case BIFF12_CFRULE_SUB_TEXT:
+ maModel.setBiff12TextType( nOperator );
+ OSL_ENSURE( maModel.mnType != XML_TOKEN_INVALID, "CondFormatRule::importCfRule - unexpected operator value" );
+ break;
+ case BIFF12_CFRULE_SUB_BLANK:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_containsBlanks;
+ break;
+ case BIFF12_CFRULE_SUB_NOTBLANK:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_notContainsBlanks;
+ break;
+ case BIFF12_CFRULE_SUB_ERROR:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_containsErrors;
+ break;
+ case BIFF12_CFRULE_SUB_NOTERROR:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_notContainsErrors;
+ break;
+ case BIFF12_CFRULE_SUB_TODAY:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_TODAY, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_today;
+ break;
+ case BIFF12_CFRULE_SUB_TOMORROW:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_TOMORROW, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_tomorrow;
+ break;
+ case BIFF12_CFRULE_SUB_YESTERDAY:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_YESTERDAY,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_yesterday;
+ break;
+ case BIFF12_CFRULE_SUB_LAST7DAYS:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_LAST7DAYS,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_last7Days;
+ break;
+ case BIFF12_CFRULE_SUB_LASTMONTH:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_LASTMONTH,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_lastMonth;
+ break;
+ case BIFF12_CFRULE_SUB_NEXTMONTH:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_NEXTMONTH,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_nextMonth;
+ break;
+ case BIFF12_CFRULE_SUB_THISWEEK:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_THISWEEK, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_thisWeek;
+ break;
+ case BIFF12_CFRULE_SUB_NEXTWEEK:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_NEXTWEEK, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_nextWeek;
+ break;
+ case BIFF12_CFRULE_SUB_LASTWEEK:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_LASTWEEK, "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_lastWeek;
+ break;
+ case BIFF12_CFRULE_SUB_THISMONTH:
+ SAL_WARN_IF(
+ nOperator != BIFF12_CFRULE_TIMEOP_THISMONTH,
+ "sc.filter",
+ "CondFormatRule::importCfRule - unexpected time operator value");
+ maModel.mnType = XML_timePeriod;
+ maModel.mnTimePeriod = XML_thisMonth;
+ break;
+ case BIFF12_CFRULE_SUB_ABOVEAVERAGE:
+ OSL_ENSURE( maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
+ maModel.mnType = XML_aboveAverage;
+ maModel.mnStdDev = nOperator; // operator field used for standard deviation
+ maModel.mbAboveAverage = true;
+ maModel.mbEqualAverage = false; // does not exist as real flag...
+ break;
+ case BIFF12_CFRULE_SUB_BELOWAVERAGE:
+ OSL_ENSURE( !maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
+ maModel.mnType = XML_aboveAverage;
+ maModel.mnStdDev = nOperator; // operator field used for standard deviation
+ maModel.mbAboveAverage = false;
+ maModel.mbEqualAverage = false; // does not exist as real flag...
+ break;
+ case BIFF12_CFRULE_SUB_DUPLICATE:
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_duplicateValues;
+ break;
+ case BIFF12_CFRULE_SUB_EQABOVEAVERAGE:
+ OSL_ENSURE( maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
+ maModel.mnType = XML_aboveAverage;
+ maModel.mnStdDev = nOperator; // operator field used for standard deviation
+ maModel.mbAboveAverage = true;
+ maModel.mbEqualAverage = true; // does not exist as real flag...
+ break;
+ case BIFF12_CFRULE_SUB_EQBELOWAVERAGE:
+ OSL_ENSURE( !maModel.mbAboveAverage, "CondFormatRule::importCfRule - wrong above-average flag" );
+ maModel.mnType = XML_aboveAverage;
+ maModel.mnStdDev = nOperator; // operator field used for standard deviation
+ maModel.mbAboveAverage = false;
+ maModel.mbEqualAverage = true; // does not exist as real flag...
+ break;
+ }
+ break;
+ case BIFF12_CFRULE_TYPE_COLORSCALE:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_COLORSCALE, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_colorScale;
+ break;
+ case BIFF12_CFRULE_TYPE_DATABAR:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_DATABAR, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_dataBar;
+ break;
+ case BIFF12_CFRULE_TYPE_TOPTEN:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_TOPTEN, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ maModel.mnType = XML_top10;
+ maModel.mnRank = nOperator; // operator field used for rank value
+ break;
+ case BIFF12_CFRULE_TYPE_ICONSET:
+ SAL_WARN_IF(
+ nSubType != BIFF12_CFRULE_SUB_ICONSET, "sc.filter",
+ "CondFormatRule::importCfRule - rule type/subtype mismatch");
+ OSL_ENSURE( nOperator == 0, "CondFormatRule::importCfRule - unexpected operator value" );
+ maModel.mnType = XML_iconSet;
+ break;
+ default:
+ OSL_FAIL( "CondFormatRule::importCfRule - unknown rule type" );
+ }
+}
+
+void CondFormatRule::setFormatEntry(sal_Int32 nPriority, ScFormatEntry* pEntry)
+{
+ maModel.mnPriority = nPriority;
+ mpFormatEntry = pEntry;
+}
+
+void CondFormatRule::finalizeImport()
+{
+ if (mpFormatEntry)
+ {
+ mpFormat->AddEntry(mpFormatEntry);
+ return;
+ }
+
+ ScConditionMode eOperator = ScConditionMode::NONE;
+
+ /* Replacement formula for unsupported rule types (text comparison rules,
+ time period rules, cell type rules). The replacement formulas below may
+ contain several placeholders:
+ - '#B' will be replaced by the current relative base address (may occur
+ several times).
+ - '#R' will be replaced by the entire range list of the conditional
+ formatting (absolute addresses).
+ - '#T' will be replaced by the quoted comparison text.
+ - '#L' will be replaced by the length of the comparison text (from
+ the 'text' attribute) used in text comparison rules.
+ - '#K' will be replaced by the rank (from the 'rank' attribute) used in
+ top-10 rules.
+ - '#M' will be replaced by the top/bottom flag (from the 'bottom'
+ attribute) used in the RANK function in top-10 rules.
+ - '#C' will be replaced by one of the comparison operators <, >, <=, or
+ >=, according to the 'aboveAverage' and 'equalAverage' flags.
+ */
+ OUString aReplaceFormula;
+
+ switch( maModel.mnType )
+ {
+ case XML_cellIs:
+ eOperator = CondFormatBuffer::convertToInternalOperator( maModel.mnOperator );
+ break;
+ case XML_duplicateValues:
+ eOperator = ScConditionMode::Duplicate;
+ break;
+ case XML_uniqueValues:
+ eOperator = ScConditionMode::NotDuplicate;
+ break;
+ case XML_expression:
+ eOperator = ScConditionMode::Direct;
+ break;
+ case XML_containsText:
+ OSL_ENSURE( maModel.mnOperator == XML_containsText, "CondFormatRule::finalizeImport - unexpected operator" );
+ eOperator = ScConditionMode::ContainsText;
+ break;
+ case XML_notContainsText:
+ // note: type XML_notContainsText vs. operator XML_notContains
+ OSL_ENSURE( maModel.mnOperator == XML_notContains, "CondFormatRule::finalizeImport - unexpected operator" );
+ eOperator = ScConditionMode::NotContainsText;
+ break;
+ case XML_beginsWith:
+ OSL_ENSURE( maModel.mnOperator == XML_beginsWith, "CondFormatRule::finalizeImport - unexpected operator" );
+ eOperator = ScConditionMode::BeginsWith;
+ break;
+ case XML_endsWith:
+ OSL_ENSURE( maModel.mnOperator == XML_endsWith, "CondFormatRule::finalizeImport - unexpected operator" );
+ eOperator = ScConditionMode::EndsWith;
+ break;
+ case XML_timePeriod:
+ break;
+ case XML_containsBlanks:
+ aReplaceFormula = "LEN(TRIM(#B))=0";
+ break;
+ case XML_notContainsBlanks:
+ aReplaceFormula = "LEN(TRIM(#B))>0";
+ break;
+ case XML_containsErrors:
+ eOperator = ScConditionMode::Error;
+ break;
+ case XML_notContainsErrors:
+ eOperator = ScConditionMode::NoError;
+ break;
+ case XML_top10:
+ if(maModel.mbPercent)
+ {
+ if(maModel.mbBottom)
+ eOperator = ScConditionMode::BottomPercent;
+ else
+ eOperator = ScConditionMode::TopPercent;
+ }
+ else
+ {
+ if(maModel.mbBottom)
+ eOperator = ScConditionMode::Bottom10;
+ else
+ eOperator = ScConditionMode::Top10;
+ }
+ break;
+ case XML_aboveAverage:
+ if(maModel.mbAboveAverage)
+ {
+ if(maModel.mbEqualAverage)
+ eOperator = ScConditionMode::AboveEqualAverage;
+ else
+ eOperator = ScConditionMode::AboveAverage;
+ }
+ else
+ {
+ if(maModel.mbEqualAverage)
+ eOperator = ScConditionMode::BelowEqualAverage;
+ else
+ eOperator = ScConditionMode::BelowAverage;
+ }
+ break;
+ case XML_colorScale:
+ break;
+ }
+
+ if( !aReplaceFormula.isEmpty() )
+ {
+ OUString aAddress;
+ sal_Int32 nStrPos = aReplaceFormula.getLength();
+ while( (nStrPos = aReplaceFormula.lastIndexOf( '#', nStrPos )) >= 0 )
+ {
+ switch( aReplaceFormula[ nStrPos + 1 ] )
+ {
+ case 'B': // current base address
+ if( aAddress.isEmpty() )
+ aAddress = FormulaProcessorBase::generateAddress2dString( mrCondFormat.getRanges().GetTopLeftCorner(), false );
+ aReplaceFormula = aReplaceFormula.replaceAt( nStrPos, 2, aAddress );
+ break;
+ default:
+ OSL_FAIL( "CondFormatRule::finalizeImport - unknown placeholder" );
+ }
+ }
+
+ // set the replacement formula
+ maModel.maFormulas.clear();
+ appendFormula( aReplaceFormula );
+ eOperator = ScConditionMode::Direct;
+ }
+
+ ScAddress aPos = mrCondFormat.getRanges().GetTopLeftCorner();
+
+ if( eOperator == ScConditionMode::Error || eOperator == ScConditionMode::NoError )
+ {
+ ScDocument& rDoc = getScDocument();
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, nullptr, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( eOperator == ScConditionMode::BeginsWith || eOperator == ScConditionMode::EndsWith ||
+ eOperator == ScConditionMode::ContainsText || eOperator == ScConditionMode::NotContainsText )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScTokenArray aTokenArray(rDoc);
+ svl::SharedStringPool& rSPool = rDoc.GetSharedStringPool();
+ aTokenArray.AddString(rSPool.intern(maModel.maText));
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, &aTokenArray, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( (eOperator != ScConditionMode::NONE) && !maModel.maFormulas.empty() )
+ {
+ ScDocument& rDoc = getScDocument();
+ std::unique_ptr<ScTokenArray> pTokenArray2;
+ if( maModel.maFormulas.size() >= 2)
+ {
+ pTokenArray2.reset(new ScTokenArray(rDoc));
+ ScTokenConversion::ConvertToTokenArray(rDoc, *pTokenArray2, maModel.maFormulas[1]);
+ rDoc.CheckLinkFormulaNeedingCheck(*pTokenArray2);
+ }
+
+ ScTokenArray aTokenArray(rDoc);
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, maModel.maFormulas[ 0 ] );
+ rDoc.CheckLinkFormulaNeedingCheck( aTokenArray);
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry(eOperator,
+ &aTokenArray, pTokenArray2.get(), rDoc, aPos, aStyleName);
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if ( eOperator == ScConditionMode::Top10 || eOperator == ScConditionMode::Bottom10 ||
+ eOperator == ScConditionMode::TopPercent || eOperator == ScConditionMode::BottomPercent )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScTokenArray aTokenArray(rDoc);
+ aTokenArray.AddDouble( maModel.mnRank );
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, &aTokenArray, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( eOperator == ScConditionMode::AboveAverage || eOperator == ScConditionMode::BelowAverage ||
+ eOperator == ScConditionMode::AboveEqualAverage || eOperator == ScConditionMode::BelowEqualAverage )
+ {
+ ScDocument& rDoc = getScDocument();
+ // actually that is still unsupported
+ ScTokenArray aTokenArrayDev(rDoc);
+ aTokenArrayDev.AddDouble( maModel.mnStdDev );
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, &aTokenArrayDev, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( eOperator == ScConditionMode::Duplicate || eOperator == ScConditionMode::NotDuplicate )
+ {
+ ScDocument& rDoc = getScDocument();
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ ScCondFormatEntry* pNewEntry = new ScCondFormatEntry( eOperator, nullptr, nullptr, rDoc, aPos, aStyleName );
+ mpFormat->AddEntry(pNewEntry);
+ }
+ else if( maModel.mnType == XML_timePeriod )
+ {
+ condformat::ScCondFormatDateType eDateType = condformat::TODAY;
+ switch( maModel.mnTimePeriod )
+ {
+ case XML_yesterday:
+ eDateType = condformat::YESTERDAY;
+ break;
+ case XML_today:
+ eDateType = condformat::TODAY;
+ break;
+ case XML_tomorrow:
+ eDateType = condformat::TOMORROW;
+ break;
+ case XML_last7Days:
+ eDateType = condformat::LAST7DAYS;
+ break;
+ case XML_lastWeek:
+ eDateType = condformat::LASTWEEK;
+ break;
+ case XML_thisWeek:
+ eDateType = condformat::THISWEEK;
+ break;
+ case XML_nextWeek:
+ eDateType = condformat::NEXTWEEK;
+ break;
+ case XML_lastMonth:
+ eDateType = condformat::LASTMONTH;
+ break;
+ case XML_thisMonth:
+ eDateType = condformat::THISMONTH;
+ break;
+ case XML_nextMonth:
+ eDateType = condformat::NEXTMONTH;
+ break;
+ default:
+ SAL_WARN("sc.filter", "CondFormatRule::finalizeImport - unknown time period type" );
+ }
+
+ ScDocument& rDoc = getScDocument();
+ ScCondDateFormatEntry* pFormatEntry = new ScCondDateFormatEntry(&rDoc);
+ pFormatEntry->SetDateType(eDateType);
+ OUString aStyleName = getStyles().createDxfStyle( maModel.mnDxfId );
+ pFormatEntry->SetStyleName( aStyleName );
+
+ mpFormat->AddEntry(pFormatEntry);
+ }
+ else if( mpColor )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScColorScaleFormat* pFormatEntry = new ScColorScaleFormat(&rDoc);
+
+ mpFormat->AddEntry(pFormatEntry);
+
+ mpColor->AddEntries( pFormatEntry, &rDoc, aPos );
+ }
+ else if (mpDataBar)
+ {
+ ScDocument& rDoc = getScDocument();
+ ScDataBarFormat* pFormatEntry = new ScDataBarFormat(&rDoc);
+
+ mpFormat->AddEntry(pFormatEntry);
+ mpDataBar->SetData( pFormatEntry, &rDoc, aPos );
+
+ }
+ else if(mpIconSet)
+ {
+ ScDocument& rDoc = getScDocument();
+ ScIconSetFormat* pFormatEntry = new ScIconSetFormat(&rDoc);
+
+ mpFormat->AddEntry(pFormatEntry);
+ mpIconSet->SetData( pFormatEntry, &rDoc, aPos );
+ }
+}
+
+ColorScaleRule* CondFormatRule::getColorScale()
+{
+ if(!mpColor)
+ mpColor.reset( new ColorScaleRule(mrCondFormat) );
+
+ return mpColor.get();
+}
+
+DataBarRule* CondFormatRule::getDataBar()
+{
+ if(!mpDataBar)
+ mpDataBar.reset( new DataBarRule(mrCondFormat) );
+
+ return mpDataBar.get();
+}
+
+IconSetRule* CondFormatRule::getIconSet()
+{
+ if(!mpIconSet)
+ mpIconSet.reset( new IconSetRule(mrCondFormat) );
+
+ return mpIconSet.get();
+}
+
+CondFormatModel::CondFormatModel() :
+ mbPivot( false )
+{
+}
+
+CondFormat::CondFormat( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ mpFormat(nullptr),
+ mbReadyForFinalize(false)
+{
+}
+
+void CondFormat::importConditionalFormatting( const AttributeList& rAttribs )
+{
+ getAddressConverter().convertToCellRangeList( maModel.maRanges, rAttribs.getString( XML_sqref, OUString() ), getSheetIndex(), true );
+ maModel.mbPivot = rAttribs.getBool( XML_pivot, false );
+ mpFormat = new ScConditionalFormat(0, &getScDocument());
+}
+
+CondFormatRuleRef CondFormat::importCfRule( const AttributeList& rAttribs )
+{
+ CondFormatRuleRef xRule = createRule();
+ xRule->importCfRule( rAttribs );
+ insertRule( xRule );
+ return xRule;
+}
+
+void CondFormat::importCondFormatting( SequenceInputStream& rStrm )
+{
+ BinRangeList aRanges;
+ rStrm.skip( 8 );
+ rStrm >> aRanges;
+ getAddressConverter().convertToCellRangeList( maModel.maRanges, aRanges, getSheetIndex(), true );
+ mpFormat = new ScConditionalFormat(0, &getScDocument());
+}
+
+void CondFormat::importCfRule( SequenceInputStream& rStrm )
+{
+ CondFormatRuleRef xRule = createRule();
+ xRule->importCfRule( rStrm );
+ insertRule( xRule );
+}
+
+void CondFormat::finalizeImport()
+{
+ // probably some error in the xml if we are not ready
+ if ( !mbReadyForFinalize )
+ return;
+ ScDocument& rDoc = getScDocument();
+ mpFormat->SetRange(maModel.maRanges);
+ maRules.forEachMem( &CondFormatRule::finalizeImport );
+ SCTAB nTab = maModel.maRanges.GetTopLeftCorner().Tab();
+ sal_Int32 nIndex = getScDocument().AddCondFormat(std::unique_ptr<ScConditionalFormat>(mpFormat), nTab);
+
+ rDoc.AddCondFormatData( maModel.maRanges, nTab, nIndex );
+}
+
+CondFormatRuleRef CondFormat::createRule()
+{
+ return std::make_shared<CondFormatRule>( *this, mpFormat );
+}
+
+void CondFormat::insertRule( CondFormatRuleRef const & xRule )
+{
+ if( xRule && (xRule->getPriority() > 0) )
+ {
+ OSL_ENSURE( maRules.find( xRule->getPriority() ) == maRules.end(), "CondFormat::insertRule - multiple rules with equal priority" );
+ maRules[ xRule->getPriority() ] = xRule;
+ }
+}
+
+CondFormatBuffer::CondFormatBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+CondFormatRef CondFormatBuffer::importConditionalFormatting( const AttributeList& rAttribs )
+{
+ CondFormatRef xCondFmt = createCondFormat();
+ xCondFmt->importConditionalFormatting( rAttribs );
+ return xCondFmt;
+}
+
+namespace {
+
+ScConditionalFormat* findFormatByRange(const ScRangeList& rRange, const ScDocument* pDoc, SCTAB nTab)
+{
+ ScConditionalFormatList* pList = pDoc->GetCondFormList(nTab);
+ for (auto const& it : *pList)
+ {
+ if (it->GetRange() == rRange)
+ {
+ return it.get();
+ }
+ }
+
+ return nullptr;
+}
+
+class ScRangeListHasher
+{
+public:
+ size_t operator() (ScRangeList const& rRanges) const
+ {
+ size_t nHash = 0;
+ for (size_t nIdx = 0; nIdx < rRanges.size(); ++nIdx)
+ nHash += rRanges[nIdx].hashArea();
+ return nHash;
+ }
+};
+
+}
+
+void CondFormatBuffer::finalizeImport()
+{
+ std::unordered_set<size_t> aDoneExtCFs;
+ typedef std::unordered_map<ScRangeList, CondFormat*, ScRangeListHasher> RangeMap;
+ RangeMap aRangeMap;
+ for (auto& rxCondFormat : maCondFormats)
+ {
+ if (aRangeMap.find(rxCondFormat->getRanges()) != aRangeMap.end())
+ continue;
+ aRangeMap[rxCondFormat->getRanges()] = rxCondFormat.get();
+ }
+
+ size_t nExtCFIndex = 0;
+ for (const auto& rxExtCondFormat : maExtCondFormats)
+ {
+ ScDocument* pDoc = &getScDocument();
+ const ScRangeList& rRange = rxExtCondFormat->getRange();
+ RangeMap::iterator it = aRangeMap.find(rRange);
+ if (it != aRangeMap.end())
+ {
+ CondFormat& rCondFormat = *it->second;
+ const std::vector<std::unique_ptr<ScFormatEntry>>& rEntries = rxExtCondFormat->getEntries();
+ const std::vector<sal_Int32>& rPriorities = rxExtCondFormat->getPriorities();
+ size_t nEntryIdx = 0;
+ for (const auto& rxEntry : rEntries)
+ {
+ CondFormatRuleRef xRule = rCondFormat.createRule();
+ ScFormatEntry* pNewEntry = rxEntry->Clone(pDoc);
+ sal_Int32 nPriority = rPriorities[nEntryIdx];
+ if (nPriority == -1)
+ nPriority = mnNonPrioritizedRuleNextPriority++;
+ xRule->setFormatEntry(nPriority, pNewEntry);
+ rCondFormat.insertRule(xRule);
+ ++nEntryIdx;
+ }
+
+ aDoneExtCFs.insert(nExtCFIndex);
+ }
+
+ ++nExtCFIndex;
+ }
+
+ for( const auto& rxCondFormat : maCondFormats )
+ {
+ if ( rxCondFormat)
+ rxCondFormat->finalizeImport();
+ }
+ for ( const auto& rxCfRule : maCfRules )
+ {
+ if ( rxCfRule )
+ rxCfRule->finalizeImport();
+ }
+
+ nExtCFIndex = 0;
+ for (const auto& rxExtCondFormat : maExtCondFormats)
+ {
+ if (aDoneExtCFs.count(nExtCFIndex))
+ {
+ ++nExtCFIndex;
+ continue;
+ }
+
+ ScDocument* pDoc = &getScDocument();
+ const ScRangeList& rRange = rxExtCondFormat->getRange();
+ SCTAB nTab = rRange.front().aStart.Tab();
+ ScConditionalFormat* pFormat = findFormatByRange(rRange, pDoc, nTab);
+ if (!pFormat)
+ {
+ // create new conditional format and insert it
+ auto pNewFormat = std::make_unique<ScConditionalFormat>(0, pDoc);
+ pFormat = pNewFormat.get();
+ pNewFormat->SetRange(rRange);
+ sal_uLong nKey = pDoc->AddCondFormat(std::move(pNewFormat), nTab);
+ pDoc->AddCondFormatData(rRange, nTab, nKey);
+ }
+
+ const std::vector< std::unique_ptr<ScFormatEntry> >& rEntries = rxExtCondFormat->getEntries();
+ for (const auto& rxEntry : rEntries)
+ {
+ pFormat->AddEntry(rxEntry->Clone(pDoc));
+ }
+
+ ++nExtCFIndex;
+ }
+
+ rStyleIdx = 0; // Resets <extlst> <cfRule> style index.
+}
+
+CondFormatRef CondFormatBuffer::importCondFormatting( SequenceInputStream& rStrm )
+{
+ CondFormatRef xCondFmt = createCondFormat();
+ xCondFmt->importCondFormatting( rStrm );
+ return xCondFmt;
+}
+
+ExtCfDataBarRuleRef CondFormatBuffer::createExtCfDataBarRule(ScDataBarFormatData* pTarget)
+{
+ ExtCfDataBarRuleRef extRule = std::make_shared<ExtCfDataBarRule>( pTarget, *this );
+ maCfRules.push_back( extRule );
+ return extRule;
+}
+
+std::vector< std::unique_ptr<ExtCfCondFormat> >& CondFormatBuffer::importExtCondFormat()
+{
+ return maExtCondFormats;
+}
+
+sal_Int32 CondFormatBuffer::convertToApiOperator( sal_Int32 nToken )
+{
+ switch( nToken )
+ {
+ case XML_between: return ConditionOperator2::BETWEEN;
+ case XML_equal: return ConditionOperator2::EQUAL;
+ case XML_greaterThan: return ConditionOperator2::GREATER;
+ case XML_greaterThanOrEqual: return ConditionOperator2::GREATER_EQUAL;
+ case XML_lessThan: return ConditionOperator2::LESS;
+ case XML_lessThanOrEqual: return ConditionOperator2::LESS_EQUAL;
+ case XML_notBetween: return ConditionOperator2::NOT_BETWEEN;
+ case XML_notEqual: return ConditionOperator2::NOT_EQUAL;
+ case XML_duplicateValues: return ConditionOperator2::DUPLICATE;
+ }
+ return ConditionOperator2::NONE;
+}
+
+ScConditionMode CondFormatBuffer::convertToInternalOperator( sal_Int32 nToken )
+{
+ switch( nToken )
+ {
+ case XML_between: return ScConditionMode::Between;
+ case XML_equal: return ScConditionMode::Equal;
+ case XML_greaterThan: return ScConditionMode::Greater;
+ case XML_greaterThanOrEqual: return ScConditionMode::EqGreater;
+ case XML_lessThan: return ScConditionMode::Less;
+ case XML_lessThanOrEqual: return ScConditionMode::EqLess;
+ case XML_notBetween: return ScConditionMode::NotBetween;
+ case XML_notEqual: return ScConditionMode::NotEqual;
+ case XML_duplicateValues: return ScConditionMode::Duplicate;
+ case XML_uniqueValues: return ScConditionMode::NotDuplicate;
+ }
+ return ScConditionMode::NONE;
+}
+
+// private --------------------------------------------------------------------
+
+CondFormatRef CondFormatBuffer::createCondFormat()
+{
+ CondFormatRef xCondFmt = std::make_shared<CondFormat>( *this );
+ maCondFormats.push_back( xCondFmt );
+ return xCondFmt;
+}
+
+ExtCfDataBarRule::ExtCfDataBarRule(ScDataBarFormatData* pTarget, const WorksheetHelper& rParent):
+ WorksheetHelper(rParent),
+ mnRuleType( ExtCfDataBarRule::UNKNOWN ),
+ mpTarget(pTarget)
+{
+}
+
+void ExtCfDataBarRule::finalizeImport()
+{
+ switch ( mnRuleType )
+ {
+ case DATABAR:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ if( maModel.maAxisPosition == "none" )
+ pDataBar->meAxisPosition = databar::NONE;
+ else if( maModel.maAxisPosition == "middle" )
+ pDataBar->meAxisPosition = databar::MIDDLE;
+ else
+ pDataBar->meAxisPosition = databar::AUTOMATIC;
+ pDataBar->mbGradient = maModel.mbGradient;
+ break;
+ }
+ case AXISCOLOR:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ pDataBar->maAxisColor = maModel.mnAxisColor;
+ break;
+ }
+ case NEGATIVEFILLCOLOR:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ pDataBar->mxNegativeColor = maModel.mnNegativeColor;
+ pDataBar->mbNeg = true;
+ break;
+ }
+ case CFVO:
+ {
+ ScDataBarFormatData* pDataBar = mpTarget;
+ ScColorScaleEntry* pEntry = nullptr;
+ if(maModel.mbIsLower)
+ pEntry = pDataBar->mpLowerLimit.get();
+ else
+ pEntry = pDataBar->mpUpperLimit.get();
+
+ if(maModel.maColorScaleType == "min")
+ pEntry->SetType(COLORSCALE_MIN);
+ else if (maModel.maColorScaleType == "max")
+ pEntry->SetType(COLORSCALE_MAX);
+ else if (maModel.maColorScaleType == "autoMin")
+ pEntry->SetType(COLORSCALE_AUTO);
+ else if (maModel.maColorScaleType == "autoMax")
+ pEntry->SetType(COLORSCALE_AUTO);
+ else if (maModel.maColorScaleType == "percentile")
+ pEntry->SetType(COLORSCALE_PERCENTILE);
+ else if (maModel.maColorScaleType == "percent")
+ pEntry->SetType(COLORSCALE_PERCENT);
+ else if (maModel.maColorScaleType == "formula")
+ pEntry->SetType(COLORSCALE_FORMULA);
+ break;
+ }
+ case UNKNOWN: // nothing to do
+ default:
+ break;
+ }
+}
+
+void ExtCfDataBarRule::importDataBar( const AttributeList& rAttribs )
+{
+ mnRuleType = DATABAR;
+ maModel.mbGradient = rAttribs.getBool( XML_gradient, true );
+ maModel.maAxisPosition = rAttribs.getString( XML_axisPosition, "automatic" );
+}
+
+void ExtCfDataBarRule::importNegativeFillColor( const AttributeList& rAttribs )
+{
+ mnRuleType = NEGATIVEFILLCOLOR;
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+ maModel.mnNegativeColor = aColor;
+}
+
+void ExtCfDataBarRule::importAxisColor( const AttributeList& rAttribs )
+{
+ mnRuleType = AXISCOLOR;
+ ThemeBuffer& rThemeBuffer = getTheme();
+ GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color aColor = importOOXColor(rAttribs, rThemeBuffer, rGraphicHelper);
+ maModel.mnAxisColor = aColor;
+}
+
+void ExtCfDataBarRule::importCfvo( const AttributeList& rAttribs )
+{
+ mnRuleType = CFVO;
+ maModel.maColorScaleType = rAttribs.getString( XML_type, OUString() );
+}
+
+ExtCfCondFormat::ExtCfCondFormat(const ScRangeList& rRange, std::vector< std::unique_ptr<ScFormatEntry> >& rEntries,
+ const std::vector<sal_Int32>* pPriorities):
+ maRange(rRange)
+{
+ maEntries.swap(rEntries);
+ if (pPriorities)
+ maPriorities = *pPriorities;
+ else
+ maPriorities.resize(maEntries.size(), -1);
+}
+
+ExtCfCondFormat::~ExtCfCondFormat()
+{
+}
+
+const ScRangeList& ExtCfCondFormat::getRange() const
+{
+ return maRange;
+}
+
+const std::vector< std::unique_ptr<ScFormatEntry> >& ExtCfCondFormat::getEntries() const
+{
+ return maEntries;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/condformatcontext.cxx b/sc/source/filter/oox/condformatcontext.cxx
new file mode 100644
index 000000000..9c9a47065
--- /dev/null
+++ b/sc/source/filter/oox/condformatcontext.cxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <condformatcontext.hxx>
+#include <extlstcontext.hxx>
+
+#include <biffhelper.hxx>
+#include <condformatbuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+ColorScaleContext::ColorScaleContext( CondFormatContext& rFragment, CondFormatRuleRef const & xRule ) :
+ WorksheetContextBase( rFragment ),
+ mxRule( xRule )
+{
+}
+
+ContextHandlerRef ColorScaleContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cfRule ):
+ return (nElement == XLS_TOKEN( colorScale )) ? this : nullptr;
+ case XLS_TOKEN( colorScale ):
+ if (nElement == XLS_TOKEN( cfvo ))
+ return this;
+ else if (nElement == XLS_TOKEN( color ))
+ return this;
+ else
+ return nullptr;
+ }
+ return nullptr;
+}
+
+void ColorScaleContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cfvo ):
+ mxRule->getColorScale()->importCfvo( rAttribs );
+ break;
+ case XLS_TOKEN( color ):
+ mxRule->getColorScale()->importColor( rAttribs );
+ break;
+ }
+}
+
+DataBarContext::DataBarContext( CondFormatContext& rFragment, CondFormatRuleRef const & xRule ) :
+ WorksheetContextBase( rFragment ),
+ mxRule( xRule )
+{
+}
+
+ContextHandlerRef DataBarContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cfRule ):
+ return (nElement == XLS_TOKEN( dataBar )) ? this : nullptr;
+ case XLS_TOKEN( dataBar ):
+ if (nElement == XLS_TOKEN( cfvo ))
+ return this;
+ else if (nElement == XLS_TOKEN( color ))
+ return this;
+ else
+ return nullptr;
+ }
+ return nullptr;
+}
+
+void DataBarContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( dataBar ):
+ mxRule->getDataBar()->importAttribs( rAttribs );
+ break;
+ case XLS_TOKEN( cfvo ):
+ mxRule->getDataBar()->importCfvo( rAttribs );
+ break;
+ case XLS_TOKEN( color ):
+ mxRule->getDataBar()->importColor( rAttribs );
+ break;
+ }
+}
+
+IconSetContext::IconSetContext(WorksheetContextBase& rParent, IconSetRule* pIconSet) :
+ WorksheetContextBase(rParent),
+ mpIconSet(pIconSet)
+{
+}
+
+ContextHandlerRef IconSetContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cfRule ):
+ case XLS14_TOKEN( cfRule ):
+ return (nElement == XLS_TOKEN( iconSet ) || nElement == XLS14_TOKEN(iconSet)) ? this : nullptr;
+ case XLS_TOKEN( iconSet ):
+ case XLS14_TOKEN(iconSet):
+ if (nElement == XLS_TOKEN( cfvo ) ||
+ nElement == XLS14_TOKEN(cfvo) ||
+ nElement == XLS14_TOKEN(cfIcon))
+ return this;
+ else
+ return nullptr;
+ case XLS14_TOKEN(cfvo):
+ if (nElement == XM_TOKEN(f))
+ return this;
+ }
+ return nullptr;
+}
+
+void IconSetContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( iconSet ):
+ case XLS14_TOKEN( iconSet ):
+ mpIconSet->importAttribs( rAttribs );
+ break;
+ case XLS_TOKEN( cfvo ):
+ case XLS14_TOKEN( cfvo ):
+ mpIconSet->importCfvo( rAttribs );
+ break;
+ case XLS14_TOKEN(cfIcon):
+ mpIconSet->importIcon(rAttribs);
+ break;
+ }
+}
+
+void IconSetContext::onCharacters(const OUString& rChars)
+{
+ maChars = rChars;
+}
+
+void IconSetContext::onEndElement()
+{
+ switch(getCurrentElement())
+ {
+ case XM_TOKEN(f):
+ mpIconSet->importFormula(maChars);
+ maChars = OUString();
+ break;
+ }
+}
+
+CondFormatContext::CondFormatContext( WorksheetFragmentBase& rFragment ) :
+ WorksheetContextBase( rFragment )
+{
+}
+
+ContextHandlerRef CondFormatContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( conditionalFormatting ):
+ return (nElement == XLS_TOKEN( cfRule )) ? this : nullptr;
+ case XLS_TOKEN( cfRule ):
+ if (nElement == XLS_TOKEN( formula ))
+ return this;
+ else if (nElement == XLS_TOKEN( colorScale ) )
+ return new ColorScaleContext( *this, mxRule );
+ else if (nElement == XLS_TOKEN( dataBar ) )
+ return new DataBarContext( *this, mxRule );
+ else if (nElement == XLS_TOKEN( iconSet ) )
+ return new IconSetContext(*this, mxRule->getIconSet());
+ else if (nElement == XLS_TOKEN( extLst ) )
+ return new ExtLstLocalContext( *this, mxRule->getDataBar()->getDataBarFormatData() );
+ else
+ return nullptr;
+ }
+ return nullptr;
+}
+
+void CondFormatContext::onEndElement()
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( conditionalFormatting ):
+ if(mxCondFmt)
+ mxCondFmt->setReadyForFinalize();
+ break;
+ }
+}
+
+void CondFormatContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( conditionalFormatting ):
+ mxCondFmt = getCondFormats().importConditionalFormatting( rAttribs );
+ break;
+ case XLS_TOKEN( cfRule ):
+ if( mxCondFmt ) mxRule = mxCondFmt->importCfRule( rAttribs );
+ break;
+ }
+}
+
+void CondFormatContext::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( formula ) ) && mxCondFmt && mxRule )
+ mxRule->appendFormula( rChars );
+}
+
+ContextHandlerRef CondFormatContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_CONDFORMATTING:
+ return (nRecId == BIFF12_ID_CFRULE) ? this : nullptr;
+ }
+ return nullptr;
+}
+
+void CondFormatContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_CONDFORMATTING:
+ mxCondFmt = getCondFormats().importCondFormatting( rStrm );
+ break;
+ case BIFF12_ID_CFRULE:
+ if( mxCondFmt ) mxCondFmt->importCfRule( rStrm );
+ break;
+ }
+}
+
+void CondFormatContext::onEndRecord()
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_CONDFORMATTING:
+ if( mxCondFmt )
+ {
+ mxCondFmt->setReadyForFinalize();
+ }
+ break;
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/connectionsbuffer.cxx b/sc/source/filter/oox/connectionsbuffer.cxx
new file mode 100644
index 000000000..c8a2b700a
--- /dev/null
+++ b/sc/source/filter/oox/connectionsbuffer.cxx
@@ -0,0 +1,315 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <connectionsbuffer.hxx>
+#include <biffhelper.hxx>
+
+#include <osl/diagnose.h>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_Int32 BIFF12_RECONNECT_AS_REQUIRED = 1;
+
+const sal_uInt8 BIFF12_CONNECTION_SAVEPASSWORD_ON = 1;
+
+const sal_uInt16 BIFF12_CONNECTION_KEEPALIVE = 0x0001;
+const sal_uInt16 BIFF12_CONNECTION_NEW = 0x0002;
+const sal_uInt16 BIFF12_CONNECTION_DELETED = 0x0004;
+const sal_uInt16 BIFF12_CONNECTION_ONLYUSECONNFILE = 0x0008;
+const sal_uInt16 BIFF12_CONNECTION_BACKGROUND = 0x0010;
+const sal_uInt16 BIFF12_CONNECTION_REFRESHONLOAD = 0x0020;
+const sal_uInt16 BIFF12_CONNECTION_SAVEDATA = 0x0040;
+
+const sal_uInt16 BIFF12_CONNECTION_HAS_SOURCEFILE = 0x0001;
+const sal_uInt16 BIFF12_CONNECTION_HAS_SOURCECONNFILE = 0x0002;
+const sal_uInt16 BIFF12_CONNECTION_HAS_DESCRIPTION = 0x0004;
+const sal_uInt16 BIFF12_CONNECTION_HAS_NAME = 0x0008;
+const sal_uInt16 BIFF12_CONNECTION_HAS_SSOID = 0x0010;
+
+const sal_uInt32 BIFF12_WEBPR_XML = 0x00000100;
+const sal_uInt32 BIFF12_WEBPR_SOURCEDATA = 0x00000200;
+const sal_uInt32 BIFF12_WEBPR_PARSEPRE = 0x00000400;
+const sal_uInt32 BIFF12_WEBPR_CONSECUTIVE = 0x00000800;
+const sal_uInt32 BIFF12_WEBPR_FIRSTROW = 0x00001000;
+const sal_uInt32 BIFF12_WEBPR_XL97CREATED = 0x00002000;
+const sal_uInt32 BIFF12_WEBPR_TEXTDATES = 0x00004000;
+const sal_uInt32 BIFF12_WEBPR_XL2000REFRESHED = 0x00008000;
+const sal_uInt32 BIFF12_WEBPR_HTMLTABLES = 0x00010000;
+
+const sal_uInt8 BIFF12_WEBPR_HAS_POSTMETHOD = 0x01;
+const sal_uInt8 BIFF12_WEBPR_HAS_EDITPAGE = 0x02;
+const sal_uInt8 BIFF12_WEBPR_HAS_URL = 0x04;
+
+} // namespace
+
+WebPrModel::WebPrModel() :
+ mnHtmlFormat( XML_none ),
+ mbXml( false ),
+ mbSourceData( false ),
+ mbParsePre( false ),
+ mbConsecutive( false ),
+ mbFirstRow( false ),
+ mbXl97Created( false ),
+ mbTextDates( false ),
+ mbXl2000Refreshed( false ),
+ mbHtmlTables( false )
+{
+}
+
+ConnectionModel::ConnectionModel() :
+ mnId( -1 ),
+ mnType( BIFF12_CONNECTION_UNKNOWN ),
+ mnReconnectMethod( BIFF12_RECONNECT_AS_REQUIRED ),
+ mnCredentials( XML_integrated ),
+ mnInterval( 0 ),
+ mbKeepAlive( false ),
+ mbNew( false ),
+ mbDeleted( false ),
+ mbOnlyUseConnFile( false ),
+ mbBackground( false ),
+ mbRefreshOnLoad( false ),
+ mbSaveData( false ),
+ mbSavePassword( false )
+{
+}
+
+WebPrModel& ConnectionModel::createWebPr()
+{
+ OSL_ENSURE( !mxWebPr, "ConnectionModel::createWebPr - multiple call" );
+ mxWebPr.reset( new WebPrModel );
+ return *mxWebPr;
+}
+
+Connection::Connection( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+ maModel.mnId = -1;
+}
+
+void Connection::importConnection( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.maDescription = rAttribs.getXString( XML_description, OUString() );
+ maModel.maSourceFile = rAttribs.getXString( XML_sourceFile, OUString() );
+ maModel.maSourceConnFile = rAttribs.getXString( XML_odcFile, OUString() );
+ maModel.maSsoId = rAttribs.getXString( XML_singleSignOnId, OUString() );
+ maModel.mnId = rAttribs.getInteger( XML_id, -1 );
+ // type and reconnectionMethod are using the BIFF12 constants instead of XML tokens
+ maModel.mnType = rAttribs.getInteger( XML_type, BIFF12_CONNECTION_UNKNOWN );
+ maModel.mnReconnectMethod = rAttribs.getInteger( XML_reconnectionMethod, BIFF12_RECONNECT_AS_REQUIRED );
+ maModel.mnCredentials = rAttribs.getToken( XML_credentials, XML_integrated );
+ maModel.mnInterval = rAttribs.getInteger( XML_interval, 0 );
+ maModel.mbKeepAlive = rAttribs.getBool( XML_keepAlive, false );
+ maModel.mbNew = rAttribs.getBool( XML_new, false );
+ maModel.mbDeleted = rAttribs.getBool( XML_deleted, false );
+ maModel.mbOnlyUseConnFile = rAttribs.getBool( XML_onlyUseConnectionFile, false );
+ maModel.mbBackground = rAttribs.getBool( XML_background, false );
+ maModel.mbRefreshOnLoad = rAttribs.getBool( XML_refreshOnLoad, false );
+ maModel.mbSaveData = rAttribs.getBool( XML_saveData, false );
+ maModel.mbSavePassword = rAttribs.getBool( XML_savePassword, false );
+}
+
+void Connection::importWebPr( const AttributeList& rAttribs )
+{
+ WebPrModel& rWebPr = maModel.createWebPr();
+
+ rWebPr.maUrl = rAttribs.getXString( XML_url, OUString() );
+ rWebPr.maPostMethod = rAttribs.getXString( XML_post, OUString() );
+ rWebPr.maEditPage = rAttribs.getXString( XML_editPage, OUString() );
+ rWebPr.mnHtmlFormat = rAttribs.getToken( XML_htmlFormat, XML_none );
+ rWebPr.mbXml = rAttribs.getBool( XML_xml, false );
+ rWebPr.mbSourceData = rAttribs.getBool( XML_sourceData, false );
+ rWebPr.mbParsePre = rAttribs.getBool( XML_parsePre, false );
+ rWebPr.mbConsecutive = rAttribs.getBool( XML_consecutive, false );
+ rWebPr.mbFirstRow = rAttribs.getBool( XML_firstRow, false );
+ rWebPr.mbXl97Created = rAttribs.getBool( XML_xl97, false );
+ rWebPr.mbTextDates = rAttribs.getBool( XML_textDates, false );
+ rWebPr.mbXl2000Refreshed = rAttribs.getBool( XML_xl2000, false );
+ rWebPr.mbHtmlTables = rAttribs.getBool( XML_htmlTables, false );
+}
+
+void Connection::importTables()
+{
+ if( maModel.mxWebPr )
+ {
+ OSL_ENSURE( maModel.mxWebPr->maTables.empty(), "Connection::importTables - multiple calls" );
+ maModel.mxWebPr->maTables.clear();
+ }
+}
+
+void Connection::importTable( const AttributeList& rAttribs, sal_Int32 nElement )
+{
+ if( !maModel.mxWebPr )
+ return;
+
+ Any aTableAny;
+ switch( nElement )
+ {
+ case XLS_TOKEN( m ): break;
+ case XLS_TOKEN( s ): aTableAny <<= rAttribs.getXString( XML_v, OUString() ); break;
+ case XLS_TOKEN( x ): aTableAny <<= rAttribs.getInteger( XML_v, -1 ); break;
+ default:
+ OSL_ENSURE( false, "Connection::importTable - unexpected element" );
+ return;
+ }
+ maModel.mxWebPr->maTables.push_back( aTableAny );
+}
+
+void Connection::importConnection( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags, nStrFlags;
+ sal_uInt8 nSavePassword, nCredentials;
+ rStrm.skip( 2 );
+ nSavePassword = rStrm.readuChar();
+ rStrm.skip( 1 );
+ maModel.mnInterval = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+ nStrFlags = rStrm.readuInt16();
+ maModel.mnType = rStrm.readInt32();
+ maModel.mnReconnectMethod = rStrm.readInt32();
+ maModel.mnId = rStrm.readInt32();
+ nCredentials = rStrm.readuChar();
+
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_SOURCEFILE ) )
+ rStrm >> maModel.maSourceFile;
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_SOURCECONNFILE ) )
+ rStrm >> maModel.maSourceConnFile;
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_DESCRIPTION ) )
+ rStrm >> maModel.maDescription;
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_NAME ) )
+ rStrm >> maModel.maName;
+ if( getFlag( nStrFlags, BIFF12_CONNECTION_HAS_SSOID ) )
+ rStrm >> maModel.maSsoId;
+
+ static const sal_Int32 spnCredentials[] = { XML_integrated, XML_none, XML_stored, XML_prompt };
+ maModel.mnCredentials = STATIC_ARRAY_SELECT( spnCredentials, nCredentials, XML_integrated );
+
+ maModel.mbKeepAlive = getFlag( nFlags, BIFF12_CONNECTION_KEEPALIVE );
+ maModel.mbNew = getFlag( nFlags, BIFF12_CONNECTION_NEW );
+ maModel.mbDeleted = getFlag( nFlags, BIFF12_CONNECTION_DELETED );
+ maModel.mbOnlyUseConnFile = getFlag( nFlags, BIFF12_CONNECTION_ONLYUSECONNFILE );
+ maModel.mbBackground = getFlag( nFlags, BIFF12_CONNECTION_BACKGROUND );
+ maModel.mbRefreshOnLoad = getFlag( nFlags, BIFF12_CONNECTION_REFRESHONLOAD );
+ maModel.mbSaveData = getFlag( nFlags, BIFF12_CONNECTION_SAVEDATA );
+ maModel.mbSavePassword = nSavePassword == BIFF12_CONNECTION_SAVEPASSWORD_ON;
+}
+
+void Connection::importWebPr( SequenceInputStream& rStrm )
+{
+ WebPrModel& rWebPr = maModel.createWebPr();
+
+ sal_uInt32 nFlags;
+ sal_uInt8 nStrFlags;
+ nFlags = rStrm.readuInt32();
+ nStrFlags = rStrm.readuChar();
+
+ if( getFlag( nStrFlags, BIFF12_WEBPR_HAS_URL ) )
+ rStrm >> rWebPr.maUrl;
+ if( getFlag( nStrFlags, BIFF12_WEBPR_HAS_POSTMETHOD ) )
+ rStrm >> rWebPr.maPostMethod;
+ if( getFlag( nStrFlags, BIFF12_WEBPR_HAS_EDITPAGE ) )
+ rStrm >> rWebPr.maEditPage;
+
+ static const sal_Int32 spnHmlFormats[] = { XML_none, XML_rtf, XML_all };
+ rWebPr.mnHtmlFormat = STATIC_ARRAY_SELECT( spnHmlFormats, extractValue< sal_uInt8 >( nFlags, 0, 8 ), XML_none );
+
+ rWebPr.mbXml = getFlag( nFlags, BIFF12_WEBPR_XML );
+ rWebPr.mbSourceData = getFlag( nFlags, BIFF12_WEBPR_SOURCEDATA );
+ rWebPr.mbParsePre = getFlag( nFlags, BIFF12_WEBPR_PARSEPRE );
+ rWebPr.mbConsecutive = getFlag( nFlags, BIFF12_WEBPR_CONSECUTIVE );
+ rWebPr.mbFirstRow = getFlag( nFlags, BIFF12_WEBPR_FIRSTROW );
+ rWebPr.mbXl97Created = getFlag( nFlags, BIFF12_WEBPR_XL97CREATED );
+ rWebPr.mbTextDates = getFlag( nFlags, BIFF12_WEBPR_TEXTDATES );
+ rWebPr.mbXl2000Refreshed = getFlag( nFlags, BIFF12_WEBPR_XL2000REFRESHED );
+ rWebPr.mbHtmlTables = getFlag( nFlags, BIFF12_WEBPR_HTMLTABLES );
+}
+
+void Connection::importWebPrTables( SequenceInputStream& /*rStrm*/ )
+{
+ if( maModel.mxWebPr )
+ {
+ OSL_ENSURE( maModel.mxWebPr->maTables.empty(), "Connection::importWebPrTables - multiple calls" );
+ maModel.mxWebPr->maTables.clear();
+ }
+}
+
+void Connection::importWebPrTable( SequenceInputStream& rStrm, sal_Int32 nRecId )
+{
+ if( !maModel.mxWebPr )
+ return;
+
+ Any aTableAny;
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCITEM_MISSING: break;
+ case BIFF12_ID_PCITEM_STRING: aTableAny <<= BiffHelper::readString( rStrm ); break;
+ case BIFF12_ID_PCITEM_INDEX: aTableAny <<= rStrm.readInt32(); break;
+ default:
+ OSL_ENSURE( false, "Connection::importWebPrTable - unexpected record" );
+ return;
+ }
+ maModel.mxWebPr->maTables.push_back( aTableAny );
+}
+
+ConnectionsBuffer::ConnectionsBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnUnusedId( 1 )
+{
+}
+
+Connection& ConnectionsBuffer::createConnection()
+{
+ ConnectionRef xConnection = std::make_shared<Connection>( *this );
+ maConnections.push_back( xConnection );
+ return *xConnection;
+}
+
+void ConnectionsBuffer::finalizeImport()
+{
+ for( const auto& rxConnection : maConnections )
+ insertConnectionToMap( rxConnection );
+}
+
+ConnectionRef ConnectionsBuffer::getConnection( sal_Int32 nConnId ) const
+{
+ return maConnectionsById.get( nConnId );
+}
+
+void ConnectionsBuffer::insertConnectionToMap( const ConnectionRef& rxConnection )
+{
+ sal_Int32 nConnId = rxConnection->getConnectionId();
+ if( nConnId > 0 )
+ {
+ OSL_ENSURE( !maConnectionsById.has( nConnId ), "ConnectionsBuffer::insertConnectionToMap - multiple connection identifier" );
+ maConnectionsById[ nConnId ] = rxConnection;
+ mnUnusedId = ::std::max< sal_Int32 >( mnUnusedId, nConnId + 1 );
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/connectionsfragment.cxx b/sc/source/filter/oox/connectionsfragment.cxx
new file mode 100644
index 000000000..3fafefd1d
--- /dev/null
+++ b/sc/source/filter/oox/connectionsfragment.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <connectionsfragment.hxx>
+
+#include <oox/token/namespaces.hxx>
+#include <biffhelper.hxx>
+#include <connectionsbuffer.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+ConnectionContext::ConnectionContext( WorkbookFragmentBase& rParent, Connection& rConnection ) :
+ WorkbookContextBase( rParent ),
+ mrConnection( rConnection )
+{
+}
+
+ContextHandlerRef ConnectionContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( connection ):
+ if( nElement == XLS_TOKEN( webPr ) )
+ {
+ mrConnection.importWebPr( rAttribs );
+ return this;
+ }
+ break;
+
+ case XLS_TOKEN( webPr ):
+ if( nElement == XLS_TOKEN( tables ) )
+ {
+ mrConnection.importTables();
+ return this;
+ }
+ break;
+
+ case XLS_TOKEN( tables ):
+ mrConnection.importTable( rAttribs, nElement );
+ break;
+ }
+ return nullptr;
+}
+
+void ConnectionContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( getCurrentElement() == XLS_TOKEN( connection ) )
+ mrConnection.importConnection( rAttribs );
+}
+
+ContextHandlerRef ConnectionContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_CONNECTION:
+ if( nRecId == BIFF12_ID_WEBPR )
+ {
+ mrConnection.importWebPr( rStrm );
+ return this;
+ }
+ break;
+
+ case BIFF12_ID_WEBPR:
+ if( nRecId == BIFF12_ID_WEBPRTABLES )
+ {
+ mrConnection.importWebPrTables( rStrm );
+ return this;
+ }
+ break;
+
+ case BIFF12_ID_WEBPRTABLES:
+ mrConnection.importWebPrTable( rStrm, nRecId );
+ break;
+ }
+ return nullptr;
+}
+
+void ConnectionContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( getCurrentElement() == BIFF12_ID_CONNECTION )
+ mrConnection.importConnection( rStrm );
+}
+
+ConnectionsFragment::ConnectionsFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef ConnectionsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( connections ) )
+ return this;
+ break;
+
+ case XLS_TOKEN( connections ):
+ if( nElement == XLS_TOKEN( connection ) )
+ return new ConnectionContext( *this, getConnections().createConnection() );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef ConnectionsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& /*rStrm*/ )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_CONNECTIONS )
+ return this;
+ break;
+
+ case BIFF12_ID_CONNECTIONS:
+ if( nRecId == BIFF12_ID_CONNECTION )
+ return new ConnectionContext( *this, getConnections().createConnection() );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* ConnectionsFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_CONNECTIONS, BIFF12_ID_CONNECTIONS + 1 },
+ { BIFF12_ID_CONNECTION, BIFF12_ID_CONNECTION + 1 },
+ { BIFF12_ID_WEBPR, BIFF12_ID_WEBPR + 1 },
+ { BIFF12_ID_WEBPRTABLES, BIFF12_ID_WEBPRTABLES + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void ConnectionsFragment::finalizeImport()
+{
+ getConnections().finalizeImport();
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/defnamesbuffer.cxx b/sc/source/filter/oox/defnamesbuffer.cxx
new file mode 100644
index 000000000..72f28cd4e
--- /dev/null
+++ b/sc/source/filter/oox/defnamesbuffer.cxx
@@ -0,0 +1,442 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <defnamesbuffer.hxx>
+
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <externallinkbuffer.hxx>
+#include <formulabase.hxx>
+#include <formulaparser.hxx>
+#include <worksheetbuffer.hxx>
+#include <tokenarray.hxx>
+#include <tokenuno.hxx>
+#include <compiler.hxx>
+#include <document.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt32 BIFF12_DEFNAME_HIDDEN = 0x00000001;
+const sal_uInt32 BIFF12_DEFNAME_FUNC = 0x00000002;
+const sal_uInt32 BIFF12_DEFNAME_VBNAME = 0x00000004;
+const sal_uInt32 BIFF12_DEFNAME_MACRO = 0x00000008;
+const sal_uInt32 BIFF12_DEFNAME_BUILTIN = 0x00000020;
+
+constexpr OUStringLiteral spcOoxPrefix(u"_xlnm.");
+
+const char* const sppcBaseNames[] =
+{
+ "Consolidate_Area",
+ "Auto_Open",
+ "Auto_Close",
+ "Extract",
+ "Database",
+ "Criteria",
+ "Print_Area",
+ "Print_Titles",
+ "Recorder",
+ "Data_Form",
+ "Auto_Activate",
+ "Auto_Deactivate",
+ "Sheet_Title",
+ "_FilterDatabase"
+};
+
+OUString lclGetBaseName( sal_Unicode cBuiltinId )
+{
+ OSL_ENSURE( cBuiltinId < SAL_N_ELEMENTS( sppcBaseNames ), "lclGetBaseName - unsupported built-in identifier" );
+ OUStringBuffer aBuffer;
+ if( cBuiltinId < SAL_N_ELEMENTS( sppcBaseNames ) )
+ aBuffer.appendAscii( sppcBaseNames[ cBuiltinId ] );
+ else
+ aBuffer.append( static_cast< sal_Int32 >( cBuiltinId ) );
+ return aBuffer.makeStringAndClear();
+}
+
+OUString lclGetPrefixedName( sal_Unicode cBuiltinId )
+{
+ return spcOoxPrefix + lclGetBaseName( cBuiltinId );
+}
+
+/** returns the built-in name identifier from a prefixed built-in name, e.g. '_xlnm.Print_Area'. */
+sal_Unicode lclGetBuiltinIdFromPrefixedName( const OUString& rModelName )
+{
+ if( rModelName.matchIgnoreAsciiCase( spcOoxPrefix ) )
+ {
+ for( sal_Unicode cBuiltinId = 0; cBuiltinId < SAL_N_ELEMENTS( sppcBaseNames ); ++cBuiltinId )
+ {
+ OUString aBaseName = lclGetBaseName( cBuiltinId );
+ sal_Int32 nBaseNameLen = aBaseName.getLength();
+ if( (rModelName.getLength() == spcOoxPrefix.getLength() + nBaseNameLen) && rModelName.matchIgnoreAsciiCase( aBaseName, spcOoxPrefix.getLength() ) )
+ return cBuiltinId;
+ }
+ }
+ return BIFF_DEFNAME_UNKNOWN;
+}
+
+/** returns the built-in name identifier from a built-in base name, e.g. 'Print_Area'. */
+sal_Unicode lclGetBuiltinIdFromBaseName( const OUString& rModelName )
+{
+ for( sal_Unicode cBuiltinId = 0; cBuiltinId < SAL_N_ELEMENTS( sppcBaseNames ); ++cBuiltinId )
+ if( rModelName.equalsIgnoreAsciiCaseAscii( sppcBaseNames[ cBuiltinId ] ) )
+ return cBuiltinId;
+ return BIFF_DEFNAME_UNKNOWN;
+}
+
+OUString lclGetUpcaseModelName( const OUString& rModelName )
+{
+ // TODO: i18n?
+ return rModelName.toAsciiUpperCase();
+}
+
+} // namespace
+
+DefinedNameModel::DefinedNameModel() :
+ mnSheet( -1 ),
+ mnFuncGroupId( -1 ),
+ mbMacro( false ),
+ mbFunction( false ),
+ mbVBName( false ),
+ mbHidden( false )
+{
+}
+
+DefinedNameBase::DefinedNameBase( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+const OUString& DefinedNameBase::getUpcaseModelName() const
+{
+ if( maUpModelName.isEmpty() )
+ maUpModelName = lclGetUpcaseModelName( maModel.maName );
+ return maUpModelName;
+}
+
+DefinedName::DefinedName( const WorkbookHelper& rHelper ) :
+ DefinedNameBase( rHelper ),
+ maScRangeData(nullptr, false),
+ mnTokenIndex( -1 ),
+ mnCalcSheet( 0 ),
+ mcBuiltinId( BIFF_DEFNAME_UNKNOWN )
+{
+}
+
+void DefinedName::importDefinedName( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.mnSheet = rAttribs.getInteger( XML_localSheetId, -1 );
+ maModel.mnFuncGroupId = rAttribs.getInteger( XML_functionGroupId, -1 );
+ maModel.mbMacro = rAttribs.getBool( XML_xlm, false );
+ maModel.mbFunction = rAttribs.getBool( XML_function, false );
+ maModel.mbVBName = rAttribs.getBool( XML_vbProcedure, false );
+ maModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+ mnCalcSheet = (maModel.mnSheet >= 0) ? getWorksheets().getCalcSheetIndex( maModel.mnSheet ) : -1;
+
+ /* Detect built-in state from name itself, there is no built-in flag.
+ Built-in names are prefixed with '_xlnm.' instead. */
+ mcBuiltinId = lclGetBuiltinIdFromPrefixedName( maModel.maName );
+}
+
+void DefinedName::setFormula( const OUString& rFormula )
+{
+ maModel.maFormula = rFormula;
+}
+
+void DefinedName::importDefinedName( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags;
+ nFlags = rStrm.readuInt32();
+ rStrm.skip( 1 ); // keyboard shortcut
+ maModel.mnSheet = rStrm.readInt32();
+ rStrm >> maModel.maName;
+ mnCalcSheet = (maModel.mnSheet >= 0) ? getWorksheets().getCalcSheetIndex( maModel.mnSheet ) : -1;
+
+ // macro function/command, hidden flag
+ maModel.mnFuncGroupId = extractValue< sal_Int32 >( nFlags, 6, 9 );
+ maModel.mbMacro = getFlag( nFlags, BIFF12_DEFNAME_MACRO );
+ maModel.mbFunction = getFlag( nFlags, BIFF12_DEFNAME_FUNC );
+ maModel.mbVBName = getFlag( nFlags, BIFF12_DEFNAME_VBNAME );
+ maModel.mbHidden = getFlag( nFlags, BIFF12_DEFNAME_HIDDEN );
+
+ // get built-in name index from name
+ if( getFlag( nFlags, BIFF12_DEFNAME_BUILTIN ) )
+ mcBuiltinId = lclGetBuiltinIdFromBaseName( maModel.maName );
+
+ // store token array data
+ sal_Int64 nRecPos = rStrm.tell();
+ sal_Int32 nFmlaSize = rStrm.readInt32();
+ rStrm.skip( nFmlaSize );
+ sal_Int32 nAddDataSize = rStrm.readInt32();
+ if( !rStrm.isEof() && (nFmlaSize > 0) && (nAddDataSize >= 0) && (rStrm.getRemaining() >= nAddDataSize) )
+ {
+ sal_Int32 nTotalSize = 8 + nFmlaSize + nAddDataSize;
+ mxFormula.reset( new StreamDataSequence );
+ rStrm.seek( nRecPos );
+ rStrm.readData( *mxFormula, nTotalSize );
+ }
+}
+
+void DefinedName::createNameObject( sal_Int32 nIndex )
+{
+ // do not create names for (macro) functions or VBA procedures
+ // #163146# do not ignore hidden names (may be regular names created by VBA scripts)
+ if( /*maModel.mbHidden ||*/ maModel.mbFunction || maModel.mbVBName )
+ return;
+
+ // convert original name to final Calc name (TODO: filter invalid characters from model name)
+ maCalcName = isBuiltinName() ? lclGetPrefixedName( mcBuiltinId ) : maModel.maName;
+
+ // #163146# do not rename sheet-local names by default, this breaks VBA scripts
+
+ // special flags for this name
+ sal_Int32 nNameFlags = 0;
+ using namespace ::com::sun::star::sheet::NamedRangeFlag;
+ if( !isGlobalName() ) switch( mcBuiltinId )
+ {
+ case BIFF_DEFNAME_CRITERIA:
+ case BIFF_DEFNAME_FILTERDATABASE: nNameFlags = FILTER_CRITERIA; break;
+ case BIFF_DEFNAME_PRINTAREA: nNameFlags = PRINT_AREA; break;
+ case BIFF_DEFNAME_PRINTTITLES: nNameFlags = COLUMN_HEADER | ROW_HEADER; break;
+ }
+
+ // create the name and insert it into the document, maCalcName will be changed to the resulting name
+ if (maModel.mnSheet >= 0)
+ maScRangeData = createLocalNamedRangeObject( maCalcName, ApiTokenSequence(), nIndex, nNameFlags, maModel.mnSheet, maModel.mbHidden );
+ else
+ maScRangeData = createNamedRangeObject( maCalcName, ApiTokenSequence(), nIndex, nNameFlags, maModel.mbHidden );
+ mnTokenIndex = nIndex;
+}
+
+bool DefinedName::isValid(
+ const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks) const
+{
+ ScRange aRange;
+ OUString aExternDocName;
+ OUString aStartTabName;
+ OUString aEndTabName;
+ ScRefFlags nFlags = ScRefFlags::VALID | ScRefFlags::TAB_VALID;
+ aRange.Parse_XL_Header(maModel.maFormula.getStr(), getScDocument(), aExternDocName,
+ aStartTabName, aEndTabName, nFlags, /*bOnlyAcceptSingle=*/false,
+ &rExternalLinks);
+ // aExternDocName is something like 'file:///path/to/my.xlsx' in the valid case, and it's an int
+ // when it's invalid.
+ bool bInvalidExternalRef = aExternDocName.toInt32() > 0;
+ return !bInvalidExternalRef;
+}
+
+std::unique_ptr<ScTokenArray> DefinedName::getScTokens(
+ const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks )
+{
+ ScCompiler aCompiler(getScDocument(), ScAddress(0, 0, mnCalcSheet), formula::FormulaGrammar::GRAM_OOXML);
+ aCompiler.SetExternalLinks( rExternalLinks);
+ std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(maModel.maFormula));
+ // Compile the tokens into RPN once to populate information into tokens
+ // where necessary, e.g. for TableRef inner reference. RPN can be discarded
+ // after, a resulting error must be reset.
+ FormulaError nErr = pArray->GetCodeError();
+ aCompiler.CompileTokenArray();
+ getScDocument().CheckLinkFormulaNeedingCheck( *pArray);
+ pArray->DelRPN();
+ pArray->SetCodeError(nErr);
+
+ return pArray;
+}
+
+void DefinedName::convertFormula( const css::uno::Sequence<css::sheet::ExternalLinkInfo>& rExternalLinks )
+{
+ ScRangeData* pScRangeData = maScRangeData.first;
+ // macro function or vba procedure
+ if (!pScRangeData)
+ return;
+
+ // convert and set formula of the defined name
+ {
+ std::unique_ptr<ScTokenArray> pTokenArray = getScTokens( rExternalLinks);
+ pScRangeData->SetCode( *pTokenArray );
+ }
+
+ ScTokenArray* pTokenArray = pScRangeData->GetCode();
+ Sequence< FormulaToken > aFTokenSeq;
+ ScTokenConversion::ConvertToTokenSequence( getScDocument(), aFTokenSeq, *pTokenArray );
+ // set built-in names (print ranges, repeated titles, filter ranges)
+ if( isGlobalName() )
+ return;
+
+ switch( mcBuiltinId )
+ {
+ case BIFF_DEFNAME_PRINTAREA:
+ {
+ Reference< XPrintAreas > xPrintAreas( getSheetFromDoc( mnCalcSheet ), UNO_QUERY );
+ ScRangeList aPrintRanges;
+ getFormulaParser().extractCellRangeList( aPrintRanges, aFTokenSeq, mnCalcSheet );
+ if( xPrintAreas.is() && !aPrintRanges.empty() )
+ xPrintAreas->setPrintAreas( AddressConverter::toApiSequence(aPrintRanges) );
+ }
+ break;
+ case BIFF_DEFNAME_PRINTTITLES:
+ {
+ Reference< XPrintAreas > xPrintAreas( getSheetFromDoc( mnCalcSheet ), UNO_QUERY );
+ ScRangeList aTitleRanges;
+ getFormulaParser().extractCellRangeList( aTitleRanges, aFTokenSeq, mnCalcSheet );
+ if( xPrintAreas.is() && !aTitleRanges.empty() )
+ {
+ bool bHasRowTitles = false;
+ bool bHasColTitles = false;
+ const ScAddress& rMaxPos = getAddressConverter().getMaxAddress();
+ for (size_t i = 0, nSize = aTitleRanges.size(); i < nSize; ++i)
+ {
+ const ScRange& rRange = aTitleRanges[i];
+ bool bFullRow = (rRange.aStart.Col() == 0) && ( rRange.aEnd.Col() >= rMaxPos.Col() );
+ bool bFullCol = (rRange.aStart.Row() == 0) && ( rRange.aEnd.Row() >= rMaxPos.Row() );
+ if( !bHasRowTitles && bFullRow && !bFullCol )
+ {
+ xPrintAreas->setTitleRows( CellRangeAddress(rRange.aStart.Tab(),
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row()) );
+ xPrintAreas->setPrintTitleRows( true );
+ bHasRowTitles = true;
+ }
+ else if( !bHasColTitles && bFullCol && !bFullRow )
+ {
+ xPrintAreas->setTitleColumns( CellRangeAddress(rRange.aStart.Tab(),
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row()) );
+ xPrintAreas->setPrintTitleColumns( true );
+ bHasColTitles = true;
+ }
+ }
+ }
+ }
+ break;
+ }
+}
+
+bool DefinedName::getAbsoluteRange( ScRange& orRange ) const
+{
+ ScRangeData* pScRangeData = maScRangeData.first;
+ ScTokenArray* pTokenArray = pScRangeData->GetCode();
+ Sequence< FormulaToken > aFTokenSeq;
+ ScTokenConversion::ConvertToTokenSequence(getScDocument(), aFTokenSeq, *pTokenArray);
+ return getFormulaParser().extractCellRange( orRange, aFTokenSeq );
+}
+
+DefinedName::~DefinedName()
+{
+ // this kind of field is owned by us - see lcl_addNewByNameAndTokens
+ bool bOwned = maScRangeData.second;
+ if (bOwned)
+ delete maScRangeData.first;
+}
+
+DefinedNamesBuffer::DefinedNamesBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+DefinedNameRef DefinedNamesBuffer::importDefinedName( const AttributeList& rAttribs )
+{
+ DefinedNameRef xDefName = createDefinedName();
+ xDefName->importDefinedName( rAttribs );
+ return xDefName;
+}
+
+void DefinedNamesBuffer::importDefinedName( SequenceInputStream& rStrm )
+{
+ createDefinedName()->importDefinedName( rStrm );
+}
+
+void DefinedNamesBuffer::finalizeImport()
+{
+ // first insert all names without formula definition into the document, and insert them into the maps
+ int index = 0;
+ for( DefinedNameRef& xDefName : maDefNames )
+ {
+ if (!xDefName->isValid(getExternalLinks().getLinkInfos()))
+ {
+ continue;
+ }
+
+ xDefName->createNameObject( ++index );
+ // map by sheet index and original model name
+ maModelNameMap[ SheetNameKey( xDefName->getLocalCalcSheet(), xDefName->getUpcaseModelName() ) ] = xDefName;
+ // map by sheet index and built-in identifier
+ if( !xDefName->isGlobalName() && xDefName->isBuiltinName() )
+ maBuiltinMap[ BuiltinKey( xDefName->getLocalCalcSheet(), xDefName->getBuiltinId() ) ] = xDefName;
+ // map by API formula token identifier
+ sal_Int32 nTokenIndex = xDefName->getTokenIndex();
+ if( nTokenIndex >= 0 )
+ maTokenIdMap[ nTokenIndex ] = xDefName;
+ }
+
+ /* Now convert all name formulas, so that the formula parser can find all
+ names in case of circular dependencies. */
+ maDefNames.forEachMem( &DefinedName::convertFormula, getExternalLinks().getLinkInfos());
+}
+
+DefinedNameRef DefinedNamesBuffer::getByIndex( sal_Int32 nIndex ) const
+{
+ return maDefNames.get( nIndex );
+}
+
+DefinedNameRef DefinedNamesBuffer::getByTokenIndex( sal_Int32 nIndex ) const
+{
+ return maTokenIdMap.get( nIndex );
+}
+
+DefinedNameRef DefinedNamesBuffer::getByModelName( const OUString& rModelName, sal_Int16 nCalcSheet ) const
+{
+ OUString aUpcaseName = lclGetUpcaseModelName( rModelName );
+ DefinedNameRef xDefName = maModelNameMap.get( SheetNameKey( nCalcSheet, aUpcaseName ) );
+ // lookup global name, if no local name exists
+ if( !xDefName && (nCalcSheet >= 0) )
+ xDefName = maModelNameMap.get( SheetNameKey( -1, aUpcaseName ) );
+ return xDefName;
+}
+
+DefinedNameRef DefinedNamesBuffer::getByBuiltinId( sal_Unicode cBuiltinId, sal_Int16 nCalcSheet ) const
+{
+ return maBuiltinMap.get( BuiltinKey( nCalcSheet, cBuiltinId ) );
+}
+
+DefinedNameRef DefinedNamesBuffer::createDefinedName()
+{
+ DefinedNameRef xDefName = std::make_shared<DefinedName>( *this );
+ maDefNames.push_back( xDefName );
+ return xDefName;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/drawingbase.cxx b/sc/source/filter/oox/drawingbase.cxx
new file mode 100644
index 000000000..0f1ee8690
--- /dev/null
+++ b/sc/source/filter/oox/drawingbase.cxx
@@ -0,0 +1,295 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawingbase.hxx>
+#include <addressconverter.hxx>
+
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <unitconverter.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::drawingml;
+
+namespace {
+
+/** Converts the passed 32-bit integer value from 1/100 mm to EMUs. */
+sal_Int64 lclHmmToEmu( sal_Int32 nValue )
+{
+ return (nValue < 0) ? -1 : convertHmmToEmu( nValue );
+}
+
+/** Converts the passed 64-bit integer value from EMUs to 1/100 mm. */
+sal_Int32 lclEmuToHmm( sal_Int64 nValue )
+{
+ return (nValue < 0) ? -1 : convertEmuToHmm( nValue );
+}
+
+} // namespace
+
+CellAnchorModel::CellAnchorModel() :
+ mnCol( -1 ),
+ mnRow( -1 ),
+ mnColOffset( 0 ),
+ mnRowOffset( 0 )
+{
+}
+
+AnchorClientDataModel::AnchorClientDataModel() :
+ mbLocksWithSheet( true ),
+ mbPrintsWithSheet( true )
+{
+}
+
+ShapeAnchor::ShapeAnchor( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ meAnchorType( ANCHOR_INVALID ),
+ meCellAnchorType( CellAnchorType::Emu ),
+ meEditAs( ANCHOR_TWOCELL )
+{
+}
+
+void ShapeAnchor::importAnchor( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( nElement )
+ {
+ case XDR_TOKEN( absoluteAnchor ):
+ meAnchorType = ANCHOR_ABSOLUTE;
+ meEditAs = ANCHOR_ABSOLUTE;
+ break;
+ case XDR_TOKEN( oneCellAnchor ):
+ meAnchorType = ANCHOR_ONECELL;
+ meEditAs = ANCHOR_ONECELL;
+ break;
+ case XDR_TOKEN( twoCellAnchor ):
+ {
+ meAnchorType = ANCHOR_TWOCELL;
+ OUString sEditAs = rAttribs.getXString( XML_editAs, OUString() );
+ if ( !sEditAs.isEmpty() )
+ {
+ if ( sEditAs.equalsIgnoreAsciiCase("absolute" ) )
+ meEditAs = ANCHOR_ABSOLUTE;
+ else if ( sEditAs.equalsIgnoreAsciiCase("oneCell") )
+ meEditAs = ANCHOR_ONECELL;
+ else if (sEditAs.equalsIgnoreAsciiCase("twoCell") )
+ meEditAs = ANCHOR_TWOCELL;
+ }
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "ShapeAnchor::importAnchor - unexpected element" );
+ }
+ meCellAnchorType = CellAnchorType::Emu;
+}
+
+void ShapeAnchor::importPos( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( meAnchorType == ANCHOR_ABSOLUTE, "ShapeAnchor::importPos - unexpected 'xdr:pos' element" );
+ maPos.X = rAttribs.getHyper( XML_x, 0 );
+ maPos.Y = rAttribs.getHyper( XML_y, 0 );
+}
+
+void ShapeAnchor::importExt( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( (meAnchorType == ANCHOR_ABSOLUTE) || (meAnchorType == ANCHOR_ONECELL), "ShapeAnchor::importExt - unexpected 'xdr:ext' element" );
+ maSize.Width = rAttribs.getHyper( XML_cx, 0 );
+ maSize.Height = rAttribs.getHyper( XML_cy, 0 );
+}
+
+void ShapeAnchor::importClientData( const AttributeList& rAttribs )
+{
+ maClientData.mbLocksWithSheet = rAttribs.getBool( XML_fLocksWithSheet, true );
+ maClientData.mbPrintsWithSheet = rAttribs.getBool( XML_fPrintsWithSheet, true );
+}
+
+void ShapeAnchor::setCellPos( sal_Int32 nElement, sal_Int32 nParentContext, std::u16string_view rValue )
+{
+ CellAnchorModel* pCellAnchor = nullptr;
+ switch( nParentContext )
+ {
+ case XDR_TOKEN( from ):
+ OSL_ENSURE( (meAnchorType == ANCHOR_ONECELL) || (meAnchorType == ANCHOR_TWOCELL), "ShapeAnchor::setCellPos - unexpected 'xdr:from' element" );
+ pCellAnchor = &maFrom;
+ break;
+ case XDR_TOKEN( to ):
+ OSL_ENSURE( meAnchorType == ANCHOR_TWOCELL, "ShapeAnchor::setCellPos - unexpected 'xdr:to' element" );
+ pCellAnchor = &maTo;
+ break;
+ default:
+ OSL_ENSURE( false, "ShapeAnchor::setCellPos - unexpected parent element" );
+ }
+ if( pCellAnchor ) switch( nElement )
+ {
+ case XDR_TOKEN( col ): pCellAnchor->mnCol = o3tl::toInt32(rValue); break;
+ case XDR_TOKEN( row ): pCellAnchor->mnRow = o3tl::toInt32(rValue); break;
+ case XDR_TOKEN( colOff ): pCellAnchor->mnColOffset = o3tl::toInt64(rValue); break;
+ case XDR_TOKEN( rowOff ): pCellAnchor->mnRowOffset = o3tl::toInt64(rValue); break;
+ default: OSL_ENSURE( false, "ShapeAnchor::setCellPos - unexpected element" );
+ }
+}
+
+void ShapeAnchor::importVmlAnchor( std::u16string_view rAnchor )
+{
+ meAnchorType = ANCHOR_VML;
+ meCellAnchorType = CellAnchorType::Pixel;
+
+ sal_Int32 nValues[8];
+ sal_Int32 nI{ 0 };
+
+ for(sal_Int32 nIndex{ 0 }; nIndex>=0;)
+ {
+ nValues[nI] = o3tl::toInt32(o3tl::getToken(rAnchor, 0, ',', nIndex ));
+ if (++nI==8)
+ {
+ maFrom.mnCol = nValues[0];
+ maFrom.mnColOffset = nValues[1];
+ maFrom.mnRow = nValues[2];
+ maFrom.mnRowOffset = nValues[3];
+ maTo.mnCol = nValues[4];
+ maTo.mnColOffset = nValues[5];
+ maTo.mnRow = nValues[6];
+ maTo.mnRowOffset = nValues[7];
+ return;
+ }
+ }
+
+ OSL_FAIL("ShapeAnchor::importVmlAnchor - missing anchor tokens" );
+}
+
+bool ShapeAnchor::isAnchorValid() const
+{
+ // Checks whether the shape is visible based on the anchor
+ // if From and To anchor has the same attribute values, the shape will not have width and height
+ // and thus we can assume that such kind of shape will be not be visible
+ return !(meAnchorType == ANCHOR_TWOCELL &&
+ (maTo.mnRow == maFrom.mnRow && maTo.mnCol == maFrom.mnCol) &&
+ (maTo.mnColOffset == maFrom.mnColOffset && maTo.mnRowOffset == maFrom.mnRowOffset));
+}
+
+EmuRectangle ShapeAnchor::calcAnchorRectEmu( const css::awt::Size& rPageSizeHmm ) const
+{
+ AddressConverter& rAddrConv = getAddressConverter();
+ EmuSize aPageSize( lclHmmToEmu( rPageSizeHmm.Width ), lclHmmToEmu( rPageSizeHmm.Height ) );
+ EmuRectangle aAnchorRect( -1, -1, -1, -1 );
+
+ // calculate shape position
+ switch( meAnchorType )
+ {
+ case ANCHOR_ABSOLUTE:
+ OSL_ENSURE( maPos.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" );
+ if( maPos.isValid() && (maPos.X < aPageSize.Width) && (maPos.Y < aPageSize.Height) )
+ aAnchorRect.setPos( maPos );
+ break;
+ case ANCHOR_ONECELL:
+ case ANCHOR_TWOCELL:
+ case ANCHOR_VML:
+ OSL_ENSURE( maFrom.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" );
+ if( maFrom.isValid() && rAddrConv.checkCol( maFrom.mnCol, true ) && rAddrConv.checkRow( maFrom.mnRow, true ) )
+ {
+ EmuPoint aPoint = calcCellAnchorEmu( maFrom );
+ if( (aPoint.X < aPageSize.Width) && (aPoint.Y < aPageSize.Height) )
+ aAnchorRect.setPos( aPoint );
+ }
+ break;
+ case ANCHOR_INVALID:
+ OSL_ENSURE( false, "ShapeAnchor::calcAnchorRectEmu - invalid anchor" );
+ break;
+ }
+
+ // calculate shape size
+ if( (aAnchorRect.X >= 0) && (aAnchorRect.Y >= 0) ) switch( meAnchorType )
+ {
+ case ANCHOR_ABSOLUTE:
+ case ANCHOR_ONECELL:
+ OSL_ENSURE( maSize.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid size" );
+ if( maSize.isValid() )
+ {
+ aAnchorRect.Width = ::std::min< sal_Int64 >( maSize.Width, aPageSize.Width - aAnchorRect.X );
+ aAnchorRect.Height = ::std::min< sal_Int64 >( maSize.Height, aPageSize.Height - aAnchorRect.Y );
+ }
+ break;
+ case ANCHOR_TWOCELL:
+ case ANCHOR_VML:
+ OSL_ENSURE( maTo.isValid(), "ShapeAnchor::calcAnchorRectEmu - invalid position" );
+ if( maTo.isValid() )
+ {
+ /* Pass a valid cell address to calcCellAnchorEmu(), otherwise
+ nothing useful is returned, even if either row or column is valid. */
+ ScAddress aToCell = rAddrConv.createValidCellAddress( BinAddress( maTo.mnCol, maTo.mnRow ), getSheetIndex(), true );
+ CellAnchorModel aValidTo = maTo;
+ aValidTo.mnCol = aToCell.Col();
+ aValidTo.mnRow = aToCell.Row();
+ EmuPoint aPoint = calcCellAnchorEmu( aValidTo );
+ // width (if column index is valid, use the calculated offset, otherwise stretch to maximum available X position)
+ aAnchorRect.Width = aPageSize.Width - aAnchorRect.X;
+ if( aToCell.Col() == maTo.mnCol )
+ aAnchorRect.Width = ::std::min< sal_Int64 >( aPoint.X - aAnchorRect.X + 1, aAnchorRect.Width );
+ // height (if row index is valid, use the calculated offset, otherwise stretch to maximum available Y position)
+ aAnchorRect.Height = aPageSize.Height - aAnchorRect.Y;
+ if( aToCell.Row() == maTo.mnRow )
+ aAnchorRect.Height = ::std::min< sal_Int64 >( aPoint.Y - aAnchorRect.Y + 1, aAnchorRect.Height );
+ }
+ break;
+ case ANCHOR_INVALID:
+ break;
+ }
+
+ return aAnchorRect;
+}
+
+css::awt::Rectangle ShapeAnchor::calcAnchorRectHmm( const css::awt::Size& rPageSizeHmm ) const
+{
+ EmuRectangle aAnchorRect = calcAnchorRectEmu( rPageSizeHmm );
+ return css::awt::Rectangle( lclEmuToHmm( aAnchorRect.X ), lclEmuToHmm( aAnchorRect.Y ), lclEmuToHmm( aAnchorRect.Width ), lclEmuToHmm( aAnchorRect.Height ) );
+}
+
+EmuPoint ShapeAnchor::calcCellAnchorEmu( const CellAnchorModel& rModel ) const
+{
+ // calculate position of top-left edge of the cell
+ css::awt::Point aPoint = getCellPosition( rModel.mnCol, rModel.mnRow );
+ EmuPoint aEmuPoint( lclHmmToEmu( aPoint.X ), lclHmmToEmu( aPoint.Y ) );
+
+ // add the offset inside the cell
+ switch( meCellAnchorType )
+ {
+ case CellAnchorType::Emu:
+ aEmuPoint.X += rModel.mnColOffset;
+ aEmuPoint.Y += rModel.mnRowOffset;
+ break;
+
+ case CellAnchorType::Pixel:
+ {
+ const UnitConverter& rUnitConv = getUnitConverter();
+ aEmuPoint.X += std::round( rUnitConv.scaleValue( rModel.mnColOffset, Unit::ScreenX, Unit::Emu ) );
+ aEmuPoint.Y += std::round( rUnitConv.scaleValue( rModel.mnRowOffset, Unit::ScreenY, Unit::Emu ) );
+ }
+ break;
+ }
+
+ return aEmuPoint;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/drawingfragment.cxx b/sc/source/filter/oox/drawingfragment.cxx
new file mode 100644
index 000000000..adf499d99
--- /dev/null
+++ b/sc/source/filter/oox/drawingfragment.cxx
@@ -0,0 +1,807 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <drawingfragment.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
+#include <com/sun/star/document/XEventsSupplier.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <rtl/strbuf.hxx>
+#include <svx/svdobj.hxx>
+#include <drwlayer.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/drawingml/connectorshapecontext.hxx>
+#include <oox/drawingml/graphicshapecontext.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/vml/vmlshape.hxx>
+#include <oox/vml/vmlshapecontainer.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+#include <formulaparser.hxx>
+#include <stylesbuffer.hxx>
+#include <themebuffer.hxx>
+#include <worksheetbuffer.hxx>
+namespace oox::xls {
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+using namespace ::oox::drawingml;
+using namespace ::oox::ole;
+
+using ::com::sun::star::awt::Size;
+using ::com::sun::star::awt::Point;
+using ::com::sun::star::awt::Rectangle;
+using ::com::sun::star::awt::XControlModel;
+// no using's for ::oox::vml, that may clash with ::oox::drawingml types
+
+ShapeMacroAttacher::ShapeMacroAttacher( const OUString& rMacroName, const Reference< XShape >& rxShape ) :
+ VbaMacroAttacherBase( rMacroName ),
+ mxShape( rxShape )
+{
+}
+
+void ShapeMacroAttacher::attachMacro( const OUString& rMacroUrl )
+{
+ try
+ {
+ Reference< XEventsSupplier > xSupplier( mxShape, UNO_QUERY_THROW );
+ Reference< XNameReplace > xEvents( xSupplier->getEvents(), UNO_SET_THROW );
+ Sequence aEventProps{ comphelper::makePropertyValue("EventType", OUString( "Script" )),
+ comphelper::makePropertyValue("Script", rMacroUrl) };
+ xEvents->replaceByName( "OnClick", Any( aEventProps ) );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+Shape::Shape( const WorksheetHelper& rHelper, const AttributeList& rAttribs, const char* pcServiceName ) :
+ ::oox::drawingml::Shape( pcServiceName ),
+ WorksheetHelper( rHelper )
+{
+ OUString aMacro = rAttribs.getXString( XML_macro, OUString() );
+ if( !aMacro.isEmpty() )
+ maMacroName = getFormulaParser().importMacroName( aMacro );
+}
+
+void Shape::finalizeXShape( XmlFilterBase& rFilter, const Reference< XShapes >& rxShapes )
+{
+ OUString sURL;
+ getShapeProperties().getProperty( PROP_URL ) >>= sURL;
+ getWorksheets().convertSheetNameRef( sURL );
+ if( !maMacroName.isEmpty() && mxShape.is() )
+ {
+ VbaMacroAttacherRef xAttacher = std::make_shared<ShapeMacroAttacher>( maMacroName, mxShape );
+ getBaseFilter().getVbaProject().registerMacroAttacher( xAttacher );
+ }
+ ::oox::drawingml::Shape::finalizeXShape( rFilter, rxShapes );
+ if ( !sURL.isEmpty() )
+ {
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape( mxShape );
+ if ( pObj )
+ pObj->setHyperlink(sURL);
+ }
+}
+
+GroupShapeContext::GroupShapeContext( const FragmentHandler2& rParent,
+ const WorksheetHelper& rHelper, const ShapePtr& rxParentShape, const ShapePtr& rxShape ) :
+ ShapeGroupContext( rParent, rxParentShape, rxShape ),
+ WorksheetHelper( rHelper )
+{
+}
+
+/*static*/ ContextHandlerRef GroupShapeContext::createShapeContext( FragmentHandler2& rParent,
+ const WorksheetHelper& rHelper, sal_Int32 nElement, const AttributeList& rAttribs,
+ const ShapePtr& rxParentShape, ShapePtr* pxShape )
+{
+ switch( nElement )
+ {
+ case XDR_TOKEN( sp ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.CustomShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new ShapeContext( rParent, rxParentShape, xShape );
+ }
+ case XDR_TOKEN( cxnSp ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.ConnectorShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new ConnectorShapeContext(rParent, rxParentShape, xShape,
+ xShape->getConnectorShapeProperties());
+ }
+ case XDR_TOKEN( pic ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.GraphicObjectShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new GraphicShapeContext( rParent, rxParentShape, xShape );
+ }
+ case XDR_TOKEN( graphicFrame ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.GraphicObjectShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new GraphicalObjectFrameContext( rParent, rxParentShape, xShape, rHelper.getSheetType() != WorksheetType::Chart );
+ }
+ case XDR_TOKEN( grpSp ):
+ {
+ ShapePtr xShape = std::make_shared<Shape>( rHelper, rAttribs, "com.sun.star.drawing.GroupShape" );
+ if( pxShape ) *pxShape = xShape;
+ return new GroupShapeContext( rParent, rHelper, rxParentShape, xShape );
+ }
+ }
+ return nullptr;
+}
+
+ContextHandlerRef GroupShapeContext::onCreateContext(
+ sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ ContextHandlerRef xContext = createShapeContext( *this, *this, nElement, rAttribs, mpGroupShapePtr );
+ return xContext ? xContext : ShapeGroupContext::onCreateContext( nElement, rAttribs );
+}
+
+DrawingFragment::DrawingFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mxDrawPage( rHelper.getDrawPage() )
+{
+ OSL_ENSURE( mxDrawPage.is(), "DrawingFragment::DrawingFragment - missing drawing page" );
+}
+
+ContextHandlerRef DrawingFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XDR_TOKEN( wsDr ) ) return this;
+ break;
+
+ case XDR_TOKEN( wsDr ):
+ switch( nElement )
+ {
+ case XDR_TOKEN( absoluteAnchor ):
+ case XDR_TOKEN( oneCellAnchor ):
+ case XDR_TOKEN( twoCellAnchor ):
+ mxAnchor.reset( new ShapeAnchor( *this ) );
+ mxAnchor->importAnchor( nElement, rAttribs );
+ return this;
+ }
+ break;
+
+ case XDR_TOKEN( absoluteAnchor ):
+ case XDR_TOKEN( oneCellAnchor ):
+ case XDR_TOKEN( twoCellAnchor ):
+ {
+ switch( nElement )
+ {
+ case XDR_TOKEN( from ):
+ case XDR_TOKEN( to ): return this;
+
+ case XDR_TOKEN( pos ): if( mxAnchor ) mxAnchor->importPos( rAttribs ); break;
+ case XDR_TOKEN( ext ): if( mxAnchor ) mxAnchor->importExt( rAttribs ); break;
+ case XDR_TOKEN( clientData ): if( mxAnchor ) mxAnchor->importClientData( rAttribs ); break;
+
+ default: return GroupShapeContext::createShapeContext( *this, *this, nElement, rAttribs, ShapePtr(), &mxShape );
+ }
+ }
+ break;
+
+ case XDR_TOKEN( from ):
+ case XDR_TOKEN( to ):
+ switch( nElement )
+ {
+ case XDR_TOKEN( col ):
+ case XDR_TOKEN( row ):
+ case XDR_TOKEN( colOff ):
+ case XDR_TOKEN( rowOff ): return this; // collect index in onCharacters()
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void DrawingFragment::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XDR_TOKEN( col ):
+ case XDR_TOKEN( row ):
+ case XDR_TOKEN( colOff ):
+ case XDR_TOKEN( rowOff ):
+ if( mxAnchor ) mxAnchor->setCellPos( getCurrentElement(), getParentElement(), rChars );
+ break;
+ }
+}
+
+void DrawingFragment::onEndElement()
+{
+ switch( getCurrentElement() )
+ {
+ case XDR_TOKEN( absoluteAnchor ):
+ case XDR_TOKEN( oneCellAnchor ):
+ case XDR_TOKEN( twoCellAnchor ):
+ if( mxDrawPage.is() && mxShape && mxAnchor )
+ {
+ EmuRectangle aShapeRectEmu = mxAnchor->calcAnchorRectEmu( getDrawPageSize() );
+ const bool bIsShapeVisible = mxAnchor->isAnchorValid();
+ if( (aShapeRectEmu.X >= 0) && (aShapeRectEmu.Y >= 0) && (aShapeRectEmu.Width >= 0) && (aShapeRectEmu.Height >= 0) )
+ {
+ const sal_Int32 aRotation = mxShape->getRotation();
+ if ((aRotation >= 45 * PER_DEGREE && aRotation < 135 * PER_DEGREE)
+ || (aRotation >= 225 * PER_DEGREE && aRotation < 315 * PER_DEGREE))
+ {
+ // When rotating any shape in MSO Excel within the range of degrees given above,
+ // Excel changes the cells in which the shape is anchored. The new position of
+ // the anchors are always calculated using a 90 degrees rotation anticlockwise.
+ // There is an important result of this operation: the top left point of the shape changes,
+ // it will be another vertex.
+ // The anchor position is given in the xml file, it is in the xdr:from and xdr:to elements.
+ // Let's see what happens in time order:
+ // We create a shape in Excel, the anchor position is in a given cell, then the rotation happens
+ // as mentioned above, and excel recalculates the cells in which the anchors are positioned.
+ // This new cell is exported into the xml elements xdr:from and xdr:to, when Excel exports the document!
+ // Thus, if we have a 90 degrees rotation and an already rotated point from which we base
+ // our calculations here in LO, the result is an incorrect 180 degrees rotation.
+ // Now, we need to create the bounding rectangle of the shape with this in mind.
+ // (Important to mention that at this point we don't talk about rotations at all, this bounding
+ // rectangle contains the original not-rotated shape. Rotation happens later in the code.)
+ // We get the new (x, y) coords, then swap width with height.
+ // To get the new coords we reflect the rectangle in the line y = x. (This will return the
+ // correct vertex, which is the actual top left one.)
+ // Another fact that appears to be true in Excel is that there are only 2 of possible anchor
+ // positions for a shape that is only rotated (and not resized for example).
+ // The first position happens in the set of degrees {[45, 135) U [225, 315)} and the second
+ // set is all the other angles. The two sets partition the circle (of all rotations: 360 degrees).
+ sal_Int64 nHalfWidth = aShapeRectEmu.Width / 2;
+ sal_Int64 nHalfHeight = aShapeRectEmu.Height / 2;
+ aShapeRectEmu.X = aShapeRectEmu.X + nHalfWidth - nHalfHeight;
+ aShapeRectEmu.Y = aShapeRectEmu.Y + nHalfHeight - nHalfWidth;
+ std::swap(aShapeRectEmu.Width, aShapeRectEmu.Height);
+ }
+
+ // TODO: DrawingML implementation expects 32-bit coordinates for EMU rectangles (change that to EmuRectangle)
+ // tdf#135918: Negative X,Y position has to be allowed to avoid shape displacement on rotation.
+ // The negative values can exist because of previous lines where the anchor rectangle must be mirrored in some ranges.
+ Rectangle aShapeRectEmu32(
+ getLimitedValue< sal_Int32, sal_Int64 >( aShapeRectEmu.X, SAL_MIN_INT32, SAL_MAX_INT32 ),
+ getLimitedValue< sal_Int32, sal_Int64 >( aShapeRectEmu.Y, SAL_MIN_INT32, SAL_MAX_INT32 ),
+ getLimitedValue< sal_Int32, sal_Int64 >( aShapeRectEmu.Width, 0, SAL_MAX_INT32 ),
+ getLimitedValue< sal_Int32, sal_Int64 >( aShapeRectEmu.Height, 0, SAL_MAX_INT32 ) );
+
+ // Make sure to set the position and size *before* calling addShape().
+ mxShape->setPosition(Point(aShapeRectEmu32.X, aShapeRectEmu32.Y));
+ mxShape->setSize(Size(aShapeRectEmu32.Width, aShapeRectEmu32.Height));
+
+ basegfx::B2DHomMatrix aTransformation;
+ if ( !bIsShapeVisible)
+ mxShape->setHidden(true);
+
+ mxShape->addShape( getOoxFilter(), &getTheme(), mxDrawPage, aTransformation, mxShape->getFillProperties() );
+
+ /* Collect all shape positions in the WorksheetHelper base
+ class. But first, scale EMUs to 1/100 mm. */
+ Rectangle aShapeRectHmm(
+ convertEmuToHmm(aShapeRectEmu32.X > 0 ? aShapeRectEmu32.X : 0), convertEmuToHmm(aShapeRectEmu32.Y > 0 ? aShapeRectEmu32.Y : 0),
+ convertEmuToHmm(aShapeRectEmu32.Width ), convertEmuToHmm(aShapeRectEmu32.Height ) );
+ extendShapeBoundingBox( aShapeRectHmm );
+ // set cell Anchoring
+ if ( mxAnchor->getEditAs() != ShapeAnchor::ANCHOR_ABSOLUTE )
+ {
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape( mxShape->getXShape() );
+ if ( pObj )
+ {
+ bool bResizeWithCell = mxAnchor->getEditAs() == ShapeAnchor::ANCHOR_TWOCELL;
+ ScDrawLayer::SetCellAnchoredFromPosition( *pObj, getScDocument(), getSheetIndex(), bResizeWithCell );
+ }
+ }
+ }
+ }
+ mxShape.reset();
+ mxAnchor.reset();
+ break;
+ }
+}
+
+// VML
+
+namespace {
+
+class VmlFindNoteFunc
+{
+public:
+ explicit VmlFindNoteFunc( const ScAddress& rPos );
+ bool operator()( const ::oox::vml::ShapeBase& rShape ) const;
+
+private:
+ sal_Int32 mnCol;
+ sal_Int32 mnRow;
+};
+
+VmlFindNoteFunc::VmlFindNoteFunc( const ScAddress& rPos ) :
+ mnCol( rPos.Col() ),
+ mnRow( rPos.Row() )
+{
+}
+
+bool VmlFindNoteFunc::operator()( const ::oox::vml::ShapeBase& rShape ) const
+{
+ const ::oox::vml::ClientData* pClientData = rShape.getClientData();
+ return pClientData && (pClientData->mnCol == mnCol) && (pClientData->mnRow == mnRow);
+}
+
+} // namespace
+
+VmlControlMacroAttacher::VmlControlMacroAttacher( const OUString& rMacroName,
+ const Reference< XIndexContainer >& rxCtrlFormIC, sal_Int32 nCtrlIndex, sal_Int32 nCtrlType, sal_Int32 nDropStyle ) :
+ VbaMacroAttacherBase( rMacroName ),
+ mxCtrlFormIC( rxCtrlFormIC ),
+ mnCtrlIndex( nCtrlIndex ),
+ mnCtrlType( nCtrlType ),
+ mnDropStyle( nDropStyle )
+{
+}
+
+void VmlControlMacroAttacher::attachMacro( const OUString& rMacroUrl )
+{
+ ScriptEventDescriptor aEventDesc;
+ aEventDesc.ScriptType = "Script";
+ aEventDesc.ScriptCode = rMacroUrl;
+
+ // editable drop downs are treated like edit boxes
+ bool bEditDropDown = (mnCtrlType == XML_Drop) && (mnDropStyle == XML_ComboEdit);
+ sal_Int32 nCtrlType = bEditDropDown ? XML_Edit : mnCtrlType;
+
+ switch( nCtrlType )
+ {
+ case XML_Button:
+ case XML_Checkbox:
+ case XML_Radio:
+ aEventDesc.ListenerType = "XActionListener";
+ aEventDesc.EventMethod = "actionPerformed";
+ break;
+ case XML_Label:
+ case XML_GBox:
+ case XML_Dialog:
+ aEventDesc.ListenerType = "XMouseListener";
+ aEventDesc.EventMethod = "mouseReleased";
+ break;
+ case XML_Edit:
+ aEventDesc.ListenerType = "XTextListener";
+ aEventDesc.EventMethod = "textChanged";
+ break;
+ case XML_Spin:
+ case XML_Scroll:
+ aEventDesc.ListenerType = "XAdjustmentListener";
+ aEventDesc.EventMethod = "adjustmentValueChanged";
+ break;
+ case XML_List:
+ case XML_Drop:
+ aEventDesc.ListenerType = "XChangeListener";
+ aEventDesc.EventMethod = "changed";
+ break;
+ default:
+ OSL_ENSURE( false, "VmlControlMacroAttacher::attachMacro - unexpected object type" );
+ return;
+ }
+
+ try
+ {
+ Reference< XEventAttacherManager > xEventMgr( mxCtrlFormIC, UNO_QUERY_THROW );
+ xEventMgr->registerScriptEvent( mnCtrlIndex, aEventDesc );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+VmlDrawing::VmlDrawing( const WorksheetHelper& rHelper ) :
+ ::oox::vml::Drawing( rHelper.getOoxFilter(), rHelper.getDrawPage(), ::oox::vml::VMLDRAWING_EXCEL ),
+ WorksheetHelper( rHelper ),
+ maControlConv( rHelper.getBaseFilter().getModel(), rHelper.getBaseFilter().getGraphicHelper() )
+{
+ // default font for legacy listboxes and dropdowns: Tahoma, 8pt
+ maListBoxFont.moName = "Tahoma";
+ maListBoxFont.moColor = "auto";
+ maListBoxFont.monSize = 160;
+}
+
+const ::oox::vml::ShapeBase* VmlDrawing::getNoteShape( const ScAddress& rPos ) const
+{
+ return getShapes().findShape( VmlFindNoteFunc( rPos ) );
+}
+
+bool VmlDrawing::isShapeSupported( const ::oox::vml::ShapeBase& rShape ) const
+{
+ const ::oox::vml::ClientData* pClientData = rShape.getClientData();
+ return !pClientData || (pClientData->mnObjType != XML_Note);
+}
+
+OUString VmlDrawing::getShapeBaseName( const ::oox::vml::ShapeBase& rShape ) const
+{
+ if( const ::oox::vml::ClientData* pClientData = rShape.getClientData() )
+ {
+ switch( pClientData->mnObjType )
+ {
+ case XML_Button: return "Button";
+ case XML_Checkbox: return "Check Box";
+ case XML_Dialog: return "Dialog Frame";
+ case XML_Drop: return "Drop Down";
+ case XML_Edit: return "Edit Box";
+ case XML_GBox: return "Group Box";
+ case XML_Label: return "Label";
+ case XML_List: return "List Box";
+ case XML_Note: return "Comment";
+ case XML_Pict: return (pClientData->mbDde || getOleObjectInfo( rShape.getShapeId() )) ? OUString( "Object" ) : OUString( "Picture" );
+ case XML_Radio: return "Option Button";
+ case XML_Scroll: return "Scroll Bar";
+ case XML_Spin: return "Spinner";
+ }
+ }
+ return ::oox::vml::Drawing::getShapeBaseName( rShape );
+}
+
+bool VmlDrawing::convertClientAnchor( Rectangle& orShapeRect, const OUString& rShapeAnchor ) const
+{
+ if( rShapeAnchor.isEmpty() )
+ return false;
+ ShapeAnchor aAnchor( *this );
+ aAnchor.importVmlAnchor( rShapeAnchor );
+ orShapeRect = aAnchor.calcAnchorRectHmm( getDrawPageSize() );
+ return (orShapeRect.Width >= 0) && (orShapeRect.Height >= 0);
+}
+
+Reference< XShape > VmlDrawing::createAndInsertClientXShape( const ::oox::vml::ShapeBase& rShape,
+ const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const
+{
+ // simulate the legacy drawing controls with OLE form controls
+ OUString aShapeName = rShape.getShapeName();
+ const ::oox::vml::ClientData* pClientData = rShape.getClientData();
+ if( !aShapeName.isEmpty() && pClientData )
+ {
+ Rectangle aShapeRect = rShapeRect;
+ const ::oox::vml::TextBox* pTextBox = rShape.getTextBox();
+ EmbeddedControl aControl( aShapeName );
+ switch( pClientData->mnObjType )
+ {
+ case XML_Button:
+ {
+ AxCommandButtonModel& rAxModel = aControl.createModel< AxCommandButtonModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ rAxModel.mnFlags = AX_FLAGS_ENABLED | AX_FLAGS_OPAQUE | AX_FLAGS_WORDWRAP;
+ rAxModel.mnVerticalAlign = pClientData->mnTextVAlign;
+ }
+ break;
+
+ case XML_Label:
+ {
+ AxLabelModel& rAxModel = aControl.createModel< AxLabelModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ rAxModel.mnFlags = AX_FLAGS_ENABLED | AX_FLAGS_WORDWRAP;
+ rAxModel.mnBorderStyle = AX_BORDERSTYLE_NONE;
+ rAxModel.mnSpecialEffect = AX_SPECIALEFFECT_FLAT;
+ rAxModel.mnVerticalAlign = pClientData->mnTextVAlign;
+ }
+ break;
+
+ case XML_Edit:
+ {
+ bool bNumeric = (pClientData->mnVTEdit == ::oox::vml::VML_CLIENTDATA_INTEGER) || (pClientData->mnVTEdit == ::oox::vml::VML_CLIENTDATA_NUMBER);
+ AxMorphDataModelBase& rAxModel = bNumeric ?
+ static_cast< AxMorphDataModelBase& >( aControl.createModel< AxNumericFieldModel >() ) :
+ static_cast< AxMorphDataModelBase& >( aControl.createModel< AxTextBoxModel >() );
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maValue, pTextBox, pClientData->mnTextHAlign );
+ setFlag( rAxModel.mnFlags, AX_FLAGS_MULTILINE, pClientData->mbMultiLine );
+ setFlag( rAxModel.mnScrollBars, AX_SCROLLBAR_VERTICAL, pClientData->mbVScroll );
+ if( pClientData->mbSecretEdit )
+ rAxModel.mnPasswordChar = '*';
+ }
+ break;
+
+ case XML_GBox:
+ {
+ AxFrameModel& rAxModel = aControl.createModel< AxFrameModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ rAxModel.mnBorderStyle = pClientData->mbNo3D ? AX_BORDERSTYLE_SINGLE : AX_BORDERSTYLE_NONE;
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_BUMPED;
+
+ /* Move top border of groupbox up by half font height, because
+ Excel specifies Y position of the groupbox border line
+ instead the top border of the caption text. */
+ if( const ::oox::vml::TextFontModel* pFontModel = pTextBox ? pTextBox->getFirstFont() : nullptr )
+ {
+ sal_Int32 nFontHeightHmm = o3tl::convert( pFontModel->monSize.get( 160 ), o3tl::Length::twip, o3tl::Length::mm100 );
+ sal_Int32 nYDiff = ::std::min< sal_Int32 >( nFontHeightHmm / 2, aShapeRect.Y );
+ aShapeRect.Y -= nYDiff;
+ aShapeRect.Height += nYDiff;
+ }
+ }
+ break;
+
+ case XML_Checkbox:
+ {
+ AxCheckBoxModel& rAxModel = aControl.createModel< AxCheckBoxModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ convertControlBackground( rAxModel, rShape );
+ rAxModel.maValue = OUString::number( pClientData->mnChecked );
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_SUNKEN;
+ rAxModel.mnVerticalAlign = pClientData->mnTextVAlign;
+ bool bTriState = (pClientData->mnChecked != ::oox::vml::VML_CLIENTDATA_UNCHECKED) && (pClientData->mnChecked != ::oox::vml::VML_CLIENTDATA_CHECKED);
+ rAxModel.mnMultiSelect = bTriState ? AX_SELECTION_MULTI : AX_SELECTION_SINGLE;
+ }
+ break;
+
+ case XML_Radio:
+ {
+ AxOptionButtonModel& rAxModel = aControl.createModel< AxOptionButtonModel >();
+
+ // unique name to prevent autoGrouping with ActiveX controls and which a GroupBox may override - see vmldrawing.cxx.
+ rAxModel.maGroupName = "autoGroup_formControl";
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, pClientData->mnTextHAlign );
+ convertControlBackground( rAxModel, rShape );
+ rAxModel.maValue = OUString::number( pClientData->mnChecked );
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_SUNKEN;
+ rAxModel.mnVerticalAlign = pClientData->mnTextVAlign;
+ }
+ break;
+
+ case XML_List:
+ {
+ AxListBoxModel& rAxModel = aControl.createModel< AxListBoxModel >();
+ convertControlFontData( rAxModel.maFontData, rAxModel.mnTextColor, maListBoxFont );
+ rAxModel.mnBorderStyle = pClientData->mbNo3D2 ? AX_BORDERSTYLE_SINGLE : AX_BORDERSTYLE_NONE;
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D2 ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_SUNKEN;
+ switch( pClientData->mnSelType )
+ {
+ case XML_Single: rAxModel.mnMultiSelect = AX_SELECTION_SINGLE; break;
+ case XML_Multi: rAxModel.mnMultiSelect = AX_SELECTION_MULTI; break;
+ case XML_Extend: rAxModel.mnMultiSelect = AX_SELECTION_EXTENDED; break;
+ }
+ }
+ break;
+
+ case XML_Drop:
+ {
+ AxComboBoxModel& rAxModel = aControl.createModel< AxComboBoxModel >();
+ convertControlFontData( rAxModel.maFontData, rAxModel.mnTextColor, maListBoxFont );
+ rAxModel.mnDisplayStyle = AX_DISPLAYSTYLE_DROPDOWN;
+ rAxModel.mnShowDropButton = AX_SHOWDROPBUTTON_ALWAYS;
+ rAxModel.mnBorderStyle = pClientData->mbNo3D2 ? AX_BORDERSTYLE_SINGLE : AX_BORDERSTYLE_NONE;
+ rAxModel.mnSpecialEffect = pClientData->mbNo3D2 ? AX_SPECIALEFFECT_FLAT : AX_SPECIALEFFECT_SUNKEN;
+ rAxModel.mnListRows = pClientData->mnDropLines;
+ }
+ break;
+
+ case XML_Spin:
+ {
+ AxSpinButtonModel& rAxModel = aControl.createModel< AxSpinButtonModel >();
+ rAxModel.mnMin = pClientData->mnMin;
+ rAxModel.mnMax = pClientData->mnMax;
+ rAxModel.mnPosition = pClientData->mnVal;
+ rAxModel.mnSmallChange = pClientData->mnInc;
+ }
+ break;
+
+ case XML_Scroll:
+ {
+ AxScrollBarModel& rAxModel = aControl.createModel< AxScrollBarModel >();
+ rAxModel.mnMin = pClientData->mnMin;
+ rAxModel.mnMax = pClientData->mnMax;
+ rAxModel.mnPosition = pClientData->mnVal;
+ rAxModel.mnSmallChange = pClientData->mnInc;
+ rAxModel.mnLargeChange = pClientData->mnPage;
+ }
+ break;
+
+ case XML_Dialog:
+ {
+ // fake with a group box
+ AxFrameModel& rAxModel = aControl.createModel< AxFrameModel >();
+ convertControlText( rAxModel.maFontData, rAxModel.mnTextColor, rAxModel.maCaption, pTextBox, XML_Left );
+ rAxModel.mnBorderStyle = AX_BORDERSTYLE_SINGLE;
+ rAxModel.mnSpecialEffect = AX_SPECIALEFFECT_FLAT;
+ }
+ break;
+ }
+
+ if( ControlModelBase* pAxModel = aControl.getModel() )
+ {
+ // create the control shape
+ pAxModel->maSize.first = aShapeRect.Width;
+ pAxModel->maSize.second = aShapeRect.Height;
+ sal_Int32 nCtrlIndex = -1;
+ Reference< XShape > xShape = createAndInsertXControlShape( aControl, rxShapes, aShapeRect, nCtrlIndex );
+
+ // control shape macro
+ if( xShape.is() && (nCtrlIndex >= 0) && !pClientData->maFmlaMacro.isEmpty() )
+ {
+ OUString aMacroName = getFormulaParser().importMacroName( pClientData->maFmlaMacro );
+ if( !aMacroName.isEmpty() )
+ {
+ Reference< XIndexContainer > xFormIC = getControlForm().getXForm();
+ VbaMacroAttacherRef xAttacher = std::make_shared<VmlControlMacroAttacher>( aMacroName, xFormIC, nCtrlIndex, pClientData->mnObjType, pClientData->mnDropStyle );
+ getBaseFilter().getVbaProject().registerMacroAttacher( xAttacher );
+ }
+ }
+
+ return xShape;
+ }
+ }
+
+ return Reference< XShape >();
+}
+
+void VmlDrawing::notifyXShapeInserted( const Reference< XShape >& rxShape,
+ const Rectangle& rShapeRect, const ::oox::vml::ShapeBase& rShape, bool bGroupChild )
+{
+ // collect all shape positions in the WorksheetHelper base class (but not children of group shapes)
+ if( !bGroupChild )
+ extendShapeBoundingBox( rShapeRect );
+
+ // convert settings from VML client data
+ const ::oox::vml::ClientData* pClientData = rShape.getClientData();
+ if(!pClientData)
+ return;
+
+ // specific settings for embedded form controls
+ try
+ {
+ Reference< XControlShape > xCtrlShape( rxShape, UNO_QUERY_THROW );
+ Reference< XControlModel > xCtrlModel( xCtrlShape->getControl(), UNO_SET_THROW );
+ PropertySet aPropSet( xCtrlModel );
+
+ // printable
+ aPropSet.setProperty( PROP_Printable, pClientData->mbPrintObject );
+
+ // control source links
+ if( !pClientData->maFmlaLink.isEmpty() || !pClientData->maFmlaRange.isEmpty() )
+ maControlConv.bindToSources( xCtrlModel, pClientData->maFmlaLink, pClientData->maFmlaRange, getSheetIndex() );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+// private --------------------------------------------------------------------
+
+sal_uInt32 VmlDrawing::convertControlTextColor( const OUString& rTextColor ) const
+{
+ // color attribute not present or 'auto' - use passed default color
+ if( rTextColor.isEmpty() || rTextColor.equalsIgnoreAsciiCase( "auto" ) )
+ return AX_SYSCOLOR_WINDOWTEXT;
+
+ if( rTextColor[ 0 ] == '#' )
+ {
+ // RGB colors in the format '#RRGGBB'
+ if( rTextColor.getLength() == 7 )
+ return OleHelper::encodeOleColor( o3tl::toUInt32(rTextColor.subView( 1 ), 16) );
+
+ // RGB colors in the format '#RGB'
+ if( rTextColor.getLength() == 4 )
+ {
+ sal_Int32 nR = o3tl::toUInt32(rTextColor.subView( 1, 1 ), 16) * 0x11;
+ sal_Int32 nG = o3tl::toUInt32(rTextColor.subView( 2, 1 ), 16) * 0x11;
+ sal_Int32 nB = o3tl::toUInt32(rTextColor.subView( 3, 1 ), 16) * 0x11;
+ return OleHelper::encodeOleColor( (nR << 16) | (nG << 8) | nB );
+ }
+
+ OSL_ENSURE( false, OStringBuffer( "VmlDrawing::convertControlTextColor - invalid color name '" ).
+ append( OUStringToOString( rTextColor, RTL_TEXTENCODING_ASCII_US ) ).append( '\'' ).getStr() );
+ return AX_SYSCOLOR_WINDOWTEXT;
+ }
+
+ const GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+
+ /* Predefined color names or system color names (resolve to RGB to detect
+ valid color name). */
+ sal_Int32 nColorToken = AttributeConversion::decodeToken( rTextColor );
+ ::Color nRgbValue = Color::getVmlPresetColor( nColorToken, API_RGB_TRANSPARENT );
+ if( nRgbValue == API_RGB_TRANSPARENT )
+ nRgbValue = rGraphicHelper.getSystemColor( nColorToken );
+ if( nRgbValue != API_RGB_TRANSPARENT )
+ return OleHelper::encodeOleColor( nRgbValue );
+
+ // try palette color
+ return OleHelper::encodeOleColor( rGraphicHelper.getPaletteColor( rTextColor.toInt32() ) );
+}
+
+void VmlDrawing::convertControlFontData( AxFontData& rAxFontData, sal_uInt32& rnOleTextColor, const ::oox::vml::TextFontModel& rFontModel ) const
+{
+ if( rFontModel.moName.has() )
+ rAxFontData.maFontName = rFontModel.moName.get();
+
+ // font height: convert from twips to points, then to internal representation of AX controls
+ rAxFontData.setHeightPoints( static_cast< sal_Int16 >( (rFontModel.monSize.get( 200 ) + 10) / 20 ) );
+
+ // font effects
+ rAxFontData.mnFontEffects = AxFontFlags::NONE;
+ setFlag( rAxFontData.mnFontEffects, AxFontFlags::Bold, rFontModel.mobBold.get( false ) );
+ setFlag( rAxFontData.mnFontEffects, AxFontFlags::Italic, rFontModel.mobItalic.get( false ) );
+ setFlag( rAxFontData.mnFontEffects, AxFontFlags::Strikeout, rFontModel.mobStrikeout.get( false ) );
+ sal_Int32 nUnderline = rFontModel.monUnderline.get( XML_none );
+ setFlag( rAxFontData.mnFontEffects, AxFontFlags::Underline, nUnderline != XML_none );
+ rAxFontData.mbDblUnderline = nUnderline == XML_double;
+
+ // font color
+ rnOleTextColor = convertControlTextColor( rFontModel.moColor.get( OUString() ) );
+}
+
+void VmlDrawing::convertControlText( AxFontData& rAxFontData, sal_uInt32& rnOleTextColor,
+ OUString& rCaption, const ::oox::vml::TextBox* pTextBox, sal_Int32 nTextHAlign ) const
+{
+ if( pTextBox )
+ {
+ rCaption = pTextBox->getText();
+ if( const ::oox::vml::TextFontModel* pFontModel = pTextBox->getFirstFont() )
+ convertControlFontData( rAxFontData, rnOleTextColor, *pFontModel );
+ }
+
+ switch( nTextHAlign )
+ {
+ case XML_Left: rAxFontData.mnHorAlign = AxHorizontalAlign::Left; break;
+ case XML_Center: rAxFontData.mnHorAlign = AxHorizontalAlign::Center; break;
+ case XML_Right: rAxFontData.mnHorAlign = AxHorizontalAlign::Right; break;
+ default: rAxFontData.mnHorAlign = AxHorizontalAlign::Left;
+ }
+}
+
+void VmlDrawing::convertControlBackground( AxMorphDataModelBase& rAxModel, const ::oox::vml::ShapeBase& rShape ) const
+{
+ const ::oox::vml::FillModel& rFillModel = rShape.getTypeModel().maFillModel;
+ bool bHasFill = rFillModel.moFilled.get( true );
+ setFlag( rAxModel.mnFlags, AX_FLAGS_OPAQUE, bHasFill );
+ if( bHasFill )
+ {
+ const GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+ ::Color nSysWindowColor = rGraphicHelper.getSystemColor( XML_window, API_RGB_WHITE );
+ ::oox::drawingml::Color aColor = ::oox::vml::ConversionHelper::decodeColor( rGraphicHelper, rFillModel.moColor, rFillModel.moOpacity, nSysWindowColor );
+ ::Color nRgbValue = aColor.getColor( rGraphicHelper );
+ rAxModel.mnBackColor = OleHelper::encodeOleColor( nRgbValue );
+ }
+}
+
+VmlDrawingFragment::VmlDrawingFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ ::oox::vml::DrawingFragment( rHelper.getOoxFilter(), rFragmentPath, rHelper.getVmlDrawing() ),
+ WorksheetHelper( rHelper )
+{
+}
+
+void VmlDrawingFragment::finalizeImport()
+{
+ ::oox::vml::DrawingFragment::finalizeImport();
+ getVmlDrawing().convertAndInsert();
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/excelchartconverter.cxx b/sc/source/filter/oox/excelchartconverter.cxx
new file mode 100644
index 000000000..bc9a0bd03
--- /dev/null
+++ b/sc/source/filter/oox/excelchartconverter.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excelchartconverter.hxx>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/data/XDataProvider.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/chart2/data/XSheetDataProvider.hpp>
+
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/drawingml/chart/datasourcemodel.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <formulaparser.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::chart2;
+using namespace ::com::sun::star::chart2::data;
+using namespace ::com::sun::star::uno;
+
+using ::oox::drawingml::chart::DataSequenceModel;
+
+ExcelChartConverter::ExcelChartConverter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+ExcelChartConverter::~ExcelChartConverter()
+{
+}
+
+void ExcelChartConverter::createDataProvider( const Reference< XChartDocument >& rxChartDoc )
+{
+ try
+ {
+ Reference< XDataReceiver > xDataRec( rxChartDoc, UNO_QUERY_THROW );
+ Reference< XDataProvider > xDataProv( getBaseFilter().getModelFactory()->createInstance(
+ "com.sun.star.chart2.data.DataProvider" ), UNO_QUERY_THROW );
+ xDataRec->attachDataProvider( xDataProv );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+Reference< XDataSequence > ExcelChartConverter::createDataSequence(
+ const Reference< XDataProvider >& rxDataProvider, const DataSequenceModel& rDataSeq,
+ const OUString& /*rRole*/, const OUString& /*aRoleQualifier*/ )
+{
+ Reference< XDataSequence > xDataSeq;
+ if (!rxDataProvider.is())
+ return xDataSeq;
+
+ Reference<XSheetDataProvider> xSheetProvider(rxDataProvider, UNO_QUERY);
+ if (!xSheetProvider.is())
+ return xDataSeq;
+
+ if (!rDataSeq.maFormula.isEmpty())
+ {
+ // parse the formula string, create a token sequence
+ FormulaParser& rParser = getFormulaParser();
+ ScAddress aBaseAddr( SCCOL( 0 ), SCROW( 0 ), SCTAB( getCurrentSheetIndex() ) );
+ ApiTokenSequence aTokens = rParser.importFormula( aBaseAddr, rDataSeq.maFormula );
+
+ try
+ {
+ // create the data sequence
+ xDataSeq = xSheetProvider->createDataSequenceByFormulaTokens(aTokens);
+ }
+ catch (Exception&)
+ {
+ OSL_FAIL( "ExcelChartConverter::createDataSequence - cannot create data sequence" );
+ }
+ }
+ else if (!rDataSeq.maData.empty())
+ {
+ // create a single-row array from constant source data
+ Matrix< Any > aMatrix( rDataSeq.maData.size(), 1 );
+ Matrix< Any >::iterator aMIt = aMatrix.begin();
+ // TODO: how to handle missing values in the map?
+ for( const auto& rEntry : rDataSeq.maData )
+ {
+ *aMIt = rEntry.second;
+ ++aMIt;
+ }
+ OUString aRangeRep = FormulaProcessorBase::generateApiArray( aMatrix );
+
+ if (!aRangeRep.isEmpty())
+ {
+ try
+ {
+ // create the data sequence
+ xDataSeq = rxDataProvider->createDataSequenceByRangeRepresentation( aRangeRep );
+ }
+ catch (Exception&)
+ {
+ OSL_FAIL( "ExcelChartConverter::createDataSequence - cannot create data sequence" );
+ }
+ }
+ }
+ return xDataSeq;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/excelfilter.cxx b/sc/source/filter/oox/excelfilter.cxx
new file mode 100644
index 000000000..e73e0204c
--- /dev/null
+++ b/sc/source/filter/oox/excelfilter.cxx
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excelfilter.hxx>
+
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <excelvbaproject.hxx>
+#include <stylesbuffer.hxx>
+#include <themebuffer.hxx>
+#include <workbookfragment.hxx>
+#include <xestream.hxx>
+
+#include <addressconverter.hxx>
+#include <document.hxx>
+#include <docsh.hxx>
+#include <scerrors.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+
+using ::oox::drawingml::table::TableStyleListPtr;
+
+ExcelFilter::ExcelFilter( const Reference< XComponentContext >& rxContext ) :
+ XmlFilterBase( rxContext ),
+ mpBookGlob( nullptr )
+{
+}
+
+ExcelFilter::~ExcelFilter()
+{
+ OSL_ENSURE( !mpBookGlob, "ExcelFilter::~ExcelFilter - workbook data not cleared" );
+}
+
+void ExcelFilter::registerWorkbookGlobals( WorkbookGlobals& rBookGlob )
+{
+ mpBookGlob = &rBookGlob;
+}
+
+WorkbookGlobals& ExcelFilter::getWorkbookGlobals() const
+{
+ OSL_ENSURE( mpBookGlob, "ExcelFilter::getWorkbookGlobals - missing workbook data" );
+ return *mpBookGlob;
+}
+
+void ExcelFilter::unregisterWorkbookGlobals()
+{
+ mpBookGlob = nullptr;
+}
+
+bool ExcelFilter::importDocument()
+{
+ /* To activate the XLSX/XLSB dumper, insert the full path to the file
+ file:///<path-to-oox-module>/source/dump/xlsbdumper.ini
+ into the environment variable OOO_XLSBDUMPER and start the office with
+ this variable (nonpro only). */
+ //OOX_DUMP_FILE( ::oox::dump::xlsb::Dumper );
+
+ OUString aWorkbookPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"officeDocument" );
+ if( aWorkbookPath.isEmpty() )
+ return false;
+
+ try
+ {
+ try
+ {
+ importDocumentProperties();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sc", "exception when importing document properties");
+ }
+ catch( ... )
+ {
+ SAL_WARN("sc", "exception when importing document properties");
+ }
+ /* Construct the WorkbookGlobals object referred to by every instance of
+ the class WorkbookHelper, and execute the import filter by constructing
+ an instance of WorkbookFragment and loading the file. */
+ WorkbookGlobalsRef xBookGlob(WorkbookHelper::constructGlobals(*this));
+ if (xBookGlob)
+ {
+ rtl::Reference<WorkbookFragment> xWorkbookFragment( new WorkbookFragment(*xBookGlob, aWorkbookPath));
+
+ ScDocument& rDoc = xWorkbookFragment->getScDocument();
+ ScDocShell* pDocSh = static_cast<ScDocShell*>(rDoc.GetDocumentShell());
+ assert( pDocSh );
+ pDocSh->SetInitialLinkUpdate( pDocSh->GetMedium());
+
+ bool bRet = importFragment( xWorkbookFragment);
+ if (bRet && !pDocSh->GetErrorCode())
+ {
+ const AddressConverter& rAC = xWorkbookFragment->getAddressConverter();
+ if (rAC.isTabOverflow())
+ pDocSh->SetError(SCWARN_IMPORT_SHEET_OVERFLOW);
+ else if (rAC.isColOverflow())
+ pDocSh->SetError(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ else if (rAC.isRowOverflow())
+ pDocSh->SetError(SCWARN_IMPORT_ROW_OVERFLOW);
+ }
+ return bRet;
+ }
+ }
+ catch (...)
+ {
+ }
+
+ return false;
+}
+
+bool ExcelFilter::exportDocument() noexcept
+{
+ return false;
+}
+
+const ::oox::drawingml::Theme* ExcelFilter::getCurrentTheme() const
+{
+ return &WorkbookHelper( getWorkbookGlobals() ).getTheme();
+}
+
+::oox::vml::Drawing* ExcelFilter::getVmlDrawing()
+{
+ return nullptr;
+}
+
+TableStyleListPtr ExcelFilter::getTableStyles()
+{
+ return TableStyleListPtr();
+}
+
+::oox::drawingml::chart::ChartConverter* ExcelFilter::getChartConverter()
+{
+ return WorkbookHelper( getWorkbookGlobals() ).getChartConverter();
+}
+
+void ExcelFilter::useInternalChartDataTable( bool bInternal )
+{
+ return WorkbookHelper( getWorkbookGlobals() ).useInternalChartDataTable( bInternal );
+}
+
+GraphicHelper* ExcelFilter::implCreateGraphicHelper() const
+{
+ return new ExcelGraphicHelper( getWorkbookGlobals() );
+}
+
+::oox::ole::VbaProject* ExcelFilter::implCreateVbaProject() const
+{
+ return new ExcelVbaProject( getComponentContext(), Reference< XSpreadsheetDocument >( getModel(), UNO_QUERY ) );
+}
+
+sal_Bool SAL_CALL ExcelFilter::filter( const css::uno::Sequence< css::beans::PropertyValue >& rDescriptor )
+{
+ if ( XmlFilterBase::filter( rDescriptor ) )
+ return true;
+
+ if ( isExportFilter() )
+ {
+ bool bExportVBA = exportVBA();
+ Reference< XExporter > xExporter(
+ new XclExpXmlStream( getComponentContext(), bExportVBA, isExportTemplate() ) );
+
+ Reference< XComponent > xDocument = getModel();
+ Reference< XFilter > xFilter( xExporter, UNO_QUERY );
+
+ if ( xFilter.is() )
+ {
+ xExporter->setSourceDocument( xDocument );
+ if ( xFilter->filter( rDescriptor ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+OUString ExcelFilter::getImplementationName()
+{
+ return "com.sun.star.comp.oox.xls.ExcelFilter";
+}
+
+} // namespace oox::xls
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_oox_xls_ExcelFilter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new oox::xls::ExcelFilter(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/excelhandlers.cxx b/sc/source/filter/oox/excelhandlers.cxx
new file mode 100644
index 000000000..ab39116b6
--- /dev/null
+++ b/sc/source/filter/oox/excelhandlers.cxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excelhandlers.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::FragmentHandler2;
+
+WorkbookFragmentBase::WorkbookFragmentBase(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ FragmentHandler2( rHelper.getOoxFilter(), rFragmentPath ),
+ WorkbookHelper( rHelper )
+{
+}
+
+WorksheetFragmentBase::WorksheetFragmentBase(
+ const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ FragmentHandler2( rHelper.getOoxFilter(), rFragmentPath ),
+ WorksheetHelper( rHelper )
+{
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/excelvbaproject.cxx b/sc/source/filter/oox/excelvbaproject.cxx
new file mode 100644
index 000000000..f143c57c7
--- /dev/null
+++ b/sc/source/filter/oox/excelvbaproject.cxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <excelvbaproject.hxx>
+
+#include <vector>
+#include <set>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/script/ModuleType.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+ExcelVbaProject::ExcelVbaProject( const Reference< XComponentContext >& rxContext, const Reference< XSpreadsheetDocument >& rxDocument ) :
+ ::oox::ole::VbaProject( rxContext, Reference< XModel >( rxDocument, UNO_QUERY ), u"Calc" ),
+ mxDocument( rxDocument )
+{
+}
+
+// protected ------------------------------------------------------------------
+
+namespace {
+
+struct SheetCodeNameInfo
+{
+ PropertySet maSheetProps; /// Property set of the sheet without codename.
+ OUString maPrefix; /// Prefix for the codename to be generated.
+
+ explicit SheetCodeNameInfo( const PropertySet& rSheetProps, const OUString& rPrefix ) :
+ maSheetProps( rSheetProps ), maPrefix( rPrefix ) {}
+};
+
+} // namespace
+
+void ExcelVbaProject::prepareImport()
+{
+ /* Check if the sheets have imported codenames. Generate new unused
+ codenames if not. */
+ if( !mxDocument.is() )
+ return;
+
+ try
+ {
+ // collect existing codenames (do not use them when creating new codenames)
+ ::std::set< OUString > aUsedCodeNames;
+
+ // collect sheets without codenames
+ ::std::vector< SheetCodeNameInfo > aCodeNameInfos;
+
+ // iterate over all imported sheets
+ Reference< XEnumerationAccess > xSheetsEA( mxDocument->getSheets(), UNO_QUERY_THROW );
+ Reference< XEnumeration > xSheetsEnum( xSheetsEA->createEnumeration(), UNO_SET_THROW );
+ // own try/catch for every sheet
+ while( xSheetsEnum->hasMoreElements() ) try
+ {
+ PropertySet aSheetProp( xSheetsEnum->nextElement() );
+ OUString aCodeName;
+ aSheetProp.getProperty( aCodeName, PROP_CodeName );
+ if( !aCodeName.isEmpty() )
+ {
+ aUsedCodeNames.insert( aCodeName );
+ }
+ else
+ {
+ // TODO: once we have chart sheets we need a switch/case on sheet type ('SheetNNN' vs. 'ChartNNN')
+ aCodeNameInfos.emplace_back( aSheetProp, "Sheet" );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ // create new codenames if sheets do not have one
+ for (auto & codeName : aCodeNameInfos)
+ {
+ // search for an unused codename
+ sal_Int32 nCounter = 1;
+ OUString aCodeName;
+ do
+ {
+ aCodeName = codeName.maPrefix + OUString::number( nCounter++ );
+ }
+ while( aUsedCodeNames.count( aCodeName ) > 0 );
+ aUsedCodeNames.insert( aCodeName );
+
+ // set codename at sheet
+ codeName.maSheetProps.setProperty( PROP_CodeName, aCodeName );
+
+ // tell base class to create a dummy module
+ addDummyModule( aCodeName, ModuleType::DOCUMENT );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/externallinkbuffer.cxx b/sc/source/filter/oox/externallinkbuffer.cxx
new file mode 100644
index 000000000..595d6e3dd
--- /dev/null
+++ b/sc/source/filter/oox/externallinkbuffer.cxx
@@ -0,0 +1,694 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <externallinkbuffer.hxx>
+#include <externalrefmgr.hxx>
+#include <compiler.hxx>
+#include <tokenarray.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/DDELinkInfo.hpp>
+#include <com/sun/star/sheet/ExternalLinkType.hpp>
+#include <com/sun/star/sheet/XDDELinks.hpp>
+#include <com/sun/star/sheet/XDDELinkResults.hpp>
+#include <com/sun/star/sheet/XExternalDocLinks.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/core/relations.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+using ::oox::core::Relation;
+using ::oox::core::Relations;
+
+namespace {
+
+const sal_uInt16 BIFF12_EXTERNALBOOK_BOOK = 0;
+const sal_uInt16 BIFF12_EXTERNALBOOK_DDE = 1;
+const sal_uInt16 BIFF12_EXTERNALBOOK_OLE = 2;
+
+const sal_uInt16 BIFF12_EXTNAME_AUTOMATIC = 0x0002;
+const sal_uInt16 BIFF12_EXTNAME_PREFERPIC = 0x0004;
+const sal_uInt16 BIFF12_EXTNAME_STDDOCNAME = 0x0008;
+const sal_uInt16 BIFF12_EXTNAME_OLEOBJECT = 0x0010;
+const sal_uInt16 BIFF12_EXTNAME_ICONIFIED = 0x0020;
+
+} // namespace
+
+ExternalNameModel::ExternalNameModel() :
+ mbNotify( false ),
+ mbPreferPic( false ),
+ mbStdDocName( false ),
+ mbOleObj( false ),
+ mbIconified( false )
+{
+}
+
+ExternalName::ExternalName( const ExternalLink& rParentLink ) :
+ DefinedNameBase( rParentLink ),
+ mrParentLink( rParentLink ),
+ mbDdeLinkCreated( false )
+{
+}
+
+void ExternalName::importDefinedName( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ OSL_ENSURE( !maModel.maName.isEmpty(), "ExternalName::importDefinedName - empty name" );
+ maModel.maFormula = rAttribs.getXString(XML_refersTo, OUString());
+ OSL_ENSURE( !maModel.maFormula.isEmpty(), "ExternalName::importDefinedName - empty formula" );
+ // zero-based index into sheet list of externalBook
+ maModel.mnSheet = rAttribs.getInteger( XML_sheetId, -1 );
+ // cache external defined names and formulas
+ ScCompiler aComp(getScDocument(), ScAddress(0, 0, maModel.mnSheet), formula::FormulaGrammar::GRAM_OOXML);
+ aComp.SetExternalLinks(getExternalLinks().getLinkInfos());
+ std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(maModel.maFormula);
+ FormulaError nErr = pArray->GetCodeError();
+ aComp.CompileTokenArray();
+ getScDocument().CheckLinkFormulaNeedingCheck(*pArray);
+ pArray->DelRPN();
+ pArray->SetCodeError(nErr);
+
+ if (pArray->HasReferences())
+ {
+ ScExternalRefManager* pRefMgr = getScDocument().GetExternalRefManager();
+ sal_uInt16 nFileId = pRefMgr->getExternalFileId(mrParentLink.getTargetUrl());
+ pRefMgr->storeRangeNameTokens(nFileId, maModel.maName, *pArray);
+ }
+}
+
+void ExternalName::importDdeItem( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ OSL_ENSURE( !maModel.maName.isEmpty(), "ExternalName::importDdeItem - empty name" );
+ maExtNameModel.mbOleObj = false;
+ maExtNameModel.mbStdDocName = rAttribs.getBool( XML_ole, false );
+ maExtNameModel.mbNotify = rAttribs.getBool( XML_advise, false );
+ maExtNameModel.mbPreferPic = rAttribs.getBool( XML_preferPic, false );
+}
+
+void ExternalName::importValues( const AttributeList& rAttribs )
+{
+ setResultSize( rAttribs.getInteger( XML_cols, 1 ), rAttribs.getInteger( XML_rows, 1 ) );
+}
+
+void ExternalName::importOleItem( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ OSL_ENSURE( !maModel.maName.isEmpty(), "ExternalName::importOleItem - empty name" );
+ maExtNameModel.mbOleObj = true;
+ maExtNameModel.mbNotify = rAttribs.getBool( XML_advise, false );
+ maExtNameModel.mbPreferPic = rAttribs.getBool( XML_preferPic, false );
+ maExtNameModel.mbIconified = rAttribs.getBool( XML_icon, false );
+}
+
+void ExternalName::importExternalName( SequenceInputStream& rStrm )
+{
+ rStrm >> maModel.maName;
+ OSL_ENSURE( !maModel.maName.isEmpty(), "ExternalName::importExternalName - empty name" );
+}
+
+void ExternalName::importExternalNameFlags( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ sal_Int32 nSheetId;
+ nFlags = rStrm.readuInt16();
+ nSheetId = rStrm.readInt32();
+ // index into sheet list of EXTSHEETNAMES (one-based in BIFF12)
+ maModel.mnSheet = nSheetId - 1;
+ // no flag for built-in names, as in OOXML...
+ maExtNameModel.mbNotify = getFlag( nFlags, BIFF12_EXTNAME_AUTOMATIC );
+ maExtNameModel.mbPreferPic = getFlag( nFlags, BIFF12_EXTNAME_PREFERPIC );
+ maExtNameModel.mbStdDocName = getFlag( nFlags, BIFF12_EXTNAME_STDDOCNAME );
+ maExtNameModel.mbOleObj = getFlag( nFlags, BIFF12_EXTNAME_OLEOBJECT );
+ maExtNameModel.mbIconified = getFlag( nFlags, BIFF12_EXTNAME_ICONIFIED );
+ OSL_ENSURE( (mrParentLink.getLinkType() == ExternalLinkType::OLE) == maExtNameModel.mbOleObj,
+ "ExternalName::importExternalNameFlags - wrong OLE flag in external name" );
+}
+
+void ExternalName::importDdeItemValues( SequenceInputStream& rStrm )
+{
+ sal_Int32 nRows, nCols;
+ nRows = rStrm.readInt32();
+ nCols = rStrm.readInt32();
+ setResultSize( nCols, nRows );
+}
+
+void ExternalName::importDdeItemBool( SequenceInputStream& rStrm )
+{
+ appendResultValue< double >( (rStrm.readuInt8() == 0) ? 0.0 : 1.0 );
+}
+
+void ExternalName::importDdeItemDouble( SequenceInputStream& rStrm )
+{
+ appendResultValue( rStrm.readDouble() );
+}
+
+void ExternalName::importDdeItemError( SequenceInputStream& rStrm )
+{
+ appendResultValue( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) );
+}
+
+void ExternalName::importDdeItemString( SequenceInputStream& rStrm )
+{
+ appendResultValue( BiffHelper::readString( rStrm ) );
+}
+
+bool ExternalName::getDdeItemInfo( DDEItemInfo& orItemInfo ) const
+{
+ if( (mrParentLink.getLinkType() == ExternalLinkType::DDE) && !maModel.maName.isEmpty() )
+ {
+ orItemInfo.Item = maModel.maName;
+ orItemInfo.Results = ContainerHelper::matrixToSequenceSequence( maResults );
+ return true;
+ }
+ return false;
+}
+
+bool ExternalName::getDdeLinkData( OUString& orDdeServer, OUString& orDdeTopic, OUString& orDdeItem )
+{
+ if( (mrParentLink.getLinkType() == ExternalLinkType::DDE) && !maModel.maName.isEmpty() )
+ {
+ // try to create a DDE link and to set the imported link results
+ if( !mbDdeLinkCreated ) try
+ {
+ PropertySet aDocProps( getDocument() );
+ Reference< XDDELinks > xDdeLinks( aDocProps.getAnyProperty( PROP_DDELinks ), UNO_QUERY_THROW );
+ mxDdeLink = xDdeLinks->addDDELink( mrParentLink.getClassName(), mrParentLink.getTargetUrl(), maModel.maName, css::sheet::DDELinkMode_DEFAULT );
+ mbDdeLinkCreated = true; // ignore if setting results fails
+ if( !maResults.empty() )
+ {
+ Reference< XDDELinkResults > xResults( mxDdeLink, UNO_QUERY_THROW );
+ xResults->setResults( ContainerHelper::matrixToSequenceSequence( maResults ) );
+ }
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "ExternalName::getDdeLinkData - cannot create DDE link" );
+ }
+ // get link data from created DDE link
+ if( mxDdeLink.is() )
+ {
+ orDdeServer = mxDdeLink->getApplication();
+ orDdeTopic = mxDdeLink->getTopic();
+ orDdeItem = mxDdeLink->getItem();
+ return true;
+ }
+ }
+ return false;
+}
+
+// private --------------------------------------------------------------------
+
+void ExternalName::setResultSize( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ OSL_ENSURE( (mrParentLink.getLinkType() == ExternalLinkType::DDE) || (mrParentLink.getLinkType() == ExternalLinkType::OLE),
+ "ExternalName::setResultSize - wrong link type" );
+ OSL_ENSURE( (nRows > 0) && (nColumns > 0), "ExternalName::setResultSize - invalid matrix size" );
+ const ScAddress& rMaxPos = getAddressConverter().getMaxApiAddress();
+ if( (0 < nRows) && (nRows <= rMaxPos.Row() + 1) && (0 < nColumns) && (nColumns <= rMaxPos.Col() + 1) )
+ maResults.resize( static_cast< size_t >( nColumns ), static_cast< size_t >( nRows ), Any( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) ) );
+ else
+ maResults.clear();
+ maCurrIt = maResults.begin();
+}
+
+void LinkSheetRange::setDeleted()
+{
+ meType = LINKSHEETRANGE_INTERNAL;
+ mnDocLink = mnFirst = mnLast = -1;
+}
+
+void LinkSheetRange::setSameSheet()
+{
+ meType = LINKSHEETRANGE_SAMESHEET;
+ mnDocLink = -1;
+ mnFirst = mnLast = 0;
+}
+
+void LinkSheetRange::setRange( sal_Int32 nFirst, sal_Int32 nLast )
+{
+ meType = LINKSHEETRANGE_INTERNAL;
+ mnDocLink = -1;
+ mnFirst = ::std::min( nFirst, nLast );
+ mnLast = ::std::max( nFirst, nLast );
+}
+
+void LinkSheetRange::setExternalRange( sal_Int32 nDocLink, sal_Int32 nFirst, sal_Int32 nLast )
+{
+ if( nDocLink < 0 )
+ {
+ setDeleted();
+ }
+ else
+ {
+ meType = LINKSHEETRANGE_EXTERNAL;
+ mnDocLink = nDocLink;
+ mnFirst = ::std::min( nFirst, nLast );
+ mnLast = ::std::max( nFirst, nLast );
+ }
+}
+
+ExternalLink::ExternalLink( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ meLinkType( ExternalLinkType::Unknown ),
+ meFuncLibType( FUNCLIB_UNKNOWN )
+{
+}
+
+void ExternalLink::importExternalReference( const AttributeList& rAttribs )
+{
+ maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+}
+
+void ExternalLink::importExternalBook( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ parseExternalReference( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
+}
+
+void ExternalLink::importSheetName( const AttributeList& rAttribs )
+{
+ insertExternalSheet( rAttribs.getXString( XML_val, OUString() ) );
+}
+
+void ExternalLink::importDefinedName( const AttributeList& rAttribs )
+{
+ createExternalName()->importDefinedName( rAttribs );
+}
+
+void ExternalLink::importDdeLink( const AttributeList& rAttribs )
+{
+ OUString aDdeService = rAttribs.getXString( XML_ddeService, OUString() );
+ OUString aDdeTopic = rAttribs.getXString( XML_ddeTopic, OUString() );
+ setDdeOleTargetUrl( aDdeService, aDdeTopic, ExternalLinkType::DDE );
+}
+
+ExternalNameRef ExternalLink::importDdeItem( const AttributeList& rAttribs )
+{
+ ExternalNameRef xExtName = createExternalName();
+ xExtName->importDdeItem( rAttribs );
+ return xExtName;
+}
+
+void ExternalLink::importOleLink( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ OUString aProgId = rAttribs.getXString( XML_progId, OUString() );
+ OUString aTargetUrl = rRelations.getExternalTargetFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ setDdeOleTargetUrl( aProgId, aTargetUrl, ExternalLinkType::OLE );
+}
+
+ExternalNameRef ExternalLink::importOleItem( const AttributeList& rAttribs )
+{
+ ExternalNameRef xExtName = createExternalName();
+ xExtName->importOleItem( rAttribs );
+ return xExtName;
+}
+
+void ExternalLink::importExternalRef( SequenceInputStream& rStrm )
+{
+ rStrm >> maRelId;
+}
+
+void ExternalLink::importExternalSelf( SequenceInputStream& )
+{
+ meLinkType = ExternalLinkType::Self;
+}
+
+void ExternalLink::importExternalSame( SequenceInputStream& )
+{
+ meLinkType = ExternalLinkType::Same;
+}
+
+void ExternalLink::importExternalAddin( SequenceInputStream& )
+{
+ meLinkType = ExternalLinkType::Unknown;
+}
+
+void ExternalLink::importExternalBook( const Relations& rRelations, SequenceInputStream& rStrm )
+{
+ switch( rStrm.readuInt16() )
+ {
+ case BIFF12_EXTERNALBOOK_BOOK:
+ parseExternalReference( rRelations, BiffHelper::readString( rStrm ) );
+ break;
+ case BIFF12_EXTERNALBOOK_DDE:
+ {
+ OUString aDdeService, aDdeTopic;
+ rStrm >> aDdeService >> aDdeTopic;
+ setDdeOleTargetUrl( aDdeService, aDdeTopic, ExternalLinkType::DDE );
+ }
+ break;
+ case BIFF12_EXTERNALBOOK_OLE:
+ {
+ OUString aTargetUrl = rRelations.getExternalTargetFromRelId( BiffHelper::readString( rStrm ) );
+ OUString aProgId = BiffHelper::readString( rStrm );
+ setDdeOleTargetUrl( aProgId, aTargetUrl, ExternalLinkType::OLE );
+ }
+ break;
+ default:
+ OSL_FAIL( "ExternalLink::importExternalBook - unknown link type" );
+ }
+}
+
+void ExternalLink::importExtSheetNames( SequenceInputStream& rStrm )
+{
+ // load external sheet names and create the sheet caches in the Calc document
+ SAL_WARN_IF( (meLinkType != ExternalLinkType::External) && (meLinkType != ExternalLinkType::Library),
+ "sc.filter",
+ "Invalid link type: " << meLinkType );
+ if( meLinkType == ExternalLinkType::External ) // ignore sheets of external libraries
+ for( sal_Int32 nSheet = 0, nCount = rStrm.readInt32(); !rStrm.isEof() && (nSheet < nCount); ++nSheet )
+ insertExternalSheet( BiffHelper::readString( rStrm ) );
+}
+
+ExternalNameRef ExternalLink::importExternalName( SequenceInputStream& rStrm )
+{
+ ExternalNameRef xExtName = createExternalName();
+ xExtName->importExternalName( rStrm );
+ return xExtName;
+}
+
+ExternalLinkInfo ExternalLink::getLinkInfo() const
+{
+ ExternalLinkInfo aLinkInfo;
+ switch( meLinkType )
+ {
+ case ExternalLinkType::Self:
+ case ExternalLinkType::Same:
+ aLinkInfo.Type = css::sheet::ExternalLinkType::SELF;
+ break;
+ case ExternalLinkType::External:
+ aLinkInfo.Type = css::sheet::ExternalLinkType::DOCUMENT;
+ aLinkInfo.Data <<= maTargetUrl;
+ break;
+ case ExternalLinkType::Library:
+ // parser will return library function names in OPCODE_BAD string tokens
+ aLinkInfo.Type = css::sheet::ExternalLinkType::SPECIAL;
+ break;
+ case ExternalLinkType::DDE:
+ {
+ aLinkInfo.Type = css::sheet::ExternalLinkType::DDE;
+ DDELinkInfo aDdeLinkInfo;
+ aDdeLinkInfo.Service = maClassName;
+ aDdeLinkInfo.Topic = maTargetUrl;
+ ::std::vector< DDEItemInfo > aItemInfos;
+ DDEItemInfo aItemInfo;
+ for( const auto& rxExtName : maExtNames )
+ if( rxExtName->getDdeItemInfo( aItemInfo ) )
+ aItemInfos.push_back( aItemInfo );
+ aDdeLinkInfo.Items = comphelper::containerToSequence( aItemInfos );
+ aLinkInfo.Data <<= aDdeLinkInfo;
+ }
+ break;
+ default:
+ aLinkInfo.Type = css::sheet::ExternalLinkType::UNKNOWN;
+ }
+ return aLinkInfo;
+}
+
+FunctionLibraryType ExternalLink::getFuncLibraryType() const
+{
+ return (meLinkType == ExternalLinkType::Library) ? meFuncLibType : FUNCLIB_UNKNOWN;
+}
+
+sal_Int32 ExternalLink::getDocumentLinkIndex() const
+{
+ OSL_ENSURE( meLinkType == ExternalLinkType::External, "ExternalLink::getDocumentLinkIndex - invalid link type" );
+ return mxDocLink.is() ? mxDocLink->getTokenIndex() : -1;
+}
+
+sal_Int32 ExternalLink::getSheetCacheIndex( sal_Int32 nTabId ) const
+{
+ OSL_ENSURE( meLinkType == ExternalLinkType::External, "ExternalLink::getSheetCacheIndex - invalid link type" );
+ return ContainerHelper::getVectorElement( maSheetCaches, nTabId, -1 );
+}
+
+Reference< XExternalSheetCache > ExternalLink::getSheetCache( sal_Int32 nTabId ) const
+{
+ sal_Int32 nCacheIdx = getSheetCacheIndex( nTabId );
+ if( mxDocLink.is() && (nCacheIdx >= 0) ) try
+ {
+ // existing mxDocLink implies that this is an external link
+ Reference< XExternalSheetCache > xSheetCache( mxDocLink->getByIndex( nCacheIdx ), UNO_QUERY_THROW );
+ return xSheetCache;
+ }
+ catch( Exception& )
+ {
+ }
+ return nullptr;
+}
+
+void ExternalLink::getSheetRange( LinkSheetRange& orSheetRange, sal_Int32 nTabId1, sal_Int32 nTabId2 ) const
+{
+ switch( meLinkType )
+ {
+ case ExternalLinkType::Same:
+ orSheetRange.setSameSheet();
+ break;
+
+ case ExternalLinkType::Self:
+ orSheetRange.setRange( nTabId1, nTabId2 );
+ break;
+
+ case ExternalLinkType::External:
+ {
+ sal_Int32 nDocLinkIdx = getDocumentLinkIndex();
+ // BIFF12: passed indexes point into sheet list of EXTSHEETLIST
+ orSheetRange.setExternalRange( nDocLinkIdx, getSheetCacheIndex( nTabId1 ), getSheetCacheIndex( nTabId2 ) );
+ }
+ break;
+
+ default:
+ // unsupported/unexpected link type: #REF! error
+ orSheetRange.setDeleted();
+ }
+}
+
+ExternalNameRef ExternalLink::getNameByIndex( sal_Int32 nIndex ) const
+{
+ return maExtNames.get( nIndex );
+}
+
+// private --------------------------------------------------------------------
+
+void ExternalLink::setExternalTargetUrl( const OUString& rTargetUrl, const OUString& rTargetType )
+{
+ meLinkType = ExternalLinkType::Unknown;
+ if( rTargetType == CREATE_OFFICEDOC_RELATION_TYPE( "externalLinkPath" ) ||
+ rTargetType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT( "externalLinkPath" ) )
+ {
+ maTargetUrl = getBaseFilter().getAbsoluteUrl( rTargetUrl );
+ if( !maTargetUrl.isEmpty() )
+ meLinkType = ExternalLinkType::External;
+ }
+ else if( rTargetType == CREATE_MSOFFICE_RELATION_TYPE( "xlExternalLinkPath/xlPathMissing" ) )
+ {
+ meLinkType = ExternalLinkType::PathMissing;
+ }
+ else if( rTargetType == CREATE_MSOFFICE_RELATION_TYPE( "xlExternalLinkPath/xlLibrary" ) )
+ {
+ meLinkType = ExternalLinkType::Library;
+ meFuncLibType = FunctionProvider::getFuncLibTypeFromLibraryName( rTargetUrl );
+ }
+ SAL_WARN_IF( meLinkType == ExternalLinkType::Unknown, "sc.filter", "Empty target URL or unknown target type, URL='" << rTargetUrl << "', type='" << rTargetType << "'" );
+
+ // create the external document link API object that will contain the sheet caches
+ if( meLinkType == ExternalLinkType::External ) try
+ {
+ PropertySet aDocProps( getDocument() );
+ Reference< XExternalDocLinks > xDocLinks( aDocProps.getAnyProperty( PROP_ExternalDocLinks ), UNO_QUERY_THROW );
+ mxDocLink = xDocLinks->addDocLink( maTargetUrl );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void ExternalLink::setDdeOleTargetUrl( const OUString& rClassName, const OUString& rTargetUrl, ExternalLinkType eLinkType )
+{
+ maClassName = rClassName;
+ maTargetUrl = rTargetUrl;
+ meLinkType = (maClassName.isEmpty() || maTargetUrl.isEmpty()) ? ExternalLinkType::Unknown : eLinkType;
+ OSL_ENSURE( meLinkType == eLinkType, "ExternalLink::setDdeOleTargetUrl - missing classname or target" );
+}
+
+void ExternalLink::parseExternalReference( const Relations& rRelations, const OUString& rRelId )
+{
+ if( const Relation* pRelation = rRelations.getRelationFromRelId( rRelId ) )
+ setExternalTargetUrl( pRelation->maTarget, pRelation->maType );
+}
+
+void ExternalLink::insertExternalSheet( const OUString& rSheetName )
+{
+ OSL_ENSURE( !rSheetName.isEmpty(), "ExternalLink::insertExternalSheet - empty sheet name" );
+ if( mxDocLink.is() )
+ {
+ Reference< XExternalSheetCache > xSheetCache = mxDocLink->addSheetCache( rSheetName, false );
+ sal_Int32 nCacheIdx = xSheetCache.is() ? xSheetCache->getTokenIndex() : -1;
+ maSheetCaches.push_back( nCacheIdx );
+ }
+}
+
+ExternalNameRef ExternalLink::createExternalName()
+{
+ ExternalNameRef xExtName = std::make_shared<ExternalName>( *this );
+ maExtNames.push_back( xExtName );
+ return xExtName;
+}
+
+RefSheetsModel::RefSheetsModel() :
+ mnExtRefId( -1 ),
+ mnTabId1( -1 ),
+ mnTabId2( -1 )
+{
+}
+
+void RefSheetsModel::readBiff12Data( SequenceInputStream& rStrm )
+{
+ mnExtRefId = rStrm.readInt32();
+ mnTabId1 = rStrm.readInt32();
+ mnTabId2 = rStrm.readInt32();
+}
+
+ExternalLinkBuffer::ExternalLinkBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mxSelfRef( std::make_shared<ExternalLink>( rHelper ) ),
+ mbUseRefSheets( false )
+{
+ mxSelfRef->setSelfLinkType();
+}
+
+ExternalLinkRef ExternalLinkBuffer::importExternalReference( const AttributeList& rAttribs )
+{
+ ExternalLinkRef xExtLink = createExternalLink();
+ xExtLink->importExternalReference( rAttribs );
+ maExtLinks.push_back( xExtLink );
+ return xExtLink;
+}
+
+ExternalLinkRef ExternalLinkBuffer::importExternalRef( SequenceInputStream& rStrm )
+{
+ mbUseRefSheets = true;
+ ExternalLinkRef xExtLink = createExternalLink();
+ xExtLink->importExternalRef( rStrm );
+ maExtLinks.push_back( xExtLink );
+ return xExtLink;
+}
+
+void ExternalLinkBuffer::importExternalSelf( SequenceInputStream& rStrm )
+{
+ mbUseRefSheets = true;
+ createExternalLink()->importExternalSelf( rStrm );
+}
+
+void ExternalLinkBuffer::importExternalSame( SequenceInputStream& rStrm )
+{
+ mbUseRefSheets = true;
+ createExternalLink()->importExternalSame( rStrm );
+}
+
+void ExternalLinkBuffer::importExternalAddin( SequenceInputStream& rStrm )
+{
+ mbUseRefSheets = true;
+ createExternalLink()->importExternalAddin( rStrm );
+}
+
+void ExternalLinkBuffer::importExternalSheets( SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( mbUseRefSheets, "ExternalLinkBuffer::importExternalSheets - missing EXTERNALREFS records" );
+ mbUseRefSheets = true;
+ OSL_ENSURE( maRefSheets.empty(), "ExternalLinkBuffer::importExternalSheets - multiple EXTERNALSHEETS records" );
+ maRefSheets.clear();
+ sal_Int32 nRefCount;
+ nRefCount = rStrm.readInt32();
+ size_t nMaxCount = getLimitedValue< size_t, sal_Int64 >( nRefCount, 0, rStrm.getRemaining() / 12 );
+ maRefSheets.reserve( nMaxCount );
+ for( size_t nRefId = 0; !rStrm.isEof() && (nRefId < nMaxCount); ++nRefId )
+ {
+ RefSheetsModel aRefSheets;
+ aRefSheets.readBiff12Data( rStrm );
+ maRefSheets.push_back( aRefSheets );
+ }
+}
+
+Sequence< ExternalLinkInfo > ExternalLinkBuffer::getLinkInfos() const
+{
+ ::std::vector< ExternalLinkInfo > aLinkInfos;
+ // add entry for implicit index 0 (self reference to this document)
+ aLinkInfos.push_back( mxSelfRef->getLinkInfo() );
+ for( const auto& rxExtLink : maExtLinks )
+ aLinkInfos.push_back( rxExtLink->getLinkInfo() );
+ return comphelper::containerToSequence( aLinkInfos );
+}
+
+ExternalLinkRef ExternalLinkBuffer::getExternalLink( sal_Int32 nRefId, bool bUseRefSheets ) const
+{
+ ExternalLinkRef xExtLink;
+ // OOXML: 0 = this document, otherwise one-based index into link list
+ if( !bUseRefSheets || !mbUseRefSheets )
+ xExtLink = (nRefId == 0) ? mxSelfRef : maLinks.get( nRefId - 1 );
+ // BIFF12: zero-based index into ref-sheets list
+ else if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
+ xExtLink = maLinks.get( pRefSheets->mnExtRefId );
+ return xExtLink;
+}
+
+LinkSheetRange ExternalLinkBuffer::getSheetRange( sal_Int32 nRefId ) const
+{
+ OSL_ENSURE( mbUseRefSheets, "ExternalLinkBuffer::getSheetRange - wrong BIFF version" );
+ LinkSheetRange aSheetRange;
+ if( const ExternalLink* pExtLink = getExternalLink( nRefId ).get() )
+ if( const RefSheetsModel* pRefSheets = getRefSheets( nRefId ) )
+ pExtLink->getSheetRange( aSheetRange, pRefSheets->mnTabId1, pRefSheets->mnTabId2 );
+ return aSheetRange;
+}
+
+// private --------------------------------------------------------------------
+
+ExternalLinkRef ExternalLinkBuffer::createExternalLink()
+{
+ ExternalLinkRef xExtLink = std::make_shared<ExternalLink>( *this );
+ maLinks.push_back( xExtLink );
+ return xExtLink;
+}
+
+const RefSheetsModel* ExternalLinkBuffer::getRefSheets( sal_Int32 nRefId ) const
+{
+ return ((0 <= nRefId) && (o3tl::make_unsigned( nRefId ) < maRefSheets.size())) ?
+ &maRefSheets[ static_cast< size_t >( nRefId ) ] : nullptr;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/externallinkfragment.cxx b/sc/source/filter/oox/externallinkfragment.cxx
new file mode 100644
index 000000000..07fbece61
--- /dev/null
+++ b/sc/source/filter/oox/externallinkfragment.cxx
@@ -0,0 +1,338 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <externallinkfragment.hxx>
+
+#include <com/sun/star/sheet/XExternalSheetCache.hpp>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <osl/diagnose.h>
+#include <addressconverter.hxx>
+#include <unitconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+
+ExternalSheetDataContext::ExternalSheetDataContext(
+ WorkbookFragmentBase& rFragment, const Reference< XExternalSheetCache >& rxSheetCache )
+ : WorkbookContextBase(rFragment)
+ , mxSheetCache(rxSheetCache)
+ , mnCurrType(XML_TOKEN_INVALID)
+{
+ OSL_ENSURE( mxSheetCache.is(), "ExternalSheetDataContext::ExternalSheetDataContext - missing sheet cache" );
+}
+
+ContextHandlerRef ExternalSheetDataContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( sheetData ):
+ if( nElement == XLS_TOKEN( row ) ) return this;
+ break;
+ case XLS_TOKEN( row ):
+ if( nElement == XLS_TOKEN( cell ) ) { importCell( rAttribs ); return this; }
+ break;
+ case XLS_TOKEN( cell ):
+ if( nElement == XLS_TOKEN( v ) ) return this; // collect characters in onCharacters()
+ break;
+ }
+ return nullptr;
+}
+
+void ExternalSheetDataContext::onCharacters( const OUString& rChars )
+{
+ if( !isCurrentElement( XLS_TOKEN( v ) ) )
+ return;
+
+ switch( mnCurrType )
+ {
+ case XML_b:
+ case XML_n:
+ setCellValue( Any( rChars.toDouble() ) );
+ break;
+ case XML_e:
+ setCellValue( Any( BiffHelper::calcDoubleFromError( getUnitConverter().calcBiffErrorCode( rChars ) ) ) );
+ break;
+ case XML_str:
+ setCellValue( Any( rChars ) );
+ break;
+ }
+ mnCurrType = XML_TOKEN_INVALID;
+}
+
+ContextHandlerRef ExternalSheetDataContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_EXTSHEETDATA:
+ if( nRecId == BIFF12_ID_EXTROW ) { maCurrPos.SetRow( rStrm.readInt32() ); return this; }
+ break;
+ case BIFF12_ID_EXTROW:
+ switch( nRecId )
+ {
+ case BIFF12_ID_EXTCELL_BLANK: importExtCellBlank( rStrm ); break;
+ case BIFF12_ID_EXTCELL_BOOL: importExtCellBool( rStrm ); break;
+ case BIFF12_ID_EXTCELL_DOUBLE: importExtCellDouble( rStrm ); break;
+ case BIFF12_ID_EXTCELL_ERROR: importExtCellError( rStrm ); break;
+ case BIFF12_ID_EXTCELL_STRING: importExtCellString( rStrm ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+// private --------------------------------------------------------------------
+
+void ExternalSheetDataContext::importCell( const AttributeList& rAttribs )
+{
+ if( getAddressConverter().convertToCellAddress( maCurrPos, rAttribs.getString( XML_r, OUString() ), 0, false ) )
+ mnCurrType = rAttribs.getToken( XML_t, XML_n );
+ else
+ mnCurrType = XML_TOKEN_INVALID;
+}
+
+void ExternalSheetDataContext::importExtCellBlank( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ setCellValue( Any( OUString() ) );
+}
+
+void ExternalSheetDataContext::importExtCellBool( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ double fValue = (rStrm.readuInt8() == 0) ? 0.0 : 1.0;
+ setCellValue( Any( fValue ) );
+}
+
+void ExternalSheetDataContext::importExtCellDouble( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ setCellValue( Any( rStrm.readDouble() ) );
+}
+
+void ExternalSheetDataContext::importExtCellError( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ setCellValue( Any( BiffHelper::calcDoubleFromError( rStrm.readuInt8() ) ) );
+}
+
+void ExternalSheetDataContext::importExtCellString( SequenceInputStream& rStrm )
+{
+ maCurrPos.SetCol( rStrm.readInt32() );
+ setCellValue( Any( BiffHelper::readString( rStrm ) ) );
+}
+
+void ExternalSheetDataContext::setCellValue( const Any& rValue )
+{
+ if( mxSheetCache.is() && getAddressConverter().checkCellAddress( maCurrPos, false ) ) try
+ {
+ mxSheetCache->setCellValue( maCurrPos.Col(), maCurrPos.Row(), rValue );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+ExternalLinkFragment::ExternalLinkFragment( const WorkbookHelper& rHelper,
+ const OUString& rFragmentPath, ExternalLink& rExtLink ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath ),
+ mrExtLink( rExtLink ),
+ mnResultType( XML_TOKEN_INVALID )
+{
+}
+
+ContextHandlerRef ExternalLinkFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( externalLink ) ) return this;
+ break;
+
+ case XLS_TOKEN( externalLink ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( externalBook ): mrExtLink.importExternalBook( getRelations(), rAttribs ); return this;
+ case XLS_TOKEN( ddeLink ): mrExtLink.importDdeLink( rAttribs ); return this;
+ case XLS_TOKEN( oleLink ): mrExtLink.importOleLink( getRelations(), rAttribs ); return this;
+ }
+ break;
+
+ case XLS_TOKEN( externalBook ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheetNames ):
+ case XLS_TOKEN( definedNames ):
+ case XLS_TOKEN( sheetDataSet ): return this;
+ }
+ break;
+
+ case XLS_TOKEN( sheetNames ):
+ if( nElement == XLS_TOKEN( sheetName ) ) mrExtLink.importSheetName( rAttribs );
+ break;
+ case XLS_TOKEN( definedNames ):
+ if( nElement == XLS_TOKEN( definedName ) ) mrExtLink.importDefinedName( rAttribs );
+ break;
+ case XLS_TOKEN( sheetDataSet ):
+ if( (nElement == XLS_TOKEN( sheetData )) && (mrExtLink.getLinkType() == ExternalLinkType::External) )
+ return createSheetDataContext( rAttribs.getInteger( XML_sheetId, -1 ) );
+ break;
+
+ case XLS_TOKEN( ddeLink ):
+ if( nElement == XLS_TOKEN( ddeItems ) ) return this;
+ break;
+ case XLS_TOKEN( ddeItems ):
+ if( nElement == XLS_TOKEN( ddeItem ) )
+ {
+ mxExtName = mrExtLink.importDdeItem( rAttribs );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( ddeItem ):
+ if( nElement == XLS_TOKEN( values ) )
+ {
+ if( mxExtName ) mxExtName->importValues( rAttribs );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( values ):
+ if( nElement == XLS_TOKEN( value ) )
+ {
+ mnResultType = rAttribs.getToken( XML_t, XML_n );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( value ):
+ if( nElement == XLS_TOKEN( val ) ) return this; // collect value in onCharacters()
+ break;
+
+ case XLS_TOKEN( oleLink ):
+ if( nElement == XLS_TOKEN( oleItems ) ) return this;
+ break;
+ case XLS_TOKEN( oleItems ):
+ if( nElement == XLS_TOKEN( oleItem ) ) mxExtName = mrExtLink.importOleItem( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void ExternalLinkFragment::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( val ) ) )
+ maResultValue = rChars;
+}
+
+void ExternalLinkFragment::onEndElement()
+{
+ if( !(isCurrentElement( XLS_TOKEN( value ) ) && mxExtName) )
+ return;
+
+ switch( mnResultType )
+ {
+ case XML_b:
+ mxExtName->appendResultValue( maResultValue.toDouble() );
+ break;
+ case XML_e:
+ mxExtName->appendResultValue( BiffHelper::calcDoubleFromError( getUnitConverter().calcBiffErrorCode( maResultValue ) ) );
+ break;
+ case XML_n:
+ mxExtName->appendResultValue( maResultValue.toDouble() );
+ break;
+ case XML_str:
+ mxExtName->appendResultValue( maResultValue );
+ break;
+ default:
+ mxExtName->appendResultValue( BiffHelper::calcDoubleFromError( BIFF_ERR_NA ) );
+ }
+}
+
+ContextHandlerRef ExternalLinkFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_EXTERNALBOOK )
+ {
+ mrExtLink.importExternalBook( getRelations(), rStrm );
+ return this;
+ }
+ break;
+
+ case BIFF12_ID_EXTERNALBOOK:
+ switch( nRecId )
+ {
+ case BIFF12_ID_EXTSHEETDATA:
+ if( mrExtLink.getLinkType() == ExternalLinkType::External )
+ return createSheetDataContext( rStrm.readInt32() );
+ break;
+
+ case BIFF12_ID_EXTSHEETNAMES: mrExtLink.importExtSheetNames( rStrm ); break;
+ case BIFF12_ID_EXTERNALNAME: mxExtName = mrExtLink.importExternalName( rStrm ); return this;
+ }
+ break;
+
+ case BIFF12_ID_EXTERNALNAME:
+ switch( nRecId )
+ {
+ case BIFF12_ID_EXTERNALNAMEFLAGS: if( mxExtName ) mxExtName->importExternalNameFlags( rStrm ); break;
+ case BIFF12_ID_DDEITEMVALUES: if( mxExtName ) mxExtName->importDdeItemValues( rStrm ); return this;
+ }
+ break;
+
+ case BIFF12_ID_DDEITEMVALUES:
+ switch( nRecId )
+ {
+ case BIFF12_ID_DDEITEM_BOOL: if( mxExtName ) mxExtName->importDdeItemBool( rStrm ); break;
+ case BIFF12_ID_DDEITEM_DOUBLE: if( mxExtName ) mxExtName->importDdeItemDouble( rStrm ); break;
+ case BIFF12_ID_DDEITEM_ERROR: if( mxExtName ) mxExtName->importDdeItemError( rStrm ); break;
+ case BIFF12_ID_DDEITEM_STRING: if( mxExtName ) mxExtName->importDdeItemString( rStrm ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef ExternalLinkFragment::createSheetDataContext( sal_Int32 nSheetId )
+{
+ return new ExternalSheetDataContext( *this, mrExtLink.getSheetCache( nSheetId ) );
+}
+
+const RecordInfo* ExternalLinkFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_DDEITEMVALUES, BIFF12_ID_DDEITEMVALUES + 1 },
+ { BIFF12_ID_EXTERNALBOOK, BIFF12_ID_EXTERNALBOOK + 228 },
+ { BIFF12_ID_EXTERNALNAME, BIFF12_ID_EXTERNALNAME + 10 },
+ { BIFF12_ID_EXTROW, -1 },
+ { BIFF12_ID_EXTSHEETDATA, BIFF12_ID_EXTSHEETDATA + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/extlstcontext.cxx b/sc/source/filter/oox/extlstcontext.cxx
new file mode 100644
index 000000000..67d52fc69
--- /dev/null
+++ b/sc/source/filter/oox/extlstcontext.cxx
@@ -0,0 +1,432 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <extlstcontext.hxx>
+#include <worksheethelper.hxx>
+#include <oox/core/contexthandler.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <colorscale.hxx>
+#include <condformatbuffer.hxx>
+#include <condformatcontext.hxx>
+#include <document.hxx>
+#include <worksheetfragment.hxx>
+#include <workbookfragment.hxx>
+#include <stylesbuffer.hxx>
+#include <stylesfragment.hxx>
+#include <SparklineFragment.hxx>
+
+#include <rangeutl.hxx>
+#include <sal/log.hxx>
+
+using ::oox::core::ContextHandlerRef;
+using ::oox::xls::CondFormatBuffer;
+
+sal_Int32 rStyleIdx = 0;
+
+namespace oox::xls {
+
+ExtCfRuleContext::ExtCfRuleContext( WorksheetContextBase& rFragment, ScDataBarFormatData* pTarget ):
+ WorksheetContextBase( rFragment ),
+ mpTarget( pTarget ),
+ mbFirstEntry(true)
+{
+}
+
+ContextHandlerRef ExtCfRuleContext::onCreateContext( sal_Int32 , const AttributeList& )
+{
+ return this;
+}
+
+void ExtCfRuleContext::onStartElement( const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( dataBar ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importDataBar( rAttribs );
+ break;
+ }
+ case XLS14_TOKEN( negativeFillColor ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importNegativeFillColor( rAttribs );
+ break;
+ }
+ case XLS14_TOKEN( axisColor ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importAxisColor( rAttribs );
+ break;
+ }
+ case XLS14_TOKEN( cfvo ):
+ {
+ ExtCfDataBarRuleRef xRule = getCondFormats().createExtCfDataBarRule(mpTarget);
+ xRule->importCfvo( rAttribs );
+ xRule->getModel().mbIsLower = mbFirstEntry;
+ mbFirstEntry = false;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+namespace {
+ bool IsSpecificTextCondMode(ScConditionMode eMode)
+ {
+ switch (eMode)
+ {
+ case ScConditionMode::BeginsWith:
+ case ScConditionMode::EndsWith:
+ case ScConditionMode::ContainsText:
+ case ScConditionMode::NotContainsText:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+}
+
+ExtConditionalFormattingContext::ExtConditionalFormattingContext(WorksheetContextBase& rFragment)
+ : WorksheetContextBase(rFragment)
+ , nFormulaCount(0)
+ , nPriority(-1)
+ , eOperator(ScConditionMode::NONE)
+ , isPreviousElementF(false)
+{
+}
+
+ContextHandlerRef ExtConditionalFormattingContext::onCreateContext(sal_Int32 nElement, const AttributeList& rAttribs)
+{
+ if (mpCurrentRule)
+ {
+ ScFormatEntry& rFormat = **maEntries.rbegin();
+ assert(rFormat.GetType() == ScFormatEntry::Type::Iconset);
+ ScIconSetFormat& rIconSet = static_cast<ScIconSetFormat&>(rFormat);
+ ScDocument& rDoc = getScDocument();
+ SCTAB nTab = getSheetIndex();
+ ScAddress aPos(0, 0, nTab);
+ mpCurrentRule->SetData(&rIconSet, &rDoc, aPos);
+ mpCurrentRule.reset();
+ }
+ if (nElement == XLS14_TOKEN(cfRule))
+ {
+ OUString aType = rAttribs.getString(XML_type, OUString());
+ OUString aId = rAttribs.getString(XML_id, OUString());
+ nPriority = rAttribs.getInteger( XML_priority, -1 );
+ maPriorities.push_back(nPriority);
+ maModel.nPriority = nPriority;
+
+ if (aType == "dataBar")
+ {
+ // an ext entry does not need to have an existing corresponding entry
+ ExtLst::const_iterator aExt = getExtLst().find( aId );
+ if(aExt == getExtLst().end())
+ return nullptr;
+
+ ScDataBarFormatData* pInfo = aExt->second;
+ if (!pInfo)
+ {
+ return nullptr;
+ }
+ return new ExtCfRuleContext( *this, pInfo );
+ }
+ else if (aType == "iconSet")
+ {
+ ScDocument& rDoc = getScDocument();
+ mpCurrentRule.reset(new IconSetRule(*this));
+ maEntries.push_back(std::make_unique<ScIconSetFormat>(&rDoc));
+ return new IconSetContext(*this, mpCurrentRule.get());
+ }
+ else if (aType == "cellIs")
+ {
+ sal_Int32 aToken = rAttribs.getToken( XML_operator, XML_TOKEN_INVALID );
+ eOperator = CondFormatBuffer::convertToInternalOperator(aToken);
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "containsText")
+ {
+ eOperator = ScConditionMode::ContainsText;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "notContainsText")
+ {
+ eOperator = ScConditionMode::NotContainsText;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "beginsWith")
+ {
+ eOperator = ScConditionMode::BeginsWith;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "endsWith")
+ {
+ eOperator = ScConditionMode::EndsWith;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else if (aType == "expression")
+ {
+ eOperator = ScConditionMode::Direct;
+ maModel.eOperator = eOperator;
+ return this;
+ }
+ else
+ {
+ SAL_WARN("sc", "unhandled XLS14_TOKEN(cfRule) with type: " << aType);
+ }
+ }
+ else if (nElement == XLS14_TOKEN( dxf ))
+ {
+ return new DxfContext( *this, getStyles().createExtDxf() );
+ }
+ else if (nElement == XM_TOKEN( sqref ) || nElement == XM_TOKEN( f ))
+ {
+ if(nElement == XM_TOKEN( f ))
+ nFormulaCount++;
+ return this;
+ }
+
+ return nullptr;
+}
+
+void ExtConditionalFormattingContext::onStartElement(const AttributeList& /*Attribs*/)
+{
+}
+
+void ExtConditionalFormattingContext::onCharacters(const OUString& rCharacters)
+{
+ switch (getCurrentElement())
+ {
+ case XM_TOKEN(f):
+ {
+ aChars = rCharacters;
+ isPreviousElementF = true;
+ }
+ break;
+ case XM_TOKEN(sqref):
+ {
+ aChars = rCharacters;
+ }
+ break;
+ }
+
+}
+
+void ExtConditionalFormattingContext::onEndElement()
+{
+ switch (getCurrentElement())
+ {
+ case XM_TOKEN(f):
+ {
+ if(!IsSpecificTextCondMode(eOperator) || nFormulaCount == 2)
+ maModel.aFormula = aChars;
+ }
+ break;
+ case XLS14_TOKEN( cfRule ):
+ {
+ getStyles().getExtDxfs().forEachMem( &Dxf::finalizeImport );
+ maModel.aStyle = getStyles().createExtDxfStyle(rStyleIdx);
+ rStyleIdx++;
+ nFormulaCount = 0;
+ maModels.push_back(maModel);
+ }
+ break;
+ case XM_TOKEN(sqref):
+ {
+ ScRangeList aRange;
+ ScDocument& rDoc = getScDocument();
+ bool bSuccess = ScRangeStringConverter::GetRangeListFromString(aRange, aChars, rDoc, formula::FormulaGrammar::CONV_XL_OOX);
+ if (!bSuccess || aRange.empty())
+ break;
+
+ SCTAB nTab = getSheetIndex();
+ for (size_t i = 0; i < aRange.size(); ++i)
+ {
+ aRange[i].aStart.SetTab(nTab);
+ aRange[i].aEnd.SetTab(nTab);
+ }
+
+ if (maModels.size() > 1)
+ {
+ std::sort(maModels.begin(), maModels.end(),
+ [](const ExtCondFormatRuleModel& lhs, const ExtCondFormatRuleModel& rhs) {
+ return lhs.nPriority < rhs.nPriority;
+ });
+ }
+
+ if (isPreviousElementF) // sqref can be alone in some cases.
+ {
+ for (size_t i = 0; i < maModels.size(); ++i)
+ {
+ ScAddress rPos = aRange.GetTopLeftCorner();
+ ScCondFormatEntry* pEntry = new ScCondFormatEntry(maModels[i].eOperator, maModels[i].aFormula, "", rDoc,
+ rPos, maModels[i].aStyle, "", "",
+ formula::FormulaGrammar::GRAM_OOXML ,
+ formula::FormulaGrammar::GRAM_OOXML,
+ ScFormatEntry::Type::ExtCondition );
+ maEntries.push_back(std::unique_ptr<ScFormatEntry>(pEntry));
+ }
+
+ assert(maModels.size() == maPriorities.size());
+ maModels.clear();
+ }
+
+ std::vector< std::unique_ptr<ExtCfCondFormat> >& rExtFormats = getCondFormats().importExtCondFormat();
+ rExtFormats.push_back(std::make_unique<ExtCfCondFormat>(aRange, maEntries, &maPriorities));
+
+ maPriorities.clear();
+ isPreviousElementF = false;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+ExtLstLocalContext::ExtLstLocalContext( WorksheetContextBase& rFragment, ScDataBarFormatData* pTarget ):
+ WorksheetContextBase(rFragment),
+ mpTarget(pTarget)
+{
+}
+
+ContextHandlerRef ExtLstLocalContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( extLst ):
+ if(nElement == XLS_TOKEN( ext ))
+ return this;
+ else
+ return nullptr;
+ case XLS_TOKEN( ext ):
+ if (nElement == XLS14_TOKEN( id ))
+ return this;
+ else
+ return nullptr;
+ }
+ return nullptr;
+}
+
+void ExtLstLocalContext::onStartElement( const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( id ):
+ break;
+ }
+}
+
+void ExtLstLocalContext::onCharacters( const OUString& rChars )
+{
+ if (getCurrentElement() == XLS14_TOKEN( id ))
+ {
+ getExtLst().insert( std::pair< OUString, ScDataBarFormatData*>(rChars, mpTarget) );
+ }
+}
+
+ExtGlobalContext::ExtGlobalContext( WorksheetContextBase& rFragment ):
+ WorksheetContextBase(rFragment)
+{
+}
+
+ContextHandlerRef ExtGlobalContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch (nElement)
+ {
+ case XLS14_TOKEN(conditionalFormatting): return new ExtConditionalFormattingContext(*this);
+ case XLS14_TOKEN(dataValidations): return new ExtDataValidationsContext(*this);
+ case XLS14_TOKEN(sparklineGroups): return new SparklineGroupsContext(*this);
+ }
+ return this;
+}
+
+void ExtGlobalContext::onStartElement( const AttributeList& /*rAttribs*/ )
+{
+}
+
+ExtLstGlobalContext::ExtLstGlobalContext( WorksheetFragment& rFragment ):
+ WorksheetContextBase(rFragment)
+{
+}
+
+ContextHandlerRef ExtLstGlobalContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ if (nElement == XLS_TOKEN( ext ))
+ return new ExtGlobalContext( *this );
+
+ return this;
+}
+
+ExtGlobalWorkbookContext::ExtGlobalWorkbookContext( WorkbookContextBase& rFragment ):
+ WorkbookContextBase(rFragment)
+{
+}
+
+ContextHandlerRef ExtGlobalWorkbookContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if (nElement == LOEXT_TOKEN(extCalcPr))
+ {
+ ScDocument& rDoc = getScDocument();
+ sal_Int32 nToken = rAttribs.getToken( XML_stringRefSyntax, XML_CalcA1 );
+ ScCalcConfig aCalcConfig = rDoc.GetCalcConfig();
+
+ switch( nToken )
+ {
+ case XML_CalcA1:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_OOO );
+ break;
+ case XML_ExcelA1:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 );
+ break;
+ case XML_ExcelR1C1:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_R1C1 );
+ break;
+ case XML_CalcA1ExcelA1:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_A1_XL_A1 );
+ break;
+ default:
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_UNSPECIFIED );
+ break;
+ }
+ rDoc.SetCalcConfig(aCalcConfig);
+ }
+
+ return this;
+}
+
+void ExtGlobalWorkbookContext::onStartElement( const AttributeList& /*rAttribs*/ )
+{
+}
+
+ExtLstGlobalWorkbookContext::ExtLstGlobalWorkbookContext( WorkbookFragment& rFragment ):
+ WorkbookContextBase(rFragment)
+{
+}
+
+ContextHandlerRef ExtLstGlobalWorkbookContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ if (nElement == XLS_TOKEN( ext ))
+ return new ExtGlobalWorkbookContext( *this );
+
+ return this;
+}
+
+} //namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/formulabase.cxx b/sc/source/filter/oox/formulabase.cxx
new file mode 100644
index 000000000..f12f0f040
--- /dev/null
+++ b/sc/source/filter/oox/formulabase.cxx
@@ -0,0 +1,1714 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formulabase.hxx>
+#include <rangelst.hxx>
+#include <addressconverter.hxx>
+
+#include <map>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/sheet/AddressConvention.hpp>
+#include <com/sun/star/sheet/ReferenceFlags.hpp>
+#include <com/sun/star/sheet/SingleReference.hpp>
+#include <com/sun/star/sheet/ComplexReference.hpp>
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
+#include <com/sun/star/sheet/FormulaMapGroup.hpp>
+#include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
+#include <com/sun/star/sheet/XFormulaOpCodeMapper.hpp>
+#include <com/sun/star/sheet/XFormulaParser.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/properties.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace {
+
+enum class FuncFlags : sal_uInt16 {
+ NONE = 0x0000,
+ VOLATILE = 0x0001, /// Result is volatile (e.g. NOW() function).
+ IMPORTONLY = 0x0002, /// Only used in import filter.
+ EXPORTONLY = 0x0004, /// Only used in export filter.
+ MACROCALL = 0x0008, /// Function is stored as macro call in BIFF Excel (_xlfn. prefix). OOXML name MUST exist.
+ MACROCALLODF = 0x0010, /// ODF-only function stored as macro call in BIFF Excel (_xlfnodf. prefix). ODF name MUST exist.
+ EXTERNAL = 0x0020, /// Function is external in Calc.
+ MACROFUNC = 0x0040, /// Function is a macro-sheet function.
+ MACROCMD = 0x0080, /// Function is a macro-sheet command.
+ ALWAYSVAR = 0x0100, /// Function is always represented by a tFuncVar token.
+ PARAMPAIRS = 0x0200, /// Optional parameters are expected to appear in pairs.
+ MACROCALL_FN = 0x0400, /** Function is stored as macro call in Excel (_xlfn. prefix)
+ for OOXML. OOXML name MUST exist. Do not use without FuncFlags::MACROCALL. */
+ MACROCALL_NEW = MACROCALL | MACROCALL_FN, /** New Excel functions not
+ defined in OOXML, _xlfn. prefix in all formats. OOXML name
+ must exist. */
+ BIFFEXPORTONLY = 0x1000, /// Only used in BIFF binary export filter.
+ INTERNAL = 0x2000, /// Function is internal in Calc.
+ EUROTOOL = 0x4000, /// function of euro tool lib, FUNCLIB_EUROTOOL
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<FuncFlags> : is_typed_flags<FuncFlags, 0x77ff> {};
+}
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+// reference helpers ==========================================================
+
+BinSingleRef2d::BinSingleRef2d() :
+ mnCol( 0 ),
+ mnRow( 0 ),
+ mbColRel( false ),
+ mbRowRel( false )
+{
+}
+
+void BinSingleRef2d::setBiff12Data( sal_uInt16 nCol, sal_Int32 nRow, bool bRelativeAsOffset )
+{
+ mnCol = nCol & BIFF12_TOK_REF_COLMASK;
+ mnRow = nRow & BIFF12_TOK_REF_ROWMASK;
+ mbColRel = getFlag( nCol, BIFF12_TOK_REF_COLREL );
+ mbRowRel = getFlag( nCol, BIFF12_TOK_REF_ROWREL );
+ if( bRelativeAsOffset && mbColRel && (mnCol > (BIFF12_TOK_REF_COLMASK >> 1)) )
+ mnCol -= (BIFF12_TOK_REF_COLMASK + 1);
+ if( bRelativeAsOffset && mbRowRel && (mnRow > (BIFF12_TOK_REF_ROWMASK >> 1)) )
+ mnRow -= (BIFF12_TOK_REF_ROWMASK + 1);
+}
+
+void BinSingleRef2d::readBiff12Data( SequenceInputStream& rStrm, bool bRelativeAsOffset )
+{
+ sal_Int32 nRow;
+ sal_uInt16 nCol;
+ nRow = rStrm.readInt32();
+ nCol = rStrm.readuInt16();
+ setBiff12Data( nCol, nRow, bRelativeAsOffset );
+}
+
+void BinComplexRef2d::readBiff12Data( SequenceInputStream& rStrm, bool bRelativeAsOffset )
+{
+ sal_Int32 nRow1, nRow2;
+ sal_uInt16 nCol1, nCol2;
+ nRow1 = rStrm.readInt32();
+ nRow2 = rStrm.readInt32();
+ nCol1 = rStrm.readuInt16();
+ nCol2 = rStrm.readuInt16();
+ maRef1.setBiff12Data( nCol1, nRow1, bRelativeAsOffset );
+ maRef2.setBiff12Data( nCol2, nRow2, bRelativeAsOffset );
+}
+
+// token vector, sequence =====================================================
+
+ApiTokenVector::ApiTokenVector()
+{
+}
+
+Any& ApiTokenVector::append( sal_Int32 nOpCode )
+{
+ mvTokens.emplace_back();
+ mvTokens.back().OpCode = nOpCode;
+ return mvTokens.back().Data;
+}
+
+ApiTokenSequence ApiTokenVector::toSequence() const
+{
+ return comphelper::containerToSequence( mvTokens );
+}
+
+// token sequence iterator ====================================================
+
+ApiTokenIterator::ApiTokenIterator( const ApiTokenSequence& rTokens, sal_Int32 nSpacesOpCode ) :
+ mpToken( rTokens.getConstArray() ),
+ mpTokenEnd( rTokens.getConstArray() + rTokens.getLength() ),
+ mnSpacesOpCode( nSpacesOpCode )
+{
+ skipSpaces();
+}
+
+ApiTokenIterator& ApiTokenIterator::operator++()
+{
+ if( is() )
+ {
+ ++mpToken;
+ skipSpaces();
+ }
+ return *this;
+}
+
+void ApiTokenIterator::skipSpaces()
+{
+ while( is() && (mpToken->OpCode == mnSpacesOpCode) )
+ ++mpToken;
+}
+
+// function data ==============================================================
+
+namespace {
+
+const size_t FUNCINFO_PARAMINFOCOUNT = 5; /// Number of parameter type entries.
+
+typedef std::shared_ptr< FunctionInfo > FunctionInfoRef;
+
+struct FunctionData
+{
+ const char* mpcOdfFuncName; /// ODF function name.
+ const char* mpcOoxFuncName; /// OOXML function name.
+ sal_uInt16 mnBiff12FuncId; /// BIFF12 function identifier.
+ sal_uInt16 mnBiffFuncId; /// BIFF2-BIFF8 function identifier.
+ sal_uInt8 mnMinParamCount; /// Minimum number of parameters.
+ sal_uInt8 mnMaxParamCount; /// Maximum number of parameters.
+ sal_uInt8 mnRetClass; /// BIFF token class of the return value.
+ FunctionParamInfo mpParamInfos[ FUNCINFO_PARAMINFOCOUNT ]; /// Information about all parameters.
+ FuncFlags mnFlags; /// Additional flags.
+
+ bool isSupported(bool bImportFilter) const;
+};
+
+bool FunctionData::isSupported(bool bImportFilter) const
+{
+ /* For import filters: the FuncFlags::EXPORTONLY, FuncFlags::BIFFEXPORTONLY
+ must not be set.
+ For export filters: the FuncFlags::IMPORTONLY, FuncFlags::BIFFEXPORTONLY
+ must not be set. */
+ if (bImportFilter)
+ return !(mnFlags & ( FuncFlags::EXPORTONLY | FuncFlags::BIFFEXPORTONLY));
+ else
+ return !(mnFlags & ( FuncFlags::IMPORTONLY | FuncFlags::BIFFEXPORTONLY));
+}
+
+const sal_uInt16 NOID = SAL_MAX_UINT16; /// No BIFF function identifier available.
+const sal_uInt8 MX = SAL_MAX_UINT8; /// Maximum parameter count.
+
+// abbreviations for function return token class
+const sal_uInt8 R = BIFF_TOKCLASS_REF;
+const sal_uInt8 V = BIFF_TOKCLASS_VAL;
+const sal_uInt8 A = BIFF_TOKCLASS_ARR;
+
+// abbreviations for parameter infos
+#define RO { FuncParamValidity::Regular }
+#define RA { FuncParamValidity::Regular }
+#define RR { FuncParamValidity::Regular }
+#define RX { FuncParamValidity::Regular }
+#define VO { FuncParamValidity::Regular }
+#define VV { FuncParamValidity::Regular }
+#define VA { FuncParamValidity::Regular }
+#define VR { FuncParamValidity::Regular }
+#define VX { FuncParamValidity::Regular }
+#define RO_E { FuncParamValidity::ExcelOnly }
+#define VR_E { FuncParamValidity::ExcelOnly }
+#define C { FuncParamValidity::CalcOnly }
+
+// Note: parameter types of all macro sheet functions (FuncFlags::MACROFUNC/FuncFlags::MACROCMD) untested!
+
+/** Functions new in BIFF2. */
+const FunctionData saFuncTableBiff2[] =
+{
+ { "COUNT", "COUNT", 0, 0, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "IF", "IF", 1, 1, 2, 3, R, { VO, RO }, FuncFlags::NONE },
+ { "ISNA", "ISNA", 2, 2, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ISERROR", "ISERROR", 3, 3, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SUM", "SUM", 4, 4, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "AVERAGE", "AVERAGE", 5, 5, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "MIN", "MIN", 6, 6, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "MAX", "MAX", 7, 7, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "ROW", "ROW", 8, 8, 0, 1, V, { RO }, FuncFlags::NONE },
+ { "COLUMN", "COLUMN", 9, 9, 0, 1, V, { RO }, FuncFlags::NONE },
+ { "NA", "NA", 10, 10, 0, 0, V, {}, FuncFlags::NONE },
+ { "NPV", "NPV", 11, 11, 2, MX, V, { VR, RX }, FuncFlags::NONE },
+ { "STDEV", "STDEV", 12, 12, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "DOLLAR", "DOLLAR", 13, 13, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "FIXED", "FIXED", 14, 14, 1, 2, V, { VR, VR, C }, FuncFlags::NONE },
+ { "SIN", "SIN", 15, 15, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "CSC", "SIN", 15, 15, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "COS", "COS", 16, 16, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SEC", "COS", 16, 16, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "TAN", "TAN", 17, 17, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "COT", "TAN", 17, 17, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "ATAN", "ATAN", 18, 18, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ACOT", "ATAN", 18, 18, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "PI", "PI", 19, 19, 0, 0, V, {}, FuncFlags::NONE },
+ { "SQRT", "SQRT", 20, 20, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "EXP", "EXP", 21, 21, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "LN", "LN", 22, 22, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "LOG10", "LOG10", 23, 23, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ABS", "ABS", 24, 24, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "INT", "INT", 25, 25, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SIGN", "SIGN", 26, 26, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ROUND", "ROUND", 27, 27, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "LOOKUP", "LOOKUP", 28, 28, 2, 3, V, { VR, RA }, FuncFlags::NONE },
+ { "INDEX", "INDEX", 29, 29, 2, 4, R, { RA, VV }, FuncFlags::NONE },
+ { "REPT", "REPT", 30, 30, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "MID", "MID", 31, 31, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEN", "LEN", 32, 32, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "VALUE", "VALUE", 33, 33, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "TRUE", "TRUE", 34, 34, 0, 0, V, {}, FuncFlags::NONE },
+ { "FALSE", "FALSE", 35, 35, 0, 0, V, {}, FuncFlags::NONE },
+ { "AND", "AND", 36, 36, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "OR", "OR", 37, 37, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "NOT", "NOT", 38, 38, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "MOD", "MOD", 39, 39, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "DCOUNT", "DCOUNT", 40, 40, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DSUM", "DSUM", 41, 41, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DAVERAGE", "DAVERAGE", 42, 42, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DMIN", "DMIN", 43, 43, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DMAX", "DMAX", 44, 44, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DSTDEV", "DSTDEV", 45, 45, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "VAR", "VAR", 46, 46, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "DVAR", "DVAR", 47, 47, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "TEXT", "TEXT", 48, 48, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "LINEST", "LINEST", 49, 49, 1, 2, A, { RA, RA, C, C }, FuncFlags::NONE },
+ { "TREND", "TREND", 50, 50, 1, 3, A, { RA, RA, RA, C }, FuncFlags::NONE },
+ { "LOGEST", "LOGEST", 51, 51, 1, 2, A, { RA, RA, C, C }, FuncFlags::NONE },
+ { "GROWTH", "GROWTH", 52, 52, 1, 3, A, { RA, RA, RA, C }, FuncFlags::NONE },
+ { "PV", "PV", 56, 56, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "FV", "FV", 57, 57, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "NPER", "NPER", 58, 58, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "PMT", "PMT", 59, 59, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "RATE", "RATE", 60, 60, 3, 6, V, { VR }, FuncFlags::NONE },
+ { "MIRR", "MIRR", 61, 61, 3, 3, V, { RA, VR }, FuncFlags::NONE },
+ { "IRR", "IRR", 62, 62, 1, 2, V, { RA, VR }, FuncFlags::NONE },
+ { "RAND", "RAND", 63, 63, 0, 0, V, {}, FuncFlags::VOLATILE },
+ { "MATCH", "MATCH", 64, 64, 2, 3, V, { VR, RX, RR }, FuncFlags::NONE },
+ { "DATE", "DATE", 65, 65, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "TIME", "TIME", 66, 66, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "DAY", "DAY", 67, 67, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "MONTH", "MONTH", 68, 68, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "YEAR", "YEAR", 69, 69, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "WEEKDAY", "WEEKDAY", 70, 70, 1, 1, V, { VR, C }, FuncFlags::NONE },
+ { "HOUR", "HOUR", 71, 71, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "MINUTE", "MINUTE", 72, 72, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SECOND", "SECOND", 73, 73, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "NOW", "NOW", 74, 74, 0, 0, V, {}, FuncFlags::VOLATILE },
+ { "AREAS", "AREAS", 75, 75, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "ROWS", "ROWS", 76, 76, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "COLUMNS", "COLUMNS", 77, 77, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "OFFSET", "OFFSET", 78, 78, 3, 5, R, { RO, VR }, FuncFlags::VOLATILE },
+ { "SEARCH", "SEARCH", 82, 82, 2, 3, V, { VR }, FuncFlags::NONE },
+ { "TRANSPOSE", "TRANSPOSE", 83, 83, 1, 1, A, { VO }, FuncFlags::NONE },
+ { "TYPE", "TYPE", 86, 86, 1, 1, V, { VX }, FuncFlags::NONE },
+ { "ATAN2", "ATAN2", 97, 97, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "ASIN", "ASIN", 98, 98, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ACOS", "ACOS", 99, 99, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "CHOOSE", "CHOOSE", 100, 100, 2, MX, R, { VO, RO }, FuncFlags::NONE },
+ { "HLOOKUP", "HLOOKUP", 101, 101, 3, 3, V, { VV, RO, RO, C }, FuncFlags::NONE },
+ { "VLOOKUP", "VLOOKUP", 102, 102, 3, 3, V, { VV, RO, RO, C }, FuncFlags::NONE },
+ { "ISREF", "ISREF", 105, 105, 1, 1, V, { RX }, FuncFlags::NONE },
+ { "LOG", "LOG", 109, 109, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "CHAR", "CHAR", 111, 111, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "LOWER", "LOWER", 112, 112, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "UPPER", "UPPER", 113, 113, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "PROPER", "PROPER", 114, 114, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "LEFT", "LEFT", 115, 115, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "RIGHT", "RIGHT", 116, 116, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "EXACT", "EXACT", 117, 117, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "TRIM", "TRIM", 118, 118, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "REPLACE", "REPLACE", 119, 119, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "SUBSTITUTE", "SUBSTITUTE", 120, 120, 3, 4, V, { VR }, FuncFlags::NONE },
+ { "CODE", "CODE", 121, 121, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "FIND", "FIND", 124, 124, 2, 3, V, { VR }, FuncFlags::NONE },
+ { "CELL", "CELL", 125, 125, 1, 2, V, { VV, RO }, FuncFlags::VOLATILE },
+ { "ISERR", "ISERR", 126, 126, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ISTEXT", "ISTEXT", 127, 127, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ISNUMBER", "ISNUMBER", 128, 128, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ISBLANK", "ISBLANK", 129, 129, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "T", "T", 130, 130, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "N", "N", 131, 131, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "DATEVALUE", "DATEVALUE", 140, 140, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "TIMEVALUE", "TIMEVALUE", 141, 141, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SLN", "SLN", 142, 142, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "SYD", "SYD", 143, 143, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "DDB", "DDB", 144, 144, 4, 5, V, { VR }, FuncFlags::NONE },
+ { "INDIRECT", "INDIRECT", 148, 148, 1, 2, R, { VR }, FuncFlags::VOLATILE },
+ { "CLEAN", "CLEAN", 162, 162, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "MDETERM", "MDETERM", 163, 163, 1, 1, V, { VA }, FuncFlags::NONE },
+ { "MINVERSE", "MINVERSE", 164, 164, 1, 1, A, { VA }, FuncFlags::NONE },
+ { "MMULT", "MMULT", 165, 165, 2, 2, A, { VA }, FuncFlags::NONE },
+ { "IPMT", "IPMT", 167, 167, 4, 6, V, { VR }, FuncFlags::NONE },
+ { "PPMT", "PPMT", 168, 168, 4, 6, V, { VR }, FuncFlags::NONE },
+ { "COUNTA", "COUNTA", 169, 169, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "PRODUCT", "PRODUCT", 183, 183, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "FACT", "FACT", 184, 184, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "DPRODUCT", "DPRODUCT", 189, 189, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "ISNONTEXT", "ISNONTEXT", 190, 190, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "STDEVP", "STDEVP", 193, 193, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "VARP", "VARP", 194, 194, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "DSTDEVP", "DSTDEVP", 195, 195, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "DVARP", "DVARP", 196, 196, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "TRUNC", "TRUNC", 197, 197, 1, 1, V, { VR, C }, FuncFlags::NONE },
+ { "ISLOGICAL", "ISLOGICAL", 198, 198, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "DCOUNTA", "DCOUNTA", 199, 199, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { nullptr, "EXTERN.CALL", 255, 255, 1, MX, R, { RO_E, RO }, FuncFlags::IMPORTONLY },
+
+ // *** macro sheet commands ***
+
+ { nullptr, "A1.R1C1", 30, 30, 0, 1, V, { VR }, FuncFlags::MACROCMD },
+ { nullptr, "RETURN", 55, 55, 0, 1, R, { RO }, FuncFlags::MACROFUNC },
+ { nullptr, "ABSREF", 79, 79, 2, 2, R, { VR, RO }, FuncFlags::MACROFUNC },
+ { nullptr, "ADD.ARROW", 81, 81, 0, 0, V, {}, FuncFlags::MACROCMD },
+ { nullptr, "ACTIVE.CELL", 94, 94, 0, 0, R, {}, FuncFlags::MACROFUNC },
+ { nullptr, "ACTIVATE", 103, 103, 0, 2, V, { VR }, FuncFlags::MACROCMD },
+ { nullptr, "ACTIVATE.NEXT", 104, 104, 0, 0, V, {}, FuncFlags::MACROCMD },
+ { nullptr, "ACTIVATE.PREV", 105, 105, 0, 0, V, {}, FuncFlags::MACROCMD },
+ { nullptr, "ADD.BAR", 151, 151, 0, 0, V, {}, FuncFlags::MACROFUNC | FuncFlags::ALWAYSVAR },
+ { nullptr, "ADD.MENU", 152, 152, 2, 2, V, { VR, RO }, FuncFlags::MACROFUNC | FuncFlags::ALWAYSVAR },
+ { nullptr, "ADD.COMMAND", 153, 153, 3, 3, V, { VR, RO }, FuncFlags::MACROFUNC | FuncFlags::ALWAYSVAR }
+};
+
+/** Functions new in BIFF3. */
+const FunctionData saFuncTableBiff3[] =
+{
+ { "LINEST", "LINEST", 49, 49, 1, 4, A, { RA, RA, VV }, FuncFlags::NONE }, // BIFF2: 1-2, BIFF3: 1-4
+ { "TREND", "TREND", 50, 50, 1, 4, A, { RA, RA, RA, VV }, FuncFlags::NONE }, // BIFF2: 1-3, BIFF3: 1-4
+ { "LOGEST", "LOGEST", 51, 51, 1, 4, A, { RA, RA, VV }, FuncFlags::NONE }, // BIFF2: 1-2, BIFF3: 1-4
+ { "GROWTH", "GROWTH", 52, 52, 1, 4, A, { RA, RA, RA, VV }, FuncFlags::NONE }, // BIFF2: 1-3, BIFF3: 1-4
+ { "TRUNC", "TRUNC", 197, 197, 1, 2, V, { VR }, FuncFlags::NONE }, // BIFF2: 1, BIFF3: 1-2
+ { "DOLLAR", "USDOLLAR", 204, 204, 1, 2, V, { VR }, FuncFlags::IMPORTONLY },
+ { "FINDB", "FINDB", 205, 205, 2, 3, V, { VR }, FuncFlags::NONE },
+ { "SEARCHB", "SEARCHB", 206, 206, 2, 3, V, { VR }, FuncFlags::NONE },
+ { "REPLACEB", "REPLACEB", 207, 207, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "LEFTB", "LEFTB", 208, 208, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "RIGHTB", "RIGHTB", 209, 209, 1, 2, V, { VR }, FuncFlags::NONE },
+ { "MIDB", "MIDB", 210, 210, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LENB", "LENB", 211, 211, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ROUNDUP", "ROUNDUP", 212, 212, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "ROUNDDOWN", "ROUNDDOWN", 213, 213, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "ASC", "ASC", 214, 214, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "JIS", "DBCS", 215, 215, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ADDRESS", "ADDRESS", 219, 219, 2, 5, V, { VR }, FuncFlags::NONE },
+ { "DAYS360", "DAYS360", 220, 220, 2, 2, V, { VR, VR, C }, FuncFlags::NONE },
+ { "TODAY", "TODAY", 221, 221, 0, 0, V, {}, FuncFlags::VOLATILE },
+ { "VDB", "VDB", 222, 222, 5, 7, V, { VR }, FuncFlags::NONE },
+ { "MEDIAN", "MEDIAN", 227, 227, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "SUMPRODUCT", "SUMPRODUCT", 228, 228, 1, MX, V, { VA }, FuncFlags::NONE },
+ { "SINH", "SINH", 229, 229, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "CSCH", "SINH", 229, 229, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "COSH", "COSH", 230, 230, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SECH", "COSH", 230, 230, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "TANH", "TANH", 231, 231, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "COTH", "TANH", 231, 231, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "ASINH", "ASINH", 232, 232, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ACOSH", "ACOSH", 233, 233, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ATANH", "ATANH", 234, 234, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "ACOTH", "ATANH", 234, 234, 1, 1, V, { VR }, FuncFlags::BIFFEXPORTONLY },
+ { "DGET", "DGET", 235, 235, 3, 3, V, { RO, RR }, FuncFlags::NONE },
+ { "INFO", "INFO", 244, 244, 1, 1, V, { VR }, FuncFlags::VOLATILE },
+
+ // *** macro sheet commands ***
+
+ { nullptr, "ADD.BAR", 151, 151, 0, 1, V, { VR }, FuncFlags::MACROFUNC }, // BIFF2: 0, BIFF3: 0-1
+ { nullptr, "ADD.MENU", 152, 152, 2, 3, V, { VR, RO }, FuncFlags::MACROFUNC }, // BIFF2: 2, BIFF3: 2-3
+ { nullptr, "ADD.COMMAND", 153, 153, 3, 4, V, { VR, RO }, FuncFlags::MACROFUNC } // BIFF2: 3, BIFF3: 3-4
+};
+
+/** Functions new in BIFF4. */
+const FunctionData saFuncTableBiff4[] =
+{
+ { "FIXED", "FIXED", 14, 14, 1, 3, V, { VR }, FuncFlags::NONE }, // BIFF2-3: 1-2, BIFF4: 1-3
+ { "RANK", "RANK", 216, 216, 2, 3, V, { VR, RO, VR }, FuncFlags::NONE },
+ { "DB", "DB", 247, 247, 4, 5, V, { VR }, FuncFlags::NONE },
+ { "FREQUENCY", "FREQUENCY", 252, 252, 2, 2, A, { RA }, FuncFlags::NONE },
+ { "ERROR.TYPE", "ERROR.TYPE", 261, 261, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "AVEDEV", "AVEDEV", 269, 269, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "BETADIST", "BETADIST", 270, 270, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "GAMMALN", "GAMMALN", 271, 271, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "BETAINV", "BETAINV", 272, 272, 3, 5, V, { VR }, FuncFlags::NONE },
+ { "BINOMDIST", "BINOMDIST", 273, 273, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.CHIDIST", "CHIDIST", 274, 274, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.CHIINV", "CHIINV", 275, 275, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "COMBIN", "COMBIN", 276, 276, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "CONFIDENCE", "CONFIDENCE", 277, 277, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "CRITBINOM", "CRITBINOM", 278, 278, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "EVEN", "EVEN", 279, 279, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "EXPONDIST", "EXPONDIST", 280, 280, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.FDIST", "FDIST", 281, 281, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.FINV", "FINV", 282, 282, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "FISHER", "FISHER", 283, 283, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "FISHERINV", "FISHERINV", 284, 284, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "COM.MICROSOFT.FLOOR", "FLOOR", 285, 285, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "GAMMADIST", "GAMMADIST", 286, 286, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "GAMMAINV", "GAMMAINV", 287, 287, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "COM.MICROSOFT.CEILING", "CEILING", 288, 288, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "HYPGEOMDIST", "HYPGEOMDIST", 289, 289, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "LOGNORMDIST", "LOGNORMDIST", 290, 290, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LOGINV", "LOGINV", 291, 291, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "NEGBINOMDIST", "NEGBINOMDIST", 292, 292, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "NORMDIST", "NORMDIST", 293, 293, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.NORMSDIST", "NORMSDIST", 294, 294, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "NORMINV", "NORMINV", 295, 295, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.NORMSINV", "NORMSINV", 296, 296, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "STANDARDIZE", "STANDARDIZE", 297, 297, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "ODD", "ODD", 298, 298, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "PERMUT", "PERMUT", 299, 299, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "POISSON", "POISSON", 300, 300, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "LEGACY.TDIST", "TDIST", 301, 301, 3, 3, V, { VR }, FuncFlags::NONE },
+ { "WEIBULL", "WEIBULL", 302, 302, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "SUMXMY2", "SUMXMY2", 303, 303, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "SUMX2MY2", "SUMX2MY2", 304, 304, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "SUMX2PY2", "SUMX2PY2", 305, 305, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "LEGACY.CHITEST", "CHITEST", 306, 306, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "CORREL", "CORREL", 307, 307, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "COVAR", "COVAR", 308, 308, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "FORECAST", "FORECAST", 309, 309, 3, 3, V, { VR, VA }, FuncFlags::NONE },
+ { "FTEST", "FTEST", 310, 310, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "INTERCEPT", "INTERCEPT", 311, 311, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "PEARSON", "PEARSON", 312, 312, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "RSQ", "RSQ", 313, 313, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "STEYX", "STEYX", 314, 314, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "SLOPE", "SLOPE", 315, 315, 2, 2, V, { VA }, FuncFlags::NONE },
+ { "TTEST", "TTEST", 316, 316, 4, 4, V, { VA, VA, VR }, FuncFlags::NONE },
+ { "PROB", "PROB", 317, 317, 3, 4, V, { VA, VA, VR }, FuncFlags::NONE },
+ { "DEVSQ", "DEVSQ", 318, 318, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "GEOMEAN", "GEOMEAN", 319, 319, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "HARMEAN", "HARMEAN", 320, 320, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "SUMSQ", "SUMSQ", 321, 321, 0, MX, V, { RX }, FuncFlags::NONE },
+ { "KURT", "KURT", 322, 322, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "SKEW", "SKEW", 323, 323, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "ZTEST", "ZTEST", 324, 324, 2, 3, V, { RX, VR }, FuncFlags::NONE },
+ { "LARGE", "LARGE", 325, 325, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "SMALL", "SMALL", 326, 326, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "QUARTILE", "QUARTILE", 327, 327, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "PERCENTILE", "PERCENTILE", 328, 328, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "PERCENTRANK", "PERCENTRANK", 329, 329, 2, 3, V, { RX, VR, VR_E }, FuncFlags::NONE },
+ { "MODE", "MODE", 330, 330, 1, MX, V, { VA }, FuncFlags::NONE },
+ { "TRIMMEAN", "TRIMMEAN", 331, 331, 2, 2, V, { RX, VR }, FuncFlags::NONE },
+ { "TINV", "TINV", 332, 332, 2, 2, V, { VR }, FuncFlags::NONE },
+
+ // *** Analysis add-in ***
+
+ { "HEX2BIN", "HEX2BIN", 384, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "HEX2DEC", "HEX2DEC", 385, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "HEX2OCT", "HEX2OCT", 386, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DEC2BIN", "DEC2BIN", 387, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DEC2HEX", "DEC2HEX", 388, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DEC2OCT", "DEC2OCT", 389, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "OCT2BIN", "OCT2BIN", 390, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "OCT2HEX", "OCT2HEX", 391, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "OCT2DEC", "OCT2DEC", 392, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "BIN2DEC", "BIN2DEC", 393, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "BIN2OCT", "BIN2OCT", 394, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "BIN2HEX", "BIN2HEX", 395, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMSUB", "IMSUB", 396, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMDIV", "IMDIV", 397, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMPOWER", "IMPOWER", 398, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMABS", "IMABS", 399, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMSQRT", "IMSQRT", 400, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMLN", "IMLN", 401, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMLOG2", "IMLOG2", 402, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMLOG10", "IMLOG10", 403, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMSIN", "IMSIN", 404, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMCOS", "IMCOS", 405, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMEXP", "IMEXP", 406, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMARGUMENT", "IMARGUMENT", 407, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMCONJUGATE", "IMCONJUGATE", 408, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMAGINARY", "IMAGINARY", 409, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMREAL", "IMREAL", 410, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "COMPLEX", "COMPLEX", 411, NOID, 2, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "IMSUM", "IMSUM", 412, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL },
+ { "IMPRODUCT", "IMPRODUCT", 413, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL },
+ { "SERIESSUM", "SERIESSUM", 414, NOID, 4, 4, V, { RR, RR, RR, RX }, FuncFlags::EXTERNAL },
+ { "FACTDOUBLE", "FACTDOUBLE", 415, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "SQRTPI", "SQRTPI", 416, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "QUOTIENT", "QUOTIENT", 417, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DELTA", "DELTA", 418, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "GESTEP", "GESTEP", 419, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "ISEVEN", "ISEVEN", 420, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "ISODD", "ISODD", 421, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "MROUND", "MROUND", 422, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "ERF", "ERF", 423, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "ERFC", "ERFC", 424, NOID, 1, 1, V, { RR }, FuncFlags::EXTERNAL },
+ { "BESSELJ", "BESSELJ", 425, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "BESSELK", "BESSELK", 426, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "BESSELY", "BESSELY", 427, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "BESSELI", "BESSELI", 428, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "XIRR", "XIRR", 429, NOID, 2, 3, V, { RX, RX, RR }, FuncFlags::EXTERNAL },
+ { "XNPV", "XNPV", 430, NOID, 3, 3, V, { RR, RX, RX }, FuncFlags::EXTERNAL },
+ { "PRICEMAT", "PRICEMAT", 431, NOID, 5, 6, V, { RR }, FuncFlags::EXTERNAL },
+ { "YIELDMAT", "YIELDMAT", 432, NOID, 5, 6, V, { RR }, FuncFlags::EXTERNAL },
+ { "INTRATE", "INTRATE", 433, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "RECEIVED", "RECEIVED", 434, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "DISC", "DISC", 435, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "PRICEDISC", "PRICEDISC", 436, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "YIELDDISC", "YIELDDISC", 437, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "TBILLEQ", "TBILLEQ", 438, NOID, 3, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "TBILLPRICE", "TBILLPRICE", 439, NOID, 3, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "TBILLYIELD", "TBILLYIELD", 440, NOID, 3, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "PRICE", "PRICE", 441, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "YIELD", "YIELD", 442, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "DOLLARDE", "DOLLARDE", 443, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "DOLLARFR", "DOLLARFR", 444, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "NOMINAL", "NOMINAL", 445, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "EFFECT", "EFFECT", 446, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "CUMPRINC", "CUMPRINC", 447, NOID, 6, 6, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "CUMIPMT", "CUMIPMT", 448, NOID, 6, 6, V, { RR }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "EDATE", "EDATE", 449, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "EOMONTH", "EOMONTH", 450, NOID, 2, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "YEARFRAC", "YEARFRAC", 451, NOID, 2, 3, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPDAYBS", "COUPDAYBS", 452, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPDAYS", "COUPDAYS", 453, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPDAYSNC", "COUPDAYSNC", 454, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPNCD", "COUPNCD", 455, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPNUM", "COUPNUM", 456, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "COUPPCD", "COUPPCD", 457, NOID, 3, 4, V, { RR }, FuncFlags::EXTERNAL },
+ { "DURATION", "DURATION", 458, NOID, 5, 6, V, { RR }, FuncFlags::EXTERNAL }, // Calc: builtin and add-in (but different!)
+ { "MDURATION", "MDURATION", 459, NOID, 5, 6, V, { RR }, FuncFlags::EXTERNAL },
+ { "ODDLPRICE", "ODDLPRICE", 460, NOID, 7, 8, V, { RR }, FuncFlags::EXTERNAL },
+ { "ODDLYIELD", "ODDLYIELD", 461, NOID, 8, 9, V, { RR }, FuncFlags::EXTERNAL },
+ { "ODDFPRICE", "ODDFPRICE", 462, NOID, 8, 9, V, { RR }, FuncFlags::EXTERNAL },
+ { "ODDFYIELD", "ODDFYIELD", 463, NOID, 8, 9, V, { RR }, FuncFlags::EXTERNAL },
+ { "RANDBETWEEN", "RANDBETWEEN", 464, NOID, 2, 2, V, { RR }, FuncFlags::VOLATILE | FuncFlags::EXTERNAL },
+ { "WEEKNUM", "WEEKNUM", 465, NOID, 1, 2, V, { RR }, FuncFlags::EXTERNAL },
+ { "AMORDEGRC", "AMORDEGRC", 466, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "AMORLINC", "AMORLINC", 467, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "CONVERT", "CONVERT", 468, NOID, 3, 3, V, { RR }, FuncFlags::EXTERNAL }, // Calc: builtin and add-in (but different!)
+ { "ACCRINT", "ACCRINT", 469, NOID, 6, 7, V, { RR }, FuncFlags::EXTERNAL },
+ { "ACCRINTM", "ACCRINTM", 470, NOID, 4, 5, V, { RR }, FuncFlags::EXTERNAL },
+ { "WORKDAY", "WORKDAY", 471, NOID, 2, 3, V, { RR, RR, RX, C }, FuncFlags::EXTERNAL },
+ { "NETWORKDAYS", "NETWORKDAYS", 472, NOID, 2, 3, V, { RR, RR, RX, C }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "GCD", "GCD", 473, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "MULTINOMIAL", "MULTINOMIAL", 474, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL },
+ { "LCM", "LCM", 475, NOID, 1, MX, V, { RX }, FuncFlags::EXTERNAL | FuncFlags::INTERNAL }, // Calc: builtin and add-in
+ { "FVSCHEDULE", "FVSCHEDULE", 476, NOID, 2, 2, V, { RR, RX }, FuncFlags::EXTERNAL },
+
+ // *** macro sheet commands ***
+
+ { nullptr, "ACTIVATE.NEXT", 104, 104, 0, 1, V, { VR }, FuncFlags::MACROCMD }, // BIFF2-3: 0, BIFF4: 0-1
+ { nullptr, "ACTIVATE.PREV", 105, 105, 0, 1, V, { VR }, FuncFlags::MACROCMD } // BIFF2-3: 0, BIFF4: 0-1
+};
+
+/** Functions new in BIFF5/BIFF7. */
+const FunctionData saFuncTableBiff5[] =
+{
+ { "WEEKDAY", "WEEKDAY", 70, 70, 1, 2, V, { VR }, FuncFlags::NONE }, // BIFF2-4: 1, BIFF5: 1-2
+ { "HLOOKUP", "HLOOKUP", 101, 101, 3, 4, V, { VV, RO, RO, VV }, FuncFlags::NONE }, // BIFF2-4: 3, BIFF5: 3-4
+ { "VLOOKUP", "VLOOKUP", 102, 102, 3, 4, V, { VV, RO, RO, VV }, FuncFlags::NONE }, // BIFF2-4: 3, BIFF5: 3-4
+ { "DAYS360", "DAYS360", 220, 220, 2, 3, V, { VR }, FuncFlags::NONE }, // BIFF3-4: 2, BIFF5: 2-3
+ { nullptr, "EXTERN.CALL", 255, 255, 1, MX, R, { RO_E, RO }, FuncFlags::EXPORTONLY }, // MACRO or EXTERNAL
+ { "CONCATENATE", "CONCATENATE", 336, 336, 0, MX, V, { VR }, FuncFlags::NONE },
+ { "POWER", "POWER", 337, 337, 2, 2, V, { VR }, FuncFlags::NONE },
+ { "RADIANS", "RADIANS", 342, 342, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "DEGREES", "DEGREES", 343, 343, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "SUBTOTAL", "SUBTOTAL", 344, 344, 2, MX, V, { VR, RO }, FuncFlags::NONE },
+ { "SUMIF", "SUMIF", 345, 345, 2, 3, V, { RO, VR, RO }, FuncFlags::NONE },
+ { "COUNTIF", "COUNTIF", 346, 346, 2, 2, V, { RO, VR }, FuncFlags::NONE },
+ { "COUNTBLANK", "COUNTBLANK", 347, 347, 1, 1, V, { RO }, FuncFlags::NONE },
+ { "ISPMT", "ISPMT", 350, 350, 4, 4, V, { VR }, FuncFlags::NONE },
+ { "DATEDIF", "DATEDIF", 351, 351, 3, 3, V, { VR }, FuncFlags::NONE },
+ { nullptr, "DATESTRING", 352, 352, 1, 1, V, { VR }, FuncFlags::IMPORTONLY }, // not supported in Calc, missing in OOXML spec
+ { nullptr, "NUMBERSTRING", 353, 353, 2, 2, V, { VR }, FuncFlags::IMPORTONLY }, // not supported in Calc, missing in OOXML spec
+ { "ROMAN", "ROMAN", 354, 354, 1, 2, V, { VR }, FuncFlags::NONE },
+
+ // *** EuroTool add-in ***
+ { "EUROCONVERT", "EUROCONVERT", NOID, NOID, 3, 5, V, { VR }, FuncFlags::EUROTOOL },
+
+ // *** macro sheet commands ***
+
+ { nullptr, "ADD.MENU", 152, 152, 2, 4, V, { VR, RO, RO, VR }, FuncFlags::MACROFUNC }, // BIFF3-4: 2-3, BIFF5: 2-4
+ { nullptr, "ADD.COMMAND", 153, 153, 3, 5, V, { VR, RO, RO, RO, VR }, FuncFlags::MACROFUNC }, // BIFF3-4: 3-4, BIFF5: 3-5
+ { nullptr, "ADD.CHART.AUTOFORMAT", 390, 390, 0, 2, V, { VR }, FuncFlags::MACROCMD },
+ { nullptr, "ADD.LIST.ITEM", 451, 451, 0, 2, V, { VR }, FuncFlags::MACROCMD },
+ { nullptr, "ACTIVE.CELL.FONT", 476, 476, 0, 14, V, { VR }, FuncFlags::MACROCMD }
+};
+
+/** Functions new in BIFF8. */
+const FunctionData saFuncTableBiff8[] =
+{
+ { "GETPIVOTDATA", "GETPIVOTDATA", 358, 358, 2, MX, V, { RR, RR, VR, VR }, FuncFlags::IMPORTONLY | FuncFlags::PARAMPAIRS },
+ { "HYPERLINK", "HYPERLINK", 359, 359, 1, 2, V, { VV, VO }, FuncFlags::NONE },
+ { nullptr, "PHONETIC", 360, 360, 1, 1, V, { RO }, FuncFlags::IMPORTONLY },
+ { "AVERAGEA", "AVERAGEA", 361, 361, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "MAXA", "MAXA", 362, 362, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "MINA", "MINA", 363, 363, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "STDEVPA", "STDEVPA", 364, 364, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "VARPA", "VARPA", 365, 365, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "STDEVA", "STDEVA", 366, 366, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "VARA", "VARA", 367, 367, 1, MX, V, { RX }, FuncFlags::NONE },
+ { "COM.MICROSOFT.BAHTTEXT", "BAHTTEXT", 368, 368, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAIDAYOFWEEK", 369, 369, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAIDIGIT", 370, 370, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAIMONTHOFYEAR", 371, 371, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAINUMSOUND", 372, 372, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAINUMSTRING", 373, 373, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAISTRINGLENGTH", 374, 374, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "ISTHAIDIGIT", 375, 375, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "ROUNDBAHTDOWN", 376, 376, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "ROUNDBAHTUP", 377, 377, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "THAIYEAR", 378, 378, 1, 1, V, { VR }, FuncFlags::MACROCALL },
+ { nullptr, "RTD", 379, 379, 3, 3, A, { VR, VR, RO }, FuncFlags::NONE }
+};
+
+/** Functions new in OOXML. */
+const FunctionData saFuncTableOox[] =
+{
+ { nullptr, "CUBEVALUE", 380, NOID, 1, MX, V, { VR, RX }, FuncFlags::NONE },
+ { nullptr, "CUBEMEMBER", 381, NOID, 2, 3, V, { VR, RX, VR }, FuncFlags::NONE },
+ { nullptr, "CUBEMEMBERPROPERTY", 382, NOID, 3, 3, V, { VR }, FuncFlags::NONE },
+ { nullptr, "CUBERANKEDMEMBER", 383, NOID, 3, 4, V, { VR }, FuncFlags::NONE },
+ { nullptr, "CUBEKPIMEMBER", 477, NOID, 3, 4, V, { VR }, FuncFlags::NONE },
+ { nullptr, "CUBESET", 478, NOID, 2, 5, V, { VR, RX, VR }, FuncFlags::NONE },
+ { nullptr, "CUBESETCOUNT", 479, NOID, 1, 1, V, { VR }, FuncFlags::NONE },
+ { "IFERROR", "IFERROR", 480, NOID, 2, 2, V, { VO, RO }, FuncFlags::MACROCALL },
+ { "COUNTIFS", "COUNTIFS", 481, NOID, 2, MX, V, { RO, VR }, FuncFlags::MACROCALL | FuncFlags::PARAMPAIRS },
+ { "SUMIFS", "SUMIFS", 482, NOID, 3, MX, V, { RO, RO, VR }, FuncFlags::MACROCALL | FuncFlags::PARAMPAIRS },
+ { "AVERAGEIF", "AVERAGEIF", 483, NOID, 2, 3, V, { RO, VR, RO }, FuncFlags::MACROCALL },
+ { "AVERAGEIFS", "AVERAGEIFS", 484, NOID, 3, MX, V, { RO, RO, VR }, FuncFlags::MACROCALL | FuncFlags::PARAMPAIRS },
+ { "COM.MICROSOFT.ISO.CEILING", "ISO.CEILING", NOID, NOID, 1, 2, V, { VR }, FuncFlags::MACROCALL },
+ { "COM.MICROSOFT.NETWORKDAYS.INTL", "NETWORKDAYS.INTL", NOID, NOID, 2, 4, V, { VR, VR, VR, RX }, FuncFlags::MACROCALL },
+ { "COM.MICROSOFT.WORKDAY.INTL", "WORKDAY.INTL", NOID, NOID, 2, 4, V, { VR, VR, VR, RX }, FuncFlags::MACROCALL }
+};
+
+/** Functions new in Excel 2010.
+
+ A lot of statistical functions have been renamed (although the 'old' function name still is valid).
+ See http://office.microsoft.com/en-us/excel-help/what-s-new-changes-made-to-excel-functions-HA010355760.aspx
+
+ Functions with FuncFlags::IMPORTONLY are rewritten in
+ sc/source/filter/excel/xeformula.cxx during export for
+ BIFF, OOXML export uses this different mapping here but still uses the
+ mapping there to determine the feature set.
+
+ FIXME: either have the exporter determine the feature set from the active
+ mapping, preferred, or enhance that mapping there such that for OOXML the
+ rewrite can be overridden.
+
+ @See sc/source/filter/excel/xlformula.cxx saFuncTable_2010
+ */
+/* FIXME: BIFF12 function identifiers available? Where to obtain? */
+const FunctionData saFuncTable2010[] =
+{
+ { "COM.MICROSOFT.COVARIANCE.P", "COVARIANCE.P", NOID, NOID, 2, 2, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.COVARIANCE.S", "COVARIANCE.S", NOID, NOID, 2, 2, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.STDEV.P", "STDEV.P", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.STDEV.S", "STDEV.S", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.VAR.P", "VAR.P" , NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.VAR.S", "VAR.S", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.BETA.DIST", "BETA.DIST" , NOID, NOID, 4, 6, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.BETA.INV", "BETA.INV", NOID, NOID, 3, 5, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.BINOM.DIST", "BINOM.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.BINOM.INV", "BINOM.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.DIST", "CHISQ.DIST", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.INV", "CHISQ.INV", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.DIST.RT", "CHISQ.DIST.RT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.INV.RT", "CHISQ.INV.RT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CHISQ.TEST", "CHISQ.TEST", NOID, NOID, 2, 2, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CONFIDENCE.NORM", "CONFIDENCE.NORM", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CONFIDENCE.T", "CONFIDENCE.T", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "FDIST", "F.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.F.DIST.RT", "F.DIST.RT", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "FINV", "F.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.F.INV.RT", "F.INV.RT", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.F.TEST", "F.TEST", NOID, NOID, 2, 2, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.EXPON.DIST", "EXPON.DIST", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.HYPGEOM.DIST", "HYPGEOM.DIST", NOID, NOID, 5, 5, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.POISSON.DIST", "POISSON.DIST", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.WEIBULL.DIST", "WEIBULL.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.GAMMA.DIST", "GAMMA.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.GAMMA.INV", "GAMMA.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.GAMMALN.PRECISE", "GAMMALN.PRECISE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.LOGNORM.DIST", "LOGNORM.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.LOGNORM.INV", "LOGNORM.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NORM.DIST", "NORM.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NORM.INV", "NORM.INV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NORM.S.DIST", "NORM.S.DIST", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NORM.S.INV", "NORM.S.INV", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.DIST", "T.DIST", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.DIST.2T", "T.DIST.2T", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.DIST.RT", "T.DIST.RT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.INV", "T.INV", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.INV.2T", "T.INV.2T", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.T.TEST", "T.TEST", NOID, NOID, 4, 4, V, { VA, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.PERCENTILE.INC", "PERCENTILE.INC", NOID, NOID, 2, 2, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.PERCENTRANK.INC", "PERCENTRANK.INC", NOID, NOID, 2, 3, V, { RX, VR, VR_E }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.QUARTILE.INC", "QUARTILE.INC", NOID, NOID, 2, 2, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.RANK.EQ", "RANK.EQ", NOID, NOID, 2, 3, V, { VR, RO, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.PERCENTILE.EXC", "PERCENTILE.EXC", NOID, NOID, 2, 2, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.PERCENTRANK.EXC", "PERCENTRANK.EXC", NOID, NOID, 2, 3, V, { RX, VR, VR_E }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.QUARTILE.EXC", "QUARTILE.EXC", NOID, NOID, 2, 2, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.RANK.AVG", "RANK.AVG", NOID, NOID, 2, 3, V, { VR, RO, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.MODE.SNGL", "MODE.SNGL", NOID, NOID, 1, MX, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.MODE.MULT", "MODE.MULT", NOID, NOID, 1, MX, V, { VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.NEGBINOM.DIST", "NEGBINOM.DIST", NOID, NOID, 4, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.Z.TEST", "Z.TEST", NOID, NOID, 2, 3, V, { RX, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CEILING.PRECISE", "CEILING.PRECISE", NOID, NOID, 1, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FLOOR.PRECISE", "FLOOR.PRECISE", NOID, NOID, 1, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.ERF.PRECISE", "ERF.PRECISE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.ERFC.PRECISE", "ERFC.PRECISE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.AGGREGATE", "AGGREGATE", NOID, NOID, 3, MX, V, { VR, RO }, FuncFlags::MACROCALL_NEW }
+};
+
+/** Functions new in Excel 2013.
+
+ See http://office.microsoft.com/en-us/excel-help/new-functions-in-excel-2013-HA103980604.aspx
+ Most functions apparently were added for ODF1.2 ODFF / OpenFormula
+ compatibility.
+
+ Functions with FuncFlags::IMPORTONLY are rewritten in
+ sc/source/filter/excel/xeformula.cxx during export for
+ BIFF, OOXML export uses this different mapping here but still uses the
+ mapping there to determine the feature set.
+
+ FIXME: either have the exporter determine the feature set from the active
+ mapping, preferred, or enhance that mapping there such that for OOXML the
+ rewrite can be overridden.
+
+ @See sc/source/filter/excel/xlformula.cxx saFuncTable_2013
+ */
+/* FIXME: BIFF12 function identifiers available? Where to obtain? */
+const FunctionData saFuncTable2013[] =
+{
+ { "ACOT", "ACOT", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ACOTH", "ACOTH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ARABIC", "ARABIC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BASE", "BASE", NOID, NOID, 2, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BINOM.DIST.RANGE", "BINOM.DIST.RANGE", NOID, NOID, 3, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITAND", "BITAND", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITLSHIFT", "BITLSHIFT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITOR", "BITOR", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITRSHIFT", "BITRSHIFT", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "BITXOR", "BITXOR", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CEILING.MATH", "CEILING.MATH", NOID, NOID, 1, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "CEILING", "CEILING.MATH", NOID, NOID, 1, 3, V, { VR }, FuncFlags::EXPORTONLY | FuncFlags::MACROCALL_NEW },
+ { "COMBINA", "COMBINA", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COT", "COT", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COTH", "COTH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "CSC", "CSC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "CSCH", "CSCH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "DAYS", "DAYS", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "DECIMAL", "DECIMAL", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.ENCODEURL","ENCODEURL", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FILTERXML","FILTERXML", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FLOOR.MATH", "FLOOR.MATH", NOID, NOID, 1, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "FLOOR", "FLOOR.MATH", NOID, NOID, 1, 3, V, { VR }, FuncFlags::EXPORTONLY | FuncFlags::MACROCALL_NEW },
+ // NOTE: this FDIST is not our LEGACY.FDIST
+ { nullptr/*"FDIST"*/, "FDIST", NOID, NOID, 3, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ // NOTE: this FINV is not our LEGACY.FINV
+ { nullptr/*"FINV"*/, "FINV", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "FORMULA", "FORMULATEXT", NOID, NOID, 1, 1, V, { RO }, FuncFlags::MACROCALL_NEW },
+ { "GAMMA", "GAMMA", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "GAUSS", "GAUSS", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "IFNA", "IFNA", NOID, NOID, 2, 2, V, { VO, RO }, FuncFlags::MACROCALL_NEW },
+ { "IMCOSH", "IMCOSH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMCOT", "IMCOT", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMCSC", "IMCSC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMCSCH", "IMCSCH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMSEC", "IMSEC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMSECH", "IMSECH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMSINH", "IMSINH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "IMTAN", "IMTAN", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ISFORMULA", "ISFORMULA", NOID, NOID, 1, 1, V, { RO }, FuncFlags::MACROCALL_NEW },
+ { "ISOWEEKNUM", "ISOWEEKNUM", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "MUNIT", "MUNIT", NOID, NOID, 1, 1, A, { VR }, FuncFlags::MACROCALL_NEW },
+ { "NUMBERVALUE", "NUMBERVALUE", NOID, NOID, 1, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "PDURATION", "PDURATION", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "PERMUTATIONA", "PERMUTATIONA", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "PHI", "PHI", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "RRI", "RRI", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "SEC", "SEC", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "SECH", "SECH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "SHEET", "SHEET", NOID, NOID, 0, 1, V, { RO }, FuncFlags::MACROCALL_NEW },
+ { "SHEETS", "SHEETS", NOID, NOID, 0, 1, V, { RO }, FuncFlags::MACROCALL_NEW },
+ { "SKEWP", "SKEW.P", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "UNICHAR", "UNICHAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "UNICODE", "UNICODE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.WEBSERVICE","WEBSERVICE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "XOR", "XOR", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW }
+};
+
+/** Functions new in Excel 2016.
+
+ See https://support.office.com/en-us/article/Forecasting-functions-897a2fe9-6595-4680-a0b0-93e0308d5f6e?ui=en-US&rs=en-US&ad=US#_forecast.ets
+ and https://support.office.com/en-us/article/What-s-New-and-Improved-in-Office-2016-for-Office-365-95c8d81d-08ba-42c1-914f-bca4603e1426?ui=en-US&rs=en-US&ad=US
+
+ @See sc/source/filter/excel/xlformula.cxx saFuncTable_2016
+ */
+/* FIXME: BIFF12 function identifiers available? Where to obtain? */
+const FunctionData saFuncTable2016[] =
+{
+ { "COM.MICROSOFT.FORECAST.ETS", "FORECAST.ETS", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FORECAST.ETS.CONFINT", "FORECAST.ETS.CONFINT", NOID, NOID, 4, 7, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FORECAST.ETS.SEASONALITY", "FORECAST.ETS.SEASONALITY", NOID, NOID, 2, 4, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FORECAST.ETS.STAT", "FORECAST.ETS.STAT", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.FORECAST.LINEAR", "FORECAST.LINEAR", NOID, NOID, 3, 3, V, { VR, VA }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.CONCAT", "CONCAT", NOID, NOID, 1, MX, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.TEXTJOIN", "TEXTJOIN", NOID, NOID, 3, MX, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.IFS", "IFS", NOID, NOID, 2, MX, R, { VO, RO }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.SWITCH", "SWITCH", NOID, NOID, 3, MX, R, { VO, RO }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.MINIFS", "MINIFS", NOID, NOID, 3, MX, R, { VO, RO }, FuncFlags::MACROCALL_NEW },
+ { "COM.MICROSOFT.MAXIFS", "MAXIFS", NOID, NOID, 3, MX, R, { VO, RO }, FuncFlags::MACROCALL_NEW }
+};
+
+
+/** Functions defined by OpenFormula, but not supported by Calc or by Excel. */
+const FunctionData saFuncTableOdf[] =
+{
+ { "CHISQDIST", nullptr, NOID, NOID, 2, 3, V, { VR }, FuncFlags::MACROCALLODF },
+ { "CHISQINV", nullptr, NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALLODF }
+};
+
+/** Functions defined by Calc, but not in OpenFormula nor supported by Excel. */
+const FunctionData saFuncTableOOoLO[] =
+{
+ { "ORG.OPENOFFICE.WEEKS", "ORG.OPENOFFICE.WEEKS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.MONTHS", "ORG.OPENOFFICE.MONTHS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.YEARS", "ORG.OPENOFFICE.YEARS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.ISLEAPYEAR", "ORG.OPENOFFICE.ISLEAPYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.DAYSINMONTH", "ORG.OPENOFFICE.DAYSINMONTH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.DAYSINYEAR", "ORG.OPENOFFICE.DAYSINYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.WEEKSINYEAR", "ORG.OPENOFFICE.WEEKSINYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.ROT13", "ORG.OPENOFFICE.ROT13", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW | FuncFlags::EXTERNAL },
+ /* Next 8 lines are for importing from .xlsx files saved by Calc before
+ * fdo#59727 was patched with the entries above. */
+ { "ORG.OPENOFFICE.WEEKS", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFWEEKS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.MONTHS", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFMONTHS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.YEARS", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDIFFYEARS", NOID, NOID, 3, 3, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.ISLEAPYEAR", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETISLEAPYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.DAYSINMONTH", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINMONTH", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.DAYSINYEAR", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETDAYSINYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.WEEKSINYEAR", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETWEEKSINYEAR", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ { "ORG.OPENOFFICE.ROT13", "COM.SUN.STAR.SHEET.ADDIN.DATEFUNCTIONS.GETROT13", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY | FuncFlags::EXTERNAL },
+ // More functions written wrongly in the past.
+ { "ORG.OPENOFFICE.ERRORTYPE", "ORG.OPENOFFICE.ERRORTYPE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.MULTIRANGE", "ORG.OPENOFFICE.MULTIRANGE", NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.GOALSEEK", "ORG.OPENOFFICE.GOALSEEK", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.EASTERSUNDAY","ORG.OPENOFFICE.EASTERSUNDAY", NOID, NOID, 1, 1, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.CURRENT", "ORG.OPENOFFICE.CURRENT", NOID, NOID, 0, 0, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.OPENOFFICE.STYLE", "ORG.OPENOFFICE.STYLE", NOID, NOID, 1, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ // And the import for the wrongly written functions even without _xlfn.
+ { "ORG.OPENOFFICE.ERRORTYPE", "ERRORTYPE", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.MULTIRANGE", "MULTIRANGE", NOID, NOID, 1, MX, V, { RX }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.GOALSEEK", "GOALSEEK", NOID, NOID, 3, 3, V, { VR }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.EASTERSUNDAY","EASTERSUNDAY", NOID, NOID, 1, 1, V, { VR }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.CURRENT", "CURRENT", NOID, NOID, 0, 0, V, { VR }, FuncFlags::IMPORTONLY },
+ { "ORG.OPENOFFICE.STYLE", "STYLE", NOID, NOID, 1, 3, V, { VR }, FuncFlags::IMPORTONLY },
+ // Other functions.
+ { "ORG.OPENOFFICE.CONVERT", "ORG.OPENOFFICE.CONVERT", NOID, NOID, 3, 3, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.COLOR", "ORG.LIBREOFFICE.COLOR", NOID, NOID, 3, 4, V, { VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.RAWSUBTRACT","ORG.LIBREOFFICE.RAWSUBTRACT",NOID, NOID, 1, MX, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.FORECAST.ETS.MULT", "ORG.LIBREOFFICE.FORECAST.ETS.MULT", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT", "ORG.LIBREOFFICE.FORECAST.ETS.PI.MULT", NOID, NOID, 4, 7, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", "ORG.LIBREOFFICE.FORECAST.ETS.STAT.MULT", NOID, NOID, 3, 6, V, { VR, VA, VR }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.ROUNDSIG", "ORG.LIBREOFFICE.ROUNDSIG", NOID, NOID, 2, 2, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.REGEX", "ORG.LIBREOFFICE.REGEX", NOID, NOID, 2, 4, V, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.FOURIER", "ORG.LIBREOFFICE.FOURIER", NOID, NOID, 2, 5, A, { RX }, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.RAND.NV", "ORG.LIBREOFFICE.RAND.NV", NOID, NOID, 0, 0, V, {}, FuncFlags::MACROCALL_NEW },
+ { "ORG.LIBREOFFICE.RANDBETWEEN.NV", "ORG.LIBREOFFICE.RANDBETWEEN.NV", NOID, NOID, 2, 2, V, { VR }, FuncFlags::MACROCALL_NEW }
+
+};
+
+const sal_Unicode API_TOKEN_OPEN = '(';
+const sal_Unicode API_TOKEN_CLOSE = ')';
+const sal_Unicode API_TOKEN_SEP = ';';
+
+const sal_Unicode API_TOKEN_ARRAY_OPEN = '{';
+const sal_Unicode API_TOKEN_ARRAY_CLOSE = '}';
+const sal_Unicode API_TOKEN_ARRAY_ROWSEP = '|';
+const sal_Unicode API_TOKEN_ARRAY_COLSEP = ';';
+
+} // namespace
+
+// function info parameter class iterator =====================================
+
+FunctionParamInfoIterator::FunctionParamInfoIterator( const FunctionInfo& rFuncInfo ) :
+ mpParamInfo( rFuncInfo.mpParamInfos ),
+ mpParamInfoEnd( rFuncInfo.mpParamInfos + FUNCINFO_PARAMINFOCOUNT ),
+ mbParamPairs( rFuncInfo.mbParamPairs )
+{
+}
+
+bool FunctionParamInfoIterator::isCalcOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == FuncParamValidity::CalcOnly);
+}
+
+bool FunctionParamInfoIterator::isExcelOnlyParam() const
+{
+ return mpParamInfo && (mpParamInfo->meValid == FuncParamValidity::ExcelOnly);
+}
+
+FunctionParamInfoIterator& FunctionParamInfoIterator::operator++()
+{
+ if( mpParamInfo )
+ {
+ // move pointer to next entry, if something explicit follows
+ if( mpParamInfo + 1 < mpParamInfoEnd )
+ ++mpParamInfo;
+ // if last parameter type is 'Excel-only' or 'Calc-only', do not repeat it
+ else if( isExcelOnlyParam() || isCalcOnlyParam() )
+ mpParamInfo = nullptr;
+ // points to last info, but parameter pairs expected, move to previous info
+ else if( mbParamPairs )
+ --mpParamInfo;
+ // otherwise: repeat last parameter class
+ }
+ return *this;
+}
+
+// function provider ==========================================================
+
+struct FunctionProviderImpl
+{
+ typedef RefMap< OUString, FunctionInfo > FuncNameMap;
+ typedef RefMap< sal_uInt16, FunctionInfo > FuncIdMap;
+
+ FunctionInfoVector maFuncs; /// All function infos in one list.
+ FuncNameMap maOoxFuncs; /// Maps OOXML function names to function data.
+ FuncIdMap maBiff12Funcs; /// Maps BIFF12 function indexes to function data.
+ FuncIdMap maBiffFuncs; /// Maps BIFF2-BIFF8 function indexes to function data.
+ FuncNameMap maMacroFuncs; /// Maps macro function names to function data.
+
+ explicit FunctionProviderImpl(bool bImportFilter);
+
+private:
+ /** Creates and inserts a function info struct from the passed function data. */
+ void initFunc(const FunctionData& rFuncData);
+
+ /** Initializes the members from the passed function data list. */
+ void initFuncs(const FunctionData* pBeg, const FunctionData* pEnd, bool bImportFilter);
+};
+
+FunctionProviderImpl::FunctionProviderImpl( bool bImportFilter )
+{
+ /* Add functions supported in the current BIFF version only. Function
+ tables from later BIFF versions may overwrite single functions from
+ earlier tables. */
+ initFuncs(saFuncTableBiff2, saFuncTableBiff2 + SAL_N_ELEMENTS(saFuncTableBiff2), bImportFilter);
+ initFuncs(saFuncTableBiff3, saFuncTableBiff3 + SAL_N_ELEMENTS(saFuncTableBiff3), bImportFilter);
+ initFuncs(saFuncTableBiff4, saFuncTableBiff4 + SAL_N_ELEMENTS(saFuncTableBiff4), bImportFilter);
+ initFuncs(saFuncTableBiff5, saFuncTableBiff5 + SAL_N_ELEMENTS(saFuncTableBiff5), bImportFilter);
+ initFuncs(saFuncTableBiff8, saFuncTableBiff8 + SAL_N_ELEMENTS(saFuncTableBiff8), bImportFilter);
+ initFuncs(saFuncTableOox , saFuncTableOox + SAL_N_ELEMENTS(saFuncTableOox ), bImportFilter);
+ initFuncs(saFuncTable2010 , saFuncTable2010 + SAL_N_ELEMENTS(saFuncTable2010 ), bImportFilter);
+ initFuncs(saFuncTable2013 , saFuncTable2013 + SAL_N_ELEMENTS(saFuncTable2013 ), bImportFilter);
+ initFuncs(saFuncTable2016 , saFuncTable2016 + SAL_N_ELEMENTS(saFuncTable2016 ), bImportFilter);
+ initFuncs(saFuncTableOdf , saFuncTableOdf + SAL_N_ELEMENTS(saFuncTableOdf ), bImportFilter);
+ initFuncs(saFuncTableOOoLO, saFuncTableOOoLO + SAL_N_ELEMENTS(saFuncTableOOoLO), bImportFilter);
+}
+
+void FunctionProviderImpl::initFunc(const FunctionData& rFuncData)
+{
+ // create a function info object
+ FunctionInfoRef xFuncInfo = std::make_shared<FunctionInfo>();
+ if( rFuncData.mpcOdfFuncName )
+ xFuncInfo->maOdfFuncName = OUString::createFromAscii( rFuncData.mpcOdfFuncName );
+ if( rFuncData.mpcOoxFuncName )
+ xFuncInfo->maOoxFuncName = OUString::createFromAscii( rFuncData.mpcOoxFuncName );
+
+ if( rFuncData.mnFlags & FuncFlags::MACROCALL )
+ {
+ OSL_ENSURE( !xFuncInfo->maOoxFuncName.isEmpty(), "FunctionProviderImpl::initFunc - missing OOXML function name" );
+ OSL_ENSURE( !(rFuncData.mnFlags & FuncFlags::MACROCALLODF ), "FunctionProviderImpl::initFunc - unexpected flag FuncFlags::MACROCALLODF" );
+ xFuncInfo->maBiffMacroName = "_xlfn." + xFuncInfo->maOoxFuncName;
+ if( rFuncData.mnFlags & FuncFlags::MACROCALL_FN )
+ {
+ xFuncInfo->maOoxFuncName = "_xlfn." + xFuncInfo->maOoxFuncName;
+ //! From here on maOoxFuncName contains the _xlfn. prefix!
+ }
+ }
+ else if( rFuncData.mnFlags & FuncFlags::MACROCALLODF )
+ {
+ OSL_ENSURE( !xFuncInfo->maOdfFuncName.isEmpty(), "FunctionProviderImpl::initFunc - missing ODF function name" );
+ xFuncInfo->maBiffMacroName = "_xlfnodf." + xFuncInfo->maOdfFuncName;
+ }
+ xFuncInfo->meFuncLibType = (rFuncData.mnFlags & FuncFlags::EUROTOOL) ? FUNCLIB_EUROTOOL : FUNCLIB_UNKNOWN;
+ xFuncInfo->mnApiOpCode = -1;
+ xFuncInfo->mnBiff12FuncId = rFuncData.mnBiff12FuncId;
+ xFuncInfo->mnBiffFuncId = rFuncData.mnBiffFuncId;
+ xFuncInfo->mnMinParamCount = rFuncData.mnMinParamCount;
+ xFuncInfo->mnMaxParamCount = (rFuncData.mnMaxParamCount == MX) ? OOX_MAX_PARAMCOUNT : rFuncData.mnMaxParamCount;
+ xFuncInfo->mnRetClass = rFuncData.mnRetClass;
+ xFuncInfo->mpParamInfos = rFuncData.mpParamInfos;
+ xFuncInfo->mbParamPairs = bool(rFuncData.mnFlags & FuncFlags::PARAMPAIRS);
+ xFuncInfo->mbVolatile = bool(rFuncData.mnFlags & FuncFlags::VOLATILE);
+ xFuncInfo->mbExternal = bool(rFuncData.mnFlags & FuncFlags::EXTERNAL);
+ xFuncInfo->mbInternal = !xFuncInfo->mbExternal || ( rFuncData.mnFlags & FuncFlags::INTERNAL );
+ bool bMacroCmd(rFuncData.mnFlags & FuncFlags::MACROCMD);
+ xFuncInfo->mbMacroFunc = bMacroCmd || ( rFuncData.mnFlags & FuncFlags::MACROFUNC );
+ xFuncInfo->mbVarParam = bMacroCmd || (rFuncData.mnMinParamCount != rFuncData.mnMaxParamCount) || ( rFuncData.mnFlags & FuncFlags::ALWAYSVAR );
+
+ setFlag( xFuncInfo->mnBiff12FuncId, BIFF_TOK_FUNCVAR_CMD, bMacroCmd );
+ setFlag( xFuncInfo->mnBiffFuncId, BIFF_TOK_FUNCVAR_CMD, bMacroCmd );
+
+ // insert the function info into the member maps
+ maFuncs.push_back( xFuncInfo );
+ if( !xFuncInfo->maOoxFuncName.isEmpty() )
+ maOoxFuncs[ xFuncInfo->maOoxFuncName ] = xFuncInfo;
+ if( xFuncInfo->mnBiff12FuncId != NOID )
+ maBiff12Funcs[ xFuncInfo->mnBiff12FuncId ] = xFuncInfo;
+ if( xFuncInfo->mnBiffFuncId != NOID )
+ maBiffFuncs[ xFuncInfo->mnBiffFuncId ] = xFuncInfo;
+ if( !xFuncInfo->maBiffMacroName.isEmpty() )
+ maMacroFuncs[ xFuncInfo->maBiffMacroName ] = xFuncInfo;
+}
+
+void FunctionProviderImpl::initFuncs(const FunctionData* pBeg, const FunctionData* pEnd, bool bImportFilter)
+{
+ for( const FunctionData* pIt = pBeg; pIt != pEnd; ++pIt )
+ if( pIt->isSupported(bImportFilter) )
+ initFunc(*pIt);
+}
+
+FunctionProvider::FunctionProvider( bool bImportFilter ) :
+ mxFuncImpl( std::make_shared<FunctionProviderImpl>( bImportFilter ) )
+{
+}
+
+FunctionProvider::~FunctionProvider()
+{
+}
+
+const FunctionInfo* FunctionProvider::getFuncInfoFromOoxFuncName( const OUString& rFuncName ) const
+{
+ return mxFuncImpl->maOoxFuncs.get( rFuncName ).get();
+}
+
+const FunctionInfo* FunctionProvider::getFuncInfoFromBiff12FuncId( sal_uInt16 nFuncId ) const
+{
+ return mxFuncImpl->maBiff12Funcs.get( nFuncId ).get();
+}
+
+const FunctionInfo* FunctionProvider::getFuncInfoFromMacroName( const OUString& rFuncName ) const
+{
+ return mxFuncImpl->maMacroFuncs.get( rFuncName ).get();
+}
+
+FunctionLibraryType FunctionProvider::getFuncLibTypeFromLibraryName( std::u16string_view rLibraryName )
+{
+ // the EUROTOOL add-in containing the EUROCONVERT function
+ if( o3tl::equalsIgnoreAsciiCase(rLibraryName, u"EUROTOOL.XLA")
+ || o3tl::equalsIgnoreAsciiCase(rLibraryName, u"EUROTOOL.XLAM"))
+ return FUNCLIB_EUROTOOL;
+
+ // default: unknown library
+ return FUNCLIB_UNKNOWN;
+}
+
+const FunctionInfoVector& FunctionProvider::getFuncs() const
+{
+ return mxFuncImpl->maFuncs;
+}
+
+// op-code and function provider ==============================================
+
+struct OpCodeProviderImpl : public ApiOpCodes
+{
+ typedef RefMap< sal_Int32, FunctionInfo > OpCodeFuncMap;
+ typedef RefMap< OUString, FunctionInfo > FuncNameMap;
+ typedef ::std::vector< FormulaOpCodeMapEntry > OpCodeEntryVector;
+
+ OpCodeFuncMap maOpCodeFuncs; /// Maps API function op-codes to function data.
+ FuncNameMap maExtProgFuncs; /// Maps programmatical API function names to function data.
+ OpCodeEntryVector maParserMap; /// OOXML token mapping for formula parser service.
+
+ explicit OpCodeProviderImpl(
+ const FunctionInfoVector& rFuncInfos,
+ const Reference< XMultiServiceFactory >& rxModelFactory );
+
+private:
+ typedef ::std::map< OUString, ApiToken > ApiTokenMap;
+ typedef Sequence< FormulaOpCodeMapEntry > OpCodeEntrySequence;
+
+ static bool fillEntrySeq( OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup );
+ static bool fillTokenMap( ApiTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup );
+ bool fillFuncTokenMaps( ApiTokenMap& orIntFuncTokenMap, ApiTokenMap& orExtFuncTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper ) const;
+
+ static bool initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId );
+ bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const OUString& rOdfName, const OUString& rOoxName );
+ bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const char* pcOdfName, const char* pcOoxName );
+ bool initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, sal_Unicode cOdfName, sal_Unicode cOoxName );
+
+ bool initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap& rFuncTokenMap );
+ bool initFuncOpCodes( const ApiTokenMap& rIntFuncTokenMap, const ApiTokenMap& rExtFuncTokenMap, const FunctionInfoVector& rFuncInfos );
+};
+
+OpCodeProviderImpl::OpCodeProviderImpl( const FunctionInfoVector& rFuncInfos,
+ const Reference< XMultiServiceFactory >& rxModelFactory )
+{
+ if( !rxModelFactory.is() )
+ return;
+
+ try
+ {
+ Reference< XFormulaOpCodeMapper > xMapper( rxModelFactory->createInstance(
+ "com.sun.star.sheet.FormulaOpCodeMapper" ), UNO_QUERY_THROW );
+
+ // op-codes provided as attributes
+ OPCODE_UNKNOWN = xMapper->getOpCodeUnknown();
+ OPCODE_EXTERNAL = xMapper->getOpCodeExternal();
+
+ using namespace ::com::sun::star::sheet::FormulaMapGroup;
+ using namespace ::com::sun::star::sheet::FormulaMapGroupSpecialOffset;
+
+ OpCodeEntrySequence aEntrySeq;
+ ApiTokenMap aTokenMap, aExtFuncTokenMap;
+ bool bIsValid =
+ // special
+ fillEntrySeq( aEntrySeq, xMapper, SPECIAL ) &&
+ initOpCode( OPCODE_PUSH, aEntrySeq, PUSH ) &&
+ initOpCode( OPCODE_MISSING, aEntrySeq, MISSING ) &&
+ initOpCode( OPCODE_SPACES, aEntrySeq, SPACES ) &&
+ initOpCode( OPCODE_NAME, aEntrySeq, NAME ) &&
+ initOpCode( OPCODE_DBAREA, aEntrySeq, DB_AREA ) &&
+ initOpCode( OPCODE_NLR, aEntrySeq, COL_ROW_NAME ) &&
+ initOpCode( OPCODE_MACRO, aEntrySeq, MACRO ) &&
+ initOpCode( OPCODE_BAD, aEntrySeq, BAD ) &&
+ initOpCode( OPCODE_NONAME, aEntrySeq, NO_NAME ) &&
+ // separators
+ fillTokenMap( aTokenMap, aEntrySeq, xMapper, SEPARATORS ) &&
+ initOpCode( OPCODE_OPEN, aTokenMap, API_TOKEN_OPEN, '(' ) &&
+ initOpCode( OPCODE_CLOSE, aTokenMap, API_TOKEN_CLOSE, ')' ) &&
+ initOpCode( OPCODE_SEP, aTokenMap, API_TOKEN_SEP, ',' ) &&
+ // array separators
+ fillTokenMap( aTokenMap, aEntrySeq, xMapper, ARRAY_SEPARATORS ) &&
+ initOpCode( OPCODE_ARRAY_OPEN, aTokenMap, API_TOKEN_ARRAY_OPEN, '{' ) &&
+ initOpCode( OPCODE_ARRAY_CLOSE, aTokenMap, API_TOKEN_ARRAY_CLOSE, '}' ) &&
+ initOpCode( OPCODE_ARRAY_ROWSEP, aTokenMap, API_TOKEN_ARRAY_ROWSEP, ';' ) &&
+ initOpCode( OPCODE_ARRAY_COLSEP, aTokenMap, API_TOKEN_ARRAY_COLSEP, ',' ) &&
+ // unary operators
+ fillTokenMap( aTokenMap, aEntrySeq, xMapper, UNARY_OPERATORS ) &&
+ initOpCode( OPCODE_PLUS_SIGN, aTokenMap, '+', '\0' ) && // same op-code as OPCODE_ADD
+ initOpCode( OPCODE_MINUS_SIGN, aTokenMap, '-', '-' ) &&
+ initOpCode( OPCODE_PERCENT, aTokenMap, '%', '%' ) &&
+ // binary operators
+ fillTokenMap( aTokenMap, aEntrySeq, xMapper, BINARY_OPERATORS ) &&
+ initOpCode( OPCODE_ADD, aTokenMap, '+', '+' ) &&
+ initOpCode( OPCODE_SUB, aTokenMap, '-', '-' ) &&
+ initOpCode( OPCODE_MULT, aTokenMap, '*', '*' ) &&
+ initOpCode( OPCODE_DIV, aTokenMap, '/', '/' ) &&
+ initOpCode( OPCODE_POWER, aTokenMap, '^', '^' ) &&
+ initOpCode( OPCODE_CONCAT, aTokenMap, '&', '&' ) &&
+ initOpCode( OPCODE_EQUAL, aTokenMap, '=', '=' ) &&
+ initOpCode( OPCODE_NOT_EQUAL, aTokenMap, "<>", "<>" ) &&
+ initOpCode( OPCODE_LESS, aTokenMap, '<', '<' ) &&
+ initOpCode( OPCODE_LESS_EQUAL, aTokenMap, "<=", "<=" ) &&
+ initOpCode( OPCODE_GREATER, aTokenMap, '>', '>' ) &&
+ initOpCode( OPCODE_GREATER_EQUAL, aTokenMap, ">=", ">=" ) &&
+ initOpCode( OPCODE_INTERSECT, aTokenMap, '!', ' ' ) &&
+ initOpCode( OPCODE_LIST, aTokenMap, '~', ',' ) &&
+ initOpCode( OPCODE_RANGE, aTokenMap, ':', ':' ) &&
+ // functions
+ fillFuncTokenMaps( aTokenMap, aExtFuncTokenMap, aEntrySeq, xMapper ) &&
+ initFuncOpCodes( aTokenMap, aExtFuncTokenMap, rFuncInfos ) &&
+ initOpCode( OPCODE_DDE, aTokenMap, "DDE", nullptr );
+
+ OSL_ENSURE( bIsValid, "OpCodeProviderImpl::OpCodeProviderImpl - opcodes not initialized" );
+
+ // OPCODE_PLUS_SIGN and OPCODE_ADD should be equal, otherwise "+" has to be passed above
+ OSL_ENSURE( OPCODE_PLUS_SIGN == OPCODE_ADD, "OpCodeProviderImpl::OpCodeProviderImpl - need opcode mapping for OPCODE_PLUS_SIGN" );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "OpCodeProviderImpl::OpCodeProviderImpl - cannot receive formula opcode mapper" );
+ }
+}
+
+bool OpCodeProviderImpl::fillEntrySeq( OpCodeEntrySequence& orEntrySeq,
+ const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup )
+{
+ try
+ {
+ orEntrySeq = rxMapper->getAvailableMappings( css::sheet::FormulaLanguage::ODFF, nMapGroup );
+ return orEntrySeq.hasElements();
+ }
+ catch( Exception& )
+ {
+ }
+ return false;
+}
+
+bool OpCodeProviderImpl::fillTokenMap( ApiTokenMap& orTokenMap, OpCodeEntrySequence& orEntrySeq,
+ const Reference< XFormulaOpCodeMapper >& rxMapper, sal_Int32 nMapGroup )
+{
+ orTokenMap.clear();
+ if( fillEntrySeq( orEntrySeq, rxMapper, nMapGroup ) )
+ {
+ for( const FormulaOpCodeMapEntry& rEntry : std::as_const(orEntrySeq) )
+ orTokenMap[ rEntry.Name ] = rEntry.Token;
+ }
+ return orEntrySeq.hasElements();
+}
+
+bool OpCodeProviderImpl::fillFuncTokenMaps( ApiTokenMap& orIntFuncTokenMap, ApiTokenMap& orExtFuncTokenMap, OpCodeEntrySequence& orEntrySeq, const Reference< XFormulaOpCodeMapper >& rxMapper ) const
+{
+ orIntFuncTokenMap.clear();
+ orExtFuncTokenMap.clear();
+ if( fillEntrySeq( orEntrySeq, rxMapper, css::sheet::FormulaMapGroup::FUNCTIONS ) )
+ {
+ for( const FormulaOpCodeMapEntry& rEntry : std::as_const(orEntrySeq) )
+ ((rEntry.Token.OpCode == OPCODE_EXTERNAL) ? orExtFuncTokenMap : orIntFuncTokenMap)[ rEntry.Name ] = rEntry.Token;
+ }
+ return orEntrySeq.hasElements();
+}
+
+bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const OpCodeEntrySequence& rEntrySeq, sal_Int32 nSpecialId )
+{
+ if( (0 <= nSpecialId) && (nSpecialId < rEntrySeq.getLength()) )
+ {
+ ornOpCode = rEntrySeq[ nSpecialId ].Token.OpCode;
+ return true;
+ }
+ OSL_FAIL( OString( OString::Concat("OpCodeProviderImpl::initOpCode - opcode for special offset ") +
+ OString::number( nSpecialId ) + " not found" ).getStr() );
+ return false;
+}
+
+bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const OUString& rOdfName, const OUString& rOoxName )
+{
+ ApiTokenMap::const_iterator aIt = rTokenMap.find( rOdfName );
+ if( aIt != rTokenMap.end() )
+ {
+ ornOpCode = aIt->second.OpCode;
+ if( !rOoxName.isEmpty() )
+ {
+ FormulaOpCodeMapEntry aEntry;
+ aEntry.Name = rOoxName;
+ aEntry.Token.OpCode = ornOpCode;
+ maParserMap.push_back( aEntry );
+ }
+ return true;
+ }
+ OSL_FAIL( OStringBuffer( "OpCodeProviderImpl::initOpCode - opcode for \"" +
+ OUStringToOString( rOdfName, RTL_TEXTENCODING_ASCII_US ) +
+ "\" not found" ).getStr() );
+ return false;
+}
+
+bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, const char* pcOdfName, const char* pcOoxName )
+{
+ OUString aOoxName;
+ if( pcOoxName ) aOoxName = OUString::createFromAscii( pcOoxName );
+ return initOpCode( ornOpCode, rTokenMap, OUString::createFromAscii( pcOdfName ), aOoxName );
+}
+
+bool OpCodeProviderImpl::initOpCode( sal_Int32& ornOpCode, const ApiTokenMap& rTokenMap, sal_Unicode cOdfName, sal_Unicode cOoxName )
+{
+ OUString aOoxName;
+ if( cOoxName ) aOoxName = OUString( cOoxName );
+ return initOpCode( ornOpCode, rTokenMap, OUString( cOdfName ), aOoxName );
+}
+
+bool OpCodeProviderImpl::initFuncOpCode( FunctionInfo& orFuncInfo, const ApiTokenMap& rFuncTokenMap )
+{
+ bool bIsValid = false;
+ if( !orFuncInfo.maOdfFuncName.isEmpty() )
+ {
+ ApiTokenMap::const_iterator aIt = rFuncTokenMap.find( orFuncInfo.maOdfFuncName );
+ if( aIt != rFuncTokenMap.end() )
+ {
+ orFuncInfo.mnApiOpCode = aIt->second.OpCode;
+ bIsValid =
+ (orFuncInfo.mnApiOpCode >= 0) &&
+ (orFuncInfo.mnApiOpCode != OPCODE_UNKNOWN) &&
+ (orFuncInfo.mnApiOpCode != OPCODE_NONAME);
+ OSL_ENSURE( bIsValid,
+ OStringBuffer( "OpCodeProviderImpl::initFuncOpCode - no valid opcode for ODF function \"" ).
+ append( OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US ) ).
+ append( '"' ).getStr() );
+
+ if( bIsValid && (orFuncInfo.mnApiOpCode == OPCODE_EXTERNAL) )
+ {
+ bIsValid = (aIt->second.Data >>= orFuncInfo.maExtProgName) && !orFuncInfo.maExtProgName.isEmpty();
+ OSL_ENSURE( bIsValid,
+ OStringBuffer( "OpCodeProviderImpl::initFuncOpCode - no programmatical name for external function \"" ).
+ append( OUStringToOString( orFuncInfo.maOdfFuncName, RTL_TEXTENCODING_ASCII_US ) ).
+ append( '"' ).getStr() );
+ }
+
+ // add to parser map, if OOXML function name exists
+ if( bIsValid && !orFuncInfo.maOoxFuncName.isEmpty() )
+ {
+ // create the parser map entry
+ FormulaOpCodeMapEntry aEntry;
+ aEntry.Name = orFuncInfo.maOoxFuncName;
+ aEntry.Token = aIt->second;
+ maParserMap.push_back( aEntry );
+ }
+ }
+ else
+ {
+ // ignore entries for functions unknown by Calc *and* by Excel
+ bIsValid = orFuncInfo.maOoxFuncName.isEmpty();
+ SAL_WARN_IF( !bIsValid, "sc",
+ "OpCodeProviderImpl::initFuncOpCode - no opcode mapping for function ODF '" <<
+ orFuncInfo.maOdfFuncName << "' <-> OOXML '" << orFuncInfo.maOoxFuncName << "'");
+ }
+ }
+ else if( orFuncInfo.mnBiffFuncId == BIFF_FUNC_EXTERNCALL )
+ {
+ orFuncInfo.mnApiOpCode = OPCODE_EXTERNAL;
+ bIsValid = true;
+ }
+ else if( !orFuncInfo.maOoxFuncName.isEmpty() )
+ {
+ orFuncInfo.mnApiOpCode = OPCODE_BAD;
+ bIsValid = true;
+ }
+
+ if( !bIsValid || (orFuncInfo.mnApiOpCode == OPCODE_UNKNOWN) || (orFuncInfo.mnApiOpCode < 0) )
+ orFuncInfo.mnApiOpCode = OPCODE_NONAME;
+ return bIsValid;
+}
+
+bool OpCodeProviderImpl::initFuncOpCodes( const ApiTokenMap& rIntFuncTokenMap, const ApiTokenMap& rExtFuncTokenMap, const FunctionInfoVector& rFuncInfos )
+{
+ bool bIsValid = true;
+ for( const FunctionInfoRef& xFuncInfo : rFuncInfos )
+ {
+ // set API opcode from ODF function name
+ if (xFuncInfo->mbExternal)
+ bIsValid &= initFuncOpCode( *xFuncInfo, rExtFuncTokenMap );
+ if (xFuncInfo->mbInternal)
+ bIsValid &= initFuncOpCode( *xFuncInfo, rIntFuncTokenMap );
+ // insert the function info into the maps
+ if( (xFuncInfo->mnApiOpCode != OPCODE_NONAME) && (xFuncInfo->mnApiOpCode != OPCODE_BAD) )
+ {
+ if( (xFuncInfo->mnApiOpCode == OPCODE_EXTERNAL) && !xFuncInfo->maExtProgName.isEmpty() )
+ maExtProgFuncs[ xFuncInfo->maExtProgName ] = xFuncInfo;
+ else
+ maOpCodeFuncs[ xFuncInfo->mnApiOpCode ] = xFuncInfo;
+ }
+ }
+ return bIsValid;
+}
+
+OpCodeProvider::OpCodeProvider( const Reference< XMultiServiceFactory >& rxModelFactory,
+ bool bImportFilter ) :
+ FunctionProvider( bImportFilter ),
+ mxOpCodeImpl( std::make_shared<OpCodeProviderImpl>( getFuncs(), rxModelFactory ) )
+{
+}
+
+OpCodeProvider::~OpCodeProvider()
+{
+}
+
+const ApiOpCodes& OpCodeProvider::getOpCodes() const
+{
+ return *mxOpCodeImpl;
+}
+
+const FunctionInfo* OpCodeProvider::getFuncInfoFromApiToken( const ApiToken& rToken ) const
+{
+ const FunctionInfo* pFuncInfo = nullptr;
+ if( (rToken.OpCode == mxOpCodeImpl->OPCODE_EXTERNAL) && rToken.Data.has< OUString >() )
+ pFuncInfo = mxOpCodeImpl->maExtProgFuncs.get( rToken.Data.get< OUString >() ).get();
+ else if( (rToken.OpCode == mxOpCodeImpl->OPCODE_MACRO) && rToken.Data.has< OUString >() )
+ pFuncInfo = getFuncInfoFromMacroName( rToken.Data.get< OUString >() );
+ else if( (rToken.OpCode == mxOpCodeImpl->OPCODE_BAD) && rToken.Data.has< OUString >() )
+ pFuncInfo = getFuncInfoFromOoxFuncName( rToken.Data.get< OUString >() );
+ else
+ pFuncInfo = mxOpCodeImpl->maOpCodeFuncs.get( rToken.OpCode ).get();
+ return pFuncInfo;
+}
+
+Sequence< FormulaOpCodeMapEntry > OpCodeProvider::getOoxParserMap() const
+{
+ return comphelper::containerToSequence( mxOpCodeImpl->maParserMap );
+}
+
+// API formula parser wrapper =================================================
+
+ApiParserWrapper::ApiParserWrapper(
+ const Reference< XMultiServiceFactory >& rxModelFactory, const OpCodeProvider& rOpCodeProv ) :
+ OpCodeProvider( rOpCodeProv )
+{
+ if( rxModelFactory.is() ) try
+ {
+ mxParser.set( rxModelFactory->createInstance( "com.sun.star.sheet.FormulaParser" ), UNO_QUERY_THROW );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( mxParser.is(), "ApiParserWrapper::ApiParserWrapper - cannot create API formula parser object" );
+ maParserProps.set( mxParser );
+ maParserProps.setProperty( PROP_CompileEnglish, true );
+ maParserProps.setProperty( PROP_FormulaConvention, css::sheet::AddressConvention::XL_OOX );
+ maParserProps.setProperty( PROP_IgnoreLeadingSpaces, false );
+ maParserProps.setProperty( PROP_OpCodeMap, getOoxParserMap() );
+}
+
+ApiTokenSequence ApiParserWrapper::parseFormula( const OUString& rFormula, const ScAddress& rRefPos )
+{
+ ApiTokenSequence aTokenSeq;
+ if( mxParser.is() ) try
+ {
+ aTokenSeq = mxParser->parseFormula( rFormula,
+ CellAddress(rRefPos.Tab(), rRefPos.Col(), rRefPos.Row()) );
+ }
+ catch( Exception& )
+ {
+ }
+ return aTokenSeq;
+}
+
+// formula parser/printer base class for filters ==============================
+
+namespace {
+
+bool lclConvertToCellAddress( ScAddress& orAddress, const SingleReference& rSingleRef, sal_Int32 nForbiddenFlags, sal_Int32 nFilterBySheet )
+{
+ orAddress = ScAddress( rSingleRef.Column, rSingleRef.Row, rSingleRef.Sheet );
+ return
+ !getFlag( rSingleRef.Flags, nForbiddenFlags ) &&
+ ((nFilterBySheet < 0) || (nFilterBySheet == rSingleRef.Sheet));
+}
+
+bool lclConvertToCellRange( ScRange& orRange, const ComplexReference& rComplexRef, sal_Int32 nForbiddenFlags, sal_Int32 nFilterBySheet )
+{
+ orRange = ScRange( rComplexRef.Reference1.Column, rComplexRef.Reference1.Row, rComplexRef.Reference1.Sheet,
+ rComplexRef.Reference2.Column, rComplexRef.Reference2.Row, rComplexRef.Reference2.Sheet );
+ return
+ !getFlag( rComplexRef.Reference1.Flags, nForbiddenFlags ) &&
+ !getFlag( rComplexRef.Reference2.Flags, nForbiddenFlags ) &&
+ (rComplexRef.Reference1.Sheet == rComplexRef.Reference2.Sheet) &&
+ ((nFilterBySheet < 0) || (nFilterBySheet == rComplexRef.Reference1.Sheet));
+}
+
+enum TokenToRangeListState { STATE_REF, STATE_SEP, STATE_OPEN, STATE_CLOSE, STATE_ERROR };
+
+TokenToRangeListState lclProcessRef( ScRangeList& orRanges, const Any& rData, sal_Int32 nFilterBySheet )
+{
+ using namespace ::com::sun::star::sheet::ReferenceFlags;
+ const sal_Int32 FORBIDDEN_FLAGS_REL = COLUMN_DELETED | ROW_DELETED | SHEET_DELETED |
+ COLUMN_RELATIVE | ROW_RELATIVE | SHEET_RELATIVE | RELATIVE_NAME;
+
+ sal_Int32 nForbiddenFlags = FORBIDDEN_FLAGS_REL;
+ SingleReference aSingleRef;
+ if( rData >>= aSingleRef )
+ {
+ ScAddress aAddress;
+ // ignore invalid addresses (with #REF! errors), but do not stop parsing
+ if( lclConvertToCellAddress( aAddress, aSingleRef, nForbiddenFlags, nFilterBySheet ) )
+ orRanges.push_back( ScRange(aAddress, aAddress) );
+ return STATE_REF;
+ }
+ ComplexReference aComplexRef;
+ if( rData >>= aComplexRef )
+ {
+ ScRange aRange;
+ // ignore invalid ranges (with #REF! errors), but do not stop parsing
+ if( lclConvertToCellRange( aRange, aComplexRef, nForbiddenFlags, nFilterBySheet ) )
+ orRanges.push_back( aRange );
+ return STATE_REF;
+ }
+ return STATE_ERROR;
+}
+
+TokenToRangeListState lclProcessOpen( sal_Int32& ornParenLevel )
+{
+ ++ornParenLevel;
+ return STATE_OPEN;
+}
+
+TokenToRangeListState lclProcessClose( sal_Int32& ornParenLevel )
+{
+ --ornParenLevel;
+ return (ornParenLevel >= 0) ? STATE_CLOSE : STATE_ERROR;
+}
+
+} // namespace
+
+FormulaProcessorBase::FormulaProcessorBase( const WorkbookHelper& rHelper ) :
+ OpCodeProvider( rHelper.getBaseFilter().getModelFactory(), rHelper.getBaseFilter().isImportFilter() ),
+ ApiOpCodes( getOpCodes() ),
+ WorkbookHelper( rHelper )
+{
+}
+
+OUString FormulaProcessorBase::generateAddress2dString( const ScAddress& rAddress, bool bAbsolute )
+{
+ return generateAddress2dString( BinAddress( rAddress ), bAbsolute );
+}
+
+OUString FormulaProcessorBase::generateAddress2dString( const BinAddress& rAddress, bool bAbsolute )
+{
+ OUStringBuffer aBuffer;
+ // column
+ for( sal_Int32 nTemp = rAddress.mnCol; nTemp >= 0; nTemp = (nTemp / 26) - 1 )
+ aBuffer.insert( 0, sal_Unicode( 'A' + (nTemp % 26) ) );
+ if( bAbsolute )
+ aBuffer.insert( 0, '$' );
+ // row
+ if( bAbsolute )
+ aBuffer.append( '$' );
+ aBuffer.append( static_cast< sal_Int32 >( rAddress.mnRow + 1 ) );
+ return aBuffer.makeStringAndClear();
+}
+
+OUString FormulaProcessorBase::generateApiString( const OUString& rString )
+{
+ OUString aRetString = rString;
+ sal_Int32 nQuotePos = aRetString.getLength();
+ while( (nQuotePos = aRetString.lastIndexOf( '"', nQuotePos )) >= 0 )
+ aRetString = aRetString.replaceAt( nQuotePos, 1, u"\"\"" );
+ return "\"" + aRetString + "\"";
+}
+
+OUString FormulaProcessorBase::generateApiArray( const Matrix< Any >& rMatrix )
+{
+ OSL_ENSURE( !rMatrix.empty(), "FormulaProcessorBase::generateApiArray - missing matrix values" );
+ OUStringBuffer aBuffer;
+ aBuffer.append( API_TOKEN_ARRAY_OPEN );
+ for( size_t nRow = 0, nHeight = rMatrix.height(); nRow < nHeight; ++nRow )
+ {
+ if( nRow > 0 )
+ aBuffer.append( API_TOKEN_ARRAY_ROWSEP );
+ for( Matrix< Any >::const_iterator aBeg = rMatrix.row_begin( nRow ), aIt = aBeg, aEnd = rMatrix.row_end( nRow ); aIt != aEnd; ++aIt )
+ {
+ double fValue = 0.0;
+ OUString aString;
+ if( aIt != aBeg )
+ aBuffer.append( API_TOKEN_ARRAY_COLSEP );
+ if( *aIt >>= fValue )
+ aBuffer.append( fValue );
+ else if( *aIt >>= aString )
+ aBuffer.append( generateApiString( aString ) );
+ else
+ aBuffer.append( "\"\"" );
+ }
+ }
+ aBuffer.append( API_TOKEN_ARRAY_CLOSE );
+ return aBuffer.makeStringAndClear();
+}
+
+Any FormulaProcessorBase::extractReference( const ApiTokenSequence& rTokens ) const
+{
+ ApiTokenIterator aTokenIt( rTokens, OPCODE_SPACES );
+ if( aTokenIt.is() && (aTokenIt->OpCode == OPCODE_PUSH) )
+ {
+ Any aRefAny = aTokenIt->Data;
+ if( !(++aTokenIt).is() && (aRefAny.has< SingleReference >() || aRefAny.has< ComplexReference >()) )
+ return aRefAny;
+ }
+ return Any();
+}
+
+bool FormulaProcessorBase::extractCellRange( ScRange& orRange,
+ const ApiTokenSequence& rTokens ) const
+{
+ ScRangeList aRanges;
+ lclProcessRef( aRanges, extractReference( rTokens ), -1 );
+ if( !aRanges.empty() )
+ {
+ orRange = aRanges.front();
+ return true;
+ }
+ return false;
+}
+
+void FormulaProcessorBase::extractCellRangeList( ScRangeList& orRanges,
+ const ApiTokenSequence& rTokens, sal_Int32 nFilterBySheet ) const
+{
+ orRanges.RemoveAll();
+ TokenToRangeListState eState = STATE_OPEN;
+ sal_Int32 nParenLevel = 0;
+ for( ApiTokenIterator aIt( rTokens, OPCODE_SPACES ); aIt.is() && (eState != STATE_ERROR); ++aIt )
+ {
+ sal_Int32 nOpCode = aIt->OpCode;
+ switch( eState )
+ {
+ // #i107275# accept OPCODE_SEP and OPCODE_LIST as separator token
+ case STATE_REF:
+ if( nOpCode == OPCODE_SEP ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_LIST ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel );
+ else eState = STATE_ERROR;
+ break;
+ case STATE_SEP:
+ if( nOpCode == OPCODE_PUSH ) eState = lclProcessRef( orRanges, aIt->Data, nFilterBySheet );
+ else if( nOpCode == OPCODE_SEP ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_LIST ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_OPEN ) eState = lclProcessOpen( nParenLevel );
+ else if( nOpCode == OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel );
+ else eState = STATE_ERROR;
+ break;
+ case STATE_OPEN:
+ if( nOpCode == OPCODE_PUSH ) eState = lclProcessRef( orRanges, aIt->Data, nFilterBySheet );
+ else if( nOpCode == OPCODE_SEP ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_LIST ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_OPEN ) eState = lclProcessOpen( nParenLevel );
+ else if( nOpCode == OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel );
+ else eState = STATE_ERROR;
+ break;
+ case STATE_CLOSE:
+ if( nOpCode == OPCODE_SEP ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_LIST ) eState = STATE_SEP;
+ else if( nOpCode == OPCODE_CLOSE ) eState = lclProcessClose( nParenLevel );
+ else eState = STATE_ERROR;
+ break;
+ default:;
+ }
+ }
+
+ if( eState == STATE_ERROR )
+ orRanges.RemoveAll();
+ else
+ getAddressConverter().validateCellRangeList( orRanges, false );
+}
+
+bool FormulaProcessorBase::extractString( OUString& orString, const ApiTokenSequence& rTokens ) const
+{
+ ApiTokenIterator aTokenIt( rTokens, OPCODE_SPACES );
+ return aTokenIt.is() && (aTokenIt->OpCode == OPCODE_PUSH) && (aTokenIt->Data >>= orString) && !(++aTokenIt).is();
+}
+
+bool FormulaProcessorBase::extractSpecialTokenInfo( ApiSpecialTokenInfo& orTokenInfo, const ApiTokenSequence& rTokens ) const
+{
+ ApiTokenIterator aTokenIt( rTokens, OPCODE_SPACES );
+ return aTokenIt.is() && (aTokenIt->OpCode == OPCODE_BAD) && (aTokenIt->Data >>= orTokenInfo);
+}
+
+void FormulaProcessorBase::convertStringToStringList(
+ ApiTokenSequence& orTokens, sal_Unicode cStringSep, bool bTrimLeadingSpaces ) const
+{
+ OUString aString;
+ if( !extractString( aString, orTokens ) || aString.isEmpty() )
+ return;
+
+ ::std::vector< ApiToken > aNewTokens;
+ for( sal_Int32 nPos{ 0 }; nPos>=0; )
+ {
+ OUString aEntry = aString.getToken( 0, cStringSep, nPos );
+ if( bTrimLeadingSpaces )
+ {
+ sal_Int32 nStart = 0;
+ while( (nStart < aEntry.getLength()) && (aEntry[ nStart ] == ' ') ) ++nStart;
+ aEntry = aEntry.copy( nStart );
+ }
+ if( !aNewTokens.empty() )
+ aNewTokens.emplace_back( OPCODE_SEP, Any() );
+ aNewTokens.emplace_back( OPCODE_PUSH, Any( aEntry ) );
+ }
+ orTokens = comphelper::containerToSequence( aNewTokens );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/formulabuffer.cxx b/sc/source/filter/oox/formulabuffer.cxx
new file mode 100644
index 000000000..7fcc4f5e2
--- /dev/null
+++ b/sc/source/filter/oox/formulabuffer.cxx
@@ -0,0 +1,476 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <formulabuffer.hxx>
+#include <externallinkbuffer.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+
+#include <autonamecache.hxx>
+#include <tokenarray.hxx>
+#include <sharedformulagroups.hxx>
+#include <externalrefmgr.hxx>
+#include <tokenstringcontext.hxx>
+#include <o3tl/safeint.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/helper/progressbar.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sheet;
+
+#include <memory>
+
+namespace oox::xls {
+
+namespace {
+
+/**
+ * Cache the token array for the last cell position in each column. We use
+ * one cache per sheet.
+ */
+class CachedTokenArray
+{
+public:
+ CachedTokenArray(const CachedTokenArray&) = delete;
+ const CachedTokenArray& operator=(const CachedTokenArray&) = delete;
+
+ struct Item
+ {
+ SCROW mnRow;
+ ScFormulaCell* mpCell;
+
+ Item(const Item&) = delete;
+ const Item& operator=(const Item&) = delete;
+
+ Item() : mnRow(-1), mpCell(nullptr) {}
+ };
+
+ explicit CachedTokenArray( const ScDocument& rDoc ) :
+ maCxt(rDoc, formula::FormulaGrammar::GRAM_OOXML) {}
+
+ Item* get( const ScAddress& rPos, std::u16string_view rFormula )
+ {
+ // Check if a token array is cached for this column.
+ ColCacheType::iterator it = maCache.find(rPos.Col());
+ if (it == maCache.end())
+ return nullptr;
+
+ Item& rCached = *it->second;
+ const ScTokenArray& rCode = *rCached.mpCell->GetCode();
+ OUString aPredicted = rCode.CreateString(maCxt, rPos);
+ if (rFormula == aPredicted)
+ return &rCached;
+
+ return nullptr;
+ }
+
+ void store( const ScAddress& rPos, ScFormulaCell* pCell )
+ {
+ ColCacheType::iterator it = maCache.find(rPos.Col());
+ if (it == maCache.end())
+ {
+ // Create an entry for this column.
+ std::pair<ColCacheType::iterator,bool> r =
+ maCache.emplace(rPos.Col(), std::make_unique<Item>());
+ if (!r.second)
+ // Insertion failed.
+ return;
+
+ it = r.first;
+ }
+
+ Item& rItem = *it->second;
+ rItem.mnRow = rPos.Row();
+ rItem.mpCell = pCell;
+ }
+
+private:
+ typedef std::unordered_map<SCCOL, std::unique_ptr<Item>> ColCacheType;
+ ColCacheType maCache;
+ sc::TokenStringContext maCxt;
+};
+
+void applySharedFormulas(
+ ScDocumentImport& rDoc,
+ SvNumberFormatter& rFormatter,
+ std::vector<FormulaBuffer::SharedFormulaEntry>& rSharedFormulas,
+ std::vector<FormulaBuffer::SharedFormulaDesc>& rCells,
+ bool bGeneratorKnownGood)
+{
+ sc::SharedFormulaGroups aGroups;
+ {
+ // Process shared formulas first.
+ for (const FormulaBuffer::SharedFormulaEntry& rEntry : rSharedFormulas)
+ {
+ const ScAddress& aPos = rEntry.maAddress;
+ sal_Int32 nId = rEntry.mnSharedId;
+ const OUString& rTokenStr = rEntry.maTokenStr;
+
+ ScCompiler aComp(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML, true, false);
+ aComp.SetNumberFormatter(&rFormatter);
+ std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(rTokenStr);
+ if (pArray)
+ {
+ aComp.CompileTokenArray(); // Generate RPN tokens.
+ aGroups.set(nId, std::move(pArray), aPos);
+ }
+ }
+ }
+
+ {
+ svl::SharedStringPool& rStrPool = rDoc.getDoc().GetSharedStringPool();
+ // Process formulas that use shared formulas.
+ for (const FormulaBuffer::SharedFormulaDesc& rDesc : rCells)
+ {
+ const ScAddress& aPos = rDesc.maAddress;
+ const sc::SharedFormulaGroupEntry* pEntry = aGroups.getEntry(rDesc.mnSharedId);
+ if (!pEntry)
+ continue;
+
+ const ScTokenArray* pArray = pEntry->getTokenArray();
+ assert(pArray);
+ const ScAddress& rOrigin = pEntry->getOrigin();
+ assert(rOrigin.IsValid());
+
+ ScFormulaCell* pCell;
+ // In case of shared-formula along a row, do not let
+ // these cells share the same token objects.
+ // If we do, any reference-updates on these cells
+ // (while editing) will mess things up. Pass the cloned array as a
+ // pointer and not as reference to avoid any further allocation.
+ if (rOrigin.Col() != aPos.Col())
+ pCell = new ScFormulaCell(rDoc.getDoc(), aPos, pArray->Clone());
+ else
+ pCell = new ScFormulaCell(rDoc.getDoc(), aPos, *pArray);
+
+ rDoc.setFormulaCell(aPos, pCell);
+ if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
+ pCell->SetNeedNumberFormat(true);
+
+ if (rDesc.maCellValue.isEmpty())
+ {
+ // No cached cell value. Mark it for re-calculation.
+ pCell->SetDirty();
+ continue;
+ }
+
+ // Set cached formula results. For now, we only use numeric and string-formula
+ // results. Find out how to utilize cached results of other types.
+ switch (rDesc.mnValueType)
+ {
+ case XML_n:
+ // numeric value.
+ pCell->SetResultDouble(rDesc.maCellValue.toDouble());
+ /* TODO: is it on purpose that we never reset dirty here
+ * and thus recalculate anyway if cell was dirty? Or is it
+ * never dirty and therefore set dirty below otherwise? This
+ * is different from the non-shared case in
+ * applyCellFormulaValues(). */
+ break;
+ case XML_str:
+ if (bGeneratorKnownGood)
+ {
+ // See applyCellFormulaValues
+ svl::SharedString aSS = rStrPool.intern(rDesc.maCellValue);
+ pCell->SetResultToken(new formula::FormulaStringToken(aSS));
+ // If we don't reset dirty, then e.g. disabling macros makes all cells
+ // that use macro functions to show #VALUE!
+ pCell->ResetDirty();
+ pCell->SetChanged(false);
+ break;
+ }
+ [[fallthrough]];
+ default:
+ // Mark it for re-calculation.
+ pCell->SetDirty();
+ }
+ }
+ }
+}
+
+void applyCellFormulas(
+ ScDocumentImport& rDoc, CachedTokenArray& rCache, SvNumberFormatter& rFormatter,
+ const Sequence<ExternalLinkInfo>& rExternalLinks,
+ const std::vector<FormulaBuffer::TokenAddressItem>& rCells )
+{
+ for (const FormulaBuffer::TokenAddressItem& rItem : rCells)
+ {
+ const ScAddress& aPos = rItem.maAddress;
+ CachedTokenArray::Item* p = rCache.get(aPos, rItem.maTokenStr);
+ if (p)
+ {
+ // Use the cached version to avoid re-compilation.
+
+ ScFormulaCell* pCell = nullptr;
+ if (p->mnRow + 1 == aPos.Row())
+ {
+ // Put them in the same formula group.
+ ScFormulaCell& rPrev = *p->mpCell;
+ ScFormulaCellGroupRef xGroup = rPrev.GetCellGroup();
+ if (!xGroup)
+ {
+ // Last cell is not grouped yet. Start a new group.
+ assert(rPrev.aPos.Row() == p->mnRow);
+ xGroup = rPrev.CreateCellGroup(1, false);
+ }
+ ++xGroup->mnLength;
+
+ pCell = new ScFormulaCell(rDoc.getDoc(), aPos, xGroup);
+ }
+ else
+ pCell = new ScFormulaCell(rDoc.getDoc(), aPos, p->mpCell->GetCode()->Clone());
+
+ rDoc.setFormulaCell(aPos, pCell);
+ if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
+ pCell->SetNeedNumberFormat(true);
+
+ // Update the cache.
+ p->mnRow = aPos.Row();
+ p->mpCell = pCell;
+ continue;
+ }
+
+ ScCompiler aCompiler(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML, true, false);
+ aCompiler.SetNumberFormatter(&rFormatter);
+ aCompiler.SetExternalLinks(rExternalLinks);
+ std::unique_ptr<ScTokenArray> pCode = aCompiler.CompileString(rItem.maTokenStr);
+ if (!pCode)
+ continue;
+
+ aCompiler.CompileTokenArray(); // Generate RPN tokens.
+
+ ScFormulaCell* pCell = new ScFormulaCell(rDoc.getDoc(), aPos, std::move(pCode));
+ rDoc.setFormulaCell(aPos, pCell);
+ if (rDoc.getDoc().GetNumberFormat(aPos.Col(), aPos.Row(), aPos.Tab()) % SV_COUNTRY_LANGUAGE_OFFSET == 0)
+ pCell->SetNeedNumberFormat(true);
+ rCache.store(aPos, pCell);
+ }
+}
+
+void applyArrayFormulas(
+ ScDocumentImport& rDoc, SvNumberFormatter& rFormatter,
+ const Sequence<ExternalLinkInfo>& rExternalLinks,
+ const std::vector<FormulaBuffer::TokenRangeAddressItem>& rArrays )
+{
+ for (const FormulaBuffer::TokenRangeAddressItem& rAddressItem : rArrays)
+ {
+ const ScAddress& aPos = rAddressItem.maTokenAndAddress.maAddress;
+
+ ScCompiler aComp(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_OOXML);
+ aComp.SetNumberFormatter(&rFormatter);
+ aComp.SetExternalLinks(rExternalLinks);
+ std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(rAddressItem.maTokenAndAddress.maTokenStr));
+ if (pArray)
+ rDoc.setMatrixCells(rAddressItem.maRange, *pArray, formula::FormulaGrammar::GRAM_OOXML);
+ }
+}
+
+void applyCellFormulaValues(
+ ScDocumentImport& rDoc, const std::vector<FormulaBuffer::FormulaValue>& rVector, bool bGeneratorKnownGood )
+{
+ svl::SharedStringPool& rStrPool = rDoc.getDoc().GetSharedStringPool();
+
+ for (const FormulaBuffer::FormulaValue& rValue : rVector)
+ {
+ const ScAddress& aCellPos = rValue.maAddress;
+ ScFormulaCell* pCell = rDoc.getDoc().GetFormulaCell(aCellPos);
+ const OUString& rValueStr = rValue.maValueStr;
+ if (!pCell)
+ continue;
+
+ switch (rValue.mnCellType)
+ {
+ case XML_n:
+ {
+ pCell->SetResultDouble(rValueStr.toDouble());
+ pCell->ResetDirty();
+ pCell->SetChanged(false);
+ }
+ break;
+ case XML_str:
+ // Excel uses t="str" for string results (per definition
+ // ECMA-376 18.18.11 ST_CellType (Cell Type) "Cell containing a
+ // formula string.", but that 't' Cell Data Type attribute, "an
+ // enumeration representing the cell's data type", is meant for
+ // the content of the <v> element). We follow that. Other
+ // applications might not and instead use t="str" for the cell
+ // content if formula. Setting an otherwise numeric result as
+ // string result fouls things up, set result strings only for
+ // documents claiming to be generated by a known good
+ // generator. See tdf#98481
+ if (bGeneratorKnownGood)
+ {
+ svl::SharedString aSS = rStrPool.intern(rValueStr);
+ pCell->SetResultToken(new formula::FormulaStringToken(aSS));
+ pCell->ResetDirty();
+ pCell->SetChanged(false);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void processSheetFormulaCells(
+ ScDocumentImport& rDoc, FormulaBuffer::SheetItem& rItem, SvNumberFormatter& rFormatter,
+ const Sequence<ExternalLinkInfo>& rExternalLinks, bool bGeneratorKnownGood )
+{
+ if (rItem.mpSharedFormulaEntries && rItem.mpSharedFormulaIDs)
+ applySharedFormulas(rDoc, rFormatter, *rItem.mpSharedFormulaEntries,
+ *rItem.mpSharedFormulaIDs, bGeneratorKnownGood);
+
+ if (rItem.mpCellFormulas)
+ {
+ CachedTokenArray aCache(rDoc.getDoc());
+ applyCellFormulas(rDoc, aCache, rFormatter, rExternalLinks, *rItem.mpCellFormulas);
+ }
+
+ if (rItem.mpArrayFormulas)
+ applyArrayFormulas(rDoc, rFormatter, rExternalLinks, *rItem.mpArrayFormulas);
+
+ if (rItem.mpCellFormulaValues)
+ applyCellFormulaValues(rDoc, *rItem.mpCellFormulaValues, bGeneratorKnownGood);
+}
+
+}
+
+FormulaBuffer::SharedFormulaEntry::SharedFormulaEntry(
+ const ScAddress& rAddr,
+ const OUString& rTokenStr, sal_Int32 nSharedId ) :
+ maAddress(rAddr), maTokenStr(rTokenStr), mnSharedId(nSharedId) {}
+
+FormulaBuffer::SharedFormulaDesc::SharedFormulaDesc(
+ const ScAddress& rAddr, sal_Int32 nSharedId,
+ const OUString& rCellValue, sal_Int32 nValueType ) :
+ maAddress(rAddr), maCellValue(rCellValue), mnSharedId(nSharedId), mnValueType(nValueType) {}
+
+FormulaBuffer::SheetItem::SheetItem() :
+ mpCellFormulas(nullptr),
+ mpArrayFormulas(nullptr),
+ mpCellFormulaValues(nullptr),
+ mpSharedFormulaEntries(nullptr),
+ mpSharedFormulaIDs(nullptr) {}
+
+FormulaBuffer::FormulaBuffer( const WorkbookHelper& rHelper ) : WorkbookHelper( rHelper )
+{
+}
+
+void FormulaBuffer::SetSheetCount( SCTAB nSheets )
+{
+ maCellFormulas.resize( nSheets );
+ maCellArrayFormulas.resize( nSheets );
+ maSharedFormulas.resize( nSheets );
+ maSharedFormulaIds.resize( nSheets );
+ maCellFormulaValues.resize( nSheets );
+}
+
+void FormulaBuffer::finalizeImport()
+{
+ ISegmentProgressBarRef xFormulaBar = getProgressBar().createSegment( getProgressBar().getFreeLength() );
+
+ ScDocumentImport& rDoc = getDocImport();
+ rDoc.getDoc().SetAutoNameCache(std::make_unique<ScAutoNameCache>(rDoc.getDoc()));
+ ScExternalRefManager::ApiGuard aExtRefGuard(rDoc.getDoc());
+
+ SCTAB nTabCount = rDoc.getDoc().GetTableCount();
+
+ // Fetch all the formulas to process first.
+ std::vector<SheetItem> aSheetItems;
+ aSheetItems.reserve(nTabCount);
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ aSheetItems.push_back(getSheetItem(nTab));
+
+ for (SheetItem& rItem : aSheetItems)
+ processSheetFormulaCells(rDoc, rItem, *rDoc.getDoc().GetFormatTable(), getExternalLinks().getLinkInfos(),
+ isGeneratorKnownGood());
+
+ // With formula results being set and not recalculated we need to
+ // force-trigger adding all linked external files to the LinkManager.
+ rDoc.getDoc().GetExternalRefManager()->addFilesToLinkManager();
+
+ rDoc.getDoc().SetAutoNameCache(nullptr);
+
+ xFormulaBar->setPosition( 1.0 );
+}
+
+FormulaBuffer::SheetItem FormulaBuffer::getSheetItem( SCTAB nTab )
+{
+ std::scoped_lock aGuard(maMtxData);
+
+ SheetItem aItem;
+
+ if( o3tl::make_unsigned(nTab) >= maCellFormulas.size() )
+ {
+ SAL_WARN( "sc", "Tab " << nTab << " out of bounds " << maCellFormulas.size() );
+ return aItem;
+ }
+
+ if( !maCellFormulas[ nTab ].empty() )
+ aItem.mpCellFormulas = &maCellFormulas[ nTab ];
+ if( !maCellArrayFormulas[ nTab ].empty() )
+ aItem.mpArrayFormulas = &maCellArrayFormulas[ nTab ];
+ if( !maCellFormulaValues[ nTab ].empty() )
+ aItem.mpCellFormulaValues = &maCellFormulaValues[ nTab ];
+ if( !maSharedFormulas[ nTab ].empty() )
+ aItem.mpSharedFormulaEntries = &maSharedFormulas[ nTab ];
+ if( !maSharedFormulaIds[ nTab ].empty() )
+ aItem.mpSharedFormulaIDs = &maSharedFormulaIds[ nTab ];
+
+ return aItem;
+}
+
+void FormulaBuffer::createSharedFormulaMapEntry(
+ const ScAddress& rAddress,
+ sal_Int32 nSharedId, const OUString& rTokens )
+{
+ assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maSharedFormulas.size() );
+ std::vector<SharedFormulaEntry>& rSharedFormulas = maSharedFormulas[ rAddress.Tab() ];
+ SharedFormulaEntry aEntry(rAddress, rTokens, nSharedId);
+ rSharedFormulas.push_back( aEntry );
+}
+
+void FormulaBuffer::setCellFormula( const ScAddress& rAddress, const OUString& rTokenStr )
+{
+ assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maCellFormulas.size() );
+ maCellFormulas[ rAddress.Tab() ].emplace_back( rTokenStr, rAddress );
+}
+
+void FormulaBuffer::setCellFormula(
+ const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rCellValue, sal_Int32 nValueType )
+{
+ assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maSharedFormulaIds.size() );
+ maSharedFormulaIds[rAddress.Tab()].emplace_back(rAddress, nSharedId, rCellValue, nValueType);
+}
+
+void FormulaBuffer::setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr )
+{
+
+ TokenAddressItem tokenPair( rTokenStr, rTokenAddress );
+ assert( rRangeAddress.aStart.Tab() >= 0 && o3tl::make_unsigned(rRangeAddress.aStart.Tab()) < maCellArrayFormulas.size() );
+ maCellArrayFormulas[ rRangeAddress.aStart.Tab() ].emplace_back( tokenPair, rRangeAddress );
+}
+
+void FormulaBuffer::setCellFormulaValue(
+ const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType )
+{
+ assert( rAddress.Tab() >= 0 && o3tl::make_unsigned(rAddress.Tab()) < maCellFormulaValues.size() );
+ FormulaValue aVal;
+ aVal.maAddress = rAddress;
+ aVal.maValueStr = rValueStr;
+ aVal.mnCellType = nCellType;
+ maCellFormulaValues[rAddress.Tab()].push_back(aVal);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/formulaparser.cxx b/sc/source/filter/oox/formulaparser.cxx
new file mode 100644
index 000000000..c4582cef0
--- /dev/null
+++ b/sc/source/filter/oox/formulaparser.cxx
@@ -0,0 +1,1855 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <formulaparser.hxx>
+
+#include <com/sun/star/sheet/ComplexReference.hpp>
+#include <com/sun/star/sheet/ExternalReference.hpp>
+#include <com/sun/star/sheet/ReferenceFlags.hpp>
+#include <com/sun/star/sheet/SingleReference.hpp>
+#include <com/sun/star/table/CellAddress.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <defnamesbuffer.hxx>
+#include <externallinkbuffer.hxx>
+#include <tablebuffer.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::sheet::ReferenceFlags;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+// formula finalizer ==========================================================
+
+FormulaFinalizer::FormulaFinalizer( const OpCodeProvider& rOpCodeProv ) :
+ OpCodeProvider( rOpCodeProv ),
+ ApiOpCodes( getOpCodes() )
+{
+ maTokens.reserve( 0x2000 );
+}
+
+ApiTokenSequence FormulaFinalizer::finalizeTokenArray( const ApiTokenSequence& rTokens )
+{
+ maTokens.clear();
+ if( rTokens.hasElements() )
+ {
+ const ApiToken* pToken = rTokens.getConstArray();
+ processTokens( pToken, pToken + rTokens.getLength() );
+ }
+ return maTokens.toSequence();
+}
+
+const FunctionInfo* FormulaFinalizer::resolveBadFuncName( const OUString& ) const
+{
+ return nullptr;
+}
+
+OUString FormulaFinalizer::resolveDefinedName( sal_Int32 ) const
+{
+ return OUString();
+}
+
+const FunctionInfo* FormulaFinalizer::getFunctionInfo( ApiToken& orFuncToken )
+{
+ // first, try to find a regular function info from token op-code
+ if( const FunctionInfo* pRegFuncInfo = getFuncInfoFromApiToken( orFuncToken ) )
+ return pRegFuncInfo;
+
+ // try to recognize a function from an external library
+ if( (orFuncToken.OpCode == OPCODE_BAD) && orFuncToken.Data.has< OUString >() )
+ {
+ // virtual call to resolveBadFuncName()
+ if( const FunctionInfo* pLibFuncInfo = resolveBadFuncName( orFuncToken.Data.get< OUString >() ) )
+ {
+ // write function op-code to the OPCODE_BAD token
+ orFuncToken.OpCode = pLibFuncInfo->mnApiOpCode;
+ // if it is an external function, insert programmatic function name
+ if( (orFuncToken.OpCode == OPCODE_EXTERNAL) && (!pLibFuncInfo->maExtProgName.isEmpty()) )
+ orFuncToken.Data <<= pLibFuncInfo->maExtProgName;
+ else
+ orFuncToken.Data.clear(); // clear string from OPCODE_BAD
+ return pLibFuncInfo;
+ }
+ }
+
+ // no success - return null
+ return nullptr;
+}
+
+const FunctionInfo* FormulaFinalizer::getExternCallInfo( ApiToken& orFuncToken, const ApiToken& rECToken )
+{
+ // try to resolve the passed token to a supported sheet function
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromApiToken( rECToken ) )
+ {
+ orFuncToken.OpCode = pFuncInfo->mnApiOpCode;
+ // programmatic add-in function name
+ if( (pFuncInfo->mnApiOpCode == OPCODE_EXTERNAL) && !pFuncInfo->maExtProgName.isEmpty() )
+ orFuncToken.Data <<= pFuncInfo->maExtProgName;
+ // name of unsupported function, convert to OPCODE_BAD to preserve the name
+ else if( (pFuncInfo->mnApiOpCode == OPCODE_BAD) && !pFuncInfo->maOoxFuncName.isEmpty() )
+ orFuncToken.Data <<= pFuncInfo->maOoxFuncName;
+ return pFuncInfo;
+ }
+
+ // macro call or unknown function name, move data to function token
+ if( (rECToken.OpCode == OPCODE_MACRO) || (rECToken.OpCode == OPCODE_BAD) )
+ orFuncToken = rECToken;
+
+ // defined name used as function call, convert to OPCODE_BAD to preserve the name
+ if( (rECToken.OpCode == OPCODE_NAME) && rECToken.Data.has< sal_Int32 >() )
+ {
+ OUString aDefName = resolveDefinedName( rECToken.Data.get< sal_Int32 >() );
+ if( !aDefName.isEmpty() )
+ {
+ orFuncToken.OpCode = OPCODE_BAD;
+ orFuncToken.Data <<= aDefName;
+ }
+ }
+
+ return nullptr;
+}
+
+void FormulaFinalizer::processTokens( const ApiToken* pToken, const ApiToken* pTokenEnd )
+{
+ while( pToken < pTokenEnd )
+ {
+ // push the current token into the vector
+ bool bValid = appendFinalToken( *pToken );
+ // try to process a function
+ if( const FunctionInfo* pFuncInfo = bValid ? getFunctionInfo( maTokens.back() ) : nullptr )
+ pToken = processParameters( *pFuncInfo, pToken + 1, pTokenEnd );
+ // otherwise, go to next token
+ else
+ ++pToken;
+ }
+}
+
+const ApiToken* FormulaFinalizer::processParameters(
+ const FunctionInfo& rFuncInfo, const ApiToken* pToken, const ApiToken* pTokenEnd )
+{
+ // remember position of the token containing the function op-code
+ size_t nFuncNameIdx = maTokens.size() - 1;
+
+ // process a function, if an OPCODE_OPEN token is following
+ OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "FormulaFinalizer::processParameters - OPCODE_OPEN expected" );
+ if( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN) )
+ {
+ // append the OPCODE_OPEN token to the vector
+ maTokens.append( OPCODE_OPEN );
+
+ // store positions of OPCODE_OPEN, parameter separators, and OPCODE_CLOSE
+ ParameterPosVector aParams;
+ pToken = findParameters( aParams, pToken, pTokenEnd );
+ OSL_ENSURE( aParams.size() >= 2, "FormulaFinalizer::processParameters - missing tokens" );
+ size_t nParamCount = aParams.size() - 1;
+
+ if( (nParamCount == 1) && isEmptyParameter( aParams[ 0 ] + 1, aParams[ 1 ] ) )
+ {
+ /* Empty pair of parentheses -> function call without parameters,
+ process parameter, there might be spaces between parentheses. */
+ processTokens( aParams[ 0 ] + 1, aParams[ 1 ] );
+ }
+ else
+ {
+ const FunctionInfo* pRealFuncInfo = &rFuncInfo;
+ ParameterPosVector::const_iterator aPosIt = aParams.begin();
+
+ /* Preprocess EXTERN.CALL functions. The actual function name is
+ contained as reference to a defined name in the first (hidden)
+ parameter. */
+ if( rFuncInfo.mnBiffFuncId == BIFF_FUNC_EXTERNCALL )
+ {
+ ApiToken& rFuncToken = maTokens[ nFuncNameIdx ];
+ rFuncToken.OpCode = OPCODE_NONAME;
+
+ // try to initialize function token from first parameter
+ if( const ApiToken* pECToken = getSingleToken( *aPosIt + 1, *(aPosIt + 1) ) )
+ if( const FunctionInfo* pECFuncInfo = getExternCallInfo( rFuncToken, *pECToken ) )
+ pRealFuncInfo = pECFuncInfo;
+
+ /* On success (something has been inserted into rFuncToken),
+ skip the first parameter. */
+ if( rFuncToken.OpCode != OPCODE_NONAME )
+ {
+ --nParamCount;
+ ++aPosIt;
+ }
+ }
+
+ // process all parameters
+ FunctionParamInfoIterator aParamInfoIt( *pRealFuncInfo );
+ size_t nLastValidSize = maTokens.size();
+ size_t nLastValidCount = 0;
+ for( size_t nParam = 0; nParam < nParamCount; ++nParam, ++aPosIt, ++aParamInfoIt )
+ {
+ // add embedded Calc-only parameters
+ if( aParamInfoIt.isCalcOnlyParam() )
+ {
+ appendCalcOnlyParameter( *pRealFuncInfo, nParam, nParamCount );
+ while( aParamInfoIt.isCalcOnlyParam() ) ++aParamInfoIt;
+ }
+
+ const ApiToken* pParamBegin = *aPosIt + 1;
+ const ApiToken* pParamEnd = *(aPosIt + 1);
+ bool bIsEmpty = isEmptyParameter( pParamBegin, pParamEnd );
+
+ if( !aParamInfoIt.isExcelOnlyParam() )
+ {
+ // handle empty parameters
+ if( bIsEmpty )
+ {
+ // append leading space tokens from original token array
+ while( (pParamBegin < pParamEnd) && (pParamBegin->OpCode == OPCODE_SPACES) )
+ maTokens.push_back( *pParamBegin++ );
+ // add default values for some empty parameters, or the OPCODE_MISSING token
+ appendEmptyParameter( *pRealFuncInfo, nParam );
+ // reset bIsEmpty flag, if something has been appended in appendEmptyParameter()
+ bIsEmpty = maTokens.back().OpCode == OPCODE_MISSING;
+ // skip OPCODE_MISSING token in the original token array
+ OSL_ENSURE( (pParamBegin == pParamEnd) || (pParamBegin->OpCode == OPCODE_MISSING), "FormulaFinalizer::processParameters - OPCODE_MISSING expected" );
+ if( pParamBegin < pParamEnd ) ++pParamBegin;
+ // append trailing space tokens from original token array
+ while( (pParamBegin < pParamEnd) && (pParamBegin->OpCode == OPCODE_SPACES) )
+ maTokens.push_back( *pParamBegin++ );
+ }
+ else
+ {
+ // if parameter is not empty, process all tokens of the parameter
+ processTokens( pParamBegin, pParamEnd );
+ }
+
+ // append parameter separator token
+ maTokens.append( OPCODE_SEP );
+ }
+
+ /* #84453# Update size of new token sequence with valid parameters
+ to be able to remove trailing optional empty parameters. */
+ if( !bIsEmpty || (nParam < pRealFuncInfo->mnMinParamCount) )
+ {
+ nLastValidSize = maTokens.size();
+ nLastValidCount = nParam + 1;
+ }
+ }
+
+ // #84453# remove trailing optional empty parameters
+ maTokens.resize( nLastValidSize );
+
+ // add trailing Calc-only parameters
+ if( aParamInfoIt.isCalcOnlyParam() )
+ appendCalcOnlyParameter( *pRealFuncInfo, nLastValidCount, nParamCount );
+
+ // add optional parameters that are required in Calc
+ appendRequiredParameters( *pRealFuncInfo, nLastValidCount );
+
+ // remove last parameter separator token
+ if( maTokens.back().OpCode == OPCODE_SEP )
+ maTokens.pop_back();
+ }
+
+ /* Append the OPCODE_CLOSE token to the vector, but only if there is
+ no OPCODE_BAD token at the end, this token already contains the
+ trailing closing parentheses. */
+ if( (pTokenEnd - 1)->OpCode != OPCODE_BAD )
+ maTokens.append( OPCODE_CLOSE );
+ }
+
+ /* Replace OPCODE_EXTERNAL with OPCODE_NONAME to get #NAME! error in cell,
+ if no matching add-in function was found. */
+ ApiToken& rFuncNameToken = maTokens[ nFuncNameIdx ];
+ if( (rFuncNameToken.OpCode == OPCODE_EXTERNAL) && !rFuncNameToken.Data.hasValue() )
+ rFuncNameToken.OpCode = OPCODE_NONAME;
+
+ return pToken;
+}
+
+bool FormulaFinalizer::isEmptyParameter( const ApiToken* pToken, const ApiToken* pTokenEnd ) const
+{
+ while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken;
+ if( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_MISSING) ) ++pToken;
+ while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken;
+ return pToken == pTokenEnd;
+}
+
+const ApiToken* FormulaFinalizer::getSingleToken( const ApiToken* pToken, const ApiToken* pTokenEnd ) const
+{
+ const ApiToken* pSingleToken = nullptr;
+ // skip leading whitespace tokens
+ while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken;
+ // remember first non-whitespace token
+ if( pToken < pTokenEnd ) pSingleToken = pToken++;
+ // skip trailing whitespace tokens
+ while( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_SPACES) ) ++pToken;
+ // return null, if other non-whitespace tokens follow
+ return (pToken == pTokenEnd) ? pSingleToken : nullptr;
+}
+
+const ApiToken* FormulaFinalizer::skipParentheses( const ApiToken* pToken, const ApiToken* pTokenEnd ) const
+{
+ // skip tokens between OPCODE_OPEN and OPCODE_CLOSE
+ OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "skipParentheses - OPCODE_OPEN expected" );
+ ++pToken;
+ while( (pToken < pTokenEnd) && (pToken->OpCode != OPCODE_CLOSE) )
+ {
+ if( pToken->OpCode == OPCODE_OPEN )
+ pToken = skipParentheses( pToken, pTokenEnd );
+ else
+ ++pToken;
+ }
+ // skip the OPCODE_CLOSE token
+ OSL_ENSURE( ((pToken < pTokenEnd) && (pToken->OpCode == OPCODE_CLOSE)) || ((pTokenEnd - 1)->OpCode == OPCODE_BAD), "skipParentheses - OPCODE_CLOSE expected" );
+ return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd;
+}
+
+const ApiToken* FormulaFinalizer::findParameters( ParameterPosVector& rParams,
+ const ApiToken* pToken, const ApiToken* pTokenEnd ) const
+{
+ // push position of OPCODE_OPEN
+ OSL_ENSURE( (pToken < pTokenEnd) && (pToken->OpCode == OPCODE_OPEN), "FormulaFinalizer::findParameters - OPCODE_OPEN expected" );
+ rParams.push_back( pToken++ );
+
+ // find positions of parameter separators
+ while( (pToken < pTokenEnd) && (pToken->OpCode != OPCODE_CLOSE) )
+ {
+ if( pToken->OpCode == OPCODE_OPEN )
+ pToken = skipParentheses( pToken, pTokenEnd );
+ else if( pToken->OpCode == OPCODE_SEP )
+ rParams.push_back( pToken++ );
+ else
+ ++pToken;
+ }
+
+ // push position of OPCODE_CLOSE
+ OSL_ENSURE( ((pToken < pTokenEnd) && (pToken->OpCode == OPCODE_CLOSE)) || ((pTokenEnd - 1)->OpCode == OPCODE_BAD), "FormulaFinalizer::findParameters - OPCODE_CLOSE expected" );
+ rParams.push_back( pToken );
+ return (pToken < pTokenEnd) ? (pToken + 1) : pTokenEnd;
+}
+
+void FormulaFinalizer::appendEmptyParameter( const FunctionInfo& rFuncInfo, size_t nParam )
+{
+ // remember old size of the token array
+ size_t nTokenArraySize = maTokens.size();
+
+ switch( rFuncInfo.mnBiff12FuncId )
+ {
+ case BIFF_FUNC_IF:
+ if( (nParam == 1) || (nParam == 2) )
+ maTokens.append< double >( OPCODE_PUSH, 0.0 );
+ break;
+ default:;
+ }
+
+ // if no token has been added, append an OPCODE_MISSING token
+ if( nTokenArraySize == maTokens.size() )
+ maTokens.append( OPCODE_MISSING );
+}
+
+void FormulaFinalizer::appendCalcOnlyParameter( const FunctionInfo& rFuncInfo, size_t nParam, size_t nParamCount )
+{
+ switch( rFuncInfo.mnBiff12FuncId )
+ {
+ case BIFF_FUNC_FLOOR:
+ case BIFF_FUNC_CEILING:
+ if (nParam == 2 && nParamCount < 3)
+ {
+ maTokens.append< double >( OPCODE_PUSH, 1.0 );
+ maTokens.append( OPCODE_SEP );
+ }
+ break;
+ }
+}
+
+void FormulaFinalizer::appendRequiredParameters( const FunctionInfo& rFuncInfo, size_t nParamCount )
+{
+ switch( rFuncInfo.mnBiff12FuncId )
+ {
+ case BIFF_FUNC_WEEKNUM:
+ if( nParamCount == 1 )
+ {
+ maTokens.append< double >( OPCODE_PUSH, 1.0 );
+ maTokens.append( OPCODE_SEP );
+ }
+ break;
+ }
+}
+
+bool FormulaFinalizer::appendFinalToken( const ApiToken& rToken )
+{
+ // replace OPCODE_MACRO without macro name with #NAME? error code
+ bool bValid = (rToken.OpCode != OPCODE_MACRO) || rToken.Data.hasValue();
+ if( bValid )
+ {
+ maTokens.push_back( rToken );
+ }
+ else
+ {
+ maTokens.append( OPCODE_ARRAY_OPEN );
+ maTokens.append( OPCODE_PUSH, BiffHelper::calcDoubleFromError( BIFF_ERR_NAME ) );
+ maTokens.append( OPCODE_ARRAY_CLOSE );
+ }
+ return bValid;
+}
+
+// parser implementation base =================================================
+
+class FormulaParserImpl : public FormulaFinalizer, public WorkbookHelper
+{
+public:
+ explicit FormulaParserImpl( const FormulaParser& rParent );
+
+ /** Converts an OOXML formula string. */
+ virtual ApiTokenSequence importOoxFormula(
+ const ScAddress& rBaseAddress,
+ const OUString& rFormulaString );
+
+ /** Imports and converts a BIFF12 token array from the passed stream. */
+ virtual ApiTokenSequence importBiff12Formula(
+ const ScAddress& rBaseAddress,
+ FormulaType eType,
+ SequenceInputStream& rStrm );
+
+ /** Tries to resolve the passed ref-id to an OLE target URL. */
+ OUString resolveOleTarget( sal_Int32 nRefId, bool bUseRefSheets ) const;
+
+protected:
+ typedef ::std::pair< sal_Int32, bool > WhiteSpace;
+ typedef ::std::vector< WhiteSpace > WhiteSpaceVec;
+
+ /** Initializes the formula parser before importing a formula. */
+ void initializeImport( const ScAddress& rBaseAddress, FormulaType eType );
+ /** Finalizes the internal token storage after import. */
+ ApiTokenSequence finalizeImport();
+
+ // token array ------------------------------------------------------------
+
+ bool resetSpaces();
+ static void appendSpaces( WhiteSpaceVec& orSpaces, sal_Int32 nCount, bool bLineFeed );
+ void appendLeadingSpaces( sal_Int32 nCount, bool bLineFeed );
+ void appendOpeningSpaces( sal_Int32 nCount, bool bLineFeed );
+ void appendClosingSpaces( sal_Int32 nCount, bool bLineFeed );
+
+ size_t getFormulaSize() const;
+ Any& appendRawToken( sal_Int32 nOpCode );
+ Any& insertRawToken( sal_Int32 nOpCode, size_t nIndexFromEnd );
+ size_t appendWhiteSpaceTokens( const WhiteSpaceVec* pSpaces );
+ size_t insertWhiteSpaceTokens( const WhiteSpaceVec* pSpaces, size_t nIndexFromEnd );
+
+ size_t getOperandSize( size_t nOpIndex ) const;
+ void pushOperandSize( size_t nSize );
+ size_t popOperandSize();
+
+ ApiToken& getOperandToken( size_t nOpIndex, size_t nTokenIndex );
+
+ bool pushOperandToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ template< typename Type >
+ bool pushValueOperandToken( const Type& rValue, sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ template< typename Type >
+ bool pushValueOperandToken( const Type& rValue )
+ { return pushValueOperandToken( rValue, OPCODE_PUSH, nullptr ); }
+ bool pushParenthesesOperandToken( const WhiteSpaceVec* pClosingSpaces = nullptr );
+ bool pushUnaryPreOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ bool pushUnaryPostOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ bool pushBinaryOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces = nullptr );
+ bool pushParenthesesOperatorToken( const WhiteSpaceVec* pOpeningSpaces = nullptr, const WhiteSpaceVec* pClosingSpaces = nullptr );
+ bool pushFunctionOperatorToken( sal_Int32 nOpCode, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces = nullptr, const WhiteSpaceVec* pClosingSpaces = nullptr );
+ bool pushFunctionOperatorToken( const FunctionInfo& rFuncInfo, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces = nullptr, const WhiteSpaceVec* pClosingSpaces = nullptr );
+
+ bool pushOperand( sal_Int32 nOpCode );
+ template< typename Type >
+ bool pushValueOperand( const Type& rValue, sal_Int32 nOpCode );
+ template< typename Type >
+ bool pushValueOperand( const Type& rValue )
+ { return pushValueOperand( rValue, OPCODE_PUSH ); }
+ bool pushBoolOperand( bool bValue );
+ bool pushErrorOperand( double fEncodedError );
+ bool pushBiffBoolOperand( sal_uInt8 nValue );
+ bool pushBiffErrorOperand( sal_uInt8 nErrorCode );
+ bool pushReferenceOperand( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset );
+ bool pushReferenceOperand( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset );
+ template< typename Type >
+ bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const Type& rApiRef );
+ bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset );
+ bool pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset );
+ bool pushEmbeddedRefOperand( const DefinedNameBase& rName, bool bPushBadToken );
+ bool pushDefinedNameOperand( const DefinedNameRef& rxDefName );
+ bool pushExternalFuncOperand( const FunctionInfo& rFuncInfo );
+ bool pushDdeLinkOperand( const OUString& rDdeServer, const OUString& rDdeTopic, const OUString& rDdeItem );
+ bool pushExternalNameOperand( const ExternalNameRef& rxExtName, const ExternalLink& rExtLink );
+ bool pushSpecialTokenOperand( const BinAddress& rBaseAddr );
+
+ bool pushUnaryPreOperator( sal_Int32 nOpCode );
+ bool pushUnaryPostOperator( sal_Int32 nOpCode );
+ bool pushBinaryOperator( sal_Int32 nOpCode );
+ bool pushParenthesesOperator();
+ bool pushFunctionOperator( sal_Int32 nOpCode, size_t nParamCount );
+ bool pushFunctionOperator( const FunctionInfo& rFuncInfo, size_t nParamCount );
+
+private:
+ // reference conversion ---------------------------------------------------
+
+ void initReference2d( SingleReference& orApiRef ) const;
+ static void initReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet );
+ void convertReference( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference2d( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference2d( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const;
+ void convertReference3d( ComplexReference& orApiRef, const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const;
+
+private:
+ // finalize token sequence ------------------------------------------------
+
+ virtual const FunctionInfo* resolveBadFuncName( const OUString& rTokenData ) const override;
+ virtual OUString resolveDefinedName( sal_Int32 nTokenIndex ) const override;
+
+protected:
+ const sal_Int32 mnMaxApiCol; /// Maximum column index in own document.
+ const sal_Int32 mnMaxApiRow; /// Maximum row index in own document.
+ const sal_Int32 mnMaxXlsCol; /// Maximum column index in imported document.
+ const sal_Int32 mnMaxXlsRow; /// Maximum row index in imported document.
+
+ ScAddress maBaseAddr; /// Base address for relative references.
+ bool mbRelativeAsOffset; /// True = relative row/column index is (signed) offset, false = explicit index.
+ bool mb2dRefsAs3dRefs; /// True = convert all 2D references to 3D references in sheet specified by base address.
+ bool mbSpecialTokens; /// True = special handling for tExp and tTbl tokens, false = exit with error.
+
+private:
+ ApiTokenVector maTokenStorage; /// Raw unordered token storage.
+ std::vector<size_t> maTokenIndexes; /// Indexes into maTokenStorage.
+ std::vector<size_t> maOperandSizeStack; /// Stack with token sizes per operand.
+ WhiteSpaceVec maLeadingSpaces; /// List of whitespaces before next token.
+ WhiteSpaceVec maOpeningSpaces; /// List of whitespaces before opening parenthesis.
+ WhiteSpaceVec maClosingSpaces; /// List of whitespaces before closing parenthesis.
+};
+
+FormulaParserImpl::FormulaParserImpl( const FormulaParser& rParent ) :
+ FormulaFinalizer( rParent ),
+ WorkbookHelper( rParent ),
+ mnMaxApiCol( rParent.getAddressConverter().getMaxApiAddress().Col() ),
+ mnMaxApiRow( rParent.getAddressConverter().getMaxApiAddress().Row() ),
+ mnMaxXlsCol( rParent.getAddressConverter().getMaxXlsAddress().Col() ),
+ mnMaxXlsRow( rParent.getAddressConverter().getMaxXlsAddress().Row() ),
+ mbRelativeAsOffset( false ),
+ mb2dRefsAs3dRefs( false ),
+ mbSpecialTokens( false )
+{
+ // reserve enough space to make resize(), push_back() etc. cheap
+ maTokenStorage.reserve( 0x2000 );
+ maTokenIndexes.reserve( 0x2000 );
+ maOperandSizeStack.reserve( 256 );
+ maLeadingSpaces.reserve( 256 );
+ maOpeningSpaces.reserve( 256 );
+ maClosingSpaces.reserve( 256 );
+}
+
+ApiTokenSequence FormulaParserImpl::importOoxFormula( const ScAddress&, const OUString& )
+{
+ OSL_FAIL( "FormulaParserImpl::importOoxFormula - not implemented" );
+ return ApiTokenSequence();
+}
+
+ApiTokenSequence FormulaParserImpl::importBiff12Formula( const ScAddress&, FormulaType, SequenceInputStream& )
+{
+ SAL_WARN("sc.filter", "FormulaParserImpl::importBiff12Formula - not implemented" );
+ return ApiTokenSequence();
+}
+
+OUString FormulaParserImpl::resolveOleTarget( sal_Int32 nRefId, bool bUseRefSheets ) const
+{
+ const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId, bUseRefSheets ).get();
+ OSL_ENSURE( pExtLink && (pExtLink->getLinkType() == ExternalLinkType::OLE), "FormulaParserImpl::resolveOleTarget - missing or wrong link" );
+ if( pExtLink && (pExtLink->getLinkType() == ExternalLinkType::OLE) )
+ return getBaseFilter().getAbsoluteUrl( pExtLink->getTargetUrl() );
+ return OUString();
+}
+
+void FormulaParserImpl::initializeImport( const ScAddress& rBaseAddr, FormulaType eType )
+{
+ maBaseAddr = rBaseAddr;
+ mbRelativeAsOffset = mb2dRefsAs3dRefs = mbSpecialTokens = false;
+ switch( eType )
+ {
+ case FormulaType::Cell:
+ mbSpecialTokens = true;
+ break;
+ case FormulaType::Array:
+ break;
+ case FormulaType::SharedFormula:
+ mbRelativeAsOffset = true;
+ break;
+ case FormulaType::CondFormat:
+ mbRelativeAsOffset = true;
+ break;
+ case FormulaType::Validation:
+ mbRelativeAsOffset = true;
+ break;
+ }
+
+ maTokenStorage.clear();
+ maTokenIndexes.clear();
+ maOperandSizeStack.clear();
+}
+
+ApiTokenSequence FormulaParserImpl::finalizeImport()
+{
+ ApiTokenSequence aTokens( static_cast< sal_Int32 >( maTokenIndexes.size() ) );
+ if( aTokens.hasElements() )
+ {
+ ApiToken* pToken = aTokens.getArray();
+ for( const auto& tokenIndex : maTokenIndexes )
+ {
+ *pToken = maTokenStorage[ tokenIndex ];
+ ++pToken;
+ }
+ }
+ return finalizeTokenArray( aTokens );
+}
+
+// token array ----------------------------------------------------------------
+
+bool FormulaParserImpl::resetSpaces()
+{
+ maLeadingSpaces.clear();
+ maOpeningSpaces.clear();
+ maClosingSpaces.clear();
+ return true;
+}
+
+void FormulaParserImpl::appendSpaces( WhiteSpaceVec& orSpaces, sal_Int32 nCount, bool bLineFeed )
+{
+ OSL_ENSURE( nCount >= 0, "FormulaParserImpl::appendSpaces - negative count" );
+ if( nCount > 0 )
+ orSpaces.emplace_back( nCount, bLineFeed );
+}
+
+void FormulaParserImpl::appendLeadingSpaces( sal_Int32 nCount, bool bLineFeed )
+{
+ appendSpaces( maLeadingSpaces, nCount, bLineFeed );
+}
+
+void FormulaParserImpl::appendOpeningSpaces( sal_Int32 nCount, bool bLineFeed )
+{
+ appendSpaces( maOpeningSpaces, nCount, bLineFeed );
+}
+
+void FormulaParserImpl::appendClosingSpaces( sal_Int32 nCount, bool bLineFeed )
+{
+ appendSpaces( maClosingSpaces, nCount, bLineFeed );
+}
+
+size_t FormulaParserImpl::getFormulaSize() const
+{
+ return maTokenIndexes.size();
+}
+
+Any& FormulaParserImpl::appendRawToken( sal_Int32 nOpCode )
+{
+ maTokenIndexes.push_back( maTokenStorage.size() );
+ return maTokenStorage.append( nOpCode );
+}
+
+Any& FormulaParserImpl::insertRawToken( sal_Int32 nOpCode, size_t nIndexFromEnd )
+{
+ maTokenIndexes.insert( maTokenIndexes.end() - nIndexFromEnd, maTokenStorage.size() );
+ return maTokenStorage.append( nOpCode );
+}
+
+size_t FormulaParserImpl::appendWhiteSpaceTokens( const WhiteSpaceVec* pSpaces )
+{
+ if( pSpaces )
+ for( const auto& rSpace : *pSpaces )
+ appendRawToken( OPCODE_SPACES ) <<= rSpace.first;
+ return pSpaces ? pSpaces->size() : 0;
+}
+
+size_t FormulaParserImpl::insertWhiteSpaceTokens( const WhiteSpaceVec* pSpaces, size_t nIndexFromEnd )
+{
+ if( pSpaces )
+ for( const auto& rSpace : *pSpaces )
+ insertRawToken( OPCODE_SPACES, nIndexFromEnd ) <<= rSpace.first;
+ return pSpaces ? pSpaces->size() : 0;
+}
+
+size_t FormulaParserImpl::getOperandSize( size_t nOpIndex ) const
+{
+ OSL_ENSURE( (nOpIndex < 1) && (!maOperandSizeStack.empty()),
+ "FormulaParserImpl::getOperandSize - invalid parameters" );
+ return maOperandSizeStack[ maOperandSizeStack.size() - 1 + nOpIndex ];
+}
+
+void FormulaParserImpl::pushOperandSize( size_t nSize )
+{
+ maOperandSizeStack.push_back( nSize );
+}
+
+size_t FormulaParserImpl::popOperandSize()
+{
+ OSL_ENSURE( !maOperandSizeStack.empty(), "FormulaParserImpl::popOperandSize - invalid call" );
+ size_t nOpSize = maOperandSizeStack.back();
+ maOperandSizeStack.pop_back();
+ return nOpSize;
+}
+
+ApiToken& FormulaParserImpl::getOperandToken( size_t nOpIndex, size_t nTokenIndex )
+{
+ SAL_WARN_IF( getOperandSize( nOpIndex ) <= nTokenIndex, "sc.filter",
+ "FormulaParserImpl::getOperandToken - invalid parameters" );
+ auto aIndexIt = maTokenIndexes.cend();
+ for( auto aEnd = maOperandSizeStack.cend(), aIt = aEnd - 1 + nOpIndex; aIt != aEnd; ++aIt )
+ aIndexIt -= *aIt;
+ return maTokenStorage[ *(aIndexIt + nTokenIndex) ];
+}
+
+bool FormulaParserImpl::pushOperandToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces );
+ appendRawToken( nOpCode );
+ pushOperandSize( nSpacesSize + 1 );
+ return true;
+}
+
+template< typename Type >
+bool FormulaParserImpl::pushValueOperandToken( const Type& rValue, sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces );
+ appendRawToken( nOpCode ) <<= rValue;
+ pushOperandSize( nSpacesSize + 1 );
+ return true;
+}
+
+bool FormulaParserImpl::pushParenthesesOperandToken( const WhiteSpaceVec* pClosingSpaces )
+{
+ size_t nSpacesSize = appendWhiteSpaceTokens( nullptr );
+ appendRawToken( OPCODE_OPEN );
+ nSpacesSize += appendWhiteSpaceTokens( pClosingSpaces );
+ appendRawToken( OPCODE_CLOSE );
+ pushOperandSize( nSpacesSize + 2 );
+ return true;
+}
+
+bool FormulaParserImpl::pushUnaryPreOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ bool bOk = !maOperandSizeStack.empty();
+ if( bOk )
+ {
+ size_t nOpSize = popOperandSize();
+ size_t nSpacesSize = insertWhiteSpaceTokens( pSpaces, nOpSize );
+ insertRawToken( nOpCode, nOpSize );
+ pushOperandSize( nOpSize + nSpacesSize + 1 );
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushUnaryPostOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ bool bOk = !maOperandSizeStack.empty();
+ if( bOk )
+ {
+ size_t nOpSize = popOperandSize();
+ size_t nSpacesSize = appendWhiteSpaceTokens( pSpaces );
+ appendRawToken( nOpCode );
+ pushOperandSize( nOpSize + nSpacesSize + 1 );
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushBinaryOperatorToken( sal_Int32 nOpCode, const WhiteSpaceVec* pSpaces )
+{
+ bool bOk = maOperandSizeStack.size() >= 2;
+ if( bOk )
+ {
+ size_t nOp2Size = popOperandSize();
+ size_t nOp1Size = popOperandSize();
+ size_t nSpacesSize = insertWhiteSpaceTokens( pSpaces, nOp2Size );
+ insertRawToken( nOpCode, nOp2Size );
+ pushOperandSize( nOp1Size + nSpacesSize + 1 + nOp2Size );
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushParenthesesOperatorToken( const WhiteSpaceVec* pOpeningSpaces, const WhiteSpaceVec* pClosingSpaces )
+{
+ bool bOk = !maOperandSizeStack.empty();
+ if( bOk )
+ {
+ size_t nOpSize = popOperandSize();
+ size_t nSpacesSize = insertWhiteSpaceTokens( pOpeningSpaces, nOpSize );
+ insertRawToken( OPCODE_OPEN, nOpSize );
+ nSpacesSize += appendWhiteSpaceTokens( pClosingSpaces );
+ appendRawToken( OPCODE_CLOSE );
+ pushOperandSize( nOpSize + nSpacesSize + 2 );
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushFunctionOperatorToken( sal_Int32 nOpCode, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces, const WhiteSpaceVec* pClosingSpaces )
+{
+ /* #i70925# if there are not enough tokens available on token stack, do
+ not exit with error, but reduce parameter count. */
+ nParamCount = ::std::min( maOperandSizeStack.size(), nParamCount );
+
+ // convert all parameters on stack to a single operand separated with OPCODE_SEP
+ bool bOk = true;
+ for( size_t nParam = 1; bOk && (nParam < nParamCount); ++nParam )
+ bOk = pushBinaryOperatorToken( OPCODE_SEP );
+
+ // add function parentheses and function name
+ return bOk &&
+ ((nParamCount > 0) ? pushParenthesesOperatorToken( nullptr, pClosingSpaces ) : pushParenthesesOperandToken( pClosingSpaces )) &&
+ pushUnaryPreOperatorToken( nOpCode, pLeadingSpaces );
+}
+
+bool FormulaParserImpl::pushFunctionOperatorToken( const FunctionInfo& rFuncInfo, size_t nParamCount, const WhiteSpaceVec* pLeadingSpaces, const WhiteSpaceVec* pClosingSpaces )
+{
+ bool bOk = pushFunctionOperatorToken( rFuncInfo.mnApiOpCode, nParamCount, pLeadingSpaces, pClosingSpaces );
+ if( bOk )
+ {
+ // create an external add-in call for the passed built-in function
+ if( (rFuncInfo.mnApiOpCode == OPCODE_EXTERNAL) && !rFuncInfo.maExtProgName.isEmpty() )
+ getOperandToken( 0, 0 ).Data <<= rFuncInfo.maExtProgName;
+ // create a bad token with unsupported function name
+ else if( (rFuncInfo.mnApiOpCode == OPCODE_BAD) && !rFuncInfo.maOoxFuncName.isEmpty() )
+ getOperandToken( 0, 0 ).Data <<= rFuncInfo.maOoxFuncName;
+ }
+ return bOk;
+}
+
+bool FormulaParserImpl::pushOperand( sal_Int32 nOpCode )
+{
+ return pushOperandToken( nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+template< typename Type >
+bool FormulaParserImpl::pushValueOperand( const Type& rValue, sal_Int32 nOpCode )
+{
+ return pushValueOperandToken( rValue, nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushBoolOperand( bool bValue )
+{
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( bValue ? BIFF_FUNC_TRUE : BIFF_FUNC_FALSE ) )
+ return pushFunctionOperator( pFuncInfo->mnApiOpCode, 0 );
+ return pushValueOperand< double >( bValue ? 1.0 : 0.0 );
+}
+
+bool FormulaParserImpl::pushErrorOperand( double fEncodedError )
+{
+ // HACK: enclose all error codes into an 1x1 matrix
+ // start token array with opening brace and leading spaces
+ pushOperand( OPCODE_ARRAY_OPEN );
+ size_t nOpSize = popOperandSize();
+ size_t nOldArraySize = maTokenIndexes.size();
+ // push a double containing the Calc error code
+ appendRawToken( OPCODE_PUSH ) <<= fEncodedError;
+ // close token array and set resulting operand size
+ appendRawToken( OPCODE_ARRAY_CLOSE );
+ pushOperandSize( nOpSize + maTokenIndexes.size() - nOldArraySize );
+ return true;
+}
+
+bool FormulaParserImpl::pushBiffBoolOperand( sal_uInt8 nValue )
+{
+ return pushBoolOperand( nValue != BIFF_TOK_BOOL_FALSE );
+}
+
+bool FormulaParserImpl::pushBiffErrorOperand( sal_uInt8 nErrorCode )
+{
+ return pushErrorOperand( BiffHelper::calcDoubleFromError( nErrorCode ) );
+}
+
+bool FormulaParserImpl::pushReferenceOperand( const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset )
+{
+ SingleReference aApiRef;
+ convertReference2d( aApiRef, rRef, bDeleted, bRelativeAsOffset );
+ return pushValueOperand( aApiRef );
+}
+
+bool FormulaParserImpl::pushReferenceOperand( const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset )
+{
+ ComplexReference aApiRef;
+ convertReference2d( aApiRef, rRef.maRef1, rRef.maRef2, bDeleted, bRelativeAsOffset );
+ return pushValueOperand( aApiRef );
+}
+
+template< typename Type >
+bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const Type& rApiRef )
+{
+ if( rSheetRange.isExternal() )
+ {
+ ExternalReference aApiExtRef;
+ aApiExtRef.Index = rSheetRange.getDocLinkIndex();
+ aApiExtRef.Reference <<= rApiRef;
+ return pushValueOperand( aApiExtRef );
+ }
+ return pushValueOperand( rApiRef );
+}
+
+bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset )
+{
+ if( rSheetRange.is3dRange() )
+ {
+ // single-cell-range over several sheets, needs to create a ComplexReference
+ ComplexReference aApiRef;
+ convertReference3d( aApiRef, rSheetRange, rRef, rRef, bDeleted, bRelativeAsOffset );
+ return pushReferenceOperand( rSheetRange, aApiRef );
+ }
+ SingleReference aApiRef;
+ convertReference3d( aApiRef, rSheetRange.getFirstSheet(), rSheetRange.isSameSheet(), rRef, bDeleted, bRelativeAsOffset );
+ return pushReferenceOperand( rSheetRange, aApiRef );
+}
+
+bool FormulaParserImpl::pushReferenceOperand( const LinkSheetRange& rSheetRange, const BinComplexRef2d& rRef, bool bDeleted, bool bRelativeAsOffset )
+{
+ ComplexReference aApiRef;
+ convertReference3d( aApiRef, rSheetRange, rRef.maRef1, rRef.maRef2, bDeleted, bRelativeAsOffset );
+ return pushReferenceOperand( rSheetRange, aApiRef );
+}
+
+bool FormulaParserImpl::pushEmbeddedRefOperand( const DefinedNameBase& rName, bool bPushBadToken )
+{
+ if( bPushBadToken && !rName.getModelName().isEmpty() && (rName.getModelName()[ 0 ] >= ' ') )
+ return pushValueOperand( rName.getModelName(), OPCODE_BAD );
+ return pushBiffErrorOperand( BIFF_ERR_NAME );
+}
+
+bool FormulaParserImpl::pushDefinedNameOperand( const DefinedNameRef& rxDefName )
+{
+ if( !rxDefName || rxDefName->getModelName().isEmpty() )
+ return pushBiffErrorOperand( BIFF_ERR_NAME );
+ if( rxDefName->isMacroFunction() )
+ return pushValueOperand( rxDefName->getModelName(), OPCODE_MACRO );
+ if( rxDefName->getTokenIndex() >= 0 )
+ return pushValueOperand( rxDefName->getTokenIndex(), OPCODE_NAME );
+ return pushEmbeddedRefOperand( *rxDefName, true );
+}
+
+bool FormulaParserImpl::pushExternalFuncOperand( const FunctionInfo& rFuncInfo )
+{
+ return (rFuncInfo.mnApiOpCode == OPCODE_EXTERNAL) ?
+ pushValueOperand( rFuncInfo.maExtProgName, OPCODE_EXTERNAL ) :
+ pushOperand( rFuncInfo.mnApiOpCode );
+}
+
+bool FormulaParserImpl::pushDdeLinkOperand( const OUString& rDdeServer, const OUString& rDdeTopic, const OUString& rDdeItem )
+{
+ // create the function call DDE("server";"topic";"item")
+ return
+ pushValueOperandToken( rDdeServer ) &&
+ pushValueOperandToken( rDdeTopic ) &&
+ pushValueOperandToken( rDdeItem ) &&
+ pushFunctionOperator( OPCODE_DDE, 3 );
+}
+
+bool FormulaParserImpl::pushExternalNameOperand( const ExternalNameRef& rxExtName, const ExternalLink& rExtLink )
+{
+ if( rxExtName ) switch( rExtLink.getLinkType() )
+ {
+ case ExternalLinkType::External:
+ return pushEmbeddedRefOperand( *rxExtName, false );
+
+ case ExternalLinkType::Library:
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( rxExtName->getUpcaseModelName() ) )
+ if( (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) && (pFuncInfo->meFuncLibType == rExtLink.getFuncLibraryType()) )
+ return pushExternalFuncOperand( *pFuncInfo );
+ break;
+
+ case ExternalLinkType::DDE:
+ {
+ OUString aDdeServer, aDdeTopic, aDdeItem;
+ if( rxExtName->getDdeLinkData( aDdeServer, aDdeTopic, aDdeItem ) )
+ return pushDdeLinkOperand( aDdeServer, aDdeTopic, aDdeItem );
+ }
+ break;
+
+ default:
+ OSL_ENSURE( rExtLink.getLinkType() != ExternalLinkType::Self, "FormulaParserImpl::pushExternalNameOperand - invalid call" );
+ }
+ return pushBiffErrorOperand( BIFF_ERR_NAME );
+}
+
+bool FormulaParserImpl::pushSpecialTokenOperand( const BinAddress& rBaseAddr )
+{
+ CellAddress aBaseAddr( maBaseAddr.Tab(), rBaseAddr.mnCol, rBaseAddr.mnRow );
+ ApiSpecialTokenInfo aTokenInfo( aBaseAddr, false );
+ return mbSpecialTokens && (getFormulaSize() == 0) && pushValueOperand( aTokenInfo, OPCODE_BAD );
+}
+
+bool FormulaParserImpl::pushUnaryPreOperator( sal_Int32 nOpCode )
+{
+ return pushUnaryPreOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushUnaryPostOperator( sal_Int32 nOpCode )
+{
+ return pushUnaryPostOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushBinaryOperator( sal_Int32 nOpCode )
+{
+ return pushBinaryOperatorToken( nOpCode, &maLeadingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushParenthesesOperator()
+{
+ return pushParenthesesOperatorToken( &maOpeningSpaces, &maClosingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushFunctionOperator( sal_Int32 nOpCode, size_t nParamCount )
+{
+ return pushFunctionOperatorToken( nOpCode, nParamCount, &maLeadingSpaces, &maClosingSpaces ) && resetSpaces();
+}
+
+bool FormulaParserImpl::pushFunctionOperator( const FunctionInfo& rFuncInfo, size_t nParamCount )
+{
+ return pushFunctionOperatorToken( rFuncInfo, nParamCount, &maLeadingSpaces, &maClosingSpaces ) && resetSpaces();
+}
+
+// reference conversion -------------------------------------------------------
+
+void FormulaParserImpl::initReference2d( SingleReference& orApiRef ) const
+{
+ if( mb2dRefsAs3dRefs )
+ {
+ initReference3d( orApiRef, sal_Int32 (maBaseAddr.Tab() ), false );
+ }
+ else
+ {
+ orApiRef.Flags = SHEET_RELATIVE;
+ // #i10184# absolute sheet index needed for relative references in shared formulas
+ orApiRef.Sheet = sal_Int32( maBaseAddr.Tab() );
+ orApiRef.RelativeSheet = 0;
+ }
+}
+
+void FormulaParserImpl::initReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet )
+{
+ orApiRef.Flags = SHEET_3D;
+ if( nSheet < 0 )
+ {
+ orApiRef.Sheet = 0;
+ orApiRef.Flags |= SHEET_DELETED;
+ }
+ else if( bSameSheet )
+ {
+ OSL_ENSURE( nSheet == 0, "FormulaParserImpl::initReference3d - invalid sheet index" );
+ orApiRef.Flags |= SHEET_RELATIVE;
+ orApiRef.RelativeSheet = 0;
+ }
+ else
+ {
+ orApiRef.Sheet = nSheet;
+ }
+}
+
+void FormulaParserImpl::convertReference( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ if( bDeleted )
+ {
+ orApiRef.Column = 0;
+ orApiRef.Row = 0;
+ // no explicit information about whether row or column is deleted
+ orApiRef.Flags |= COLUMN_DELETED | ROW_DELETED;
+ }
+ else
+ {
+ // column/row indexes and flags
+ setFlag( orApiRef.Flags, COLUMN_RELATIVE, rRef.mbColRel );
+ setFlag( orApiRef.Flags, ROW_RELATIVE, rRef.mbRowRel );
+ (rRef.mbColRel ? orApiRef.RelativeColumn : orApiRef.Column) = rRef.mnCol;
+ (rRef.mbRowRel ? orApiRef.RelativeRow : orApiRef.Row) = rRef.mnRow;
+ // convert absolute indexes to relative offsets used in API
+ if( !bRelativeAsOffset )
+ {
+ if( rRef.mbColRel )
+ orApiRef.RelativeColumn -= sal_Int32( maBaseAddr.Col() );
+ if( rRef.mbRowRel )
+ orApiRef.RelativeRow -= maBaseAddr.Row();
+ }
+ }
+}
+
+void FormulaParserImpl::convertReference( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ convertReference( orApiRef.Reference1, rRef1, bDeleted, bRelativeAsOffset );
+ convertReference( orApiRef.Reference2, rRef2, bDeleted, bRelativeAsOffset );
+ /* Handle references to complete rows or columns (e.g. $1:$2 or C:D),
+ need to expand or shrink to limits of own document. */
+ if( !bDeleted && !rRef1.mbColRel && !rRef2.mbColRel && (orApiRef.Reference1.Column == 0) && (orApiRef.Reference2.Column == mnMaxXlsCol) )
+ orApiRef.Reference2.Column = mnMaxApiCol;
+ if( !bDeleted && !rRef1.mbRowRel && !rRef2.mbRowRel && (orApiRef.Reference1.Row == 0) && (orApiRef.Reference2.Row == mnMaxXlsRow) )
+ orApiRef.Reference2.Row = mnMaxApiRow;
+}
+
+void FormulaParserImpl::convertReference2d( SingleReference& orApiRef, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ initReference2d( orApiRef );
+ convertReference( orApiRef, rRef, bDeleted, bRelativeAsOffset );
+}
+
+void FormulaParserImpl::convertReference2d( ComplexReference& orApiRef, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ initReference2d( orApiRef.Reference1 );
+ initReference2d( orApiRef.Reference2 );
+ convertReference( orApiRef, rRef1, rRef2, bDeleted, bRelativeAsOffset );
+ // remove sheet name from second part of reference
+ setFlag( orApiRef.Reference2.Flags, SHEET_3D, false );
+}
+
+void FormulaParserImpl::convertReference3d( SingleReference& orApiRef, sal_Int32 nSheet, bool bSameSheet, const BinSingleRef2d& rRef, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ initReference3d( orApiRef, nSheet, bSameSheet );
+ convertReference( orApiRef, rRef, bDeleted, bRelativeAsOffset );
+}
+
+void FormulaParserImpl::convertReference3d( ComplexReference& orApiRef, const LinkSheetRange& rSheetRange, const BinSingleRef2d& rRef1, const BinSingleRef2d& rRef2, bool bDeleted, bool bRelativeAsOffset ) const
+{
+ bool bSameSheet = rSheetRange.isSameSheet();
+ initReference3d( orApiRef.Reference1, rSheetRange.getFirstSheet(), bSameSheet );
+ initReference3d( orApiRef.Reference2, rSheetRange.getLastSheet(), bSameSheet );
+ convertReference( orApiRef, rRef1, rRef2, bDeleted, bRelativeAsOffset );
+ // remove sheet name from second part of reference
+ setFlag( orApiRef.Reference2.Flags, SHEET_3D, rSheetRange.is3dRange() );
+}
+
+// finalize token sequence ----------------------------------------------------
+
+const FunctionInfo* FormulaParserImpl::resolveBadFuncName( const OUString& rTokenData ) const
+{
+ /* Try to parse calls to library functions. The format of such a function
+ call is "[n]!funcname", n>0 being the link identifier of the function
+ library spreadsheet file. */
+ sal_Int32 nBracketOpen = rTokenData.indexOf( '[' );
+ sal_Int32 nBracketClose = rTokenData.indexOf( ']' );
+ sal_Int32 nExclamation = rTokenData.indexOf( '!' );
+ if( (0 == nBracketOpen) && (nBracketOpen + 1 < nBracketClose) && (nBracketClose + 1 == nExclamation) && (nExclamation + 1 < rTokenData.getLength()) )
+ {
+ sal_Int32 nRefId = o3tl::toInt32(rTokenData.subView( nBracketOpen + 1, nBracketClose - nBracketOpen - 1 ));
+ const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get();
+ if( pExtLink && (pExtLink->getLinkType() == ExternalLinkType::Library) )
+ {
+ OUString aFuncName = rTokenData.copy( nExclamation + 1 ).toAsciiUpperCase();
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( aFuncName ) )
+ if( (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) && (pFuncInfo->meFuncLibType == pExtLink->getFuncLibraryType()) )
+ return pFuncInfo;
+ }
+ }
+ return nullptr;
+}
+
+OUString FormulaParserImpl::resolveDefinedName( sal_Int32 nTokenIndex ) const
+{
+ if( const DefinedName* pDefName = getDefinedNames().getByTokenIndex( nTokenIndex ).get() )
+ return pDefName->getCalcName();
+ return OUString();
+}
+
+// OOXML/BIFF12 parser implementation =========================================
+
+namespace {
+
+class OoxFormulaParserImpl : public FormulaParserImpl
+{
+public:
+ explicit OoxFormulaParserImpl( const FormulaParser& rParent );
+
+ virtual ApiTokenSequence importOoxFormula(
+ const ScAddress& rBaseAddr,
+ const OUString& rFormulaString ) override;
+
+ virtual ApiTokenSequence importBiff12Formula(
+ const ScAddress& rBaseAddr,
+ FormulaType eType,
+ SequenceInputStream& rStrm ) override;
+
+private:
+ // import token contents and create API formula token ---------------------
+
+ bool importAttrToken( SequenceInputStream& rStrm );
+ bool importSpaceToken( SequenceInputStream& rStrm );
+ bool importTableToken( SequenceInputStream& rStrm );
+ bool importArrayToken( SequenceInputStream& rStrm );
+ bool importRefToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset );
+ bool importAreaToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset );
+ bool importRef3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset );
+ bool importArea3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset );
+ bool importMemAreaToken( SequenceInputStream& rStrm, bool bAddData );
+ bool importMemFuncToken( SequenceInputStream& rStrm );
+ bool importNameToken( SequenceInputStream& rStrm );
+ bool importNameXToken( SequenceInputStream& rStrm );
+ bool importFuncToken( SequenceInputStream& rStrm );
+ bool importFuncVarToken( SequenceInputStream& rStrm );
+ bool importExpToken( SequenceInputStream& rStrm );
+
+ LinkSheetRange readSheetRange( SequenceInputStream& rStrm );
+
+ void swapStreamPosition( SequenceInputStream& rStrm );
+ void skipMemAreaAddData( SequenceInputStream& rStrm );
+
+ // convert BIN token and push API operand or operator ---------------------
+
+ bool pushBiff12Name( sal_Int32 nNameId );
+ bool pushBiff12ExtName( sal_Int32 nRefId, sal_Int32 nNameId );
+ bool pushBiff12Function( sal_uInt16 nFuncId );
+ bool pushBiff12Function( sal_uInt16 nFuncId, sal_uInt8 nParamCount );
+
+private:
+ ApiParserWrapper maApiParser; /// Wrapper for the API formula parser object.
+ sal_Int64 mnAddDataPos; /// Current stream position for additional data (tExp, tArray, tMemArea).
+ bool mbNeedExtRefs; /// True = parser needs initialization of external reference info.
+};
+
+}
+
+OoxFormulaParserImpl::OoxFormulaParserImpl( const FormulaParser& rParent ) :
+ FormulaParserImpl( rParent ),
+ maApiParser( rParent.getBaseFilter().getModelFactory(), rParent ),
+ mnAddDataPos( 0 ),
+ mbNeedExtRefs( true )
+{
+}
+
+ApiTokenSequence OoxFormulaParserImpl::importOoxFormula( const ScAddress& rBaseAddr, const OUString& rFormulaString )
+{
+ if( mbNeedExtRefs )
+ {
+ maApiParser.getParserProperties().setProperty( PROP_ExternalLinks, getExternalLinks().getLinkInfos() );
+ mbNeedExtRefs = false;
+ }
+ return finalizeTokenArray( maApiParser.parseFormula( rFormulaString, rBaseAddr ) );
+}
+
+ApiTokenSequence OoxFormulaParserImpl::importBiff12Formula( const ScAddress& rBaseAddr, FormulaType eType, SequenceInputStream& rStrm )
+{
+ initializeImport( rBaseAddr, eType );
+
+ sal_Int32 nFmlaSize = rStrm.readInt32();
+ sal_Int64 nFmlaPos = rStrm.tell();
+ sal_Int64 nFmlaEndPos = nFmlaPos + nFmlaSize;
+
+ rStrm.seek( nFmlaEndPos );
+ sal_Int32 nAddDataSize = rStrm.readInt32();
+ mnAddDataPos = rStrm.tell();
+ sal_Int64 nAddDataEndPos = mnAddDataPos + nAddDataSize;
+ rStrm.seek( nFmlaPos );
+
+ bool bOk = (nFmlaSize >= 0) && (nAddDataSize >= 0);
+ bool bRelativeAsOffset = mbRelativeAsOffset;
+
+ while( bOk && !rStrm.isEof() && (rStrm.tell() < nFmlaEndPos) )
+ {
+ sal_uInt8 nTokenId;
+ nTokenId = rStrm.readuChar();
+ sal_uInt8 nTokenClass = nTokenId & BIFF_TOKCLASS_MASK;
+ sal_uInt8 nBaseId = nTokenId & BIFF_TOKID_MASK;
+
+ if( nTokenClass == BIFF_TOKCLASS_NONE )
+ {
+ // base tokens
+ switch( nBaseId )
+ {
+ case BIFF_TOKID_EXP: bOk = importExpToken( rStrm ); break;
+ case BIFF_TOKID_ADD: bOk = pushBinaryOperator( OPCODE_ADD ); break;
+ case BIFF_TOKID_SUB: bOk = pushBinaryOperator( OPCODE_SUB ); break;
+ case BIFF_TOKID_MUL: bOk = pushBinaryOperator( OPCODE_MULT ); break;
+ case BIFF_TOKID_DIV: bOk = pushBinaryOperator( OPCODE_DIV ); break;
+ case BIFF_TOKID_POWER: bOk = pushBinaryOperator( OPCODE_POWER ); break;
+ case BIFF_TOKID_CONCAT: bOk = pushBinaryOperator( OPCODE_CONCAT ); break;
+ case BIFF_TOKID_LT: bOk = pushBinaryOperator( OPCODE_LESS ); break;
+ case BIFF_TOKID_LE: bOk = pushBinaryOperator( OPCODE_LESS_EQUAL ); break;
+ case BIFF_TOKID_EQ: bOk = pushBinaryOperator( OPCODE_EQUAL ); break;
+ case BIFF_TOKID_GE: bOk = pushBinaryOperator( OPCODE_GREATER_EQUAL ); break;
+ case BIFF_TOKID_GT: bOk = pushBinaryOperator( OPCODE_GREATER ); break;
+ case BIFF_TOKID_NE: bOk = pushBinaryOperator( OPCODE_NOT_EQUAL ); break;
+ case BIFF_TOKID_ISECT: bOk = pushBinaryOperator( OPCODE_INTERSECT ); break;
+ case BIFF_TOKID_LIST: bOk = pushBinaryOperator( OPCODE_LIST ); break;
+ case BIFF_TOKID_RANGE: bOk = pushBinaryOperator( OPCODE_RANGE ); break;
+ case BIFF_TOKID_UPLUS: bOk = pushUnaryPreOperator( OPCODE_PLUS_SIGN ); break;
+ case BIFF_TOKID_UMINUS: bOk = pushUnaryPreOperator( OPCODE_MINUS_SIGN ); break;
+ case BIFF_TOKID_PERCENT: bOk = pushUnaryPostOperator( OPCODE_PERCENT ); break;
+ case BIFF_TOKID_PAREN: bOk = pushParenthesesOperator(); break;
+ case BIFF_TOKID_MISSARG: bOk = pushOperand( OPCODE_MISSING ); break;
+ case BIFF_TOKID_STR: bOk = pushValueOperand( BiffHelper::readString( rStrm, false ) ); break;
+ case BIFF_TOKID_NLR: bOk = importTableToken( rStrm ); break;
+ case BIFF_TOKID_ATTR: bOk = importAttrToken( rStrm ); break;
+ case BIFF_TOKID_ERR: bOk = pushBiffErrorOperand( rStrm.readuInt8() ); break;
+ case BIFF_TOKID_BOOL: bOk = pushBiffBoolOperand( rStrm.readuInt8() ); break;
+ case BIFF_TOKID_INT: bOk = pushValueOperand< double >( rStrm.readuInt16() ); break;
+ case BIFF_TOKID_NUM: bOk = pushValueOperand( rStrm.readDouble() ); break;
+ default: bOk = false;
+ }
+ }
+ else
+ {
+ // classified tokens
+ switch( nBaseId )
+ {
+ case BIFF_TOKID_ARRAY: bOk = importArrayToken( rStrm ); break;
+ case BIFF_TOKID_FUNC: bOk = importFuncToken( rStrm ); break;
+ case BIFF_TOKID_FUNCVAR: bOk = importFuncVarToken( rStrm ); break;
+ case BIFF_TOKID_NAME: bOk = importNameToken( rStrm ); break;
+ case BIFF_TOKID_REF: bOk = importRefToken( rStrm, false, false ); break;
+ case BIFF_TOKID_AREA: bOk = importAreaToken( rStrm, false, false ); break;
+ case BIFF_TOKID_MEMAREA: bOk = importMemAreaToken( rStrm, true ); break;
+ case BIFF_TOKID_MEMERR: bOk = importMemAreaToken( rStrm, false ); break;
+ case BIFF_TOKID_MEMNOMEM: bOk = importMemAreaToken( rStrm, false ); break;
+ case BIFF_TOKID_MEMFUNC: bOk = importMemFuncToken( rStrm ); break;
+ case BIFF_TOKID_REFERR: bOk = importRefToken( rStrm, true, false ); break;
+ case BIFF_TOKID_AREAERR: bOk = importAreaToken( rStrm, true, false ); break;
+ case BIFF_TOKID_REFN: bOk = importRefToken( rStrm, false, true ); break;
+ case BIFF_TOKID_AREAN: bOk = importAreaToken( rStrm, false, true ); break;
+ case BIFF_TOKID_MEMAREAN: bOk = importMemFuncToken( rStrm ); break;
+ case BIFF_TOKID_MEMNOMEMN: bOk = importMemFuncToken( rStrm ); break;
+ case BIFF_TOKID_NAMEX: bOk = importNameXToken( rStrm ); break;
+ case BIFF_TOKID_REF3D: bOk = importRef3dToken( rStrm, false, bRelativeAsOffset ); break;
+ case BIFF_TOKID_AREA3D: bOk = importArea3dToken( rStrm, false, bRelativeAsOffset ); break;
+ case BIFF_TOKID_REFERR3D: bOk = importRef3dToken( rStrm, true, bRelativeAsOffset ); break;
+ case BIFF_TOKID_AREAERR3D: bOk = importArea3dToken( rStrm, true, bRelativeAsOffset ); break;
+ default: bOk = false;
+ }
+ }
+ }
+
+ // build and finalize the token sequence
+ ApiTokenSequence aFinalTokens;
+ if( bOk && (rStrm.tell() == nFmlaEndPos) && (mnAddDataPos == nAddDataEndPos) )
+ aFinalTokens = finalizeImport();
+
+ // seek behind token array
+ if( (nFmlaSize >= 0) && (nAddDataSize >= 0) )
+ rStrm.seek( nAddDataEndPos );
+
+ // return the final token sequence
+ return aFinalTokens;
+}
+
+// import token contents and create API formula token -------------------------
+
+bool OoxFormulaParserImpl::importAttrToken( SequenceInputStream& rStrm )
+{
+ bool bOk = true;
+ sal_uInt8 nType;
+ nType = rStrm.readuChar();
+ // equal flags in all BIFFs
+ switch( nType )
+ {
+ case 0: // sometimes, tAttrSkip tokens miss the type flag
+ case BIFF_TOK_ATTR_VOLATILE:
+ case BIFF_TOK_ATTR_IF:
+ case BIFF_TOK_ATTR_SKIP:
+ case BIFF_TOK_ATTR_ASSIGN:
+ case BIFF_TOK_ATTR_IFERROR:
+ rStrm.skip( 2 );
+ break;
+ case BIFF_TOK_ATTR_CHOOSE:
+ rStrm.skip( 2 * rStrm.readuInt16() + 2 );
+ break;
+ case BIFF_TOK_ATTR_SUM:
+ rStrm.skip( 2 );
+ bOk = pushBiff12Function( BIFF_FUNC_SUM, 1 );
+ break;
+ case BIFF_TOK_ATTR_SPACE:
+ case BIFF_TOK_ATTR_SPACE_VOLATILE:
+ bOk = importSpaceToken( rStrm );
+ break;
+ default:
+ bOk = false;
+ }
+ return bOk;
+}
+
+bool OoxFormulaParserImpl::importSpaceToken( SequenceInputStream& rStrm )
+{
+ // equal constants in BIFF and OOX
+ sal_uInt8 nType, nCount;
+ nType = rStrm.readuChar();
+ nCount = rStrm.readuChar();
+ switch( nType )
+ {
+ case BIFF_TOK_ATTR_SPACE_SP:
+ appendLeadingSpaces( nCount, false );
+ break;
+ case BIFF_TOK_ATTR_SPACE_BR:
+ appendLeadingSpaces( nCount, true );
+ break;
+ case BIFF_TOK_ATTR_SPACE_SP_OPEN:
+ appendOpeningSpaces( nCount, false );
+ break;
+ case BIFF_TOK_ATTR_SPACE_BR_OPEN:
+ appendOpeningSpaces( nCount, true );
+ break;
+ case BIFF_TOK_ATTR_SPACE_SP_CLOSE:
+ appendClosingSpaces( nCount, false );
+ break;
+ case BIFF_TOK_ATTR_SPACE_BR_CLOSE:
+ appendClosingSpaces( nCount, true );
+ break;
+ }
+ return true;
+}
+
+bool OoxFormulaParserImpl::importTableToken( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags, nTableId, nCol1, nCol2;
+ rStrm.skip( 3 );
+ nFlags = rStrm.readuInt16();
+ nTableId = rStrm.readuInt16();
+ rStrm.skip( 2 );
+ nCol1 = rStrm.readuInt16();
+ nCol2 = rStrm.readuInt16();
+ TableRef xTable = getTables().getTable( nTableId );
+ sal_Int32 nTokenIndex = xTable ? xTable->getTokenIndex() : -1;
+ if( nTokenIndex >= 0 )
+ {
+ sal_Int32 nWidth = xTable->getWidth();
+ sal_Int32 nHeight = xTable->getHeight();
+ sal_Int32 nStartCol = 0;
+ sal_Int32 nEndCol = nWidth - 1;
+ sal_Int32 nStartRow = 0;
+ sal_Int32 nEndRow = nHeight - 1;
+ bool bFixedStartRow = true;
+ bool bFixedHeight = false;
+
+ bool bSingleCol = getFlag( nFlags, BIFF12_TOK_TABLE_COLUMN );
+ bool bColRange = getFlag( nFlags, BIFF12_TOK_TABLE_COLRANGE );
+ bool bValidRef = !bSingleCol || !bColRange;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - illegal combination of single column and column range" );
+ if( bValidRef )
+ {
+ if( bSingleCol )
+ nStartCol = nEndCol = nCol1;
+ else if( bColRange )
+ { nStartCol = nCol1; nEndCol = nCol2; }
+ bValidRef = (nStartCol <= nEndCol) && (nEndCol < nWidth);
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - invalid column range" );
+ }
+
+ if( bValidRef )
+ {
+ bool bAllRows = getFlag( nFlags, BIFF12_TOK_TABLE_ALL );
+ bool bHeaderRows = getFlag( nFlags, BIFF12_TOK_TABLE_HEADERS );
+ bool bDataRows = getFlag( nFlags, BIFF12_TOK_TABLE_DATA );
+ bool bTotalsRows = getFlag( nFlags, BIFF12_TOK_TABLE_TOTALS );
+ bool bThisRow = getFlag( nFlags, BIFF12_TOK_TABLE_THISROW );
+
+ sal_Int32 nStartDataRow = xTable->getHeaderRows();
+ sal_Int32 nEndDataRow = nEndRow - xTable->getTotalsRows();
+ bValidRef = (nStartRow <= nStartDataRow) && (nStartDataRow <= nEndDataRow) && (nEndDataRow <= nEndRow);
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - invalid data row range" );
+ if( bValidRef )
+ {
+ if( bAllRows )
+ {
+ bValidRef = !bHeaderRows && !bDataRows && !bTotalsRows && !bThisRow;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#All] table token" );
+ }
+ else if( bHeaderRows )
+ {
+ bValidRef = !bTotalsRows && !bThisRow;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Headers] table token" );
+ nEndRow = bDataRows ? nEndDataRow : (nStartDataRow - 1);
+ bFixedHeight = !bDataRows;
+ }
+ else if( bDataRows )
+ {
+ bValidRef = !bThisRow;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Data] table token" );
+ nStartRow = nStartDataRow;
+ if( !bTotalsRows ) nEndRow = nEndDataRow;
+ }
+ else if( bTotalsRows )
+ {
+ bValidRef = !bThisRow;
+ OSL_ENSURE( bValidRef, "OoxFormulaParserImpl::importTableToken - unexpected flags in [#Totals] table token" );
+ nStartRow = nEndDataRow + 1;
+ bFixedStartRow = false;
+ bFixedHeight = !bDataRows;
+ }
+ else if( bThisRow )
+ {
+ nStartRow = nEndRow = maBaseAddr.Row() - xTable->getRange().aStart.Row();
+ bFixedHeight = true;
+ }
+ else
+ {
+ // nothing is the same as [#Data]
+ nStartRow = nStartDataRow;
+ nEndRow = nEndDataRow;
+ }
+ }
+ if( bValidRef )
+ bValidRef = (0 <= nStartRow) && (nStartRow <= nEndRow) && (nEndRow < nHeight);
+ }
+ if( bValidRef )
+ {
+ // push single database area token, if table token refers to entire table
+ if( (nStartCol == 0) && (nEndCol + 1 == nWidth) && (nStartRow == 0) && (nEndRow + 1 == nHeight) )
+ return pushValueOperand( nTokenIndex, OPCODE_DBAREA );
+ // create an OFFSET function call to refer to a subrange of the table
+ const FunctionInfo* pRowsInfo = getFuncInfoFromBiff12FuncId( BIFF_FUNC_ROWS );
+ const FunctionInfo* pColumnsInfo = getFuncInfoFromBiff12FuncId( BIFF_FUNC_COLUMNS );
+ return
+ pRowsInfo && pColumnsInfo &&
+ pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) &&
+ (bFixedStartRow ?
+ pushValueOperandToken< double >( nStartRow ) :
+ (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) &&
+ pushFunctionOperatorToken( *pRowsInfo, 1 ) &&
+ pushValueOperandToken< double >( nHeight - nStartRow ) &&
+ pushBinaryOperatorToken( OPCODE_SUB ))) &&
+ pushValueOperandToken< double >( nStartCol ) &&
+ (bFixedHeight ?
+ pushValueOperandToken< double >( nEndRow - nStartRow + 1 ) :
+ (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) &&
+ pushFunctionOperatorToken( *pRowsInfo, 1 ) &&
+ (((nStartRow == 0) && (nEndRow + 1 == nHeight)) ||
+ (pushValueOperandToken< double >( nHeight - (nEndRow - nStartRow + 1) ) &&
+ pushBinaryOperatorToken( OPCODE_SUB ))))) &&
+ (((nStartCol == 0) && (nEndCol + 1 == nWidth)) ?
+ (pushValueOperandToken( nTokenIndex, OPCODE_DBAREA ) &&
+ pushFunctionOperatorToken( *pColumnsInfo, 1 )) :
+ pushValueOperandToken< double >( nEndCol - nStartCol + 1 )) &&
+ pushBiff12Function( BIFF_FUNC_OFFSET, 5 );
+ }
+ }
+ return pushBiffErrorOperand( BIFF_ERR_REF );
+}
+
+bool OoxFormulaParserImpl::importArrayToken( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 14 );
+
+ // start token array with opening brace and leading spaces
+ pushOperand( OPCODE_ARRAY_OPEN );
+ size_t nOpSize = popOperandSize();
+ size_t nOldArraySize = getFormulaSize();
+
+ // read array size
+ swapStreamPosition( rStrm );
+ sal_Int32 nRows = rStrm.readInt32();
+ sal_Int32 nCols = rStrm.readInt32();
+ OSL_ENSURE( (nCols > 0) && (nRows > 0), "OoxFormulaParserImpl::importArrayToken - empty array" );
+
+ // read array values and build token array
+ for( sal_Int32 nRow = 0; !rStrm.isEof() && (nRow < nRows); ++nRow )
+ {
+ if( nRow > 0 )
+ appendRawToken( OPCODE_ARRAY_ROWSEP );
+ for( sal_Int32 nCol = 0; !rStrm.isEof() && (nCol < nCols); ++nCol )
+ {
+ if( nCol > 0 )
+ appendRawToken( OPCODE_ARRAY_COLSEP );
+ switch( rStrm.readuInt8() )
+ {
+ case BIFF_TOK_ARRAY_DOUBLE:
+ appendRawToken( OPCODE_PUSH ) <<= rStrm.readDouble();
+ break;
+ case BIFF_TOK_ARRAY_STRING:
+ appendRawToken( OPCODE_PUSH ) <<= BiffHelper::readString( rStrm, false );
+ break;
+ case BIFF_TOK_ARRAY_BOOL:
+ appendRawToken( OPCODE_PUSH ) <<= static_cast< double >( (rStrm.readuInt8() == BIFF_TOK_BOOL_FALSE) ? 0.0 : 1.0 );
+ break;
+ case BIFF_TOK_ARRAY_ERROR:
+ appendRawToken( OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( rStrm.readuInt8() );
+ rStrm.skip( 3 );
+ break;
+ default:
+ OSL_FAIL( "OoxFormulaParserImpl::importArrayToken - unknown data type" );
+ appendRawToken( OPCODE_PUSH ) <<= BiffHelper::calcDoubleFromError( BIFF_ERR_NA );
+ }
+ }
+ }
+ swapStreamPosition( rStrm );
+
+ // close token array and set resulting operand size
+ appendRawToken( OPCODE_ARRAY_CLOSE );
+ pushOperandSize( nOpSize + getFormulaSize() - nOldArraySize );
+ return true;
+}
+
+bool OoxFormulaParserImpl::importRefToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset )
+{
+ BinSingleRef2d aRef;
+ aRef.readBiff12Data( rStrm, bRelativeAsOffset );
+ return pushReferenceOperand( aRef, bDeleted, bRelativeAsOffset );
+}
+
+bool OoxFormulaParserImpl::importAreaToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset )
+{
+ BinComplexRef2d aRef;
+ aRef.readBiff12Data( rStrm, bRelativeAsOffset );
+ return pushReferenceOperand( aRef, bDeleted, bRelativeAsOffset );
+}
+
+bool OoxFormulaParserImpl::importRef3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset )
+{
+ LinkSheetRange aSheetRange = readSheetRange( rStrm );
+ BinSingleRef2d aRef;
+ aRef.readBiff12Data( rStrm, bRelativeAsOffset );
+ return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset );
+}
+
+bool OoxFormulaParserImpl::importArea3dToken( SequenceInputStream& rStrm, bool bDeleted, bool bRelativeAsOffset )
+{
+ LinkSheetRange aSheetRange = readSheetRange( rStrm );
+ BinComplexRef2d aRef;
+ aRef.readBiff12Data( rStrm, bRelativeAsOffset );
+ return pushReferenceOperand( aSheetRange, aRef, bDeleted, bRelativeAsOffset );
+}
+
+bool OoxFormulaParserImpl::importMemAreaToken( SequenceInputStream& rStrm, bool bAddData )
+{
+ rStrm.skip( 6 );
+ if( bAddData )
+ skipMemAreaAddData( rStrm );
+ return true;
+}
+
+bool OoxFormulaParserImpl::importMemFuncToken( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 2 );
+ return true;
+}
+
+bool OoxFormulaParserImpl::importNameToken( SequenceInputStream& rStrm )
+{
+ return pushBiff12Name( rStrm.readInt32() );
+}
+
+bool OoxFormulaParserImpl::importNameXToken( SequenceInputStream& rStrm )
+{
+ sal_Int32 nRefId = rStrm.readInt16();
+ sal_Int32 nNameId = rStrm.readInt32();
+ return pushBiff12ExtName( nRefId, nNameId );
+}
+
+bool OoxFormulaParserImpl::importFuncToken( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFuncId;
+ nFuncId = rStrm.readuInt16();
+ return pushBiff12Function( nFuncId );
+}
+
+bool OoxFormulaParserImpl::importFuncVarToken( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nParamCount;
+ sal_uInt16 nFuncId;
+ nParamCount = rStrm.readuChar();
+ nFuncId = rStrm.readuInt16();
+ return pushBiff12Function( nFuncId, nParamCount );
+}
+
+bool OoxFormulaParserImpl::importExpToken( SequenceInputStream& rStrm )
+{
+ BinAddress aBaseAddr;
+ aBaseAddr.mnRow = rStrm.readInt32();
+ swapStreamPosition( rStrm );
+ aBaseAddr.mnCol = rStrm.readInt32();
+ swapStreamPosition( rStrm );
+ return pushSpecialTokenOperand( aBaseAddr );
+}
+
+LinkSheetRange OoxFormulaParserImpl::readSheetRange( SequenceInputStream& rStrm )
+{
+ return getExternalLinks().getSheetRange( rStrm.readInt16() );
+}
+
+void OoxFormulaParserImpl::swapStreamPosition( SequenceInputStream& rStrm )
+{
+ sal_Int64 nRecPos = rStrm.tell();
+ rStrm.seek( mnAddDataPos );
+ mnAddDataPos = nRecPos;
+}
+
+void OoxFormulaParserImpl::skipMemAreaAddData( SequenceInputStream& rStrm )
+{
+ swapStreamPosition( rStrm );
+ rStrm.skip( 16 * rStrm.readInt32() );
+ swapStreamPosition( rStrm );
+}
+
+// convert BIN token and push API operand or operator -------------------------
+
+bool OoxFormulaParserImpl::pushBiff12Name( sal_Int32 nNameId )
+{
+ // one-based in BIFF12 formulas
+ return pushDefinedNameOperand( getDefinedNames().getByIndex( nNameId - 1 ) );
+}
+
+bool OoxFormulaParserImpl::pushBiff12ExtName( sal_Int32 nRefId, sal_Int32 nNameId )
+{
+ if( const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId ).get() )
+ {
+ if( pExtLink->getLinkType() == ExternalLinkType::Self )
+ return pushBiff12Name( nNameId );
+ // external name indexes are one-based in BIFF12
+ ExternalNameRef xExtName = pExtLink->getNameByIndex( nNameId - 1 );
+ return pushExternalNameOperand( xExtName, *pExtLink );
+ }
+ return pushBiffErrorOperand( BIFF_ERR_NAME );
+}
+
+bool OoxFormulaParserImpl::pushBiff12Function( sal_uInt16 nFuncId )
+{
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( nFuncId ) )
+ if( pFuncInfo->mnMinParamCount == pFuncInfo->mnMaxParamCount )
+ return pushFunctionOperator( *pFuncInfo, pFuncInfo->mnMinParamCount );
+ return pushFunctionOperator( OPCODE_NONAME, 0 );
+}
+
+bool OoxFormulaParserImpl::pushBiff12Function( sal_uInt16 nFuncId, sal_uInt8 nParamCount )
+{
+ if( getFlag( nFuncId, BIFF_TOK_FUNCVAR_CMD ) )
+ nParamCount &= BIFF_TOK_FUNCVAR_COUNTMASK;
+ if( const FunctionInfo* pFuncInfo = getFuncInfoFromBiff12FuncId( nFuncId ) )
+ return pushFunctionOperator( *pFuncInfo, nParamCount );
+ return pushFunctionOperator( OPCODE_NONAME, nParamCount );
+}
+
+namespace {
+
+/** Extracts the reference identifier and the remaining data from a formula in
+ the format '[RefID]Remaining'. */
+bool lclExtractRefId( sal_Int32& rnRefId, OUString& rRemainder, const OUString& rFormulaString )
+{
+ if( (rFormulaString.getLength() >= 4) && (rFormulaString[ 0 ] == '[') )
+ {
+ sal_Int32 nBracketClose = rFormulaString.indexOf( ']', 1 );
+ if( nBracketClose >= 2 )
+ {
+ rnRefId = o3tl::toInt32(rFormulaString.subView( 1, nBracketClose - 1 ));
+ rRemainder = rFormulaString.copy( nBracketClose + 1 );
+ return !rRemainder.isEmpty();
+ }
+ }
+ return false;
+}
+
+}
+
+FormulaParser::FormulaParser( const WorkbookHelper& rHelper ) :
+ FormulaProcessorBase( rHelper )
+{
+ mxImpl.reset( new OoxFormulaParserImpl( *this ) );
+}
+
+FormulaParser::~FormulaParser()
+{
+}
+
+ApiTokenSequence FormulaParser::importFormula( const ScAddress& rBaseAddress, const OUString& rFormulaString ) const
+{
+ return mxImpl->importOoxFormula( rBaseAddress, rFormulaString );
+}
+
+ApiTokenSequence FormulaParser::importFormula( const ScAddress& rBaseAddress, FormulaType eType, SequenceInputStream& rStrm ) const
+{
+ return mxImpl->importBiff12Formula( rBaseAddress, eType, rStrm );
+}
+
+OUString FormulaParser::importOleTargetLink( const OUString& rFormulaString )
+{
+ sal_Int32 nRefId = -1;
+ OUString aRemainder;
+ if( lclExtractRefId( nRefId, aRemainder, rFormulaString ) && (aRemainder.getLength() >= 3) &&
+ (aRemainder[ 0 ] == '!') && (aRemainder[ 1 ] == '\'') && (aRemainder[ aRemainder.getLength() - 1 ] == '\'') )
+ return mxImpl->resolveOleTarget( nRefId, false );
+ return OUString();
+}
+
+OUString FormulaParser::importOleTargetLink( SequenceInputStream& rStrm )
+{
+ OUString aTargetLink;
+ sal_Int32 nFmlaSize = rStrm.readInt32();
+ sal_Int64 nFmlaEndPos = rStrm.tell() + ::std::max< sal_Int32 >( nFmlaSize, 0 );
+ if( (nFmlaSize == 7) && (rStrm.getRemaining() >= 7) )
+ {
+ sal_uInt8 nToken = rStrm.readuChar();
+ sal_Int16 nRefId = rStrm.readInt16();
+ rStrm.skip(4); //nNameId
+ if( nToken == (BIFF_TOKCLASS_VAL|BIFF_TOKID_NAMEX) )
+ aTargetLink = mxImpl->resolveOleTarget( nRefId, true );
+ }
+ rStrm.seek( nFmlaEndPos );
+ return aTargetLink;
+}
+
+OUString FormulaParser::importMacroName( const OUString& rFormulaString )
+{
+ /* Valid macros are either sheet macros or VBA macros. OOXML and all BIFF
+ documents store defined names for sheet macros, but OOXML documents do
+ not store any defined name for VBA macros (while BIFF documents do).
+ Sheet macros may be defined locally to a sheet, or globally to the
+ document. As a result, all of the following macro specifiers are valid:
+
+ 1) Macros located in the own document:
+ [0]!MySheetMacro (global sheet macro 'MySheetMacro')
+ Macro1!MyMacro (sheet-local sheet macro 'MyMacro')
+ [0]!MyVBAProc (VBA macro 'MyVBAProc')
+ [0]!Mod1.MyVBAProc (VBA macro 'MyVBAProc' from code module 'Mod1')
+
+ 2) Macros from an external document:
+ [2]!MySheetMacro (global external sheet macro 'MySheetMacro')
+ [2]Macro1!MyMacro (sheet-local external sheet macro 'MyMacro')
+ [2]!MyVBAProc (external VBA macro 'MyVBAProc')
+ [2]!Mod1.MyVBAProc (external VBA macro from code module 'Mod1')
+
+ This implementation is only interested in VBA macros from the own
+ document, ignoring the valid syntax 'Macro1!MyMacro' for sheet-local
+ sheet macros.
+ */
+ sal_Int32 nRefId = -1;
+ OUString aRemainder;
+ if( lclExtractRefId( nRefId, aRemainder, rFormulaString ) && (aRemainder.getLength() > 1) && (aRemainder[ 0 ] == '!') )
+ {
+ /* In BIFF12 documents, the reference identifier is always the
+ one-based index of the external link as it is in OOXML documents
+ (it is not an index into the list of reference sheets as used in
+ cell formulas). Index 0 is an implicit placeholder for the own
+ document. In BIFF12 documents, the reference to the own document is
+ stored explicitly, mostly at the top of the list, so index 1 may
+ resolve to the own document too.
+ Passing 'false' to getExternalLink() specifies to ignore the
+ reference sheets list (if existing) and to access the list of
+ external links directly. */
+ const ExternalLink* pExtLink = getExternalLinks().getExternalLink( nRefId, false ).get();
+ OSL_ENSURE( pExtLink, "FormulaParser::importMacroName - missing link" );
+ // do not accept macros in external documents (not supported)
+ if( pExtLink && (pExtLink->getLinkType() == ExternalLinkType::Self) )
+ {
+ // ignore sheet macros (defined name for VBA macros may not exist, see above)
+ OUString aMacroName = aRemainder.copy( 1 );
+ const DefinedName* pDefName = getDefinedNames().getByModelName( aMacroName ).get();
+ if( !pDefName || pDefName->isVBName() )
+ return aMacroName;
+ }
+ }
+ return OUString();
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/numberformatsbuffer.cxx b/sc/source/filter/oox/numberformatsbuffer.cxx
new file mode 100644
index 000000000..af8230604
--- /dev/null
+++ b/sc/source/filter/oox/numberformatsbuffer.cxx
@@ -0,0 +1,2094 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <numberformatsbuffer.hxx>
+#include <biffhelper.hxx>
+
+#include <com/sun/star/i18n/NumberFormatIndex.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <officecfg/Setup.hxx>
+#include <officecfg/System.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <rtl/ustrbuf.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/numformat.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/tokens.hxx>
+#include <scitems.hxx>
+#include <document.hxx>
+#include <ftools.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+namespace {
+
+/** Stores the number format used in Calc for an Excel built-in number format. */
+struct BuiltinFormat
+{
+ sal_Int32 mnNumFmtId; /// Built-in number format index.
+ const char* mpcFmtCode; /// Format string, UTF-8, may be 0 (mnPredefId is used then).
+ sal_Int16 mnPredefId; /// Predefined format index, if mpcFmtCode is 0.
+ sal_Int32 mnReuseId; /// Use this format, if mpcFmtCode is 0 and mnPredefId is -1.
+};
+
+/** Defines a literal built-in number format. */
+#define NUMFMT_STRING( INDEX, FORMATCODE ) \
+ { INDEX, FORMATCODE, -1, -1 }
+
+/** Defines a built-in number format that maps to an own predefined format. */
+#define NUMFMT_PREDEF( INDEX, PREDEFINED ) \
+ { INDEX, nullptr, css::i18n::NumberFormatIndex::PREDEFINED, -1 }
+
+/** Defines a built-in number format that is the same as the specified in nReuseId. */
+#define NUMFMT_REUSE( INDEX, REUSED_INDEX ) \
+ { INDEX, nullptr, -1, REUSED_INDEX }
+
+/** Terminates a built-in number format table. */
+#define NUMFMT_ENDTABLE() \
+ { -1, nullptr, -1, -1 }
+
+/** Defines builtin date and time formats 14...22.
+ @param SYSTEMDATE Complete short system date (for formats 14 and 22).
+ @param DAY Day format (for formats 15 and 16).
+ @param DAYSEP Separator between day and month (for formats 15 and 16).
+ @param MONTH Month format (for formats 15...17).
+ @param MONTHSEP Separator between month and year (for formats 15 and 17).
+ @param YEAR Year format (for formats 15 and 17).
+ @param HOUR12 Hour format for 12-hour AM/PM formats (formats 18 and 19).
+ @param HOUR24 Hour format for 24-hour formats (formats 20...22). */
+#define NUMFMT_ALLDATETIMES( SYSTEMDATE, DAY, DAYSEP, MONTH, MONTHSEP, YEAR, HOUR12, HOUR24 ) \
+ NUMFMT_STRING( 14, SYSTEMDATE ), \
+ NUMFMT_STRING( 15, DAY DAYSEP MONTH MONTHSEP YEAR ), \
+ NUMFMT_STRING( 16, DAY DAYSEP MONTH ), \
+ NUMFMT_STRING( 17, MONTH MONTHSEP YEAR ), \
+ NUMFMT_STRING( 18, HOUR12 ":mm AM/PM" ), \
+ NUMFMT_STRING( 19, HOUR12 ":mm:ss AM/PM" ), \
+ NUMFMT_STRING( 20, HOUR24 ":mm" ), \
+ NUMFMT_STRING( 21, HOUR24 ":mm:ss" ), \
+ NUMFMT_STRING( 22, SYSTEMDATE " " HOUR24 ":mm" )
+
+/** Defines builtin time formats INDEX and INDEX+1 for CJK locales.
+ @param INDEX First number format index.
+ @param HOURFORMAT Hour format.
+ @param HOUR Hour symbol.
+ @param MINUTE Minute symbol.
+ @param SECOND Second symbol. */
+#define NUMFMT_TIME_CJK( INDEX, HOURFORMAT, HOUR, MINUTE, SECOND ) \
+ NUMFMT_STRING( INDEX + 0, HOURFORMAT "\"" HOUR "\"mm\"" MINUTE "\"" ), \
+ NUMFMT_STRING( INDEX + 1, HOURFORMAT "\"" HOUR "\"mm\"" MINUTE "\"ss\"" SECOND "\"" )
+
+/** Defines builtin time formats 32...35 for CJK locales.
+ @param HOUR12 Hour format for 12-hour AM/PM formats (formats 34 and 35).
+ @param HOUR24 Hour format for 24-hour formats (formats 32 and 33).
+ @param HOUR Hour symbol.
+ @param MINUTE Minute symbol.
+ @param SECOND Second symbol. */
+#define NUMFMT_ALLTIMES_CJK( HOUR12, HOUR24, HOUR, MINUTE, SECOND ) \
+ NUMFMT_TIME_CJK( 32, HOUR24, HOUR, MINUTE, SECOND ), \
+ NUMFMT_TIME_CJK( 34, "AM/PM" HOUR12, HOUR, MINUTE, SECOND )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "symbol, [minus], number".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0;" MODIF SYMBOL SPACE "-#,##0" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0;" "[RED]" MODIF SYMBOL SPACE "-#,##0" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00;" MODIF SYMBOL SPACE "-#,##0.00" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00;" "[RED]" MODIF SYMBOL SPACE "-#,##0.00" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "symbol, [minus], number".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ACCOUNTING_SYMBOL_MINUS_NUMBER( INDEX, SYMBOL, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_ " "* #,##0_ ;" "_ " "* -#,##0_ ;" "_ " "* \"-\"_ ;" "_ @_ " ), \
+ NUMFMT_STRING( INDEX + 1, "_ " SYMBOL SPACE "* #,##0_ ;" "_ " SYMBOL SPACE "* -#,##0_ ;" "_ " SYMBOL SPACE "* \"-\"_ ;" "_ @_ " ), \
+ NUMFMT_STRING( INDEX + 2, "_ " "* #,##0.00_ ;" "_ " "* -#,##0.00_ ;" "_ " "* \"-\"?\?_ ;" "_ @_ " ), \
+ NUMFMT_STRING( INDEX + 3, "_ " SYMBOL SPACE "* #,##0.00_ ;" "_ " SYMBOL SPACE "* -#,##0.00_ ;" "_ " SYMBOL SPACE "* \"-\"?\?_ ;" "_ @_ " )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "symbol, [minus], number".
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( SYMBOL, SPACE ) \
+ NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_SYMBOL_MINUS_NUMBER( 37, "", "", "" ), \
+ NUMFMT_ACCOUNTING_SYMBOL_MINUS_NUMBER( 41, SYMBOL, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "symbol, number, [minus]".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0_-;" MODIF SYMBOL SPACE "#,##0-" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0_-;" "[RED]" MODIF SYMBOL SPACE "#,##0-" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00_-;" MODIF SYMBOL SPACE "#,##0.00-" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00_-;" "[RED]" MODIF SYMBOL SPACE "#,##0.00-" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "symbol, number, [minus]".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ACCOUNTING_SYMBOL_NUMBER_MINUS( INDEX, SYMBOL, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_-" "* #,##0_-;" "_-" "* #,##0-;" "_-" "* \"-\"_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 1, "_-" SYMBOL SPACE "* #,##0_-;" "_-" SYMBOL SPACE "* #,##0-;" "_-" SYMBOL SPACE "* \"-\"_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 2, "_-" "* #,##0.00_-;" "_-" "* #,##0.00-;" "_-" "* \"-\"?\?_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 3, "_-" SYMBOL SPACE "* #,##0.00_-;" "_-" SYMBOL SPACE "* #,##0.00-;" "_-" SYMBOL SPACE "* \"-\"?\?_-;" "_-@_-" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "symbol, number, [minus]".
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( SYMBOL, SPACE ) \
+ NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_SYMBOL_NUMBER_MINUS( 37, "", "", "" ), \
+ NUMFMT_ACCOUNTING_SYMBOL_NUMBER_MINUS( 41, SYMBOL, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "number, symbol, [minus]".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between number and currency symbol.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL "_-;" MODIF "#,##0" SPACE SYMBOL "-" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL "_-;" "[RED]" MODIF "#,##0" SPACE SYMBOL "-" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL "_-;" MODIF "#,##0.00" SPACE SYMBOL "-" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL "_-;" "[RED]" MODIF "#,##0.00" SPACE SYMBOL "-" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "number, symbol, [minus]".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ACCOUNTING_NUMBER_SYMBOL_MINUS( INDEX, SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_-* #,##0" SPACE BLINDS "_-;_-* #,##0" SPACE BLINDS "-;_-* \"-\"" SPACE BLINDS "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 1, "_-* #,##0" SPACE SYMBOL "_-;_-* #,##0" SPACE SYMBOL "-;_-* \"-\"" SPACE SYMBOL "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 2, "_-* #,##0.00" SPACE BLINDS "_-;_-* #,##0.00" SPACE BLINDS "-;_-* \"-\"?\?" SPACE BLINDS "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 3, "_-* #,##0.00" SPACE SYMBOL "_-;_-* #,##0.00" SPACE SYMBOL "-;_-* \"-\"?\?" SPACE SYMBOL "_-;_-@_-" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "number, symbol, [minus]".
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ALLCURRENCIES_NUMBER_SYMBOL_MINUS( SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_NUMBER_SYMBOL_MINUS( 37, BLINDS, SPACE, "" ), \
+ NUMFMT_ACCOUNTING_NUMBER_SYMBOL_MINUS( 41, SYMBOL, BLINDS, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "[minus], symbol, number".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0;" MODIF "-" SYMBOL SPACE "#,##0" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0;" "[RED]" MODIF "-" SYMBOL SPACE "#,##0" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00;" MODIF "-" SYMBOL SPACE "#,##0.00" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00;" "[RED]" MODIF "-" SYMBOL SPACE "#,##0.00" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following order:
+ "[minus], symbol, number".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( INDEX, SYMBOL, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_-" "* #,##0_-;" "-" "* #,##0_-;" "_-" "* \"-\"_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 1, "_-" SYMBOL SPACE "* #,##0_-;" "-" SYMBOL SPACE "* #,##0_-;" "_-" SYMBOL SPACE "* \"-\"_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 2, "_-" "* #,##0.00_-;" "-" "* #,##0.00_-;" "_-" "* \"-\"?\?_-;" "_-@_-" ), \
+ NUMFMT_STRING( INDEX + 3, "_-" SYMBOL SPACE "* #,##0.00_-;" "-" SYMBOL SPACE "* #,##0.00_-;" "_-" SYMBOL SPACE "* \"-\"?\?_-;" "_-@_-" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following order:
+ "[minus], symbol, number".
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( SYMBOL, SPACE ) \
+ NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 37, "", "", "" ), \
+ NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( 41, SYMBOL, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "[minus], number, symbol".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between number and currency symbol.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL ";" MODIF "-#,##0" SPACE SYMBOL ), \
+ NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL ";" "[RED]" MODIF "-#,##0" SPACE SYMBOL ), \
+ NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL ";" MODIF "-#,##0.00" SPACE SYMBOL ), \
+ NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL ";" "[RED]" MODIF "-#,##0.00" SPACE SYMBOL )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "[minus], number, symbol".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ACCOUNTING_MINUS_NUMBER_SYMBOL( INDEX, SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_-* #,##0" SPACE BLINDS "_-;-* #,##0" SPACE BLINDS "_-;_-* \"-\"" SPACE BLINDS "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 1, "_-* #,##0" SPACE SYMBOL "_-;-* #,##0" SPACE SYMBOL "_-;_-* \"-\"" SPACE SYMBOL "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 2, "_-* #,##0.00" SPACE BLINDS "_-;-* #,##0.00" SPACE BLINDS "_-;_-* \"-\"?\?" SPACE BLINDS "_-;_-@_-" ), \
+ NUMFMT_STRING( INDEX + 3, "_-* #,##0.00" SPACE SYMBOL "_-;-* #,##0.00" SPACE SYMBOL "_-;_-* \"-\"?\?" SPACE SYMBOL "_-;_-@_-" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "[minus], number, symbol".
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_MINUS_NUMBER_SYMBOL( 37, BLINDS, SPACE, "" ), \
+ NUMFMT_ACCOUNTING_MINUS_NUMBER_SYMBOL( 41, SYMBOL, BLINDS, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "[opening parenthesis], symbol, number, [closing parenthesis].".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF SYMBOL SPACE "#,##0_);" MODIF "(" SYMBOL SPACE "#,##0)" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF SYMBOL SPACE "#,##0_);" "[RED]" MODIF "(" SYMBOL SPACE "#,##0)" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF SYMBOL SPACE "#,##0.00_);" MODIF "(" SYMBOL SPACE "#,##0.00)" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF SYMBOL SPACE "#,##0.00_);" "[RED]" MODIF "(" SYMBOL SPACE "#,##0.00)" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "[opening parenthesis], symbol, number, [closing parenthesis].".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ACCOUNTING_OPEN_SYMBOL_NUMBER_CLOSE( INDEX, SYMBOL, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_(" "* #,##0_);" "_(" "* (#,##0);" "_(" "* \"-\"_);" "_(@_)" ), \
+ NUMFMT_STRING( INDEX + 1, "_(" SYMBOL SPACE "* #,##0_);" "_(" SYMBOL SPACE "* (#,##0);" "_(" SYMBOL SPACE "* \"-\"_);" "_(@_)" ), \
+ NUMFMT_STRING( INDEX + 2, "_(" "* #,##0.00_);" "_(" "* (#,##0.00);" "_(" "* \"-\"?\?_);" "_(@_)" ), \
+ NUMFMT_STRING( INDEX + 3, "_(" SYMBOL SPACE "* #,##0.00_);" "_(" SYMBOL SPACE "* (#,##0.00);" "_(" SYMBOL SPACE "* \"-\"?\?_);" "_(@_)" )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "[opening parenthesis], symbol, number, [closing parenthesis].".
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between currency symbol and number. */
+#define NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( SYMBOL, SPACE ) \
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 37, "", "", "" ), \
+ NUMFMT_ACCOUNTING_OPEN_SYMBOL_NUMBER_CLOSE( 41, SYMBOL, SPACE )
+
+/** Defines builtin currency formats INDEX...INDEX+3 in the following format:
+ "[opening parenthesis], number, symbol, [closing parenthesis].".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param SPACE Space character(s) between number and currency symbol.
+ @param MODIF Leading modifier for each portion (e.g. "t" for Thai formats). */
+#define NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( INDEX, SYMBOL, SPACE, MODIF ) \
+ NUMFMT_STRING( INDEX + 0, MODIF "#,##0" SPACE SYMBOL "_);" MODIF "(#,##0" SPACE SYMBOL ")" ), \
+ NUMFMT_STRING( INDEX + 1, MODIF "#,##0" SPACE SYMBOL "_);" "[RED]" MODIF "(#,##0" SPACE SYMBOL ")" ), \
+ NUMFMT_STRING( INDEX + 2, MODIF "#,##0.00" SPACE SYMBOL "_);" MODIF "(#,##0.00" SPACE SYMBOL ")" ), \
+ NUMFMT_STRING( INDEX + 3, MODIF "#,##0.00" SPACE SYMBOL "_);" "[RED]" MODIF "(#,##0.00" SPACE SYMBOL ")" )
+
+/** Defines builtin accounting formats INDEX...INDEX+3 in the following format:
+ "[opening parenthesis], number, symbol, [closing parenthesis].".
+ @param INDEX First number format index.
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ACCOUNTING_OPEN_NUMBER_SYMBOL_CLOSE( INDEX, SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_STRING( INDEX + 0, "_ * #,##0_)" SPACE BLINDS "_ ;_ * (#,##0)" SPACE BLINDS "_ ;_ * \"-\"_)" SPACE BLINDS "_ ;_ @_ " ), \
+ NUMFMT_STRING( INDEX + 1, "_ * #,##0_)" SPACE SYMBOL "_ ;_ * (#,##0)" SPACE SYMBOL "_ ;_ * \"-\"_)" SPACE SYMBOL "_ ;_ @_ " ), \
+ NUMFMT_STRING( INDEX + 2, "_ * #,##0.00_)" SPACE BLINDS "_ ;_ * (#,##0.00)" SPACE BLINDS "_ ;_ * \"-\"?\?_)" SPACE BLINDS "_ ;_ @_ " ), \
+ NUMFMT_STRING( INDEX + 3, "_ * #,##0.00_)" SPACE SYMBOL "_ ;_ * (#,##0.00)" SPACE SYMBOL "_ ;_ * \"-\"?\?_)" SPACE SYMBOL "_ ;_ @_ " )
+
+/** Defines builtin currency formats 5...8 (with currency symbol), 37...40
+ (blind currency symbol), and 41...44 (accounting), in the following format:
+ "[opening parenthesis], number, symbol, [closing parenthesis].".
+ @param SYMBOL Currency symbol.
+ @param BLINDS Blind currency symbol.
+ @param SPACE Space character(s) between number and currency symbol. */
+#define NUMFMT_ALLCURRENCIES_OPEN_NUMBER_SYMBOL_CLOSE( SYMBOL, BLINDS, SPACE ) \
+ NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( 5, SYMBOL, SPACE, "" ), \
+ NUMFMT_CURRENCY_OPEN_NUMBER_SYMBOL_CLOSE( 37, BLINDS, SPACE, "" ), \
+ NUMFMT_ACCOUNTING_OPEN_NUMBER_SYMBOL_CLOSE( 41, SYMBOL, BLINDS, SPACE )
+
+// currency unit characters
+#define UTF8_BAHT "\340\270\277"
+#define UTF8_COLON "\342\202\241"
+#define UTF8_CURR_AR_AE "\330\257.\330\245."
+#define UTF8_CURR_AR_BH "\330\257.\330\250."
+#define UTF8_CURR_AR_DZ "\330\257.\330\254."
+#define UTF8_CURR_AR_EG "\330\254.\331\205."
+#define UTF8_CURR_AR_IQ "\330\257.\330\271."
+#define UTF8_CURR_AR_JO "\330\257.\330\247."
+#define UTF8_CURR_AR_KW "\330\257.\331\203."
+#define UTF8_CURR_AR_LB "\331\204.\331\204."
+#define UTF8_CURR_AR_LY "\330\257.\331\204."
+#define UTF8_CURR_AR_MA "\330\257.\331\205."
+#define UTF8_CURR_AR_OM "\330\261.\330\271."
+#define UTF8_CURR_AR_QA "\330\261.\331\202."
+#define UTF8_CURR_AR_SA "\330\261.\330\263."
+#define UTF8_CURR_AR_SY "\331\204.\330\263."
+#define UTF8_CURR_AR_TN "\330\257.\330\252."
+#define UTF8_CURR_AR_YE "\330\261.\331\212."
+#define UTF8_CURR_BN_IN "\340\246\237\340\246\276"
+#define UTF8_CURR_FA_IR "\330\261\331\212\330\247\331\204"
+#define UTF8_CURR_GU_IN "\340\252\260\340\253\202"
+#define UTF8_CURR_HI_IN "\340\244\260\340\245\201"
+#define UTF8_CURR_KN_IN "\340\262\260\340\263\202"
+#define UTF8_CURR_ML_IN "\340\264\225"
+#define UTF8_CURR_PA_IN "\340\250\260\340\251\201"
+#define UTF8_CURR_TA_IN "\340\256\260\340\257\202"
+#define UTF8_CURR_TE_IN "\340\260\260\340\261\202"
+#define UTF8_DONG "\342\202\253"
+#define UTF8_EURO "\342\202\254"
+#define UTF8_POUND_GB "\302\243"
+#define UTF8_RUFIYAA "\336\203"
+#define UTF8_SHEQEL "\342\202\252"
+#define UTF8_TUGRUG "\342\202\256"
+#define UTF8_WON "\342\202\251"
+#define UTF8_YEN_CN "\357\277\245"
+#define UTF8_YEN_JP "\302\245"
+
+// Unicode characters for currency units
+#define UTF8_CCARON_LC "\304\215"
+#define UTF8_LSTROKE_LC "\305\202"
+// Armenian
+#define UTF8_HY_DA_LC "\325\244"
+#define UTF8_HY_REH_LC "\326\200"
+// Cyrillic
+#define UTF8_CYR_G_LC "\320\263"
+#define UTF8_CYR_L_LC "\320\273"
+#define UTF8_CYR_M_LC "\320\274"
+#define UTF8_CYR_N_LC "\320\275"
+#define UTF8_CYR_O_LC "\320\276"
+#define UTF8_CYR_R_LC "\321\200"
+#define UTF8_CYR_S_LC "\321\201"
+#define UTF8_CYR_W_LC "\320\262"
+
+// Japanese/Chinese date/time characters
+#define UTF8_CJ_YEAR "\345\271\264"
+#define UTF8_CJ_MON "\346\234\210"
+#define UTF8_CJ_DAY "\346\227\245"
+#define UTF8_CJ_HOUR "\346\231\202"
+#define UTF8_CJ_MIN "\345\210\206"
+#define UTF8_CJ_SEC "\347\247\222"
+
+// Chinese Simplified date/time characters
+#define UTF8_CS_YEAR "\345\271\264"
+#define UTF8_CS_MON "\346\234\210"
+#define UTF8_CS_DAY "\346\227\245"
+#define UTF8_CS_HOUR "\346\227\266"
+#define UTF8_CS_MIN "\345\210\206"
+#define UTF8_CS_SEC "\347\247\222"
+
+// Korean date/time characters
+#define UTF8_KO_YEAR "\353\205\204"
+#define UTF8_KO_MON "\354\233\224"
+#define UTF8_KO_DAY "\354\235\274"
+#define UTF8_KO_HOUR "\354\213\234"
+#define UTF8_KO_MIN "\353\266\204"
+#define UTF8_KO_SEC "\354\264\210"
+
+/** Default number format table. Last parent of all other tables, used for unknown locales. */
+const BuiltinFormat spBuiltinFormats_BASE[] =
+{
+ // 0..13 numeric and currency formats
+ NUMFMT_PREDEF( 0, NUMBER_STANDARD ), // General
+ NUMFMT_PREDEF( 1, NUMBER_INT ), // 0
+ NUMFMT_PREDEF( 2, NUMBER_DEC2 ), // 0.00
+ NUMFMT_PREDEF( 3, NUMBER_1000INT ), // #,##0
+ NUMFMT_PREDEF( 4, NUMBER_1000DEC2 ), // #,##0.00
+ NUMFMT_PREDEF( 5, CURRENCY_1000INT ), // #,##0[symbol]
+ NUMFMT_PREDEF( 6, CURRENCY_1000INT_RED ), // #,##0[symbol];[RED]-#,##0[symbol]
+ NUMFMT_PREDEF( 7, CURRENCY_1000DEC2 ), // #,##0.00[symbol]
+ NUMFMT_PREDEF( 8, CURRENCY_1000DEC2_RED ), // #,##0.00[symbol];[RED]-#,##0.00[symbol]
+ NUMFMT_PREDEF( 9, PERCENT_INT ), // 0%
+ NUMFMT_PREDEF( 10, PERCENT_DEC2 ), // 0.00%
+ NUMFMT_PREDEF( 11, SCIENTIFIC_000E00 ), // 0.00E+00
+ NUMFMT_PREDEF( 12, FRACTION_1 ), // # ?/?
+ NUMFMT_PREDEF( 13, FRACTION_2 ), // # ??/??
+
+ // 14...22 date and time formats
+ NUMFMT_PREDEF( 14, DATE_SYS_DDMMYYYY ),
+ NUMFMT_PREDEF( 15, DATE_SYS_DMMMYY ),
+ NUMFMT_PREDEF( 16, DATE_SYS_DDMMM ),
+ NUMFMT_PREDEF( 17, DATE_SYS_MMYY ),
+ NUMFMT_PREDEF( 18, TIME_HHMMAMPM ),
+ NUMFMT_PREDEF( 19, TIME_HHMMSSAMPM ),
+ NUMFMT_PREDEF( 20, TIME_HHMM ),
+ NUMFMT_PREDEF( 21, TIME_HHMMSS ),
+ NUMFMT_PREDEF( 22, DATETIME_SYSTEM_SHORT_HHMM ),
+
+ // 23...36 international formats
+ NUMFMT_REUSE( 23, 0 ),
+ NUMFMT_REUSE( 24, 0 ),
+ NUMFMT_REUSE( 25, 0 ),
+ NUMFMT_REUSE( 26, 0 ),
+ NUMFMT_REUSE( 27, 14 ),
+ NUMFMT_REUSE( 28, 14 ),
+ NUMFMT_REUSE( 29, 14 ),
+ NUMFMT_REUSE( 30, 14 ),
+ NUMFMT_REUSE( 31, 14 ),
+ NUMFMT_REUSE( 32, 21 ),
+ NUMFMT_REUSE( 33, 21 ),
+ NUMFMT_REUSE( 34, 21 ),
+ NUMFMT_REUSE( 35, 21 ),
+ NUMFMT_REUSE( 36, 14 ),
+
+ // 37...44 accounting formats, defaults without currency symbol here
+ NUMFMT_CURRENCY_MINUS_SYMBOL_NUMBER( 37, "", "", "" ),
+ NUMFMT_ACCOUNTING_MINUS_SYMBOL_NUMBER( 41, "", "" ),
+
+ // 45...49 more special formats
+ NUMFMT_STRING( 45, "mm:ss" ),
+ NUMFMT_STRING( 46, "[h]:mm:ss" ),
+ NUMFMT_STRING( 47, "mm:ss.0" ),
+ NUMFMT_STRING( 48, "##0.0E+0" ),
+ NUMFMT_PREDEF( 49, TEXT ),
+
+ // 50...81 international formats
+ NUMFMT_REUSE( 50, 14 ),
+ NUMFMT_REUSE( 51, 14 ),
+ NUMFMT_REUSE( 52, 14 ),
+ NUMFMT_REUSE( 53, 14 ),
+ NUMFMT_REUSE( 54, 14 ),
+ NUMFMT_REUSE( 55, 14 ),
+ NUMFMT_REUSE( 56, 14 ),
+ NUMFMT_REUSE( 57, 14 ),
+ NUMFMT_REUSE( 58, 14 ),
+ NUMFMT_REUSE( 59, 1 ),
+ NUMFMT_REUSE( 60, 2 ),
+ NUMFMT_REUSE( 61, 3 ),
+ NUMFMT_REUSE( 62, 4 ),
+ NUMFMT_REUSE( 63, 5 ),
+ NUMFMT_REUSE( 64, 6 ),
+ NUMFMT_REUSE( 65, 7 ),
+ NUMFMT_REUSE( 66, 8 ),
+ NUMFMT_REUSE( 67, 9 ),
+ NUMFMT_REUSE( 68, 10 ),
+ NUMFMT_REUSE( 69, 12 ),
+ NUMFMT_REUSE( 70, 13 ),
+ NUMFMT_REUSE( 71, 14 ),
+ NUMFMT_REUSE( 72, 14 ),
+ NUMFMT_REUSE( 73, 15 ),
+ NUMFMT_REUSE( 74, 16 ),
+ NUMFMT_REUSE( 75, 17 ),
+ NUMFMT_REUSE( 76, 20 ),
+ NUMFMT_REUSE( 77, 21 ),
+ NUMFMT_REUSE( 78, 22 ),
+ NUMFMT_REUSE( 79, 45 ),
+ NUMFMT_REUSE( 80, 46 ),
+ NUMFMT_REUSE( 81, 47 ),
+
+ // 82...163 not used, must not occur in a file (Excel may crash)
+
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, U.A.E. */
+const BuiltinFormat spBuiltinFormats_ar_AE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_AE "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Bahrain. */
+const BuiltinFormat spBuiltinFormats_ar_BH[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_BH "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Algeria. */
+const BuiltinFormat spBuiltinFormats_ar_DZ[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_DZ "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Egypt. */
+const BuiltinFormat spBuiltinFormats_ar_EG[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_EG "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Iraq. */
+const BuiltinFormat spBuiltinFormats_ar_IQ[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_IQ "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Jordan. */
+const BuiltinFormat spBuiltinFormats_ar_JO[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_JO "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Kuwait. */
+const BuiltinFormat spBuiltinFormats_ar_KW[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_KW "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Lebanon. */
+const BuiltinFormat spBuiltinFormats_ar_LB[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_LB "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Libya. */
+const BuiltinFormat spBuiltinFormats_ar_LY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_LY "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Morocco. */
+const BuiltinFormat spBuiltinFormats_ar_MA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_MA "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Oman. */
+const BuiltinFormat spBuiltinFormats_ar_OM[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_OM "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Qatar. */
+const BuiltinFormat spBuiltinFormats_ar_QA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_QA "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Saudi Arabia. */
+const BuiltinFormat spBuiltinFormats_ar_SA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_SA "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Syria. */
+const BuiltinFormat spBuiltinFormats_ar_SY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_SY "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Tunisia. */
+const BuiltinFormat spBuiltinFormats_ar_TN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_TN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Arabic, Yemen. */
+const BuiltinFormat spBuiltinFormats_ar_YE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_AR_YE "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Belarusian, Belarus. */
+const BuiltinFormat spBuiltinFormats_be_BY[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Bulgarian, Bulgaria. */
+const BuiltinFormat spBuiltinFormats_bg_BG[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.M.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_L_LC UTF8_CYR_W_LC "\"", "_" UTF8_CYR_L_LC "_" UTF8_CYR_W_LC, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Bengali, India. */
+const BuiltinFormat spBuiltinFormats_bn_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_BN_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Czech, Czech Republic. */
+const BuiltinFormat spBuiltinFormats_cs_CZ[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"K" UTF8_CCARON_LC "\"", "_K_" UTF8_CCARON_LC, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Danish, Denmark. */
+const BuiltinFormat spBuiltinFormats_da_DK[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Austria. */
+const BuiltinFormat spBuiltinFormats_de_AT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Switzerland. */
+const BuiltinFormat spBuiltinFormats_de_CH[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Germany. */
+const BuiltinFormat spBuiltinFormats_de_DE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Liechtenstein. */
+const BuiltinFormat spBuiltinFormats_de_LI[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ". ", "MMM", " ", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"CHF\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** German, Luxembourg. */
+const BuiltinFormat spBuiltinFormats_de_LU[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Divehi, Maldives. */
+const BuiltinFormat spBuiltinFormats_div_MV[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_NUMBER_SYMBOL_MINUS( "\"" UTF8_RUFIYAA ".\"", "_" UTF8_RUFIYAA "_.", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Greek, Greece. */
+const BuiltinFormat spBuiltinFormats_el_GR[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Australia. */
+const BuiltinFormat spBuiltinFormats_en_AU[] =
+{
+ NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Belize. */
+const BuiltinFormat spBuiltinFormats_en_BZ[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"BZ$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Canada. */
+const BuiltinFormat spBuiltinFormats_en_CA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Caribbean. */
+const BuiltinFormat spBuiltinFormats_en_CB[] =
+{
+ NUMFMT_ALLDATETIMES( "MM/DD/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, United Kingdom. */
+const BuiltinFormat spBuiltinFormats_en_GB[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_POUND_GB, "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Ireland. */
+const BuiltinFormat spBuiltinFormats_en_IE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Jamaica. */
+const BuiltinFormat spBuiltinFormats_en_JM[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"J$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, New Zealand. */
+const BuiltinFormat spBuiltinFormats_en_NZ[] =
+{
+ NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Philippines. */
+const BuiltinFormat spBuiltinFormats_en_PH[] =
+{
+ NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Php\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Trinidad and Tobago. */
+const BuiltinFormat spBuiltinFormats_en_TT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"TT$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, USA. */
+const BuiltinFormat spBuiltinFormats_en_US[] =
+{
+ NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, South Africa. */
+const BuiltinFormat spBuiltinFormats_en_ZA[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\\R", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** English, Zimbabwe. */
+const BuiltinFormat spBuiltinFormats_en_ZW[] =
+{
+ NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Z$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Argentina. */
+const BuiltinFormat spBuiltinFormats_es_AR[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Bolivia. */
+const BuiltinFormat spBuiltinFormats_es_BO[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"$b\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Chile. */
+const BuiltinFormat spBuiltinFormats_es_CL[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Colombia. */
+const BuiltinFormat spBuiltinFormats_es_CO[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Costa Rica. */
+const BuiltinFormat spBuiltinFormats_es_CR[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( UTF8_COLON, "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Dominican Republic. */
+const BuiltinFormat spBuiltinFormats_es_DO[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"RD$\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Ecuador. */
+const BuiltinFormat spBuiltinFormats_es_EC[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Spain. */
+const BuiltinFormat spBuiltinFormats_es_ES[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Guatemala. */
+const BuiltinFormat spBuiltinFormats_es_GT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\Q", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Honduras. */
+const BuiltinFormat spBuiltinFormats_es_HN[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"L.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Mexico. */
+const BuiltinFormat spBuiltinFormats_es_MX[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Nicaragua. */
+const BuiltinFormat spBuiltinFormats_es_NI[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"C$\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Panama. */
+const BuiltinFormat spBuiltinFormats_es_PA[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"B/.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Peru. */
+const BuiltinFormat spBuiltinFormats_es_PE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"S/.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Puerto Rico. */
+const BuiltinFormat spBuiltinFormats_es_PR[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Paraguay. */
+const BuiltinFormat spBuiltinFormats_es_PY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Gs\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, El Salvador. */
+const BuiltinFormat spBuiltinFormats_es_SV[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "DD\\/MM\\/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Uruguay. */
+const BuiltinFormat spBuiltinFormats_es_UY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"$U\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Spanish, Venezuela. */
+const BuiltinFormat spBuiltinFormats_es_VE[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "Bs", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Estonian, Estonia. */
+const BuiltinFormat spBuiltinFormats_et_EE[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "D.MM.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr\"", "_k_r", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Farsi, Iran. */
+const BuiltinFormat spBuiltinFormats_fa_IR[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"" UTF8_CURR_FA_IR "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Finnish, Finland. */
+const BuiltinFormat spBuiltinFormats_fi_FI[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_STRING( 9, "0\\ %" ),
+ NUMFMT_STRING( 10, "0.00\\ %" ),
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Faroese, Faroe Islands. */
+const BuiltinFormat spBuiltinFormats_fo_FO[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Belgium. */
+const BuiltinFormat spBuiltinFormats_fr_BE[] =
+{
+ NUMFMT_ALLDATETIMES( "D/MM/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Canada. */
+const BuiltinFormat spBuiltinFormats_fr_CA[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_NUMBER_SYMBOL_CLOSE( "$", "_$", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Switzerland. */
+const BuiltinFormat spBuiltinFormats_fr_CH[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, France. */
+const BuiltinFormat spBuiltinFormats_fr_FR[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Luxembourg. */
+const BuiltinFormat spBuiltinFormats_fr_LU[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** French, Monaco. */
+const BuiltinFormat spBuiltinFormats_fr_MC[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Galizian, Spain. */
+const BuiltinFormat spBuiltinFormats_gl_ES[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Gujarati, India. */
+const BuiltinFormat spBuiltinFormats_gu_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_GU_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Hebrew, Israel. */
+const BuiltinFormat spBuiltinFormats_he_IL[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( UTF8_SHEQEL, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Hindi, India. */
+const BuiltinFormat spBuiltinFormats_hi_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_HI_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Croatian, Bosnia and Herzegowina. */
+const BuiltinFormat spBuiltinFormats_hr_BA[] =
+{
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"KM\"", "_K_M", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Croatian, Croatia. */
+const BuiltinFormat spBuiltinFormats_hr_HR[] =
+{
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kn\"", "_k_n", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Hungarian, Hungary. */
+const BuiltinFormat spBuiltinFormats_hu_HU[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ // MMM is rendered differently in Calc and Excel (see #i41488#)
+ NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Ft\"", "_F_t", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Armenian, Armenia. */
+const BuiltinFormat spBuiltinFormats_hy_AM[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_HY_DA_LC UTF8_HY_REH_LC ".\"", "_" UTF8_HY_DA_LC "_" UTF8_HY_REH_LC "_.", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Indonesian, Indonesia. */
+const BuiltinFormat spBuiltinFormats_id_ID[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"Rp\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Icelandic, Iceland. */
+const BuiltinFormat spBuiltinFormats_is_IS[] =
+{
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr.\"", "_k_r_.", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Italian, Switzerland. */
+const BuiltinFormat spBuiltinFormats_it_CH[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"SFr.\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Italian, Italy. */
+const BuiltinFormat spBuiltinFormats_it_IT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Georgian, Georgia. */
+const BuiltinFormat spBuiltinFormats_ka_GE[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Lari\"", "_L_a_r_i", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Kazakh, Kazakhstan. */
+const BuiltinFormat spBuiltinFormats_kk_KZ[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\\T", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Kannada, India. */
+const BuiltinFormat spBuiltinFormats_kn_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_KN_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Kyrgyz, Kyrgyzstan. */
+const BuiltinFormat spBuiltinFormats_ky_KG[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_S_LC UTF8_CYR_O_LC UTF8_CYR_M_LC "\"", "_" UTF8_CYR_S_LC "_" UTF8_CYR_O_LC "_" UTF8_CYR_M_LC, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Lithuanian, Lithuania. */
+const BuiltinFormat spBuiltinFormats_lt_LT[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Lt\"", "_L_t", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Latvian, Latvia. */
+const BuiltinFormat spBuiltinFormats_lv_LV[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "YYYY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"Ls\"", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Malayalam, India. */
+const BuiltinFormat spBuiltinFormats_ml_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_ML_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Mongolian, Mongolia. */
+const BuiltinFormat spBuiltinFormats_mn_MN[] =
+{
+ NUMFMT_ALLDATETIMES( "YY.MM.DD", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_TUGRUG, "_" UTF8_TUGRUG, "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Malay, Brunei Darussalam. */
+const BuiltinFormat spBuiltinFormats_ms_BN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Malay, Malaysia. */
+const BuiltinFormat spBuiltinFormats_ms_MY[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\R", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Maltese, Malta. */
+const BuiltinFormat spBuiltinFormats_mt_MT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "\"Lm\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Dutch, Belgium. */
+const BuiltinFormat spBuiltinFormats_nl_BE[] =
+{
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_ALLDATETIMES( "D\\/MM\\/YYYY", "D", "\\/", "MMM", "\\/", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Dutch, Netherlands. */
+const BuiltinFormat spBuiltinFormats_nl_NL[] =
+{
+ NUMFMT_ALLDATETIMES( "D-M-YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Norwegian (Bokmal and Nynorsk), Norway. */
+const BuiltinFormat spBuiltinFormats_no_NO[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"kr\"", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Punjabi, India. */
+const BuiltinFormat spBuiltinFormats_pa_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_PA_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Polish, Poland. */
+const BuiltinFormat spBuiltinFormats_pl_PL[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ // MMM is rendered differently in Calc and Excel (see #i72300#)
+ NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"z" UTF8_LSTROKE_LC "\"", "_z_" UTF8_LSTROKE_LC, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Portuguese, Brazil. */
+const BuiltinFormat spBuiltinFormats_pt_BR[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "/", "MMM", "/", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"R$\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Portuguese, Portugal. */
+const BuiltinFormat spBuiltinFormats_pt_PT[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Romanian, Romania. */
+const BuiltinFormat spBuiltinFormats_ro_RO[] =
+{
+ // space character is group separator, literal spaces must be quoted (but see #i75367#)
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"lei\"", "_l_e_i", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Russian, Russian Federation. */
+const BuiltinFormat spBuiltinFormats_ru_RU[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Slovak, Slovakia. */
+const BuiltinFormat spBuiltinFormats_sk_SK[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"Sk\"", "_S_k", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Slovenian, Slovenia. */
+const BuiltinFormat spBuiltinFormats_sl_SI[] =
+{
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"SIT\"", "_S_I_T", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Swedish, Finland. */
+const BuiltinFormat spBuiltinFormats_sv_FI[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_STRING( 9, "0\\ %" ),
+ NUMFMT_STRING( 10, "0.00\\ %" ),
+ NUMFMT_ALLDATETIMES( "D.M.YYYY", "D", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_EURO, "_" UTF8_EURO, "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Swedish, Sweden. */
+const BuiltinFormat spBuiltinFormats_sv_SE[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"kr\"", "_k_r", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Swahili, Tanzania. */
+const BuiltinFormat spBuiltinFormats_sw_TZ[] =
+{
+ NUMFMT_ALLDATETIMES( "M/D/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\S", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Tamil, India. */
+const BuiltinFormat spBuiltinFormats_ta_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YYYY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_TA_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Telugu, India. */
+const BuiltinFormat spBuiltinFormats_te_IN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD-MM-YY", "DD", "-", "MMM", "-", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( "\"" UTF8_CURR_TE_IN "\"", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Thai, Thailand. */
+const BuiltinFormat spBuiltinFormats_th_TH[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_BAHT, "" ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 63, UTF8_BAHT, "", "t" ),
+ NUMFMT_STRING( 59, "t0" ),
+ NUMFMT_STRING( 60, "t0.00" ),
+ NUMFMT_STRING( 61, "t#,##0" ),
+ NUMFMT_STRING( 62, "t#,##0.00" ),
+ NUMFMT_STRING( 67, "t0%" ),
+ NUMFMT_STRING( 68, "t0.00%" ),
+ NUMFMT_STRING( 69, "t# ?/?" ),
+ NUMFMT_STRING( 70, "t# ?\?/?\?" ),
+ NUMFMT_STRING( 71, "tD/M/EE" ),
+ NUMFMT_STRING( 72, "tD-MMM-E" ),
+ NUMFMT_STRING( 73, "tD-MMM" ),
+ NUMFMT_STRING( 74, "tMMM-E" ),
+ NUMFMT_STRING( 75, "th:mm" ),
+ NUMFMT_STRING( 76, "th:mm:ss" ),
+ NUMFMT_STRING( 77, "tD/M/EE h:mm" ),
+ NUMFMT_STRING( 78, "tmm:ss" ),
+ NUMFMT_STRING( 79, "t[h]:mm:ss" ),
+ NUMFMT_STRING( 80, "tmm:ss.0" ),
+ NUMFMT_STRING( 81, "D/M/E" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Turkish, Turkey. */
+const BuiltinFormat spBuiltinFormats_tr_TR[] =
+{
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"TL\"", "_T_L", " " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Tatar, Russian Federation. */
+const BuiltinFormat spBuiltinFormats_tt_RU[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_R_LC ".\"", "_" UTF8_CYR_R_LC "_.", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Ukrainian, Ukraine. */
+const BuiltinFormat spBuiltinFormats_uk_UA[] =
+{
+ // space character is group separator, literal spaces must be quoted
+ NUMFMT_ALLDATETIMES( "DD.MM.YYYY", "DD", ".", "MMM", ".", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( "\"" UTF8_CYR_G_LC UTF8_CYR_R_LC UTF8_CYR_N_LC ".\"", "_" UTF8_CYR_G_LC "_" UTF8_CYR_R_LC "_" UTF8_CYR_N_LC "_.", "\\ " ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Urdu, Pakistan. */
+const BuiltinFormat spBuiltinFormats_ur_PK[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_NUMBER_MINUS( "\"Rs\"", "" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Vietnamese, Viet Nam. */
+const BuiltinFormat spBuiltinFormats_vi_VN[] =
+{
+ NUMFMT_ALLDATETIMES( "DD/MM/YYYY", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_NUMBER_SYMBOL( UTF8_DONG, "_" UTF8_DONG, " " ),
+ NUMFMT_ENDTABLE()
+};
+
+// CJK ------------------------------------------------------------------------
+
+/** Base table for CJK locales. */
+const BuiltinFormat spBuiltinFormats_CJK[] =
+{
+ NUMFMT_REUSE( 29, 28 ),
+ NUMFMT_REUSE( 36, 27 ),
+ NUMFMT_REUSE( 50, 27 ),
+ NUMFMT_REUSE( 51, 28 ),
+ NUMFMT_REUSE( 52, 34 ),
+ NUMFMT_REUSE( 53, 35 ),
+ NUMFMT_REUSE( 54, 28 ),
+ NUMFMT_REUSE( 55, 34 ),
+ NUMFMT_REUSE( 56, 35 ),
+ NUMFMT_REUSE( 57, 27 ),
+ NUMFMT_REUSE( 58, 28 ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Japanese, Japan. */
+const BuiltinFormat spBuiltinFormats_ja_JP[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY/MM/DD", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_YEN_JP, "" ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ),
+ NUMFMT_STRING( 27, "[$-411]GE.MM.DD" ),
+ NUMFMT_STRING( 28, "[$-411]GGGE\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_STRING( 30, "MM/DD/YY" ),
+ NUMFMT_STRING( 31, "YYYY\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_TIME_CJK( 32, "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ),
+ NUMFMT_STRING( 34, "YYYY\"" UTF8_CJ_YEAR "\"MM\"" UTF8_CJ_MON "\"" ),
+ NUMFMT_STRING( 35, "MM\"" UTF8_CJ_MON "\"DD\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Korean, South Korea. */
+const BuiltinFormat spBuiltinFormats_ko_KR[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY-MM-DD", "DD", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( UTF8_WON, "" ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ),
+ NUMFMT_STRING( 27, "YYYY" UTF8_CJ_YEAR " MM" UTF8_CJ_MON " DD" UTF8_CJ_DAY ),
+ NUMFMT_STRING( 28, "MM-DD" ),
+ NUMFMT_STRING( 30, "MM-DD-YY" ),
+ NUMFMT_STRING( 31, "YYYY" UTF8_KO_YEAR " MM" UTF8_KO_MON " DD" UTF8_KO_DAY ),
+ NUMFMT_TIME_CJK( 32, "h", UTF8_KO_HOUR, UTF8_KO_MIN, UTF8_KO_SEC ),
+ // slashes must be quoted to prevent conversion to minus
+ NUMFMT_STRING( 34, "YYYY\\/MM\\/DD" ),
+ NUMFMT_REUSE( 35, 14 ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, China. */
+const BuiltinFormat spBuiltinFormats_zh_CN[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY-M-D", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_SYMBOL_MINUS_NUMBER( UTF8_YEN_CN, "" ),
+ NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CS_HOUR, UTF8_CS_MIN, UTF8_CS_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ),
+ NUMFMT_STRING( 27, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"" ),
+ NUMFMT_STRING( 28, "M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ),
+ NUMFMT_STRING( 30, "M-D-YY" ),
+ NUMFMT_STRING( 31, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ),
+ NUMFMT_REUSE( 52, 27 ),
+ NUMFMT_REUSE( 53, 28 ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, Hong Kong. */
+const BuiltinFormat spBuiltinFormats_zh_HK[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\"HK$\"", "" ),
+ NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ),
+ NUMFMT_STRING( 27, "[$-404]D/M/E" ),
+ NUMFMT_STRING( 28, "[$-404]D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"E\"" UTF8_CJ_YEAR "\"" ),
+ NUMFMT_STRING( 30, "M/D/YY" ),
+ NUMFMT_STRING( 31, "D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"YYYY\"" UTF8_CJ_YEAR "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, Macau. */
+const BuiltinFormat spBuiltinFormats_zh_MO[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "\\P", "" ),
+ NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ),
+ NUMFMT_STRING( 27, "[$-404]D/M/E" ),
+ NUMFMT_STRING( 28, "[$-404]D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"E\"" UTF8_CJ_YEAR "\"" ),
+ NUMFMT_STRING( 30, "M/D/YY" ),
+ NUMFMT_STRING( 31, "D\"" UTF8_CJ_DAY "\"M\"" UTF8_CJ_MON "\"YYYY\"" UTF8_CJ_YEAR "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, Singapore. */
+const BuiltinFormat spBuiltinFormats_zh_SG[] =
+{
+ NUMFMT_ALLDATETIMES( "D/M/YYYY", "D", "-", "MMM", "-", "YY", "h", "h" ),
+ NUMFMT_ALLCURRENCIES_OPEN_SYMBOL_NUMBER_CLOSE( "$", "" ),
+ NUMFMT_ALLTIMES_CJK( "h", "h", UTF8_CS_HOUR, UTF8_CS_MIN, UTF8_CS_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "$", "", "" ),
+ NUMFMT_STRING( 27, "YYYY\"" UTF8_CS_YEAR "\"M\"" UTF8_CS_MON "\"" ),
+ NUMFMT_STRING( 28, "M\"" UTF8_CS_MON "\"D\"" UTF8_CS_DAY "\"" ),
+ NUMFMT_STRING( 30, "M/D/YY" ),
+ NUMFMT_STRING( 31, "D\"" UTF8_CS_DAY "\"M\"" UTF8_CS_MON "\"YYYY\"" UTF8_CS_YEAR "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Chinese, Taiwan. */
+const BuiltinFormat spBuiltinFormats_zh_TW[] =
+{
+ NUMFMT_ALLDATETIMES( "YYYY/M/D", "D", "-", "MMM", "-", "YY", "hh", "hh" ),
+ NUMFMT_ALLCURRENCIES_MINUS_SYMBOL_NUMBER( "$", "" ),
+ NUMFMT_ALLTIMES_CJK( "hh", "hh", UTF8_CJ_HOUR, UTF8_CJ_MIN, UTF8_CJ_SEC ),
+ NUMFMT_CURRENCY_OPEN_SYMBOL_NUMBER_CLOSE( 23, "\"US$\"", "", "" ),
+ NUMFMT_STRING( 27, "[$-404]E/M/D" ),
+ NUMFMT_STRING( 28, "[$-404]E\"" UTF8_CJ_YEAR "\"M\"" UTF8_CJ_MON "\"D\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_STRING( 30, "M/D/YY" ),
+ NUMFMT_STRING( 31, "YYYY\"" UTF8_CJ_YEAR "\"M\"" UTF8_CJ_MON "\"D\"" UTF8_CJ_DAY "\"" ),
+ NUMFMT_ENDTABLE()
+};
+
+/** Specifies a built-in number format table for a specific locale. */
+struct BuiltinFormatTable
+{
+ const char* mpcLocale; /// The locale for this table.
+ const char* mpcParent; /// The locale of the parent table.
+ const BuiltinFormat* mpFormats; /// The number format table (may be 0, if equal to parent).
+};
+
+const BuiltinFormatTable spBuiltinFormatTables[] =
+{ // locale parent format table
+ { "*", "", spBuiltinFormats_BASE }, // Base table
+ { "af-ZA", "*", spBuiltinFormats_en_ZA }, // Afrikaans, South Africa
+ { "ar-AE", "*", spBuiltinFormats_ar_AE }, // Arabic, U.A.E.
+ { "ar-BH", "*", spBuiltinFormats_ar_BH }, // Arabic, Bahrain
+ { "ar-DZ", "*", spBuiltinFormats_ar_DZ }, // Arabic, Algeria
+ { "ar-EG", "*", spBuiltinFormats_ar_EG }, // Arabic, Egypt
+ { "ar-IQ", "*", spBuiltinFormats_ar_IQ }, // Arabic, Iraq
+ { "ar-JO", "*", spBuiltinFormats_ar_JO }, // Arabic, Jordan
+ { "ar-KW", "*", spBuiltinFormats_ar_KW }, // Arabic, Kuwait
+ { "ar-LB", "*", spBuiltinFormats_ar_LB }, // Arabic, Lebanon
+ { "ar-LY", "*", spBuiltinFormats_ar_LY }, // Arabic, Libya
+ { "ar-MA", "*", spBuiltinFormats_ar_MA }, // Arabic, Morocco
+ { "ar-OM", "*", spBuiltinFormats_ar_OM }, // Arabic, Oman
+ { "ar-QA", "*", spBuiltinFormats_ar_QA }, // Arabic, Qatar
+ { "ar-SA", "*", spBuiltinFormats_ar_SA }, // Arabic, Saudi Arabia
+ { "ar-SY", "*", spBuiltinFormats_ar_SY }, // Arabic, Syria
+ { "ar-TN", "*", spBuiltinFormats_ar_TN }, // Arabic, Tunisia
+ { "ar-YE", "*", spBuiltinFormats_ar_YE }, // Arabic, Yemen
+ { "be-BY", "*", spBuiltinFormats_be_BY }, // Belarusian, Belarus
+ { "bg-BG", "*", spBuiltinFormats_bg_BG }, // Bulgarian, Bulgaria
+ { "bn-IN", "*", spBuiltinFormats_bn_IN }, // Bengali, India
+ { "ca-ES", "*", spBuiltinFormats_es_ES }, // Catalan, Spain
+ { "cs-CZ", "*", spBuiltinFormats_cs_CZ }, // Czech, Czech Republic
+ { "cy-GB", "*", spBuiltinFormats_en_GB }, // Welsh, United Kingdom
+ { "da-DK", "*", spBuiltinFormats_da_DK }, // Danish, Denmark
+ { "de-AT", "*", spBuiltinFormats_de_AT }, // German, Austria
+ { "de-CH", "*", spBuiltinFormats_de_CH }, // German, Switzerland
+ { "de-DE", "*", spBuiltinFormats_de_DE }, // German, Germany
+ { "de-LI", "*", spBuiltinFormats_de_LI }, // German, Liechtenstein
+ { "de-LU", "*", spBuiltinFormats_de_LU }, // German, Luxembourg
+ { "div-MV", "*", spBuiltinFormats_div_MV }, // Divehi, Maldives
+ { "el-GR", "*", spBuiltinFormats_el_GR }, // Greek, Greece
+ { "en-AU", "*", spBuiltinFormats_en_AU }, // English, Australia
+ { "en-BZ", "*", spBuiltinFormats_en_BZ }, // English, Belize
+ { "en-CA", "*", spBuiltinFormats_en_CA }, // English, Canada
+ { "en-CB", "*", spBuiltinFormats_en_CB }, // English, Caribbean
+ { "en-GB", "*", spBuiltinFormats_en_GB }, // English, United Kingdom
+ { "en-IE", "*", spBuiltinFormats_en_IE }, // English, Ireland
+ { "en-JM", "*", spBuiltinFormats_en_JM }, // English, Jamaica
+ { "en-NZ", "*", spBuiltinFormats_en_NZ }, // English, New Zealand
+ { "en-PH", "*", spBuiltinFormats_en_PH }, // English, Philippines
+ { "en-TT", "*", spBuiltinFormats_en_TT }, // English, Trinidad and Tobago
+ { "en-US", "*", spBuiltinFormats_en_US }, // English, USA
+ { "en-ZA", "*", spBuiltinFormats_en_ZA }, // English, South Africa
+ { "en-ZW", "*", spBuiltinFormats_en_ZW }, // English, Zimbabwe
+ { "es-AR", "*", spBuiltinFormats_es_AR }, // Spanish, Argentina
+ { "es-BO", "*", spBuiltinFormats_es_BO }, // Spanish, Bolivia
+ { "es-CL", "*", spBuiltinFormats_es_CL }, // Spanish, Chile
+ { "es-CO", "*", spBuiltinFormats_es_CO }, // Spanish, Colombia
+ { "es-CR", "*", spBuiltinFormats_es_CR }, // Spanish, Costa Rica
+ { "es-DO", "*", spBuiltinFormats_es_DO }, // Spanish, Dominican Republic
+ { "es-EC", "*", spBuiltinFormats_es_EC }, // Spanish, Ecuador
+ { "es-ES", "*", spBuiltinFormats_es_ES }, // Spanish, Spain
+ { "es-GT", "*", spBuiltinFormats_es_GT }, // Spanish, Guatemala
+ { "es-HN", "*", spBuiltinFormats_es_HN }, // Spanish, Honduras
+ { "es-MX", "*", spBuiltinFormats_es_MX }, // Spanish, Mexico
+ { "es-NI", "*", spBuiltinFormats_es_NI }, // Spanish, Nicaragua
+ { "es-PA", "*", spBuiltinFormats_es_PA }, // Spanish, Panama
+ { "es-PE", "*", spBuiltinFormats_es_PE }, // Spanish, Peru
+ { "es-PR", "*", spBuiltinFormats_es_PR }, // Spanish, Puerto Rico
+ { "es-PY", "*", spBuiltinFormats_es_PY }, // Spanish, Paraguay
+ { "es-SV", "*", spBuiltinFormats_es_SV }, // Spanish, El Salvador
+ { "es-UY", "*", spBuiltinFormats_es_UY }, // Spanish, Uruguay
+ { "es-VE", "*", spBuiltinFormats_es_VE }, // Spanish, Venezuela
+ { "et-EE", "*", spBuiltinFormats_et_EE }, // Estonian, Estonia
+ { "fa-IR", "*", spBuiltinFormats_fa_IR }, // Farsi, Iran
+ { "fi-FI", "*", spBuiltinFormats_fi_FI }, // Finnish, Finland
+ { "fo-FO", "*", spBuiltinFormats_fo_FO }, // Faroese, Faroe Islands
+ { "fr-BE", "*", spBuiltinFormats_fr_BE }, // French, Belgium
+ { "fr-CA", "*", spBuiltinFormats_fr_CA }, // French, Canada
+ { "fr-CH", "*", spBuiltinFormats_fr_CH }, // French, Switzerland
+ { "fr-FR", "*", spBuiltinFormats_fr_FR }, // French, France
+ { "fr-LU", "*", spBuiltinFormats_fr_LU }, // French, Luxembourg
+ { "fr-MC", "*", spBuiltinFormats_fr_MC }, // French, Monaco
+ { "gl-ES", "*", spBuiltinFormats_gl_ES }, // Galizian, Spain
+ { "gu-IN", "*", spBuiltinFormats_gu_IN }, // Gujarati, India
+ { "he-IL", "*", spBuiltinFormats_he_IL }, // Hebrew, Israel
+ { "hi-IN", "*", spBuiltinFormats_hi_IN }, // Hindi, India
+ { "hr-BA", "*", spBuiltinFormats_hr_BA }, // Croatian, Bosnia and Herzegowina
+ { "hr-HR", "*", spBuiltinFormats_hr_HR }, // Croatian, Croatia
+ { "hu-HU", "*", spBuiltinFormats_hu_HU }, // Hungarian, Hungary
+ { "hy-AM", "*", spBuiltinFormats_hy_AM }, // Armenian, Armenia
+ { "id-ID", "*", spBuiltinFormats_id_ID }, // Indonesian, Indonesia
+ { "is-IS", "*", spBuiltinFormats_is_IS }, // Icelandic, Iceland
+ { "it-CH", "*", spBuiltinFormats_it_CH }, // Italian, Switzerland
+ { "it-IT", "*", spBuiltinFormats_it_IT }, // Italian, Italy
+ { "ka-GE", "*", spBuiltinFormats_ka_GE }, // Georgian, Georgia
+ { "kk-KZ", "*", spBuiltinFormats_kk_KZ }, // Kazakh, Kazakhstan
+ { "kn-IN", "*", spBuiltinFormats_kn_IN }, // Kannada, India
+ { "kok-IN", "*", spBuiltinFormats_hi_IN }, // Konkani, India
+ { "ky-KG", "*", spBuiltinFormats_ky_KG }, // Kyrgyz, Kyrgyzstan
+ { "lt-LT", "*", spBuiltinFormats_lt_LT }, // Lithuanian, Lithuania
+ { "lv-LV", "*", spBuiltinFormats_lv_LV }, // Latvian, Latvia
+ { "mi-NZ", "*", spBuiltinFormats_en_NZ }, // Maori, New Zealand
+ { "ml-IN", "*", spBuiltinFormats_ml_IN }, // Malayalam, India
+ { "mn-MN", "*", spBuiltinFormats_mn_MN }, // Mongolian, Mongolia
+ { "mr-IN", "*", spBuiltinFormats_hi_IN }, // Marathi, India
+ { "ms-BN", "*", spBuiltinFormats_ms_BN }, // Malay, Brunei Darussalam
+ { "ms-MY", "*", spBuiltinFormats_ms_MY }, // Malay, Malaysia
+ { "mt-MT", "*", spBuiltinFormats_mt_MT }, // Maltese, Malta
+ { "nb-NO", "*", spBuiltinFormats_no_NO }, // Norwegian Bokmal, Norway
+ { "nl-BE", "*", spBuiltinFormats_nl_BE }, // Dutch, Belgium
+ { "nl-NL", "*", spBuiltinFormats_nl_NL }, // Dutch, Netherlands
+ { "nn-NO", "*", spBuiltinFormats_no_NO }, // Norwegian Nynorsk, Norway
+ { "nso-ZA", "*", spBuiltinFormats_en_ZA }, // Northern Sotho, South Africa
+ { "pa-IN", "*", spBuiltinFormats_pa_IN }, // Punjabi, India
+ { "pl-PL", "*", spBuiltinFormats_pl_PL }, // Polish, Poland
+ { "pt-BR", "*", spBuiltinFormats_pt_BR }, // Portuguese, Brazil
+ { "pt-PT", "*", spBuiltinFormats_pt_PT }, // Portuguese, Portugal
+ { "qu-BO", "*", spBuiltinFormats_es_BO }, // Quechua, Bolivia
+ { "qu-EC", "*", spBuiltinFormats_es_EC }, // Quechua, Ecuador
+ { "qu-PE", "*", spBuiltinFormats_es_PE }, // Quechua, Peru
+ { "ro-RO", "*", spBuiltinFormats_ro_RO }, // Romanian, Romania
+ { "ru-RU", "*", spBuiltinFormats_ru_RU }, // Russian, Russian Federation
+ { "sa-IN", "*", spBuiltinFormats_hi_IN }, // Sanskrit, India
+ { "se-FI", "*", spBuiltinFormats_fi_FI }, // Sami, Finland
+ { "se-NO", "*", spBuiltinFormats_no_NO }, // Sami, Norway
+ { "se-SE", "*", spBuiltinFormats_sv_SE }, // Sami, Sweden
+ { "sk-SK", "*", spBuiltinFormats_sk_SK }, // Slovak, Slovakia
+ { "sl-SI", "*", spBuiltinFormats_sl_SI }, // Slovenian, Slovenia
+ { "sv-FI", "*", spBuiltinFormats_sv_FI }, // Swedish, Finland
+ { "sv-SE", "*", spBuiltinFormats_sv_SE }, // Swedish, Sweden
+ { "sw-TZ", "*", spBuiltinFormats_sw_TZ }, // Swahili, Tanzania
+ { "syr-SY", "*", spBuiltinFormats_ar_SY }, // Syriac, Syria
+ { "syr-TR", "*", spBuiltinFormats_tr_TR }, // Syriac, Turkey
+ { "ta-IN", "*", spBuiltinFormats_ta_IN }, // Tamil, India
+ { "te-IN", "*", spBuiltinFormats_te_IN }, // Telugu, India
+ { "th-TH", "*", spBuiltinFormats_th_TH }, // Thai, Thailand
+ { "tn-ZA", "*", spBuiltinFormats_en_ZA }, // Tswana, South Africa
+ { "tr-TR", "*", spBuiltinFormats_tr_TR }, // Turkish, Turkey
+ { "tt-RU", "*", spBuiltinFormats_tt_RU }, // Tatar, Russian Federation
+ { "uk-UA", "*", spBuiltinFormats_uk_UA }, // Ukrainian, Ukraine
+ { "ur-PK", "*", spBuiltinFormats_ur_PK }, // Urdu, Pakistan
+ { "vi-VN", "*", spBuiltinFormats_vi_VN }, // Vietnamese, Viet Nam
+ { "xh-ZA", "*", spBuiltinFormats_en_ZA }, // Xhosa, South Africa
+ { "zu-ZA", "*", spBuiltinFormats_en_ZA }, // Zulu, South Africa
+
+ { "*CJK", "*", spBuiltinFormats_CJK }, // CJK base table
+ { "ja-JP", "*CJK", spBuiltinFormats_ja_JP }, // Japanese, Japan
+ { "ko-KR", "*CJK", spBuiltinFormats_ko_KR }, // Korean, South Korea
+ { "zh-CN", "*CJK", spBuiltinFormats_zh_CN }, // Chinese, China
+ { "zh-HK", "*CJK", spBuiltinFormats_zh_HK }, // Chinese, Hong Kong
+ { "zh-MO", "*CJK", spBuiltinFormats_zh_MO }, // Chinese, Macau
+ { "zh-SG", "*CJK", spBuiltinFormats_zh_SG }, // Chinese, Singapore
+ { "zh-TW", "*CJK", spBuiltinFormats_zh_TW } // Chinese, Taiwan
+};
+
+} // namespace
+
+NumFmtModel::NumFmtModel() :
+ mnPredefId( -1 )
+{
+}
+
+ApiNumFmtData::ApiNumFmtData() :
+ mnIndex( 0 )
+{
+}
+
+namespace {
+
+sal_Int32 lclCreatePredefinedFormat( const Reference< XNumberFormats >& rxNumFmts,
+ sal_Int16 nPredefId, const Locale& rToLocale )
+{
+ sal_Int32 nIndex = 0;
+ try
+ {
+ Reference< XNumberFormatTypes > xNumFmtTypes( rxNumFmts, UNO_QUERY_THROW );
+ nIndex = (nPredefId >= 0) ?
+ xNumFmtTypes->getFormatIndex( nPredefId, rToLocale ) :
+ xNumFmtTypes->getStandardIndex( rToLocale );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( OStringBuffer( "lclCreatePredefinedFormat - cannot create predefined number format " ).
+ append( OString::number( nPredefId ) ).getStr() );
+ }
+ return nIndex;
+}
+
+sal_Int32 lclCreateFormat( const Reference< XNumberFormats >& rxNumFmts,
+ const OUString& rFmtCode, const Locale& rToLocale, const Locale& rFromLocale )
+{
+ sal_Int32 nIndex = 0;
+ try
+ {
+ nIndex = rxNumFmts->addNewConverted( rFmtCode, rFromLocale, rToLocale );
+ }
+ catch( Exception& )
+ {
+ // BIFF2-BIFF4 stores standard format explicitly in stream
+ if( rFmtCode.equalsIgnoreAsciiCase( "general" ) )
+ {
+ nIndex = lclCreatePredefinedFormat( rxNumFmts, 0, rToLocale );
+ }
+ else
+ {
+ // do not assert fractional number formats with fixed denominator
+ OSL_ENSURE( rFmtCode.startsWith( "#\\ ?/" ) ||
+ rFmtCode.startsWith( "#\\ ?\?/" ) ||
+ rFmtCode.startsWith( "#\\ ?\?\?/" ),
+ OStringBuffer( "lclCreateFormat - cannot create number format '" ).
+ append( OUStringToOString( rFmtCode, osl_getThreadTextEncoding() ) ).
+ append( '\'' ).getStr() );
+ }
+ }
+ return nIndex;
+}
+
+/** Functor for converting an XML number format to an API number format index. */
+class NumberFormatFinalizer
+{
+public:
+ explicit NumberFormatFinalizer( const WorkbookHelper& rHelper );
+
+ void operator()( NumberFormat& rNumFmt ) const
+ { rNumFmt.finalizeImport( mxNumFmts, maEnUsLocale ); }
+
+private:
+ Reference< XNumberFormats > mxNumFmts;
+ Locale maEnUsLocale;
+};
+
+NumberFormatFinalizer::NumberFormatFinalizer( const WorkbookHelper& rHelper ) :
+ maEnUsLocale( "en", "US", OUString() )
+{
+ try
+ {
+ Reference< XNumberFormatsSupplier > xNumFmtsSupp( rHelper.getDocument(), UNO_QUERY_THROW );
+ mxNumFmts = xNumFmtsSupp->getNumberFormats();
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( mxNumFmts.is(), "NumberFormatFinalizer::NumberFormatFinalizer - cannot get number formats" );
+}
+
+sal_Int32 lclPosToken ( const OUString& sFormat, std::u16string_view sSearch, sal_Int32 nStartPos )
+{
+ sal_Int32 nLength = sFormat.getLength();
+ for ( sal_Int32 i = nStartPos; i < nLength && i >= 0 ; i++ )
+ {
+ switch(sFormat[i])
+ {
+ case '\"' : // skip text
+ i = sFormat.indexOf('\"',i+1);
+ break;
+ case '[' : // skip condition
+ i = sFormat.indexOf(']',i+1);
+ break;
+ default :
+ if ( sFormat.match(sSearch, i) )
+ return i;
+ break;
+ }
+ if ( i < 0 )
+ i--;
+ }
+ return -2;
+}
+
+} // namespace
+
+NumberFormat::NumberFormat( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void NumberFormat::setFormatCode( const OUString& rFmtCode )
+{
+ // Special case for fraction code '\ ?/?', it is passed to us in xml, the '\' is not
+ // an escape character but merely should be telling the formatter to display the next
+ // char in the format ( afaics it does that anyhow )
+ sal_Int32 nPosEscape = 0;
+ sal_Int32 nErase = 0;
+ sal_Int32 nLastIndex = rFmtCode.getLength() - 1;
+ OUStringBuffer sFormat(rFmtCode);
+
+ while ( ( nPosEscape = lclPosToken( rFmtCode, u"\\ ", nPosEscape ) ) > 0 )
+ {
+ sal_Int32 nPos = nPosEscape + 2;
+ while ( nPos < nLastIndex && ( rFmtCode[nPos] == '?' || rFmtCode[nPos] == '#' || rFmtCode[nPos] == '0' ) )
+ nPos++;
+ if ( nPos < nLastIndex && rFmtCode[nPos] == '/' )
+ {
+ sFormat.remove(nPosEscape - nErase, 1);
+ nErase ++;
+ } // tdf#81939 preserve other escape characters
+ nPosEscape = lclPosToken( rFmtCode, u";", nPosEscape ); // skip to next format
+ }
+ maModel.maFmtCode = sFormat.makeStringAndClear();
+}
+
+void NumberFormat::setFormatCode( const Locale& rLocale, const char* pcFmtCode )
+{
+ maModel.maLocale = rLocale;
+ maModel.maFmtCode = OStringToOUString( std::string_view( pcFmtCode ), RTL_TEXTENCODING_UTF8 );
+ maModel.mnPredefId = -1;
+}
+
+void NumberFormat::setPredefinedId( const Locale& rLocale, sal_Int16 nPredefId )
+{
+ maModel.maLocale = rLocale;
+ maModel.maFmtCode.clear();
+ maModel.mnPredefId = nPredefId;
+}
+
+void NumberFormat::finalizeImport( const Reference< XNumberFormats >& rxNumFmts, const Locale& rFromLocale )
+{
+ if( rxNumFmts.is() && !maModel.maFmtCode.isEmpty() )
+ maApiData.mnIndex = lclCreateFormat( rxNumFmts, maModel.maFmtCode, maModel.maLocale, rFromLocale );
+ else
+ maApiData.mnIndex = lclCreatePredefinedFormat( rxNumFmts, maModel.mnPredefId, maModel.maLocale );
+}
+
+sal_uInt32 NumberFormat::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ const ScDocument& rDoc = getScDocument();
+ static sal_uInt32 nDflt = rDoc.GetFormatTable()->GetStandardIndex( ScGlobal::eLnge );
+ sal_uInt32 nScNumFmt = nDflt;
+ if ( maApiData.mnIndex )
+ nScNumFmt = maApiData.mnIndex;
+
+ ScfTools::PutItem( rItemSet, SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ), bSkipPoolDefs );
+ if( rItemSet.GetItemState( ATTR_VALUE_FORMAT, false ) == SfxItemState::SET )
+ ScGlobal::AddLanguage( rItemSet, *(rDoc.GetFormatTable()) );
+ else
+ nScNumFmt = 0;
+
+ return nScNumFmt;
+}
+
+NumberFormatsBuffer::NumberFormatsBuffer( const WorkbookHelper& rHelper )
+ : WorkbookHelper(rHelper)
+ , mnHighestId(0)
+{
+ // get the current locale
+ // try user-defined locale setting
+ maLocaleStr = officecfg::Setup::L10N::ooSetupSystemLocale::get();
+ // if set to "use system", get locale from system
+ if( maLocaleStr.isEmpty() )
+ maLocaleStr = officecfg::System::L10N::Locale::get();
+
+ // create built-in formats for current locale
+ insertBuiltinFormats();
+}
+
+NumberFormatRef NumberFormatsBuffer::createNumFmt( sal_uInt32 nNumFmtId, const OUString& rFmtCode )
+{
+ NumberFormatRef xNumFmt;
+ xNumFmt = std::make_shared<NumberFormat>( *this );
+ maNumFmts[ nNumFmtId ] = xNumFmt;
+ if ( nNumFmtId > mnHighestId )
+ mnHighestId = nNumFmtId;
+ xNumFmt->setFormatCode( rFmtCode );
+ return xNumFmt;
+}
+
+NumberFormatRef NumberFormatsBuffer::importNumFmt( const AttributeList& rAttribs )
+{
+ sal_Int32 nNumFmtId = rAttribs.getInteger( XML_numFmtId, -1 );
+ OUString aFmtCode = rAttribs.getXString( XML_formatCode, OUString() );
+ return createNumFmt( nNumFmtId, aFmtCode );
+}
+
+void NumberFormatsBuffer::importNumFmt( SequenceInputStream& rStrm )
+{
+ sal_Int32 nNumFmtId = rStrm.readuInt16();
+ OUString aFmtCode = BiffHelper::readString( rStrm );
+ createNumFmt( nNumFmtId, aFmtCode );
+}
+
+void NumberFormatsBuffer::finalizeImport()
+{
+ maNumFmts.forEach( NumberFormatFinalizer( *this ) );
+}
+
+sal_uInt32 NumberFormatsBuffer::fillToItemSet( SfxItemSet& rItemSet, sal_uInt32 nNumFmtId, bool bSkipPoolDefs ) const
+{
+ const NumberFormat* pNumFmt = maNumFmts.get(nNumFmtId).get();
+ if (!pNumFmt)
+ return 0;
+
+ return pNumFmt->fillToItemSet( rItemSet, bSkipPoolDefs);
+}
+
+void NumberFormatsBuffer::insertBuiltinFormats()
+{
+ // build a map containing pointers to all tables
+ typedef ::std::map< OUString, const BuiltinFormatTable* > BuiltinMap;
+ BuiltinMap aBuiltinMap;
+ for(auto const &rTable : spBuiltinFormatTables)
+ aBuiltinMap[ OUString::createFromAscii(rTable.mpcLocale) ] = &rTable;
+
+ // convert locale string to locale struct
+ Locale aSysLocale( LanguageTag::convertToLocale( maLocaleStr));
+
+ // build a list of table pointers for the current locale, with all parent tables
+ typedef ::std::vector< const BuiltinFormatTable* > BuiltinVec;
+ BuiltinVec aBuiltinVec;
+ BuiltinMap::const_iterator aMIt = aBuiltinMap.find( maLocaleStr ), aMEnd = aBuiltinMap.end();
+ OSL_ENSURE( aMIt != aMEnd,
+ OStringBuffer( "NumberFormatsBuffer::insertBuiltinFormats - locale '" +
+ OUStringToOString( maLocaleStr, RTL_TEXTENCODING_ASCII_US ) +
+ "' not supported (#i29949#)" ).getStr() );
+ // start with default table, if no table has been found
+ if( aMIt == aMEnd )
+ aMIt = aBuiltinMap.find( "*" );
+ OSL_ENSURE( aMIt != aMEnd, "NumberFormatsBuffer::insertBuiltinFormats - default map not found" );
+ // insert all tables into the vector
+ for( ; aMIt != aMEnd; aMIt = aBuiltinMap.find( OUString::createFromAscii( aMIt->second->mpcParent ) ) )
+ aBuiltinVec.push_back( aMIt->second );
+
+ // insert the default formats in the format map (in reverse order from default table to system locale)
+ std::map< sal_uInt32, sal_uInt32 > aReuseMap;
+ for( BuiltinVec::reverse_iterator aVIt = aBuiltinVec.rbegin(), aVEnd = aBuiltinVec.rend(); aVIt != aVEnd; ++aVIt )
+ {
+ // do not put the current system locale for default table
+ Locale aLocale;
+ if( (*aVIt)->mpcParent[ 0 ] != '\0' && OUString::createFromAscii((*aVIt)->mpcLocale) != maLocaleStr )
+ aLocale = aSysLocale;
+ for( const BuiltinFormat* pBuiltin = (*aVIt)->mpFormats; pBuiltin && (pBuiltin->mnNumFmtId >= 0); ++pBuiltin )
+ {
+ NumberFormatRef& rxNumFmt = maNumFmts[ pBuiltin->mnNumFmtId ];
+ rxNumFmt = std::make_shared<NumberFormat>( *this );
+
+ bool bReuse = false;
+ if( pBuiltin->mpcFmtCode )
+ rxNumFmt->setFormatCode( aLocale, pBuiltin->mpcFmtCode );
+ else if( pBuiltin->mnPredefId >= 0 )
+ rxNumFmt->setPredefinedId( aLocale, pBuiltin->mnPredefId );
+ else
+ bReuse = pBuiltin->mnReuseId >= 0;
+
+ if( bReuse )
+ aReuseMap[ pBuiltin->mnNumFmtId ] = pBuiltin->mnReuseId;
+ else
+ aReuseMap.erase( pBuiltin->mnNumFmtId );
+ }
+ }
+
+ // copy reused number formats
+ for( const auto& [rNumFmtId, rReuseId] : aReuseMap )
+ {
+ maNumFmts[ rNumFmtId ] = maNumFmts[ rReuseId ];
+ if ( rNumFmtId > mnHighestId )
+ mnHighestId = rNumFmtId;
+ }
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/ooxformulaparser.cxx b/sc/source/filter/oox/ooxformulaparser.cxx
new file mode 100644
index 000000000..0a7c0d524
--- /dev/null
+++ b/sc/source/filter/oox/ooxformulaparser.cxx
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ooxformulaparser.hxx>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <osl/diagnose.h>
+#include <cppuhelper/supportsservice.hxx>
+#include <formulaparser.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+class OOXMLFormulaParserImpl : private FormulaFinalizer
+{
+public:
+ explicit OOXMLFormulaParserImpl( const Reference< XMultiServiceFactory >& rxModelFactory );
+
+ Sequence< FormulaToken > parseFormula( const OUString& rFormula, const ScAddress& rReferencePos );
+
+protected:
+ virtual const FunctionInfo* resolveBadFuncName( const OUString& rTokenData ) const override;
+
+private:
+ ApiParserWrapper maApiParser;
+};
+
+OOXMLFormulaParserImpl::OOXMLFormulaParserImpl( const Reference< XMultiServiceFactory >& rxModelFactory ) :
+ FormulaFinalizer( OpCodeProvider( rxModelFactory, true ) ),
+ maApiParser( rxModelFactory, *this )
+{
+}
+
+Sequence< FormulaToken > OOXMLFormulaParserImpl::parseFormula( const OUString& rFormula, const ScAddress& rReferencePos )
+{
+ return finalizeTokenArray( maApiParser.parseFormula( rFormula, rReferencePos ) );
+}
+
+const FunctionInfo* OOXMLFormulaParserImpl::resolveBadFuncName( const OUString& rTokenData ) const
+{
+ /* Try to parse calls to library functions. The format of such a function
+ call is assumed to be
+ "'<path-to-office-install>\Library\<libname>'!<funcname>". */
+
+ // the string has to start with an apostroph (followed by the library URL)
+ if( (rTokenData.getLength() >= 6) && (rTokenData[ 0 ] == '\'') )
+ {
+ // library URL and function name are separated by an exclamation mark
+ sal_Int32 nExclamPos = rTokenData.lastIndexOf( '!' );
+ if( (1 < nExclamPos) && (nExclamPos + 1 < rTokenData.getLength()) && (rTokenData[ nExclamPos - 1 ] == '\'') )
+ {
+ // find the last backslash that separates library path and name
+ sal_Int32 nFileSep = rTokenData.lastIndexOf( '\\', nExclamPos - 2 );
+ if( nFileSep > 1 )
+ {
+ // find preceding backslash that separates the last directory name
+ sal_Int32 nDirSep = rTokenData.lastIndexOf( '\\', nFileSep - 1 );
+ // function library is located in a directory called 'library'
+ if( (nDirSep > 0) && rTokenData.matchIgnoreAsciiCase( "\\LIBRARY\\", nDirSep ) )
+ {
+ // try to find a function info for the function name
+ OUString aFuncName = rTokenData.copy( nExclamPos + 1 ).toAsciiUpperCase();
+ const FunctionInfo* pFuncInfo = getFuncInfoFromOoxFuncName( aFuncName );
+ if( pFuncInfo && (pFuncInfo->meFuncLibType != FUNCLIB_UNKNOWN) )
+ {
+ // check that the name of the library matches
+ OUString aLibName = rTokenData.copy( nFileSep + 1, nExclamPos - nFileSep - 2 );
+ if( pFuncInfo->meFuncLibType == getFuncLibTypeFromLibraryName( aLibName ) )
+ return pFuncInfo;
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+OOXMLFormulaParser::OOXMLFormulaParser()
+{
+}
+
+OOXMLFormulaParser::~OOXMLFormulaParser()
+{
+}
+
+// com.sun.star.lang.XServiceInfo interface -----------------------------------
+OUString SAL_CALL OOXMLFormulaParser::getImplementationName()
+{
+ return "com.sun.star.comp.oox.xls.FormulaParser";
+}
+
+sal_Bool SAL_CALL OOXMLFormulaParser::supportsService( const OUString& rService )
+{
+ return cppu::supportsService(this, rService);
+}
+
+Sequence< OUString > SAL_CALL OOXMLFormulaParser::getSupportedServiceNames()
+{
+ return { "com.sun.star.sheet.FilterFormulaParser" };
+}
+
+// com.sun.star.lang.XInitialization interface --------------------------------
+
+void SAL_CALL OOXMLFormulaParser::initialize( const Sequence< Any >& rArgs )
+{
+ OSL_ENSURE( rArgs.hasElements(), "OOXMLFormulaParser::initialize - missing arguments" );
+ if( !rArgs.hasElements() )
+ throw RuntimeException();
+ mxComponent.set( rArgs[ 0 ], UNO_QUERY_THROW );
+}
+
+// com.sun.star.sheet.XFilterFormulaParser interface --------------------------
+
+OUString SAL_CALL OOXMLFormulaParser::getSupportedNamespace()
+{
+ return "http://schemas.microsoft.com/office/excel/formula";
+}
+
+// com.sun.star.sheet.XFormulaParser interface --------------------------------
+
+Sequence< FormulaToken > SAL_CALL OOXMLFormulaParser::parseFormula(
+ const OUString& rFormula, const CellAddress& rReferencePos )
+{
+ if( !mxParserImpl )
+ {
+ Reference< XMultiServiceFactory > xModelFactory( mxComponent, UNO_QUERY_THROW );
+ mxParserImpl = std::make_shared<OOXMLFormulaParserImpl>( xModelFactory );
+ }
+ return mxParserImpl->parseFormula( rFormula,
+ ScAddress(rReferencePos.Column, rReferencePos.Row, rReferencePos.Sheet) );
+}
+
+OUString SAL_CALL OOXMLFormulaParser::printFormula(
+ const Sequence< FormulaToken >& /*rTokens*/, const CellAddress& /*rReferencePos*/ )
+{
+ // not implemented
+ throw RuntimeException();
+}
+
+} // namespace oox::xls
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_oox_xls_FormulaParser_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new oox::xls::OOXMLFormulaParser());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pagesettings.cxx b/sc/source/filter/oox/pagesettings.cxx
new file mode 100644
index 000000000..28cf7c742
--- /dev/null
+++ b/sc/source/filter/oox/pagesettings.cxx
@@ -0,0 +1,1065 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pagesettings.hxx>
+
+#include <algorithm>
+#include <set>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/sheet/XHeaderFooterContent.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/style/GraphicLocation.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/text/FilenameDisplayFormat.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/text/XTextCursor.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <osl/diagnose.h>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sax/tools/converter.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/graphichelper.hxx>
+#include <oox/helper/propertymap.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/core/relations.hxx>
+#include <stylesbuffer.hxx>
+#include <document.hxx>
+#include <biffhelper.hxx>
+#include <filter/msfilter/util.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+using ::oox::core::Relations;
+
+namespace {
+
+const double OOX_MARGIN_DEFAULT_LR = 0.748; /// Left/right default margin in inches.
+const double OOX_MARGIN_DEFAULT_TB = 0.984; /// Top/bottom default margin in inches.
+const double OOX_MARGIN_DEFAULT_HF = 0.512; /// Header/footer default margin in inches.
+
+const sal_uInt16 BIFF12_PRINTOPT_HORCENTER = 0x0001;
+const sal_uInt16 BIFF12_PRINTOPT_VERCENTER = 0x0002;
+const sal_uInt16 BIFF12_PRINTOPT_PRINTHEADING = 0x0004;
+const sal_uInt16 BIFF12_PRINTOPT_PRINTGRID = 0x0008;
+
+const sal_uInt16 BIFF12_HEADERFOOTER_DIFFEVEN = 0x0001;
+const sal_uInt16 BIFF12_HEADERFOOTER_DIFFFIRST = 0x0002;
+
+const sal_uInt16 BIFF12_PAGESETUP_INROWS = 0x0001;
+const sal_uInt16 BIFF12_PAGESETUP_LANDSCAPE = 0x0002;
+const sal_uInt16 BIFF12_PAGESETUP_INVALID = 0x0004;
+const sal_uInt16 BIFF12_PAGESETUP_BLACKWHITE = 0x0008;
+const sal_uInt16 BIFF12_PAGESETUP_DRAFTQUALITY = 0x0010;
+const sal_uInt16 BIFF12_PAGESETUP_PRINTNOTES = 0x0020;
+const sal_uInt16 BIFF12_PAGESETUP_DEFAULTORIENT = 0x0040;
+const sal_uInt16 BIFF12_PAGESETUP_USEFIRSTPAGE = 0x0080;
+const sal_uInt16 BIFF12_PAGESETUP_NOTES_END = 0x0100; // different to BIFF flag
+
+const sal_uInt16 BIFF12_CHARTPAGESETUP_LANDSCAPE = 0x0001;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_INVALID = 0x0002;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_BLACKWHITE = 0x0004;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_DEFAULTORIENT= 0x0008;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_USEFIRSTPAGE = 0x0010;
+const sal_uInt16 BIFF12_CHARTPAGESETUP_DRAFTQUALITY = 0x0020;
+
+} // namespace
+
+PageSettingsModel::PageSettingsModel() :
+ mfLeftMargin( OOX_MARGIN_DEFAULT_LR ),
+ mfRightMargin( OOX_MARGIN_DEFAULT_LR ),
+ mfTopMargin( OOX_MARGIN_DEFAULT_TB ),
+ mfBottomMargin( OOX_MARGIN_DEFAULT_TB ),
+ mfHeaderMargin( OOX_MARGIN_DEFAULT_HF ),
+ mfFooterMargin( OOX_MARGIN_DEFAULT_HF ),
+ mnPaperSize( 1 ),
+ mnPaperWidth( 0 ),
+ mnPaperHeight( 0 ),
+ mnCopies( 1 ),
+ mnScale( 100 ),
+ mnFirstPage( 1 ),
+ mnFitToWidth( 1 ),
+ mnFitToHeight( 1 ),
+ mnHorPrintRes( 600 ),
+ mnVerPrintRes( 600 ),
+ mnOrientation( XML_default ),
+ mnPageOrder( XML_downThenOver ),
+ mnCellComments( XML_none ),
+ mnPrintErrors( XML_displayed ),
+ mbUseEvenHF( false ),
+ mbUseFirstHF( false ),
+ mbValidSettings( true ),
+ mbUseFirstPage( false ),
+ mbBlackWhite( false ),
+ mbDraftQuality( false ),
+ mbFitToPages( false ),
+ mbHorCenter( false ),
+ mbVerCenter( false ),
+ mbPrintGrid( false ),
+ mbPrintHeadings( false )
+{
+}
+
+void PageSettingsModel::setBiffPrintErrors( sal_uInt8 nPrintErrors )
+{
+ static const sal_Int32 spnErrorIds[] = { XML_displayed, XML_none, XML_dash, XML_NA };
+ mnPrintErrors = STATIC_ARRAY_SELECT( spnErrorIds, nPrintErrors, XML_none );
+}
+
+PageSettings::PageSettings( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void PageSettings::importPrintOptions( const AttributeList& rAttribs )
+{
+ maModel.mbHorCenter = rAttribs.getBool( XML_horizontalCentered, false );
+ maModel.mbVerCenter = rAttribs.getBool( XML_verticalCentered, false );
+ maModel.mbPrintGrid = rAttribs.getBool( XML_gridLines, false );
+ maModel.mbPrintHeadings = rAttribs.getBool( XML_headings, false );
+}
+
+void PageSettings::importPageMargins( const AttributeList& rAttribs )
+{
+ maModel.mfLeftMargin = rAttribs.getDouble( XML_left, OOX_MARGIN_DEFAULT_LR );
+ maModel.mfRightMargin = rAttribs.getDouble( XML_right, OOX_MARGIN_DEFAULT_LR );
+ maModel.mfTopMargin = rAttribs.getDouble( XML_top, OOX_MARGIN_DEFAULT_TB );
+ maModel.mfBottomMargin = rAttribs.getDouble( XML_bottom, OOX_MARGIN_DEFAULT_TB );
+ maModel.mfHeaderMargin = rAttribs.getDouble( XML_header, OOX_MARGIN_DEFAULT_HF );
+ maModel.mfFooterMargin = rAttribs.getDouble( XML_footer, OOX_MARGIN_DEFAULT_HF );
+}
+
+void PageSettings::importPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ OUString aStr;
+ maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ maModel.mnPaperSize = rAttribs.getInteger( XML_paperSize, 1 );
+ aStr = rAttribs.getString ( XML_paperWidth, OUString() );
+ ::sax::Converter::convertMeasure(
+ maModel.mnPaperWidth, aStr);
+ aStr = rAttribs.getString ( XML_paperHeight, OUString() );
+ ::sax::Converter::convertMeasure(
+ maModel.mnPaperHeight, aStr );
+ maModel.mnCopies = rAttribs.getInteger( XML_copies, 1 );
+ maModel.mnScale = rAttribs.getInteger( XML_scale, 100 );
+ maModel.mnFirstPage = rAttribs.getInteger( XML_firstPageNumber, 1 );
+ maModel.mnFitToWidth = rAttribs.getInteger( XML_fitToWidth, 1 );
+ maModel.mnFitToHeight = rAttribs.getInteger( XML_fitToHeight, 1 );
+ maModel.mnHorPrintRes = rAttribs.getInteger( XML_horizontalDpi, 600 );
+ maModel.mnVerPrintRes = rAttribs.getInteger( XML_verticalDpi, 600 );
+ maModel.mnOrientation = rAttribs.getToken( XML_orientation, XML_default );
+ maModel.mnPageOrder = rAttribs.getToken( XML_pageOrder, XML_downThenOver );
+ maModel.mnCellComments = rAttribs.getToken( XML_cellComments, XML_none );
+ maModel.mnPrintErrors = rAttribs.getToken( XML_errors, XML_displayed );
+ maModel.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, false );
+ maModel.mbUseFirstPage = rAttribs.getBool( XML_useFirstPageNumber, false );
+ maModel.mbBlackWhite = rAttribs.getBool( XML_blackAndWhite, false );
+ maModel.mbDraftQuality = rAttribs.getBool( XML_draft, false );
+}
+
+void PageSettings::importChartPageSetup( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ OUString aStr;
+ maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ maModel.mnPaperSize = rAttribs.getInteger( XML_paperSize, 1 );
+ aStr = rAttribs.getString ( XML_paperWidth, OUString() );
+ ::sax::Converter::convertMeasure(
+ maModel.mnPaperWidth, aStr );
+ aStr = rAttribs.getString ( XML_paperHeight, OUString() );
+ ::sax::Converter::convertMeasure(
+ maModel.mnPaperHeight, aStr );
+ maModel.mnCopies = rAttribs.getInteger( XML_copies, 1 );
+ maModel.mnFirstPage = rAttribs.getInteger( XML_firstPageNumber, 1 );
+ maModel.mnHorPrintRes = rAttribs.getInteger( XML_horizontalDpi, 600 );
+ maModel.mnVerPrintRes = rAttribs.getInteger( XML_verticalDpi, 600 );
+ maModel.mnOrientation = rAttribs.getToken( XML_orientation, XML_default );
+ maModel.mbValidSettings = rAttribs.getBool( XML_usePrinterDefaults, false );
+ maModel.mbUseFirstPage = rAttribs.getBool( XML_useFirstPageNumber, false );
+ maModel.mbBlackWhite = rAttribs.getBool( XML_blackAndWhite, false );
+ maModel.mbDraftQuality = rAttribs.getBool( XML_draft, false );
+}
+
+void PageSettings::importHeaderFooter( const AttributeList& rAttribs )
+{
+ maModel.mbUseEvenHF = rAttribs.getBool( XML_differentOddEven, false );
+ maModel.mbUseFirstHF = rAttribs.getBool( XML_differentFirst, false );
+}
+
+void PageSettings::importHeaderFooterCharacters( std::u16string_view rChars, sal_Int32 nElement )
+{
+ switch( nElement )
+ {
+ case XLS_TOKEN( oddHeader ): maModel.maOddHeader += rChars; break;
+ case XLS_TOKEN( oddFooter ): maModel.maOddFooter += rChars; break;
+ case XLS_TOKEN( evenHeader ): maModel.maEvenHeader += rChars; break;
+ case XLS_TOKEN( evenFooter ): maModel.maEvenFooter += rChars; break;
+ case XLS_TOKEN( firstHeader ): maModel.maFirstHeader += rChars; break;
+ case XLS_TOKEN( firstFooter ): maModel.maFirstFooter += rChars; break;
+ }
+}
+
+void PageSettings::importPicture( const Relations& rRelations, const AttributeList& rAttribs )
+{
+ importPictureData( rRelations, rAttribs.getString( R_TOKEN( id ), OUString() ) );
+}
+
+void PageSettings::importPageMargins( SequenceInputStream& rStrm )
+{
+ maModel.mfLeftMargin = rStrm.readDouble();
+ maModel.mfRightMargin = rStrm.readDouble();
+ maModel.mfTopMargin = rStrm.readDouble();
+ maModel.mfBottomMargin = rStrm.readDouble();
+ maModel.mfHeaderMargin = rStrm.readDouble();
+ maModel.mfFooterMargin = rStrm.readDouble();
+}
+
+void PageSettings::importPrintOptions( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ maModel.mbHorCenter = getFlag( nFlags, BIFF12_PRINTOPT_HORCENTER );
+ maModel.mbVerCenter = getFlag( nFlags, BIFF12_PRINTOPT_VERCENTER );
+ maModel.mbPrintGrid = getFlag( nFlags, BIFF12_PRINTOPT_PRINTGRID );
+ maModel.mbPrintHeadings = getFlag( nFlags, BIFF12_PRINTOPT_PRINTHEADING );
+}
+
+void PageSettings::importPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
+{
+ OUString aRelId;
+ sal_uInt16 nFlags;
+ maModel.mnPaperSize = rStrm.readInt32();
+ maModel.mnScale = rStrm.readInt32();
+ maModel.mnHorPrintRes = rStrm.readInt32();
+ maModel.mnVerPrintRes = rStrm.readInt32();
+ maModel.mnCopies = rStrm.readInt32();
+ maModel.mnFirstPage = rStrm.readInt32();
+ maModel.mnFitToWidth = rStrm.readInt32();
+ maModel.mnFitToHeight = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ rStrm >> aRelId;
+ maModel.setBiffPrintErrors( extractValue< sal_uInt8 >( nFlags, 9, 2 ) );
+ maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( aRelId );
+ maModel.mnOrientation = getFlagValue( nFlags, BIFF12_PAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, BIFF12_PAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) );
+ maModel.mnPageOrder = getFlagValue( nFlags, BIFF12_PAGESETUP_INROWS, XML_overThenDown, XML_downThenOver );
+ maModel.mnCellComments = getFlagValue( nFlags, BIFF12_PAGESETUP_PRINTNOTES, getFlagValue( nFlags, BIFF12_PAGESETUP_NOTES_END, XML_atEnd, XML_asDisplayed ), XML_none );
+ maModel.mbValidSettings = !getFlag( nFlags, BIFF12_PAGESETUP_INVALID );
+ maModel.mbUseFirstPage = getFlag( nFlags, BIFF12_PAGESETUP_USEFIRSTPAGE );
+ maModel.mbBlackWhite = getFlag( nFlags, BIFF12_PAGESETUP_BLACKWHITE );
+ maModel.mbDraftQuality = getFlag( nFlags, BIFF12_PAGESETUP_DRAFTQUALITY );
+}
+
+void PageSettings::importChartPageSetup( const Relations& rRelations, SequenceInputStream& rStrm )
+{
+ OUString aRelId;
+ sal_uInt16 nFirstPage, nFlags;
+ maModel.mnPaperSize = rStrm.readInt32();
+ maModel.mnHorPrintRes = rStrm.readInt32();
+ maModel.mnVerPrintRes = rStrm.readInt32();
+ maModel.mnCopies = rStrm.readInt32();
+ nFirstPage = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+ rStrm >> aRelId;
+ maModel.maBinSettPath = rRelations.getFragmentPathFromRelId( aRelId );
+ maModel.mnFirstPage = nFirstPage; // 16-bit in CHARTPAGESETUP
+ maModel.mnOrientation = getFlagValue( nFlags, BIFF12_CHARTPAGESETUP_DEFAULTORIENT, XML_default, getFlagValue( nFlags, BIFF12_CHARTPAGESETUP_LANDSCAPE, XML_landscape, XML_portrait ) );
+ maModel.mbValidSettings = !getFlag( nFlags, BIFF12_CHARTPAGESETUP_INVALID );
+ maModel.mbUseFirstPage = getFlag( nFlags, BIFF12_CHARTPAGESETUP_USEFIRSTPAGE );
+ maModel.mbBlackWhite = getFlag( nFlags, BIFF12_CHARTPAGESETUP_BLACKWHITE );
+ maModel.mbDraftQuality = getFlag( nFlags, BIFF12_CHARTPAGESETUP_DRAFTQUALITY );
+}
+
+void PageSettings::importHeaderFooter( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ rStrm >> maModel.maOddHeader >> maModel.maOddFooter
+ >> maModel.maEvenHeader >> maModel.maEvenFooter
+ >> maModel.maFirstHeader >> maModel.maFirstFooter;
+ maModel.mbUseEvenHF = getFlag( nFlags, BIFF12_HEADERFOOTER_DIFFEVEN );
+ maModel.mbUseFirstHF = getFlag( nFlags, BIFF12_HEADERFOOTER_DIFFFIRST );
+}
+
+void PageSettings::importPicture( const Relations& rRelations, SequenceInputStream& rStrm )
+{
+ importPictureData( rRelations, BiffHelper::readString( rStrm ) );
+}
+
+void PageSettings::setFitToPagesMode( bool bFitToPages )
+{
+ maModel.mbFitToPages = bFitToPages;
+}
+
+void PageSettings::finalizeImport()
+{
+ OUStringBuffer aStyleNameBuffer( "PageStyle_" );
+ Reference<container::XNamed> xSheetName(getSheet(), UNO_QUERY);
+ if( xSheetName.is() )
+ aStyleNameBuffer.append( xSheetName->getName() );
+ else
+ aStyleNameBuffer.append( static_cast< sal_Int32 >( getSheetIndex() + 1 ) );
+ OUString aStyleName = aStyleNameBuffer.makeStringAndClear();
+
+ Reference<style::XStyle> xStyle = createStyleObject(aStyleName, true);
+ PropertySet aStyleProps( xStyle );
+ getPageSettingsConverter().writePageSettingsProperties( aStyleProps, maModel, getSheetType() );
+
+ // Set page style name to the sheet.
+ SCTAB nTab = getSheetIndex();
+ getScDocument().SetPageStyle(nTab, aStyleName);
+}
+
+void PageSettings::importPictureData( const Relations& rRelations, const OUString& rRelId )
+{
+ OUString aPicturePath = rRelations.getFragmentPathFromRelId(rRelId);
+ if (!aPicturePath.isEmpty())
+ {
+ maModel.mxGraphic = getBaseFilter().getGraphicHelper().importEmbeddedGraphic(aPicturePath);
+ }
+}
+
+namespace {
+
+enum HFPortionId
+{
+ HF_LEFT,
+ HF_CENTER,
+ HF_RIGHT,
+ HF_COUNT
+};
+
+struct HFPortionInfo
+{
+ Reference<text::XText> mxText; /// XText interface of this portion.
+ Reference<text::XTextCursor> mxStart; /// Start position of current text range for formatting.
+ Reference<text::XTextCursor> mxEnd; /// End position of current text range for formatting.
+ double mfTotalHeight; /// Sum of heights of previous lines in points.
+ double mfCurrHeight; /// Height of the current text line in points.
+
+ bool initialize( const Reference<text::XText>& rxText );
+};
+
+}
+
+bool HFPortionInfo::initialize( const Reference<text::XText>& rxText )
+{
+ mfTotalHeight = mfCurrHeight = 0.0;
+ mxText = rxText;
+ if( mxText.is() )
+ {
+ mxStart = mxText->createTextCursor();
+ mxEnd = mxText->createTextCursor();
+ }
+ bool bRet = mxText.is() && mxStart.is() && mxEnd.is();
+ OSL_ENSURE( bRet, "HFPortionInfo::initialize - missing interfaces" );
+ return bRet;
+}
+
+class HeaderFooterParser : public WorkbookHelper
+{
+public:
+ explicit HeaderFooterParser( const WorkbookHelper& rHelper );
+
+ /** Parses the passed string and creates the header/footer contents.
+ @returns The total height of the converted header or footer in points. */
+ double parse(
+ const Reference<sheet::XHeaderFooterContent>& rxContext,
+ const OUString& rData );
+
+private:
+ /** Returns the current edit engine text object. */
+ HFPortionInfo& getPortion() { return maPortions[ meCurrPortion ]; }
+ /** Returns the start cursor of the current text range. */
+ const Reference<text::XTextCursor>& getStartPos() { return getPortion().mxStart; }
+ /** Returns the end cursor of the current text range. */
+ const Reference<text::XTextCursor>& getEndPos() { return getPortion().mxEnd; }
+
+ /** Returns the current line height of the specified portion. */
+ double getCurrHeight( HFPortionId ePortion ) const;
+
+ /** Updates the current line height of the specified portion, using the current font size. */
+ void updateCurrHeight( HFPortionId ePortion );
+ /** Updates the current line height, using the current font size. */
+ void updateCurrHeight();
+
+ /** Sets the font attributes at the current selection. */
+ void setAttributes();
+ /** Appends and clears internal string buffer. */
+ void appendText();
+ /** Appends a line break and adjusts internal text height data. */
+ void appendLineBreak();
+
+ /** Creates a text field from the passed service name. */
+ Reference<text::XTextContent> createField( const OUString& rServiceName ) const;
+ /** Appends the passed text field. */
+ void appendField( const Reference<text::XTextContent>& rxContent );
+
+ /** Sets the passed font name if it is valid. */
+ void convertFontName( const OUString& rStyle );
+ /** Converts a font style given as string. */
+ void convertFontStyle( std::u16string_view rStyle );
+ /** Converts a font color given as string. */
+ void convertFontColor( const OUString& rColor );
+
+ /** Finalizes current portion: sets font attributes and updates text height data. */
+ void finalizePortion();
+ /** Changes current header/footer portion. */
+ void setNewPortion( HFPortionId ePortion );
+
+private:
+ typedef ::std::vector< HFPortionInfo > HFPortionInfoVec;
+
+ const std::set< OString > maBoldNames; /// All names for bold font style in lowercase UTF-8.
+ const std::set< OString > maItalicNames; /// All names for italic font style in lowercase UTF-8.
+ HFPortionInfoVec maPortions;
+ HFPortionId meCurrPortion; /// Identifier of current H/F portion.
+ OUStringBuffer maBuffer; /// Text data to append to current text range.
+ FontModel maFontModel; /// Font attributes of current text range.
+};
+
+namespace {
+
+// different names for bold font style (lowercase)
+const char* const sppcBoldNames[] =
+{
+ "bold",
+ "fett", // German 'bold'
+ "demibold",
+ "halbfett", // German 'demibold'
+ "black",
+ "heavy",
+ "f\303\251lk\303\266v\303\251r" // Hungarian 'bold'
+};
+
+// different names for italic font style (lowercase)
+const char* const sppcItalicNames[] =
+{
+ "italic",
+ "kursiv", // German 'italic'
+ "oblique",
+ "schr\303\204g", // German 'oblique' with uppercase A umlaut
+ "schr\303\244g", // German 'oblique' with lowercase A umlaut
+ "d\305\221lt" // Hungarian 'italic'
+};
+
+} // namespace
+
+constexpr OUStringLiteral gaPageNumberService( u"com.sun.star.text.TextField.PageNumber" );
+constexpr OUStringLiteral gaPageCountService( u"com.sun.star.text.TextField.PageCount" );
+constexpr OUStringLiteral gaSheetNameService( u"com.sun.star.text.TextField.SheetName" );
+constexpr OUStringLiteral gaFileNameService( u"com.sun.star.text.TextField.FileName" );
+constexpr OUStringLiteral gaDateTimeService( u"com.sun.star.text.TextField.DateTime" );
+
+HeaderFooterParser::HeaderFooterParser( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ maBoldNames( sppcBoldNames, sppcBoldNames + SAL_N_ELEMENTS(sppcBoldNames) ),
+ maItalicNames( sppcItalicNames, sppcItalicNames + SAL_N_ELEMENTS(sppcItalicNames) ),
+ maPortions( static_cast< size_t >( HF_COUNT ) ),
+ meCurrPortion( HF_CENTER )
+{
+}
+
+double HeaderFooterParser::parse( const Reference<sheet::XHeaderFooterContent>& rxContext, const OUString& rData )
+{
+ if( !rxContext.is() || rData.isEmpty() ||
+ !maPortions[ HF_LEFT ].initialize( rxContext->getLeftText() ) ||
+ !maPortions[ HF_CENTER ].initialize( rxContext->getCenterText() ) ||
+ !maPortions[ HF_RIGHT ].initialize( rxContext->getRightText() ) )
+ return 0.0;
+
+ meCurrPortion = HF_CENTER;
+ maBuffer.setLength( 0 );
+ maFontModel = getStyles().getDefaultFontModel();
+ OUStringBuffer aFontName; // current font name
+ OUStringBuffer aFontStyle; // current font style
+ sal_Int32 nFontHeight = 0; // current font height
+
+ /** State of the parser. */
+ enum
+ {
+ STATE_TEXT, /// Literal text data.
+ STATE_TOKEN, /// Control token following a '&' character.
+ STATE_FONTNAME, /// Font name ('&' is followed by '"', reads until next '"' or ',').
+ STATE_FONTSTYLE, /// Font style name (font part after ',', reads until next '"').
+ STATE_FONTHEIGHT /// Font height ('&' is followed by num. digits, reads until non-digit).
+ }
+ eState = STATE_TEXT;
+
+ const sal_Unicode* pcChar = rData.getStr();
+ const sal_Unicode* pcEnd = pcChar + rData.getLength();
+ for( ; (pcChar != pcEnd) && (*pcChar != 0); ++pcChar )
+ {
+ sal_Unicode cChar = *pcChar;
+ switch( eState )
+ {
+ case STATE_TEXT:
+ {
+ switch( cChar )
+ {
+ case '&': // new token
+ appendText();
+ eState = STATE_TOKEN;
+ break;
+ case '\n': // line break
+ appendText();
+ appendLineBreak();
+ break;
+ default:
+ maBuffer.append( cChar );
+ }
+ }
+ break;
+
+ case STATE_TOKEN:
+ {
+ // default: back to text mode, may be changed in specific cases
+ eState = STATE_TEXT;
+ // ignore case of token codes
+ if( ('a' <= cChar) && (cChar <= 'z') )
+ cChar = (cChar - 'a') + 'A';
+ switch( cChar )
+ {
+ case '&': maBuffer.append( cChar ); break; // the '&' character
+
+ case 'L': setNewPortion( HF_LEFT ); break; // left portion
+ case 'C': setNewPortion( HF_CENTER ); break; // center portion
+ case 'R': setNewPortion( HF_RIGHT ); break; // right portion
+
+ case 'P': // page number
+ appendField( createField( gaPageNumberService ) );
+ break;
+ case 'N': // total page count
+ appendField( createField( gaPageCountService ) );
+ break;
+ case 'A': // current sheet name
+ appendField( createField( gaSheetNameService ) );
+ break;
+
+ case 'F': // file name
+ {
+ Reference<text::XTextContent> xContent = createField( gaFileNameService );
+ PropertySet aPropSet( xContent );
+ aPropSet.setProperty( PROP_FileFormat, css::text::FilenameDisplayFormat::NAME_AND_EXT );
+ appendField( xContent );
+ }
+ break;
+ case 'Z': // file path (without file name), OOXML, BIFF12, and BIFF8 only
+ {
+ Reference<text::XTextContent> xContent = createField( gaFileNameService );
+ PropertySet aPropSet( xContent );
+ // FilenameDisplayFormat::PATH not supported by Calc
+ aPropSet.setProperty( PROP_FileFormat, css::text::FilenameDisplayFormat::FULL );
+ appendField( xContent );
+ /* path only is not supported -- if we find a '&Z&F'
+ combination for path/name, skip the '&F' part */
+ if( (pcChar + 2 < pcEnd) && (pcChar[ 1 ] == '&') && ((pcChar[ 2 ] == 'f') || (pcChar[ 2 ] == 'F')) )
+ pcChar += 2;
+ }
+ break;
+ case 'D': // date
+ {
+ Reference<text::XTextContent> xContent = createField( gaDateTimeService );
+ PropertySet aPropSet( xContent );
+ aPropSet.setProperty( PROP_IsDate, true );
+ appendField( xContent );
+ }
+ break;
+ case 'T': // time
+ {
+ Reference<text::XTextContent> xContent = createField( gaDateTimeService );
+ PropertySet aPropSet( xContent );
+ aPropSet.setProperty( PROP_IsDate, false );
+ appendField( xContent );
+ }
+ break;
+
+ case 'B': // bold
+ setAttributes();
+ maFontModel.mbBold = !maFontModel.mbBold;
+ break;
+ case 'I': // italic
+ setAttributes();
+ maFontModel.mbItalic = !maFontModel.mbItalic;
+ break;
+ case 'U': // underline
+ setAttributes();
+ maFontModel.mnUnderline = (maFontModel.mnUnderline == XML_single) ? XML_none : XML_single;
+ break;
+ case 'E': // double underline
+ setAttributes();
+ maFontModel.mnUnderline = (maFontModel.mnUnderline == XML_double) ? XML_none : XML_double;
+ break;
+ case 'S': // strikeout
+ setAttributes();
+ maFontModel.mbStrikeout = !maFontModel.mbStrikeout;
+ break;
+ case 'X': // superscript
+ setAttributes();
+ maFontModel.mnEscapement = (maFontModel.mnEscapement == XML_superscript) ? XML_baseline : XML_superscript;
+ break;
+ case 'Y': // subscript
+ setAttributes();
+ maFontModel.mnEscapement = (maFontModel.mnEscapement == XML_subscript) ? XML_baseline : XML_subscript;
+ break;
+ case 'O': // outlined
+ setAttributes();
+ maFontModel.mbOutline = !maFontModel.mbOutline;
+ break;
+ case 'H': // shadow
+ setAttributes();
+ maFontModel.mbShadow = !maFontModel.mbShadow;
+ break;
+
+ case 'K': // text color (not in BIFF)
+ if( pcChar + 6 < pcEnd )
+ {
+ setAttributes();
+ // eat the following 6 characters
+ convertFontColor( OUString( pcChar + 1, 6 ) );
+ pcChar += 6;
+ }
+ break;
+
+ case '\"': // font name
+ aFontName.setLength( 0 );
+ aFontStyle.setLength( 0 );
+ eState = STATE_FONTNAME;
+ break;
+ default:
+ if( ('0' <= cChar) && (cChar <= '9') ) // font size
+ {
+ nFontHeight = cChar - '0';
+ eState = STATE_FONTHEIGHT;
+ }
+ }
+ }
+ break;
+
+ case STATE_FONTNAME:
+ {
+ switch( cChar )
+ {
+ case '\"':
+ setAttributes();
+ convertFontName( aFontName.makeStringAndClear() );
+ eState = STATE_TEXT;
+ break;
+ case ',':
+ eState = STATE_FONTSTYLE;
+ break;
+ default:
+ aFontName.append( cChar );
+ }
+ }
+ break;
+
+ case STATE_FONTSTYLE:
+ {
+ switch( cChar )
+ {
+ case '\"':
+ setAttributes();
+ convertFontName( aFontName.makeStringAndClear() );
+ convertFontStyle( aFontStyle );
+ aFontStyle.setLength(0);
+ eState = STATE_TEXT;
+ break;
+ default:
+ aFontStyle.append( cChar );
+ }
+ }
+ break;
+
+ case STATE_FONTHEIGHT:
+ {
+ if( ('0' <= cChar) && (cChar <= '9') )
+ {
+ if( nFontHeight >= 0 )
+ {
+ nFontHeight *= 10;
+ nFontHeight += (cChar - '0');
+ if( nFontHeight > 1000 )
+ nFontHeight = -1;
+ }
+ }
+ else
+ {
+ if( nFontHeight > 0 )
+ {
+ setAttributes();
+ maFontModel.mfHeight = nFontHeight;
+ }
+ --pcChar;
+ eState = STATE_TEXT;
+ }
+ }
+ break;
+ }
+ }
+
+ // finalize
+ finalizePortion();
+ maPortions[ HF_LEFT ].mfTotalHeight += getCurrHeight( HF_LEFT );
+ maPortions[ HF_CENTER ].mfTotalHeight += getCurrHeight( HF_CENTER );
+ maPortions[ HF_RIGHT ].mfTotalHeight += getCurrHeight( HF_RIGHT );
+
+ return ::std::max( maPortions[ HF_LEFT ].mfTotalHeight,
+ ::std::max( maPortions[ HF_CENTER ].mfTotalHeight, maPortions[ HF_RIGHT ].mfTotalHeight ) );
+}
+
+// private --------------------------------------------------------------------
+
+double HeaderFooterParser::getCurrHeight( HFPortionId ePortion ) const
+{
+ double fMaxHt = maPortions[ ePortion ].mfCurrHeight;
+ return (fMaxHt == 0.0) ? maFontModel.mfHeight : fMaxHt;
+}
+
+void HeaderFooterParser::updateCurrHeight( HFPortionId ePortion )
+{
+ double& rfMaxHt = maPortions[ ePortion ].mfCurrHeight;
+ rfMaxHt = ::std::max( rfMaxHt, maFontModel.mfHeight );
+}
+
+void HeaderFooterParser::updateCurrHeight()
+{
+ updateCurrHeight( meCurrPortion );
+}
+
+void HeaderFooterParser::setAttributes()
+{
+ Reference<text::XTextRange> xRange = getStartPos();
+ getEndPos()->gotoRange( xRange, false );
+ getEndPos()->gotoEnd( true );
+ if( !getEndPos()->isCollapsed() )
+ {
+ Font aFont( *this, maFontModel );
+ aFont.finalizeImport();
+ PropertySet aPropSet( getEndPos() );
+ aFont.writeToPropertySet( aPropSet );
+ getStartPos()->gotoEnd( false );
+ getEndPos()->gotoEnd( false );
+ }
+}
+
+void HeaderFooterParser::appendText()
+{
+ if( !maBuffer.isEmpty() )
+ {
+ getEndPos()->gotoEnd( false );
+ getEndPos()->setString( maBuffer.makeStringAndClear() );
+ updateCurrHeight();
+ }
+}
+
+void HeaderFooterParser::appendLineBreak()
+{
+ getEndPos()->gotoEnd( false );
+ getEndPos()->setString( OUString( '\n' ) );
+ getPortion().mfTotalHeight += getCurrHeight( meCurrPortion ); // add the current line height.
+ getPortion().mfCurrHeight = 0;
+}
+
+Reference<text::XTextContent> HeaderFooterParser::createField( const OUString& rServiceName ) const
+{
+ Reference<text::XTextContent> xContent;
+ try
+ {
+ xContent.set( getBaseFilter().getModelFactory()->createInstance( rServiceName ), UNO_QUERY_THROW );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( OStringBuffer( "HeaderFooterParser::createField - error while creating text field \"" ).
+ append( OUStringToOString( rServiceName, RTL_TEXTENCODING_ASCII_US ) ).
+ append( '"' ).getStr() );
+ }
+ return xContent;
+}
+
+void HeaderFooterParser::appendField( const Reference<text::XTextContent>& rxContent )
+{
+ getEndPos()->gotoEnd( false );
+ try
+ {
+ Reference<text::XTextRange> xRange( getEndPos(), UNO_QUERY_THROW );
+ getPortion().mxText->insertTextContent( xRange, rxContent, false );
+ updateCurrHeight();
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void HeaderFooterParser::convertFontName( const OUString& rName )
+{
+ if( !rName.isEmpty() )
+ {
+ // single dash is document default font
+ if( (rName.getLength() == 1) && (rName[ 0 ] == '-') )
+ maFontModel.maName = getStyles().getDefaultFontModel().maName;
+ else
+ maFontModel.maName = rName;
+ }
+}
+
+void HeaderFooterParser::convertFontStyle( std::u16string_view rStyle )
+{
+ maFontModel.mbBold = maFontModel.mbItalic = false;
+ if (rStyle.empty())
+ return;
+ for( sal_Int32 nPos{ 0 }; nPos>=0; )
+ {
+ OString aToken = OUStringToOString( o3tl::getToken(rStyle, 0, ' ', nPos ), RTL_TEXTENCODING_UTF8 ).toAsciiLowerCase();
+ if( !aToken.isEmpty() )
+ {
+ if( maBoldNames.count( aToken ) > 0 )
+ maFontModel.mbBold = true;
+ else if( maItalicNames.count( aToken ) > 0 )
+ maFontModel.mbItalic = true;
+ }
+ }
+}
+
+void HeaderFooterParser::convertFontColor( const OUString& rColor )
+{
+ OSL_ENSURE( rColor.getLength() == 6, "HeaderFooterParser::convertFontColor - invalid font color code" );
+ if( (rColor[ 2 ] == '+') || (rColor[ 2 ] == '-') )
+ // theme color: TTSNNN (TT = decimal theme index, S = +/-, NNN = decimal tint/shade in percent)
+ maFontModel.maColor.setTheme(
+ o3tl::toInt32(rColor.subView( 0, 2 )),
+ static_cast< double >( o3tl::toInt32(rColor.subView( 2 )) ) / 100.0 );
+ else
+ // RGB color: RRGGBB
+ maFontModel.maColor.setRgb( ::Color(ColorTransparency, rColor.toUInt32( 16 )) );
+}
+
+void HeaderFooterParser::finalizePortion()
+{
+ appendText();
+ setAttributes();
+}
+
+void HeaderFooterParser::setNewPortion( HFPortionId ePortion )
+{
+ if( ePortion != meCurrPortion )
+ {
+ finalizePortion();
+ meCurrPortion = ePortion;
+ maFontModel = getStyles().getDefaultFontModel();
+ }
+}
+
+PageSettingsConverter::HFHelperData::HFHelperData( sal_Int32 nLeftPropId, sal_Int32 nRightPropId, sal_Int32 nFirstPropId ) :
+ mnLeftPropId( nLeftPropId ),
+ mnRightPropId( nRightPropId ),
+ mnFirstPropId( nFirstPropId ),
+ mnHeight( 0 ),
+ mnBodyDist( 0 ),
+ mbHasContent( false ),
+ mbShareOddEven( false ),
+ mbShareFirst( false ),
+ mbDynamicHeight( false )
+{
+}
+
+PageSettingsConverter::PageSettingsConverter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mxHFParser( new HeaderFooterParser( rHelper ) ),
+ maHeaderData( PROP_LeftPageHeaderContent, PROP_RightPageHeaderContent, PROP_FirstPageHeaderContent ),
+ maFooterData( PROP_LeftPageFooterContent, PROP_RightPageFooterContent, PROP_FirstPageFooterContent )
+{
+}
+
+PageSettingsConverter::~PageSettingsConverter()
+{
+}
+
+void PageSettingsConverter::writePageSettingsProperties(
+ PropertySet& rPropSet, const PageSettingsModel& rModel, WorksheetType eSheetType )
+{
+ // special handling for chart sheets
+ bool bChartSheet = eSheetType == WorksheetType::Chart;
+
+ // printout scaling
+ if( bChartSheet )
+ {
+ // always fit chart sheet to 1 page
+ rPropSet.setProperty< sal_Int16 >( PROP_ScaleToPages, 1 );
+ }
+ else if( rModel.mbFitToPages )
+ {
+ // fit to number of pages
+ rPropSet.setProperty( PROP_ScaleToPagesX, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToWidth, 0, 1000 ) );
+ rPropSet.setProperty( PROP_ScaleToPagesY, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnFitToHeight, 0, 1000 ) );
+ }
+ else
+ {
+ // scale may be 0 which indicates uninitialized
+ sal_Int16 nScale = (rModel.mnScale > 0) ? getLimitedValue< sal_Int16, sal_Int32 >( rModel.mnScale, 10, 400 ) : 100;
+ rPropSet.setProperty( PROP_PageScale, nScale );
+ }
+
+ // paper orientation
+ bool bLandscape = rModel.mnOrientation == XML_landscape;
+ // default orientation for current sheet type (chart sheets default to landscape)
+ if( bChartSheet && ( !rModel.mbValidSettings || (rModel.mnOrientation == XML_default) ) )
+ bLandscape = true;
+
+ // paper size
+ if( !rModel.mbValidSettings )
+ {
+ awt::Size aSize;
+ bool bValid = false;
+
+ if( 0 < rModel.mnPaperSize )
+ {
+ const msfilter::util::ApiPaperSize& rPaperSize = msfilter::util::PaperSizeConv::getApiSizeForMSPaperSizeIndex( rModel.mnPaperSize );
+ aSize = awt::Size( rPaperSize.mnWidth, rPaperSize.mnHeight );
+ bValid = ( rPaperSize.mnWidth != 0 && rPaperSize.mnHeight != 0 );
+ }
+ if( rModel.mnPaperWidth > 0 && rModel.mnPaperHeight > 0 )
+ {
+ aSize = awt::Size( rModel.mnPaperWidth, rModel.mnPaperHeight );
+ bValid = true;
+ }
+
+ if( bValid )
+ {
+ if( bLandscape )
+ ::std::swap( aSize.Width, aSize.Height );
+ rPropSet.setProperty( PROP_Size, aSize );
+ }
+ }
+
+ // header/footer
+ convertHeaderFooterData( rPropSet, maHeaderData, rModel.maOddHeader, rModel.maEvenHeader, rModel.maFirstHeader, rModel.mbUseEvenHF, rModel.mbUseFirstHF, rModel.mfTopMargin, rModel.mfHeaderMargin );
+ convertHeaderFooterData( rPropSet, maFooterData, rModel.maOddFooter, rModel.maEvenFooter, rModel.maFirstFooter, rModel.mbUseEvenHF, rModel.mbUseFirstHF, rModel.mfBottomMargin, rModel.mfFooterMargin );
+
+ // write all properties to property set
+ PropertyMap aPropMap;
+ aPropMap.setProperty( PROP_IsLandscape, bLandscape);
+ aPropMap.setProperty( PROP_FirstPageNumber, getLimitedValue< sal_Int16, sal_Int32 >( rModel.mbUseFirstPage ? rModel.mnFirstPage : 0, 0, 9999 ));
+ aPropMap.setProperty( PROP_PrintDownFirst, (rModel.mnPageOrder == XML_downThenOver));
+ aPropMap.setProperty( PROP_PrintAnnotations, (rModel.mnCellComments == XML_asDisplayed));
+ aPropMap.setProperty( PROP_CenterHorizontally, rModel.mbHorCenter);
+ aPropMap.setProperty( PROP_CenterVertically, rModel.mbVerCenter);
+ aPropMap.setProperty( PROP_PrintGrid, (!bChartSheet && rModel.mbPrintGrid)); // no gridlines in chart sheets
+ aPropMap.setProperty( PROP_PrintHeaders, (!bChartSheet && rModel.mbPrintHeadings)); // no column/row headings in chart sheets
+ aPropMap.setProperty<sal_Int32>( PROP_LeftMargin, std::round(o3tl::convert( rModel.mfLeftMargin, o3tl::Length::in, o3tl::Length::mm100 )));
+ aPropMap.setProperty<sal_Int32>( PROP_RightMargin, std::round(o3tl::convert( rModel.mfRightMargin, o3tl::Length::in, o3tl::Length::mm100 )));
+ // #i23296# In Calc, "TopMargin" property is distance to top of header if enabled
+ aPropMap.setProperty<sal_Int32>( PROP_TopMargin, std::round(o3tl::convert( maHeaderData.mbHasContent ? rModel.mfHeaderMargin : rModel.mfTopMargin, o3tl::Length::in, o3tl::Length::mm100 )));
+ // #i23296# In Calc, "BottomMargin" property is distance to bottom of footer if enabled
+ aPropMap.setProperty<sal_Int32>( PROP_BottomMargin, std::round(o3tl::convert( maFooterData.mbHasContent ? rModel.mfFooterMargin : rModel.mfBottomMargin, o3tl::Length::in, o3tl::Length::mm100 )));
+ aPropMap.setProperty( PROP_HeaderIsOn, maHeaderData.mbHasContent);
+ aPropMap.setProperty( PROP_HeaderIsShared, maHeaderData.mbShareOddEven);
+ aPropMap.setProperty( PROP_FirstPageHeaderIsShared, maHeaderData.mbShareFirst);
+ aPropMap.setProperty( PROP_HeaderIsDynamicHeight, maHeaderData.mbDynamicHeight);
+ aPropMap.setProperty( PROP_HeaderHeight, maHeaderData.mnHeight);
+ aPropMap.setProperty( PROP_HeaderBodyDistance, maHeaderData.mnBodyDist);
+ aPropMap.setProperty( PROP_FooterIsOn, maFooterData.mbHasContent);
+ aPropMap.setProperty( PROP_FooterIsShared, maFooterData.mbShareOddEven);
+ aPropMap.setProperty( PROP_FirstPageFooterIsShared, maFooterData.mbShareFirst);
+ aPropMap.setProperty( PROP_FooterIsDynamicHeight, maFooterData.mbDynamicHeight);
+ aPropMap.setProperty( PROP_FooterHeight, maFooterData.mnHeight);
+ aPropMap.setProperty( PROP_FooterBodyDistance, maFooterData.mnBodyDist);
+ // background image
+ if (rModel.mxGraphic.is())
+ {
+ aPropMap.setProperty(PROP_BackGraphic, rModel.mxGraphic);
+ aPropMap.setProperty(PROP_BackGraphicLocation, css::style::GraphicLocation_TILED);
+ }
+
+ rPropSet.setProperties( aPropMap );
+}
+
+void PageSettingsConverter::convertHeaderFooterData(
+ PropertySet& rPropSet, HFHelperData& orHFData,
+ const OUString& rOddContent, const OUString& rEvenContent, const OUString& rFirstContent,
+ bool bUseEvenContent, bool bUseFirstContent,
+ double fPageMargin, double fContentMargin )
+{
+ bool bHasOddContent = !rOddContent.isEmpty();
+ bool bHasEvenContent = bUseEvenContent && !rEvenContent.isEmpty();
+ bool bHasFirstContent = bUseFirstContent && !rFirstContent.isEmpty();
+
+ sal_Int32 nOddHeight = bHasOddContent ? writeHeaderFooter( rPropSet, orHFData.mnRightPropId, rOddContent ) : 0;
+ sal_Int32 nEvenHeight = bHasEvenContent ? writeHeaderFooter( rPropSet, orHFData.mnLeftPropId, rEvenContent ) : 0;
+ sal_Int32 nFirstHeight = bHasFirstContent ? writeHeaderFooter( rPropSet, orHFData.mnFirstPropId, rFirstContent ) : 0;
+
+ orHFData.mnHeight = 750;
+ orHFData.mnBodyDist = 250;
+ orHFData.mbHasContent = bHasOddContent || bHasEvenContent || bHasFirstContent;
+ orHFData.mbShareOddEven = !bUseEvenContent;
+ orHFData.mbShareFirst = !bUseFirstContent;
+ orHFData.mbDynamicHeight = true;
+
+ if( !orHFData.mbHasContent )
+ return;
+
+ // use maximum height of odd/even/first header/footer
+ orHFData.mnHeight = ::std::max( ::std::max( nOddHeight, nEvenHeight ), nFirstHeight );
+ /* Calc contains distance between bottom of header and top of page
+ body in "HeaderBodyDistance" property, and distance between bottom
+ of page body and top of footer in "FooterBodyDistance" property */
+ orHFData.mnBodyDist = std::round(o3tl::convert( fPageMargin - fContentMargin, o3tl::Length::in, o3tl::Length::mm100 )) - orHFData.mnHeight;
+ /* #i23296# Distance less than 0 means, header or footer overlays page
+ body. As this is not possible in Calc, set fixed header or footer
+ height (crop header/footer) to get correct top position of page body. */
+ orHFData.mbDynamicHeight = orHFData.mnBodyDist >= 0;
+ /* "HeaderHeight" property is in fact distance from top of header to
+ top of page body (including "HeaderBodyDistance").
+ "FooterHeight" property is in fact distance from bottom of page
+ body to bottom of footer (including "FooterBodyDistance"). */
+ orHFData.mnHeight += orHFData.mnBodyDist;
+ // negative body distance not allowed
+ orHFData.mnBodyDist = ::std::max< sal_Int32 >( orHFData.mnBodyDist, 0 );
+}
+
+sal_Int32 PageSettingsConverter::writeHeaderFooter(
+ PropertySet& rPropSet, sal_Int32 nPropId, const OUString& rContent )
+{
+ OSL_ENSURE( !rContent.isEmpty(), "PageSettingsConverter::writeHeaderFooter - empty h/f string found" );
+ sal_Int32 nHeight = 0;
+ if( !rContent.isEmpty() )
+ {
+ Reference<sheet::XHeaderFooterContent> xHFContent(rPropSet.getAnyProperty(nPropId), UNO_QUERY);
+ if( xHFContent.is() )
+ {
+ double fTotalHeight = mxHFParser->parse( xHFContent, rContent );
+ rPropSet.setProperty( nPropId, xHFContent );
+ nHeight = std::round(o3tl::convert(fTotalHeight, o3tl::Length::pt, o3tl::Length::mm100));
+ }
+ }
+ return nHeight;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pivotcachebuffer.cxx b/sc/source/filter/oox/pivotcachebuffer.cxx
new file mode 100644
index 000000000..b1b12ba43
--- /dev/null
+++ b/sc/source/filter/oox/pivotcachebuffer.cxx
@@ -0,0 +1,1225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pivotcachebuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupInfo.hpp>
+#include <com/sun/star/sheet/XDataPilotFieldGrouping.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <tools/diagnose_ex.h>
+#include <defnamesbuffer.hxx>
+#include <pivotcachefragment.hxx>
+#include <sheetdatabuffer.hxx>
+#include <tablebuffer.hxx>
+#include <unitconverter.hxx>
+#include <worksheetbuffer.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <tools/datetime.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+using ::oox::core::Relations;
+
+namespace {
+
+const sal_uInt16 BIFF12_PCDFIELD_SERVERFIELD = 0x0001;
+const sal_uInt16 BIFF12_PCDFIELD_NOUNIQUEITEMS = 0x0002;
+const sal_uInt16 BIFF12_PCDFIELD_DATABASEFIELD = 0x0004;
+const sal_uInt16 BIFF12_PCDFIELD_HASCAPTION = 0x0008;
+const sal_uInt16 BIFF12_PCDFIELD_MEMBERPROPFIELD = 0x0010;
+const sal_uInt16 BIFF12_PCDFIELD_HASFORMULA = 0x0100;
+const sal_uInt16 BIFF12_PCDFIELD_HASPROPERTYNAME = 0x0200;
+
+const sal_uInt16 BIFF12_PCDFSITEMS_HASSEMIMIXED = 0x0001;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASNONDATE = 0x0002;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASDATE = 0x0004;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASSTRING = 0x0008;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASBLANK = 0x0010;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASMIXED = 0x0020;
+const sal_uInt16 BIFF12_PCDFSITEMS_ISNUMERIC = 0x0040;
+const sal_uInt16 BIFF12_PCDFSITEMS_ISINTEGER = 0x0080;
+const sal_uInt16 BIFF12_PCDFSITEMS_HASLONGTEXT = 0x0200;
+
+const sal_uInt16 BIFF12_PCITEM_ARRAY_DOUBLE = 0x0001;
+const sal_uInt16 BIFF12_PCITEM_ARRAY_STRING = 0x0002;
+const sal_uInt16 BIFF12_PCITEM_ARRAY_ERROR = 0x0010;
+const sal_uInt16 BIFF12_PCITEM_ARRAY_DATE = 0x0020;
+
+const sal_uInt8 BIFF12_PCDFRANGEPR_AUTOSTART = 0x01;
+const sal_uInt8 BIFF12_PCDFRANGEPR_AUTOEND = 0x02;
+const sal_uInt8 BIFF12_PCDFRANGEPR_DATEGROUP = 0x04;
+
+const sal_uInt8 BIFF12_PCDEFINITION_SAVEDATA = 0x01;
+const sal_uInt8 BIFF12_PCDEFINITION_INVALID = 0x02;
+const sal_uInt8 BIFF12_PCDEFINITION_REFRESHONLOAD = 0x04;
+const sal_uInt8 BIFF12_PCDEFINITION_OPTIMIZEMEMORY = 0x08;
+const sal_uInt8 BIFF12_PCDEFINITION_ENABLEREFRESH = 0x10;
+const sal_uInt8 BIFF12_PCDEFINITION_BACKGROUNDQUERY = 0x20;
+const sal_uInt8 BIFF12_PCDEFINITION_UPGRADEONREFR = 0x40;
+const sal_uInt8 BIFF12_PCDEFINITION_TUPLECACHE = 0x80;
+
+const sal_uInt8 BIFF12_PCDEFINITION_HASUSERNAME = 0x01;
+const sal_uInt8 BIFF12_PCDEFINITION_HASRELID = 0x02;
+const sal_uInt8 BIFF12_PCDEFINITION_SUPPORTSUBQUERY = 0x04;
+const sal_uInt8 BIFF12_PCDEFINITION_SUPPORTDRILL = 0x08;
+
+const sal_uInt8 BIFF12_PCDWBSOURCE_HASRELID = 0x01;
+const sal_uInt8 BIFF12_PCDWBSOURCE_HASSHEET = 0x02;
+
+
+/** Adjusts the weird date format read from binary streams.
+
+ Dates before 1900-Mar-01 are stored including the non-existing leap day
+ 1900-02-29. tools::Time values (without date) are stored as times of day
+ 1900-Jan-00. Nothing has to be done when the workbook is stored in 1904
+ date mode (dates before 1904-Jan-01 will not occur in this case).
+ */
+void lclAdjustBinDateTime( css::util::DateTime& orDateTime )
+{
+ if( (orDateTime.Year == 1900) && (orDateTime.Month <= 2) )
+ {
+ OSL_ENSURE( (orDateTime.Month == 1) || ((orDateTime.Month == 2) && (orDateTime.Day > 0)), "lclAdjustBinDateTime - invalid date" );
+ switch( orDateTime.Month )
+ {
+ case 2: if( orDateTime.Day > 1 ) --orDateTime.Day; else { orDateTime.Day += 30; --orDateTime.Month; } break;
+ case 1: if( orDateTime.Day > 1 ) --orDateTime.Day; else { orDateTime.Day += 30; orDateTime.Month = 12; --orDateTime.Year; } break;
+ }
+ }
+}
+
+} // namespace
+
+PivotCacheItem::PivotCacheItem() :
+ mnType( XML_m ), mbUnused( false )
+{
+}
+
+void PivotCacheItem::readString( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getXString( XML_v, OUString() );
+ mnType = XML_s;
+}
+
+void PivotCacheItem::readNumeric( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getDouble( XML_v, 0.0 );
+ mnType = XML_n;
+ mbUnused = rAttribs.getBool( XML_u, false );
+}
+
+void PivotCacheItem::readDate( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getDateTime( XML_v, css::util::DateTime() );
+ mnType = XML_d;
+}
+
+void PivotCacheItem::readBool( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getBool( XML_v, false );
+ mnType = XML_b;
+}
+
+void PivotCacheItem::readError( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getXString( XML_v, OUString() );
+ mnType = XML_e;
+}
+
+void PivotCacheItem::readIndex( const AttributeList& rAttribs )
+{
+ maValue <<= rAttribs.getInteger( XML_v, -1 );
+ mnType = XML_x;
+}
+
+void PivotCacheItem::readString( SequenceInputStream& rStrm )
+{
+ maValue <<= BiffHelper::readString( rStrm );
+ mnType = XML_s;
+}
+
+void PivotCacheItem::readDouble( SequenceInputStream& rStrm )
+{
+ maValue <<= rStrm.readDouble();
+ mnType = XML_n;
+}
+
+void PivotCacheItem::readDate( SequenceInputStream& rStrm )
+{
+ css::util::DateTime aDateTime;
+ aDateTime.Year = rStrm.readuInt16();
+ aDateTime.Month = rStrm.readuInt16();
+ aDateTime.Day = rStrm.readuInt8();
+ aDateTime.Hours = rStrm.readuInt8();
+ aDateTime.Minutes = rStrm.readuInt8();
+ aDateTime.Seconds = rStrm.readuInt8();
+ lclAdjustBinDateTime( aDateTime );
+ maValue <<= aDateTime;
+ mnType = XML_d;
+}
+
+void PivotCacheItem::readBool( SequenceInputStream& rStrm )
+{
+ maValue <<= (rStrm.readuInt8() != 0);
+ mnType = XML_b;
+}
+
+void PivotCacheItem::readError(SequenceInputStream& rStrm, const UnitConverter& rUnitConverter)
+{
+ maValue <<= rUnitConverter.calcErrorString(rStrm.readuInt8());
+ mnType = XML_e;
+}
+
+void PivotCacheItem::readIndex( SequenceInputStream& rStrm )
+{
+ maValue <<= rStrm.readInt32();
+ mnType = XML_x;
+}
+
+void PivotCacheItem::setStringValue( const OUString& sString )
+{
+ mnType = XML_s;
+ maValue <<= sString;
+}
+
+OUString PivotCacheItem::getName() const
+{
+ switch( mnType )
+ {
+ case XML_m: return OUString();
+ case XML_s: return maValue.get< OUString >();
+ case XML_n: return OUString::number( maValue.get< double >() ); // !TODO
+ case XML_i: return OUString::number( maValue.get< sal_Int32 >() );
+ case XML_d: return OUString(); // !TODO
+ case XML_b: return OUString::boolean( maValue.get< bool >() ); // !TODO
+ case XML_e: return OUString(); // !TODO
+ }
+ OSL_FAIL( "PivotCacheItem::getName - invalid data type" );
+ return OUString();
+}
+
+OUString PivotCacheItem::getFormattedName(const ScDPSaveDimension& rSaveDim, ScDPObject* pObj, const DateTime& rNullDate) const
+{
+ switch( mnType )
+ {
+ case XML_m: return OUString();
+ case XML_s: return maValue.get< OUString >();
+ case XML_n: return pObj->GetFormattedString(rSaveDim.GetName(), maValue.get<double>());
+ case XML_i: return pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(maValue.get< sal_Int32 >()));
+ case XML_b: return pObj->GetFormattedString(rSaveDim.GetName(), static_cast<double>(maValue.get< bool >()));
+ case XML_d: return pObj->GetFormattedString(rSaveDim.GetName(), maValue.get< css::util::DateTime >() - rNullDate);
+ case XML_e: return maValue.get< OUString >();
+ }
+ OSL_FAIL( "PivotCacheItem::getFormattedName - invalid data type" );
+ return OUString();
+}
+
+PivotCacheItemList::PivotCacheItemList( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void PivotCacheItemList::importItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ PivotCacheItem& rItem = createItem();
+ switch( nElement )
+ {
+ case XLS_TOKEN( m ): break;
+ case XLS_TOKEN( s ): rItem.readString( rAttribs ); break;
+ case XLS_TOKEN( n ): rItem.readNumeric( rAttribs ); break;
+ case XLS_TOKEN( d ): rItem.readDate( rAttribs ); break;
+ case XLS_TOKEN( b ): rItem.readBool( rAttribs ); break;
+ case XLS_TOKEN( e ): rItem.readError( rAttribs ); break;
+ default: OSL_FAIL( "PivotCacheItemList::importItem - unknown element type" );
+ }
+}
+
+void PivotCacheItemList::importItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ if( nRecId == BIFF12_ID_PCITEM_ARRAY )
+ {
+ importArray( rStrm );
+ return;
+ }
+
+ PivotCacheItem& rItem = createItem();
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCITEM_MISSING:
+ case BIFF12_ID_PCITEMA_MISSING: break;
+ case BIFF12_ID_PCITEM_STRING:
+ case BIFF12_ID_PCITEMA_STRING: rItem.readString( rStrm ); break;
+ case BIFF12_ID_PCITEM_DOUBLE:
+ case BIFF12_ID_PCITEMA_DOUBLE: rItem.readDouble( rStrm ); break;
+ case BIFF12_ID_PCITEM_DATE:
+ case BIFF12_ID_PCITEMA_DATE: rItem.readDate( rStrm ); break;
+ case BIFF12_ID_PCITEM_BOOL:
+ case BIFF12_ID_PCITEMA_BOOL: rItem.readBool( rStrm ); break;
+ case BIFF12_ID_PCITEM_ERROR:
+ case BIFF12_ID_PCITEMA_ERROR: rItem.readError(rStrm, getUnitConverter()); break;
+ default: OSL_FAIL( "PivotCacheItemList::importItem - unknown record type" );
+ }
+}
+
+const PivotCacheItem* PivotCacheItemList::getCacheItem( sal_Int32 nItemIdx ) const
+{
+ return ContainerHelper::getVectorElement( maItems, nItemIdx );
+}
+
+void PivotCacheItemList::applyItemCaptions( const IdCaptionPairList& vCaptions )
+{
+ for( const auto& [rId, rCaption] : vCaptions )
+ {
+ if ( o3tl::make_unsigned( rId ) < maItems.size() )
+ maItems[ rId ].setStringValue( rCaption );
+ }
+}
+
+void PivotCacheItemList::getCacheItemNames( ::std::vector< OUString >& orItemNames ) const
+{
+ orItemNames.clear();
+ orItemNames.reserve( maItems.size() );
+ for( const auto& rItem : maItems )
+ orItemNames.push_back( rItem.getName() );
+}
+
+// private --------------------------------------------------------------------
+
+PivotCacheItem& PivotCacheItemList::createItem()
+{
+ maItems.emplace_back();
+ return maItems.back();
+}
+
+void PivotCacheItemList::importArray( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nType = rStrm.readuInt16();
+ sal_Int32 nCount = rStrm.readInt32();
+ for( sal_Int32 nIdx = 0; !rStrm.isEof() && (nIdx < nCount); ++nIdx )
+ {
+ switch( nType )
+ {
+ case BIFF12_PCITEM_ARRAY_DOUBLE: createItem().readDouble( rStrm ); break;
+ case BIFF12_PCITEM_ARRAY_STRING: createItem().readString( rStrm ); break;
+ case BIFF12_PCITEM_ARRAY_ERROR: createItem().readError(rStrm, getUnitConverter()); break;
+ case BIFF12_PCITEM_ARRAY_DATE: createItem().readDate( rStrm ); break;
+ default:
+ OSL_FAIL( "PivotCacheItemList::importArray - unknown data type" );
+ return;
+ }
+ }
+}
+
+PCFieldModel::PCFieldModel() :
+ mnNumFmtId( 0 ),
+ mnSqlType( 0 ),
+ mnHierarchy( 0 ),
+ mnLevel( 0 ),
+ mnMappingCount( 0 ),
+ mbDatabaseField( true ),
+ mbServerField( false ),
+ mbUniqueList( true ),
+ mbMemberPropField( false )
+{
+}
+
+PCSharedItemsModel::PCSharedItemsModel() :
+ mbHasSemiMixed( true ),
+ mbHasNonDate( true ),
+ mbHasDate( false ),
+ mbHasString( true ),
+ mbHasBlank( false ),
+ mbHasMixed( false ),
+ mbIsNumeric( false ),
+ mbIsInteger( false ),
+ mbHasLongText( false )
+{
+}
+
+PCFieldGroupModel::PCFieldGroupModel() :
+ mfStartValue( 0.0 ),
+ mfEndValue( 0.0 ),
+ mfInterval( 1.0 ),
+ mnParentField( -1 ),
+ mnBaseField( -1 ),
+ mnGroupBy( XML_range ),
+ mbRangeGroup( false ),
+ mbDateGroup( false ),
+ mbAutoStart( true ),
+ mbAutoEnd( true )
+{
+}
+
+void PCFieldGroupModel::setBiffGroupBy( sal_uInt8 nGroupBy )
+{
+ static const sal_Int32 spnGroupBy[] = { XML_range,
+ XML_seconds, XML_minutes, XML_hours, XML_days, XML_months, XML_quarters, XML_years };
+ mnGroupBy = STATIC_ARRAY_SELECT( spnGroupBy, nGroupBy, XML_range );
+}
+
+PivotCacheField::PivotCacheField( const WorkbookHelper& rHelper, bool bIsDatabaseField ) :
+ WorkbookHelper( rHelper ),
+ maSharedItems( rHelper ),
+ maGroupItems( rHelper )
+{
+ maFieldModel.mbDatabaseField = bIsDatabaseField;
+}
+
+void PivotCacheField::importCacheField( const AttributeList& rAttribs )
+{
+ maFieldModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maFieldModel.maCaption = rAttribs.getXString( XML_caption, OUString() );
+ maFieldModel.maPropertyName = rAttribs.getXString( XML_propertyName, OUString() );
+ maFieldModel.maFormula = rAttribs.getXString( XML_formula, OUString() );
+ maFieldModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ maFieldModel.mnSqlType = rAttribs.getInteger( XML_sqlType, 0 );
+ maFieldModel.mnHierarchy = rAttribs.getInteger( XML_hierarchy, 0 );
+ maFieldModel.mnLevel = rAttribs.getInteger( XML_level, 0 );
+ maFieldModel.mnMappingCount = rAttribs.getInteger( XML_mappingCount, 0 );
+ maFieldModel.mbDatabaseField = rAttribs.getBool( XML_databaseField, true );
+ maFieldModel.mbServerField = rAttribs.getBool( XML_serverField, false );
+ maFieldModel.mbUniqueList = rAttribs.getBool( XML_uniqueList, true );
+ maFieldModel.mbMemberPropField = rAttribs.getBool( XML_memberPropertyField, false );
+}
+
+void PivotCacheField::importSharedItems( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( maSharedItems.empty(), "PivotCacheField::importSharedItems - multiple shared items elements" );
+ maSharedItemsModel.mbHasSemiMixed = rAttribs.getBool( XML_containsSemiMixedTypes, true );
+ maSharedItemsModel.mbHasNonDate = rAttribs.getBool( XML_containsNonDate, true );
+ maSharedItemsModel.mbHasDate = rAttribs.getBool( XML_containsDate, false );
+ maSharedItemsModel.mbHasString = rAttribs.getBool( XML_containsString, true );
+ maSharedItemsModel.mbHasBlank = rAttribs.getBool( XML_containsBlank, false );
+ maSharedItemsModel.mbHasMixed = rAttribs.getBool( XML_containsMixedTypes, false );
+ maSharedItemsModel.mbIsNumeric = rAttribs.getBool( XML_containsNumber, false );
+ maSharedItemsModel.mbIsInteger = rAttribs.getBool( XML_containsInteger, false );
+ maSharedItemsModel.mbHasLongText = rAttribs.getBool( XML_longText, false );
+}
+
+void PivotCacheField::importSharedItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ maSharedItems.importItem( nElement, rAttribs );
+}
+
+void PivotCacheField::importFieldGroup( const AttributeList& rAttribs )
+{
+ maFieldGroupModel.mnParentField = rAttribs.getInteger( XML_par, -1 );
+ maFieldGroupModel.mnBaseField = rAttribs.getInteger( XML_base, -1 );
+}
+
+void PivotCacheField::importRangePr( const AttributeList& rAttribs )
+{
+ maFieldGroupModel.maStartDate = rAttribs.getDateTime( XML_startDate, css::util::DateTime() );
+ maFieldGroupModel.maEndDate = rAttribs.getDateTime( XML_endDate, css::util::DateTime() );
+ maFieldGroupModel.mfStartValue = rAttribs.getDouble( XML_startNum, 0.0 );
+ maFieldGroupModel.mfEndValue = rAttribs.getDouble( XML_endNum, 0.0 );
+ maFieldGroupModel.mfInterval = rAttribs.getDouble( XML_groupInterval, 1.0 );
+ maFieldGroupModel.mnGroupBy = rAttribs.getToken( XML_groupBy, XML_range );
+ maFieldGroupModel.mbRangeGroup = true;
+ maFieldGroupModel.mbDateGroup = maFieldGroupModel.mnGroupBy != XML_range;
+ maFieldGroupModel.mbAutoStart = rAttribs.getBool( XML_autoStart, true );
+ maFieldGroupModel.mbAutoEnd = rAttribs.getBool( XML_autoEnd, true );
+}
+
+void PivotCacheField::importDiscretePrItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ OSL_ENSURE( nElement == XLS_TOKEN( x ), "PivotCacheField::importDiscretePrItem - unexpected element" );
+ if( nElement == XLS_TOKEN( x ) )
+ maDiscreteItems.push_back( rAttribs.getInteger( XML_v, -1 ) );
+}
+
+void PivotCacheField::importGroupItem( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ maGroupItems.importItem( nElement, rAttribs );
+}
+
+void PivotCacheField::importPCDField( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ maFieldModel.mnNumFmtId = rStrm.readInt32();
+ maFieldModel.mnSqlType = rStrm.readInt16();
+ maFieldModel.mnHierarchy = rStrm.readInt32();
+ maFieldModel.mnLevel = rStrm.readInt32();
+ maFieldModel.mnMappingCount = rStrm.readInt32();
+ rStrm >> maFieldModel.maName;
+ if( getFlag( nFlags, BIFF12_PCDFIELD_HASCAPTION ) )
+ rStrm >> maFieldModel.maCaption;
+ if( getFlag( nFlags, BIFF12_PCDFIELD_HASFORMULA ) )
+ rStrm.skip( ::std::max< sal_Int32 >( rStrm.readInt32(), 0 ) );
+ if( maFieldModel.mnMappingCount > 0 )
+ rStrm.skip( ::std::max< sal_Int32 >( rStrm.readInt32(), 0 ) );
+ if( getFlag( nFlags, BIFF12_PCDFIELD_HASPROPERTYNAME ) )
+ rStrm >> maFieldModel.maPropertyName;
+
+ maFieldModel.mbDatabaseField = getFlag( nFlags, BIFF12_PCDFIELD_DATABASEFIELD );
+ maFieldModel.mbServerField = getFlag( nFlags, BIFF12_PCDFIELD_SERVERFIELD );
+ maFieldModel.mbUniqueList = !getFlag( nFlags, BIFF12_PCDFIELD_NOUNIQUEITEMS );
+ maFieldModel.mbMemberPropField = getFlag( nFlags, BIFF12_PCDFIELD_MEMBERPROPFIELD );
+}
+
+void PivotCacheField::importPCDFSharedItems( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ maSharedItemsModel.mbHasSemiMixed = getFlag( nFlags, BIFF12_PCDFSITEMS_HASSEMIMIXED );
+ maSharedItemsModel.mbHasNonDate = getFlag( nFlags, BIFF12_PCDFSITEMS_HASNONDATE );
+ maSharedItemsModel.mbHasDate = getFlag( nFlags, BIFF12_PCDFSITEMS_HASDATE );
+ maSharedItemsModel.mbHasString = getFlag( nFlags, BIFF12_PCDFSITEMS_HASSTRING );
+ maSharedItemsModel.mbHasBlank = getFlag( nFlags, BIFF12_PCDFSITEMS_HASBLANK );
+ maSharedItemsModel.mbHasMixed = getFlag( nFlags, BIFF12_PCDFSITEMS_HASMIXED );
+ maSharedItemsModel.mbIsNumeric = getFlag( nFlags, BIFF12_PCDFSITEMS_ISNUMERIC );
+ maSharedItemsModel.mbIsInteger = getFlag( nFlags, BIFF12_PCDFSITEMS_ISINTEGER );
+ maSharedItemsModel.mbHasLongText = getFlag( nFlags, BIFF12_PCDFSITEMS_HASLONGTEXT );
+}
+
+void PivotCacheField::importPCDFSharedItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ maSharedItems.importItem( nRecId, rStrm );
+}
+
+void PivotCacheField::importPCDFieldGroup( SequenceInputStream& rStrm )
+{
+ maFieldGroupModel.mnParentField = rStrm.readInt32();
+ maFieldGroupModel.mnBaseField = rStrm.readInt32();
+}
+
+void PivotCacheField::importPCDFRangePr( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nGroupBy, nFlags;
+ nGroupBy = rStrm.readuChar();
+ nFlags = rStrm.readuChar();
+ maFieldGroupModel.mfStartValue = rStrm.readDouble();
+ maFieldGroupModel.mfEndValue = rStrm.readDouble();
+ maFieldGroupModel.mfInterval = rStrm.readDouble();
+
+ maFieldGroupModel.setBiffGroupBy( nGroupBy );
+ maFieldGroupModel.mbRangeGroup = true;
+ maFieldGroupModel.mbDateGroup = getFlag( nFlags, BIFF12_PCDFRANGEPR_DATEGROUP );
+ maFieldGroupModel.mbAutoStart = getFlag( nFlags, BIFF12_PCDFRANGEPR_AUTOSTART );
+ maFieldGroupModel.mbAutoEnd = getFlag( nFlags, BIFF12_PCDFRANGEPR_AUTOEND );
+
+ OSL_ENSURE( maFieldGroupModel.mbDateGroup == (maFieldGroupModel.mnGroupBy != XML_range), "PivotCacheField::importPCDFRangePr - wrong date flag" );
+ if( maFieldGroupModel.mbDateGroup )
+ {
+ maFieldGroupModel.maStartDate = getUnitConverter().calcDateTimeFromSerial( maFieldGroupModel.mfStartValue );
+ maFieldGroupModel.maEndDate = getUnitConverter().calcDateTimeFromSerial( maFieldGroupModel.mfEndValue );
+ }
+}
+
+void PivotCacheField::importPCDFDiscretePrItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( nRecId == BIFF12_ID_PCITEM_INDEX, "PivotCacheField::importPCDFDiscretePrItem - unexpected record" );
+ if( nRecId == BIFF12_ID_PCITEM_INDEX )
+ maDiscreteItems.push_back( rStrm.readInt32() );
+}
+
+void PivotCacheField::importPCDFGroupItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ maGroupItems.importItem( nRecId, rStrm );
+}
+
+const PivotCacheItem* PivotCacheField::getCacheItem( sal_Int32 nItemIdx ) const
+{
+ if( hasGroupItems() )
+ return maGroupItems.getCacheItem( nItemIdx );
+ if( hasSharedItems() )
+ return maSharedItems.getCacheItem( nItemIdx );
+ return nullptr;
+}
+
+void PivotCacheField::applyItemCaptions( const IdCaptionPairList& vCaptions )
+{
+ if( hasGroupItems() )
+ maGroupItems.applyItemCaptions( vCaptions );
+ if( hasSharedItems() )
+ maSharedItems.applyItemCaptions( vCaptions );
+}
+
+void PivotCacheField::getCacheItemNames( ::std::vector< OUString >& orItemNames ) const
+{
+ if( hasGroupItems() )
+ maGroupItems.getCacheItemNames( orItemNames );
+ else if( hasSharedItems() )
+ maSharedItems.getCacheItemNames( orItemNames );
+}
+
+const PivotCacheItemList& PivotCacheField::getCacheItems() const
+{
+ if( hasGroupItems() )
+ return maGroupItems;
+ return maSharedItems;
+}
+
+void PivotCacheField::convertNumericGrouping( const Reference< XDataPilotField >& rxDPField ) const
+{
+ OSL_ENSURE( hasGroupItems() && hasNumericGrouping(), "PivotCacheField::convertNumericGrouping - not a numeric group field" );
+ PropertySet aPropSet( rxDPField );
+ if( hasGroupItems() && hasNumericGrouping() && aPropSet.is() )
+ {
+ DataPilotFieldGroupInfo aGroupInfo;
+ aGroupInfo.HasAutoStart = maFieldGroupModel.mbAutoStart;
+ aGroupInfo.HasAutoEnd = maFieldGroupModel.mbAutoEnd;
+ aGroupInfo.HasDateValues = false;
+ aGroupInfo.Start = maFieldGroupModel.mfStartValue;
+ aGroupInfo.End = maFieldGroupModel.mfEndValue;
+ aGroupInfo.Step = maFieldGroupModel.mfInterval;
+ aGroupInfo.GroupBy = 0;
+ aPropSet.setProperty( PROP_GroupInfo, aGroupInfo );
+ }
+}
+
+OUString PivotCacheField::createDateGroupField( const Reference< XDataPilotField >& rxBaseDPField ) const
+{
+ OSL_ENSURE( hasGroupItems() && hasDateGrouping(), "PivotCacheField::createDateGroupField - not a numeric group field" );
+ Reference< XDataPilotField > xDPGroupField;
+ PropertySet aPropSet( rxBaseDPField );
+ if( hasGroupItems() && hasDateGrouping() && aPropSet.is() )
+ {
+ bool bDayRanges = (maFieldGroupModel.mnGroupBy == XML_days) && (maFieldGroupModel.mfInterval >= 2.0);
+
+ DataPilotFieldGroupInfo aGroupInfo;
+ aGroupInfo.HasAutoStart = maFieldGroupModel.mbAutoStart;
+ aGroupInfo.HasAutoEnd = maFieldGroupModel.mbAutoEnd;
+ aGroupInfo.HasDateValues = true;
+ aGroupInfo.Start = getUnitConverter().calcSerialFromDateTime( maFieldGroupModel.maStartDate );
+ aGroupInfo.End = getUnitConverter().calcSerialFromDateTime( maFieldGroupModel.maEndDate );
+ aGroupInfo.Step = bDayRanges ? maFieldGroupModel.mfInterval : 0.0;
+
+ using namespace ::com::sun::star::sheet::DataPilotFieldGroupBy;
+ switch( maFieldGroupModel.mnGroupBy )
+ {
+ case XML_years: aGroupInfo.GroupBy = YEARS; break;
+ case XML_quarters: aGroupInfo.GroupBy = QUARTERS; break;
+ case XML_months: aGroupInfo.GroupBy = MONTHS; break;
+ case XML_days: aGroupInfo.GroupBy = DAYS; break;
+ case XML_hours: aGroupInfo.GroupBy = HOURS; break;
+ case XML_minutes: aGroupInfo.GroupBy = MINUTES; break;
+ case XML_seconds: aGroupInfo.GroupBy = SECONDS; break;
+ default: OSL_FAIL( "PivotCacheField::convertRangeGrouping - unknown date/time interval" );
+ }
+
+ try
+ {
+ Reference< XDataPilotFieldGrouping > xDPGrouping( rxBaseDPField, UNO_QUERY_THROW );
+ xDPGroupField = xDPGrouping->createDateGroup( aGroupInfo );
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ Reference< XNamed > xFieldName( xDPGroupField, UNO_QUERY );
+ return xFieldName.is() ? xFieldName->getName() : OUString();
+}
+
+OUString PivotCacheField::createParentGroupField( const Reference< XDataPilotField >& rxBaseDPField, const PivotCacheField& rBaseCacheField, PivotCacheGroupItemVector& orItemNames ) const
+{
+ SAL_WARN_IF( !hasGroupItems() || maDiscreteItems.empty(), "sc", "PivotCacheField::createParentGroupField - not a group field" );
+ SAL_WARN_IF( maDiscreteItems.size() != orItemNames.size(), "sc", "PivotCacheField::createParentGroupField - number of item names does not match grouping info" );
+ Reference< XDataPilotFieldGrouping > xDPGrouping( rxBaseDPField, UNO_QUERY );
+ if( !xDPGrouping.is() ) return OUString();
+
+ // map the group item indexes from maGroupItems to all item indexes from maDiscreteItems
+ std::vector< std::vector<sal_Int32> > aItemMap( maGroupItems.size() );
+ sal_Int32 nIndex = -1;
+ for( const auto& rDiscreteItem : maDiscreteItems )
+ {
+ ++nIndex;
+ if( std::vector<sal_Int32>* pItems = ContainerHelper::getVectorElementAccess( aItemMap, rDiscreteItem ) )
+ {
+ if ( const PivotCacheItem* pItem = rBaseCacheField.getCacheItems().getCacheItem( nIndex ) )
+ {
+ // Skip unspecified or unused entries or errors
+ if ( pItem->isUnused() || ( pItem->getType() == XML_m ) || ( pItem->getType() == XML_e ) )
+ continue;
+ }
+ pItems->push_back( nIndex );
+ }
+ }
+
+ // process all groups
+ Reference< XDataPilotField > xDPGroupField;
+ nIndex = 0;
+ for( const auto& rItems : aItemMap )
+ {
+ SAL_WARN_IF( rItems.empty(), "sc", "PivotCacheField::createParentGroupField - item/group should not be empty" );
+ if( !rItems.empty() )
+ {
+ /* Insert the names of the items that are part of this group. Calc
+ expects the names of the members of the field whose members are
+ grouped (which may be the names of groups too). Excel provides
+ the names of the base field items instead (no group names
+ involved). Therefore, the passed collection of current item
+ names as they are already grouped is used here to resolve the
+ item names. */
+ ::std::vector< OUString > aMembers;
+ for( auto i : rItems )
+ if( const PivotCacheGroupItem* pName = ContainerHelper::getVectorElement( orItemNames, i ) )
+ if( ::std::find( aMembers.begin(), aMembers.end(), pName->maGroupName ) == aMembers.end() )
+ aMembers.push_back( pName->maGroupName );
+
+ /* Check again, that this is not just a group that is not grouped
+ further with other items. */
+ if( !aMembers.empty() ) try
+ {
+ // only the first call of createNameGroup() returns the new field
+ Reference< XDataPilotField > xDPNewField = xDPGrouping->createNameGroup( comphelper::containerToSequence( aMembers ) );
+ SAL_WARN_IF( xDPGroupField.is() == xDPNewField.is(), "sc", "PivotCacheField::createParentGroupField - missing group field" );
+ if( !xDPGroupField.is() )
+ xDPGroupField = xDPNewField;
+
+ // get current grouping info
+ DataPilotFieldGroupInfo aGroupInfo;
+ PropertySet aPropSet( xDPGroupField );
+ aPropSet.getProperty( aGroupInfo, PROP_GroupInfo );
+
+ /* Find the group object and the auto-generated group name.
+ The returned field contains all groups derived from the
+ previous field if that is grouped too. To find the correct
+ group, the first item used to create the group is searched.
+ Calc provides the original item names of the base field
+ when the group is querried for its members. Its does not
+ provide the names of members that are already groups in the
+ field used to create the new groups. (Is this a bug?)
+ Therefore, a name from the passed list of original item
+ names is used to find the correct group. */
+ OUString aFirstItem;
+ if( const PivotCacheGroupItem* pName = ContainerHelper::getVectorElement( orItemNames, rItems.front() ) )
+ aFirstItem = pName->maOrigName;
+ Reference< XNamed > xGroupName;
+ OUString aAutoName;
+ Reference< XIndexAccess > xGroupsIA( aGroupInfo.Groups, UNO_QUERY_THROW );
+ for( sal_Int32 nIdx = 0, nCount = xGroupsIA->getCount(); (nIdx < nCount) && (aAutoName.isEmpty()); ++nIdx ) try
+ {
+ Reference< XNameAccess > xItemsNA( xGroupsIA->getByIndex( nIdx ), UNO_QUERY_THROW );
+ if( xItemsNA->hasByName( aFirstItem ) )
+ {
+ xGroupName.set( xGroupsIA->getByIndex( nIdx ), UNO_QUERY_THROW );
+ aAutoName = xGroupName->getName();
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("sc", "PivotCacheField::createParentGroupField" );
+ }
+ SAL_WARN_IF( aAutoName.isEmpty(), "sc", "PivotCacheField::createParentGroupField - cannot find auto-generated group name" );
+
+ // get the real group name from the list of group items
+ OUString aGroupName;
+ if( const PivotCacheItem* pGroupItem = maGroupItems.getCacheItem( nIndex ) )
+ aGroupName = pGroupItem->getName();
+ SAL_WARN_IF( aGroupName.isEmpty(), "sc", "PivotCacheField::createParentGroupField - cannot find group name" );
+ if( aGroupName.isEmpty() )
+ aGroupName = aAutoName;
+
+ if( xGroupName.is() && !aGroupName.isEmpty() )
+ {
+ // replace the auto-generated group name with the real name
+ if( aAutoName != aGroupName )
+ {
+ xGroupName->setName( aGroupName );
+ aPropSet.setProperty( PROP_GroupInfo, aGroupInfo );
+ }
+ // replace original item names in passed vector with group name
+ for( auto i : rItems )
+ if( PivotCacheGroupItem* pName = ContainerHelper::getVectorElementAccess( orItemNames, i ) )
+ pName->maGroupName = aGroupName;
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION("sc", "PivotCacheField::createParentGroupField" );
+ }
+ }
+ ++nIndex;
+ }
+
+ Reference< XNamed > xFieldName( xDPGroupField, UNO_QUERY );
+ return xFieldName.is() ? xFieldName->getName() : OUString();
+}
+
+void PivotCacheField::writeSourceHeaderCell( const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ CellModel aModel;
+ aModel.maCellAddr = ScAddress( SCCOL( nCol ), SCROW( nRow ), rSheetHelper.getSheetIndex() );
+ rSheetHelper.getSheetData().setStringCell( aModel, maFieldModel.maName );
+}
+
+void PivotCacheField::writeSourceDataCell( const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem ) const
+{
+ bool bHasIndex = rItem.getType() == XML_x;
+ OSL_ENSURE( bHasIndex != maSharedItems.empty(), "PivotCacheField::writeSourceDataCell - shared items missing or not expected" );
+ if( bHasIndex )
+ writeSharedItemToSourceDataCell( rSheetHelper, nCol, nRow, rItem.getValue().get< sal_Int32 >() );
+ else
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, rItem );
+}
+
+void PivotCacheField::importPCRecordItem( SequenceInputStream& rStrm, const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ if( hasSharedItems() )
+ {
+ writeSharedItemToSourceDataCell( rSheetHelper, nCol, nRow, rStrm.readInt32() );
+ }
+ else
+ {
+ PivotCacheItem aItem;
+ if( maSharedItemsModel.mbIsNumeric )
+ aItem.readDouble( rStrm );
+ else if( maSharedItemsModel.mbHasDate && !maSharedItemsModel.mbHasString )
+ aItem.readDate( rStrm );
+ else
+ aItem.readString( rStrm );
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, aItem );
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void PivotCacheField::writeItemToSourceDataCell( const WorksheetHelper& rSheetHelper,
+ sal_Int32 nCol, sal_Int32 nRow, const PivotCacheItem& rItem )
+{
+ if( rItem.getType() == XML_m )
+ return;
+
+ CellModel aModel;
+ aModel.maCellAddr = ScAddress( SCCOL( nCol ), SCROW( nRow ), rSheetHelper.getSheetIndex() );
+ SheetDataBuffer& rSheetData = rSheetHelper.getSheetData();
+ switch( rItem.getType() )
+ {
+ case XML_s: rSheetData.setStringCell( aModel, rItem.getValue().get< OUString >() ); break;
+ case XML_n: rSheetData.setValueCell( aModel, rItem.getValue().get< double >() ); break;
+ case XML_i: rSheetData.setValueCell( aModel, rItem.getValue().get< sal_Int16 >() ); break;
+ case XML_d: rSheetData.setDateTimeCell( aModel, rItem.getValue().get< css::util::DateTime >() ); break;
+ case XML_b: rSheetData.setBooleanCell( aModel, rItem.getValue().get< bool >() ); break;
+ case XML_e: rSheetData.setErrorCell(aModel, rItem.getValue().get<OUString>()); break;
+ default: OSL_FAIL( "PivotCacheField::writeItemToSourceDataCell - unexpected item data type" );
+ }
+}
+
+void PivotCacheField::writeSharedItemToSourceDataCell(
+ const WorksheetHelper& rSheetHelper, sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nItemIdx ) const
+{
+ if( const PivotCacheItem* pCacheItem = maSharedItems.getCacheItem( nItemIdx ) )
+ writeItemToSourceDataCell( rSheetHelper, nCol, nRow, *pCacheItem );
+}
+
+PCDefinitionModel::PCDefinitionModel() :
+ mfRefreshedDate( 0.0 ),
+ mnRecords( 0 ),
+ mnMissItemsLimit( 0 ),
+ mbInvalid( false ),
+ mbSaveData( true ),
+ mbRefreshOnLoad( false ),
+ mbOptimizeMemory( false ),
+ mbEnableRefresh( true ),
+ mbBackgroundQuery( false ),
+ mbUpgradeOnRefresh( false ),
+ mbTupleCache( false ),
+ mbSupportSubquery( false ),
+ mbSupportDrill( false )
+{
+}
+
+PCSourceModel::PCSourceModel() :
+ mnSourceType( XML_TOKEN_INVALID ),
+ mnConnectionId( 0 )
+{
+}
+
+PCWorksheetSourceModel::PCWorksheetSourceModel()
+{
+ maRange.SetInvalid();
+}
+
+PivotCache::PivotCache( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnCurrRow( -1 ),
+ mbValidSource( false ),
+ mbDummySheet( false )
+{
+}
+
+void PivotCache::importPivotCacheDefinition( const AttributeList& rAttribs )
+{
+ maDefModel.maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ maDefModel.maRefreshedBy = rAttribs.getXString( XML_refreshedBy, OUString() );
+ maDefModel.mfRefreshedDate = rAttribs.getDouble( XML_refreshedDate, 0.0 );
+ maDefModel.mnRecords = rAttribs.getInteger( XML_recordCount, 0 );
+ maDefModel.mnMissItemsLimit = rAttribs.getInteger( XML_missingItemsLimit, 0 );
+ maDefModel.mbInvalid = rAttribs.getBool( XML_invalid, false );
+ maDefModel.mbSaveData = rAttribs.getBool( XML_saveData, true );
+ maDefModel.mbRefreshOnLoad = rAttribs.getBool( XML_refreshOnLoad, false );
+ maDefModel.mbOptimizeMemory = rAttribs.getBool( XML_optimizeMemory, false );
+ maDefModel.mbEnableRefresh = rAttribs.getBool( XML_enableRefresh, true );
+ maDefModel.mbBackgroundQuery = rAttribs.getBool( XML_backgroundQuery, false );
+ maDefModel.mbUpgradeOnRefresh = rAttribs.getBool( XML_upgradeOnRefresh, false );
+ maDefModel.mbTupleCache = rAttribs.getBool( XML_tupleCache, false );
+ maDefModel.mbSupportSubquery = rAttribs.getBool( XML_supportSubquery, false );
+ maDefModel.mbSupportDrill = rAttribs.getBool( XML_supportAdvancedDrill, false );
+}
+
+void PivotCache::importCacheSource( const AttributeList& rAttribs )
+{
+ maSourceModel.mnSourceType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID );
+ maSourceModel.mnConnectionId = rAttribs.getInteger( XML_connectionId, 0 );
+}
+
+void PivotCache::importWorksheetSource( const AttributeList& rAttribs, const Relations& rRelations )
+{
+ maSheetSrcModel.maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ maSheetSrcModel.maSheet = rAttribs.getXString( XML_sheet, OUString() );
+ maSheetSrcModel.maDefName = rAttribs.getXString( XML_name, OUString() );
+
+ // resolve URL of external document
+ maTargetUrl = rRelations.getExternalTargetFromRelId( maSheetSrcModel.maRelId );
+ // store range address unchecked with sheet index 0, will be resolved/checked later
+ AddressConverter::convertToCellRangeUnchecked( maSheetSrcModel.maRange, rAttribs.getString( XML_ref, OUString() ), 0 );
+}
+
+void PivotCache::importPCDefinition( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nFlags1, nFlags2;
+ rStrm.skip( 3 ); // create/refresh version id's
+ nFlags1 = rStrm.readuChar();
+ maDefModel.mnMissItemsLimit = rStrm.readInt32();
+ maDefModel.mfRefreshedDate = rStrm.readDouble();
+ nFlags2 = rStrm.readuChar();
+ maDefModel.mnRecords = rStrm.readInt32();
+ if( getFlag( nFlags2, BIFF12_PCDEFINITION_HASUSERNAME ) )
+ rStrm >> maDefModel.maRefreshedBy;
+ if( getFlag( nFlags2, BIFF12_PCDEFINITION_HASRELID ) )
+ rStrm >> maDefModel.maRelId;
+
+ maDefModel.mbInvalid = getFlag( nFlags1, BIFF12_PCDEFINITION_INVALID );
+ maDefModel.mbSaveData = getFlag( nFlags1, BIFF12_PCDEFINITION_SAVEDATA );
+ maDefModel.mbRefreshOnLoad = getFlag( nFlags1, BIFF12_PCDEFINITION_REFRESHONLOAD );
+ maDefModel.mbOptimizeMemory = getFlag( nFlags1, BIFF12_PCDEFINITION_OPTIMIZEMEMORY );
+ maDefModel.mbEnableRefresh = getFlag( nFlags1, BIFF12_PCDEFINITION_ENABLEREFRESH );
+ maDefModel.mbBackgroundQuery = getFlag( nFlags1, BIFF12_PCDEFINITION_BACKGROUNDQUERY );
+ maDefModel.mbUpgradeOnRefresh = getFlag( nFlags1, BIFF12_PCDEFINITION_UPGRADEONREFR );
+ maDefModel.mbTupleCache = getFlag( nFlags1, BIFF12_PCDEFINITION_TUPLECACHE );
+ maDefModel.mbSupportSubquery = getFlag( nFlags2, BIFF12_PCDEFINITION_SUPPORTSUBQUERY );
+ maDefModel.mbSupportDrill = getFlag( nFlags2, BIFF12_PCDEFINITION_SUPPORTDRILL );
+}
+
+void PivotCache::importPCDSource( SequenceInputStream& rStrm )
+{
+ sal_Int32 nSourceType;
+ nSourceType = rStrm.readInt32();
+ maSourceModel.mnConnectionId = rStrm.readInt32();
+ static const sal_Int32 spnSourceTypes[] = { XML_worksheet, XML_external, XML_consolidation, XML_scenario };
+ maSourceModel.mnSourceType = STATIC_ARRAY_SELECT( spnSourceTypes, nSourceType, XML_TOKEN_INVALID );
+}
+
+void PivotCache::importPCDSheetSource( SequenceInputStream& rStrm, const Relations& rRelations )
+{
+ sal_uInt8 nIsDefName, nIsBuiltinName, nFlags;
+ nIsDefName = rStrm.readuChar();
+ nIsBuiltinName = rStrm.readuChar();
+ nFlags = rStrm.readuChar();
+ if( getFlag( nFlags, BIFF12_PCDWBSOURCE_HASSHEET ) )
+ rStrm >> maSheetSrcModel.maSheet;
+ if( getFlag( nFlags, BIFF12_PCDWBSOURCE_HASRELID ) )
+ rStrm >> maSheetSrcModel.maRelId;
+
+ // read cell range or defined name
+ if( nIsDefName == 0 )
+ {
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ // store range address unchecked with sheet index 0, will be resolved/checked later
+ AddressConverter::convertToCellRangeUnchecked( maSheetSrcModel.maRange, aBinRange, 0 );
+ }
+ else
+ {
+ rStrm >> maSheetSrcModel.maDefName;
+ if( nIsBuiltinName != 0 )
+ maSheetSrcModel.maDefName = "_xlnm." + maSheetSrcModel.maDefName;
+ }
+
+ // resolve URL of external document
+ maTargetUrl = rRelations.getExternalTargetFromRelId( maSheetSrcModel.maRelId );
+}
+
+PivotCacheField& PivotCache::createCacheField()
+{
+ PivotCacheFieldVector::value_type xCacheField = std::make_shared<PivotCacheField>( *this, true/*bIsDatabaseField*/ );
+ maFields.push_back( xCacheField );
+ return *xCacheField;
+}
+
+void PivotCache::finalizeImport()
+{
+ // collect all fields that are based on source data (needed to finalize source data below)
+ OSL_ENSURE( !maFields.empty(), "PivotCache::finalizeImport - no pivot cache fields found" );
+ for( PivotCacheFieldVector::const_iterator aIt = maFields.begin(), aEnd = maFields.end(); aIt != aEnd; ++aIt )
+ {
+ if( (*aIt)->isDatabaseField() )
+ {
+ OSL_ENSURE( (aIt == maFields.begin()) || (*(aIt - 1))->isDatabaseField(),
+ "PivotCache::finalizeImport - database field follows a calculated field" );
+ maDatabaseIndexes.push_back( static_cast< sal_Int32 >( maDatabaseFields.size() ) );
+ maDatabaseFields.push_back( *aIt );
+ }
+ else
+ {
+ maDatabaseIndexes.push_back( -1 );
+ }
+ }
+ OSL_ENSURE( !maDatabaseFields.empty(), "PivotCache::finalizeImport - no pivot cache source fields found" );
+
+ // finalize source data depending on source type
+ switch( maSourceModel.mnSourceType )
+ {
+ case XML_worksheet:
+ {
+ // decide whether an external document is used
+ bool bInternal = maTargetUrl.isEmpty() && maSheetSrcModel.maRelId.isEmpty();
+ bool bExternal = !maTargetUrl.isEmpty(); // relation ID may be empty, e.g. BIFF import
+ OSL_ENSURE( bInternal || bExternal, "PivotCache::finalizeImport - invalid external document URL" );
+ if( bInternal )
+ finalizeInternalSheetSource();
+ else if( bExternal )
+ finalizeExternalSheetSource();
+ }
+ break;
+
+ // currently, we only support worksheet data sources
+ case XML_external:
+ break;
+ case XML_consolidation:
+ break;
+ case XML_scenario:
+ break;
+ }
+}
+
+PivotCacheField* PivotCache::getCacheField( sal_Int32 nFieldIdx )
+{
+ return maFields.get( nFieldIdx ).get();
+}
+
+const PivotCacheField* PivotCache::getCacheField( sal_Int32 nFieldIdx ) const
+{
+ return maFields.get( nFieldIdx ).get();
+}
+
+sal_Int32 PivotCache::getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const
+{
+ return ContainerHelper::getVectorElement( maDatabaseIndexes, nFieldIdx, -1 );
+}
+
+void PivotCache::writeSourceHeaderCells( const WorksheetHelper& rSheetHelper ) const
+{
+ OSL_ENSURE( static_cast< size_t >( maSheetSrcModel.maRange.aEnd.Col() - maSheetSrcModel.maRange.aStart.Col() + 1 ) == maDatabaseFields.size(),
+ "PivotCache::writeSourceHeaderCells - source cell range width does not match number of source fields" );
+ SCCOL nCol = maSheetSrcModel.maRange.aStart.Col();
+ SCCOL nMaxCol = getAddressConverter().getMaxApiAddress().Col();
+ SCROW nRow = maSheetSrcModel.maRange.aStart.Row();
+ mnCurrRow = -1;
+ updateSourceDataRow( rSheetHelper, nRow );
+ for( const auto& rxDatabaseField : maDatabaseFields )
+ {
+ if (nCol > nMaxCol)
+ break;
+ rxDatabaseField->writeSourceHeaderCell( rSheetHelper, nCol, nRow );
+ ++nCol;
+ }
+}
+
+void PivotCache::writeSourceDataCell( const WorksheetHelper& rSheetHelper, sal_Int32 nColIdx, sal_Int32 nRowIdx, const PivotCacheItem& rItem ) const
+{
+ SCCOL nCol = maSheetSrcModel.maRange.aStart.Col() + nColIdx;
+ OSL_ENSURE( ( maSheetSrcModel.maRange.aStart.Col() <= nCol ) && ( nCol <= maSheetSrcModel.maRange.aEnd.Col() ), "PivotCache::writeSourceDataCell - invalid column index" );
+ SCROW nRow = maSheetSrcModel.maRange.aStart.Row() + nRowIdx;
+ OSL_ENSURE( ( maSheetSrcModel.maRange.aStart.Row() < nRow ) && ( nRow <= maSheetSrcModel.maRange.aEnd.Row() ), "PivotCache::writeSourceDataCell - invalid row index" );
+ updateSourceDataRow( rSheetHelper, nRow );
+ if( const PivotCacheField* pCacheField = maDatabaseFields.get( nColIdx ).get() )
+ pCacheField->writeSourceDataCell( rSheetHelper, nCol, nRow, rItem );
+}
+
+void PivotCache::importPCRecord( SequenceInputStream& rStrm, const WorksheetHelper& rSheetHelper, sal_Int32 nRowIdx ) const
+{
+ SCROW nRow = maSheetSrcModel.maRange.aStart.Row() + nRowIdx;
+ OSL_ENSURE( ( maSheetSrcModel.maRange.aStart.Row() < nRow ) && ( nRow <= maSheetSrcModel.maRange.aEnd.Row() ), "PivotCache::importPCRecord - invalid row index" );
+ SCCOL nCol = maSheetSrcModel.maRange.aStart.Col();
+ SCCOL nMaxCol = getAddressConverter().getMaxApiAddress().Col();
+ for( const auto& rxDatabaseField : maDatabaseFields )
+ {
+ if( rStrm.isEof() || (nCol > nMaxCol) )
+ break;
+ rxDatabaseField->importPCRecordItem( rStrm, rSheetHelper, nCol, nRow );
+ ++nCol;
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void PivotCache::finalizeInternalSheetSource()
+{
+ // resolve sheet name to sheet index
+ sal_Int16 nSheet = getWorksheets().getCalcSheetIndex( maSheetSrcModel.maSheet );
+
+ // if cache is based on a defined name or table, try to resolve to cell range
+ if( !maSheetSrcModel.maDefName.isEmpty() )
+ {
+ // local or global defined name
+ if( const DefinedName* pDefName = getDefinedNames().getByModelName( maSheetSrcModel.maDefName, nSheet ).get() )
+ {
+ mbValidSource = pDefName->getAbsoluteRange( maSheetSrcModel.maRange );
+ }
+ // table
+ else if( const Table* pTable = getTables().getTable( maSheetSrcModel.maDefName ).get() )
+ {
+ // get original range from table, but exclude the totals row(s)
+ maSheetSrcModel.maRange = pTable->getOriginalRange();
+ mbValidSource = (pTable->getHeight() - pTable->getTotalsRows()) > 1;
+ if( mbValidSource )
+ maSheetSrcModel.maRange.aEnd.SetRow( maSheetSrcModel.maRange.aEnd.Row() - pTable->getTotalsRows() );
+ }
+ }
+ // else try the cell range (if the sheet exists)
+ else if( nSheet >= 0 )
+ {
+ // insert sheet index into the range, range address will be checked below
+ maSheetSrcModel.maRange.aStart.SetTab( nSheet );
+ mbValidSource = true;
+ }
+ // else sheet has been deleted, generate the source data from cache
+ else if( !maSheetSrcModel.maSheet.isEmpty() )
+ {
+ prepareSourceDataSheet();
+ // return here to skip the source range check below
+ return;
+ }
+
+ // check range location, do not allow ranges that overflow the sheet partly
+ mbValidSource = mbValidSource &&
+ getAddressConverter().checkCellRange( maSheetSrcModel.maRange, false, true ) &&
+ ( maSheetSrcModel.maRange.aStart.Row() < maSheetSrcModel.maRange.aEnd.Row() );
+}
+
+void PivotCache::finalizeExternalSheetSource()
+{
+ /* If pivot cache is based on external sheet data, try to restore sheet
+ data from cache records. No support for external defined names or tables,
+ sheet name and path to cache records fragment (OOXML only) are required. */
+ bool bHasRelation = !maDefModel.maRelId.isEmpty();
+ if( bHasRelation && maSheetSrcModel.maDefName.isEmpty() && !maSheetSrcModel.maSheet.isEmpty() )
+ prepareSourceDataSheet();
+}
+
+void PivotCache::prepareSourceDataSheet()
+{
+ ScRange& rRange = maSheetSrcModel.maRange;
+ // data will be inserted in top-left cell, sheet index is still set to 0 (will be set below)
+ rRange.aEnd.SetCol( rRange.aEnd.Col() - rRange.aStart.Col() );
+ rRange.aStart.SetCol( 0 );
+ rRange.aEnd.SetRow( rRange.aEnd.Row() - rRange.aStart.Row() );
+ rRange.aStart.SetRow( 0 );
+ // check range location, do not allow ranges that overflow the sheet partly
+ if( getAddressConverter().checkCellRange( rRange, false, true ) )
+ {
+ maColSpans.insert( ValueRange( rRange.aStart.Col(), rRange.aEnd.Col() ) );
+ OUString aSheetName = "DPCache_" + maSheetSrcModel.maSheet;
+ rRange.aStart.SetTab( getWorksheets().insertEmptySheet( aSheetName ) );
+ mbValidSource = mbDummySheet = rRange.aStart.Tab() >= 0;
+ }
+}
+
+void PivotCache::updateSourceDataRow( const WorksheetHelper& rSheetHelper, sal_Int32 nRow ) const
+{
+ if( mnCurrRow != nRow )
+ {
+ rSheetHelper.getSheetData().setColSpans( nRow, maColSpans );
+ mnCurrRow = nRow;
+ }
+}
+
+PivotCacheBuffer::PivotCacheBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void PivotCacheBuffer::registerPivotCacheFragment( sal_Int32 nCacheId, const OUString& rFragmentPath )
+{
+ OSL_ENSURE( nCacheId >= 0, "PivotCacheBuffer::registerPivotCacheFragment - invalid pivot cache identifier" );
+ OSL_ENSURE( maFragmentPaths.count( nCacheId ) == 0, "PivotCacheBuffer::registerPivotCacheFragment - fragment path exists already" );
+ if( (nCacheId >= 0) && !rFragmentPath.isEmpty() )
+ maFragmentPaths[ nCacheId ] = rFragmentPath;
+}
+
+PivotCache* PivotCacheBuffer::importPivotCacheFragment( sal_Int32 nCacheId )
+{
+ /* OOXML/BIFF12 filter: On first call for the cache ID, the pivot
+ cache object is created and inserted into maCaches. Then, the cache
+ definition fragment is read and the cache is returned. On
+ subsequent calls, the created cache will be found in maCaches and
+ returned immediately. */
+ // try to find an imported pivot cache
+ if( PivotCache* pCache = maCaches.get( nCacheId ).get() )
+ return pCache;
+
+ // check if a fragment path exists for the passed cache identifier
+ FragmentPathMap::iterator aIt = maFragmentPaths.find( nCacheId );
+ if( aIt == maFragmentPaths.end() )
+ return nullptr;
+
+ /* Import the cache fragment. This may create a dummy data sheet
+ for external sheet sources. */
+ PivotCache& rCache = createPivotCache( nCacheId );
+ importOoxFragment( new PivotCacheDefinitionFragment( *this, aIt->second, rCache ) );
+ return &rCache;
+}
+
+PivotCache& PivotCacheBuffer::createPivotCache( sal_Int32 nCacheId )
+{
+ maCacheIds.push_back( nCacheId );
+ PivotCacheMap::mapped_type& rxCache = maCaches[ nCacheId ];
+ rxCache = std::make_shared<PivotCache>( *this );
+ return *rxCache;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pivotcachefragment.cxx b/sc/source/filter/oox/pivotcachefragment.cxx
new file mode 100644
index 000000000..f508c36cf
--- /dev/null
+++ b/sc/source/filter/oox/pivotcachefragment.cxx
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pivotcachefragment.hxx>
+
+#include <osl/diagnose.h>
+#include <oox/token/namespaces.hxx>
+#include <biffhelper.hxx>
+#include <formulabuffer.hxx>
+#include <pivotcachebuffer.hxx>
+#include <worksheetbuffer.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+
+PivotCacheFieldContext::PivotCacheFieldContext( WorkbookFragmentBase& rFragment, PivotCacheField& rCacheField ) :
+ WorkbookContextBase( rFragment ),
+ mrCacheField( rCacheField )
+{
+}
+
+ContextHandlerRef PivotCacheFieldContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( cacheField ):
+ if( nElement == XLS_TOKEN( sharedItems ) ) { mrCacheField.importSharedItems( rAttribs ); return this; }
+ if( nElement == XLS_TOKEN( fieldGroup ) ) { mrCacheField.importFieldGroup( rAttribs ); return this; }
+ break;
+
+ case XLS_TOKEN( fieldGroup ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( rangePr ): mrCacheField.importRangePr( rAttribs ); break;
+ case XLS_TOKEN( discretePr ): return this;
+ case XLS_TOKEN( groupItems ): return this;
+ }
+ break;
+
+ case XLS_TOKEN( sharedItems ): mrCacheField.importSharedItem( nElement, rAttribs ); break;
+ case XLS_TOKEN( discretePr ): mrCacheField.importDiscretePrItem( nElement, rAttribs ); break;
+ case XLS_TOKEN( groupItems ): mrCacheField.importGroupItem( nElement, rAttribs ); break;
+ }
+ return nullptr;
+}
+
+void PivotCacheFieldContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrCacheField.importCacheField( rAttribs );
+}
+
+ContextHandlerRef PivotCacheFieldContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_PCDFIELD:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCDFSHAREDITEMS: mrCacheField.importPCDFSharedItems( rStrm ); return this;
+ case BIFF12_ID_PCDFIELDGROUP: mrCacheField.importPCDFieldGroup( rStrm ); return this;
+ }
+ break;
+
+ case BIFF12_ID_PCDFIELDGROUP:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCDFRANGEPR: mrCacheField.importPCDFRangePr( rStrm ); break;
+ case BIFF12_ID_PCDFDISCRETEPR: return this;
+ case BIFF12_ID_PCDFGROUPITEMS: return this;
+ }
+ break;
+
+ case BIFF12_ID_PCDFSHAREDITEMS: mrCacheField.importPCDFSharedItem( nRecId, rStrm ); break;
+ case BIFF12_ID_PCDFDISCRETEPR: mrCacheField.importPCDFDiscretePrItem( nRecId, rStrm ); break;
+ case BIFF12_ID_PCDFGROUPITEMS: mrCacheField.importPCDFGroupItem( nRecId, rStrm ); break;
+ }
+ return nullptr;
+}
+
+void PivotCacheFieldContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrCacheField.importPCDField( rStrm );
+}
+
+PivotCacheDefinitionFragment::PivotCacheDefinitionFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath, PivotCache& rPivotCache ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath ),
+ mrPivotCache( rPivotCache )
+{
+}
+
+ContextHandlerRef PivotCacheDefinitionFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( pivotCacheDefinition ) ) { mrPivotCache.importPivotCacheDefinition( rAttribs ); return this; }
+ break;
+
+ case XLS_TOKEN( pivotCacheDefinition ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( cacheSource ): mrPivotCache.importCacheSource( rAttribs ); return this;
+ case XLS_TOKEN( cacheFields ): return this;
+ }
+ break;
+
+ case XLS_TOKEN( cacheSource ):
+ if( nElement == XLS_TOKEN( worksheetSource ) ) mrPivotCache.importWorksheetSource( rAttribs, getRelations() );
+ break;
+
+ case XLS_TOKEN( cacheFields ):
+ if( nElement == XLS_TOKEN( cacheField ) ) return new PivotCacheFieldContext( *this, mrPivotCache.createCacheField() );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef PivotCacheDefinitionFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_PCDEFINITION ) { mrPivotCache.importPCDefinition( rStrm ); return this; }
+ break;
+
+ case BIFF12_ID_PCDEFINITION:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCDSOURCE: mrPivotCache.importPCDSource( rStrm ); return this;
+ case BIFF12_ID_PCDFIELDS: return this;
+ }
+ break;
+
+ case BIFF12_ID_PCDSOURCE:
+ if( nRecId == BIFF12_ID_PCDSHEETSOURCE ) mrPivotCache.importPCDSheetSource( rStrm, getRelations() );
+ break;
+
+ case BIFF12_ID_PCDFIELDS:
+ if( nRecId == BIFF12_ID_PCDFIELD ) return new PivotCacheFieldContext( *this, mrPivotCache.createCacheField() );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* PivotCacheDefinitionFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_PCDEFINITION, BIFF12_ID_PCDEFINITION + 1 },
+ { BIFF12_ID_PCDFDISCRETEPR, BIFF12_ID_PCDFDISCRETEPR + 1 },
+ { BIFF12_ID_PCDFGROUPITEMS, BIFF12_ID_PCDFGROUPITEMS + 1 },
+ { BIFF12_ID_PCDFIELD, BIFF12_ID_PCDFIELD + 1 },
+ { BIFF12_ID_PCDFIELDGROUP, BIFF12_ID_PCDFIELDGROUP + 1 },
+ { BIFF12_ID_PCDFIELDS, BIFF12_ID_PCDFIELDS + 1 },
+ { BIFF12_ID_PCDFRANGEPR, BIFF12_ID_PCDFRANGEPR + 1 },
+ { BIFF12_ID_PCDFSHAREDITEMS, BIFF12_ID_PCDFSHAREDITEMS + 1 },
+ { BIFF12_ID_PCITEM_ARRAY, BIFF12_ID_PCITEM_ARRAY + 1 },
+ { BIFF12_ID_PCDSHEETSOURCE, BIFF12_ID_PCDSHEETSOURCE + 1 },
+ { BIFF12_ID_PCDSOURCE, BIFF12_ID_PCDSOURCE + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void PivotCacheDefinitionFragment::finalizeImport()
+{
+ // finalize the cache (check source range etc.)
+ mrPivotCache.finalizeImport();
+
+ // load the cache records, if the cache is based on a deleted or an external worksheet
+ if( mrPivotCache.isValidDataSource() && mrPivotCache.isBasedOnDummySheet() )
+ {
+ OUString aRecFragmentPath = getRelations().getFragmentPathFromRelId( mrPivotCache.getRecordsRelId() );
+ if( !aRecFragmentPath.isEmpty() )
+ {
+ SCTAB nSheet = mrPivotCache.getSourceRange().aStart.Tab();
+ WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, ISegmentProgressBarRef(), WorksheetType::Work, nSheet );
+ if( xSheetGlob )
+ importOoxFragment( new PivotCacheRecordsFragment( *xSheetGlob, aRecFragmentPath, mrPivotCache ) );
+ }
+ }
+}
+
+PivotCacheRecordsFragment::PivotCacheRecordsFragment( const WorksheetHelper& rHelper,
+ const OUString& rFragmentPath, const PivotCache& rPivotCache ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mrPivotCache( rPivotCache ),
+ mnColIdx( 0 ),
+ mnRowIdx( 0 ),
+ mbInRecord( false )
+{
+ sal_Int32 nSheetCount = rPivotCache.getWorksheets().getAllSheetCount();
+
+ // prepare sheet: insert column header names into top row
+ rPivotCache.writeSourceHeaderCells( *this );
+ // resize formula buffers since we've added a new dummy sheet
+ rHelper.getFormulaBuffer().SetSheetCount( nSheetCount );
+}
+
+ContextHandlerRef PivotCacheRecordsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( pivotCacheRecords ) ) return this;
+ break;
+
+ case XLS_TOKEN( pivotCacheRecords ):
+ if( nElement == XLS_TOKEN( r ) ) { startCacheRecord(); return this; }
+ break;
+
+ case XLS_TOKEN( r ):
+ {
+ PivotCacheItem aItem;
+ switch( nElement )
+ {
+ case XLS_TOKEN( m ): break;
+ case XLS_TOKEN( s ): aItem.readString( rAttribs ); break;
+ case XLS_TOKEN( n ): aItem.readNumeric( rAttribs ); break;
+ case XLS_TOKEN( d ): aItem.readDate( rAttribs ); break;
+ case XLS_TOKEN( b ): aItem.readBool( rAttribs ); break;
+ case XLS_TOKEN( e ): aItem.readError( rAttribs ); break;
+ case XLS_TOKEN( x ): aItem.readIndex( rAttribs ); break;
+ default: OSL_FAIL( "OoxPivotCacheRecordsFragment::onCreateContext - unexpected element" );
+ }
+ mrPivotCache.writeSourceDataCell( *this, mnColIdx, mnRowIdx, aItem );
+ ++mnColIdx;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef PivotCacheRecordsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_PCRECORDS ) return this;
+ break;
+
+ case BIFF12_ID_PCRECORDS:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCRECORD: importPCRecord( rStrm ); break;
+ case BIFF12_ID_PCRECORDDT: startCacheRecord(); break;
+ default: importPCRecordItem( nRecId, rStrm ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* PivotCacheRecordsFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_PCRECORDS, BIFF12_ID_PCRECORDS + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+// private --------------------------------------------------------------------
+
+void PivotCacheRecordsFragment::startCacheRecord()
+{
+ mnColIdx = 0;
+ ++mnRowIdx;
+ mbInRecord = true;
+}
+
+void PivotCacheRecordsFragment::importPCRecord( SequenceInputStream& rStrm )
+{
+ startCacheRecord();
+ mrPivotCache.importPCRecord( rStrm, *this, mnRowIdx );
+ mbInRecord = false;
+}
+
+void PivotCacheRecordsFragment::importPCRecordItem( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ if( !mbInRecord )
+ return;
+
+ PivotCacheItem aItem;
+ switch( nRecId )
+ {
+ case BIFF12_ID_PCITEM_MISSING: break;
+ case BIFF12_ID_PCITEM_STRING: aItem.readString( rStrm ); break;
+ case BIFF12_ID_PCITEM_DOUBLE: aItem.readDouble( rStrm ); break;
+ case BIFF12_ID_PCITEM_DATE: aItem.readDate( rStrm ); break;
+ case BIFF12_ID_PCITEM_BOOL: aItem.readBool( rStrm ); break;
+ case BIFF12_ID_PCITEM_ERROR: aItem.readError( rStrm, getUnitConverter() ); break;
+ case BIFF12_ID_PCITEM_INDEX: aItem.readIndex( rStrm ); break;
+ default: OSL_FAIL( "OoxPivotCacheRecordsFragment::importPCRecordItem - unexpected record" );
+ }
+ mrPivotCache.writeSourceDataCell( *this, mnColIdx, mnRowIdx, aItem );
+ ++mnColIdx;
+}
+
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pivottablebuffer.cxx b/sc/source/filter/oox/pivottablebuffer.cxx
new file mode 100644
index 000000000..3aadd5860
--- /dev/null
+++ b/sc/source/filter/oox/pivottablebuffer.cxx
@@ -0,0 +1,1480 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pivottablebuffer.hxx>
+
+#include <set>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/sheet/CellFlags.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/GeneralFunction.hpp>
+#include <com/sun/star/sheet/XDataPilotDataLayoutFieldSupplier.hpp>
+#include <com/sun/star/sheet/XDataPilotTablesSupplier.hpp>
+#include <com/sun/star/sheet/XSheetOperation.hpp>
+#include <com/sun/star/xml/sax/XFastAttributeList.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+#include <dapiuno.hxx>
+#include <dpobject.hxx>
+#include <dpsave.hxx>
+#include <dpdimsave.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <workbooksettings.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_Int32 OOX_PT_DATALAYOUTFIELD = -2; /// Placeholder index of data layout field.
+
+const sal_Int32 OOX_PT_PREVIOUS_ITEM = 0x001000FC; /// Calculation of data item result is based on previous item.
+const sal_Int32 OOX_PT_NEXT_ITEM = 0x001000FD; /// Calculation of data item result is based on next item.
+
+const sal_uInt32 BIFF12_PTFIELD_DATAFIELD = 0x00000008;
+const sal_uInt32 BIFF12_PTFIELD_DEFAULT = 0x00000100;
+const sal_uInt32 BIFF12_PTFIELD_SUM = 0x00000200;
+const sal_uInt32 BIFF12_PTFIELD_COUNTA = 0x00000400;
+const sal_uInt32 BIFF12_PTFIELD_AVERAGE = 0x00000800;
+const sal_uInt32 BIFF12_PTFIELD_MAX = 0x00001000;
+const sal_uInt32 BIFF12_PTFIELD_MIN = 0x00002000;
+const sal_uInt32 BIFF12_PTFIELD_PRODUCT = 0x00004000;
+const sal_uInt32 BIFF12_PTFIELD_COUNT = 0x00008000;
+const sal_uInt32 BIFF12_PTFIELD_STDDEV = 0x00010000;
+const sal_uInt32 BIFF12_PTFIELD_STDDEVP = 0x00020000;
+const sal_uInt32 BIFF12_PTFIELD_VAR = 0x00040000;
+const sal_uInt32 BIFF12_PTFIELD_VARP = 0x00080000;
+
+const sal_uInt32 BIFF12_PTFIELD_SHOWALL = 0x00000020;
+const sal_uInt32 BIFF12_PTFIELD_OUTLINE = 0x00000040;
+const sal_uInt32 BIFF12_PTFIELD_INSERTBLANKROW = 0x00000080;
+const sal_uInt32 BIFF12_PTFIELD_SUBTOTALTOP = 0x00000100;
+const sal_uInt32 BIFF12_PTFIELD_INSERTPAGEBREAK = 0x00000800;
+const sal_uInt32 BIFF12_PTFIELD_AUTOSORT = 0x00001000;
+const sal_uInt32 BIFF12_PTFIELD_SORTASCENDING = 0x00002000;
+const sal_uInt32 BIFF12_PTFIELD_AUTOSHOW = 0x00004000;
+const sal_uInt32 BIFF12_PTFIELD_AUTOSHOWTOP = 0x00008000;
+const sal_uInt32 BIFF12_PTFIELD_MULTIPAGEITEMS = 0x00080000;
+
+const sal_uInt16 BIFF12_PTFITEM_HIDDEN = 0x0001;
+const sal_uInt16 BIFF12_PTFITEM_HIDEDETAILS = 0x0002;
+
+const sal_uInt8 BIFF12_PTPAGEFIELD_HASNAME = 0x01;
+const sal_Int32 BIFF12_PTPAGEFIELD_MULTIITEMS = 0x001000FE;
+
+const sal_uInt16 BIFF12_PTFILTER_HASNAME = 0x0001;
+const sal_uInt16 BIFF12_PTFILTER_HASDESCRIPTION = 0x0002;
+const sal_uInt16 BIFF12_PTFILTER_HASSTRVALUE1 = 0x0004;
+const sal_uInt16 BIFF12_PTFILTER_HASSTRVALUE2 = 0x0008;
+
+const sal_uInt8 BIFF12_TOP10FILTER_TOP = 0x01;
+const sal_uInt8 BIFF12_TOP10FILTER_PERCENT = 0x02;
+
+const sal_uInt32 BIFF12_PTDEF_SHOWITEMS = 0x00000100;
+const sal_uInt32 BIFF12_PTDEF_DISABLEFIELDLIST = 0x00000400;
+const sal_uInt32 BIFF12_PTDEF_HIDECALCMEMBERS = 0x00001000;
+const sal_uInt32 BIFF12_PTDEF_WITHHIDDENTOTALS = 0x00002000;
+const sal_uInt32 BIFF12_PTDEF_HIDEDRILL = 0x00100000;
+const sal_uInt32 BIFF12_PTDEF_PRINTDRILL = 0x00200000;
+const sal_uInt32 BIFF12_PTDEF_HIDEHEADERS = 0x80000000;
+
+const sal_uInt32 BIFF12_PTDEF_SHOWEMPTYROW = 0x00000004;
+const sal_uInt32 BIFF12_PTDEF_SHOWEMPTYCOL = 0x00000008;
+const sal_uInt32 BIFF12_PTDEF_ENABLEDRILL = 0x00000020;
+const sal_uInt32 BIFF12_PTDEF_PRESERVEFORMATTING = 0x00000080;
+const sal_uInt32 BIFF12_PTDEF_USEAUTOFORMAT = 0x00000100;
+const sal_uInt32 BIFF12_PTDEF_SHOWERROR = 0x00000200;
+const sal_uInt32 BIFF12_PTDEF_SHOWMISSING = 0x00000400;
+const sal_uInt32 BIFF12_PTDEF_PAGEOVERTHENDOWN = 0x00000800;
+const sal_uInt32 BIFF12_PTDEF_SUBTOTALHIDDENITEMS = 0x00001000;
+const sal_uInt32 BIFF12_PTDEF_ROWGRANDTOTALS = 0x00002000;
+const sal_uInt32 BIFF12_PTDEF_COLGRANDTOTALS = 0x00004000;
+const sal_uInt32 BIFF12_PTDEF_FIELDPRINTTITLES = 0x00008000;
+const sal_uInt32 BIFF12_PTDEF_ITEMPRINTTITLES = 0x00020000;
+const sal_uInt32 BIFF12_PTDEF_MERGEITEM = 0x00040000;
+const sal_uInt32 BIFF12_PTDEF_HASDATACAPTION = 0x00080000;
+const sal_uInt32 BIFF12_PTDEF_HASGRANDTOTALCAPTION = 0x00100000;
+const sal_uInt32 BIFF12_PTDEF_HASPAGESTYLE = 0x00200000;
+const sal_uInt32 BIFF12_PTDEF_HASPIVOTTABLESTYLE = 0x00400000;
+const sal_uInt32 BIFF12_PTDEF_HASVACATEDSTYLE = 0x00800000;
+const sal_uInt32 BIFF12_PTDEF_APPLYNUMFMT = 0x01000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYFONT = 0x02000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYALIGNMENT = 0x04000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYBORDER = 0x08000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYFILL = 0x10000000;
+const sal_uInt32 BIFF12_PTDEF_APPLYPROTECTION = 0x20000000;
+const sal_uInt32 BIFF12_PTDEF_HASTAG = 0x40000000;
+
+const sal_uInt32 BIFF12_PTDEF_NOERRORCAPTION = 0x00000040;
+const sal_uInt32 BIFF12_PTDEF_NOMISSINGCAPTION = 0x00000080;
+const sal_uInt32 BIFF12_PTDEF_HASROWHEADERCAPTION = 0x00000400;
+const sal_uInt32 BIFF12_PTDEF_HASCOLHEADERCAPTION = 0x00000800;
+const sal_uInt32 BIFF12_PTDEF_FIELDLISTSORTASC = 0x00001000;
+const sal_uInt32 BIFF12_PTDEF_NOCUSTOMLISTSORT = 0x00004000;
+
+const sal_uInt8 BIFF12_PTDEF_ROWAXIS = 1;
+const sal_uInt8 BIFF12_PTDEF_COLAXIS = 2;
+
+} // namespace
+
+PTFieldItemModel::PTFieldItemModel() :
+ mnCacheItem( -1 ),
+ mnType( XML_data ),
+ mbShowDetails( true ),
+ mbHidden( false )
+{
+}
+
+void PTFieldItemModel::setBiffType( sal_uInt16 nType )
+{
+ static const sal_Int32 spnTypes[] = { XML_data, XML_default,
+ XML_sum, XML_countA, XML_avg, XML_max, XML_min, XML_product, XML_count,
+ XML_stdDev, XML_stdDevP, XML_var, XML_varP, XML_grand, XML_blank };
+ mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_data );
+}
+
+PTFieldModel::PTFieldModel() :
+ mnAxis( XML_TOKEN_INVALID ),
+ mnNumFmtId( 0 ),
+ mnAutoShowItems( 10 ),
+ mnAutoShowRankBy( -1 ),
+ mnSortType( XML_manual ),
+ mnSortRefField( -1 ),
+ mnSortRefItem( -1 ),
+ mbDataField( false ),
+ mbDefaultSubtotal( true ),
+ mbSumSubtotal( false ),
+ mbCountASubtotal( false ),
+ mbAverageSubtotal( false ),
+ mbMaxSubtotal( false ),
+ mbMinSubtotal( false ),
+ mbProductSubtotal( false ),
+ mbCountSubtotal( false ),
+ mbStdDevSubtotal( false ),
+ mbStdDevPSubtotal( false ),
+ mbVarSubtotal( false ),
+ mbVarPSubtotal( false ),
+ mbShowAll( true ),
+ mbOutline( true ),
+ mbSubtotalTop( true ),
+ mbInsertBlankRow( false ),
+ mbInsertPageBreak( false ),
+ mbAutoShow( false ),
+ mbTopAutoShow( true ),
+ mbMultiPageItems( false )
+{
+}
+
+void PTFieldModel::setBiffAxis( sal_uInt8 nAxis )
+{
+ /* Weird. The axis field is organized as bit field, but only one of the
+ row/col/page flags are allowed at the same time and refer to the values
+ 'axisRow', 'axisCol', and 'axisPage' of the XML attribute
+ 'pivotField@axis'. Additionally, the fourth bit determines if the field
+ is a data field, which may appear combined with the row/col/page flags.
+ Therefore, this bit is unrelated to the 'axisValues' value of the
+ 'pivotField@axis' attribute, but refers to the 'pivotField@dataField'
+ boolean attribute. */
+ static const sal_Int32 spnAxisIds[] = { XML_TOKEN_INVALID, XML_axisRow, XML_axisCol, XML_TOKEN_INVALID, XML_axisPage };
+ mnAxis = STATIC_ARRAY_SELECT( spnAxisIds, nAxis, XML_TOKEN_INVALID );
+}
+
+PTPageFieldModel::PTPageFieldModel() :
+ mnField( -1 ),
+ mnItem( BIFF12_PTPAGEFIELD_MULTIITEMS )
+{
+}
+
+PTDataFieldModel::PTDataFieldModel() :
+ mnField( -1 ),
+ mnSubtotal( XML_sum ),
+ mnShowDataAs( XML_normal ),
+ mnBaseField( -1 ),
+ mnBaseItem( -1 ),
+ mnNumFmtId( 0 )
+{
+}
+
+void PTDataFieldModel::setBiffSubtotal( sal_Int32 nSubtotal )
+{
+ static const sal_Int32 spnSubtotals[] = { XML_sum, XML_count, XML_average, XML_max, XML_min, XML_product, XML_countNums, XML_stdDev, XML_stdDevp, XML_var, XML_varp };
+ mnSubtotal = STATIC_ARRAY_SELECT( spnSubtotals, nSubtotal, XML_TOKEN_INVALID );
+}
+
+void PTDataFieldModel::setBiffShowDataAs( sal_Int32 nShowDataAs )
+{
+ static const sal_Int32 spnShowDataAs[] = { XML_normal, XML_difference, XML_percent, XML_percentDiff, XML_runTotal, XML_percentOfRow, XML_percentOfCol, XML_percentOfTotal, XML_index };
+ mnShowDataAs = STATIC_ARRAY_SELECT( spnShowDataAs, nShowDataAs, XML_TOKEN_INVALID );
+}
+
+PivotTableField::PivotTableField( PivotTable& rPivotTable, sal_Int32 nFieldIndex ) :
+ WorkbookHelper( rPivotTable ),
+ mrPivotTable( rPivotTable ),
+ mnFieldIndex( nFieldIndex )
+{
+}
+
+void PivotTableField::importPivotField( const AttributeList& rAttribs )
+{
+ /* The documentation mentions a value 'axisValues' for the attribute
+ 'pivotField@axis'. But this value is not used to mark a data field, as
+ data fields may be inserted in one of the row/column/page dimensions at
+ the same time. Therefore, the boolean attribute 'pivotField@dataField'
+ is really used to mark data fields. */
+ maModel.mnAxis = rAttribs.getToken( XML_axis, XML_TOKEN_INVALID );
+ maModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ maModel.mnAutoShowItems = rAttribs.getInteger( XML_itemPageCount, 10 );
+ maModel.mnAutoShowRankBy = rAttribs.getInteger( XML_rankBy, -1 );
+ maModel.mnSortType = rAttribs.getToken( XML_sortType, XML_manual );
+ maModel.mbDataField = rAttribs.getBool( XML_dataField, false );
+ maModel.mbDefaultSubtotal = rAttribs.getBool( XML_defaultSubtotal, true );
+ maModel.mbSumSubtotal = rAttribs.getBool( XML_sumSubtotal, false );
+ maModel.mbCountASubtotal = rAttribs.getBool( XML_countASubtotal, false );
+ maModel.mbAverageSubtotal = rAttribs.getBool( XML_avgSubtotal, false );
+ maModel.mbMaxSubtotal = rAttribs.getBool( XML_maxSubtotal, false );
+ maModel.mbMinSubtotal = rAttribs.getBool( XML_minSubtotal, false );
+ maModel.mbProductSubtotal = rAttribs.getBool( XML_productSubtotal, false );
+ maModel.mbCountSubtotal = rAttribs.getBool( XML_countSubtotal, false );
+ maModel.mbStdDevSubtotal = rAttribs.getBool( XML_stdDevSubtotal, false );
+ maModel.mbStdDevPSubtotal = rAttribs.getBool( XML_stdDevPSubtotal, false );
+ maModel.mbVarSubtotal = rAttribs.getBool( XML_varSubtotal, false );
+ maModel.mbVarPSubtotal = rAttribs.getBool( XML_varPSubtotal, false );
+ maModel.mbShowAll = rAttribs.getBool( XML_showAll, true );
+ maModel.mbOutline = rAttribs.getBool( XML_outline, true );
+ maModel.mbSubtotalTop = rAttribs.getBool( XML_subtotalTop, true );
+ maModel.mbInsertBlankRow = rAttribs.getBool( XML_insertBlankRow, false );
+ maModel.mbInsertPageBreak = rAttribs.getBool( XML_insertPageBreak, false );
+ maModel.mbAutoShow = rAttribs.getBool( XML_autoShow, false );
+ maModel.mbTopAutoShow = rAttribs.getBool( XML_topAutoShow, true );
+ maModel.mbMultiPageItems = rAttribs.getBool( XML_multipleItemSelectionAllowed, false );
+}
+
+void PivotTableField::importItem( const AttributeList& rAttribs )
+{
+ PTFieldItemModel aModel;
+ aModel.mnCacheItem = rAttribs.getInteger( XML_x, -1 );
+ aModel.mnType = rAttribs.getToken( XML_t, XML_data );
+ aModel.mbShowDetails = rAttribs.getBool( XML_sd, true );
+ aModel.mbHidden = rAttribs.getBool( XML_h, false );
+ aModel.msCaption = rAttribs.getXString( XML_n, OUString() );
+ maItems.push_back( aModel );
+}
+
+void PivotTableField::importReference( const AttributeList& rAttribs )
+{
+ // field index is stored as unsigned integer
+ maModel.mnSortRefField = static_cast< sal_Int32 >( rAttribs.getUnsigned( XML_field, SAL_MAX_UINT32 ) );
+}
+
+void PivotTableField::importReferenceItem( const AttributeList& rAttribs )
+{
+ maModel.mnSortRefItem = rAttribs.getInteger( XML_v, -1 );
+}
+
+void PivotTableField::importPTField( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags1, nFlags2;
+ nFlags1 = rStrm.readuInt32();
+ maModel.mnNumFmtId = rStrm.readInt32();
+ nFlags2 = rStrm.readuInt32();
+ maModel.mnAutoShowItems = rStrm.readInt32();
+ maModel.mnAutoShowRankBy = rStrm.readInt32();
+
+ maModel.setBiffAxis( extractValue< sal_uInt8 >( nFlags1, 0, 3 ) );
+ maModel.mbDataField = getFlag( nFlags1, BIFF12_PTFIELD_DATAFIELD );
+ maModel.mbDefaultSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_DEFAULT );
+ maModel.mbSumSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_SUM );
+ maModel.mbCountASubtotal = getFlag( nFlags1, BIFF12_PTFIELD_COUNTA );
+ maModel.mbAverageSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_AVERAGE );
+ maModel.mbMaxSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_MAX );
+ maModel.mbMinSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_MIN );
+ maModel.mbProductSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_PRODUCT );
+ maModel.mbCountSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_COUNT );
+ maModel.mbStdDevSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_STDDEV );
+ maModel.mbStdDevPSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_STDDEVP );
+ maModel.mbVarSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_VAR );
+ maModel.mbVarPSubtotal = getFlag( nFlags1, BIFF12_PTFIELD_VARP );
+
+ maModel.mbShowAll = getFlag( nFlags2, BIFF12_PTFIELD_SHOWALL );
+ maModel.mbOutline = getFlag( nFlags2, BIFF12_PTFIELD_OUTLINE );
+ maModel.mbSubtotalTop = getFlag( nFlags2, BIFF12_PTFIELD_SUBTOTALTOP );
+ maModel.mbInsertBlankRow = getFlag( nFlags2, BIFF12_PTFIELD_INSERTBLANKROW );
+ maModel.mbInsertPageBreak = getFlag( nFlags2, BIFF12_PTFIELD_INSERTPAGEBREAK );
+ maModel.mbAutoShow = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSHOW );
+ maModel.mbTopAutoShow = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSHOWTOP );
+ maModel.mbMultiPageItems = getFlag( nFlags2, BIFF12_PTFIELD_MULTIPAGEITEMS );
+
+ bool bAutoSort = getFlag( nFlags2, BIFF12_PTFIELD_AUTOSORT );
+ bool bAscending = getFlag( nFlags2, BIFF12_PTFIELD_SORTASCENDING );
+ maModel.mnSortType = bAutoSort ? (bAscending ? XML_ascending : XML_descending) : XML_manual;
+}
+
+void PivotTableField::importPTFItem( SequenceInputStream& rStrm )
+{
+ PTFieldItemModel aModel;
+ sal_uInt8 nType;
+ sal_uInt16 nFlags;
+ nType = rStrm.readuChar();
+ nFlags = rStrm.readuInt16();
+ aModel.mnCacheItem = rStrm.readInt32();
+
+ aModel.setBiffType( nType );
+ aModel.mbShowDetails = !getFlag( nFlags, BIFF12_PTFITEM_HIDEDETAILS );
+ aModel.mbHidden = getFlag( nFlags, BIFF12_PTFITEM_HIDDEN );
+
+ maItems.push_back( aModel );
+}
+
+void PivotTableField::importPTReference( SequenceInputStream& rStrm )
+{
+ maModel.mnSortRefField = rStrm.readInt32();
+}
+
+void PivotTableField::importPTReferenceItem( SequenceInputStream& rStrm )
+{
+ maModel.mnSortRefItem = rStrm.readInt32();
+}
+
+void PivotTableField::finalizeImport( const Reference< XDataPilotDescriptor >& rxDPDesc )
+{
+ /* Process all fields based on source data, other fields (e.g. group
+ fields) are processed from here. PivotCache::getCacheDatabaseIndex()
+ returns -1 for all fields not based on source data. */
+ Reference< XDataPilotField > xDPField;
+ sal_Int32 nDatabaseIdx = mrPivotTable.getCacheDatabaseIndex( mnFieldIndex );
+ if( !((nDatabaseIdx >= 0) && rxDPDesc.is()) )
+ return;
+
+ try
+ {
+ // try to get the source field and its name from passed DataPilot descriptor
+ Reference< XIndexAccess > xDPFieldsIA( rxDPDesc->getDataPilotFields(), UNO_SET_THROW );
+ xDPField.set( xDPFieldsIA->getByIndex( nDatabaseIdx ), UNO_QUERY_THROW );
+ Reference< XNamed > xDPFieldName( xDPField, UNO_QUERY_THROW );
+ maDPFieldName = xDPFieldName->getName();
+ OSL_ENSURE( !maDPFieldName.isEmpty(), "PivotTableField::finalizeImport - no field name in source data found" );
+
+ // try to convert grouping settings
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
+ {
+ // numeric grouping is done inplace, no nested group fields will appear
+ if( pCacheField->hasNumericGrouping() )
+ {
+ pCacheField->convertNumericGrouping( xDPField );
+ }
+ else if( pCacheField->hasDateGrouping() )
+ {
+ // first date group settings are inplace
+ pCacheField->createDateGroupField( xDPField );
+ // create all nested group fields (if any)
+ mrPivotTable.finalizeDateGroupingImport( xDPField, mnFieldIndex );
+ }
+ else if( pCacheField->hasParentGrouping() )
+ {
+
+ // create a list of all item names, needed to map between original and group items
+ ::std::vector< OUString > aItems;
+ pCacheField->getCacheItemNames( aItems );
+ PivotCacheGroupItemVector aItemNames;
+ for( const auto& rItem : aItems )
+ aItemNames.emplace_back( rItem );
+ // create all nested group fields (if any)
+ mrPivotTable.finalizeParentGroupingImport( xDPField, *pCacheField, aItemNames );
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void PivotTableField::finalizeDateGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, sal_Int32 nBaseFieldIdx )
+{
+ if( maDPFieldName.isEmpty() ) // prevent endless loops if file format is broken
+ {
+ if( PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
+ {
+ if( !pCacheField->isDatabaseField() && pCacheField->hasDateGrouping() && (pCacheField->getGroupBaseField() == nBaseFieldIdx) )
+ {
+ maDPFieldName = pCacheField->createDateGroupField( rxBaseDPField );
+ pCacheField->setFinalGroupName(maDPFieldName);
+ OSL_ENSURE( !maDPFieldName.isEmpty(), "PivotTableField::finalizeDateGroupingImport - cannot create date group field" );
+ }
+ }
+ }
+}
+
+void PivotTableField::finalizeParentGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, const PivotCacheField& rBaseCacheField, PivotCacheGroupItemVector& orItemNames )
+{
+ if( !maDPFieldName.isEmpty() ) // prevent endless loops if file format is broken
+ return;
+
+ PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex );
+ if( !pCacheField )
+ return;
+
+ // data field can have user defined groupname captions, apply them
+ // if they do
+ IdCaptionPairList captionList;
+ for( const auto& rItem : maItems )
+ {
+ if ( rItem.mnType == XML_data && rItem.msCaption.getLength() )
+ captionList.emplace_back( rItem.mnCacheItem, rItem.msCaption );
+ }
+ if ( !captionList.empty() )
+ pCacheField->applyItemCaptions( captionList );
+
+ maDPFieldName = pCacheField->createParentGroupField( rxBaseDPField, rBaseCacheField, orItemNames );
+ pCacheField->setFinalGroupName(maDPFieldName);
+ // on success, try to create nested group fields
+ Reference< XDataPilotField > xDPField = mrPivotTable.getDataPilotField( maDPFieldName );
+ if( xDPField.is() )
+ mrPivotTable.finalizeParentGroupingImport( xDPField, *pCacheField, orItemNames );
+}
+
+void PivotTableField::finalizeImportBasedOnCache( const Reference< XDataPilotDescriptor >& rxDPDesc)
+{
+ /* Process all fields based on source data, other fields (e.g. group
+ fields) are processed based on cache fields.*/
+ Reference< XDataPilotField > xDPField;
+ sal_Int32 nDatabaseIdx = mrPivotTable.getCacheDatabaseIndex( mnFieldIndex );
+ if( (nDatabaseIdx >= 0) && rxDPDesc.is() ) try
+ {
+ // Try to get the source field and its name from passed DataPilot descriptor
+ Reference< XIndexAccess > xDPFieldsIA( rxDPDesc->getDataPilotFields(), UNO_SET_THROW );
+ xDPField.set( xDPFieldsIA->getByIndex( nDatabaseIdx ), UNO_QUERY_THROW );
+ Reference< XNamed > xDPFieldName( xDPField, UNO_QUERY_THROW );
+ maDPFieldName = xDPFieldName->getName();
+ SAL_WARN_IF( maDPFieldName.isEmpty(), "sc.filter", "PivotTableField::finalizeImportBasedOnCache - no field name in source data found" );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // Use group names already generated for another table using the same group field.
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
+ {
+ if(!pCacheField->getFinalGroupName().isEmpty())
+ maDPFieldName = pCacheField->getFinalGroupName();
+ }
+}
+
+void PivotTableField::convertRowField()
+{
+ convertRowColPageField( XML_axisRow );
+}
+
+void PivotTableField::convertColField()
+{
+ convertRowColPageField( XML_axisCol );
+}
+
+void PivotTableField::convertHiddenField()
+{
+ convertRowColPageField( XML_TOKEN_INVALID );
+}
+
+void PivotTableField::convertPageField( const PTPageFieldModel& rPageField )
+{
+ OSL_ENSURE( rPageField.mnField == mnFieldIndex, "PivotTableField::convertPageField - wrong field index" );
+ // convert all settings common for row/column/page fields
+ Reference< XDataPilotField > xDPField = convertRowColPageField( XML_axisPage );
+
+ if( !xDPField.is() )
+ return;
+
+ PropertySet aPropSet( xDPField );
+
+ // find cache item used as 'selected page'
+ sal_Int32 nCacheItem = -1;
+ if( maModel.mbMultiPageItems )
+ {
+ // multiple items may be selected
+ OSL_ENSURE( rPageField.mnItem == BIFF12_PTPAGEFIELD_MULTIITEMS, "PivotTableField::convertPageField - unexpected cache item index" );
+ // try to find a single visible item
+ bool bHasMultiItems = false;
+ for( const auto& rItem : maItems )
+ {
+ if( (rItem.mnType == XML_data) && !rItem.mbHidden )
+ {
+ bHasMultiItems = nCacheItem >= 0;
+ nCacheItem = bHasMultiItems ? -1 : rItem.mnCacheItem;
+ }
+
+ if( bHasMultiItems )
+ break;
+ }
+ }
+ else
+ {
+ // single item may be selected
+ if( (0 <= rPageField.mnItem) && (o3tl::make_unsigned(rPageField.mnItem) < maItems.size()) )
+ nCacheItem = maItems[ rPageField.mnItem ].mnCacheItem;
+ }
+
+ if( nCacheItem < 0 )
+ return;
+
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheField( mnFieldIndex ) )
+ {
+ if( const PivotCacheItem* pSharedItem = pCacheField->getCacheItem( nCacheItem ) )
+ {
+ ScDPObject* pDPObj = mrPivotTable.getDPObject();
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(pCacheField->getName());
+ OUString aSelectedPage = pSharedItem->getFormattedName(*pDim, pDPObj, DateTime(getWorkbookSettings().getNullDate()));
+ aPropSet.setProperty( PROP_SelectedPage, aSelectedPage );
+ }
+ }
+}
+
+void PivotTableField::convertDataField( const PTDataFieldModel& rDataField )
+{
+ OSL_ENSURE( rDataField.mnField == mnFieldIndex, "PivotTableField::convertDataField - wrong field index" );
+ OSL_ENSURE( maModel.mbDataField, "PivotTableField::convertDataField - not a data field" );
+ Reference< XDataPilotField > xDPField = mrPivotTable.getDataPilotField( maDPFieldName );
+ if( !xDPField.is() )
+ return;
+
+ PropertySet aPropSet( xDPField );
+
+ // field orientation
+ aPropSet.setProperty( PROP_Orientation, DataPilotFieldOrientation_DATA );
+
+ if (!rDataField.maName.isEmpty())
+ aPropSet.setProperty(PROP_Name, rDataField.maName);
+
+ /* Field aggregation function. Documentation is a little bit confused
+ about which names to use for the count functions. The name 'count'
+ means 'count all', and 'countNum' means 'count numbers'. On the
+ other hand, for subtotals, 'countA' means 'count all', and 'count'
+ means 'count numbers' (see above). */
+ GeneralFunction eAggFunc = GeneralFunction_SUM;
+ switch( rDataField.mnSubtotal )
+ {
+ case XML_sum: eAggFunc = GeneralFunction_SUM; break;
+ case XML_count: eAggFunc = GeneralFunction_COUNT; break;
+ case XML_average: eAggFunc = GeneralFunction_AVERAGE; break;
+ case XML_max: eAggFunc = GeneralFunction_MAX; break;
+ case XML_min: eAggFunc = GeneralFunction_MIN; break;
+ case XML_product: eAggFunc = GeneralFunction_PRODUCT; break;
+ case XML_countNums: eAggFunc = GeneralFunction_COUNTNUMS; break;
+ case XML_stdDev: eAggFunc = GeneralFunction_STDEV; break;
+ case XML_stdDevp: eAggFunc = GeneralFunction_STDEVP; break;
+ case XML_var: eAggFunc = GeneralFunction_VAR; break;
+ case XML_varp: eAggFunc = GeneralFunction_VARP; break;
+ default: OSL_FAIL( "PivotTableField::convertDataField - unknown aggregation function" );
+ }
+ aPropSet.setProperty( PROP_Function, eAggFunc );
+
+ // field reference ('show data as')
+ DataPilotFieldReference aReference;
+ aReference.ReferenceType = DataPilotFieldReferenceType::NONE;
+ switch( rDataField.mnShowDataAs )
+ {
+ case XML_difference: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_DIFFERENCE; break;
+ case XML_percent: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_PERCENTAGE; break;
+ case XML_percentDiff: aReference.ReferenceType = DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE; break;
+ case XML_runTotal: aReference.ReferenceType = DataPilotFieldReferenceType::RUNNING_TOTAL; break;
+ case XML_percentOfRow: aReference.ReferenceType = DataPilotFieldReferenceType::ROW_PERCENTAGE; break;
+ case XML_percentOfCol: aReference.ReferenceType = DataPilotFieldReferenceType::COLUMN_PERCENTAGE; break;
+ case XML_percentOfTotal: aReference.ReferenceType = DataPilotFieldReferenceType::TOTAL_PERCENTAGE; break;
+ case XML_index: aReference.ReferenceType = DataPilotFieldReferenceType::INDEX; break;
+ }
+ if( aReference.ReferenceType == DataPilotFieldReferenceType::NONE )
+ return;
+
+ const PivotCacheField* pCacheField = mrPivotTable.getCacheField( rDataField.mnBaseField );
+ if( !pCacheField )
+ return;
+
+ aReference.ReferenceField = pCacheField->getName();
+ switch( rDataField.mnBaseItem )
+ {
+ case OOX_PT_PREVIOUS_ITEM:
+ aReference.ReferenceItemType = DataPilotFieldReferenceItemType::PREVIOUS;
+ break;
+ case OOX_PT_NEXT_ITEM:
+ aReference.ReferenceItemType = DataPilotFieldReferenceItemType::NEXT;
+ break;
+ default:
+ aReference.ReferenceItemType = DataPilotFieldReferenceItemType::NAMED;
+ if( const PivotCacheItem* pCacheItem = pCacheField->getCacheItem( rDataField.mnBaseItem ) )
+ aReference.ReferenceItemName = pCacheItem->getName();
+ }
+ aPropSet.setProperty( PROP_Reference, aReference );
+}
+
+// private --------------------------------------------------------------------
+
+Reference< XDataPilotField > PivotTableField::convertRowColPageField( sal_Int32 nAxis )
+{
+ bool bDataLayout = mnFieldIndex == OOX_PT_DATALAYOUTFIELD;
+ Reference< XDataPilotField > xDPField = bDataLayout ? mrPivotTable.getDataLayoutField() : mrPivotTable.getDataPilotField( maDPFieldName );
+ OSL_ENSURE( bDataLayout || (nAxis == maModel.mnAxis), "PivotTableField::convertRowColPageField - field axis mismatch" );
+
+ if( xDPField.is() )
+ {
+ // TODO: Use this to set properties directly, bypassing the slow uno layer.
+ ScDPObject* pDPObj = mrPivotTable.getDPObject();
+
+ PropertySet aPropSet( xDPField );
+
+ // field orientation
+ DataPilotFieldOrientation eFieldOrient = DataPilotFieldOrientation_HIDDEN;
+ switch( nAxis )
+ {
+ case XML_axisRow: eFieldOrient = DataPilotFieldOrientation_ROW; break;
+ case XML_axisCol: eFieldOrient = DataPilotFieldOrientation_COLUMN; break;
+ case XML_axisPage: eFieldOrient = DataPilotFieldOrientation_PAGE; break;
+ }
+ if( eFieldOrient != DataPilotFieldOrientation_HIDDEN )
+ aPropSet.setProperty( PROP_Orientation, eFieldOrient );
+
+ // all other settings not for the data layout field
+ if( !bDataLayout )
+ {
+ /* Field subtotal functions. Ignore the 'defaultSubtotal' flag, if
+ explicit functions are set. This is different behaviour between
+ XML (where 'defaultSubtotal' is set regardless of other
+ functions) and binary formats (where 'defaultSubtotal' is not
+ set if other functions are set). */
+ ::std::vector< GeneralFunction > aSubtotals;
+ /* Order of subtotals is fixed in Excel. Documentation is a little
+ bit confused about which names to use for the count functions.
+ For subtotals, 'countA' means 'count all', and 'count' means
+ 'count numbers'. On the other hand, for the data field
+ aggregation function, 'count' means 'count all', and 'countNum'
+ means 'count numbers' (see below). */
+ if( maModel.mbSumSubtotal ) aSubtotals.push_back( GeneralFunction_SUM );
+ if( maModel.mbCountASubtotal ) aSubtotals.push_back( GeneralFunction_COUNT );
+ if( maModel.mbAverageSubtotal ) aSubtotals.push_back( GeneralFunction_AVERAGE );
+ if( maModel.mbMaxSubtotal ) aSubtotals.push_back( GeneralFunction_MAX );
+ if( maModel.mbMinSubtotal ) aSubtotals.push_back( GeneralFunction_MIN );
+ if( maModel.mbProductSubtotal ) aSubtotals.push_back( GeneralFunction_PRODUCT );
+ if( maModel.mbCountSubtotal ) aSubtotals.push_back( GeneralFunction_COUNTNUMS );
+ if( maModel.mbStdDevSubtotal ) aSubtotals.push_back( GeneralFunction_STDEV );
+ if( maModel.mbStdDevPSubtotal ) aSubtotals.push_back( GeneralFunction_STDEVP );
+ if( maModel.mbVarSubtotal ) aSubtotals.push_back( GeneralFunction_VAR );
+ if( maModel.mbVarPSubtotal ) aSubtotals.push_back( GeneralFunction_VARP );
+ // if no function is set manually, check the 'defaultSubtotal' flag
+ if( aSubtotals.empty() && maModel.mbDefaultSubtotal )
+ aSubtotals.push_back( GeneralFunction_AUTO );
+ aPropSet.setProperty( PROP_Subtotals, comphelper::containerToSequence( aSubtotals ) );
+
+ // layout settings
+ DataPilotFieldLayoutInfo aLayoutInfo;
+ aLayoutInfo.LayoutMode = maModel.mbOutline ?
+ (maModel.mbSubtotalTop ? DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP : DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM) :
+ DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ aLayoutInfo.AddEmptyLines = maModel.mbInsertBlankRow;
+ aPropSet.setProperty( PROP_LayoutInfo, aLayoutInfo );
+ aPropSet.setProperty( PROP_ShowEmpty, maModel.mbShowAll );
+
+ // auto show (OOXML/BIFF12 only)
+ if( maModel.mbAutoShow )
+ {
+ DataPilotFieldAutoShowInfo aAutoShowInfo;
+ aAutoShowInfo.IsEnabled = true;
+ aAutoShowInfo.ShowItemsMode = maModel.mbTopAutoShow ? DataPilotFieldShowItemsMode::FROM_TOP : DataPilotFieldShowItemsMode::FROM_BOTTOM;
+ aAutoShowInfo.ItemCount = maModel.mnAutoShowItems;
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheFieldOfDataField( maModel.mnAutoShowRankBy ) )
+ aAutoShowInfo.DataField = pCacheField->getName();
+ aPropSet.setProperty( PROP_AutoShowInfo, aAutoShowInfo );
+ }
+
+ // auto sort
+ DataPilotFieldSortInfo aSortInfo;
+ aSortInfo.IsAscending = maModel.mnSortType == XML_ascending;
+ if( (maModel.mnSortType != XML_ascending) && (maModel.mnSortType != XML_descending) )
+ {
+ aSortInfo.Mode = DataPilotFieldSortMode::MANUAL;
+ }
+ else
+ {
+ const PivotCacheField* pCacheField = (maModel.mnSortRefField == OOX_PT_DATALAYOUTFIELD) ?
+ mrPivotTable.getCacheFieldOfDataField( maModel.mnSortRefItem ) : nullptr;
+ if( pCacheField )
+ {
+ aSortInfo.Mode = DataPilotFieldSortMode::DATA;
+ aSortInfo.Field = pCacheField->getName();
+ }
+ else
+ {
+ aSortInfo.Mode = DataPilotFieldSortMode::NAME;
+ }
+ }
+ aPropSet.setProperty( PROP_SortInfo, aSortInfo );
+
+ // item settings
+ if (const PivotCacheField* pCacheField = mrPivotTable.getCacheField(mnFieldIndex))
+ {
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(pCacheField->getName());
+ SAL_WARN_IF(!pDim, "sc.filter", "PivotTableField::convertRowColPageField - no Dimension found for: " << pCacheField->getName());
+
+ if (pDim) try
+ {
+ for( const auto& rItem : maItems )
+ {
+ if (rItem.mnType != XML_data)
+ continue;
+
+ const PivotCacheItem* pSharedItem = pCacheField->getCacheItem(rItem.mnCacheItem);
+ if (!pSharedItem)
+ continue;
+
+ try
+ {
+ ScDPSaveMember* pMem = pDim->GetMemberByName(pSharedItem->getFormattedName(*pDim, pDPObj, DateTime(getWorkbookSettings().getNullDate())));
+ pMem->SetShowDetails(rItem.mbShowDetails);
+ pMem->SetIsVisible(!rItem.mbHidden);
+ }
+ catch( Exception& )
+ {
+ // catch every failed container access to be able to process following items
+ }
+ }
+ }
+ catch (const Exception&) {}
+ }
+ }
+ }
+ return xDPField;
+}
+
+PTFilterModel::PTFilterModel() :
+ mfValue( 0.0 ),
+ mnField( -1 ),
+ mnMemPropField( -1 ),
+ mnType( XML_TOKEN_INVALID ),
+ mnEvalOrder( 0 ),
+ mnId( -1 ),
+ mnMeasureField( -1 ),
+ mnMeasureHier( -1 ),
+ mbTopFilter( true )
+{
+}
+
+PivotTableFilter::PivotTableFilter( const PivotTable& rPivotTable ) :
+ WorkbookHelper( rPivotTable ),
+ mrPivotTable( rPivotTable )
+{
+}
+
+void PivotTableFilter::importFilter( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.maDescription = rAttribs.getXString( XML_description, OUString() );
+ maModel.maStrValue1 = rAttribs.getXString( XML_stringValue1, OUString() );
+ maModel.maStrValue2 = rAttribs.getXString( XML_stringValue2, OUString() );
+ maModel.mnField = rAttribs.getInteger( XML_fld, -1 );
+ maModel.mnMemPropField = rAttribs.getInteger( XML_mpFld, -1 );
+ maModel.mnType = rAttribs.getToken( XML_type, XML_TOKEN_INVALID );
+ maModel.mnEvalOrder = rAttribs.getInteger( XML_evalOrder, 0 );
+ maModel.mnId = rAttribs.getInteger( XML_id, -1 );
+ maModel.mnMeasureField = rAttribs.getInteger( XML_iMeasureFld, -1 );
+ maModel.mnMeasureHier = rAttribs.getInteger( XML_iMeasureHier, -1 );
+}
+
+void PivotTableFilter::importTop10( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( rAttribs.getBool( XML_percent, false ) == (maModel.mnType == XML_percent),
+ "PivotTableFilter::importTop10 - unexpected value of percent attribute" );
+ maModel.mfValue = rAttribs.getDouble( XML_val, 0.0 );
+ maModel.mbTopFilter = rAttribs.getBool( XML_top, true );
+}
+
+void PivotTableFilter::importPTFilter( SequenceInputStream& rStrm )
+{
+ sal_Int32 nType;
+ sal_uInt16 nFlags;
+ maModel.mnField = rStrm.readInt32();
+ maModel.mnMemPropField = rStrm.readInt32();
+ nType = rStrm.readInt32();
+ rStrm.skip( 4 ); // unused
+ maModel.mnId = rStrm.readInt32();
+ maModel.mnMeasureField = rStrm.readInt32();
+ maModel.mnMeasureHier = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ if( getFlag( nFlags, BIFF12_PTFILTER_HASNAME ) )
+ rStrm >> maModel.maName;
+ if( getFlag( nFlags, BIFF12_PTFILTER_HASDESCRIPTION ) )
+ rStrm >> maModel.maDescription;
+ if( getFlag( nFlags, BIFF12_PTFILTER_HASSTRVALUE1 ) )
+ rStrm >> maModel.maStrValue1;
+ if( getFlag( nFlags, BIFF12_PTFILTER_HASSTRVALUE2 ) )
+ rStrm >> maModel.maStrValue2;
+
+ static const sal_Int32 spnTypes[] =
+ {
+ XML_unknown,
+ // data field top10 filter (1-3)
+ XML_count, XML_percent, XML_sum,
+ // caption filter (4-17)
+ XML_captionEqual, XML_captionNotEqual,
+ XML_captionBeginsWith, XML_captionNotBeginsWith, XML_captionEndsWith, XML_captionNotEndsWith,
+ XML_captionContains, XML_captionNotContains, XML_captionGreaterThan, XML_captionGreaterThanOrEqual,
+ XML_captionLessThan, XML_captionLessThanOrEqual, XML_captionBetween, XML_captionNotBetween,
+ // value filter (18-25)
+ XML_valueEqual, XML_valueNotEqual, XML_valueGreaterThan, XML_valueGreaterThanOrEqual,
+ XML_valueLessThan, XML_valueLessThanOrEqual, XML_valueBetween, XML_valueNotBetween,
+ // date filter (26-65)
+ XML_dateEqual, XML_dateOlderThan, XML_dateNewerThan, XML_dateBetween,
+ XML_tomorrow, XML_today, XML_yesterday, XML_nextWeek, XML_thisWeek, XML_lastWeek,
+ XML_nextMonth, XML_thisMonth, XML_lastMonth, XML_nextQuarter, XML_thisQuarter, XML_lastQuarter,
+ XML_nextYear, XML_thisYear, XML_lastYear, XML_yearToDate, XML_Q1, XML_Q2, XML_Q3, XML_Q4,
+ XML_M1, XML_M2, XML_M3, XML_M4, XML_M5, XML_M6, XML_M7, XML_M8, XML_M9, XML_M10, XML_M11, XML_M12,
+ XML_dateNotEqual, XML_dateOlderThanOrEqual, XML_dateNewerThanOrEqual, XML_dateNotBetween
+ };
+ maModel.mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID );
+}
+
+void PivotTableFilter::importTop10Filter( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nFlags;
+ nFlags = rStrm.readuChar();
+ maModel.mfValue = rStrm.readDouble();
+
+ SAL_WARN_IF(
+ getFlag(nFlags, BIFF12_TOP10FILTER_PERCENT) != (maModel.mnType == XML_percent),
+ "sc.filter",
+ "PivotTableFilter::importTop10 - unexpected value of percent attribute");
+ maModel.mbTopFilter = getFlag( nFlags, BIFF12_TOP10FILTER_TOP );
+}
+
+void PivotTableFilter::finalizeImport()
+{
+ // only simple top10 filter supported
+ if( maModel.mnType != XML_count )
+ return;
+
+ PropertySet aPropSet( mrPivotTable.getDataPilotField( maModel.mnField ) );
+ if( aPropSet.is() )
+ {
+ DataPilotFieldAutoShowInfo aAutoShowInfo;
+ aAutoShowInfo.IsEnabled = true;
+ aAutoShowInfo.ShowItemsMode = maModel.mbTopFilter ? DataPilotFieldShowItemsMode::FROM_TOP : DataPilotFieldShowItemsMode::FROM_BOTTOM;
+ aAutoShowInfo.ItemCount = getLimitedValue< sal_Int32, double >( maModel.mfValue, 0, SAL_MAX_INT32 );
+ if( const PivotCacheField* pCacheField = mrPivotTable.getCacheFieldOfDataField( maModel.mnMeasureField ) )
+ aAutoShowInfo.DataField = pCacheField->getName();
+ aPropSet.setProperty( PROP_AutoShowInfo, aAutoShowInfo );
+ }
+}
+
+PTDefinitionModel::PTDefinitionModel() :
+ mnCacheId( -1 ),
+ mnDataPosition( 0 ),
+ mnPageWrap( 0 ),
+ mnIndent( 1 ),
+ mnChartFormat( 0 ),
+ mbDataOnRows( false ),
+ mbShowError( false ),
+ mbShowMissing( true ),
+ mbShowItems( true ),
+ mbDisableFieldList( false ),
+ mbShowCalcMembers( true ),
+ mbVisualTotals( true ),
+ mbShowDrill( true ),
+ mbPrintDrill( false ),
+ mbEnableDrill( true ),
+ mbPreserveFormatting( true ),
+ mbUseAutoFormat( false ),
+ mbPageOverThenDown( false ),
+ mbSubtotalHiddenItems( false ),
+ mbRowGrandTotals( true ),
+ mbColGrandTotals( true ),
+ mbFieldPrintTitles( false ),
+ mbItemPrintTitles( false ),
+ mbMergeItem( false ),
+ mbShowEmptyRow( false ),
+ mbShowEmptyCol( false ),
+ mbShowHeaders( true ),
+ mbFieldListSortAsc( false ),
+ mbCustomListSort( true )
+{
+}
+
+PTLocationModel::PTLocationModel() :
+ mnFirstHeaderRow( 0 ),
+ mnFirstDataRow( 0 ),
+ mnFirstDataCol( 0 ),
+ mnRowPageCount( 0 ),
+ mnColPageCount( 0 )
+{
+}
+
+PivotTable::PivotTable( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mpDPObject(nullptr),
+ maDataField( *this, OOX_PT_DATALAYOUTFIELD ),
+ mpPivotCache( nullptr )
+{
+}
+
+void PivotTable::importPivotTableDefinition( const AttributeList& rAttribs )
+{
+ maDefModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maDefModel.maDataCaption = rAttribs.getXString( XML_dataCaption , OUString() );
+ maDefModel.maGrandTotalCaption = rAttribs.getXString( XML_grandTotalCaption, OUString() );
+ maDefModel.maRowHeaderCaption = rAttribs.getXString( XML_rowHeaderCaption, OUString() );
+ maDefModel.maColHeaderCaption = rAttribs.getXString( XML_colHeaderCaption, OUString() );
+ maDefModel.maErrorCaption = rAttribs.getXString( XML_errorCaption, OUString() );
+ maDefModel.maMissingCaption = rAttribs.getXString( XML_missingCaption, OUString() );
+ maDefModel.maPageStyle = rAttribs.getXString( XML_pageStyle, OUString() );
+ maDefModel.maPivotTableStyle = rAttribs.getXString( XML_pivotTableStyle, OUString() );
+ maDefModel.maVacatedStyle = rAttribs.getXString( XML_vacatedStyle, OUString() );
+ maDefModel.maTag = rAttribs.getXString( XML_tag, OUString() );
+ maDefModel.mnCacheId = rAttribs.getInteger( XML_cacheId, -1 );
+ maDefModel.mnDataPosition = rAttribs.getInteger( XML_dataPosition, 0 );
+ maDefModel.mnPageWrap = rAttribs.getInteger( XML_pageWrap, 0 );
+ maDefModel.mnIndent = rAttribs.getInteger( XML_indent, 1 );
+ maDefModel.mnChartFormat = rAttribs.getInteger( XML_chartFormat, 0 );
+ maDefModel.mnAutoFormatId = rAttribs.getInteger( XML_autoFormatId, 0 );
+ maDefModel.mbDataOnRows = rAttribs.getBool( XML_dataOnRows, false );
+ maDefModel.mbShowError = rAttribs.getBool( XML_showError, false );
+ maDefModel.mbShowMissing = rAttribs.getBool( XML_showMissing, true );
+ maDefModel.mbShowItems = rAttribs.getBool( XML_showItems, true );
+ maDefModel.mbDisableFieldList = rAttribs.getBool( XML_disableFieldList, false );
+ maDefModel.mbShowCalcMembers = rAttribs.getBool( XML_showCalcMbrs, true );
+ maDefModel.mbVisualTotals = rAttribs.getBool( XML_visualTotals, true );
+ maDefModel.mbShowDrill = rAttribs.getBool( XML_showDrill, true );
+ maDefModel.mbPrintDrill = rAttribs.getBool( XML_printDrill, false );
+ maDefModel.mbEnableDrill = rAttribs.getBool( XML_enableDrill, true );
+ maDefModel.mbPreserveFormatting = rAttribs.getBool( XML_preserveFormatting, true );
+ maDefModel.mbUseAutoFormat = rAttribs.getBool( XML_useAutoFormatting, false );
+ maDefModel.mbPageOverThenDown = rAttribs.getBool( XML_pageOverThenDown, false );
+ maDefModel.mbSubtotalHiddenItems = rAttribs.getBool( XML_subtotalHiddenItems, false );
+ maDefModel.mbRowGrandTotals = rAttribs.getBool( XML_rowGrandTotals, true );
+ maDefModel.mbColGrandTotals = rAttribs.getBool( XML_colGrandTotals, true );
+ maDefModel.mbFieldPrintTitles = rAttribs.getBool( XML_fieldPrintTitles, false );
+ maDefModel.mbItemPrintTitles = rAttribs.getBool( XML_itemPrintTitles, false );
+ maDefModel.mbMergeItem = rAttribs.getBool( XML_mergeItem, false );
+ maDefModel.mbShowEmptyRow = rAttribs.getBool( XML_showEmptyRow, false );
+ maDefModel.mbShowEmptyCol = rAttribs.getBool( XML_showEmptyCol, false );
+ maDefModel.mbShowHeaders = rAttribs.getBool( XML_showHeaders, true );
+ maDefModel.mbFieldListSortAsc = rAttribs.getBool( XML_fieldListSortAscending, false );
+ maDefModel.mbCustomListSort = rAttribs.getBool( XML_customListSort, true );
+ maDefModel.mbApplyNumFmt = rAttribs.getBool( XML_applyNumberFormats, false );
+ maDefModel.mbApplyFont = rAttribs.getBool( XML_applyFontFormats, false );
+ maDefModel.mbApplyAlignment = rAttribs.getBool( XML_applyAlignmentFormats, false );
+ maDefModel.mbApplyBorder = rAttribs.getBool( XML_applyBorderFormats, false );
+ maDefModel.mbApplyFill = rAttribs.getBool( XML_applyPatternFormats, false );
+ // OOXML and BIFF12 documentation differ: OOXML mentions width/height, BIFF12 mentions protection
+ maDefModel.mbApplyProtection = rAttribs.getBool( XML_applyWidthHeightFormats, false );
+}
+
+void PivotTable::importLocation( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ AddressConverter::convertToCellRangeUnchecked( maLocationModel.maRange, rAttribs.getString( XML_ref, OUString() ), nSheet );
+ maLocationModel.mnFirstHeaderRow = rAttribs.getInteger( XML_firstHeaderRow, 0 );
+ maLocationModel.mnFirstDataRow = rAttribs.getInteger( XML_firstDataRow, 0 );
+ maLocationModel.mnFirstDataCol = rAttribs.getInteger( XML_firstDataCol, 0 );
+ maLocationModel.mnRowPageCount = rAttribs.getInteger( XML_rowPageCount, 0 );
+ maLocationModel.mnColPageCount = rAttribs.getInteger( XML_colPageCount, 0 );
+}
+
+void PivotTable::importRowField( const AttributeList& rAttribs )
+{
+ importField( maRowFields, rAttribs );
+}
+
+void PivotTable::importColField( const AttributeList& rAttribs )
+{
+ importField( maColFields, rAttribs );
+}
+
+void PivotTable::importPageField( const AttributeList& rAttribs )
+{
+ PTPageFieldModel aModel;
+ aModel.maName = rAttribs.getXString( XML_name, OUString() );
+ aModel.mnField = rAttribs.getInteger( XML_fld, -1 );
+ // specification is wrong, XML_item is not the cache item, but the field item
+ aModel.mnItem = rAttribs.getInteger( XML_item, BIFF12_PTPAGEFIELD_MULTIITEMS );
+ maPageFields.push_back( aModel );
+}
+
+void PivotTable::importDataField( const AttributeList& rAttribs )
+{
+ PTDataFieldModel aModel;
+ aModel.maName = rAttribs.getXString( XML_name, OUString() );
+ aModel.mnField = rAttribs.getInteger( XML_fld, -1 );
+ aModel.mnSubtotal = rAttribs.getToken( XML_subtotal, XML_sum );
+ aModel.mnShowDataAs = rAttribs.getToken( XML_showDataAs, XML_normal );
+ aModel.mnBaseField = rAttribs.getInteger( XML_baseField, -1 );
+ aModel.mnBaseItem = rAttribs.getInteger( XML_baseItem, -1 );
+ aModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ maDataFields.push_back( aModel );
+}
+
+void PivotTable::putToInteropGrabBag(const OUString& sName, const AttributeList& rAttribs)
+{
+ if (auto xFastAttributeList = rAttribs.getFastAttributeList())
+ {
+ // Store both known and unknown attribute sequences to the grab bag as is
+ css::uno::Sequence<css::xml::FastAttribute> aFast = xFastAttributeList->getFastAttributes();
+ css::uno::Sequence<css::xml::Attribute> aUnk = xFastAttributeList->getUnknownAttributes();
+ css::uno::Sequence<css::uno::Any> aVal{ css::uno::Any(aFast), css::uno::Any(aUnk) };
+ maInteropGrabBag[sName] <<= aVal;
+ }
+}
+
+void PivotTable::importPTDefinition( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags1, nFlags2, nFlags3;
+ sal_uInt8 nDataAxis;
+ nFlags1 = rStrm.readuInt32();
+ nFlags2 = rStrm.readuInt32();
+ nFlags3 = rStrm.readuInt32();
+ nDataAxis = rStrm.readuChar();
+ maDefModel.mnPageWrap = rStrm.readuInt8();
+ rStrm.skip( 2 ); // refresh versions
+ maDefModel.mnDataPosition = rStrm.readInt32();
+ maDefModel.mnAutoFormatId = rStrm.readuInt16();
+ rStrm.skip( 2 ); // unused
+ maDefModel.mnChartFormat = rStrm.readInt32();
+ maDefModel.mnCacheId = rStrm.readInt32();
+ rStrm >> maDefModel.maName;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASDATACAPTION ) )
+ rStrm >> maDefModel.maDataCaption;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASGRANDTOTALCAPTION ) )
+ rStrm >> maDefModel.maGrandTotalCaption;
+ if( !getFlag( nFlags3, BIFF12_PTDEF_NOERRORCAPTION ) ) // missing flag indicates existing string
+ rStrm >> maDefModel.maErrorCaption;
+ if( !getFlag( nFlags3, BIFF12_PTDEF_NOMISSINGCAPTION ) ) // missing flag indicates existing string
+ rStrm >> maDefModel.maMissingCaption;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASPAGESTYLE ) )
+ rStrm >> maDefModel.maPageStyle;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASPIVOTTABLESTYLE ) )
+ rStrm >> maDefModel.maPivotTableStyle;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASVACATEDSTYLE ) )
+ rStrm >> maDefModel.maVacatedStyle;
+ if( getFlag( nFlags2, BIFF12_PTDEF_HASTAG ) )
+ rStrm >> maDefModel.maTag;
+ if( getFlag( nFlags3, BIFF12_PTDEF_HASCOLHEADERCAPTION ) ) // TODO: right order (col/row)? spec is unclear
+ rStrm >> maDefModel.maColHeaderCaption;
+ if( getFlag( nFlags3, BIFF12_PTDEF_HASROWHEADERCAPTION ) )
+ rStrm >> maDefModel.maRowHeaderCaption;
+
+ SAL_WARN_IF(
+ (nDataAxis != BIFF12_PTDEF_ROWAXIS) && (nDataAxis != BIFF12_PTDEF_COLAXIS),
+ "sc.filter",
+ "PivotTable::importPTDefinition - unexpected axis position for data field");
+
+ maDefModel.mnIndent = extractValue< sal_uInt8 >( nFlags1, 24, 7 );
+ maDefModel.mbDataOnRows = nDataAxis == BIFF12_PTDEF_ROWAXIS;
+ maDefModel.mbShowError = getFlag( nFlags2, BIFF12_PTDEF_SHOWERROR );
+ maDefModel.mbShowMissing = getFlag( nFlags2, BIFF12_PTDEF_SHOWMISSING );
+ maDefModel.mbShowItems = getFlag( nFlags1, BIFF12_PTDEF_SHOWITEMS );
+ maDefModel.mbDisableFieldList = getFlag( nFlags1, BIFF12_PTDEF_DISABLEFIELDLIST );
+ maDefModel.mbShowCalcMembers = !getFlag( nFlags1, BIFF12_PTDEF_HIDECALCMEMBERS );
+ maDefModel.mbVisualTotals = !getFlag( nFlags1, BIFF12_PTDEF_WITHHIDDENTOTALS );
+ maDefModel.mbShowDrill = !getFlag( nFlags1, BIFF12_PTDEF_HIDEDRILL );
+ maDefModel.mbPrintDrill = getFlag( nFlags1, BIFF12_PTDEF_PRINTDRILL );
+ maDefModel.mbEnableDrill = getFlag( nFlags2, BIFF12_PTDEF_ENABLEDRILL );
+ maDefModel.mbPreserveFormatting = getFlag( nFlags2, BIFF12_PTDEF_PRESERVEFORMATTING );
+ maDefModel.mbUseAutoFormat = getFlag( nFlags2, BIFF12_PTDEF_USEAUTOFORMAT );
+ maDefModel.mbPageOverThenDown = getFlag( nFlags2, BIFF12_PTDEF_PAGEOVERTHENDOWN );
+ maDefModel.mbSubtotalHiddenItems = getFlag( nFlags2, BIFF12_PTDEF_SUBTOTALHIDDENITEMS );
+ maDefModel.mbRowGrandTotals = getFlag( nFlags2, BIFF12_PTDEF_ROWGRANDTOTALS );
+ maDefModel.mbColGrandTotals = getFlag( nFlags2, BIFF12_PTDEF_COLGRANDTOTALS );
+ maDefModel.mbFieldPrintTitles = getFlag( nFlags2, BIFF12_PTDEF_FIELDPRINTTITLES );
+ maDefModel.mbItemPrintTitles = getFlag( nFlags2, BIFF12_PTDEF_ITEMPRINTTITLES );
+ maDefModel.mbMergeItem = getFlag( nFlags2, BIFF12_PTDEF_MERGEITEM );
+ maDefModel.mbApplyNumFmt = getFlag( nFlags2, BIFF12_PTDEF_APPLYNUMFMT );
+ maDefModel.mbApplyFont = getFlag( nFlags2, BIFF12_PTDEF_APPLYFONT );
+ maDefModel.mbApplyAlignment = getFlag( nFlags2, BIFF12_PTDEF_APPLYALIGNMENT );
+ maDefModel.mbApplyBorder = getFlag( nFlags2, BIFF12_PTDEF_APPLYBORDER );
+ maDefModel.mbApplyFill = getFlag( nFlags2, BIFF12_PTDEF_APPLYFILL );
+ maDefModel.mbApplyProtection = getFlag( nFlags2, BIFF12_PTDEF_APPLYPROTECTION );
+ maDefModel.mbShowEmptyRow = getFlag( nFlags2, BIFF12_PTDEF_SHOWEMPTYROW );
+ maDefModel.mbShowEmptyCol = getFlag( nFlags2, BIFF12_PTDEF_SHOWEMPTYCOL );
+ maDefModel.mbShowHeaders = !getFlag( nFlags1, BIFF12_PTDEF_HIDEHEADERS );
+ maDefModel.mbFieldListSortAsc = getFlag( nFlags3, BIFF12_PTDEF_FIELDLISTSORTASC );
+ maDefModel.mbCustomListSort = !getFlag( nFlags3, BIFF12_PTDEF_NOCUSTOMLISTSORT );
+}
+
+void PivotTable::importPTLocation( SequenceInputStream& rStrm, sal_Int16 nSheet )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ maLocationModel.mnFirstHeaderRow = rStrm.readInt32();
+ maLocationModel.mnFirstDataRow = rStrm.readInt32();
+ maLocationModel.mnFirstDataCol = rStrm.readInt32();
+ maLocationModel.mnRowPageCount = rStrm.readInt32();
+ maLocationModel.mnColPageCount = rStrm.readInt32();
+ AddressConverter::convertToCellRangeUnchecked( maLocationModel.maRange, aBinRange, nSheet );
+}
+
+void PivotTable::importPTRowFields( SequenceInputStream& rStrm )
+{
+ importFields( maRowFields, rStrm );
+}
+
+void PivotTable::importPTColFields( SequenceInputStream& rStrm )
+{
+ importFields( maColFields, rStrm );
+}
+
+void PivotTable::importPTPageField( SequenceInputStream& rStrm )
+{
+ PTPageFieldModel aModel;
+ sal_uInt8 nFlags;
+ aModel.mnField = rStrm.readInt32();
+ aModel.mnItem = rStrm.readInt32();
+ rStrm.skip( 4 ); // hierarchy
+ nFlags = rStrm.readuChar();
+ if( getFlag( nFlags, BIFF12_PTPAGEFIELD_HASNAME ) )
+ rStrm >> aModel.maName;
+ maPageFields.push_back( aModel );
+}
+
+void PivotTable::importPTDataField( SequenceInputStream& rStrm )
+{
+ PTDataFieldModel aModel;
+ sal_Int32 nSubtotal, nShowDataAs;
+ sal_uInt8 nHasName;
+ aModel.mnField = rStrm.readInt32( );
+ nSubtotal = rStrm.readInt32();
+ nShowDataAs = rStrm.readInt32();
+ aModel.mnBaseField = rStrm.readInt32();
+ aModel.mnBaseItem = rStrm.readInt32();
+ aModel.mnNumFmtId = rStrm.readInt32();
+ nHasName = rStrm.readuChar();
+ if( nHasName == 1 )
+ rStrm >> aModel.maName;
+ aModel.setBiffSubtotal( nSubtotal );
+ aModel.setBiffShowDataAs( nShowDataAs );
+ maDataFields.push_back( aModel );
+}
+
+PivotTableField& PivotTable::createTableField()
+{
+ sal_Int32 nFieldIndex = static_cast< sal_Int32 >( maFields.size() );
+ PivotTableFieldVector::value_type xTableField = std::make_shared<PivotTableField>( *this, nFieldIndex );
+ maFields.push_back( xTableField );
+ return *xTableField;
+}
+
+PivotTableFilter& PivotTable::createTableFilter()
+{
+ PivotTableFilterVector::value_type xTableFilter = std::make_shared<PivotTableFilter>( *this );
+ maFilters.push_back( xTableFilter );
+ return *xTableFilter;
+}
+
+void PivotTable::finalizeImport()
+{
+ if( !getAddressConverter().validateCellRange( maLocationModel.maRange, true, true ) )
+ return;
+
+ mpPivotCache = getPivotCaches().importPivotCacheFragment( maDefModel.mnCacheId );
+ if( !mpPivotCache || !mpPivotCache->isValidDataSource() || maDefModel.maName.isEmpty() )
+ return;
+
+ // clear destination area of the original pivot table
+ try
+ {
+ Reference< XSheetOperation > xSheetOp( getCellRangeFromDoc( maLocationModel.maRange ), UNO_QUERY_THROW );
+ using namespace ::com::sun::star::sheet::CellFlags;
+ xSheetOp->clearContents( VALUE | DATETIME | STRING | FORMULA | HARDATTR | STYLES | EDITATTR | FORMATTED );
+ }
+ catch( Exception& )
+ {
+ }
+
+ try
+ {
+ // create a new data pilot descriptor based on the source data
+ Reference< XDataPilotTablesSupplier > xDPTablesSupp( getSheetFromDoc( maLocationModel.maRange.aStart.Tab() ), UNO_QUERY_THROW );
+ Reference< XDataPilotTables > xDPTables( xDPTablesSupp->getDataPilotTables(), UNO_SET_THROW );
+ mxDPDescriptor.set( xDPTables->createDataPilotDescriptor(), UNO_SET_THROW );
+ ScRange aRange = mpPivotCache->getSourceRange();
+ CellRangeAddress aCellRangeAddress( aRange.aStart.Tab(),
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ mxDPDescriptor->setSourceRange( aCellRangeAddress );
+ mxDPDescriptor->setTag( maDefModel.maTag );
+
+ // TODO: This is a hack. Eventually we need to convert the whole thing to the internal API.
+ auto pImpl = comphelper::getFromUnoTunnel<ScDataPilotDescriptorBase>(mxDPDescriptor);
+ if (!pImpl)
+ return;
+
+ mpDPObject = pImpl->GetDPObject();
+ if (!mpDPObject)
+ return;
+
+ // global data pilot properties
+ PropertySet aDescProp( mxDPDescriptor );
+ aDescProp.setProperty( PROP_ColumnGrand, maDefModel.mbColGrandTotals );
+ aDescProp.setProperty( PROP_RowGrand, maDefModel.mbRowGrandTotals );
+ aDescProp.setProperty( PROP_ShowFilterButton, false );
+ aDescProp.setProperty( PROP_DrillDownOnDoubleClick, maDefModel.mbEnableDrill );
+
+ // finalize all fields, this finds field names and creates grouping fields
+ finalizeFieldsImport();
+
+ // all row fields
+ for( const auto& rRowField : maRowFields )
+ if( PivotTableField* pField = getTableField( rRowField ) )
+ pField->convertRowField();
+
+ // all column fields
+ for( const auto& rColField : maColFields )
+ if( PivotTableField* pField = getTableField( rColField ) )
+ pField->convertColField();
+
+ // all page fields
+ for( const auto& rPageField : maPageFields )
+ if( PivotTableField* pField = getTableField( rPageField.mnField ) )
+ pField->convertPageField( rPageField );
+
+ // all hidden fields
+ ::std::set< sal_Int32 > aVisFields;
+ aVisFields.insert( maRowFields.begin(), maRowFields.end() );
+ aVisFields.insert( maColFields.begin(), maColFields.end() );
+ for( const auto& rPageField : maPageFields )
+ aVisFields.insert( rPageField.mnField );
+ sal_Int32 nIndex = 0;
+ for( auto& rxField : maFields )
+ {
+ if( aVisFields.count( nIndex ) == 0 )
+ rxField->convertHiddenField();
+ ++nIndex;
+ }
+
+ // all data fields
+ for( auto& rDataField : maDataFields )
+ {
+ if( const PivotCacheField* pCacheField = getCacheField( rDataField.mnField ) )
+ {
+ if ( pCacheField-> getGroupBaseField() != -1 )
+ rDataField.mnField = pCacheField-> getGroupBaseField();
+ }
+ if( PivotTableField* pField = getTableField( rDataField.mnField ) )
+ pField->convertDataField( rDataField );
+ }
+
+ // filters
+ maFilters.forEachMem( &PivotTableFilter::finalizeImport );
+
+ // calculate base position of table
+ CellAddress aPos( maLocationModel.maRange.aStart.Tab(), maLocationModel.maRange.aStart.Col(), maLocationModel.maRange.aStart.Row() );
+ /* If page fields exist, include them into the destination
+ area (they are excluded in Excel). Add an extra blank row. */
+ if( !maPageFields.empty() )
+ aPos.Row = ::std::max< sal_Int32 >( static_cast< sal_Int32 >( aPos.Row - maPageFields.size() - 1 ), 0 );
+
+ // save interop grab bag
+ mpDPObject->PutInteropGrabBag(std::move(maInteropGrabBag));
+
+ // insert the DataPilot table into the sheet
+ xDPTables->insertNewByName( maDefModel.maName, aPos, mxDPDescriptor );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sc", "PivotTable::finalizeImport - exception while creating the DataPilot table" );
+ }
+}
+
+void PivotTable::finalizeFieldsImport()
+{
+ if (maFields.empty())
+ return;
+
+ /* Check whether group fields are already imported for another table
+ sharing the same groups. */
+ ScDPObject* pDPObj = getDPObject();
+ const ScDocument& rDoc = getDocImport().getDoc();
+ if (rDoc.HasPivotTable())
+ {
+ const ScDPCollection* pDPCollection = rDoc.GetDPCollection();
+ assert(pDPCollection != nullptr);
+ const ScDPDimensionSaveData* pGroups = nullptr;
+ bool bRefFound = pDPCollection->GetReferenceGroups(*pDPObj, &pGroups);
+ // Apply reference groups on this table.
+ if (bRefFound && pGroups && pGroups->HasGroupDimensions()) {
+ ScDPSaveData* pSaveData = pDPObj->GetSaveData();
+ if (pSaveData) {
+ pSaveData->SetDimensionData(pGroups);
+ pDPObj->ReloadGroupTableData();
+ maFields.forEachMem(&PivotTableField::finalizeImportBasedOnCache, ::std::cref(mxDPDescriptor));
+ return;
+ }
+
+ }
+ }
+ maFields.forEachMem(&PivotTableField::finalizeImport, ::std::cref(mxDPDescriptor));
+}
+
+void PivotTable::finalizeDateGroupingImport( const Reference< XDataPilotField >& rxBaseDPField, sal_Int32 nBaseFieldIdx )
+{
+ // process all fields, there is no chaining information in the cache fields
+ maFields.forEachMem( &PivotTableField::finalizeDateGroupingImport, ::std::cref(rxBaseDPField), nBaseFieldIdx );
+}
+
+void PivotTable::finalizeParentGroupingImport( const Reference< XDataPilotField >& rxBaseDPField,
+ const PivotCacheField& rBaseCacheField, PivotCacheGroupItemVector& orItemNames )
+{
+ // try to create parent group fields that group the items of the passed base field
+ if( PivotTableField* pParentTableField = maFields.get( rBaseCacheField.getParentGroupField() ).get() )
+ pParentTableField->finalizeParentGroupingImport( rxBaseDPField, rBaseCacheField, orItemNames );
+}
+
+Reference< XDataPilotField > PivotTable::getDataPilotField( const OUString& rFieldName ) const
+{
+ Reference< XDataPilotField > xDPField;
+ if( !rFieldName.isEmpty() && mxDPDescriptor.is() ) try
+ {
+ Reference< XNameAccess > xDPFieldsNA( mxDPDescriptor->getDataPilotFields(), UNO_QUERY_THROW );
+ xDPField.set( xDPFieldsNA->getByName( rFieldName ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xDPField;
+}
+
+Reference< XDataPilotField > PivotTable::getDataPilotField( sal_Int32 nFieldIdx ) const
+{
+ Reference< XDataPilotField > xDPField;
+ if( const PivotTableField* pTableField = maFields.get( nFieldIdx ).get() )
+ xDPField = getDataPilotField( pTableField->getDPFieldName() );
+ return xDPField;
+}
+
+Reference< XDataPilotField > PivotTable::getDataLayoutField() const
+{
+ Reference< XDataPilotField > xDPField;
+ try
+ {
+ Reference< XDataPilotDataLayoutFieldSupplier > xDPDataFieldSupp( mxDPDescriptor, UNO_QUERY_THROW );
+ xDPField = xDPDataFieldSupp->getDataLayoutField();
+ }
+ catch( Exception& )
+ {
+ }
+ return xDPField;
+}
+
+PivotCacheField* PivotTable::getCacheField( sal_Int32 nFieldIdx )
+{
+ return mpPivotCache ? mpPivotCache->getCacheField( nFieldIdx ) : nullptr;
+}
+
+const PivotCacheField* PivotTable::getCacheField( sal_Int32 nFieldIdx ) const
+{
+ return mpPivotCache ? mpPivotCache->getCacheField( nFieldIdx ) : nullptr;
+}
+
+const PivotCacheField* PivotTable::getCacheFieldOfDataField( sal_Int32 nDataItemIdx ) const
+{
+ const PTDataFieldModel* pDataField = ContainerHelper::getVectorElement( maDataFields, nDataItemIdx );
+ return pDataField ? getCacheField( pDataField->mnField ) : nullptr;
+}
+
+sal_Int32 PivotTable::getCacheDatabaseIndex( sal_Int32 nFieldIdx ) const
+{
+ return mpPivotCache ? mpPivotCache->getCacheDatabaseIndex( nFieldIdx ) : -1;
+}
+
+// private --------------------------------------------------------------------
+
+PivotTableField* PivotTable::getTableField( sal_Int32 nFieldIdx )
+{
+ return (nFieldIdx == OOX_PT_DATALAYOUTFIELD) ? &maDataField : maFields.get( nFieldIdx ).get();
+}
+
+void PivotTable::importField( IndexVector& orFields, const AttributeList& rAttribs )
+{
+ orFields.push_back( rAttribs.getInteger( XML_x, -1 ) );
+}
+
+void PivotTable::importFields( IndexVector& orFields, SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( orFields.empty(), "PivotTable::importFields - multiple record instances" );
+ orFields.clear();
+ sal_Int32 nCount = rStrm.readInt32();
+ OSL_ENSURE( 4 * nCount == rStrm.getRemaining(), "PivotTable::importFields - invalid field count" );
+ nCount = static_cast< sal_Int32 >( rStrm.getRemaining() / 4 );
+ for( sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx )
+ orFields.push_back( rStrm.readInt32() );
+}
+
+PivotTableBuffer::PivotTableBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+PivotTable& PivotTableBuffer::createPivotTable()
+{
+ PivotTableVector::value_type xTable = std::make_shared<PivotTable>( *this );
+ maTables.push_back( xTable );
+ return *xTable;
+}
+
+void PivotTableBuffer::finalizeImport()
+{
+ if(maTables.empty())
+ return;
+
+ // Create formula groups. This needs to be done before pivot tables, because
+ // import may lead to calling ScDPObject::GetSource(), which calls ScDPObject::CreateObjects(),
+ // which will ensure all the cells are not dirty, causing recalculation even though
+ // ScDPObject::GetSource() doesn't need it. And the recalculation is slower without formula
+ // groups set up. Fixing that properly seems quite complex, given that do-everything approach
+ // of ScDPObject, so at least ensure the calculation is efficient.
+ ScDocument& rDoc = getDocImport().getDoc();
+ rDoc.RegroupFormulaCells( ScRange( 0, 0, 0, rDoc.MaxCol(), rDoc.MaxRow(), rDoc.GetMaxTableNumber()));
+
+ maTables.forEachMem( &PivotTable::finalizeImport );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/pivottablefragment.cxx b/sc/source/filter/oox/pivottablefragment.cxx
new file mode 100644
index 000000000..76d2abdcd
--- /dev/null
+++ b/sc/source/filter/oox/pivottablefragment.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <pivottablefragment.hxx>
+#include <pivottablebuffer.hxx>
+#include <biffhelper.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+PivotTableFieldContext::PivotTableFieldContext( WorksheetFragmentBase& rFragment, PivotTableField& rTableField ) :
+ WorksheetContextBase( rFragment ),
+ mrTableField( rTableField )
+{
+}
+
+ContextHandlerRef PivotTableFieldContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( pivotField ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( items ): return this;
+ case XLS_TOKEN( autoSortScope ): return this;
+ }
+ break;
+ case XLS_TOKEN( items ):
+ if( nElement == XLS_TOKEN( item ) ) mrTableField.importItem( rAttribs );
+ break;
+ case XLS_TOKEN( autoSortScope ):
+ if( nElement == XLS_TOKEN( pivotArea ) ) return this;
+ break;
+ case XLS_TOKEN( pivotArea ):
+ if( nElement == XLS_TOKEN( references ) ) return this;
+ break;
+ case XLS_TOKEN( references ):
+ if( nElement == XLS_TOKEN( reference ) ) { mrTableField.importReference( rAttribs ); return this; }
+ break;
+ case XLS_TOKEN( reference ):
+ if( nElement == XLS_TOKEN( x ) ) mrTableField.importReferenceItem( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void PivotTableFieldContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrTableField.importPivotField( rAttribs );
+}
+
+ContextHandlerRef PivotTableFieldContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_PTFIELD:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PTFITEMS: return this;
+ case BIFF12_ID_AUTOSORTSCOPE: return this;
+ }
+ break;
+ case BIFF12_ID_PTFITEMS:
+ if( nRecId == BIFF12_ID_PTFITEM ) mrTableField.importPTFItem( rStrm );
+ break;
+ case BIFF12_ID_AUTOSORTSCOPE:
+ if( nRecId == BIFF12_ID_PIVOTAREA ) return this;
+ break;
+ case BIFF12_ID_PIVOTAREA:
+ if( nRecId == BIFF12_ID_PTREFERENCES ) return this;
+ break;
+ case BIFF12_ID_PTREFERENCES:
+ if( nRecId == BIFF12_ID_PTREFERENCE ) { mrTableField.importPTReference( rStrm ); return this; }
+ break;
+ case BIFF12_ID_PTREFERENCE:
+ if( nRecId == BIFF12_ID_PTREFERENCEITEM ) mrTableField.importPTReferenceItem( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+void PivotTableFieldContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrTableField.importPTField( rStrm );
+}
+
+PivotTableFilterContext::PivotTableFilterContext( WorksheetFragmentBase& rFragment, PivotTableFilter& rTableFilter ) :
+ WorksheetContextBase( rFragment ),
+ mrTableFilter( rTableFilter )
+{
+}
+
+ContextHandlerRef PivotTableFilterContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( filter ):
+ if( nElement == XLS_TOKEN( autoFilter ) ) return this;
+ break;
+ case XLS_TOKEN( autoFilter ):
+ if( nElement == XLS_TOKEN( filterColumn ) ) return this;
+ break;
+ case XLS_TOKEN( filterColumn ):
+ if( nElement == XLS_TOKEN( top10 ) ) mrTableFilter.importTop10( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void PivotTableFilterContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrTableFilter.importFilter( rAttribs );
+}
+
+ContextHandlerRef PivotTableFilterContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_PTFILTER:
+ if( nRecId == BIFF12_ID_AUTOFILTER ) return this;
+ break;
+ case BIFF12_ID_AUTOFILTER:
+ if( nRecId == BIFF12_ID_FILTERCOLUMN ) return this;
+ break;
+ case BIFF12_ID_FILTERCOLUMN:
+ if( nRecId == BIFF12_ID_TOP10FILTER ) mrTableFilter.importTop10Filter( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+void PivotTableFilterContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrTableFilter.importPTFilter( rStrm );
+}
+
+PivotTableFragment::PivotTableFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mrPivotTable( getPivotTables().createPivotTable() )
+{
+}
+
+ContextHandlerRef PivotTableFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( pivotTableDefinition ) ) { mrPivotTable.importPivotTableDefinition( rAttribs ); return this; }
+ break;
+
+ case XLS_TOKEN( pivotTableDefinition ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( location ): mrPivotTable.importLocation( rAttribs, getSheetIndex() ); break;
+ case XLS_TOKEN( pivotFields ): return this;
+ case XLS_TOKEN( rowFields ): return this;
+ case XLS_TOKEN( colFields ): return this;
+ case XLS_TOKEN( pageFields ): return this;
+ case XLS_TOKEN( dataFields ): return this;
+ case XLS_TOKEN( filters ): return this;
+ case XLS_TOKEN(pivotTableStyleInfo):
+ mrPivotTable.putToInteropGrabBag("pivotTableStyleInfo", rAttribs);
+ break;
+ }
+ break;
+
+ case XLS_TOKEN( pivotFields ):
+ if( nElement == XLS_TOKEN( pivotField ) ) return new PivotTableFieldContext( *this, mrPivotTable.createTableField() );
+ break;
+ case XLS_TOKEN( rowFields ):
+ if( nElement == XLS_TOKEN( field ) ) mrPivotTable.importRowField( rAttribs );
+ break;
+ case XLS_TOKEN( colFields ):
+ if( nElement == XLS_TOKEN( field ) ) mrPivotTable.importColField( rAttribs );
+ break;
+ case XLS_TOKEN( pageFields ):
+ if( nElement == XLS_TOKEN( pageField ) ) mrPivotTable.importPageField( rAttribs );
+ break;
+ case XLS_TOKEN( dataFields ):
+ if( nElement == XLS_TOKEN( dataField ) ) mrPivotTable.importDataField( rAttribs );
+ break;
+ case XLS_TOKEN( filters ):
+ if( nElement == XLS_TOKEN( filter ) ) return new PivotTableFilterContext( *this, mrPivotTable.createTableFilter() );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef PivotTableFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_PTDEFINITION ) { mrPivotTable.importPTDefinition( rStrm ); return this; }
+ break;
+
+ case BIFF12_ID_PTDEFINITION:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PTLOCATION: mrPivotTable.importPTLocation( rStrm, getSheetIndex() ); break;
+ case BIFF12_ID_PTFIELDS: return this;
+ case BIFF12_ID_PTROWFIELDS: mrPivotTable.importPTRowFields( rStrm ); break;
+ case BIFF12_ID_PTCOLFIELDS: mrPivotTable.importPTColFields( rStrm ); break;
+ case BIFF12_ID_PTPAGEFIELDS: return this;
+ case BIFF12_ID_PTDATAFIELDS: return this;
+ case BIFF12_ID_PTFILTERS: return this;
+ }
+ break;
+
+ case BIFF12_ID_PTFIELDS:
+ if( nRecId == BIFF12_ID_PTFIELD ) return new PivotTableFieldContext( *this, mrPivotTable.createTableField() );
+ break;
+ case BIFF12_ID_PTPAGEFIELDS:
+ if( nRecId == BIFF12_ID_PTPAGEFIELD ) mrPivotTable.importPTPageField( rStrm );
+ break;
+ case BIFF12_ID_PTDATAFIELDS:
+ if( nRecId == BIFF12_ID_PTDATAFIELD ) mrPivotTable.importPTDataField( rStrm );
+ break;
+ case BIFF12_ID_PTFILTERS:
+ if( nRecId == BIFF12_ID_PTFILTER ) return new PivotTableFilterContext( *this, mrPivotTable.createTableFilter() );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* PivotTableFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_AUTOFILTER, BIFF12_ID_AUTOFILTER + 1 },
+ { BIFF12_ID_AUTOSORTSCOPE, BIFF12_ID_AUTOSORTSCOPE + 1 },
+ { BIFF12_ID_FILTERCOLUMN, BIFF12_ID_FILTERCOLUMN + 1 },
+ { BIFF12_ID_PIVOTAREA, BIFF12_ID_PIVOTAREA + 1 },
+ { BIFF12_ID_PTCOLFIELDS, BIFF12_ID_PTCOLFIELDS + 1 },
+ { BIFF12_ID_PTDATAFIELD, BIFF12_ID_PTDATAFIELD + 1 },
+ { BIFF12_ID_PTDATAFIELDS, BIFF12_ID_PTDATAFIELDS + 1 },
+ { BIFF12_ID_PTDEFINITION, BIFF12_ID_PTDEFINITION + 35 },
+ { BIFF12_ID_PTFIELD, BIFF12_ID_PTFIELD + 1 },
+ { BIFF12_ID_PTFIELDS, BIFF12_ID_PTFIELDS + 1 },
+ { BIFF12_ID_PTFILTER, BIFF12_ID_PTFILTER + 1 },
+ { BIFF12_ID_PTFILTERS, BIFF12_ID_PTFILTERS + 1 },
+ { BIFF12_ID_PTFITEM, BIFF12_ID_PTFITEM - 1 },
+ { BIFF12_ID_PTFITEMS, BIFF12_ID_PTFITEMS + 1 },
+ { BIFF12_ID_PTLOCATION, BIFF12_ID_PTLOCATION - 1 },
+ { BIFF12_ID_PTPAGEFIELD, BIFF12_ID_PTPAGEFIELD + 1 },
+ { BIFF12_ID_PTPAGEFIELDS, BIFF12_ID_PTPAGEFIELDS + 1 },
+ { BIFF12_ID_PTREFERENCE, BIFF12_ID_PTREFERENCE + 1 },
+ { BIFF12_ID_PTREFERENCEITEM, BIFF12_ID_PTREFERENCEITEM + 1 },
+ { BIFF12_ID_PTREFERENCES, BIFF12_ID_PTREFERENCES + 1 },
+ { BIFF12_ID_PTROWFIELDS, BIFF12_ID_PTROWFIELDS + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/querytablebuffer.cxx b/sc/source/filter/oox/querytablebuffer.cxx
new file mode 100644
index 000000000..82f9821fc
--- /dev/null
+++ b/sc/source/filter/oox/querytablebuffer.cxx
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <querytablebuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/sheet/XAreaLink.hpp>
+#include <com/sun/star/sheet/XAreaLinks.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <connectionsbuffer.hxx>
+#include <defnamesbuffer.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt32 BIFF12_QUERYTABLE_HEADERS = 0x00000001;
+const sal_uInt32 BIFF12_QUERYTABLE_ROWNUMBERS = 0x00000002;
+const sal_uInt32 BIFF12_QUERYTABLE_DISABLEREFRESH = 0x00000004;
+const sal_uInt32 BIFF12_QUERYTABLE_BACKGROUND = 0x00000008;
+const sal_uInt32 BIFF12_QUERYTABLE_FIRSTBACKGROUND = 0x00000010;
+const sal_uInt32 BIFF12_QUERYTABLE_REFRESHONLOAD = 0x00000020;
+const sal_uInt32 BIFF12_QUERYTABLE_FILLFORMULAS = 0x00000100;
+const sal_uInt32 BIFF12_QUERYTABLE_SAVEDATA = 0x00000200;
+const sal_uInt32 BIFF12_QUERYTABLE_DISABLEEDIT = 0x00000400;
+const sal_uInt32 BIFF12_QUERYTABLE_PRESERVEFORMAT = 0x00000800;
+const sal_uInt32 BIFF12_QUERYTABLE_ADJUSTCOLWIDTH = 0x00001000;
+const sal_uInt32 BIFF12_QUERYTABLE_INTERMEDIATE = 0x00002000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYNUMFMT = 0x00004000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYFONT = 0x00008000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYALIGNMENT = 0x00010000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYBORDER = 0x00020000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYFILL = 0x00040000;
+const sal_uInt32 BIFF12_QUERYTABLE_APPLYPROTECTION = 0x00080000;
+
+void lclAppendWebQueryTableName( OUStringBuffer& rTables, std::u16string_view rTableName )
+{
+ if( !rTableName.empty() )
+ {
+ if( !rTables.isEmpty() )
+ rTables.append( ';' );
+ rTables.append( OUString::Concat("HTML__") + rTableName );
+ }
+}
+
+void lclAppendWebQueryTableIndex( OUStringBuffer& rTables, sal_Int32 nTableIndex )
+{
+ if( nTableIndex > 0 )
+ {
+ if( !rTables.isEmpty() )
+ rTables.append( ';' );
+ rTables.append( "HTML_" + OUString::number( nTableIndex ) );
+ }
+}
+
+OUString lclBuildWebQueryTables( const WebPrModel::TablesVector& rTables )
+{
+ if( rTables.empty() )
+ return "HTML_tables";
+
+ OUStringBuffer aTables;
+ for( const auto& rTable : rTables )
+ {
+ if( rTable.has< OUString >() )
+ lclAppendWebQueryTableName( aTables, rTable.get< OUString >() );
+ else if( rTable.has< sal_Int32 >() )
+ lclAppendWebQueryTableIndex( aTables, rTable.get< sal_Int32 >() );
+ }
+ return aTables.makeStringAndClear();
+}
+
+Reference< XAreaLink > lclFindAreaLink(
+ const Reference< XAreaLinks >& rxAreaLinks, const ScAddress& rDestPos,
+ std::u16string_view rFileUrl, std::u16string_view rTables, std::u16string_view rFilterName, std::u16string_view rFilterOptions )
+{
+ try
+ {
+ Reference< XEnumerationAccess > xAreaLinksEA( rxAreaLinks, UNO_QUERY_THROW );
+ Reference< XEnumeration > xAreaLinksEnum( xAreaLinksEA->createEnumeration(), UNO_SET_THROW );
+ while( xAreaLinksEnum->hasMoreElements() )
+ {
+ Reference< XAreaLink > xAreaLink( xAreaLinksEnum->nextElement(), UNO_QUERY_THROW );
+ PropertySet aPropSet( xAreaLink );
+ CellRangeAddress aDestArea = xAreaLink->getDestArea();
+ OUString aString;
+ if( (rDestPos.Tab() == aDestArea.Sheet) && (rDestPos.Col() == aDestArea.StartColumn) && (rDestPos.Row() == aDestArea.StartRow) &&
+ (rTables == xAreaLink->getSourceArea()) &&
+ aPropSet.getProperty( aString, PROP_Url ) && (rFileUrl == aString) &&
+ aPropSet.getProperty( aString, PROP_Filter ) && (rFilterName == aString) &&
+ aPropSet.getProperty( aString, PROP_FilterOptions ) && (rFilterOptions == aString) )
+ return xAreaLink;
+ }
+ }
+ catch( Exception& )
+ {
+ }
+ return Reference< XAreaLink >();
+}
+
+} // namespace
+
+QueryTableModel::QueryTableModel() :
+ mnConnId( -1 ),
+ mnGrowShrinkType( XML_insertDelete ),
+ mbHeaders( true ),
+ mbRowNumbers( false ),
+ mbDisableRefresh( false ),
+ mbBackground( true ),
+ mbFirstBackground( false ),
+ mbRefreshOnLoad( false ),
+ mbFillFormulas( false ),
+ mbRemoveDataOnSave( false ),
+ mbDisableEdit( false ),
+ mbPreserveFormat( true ),
+ mbAdjustColWidth( true ),
+ mbIntermediate( false )
+{
+}
+
+QueryTable::QueryTable( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void QueryTable::importQueryTable( const AttributeList& rAttribs )
+{
+ maModel.maDefName = rAttribs.getXString( XML_name, OUString() );
+ maModel.mnConnId = rAttribs.getInteger( XML_connectionId, -1 );
+ maModel.mnGrowShrinkType = rAttribs.getToken( XML_growShrinkType, XML_insertDelete );
+ maModel.mnAutoFormatId = rAttribs.getInteger( XML_autoFormatId, 0 );
+ maModel.mbHeaders = rAttribs.getBool( XML_headers, true );
+ maModel.mbRowNumbers = rAttribs.getBool( XML_rowNumbers, false );
+ maModel.mbDisableRefresh = rAttribs.getBool( XML_disableRefresh, false );
+ maModel.mbBackground = rAttribs.getBool( XML_backgroundRefresh, true );
+ maModel.mbFirstBackground = rAttribs.getBool( XML_firstBackgroundRefresh, false );
+ maModel.mbRefreshOnLoad = rAttribs.getBool( XML_refreshOnLoad, false );
+ maModel.mbFillFormulas = rAttribs.getBool( XML_fillFormulas, false );
+ maModel.mbRemoveDataOnSave = rAttribs.getBool( XML_removeDataOnSave, false );
+ maModel.mbDisableEdit = rAttribs.getBool( XML_disableEdit, false );
+ maModel.mbPreserveFormat = rAttribs.getBool( XML_preserveFormatting, true );
+ maModel.mbAdjustColWidth = rAttribs.getBool( XML_adjustColumnWidth, true );
+ maModel.mbIntermediate = rAttribs.getBool( XML_intermediate, false );
+ maModel.mbApplyNumFmt = rAttribs.getBool( XML_applyNumberFormats, false );
+ maModel.mbApplyFont = rAttribs.getBool( XML_applyFontFormats, false );
+ maModel.mbApplyAlignment = rAttribs.getBool( XML_applyAlignmentFormats, false );
+ maModel.mbApplyBorder = rAttribs.getBool( XML_applyBorderFormats, false );
+ maModel.mbApplyFill = rAttribs.getBool( XML_applyPatternFormats, false );
+ // OOXML and BIFF12 documentation differ: OOXML mentions width/height, BIFF12 mentions protection
+ maModel.mbApplyProtection = rAttribs.getBool( XML_applyWidthHeightFormats, false );
+}
+
+void QueryTable::importQueryTable( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags;
+ nFlags = rStrm.readuInt32();
+ maModel.mnAutoFormatId = rStrm.readuInt16();
+ maModel.mnConnId = rStrm.readInt32();
+ rStrm >> maModel.maDefName;
+
+ static const sal_Int32 spnGrowShrinkTypes[] = { XML_insertClear, XML_insertDelete, XML_overwriteClear };
+ maModel.mnGrowShrinkType = STATIC_ARRAY_SELECT( spnGrowShrinkTypes, extractValue< sal_uInt8 >( nFlags, 6, 2 ), XML_insertDelete );
+
+ maModel.mbHeaders = getFlag( nFlags, BIFF12_QUERYTABLE_HEADERS );
+ maModel.mbRowNumbers = getFlag( nFlags, BIFF12_QUERYTABLE_ROWNUMBERS );
+ maModel.mbDisableRefresh = getFlag( nFlags, BIFF12_QUERYTABLE_DISABLEREFRESH );
+ maModel.mbBackground = getFlag( nFlags, BIFF12_QUERYTABLE_BACKGROUND );
+ maModel.mbFirstBackground = getFlag( nFlags, BIFF12_QUERYTABLE_FIRSTBACKGROUND );
+ maModel.mbRefreshOnLoad = getFlag( nFlags, BIFF12_QUERYTABLE_REFRESHONLOAD );
+ maModel.mbFillFormulas = getFlag( nFlags, BIFF12_QUERYTABLE_FILLFORMULAS );
+ maModel.mbRemoveDataOnSave = !getFlag( nFlags, BIFF12_QUERYTABLE_SAVEDATA ); // flag negated in BIFF12
+ maModel.mbDisableEdit = getFlag( nFlags, BIFF12_QUERYTABLE_DISABLEEDIT );
+ maModel.mbPreserveFormat = getFlag( nFlags, BIFF12_QUERYTABLE_PRESERVEFORMAT );
+ maModel.mbAdjustColWidth = getFlag( nFlags, BIFF12_QUERYTABLE_ADJUSTCOLWIDTH );
+ maModel.mbIntermediate = getFlag( nFlags, BIFF12_QUERYTABLE_INTERMEDIATE );
+ maModel.mbApplyNumFmt = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYNUMFMT );
+ maModel.mbApplyFont = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYFONT );
+ maModel.mbApplyAlignment = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYALIGNMENT );
+ maModel.mbApplyBorder = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYBORDER );
+ maModel.mbApplyFill = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYFILL );
+ maModel.mbApplyProtection = getFlag( nFlags, BIFF12_QUERYTABLE_APPLYPROTECTION );
+}
+
+void QueryTable::finalizeImport()
+{
+ ConnectionRef xConnection = getConnections().getConnection( maModel.mnConnId );
+ OSL_ENSURE( xConnection, "QueryTable::finalizeImport - missing connection object" );
+ if( !(xConnection && (xConnection->getConnectionType() == BIFF12_CONNECTION_HTML)) )
+ return;
+
+ // check that valid web query properties exist
+ const WebPrModel* pWebPr = xConnection->getModel().mxWebPr.get();
+ if( !pWebPr || pWebPr->mbXml )
+ return;
+
+ OUString aFileUrl = getBaseFilter().getAbsoluteUrl( pWebPr->maUrl );
+ if( aFileUrl.isEmpty() )
+ return;
+
+ // resolve destination cell range (stored as defined name containing the range)
+ OUString aDefName = maModel.maDefName.replace( ' ', '_' ).replace( '-', '_' );
+ DefinedNameRef xDefName = getDefinedNames().getByModelName( aDefName, getSheetIndex() );
+ OSL_ENSURE( xDefName, "QueryTable::finalizeImport - missing defined name" );
+ if( !xDefName )
+ return;
+
+ ScRange aDestRange;
+ bool bIsRange = xDefName->getAbsoluteRange( aDestRange ) && (aDestRange.aStart.Tab() == getSheetIndex());
+ OSL_ENSURE( bIsRange, "QueryTable::finalizeImport - defined name does not contain valid cell range" );
+ if( !(bIsRange && getAddressConverter().checkCellRange( aDestRange, false, true )) )
+ return;
+
+ // find tables mode: entire document, all tables, or specific tables
+ OUString aTables = pWebPr->mbHtmlTables ? lclBuildWebQueryTables( pWebPr->maTables ) : "HTML_all";
+ if( aTables.isEmpty() )
+ return;
+
+ try
+ {
+ PropertySet aDocProps( getDocument() );
+ Reference< XAreaLinks > xAreaLinks( aDocProps.getAnyProperty( PROP_AreaLinks ), UNO_QUERY_THROW );
+ CellAddress aDestPos( aDestRange.aStart.Tab(), aDestRange.aStart.Col(), aDestRange.aStart.Row() );
+ static const OUStringLiteral aFilterName = u"calc_HTML_WebQuery";
+ xAreaLinks->insertAtPosition( aDestPos, aFileUrl, aTables, aFilterName, /*aFilterOptions*/"" );
+ // set refresh interval (convert minutes to seconds)
+ sal_Int32 nRefreshPeriod = xConnection->getModel().mnInterval * 60;
+ if( nRefreshPeriod > 0 )
+ {
+ PropertySet aPropSet( lclFindAreaLink( xAreaLinks, aDestRange.aStart, aFileUrl, aTables, aFilterName, /*aFilterOptions*/u"" ) );
+ aPropSet.setProperty( PROP_RefreshPeriod, nRefreshPeriod );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+QueryTableBuffer::QueryTableBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+QueryTable& QueryTableBuffer::createQueryTable()
+{
+ QueryTableVector::value_type xQueryTable = std::make_shared<QueryTable>( *this );
+ maQueryTables.push_back( xQueryTable );
+ return *xQueryTable;
+}
+
+void QueryTableBuffer::finalizeImport()
+{
+ maQueryTables.forEachMem( &QueryTable::finalizeImport );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/querytablefragment.cxx b/sc/source/filter/oox/querytablefragment.cxx
new file mode 100644
index 000000000..a41bda46a
--- /dev/null
+++ b/sc/source/filter/oox/querytablefragment.cxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <querytablefragment.hxx>
+#include <querytablebuffer.hxx>
+#include <biffhelper.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+QueryTableFragment::QueryTableFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mrQueryTable( getQueryTables().createQueryTable() )
+{
+}
+
+ContextHandlerRef QueryTableFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( queryTable ) )
+ mrQueryTable.importQueryTable( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef QueryTableFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_QUERYTABLE )
+ mrQueryTable.importQueryTable( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* QueryTableFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_QUERYTABLE, BIFF12_ID_QUERYTABLE + 1 },
+ { BIFF12_ID_QUERYTABLEREFRESH, BIFF12_ID_QUERYTABLEREFRESH + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/revisionfragment.cxx b/sc/source/filter/oox/revisionfragment.cxx
new file mode 100644
index 000000000..350adbea9
--- /dev/null
+++ b/sc/source/filter/oox/revisionfragment.cxx
@@ -0,0 +1,455 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <revisionfragment.hxx>
+#include <oox/core/relations.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/core/fastparser.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sax/tools/converter.hxx>
+#include <editeng/editobj.hxx>
+
+#include <chgtrack.hxx>
+#include <document.hxx>
+#include <compiler.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include <chgviset.hxx>
+#include <richstringcontext.hxx>
+#include <tokenarray.hxx>
+
+#include <com/sun/star/util/DateTime.hpp>
+
+using namespace com::sun::star;
+
+namespace oox::xls {
+
+namespace {
+
+enum RevisionType
+{
+ REV_UNKNOWN = 0,
+ REV_CELLCHANGE,
+ REV_INSERTROW
+};
+
+/**
+ * For nc (new cell) or oc (old cell) elements under rcc (cell content
+ * revision).
+ */
+class RCCCellValueContext : public WorkbookContextBase
+{
+ sal_Int32 mnSheetIndex;
+ ScAddress& mrPos;
+ ScCellValue& mrCellValue;
+ sal_Int32 mnType;
+
+ RichStringRef mxRichString;
+
+public:
+ RCCCellValueContext(
+ RevisionLogFragment& rParent, sal_Int32 nSheetIndex, ScAddress& rPos, ScCellValue& rCellValue ) :
+ WorkbookContextBase(rParent),
+ mnSheetIndex(nSheetIndex),
+ mrPos(rPos),
+ mrCellValue(rCellValue),
+ mnType(-1) {}
+
+protected:
+ virtual oox::core::ContextHandlerRef onCreateContext(
+ sal_Int32 nElement, const AttributeList& /*rAttribs*/ ) override
+ {
+ if (nElement == XLS_TOKEN(is))
+ {
+ mxRichString = std::make_shared<RichString>(*this);
+ return new RichStringContext(*this, mxRichString);
+ }
+
+ return this;
+ }
+
+ virtual void onStartElement( const AttributeList& rAttribs ) override
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(nc):
+ case XLS_TOKEN(oc):
+ importCell(rAttribs);
+ break;
+ default:
+ ;
+ }
+ }
+
+ virtual void onCharacters( const OUString& rChars ) override
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(v):
+ {
+ if (mnType == XML_n || mnType == XML_b)
+ mrCellValue.set(rChars.toDouble());
+ }
+ break;
+ case XLS_TOKEN(t):
+ {
+ if (mnType == XML_inlineStr)
+ {
+ ScDocument& rDoc = getScDocument();
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ mrCellValue.set(rPool.intern(rChars));
+ }
+ }
+ break;
+ case XLS_TOKEN(f):
+ {
+ // formula string
+ ScDocument& rDoc = getScDocument();
+ ScCompiler aComp(rDoc, mrPos, formula::FormulaGrammar::GRAM_OOXML);
+ std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(rChars);
+ if (!pArray)
+ break;
+
+ mrCellValue.set(new ScFormulaCell(rDoc, mrPos, std::move(pArray)));
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+ virtual void onEndElement() override
+ {
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(nc):
+ case XLS_TOKEN(oc):
+ {
+ if (mrCellValue.isEmpty() && mxRichString)
+ {
+ // The value is a rich text string.
+ ScDocument& rDoc = getScDocument();
+ std::unique_ptr<EditTextObject> pTextObj = mxRichString->convert(rDoc.GetEditEngine(), nullptr);
+ if (pTextObj)
+ {
+ svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
+ pTextObj->NormalizeString(rPool);
+ mrCellValue.set(pTextObj.release());
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+
+private:
+ void importCell( const AttributeList& rAttribs )
+ {
+ mnType = rAttribs.getToken(XML_t, XML_n);
+ OUString aRefStr = rAttribs.getString(XML_r, OUString());
+ if (!aRefStr.isEmpty())
+ {
+ mrPos.Parse(aRefStr, getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
+ if (mnSheetIndex != -1)
+ mrPos.SetTab(mnSheetIndex-1);
+ }
+ }
+};
+
+struct RevisionMetadata
+{
+ OUString maUserName;
+ DateTime maDateTime;
+
+ RevisionMetadata() : maDateTime(DateTime::EMPTY) {}
+ RevisionMetadata( const RevisionMetadata& r ) :
+ maUserName(r.maUserName), maDateTime(r.maDateTime) {}
+};
+
+}
+
+struct RevisionHeadersFragment::Impl
+{
+ std::map<OUString, RevisionMetadata> maRevData;
+
+ Impl() {}
+};
+
+RevisionHeadersFragment::RevisionHeadersFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase(rHelper, rFragmentPath),
+ mpImpl(new Impl) {}
+
+RevisionHeadersFragment::~RevisionHeadersFragment()
+{
+}
+
+oox::core::ContextHandlerRef RevisionHeadersFragment::onCreateContext(
+ sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
+{
+ return this;
+}
+
+void RevisionHeadersFragment::onStartElement( const AttributeList& rAttribs )
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(headers):
+ break;
+ case XLS_TOKEN(header):
+ importHeader(rAttribs);
+ break;
+ case XLS_TOKEN(sheetIdMap):
+ break;
+ case XLS_TOKEN(sheetId):
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionHeadersFragment::onCharacters( const OUString& /*rChars*/ ) {}
+
+void RevisionHeadersFragment::onEndElement()
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(headers):
+ break;
+ case XLS_TOKEN(header):
+ break;
+ case XLS_TOKEN(sheetIdMap):
+ break;
+ case XLS_TOKEN(sheetId):
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionHeadersFragment::finalizeImport()
+{
+ ScDocument& rDoc = getScDocument();
+ std::unique_ptr<ScChangeTrack> pCT(new ScChangeTrack(rDoc));
+ OUString aSelfUser = pCT->GetUser(); // owner of this document.
+ pCT->SetUseFixDateTime(true);
+
+ const oox::core::Relations& rRels = getRelations();
+ for (const auto& [rRelId, rData] : mpImpl->maRevData)
+ {
+ OUString aPath = rRels.getFragmentPathFromRelId(rRelId);
+ if (aPath.isEmpty())
+ continue;
+
+ // Parse each revision log fragment.
+ pCT->SetUser(rData.maUserName);
+ pCT->SetFixDateTimeLocal(rData.maDateTime);
+ std::unique_ptr<oox::core::FastParser> xParser(oox::core::XmlFilterBase::createParser());
+ rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionLogFragment(*this, aPath, *pCT));
+ importOoxFragment(xFragment, *xParser);
+ }
+
+ pCT->SetUser(aSelfUser); // set the default user to the document owner.
+ pCT->SetUseFixDateTime(false);
+ rDoc.SetChangeTrack(std::move(pCT));
+
+ // Turn on visibility of tracked changes.
+ ScChangeViewSettings aSettings;
+ aSettings.SetShowChanges(true);
+ rDoc.SetChangeViewSettings(aSettings);
+}
+
+void RevisionHeadersFragment::importHeader( const AttributeList& rAttribs )
+{
+ OUString aRId = rAttribs.getString(R_TOKEN(id), OUString());
+ if (aRId.isEmpty())
+ // All bets are off if we don't have a relation ID.
+ return;
+
+ RevisionMetadata aMetadata;
+ OUString aDateTimeStr = rAttribs.getString(XML_dateTime, OUString());
+ if (!aDateTimeStr.isEmpty())
+ {
+ util::DateTime aDateTime;
+ if (sax::Converter::parseDateTime(aDateTime, aDateTimeStr))
+ {
+ Date aDate(aDateTime);
+ tools::Time aTime(aDateTime);
+ aMetadata.maDateTime.SetDate(aDate.GetDate());
+ aMetadata.maDateTime.SetTime(aTime.GetTime());
+ }
+ else
+ SAL_WARN("sc.filter", "RevisionHeadersFragment: broken DateTime '" << aDateTimeStr << "'");
+ }
+
+ aMetadata.maUserName = rAttribs.getString(XML_userName, OUString());
+
+ mpImpl->maRevData.emplace(aRId, aMetadata);
+}
+
+struct RevisionLogFragment::Impl
+{
+ ScChangeTrack& mrChangeTrack;
+
+ sal_Int32 mnSheetIndex;
+
+ RevisionType meType;
+
+ // rcc
+ ScAddress maOldCellPos;
+ ScAddress maNewCellPos;
+ ScCellValue maOldCellValue;
+ ScCellValue maNewCellValue;
+
+ // rrc
+ ScRange maRange;
+
+ bool mbEndOfList;
+
+ explicit Impl( ScChangeTrack& rChangeTrack ) :
+ mrChangeTrack(rChangeTrack),
+ mnSheetIndex(-1),
+ meType(REV_UNKNOWN),
+ mbEndOfList(false) {}
+};
+
+RevisionLogFragment::RevisionLogFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath, ScChangeTrack& rChangeTrack ) :
+ WorkbookFragmentBase(rHelper, rFragmentPath),
+ mpImpl(new Impl(rChangeTrack)) {}
+
+RevisionLogFragment::~RevisionLogFragment()
+{
+}
+
+oox::core::ContextHandlerRef RevisionLogFragment::onCreateContext(
+ sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ switch (nElement)
+ {
+ case XLS_TOKEN(nc):
+ return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maNewCellPos, mpImpl->maNewCellValue);
+ case XLS_TOKEN(oc):
+ return new RCCCellValueContext(*this, mpImpl->mnSheetIndex, mpImpl->maOldCellPos, mpImpl->maOldCellValue);
+ default:
+ ;
+ }
+ return this;
+}
+
+void RevisionLogFragment::onStartElement( const AttributeList& rAttribs )
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(rcc):
+ mpImpl->maNewCellPos.SetInvalid();
+ mpImpl->maOldCellPos.SetInvalid();
+ mpImpl->maNewCellValue.clear();
+ mpImpl->maOldCellValue.clear();
+ importRcc(rAttribs);
+ break;
+ case XLS_TOKEN(rrc):
+ importRrc(rAttribs);
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionLogFragment::onCharacters( const OUString& /*rChars*/ ) {}
+
+void RevisionLogFragment::onEndElement()
+{
+ switch (getCurrentElement())
+ {
+ case XLS_TOKEN(rcc):
+ case XLS_TOKEN(rrc):
+ pushRevision();
+ break;
+ default:
+ ;
+ }
+}
+
+void RevisionLogFragment::finalizeImport() {}
+
+void RevisionLogFragment::importCommon( const AttributeList& rAttribs )
+{
+ mpImpl->mnSheetIndex = rAttribs.getInteger(XML_sId, -1);
+}
+
+void RevisionLogFragment::importRcc( const AttributeList& rAttribs )
+{
+ importCommon(rAttribs);
+
+ mpImpl->meType = REV_CELLCHANGE;
+}
+
+void RevisionLogFragment::importRrc( const AttributeList& rAttribs )
+{
+ importCommon(rAttribs);
+
+ if (mpImpl->mnSheetIndex == -1)
+ // invalid sheet index, or sheet index not given.
+ return;
+
+ mpImpl->meType = REV_UNKNOWN;
+ sal_Int32 nAction = rAttribs.getToken(XML_action, -1);
+ if (nAction == -1)
+ return;
+
+ OUString aRefStr = rAttribs.getString(XML_ref, OUString());
+ mpImpl->maRange.Parse(aRefStr, getScDocument(), formula::FormulaGrammar::CONV_XL_OOX);
+ if (!mpImpl->maRange.IsValid())
+ return;
+
+ switch (nAction)
+ {
+ case XML_insertRow:
+ mpImpl->meType = REV_INSERTROW;
+ mpImpl->maRange.aEnd.SetCol(getScDocument().MaxCol());
+ mpImpl->maRange.aStart.SetTab(mpImpl->mnSheetIndex-1);
+ mpImpl->maRange.aEnd.SetTab(mpImpl->mnSheetIndex-1);
+ break;
+ default:
+ // Unknown action type. Ignore it.
+ return;
+ }
+
+ mpImpl->mbEndOfList = rAttribs.getBool(XML_eol, false);
+}
+
+void RevisionLogFragment::pushRevision()
+{
+ switch (mpImpl->meType)
+ {
+ case REV_CELLCHANGE:
+ mpImpl->mrChangeTrack.AppendContentOnTheFly(
+ mpImpl->maNewCellPos, mpImpl->maOldCellValue, mpImpl->maNewCellValue);
+ break;
+ case REV_INSERTROW:
+ mpImpl->mrChangeTrack.AppendInsert(mpImpl->maRange, mpImpl->mbEndOfList);
+ break;
+ default:
+ ;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/richstring.cxx b/sc/source/filter/oox/richstring.cxx
new file mode 100644
index 000000000..294fddfbc
--- /dev/null
+++ b/sc/source/filter/oox/richstring.cxx
@@ -0,0 +1,597 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <richstring.hxx>
+#include <biffhelper.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <rtl/ustrbuf.hxx>
+#include <editeng/editobj.hxx>
+#include <osl/diagnose.h>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/tokens.hxx>
+#include <editutil.hxx>
+
+#include <vcl/svapp.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt8 BIFF12_STRINGFLAG_FONTS = 0x01;
+const sal_uInt8 BIFF12_STRINGFLAG_PHONETICS = 0x02;
+
+bool lclNeedsRichTextFormat( const oox::xls::Font* pFont )
+{
+ return pFont && pFont->needsRichTextFormat();
+}
+
+sal_Int32 lcl_getHexLetterValue(sal_Unicode nCode)
+{
+ if (nCode >= '0' && nCode <= '9')
+ return nCode - '0';
+
+ if (nCode >= 'A' && nCode <= 'F')
+ return nCode - 'A' + 10;
+
+ if (nCode >= 'a' && nCode <= 'f')
+ return nCode - 'a' + 10;
+
+ return -1;
+}
+
+bool lcl_validEscape(sal_Unicode nCode)
+{
+ // Valid XML chars that can be escaped (ignoring the restrictions) as in the OOX open spec
+ // 2.1.1742 Part 1 Section 22.9.2.19, ST_Xstring (Escaped String)
+ if (nCode == 0x000D || nCode == 0x000A || nCode == 0x0009 || nCode == 0x005F)
+ return true;
+
+ // Other valid XML chars in basic multilingual plane that cannot be escaped.
+ if ((nCode >= 0x0020 && nCode <= 0xD7FF) || (nCode >= 0xE000 && nCode <= 0xFFFD))
+ return false;
+
+ return true;
+}
+
+OUString lcl_unEscapeUnicodeChars(const OUString& rSrc)
+{
+ // Example: Escaped representation of unicode char 0x000D is _x000D_
+
+ sal_Int32 nLen = rSrc.getLength();
+ if (!nLen)
+ return rSrc;
+
+ sal_Int32 nStart = 0;
+ bool bFound = false;
+ const OUString aPrefix = "_x";
+ sal_Int32 nPrefixStart = rSrc.indexOf(aPrefix, nStart);
+
+ if (nPrefixStart == -1)
+ return rSrc;
+
+ OUStringBuffer aBuf(rSrc);
+ sal_Int32 nOffset = 0; // index offset in aBuf w.r.t rSrc.
+
+ do
+ {
+ sal_Int32 nEnd = -1;
+ sal_Unicode nCode = 0;
+ bool bFoundThis = false;
+ for (sal_Int32 nIdx = 0; nIdx < 5; ++nIdx)
+ {
+ sal_Int32 nThisIdx = nPrefixStart + nIdx + 2;
+ if (nThisIdx >= nLen)
+ break;
+
+ sal_Unicode nThisCode = rSrc[nThisIdx];
+ sal_Int32 nLetter = lcl_getHexLetterValue(nThisCode);
+
+ if (!nIdx && nLetter < 0)
+ break;
+
+ if (nLetter >= 0)
+ {
+ nCode = (nCode << 4) + static_cast<sal_Unicode>(nLetter);
+ }
+ else if (nThisCode == '_')
+ {
+ nEnd = nThisIdx + 1;
+ bFoundThis = true;
+ break;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (bFoundThis)
+ {
+ // nEnd is already set inside the inner loop in this case.
+ if (lcl_validEscape(nCode))
+ {
+ bFound = true;
+ sal_Int32 nEscStrLen = nEnd - nPrefixStart;
+ aBuf.remove(nPrefixStart - nOffset, nEscStrLen);
+ aBuf.insert(nPrefixStart - nOffset, nCode);
+
+ nOffset += nEscStrLen - 1;
+ }
+ }
+ else
+ {
+ // Start the next search just after last "_x"
+ nEnd = nPrefixStart + 2;
+ }
+
+ nStart = nEnd;
+ nPrefixStart = rSrc.indexOf(aPrefix, nStart);
+ }
+ while (nPrefixStart != -1);
+
+ if (bFound)
+ return aBuf.makeStringAndClear();
+
+ return rSrc;
+}
+
+} // namespace
+
+RichStringPortion::RichStringPortion( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnFontId( -1 ),
+ mbConverted( false )
+{
+}
+
+void RichStringPortion::setText( const OUString& rText )
+{
+ maText = lcl_unEscapeUnicodeChars(rText);
+}
+
+FontRef const & RichStringPortion::createFont()
+{
+ mxFont = std::make_shared<Font>( *this, false );
+ return mxFont;
+}
+
+void RichStringPortion::setFontId( sal_Int32 nFontId )
+{
+ mnFontId = nFontId;
+}
+
+void RichStringPortion::finalizeImport()
+{
+ if( mxFont )
+ mxFont->finalizeImport();
+ else if( mnFontId >= 0 )
+ mxFont = getStyles().getFont( mnFontId );
+}
+
+void RichStringPortion::convert( const Reference< XText >& rxText, bool bReplace )
+{
+ if ( mbConverted )
+ return;
+
+ Reference< XTextRange > xRange;
+ if( bReplace )
+ xRange = rxText;
+ else
+ xRange = rxText->getEnd();
+ OSL_ENSURE( xRange.is(), "RichStringPortion::convert - cannot get text range interface" );
+
+ if( xRange.is() )
+ {
+ xRange->setString( maText );
+ if( mxFont )
+ {
+ PropertySet aPropSet( xRange );
+ mxFont->writeToPropertySet( aPropSet );
+ }
+ }
+
+ mbConverted = true;
+}
+
+void RichStringPortion::convert( ScEditEngineDefaulter& rEE, ESelection& rSelection, const oox::xls::Font* pFont )
+{
+ rSelection.nStartPos = rSelection.nEndPos;
+ rSelection.nStartPara = rSelection.nEndPara;
+ SfxItemSet aItemSet( rEE.GetEmptyItemSet() );
+
+ const Font* pFontToUse = mxFont ? mxFont.get() : lclNeedsRichTextFormat( pFont ) ? pFont : nullptr;
+
+ if ( pFontToUse )
+ pFontToUse->fillToItemSet( aItemSet, true );
+
+ // #TODO need to manually adjust nEndPos ( and nEndPara ) to cater for any paragraphs
+ sal_Int32 nLastParaLoc = -1;
+ sal_Int32 nSearchIndex = maText.indexOf( '\n' );
+ sal_Int32 nParaOccurrence = 0;
+ while ( nSearchIndex != -1 )
+ {
+ nLastParaLoc = nSearchIndex;
+ ++nParaOccurrence;
+ rSelection.nEndPos = 0;
+ nSearchIndex = maText.indexOf( '\n', nSearchIndex + 1);
+ }
+
+ rSelection.nEndPara += nParaOccurrence;
+ if ( nLastParaLoc != -1 )
+ {
+ rSelection.nEndPos = maText.getLength() - 1 - nLastParaLoc;
+ }
+ else
+ {
+ rSelection.nEndPos = rSelection.nStartPos + maText.getLength();
+ }
+ rEE.QuickSetAttribs( aItemSet, rSelection );
+}
+
+void RichStringPortion::writeFontProperties( const Reference<XText>& rxText ) const
+{
+ PropertySet aPropSet(rxText);
+
+ if (mxFont)
+ mxFont->writeToPropertySet(aPropSet);
+}
+
+void FontPortionModel::read( SequenceInputStream& rStrm )
+{
+ mnPos = rStrm.readuInt16();
+ mnFontId = rStrm.readuInt16();
+}
+
+void FontPortionModelList::appendPortion( const FontPortionModel& rPortion )
+{
+ // #i33341# real life -- same character index may occur several times
+ OSL_ENSURE( mvModels.empty() || (mvModels.back().mnPos <= rPortion.mnPos), "FontPortionModelList::appendPortion - wrong char order" );
+ if( mvModels.empty() || (mvModels.back().mnPos < rPortion.mnPos) )
+ mvModels.push_back( rPortion );
+ else
+ mvModels.back().mnFontId = rPortion.mnFontId;
+}
+
+void FontPortionModelList::importPortions( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCount = rStrm.readInt32();
+ mvModels.clear();
+ if( nCount > 0 )
+ {
+ mvModels.reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 4 ) );
+ /* #i33341# real life -- same character index may occur several times
+ -> use appendPortion() to validate string position. */
+ FontPortionModel aPortion;
+ for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
+ {
+ aPortion.read( rStrm );
+ appendPortion( aPortion );
+ }
+ }
+}
+
+PhoneticDataModel::PhoneticDataModel() :
+ mnFontId( -1 ),
+ mnType( XML_fullwidthKatakana ),
+ mnAlignment( XML_left )
+{
+}
+
+void PhoneticDataModel::setBiffData( sal_Int32 nType, sal_Int32 nAlignment )
+{
+ static const sal_Int32 spnTypeIds[] = { XML_halfwidthKatakana, XML_fullwidthKatakana, XML_hiragana, XML_noConversion };
+ mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_fullwidthKatakana );
+
+ static const sal_Int32 spnAlignments[] = { XML_noControl, XML_left, XML_center, XML_distributed };
+ mnAlignment = STATIC_ARRAY_SELECT( spnAlignments, nAlignment, XML_left );
+}
+
+PhoneticSettings::PhoneticSettings( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void PhoneticSettings::importPhoneticPr( const AttributeList& rAttribs )
+{
+ maModel.mnFontId = rAttribs.getInteger( XML_fontId, -1 );
+ maModel.mnType = rAttribs.getToken( XML_type, XML_fullwidthKatakana );
+ maModel.mnAlignment = rAttribs.getToken( XML_alignment, XML_left );
+}
+
+void PhoneticSettings::importPhoneticPr( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFontId;
+ sal_Int32 nType, nAlignment;
+ nFontId = rStrm.readuInt16();
+ nType = rStrm.readInt32();
+ nAlignment = rStrm.readInt32();
+ maModel.mnFontId = nFontId;
+ maModel.setBiffData( nType, nAlignment );
+}
+
+void PhoneticSettings::importStringData( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFontId, nFlags;
+ nFontId = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+ maModel.mnFontId = nFontId;
+ maModel.setBiffData( extractValue< sal_Int32 >( nFlags, 0, 2 ), extractValue< sal_Int32 >( nFlags, 2, 2 ) );
+}
+
+RichStringPhonetic::RichStringPhonetic( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnBasePos( -1 ),
+ mnBaseEnd( -1 )
+{
+}
+
+void RichStringPhonetic::setText( const OUString& rText )
+{
+ maText = rText;
+}
+
+void RichStringPhonetic::importPhoneticRun( const AttributeList& rAttribs )
+{
+ mnBasePos = rAttribs.getInteger( XML_sb, -1 );
+ mnBaseEnd = rAttribs.getInteger( XML_eb, -1 );
+}
+
+void RichStringPhonetic::setBaseRange( sal_Int32 nBasePos, sal_Int32 nBaseEnd )
+{
+ mnBasePos = nBasePos;
+ mnBaseEnd = nBaseEnd;
+}
+
+void PhoneticPortionModel::read( SequenceInputStream& rStrm )
+{
+ mnPos = rStrm.readuInt16();
+ mnBasePos = rStrm.readuInt16();
+ mnBaseLen = rStrm.readuInt16();
+}
+
+void PhoneticPortionModelList::appendPortion( const PhoneticPortionModel& rPortion )
+{
+ // same character index may occur several times
+ OSL_ENSURE( mvModels.empty() || ((mvModels.back().mnPos <= rPortion.mnPos) &&
+ (mvModels.back().mnBasePos + mvModels.back().mnBaseLen <= rPortion.mnBasePos)),
+ "PhoneticPortionModelList::appendPortion - wrong char order" );
+ if( mvModels.empty() || (mvModels.back().mnPos < rPortion.mnPos) )
+ {
+ mvModels.push_back( rPortion );
+ }
+ else if( mvModels.back().mnPos == rPortion.mnPos )
+ {
+ mvModels.back().mnBasePos = rPortion.mnBasePos;
+ mvModels.back().mnBaseLen = rPortion.mnBaseLen;
+ }
+}
+
+void PhoneticPortionModelList::importPortions( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCount = rStrm.readInt32();
+ mvModels.clear();
+ if( nCount > 0 )
+ {
+ mvModels.reserve( getLimitedValue< size_t, sal_Int64 >( nCount, 0, rStrm.getRemaining() / 6 ) );
+ PhoneticPortionModel aPortion;
+ for( sal_Int32 nIndex = 0; !rStrm.isEof() && (nIndex < nCount); ++nIndex )
+ {
+ aPortion.read( rStrm );
+ appendPortion( aPortion );
+ }
+ }
+}
+
+RichString::RichString( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ maPhonSettings( rHelper )
+{
+}
+
+RichStringPortionRef RichString::importText()
+{
+ return createPortion();
+}
+
+RichStringPortionRef RichString::importRun()
+{
+ return createPortion();
+}
+
+RichStringPhoneticRef RichString::importPhoneticRun( const AttributeList& rAttribs )
+{
+ RichStringPhoneticRef xPhonetic = createPhonetic();
+ xPhonetic->importPhoneticRun( rAttribs );
+ return xPhonetic;
+}
+
+void RichString::importPhoneticPr( const AttributeList& rAttribs )
+{
+ maPhonSettings.importPhoneticPr( rAttribs );
+}
+
+void RichString::importString( SequenceInputStream& rStrm, bool bRich )
+{
+ sal_uInt8 nFlags = bRich ? rStrm.readuInt8() : 0;
+ OUString aBaseText = BiffHelper::readString( rStrm );
+
+ if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_FONTS ) )
+ {
+ FontPortionModelList aPortions;
+ aPortions.importPortions( rStrm );
+ createTextPortions( aBaseText, aPortions );
+ }
+ else
+ {
+ createPortion()->setText( aBaseText );
+ }
+
+ if( !rStrm.isEof() && getFlag( nFlags, BIFF12_STRINGFLAG_PHONETICS ) )
+ {
+ OUString aPhoneticText = BiffHelper::readString( rStrm );
+ PhoneticPortionModelList aPortions;
+ aPortions.importPortions( rStrm );
+ maPhonSettings.importStringData( rStrm );
+ createPhoneticPortions( aPhoneticText, aPortions, aBaseText.getLength() );
+ }
+}
+
+void RichString::finalizeImport()
+{
+ maTextPortions.forEachMem( &RichStringPortion::finalizeImport );
+}
+
+bool RichString::extractPlainString( OUString& orString, const oox::xls::Font* pFirstPortionFont ) const
+{
+ if( !maPhonPortions.empty() )
+ return false;
+ if( maTextPortions.empty() )
+ {
+ orString.clear();
+ return true;
+ }
+ if( (maTextPortions.size() == 1) && !maTextPortions.front()->hasFont() && !lclNeedsRichTextFormat( pFirstPortionFont ) )
+ {
+ orString = maTextPortions.front()->getText();
+ return orString.indexOf( '\x0A' ) < 0;
+ }
+ return false;
+}
+
+void RichString::convert( const Reference< XText >& rxText ) const
+{
+ if (maTextPortions.size() == 1)
+ {
+ // Set text directly to the cell when the string has only one portion.
+ // It's much faster this way.
+ RichStringPortion& rPtn = *maTextPortions.front();
+ rxText->setString(rPtn.getText());
+ rPtn.writeFontProperties(rxText);
+ return;
+ }
+
+ bool bReplaceOld = true;
+ for( const auto& rxTextPortion : maTextPortions )
+ {
+ rxTextPortion->convert( rxText, bReplaceOld );
+ bReplaceOld = false; // do not replace first portion text with following portions
+ }
+}
+
+std::unique_ptr<EditTextObject> RichString::convert( ScEditEngineDefaulter& rEE, const oox::xls::Font* pFirstPortionFont ) const
+{
+ ESelection aSelection;
+
+ OUStringBuffer sString;
+ for( const auto& rxTextPortion : maTextPortions )
+ sString.append(rxTextPortion->getText());
+
+ // fdo#84370 - diving into editeng is not thread safe.
+ SolarMutexGuard aGuard;
+
+ rEE.SetTextCurrentDefaults( sString.makeStringAndClear() );
+
+ for( const auto& rxTextPortion : maTextPortions )
+ {
+ rxTextPortion->convert( rEE, aSelection, pFirstPortionFont );
+ pFirstPortionFont = nullptr;
+ }
+
+ return rEE.CreateTextObject();
+}
+
+// private --------------------------------------------------------------------
+
+RichStringPortionRef RichString::createPortion()
+{
+ RichStringPortionRef xPortion = std::make_shared<RichStringPortion>( *this );
+ maTextPortions.push_back( xPortion );
+ return xPortion;
+}
+
+RichStringPhoneticRef RichString::createPhonetic()
+{
+ RichStringPhoneticRef xPhonetic = std::make_shared<RichStringPhonetic>( *this );
+ maPhonPortions.push_back( xPhonetic );
+ return xPhonetic;
+}
+
+void RichString::createTextPortions( const OUString& rText, FontPortionModelList& rPortions )
+{
+ maTextPortions.clear();
+ if( rText.isEmpty() )
+ return;
+
+ sal_Int32 nStrLen = rText.getLength();
+ // add leading and trailing string position to ease the following loop
+ if( rPortions.empty() || (rPortions.front().mnPos > 0) )
+ rPortions.insert( rPortions.begin(), FontPortionModel( 0 ) );
+ if( rPortions.back().mnPos < nStrLen )
+ rPortions.push_back( FontPortionModel( nStrLen ) );
+
+ // create all string portions according to the font id vector
+ for( ::std::vector< FontPortionModel >::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
+ {
+ sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
+ if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
+ {
+ RichStringPortionRef xPortion = createPortion();
+ xPortion->setText( rText.copy( aIt->mnPos, nPortionLen ) );
+ xPortion->setFontId( aIt->mnFontId );
+ }
+ }
+}
+
+void RichString::createPhoneticPortions( const OUString& rText, PhoneticPortionModelList& rPortions, sal_Int32 nBaseLen )
+{
+ maPhonPortions.clear();
+ if( rText.isEmpty())
+ return;
+
+ sal_Int32 nStrLen = rText.getLength();
+ // no portions - assign phonetic text to entire base text
+ if( rPortions.empty() )
+ rPortions.push_back( PhoneticPortionModel( 0, 0, nBaseLen ) );
+ // add trailing string position to ease the following loop
+ if( rPortions.back().mnPos < nStrLen )
+ rPortions.push_back( PhoneticPortionModel( nStrLen, nBaseLen, 0 ) );
+
+ // create all phonetic portions according to the portions vector
+ for( ::std::vector< PhoneticPortionModel >::const_iterator aIt = rPortions.begin(); aIt->mnPos < nStrLen; ++aIt )
+ {
+ sal_Int32 nPortionLen = (aIt + 1)->mnPos - aIt->mnPos;
+ if( (0 < nPortionLen) && (aIt->mnPos + nPortionLen <= nStrLen) )
+ {
+ RichStringPhoneticRef xPhonetic = createPhonetic();
+ xPhonetic->setText( rText.copy( aIt->mnPos, nPortionLen ) );
+ xPhonetic->setBaseRange( aIt->mnBasePos, aIt->mnBasePos + aIt->mnBaseLen );
+ }
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/richstringcontext.cxx b/sc/source/filter/oox/richstringcontext.cxx
new file mode 100644
index 000000000..e7c3fd618
--- /dev/null
+++ b/sc/source/filter/oox/richstringcontext.cxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <richstringcontext.hxx>
+
+#include <stylesfragment.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+ContextHandlerRef RichStringContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ {
+ switch( nElement )
+ {
+ case XLS_TOKEN( t ):
+ mxPortion = mxString->importText();
+ return this; // collect text in onCharacters()
+ case XLS_TOKEN( r ):
+ mxPortion = mxString->importRun();
+ return this;
+ case XLS_TOKEN( rPh ):
+ mxPhonetic = mxString->importPhoneticRun( rAttribs );
+ return this;
+ case XLS_TOKEN( phoneticPr ):
+ mxString->importPhoneticPr( rAttribs );
+ break;
+ }
+ }
+ else switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( r ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( rPr ):
+ if( mxPortion )
+ return new FontContext( *this, mxPortion->createFont() );
+ break;
+
+ case XLS_TOKEN( t ):
+ return this; // collect portion text in onCharacters()
+ }
+ break;
+
+ case XLS_TOKEN( rPh ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( t ):
+ return this; // collect phonetic text in onCharacters()
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void RichStringContext::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( t ) ) ) switch( getParentElement() )
+ {
+ case XLS_TOKEN( rPh ):
+ if( mxPhonetic )
+ mxPhonetic->setText( rChars );
+ break;
+ default:
+ if( mxPortion )
+ mxPortion->setText( rChars );
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/scenariobuffer.cxx b/sc/source/filter/oox/scenariobuffer.cxx
new file mode 100644
index 000000000..7c45c822d
--- /dev/null
+++ b/sc/source/filter/oox/scenariobuffer.cxx
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scenariobuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XScenarios.hpp>
+#include <com/sun/star/sheet/XScenariosSupplier.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+
+ScenarioCellModel::ScenarioCellModel() :
+ mnNumFmtId( 0 ),
+ mbDeleted( false )
+{
+}
+
+ScenarioModel::ScenarioModel() :
+ mbLocked( false ),
+ mbHidden( false ),
+ mbActive( false )
+{
+}
+
+Scenario::Scenario( const WorkbookHelper& rHelper, sal_Int16 nSheet, bool bIsActive ) :
+ WorkbookHelper( rHelper ),
+ mnSheet( nSheet )
+{
+ maModel.mbActive = bIsActive;
+}
+
+void Scenario::importScenario( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.maComment = rAttribs.getXString( XML_comment, OUString() );
+ maModel.maUser = rAttribs.getXString( XML_user, OUString() );
+ maModel.mbLocked = rAttribs.getBool( XML_locked, false );
+ maModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+}
+
+void Scenario::importInputCells( const AttributeList& rAttribs )
+{
+ ScenarioCellModel aModel;
+ AddressConverter::convertToCellAddressUnchecked( aModel.maPos, rAttribs.getString( XML_r, OUString() ), mnSheet );
+ aModel.maValue = rAttribs.getXString( XML_val, OUString() );
+ aModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, 0 );
+ aModel.mbDeleted = rAttribs.getBool( XML_deleted, false );
+ maCells.push_back( aModel );
+}
+
+void Scenario::importScenario( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 2 ); // cell count
+ // two longs instead of flag field
+ maModel.mbLocked = rStrm.readInt32() != 0;
+ maModel.mbHidden = rStrm.readInt32() != 0;
+ rStrm >> maModel.maName >> maModel.maComment >> maModel.maUser;
+}
+
+void Scenario::importInputCells( SequenceInputStream& rStrm )
+{
+ // TODO: where is the deleted flag?
+ ScenarioCellModel aModel;
+ BinAddress aPos;
+ rStrm >> aPos;
+ rStrm.skip( 8 );
+ aModel.mnNumFmtId = rStrm.readuInt16();
+ rStrm >> aModel.maValue;
+ AddressConverter::convertToCellAddressUnchecked( aModel.maPos, aPos, mnSheet );
+ maCells.push_back( aModel );
+}
+
+void Scenario::finalizeImport()
+{
+ AddressConverter& rAddrConv = getAddressConverter();
+ ScRangeList aRanges;
+ for( const auto& rCell : maCells )
+ if( !rCell.mbDeleted && rAddrConv.checkCellAddress( rCell.maPos, true ) )
+ aRanges.push_back( ScRange(rCell.maPos, rCell.maPos) );
+
+ if( aRanges.empty() || maModel.maName.isEmpty() )
+ return;
+
+ try
+ {
+ /* Find an unused name for the scenario (Calc stores scenario data in
+ hidden sheets named after the scenario following the base sheet). */
+ Reference< XNameAccess > xSheetsNA( getDocument()->getSheets(), UNO_QUERY_THROW );
+ OUString aScenName = ContainerHelper::getUnusedName( xSheetsNA, maModel.maName, '_' );
+
+ // create the new scenario sheet
+ Reference< XScenariosSupplier > xScenariosSupp( getSheetFromDoc( mnSheet ), UNO_QUERY_THROW );
+ Reference< XScenarios > xScenarios( xScenariosSupp->getScenarios(), UNO_SET_THROW );
+ xScenarios->addNewByName( aScenName, AddressConverter::toApiSequence(aRanges), maModel.maComment );
+
+ // write scenario cell values
+ Reference< XSpreadsheet > xSheet( getSheetFromDoc( aScenName ), UNO_SET_THROW );
+ for( const auto& rCell : maCells )
+ {
+ if( !rCell.mbDeleted ) try
+ {
+ // use XCell::setFormula to auto-detect values and strings
+ Reference< XCell > xCell( xSheet->getCellByPosition( rCell.maPos.Col(), rCell.maPos.Row() ), UNO_SET_THROW );
+ xCell->setFormula( rCell.maValue );
+ }
+ catch( Exception& )
+ {
+ }
+ }
+
+ // scenario properties
+ PropertySet aPropSet( xScenarios->getByName( aScenName ) );
+ aPropSet.setProperty( PROP_IsActive, maModel.mbActive );
+ aPropSet.setProperty( PROP_CopyBack, false );
+ aPropSet.setProperty( PROP_CopyStyles, false );
+ aPropSet.setProperty( PROP_CopyFormulas, false );
+ aPropSet.setProperty( PROP_Protected, maModel.mbLocked );
+ // #112621# do not show/print scenario border
+ aPropSet.setProperty( PROP_ShowBorder, false );
+ aPropSet.setProperty( PROP_PrintBorder, false );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+SheetScenariosModel::SheetScenariosModel() :
+ mnCurrent( 0 ),
+ mnShown( 0 )
+{
+}
+
+SheetScenarios::SheetScenarios( const WorkbookHelper& rHelper, sal_Int16 nSheet ) :
+ WorkbookHelper( rHelper ),
+ mnSheet( nSheet )
+{
+}
+
+void SheetScenarios::importScenarios( const AttributeList& rAttribs )
+{
+ maModel.mnCurrent = rAttribs.getInteger( XML_current, 0 );
+ maModel.mnShown = rAttribs.getInteger( XML_show, 0 );
+}
+
+void SheetScenarios::importScenarios( SequenceInputStream& rStrm )
+{
+ maModel.mnCurrent = rStrm.readuInt16();
+ maModel.mnShown = rStrm.readuInt16();
+}
+
+Scenario& SheetScenarios::createScenario()
+{
+ bool bIsActive = maScenarios.size() == static_cast<sal_uInt32>(maModel.mnShown);
+ ScenarioVector::value_type xScenario = std::make_shared<Scenario>( *this, mnSheet, bIsActive );
+ maScenarios.push_back( xScenario );
+ return *xScenario;
+}
+
+void SheetScenarios::finalizeImport()
+{
+ maScenarios.forEachMem( &Scenario::finalizeImport );
+}
+
+ScenarioBuffer::ScenarioBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+SheetScenarios& ScenarioBuffer::createSheetScenarios( sal_Int16 nSheet )
+{
+ SheetScenariosMap::mapped_type& rxSheetScens = maSheetScenarios[ nSheet ];
+ if( !rxSheetScens )
+ rxSheetScens = std::make_shared<SheetScenarios>( *this, nSheet );
+ return *rxSheetScens;
+}
+
+void ScenarioBuffer::finalizeImport()
+{
+ maSheetScenarios.forEachMem( &SheetScenarios::finalizeImport );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/scenariocontext.cxx b/sc/source/filter/oox/scenariocontext.cxx
new file mode 100644
index 000000000..2e192d4a3
--- /dev/null
+++ b/sc/source/filter/oox/scenariocontext.cxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scenariocontext.hxx>
+#include <biffhelper.hxx>
+
+#include <scenariobuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+ScenarioContext::ScenarioContext( WorksheetContextBase& rParent, SheetScenarios& rSheetScenarios ) :
+ WorksheetContextBase( rParent ),
+ mrScenario( rSheetScenarios.createScenario() )
+{
+}
+
+ContextHandlerRef ScenarioContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( scenario ):
+ if( nElement == XLS_TOKEN( inputCells ) ) mrScenario.importInputCells( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void ScenarioContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrScenario.importScenario( rAttribs );
+}
+
+ContextHandlerRef ScenarioContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_SCENARIO:
+ if( nRecId == BIFF12_ID_INPUTCELLS ) mrScenario.importInputCells( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+void ScenarioContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrScenario.importScenario( rStrm );
+}
+
+ScenariosContext::ScenariosContext( WorksheetFragmentBase& rFragment ) :
+ WorksheetContextBase( rFragment ),
+ mrSheetScenarios( getScenarios().createSheetScenarios( getSheetIndex() ) )
+{
+}
+
+ContextHandlerRef ScenariosContext::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( scenarios ):
+ if( nElement == XLS_TOKEN( scenario ) ) return new ScenarioContext( *this, mrSheetScenarios );
+ break;
+ }
+ return nullptr;
+}
+
+void ScenariosContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( isRootElement() )
+ mrSheetScenarios.importScenarios( rAttribs );
+}
+
+ContextHandlerRef ScenariosContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_SCENARIOS:
+ if( nRecId == BIFF12_ID_SCENARIO ) return new ScenarioContext( *this, mrSheetScenarios );
+ break;
+ }
+ return nullptr;
+}
+
+void ScenariosContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ if( isRootElement() )
+ mrSheetScenarios.importScenarios( rStrm );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/sharedstringsbuffer.cxx b/sc/source/filter/oox/sharedstringsbuffer.cxx
new file mode 100644
index 000000000..2f1287d73
--- /dev/null
+++ b/sc/source/filter/oox/sharedstringsbuffer.cxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sharedstringsbuffer.hxx>
+
+namespace oox::xls {
+
+SharedStringsBuffer::SharedStringsBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+RichStringRef SharedStringsBuffer::createRichString()
+{
+ RichStringRef xString = std::make_shared<RichString>( *this );
+ maStrings.push_back( xString );
+ return xString;
+}
+
+void SharedStringsBuffer::finalizeImport()
+{
+ maStrings.forEachMem( &RichString::finalizeImport );
+}
+
+RichStringRef SharedStringsBuffer::getString( sal_Int32 nStringId ) const
+{
+ return maStrings.get( nStringId );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/sharedstringsfragment.cxx b/sc/source/filter/oox/sharedstringsfragment.cxx
new file mode 100644
index 000000000..a1f106645
--- /dev/null
+++ b/sc/source/filter/oox/sharedstringsfragment.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sharedstringsfragment.hxx>
+#include <biffhelper.hxx>
+
+#include <richstringcontext.hxx>
+#include <sharedstringsbuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+SharedStringsFragment::SharedStringsFragment(
+ const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef SharedStringsFragment::onCreateContext( sal_Int32 nElement, const AttributeList& )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( sst ) )
+ return this;
+ break;
+
+ case XLS_TOKEN( sst ):
+ if( nElement == XLS_TOKEN( si ) )
+ return new RichStringContext( *this, getSharedStrings().createRichString() );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef SharedStringsFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_SST )
+ return this;
+ break;
+
+ case BIFF12_ID_SST:
+ if( nRecId == BIFF12_ID_SI )
+ getSharedStrings().createRichString()->importString( rStrm, true );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* SharedStringsFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_SST, BIFF12_ID_SST + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void SharedStringsFragment::finalizeImport()
+{
+ getSharedStrings().finalizeImport();
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/sheetdatabuffer.cxx b/sc/source/filter/oox/sheetdatabuffer.cxx
new file mode 100644
index 000000000..244d70b62
--- /dev/null
+++ b/sc/source/filter/oox/sheetdatabuffer.cxx
@@ -0,0 +1,823 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sheetdatabuffer.hxx>
+
+#include <algorithm>
+#include <com/sun/star/sheet/XArrayFormulaTokens.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/table/XCell.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <editeng/boxitem.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <formulaparser.hxx>
+#include <sharedstringsbuffer.hxx>
+#include <unitconverter.hxx>
+#include <rangelst.hxx>
+#include <document.hxx>
+#include <scitems.hxx>
+#include <docpool.hxx>
+#include <paramisc.hxx>
+#include <patattr.hxx>
+#include <documentimport.hxx>
+#include <formulabuffer.hxx>
+#include <numformat.hxx>
+#include <sax/tools/converter.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+CellModel::CellModel() :
+ mnCellType( XML_TOKEN_INVALID ),
+ mnXfId( -1 ),
+ mbShowPhonetic( false )
+{
+}
+
+CellFormulaModel::CellFormulaModel() :
+ mnFormulaType( XML_TOKEN_INVALID ),
+ mnSharedId( -1 )
+{
+}
+
+bool CellFormulaModel::isValidArrayRef( const ScAddress& rCellAddr )
+{
+ return (maFormulaRef.aStart == rCellAddr );
+}
+
+bool CellFormulaModel::isValidSharedRef( const ScAddress& rCellAddr )
+{
+ return
+ (maFormulaRef.aStart.Tab() == rCellAddr.Tab() ) &&
+ (maFormulaRef.aStart.Col() <= rCellAddr.Col() ) && (rCellAddr.Col() <= maFormulaRef.aEnd.Col()) &&
+ (maFormulaRef.aStart.Row() <= rCellAddr.Row() ) && (rCellAddr.Row() <= maFormulaRef.aEnd.Row());
+}
+
+DataTableModel::DataTableModel() :
+ mb2dTable( false ),
+ mbRowTable( false ),
+ mbRef1Deleted( false ),
+ mbRef2Deleted( false )
+{
+}
+
+CellBlockBuffer::CellBlockBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ mnCurrRow( -1 )
+{
+}
+
+void CellBlockBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
+{
+ OSL_ENSURE( maColSpans.count( nRow ) == 0, "CellBlockBuffer::setColSpans - multiple column spans for the same row" );
+ OSL_ENSURE( (mnCurrRow < nRow) && (maColSpans.empty() || (maColSpans.rbegin()->first < nRow)), "CellBlockBuffer::setColSpans - rows are unsorted" );
+ if( mnCurrRow >= nRow )
+ return;
+ auto pair = maColSpans.try_emplace(nRow, rColSpans.getRanges());
+ if( pair.second ) // insert happened
+ mnCurrRow = nRow;
+}
+
+SheetDataBuffer::SheetDataBuffer( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ maCellBlocks( rHelper ),
+ mbPendingSharedFmla( false )
+{
+}
+
+void SheetDataBuffer::setColSpans( sal_Int32 nRow, const ValueRangeSet& rColSpans )
+{
+ maCellBlocks.setColSpans( nRow, rColSpans );
+}
+
+void SheetDataBuffer::setBlankCell( const CellModel& rModel )
+{
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setValueCell( const CellModel& rModel, double fValue )
+{
+ getDocImport().setNumericCell(rModel.maCellAddr, fValue);
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setStringCell( const CellModel& rModel, const OUString& rText )
+{
+ if (!rText.isEmpty())
+ getDocImport().setStringCell(rModel.maCellAddr, rText);
+
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setStringCell( const CellModel& rModel, const RichStringRef& rxString )
+{
+ OSL_ENSURE( rxString, "SheetDataBuffer::setStringCell - missing rich string object" );
+ const oox::xls::Font* pFirstPortionFont = getStyles().getFontFromCellXf( rModel.mnXfId ).get();
+ OUString aText;
+ if( rxString->extractPlainString( aText, pFirstPortionFont ) )
+ {
+ setStringCell( rModel, aText );
+ }
+ else
+ {
+ putRichString( rModel.maCellAddr, *rxString, pFirstPortionFont );
+ setCellFormat( rModel );
+ }
+}
+
+void SheetDataBuffer::setStringCell( const CellModel& rModel, sal_Int32 nStringId )
+{
+ RichStringRef xString = getSharedStrings().getString( nStringId );
+ if( xString )
+ setStringCell( rModel, xString );
+ else
+ setBlankCell( rModel );
+}
+
+void SheetDataBuffer::setDateTimeCell( const CellModel& rModel, const css::util::DateTime& rDateTime )
+{
+ // write serial date/time value into the cell
+ double fSerial = getUnitConverter().calcSerialFromDateTime( rDateTime );
+ setValueCell( rModel, fSerial );
+ // set appropriate number format
+ using namespace ::com::sun::star::util::NumberFormat;
+ sal_Int16 nStdFmt = (fSerial < 1.0) ? TIME : (((rDateTime.Hours > 0) || (rDateTime.Minutes > 0) || (rDateTime.Seconds > 0)) ? DATETIME : DATE);
+ // set number format
+ try
+ {
+ Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY_THROW );
+ Reference< XNumberFormatTypes > xNumFmtTypes( xNumFmtsSupp->getNumberFormats(), UNO_QUERY_THROW );
+ sal_Int32 nIndex = xNumFmtTypes->getStandardFormat( nStdFmt, Locale() );
+ PropertySet aPropSet( getCell( rModel.maCellAddr ) );
+ aPropSet.setProperty( PROP_NumberFormat, nIndex );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void SheetDataBuffer::setBooleanCell( const CellModel& rModel, bool bValue )
+{
+ getFormulaBuffer().setCellFormula(
+ rModel.maCellAddr, bValue ? OUString("TRUE()") : OUString("FALSE()"));
+
+ // #108770# set 'Standard' number format for all Boolean cells
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setErrorCell( const CellModel& rModel, const OUString& rErrorCode )
+{
+ // Using the formula compiler now we can simply pass on the error string.
+ getFormulaBuffer().setCellFormula( rModel.maCellAddr, rErrorCode);
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::setErrorCell( const CellModel& rModel, sal_uInt8 nErrorCode )
+{
+ setErrorCell( rModel, getUnitConverter().calcErrorString( nErrorCode));
+}
+
+void SheetDataBuffer::setDateCell( const CellModel& rModel, const OUString& rDateString )
+{
+ css::util::DateTime aDateTime;
+ if (!sax::Converter::parseDateTime( aDateTime, rDateString))
+ {
+ SAL_WARN("sc.filter", "SheetDataBuffer::setDateCell - could not parse: " << rDateString);
+ // At least don't lose data.
+ setStringCell( rModel, rDateString);
+ return;
+ }
+
+ double fSerial = getUnitConverter().calcSerialFromDateTime( aDateTime);
+ setValueCell( rModel, fSerial);
+}
+
+void SheetDataBuffer::createSharedFormula(const ScAddress& rAddr, const ApiTokenSequence& rTokens)
+{
+ BinAddress aAddr(rAddr);
+ maSharedFormulas[aAddr] = rTokens;
+ if( mbPendingSharedFmla )
+ setCellFormula( maSharedFmlaAddr, resolveSharedFormula( maSharedBaseAddr ) );
+}
+
+void SheetDataBuffer::setFormulaCell( const CellModel& rModel, const ApiTokenSequence& rTokens )
+{
+ mbPendingSharedFmla = false;
+ ApiTokenSequence aTokens;
+
+ /* Detect special token passed as placeholder for array formulas, shared
+ formulas, and table operations. In BIFF, these formulas are represented
+ by a single tExp resp. tTbl token. If the formula parser finds these
+ tokens, it puts a single OPCODE_BAD token with the base address and
+ formula type into the token sequence. This information will be
+ extracted here, and in case of a shared formula, the shared formula
+ buffer will generate the resulting formula token array. */
+ ApiSpecialTokenInfo aTokenInfo;
+ if( rTokens.hasElements() && getFormulaParser().extractSpecialTokenInfo( aTokenInfo, rTokens ) )
+ {
+ /* The second member of the token info is set to true, if the formula
+ represents a table operation, which will be skipped. In BIFF12 it
+ is not possible to distinguish array and shared formulas
+ (BIFF5/BIFF8 provide this information with a special flag in the
+ FORMULA record). */
+ if( !aTokenInfo.Second )
+ {
+ /* Construct the token array representing the shared formula. If
+ the returned sequence is empty, the definition of the shared
+ formula has not been loaded yet, or the cell is part of an
+ array formula. In this case, the cell will be remembered. After
+ reading the formula definition it will be retried to insert the
+ formula via retryPendingSharedFormulaCell(). */
+ ScAddress aTokenAddr( aTokenInfo.First.Column, aTokenInfo.First.Row, aTokenInfo.First.Sheet );
+ aTokens = resolveSharedFormula( aTokenAddr );
+ if( !aTokens.hasElements() )
+ {
+ maSharedFmlaAddr = rModel.maCellAddr;
+ maSharedBaseAddr = aTokenAddr;
+ mbPendingSharedFmla = true;
+ }
+ }
+ }
+ else
+ {
+ // simple formula, use the passed token array
+ aTokens = rTokens;
+ }
+
+ setCellFormula( rModel.maCellAddr, aTokens );
+ setCellFormat( rModel );
+}
+
+void SheetDataBuffer::createArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens )
+{
+ /* Array formulas will be inserted later in finalizeImport(). This is
+ needed to not disturb collecting all the cells, which will be put into
+ the sheet in large blocks to increase performance. */
+ maArrayFormulas.emplace_back( rRange, rTokens );
+}
+
+void SheetDataBuffer::createTableOperation( const ScRange& rRange, const DataTableModel& rModel )
+{
+ /* Table operations will be inserted later in finalizeImport(). This is
+ needed to not disturb collecting all the cells, which will be put into
+ the sheet in large blocks to increase performance. */
+ maTableOperations.emplace_back( rRange, rModel );
+}
+
+void SheetDataBuffer::setRowFormat( sal_Int32 nRow, sal_Int32 nXfId, bool bCustomFormat )
+{
+ // set row formatting
+ if( bCustomFormat )
+ {
+ // try to expand cached row range, if formatting is equal
+ if( (maXfIdRowRange.maRowRange.mnLast < 0) || !maXfIdRowRange.tryExpand( nRow, nXfId ) )
+ {
+
+ maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
+ maXfIdRowRange.set( nRow, nXfId );
+ }
+ }
+ else if( maXfIdRowRange.maRowRange.mnLast >= 0 )
+ {
+ // finish last cached row range
+ maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
+ maXfIdRowRange.set( -1, -1 );
+ }
+}
+
+void SheetDataBuffer::setMergedRange( const ScRange& rRange )
+{
+ maMergedRanges.emplace_back( rRange );
+}
+
+typedef std::pair<sal_Int32, sal_Int32> FormatKeyPair;
+
+static void addIfNotInMyMap( const StylesBuffer& rStyles, std::map< FormatKeyPair, ScRangeList >& rMap, sal_Int32 nXfId, sal_Int32 nFormatId, const ScRangeList& rRangeList )
+{
+ Xf* pXf1 = rStyles.getCellXf( nXfId ).get();
+ if ( !pXf1 )
+ return;
+
+ auto it = std::find_if(rMap.begin(), rMap.end(),
+ [&nFormatId, &rStyles, &pXf1](const std::pair<FormatKeyPair, ScRangeList>& rEntry) {
+ if (rEntry.first.second != nFormatId)
+ return false;
+ Xf* pXf2 = rStyles.getCellXf( rEntry.first.first ).get();
+ return *pXf1 == *pXf2;
+ });
+ if (it != rMap.end()) // already exists
+ {
+ // add ranges from the rangelist to the existing rangelist for the
+ // matching style ( should we check if they overlap ? )
+ it->second.insert(it->second.end(), rRangeList.begin(), rRangeList.end());
+ return;
+ }
+ rMap[ FormatKeyPair( nXfId, nFormatId ) ] = rRangeList;
+}
+
+void SheetDataBuffer::addColXfStyles()
+{
+ std::map< FormatKeyPair, ScRangeList > rangeStyleListMap;
+ for( const auto& [rFormatKeyPair, rRangeList] : maXfIdRangeLists )
+ {
+ addIfNotInMyMap( getStyles(), rangeStyleListMap, rFormatKeyPair.first, rFormatKeyPair.second, rRangeList );
+ }
+ // gather all ranges that have the same style and apply them in bulk
+ // Collect data in unsorted vectors and sort them just once at the end
+ // instead of possibly slow repeated inserts.
+ TmpColStyles tmpStylesPerColumn;
+ for ( const auto& [rFormatKeyPair, rRanges] : rangeStyleListMap )
+ {
+ for (const ScRange & rAddress : rRanges)
+ {
+ RowRangeStyle aStyleRows;
+ aStyleRows.mnNumFmt.first = rFormatKeyPair.first;
+ aStyleRows.mnNumFmt.second = rFormatKeyPair.second;
+ aStyleRows.mnStartRow = rAddress.aStart.Row();
+ aStyleRows.mnEndRow = rAddress.aEnd.Row();
+ for ( sal_Int32 nCol = rAddress.aStart.Col(); nCol <= rAddress.aEnd.Col(); ++nCol )
+ tmpStylesPerColumn[ nCol ].push_back( aStyleRows );
+ }
+ }
+ for( auto& rowStyles : tmpStylesPerColumn )
+ {
+ TmpRowStyles& s = rowStyles.second;
+ std::sort( s.begin(), s.end(), StyleRowRangeComp());
+ s.erase( std::unique( s.begin(), s.end(),
+ [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
+ // Synthetize operator== from operator < . Do not create an actual operator==
+ // as operator< is somewhat specific (see StyleRowRangeComp).
+ { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
+ s.end());
+ // Broken documents may have overlapping ranges that cause problems, repeat once more.
+ if(!std::is_sorted(s.begin(), s.end(), StyleRowRangeComp()))
+ {
+ std::sort( s.begin(), s.end(), StyleRowRangeComp());
+ s.erase( std::unique( s.begin(), s.end(),
+ [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
+ { return !StyleRowRangeComp()(lhs,rhs) && !StyleRowRangeComp()(rhs,lhs); } ),
+ s.end());
+ }
+ maStylesPerColumn[ rowStyles.first ].insert_sorted_unique_vector( std::move( s ));
+ }
+}
+
+void SheetDataBuffer::addColXfStyleProcessRowRanges()
+{
+ // count the number of row-range-styles we have
+ AddressConverter& rAddrConv = getAddressConverter();
+ int cnt = 0;
+ for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
+ {
+ if ( nXfId == -1 ) // it's a dud skip it
+ continue;
+ cnt += rRowRangeList.size();
+ }
+ // pre-allocate space in the sorted_vector
+ for ( sal_Int32 nCol = 0; nCol <= rAddrConv.getMaxApiAddress().Col(); ++nCol )
+ {
+ RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
+ rRowStyles.reserve(rRowStyles.size() + cnt);
+ }
+ const auto nMaxCol = rAddrConv.getMaxApiAddress().Col();
+ for ( sal_Int32 nCol = 0; nCol <= nMaxCol; ++nCol )
+ {
+ RowStyles& rRowStyles = maStylesPerColumn[ nCol ];
+ for ( const auto& [nXfId, rRowRangeList] : maXfIdRowRangeList )
+ {
+ if ( nXfId == -1 ) // it's a dud skip it
+ continue;
+ // get all row ranges for id
+ for ( const auto& rRange : rRowRangeList )
+ {
+ RowRangeStyle aStyleRows;
+ aStyleRows.mnNumFmt.first = nXfId;
+ aStyleRows.mnNumFmt.second = -1;
+
+ // Reset row range for each column
+ aStyleRows.mnStartRow = rRange.mnFirst;
+ aStyleRows.mnEndRow = rRange.mnLast;
+
+ // If aStyleRows includes rows already allocated to a style
+ // in rRowStyles, then we need to split it into parts.
+ // ( to occupy only rows that have no style definition)
+
+ // Start iterating at the first element that is not completely before aStyleRows
+ RowStyles::const_iterator rows_it = rRowStyles.lower_bound(aStyleRows);
+ bool bAddRange = true;
+ for ( ; rows_it != rRowStyles.end(); ++rows_it )
+ {
+ // Add the part of aStyleRows that does not overlap with r
+ if ( aStyleRows.mnStartRow < rows_it->mnStartRow )
+ {
+ RowRangeStyle aSplit = aStyleRows;
+ aSplit.mnEndRow = std::min(aStyleRows.mnEndRow, rows_it->mnStartRow - 1);
+ rows_it = rRowStyles.insert( aSplit ).first;
+ }
+
+ // Done if no part of aStyleRows extends beyond r
+ if ( aStyleRows.mnEndRow <= rows_it->mnEndRow )
+ {
+ bAddRange = false;
+ break;
+ }
+
+ // Cut off the part aStyleRows that was handled above
+ aStyleRows.mnStartRow = rows_it->mnEndRow + 1;
+ }
+ if ( bAddRange )
+ rRowStyles.insert( aStyleRows );
+ }
+ }
+ }
+}
+
+void SheetDataBuffer::finalizeImport()
+{
+ ScDocumentImport& rDocImport = getDocImport();
+
+ SCTAB nStartTabInvalidatedIters(SCTAB_MAX);
+ SCTAB nEndTabInvalidatedIters(0);
+
+ // create all array formulas
+ for( const auto& [rRange, rTokens] : maArrayFormulas )
+ {
+ finalizeArrayFormula(rRange, rTokens);
+
+ nStartTabInvalidatedIters = std::min(rRange.aStart.Tab(), nStartTabInvalidatedIters);
+ nEndTabInvalidatedIters = std::max(rRange.aEnd.Tab(), nEndTabInvalidatedIters);
+ }
+
+ for (SCTAB nTab = nStartTabInvalidatedIters; nTab <= nEndTabInvalidatedIters; ++nTab)
+ rDocImport.invalidateBlockPositionSet(nTab);
+
+ // create all table operations
+ for( const auto& [rRange, rModel] : maTableOperations )
+ finalizeTableOperation( rRange, rModel );
+
+ // write default formatting of remaining row range
+ maXfIdRowRangeList[ maXfIdRowRange.mnXfId ].push_back( maXfIdRowRange.maRowRange );
+
+ addColXfStyles();
+
+ addColXfStyleProcessRowRanges();
+
+ ScDocument& rDoc = rDocImport.getDoc();
+ StylesBuffer& rStyles = getStyles();
+ ScDocumentImport::Attrs aPendingAttrParam;
+ SCCOL pendingColStart = -1;
+ SCCOL pendingColEnd = -1;
+ for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
+ {
+ SCCOL nScCol = static_cast< SCCOL >( rCol );
+
+ // tdf#91567 Get pattern from the first row without AutoFilter
+ const ScPatternAttr* pDefPattern = nullptr;
+ bool bAutoFilter = true;
+ SCROW nScRow = 0;
+ while ( bAutoFilter && nScRow < rDoc.MaxRow() )
+ {
+ pDefPattern = rDoc.GetPattern( nScCol, nScRow, getSheetIndex() );
+ if ( pDefPattern )
+ {
+ const ScMergeFlagAttr* pAttr = pDefPattern->GetItemSet().GetItem( ATTR_MERGE_FLAG );
+ bAutoFilter = pAttr->HasAutoFilter();
+ }
+ else
+ break;
+ nScRow++;
+ }
+ if ( !pDefPattern || nScRow == rDoc.MaxRow() )
+ pDefPattern = rDoc.GetDefPattern();
+
+ Xf::AttrList aAttrs(pDefPattern);
+ for ( const auto& rRowStyle : rRowStyles )
+ {
+ Xf* pXf = rStyles.getCellXf( rRowStyle.mnNumFmt.first ).get();
+
+ if ( pXf )
+ pXf->applyPatternToAttrList( aAttrs, rRowStyle.mnStartRow, rRowStyle.mnEndRow, rRowStyle.mnNumFmt.second );
+ }
+ if (aAttrs.maAttrs.empty() || aAttrs.maAttrs.back().nEndRow != rDoc.MaxRow())
+ {
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = rDoc.MaxRow();
+ aEntry.pPattern = pDefPattern;
+ rDoc.GetPool()->Put(*aEntry.pPattern);
+ aAttrs.maAttrs.push_back(aEntry);
+
+ if (!sc::NumFmtUtil::isLatinScript(*aEntry.pPattern, rDoc))
+ aAttrs.mbLatinNumFmtOnly = false;
+ }
+
+ ScDocumentImport::Attrs aAttrParam;
+ aAttrParam.mvData.swap(aAttrs.maAttrs);
+ aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
+
+ // Compress setting the attributes, set the same set in one call.
+ if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
+ ++pendingColEnd;
+ else
+ {
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+ pendingColStart = pendingColEnd = nScCol;
+ aPendingAttrParam = std::move( aAttrParam );
+ }
+ }
+ if( pendingColStart != -1 )
+ rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
+
+ // merge all cached merged ranges and update right/bottom cell borders
+ for( const auto& rMergedRange : maMergedRanges )
+ applyCellMerging( rMergedRange.maRange );
+ for( const auto& rCenterFillRange : maCenterFillRanges )
+ applyCellMerging( rCenterFillRange.maRange );
+}
+
+// private --------------------------------------------------------------------
+
+SheetDataBuffer::XfIdRowRange::XfIdRowRange() :
+ maRowRange( -1 ),
+ mnXfId( -1 )
+{
+}
+
+void SheetDataBuffer::XfIdRowRange::set( sal_Int32 nRow, sal_Int32 nXfId )
+{
+ maRowRange = ValueRange( nRow );
+ mnXfId = nXfId;
+}
+
+bool SheetDataBuffer::XfIdRowRange::tryExpand( sal_Int32 nRow, sal_Int32 nXfId )
+{
+ if( mnXfId == nXfId )
+ {
+ if( maRowRange.mnLast + 1 == nRow )
+ {
+ ++maRowRange.mnLast;
+ return true;
+ }
+ if( maRowRange.mnFirst == nRow + 1 )
+ {
+ --maRowRange.mnFirst;
+ return true;
+ }
+ }
+ return false;
+}
+
+SheetDataBuffer::MergedRange::MergedRange( const ScRange& rRange ) :
+ maRange( rRange ),
+ mnHorAlign( XML_TOKEN_INVALID )
+{
+}
+
+SheetDataBuffer::MergedRange::MergedRange( const ScAddress& rAddress, sal_Int32 nHorAlign ) :
+ maRange( rAddress, rAddress ),
+ mnHorAlign( nHorAlign )
+{
+}
+
+bool SheetDataBuffer::MergedRange::tryExpand( const ScAddress& rAddress, sal_Int32 nHorAlign )
+{
+ if( (mnHorAlign == nHorAlign) && (maRange.aStart.Row() == rAddress.Row() ) &&
+ (maRange.aEnd.Row() == rAddress.Row() ) && (maRange.aEnd.Col() + 1 == rAddress.Col() ) )
+ {
+ maRange.aEnd.IncCol();
+ return true;
+ }
+ return false;
+}
+
+void SheetDataBuffer::setCellFormula( const ScAddress& rCellAddr, const ApiTokenSequence& rTokens )
+{
+ if( rTokens.hasElements() )
+ {
+ putFormulaTokens( rCellAddr, rTokens );
+ }
+}
+
+
+ApiTokenSequence SheetDataBuffer::resolveSharedFormula( const ScAddress& rAddr ) const
+{
+ BinAddress aAddr(rAddr);
+ ApiTokenSequence aTokens = ContainerHelper::getMapElement( maSharedFormulas, aAddr, ApiTokenSequence() );
+ return aTokens;
+}
+
+void SheetDataBuffer::finalizeArrayFormula( const ScRange& rRange, const ApiTokenSequence& rTokens ) const
+{
+ Reference< XArrayFormulaTokens > xTokens( getCellRange( rRange ), UNO_QUERY );
+ OSL_ENSURE( xTokens.is(), "SheetDataBuffer::finalizeArrayFormula - missing formula token interface" );
+ if( xTokens.is() )
+ xTokens->setArrayTokens( rTokens );
+}
+
+void SheetDataBuffer::finalizeTableOperation( const ScRange& rRange, const DataTableModel& rModel )
+{
+ if (rModel.mbRef1Deleted)
+ return;
+
+ if (rModel.maRef1.isEmpty())
+ return;
+
+ if (rRange.aStart.Col() <= 0 || rRange.aStart.Row() <= 0)
+ return;
+
+ sal_Int16 nSheet = getSheetIndex();
+
+ ScAddress aRef1( 0, 0, 0 );
+ if (!getAddressConverter().convertToCellAddress(aRef1, rModel.maRef1, nSheet, true))
+ return;
+
+ ScDocumentImport& rDoc = getDocImport();
+ ScTabOpParam aParam;
+
+ ScRange aScRange(rRange);
+
+ if (rModel.mb2dTable)
+ {
+ // Two-variable data table.
+ if (rModel.mbRef2Deleted)
+ return;
+
+ if (rModel.maRef2.isEmpty())
+ return;
+
+ ScAddress aRef2( 0, 0, 0 );
+ if (!getAddressConverter().convertToCellAddress(aRef2, rModel.maRef2, nSheet, true))
+ return;
+
+ aParam.meMode = ScTabOpParam::Both;
+
+ aScRange.aStart.IncCol(-1);
+ aScRange.aStart.IncRow(-1);
+
+ aParam.aRefFormulaCell.Set(aScRange.aStart.Col(), aScRange.aStart.Row(), nSheet, false, false, false);
+ aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
+
+ // Ref1 is row input cell and Ref2 is column input cell.
+ aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
+ aParam.aRefColCell.Set(aRef2.Col(), aRef2.Row(), aRef2.Tab(), false, false, false);
+ rDoc.setTableOpCells(aScRange, aParam);
+
+ return;
+ }
+
+ // One-variable data table.
+
+ if (rModel.mbRowTable)
+ {
+ // One-variable row input cell (horizontal).
+ aParam.meMode = ScTabOpParam::Row;
+ aParam.aRefRowCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
+ aParam.aRefFormulaCell.Set(rRange.aStart.Col()-1, rRange.aStart.Row(), nSheet, false, true, false);
+ aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
+ aScRange.aStart.IncRow(-1);
+ rDoc.setTableOpCells(aScRange, aParam);
+ }
+ else
+ {
+ // One-variable column input cell (vertical).
+ aParam.meMode = ScTabOpParam::Column;
+ aParam.aRefColCell.Set(aRef1.Col(), aRef1.Row(), aRef1.Tab(), false, false, false);
+ aParam.aRefFormulaCell.Set(rRange.aStart.Col(), rRange.aStart.Row()-1, nSheet, true, false, false);
+ aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
+ aScRange.aStart.IncCol(-1);
+ rDoc.setTableOpCells(aScRange, aParam);
+ }
+}
+
+void SheetDataBuffer::setCellFormat( const CellModel& rModel )
+{
+ if( rModel.mnXfId < 0 )
+ return;
+
+ ScRangeList& rRangeList = maXfIdRangeLists[ XfIdNumFmtKey( rModel.mnXfId, -1 ) ];
+ ScRange* pLastRange = rRangeList.empty() ? nullptr : &rRangeList.back();
+ /* The xlsx sheet data contains row wise information.
+ * It is sufficient to check if the row range size is one
+ */
+ if (!rRangeList.empty() &&
+ *pLastRange == rModel.maCellAddr)
+ ; // do nothing - this probably bad data
+ else if (!rRangeList.empty() &&
+ pLastRange->aStart.Tab() == rModel.maCellAddr.Tab() &&
+ pLastRange->aStart.Row() == pLastRange->aEnd.Row() &&
+ pLastRange->aStart.Row() == rModel.maCellAddr.Row() &&
+ pLastRange->aEnd.Col() + 1 == rModel.maCellAddr.Col())
+ {
+ pLastRange->aEnd.IncCol(); // Expand Column
+ }
+ else
+ {
+ rRangeList.push_back(ScRange(rModel.maCellAddr));
+ pLastRange = &rRangeList.back();
+ }
+
+ if (rRangeList.size() > 1)
+ {
+ for (size_t i = rRangeList.size() - 1; i != 0; --i)
+ {
+ ScRange& rMergeRange = rRangeList[i - 1];
+ if (pLastRange->aStart.Tab() != rMergeRange.aStart.Tab())
+ break;
+
+ /* Try to merge this with the previous range */
+ if (pLastRange->aStart.Row() == (rMergeRange.aEnd.Row() + 1) &&
+ pLastRange->aStart.Col() == rMergeRange.aStart.Col() &&
+ pLastRange->aEnd.Col() == rMergeRange.aEnd.Col())
+ {
+ rMergeRange.aEnd.SetRow(pLastRange->aEnd.Row());
+ rRangeList.Remove(rRangeList.size() - 1);
+ break;
+ }
+ else if (pLastRange->aStart.Row() > (rMergeRange.aEnd.Row() + 1))
+ break; // Un-necessary to check with any other rows
+ }
+ }
+ // update merged ranges for 'center across selection' and 'fill'
+ const Xf* pXf = getStyles().getCellXf( rModel.mnXfId ).get();
+ if( !pXf )
+ return;
+
+ sal_Int32 nHorAlign = pXf->getAlignment().getModel().mnHorAlign;
+ if( (nHorAlign == XML_centerContinuous) || (nHorAlign == XML_fill) )
+ {
+ /* start new merged range, if cell is not empty (#108781#),
+ or try to expand last range with empty cell */
+ if( rModel.mnCellType != XML_TOKEN_INVALID )
+ maCenterFillRanges.emplace_back( rModel.maCellAddr, nHorAlign );
+ else if( !maCenterFillRanges.empty() )
+ maCenterFillRanges.rbegin()->tryExpand( rModel.maCellAddr, nHorAlign );
+ }
+}
+
+static void lcl_SetBorderLine( ScDocument& rDoc, const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
+{
+ SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
+ SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
+
+ const SvxBoxItem* pFromItem =
+ rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
+ const SvxBoxItem* pToItem =
+ rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
+
+ SvxBoxItem aNewItem( *pToItem );
+ aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
+ rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
+}
+
+void SheetDataBuffer::applyCellMerging( const ScRange& rRange )
+{
+ bool bMultiCol = rRange.aStart.Col() < rRange.aEnd.Col();
+ bool bMultiRow = rRange.aStart.Row() < rRange.aEnd.Row();
+
+ const ScAddress& rStart = rRange.aStart;
+ const ScAddress& rEnd = rRange.aEnd;
+ ScDocument& rDoc = getScDocument();
+ // set correct right border
+ if( bMultiCol )
+ lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::RIGHT );
+ // set correct lower border
+ if( bMultiRow )
+ lcl_SetBorderLine( rDoc, rRange, getSheetIndex(), SvxBoxItemLine::BOTTOM );
+ // do merge
+ if( bMultiCol || bMultiRow )
+ rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), getSheetIndex() );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/sheetdatacontext.cxx b/sc/source/filter/oox/sheetdatacontext.cxx
new file mode 100644
index 000000000..f4a9330b4
--- /dev/null
+++ b/sc/source/filter/oox/sheetdatacontext.cxx
@@ -0,0 +1,583 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sheetdatacontext.hxx>
+
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <formulaparser.hxx>
+#include <richstringcontext.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+namespace {
+
+// record constants -----------------------------------------------------------
+
+const sal_uInt32 BIFF12_CELL_SHOWPHONETIC = 0x01000000;
+
+const sal_uInt8 BIFF12_DATATABLE_ROW = 0x01;
+const sal_uInt8 BIFF12_DATATABLE_2D = 0x02;
+const sal_uInt8 BIFF12_DATATABLE_REF1DEL = 0x04;
+const sal_uInt8 BIFF12_DATATABLE_REF2DEL = 0x08;
+
+const sal_uInt16 BIFF12_ROW_THICKTOP = 0x0001;
+const sal_uInt16 BIFF12_ROW_THICKBOTTOM = 0x0002;
+const sal_uInt16 BIFF12_ROW_COLLAPSED = 0x0800;
+const sal_uInt16 BIFF12_ROW_HIDDEN = 0x1000;
+const sal_uInt16 BIFF12_ROW_CUSTOMHEIGHT = 0x2000;
+const sal_uInt16 BIFF12_ROW_CUSTOMFORMAT = 0x4000;
+const sal_uInt8 BIFF12_ROW_SHOWPHONETIC = 0x01;
+
+} // namespace
+
+SheetDataContext::SheetDataContext( WorksheetFragmentBase& rFragment ) :
+ WorksheetContextBase( rFragment ),
+ mrAddressConv( rFragment.getAddressConverter() ),
+ mrSheetData( rFragment.getSheetData() ),
+ mnSheet( rFragment.getSheetIndex() ),
+ mbHasFormula( false ),
+ mbValidRange( false ),
+ mnRow( -1 ),
+ mnCol( -1 )
+{
+ SAL_INFO( "sc.filter", "start safe sheet data context - unlock" );
+ mxFormulaParser.reset(rFragment.createFormulaParser());
+}
+
+SheetDataContext::~SheetDataContext()
+{
+ SAL_INFO( "sc.filter", "end safe sheet data context - relock" );
+}
+
+ContextHandlerRef SheetDataContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( sheetData ):
+ if( nElement == XLS_TOKEN( row ) ) { importRow( rAttribs ); return this; }
+ break;
+
+ case XLS_TOKEN( row ):
+ // do not process cell elements with invalid (out-of-range) address
+ if( nElement == XLS_TOKEN( c ) && importCell( rAttribs ) )
+ return this;
+ break;
+
+ case XLS_TOKEN( c ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( is ):
+ mxInlineStr = std::make_shared<RichString>( *this );
+ return new RichStringContext( *this, mxInlineStr );
+ case XLS_TOKEN( v ):
+ return this; // characters contain cell value
+ case XLS_TOKEN( f ):
+ importFormula( rAttribs );
+ return this; // characters contain formula string
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void SheetDataContext::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( v ):
+ maCellValue = rChars;
+ break;
+ case XLS_TOKEN( f ):
+ if( maFmlaData.mnFormulaType != XML_TOKEN_INVALID )
+ {
+ maFormulaStr = rChars;
+ }
+ break;
+ }
+}
+
+void SheetDataContext::onEndElement()
+{
+ if( getCurrentElement() != XLS_TOKEN( c ) )
+ return;
+
+ // try to create a formula cell
+ if( mbHasFormula ) switch( maFmlaData.mnFormulaType )
+ {
+ // will buffer formulas but need to
+ // a) need to set format first
+ // :/
+ case XML_normal:
+ setCellFormula( maCellData.maCellAddr, maFormulaStr );
+ mrSheetData.setCellFormat( maCellData );
+
+ // If a number cell has some preloaded value, stick it into the buffer
+ // but do this only for real cell formulas (not array, shared etc.)
+ if (!maCellValue.isEmpty())
+ setCellFormulaValue(maCellData.maCellAddr, maCellValue, maCellData.mnCellType);
+ break;
+
+ case XML_shared:
+ if( maFmlaData.mnSharedId >= 0 )
+ {
+ if( mbValidRange && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
+ createSharedFormulaMapEntry(maCellData.maCellAddr, maFmlaData.mnSharedId, maFormulaStr);
+
+ setCellFormula(maCellData.maCellAddr, maFmlaData.mnSharedId, maCellValue, maCellData.mnCellType);
+ mrSheetData.setCellFormat( maCellData );
+ }
+ else
+ // no success, set plain cell value and formatting below
+ mbHasFormula = false;
+ break;
+ case XML_array:
+ if( mbValidRange && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
+ {
+ setCellArrayFormula( maFmlaData.maFormulaRef, maCellData.maCellAddr, maFormulaStr );
+ }
+ // set cell formatting, but do not set result as cell value
+ mrSheetData.setBlankCell( maCellData );
+ break;
+ case XML_dataTable:
+ if( mbValidRange )
+ mrSheetData.createTableOperation( maFmlaData.maFormulaRef, maTableData );
+ // set cell formatting, but do not set result as cell value
+ mrSheetData.setBlankCell( maCellData );
+ break;
+ default:
+ OSL_ENSURE( maFmlaData.mnFormulaType == XML_TOKEN_INVALID, "SheetDataContext::onEndElement - unknown formula type" );
+ mbHasFormula = false;
+ }
+
+ if( mbHasFormula )
+ return;
+
+ // no formula created: try to set the cell value
+ if( !maCellValue.isEmpty() ) switch( maCellData.mnCellType )
+ {
+ case XML_n:
+ mrSheetData.setValueCell( maCellData, maCellValue.toDouble() );
+ break;
+ case XML_b:
+ mrSheetData.setBooleanCell( maCellData, maCellValue.toDouble() != 0.0 );
+ break;
+ case XML_e:
+ mrSheetData.setErrorCell( maCellData, maCellValue );
+ break;
+ case XML_str:
+ mrSheetData.setStringCell( maCellData, maCellValue );
+ break;
+ case XML_s:
+ mrSheetData.setStringCell( maCellData, maCellValue.toInt32() );
+ break;
+ case XML_d:
+ mrSheetData.setDateCell( maCellData, maCellValue );
+ break;
+ }
+ else if( (maCellData.mnCellType == XML_inlineStr) && mxInlineStr )
+ {
+ mxInlineStr->finalizeImport();
+ mrSheetData.setStringCell( maCellData, mxInlineStr );
+ }
+ else
+ {
+ // empty cell, update cell type
+ maCellData.mnCellType = XML_TOKEN_INVALID;
+ mrSheetData.setBlankCell( maCellData );
+ }
+}
+
+ContextHandlerRef SheetDataContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_SHEETDATA:
+ if( nRecId == BIFF12_ID_ROW ) { importRow( rStrm ); return this; }
+ break;
+
+ case BIFF12_ID_ROW:
+ switch( nRecId )
+ {
+ case BIFF12_ID_ARRAY: importArray( rStrm ); break;
+ case BIFF12_ID_CELL_BOOL: importCellBool( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_BLANK: importCellBlank( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_DOUBLE: importCellDouble( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_ERROR: importCellError( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_RK: importCellRk( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_RSTRING: importCellRString( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_SI: importCellSi( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_CELL_STRING: importCellString( rStrm, CELLTYPE_VALUE ); break;
+ case BIFF12_ID_DATATABLE: importDataTable( rStrm ); break;
+ case BIFF12_ID_FORMULA_BOOL: importCellBool( rStrm, CELLTYPE_FORMULA ); break;
+ case BIFF12_ID_FORMULA_DOUBLE: importCellDouble( rStrm, CELLTYPE_FORMULA ); break;
+ case BIFF12_ID_FORMULA_ERROR: importCellError( rStrm, CELLTYPE_FORMULA ); break;
+ case BIFF12_ID_FORMULA_STRING: importCellString( rStrm, CELLTYPE_FORMULA ); break;
+ case BIFF12_ID_MULTCELL_BOOL: importCellBool( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_BLANK: importCellBlank( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_DOUBLE: importCellDouble( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_ERROR: importCellError( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_RK: importCellRk( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_RSTRING:importCellRString( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_SI: importCellSi( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_MULTCELL_STRING: importCellString( rStrm, CELLTYPE_MULTI ); break;
+ case BIFF12_ID_SHAREDFMLA: importSharedFmla( rStrm ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+// private --------------------------------------------------------------------
+
+void SheetDataContext::importRow( const AttributeList& rAttribs )
+{
+ RowModel aModel;
+ sal_Int32 nRow = rAttribs.getInteger( XML_r, -1 ); // 1-based row index
+ if(nRow != -1)
+ {
+ aModel.mnRow = nRow;
+ mnRow = nRow-1; // to 0-based row index.
+ }
+ else
+ aModel.mnRow = (++mnRow + 1); // increment 0-based row index, to 1-based model row
+ mrAddressConv.checkRow( mnRow, true);
+ mnCol = -1;
+
+ aModel.mfHeight = rAttribs.getDouble( XML_ht, -1.0 );
+ aModel.mnXfId = rAttribs.getInteger( XML_s, -1 );
+ aModel.mnLevel = rAttribs.getInteger( XML_outlineLevel, 0 );
+ aModel.mbCustomHeight = rAttribs.getBool( XML_customHeight, false );
+ aModel.mbCustomFormat = rAttribs.getBool( XML_customFormat, false );
+ aModel.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
+ aModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+ aModel.mbCollapsed = rAttribs.getBool( XML_collapsed, false );
+ aModel.mbThickTop = rAttribs.getBool( XML_thickTop, false );
+ aModel.mbThickBottom = rAttribs.getBool( XML_thickBot, false );
+
+ if (aModel.mfHeight > 0 && getFilter().isMSODocument())
+ {
+ aModel.mfHeight -= fmod(aModel.mfHeight, 0.75); //round down to 0.75pt
+ }
+
+ // decode the column spans (space-separated list of colon-separated integer pairs)
+ OUString aColSpansText = rAttribs.getString( XML_spans, OUString() );
+ sal_Int32 nMaxCol = mrAddressConv.getMaxApiAddress().Col();
+ sal_Int32 nIndex = 0;
+ while( nIndex >= 0 )
+ {
+ std::u16string_view aColSpanToken = o3tl::getToken(aColSpansText, 0, ' ', nIndex );
+ size_t nSepPos = aColSpanToken.find( ':' );
+ if( (0 < nSepPos) && (nSepPos + 1 < aColSpanToken.size()) )
+ {
+ // OOXML uses 1-based integer column indexes, row model expects 0-based colspans
+ const sal_Int32 nCol1 = o3tl::toInt32(aColSpanToken.substr( 0, nSepPos )) - 1;
+ const bool bValid1 = mrAddressConv.checkCol( nCol1, true);
+ if (bValid1)
+ {
+ const sal_Int32 nCol2 = o3tl::toInt32(aColSpanToken.substr( nSepPos + 1 )) - 1;
+ mrAddressConv.checkCol( nCol2, true);
+ aModel.insertColSpan( ValueRange( nCol1, ::std::min( nCol2, nMaxCol )));
+ }
+ }
+ }
+
+ // set row properties in the current sheet
+ setRowModel( aModel );
+}
+
+bool SheetDataContext::importCell( const AttributeList& rAttribs )
+{
+ bool bValid = true;
+ const char* p = rAttribs.getChar(XML_r);
+
+ if (!p)
+ {
+ ++mnCol;
+ ScAddress aAddress( mnCol, mnRow, mnSheet );
+ bValid = mrAddressConv.checkCellAddress( aAddress, true );
+ maCellData.maCellAddr = aAddress;
+ }
+ else
+ {
+ bValid = mrAddressConv.convertToCellAddress(maCellData.maCellAddr, p, mnSheet, true);
+ mnCol = maCellData.maCellAddr.Col();
+ }
+
+ if( bValid )
+ {
+ maCellData.mnCellType = rAttribs.getToken( XML_t, XML_n );
+ maCellData.mnXfId = rAttribs.getInteger( XML_s, -1 );
+ maCellData.mbShowPhonetic = rAttribs.getBool( XML_ph, false );
+
+ // reset cell value, formula settings, and inline string
+ maCellValue.clear();
+ mxInlineStr.reset();
+ mbHasFormula = false;
+
+ // update used area of the sheet
+ extendUsedArea( maCellData.maCellAddr );
+ }
+ return bValid;
+}
+
+void SheetDataContext::importFormula( const AttributeList& rAttribs )
+{
+ mbHasFormula = true;
+ mbValidRange = mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, rAttribs.getString( XML_ref, OUString() ), mnSheet, true, true );
+
+ maFmlaData.mnFormulaType = rAttribs.getToken( XML_t, XML_normal );
+ maFmlaData.mnSharedId = rAttribs.getInteger( XML_si, -1 );
+
+ if( maFmlaData.mnFormulaType == XML_dataTable )
+ {
+ maTableData.maRef1 = rAttribs.getString( XML_r1, OUString() );
+ maTableData.maRef2 = rAttribs.getString( XML_r2, OUString() );
+ maTableData.mb2dTable = rAttribs.getBool( XML_dt2D, false );
+ maTableData.mbRowTable = rAttribs.getBool( XML_dtr, false );
+ maTableData.mbRef1Deleted = rAttribs.getBool( XML_del1, false );
+ maTableData.mbRef2Deleted = rAttribs.getBool( XML_del2, false );
+ }
+
+ maFormulaStr.clear();
+}
+
+void SheetDataContext::importRow( SequenceInputStream& rStrm )
+{
+ RowModel aModel;
+ sal_Int32 nSpanCount;
+ sal_uInt16 nHeight, nFlags1;
+ sal_uInt8 nFlags2;
+ maCurrPos.mnRow = rStrm.readInt32();
+ aModel.mnXfId = rStrm.readInt32();
+ nHeight = rStrm.readuInt16();
+ nFlags1 = rStrm.readuInt16();
+ nFlags2 = rStrm.readuChar();
+ nSpanCount = rStrm.readInt32();
+ maCurrPos.mnCol = 0;
+
+ mrAddressConv.checkRow( maCurrPos.mnRow, true);
+ // row index is 0-based in BIFF12, but RowModel expects 1-based
+ aModel.mnRow = maCurrPos.mnRow + 1;
+ // row height is in twips in BIFF12, convert to points
+ aModel.mfHeight = nHeight / 20.0;
+ aModel.mnLevel = extractValue< sal_Int32 >( nFlags1, 8, 3 );
+ aModel.mbCustomHeight = getFlag( nFlags1, BIFF12_ROW_CUSTOMHEIGHT );
+ aModel.mbCustomFormat = getFlag( nFlags1, BIFF12_ROW_CUSTOMFORMAT );
+ aModel.mbShowPhonetic = getFlag( nFlags2, BIFF12_ROW_SHOWPHONETIC );
+ aModel.mbHidden = getFlag( nFlags1, BIFF12_ROW_HIDDEN );
+ aModel.mbCollapsed = getFlag( nFlags1, BIFF12_ROW_COLLAPSED );
+ aModel.mbThickTop = getFlag( nFlags1, BIFF12_ROW_THICKTOP );
+ aModel.mbThickBottom = getFlag( nFlags1, BIFF12_ROW_THICKBOTTOM );
+
+ // read the column spans
+ sal_Int32 nMaxCol = mrAddressConv.getMaxApiAddress().Col();
+ for( sal_Int32 nSpanIdx = 0; (nSpanIdx < nSpanCount) && !rStrm.isEof(); ++nSpanIdx )
+ {
+ sal_Int32 nFirstCol, nLastCol;
+ nFirstCol = rStrm.readInt32();
+ const bool bValid1 = mrAddressConv.checkCol( nFirstCol, true);
+ nLastCol = rStrm.readInt32();
+ mrAddressConv.checkCol( nLastCol, true);
+ if (bValid1)
+ aModel.insertColSpan( ValueRange( nFirstCol, ::std::min( nLastCol, nMaxCol ) ) );
+ }
+
+ // set row properties in the current sheet
+ setRowModel( aModel );
+}
+
+bool SheetDataContext::readCellHeader( SequenceInputStream& rStrm, CellType eCellType )
+{
+ switch( eCellType )
+ {
+ case CELLTYPE_VALUE:
+ case CELLTYPE_FORMULA: maCurrPos.mnCol = rStrm.readInt32(); break;
+ case CELLTYPE_MULTI: ++maCurrPos.mnCol; break;
+ }
+
+ sal_uInt32 nXfId = rStrm.readuInt32();
+
+ bool bValidAddr = mrAddressConv.convertToCellAddress( maCellData.maCellAddr, maCurrPos, mnSheet, true );
+ maCellData.mnXfId = extractValue< sal_Int32 >( nXfId, 0, 24 );
+ maCellData.mbShowPhonetic = getFlag( nXfId, BIFF12_CELL_SHOWPHONETIC );
+
+ // update used area of the sheet
+ if( bValidAddr )
+ extendUsedArea( maCellData.maCellAddr );
+ return bValidAddr;
+}
+
+ApiTokenSequence SheetDataContext::readCellFormula( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 2 );
+ return mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::Cell, rStrm );
+}
+
+bool SheetDataContext::readFormulaRef( SequenceInputStream& rStrm )
+{
+ BinRange aRange;
+ rStrm >> aRange;
+ return mrAddressConv.convertToCellRange( maFmlaData.maFormulaRef, aRange, mnSheet, true, true );
+}
+
+void SheetDataContext::importCellBool( SequenceInputStream& rStrm, CellType eCellType )
+{
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_b;
+ bool bValue = rStrm.readuInt8() != 0;
+ if( eCellType == CELLTYPE_FORMULA )
+ mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
+ else
+ mrSheetData.setBooleanCell( maCellData, bValue );
+ }
+}
+
+void SheetDataContext::importCellBlank( SequenceInputStream& rStrm, CellType eCellType )
+{
+ OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellBlank - no formula cells supported" );
+ if( readCellHeader( rStrm, eCellType ) )
+ mrSheetData.setBlankCell( maCellData );
+}
+
+void SheetDataContext::importCellDouble( SequenceInputStream& rStrm, CellType eCellType )
+{
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_n;
+ double fValue = rStrm.readDouble();
+ if( eCellType == CELLTYPE_FORMULA )
+ mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
+ else
+ mrSheetData.setValueCell( maCellData, fValue );
+ }
+}
+
+void SheetDataContext::importCellError( SequenceInputStream& rStrm, CellType eCellType )
+{
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_e;
+ sal_uInt8 nErrorCode = rStrm.readuInt8();
+ if( eCellType == CELLTYPE_FORMULA )
+ mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
+ else
+ mrSheetData.setErrorCell( maCellData, nErrorCode );
+ }
+}
+
+void SheetDataContext::importCellRk( SequenceInputStream& rStrm, CellType eCellType )
+{
+ OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellRk - no formula cells supported" );
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_n;
+ mrSheetData.setValueCell( maCellData, BiffHelper::calcDoubleFromRk( rStrm.readInt32() ) );
+ }
+}
+
+void SheetDataContext::importCellRString( SequenceInputStream& rStrm, CellType eCellType )
+{
+ OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellRString - no formula cells supported" );
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_inlineStr;
+ RichStringRef xString = std::make_shared<RichString>( *this );
+ xString->importString( rStrm, true );
+ xString->finalizeImport();
+ mrSheetData.setStringCell( maCellData, xString );
+ }
+}
+
+void SheetDataContext::importCellSi( SequenceInputStream& rStrm, CellType eCellType )
+{
+ OSL_ENSURE( eCellType != CELLTYPE_FORMULA, "SheetDataContext::importCellSi - no formula cells supported" );
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_s;
+ mrSheetData.setStringCell( maCellData, rStrm.readInt32() );
+ }
+}
+
+void SheetDataContext::importCellString( SequenceInputStream& rStrm, CellType eCellType )
+{
+ if( readCellHeader( rStrm, eCellType ) )
+ {
+ maCellData.mnCellType = XML_inlineStr;
+ // always import the string, stream will point to formula afterwards, if existing
+ RichStringRef xString = std::make_shared<RichString>( *this );
+ xString->importString( rStrm, false );
+ xString->finalizeImport();
+ if( eCellType == CELLTYPE_FORMULA )
+ mrSheetData.setFormulaCell( maCellData, readCellFormula( rStrm ) );
+ else
+ mrSheetData.setStringCell( maCellData, xString );
+ }
+}
+
+void SheetDataContext::importArray( SequenceInputStream& rStrm )
+{
+ if( readFormulaRef( rStrm ) && maFmlaData.isValidArrayRef( maCellData.maCellAddr ) )
+ {
+ rStrm.skip( 1 );
+ ApiTokenSequence aTokens = mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::Array, rStrm );
+ mrSheetData.createArrayFormula( maFmlaData.maFormulaRef, aTokens );
+ }
+}
+
+void SheetDataContext::importDataTable( SequenceInputStream& rStrm )
+{
+ if( !readFormulaRef( rStrm ) )
+ return;
+
+ BinAddress aRef1, aRef2;
+ sal_uInt8 nFlags;
+ rStrm >> aRef1 >> aRef2;
+ nFlags = rStrm.readuChar();
+ maTableData.maRef1 = FormulaProcessorBase::generateAddress2dString( aRef1, false );
+ maTableData.maRef2 = FormulaProcessorBase::generateAddress2dString( aRef2, false );
+ maTableData.mbRowTable = getFlag( nFlags, BIFF12_DATATABLE_ROW );
+ maTableData.mb2dTable = getFlag( nFlags, BIFF12_DATATABLE_2D );
+ maTableData.mbRef1Deleted = getFlag( nFlags, BIFF12_DATATABLE_REF1DEL );
+ maTableData.mbRef2Deleted = getFlag( nFlags, BIFF12_DATATABLE_REF2DEL );
+ mrSheetData.createTableOperation( maFmlaData.maFormulaRef, maTableData );
+}
+
+void SheetDataContext::importSharedFmla( SequenceInputStream& rStrm )
+{
+ if( readFormulaRef( rStrm ) && maFmlaData.isValidSharedRef( maCellData.maCellAddr ) )
+ {
+ ApiTokenSequence aTokens = mxFormulaParser->importFormula( maCellData.maCellAddr, FormulaType::SharedFormula, rStrm );
+ mrSheetData.createSharedFormula( maCellData.maCellAddr, aTokens );
+ }
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/stylesbuffer.cxx b/sc/source/filter/oox/stylesbuffer.cxx
new file mode 100644
index 000000000..fd4401446
--- /dev/null
+++ b/sc/source/filter/oox/stylesbuffer.cxx
@@ -0,0 +1,3064 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stylesbuffer.hxx>
+
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/awt/FontFamily.hpp>
+#include <com/sun/star/awt/FontPitch.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontType.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/awt/XFont2.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/table/BorderLineStyle.hpp>
+#include <com/sun/star/table/CellVertJustify2.hpp>
+#include <com/sun/star/table/CellJustifyMethod.hpp>
+#include <editeng/justifyitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <tools/fontenum.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/unohelp.hxx>
+#include <rtl/tencinfo.h>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertymap.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <themebuffer.hxx>
+#include <unitconverter.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <docpool.hxx>
+#include <ftools.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <documentimport.hxx>
+#include <patattr.hxx>
+#include <stlsheet.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+
+
+namespace {
+
+// OOXML constants ------------------------------------------------------------
+
+// OOXML predefined color indexes (also used in BIFF3-BIFF8)
+const sal_Int32 OOX_COLOR_USEROFFSET = 0; /// First user defined color in palette (OOXML/BIFF12).
+
+// OOXML font family (also used in BIFF)
+const sal_Int32 OOX_FONTFAMILY_NONE = 0;
+const sal_Int32 OOX_FONTFAMILY_ROMAN = 1;
+const sal_Int32 OOX_FONTFAMILY_SWISS = 2;
+const sal_Int32 OOX_FONTFAMILY_MODERN = 3;
+const sal_Int32 OOX_FONTFAMILY_SCRIPT = 4;
+const sal_Int32 OOX_FONTFAMILY_DECORATIVE = 5;
+
+// OOXML cell text direction (also used in BIFF)
+const sal_Int32 OOX_XF_TEXTDIR_CONTEXT = 0;
+const sal_Int32 OOX_XF_TEXTDIR_LTR = 1;
+const sal_Int32 OOX_XF_TEXTDIR_RTL = 2;
+
+// OOXML cell rotation (also used in BIFF)
+const sal_Int32 OOX_XF_ROTATION_NONE = 0;
+const sal_Int32 OOX_XF_ROTATION_STACKED = 255;
+
+// OOXML cell indentation
+const sal_Int32 OOX_XF_INDENT_NONE = 0;
+
+// OOXML built-in cell styles (also used in BIFF)
+const sal_Int32 OOX_STYLE_NORMAL = 0; /// Default cell style.
+const sal_Int32 OOX_STYLE_ROWLEVEL = 1; /// RowLevel_x cell style.
+const sal_Int32 OOX_STYLE_COLLEVEL = 2; /// ColLevel_x cell style.
+
+// BIFF12 constants -----------------------------------------------------------
+
+// BIFF12 color types
+const sal_uInt8 BIFF12_COLOR_AUTO = 0;
+const sal_uInt8 BIFF12_COLOR_INDEXED = 1;
+const sal_uInt8 BIFF12_COLOR_RGB = 2;
+const sal_uInt8 BIFF12_COLOR_THEME = 3;
+
+// BIFF12 diagonal borders
+const sal_uInt8 BIFF12_BORDER_DIAG_TLBR = 0x01; /// Top-left to bottom-right.
+const sal_uInt8 BIFF12_BORDER_DIAG_BLTR = 0x02; /// Bottom-left to top-right.
+
+// BIFF12 gradient fill
+const sal_Int32 BIFF12_FILL_GRADIENT = 40;
+
+// BIFF12 XF flags
+const sal_uInt32 BIFF12_XF_WRAPTEXT = 0x00400000;
+const sal_uInt32 BIFF12_XF_JUSTLASTLINE = 0x00800000;
+const sal_uInt32 BIFF12_XF_SHRINK = 0x01000000;
+const sal_uInt32 BIFF12_XF_LOCKED = 0x10000000;
+const sal_uInt32 BIFF12_XF_HIDDEN = 0x20000000;
+
+// BIFF12 XF attribute used flags
+const sal_uInt16 BIFF12_XF_NUMFMT_USED = 0x0001;
+const sal_uInt16 BIFF12_XF_FONT_USED = 0x0002;
+const sal_uInt16 BIFF12_XF_ALIGN_USED = 0x0004;
+const sal_uInt16 BIFF12_XF_BORDER_USED = 0x0008;
+const sal_uInt16 BIFF12_XF_AREA_USED = 0x0010;
+const sal_uInt16 BIFF12_XF_PROT_USED = 0x0020;
+
+// BIFF12 DXF constants
+const sal_uInt16 BIFF12_DXF_FILL_PATTERN = 0;
+const sal_uInt16 BIFF12_DXF_FILL_FGCOLOR = 1;
+const sal_uInt16 BIFF12_DXF_FILL_BGCOLOR = 2;
+const sal_uInt16 BIFF12_DXF_FILL_GRADIENT = 3;
+const sal_uInt16 BIFF12_DXF_FILL_STOP = 4;
+const sal_uInt16 BIFF12_DXF_FONT_COLOR = 5;
+const sal_uInt16 BIFF12_DXF_BORDER_TOP = 6;
+const sal_uInt16 BIFF12_DXF_BORDER_BOTTOM = 7;
+const sal_uInt16 BIFF12_DXF_BORDER_LEFT = 8;
+const sal_uInt16 BIFF12_DXF_BORDER_RIGHT = 9;
+const sal_uInt16 BIFF12_DXF_FONT_NAME = 24;
+const sal_uInt16 BIFF12_DXF_FONT_WEIGHT = 25;
+const sal_uInt16 BIFF12_DXF_FONT_UNDERLINE = 26;
+const sal_uInt16 BIFF12_DXF_FONT_ESCAPEMENT = 27;
+const sal_uInt16 BIFF12_DXF_FONT_ITALIC = 28;
+const sal_uInt16 BIFF12_DXF_FONT_STRIKE = 29;
+const sal_uInt16 BIFF12_DXF_FONT_OUTLINE = 30;
+const sal_uInt16 BIFF12_DXF_FONT_SHADOW = 31;
+const sal_uInt16 BIFF12_DXF_FONT_HEIGHT = 36;
+const sal_uInt16 BIFF12_DXF_FONT_SCHEME = 37;
+const sal_uInt16 BIFF12_DXF_NUMFMT_CODE = 38;
+const sal_uInt16 BIFF12_DXF_NUMFMT_ID = 41;
+
+// BIFF12 CELLSTYLE flags
+const sal_uInt16 BIFF12_CELLSTYLE_BUILTIN = 0x0001;
+const sal_uInt16 BIFF12_CELLSTYLE_HIDDEN = 0x0002;
+const sal_uInt16 BIFF12_CELLSTYLE_CUSTOM = 0x0004;
+
+// BIFF constants -------------------------------------------------------------
+
+// BIFF font flags, also used in BIFF12
+const sal_uInt16 BIFF_FONTFLAG_ITALIC = 0x0002;
+const sal_uInt16 BIFF_FONTFLAG_STRIKEOUT = 0x0008;
+const sal_uInt16 BIFF_FONTFLAG_OUTLINE = 0x0010;
+const sal_uInt16 BIFF_FONTFLAG_SHADOW = 0x0020;
+
+// BIFF font weight
+const sal_uInt16 BIFF_FONTWEIGHT_BOLD = 450;
+
+// BIFF font underline, also used in BIFF12
+const sal_uInt8 BIFF_FONTUNDERL_NONE = 0;
+const sal_uInt8 BIFF_FONTUNDERL_SINGLE = 1;
+const sal_uInt8 BIFF_FONTUNDERL_DOUBLE = 2;
+const sal_uInt8 BIFF_FONTUNDERL_SINGLE_ACC = 33;
+const sal_uInt8 BIFF_FONTUNDERL_DOUBLE_ACC = 34;
+
+::Color lclReadRgbColor( BinaryInputStream& rStrm )
+{
+ sal_uInt8 nR, nG, nB, nA;
+ nR = rStrm.readuChar();
+ nG = rStrm.readuChar();
+ nB = rStrm.readuChar();
+ nA = rStrm.readuChar();
+ sal_Int32 nValue = nA;
+ nValue <<= 8;
+ nValue |= nR;
+ nValue <<= 8;
+ nValue |= nG;
+ nValue <<= 8;
+ nValue |= nB;
+ return ::Color(ColorTransparency, nValue);
+}
+
+} // namespace
+
+ExcelGraphicHelper::ExcelGraphicHelper( const WorkbookHelper& rHelper ) :
+ GraphicHelper( rHelper.getBaseFilter().getComponentContext(), rHelper.getBaseFilter().getTargetFrame(), rHelper.getBaseFilter().getStorage() ),
+ WorkbookHelper( rHelper )
+{
+}
+
+::Color ExcelGraphicHelper::getSchemeColor( sal_Int32 nToken ) const
+{
+ return getTheme().getColorByToken( nToken );
+}
+
+::Color ExcelGraphicHelper::getPaletteColor( sal_Int32 nPaletteIdx ) const
+{
+ return getStyles().getPaletteColor( nPaletteIdx );
+}
+
+void Color::setAuto()
+{
+ clearTransformations();
+ setSchemeClr( XML_phClr );
+}
+
+void Color::setRgb( ::Color nRgbValue, double fTint )
+{
+ clearTransformations();
+ setSrgbClr( sal_uInt32(nRgbValue) & 0xFFFFFF );
+ if( fTint != 0.0 ) addExcelTintTransformation( fTint );
+}
+
+void Color::setTheme( sal_Int32 nThemeIdx, double fTint )
+{
+ clearTransformations();
+ static const sal_Int32 spnColorTokens[] = {
+ XML_lt1, XML_dk1, XML_lt2, XML_dk2, XML_accent1, XML_accent2,
+ XML_accent3, XML_accent4, XML_accent5, XML_accent6, XML_hlink, XML_folHlink };
+ setSchemeClr( STATIC_ARRAY_SELECT( spnColorTokens, nThemeIdx, XML_TOKEN_INVALID ) );
+ if( fTint != 0.0 ) addExcelTintTransformation( fTint );
+}
+
+void Color::setIndexed( sal_Int32 nPaletteIdx, double fTint )
+{
+ clearTransformations();
+ setPaletteClr( nPaletteIdx );
+ if( fTint != 0.0 ) addExcelTintTransformation( fTint );
+}
+
+void Color::importColor( const AttributeList& rAttribs )
+{
+ // tdf#113271 The order of import color is very important in case of more than one color attributes was provided.
+ // This order (theme -> rgb -> indexed -> auto) is not documented and was gathered experimentally based on MS Excel 2013.
+ if( rAttribs.hasAttribute( XML_theme ) )
+ setTheme( rAttribs.getInteger( XML_theme, -1 ), rAttribs.getDouble( XML_tint, 0.0 ) );
+ else if( rAttribs.hasAttribute( XML_rgb ) )
+ setRgb( ::Color(ColorTransparency, rAttribs.getIntegerHex( XML_rgb, sal_Int32(API_RGB_TRANSPARENT) ) ), rAttribs.getDouble( XML_tint, 0.0 ) );
+ else if( rAttribs.hasAttribute( XML_indexed ) )
+ setIndexed( rAttribs.getInteger( XML_indexed, -1 ), rAttribs.getDouble( XML_tint, 0.0 ) );
+ else if( rAttribs.getBool( XML_auto, false ) )
+ setAuto();
+ else
+ {
+ OSL_FAIL( "Color::importColor - unknown color type" );
+ setAuto();
+ }
+}
+
+void Color::importColor( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nFlags, nIndex;
+ sal_Int16 nTint;
+ nFlags = rStrm.readuChar();
+ nIndex = rStrm.readuChar();
+ nTint = rStrm.readInt16();
+
+ // scale tint from signed 16-bit to double range -1.0 ... 1.0
+ double fTint = nTint;
+ if( nTint < 0 )
+ fTint /= -SAL_MIN_INT16;
+ else if( nTint > 0 )
+ fTint /= SAL_MAX_INT16;
+
+ switch( extractValue< sal_uInt8 >( nFlags, 1, 7 ) )
+ {
+ case BIFF12_COLOR_AUTO:
+ setAuto();
+ rStrm.skip( 4 );
+ break;
+ case BIFF12_COLOR_INDEXED:
+ setIndexed( nIndex, fTint );
+ rStrm.skip( 4 );
+ break;
+ case BIFF12_COLOR_RGB:
+ setRgb( lclReadRgbColor( rStrm ), fTint );
+ break;
+ case BIFF12_COLOR_THEME:
+ setTheme( nIndex, fTint );
+ rStrm.skip( 4 );
+ break;
+ default:
+ OSL_FAIL( "Color::importColor - unknown color type" );
+ setAuto();
+ rStrm.skip( 4 );
+ }
+}
+
+void Color::importColorId( SequenceInputStream& rStrm )
+{
+ setIndexed( rStrm.readInt32() );
+}
+
+SequenceInputStream& operator>>( SequenceInputStream& rStrm, Color& orColor )
+{
+ orColor.importColor( rStrm );
+ return rStrm;
+}
+
+namespace {
+
+/** Standard EGA colors, bright. */
+#define PALETTE_EGA_COLORS_LIGHT \
+ ::Color(0x000000), ::Color(0xFFFFFF), ::Color(0xFF0000), ::Color(0x00FF00), ::Color(0x0000FF), ::Color(0xFFFF00), ::Color(0xFF00FF), ::Color(0x00FFFF)
+/** Standard EGA colors), dark. */
+#define PALETTE_EGA_COLORS_DARK \
+ ::Color(0x800000), ::Color(0x008000), ::Color(0x000080), ::Color(0x808000), ::Color(0x800080), ::Color(0x008080), ::Color(0xC0C0C0), ::Color(0x808080)
+
+/** Default color table for BIFF8/BIFF12/OOXML. */
+const ::Color spnDefColors8[] =
+{
+/* 0 */ PALETTE_EGA_COLORS_LIGHT,
+/* 8 */ PALETTE_EGA_COLORS_LIGHT,
+/* 16 */ PALETTE_EGA_COLORS_DARK,
+/* 24 */ ::Color(0x9999FF), ::Color(0x993366), ::Color(0xFFFFCC), ::Color(0xCCFFFF), ::Color(0x660066), ::Color(0xFF8080), ::Color(0x0066CC), ::Color(0xCCCCFF),
+/* 32 */ ::Color(0x000080), ::Color(0xFF00FF), ::Color(0xFFFF00), ::Color(0x00FFFF), ::Color(0x800080), ::Color(0x800000), ::Color(0x008080), ::Color(0x0000FF),
+/* 40 */ ::Color(0x00CCFF), ::Color(0xCCFFFF), ::Color(0xCCFFCC), ::Color(0xFFFF99), ::Color(0x99CCFF), ::Color(0xFF99CC), ::Color(0xCC99FF), ::Color(0xFFCC99),
+/* 48 */ ::Color(0x3366FF), ::Color(0x33CCCC), ::Color(0x99CC00), ::Color(0xFFCC00), ::Color(0xFF9900), ::Color(0xFF6600), ::Color(0x666699), ::Color(0x969696),
+/* 56 */ ::Color(0x003366), ::Color(0x339966), ::Color(0x003300), ::Color(0x333300), ::Color(0x993300), ::Color(0x993366), ::Color(0x333399), ::Color(0x333333)
+};
+
+#undef PALETTE_EGA_COLORS_LIGHT
+#undef PALETTE_EGA_COLORS_DARK
+
+} // namespace
+
+ColorPalette::ColorPalette( const WorkbookHelper& rHelper )
+ : WorkbookHelper(rHelper)
+ , mnAppendIndex(0)
+{
+ // default colors
+ maColors.insert( maColors.begin(), spnDefColors8, spnDefColors8 + SAL_N_ELEMENTS(spnDefColors8) );
+ mnAppendIndex = OOX_COLOR_USEROFFSET;
+}
+
+void ColorPalette::importPaletteColor( const AttributeList& rAttribs )
+{
+ appendColor( ::Color(ColorTransparency, rAttribs.getIntegerHex( XML_rgb, sal_Int32(API_RGB_WHITE) ) ) );
+}
+
+void ColorPalette::importPaletteColor( SequenceInputStream& rStrm )
+{
+ ::Color nRgb = lclReadRgbColor( rStrm );
+ appendColor( nRgb );
+}
+
+::Color ColorPalette::getColor( sal_Int32 nPaletteIdx ) const
+{
+ ::Color nColor = API_RGB_TRANSPARENT;
+ if( const ::Color* pnPaletteColor = ContainerHelper::getVectorElement( maColors, nPaletteIdx ) )
+ {
+ nColor = *pnPaletteColor;
+ }
+ else switch( nPaletteIdx )
+ {
+ case OOX_COLOR_WINDOWTEXT3:
+ case OOX_COLOR_WINDOWTEXT:
+ case OOX_COLOR_CHWINDOWTEXT: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_windowText ); break;
+ case OOX_COLOR_WINDOWBACK3:
+ case OOX_COLOR_WINDOWBACK:
+ case OOX_COLOR_CHWINDOWBACK: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_window ); break;
+ case OOX_COLOR_BUTTONBACK: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_btnFace ); break;
+ case OOX_COLOR_CHBORDERAUTO: nColor = API_RGB_BLACK; /* really always black? */ break;
+ case OOX_COLOR_NOTEBACK: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_infoBk ); break;
+ case OOX_COLOR_NOTETEXT: nColor = getBaseFilter().getGraphicHelper().getSystemColor( XML_infoText ); break;
+ case OOX_COLOR_FONTAUTO: nColor = API_RGB_TRANSPARENT; break;
+ default: OSL_FAIL( "ColorPalette::getColor - unknown color index" );
+ }
+ return nColor;
+}
+
+void ColorPalette::appendColor( ::Color nRGBValue )
+{
+ if( mnAppendIndex < maColors.size() )
+ maColors[ mnAppendIndex ] = nRGBValue;
+ else
+ maColors.push_back( nRGBValue );
+ ++mnAppendIndex;
+}
+
+namespace {
+
+void lclSetFontName( ApiScriptFontName& rFontName, const FontDescriptor& rFontDesc, bool bHasGlyphs )
+{
+ if( bHasGlyphs )
+ {
+ rFontName.maName = rFontDesc.Name;
+ rFontName.mnFamily = rFontDesc.Family;
+ // API font descriptor contains rtl_TextEncoding constants
+ rFontName.mnTextEnc = rFontDesc.CharSet;
+ }
+ else
+ {
+ rFontName = ApiScriptFontName();
+ }
+}
+
+} // namespace
+
+FontModel::FontModel() :
+ mnScheme( XML_none ),
+ mnFamily( OOX_FONTFAMILY_NONE ),
+ mnCharSet( WINDOWS_CHARSET_DEFAULT ),
+ mfHeight( 0.0 ),
+ mnUnderline( XML_none ),
+ mnEscapement( XML_baseline ),
+ mbBold( false ),
+ mbItalic( false ),
+ mbStrikeout( false ),
+ mbOutline( false ),
+ mbShadow( false )
+{
+}
+
+void FontModel::setBiff12Scheme( sal_uInt8 nScheme )
+{
+ static const sal_Int32 spnSchemes[] = { XML_none, XML_major, XML_minor };
+ mnScheme = STATIC_ARRAY_SELECT( spnSchemes, nScheme, XML_none );
+}
+
+void FontModel::setBiffHeight( sal_uInt16 nHeight )
+{
+ mfHeight = nHeight / 20.0; // convert twips to points
+}
+
+void FontModel::setBiffWeight( sal_uInt16 nWeight )
+{
+ mbBold = nWeight >= BIFF_FONTWEIGHT_BOLD;
+}
+
+void FontModel::setBiffUnderline( sal_uInt16 nUnderline )
+{
+ switch( nUnderline )
+ {
+ case BIFF_FONTUNDERL_NONE: mnUnderline = XML_none; break;
+ case BIFF_FONTUNDERL_SINGLE: mnUnderline = XML_single; break;
+ case BIFF_FONTUNDERL_DOUBLE: mnUnderline = XML_double; break;
+ case BIFF_FONTUNDERL_SINGLE_ACC: mnUnderline = XML_singleAccounting; break;
+ case BIFF_FONTUNDERL_DOUBLE_ACC: mnUnderline = XML_doubleAccounting; break;
+ default: mnUnderline = XML_none;
+ }
+}
+
+void FontModel::setBiffEscapement( sal_uInt16 nEscapement )
+{
+ static const sal_Int32 spnEscapes[] = { XML_baseline, XML_superscript, XML_subscript };
+ mnEscapement = STATIC_ARRAY_SELECT( spnEscapes, nEscapement, XML_baseline );
+}
+
+ApiFontUsedFlags::ApiFontUsedFlags( bool bAllUsed ) :
+ mbNameUsed( bAllUsed ),
+ mbColorUsed( bAllUsed ),
+ mbSchemeUsed( bAllUsed ),
+ mbHeightUsed( bAllUsed ),
+ mbUnderlineUsed( bAllUsed ),
+ mbEscapementUsed( bAllUsed ),
+ mbWeightUsed( bAllUsed ),
+ mbPostureUsed( bAllUsed ),
+ mbStrikeoutUsed( bAllUsed ),
+ mbOutlineUsed( bAllUsed ),
+ mbShadowUsed( bAllUsed )
+{
+}
+
+ApiScriptFontName::ApiScriptFontName() :
+ mnFamily( css::awt::FontFamily::DONTKNOW ),
+ mnTextEnc( RTL_TEXTENCODING_DONTKNOW )
+{
+}
+
+ApiFontData::ApiFontData() :
+ maDesc(
+ "Calibri",
+ 220, // height 11 points
+ 0,
+ OUString(),
+ css::awt::FontFamily::DONTKNOW,
+ RTL_TEXTENCODING_DONTKNOW,
+ css::awt::FontPitch::DONTKNOW,
+ 100.0,
+ css::awt::FontWeight::NORMAL,
+ css::awt::FontSlant_NONE,
+ css::awt::FontUnderline::NONE,
+ css::awt::FontStrikeout::NONE,
+ 0.0,
+ false,
+ false,
+ css::awt::FontType::DONTKNOW ),
+ mnColor( API_RGB_TRANSPARENT ),
+ mnEscapement( API_ESCAPE_NONE ),
+ mnEscapeHeight( API_ESCAPEHEIGHT_NONE ),
+ mbOutline( false ),
+ mbShadow( false )
+{
+ maLatinFont.maName = maDesc.Name;
+}
+
+Font::Font( const WorkbookHelper& rHelper, bool bDxf ) :
+ WorkbookHelper( rHelper ),
+ maModel( rHelper.getTheme().getDefaultFontModel() ),
+ maUsedFlags( !bDxf ),
+ mbDxf( bDxf )
+{
+}
+
+Font::Font( const WorkbookHelper& rHelper, const FontModel& rModel ) :
+ WorkbookHelper( rHelper ),
+ maModel( rModel ),
+ maUsedFlags( true ),
+ mbDxf( false )
+{
+}
+
+void Font::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ const FontModel& rDefModel = getTheme().getDefaultFontModel();
+ switch( nElement )
+ {
+ case XLS_TOKEN( name ): // when in <font> element
+ case XLS_TOKEN( rFont ): // when in <rPr> element
+ if( rAttribs.hasAttribute( XML_val ) )
+ {
+ maModel.maName = rAttribs.getXString( XML_val, OUString() );
+ maUsedFlags.mbNameUsed = true;
+ }
+ break;
+ case XLS_TOKEN( scheme ):
+ maModel.mnScheme = rAttribs.getToken( XML_val, rDefModel.mnScheme );
+ break;
+ case XLS_TOKEN( family ):
+ maModel.mnFamily = rAttribs.getInteger( XML_val, rDefModel.mnFamily );
+ break;
+ case XLS_TOKEN( charset ):
+ maModel.mnCharSet = rAttribs.getInteger( XML_val, rDefModel.mnCharSet );
+ break;
+ case XLS_TOKEN( sz ):
+ maModel.mfHeight = rAttribs.getDouble( XML_val, rDefModel.mfHeight );
+ maUsedFlags.mbHeightUsed = true;
+ break;
+ case XLS_TOKEN( color ):
+ maModel.maColor.importColor( rAttribs );
+ maUsedFlags.mbColorUsed = true;
+ break;
+ case XLS_TOKEN( u ):
+ maModel.mnUnderline = rAttribs.getToken( XML_val, XML_single );
+ maUsedFlags.mbUnderlineUsed = true;
+ break;
+ case XLS_TOKEN( vertAlign ):
+ maModel.mnEscapement = rAttribs.getToken( XML_val, XML_baseline );
+ maUsedFlags.mbEscapementUsed = true;
+ break;
+ case XLS_TOKEN( b ):
+ maModel.mbBold = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbWeightUsed = true;
+ break;
+ case XLS_TOKEN( i ):
+ maModel.mbItalic = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbPostureUsed = true;
+ break;
+ case XLS_TOKEN( strike ):
+ maModel.mbStrikeout = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbStrikeoutUsed = true;
+ break;
+ case XLS_TOKEN( outline ):
+ maModel.mbOutline = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbOutlineUsed = true;
+ break;
+ case XLS_TOKEN( shadow ):
+ maModel.mbShadow = rAttribs.getBool( XML_val, true );
+ maUsedFlags.mbShadowUsed = true;
+ break;
+ }
+}
+
+void Font::importFont( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( mbDxf, "sc", "Font::importFont - unexpected conditional formatting flag" );
+
+ sal_uInt16 nHeight, nFlags, nWeight, nEscapement;
+ sal_uInt8 nUnderline, nFamily, nCharSet, nScheme;
+ nHeight = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+ nWeight = rStrm.readuInt16();
+ nEscapement = rStrm.readuInt16();
+ nUnderline = rStrm.readuChar();
+ nFamily = rStrm.readuChar();
+ nCharSet = rStrm.readuChar();
+ rStrm.skip( 1 );
+ rStrm >> maModel.maColor;
+ nScheme = rStrm.readuChar();
+ rStrm >> maModel.maName;
+
+ // equal constants in all BIFFs for weight, underline, and escapement
+ maModel.setBiff12Scheme( nScheme );
+ maModel.setBiffHeight( nHeight );
+ maModel.setBiffWeight( nWeight );
+ maModel.setBiffUnderline( nUnderline );
+ maModel.setBiffEscapement( nEscapement );
+ maModel.mnFamily = nFamily;
+ maModel.mnCharSet = nCharSet;
+ // equal flags in all BIFFs
+ maModel.mbItalic = getFlag( nFlags, BIFF_FONTFLAG_ITALIC );
+ maModel.mbStrikeout = getFlag( nFlags, BIFF_FONTFLAG_STRIKEOUT );
+ maModel.mbOutline = getFlag( nFlags, BIFF_FONTFLAG_OUTLINE );
+ maModel.mbShadow = getFlag( nFlags, BIFF_FONTFLAG_SHADOW );
+}
+
+void Font::importDxfName( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfName - missing conditional formatting flag" );
+ maModel.maName = BiffHelper::readString( rStrm, false );
+ maUsedFlags.mbColorUsed = true;
+}
+
+void Font::importDxfColor( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfColor - missing conditional formatting flag" );
+ rStrm >> maModel.maColor;
+ maUsedFlags.mbColorUsed = true;
+}
+
+void Font::importDxfScheme( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfScheme - missing conditional formatting flag" );
+ maModel.setBiff12Scheme( rStrm.readuInt8() );
+ maUsedFlags.mbSchemeUsed = true;
+}
+
+void Font::importDxfHeight( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfHeight - missing conditional formatting flag" );
+ maModel.setBiffHeight( rStrm.readuInt16() );
+ maUsedFlags.mbHeightUsed = true;
+}
+
+void Font::importDxfWeight( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfWeight - missing conditional formatting flag" );
+ maModel.setBiffWeight( rStrm.readuInt16() );
+ maUsedFlags.mbWeightUsed = true;
+}
+
+void Font::importDxfUnderline( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfUnderline - missing conditional formatting flag" );
+ maModel.setBiffUnderline( rStrm.readuInt16() );
+ maUsedFlags.mbUnderlineUsed = true;
+}
+
+void Font::importDxfEscapement( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfEscapement - missing conditional formatting flag" );
+ maModel.setBiffEscapement( rStrm.readuInt16() );
+ maUsedFlags.mbEscapementUsed = true;
+}
+
+void Font::importDxfFlag( sal_Int32 nElement, SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Font::importDxfFlag - missing conditional formatting flag" );
+ bool bFlag = rStrm.readuInt8() != 0;
+ switch( nElement )
+ {
+ case XML_i:
+ maModel.mbItalic = bFlag;
+ maUsedFlags.mbPostureUsed = true;
+ break;
+ case XML_strike:
+ maModel.mbStrikeout = bFlag;
+ maUsedFlags.mbStrikeoutUsed = true;
+ break;
+ case XML_outline:
+ maModel.mbOutline = bFlag;
+ maUsedFlags.mbOutlineUsed = true;
+ break;
+ case XML_shadow:
+ maModel.mbShadow = bFlag;
+ maUsedFlags.mbShadowUsed = true;
+ break;
+ default:
+ OSL_FAIL( "Font::importDxfFlag - unexpected element identifier" );
+ }
+}
+
+void Font::finalizeImport()
+{
+ // font name
+ maApiData.maDesc.Name = maModel.maName;
+
+ // font family
+ switch( maModel.mnFamily )
+ {
+ case OOX_FONTFAMILY_NONE: maApiData.maDesc.Family = css::awt::FontFamily::DONTKNOW; break;
+ case OOX_FONTFAMILY_ROMAN: maApiData.maDesc.Family = css::awt::FontFamily::ROMAN; break;
+ case OOX_FONTFAMILY_SWISS: maApiData.maDesc.Family = css::awt::FontFamily::SWISS; break;
+ case OOX_FONTFAMILY_MODERN: maApiData.maDesc.Family = css::awt::FontFamily::MODERN; break;
+ case OOX_FONTFAMILY_SCRIPT: maApiData.maDesc.Family = css::awt::FontFamily::SCRIPT; break;
+ case OOX_FONTFAMILY_DECORATIVE: maApiData.maDesc.Family = css::awt::FontFamily::DECORATIVE; break;
+ }
+
+ // character set (API font descriptor uses rtl_TextEncoding in member CharSet!)
+ if( (0 <= maModel.mnCharSet) && (maModel.mnCharSet <= SAL_MAX_UINT8) )
+ maApiData.maDesc.CharSet = static_cast< sal_Int16 >(
+ rtl_getTextEncodingFromWindowsCharset( static_cast< sal_uInt8 >( maModel.mnCharSet ) ) );
+
+ // color, height, weight, slant, strikeout, outline, shadow
+ maApiData.mnColor = maModel.maColor.getColor( getBaseFilter().getGraphicHelper() );
+ maApiData.maDesc.Height = static_cast< sal_Int16 >( maModel.mfHeight * 20.0 );
+ maApiData.maDesc.Weight = maModel.mbBold ? css::awt::FontWeight::BOLD : css::awt::FontWeight::NORMAL;
+ maApiData.maDesc.Slant = maModel.mbItalic ? css::awt::FontSlant_ITALIC : css::awt::FontSlant_NONE;
+ maApiData.maDesc.Strikeout = maModel.mbStrikeout ? css::awt::FontStrikeout::SINGLE : css::awt::FontStrikeout::NONE;
+ maApiData.mbOutline = maModel.mbOutline;
+ maApiData.mbShadow = maModel.mbShadow;
+
+ // underline
+ switch( maModel.mnUnderline )
+ {
+ case XML_double: maApiData.maDesc.Underline = css::awt::FontUnderline::DOUBLE; break;
+ case XML_doubleAccounting: maApiData.maDesc.Underline = css::awt::FontUnderline::DOUBLE; break;
+ case XML_none: maApiData.maDesc.Underline = css::awt::FontUnderline::NONE; break;
+ case XML_single: maApiData.maDesc.Underline = css::awt::FontUnderline::SINGLE; break;
+ case XML_singleAccounting: maApiData.maDesc.Underline = css::awt::FontUnderline::SINGLE; break;
+ }
+
+ // escapement
+ switch( maModel.mnEscapement )
+ {
+ case XML_baseline:
+ maApiData.mnEscapement = API_ESCAPE_NONE;
+ maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_NONE;
+ break;
+ case XML_superscript:
+ maApiData.mnEscapement = API_ESCAPE_SUPERSCRIPT;
+ maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_DEFAULT;
+ break;
+ case XML_subscript:
+ maApiData.mnEscapement = API_ESCAPE_SUBSCRIPT;
+ maApiData.mnEscapeHeight = API_ESCAPEHEIGHT_DEFAULT;
+ break;
+ }
+
+ // supported script types
+ if( !maUsedFlags.mbNameUsed )
+ return;
+
+ PropertySet aDocProps( getDocument() );
+ Reference< XDevice > xDevice( aDocProps.getAnyProperty( PROP_ReferenceDevice ), UNO_QUERY );
+ if( !xDevice.is() )
+ return;
+
+ Reference< XFont2 > xFont( xDevice->getFont( maApiData.maDesc ), UNO_QUERY );
+ if( !xFont.is() )
+ return;
+
+ // #91658# CJK fonts
+ bool bHasAsian =
+ xFont->hasGlyphs( OUString( u'\x3041' ) ) || // 3040-309F: Hiragana
+ xFont->hasGlyphs( OUString( u'\x30A1' ) ) || // 30A0-30FF: Katakana
+ xFont->hasGlyphs( OUString( u'\x3111' ) ) || // 3100-312F: Bopomofo
+ xFont->hasGlyphs( OUString( u'\x3131' ) ) || // 3130-318F: Hangul Compatibility Jamo
+ xFont->hasGlyphs( OUString( u'\x3301' ) ) || // 3300-33FF: CJK Compatibility
+ xFont->hasGlyphs( OUString( u'\x3401' ) ) || // 3400-4DBF: CJK Unified Ideographs Extension A
+ xFont->hasGlyphs( OUString( u'\x4E01' ) ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFont->hasGlyphs( OUString( u'\x7E01' ) ) || // 4E00-9FFF: CJK Unified Ideographs
+ xFont->hasGlyphs( OUString( u'\xA001' ) ) || // A001-A48F: Yi Syllables
+ xFont->hasGlyphs( OUString( u'\xAC01' ) ) || // AC00-D7AF: Hangul Syllables
+ xFont->hasGlyphs( OUString( u'\xCC01' ) ) || // AC00-D7AF: Hangul Syllables
+ xFont->hasGlyphs( OUString( u'\xF901' ) ) || // F900-FAFF: CJK Compatibility Ideographs
+ xFont->hasGlyphs( OUString( u'\xFF71' ) ); // FF00-FFEF: Halfwidth/Fullwidth Forms
+ // #113783# CTL fonts
+ bool bHasCmplx =
+ xFont->hasGlyphs( OUString( u'\x05D1' ) ) || // 0590-05FF: Hebrew
+ xFont->hasGlyphs( OUString( u'\x0631' ) ) || // 0600-06FF: Arabic
+ xFont->hasGlyphs( OUString( u'\x0721' ) ) || // 0700-074F: Syriac
+ xFont->hasGlyphs( OUString( u'\x0911' ) ) || // 0900-0DFF: Indic scripts
+ xFont->hasGlyphs( OUString( u'\x0E01' ) ) || // 0E00-0E7F: Thai
+ xFont->hasGlyphs( OUString( u'\xFB21' ) ) || // FB1D-FB4F: Hebrew Presentation Forms
+ xFont->hasGlyphs( OUString( u'\xFB51' ) ) || // FB50-FDFF: Arabic Presentation Forms-A
+ xFont->hasGlyphs( OUString( u'\xFE71' ) ); // FE70-FEFF: Arabic Presentation Forms-B
+ // Western fonts
+ bool bHasLatin =
+ (!bHasAsian && !bHasCmplx) ||
+ xFont->hasGlyphs( OUString( 'A' ) );
+
+ lclSetFontName( maApiData.maLatinFont, maApiData.maDesc, bHasLatin );
+ lclSetFontName( maApiData.maAsianFont, maApiData.maDesc, bHasAsian );
+ lclSetFontName( maApiData.maCmplxFont, maApiData.maDesc, bHasCmplx );
+}
+
+bool Font::needsRichTextFormat() const
+{
+ return maApiData.mnEscapement != API_ESCAPE_NONE;
+}
+
+static ::FontFamily lcl_getFontFamily( sal_Int32 nFamily )
+{
+ ::FontFamily eScFamily = FAMILY_DONTKNOW;
+ switch( nFamily )
+ {
+ case css::awt::FontFamily::DONTKNOW:
+ eScFamily = FAMILY_DONTKNOW;
+ break;
+ case css::awt::FontFamily::ROMAN:
+ eScFamily = FAMILY_ROMAN;
+ break;
+ case css::awt::FontFamily::SWISS:
+ eScFamily = FAMILY_SWISS;
+ break;
+ case css::awt::FontFamily::MODERN:
+ eScFamily = FAMILY_MODERN;
+ break;
+ case css::awt::FontFamily::SCRIPT:
+ eScFamily = FAMILY_SCRIPT;
+ break;
+ case css::awt::FontFamily::DECORATIVE:
+ eScFamily = FAMILY_DECORATIVE;
+ break;
+ }
+ return eScFamily;
+}
+
+void Font::fillToItemSet( SfxItemSet& rItemSet, bool bEditEngineText, bool bSkipPoolDefs ) const
+{
+ if ( maUsedFlags.mbNameUsed )
+ {
+ if( !maApiData.maLatinFont.maName.isEmpty() )
+ {
+ rtl_TextEncoding eFontEnc = maApiData.maLatinFont.mnTextEnc;
+ // taken from binary importer
+ rtl_TextEncoding eTempTextEnc = (bEditEngineText && (eFontEnc == getTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+
+ SvxFontItem aFontItem( lcl_getFontFamily( maApiData.maLatinFont.mnFamily ), maApiData.maLatinFont.maName, OUString(),
+ PITCH_DONTKNOW, eTempTextEnc, ATTR_FONT );
+ ScfTools::PutItem( rItemSet, aFontItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTINFO) : ATTR_FONT, bSkipPoolDefs );
+ }
+ if( !maApiData.maAsianFont.maName.isEmpty() )
+ {
+ rtl_TextEncoding eFontEnc = maApiData.maAsianFont.mnTextEnc;
+ // taken from binary importer
+ rtl_TextEncoding eTempTextEnc = (bEditEngineText && (eFontEnc == getTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+ SvxFontItem aFontItem( lcl_getFontFamily( maApiData.maAsianFont.mnFamily ), maApiData.maAsianFont.maName, OUString(),
+ PITCH_DONTKNOW, eTempTextEnc, ATTR_FONT );
+ ScfTools::PutItem( rItemSet, aFontItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTINFO_CJK) : ATTR_CJK_FONT, bSkipPoolDefs );
+ }
+ if( !maApiData.maCmplxFont.maName.isEmpty() )
+ {
+ rtl_TextEncoding eFontEnc = maApiData.maCmplxFont.mnTextEnc;
+ // taken from binary importer
+ rtl_TextEncoding eTempTextEnc = (bEditEngineText && (eFontEnc == getTextEncoding())) ?
+ ScfTools::GetSystemTextEncoding() : eFontEnc;
+ SvxFontItem aFontItem( lcl_getFontFamily( maApiData.maCmplxFont.mnFamily ), maApiData.maCmplxFont.maName, OUString(),
+ PITCH_DONTKNOW, eTempTextEnc, ATTR_FONT );
+ ScfTools::PutItem( rItemSet, aFontItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTINFO_CTL) : ATTR_CTL_FONT, bSkipPoolDefs );
+ }
+ }
+ // font height
+ if( maUsedFlags.mbHeightUsed )
+ {
+ sal_Int32 nHeight = maApiData.maDesc.Height;
+ // do we use XclFontItemType::HeaderFooter ( or is it just relevant for the binary filter )
+ if( bEditEngineText/* && (eType != XclFontItemType::HeaderFooter) */) // do not convert header/footer height
+ nHeight = convertTwipToMm100(nHeight);
+ SvxFontHeightItem aHeightItem( nHeight, 100, ATTR_FONT_HEIGHT );
+ ScfTools::PutItem( rItemSet, aHeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTHEIGHT) : ATTR_FONT_HEIGHT, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aHeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTHEIGHT_CJK) : ATTR_CJK_FONT_HEIGHT, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aHeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_FONTHEIGHT_CTL) : ATTR_CTL_FONT_HEIGHT, bSkipPoolDefs );
+ }
+ // font weight
+ if( maUsedFlags.mbWeightUsed )
+ {
+ ::FontWeight fWeight = vcl::unohelper::ConvertFontWeight( maApiData.maDesc.Weight );
+ SvxWeightItem aWeightItem( fWeight, ATTR_FONT_WEIGHT );
+ ScfTools::PutItem( rItemSet, aWeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_WEIGHT) : ATTR_FONT_WEIGHT, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aWeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_WEIGHT_CTL) : ATTR_CTL_FONT_WEIGHT, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aWeightItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_WEIGHT_CJK) : ATTR_CJK_FONT_WEIGHT, bSkipPoolDefs );
+ }
+ // font posture
+ if( maUsedFlags.mbPostureUsed )
+ {
+ SvxPostureItem aPostItem( ( maApiData.maDesc.Slant == css::awt::FontSlant_ITALIC ) ? ITALIC_NORMAL : ITALIC_NONE, ATTR_FONT_POSTURE);
+ ScfTools::PutItem( rItemSet, aPostItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_ITALIC) : ATTR_FONT_POSTURE, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aPostItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_ITALIC_CJK) : ATTR_CJK_FONT_POSTURE, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aPostItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_ITALIC_CTL) : ATTR_CTL_FONT_POSTURE, bSkipPoolDefs );
+ }
+ // character color
+ if( maUsedFlags.mbColorUsed )
+ {
+ ScfTools::PutItem( rItemSet,SvxColorItem( maApiData.mnColor, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_COLOR) : ATTR_FONT_COLOR), bSkipPoolDefs );
+ }
+ // underline style
+ if( maUsedFlags.mbUnderlineUsed )
+ {
+ FontLineStyle eScUnderl;
+ if ( maApiData.maDesc.Underline == css::awt::FontUnderline::DOUBLE )
+ eScUnderl = LINESTYLE_DOUBLE;
+ else if ( maApiData.maDesc.Underline == css::awt::FontUnderline::SINGLE )
+ eScUnderl = LINESTYLE_SINGLE;
+ else
+ eScUnderl = LINESTYLE_NONE;
+ SvxUnderlineItem aUnderlItem( eScUnderl, ATTR_FONT_UNDERLINE );
+ ScfTools::PutItem( rItemSet, aUnderlItem, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_UNDERLINE) : ATTR_FONT_UNDERLINE, bSkipPoolDefs );
+ }
+ // strike out style
+ if( maUsedFlags.mbStrikeoutUsed )
+ {
+ ScfTools::PutItem( rItemSet, SvxCrossedOutItem( maModel.mbStrikeout ? STRIKEOUT_SINGLE : STRIKEOUT_NONE, bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_STRIKEOUT) : ATTR_FONT_CROSSEDOUT ), bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_STRIKEOUT) : ATTR_FONT_CROSSEDOUT, bSkipPoolDefs );
+ }
+
+ // outline style
+ if( maUsedFlags.mbOutlineUsed )
+ {
+ ScfTools::PutItem( rItemSet, SvxContourItem( maApiData.mbOutline, ATTR_FONT_CONTOUR ), bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_OUTLINE) : ATTR_FONT_CONTOUR, bSkipPoolDefs );
+ }
+
+ // shadow style
+ if( maUsedFlags.mbShadowUsed )
+ {
+ ScfTools::PutItem( rItemSet, SvxShadowedItem( maApiData.mbShadow, ATTR_FONT_SHADOWED ), bEditEngineText ? static_cast<sal_uInt16>(EE_CHAR_SHADOW) : ATTR_FONT_SHADOWED, bSkipPoolDefs );
+ }
+ if( !maUsedFlags.mbEscapementUsed )
+ return;
+
+ SvxEscapement eScEscapem = SvxEscapement::Off;
+ if ( maApiData.mnEscapement == API_ESCAPE_SUPERSCRIPT )
+ eScEscapem = SvxEscapement::Superscript;
+ else if ( maApiData.mnEscapement == API_ESCAPE_SUBSCRIPT )
+ eScEscapem = SvxEscapement::Subscript;
+ if( bEditEngineText )
+ {
+ // #TODO handle EscapementHeight
+ rItemSet.Put( SvxEscapementItem( eScEscapem, EE_CHAR_ESCAPEMENT ) );
+ }
+}
+
+void Font::writeToPropertyMap( PropertyMap& rPropMap ) const
+{
+ // font name properties
+ if( maUsedFlags.mbNameUsed )
+ {
+ if( !maApiData.maLatinFont.maName.isEmpty() )
+ {
+ rPropMap.setProperty( PROP_CharFontName, maApiData.maLatinFont.maName);
+ rPropMap.setProperty( PROP_CharFontFamily, maApiData.maLatinFont.mnFamily);
+ rPropMap.setProperty( PROP_CharFontCharSet, maApiData.maLatinFont.mnTextEnc);
+ }
+ if( !maApiData.maAsianFont.maName.isEmpty() )
+ {
+ rPropMap.setProperty( PROP_CharFontNameAsian, maApiData.maAsianFont.maName);
+ rPropMap.setProperty( PROP_CharFontFamilyAsian, maApiData.maAsianFont.mnFamily);
+ rPropMap.setProperty( PROP_CharFontCharSetAsian, maApiData.maAsianFont.mnTextEnc);
+ }
+ if( !maApiData.maCmplxFont.maName.isEmpty() )
+ {
+ rPropMap.setProperty( PROP_CharFontNameComplex, maApiData.maCmplxFont.maName);
+ rPropMap.setProperty( PROP_CharFontFamilyComplex, maApiData.maCmplxFont.mnFamily);
+ rPropMap.setProperty( PROP_CharFontCharSetComplex, maApiData.maCmplxFont.mnTextEnc);
+ }
+ }
+ // font height
+ if( maUsedFlags.mbHeightUsed )
+ {
+ float fHeight = static_cast< float >( maApiData.maDesc.Height / 20.0 ); // twips to points
+ rPropMap.setProperty( PROP_CharHeight, fHeight);
+ rPropMap.setProperty( PROP_CharHeightAsian, fHeight);
+ rPropMap.setProperty( PROP_CharHeightComplex, fHeight);
+ }
+ // font weight
+ if( maUsedFlags.mbWeightUsed )
+ {
+ float fWeight = maApiData.maDesc.Weight;
+ rPropMap.setProperty( PROP_CharWeight, fWeight);
+ rPropMap.setProperty( PROP_CharWeightAsian, fWeight);
+ rPropMap.setProperty( PROP_CharWeightComplex, fWeight);
+ }
+ // font posture
+ if( maUsedFlags.mbPostureUsed )
+ {
+ rPropMap.setProperty( PROP_CharPosture, maApiData.maDesc.Slant);
+ rPropMap.setProperty( PROP_CharPostureAsian, maApiData.maDesc.Slant);
+ rPropMap.setProperty( PROP_CharPostureComplex, maApiData.maDesc.Slant);
+ }
+ // character color
+ if( maUsedFlags.mbColorUsed )
+ rPropMap.setProperty( PROP_CharColor, maApiData.mnColor);
+ // underline style
+ if( maUsedFlags.mbUnderlineUsed )
+ rPropMap.setProperty( PROP_CharUnderline, maApiData.maDesc.Underline);
+ // strike out style
+ if( maUsedFlags.mbStrikeoutUsed )
+ rPropMap.setProperty( PROP_CharStrikeout, maApiData.maDesc.Strikeout);
+ // outline style
+ if( maUsedFlags.mbOutlineUsed )
+ rPropMap.setProperty( PROP_CharContoured, maApiData.mbOutline);
+ // shadow style
+ if( maUsedFlags.mbShadowUsed )
+ rPropMap.setProperty( PROP_CharShadowed, maApiData.mbShadow);
+ // escapement
+ if( maUsedFlags.mbEscapementUsed )
+ {
+ rPropMap.setProperty( PROP_CharEscapement, maApiData.mnEscapement);
+ rPropMap.setProperty( PROP_CharEscapementHeight, maApiData.mnEscapeHeight);
+ }
+}
+
+void Font::writeToPropertySet( PropertySet& rPropSet ) const
+{
+ PropertyMap aPropMap;
+ writeToPropertyMap( aPropMap );
+ rPropSet.setProperties( aPropMap );
+}
+
+AlignmentModel::AlignmentModel() :
+ mnHorAlign( XML_general ),
+ mnVerAlign( XML_bottom ),
+ mnTextDir( OOX_XF_TEXTDIR_CONTEXT ),
+ mnRotation( OOX_XF_ROTATION_NONE ),
+ mnIndent( OOX_XF_INDENT_NONE ),
+ mbWrapText( false ),
+ mbShrink( false ),
+ mbJustLastLine( false )
+{
+}
+
+void AlignmentModel::setBiffHorAlign( sal_uInt8 nHorAlign )
+{
+ static const sal_Int32 spnHorAligns[] = {
+ XML_general, XML_left, XML_center, XML_right,
+ XML_fill, XML_justify, XML_centerContinuous, XML_distributed };
+ mnHorAlign = STATIC_ARRAY_SELECT( spnHorAligns, nHorAlign, XML_general );
+}
+
+void AlignmentModel::setBiffVerAlign( sal_uInt8 nVerAlign )
+{
+ static const sal_Int32 spnVerAligns[] = {
+ XML_top, XML_center, XML_bottom, XML_justify, XML_distributed };
+ mnVerAlign = STATIC_ARRAY_SELECT( spnVerAligns, nVerAlign, XML_bottom );
+}
+
+ApiAlignmentData::ApiAlignmentData() :
+ meHorJustify( css::table::CellHoriJustify_STANDARD ),
+ mnHorJustifyMethod( css::table::CellJustifyMethod::AUTO ),
+ mnVerJustify( css::table::CellVertJustify2::STANDARD ),
+ mnVerJustifyMethod( css::table::CellJustifyMethod::AUTO ),
+ meOrientation( css::table::CellOrientation_STANDARD ),
+ mnRotation( 0 ),
+ mnWritingMode( css::text::WritingMode2::PAGE ),
+ mnIndent( 0 ),
+ mbWrapText( false ),
+ mbShrink( false )
+{
+}
+
+bool operator==( const ApiAlignmentData& rLeft, const ApiAlignmentData& rRight )
+{
+ return
+ (rLeft.meHorJustify == rRight.meHorJustify) &&
+ (rLeft.mnHorJustifyMethod == rRight.mnHorJustifyMethod) &&
+ (rLeft.mnVerJustify == rRight.mnVerJustify) &&
+ (rLeft.mnVerJustifyMethod == rRight.mnVerJustifyMethod) &&
+ (rLeft.meOrientation == rRight.meOrientation) &&
+ (rLeft.mnRotation == rRight.mnRotation) &&
+ (rLeft.mnWritingMode == rRight.mnWritingMode) &&
+ (rLeft.mnIndent == rRight.mnIndent) &&
+ (rLeft.mbWrapText == rRight.mbWrapText) &&
+ (rLeft.mbShrink == rRight.mbShrink);
+}
+
+Alignment::Alignment( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void Alignment::importAlignment( const AttributeList& rAttribs )
+{
+ maModel.mnVerAlign = rAttribs.getToken( XML_vertical, XML_bottom );
+ maModel.mnTextDir = rAttribs.getInteger( XML_readingOrder, OOX_XF_TEXTDIR_CONTEXT );
+ maModel.mnRotation = rAttribs.getInteger( XML_textRotation, OOX_XF_ROTATION_NONE );
+ sal_Int32 nDefaultHorAlign = XML_general;
+ if (maModel.mnRotation != OOX_XF_ROTATION_NONE)
+ {
+ if (maModel.mnRotation < 90 || maModel.mnRotation == 180)
+ {
+ nDefaultHorAlign = XML_left;
+ }
+ else
+ {
+ nDefaultHorAlign = XML_right;
+ }
+ }
+ maModel.mnHorAlign = rAttribs.getToken( XML_horizontal, nDefaultHorAlign );
+ maModel.mnIndent = rAttribs.getInteger( XML_indent, OOX_XF_INDENT_NONE );
+ maModel.mbWrapText = rAttribs.getBool( XML_wrapText, false );
+ maModel.mbShrink = rAttribs.getBool( XML_shrinkToFit, false );
+ maModel.mbJustLastLine = rAttribs.getBool( XML_justifyLastLine, false );
+}
+
+void Alignment::setBiff12Data( sal_uInt32 nFlags )
+{
+ maModel.setBiffHorAlign( extractValue< sal_uInt8 >( nFlags, 16, 3 ) );
+ maModel.setBiffVerAlign( extractValue< sal_uInt8 >( nFlags, 19, 3 ) );
+ maModel.mnTextDir = extractValue< sal_Int32 >( nFlags, 26, 2 );
+ maModel.mnRotation = extractValue< sal_Int32 >( nFlags, 0, 8 );
+ maModel.mnIndent = extractValue< sal_uInt8 >( nFlags, 8, 8 );
+ maModel.mbWrapText = getFlag( nFlags, BIFF12_XF_WRAPTEXT );
+ maModel.mbShrink = getFlag( nFlags, BIFF12_XF_SHRINK );
+ maModel.mbJustLastLine = getFlag( nFlags, BIFF12_XF_JUSTLASTLINE );
+}
+
+void Alignment::finalizeImport()
+{
+ // horizontal alignment
+ switch( maModel.mnHorAlign )
+ {
+ case XML_center: maApiData.meHorJustify = css::table::CellHoriJustify_CENTER; break;
+ case XML_centerContinuous: maApiData.meHorJustify = css::table::CellHoriJustify_CENTER; break;
+ case XML_distributed: maApiData.meHorJustify = css::table::CellHoriJustify_BLOCK; break;
+ case XML_fill: maApiData.meHorJustify = css::table::CellHoriJustify_REPEAT; break;
+ case XML_general: maApiData.meHorJustify = css::table::CellHoriJustify_STANDARD; break;
+ case XML_justify: maApiData.meHorJustify = css::table::CellHoriJustify_BLOCK; break;
+ case XML_left: maApiData.meHorJustify = css::table::CellHoriJustify_LEFT; break;
+ case XML_right: maApiData.meHorJustify = css::table::CellHoriJustify_RIGHT; break;
+ }
+
+ if (maModel.mnHorAlign == XML_distributed)
+ maApiData.mnHorJustifyMethod = css::table::CellJustifyMethod::DISTRIBUTE;
+
+ // vertical alignment
+ switch( maModel.mnVerAlign )
+ {
+ case XML_bottom: maApiData.mnVerJustify = css::table::CellVertJustify2::BOTTOM; break;
+ case XML_center: maApiData.mnVerJustify = css::table::CellVertJustify2::CENTER; break;
+ case XML_distributed: maApiData.mnVerJustify = css::table::CellVertJustify2::BLOCK; break;
+ case XML_justify: maApiData.mnVerJustify = css::table::CellVertJustify2::BLOCK; break;
+ case XML_top: maApiData.mnVerJustify = css::table::CellVertJustify2::TOP; break;
+ }
+
+ if (maModel.mnVerAlign == XML_distributed)
+ maApiData.mnVerJustifyMethod = css::table::CellJustifyMethod::DISTRIBUTE;
+
+ /* indentation: expressed as number of blocks of 3 space characters in
+ OOXML. */
+ UnitConverter& rUnitConverter = getUnitConverter();
+ // Note: indents are stored in twips
+ sal_Int32 nIndent = rUnitConverter.scaleValue( 3.0 * maModel.mnIndent, Unit::Space, Unit::Twip);
+ if( (0 <= nIndent) && (nIndent <= SAL_MAX_INT16) )
+ maApiData.mnIndent = static_cast< sal_Int16 >( nIndent );
+
+ // complex text direction
+ switch( maModel.mnTextDir )
+ {
+ case OOX_XF_TEXTDIR_CONTEXT: maApiData.mnWritingMode = css::text::WritingMode2::PAGE; break;
+ case OOX_XF_TEXTDIR_LTR: maApiData.mnWritingMode = css::text::WritingMode2::LR_TB; break;
+ case OOX_XF_TEXTDIR_RTL: maApiData.mnWritingMode = css::text::WritingMode2::RL_TB; break;
+ }
+
+ // rotation: 0-90 means 0 to 90 degrees ccw, 91-180 means 1 to 90 degrees cw, 255 means stacked
+ sal_Int32 nOoxRot = maModel.mnRotation;
+ maApiData.mnRotation = Degree100(((0 <= nOoxRot) && (nOoxRot <= 90)) ?
+ (100 * nOoxRot) :
+ (((91 <= nOoxRot) && (nOoxRot <= 180)) ? (100 * (450 - nOoxRot)) : 0));
+
+ // "Orientation" property used for character stacking
+ maApiData.meOrientation = (nOoxRot == OOX_XF_ROTATION_STACKED) ?
+ css::table::CellOrientation_STACKED : css::table::CellOrientation_STANDARD;
+
+ // alignment flags (#i84960 automatic line break, if vertically justified/distributed)
+ maApiData.mbWrapText = maModel.mbWrapText || (maModel.mnVerAlign == XML_distributed) || (maModel.mnVerAlign == XML_justify);
+ maApiData.mbShrink = maModel.mbShrink;
+
+}
+
+::SvxCellVerJustify Alignment::GetScVerAlign() const
+{
+ ::SvxCellVerJustify nVert = ::SvxCellVerJustify::Standard;
+ switch ( maApiData.mnVerJustify )
+ {
+ case css::table::CellVertJustify2::BOTTOM:
+ nVert = ::SvxCellVerJustify::Bottom;
+ break;
+ case css::table::CellVertJustify2::CENTER:
+ nVert = ::SvxCellVerJustify::Center;
+ break;
+ case css::table::CellVertJustify2::TOP:
+ nVert = ::SvxCellVerJustify::Top;
+ break;
+ case css::table::CellVertJustify2::BLOCK:
+ nVert = ::SvxCellVerJustify::Block;
+ break;
+ case css::table::CellVertJustify2::STANDARD:
+ default:
+ nVert = ::SvxCellVerJustify::Standard;
+ break;
+ }
+ return nVert;
+}
+
+::SvxCellHorJustify Alignment::GetScHorAlign() const
+{
+ ::SvxCellHorJustify nHori = ::SvxCellHorJustify::Standard;
+ switch( maApiData.meHorJustify )
+ {
+ case css::table::CellHoriJustify_LEFT:
+ nHori = ::SvxCellHorJustify::Left;
+ break;
+ case css::table::CellHoriJustify_CENTER:
+ nHori = ::SvxCellHorJustify::Center;
+ break;
+ case css::table::CellHoriJustify_RIGHT:
+ nHori = ::SvxCellHorJustify::Right;
+ break;
+ case css::table::CellHoriJustify_BLOCK:
+ nHori = ::SvxCellHorJustify::Block;
+ break;
+ case css::table::CellHoriJustify_REPEAT:
+ nHori = ::SvxCellHorJustify::Repeat;
+ break;
+ case css::table::CellHoriJustify_STANDARD:
+ default:
+ nHori = ::SvxCellHorJustify::Standard;
+ break;
+ }
+ return nHori;
+}
+
+SvxFrameDirection Alignment::GetScFrameDir() const
+{
+ SvxFrameDirection eFrameDir = SvxFrameDirection::Environment;
+ switch( maApiData.mnWritingMode )
+ {
+ case css::text::WritingMode2::PAGE:
+ eFrameDir = SvxFrameDirection::Environment;
+ break;
+ case css::text::WritingMode2::LR_TB:
+ eFrameDir = SvxFrameDirection::Horizontal_LR_TB;
+ break;
+ case css::text::WritingMode2::RL_TB:
+ eFrameDir = SvxFrameDirection::Horizontal_RL_TB;
+ break;
+ default:
+ OSL_FAIL( "GetScFrameDir - unknown CTL text direction" );
+ }
+ return eFrameDir;
+}
+
+void Alignment::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ // horizontal alignment
+ ScfTools::PutItem( rItemSet, SvxHorJustifyItem( GetScHorAlign(), ATTR_HOR_JUSTIFY ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( ( maApiData.mnHorJustifyMethod == css::table::CellJustifyMethod::DISTRIBUTE ) ? ::SvxCellJustifyMethod::Distribute : ::SvxCellJustifyMethod::Auto, ATTR_HOR_JUSTIFY_METHOD ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, SvxVerJustifyItem( GetScVerAlign(), ATTR_VER_JUSTIFY ), bSkipPoolDefs );
+ // vertical alignment
+ ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( ( maApiData.mnVerJustifyMethod == css::table::CellJustifyMethod::DISTRIBUTE ) ? ::SvxCellJustifyMethod::Distribute : ::SvxCellJustifyMethod::Auto, ATTR_VER_JUSTIFY_METHOD ), bSkipPoolDefs );
+
+ // CTL text direction
+ ScfTools::PutItem( rItemSet, SvxFrameDirectionItem( GetScFrameDir(), ATTR_WRITINGDIR ), bSkipPoolDefs );
+ // set an angle in the range from -90 to 90 degrees
+ ScfTools::PutItem( rItemSet, ScRotateValueItem( maApiData.mnRotation ), bSkipPoolDefs );
+ // Orientation
+ ScfTools::PutItem( rItemSet, ScVerticalStackCell( maApiData.meOrientation == css::table::CellOrientation_STACKED ), bSkipPoolDefs );
+ // indent
+ ScfTools::PutItem( rItemSet, ScIndentItem( maApiData.mnIndent ), bSkipPoolDefs );
+ // line wrap
+ ScfTools::PutItem( rItemSet, ScLineBreakCell( maApiData.mbWrapText ), bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, ScShrinkToFitCell( maApiData.mbShrink ), bSkipPoolDefs );
+}
+
+ProtectionModel::ProtectionModel() :
+ mbLocked( true ), // default in Excel and Calc
+ mbHidden( false )
+{
+}
+
+ApiProtectionData::ApiProtectionData() :
+ maCellProt( true, false, false, false )
+{
+}
+
+bool operator==( const ApiProtectionData& rLeft, const ApiProtectionData& rRight )
+{
+ return
+ (rLeft.maCellProt.IsLocked == rRight.maCellProt.IsLocked) &&
+ (rLeft.maCellProt.IsFormulaHidden == rRight.maCellProt.IsFormulaHidden) &&
+ (rLeft.maCellProt.IsHidden == rRight.maCellProt.IsHidden) &&
+ (rLeft.maCellProt.IsPrintHidden == rRight.maCellProt.IsPrintHidden);
+}
+
+Protection::Protection( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void Protection::importProtection( const AttributeList& rAttribs )
+{
+ maModel.mbLocked = rAttribs.getBool( XML_locked, true );
+ maModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+}
+
+void Protection::setBiff12Data( sal_uInt32 nFlags )
+{
+ maModel.mbLocked = getFlag( nFlags, BIFF12_XF_LOCKED );
+ maModel.mbHidden = getFlag( nFlags, BIFF12_XF_HIDDEN );
+}
+
+void Protection::finalizeImport()
+{
+ maApiData.maCellProt.IsLocked = maModel.mbLocked;
+ maApiData.maCellProt.IsFormulaHidden = maModel.mbHidden;
+}
+
+void Protection::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ ScfTools::PutItem( rItemSet, ScProtectionAttr( maApiData.maCellProt.IsLocked, maApiData.maCellProt.IsFormulaHidden ), bSkipPoolDefs );
+}
+
+namespace {
+
+bool lcl_isBorder(const css::table::BorderLine& rBorder)
+{
+ return (rBorder.InnerLineWidth > 0) || (rBorder.OuterLineWidth > 0);
+}
+
+}
+
+BorderLineModel::BorderLineModel( bool bDxf ) :
+ mnStyle( XML_none ),
+ mbUsed( !bDxf )
+{
+ maColor.setIndexed( OOX_COLOR_WINDOWTEXT );
+}
+
+void BorderLineModel::setBiffStyle( sal_Int32 nLineStyle )
+{
+ static const sal_Int32 spnStyleIds[] = {
+ XML_none, XML_thin, XML_medium, XML_dashed,
+ XML_dotted, XML_thick, XML_double, XML_hair,
+ XML_mediumDashed, XML_dashDot, XML_mediumDashDot, XML_dashDotDot,
+ XML_mediumDashDotDot, XML_slantDashDot };
+ mnStyle = STATIC_ARRAY_SELECT( spnStyleIds, nLineStyle, XML_none );
+}
+
+BorderModel::BorderModel( bool bDxf ) :
+ maLeft( bDxf ),
+ maRight( bDxf ),
+ maTop( bDxf ),
+ maBottom( bDxf ),
+ maDiagonal( bDxf ),
+ mbDiagTLtoBR( false ),
+ mbDiagBLtoTR( false )
+{
+}
+
+ApiBorderData::ApiBorderData() :
+ mbBorderUsed( false ),
+ mbDiagUsed( false )
+{
+}
+
+bool ApiBorderData::hasAnyOuterBorder() const
+{
+ return
+ ( lcl_isBorder( maTop ) && maTop.OuterLineWidth > 0 ) ||
+ ( lcl_isBorder( maBottom ) && maBottom.OuterLineWidth > 0 ) ||
+ ( lcl_isBorder( maLeft ) && maLeft.OuterLineWidth > 0 ) ||
+ ( lcl_isBorder( maRight ) && maRight.OuterLineWidth > 0 );
+}
+
+namespace {
+
+void lclSetBorderLineWidth( BorderLine& rBorderLine,
+ sal_Int16 nOuter, sal_Int16 nDist = API_LINE_NONE, sal_Int16 nInner = API_LINE_NONE )
+{
+ rBorderLine.OuterLineWidth = nOuter;
+ rBorderLine.LineDistance = nDist;
+ rBorderLine.InnerLineWidth = nInner;
+}
+
+} // namespace
+
+Border::Border( const WorkbookHelper& rHelper, bool bDxf ) :
+ WorkbookHelper( rHelper ),
+ maModel( bDxf ),
+ mbDxf( bDxf )
+{
+}
+
+void Border::importBorder( const AttributeList& rAttribs )
+{
+ maModel.mbDiagTLtoBR = rAttribs.getBool( XML_diagonalDown, false );
+ maModel.mbDiagBLtoTR = rAttribs.getBool( XML_diagonalUp, false );
+}
+
+void Border::importStyle( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( BorderLineModel* pBorderLine = getBorderLine( nElement ) )
+ {
+ pBorderLine->mnStyle = rAttribs.getToken( XML_style, XML_none );
+ pBorderLine->mbUsed = true;
+ }
+}
+
+void Border::importColor( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( BorderLineModel* pBorderLine = getBorderLine( nElement ) )
+ pBorderLine->maColor.importColor( rAttribs );
+}
+
+void Border::importBorder( SequenceInputStream& rStrm )
+{
+ sal_uInt8 nFlags = rStrm.readuInt8();
+ maModel.mbDiagTLtoBR = getFlag( nFlags, BIFF12_BORDER_DIAG_TLBR );
+ maModel.mbDiagBLtoTR = getFlag( nFlags, BIFF12_BORDER_DIAG_BLTR );
+ maModel.maTop.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maTop.maColor;
+ maModel.maBottom.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maBottom.maColor;
+ maModel.maLeft.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maLeft.maColor;
+ maModel.maRight.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maRight.maColor;
+ maModel.maDiagonal.setBiffStyle( rStrm.readuInt16() );
+ rStrm >> maModel.maDiagonal.maColor;
+}
+
+void Border::importDxfBorder( sal_Int32 nElement, SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Border::importDxfBorder - missing conditional formatting flag" );
+ if( BorderLineModel* pBorderLine = getBorderLine( nElement ) )
+ {
+ sal_uInt16 nStyle;
+ rStrm >> pBorderLine->maColor;
+ nStyle = rStrm.readuInt16();
+ pBorderLine->setBiffStyle( nStyle );
+ pBorderLine->mbUsed = true;
+ }
+}
+
+void Border::finalizeImport( bool bRTL )
+{
+ if ( bRTL )
+ {
+ BorderLineModel aTmp = maModel.maLeft;
+ maModel.maLeft = maModel.maRight;
+ maModel.maRight = aTmp;
+ }
+ maApiData.mbBorderUsed = maModel.maLeft.mbUsed || maModel.maRight.mbUsed || maModel.maTop.mbUsed || maModel.maBottom.mbUsed;
+ maApiData.mbDiagUsed = maModel.maDiagonal.mbUsed;
+
+ convertBorderLine( maApiData.maLeft, maModel.maLeft );
+ convertBorderLine( maApiData.maRight, maModel.maRight );
+ convertBorderLine( maApiData.maTop, maModel.maTop );
+ convertBorderLine( maApiData.maBottom, maModel.maBottom );
+
+ if( maModel.mbDiagTLtoBR )
+ convertBorderLine( maApiData.maTLtoBR, maModel.maDiagonal );
+ if( maModel.mbDiagBLtoTR )
+ convertBorderLine( maApiData.maBLtoTR, maModel.maDiagonal );
+}
+
+void Border::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ if( maApiData.mbBorderUsed )
+ {
+ SvxBoxItem aBoxItem( ATTR_BORDER );
+ ::editeng::SvxBorderLine aLine;
+
+ if (SvxBoxItem::LineToSvxLine(maApiData.maLeft, aLine, false))
+ {
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ }
+ if (SvxBoxItem::LineToSvxLine(maApiData.maRight, aLine, false))
+ {
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ }
+ if (SvxBoxItem::LineToSvxLine(maApiData.maTop, aLine, false))
+ {
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::TOP );
+ }
+ if (SvxBoxItem::LineToSvxLine(maApiData.maBottom, aLine, false))
+ {
+ aBoxItem.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ }
+ ScfTools::PutItem( rItemSet, aBoxItem, bSkipPoolDefs );
+ }
+ if ( !maApiData.mbDiagUsed )
+ return;
+
+ SvxLineItem aTLBRItem( ATTR_BORDER_TLBR );
+ SvxLineItem aBLTRItem( ATTR_BORDER_BLTR );
+ ::editeng::SvxBorderLine aLine;
+ if (SvxBoxItem::LineToSvxLine(maApiData.maTLtoBR, aLine, false))
+ {
+ aTLBRItem.SetLine( &aLine );
+ }
+ if (SvxBoxItem::LineToSvxLine(maApiData.maBLtoTR, aLine, false))
+ {
+ aBLTRItem.SetLine( &aLine );
+ }
+ ScfTools::PutItem( rItemSet, aTLBRItem, bSkipPoolDefs );
+ ScfTools::PutItem( rItemSet, aBLTRItem, bSkipPoolDefs );
+}
+
+BorderLineModel* Border::getBorderLine( sal_Int32 nElement )
+{
+ switch( nElement )
+ {
+ case XLS_TOKEN( left ): return &maModel.maLeft;
+ case XLS_TOKEN( start ): return &maModel.maLeft;
+ case XLS_TOKEN( right ): return &maModel.maRight;
+ case XLS_TOKEN( end ): return &maModel.maRight;
+ case XLS_TOKEN( top ): return &maModel.maTop;
+ case XLS_TOKEN( bottom ): return &maModel.maBottom;
+ case XLS_TOKEN( diagonal ): return &maModel.maDiagonal;
+ }
+ return nullptr;
+}
+
+bool Border::convertBorderLine( BorderLine2& rBorderLine, const BorderLineModel& rModel )
+{
+ // Document: sc/qa/unit/data/README.cellborders
+
+ rBorderLine.Color = sal_Int32(rModel.maColor.getColor( getBaseFilter().getGraphicHelper(), API_RGB_BLACK ));
+ switch( rModel.mnStyle )
+ {
+ case XML_dashDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_THIN );
+ rBorderLine.LineStyle = BorderLineStyle::DASH_DOT;
+ break;
+ case XML_dashDotDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_THIN );
+ rBorderLine.LineStyle = BorderLineStyle::DASH_DOT_DOT;
+ break;
+ case XML_dashed:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_THIN );
+ rBorderLine.LineStyle = BorderLineStyle::FINE_DASHED;
+ break;
+ case XML_dotted:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_THIN );
+ rBorderLine.LineStyle = BorderLineStyle::DOTTED;
+ break;
+ case XML_double:
+ lclSetBorderLineWidth( rBorderLine, 10, 15, 10 );
+ rBorderLine.LineStyle = BorderLineStyle::DOUBLE_THIN;
+ break;
+ case XML_hair: lclSetBorderLineWidth( rBorderLine, API_LINE_HAIR ); break;
+ case XML_medium: lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM ); break;
+ case XML_mediumDashDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM );
+ rBorderLine.LineStyle = BorderLineStyle::DASH_DOT;
+ break;
+ case XML_mediumDashDotDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM );
+ rBorderLine.LineStyle = BorderLineStyle::DASH_DOT_DOT;
+ break;
+ case XML_mediumDashed:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM );
+ rBorderLine.LineStyle = BorderLineStyle::DASHED;
+ break;
+ case XML_none: lclSetBorderLineWidth( rBorderLine, API_LINE_NONE ); break;
+ case XML_slantDashDot:
+ lclSetBorderLineWidth( rBorderLine, API_LINE_MEDIUM );
+ rBorderLine.LineStyle = BorderLineStyle::FINE_DASHED;
+ break;
+ case XML_thick: lclSetBorderLineWidth( rBorderLine, API_LINE_THICK ); break;
+ case XML_thin: lclSetBorderLineWidth( rBorderLine, API_LINE_THIN ); break;
+ default: lclSetBorderLineWidth( rBorderLine, API_LINE_NONE ); break;
+ }
+ return rModel.mbUsed;
+}
+
+PatternFillModel::PatternFillModel( bool bDxf ) :
+ mnPattern( XML_none ),
+ mbPattColorUsed( !bDxf ),
+ mbFillColorUsed( !bDxf ),
+ mbPatternUsed( !bDxf )
+{
+ maPatternColor.setIndexed( OOX_COLOR_WINDOWTEXT );
+ maFilterPatternColor.setIndexed( OOX_COLOR_WINDOWTEXT );
+ maFillColor.setIndexed( OOX_COLOR_WINDOWBACK );
+}
+
+void PatternFillModel::setBiffPattern( sal_Int32 nPattern )
+{
+ static const sal_Int32 spnPatternIds[] = {
+ XML_none, XML_solid, XML_mediumGray, XML_darkGray,
+ XML_lightGray, XML_darkHorizontal, XML_darkVertical, XML_darkDown,
+ XML_darkUp, XML_darkGrid, XML_darkTrellis, XML_lightHorizontal,
+ XML_lightVertical, XML_lightDown, XML_lightUp, XML_lightGrid,
+ XML_lightTrellis, XML_gray125, XML_gray0625 };
+ mnPattern = STATIC_ARRAY_SELECT( spnPatternIds, nPattern, XML_none );
+}
+
+GradientFillModel::GradientFillModel() :
+ mnType( XML_linear ),
+ mfAngle( 0.0 ),
+ mfLeft( 0.0 ),
+ mfRight( 0.0 ),
+ mfTop( 0.0 ),
+ mfBottom( 0.0 )
+{
+}
+
+void GradientFillModel::readGradient( SequenceInputStream& rStrm )
+{
+ sal_Int32 nType;
+ nType = rStrm.readInt32();
+ mfAngle = rStrm.readDouble();
+ mfLeft = rStrm.readDouble();
+ mfRight = rStrm.readDouble();
+ mfTop = rStrm.readDouble();
+ mfBottom = rStrm.readDouble();
+ static const sal_Int32 spnTypes[] = { XML_linear, XML_path };
+ mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID );
+}
+
+void GradientFillModel::readGradientStop( SequenceInputStream& rStrm, bool bDxf )
+{
+ Color aColor;
+ double fPosition;
+ if( bDxf )
+ {
+ rStrm.skip( 2 );
+ fPosition = rStrm.readDouble();
+ rStrm >> aColor;
+ }
+ else
+ {
+ rStrm >> aColor;
+ fPosition = rStrm.readDouble();
+ }
+ if( !rStrm.isEof() && (fPosition >= 0.0) )
+ maColors[ fPosition ] = aColor;
+}
+
+ApiSolidFillData::ApiSolidFillData() :
+ mnColor( API_RGB_TRANSPARENT ),
+ mnFilterColor( API_RGB_TRANSPARENT ),
+ mbTransparent( true ),
+ mbUsed( false )
+{
+}
+
+namespace {
+
+sal_Int32 lclGetMixedColorComp( sal_Int32 nPatt, sal_Int32 nFill, sal_Int32 nAlpha )
+{
+ return ((nPatt - nFill) * nAlpha) / 0x80 + nFill;
+}
+
+::Color lclGetMixedColor( ::Color nPattColor, ::Color nFillColor, sal_Int32 nAlpha )
+{
+ return ::Color(
+ lclGetMixedColorComp( nPattColor.GetRed(), nFillColor.GetRed(), nAlpha ),
+ lclGetMixedColorComp( nPattColor.GetGreen(), nFillColor.GetGreen(), nAlpha ),
+ lclGetMixedColorComp( nPattColor.GetBlue(), nFillColor.GetBlue(), nAlpha ) );
+}
+
+} // namespace
+
+Fill::Fill( const WorkbookHelper& rHelper, bool bDxf ) :
+ WorkbookHelper( rHelper ),
+ mbDxf( bDxf )
+{
+}
+
+void Fill::importPatternFill( const AttributeList& rAttribs )
+{
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->mnPattern = rAttribs.getToken( XML_patternType, XML_none );
+ if( mbDxf )
+ mxPatternModel->mbPatternUsed = rAttribs.hasAttribute( XML_patternType );
+}
+
+void Fill::importFgColor( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( mxPatternModel, "Fill::importFgColor - missing pattern data" );
+ if( mxPatternModel )
+ {
+ mxPatternModel->maPatternColor.importColor( rAttribs );
+ mxPatternModel->mbPattColorUsed = true;
+ }
+}
+
+void Fill::importBgColor( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( mxPatternModel, "Fill::importBgColor - missing pattern data" );
+ if( mxPatternModel )
+ {
+ mxPatternModel->maFillColor.importColor( rAttribs );
+ mxPatternModel->mbFillColorUsed = true;
+ }
+}
+
+void Fill::importGradientFill( const AttributeList& rAttribs )
+{
+ mxGradientModel = std::make_shared<GradientFillModel>();
+ mxGradientModel->mnType = rAttribs.getToken( XML_type, XML_linear );
+ mxGradientModel->mfAngle = rAttribs.getDouble( XML_degree, 0.0 );
+ mxGradientModel->mfLeft = rAttribs.getDouble( XML_left, 0.0 );
+ mxGradientModel->mfRight = rAttribs.getDouble( XML_right, 0.0 );
+ mxGradientModel->mfTop = rAttribs.getDouble( XML_top, 0.0 );
+ mxGradientModel->mfBottom = rAttribs.getDouble( XML_bottom, 0.0 );
+}
+
+void Fill::importColor( const AttributeList& rAttribs, double fPosition )
+{
+ OSL_ENSURE( mxGradientModel, "Fill::importColor - missing gradient data" );
+ if( mxGradientModel && (fPosition >= 0.0) )
+ mxGradientModel->maColors[ fPosition ].importColor( rAttribs );
+}
+
+void Fill::importFill( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( mbDxf, "sc", "Fill::importFill - unexpected conditional formatting flag" );
+ sal_Int32 nPattern = rStrm.readInt32();
+ if( nPattern == BIFF12_FILL_GRADIENT )
+ {
+ mxGradientModel = std::make_shared<GradientFillModel>();
+ sal_Int32 nStopCount;
+ rStrm.skip( 16 );
+ mxGradientModel->readGradient( rStrm );
+ nStopCount = rStrm.readInt32();
+ for( sal_Int32 nStop = 0; (nStop < nStopCount) && !rStrm.isEof(); ++nStop )
+ mxGradientModel->readGradientStop( rStrm, false );
+ }
+ else
+ {
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->setBiffPattern( nPattern );
+ rStrm >> mxPatternModel->maPatternColor >> mxPatternModel->maFillColor;
+ }
+}
+
+void Fill::importDxfPattern( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfPattern - missing conditional formatting flag" );
+ if( !mxPatternModel )
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->setBiffPattern( rStrm.readuInt8() );
+ mxPatternModel->mbPatternUsed = true;
+}
+
+void Fill::importDxfFgColor( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfFgColor - missing conditional formatting flag" );
+ if( !mxPatternModel )
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->maPatternColor.importColor( rStrm );
+ mxPatternModel->mbPattColorUsed = true;
+}
+
+void Fill::importDxfBgColor( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfBgColor - missing conditional formatting flag" );
+ if( !mxPatternModel )
+ mxPatternModel = std::make_shared<PatternFillModel>( mbDxf );
+ mxPatternModel->maFillColor.importColor( rStrm );
+ mxPatternModel->mbFillColorUsed = true;
+}
+
+void Fill::importDxfGradient( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfGradient - missing conditional formatting flag" );
+ if( !mxGradientModel )
+ mxGradientModel = std::make_shared<GradientFillModel>();
+ mxGradientModel->readGradient( rStrm );
+}
+
+void Fill::importDxfStop( SequenceInputStream& rStrm )
+{
+ SAL_WARN_IF( !mbDxf, "sc", "Fill::importDxfStop - missing conditional formatting flag" );
+ if( !mxGradientModel )
+ mxGradientModel = std::make_shared<GradientFillModel>();
+ mxGradientModel->readGradientStop( rStrm, true );
+}
+
+void Fill::finalizeImport()
+{
+ const GraphicHelper& rGraphicHelper = getBaseFilter().getGraphicHelper();
+
+ if( mxPatternModel )
+ {
+ // finalize the OOXML data struct
+ PatternFillModel& rModel = *mxPatternModel;
+ if( mbDxf )
+ {
+ if( rModel.mbFillColorUsed && (!rModel.mbPatternUsed || (rModel.mnPattern == XML_solid)) )
+ {
+ rModel.maFilterPatternColor = rModel.maPatternColor;
+ rModel.maPatternColor = rModel.maFillColor;
+ rModel.mnPattern = XML_solid;
+ rModel.mbPattColorUsed = rModel.mbPatternUsed = true;
+ }
+ else if(
+ !rModel.mbFillColorUsed && !rModel.mbPattColorUsed &&
+ rModel.mbPatternUsed && rModel.mnPattern == XML_solid )
+ {
+ rModel.mbPatternUsed = false;
+ }
+ else
+ rModel.maFilterPatternColor = rModel.maPatternColor;
+ }
+
+ // convert to API fill settings
+ maApiData.mbUsed = rModel.mbPatternUsed;
+ if( rModel.mnPattern == XML_none )
+ {
+ maApiData.mnColor = API_RGB_TRANSPARENT;
+ maApiData.mbTransparent = true;
+ }
+ else
+ {
+ sal_Int32 nAlpha = 0x80;
+ switch( rModel.mnPattern )
+ {
+ case XML_darkDown: nAlpha = 0x40; break;
+ case XML_darkGray: nAlpha = 0x60; break;
+ case XML_darkGrid: nAlpha = 0x40; break;
+ case XML_darkHorizontal: nAlpha = 0x40; break;
+ case XML_darkTrellis: nAlpha = 0x60; break;
+ case XML_darkUp: nAlpha = 0x40; break;
+ case XML_darkVertical: nAlpha = 0x40; break;
+ case XML_gray0625: nAlpha = 0x08; break;
+ case XML_gray125: nAlpha = 0x10; break;
+ case XML_lightDown: nAlpha = 0x20; break;
+ case XML_lightGray: nAlpha = 0x20; break;
+ case XML_lightGrid: nAlpha = 0x38; break;
+ case XML_lightHorizontal: nAlpha = 0x20; break;
+ case XML_lightTrellis: nAlpha = 0x30; break;
+ case XML_lightUp: nAlpha = 0x20; break;
+ case XML_lightVertical: nAlpha = 0x20; break;
+ case XML_mediumGray: nAlpha = 0x40; break;
+ case XML_solid: nAlpha = 0x80; break;
+ }
+
+ ::Color nWinTextColor = rGraphicHelper.getSystemColor( XML_windowText );
+ ::Color nWinColor = rGraphicHelper.getSystemColor( XML_window );
+
+ if (!rModel.mbPattColorUsed)
+ {
+ rModel.maPatternColor.setAuto();
+ rModel.maFilterPatternColor.setAuto();
+ }
+ ::Color nPattColor = rModel.maPatternColor.getColor( rGraphicHelper, nWinTextColor );
+ ::Color nFiltPattColor = rModel.maFilterPatternColor.getColor( rGraphicHelper, nWinTextColor );
+
+ if( !rModel.mbFillColorUsed )
+ rModel.maFillColor.setAuto();
+ ::Color nFillColor = rModel.maFillColor.getColor( rGraphicHelper, nWinColor );
+
+ maApiData.mnColor = lclGetMixedColor( nPattColor, nFillColor, nAlpha );
+ maApiData.mnFilterColor = lclGetMixedColor( nFiltPattColor, nFillColor, nAlpha );
+ maApiData.mbTransparent = false;
+ }
+ }
+ else if( mxGradientModel && !mxGradientModel->maColors.empty() )
+ {
+ GradientFillModel& rModel = *mxGradientModel;
+ maApiData.mbUsed = true; // no support for differential attributes
+ GradientFillModel::ColorMap::const_iterator aIt = rModel.maColors.begin();
+ OSL_ENSURE( !aIt->second.isAuto(), "Fill::finalizeImport - automatic gradient color" );
+ maApiData.mnColor = aIt->second.getColor( rGraphicHelper, API_RGB_WHITE );
+ if( ++aIt != rModel.maColors.end() )
+ {
+ OSL_ENSURE( !aIt->second.isAuto(), "Fill::finalizeImport - automatic gradient color" );
+ ::Color nEndColor = aIt->second.getColor( rGraphicHelper, API_RGB_WHITE );
+ maApiData.mnColor = lclGetMixedColor( maApiData.mnColor, nEndColor, 0x40 );
+ maApiData.mbTransparent = false;
+ }
+ }
+}
+
+void Fill::fillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
+{
+ if( !maApiData.mbUsed )
+ return;
+
+ SvxBrushItem aBrushItem( ATTR_BACKGROUND );
+ if ( maApiData.mbTransparent )
+ {
+ aBrushItem.SetColor( COL_TRANSPARENT );
+ aBrushItem.SetFiltColor( COL_TRANSPARENT );
+ }
+ else
+ {
+ aBrushItem.SetColor( maApiData.mnColor );
+ aBrushItem.SetFiltColor( maApiData.mnFilterColor );
+ }
+ ScfTools::PutItem( rItemSet, aBrushItem, bSkipPoolDefs );
+}
+
+XfModel::XfModel() :
+ mnStyleXfId( -1 ),
+ mnFontId( -1 ),
+ mnNumFmtId( -1 ),
+ mnBorderId( -1 ),
+ mnFillId( -1 ),
+ mbCellXf( true ),
+ mbFontUsed( false ),
+ mbNumFmtUsed( false ),
+ mbAlignUsed( false ),
+ mbProtUsed( false ),
+ mbBorderUsed( false ),
+ mbAreaUsed( false )
+{
+}
+
+Xf::AttrList::AttrList(const ScPatternAttr* pDefPattern):
+ mbLatinNumFmtOnly(true),
+ mpDefPattern(pDefPattern)
+{}
+
+Xf::Xf( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnScNumFmt(0),
+ maAlignment( rHelper ),
+ maProtection( rHelper ),
+ meRotationRef( css::table::CellVertJustify2::STANDARD ),
+ mpStyleSheet( nullptr )
+{
+}
+
+void Xf::importXf( const AttributeList& rAttribs, bool bCellXf )
+{
+ maModel.mbCellXf = bCellXf;
+ // tdf#70565 Set proper default value to "0" of xfId attribute
+ // When xfId is not exist during .xlsx import
+ // it must have values set to "0".
+ // This doesn't impact spreadsheets created with MS Excel,
+ // as xfId attribute is always created during export to .xlsx
+ // Not setting "0" value is causing wrong .xlsx import by LibreOffice,
+ // for spreadsheets created by external applications (ex. SAP BI).
+ bool bApplyDefault;
+ if ( maModel.mbCellXf )
+ {
+ const sal_Int32 xfId = rAttribs.getInteger( XML_xfId, -1 );
+ // No xfId => no cellStyleXfs that could overwrite this on change, thus
+ // has to be applied.
+ bApplyDefault = (xfId < 0);
+ maModel.mnStyleXfId = std::max<sal_Int32>(0, xfId);
+ }
+ else
+ {
+ maModel.mnStyleXfId = rAttribs.getInteger( XML_xfId, -1 );
+ bApplyDefault = true;
+ }
+ maModel.mnFontId = rAttribs.getInteger( XML_fontId, -1 );
+ maModel.mnNumFmtId = rAttribs.getInteger( XML_numFmtId, -1 );
+ maModel.mnBorderId = rAttribs.getInteger( XML_borderId, -1 );
+ maModel.mnFillId = rAttribs.getInteger( XML_fillId, -1 );
+
+ // Default value of the apply*** attributes is dependent on context:
+ // true in cellStyleXfs element, false in cellXfs element...
+ // But it's not as easy as it sounds, for docs see
+ // https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/59922f8b-0edc-4e93-a822-9f22254aec46
+ // and apparently in reality cellStyleXfs xf and cellXfs xf are not merged
+ // at all, see
+ // https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/bcf98682-e8d3-44b8-b8f8-0bf696878ba1
+ // "b. The standard states that both the cell style xf records and cell xf
+ // records must be read to understand the full set of formatting applied to
+ // a cell."
+ // "In Office, only the cell xf record defines the formatting applied to a cell."
+
+ // So for reading documents this is all crap and effectively xf records
+ // apply their explicit properties by default unless denied.
+ // bApplyDefault==false only for cellXf xf with xfId.
+
+ // For cellXf xf, mbAlignUsed and mbProtUsed will be set when actually
+ // importing the element.
+ maModel.mbAlignUsed = rAttribs.getBool( XML_applyAlignment, bApplyDefault);
+ maModel.mbProtUsed = rAttribs.getBool( XML_applyProtection, bApplyDefault);
+
+ maModel.mbFontUsed = rAttribs.getBool( XML_applyFont, bApplyDefault || maModel.mnFontId > 0);
+ maModel.mbNumFmtUsed = rAttribs.getBool( XML_applyNumberFormat, bApplyDefault || maModel.mnNumFmtId > 0);
+ maModel.mbBorderUsed = rAttribs.getBool( XML_applyBorder, bApplyDefault || maModel.mnBorderId > 0);
+ maModel.mbAreaUsed = rAttribs.getBool( XML_applyFill, bApplyDefault || maModel.mnFillId > 0);
+}
+
+void Xf::importAlignment( const AttributeList& rAttribs )
+{
+ maAlignment.importAlignment( rAttribs );
+ if (maModel.mbCellXf)
+ maModel.mbAlignUsed = true;
+}
+
+void Xf::importProtection( const AttributeList& rAttribs )
+{
+ maProtection.importProtection( rAttribs );
+ if (maModel.mbCellXf)
+ maModel.mbProtUsed = true;
+}
+
+void Xf::importXf( SequenceInputStream& rStrm, bool bCellXf )
+{
+ maModel.mbCellXf = bCellXf;
+ maModel.mnStyleXfId = rStrm.readuInt16();
+ maModel.mnNumFmtId = rStrm.readuInt16();
+ maModel.mnFontId = rStrm.readuInt16();
+ maModel.mnFillId = rStrm.readuInt16();
+ maModel.mnBorderId = rStrm.readuInt16();
+ sal_uInt32 nFlags = rStrm.readuInt32();
+ maAlignment.setBiff12Data( nFlags );
+ maProtection.setBiff12Data( nFlags );
+ // used flags, see comments in Xf::setBiffUsedFlags()
+ sal_uInt16 nUsedFlags = rStrm.readuInt16();
+ maModel.mbFontUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_FONT_USED );
+ maModel.mbNumFmtUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_NUMFMT_USED );
+ maModel.mbAlignUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_ALIGN_USED );
+ maModel.mbProtUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_PROT_USED );
+ maModel.mbBorderUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_BORDER_USED );
+ maModel.mbAreaUsed = maModel.mbCellXf == getFlag( nUsedFlags, BIFF12_XF_AREA_USED );
+}
+
+void Xf::finalizeImport()
+{
+ // alignment and protection
+ maAlignment.finalizeImport();
+ maProtection.finalizeImport();
+}
+
+FontRef Xf::getFont() const
+{
+ return getStyles().getFont( maModel.mnFontId );
+}
+
+void Xf::applyPatternToAttrList( AttrList& rAttrs, SCROW nRow1, SCROW nRow2, sal_Int32 nNumFmtId )
+{
+ createPattern();
+ ScPatternAttr& rPat = *mpPattern;
+ ScDocumentImport& rDocImport = getDocImport();
+ ScDocument& rDoc = getScDocument();
+ if ( isCellXf() )
+ {
+ StylesBuffer& rStyles = getStyles();
+ rStyles.createCellStyle( maModel.mnStyleXfId );
+
+ mpStyleSheet = rStyles.getCellStyleSheet( maModel.mnStyleXfId );
+ if ( mpStyleSheet )
+ {
+ //rDoc.ApplySelectionStyle( static_cast<ScStyleSheet&>(*mpStyleSheet), rMarkData );
+ rPat.SetStyleSheet(mpStyleSheet, false);
+ }
+ else
+ {
+ ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
+ if (pStylePool)
+ {
+ ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(
+ pStylePool->Find(
+ ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ rPat.SetStyleSheet( pStyleSheet, false );
+ }
+ }
+ }
+ if ( nNumFmtId >= 0 )
+ {
+ ScPatternAttr aNumPat(rDoc.GetPool());
+ mnScNumFmt = getStyles().writeNumFmtToItemSet( aNumPat.GetItemSet(), nNumFmtId, false );
+ rPat.GetItemSet().Put(aNumPat.GetItemSet());
+ }
+
+ if (!rDocImport.isLatinScript(mnScNumFmt))
+ rAttrs.mbLatinNumFmtOnly = false;
+
+ if (!rPat.GetStyleName())
+ return;
+
+ // Check for a gap between the last entry and this one.
+ bool bHasGap = false;
+ if (rAttrs.maAttrs.empty() && nRow1 > 0)
+ // First attribute range doesn't start at row 0.
+ bHasGap = true;
+
+ if (!rAttrs.maAttrs.empty() && rAttrs.maAttrs.back().nEndRow + 1 < nRow1)
+ bHasGap = true;
+
+ if (bHasGap)
+ {
+ // Fill this gap with the default pattern.
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow1 - 1;
+ aEntry.pPattern = &rDoc.GetPool()->Put(*rAttrs.mpDefPattern);
+ rAttrs.maAttrs.push_back(aEntry);
+
+ // Check if the default pattern is 'General'.
+ if (!rDocImport.isLatinScript(*aEntry.pPattern))
+ rAttrs.mbLatinNumFmtOnly = false;
+ }
+
+ ScAttrEntry aEntry;
+ aEntry.nEndRow = nRow2;
+ aEntry.pPattern = &rDoc.GetPool()->Put(rPat);
+ rAttrs.maAttrs.push_back(aEntry);
+
+ if (!rDocImport.isLatinScript(*aEntry.pPattern))
+ rAttrs.mbLatinNumFmtOnly = false;
+}
+
+void Xf::writeToDoc( ScDocumentImport& rDoc, const ScRange& rRange )
+{
+ const StylesBuffer& rStyles = getStyles();
+
+ if (isCellXf())
+ {
+ // Cell style name.
+ OUString aStyleName = rStyles.createCellStyle(maModel.mnStyleXfId);
+
+ ScStyleSheet* pStyleSheet =
+ static_cast<ScStyleSheet*>(
+ rDoc.getDoc().GetStyleSheetPool()->Find(aStyleName, SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ {
+ rDoc.getDoc().ApplyStyleAreaTab(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab(),
+ *pStyleSheet);
+ }
+ }
+
+ const ScPatternAttr& rAttr = createPattern();
+ rDoc.getDoc().ApplyPatternAreaTab(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab(), rAttr);
+}
+
+const ::ScPatternAttr&
+Xf::createPattern( bool bSkipPoolDefs )
+{
+ if( mpPattern )
+ return *mpPattern;
+ mpPattern.reset( new ::ScPatternAttr( getScDocument().GetPool() ) );
+ SfxItemSet& rItemSet = mpPattern->GetItemSet();
+ /* Enables the used flags, if the formatting attributes differ from the
+ style XF. In cell XFs Excel uses the cell attributes, if they differ
+ from the parent style XF (even if the used flag is switched off).
+ #109899# ...or if the respective flag is not set in parent style XF.
+ */
+ StylesBuffer& rStyles = getStyles();
+
+ const Xf* pStyleXf = isCellXf() ? rStyles.getStyleXf( maModel.mnStyleXfId ).get() : nullptr;
+ if( pStyleXf && !mpStyleSheet )
+ {
+ rStyles.createCellStyle( maModel.mnStyleXfId );
+ mpStyleSheet = rStyles.getCellStyleSheet( maModel.mnStyleXfId );
+ OSL_ENSURE( mpStyleSheet, "Xf::createPattern - no parentStyle created" );
+
+ const XfModel& rStyleData = pStyleXf->maModel;
+ if( !maModel.mbFontUsed )
+ maModel.mbFontUsed = !rStyleData.mbFontUsed || (maModel.mnFontId != rStyleData.mnFontId);
+ if( !maModel.mbNumFmtUsed )
+ maModel.mbNumFmtUsed = !rStyleData.mbNumFmtUsed || (maModel.mnNumFmtId != rStyleData.mnNumFmtId);
+ if( !maModel.mbAlignUsed )
+ maModel.mbAlignUsed = !rStyleData.mbAlignUsed || !(maAlignment.getApiData() == pStyleXf->maAlignment.getApiData());
+ if( !maModel.mbProtUsed )
+ maModel.mbProtUsed = !rStyleData.mbProtUsed || !(maProtection.getApiData() == pStyleXf->maProtection.getApiData());
+ if( !maModel.mbBorderUsed )
+ maModel.mbBorderUsed = !rStyleData.mbBorderUsed || !StylesBuffer::equalBorders( maModel.mnBorderId, rStyleData.mnBorderId );
+ if( !maModel.mbAreaUsed )
+ maModel.mbAreaUsed = !rStyleData.mbAreaUsed || !StylesBuffer::equalFills( maModel.mnFillId, rStyleData.mnFillId );
+ }
+ // cell protection
+ if( maModel.mbProtUsed )
+ {
+ maProtection.fillToItemSet( rItemSet, bSkipPoolDefs );
+ }
+
+ // font
+ if( maModel.mbFontUsed )
+ {
+ rStyles.writeFontToItemSet( rItemSet, maModel.mnFontId, bSkipPoolDefs );
+ }
+
+ // value format
+ if( maModel.mbNumFmtUsed )
+ {
+ mnScNumFmt = rStyles.writeNumFmtToItemSet( rItemSet, maModel.mnNumFmtId, bSkipPoolDefs );
+ }
+ // alignment
+ if( maModel.mbAlignUsed )
+ {
+ maAlignment.fillToItemSet( rItemSet, bSkipPoolDefs );
+ }
+
+ // border
+ if( maModel.mbBorderUsed )
+ {
+ rStyles.writeBorderToItemSet( rItemSet, maModel.mnBorderId, bSkipPoolDefs );
+ }
+
+ // area
+ if( maModel.mbAreaUsed )
+ {
+ rStyles.writeFillToItemSet( rItemSet, maModel.mnFillId, bSkipPoolDefs );
+ }
+
+ /* #i38709# Decide which rotation reference mode to use. If any outer
+ border line of the cell is set (either explicitly or via cell style),
+ and the cell contents are rotated, set rotation reference to bottom of
+ cell. This causes the borders to be painted rotated with the text. */
+ if( const Alignment* pAlignment = maModel.mbAlignUsed ? &maAlignment : (pStyleXf ? &pStyleXf->maAlignment : nullptr) )
+ {
+ SvxRotateMode eRotateMode = SVX_ROTATE_MODE_STANDARD;
+ sal_Int32 nBorderId = maModel.mbBorderUsed ? maModel.mnBorderId : (pStyleXf ? pStyleXf->maModel.mnBorderId : -1);
+ if( const Border* pBorder = rStyles.getBorder( nBorderId ).get() )
+ {
+ if( (pAlignment->getApiData().mnRotation) && pBorder->getApiData().hasAnyOuterBorder() )
+ {
+ meRotationRef = css::table::CellVertJustify2::BOTTOM;
+ eRotateMode = SVX_ROTATE_MODE_BOTTOM;
+ }
+ }
+ ScfTools::PutItem( rItemSet, SvxRotateModeItem( eRotateMode, ATTR_ROTATE_MODE ), bSkipPoolDefs );
+ }
+
+ return *mpPattern;
+}
+
+Dxf::Dxf( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+FontRef const & Dxf::createFont( bool bAlwaysNew )
+{
+ if( bAlwaysNew || !mxFont )
+ mxFont = std::make_shared<Font>( *this, true );
+ return mxFont;
+}
+
+BorderRef const & Dxf::createBorder( bool bAlwaysNew )
+{
+ if( bAlwaysNew || !mxBorder )
+ mxBorder = std::make_shared<Border>( *this, true );
+ return mxBorder;
+}
+
+FillRef const & Dxf::createFill( bool bAlwaysNew )
+{
+ if( bAlwaysNew || !mxFill )
+ mxFill = std::make_shared<Fill>( *this, true );
+ return mxFill;
+}
+
+void Dxf::importNumFmt( const AttributeList& rAttribs )
+{
+ // don't propagate number formats defined in Dxf entries
+ // they can have the same id ( but different format codes ) as those
+ // defined globally earlier. We discard the id defined in XML_numFmtId
+ // and generate one ourselves ( this assumes that the normal numberformat
+ // import has already taken place )
+ sal_Int32 nNumFmtId = getStyles().nextFreeNumFmtId();
+ OUString aFmtCode = rAttribs.getXString( XML_formatCode, OUString() );
+ mxNumFmt = getStyles().createNumFmt( nNumFmtId, aFmtCode );
+}
+
+void Dxf::importDxf( SequenceInputStream& rStrm )
+{
+ sal_Int32 nNumFmtId = -1;
+ OUString aFmtCode;
+ sal_uInt16 nRecCount;
+ rStrm.skip( 4 ); // flags
+ nRecCount = rStrm.readuInt16();
+ for( sal_uInt16 nRec = 0; !rStrm.isEof() && (nRec < nRecCount); ++nRec )
+ {
+ sal_uInt16 nSubRecId, nSubRecSize;
+ sal_Int64 nRecEnd = rStrm.tell();
+ nSubRecId = rStrm.readuInt16();
+ nSubRecSize = rStrm.readuInt16();
+ nRecEnd += nSubRecSize;
+ switch( nSubRecId )
+ {
+ case BIFF12_DXF_FILL_PATTERN: createFill( false )->importDxfPattern( rStrm ); break;
+ case BIFF12_DXF_FILL_FGCOLOR: createFill( false )->importDxfFgColor( rStrm ); break;
+ case BIFF12_DXF_FILL_BGCOLOR: createFill( false )->importDxfBgColor( rStrm ); break;
+ case BIFF12_DXF_FILL_GRADIENT: createFill( false )->importDxfGradient( rStrm ); break;
+ case BIFF12_DXF_FILL_STOP: createFill( false )->importDxfStop( rStrm ); break;
+ case BIFF12_DXF_FONT_COLOR: createFont( false )->importDxfColor( rStrm ); break;
+ case BIFF12_DXF_BORDER_TOP: createBorder( false )->importDxfBorder( XLS_TOKEN( top ), rStrm ); break;
+ case BIFF12_DXF_BORDER_BOTTOM: createBorder( false )->importDxfBorder( XLS_TOKEN( bottom ), rStrm ); break;
+ case BIFF12_DXF_BORDER_LEFT: createBorder( false )->importDxfBorder( XLS_TOKEN( left ), rStrm ); break;
+ case BIFF12_DXF_BORDER_RIGHT: createBorder( false )->importDxfBorder( XLS_TOKEN( right ), rStrm ); break;
+ case BIFF12_DXF_FONT_NAME: createFont( false )->importDxfName( rStrm ); break;
+ case BIFF12_DXF_FONT_WEIGHT: createFont( false )->importDxfWeight( rStrm ); break;
+ case BIFF12_DXF_FONT_UNDERLINE: createFont( false )->importDxfUnderline( rStrm ); break;
+ case BIFF12_DXF_FONT_ESCAPEMENT: createFont( false )->importDxfEscapement( rStrm ); break;
+ case BIFF12_DXF_FONT_ITALIC: createFont( false )->importDxfFlag( XML_i, rStrm ); break;
+ case BIFF12_DXF_FONT_STRIKE: createFont( false )->importDxfFlag( XML_strike, rStrm ); break;
+ case BIFF12_DXF_FONT_OUTLINE: createFont( false )->importDxfFlag( XML_outline, rStrm ); break;
+ case BIFF12_DXF_FONT_SHADOW: createFont( false )->importDxfFlag( XML_shadow, rStrm ); break;
+ case BIFF12_DXF_FONT_HEIGHT: createFont( false )->importDxfHeight( rStrm ); break;
+ case BIFF12_DXF_FONT_SCHEME: createFont( false )->importDxfScheme( rStrm ); break;
+ case BIFF12_DXF_NUMFMT_CODE: aFmtCode = BiffHelper::readString( rStrm, false ); break;
+ case BIFF12_DXF_NUMFMT_ID: nNumFmtId = rStrm.readuInt16(); break;
+ }
+ rStrm.seek( nRecEnd );
+ }
+ OSL_ENSURE( !rStrm.isEof() && (rStrm.getRemaining() == 0), "Dxf::importDxf - unexpected remaining data" );
+ mxNumFmt = getStyles().createNumFmt( nNumFmtId, aFmtCode );
+}
+
+void Dxf::finalizeImport()
+{
+ if( mxFont )
+ mxFont->finalizeImport();
+ bool bRTL = false;
+ // number format already finalized by the number formats buffer
+ if( mxAlignment )
+ {
+ mxAlignment->finalizeImport();
+ // how do we detect RTL when text dir is OOX_XF_CONTEXT? ( seems you
+ // would need access to the cell content, which we don't here )
+ if ( mxAlignment->getModel().mnTextDir == OOX_XF_TEXTDIR_RTL )
+ bRTL = true;
+ }
+ if( mxProtection )
+ mxProtection->finalizeImport();
+ if( mxBorder )
+ {
+ mxBorder->finalizeImport( bRTL );
+ }
+ if( mxFill )
+ mxFill->finalizeImport();
+}
+
+void Dxf::fillToItemSet( SfxItemSet& rSet ) const
+{
+ if (mxFont)
+ mxFont->fillToItemSet(rSet, false);
+ if (mxNumFmt)
+ mxNumFmt->fillToItemSet(rSet);
+ if (mxAlignment)
+ mxAlignment->fillToItemSet(rSet);
+ if (mxProtection)
+ mxProtection->fillToItemSet(rSet);
+ if (mxBorder)
+ mxBorder->fillToItemSet(rSet);
+ if (mxFill)
+ mxFill->fillToItemSet(rSet);
+}
+
+namespace {
+
+const char* const sppcStyleNames[] =
+{
+ "Normal",
+ "RowLevel_", // outline level will be appended
+ "ColLevel_", // outline level will be appended
+ "Comma",
+ "Currency",
+ "Percent",
+ "Comma [0]", // new in BIFF4
+ "Currency [0]",
+ "Hyperlink", // new in BIFF8
+ "Followed Hyperlink",
+ "Note", // new in OOX
+ "Warning Text",
+ nullptr,
+ nullptr,
+ nullptr,
+ "Title",
+ "Heading 1",
+ "Heading 2",
+ "Heading 3",
+ "Heading 4",
+ "Input",
+ "Output",
+ "Calculation",
+ "Check Cell",
+ "Linked Cell",
+ "Total",
+ "Good",
+ "Bad",
+ "Neutral",
+ "Accent1",
+ "20% - Accent1",
+ "40% - Accent1",
+ "60% - Accent1",
+ "Accent2",
+ "20% - Accent2",
+ "40% - Accent2",
+ "60% - Accent2",
+ "Accent3",
+ "20% - Accent3",
+ "40% - Accent3",
+ "60% - Accent3",
+ "Accent4",
+ "20% - Accent4",
+ "40% - Accent4",
+ "60% - Accent4",
+ "Accent5",
+ "20% - Accent5",
+ "40% - Accent5",
+ "60% - Accent5",
+ "Accent6",
+ "20% - Accent6",
+ "40% - Accent6",
+ "60% - Accent6",
+ "Explanatory Text"
+};
+const sal_Int32 snStyleNamesCount = static_cast< sal_Int32 >( SAL_N_ELEMENTS( sppcStyleNames ) );
+
+OUString lclGetBuiltinStyleName( sal_Int32 nBuiltinId, std::u16string_view rName, sal_Int32 nLevel = 0 )
+{
+ OSL_ENSURE( (0 <= nBuiltinId) && (nBuiltinId < snStyleNamesCount), "lclGetBuiltinStyleName - unknown built-in style" );
+ OUStringBuffer aStyleName("Excel Built-in ");
+ if( (0 <= nBuiltinId) && (nBuiltinId < snStyleNamesCount) && (sppcStyleNames[ nBuiltinId ] != nullptr) )
+ aStyleName.appendAscii( sppcStyleNames[ nBuiltinId ] );
+ else if( !rName.empty() )
+ aStyleName.append( rName );
+ else
+ aStyleName.append( nBuiltinId );
+ if( (nBuiltinId == OOX_STYLE_ROWLEVEL) || (nBuiltinId == OOX_STYLE_COLLEVEL) )
+ aStyleName.append( nLevel );
+ return aStyleName.makeStringAndClear();
+}
+
+OUString lclCreateStyleName( const CellStyleModel& rModel )
+{
+ return rModel.mbBuiltin ? lclGetBuiltinStyleName( rModel.mnBuiltinId, rModel.maName, rModel.mnLevel ) : rModel.maName;
+}
+
+} // namespace
+
+CellStyleModel::CellStyleModel() :
+ mnXfId( -1 ),
+ mnBuiltinId( -1 ),
+ mnLevel( 0 ),
+ mbBuiltin( false ),
+ mbCustom( false ),
+ mbHidden( false )
+{
+}
+
+bool CellStyleModel::isBuiltin() const
+{
+ return mbBuiltin && (mnBuiltinId >= 0);
+}
+
+bool CellStyleModel::isDefaultStyle() const
+{
+ return mbBuiltin && (mnBuiltinId == OOX_STYLE_NORMAL);
+}
+
+CellStyle::CellStyle( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbCreated( false ),
+ mpStyleSheet( nullptr )
+{
+}
+
+void CellStyle::importCellStyle( const AttributeList& rAttribs )
+{
+ maModel.maName = rAttribs.getXString( XML_name, OUString() );
+ maModel.mnXfId = rAttribs.getInteger( XML_xfId, -1 );
+ maModel.mnBuiltinId = rAttribs.getInteger( XML_builtinId, -1 );
+ maModel.mnLevel = rAttribs.getInteger( XML_iLevel, 0 );
+ maModel.mbBuiltin = rAttribs.hasAttribute( XML_builtinId );
+ maModel.mbCustom = rAttribs.getBool( XML_customBuiltin, false );
+ maModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+}
+
+void CellStyle::importCellStyle( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags;
+ maModel.mnXfId = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ maModel.mnBuiltinId = rStrm.readInt8();
+ maModel.mnLevel = rStrm.readInt8();
+ rStrm >> maModel.maName;
+ maModel.mbBuiltin = getFlag( nFlags, BIFF12_CELLSTYLE_BUILTIN );
+ maModel.mbCustom = getFlag( nFlags, BIFF12_CELLSTYLE_CUSTOM );
+ maModel.mbHidden = getFlag( nFlags, BIFF12_CELLSTYLE_HIDDEN );
+}
+
+void CellStyle::createCellStyle()
+{
+
+ // #i1624# #i1768# ignore unnamed user styles
+ bool bDefStyle = maModel.isDefaultStyle();
+ if( !mbCreated )
+ {
+ if ( bDefStyle && maFinalName.isEmpty() )
+ maFinalName = ScResId( STR_STYLENAME_STANDARD );
+ mbCreated = maFinalName.isEmpty();
+ }
+
+ if( mbCreated || mpStyleSheet )
+ return;
+
+ bool bCreatePattern = false;
+ Xf* pXF = getStyles().getStyleXf( maModel.mnXfId ).get();
+ ::ScDocument& rDoc = getScDocument();
+
+ if( bDefStyle )
+ {
+ // use existing "Default" style sheet
+ mpStyleSheet = static_cast< ScStyleSheet* >( rDoc.GetStyleSheetPool()->Find(
+ ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) );
+ OSL_ENSURE( mpStyleSheet, "CellStyle::createStyle - Default style not found" );
+ bCreatePattern = true;
+ }
+ else
+ {
+ mpStyleSheet = static_cast< ScStyleSheet* >( rDoc.GetStyleSheetPool()->Find( maFinalName, SfxStyleFamily::Para ) );
+ if( !mpStyleSheet )
+ {
+ mpStyleSheet = &static_cast< ScStyleSheet& >( rDoc.GetStyleSheetPool()->Make( maFinalName, SfxStyleFamily::Para, SfxStyleSearchBits::UserDefined ) );
+ bCreatePattern = true;
+ }
+ }
+
+ // bDefStyle==true omits default pool items in CreatePattern()
+ if( bCreatePattern && mpStyleSheet && pXF )
+ mpStyleSheet->GetItemSet().Put( pXF->createPattern( bDefStyle ).GetItemSet() );
+}
+
+void CellStyle::finalizeImport( const OUString& rFinalName )
+{
+ maFinalName = rFinalName;
+ if( !maModel.isBuiltin() || maModel.mbCustom )
+ createCellStyle();
+}
+
+CellStyleBuffer::CellStyleBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+CellStyleRef CellStyleBuffer::importCellStyle( const AttributeList& rAttribs )
+{
+ CellStyleRef xCellStyle = std::make_shared<CellStyle>( *this );
+ xCellStyle->importCellStyle( rAttribs );
+ insertCellStyle( xCellStyle );
+ return xCellStyle;
+}
+
+CellStyleRef CellStyleBuffer::importCellStyle( SequenceInputStream& rStrm )
+{
+ CellStyleRef xCellStyle = std::make_shared<CellStyle>( *this );
+ xCellStyle->importCellStyle( rStrm );
+ insertCellStyle( xCellStyle );
+ return xCellStyle;
+}
+
+void CellStyleBuffer::finalizeImport()
+{
+ // calculate final names of all styles
+ typedef RefMap< OUString, CellStyle, IgnoreCaseCompare > CellStyleNameMap;
+ CellStyleNameMap aCellStyles;
+ CellStyleVector aConflictNameStyles;
+
+ /* First, reserve style names that are built-in in Calc. This causes that
+ imported cell styles get different unused names and thus do not try to
+ overwrite these built-in styles. */
+ try
+ {
+ // unfortunately, com.sun.star.style.StyleFamily does not implement XEnumerationAccess...
+ Reference< XIndexAccess > xStyleFamilyIA( getCellStyleFamily(), UNO_QUERY_THROW );
+ for( sal_Int32 nIndex = 0, nCount = xStyleFamilyIA->getCount(); nIndex < nCount; ++nIndex )
+ {
+ Reference< XStyle > xStyle( xStyleFamilyIA->getByIndex( nIndex ), UNO_QUERY_THROW );
+ if( !xStyle->isUserDefined() )
+ {
+ // create an empty entry by using ::std::map<>::operator[]
+ aCellStyles[ xStyle->getName() ];
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ /* Calculate names of built-in styles. Store styles with reserved names
+ in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maBuiltinStyles )
+ {
+ const CellStyleModel& rModel = rxStyle->getModel();
+ if (rModel.isDefaultStyle())
+ continue;
+
+ OUString aStyleName = lclCreateStyleName( rModel );
+ /* If a builtin style entry already exists,
+ we just stick with the last definition and ignore
+ the preceding ones. */
+ aCellStyles[ aStyleName ] = rxStyle;
+ }
+
+ /* Calculate names of user defined styles. Store styles with reserved
+ names in the aConflictNameStyles list. */
+ for( const auto& rxStyle : maUserStyles )
+ {
+ const CellStyleModel& rModel = rxStyle->getModel();
+ OUString aStyleName = lclCreateStyleName( rModel );
+ // #i1624# #i1768# ignore unnamed user styles
+ if( aStyleName.getLength() > 0 )
+ {
+ if( aCellStyles.find( aStyleName ) != aCellStyles.end() )
+ aConflictNameStyles.push_back( rxStyle );
+ else
+ aCellStyles[ aStyleName ] = rxStyle;
+ }
+ }
+
+ // find unused names for all styles with conflicting names
+ // having the index counter outside the loop prevents performance problems with opening some pathological documents (tdf#62095)
+ sal_Int32 nIndex = 0;
+ for( const auto& rxStyle : aConflictNameStyles )
+ {
+ const CellStyleModel& rModel = rxStyle->getModel();
+ OUString aStyleName = lclCreateStyleName( rModel );
+ OUString aUnusedName;
+ do
+ {
+ aUnusedName = aStyleName + OUStringChar(' ') + OUString::number( ++nIndex );
+ }
+ while( !aCellStyles.try_emplace( aUnusedName, rxStyle ).second );
+ }
+
+ // set final names and create user-defined and modified built-in cell styles
+ aCellStyles.forEachMemWithKey( &CellStyle::finalizeImport );
+}
+
+sal_Int32 CellStyleBuffer::getDefaultXfId() const
+{
+ return mxDefStyle ? mxDefStyle->getModel().mnXfId : -1;
+}
+
+OUString CellStyleBuffer::getDefaultStyleName() const
+{
+ return createCellStyle( mxDefStyle );
+}
+
+OUString CellStyleBuffer::createCellStyle( sal_Int32 nXfId ) const
+{
+ return createCellStyle( maStylesByXf.get( nXfId ) );
+}
+
+::ScStyleSheet* CellStyleBuffer::getCellStyleSheet( sal_Int32 nXfId ) const
+{
+ return getCellStyleSheet( maStylesByXf.get( nXfId ) );
+}
+
+// private --------------------------------------------------------------------
+
+void CellStyleBuffer::insertCellStyle( CellStyleRef const & xCellStyle )
+{
+ const CellStyleModel& rModel = xCellStyle->getModel();
+ if( rModel.mnXfId < 0 )
+ return;
+
+ // insert into the built-in map or user defined map
+ (rModel.isBuiltin() ? maBuiltinStyles : maUserStyles).push_back( xCellStyle );
+
+ // insert into the XF identifier map
+ OSL_ENSURE( maStylesByXf.count( rModel.mnXfId ) == 0, "CellStyleBuffer::insertCellStyle - multiple styles with equal XF identifier" );
+ maStylesByXf[ rModel.mnXfId ] = xCellStyle;
+
+ // remember default cell style
+ if( rModel.isDefaultStyle() )
+ mxDefStyle = xCellStyle;
+}
+
+::ScStyleSheet* CellStyleBuffer::getCellStyleSheet( const CellStyleRef& rxCellStyle )
+{
+ ::ScStyleSheet* pStyleSheet = nullptr;
+ if ( rxCellStyle )
+ pStyleSheet = rxCellStyle->getStyleSheet();
+ return pStyleSheet;
+}
+
+OUString CellStyleBuffer::createCellStyle( const CellStyleRef& rxCellStyle )
+{
+ if( rxCellStyle )
+ {
+ rxCellStyle->createCellStyle();
+ const OUString& rStyleName = rxCellStyle->getFinalStyleName();
+ if( !rStyleName.isEmpty() )
+ return rStyleName;
+ }
+ // on error: fallback to default style
+ return lclGetBuiltinStyleName( OOX_STYLE_NORMAL, u"" );
+}
+
+AutoFormatModel::AutoFormatModel() :
+ mnAutoFormatId( 0 ),
+ mbApplyNumFmt( false ),
+ mbApplyFont( false ),
+ mbApplyAlignment( false ),
+ mbApplyBorder( false ),
+ mbApplyFill( false ),
+ mbApplyProtection( false )
+{
+}
+
+StylesBuffer::StylesBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ maPalette( rHelper ),
+ maNumFmts( rHelper ),
+ maCellStyles( rHelper )
+{
+}
+
+FontRef StylesBuffer::createFont()
+{
+ FontRef xFont = std::make_shared<Font>( *this, false );
+ maFonts.push_back( xFont );
+ return xFont;
+}
+
+NumberFormatRef StylesBuffer::createNumFmt( sal_Int32 nNumFmtId, const OUString& rFmtCode )
+{
+ return maNumFmts.createNumFmt( nNumFmtId, rFmtCode );
+}
+
+sal_Int32 StylesBuffer::nextFreeNumFmtId()
+{
+ return maNumFmts.nextFreeId();
+}
+
+BorderRef StylesBuffer::createBorder()
+{
+ BorderRef xBorder = std::make_shared<Border>( *this, false );
+ maBorders.push_back( xBorder );
+ return xBorder;
+}
+
+FillRef StylesBuffer::createFill()
+{
+ FillRef xFill = std::make_shared<Fill>( *this, false );
+ maFills.push_back( xFill );
+ return xFill;
+}
+
+XfRef StylesBuffer::createCellXf()
+{
+ XfRef xXf = std::make_shared<Xf>( *this );
+ maCellXfs.push_back( xXf );
+ return xXf;
+}
+
+XfRef StylesBuffer::createStyleXf()
+{
+ XfRef xXf = std::make_shared<Xf>( *this );
+ maStyleXfs.push_back( xXf );
+ return xXf;
+}
+
+DxfRef StylesBuffer::createDxf()
+{
+ DxfRef xDxf = std::make_shared<Dxf>( *this );
+ maDxfs.push_back( xDxf );
+ return xDxf;
+}
+
+DxfRef StylesBuffer::createExtDxf()
+{
+ DxfRef xDxf = std::make_shared<Dxf>( *this );
+ maExtDxfs.push_back( xDxf );
+ return xDxf;
+}
+
+void StylesBuffer::importPaletteColor( const AttributeList& rAttribs )
+{
+ maPalette.importPaletteColor( rAttribs );
+}
+
+NumberFormatRef StylesBuffer::importNumFmt( const AttributeList& rAttribs )
+{
+ return maNumFmts.importNumFmt( rAttribs );
+}
+
+CellStyleRef StylesBuffer::importCellStyle( const AttributeList& rAttribs )
+{
+ return maCellStyles.importCellStyle( rAttribs );
+}
+
+void StylesBuffer::importPaletteColor( SequenceInputStream& rStrm )
+{
+ maPalette.importPaletteColor( rStrm );
+}
+
+void StylesBuffer::importNumFmt( SequenceInputStream& rStrm )
+{
+ maNumFmts.importNumFmt( rStrm );
+}
+
+void StylesBuffer::importCellStyle( SequenceInputStream& rStrm )
+{
+ maCellStyles.importCellStyle( rStrm );
+}
+
+void StylesBuffer::finalizeImport()
+{
+ // fonts first, are needed to finalize unit converter and XFs below
+ maFonts.forEachMem( &Font::finalizeImport );
+ // finalize unit coefficients after default font is known
+ getUnitConverter().finalizeImport();
+ // number formats
+ maNumFmts.finalizeImport();
+ // borders and fills
+ // is there a document wide RTL setting that we
+ // would/could need to pass to finalizeImport here ?
+ maBorders.forEachMem( &Border::finalizeImport, false );
+ maFills.forEachMem( &Fill::finalizeImport );
+ // style XFs and cell XFs
+ maStyleXfs.forEachMem( &Xf::finalizeImport );
+ maCellXfs.forEachMem( &Xf::finalizeImport );
+ // built-in and user defined cell styles
+ maCellStyles.finalizeImport();
+ // differential formatting (for conditional formatting)
+ maDxfs.forEachMem( &Dxf::finalizeImport );
+}
+
+::Color StylesBuffer::getPaletteColor( sal_Int32 nPaletteIdx ) const
+{
+ return maPalette.getColor( nPaletteIdx );
+}
+
+FontRef StylesBuffer::getFont( sal_Int32 nFontId ) const
+{
+ return maFonts.get( nFontId );
+}
+
+BorderRef StylesBuffer::getBorder( sal_Int32 nBorderId ) const
+{
+ return maBorders.get( nBorderId );
+}
+
+XfRef StylesBuffer::getCellXf( sal_Int32 nXfId ) const
+{
+ return maCellXfs.get( nXfId );
+}
+
+XfRef StylesBuffer::getStyleXf( sal_Int32 nXfId ) const
+{
+ return maStyleXfs.get( nXfId );
+}
+
+FontRef StylesBuffer::getFontFromCellXf( sal_Int32 nXfId ) const
+{
+ FontRef xFont;
+ if( const Xf* pXf = getCellXf( nXfId ).get() )
+ xFont = pXf->getFont();
+ return xFont;
+}
+
+FontRef StylesBuffer::getDefaultFont() const
+{
+ FontRef xDefFont;
+ if( const Xf* pXf = getStyleXf( maCellStyles.getDefaultXfId() ).get() )
+ xDefFont = pXf->getFont();
+ // no font from styles - try first loaded font (e.g. BIFF2)
+ if( !xDefFont )
+ xDefFont = maFonts.get( 0 );
+ OSL_ENSURE( xDefFont, "StylesBuffer::getDefaultFont - no default font found" );
+ return xDefFont;
+}
+
+const FontModel& StylesBuffer::getDefaultFontModel() const
+{
+ FontRef xDefFont = getDefaultFont();
+ return xDefFont ? xDefFont->getModel() : getTheme().getDefaultFontModel();
+}
+
+bool StylesBuffer::equalBorders( sal_Int32 nBorderId1, sal_Int32 nBorderId2 )
+{
+ // in OOXML, borders are assumed to be unique
+ return nBorderId1 == nBorderId2;
+}
+
+bool StylesBuffer::equalFills( sal_Int32 nFillId1, sal_Int32 nFillId2 )
+{
+ // in OOXML, fills are assumed to be unique
+ return nFillId1 == nFillId2;
+}
+
+OUString StylesBuffer::getDefaultStyleName() const
+{
+ return maCellStyles.getDefaultStyleName();
+}
+
+OUString StylesBuffer::createCellStyle( sal_Int32 nXfId ) const
+{
+ return maCellStyles.createCellStyle( nXfId );
+}
+
+::ScStyleSheet* StylesBuffer::getCellStyleSheet( sal_Int32 nXfId ) const
+{
+ return maCellStyles.getCellStyleSheet( nXfId );
+}
+
+OUString StylesBuffer::createDxfStyle( sal_Int32 nDxfId ) const
+{
+ OUString& rStyleName = maDxfStyles[ nDxfId ];
+ if (!rStyleName.isEmpty())
+ return rStyleName;
+
+ if (Dxf* pDxf = maDxfs.get(nDxfId).get())
+ {
+ // FIXME: How can we know whether this dxf is for conditional formatting,
+ // not for color filter? Currently this style is created for each dxf
+ // (which might only be used by color filter)
+ rStyleName = "ConditionalStyle_" + OUString::number(nDxfId + 1);
+
+ // Create a cell style. This may overwrite an existing style if
+ // one with the same name exists.
+ ScStyleSheet& rStyleSheet = ScfTools::MakeCellStyleSheet(
+ *getScDocument().GetStyleSheetPool(), rStyleName, true);
+
+ rStyleSheet.ResetParent();
+ SfxItemSet& rStyleItemSet =
+ rStyleSheet.GetItemSet();
+
+ pDxf->fillToItemSet(rStyleItemSet);
+
+ }
+
+ // on error: fallback to default style
+ if (rStyleName.isEmpty())
+ rStyleName = maCellStyles.getDefaultStyleName();
+
+ return rStyleName;
+}
+
+OUString StylesBuffer::createExtDxfStyle( sal_Int32 nDxfId ) const
+{
+ OUString rStyleName;
+
+ if (Dxf* pDxf = maExtDxfs.get(nDxfId).get())
+ {
+ rStyleName = "ExtConditionalStyle_" + OUString::number(nDxfId + 1);
+
+ // Create a cell style. This may overwrite an existing style if
+ // one with the same name exists.
+ ScStyleSheet& rStyleSheet = ScfTools::MakeCellStyleSheet(
+ *getScDocument().GetStyleSheetPool(), rStyleName, true);
+
+ rStyleSheet.ResetParent();
+ SfxItemSet& rStyleItemSet =
+ rStyleSheet.GetItemSet();
+
+ pDxf->fillToItemSet(rStyleItemSet);
+ }
+
+ // on error: fallback to default style
+ if (rStyleName.isEmpty())
+ rStyleName = maCellStyles.getDefaultStyleName();
+
+ return rStyleName;
+}
+
+void StylesBuffer::writeFontToItemSet( SfxItemSet& rItemSet, sal_Int32 nFontId, bool bSkipPoolDefs ) const
+{
+ if( Font* pFont = maFonts.get( nFontId ).get() )
+ pFont->fillToItemSet( rItemSet, false, bSkipPoolDefs );
+}
+
+sal_uInt32 StylesBuffer::writeNumFmtToItemSet( SfxItemSet& rItemSet, sal_uInt32 nNumFmtId, bool bSkipPoolDefs ) const
+{
+ return maNumFmts.fillToItemSet( rItemSet, nNumFmtId, bSkipPoolDefs );
+}
+
+void StylesBuffer::writeBorderToItemSet( SfxItemSet& rItemSet, sal_Int32 nBorderId, bool bSkipPoolDefs ) const
+{
+ if( Border* pBorder = maBorders.get( nBorderId ).get() )
+ pBorder->fillToItemSet( rItemSet, bSkipPoolDefs );
+}
+
+void StylesBuffer::writeFillToItemSet( SfxItemSet& rItemSet, sal_Int32 nFillId, bool bSkipPoolDefs ) const
+{
+ if( Fill* pFill = maFills.get( nFillId ).get() )
+ pFill->fillToItemSet( rItemSet, bSkipPoolDefs );
+}
+
+bool operator==( const XfModel& rXfModel1, const XfModel& rXfModel2 )
+{
+ return ( rXfModel1.mbCellXf == rXfModel2.mbCellXf &&
+ rXfModel1.mnStyleXfId == rXfModel2.mnStyleXfId &&
+ rXfModel1.mbFontUsed == rXfModel2.mbFontUsed &&
+ rXfModel1.mnFontId == rXfModel2.mnFontId &&
+ rXfModel1.mbNumFmtUsed == rXfModel2.mbNumFmtUsed &&
+ rXfModel1.mnNumFmtId == rXfModel2.mnNumFmtId &&
+ rXfModel1.mbAlignUsed == rXfModel2.mbAlignUsed &&
+ rXfModel1.mbBorderUsed == rXfModel2.mbBorderUsed &&
+ rXfModel1.mnBorderId == rXfModel2.mnBorderId &&
+ rXfModel1.mbAreaUsed == rXfModel2.mbAreaUsed &&
+ rXfModel1.mnFillId == rXfModel2.mnFillId &&
+ rXfModel1.mbProtUsed == rXfModel2.mbProtUsed );
+}
+
+bool operator==( const Xf& rXf1, const Xf& rXf2 )
+{
+ if ( rXf1.maModel == rXf2.maModel )
+ {
+ if ( rXf1.maModel.mbAlignUsed )
+ {
+ if ( !( rXf1.maAlignment.getApiData() == rXf2.maAlignment.getApiData() ) )
+ return false;
+ }
+ if ( rXf1.maModel.mbProtUsed )
+ {
+ if ( !( rXf1.maProtection.getApiData() == rXf2.maProtection.getApiData() ) )
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+void StylesBuffer::writeCellXfToDoc(
+ ScDocumentImport& rDoc, const ScRange& rRange, sal_Int32 nXfId ) const
+{
+ Xf* pXf = maCellXfs.get(nXfId).get();
+ if (!pXf)
+ return;
+
+ pXf->writeToDoc(rDoc, rRange);
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/stylesfragment.cxx b/sc/source/filter/oox/stylesfragment.cxx
new file mode 100644
index 000000000..e6707704d
--- /dev/null
+++ b/sc/source/filter/oox/stylesfragment.cxx
@@ -0,0 +1,317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stylesfragment.hxx>
+#include <biffhelper.hxx>
+
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+IndexedColorsContext::IndexedColorsContext( WorkbookFragmentBase& rFragment ) :
+ WorkbookContextBase( rFragment )
+{
+}
+
+ContextHandlerRef IndexedColorsContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( indexedColors ):
+ if( nElement == XLS_TOKEN( rgbColor ) ) getStyles().importPaletteColor( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef IndexedColorsContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case BIFF12_ID_INDEXEDCOLORS:
+ if( nRecId == BIFF12_ID_RGBCOLOR ) getStyles().importPaletteColor( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef FontContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxFont )
+ mxFont->importAttribs( nElement, rAttribs );
+ return nullptr;
+}
+
+void BorderContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( mxBorder && (getCurrentElement() == XLS_TOKEN( border )) )
+ mxBorder->importBorder( rAttribs );
+}
+
+ContextHandlerRef BorderContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxBorder ) switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( border ):
+ mxBorder->importStyle( nElement, rAttribs );
+ return this;
+
+ default:
+ if( nElement == XLS_TOKEN( color ) )
+ mxBorder->importColor( getCurrentElement(), rAttribs );
+ }
+ return nullptr;
+}
+
+ContextHandlerRef FillContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxFill ) switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( fill ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( patternFill ): mxFill->importPatternFill( rAttribs ); return this;
+ case XLS_TOKEN( gradientFill ): mxFill->importGradientFill( rAttribs ); return this;
+ }
+ break;
+ case XLS_TOKEN( patternFill ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( fgColor ): mxFill->importFgColor( rAttribs ); break;
+ case XLS_TOKEN( bgColor ): mxFill->importBgColor( rAttribs ); break;
+ }
+ break;
+ case XLS_TOKEN( gradientFill ):
+ if( nElement == XLS_TOKEN( stop ) )
+ {
+ mfGradPos = rAttribs.getDouble( XML_position, -1.0 );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( stop ):
+ if( nElement == XLS_TOKEN( color ) )
+ mxFill->importColor( rAttribs, mfGradPos );
+ break;
+ }
+ return nullptr;
+}
+
+void XfContext::onStartElement( const AttributeList& rAttribs )
+{
+ if( mxXf && (getCurrentElement() == XLS_TOKEN( xf )) )
+ mxXf->importXf( rAttribs, mbCellXf );
+}
+
+ContextHandlerRef XfContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxXf ) switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( xf ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( alignment ): mxXf->importAlignment( rAttribs ); break;
+ case XLS_TOKEN( protection ): mxXf->importProtection( rAttribs ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef DxfContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ if( mxDxf ) switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( dxf ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( font ): return new FontContext( *this, mxDxf->createFont() );
+ case XLS_TOKEN( border ): return new BorderContext( *this, mxDxf->createBorder() );
+ case XLS_TOKEN( fill ): return new FillContext( *this, mxDxf->createFill() );
+
+ case XLS_TOKEN( numFmt ): mxDxf->importNumFmt( rAttribs ); break;
+#if 0
+ case XLS_TOKEN( alignment ): mxDxf->importAlignment( rAttribs ); break;
+ case XLS_TOKEN( protection ): mxDxf->importProtection( rAttribs ); break;
+#endif
+ }
+ break;
+ }
+
+ if( mxExtDxf ) switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( dxf ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( font ): return new FontContext( *this, mxExtDxf->createFont() );
+ case XLS_TOKEN( border ): return new BorderContext( *this, mxExtDxf->createBorder() );
+ case XLS_TOKEN( fill ): return new FillContext( *this, mxExtDxf->createFill() );
+ case XLS_TOKEN( numFmt ): mxExtDxf->importNumFmt( rAttribs ); break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+StylesFragment::StylesFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef StylesFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( styleSheet ) ) return this;
+ break;
+
+ case XLS_TOKEN( styleSheet ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( colors ):
+ case XLS_TOKEN( numFmts ):
+ case XLS_TOKEN( fonts ):
+ case XLS_TOKEN( borders ):
+ case XLS_TOKEN( fills ):
+ case XLS_TOKEN( cellXfs ):
+ case XLS_TOKEN( cellStyleXfs ):
+ case XLS_TOKEN( dxfs ):
+ case XLS_TOKEN( cellStyles ): return this;
+ }
+ break;
+
+ case XLS_TOKEN( colors ):
+ if( nElement == XLS_TOKEN( indexedColors ) ) return new IndexedColorsContext( *this );
+ break;
+ case XLS_TOKEN( numFmts ):
+ if( nElement == XLS_TOKEN( numFmt ) ) getStyles().importNumFmt( rAttribs );
+ break;
+ case XLS_TOKEN( fonts ):
+ if( nElement == XLS_TOKEN( font ) ) return new FontContext( *this, getStyles().createFont() );
+ break;
+ case XLS_TOKEN( borders ):
+ if( nElement == XLS_TOKEN( border ) ) return new BorderContext( *this, getStyles().createBorder() );
+ break;
+ case XLS_TOKEN( fills ):
+ if( nElement == XLS_TOKEN( fill ) ) return new FillContext( *this, getStyles().createFill() );
+ break;
+ case XLS_TOKEN( cellXfs ):
+ if( nElement == XLS_TOKEN( xf ) ) return new XfContext( *this, getStyles().createCellXf(), true );
+ break;
+ case XLS_TOKEN( cellStyleXfs ):
+ if( nElement == XLS_TOKEN( xf ) ) return new XfContext( *this, getStyles().createStyleXf(), false );
+ break;
+ case XLS_TOKEN( dxfs ):
+ if( nElement == XLS_TOKEN( dxf ) ) return new DxfContext( *this, getStyles().createDxf() );
+ break;
+ case XLS_TOKEN( cellStyles ):
+ if( nElement == XLS_TOKEN( cellStyle ) ) getStyles().importCellStyle( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef StylesFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_STYLESHEET ) return this;
+ break;
+
+ case BIFF12_ID_STYLESHEET:
+ switch( nRecId )
+ {
+ case BIFF12_ID_COLORS:
+ case BIFF12_ID_NUMFMTS:
+ case BIFF12_ID_FONTS:
+ case BIFF12_ID_BORDERS:
+ case BIFF12_ID_FILLS:
+ case BIFF12_ID_CELLXFS:
+ case BIFF12_ID_CELLSTYLEXFS:
+ case BIFF12_ID_DXFS:
+ case BIFF12_ID_CELLSTYLES: return this;
+ }
+ break;
+
+ case BIFF12_ID_COLORS:
+ if( nRecId == BIFF12_ID_INDEXEDCOLORS ) return new IndexedColorsContext( *this );
+ break;
+ case BIFF12_ID_NUMFMTS:
+ if( nRecId == BIFF12_ID_NUMFMT ) getStyles().importNumFmt( rStrm );
+ break;
+ case BIFF12_ID_FONTS:
+ if( nRecId == BIFF12_ID_FONT ) getStyles().createFont()->importFont( rStrm );
+ break;
+ case BIFF12_ID_BORDERS:
+ if( nRecId == BIFF12_ID_BORDER ) getStyles().createBorder()->importBorder( rStrm );
+ break;
+ case BIFF12_ID_FILLS:
+ if( nRecId == BIFF12_ID_FILL ) getStyles().createFill()->importFill( rStrm );
+ break;
+ case BIFF12_ID_CELLXFS:
+ if( nRecId == BIFF12_ID_XF ) getStyles().createCellXf()->importXf( rStrm, true );
+ break;
+ case BIFF12_ID_CELLSTYLEXFS:
+ if( nRecId == BIFF12_ID_XF ) getStyles().createStyleXf()->importXf( rStrm, false );
+ break;
+ case BIFF12_ID_DXFS:
+ if( nRecId == BIFF12_ID_DXF ) getStyles().createDxf()->importDxf( rStrm );
+ break;
+ case BIFF12_ID_CELLSTYLES:
+ if( nRecId == BIFF12_ID_CELLSTYLE ) getStyles().importCellStyle( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* StylesFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_BORDERS, BIFF12_ID_BORDERS + 1 },
+ { BIFF12_ID_CELLSTYLES, BIFF12_ID_CELLSTYLES + 1 },
+ { BIFF12_ID_CELLSTYLEXFS, BIFF12_ID_CELLSTYLEXFS + 1 },
+ { BIFF12_ID_CELLXFS, BIFF12_ID_CELLXFS + 1 },
+ { BIFF12_ID_COLORS, BIFF12_ID_COLORS + 1 },
+ { BIFF12_ID_DXFS, BIFF12_ID_DXFS + 1 },
+ { BIFF12_ID_FILLS, BIFF12_ID_FILLS + 1 },
+ { BIFF12_ID_FONTS, BIFF12_ID_FONTS + 1 },
+ { BIFF12_ID_INDEXEDCOLORS, BIFF12_ID_INDEXEDCOLORS + 1 },
+ { BIFF12_ID_MRUCOLORS, BIFF12_ID_MRUCOLORS + 1 },
+ { BIFF12_ID_NUMFMTS, BIFF12_ID_NUMFMTS + 1 },
+ { BIFF12_ID_STYLESHEET, BIFF12_ID_STYLESHEET + 1 },
+ { BIFF12_ID_TABLESTYLES, BIFF12_ID_TABLESTYLES + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void StylesFragment::finalizeImport()
+{
+ getStyles().finalizeImport();
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/tablebuffer.cxx b/sc/source/filter/oox/tablebuffer.cxx
new file mode 100644
index 000000000..a6352e63f
--- /dev/null
+++ b/sc/source/filter/oox/tablebuffer.cxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tablebuffer.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XDatabaseRange.hpp>
+#include <com/sun/star/sheet/XDatabaseRanges.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+TableModel::TableModel() :
+ mnId( -1 ),
+ mnType( XML_worksheet ),
+ mnHeaderRows( 1 ),
+ mnTotalsRows( 0 )
+{
+}
+
+Table::Table( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ maAutoFilters( rHelper ),
+ maTableColumns( rHelper ),
+ mnTokenIndex( -1 )
+{
+}
+
+void Table::importTable( const AttributeList& rAttribs, sal_Int16 nSheet )
+{
+ AddressConverter::convertToCellRangeUnchecked( maModel.maRange, rAttribs.getString( XML_ref, OUString() ), nSheet );
+ maModel.maProgName = rAttribs.getXString( XML_name, OUString() );
+ maModel.maDisplayName = rAttribs.getXString( XML_displayName, OUString() );
+ maModel.mnId = rAttribs.getInteger( XML_id, -1 );
+ maModel.mnType = rAttribs.getToken( XML_tableType, XML_worksheet );
+ maModel.mnHeaderRows = rAttribs.getInteger( XML_headerRowCount, 1 );
+ maModel.mnTotalsRows = rAttribs.getInteger( XML_totalsRowCount, 0 );
+}
+
+void Table::importTable( SequenceInputStream& rStrm, sal_Int16 nSheet )
+{
+ BinRange aBinRange;
+ sal_Int32 nType;
+ rStrm >> aBinRange;
+ nType = rStrm.readInt32();
+ maModel.mnId = rStrm.readInt32();
+ maModel.mnHeaderRows = rStrm.readInt32();
+ maModel.mnTotalsRows = rStrm.readInt32();
+ rStrm.skip( 32 );
+ rStrm >> maModel.maProgName >> maModel.maDisplayName;
+
+ AddressConverter::convertToCellRangeUnchecked( maModel.maRange, aBinRange, nSheet );
+ static const sal_Int32 spnTypes[] = { XML_worksheet, XML_TOKEN_INVALID, XML_TOKEN_INVALID, XML_queryTable };
+ maModel.mnType = STATIC_ARRAY_SELECT( spnTypes, nType, XML_TOKEN_INVALID );
+}
+
+void Table::finalizeImport()
+{
+ // Create database range. Note that Excel 2007 and later names database
+ // ranges (or tables in their terminology) as Table1, Table2 etc. We need
+ // to import them as named db ranges because they may be referenced by
+ // name in formula expressions.
+ if( (maModel.mnId <= 0) || maModel.maDisplayName.isEmpty() )
+ return;
+
+ try
+ {
+ maDBRangeName = maModel.maDisplayName;
+
+ Reference< XDatabaseRange > xDatabaseRange(
+ createDatabaseRangeObject( maDBRangeName, maModel.maRange ), UNO_SET_THROW);
+ ::css::table::CellRangeAddress aAddressRange = xDatabaseRange->getDataArea();
+ maDestRange = ScRange( aAddressRange.StartColumn, aAddressRange.StartRow, aAddressRange.Sheet,
+ aAddressRange.EndColumn, aAddressRange.EndRow, aAddressRange.Sheet );
+
+ PropertySet aPropSet( xDatabaseRange );
+
+ // Default HasHeader is true at ScDBData.
+ if (maModel.mnHeaderRows != 1)
+ {
+ SAL_WARN_IF( maModel.mnHeaderRows > 1, "sc.filter",
+ "Table HeaderRows > 1 not supported: " << maModel.mnHeaderRows);
+ if (maModel.mnHeaderRows == 0)
+ aPropSet.setProperty( PROP_ContainsHeader, false);
+ }
+
+ if (maModel.mnTotalsRows > 0)
+ {
+ SAL_WARN_IF( maModel.mnTotalsRows > 1, "sc.filter",
+ "Table TotalsRows > 1 not supported: " << maModel.mnTotalsRows);
+ aPropSet.setProperty( PROP_TotalsRow, true);
+ }
+
+ // get formula token index of the database range
+ if( !aPropSet.getProperty( mnTokenIndex, PROP_TokenIndex ) )
+ mnTokenIndex = -1;
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "Table::finalizeImport - cannot create database range" );
+ }
+}
+
+void Table::applyAutoFilters()
+{
+ if( maDBRangeName.isEmpty() )
+ return;
+
+ try
+ {
+ // get the range ( maybe we should cache the xDatabaseRange from finalizeImport )
+ PropertySet aDocProps( getDocument() );
+ Reference< XDatabaseRanges > xDatabaseRanges( aDocProps.getAnyProperty( PROP_DatabaseRanges ), UNO_QUERY_THROW );
+ Reference< XDatabaseRange > xDatabaseRange( xDatabaseRanges->getByName( maDBRangeName ), UNO_QUERY );
+ maAutoFilters.finalizeImport( xDatabaseRange, maModel.maRange.aStart.Tab() );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "Table::applyAutofilters - cannot create filter" );
+ }
+}
+
+void Table::applyTableColumns()
+{
+ maTableColumns.finalizeImport( findDatabaseRangeByIndex( mnTokenIndex ));
+}
+
+TableBuffer::TableBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+Table& TableBuffer::createTable()
+{
+ TableVector::value_type xTable = std::make_shared<Table>( *this );
+ maTables.push_back( xTable );
+ return *xTable;
+}
+
+void TableBuffer::finalizeImport()
+{
+ // map all tables by identifier and display name
+ for( const auto& rxTable : maTables )
+ insertTableToMaps( rxTable );
+ // finalize all valid tables
+ maIdTables.forEachMem( &Table::finalizeImport );
+}
+
+void TableBuffer::applyAutoFilters()
+{
+ maIdTables.forEachMem( &Table::applyAutoFilters );
+}
+
+void TableBuffer::applyTableColumns()
+{
+ maIdTables.forEachMem( &Table::applyTableColumns );
+}
+
+TableRef TableBuffer::getTable( sal_Int32 nTableId ) const
+{
+ return maIdTables.get( nTableId );
+}
+
+TableRef TableBuffer::getTable( const OUString& rDispName ) const
+{
+ return maNameTables.get( rDispName );
+}
+
+// private --------------------------------------------------------------------
+
+void TableBuffer::insertTableToMaps( const TableRef& rxTable )
+{
+ sal_Int32 nTableId = rxTable->getTableId();
+ const OUString& rDispName = rxTable->getDisplayName();
+ if( (nTableId > 0) && !rDispName.isEmpty() )
+ {
+ OSL_ENSURE( !maIdTables.has( nTableId ), "TableBuffer::insertTableToMaps - multiple table identifier" );
+ maIdTables[ nTableId ] = rxTable;
+ OSL_ENSURE( !maNameTables.has( rDispName ), "TableBuffer::insertTableToMaps - multiple table name" );
+ maNameTables[ rDispName ] = rxTable;
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/tablecolumnsbuffer.cxx b/sc/source/filter/oox/tablecolumnsbuffer.cxx
new file mode 100644
index 000000000..4a4f2997c
--- /dev/null
+++ b/sc/source/filter/oox/tablecolumnsbuffer.cxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tablecolumnsbuffer.hxx>
+
+#include <sal/log.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/tokens.hxx>
+#include <dbdata.hxx>
+
+namespace oox::xls {
+
+TableColumn::TableColumn( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnId( -1 ),
+ mnDataDxfId( -1 )
+{
+}
+
+void TableColumn::importTableColumn( const AttributeList& rAttribs )
+{
+ mnId = rAttribs.getInteger( XML_id, -1 );
+ maName = rAttribs.getString( XML_name, OUString() );
+ mnDataDxfId = rAttribs.getInteger( XML_dataDxfId, -1 );
+}
+
+void TableColumn::importTableColumn( SequenceInputStream& /*rStrm*/ )
+{
+ /* XXX not implemented */
+ (void) mnId;
+}
+
+const OUString& TableColumn::getName() const
+{
+ return maName;
+}
+
+TableColumns::TableColumns( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnCount(0)
+{
+}
+
+void TableColumns::importTableColumns( const AttributeList& rAttribs )
+{
+ mnCount = rAttribs.getInteger( XML_count, 0 );
+}
+
+void TableColumns::importTableColumns( SequenceInputStream& /*rStrm*/ )
+{
+ /* XXX not implemented */
+ (void) mnCount;
+}
+
+TableColumn& TableColumns::createTableColumn()
+{
+ TableColumnVector::value_type xTableColumn = std::make_shared<TableColumn>( *this );
+ maTableColumnVector.push_back( xTableColumn );
+ return *xTableColumn;
+}
+
+bool TableColumns::finalizeImport( ScDBData* pDBData )
+{
+ SAL_WARN_IF( static_cast<size_t>(mnCount) != maTableColumnVector.size(), "sc.filter",
+ "TableColumns::finalizeImport - count attribute doesn't match number of tableColumn elements");
+ if ( pDBData )
+ {
+ /* TODO: use svl::SharedString for names */
+ ::std::vector< OUString > aNames( maTableColumnVector.size());
+ size_t i = 0;
+ for (const auto& rxTableColumn : maTableColumnVector)
+ {
+ aNames[i] = rxTableColumn->getName();
+ ++i;
+ }
+ pDBData->SetTableColumnNames( std::move(aNames) );
+ return true;
+ }
+ return false;
+}
+
+TableColumnsBuffer::TableColumnsBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+TableColumns& TableColumnsBuffer::createTableColumns()
+{
+ TableColumnsVector::value_type xTableColumns = std::make_shared<TableColumns>( *this );
+ maTableColumnsVector.push_back( xTableColumns );
+ return *xTableColumns;
+}
+
+void TableColumnsBuffer::finalizeImport( ScDBData* pDBData )
+{
+ TableColumns* pTableColumns = getActiveTableColumns();
+ if ( pTableColumns )
+ pTableColumns->finalizeImport( pDBData );
+}
+
+TableColumns* TableColumnsBuffer::getActiveTableColumns()
+{
+ // not more than one table columns descriptor per table
+ SAL_WARN_IF( maTableColumnsVector.size() > 1, "sc.filter",
+ "TableColumnsBuffer::getActiveTableColumns - too many table columns" );
+ // stick to the last imported table columns
+ return maTableColumnsVector.empty() ? nullptr : maTableColumnsVector.back().get();
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/tablecolumnscontext.cxx b/sc/source/filter/oox/tablecolumnscontext.cxx
new file mode 100644
index 000000000..270f544bd
--- /dev/null
+++ b/sc/source/filter/oox/tablecolumnscontext.cxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tablecolumnscontext.hxx>
+
+#include <tablecolumnsbuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using ::oox::core::ContextHandlerRef;
+
+TableColumnContext::TableColumnContext( WorksheetContextBase& rParent, TableColumn& rTableColumn ) :
+ WorksheetContextBase( rParent ),
+ mrTableColumn( rTableColumn )
+{
+}
+
+ContextHandlerRef TableColumnContext::onCreateContext( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
+{
+ /* no known child elements */
+ return nullptr;
+}
+
+void TableColumnContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrTableColumn.importTableColumn( rAttribs );
+}
+
+ContextHandlerRef TableColumnContext::onCreateRecordContext( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
+{
+ /* no known child elements */
+ return nullptr;
+}
+
+void TableColumnContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrTableColumn.importTableColumn( rStrm );
+}
+
+TableColumnsContext::TableColumnsContext( WorksheetFragmentBase& rFragment, TableColumns& rTableColumns ) :
+ WorksheetContextBase( rFragment ),
+ mrTableColumns( rTableColumns )
+{
+}
+
+ContextHandlerRef TableColumnsContext::onCreateContext( sal_Int32 nElement, const AttributeList& /*rAttribs*/ )
+{
+ if( (getCurrentElement() == XLS_TOKEN( tableColumns )) && (nElement == XLS_TOKEN( tableColumn )) )
+ return new TableColumnContext( *this, mrTableColumns.createTableColumn() );
+ return nullptr;
+}
+
+void TableColumnsContext::onStartElement( const AttributeList& rAttribs )
+{
+ mrTableColumns.importTableColumns( rAttribs );
+}
+
+ContextHandlerRef TableColumnsContext::onCreateRecordContext( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
+{
+ /* XXX not implemented */
+#if 0
+ if( (getCurrentElement() == BIFF12_ID_TABLECOLUMNS) && (nRecId == BIFF12_ID_TABLECOLUMN) )
+ return new TableColumnContext( *this, mrTableColumns.createTableColumn() );
+#endif
+ return nullptr;
+}
+
+void TableColumnsContext::onStartRecord( SequenceInputStream& rStrm )
+{
+ mrTableColumns.importTableColumns( rStrm );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/tablefragment.cxx b/sc/source/filter/oox/tablefragment.cxx
new file mode 100644
index 000000000..99f3ede7a
--- /dev/null
+++ b/sc/source/filter/oox/tablefragment.cxx
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tablefragment.hxx>
+#include <biffhelper.hxx>
+
+#include <autofiltercontext.hxx>
+#include <tablecolumnscontext.hxx>
+#include <tablebuffer.hxx>
+#include <oox/token/namespaces.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+TableFragment::TableFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath ),
+ mrTable( getTables().createTable() )
+{
+}
+
+ContextHandlerRef TableFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( table ) )
+ {
+ mrTable.importTable( rAttribs, getSheetIndex() );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( table ):
+ switch (nElement)
+ {
+ case XLS_TOKEN( autoFilter ):
+ return new AutoFilterContext( *this, mrTable.createAutoFilter() );
+ case XLS_TOKEN( tableColumns ):
+ return new TableColumnsContext( *this, mrTable.createTableColumns() );
+ }
+ break;
+ }
+ return nullptr;
+}
+
+ContextHandlerRef TableFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_TABLE )
+ {
+ mrTable.importTable( rStrm, getSheetIndex() );
+ return this;
+ }
+ break;
+ case BIFF12_ID_TABLE:
+ if( nRecId == BIFF12_ID_AUTOFILTER )
+ return new AutoFilterContext( *this, mrTable.createAutoFilter() );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* TableFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_AUTOFILTER, BIFF12_ID_AUTOFILTER + 1 },
+ { BIFF12_ID_CUSTOMFILTERS, BIFF12_ID_CUSTOMFILTERS + 1 },
+ { BIFF12_ID_DISCRETEFILTERS, BIFF12_ID_DISCRETEFILTERS + 1 },
+ { BIFF12_ID_FILTERCOLUMN, BIFF12_ID_FILTERCOLUMN + 1 },
+ { BIFF12_ID_TABLE, BIFF12_ID_TABLE + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/themebuffer.cxx b/sc/source/filter/oox/themebuffer.cxx
new file mode 100644
index 000000000..3c82a3161
--- /dev/null
+++ b/sc/source/filter/oox/themebuffer.cxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <themebuffer.hxx>
+
+#include <stylesbuffer.hxx>
+
+namespace oox::xls {
+
+
+ThemeBuffer::ThemeBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mxDefFontModel( new FontModel )
+{
+ //! TODO: locale dependent font name
+ mxDefFontModel->maName = "Cambria";
+ mxDefFontModel->mfHeight = 11.0;
+}
+
+ThemeBuffer::~ThemeBuffer()
+{
+}
+
+::Color ThemeBuffer::getColorByToken( sal_Int32 nToken ) const
+{
+ ::Color nColor = 0;
+ return getClrScheme().getColor( nToken, nColor ) ? nColor : API_RGB_TRANSPARENT;
+}
+
+::Color ThemeBuffer::getColorByIndex(size_t nIndex) const
+{
+ ::Color nColor = 0;
+ return getClrScheme().getColorByIndex(nIndex, nColor) ? nColor : API_RGB_TRANSPARENT;
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/unitconverter.cxx b/sc/source/filter/oox/unitconverter.cxx
new file mode 100644
index 000000000..d17f590aa
--- /dev/null
+++ b/sc/source/filter/oox/unitconverter.cxx
@@ -0,0 +1,228 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unitconverter.hxx>
+
+#include <com/sun/star/awt/DeviceInfo.hpp>
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/awt/XFont.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <stylesbuffer.hxx>
+#include <biffhelper.hxx>
+
+namespace com::sun::star::awt { struct FontDescriptor; }
+
+namespace oox::xls {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+/** Returns true, if the passed year is a leap year. */
+bool lclIsLeapYear( sal_Int32 nYear )
+{
+ return ((nYear % 4) == 0) && (((nYear % 100) != 0) || ((nYear % 400) == 0));
+}
+
+void lclSkipYearBlock( sal_Int32& ornDays, sal_Int16& ornYear, sal_Int32 nDaysInBlock, sal_Int32 nYearsPerBlock, sal_Int32 nMaxBlocks )
+{
+ sal_Int32 nBlocks = ::std::min< sal_Int32 >( ornDays / nDaysInBlock, nMaxBlocks );
+ ornYear = static_cast< sal_Int16 >( ornYear + nYearsPerBlock * nBlocks );
+ ornDays -= nBlocks * nDaysInBlock;
+}
+
+/** Returns the number of days before the passed date, starting from the null
+ date 0000-Jan-01, using standard leap year conventions. */
+sal_Int32 lclGetDays( const util::Date& rDate )
+{
+ // number of days in all full years before passed date including all leap days
+ sal_Int32 nDays = rDate.Year * 365 + ((rDate.Year + 3) / 4) - ((rDate.Year + 99) / 100) + ((rDate.Year + 399) / 400);
+ OSL_ENSURE( (1 <= rDate.Month) && (rDate.Month <= 12), "lclGetDays - invalid month" );
+ OSL_ENSURE( (1 <= rDate.Day) && (rDate.Day <= 31), "lclGetDays - invalid day" ); // yes, this is weak...
+ if( (1 <= rDate.Month) && (rDate.Month <= 12) )
+ {
+ // number of days at start of month jan feb mar apr may jun jul aug sep oct nov dec
+ static const sal_Int32 spnCumDays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+ // add number of days in full months before passed date
+ nDays += spnCumDays[ rDate.Month - 1 ];
+ // add number of days from passed date (this adds one day too much)
+ nDays += rDate.Day;
+ /* Remove the one day added too much if there is no leap day before
+ the passed day in the passed year. This means: remove the day, if
+ we are in january or february (leap day not reached if existing),
+ or if the passed year is not a leap year. */
+ if( (rDate.Month < 3) || !lclIsLeapYear( rDate.Year ) )
+ --nDays;
+ }
+ return nDays;
+}
+
+} // namespace
+
+UnitConverter::UnitConverter( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mnNullDate( lclGetDays( util::Date( 30, 12, 1899 ) ) )
+{
+ // initialize constant and default coefficients
+ const DeviceInfo& rDeviceInfo = getBaseFilter().getGraphicHelper().getDeviceInfo();
+ maCoeffs[Unit::Twip] = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::emu);
+ maCoeffs[Unit::Emu] = 1;
+ maCoeffs[Unit::ScreenX] = o3tl::convert((rDeviceInfo.PixelPerMeterX > 0) ? (1000.0 / rDeviceInfo.PixelPerMeterX) : 0.5, o3tl::Length::mm, o3tl::Length::emu);
+ maCoeffs[Unit::ScreenY] = o3tl::convert((rDeviceInfo.PixelPerMeterY > 0) ? (1000.0 / rDeviceInfo.PixelPerMeterY) : 0.5, o3tl::Length::mm, o3tl::Length::emu);
+ maCoeffs[Unit::Digit] = o3tl::convert(2.0, o3tl::Length::mm, o3tl::Length::emu); // default: 1 digit = 2 mm
+ maCoeffs[Unit::Space] = o3tl::convert(1.0, o3tl::Length::mm, o3tl::Length::emu); // default 1 space = 1 mm
+
+ // error code maps
+ addErrorCode( BIFF_ERR_NULL, "#NULL!" );
+ addErrorCode( BIFF_ERR_DIV0, "#DIV/0!" );
+ addErrorCode( BIFF_ERR_VALUE, "#VALUE!" );
+ addErrorCode( BIFF_ERR_REF, "#REF!" );
+ addErrorCode( BIFF_ERR_NAME, "#NAME?" );
+ addErrorCode( BIFF_ERR_NUM, "#NUM!" );
+ addErrorCode( BIFF_ERR_NA, "#N/A" );
+}
+
+void UnitConverter::finalizeImport()
+{
+ PropertySet aDocProps( getDocument() );
+ Reference< XDevice > xDevice( aDocProps.getAnyProperty( PROP_ReferenceDevice ), UNO_QUERY );
+ if( !xDevice.is() )
+ return;
+
+ // get character widths from default font
+ const oox::xls::Font* pDefFont = getStyles().getDefaultFont().get();
+ if( !pDefFont )
+ return;
+
+ // XDevice expects pixels in font descriptor, but font contains twips
+ const FontDescriptor& aDesc = pDefFont->getFontDescriptor();
+ Reference< XFont > xFont = xDevice->getFont( aDesc );
+ if( !xFont.is() )
+ return;
+
+ // get maximum width of all digits
+ sal_Int64 nDigitWidth = 0;
+ for( sal_Unicode cChar = '0'; cChar <= '9'; ++cChar )
+ nDigitWidth = ::std::max(nDigitWidth, o3tl::convert(xFont->getCharWidth(cChar),
+ o3tl::Length::twip, o3tl::Length::emu));
+ if( nDigitWidth > 0 )
+ maCoeffs[ Unit::Digit ] = nDigitWidth;
+ // get width of space character
+ sal_Int64 nSpaceWidth
+ = o3tl::convert(xFont->getCharWidth(' '), o3tl::Length::twip, o3tl::Length::emu);
+ if( nSpaceWidth > 0 )
+ maCoeffs[ Unit::Space ] = nSpaceWidth;
+}
+
+void UnitConverter::finalizeNullDate( const util::Date& rNullDate )
+{
+ // convert the nulldate to number of days since 0000-Jan-01
+ mnNullDate = lclGetDays( rNullDate );
+}
+
+// conversion -----------------------------------------------------------------
+
+double UnitConverter::scaleValue( double fValue, Unit eFromUnit, Unit eToUnit ) const
+{
+ return (eFromUnit == eToUnit) ? fValue : (fValue * getCoefficient( eFromUnit ) / getCoefficient( eToUnit ));
+}
+
+double UnitConverter::calcSerialFromDateTime( const util::DateTime& rDateTime ) const
+{
+ sal_Int32 nDays = lclGetDays( util::Date( rDateTime.Day, rDateTime.Month, rDateTime.Year ) ) - mnNullDate;
+ OSL_ENSURE( nDays >= 0, "UnitConverter::calcDateTimeSerial - invalid date" );
+ OSL_ENSURE( (rDateTime.Hours <= 23) && (rDateTime.Minutes <= 59) && (rDateTime.Seconds <= 59), "UnitConverter::calcDateTimeSerial - invalid time" );
+ return nDays + rDateTime.Hours / 24.0 + rDateTime.Minutes / 1440.0 + rDateTime.Seconds / 86400.0;
+}
+
+util::DateTime UnitConverter::calcDateTimeFromSerial( double fSerial ) const
+{
+ util::DateTime aDateTime( 0, 0, 0, 0, 1, 1, 0, false );
+ double fDays = 0.0;
+ double fTime = modf( fSerial, &fDays );
+
+ // calculate date from number of days with O(1) complexity
+ sal_Int32 nDays = getLimitedValue< sal_Int32, double >( fDays + mnNullDate, 0, 3652424 );
+ // skip year 0, assumed to be a leap year. By starting at year 1, leap years can be handled easily
+ if( nDays >= 366 ) { ++aDateTime.Year; nDays -= 366; }
+ // skip full blocks of 400, 100, 4 years, and remaining full years
+ lclSkipYearBlock( nDays, aDateTime.Year, 400 * 365 + 97, 400, 24 );
+ lclSkipYearBlock( nDays, aDateTime.Year, 100 * 365 + 24, 100, 3 );
+ lclSkipYearBlock( nDays, aDateTime.Year, 4 * 365 + 1, 4, 24 );
+ lclSkipYearBlock( nDays, aDateTime.Year, 365, 1, 3 );
+ // skip full months of current year
+ static const sal_Int32 spnDaysInMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ if( (nDays >= 59) && !lclIsLeapYear( aDateTime.Year ) ) ++nDays;
+ const sal_Int32* pnDaysInMonth = spnDaysInMonth;
+ while( *pnDaysInMonth >= nDays ) { ++aDateTime.Month; nDays -= *pnDaysInMonth; ++pnDaysInMonth; }
+ aDateTime.Day = static_cast< sal_uInt16 >( nDays + 1 );
+
+ // calculate time from fractional part of serial
+ sal_Int32 nTime = getLimitedValue< sal_Int32, double >( fTime * 86400, 0, 86399 );
+ aDateTime.Seconds = static_cast< sal_uInt16 >( nTime % 60 );
+ nTime /= 60;
+ aDateTime.Minutes = static_cast< sal_uInt16 >( nTime % 60 );
+ aDateTime.Hours = static_cast< sal_uInt16 >( nTime / 60 );
+
+ return aDateTime;
+}
+
+sal_uInt8 UnitConverter::calcBiffErrorCode( const OUString& rErrorCode ) const
+{
+ auto aIt = maOoxErrCodes.find( rErrorCode );
+ return (aIt == maOoxErrCodes.end()) ? BIFF_ERR_NA : aIt->second;
+}
+
+OUString UnitConverter::calcErrorString( sal_uInt8 nErrorCode ) const
+{
+ auto iFail( maOoxErrCodes.cend());
+ for (auto aIt( maOoxErrCodes.cbegin()); aIt != maOoxErrCodes.cend(); ++aIt)
+ {
+ if (aIt->second == nErrorCode)
+ return aIt->first;
+ if (aIt->second == BIFF_ERR_NA)
+ iFail = aIt;
+ }
+ assert(iFail != maOoxErrCodes.end()); // BIFF_ERR_NA really should be in the map...
+ return iFail != maOoxErrCodes.end() ? iFail->first : OUString();
+}
+
+void UnitConverter::addErrorCode( sal_uInt8 nErrorCode, const OUString& rErrorCode )
+{
+ maOoxErrCodes[ rErrorCode ] = nErrorCode;
+}
+
+double UnitConverter::getCoefficient( Unit eUnit ) const
+{
+ return maCoeffs[ eUnit ];
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/viewsettings.cxx b/sc/source/filter/oox/viewsettings.cxx
new file mode 100644
index 000000000..c72a61b17
--- /dev/null
+++ b/sc/source/filter/oox/viewsettings.cxx
@@ -0,0 +1,651 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <viewsettings.hxx>
+
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <com/sun/star/document/NamedPropertyValues.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <comphelper/indexedpropertyvalues.hxx>
+#include <osl/diagnose.h>
+#include <unotools/mediadescriptor.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/helper/propertymap.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <workbooksettings.hxx>
+#include <worksheetbuffer.hxx>
+#include <vcl/svapp.hxx>
+
+namespace com::sun::star::container { class XNameContainer; }
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::uno;
+
+using ::oox::core::FilterBase;
+
+namespace {
+
+const sal_Int32 OOX_BOOKVIEW_TABBARRATIO_DEF = 600; /// Default tabbar ratio.
+const sal_Int32 OOX_SHEETVIEW_NORMALZOOM_DEF = 100; /// Default zoom for normal view.
+const sal_Int32 OOX_SHEETVIEW_SHEETLAYZOOM_DEF = 60; /// Default zoom for pagebreak preview.
+
+const sal_uInt8 BIFF12_PANE_FROZEN = 0x01;
+const sal_uInt8 BIFF12_PANE_FROZENNOSPLIT = 0x02;
+
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWFORMULAS = 0x0002;
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWGRID = 0x0004;
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWHEADINGS = 0x0008;
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWZEROS = 0x0010;
+const sal_uInt16 BIFF12_SHEETVIEW_RIGHTTOLEFT = 0x0020;
+const sal_uInt16 BIFF12_SHEETVIEW_SELECTED = 0x0040;
+const sal_uInt16 BIFF12_SHEETVIEW_SHOWOUTLINE = 0x0100;
+const sal_uInt16 BIFF12_SHEETVIEW_DEFGRIDCOLOR = 0x0200;
+
+const sal_uInt16 BIFF12_CHARTSHEETVIEW_SELECTED = 0x0001;
+const sal_uInt16 BIFF12_CHARTSHEETVIEW_ZOOMTOFIT = 0x0002;
+
+const sal_uInt8 BIFF12_WBVIEW_HIDDEN = 0x01;
+const sal_uInt8 BIFF12_WBVIEW_MINIMIZED = 0x02;
+const sal_uInt8 BIFF12_WBVIEW_SHOWHORSCROLL = 0x08;
+const sal_uInt8 BIFF12_WBVIEW_SHOWVERSCROLL = 0x10;
+const sal_uInt8 BIFF12_WBVIEW_SHOWTABBAR = 0x20;
+
+// Attention: view settings in Calc do not use com.sun.star.view.DocumentZoomType!
+const sal_Int16 API_ZOOMTYPE_PERCENT = 0; /// Zoom value in percent.
+
+const sal_Int32 API_ZOOMVALUE_MIN = 20; /// Minimum zoom in Calc.
+const sal_Int32 API_ZOOMVALUE_MAX = 400; /// Maximum zoom in Calc.
+
+// no predefined constants for split mode
+const sal_Int16 API_SPLITMODE_NONE = 0; /// No splits in window.
+const sal_Int16 API_SPLITMODE_SPLIT = 1; /// Window is split.
+const sal_Int16 API_SPLITMODE_FREEZE = 2; /// Window has frozen panes.
+
+// no predefined constants for pane identifiers
+const sal_Int16 API_SPLITPANE_TOPLEFT = 0; /// Top-left, or top pane.
+const sal_Int16 API_SPLITPANE_TOPRIGHT = 1; /// Top-right pane.
+const sal_Int16 API_SPLITPANE_BOTTOMLEFT = 2; /// Bottom-left, bottom, left, or single pane.
+const sal_Int16 API_SPLITPANE_BOTTOMRIGHT = 3; /// Bottom-right, or right pane.
+
+/** Returns the OOXML pane identifier from the passed BIFF pane id. */
+sal_Int32 lclGetOoxPaneId( sal_Int32 nBiffPaneId, sal_Int32 nDefaultPaneId )
+{
+ static const sal_Int32 spnPaneIds[] = { XML_bottomRight, XML_topRight, XML_bottomLeft, XML_topLeft };
+ return STATIC_ARRAY_SELECT( spnPaneIds, nBiffPaneId, nDefaultPaneId );
+}
+
+} // namespace
+
+PaneSelectionModel::PaneSelectionModel() :
+ mnActiveCellId( 0 )
+{
+}
+
+SheetViewModel::SheetViewModel() :
+ mnWorkbookViewId( 0 ),
+ mnViewType( XML_normal ),
+ mnActivePaneId( XML_topLeft ),
+ mnPaneState( XML_split ),
+ mfSplitX( 0.0 ),
+ mfSplitY( 0.0 ),
+ mnCurrentZoom( 0 ),
+ mnNormalZoom( 0 ),
+ mnSheetLayoutZoom( 0 ),
+ mnPageLayoutZoom( 0 ),
+ mbSelected( false ),
+ mbRightToLeft( false ),
+ mbDefGridColor( true ),
+ mbShowFormulas( false ),
+ mbShowGrid( true ),
+ mbShowHeadings( true ),
+ mbShowZeros( true ),
+ mbShowOutline( true ),
+ mbZoomToFit( false )
+{
+ maGridColor.setIndexed( OOX_COLOR_WINDOWTEXT );
+}
+
+bool SheetViewModel::isPageBreakPreview() const
+{
+ return mnViewType == XML_pageBreakPreview;
+}
+
+sal_Int32 SheetViewModel::getNormalZoom() const
+{
+ const sal_Int32& rnZoom = isPageBreakPreview() ? mnNormalZoom : mnCurrentZoom;
+ sal_Int32 nZoom = (rnZoom > 0) ? rnZoom : OOX_SHEETVIEW_NORMALZOOM_DEF;
+ return getLimitedValue< sal_Int32 >( nZoom, API_ZOOMVALUE_MIN, API_ZOOMVALUE_MAX );
+}
+
+sal_Int32 SheetViewModel::getPageBreakZoom() const
+{
+ const sal_Int32& rnZoom = isPageBreakPreview() ? mnCurrentZoom : mnSheetLayoutZoom;
+ sal_Int32 nZoom = (rnZoom > 0) ? rnZoom : OOX_SHEETVIEW_SHEETLAYZOOM_DEF;
+ return getLimitedValue< sal_Int32 >( nZoom, API_ZOOMVALUE_MIN, API_ZOOMVALUE_MAX );
+}
+
+::Color SheetViewModel::getGridColor( const FilterBase& rFilter ) const
+{
+ return mbDefGridColor ? API_RGB_TRANSPARENT : maGridColor.getColor( rFilter.getGraphicHelper() );
+}
+
+const PaneSelectionModel* SheetViewModel::getActiveSelection() const
+{
+ return maPaneSelMap.get( mnActivePaneId ).get();
+}
+
+PaneSelectionModel& SheetViewModel::createPaneSelection( sal_Int32 nPaneId )
+{
+ PaneSelectionModelMap::mapped_type& rxPaneSel = maPaneSelMap[ nPaneId ];
+ if( !rxPaneSel )
+ rxPaneSel = std::make_shared<PaneSelectionModel>();
+ return *rxPaneSel;
+}
+
+SheetViewSettings::SheetViewSettings( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper )
+{
+}
+
+void SheetViewSettings::importSheetView( const AttributeList& rAttribs )
+{
+ SheetViewModel& rModel = *createSheetView();
+ rModel.maGridColor.setIndexed( rAttribs.getInteger( XML_colorId, OOX_COLOR_WINDOWTEXT ) );
+ rModel.maFirstPos = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_topLeftCell, OUString() ), getSheetIndex(), false );
+ rModel.mnWorkbookViewId = rAttribs.getToken( XML_workbookViewId, 0 );
+ rModel.mnViewType = rAttribs.getToken( XML_view, XML_normal );
+ rModel.mnCurrentZoom = rAttribs.getInteger( XML_zoomScale, 100 );
+ rModel.mnNormalZoom = rAttribs.getInteger( XML_zoomScaleNormal, 0 );
+ rModel.mnSheetLayoutZoom = rAttribs.getInteger( XML_zoomScaleSheetLayoutView, 0 );
+ rModel.mnPageLayoutZoom = rAttribs.getInteger( XML_zoomScalePageLayoutView, 0 );
+ rModel.mbSelected = rAttribs.getBool( XML_tabSelected, false );
+ rModel.mbRightToLeft = rAttribs.getBool( XML_rightToLeft, false );
+ rModel.mbDefGridColor = rAttribs.getBool( XML_defaultGridColor, true );
+ rModel.mbShowFormulas = rAttribs.getBool( XML_showFormulas, false );
+ rModel.mbShowGrid = rAttribs.getBool( XML_showGridLines, true );
+ rModel.mbShowHeadings = rAttribs.getBool( XML_showRowColHeaders, true );
+ rModel.mbShowZeros = rAttribs.getBool( XML_showZeros, true );
+ rModel.mbShowOutline = rAttribs.getBool( XML_showOutlineSymbols, true );
+}
+
+void SheetViewSettings::importPane( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importPane - missing sheet view model" );
+ if( !maSheetViews.empty() )
+ {
+ SheetViewModel& rModel = *maSheetViews.back();
+ rModel.maSecondPos = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_topLeftCell, OUString() ), getSheetIndex(), false );
+ rModel.mnActivePaneId = rAttribs.getToken( XML_activePane, XML_topLeft );
+ rModel.mnPaneState = rAttribs.getToken( XML_state, XML_split );
+ rModel.mfSplitX = rAttribs.getDouble( XML_xSplit, 0.0 );
+ rModel.mfSplitY = rAttribs.getDouble( XML_ySplit, 0.0 );
+ }
+}
+
+void SheetViewSettings::importSelection( const AttributeList& rAttribs )
+{
+ OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importSelection - missing sheet view model" );
+ if( !maSheetViews.empty() )
+ {
+ // pane this selection belongs to
+ sal_Int32 nPaneId = rAttribs.getToken( XML_pane, XML_topLeft );
+ PaneSelectionModel& rSelData = maSheetViews.back()->createPaneSelection( nPaneId );
+ // cursor position
+ rSelData.maActiveCell = getAddressConverter().createValidCellAddress( rAttribs.getString( XML_activeCell, OUString() ), getSheetIndex(), false );
+ rSelData.mnActiveCellId = rAttribs.getInteger( XML_activeCellId, 0 );
+ // selection
+ rSelData.maSelection.RemoveAll();
+ getAddressConverter().convertToCellRangeList( rSelData.maSelection, rAttribs.getString( XML_sqref, OUString() ), getSheetIndex(), false );
+ }
+}
+
+void SheetViewSettings::importChartSheetView( const AttributeList& rAttribs )
+{
+ SheetViewModel& rModel = *createSheetView();
+ rModel.mnWorkbookViewId = rAttribs.getToken( XML_workbookViewId, 0 );
+ rModel.mnCurrentZoom = rAttribs.getInteger( XML_zoomScale, 100 );
+ rModel.mbSelected = rAttribs.getBool( XML_tabSelected, false );
+ rModel.mbZoomToFit = rAttribs.getBool( XML_zoomToFit, false );
+}
+
+void SheetViewSettings::importSheetView( SequenceInputStream& rStrm )
+{
+ SheetViewModel& rModel = *createSheetView();
+ sal_uInt16 nFlags;
+ sal_Int32 nViewType;
+ BinAddress aFirstPos;
+ nFlags = rStrm.readuInt16();
+ nViewType = rStrm.readInt32();
+ rStrm >> aFirstPos;
+ rModel.maGridColor.importColorId( rStrm );
+ rModel.mnCurrentZoom = rStrm.readuInt16();
+ rModel.mnNormalZoom = rStrm.readuInt16();
+ rModel.mnSheetLayoutZoom = rStrm.readuInt16();
+ rModel.mnPageLayoutZoom = rStrm.readuInt16();
+ rModel.mnWorkbookViewId = rStrm.readInt32();
+
+ rModel.maFirstPos = getAddressConverter().createValidCellAddress( aFirstPos, getSheetIndex(), false );
+ static const sal_Int32 spnViewTypes[] = { XML_normal, XML_pageBreakPreview, XML_pageLayout };
+ rModel.mnViewType = STATIC_ARRAY_SELECT( spnViewTypes, nViewType, XML_normal );
+ rModel.mbSelected = getFlag( nFlags, BIFF12_SHEETVIEW_SELECTED );
+ rModel.mbRightToLeft = getFlag( nFlags, BIFF12_SHEETVIEW_RIGHTTOLEFT );
+ rModel.mbDefGridColor = getFlag( nFlags, BIFF12_SHEETVIEW_DEFGRIDCOLOR );
+ rModel.mbShowFormulas = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWFORMULAS );
+ rModel.mbShowGrid = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWGRID );
+ rModel.mbShowHeadings = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWHEADINGS );
+ rModel.mbShowZeros = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWZEROS );
+ rModel.mbShowOutline = getFlag( nFlags, BIFF12_SHEETVIEW_SHOWOUTLINE );
+}
+
+void SheetViewSettings::importPane( SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importPane - missing sheet view model" );
+ if( maSheetViews.empty() )
+ return;
+
+ SheetViewModel& rModel = *maSheetViews.back();
+
+ BinAddress aSecondPos;
+ sal_Int32 nActivePaneId;
+ sal_uInt8 nFlags;
+ rModel.mfSplitX = rStrm.readDouble();
+ rModel.mfSplitY = rStrm.readDouble();
+ rStrm >> aSecondPos;
+ nActivePaneId = rStrm.readInt32();
+ nFlags = rStrm.readuChar();
+
+ rModel.maSecondPos = getAddressConverter().createValidCellAddress( aSecondPos, getSheetIndex(), false );
+ rModel.mnActivePaneId = lclGetOoxPaneId( nActivePaneId, XML_topLeft );
+ rModel.mnPaneState = getFlagValue( nFlags, BIFF12_PANE_FROZEN, getFlagValue( nFlags, BIFF12_PANE_FROZENNOSPLIT, XML_frozen, XML_frozenSplit ), XML_split );
+}
+
+void SheetViewSettings::importSelection( SequenceInputStream& rStrm )
+{
+ OSL_ENSURE( !maSheetViews.empty(), "SheetViewSettings::importSelection - missing sheet view model" );
+ if( maSheetViews.empty() )
+ return;
+
+ // pane this selection belongs to
+ sal_Int32 nPaneId = rStrm.readInt32();
+ PaneSelectionModel& rPaneSel = maSheetViews.back()->createPaneSelection( lclGetOoxPaneId( nPaneId, -1 ) );
+ // cursor position
+ BinAddress aActiveCell;
+ rStrm >> aActiveCell;
+ rPaneSel.mnActiveCellId = rStrm.readInt32();
+ rPaneSel.maActiveCell = getAddressConverter().createValidCellAddress( aActiveCell, getSheetIndex(), false );
+ // selection
+ BinRangeList aSelection;
+ rStrm >> aSelection;
+ rPaneSel.maSelection.RemoveAll();
+ getAddressConverter().convertToCellRangeList( rPaneSel.maSelection, aSelection, getSheetIndex(), false );
+}
+
+void SheetViewSettings::importChartSheetView( SequenceInputStream& rStrm )
+{
+ SheetViewModel& rModel = *createSheetView();
+ sal_uInt16 nFlags;
+ nFlags = rStrm.readuInt16();
+ rModel.mnCurrentZoom = rStrm.readInt32();
+ rModel.mnWorkbookViewId = rStrm.readInt32();
+
+ rModel.mbSelected = getFlag( nFlags, BIFF12_CHARTSHEETVIEW_SELECTED );
+ rModel.mbZoomToFit = getFlag( nFlags, BIFF12_CHARTSHEETVIEW_ZOOMTOFIT );
+}
+
+void SheetViewSettings::finalizeImport()
+{
+ // force creation of sheet view model to get the Excel defaults
+ SheetViewModelRef xModel = maSheetViews.empty() ? createSheetView() : maSheetViews.front();
+
+ // #i59590# #158194# special handling for chart sheets (Excel ignores some settings in chart sheets)
+ if( getSheetType() == WorksheetType::Chart )
+ {
+ xModel->maPaneSelMap.clear();
+ xModel->maFirstPos = xModel->maSecondPos = ScAddress( SCCOL ( 0 ), SCROW ( 0 ), getSheetIndex() );
+ xModel->mnViewType = XML_normal;
+ xModel->mnActivePaneId = XML_topLeft;
+ xModel->mnPaneState = XML_split;
+ xModel->mfSplitX = xModel->mfSplitY = 0.0;
+ xModel->mbRightToLeft = false;
+ xModel->mbDefGridColor = true;
+ xModel->mbShowFormulas = false;
+ xModel->mbShowGrid = true;
+ xModel->mbShowHeadings = true;
+ xModel->mbShowZeros = true;
+ xModel->mbShowOutline = true;
+ }
+
+ // sheet selected (active sheet must be selected)
+ bool bSelected = xModel->mbSelected || (getSheetIndex() == getViewSettings().getActiveCalcSheet());
+ if ( bSelected )
+ {
+ // active tab/sheet cannot be hidden
+ // always force it to be displayed
+ PropertySet aPropSet( getSheet() );
+ aPropSet.setProperty( PROP_IsVisible, true );
+ }
+ // visible area and current cursor position (selection not supported via API)
+ ScAddress aFirstPos = xModel->maFirstPos;
+ const PaneSelectionModel* pPaneSel = xModel->getActiveSelection();
+ ScAddress aCursor = pPaneSel ? pPaneSel->maActiveCell : aFirstPos;
+
+ // freeze/split position default
+ sal_Int16 nHSplitMode = API_SPLITMODE_NONE;
+ sal_Int16 nVSplitMode = API_SPLITMODE_NONE;
+ sal_Int32 nHSplitPos = 0;
+ sal_Int32 nVSplitPos = 0;
+ // active pane default
+ sal_Int16 nActivePane = API_SPLITPANE_BOTTOMLEFT;
+
+ // freeze/split position
+ if( (xModel->mnPaneState == XML_frozen) || (xModel->mnPaneState == XML_frozenSplit) )
+ {
+ /* Frozen panes: handle split position as row/column positions.
+ #i35812# Excel uses number of visible rows/columns in the
+ frozen area (rows/columns scolled outside are not included),
+ Calc uses absolute position of first unfrozen row/column. */
+ const ScAddress& rMaxApiPos = getAddressConverter().getMaxApiAddress();
+ if( (xModel->mfSplitX >= 1.0) && ( xModel->maFirstPos.Col() + xModel->mfSplitX <= rMaxApiPos.Col() ) )
+ nHSplitPos = static_cast< sal_Int32 >( xModel->maFirstPos.Col() + xModel->mfSplitX );
+ nHSplitMode = (nHSplitPos > 0) ? API_SPLITMODE_FREEZE : API_SPLITMODE_NONE;
+ if( (xModel->mfSplitY >= 1.0) && ( xModel->maFirstPos.Row() + xModel->mfSplitY <= rMaxApiPos.Row() ) )
+ nVSplitPos = static_cast< sal_Int32 >( xModel->maFirstPos.Row() + xModel->mfSplitY );
+ nVSplitMode = (nVSplitPos > 0) ? API_SPLITMODE_FREEZE : API_SPLITMODE_NONE;
+ }
+ else if( xModel->mnPaneState == XML_split )
+ {
+ // split window: view settings API uses twips...
+ nHSplitPos = getLimitedValue< sal_Int32, double >( xModel->mfSplitX + 0.5, 0, SAL_MAX_INT32 );
+ nHSplitMode = (nHSplitPos > 0) ? API_SPLITMODE_SPLIT : API_SPLITMODE_NONE;
+ nVSplitPos = getLimitedValue< sal_Int32, double >( xModel->mfSplitY + 0.5, 0, SAL_MAX_INT32 );
+ nVSplitMode = (nVSplitPos > 0) ? API_SPLITMODE_SPLIT : API_SPLITMODE_NONE;
+ }
+
+ // active pane
+ switch( xModel->mnActivePaneId )
+ {
+ // no horizontal split -> always use left panes
+ // no vertical split -> always use *bottom* panes
+ case XML_topLeft:
+ nActivePane = (nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_TOPLEFT;
+ break;
+ case XML_topRight:
+ nActivePane = (nHSplitMode == API_SPLITMODE_NONE) ?
+ ((nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_TOPLEFT) :
+ ((nVSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMRIGHT : API_SPLITPANE_TOPRIGHT);
+ break;
+ case XML_bottomLeft:
+ nActivePane = API_SPLITPANE_BOTTOMLEFT;
+ break;
+ case XML_bottomRight:
+ nActivePane = (nHSplitMode == API_SPLITMODE_NONE) ? API_SPLITPANE_BOTTOMLEFT : API_SPLITPANE_BOTTOMRIGHT;
+ break;
+ }
+
+ // write the sheet view settings into the property sequence
+ PropertyMap aPropMap;
+ aPropMap.setProperty( PROP_TableSelected, bSelected);
+ aPropMap.setProperty( PROP_CursorPositionX, aCursor.Col() );
+ aPropMap.setProperty( PROP_CursorPositionY, aCursor.Row() );
+ aPropMap.setProperty( PROP_HorizontalSplitMode, nHSplitMode);
+ aPropMap.setProperty( PROP_VerticalSplitMode, nVSplitMode);
+ aPropMap.setProperty( PROP_HorizontalSplitPositionTwips, nHSplitPos);
+ aPropMap.setProperty( PROP_VerticalSplitPositionTwips, nVSplitPos);
+ aPropMap.setProperty( PROP_ActiveSplitRange, nActivePane);
+ aPropMap.setProperty( PROP_PositionLeft, aFirstPos.Col() );
+ aPropMap.setProperty( PROP_PositionTop, aFirstPos.Row() );
+ aPropMap.setProperty( PROP_PositionRight, xModel->maSecondPos.Col() );
+ aPropMap.setProperty( PROP_PositionBottom, ((nVSplitPos > 0) ? xModel->maSecondPos.Row() : xModel->maFirstPos.Row() ) );
+ aPropMap.setProperty( PROP_ZoomType, API_ZOOMTYPE_PERCENT);
+ aPropMap.setProperty( PROP_ZoomValue, static_cast< sal_Int16 >( xModel->getNormalZoom() ));
+ aPropMap.setProperty( PROP_PageViewZoomValue, static_cast< sal_Int16 >( xModel->getPageBreakZoom() ));
+ aPropMap.setProperty( PROP_GridColor, xModel->getGridColor( getBaseFilter() ));
+ aPropMap.setProperty( PROP_ShowPageBreakPreview, xModel->isPageBreakPreview());
+ aPropMap.setProperty( PROP_ShowFormulas, xModel->mbShowFormulas);
+ aPropMap.setProperty( PROP_ShowGrid, xModel->mbShowGrid);
+ aPropMap.setProperty( PROP_HasColumnRowHeaders, xModel->mbShowHeadings);
+ aPropMap.setProperty( PROP_ShowZeroValues, xModel->mbShowZeros);
+ aPropMap.setProperty( PROP_IsOutlineSymbolsSet, xModel->mbShowOutline);
+
+ // store sheet view settings in global view settings object
+ getViewSettings().setSheetViewSettings( getSheetIndex(), xModel, Any( aPropMap.makePropertyValueSequence() ) );
+}
+
+bool SheetViewSettings::isSheetRightToLeft() const
+{
+ return !maSheetViews.empty() && maSheetViews.front()->mbRightToLeft;
+}
+
+// private --------------------------------------------------------------------
+
+SheetViewModelRef SheetViewSettings::createSheetView()
+{
+ SheetViewModelRef xModel = std::make_shared<SheetViewModel>();
+ maSheetViews.push_back( xModel );
+ return xModel;
+}
+
+WorkbookViewModel::WorkbookViewModel() :
+ mnWinX( 0 ),
+ mnWinY( 0 ),
+ mnWinWidth( 0 ),
+ mnWinHeight( 0 ),
+ mnActiveSheet( 0 ),
+ mnFirstVisSheet( 0 ),
+ mnTabBarWidth( OOX_BOOKVIEW_TABBARRATIO_DEF ),
+ mnVisibility( XML_visible ),
+ mbShowTabBar( true ),
+ mbShowHorScroll( true ),
+ mbShowVerScroll( true ),
+ mbMinimized( false )
+{
+}
+
+ViewSettings::ViewSettings( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper ),
+ mbValidOleSize( false )
+{
+}
+
+void ViewSettings::importWorkbookView( const AttributeList& rAttribs )
+{
+ WorkbookViewModel& rModel = createWorkbookView();
+ rModel.mnWinX = rAttribs.getInteger( XML_xWindow, 0 );
+ rModel.mnWinY = rAttribs.getInteger( XML_yWindow, 0 );
+ rModel.mnWinWidth = rAttribs.getInteger( XML_windowWidth, 0 );
+ rModel.mnWinHeight = rAttribs.getInteger( XML_windowHeight, 0 );
+ rModel.mnActiveSheet = rAttribs.getInteger( XML_activeTab, 0 );
+ rModel.mnFirstVisSheet = rAttribs.getInteger( XML_firstSheet, 0 );
+ rModel.mnTabBarWidth = rAttribs.getInteger( XML_tabRatio, 600 );
+ rModel.mnVisibility = rAttribs.getToken( XML_visibility, XML_visible );
+ rModel.mbShowTabBar = rAttribs.getBool( XML_showSheetTabs, true );
+ rModel.mbShowHorScroll = rAttribs.getBool( XML_showHorizontalScroll, true );
+ rModel.mbShowVerScroll = rAttribs.getBool( XML_showVerticalScroll, true );
+ rModel.mbMinimized = rAttribs.getBool( XML_minimized, false );
+}
+
+void ViewSettings::importOleSize( const AttributeList& rAttribs )
+{
+ OUString aRange = rAttribs.getString( XML_ref, OUString() );
+ mbValidOleSize = getAddressConverter().convertToCellRange( maOleSize, aRange, 0, true, false );
+}
+
+void ViewSettings::importWorkbookView( SequenceInputStream& rStrm )
+{
+ WorkbookViewModel& rModel = createWorkbookView();
+ sal_uInt8 nFlags;
+ rModel.mnWinX = rStrm.readInt32();
+ rModel.mnWinY = rStrm.readInt32();
+ rModel.mnWinWidth = rStrm.readInt32();
+ rModel.mnWinHeight = rStrm.readInt32();
+ rModel.mnTabBarWidth = rStrm.readInt32();
+ rModel.mnFirstVisSheet = rStrm.readInt32();
+ rModel.mnActiveSheet = rStrm.readInt32();
+ nFlags = rStrm.readuChar();
+ rModel.mnVisibility = getFlagValue( nFlags, BIFF12_WBVIEW_HIDDEN, XML_hidden, XML_visible );
+ rModel.mbShowTabBar = getFlag( nFlags, BIFF12_WBVIEW_SHOWTABBAR );
+ rModel.mbShowHorScroll = getFlag( nFlags, BIFF12_WBVIEW_SHOWHORSCROLL );
+ rModel.mbShowVerScroll = getFlag( nFlags, BIFF12_WBVIEW_SHOWVERSCROLL );
+ rModel.mbMinimized = getFlag( nFlags, BIFF12_WBVIEW_MINIMIZED );
+}
+
+void ViewSettings::importOleSize( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ mbValidOleSize = getAddressConverter().convertToCellRange( maOleSize, aBinRange, 0, true, false );
+}
+
+void ViewSettings::setSheetViewSettings( sal_Int16 nSheet, const SheetViewModelRef& rxSheetView, const Any& rProperties )
+{
+ maSheetViews[ nSheet ] = rxSheetView;
+ maSheetProps[ nSheet ] = rProperties;
+}
+
+void ViewSettings::setSheetUsedArea( const ScRange& rUsedArea )
+{
+ assert( rUsedArea.IsValid() );
+ maSheetUsedAreas[ rUsedArea.aStart.Tab() ] = rUsedArea;
+}
+
+void ViewSettings::finalizeImport()
+{
+ const WorksheetBuffer& rWorksheets = getWorksheets();
+ if( rWorksheets.getWorksheetCount() <= 0 ) return;
+
+ // force creation of workbook view model to get the Excel defaults
+ const WorkbookViewModel& rModel = maBookViews.empty() ? createWorkbookView() : *maBookViews.front();
+
+ // show object mode is part of workbook settings
+ sal_Int16 nShowMode = getWorkbookSettings().getApiShowObjectMode();
+
+ // view settings for all sheets
+ Reference< XNameContainer > xSheetsNC = NamedPropertyValues::create( getBaseFilter().getComponentContext() );
+ if( !xSheetsNC.is() ) return;
+ for( const auto& [rWorksheet, rObj] : maSheetProps )
+ ContainerHelper::insertByName( xSheetsNC, rWorksheets.getCalcSheetName( rWorksheet ), rObj );
+
+ // use active sheet to set sheet properties that are document-global in Calc
+ sal_Int16 nActiveSheet = getActiveCalcSheet();
+ SheetViewModelRef& rxActiveSheetView = maSheetViews[ nActiveSheet ];
+ OSL_ENSURE( rxActiveSheetView, "ViewSettings::finalizeImport - missing active sheet view settings" );
+ if( !rxActiveSheetView )
+ rxActiveSheetView = std::make_shared<SheetViewModel>();
+
+ rtl::Reference< comphelper::IndexedPropertyValuesContainer > xContainer = new comphelper::IndexedPropertyValuesContainer();
+ try
+ {
+ PropertyMap aPropMap;
+ aPropMap.setProperty( PROP_Tables, xSheetsNC);
+ aPropMap.setProperty( PROP_ActiveTable, rWorksheets.getCalcSheetName( nActiveSheet ));
+ aPropMap.setProperty( PROP_HasHorizontalScrollBar, rModel.mbShowHorScroll);
+ aPropMap.setProperty( PROP_HasVerticalScrollBar, rModel.mbShowVerScroll);
+ aPropMap.setProperty( PROP_HasSheetTabs, rModel.mbShowTabBar);
+ aPropMap.setProperty( PROP_RelativeHorizontalTabbarWidth, double( rModel.mnTabBarWidth / 1000.0 ));
+ aPropMap.setProperty( PROP_ShowObjects, nShowMode);
+ aPropMap.setProperty( PROP_ShowCharts, nShowMode);
+ aPropMap.setProperty( PROP_ShowDrawing, nShowMode);
+ aPropMap.setProperty( PROP_GridColor, rxActiveSheetView->getGridColor( getBaseFilter() ));
+ aPropMap.setProperty( PROP_ShowPageBreakPreview, rxActiveSheetView->isPageBreakPreview());
+ aPropMap.setProperty( PROP_ShowFormulas, rxActiveSheetView->mbShowFormulas);
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ // tdf#126541 sheet based grid visibility shouldn't overwrite the global grid visibility
+ aPropMap.setProperty(PROP_ShowGrid, true);
+ }
+ else
+ {
+ // tdf#142854 except for headless mode, otherwise we could get a regression here:
+ // The sheet based grid visibility (bShowGrid) is stored in view settings. Headless
+ // mode means not to export view setting, including sheet based grid visibility.
+ // As the old workaround, use global visibility to keep the losing sheet visibility.
+ // FIXME: this only works correctly if all sheets have the same grid visibility.
+ // The sheet based bShowGrid should be moved to another location, which is supported
+ // by the headless mode, too.
+ aPropMap.setProperty(PROP_ShowGrid, rxActiveSheetView->mbShowGrid);
+ }
+ aPropMap.setProperty( PROP_HasColumnRowHeaders, rxActiveSheetView->mbShowHeadings);
+ aPropMap.setProperty( PROP_ShowZeroValues, rxActiveSheetView->mbShowZeros);
+ aPropMap.setProperty( PROP_IsOutlineSymbolsSet, rxActiveSheetView->mbShowOutline);
+
+ xContainer->insertByIndex( 0, Any( aPropMap.makePropertyValueSequence() ) );
+ Reference< XViewDataSupplier > xViewDataSuppl( getDocument(), UNO_QUERY_THROW );
+ xViewDataSuppl->setViewData( xContainer );
+ }
+ catch( Exception& )
+ {
+ OSL_FAIL( "ViewSettings::finalizeImport - cannot create document view settings" );
+ }
+
+ /* Set visible area to be used if this document is an embedded OLE object.
+ #i44077# If a new OLE object is inserted from file, there is no OLESIZE
+ record in the Excel file. In this case, use the used area calculated
+ from file contents (used cells and drawing objects). */
+ maOleSize.aStart.SetTab( nActiveSheet );
+ maOleSize.aEnd.SetTab( nActiveSheet );
+ const ScRange* pVisibleArea = mbValidOleSize ?
+ &maOleSize : ContainerHelper::getMapElement( maSheetUsedAreas, nActiveSheet );
+ if( !pVisibleArea )
+ return;
+
+ // calculate the visible area in units of 1/100 mm
+ PropertySet aRangeProp( getCellRangeFromDoc( *pVisibleArea ) );
+ css::awt::Point aPos;
+ css::awt::Size aSize;
+ if( aRangeProp.getProperty( aPos, PROP_Position ) && aRangeProp.getProperty( aSize, PROP_Size ) )
+ {
+ // set the visible area as sequence of long at the media descriptor
+ Sequence< sal_Int32 > aWinExtent{ aPos.X, aPos.Y,
+ aPos.X + aSize.Width, aPos.Y + aSize.Height };
+ getBaseFilter().getMediaDescriptor()[ "WinExtent" ] <<= aWinExtent;
+ }
+}
+
+sal_Int16 ViewSettings::getActiveCalcSheet() const
+{
+ return maBookViews.empty() ? 0 : ::std::max< sal_Int16 >( getWorksheets().getCalcSheetIndex( maBookViews.front()->mnActiveSheet ), 0 );
+}
+
+// private --------------------------------------------------------------------
+
+WorkbookViewModel& ViewSettings::createWorkbookView()
+{
+ WorkbookViewModelRef xModel = std::make_shared<WorkbookViewModel>();
+ maBookViews.push_back( xModel );
+ return *xModel;
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/workbookfragment.cxx b/sc/source/filter/oox/workbookfragment.cxx
new file mode 100644
index 000000000..4a99b4230
--- /dev/null
+++ b/sc/source/filter/oox/workbookfragment.cxx
@@ -0,0 +1,641 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <workbookfragment.hxx>
+
+#include <oox/core/filterbase.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/drawingml/themefragmenthandler.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/progressbar.hxx>
+#include <oox/ole/olestorage.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+
+#include <chartsheetfragment.hxx>
+#include <connectionsfragment.hxx>
+#include <externallinkbuffer.hxx>
+#include <externallinkfragment.hxx>
+#include <formulabuffer.hxx>
+#include <pivotcachebuffer.hxx>
+#include <sharedstringsfragment.hxx>
+#include <revisionfragment.hxx>
+#include <stylesfragment.hxx>
+#include <tablebuffer.hxx>
+#include <themebuffer.hxx>
+#include <viewsettings.hxx>
+#include <workbooksettings.hxx>
+#include <worksheetbuffer.hxx>
+#include <worksheethelper.hxx>
+#include <worksheetfragment.hxx>
+#include <extlstcontext.hxx>
+#include <documentimport.hxx>
+#include <biffhelper.hxx>
+
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <docsh.hxx>
+#include <calcconfig.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <formulaopt.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/weld.hxx>
+
+#include <oox/core/fastparser.hxx>
+#include <comphelper/threadpool.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+
+#include <oox/ole/vbaproject.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <officecfg/Office/Calc.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+using namespace ::oox::core;
+
+using ::oox::drawingml::ThemeFragmentHandler;
+
+namespace {
+
+const double PROGRESS_LENGTH_GLOBALS = 0.1; /// 10% of progress bar for globals import.
+
+} // namespace
+
+WorkbookFragment::WorkbookFragment( const WorkbookHelper& rHelper, const OUString& rFragmentPath ) :
+ WorkbookFragmentBase( rHelper, rFragmentPath )
+{
+}
+
+ContextHandlerRef WorkbookFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nElement == XLS_TOKEN( workbook ) ) return this;
+ break;
+
+ case XLS_TOKEN( workbook ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheets ):
+ case XLS_TOKEN( bookViews ):
+ case XLS_TOKEN( externalReferences ):
+ case XLS_TOKEN( definedNames ):
+ case XLS_TOKEN( pivotCaches ): return this;
+
+ case XLS_TOKEN( fileSharing ): getWorkbookSettings().importFileSharing( rAttribs ); break;
+ case XLS_TOKEN( workbookPr ): getWorkbookSettings().importWorkbookPr( rAttribs ); break;
+ case XLS_TOKEN( calcPr ): getWorkbookSettings().importCalcPr( rAttribs ); break;
+ case XLS_TOKEN( oleSize ): getViewSettings().importOleSize( rAttribs ); break;
+
+ case XLS_TOKEN( extLst ): return new ExtLstGlobalWorkbookContext( *this );
+ }
+ break;
+
+ case XLS_TOKEN( sheets ):
+ if( nElement == XLS_TOKEN( sheet ) ) getWorksheets().importSheet( rAttribs );
+ break;
+ case XLS_TOKEN( bookViews ):
+ if( nElement == XLS_TOKEN( workbookView ) ) getViewSettings().importWorkbookView( rAttribs );
+ break;
+ case XLS_TOKEN( externalReferences ):
+ if( nElement == XLS_TOKEN( externalReference ) ) importExternalReference( rAttribs );
+ break;
+ case XLS_TOKEN( definedNames ):
+ if( nElement == XLS_TOKEN( definedName ) ) { importDefinedName( rAttribs ); return this; } // collect formula
+ break;
+ case XLS_TOKEN( pivotCaches ):
+ if( nElement == XLS_TOKEN( pivotCache ) ) importPivotCache( rAttribs );
+ break;
+ }
+ return nullptr;
+}
+
+void WorkbookFragment::onCharacters( const OUString& rChars )
+{
+ if( isCurrentElement( XLS_TOKEN( definedName ) ) && mxCurrName )
+ mxCurrName->setFormula( rChars );
+}
+
+ContextHandlerRef WorkbookFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_WORKBOOK ) return this;
+ break;
+
+ case BIFF12_ID_WORKBOOK:
+ switch( nRecId )
+ {
+ case BIFF12_ID_SHEETS:
+ case BIFF12_ID_BOOKVIEWS:
+ case BIFF12_ID_EXTERNALREFS:
+ case BIFF12_ID_PIVOTCACHES: return this;
+
+ case BIFF12_ID_FILESHARING: getWorkbookSettings().importFileSharing( rStrm ); break;
+ case BIFF12_ID_WORKBOOKPR: getWorkbookSettings().importWorkbookPr( rStrm ); break;
+ case BIFF12_ID_CALCPR: getWorkbookSettings().importCalcPr( rStrm ); break;
+ case BIFF12_ID_OLESIZE: getViewSettings().importOleSize( rStrm ); break;
+ case BIFF12_ID_DEFINEDNAME: getDefinedNames().importDefinedName( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_SHEETS:
+ if( nRecId == BIFF12_ID_SHEET ) getWorksheets().importSheet( rStrm );
+ break;
+ case BIFF12_ID_BOOKVIEWS:
+ if( nRecId == BIFF12_ID_WORKBOOKVIEW ) getViewSettings().importWorkbookView( rStrm );
+ break;
+
+ case BIFF12_ID_EXTERNALREFS:
+ switch( nRecId )
+ {
+ case BIFF12_ID_EXTERNALREF: importExternalRef( rStrm ); break;
+ case BIFF12_ID_EXTERNALSELF: getExternalLinks().importExternalSelf( rStrm ); break;
+ case BIFF12_ID_EXTERNALSAME: getExternalLinks().importExternalSame( rStrm ); break;
+ case BIFF12_ID_EXTERNALADDIN: getExternalLinks().importExternalAddin( rStrm ); break;
+ case BIFF12_ID_EXTERNALSHEETS: getExternalLinks().importExternalSheets( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_PIVOTCACHES:
+ if( nRecId == BIFF12_ID_PIVOTCACHE ) importPivotCache( rStrm );
+ }
+ return nullptr;
+}
+
+const RecordInfo* WorkbookFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_BOOKVIEWS, BIFF12_ID_BOOKVIEWS + 1 },
+ { BIFF12_ID_EXTERNALREFS, BIFF12_ID_EXTERNALREFS + 1 },
+ { BIFF12_ID_FUNCTIONGROUPS, BIFF12_ID_FUNCTIONGROUPS + 2 },
+ { BIFF12_ID_PIVOTCACHE, BIFF12_ID_PIVOTCACHE + 1 },
+ { BIFF12_ID_PIVOTCACHES, BIFF12_ID_PIVOTCACHES + 1 },
+ { BIFF12_ID_SHEETS, BIFF12_ID_SHEETS + 1 },
+ { BIFF12_ID_WORKBOOK, BIFF12_ID_WORKBOOK + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+namespace {
+
+typedef std::pair<WorksheetGlobalsRef, FragmentHandlerRef> SheetFragmentHandler;
+typedef std::vector<SheetFragmentHandler> SheetFragmentVector;
+
+class WorkerThread : public comphelper::ThreadTask
+{
+ sal_Int32 &mrSheetsLeft;
+ WorkbookFragment& mrWorkbookHandler;
+ rtl::Reference<FragmentHandler> mxHandler;
+
+public:
+ WorkerThread( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
+ WorkbookFragment& rWorkbookHandler,
+ const rtl::Reference<FragmentHandler>& xHandler,
+ sal_Int32 &rSheetsLeft ) :
+ comphelper::ThreadTask( pTag ),
+ mrSheetsLeft( rSheetsLeft ),
+ mrWorkbookHandler( rWorkbookHandler ),
+ mxHandler( xHandler )
+ {
+ }
+
+ virtual void doWork() override
+ {
+ // We hold the solar mutex in all threads except for
+ // the small safe section of the inner loop in
+ // sheetdatacontext.cxx
+ SAL_INFO( "sc.filter", "start wait on solar" );
+ SolarMutexGuard aGuard;
+ SAL_INFO( "sc.filter", "got solar" );
+
+ std::unique_ptr<oox::core::FastParser> xParser(
+ oox::core::XmlFilterBase::createParser() );
+
+ SAL_INFO( "sc.filter", "start import" );
+ mrWorkbookHandler.importOoxFragment( mxHandler, *xParser );
+ SAL_INFO( "sc.filter", "end import, release solar" );
+ mrSheetsLeft--;
+ assert( mrSheetsLeft >= 0 );
+ if( mrSheetsLeft == 0 )
+ Application::EndYield();
+ }
+};
+
+class ProgressBarTimer : private Timer
+{
+ // FIXME: really we should unify all sheet loading
+ // progress reporting into something pleasant.
+ class ProgressWrapper : public ISegmentProgressBar
+ {
+ double mfPosition;
+ ISegmentProgressBarRef mxWrapped;
+ public:
+ explicit ProgressWrapper(const ISegmentProgressBarRef &xRef)
+ : mfPosition(0.0)
+ , mxWrapped(xRef)
+ {
+ }
+
+ // IProgressBar
+ virtual double getPosition() const override { return mfPosition; }
+ virtual void setPosition( double fPosition ) override { mfPosition = fPosition; }
+ // ISegmentProgressBar
+ virtual double getFreeLength() const override { return 0.0; }
+ virtual ISegmentProgressBarRef createSegment( double /* fLength */ ) override
+ {
+ return ISegmentProgressBarRef();
+ }
+ void UpdateBar()
+ {
+ mxWrapped->setPosition( mfPosition );
+ }
+ };
+ std::vector< ISegmentProgressBarRef > aSegments;
+public:
+ ProgressBarTimer() : Timer("sc ProgressBarTimer")
+ {
+ SetTimeout( 500 );
+ }
+ virtual ~ProgressBarTimer() override
+ {
+ aSegments.clear();
+ }
+ const ISegmentProgressBarRef& wrapProgress( const ISegmentProgressBarRef &xProgress )
+ {
+ aSegments.push_back( std::make_shared<ProgressWrapper>( xProgress ) );
+ return aSegments.back();
+ }
+ virtual void Invoke() override
+ {
+ for(std::shared_ptr<ISegmentProgressBar> & pSegment : aSegments)
+ static_cast< ProgressWrapper *>( pSegment.get() )->UpdateBar();
+ }
+};
+
+void importSheetFragments( WorkbookFragment& rWorkbookHandler, SheetFragmentVector& rSheets )
+{
+ rWorkbookHandler.getDocImport().initForSheets();
+
+ // test sequential read in this mode
+ comphelper::ThreadPool &rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ sal_Int32 nSheetsLeft = 0;
+ ProgressBarTimer aProgressUpdater;
+ for( auto& [rxSheetGlob, rxFragment] : rSheets )
+ {
+ // getting at the WorksheetGlobals is rather unpleasant
+ IWorksheetProgress *pProgress = WorksheetHelper::getWorksheetInterface( rxSheetGlob );
+ pProgress->setCustomRowProgress(
+ aProgressUpdater.wrapProgress(
+ pProgress->getRowProgress() ) );
+ rSharedPool.pushTask( std::make_unique<WorkerThread>( pTag, rWorkbookHandler, rxFragment,
+ /* ref */ nSheetsLeft ) );
+ nSheetsLeft++;
+ }
+
+ // coverity[loop_top] - this isn't an infinite loop where nSheetsLeft gets decremented by the above threads
+ while( nSheetsLeft > 0 && !Application::IsQuit())
+ {
+ // This is a much more controlled re-enterancy hazard than
+ // allowing a yield deeper inside the filter code for progress
+ // bar updating.
+ Application::Yield();
+ }
+ rSharedPool.waitUntilDone(pTag);
+
+ // threads joined in ThreadPool destructor
+}
+
+}
+
+void WorkbookFragment::finalizeImport()
+{
+ ISegmentProgressBarRef xGlobalSegment = getProgressBar().createSegment( PROGRESS_LENGTH_GLOBALS );
+
+ // read the theme substream
+ OUString aThemeFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"theme" );
+ if( !aThemeFragmentPath.isEmpty() )
+ importOoxFragment( new ThemeFragmentHandler( getFilter(), aThemeFragmentPath, getTheme() ) );
+ xGlobalSegment->setPosition( 0.25 );
+
+ // read the styles substream (requires finalized theme buffer)
+ OUString aStylesFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"styles" );
+ if( !aStylesFragmentPath.isEmpty() )
+ importOoxFragment( new StylesFragment( *this, aStylesFragmentPath ) );
+ xGlobalSegment->setPosition( 0.5 );
+
+ // read the shared string table substream (requires finalized styles buffer)
+ OUString aSstFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"sharedStrings" );
+ if( !aSstFragmentPath.isEmpty() )
+ if (!importOoxFragment( new SharedStringsFragment( *this, aSstFragmentPath ) ))
+ importOoxFragment(new SharedStringsFragment(*this, aSstFragmentPath.replaceFirst("sharedStrings","SharedStrings")));
+ xGlobalSegment->setPosition( 0.75 );
+
+ // read the connections substream
+ OUString aConnFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"connections" );
+ if( !aConnFragmentPath.isEmpty() )
+ importOoxFragment( new ConnectionsFragment( *this, aConnFragmentPath ) );
+ xGlobalSegment->setPosition( 1.0 );
+
+ /* Create fragments for all sheets, before importing them. Needed to do
+ some preprocessing in the fragment constructors, e.g. loading the table
+ fragments for all sheets that are needed before the cell formulas are
+ loaded. Additionally, the instances of the WorkbookGlobals structures
+ have to be stored for every sheet. */
+ SheetFragmentVector aSheetFragments;
+ std::vector<WorksheetHelper*> aHelpers;
+ WorksheetBuffer& rWorksheets = getWorksheets();
+ sal_Int32 nWorksheetCount = rWorksheets.getWorksheetCount();
+ for( sal_Int32 nWorksheet = 0; nWorksheet < nWorksheetCount; ++nWorksheet )
+ {
+ sal_Int16 nCalcSheet = rWorksheets.getCalcSheetIndex( nWorksheet );
+ const Relation* pRelation = getRelations().getRelationFromRelId( rWorksheets.getWorksheetRelId( nWorksheet ) );
+ if( (nCalcSheet >= 0) && pRelation )
+ {
+ // get fragment path of the sheet
+ OUString aFragmentPath = getFragmentPathFromRelation( *pRelation );
+ OSL_ENSURE( !aFragmentPath.isEmpty(), "WorkbookFragment::finalizeImport - cannot access sheet fragment" );
+ if( !aFragmentPath.isEmpty() )
+ {
+ // leave space for formula processing ( calculate the segments as
+ // if there is an extra sheet )
+ double fSegmentLength = getProgressBar().getFreeLength() / (nWorksheetCount - ( nWorksheet - 1) );
+ ISegmentProgressBarRef xSheetSegment = getProgressBar().createSegment( fSegmentLength );
+
+ // get the sheet type according to the relations type
+ WorksheetType eSheetType = WorksheetType::Empty;
+ if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "worksheet" ) ||
+ pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT( "worksheet" ))
+ eSheetType = WorksheetType::Work;
+ else if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "chartsheet" ) ||
+ pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT( "chartsheet" ))
+ eSheetType = WorksheetType::Chart;
+ else if( (pRelation->maType == CREATE_MSOFFICE_RELATION_TYPE( "xlMacrosheet" )) ||
+ (pRelation->maType == CREATE_MSOFFICE_RELATION_TYPE( "xlIntlMacrosheet" )) )
+ eSheetType = WorksheetType::Macro;
+ else if( pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE( "dialogsheet" ) ||
+ pRelation->maType == CREATE_OFFICEDOC_RELATION_TYPE_STRICT(" dialogsheet" ))
+ eSheetType = WorksheetType::Dialog;
+ OSL_ENSURE( eSheetType != WorksheetType::Empty, "WorkbookFragment::finalizeImport - unknown sheet type" );
+ if( eSheetType != WorksheetType::Empty )
+ {
+ // create the WorksheetGlobals object
+ WorksheetGlobalsRef xSheetGlob = WorksheetHelper::constructGlobals( *this, xSheetSegment, eSheetType, nCalcSheet );
+ OSL_ENSURE( xSheetGlob, "WorkbookFragment::finalizeImport - missing sheet in document" );
+ if( xSheetGlob )
+ {
+ // create the sheet fragment handler
+ ::rtl::Reference< WorksheetFragmentBase > xFragment;
+ switch( eSheetType )
+ {
+ case WorksheetType::Work:
+ case WorksheetType::Macro:
+ case WorksheetType::Dialog:
+ xFragment.set( new WorksheetFragment( *xSheetGlob, aFragmentPath ) );
+ break;
+ case WorksheetType::Chart:
+ xFragment.set( new ChartsheetFragment( *xSheetGlob, aFragmentPath ) );
+ break;
+ // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
+ case WorksheetType::Empty:
+ break;
+ }
+
+ // insert the fragment into the map
+ if( xFragment.is() )
+ {
+ aSheetFragments.emplace_back( xSheetGlob, xFragment.get() );
+ aHelpers.push_back(xFragment.get());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // setup structure sizes for the number of sheets
+ getFormulaBuffer().SetSheetCount( nWorksheetCount );
+
+ // create all database ranges and defined names, in that order
+ getTables().finalizeImport();
+ getTables().applyTableColumns();
+ getDefinedNames().finalizeImport();
+ // open the VBA project storage
+ OUString aVbaFragmentPath = getFragmentPathFromFirstType( CREATE_MSOFFICE_RELATION_TYPE( "vbaProject" ) );
+ if( !aVbaFragmentPath.isEmpty() )
+ {
+ Reference< XInputStream > xInStrm = getBaseFilter().openInputStream( aVbaFragmentPath );
+ if( xInStrm.is() )
+ {
+ StorageRef xPrjStrg = std::make_shared<::oox::ole::OleStorage>( getBaseFilter().getComponentContext(), xInStrm, false );
+ setVbaProjectStorage( xPrjStrg );
+ getBaseFilter().getVbaProject().readVbaModules( *xPrjStrg );
+ }
+ }
+
+ // lock the model to prevent broadcasting, speeds up load a lot
+ getScDocument().InitDrawLayer();
+ auto pModel = getScDocument().GetDrawLayer();
+ bool bWasLocked = pModel->isLocked();
+ pModel->setLock(true);
+
+ // load all worksheets
+ importSheetFragments(*this, aSheetFragments);
+
+ sal_Int16 nActiveSheet = getViewSettings().getActiveCalcSheet();
+ getWorksheets().finalizeImport( nActiveSheet );
+
+ // final conversions, e.g. calculation settings and view settings
+ finalizeWorkbookImport();
+
+ //stop preventing establishment of listeners as is done in
+ //ScDocShell::AfterXMLLoading() for ods
+ getScDocument().SetInsertingFromOtherDoc(false);
+
+ for( WorksheetHelper* pHelper : aHelpers )
+ {
+ pHelper->finalizeDrawingImport();
+ }
+
+ for( auto& [rxSheetGlob, rxFragment] : aSheetFragments )
+ {
+ // delete fragment object and WorkbookGlobals object, will free all allocated sheet buffers
+ rxFragment.clear();
+ rxSheetGlob.reset();
+ }
+
+ getDocImport().finalize();
+
+ recalcFormulaCells();
+
+ OUString aRevHeadersPath = getFragmentPathFromFirstType(CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders"));
+ if (!aRevHeadersPath.isEmpty())
+ {
+ std::unique_ptr<oox::core::FastParser> xParser(oox::core::XmlFilterBase::createParser());
+ rtl::Reference<oox::core::FragmentHandler> xFragment(new RevisionHeadersFragment(*this, aRevHeadersPath));
+ importOoxFragment(xFragment, *xParser);
+ }
+
+ // attach macros to registered objects now that all objects have been created.
+ getBaseFilter().getVbaProject().attachMacros();
+
+ pModel->setLock(bWasLocked);
+}
+
+namespace {
+
+ScDocShell& getDocShell(const ScDocument& rDoc)
+{
+ return static_cast<ScDocShell&>(*rDoc.GetDocumentShell());
+}
+
+class MessageWithCheck : public weld::MessageDialogController
+{
+private:
+ std::unique_ptr<weld::CheckButton> m_xWarningOnBox;
+public:
+ MessageWithCheck(weld::Window *pParent, const OUString& rUIFile, const OString& rDialogId)
+ : MessageDialogController(pParent, rUIFile, rDialogId, "ask")
+ , m_xWarningOnBox(m_xBuilder->weld_check_button("ask"))
+ {
+ }
+ bool get_active() const { return m_xWarningOnBox->get_active(); }
+ void hide_ask() const { m_xWarningOnBox->set_visible(false); };
+};
+
+}
+
+void WorkbookFragment::recalcFormulaCells()
+{
+ // Recalculate formula cells.
+ ScDocument& rDoc = getScDocument();
+ ScDocShell& rDocSh = getDocShell(rDoc);
+ ScRecalcOptions nRecalcMode =
+ static_cast<ScRecalcOptions>(officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::get());
+ bool bHardRecalc = false;
+ if (nRecalcMode == RECALC_ASK)
+ {
+ if (rDoc.IsUserInteractionEnabled())
+ {
+ // Ask the user if full re-calculation is desired.
+ MessageWithCheck aQueryBox(ScDocShell::GetActiveDialogParent(), "modules/scalc/ui/recalcquerydialog.ui", "RecalcQueryDialog");
+ aQueryBox.set_primary_text(ScResId(STR_QUERY_FORMULA_RECALC_ONLOAD_XLS));
+ aQueryBox.set_default_response(RET_YES);
+
+ if ( officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::isReadOnly() )
+ aQueryBox.hide_ask();
+
+ bHardRecalc = aQueryBox.run() == RET_YES;
+
+ if (aQueryBox.get_active())
+ {
+ // Always perform selected action in the future.
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Calc::Formula::Load::OOXMLRecalcMode::set(sal_Int32(0), batch);
+ ScFormulaOptions aOpt = SC_MOD()->GetFormulaOptions();
+ aOpt.SetOOXMLRecalcOptions(bHardRecalc ? RECALC_ALWAYS : RECALC_NEVER);
+ /* XXX is this really supposed to set the ScModule options?
+ * Not the ScDocShell options? */
+ SC_MOD()->SetFormulaOptions(aOpt);
+
+ batch->commit();
+ }
+ }
+ }
+ else if (nRecalcMode == RECALC_ALWAYS)
+ bHardRecalc = true;
+
+ if (bHardRecalc)
+ rDocSh.DoHardRecalc();
+ else
+ {
+ getDocImport().broadcastRecalcAfterImport();
+ // Full ScDocument::CalcFormulaTree() of all dirty cells is not
+ // necessary here, the View will recalculate them in the visible area,
+ // or any other method accessing formula cell results.
+ }
+}
+
+// private --------------------------------------------------------------------
+
+void WorkbookFragment::importExternalReference( const AttributeList& rAttribs )
+{
+ if( ExternalLink* pExtLink = getExternalLinks().importExternalReference( rAttribs ).get() )
+ importExternalLinkFragment( *pExtLink );
+}
+
+void WorkbookFragment::importDefinedName( const AttributeList& rAttribs )
+{
+ mxCurrName = getDefinedNames().importDefinedName( rAttribs );
+}
+
+void WorkbookFragment::importPivotCache( const AttributeList& rAttribs )
+{
+ sal_Int32 nCacheId = rAttribs.getInteger( XML_cacheId, -1 );
+ OUString aRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ importPivotCacheDefFragment( aRelId, nCacheId );
+}
+
+void WorkbookFragment::importExternalRef( SequenceInputStream& rStrm )
+{
+ if( ExternalLink* pExtLink = getExternalLinks().importExternalRef( rStrm ).get() )
+ importExternalLinkFragment( *pExtLink );
+}
+
+void WorkbookFragment::importPivotCache( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCacheId = rStrm.readInt32();
+ OUString aRelId = BiffHelper::readString( rStrm );
+ importPivotCacheDefFragment( aRelId, nCacheId );
+}
+
+void WorkbookFragment::importExternalLinkFragment( ExternalLink& rExtLink )
+{
+ OUString aFragmentPath = getFragmentPathFromRelId( rExtLink.getRelId() );
+ if( !aFragmentPath.isEmpty() )
+ importOoxFragment( new ExternalLinkFragment( *this, aFragmentPath, rExtLink ) );
+}
+
+void WorkbookFragment::importPivotCacheDefFragment( const OUString& rRelId, sal_Int32 nCacheId )
+{
+ // pivot caches will be imported on demand, here we just store the fragment path in the buffer
+ getPivotCaches().registerPivotCacheFragment( nCacheId, getFragmentPathFromRelId( rRelId ) );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/workbookhelper.cxx b/sc/source/filter/oox/workbookhelper.cxx
new file mode 100644
index 000000000..1f07567f1
--- /dev/null
+++ b/sc/source/filter/oox/workbookhelper.cxx
@@ -0,0 +1,1036 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <workbookhelper.hxx>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/sheet/XDatabaseRanges.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XViewDataSupplier.hpp>
+#include <o3tl/any.hxx>
+#include <osl/thread.h>
+#include <osl/diagnose.h>
+#include <oox/helper/progressbar.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/ole/vbaproject.hxx>
+#include <oox/token/properties.hxx>
+#include <addressconverter.hxx>
+#include <connectionsbuffer.hxx>
+#include <defnamesbuffer.hxx>
+#include <excelchartconverter.hxx>
+#include <excelfilter.hxx>
+#include <externallinkbuffer.hxx>
+#include <formulaparser.hxx>
+#include <pagesettings.hxx>
+#include <pivotcachebuffer.hxx>
+#include <pivottablebuffer.hxx>
+#include <scenariobuffer.hxx>
+#include <sharedstringsbuffer.hxx>
+#include <stylesbuffer.hxx>
+#include <tablebuffer.hxx>
+#include <themebuffer.hxx>
+#include <unitconverter.hxx>
+#include <viewsettings.hxx>
+#include <workbooksettings.hxx>
+#include <worksheetbuffer.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <docuno.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <tokenuno.hxx>
+#include <dbdata.hxx>
+#include <datauno.hxx>
+#include <globalnames.hxx>
+#include <documentimport.hxx>
+#include <drwlayer.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <formulabuffer.hxx>
+#include <editutil.hxx>
+#include <editeng/editstat.hxx>
+#include <unotools/charclass.hxx>
+#include <o3tl/string_view.hxx>
+#include <ViewSettingsSequenceDefines.hxx>
+
+#include <memory>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+using ::oox::core::FilterBase;
+using ::oox::core::FragmentHandler;
+using ::oox::core::XmlFilterBase;
+
+bool IgnoreCaseCompare::operator()( std::u16string_view rName1, std::u16string_view rName2 ) const
+{
+ // TODO: compare with collator
+ return o3tl::compareToIgnoreAsciiCase(rName1, rName2 ) < 0;
+}
+
+class WorkbookGlobals
+{
+public:
+ // noncopyable ------------------------------------------------------------
+
+ WorkbookGlobals(const WorkbookGlobals&) = delete;
+ const WorkbookGlobals& operator=(const WorkbookGlobals&) = delete;
+
+ explicit WorkbookGlobals( ExcelFilter& rFilter );
+ ~WorkbookGlobals();
+
+ /** Returns true, if this helper refers to a valid document. */
+ bool isValid() const { return mxDoc.is(); }
+
+ // filter -----------------------------------------------------------------
+
+ /** Returns the base filter object (base class of all filters). */
+ FilterBase& getBaseFilter() const { return mrBaseFilter; }
+ /** Returns the filter progress bar. */
+ SegmentProgressBar& getProgressBar() const { return *mxProgressBar; }
+ /** Returns the VBA project storage. */
+ const StorageRef& getVbaProjectStorage() const { return mxVbaPrjStrg; }
+ /** Returns the index of the current Calc sheet, if filter currently processes a sheet. */
+ sal_Int16 getCurrentSheetIndex() const { return mnCurrSheet; }
+ /** Returns true when reading a file generated by a known good generator. */
+ bool isGeneratorKnownGood() const { return mbGeneratorKnownGood; }
+
+ /** Sets the VBA project storage used to import VBA source code and forms. */
+ void setVbaProjectStorage( const StorageRef& rxVbaPrjStrg ) { mxVbaPrjStrg = rxVbaPrjStrg; }
+ /** Sets the index of the current Calc sheet, if filter currently processes a sheet. */
+ void setCurrentSheetIndex( SCTAB nSheet ) { mnCurrSheet = nSheet; }
+
+ // document model ---------------------------------------------------------
+
+ ScEditEngineDefaulter& getEditEngine() const
+ {
+ return *mxEditEngine;
+ }
+
+ ScDocument& getScDocument() { return *mpDoc; }
+
+ ScDocumentImport& getDocImport();
+
+ /** Returns a reference to the source/target spreadsheet document model. */
+ const Reference< XSpreadsheetDocument >& getDocument() const { return mxDoc; }
+ /** Returns the cell or page styles container from the Calc document. */
+ Reference< XNameContainer > getStyleFamily( bool bPageStyles ) const;
+ /** Returns the specified cell or page style from the Calc document. */
+ Reference< XStyle > getStyleObject( const OUString& rStyleName, bool bPageStyle ) const;
+ /** Creates and returns a defined name on-the-fly in the Calc document. */
+ WorkbookHelper::RangeDataRet createNamedRangeObject( OUString& orName, const Sequence< FormulaToken>& rTokens, sal_Int32 nIndex, sal_Int32 nNameFlags, bool bHidden );
+ /** Creates and returns a defined name on the-fly in the correct Calc sheet. */
+ WorkbookHelper::RangeDataRet createLocalNamedRangeObject( OUString& orName, const Sequence< FormulaToken>& rTokens, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab, bool bHidden );
+ /** Creates and returns a database range on-the-fly in the Calc document. */
+ Reference< XDatabaseRange > createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr );
+ /** Creates and returns an unnamed database range on-the-fly in the Calc document. */
+ Reference< XDatabaseRange > createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr );
+ /** Finds the (already existing) database range of the given formula token index. */
+ ScDBData* findDatabaseRangeByIndex( sal_uInt16 nIndex );
+ /** Creates and returns a com.sun.star.style.Style object for cells or pages. */
+ Reference< XStyle > createStyleObject( OUString& orStyleName, bool bPageStyle );
+ /** Helper to switch chart data table - specifically for xlsx imports */
+ void useInternalChartDataTable( bool bInternal );
+
+ // buffers ----------------------------------------------------------------
+
+ FormulaBuffer& getFormulaBuffer() const { return *mxFormulaBuffer; }
+ /** Returns the global workbook settings object. */
+ WorkbookSettings& getWorkbookSettings() const { return *mxWorkbookSettings; }
+ /** Returns the workbook and sheet view settings object. */
+ ViewSettings& getViewSettings() const { return *mxViewSettings; }
+ /** Returns the worksheet buffer containing sheet names and properties. */
+ WorksheetBuffer& getWorksheets() const { return *mxWorksheets; }
+ /** Returns the office theme object read from the theme substorage. */
+ ThemeBuffer& getTheme() const { return *mxTheme; }
+ /** Returns all cell formatting objects read from the styles substream. */
+ StylesBuffer& getStyles() const { return *mxStyles; }
+ /** Returns the shared strings read from the shared strings substream. */
+ SharedStringsBuffer& getSharedStrings() const { return *mxSharedStrings; }
+ /** Returns the external links read from the external links substream. */
+ ExternalLinkBuffer& getExternalLinks() const { return *mxExtLinks; }
+ /** Returns the defined names read from the workbook globals. */
+ DefinedNamesBuffer& getDefinedNames() const { return *mxDefNames; }
+ /** Returns the tables collection (equivalent to Calc's database ranges). */
+ TableBuffer& getTables() const { return *mxTables; }
+ /** Returns the scenarios collection. */
+ ScenarioBuffer& getScenarios() const { return *mxScenarios; }
+ /** Returns the collection of external data connections. */
+ ConnectionsBuffer& getConnections() const { return *mxConnections; }
+ /** Returns the collection of pivot caches. */
+ PivotCacheBuffer& getPivotCaches() const { return *mxPivotCaches; }
+ /** Returns the collection of pivot tables. */
+ PivotTableBuffer& getPivotTables() { return *mxPivotTables; }
+
+ // converters -------------------------------------------------------------
+
+ /** Returns a shared import formula parser. */
+ FormulaParser& getFormulaParser() const { return *mxFmlaParser; }
+ /** Returns an unshared import formula parser. */
+ FormulaParser* createFormulaParser() { return new FormulaParser(*this); }
+ /** Returns the measurement unit converter. */
+ UnitConverter& getUnitConverter() const { return *mxUnitConverter; }
+ /** Returns the converter for string to cell address/range conversion. */
+ AddressConverter& getAddressConverter() const { return *mxAddrConverter; }
+ /** Returns the chart object converter. */
+ oox::drawingml::chart::ChartConverter* getChartConverter() const { return mxChartConverter.get(); }
+ /** Returns the page/print settings converter. */
+ PageSettingsConverter& getPageSettingsConverter() const { return *mxPageSettConverter; }
+
+ // OOXML/BIFF12 specific --------------------------------------------------
+
+ /** Returns the base OOXML/BIFF12 filter object. */
+ XmlFilterBase& getOoxFilter() const { return *mpOoxFilter; }
+
+ // BIFF2-BIFF8 specific ---------------------------------------------------
+
+ /** Returns the text encoding used to import/export byte strings. */
+ rtl_TextEncoding getTextEncoding() const { return meTextEnc; }
+
+private:
+ /** Initializes some basic members and sets needed document properties. */
+ void initialize();
+ /** Finalizes the filter process (sets some needed document properties). */
+ void finalize();
+
+private:
+ typedef ::std::unique_ptr< ScEditEngineDefaulter > EditEngineDefaulterPtr;
+ typedef ::std::unique_ptr< FormulaBuffer > FormulaBufferPtr;
+ typedef ::std::unique_ptr< SegmentProgressBar > ProgressBarPtr;
+ typedef ::std::unique_ptr< WorkbookSettings > WorkbookSettPtr;
+ typedef ::std::unique_ptr< ViewSettings > ViewSettingsPtr;
+ typedef ::std::unique_ptr< WorksheetBuffer > WorksheetBfrPtr;
+ typedef ::std::shared_ptr< ThemeBuffer > ThemeBfrRef;
+ typedef ::std::unique_ptr< StylesBuffer > StylesBfrPtr;
+ typedef ::std::unique_ptr< SharedStringsBuffer > SharedStrBfrPtr;
+ typedef ::std::unique_ptr< ExternalLinkBuffer > ExtLinkBfrPtr;
+ typedef ::std::unique_ptr< DefinedNamesBuffer > DefNamesBfrPtr;
+ typedef ::std::unique_ptr< TableBuffer > TableBfrPtr;
+ typedef ::std::unique_ptr< ScenarioBuffer > ScenarioBfrPtr;
+ typedef ::std::unique_ptr< ConnectionsBuffer > ConnectionsBfrPtr;
+ typedef ::std::unique_ptr< PivotCacheBuffer > PivotCacheBfrPtr;
+ typedef ::std::unique_ptr< PivotTableBuffer > PivotTableBfrPtr;
+ typedef ::std::unique_ptr< FormulaParser > FormulaParserPtr;
+ typedef ::std::unique_ptr< UnitConverter > UnitConvPtr;
+ typedef ::std::unique_ptr< AddressConverter > AddressConvPtr;
+ typedef ::std::unique_ptr< oox::drawingml::chart::ChartConverter > ExcelChartConvPtr;
+ typedef ::std::unique_ptr< PageSettingsConverter > PageSettConvPtr;
+
+ OUString maCellStyles; /// Style family name for cell styles.
+ OUString maPageStyles; /// Style family name for page styles.
+ OUString maCellStyleServ; /// Service name for a cell style.
+ OUString maPageStyleServ; /// Service name for a page style.
+ Reference< XSpreadsheetDocument > mxDoc; /// Document model.
+ FilterBase& mrBaseFilter; /// Base filter object.
+ ExcelFilter& mrExcelFilter; /// Base object for registration of this structure.
+ ProgressBarPtr mxProgressBar; /// The progress bar.
+ StorageRef mxVbaPrjStrg; /// Storage containing the VBA project.
+ sal_Int16 mnCurrSheet; /// Current sheet index in Calc document.
+ bool mbGeneratorKnownGood; /// Whether reading a file generated by Excel or Calc.
+
+ // buffers
+ FormulaBufferPtr mxFormulaBuffer;
+ WorkbookSettPtr mxWorkbookSettings; /// Global workbook settings.
+ ViewSettingsPtr mxViewSettings; /// Workbook and sheet view settings.
+ WorksheetBfrPtr mxWorksheets; /// Sheet info buffer.
+ ThemeBfrRef mxTheme; /// Formatting theme from theme substream.
+ StylesBfrPtr mxStyles; /// All cell style objects from styles substream.
+ SharedStrBfrPtr mxSharedStrings; /// All strings from shared strings substream.
+ ExtLinkBfrPtr mxExtLinks; /// All external links.
+ DefNamesBfrPtr mxDefNames; /// All defined names.
+ TableBfrPtr mxTables; /// All tables (database ranges).
+ ScenarioBfrPtr mxScenarios; /// All scenarios.
+ ConnectionsBfrPtr mxConnections; /// All external data connections.
+ PivotCacheBfrPtr mxPivotCaches; /// All pivot caches in the document.
+ PivotTableBfrPtr mxPivotTables; /// All pivot tables in the document.
+
+ // converters
+ FormulaParserPtr mxFmlaParser; /// Import formula parser.
+ UnitConvPtr mxUnitConverter; /// General unit converter.
+ AddressConvPtr mxAddrConverter; /// Cell address and cell range address converter.
+ ExcelChartConvPtr mxChartConverter; /// Chart object converter.
+ PageSettConvPtr mxPageSettConverter; /// Page/print settings converter.
+
+ EditEngineDefaulterPtr mxEditEngine;
+
+ // OOXML/BIFF12 specific
+ XmlFilterBase* mpOoxFilter; /// Base OOXML/BIFF12 filter object.
+
+ // BIFF2-BIFF8 specific
+ rtl_TextEncoding meTextEnc; /// BIFF byte string text encoding.
+ ScDocument* mpDoc;
+ ScDocShell* mpDocShell;
+ std::unique_ptr<ScDocumentImport> mxDocImport;
+};
+
+WorkbookGlobals::WorkbookGlobals( ExcelFilter& rFilter ) :
+ mrBaseFilter( rFilter ),
+ mrExcelFilter( rFilter ),
+ mpOoxFilter( &rFilter ),
+ mpDoc(nullptr),
+ mpDocShell(nullptr)
+{
+ // register at the filter, needed for virtual callbacks (even during construction)
+ mrExcelFilter.registerWorkbookGlobals( *this );
+ initialize();
+}
+
+WorkbookGlobals::~WorkbookGlobals()
+{
+ finalize();
+ mrExcelFilter.unregisterWorkbookGlobals();
+}
+
+ScDocumentImport& WorkbookGlobals::getDocImport()
+{
+ return *mxDocImport;
+}
+
+Reference< XNameContainer > WorkbookGlobals::getStyleFamily( bool bPageStyles ) const
+{
+ Reference< XNameContainer > xStylesNC;
+ try
+ {
+ Reference< XStyleFamiliesSupplier > xFamiliesSup( mxDoc, UNO_QUERY_THROW );
+ Reference< XNameAccess > xFamiliesNA( xFamiliesSup->getStyleFamilies(), UNO_SET_THROW );
+ xStylesNC.set( xFamiliesNA->getByName( bPageStyles ? maPageStyles : maCellStyles ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xStylesNC.is(), "WorkbookGlobals::getStyleFamily - cannot access style family" );
+ return xStylesNC;
+}
+
+Reference< XStyle > WorkbookGlobals::getStyleObject( const OUString& rStyleName, bool bPageStyle ) const
+{
+ Reference< XStyle > xStyle;
+ try
+ {
+ Reference< XNameContainer > xStylesNC( getStyleFamily( bPageStyle ), UNO_SET_THROW );
+ xStyle.set( xStylesNC->getByName( rStyleName ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xStyle.is(), "WorkbookGlobals::getStyleObject - cannot access style object" );
+ return xStyle;
+}
+
+namespace {
+
+WorkbookHelper::RangeDataRet lcl_addNewByNameAndTokens( ScDocument& rDoc, ScRangeName* pNames, const OUString& rName, const Sequence<FormulaToken>& rTokens, sal_Int16 nIndex, sal_Int32 nUnoType, bool bHidden )
+{
+ bool bDone = false;
+ ScRangeData::Type nNewType = ScRangeData::Type::Name;
+ if ( nUnoType & NamedRangeFlag::FILTER_CRITERIA ) nNewType |= ScRangeData::Type::Criteria;
+ if ( nUnoType & NamedRangeFlag::PRINT_AREA ) nNewType |= ScRangeData::Type::PrintArea;
+ if ( nUnoType & NamedRangeFlag::COLUMN_HEADER ) nNewType |= ScRangeData::Type::ColHeader;
+ if ( nUnoType & NamedRangeFlag::ROW_HEADER ) nNewType |= ScRangeData::Type::RowHeader;
+ ScTokenArray aTokenArray(rDoc);
+ (void)ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, rTokens );
+ ScRangeData* pNew = new ScRangeData( rDoc, rName, aTokenArray, ScAddress(), nNewType );
+ pNew->GuessPosition();
+ if ( nIndex )
+ pNew->SetIndex( nIndex );
+ // create but not insert hidden FILTER_CRITERIA named ranges to ScRangeName
+ if ( bHidden && nNewType == ScRangeData::Type::Criteria )
+ {
+ return WorkbookHelper::RangeDataRet(pNew, true);
+ }
+ if ( pNames->insert(pNew) )
+ bDone = true;
+ if (!bDone)
+ {
+ delete pNew;
+ throw RuntimeException();
+ }
+ return WorkbookHelper::RangeDataRet(pNew, false);
+}
+
+OUString findUnusedName( const ScRangeName* pRangeName, const OUString& rSuggestedName )
+{
+ OUString aNewName = rSuggestedName;
+ sal_Int32 nIndex = 0;
+ while(pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(aNewName)))
+ aNewName = rSuggestedName + OUStringChar('_') + OUString::number( nIndex++ );
+
+ return aNewName;
+}
+
+}
+
+WorkbookHelper::RangeDataRet WorkbookGlobals::createNamedRangeObject(
+ OUString& orName, const Sequence< FormulaToken>& rTokens, sal_Int32 nIndex, sal_Int32 nNameFlags, bool bHidden )
+{
+ // create the name and insert it into the Calc document
+ WorkbookHelper::RangeDataRet aScRangeData(nullptr, false);
+ if( !orName.isEmpty() )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScRangeName* pNames = rDoc.GetRangeName();
+ // find an unused name
+ orName = findUnusedName( pNames, orName );
+ // create the named range
+ aScRangeData = lcl_addNewByNameAndTokens( rDoc, pNames, orName, rTokens, nIndex, nNameFlags, bHidden );
+ }
+ return aScRangeData;
+}
+
+WorkbookHelper::RangeDataRet WorkbookGlobals::createLocalNamedRangeObject(
+ OUString& orName, const Sequence< FormulaToken >& rTokens, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab, bool bHidden )
+{
+ // create the name and insert it into the Calc document
+ WorkbookHelper::RangeDataRet aScRangeData(nullptr, false);
+ if( !orName.isEmpty() )
+ {
+ ScDocument& rDoc = getScDocument();
+ ScRangeName* pNames = rDoc.GetRangeName( nTab );
+ if(!pNames)
+ throw RuntimeException("invalid sheet index used");
+ // find an unused name
+ orName = findUnusedName( pNames, orName );
+ // create the named range
+ aScRangeData = lcl_addNewByNameAndTokens( rDoc, pNames, orName, rTokens, nIndex, nNameFlags, bHidden );
+ }
+ return aScRangeData;
+}
+
+Reference< XDatabaseRange > WorkbookGlobals::createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr )
+{
+ // validate cell range
+ ScRange aDestRange = rRangeAddr;
+ bool bValidRange = getAddressConverter().validateCellRange( aDestRange, true, true );
+
+ // create database range and insert it into the Calc document
+ Reference< XDatabaseRange > xDatabaseRange;
+ if( bValidRange && !orName.isEmpty() ) try
+ {
+ // find an unused name
+ PropertySet aDocProps( mxDoc );
+ Reference< XDatabaseRanges > xDatabaseRanges( aDocProps.getAnyProperty( PROP_DatabaseRanges ), UNO_QUERY_THROW );
+ orName = ContainerHelper::getUnusedName( xDatabaseRanges, orName, '_' );
+ // create the database range
+ CellRangeAddress aApiRange( aDestRange.aStart.Tab(), aDestRange.aStart.Col(), aDestRange.aStart.Row(),
+ aDestRange.aEnd.Col(), aDestRange.aEnd.Row() );
+ xDatabaseRanges->addNewByName( orName, aApiRange );
+ xDatabaseRange.set( xDatabaseRanges->getByName( orName ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xDatabaseRange.is(), "WorkbookGlobals::createDatabaseRangeObject - cannot create database range" );
+ return xDatabaseRange;
+}
+
+Reference< XDatabaseRange > WorkbookGlobals::createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr )
+{
+ // validate cell range
+ ScRange aDestRange = rRangeAddr;
+ bool bValidRange = getAddressConverter().validateCellRange( aDestRange, true, true );
+
+ // create database range and insert it into the Calc document
+ Reference< XDatabaseRange > xDatabaseRange;
+ if( bValidRange ) try
+ {
+ ScDocument& rDoc = getScDocument();
+ if( rDoc.GetTableCount() <= aDestRange.aStart.Tab() )
+ throw css::lang::IndexOutOfBoundsException();
+ std::unique_ptr<ScDBData> pNewDBData(new ScDBData( STR_DB_LOCAL_NONAME, aDestRange.aStart.Tab(),
+ aDestRange.aStart.Col(), aDestRange.aStart.Row(),
+ aDestRange.aEnd.Col(), aDestRange.aEnd.Row() ));
+ rDoc.SetAnonymousDBData( aDestRange.aStart.Tab() , std::move(pNewDBData) );
+ ScDocShell* pDocSh = static_cast< ScDocShell* >(rDoc.GetDocumentShell());
+ xDatabaseRange.set(new ScDatabaseRangeObj(pDocSh, aDestRange.aStart.Tab()));
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xDatabaseRange.is(), "WorkbookData::createDatabaseRangeObject - cannot create database range" );
+ return xDatabaseRange;
+}
+
+ScDBData* WorkbookGlobals::findDatabaseRangeByIndex( sal_uInt16 nIndex )
+{
+ ScDBCollection* pDBCollection = getScDocument().GetDBCollection();
+ if (!pDBCollection)
+ return nullptr;
+ return pDBCollection->getNamedDBs().findByIndex( nIndex );
+}
+
+Reference< XStyle > WorkbookGlobals::createStyleObject( OUString& orStyleName, bool bPageStyle )
+{
+ Reference< XStyle > xStyle;
+ try
+ {
+ Reference< XNameContainer > xStylesNC( getStyleFamily( bPageStyle ), UNO_SET_THROW );
+ xStyle.set( mrBaseFilter.getModelFactory()->createInstance( bPageStyle ? maPageStyleServ : maCellStyleServ ), UNO_QUERY_THROW );
+ orStyleName = ContainerHelper::insertByUnusedName( xStylesNC, orStyleName, ' ', Any( xStyle ) );
+ }
+ catch( Exception& )
+ {
+ }
+ OSL_ENSURE( xStyle.is(), "WorkbookGlobals::createStyleObject - cannot create style" );
+ return xStyle;
+}
+
+void WorkbookGlobals::useInternalChartDataTable( bool bInternal )
+{
+ if( bInternal )
+ mxChartConverter.reset( new oox::drawingml::chart::ChartConverter() );
+ else
+ mxChartConverter.reset( new ExcelChartConverter( *this ) );
+}
+
+// BIFF specific --------------------------------------------------------------
+
+// private --------------------------------------------------------------------
+
+void WorkbookGlobals::initialize()
+{
+ maCellStyles = "CellStyles";
+ maPageStyles = "PageStyles";
+ maCellStyleServ = "com.sun.star.style.CellStyle";
+ maPageStyleServ = "com.sun.star.style.PageStyle";
+ mnCurrSheet = -1;
+ mbGeneratorKnownGood = false;
+ meTextEnc = osl_getThreadTextEncoding();
+
+ // the spreadsheet document
+ mxDoc.set( mrBaseFilter.getModel(), UNO_QUERY );
+ OSL_ENSURE( mxDoc.is(), "WorkbookGlobals::initialize - no spreadsheet document" );
+
+ if (mxDoc)
+ {
+ ScModelObj* pModel = dynamic_cast<ScModelObj*>(mxDoc.get());
+ if (pModel)
+ mpDocShell = static_cast<ScDocShell*>(pModel->GetEmbeddedObject());
+ if (mpDocShell)
+ mpDoc = &mpDocShell->GetDocument();
+ }
+
+ if (!mpDoc)
+ throw RuntimeException("Workbookhelper::getScDocument(): Failed to access ScDocument from model");
+
+ Reference< XDocumentPropertiesSupplier > xPropSupplier( mxDoc, UNO_QUERY);
+ Reference< XDocumentProperties > xDocProps = xPropSupplier->getDocumentProperties();
+ const OUString aGenerator( xDocProps->getGenerator());
+
+ if (aGenerator.startsWithIgnoreAsciiCase("Microsoft"))
+ {
+ mbGeneratorKnownGood = true;
+ ScCalcConfig aCalcConfig = mpDoc->GetCalcConfig();
+ aCalcConfig.SetStringRefSyntax( formula::FormulaGrammar::CONV_XL_A1 ) ;
+ mpDoc->SetCalcConfig(aCalcConfig);
+ }
+ else if (aGenerator.startsWithIgnoreAsciiCase("LibreOffice"))
+ {
+ mbGeneratorKnownGood = true;
+ }
+
+ mxDocImport.reset(new ScDocumentImport(*mpDoc));
+
+ mxFormulaBuffer.reset( new FormulaBuffer( *this ) );
+ mxWorkbookSettings.reset( new WorkbookSettings( *this ) );
+ mxViewSettings.reset( new ViewSettings( *this ) );
+ mxWorksheets.reset( new WorksheetBuffer( *this ) );
+ mxTheme = std::make_shared<ThemeBuffer>( *this );
+ mxStyles.reset( new StylesBuffer( *this ) );
+ mxSharedStrings.reset( new SharedStringsBuffer( *this ) );
+ mxExtLinks.reset( new ExternalLinkBuffer( *this ) );
+ mxDefNames.reset( new DefinedNamesBuffer( *this ) );
+ mxTables.reset( new TableBuffer( *this ) );
+ mxScenarios.reset( new ScenarioBuffer( *this ) );
+ mxConnections.reset( new ConnectionsBuffer( *this ) );
+ mxPivotCaches.reset( new PivotCacheBuffer( *this ) );
+ mxPivotTables.reset( new PivotTableBuffer( *this ) );
+
+ mxUnitConverter.reset( new UnitConverter( *this ) );
+ mxAddrConverter.reset( new AddressConverter( *this ) );
+ mxChartConverter.reset( new ExcelChartConverter( *this ) );
+ mxPageSettConverter.reset( new PageSettingsConverter( *this ) );
+
+ // initialise edit engine
+ ScDocument& rDoc = getScDocument();
+ mxEditEngine.reset( new ScEditEngineDefaulter( rDoc.GetEnginePool() ) );
+ mxEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ mxEditEngine->SetEditTextObjectPool( rDoc.GetEditPool() );
+ mxEditEngine->SetUpdateLayout( false );
+ mxEditEngine->EnableUndo( false );
+ mxEditEngine->SetControlWord( mxEditEngine->GetControlWord() & ~EEControlBits::ALLOWBIGOBJS );
+
+ // set some document properties needed during import
+ if( mrBaseFilter.isImportFilter() )
+ {
+ // enable editing read-only documents (e.g. from read-only files)
+ mpDoc->EnableChangeReadOnly(true);
+ // #i76026# disable Undo while loading the document
+ mpDoc->EnableUndo(false);
+ // #i79826# disable calculating automatic row height while loading the document
+ mpDoc->LockAdjustHeight();
+ // disable automatic update of linked sheets and DDE links
+ mpDoc->EnableExecuteLink(false);
+
+ mxProgressBar.reset( new SegmentProgressBar( mrBaseFilter.getStatusIndicator(), ScResId(STR_LOAD_DOC) ) );
+ mxFmlaParser.reset( createFormulaParser() );
+
+ //prevent unnecessary broadcasts and "half way listeners" as
+ //is done in ScDocShell::BeforeXMLLoading() for ods
+ mpDoc->SetInsertingFromOtherDoc(true);
+ }
+ else if( mrBaseFilter.isExportFilter() )
+ {
+ mxProgressBar.reset( new SegmentProgressBar( mrBaseFilter.getStatusIndicator(), ScResId(STR_SAVE_DOC) ) );
+ }
+}
+
+void WorkbookGlobals::finalize()
+{
+ // set some document properties needed after import
+ if( !mrBaseFilter.isImportFilter() )
+ return;
+
+ // #i74668# do not insert default sheets
+ mpDocShell->SetEmpty(false);
+ // enable automatic update of linked sheets and DDE links
+ mpDoc->EnableExecuteLink(true);
+ // #i79826# enable updating automatic row height after loading the document
+ mpDoc->UnlockAdjustHeight();
+
+ // #i76026# enable Undo after loading the document
+ mpDoc->EnableUndo(true);
+
+ // disable editing read-only documents (e.g. from read-only files)
+ mpDoc->EnableChangeReadOnly(false);
+ // #111099# open forms in alive mode (has no effect, if no controls in document)
+ ScDrawLayer* pModel = mpDoc->GetDrawLayer();
+ if (pModel)
+ pModel->SetOpenInDesignMode(false);
+}
+
+
+WorkbookHelper::~WorkbookHelper()
+{
+}
+
+/*static*/ WorkbookGlobalsRef WorkbookHelper::constructGlobals( ExcelFilter& rFilter )
+{
+ WorkbookGlobalsRef xBookGlob = std::make_shared<WorkbookGlobals>( rFilter );
+ if( !xBookGlob->isValid() )
+ xBookGlob.reset();
+ return xBookGlob;
+}
+
+// filter ---------------------------------------------------------------------
+
+FilterBase& WorkbookHelper::getBaseFilter() const
+{
+ return mrBookGlob.getBaseFilter();
+}
+
+SegmentProgressBar& WorkbookHelper::getProgressBar() const
+{
+ return mrBookGlob.getProgressBar();
+}
+
+sal_Int16 WorkbookHelper::getCurrentSheetIndex() const
+{
+ return mrBookGlob.getCurrentSheetIndex();
+}
+
+bool WorkbookHelper::isGeneratorKnownGood() const
+{
+ return mrBookGlob.isGeneratorKnownGood();
+}
+
+void WorkbookHelper::setVbaProjectStorage( const StorageRef& rxVbaPrjStrg )
+{
+ mrBookGlob.setVbaProjectStorage( rxVbaPrjStrg );
+}
+
+void WorkbookHelper::setCurrentSheetIndex( SCTAB nSheet )
+{
+ mrBookGlob.setCurrentSheetIndex( nSheet );
+}
+
+void WorkbookHelper::finalizeWorkbookImport()
+{
+ // workbook settings, document and sheet view settings
+ mrBookGlob.getWorkbookSettings().finalizeImport();
+ mrBookGlob.getViewSettings().finalizeImport();
+
+ // Import the VBA project (after finalizing workbook settings which
+ // contains the workbook code name). Do it before processing formulas in
+ // order to correctly resolve VBA custom function names.
+ StorageRef xVbaPrjStrg = mrBookGlob.getVbaProjectStorage();
+ if( xVbaPrjStrg && xVbaPrjStrg->isStorage() )
+ getBaseFilter().getVbaProject().importModulesAndForms( *xVbaPrjStrg, getBaseFilter().getGraphicHelper() );
+
+ // need to import formulas before scenarios
+ mrBookGlob.getFormulaBuffer().finalizeImport();
+
+ // Insert all pivot tables. Must be done after loading all sheets and
+ // formulas, because data pilots expect existing source data on
+ // creation.
+ getPivotTables().finalizeImport();
+
+ /* Insert scenarios after all sheet processing is done, because new hidden
+ sheets are created for scenarios which would confuse code that relies
+ on certain sheet indexes. Must be done after pivot tables too. */
+ mrBookGlob.getScenarios().finalizeImport();
+
+ /* Set 'Default' page style to automatic page numbering (default is manual
+ number 1). Otherwise hidden sheets (e.g. for scenarios) which have
+ 'Default' page style will break automatic page numbering for following
+ sheets. Automatic numbering is set by passing the value 0. */
+ PropertySet aDefPageStyle( getStyleObject( "Default", true ) );
+ aDefPageStyle.setProperty< sal_Int16 >( PROP_FirstPageNumber, 0 );
+
+ // Has any string ref syntax been imported?
+ // If not, we need to take action
+ ScCalcConfig aCalcConfig = getScDocument().GetCalcConfig();
+
+ if ( !aCalcConfig.mbHasStringRefSyntax )
+ {
+ aCalcConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_A1_XL_A1;
+ getScDocument().SetCalcConfig(aCalcConfig);
+ }
+
+ // set selected sheet and positionleft/positiontop for OLE objects
+ Reference<XViewDataSupplier> xViewDataSupplier(getDocument(), UNO_QUERY);
+ if (!xViewDataSupplier.is())
+ return;
+
+ Reference<XIndexAccess> xIndexAccess(xViewDataSupplier->getViewData());
+ if (!(xIndexAccess.is() && xIndexAccess->getCount() > 0))
+ return;
+
+ Sequence< PropertyValue > aSeq;
+ if (!(xIndexAccess->getByIndex(0) >>= aSeq))
+ return;
+
+ OUString sTabName;
+ Reference< XNameAccess > xSheetsNC;
+ for (const auto& rProp : std::as_const(aSeq))
+ {
+ OUString sName(rProp.Name);
+ if (sName == SC_ACTIVETABLE)
+ {
+ if(rProp.Value >>= sTabName)
+ {
+ SCTAB nTab(0);
+ if (getScDocument().GetTable(sTabName, nTab))
+ getScDocument().SetVisibleTab(nTab);
+ }
+ }
+ else if (sName == SC_TABLES)
+ {
+ rProp.Value >>= xSheetsNC;
+ }
+ }
+ if (!(xSheetsNC.is() && xSheetsNC->hasByName(sTabName)))
+ return;
+
+ Sequence<PropertyValue> aProperties;
+ Any aAny = xSheetsNC->getByName(sTabName);
+ if ( !(aAny >>= aProperties) )
+ return;
+
+ for (const auto& rProp : std::as_const(aProperties))
+ {
+ OUString sName(rProp.Name);
+ if (sName == SC_POSITIONLEFT)
+ {
+ SCCOL nPosLeft = *o3tl::doAccess<SCCOL>(rProp.Value);
+ getScDocument().SetPosLeft(nPosLeft);
+ }
+ else if (sName == SC_POSITIONTOP)
+ {
+ SCROW nPosTop = *o3tl::doAccess<SCROW>(rProp.Value);
+ getScDocument().SetPosTop(nPosTop);
+ }
+ }
+}
+
+// document model -------------------------------------------------------------
+
+ScDocument& WorkbookHelper::getScDocument()
+{
+ return mrBookGlob.getScDocument();
+}
+
+const ScDocument& WorkbookHelper::getScDocument() const
+{
+ return mrBookGlob.getScDocument();
+}
+
+ScDocumentImport& WorkbookHelper::getDocImport()
+{
+ return mrBookGlob.getDocImport();
+}
+
+const ScDocumentImport& WorkbookHelper::getDocImport() const
+{
+ return mrBookGlob.getDocImport();
+}
+
+ScEditEngineDefaulter& WorkbookHelper::getEditEngine() const
+{
+ return mrBookGlob.getEditEngine();
+}
+
+const Reference< XSpreadsheetDocument > & WorkbookHelper::getDocument() const
+{
+ return mrBookGlob.getDocument();
+}
+
+Reference< XSpreadsheet > WorkbookHelper::getSheetFromDoc( sal_Int32 nSheet ) const
+{
+ Reference< XSpreadsheet > xSheet;
+ try
+ {
+ Reference< XIndexAccess > xSheetsIA( getDocument()->getSheets(), UNO_QUERY_THROW );
+ xSheet.set( xSheetsIA->getByIndex( nSheet ), UNO_QUERY_THROW );
+ }
+ catch( Exception& )
+ {
+ }
+ return xSheet;
+}
+
+Reference< XSpreadsheet > WorkbookHelper::getSheetFromDoc( const OUString& rSheet ) const
+{
+ Reference< XSpreadsheet > xSheet;
+ try
+ {
+ Reference< XNameAccess > xSheetsNA( getDocument()->getSheets(), UNO_QUERY_THROW );
+ xSheet.set( xSheetsNA->getByName( rSheet ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xSheet;
+}
+
+Reference< XCellRange > WorkbookHelper::getCellRangeFromDoc( const ScRange& rRange ) const
+{
+ Reference< XCellRange > xRange;
+ try
+ {
+ Reference< XSpreadsheet > xSheet( getSheetFromDoc( rRange.aStart.Tab() ), UNO_SET_THROW );
+ xRange = xSheet->getCellRangeByPosition( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRange;
+}
+
+Reference< XNameContainer > WorkbookHelper::getCellStyleFamily() const
+{
+ return mrBookGlob.getStyleFamily( false/*bPageStyles*/ );
+}
+
+Reference< XStyle > WorkbookHelper::getStyleObject( const OUString& rStyleName, bool bPageStyle ) const
+{
+ return mrBookGlob.getStyleObject( rStyleName, bPageStyle );
+}
+
+WorkbookHelper::RangeDataRet WorkbookHelper::createNamedRangeObject( OUString& orName, const Sequence< FormulaToken>& rTokens, sal_Int32 nIndex, sal_Int32 nNameFlags, bool bHidden ) const
+{
+ return mrBookGlob.createNamedRangeObject( orName, rTokens, nIndex, nNameFlags, bHidden );
+}
+
+WorkbookHelper::RangeDataRet WorkbookHelper::createLocalNamedRangeObject( OUString& orName, const Sequence< FormulaToken>& rTokens, sal_Int32 nIndex, sal_Int32 nNameFlags, sal_Int32 nTab, bool bHidden ) const
+{
+ return mrBookGlob.createLocalNamedRangeObject( orName, rTokens, nIndex, nNameFlags, nTab, bHidden );
+}
+
+Reference< XDatabaseRange > WorkbookHelper::createDatabaseRangeObject( OUString& orName, const ScRange& rRangeAddr ) const
+{
+ return mrBookGlob.createDatabaseRangeObject( orName, rRangeAddr );
+}
+
+Reference< XDatabaseRange > WorkbookHelper::createUnnamedDatabaseRangeObject( const ScRange& rRangeAddr ) const
+{
+ return mrBookGlob.createUnnamedDatabaseRangeObject( rRangeAddr );
+}
+
+ScDBData* WorkbookHelper::findDatabaseRangeByIndex( sal_uInt16 nIndex ) const
+{
+ return mrBookGlob.findDatabaseRangeByIndex( nIndex );
+}
+
+Reference< XStyle > WorkbookHelper::createStyleObject( OUString& orStyleName, bool bPageStyle ) const
+{
+ return mrBookGlob.createStyleObject( orStyleName, bPageStyle );
+}
+
+// buffers --------------------------------------------------------------------
+
+FormulaBuffer& WorkbookHelper::getFormulaBuffer() const
+{
+ return mrBookGlob.getFormulaBuffer();
+}
+
+WorkbookSettings& WorkbookHelper::getWorkbookSettings() const
+{
+ return mrBookGlob.getWorkbookSettings();
+}
+
+ViewSettings& WorkbookHelper::getViewSettings() const
+{
+ return mrBookGlob.getViewSettings();
+}
+
+WorksheetBuffer& WorkbookHelper::getWorksheets() const
+{
+ return mrBookGlob.getWorksheets();
+}
+
+ThemeBuffer& WorkbookHelper::getTheme() const
+{
+ return mrBookGlob.getTheme();
+}
+
+StylesBuffer& WorkbookHelper::getStyles() const
+{
+ return mrBookGlob.getStyles();
+}
+
+SharedStringsBuffer& WorkbookHelper::getSharedStrings() const
+{
+ return mrBookGlob.getSharedStrings();
+}
+
+ExternalLinkBuffer& WorkbookHelper::getExternalLinks() const
+{
+ return mrBookGlob.getExternalLinks();
+}
+
+DefinedNamesBuffer& WorkbookHelper::getDefinedNames() const
+{
+ return mrBookGlob.getDefinedNames();
+}
+
+TableBuffer& WorkbookHelper::getTables() const
+{
+ return mrBookGlob.getTables();
+}
+
+ScenarioBuffer& WorkbookHelper::getScenarios() const
+{
+ return mrBookGlob.getScenarios();
+}
+
+ConnectionsBuffer& WorkbookHelper::getConnections() const
+{
+ return mrBookGlob.getConnections();
+}
+
+PivotCacheBuffer& WorkbookHelper::getPivotCaches() const
+{
+ return mrBookGlob.getPivotCaches();
+}
+
+PivotTableBuffer& WorkbookHelper::getPivotTables() const
+{
+ return mrBookGlob.getPivotTables();
+}
+
+// converters -----------------------------------------------------------------
+
+FormulaParser& WorkbookHelper::getFormulaParser() const
+{
+ return mrBookGlob.getFormulaParser();
+}
+
+FormulaParser* WorkbookHelper::createFormulaParser() const
+{
+ return mrBookGlob.createFormulaParser();
+}
+
+UnitConverter& WorkbookHelper::getUnitConverter() const
+{
+ return mrBookGlob.getUnitConverter();
+}
+
+AddressConverter& WorkbookHelper::getAddressConverter() const
+{
+ return mrBookGlob.getAddressConverter();
+}
+
+oox::drawingml::chart::ChartConverter* WorkbookHelper::getChartConverter() const
+{
+ return mrBookGlob.getChartConverter();
+}
+
+void WorkbookHelper::useInternalChartDataTable( bool bInternal )
+{
+ mrBookGlob.useInternalChartDataTable( bInternal );
+}
+
+PageSettingsConverter& WorkbookHelper::getPageSettingsConverter() const
+{
+ return mrBookGlob.getPageSettingsConverter();
+}
+
+// OOXML/BIFF12 specific ------------------------------------------------------
+
+XmlFilterBase& WorkbookHelper::getOoxFilter() const
+{
+ return mrBookGlob.getOoxFilter();
+}
+
+bool WorkbookHelper::importOoxFragment( const rtl::Reference<FragmentHandler>& rxHandler )
+{
+ return getOoxFilter().importFragment( rxHandler );
+}
+
+bool WorkbookHelper::importOoxFragment( const rtl::Reference<FragmentHandler>& rxHandler, oox::core::FastParser& rParser )
+{
+ return getOoxFilter().importFragment(rxHandler, rParser);
+}
+
+// BIFF specific --------------------------------------------------------------
+
+rtl_TextEncoding WorkbookHelper::getTextEncoding() const
+{
+ return mrBookGlob.getTextEncoding();
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/workbooksettings.cxx b/sc/source/filter/oox/workbooksettings.cxx
new file mode 100644
index 000000000..f23afc412
--- /dev/null
+++ b/sc/source/filter/oox/workbooksettings.cxx
@@ -0,0 +1,298 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <workbooksettings.hxx>
+
+#include <com/sun/star/sheet/XCalculatable.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <oox/core/binarycodec.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/core/xmlfilterbase.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <unitconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+
+namespace {
+
+const sal_uInt32 BIFF12_WORKBOOKPR_DATE1904 = 0x00000001;
+const sal_uInt32 BIFF12_WORKBOOKPR_STRIPEXT = 0x00000080;
+
+const sal_uInt16 BIFF12_CALCPR_A1 = 0x0002;
+const sal_uInt16 BIFF12_CALCPR_ITERATE = 0x0004;
+const sal_uInt16 BIFF12_CALCPR_FULLPRECISION = 0x0008;
+const sal_uInt16 BIFF12_CALCPR_CALCCOMPLETED = 0x0010;
+const sal_uInt16 BIFF12_CALCPR_CALCONSAVE = 0x0020;
+const sal_uInt16 BIFF12_CALCPR_CONCURRENT = 0x0040;
+const sal_uInt16 BIFF12_CALCPR_MANUALPROC = 0x0080;
+
+// no predefined constants for show objects mode
+const sal_Int16 API_SHOWMODE_SHOW = 0; /// Show drawing objects.
+const sal_Int16 API_SHOWMODE_HIDE = 1; /// Hide drawing objects.
+const sal_Int16 API_SHOWMODE_PLACEHOLDER = 2; /// Show placeholders for drawing objects.
+
+} // namespace
+
+FileSharingModel::FileSharingModel() :
+ mnSpinCount( 0 ),
+ mnPasswordHash( 0 ),
+ mbRecommendReadOnly( false )
+{
+}
+
+WorkbookSettingsModel::WorkbookSettingsModel() :
+ mnShowObjectMode( XML_all ),
+ mnUpdateLinksMode( XML_userSet ),
+ mnDefaultThemeVer( -1 ),
+ mbDateMode1904( false ),
+ mbDateCompatibility ( false ),
+ mbSaveExtLinkValues( true )
+{
+}
+
+void WorkbookSettingsModel::setBiffObjectMode( sal_uInt16 nObjMode )
+{
+ static const sal_Int32 spnObjModes[] = { XML_all, XML_placeholders, XML_none };
+ mnShowObjectMode = STATIC_ARRAY_SELECT( spnObjModes, nObjMode, XML_all );
+}
+
+CalcSettingsModel::CalcSettingsModel() :
+ mfIterateDelta( 0.001 ),
+ mnCalcId( -1 ),
+ mnRefMode( XML_A1 ),
+ mnCalcMode( XML_auto ),
+ mnIterateCount( 100 ),
+ mnProcCount( -1 ),
+ mbCalcOnSave( true ),
+ mbCalcCompleted( true ),
+ mbFullPrecision( true ),
+ mbIterate( false ),
+ mbConcurrent( true )
+{
+}
+
+WorkbookSettings::WorkbookSettings( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void WorkbookSettings::importFileSharing( const AttributeList& rAttribs )
+{
+ maFileSharing.maUserName = rAttribs.getXString( XML_userName, OUString() );
+ maFileSharing.maAlgorithmName = rAttribs.getString( XML_algorithmName, OUString());
+ maFileSharing.maHashValue = rAttribs.getString( XML_hashValue, OUString());
+ maFileSharing.maSaltValue = rAttribs.getString( XML_saltValue, OUString());
+ maFileSharing.mnSpinCount = rAttribs.getUnsigned( XML_spinCount, 0);
+ maFileSharing.mnPasswordHash = oox::core::CodecHelper::getPasswordHash( rAttribs, XML_reservationPassword );
+ maFileSharing.mbRecommendReadOnly = rAttribs.getBool( XML_readOnlyRecommended, false );
+}
+
+void WorkbookSettings::importWorkbookPr( const AttributeList& rAttribs )
+{
+ maBookSettings.maCodeName = rAttribs.getString( XML_codeName, OUString() );
+ maBookSettings.mnShowObjectMode = rAttribs.getToken( XML_showObjects, XML_all );
+ maBookSettings.mnUpdateLinksMode = rAttribs.getToken( XML_updateLinks, XML_userSet );
+ maBookSettings.mnDefaultThemeVer = rAttribs.getInteger( XML_defaultThemeVersion, -1 );
+ maBookSettings.mbSaveExtLinkValues = rAttribs.getBool( XML_saveExternalLinkValues, true );
+ setDateMode( rAttribs.getBool( XML_date1904, false ), rAttribs.getBool( XML_dateCompatibility, true ) );
+}
+
+void WorkbookSettings::importCalcPr( const AttributeList& rAttribs )
+{
+ maCalcSettings.mfIterateDelta = rAttribs.getDouble( XML_iterateDelta, 0.0001 );
+ maCalcSettings.mnCalcId = rAttribs.getInteger( XML_calcId, -1 );
+ maCalcSettings.mnRefMode = rAttribs.getToken( XML_refMode, XML_A1 );
+ maCalcSettings.mnCalcMode = rAttribs.getToken( XML_calcMode, XML_auto );
+ maCalcSettings.mnIterateCount = rAttribs.getInteger( XML_iterateCount, 100 );
+ maCalcSettings.mnProcCount = rAttribs.getInteger( XML_concurrentManualCount, -1 );
+ maCalcSettings.mbCalcOnSave = rAttribs.getBool( XML_calcOnSave, true );
+ maCalcSettings.mbCalcCompleted = rAttribs.getBool( XML_calcCompleted, true );
+ maCalcSettings.mbFullPrecision = rAttribs.getBool( XML_fullPrecision, true );
+ maCalcSettings.mbIterate = rAttribs.getBool( XML_iterate, false );
+ maCalcSettings.mbConcurrent = rAttribs.getBool( XML_concurrentCalc, true );
+}
+
+void WorkbookSettings::importFileSharing( SequenceInputStream& rStrm )
+{
+ maFileSharing.mbRecommendReadOnly = rStrm.readuInt16() != 0;
+ maFileSharing.mnPasswordHash = rStrm.readuInt16();
+ rStrm >> maFileSharing.maUserName;
+}
+
+void WorkbookSettings::importWorkbookPr( SequenceInputStream& rStrm )
+{
+ sal_uInt32 nFlags;
+ nFlags = rStrm.readuInt32();
+ maBookSettings.mnDefaultThemeVer = rStrm.readInt32();
+ rStrm >> maBookSettings.maCodeName;
+ maBookSettings.setBiffObjectMode( extractValue< sal_uInt16 >( nFlags, 13, 2 ) );
+ // set flag means: strip external link values
+ maBookSettings.mbSaveExtLinkValues = !getFlag( nFlags, BIFF12_WORKBOOKPR_STRIPEXT );
+ setDateMode( getFlag( nFlags, BIFF12_WORKBOOKPR_DATE1904 ) );
+}
+
+void WorkbookSettings::importCalcPr( SequenceInputStream& rStrm )
+{
+ sal_Int32 nCalcMode, nProcCount;
+ sal_uInt16 nFlags;
+ maCalcSettings.mnCalcId = rStrm.readInt32();
+ nCalcMode = rStrm.readInt32();
+ maCalcSettings.mnIterateCount = rStrm.readInt32();
+ maCalcSettings.mfIterateDelta = rStrm.readDouble();
+ nProcCount = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+
+ static const sal_Int32 spnCalcModes[] = { XML_manual, XML_auto, XML_autoNoTable };
+ maCalcSettings.mnRefMode = getFlagValue( nFlags, BIFF12_CALCPR_A1, XML_A1, XML_R1C1 );
+ maCalcSettings.mnCalcMode = STATIC_ARRAY_SELECT( spnCalcModes, nCalcMode, XML_auto );
+ maCalcSettings.mnProcCount = getFlagValue< sal_Int32 >( nFlags, BIFF12_CALCPR_MANUALPROC, nProcCount, -1 );
+ maCalcSettings.mbCalcOnSave = getFlag( nFlags, BIFF12_CALCPR_CALCONSAVE );
+ maCalcSettings.mbCalcCompleted = getFlag( nFlags, BIFF12_CALCPR_CALCCOMPLETED );
+ maCalcSettings.mbFullPrecision = getFlag( nFlags, BIFF12_CALCPR_FULLPRECISION );
+ maCalcSettings.mbIterate = getFlag( nFlags, BIFF12_CALCPR_ITERATE );
+ maCalcSettings.mbConcurrent = getFlag( nFlags, BIFF12_CALCPR_CONCURRENT );
+}
+
+void WorkbookSettings::finalizeImport()
+{
+ // default settings
+ PropertySet aPropSet( getDocument() );
+ aPropSet.setProperty( PROP_IgnoreCase, true ); // always in Excel
+ aPropSet.setProperty( PROP_RegularExpressions, false ); // not supported in Excel
+ aPropSet.setProperty( PROP_Wildcards, true ); // always in Excel
+
+ // write protection
+ if (maFileSharing.mbRecommendReadOnly || (maFileSharing.mnPasswordHash != 0) ||
+ !maFileSharing.maHashValue.isEmpty()) try
+ {
+ getBaseFilter().getMediaDescriptor()[ "ReadOnly" ] <<= true;
+
+ Reference< XPropertySet > xDocumentSettings( getBaseFilter().getModelFactory()->createInstance(
+ "com.sun.star.document.Settings" ), UNO_QUERY_THROW );
+ PropertySet aSettingsProp( xDocumentSettings );
+
+ /* TODO: not setting read-only if only mnPasswordHash ('password'
+ * attribute) is present looks a bit silly, any reason for that?
+ * 'readOnlyRecommended' is defined as "indicates on open, whether the
+ * application alerts the user that the file be marked as read-only",
+ * which sounds silly in itself and seems not to be present if the
+ * 'password' attribute isn't present, but... */
+ if (maFileSharing.mbRecommendReadOnly || !maFileSharing.maHashValue.isEmpty())
+ aSettingsProp.setProperty( PROP_LoadReadonly, true );
+
+ if (!maFileSharing.maHashValue.isEmpty())
+ {
+ Sequence<PropertyValue> aResult{
+ comphelper::makePropertyValue("algorithm-name", maFileSharing.maAlgorithmName),
+ comphelper::makePropertyValue("salt", maFileSharing.maSaltValue),
+ comphelper::makePropertyValue("iteration-count", maFileSharing.mnSpinCount),
+ comphelper::makePropertyValue("hash", maFileSharing.maHashValue)
+ };
+ aSettingsProp.setProperty(PROP_ModifyPasswordInfo, aResult);
+ }
+
+ if( maFileSharing.mnPasswordHash != 0 )
+ aSettingsProp.setProperty( PROP_ModifyPasswordHash, static_cast< sal_Int32 >( maFileSharing.mnPasswordHash ) );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // calculation settings
+ css::util::Date aNullDate = getNullDate();
+
+ aPropSet.setProperty( PROP_NullDate, aNullDate );
+ aPropSet.setProperty( PROP_IsIterationEnabled, maCalcSettings.mbIterate );
+ aPropSet.setProperty( PROP_IterationCount, maCalcSettings.mnIterateCount );
+ aPropSet.setProperty( PROP_IterationEpsilon, maCalcSettings.mfIterateDelta );
+ aPropSet.setProperty( PROP_CalcAsShown, !maCalcSettings.mbFullPrecision );
+ aPropSet.setProperty( PROP_LookUpLabels, false );
+
+ Reference< XNumberFormatsSupplier > xNumFmtsSupp( getDocument(), UNO_QUERY );
+ if( xNumFmtsSupp.is() )
+ {
+ PropertySet aNumFmtProp( xNumFmtsSupp->getNumberFormatSettings() );
+ aNumFmtProp.setProperty( PROP_NullDate, aNullDate );
+ }
+
+ Reference< XCalculatable > xCalculatable( getDocument(), UNO_QUERY );
+ if( xCalculatable.is() )
+ xCalculatable->enableAutomaticCalculation( (maCalcSettings.mnCalcMode == XML_auto) || (maCalcSettings.mnCalcMode == XML_autoNoTable) );
+
+ // VBA code name
+ aPropSet.setProperty( PROP_CodeName, maBookSettings.maCodeName );
+}
+
+sal_Int16 WorkbookSettings::getApiShowObjectMode() const
+{
+ switch( maBookSettings.mnShowObjectMode )
+ {
+ case XML_all: return API_SHOWMODE_SHOW;
+ case XML_none: return API_SHOWMODE_HIDE;
+ // #i80528# placeholders not supported anymore, but this is handled internally in Calc
+ case XML_placeholders: return API_SHOWMODE_PLACEHOLDER;
+ }
+ return API_SHOWMODE_SHOW;
+}
+
+css::util::Date const & WorkbookSettings::getNullDate() const
+{
+ static const css::util::Date saDate1900 ( 30, 12, 1899 );
+ static const css::util::Date saDate1904 ( 1, 1, 1904 );
+ static const css::util::Date saDateBackCompatibility1900( 31, 12, 1899 );
+
+ if( getOoxFilter().getVersion() == oox::core::ISOIEC_29500_2008 )
+ {
+ if( !maBookSettings.mbDateCompatibility )
+ return saDate1900;
+
+ return maBookSettings.mbDateMode1904 ? saDate1904 :
+ saDateBackCompatibility1900;
+ }
+
+ return maBookSettings.mbDateMode1904 ? saDate1904 : saDate1900;
+}
+
+void WorkbookSettings::setDateMode( bool bDateMode1904, bool bDateCompatibility )
+{
+ maBookSettings.mbDateMode1904 = bDateMode1904;
+ maBookSettings.mbDateCompatibility = bDateCompatibility;
+
+ getUnitConverter().finalizeNullDate( getNullDate() );
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/worksheetbuffer.cxx b/sc/source/filter/oox/worksheetbuffer.cxx
new file mode 100644
index 000000000..a00f6cb6c
--- /dev/null
+++ b/sc/source/filter/oox/worksheetbuffer.cxx
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <worksheetbuffer.hxx>
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/containerhelper.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <biffhelper.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::uno;
+
+SheetInfoModel::SheetInfoModel() :
+ mnSheetId( -1 ),
+ mnState( XML_visible )
+{
+}
+
+WorksheetBuffer::WorksheetBuffer( const WorkbookHelper& rHelper ) :
+ WorkbookHelper( rHelper )
+{
+}
+
+void WorksheetBuffer::importSheet( const AttributeList& rAttribs )
+{
+ SheetInfoModel aModel;
+ aModel.maRelId = rAttribs.getString( R_TOKEN( id ), OUString() );
+ aModel.maName = rAttribs.getXString( XML_name, OUString() );
+ aModel.mnSheetId = rAttribs.getInteger( XML_sheetId, -1 );
+ aModel.mnState = rAttribs.getToken( XML_state, XML_visible );
+ insertSheet( aModel );
+}
+
+void WorksheetBuffer::importSheet( SequenceInputStream& rStrm )
+{
+ sal_Int32 nState;
+ SheetInfoModel aModel;
+ nState = rStrm.readInt32();
+ aModel.mnSheetId = rStrm.readInt32();
+ rStrm >> aModel.maRelId >> aModel.maName;
+ static const sal_Int32 spnStates[] = { XML_visible, XML_hidden, XML_veryHidden };
+ aModel.mnState = STATIC_ARRAY_SELECT( spnStates, nState, XML_visible );
+ insertSheet( aModel );
+}
+
+sal_Int16 WorksheetBuffer::insertEmptySheet( const OUString& rPreferredName )
+{
+ IndexNamePair aIndexName = createSheet( rPreferredName, SAL_MAX_INT32 );
+ ScDocument& rDoc = getScDocument();
+
+ rDoc.SetVisible( aIndexName.first, false );
+ return aIndexName.first;
+}
+
+sal_Int32 WorksheetBuffer::getWorksheetCount() const
+{
+ return static_cast< sal_Int32 >( maSheetInfos.size() );
+}
+
+sal_Int32 WorksheetBuffer::getAllSheetCount() const
+{
+ const ScDocumentImport& rDoc = getDocImport();
+ return rDoc.getSheetCount();
+}
+
+OUString WorksheetBuffer::getWorksheetRelId( sal_Int32 nWorksheet ) const
+{
+ const SheetInfo* pSheetInfo = maSheetInfos.get( nWorksheet ).get();
+ return pSheetInfo ? pSheetInfo->maRelId : OUString();
+}
+
+sal_Int16 WorksheetBuffer::getCalcSheetIndex( sal_Int32 nWorksheet ) const
+{
+ const SheetInfo* pSheetInfo = maSheetInfos.get( nWorksheet ).get();
+ return pSheetInfo ? pSheetInfo->mnCalcSheet : -1;
+}
+
+OUString WorksheetBuffer::getCalcSheetName( sal_Int32 nWorksheet ) const
+{
+ const SheetInfo* pSheetInfo = maSheetInfos.get( nWorksheet ).get();
+ return pSheetInfo ? pSheetInfo->maCalcName : OUString();
+}
+
+void WorksheetBuffer::convertSheetNameRef( OUString& sSheetNameRef ) const
+{
+ if( !sSheetNameRef.startsWith("#") )
+ return;
+
+ sal_Int32 nSepPos = sSheetNameRef.lastIndexOf( '!' );
+ if( nSepPos <= 0 )
+ return;
+
+ // Do not attempt to blindly convert '#SheetName!A1' to
+ // '#SheetName.A1', it can be #SheetName!R1C1 as well. Hyperlink
+ // handler has to handle all, but prefer '#SheetName.A1' if
+ // possible.
+ if (nSepPos < sSheetNameRef.getLength() - 1)
+ {
+ ScRange aRange;
+ if ((aRange.ParseAny( sSheetNameRef.copy( nSepPos + 1 ), getScDocument(),
+ formula::FormulaGrammar::CONV_XL_R1C1) & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ sSheetNameRef = sSheetNameRef.replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ) );
+ }
+ // #i66592# convert sheet names that have been renamed on import
+ OUString aSheetName = sSheetNameRef.copy( 1, nSepPos - 1 );
+ OUString aCalcName = getCalcSheetName( aSheetName );
+ if( !aCalcName.isEmpty() )
+ sSheetNameRef = sSheetNameRef.replaceAt( 1, nSepPos - 1, aCalcName );
+}
+
+sal_Int16 WorksheetBuffer::getCalcSheetIndex( const OUString& rWorksheetName ) const
+{
+ const SheetInfo* pSheetInfo = maSheetInfosByName.get( rWorksheetName ).get();
+ return pSheetInfo ? pSheetInfo->mnCalcSheet : -1;
+}
+
+OUString WorksheetBuffer::getCalcSheetName( const OUString& rWorksheetName ) const
+{
+ if( const SheetInfo* pSheetInfo = maSheetInfosByName.get( rWorksheetName ).get() )
+ {
+ bool bIsQuoted = pSheetInfo->maName != rWorksheetName;
+ return bIsQuoted ? pSheetInfo->maCalcQuotedName : pSheetInfo->maCalcName;
+ }
+ return OUString();
+}
+
+// private --------------------------------------------------------------------
+
+namespace {
+
+OUString lclQuoteName( std::u16string_view rName )
+{
+ OUStringBuffer aBuffer( rName );
+ // duplicate all quote characters
+ for( sal_Int32 nPos = aBuffer.getLength() - 1; nPos >= 0; --nPos )
+ if( aBuffer[nPos] == '\'' )
+ aBuffer.insert( nPos, '\'' );
+ // add outer quotes and return
+ return aBuffer.insert( 0, '\'' ).append( '\'' ).makeStringAndClear();
+}
+
+} // namespace
+
+WorksheetBuffer::SheetInfo::SheetInfo( const SheetInfoModel& rModel, sal_Int16 nCalcSheet, const OUString& rCalcName ) :
+ SheetInfoModel( rModel ),
+ maCalcName( rCalcName ),
+ maCalcQuotedName( lclQuoteName( rCalcName ) ),
+ mnCalcSheet( nCalcSheet )
+{
+}
+
+WorksheetBuffer::IndexNamePair WorksheetBuffer::createSheet( const OUString& rPreferredName, sal_Int32 nSheetPos )
+{
+ //FIXME: Rewrite this block using ScDocument[Import] instead of UNO
+ try
+ {
+ Reference< XSpreadsheets > xSheets( getDocument()->getSheets(), UNO_SET_THROW );
+ Reference< XIndexAccess > xSheetsIA( xSheets, UNO_QUERY_THROW );
+ sal_Int16 nCalcSheet = -1;
+ OUString aSheetName = rPreferredName.isEmpty() ? ScResId(STR_TABLE_DEF) : rPreferredName;
+ if( nSheetPos < xSheetsIA->getCount() )
+ {
+ nCalcSheet = static_cast< sal_Int16 >( nSheetPos );
+ // existing sheet - try to rename
+ Reference< XNamed > xSheetName( xSheetsIA->getByIndex( nSheetPos ), UNO_QUERY_THROW );
+ if( xSheetName->getName() != aSheetName )
+ {
+ aSheetName = ContainerHelper::getUnusedName( xSheets, aSheetName, ' ' );
+ xSheetName->setName( aSheetName );
+ }
+ }
+ else
+ {
+ nCalcSheet = static_cast< sal_Int16 >( xSheetsIA->getCount() );
+ // new sheet - insert with unused name
+ aSheetName = ContainerHelper::getUnusedName( xSheets, aSheetName, ' ' );
+ xSheets->insertNewByName( aSheetName, nCalcSheet );
+ }
+
+ // return final sheet index if sheet exists
+ return IndexNamePair( nCalcSheet, aSheetName );
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL( "WorksheetBuffer::createSheet - cannot insert or rename worksheet" );
+ }
+ return IndexNamePair( -1, OUString() );
+}
+
+void WorksheetBuffer::insertSheet( const SheetInfoModel& rModel )
+{
+ sal_Int32 nWorksheet = static_cast< sal_Int32 >( maSheetInfos.size() );
+ IndexNamePair aIndexName = createSheet( rModel.maName, nWorksheet );
+ auto xSheetInfo = std::make_shared<SheetInfo>( rModel, aIndexName.first, aIndexName.second );
+ maSheetInfos.push_back( xSheetInfo );
+ maSheetInfosByName[ rModel.maName ] = xSheetInfo;
+ maSheetInfosByName[ lclQuoteName( rModel.maName ) ] = xSheetInfo;
+}
+
+void WorksheetBuffer::finalizeImport( sal_Int16 nActiveSheet )
+{
+ ScDocument& rDoc = getScDocument();
+
+ for ( const auto& aSheetInfo: maSheetInfos )
+ {
+ // make sure at least 1 sheet (the active one) is visible
+ if ( aSheetInfo->mnCalcSheet == nActiveSheet)
+ rDoc.SetVisible( aSheetInfo->mnCalcSheet, true );
+ else
+ rDoc.SetVisible( aSheetInfo->mnCalcSheet, aSheetInfo->mnState == XML_visible );
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/worksheetfragment.cxx b/sc/source/filter/oox/worksheetfragment.cxx
new file mode 100644
index 000000000..218ab4475
--- /dev/null
+++ b/sc/source/filter/oox/worksheetfragment.cxx
@@ -0,0 +1,911 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <worksheetfragment.hxx>
+#include <formulaparser.hxx>
+
+#include <osl/diagnose.h>
+#include <oox/core/filterbase.hxx>
+#include <oox/core/relations.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+#include <autofilterbuffer.hxx>
+#include <autofiltercontext.hxx>
+#include <commentsfragment.hxx>
+#include <condformatcontext.hxx>
+#include <drawingfragment.hxx>
+#include <pagesettings.hxx>
+#include <pivottablefragment.hxx>
+#include <querytablefragment.hxx>
+#include <scenariocontext.hxx>
+#include <sheetdatabuffer.hxx>
+#include <sheetdatacontext.hxx>
+#include <tablefragment.hxx>
+#include <extlstcontext.hxx>
+#include <viewsettings.hxx>
+#include <worksheetsettings.hxx>
+
+namespace oox::xls {
+
+using namespace ::oox::core;
+
+namespace {
+
+const sal_uInt16 BIFF_COLINFO_HIDDEN = 0x0001;
+const sal_uInt16 BIFF_COLINFO_SHOWPHONETIC = 0x0008;
+const sal_uInt16 BIFF_COLINFO_COLLAPSED = 0x1000;
+
+const sal_uInt16 BIFF_DEFROW_CUSTOMHEIGHT = 0x0001;
+const sal_uInt16 BIFF_DEFROW_HIDDEN = 0x0002;
+const sal_uInt16 BIFF_DEFROW_THICKTOP = 0x0004;
+const sal_uInt16 BIFF_DEFROW_THICKBOTTOM = 0x0008;
+
+const sal_uInt32 BIFF_DATAVAL_STRINGLIST = 0x00000080;
+const sal_uInt32 BIFF_DATAVAL_ALLOWBLANK = 0x00000100;
+const sal_uInt32 BIFF_DATAVAL_NODROPDOWN = 0x00000200;
+const sal_uInt32 BIFF_DATAVAL_SHOWINPUT = 0x00040000;
+const sal_uInt32 BIFF_DATAVAL_SHOWERROR = 0x00080000;
+
+const sal_Int32 BIFF12_OLEOBJECT_ICON = 4;
+const sal_Int32 BIFF12_OLEOBJECT_ALWAYS = 1;
+const sal_uInt16 BIFF12_OLEOBJECT_LINKED = 0x0001;
+const sal_uInt16 BIFF12_OLEOBJECT_AUTOLOAD = 0x0002;
+
+} // namespace
+
+void DataValidationsContextBase::SetValidation( WorksheetHelper& rTarget )
+{
+ if (!mxValModel)
+ return;
+
+ rTarget.getAddressConverter().convertToCellRangeList(mxValModel->maRanges, maSqref, rTarget.getSheetIndex(), true);
+ mxValModel->msRef = maSqref;
+
+ mxValModel->maTokens1 = rTarget.getFormulaParser().importFormula(mxValModel->maRanges.GetTopLeftCorner(), maFormula1);
+ // process string list of a list validation (convert to list of string tokens)
+ if (mxValModel->mnType == XML_list)
+ rTarget.getFormulaParser().convertStringToStringList(mxValModel->maTokens1, ',', true);
+
+ mxValModel->maTokens2 = rTarget.getFormulaParser().importFormula(mxValModel->maRanges.GetTopLeftCorner(), maFormula2);
+
+ rTarget.setValidation(*mxValModel);
+ mxValModel.reset();
+}
+
+void DataValidationsContextBase::importDataValidation( const AttributeList& rAttribs )
+{
+ mxValModel.reset(new ValidationModel);
+ maFormula1.clear();
+ maFormula2.clear();
+ maSqref = rAttribs.getString(XML_sqref, OUString());
+ mxValModel->maInputTitle = rAttribs.getXString(XML_promptTitle, OUString());
+ mxValModel->maInputMessage = rAttribs.getXString(XML_prompt, OUString());
+ mxValModel->maErrorTitle = rAttribs.getXString(XML_errorTitle, OUString());
+ mxValModel->maErrorMessage = rAttribs.getXString(XML_error, OUString());
+ mxValModel->mnType = rAttribs.getToken(XML_type, XML_none);
+ mxValModel->mnOperator = rAttribs.getToken(XML_operator, XML_between);
+ mxValModel->mnErrorStyle = rAttribs.getToken(XML_errorStyle, XML_stop);
+ mxValModel->mbShowInputMsg = rAttribs.getBool(XML_showInputMessage, false);
+ mxValModel->mbShowErrorMsg = rAttribs.getBool(XML_showErrorMessage, false);
+ /* The attribute showDropDown@dataValidation is in fact a "suppress
+ dropdown" flag, as it was in the BIFF format! ECMA specification
+ and attribute name are plain wrong! */
+ mxValModel->mbNoDropDown = rAttribs.getBool(XML_showDropDown, false);
+ mxValModel->mbAllowBlank = rAttribs.getBool(XML_allowBlank, false);
+}
+
+void DataValidationsContextBase::importDataValidation( SequenceInputStream& rStrm, WorksheetHelper& rTarget )
+{
+ ValidationModel aModel;
+
+ sal_uInt32 nFlags;
+ BinRangeList aRanges;
+ nFlags = rStrm.readuInt32();
+ rStrm >> aRanges >> aModel.maErrorTitle >> aModel.maErrorMessage >> aModel.maInputTitle >> aModel.maInputMessage;
+
+ // equal flags in all BIFFs
+ aModel.setBiffType(extractValue< sal_uInt8 >(nFlags, 0, 4));
+ aModel.setBiffOperator(extractValue< sal_uInt8 >(nFlags, 20, 4));
+ aModel.setBiffErrorStyle(extractValue< sal_uInt8 >(nFlags, 4, 3));
+ aModel.mbAllowBlank = getFlag(nFlags, BIFF_DATAVAL_ALLOWBLANK);
+ aModel.mbNoDropDown = getFlag(nFlags, BIFF_DATAVAL_NODROPDOWN);
+ aModel.mbShowInputMsg = getFlag(nFlags, BIFF_DATAVAL_SHOWINPUT);
+ aModel.mbShowErrorMsg = getFlag(nFlags, BIFF_DATAVAL_SHOWERROR);
+
+ // cell range list
+ rTarget.getAddressConverter().convertToCellRangeList(aModel.maRanges, aRanges, rTarget.getSheetIndex(), true);
+
+ // condition formula(s)
+ FormulaParser& rParser = rTarget.getFormulaParser();
+ ScAddress aBaseAddr = aModel.maRanges.GetTopLeftCorner();
+ aModel.maTokens1 = rParser.importFormula(aBaseAddr, FormulaType::Validation, rStrm);
+ aModel.maTokens2 = rParser.importFormula(aBaseAddr, FormulaType::Validation, rStrm);
+ // process string list of a list validation (convert to list of string tokens)
+ if ((aModel.mnType == XML_list) && getFlag(nFlags, BIFF_DATAVAL_STRINGLIST))
+ rParser.convertStringToStringList(aModel.maTokens1, ',', true);
+
+ // set validation data
+ rTarget.setValidation(aModel);
+}
+
+DataValidationsContext::DataValidationsContext( WorksheetFragmentBase& rFragment ) :
+ WorksheetContextBase( rFragment )
+{
+}
+
+ContextHandlerRef DataValidationsContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElementWithMce() )
+ {
+ case XLS_TOKEN( dataValidations ):
+ if( nElement == XLS_TOKEN( dataValidation ) )
+ {
+ importDataValidation( rAttribs );
+ return this;
+ }
+ break;
+ case XLS_TOKEN( dataValidation ):
+ switch( nElement )
+ {
+ case MCE_TOKEN( AlternateContent ):
+ case XLS_TOKEN( formula1 ):
+ case XLS_TOKEN( formula2 ):
+ return this; // collect formulas in onCharacters()
+ }
+ break;
+ case MCE_TOKEN( AlternateContent ):
+ switch( nElement )
+ {
+ case MCE_TOKEN( Choice ):
+ case MCE_TOKEN( Fallback ):
+ return this;
+ }
+ break;
+ case MCE_TOKEN( Choice ):
+ switch( nElement )
+ {
+ case X12AC_TOKEN( list ):
+ return this;
+ }
+ break;
+ case MCE_TOKEN( Fallback ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( formula1 ):
+ if (!isFormula1Set()) // only if more preferable choice was not used
+ return this; // collect formulas in onCharacters()
+ break;
+ case XLS_TOKEN( formula2 ):
+ if (!isFormula2Set()) // only if more preferable choice was not used
+ return this; // collect formulas in onCharacters()
+ break;
+ }
+ break;
+ }
+ return nullptr;
+}
+
+namespace {
+// Convert strings like 1,"2,3",4 to form "1","2,3","4"
+OUString NormalizeOoxList(const OUString& aList)
+{
+ OUStringBuffer aResult("\"");
+ bool bInsideQuotes = false;
+ const sal_Int32 nLen = aList.getLength();
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ sal_Unicode ch = aList[i];
+
+ switch (ch)
+ {
+ case L'"':
+ bInsideQuotes = !bInsideQuotes;
+ break;
+ case L',':
+ if (!bInsideQuotes)
+ {
+ aResult.append("\",\"");
+ break;
+ }
+ [[fallthrough]];
+ default:
+ aResult.append(ch);
+ break;
+ }
+ }
+ return aResult.append('"').makeStringAndClear();
+}
+}
+
+void DataValidationsContext::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( formula1 ):
+ SetFormula1( rChars );
+ break;
+ case XLS_TOKEN( formula2 ):
+ SetFormula2( rChars );
+ break;
+ case X12AC_TOKEN( list ):
+ SetFormula1( NormalizeOoxList( rChars ) );
+ break;
+ }
+}
+
+void DataValidationsContext::onEndElement()
+{
+ if( getCurrentElementWithMce() == XLS_TOKEN( dataValidation ) )
+ {
+ SetValidation( *this );
+ }
+}
+
+ContextHandlerRef DataValidationsContext::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ if( nRecId == BIFF12_ID_DATAVALIDATION )
+ importDataValidation( rStrm, *this );
+ return nullptr;
+}
+
+ExtDataValidationsContext::ExtDataValidationsContext( WorksheetContextBase& rFragment ) :
+ WorksheetContextBase( rFragment ), mCurrFormula( 0 )
+{
+}
+
+ContextHandlerRef ExtDataValidationsContext::onCreateContext(sal_Int32 nElement, const AttributeList& rAttribs)
+{
+ switch( getCurrentElement() )
+ {
+ case XLS14_TOKEN( dataValidations ):
+ if ( nElement == XLS14_TOKEN( dataValidation ) )
+ {
+ importDataValidation( rAttribs );
+ return this;
+ }
+ break;
+ case XLS14_TOKEN( dataValidation ):
+ switch ( nElement )
+ {
+ case XLS14_TOKEN( formula1 ):
+ case XLS14_TOKEN( formula2 ):
+ mCurrFormula = nElement;
+ return this;
+ case XM_TOKEN( sqref ):
+ return this; // collect sqref in onCharacters()
+ }
+ break;
+ case XLS14_TOKEN( formula1 ):
+ case XLS14_TOKEN( formula2 ):
+ switch( nElement )
+ {
+ case XM_TOKEN( f ):
+ return this; // collect formulas in onCharacters()
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void ExtDataValidationsContext::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XM_TOKEN( f ):
+ switch( mCurrFormula )
+ {
+ case XLS14_TOKEN( formula1 ):
+ SetFormula1( rChars );
+ break;
+ case XLS14_TOKEN( formula2 ):
+ SetFormula2( rChars );
+ break;
+ }
+ break;
+ case XM_TOKEN( sqref ):
+ SetSqref( rChars );
+ break;
+ }
+}
+
+void ExtDataValidationsContext::onEndElement()
+{
+ if( isCurrentElement( XLS14_TOKEN( dataValidation ) ) )
+ {
+ SetValidation( *this );
+ }
+}
+
+WorksheetFragment::WorksheetFragment( const WorksheetHelper& rHelper, const OUString& rFragmentPath ) :
+ WorksheetFragmentBase( rHelper, rFragmentPath )
+{
+ // import data tables related to this worksheet
+ RelationsRef xTableRels = getRelations().getRelationsFromTypeFromOfficeDoc( u"table" );
+ for( const auto& rEntry : *xTableRels )
+ importOoxFragment( new TableFragment( *this, getFragmentPathFromRelation( rEntry.second ) ) );
+
+ // import comments related to this worksheet
+ OUString aCommentsFragmentPath = getFragmentPathFromFirstTypeFromOfficeDoc( u"comments" );
+ if( !aCommentsFragmentPath.isEmpty() )
+ importOoxFragment( new CommentsFragment( *this, aCommentsFragmentPath ) );
+}
+
+ContextHandlerRef WorksheetFragment::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT: switch( getSheetType() )
+ {
+ case WorksheetType::Work: return (nElement == XLS_TOKEN( worksheet )) ? this : nullptr;
+ case WorksheetType::Chart: return nullptr;
+ case WorksheetType::Macro: return (nElement == XM_TOKEN( macrosheet )) ? this : nullptr;
+ case WorksheetType::Dialog: return (nElement == XLS_TOKEN( dialogsheet )) ? this : nullptr;
+ case WorksheetType::Empty: return nullptr;
+ }
+ break;
+
+ case XLS_TOKEN( worksheet ):
+ case XM_TOKEN( macrosheet ):
+ case XLS_TOKEN( dialogsheet ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheetData ): return new SheetDataContext( *this );
+ case XLS_TOKEN( conditionalFormatting ): return new CondFormatContext( *this );
+ case XLS_TOKEN( dataValidations ): return new DataValidationsContext( *this );
+ case XLS_TOKEN( autoFilter ): return new AutoFilterContext( *this, getAutoFilters().createAutoFilter() );
+ case XLS_TOKEN( scenarios ): return new ScenariosContext( *this );
+ case XLS_TOKEN( extLst ): return new ExtLstGlobalContext( *this );
+
+ case XLS_TOKEN( sheetViews ):
+ case XLS_TOKEN( cols ):
+ case XLS_TOKEN( mergeCells ):
+ case XLS_TOKEN( hyperlinks ):
+ case XLS_TOKEN( rowBreaks ):
+ case XLS_TOKEN( colBreaks ):
+ case XLS_TOKEN( oleObjects ):
+ case XLS_TOKEN( controls ): return this;
+
+ case XLS_TOKEN( sheetPr ): getWorksheetSettings().importSheetPr( rAttribs ); return this;
+ case XLS_TOKEN( dimension ): importDimension( rAttribs ); break;
+ case XLS_TOKEN( sheetFormatPr ): importSheetFormatPr( rAttribs ); break;
+ case XLS_TOKEN( sheetProtection ): getWorksheetSettings().importSheetProtection( rAttribs ); break;
+ case XLS_TOKEN( protectedRanges ):
+ // no attribs known (yet?)
+ return this;
+ case XLS_TOKEN( phoneticPr ): getWorksheetSettings().importPhoneticPr( rAttribs ); break;
+ case XLS_TOKEN( printOptions ): getPageSettings().importPrintOptions( rAttribs ); break;
+ case XLS_TOKEN( pageMargins ): getPageSettings().importPageMargins( rAttribs ); break;
+ case XLS_TOKEN( pageSetup ): getPageSettings().importPageSetup( getRelations(), rAttribs ); break;
+ case XLS_TOKEN( headerFooter ): getPageSettings().importHeaderFooter( rAttribs ); return this;
+ case XLS_TOKEN( picture ): getPageSettings().importPicture( getRelations(), rAttribs ); break;
+ case XLS_TOKEN( drawing ): importDrawing( rAttribs ); break;
+ case XLS_TOKEN( legacyDrawing ): importLegacyDrawing( rAttribs ); break;
+ }
+ break;
+
+ case XLS_TOKEN( sheetPr ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( tabColor ): getWorksheetSettings().importTabColor( rAttribs ); break;
+ case XLS_TOKEN( outlinePr ): getWorksheetSettings().importOutlinePr( rAttribs ); break;
+ case XLS_TOKEN( pageSetUpPr ): importPageSetUpPr( rAttribs ); break;
+ }
+ break;
+
+ case XLS_TOKEN( sheetViews ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( sheetView ): getSheetViewSettings().importSheetView( rAttribs ); return this;
+ }
+ break;
+ case XLS_TOKEN( sheetView ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( pane ): getSheetViewSettings().importPane( rAttribs ); break;
+ case XLS_TOKEN( selection ): getSheetViewSettings().importSelection( rAttribs ); break;
+ }
+ break;
+
+ case XLS_TOKEN( cols ):
+ if( nElement == XLS_TOKEN( col ) ) importCol( rAttribs );
+ break;
+ case XLS_TOKEN( mergeCells ):
+ if( nElement == XLS_TOKEN( mergeCell ) ) importMergeCell( rAttribs );
+ break;
+ case XLS_TOKEN( hyperlinks ):
+ if( nElement == XLS_TOKEN( hyperlink ) ) importHyperlink( rAttribs );
+ break;
+ case XLS_TOKEN( rowBreaks ):
+ if( nElement == XLS_TOKEN( brk ) ) importBrk( rAttribs, true );
+ break;
+ case XLS_TOKEN( colBreaks ):
+ if( nElement == XLS_TOKEN( brk ) ) importBrk( rAttribs, false );
+ break;
+
+ case XLS_TOKEN( protectedRanges ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( protectedRange ): getWorksheetSettings().importProtectedRange( rAttribs ); return this;
+ }
+ break;
+
+ case XLS_TOKEN( headerFooter ):
+ switch( nElement )
+ {
+ case XLS_TOKEN( firstHeader ):
+ case XLS_TOKEN( firstFooter ):
+ case XLS_TOKEN( oddHeader ):
+ case XLS_TOKEN( oddFooter ):
+ case XLS_TOKEN( evenHeader ):
+ case XLS_TOKEN( evenFooter ): return this; // collect h/f contents in onCharacters()
+ }
+ break;
+ // Only process an oleObject or control if outside a mc:AlternateContent
+ // element OR if within a mc:Fallback. I suppose ideally we
+ // should process the stuff within 'mc:Choice'
+ case XLS_TOKEN( controls ):
+ case XLS_TOKEN( oleObjects ):
+ if ( getCurrentElement() == XLS_TOKEN( controls ) )
+ {
+ if( isMCEStateEmpty() || getMCEState() == MCE_STATE::Started )
+ {
+ if ( getCurrentElement() == XLS_TOKEN( oleObjects ) ) importOleObject( rAttribs );
+ else
+ importControl( rAttribs );
+ }
+ else if ( !isMCEStateEmpty() && getMCEState() == MCE_STATE::FoundChoice )
+ {
+ // reset the handling within 'Choice'
+ // this will force attempted handling in Fallback
+ setMCEState( MCE_STATE::Started );
+ }
+ }
+ break;
+ }
+ return nullptr;
+}
+
+void WorksheetFragment::onCharacters( const OUString& rChars )
+{
+ switch( getCurrentElement() )
+ {
+ case XLS_TOKEN( firstHeader ):
+ case XLS_TOKEN( firstFooter ):
+ case XLS_TOKEN( oddHeader ):
+ case XLS_TOKEN( oddFooter ):
+ case XLS_TOKEN( evenHeader ):
+ case XLS_TOKEN( evenFooter ):
+ getPageSettings().importHeaderFooterCharacters( rChars, getCurrentElement() );
+ break;
+ }
+}
+
+ContextHandlerRef WorksheetFragment::onCreateRecordContext( sal_Int32 nRecId, SequenceInputStream& rStrm )
+{
+ switch( getCurrentElement() )
+ {
+ case XML_ROOT_CONTEXT:
+ if( nRecId == BIFF12_ID_WORKSHEET ) return this;
+ break;
+
+ case BIFF12_ID_WORKSHEET:
+ switch( nRecId )
+ {
+ case BIFF12_ID_SHEETDATA: return new SheetDataContext( *this );
+ case BIFF12_ID_CONDFORMATTING: return new CondFormatContext( *this );
+ case BIFF12_ID_DATAVALIDATIONS: return new DataValidationsContext( *this );
+ case BIFF12_ID_AUTOFILTER: return new AutoFilterContext( *this, getAutoFilters().createAutoFilter() );
+ case BIFF12_ID_SCENARIOS: return new ScenariosContext( *this );
+
+ case BIFF12_ID_SHEETVIEWS:
+ case BIFF12_ID_COLS:
+ case BIFF12_ID_MERGECELLS:
+ case BIFF12_ID_ROWBREAKS:
+ case BIFF12_ID_COLBREAKS:
+ case BIFF12_ID_OLEOBJECTS:
+ case BIFF12_ID_CONTROLS: return this;
+
+ case BIFF12_ID_SHEETPR: getWorksheetSettings().importSheetPr( rStrm ); break;
+ case BIFF12_ID_DIMENSION: importDimension( rStrm ); break;
+ case BIFF12_ID_SHEETFORMATPR: importSheetFormatPr( rStrm ); break;
+ case BIFF12_ID_HYPERLINK: importHyperlink( rStrm ); break;
+ case BIFF12_ID_PAGEMARGINS: getPageSettings().importPageMargins( rStrm ); break;
+ case BIFF12_ID_PAGESETUP: getPageSettings().importPageSetup( getRelations(), rStrm ); break;
+ case BIFF12_ID_PRINTOPTIONS: getPageSettings().importPrintOptions( rStrm ); break;
+ case BIFF12_ID_HEADERFOOTER: getPageSettings().importHeaderFooter( rStrm ); break;
+ case BIFF12_ID_PICTURE: getPageSettings().importPicture( getRelations(), rStrm ); break;
+ case BIFF12_ID_SHEETPROTECTION: getWorksheetSettings().importSheetProtection( rStrm ); break;
+ case BIFF12_ID_PHONETICPR: getWorksheetSettings().importPhoneticPr( rStrm ); break;
+ case BIFF12_ID_DRAWING: importDrawing( rStrm ); break;
+ case BIFF12_ID_LEGACYDRAWING: importLegacyDrawing( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_SHEETVIEWS:
+ switch( nRecId )
+ {
+ case BIFF12_ID_SHEETVIEW: getSheetViewSettings().importSheetView( rStrm ); return this;
+ }
+ break;
+ case BIFF12_ID_SHEETVIEW:
+ switch( nRecId )
+ {
+ case BIFF12_ID_PANE: getSheetViewSettings().importPane( rStrm ); break;
+ case BIFF12_ID_SELECTION: getSheetViewSettings().importSelection( rStrm ); break;
+ }
+ break;
+
+ case BIFF12_ID_COLS:
+ if( nRecId == BIFF12_ID_COL ) importCol( rStrm );
+ break;
+ case BIFF12_ID_MERGECELLS:
+ if( nRecId == BIFF12_ID_MERGECELL ) importMergeCell( rStrm );
+ break;
+ case BIFF12_ID_ROWBREAKS:
+ if( nRecId == BIFF12_ID_BRK ) importBrk( rStrm, true );
+ break;
+ case BIFF12_ID_COLBREAKS:
+ if( nRecId == BIFF12_ID_BRK ) importBrk( rStrm, false );
+ break;
+ case BIFF12_ID_OLEOBJECTS:
+ if( nRecId == BIFF12_ID_OLEOBJECT ) importOleObject( rStrm );
+ break;
+ case BIFF12_ID_CONTROLS:
+ if( nRecId == BIFF12_ID_CONTROL ) importControl( rStrm );
+ break;
+ }
+ return nullptr;
+}
+
+const RecordInfo* WorksheetFragment::getRecordInfos() const
+{
+ static const RecordInfo spRecInfos[] =
+ {
+ { BIFF12_ID_AUTOFILTER, BIFF12_ID_AUTOFILTER + 1 },
+ { BIFF12_ID_CFRULE, BIFF12_ID_CFRULE + 1 },
+ { BIFF12_ID_COLBREAKS, BIFF12_ID_COLBREAKS + 1 },
+ { BIFF12_ID_COLORSCALE, BIFF12_ID_COLORSCALE + 1 },
+ { BIFF12_ID_COLS, BIFF12_ID_COLS + 1 },
+ { BIFF12_ID_CONDFORMATTING, BIFF12_ID_CONDFORMATTING + 1 },
+ { BIFF12_ID_CONTROLS, BIFF12_ID_CONTROLS + 2 },
+ { BIFF12_ID_CUSTOMFILTERS, BIFF12_ID_CUSTOMFILTERS + 1 },
+ { BIFF12_ID_CUSTOMSHEETVIEW, BIFF12_ID_CUSTOMSHEETVIEW + 1 },
+ { BIFF12_ID_CUSTOMSHEETVIEWS, BIFF12_ID_CUSTOMSHEETVIEWS + 3 },
+ { BIFF12_ID_DATABAR, BIFF12_ID_DATABAR + 1 },
+ { BIFF12_ID_DATAVALIDATIONS, BIFF12_ID_DATAVALIDATIONS + 1 },
+ { BIFF12_ID_DISCRETEFILTERS, BIFF12_ID_DISCRETEFILTERS + 1 },
+ { BIFF12_ID_FILTERCOLUMN, BIFF12_ID_FILTERCOLUMN + 1 },
+ { BIFF12_ID_HEADERFOOTER, BIFF12_ID_HEADERFOOTER + 1 },
+ { BIFF12_ID_ICONSET, BIFF12_ID_ICONSET + 1 },
+ { BIFF12_ID_MERGECELLS, BIFF12_ID_MERGECELLS + 1 },
+ { BIFF12_ID_OLEOBJECTS, BIFF12_ID_OLEOBJECTS + 2 },
+ { BIFF12_ID_ROW, -1 },
+ { BIFF12_ID_ROWBREAKS, BIFF12_ID_ROWBREAKS + 1 },
+ { BIFF12_ID_SCENARIO, BIFF12_ID_SCENARIO + 1 },
+ { BIFF12_ID_SCENARIOS, BIFF12_ID_SCENARIOS + 1 },
+ { BIFF12_ID_SHEETDATA, BIFF12_ID_SHEETDATA + 1 },
+ { BIFF12_ID_SHEETVIEW, BIFF12_ID_SHEETVIEW + 1 },
+ { BIFF12_ID_SHEETVIEWS, BIFF12_ID_SHEETVIEWS + 1 },
+ { BIFF12_ID_TABLEPARTS, BIFF12_ID_TABLEPARTS + 2 },
+ { BIFF12_ID_WORKSHEET, BIFF12_ID_WORKSHEET + 1 },
+ { -1, -1 }
+ };
+ return spRecInfos;
+}
+
+void WorksheetFragment::initializeImport()
+{
+ // initial processing in base class WorksheetHelper
+ initializeWorksheetImport();
+
+ // import query table fragments related to this worksheet
+ RelationsRef xQueryRels = getRelations().getRelationsFromTypeFromOfficeDoc( u"queryTable" );
+ for( const auto& rEntry : *xQueryRels )
+ importOoxFragment( new QueryTableFragment( *this, getFragmentPathFromRelation( rEntry.second ) ) );
+
+ // import pivot table fragments related to this worksheet
+ RelationsRef xPivotRels = getRelations().getRelationsFromTypeFromOfficeDoc( u"pivotTable" );
+ for( const auto& rEntry : *xPivotRels )
+ importOoxFragment( new PivotTableFragment( *this, getFragmentPathFromRelation( rEntry.second ) ) );
+}
+
+void WorksheetFragment::finalizeImport()
+{
+ // final processing in base class WorksheetHelper
+ finalizeWorksheetImport();
+}
+
+// private --------------------------------------------------------------------
+
+void WorksheetFragment::importPageSetUpPr( const AttributeList& rAttribs )
+{
+ // for whatever reason, this flag is still stored separated from the page settings
+ getPageSettings().setFitToPagesMode( rAttribs.getBool( XML_fitToPage, false ) );
+}
+
+void WorksheetFragment::importDimension( const AttributeList& rAttribs )
+{
+ ScRange aRange;
+ AddressConverter::convertToCellRangeUnchecked( aRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex() );
+ /* OOXML stores the used area, if existing, or "A1" if the sheet is empty.
+ In case of "A1", the dimension at the WorksheetHelper object will not
+ be set. If the cell A1 exists, the used area will be updated while
+ importing the cell. */
+ if( (aRange.aEnd.Col() > 0) || (aRange.aEnd.Row() > 0) )
+ {
+ extendUsedArea( aRange );
+ }
+}
+
+void WorksheetFragment::importSheetFormatPr( const AttributeList& rAttribs )
+{
+ // default column settings
+ setBaseColumnWidth( rAttribs.getInteger( XML_baseColWidth, 8 ) );
+ setDefaultColumnWidth( rAttribs.getDouble( XML_defaultColWidth, 0.0 ) );
+ // default row settings
+
+ // We don't need to import:
+ // XML_outlineLevelRow
+ // XML_outlineLevelCol
+ // as it will be updated during export to OOXML
+ double fDefaultRowHeight = rAttribs.getDouble(XML_defaultRowHeight, 0.0);
+ if (getFilter().isMSODocument())
+ {
+ fDefaultRowHeight -= fmod(fDefaultRowHeight, 0.75); //round down to 0.75pt
+ }
+ setDefaultRowSettings(
+ fDefaultRowHeight,
+ rAttribs.getBool( XML_customHeight, false ),
+ rAttribs.getBool( XML_zeroHeight, false ),
+ rAttribs.getBool( XML_thickTop, false ),
+ rAttribs.getBool( XML_thickBottom, false ) );
+}
+
+void WorksheetFragment::importCol( const AttributeList& rAttribs )
+{
+ ColumnModel aModel;
+ aModel.maRange.mnFirst = rAttribs.getInteger( XML_min, -1 );
+ aModel.maRange.mnLast = rAttribs.getInteger( XML_max, -1 );
+ aModel.mfWidth = rAttribs.getDouble( XML_width, 0.0 );
+ aModel.mnXfId = rAttribs.getInteger( XML_style, -1 );
+ aModel.mnLevel = rAttribs.getInteger( XML_outlineLevel, 0 );
+ aModel.mbShowPhonetic = rAttribs.getBool( XML_phonetic, false );
+ aModel.mbHidden = rAttribs.getBool( XML_hidden, false );
+ aModel.mbCollapsed = rAttribs.getBool( XML_collapsed, false );
+ // set column properties in the current sheet
+ setColumnModel( aModel );
+}
+
+void WorksheetFragment::importMergeCell( const AttributeList& rAttribs )
+{
+ ScRange aRange;
+ if( getAddressConverter().convertToCellRange( aRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex(), true, true ) )
+ getSheetData().setMergedRange( aRange );
+}
+
+void WorksheetFragment::importHyperlink( const AttributeList& rAttribs )
+{
+ HyperlinkModel aModel;
+ if( getAddressConverter().convertToCellRange( aModel.maRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex(), true, true ) )
+ {
+ aModel.maTarget = getRelations().getExternalTargetFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ aModel.maLocation = rAttribs.getXString( XML_location, OUString() );
+ aModel.maDisplay = rAttribs.getXString( XML_display, OUString() );
+ aModel.maTooltip = rAttribs.getXString( XML_tooltip, OUString() );
+ setHyperlink( aModel );
+ }
+}
+
+void WorksheetFragment::importBrk( const AttributeList& rAttribs, bool bRowBreak )
+{
+ PageBreakModel aModel;
+ aModel.mnColRow = rAttribs.getInteger( XML_id, 0 );
+ aModel.mnMin = rAttribs.getInteger( XML_min, aModel.mnColRow );
+ aModel.mnMax = rAttribs.getInteger( XML_max, aModel.mnColRow );
+ aModel.mbManual = rAttribs.getBool( XML_man, false );
+ setPageBreak( aModel, bRowBreak );
+}
+
+void WorksheetFragment::importDrawing( const AttributeList& rAttribs )
+{
+ setDrawingPath( getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ) );
+}
+
+void WorksheetFragment::importLegacyDrawing( const AttributeList& rAttribs )
+{
+ setVmlDrawingPath( getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) ) );
+}
+
+void WorksheetFragment::importOleObject( const AttributeList& rAttribs )
+{
+ ::oox::vml::OleObjectInfo aInfo;
+ aInfo.setShapeId( rAttribs.getInteger( XML_shapeId, 0 ) );
+ OSL_ENSURE( rAttribs.hasAttribute( XML_link ) != rAttribs.hasAttribute( R_TOKEN( id ) ),
+ "WorksheetFragment::importOleObject - OLE object must be either linked or embedded" );
+ aInfo.mbLinked = rAttribs.hasAttribute( XML_link );
+ if( aInfo.mbLinked )
+ aInfo.maTargetLink = getFormulaParser().importOleTargetLink( rAttribs.getString( XML_link, OUString() ) );
+ else if( rAttribs.hasAttribute( R_TOKEN( id ) ) )
+ importEmbeddedOleData( aInfo.maEmbeddedData, rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ aInfo.maProgId = rAttribs.getString( XML_progId, OUString() );
+ aInfo.mbShowAsIcon = rAttribs.getToken( XML_dvAspect, XML_DVASPECT_CONTENT ) == XML_DVASPECT_ICON;
+ aInfo.mbAutoUpdate = rAttribs.getToken( XML_oleUpdate, XML_OLEUPDATE_ONCALL ) == XML_OLEUPDATE_ALWAYS;
+ aInfo.mbAutoLoad = rAttribs.getBool( XML_autoLoad, false );
+ getVmlDrawing().registerOleObject( aInfo );
+}
+
+void WorksheetFragment::importControl( const AttributeList& rAttribs )
+{
+ ::oox::vml::ControlInfo aInfo;
+ aInfo.setShapeId( rAttribs.getInteger( XML_shapeId, 0 ) );
+ aInfo.maFragmentPath = getFragmentPathFromRelId( rAttribs.getString( R_TOKEN( id ), OUString() ) );
+ aInfo.maName = rAttribs.getString( XML_name, OUString() );
+ getVmlDrawing().registerControl( aInfo );
+}
+
+void WorksheetFragment::importDimension( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ aBinRange.read( rStrm );
+ ScRange aRange;
+ AddressConverter::convertToCellRangeUnchecked( aRange, aBinRange, getSheetIndex() );
+ /* BIFF12 stores the used area, if existing, or "A1" if the sheet is
+ empty. In case of "A1", the dimension at the WorksheetHelper object
+ will not be set. If the cell A1 exists, the used area will be updated
+ while importing the cell. */
+ if( (aRange.aEnd.Col() > 0) || (aRange.aEnd.Row() > 0) )
+ extendUsedArea( aRange );
+}
+
+void WorksheetFragment::importSheetFormatPr( SequenceInputStream& rStrm )
+{
+ sal_Int32 nDefaultWidth;
+ sal_uInt16 nBaseWidth, nDefaultHeight, nFlags;
+ nDefaultWidth = rStrm.readInt32();
+ nBaseWidth = rStrm.readuInt16();
+ nDefaultHeight = rStrm.readuInt16();
+ nFlags = rStrm.readuInt16();
+
+ // base column with
+ setBaseColumnWidth( nBaseWidth );
+ // default width is stored as 1/256th of a character in BIFF12, convert to entire character
+ setDefaultColumnWidth( static_cast< double >( nDefaultWidth ) / 256.0 );
+ // row height is in twips in BIFF12, convert to points; equal flags in all BIFFs
+ setDefaultRowSettings(
+ nDefaultHeight / 20.0,
+ getFlag( nFlags, BIFF_DEFROW_CUSTOMHEIGHT ),
+ getFlag( nFlags, BIFF_DEFROW_HIDDEN ),
+ getFlag( nFlags, BIFF_DEFROW_THICKTOP ),
+ getFlag( nFlags, BIFF_DEFROW_THICKBOTTOM ) );
+}
+
+void WorksheetFragment::importCol( SequenceInputStream& rStrm )
+{
+ ColumnModel aModel;
+
+ sal_Int32 nWidth;
+ sal_uInt16 nFlags;
+ aModel.maRange.mnFirst = rStrm.readInt32();
+ aModel.maRange.mnLast = rStrm.readInt32();
+ nWidth = rStrm.readInt32();
+ aModel.mnXfId = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+
+ // column indexes are 0-based in BIFF12, but ColumnModel expects 1-based
+ ++aModel.maRange.mnFirst;
+ ++aModel.maRange.mnLast;
+ // width is stored as 1/256th of a character in BIFF12, convert to entire character
+ aModel.mfWidth = static_cast< double >( nWidth ) / 256.0;
+ // equal flags in all BIFFs
+ aModel.mnLevel = extractValue< sal_Int32 >( nFlags, 8, 3 );
+ aModel.mbShowPhonetic = getFlag( nFlags, BIFF_COLINFO_SHOWPHONETIC );
+ aModel.mbHidden = getFlag( nFlags, BIFF_COLINFO_HIDDEN );
+ aModel.mbCollapsed = getFlag( nFlags, BIFF_COLINFO_COLLAPSED );
+ // set column properties in the current sheet
+ setColumnModel( aModel );
+}
+
+void WorksheetFragment::importMergeCell( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ ScRange aRange;
+ if( getAddressConverter().convertToCellRange( aRange, aBinRange, getSheetIndex(), true, true ) )
+ getSheetData().setMergedRange( aRange );
+}
+
+void WorksheetFragment::importHyperlink( SequenceInputStream& rStrm )
+{
+ BinRange aBinRange;
+ rStrm >> aBinRange;
+ HyperlinkModel aModel;
+ if( getAddressConverter().convertToCellRange( aModel.maRange, aBinRange, getSheetIndex(), true, true ) )
+ {
+ aModel.maTarget = getRelations().getExternalTargetFromRelId( BiffHelper::readString( rStrm ) );
+ rStrm >> aModel.maLocation >> aModel.maTooltip >> aModel.maDisplay;
+ setHyperlink( aModel );
+ }
+}
+
+void WorksheetFragment::importBrk( SequenceInputStream& rStrm, bool bRowBreak )
+{
+ PageBreakModel aModel;
+ sal_Int32 nManual;
+ aModel.mnColRow = rStrm.readInt32();
+ aModel.mnMin = rStrm.readInt32();
+ aModel.mnMax = rStrm.readInt32();
+ nManual = rStrm.readInt32();
+ aModel.mbManual = nManual != 0;
+ setPageBreak( aModel, bRowBreak );
+}
+
+void WorksheetFragment::importDrawing( SequenceInputStream& rStrm )
+{
+ setDrawingPath( getFragmentPathFromRelId( BiffHelper::readString( rStrm ) ) );
+}
+
+void WorksheetFragment::importLegacyDrawing( SequenceInputStream& rStrm )
+{
+ setVmlDrawingPath( getFragmentPathFromRelId( BiffHelper::readString( rStrm ) ) );
+}
+
+void WorksheetFragment::importOleObject( SequenceInputStream& rStrm )
+{
+ ::oox::vml::OleObjectInfo aInfo;
+ sal_Int32 nAspect, nUpdateMode, nShapeId;
+ sal_uInt16 nFlags;
+ nAspect = rStrm.readInt32();
+ nUpdateMode = rStrm.readInt32();
+ nShapeId = rStrm.readInt32();
+ nFlags = rStrm.readuInt16();
+ rStrm >> aInfo.maProgId;
+ aInfo.mbLinked = getFlag( nFlags, BIFF12_OLEOBJECT_LINKED );
+ if( aInfo.mbLinked )
+ aInfo.maTargetLink = getFormulaParser().importOleTargetLink( rStrm );
+ else
+ importEmbeddedOleData( aInfo.maEmbeddedData, BiffHelper::readString( rStrm ) );
+ aInfo.setShapeId( nShapeId );
+ aInfo.mbShowAsIcon = nAspect == BIFF12_OLEOBJECT_ICON;
+ aInfo.mbAutoUpdate = nUpdateMode == BIFF12_OLEOBJECT_ALWAYS;
+ aInfo.mbAutoLoad = getFlag( nFlags, BIFF12_OLEOBJECT_AUTOLOAD );
+ getVmlDrawing().registerOleObject( aInfo );
+}
+
+void WorksheetFragment::importControl( SequenceInputStream& rStrm )
+{
+ ::oox::vml::ControlInfo aInfo;
+ aInfo.setShapeId( rStrm.readInt32() );
+ aInfo.maFragmentPath = getFragmentPathFromRelId( BiffHelper::readString( rStrm ) );
+ rStrm >> aInfo.maName;
+ getVmlDrawing().registerControl( aInfo );
+}
+
+void WorksheetFragment::importEmbeddedOleData( StreamDataSequence& orEmbeddedData, const OUString& rRelId )
+{
+ OUString aFragmentPath = getFragmentPathFromRelId( rRelId );
+ if( !aFragmentPath.isEmpty() )
+ getBaseFilter().importBinaryData( orEmbeddedData, aFragmentPath );
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/worksheethelper.cxx b/sc/source/filter/oox/worksheethelper.cxx
new file mode 100644
index 000000000..0d0aa8ce7
--- /dev/null
+++ b/sc/source/filter/oox/worksheethelper.cxx
@@ -0,0 +1,1655 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <worksheethelper.hxx>
+
+#include <algorithm>
+#include <utility>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/sheet/ConditionOperator2.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+#include <com/sun/star/sheet/XCellAddressable.hpp>
+#include <com/sun/star/sheet/XMultiFormulaTokens.hpp>
+#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
+#include <com/sun/star/sheet/XSheetCondition2.hpp>
+#include <com/sun/star/sheet/XSheetOutline.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/propertyset.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <addressconverter.hxx>
+#include <autofilterbuffer.hxx>
+#include <commentsbuffer.hxx>
+#include <condformatbuffer.hxx>
+#include <document.hxx>
+#include <drawingfragment.hxx>
+#include <pagesettings.hxx>
+#include <querytablebuffer.hxx>
+#include <sheetdatabuffer.hxx>
+#include <stylesbuffer.hxx>
+#include <tokenuno.hxx>
+#include <unitconverter.hxx>
+#include <viewsettings.hxx>
+#include <worksheetbuffer.hxx>
+#include <worksheetsettings.hxx>
+#include <formulabuffer.hxx>
+#include <scitems.hxx>
+#include <editutil.hxx>
+#include <tokenarray.hxx>
+#include <tablebuffer.hxx>
+#include <documentimport.hxx>
+#include <stlsheet.hxx>
+#include <stlpool.hxx>
+#include <cellvalue.hxx>
+#include <columnspanset.hxx>
+#include <dbdata.hxx>
+
+#include <svl/stritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/flditem.hxx>
+#include <tools/gen.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sheet;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+void lclUpdateProgressBar( const ISegmentProgressBarRef& rxProgressBar, double fPosition )
+{
+ if( rxProgressBar )
+ rxProgressBar->setPosition( fPosition );
+}
+
+// TODO Needed because input might be >32-bit (in 64-bit builds),
+// or a negative, already overflown value (in 32-bit builds)
+sal_Int32 lclClampToNonNegativeInt32( tools::Long aVal )
+{
+ if ( aVal > SAL_MAX_INT32 || aVal < 0 )
+ {
+ SAL_WARN( "sc.filter", "Overflow detected, " << aVal << " does not fit into sal_Int32, or is negative." );
+ return SAL_MAX_INT32;
+ }
+ return static_cast<sal_Int32>( aVal );
+}
+
+} // namespace
+
+ColumnModel::ColumnModel() :
+ maRange( -1 ),
+ mfWidth( 0.0 ),
+ mnXfId( -1 ),
+ mnLevel( 0 ),
+ mbShowPhonetic( false ),
+ mbHidden( false ),
+ mbCollapsed( false )
+{
+}
+
+bool ColumnModel::isMergeable( const ColumnModel& rModel ) const
+{
+ return
+ (maRange.mnFirst <= rModel.maRange.mnFirst) &&
+ (rModel.maRange.mnFirst <= maRange.mnLast + 1) &&
+ (mfWidth == rModel.mfWidth) &&
+ // ignore mnXfId, cell formatting is always set directly
+ (mnLevel == rModel.mnLevel) &&
+ (mbHidden == rModel.mbHidden) &&
+ (mbCollapsed == rModel.mbCollapsed);
+}
+
+RowModel::RowModel() :
+ mnRow( -1 ),
+ mfHeight( 0.0 ),
+ mnXfId( -1 ),
+ mnLevel( 0 ),
+ mbCustomHeight( false ),
+ mbCustomFormat( false ),
+ mbShowPhonetic( false ),
+ mbHidden( false ),
+ mbCollapsed( false ),
+ mbThickTop( false ),
+ mbThickBottom( false )
+{
+}
+
+void RowModel::insertColSpan( const ValueRange& rColSpan )
+{
+ if( (0 <= rColSpan.mnFirst) && (rColSpan.mnFirst <= rColSpan.mnLast) )
+ maColSpans.insert( rColSpan );
+}
+
+bool RowModel::isMergeable( const RowModel& rModel ) const
+{
+ return
+ // ignore maColSpans - is handled separately in SheetDataBuffer class
+ (mfHeight == rModel.mfHeight) &&
+ // ignore mnXfId, mbCustomFormat, mbShowPhonetic - cell formatting is always set directly
+ (mnLevel == rModel.mnLevel) &&
+ (mbCustomHeight == rModel.mbCustomHeight) &&
+ (mbHidden == rModel.mbHidden) &&
+ (mbCollapsed == rModel.mbCollapsed);
+}
+
+PageBreakModel::PageBreakModel()
+ : mnColRow(0)
+ , mnMin(0)
+ , mnMax(0)
+ , mbManual(false)
+{
+}
+
+HyperlinkModel::HyperlinkModel()
+{
+}
+
+ValidationModel::ValidationModel() :
+ mnType( XML_none ),
+ mnOperator( XML_between ),
+ mnErrorStyle( XML_stop ),
+ mbShowInputMsg( false ),
+ mbShowErrorMsg( false ),
+ mbNoDropDown( false ),
+ mbAllowBlank( false )
+{
+}
+
+void ValidationModel::setBiffType( sal_uInt8 nType )
+{
+ static const sal_Int32 spnTypeIds[] = {
+ XML_none, XML_whole, XML_decimal, XML_list, XML_date, XML_time, XML_textLength, XML_custom };
+ mnType = STATIC_ARRAY_SELECT( spnTypeIds, nType, XML_none );
+}
+
+void ValidationModel::setBiffOperator( sal_uInt8 nOperator )
+{
+ static const sal_Int32 spnOperators[] = {
+ XML_between, XML_notBetween, XML_equal, XML_notEqual,
+ XML_greaterThan, XML_lessThan, XML_greaterThanOrEqual, XML_lessThanOrEqual };
+ mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
+}
+
+void ValidationModel::setBiffErrorStyle( sal_uInt8 nErrorStyle )
+{
+ static const sal_Int32 spnErrorStyles[] = { XML_stop, XML_warning, XML_information };
+ mnErrorStyle = STATIC_ARRAY_SELECT( spnErrorStyles, nErrorStyle, XML_stop );
+}
+
+class WorksheetGlobals : public WorkbookHelper, public IWorksheetProgress
+{
+public:
+ explicit WorksheetGlobals(
+ const WorkbookHelper& rHelper,
+ const ISegmentProgressBarRef& rxProgressBar,
+ WorksheetType eSheetType,
+ SCTAB nSheet );
+
+ /** Returns true, if this helper refers to an existing Calc sheet. */
+ bool isValidSheet() const { return mxSheet.is(); }
+
+ /** Returns the type of this sheet. */
+ WorksheetType getSheetType() const { return meSheetType; }
+ /** Returns the index of the current sheet. */
+ SCTAB getSheetIndex() const { return maUsedArea.aStart.Tab(); }
+ /** Returns the XSpreadsheet interface of the current sheet. */
+ const Reference< XSpreadsheet >& getSheet() const { return mxSheet; }
+
+ /** Returns the XCell interface for the passed cell address. */
+ Reference< XCell > getCell( const ScAddress& rAddress ) const;
+ /** Returns the XCellRange interface for the passed cell range address. */
+ Reference< XCellRange > getCellRange( const ScRange& rRange ) const;
+ /** Returns the XSheetCellRanges interface for the passed cell range addresses. */
+ Reference< XSheetCellRanges > getCellRangeList( const ScRangeList& rRanges ) const;
+
+ /** Returns the XCellRange interface for a column. */
+ Reference< XCellRange > getColumn( sal_Int32 nCol ) const;
+ /** Returns the XCellRange interface for a row. */
+ Reference< XCellRange > getRow( sal_Int32 nRow ) const;
+
+ /** Returns the XDrawPage interface of the draw page of the current sheet. */
+ Reference< XDrawPage > getDrawPage() const;
+ /** Returns the size of the entire drawing page in 1/100 mm. */
+ const awt::Size& getDrawPageSize() const;
+
+ /** Returns the absolute position of the top-left corner of the cell in 1/100 mm. */
+ awt::Point getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const;
+
+ /** Returns the address of the cell that contains the passed point in 1/100 mm. */
+ ScAddress getCellAddressFromPosition( const awt::Point& rPosition ) const;
+ /** Returns the cell range address that contains the passed rectangle in 1/100 mm. */
+ ScRange getCellRangeFromRectangle( const awt::Rectangle& rRect ) const;
+
+ /** Returns the buffer for cell contents and cell formatting. */
+ SheetDataBuffer& getSheetData() { return maSheetData; }
+ /** Returns the conditional formatting in this sheet. */
+ CondFormatBuffer& getCondFormats() { return maCondFormats; }
+ /** Returns the buffer for all cell comments in this sheet. */
+ CommentsBuffer& getComments() { return maComments; }
+ /** Returns the auto filters for the sheet. */
+ AutoFilterBuffer& getAutoFilters() { return maAutoFilters; }
+ /** Returns the buffer for all web query tables in this sheet. */
+ QueryTableBuffer& getQueryTables() { return maQueryTables; }
+ /** Returns the worksheet settings object. */
+ WorksheetSettings& getWorksheetSettings() { return maSheetSett; }
+ /** Returns the page/print settings for this sheet. */
+ PageSettings& getPageSettings() { return maPageSett; }
+ /** Returns the view settings for this sheet. */
+ SheetViewSettings& getSheetViewSettings() { return maSheetViewSett; }
+ /** Returns the VML drawing page for this sheet (OOXML/BIFF12 only). */
+ VmlDrawing& getVmlDrawing() { return *mxVmlDrawing; }
+ /** returns the ExtLst entries that need to be filled */
+ ExtLst& getExtLst() { return maExtLst; }
+
+ /** Sets a column or row page break described in the passed struct. */
+ void setPageBreak( const PageBreakModel& rModel, bool bRowBreak );
+ /** Inserts the hyperlink URL into the spreadsheet. */
+ void setHyperlink( const HyperlinkModel& rModel );
+ /** Inserts the data validation settings into the spreadsheet. */
+ void setValidation( const ValidationModel& rModel );
+ /** Sets the path to the DrawingML fragment of this sheet. */
+ void setDrawingPath( const OUString& rDrawingPath );
+ /** Sets the path to the legacy VML drawing fragment of this sheet. */
+ void setVmlDrawingPath( const OUString& rVmlDrawingPath );
+
+ /** Extends the used area of this sheet by the passed cell position. */
+ void extendUsedArea( const ScAddress& rAddress );
+
+ /** Extends the used area of this sheet by the passed cell range. */
+ void extendUsedArea( const ScRange& rRange );
+ /** Extends the shape bounding box by the position and size of the passed rectangle. */
+ void extendShapeBoundingBox( const awt::Rectangle& rShapeRect );
+
+ /** Sets base width for all columns (without padding pixels). This value
+ is only used, if base width has not been set with setDefaultColumnWidth(). */
+ void setBaseColumnWidth( sal_Int32 nWidth );
+ /** Sets default width for all columns. This function overrides the base
+ width set with the setBaseColumnWidth() function. */
+ void setDefaultColumnWidth( double fWidth );
+ /** Sets column settings for a specific column range.
+ @descr Column default formatting is converted directly, other settings
+ are cached and converted in the finalizeImport() call. */
+ void setColumnModel( const ColumnModel& rModel );
+ /** Converts column default cell formatting. */
+ void convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId );
+
+ /** Sets default height and hidden state for all unused rows in the sheet. */
+ void setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom );
+ /** Sets row settings for a specific row.
+ @descr Row default formatting is converted directly, other settings
+ are cached and converted in the finalizeImport() call. */
+ void setRowModel( const RowModel& rModel );
+
+ /** Initial conversion before importing the worksheet. */
+ void initializeWorksheetImport();
+ /** Final conversion after importing the worksheet. */
+ void finalizeWorksheetImport();
+
+ void finalizeDrawingImport();
+
+ /// Allow the threaded importer to override our progress bar impl.
+ virtual ISegmentProgressBarRef getRowProgress() override
+ {
+ return mxRowProgress;
+ }
+ virtual void setCustomRowProgress( const ISegmentProgressBarRef &rxRowProgress ) override
+ {
+ mxRowProgress = rxRowProgress;
+ mbFastRowProgress = true;
+ }
+
+private:
+ typedef ::std::vector< sal_Int32 > OutlineLevelVec;
+ typedef ::std::pair< ColumnModel, sal_Int32 > ColumnModelRange;
+ typedef ::std::map< sal_Int32, ColumnModelRange > ColumnModelRangeMap;
+ typedef ::std::pair< RowModel, sal_Int32 > RowModelRange;
+ typedef ::std::map< sal_Int32, RowModelRange > RowModelRangeMap;
+
+ /** Inserts all imported hyperlinks into their cell ranges. */
+ void finalizeHyperlinkRanges();
+ /** Generates the final URL for the passed hyperlink. */
+ OUString getHyperlinkUrl( const HyperlinkModel& rHyperlink ) const;
+ /** Inserts a hyperlinks into the specified cell. */
+ void insertHyperlink( const ScAddress& rAddress, const OUString& rUrl );
+
+ /** Inserts all imported data validations into their cell ranges. */
+ void finalizeValidationRanges() const;
+
+ /** Converts column properties for all columns in the sheet. */
+ void convertColumns();
+ /** Converts column properties. */
+ void convertColumns( OutlineLevelVec& orColLevels, const ValueRange& rColRange, const ColumnModel& rModel );
+
+ /** Converts row properties for all rows in the sheet. */
+ void convertRows(const std::vector<sc::ColRowSpan>& rSpans);
+ /** Converts row properties. */
+ void convertRows(OutlineLevelVec& orRowLevels, const ValueRange& rRowRange,
+ const RowModel& rModel,
+ const std::vector<sc::ColRowSpan>& rSpans,
+ double fDefHeight = -1.0);
+
+ /** Converts outline grouping for the passed column or row. */
+ void convertOutlines( OutlineLevelVec& orLevels, sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows );
+ /** Groups columns or rows for the given range. */
+ void groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapsed, bool bRows );
+
+ /** Imports the drawings of the sheet (DML, VML, DFF) and updates the used area. */
+ void finalizeDrawings();
+
+ /** Update the row import progress bar */
+ void UpdateRowProgress( const ScRange& rUsedArea, SCROW nRow );
+
+private:
+ typedef ::std::unique_ptr< VmlDrawing > VmlDrawingPtr;
+
+ const ScAddress& mrMaxApiPos; /// Reference to maximum Calc cell address from address converter.
+ ScRange maUsedArea; /// Used area of the sheet, and sheet index of the sheet.
+ ColumnModel maDefColModel; /// Default column formatting.
+ ColumnModelRangeMap maColModels; /// Ranges of columns sorted by first column index.
+ RowModel maDefRowModel; /// Default row formatting.
+ RowModelRangeMap maRowModels; /// Ranges of rows sorted by first row index.
+ std::vector< HyperlinkModel > maHyperlinks; /// Cell ranges containing hyperlinks.
+ std::vector< ValidationModel > maValidations; /// Cell ranges containing data validation settings.
+ SheetDataBuffer maSheetData; /// Buffer for cell contents and cell formatting.
+ CondFormatBuffer maCondFormats; /// Buffer for conditional formatting.
+ CommentsBuffer maComments; /// Buffer for all cell comments in this sheet.
+ AutoFilterBuffer maAutoFilters; /// Sheet auto filters (not associated to a table).
+ QueryTableBuffer maQueryTables; /// Buffer for all web query tables in this sheet.
+ WorksheetSettings maSheetSett; /// Global settings for this sheet.
+ PageSettings maPageSett; /// Page/print settings for this sheet.
+ SheetViewSettings maSheetViewSett; /// View settings for this sheet.
+ VmlDrawingPtr mxVmlDrawing; /// Collection of all VML shapes.
+ ExtLst maExtLst; /// List of extended elements
+ OUString maDrawingPath; /// Path to DrawingML fragment.
+ OUString maVmlDrawingPath; /// Path to legacy VML drawing fragment.
+ awt::Size maDrawPageSize; /// Current size of the drawing page in 1/100 mm.
+ awt::Rectangle maShapeBoundingBox; /// Bounding box for all shapes from all drawings.
+ ISegmentProgressBarRef mxProgressBar; /// Sheet progress bar.
+ bool mbFastRowProgress; /// Do we have a progress bar thread ?
+ ISegmentProgressBarRef mxRowProgress; /// Progress bar for row/cell processing.
+ ISegmentProgressBarRef mxFinalProgress; /// Progress bar for finalization.
+ WorksheetType meSheetType; /// Type of this sheet.
+ Reference< XSpreadsheet > mxSheet; /// Reference to the current sheet.
+ bool mbHasDefWidth; /// True = default column width is set from defaultColWidth attribute.
+};
+
+constexpr OUStringLiteral gaSheetCellRanges( u"com.sun.star.sheet.SheetCellRanges" ); /// Service name for a SheetCellRanges object.
+
+WorksheetGlobals::WorksheetGlobals( const WorkbookHelper& rHelper, const ISegmentProgressBarRef& rxProgressBar, WorksheetType eSheetType, SCTAB nSheet ) :
+ WorkbookHelper( rHelper ),
+ mrMaxApiPos( rHelper.getAddressConverter().getMaxApiAddress() ),
+ maUsedArea( SCCOL_MAX, SCROW_MAX, nSheet, -1, -1, nSheet ), // Set start address to largest possible value, and end address to smallest
+ maSheetData( *this ),
+ maCondFormats( *this ),
+ maComments( *this ),
+ maAutoFilters( *this ),
+ maQueryTables( *this ),
+ maSheetSett( *this ),
+ maPageSett( *this ),
+ maSheetViewSett( *this ),
+ mxProgressBar( rxProgressBar ),
+ mbFastRowProgress( false ),
+ meSheetType( eSheetType ),
+ mxSheet(getSheetFromDoc( nSheet )),
+ mbHasDefWidth( false )
+{
+ if( !mxSheet.is() )
+ maUsedArea.aStart.SetTab( -1 );
+
+ // default column settings (width and hidden state may be updated later)
+ maDefColModel.mfWidth = 8.5;
+ maDefColModel.mnXfId = -1;
+ maDefColModel.mnLevel = 0;
+ maDefColModel.mbHidden = false;
+ maDefColModel.mbCollapsed = false;
+
+ // default row settings (height and hidden state may be updated later)
+ maDefRowModel.mfHeight = 0.0;
+ maDefRowModel.mnXfId = -1;
+ maDefRowModel.mnLevel = 0;
+ maDefRowModel.mbCustomHeight = false;
+ maDefRowModel.mbCustomFormat = false;
+ maDefRowModel.mbShowPhonetic = false;
+ maDefRowModel.mbHidden = false;
+ maDefRowModel.mbCollapsed = false;
+
+ // buffers
+ mxVmlDrawing.reset( new VmlDrawing( *this ) );
+
+ // prepare progress bars
+ if( mxProgressBar )
+ {
+ mxRowProgress = mxProgressBar->createSegment( 0.5 );
+ mxFinalProgress = mxProgressBar->createSegment( 0.5 );
+ }
+}
+
+Reference< XCell > WorksheetGlobals::getCell( const ScAddress& rAddress ) const
+{
+ Reference< XCell > xCell;
+ if( mxSheet.is() ) try
+ {
+ xCell = mxSheet->getCellByPosition( rAddress.Col(), rAddress.Row() );
+ }
+ catch( Exception& )
+ {
+ }
+ return xCell;
+}
+
+Reference< XCellRange > WorksheetGlobals::getCellRange( const ScRange& rRange ) const
+{
+ Reference< XCellRange > xRange;
+ if( mxSheet.is() ) try
+ {
+ xRange = mxSheet->getCellRangeByPosition( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRange;
+}
+
+Reference< XSheetCellRanges > WorksheetGlobals::getCellRangeList( const ScRangeList& rRanges ) const
+{
+ Reference< XSheetCellRanges > xRanges;
+ if( mxSheet.is() && !rRanges.empty() ) try
+ {
+ xRanges.set( getBaseFilter().getModelFactory()->createInstance( gaSheetCellRanges ), UNO_QUERY_THROW );
+ Reference< XSheetCellRangeContainer > xRangeCont( xRanges, UNO_QUERY_THROW );
+ xRangeCont->addRangeAddresses( AddressConverter::toApiSequence(rRanges), false );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRanges;
+}
+
+Reference< XCellRange > WorksheetGlobals::getColumn( sal_Int32 nCol ) const
+{
+ Reference< XCellRange > xColumn;
+ try
+ {
+ Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW );
+ Reference< XTableColumns > xColumns( xColRowRange->getColumns(), UNO_SET_THROW );
+ xColumn.set( xColumns->getByIndex( nCol ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xColumn;
+}
+
+Reference< XCellRange > WorksheetGlobals::getRow( sal_Int32 nRow ) const
+{
+ Reference< XCellRange > xRow;
+ try
+ {
+ Reference< XColumnRowRange > xColRowRange( mxSheet, UNO_QUERY_THROW );
+ Reference< XTableRows > xRows( xColRowRange->getRows(), UNO_SET_THROW );
+ xRow.set( xRows->getByIndex( nRow ), UNO_QUERY );
+ }
+ catch( Exception& )
+ {
+ }
+ return xRow;
+}
+
+Reference< XDrawPage > WorksheetGlobals::getDrawPage() const
+{
+ Reference< XDrawPage > xDrawPage;
+ try
+ {
+ xDrawPage = Reference< XDrawPageSupplier >( mxSheet, UNO_QUERY_THROW )->getDrawPage();
+ }
+ catch( Exception& )
+ {
+ }
+ return xDrawPage;
+}
+
+const awt::Size& WorksheetGlobals::getDrawPageSize() const
+{
+ OSL_ENSURE( (maDrawPageSize.Width > 0) && (maDrawPageSize.Height > 0), "WorksheetGlobals::getDrawPageSize - called too early, size invalid" );
+ return maDrawPageSize;
+}
+
+awt::Point WorksheetGlobals::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ const tools::Rectangle aMMRect( getScDocument().GetMMRect( nCol, nRow, nCol, nRow, getSheetIndex() ) );
+ awt::Point aPoint( lclClampToNonNegativeInt32( aMMRect.Left() ),
+ lclClampToNonNegativeInt32( aMMRect.Top() ) );
+ return aPoint;
+}
+
+namespace {
+
+sal_Int32 lclGetMidAddr( sal_Int32 nBegAddr, sal_Int32 nEndAddr, sal_Int32 nBegPos, sal_Int32 nEndPos, sal_Int32 nSearchPos )
+{
+ // use sal_Int64 to prevent integer overflow
+ return nBegAddr + 1 + static_cast< sal_Int32 >( static_cast< sal_Int64 >( nEndAddr - nBegAddr - 2 ) * (nSearchPos - nBegPos) / (nEndPos - nBegPos) );
+}
+
+bool lclPrepareInterval( sal_Int32 nBegAddr, sal_Int32& rnMidAddr, sal_Int32 nEndAddr,
+ sal_Int32 nBegPos, sal_Int32 nEndPos, sal_Int32 nSearchPos )
+{
+ // searched position before nBegPos -> use nBegAddr
+ if( nSearchPos <= nBegPos )
+ {
+ rnMidAddr = nBegAddr;
+ return false;
+ }
+
+ // searched position after nEndPos, or begin next to end -> use nEndAddr
+ if( (nSearchPos >= nEndPos) || (nBegAddr + 1 >= nEndAddr) )
+ {
+ rnMidAddr = nEndAddr;
+ return false;
+ }
+
+ /* Otherwise find mid address according to position. lclGetMidAddr() will
+ return an address between nBegAddr and nEndAddr. */
+ rnMidAddr = lclGetMidAddr( nBegAddr, nEndAddr, nBegPos, nEndPos, nSearchPos );
+ return true;
+}
+
+bool lclUpdateInterval( sal_Int32& rnBegAddr, sal_Int32& rnMidAddr, sal_Int32& rnEndAddr,
+ sal_Int32& rnBegPos, sal_Int32 nMidPos, sal_Int32& rnEndPos, sal_Int32 nSearchPos )
+{
+ // nSearchPos < nMidPos: use the interval [begin,mid] in the next iteration
+ if( nSearchPos < nMidPos )
+ {
+ // if rnBegAddr is next to rnMidAddr, the latter is the column/row in question
+ if( rnBegAddr + 1 >= rnMidAddr )
+ return false;
+ // otherwise, set interval end to mid
+ rnEndPos = nMidPos;
+ rnEndAddr = rnMidAddr;
+ rnMidAddr = lclGetMidAddr( rnBegAddr, rnEndAddr, rnBegPos, rnEndPos, nSearchPos );
+ return true;
+ }
+
+ // nSearchPos > nMidPos: use the interval [mid,end] in the next iteration
+ if( nSearchPos > nMidPos )
+ {
+ // if rnMidAddr is next to rnEndAddr, the latter is the column/row in question
+ if( rnMidAddr + 1 >= rnEndAddr )
+ {
+ rnMidAddr = rnEndAddr;
+ return false;
+ }
+ // otherwise, set interval start to mid
+ rnBegPos = nMidPos;
+ rnBegAddr = rnMidAddr;
+ rnMidAddr = lclGetMidAddr( rnBegAddr, rnEndAddr, rnBegPos, rnEndPos, nSearchPos );
+ return true;
+ }
+
+ // nSearchPos == nMidPos: rnMidAddr is the column/row in question, do not loop anymore
+ return false;
+}
+
+} // namespace
+
+ScAddress WorksheetGlobals::getCellAddressFromPosition( const awt::Point& rPosition ) const
+{
+ // starting cell address and its position in drawing layer (top-left edge)
+ sal_Int32 nBegCol = 0;
+ sal_Int32 nBegRow = 0;
+ awt::Point aBegPos( 0, 0 );
+
+ // end cell address and its position in drawing layer (bottom-right edge)
+ sal_Int32 nEndCol = mrMaxApiPos.Col() + 1;
+ sal_Int32 nEndRow = mrMaxApiPos.Row() + 1;
+ awt::Point aEndPos( maDrawPageSize.Width, maDrawPageSize.Height );
+
+ // starting point for interval search
+ sal_Int32 nMidCol, nMidRow;
+ bool bLoopCols = lclPrepareInterval( nBegCol, nMidCol, nEndCol, aBegPos.X, aEndPos.X, rPosition.X );
+ bool bLoopRows = lclPrepareInterval( nBegRow, nMidRow, nEndRow, aBegPos.Y, aEndPos.Y, rPosition.Y );
+ awt::Point aMidPos = getCellPosition( nMidCol, nMidRow );
+
+ /* The loop will find the column/row index of the cell right of/below
+ the cell containing the passed point, unless the point is located at
+ the top or left border of the containing cell. */
+ while( bLoopCols || bLoopRows )
+ {
+ bLoopCols = bLoopCols && lclUpdateInterval( nBegCol, nMidCol, nEndCol, aBegPos.X, aMidPos.X, aEndPos.X, rPosition.X );
+ bLoopRows = bLoopRows && lclUpdateInterval( nBegRow, nMidRow, nEndRow, aBegPos.Y, aMidPos.Y, aEndPos.Y, rPosition.Y );
+ aMidPos = getCellPosition( nMidCol, nMidRow );
+ }
+
+ /* The cell left of/above the current search position contains the passed
+ point, unless the point is located on the top/left border of the cell,
+ or the last column/row of the sheet has been reached. */
+ if( aMidPos.X > rPosition.X ) --nMidCol;
+ if( aMidPos.Y > rPosition.Y ) --nMidRow;
+ return ScAddress( nMidCol, nMidRow, getSheetIndex() );
+}
+
+ScRange WorksheetGlobals::getCellRangeFromRectangle( const awt::Rectangle& rRect ) const
+{
+ ScAddress aStartAddr = getCellAddressFromPosition( awt::Point( rRect.X, rRect.Y ) );
+ awt::Point aBotRight( rRect.X + rRect.Width, rRect.Y + rRect.Height );
+ ScAddress aEndAddr = getCellAddressFromPosition( aBotRight );
+ bool bMultiCols = aStartAddr.Col() < aEndAddr.Col();
+ bool bMultiRows = aStartAddr.Row() < aEndAddr.Row();
+ if( bMultiCols || bMultiRows )
+ {
+ /* Reduce end position of the cell range to previous column or row, if
+ the rectangle ends exactly between two columns or rows. */
+ awt::Point aEndPos = getCellPosition( aEndAddr.Col(), aEndAddr.Row() );
+ if( bMultiCols && (aBotRight.X <= aEndPos.X) )
+ aEndAddr.IncCol(-1);
+ if( bMultiRows && (aBotRight.Y <= aEndPos.Y) )
+ aEndAddr.IncRow(-1);
+ }
+ return ScRange( aStartAddr.Col(), aStartAddr.Row(), getSheetIndex(),
+ aEndAddr.Col(), aEndAddr.Row(), getSheetIndex() );
+}
+
+void WorksheetGlobals::setPageBreak( const PageBreakModel& rModel, bool bRowBreak )
+{
+ if( rModel.mbManual && (rModel.mnColRow > 0) )
+ {
+ PropertySet aPropSet( bRowBreak ? getRow( rModel.mnColRow ) : getColumn( rModel.mnColRow ) );
+ aPropSet.setProperty( PROP_IsStartOfNewPage, true );
+ }
+}
+
+void WorksheetGlobals::setHyperlink( const HyperlinkModel& rModel )
+{
+ maHyperlinks.push_back( rModel );
+}
+
+void WorksheetGlobals::setValidation( const ValidationModel& rModel )
+{
+ maValidations.push_back( rModel );
+}
+
+void WorksheetGlobals::setDrawingPath( const OUString& rDrawingPath )
+{
+ maDrawingPath = rDrawingPath;
+}
+
+void WorksheetGlobals::setVmlDrawingPath( const OUString& rVmlDrawingPath )
+{
+ maVmlDrawingPath = rVmlDrawingPath;
+}
+
+void WorksheetGlobals::extendUsedArea( const ScAddress& rAddress )
+{
+ maUsedArea.aStart.SetCol( ::std::min( maUsedArea.aStart.Col(), rAddress.Col() ) );
+ maUsedArea.aStart.SetRow( ::std::min( maUsedArea.aStart.Row(), rAddress.Row() ) );
+ maUsedArea.aEnd.SetCol( ::std::max( maUsedArea.aEnd.Col(), rAddress.Col() ) );
+ maUsedArea.aEnd.SetRow( ::std::max( maUsedArea.aEnd.Row(), rAddress.Row() ) );
+}
+
+void WorksheetGlobals::extendUsedArea( const ScRange& rRange )
+{
+ extendUsedArea( rRange.aStart );
+ extendUsedArea( rRange.aEnd );
+}
+
+void WorksheetHelper::extendUsedArea( const ScRange& rRange )
+{
+ extendUsedArea( rRange.aStart );
+ extendUsedArea( rRange.aEnd );
+}
+
+void WorksheetGlobals::extendShapeBoundingBox( const awt::Rectangle& rShapeRect )
+{
+ if( (maShapeBoundingBox.Width == 0) && (maShapeBoundingBox.Height == 0) )
+ {
+ // width and height of maShapeBoundingBox are assumed to be zero on first cell
+ maShapeBoundingBox = rShapeRect;
+ }
+ else
+ {
+ sal_Int32 nEndX = ::std::max( maShapeBoundingBox.X + maShapeBoundingBox.Width, rShapeRect.X + rShapeRect.Width );
+ sal_Int32 nEndY = ::std::max( maShapeBoundingBox.Y + maShapeBoundingBox.Height, rShapeRect.Y + rShapeRect.Height );
+ maShapeBoundingBox.X = ::std::min( maShapeBoundingBox.X, rShapeRect.X );
+ maShapeBoundingBox.Y = ::std::min( maShapeBoundingBox.Y, rShapeRect.Y );
+ maShapeBoundingBox.Width = nEndX - maShapeBoundingBox.X;
+ maShapeBoundingBox.Height = nEndY - maShapeBoundingBox.Y;
+ }
+}
+
+void WorksheetGlobals::setBaseColumnWidth( sal_Int32 nWidth )
+{
+ // do not modify width, if setDefaultColumnWidth() has been used
+ if( !mbHasDefWidth && (nWidth > 0) )
+ {
+ // #i3006# add 5 pixels padding to the width
+ maDefColModel.mfWidth = nWidth + getUnitConverter().scaleValue( 5, Unit::ScreenX, Unit::Digit );
+ }
+}
+
+void WorksheetGlobals::setDefaultColumnWidth( double fWidth )
+{
+ // overrides a width set with setBaseColumnWidth()
+ if( fWidth > 0.0 )
+ {
+ maDefColModel.mfWidth = fWidth;
+ mbHasDefWidth = true;
+ }
+}
+
+void WorksheetGlobals::setColumnModel( const ColumnModel& rModel )
+{
+ // convert 1-based OOXML column indexes to 0-based API column indexes
+ sal_Int32 nFirstCol = rModel.maRange.mnFirst - 1;
+ sal_Int32 nLastCol = rModel.maRange.mnLast - 1;
+ if( !(getAddressConverter().checkCol( nFirstCol, true ) && (nFirstCol <= nLastCol)) )
+ return;
+
+ // Validate last column index.
+ // If last column is equal to last possible column, Excel adds one
+ // more. We do that also in XclExpColinfo::SaveXml() and for 1024 end
+ // up with 1025 instead, which would lead to excess columns in
+ // checkCol(). Cater for this oddity.
+ if (nLastCol == mrMaxApiPos.Col() + 1)
+ --nLastCol;
+ // This is totally fouled up. If we saved 1025 and the file is saved
+ // with Excel again, it increments the value to 1026.
+ else if (nLastCol == mrMaxApiPos.Col() + 2)
+ nLastCol -= 2;
+ // Excel may add a column range for the remaining columns (with
+ // <cols><col .../></cols>), even if not used or only used to grey out
+ // columns in page break view. Don't let that trigger overflow warning,
+ // so check for the last possible column. If there really is content in
+ // the range that should be caught anyway.
+ else if (nLastCol == getAddressConverter().getMaxXlsAddress().Col())
+ nLastCol = mrMaxApiPos.Col();
+ // User may have applied custom column widths to arbitrary excess
+ // columns. Ignore those and don't track as overflow columns (false).
+ // Effectively this does the same as the above cases, just keep them
+ // for explanation.
+ // Actual data present should trigger the overflow detection later.
+ else if( !getAddressConverter().checkCol( nLastCol, false ) )
+ nLastCol = mrMaxApiPos.Col();
+ // try to find entry in column model map that is able to merge with the passed model
+ bool bInsertModel = true;
+ if( !maColModels.empty() )
+ {
+ // find first column model range following nFirstCol (nFirstCol < aIt->first), or end of map
+ ColumnModelRangeMap::iterator aIt = maColModels.upper_bound( nFirstCol );
+ OSL_ENSURE( aIt == maColModels.end(), "WorksheetGlobals::setColModel - columns are unsorted" );
+ // if inserting before another column model, get last free column
+ OSL_ENSURE( (aIt == maColModels.end()) || (nLastCol < aIt->first), "WorksheetGlobals::setColModel - multiple models of the same column" );
+ if( aIt != maColModels.end() )
+ nLastCol = ::std::min( nLastCol, aIt->first - 1 );
+ if( aIt != maColModels.begin() )
+ {
+ // go to previous map element (which may be able to merge with the passed model)
+ --aIt;
+ // the usage of upper_bound() above ensures that aIt->first is less than or equal to nFirstCol now
+ sal_Int32& rnLastMapCol = aIt->second.second;
+ OSL_ENSURE( rnLastMapCol < nFirstCol, "WorksheetGlobals::setColModel - multiple models of the same column" );
+ nFirstCol = ::std::max( rnLastMapCol + 1, nFirstCol );
+ if( (rnLastMapCol + 1 == nFirstCol) && (nFirstCol <= nLastCol) && aIt->second.first.isMergeable( rModel ) )
+ {
+ // can merge with existing model, update last column index
+ rnLastMapCol = nLastCol;
+ bInsertModel = false;
+ }
+ }
+ }
+ if( nFirstCol <= nLastCol )
+ {
+ // insert the column model, if it has not been merged with another
+ if( bInsertModel )
+ maColModels[ nFirstCol ] = ColumnModelRange( rModel, nLastCol );
+ // set column formatting directly
+ convertColumnFormat( nFirstCol, nLastCol, rModel.mnXfId );
+ }
+}
+
+void WorksheetGlobals::convertColumnFormat( sal_Int32 nFirstCol, sal_Int32 nLastCol, sal_Int32 nXfId )
+{
+ ScRange aRange( nFirstCol, 0, getSheetIndex(), nLastCol, mrMaxApiPos.Row(), getSheetIndex() );
+ if( getAddressConverter().validateCellRange( aRange, true, false ) )
+ {
+ const StylesBuffer& rStyles = getStyles();
+
+ // Set cell styles via direct API - the preferred approach.
+ ScDocumentImport& rDoc = getDocImport();
+ rStyles.writeCellXfToDoc(rDoc, aRange, nXfId);
+ }
+}
+
+void WorksheetGlobals::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom )
+{
+ maDefRowModel.mfHeight = fHeight;
+ maDefRowModel.mbCustomHeight = bCustomHeight;
+ maDefRowModel.mbHidden = bHidden;
+ maDefRowModel.mbThickTop = bThickTop;
+ maDefRowModel.mbThickBottom = bThickBottom;
+}
+
+void WorksheetGlobals::setRowModel( const RowModel& rModel )
+{
+ // convert 1-based OOXML row index to 0-based API row index
+ sal_Int32 nRow = rModel.mnRow - 1;
+ if( getAddressConverter().checkRow( nRow, true ) )
+ {
+ // try to find entry in row model map that is able to merge with the passed model
+ bool bInsertModel = true;
+ bool bUnusedRow = true;
+ if( !maRowModels.empty() )
+ {
+ // find first row model range following nRow (nRow < aIt->first), or end of map
+ RowModelRangeMap::iterator aIt = maRowModels.upper_bound( nRow );
+ OSL_ENSURE( aIt == maRowModels.end(), "WorksheetGlobals::setRowModel - rows are unsorted" );
+ if( aIt != maRowModels.begin() )
+ {
+ // go to previous map element (which may be able to merge with the passed model)
+ --aIt;
+ // the usage of upper_bound() above ensures that aIt->first is less than or equal to nRow now
+ sal_Int32& rnLastMapRow = aIt->second.second;
+ bUnusedRow = rnLastMapRow < nRow;
+ OSL_ENSURE( bUnusedRow, "WorksheetGlobals::setRowModel - multiple models of the same row" );
+ if( (rnLastMapRow + 1 == nRow) && aIt->second.first.isMergeable( rModel ) )
+ {
+ // can merge with existing model, update last row index
+ ++rnLastMapRow;
+ bInsertModel = false;
+ }
+ }
+ }
+ if( bUnusedRow )
+ {
+ // insert the row model, if it has not been merged with another
+ if( bInsertModel )
+ maRowModels[ nRow ] = RowModelRange( rModel, nRow );
+ // set row formatting
+ maSheetData.setRowFormat( nRow, rModel.mnXfId, rModel.mbCustomFormat );
+ // set column spans
+ maSheetData.setColSpans( nRow, rModel.maColSpans );
+ }
+ }
+
+ UpdateRowProgress( maUsedArea, nRow );
+}
+
+// This is called at a higher frequency inside the (threaded) inner loop.
+void WorksheetGlobals::UpdateRowProgress( const ScRange& rUsedArea, SCROW nRow )
+{
+ if (!mxRowProgress || nRow < rUsedArea.aStart.Row() || rUsedArea.aEnd.Row() < nRow)
+ return;
+
+ double fNewPos = static_cast<double>(nRow - rUsedArea.aStart.Row() + 1.0) / (rUsedArea.aEnd.Row() - rUsedArea.aStart.Row() + 1.0);
+
+ if (mbFastRowProgress)
+ mxRowProgress->setPosition(fNewPos);
+ else
+ {
+ double fCurPos = mxRowProgress->getPosition();
+ if (fCurPos < fNewPos && (fNewPos - fCurPos) > 0.3)
+ // Try not to re-draw progress bar too frequently.
+ mxRowProgress->setPosition(fNewPos);
+ }
+}
+
+void WorksheetGlobals::initializeWorksheetImport()
+{
+ // set default cell style for unused cells
+ ScDocumentImport& rDoc = getDocImport();
+
+ ScStyleSheet* pStyleSheet =
+ static_cast<ScStyleSheet*>(rDoc.getDoc().GetStyleSheetPool()->Find(
+ getStyles().getDefaultStyleName(), SfxStyleFamily::Para));
+
+ if (pStyleSheet)
+ rDoc.setCellStyleToSheet(getSheetIndex(), *pStyleSheet);
+
+ /* Remember the current sheet index in global data, needed by global
+ objects, e.g. the chart converter. */
+ setCurrentSheetIndex( getSheetIndex() );
+}
+
+void WorksheetGlobals::finalizeWorksheetImport()
+{
+ lclUpdateProgressBar( mxRowProgress, 1.0 );
+ maSheetData.finalizeImport();
+ // assumes getTables().finalizeImport ( which creates the DatabaseRanges )
+ // has been called already
+ getTables().applyAutoFilters();
+
+ getCondFormats().finalizeImport();
+ lclUpdateProgressBar( mxFinalProgress, 0.25 );
+ finalizeHyperlinkRanges();
+ finalizeValidationRanges();
+ maAutoFilters.finalizeImport( getSheetIndex() );
+ maQueryTables.finalizeImport();
+ maSheetSett.finalizeImport();
+ maPageSett.finalizeImport();
+ maSheetViewSett.finalizeImport();
+
+ lclUpdateProgressBar( mxFinalProgress, 0.5 );
+ convertColumns();
+
+ // tdf#99913 rows hidden by filter need extra flag
+ ScDocument& rDoc = getScDocument();
+ std::vector<sc::ColRowSpan> aSpans;
+ SCTAB nTab = getSheetIndex();
+ ScDBData* pDBData = rDoc.GetAnonymousDBData(nTab);
+ if (pDBData && pDBData->HasAutoFilter())
+ {
+ ScRange aRange;
+ pDBData->GetArea(aRange);
+ SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
+ aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
+ }
+ ScDBCollection* pDocColl = rDoc.GetDBCollection();
+ if (!pDocColl->empty())
+ {
+ ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ if (rxDB->GetTab() == nTab && rxDB->HasAutoFilter())
+ {
+ ScRange aRange;
+ rxDB->GetArea(aRange);
+ SCCOLROW nStartRow = static_cast<SCCOLROW>(aRange.aStart.Row());
+ SCCOLROW nEndRow = static_cast<SCCOLROW>(aRange.aEnd.Row());
+ aSpans.push_back(sc::ColRowSpan(nStartRow, nEndRow));
+ }
+ }
+ }
+ convertRows(aSpans);
+ lclUpdateProgressBar( mxFinalProgress, 1.0 );
+}
+
+void WorksheetGlobals::finalizeDrawingImport()
+{
+ finalizeDrawings();
+
+ // forget current sheet index in global data
+ setCurrentSheetIndex( -1 );
+}
+
+// private --------------------------------------------------------------------
+
+void WorksheetGlobals::finalizeHyperlinkRanges()
+{
+ for (auto const& link : maHyperlinks)
+ {
+ OUString aUrl = getHyperlinkUrl(link);
+ // try to insert URL into each cell of the range
+ if( !aUrl.isEmpty() )
+ for( ScAddress aAddress(link.maRange.aStart.Col(), link.maRange.aStart.Row(), getSheetIndex() ); aAddress.Row() <= link.maRange.aEnd.Row(); aAddress.IncRow() )
+ for( aAddress.SetCol(link.maRange.aStart.Col()); aAddress.Col() <= link.maRange.aEnd.Col(); aAddress.IncCol() )
+ insertHyperlink( aAddress, aUrl );
+ }
+}
+
+OUString WorksheetGlobals::getHyperlinkUrl( const HyperlinkModel& rHyperlink ) const
+{
+ OUStringBuffer aUrlBuffer;
+ if( !rHyperlink.maTarget.isEmpty() )
+ aUrlBuffer.append( getBaseFilter().getAbsoluteUrl( rHyperlink.maTarget ) );
+ if( !rHyperlink.maLocation.isEmpty() )
+ aUrlBuffer.append( '#' ).append( rHyperlink.maLocation );
+ OUString aUrl = aUrlBuffer.makeStringAndClear();
+
+ if( aUrl.startsWith("#") )
+ {
+ sal_Int32 nSepPos = aUrl.lastIndexOf( '!' );
+ if( nSepPos > 0 )
+ {
+ // Do not attempt to blindly convert '#SheetName!A1' to
+ // '#SheetName.A1', it can be #SheetName!R1C1 as well. Hyperlink
+ // handler has to handle all, but prefer '#SheetName.A1' if
+ // possible.
+ if (nSepPos < aUrl.getLength() - 1)
+ {
+ ScRange aRange;
+ const ScDocumentImport& rDoc = getDocImport();
+ if ((aRange.ParseAny( aUrl.copy( nSepPos + 1 ), rDoc.getDoc(),
+ formula::FormulaGrammar::CONV_XL_R1C1)
+ & ScRefFlags::VALID) == ScRefFlags::ZERO)
+ aUrl = aUrl.replaceAt( nSepPos, 1, rtl::OUStringChar( '.' ) );
+ }
+ // #i66592# convert sheet names that have been renamed on import
+ OUString aSheetName = aUrl.copy( 1, nSepPos - 1 );
+ OUString aCalcName = getWorksheets().getCalcSheetName( aSheetName );
+ if( !aCalcName.isEmpty() )
+ aUrl = aUrl.replaceAt( 1, nSepPos - 1, aCalcName );
+ }
+ }
+
+ return aUrl;
+}
+
+void WorksheetGlobals::insertHyperlink( const ScAddress& rAddress, const OUString& rUrl )
+{
+ ScDocumentImport& rDoc = getDocImport();
+ ScRefCellValue aCell(rDoc.getDoc(), rAddress);
+
+ if (aCell.meType == CELLTYPE_STRING || aCell.meType == CELLTYPE_EDIT)
+ {
+ OUString aStr = aCell.getString(&rDoc.getDoc());
+ ScFieldEditEngine& rEE = rDoc.getDoc().GetEditEngine();
+ rEE.Clear();
+
+ SvxURLField aURLField(rUrl, aStr, SvxURLFormat::Repr);
+ SvxFieldItem aURLItem(aURLField, EE_FEATURE_FIELD);
+ rEE.QuickInsertField(aURLItem, ESelection());
+
+ rDoc.setEditCell(rAddress, rEE.CreateTextObject());
+ }
+ else
+ {
+ // Handle other cell types e.g. formulas ( and ? ) that have associated
+ // hyperlinks.
+ // Ideally all hyperlinks should be treated as below. For the moment,
+ // given the current absence of ods support lets just handle what we
+ // previously didn't handle the new way.
+ // Unfortunately we won't be able to preserve such hyperlinks when
+ // saving to ods. Note: when we are able to save such hyperlinks to ods
+ // we should handle *all* imported hyperlinks as below ( e.g. as cell
+ // attribute ) for better interoperability.
+
+ SfxStringItem aItem(ATTR_HYPERLINK, rUrl);
+ rDoc.getDoc().ApplyAttr(rAddress.Col(), rAddress.Row(), rAddress.Tab(), aItem);
+ }
+}
+
+void WorksheetGlobals::finalizeValidationRanges() const
+{
+ for (auto const& validation : maValidations)
+ {
+ PropertySet aPropSet( getCellRangeList(validation.maRanges) );
+
+ Reference< XPropertySet > xValidation( aPropSet.getAnyProperty( PROP_Validation ), UNO_QUERY );
+ if( xValidation.is() )
+ {
+ PropertySet aValProps( xValidation );
+
+ try
+ {
+ const OUString aToken = validation.msRef.getToken( 0, ' ' );
+
+ Reference<XSpreadsheet> xSheet = getSheetFromDoc( getCurrentSheetIndex() );
+ Reference<XCellRange> xDBCellRange;
+ Reference<XCell> xCell;
+ xDBCellRange = xSheet->getCellRangeByName( aToken );
+
+ xCell = xDBCellRange->getCellByPosition( 0, 0 );
+ Reference<XCellAddressable> xCellAddressable( xCell, UNO_QUERY_THROW );
+ CellAddress aFirstCell = xCellAddressable->getCellAddress();
+ Reference<XSheetCondition> xCondition( xValidation, UNO_QUERY_THROW );
+ xCondition->setSourcePosition( aFirstCell );
+ }
+ catch(const Exception&)
+ {
+ }
+
+ // convert validation type to API enum
+ ValidationType eType = ValidationType_ANY;
+ switch( validation.mnType )
+ {
+ case XML_custom: eType = ValidationType_CUSTOM; break;
+ case XML_date: eType = ValidationType_DATE; break;
+ case XML_decimal: eType = ValidationType_DECIMAL; break;
+ case XML_list: eType = ValidationType_LIST; break;
+ case XML_none: eType = ValidationType_ANY; break;
+ case XML_textLength: eType = ValidationType_TEXT_LEN; break;
+ case XML_time: eType = ValidationType_TIME; break;
+ case XML_whole: eType = ValidationType_WHOLE; break;
+ default: OSL_FAIL( "WorksheetData::finalizeValidationRanges - unknown validation type" );
+ }
+ aValProps.setProperty( PROP_Type, eType );
+
+ // convert error alert style to API enum
+ ValidationAlertStyle eAlertStyle = ValidationAlertStyle_STOP;
+ switch( validation.mnErrorStyle )
+ {
+ case XML_information: eAlertStyle = ValidationAlertStyle_INFO; break;
+ case XML_stop: eAlertStyle = ValidationAlertStyle_STOP; break;
+ case XML_warning: eAlertStyle = ValidationAlertStyle_WARNING; break;
+ default: OSL_FAIL( "WorksheetData::finalizeValidationRanges - unknown error style" );
+ }
+ aValProps.setProperty( PROP_ErrorAlertStyle, eAlertStyle );
+
+ // convert dropdown style to API visibility constants
+ sal_Int16 nVisibility = validation.mbNoDropDown ? TableValidationVisibility::INVISIBLE : TableValidationVisibility::UNSORTED;
+ aValProps.setProperty( PROP_ShowList, nVisibility );
+
+ // messages
+ aValProps.setProperty( PROP_ShowInputMessage, validation.mbShowInputMsg );
+ aValProps.setProperty( PROP_InputTitle, validation.maInputTitle );
+ aValProps.setProperty( PROP_InputMessage, validation.maInputMessage );
+ aValProps.setProperty( PROP_ShowErrorMessage, validation.mbShowErrorMsg );
+ aValProps.setProperty( PROP_ErrorTitle, validation.maErrorTitle );
+ aValProps.setProperty( PROP_ErrorMessage, validation.maErrorMessage );
+
+ // allow blank cells
+ aValProps.setProperty( PROP_IgnoreBlankCells, validation.mbAllowBlank );
+
+ try
+ {
+ // condition operator
+ Reference< XSheetCondition2 > xSheetCond( xValidation, UNO_QUERY_THROW );
+ if( eType == ValidationType_CUSTOM )
+ xSheetCond->setConditionOperator( ConditionOperator2::FORMULA );
+ else
+ xSheetCond->setConditionOperator( CondFormatBuffer::convertToApiOperator( validation.mnOperator ) );
+
+ // condition formulas
+ Reference< XMultiFormulaTokens > xTokens( xValidation, UNO_QUERY_THROW );
+ xTokens->setTokens( 0, validation.maTokens1 );
+ xTokens->setTokens( 1, validation.maTokens2 );
+ }
+ catch( Exception& )
+ {
+ }
+
+ // write back validation settings to cell range(s)
+ aPropSet.setProperty( PROP_Validation, xValidation );
+ }
+ }
+}
+
+void WorksheetGlobals::convertColumns()
+{
+ sal_Int32 nNextCol = 0;
+ sal_Int32 nMaxCol = mrMaxApiPos.Col();
+ // stores first grouped column index for each level
+ OutlineLevelVec aColLevels;
+
+ for (auto const& colModel : maColModels)
+ {
+ // column indexes are stored 0-based in maColModels
+ ValueRange aColRange( ::std::max( colModel.first, nNextCol ), ::std::min( colModel.second.second, nMaxCol ) );
+ // process gap between two column models, use default column model
+ if( nNextCol < aColRange.mnFirst )
+ convertColumns( aColLevels, ValueRange( nNextCol, aColRange.mnFirst - 1 ), maDefColModel );
+ // process the column model
+ convertColumns( aColLevels, aColRange, colModel.second.first );
+ // cache next column to be processed
+ nNextCol = aColRange.mnLast + 1;
+ }
+
+ // remaining default columns to end of sheet
+ convertColumns( aColLevels, ValueRange( nNextCol, nMaxCol ), maDefColModel );
+ // close remaining column outlines spanning to end of sheet
+ convertOutlines( aColLevels, nMaxCol + 1, 0, false, false );
+}
+
+void WorksheetGlobals::convertColumns( OutlineLevelVec& orColLevels,
+ const ValueRange& rColRange, const ColumnModel& rModel )
+{
+ // column width: convert 'number of characters' to column width in twips
+ sal_Int32 nWidth = std::round(getUnitConverter().scaleValue( rModel.mfWidth, Unit::Digit, Unit::Twip ));
+
+ SCTAB nTab = getSheetIndex();
+ ScDocument& rDoc = getScDocument();
+ SCCOL nStartCol = rColRange.mnFirst;
+ SCCOL nEndCol = rColRange.mnLast;
+
+ if( nWidth > 0 )
+ {
+ // macro sheets have double width
+ if( meSheetType == WorksheetType::Macro )
+ nWidth *= 2;
+
+ for( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
+ {
+ rDoc.SetColWidthOnly(nCol, nTab, nWidth);
+ }
+ }
+
+ // hidden columns: TODO: #108683# hide columns later?
+ if( rModel.mbHidden )
+ {
+ rDoc.SetColHidden( nStartCol, nEndCol, nTab, true );
+ }
+
+ // outline settings for this column range
+ convertOutlines( orColLevels, rColRange.mnFirst, rModel.mnLevel, rModel.mbCollapsed, false );
+}
+
+void WorksheetGlobals::convertRows(const std::vector<sc::ColRowSpan>& rSpans)
+{
+ sal_Int32 nNextRow = 0;
+ sal_Int32 nMaxRow = mrMaxApiPos.Row();
+ // stores first grouped row index for each level
+ OutlineLevelVec aRowLevels;
+
+ for (auto const& rowModel : maRowModels)
+ {
+ // row indexes are stored 0-based in maRowModels
+ ValueRange aRowRange( ::std::max( rowModel.first, nNextRow ), ::std::min( rowModel.second.second, nMaxRow ) );
+ // process gap between two row models, use default row model
+ if( nNextRow < aRowRange.mnFirst )
+ convertRows(aRowLevels, ValueRange(nNextRow, aRowRange.mnFirst - 1), maDefRowModel,
+ rSpans);
+ // process the row model
+ convertRows(aRowLevels, aRowRange, rowModel.second.first, rSpans, maDefRowModel.mfHeight);
+ // cache next row to be processed
+ nNextRow = aRowRange.mnLast + 1;
+ }
+
+ // remaining default rows to end of sheet
+ convertRows(aRowLevels, ValueRange(nNextRow, nMaxRow), maDefRowModel, rSpans);
+ // close remaining row outlines spanning to end of sheet
+ convertOutlines( aRowLevels, nMaxRow + 1, 0, false, true );
+}
+
+void WorksheetGlobals::convertRows(OutlineLevelVec& orRowLevels, const ValueRange& rRowRange,
+ const RowModel& rModel,
+ const std::vector<sc::ColRowSpan>& rSpans, double fDefHeight)
+{
+ // row height: convert points to row height in twips
+ double fHeight = (rModel.mfHeight >= 0.0) ? rModel.mfHeight : fDefHeight;
+ sal_Int32 nHeight = std::round(o3tl::toTwips( fHeight, o3tl::Length::pt ));
+ SCROW nStartRow = rRowRange.mnFirst;
+ SCROW nEndRow = rRowRange.mnLast;
+ SCTAB nTab = getSheetIndex();
+ if( nHeight > 0 )
+ {
+ /* always import the row height, ensures better layout */
+ ScDocument& rDoc = getScDocument();
+ rDoc.SetRowHeightOnly(nStartRow, nEndRow, nTab, nHeight);
+ if(rModel.mbCustomHeight)
+ rDoc.SetManualHeight( nStartRow, nEndRow, nTab, true );
+ }
+
+ // hidden rows: TODO: #108683# hide rows later?
+ if( rModel.mbHidden )
+ {
+ ScDocument& rDoc = getScDocument();
+ rDoc.SetRowHidden( nStartRow, nEndRow, nTab, true );
+ for (const auto& rSpan : rSpans)
+ {
+ // tdf#99913 rows hidden by filter need extra flag
+ if (rSpan.mnStart <= nStartRow && nStartRow <= rSpan.mnEnd)
+ {
+ SCROW nLast = ::std::min(nEndRow, rSpan.mnEnd);
+ rDoc.SetRowFiltered(nStartRow, nLast, nTab, true);
+ break;
+ }
+ }
+ }
+
+ // outline settings for this row range
+ convertOutlines( orRowLevels, rRowRange.mnFirst, rModel.mnLevel, rModel.mbCollapsed, true );
+}
+
+void WorksheetGlobals::convertOutlines( OutlineLevelVec& orLevels,
+ sal_Int32 nColRow, sal_Int32 nLevel, bool bCollapsed, bool bRows )
+{
+ /* It is ensured from caller functions, that this function is called
+ without any gaps between the processed column or row ranges. */
+
+ OSL_ENSURE( nLevel >= 0, "WorksheetGlobals::convertOutlines - negative outline level" );
+ nLevel = ::std::max< sal_Int32 >( nLevel, 0 );
+
+ sal_Int32 nSize = orLevels.size();
+ if( nSize < nLevel )
+ {
+ // Outline level increased. Push the begin column position.
+ orLevels.insert(orLevels.end(), nLevel - nSize, nColRow);
+ }
+ else if( nLevel < nSize )
+ {
+ // Outline level decreased. Pop them all out.
+ for( sal_Int32 nIndex = nLevel; nIndex < nSize; ++nIndex )
+ {
+ sal_Int32 nFirstInLevel = orLevels.back();
+ orLevels.pop_back();
+ groupColumnsOrRows( nFirstInLevel, nColRow - 1, bCollapsed, bRows );
+ bCollapsed = false; // collapse only once
+ }
+ }
+}
+
+void WorksheetGlobals::groupColumnsOrRows( sal_Int32 nFirstColRow, sal_Int32 nLastColRow, bool bCollapse, bool bRows )
+{
+ try
+ {
+ Reference< XSheetOutline > xOutline( mxSheet, UNO_QUERY_THROW );
+ if( bRows )
+ {
+ CellRangeAddress aRange( getSheetIndex(), 0, nFirstColRow, 0, nLastColRow );
+ xOutline->group( aRange, TableOrientation_ROWS );
+ if( bCollapse )
+ xOutline->hideDetail( aRange );
+ }
+ else
+ {
+ CellRangeAddress aRange( getSheetIndex(), nFirstColRow, 0, nLastColRow, 0 );
+ xOutline->group( aRange, TableOrientation_COLUMNS );
+ if( bCollapse )
+ xOutline->hideDetail( aRange );
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void WorksheetGlobals::finalizeDrawings()
+{
+ // calculate the current drawing page size (after rows/columns are imported)
+ const Size aPageSize( getScDocument().GetMMRect( 0, 0, mrMaxApiPos.Col(), mrMaxApiPos.Row(), getSheetIndex() ).GetSize() );
+ maDrawPageSize.Width = lclClampToNonNegativeInt32( aPageSize.Width() );
+ maDrawPageSize.Height = lclClampToNonNegativeInt32( aPageSize.Height() );
+
+ // import DML and VML
+ if( !maDrawingPath.isEmpty() )
+ importOoxFragment( new DrawingFragment( *this, maDrawingPath ) );
+ if( !maVmlDrawingPath.isEmpty() )
+ importOoxFragment( new VmlDrawingFragment( *this, maVmlDrawingPath ) );
+
+ // comments (after callout shapes have been imported from VML/DFF)
+ maComments.finalizeImport();
+
+ /* Extend used area of the sheet by cells covered with drawing objects.
+ Needed if the imported document is inserted as "OLE object from file"
+ and thus does not provide an OLE size property by itself. */
+ if( (maShapeBoundingBox.Width > 0) || (maShapeBoundingBox.Height > 0) )
+ extendUsedArea( getCellRangeFromRectangle( maShapeBoundingBox ) );
+
+ // if no used area is set, default to A1
+ if( maUsedArea.aStart.Col() > maUsedArea.aEnd.Col() )
+ {
+ maUsedArea.aStart.SetCol( 0 );
+ maUsedArea.aEnd.SetCol( 0 );
+ }
+
+ if( maUsedArea.aStart.Row() > maUsedArea.aEnd.Row() )
+ {
+ maUsedArea.aStart.SetRow( 0 );
+ maUsedArea.aEnd.SetRow( 0 );
+ }
+
+ /* Register the used area of this sheet in global view settings. The
+ global view settings will set the visible area if this document is an
+ embedded OLE object. */
+ getViewSettings().setSheetUsedArea( maUsedArea );
+
+ /* #i103686# Set right-to-left sheet layout. Must be done after all
+ drawing shapes to simplify calculation of shape coordinates. */
+ if( maSheetViewSett.isSheetRightToLeft() )
+ {
+ PropertySet aPropSet( mxSheet );
+ aPropSet.setProperty( PROP_TableLayout, WritingMode2::RL_TB );
+ }
+}
+
+WorksheetHelper::WorksheetHelper( WorksheetGlobals& rSheetGlob ) :
+ WorkbookHelper( rSheetGlob ),
+ mrSheetGlob( rSheetGlob )
+{
+}
+
+ScDocument& WorksheetHelper::getScDocument()
+{
+ return getDocImport().getDoc();
+}
+
+/*static*/ WorksheetGlobalsRef WorksheetHelper::constructGlobals( const WorkbookHelper& rHelper,
+ const ISegmentProgressBarRef& rxProgressBar, WorksheetType eSheetType, SCTAB nSheet )
+{
+ WorksheetGlobalsRef xSheetGlob = std::make_shared<WorksheetGlobals>( rHelper, rxProgressBar, eSheetType, nSheet );
+ if( !xSheetGlob->isValidSheet() )
+ xSheetGlob.reset();
+ return xSheetGlob;
+}
+
+/* static */ IWorksheetProgress *WorksheetHelper::getWorksheetInterface( const WorksheetGlobalsRef &xRef )
+{
+ return static_cast< IWorksheetProgress *>( xRef.get() );
+}
+
+WorksheetType WorksheetHelper::getSheetType() const
+{
+ return mrSheetGlob.getSheetType();
+}
+
+SCTAB WorksheetHelper::getSheetIndex() const
+{
+ return mrSheetGlob.getSheetIndex();
+}
+
+const Reference< XSpreadsheet >& WorksheetHelper::getSheet() const
+{
+ return mrSheetGlob.getSheet();
+}
+
+Reference< XCell > WorksheetHelper::getCell( const ScAddress& rAddress ) const
+{
+ return mrSheetGlob.getCell( rAddress );
+}
+
+Reference< XCellRange > WorksheetHelper::getCellRange( const ScRange& rRange ) const
+{
+ return mrSheetGlob.getCellRange( rRange );
+}
+
+Reference< XDrawPage > WorksheetHelper::getDrawPage() const
+{
+ return mrSheetGlob.getDrawPage();
+}
+
+awt::Point WorksheetHelper::getCellPosition( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return mrSheetGlob.getCellPosition( nCol, nRow );
+}
+
+const awt::Size& WorksheetHelper::getDrawPageSize() const
+{
+ return mrSheetGlob.getDrawPageSize();
+}
+
+SheetDataBuffer& WorksheetHelper::getSheetData() const
+{
+ return mrSheetGlob.getSheetData();
+}
+
+CondFormatBuffer& WorksheetHelper::getCondFormats() const
+{
+ return mrSheetGlob.getCondFormats();
+}
+
+CommentsBuffer& WorksheetHelper::getComments() const
+{
+ return mrSheetGlob.getComments();
+}
+
+AutoFilterBuffer& WorksheetHelper::getAutoFilters() const
+{
+ return mrSheetGlob.getAutoFilters();
+}
+
+QueryTableBuffer& WorksheetHelper::getQueryTables() const
+{
+ return mrSheetGlob.getQueryTables();
+}
+
+WorksheetSettings& WorksheetHelper::getWorksheetSettings() const
+{
+ return mrSheetGlob.getWorksheetSettings();
+}
+
+PageSettings& WorksheetHelper::getPageSettings() const
+{
+ return mrSheetGlob.getPageSettings();
+}
+
+SheetViewSettings& WorksheetHelper::getSheetViewSettings() const
+{
+ return mrSheetGlob.getSheetViewSettings();
+}
+
+VmlDrawing& WorksheetHelper::getVmlDrawing() const
+{
+ return mrSheetGlob.getVmlDrawing();
+}
+
+ExtLst& WorksheetHelper::getExtLst() const
+{
+ return mrSheetGlob.getExtLst();
+}
+
+void WorksheetHelper::setPageBreak( const PageBreakModel& rModel, bool bRowBreak )
+{
+ mrSheetGlob.setPageBreak( rModel, bRowBreak );
+}
+
+void WorksheetHelper::setHyperlink( const HyperlinkModel& rModel )
+{
+ mrSheetGlob.setHyperlink( rModel );
+}
+
+void WorksheetHelper::setValidation( const ValidationModel& rModel )
+{
+ mrSheetGlob.setValidation( rModel );
+}
+
+void WorksheetHelper::setDrawingPath( const OUString& rDrawingPath )
+{
+ mrSheetGlob.setDrawingPath( rDrawingPath );
+}
+
+void WorksheetHelper::setVmlDrawingPath( const OUString& rVmlDrawingPath )
+{
+ mrSheetGlob.setVmlDrawingPath( rVmlDrawingPath );
+}
+
+void WorksheetHelper::extendUsedArea( const ScAddress& rAddress )
+{
+ mrSheetGlob.extendUsedArea( rAddress );
+}
+
+void WorksheetHelper::extendShapeBoundingBox( const awt::Rectangle& rShapeRect )
+{
+ mrSheetGlob.extendShapeBoundingBox( rShapeRect );
+}
+
+void WorksheetHelper::setBaseColumnWidth( sal_Int32 nWidth )
+{
+ mrSheetGlob.setBaseColumnWidth( nWidth );
+}
+
+void WorksheetHelper::setDefaultColumnWidth( double fWidth )
+{
+ mrSheetGlob.setDefaultColumnWidth( fWidth );
+}
+
+void WorksheetHelper::setColumnModel( const ColumnModel& rModel )
+{
+ mrSheetGlob.setColumnModel( rModel );
+}
+
+void WorksheetHelper::setDefaultRowSettings( double fHeight, bool bCustomHeight, bool bHidden, bool bThickTop, bool bThickBottom )
+{
+ mrSheetGlob.setDefaultRowSettings( fHeight, bCustomHeight, bHidden, bThickTop, bThickBottom );
+}
+
+void WorksheetHelper::setRowModel( const RowModel& rModel )
+{
+ mrSheetGlob.setRowModel( rModel );
+}
+
+void WorksheetHelper::setCellFormulaValue(
+ const ScAddress& rAddress, const OUString& rValueStr, sal_Int32 nCellType )
+{
+ getFormulaBuffer().setCellFormulaValue(rAddress, rValueStr, nCellType);
+}
+
+void WorksheetHelper::putRichString( const ScAddress& rAddress, const RichString& rString, const oox::xls::Font* pFirstPortionFont )
+{
+ ScEditEngineDefaulter& rEE = getEditEngine();
+
+ // The cell will own the text object instance returned from convert().
+ getDocImport().setEditCell(rAddress, rString.convert(rEE, pFirstPortionFont));
+}
+
+void WorksheetHelper::putFormulaTokens( const ScAddress& rAddress, const ApiTokenSequence& rTokens )
+{
+ ScDocumentImport& rDoc = getDocImport();
+ std::unique_ptr<ScTokenArray> pTokenArray(new ScTokenArray(rDoc.getDoc()));
+ ScTokenConversion::ConvertToTokenArray(rDoc.getDoc(), *pTokenArray, rTokens);
+ rDoc.setFormulaCell(rAddress, std::move(pTokenArray));
+}
+
+void WorksheetHelper::initializeWorksheetImport()
+{
+ mrSheetGlob.initializeWorksheetImport();
+}
+
+void WorksheetHelper::finalizeWorksheetImport()
+{
+ mrSheetGlob.finalizeWorksheetImport();
+}
+
+void WorksheetHelper::finalizeDrawingImport()
+{
+ mrSheetGlob.finalizeDrawingImport();
+}
+
+void WorksheetHelper::setCellFormula( const ScAddress& rTokenAddress, const OUString& rTokenStr )
+{
+ getFormulaBuffer().setCellFormula( rTokenAddress, rTokenStr );
+}
+
+void WorksheetHelper::setCellFormula(
+ const ScAddress& rAddr, sal_Int32 nSharedId,
+ const OUString& rCellValue, sal_Int32 nValueType )
+{
+ getFormulaBuffer().setCellFormula(rAddr, nSharedId, rCellValue, nValueType);
+}
+
+void WorksheetHelper::setCellArrayFormula( const ScRange& rRangeAddress, const ScAddress& rTokenAddress, const OUString& rTokenStr )
+{
+ getFormulaBuffer().setCellArrayFormula( rRangeAddress, rTokenAddress, rTokenStr );
+}
+
+void WorksheetHelper::createSharedFormulaMapEntry(
+ const ScAddress& rAddress, sal_Int32 nSharedId, const OUString& rTokens )
+{
+ getFormulaBuffer().createSharedFormulaMapEntry(rAddress, nSharedId, rTokens);
+}
+
+} // namespace oox
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/oox/worksheetsettings.cxx b/sc/source/filter/oox/worksheetsettings.cxx
new file mode 100644
index 000000000..988207aa9
--- /dev/null
+++ b/sc/source/filter/oox/worksheetsettings.cxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <worksheetsettings.hxx>
+
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <oox/core/binarycodec.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/helper/binaryinputstream.hxx>
+#include <oox/helper/attributelist.hxx>
+#include <oox/token/properties.hxx>
+#include <oox/token/tokens.hxx>
+#include <pagesettings.hxx>
+#include <tabprotection.hxx>
+#include <document.hxx>
+#include <addressconverter.hxx>
+#include <biffhelper.hxx>
+
+namespace oox::xls {
+
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+const sal_uInt8 BIFF12_SHEETPR_FILTERMODE = 0x01;
+
+const sal_uInt16 BIFF_SHEETPR_APPLYSTYLES = 0x0020;
+const sal_uInt16 BIFF_SHEETPR_SYMBOLSBELOW = 0x0040;
+const sal_uInt16 BIFF_SHEETPR_SYMBOLSRIGHT = 0x0080;
+const sal_uInt16 BIFF_SHEETPR_FITTOPAGES = 0x0100;
+
+} // namespace
+
+SheetSettingsModel::SheetSettingsModel() :
+ mbFilterMode( false ),
+ mbApplyStyles( false ),
+ mbSummaryBelow( true ),
+ mbSummaryRight( true )
+{
+}
+
+SheetProtectionModel::SheetProtectionModel() :
+ mnSpinCount( 0 ),
+ mnPasswordHash( 0 ),
+ mbSheet( false ),
+ mbObjects( false ),
+ mbScenarios( false ),
+ mbFormatCells( true ),
+ mbFormatColumns( true ),
+ mbFormatRows( true ),
+ mbInsertColumns( true ),
+ mbInsertRows( true ),
+ mbInsertHyperlinks( true ),
+ mbDeleteColumns( true ),
+ mbDeleteRows( true ),
+ mbSelectLocked( false ),
+ mbSort( true ),
+ mbAutoFilter( true ),
+ mbPivotTables( true ),
+ mbSelectUnlocked( false )
+{
+}
+
+WorksheetSettings::WorksheetSettings( const WorksheetHelper& rHelper ) :
+ WorksheetHelper( rHelper ),
+ maPhoneticSett( rHelper )
+{
+}
+
+void WorksheetSettings::importSheetPr( const AttributeList& rAttribs )
+{
+ maSheetSettings.maCodeName = rAttribs.getString( XML_codeName, OUString() );
+ maSheetSettings.mbFilterMode = rAttribs.getBool( XML_filterMode, false );
+}
+
+void WorksheetSettings::importChartSheetPr( const AttributeList& rAttribs )
+{
+ maSheetSettings.maCodeName = rAttribs.getString( XML_codeName, OUString() );
+}
+
+void WorksheetSettings::importTabColor( const AttributeList& rAttribs )
+{
+ maSheetSettings.maTabColor.importColor( rAttribs );
+}
+
+void WorksheetSettings::importOutlinePr( const AttributeList& rAttribs )
+{
+ maSheetSettings.mbApplyStyles = rAttribs.getBool( XML_applyStyles, false );
+ maSheetSettings.mbSummaryBelow = rAttribs.getBool( XML_summaryBelow, true );
+ maSheetSettings.mbSummaryRight = rAttribs.getBool( XML_summaryRight, true );
+}
+
+void WorksheetSettings::importSheetProtection( const AttributeList& rAttribs )
+{
+ maSheetProt.maAlgorithmName = rAttribs.getString( XML_algorithmName, OUString());
+ maSheetProt.maHashValue = rAttribs.getString( XML_hashValue, OUString());
+ maSheetProt.maSaltValue = rAttribs.getString( XML_saltValue, OUString());
+ maSheetProt.mnSpinCount = rAttribs.getUnsigned( XML_spinCount, 0);
+ maSheetProt.mnPasswordHash = oox::core::CodecHelper::getPasswordHash( rAttribs, XML_password );
+ maSheetProt.mbSheet = rAttribs.getBool( XML_sheet, false );
+ maSheetProt.mbObjects = rAttribs.getBool( XML_objects, false );
+ maSheetProt.mbScenarios = rAttribs.getBool( XML_scenarios, false );
+ maSheetProt.mbFormatCells = rAttribs.getBool( XML_formatCells, true );
+ maSheetProt.mbFormatColumns = rAttribs.getBool( XML_formatColumns, true );
+ maSheetProt.mbFormatRows = rAttribs.getBool( XML_formatRows, true );
+ maSheetProt.mbInsertColumns = rAttribs.getBool( XML_insertColumns, true );
+ maSheetProt.mbInsertRows = rAttribs.getBool( XML_insertRows, true );
+ maSheetProt.mbInsertHyperlinks = rAttribs.getBool( XML_insertHyperlinks, true );
+ maSheetProt.mbDeleteColumns = rAttribs.getBool( XML_deleteColumns, true );
+ maSheetProt.mbDeleteRows = rAttribs.getBool( XML_deleteRows, true );
+ maSheetProt.mbSelectLocked = rAttribs.getBool( XML_selectLockedCells, false );
+ maSheetProt.mbSort = rAttribs.getBool( XML_sort, true );
+ maSheetProt.mbAutoFilter = rAttribs.getBool( XML_autoFilter, true );
+ maSheetProt.mbPivotTables = rAttribs.getBool( XML_pivotTables, true );
+ maSheetProt.mbSelectUnlocked = rAttribs.getBool( XML_selectUnlockedCells, false );
+}
+
+void WorksheetSettings::importProtectedRange( const AttributeList& rAttribs )
+{
+ ScEnhancedProtection aProt;
+ aProt.maTitle = rAttribs.getString( XML_name, OUString());
+ /* XXX ECMA-376/OOXML XMLSchema and ISO/IEC 29500 say 'securityDescriptor'
+ * would be an element, but Excel2013 stores it as attribute. */
+ aProt.maSecurityDescriptorXML = rAttribs.getString( XML_securityDescriptor, OUString());
+ /* XXX ECMA-376/OOXML or ISO/IEC 29500 do not even mention a 'password'
+ * attribute here (or anywhere else), but this is what Excel2013 writes,
+ * similar to BIFF, if the original file was a BIFF file. OOXML XMLschema
+ * and ISO/IEC 29500 instead define 'algorithmName', 'hashValue',
+ * 'saltValue' and 'spinCount' that are written if the protection was newly
+ * created. */
+ aProt.mnPasswordVerifier = rAttribs.getIntegerHex( XML_password, 0);
+ aProt.maPasswordHash.maAlgorithmName = rAttribs.getString( XML_algorithmName, OUString());
+ aProt.maPasswordHash.maHashValue = rAttribs.getString( XML_hashValue, OUString());
+ aProt.maPasswordHash.maSaltValue = rAttribs.getString( XML_saltValue, OUString());
+ aProt.maPasswordHash.mnSpinCount = rAttribs.getUnsigned( XML_spinCount, 0);
+ OUString aRefs( rAttribs.getString( XML_sqref, OUString()));
+ if (!aRefs.isEmpty())
+ {
+ std::unique_ptr<ScRangeList> xRangeList(new ScRangeList());
+ getAddressConverter().convertToCellRangeList( *xRangeList, aRefs, getSheetIndex(), true );
+ if (!xRangeList->empty())
+ {
+ aProt.maRangeList = xRangeList.release();
+ }
+ }
+ maSheetProt.maEnhancedProtections.push_back( aProt);
+}
+
+void WorksheetSettings::importChartProtection( const AttributeList& rAttribs )
+{
+ maSheetProt.mnPasswordHash = oox::core::CodecHelper::getPasswordHash( rAttribs, XML_password );
+ maSheetProt.mbSheet = rAttribs.getBool( XML_content, false );
+ maSheetProt.mbObjects = rAttribs.getBool( XML_objects, false );
+}
+
+void WorksheetSettings::importPhoneticPr( const AttributeList& rAttribs )
+{
+ maPhoneticSett.importPhoneticPr( rAttribs );
+}
+
+void WorksheetSettings::importSheetPr( SequenceInputStream& rStrm )
+{
+ sal_uInt16 nFlags1;
+ sal_uInt8 nFlags2;
+ nFlags1 = rStrm.readuInt16();
+ nFlags2 = rStrm.readuChar();
+ rStrm >> maSheetSettings.maTabColor;
+ rStrm.skip( 8 ); // sync anchor cell
+ rStrm >> maSheetSettings.maCodeName;
+ // sheet settings
+ maSheetSettings.mbFilterMode = getFlag( nFlags2, BIFF12_SHEETPR_FILTERMODE );
+ // outline settings, equal flags in all BIFFs
+ maSheetSettings.mbApplyStyles = getFlag( nFlags1, BIFF_SHEETPR_APPLYSTYLES );
+ maSheetSettings.mbSummaryRight = getFlag( nFlags1, BIFF_SHEETPR_SYMBOLSRIGHT );
+ maSheetSettings.mbSummaryBelow = getFlag( nFlags1, BIFF_SHEETPR_SYMBOLSBELOW );
+ /* Fit printout to width/height - for whatever reason, this flag is still
+ stored separated from the page settings */
+ getPageSettings().setFitToPagesMode( getFlag( nFlags1, BIFF_SHEETPR_FITTOPAGES ) );
+}
+
+void WorksheetSettings::importChartSheetPr( SequenceInputStream& rStrm )
+{
+ rStrm.skip( 2 ); // flags, contains only the 'published' flag
+ rStrm >> maSheetSettings.maTabColor >> maSheetSettings.maCodeName;
+}
+
+void WorksheetSettings::importSheetProtection( SequenceInputStream& rStrm )
+{
+ maSheetProt.mnPasswordHash = rStrm.readuInt16();
+ // no flags field for all these boolean flags?!?
+ maSheetProt.mbSheet = rStrm.readInt32() != 0;
+ maSheetProt.mbObjects = rStrm.readInt32() == 0;
+ maSheetProt.mbScenarios = rStrm.readInt32() == 0;
+ maSheetProt.mbFormatCells = rStrm.readInt32() == 0;
+ maSheetProt.mbFormatColumns = rStrm.readInt32() == 0;
+ maSheetProt.mbFormatRows = rStrm.readInt32() == 0;
+ maSheetProt.mbInsertColumns = rStrm.readInt32() == 0;
+ maSheetProt.mbInsertRows = rStrm.readInt32() == 0;
+ maSheetProt.mbInsertHyperlinks = rStrm.readInt32() == 0;
+ maSheetProt.mbDeleteColumns = rStrm.readInt32() == 0;
+ maSheetProt.mbDeleteRows = rStrm.readInt32() == 0;
+ maSheetProt.mbSelectLocked = rStrm.readInt32() == 0;
+ maSheetProt.mbSort = rStrm.readInt32() == 0;
+ maSheetProt.mbAutoFilter = rStrm.readInt32() == 0;
+ maSheetProt.mbPivotTables = rStrm.readInt32() == 0;
+ maSheetProt.mbSelectUnlocked = rStrm.readInt32() == 0;
+}
+
+void WorksheetSettings::importChartProtection( SequenceInputStream& rStrm )
+{
+ maSheetProt.mnPasswordHash = rStrm.readuInt16();
+ // no flags field for all these boolean flags?!?
+ maSheetProt.mbSheet = rStrm.readInt32() != 0;
+ maSheetProt.mbObjects = rStrm.readInt32() != 0;
+}
+
+void WorksheetSettings::importPhoneticPr( SequenceInputStream& rStrm )
+{
+ maPhoneticSett.importPhoneticPr( rStrm );
+}
+
+void WorksheetSettings::finalizeImport()
+{
+ // sheet protection
+ if( maSheetProt.mbSheet )
+ {
+ ScTableProtection aProtect;
+ aProtect.setProtected(true);
+ aProtect.setPasswordHash( maSheetProt.maAlgorithmName, maSheetProt.maHashValue,
+ maSheetProt.maSaltValue, maSheetProt.mnSpinCount);
+ // Set the simple hash after the proper hash because setting the proper
+ // hash resets the simple hash, yet if the simple hash is present we
+ // may as well use it and more important want to keep it for saving the
+ // document again.
+ if (maSheetProt.mnPasswordHash)
+ {
+ Sequence<sal_Int8> aPass{
+ sal_Int8(maSheetProt.mnPasswordHash >> 8),
+ sal_Int8(maSheetProt.mnPasswordHash & 0xFF)};
+ aProtect.setPasswordHash(aPass, PASSHASH_XL);
+ }
+ aProtect.setOption( ScTableProtection::OBJECTS, !maSheetProt.mbObjects);
+ aProtect.setOption( ScTableProtection::SCENARIOS, !maSheetProt.mbScenarios );
+ aProtect.setOption( ScTableProtection::FORMAT_CELLS, !maSheetProt.mbFormatCells );
+ aProtect.setOption( ScTableProtection::FORMAT_COLUMNS, !maSheetProt.mbFormatColumns );
+ aProtect.setOption( ScTableProtection::FORMAT_ROWS, !maSheetProt.mbFormatRows );
+ aProtect.setOption( ScTableProtection::INSERT_COLUMNS, !maSheetProt.mbInsertColumns );
+ aProtect.setOption( ScTableProtection::INSERT_ROWS, !maSheetProt.mbInsertRows );
+ aProtect.setOption( ScTableProtection::INSERT_HYPERLINKS, !maSheetProt.mbInsertHyperlinks );
+ aProtect.setOption( ScTableProtection::DELETE_COLUMNS, !maSheetProt.mbDeleteColumns );
+ aProtect.setOption( ScTableProtection::DELETE_ROWS,!maSheetProt.mbDeleteRows );
+ aProtect.setOption( ScTableProtection::SELECT_LOCKED_CELLS, !maSheetProt.mbSelectLocked );
+ aProtect.setOption( ScTableProtection::SORT, !maSheetProt.mbSort );
+ aProtect.setOption( ScTableProtection::AUTOFILTER, !maSheetProt.mbAutoFilter );
+ aProtect.setOption( ScTableProtection::PIVOT_TABLES, !maSheetProt.mbPivotTables );
+ aProtect.setOption( ScTableProtection::SELECT_UNLOCKED_CELLS, !maSheetProt.mbSelectUnlocked );
+
+ aProtect.setEnhancedProtection( std::vector(maSheetProt.maEnhancedProtections) );
+
+ getScDocument().SetTabProtection( getSheetIndex(), &aProtect );
+ }
+
+ // VBA code name
+ PropertySet aPropSet( getSheet() );
+ aPropSet.setProperty( PROP_CodeName, maSheetSettings.maCodeName );
+
+ // sheet tab color
+ if( !maSheetSettings.maTabColor.isAuto() )
+ {
+ ::Color nColor = maSheetSettings.maTabColor.getColor( getBaseFilter().getGraphicHelper() );
+ aPropSet.setProperty( PROP_TabColor, nColor );
+ }
+}
+
+} // namespace oox::xls
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/orcus/filterdetect.cxx b/sc/source/filter/orcus/filterdetect.cxx
new file mode 100644
index 000000000..21eb1d492
--- /dev/null
+++ b/sc/source/filter/orcus/filterdetect.cxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <com/sun/star/document/XExtendedFilterDetection.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <unotools/mediadescriptor.hxx>
+
+#include <tools/stream.hxx>
+
+#include <orcus/format_detection.hpp>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace {
+
+class OrcusFormatDetect : public ::cppu::WeakImplHelper<
+ css::document::XExtendedFilterDetection,
+ css::lang::XServiceInfo >
+{
+public:
+ explicit OrcusFormatDetect();
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ virtual OUString SAL_CALL
+ detect( css::uno::Sequence< css::beans::PropertyValue >& rMediaDescSeq ) override;
+
+private:
+};
+
+OrcusFormatDetect::OrcusFormatDetect()
+{
+}
+
+OUString OrcusFormatDetect::getImplementationName()
+{
+ return OUString();
+}
+
+sal_Bool OrcusFormatDetect::supportsService(const OUString& /*rServiceName*/)
+{
+ return false;
+}
+
+css::uno::Sequence<OUString> OrcusFormatDetect::getSupportedServiceNames()
+{
+ return css::uno::Sequence<OUString>();
+}
+
+OUString OrcusFormatDetect::detect(css::uno::Sequence<css::beans::PropertyValue>& rMediaDescSeq)
+{
+ utl::MediaDescriptor aMediaDescriptor( rMediaDescSeq );
+ bool bAborted = aMediaDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ABORTED, false);
+ if (bAborted)
+ return OUString();
+
+ css::uno::Reference<css::io::XInputStream> xInputStream(aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM], css::uno::UNO_QUERY );
+ SvMemoryStream aContent(xInputStream->available());
+
+ static const sal_Int32 nBytes = 4096;
+ css::uno::Sequence<sal_Int8> aSeq(nBytes);
+ bool bEnd = false;
+ while(!bEnd)
+ {
+ sal_Int32 nReadBytes = xInputStream->readBytes(aSeq, nBytes);
+ bEnd = (nReadBytes != nBytes);
+ aContent.WriteBytes(aSeq.getConstArray(), nReadBytes);
+ }
+
+ orcus::format_t eFormat = orcus::detect(static_cast<const unsigned char*>(aContent.GetData()), aContent.GetSize());
+
+ switch (eFormat)
+ {
+ case orcus::format_t::gnumeric:
+ return "Gnumeric XML";
+ case orcus::format_t::xls_xml:
+ return "calc_MS_Excel_2003_XML";
+ default:
+ ;
+ }
+
+ return OUString();
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_sc_OrcusFormatDetect_get_implementation(css::uno::XComponentContext* ,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new OrcusFormatDetect());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/orcus/interface.cxx b/sc/source/filter/orcus/interface.cxx
new file mode 100644
index 000000000..fc8e5bfef
--- /dev/null
+++ b/sc/source/filter/orcus/interface.cxx
@@ -0,0 +1,2198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <limits>
+#include <memory>
+#include <orcusinterface.hxx>
+
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <rangenam.hxx>
+#include <tokenarray.hxx>
+#include <globalnames.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <compiler.hxx>
+#include <stlpool.hxx>
+#include <scitems.hxx>
+#include <patattr.hxx>
+#include <docpool.hxx>
+#include <attrib.hxx>
+
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/justifyitem.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/intitem.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <i18nlangtag/lang.h>
+#include <tools/fontenum.hxx>
+#include <sal/log.hxx>
+
+#include <stylesbuffer.hxx>
+#include <orcus/exception.hpp>
+#include <stylehelper.hxx>
+
+using namespace com::sun::star;
+
+namespace os = orcus::spreadsheet;
+
+namespace {
+
+formula::FormulaGrammar::Grammar getCalcGrammarFromOrcus( os::formula_grammar_t grammar )
+{
+ formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_ODFF;
+ switch(grammar)
+ {
+ case orcus::spreadsheet::formula_grammar_t::ods:
+ eGrammar = formula::FormulaGrammar::GRAM_ODFF;
+ break;
+ case orcus::spreadsheet::formula_grammar_t::xlsx:
+ eGrammar = formula::FormulaGrammar::GRAM_OOXML;
+ break;
+ case orcus::spreadsheet::formula_grammar_t::gnumeric:
+ eGrammar = formula::FormulaGrammar::GRAM_ENGLISH_XL_A1;
+ break;
+ case orcus::spreadsheet::formula_grammar_t::xls_xml:
+ eGrammar = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
+ break;
+ case orcus::spreadsheet::formula_grammar_t::unknown:
+ break;
+ }
+
+ return eGrammar;
+}
+
+}
+
+ScOrcusGlobalSettings::ScOrcusGlobalSettings(ScDocumentImport& rDoc)
+ : mrDoc(rDoc)
+ , meCalcGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED)
+ , meOrcusGrammar(os::formula_grammar_t::unknown)
+ , mnTextEncoding(RTL_TEXTENCODING_UTF8)
+{
+}
+
+void ScOrcusGlobalSettings::set_origin_date(int year, int month, int day)
+{
+ mrDoc.setOriginDate(year, month, day);
+}
+
+void ScOrcusGlobalSettings::set_character_set(orcus::character_set_t cs)
+{
+ switch (cs)
+ {
+ case orcus::character_set_t::iso_2022_jp:
+ case orcus::character_set_t::iso_2022_jp_2:
+ mnTextEncoding = RTL_TEXTENCODING_ISO_2022_JP;
+ break;
+ case orcus::character_set_t::jis_x0201:
+ mnTextEncoding = RTL_TEXTENCODING_JIS_X_0201;
+ break;
+ case orcus::character_set_t::shift_jis:
+ mnTextEncoding = RTL_TEXTENCODING_SHIFT_JIS;
+ break;
+ case orcus::character_set_t::us_ascii:
+ mnTextEncoding = RTL_TEXTENCODING_ASCII_US;
+ break;
+ case orcus::character_set_t::utf_7:
+ mnTextEncoding = RTL_TEXTENCODING_UTF7;
+ break;
+ case orcus::character_set_t::windows_1250:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1250;
+ break;
+ case orcus::character_set_t::windows_1251:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1251;
+ break;
+ case orcus::character_set_t::windows_1252:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1252;
+ break;
+ case orcus::character_set_t::windows_1253:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1253;
+ break;
+ case orcus::character_set_t::windows_1254:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1254;
+ break;
+ case orcus::character_set_t::windows_1255:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1255;
+ break;
+ case orcus::character_set_t::windows_1256:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1256;
+ break;
+ case orcus::character_set_t::windows_1257:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1257;
+ break;
+ case orcus::character_set_t::windows_1258:
+ mnTextEncoding = RTL_TEXTENCODING_MS_1258;
+ break;
+ default:
+ ; // Add more as needed.
+ }
+}
+
+void ScOrcusGlobalSettings::set_default_formula_grammar(os::formula_grammar_t grammar)
+{
+ meCalcGrammar = getCalcGrammarFromOrcus(grammar);
+ meOrcusGrammar = grammar;
+}
+
+orcus::spreadsheet::formula_grammar_t ScOrcusGlobalSettings::get_default_formula_grammar() const
+{
+ return meOrcusGrammar;
+}
+
+ScOrcusRefResolver::ScOrcusRefResolver( const ScOrcusGlobalSettings& rGS ) :
+ mrGlobalSettings(rGS) {}
+
+os::src_address_t ScOrcusRefResolver::resolve_address(std::string_view address)
+{
+ OUString aStr(address.data(), address.size(), mrGlobalSettings.getTextEncoding());
+
+ ScAddress aAddr;
+ aAddr.Parse(aStr, mrGlobalSettings.getDoc().getDoc(),
+ formula::FormulaGrammar::extractRefConvention(
+ mrGlobalSettings.getCalcGrammar()));
+
+ if (!aAddr.IsValid())
+ {
+ std::ostringstream os;
+ os << "'" << address << "' is not a valid address expression.";
+ throw orcus::invalid_arg_error(os.str());
+ }
+
+ os::src_address_t ret;
+ ret.sheet = aAddr.Tab();
+ ret.column = aAddr.Col();
+ ret.row = aAddr.Row();
+
+ return ret;
+}
+
+os::src_range_t ScOrcusRefResolver::resolve_range(std::string_view range)
+{
+ OUString aStr(range.data(), range.size(), mrGlobalSettings.getTextEncoding());
+
+ ScRange aRange;
+ aRange.Parse(aStr, mrGlobalSettings.getDoc().getDoc(),
+ formula::FormulaGrammar::extractRefConvention(
+ mrGlobalSettings.getCalcGrammar()));
+
+ if (!aRange.IsValid())
+ {
+ std::ostringstream os;
+ os << "'" << range << "' is not a valid range expression.";
+ throw orcus::invalid_arg_error(os.str());
+ }
+
+ os::src_range_t ret;
+ ret.first.sheet = aRange.aStart.Tab();
+ ret.first.column = aRange.aStart.Col();
+ ret.first.row = aRange.aStart.Row();
+ ret.last.sheet = aRange.aEnd.Tab();
+ ret.last.column = aRange.aEnd.Col();
+ ret.last.row = aRange.aEnd.Row();
+
+ return ret;
+}
+
+ScOrcusNamedExpression::ScOrcusNamedExpression(
+ ScDocumentImport& rDoc, const ScOrcusGlobalSettings& rGS, SCTAB nTab ) :
+ mrDoc(rDoc), mrGlobalSettings(rGS), mnTab(nTab) {}
+
+void ScOrcusNamedExpression::reset()
+{
+ maBasePos.SetTab(0);
+ maBasePos.SetCol(0);
+ maBasePos.SetRow(0);
+ maName.clear();
+ maExpr.clear();
+}
+
+void ScOrcusNamedExpression::set_base_position(const orcus::spreadsheet::src_address_t& pos)
+{
+ maBasePos.SetTab(pos.sheet);
+ maBasePos.SetCol(pos.column);
+ maBasePos.SetRow(pos.row);
+}
+
+void ScOrcusNamedExpression::set_named_expression(std::string_view name, std::string_view expression)
+{
+ maName = OUString(name.data(), name.size(), mrGlobalSettings.getTextEncoding());
+ maExpr = OUString(expression.data(), expression.size(), mrGlobalSettings.getTextEncoding());
+}
+
+void ScOrcusNamedExpression::set_named_range(std::string_view /*name*/, std::string_view /*range*/)
+{
+ throw std::runtime_error("ScOrcusNamedExpression::set_named_range not implemented yet.");
+}
+
+void ScOrcusNamedExpression::commit()
+{
+ ScRangeName* pNames = mnTab >= 0 ? mrDoc.getDoc().GetRangeName(mnTab) : mrDoc.getDoc().GetRangeName();
+ if (!pNames)
+ return;
+
+ ScRangeData* pRange = new ScRangeData(
+ mrDoc.getDoc(), maName, maExpr, maBasePos, ScRangeData::Type::Name,
+ mrGlobalSettings.getCalcGrammar());
+
+ pNames->insert(pRange, false);
+
+ reset(); // make sure to reset the state for the next run.
+}
+
+ScOrcusFactory::CellStoreToken::CellStoreToken(const ScAddress& rPos, Type eType)
+ : mfValue(std::numeric_limits<double>::quiet_NaN())
+ , maPos(rPos)
+ , meType(eType)
+ , mnIndex1(0)
+ , mnIndex2(0)
+ , meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED)
+{
+}
+
+ScOrcusFactory::CellStoreToken::CellStoreToken(const ScAddress& rPos, double fValue)
+ : mfValue(fValue)
+ , maPos(rPos)
+ , meType(Type::Numeric)
+ , mnIndex1(0)
+ , mnIndex2(0)
+ , meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED)
+{
+}
+
+ScOrcusFactory::CellStoreToken::CellStoreToken(const ScAddress& rPos, uint32_t nIndex)
+ : mfValue(std::numeric_limits<double>::quiet_NaN())
+ , maPos(rPos)
+ , meType(Type::String)
+ , mnIndex1(nIndex)
+ , mnIndex2(0)
+ , meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED)
+{
+}
+
+ScOrcusFactory::CellStoreToken::CellStoreToken(const ScAddress& rPos, const OUString& rFormula,
+ formula::FormulaGrammar::Grammar eGrammar)
+ : maStr1(rFormula)
+ , mfValue(std::numeric_limits<double>::quiet_NaN())
+ , maPos(rPos)
+ , meType(Type::Formula)
+ , mnIndex1(0)
+ , mnIndex2(0)
+ , meGrammar(eGrammar)
+{
+}
+
+ScOrcusFactory::ScOrcusFactory(ScDocument& rDoc, bool bSkipDefaultStyles) :
+ maDoc(rDoc),
+ maGlobalSettings(maDoc),
+ maSharedStrings(*this),
+ maNamedExpressions(maDoc, maGlobalSettings),
+ maStyles(*this, bSkipDefaultStyles),
+ mnProgress(0) {}
+
+orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::append_sheet(
+ orcus::spreadsheet::sheet_t sheet_index, std::string_view sheet_name)
+{
+ OUString aTabName(sheet_name.data(), sheet_name.size(), maGlobalSettings.getTextEncoding());
+
+ if (sheet_index == 0)
+ {
+ // The calc document initializes with one sheet already present.
+ assert(maDoc.getSheetCount() == 1);
+ maDoc.setSheetName(0, aTabName);
+ maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, 0, *this));
+ return maSheets.back().get();
+ }
+
+ if (!maDoc.appendSheet(aTabName))
+ return nullptr;
+
+ SCTAB nTab = maDoc.getSheetCount() - 1;
+ maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this));
+ return maSheets.back().get();
+}
+
+namespace {
+
+class FindSheetByIndex
+{
+ SCTAB mnTab;
+public:
+ explicit FindSheetByIndex(SCTAB nTab) : mnTab(nTab) {}
+ bool operator() (const std::unique_ptr<ScOrcusSheet>& rSheet) const
+ {
+ return rSheet->getIndex() == mnTab;
+ }
+};
+
+}
+
+orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::get_sheet(std::string_view sheet_name)
+{
+ OUString aTabName(sheet_name.data(), sheet_name.size(), maGlobalSettings.getTextEncoding());
+ SCTAB nTab = maDoc.getSheetIndex(aTabName);
+ if (nTab < 0)
+ // Sheet by that name not found.
+ return nullptr;
+
+ // See if we already have an orcus sheet instance by that index.
+ std::vector< std::unique_ptr<ScOrcusSheet> >::iterator it =
+ std::find_if(maSheets.begin(), maSheets.end(), FindSheetByIndex(nTab));
+
+ if (it != maSheets.end())
+ // We already have one. Return it.
+ return it->get();
+
+ // Create a new orcus sheet instance for this.
+ maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this));
+ return maSheets.back().get();
+}
+
+orcus::spreadsheet::iface::import_sheet* ScOrcusFactory::get_sheet(orcus::spreadsheet::sheet_t sheet_index)
+{
+ SCTAB nTab = static_cast<SCTAB>(sheet_index);
+ // See if we already have an orcus sheet instance by that index.
+ std::vector< std::unique_ptr<ScOrcusSheet> >::iterator it =
+ std::find_if(maSheets.begin(), maSheets.end(), FindSheetByIndex(nTab));
+
+ if (it != maSheets.end())
+ // We already have one. Return it.
+ return it->get();
+
+ // Create a new orcus sheet instance for this.
+ maSheets.push_back(std::make_unique<ScOrcusSheet>(maDoc, nTab, *this));
+ return maSheets.back().get();
+}
+
+orcus::spreadsheet::iface::import_global_settings* ScOrcusFactory::get_global_settings()
+{
+ return &maGlobalSettings;
+}
+
+orcus::spreadsheet::iface::import_shared_strings* ScOrcusFactory::get_shared_strings()
+{
+ return &maSharedStrings;
+}
+
+orcus::spreadsheet::iface::import_named_expression* ScOrcusFactory::get_named_expression()
+{
+ return &maNamedExpressions;
+}
+
+orcus::spreadsheet::iface::import_styles* ScOrcusFactory::get_styles()
+{
+ return &maStyles;
+}
+
+void ScOrcusFactory::finalize()
+{
+ auto toFormulaCell = [this]( const CellStoreToken& rToken ) -> std::unique_ptr<ScFormulaCell>
+ {
+ const ScOrcusSheet& rSheet = *maSheets.at(rToken.maPos.Tab());
+ const sc::SharedFormulaGroups& rSFG = rSheet.getSharedFormulaGroups();
+ const ScTokenArray* pArray = rSFG.get(rToken.mnIndex1);
+ if (!pArray)
+ return std::unique_ptr<ScFormulaCell>();
+
+ return std::make_unique<ScFormulaCell>(maDoc.getDoc(), rToken.maPos, *pArray);
+ };
+
+ int nCellCount = 0;
+
+ for (const CellStoreToken& rToken : maCellStoreTokens)
+ {
+ switch (rToken.meType)
+ {
+ case CellStoreToken::Type::Auto:
+ {
+ maDoc.setAutoInput(rToken.maPos, rToken.maStr1);
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::String:
+ {
+ if (rToken.mnIndex1 >= maStrings.size())
+ // String index out-of-bound! Something is up.
+ break;
+
+ maDoc.setStringCell(rToken.maPos, maStrings[rToken.mnIndex1]);
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::Numeric:
+ {
+ maDoc.setNumericCell(rToken.maPos, rToken.mfValue);
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::Formula:
+ {
+ maDoc.setFormulaCell(
+ rToken.maPos, rToken.maStr1, rToken.meGrammar);
+
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::FormulaWithResult:
+ {
+ if (std::isfinite(rToken.mfValue))
+ maDoc.setFormulaCell(rToken.maPos, rToken.maStr1, rToken.meGrammar, &rToken.mfValue);
+ else
+ maDoc.setFormulaCell(rToken.maPos, rToken.maStr1, rToken.meGrammar, rToken.maStr2);
+
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::SharedFormula:
+ {
+ std::unique_ptr<ScFormulaCell> pCell = toFormulaCell(rToken);
+ if (!pCell)
+ break;
+
+ maDoc.setFormulaCell(rToken.maPos, pCell.release());
+
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::SharedFormulaWithResult:
+ {
+ std::unique_ptr<ScFormulaCell> pCell = toFormulaCell(rToken);
+ if (!pCell)
+ break;
+
+ if (std::isfinite(rToken.mfValue))
+ pCell->SetResultDouble(rToken.mfValue);
+ else
+ pCell->SetHybridString(
+ maDoc.getDoc().GetSharedStringPool().intern(rToken.maStr2));
+
+ maDoc.setFormulaCell(rToken.maPos, pCell.release());
+
+ ++nCellCount;
+ break;
+ }
+ case CellStoreToken::Type::Matrix:
+ {
+ if (!rToken.mnIndex1 || !rToken.mnIndex2)
+ break;
+
+ ScRange aRange(rToken.maPos);
+ aRange.aEnd.IncCol(rToken.mnIndex1-1);
+ aRange.aEnd.IncRow(rToken.mnIndex2-1);
+
+ ScCompiler aComp(maDoc.getDoc(), aRange.aStart, rToken.meGrammar);
+ std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(rToken.maStr1));
+ if (!pArray)
+ break;
+
+ maDoc.setMatrixCells(aRange, *pArray, rToken.meGrammar);
+ break;
+ }
+ case CellStoreToken::Type::FillDownCells:
+ {
+ if (!rToken.mnIndex1)
+ break;
+
+ maDoc.fillDownCells(rToken.maPos, rToken.mnIndex1);
+ break;
+ }
+ default:
+ ;
+ }
+
+ if (nCellCount == 100000)
+ {
+ incrementProgress();
+ nCellCount = 0;
+ }
+ }
+
+ if (mxStatusIndicator.is())
+ mxStatusIndicator->end();
+
+ maDoc.finalize();
+}
+
+ScDocumentImport& ScOrcusFactory::getDoc()
+{
+ return maDoc;
+}
+
+size_t ScOrcusFactory::appendString(const OUString& rStr)
+{
+ size_t nPos = maStrings.size();
+ maStrings.push_back(rStr);
+ maStringHash.emplace(rStr, nPos);
+
+ return nPos;
+}
+
+size_t ScOrcusFactory::addString(const OUString& rStr)
+{
+ // Add only if the string is not yet present in the string pool.
+ StringHashType::iterator it = maStringHash.find(rStr);
+ if (it != maStringHash.end())
+ return it->second;
+
+ return appendString(rStr);
+}
+
+const OUString* ScOrcusFactory::getString(size_t nIndex) const
+{
+ return nIndex < maStrings.size() ? &maStrings[nIndex] : nullptr;
+}
+
+void ScOrcusFactory::pushCellStoreAutoToken( const ScAddress& rPos, const OUString& rVal )
+{
+ maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::Auto);
+ maCellStoreTokens.back().maStr1 = rVal;
+}
+
+void ScOrcusFactory::pushCellStoreToken( const ScAddress& rPos, uint32_t nStrIndex )
+{
+ maCellStoreTokens.emplace_back(rPos, nStrIndex);
+}
+
+void ScOrcusFactory::pushCellStoreToken( const ScAddress& rPos, double fValue )
+{
+ maCellStoreTokens.emplace_back(rPos, fValue);
+}
+
+void ScOrcusFactory::pushCellStoreToken(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar )
+{
+ maCellStoreTokens.emplace_back(rPos, rFormula, eGrammar);
+}
+
+void ScOrcusFactory::pushFillDownCellsToken( const ScAddress& rPos, uint32_t nFillSize )
+{
+ maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::FillDownCells);
+ maCellStoreTokens.back().mnIndex1 = nFillSize;
+}
+
+void ScOrcusFactory::pushSharedFormulaToken( const ScAddress& rPos, uint32_t nIndex )
+{
+ maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::SharedFormula);
+ maCellStoreTokens.back().mnIndex1 = nIndex;
+}
+
+void ScOrcusFactory::pushMatrixFormulaToken(
+ const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar,
+ uint32_t nRowRange, uint32_t nColRange )
+{
+ maCellStoreTokens.emplace_back(rPos, CellStoreToken::Type::Matrix);
+ CellStoreToken& rT = maCellStoreTokens.back();
+ rT.maStr1 = rFormula;
+ rT.meGrammar = eGrammar;
+ rT.mnIndex1 = nColRange;
+ rT.mnIndex2 = nRowRange;
+}
+
+void ScOrcusFactory::pushFormulaResult( const ScAddress& rPos, double fValue )
+{
+ // Formula result is expected to be pushed immediately following the
+ // formula token it belongs.
+ if (maCellStoreTokens.empty())
+ return;
+
+ CellStoreToken& rToken = maCellStoreTokens.back();
+ if (rToken.maPos != rPos)
+ return;
+
+ switch (rToken.meType)
+ {
+ case CellStoreToken::Type::Formula:
+ rToken.meType = CellStoreToken::Type::FormulaWithResult;
+ break;
+ case CellStoreToken::Type::SharedFormula:
+ rToken.meType = CellStoreToken::Type::SharedFormulaWithResult;
+ break;
+ default:
+ return;
+ }
+
+ rToken.mfValue = fValue;
+}
+
+void ScOrcusFactory::pushFormulaResult( const ScAddress& rPos, const OUString& rValue )
+{
+ // Formula result is expected to be pushed immediately following the
+ // formula token it belongs.
+ if (maCellStoreTokens.empty())
+ return;
+
+ CellStoreToken& rToken = maCellStoreTokens.back();
+ if (rToken.maPos != rPos)
+ return;
+
+ switch (rToken.meType)
+ {
+ case CellStoreToken::Type::Formula:
+ rToken.meType = CellStoreToken::Type::FormulaWithResult;
+ break;
+ case CellStoreToken::Type::SharedFormula:
+ rToken.meType = CellStoreToken::Type::SharedFormulaWithResult;
+ break;
+ default:
+ return;
+ }
+
+ rToken.maStr2 = rValue;
+}
+
+void ScOrcusFactory::incrementProgress()
+{
+ if (!mxStatusIndicator.is())
+ // Status indicator object not set.
+ return;
+
+ // For now, we'll hard-code the progress range to be 100, and stops at 99
+ // in all cases.
+
+ if (!mnProgress)
+ mxStatusIndicator->start(ScResId(STR_LOAD_DOC), 100);
+
+ if (mnProgress == 99)
+ return;
+
+ ++mnProgress;
+ mxStatusIndicator->setValue(mnProgress);
+}
+
+void ScOrcusFactory::setStatusIndicator(const uno::Reference<task::XStatusIndicator>& rIndicator)
+{
+ mxStatusIndicator = rIndicator;
+}
+
+const ScOrcusGlobalSettings& ScOrcusFactory::getGlobalSettings() const
+{
+ return maGlobalSettings;
+}
+
+ScOrcusSheetProperties::ScOrcusSheetProperties(SCTAB nTab, ScDocumentImport& rDoc):
+ mrDoc(rDoc),
+ mnTab(nTab)
+{
+}
+
+ScOrcusSheetProperties::~ScOrcusSheetProperties()
+{
+}
+
+namespace {
+
+double translateToInternal(double nVal, orcus::length_unit_t unit)
+{
+ switch(unit)
+ {
+ case orcus::length_unit_t::inch:
+ return nVal * 72.0 * 20.0;
+ case orcus::length_unit_t::twip:
+ return nVal;
+ case orcus::length_unit_t::point:
+ return nVal * 20.0;
+ case orcus::length_unit_t::centimeter:
+ return nVal * 20.0 * 72.0 / 2.54;
+ case orcus::length_unit_t::unknown:
+ if (nVal != 0)
+ SAL_WARN("sc.orcus", "unknown unit");
+ break;
+ default:
+ break;
+ }
+ return nVal;
+}
+
+
+}
+
+void ScOrcusSheetProperties::set_column_width(os::col_t col, double width, orcus::length_unit_t unit)
+{
+ double nNewWidth = translateToInternal(width, unit);
+ mrDoc.getDoc().SetColWidthOnly(col, mnTab, nNewWidth);
+}
+
+void ScOrcusSheetProperties::set_column_hidden(os::col_t col, bool hidden)
+{
+ if (hidden)
+ mrDoc.getDoc().SetColHidden(col, col, mnTab, hidden);
+}
+
+void ScOrcusSheetProperties::set_row_height(os::row_t row, double height, orcus::length_unit_t unit)
+{
+ double nNewHeight = translateToInternal(height, unit);
+ mrDoc.getDoc().SetRowHeightOnly(row, row,mnTab, nNewHeight);
+}
+
+void ScOrcusSheetProperties::set_row_hidden(os::row_t row, bool hidden)
+{
+ if (hidden)
+ mrDoc.getDoc().SetRowHidden(row, row, mnTab, hidden);
+}
+
+void ScOrcusSheetProperties::set_merge_cell_range(const orcus::spreadsheet::range_t& range)
+{
+ mrDoc.setMergedCells(mnTab, range.first.column, range.first.row, range.last.column, range.last.row);
+}
+
+ScOrcusConditionalFormat::ScOrcusConditionalFormat(SCTAB nTab, ScDocument& rDoc):
+ mnTab(nTab),
+ mrDoc(rDoc),
+ mpCurrentFormat(new ScConditionalFormat(0, &mrDoc)),
+ meEntryType(ScFormatEntry::Type::Condition)
+{
+}
+
+ScOrcusConditionalFormat::~ScOrcusConditionalFormat()
+{
+}
+
+void ScOrcusConditionalFormat::set_color(os::color_elem_t /*alpha*/, os::color_elem_t /*red*/,
+ os::color_elem_t /*green*/, os::color_elem_t /*blue*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_color");
+}
+
+void ScOrcusConditionalFormat::set_condition_type(os::condition_type_t /*type*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Condition);
+ SAL_INFO("sc.orcus.condformat", "set_condition_type");
+}
+
+void ScOrcusConditionalFormat::set_formula(std::string_view /*formula*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_formula");
+}
+
+void ScOrcusConditionalFormat::set_date(os::condition_date_t /*date*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Date);
+ SAL_INFO("sc.orcus.condformat", "set_date");
+}
+
+void ScOrcusConditionalFormat::commit_condition()
+{
+ SAL_INFO("sc.orcus.condformat", "commit_condition");
+}
+
+void ScOrcusConditionalFormat::set_icon_name(std::string_view /*name*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Iconset);
+ SAL_INFO("sc.orcus.condformat", "set_icon_name");
+}
+
+void ScOrcusConditionalFormat::set_databar_gradient(bool /*gradient*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_databar_gradient");
+}
+
+void ScOrcusConditionalFormat::set_databar_axis(os::databar_axis_t /*axis*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_databar_axis");
+}
+
+void ScOrcusConditionalFormat::set_databar_color_positive(os::color_elem_t /*alpha*/, os::color_elem_t /*red*/,
+ os::color_elem_t /*green*/, os::color_elem_t /*blue*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_databar_color_positive");
+}
+
+void ScOrcusConditionalFormat::set_databar_color_negative(os::color_elem_t /*alpha*/, os::color_elem_t /*red*/,
+ os::color_elem_t /*green*/, os::color_elem_t /*blue*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_databar_color_negative");
+}
+
+void ScOrcusConditionalFormat::set_min_databar_length(double /*length*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_min_databar_length");
+}
+
+void ScOrcusConditionalFormat::set_max_databar_length(double /*length*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Databar);
+ SAL_INFO("sc.orcus.condformat", "set_max_databar_length");
+}
+
+void ScOrcusConditionalFormat::set_show_value(bool /*show*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_show_value");
+}
+
+void ScOrcusConditionalFormat::set_iconset_reverse(bool /*reverse*/)
+{
+ assert(meEntryType == ScFormatEntry::Type::Iconset);
+ SAL_INFO("sc.orcus.condformat", "set_iconset_reverse");
+}
+
+void ScOrcusConditionalFormat::set_xf_id(size_t /*xf*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_xf_id");
+}
+
+void ScOrcusConditionalFormat::set_operator(os::condition_operator_t /*condition_type*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_operator");
+}
+
+void ScOrcusConditionalFormat::set_type(os::conditional_format_t type)
+{
+ switch (type)
+ {
+ case os::conditional_format_t::condition:
+ case os::conditional_format_t::formula:
+ meEntryType = ScFormatEntry::Type::Condition;
+ // mpCurrentEntry.reset(new ScCondFormatEntry());
+ break;
+ case os::conditional_format_t::date:
+ break;
+ case os::conditional_format_t::colorscale:
+ break;
+ case os::conditional_format_t::databar:
+ break;
+ case os::conditional_format_t::iconset:
+ break;
+ default:
+ SAL_INFO("sc.orcus.condformat", "unknown conditional_format_t value");
+ break;
+ }
+ SAL_INFO("sc.orcus.condformat", "set_type");
+}
+
+void ScOrcusConditionalFormat::commit_entry()
+{
+ SAL_INFO("sc.orcus.condformat", "commit_entry");
+}
+
+void ScOrcusConditionalFormat::set_range(std::string_view /*range*/)
+{
+ SAL_INFO("sc.orcus.condformat", "set_range");
+}
+
+void ScOrcusConditionalFormat::set_range(os::row_t row_start, os::col_t col_start,
+ os::row_t row_end, os::col_t col_end)
+{
+ SAL_INFO("sc.orcus.condformat", "set_range");
+ ScRange aRange(col_start, row_start, mnTab, col_end, row_end, mnTab);
+ mpCurrentFormat->SetRange(aRange);
+}
+
+void ScOrcusConditionalFormat::commit_format()
+{
+ SAL_INFO("sc.orcus.condformat", "commit_format");
+ mpCurrentFormat.reset(new ScConditionalFormat(0, &mrDoc));
+}
+
+ScOrcusSheet::ScOrcusSheet(ScDocumentImport& rDoc, SCTAB nTab, ScOrcusFactory& rFactory) :
+ mrDoc(rDoc),
+ mnTab(nTab),
+ mrFactory(rFactory),
+ mrStyles(static_cast<ScOrcusStyles&>(*mrFactory.get_styles())),
+ maAutoFilter(rFactory.getGlobalSettings()),
+ maProperties(mnTab, mrDoc),
+ maConditionalFormat(mnTab, rDoc.getDoc()),
+ maNamedExpressions(rDoc, rFactory.getGlobalSettings(), nTab),
+ maFormula(*this),
+ maArrayFormula(*this),
+ mnCellCount(0)
+{
+}
+
+void ScOrcusFormula::reset()
+{
+ mnCol = -1;
+ mnRow = -1;
+ maFormula.clear();
+ meGrammar = formula::FormulaGrammar::GRAM_UNSPECIFIED;
+ mnSharedFormulaIndex = 0;
+ mbShared = false;
+ meResType = ResultType::NotSet;
+ maResult.clear();
+ mfResult = 0.0;
+}
+
+ScOrcusFormula::ScOrcusFormula( ScOrcusSheet& rSheet ) :
+ mrSheet(rSheet),
+ mnCol(-1),
+ mnRow(-1),
+ meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED),
+ mnSharedFormulaIndex(0),
+ mbShared(false),
+ meResType(ResultType::NotSet),
+ mfResult(0.0) {}
+
+ScOrcusFormula::~ScOrcusFormula() {}
+
+void ScOrcusFormula::set_position(os::row_t row, os::col_t col)
+{
+ mnCol = col;
+ mnRow = row;
+}
+
+void ScOrcusFormula::set_formula(os::formula_grammar_t grammar, std::string_view formula)
+{
+ maFormula = OUString(formula.data(), formula.size(), mrSheet.getFactory().getGlobalSettings().getTextEncoding());
+ meGrammar = getCalcGrammarFromOrcus(grammar);
+}
+
+void ScOrcusFormula::set_shared_formula_index(size_t index)
+{
+ mnSharedFormulaIndex = index;
+ mbShared = true;
+}
+
+void ScOrcusFormula::set_result_value(double value)
+{
+ meResType = ResultType::Value;
+ mfResult = value;
+}
+
+void ScOrcusFormula::set_result_string(std::string_view value)
+{
+ meResType = ResultType::String;
+ maResult = OUString(value.data(), value.size(), mrSheet.getFactory().getGlobalSettings().getTextEncoding());
+}
+
+void ScOrcusFormula::set_result_empty()
+{
+ meResType = ResultType::Empty;
+}
+
+void ScOrcusFormula::set_result_bool(bool value)
+{
+ meResType = ResultType::Value;
+ mfResult = value ? 1.0 : 0.0;
+}
+
+void ScOrcusFormula::commit()
+{
+ ScOrcusFactory& rFactory = mrSheet.getFactory();
+ sc::SharedFormulaGroups& rGroups = mrSheet.getSharedFormulaGroups();
+ ScAddress aPos(mnCol, mnRow, mrSheet.getIndex());
+
+ if (mbShared)
+ {
+ if (maFormula.isEmpty())
+ {
+ // shared formula that references existing formula token.
+ const ScTokenArray* pArray = rGroups.get(mnSharedFormulaIndex);
+ if (!pArray)
+ return;
+ }
+ else
+ {
+ // topmost shared formula with new formula token.
+
+ // Compile the formula expression into tokens.
+ ScCompiler aComp(mrSheet.getDoc().getDoc(), aPos, meGrammar);
+ std::unique_ptr<ScTokenArray> pArray = aComp.CompileString(maFormula);
+ if (!pArray)
+ // Tokenization failed.
+ return;
+
+ rGroups.set(mnSharedFormulaIndex, std::move(pArray));
+ }
+ rFactory.pushSharedFormulaToken(aPos, mnSharedFormulaIndex);
+ }
+ else
+ {
+ // non-shared formula
+ rFactory.pushCellStoreToken(aPos, maFormula, meGrammar);
+ }
+
+ switch (meResType)
+ {
+ case ResultType::String:
+ {
+ rFactory.pushFormulaResult(aPos, maResult);
+ break;
+ }
+ case ResultType::Value:
+ rFactory.pushFormulaResult(aPos, mfResult);
+ break;
+ default:
+ ;
+ }
+
+ mrSheet.cellInserted();
+}
+
+void ScOrcusArrayFormula::reset()
+{
+ mnCol = -1;
+ mnRow = -1;
+ mnColRange = 0;
+ mnRowRange = 0;
+
+ maFormula.clear();
+ meGrammar = formula::FormulaGrammar::GRAM_UNSPECIFIED;
+}
+
+ScOrcusArrayFormula::ScOrcusArrayFormula( ScOrcusSheet& rSheet ) :
+ mrSheet(rSheet),
+ mnCol(-1),
+ mnRow(-1),
+ mnColRange(0),
+ mnRowRange(0),
+ meGrammar(formula::FormulaGrammar::GRAM_UNSPECIFIED) {}
+
+ScOrcusArrayFormula::~ScOrcusArrayFormula() {}
+
+void ScOrcusArrayFormula::set_range(const os::range_t& range)
+{
+ mnCol = range.first.column;
+ mnRow = range.first.row;
+
+ mnColRange = range.last.column - range.first.column + 1;
+ mnRowRange = range.last.row - range.first.column + 1;
+}
+
+void ScOrcusArrayFormula::set_formula(os::formula_grammar_t grammar, std::string_view formula)
+{
+ meGrammar = getCalcGrammarFromOrcus(grammar);
+ maFormula = OUString(formula.data(), formula.size(), mrSheet.getFactory().getGlobalSettings().getTextEncoding());
+}
+
+void ScOrcusArrayFormula::set_result_value(os::row_t /*row*/, os::col_t /*col*/, double /*value*/)
+{
+ // TODO : implement result cache for matrix
+}
+
+void ScOrcusArrayFormula::set_result_string(os::row_t /*row*/, os::col_t /*col*/, std::string_view /*value*/)
+{
+ // TODO : implement result cache for matrix
+}
+
+void ScOrcusArrayFormula::set_result_empty(os::row_t /*row*/, os::col_t /*col*/)
+{
+ // TODO : implement result cache for matrix
+}
+
+void ScOrcusArrayFormula::set_result_bool(os::row_t /*row*/, os::col_t /*col*/, bool /*value*/)
+{
+ // TODO : implement result cache for matrix
+}
+
+void ScOrcusArrayFormula::commit()
+{
+ ScAddress aPos(mnCol, mnRow, mrSheet.getIndex());
+ mrSheet.getFactory().pushMatrixFormulaToken(aPos, maFormula, meGrammar, mnRowRange, mnColRange);
+ mrSheet.cellInserted();
+}
+
+void ScOrcusSheet::cellInserted()
+{
+ ++mnCellCount;
+ if (mnCellCount == 100000)
+ {
+ mrFactory.incrementProgress();
+ mnCellCount = 0;
+ }
+}
+
+ScDocumentImport& ScOrcusSheet::getDoc()
+{
+ return mrDoc;
+}
+
+os::iface::import_auto_filter* ScOrcusSheet::get_auto_filter()
+{
+ return &maAutoFilter;
+}
+
+os::iface::import_table* ScOrcusSheet::get_table()
+{
+ return nullptr;
+}
+
+os::iface::import_sheet_properties* ScOrcusSheet::get_sheet_properties()
+{
+ return &maProperties;
+}
+
+os::iface::import_conditional_format* ScOrcusSheet::get_conditional_format()
+{
+ return &maConditionalFormat;
+}
+
+os::iface::import_named_expression* ScOrcusSheet::get_named_expression()
+{
+ return &maNamedExpressions;
+}
+
+os::iface::import_formula* ScOrcusSheet::get_formula()
+{
+ maFormula.reset();
+ return &maFormula;
+}
+
+os::iface::import_array_formula* ScOrcusSheet::get_array_formula()
+{
+ maArrayFormula.reset();
+ return &maArrayFormula;
+}
+
+void ScOrcusSheet::set_auto(os::row_t row, os::col_t col, std::string_view value)
+{
+ OUString aVal(value.data(), value.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ mrFactory.pushCellStoreAutoToken(ScAddress(col, row, mnTab), aVal);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_string(os::row_t row, os::col_t col, os::string_id_t sindex)
+{
+ mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), sindex);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_value(os::row_t row, os::col_t col, double value)
+{
+ mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), value);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_bool(os::row_t row, os::col_t col, bool value)
+{
+ mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), value ? 1.0 : 0.0);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_date_time(
+ os::row_t row, os::col_t col, int year, int month, int day, int hour, int minute, double second)
+{
+ SvNumberFormatter* pFormatter = mrDoc.getDoc().GetFormatTable();
+
+ Date aDate(day, month, year);
+ sal_uInt32 nSec = floor(second);
+ sal_uInt32 nNanoSec = (second - nSec) * ::tools::Time::nanoSecPerSec;
+ tools::Time aTime(hour, minute, nSec, nNanoSec);
+ tools::Long nDateDiff = aDate - pFormatter->GetNullDate();
+
+ double fTime =
+ static_cast<double>(aTime.GetNanoSec()) / ::tools::Time::nanoSecPerSec +
+ aTime.GetSec() +
+ aTime.GetMin() * ::tools::Time::secondPerMinute +
+ aTime.GetHour() * ::tools::Time::secondPerHour;
+
+ fTime /= DATE_TIME_FACTOR;
+
+ mrFactory.pushCellStoreToken(ScAddress(col, row, mnTab), nDateDiff + fTime);
+ cellInserted();
+}
+
+void ScOrcusSheet::set_format(os::row_t row, os::col_t col, size_t xf_index)
+{
+ SAL_INFO("sc.orcus.style", "set format: " << xf_index);
+
+ ScPatternAttr aPattern(mrDoc.getDoc().GetPool());
+ mrStyles.applyXfToItemSet(aPattern.GetItemSet(), xf_index);
+ mrDoc.getDoc().ApplyPattern(col, row, mnTab, aPattern);
+}
+
+void ScOrcusSheet::set_format(os::row_t row_start, os::col_t col_start,
+ os::row_t row_end, os::col_t col_end, size_t xf_index)
+{
+ SAL_INFO("sc.orcus.style", "set format range: " << xf_index);
+ ScPatternAttr aPattern(mrDoc.getDoc().GetPool());
+ mrStyles.applyXfToItemSet(aPattern.GetItemSet(), xf_index);
+ mrDoc.getDoc().ApplyPatternAreaTab(col_start, row_start, col_end, row_end, mnTab, aPattern);
+}
+
+orcus::spreadsheet::range_size_t ScOrcusSheet::get_sheet_size() const
+{
+ orcus::spreadsheet::range_size_t ret;
+ ret.rows = MAXROWCOUNT;
+ ret.columns = MAXCOLCOUNT;
+
+ return ret;
+}
+
+void ScOrcusSheet::fill_down_cells(os::row_t row, os::col_t col, os::row_t range_size)
+{
+ mrFactory.pushFillDownCellsToken(ScAddress(col, row, mnTab), range_size);
+ cellInserted();
+}
+
+const sc::SharedFormulaGroups& ScOrcusSheet::getSharedFormulaGroups() const
+{
+ return maFormulaGroups;
+}
+
+sc::SharedFormulaGroups& ScOrcusSheet::getSharedFormulaGroups()
+{
+ return maFormulaGroups;
+}
+
+ScOrcusFactory& ScOrcusSheet::getFactory()
+{
+ return mrFactory;
+}
+
+ScOrcusSharedStrings::ScOrcusSharedStrings(ScOrcusFactory& rFactory) :
+ mrFactory(rFactory) {}
+
+size_t ScOrcusSharedStrings::append(std::string_view s)
+{
+ OUString aNewString(s.data(), s.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ return mrFactory.appendString(aNewString);
+}
+
+size_t ScOrcusSharedStrings::add(std::string_view s)
+{
+ OUString aNewString(s.data(), s.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ return mrFactory.addString(aNewString);
+}
+
+void ScOrcusSharedStrings::set_segment_font(size_t /*font_index*/)
+{
+}
+
+void ScOrcusSharedStrings::set_segment_bold(bool /*b*/)
+{
+}
+
+void ScOrcusSharedStrings::set_segment_italic(bool /*b*/)
+{
+}
+
+void ScOrcusSharedStrings::set_segment_font_name(std::string_view /*s*/)
+{
+}
+
+void ScOrcusSharedStrings::set_segment_font_size(double /*point*/)
+{
+}
+
+void ScOrcusSharedStrings::set_segment_font_color(orcus::spreadsheet::color_elem_t,
+ orcus::spreadsheet::color_elem_t,
+ orcus::spreadsheet::color_elem_t,
+ orcus::spreadsheet::color_elem_t)
+{
+}
+
+void ScOrcusSharedStrings::append_segment(std::string_view s)
+{
+ maCurSegment.append(s.data(), s.size());
+}
+
+size_t ScOrcusSharedStrings::commit_segments()
+{
+ OString aStr = maCurSegment.makeStringAndClear();
+ return mrFactory.addString(
+ OStringToOUString(aStr, mrFactory.getGlobalSettings().getTextEncoding()));
+}
+
+ScOrcusStyles::ScOrcusStyles( ScOrcusFactory& rFactory, bool bSkipDefaultStyles ) :
+ mrFactory(rFactory)
+{
+ ScDocument& rDoc = rFactory.getDoc().getDoc();
+ if (!bSkipDefaultStyles && !rDoc.GetStyleSheetPool()->HasStandardStyles())
+ rDoc.GetStyleSheetPool()->CreateStandardStyles();
+}
+
+/*
+namespace {
+
+std::ostream& operator<<(std::ostream& rStrm, const Color& rColor)
+{
+ rStrm << "Red: " << (int)rColor.GetRed() << ", Green: " << (int)rColor.GetGreen() << ", Blue: " << (int)rColor.GetBlue();
+ return rStrm;
+}
+
+}
+*/
+
+void ScOrcusStyles::font::applyToItemSet(SfxItemSet& rSet) const
+{
+ if (mbItalic)
+ {
+ FontItalic eItalic = *mbItalic ? ITALIC_NORMAL : ITALIC_NONE;
+ rSet.Put(SvxPostureItem(eItalic, ATTR_FONT_POSTURE));
+ rSet.Put(SvxPostureItem(eItalic, ATTR_CJK_FONT_POSTURE));
+ rSet.Put(SvxPostureItem(eItalic, ATTR_CTL_FONT_POSTURE));
+ }
+
+ if (mbBold)
+ {
+ FontWeight eWeight = *mbBold ? WEIGHT_BOLD : WEIGHT_NORMAL;
+ rSet.Put(SvxWeightItem(eWeight, ATTR_FONT_WEIGHT));
+ rSet.Put(SvxWeightItem(eWeight, ATTR_CJK_FONT_WEIGHT));
+ rSet.Put(SvxWeightItem(eWeight, ATTR_CTL_FONT_WEIGHT));
+ }
+
+ if (maColor)
+ rSet.Put( SvxColorItem(*maColor, ATTR_FONT_COLOR));
+
+ if (maName && !maName->isEmpty())
+ rSet.Put( SvxFontItem( FAMILY_DONTKNOW, *maName, *maName, PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ));
+
+ if (mnSize)
+ {
+ double fSize = translateToInternal(*mnSize, orcus::length_unit_t::point);
+ rSet.Put(SvxFontHeightItem(fSize, 100, ATTR_FONT_HEIGHT));
+ rSet.Put(SvxFontHeightItem(fSize, 100, ATTR_CJK_FONT_HEIGHT));
+ rSet.Put(SvxFontHeightItem(fSize, 100, ATTR_CTL_FONT_HEIGHT));
+ }
+
+ if (meUnderline)
+ {
+ SvxUnderlineItem aUnderline(*meUnderline, ATTR_FONT_UNDERLINE);
+ if (maUnderlineColor)
+ aUnderline.SetColor(*maUnderlineColor);
+ rSet.Put(aUnderline);
+ }
+
+ if (meStrikeout)
+ rSet.Put(SvxCrossedOutItem(*meStrikeout, ATTR_FONT_CROSSEDOUT));
+}
+
+void ScOrcusStyles::fill::applyToItemSet(SfxItemSet& rSet) const
+{
+ if (!mePattern || !maFgColor)
+ return;
+
+ if (*mePattern == os::fill_pattern_t::solid)
+ rSet.Put(SvxBrushItem(*maFgColor, ATTR_BACKGROUND));
+}
+
+void ScOrcusStyles::protection::applyToItemSet(SfxItemSet& rSet) const
+{
+ if (!mbLocked && !mbHidden && !mbPrintContent && !mbFormulaHidden)
+ return;
+
+ bool bLocked = mbLocked.value_or(true); // defaults to true.
+ bool bHidden = mbHidden.value_or(false);
+ bool bFormulaHidden = mbFormulaHidden.value_or(false);
+ bool bPrintContent = mbPrintContent.value_or(false);
+ rSet.Put(ScProtectionAttr(bLocked, bFormulaHidden, bHidden, bPrintContent));
+}
+
+namespace {
+
+SvxBoxItemLine getDirection(os::border_direction_t dir)
+{
+ switch (dir)
+ {
+ case os::border_direction_t::right:
+ return SvxBoxItemLine::RIGHT;
+ case os::border_direction_t::left:
+ return SvxBoxItemLine::LEFT;
+ case os::border_direction_t::top:
+ return SvxBoxItemLine::TOP;
+ case os::border_direction_t::bottom:
+ return SvxBoxItemLine::BOTTOM;
+ default:
+ break;
+ }
+ return SvxBoxItemLine::RIGHT;
+}
+
+}
+
+void ScOrcusStyles::border::applyToItemSet(SfxItemSet& rSet) const
+{
+ if (maBorders.empty())
+ return;
+
+ SvxBoxItem aBoxItem(ATTR_BORDER);
+ SvxLineItem aDiagonal_TLBR(ATTR_BORDER_TLBR);
+ SvxLineItem aDiagonal_BLTR(ATTR_BORDER_BLTR);
+
+ for (const auto& [dir, attrs] : maBorders)
+ {
+ SvxBoxItemLine eDir = getDirection(dir);
+
+ SvxBorderLineStyle eStyle = attrs.meStyle.value_or(SvxBorderLineStyle::SOLID);
+ Color aColor = attrs.maColor.value_or(COL_BLACK);
+ double nWidth = attrs.mnWidth.value_or(0.0);
+
+ switch (dir)
+ {
+ case os::border_direction_t::diagonal_tl_br:
+ {
+ editeng::SvxBorderLine aLine(&aColor, nWidth, eStyle);
+ aDiagonal_TLBR.SetLine(&aLine);
+ break;
+ }
+ case os::border_direction_t::diagonal_bl_tr:
+ {
+ editeng::SvxBorderLine aLine(&aColor, nWidth, eStyle);
+ aDiagonal_BLTR.SetLine(&aLine);
+ break;
+ }
+ default:
+ {
+ editeng::SvxBorderLine aLine(&aColor, nWidth, eStyle);
+ aBoxItem.SetLine(&aLine, eDir);
+ }
+ }
+ }
+
+ rSet.Put(aDiagonal_BLTR);
+ rSet.Put(aDiagonal_TLBR);
+ rSet.Put(aBoxItem);
+}
+
+void ScOrcusStyles::number_format::applyToItemSet(SfxItemSet& rSet, const ScDocument& rDoc) const
+{
+ if (!maCode)
+ return;
+
+ sal_uInt32 nKey;
+ sal_Int32 nCheckPos;
+ SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
+ OUString Code = *maCode; /* <-- Done because the SvNumberFormatter::PutEntry demands a non const NumFormat Code*/
+ SvNumFormatType type = SvNumFormatType::ALL;
+
+ pFormatter->PutEntry(Code, nCheckPos, type, nKey, LANGUAGE_ENGLISH_US);
+ if (!nCheckPos)
+ rSet.Put(SfxUInt32Item(ATTR_VALUE_FORMAT, nKey));
+}
+
+ScOrcusStyles::xf::xf():
+ mnFontId(0),
+ mnFillId(0),
+ mnBorderId(0),
+ mnProtectionId(0),
+ mnNumberFormatId(0),
+ mnStyleXf(0),
+ mbAlignment(false),
+ meHorAlignment(SvxCellHorJustify::Standard),
+ meVerAlignment(SvxCellVerJustify::Standard),
+ meHorAlignMethod(SvxCellJustifyMethod::Auto),
+ meVerAlignMethod(SvxCellJustifyMethod::Auto)
+{
+}
+
+ScOrcusStyles::cell_style::cell_style():
+ maParentName(OUString(SC_STYLE_PROG_STANDARD)),
+ mnXFId(0),
+ mnBuiltInId(0)
+{
+}
+
+void ScOrcusStyles::applyXfToItemSet(SfxItemSet& rSet, const xf& rXf)
+{
+ size_t nFontId = rXf.mnFontId;
+ if (nFontId >= maFonts.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid font id");
+ return;
+ }
+
+ maFonts[nFontId].applyToItemSet(rSet);
+
+ size_t nFillId = rXf.mnFillId;
+ if (nFillId >= maFills.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid fill id");
+ return;
+ }
+
+ const fill& rFill = maFills[nFillId];
+ rFill.applyToItemSet(rSet);
+
+ size_t nBorderId = rXf.mnBorderId;
+ if (nBorderId >= maBorders.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid border id");
+ return;
+ }
+ maBorders[nBorderId].applyToItemSet(rSet);
+
+ size_t nProtectionId = rXf.mnProtectionId;
+ if (nProtectionId >= maProtections.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid protection id");
+ return;
+ }
+
+ maProtections[nProtectionId].applyToItemSet(rSet);
+
+ size_t nNumberFormatId = rXf.mnNumberFormatId;
+ if (nNumberFormatId >= maNumberFormats.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid number format id");
+ return;
+ }
+ const number_format& rFormat = maNumberFormats[nNumberFormatId];
+ rFormat.applyToItemSet(rSet, mrFactory.getDoc().getDoc());
+
+ if(rXf.mbAlignment)
+ {
+ rSet.Put(SvxHorJustifyItem(rXf.meHorAlignment, ATTR_HOR_JUSTIFY));
+ rSet.Put(SvxVerJustifyItem(rXf.meVerAlignment, ATTR_VER_JUSTIFY));
+ rSet.Put(SvxJustifyMethodItem(rXf.meHorAlignMethod, ATTR_HOR_JUSTIFY_METHOD));
+ rSet.Put(SvxJustifyMethodItem(rXf.meVerAlignMethod, ATTR_VER_JUSTIFY_METHOD));
+ }
+}
+
+void ScOrcusStyles::applyXfToItemSet(SfxItemSet& rSet, size_t xfId)
+{
+ SAL_INFO("sc.orcus.style", "applyXfToitemSet: " << xfId);
+ if (maCellXfs.size() <= xfId)
+ {
+ SAL_WARN("sc.orcus.style", "invalid xf id");
+ return;
+ }
+
+ const xf& rXf = maCellXfs[xfId];
+ applyXfToItemSet(rSet, rXf);
+}
+
+void ScOrcusStyles::set_font_count(size_t /*n*/)
+{
+ // needed at all?
+}
+
+void ScOrcusStyles::set_font_bold(bool b)
+{
+ maCurrentFont.mbBold = b;
+}
+
+void ScOrcusStyles::set_font_italic(bool b)
+{
+ maCurrentFont.mbItalic = b;
+}
+
+void ScOrcusStyles::set_font_name(std::string_view name)
+{
+ OUString aName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentFont.maName = aName;
+}
+
+void ScOrcusStyles::set_font_size(double point)
+{
+ maCurrentFont.mnSize = point;
+}
+
+void ScOrcusStyles::set_font_underline(orcus::spreadsheet::underline_t e)
+{
+ switch(e)
+ {
+ case orcus::spreadsheet::underline_t::single_line:
+ case orcus::spreadsheet::underline_t::single_accounting:
+ maCurrentFont.meUnderline = LINESTYLE_SINGLE;
+ break;
+ case orcus::spreadsheet::underline_t::double_line:
+ case orcus::spreadsheet::underline_t::double_accounting:
+ maCurrentFont.meUnderline = LINESTYLE_DOUBLE;
+ break;
+ case orcus::spreadsheet::underline_t::none:
+ maCurrentFont.meUnderline = LINESTYLE_NONE;
+ break;
+ case orcus::spreadsheet::underline_t::dotted:
+ maCurrentFont.meUnderline = LINESTYLE_DOTTED;
+ break;
+ case orcus::spreadsheet::underline_t::dash:
+ maCurrentFont.meUnderline = LINESTYLE_DASH;
+ break;
+ case orcus::spreadsheet::underline_t::long_dash:
+ maCurrentFont.meUnderline = LINESTYLE_LONGDASH;
+ break;
+ case orcus::spreadsheet::underline_t::dot_dash:
+ maCurrentFont.meUnderline = LINESTYLE_DASHDOT;
+ break;
+ case orcus::spreadsheet::underline_t::dot_dot_dot_dash:
+ maCurrentFont.meUnderline = LINESTYLE_DASHDOTDOT; // dot-dot-dot-dash is absent from underline types in libo
+ break;
+ case orcus::spreadsheet::underline_t::wave:
+ maCurrentFont.meUnderline = LINESTYLE_WAVE;
+ break;
+ default:
+ ;
+ }
+}
+
+void ScOrcusStyles::set_font_underline_width(orcus::spreadsheet::underline_width_t e )
+{
+ if (e == orcus::spreadsheet::underline_width_t::bold || e == orcus::spreadsheet::underline_width_t::thick)
+ {
+ if (maCurrentFont.meUnderline)
+ {
+ switch (*maCurrentFont.meUnderline)
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_SINGLE:
+ maCurrentFont.meUnderline = LINESTYLE_BOLD;
+ break;
+ case LINESTYLE_DOTTED:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDDOTTED;
+ break;
+ case LINESTYLE_DASH:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDDASH;
+ break;
+ case LINESTYLE_LONGDASH:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDLONGDASH;
+ break;
+ case LINESTYLE_DASHDOT:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDDASHDOT;
+ break;
+ case LINESTYLE_DASHDOTDOT:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDDASHDOTDOT;
+ break;
+ case LINESTYLE_WAVE:
+ maCurrentFont.meUnderline = LINESTYLE_BOLDWAVE;
+ break;
+ default:
+ ;
+ }
+ }
+ else
+ maCurrentFont.meUnderline = LINESTYLE_BOLD;
+ }
+}
+
+void ScOrcusStyles::set_font_underline_mode(orcus::spreadsheet::underline_mode_t /*e*/)
+{
+}
+
+void ScOrcusStyles::set_font_underline_type(orcus::spreadsheet::underline_type_t e )
+{
+ if (e == orcus::spreadsheet::underline_type_t::double_type)
+ {
+ if (maCurrentFont.meUnderline)
+ {
+ switch (*maCurrentFont.meUnderline)
+ {
+ case LINESTYLE_NONE:
+ case LINESTYLE_SINGLE:
+ maCurrentFont.meUnderline = LINESTYLE_DOUBLE;
+ break;
+ case LINESTYLE_WAVE:
+ maCurrentFont.meUnderline = LINESTYLE_DOUBLEWAVE;
+ break;
+ default:
+ ;
+ }
+ }
+ else
+ maCurrentFont.meUnderline = LINESTYLE_DOUBLE;
+ }
+}
+
+void ScOrcusStyles::set_font_underline_color(orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue)
+{
+ maCurrentFont.maUnderlineColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+void ScOrcusStyles::set_font_color(orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue)
+{
+ maCurrentFont.maColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+void ScOrcusStyles::set_strikethrough_style(orcus::spreadsheet::strikethrough_style_t /*s*/)
+{
+}
+
+void ScOrcusStyles::set_strikethrough_type(orcus::spreadsheet::strikethrough_type_t s)
+{
+ if (maCurrentFont.meStrikeout)
+ {
+ if (*maCurrentFont.meStrikeout == STRIKEOUT_BOLD ||
+ *maCurrentFont.meStrikeout == STRIKEOUT_SLASH ||
+ *maCurrentFont.meStrikeout == STRIKEOUT_X)
+ return;
+ }
+
+ switch (s)
+ {
+ case os::strikethrough_type_t::unknown:
+ maCurrentFont.meStrikeout = STRIKEOUT_DONTKNOW;
+ break;
+ case os::strikethrough_type_t::none:
+ maCurrentFont.meStrikeout = STRIKEOUT_NONE;
+ break;
+ case os::strikethrough_type_t::single:
+ maCurrentFont.meStrikeout = STRIKEOUT_SINGLE;
+ break;
+ case os::strikethrough_type_t::double_type:
+ maCurrentFont.meStrikeout = STRIKEOUT_DOUBLE;
+ break;
+ default:
+ ;
+ }
+}
+
+void ScOrcusStyles::set_strikethrough_width(orcus::spreadsheet::strikethrough_width_t s)
+{
+ switch (s)
+ {
+ case os::strikethrough_width_t::bold:
+ maCurrentFont.meStrikeout = STRIKEOUT_BOLD;
+ break;
+ default:
+ ;
+ }
+}
+
+void ScOrcusStyles::set_strikethrough_text(orcus::spreadsheet::strikethrough_text_t s)
+{
+ switch (s)
+ {
+ case os::strikethrough_text_t::slash:
+ maCurrentFont.meStrikeout = STRIKEOUT_SLASH;
+ break;
+ case os::strikethrough_text_t::cross:
+ maCurrentFont.meStrikeout = STRIKEOUT_X;
+ break;
+ default:
+ ;
+ }
+}
+
+size_t ScOrcusStyles::commit_font()
+{
+ SAL_INFO("sc.orcus.style", "commit font");
+ maFonts.push_back(maCurrentFont);
+ maCurrentFont = ScOrcusStyles::font();
+ return maFonts.size() - 1;
+}
+
+// fill
+
+void ScOrcusStyles::set_fill_count(size_t /*n*/)
+{
+ // needed at all?
+}
+
+void ScOrcusStyles::set_fill_pattern_type(orcus::spreadsheet::fill_pattern_t fp)
+{
+ maCurrentFill.mePattern = fp;
+}
+
+void ScOrcusStyles::set_fill_fg_color(
+ orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red, orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue)
+{
+ maCurrentFill.maFgColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+void ScOrcusStyles::set_fill_bg_color(
+ orcus::spreadsheet::color_elem_t alpha, orcus::spreadsheet::color_elem_t red, orcus::spreadsheet::color_elem_t green, orcus::spreadsheet::color_elem_t blue)
+{
+ maCurrentFill.maBgColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+size_t ScOrcusStyles::commit_fill()
+{
+ SAL_INFO("sc.orcus.style", "commit fill");
+ maFills.push_back(maCurrentFill);
+ maCurrentFill = ScOrcusStyles::fill();
+ return maFills.size() - 1;
+}
+
+// border
+
+void ScOrcusStyles::set_border_count(size_t /*n*/)
+{
+ // needed at all?
+}
+
+void ScOrcusStyles::set_border_style(
+ orcus::spreadsheet::border_direction_t dir, orcus::spreadsheet::border_style_t style)
+{
+ border::border_line& rBorder = maCurrentBorder.maBorders[dir];
+
+ switch (style)
+ {
+ case orcus::spreadsheet::border_style_t::solid:
+ rBorder.meStyle = SvxBorderLineStyle::SOLID;
+ rBorder.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case orcus::spreadsheet::border_style_t::hair:
+ rBorder.meStyle = SvxBorderLineStyle::SOLID;
+ rBorder.mnWidth = oox::xls::API_LINE_HAIR;
+ break;
+ case orcus::spreadsheet::border_style_t::medium:
+ rBorder.meStyle = SvxBorderLineStyle::SOLID;
+ rBorder.mnWidth = oox::xls::API_LINE_MEDIUM;
+ break;
+ case orcus::spreadsheet::border_style_t::thick:
+ rBorder.meStyle = SvxBorderLineStyle::SOLID;
+ rBorder.mnWidth = oox::xls::API_LINE_THICK;
+ break;
+ case orcus::spreadsheet::border_style_t::thin:
+ rBorder.meStyle = SvxBorderLineStyle::SOLID;
+ rBorder.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case orcus::spreadsheet::border_style_t::dash_dot:
+ rBorder.meStyle = SvxBorderLineStyle::DASH_DOT;
+ rBorder.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case orcus::spreadsheet::border_style_t::dash_dot_dot:
+ rBorder.meStyle = SvxBorderLineStyle::DASH_DOT_DOT;
+ rBorder.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case orcus::spreadsheet::border_style_t::dashed:
+ rBorder.meStyle = SvxBorderLineStyle::DASHED;
+ rBorder.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case orcus::spreadsheet::border_style_t::dotted:
+ rBorder.meStyle = SvxBorderLineStyle::DOTTED;
+ rBorder.mnWidth = oox::xls::API_LINE_THIN;
+ break;
+ case orcus::spreadsheet::border_style_t::double_border:
+ rBorder.meStyle = SvxBorderLineStyle::DOUBLE;
+ rBorder.mnWidth = oox::xls::API_LINE_THICK;
+ break;
+ case orcus::spreadsheet::border_style_t::medium_dash_dot:
+ case orcus::spreadsheet::border_style_t::slant_dash_dot:
+ rBorder.meStyle = SvxBorderLineStyle::DASH_DOT;
+ rBorder.mnWidth = oox::xls::API_LINE_MEDIUM;
+ break;
+ case orcus::spreadsheet::border_style_t::medium_dash_dot_dot:
+ rBorder.meStyle = SvxBorderLineStyle::DASH_DOT_DOT;
+ rBorder.mnWidth = oox::xls::API_LINE_MEDIUM;
+ break;
+ case orcus::spreadsheet::border_style_t::medium_dashed:
+ rBorder.meStyle = SvxBorderLineStyle::DASHED;
+ rBorder.mnWidth = oox::xls::API_LINE_MEDIUM;
+ break;
+ case orcus::spreadsheet::border_style_t::unknown:
+ case orcus::spreadsheet::border_style_t::none:
+ rBorder.mnWidth = oox::xls::API_LINE_NONE;
+ break;
+ default:
+ ;
+ }
+}
+
+void ScOrcusStyles::set_border_color(orcus::spreadsheet::border_direction_t dir,
+ orcus::spreadsheet::color_elem_t alpha,
+ orcus::spreadsheet::color_elem_t red,
+ orcus::spreadsheet::color_elem_t green,
+ orcus::spreadsheet::color_elem_t blue)
+{
+ border::border_line& current_line = maCurrentBorder.maBorders[dir];
+ current_line.maColor = Color(ColorAlpha, alpha, red, green, blue);
+}
+
+void ScOrcusStyles::set_border_width(orcus::spreadsheet::border_direction_t dir, double val, orcus::length_unit_t unit )
+{
+ border::border_line& current_line = maCurrentBorder.maBorders[dir];
+ current_line.mnWidth = translateToInternal(val, unit);
+}
+
+size_t ScOrcusStyles::commit_border()
+{
+ SAL_INFO("sc.orcus.style", "commit border");
+ maBorders.push_back(maCurrentBorder);
+ maCurrentBorder = ScOrcusStyles::border();
+ return maBorders.size() - 1;
+}
+
+// cell protection
+void ScOrcusStyles::set_cell_hidden(bool b)
+{
+ maCurrentProtection.mbHidden = b;
+}
+
+void ScOrcusStyles::set_cell_locked(bool b)
+{
+ maCurrentProtection.mbLocked = b;
+}
+
+void ScOrcusStyles::set_cell_print_content(bool b )
+{
+ maCurrentProtection.mbPrintContent = b;
+}
+
+void ScOrcusStyles::set_cell_formula_hidden(bool b )
+{
+ maCurrentProtection.mbFormulaHidden = b;
+}
+
+size_t ScOrcusStyles::commit_cell_protection()
+{
+ SAL_INFO("sc.orcus.style", "commit cell protection");
+ maProtections.push_back(maCurrentProtection);
+ maCurrentProtection = ScOrcusStyles::protection();
+ return maProtections.size() - 1;
+}
+
+void ScOrcusStyles::set_number_format_count(size_t)
+{
+}
+
+void ScOrcusStyles::set_number_format_identifier(size_t)
+{
+}
+
+void ScOrcusStyles::set_number_format_code(std::string_view s)
+{
+ OUString aCode(s.data(), s.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentNumberFormat.maCode = aCode;
+}
+
+size_t ScOrcusStyles::commit_number_format()
+{
+ SAL_INFO("sc.orcus.style", "commit number format");
+ maNumberFormats.push_back(maCurrentNumberFormat);
+ maCurrentNumberFormat = ScOrcusStyles::number_format();
+ return maNumberFormats.size() - 1;
+}
+
+// cell style xf
+
+void ScOrcusStyles::set_cell_style_xf_count(size_t /*n*/)
+{
+ // needed at all?
+}
+
+size_t ScOrcusStyles::commit_cell_style_xf()
+{
+ SAL_INFO("sc.orcus.style", "commit cell style xf");
+ maCellStyleXfs.push_back(maCurrentXF);
+ return maCellStyleXfs.size() - 1;
+}
+
+// cell xf
+
+void ScOrcusStyles::set_cell_xf_count(size_t /*n*/)
+{
+ // needed at all?
+}
+
+size_t ScOrcusStyles::commit_cell_xf()
+{
+ SAL_INFO("sc.orcus.style", "commit cell xf");
+ maCellXfs.push_back(maCurrentXF);
+ return maCellXfs.size() - 1;
+}
+
+// dxf
+
+void ScOrcusStyles::set_dxf_count(size_t /*n*/)
+{
+}
+
+size_t ScOrcusStyles::commit_dxf()
+{
+ return 0;
+}
+
+// xf (cell format) - used both by cell xf and cell style xf.
+
+void ScOrcusStyles::set_xf_number_format(size_t index)
+{
+ maCurrentXF.mnNumberFormatId = index;
+}
+
+void ScOrcusStyles::set_xf_font(size_t index)
+{
+ maCurrentXF.mnFontId = index;
+}
+
+void ScOrcusStyles::set_xf_fill(size_t index)
+{
+ maCurrentXF.mnFillId = index;
+}
+
+void ScOrcusStyles::set_xf_border(size_t index)
+{
+ maCurrentXF.mnBorderId = index;
+}
+
+void ScOrcusStyles::set_xf_protection(size_t index)
+{
+ maCurrentXF.mnProtectionId = index;
+}
+
+void ScOrcusStyles::set_xf_style_xf(size_t index)
+{
+ maCurrentXF.mnStyleXf = index;
+}
+
+void ScOrcusStyles::set_xf_apply_alignment(bool /*b*/)
+{
+}
+
+void ScOrcusStyles::set_xf_horizontal_alignment(orcus::spreadsheet::hor_alignment_t align)
+{
+ switch (align)
+ {
+ case os::hor_alignment_t::left:
+ maCurrentXF.meHorAlignment = SvxCellHorJustify::Left;
+ break;
+ case os::hor_alignment_t::right:
+ maCurrentXF.meHorAlignment = SvxCellHorJustify::Right;
+ break;
+ case os::hor_alignment_t::center:
+ maCurrentXF.meHorAlignment = SvxCellHorJustify::Center;
+ break;
+ case os::hor_alignment_t::justified:
+ maCurrentXF.meHorAlignment = SvxCellHorJustify::Block;
+ break;
+ case os::hor_alignment_t::distributed:
+ maCurrentXF.meHorAlignment = SvxCellHorJustify::Block;
+ maCurrentXF.meHorAlignMethod = SvxCellJustifyMethod::Distribute;
+ break;
+ case os::hor_alignment_t::unknown:
+ maCurrentXF.meHorAlignment = SvxCellHorJustify::Standard;
+ break;
+ default:
+ ;
+ }
+ maCurrentXF.mbAlignment = true;
+}
+
+void ScOrcusStyles::set_xf_vertical_alignment(orcus::spreadsheet::ver_alignment_t align)
+{
+ switch (align)
+ {
+ case os::ver_alignment_t::top:
+ maCurrentXF.meVerAlignment = SvxCellVerJustify::Top;
+ break;
+ case os::ver_alignment_t::bottom:
+ maCurrentXF.meVerAlignment = SvxCellVerJustify::Bottom;
+ break;
+ case os::ver_alignment_t::middle:
+ maCurrentXF.meVerAlignment = SvxCellVerJustify::Center;
+ break;
+ case os::ver_alignment_t::justified:
+ maCurrentXF.meVerAlignment = SvxCellVerJustify::Block;
+ break;
+ case os::ver_alignment_t::distributed:
+ maCurrentXF.meVerAlignment = SvxCellVerJustify::Block;
+ maCurrentXF.meVerAlignMethod = SvxCellJustifyMethod::Distribute;
+ break;
+ case os::ver_alignment_t::unknown:
+ maCurrentXF.meVerAlignment = SvxCellVerJustify::Standard;
+ break;
+ default:
+ ;
+ }
+ maCurrentXF.mbAlignment = true;
+}
+
+// cell style entry
+// not needed for now for gnumeric
+
+void ScOrcusStyles::set_cell_style_count(size_t /*n*/)
+{
+ // needed at all?
+}
+
+void ScOrcusStyles::set_cell_style_name(std::string_view name)
+{
+ OUString aName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentCellStyle.maName = aName;
+}
+
+void ScOrcusStyles::set_cell_style_xf(size_t index)
+{
+ maCurrentCellStyle.mnXFId = index;
+}
+
+void ScOrcusStyles::set_cell_style_builtin(size_t index)
+{
+ // not needed for gnumeric
+ maCurrentCellStyle.mnBuiltInId = index;
+}
+
+void ScOrcusStyles::set_cell_style_parent_name(std::string_view name)
+{
+ const OUString aParentName(name.data(), name.size(), mrFactory.getGlobalSettings().getTextEncoding());
+ maCurrentCellStyle.maParentName = aParentName;
+}
+
+size_t ScOrcusStyles::commit_cell_style()
+{
+ SAL_INFO("sc.orcus.style", "commit cell style: " << maCurrentCellStyle.maName);
+ if (maCurrentCellStyle.mnXFId >= maCellStyleXfs.size())
+ {
+ SAL_WARN("sc.orcus.style", "invalid xf id for commit cell style");
+ return 0;
+ }
+ if (maCurrentCellStyle.mnXFId == 0)
+ {
+ return 0;
+ }
+
+ ScStyleSheetPool* pPool = mrFactory.getDoc().getDoc().GetStyleSheetPool();
+ SfxStyleSheetBase& rBase = pPool->Make(maCurrentCellStyle.maName, SfxStyleFamily::Para);
+ // Need to convert the parent name to localized UI name, see tdf#139205.
+ rBase.SetParent(ScStyleNameConversion::ProgrammaticToDisplayName(maCurrentCellStyle.maParentName,
+ SfxStyleFamily::Para));
+ SfxItemSet& rSet = rBase.GetItemSet();
+
+ xf& rXf = maCellStyleXfs[maCurrentCellStyle.mnXFId];
+ applyXfToItemSet(rSet, rXf);
+
+ maCurrentXF = ScOrcusStyles::xf();
+ maCurrentCellStyle = ScOrcusStyles::cell_style();
+
+ return 0;
+}
+
+// auto filter import
+
+ScOrcusAutoFilter::ScOrcusAutoFilter( const ScOrcusGlobalSettings& rGS ) :
+ mrGlobalSettings(rGS)
+{
+}
+
+ScOrcusAutoFilter::~ScOrcusAutoFilter()
+{
+}
+
+void ScOrcusAutoFilter::set_range(const orcus::spreadsheet::range_t& range)
+{
+ maRange.aStart.SetRow(range.first.row);
+ maRange.aStart.SetCol(range.first.column);
+ maRange.aEnd.SetRow(range.last.row);
+ maRange.aEnd.SetCol(range.last.column);
+}
+
+void ScOrcusAutoFilter::set_column(orcus::spreadsheet::col_t col)
+{
+ SAL_INFO("sc.orcus.autofilter", "set_column: " << col);
+}
+
+void ScOrcusAutoFilter::append_column_match_value(std::string_view value)
+{
+ OUString aString(value.data(), value.size(), mrGlobalSettings.getTextEncoding());
+ SAL_INFO("sc.orcus.autofilter", "append_column_match_value: " << aString);
+}
+
+void ScOrcusAutoFilter::commit_column()
+{
+ SAL_INFO("sc.orcus.autofilter", "commit column");
+}
+
+void ScOrcusAutoFilter::commit()
+{
+ SAL_INFO("sc.orcus.autofilter", "commit");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/orcus/orcusfiltersimpl.cxx b/sc/source/filter/orcus/orcusfiltersimpl.cxx
new file mode 100644
index 000000000..e7fdb44ca
--- /dev/null
+++ b/sc/source/filter/orcus/orcusfiltersimpl.cxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <orcusfiltersimpl.hxx>
+#include <orcusinterface.hxx>
+#include <tokenarray.hxx>
+
+#include <osl/thread.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/itemset.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <orcus/orcus_csv.hpp>
+#include <orcus/orcus_gnumeric.hpp>
+#include <orcus/orcus_xlsx.hpp>
+#include <orcus/orcus_xls_xml.hpp>
+#include <orcus/orcus_ods.hpp>
+#include <orcus/orcus_import_ods.hpp>
+#include <orcus/stream.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+using namespace com::sun::star;
+
+namespace
+{
+uno::Reference<task::XStatusIndicator> getStatusIndicator(const SfxMedium& rMedium)
+{
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+ SfxItemSet* pSet = rMedium.GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = pSet->GetItem<SfxUnoAnyItem>(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ xStatusIndicator.set(pItem->GetValue(), uno::UNO_QUERY);
+ }
+ return xStatusIndicator;
+}
+
+bool loadFileContent(SfxMedium& rMedium, orcus::iface::import_filter& filter)
+{
+ SvStream* pStream = rMedium.GetInStream();
+ pStream->Seek(0);
+ static const size_t nReadBuffer = 1024 * 32;
+ OStringBuffer aBuffer((int(nReadBuffer)));
+ size_t nRead = 0;
+ do
+ {
+ char pData[nReadBuffer];
+ nRead = pStream->ReadBytes(pData, nReadBuffer);
+ aBuffer.append(pData, nRead);
+ } while (nRead == nReadBuffer);
+
+ try
+ {
+ filter.read_stream(aBuffer);
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("sc", "Unable to load file via orcus filter! " << e.what());
+ return false;
+ }
+
+ return true;
+}
+}
+
+bool ScOrcusFiltersImpl::importCSV(ScDocument& rDoc, SfxMedium& rMedium) const
+{
+ ScOrcusFactory aFactory(rDoc);
+ aFactory.setStatusIndicator(getStatusIndicator(rMedium));
+
+ orcus::orcus_csv filter(&aFactory);
+ return loadFileContent(rMedium, filter);
+}
+
+bool ScOrcusFiltersImpl::importGnumeric(ScDocument& rDoc, SfxMedium& rMedium) const
+{
+ ScOrcusFactory aFactory(rDoc);
+ aFactory.setStatusIndicator(getStatusIndicator(rMedium));
+
+ orcus::orcus_gnumeric filter(&aFactory);
+ return loadFileContent(rMedium, filter);
+}
+
+bool ScOrcusFiltersImpl::importExcel2003XML(ScDocument& rDoc, SfxMedium& rMedium) const
+{
+ ScOrcusFactory aFactory(rDoc);
+ aFactory.setStatusIndicator(getStatusIndicator(rMedium));
+
+ orcus::orcus_xls_xml filter(&aFactory);
+ return loadFileContent(rMedium, filter);
+}
+
+bool ScOrcusFiltersImpl::importXLSX(ScDocument& rDoc, SfxMedium& rMedium) const
+{
+ ScOrcusFactory aFactory(rDoc);
+ aFactory.setStatusIndicator(getStatusIndicator(rMedium));
+
+ orcus::orcus_xlsx filter(&aFactory);
+ return loadFileContent(rMedium, filter);
+}
+
+bool ScOrcusFiltersImpl::importODS(ScDocument& rDoc, SfxMedium& rMedium) const
+{
+ ScOrcusFactory aFactory(rDoc);
+ aFactory.setStatusIndicator(getStatusIndicator(rMedium));
+
+ orcus::orcus_ods filter(&aFactory);
+ return loadFileContent(rMedium, filter);
+}
+
+bool ScOrcusFiltersImpl::importODS_Styles(ScDocument& rDoc, OUString& aPath) const
+{
+ try
+ {
+#if defined _WIN32
+ OString aPath8 = OUStringToOString(aPath, RTL_TEXTENCODING_UTF8);
+#else
+ OString aPath8 = OUStringToOString(aPath, osl_getThreadTextEncoding());
+#endif
+ orcus::file_content content(aPath8);
+ ScOrcusFactory aFactory(rDoc);
+ ScOrcusStyles aStyles(aFactory);
+ orcus::import_ods::read_styles(content.str(), &aStyles);
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("sc", "Unable to load styles from xml file! " << e.what());
+ return false;
+ }
+
+ return true;
+}
+
+std::unique_ptr<ScOrcusXMLContext> ScOrcusFiltersImpl::createXMLContext(ScDocument& rDoc,
+ const OUString& rPath) const
+{
+ return std::make_unique<ScOrcusXMLContextImpl>(rDoc, rPath);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/orcus/xmlcontext.cxx b/sc/source/filter/orcus/xmlcontext.cxx
new file mode 100644
index 000000000..94ef50159
--- /dev/null
+++ b/sc/source/filter/orcus/xmlcontext.cxx
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <orcusfiltersimpl.hxx>
+#include <orcusinterface.hxx>
+#include <orcusxml.hxx>
+#include <document.hxx>
+#include <tokenarray.hxx>
+
+#include <vcl/weld.hxx>
+#include <ucbhelper/content.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+
+#include <orcus/xml_structure_tree.hpp>
+#include <orcus/xml_namespace.hpp>
+#include <orcus/orcus_xml.hpp>
+#include <orcus/sax_parser_base.hpp>
+#include <orcus/stream.hpp>
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include <string>
+#include <sstream>
+
+namespace com::sun::star::ucb { class XCommandEnvironment; }
+
+#define BUFFER_SIZE 4096
+
+using namespace com::sun::star;
+
+namespace {
+
+ScOrcusXMLTreeParam::EntryData& setUserDataToEntry(weld::TreeView& rControl,
+ const weld::TreeIter& rEntry, ScOrcusXMLTreeParam::UserDataStoreType& rStore, ScOrcusXMLTreeParam::EntryType eType)
+{
+ rStore.push_back(std::make_unique<ScOrcusXMLTreeParam::EntryData>(eType));
+ rControl.set_id(rEntry, weld::toId(rStore.back().get()));
+ return *rStore.back();
+}
+
+void setEntityNameToUserData(
+ ScOrcusXMLTreeParam::EntryData& rEntryData,
+ const orcus::xml_structure_tree::entity_name& entity, const orcus::xml_structure_tree::walker& walker)
+{
+ rEntryData.mnNamespaceID = walker.get_xmlns_index(entity.ns);
+}
+
+OUString toString(const orcus::xml_structure_tree::entity_name& entity, const orcus::xml_structure_tree::walker& walker)
+{
+ OUStringBuffer aBuf;
+ if (entity.ns)
+ {
+ // Namespace exists. Use the short version of the xml namespace name for display.
+ std::string aShortName = walker.get_xmlns_short_name(entity.ns);
+ aBuf.appendAscii(aShortName.c_str());
+ aBuf.append(':');
+ }
+ aBuf.append(OUString(entity.name.data(), entity.name.size(), RTL_TEXTENCODING_UTF8));
+ return aBuf.makeStringAndClear();
+}
+
+void populateTree(
+ weld::TreeView& rTreeCtrl, orcus::xml_structure_tree::walker& rWalker,
+ const orcus::xml_structure_tree::entity_name& rElemName, bool bRepeat,
+ const weld::TreeIter* pParent, ScOrcusXMLTreeParam& rParam)
+{
+ OUString sEntry(toString(rElemName, rWalker));
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeCtrl.make_iterator());
+ rTreeCtrl.insert(pParent, -1, &sEntry, nullptr, nullptr, nullptr, false, xEntry.get());
+ rTreeCtrl.set_image(*xEntry, rParam.maImgElementDefault, -1);
+
+ ScOrcusXMLTreeParam::EntryData& rEntryData = setUserDataToEntry(rTreeCtrl,
+ *xEntry, rParam.m_UserDataStore,
+ bRepeat ? ScOrcusXMLTreeParam::ElementRepeat : ScOrcusXMLTreeParam::ElementDefault);
+
+ setEntityNameToUserData(rEntryData, rElemName, rWalker);
+
+ if (bRepeat)
+ {
+ // Recurring elements use different icon.
+ rTreeCtrl.set_image(*xEntry, rParam.maImgElementRepeat, -1);
+ }
+
+ orcus::xml_structure_tree::entity_names_type aNames = rWalker.get_attributes();
+
+ // Insert attributes.
+ for (const orcus::xml_structure_tree::entity_name& rAttrName : aNames)
+ {
+ OUString sAttr(toString(rAttrName, rWalker));
+ std::unique_ptr<weld::TreeIter> xAttr(rTreeCtrl.make_iterator());
+ rTreeCtrl.insert(xEntry.get(), -1, &sAttr, nullptr, nullptr, nullptr, false, xAttr.get());
+
+ ScOrcusXMLTreeParam::EntryData& rAttrData =
+ setUserDataToEntry(rTreeCtrl, *xAttr, rParam.m_UserDataStore, ScOrcusXMLTreeParam::Attribute);
+ setEntityNameToUserData(rAttrData, rAttrName, rWalker);
+
+ rTreeCtrl.set_image(*xAttr, rParam.maImgAttribute, -1);
+ }
+
+ aNames = rWalker.get_children();
+
+ // Non-leaf if it has child elements, leaf otherwise.
+ rEntryData.mbLeafNode = aNames.empty();
+
+ // Insert child elements recursively.
+ for (const auto& rName : aNames)
+ {
+ orcus::xml_structure_tree::element aElem = rWalker.descend(rName);
+ populateTree(rTreeCtrl, rWalker, rName, aElem.repeat, xEntry.get(), rParam);
+ rWalker.ascend();
+ }
+}
+
+class TreeUpdateSwitch
+{
+ weld::TreeView& mrTreeCtrl;
+public:
+ explicit TreeUpdateSwitch(weld::TreeView& rTreeCtrl) : mrTreeCtrl(rTreeCtrl)
+ {
+ mrTreeCtrl.freeze();
+ }
+
+ ~TreeUpdateSwitch()
+ {
+ mrTreeCtrl.thaw();
+ }
+};
+
+void loadContentFromURL(const OUString& rURL, std::string& rStrm)
+{
+ ucbhelper::Content aContent(
+ rURL, uno::Reference<ucb::XCommandEnvironment>(), comphelper::getProcessComponentContext());
+ uno::Reference<io::XInputStream> xStrm = aContent.openStream();
+
+ std::ostringstream aStrmBuf;
+ uno::Sequence<sal_Int8> aBytes;
+ size_t nBytesRead = 0;
+ do
+ {
+ nBytesRead = xStrm->readBytes(aBytes, BUFFER_SIZE);
+ const sal_Int8* p = aBytes.getConstArray();
+ aStrmBuf << std::string(p, p + nBytesRead);
+ }
+ while (nBytesRead == BUFFER_SIZE);
+
+ rStrm = aStrmBuf.str();
+}
+
+}
+
+ScOrcusXMLContextImpl::ScOrcusXMLContextImpl(ScDocument& rDoc, const OUString& rPath) :
+ ScOrcusXMLContext(), mrDoc(rDoc), maPath(rPath) {}
+
+ScOrcusXMLContextImpl::~ScOrcusXMLContextImpl() {}
+
+void ScOrcusXMLContextImpl::loadXMLStructure(weld::TreeView& rTreeCtrl, ScOrcusXMLTreeParam& rParam)
+{
+ rParam.m_UserDataStore.clear();
+
+ std::string aStrm;
+ loadContentFromURL(maPath, aStrm);
+
+ if (aStrm.empty())
+ return;
+
+ orcus::xmlns_context cxt = maNsRepo.create_context();
+ orcus::xml_structure_tree aXmlTree(cxt);
+ try
+ {
+ aXmlTree.parse(aStrm);
+
+ TreeUpdateSwitch aSwitch(rTreeCtrl);
+ rTreeCtrl.clear();
+
+ orcus::xml_structure_tree::walker aWalker = aXmlTree.get_walker();
+
+ // Root element.
+ orcus::xml_structure_tree::element aElem = aWalker.root();
+ populateTree(rTreeCtrl, aWalker, aElem.name, aElem.repeat, nullptr, rParam);
+ }
+ catch (const orcus::sax::malformed_xml_error& e)
+ {
+ SAL_WARN("sc.orcus", "Malformed XML error: " << e.what());
+ }
+ catch (const std::exception& e)
+ {
+ SAL_WARN("sc.orcus", "parsing failed with an unknown error " << e.what());
+ }
+
+ rTreeCtrl.all_foreach([&rTreeCtrl](weld::TreeIter& rEntry){
+ rTreeCtrl.expand_row(rEntry);
+ return false;
+ });
+}
+
+namespace {
+
+class SetNamespaceAlias
+{
+ orcus::orcus_xml& mrFilter;
+ orcus::xmlns_repository& mrNsRepo;
+public:
+ SetNamespaceAlias(orcus::orcus_xml& filter, orcus::xmlns_repository& repo) :
+ mrFilter(filter), mrNsRepo(repo) {}
+
+ void operator() (size_t index)
+ {
+ orcus::xmlns_id_t nsid = mrNsRepo.get_identifier(index);
+ if (nsid == orcus::XMLNS_UNKNOWN_ID)
+ return;
+
+ std::string alias = mrNsRepo.get_short_name(index);
+ mrFilter.set_namespace_alias(alias.c_str(), nsid);
+ }
+};
+
+}
+
+void ScOrcusXMLContextImpl::importXML(const ScOrcusImportXMLParam& rParam)
+{
+ ScOrcusFactory aFactory(mrDoc, true);
+
+ OUString aSysPath;
+ if (osl::FileBase::getSystemPathFromFileURL(maPath, aSysPath) != osl::FileBase::E_None)
+ return;
+
+ OString aOSysPath = OUStringToOString(aSysPath, RTL_TEXTENCODING_UTF8);
+ const char* path = aOSysPath.getStr();
+
+ try
+ {
+ orcus::orcus_xml filter(maNsRepo, &aFactory, nullptr);
+
+ // Define all used namespaces.
+ std::for_each(rParam.maNamespaces.begin(), rParam.maNamespaces.end(), SetNamespaceAlias(filter, maNsRepo));
+
+ // Set cell links.
+ for (const ScOrcusImportXMLParam::CellLink& rLink : rParam.maCellLinks)
+ {
+ OUString aTabName;
+ mrDoc.GetName(rLink.maPos.Tab(), aTabName);
+ filter.set_cell_link(
+ rLink.maPath.getStr(),
+ OUStringToOString(aTabName, RTL_TEXTENCODING_UTF8).getStr(),
+ rLink.maPos.Row(), rLink.maPos.Col());
+ }
+
+ // Set range links.
+ for (const ScOrcusImportXMLParam::RangeLink& rLink : rParam.maRangeLinks)
+ {
+ OUString aTabName;
+ mrDoc.GetName(rLink.maPos.Tab(), aTabName);
+ filter.start_range(
+ OUStringToOString(aTabName, RTL_TEXTENCODING_UTF8).getStr(),
+ rLink.maPos.Row(), rLink.maPos.Col());
+
+ std::for_each(rLink.maFieldPaths.begin(), rLink.maFieldPaths.end(),
+ [&filter](const OString& rFieldPath)
+ {
+ filter.append_field_link(rFieldPath, std::string_view());
+ }
+ );
+
+ std::for_each(rLink.maRowGroups.begin(), rLink.maRowGroups.end(),
+ [&filter] (const OString& rRowGroup)
+ {
+ filter.set_range_row_group(rRowGroup);
+ }
+ );
+
+ filter.commit_range();
+ }
+
+ orcus::file_content content(path);
+ filter.read_stream(content.str());
+
+ aFactory.finalize();
+ }
+ catch (const std::exception&)
+ {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/qpro/README b/sc/source/filter/qpro/README
new file mode 100644
index 000000000..9f49f7057
--- /dev/null
+++ b/sc/source/filter/qpro/README
@@ -0,0 +1,4 @@
+Quattro Pro filter
+
+Old spec files can be found at:
+http://web.archive.org/web/20021020204833/http://www.corel.com/partners_developers/ds/co32sdk/docs/qp7/qpf1copy.htm
diff --git a/sc/source/filter/qpro/qpro.cxx b/sc/source/filter/qpro/qpro.cxx
new file mode 100644
index 000000000..5ee451af9
--- /dev/null
+++ b/sc/source/filter/qpro/qpro.cxx
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <qproform.hxx>
+#include <qpro.hxx>
+#include <qprostyle.hxx>
+
+#include <scerrors.hxx>
+#include <ftools.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include <tools/stream.hxx>
+#include <unotools/configmgr.hxx>
+#include <docoptio.hxx>
+#include <scdll.hxx>
+#include <memory>
+
+ErrCode ScQProReader::readSheet( SCTAB nTab, ScDocument& rDoc, ScQProStyle *pStyle )
+{
+ ErrCode eRet = ERRCODE_NONE;
+ sal_uInt8 nCol, nDummy;
+ sal_uInt16 nRow;
+ sal_uInt16 nStyle;
+ bool bEndOfSheet = false;
+
+ SAL_INFO("sc", "Read sheet " << nTab);
+
+ while( ERRCODE_NONE == eRet && !bEndOfSheet && nextRecord() )
+ {
+ switch( getId() )
+ {
+ case 0x000f:{ // Label cell
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle ).ReadUChar( nDummy );
+ sal_uInt16 nLen = getLength();
+ if (!mpStream->good() || nLen < 7)
+ eRet = SCERR_IMPORT_FORMAT;
+ else
+ {
+ OUString aLabel(readString(nLen - 7));
+ nStyle = nStyle >> 3;
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ rDoc.EnsureTable(nTab);
+ rDoc.SetTextCell(ScAddress(nCol,nRow,nTab), aLabel);
+ }
+ }
+ break;
+
+ case 0x00cb: // End of sheet
+ bEndOfSheet = true;
+ break;
+
+ case 0x000c: // Blank cell
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle );
+ if (!mpStream->good())
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ break;
+ }
+ nStyle = nStyle >> 3;
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ break;
+
+ case 0x000d:{ // Integer cell
+ sal_Int16 nValue;
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle ).ReadInt16( nValue );
+ if (!mpStream->good())
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ break;
+ }
+ nStyle = nStyle >> 3;
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ rDoc.EnsureTable(nTab);
+ rDoc.SetValue(ScAddress(nCol,nRow,nTab), static_cast<double>(nValue));
+ }
+ break;
+
+ case 0x000e:{ // Floating point cell
+ double nValue;
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle ).ReadDouble( nValue );
+ if (!mpStream->good())
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ break;
+ }
+ nStyle = nStyle >> 3;
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ rDoc.EnsureTable(nTab);
+ rDoc.SetValue(ScAddress(nCol,nRow,nTab), nValue);
+ }
+ break;
+
+ case 0x0010:
+ {
+ // Formula cell
+ double nValue;
+ sal_uInt16 nState, nLen;
+ mpStream->ReadUChar( nCol ).ReadUChar( nDummy ).ReadUInt16( nRow ).ReadUInt16( nStyle ).ReadDouble( nValue ).ReadUInt16( nState ).ReadUInt16( nLen );
+ if (!mpStream->good())
+ {
+ eRet = SCERR_IMPORT_FORMAT;
+ break;
+ }
+ ScAddress aAddr( nCol, nRow, nTab );
+ std::unique_ptr<ScTokenArray> pArray;
+
+ QProToSc aConv(*mpStream, rDoc.GetSharedStringPool(), aAddr);
+ if (ConvErr::OK != aConv.Convert( rDoc, pArray ))
+ eRet = SCERR_IMPORT_FORMAT;
+ else
+ {
+ ScFormulaCell* pFormula = new ScFormulaCell(rDoc, aAddr, std::move(pArray));
+ nStyle = nStyle >> 3;
+ pFormula->AddRecalcMode( ScRecalcMode::ONLOAD_ONCE );
+ pStyle->SetFormat( &rDoc, nCol, nRow, nTab, nStyle );
+ rDoc.EnsureTable(nTab);
+ rDoc.SetFormulaCell(ScAddress(nCol,nRow,nTab), pFormula);
+ }
+ }
+ break;
+ }
+ }
+ return eRet;
+}
+
+ErrCode ScFormatFilterPluginImpl::ScImportQuattroPro(SvStream *pStream, ScDocument& rDoc)
+{
+ ScQProReader aReader(pStream);
+ ErrCode eRet = aReader.import(rDoc);
+ return eRet;
+}
+
+ScQProReader::ScQProReader(SvStream* pStream)
+ : mnId(0)
+ , mnLength(0)
+ , mnOffset(0)
+ , mpStream(pStream)
+ , mbEndOfFile(false)
+ , mnMaxTab(utl::ConfigManager::IsFuzzing() ? 128 : MAXTAB)
+{
+ if( mpStream )
+ {
+ mpStream->SetBufferSize( 65535 );
+ mpStream->SetStreamCharSet( RTL_TEXTENCODING_MS_1252 );
+ }
+}
+
+ScQProReader::~ScQProReader()
+{
+ if( mpStream )
+ mpStream->SetBufferSize( 0 );
+}
+
+ErrCode ScQProReader::parse(ScDocument& rDoc)
+{
+ ErrCode eRet = ERRCODE_NONE;
+ sal_uInt16 nVersion;
+ sal_uInt16 i = 1, j = 1;
+ SCTAB nTab = 0;
+ SetEof( false );
+
+ if( !recordsLeft() )
+ return SCERR_IMPORT_OPEN;
+
+ std::unique_ptr<ScQProStyle> pStyleElement( new ScQProStyle );
+
+ while( nextRecord() && eRet == ERRCODE_NONE)
+ {
+ switch( getId() )
+ {
+ case 0x0000: // Beginning of file
+ mpStream->ReadUInt16( nVersion );
+ break;
+
+ case 0x00ca: // Beginning of sheet
+ if (nTab <= mnMaxTab)
+ {
+ if( nTab < 26 )
+ {
+ OUString aName = OUStringChar( sal_Unicode('A' + nTab) );
+ if (!nTab)
+ rDoc.RenameTab( nTab, aName );
+ else
+ rDoc.InsertTab( nTab, aName );
+ }
+ eRet = readSheet( nTab, rDoc, pStyleElement.get() );
+ nTab++;
+ }
+ break;
+
+ case 0x0001: // End of file
+ SetEof( true );
+ break;
+
+ case 0x00ce:{ // Attribute cell
+ sal_uInt8 nFormat, nAlign, nFont;
+ sal_Int16 nColor;
+ mpStream->ReadUChar( nFormat ).ReadUChar( nAlign ).ReadInt16( nColor ).ReadUChar( nFont );
+ pStyleElement->setAlign( i, nAlign );
+ pStyleElement->setFont( i, nFont );
+ i++;
+ }
+ break;
+
+ case 0x00cf:{ // Font description
+ sal_uInt16 nPtSize, nFontAttr;
+ OUString aLabel;
+ mpStream->ReadUInt16( nPtSize ).ReadUInt16( nFontAttr );
+ pStyleElement->setFontRecord( j, nFontAttr, nPtSize );
+ sal_uInt16 nLen = getLength();
+ if (nLen >= 4)
+ aLabel = readString(nLen - 4);
+ else
+ eRet = SCERR_IMPORT_FORMAT;
+ pStyleElement->setFontType( j, aLabel );
+ j++;
+ }
+ break;
+ }
+ }
+ return eRet;
+}
+
+ErrCode ScQProReader::import( ScDocument& rDoc)
+{
+ ErrCode eRet = parse(rDoc);
+ rDoc.CalcAfterLoad();
+ return eRet;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportQPW(SvStream &rStream)
+{
+ ScDLL::Init();
+ ScDocument aDocument;
+ ScDocOptions aDocOpt = aDocument.GetDocOptions();
+ aDocOpt.SetLookUpColRowNames(false);
+ aDocument.SetDocOptions(aDocOpt);
+ aDocument.MakeTable(0);
+ aDocument.EnableExecuteLink(false);
+ aDocument.SetInsertingFromOtherDoc(true);
+ aDocument.SetImportingXML(true);
+ aDocument.SetHardRecalcState(ScDocument::HardRecalcState::ETERNAL);
+
+ ScQProReader aReader(&rStream);
+ ErrCode eRet = aReader.parse(aDocument);
+ return eRet == ERRCODE_NONE;
+}
+
+bool ScQProReader::recordsLeft()
+{
+ return mpStream && mpStream->good();
+}
+
+bool ScQProReader::nextRecord()
+{
+ if( !recordsLeft() )
+ return false;
+
+ if( mbEndOfFile )
+ return false;
+
+ sal_uInt32 nPos = mpStream->Tell();
+ if( nPos != mnOffset + mnLength )
+ mpStream->Seek( mnOffset + mnLength );
+
+ mnLength = mnId = 0;
+ mpStream->ReadUInt16( mnId ).ReadUInt16( mnLength );
+
+ mnOffset = mpStream->Tell();
+#ifdef DEBUG_SC_QPRO
+ fprintf( stderr, "Read record 0x%x length 0x%x at offset 0x%x\n",
+ (unsigned)mnId, (unsigned)mnLength, (unsigned)mnOffset );
+
+#if 1 // rather verbose
+ int len = mnLength;
+ while (len > 0) {
+ int i, chunk = std::min(len, 16);
+ unsigned char data[16];
+ mpStream->Read( data, chunk );
+
+ for (i = 0; i < chunk; i++)
+ fprintf( stderr, "%.2x ", data[i] );
+ fprintf( stderr, "| " );
+ for (i = 0; i < chunk; i++)
+ fprintf( stderr, "%c", data[i] < 127 && data[i] > 30 ? data[i] : '.' );
+ fprintf( stderr, "\n" );
+
+ len -= chunk;
+ }
+ mpStream->Seek( mnOffset );
+#endif
+#endif
+ return true;
+}
+
+OUString ScQProReader::readString(sal_uInt16 nLength)
+{
+ return read_uInt8s_ToOUString(*mpStream, nLength, mpStream->GetStreamCharSet());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/qpro/qproform.cxx b/sc/source/filter/qpro/qproform.cxx
new file mode 100644
index 000000000..9ece7da03
--- /dev/null
+++ b/sc/source/filter/qpro/qproform.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+
+#include <qproform.hxx>
+#include <formel.hxx>
+#include <tokstack.hxx>
+
+void QProToSc::ReadSRD( const ScDocument& rDoc, ScSingleRefData& rSRD, sal_Int8 nPage, sal_Int8 nCol, sal_uInt16 nRelBit )
+{
+ sal_uInt16 nTmp = nRelBit & 0x1fff;
+ rSRD.InitAddress( ScAddress( nCol, (~nTmp + 1), 0 ) );
+ if( nRelBit & 0x4000 )
+ {
+ rSRD.SetRelCol(nCol);
+ }
+ else
+ {
+ rSRD.SetAbsCol(nCol);
+ }
+
+ if( nRelBit & 0x2000 )
+ {
+ SCROW nRelRow = static_cast<sal_Int16>(nTmp << 3); // This looks weird... Mistake?
+ nRelRow /= 8;
+ rSRD.SetRelRow(nRelRow);
+ }
+ else
+ {
+ rSRD.SetAbsRow(nTmp);
+ }
+ if( nRelBit & 0x8000 )
+ {
+ rSRD.SetRelTab(nPage);
+ }
+ else
+ {
+ rSRD.SetAbsTab(nPage);
+ }
+ if (rSRD.toAbs(rDoc, aEingPos).Tab() != aEingPos.Tab())
+ rSRD.SetFlag3D(true);
+}
+
+QProToSc::QProToSc( SvStream& rStream, svl::SharedStringPool& rSPool, const ScAddress& rRefPos ) :
+ ConverterBase(rSPool),
+ maIn( rStream )
+{
+ aEingPos = rRefPos;
+}
+
+void QProToSc::DoFunc( DefTokenId eOc, sal_uInt16 nArgs, const char* pExtString )
+{
+ TokenId eParam[ nBufSize ];
+ sal_Int32 nCount;
+ TokenId nPush;
+
+ bool bAddIn = false;
+
+ if( eOc == ocNoName )
+ {
+ bAddIn = true;
+ if( pExtString )
+ {
+ OString s = OString::Concat("QPRO_") + pExtString;
+ nPush = aPool.Store(eOc, OStringToOUString(s, maIn.GetStreamCharSet()));
+ aPool << nPush;
+ }
+ else
+ aPool << ocNoName;
+ }
+
+ if( nArgs < nBufSize )
+ {
+ for( nCount = 0; nCount < nArgs && aStack.HasMoreTokens() ; nCount++ )
+ aStack >> eParam[ nCount ];
+
+ if (nCount < nArgs)
+ // Adapt count to reality. All sort of binary crap is possible.
+ nArgs = static_cast<sal_uInt16>(nCount);
+ }
+ else
+ return;
+
+ switch( eOc )
+ {
+ case ocIndex:
+ nPush = eParam[ 0 ];
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = nPush;
+ IncToken( eParam[ 0 ] );
+ IncToken( eParam[ 1 ] );
+ break;
+
+ case ocIRR:
+ nPush = eParam[ 0 ];
+ eParam[ 0 ] = eParam[ 1 ];
+ eParam[ 1 ] = nPush;
+ break;
+
+ case ocGetYear:
+ nPush = aPool.Store( 1900.0 );
+ aPool << ocOpen;
+ break;
+
+ default:
+ break;
+ }
+
+ if( !bAddIn )
+ aPool << eOc;
+
+ aPool << ocOpen;
+
+ if( nArgs> 0 )
+ {
+ if( eOc == ocRRI )
+ {
+ // There should be at least 3 arguments, but with binary crap may not...
+ SAL_WARN_IF( nArgs < 3, "sc.filter","QProToSc::DoFunc - ocRRI expects 3 parameters but got " << nArgs);
+ // Store first 3 parameters to pool in order 2,1,0
+ if (nArgs > 3)
+ nArgs = 3;
+ }
+ else if( eOc == ocIpmt )
+ {
+ // There should be at least 4 arguments, but with binary crap may not...
+ SAL_WARN_IF( nArgs < 4, "sc.filter","QProToSc::DoFunc - ocIpmt expects 4 parameters but got " << nArgs);
+ // Store first 4 parameters to pool in order 3,2,1,0
+ if (nArgs > 4)
+ nArgs = 4;
+ }
+
+ sal_Int16 nLast = nArgs - 1;
+ aPool << eParam[ nLast ];
+ for( nCount = nLast - 1 ; nCount >= 0 ; nCount-- )
+ {
+ aPool << ocSep << eParam[ nCount ];
+ }
+ }
+
+ if( eOc == ocGetYear )
+ aPool << ocClose << ocSub << nPush;
+ else if( eOc == ocFixed )
+ aPool << ocSep << ocTrue << ocOpen << ocClose;
+
+ aPool << ocClose;
+ aPool >> aStack;
+}
+
+void QProToSc::IncToken( TokenId &rParam )
+{
+ aPool << ocOpen << rParam << mnAddToken;
+ rParam = aPool.Store();
+}
+
+#define SAFEDEC_OR_RET(nRef, amt, ret) \
+do { \
+ if (nRef < amt)\
+ return ret; \
+ nRef-=amt; \
+} while(false)
+
+#define SAFEREAD_OR_BREAK( aStream, i, nRef, eRet, ret ) \
+ if (!aStream.good()) \
+ { \
+ i = nRef-1; /* will be incremented at end of while */ \
+ eRet = ret; \
+ break; /* switch */ \
+ }
+
+ConvErr QProToSc::Convert( const ScDocument& rDoc, std::unique_ptr<ScTokenArray>& pArray )
+{
+ sal_uInt8 nFmla[ nBufSize ] = {0};
+ sal_uInt8 nArgArray[ nBufSize ] = {0};
+ sal_Int8 nCol, nPage;
+ sal_uInt16 nIntCount = 0, nStringCount = 0, nFloatCount = 0, nDLLCount = 0, nArgCount = 0;
+ sal_uInt16 nIntArray[ nBufSize ] = {0};
+ OUString sStringArray[ nBufSize ];
+ sal_uInt16 nDLLArray[ nBufSize ] = {0};
+ sal_uInt16 nNote, nRelBits;
+ TokenId nPush;
+ ScComplexRefData aCRD;
+ ScSingleRefData aSRD;
+ QPRO_FUNC_TYPE eType;
+ DefTokenId eOc;
+ double nFloatArray[ nBufSize ] = {0};
+ const char* pExtString = nullptr;
+
+ aCRD.InitFlags();
+ aSRD.InitFlags();
+ sal_uInt16 nRef = 0;
+ maIn.ReadUInt16( nRef );
+
+ if( nRef < nBufSize )
+ {
+ for( sal_uInt16 i=0; i < nRef; i++)
+ {
+ maIn.ReadUChar( nFmla[i] );
+
+ if( nFmla[ i ] == 0x05 )
+ {
+ sal_uInt16 nInt = 0;
+ maIn.ReadUInt16( nInt );
+ nIntArray[ nIntCount ] = nInt;
+ SAFEDEC_OR_RET(nRef, 2, ConvErr::Count);
+ nIntCount++;
+ }
+
+ if( nFmla[ i ] == 0x00 )
+ {
+ double nFloat = 0;
+ maIn.ReadDouble( nFloat );
+ nFloatArray[ nFloatCount ] = nFloat;
+ SAFEDEC_OR_RET(nRef, 8, ConvErr::Count);
+ nFloatCount++;
+ }
+
+ if( nFmla[ i ] == 0x1a )
+ {
+ sal_uInt8 nArg = 0;
+ sal_uInt16 nDummy, nDLLId = 0;
+ maIn.ReadUChar( nArg ).ReadUInt16( nDummy ).ReadUInt16( nDLLId );
+ nArgArray[ nArgCount ] = nArg;
+ nDLLArray[ nDLLCount ] = nDLLId;
+ SAFEDEC_OR_RET(nRef, 5, ConvErr::Count);
+ nDLLCount++;
+ nArgCount++;
+ }
+ if( nFmla[ i ] == 0x06 )
+ {
+ OUString aTmp(::read_zeroTerminated_uInt8s_ToOUString(maIn, maIn.GetStreamCharSet()));
+ sStringArray[ nStringCount ] = aTmp;
+ nStringCount++;
+ SAFEDEC_OR_RET(nRef, aTmp.getLength() + 1, ConvErr::Count);
+ }
+ }
+ }
+ else
+ return ConvErr::Count;
+
+ sal_uInt16 i = 0;
+ nIntCount = 0;
+ nFloatCount = 0;
+ nDLLCount = 0;
+ nArgCount = 0;
+ nStringCount = 0;
+ ConvErr eRet = ConvErr::OK;
+
+ while( i < nRef && ( nFmla[ i ] != 0x03 ) )
+ {
+ eType = IndexToType( nFmla[ i ] );
+ eOc = IndexToToken( nFmla[ i ] );
+ if( eOc == ocNoName )
+ pExtString = getString( nFmla[ i ] );
+
+ switch( eType )
+ {
+ case FT_NotImpl:
+ DoFunc( ocNoName, 0, pExtString );
+ break;
+
+ case FT_FuncFix0:
+ DoFunc( eOc, 0, nullptr );
+ break;
+
+ case FT_FuncFix1:
+ DoFunc( eOc, 1, nullptr );
+ break;
+
+ case FT_FuncFix2:
+ DoFunc( eOc, 2, nullptr );
+ break;
+
+ case FT_FuncFix3:
+ DoFunc( eOc, 3, nullptr );
+ break;
+
+ case FT_FuncFix4:
+ DoFunc( eOc, 4, nullptr );
+ break;
+
+ case FT_FuncFix5:
+ DoFunc( eOc, 5, nullptr );
+ break;
+
+ case FT_FuncFix6:
+ DoFunc( eOc, 6, nullptr );
+ break;
+
+ case FT_DLL:{
+ eOc = IndexToDLLId( nDLLArray[ nDLLCount ] );
+ sal_uInt8 nPar = nArgArray[ nArgCount ];
+ DoFunc( eOc, nPar, nullptr );
+ nDLLCount++;
+ nArgCount++;
+ }
+ break;
+
+ case FT_Cref : // Single cell reference
+ maIn.ReadUInt16( nNote ).ReadSChar( nCol ).ReadSChar( nPage ).ReadUInt16( nRelBits );
+ SAFEREAD_OR_BREAK( maIn, i, nRef, eRet, ConvErr::Count);
+ ReadSRD( rDoc, aSRD, nPage, nCol, nRelBits );
+ aStack << aPool.Store( aSRD );
+ break;
+
+ case FT_Range: // Block reference
+ maIn.ReadUInt16( nNote ).ReadSChar( nCol ).ReadSChar( nPage ).ReadUInt16( nRelBits );
+ SAFEREAD_OR_BREAK( maIn, i, nRef, eRet, ConvErr::Count);
+ ReadSRD( rDoc, aCRD.Ref1, nPage, nCol, nRelBits );
+ maIn.ReadSChar( nCol ).ReadSChar( nPage ).ReadUInt16( nRelBits );
+ SAFEREAD_OR_BREAK( maIn, i, nRef, eRet, ConvErr::Count);
+ ReadSRD( rDoc, aCRD.Ref2, nPage, nCol, nRelBits );
+ // Sheet name of second corner is not displayed if identical
+ if (aCRD.Ref1.IsFlag3D() && aCRD.Ref1.Tab() == aCRD.Ref2.Tab() &&
+ aCRD.Ref1.IsTabRel() == aCRD.Ref2.IsTabRel())
+ aCRD.Ref2.SetFlag3D( false);
+ aStack << aPool.Store( aCRD );
+ break;
+
+ case FT_FuncVar:{ // Sum of a sequence of numbers
+ i++;
+ sal_uInt8 nArgs = nFmla[ i ];
+ DoFunc( eOc, nArgs, nullptr );
+ }
+ break;
+
+ case FT_Op: // operators
+ aStack >> nPush;
+ aPool << aStack << eOc << nPush;
+ aPool >> aStack;
+ break;
+
+ case FT_Braces:
+ aPool << ocOpen << aStack << ocClose;
+ aPool >> aStack;
+ break;
+
+ case FT_ConstInt:{
+ sal_uInt16 nVal;
+ nVal = nIntArray[ nIntCount ];
+ aStack << aPool.Store( static_cast<double>(nVal) );
+ nIntCount++;
+ }
+ break;
+
+ case FT_ConstFloat:{
+ double nVal;
+ nVal = nFloatArray[ nFloatCount ];
+ aStack << aPool.Store( nVal );
+ nFloatCount++;
+ }
+ break;
+
+ case FT_ConstString:{
+ OUString aLabel(sStringArray[ nStringCount ]);
+ aStack << aPool.Store( aLabel );
+ nStringCount++;
+ }
+ break;
+
+ case FT_Neg:
+ aPool << ocNegSub << aStack;
+ aPool >> aStack;
+ break;
+
+ case FT_NOP: // indicates invalid opcode.
+ case FT_Return: // indicates end of formula
+ break;
+ }
+ i++;
+ }
+ pArray = aPool.GetTokenArray(rDoc, aStack.Get());
+ return eRet;
+}
+
+const struct
+{
+ DefTokenId nToken;
+ QPRO_FUNC_TYPE nType;
+} aFuncMap[] = {
+ { ocPush, FT_ConstFloat },
+ { ocPush, FT_Cref },
+ { ocPush, FT_Range },
+ { ocPush, FT_Return },
+ { ocPush, FT_Braces },
+ { ocPush, FT_ConstInt },
+ { ocPush, FT_ConstString },
+ { ocPush, FT_NOP },
+ { ocNegSub, FT_Neg }, // 0x08
+ { ocAdd, FT_Op },
+ { ocSub, FT_Op },
+ { ocMul, FT_Op },
+ { ocDiv, FT_Op },
+ { ocPow, FT_Op },
+ { ocEqual, FT_Op },
+ { ocNotEqual, FT_Op },
+ { ocLessEqual, FT_Op }, // 0x10
+ { ocGreaterEqual, FT_Op },
+ { ocLess, FT_Op },
+ { ocGreater, FT_Op },
+ { ocAnd, FT_Op },
+ { ocOr, FT_Op },
+ { ocNot, FT_FuncFix1 },
+ { ocPush, FT_NOP }, // Unary plus
+ { ocAddress, FT_FuncFix4 }, // Address of
+ { ocNoName, FT_NotImpl }, // Halt function
+ { ocNoName, FT_DLL }, // DLL function
+ { ocNoName, FT_NOP }, // Extended operands
+ { ocNoName, FT_NOP }, // Extended operands
+ { ocNoName, FT_NOP }, // Reserved
+ { ocNoName, FT_NOP }, // Reserved
+ { ocNotAvail, FT_FuncFix0 }, // NA
+ { ocNoName, FT_FuncFix0 }, // Error // 0x20
+ { ocAbs, FT_FuncFix1 },
+ { ocInt, FT_FuncFix1 },
+ { ocSqrt, FT_FuncFix1 },
+ { ocLog10, FT_FuncFix1 },
+ { ocLn, FT_FuncFix1 },
+ { ocPi, FT_FuncFix0 },
+ { ocSin, FT_FuncFix1 },
+ { ocCos, FT_FuncFix1 },
+ { ocTan, FT_FuncFix1 },
+ { ocArcTan2, FT_FuncFix2 },
+ { ocArcTan, FT_FuncFix1 },
+ { ocArcSin, FT_FuncFix1 },
+ { ocArcCos, FT_FuncFix1 },
+ { ocExp, FT_FuncFix1 },
+ { ocMod, FT_FuncFix2 },
+ { ocChoose, FT_FuncVar }, // 0x30
+ { ocIsNA, FT_FuncFix1 },
+ { ocIsError, FT_FuncFix1 },
+ { ocFalse, FT_FuncFix0 },
+ { ocTrue, FT_FuncFix0 },
+ { ocRandom, FT_FuncFix0 },
+ { ocGetDate, FT_FuncFix3 },
+ { ocGetActTime, FT_FuncFix0 },
+ { ocNoName, FT_NotImpl }, // QPro Pmt
+ { ocNoName, FT_NotImpl }, // QPro Pv
+ { ocNoName, FT_NotImpl }, // QPro Fv
+ { ocIf, FT_FuncFix3 },
+ { ocGetDay, FT_FuncFix1 },
+ { ocGetMonth, FT_FuncFix1 },
+ { ocGetYear, FT_FuncFix1 },
+ { ocRound, FT_FuncFix2 },
+ { ocGetTime, FT_FuncFix3 }, // 0x40
+ { ocGetHour, FT_FuncFix1 },
+ { ocGetMin, FT_FuncFix1 },
+ { ocGetSec, FT_FuncFix1 },
+ { ocIsValue, FT_FuncFix1 },
+ { ocIsString, FT_FuncFix1 },
+ { ocLen, FT_FuncFix1 },
+ { ocValue, FT_FuncFix1 },
+ { ocFixed, FT_FuncFix2 },
+ { ocMid, FT_FuncFix3 },
+ { ocChar, FT_FuncFix1 },
+ { ocCode, FT_FuncFix1 },
+ { ocFind, FT_FuncFix3 },
+ { ocGetDateValue, FT_FuncFix1 },
+ { ocGetTimeValue, FT_FuncFix1 },
+ { ocNoName, FT_NotImpl },
+ { ocSum, FT_FuncVar }, // 0x50
+ { ocAverage, FT_FuncVar },
+ { ocCount, FT_FuncVar },
+ { ocMin, FT_FuncVar },
+ { ocMax, FT_FuncVar },
+ { ocVLookup, FT_FuncFix3 },
+ { ocNPV, FT_FuncFix2 },
+ { ocVar, FT_FuncVar },
+ { ocNormDist, FT_FuncVar },
+ { ocIRR, FT_FuncFix2 },
+ { ocHLookup, FT_FuncFix3 },
+ { ocDBSum, FT_FuncFix3 },
+ { ocDBAverage, FT_FuncFix3 },
+ { ocDBCount, FT_FuncFix3 },
+ { ocDBMin, FT_FuncFix3 },
+ { ocDBMax, FT_FuncFix3 },
+ { ocDBVar, FT_FuncFix3 }, // 0x60
+ { ocDBStdDev, FT_FuncFix3 },
+ { ocNoName, FT_NotImpl },
+ { ocColumns, FT_FuncFix1 },
+ { ocRows, FT_FuncFix1 },
+ { ocRept, FT_FuncFix2 },
+ { ocUpper, FT_FuncFix1 },
+ { ocLower, FT_FuncFix1 },
+ { ocLeft, FT_FuncFix2 },
+ { ocRight, FT_FuncFix2 },
+ { ocReplace, FT_FuncFix4 },
+ { ocProper, FT_FuncFix1 },
+ { ocCell, FT_FuncFix2 },
+ { ocTrim, FT_FuncFix1 },
+ { ocClean, FT_FuncFix1 },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl }, // 0x70
+ { ocExact, FT_FuncFix2 },
+ { ocNoName, FT_NotImpl }, // Call()
+ { ocIndirect, FT_FuncFix1 },
+ { ocRRI, FT_FuncFix3 }, // Interest
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocSLN, FT_FuncFix3 },
+ { ocSYD, FT_FuncFix4 },
+ { ocDDB, FT_FuncFix4 },
+ { ocStDevP, FT_FuncVar },
+ { ocVarP, FT_FuncVar },
+ { ocDBStdDevP, FT_FuncVar },
+ { ocDBVarP, FT_FuncVar },
+ { ocPV, FT_FuncFix3 }, // QPro Pval
+ { ocPMT, FT_FuncFix5 }, // QPro Paymt
+ { ocFV, FT_FuncFix3 }, // QPro Fval // 0x80
+ { ocNper, FT_FuncFix5 },
+ { ocRate, FT_FuncFix5 },
+ { ocIpmt, FT_FuncFix4 },
+ { ocPpmt, FT_FuncFix6 },
+ { ocSumProduct, FT_FuncFix2 },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocDeg, FT_FuncFix1 },
+ { ocRad, FT_FuncFix1 },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl },
+ { ocGetActDate, FT_FuncFix0 },
+ { ocNPV, FT_FuncFix2 },
+ { ocNoName, FT_NotImpl }, // 0x90
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NOP },
+ { ocNoName, FT_NOP }, // 147
+ { ocNoName, FT_NOP }, // 148
+ { ocNoName, FT_NOP }, // 149
+ { ocNoName, FT_NOP }, // 150
+ { ocNoName, FT_NOP }, // 151
+ { ocNoName, FT_NOP }, // 152
+ { ocNoName, FT_NOP }, // 153
+ { ocSheet, FT_FuncFix1 },
+ { ocNoName, FT_NOP }, // 155 - opcodes do not represent any function.
+ { ocNoName, FT_NOP }, // 156
+ { ocIndex, FT_FuncFix4 },
+ { ocNoName, FT_NotImpl },
+ { ocNoName, FT_NotImpl }, // Gives the property of the particular object
+ { ocNoName, FT_NotImpl }, // Dynamic Data Exchange Link // 0x100
+ { ocNoName, FT_NotImpl } // gives properties of DOS menus
+};
+
+const int nIndexCount = SAL_N_ELEMENTS( aFuncMap );
+
+DefTokenId QProToSc::IndexToToken( sal_uInt16 nIndex )
+{
+ if( nIndex < nIndexCount )
+ return aFuncMap[ nIndex ].nToken;
+ return ocNoName;
+}
+
+QPRO_FUNC_TYPE QProToSc::IndexToType( sal_uInt8 nIndex )
+{
+ if( nIndex < nIndexCount )
+ return aFuncMap[ nIndex ].nType;
+ return FT_NotImpl;
+}
+
+DefTokenId QProToSc::IndexToDLLId( sal_uInt16 nIndex )
+{
+ DefTokenId eId;
+ switch( nIndex )
+ {
+ case 0x0001:
+ eId = ocAveDev;
+ break;
+
+ case 0x0024:
+ eId = ocGCD;
+ break;
+
+ case 0x0025:
+ eId = ocLCM;
+ break;
+
+ case 0x0027:
+ eId = ocCeil;
+ break;
+
+ case 0x0028:
+ eId = ocEven;
+ break;
+
+ case 0x0022:
+ eId = ocFact;
+ break;
+
+ case 0x002a:
+ eId = ocFloor;
+ break;
+
+ case 0x002d:
+ eId = ocOdd;
+ break;
+
+ case 0x0006:
+ eId = ocBetaDist;
+ break;
+
+ case 0x0008:
+ eId = ocBetaInv;
+ break;
+
+ case 0x0010:
+ eId = ocCovar;
+ break;
+
+ case 0x000b:
+ eId = ocChiInv;
+ break;
+
+ case 0x003d:
+ eId = ocPDuration;
+ break;
+
+ case 0x0019:
+ eId = ocFInv;
+ break;
+
+ case 0x001a:
+ eId = ocFisher;
+ break;
+
+ case 0x001b:
+ eId = ocFisherInv;
+ break;
+
+ case 0x0030:
+ eId = ocMedian;
+ break;
+
+ default:
+ eId = ocNoName;
+ break;
+ }
+ return eId;
+}
+
+const char* QProToSc::getString( sal_uInt8 nIndex )
+{
+ const char* pExtString = nullptr;
+ switch( nIndex )
+ {
+ case 57:
+ pExtString = "Pv";
+ break;
+
+ case 58:
+ pExtString = "Fv";
+ break;
+
+ case 98:
+ pExtString = "Index2D";
+ break;
+
+ case 111:
+ pExtString = "S";
+ break;
+
+ case 112:
+ pExtString = "N";
+ break;
+
+ case 114:
+ pExtString = "CALL";
+ break;
+
+ case 117:
+ pExtString = "TERM";
+ break;
+
+ case 118:
+ pExtString = "CTERM";
+ break;
+
+ case 134:
+ pExtString = "MEMAVAIL";
+ break;
+
+ case 135:
+ pExtString = "MEMEMSAVAIL";
+ break;
+
+ case 136:
+ pExtString = "FILEEXISTS";
+ break;
+
+ case 137:
+ pExtString = "CURVALUE";
+ break;
+
+ case 140:
+ pExtString = "HEX";
+ break;
+
+ case 141:
+ pExtString = "NUM";
+ break;
+
+ case 145:
+ pExtString = "VERSION";
+ break;
+
+ case 157:
+ pExtString = "INDEX3D";
+ break;
+
+ case 158:
+ pExtString = "CELLINDEX3D";
+ break;
+
+ case 159:
+ pExtString = "PROPERTY";
+ break;
+
+ case 160:
+ pExtString = "DDE";
+ break;
+
+ case 161:
+ pExtString = "COMMAND";
+ break;
+
+ default:
+ pExtString = nullptr;
+ break;
+ }
+ return pExtString;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/qpro/qprostyle.cxx b/sc/source/filter/qpro/qprostyle.cxx
new file mode 100644
index 000000000..a23187c47
--- /dev/null
+++ b/sc/source/filter/qpro/qprostyle.cxx
@@ -0,0 +1,139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <qprostyle.hxx>
+
+#include <scitems.hxx>
+#include <svx/algitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/justifyitem.hxx>
+
+#include <attrib.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <document.hxx>
+
+void ScQProStyle::SetFormat( ScDocument *pDoc, sal_uInt8 nCol, sal_uInt16 nRow, SCTAB nTab, sal_uInt16 nStyle )
+{
+ if (nStyle >= maxsize)
+ return;
+
+ ScPatternAttr aPattern(pDoc->GetPool());
+ SfxItemSet& rItemSet = aPattern.GetItemSet();
+
+ sal_uInt8 nTmp = maAlign[ nStyle ];
+ sal_uInt8 nHor = ( nTmp & 0x07 );
+ sal_uInt8 nVer = ( nTmp & 0x18 );
+ sal_uInt8 nOrient = ( nTmp & 0x60 );
+
+ // Horizontal Alignment
+ SvxCellHorJustify eJustify = SvxCellHorJustify::Standard;
+ switch( nHor )
+ {
+ case 0x00:
+ eJustify = SvxCellHorJustify::Standard;
+ break;
+
+ case 0x01:
+ eJustify = SvxCellHorJustify::Left;
+ break;
+
+ case 0x02:
+ eJustify = SvxCellHorJustify::Center;
+ break;
+
+ case 0x03:
+ eJustify = SvxCellHorJustify::Right;
+ break;
+
+ case 0x04:
+ eJustify = SvxCellHorJustify::Block;
+ break;
+ }
+ rItemSet.Put( SvxHorJustifyItem( eJustify, ATTR_HOR_JUSTIFY ) );
+
+ // Vertical Alignment
+ SvxCellVerJustify eVerJustify = SvxCellVerJustify::Standard;
+ switch( nVer )
+ {
+ case 0x00:
+ eVerJustify = SvxCellVerJustify::Bottom;
+ break;
+
+ case 0x08:
+ eVerJustify = SvxCellVerJustify::Center;
+ break;
+
+ case 0x10:
+ eVerJustify = SvxCellVerJustify::Top;
+ break;
+ }
+
+ rItemSet.Put(SvxVerJustifyItem( eVerJustify, ATTR_VER_JUSTIFY ) );
+
+ // Orientation
+ SvxCellOrientation eOrient = SvxCellOrientation::Standard;
+ switch( nOrient )
+ {
+ case 0x20:
+ eOrient = SvxCellOrientation::TopBottom;
+ break;
+
+ }
+ rItemSet.Put( SvxOrientationItem( eOrient, TypedWhichId<SvxOrientationItem>(0)) );
+
+ // Wrap cell contents
+ if( nTmp & 0x80 )
+ {
+ ScLineBreakCell aWrapItem(true);
+ rItemSet.Put( aWrapItem );
+ }
+
+ // Font Attributes
+ sal_uInt16 nTmpFnt = maFontRecord[ maFont[ nStyle ] ];
+ bool bIsBold, bIsItalic, bIsUnderLine;
+
+ bIsBold = ( nTmpFnt & 0x0001 ) != 0;
+ bIsItalic = ( nTmpFnt & 0x0002 ) != 0;
+ bIsUnderLine = ( nTmpFnt & 0x0004 ) != 0;
+ //(nTmpFnt & 0x0020 ) for StrikeThrough
+
+ if( bIsBold )
+ rItemSet.Put( SvxWeightItem( WEIGHT_BOLD,ATTR_FONT_WEIGHT) );
+ if( bIsItalic )
+ rItemSet.Put( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) );
+ if( bIsUnderLine )
+ rItemSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, ATTR_FONT_UNDERLINE ) );
+
+ if (maFontHeight[ maFont [ nStyle ] ])
+ rItemSet.Put( SvxFontHeightItem( static_cast<sal_uLong>(20 * maFontHeight[ maFont[ nStyle ] ] ), 100, ATTR_FONT_HEIGHT ) );
+
+ OUString fntName = maFontType[ maFont[ nStyle ] ];
+ rItemSet.Put( SvxFontItem( FAMILY_SYSTEM, fntName, OUString(), PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ) );
+
+ pDoc->ApplyPattern( nCol, nRow, nTab, aPattern );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/eeimpars.cxx b/sc/source/filter/rtf/eeimpars.cxx
new file mode 100644
index 000000000..8d768efa5
--- /dev/null
+++ b/sc/source/filter/rtf/eeimpars.cxx
@@ -0,0 +1,657 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <editeng/adjustitem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/langitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdpage.hxx>
+#include <sfx2/sfxhtml.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/string.hxx>
+#include <osl/diagnose.h>
+#include <officecfg/Office/Common.hxx>
+
+#include <eeimport.hxx>
+#include <global.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+#include <docpool.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <eeparser.hxx>
+#include <drwlayer.hxx>
+#include <rangenam.hxx>
+#include <progress.hxx>
+#include <stringutil.hxx>
+#include <rowheightcontext.hxx>
+#include <fuinsert.hxx>
+
+#include <globstr.hrc>
+#include <scresid.hxx>
+
+#include <memory>
+
+ScEEImport::ScEEImport( ScDocument* pDocP, const ScRange& rRange ) :
+ maRange( rRange ),
+ mpDoc( pDocP )
+{
+ const ScPatternAttr* pPattern = mpDoc->GetPattern(
+ maRange.aStart.Col(), maRange.aStart.Row(), maRange.aStart.Tab() );
+ mpEngine.reset( new ScTabEditEngine(*pPattern, mpDoc->GetEditPool(), mpDoc, mpDoc->GetEditPool()) );
+ mpEngine->SetUpdateLayout( false );
+ mpEngine->EnableUndo( false );
+}
+
+ScEEImport::~ScEEImport()
+{
+ // Sequence important, or else we crash in some dtor!
+ // Is guaranteed as ScEEImport is base class
+}
+
+ErrCode ScEEImport::Read( SvStream& rStream, const OUString& rBaseURL )
+{
+ ErrCode nErr = mpParser->Read( rStream, rBaseURL );
+
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ mpParser->GetDimensions( nEndCol, nEndRow );
+ if ( nEndCol != 0 )
+ {
+ nEndCol += maRange.aStart.Col() - 1;
+ if ( nEndCol > mpDoc->MaxCol() )
+ nEndCol = mpDoc->MaxCol();
+ }
+ else
+ nEndCol = maRange.aStart.Col();
+ if ( nEndRow != 0 )
+ {
+ nEndRow += maRange.aStart.Row() - 1;
+ if ( nEndRow > mpDoc->MaxRow() )
+ nEndRow = mpDoc->MaxRow();
+ }
+ else
+ nEndRow = maRange.aStart.Row();
+ maRange.aEnd.Set( nEndCol, nEndRow, maRange.aStart.Tab() );
+
+ return nErr;
+}
+
+namespace
+{
+ bool IsValidSel(const ScTabEditEngine& rEngine, const ESelection& rSel)
+ {
+ const auto nParaCount = rEngine.GetParagraphCount();
+ return rSel.nStartPara < nParaCount && rSel.nEndPara < nParaCount;
+ }
+}
+
+void ScEEImport::WriteToDocument( bool bSizeColsRows, double nOutputFactor, SvNumberFormatter* pFormatter, bool bConvertDate )
+{
+ std::unique_ptr<ScProgress> pProgress( new ScProgress( mpDoc->GetDocumentShell(),
+ ScResId( STR_LOAD_DOC ), mpParser->ListSize(), true ) );
+ sal_uLong nProgress = 0;
+
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ SCTAB nTab;
+ SCROW nOverlapRowMax, nLastMergedRow;
+ SCCOL nMergeColAdd;
+ nStartCol = maRange.aStart.Col();
+ nStartRow = maRange.aStart.Row();
+ nTab = maRange.aStart.Tab();
+ nEndCol = maRange.aEnd.Col();
+ nEndRow = maRange.aEnd.Row();
+ nOverlapRowMax = 0;
+ nMergeColAdd = 0;
+ nLastMergedRow = SCROW_MAX;
+ bool bHasGraphics = false;
+ ScEEParseEntry* pE;
+ if (!pFormatter)
+ pFormatter = mpDoc->GetFormatTable();
+ bool bNumbersEnglishUS = false;
+ if (pFormatter->GetLanguage() == LANGUAGE_SYSTEM && !utl::ConfigManager::IsFuzzing())
+ {
+ // Automatic language option selected. Check for the global 'use US English' option.
+ bNumbersEnglishUS = officecfg::Office::Common::Filter::HTML::Import::NumbersEnglishUS::get();
+ }
+ ScDocumentPool* pDocPool = mpDoc->GetPool();
+ ScRangeName* pRangeNames = mpDoc->GetRangeName();
+ for ( size_t i = 0, n = mpParser->ListSize(); i < n; ++i )
+ {
+ pE = mpParser->ListEntry( i );
+ SCROW nRow = nStartRow + pE->nRow;
+ if ( nRow != nLastMergedRow )
+ nMergeColAdd = 0;
+ SCCOL nCol = nStartCol + pE->nCol + nMergeColAdd;
+ // Determine RowMerge
+ // Pure ColMerge and ColMerge of the first MergeRow already done during parsing
+ if (nRow <= nOverlapRowMax && mpDoc->ValidCol(nCol))
+ {
+ while ( nCol <= mpDoc->MaxCol() && mpDoc->HasAttrib( nCol, nRow, nTab,
+ nCol, nRow, nTab, HasAttrFlags::Overlapped ) )
+ {
+ nCol++;
+ nMergeColAdd++;
+ }
+ nLastMergedRow = nRow;
+ }
+ // Add for second run
+ pE->nCol = nCol;
+ pE->nRow = nRow;
+ if ( mpDoc->ValidCol(nCol) && mpDoc->ValidRow(nRow) )
+ {
+ SfxItemSet aSet = mpEngine->GetAttribs( pE->aSel );
+ // Remove default: we set left/right ourselves depending on Text or
+ // Number
+ // EditView.GetAttribs always returns complete Set filled with
+ // defaults
+ const SfxPoolItem& rItem = aSet.Get( EE_PARA_JUST );
+ if ( static_cast<const SvxAdjustItem&>(rItem).GetAdjust() == SvxAdjust::Left )
+ aSet.ClearItem( EE_PARA_JUST );
+
+ // Test whether simple String without mixed attributes
+ bool bSimple = ( pE->aSel.nStartPara == pE->aSel.nEndPara );
+ for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && bSimple; nId++)
+ {
+ const SfxPoolItem* pItem = nullptr;
+ SfxItemState eState = aSet.GetItemState( nId, true, &pItem );
+ if (eState == SfxItemState::DONTCARE)
+ bSimple = false;
+ else if (eState == SfxItemState::SET)
+ {
+ if ( nId == EE_CHAR_ESCAPEMENT ) // Super-/Subscript always via EE
+ {
+ if ( static_cast<SvxEscapement>(static_cast<const SvxEscapementItem*>(pItem)->GetEnumValue())
+ != SvxEscapement::Off )
+ bSimple = false;
+ }
+ }
+ }
+ if ( bSimple )
+ { // Contains field commands?
+ SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false );
+ if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET )
+ bSimple = false;
+ }
+
+ // HTML
+ OUString aValStr, aNumStr;
+ double fVal = 0.0;
+ sal_uInt32 nNumForm = 0;
+ LanguageType eNumLang = LANGUAGE_NONE;
+ if ( pE->pNumStr )
+ { // SDNUM needs to be if SDVAL
+ aNumStr = *pE->pNumStr;
+ if ( pE->pValStr )
+ aValStr = *pE->pValStr;
+ fVal = SfxHTMLParser::GetTableDataOptionsValNum(
+ nNumForm, eNumLang, aValStr, aNumStr, *pFormatter );
+ }
+
+ // Set attributes
+ auto pAttr = std::make_unique<ScPatternAttr>( pDocPool );
+ pAttr->GetFromEditItemSet( &aSet );
+ SfxItemSet* pAttrItemSet = &pAttr->GetItemSet();
+ if (!aNumStr.isEmpty())
+ {
+ pAttrItemSet->Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumForm ) );
+ pAttrItemSet->Put( SvxLanguageItem( eNumLang, ATTR_LANGUAGE_FORMAT ) );
+ }
+ const SfxItemSet& rESet = pE->aItemSet;
+ if ( rESet.Count() )
+ {
+ const SfxPoolItem* pItem;
+ if ( rESet.GetItemState( ATTR_BACKGROUND, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_BORDER, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_SHADOW, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ // HTML
+ if ( rESet.GetItemState( ATTR_HOR_JUSTIFY, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_VER_JUSTIFY, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_LINEBREAK, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_FONT_COLOR, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ if ( rESet.GetItemState( ATTR_FONT_UNDERLINE, false, &pItem) == SfxItemState::SET )
+ pAttrItemSet->Put( *pItem );
+ // HTML LATIN/CJK/CTL script type dependent
+ const SfxPoolItem* pFont;
+ if ( rESet.GetItemState( ATTR_FONT, false, &pFont) != SfxItemState::SET )
+ pFont = nullptr;
+ const SfxPoolItem* pHeight;
+ if ( rESet.GetItemState( ATTR_FONT_HEIGHT, false, &pHeight) != SfxItemState::SET )
+ pHeight = nullptr;
+ const SfxPoolItem* pWeight;
+ if ( rESet.GetItemState( ATTR_FONT_WEIGHT, false, &pWeight) != SfxItemState::SET )
+ pWeight = nullptr;
+ const SfxPoolItem* pPosture;
+ if ( rESet.GetItemState( ATTR_FONT_POSTURE, false, &pPosture) != SfxItemState::SET )
+ pPosture = nullptr;
+ // Number format
+ const SfxPoolItem* pNumFmt = nullptr;
+ if ( rESet.GetItemState(ATTR_VALUE_FORMAT, false, &pNumFmt) == SfxItemState::SET )
+ pAttrItemSet->Put(*pNumFmt);
+ if ( pFont || pHeight || pWeight || pPosture )
+ {
+ OUString aStr( mpEngine->GetText( pE->aSel ) );
+ SvtScriptType nScriptType = mpDoc->GetStringScriptType( aStr );
+ const SvtScriptType nScripts[3] = { SvtScriptType::LATIN,
+ SvtScriptType::ASIAN, SvtScriptType::COMPLEX };
+ for (SvtScriptType nScript : nScripts)
+ {
+ if ( nScriptType & nScript )
+ {
+ if ( pFont )
+ {
+ pAttrItemSet->Put( pFont->CloneSetWhich(
+ ScGlobal::GetScriptedWhichID(nScript, ATTR_FONT )) );
+ }
+ if ( pHeight )
+ {
+ pAttrItemSet->Put( pHeight->CloneSetWhich(
+ ScGlobal::GetScriptedWhichID(nScript, ATTR_FONT_HEIGHT )) );
+ }
+ if ( pWeight )
+ {
+ pAttrItemSet->Put( pWeight->CloneSetWhich(
+ ScGlobal::GetScriptedWhichID(nScript, ATTR_FONT_WEIGHT )) );
+ }
+ if ( pPosture )
+ {
+ pAttrItemSet->Put( pPosture->CloneSetWhich(
+ ScGlobal::GetScriptedWhichID(nScript, ATTR_FONT_POSTURE )) );
+ }
+ }
+ }
+ }
+ }
+ if ( pE->nColOverlap > 1 || pE->nRowOverlap > 1 )
+ { // Merged cells, with SfxItemSet.Put() is faster than
+ // with ScDocument.DoMerge() afterwards
+ ScMergeAttr aMerge( pE->nColOverlap, pE->nRowOverlap );
+ pAttrItemSet->Put( aMerge );
+ SCROW nRO = 0;
+ if ( pE->nColOverlap > 1 )
+ mpDoc->ApplyFlagsTab( nCol+1, nRow,
+ nCol + pE->nColOverlap - 1, nRow, nTab,
+ ScMF::Hor );
+ if ( pE->nRowOverlap > 1 )
+ {
+ nRO = nRow + pE->nRowOverlap - 1;
+ mpDoc->ApplyFlagsTab( nCol, nRow+1,
+ nCol, nRO , nTab,
+ ScMF::Ver );
+ if ( nRO > nOverlapRowMax )
+ nOverlapRowMax = nRO;
+ }
+ if ( pE->nColOverlap > 1 && pE->nRowOverlap > 1 )
+ mpDoc->ApplyFlagsTab( nCol+1, nRow+1,
+ nCol + pE->nColOverlap - 1, nRO, nTab,
+ ScMF::Hor | ScMF::Ver );
+ }
+ const ScStyleSheet* pStyleSheet =
+ mpDoc->GetPattern( nCol, nRow, nTab )->GetStyleSheet();
+ pAttr->SetStyleSheet( const_cast<ScStyleSheet*>(pStyleSheet) );
+ auto rAttrItemSet2 = mpDoc->SetPattern( nCol, nRow, nTab, std::move(pAttr) )->GetItemSet();
+
+ // Add data
+ if (bSimple)
+ {
+ ScSetStringParam aParam;
+ aParam.mpNumFormatter = pFormatter;
+ aParam.mbDetectNumberFormat = true;
+ aParam.meSetTextNumFormat = ScSetStringParam::SpecialNumberOnly;
+ aParam.mbHandleApostrophe = false;
+ aParam.mbCheckLinkFormula = true;
+
+ if (!aValStr.isEmpty())
+ mpDoc->SetValue( nCol, nRow, nTab, fVal );
+ else if ( !pE->aSel.HasRange() )
+ {
+ // maybe ALT text of IMG or similar
+ mpDoc->SetString( nCol, nRow, nTab, pE->aAltText, &aParam );
+ // If SelRange is completely empty, the succeeding text can be in the same paragraph!
+ }
+ else
+ {
+ OUString aStr;
+ if( pE->bEntirePara )
+ {
+ aStr = mpEngine->GetText( pE->aSel.nStartPara );
+ }
+ else
+ {
+ aStr = comphelper::string::strip(mpEngine->GetText(pE->aSel), ' ');
+ }
+
+ bool bTextFormat = false;
+
+ if (const SfxUInt32Item* pNumFmt = rAttrItemSet2.GetItemIfSet(ATTR_VALUE_FORMAT, false))
+ {
+ sal_uInt32 nNumFmt = pNumFmt->GetValue();
+ SvNumFormatType nType = pFormatter->GetType(nNumFmt);
+ if (nType == SvNumFormatType::TEXT)
+ // Format is set to Text.
+ bTextFormat = true;
+ }
+
+ // TODO: RTF import should follow the language tag,
+ // currently this follows the HTML options for both, HTML
+ // and RTF.
+ if (bNumbersEnglishUS)
+ {
+ pFormatter->ChangeIntl( LANGUAGE_ENGLISH_US);
+ sal_uInt32 nIndex = pFormatter->GetStandardIndex( LANGUAGE_ENGLISH_US);
+ double fEnVal = 0.0;
+ if (pFormatter->IsNumberFormat( aStr, nIndex, fEnVal))
+ {
+ sal_uInt32 nNewIndex =
+ pFormatter->GetFormatForLanguageIfBuiltIn(
+ nIndex, LANGUAGE_SYSTEM);
+ OSL_ENSURE( nNewIndex != nIndex, "ScEEImport::WriteToDocument: NumbersEnglishUS not a built-in format?");
+ pFormatter->GetInputLineString( fEnVal, nNewIndex, aStr);
+ }
+ else
+ bTextFormat = true;
+ pFormatter->ChangeIntl( LANGUAGE_SYSTEM);
+ }
+
+ // #105460#, #i4180# String cells can't contain tabs or linebreaks
+ // -> replace with spaces
+ aStr = aStr.replaceAll( "\t", " " );
+ aStr = aStr.replaceAll( "\n", " " );
+
+ if (bTextFormat)
+ {
+ aParam.mbDetectNumberFormat = false;
+ aParam.meSetTextNumFormat = ScSetStringParam::Always;
+ }
+ else
+ aParam.mbDetectNumberFormat = bConvertDate;
+
+ mpDoc->SetString(nCol, nRow, nTab, aStr, &aParam);
+ }
+ }
+ else if (std::unique_ptr<EditTextObject> pTextObject = IsValidSel(*mpEngine, pE->aSel) ? mpEngine->CreateTextObject(pE->aSel) : nullptr)
+ {
+ // The cell will own the text object instance.
+ mpDoc->SetEditText(ScAddress(nCol,nRow,nTab), std::move(pTextObject));
+ }
+ if ( !pE->maImageList.empty() )
+ bHasGraphics |= GraphicSize( nCol, nRow, pE );
+ if ( pE->pName )
+ { // Anchor Name => RangeName
+ if (!pRangeNames->findByUpperName(ScGlobal::getCharClass().uppercase(*pE->pName)))
+ {
+ ScRangeData* pData = new ScRangeData( *mpDoc, *pE->pName,
+ ScAddress( nCol, nRow, nTab ) );
+ pRangeNames->insert( pData );
+ }
+ }
+ }
+ pProgress->SetStateOnPercent( ++nProgress );
+ }
+ if ( bSizeColsRows )
+ {
+ // Column widths
+ ColWidthsMap& rColWidths = mpParser->GetColWidths();
+ if ( !rColWidths.empty() )
+ {
+ nProgress = 0;
+ pProgress->SetState( nProgress, nEndCol - nStartCol + 1 );
+ for ( SCCOL nCol = nStartCol; nCol <= nEndCol; nCol++ )
+ {
+ sal_uInt16 nWidth = 0;
+ ColWidthsMap::const_iterator it = rColWidths.find( nCol );
+ if ( it != rColWidths.end() )
+ nWidth = it->second;
+ if ( nWidth )
+ mpDoc->SetColWidth( nCol, nTab, nWidth );
+ pProgress->SetState( ++nProgress );
+ }
+ }
+ pProgress.reset(); // SetOptimalHeight has its own ProgressBar
+ // Adjust line height, base is 100% zoom
+ Fraction aZoom( 1, 1 );
+ // Factor is printer to display ratio
+ double nPPTX = ScGlobal::nScreenPPTX * static_cast<double>(aZoom) / nOutputFactor;
+ double nPPTY = ScGlobal::nScreenPPTY * static_cast<double>(aZoom);
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ sc::RowHeightContext aCxt(mpDoc->MaxRow(), nPPTX, nPPTY, aZoom, aZoom, pVirtDev);
+ aCxt.setExtraHeight(ScGlobal::nLastRowHeightExtra);
+ mpDoc->SetOptimalHeight(aCxt, 0, nEndRow, 0, true);
+
+ if ( !maRowHeights.empty() )
+ {
+ for ( SCROW nRow = nStartRow; nRow <= nEndRow; nRow++ )
+ {
+ RowHeightMap::const_iterator it = maRowHeights.find( nRow );
+ sal_uInt16 nHeight = it == maRowHeights.end() ? 0 : it->second;
+ if ( nHeight > mpDoc->GetRowHeight( nRow, nTab ) )
+ mpDoc->SetRowHeight( nRow, nTab, nHeight );
+ }
+ }
+ }
+ if ( !bHasGraphics )
+ return;
+
+ // Insert graphics
+ for ( size_t i = 0, nListSize = mpParser->ListSize(); i < nListSize; ++i )
+ {
+ pE = mpParser->ListEntry( i );
+ if ( !pE->maImageList.empty() )
+ {
+ SCCOL nCol = pE->nCol;
+ SCROW nRow = pE->nRow;
+ if ( mpDoc->ValidCol(nCol) && mpDoc->ValidRow(nRow) )
+ InsertGraphic( nCol, nRow, nTab, pE );
+ }
+ }
+}
+
+bool ScEEImport::GraphicSize( SCCOL nCol, SCROW nRow, ScEEParseEntry* pE )
+{
+ if ( pE->maImageList.empty() )
+ return false;
+ bool bHasGraphics = false;
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+ tools::Long nWidth, nHeight;
+ nWidth = nHeight = 0;
+ char nDir = nHorizontal;
+ for (const std::unique_ptr<ScHTMLImage> & pImage : pE->maImageList)
+ {
+ ScHTMLImage* pI = pImage.get();
+ if ( pI->pGraphic )
+ bHasGraphics = true;
+ Size aSizePix = pI->aSize;
+ aSizePix.AdjustWidth(2 * pI->aSpace.X() );
+ aSizePix.AdjustHeight(2 * pI->aSpace.Y() );
+ Size aLogicSize = pDefaultDev->PixelToLogic( aSizePix, MapMode( MapUnit::MapTwip ) );
+ if ( nDir & nHorizontal )
+ nWidth += aLogicSize.Width();
+ else if ( nWidth < aLogicSize.Width() )
+ nWidth = aLogicSize.Width();
+ if ( nDir & nVertical )
+ nHeight += aLogicSize.Height();
+ else if ( nHeight < aLogicSize.Height() )
+ nHeight = aLogicSize.Height();
+ nDir = pI->nDir;
+ }
+ // Column widths
+ ColWidthsMap& rColWidths = mpParser->GetColWidths();
+ tools::Long nThisWidth = 0;
+ ColWidthsMap::const_iterator it = rColWidths.find( nCol );
+ if ( it != rColWidths.end() )
+ nThisWidth = it->second;
+ tools::Long nColWidths = nThisWidth;
+ SCCOL nColSpanCol = nCol + pE->nColOverlap;
+ for ( SCCOL nC = nCol + 1; nC < nColSpanCol; nC++ )
+ {
+ ColWidthsMap::const_iterator it2 = rColWidths.find( nC );
+ if ( it2 != rColWidths.end() )
+ nColWidths += it2->second;
+ }
+ if ( nWidth > nColWidths )
+ { // Only insert difference in first column
+ rColWidths[ nCol ] = nWidth - nColWidths + nThisWidth;
+ }
+ // Distribute line height difference between all affected lines
+ SCROW nRowSpan = pE->nRowOverlap;
+
+ assert(nRowSpan != 0);
+ if ( nRowSpan == 0 )
+ return bHasGraphics;
+
+ nHeight /= nRowSpan;
+
+ if ( nHeight == 0 )
+ nHeight = 1; // For definite comparison
+ for ( SCROW nR = nRow; nR < nRow + nRowSpan; nR++ )
+ {
+ RowHeightMap::const_iterator it2 = maRowHeights.find( nR );
+ tools::Long nRowHeight = it2 == maRowHeights.end() ? 0 : it2->second;
+ if ( nHeight > nRowHeight )
+ {
+ maRowHeights[ nR ] = nHeight;
+ }
+ }
+ return bHasGraphics;
+}
+
+void ScEEImport::InsertGraphic( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ ScEEParseEntry* pE )
+{
+ if ( pE->maImageList.empty() )
+ return ;
+ ScDrawLayer* pModel = mpDoc->GetDrawLayer();
+ if (!pModel)
+ {
+ mpDoc->InitDrawLayer();
+ pModel = mpDoc->GetDrawLayer();
+ }
+ SdrPage* pPage = pModel->GetPage( static_cast<sal_uInt16>(nTab) );
+ OutputDevice* pDefaultDev = Application::GetDefaultDevice();
+
+ Point aCellInsertPos(
+ o3tl::convert(mpDoc->GetColOffset(nCol, nTab), o3tl::Length::twip, o3tl::Length::mm100),
+ o3tl::convert(mpDoc->GetRowOffset(nRow, nTab), o3tl::Length::twip, o3tl::Length::mm100));
+
+ Point aInsertPos( aCellInsertPos );
+ Point aSpace;
+ Size aLogicSize;
+ char nDir = nHorizontal;
+ for (const std::unique_ptr<ScHTMLImage> & pImage : pE->maImageList)
+ {
+ ScHTMLImage* pI = pImage.get();
+ if ( nDir & nHorizontal )
+ { // Horizontal
+ aInsertPos.AdjustX(aLogicSize.Width() );
+ aInsertPos.AdjustX(aSpace.X() );
+ aInsertPos.setY( aCellInsertPos.Y() );
+ }
+ else
+ { // Vertical
+ aInsertPos.setX( aCellInsertPos.X() );
+ aInsertPos.AdjustY(aLogicSize.Height() );
+ aInsertPos.AdjustY(aSpace.Y() );
+ }
+ // Add offset of Spacing
+ aSpace = pDefaultDev->PixelToLogic( pI->aSpace, MapMode( MapUnit::Map100thMM ) );
+ aInsertPos += aSpace;
+
+ Size aSizePix = pI->aSize;
+ aLogicSize = pDefaultDev->PixelToLogic( aSizePix, MapMode( MapUnit::Map100thMM ) );
+
+ // Limit size
+ ::ScLimitSizeOnDrawPage( aLogicSize, aInsertPos, pPage->GetSize() );
+
+ if ( pI->pGraphic )
+ {
+ tools::Rectangle aRect ( aInsertPos, aLogicSize );
+ SdrGrafObj* pObj = new SdrGrafObj(
+ *pModel,
+ *pI->pGraphic,
+ aRect);
+
+ // calling SetGraphicLink here doesn't work
+ pObj->SetName( pI->aURL );
+
+ pPage->InsertObject( pObj );
+
+ // SetGraphicLink has to be used after inserting the object,
+ // otherwise an empty graphic is swapped in and the contact stuff crashes.
+ // See #i37444#.
+ pObj->SetGraphicLink( pI->aURL );
+
+ pObj->SetLogicRect( aRect ); // Only after InsertObject!
+ }
+ nDir = pI->nDir;
+ }
+}
+
+ScEEParser::ScEEParser( EditEngine* pEditP ) :
+ pEdit( pEditP ),
+ pPool( EditEngine::CreatePool() ),
+ pDocPool( new ScDocumentPool ),
+ nRtfLastToken(0),
+ nColCnt(0),
+ nRowCnt(0),
+ nColMax(0),
+ nRowMax(0)
+{
+ // pPool is foisted on SvxRTFParser at RtfImportState::Start later on
+ pPool->SetSecondaryPool( pDocPool.get() );
+ pPool->FreezeIdRanges();
+ NewActEntry( nullptr );
+}
+
+ScEEParser::~ScEEParser()
+{
+ mxActEntry.reset();
+ maList.clear();
+
+ // Don't delete Pool until the lists have been deleted
+ pPool->SetSecondaryPool( nullptr );
+ pDocPool.clear();
+ pPool.clear();
+}
+
+void ScEEParser::NewActEntry( const ScEEParseEntry* pE )
+{ // New free-flying mxActEntry
+ mxActEntry = std::make_shared<ScEEParseEntry>(pPool.get());
+ mxActEntry->aSel.nStartPara = (pE ? pE->aSel.nEndPara + 1 : 0);
+ mxActEntry->aSel.nStartPos = 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/expbase.cxx b/sc/source/filter/rtf/expbase.cxx
new file mode 100644
index 000000000..d5461f1d6
--- /dev/null
+++ b/sc/source/filter/rtf/expbase.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <expbase.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+
+ScExportBase::ScExportBase( SvStream& rStrmP, ScDocument* pDocP,
+ const ScRange& rRangeP )
+ :
+ rStrm( rStrmP ),
+ aRange( rRangeP ),
+ pDoc( pDocP ),
+ pFormatter( pDocP->GetFormatTable() )
+{
+}
+
+ScExportBase::~ScExportBase()
+{
+}
+
+bool ScExportBase::GetDataArea( SCTAB nTab, SCCOL& nStartCol,
+ SCROW& nStartRow, SCCOL& nEndCol, SCROW& nEndRow ) const
+{
+ pDoc->GetDataStart( nTab, nStartCol, nStartRow );
+ pDoc->GetPrintArea( nTab, nEndCol, nEndRow );
+ return TrimDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+}
+
+bool ScExportBase::TrimDataArea( SCTAB nTab, SCCOL& nStartCol,
+ SCROW& nStartRow, SCCOL& nEndCol, SCROW& nEndRow ) const
+{
+ while ( nStartCol <= nEndCol && pDoc->ColHidden(nStartCol, nTab))
+ ++nStartCol;
+ while ( nStartCol <= nEndCol && pDoc->ColHidden(nEndCol, nTab))
+ --nEndCol;
+ nStartRow = pDoc->FirstVisibleRow(nStartRow, nEndRow, nTab);
+ nEndRow = pDoc->LastVisibleRow(nStartRow, nEndRow, nTab);
+ return nStartCol <= nEndCol && nStartRow <= nEndRow && nEndRow !=
+ ::std::numeric_limits<SCROW>::max();
+}
+
+bool ScExportBase::IsEmptyTable( SCTAB nTab ) const
+{
+ if ( !pDoc->HasTable( nTab ) || !pDoc->IsVisible( nTab ) )
+ return true;
+ SCCOL nStartCol, nEndCol;
+ SCROW nStartRow, nEndRow;
+ return !GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
+}
+
+ScFieldEditEngine& ScExportBase::GetEditEngine() const
+{
+ if ( !pEditEngine )
+ const_cast<ScExportBase*>(this)->pEditEngine.reset( new ScFieldEditEngine(pDoc, pDoc->GetEditPool()) );
+ return *pEditEngine;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/rtfexp.cxx b/sc/source/filter/rtf/rtfexp.cxx
new file mode 100644
index 000000000..b8e4015fd
--- /dev/null
+++ b/sc/source/filter/rtf/rtfexp.cxx
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svtools/rtfout.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <tools/stream.hxx>
+
+#include <rtfexp.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <patattr.hxx>
+#include <attrib.hxx>
+#include <cellform.hxx>
+#include <editutil.hxx>
+#include <ftools.hxx>
+
+void ScFormatFilterPluginImpl::ScExportRTF( SvStream& rStrm, ScDocument* pDoc,
+ const ScRange& rRange, const rtl_TextEncoding /*eNach*/ )
+{
+ ScRTFExport aEx( rStrm, pDoc, rRange );
+ aEx.Write();
+}
+
+ScRTFExport::ScRTFExport( SvStream& rStrmP, ScDocument* pDocP, const ScRange& rRangeP )
+ :
+ ScExportBase( rStrmP, pDocP, rRangeP ),
+ pCellX( new sal_uLong[ pDoc->MaxCol()+2 ] )
+{
+}
+
+ScRTFExport::~ScRTFExport()
+{
+}
+
+void ScRTFExport::Write()
+{
+ rStrm.WriteChar( '{' ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_RTF );
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_ANSI ).WriteCharPtr( SAL_NEWLINE_STRING );
+
+ // Data
+ for ( SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); nTab++ )
+ {
+ if ( nTab > aRange.aStart.Tab() )
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PAR );
+ WriteTab( nTab );
+ }
+
+ rStrm.WriteChar( '}' ).WriteCharPtr( SAL_NEWLINE_STRING );
+}
+
+void ScRTFExport::WriteTab( SCTAB nTab )
+{
+ rStrm.WriteChar( '{' ).WriteCharPtr( SAL_NEWLINE_STRING );
+ if ( pDoc->HasTable( nTab ) )
+ {
+ memset( &pCellX[0], 0, (pDoc->MaxCol()+2) * sizeof(sal_uLong) );
+ SCCOL nCol;
+ SCCOL nEndCol = aRange.aEnd.Col();
+ for ( nCol = aRange.aStart.Col(); nCol <= nEndCol; nCol++ )
+ {
+ pCellX[nCol+1] = pCellX[nCol] + pDoc->GetColWidth( nCol, nTab );
+ }
+
+ SCROW nEndRow = aRange.aEnd.Row();
+ for ( SCROW nRow = aRange.aStart.Row(); nRow <= nEndRow; nRow++ )
+ {
+ WriteRow( nTab, nRow );
+ }
+ }
+ rStrm.WriteChar( '}' ).WriteCharPtr( SAL_NEWLINE_STRING );
+}
+
+void ScRTFExport::WriteRow( SCTAB nTab, SCROW nRow )
+{
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TROWD ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRGAPH ).WriteCharPtr( "30" ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRLEFT ).WriteCharPtr( "-30" );
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRRH ).WriteOString( OString::number(pDoc->GetRowHeight(nRow, nTab)) );
+ SCCOL nCol;
+ SCCOL nEndCol = aRange.aEnd.Col();
+ for ( nCol = aRange.aStart.Col(); nCol <= nEndCol; nCol++ )
+ {
+ const ScPatternAttr* pAttr = pDoc->GetPattern( nCol, nRow, nTab );
+ const ScMergeAttr& rMergeAttr = pAttr->GetItem( ATTR_MERGE );
+ const SvxVerJustifyItem& rVerJustifyItem= pAttr->GetItem( ATTR_VER_JUSTIFY );
+
+ const char* pChar;
+
+ if ( rMergeAttr.GetColMerge() != 0 )
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CLMGF );
+ else
+ {
+ const ScMergeFlagAttr& rMergeFlagAttr = pAttr->GetItem( ATTR_MERGE_FLAG );
+ if ( rMergeFlagAttr.IsHorOverlapped() )
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CLMRG );
+ }
+
+ switch( rVerJustifyItem.GetValue() )
+ {
+ case SvxCellVerJustify::Top: pChar = OOO_STRING_SVTOOLS_RTF_CLVERTALT; break;
+ case SvxCellVerJustify::Center: pChar = OOO_STRING_SVTOOLS_RTF_CLVERTALC; break;
+ case SvxCellVerJustify::Bottom: pChar = OOO_STRING_SVTOOLS_RTF_CLVERTALB; break;
+ case SvxCellVerJustify::Standard: pChar = OOO_STRING_SVTOOLS_RTF_CLVERTALB; break; //! Bottom
+ default: pChar = nullptr; break;
+ }
+ if ( pChar )
+ rStrm.WriteCharPtr( pChar );
+
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELLX ).WriteOString( OString::number(pCellX[nCol+1]) );
+ if ( (nCol & 0x0F) == 0x0F )
+ rStrm.WriteCharPtr( SAL_NEWLINE_STRING ); // Do not let lines get too long
+ }
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PARD ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PLAIN ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_INTBL ).WriteCharPtr( SAL_NEWLINE_STRING );
+
+ sal_uInt64 nStrmPos = rStrm.Tell();
+ for ( nCol = aRange.aStart.Col(); nCol <= nEndCol; nCol++ )
+ {
+ WriteCell( nTab, nRow, nCol );
+ if ( rStrm.Tell() - nStrmPos > 255 )
+ { // Do not let lines get too long
+ rStrm.WriteCharPtr( SAL_NEWLINE_STRING );
+ nStrmPos = rStrm.Tell();
+ }
+ }
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_ROW ).WriteCharPtr( SAL_NEWLINE_STRING );
+}
+
+void ScRTFExport::WriteCell( SCTAB nTab, SCROW nRow, SCCOL nCol )
+{
+ const ScPatternAttr* pAttr = pDoc->GetPattern( nCol, nRow, nTab );
+
+ const ScMergeFlagAttr& rMergeFlagAttr = pAttr->GetItem( ATTR_MERGE_FLAG );
+ if ( rMergeFlagAttr.IsHorOverlapped() )
+ {
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELL );
+ return ;
+ }
+
+ bool bValueData = false;
+ OUString aContent;
+ ScAddress aPos(nCol, nRow, nTab);
+ ScRefCellValue aCell(*pDoc, aPos);
+ switch (aCell.meType)
+ {
+ case CELLTYPE_NONE:
+ bValueData = false;
+ break;
+ case CELLTYPE_EDIT:
+ {
+ bValueData = false;
+ const EditTextObject* pObj = aCell.mpEditText;
+ EditEngine& rEngine = GetEditEngine();
+ rEngine.SetText(*pObj);
+ aContent = rEngine.GetText(); // LineFeed in between paragraphs!
+ }
+ break;
+ default:
+ {
+ bValueData = pDoc->HasValueData(aPos);
+ sal_uInt32 nFormat = pAttr->GetNumberFormat(pFormatter);
+ const Color* pColor;
+ aContent = ScCellFormat::GetString(*pDoc, aPos, nFormat, &pColor, *pFormatter);
+ }
+ }
+
+ bool bResetAttr(false);
+
+ const SvxHorJustifyItem& rHorJustifyItem = pAttr->GetItem( ATTR_HOR_JUSTIFY );
+ const SvxWeightItem& rWeightItem = pAttr->GetItem( ATTR_FONT_WEIGHT );
+ const SvxPostureItem& rPostureItem = pAttr->GetItem( ATTR_FONT_POSTURE );
+ const SvxUnderlineItem& rUnderlineItem = pAttr->GetItem( ATTR_FONT_UNDERLINE );
+
+ const char* pChar;
+
+ switch( rHorJustifyItem.GetValue() )
+ {
+ case SvxCellHorJustify::Standard:
+ pChar = (bValueData ? OOO_STRING_SVTOOLS_RTF_QR : OOO_STRING_SVTOOLS_RTF_QL);
+ break;
+ case SvxCellHorJustify::Center: pChar = OOO_STRING_SVTOOLS_RTF_QC; break;
+ case SvxCellHorJustify::Block: pChar = OOO_STRING_SVTOOLS_RTF_QJ; break;
+ case SvxCellHorJustify::Right: pChar = OOO_STRING_SVTOOLS_RTF_QR; break;
+ case SvxCellHorJustify::Left:
+ case SvxCellHorJustify::Repeat:
+ default: pChar = OOO_STRING_SVTOOLS_RTF_QL; break;
+ }
+ rStrm.WriteCharPtr( pChar );
+
+ if ( rWeightItem.GetWeight() >= WEIGHT_BOLD )
+ { // bold
+ bResetAttr = true;
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_B );
+ }
+ if ( rPostureItem.GetPosture() != ITALIC_NONE )
+ { // italic
+ bResetAttr = true;
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_I );
+ }
+ if ( rUnderlineItem.GetLineStyle() != LINESTYLE_NONE )
+ { // underline
+ bResetAttr = true;
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_UL );
+ }
+
+ rStrm.WriteChar( ' ' );
+ RTFOutFuncs::Out_String( rStrm, aContent );
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELL );
+
+ if ( bResetAttr )
+ rStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PLAIN );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/rtfimp.cxx b/sc/source/filter/rtf/rtfimp.cxx
new file mode 100644
index 000000000..fe3856dfa
--- /dev/null
+++ b/sc/source/filter/rtf/rtfimp.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <filter.hxx>
+#include <editutil.hxx>
+#include <rtfimp.hxx>
+#include <rtfparse.hxx>
+#include <ftools.hxx>
+
+ErrCode ScFormatFilterPluginImpl::ScImportRTF( SvStream &rStream, const OUString& rBaseURL, ScDocument *pDoc, ScRange& rRange )
+{
+ ScRTFImport aImp( pDoc, rRange );
+ ErrCode nErr = aImp.Read( rStream, rBaseURL );
+ ScRange aR = aImp.GetRange();
+ rRange.aEnd = aR.aEnd;
+ aImp.WriteToDocument();
+ return nErr;
+}
+
+std::unique_ptr<ScEEAbsImport> ScFormatFilterPluginImpl::CreateRTFImport( ScDocument* pDoc, const ScRange& rRange )
+{
+ return std::make_unique<ScRTFImport>( pDoc, rRange );
+}
+
+ScRTFImport::ScRTFImport( ScDocument* pDocP, const ScRange& rRange ) :
+ ScEEImport( pDocP, rRange )
+{
+ mpParser.reset(new ScRTFParser( mpEngine.get() ));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/rtf/rtfparse.cxx b/sc/source/filter/rtf/rtfparse.cxx
new file mode 100644
index 000000000..b2d2b8c25
--- /dev/null
+++ b/sc/source/filter/rtf/rtfparse.cxx
@@ -0,0 +1,403 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <scitems.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/svxrtf.hxx>
+#include <svtools/rtftoken.h>
+#include <osl/diagnose.h>
+#include <svl/itempool.hxx>
+
+#include <rtfparse.hxx>
+
+#define SC_RTFTWIPTOL 10 // 10 Twips tolerance when determining columns
+
+ScRTFParser::ScRTFParser( EditEngine* pEditP ) :
+ ScEEParser( pEditP ),
+ mnCurPos(0),
+ pActDefault( nullptr ),
+ pDefMerge( nullptr ),
+ nStartAdjust( sal_uLong(~0) ),
+ nLastWidth(0),
+ bNewDef( false )
+{
+ // RTF default FontSize 12Pt
+ tools::Long nMM = o3tl::convert(12, o3tl::Length::pt, o3tl::Length::mm100);
+
+ pPool->SetPoolDefaultItem( SvxFontHeightItem( nMM, 100, EE_CHAR_FONTHEIGHT ) );
+ // Free-flying pInsDefault
+ pInsDefault.reset( new ScRTFCellDefault( pPool.get() ) );
+}
+
+ScRTFParser::~ScRTFParser()
+{
+ pInsDefault.reset();
+ maDefaultList.clear();
+}
+
+ErrCode ScRTFParser::Read( SvStream& rStream, const OUString& rBaseURL )
+{
+ Link<RtfImportInfo&,void> aOldLink = pEdit->GetRtfImportHdl();
+ pEdit->SetRtfImportHdl( LINK( this, ScRTFParser, RTFImportHdl ) );
+ ErrCode nErr = pEdit->Read( rStream, rBaseURL, EETextFormat::Rtf );
+ if ( nRtfLastToken == RTF_PAR )
+ {
+ if ( !maList.empty() )
+ {
+ auto& pE = maList.back();
+ if ( // Completely empty
+ ( pE->aSel.nStartPara == pE->aSel.nEndPara
+ && pE->aSel.nStartPos == pE->aSel.nEndPos
+ )
+ || // Empty paragraph
+ ( pE->aSel.nStartPara + 1 == pE->aSel.nEndPara
+ && pE->aSel.nStartPos == pEdit->GetTextLen( pE->aSel.nStartPara )
+ && pE->aSel.nEndPos == 0
+ )
+ )
+ { // Don't take over the last paragraph
+ maList.pop_back();
+ }
+ }
+ }
+ ColAdjust();
+ pEdit->SetRtfImportHdl( aOldLink );
+ return nErr;
+}
+
+void ScRTFParser::EntryEnd( ScEEParseEntry* pE, const ESelection& aSel )
+{
+ // Paragraph -2 strips the attached empty paragraph
+ pE->aSel.nEndPara = aSel.nEndPara - 2;
+ // Although it's called nEndPos, the last one is position + 1
+ pE->aSel.nEndPos = pEdit->GetTextLen( aSel.nEndPara - 1 );
+}
+
+inline void ScRTFParser::NextRow()
+{
+ if ( nRowMax < ++nRowCnt )
+ nRowMax = nRowCnt;
+}
+
+bool ScRTFParser::SeekTwips( sal_uInt16 nTwips, SCCOL* pCol )
+{
+ ScRTFColTwips::const_iterator it = aColTwips.find( nTwips );
+ bool bFound = it != aColTwips.end();
+ sal_uInt16 nPos = it - aColTwips.begin();
+ *pCol = static_cast<SCCOL>(nPos);
+ if ( bFound )
+ return true;
+ sal_uInt16 nCount = aColTwips.size();
+ if ( !nCount )
+ return false;
+ SCCOL nCol = *pCol;
+ // nCol is insertion position; the next one higher up is there (or not)
+ if ( nCol < static_cast<SCCOL>(nCount) && ((aColTwips[nCol] - SC_RTFTWIPTOL) <= nTwips) )
+ return true;
+ // Not smaller than everything else? Then compare with the next lower one
+ else if ( nCol != 0 && ((aColTwips[nCol-1] + SC_RTFTWIPTOL) >= nTwips) )
+ {
+ (*pCol)--;
+ return true;
+ }
+ return false;
+}
+
+void ScRTFParser::ColAdjust()
+{
+ if ( nStartAdjust == sal_uLong(~0) )
+ return;
+
+ SCCOL nCol = 0;
+ for (size_t i = nStartAdjust, nListSize = maList.size(); i < nListSize; ++i)
+ {
+ auto& pE = maList[i];
+ if ( pE->nCol == 0 )
+ nCol = 0;
+ pE->nCol = nCol;
+ if ( pE->nColOverlap > 1 )
+ nCol = nCol + pE->nColOverlap; // Merged cells with \clmrg
+ else
+ {
+ SeekTwips( pE->nTwips, &nCol );
+ if ( ++nCol <= pE->nCol )
+ nCol = pE->nCol + 1; // Moved cell X
+ pE->nColOverlap = nCol - pE->nCol; // Merged cells without \clmrg
+ }
+ if ( nCol > nColMax )
+ nColMax = nCol;
+ }
+ nStartAdjust = sal_uLong(~0);
+ aColTwips.clear();
+}
+
+IMPL_LINK( ScRTFParser, RTFImportHdl, RtfImportInfo&, rInfo, void )
+{
+ switch ( rInfo.eState )
+ {
+ case RtfImportState::NextToken:
+ ProcToken( &rInfo );
+ break;
+ case RtfImportState::UnknownAttr:
+ ProcToken( &rInfo );
+ break;
+ case RtfImportState::Start:
+ {
+ SvxRTFParser* pParser = static_cast<SvxRTFParser*>(rInfo.pParser);
+ pParser->SetAttrPool( pPool.get() );
+ pParser->SetPardMap(SID_ATTR_BRUSH, ATTR_BACKGROUND);
+ pParser->SetPardMap(SID_ATTR_BORDER_OUTER, ATTR_BORDER);
+ pParser->SetPardMap(SID_ATTR_BORDER_SHADOW, ATTR_SHADOW);
+ }
+ break;
+ case RtfImportState::End:
+ if ( rInfo.aSelection.nEndPos )
+ { // If still text: create last paragraph
+ pActDefault = nullptr;
+ rInfo.nToken = RTF_PAR;
+ // EditEngine did not attach an empty paragraph anymore
+ // which EntryEnd could strip
+ rInfo.aSelection.nEndPara++;
+ ProcToken( &rInfo );
+ }
+ break;
+ case RtfImportState::SetAttr:
+ break;
+ case RtfImportState::InsertText:
+ break;
+ case RtfImportState::InsertPara:
+ break;
+ default:
+ OSL_FAIL("unknown ImportInfo.eState");
+ }
+}
+
+// Bad behavior:
+// For RTF_INTBL or respectively at the start of the first RTF_CELL
+// after RTF_CELLX if there was no RTF_INTBL
+void ScRTFParser::NewCellRow()
+{
+ if ( bNewDef )
+ {
+ bNewDef = false;
+ // Not flush on the right? => new table
+ if ( nLastWidth && !maDefaultList.empty() )
+ {
+ const ScRTFCellDefault& rD = *maDefaultList.back();
+ if (rD.nTwips != nLastWidth)
+ {
+ SCCOL n1, n2;
+ if ( !( SeekTwips( nLastWidth, &n1 )
+ && SeekTwips( rD.nTwips, &n2 )
+ && n1 == n2
+ )
+ )
+ {
+ ColAdjust();
+ }
+ }
+ }
+ // Build up TwipCols only after nLastWidth comparison!
+ for (const std::unique_ptr<ScRTFCellDefault> & pCellDefault : maDefaultList)
+ {
+ const ScRTFCellDefault& rD = *pCellDefault;
+ SCCOL nCol;
+ if ( !SeekTwips(rD.nTwips, &nCol) )
+ aColTwips.insert( rD.nTwips );
+ }
+ }
+ pDefMerge = nullptr;
+ pActDefault = maDefaultList.empty() ? nullptr : maDefaultList[0].get();
+ mnCurPos = 0;
+ OSL_ENSURE( pActDefault, "NewCellRow: pActDefault==0" );
+}
+
+/*
+ SW:
+ ~~~
+ [\par]
+ \trowd \cellx \cellx ...
+ \intbl \cell \cell ...
+ \row
+ [\par]
+ [\trowd \cellx \cellx ...]
+ \intbl \cell \cell ...
+ \row
+ [\par]
+
+ M$-Word:
+ ~~~~~~~~
+ [\par]
+ \trowd \cellx \cellx ...
+ \intbl \cell \cell ...
+ \intbl \row
+ [\par]
+ [\trowd \cellx \cellx ...]
+ \intbl \cell \cell ...
+ \intbl \row
+ [\par]
+
+ */
+
+void ScRTFParser::ProcToken( RtfImportInfo* pInfo )
+{
+ switch ( pInfo->nToken )
+ {
+ case RTF_TROWD: // denotes table row default, before RTF_CELLX
+ {
+ if (!maDefaultList.empty())
+ nLastWidth = maDefaultList.back()->nTwips;
+
+ nColCnt = 0;
+ if (pActDefault != pInsDefault.get())
+ pActDefault = nullptr;
+ maDefaultList.clear();
+ pDefMerge = nullptr;
+ nRtfLastToken = pInfo->nToken;
+ mnCurPos = 0;
+ }
+ break;
+ case RTF_CLMGF: // The first cell of cells to be merged
+ {
+ pDefMerge = pInsDefault.get();
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CLMRG: // A cell to be merged with the preceding cell
+ {
+ if (!pDefMerge && !maDefaultList.empty())
+ {
+ pDefMerge = maDefaultList.back().get();
+ mnCurPos = maDefaultList.size() - 1;
+ }
+ OSL_ENSURE( pDefMerge, "RTF_CLMRG: pDefMerge==0" );
+ if ( pDefMerge ) // Else broken RTF
+ pDefMerge->nColOverlap++; // multiple successive ones possible
+ pInsDefault->nColOverlap = 0; // Flag: ignore these
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CELLX: // closes cell default
+ {
+ bNewDef = true;
+ pInsDefault->nCol = nColCnt;
+ pInsDefault->nTwips = pInfo->nTokenValue; // Right cell border
+ maDefaultList.push_back( std::move(pInsDefault) );
+ // New free-flying pInsDefault
+ pInsDefault.reset( new ScRTFCellDefault( pPool.get() ) );
+ if ( ++nColCnt > nColMax )
+ nColMax = nColCnt;
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_INTBL: // before the first RTF_CELL
+ {
+ // Once over NextToken and once over UnknownAttrToken
+ // or e.g. \intbl ... \cell \pard \intbl ... \cell
+ if ( nRtfLastToken != RTF_INTBL && nRtfLastToken != RTF_CELL && nRtfLastToken != RTF_PAR )
+ {
+ NewCellRow();
+ nRtfLastToken = pInfo->nToken;
+ }
+ }
+ break;
+ case RTF_CELL: // denotes the end of a cell.
+ {
+ OSL_ENSURE( pActDefault, "RTF_CELL: pActDefault==0" );
+ if ( bNewDef || !pActDefault )
+ NewCellRow(); // before was no \intbl, bad behavior
+ // Broken RTF? Let's save what we can
+ if ( !pActDefault )
+ pActDefault = pInsDefault.get();
+ if ( pActDefault->nColOverlap > 0 )
+ { // Not merged with preceding
+ mxActEntry->nCol = pActDefault->nCol;
+ mxActEntry->nColOverlap = pActDefault->nColOverlap;
+ mxActEntry->nTwips = pActDefault->nTwips;
+ mxActEntry->nRow = nRowCnt;
+ mxActEntry->aItemSet.Set(pActDefault->aItemSet);
+ EntryEnd(mxActEntry.get(), pInfo->aSelection);
+
+ if ( nStartAdjust == sal_uLong(~0) )
+ nStartAdjust = maList.size();
+ maList.push_back(mxActEntry);
+ NewActEntry(mxActEntry.get()); // New free-flying mxActEntry
+ }
+ else
+ { // Assign current Twips to MergeCell
+ if ( !maList.empty() )
+ {
+ auto& pE = maList.back();
+ pE->nTwips = pActDefault->nTwips;
+ }
+ // Adjust selection of free-flying mxActEntry
+ // Paragraph -1 due to separated text in EditEngine during parsing
+ mxActEntry->aSel.nStartPara = pInfo->aSelection.nEndPara - 1;
+ }
+
+ pActDefault = nullptr;
+ if (!maDefaultList.empty() && (mnCurPos+1) < maDefaultList.size())
+ pActDefault = maDefaultList[++mnCurPos].get();
+
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_ROW: // denotes the end of a row
+ {
+ NextRow();
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_PAR: // Paragraph
+ {
+ if ( !pActDefault )
+ { // text not in table
+ ColAdjust(); // close the processing table
+ mxActEntry->nCol = 0;
+ mxActEntry->nRow = nRowCnt;
+ EntryEnd(mxActEntry.get(), pInfo->aSelection);
+ maList.push_back(mxActEntry);
+ NewActEntry(mxActEntry.get()); // new mxActEntry
+ NextRow();
+ }
+ nRtfLastToken = pInfo->nToken;
+ }
+ break;
+ default:
+ { // do not set nRtfLastToken
+ switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
+ {
+ case RTF_SHADINGDEF:
+ static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBackgroundAttr(
+ pInfo->nToken, pInsDefault->aItemSet, true );
+ break;
+ case RTF_BRDRDEF:
+ static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBorderAttr(
+ pInfo->nToken, pInsDefault->aItemSet, true );
+ break;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xcl97/XclExpChangeTrack.cxx b/sc/source/filter/xcl97/XclExpChangeTrack.cxx
new file mode 100644
index 000000000..2ef1b5bce
--- /dev/null
+++ b/sc/source/filter/xcl97/XclExpChangeTrack.cxx
@@ -0,0 +1,1647 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <numeric>
+#include <stdio.h>
+#include <sot/storage.hxx>
+#include <XclExpChangeTrack.hxx>
+#include <xeformula.hxx>
+#include <xehelper.hxx>
+#include <xltools.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <editutil.hxx>
+#include <root.hxx>
+#include <tools/Guid.hxx>
+
+#include <oox/export/utils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/tokens.hxx>
+#include <rtl/uuid.h>
+#include <svl/sharedstring.hxx>
+
+using namespace oox;
+
+static OString lcl_DateTimeToOString( const DateTime& rDateTime )
+{
+ char sBuf[ 200 ];
+ snprintf( sBuf, sizeof( sBuf ),
+ "%d-%02d-%02dT%02d:%02d:%02d.%09" SAL_PRIuUINT32 "Z",
+ rDateTime.GetYear(), rDateTime.GetMonth(), rDateTime.GetDay(),
+ rDateTime.GetHour(), rDateTime.GetMin(), rDateTime.GetSec(),
+ rDateTime.GetNanoSec() );
+ return OString(sBuf);
+}
+
+// local functions
+
+static void lcl_WriteDateTime( XclExpStream& rStrm, const DateTime& rDateTime )
+{
+ rStrm.SetSliceSize( 7 );
+ rStrm << static_cast<sal_uInt16>(rDateTime.GetYear())
+ << static_cast<sal_uInt8>(rDateTime.GetMonth())
+ << static_cast<sal_uInt8>(rDateTime.GetDay())
+ << static_cast<sal_uInt8>(rDateTime.GetHour())
+ << static_cast<sal_uInt8>(rDateTime.GetMin())
+ << static_cast<sal_uInt8>(rDateTime.GetSec());
+ rStrm.SetSliceSize( 0 );
+}
+
+// write string and fill rest of <nLength> with zero bytes
+// <nLength> is without string header
+static void lcl_WriteFixedString( XclExpStream& rStrm, const XclExpString& rString, std::size_t nLength )
+{
+ std::size_t nStrBytes = rString.GetBufferSize();
+ OSL_ENSURE( nLength >= nStrBytes, "lcl_WriteFixedString - String too long" );
+ if( rString.Len() > 0 )
+ rStrm << rString;
+ if( nLength > nStrBytes )
+ rStrm.WriteZeroBytes( nLength - nStrBytes );
+}
+
+static void lcl_GenerateGUID( sal_uInt8* pGUID, bool& rValidGUID )
+{
+ rtl_createUuid( pGUID, rValidGUID ? pGUID : nullptr, false );
+ rValidGUID = true;
+}
+
+static void lcl_WriteGUID( XclExpStream& rStrm, const sal_uInt8* pGUID )
+{
+ rStrm.SetSliceSize( 16 );
+ for( std::size_t nIndex = 0; nIndex < 16; nIndex++ )
+ rStrm << pGUID[ nIndex ];
+ rStrm.SetSliceSize( 0 );
+}
+
+XclExpUserBView::XclExpUserBView( const OUString& rUsername, const sal_uInt8* pGUID ) :
+ sUsername( rUsername )
+{
+ memcpy( aGUID, pGUID, 16 );
+}
+
+void XclExpUserBView::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt32(0xFF078014)
+ << sal_uInt32(0x00000001);
+ lcl_WriteGUID( rStrm, aGUID );
+ rStrm.WriteZeroBytes( 8 );
+ rStrm << sal_uInt32(1200)
+ << sal_uInt32(1000)
+ << sal_uInt16(1000)
+ << sal_uInt16(0x0CF7)
+ << sal_uInt16(0x0000)
+ << sal_uInt16(0x0001)
+ << sal_uInt16(0x0000);
+ if( sUsername.Len() > 0 )
+ rStrm << sUsername;
+}
+
+sal_uInt16 XclExpUserBView::GetNum() const
+{
+ return 0x01A9;
+}
+
+std::size_t XclExpUserBView::GetLen() const
+{
+ return 50 + ((sUsername.Len() > 0) ? sUsername.GetSize() : 0);
+}
+
+XclExpUserBViewList::XclExpUserBViewList( const ScChangeTrack& rChangeTrack )
+{
+ sal_uInt8 aGUID[ 16 ];
+ bool bValidGUID = false;
+ const std::set<OUString>& rStrColl = rChangeTrack.GetUserCollection();
+ aViews.reserve(rStrColl.size());
+ for (const auto& rStr : rStrColl)
+ {
+ lcl_GenerateGUID( aGUID, bValidGUID );
+ aViews.emplace_back( rStr, aGUID );
+ }
+}
+
+XclExpUserBViewList::~XclExpUserBViewList()
+{
+}
+
+void XclExpUserBViewList::Save( XclExpStream& rStrm )
+{
+ for( XclExpUserBView& rView : aViews )
+ rView.Save( rStrm );
+}
+
+XclExpUsersViewBegin::XclExpUsersViewBegin( const sal_uInt8* pGUID, sal_uInt32 nTab ) :
+ nCurrTab( nTab )
+{
+ memcpy( aGUID, pGUID, 16 );
+}
+
+void XclExpUsersViewBegin::SaveCont( XclExpStream& rStrm )
+{
+ lcl_WriteGUID( rStrm, aGUID );
+ rStrm << nCurrTab
+ << sal_uInt32(100)
+ << sal_uInt32(64)
+ << sal_uInt32(3)
+ << sal_uInt32(0x0000003C)
+ << sal_uInt16(0)
+ << sal_uInt16(3)
+ << sal_uInt16(0)
+ << sal_uInt16(3)
+ << double(0)
+ << double(0)
+ << sal_Int16(-1)
+ << sal_Int16(-1);
+}
+
+sal_uInt16 XclExpUsersViewBegin::GetNum() const
+{
+ return 0x01AA;
+}
+
+std::size_t XclExpUsersViewBegin::GetLen() const
+{
+ return 64;
+}
+
+void XclExpUsersViewEnd::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0001);
+}
+
+sal_uInt16 XclExpUsersViewEnd::GetNum() const
+{
+ return 0x01AB;
+}
+
+std::size_t XclExpUsersViewEnd::GetLen() const
+{
+ return 2;
+}
+
+void XclExpChTr0x0191::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0000);
+}
+
+sal_uInt16 XclExpChTr0x0191::GetNum() const
+{
+ return 0x0191;
+}
+
+std::size_t XclExpChTr0x0191::GetLen() const
+{
+ return 2;
+}
+
+void XclExpChTr0x0198::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0006)
+ << sal_uInt16(0x0000);
+}
+
+sal_uInt16 XclExpChTr0x0198::GetNum() const
+{
+ return 0x0198;
+}
+
+std::size_t XclExpChTr0x0198::GetLen() const
+{
+ return 4;
+}
+
+void XclExpChTr0x0192::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16( 0x0022 );
+ rStrm.WriteZeroBytes( 510 );
+}
+
+sal_uInt16 XclExpChTr0x0192::GetNum() const
+{
+ return 0x0192;
+}
+
+std::size_t XclExpChTr0x0192::GetLen() const
+{
+ return 512;
+}
+
+void XclExpChTr0x0197::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0000);
+}
+
+sal_uInt16 XclExpChTr0x0197::GetNum() const
+{
+ return 0x0197;
+}
+
+std::size_t XclExpChTr0x0197::GetLen() const
+{
+ return 2;
+}
+
+XclExpChTrEmpty::~XclExpChTrEmpty()
+{
+}
+
+sal_uInt16 XclExpChTrEmpty::GetNum() const
+{
+ return nRecNum;
+}
+
+std::size_t XclExpChTrEmpty::GetLen() const
+{
+ return 0;
+}
+
+XclExpChTr0x0195::~XclExpChTr0x0195()
+{
+}
+
+void XclExpChTr0x0195::SaveCont( XclExpStream& rStrm )
+{
+ rStrm.WriteZeroBytes( 162 );
+}
+
+sal_uInt16 XclExpChTr0x0195::GetNum() const
+{
+ return 0x0195;
+}
+
+std::size_t XclExpChTr0x0195::GetLen() const
+{
+ return 162;
+}
+
+XclExpChTr0x0194::~XclExpChTr0x0194()
+{
+}
+
+void XclExpChTr0x0194::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt32(0);
+ lcl_WriteDateTime( rStrm, aDateTime );
+ rStrm << sal_uInt8(0);
+ lcl_WriteFixedString( rStrm, sUsername, 147 );
+}
+
+sal_uInt16 XclExpChTr0x0194::GetNum() const
+{
+ return 0x0194;
+}
+
+std::size_t XclExpChTr0x0194::GetLen() const
+{
+ return 162;
+}
+
+XclExpChTrHeader::~XclExpChTrHeader()
+{
+}
+
+void XclExpChTrHeader::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16(0x0006)
+ << sal_uInt16(0x0000)
+ << sal_uInt16(0x000D);
+ lcl_WriteGUID( rStrm, aGUID );
+ lcl_WriteGUID( rStrm, aGUID );
+ rStrm << nCount
+ << sal_uInt16(0x0001)
+ << sal_uInt32(0x00000000)
+ << sal_uInt16(0x001E);
+}
+
+sal_uInt16 XclExpChTrHeader::GetNum() const
+{
+ return 0x0196;
+}
+
+std::size_t XclExpChTrHeader::GetLen() const
+{
+ return 50;
+}
+
+void XclExpChTrHeader::SaveXml( XclExpXmlStream& rRevisionHeadersStrm )
+{
+ sax_fastparser::FSHelperPtr pHeaders = rRevisionHeadersStrm.GetCurrentStream();
+ tools::Guid aGuid(aGUID);
+ rRevisionHeadersStrm.WriteAttributes(
+ XML_guid, aGuid.getString(),
+ XML_lastGuid, nullptr, // OOXTODO
+ XML_shared, nullptr, // OOXTODO
+ XML_diskRevisions, nullptr, // OOXTODO
+ XML_history, nullptr, // OOXTODO
+ XML_trackRevisions, nullptr, // OOXTODO
+ XML_exclusive, nullptr, // OOXTODO
+ XML_revisionId, nullptr, // OOXTODO
+ XML_version, nullptr, // OOXTODO
+ XML_keepChangeHistory, nullptr, // OOXTODO
+ XML_protected, nullptr, // OOXTODO
+ XML_preserveHistory, nullptr); // OOXTODO
+ pHeaders->write( ">" );
+}
+
+void XclExpXmlChTrHeaders::SetGUID( const sal_uInt8* pGUID )
+{
+ memcpy(maGUID, pGUID, 16);
+}
+
+void XclExpXmlChTrHeaders::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pHeaders = rStrm.GetCurrentStream();
+
+ pHeaders->write("<")->writeId(XML_headers);
+
+ tools::Guid aGuid(maGUID);
+ rStrm.WriteAttributes(
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)),
+ XML_guid, aGuid.getString(),
+ XML_lastGuid, nullptr, // OOXTODO
+ XML_shared, nullptr, // OOXTODO
+ XML_diskRevisions, nullptr, // OOXTODO
+ XML_history, nullptr, // OOXTODO
+ XML_trackRevisions, nullptr, // OOXTODO
+ XML_exclusive, nullptr, // OOXTODO
+ XML_revisionId, nullptr, // OOXTODO
+ XML_version, nullptr, // OOXTODO
+ XML_keepChangeHistory, nullptr, // OOXTODO
+ XML_protected, nullptr, // OOXTODO
+ XML_preserveHistory, nullptr); // OOXTODO
+
+ pHeaders->write(">");
+}
+
+XclExpXmlChTrHeader::XclExpXmlChTrHeader(
+ const OUString& rUserName, const DateTime& rDateTime, const sal_uInt8* pGUID,
+ sal_Int32 nLogNumber, const XclExpChTrTabIdBuffer& rBuf ) :
+ maUserName(rUserName), maDateTime(rDateTime), mnLogNumber(nLogNumber),
+ mnMinAction(0), mnMaxAction(0)
+{
+ memcpy(maGUID, pGUID, 16);
+ if (rBuf.GetBufferCount())
+ {
+ maTabBuffer.resize(rBuf.GetBufferCount());
+ rBuf.GetBufferCopy(maTabBuffer.data());
+ }
+}
+
+void XclExpXmlChTrHeader::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pHeader = rStrm.GetCurrentStream();
+
+ pHeader->write("<")->writeId(XML_header);
+
+ OUString aRelId;
+ sax_fastparser::FSHelperPtr pRevLogStrm = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName("xl/revisions/", "revisionLog", mnLogNumber),
+ XclXmlUtils::GetStreamName(nullptr, "revisionLog", mnLogNumber),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("revisionLog"),
+ &aRelId);
+
+ tools::Guid aGuid(maGUID);
+ rStrm.WriteAttributes(
+ XML_guid, aGuid.getString(),
+ XML_dateTime, lcl_DateTimeToOString(maDateTime),
+ XML_userName, maUserName,
+ FSNS(XML_r, XML_id), aRelId);
+
+ if (mnMinAction)
+ rStrm.WriteAttributes(XML_minRId, OUString::number(mnMinAction));
+
+ if (mnMaxAction)
+ rStrm.WriteAttributes(XML_maxRId, OUString::number(mnMaxAction));
+
+ if (!maTabBuffer.empty())
+ // next available sheet index.
+ rStrm.WriteAttributes(XML_maxSheetId, OUString::number(maTabBuffer.back()+1));
+
+ pHeader->write(">");
+
+ if (!maTabBuffer.empty())
+ {
+ // Write sheet index map.
+ size_t n = maTabBuffer.size();
+ pHeader->startElement(XML_sheetIdMap, XML_count, OString::number(n));
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ pHeader->singleElement(XML_sheetId, XML_val, OString::number(maTabBuffer[i]));
+ }
+ pHeader->endElement(XML_sheetIdMap);
+ }
+
+ // Write all revision logs in a separate stream.
+
+ rStrm.PushStream(pRevLogStrm);
+
+ pRevLogStrm->write("<")->writeId(XML_revisions);
+
+ rStrm.WriteAttributes(
+ XML_xmlns, rStrm.getNamespaceURL(OOX_NS(xls)),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)));
+
+ pRevLogStrm->write(">");
+
+ for (const auto& rxAction : maActions)
+ {
+ rxAction->SaveXml(rStrm);
+ }
+
+ pRevLogStrm->write("</")->writeId(XML_revisions)->write(">");
+
+ rStrm.PopStream();
+
+ pHeader->write("</")->writeId(XML_header)->write(">");
+}
+
+void XclExpXmlChTrHeader::AppendAction( std::unique_ptr<XclExpChTrAction> pAction )
+{
+ sal_uInt32 nActionNum = pAction->GetActionNumber();
+ if (!mnMinAction || mnMinAction > nActionNum)
+ mnMinAction = nActionNum;
+
+ if (!mnMaxAction || mnMaxAction < nActionNum)
+ mnMaxAction = nActionNum;
+
+ maActions.push_back(std::move(pAction));
+}
+
+XclExpChTrInfo::XclExpChTrInfo( const OUString& rUsername, const DateTime& rDateTime, const sal_uInt8* pGUID ) :
+ sUsername( rUsername ),
+ aDateTime( rDateTime )
+{
+ memcpy( aGUID, pGUID, 16 );
+}
+
+XclExpChTrInfo::~XclExpChTrInfo()
+{
+}
+
+void XclExpChTrInfo::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt32(0xFFFFFFFF)
+ << sal_uInt32(0x00000000)
+ << sal_uInt32(0x00000020)
+ << sal_uInt16(0xFFFF);
+ lcl_WriteGUID( rStrm, aGUID );
+ rStrm << sal_uInt16(0x04B0);
+ lcl_WriteFixedString( rStrm, sUsername, 113 );
+ lcl_WriteDateTime( rStrm, aDateTime );
+ rStrm << sal_uInt8(0x0000)
+ << sal_uInt16(0x0002);
+}
+
+sal_uInt16 XclExpChTrInfo::GetNum() const
+{
+ return 0x0138;
+}
+
+std::size_t XclExpChTrInfo::GetLen() const
+{
+ return 158;
+}
+
+XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( sal_uInt16 nCount ) :
+ nBufSize( nCount ),
+ nLastId( nCount )
+{
+ pBuffer.reset( new sal_uInt16[ nBufSize ] );
+ memset( pBuffer.get(), 0, sizeof(sal_uInt16) * nBufSize );
+ pLast = pBuffer.get() + nBufSize - 1;
+}
+
+XclExpChTrTabIdBuffer::XclExpChTrTabIdBuffer( const XclExpChTrTabIdBuffer& rCopy ) :
+ nBufSize( rCopy.nBufSize ),
+ nLastId( rCopy.nLastId )
+{
+ pBuffer.reset( new sal_uInt16[ nBufSize ] );
+ memcpy( pBuffer.get(), rCopy.pBuffer.get(), sizeof(sal_uInt16) * nBufSize );
+ pLast = pBuffer.get() + nBufSize - 1;
+}
+
+XclExpChTrTabIdBuffer::~XclExpChTrTabIdBuffer()
+{
+}
+
+void XclExpChTrTabIdBuffer::InitFill( sal_uInt16 nIndex )
+{
+ OSL_ENSURE( nIndex < nLastId, "XclExpChTrTabIdBuffer::Insert - out of range" );
+
+ sal_uInt16 nFreeCount = 0;
+ for( sal_uInt16* pElem = pBuffer.get(); pElem <= pLast; pElem++ )
+ {
+ if( !*pElem )
+ nFreeCount++;
+ if( nFreeCount > nIndex )
+ {
+ *pElem = nLastId--;
+ return;
+ }
+ }
+}
+
+void XclExpChTrTabIdBuffer::InitFillup()
+{
+ sal_uInt16 nFreeCount = 1;
+ for( sal_uInt16* pElem = pBuffer.get(); pElem <= pLast; pElem++ )
+ if( !*pElem )
+ *pElem = nFreeCount++;
+ nLastId = nBufSize;
+}
+
+sal_uInt16 XclExpChTrTabIdBuffer::GetId( sal_uInt16 nIndex ) const
+{
+ OSL_ENSURE( nIndex < nBufSize, "XclExpChTrTabIdBuffer::GetId - out of range" );
+ return pBuffer[ nIndex ];
+}
+
+void XclExpChTrTabIdBuffer::Remove()
+{
+ OSL_ENSURE( pBuffer.get() <= pLast, "XclExpChTrTabIdBuffer::Remove - buffer empty" );
+ sal_uInt16* pElem = pBuffer.get();
+ while( (pElem <= pLast) && (*pElem != nLastId) )
+ pElem++;
+ while( pElem < pLast )
+ {
+ *pElem = *(pElem + 1);
+ pElem++;
+ }
+ pLast--;
+ nLastId--;
+}
+
+XclExpChTrTabId::XclExpChTrTabId( const XclExpChTrTabIdBuffer& rBuffer )
+ : nTabCount( rBuffer.GetBufferCount() )
+{
+ pBuffer.reset( new sal_uInt16[ nTabCount ] );
+ rBuffer.GetBufferCopy( pBuffer.get() );
+}
+
+XclExpChTrTabId::~XclExpChTrTabId()
+{
+ Clear();
+}
+
+void XclExpChTrTabId::Copy( const XclExpChTrTabIdBuffer& rBuffer )
+{
+ Clear();
+ nTabCount = rBuffer.GetBufferCount();
+ pBuffer.reset( new sal_uInt16[ nTabCount ] );
+ rBuffer.GetBufferCopy( pBuffer.get() );
+}
+
+void XclExpChTrTabId::SaveCont( XclExpStream& rStrm )
+{
+ rStrm.EnableEncryption();
+ if( pBuffer )
+ rStrm.Write(pBuffer.get(), nTabCount);
+ else
+ for( sal_uInt16 nIndex = 1; nIndex <= nTabCount; nIndex++ )
+ rStrm << nIndex;
+}
+
+sal_uInt16 XclExpChTrTabId::GetNum() const
+{
+ return 0x013D;
+}
+
+std::size_t XclExpChTrTabId::GetLen() const
+{
+ return nTabCount << 1;
+}
+
+// ! does not copy additional actions
+XclExpChTrAction::XclExpChTrAction( const XclExpChTrAction& rCopy ) :
+ ExcRecord( rCopy ),
+ sUsername( rCopy.sUsername ),
+ aDateTime( rCopy.aDateTime ),
+ nIndex( 0 ),
+ bAccepted( rCopy.bAccepted ),
+ rTabInfo( rCopy.rTabInfo ),
+ rIdBuffer( rCopy.rIdBuffer ),
+ nLength( rCopy.nLength ),
+ nOpCode( rCopy.nOpCode ),
+ bForceInfo( rCopy.bForceInfo )
+{
+}
+
+XclExpChTrAction::XclExpChTrAction(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ sal_uInt16 nNewOpCode ) :
+ sUsername( rAction.GetUser() ),
+ aDateTime( rAction.GetDateTime() ),
+ nIndex( 0 ),
+ bAccepted( rAction.IsAccepted() ),
+ rTabInfo( rRoot.GetTabInfo() ),
+ rIdBuffer( rTabIdBuffer ),
+ nLength( 0 ),
+ nOpCode( nNewOpCode ),
+ bForceInfo( false )
+{
+ aDateTime.SetSec( 0 );
+ aDateTime.SetNanoSec( 0 );
+}
+
+XclExpChTrAction::~XclExpChTrAction()
+{
+}
+
+void XclExpChTrAction::SetAddAction( XclExpChTrAction* pAction )
+{
+ if( pAddAction )
+ pAddAction->SetAddAction( pAction );
+ else
+ pAddAction.reset( pAction );
+}
+
+void XclExpChTrAction::AddDependentContents(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const ScChangeTrack& rChangeTrack )
+{
+ ScChangeActionMap aActionMap;
+
+ rChangeTrack.GetDependents( const_cast<ScChangeAction*>(&rAction), aActionMap );
+ for( const auto& rEntry : aActionMap )
+ if( rEntry.second->GetType() == SC_CAT_CONTENT )
+ SetAddAction( new XclExpChTrCellContent(
+ *static_cast<const ScChangeActionContent*>(rEntry.second), rRoot, rIdBuffer ) );
+}
+
+void XclExpChTrAction::SetIndex( sal_uInt32& rIndex )
+{
+ nIndex = rIndex++;
+}
+
+void XclExpChTrAction::SaveCont( XclExpStream& rStrm )
+{
+ OSL_ENSURE( nOpCode != EXC_CHTR_OP_UNKNOWN, "XclExpChTrAction::SaveCont - unknown action" );
+ rStrm << nLength
+ << nIndex
+ << nOpCode
+ << static_cast<sal_uInt16>(bAccepted ? EXC_CHTR_ACCEPT : EXC_CHTR_NOTHING);
+ SaveActionData( rStrm );
+}
+
+void XclExpChTrAction::PrepareSaveAction( XclExpStream& /*rStrm*/ ) const
+{
+}
+
+void XclExpChTrAction::CompleteSaveAction( XclExpStream& /*rStrm*/ ) const
+{
+}
+
+void XclExpChTrAction::Save( XclExpStream& rStrm )
+{
+ PrepareSaveAction( rStrm );
+ ExcRecord::Save( rStrm );
+ if( pAddAction )
+ pAddAction->Save( rStrm );
+ CompleteSaveAction( rStrm );
+}
+
+std::size_t XclExpChTrAction::GetLen() const
+{
+ return GetHeaderByteCount() + GetActionByteCount();
+}
+
+XclExpChTrData::XclExpChTrData() :
+ mpFormulaCell( nullptr ),
+ fValue( 0.0 ),
+ nRKValue( 0 ),
+ nType( EXC_CHTR_TYPE_EMPTY ),
+ nSize( 0 )
+{
+}
+
+XclExpChTrData::~XclExpChTrData()
+{
+ Clear();
+}
+
+void XclExpChTrData::Clear()
+{
+ pString.reset();
+ mpFormulaCell = nullptr;
+ mxTokArr.reset();
+ maRefLog.clear();
+ fValue = 0.0;
+ nRKValue = 0;
+ nType = EXC_CHTR_TYPE_EMPTY;
+ nSize = 0;
+}
+
+void XclExpChTrData::WriteFormula( XclExpStream& rStrm, const XclExpChTrTabIdBuffer& rTabIdBuffer )
+{
+ OSL_ENSURE( mxTokArr && !mxTokArr->Empty(), "XclExpChTrData::Write - no formula" );
+ rStrm << *mxTokArr;
+
+ for( const auto& rLogEntry : maRefLog )
+ {
+ if( rLogEntry.mpUrl && rLogEntry.mpFirstTab )
+ {
+ rStrm << *rLogEntry.mpUrl << sal_uInt8(0x01) << *rLogEntry.mpFirstTab << sal_uInt8(0x02);
+ }
+ else
+ {
+ bool bSingleTab = rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab;
+ rStrm.SetSliceSize( bSingleTab ? 6 : 8 );
+ rStrm << sal_uInt8(0x01) << sal_uInt8(0x02) << sal_uInt8(0x00);
+ rStrm << rTabIdBuffer.GetId( rLogEntry.mnFirstXclTab );
+ if( bSingleTab )
+ rStrm << sal_uInt8(0x02);
+ else
+ rStrm << sal_uInt8(0x00) << rTabIdBuffer.GetId( rLogEntry.mnLastXclTab );
+ }
+ }
+ rStrm.SetSliceSize( 0 );
+ rStrm << sal_uInt8(0x00);
+}
+
+void XclExpChTrData::Write( XclExpStream& rStrm, const XclExpChTrTabIdBuffer& rTabIdBuffer )
+{
+ switch( nType )
+ {
+ case EXC_CHTR_TYPE_RK:
+ rStrm << nRKValue;
+ break;
+ case EXC_CHTR_TYPE_DOUBLE:
+ rStrm << fValue;
+ break;
+ case EXC_CHTR_TYPE_STRING:
+ OSL_ENSURE( pString, "XclExpChTrData::Write - no string" );
+ rStrm << *pString;
+ break;
+ case EXC_CHTR_TYPE_FORMULA:
+ WriteFormula( rStrm, rTabIdBuffer );
+ break;
+ }
+}
+
+XclExpChTrCellContent::XclExpChTrCellContent(
+ const ScChangeActionContent& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer ) :
+ XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_CELL ),
+ XclExpRoot( rRoot ),
+ aPosition( rAction.GetBigRange().MakeRange( rRoot.GetDoc()).aStart )
+{
+ sal_uInt32 nDummy32;
+ sal_uInt16 nDummy16;
+ GetCellData( rRoot, rAction.GetOldCell(), pOldData, nDummy32, nOldLength );
+ GetCellData( rRoot, rAction.GetNewCell(), pNewData, nLength, nDummy16 );
+}
+
+XclExpChTrCellContent::~XclExpChTrCellContent()
+{
+ pOldData.reset();
+ pNewData.reset();
+}
+
+void XclExpChTrCellContent::MakeEmptyChTrData( std::unique_ptr<XclExpChTrData>& rpData )
+{
+ if( rpData )
+ rpData->Clear();
+ else
+ rpData.reset( new XclExpChTrData );
+}
+
+void XclExpChTrCellContent::GetCellData(
+ const XclExpRoot& rRoot, const ScCellValue& rScCell,
+ std::unique_ptr<XclExpChTrData>& rpData, sal_uInt32& rXclLength1, sal_uInt16& rXclLength2 )
+{
+ MakeEmptyChTrData( rpData );
+ rXclLength1 = 0x0000003A;
+ rXclLength2 = 0x0000;
+
+ if (rScCell.isEmpty())
+ {
+ rpData.reset();
+ return;
+ }
+
+ switch (rScCell.meType)
+ {
+ case CELLTYPE_VALUE:
+ {
+ rpData->fValue = rScCell.mfValue;
+ if( XclTools::GetRKFromDouble( rpData->nRKValue, rpData->fValue ) )
+ {
+ rpData->nType = EXC_CHTR_TYPE_RK;
+ rpData->nSize = 4;
+ rXclLength1 = 0x0000003E;
+ rXclLength2 = 0x0004;
+ }
+ else
+ {
+ rpData->nType = EXC_CHTR_TYPE_DOUBLE;
+ rpData->nSize = 8;
+ rXclLength1 = 0x00000042;
+ rXclLength2 = 0x0008;
+ }
+ }
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ {
+ OUString sCellStr;
+ if (rScCell.meType == CELLTYPE_STRING)
+ {
+ sCellStr = rScCell.mpString->getString();
+ rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
+ rRoot, sCellStr, nullptr);
+ }
+ else
+ {
+ XclExpHyperlinkHelper aLinkHelper( rRoot, aPosition );
+ if (rScCell.mpEditText)
+ {
+ sCellStr = ScEditUtil::GetString(*rScCell.mpEditText, &GetDoc());
+ rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
+ rRoot, *rScCell.mpEditText, nullptr, aLinkHelper);
+ }
+ else
+ {
+ rpData->mpFormattedString = XclExpStringHelper::CreateCellString(
+ rRoot, OUString(), nullptr);
+ }
+ }
+ rpData->pString.reset( new XclExpString( sCellStr, XclStrFlags::NONE, 32766 ) );
+ rpData->nType = EXC_CHTR_TYPE_STRING;
+ rpData->nSize = 3 + rpData->pString->GetSize();
+ rXclLength1 = 64 + (sCellStr.getLength() << 1);
+ rXclLength2 = 6 + static_cast<sal_uInt16>(sCellStr.getLength() << 1);
+ }
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ const ScFormulaCell* pFmlCell = rScCell.mpFormula;
+ rpData->mpFormulaCell = pFmlCell;
+
+ const ScTokenArray* pTokenArray = pFmlCell->GetCode();
+ if( pTokenArray )
+ {
+ XclExpRefLog& rRefLog = rpData->maRefLog;
+ rpData->mxTokArr = GetFormulaCompiler().CreateFormula(
+ EXC_FMLATYPE_CELL, *pTokenArray, &pFmlCell->aPos, &rRefLog );
+ rpData->nType = EXC_CHTR_TYPE_FORMULA;
+ std::size_t nSize = std::accumulate(rRefLog.begin(), rRefLog.end(),
+ static_cast<std::size_t>(rpData->mxTokArr->GetSize() + 3),
+ [](const std::size_t& rSum, const XclExpRefLogEntry& rLogEntry) {
+ if( rLogEntry.mpUrl && rLogEntry.mpFirstTab )
+ return rSum + rLogEntry.mpUrl->GetSize() + rLogEntry.mpFirstTab->GetSize() + 2;
+ else
+ return rSum + ((rLogEntry.mnFirstXclTab == rLogEntry.mnLastXclTab) ? 6 : 8);
+ });
+ rpData->nSize = ::std::min< std::size_t >( nSize, 0xFFFF );
+ rXclLength1 = 0x00000052;
+ rXclLength2 = 0x0018;
+ }
+ }
+ break;
+ default:;
+ }
+}
+
+void XclExpChTrCellContent::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, aPosition.Tab() );
+ rStrm << static_cast<sal_uInt16>((pOldData ? (pOldData->nType << 3) : 0x0000) | (pNewData ? pNewData->nType : 0x0000))
+ << sal_uInt16(0x0000);
+ Write2DAddress( rStrm, aPosition );
+ rStrm << nOldLength
+ << sal_uInt32(0x00000000);
+ if( pOldData )
+ pOldData->Write( rStrm, rIdBuffer );
+ if( pNewData )
+ pNewData->Write( rStrm, rIdBuffer );
+}
+
+sal_uInt16 XclExpChTrCellContent::GetNum() const
+{
+ return 0x013B;
+}
+
+std::size_t XclExpChTrCellContent::GetActionByteCount() const
+{
+ std::size_t nLen = 16;
+ if( pOldData )
+ nLen += pOldData->nSize;
+ if( pNewData )
+ nLen += pNewData->nSize;
+ return nLen;
+}
+
+static const char* lcl_GetType( XclExpChTrData* pData )
+{
+ switch( pData->nType )
+ {
+ case EXC_CHTR_TYPE_RK:
+ case EXC_CHTR_TYPE_DOUBLE:
+ return "n";
+ case EXC_CHTR_TYPE_FORMULA:
+ {
+ ScFormulaCell* pFormulaCell = const_cast< ScFormulaCell* >( pData->mpFormulaCell );
+ const char* sType;
+ OUString sValue;
+ XclXmlUtils::GetFormulaTypeAndValue( *pFormulaCell, sType, sValue );
+ return sType;
+ }
+ break;
+ case EXC_CHTR_TYPE_STRING:
+ return "inlineStr";
+ default:
+ break;
+ }
+ return "*unknown*";
+}
+
+static void lcl_WriteCell( XclExpXmlStream& rStrm, sal_Int32 nElement, const ScAddress& rPosition, XclExpChTrData* pData )
+{
+ sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
+
+ pStream->startElement(nElement,
+ XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), rPosition),
+ XML_s, nullptr, // OOXTODO: not supported
+ XML_t, lcl_GetType(pData),
+ XML_cm, nullptr, // OOXTODO: not supported
+ XML_vm, nullptr, // OOXTODO: not supported
+ XML_ph, nullptr); // OOXTODO: not supported
+ switch( pData->nType )
+ {
+ case EXC_CHTR_TYPE_RK:
+ case EXC_CHTR_TYPE_DOUBLE:
+ pStream->startElement(XML_v);
+ pStream->write( pData->fValue );
+ pStream->endElement( XML_v );
+ break;
+ case EXC_CHTR_TYPE_FORMULA:
+ pStream->startElement( XML_f
+ // OOXTODO: other attributes? see XclExpFormulaCell::SaveXml()
+ );
+ pStream->writeEscaped( XclXmlUtils::ToOUString(
+ rStrm.GetRoot().GetCompileFormulaContext(),
+ pData->mpFormulaCell->aPos, pData->mpFormulaCell->GetCode()));
+ pStream->endElement( XML_f );
+ break;
+ case EXC_CHTR_TYPE_STRING:
+ pStream->startElement(XML_is);
+ if( pData->mpFormattedString )
+ pData->mpFormattedString->WriteXml( rStrm );
+ else
+ pData->pString->WriteXml( rStrm );
+ pStream->endElement( XML_is );
+ break;
+ default:
+ // ignore
+ break;
+ }
+ pStream->endElement( nElement );
+}
+
+void XclExpChTrCellContent::SaveXml( XclExpXmlStream& rRevisionLogStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
+ pStream->startElement( XML_rcc,
+ XML_rId, OString::number(GetActionNumber()),
+ XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
+ XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
+ XML_sId, OString::number(GetTabId(aPosition.Tab())),
+ XML_odxf, nullptr, // OOXTODO: not supported
+ XML_xfDxf, nullptr, // OOXTODO: not supported
+ XML_s, nullptr, // OOXTODO: not supported
+ XML_dxf, nullptr, // OOXTODO: not supported
+ XML_numFmtId, nullptr, // OOXTODO: not supported
+ XML_quotePrefix, nullptr, // OOXTODO: not supported
+ XML_oldQuotePrefix, nullptr, // OOXTODO: not supported
+ XML_ph, nullptr, // OOXTODO: not supported
+ XML_oldPh, nullptr, // OOXTODO: not supported
+ XML_endOfListFormulaUpdate, nullptr); // OOXTODO: not supported
+ if( pOldData )
+ {
+ lcl_WriteCell( rRevisionLogStrm, XML_oc, aPosition, pOldData.get() );
+ if (!pNewData)
+ {
+ pStream->singleElement(XML_nc, XML_r, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aPosition));
+ }
+ }
+ if( pNewData )
+ {
+ lcl_WriteCell( rRevisionLogStrm, XML_nc, aPosition, pNewData.get() );
+ }
+ // OOXTODO: XML_odxf, XML_ndxf, XML_extLst elements
+ pStream->endElement( XML_rcc );
+}
+
+XclExpChTrInsert::XclExpChTrInsert( const XclExpChTrInsert& rCopy ) :
+ XclExpChTrAction(rCopy),
+ mbEndOfList(rCopy.mbEndOfList),
+ aRange(rCopy.aRange) {}
+
+XclExpChTrInsert::XclExpChTrInsert(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ const ScChangeTrack& rChangeTrack ) :
+ XclExpChTrAction( rAction, rRoot, rTabIdBuffer ),
+ mbEndOfList(false),
+ aRange( rAction.GetBigRange().MakeRange( rRoot.GetDoc()) )
+{
+ nLength = 0x00000030;
+ switch( rAction.GetType() )
+ {
+ case SC_CAT_INSERT_COLS: nOpCode = EXC_CHTR_OP_INSCOL; break;
+ case SC_CAT_INSERT_ROWS:
+ {
+ const ScChangeActionIns& rIns = static_cast<const ScChangeActionIns&>(rAction);
+ mbEndOfList = rIns.IsEndOfList();
+ nOpCode = EXC_CHTR_OP_INSROW;
+ }
+ break;
+ case SC_CAT_DELETE_COLS: nOpCode = EXC_CHTR_OP_DELCOL; break;
+ case SC_CAT_DELETE_ROWS: nOpCode = EXC_CHTR_OP_DELROW; break;
+ default:
+ OSL_FAIL( "XclExpChTrInsert::XclExpChTrInsert - unknown action" );
+ }
+
+ if( nOpCode & EXC_CHTR_OP_COLFLAG )
+ {
+ aRange.aStart.SetRow( 0 );
+ aRange.aEnd.SetRow( rRoot.GetXclMaxPos().Row() );
+ }
+ else
+ {
+ aRange.aStart.SetCol( 0 );
+ aRange.aEnd.SetCol( rRoot.GetXclMaxPos().Col() );
+ }
+
+ if( nOpCode & EXC_CHTR_OP_DELFLAG )
+ {
+ SetAddAction( new XclExpChTr0x014A( *this ) );
+ AddDependentContents( rAction, rRoot, rChangeTrack );
+ }
+}
+
+XclExpChTrInsert::~XclExpChTrInsert()
+{
+}
+
+void XclExpChTrInsert::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, aRange.aStart.Tab() );
+ sal_uInt16 nFlagVal = mbEndOfList ? 0x0001 : 0x0000;
+ rStrm << nFlagVal;
+ Write2DRange( rStrm, aRange );
+ rStrm << sal_uInt32(0x00000000);
+}
+
+void XclExpChTrInsert::PrepareSaveAction( XclExpStream& rStrm ) const
+{
+ if( (nOpCode == EXC_CHTR_OP_DELROW) || (nOpCode == EXC_CHTR_OP_DELCOL) )
+ XclExpChTrEmpty( 0x0150 ).Save( rStrm );
+}
+
+void XclExpChTrInsert::CompleteSaveAction( XclExpStream& rStrm ) const
+{
+ if( (nOpCode == EXC_CHTR_OP_DELROW) || (nOpCode == EXC_CHTR_OP_DELCOL) )
+ XclExpChTrEmpty( 0x0151 ).Save( rStrm );
+}
+
+sal_uInt16 XclExpChTrInsert::GetNum() const
+{
+ return 0x0137;
+}
+
+std::size_t XclExpChTrInsert::GetActionByteCount() const
+{
+ return 16;
+}
+
+static const char* lcl_GetAction( sal_uInt16 nOpCode )
+{
+ switch( nOpCode )
+ {
+ case EXC_CHTR_OP_INSCOL: return "insertCol";
+ case EXC_CHTR_OP_INSROW: return "insertRow";
+ case EXC_CHTR_OP_DELCOL: return "deleteCol";
+ case EXC_CHTR_OP_DELROW: return "deleteRow";
+ default: return "*unknown*";
+ }
+}
+
+void XclExpChTrInsert::SaveXml( XclExpXmlStream& rRevisionLogStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
+ pStream->startElement( XML_rrc,
+ XML_rId, OString::number(GetActionNumber()),
+ XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
+ XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
+ XML_sId, OString::number(GetTabId(aRange.aStart.Tab())),
+ XML_eol, ToPsz10(mbEndOfList),
+ XML_ref, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aRange),
+ XML_action, lcl_GetAction( nOpCode ),
+ XML_edge, nullptr); // OOXTODO: ???
+
+ // OOXTODO: does this handle XML_rfmt, XML_undo?
+ XclExpChTrAction* pAction = GetAddAction();
+ while( pAction != nullptr )
+ {
+ pAction->SaveXml( rRevisionLogStrm );
+ pAction = pAction->GetAddAction();
+ }
+ pStream->endElement( XML_rrc );
+}
+
+XclExpChTrInsertTab::XclExpChTrInsertTab(
+ const ScChangeAction& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer ) :
+ XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_INSTAB ),
+ XclExpRoot( rRoot ),
+ nTab( static_cast<SCTAB>(rAction.GetBigRange().aStart.Tab()) )
+{
+ nLength = 0x0000021C;
+ bForceInfo = true;
+}
+
+XclExpChTrInsertTab::~XclExpChTrInsertTab()
+{
+}
+
+void XclExpChTrInsertTab::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, nTab );
+ rStrm << sal_uInt32( 0 );
+ lcl_WriteFixedString( rStrm, XclExpString( GetTabInfo().GetScTabName( nTab ) ), 127 );
+ lcl_WriteDateTime( rStrm, GetDateTime() );
+ rStrm.WriteZeroBytes( 133 );
+}
+
+sal_uInt16 XclExpChTrInsertTab::GetNum() const
+{
+ return 0x014D;
+}
+
+std::size_t XclExpChTrInsertTab::GetActionByteCount() const
+{
+ return 276;
+}
+
+void XclExpChTrInsertTab::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
+ pStream->singleElement( XML_ris,
+ XML_rId, OString::number(GetActionNumber()),
+ XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
+ XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
+ XML_sheetId, OString::number(GetTabId(nTab)),
+ XML_name, GetTabInfo().GetScTabName(nTab).toUtf8(),
+ XML_sheetPosition, OString::number(nTab) );
+}
+
+XclExpChTrMoveRange::XclExpChTrMoveRange(
+ const ScChangeActionMove& rAction,
+ const XclExpRoot& rRoot,
+ const XclExpChTrTabIdBuffer& rTabIdBuffer,
+ const ScChangeTrack& rChangeTrack ) :
+ XclExpChTrAction( rAction, rRoot, rTabIdBuffer, EXC_CHTR_OP_MOVE ),
+ aDestRange( rAction.GetBigRange().MakeRange( rRoot.GetDoc() ) )
+{
+ nLength = 0x00000042;
+ aSourceRange = aDestRange;
+ sal_Int32 nDCols, nDRows, nDTabs;
+ rAction.GetDelta( nDCols, nDRows, nDTabs );
+ aSourceRange.aStart.IncRow( static_cast<SCROW>(-nDRows) );
+ aSourceRange.aStart.IncCol( static_cast<SCCOL>(-nDCols) );
+ aSourceRange.aStart.IncTab( static_cast<SCTAB>(-nDTabs) );
+ aSourceRange.aEnd.IncRow( static_cast<SCROW>(-nDRows) );
+ aSourceRange.aEnd.IncCol( static_cast<SCCOL>(-nDCols) );
+ aSourceRange.aEnd.IncTab( static_cast<SCTAB>(-nDTabs) );
+ AddDependentContents( rAction, rRoot, rChangeTrack );
+}
+
+XclExpChTrMoveRange::~XclExpChTrMoveRange()
+{
+}
+
+void XclExpChTrMoveRange::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, aDestRange.aStart.Tab() );
+ Write2DRange( rStrm, aSourceRange );
+ Write2DRange( rStrm, aDestRange );
+ WriteTabId( rStrm, aSourceRange.aStart.Tab() );
+ rStrm << sal_uInt32(0x00000000);
+}
+
+void XclExpChTrMoveRange::PrepareSaveAction( XclExpStream& rStrm ) const
+{
+ XclExpChTrEmpty( 0x014E ).Save( rStrm );
+}
+
+void XclExpChTrMoveRange::CompleteSaveAction( XclExpStream& rStrm ) const
+{
+ XclExpChTrEmpty( 0x014F ).Save( rStrm );
+}
+
+sal_uInt16 XclExpChTrMoveRange::GetNum() const
+{
+ return 0x0140;
+}
+
+std::size_t XclExpChTrMoveRange::GetActionByteCount() const
+{
+ return 24;
+}
+
+void XclExpChTrMoveRange::SaveXml( XclExpXmlStream& rRevisionLogStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rRevisionLogStrm.GetCurrentStream();
+
+ pStream->startElement( XML_rm,
+ XML_rId, OString::number(GetActionNumber()),
+ XML_ua, ToPsz( GetAccepted () ), // OOXTODO? bAccepted == ua or ra; not sure.
+ XML_ra, nullptr, // OOXTODO: RRD.fUndoAction? Or RRD.fAccepted?
+ XML_sheetId, OString::number(GetTabId(aDestRange.aStart.Tab())),
+ XML_source, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aSourceRange),
+ XML_destination, XclXmlUtils::ToOString(rRevisionLogStrm.GetRoot().GetDoc(), aDestRange),
+ XML_sourceSheetId, OString::number(GetTabId(aSourceRange.aStart.Tab())) );
+ // OOXTODO: does this handle XML_rfmt, XML_undo?
+ XclExpChTrAction* pAction = GetAddAction();
+ while( pAction != nullptr )
+ {
+ pAction->SaveXml( rRevisionLogStrm );
+ pAction = pAction->GetAddAction();
+ }
+ pStream->endElement( XML_rm );
+}
+
+XclExpChTr0x014A::XclExpChTr0x014A( const XclExpChTrInsert& rAction ) :
+ XclExpChTrInsert( rAction )
+{
+ nLength = 0x00000026;
+ nOpCode = EXC_CHTR_OP_FORMAT;
+}
+
+XclExpChTr0x014A::~XclExpChTr0x014A()
+{
+}
+
+void XclExpChTr0x014A::SaveActionData( XclExpStream& rStrm ) const
+{
+ WriteTabId( rStrm, aRange.aStart.Tab() );
+ rStrm << sal_uInt16(0x0003)
+ << sal_uInt16(0x0001);
+ Write2DRange( rStrm, aRange );
+}
+
+sal_uInt16 XclExpChTr0x014A::GetNum() const
+{
+ return 0x014A;
+}
+
+std::size_t XclExpChTr0x014A::GetActionByteCount() const
+{
+ return 14;
+}
+
+void XclExpChTr0x014A::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
+
+ pStream->startElement( XML_rfmt,
+ XML_sheetId, OString::number(GetTabId(aRange.aStart.Tab())),
+ XML_xfDxf, nullptr, // OOXTODO: not supported
+ XML_s, nullptr, // OOXTODO: style
+ XML_sqref, XclXmlUtils::ToOString(rStrm.GetRoot().GetDoc(), aRange),
+ XML_start, nullptr, // OOXTODO: for string changes
+ XML_length, nullptr); // OOXTODO: for string changes
+ // OOXTODO: XML_dxf, XML_extLst
+
+ pStream->endElement( XML_rfmt );
+}
+
+std::size_t ExcXmlRecord::GetLen() const
+{
+ return 0;
+}
+
+sal_uInt16 ExcXmlRecord::GetNum() const
+{
+ return 0;
+}
+
+void ExcXmlRecord::Save( XclExpStream& )
+{
+ // Do nothing; ignored for BIFF output.
+}
+
+namespace {
+
+class EndXmlElement : public ExcXmlRecord
+{
+ sal_Int32 mnElement;
+public:
+ explicit EndXmlElement( sal_Int32 nElement ) : mnElement( nElement) {}
+ virtual void SaveXml( XclExpXmlStream& rStrm ) override;
+};
+
+}
+
+void EndXmlElement::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr pStream = rStrm.GetCurrentStream();
+ pStream->write("</")->writeId(mnElement)->write(">");
+}
+
+XclExpChangeTrack::XclExpChangeTrack( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot ),
+ pTabIdBuffer( nullptr )
+{
+ OSL_ENSURE( GetOldRoot().pTabId, "XclExpChangeTrack::XclExpChangeTrack - root data incomplete" );
+ if( !GetOldRoot().pTabId )
+ return;
+
+ ScChangeTrack* pTempChangeTrack = CreateTempChangeTrack();
+ if (!pTempChangeTrack)
+ return;
+
+ pTabIdBuffer = new XclExpChTrTabIdBuffer( GetTabInfo().GetXclTabCount() );
+ maBuffers.push_back( std::unique_ptr<XclExpChTrTabIdBuffer>(pTabIdBuffer) );
+
+ // calculate final table order (tab id list)
+ const ScChangeAction* pScAction;
+ for( pScAction = pTempChangeTrack->GetLast(); pScAction; pScAction = pScAction->GetPrev() )
+ {
+ if( pScAction->GetType() == SC_CAT_INSERT_TABS )
+ {
+ SCTAB nScTab = static_cast< SCTAB >( pScAction->GetBigRange().aStart.Tab() );
+ pTabIdBuffer->InitFill( GetTabInfo().GetXclTab( nScTab ) );
+ }
+ }
+ pTabIdBuffer->InitFillup();
+ GetOldRoot().pTabId->Copy( *pTabIdBuffer );
+
+ // get actions in reverse order
+ pScAction = pTempChangeTrack->GetLast();
+ while( pScAction )
+ {
+ PushActionRecord( *pScAction );
+ const ScChangeAction* pPrevAction = pScAction->GetPrev();
+ pScAction = pPrevAction;
+ }
+
+ // build record list
+ if (GetOutput() == EXC_OUTPUT_BINARY)
+ {
+ XclExpChTrHeader* pHeader = new XclExpChTrHeader; // header record for last GUID
+ maRecList.push_back( std::unique_ptr<ExcRecord>(pHeader) );
+ maRecList.push_back( std::unique_ptr<ExcRecord>( new XclExpChTr0x0195 ) );
+ maRecList.push_back( std::unique_ptr<ExcRecord>( new XclExpChTr0x0194( *pTempChangeTrack ) ) );
+
+ OUString sLastUsername;
+ DateTime aLastDateTime( DateTime::EMPTY );
+ sal_uInt32 nIndex = 1;
+ sal_uInt8 aGUID[ 16 ]; // GUID for action info records
+ bool bValidGUID = false;
+ while( !aActionStack.empty() )
+ {
+ XclExpChTrAction* pAction = aActionStack.top();
+ aActionStack.pop();
+
+ if( (nIndex == 1) || pAction->ForceInfoRecord() ||
+ (pAction->GetUsername() != sLastUsername) ||
+ (pAction->GetDateTime() != aLastDateTime) )
+ {
+ lcl_GenerateGUID( aGUID, bValidGUID );
+ sLastUsername = pAction->GetUsername();
+ aLastDateTime = pAction->GetDateTime();
+
+ maRecList.push_back( std::unique_ptr<ExcRecord>(new XclExpChTrInfo(sLastUsername, aLastDateTime, aGUID)) );
+ maRecList.push_back( std::unique_ptr<ExcRecord>(new XclExpChTrTabId(pAction->GetTabIdBuffer())) );
+ pHeader->SetGUID( aGUID );
+ }
+ pAction->SetIndex( nIndex );
+ maRecList.push_back( std::unique_ptr<ExcRecord>(pAction) );
+ }
+
+ pHeader->SetGUID( aGUID );
+ pHeader->SetCount( nIndex - 1 );
+ maRecList.push_back( std::unique_ptr<ExcRecord>(new ExcEof) );
+ }
+ else
+ {
+ XclExpXmlChTrHeaders* pHeaders = new XclExpXmlChTrHeaders;
+ maRecList.push_back( std::unique_ptr<ExcRecord>(pHeaders));
+
+ OUString sLastUsername;
+ DateTime aLastDateTime(DateTime::EMPTY);
+ sal_uInt32 nIndex = 1;
+ sal_Int32 nLogNumber = 1;
+ XclExpXmlChTrHeader* pCurHeader = nullptr;
+ sal_uInt8 aGUID[ 16 ]; // GUID for action info records
+ bool bValidGUID = false;
+
+ while (!aActionStack.empty())
+ {
+ XclExpChTrAction* pAction = aActionStack.top();
+ aActionStack.pop();
+
+ if( (nIndex == 1) || pAction->ForceInfoRecord() ||
+ (pAction->GetUsername() != sLastUsername) ||
+ (pAction->GetDateTime() != aLastDateTime) )
+ {
+ lcl_GenerateGUID( aGUID, bValidGUID );
+ sLastUsername = pAction->GetUsername();
+ aLastDateTime = pAction->GetDateTime();
+
+ pCurHeader = new XclExpXmlChTrHeader(sLastUsername, aLastDateTime, aGUID, nLogNumber, pAction->GetTabIdBuffer());
+ maRecList.push_back( std::unique_ptr<ExcRecord>(pCurHeader));
+ nLogNumber++;
+ pHeaders->SetGUID(aGUID);
+ }
+ pAction->SetIndex(nIndex);
+ pCurHeader->AppendAction(std::unique_ptr<XclExpChTrAction>(pAction));
+ }
+
+ pHeaders->SetGUID(aGUID);
+ maRecList.push_back( std::unique_ptr<ExcRecord>(new EndXmlElement(XML_headers)));
+ }
+}
+
+XclExpChangeTrack::~XclExpChangeTrack()
+{
+ while( !aActionStack.empty() )
+ {
+ delete aActionStack.top();
+ aActionStack.pop();
+ }
+}
+
+ScChangeTrack* XclExpChangeTrack::CreateTempChangeTrack()
+{
+ // get original change track
+ ScChangeTrack* pOrigChangeTrack = GetDoc().GetChangeTrack();
+ OSL_ENSURE( pOrigChangeTrack, "XclExpChangeTrack::CreateTempChangeTrack - no change track data" );
+ if( !pOrigChangeTrack )
+ return nullptr;
+
+ assert(!xTempDoc);
+ // create empty document
+ xTempDoc.reset(new ScDocument);
+
+ // adjust table count
+ SCTAB nOrigCount = GetDoc().GetTableCount();
+ OUString sTabName;
+ for( sal_Int32 nIndex = 0; nIndex < nOrigCount; nIndex++ )
+ {
+ xTempDoc->CreateValidTabName(sTabName);
+ xTempDoc->InsertTab(SC_TAB_APPEND, sTabName);
+ }
+ OSL_ENSURE(nOrigCount == xTempDoc->GetTableCount(),
+ "XclExpChangeTrack::CreateTempChangeTrack - table count mismatch");
+ if(nOrigCount != xTempDoc->GetTableCount())
+ return nullptr;
+
+ return pOrigChangeTrack->Clone(xTempDoc.get());
+}
+
+void XclExpChangeTrack::PushActionRecord( const ScChangeAction& rAction )
+{
+ XclExpChTrAction* pXclAction = nullptr;
+ ScChangeTrack* pTempChangeTrack = xTempDoc->GetChangeTrack();
+ switch( rAction.GetType() )
+ {
+ case SC_CAT_CONTENT:
+ pXclAction = new XclExpChTrCellContent( static_cast<const ScChangeActionContent&>(rAction), GetRoot(), *pTabIdBuffer );
+ break;
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_COLS:
+ if (pTempChangeTrack)
+ pXclAction = new XclExpChTrInsert( rAction, GetRoot(), *pTabIdBuffer, *pTempChangeTrack );
+ break;
+ case SC_CAT_INSERT_TABS:
+ {
+ pXclAction = new XclExpChTrInsertTab( rAction, GetRoot(), *pTabIdBuffer );
+ XclExpChTrTabIdBuffer* pNewBuffer = new XclExpChTrTabIdBuffer( *pTabIdBuffer );
+ pNewBuffer->Remove();
+ maBuffers.push_back( std::unique_ptr<XclExpChTrTabIdBuffer>(pNewBuffer) );
+ pTabIdBuffer = pNewBuffer;
+ }
+ break;
+ case SC_CAT_MOVE:
+ if (pTempChangeTrack)
+ pXclAction = new XclExpChTrMoveRange( static_cast<const ScChangeActionMove&>(rAction), GetRoot(), *pTabIdBuffer, *pTempChangeTrack );
+ break;
+ default:;
+ }
+ if( pXclAction )
+ aActionStack.push( pXclAction );
+}
+
+bool XclExpChangeTrack::WriteUserNamesStream()
+{
+ bool bRet = false;
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( EXC_STREAM_USERNAMES );
+ OSL_ENSURE( xSvStrm.is(), "XclExpChangeTrack::WriteUserNamesStream - no stream" );
+ if( xSvStrm.is() )
+ {
+ XclExpStream aXclStrm( *xSvStrm, GetRoot() );
+ XclExpChTr0x0191().Save( aXclStrm );
+ XclExpChTr0x0198().Save( aXclStrm );
+ XclExpChTr0x0192().Save( aXclStrm );
+ XclExpChTr0x0197().Save( aXclStrm );
+ xSvStrm->Commit();
+ bRet = true;
+ }
+ return bRet;
+}
+
+void XclExpChangeTrack::Write()
+{
+ if (maRecList.empty())
+ return;
+
+ if( !WriteUserNamesStream() )
+ return;
+
+ tools::SvRef<SotStorageStream> xSvStrm = OpenStream( EXC_STREAM_REVLOG );
+ OSL_ENSURE( xSvStrm.is(), "XclExpChangeTrack::Write - no stream" );
+ if( xSvStrm.is() )
+ {
+ XclExpStream aXclStrm( *xSvStrm, GetRoot(), EXC_MAXRECSIZE_BIFF8 + 8 );
+
+ for(const auto& rxRec : maRecList)
+ rxRec->Save(aXclStrm);
+
+ xSvStrm->Commit();
+ }
+}
+
+static void lcl_WriteUserNamesXml( XclExpXmlStream& rWorkbookStrm )
+{
+ sax_fastparser::FSHelperPtr pUserNames = rWorkbookStrm.CreateOutputStream(
+ "xl/revisions/userNames.xml",
+ u"revisions/userNames.xml",
+ rWorkbookStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("usernames"));
+ pUserNames->startElement( XML_users,
+ XML_xmlns, rWorkbookStrm.getNamespaceURL(OOX_NS(xls)).toUtf8(),
+ FSNS( XML_xmlns, XML_r ), rWorkbookStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
+ XML_count, "0" );
+ // OOXTODO: XML_userinfo elements for each user editing the file
+ // Doesn't seem to be supported by .xls output either (based on
+ // contents of XclExpChangeTrack::WriteUserNamesStream()).
+ pUserNames->endElement( XML_users );
+}
+
+void XclExpChangeTrack::WriteXml( XclExpXmlStream& rWorkbookStrm )
+{
+ if (maRecList.empty())
+ return;
+
+ lcl_WriteUserNamesXml( rWorkbookStrm );
+
+ sax_fastparser::FSHelperPtr pRevisionHeaders = rWorkbookStrm.CreateOutputStream(
+ "xl/revisions/revisionHeaders.xml",
+ u"revisions/revisionHeaders.xml",
+ rWorkbookStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml",
+ CREATE_OFFICEDOC_RELATION_TYPE("revisionHeaders"));
+ // OOXTODO: XML_userinfo elements for each user editing the file
+ // Doesn't seem to be supported by .xls output either (based on
+ // contents of XclExpChangeTrack::WriteUserNamesStream()).
+ rWorkbookStrm.PushStream( pRevisionHeaders );
+
+ for (const auto& rxRec : maRecList)
+ rxRec->SaveXml(rWorkbookStrm);
+
+ rWorkbookStrm.PopStream();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xcl97/XclImpChangeTrack.cxx b/sc/source/filter/xcl97/XclImpChangeTrack.cxx
new file mode 100644
index 000000000..7c183da24
--- /dev/null
+++ b/sc/source/filter/xcl97/XclImpChangeTrack.cxx
@@ -0,0 +1,517 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <XclImpChangeTrack.hxx>
+#include <sot/storage.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sal/log.hxx>
+#include <chgviset.hxx>
+#include <formulacell.hxx>
+#include <chgtrack.hxx>
+#include <xihelper.hxx>
+#include <xilink.hxx>
+#include <externalrefmgr.hxx>
+#include <document.hxx>
+#include <documentimport.hxx>
+#include <excdefs.hxx>
+
+
+XclImpChangeTrack::XclImpChangeTrack( const XclImpRoot& rRoot, const XclImpStream& rBookStrm ) :
+ XclImpRoot( rRoot ),
+ aRecHeader(),
+ nTabIdCount( 0 ),
+ bGlobExit( false ),
+ eNestedMode( nmBase )
+{
+ // Verify that the User Names stream exists before going any further. Excel adds both
+ // "Revision Log" and "User Names" streams when Change Tracking is active but the Revision log
+ // remains if Change Tracking is turned off.
+ tools::SvRef<SotStorageStream> xUserStrm = OpenStream( EXC_STREAM_USERNAMES );
+ if( !xUserStrm.is() )
+ return;
+
+ xInStrm = OpenStream( EXC_STREAM_REVLOG );
+ if( !xInStrm.is() )
+ return;
+
+ sal_uInt64 const nStreamLen = xInStrm->TellEnd();
+ if( (xInStrm->GetErrorCode() == ERRCODE_NONE) && (nStreamLen != STREAM_SEEK_TO_END) )
+ {
+ xInStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ pStrm.reset( new XclImpStream( *xInStrm, GetRoot() ) );
+ pStrm->CopyDecrypterFrom( rBookStrm );
+ pChangeTrack.reset(new ScChangeTrack( GetDoc() ));
+
+ sOldUsername = pChangeTrack->GetUser();
+ pChangeTrack->SetUseFixDateTime( true );
+
+ ReadRecords();
+ }
+}
+
+XclImpChangeTrack::~XclImpChangeTrack()
+{
+ pChangeTrack.reset();
+ pStrm.reset();
+}
+
+void XclImpChangeTrack::DoAcceptRejectAction( ScChangeAction* pAction )
+{
+ if( !pAction ) return;
+ switch( aRecHeader.nAccept )
+ {
+ case EXC_CHTR_ACCEPT:
+ pChangeTrack->Accept( pAction );
+ break;
+ case EXC_CHTR_REJECT:
+ break;
+ }
+}
+
+void XclImpChangeTrack::DoAcceptRejectAction( sal_uInt32 nFirst, sal_uInt32 nLast )
+{
+ for( sal_uInt32 nIndex = nFirst; nIndex <= nLast; nIndex++ )
+ DoAcceptRejectAction( pChangeTrack->GetAction( nIndex ) );
+}
+
+void XclImpChangeTrack::DoInsertRange( const ScRange& rRange, bool bEndOfList )
+{
+ sal_uInt32 nFirst = pChangeTrack->GetActionMax() + 1;
+ pChangeTrack->AppendInsert(rRange, bEndOfList);
+ sal_uInt32 nLast = pChangeTrack->GetActionMax();
+ DoAcceptRejectAction( nFirst, nLast );
+}
+
+void XclImpChangeTrack::DoDeleteRange( const ScRange& rRange )
+{
+ sal_uLong nFirst, nLast;
+ pChangeTrack->AppendDeleteRange( rRange, &GetDoc() , nFirst, nLast );
+ DoAcceptRejectAction( nFirst, nLast );
+}
+
+SCTAB XclImpChangeTrack::ReadTabNum()
+{
+ return static_cast<SCTAB>(GetTabInfo().GetCurrentIndex(
+ pStrm->ReaduInt16(), nTabIdCount ));
+}
+
+void XclImpChangeTrack::ReadDateTime( DateTime& rDateTime )
+{
+ sal_uInt16 nYear;
+ sal_uInt8 nMonth, nDay, nHour, nMin, nSec;
+
+ nYear = pStrm->ReaduInt16();
+ nMonth = pStrm->ReaduInt8();
+ nDay = pStrm->ReaduInt8();
+ nHour = pStrm->ReaduInt8();
+ nMin = pStrm->ReaduInt8();
+ nSec = pStrm->ReaduInt8();
+
+ rDateTime.SetYear( nYear );
+ rDateTime.SetMonth( nMonth );
+ rDateTime.SetDay( nDay );
+ rDateTime.SetHour( nHour );
+ rDateTime.SetMin( nMin );
+ rDateTime.SetSec( nSec );
+ rDateTime.SetNanoSec( 0 );
+}
+
+bool XclImpChangeTrack::CheckRecord( sal_uInt16 nOpCode )
+{
+ if( (nOpCode != EXC_CHTR_OP_UNKNOWN) && (aRecHeader.nOpCode != nOpCode) )
+ {
+ OSL_FAIL( "XclImpChangeTrack::CheckRecord - unknown action" );
+ return false;
+ }
+ return aRecHeader.nIndex != 0;
+}
+
+void XclImpChangeTrack::Read3DTabRefInfo( SCTAB& rFirstTab, SCTAB& rLastTab, ExcelToSc8::ExternalTabInfo& rExtInfo )
+{
+ if( LookAtuInt8() == 0x01 )
+ {
+ rExtInfo.mbExternal = false;
+ // internal ref - read tab num and return sc tab num (position in TABID list)
+ pStrm->Ignore( 3 );
+ rFirstTab = static_cast< SCTAB >( GetTabInfo().GetCurrentIndex( pStrm->ReaduInt16(), nTabIdCount ) );
+ sal_uInt8 nFillByte = pStrm->ReaduInt8();
+ rLastTab = (nFillByte == 0x00) ?
+ static_cast< SCTAB >( GetTabInfo().GetCurrentIndex( pStrm->ReaduInt16(), nTabIdCount ) ) : rFirstTab;
+ }
+ else
+ {
+ // external ref - read doc and tab name and find sc tab num
+ // - URL
+ OUString aEncUrl( pStrm->ReadUniString() );
+ OUString aUrl;
+ bool bSelf;
+ XclImpUrlHelper::DecodeUrl( aUrl, bSelf, GetRoot(), aEncUrl );
+ pStrm->Ignore( 1 );
+ // - sheet name, always separated from URL
+ OUString aTabName( pStrm->ReadUniString() );
+ pStrm->Ignore( 1 );
+
+ rExtInfo.mbExternal = true;
+ ScExternalRefManager* pRefMgr = GetDoc().GetExternalRefManager();
+ pRefMgr->convertToAbsName(aUrl);
+ rExtInfo.mnFileId = pRefMgr->getExternalFileId(aUrl);
+ rExtInfo.maTabName = aTabName;
+ rFirstTab = rLastTab = 0;
+ }
+}
+
+void XclImpChangeTrack::ReadFormula( std::unique_ptr<ScTokenArray>& rpTokenArray, const ScAddress& rPosition )
+{
+ sal_uInt16 nFmlSize = pStrm->ReaduInt16();
+
+ // create a memory stream and copy the formula to be able to read simultaneously
+ // the formula and the additional 3D tab ref data following the formula
+ // here we have to simulate an Excel record to be able to use an XclImpStream...
+ // 2do: remove the stream member from formula converter and add it as a parameter
+ // to the Convert() routine (to prevent the construction/destruction of the
+ // converter in each formula)
+ SvMemoryStream aMemStrm;
+ aMemStrm.WriteUInt16( 0x0001 ).WriteUInt16( nFmlSize );
+ size_t nRead = pStrm->CopyToStream( aMemStrm, nFmlSize );
+
+ // survive reading invalid streams!
+ // if we can't read as many bytes as required just don't use them and
+ // assume that this part is broken
+ if(nRead != nFmlSize)
+ {
+ rpTokenArray = nullptr;
+ pStrm->Ignore(1);
+ return;
+ }
+
+ XclImpStream aFmlaStrm( aMemStrm, GetRoot() );
+ aFmlaStrm.StartNextRecord();
+ XclImpChTrFmlConverter aFmlConv( GetRoot(), *this );
+
+ // read the formula, 3D tab refs from extended data
+ std::unique_ptr<ScTokenArray> pArray;
+ aFmlConv.Reset( rPosition );
+ bool bOK = (aFmlConv.Convert( pArray, aFmlaStrm, nFmlSize, false ) == ConvErr::OK); // JEG : Check This
+ rpTokenArray = (bOK && pArray) ? std::move( pArray ) : nullptr;
+ pStrm->Ignore( 1 );
+}
+
+void XclImpChangeTrack::ReadCell(
+ ScCellValue& rCell, sal_uInt32& rFormat, sal_uInt16 nFlags, const ScAddress& rPosition )
+{
+ rCell.clear();
+ rFormat = 0;
+ switch( nFlags & EXC_CHTR_TYPE_MASK )
+ {
+ case EXC_CHTR_TYPE_EMPTY:
+ break;
+ case EXC_CHTR_TYPE_RK:
+ {
+ double fValue = XclTools::GetDoubleFromRK( pStrm->ReadInt32() );
+ if( pStrm->IsValid() )
+ {
+ rCell.meType = CELLTYPE_VALUE;
+ rCell.mfValue = fValue;
+ }
+ }
+ break;
+ case EXC_CHTR_TYPE_DOUBLE:
+ {
+ double fValue = pStrm->ReadDouble();
+ if( pStrm->IsValid() )
+ {
+ rCell.meType = CELLTYPE_VALUE;
+ rCell.mfValue = fValue;
+ }
+ }
+ break;
+ case EXC_CHTR_TYPE_STRING:
+ {
+ OUString sString = pStrm->ReadUniString();
+ if( pStrm->IsValid() )
+ {
+ rCell.meType = CELLTYPE_STRING;
+ rCell.mpString = new svl::SharedString(GetDoc().GetSharedStringPool().intern(sString));
+ }
+ }
+ break;
+ case EXC_CHTR_TYPE_BOOL:
+ {
+ double fValue = static_cast<double>(pStrm->ReaduInt16() != 0);
+ if( pStrm->IsValid() )
+ {
+ rCell.meType = CELLTYPE_VALUE;
+ rCell.mfValue = fValue;
+ rFormat = GetFormatter().GetStandardFormat( SvNumFormatType::LOGICAL, ScGlobal::eLnge );
+ }
+ }
+ break;
+ case EXC_CHTR_TYPE_FORMULA:
+ {
+ std::unique_ptr<ScTokenArray> pTokenArray;
+ ReadFormula( pTokenArray, rPosition );
+ if( pStrm->IsValid() && pTokenArray )
+ {
+ rCell.meType = CELLTYPE_FORMULA;
+ rCell.mpFormula = new ScFormulaCell(GetDoc(), rPosition, std::move(pTokenArray));
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "XclImpChangeTrack::ReadCell - unknown data type" );
+ }
+}
+
+void XclImpChangeTrack::ReadChTrInsert()
+{
+ *pStrm >> aRecHeader;
+ if( !CheckRecord( EXC_CHTR_OP_UNKNOWN ) )
+ return;
+
+ if( (aRecHeader.nOpCode != EXC_CHTR_OP_INSROW) &&
+ (aRecHeader.nOpCode != EXC_CHTR_OP_INSCOL) &&
+ (aRecHeader.nOpCode != EXC_CHTR_OP_DELROW) &&
+ (aRecHeader.nOpCode != EXC_CHTR_OP_DELCOL) )
+ {
+ OSL_FAIL( "XclImpChangeTrack::ReadChTrInsert - unknown action" );
+ return;
+ }
+
+ ScRange aRange;
+ aRange.aStart.SetTab( ReadTabNum() );
+ aRange.aEnd.SetTab( aRange.aStart.Tab() );
+ sal_uInt16 nFlags = pStrm->ReaduInt16();
+ bool bEndOfList = (nFlags & 0x0001); // row auto-inserted at the bottom.
+ Read2DRange( aRange );
+
+ if( aRecHeader.nOpCode & EXC_CHTR_OP_COLFLAG )
+ aRange.aEnd.SetRow( GetDocImport().getDoc().MaxRow() );
+ else
+ aRange.aEnd.SetCol( GetDocImport().getDoc().MaxCol() );
+
+ bool bValid = pStrm->IsValid();
+ if( FoundNestedMode() )
+ ReadNestedRecords();
+
+ if( bValid )
+ {
+ if( aRecHeader.nOpCode & EXC_CHTR_OP_DELFLAG )
+ DoDeleteRange( aRange );
+ else
+ DoInsertRange(aRange, bEndOfList);
+ }
+}
+
+void XclImpChangeTrack::ReadChTrInfo()
+{
+ pStrm->DisableDecryption();
+ pStrm->Ignore( 32 );
+ OUString sUsername( pStrm->ReadUniString() );
+ if( !pStrm->IsValid() ) return;
+
+ if( !sUsername.isEmpty() )
+ pChangeTrack->SetUser( sUsername );
+ pStrm->Seek( 148 );
+ if( !pStrm->IsValid() ) return;
+
+ DateTime aDateTime( DateTime::EMPTY );
+ ReadDateTime( aDateTime );
+ if( pStrm->IsValid() )
+ pChangeTrack->SetFixDateTimeLocal( aDateTime );
+}
+
+void XclImpChangeTrack::ReadChTrCellContent()
+{
+ *pStrm >> aRecHeader;
+ if( !CheckRecord( EXC_CHTR_OP_CELL ) )
+ return;
+
+ ScAddress aPosition;
+ SCTAB nTab = ReadTabNum();
+ aPosition.SetTab( nTab );
+ sal_uInt16 nValueType;
+ nValueType = pStrm->ReaduInt16();
+ sal_uInt16 nOldValueType = (nValueType >> 3) & EXC_CHTR_TYPE_MASK;
+ sal_uInt16 nNewValueType = nValueType & EXC_CHTR_TYPE_MASK;
+ pStrm->Ignore( 2 );
+ Read2DAddress( aPosition );
+ sal_uInt16 nOldSize;
+ nOldSize = pStrm->ReaduInt16();
+ SAL_WARN_IF( (nOldSize == 0) != (nOldValueType == EXC_CHTR_TYPE_EMPTY),
+ "sc.filter",
+ "XclImpChangeTrack::ReadChTrCellContent - old value mismatch" );
+ pStrm->Ignore( 4 );
+ switch( nValueType & EXC_CHTR_TYPE_FORMATMASK )
+ {
+ case 0x0000: break;
+ case 0x1100: pStrm->Ignore( 16 ); break;
+ case 0x1300: pStrm->Ignore( 8 ); break;
+ default: OSL_FAIL( "XclImpChangeTrack::ReadChTrCellContent - unknown format info" );
+ }
+
+ ScCellValue aOldCell;
+ ScCellValue aNewCell;
+ sal_uInt32 nOldFormat;
+ sal_uInt32 nNewFormat;
+ ReadCell(aOldCell, nOldFormat, nOldValueType, aPosition);
+ ReadCell(aNewCell, nNewFormat, nNewValueType, aPosition);
+ if( !pStrm->IsValid() || (pStrm->GetRecLeft() > 0) )
+ {
+ OSL_FAIL( "XclImpChangeTrack::ReadChTrCellContent - bytes left, action ignored" );
+ aOldCell.clear();
+ aNewCell.clear();
+ }
+ else
+ {
+ ScChangeActionContent* pNewAction =
+ pChangeTrack->AppendContentOnTheFly(aPosition, aOldCell, aNewCell, nOldFormat, nNewFormat);
+ DoAcceptRejectAction( pNewAction );
+ }
+}
+
+void XclImpChangeTrack::ReadChTrTabId()
+{
+ if( nTabIdCount == 0 ) // read only 1st time, otherwise calculated by <ReadChTrInsertTab()>
+ nTabIdCount = static_cast< sal_uInt16 >( pStrm->GetRecLeft() >> 1 );
+}
+
+void XclImpChangeTrack::ReadChTrMoveRange()
+{
+ *pStrm >> aRecHeader;
+ if( !CheckRecord( EXC_CHTR_OP_MOVE ) )
+ return;
+
+ ScRange aSourceRange;
+ ScRange aDestRange;
+ aDestRange.aStart.SetTab( ReadTabNum() );
+ aDestRange.aEnd.SetTab( aDestRange.aStart.Tab() );
+ Read2DRange( aSourceRange );
+ Read2DRange( aDestRange );
+ aSourceRange.aStart.SetTab( ReadTabNum() );
+ aSourceRange.aEnd.SetTab( aSourceRange.aStart.Tab() );
+
+ bool bValid = pStrm->IsValid();
+ if( FoundNestedMode() )
+ ReadNestedRecords();
+
+ if( bValid )
+ {
+ pChangeTrack->AppendMove( aSourceRange, aDestRange, nullptr );
+ DoAcceptRejectAction( pChangeTrack->GetLast() );
+ }
+}
+
+void XclImpChangeTrack::ReadChTrInsertTab()
+{
+ *pStrm >> aRecHeader;
+ if( CheckRecord( EXC_CHTR_OP_INSTAB ) )
+ {
+ SCTAB nTab = ReadTabNum();
+ if( pStrm->IsValid() )
+ {
+ nTabIdCount++;
+ DoInsertRange(ScRange(0, 0, nTab, GetDocImport().getDoc().MaxCol(), GetDocImport().getDoc().MaxRow(), nTab), false);
+ }
+ }
+}
+
+void XclImpChangeTrack::InitNestedMode()
+{
+ OSL_ENSURE( eNestedMode == nmBase, "XclImpChangeTrack::InitNestedMode - unexpected nested mode" );
+ if( eNestedMode == nmBase )
+ eNestedMode = nmFound;
+}
+
+void XclImpChangeTrack::ReadNestedRecords()
+{
+ OSL_ENSURE( eNestedMode == nmFound, "XclImpChangeTrack::StartNestedMode - missing nested mode" );
+ if( eNestedMode == nmFound )
+ {
+ eNestedMode = nmNested;
+ ReadRecords();
+ }
+}
+
+bool XclImpChangeTrack::EndNestedMode()
+{
+ OSL_ENSURE( eNestedMode != nmBase, "XclImpChangeTrack::EndNestedMode - missing nested mode" );
+ bool bReturn = (eNestedMode == nmNested);
+ eNestedMode = nmBase;
+ return bReturn;
+}
+
+void XclImpChangeTrack::ReadRecords()
+{
+ bool bExitLoop = false;
+
+ while( !bExitLoop && !bGlobExit && pStrm->StartNextRecord() )
+ {
+ switch( pStrm->GetRecId() )
+ {
+ case 0x000A: bGlobExit = true; break;
+ case 0x0137: ReadChTrInsert(); break;
+ case 0x0138: ReadChTrInfo(); break;
+ case 0x013B: ReadChTrCellContent(); break;
+ case 0x013D: ReadChTrTabId(); break;
+ case 0x0140: ReadChTrMoveRange(); break;
+ case 0x014D: ReadChTrInsertTab(); break;
+ case 0x014E:
+ case 0x0150: InitNestedMode(); break;
+ case 0x014F:
+ case 0x0151: bExitLoop = EndNestedMode(); break;
+ }
+ }
+}
+
+void XclImpChangeTrack::Apply()
+{
+ if( pChangeTrack )
+ {
+ pChangeTrack->SetUser( sOldUsername );
+ pChangeTrack->SetUseFixDateTime( false );
+
+ GetDoc().SetChangeTrack( std::move(pChangeTrack) );
+
+ ScChangeViewSettings aSettings;
+ aSettings.SetShowChanges( true );
+ GetDoc().SetChangeViewSettings( aSettings );
+ }
+}
+
+
+XclImpChTrFmlConverter::XclImpChTrFmlConverter(
+ XclImpRoot& rRoot, XclImpChangeTrack& rXclChTr ) :
+ ExcelToSc8( rRoot ),
+ rChangeTrack( rXclChTr ) {}
+
+XclImpChTrFmlConverter::~XclImpChTrFmlConverter()
+{
+}
+
+// virtual, called from ExcToSc8::Convert()
+bool XclImpChTrFmlConverter::Read3DTabReference( sal_uInt16 /*nIxti*/, SCTAB& rFirstTab, SCTAB& rLastTab,
+ ExternalTabInfo& rExtInfo )
+{
+ rChangeTrack.Read3DTabRefInfo( rFirstTab, rLastTab, rExtInfo );
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xcl97/xcl97esc.cxx b/sc/source/filter/xcl97/xcl97esc.cxx
new file mode 100644
index 000000000..1ecb0891d
--- /dev/null
+++ b/sc/source/filter/xcl97/xcl97esc.cxx
@@ -0,0 +1,574 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/embed/XClassifiedObject.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <svx/svdpage.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <svx/sdasitm.hxx>
+#include <sfx2/docfile.hxx>
+#include <sal/log.hxx>
+
+#include <sot/exchange.hxx>
+#include <sot/storage.hxx>
+#include <xeescher.hxx>
+
+#include <drwlayer.hxx>
+#include <xecontent.hxx>
+#include <editeng/flditem.hxx>
+#include <userdat.hxx>
+#include <xcl97rec.hxx>
+#include <xcl97esc.hxx>
+#include <unotools/streamwrap.hxx>
+#include <oox/ole/olehelper.hxx>
+#include <sfx2/objsh.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::container::XIndexAccess;
+using ::com::sun::star::embed::XClassifiedObject;
+using ::com::sun::star::drawing::XShape;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::form::XFormsSupplier;
+using ::com::sun::star::io::XOutputStream;
+using ::com::sun::star::script::ScriptEventDescriptor;
+using ::com::sun::star::script::XEventAttacherManager;
+
+XclEscherExGlobal::XclEscherExGlobal( const XclExpRoot& rRoot ) :
+ XclExpRoot( rRoot )
+{
+ SetBaseURI( GetMedium().GetBaseURL( true ) );
+}
+
+SvStream* XclEscherExGlobal::ImplQueryPictureStream()
+{
+ mxPicTempFile.reset( new ::utl::TempFile );
+ if( mxPicTempFile->IsValid() )
+ {
+ mxPicTempFile->EnableKillingFile();
+ mxPicStrm = ::utl::UcbStreamHelper::CreateStream( mxPicTempFile->GetURL(), StreamMode::STD_READWRITE );
+ mxPicStrm->SetEndian( SvStreamEndian::LITTLE );
+ }
+ return mxPicStrm.get();
+}
+
+XclEscherEx::XclEscherEx( const XclExpRoot& rRoot, XclExpObjectManager& rObjMgr, SvStream& rStrm, const XclEscherEx* pParent ) :
+ EscherEx( pParent ? pParent->mxGlobal : std::make_shared<XclEscherExGlobal>( rRoot ), &rStrm ),
+ XclExpRoot( rRoot ),
+ mrObjMgr( rObjMgr ),
+ pCurrXclObj( nullptr ),
+ pTheClientData( new XclEscherClientData ),
+ pAdditionalText( nullptr ),
+ nAdditionalText( 0 ),
+ mnNextKey( 0 ),
+ mbIsRootDff( pParent == nullptr )
+{
+ InsertPersistOffset( mnNextKey, 0 );
+}
+
+XclEscherEx::~XclEscherEx()
+{
+ OSL_ENSURE( aStack.empty(), "~XclEscherEx: stack not empty" );
+ DeleteCurrAppData();
+ pTheClientData.reset();
+}
+
+sal_uInt32 XclEscherEx::InitNextDffFragment()
+{
+ /* Current value of mnNextKey will be used by caller to refer to the
+ starting point of the DFF fragment. The key exists already in the
+ PersistTable (has been inserted by c'tor of previous call of
+ InitNextDffFragment(), has been updated by UpdateDffFragmentEnd(). */
+ sal_uInt32 nPersistKey = mnNextKey;
+
+ /* Prepare the next key that is used by caller as end point of the DFF
+ fragment. Will be updated by caller when writing to the DFF stream,
+ using the UpdateDffFragmentEnd() function. This is needed to find DFF
+ data written by the SVX base class implementation without interaction,
+ e.g. the solver container that will be written after the last shape. */
+ ++mnNextKey;
+ InsertPersistOffset( mnNextKey, mpOutStrm->Tell() );
+
+ return nPersistKey;
+}
+
+void XclEscherEx::UpdateDffFragmentEnd()
+{
+ // update existing fragment key with new stream position
+ ReplacePersistOffset( mnNextKey, mpOutStrm->Tell() );
+}
+
+sal_uInt32 XclEscherEx::GetDffFragmentPos( sal_uInt32 nFragmentKey )
+{
+ /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
+ is non-const due to tools/List usage. */
+ return GetPersistOffset( nFragmentKey );
+}
+
+sal_uInt32 XclEscherEx::GetDffFragmentSize( sal_uInt32 nFragmentKey )
+{
+ /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
+ is non-const due to tools/List usage. */
+ return GetDffFragmentPos( nFragmentKey + 1 ) - GetDffFragmentPos( nFragmentKey );
+}
+
+bool XclEscherEx::HasPendingDffData()
+{
+ /* TODO: this function is non-const because PersistTable::PtGetOffsetByID()
+ is non-const due to tools/List usage. */
+ return GetDffFragmentPos( mnNextKey ) < GetStreamPos();
+}
+
+XclExpDffAnchorBase* XclEscherEx::CreateDffAnchor( const SdrObject& rSdrObj ) const
+{
+ // the object manager creates the correct anchor type according to context
+ XclExpDffAnchorBase* pAnchor = mrObjMgr.CreateDffAnchor();
+ // pass the drawing object, that will calculate the anchor position
+ pAnchor->SetSdrObject( rSdrObj );
+ return pAnchor;
+}
+
+namespace {
+
+bool lcl_IsFontwork( const SdrObject* pObj )
+{
+ bool bIsFontwork = false;
+ if( pObj->GetObjIdentifier() == SdrObjKind::CustomShape )
+ {
+ static const OUStringLiteral aTextPath = u"TextPath";
+ const SdrCustomShapeGeometryItem& rGeometryItem =
+ pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ if( const Any* pAny = rGeometryItem.GetPropertyValueByName( aTextPath, aTextPath ) )
+ *pAny >>= bIsFontwork;
+ }
+ return bIsFontwork;
+}
+
+} // namespace
+
+EscherExHostAppData* XclEscherEx::StartShape( const Reference< XShape >& rxShape, const tools::Rectangle* pChildAnchor )
+{
+ if ( nAdditionalText )
+ nAdditionalText++;
+ bool bInGroup = ( pCurrXclObj != nullptr );
+ if ( bInGroup )
+ { // stacked recursive group object
+ if ( !pCurrAppData->IsStackedGroup() )
+ { //! UpdateDffFragmentEnd only once
+ pCurrAppData->SetStackedGroup( true );
+ UpdateDffFragmentEnd();
+ }
+ }
+ aStack.push( std::make_pair( pCurrXclObj, std::move(pCurrAppData) ) );
+ pCurrAppData.reset( new XclEscherHostAppData );
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rxShape);
+ //added for exporting OCX control
+ sal_Int16 nMsCtlType = 0;
+ if ( !pObj )
+ pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() ); // just what is it?!?
+ else
+ {
+ pCurrXclObj = nullptr;
+ SdrObjKind nObjType = pObj->GetObjIdentifier();
+
+ if( nObjType == SdrObjKind::OLE2 )
+ {
+ // no OLE objects in embedded drawings (chart shapes)
+ if( mbIsRootDff )
+ {
+ //! not-const because GetObjRef may load the OLE object
+ Reference < XClassifiedObject > xObj( static_cast<SdrOle2Obj*>(pObj)->GetObjRef() );
+ if ( xObj.is() )
+ {
+ SvGlobalName aObjClsId( xObj->getClassID() );
+ if ( SotExchange::IsChart( aObjClsId ) )
+ { // yes, it's a chart diagram
+ mrObjMgr.AddObj( std::make_unique<XclExpChartObj>( mrObjMgr, rxShape, pChildAnchor, &GetDoc() ) );
+ pCurrXclObj = nullptr; // no metafile or whatsoever
+ }
+ else // metafile and OLE object
+ pCurrXclObj = new XclObjOle( mrObjMgr, *pObj );
+ }
+ else // just a metafile
+ pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() );
+ }
+ else
+ pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() );
+ }
+ else if( nObjType == SdrObjKind::UNO )
+ {
+ //added for exporting OCX control
+ Reference< XPropertySet > xPropSet( rxShape, UNO_QUERY );
+ Any aAny;
+ try
+ {
+ aAny = xPropSet->getPropertyValue("ControlTypeinMSO");
+ aAny >>= nMsCtlType;
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ControlTypeinMSO!");
+ }
+ if( nMsCtlType == 2 ) //OCX Form Control
+ pCurrXclObj = CreateOCXCtrlObj( rxShape, pChildAnchor ).release();
+ else //TBX Form Control
+ pCurrXclObj = CreateTBXCtrlObj( rxShape, pChildAnchor ).release();
+ if( !pCurrXclObj )
+ pCurrXclObj = new XclObjAny( mrObjMgr, rxShape, &GetDoc() ); // just a metafile
+ }
+ else if( !ScDrawLayer::IsNoteCaption( pObj ) )
+ {
+ // ignore permanent note shapes
+ // #i12190# do not ignore callouts (do not filter by object type ID)
+ pCurrXclObj = ShapeInteractionHelper::CreateShapeObj( mrObjMgr, rxShape, &GetDoc() );
+ ShapeInteractionHelper::PopulateShapeInteractionInfo( mrObjMgr, rxShape, *pCurrAppData );
+ }
+ }
+ if ( pCurrXclObj )
+ {
+ if ( !mrObjMgr.AddObj( std::unique_ptr<XclObj>(pCurrXclObj) ) )
+ { // maximum count reached, object got deleted
+ pCurrXclObj = nullptr;
+ }
+ else
+ {
+ pCurrAppData->SetClientData( pTheClientData.get() );
+ if ( nAdditionalText == 0 )
+ {
+ if ( pObj )
+ {
+ if ( !bInGroup )
+ {
+ /* Create a dummy anchor carrying the flags. Real
+ coordinates are calculated later in virtual call of
+ WriteData(EscherEx&,const Rectangle&). */
+ XclExpDffAnchorBase* pAnchor = mrObjMgr.CreateDffAnchor();
+ pAnchor->SetFlags( *pObj );
+ pCurrAppData->SetClientAnchor( pAnchor );
+ }
+ const SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( pObj );
+ if( pTextObj && !lcl_IsFontwork( pTextObj ) && (pObj->GetObjIdentifier() != SdrObjKind::Caption) )
+ {
+ const OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject();
+ if( pParaObj )
+ pCurrAppData->SetClientTextbox(
+ new XclEscherClientTextbox( GetRoot(), *pTextObj, pCurrXclObj ) );
+ }
+ }
+ else
+ {
+ if ( !bInGroup )
+ pCurrAppData->SetClientAnchor( mrObjMgr.CreateDffAnchor() );
+ }
+ }
+ else if ( nAdditionalText == 3 )
+ {
+ if ( pAdditionalText )
+ {
+ pAdditionalText->SetXclObj( pCurrXclObj );
+ pCurrAppData->SetClientTextbox( pAdditionalText );
+ }
+ }
+ }
+ }
+ if(pObj)
+ {
+ //add for exporting OCX control
+ //for OCX control import from MS office file,we need keep the id value as MS office file.
+ //GetOldRoot().pObjRecs->Add( pCurrXclObj ) statement has generated the id value as obj id rule;
+ //but we trick it here.
+ SdrObjKind nObjType = pObj->GetObjIdentifier();
+ if( nObjType == SdrObjKind::UNO && pCurrXclObj )
+ {
+ Reference< XPropertySet > xPropSet( rxShape, UNO_QUERY );
+ Any aAny;
+ try
+ {
+ aAny = xPropSet->getPropertyValue("ObjIDinMSO");
+ }
+ catch(const Exception&)
+ {
+ SAL_WARN("sc", "XclEscherEx::StartShape, this control can't get the property ObjIDinMSO!");
+ }
+ sal_uInt16 nObjIDinMSO = 0xFFFF;
+ aAny >>= nObjIDinMSO;
+ if( nObjIDinMSO != 0xFFFF && nMsCtlType == 2) //OCX
+ {
+ pCurrXclObj->SetId(nObjIDinMSO);
+ }
+ }
+ }
+ if ( !pCurrXclObj )
+ pCurrAppData->SetDontWriteShape( true );
+ return pCurrAppData.get();
+}
+
+void XclEscherEx::EndShape( sal_uInt16 nShapeType, sal_uInt32 nShapeID )
+{
+ // own escher data created? -> never delete such objects
+ bool bOwnEscher = pCurrXclObj && pCurrXclObj->IsOwnEscher();
+
+ // post process the current object - not for objects with own escher data
+ if( pCurrXclObj && !bOwnEscher )
+ {
+ // escher data of last shape not written? -> delete it from object list
+ if( nShapeID == 0 )
+ {
+ std::unique_ptr<XclObj> pLastObj = mrObjMgr.RemoveLastObj();
+ OSL_ENSURE( pLastObj.get() == pCurrXclObj, "XclEscherEx::EndShape - wrong object" );
+ pCurrXclObj = nullptr;
+ }
+
+ if( pCurrXclObj )
+ {
+ // set shape type
+ if ( pCurrAppData->IsStackedGroup() )
+ pCurrXclObj->SetEscherShapeTypeGroup();
+ else
+ {
+ pCurrXclObj->SetEscherShapeType( nShapeType );
+ UpdateDffFragmentEnd();
+ }
+ }
+ }
+
+ // get next object from stack
+ DeleteCurrAppData();
+ if (aStack.empty())
+ {
+ pCurrXclObj = nullptr;
+ pCurrAppData = nullptr;
+ }
+ else
+ {
+ pCurrXclObj = aStack.top().first;
+ pCurrAppData = std::move(aStack.top().second);
+ aStack.pop();
+ }
+ if( nAdditionalText == 3 )
+ nAdditionalText = 0;
+}
+
+EscherExHostAppData* XclEscherEx::EnterAdditionalTextGroup()
+{
+ nAdditionalText = 1;
+ pAdditionalText = static_cast<XclEscherClientTextbox*>( pCurrAppData->GetClientTextbox() );
+ pCurrAppData->SetClientTextbox( nullptr );
+ return pCurrAppData.get();
+}
+
+void XclEscherEx::EndDocument()
+{
+ if( mbIsRootDff )
+ Flush( static_cast< XclEscherExGlobal& >( *mxGlobal ).GetPictureStream() );
+
+ // seek back DFF stream to prepare saving the MSODRAWING[GROUP] records
+ mpOutStrm->Seek( 0 );
+}
+
+std::unique_ptr<XclExpOcxControlObj> XclEscherEx::CreateOCXCtrlObj( Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor )
+{
+ ::std::unique_ptr< XclExpOcxControlObj > xOcxCtrl;
+
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ if( xCtrlModel.is() )
+ {
+ // output stream
+ if( !mxCtlsStrm.is() )
+ mxCtlsStrm = OpenStream( EXC_STREAM_CTLS );
+ if( mxCtlsStrm.is() )
+ {
+ OUString aClassName;
+ sal_uInt32 nStrmStart = static_cast< sal_uInt32 >( mxCtlsStrm->Tell() );
+
+ // writes from xCtrlModel into mxCtlsStrm, raw class name returned in aClassName
+ Reference< XOutputStream > xOut( new utl::OSeekableOutputStreamWrapper( *mxCtlsStrm ) );
+ Reference< css::frame::XModel > xModel( GetDocShell() ? GetDocShell()->GetModel() : nullptr );
+ if( xModel.is() && xOut.is() && oox::ole::MSConvertOCXControls::WriteOCXExcelKludgeStream( xModel, xOut, xCtrlModel, xShape->getSize(), aClassName ) )
+ {
+ sal_uInt32 nStrmSize = static_cast< sal_uInt32 >( mxCtlsStrm->Tell() - nStrmStart );
+ // adjust the class name to "Forms.***.1"
+ aClassName = "Forms." + aClassName + ".1";
+ xOcxCtrl.reset( new XclExpOcxControlObj( mrObjMgr, xShape, pChildAnchor, aClassName, nStrmStart, nStrmSize ) );
+ }
+ }
+ }
+ return xOcxCtrl;
+}
+
+std::unique_ptr<XclExpTbxControlObj> XclEscherEx::CreateTBXCtrlObj( Reference< XShape > const & xShape, const tools::Rectangle* pChildAnchor )
+{
+ ::std::unique_ptr< XclExpTbxControlObj > xTbxCtrl( new XclExpTbxControlObj( mrObjMgr, xShape, pChildAnchor ) );
+ if( xTbxCtrl->GetObjType() == EXC_OBJTYPE_UNKNOWN )
+ xTbxCtrl.reset();
+ else
+ {
+ // find attached macro
+ Reference< XControlModel > xCtrlModel = XclControlHelper::GetControlModel( xShape );
+ ConvertTbxMacro( *xTbxCtrl, xCtrlModel );
+ }
+ return xTbxCtrl;
+}
+
+void XclEscherEx::ConvertTbxMacro( XclExpTbxControlObj& rTbxCtrlObj, Reference< XControlModel > const & xCtrlModel )
+{
+ SdrPage* pSdrPage = GetSdrPage( GetCurrScTab() );
+ if( !(xCtrlModel.is() && GetDocShell() && pSdrPage) )
+ return;
+
+ try
+ {
+ Reference< XFormsSupplier > xFormsSupplier( pSdrPage->getUnoPage(), UNO_QUERY_THROW );
+ Reference< XIndexAccess > xFormsIA( xFormsSupplier->getForms(), UNO_QUERY_THROW );
+
+ // 1) try to find the index of the processed control in the form
+
+ Reference< XIndexAccess > xFormIA; // needed in step 2) below
+ sal_Int32 nFoundIdx = -1;
+
+ // search all existing forms in the draw page
+ for( sal_Int32 nFormIdx = 0, nFormCount = xFormsIA->getCount();
+ (nFoundIdx < 0) && (nFormIdx < nFormCount); ++nFormIdx )
+ {
+ // get the XIndexAccess interface of the form with index nFormIdx
+ if( xFormIA.set( xFormsIA->getByIndex( nFormIdx ), UNO_QUERY ) )
+ {
+ // search all elements (controls) of the current form by index
+ for( sal_Int32 nCtrlIdx = 0, nCtrlCount = xFormIA->getCount();
+ (nFoundIdx < 0) && (nCtrlIdx < nCtrlCount); ++nCtrlIdx )
+ {
+ // compare implementation pointers of the control models
+ Reference< XControlModel > xCurrModel( xFormIA->getByIndex( nCtrlIdx ), UNO_QUERY );
+ if( xCtrlModel.get() == xCurrModel.get() )
+ nFoundIdx = nCtrlIdx;
+ }
+ }
+ }
+
+ // 2) try to find an attached macro
+
+ if( xFormIA.is() && (nFoundIdx >= 0) )
+ {
+ Reference< XEventAttacherManager > xEventMgr( xFormIA, UNO_QUERY_THROW );
+ // loop over all events attached to the found control
+ const Sequence< ScriptEventDescriptor > aEventSeq( xEventMgr->getScriptEvents( nFoundIdx ) );
+ for( const auto& rEvent : aEventSeq )
+ {
+ // try to set the event data at the Excel control object, returns true on success
+ if (rTbxCtrlObj.SetMacroLink( rEvent ))
+ break;
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+void XclEscherEx::DeleteCurrAppData()
+{
+ if ( pCurrAppData )
+ {
+ delete pCurrAppData->GetClientAnchor();
+ delete pCurrAppData->GetClientTextbox();
+ delete pCurrAppData->GetInteractionInfo();
+ pCurrAppData.reset();
+ }
+}
+
+// --- class XclEscherClientData -------------------------------------
+
+void XclEscherClientData::WriteData( EscherEx& rEx ) const
+{ // actual data is in the following OBJ record
+ rEx.AddAtom( 0, ESCHER_ClientData );
+}
+
+// --- class XclEscherClientTextbox -------------------------------------
+
+XclEscherClientTextbox::XclEscherClientTextbox( const XclExpRoot& rRoot,
+ const SdrTextObj& rObj, XclObj* pObj )
+ :
+ XclExpRoot( rRoot ),
+ rTextObj( rObj ),
+ pXclObj( pObj )
+{
+}
+
+void XclEscherClientTextbox::WriteData( EscherEx& /*rEx*/ ) const
+{
+ pXclObj->SetText( GetRoot(), rTextObj );
+}
+
+XclExpShapeObj*
+ShapeInteractionHelper::CreateShapeObj( XclExpObjectManager& rObjMgr, const Reference< XShape >& xShape, ScDocument* pDoc )
+{
+ return new XclExpShapeObj( rObjMgr, xShape, pDoc );
+}
+
+void ShapeInteractionHelper::PopulateShapeInteractionInfo(const XclExpObjectManager& rObjMgr,
+ const Reference<XShape>& xShape,
+ EscherExHostAppData& rHostAppData)
+{
+ try
+ {
+ SvMemoryStream* pMemStrm = nullptr;
+ OUString sHyperLink;
+ OUString sMacro;
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
+ if (pObj)
+ sHyperLink = pObj->getHyperlink();
+ if (ScMacroInfo* pInfo = ScDrawLayer::GetMacroInfo(pObj))
+ {
+ sMacro = pInfo->GetMacro();
+ }
+ if (!sHyperLink.isEmpty())
+ {
+ pMemStrm = new SvMemoryStream();
+ XclExpStream tmpStream(*pMemStrm, rObjMgr.GetRoot());
+ ScAddress dummyAddress;
+ SvxURLField aUrlField;
+ aUrlField.SetURL(sHyperLink);
+ XclExpHyperlink hExpHlink(rObjMgr.GetRoot(), aUrlField, dummyAddress);
+ hExpHlink.WriteEmbeddedData(tmpStream);
+ }
+ if (!sHyperLink.isEmpty() || !sMacro.isEmpty())
+ rHostAppData.SetInteractionInfo(new InteractionInfo(pMemStrm));
+ }
+ catch (Exception&)
+ {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xcl97/xcl97rec.cxx b/sc/source/filter/xcl97/xcl97rec.cxx
new file mode 100644
index 000000000..d4524b22e
--- /dev/null
+++ b/sc/source/filter/xcl97/xcl97rec.cxx
@@ -0,0 +1,2026 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/sdtaitm.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/editobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <sot/storage.hxx>
+#include <svl/itemset.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/unoapi.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <tools/urlobj.hxx>
+
+#include <rtl/math.hxx>
+#include <rtl/uuid.h>
+#include <sal/log.hxx>
+#include <drwlayer.hxx>
+
+#include <root.hxx>
+#include <xcl97rec.hxx>
+#include <xcl97esc.hxx>
+#include <xeescher.hxx>
+#include <xehelper.hxx>
+#include <xelink.hxx>
+#include <xlcontent.hxx>
+
+#include <unotools/fltrcfg.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <filter/msfilter/msoleexp.hxx>
+
+#include <unotools/localedatawrapper.hxx>
+
+#include <stdio.h>
+
+#include <document.hxx>
+#include <rangelst.hxx>
+#include <docoptio.hxx>
+#include <tabprotection.hxx>
+
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/chart/XChartDocument.hpp>
+#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
+#include <com/sun/star/chart2/XChartTypeContainer.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+
+#include <sax/fastattribs.hxx>
+#include <oox/token/tokens.hxx>
+#include <oox/token/namespaces.hxx>
+#include <oox/token/relationship.hxx>
+#include <oox/export/shapes.hxx>
+#include <oox/export/utils.hxx>
+#include <oox/export/vmlexport.hxx>
+#include <detfunc.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::drawing::XShape;
+using ::oox::drawingml::ShapeExport;
+using ::oox::vml::VMLExport;
+using namespace oox;
+
+XclExpObjList::XclExpObjList( const XclExpRoot& rRoot, XclEscherEx& rEscherEx ) :
+ XclExpRoot( rRoot ),
+ mnScTab( rRoot.GetCurrScTab() ),
+ mrEscherEx( rEscherEx )
+{
+ pMsodrawingPerSheet.reset( new XclExpMsoDrawing( rEscherEx ) );
+ // open the DGCONTAINER and the patriarch group shape
+ mrEscherEx.OpenContainer( ESCHER_DgContainer );
+ tools::Rectangle aRect( 0, 0, 0, 0 );
+ mrEscherEx.EnterGroup( &aRect );
+ mrEscherEx.UpdateDffFragmentEnd();
+}
+
+XclExpObjList::~XclExpObjList()
+{
+ maObjs.clear();
+ pMsodrawingPerSheet.reset();
+ pSolverContainer.reset();
+}
+
+sal_uInt16 XclExpObjList::Add( std::unique_ptr<XclObj> pObj )
+{
+ OSL_ENSURE( maObjs.size() < 0xFFFF, "XclExpObjList::Add: too much for Xcl" );
+
+ size_t nSize = maObjs.size();
+
+ if ( nSize < 0xFFFF )
+ {
+ pObj->SetId( nSize+1 );
+ pObj->SetTab( mnScTab );
+ maObjs.push_back(std::move(pObj));
+ ++nSize;
+ }
+ else
+ {
+ nSize = 0;
+ }
+
+ return nSize;
+}
+
+std::unique_ptr<XclObj> XclExpObjList::pop_back ()
+{
+ auto ret = std::move(maObjs.back());
+ maObjs.pop_back();
+ return ret;
+}
+
+void XclExpObjList::EndSheet()
+{
+ // Is there still something in the stream? -> The solver container
+ if( mrEscherEx.HasPendingDffData() )
+ pSolverContainer.reset( new XclExpMsoDrawing( mrEscherEx ) );
+
+ // close the DGCONTAINER created by XclExpObjList ctor MSODRAWING
+ mrEscherEx.CloseContainer();
+}
+
+void XclExpObjList::Save( XclExpStream& rStrm )
+{
+ //! Escher must be written, even if there are no objects
+ pMsodrawingPerSheet->Save( rStrm );
+
+ for ( const auto& rxObj : maObjs )
+ rxObj->Save( rStrm );
+
+ if( pSolverContainer )
+ pSolverContainer->Save( rStrm );
+}
+
+namespace {
+
+bool IsFormControlObject( const XclObj *rObj )
+{
+ switch( rObj->GetObjType() )
+ {
+ case EXC_OBJTYPE_CHECKBOX:
+ case EXC_OBJTYPE_BUTTON:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool IsVmlObject( const XclObj *rObj )
+{
+ switch( rObj->GetObjType() )
+ {
+ case EXC_OBJTYPE_NOTE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+sal_Int32 GetVmlObjectCount( XclExpObjList& rList )
+{
+ return static_cast<sal_Int32>(std::count_if(rList.begin(), rList.end(),
+ [](const std::unique_ptr<XclObj>& rxObj) { return IsVmlObject( rxObj.get() ) || IsFormControlObject( rxObj.get() ); }));
+}
+
+bool IsValidObject( const XclObj& rObj )
+{
+ if (rObj.GetObjType() == EXC_OBJTYPE_CHART)
+ {
+ // Chart object. Make sure it's a valid chart object. We skip
+ // invalid chart objects from exporting to prevent Excel from
+ // complaining on load.
+
+ const XclExpChartObj& rChartObj = static_cast<const XclExpChartObj&>(rObj);
+ uno::Reference<chart2::XChartDocument> xChartDoc(rChartObj.GetChartDoc(), uno::UNO_QUERY);
+ if (!xChartDoc.is())
+ return false;
+
+ uno::Reference<chart2::XDiagram> xDiagram = xChartDoc->getFirstDiagram();
+ if (!xDiagram.is())
+ return false;
+
+ uno::Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(xDiagram, uno::UNO_QUERY);
+ if (!xCooSysContainer.is())
+ return false;
+
+ const uno::Sequence<uno::Reference<chart2::XCoordinateSystem>> xCooSysSeq = xCooSysContainer->getCoordinateSystems();
+ for (const auto& rCooSys : xCooSysSeq)
+ {
+ Reference<chart2::XChartTypeContainer> xChartTypeCont(rCooSys, uno::UNO_QUERY);
+ if (!xChartTypeCont.is())
+ return false;
+
+ uno::Sequence<uno::Reference<chart2::XChartType>> xChartTypeSeq = xChartTypeCont->getChartTypes();
+ if (!xChartTypeSeq.hasElements())
+ // No chart type. Not good.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void SaveDrawingMLObjects( XclExpObjList& rList, XclExpXmlStream& rStrm )
+{
+ std::vector<XclObj*> aList;
+ // do not add objects to the list that are in the group,
+ // because the group already contains them. For this, count
+ // the next skipped objects, i.e. objects of a group,
+ // including objects of its subgroups
+ size_t nSkipObj = 0;
+ for (const auto& rxObj : rList)
+ {
+ // FIXME: Can DrawingML objects be grouped with VML or not valid objects?
+ if (IsVmlObject(rxObj.get()) || !IsValidObject(*rxObj))
+ continue;
+
+ if (nSkipObj == 0)
+ aList.push_back(rxObj.get());
+ else
+ --nSkipObj;
+
+ XclObjAny* pObj = nullptr;
+ if (rxObj->GetObjType() == 0) // group (it can be a subgroup)
+ pObj = dynamic_cast<XclObjAny*>(rxObj.get());
+ if (pObj)
+ {
+ css::uno::Reference<css::drawing::XShapes> xShapes(pObj->GetShape(), UNO_QUERY);
+ if (xShapes)
+ {
+ // skip (also) the objects of this group
+ nSkipObj += xShapes->getCount();
+ }
+ }
+ }
+
+ if (aList.empty())
+ return;
+
+ sal_Int32 nDrawing = drawingml::DrawingML::getNewDrawingUniqueId();
+ OUString sId;
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "drawings/drawing", nDrawing ),
+ XclXmlUtils::GetStreamName( "../", "drawings/drawing", nDrawing ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.drawing+xml",
+ oox::getRelationship(Relationship::DRAWING),
+ &sId );
+
+ rStrm.GetCurrentStream()->singleElement(XML_drawing, FSNS(XML_r, XML_id), sId.toUtf8());
+
+ rStrm.PushStream( pDrawing );
+ pDrawing->startElement( FSNS( XML_xdr, XML_wsDr ),
+ FSNS(XML_xmlns, XML_xdr), rStrm.getNamespaceURL(OOX_NS(dmlSpreadDr)).toUtf8(),
+ FSNS(XML_xmlns, XML_a), rStrm.getNamespaceURL(OOX_NS(dml)).toUtf8(),
+ FSNS(XML_xmlns, XML_r), rStrm.getNamespaceURL(OOX_NS(officeRel)).toUtf8() );
+
+ sal_Int32 nShapeId = 1000; // unique id of the shape inside one worksheet (not the whole document)
+ for (const auto& rpObj : aList)
+ {
+ // validate shapeId
+ if ( IsFormControlObject( rpObj ) )
+ {
+ XclExpTbxControlObj* pXclExpTbxControlObj = dynamic_cast<XclExpTbxControlObj*>(rpObj);
+ if (pXclExpTbxControlObj)
+ {
+ pXclExpTbxControlObj->setShapeId(++nShapeId);
+ }
+ }
+
+ rpObj->SaveXml(rStrm);
+ }
+
+ pDrawing->endElement( FSNS( XML_xdr, XML_wsDr ) );
+
+ rStrm.PopStream();
+}
+
+void SaveFormControlObjects(XclExpObjList& rList, XclExpXmlStream& rStrm)
+{
+ bool hasControls = false;
+ for (const auto& rxObj : rList)
+ {
+ if (IsFormControlObject(rxObj.get()))
+ {
+ hasControls = true;
+ break;
+ }
+ }
+
+ if (!hasControls)
+ {
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
+
+ rWorksheet->startElement(FSNS(XML_mc, XML_AlternateContent),
+ FSNS(XML_xmlns, XML_mc), rStrm.getNamespaceURL(OOX_NS(mce)).toUtf8());
+ rWorksheet->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, "x14");
+ rWorksheet->startElement(XML_controls);
+
+ for (const auto& rxObj : rList)
+ {
+ if (IsFormControlObject(rxObj.get()))
+ {
+ XclExpTbxControlObj* pXclExpTbxControlObj = dynamic_cast<XclExpTbxControlObj*>(rxObj.get());
+ if (pXclExpTbxControlObj)
+ {
+ const OUString aIdFormControlPr = pXclExpTbxControlObj->SaveControlPropertiesXml(rStrm);
+ pXclExpTbxControlObj->SaveSheetXml(rStrm, aIdFormControlPr);
+ }
+ }
+ }
+
+ rWorksheet->endElement(XML_controls);
+ rWorksheet->endElement(FSNS(XML_mc, XML_Choice));
+ rWorksheet->endElement(FSNS(XML_mc, XML_AlternateContent));
+}
+
+void SaveVmlObjects( XclExpObjList& rList, XclExpXmlStream& rStrm )
+{
+ if( GetVmlObjectCount( rList ) == 0 )
+ return;
+
+ sal_Int32 nDrawing = drawingml::DrawingML::getNewVMLUniqueId();
+ OUString sId;
+ sax_fastparser::FSHelperPtr pVmlDrawing = rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "drawings/vmlDrawing", nDrawing ),
+ XclXmlUtils::GetStreamName( "../", "drawings/vmlDrawing", nDrawing ),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.vmlDrawing",
+ oox::getRelationship(Relationship::VMLDRAWING),
+ &sId );
+
+ rStrm.GetCurrentStream()->singleElement(XML_legacyDrawing, FSNS(XML_r, XML_id), sId.toUtf8());
+
+ rStrm.PushStream( pVmlDrawing );
+ pVmlDrawing->write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n");
+ pVmlDrawing->startElement( XML_xml,
+ FSNS(XML_xmlns, XML_v), rStrm.getNamespaceURL(OOX_NS(vml)).toUtf8(),
+ FSNS(XML_xmlns, XML_o), rStrm.getNamespaceURL(OOX_NS(vmlOffice)).toUtf8(),
+ FSNS(XML_xmlns, XML_x), rStrm.getNamespaceURL(OOX_NS(vmlExcel)).toUtf8(),
+ FSNS(XML_xmlns, XML_w10), rStrm.getNamespaceURL(OOX_NS(vmlWord)).toUtf8() );
+
+ for ( const auto& rxObj : rList )
+ {
+ if (IsFormControlObject(rxObj.get()))
+ {
+ auto pFormControlObject = dynamic_cast<XclExpTbxControlObj*>(rxObj.get());
+ if (pFormControlObject)
+ {
+ pFormControlObject->SaveVml(rStrm);
+ }
+ }
+
+ if( !IsVmlObject( rxObj.get() ) )
+ continue;
+ rxObj->SaveXml( rStrm );
+ }
+
+ pVmlDrawing->endElement( XML_xml );
+
+ rStrm.PopStream();
+}
+
+}
+
+void XclExpObjList::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( pSolverContainer )
+ pSolverContainer->SaveXml( rStrm );
+
+ if( maObjs.empty())
+ return;
+
+ SaveDrawingMLObjects( *this, rStrm );
+ SaveVmlObjects( *this, rStrm );
+ SaveFormControlObjects( *this, rStrm );
+}
+
+// --- class XclObj --------------------------------------------------
+
+XclObj::XclObj( XclExpObjectManager& rObjMgr, sal_uInt16 nObjType, bool bOwnEscher ) :
+ XclExpRecord( EXC_ID_OBJ, 26 ),
+ mrEscherEx( rObjMgr.GetEscherEx() ),
+ mnObjType( nObjType ),
+ nObjId(0),
+ nGrbit( 0x6011 ), // AutoLine, AutoFill, Printable, Locked
+ mnScTab(0),
+ bFirstOnSheet( !rObjMgr.HasObj() ),
+ mbOwnEscher( bOwnEscher )
+{
+ //! first object continues the first MSODRAWING record
+ if ( bFirstOnSheet )
+ pMsodrawing = rObjMgr.GetMsodrawingPerSheet();
+ else
+ pMsodrawing = new XclExpMsoDrawing( mrEscherEx );
+}
+
+XclObj::~XclObj()
+{
+ if ( !bFirstOnSheet )
+ delete pMsodrawing;
+ pClientTextbox.reset();
+ pTxo.reset();
+}
+
+void XclObj::ImplWriteAnchor( const SdrObject* pSdrObj, const tools::Rectangle* pChildAnchor )
+{
+ if( pChildAnchor )
+ {
+ mrEscherEx.AddChildAnchor( *pChildAnchor );
+ }
+ else if( pSdrObj )
+ {
+ std::unique_ptr< XclExpDffAnchorBase > xDffAnchor( mrEscherEx.CreateDffAnchor( *pSdrObj ) );
+ xDffAnchor->WriteDffData( mrEscherEx );
+ }
+}
+
+void XclObj::SetEscherShapeType( sal_uInt16 nType )
+{
+//ToDo: what about the other defined or... types?
+ switch ( nType )
+ {
+ case ESCHER_ShpInst_Line :
+ mnObjType = EXC_OBJTYPE_LINE;
+ break;
+ case ESCHER_ShpInst_Rectangle :
+ case ESCHER_ShpInst_RoundRectangle :
+ mnObjType = EXC_OBJTYPE_RECTANGLE;
+ break;
+ case ESCHER_ShpInst_Ellipse :
+ mnObjType = EXC_OBJTYPE_OVAL;
+ break;
+ case ESCHER_ShpInst_Arc :
+ mnObjType = EXC_OBJTYPE_ARC;
+ break;
+ case ESCHER_ShpInst_TextBox :
+ mnObjType = EXC_OBJTYPE_TEXT;
+ break;
+ case ESCHER_ShpInst_PictureFrame :
+ mnObjType = EXC_OBJTYPE_PICTURE;
+ break;
+ default:
+ mnObjType = EXC_OBJTYPE_DRAWING;
+ }
+}
+
+void XclObj::SetText( const XclExpRoot& rRoot, const SdrTextObj& rObj )
+{
+ OSL_ENSURE( !pClientTextbox, "XclObj::SetText: already set" );
+ if ( !pClientTextbox )
+ {
+ mrEscherEx.UpdateDffFragmentEnd();
+ pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
+ mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record
+ mrEscherEx.UpdateDffFragmentEnd();
+ pTxo.reset( new XclTxo( rRoot, rObj ) );
+ }
+}
+
+void XclObj::WriteBody( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mnObjType != EXC_OBJTYPE_UNKNOWN, "XclObj::WriteBody - unknown type" );
+
+ // create a substream to be able to create subrecords
+ SvMemoryStream aMemStrm;
+ std::optional< XclExpStream > pXclStrm( std::in_place, aMemStrm, rStrm.GetRoot() );
+
+ // write the ftCmo subrecord
+ pXclStrm->StartRecord( EXC_ID_OBJCMO, 18 );
+ *pXclStrm << mnObjType << nObjId << nGrbit;
+ pXclStrm->WriteZeroBytes( 12 );
+ pXclStrm->EndRecord();
+
+ // write other subrecords
+ WriteSubRecs( *pXclStrm );
+
+ // write the ftEnd subrecord
+ pXclStrm->StartRecord( EXC_ID_OBJEND, 0 );
+ pXclStrm->EndRecord();
+
+ // copy the data to the OBJ record
+ pXclStrm.reset();
+ aMemStrm.Seek( 0 );
+ rStrm.CopyFromStream( aMemStrm );
+}
+
+void XclObj::Save( XclExpStream& rStrm )
+{
+ // MSODRAWING record (msofbtSpContainer)
+ if ( !bFirstOnSheet )
+ pMsodrawing->Save( rStrm );
+
+ // OBJ
+ XclExpRecord::Save( rStrm );
+
+ // second MSODRAWING record and TXO and CONTINUE records
+ SaveTextRecs( rStrm );
+}
+
+void XclObj::WriteSubRecs( XclExpStream& rStrm )
+{
+ if( mnObjType != EXC_OBJTYPE_NOTE )
+ return;
+
+ // FtNts subrecord
+ AddRecSize( 26 );
+ // ft, cb
+ rStrm << EXC_ID_OBJNTS << sal_uInt16(0x0016);
+ sal_uInt8 aGUID[16];
+ rtl_createUuid( aGUID, nullptr, false );
+ // guid
+ rStrm.SetSliceSize( 16 );
+ for( int i = 0; i < 16; i++ )
+ rStrm << aGUID[i];
+ rStrm.SetSliceSize( 0 );
+ // fSharedNote
+ rStrm << sal_uInt16(0);
+ // unused
+ rStrm.WriteZeroBytes( 4 );
+}
+
+void XclObj::SaveTextRecs( XclExpStream& rStrm )
+{
+ // MSODRAWING record (msofbtClientTextbox)
+ if ( pClientTextbox )
+ pClientTextbox->Save( rStrm );
+ // TXO and CONTINUE records
+ if ( pTxo )
+ pTxo->Save( rStrm );
+}
+
+// --- class XclObjComment ------------------------------------------
+
+// tdf#118662 static helper to allow single function access as friend in SdrCaptionObj
+void setSuppressGetBitmapFromXclObjComment(SdrCaptionObj* pSdrCaptionObj, bool bValue)
+{
+ if(nullptr != pSdrCaptionObj)
+ {
+ pSdrCaptionObj->setSuppressGetBitmap(bValue);
+ }
+}
+
+XclObjComment::XclObjComment( XclExpObjectManager& rObjMgr, const tools::Rectangle& rRect, const EditTextObject& rEditObj, SdrCaptionObj* pCaption, bool bVisible, const ScAddress& rAddress, const tools::Rectangle &rFrom, const tools::Rectangle &rTo ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_NOTE, true )
+ , maScPos( rAddress )
+ , mpCaption( pCaption )
+ , mbVisible( bVisible )
+ , maFrom ( rFrom )
+ , maTo ( rTo )
+{
+ // tdf#118662 due to no longer cloning the SdrCaptionObj an old 'hack' using the
+ // fact that no Graphics gets created when a SdrObject is not inserted in a SdrPage
+ // does not work anymore. In SvxShape::GetBitmap that info was used, and here the
+ // SdrCaptionObj was cloned for the only reason to have one not added to a SdrPage.
+ // To emulate old behaviour, use a boolean flag at the SdrCaptionObj.
+ setSuppressGetBitmapFromXclObjComment(mpCaption, true);
+
+ ProcessEscherObj( rObjMgr.GetRoot(), rRect, pCaption, bVisible);
+ // TXO
+ pTxo .reset(new XclTxo( rObjMgr.GetRoot(), rEditObj, pCaption ));
+}
+
+static void lcl_FillProps( EscherPropertyContainer& rPropOpt, SdrObject* pCaption, bool bVisible )
+{
+ if( pCaption )
+ {
+ Reference< XShape > aXShape = GetXShapeForSdrObject( pCaption );
+ Reference< XPropertySet > aXPropSet( aXShape, UNO_QUERY );
+ if( aXPropSet.is() )
+ {
+ rPropOpt.CreateFillProperties( aXPropSet, true);
+
+ rPropOpt.AddOpt( ESCHER_Prop_lTxid, 0 ); // undocumented
+ rPropOpt.AddOpt( 0x0158, 0x00000000 ); // undocumented
+
+ sal_uInt32 nValue = 0;
+ if( !rPropOpt.GetOpt( ESCHER_Prop_FitTextToShape, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field
+
+ // Maybe the colour is the same as the 'ToolTip' System colour, but the tooltip
+ // colour shouldn't have influence on the fill colour of the exported shape
+ if( !rPropOpt.GetOpt( ESCHER_Prop_fillColor, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x08000050 );
+ if( !rPropOpt.GetOpt( ESCHER_Prop_fillBackColor, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x08000050 );
+ if( !rPropOpt.GetOpt( ESCHER_Prop_fNoFillHitTest, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 ); // bool field
+ if( !rPropOpt.GetOpt( ESCHER_Prop_shadowColor, nValue ) )
+ rPropOpt.AddOpt( ESCHER_Prop_shadowColor, 0x00000000 );
+ if( !rPropOpt.GetOpt( ESCHER_Prop_fshadowObscured, nValue ) ) // bool field
+ rPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x00030003 ); // bool field
+ }
+ }
+
+ sal_uInt32 nFlags = 0x000A0000;
+ ::set_flag( nFlags, sal_uInt32(2), !bVisible );
+ rPropOpt.AddOpt( ESCHER_Prop_fPrint, nFlags ); // bool field
+}
+
+void XclObjComment::ProcessEscherObj( const XclExpRoot& rRoot, const tools::Rectangle& rRect, SdrObject* pCaption, const bool bVisible )
+{
+ EscherPropertyContainer aPropOpt;
+
+ lcl_FillProps( aPropOpt, pCaption, bVisible );
+
+ nGrbit = 0; // all off: AutoLine, AutoFill, Printable, Locked
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ XclExpDffNoteAnchor( rRoot, rRect ).WriteDffData( mrEscherEx );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.UpdateDffFragmentEnd();
+
+ //! Be sure to construct the MSODRAWING ClientTextbox record _after_ the
+ //! base OBJ's MSODRAWING record Escher data is completed.
+ pClientTextbox.reset( new XclExpMsoDrawing( mrEscherEx ) );
+ mrEscherEx.AddAtom( 0, ESCHER_ClientTextbox ); // TXO record
+ mrEscherEx.UpdateDffFragmentEnd();
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+}
+
+XclObjComment::~XclObjComment()
+{
+ // tdf#118662 reset flag
+ setSuppressGetBitmapFromXclObjComment(mpCaption, false);
+}
+
+void XclObjComment::Save( XclExpStream& rStrm )
+{
+ // content of this record
+ XclObj::Save( rStrm );
+}
+
+namespace {
+
+class VmlCommentExporter : public VMLExport
+{
+ ScAddress maScPos;
+ SdrCaptionObj* mpCaption;
+ bool mbVisible;
+ tools::Rectangle maFrom;
+ tools::Rectangle maTo;
+
+public:
+ VmlCommentExporter ( const sax_fastparser::FSHelperPtr& p, const ScAddress& aScPos, SdrCaptionObj* pCaption, bool bVisible, const tools::Rectangle &aFrom, const tools::Rectangle &aTo );
+protected:
+ virtual void Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect ) override;
+ using VMLExport::StartShape;
+ virtual sal_Int32 StartShape() override;
+ using VMLExport::EndShape;
+ virtual void EndShape( sal_Int32 nShapeElement ) override;
+};
+
+}
+
+VmlCommentExporter::VmlCommentExporter( const sax_fastparser::FSHelperPtr& p, const ScAddress& aScPos, SdrCaptionObj* pCaption,
+ bool bVisible, const tools::Rectangle &aFrom, const tools::Rectangle &aTo )
+ : VMLExport( p )
+ , maScPos( aScPos )
+ , mpCaption( pCaption )
+ , mbVisible( bVisible )
+ , maFrom ( aFrom )
+ , maTo ( aTo )
+{
+}
+
+void VmlCommentExporter::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& rRect )
+{
+ lcl_FillProps( rProps, mpCaption, mbVisible );
+ rProps.AddOpt( ESCHER_Prop_fHidden, sal_uInt32(mbVisible) ); // bool field
+
+ // shadow property value for comment ( set in lcl_FillProps [*] ) has been
+ // overwritten by new value ( 0x20000 ) in the generic part of the export
+ // ( see EscherPropertyContainer::CreateShadowProperties )
+ // Safer option here is to just force the needed value here for oox vml
+ // export alone ( and avoid potential problems with binary export )
+ // #TODO investigate value of ESCHER_Prop_fshadowObscured generally
+ // in binary export ( if indeed this value is good for binary export )
+ // we can change the heuristics and/or initialisation path and get
+ // rid of line below.
+ // [*] lcl_FillProps seems to be called twice when exporting to xlsx
+ // once from XclObjComment::ProcessEscherObj #TODO look into that also
+ rProps.AddOpt( ESCHER_Prop_fshadowObscured, 0x00030003 ); // force value for comments
+
+ VMLExport::Commit( rProps, rRect );
+}
+
+sal_Int32 VmlCommentExporter::StartShape()
+{
+ AddShapeAttribute( XML_type, "#_x0000_t202" );
+
+ sal_Int32 nId = VMLExport::StartShape();
+
+ return nId;
+}
+
+static const char* lcl_GetHorizAlignFromItemSetChar(const SfxItemSet& rItemSet)
+{
+ switch (rItemSet.Get(EE_PARA_JUST).GetAdjust())
+ {
+ case SvxAdjust::Center:
+ return "Center";
+ case SvxAdjust::Right:
+ return "Right";
+ case SvxAdjust::Block:
+ return "Justify";
+ default:
+ return "Left";
+ }
+}
+
+static const char* lcl_GetVertAlignFromItemSetChar( const SfxItemSet& rItemSet )
+{
+ switch( rItemSet.Get( SDRATTR_TEXT_VERTADJUST ).GetValue() )
+ {
+ case SDRTEXTVERTADJUST_CENTER:
+ return "Center";
+ case SDRTEXTVERTADJUST_BOTTOM:
+ return "Bottom";
+ case SDRTEXTVERTADJUST_BLOCK:
+ return "Justify";
+ case SDRTEXTVERTADJUST_TOP:
+ default:
+ return "Top";
+ }
+}
+
+void VmlCommentExporter::EndShape( sal_Int32 nShapeElement )
+{
+ char pAnchor[100];
+ sax_fastparser::FSHelperPtr pVmlDrawing = GetFS();
+ snprintf( pAnchor, 100, "%" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64 ", %" SAL_PRIdINT64,
+ sal_Int64(maFrom.Left()), sal_Int64(maFrom.Top()), sal_Int64(maFrom.Right()), sal_Int64(maFrom.Bottom()),
+ sal_Int64(maTo.Left()), sal_Int64(maTo.Top()), sal_Int64(maTo.Right()), sal_Int64(maTo.Bottom()) );
+
+ // Getting comment text alignments
+ const char* pVertAlign = lcl_GetVertAlignFromItemSetChar(mpCaption->GetMergedItemSet());
+ const char* pHorizAlign = lcl_GetHorizAlignFromItemSetChar(mpCaption->GetMergedItemSet());
+
+ pVmlDrawing->startElement(FSNS(XML_x, XML_ClientData), XML_ObjectType, "Note");
+ pVmlDrawing->singleElement(FSNS(XML_x, XML_MoveWithCells));
+ pVmlDrawing->singleElement(FSNS(XML_x, XML_SizeWithCells));
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_Anchor ), pAnchor );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_AutoFill ), "False" );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_TextVAlign ), pVertAlign );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_TextHAlign ), pHorizAlign );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS( XML_x, XML_Row ), maScPos.Row() );
+ XclXmlUtils::WriteElement( pVmlDrawing, FSNS(XML_x, XML_Column), sal_Int32(maScPos.Col()));
+ if(mbVisible)
+ pVmlDrawing->singleElement(FSNS(XML_x, XML_Visible));
+ pVmlDrawing->endElement( FSNS( XML_x, XML_ClientData ) );
+
+ VMLExport::EndShape( nShapeElement );
+}
+
+void XclObjComment::SaveXml( XclExpXmlStream& rStrm )
+{
+ VmlCommentExporter aCommentExporter( rStrm.GetCurrentStream(), maScPos, mpCaption, mbVisible, maFrom, maTo );
+ aCommentExporter.AddSdrObject( *mpCaption );
+}
+
+// --- class XclObjDropDown ------------------------------------------
+
+XclObjDropDown::XclObjDropDown( XclExpObjectManager& rObjMgr, const ScAddress& rPos, bool bFilt ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_DROPDOWN, true ),
+ bIsFiltered( bFilt )
+{
+ SetLocked( true );
+ SetPrintable( false );
+ SetAutoFill( true );
+ SetAutoLine( false );
+ nGrbit |= 0x0100; // undocumented
+ mrEscherEx.OpenContainer( ESCHER_SpContainer );
+ mrEscherEx.AddShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty );
+ EscherPropertyContainer aPropOpt;
+ aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x01040104 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00010000 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x00080000 ); // bool field
+ aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x000A0000 ); // bool field
+ aPropOpt.Commit( mrEscherEx.GetStream() );
+
+ XclExpDffDropDownAnchor( rObjMgr.GetRoot(), rPos ).WriteDffData( mrEscherEx );
+
+ mrEscherEx.AddAtom( 0, ESCHER_ClientData ); // OBJ record
+ mrEscherEx.UpdateDffFragmentEnd();
+ mrEscherEx.CloseContainer(); // ESCHER_SpContainer
+
+ // old size + ftSbs + ftLbsData
+ AddRecSize( 24 + 20 );
+}
+
+XclObjDropDown::~XclObjDropDown()
+{
+}
+
+void XclObjDropDown::WriteSubRecs( XclExpStream& rStrm )
+{
+ // ftSbs subrecord - Scroll bars (dummy)
+ rStrm.StartRecord( EXC_ID_OBJSBS, 20 );
+ rStrm.WriteZeroBytes( 20 );
+ rStrm.EndRecord();
+
+ // ftLbsData subrecord - Listbox data
+ sal_uInt16 nDropDownFlags = 0;
+ ::insert_value( nDropDownFlags, EXC_OBJ_DROPDOWN_SIMPLE, 0, 2 );
+ ::set_flag( nDropDownFlags, EXC_OBJ_DROPDOWN_FILTERED, bIsFiltered );
+ rStrm.StartRecord( EXC_ID_OBJLBSDATA, 16 );
+ rStrm << sal_uInt32(0) << sal_uInt16(0) << sal_uInt16(0x0301) << sal_uInt16(0)
+ << nDropDownFlags << sal_uInt16( 20 ) << sal_uInt16( 130 );
+ rStrm.EndRecord();
+}
+
+// --- class XclTxo --------------------------------------------------
+
+static sal_uInt8 lcl_GetHorAlignFromItemSet( const SfxItemSet& rItemSet )
+{
+ sal_uInt8 nHorAlign = EXC_OBJ_HOR_LEFT;
+
+ switch( rItemSet.Get( EE_PARA_JUST ).GetAdjust() )
+ {
+ case SvxAdjust::Left: nHorAlign = EXC_OBJ_HOR_LEFT; break;
+ case SvxAdjust::Center: nHorAlign = EXC_OBJ_HOR_CENTER; break;
+ case SvxAdjust::Right: nHorAlign = EXC_OBJ_HOR_RIGHT; break;
+ case SvxAdjust::Block: nHorAlign = EXC_OBJ_HOR_JUSTIFY; break;
+ default:;
+ }
+ return nHorAlign;
+}
+
+static sal_uInt8 lcl_GetVerAlignFromItemSet( const SfxItemSet& rItemSet )
+{
+ sal_uInt8 nVerAlign = EXC_OBJ_VER_TOP;
+
+ switch( rItemSet.Get( SDRATTR_TEXT_VERTADJUST ).GetValue() )
+ {
+ case SDRTEXTVERTADJUST_TOP: nVerAlign = EXC_OBJ_VER_TOP; break;
+ case SDRTEXTVERTADJUST_CENTER: nVerAlign = EXC_OBJ_VER_CENTER; break;
+ case SDRTEXTVERTADJUST_BOTTOM: nVerAlign = EXC_OBJ_VER_BOTTOM; break;
+ case SDRTEXTVERTADJUST_BLOCK: nVerAlign = EXC_OBJ_VER_JUSTIFY; break;
+ default:;
+ }
+ return nVerAlign;
+}
+
+XclTxo::XclTxo( const OUString& rString, sal_uInt16 nFontIx ) :
+ mpString( std::make_shared<XclExpString>( rString ) ),
+ mnRotation( EXC_OBJ_ORIENT_NONE ),
+ mnHorAlign( EXC_OBJ_HOR_LEFT ),
+ mnVerAlign( EXC_OBJ_VER_TOP )
+{
+ if( mpString->Len() )
+ {
+ // If there is text, Excel *needs* the 2nd CONTINUE record with at least two format runs
+ mpString->AppendFormat( 0, nFontIx );
+ mpString->AppendFormat( mpString->Len(), EXC_FONT_APP );
+ }
+}
+
+XclTxo::XclTxo( const XclExpRoot& rRoot, const SdrTextObj& rTextObj ) :
+ mpString( XclExpStringHelper::CreateString( rRoot, rTextObj ) ),
+ mnRotation( EXC_OBJ_ORIENT_NONE ),
+ mnHorAlign( EXC_OBJ_HOR_LEFT ),
+ mnVerAlign( EXC_OBJ_VER_TOP )
+{
+ // additional alignment and orientation items
+ const SfxItemSet& rItemSet = rTextObj.GetMergedItemSet();
+
+ // horizontal alignment
+ SetHorAlign( lcl_GetHorAlignFromItemSet( rItemSet ) );
+
+ // vertical alignment
+ SetVerAlign( lcl_GetVerAlignFromItemSet( rItemSet ) );
+
+ // rotation
+ Degree100 nAngle = rTextObj.GetRotateAngle();
+ if( (4500_deg100 < nAngle) && (nAngle < 13500_deg100) )
+ mnRotation = EXC_OBJ_ORIENT_90CCW;
+ else if( (22500_deg100 < nAngle) && (nAngle < 31500_deg100) )
+ mnRotation = EXC_OBJ_ORIENT_90CW;
+ else
+ mnRotation = EXC_OBJ_ORIENT_NONE;
+}
+
+XclTxo::XclTxo( const XclExpRoot& rRoot, const EditTextObject& rEditObj, SdrObject* pCaption ) :
+ mpString( XclExpStringHelper::CreateString( rRoot, rEditObj ) ),
+ mnRotation( EXC_OBJ_ORIENT_NONE ),
+ mnHorAlign( EXC_OBJ_HOR_LEFT ),
+ mnVerAlign( EXC_OBJ_VER_TOP )
+{
+ if(!pCaption)
+ return;
+
+ // Excel has one alignment per NoteObject while Calc supports
+ // one alignment per paragraph - use the first paragraph
+ // alignment (if set) as our overall alignment.
+ OUString aParaText( rEditObj.GetText( 0 ) );
+ if( !aParaText.isEmpty() )
+ {
+ const SfxItemSet& aSet( rEditObj.GetParaAttribs( 0));
+ if( const SvxAdjustItem* pItem = aSet.GetItemIfSet( EE_PARA_JUST ) )
+ {
+ SvxAdjust eEEAlign = pItem->GetAdjust();
+ pCaption->SetMergedItem( SvxAdjustItem( eEEAlign, EE_PARA_JUST ) );
+ }
+ }
+ const SfxItemSet& rItemSet = pCaption->GetMergedItemSet();
+
+ // horizontal alignment
+ SetHorAlign( lcl_GetHorAlignFromItemSet( rItemSet ) );
+
+ // vertical alignment
+ SetVerAlign( lcl_GetVerAlignFromItemSet( rItemSet ) );
+
+ // orientation alignment
+ const SvxWritingModeItem& rItem = rItemSet.Get( SDRATTR_TEXTDIRECTION );
+ if( rItem.GetValue() == css::text::WritingMode_TB_RL )
+ mnRotation = EXC_OBJ_ORIENT_90CW;
+}
+
+void XclTxo::SaveCont( XclExpStream& rStrm )
+{
+ OSL_ENSURE( mpString, "XclTxo::SaveCont - missing string" );
+
+ // #i96858# do not save existing string formatting if text is empty
+ sal_uInt16 nRunLen = mpString->IsEmpty() ? 0 : (8 * mpString->GetFormatsCount());
+ // alignment
+ sal_uInt16 nFlags = 0;
+ ::insert_value( nFlags, mnHorAlign, 1, 3 );
+ ::insert_value( nFlags, mnVerAlign, 4, 3 );
+
+ rStrm << nFlags << mnRotation;
+ rStrm.WriteZeroBytes( 6 );
+ rStrm << mpString->Len() << nRunLen << sal_uInt32( 0 );
+}
+
+void XclTxo::Save( XclExpStream& rStrm )
+{
+ // Write the TXO part
+ ExcRecord::Save( rStrm );
+
+ // CONTINUE records are only written if there is some text
+ if( mpString->IsEmpty() )
+ return;
+
+ // CONTINUE for character array
+ rStrm.StartRecord( EXC_ID_CONT, mpString->GetBufferSize() + 1 );
+ rStrm << static_cast< sal_uInt8 >( mpString->GetFlagField() & EXC_STRF_16BIT ); // only Unicode flag
+ mpString->WriteBuffer( rStrm );
+ rStrm.EndRecord();
+
+ // CONTINUE for formatting runs
+ rStrm.StartRecord( EXC_ID_CONT, 8 * mpString->GetFormatsCount() );
+ const XclFormatRunVec& rFormats = mpString->GetFormats();
+ for( const auto& rFormat : rFormats )
+ rStrm << rFormat.mnChar << rFormat.mnFontIdx << sal_uInt32( 0 );
+ rStrm.EndRecord();
+}
+
+sal_uInt16 XclTxo::GetNum() const
+{
+ return EXC_ID_TXO;
+}
+
+std::size_t XclTxo::GetLen() const
+{
+ return 18;
+}
+
+// --- class XclObjOle -------------------------------------------
+
+XclObjOle::XclObjOle( XclExpObjectManager& rObjMgr, const SdrObject& rObj ) :
+ XclObj( rObjMgr, EXC_OBJTYPE_PICTURE ),
+ rOleObj( rObj ),
+ pRootStorage( rObjMgr.GetRoot().GetRootStorage().get() )
+{
+}
+
+XclObjOle::~XclObjOle()
+{
+}
+
+void XclObjOle::WriteSubRecs( XclExpStream& rStrm )
+{
+ // write only as embedded, not linked
+ OUString aStorageName( "MBD" );
+ char aBuf[ sizeof(sal_uInt32) * 2 + 1 ];
+ // FIXME Eeek! Is this just a way to get a unique id?
+ sal_uInt32 nPictureId = sal_uInt32(reinterpret_cast<sal_uIntPtr>(this) >> 2);
+ sprintf( aBuf, "%08X", static_cast< unsigned int >( nPictureId ) );
+ aStorageName += OUString::createFromAscii(aBuf);
+ tools::SvRef<SotStorage> xOleStg = pRootStorage->OpenSotStorage( aStorageName );
+ if( !xOleStg.is() )
+ return;
+
+ uno::Reference < embed::XEmbeddedObject > xObj( static_cast<const SdrOle2Obj&>(rOleObj).GetObjRef() );
+ if ( !xObj.is() )
+ return;
+
+ // set version to "old" version, because it must be
+ // saved in MS notation.
+ sal_uInt32 nFl = 0;
+ const SvtFilterOptions& rFltOpts = SvtFilterOptions::Get();
+ if( rFltOpts.IsMath2MathType() )
+ nFl |= OLE_STARMATH_2_MATHTYPE;
+
+ if( rFltOpts.IsWriter2WinWord() )
+ nFl |= OLE_STARWRITER_2_WINWORD;
+
+ if( rFltOpts.IsCalc2Excel() )
+ nFl |= OLE_STARCALC_2_EXCEL;
+
+ if( rFltOpts.IsImpress2PowerPoint() )
+ nFl |= OLE_STARIMPRESS_2_POWERPOINT;
+
+ SvxMSExportOLEObjects aOLEExpFilt( nFl );
+ aOLEExpFilt.ExportOLEObject( xObj, *xOleStg );
+
+ // OBJCF subrecord, undocumented as usual
+ rStrm.StartRecord( EXC_ID_OBJCF, 2 );
+ rStrm << sal_uInt16(0x0002);
+ rStrm.EndRecord();
+
+ // OBJFLAGS subrecord, undocumented as usual
+ rStrm.StartRecord( EXC_ID_OBJFLAGS, 2 );
+ sal_uInt16 nFlags = EXC_OBJ_PIC_MANUALSIZE;
+ ::set_flag( nFlags, EXC_OBJ_PIC_SYMBOL, static_cast<const SdrOle2Obj&>(rOleObj).GetAspect() == embed::Aspects::MSOLE_ICON );
+ rStrm << nFlags;
+ rStrm.EndRecord();
+
+ // OBJPICTFMLA subrecord, undocumented as usual
+ XclExpString aName( xOleStg->GetUserName() );
+ sal_uInt16 nPadLen = static_cast<sal_uInt16>(aName.GetSize() & 0x01);
+ sal_uInt16 nFmlaLen = static_cast< sal_uInt16 >( 12 + aName.GetSize() + nPadLen );
+ sal_uInt16 nSubRecLen = nFmlaLen + 6;
+
+ rStrm.StartRecord( EXC_ID_OBJPICTFMLA, nSubRecLen );
+ rStrm << nFmlaLen
+ << sal_uInt16( 5 ) << sal_uInt32( 0 ) << sal_uInt8( 2 )
+ << sal_uInt32( 0 ) << sal_uInt8( 3 )
+ << aName;
+ if( nPadLen )
+ rStrm << sal_uInt8( 0 ); // pad byte
+ rStrm << nPictureId;
+ rStrm.EndRecord();
+}
+
+void XclObjOle::Save( XclExpStream& rStrm )
+{
+ // content of this record
+ XclObj::Save( rStrm );
+}
+
+// --- class XclObjAny -------------------------------------------
+
+XclObjAny::XclObjAny( XclExpObjectManager& rObjMgr, const Reference< XShape >& rShape, ScDocument* pDoc )
+ : XclObj( rObjMgr, EXC_OBJTYPE_UNKNOWN )
+ , mxShape( rShape )
+ , mpDoc(pDoc)
+{
+}
+
+XclObjAny::~XclObjAny()
+{
+}
+
+void XclObjAny::WriteSubRecs( XclExpStream& rStrm )
+{
+ if( mnObjType == EXC_OBJTYPE_GROUP )
+ // ftGmo subrecord
+ rStrm << EXC_ID_OBJGMO << sal_uInt16(2) << sal_uInt16(0);
+}
+
+void XclObjAny::Save( XclExpStream& rStrm )
+{
+ if( mnObjType == EXC_OBJTYPE_GROUP )
+ // old size + ftGmo
+ AddRecSize( 6 );
+
+ // content of this record
+ XclObj::Save( rStrm );
+}
+
+// --- class ExcBof8_Base --------------------------------------------
+
+ExcBof8_Base::ExcBof8_Base()
+{
+ nVers = 0x0600;
+ nRupBuild = 0x0dbb;
+ nRupYear = 0x07cc;
+}
+
+void XclObjAny::WriteFromTo( XclExpXmlStream& rStrm, const Reference< XShape >& rShape, SCTAB nTab )
+{
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
+
+ awt::Point aTopLeft = rShape->getPosition();
+ awt::Size aSize = rShape->getSize();
+
+ // There are a few cases where we must adjust these values
+ // Do not adjust objects, which have rotation incorporated into their points
+ // but report a rotation angle nevertheless.
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rShape);
+ if (pObj && pObj->GetObjIdentifier() != SdrObjKind::Line && pObj->GetObjIdentifier() != SdrObjKind::PolyLine
+ && pObj->GetObjIdentifier() != SdrObjKind::PathLine && pObj->GetObjIdentifier() != SdrObjKind::FreehandLine
+ && pObj->GetObjIdentifier() != SdrObjKind::PathPolyLine)
+ {
+ Degree100 nRotation = NormAngle36000(pObj->GetRotateAngle());
+ if (nRotation)
+ {
+ sal_Int16 nHalfWidth = aSize.Width / 2;
+ sal_Int16 nHalfHeight = aSize.Height / 2;
+
+ // Center of bounding box of the rotated shape
+ const auto aSnapRectCenter(pObj->GetSnapRect().Center());
+ aTopLeft.X = aSnapRectCenter.X() - nHalfWidth;
+ aTopLeft.Y = aSnapRectCenter.Y() - nHalfHeight;
+
+ // MSO changes the anchor positions at these angles and that does an extra 90 degrees
+ // rotation on our shapes, so we output it in such position that MSO
+ // can draw this shape correctly.
+ if ((nRotation > 4500_deg100 && nRotation <= 13500_deg100) || (nRotation > 22500_deg100 && nRotation <= 31500_deg100))
+ {
+ aTopLeft.X = aTopLeft.X - nHalfHeight + nHalfWidth;
+ aTopLeft.Y = aTopLeft.Y - nHalfWidth + nHalfHeight;
+
+ std::swap(aSize.Width, aSize.Height);
+ }
+ }
+ }
+
+ tools::Rectangle aLocation( aTopLeft.X, aTopLeft.Y, aTopLeft.X + aSize.Width, aTopLeft.Y + aSize.Height );
+ ScRange aRange = rStrm.GetRoot().GetDoc().GetRange( nTab, aLocation );
+ tools::Rectangle aRangeRect = rStrm.GetRoot().GetDoc().GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col()-1, aRange.aEnd.Row()-1,
+ nTab );
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_from));
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_col ), static_cast<sal_Int32>(aRange.aStart.Col()) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_colOff ),
+ oox::drawingml::convertHmmToEmu( aLocation.Left() - aRangeRect.Left() ) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_row ), static_cast<sal_Int32>(aRange.aStart.Row()) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_rowOff ),
+ oox::drawingml::convertHmmToEmu( aLocation.Top() - aRangeRect.Top() ) );
+ pDrawing->endElement( FSNS( XML_xdr, XML_from ) );
+
+ pDrawing->startElement(FSNS(XML_xdr, XML_to));
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_col ), static_cast<sal_Int32>(aRange.aEnd.Col()) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_colOff ),
+ oox::drawingml::convertHmmToEmu( aLocation.Right() - aRangeRect.Right() ) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_row ), static_cast<sal_Int32>(aRange.aEnd.Row()) );
+ XclXmlUtils::WriteElement( pDrawing, FSNS( XML_xdr, XML_rowOff ),
+ oox::drawingml::convertHmmToEmu( aLocation.Bottom() - aRangeRect.Bottom() ) );
+ pDrawing->endElement( FSNS( XML_xdr, XML_to ) );
+}
+
+void XclObjAny::WriteFromTo( XclExpXmlStream& rStrm, const XclObjAny& rObj )
+{
+ WriteFromTo( rStrm, rObj.GetShape(), rObj.GetTab() );
+}
+
+static const char*
+GetEditAs( const XclObjAny& rObj )
+{
+ if( const SdrObject* pShape = EscherEx::GetSdrObject( rObj.GetShape() ) )
+ {
+ switch( ScDrawLayer::GetAnchorType( *pShape ) )
+ {
+ case SCA_CELL:
+ return "oneCell";
+ case SCA_CELL_RESIZE:
+ return "twoCell";
+ default:
+ case SCA_PAGE:
+ break; // absolute
+ }
+ }
+ return "absolute";
+}
+
+namespace {
+
+ScRefFlags parseRange(const OUString& rString, ScRange& rRange, const ScDocument& rDoc)
+{
+ // start with the address convention set in the document
+ formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ ScRefFlags nResult = rRange.Parse(rString, rDoc, eConv);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try the default calc address convention
+ nResult = rRange.Parse(rString, rDoc);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try excel a1
+ nResult = rRange.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_A1);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try r1c1
+ return rRange.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
+}
+
+ScRefFlags parseAddress(const OUString& rString, ScAddress& rAddress, const ScDocument& rDoc)
+{
+ // start with the address convention set in the document
+ formula::FormulaGrammar::AddressConvention eConv = rDoc.GetAddressConvention();
+ ScRefFlags nResult = rAddress.Parse(rString, rDoc, eConv);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try the default calc address convention
+ nResult = rAddress.Parse(rString, rDoc);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try excel a1
+ nResult = rAddress.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_A1);
+ if ( nResult & ScRefFlags::VALID )
+ return nResult;
+
+ // try r1c1
+ return rAddress.Parse(rString, rDoc, formula::FormulaGrammar::CONV_XL_R1C1);
+}
+
+void transformURL(const OUString& rOldURL, OUString& rNewURL, const ScDocument& rDoc)
+{
+ if (rOldURL.startsWith("#"))
+ {
+ // URL has to be decoded for escaped characters (%20)
+ OUString aURL = INetURLObject::decode( rOldURL,
+ INetURLObject::DecodeMechanism::WithCharset );
+ OUString aAddressString = aURL.copy(1);
+
+ ScRange aRange;
+ ScRefFlags nResult = parseRange(aAddressString, aRange, rDoc);
+ if ( nResult & ScRefFlags::VALID )
+ {
+ OUString aString = aRange.Format(rDoc, nResult, formula::FormulaGrammar::CONV_XL_OOX);
+ rNewURL = "#" + aString;
+ return;
+ }
+ else
+ {
+ ScAddress aAddress;
+ nResult = parseAddress(aAddressString, aAddress, rDoc);
+ if( nResult & ScRefFlags::VALID )
+ {
+ OUString aString = aAddress.Format(nResult, &rDoc, formula::FormulaGrammar::CONV_XL_OOX);
+ rNewURL = "#" + aString;
+ return;
+ }
+ }
+ }
+
+ rNewURL = rOldURL;
+}
+
+}
+
+ScURLTransformer::ScURLTransformer(ScDocument& rDoc)
+ : mrDoc(rDoc)
+{
+}
+
+OUString ScURLTransformer::getTransformedString(const OUString& rURL) const
+{
+ OUString aNewURL;
+ transformURL(rURL, aNewURL, mrDoc);
+ return aNewURL;
+}
+
+bool ScURLTransformer::isExternalURL(const OUString& rURL) const
+{
+ return !rURL.startsWith("#");
+}
+
+void XclObjAny::SaveXml( XclExpXmlStream& rStrm )
+{
+ // Do not output any of the detective shapes and validation circles.
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape(mxShape);
+ if (pObject)
+ {
+ ScDocument& rDoc = rStrm.GetRoot().GetDoc();
+ ScDetectiveFunc aDetFunc(rDoc, mnScTab);
+ ScAddress aPosition;
+ ScRange aSourceRange;
+ bool bRedLine;
+ ScDetectiveObjType eObjType
+ = aDetFunc.GetDetectiveObjectType(pObject, mnScTab, aPosition, aSourceRange, bRedLine);
+
+ if (eObjType != SC_DETOBJ_NONE)
+ return;
+ }
+
+ sax_fastparser::FSHelperPtr pDrawing = rStrm.GetCurrentStream();
+
+ ShapeExport aDML(XML_xdr, pDrawing, nullptr, &rStrm, drawingml::DOCUMENT_XLSX);
+ auto pURLTransformer = std::make_shared<ScURLTransformer>(*mpDoc);
+ aDML.SetURLTranslator(pURLTransformer);
+
+ pDrawing->startElement( FSNS( XML_xdr, XML_twoCellAnchor ), // OOXTODO: oneCellAnchor, absoluteAnchor
+ XML_editAs, GetEditAs( *this ) );
+ Reference< XPropertySet > xPropSet( mxShape, UNO_QUERY );
+ if (xPropSet.is())
+ {
+ WriteFromTo( rStrm, *this );
+ aDML.WriteShape( mxShape );
+ }
+
+ pDrawing->singleElement( FSNS( XML_xdr, XML_clientData)
+ // OOXTODO: XML_fLocksWithSheet
+ // OOXTODO: XML_fPrintsWithSheet
+ );
+ pDrawing->endElement( FSNS( XML_xdr, XML_twoCellAnchor ) );
+}
+
+void ExcBof8_Base::SaveCont( XclExpStream& rStrm )
+{
+ rStrm.DisableEncryption();
+ rStrm << nVers << nDocType << nRupBuild << nRupYear
+ << sal_uInt32(0)/*nFileHistory*/
+ << sal_uInt32(0x06) /*nLowestBiffVer = Biff8*/;
+}
+
+sal_uInt16 ExcBof8_Base::GetNum() const
+{
+ return 0x0809;
+}
+
+std::size_t ExcBof8_Base::GetLen() const
+{
+ return 16;
+}
+
+// --- class ExcBof8 -------------------------------------------------
+
+ExcBof8::ExcBof8()
+{
+ nDocType = 0x0010;
+}
+
+// --- class ExcBofW8 ------------------------------------------------
+
+ExcBofW8::ExcBofW8()
+{
+ nDocType = 0x0005;
+}
+
+// --- class ExcBundlesheet8 -----------------------------------------
+
+ExcBundlesheet8::ExcBundlesheet8( const RootData& rRootData, SCTAB _nTab ) :
+ ExcBundlesheetBase( rRootData, static_cast<sal_uInt16>(_nTab) ),
+ sUnicodeName( rRootData.pER->GetTabInfo().GetScTabName( _nTab ) )
+{
+}
+
+ExcBundlesheet8::ExcBundlesheet8( const OUString& rString ) :
+ sUnicodeName( rString )
+{
+}
+
+void ExcBundlesheet8::SaveCont( XclExpStream& rStrm )
+{
+ m_nOwnPos = rStrm.GetSvStreamPos();
+ // write dummy position, real position comes later
+ rStrm.DisableEncryption();
+ rStrm << sal_uInt32(0);
+ rStrm.EnableEncryption();
+ rStrm << nGrbit << GetName();
+}
+
+std::size_t ExcBundlesheet8::GetLen() const
+{ // Text max 255 chars
+ return 8 + GetName().GetBufferSize();
+}
+
+void ExcBundlesheet8::SaveXml( XclExpXmlStream& rStrm )
+{
+ OUString sId;
+ rStrm.CreateOutputStream(
+ XclXmlUtils::GetStreamName( "xl/", "worksheets/sheet", nTab+1),
+ XclXmlUtils::GetStreamName( nullptr, "worksheets/sheet", nTab+1),
+ rStrm.GetCurrentStream()->getOutputStream(),
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
+ oox::getRelationship(Relationship::WORKSHEET),
+ &sId );
+
+ rStrm.GetCurrentStream()->singleElement( XML_sheet,
+ XML_name, sUnicodeName.toUtf8(),
+ XML_sheetId, OString::number( nTab+1 ),
+ XML_state, nGrbit == 0x0000 ? "visible" : "hidden",
+ FSNS( XML_r, XML_id ), sId.toUtf8() );
+}
+
+// --- class XclObproj -----------------------------------------------
+
+sal_uInt16 XclObproj::GetNum() const
+{
+ return 0x00D3;
+}
+
+std::size_t XclObproj::GetLen() const
+{
+ return 0;
+}
+
+// ---- class XclCodename --------------------------------------------
+
+XclCodename::XclCodename( const OUString& r ) : aName( r )
+{
+}
+
+void XclCodename::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << aName;
+}
+
+sal_uInt16 XclCodename::GetNum() const
+{
+ return 0x01BA;
+}
+
+std::size_t XclCodename::GetLen() const
+{
+ return aName.GetSize();
+}
+
+// ---- Scenarios ----------------------------------------------------
+
+ExcEScenarioCell::ExcEScenarioCell( sal_uInt16 nC, sal_uInt16 nR, const OUString& rTxt ) :
+ nCol( nC ),
+ nRow( nR ),
+ sText( rTxt, XclStrFlags::NONE, 255 )
+{
+}
+
+void ExcEScenarioCell::WriteAddress( XclExpStream& rStrm ) const
+{
+ rStrm << nRow << nCol;
+}
+
+void ExcEScenarioCell::WriteText( XclExpStream& rStrm ) const
+{
+ rStrm << sText;
+}
+
+void ExcEScenarioCell::SaveXml( XclExpXmlStream& rStrm ) const
+{
+ rStrm.GetCurrentStream()->singleElement( XML_inputCells,
+ // OOXTODO: XML_deleted,
+ // OOXTODO: XML_numFmtId,
+ XML_r, XclXmlUtils::ToOString( rStrm.GetRoot().GetDoc(), ScAddress( nCol, nRow, 0 ) ),
+ // OOXTODO: XML_undone,
+ XML_val, XclXmlUtils::ToOString( sText ) );
+}
+
+ExcEScenario::ExcEScenario( const XclExpRoot& rRoot, SCTAB nTab )
+{
+ OUString sTmpName;
+ OUString sTmpComm;
+ OUString aTmp;
+ Color aDummyCol;
+ ScScenarioFlags nFlags;
+
+ ScDocument& rDoc = rRoot.GetDoc();
+ rDoc.GetName(nTab, aTmp);
+ sTmpName = aTmp;
+ sName.Assign( sTmpName, XclStrFlags::EightBitLength );
+ nRecLen = 8 + sName.GetBufferSize();
+
+ rDoc.GetScenarioData( nTab, aTmp, aDummyCol, nFlags );
+ sTmpComm = aTmp;
+ sComment.Assign( sTmpComm, XclStrFlags::NONE, 255 );
+ if( sComment.Len() )
+ nRecLen += sComment.GetSize();
+ bProtected = (nFlags & ScScenarioFlags::Protected) != ScScenarioFlags::NONE;
+
+ sUserName.Assign( rRoot.GetUserName(), XclStrFlags::NONE, 255 );
+ nRecLen += sUserName.GetSize();
+
+ const ScRangeList* pRList = rDoc.GetScenarioRanges( nTab );
+ if( !pRList )
+ return;
+
+ bool bContLoop = true;
+ SCROW nRow;
+ SCCOL nCol;
+ OUString sText;
+ double fVal;
+
+ for( size_t nRange = 0; (nRange < pRList->size()) && bContLoop; nRange++ )
+ {
+ const ScRange & rRange = (*pRList)[nRange];
+ for( nRow = rRange.aStart.Row(); (nRow <= rRange.aEnd.Row()) && bContLoop; nRow++ )
+ for( nCol = rRange.aStart.Col(); (nCol <= rRange.aEnd.Col()) && bContLoop; nCol++ )
+ {
+ if( rDoc.HasValueData( nCol, nRow, nTab ) )
+ {
+ fVal = rDoc.GetValue( nCol, nRow, nTab );
+ sText = ::rtl::math::doubleToUString( fVal,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max,
+ ScGlobal::getLocaleData().getNumDecimalSep()[0],
+ true );
+ }
+ else
+ sText = rDoc.GetString(nCol, nRow, nTab);
+ bContLoop = Append( static_cast<sal_uInt16>(nCol),
+ static_cast<sal_uInt16>(nRow), sText );
+ }
+ }
+}
+
+bool ExcEScenario::Append( sal_uInt16 nCol, sal_uInt16 nRow, const OUString& rTxt )
+{
+ if( aCells.size() == EXC_SCEN_MAXCELL )
+ return false;
+
+ ExcEScenarioCell aCell(nCol, nRow, rTxt);
+ aCells.push_back(aCell);
+ nRecLen += 6 + aCell.GetStringBytes(); // 4 bytes address, 2 bytes ifmt
+ return true;
+}
+
+void ExcEScenario::SaveCont( XclExpStream& rStrm )
+{
+ sal_uInt16 count = aCells.size();
+
+ rStrm << count // number of cells
+ << sal_uInt8(bProtected) // fProtection
+ << sal_uInt8(0) // fHidden
+ << static_cast<sal_uInt8>(sName.Len()) // length of scen name
+ << static_cast<sal_uInt8>(sComment.Len()) // length of comment
+ << static_cast<sal_uInt8>(sUserName.Len()); // length of user name
+ sName.WriteFlagField( rStrm );
+ sName.WriteBuffer( rStrm );
+
+ rStrm << sUserName;
+
+ if( sComment.Len() )
+ rStrm << sComment;
+
+ for( const auto& rCell : aCells )
+ rCell.WriteAddress( rStrm ); // pos of cell
+ for( const auto& rCell : aCells )
+ rCell.WriteText( rStrm ); // string content
+ rStrm.SetSliceSize( 2 );
+ rStrm.WriteZeroBytes( 2 * count ); // date format
+}
+
+sal_uInt16 ExcEScenario::GetNum() const
+{
+ return 0x00AF;
+}
+
+std::size_t ExcEScenario::GetLen() const
+{
+ return nRecLen;
+}
+
+void ExcEScenario::SaveXml( XclExpXmlStream& rStrm )
+{
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_scenario,
+ XML_name, XclXmlUtils::ToOString( sName ).getStr(),
+ XML_locked, ToPsz( bProtected ),
+ // OOXTODO: XML_hidden,
+ XML_count, OString::number( aCells.size() ).getStr(),
+ XML_user, XESTRING_TO_PSZ( sUserName ),
+ XML_comment, XESTRING_TO_PSZ( sComment ) );
+
+ for( const auto& rCell : aCells )
+ rCell.SaveXml( rStrm );
+
+ rWorkbook->endElement( XML_scenario );
+}
+
+ExcEScenarioManager::ExcEScenarioManager( const XclExpRoot& rRoot, SCTAB nTab ) :
+ nActive( 0 )
+{
+ ScDocument& rDoc = rRoot.GetDoc();
+ if( rDoc.IsScenario( nTab ) )
+ return;
+
+ SCTAB nFirstTab = nTab + 1;
+ SCTAB nNewTab = nFirstTab;
+
+ while( rDoc.IsScenario( nNewTab ) )
+ {
+ aScenes.emplace_back( rRoot, nNewTab );
+
+ if( rDoc.IsActiveScenario( nNewTab ) )
+ nActive = static_cast<sal_uInt16>(nNewTab - nFirstTab);
+ nNewTab++;
+ }
+}
+
+ExcEScenarioManager::~ExcEScenarioManager()
+{
+}
+
+void ExcEScenarioManager::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(aScenes.size()) // number of scenarios
+ << nActive // active scen
+ << nActive // last displayed
+ << sal_uInt16(0); // reference areas
+}
+
+void ExcEScenarioManager::Save( XclExpStream& rStrm )
+{
+ if( !aScenes.empty() )
+ ExcRecord::Save( rStrm );
+
+ for( ExcEScenario& rScenario : aScenes )
+ rScenario.Save( rStrm );
+}
+
+void ExcEScenarioManager::SaveXml( XclExpXmlStream& rStrm )
+{
+ if( aScenes.empty() )
+ return;
+
+ sax_fastparser::FSHelperPtr& rWorkbook = rStrm.GetCurrentStream();
+ rWorkbook->startElement( XML_scenarios,
+ XML_current, OString::number( nActive ),
+ XML_show, OString::number( nActive )
+ // OOXTODO: XML_sqref
+ );
+
+ for( ExcEScenario& rScenario : aScenes )
+ rScenario.SaveXml( rStrm );
+
+ rWorkbook->endElement( XML_scenarios );
+}
+
+sal_uInt16 ExcEScenarioManager::GetNum() const
+{
+ return 0x00AE;
+}
+
+std::size_t ExcEScenarioManager::GetLen() const
+{
+ return 8;
+}
+
+namespace {
+
+struct XclExpTabProtectOption
+{
+ ScTableProtection::Option eOption;
+ sal_uInt16 nMask;
+};
+
+}
+
+XclExpSheetProtectOptions::XclExpSheetProtectOptions( const XclExpRoot& rRoot, SCTAB nTab ) :
+ XclExpRecord( 0x0867, 23 )
+{
+ static const XclExpTabProtectOption aTable[] =
+ {
+ { ScTableProtection::OBJECTS, 0x0001 },
+ { ScTableProtection::SCENARIOS, 0x0002 },
+ { ScTableProtection::FORMAT_CELLS, 0x0004 },
+ { ScTableProtection::FORMAT_COLUMNS, 0x0008 },
+ { ScTableProtection::FORMAT_ROWS, 0x0010 },
+ { ScTableProtection::INSERT_COLUMNS, 0x0020 },
+ { ScTableProtection::INSERT_ROWS, 0x0040 },
+ { ScTableProtection::INSERT_HYPERLINKS, 0x0080 },
+
+ { ScTableProtection::DELETE_COLUMNS, 0x0100 },
+ { ScTableProtection::DELETE_ROWS, 0x0200 },
+ { ScTableProtection::SELECT_LOCKED_CELLS, 0x0400 },
+ { ScTableProtection::SORT, 0x0800 },
+ { ScTableProtection::AUTOFILTER, 0x1000 },
+ { ScTableProtection::PIVOT_TABLES, 0x2000 },
+ { ScTableProtection::SELECT_UNLOCKED_CELLS, 0x4000 },
+
+ { ScTableProtection::NONE, 0x0000 }
+ };
+
+ mnOptions = 0x0000;
+ const ScTableProtection* pProtect = rRoot.GetDoc().GetTabProtection(nTab);
+ if (!pProtect)
+ return;
+
+ for (int i = 0; aTable[i].nMask != 0x0000; ++i)
+ {
+ if ( pProtect->isOptionEnabled(aTable[i].eOption) )
+ mnOptions |= aTable[i].nMask;
+ }
+}
+
+void XclExpSheetProtectOptions::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 nBytes = 0x0867;
+ rStrm << nBytes;
+
+ for (int i = 0; i < 9; ++i)
+ rStrm << static_cast<unsigned char>(0);
+
+ nBytes = 0x0200;
+ rStrm << nBytes;
+ nBytes = 0x0100;
+ rStrm << nBytes;
+ nBytes = 0xFFFF;
+ rStrm << nBytes << nBytes;
+
+ rStrm << mnOptions;
+ nBytes = 0;
+ rStrm << nBytes;
+}
+
+XclExpSheetEnhancedProtection::XclExpSheetEnhancedProtection( const XclExpRoot& rRoot,
+ const ScEnhancedProtection & rProt ) :
+ XclExpRecord( 0x0868 ),
+ mrRoot( rRoot ),
+ maEnhancedProtection( rProt )
+{
+}
+
+void XclExpSheetEnhancedProtection::WriteBody( XclExpStream& rStrm )
+{
+ sal_uInt16 const nRecordType = 0x0868;
+ rStrm << nRecordType; // frtHeader rt
+ rStrm.WriteZeroBytesToRecord(10); // frtHeader unused
+ rStrm << EXC_ISFPROTECTION; // isf
+ rStrm.WriteZeroBytesToRecord(5); // reserved1 (1 bytes) and reserved2 (4 bytes)
+
+ XclRangeList aRefs;
+ if (maEnhancedProtection.maRangeList.is())
+ mrRoot.GetAddressConverter().ConvertRangeList( aRefs, *maEnhancedProtection.maRangeList, false);
+ sal_uInt16 nCref = ulimit_cast<sal_uInt16>(aRefs.size());
+ rStrm << nCref; // cref
+ rStrm.WriteZeroBytesToRecord(6); // cbFeatData if EXC_ISFFEC2 (4 bytes) and reserved3 (2 bytes)
+ aRefs.Write( rStrm, true, nCref); // refs
+
+ // FeatProtection structure
+ rStrm << maEnhancedProtection.mnAreserved; // 1 bit A and 31 bits reserved
+ rStrm << maEnhancedProtection.mnPasswordVerifier; // wPassword
+ rStrm << XclExpString( maEnhancedProtection.maTitle); // stTitle
+ bool bSDContainer = ((maEnhancedProtection.mnAreserved & 0x00000001) == 0x00000001);
+ sal_uInt32 nCbSD = maEnhancedProtection.maSecurityDescriptor.size();
+ SAL_WARN_IF( bSDContainer && nCbSD < 20, "sc.filter",
+ "XclExpSheetEnhancedProtection A flag indicates container but cbSD < 20");
+ SAL_WARN_IF( !bSDContainer && nCbSD > 0, "sc.filter",
+ "XclExpSheetEnhancedProtection A flag indicates no container but cbSD > 0");
+ if (bSDContainer)
+ {
+ rStrm << nCbSD;
+ rStrm.Write( &maEnhancedProtection.maSecurityDescriptor.front(), nCbSD);
+ }
+}
+
+void XclCalccount::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nCount;
+}
+
+XclCalccount::XclCalccount( const ScDocument& rDoc )
+{
+ nCount = rDoc.GetDocOptions().GetIterCount();
+}
+
+sal_uInt16 XclCalccount::GetNum() const
+{
+ return 0x000C;
+}
+
+std::size_t XclCalccount::GetLen() const
+{
+ return 2;
+}
+
+void XclCalccount::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_iterateCount, OUString::number(nCount));
+}
+
+void XclIteration::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << nIter;
+}
+
+XclIteration::XclIteration( const ScDocument& rDoc )
+{
+ nIter = rDoc.GetDocOptions().IsIter()? 1 : 0;
+}
+
+sal_uInt16 XclIteration::GetNum() const
+{
+ return 0x0011;
+}
+
+std::size_t XclIteration::GetLen() const
+{
+ return 2;
+}
+
+void XclIteration::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_iterate, ToPsz(nIter == 1));
+}
+
+void XclDelta::SaveCont( XclExpStream& rStrm )
+{
+ rStrm << fDelta;
+}
+
+XclDelta::XclDelta( const ScDocument& rDoc )
+{
+ fDelta = rDoc.GetDocOptions().GetIterEps();
+}
+
+sal_uInt16 XclDelta::GetNum() const
+{
+ return 0x0010;
+}
+
+std::size_t XclDelta::GetLen() const
+{
+ return 8;
+}
+
+void XclDelta::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_iterateDelta, OUString::number(fDelta));
+}
+
+XclExpFileEncryption::XclExpFileEncryption( const XclExpRoot& rRoot ) :
+ XclExpRecord(0x002F, 54),
+ mrRoot(rRoot)
+{
+}
+
+XclExpFileEncryption::~XclExpFileEncryption()
+{
+}
+
+void XclExpFileEncryption::WriteBody( XclExpStream& rStrm )
+{
+ // 0x0000 - neither standard nor strong encryption
+ // 0x0001 - standard or strong encryption
+ rStrm << static_cast<sal_uInt16>(0x0001);
+
+ // 0x0000 - non standard encryption
+ // 0x0001 - standard encryption
+ sal_uInt16 nStdEnc = 0x0001;
+ rStrm << nStdEnc << nStdEnc;
+
+ sal_uInt8 pnDocId[16];
+ sal_uInt8 pnSalt[16];
+ sal_uInt8 pnSaltHash[16];
+ XclExpEncrypterRef xEnc = std::make_shared<XclExpBiff8Encrypter>(mrRoot);
+ xEnc->GetDocId(pnDocId);
+ xEnc->GetSalt(pnSalt);
+ xEnc->GetSaltDigest(pnSaltHash);
+
+ rStrm.Write(pnDocId, 16);
+ rStrm.Write(pnSalt, 16);
+ rStrm.Write(pnSaltHash, 16);
+
+ rStrm.SetEncrypter(xEnc);
+}
+
+XclExpInterfaceHdr::XclExpInterfaceHdr( sal_uInt16 nCodePage ) :
+ XclExpUInt16Record( EXC_ID_INTERFACEHDR, nCodePage )
+{
+}
+
+void XclExpInterfaceHdr::WriteBody( XclExpStream& rStrm )
+{
+ rStrm.DisableEncryption();
+ rStrm << GetValue();
+}
+
+XclExpInterfaceEnd::XclExpInterfaceEnd() :
+ XclExpRecord(0x00E2, 0) {}
+
+XclExpInterfaceEnd::~XclExpInterfaceEnd() {}
+
+void XclExpInterfaceEnd::WriteBody( XclExpStream& rStrm )
+{
+ // Don't forget to re-enable encryption.
+ rStrm.EnableEncryption();
+}
+
+XclExpWriteAccess::XclExpWriteAccess() :
+ XclExpRecord(0x005C, 112)
+{
+}
+
+XclExpWriteAccess::~XclExpWriteAccess()
+{
+}
+
+void XclExpWriteAccess::WriteBody( XclExpStream& rStrm )
+{
+ static const sal_uInt8 aData[] = {
+ 0x04, 0x00, 0x00, 'C', 'a', 'l', 'c', 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
+
+ for (std::size_t i = 0; i < sizeof(aData); ++i)
+ rStrm << aData[i];
+}
+
+XclExpFileSharing::XclExpFileSharing( const XclExpRoot& rRoot, sal_uInt16 nPasswordHash, bool bRecommendReadOnly ) :
+ XclExpRecord( EXC_ID_FILESHARING ),
+ mnPasswordHash( nPasswordHash ),
+ mbRecommendReadOnly( bRecommendReadOnly )
+{
+ if( rRoot.GetBiff() <= EXC_BIFF5 )
+ maUserName.AssignByte( rRoot.GetUserName(), rRoot.GetTextEncoding(), XclStrFlags::EightBitLength );
+ else
+ maUserName.Assign( rRoot.GetUserName() );
+}
+
+void XclExpFileSharing::Save( XclExpStream& rStrm )
+{
+ if( (mnPasswordHash != 0) || mbRecommendReadOnly )
+ XclExpRecord::Save( rStrm );
+}
+
+void XclExpFileSharing::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << sal_uInt16( mbRecommendReadOnly ? 1 : 0 ) << mnPasswordHash << maUserName;
+}
+
+XclExpProt4Rev::XclExpProt4Rev() :
+ XclExpRecord(0x01AF, 2)
+{
+}
+
+XclExpProt4Rev::~XclExpProt4Rev()
+{
+}
+
+void XclExpProt4Rev::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(0x0000);
+}
+
+XclExpProt4RevPass::XclExpProt4RevPass() :
+ XclExpRecord(0x01BC, 2)
+{
+}
+
+XclExpProt4RevPass::~XclExpProt4RevPass()
+{
+}
+
+void XclExpProt4RevPass::WriteBody( XclExpStream& rStrm )
+{
+ rStrm << static_cast<sal_uInt16>(0x0000);
+}
+
+const sal_uInt8 nDataRecalcId[] = {
+ 0xC1, 0x01, 0x00, 0x00, 0x54, 0x8D, 0x01, 0x00
+};
+
+XclExpRecalcId::XclExpRecalcId() :
+ XclExpDummyRecord(0x01C1, nDataRecalcId, sizeof(nDataRecalcId))
+{
+}
+
+const sal_uInt8 nDataBookExt[] = {
+ 0x63, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02
+};
+
+XclExpBookExt::XclExpBookExt() :
+ XclExpDummyRecord(0x0863, nDataBookExt, sizeof(nDataBookExt))
+{
+}
+
+XclRefmode::XclRefmode( const ScDocument& rDoc ) :
+ XclExpBoolRecord( 0x000F, rDoc.GetAddressConvention() != formula::FormulaGrammar::CONV_XL_R1C1 )
+{
+}
+
+void XclRefmode::SaveXml( XclExpXmlStream& rStrm )
+{
+ rStrm.WriteAttributes(XML_refMode, GetBool() ? "A1" : "R1C1");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/SparklineGroupsExport.cxx b/sc/source/filter/xml/SparklineGroupsExport.cxx
new file mode 100644
index 000000000..077d43bea
--- /dev/null
+++ b/sc/source/filter/xml/SparklineGroupsExport.cxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include "SparklineGroupsExport.hxx"
+#include "xmlexprt.hxx"
+#include <rangeutl.hxx>
+#include <SparklineList.hxx>
+#include <document.hxx>
+
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sax/tools/converter.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+using namespace css;
+using namespace xmloff::token;
+
+namespace sc
+{
+SparklineGroupsExport::SparklineGroupsExport(ScXMLExport& rExport, SCTAB nTable)
+ : m_rExport(rExport)
+ , m_nTable(nTable)
+{
+}
+
+void SparklineGroupsExport::insertColor(Color aColor, XMLTokenEnum eToken)
+{
+ OUStringBuffer aStringBuffer;
+ if (aColor != COL_TRANSPARENT)
+ {
+ sax::Converter::convertColor(aStringBuffer, aColor);
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, eToken, aStringBuffer.makeStringAndClear());
+ }
+}
+
+void SparklineGroupsExport::insertBool(bool bValue, XMLTokenEnum eToken)
+{
+ if (bValue)
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, eToken, "true");
+}
+
+void SparklineGroupsExport::addSparklineAttributes(Sparkline const& rSparkline)
+{
+ auto const* pDocument = m_rExport.GetDocument();
+
+ {
+ OUString sAddressString;
+ ScAddress aAddress(rSparkline.getColumn(), rSparkline.getRow(), m_nTable);
+ ScRangeStringConverter::GetStringFromAddress(sAddressString, aAddress, pDocument,
+ formula::FormulaGrammar::CONV_OOO);
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CELL_ADDRESS, sAddressString);
+ }
+
+ {
+ OUString sDataRangeString;
+ ScRangeList const& rRangeList = rSparkline.getInputRange();
+ ScRangeStringConverter::GetStringFromRangeList(sDataRangeString, &rRangeList, pDocument,
+ formula::FormulaGrammar::CONV_OOO);
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DATA_RANGE, sDataRangeString);
+ }
+}
+
+namespace
+{
+OUString convertSparklineType(sc::SparklineType eType)
+{
+ switch (eType)
+ {
+ case sc::SparklineType::Line:
+ return u"line";
+ case sc::SparklineType::Column:
+ return u"column";
+ case sc::SparklineType::Stacked:
+ return u"stacked";
+ }
+ return u"";
+}
+
+OUString convertDisplayEmptyCellsAs(sc::DisplayEmptyCellsAs eType)
+{
+ switch (eType)
+ {
+ case sc::DisplayEmptyCellsAs::Zero:
+ return u"zero";
+ case sc::DisplayEmptyCellsAs::Gap:
+ return u"gap";
+ case sc::DisplayEmptyCellsAs::Span:
+ return u"span";
+ }
+ return u"";
+}
+
+OUString convertAxisType(sc::AxisType eType)
+{
+ switch (eType)
+ {
+ case sc::AxisType::Individual:
+ return u"individual";
+ case sc::AxisType::Group:
+ return u"group";
+ case sc::AxisType::Custom:
+ return u"custom";
+ }
+ return u"";
+}
+
+} // end anonymous ns
+
+void SparklineGroupsExport::addSparklineGroupAttributes(SparklineAttributes const& rAttributes)
+{
+ OUString sType = convertSparklineType(rAttributes.getType());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, sType);
+
+ // Line Weight = Line Width in ODF
+
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_LINE_WIDTH,
+ OUString::number(rAttributes.getLineWeight()) + "pt");
+
+ insertBool(rAttributes.isDateAxis(), XML_DATE_AXIS);
+
+ OUString sDisplayEmptyCellsAs
+ = convertDisplayEmptyCellsAs(rAttributes.getDisplayEmptyCellsAs());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DISPLAY_EMPTY_CELLS_AS,
+ sDisplayEmptyCellsAs);
+
+ insertBool(rAttributes.isMarkers(), XML_MARKERS);
+ insertBool(rAttributes.isHigh(), XML_HIGH);
+ insertBool(rAttributes.isLow(), XML_LOW);
+ insertBool(rAttributes.isFirst(), XML_FIRST);
+ insertBool(rAttributes.isLast(), XML_LAST);
+ insertBool(rAttributes.isNegative(), XML_NEGATIVE);
+ insertBool(rAttributes.shouldDisplayXAxis(), XML_DISPLAY_X_AXIS);
+ insertBool(rAttributes.shouldDisplayHidden(), XML_DISPLAY_HIDDEN);
+
+ OUString sMinAxisType = convertAxisType(rAttributes.getMinAxisType());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MIN_AXIS_TYPE, sMinAxisType);
+
+ OUString sMaxAxisType = convertAxisType(rAttributes.getMaxAxisType());
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MAX_AXIS_TYPE, sMaxAxisType);
+
+ insertBool(rAttributes.isRightToLeft(), XML_RIGHT_TO_LEFT);
+
+ if (rAttributes.getManualMax() && rAttributes.getMaxAxisType() == sc::AxisType::Custom)
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MANUAL_MAX,
+ OUString::number(*rAttributes.getManualMax()));
+
+ if (rAttributes.getManualMin() && rAttributes.getMinAxisType() == sc::AxisType::Custom)
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MANUAL_MIN,
+ OUString::number(*rAttributes.getManualMin()));
+
+ insertColor(rAttributes.getColorSeries(), XML_COLOR_SERIES);
+ insertColor(rAttributes.getColorNegative(), XML_COLOR_NEGATIVE);
+ insertColor(rAttributes.getColorAxis(), XML_COLOR_AXIS);
+ insertColor(rAttributes.getColorMarkers(), XML_COLOR_MARKERS);
+ insertColor(rAttributes.getColorFirst(), XML_COLOR_FIRST);
+ insertColor(rAttributes.getColorLast(), XML_COLOR_LAST);
+ insertColor(rAttributes.getColorHigh(), XML_COLOR_HIGH);
+ insertColor(rAttributes.getColorLow(), XML_COLOR_LOW);
+}
+
+void SparklineGroupsExport::addSparklineGroup(
+ std::shared_ptr<SparklineGroup> const& pSparklineGroup,
+ std::vector<std::shared_ptr<Sparkline>> const& rSparklines)
+{
+ auto const& rAttributes = pSparklineGroup->getAttributes();
+
+ OUString sID = pSparklineGroup->getID().getOUString();
+ m_rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_ID, sID);
+
+ addSparklineGroupAttributes(rAttributes);
+
+ SvXMLElementExport aElementSparklineGroup(m_rExport, XML_NAMESPACE_CALC_EXT,
+ XML_SPARKLINE_GROUP, true, true);
+
+ SvXMLElementExport aElementSparklines(m_rExport, XML_NAMESPACE_CALC_EXT, XML_SPARKLINES, true,
+ true);
+
+ for (auto const& rSparkline : rSparklines)
+ {
+ addSparklineAttributes(*rSparkline);
+ SvXMLElementExport aElementSparkline(m_rExport, XML_NAMESPACE_CALC_EXT, XML_SPARKLINE, true,
+ true);
+ }
+}
+
+void SparklineGroupsExport::write()
+{
+ auto* pDocument = m_rExport.GetDocument();
+ if (sc::SparklineList* pSparklineList = pDocument->GetSparklineList(m_nTable))
+ {
+ auto const& aSparklineGroups = pSparklineList->getSparklineGroups();
+ if (!aSparklineGroups.empty())
+ {
+ SvXMLElementExport aElement(m_rExport, XML_NAMESPACE_CALC_EXT, XML_SPARKLINE_GROUPS,
+ true, true);
+
+ for (auto const& pSparklineGroup : aSparklineGroups)
+ {
+ auto const& aSparklines = pSparklineList->getSparklinesFor(pSparklineGroup);
+ addSparklineGroup(pSparklineGroup, aSparklines);
+ }
+ }
+ }
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/SparklineGroupsExport.hxx b/sc/source/filter/xml/SparklineGroupsExport.hxx
new file mode 100644
index 000000000..935941373
--- /dev/null
+++ b/sc/source/filter/xml/SparklineGroupsExport.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <tools/color.hxx>
+#include <xmloff/xmltoken.hxx>
+
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+
+class ScXMLExport;
+
+namespace sc
+{
+/** Handle the export of sparkline groups and sparklines */
+class SparklineGroupsExport
+{
+ ScXMLExport& m_rExport;
+ SCTAB m_nTable;
+
+ void addSparklineGroupAttributes(sc::SparklineAttributes const& rAttributes);
+ void addSparklineGroup(std::shared_ptr<SparklineGroup> const& pSparklineGroup,
+ std::vector<std::shared_ptr<Sparkline>> const& rSparklines);
+ void addSparklineAttributes(Sparkline const& rSparkline);
+
+ void insertColor(Color aColor, xmloff::token::XMLTokenEnum eToken);
+ void insertBool(bool bValue, xmloff::token::XMLTokenEnum eToken);
+
+public:
+ SparklineGroupsExport(ScXMLExport& rExport, SCTAB nTable);
+
+ void write();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/SparklineGroupsImportContext.cxx b/sc/source/filter/xml/SparklineGroupsImportContext.cxx
new file mode 100644
index 000000000..b1164e3ef
--- /dev/null
+++ b/sc/source/filter/xml/SparklineGroupsImportContext.cxx
@@ -0,0 +1,333 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SparklineGroupsImportContext.hxx"
+
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmluconv.hxx>
+
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <Sparkline.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineAttributes.hxx>
+
+using namespace xmloff::token;
+using namespace css;
+
+namespace sc
+{
+SparklineGroupsImportContext::SparklineGroupsImportContext(ScXMLImport& rImport)
+ : ScXMLImportContext(rImport)
+{
+}
+
+namespace
+{
+sc::SparklineType parseSparklineType(std::string_view aString)
+{
+ if (aString == "column")
+ return sc::SparklineType::Column;
+ else if (aString == "stacked")
+ return sc::SparklineType::Stacked;
+ return sc::SparklineType::Line;
+}
+
+sc::DisplayEmptyCellsAs parseDisplayEmptyCellsAs(std::string_view aString)
+{
+ if (aString == "span")
+ return sc::DisplayEmptyCellsAs::Span;
+ else if (aString == "gap")
+ return sc::DisplayEmptyCellsAs::Gap;
+ return sc::DisplayEmptyCellsAs::Zero;
+}
+
+sc::AxisType parseAxisType(std::string_view aString)
+{
+ if (aString == "group")
+ return sc::AxisType::Group;
+ else if (aString == "custom")
+ return sc::AxisType::Custom;
+ return sc::AxisType::Individual;
+}
+
+} // end anonymous namespace
+
+void SparklineGroupsImportContext::fillSparklineGroupID(
+ uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
+{
+ for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (rIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_ID):
+ {
+ tools::Guid aGuid(rIter.toView());
+ m_pCurrentSparklineGroup->setID(aGuid);
+ break;
+ }
+ }
+ }
+}
+
+void SparklineGroupsImportContext::fillSparklineGroupAttributes(
+ uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
+{
+ sc::SparklineAttributes& rAttributes = m_pCurrentSparklineGroup->getAttributes();
+
+ for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (rIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ rAttributes.setType(parseSparklineType(rIter.toView()));
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_LINE_WIDTH):
+ {
+ OUString sLineWidth = rIter.toString();
+ double fLineWidth;
+ sal_Int16 const eSrcUnit
+ = ::sax::Converter::GetUnitFromString(sLineWidth, util::MeasureUnit::POINT);
+ ::sax::Converter::convertDouble(fLineWidth, sLineWidth, eSrcUnit,
+ util::MeasureUnit::POINT);
+ rAttributes.setLineWeight(fLineWidth);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DATE_AXIS):
+ {
+ rAttributes.setDateAxis(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DISPLAY_EMPTY_CELLS_AS):
+ {
+ auto eDisplayEmptyCellsAs = parseDisplayEmptyCellsAs(rIter.toView());
+ rAttributes.setDisplayEmptyCellsAs(eDisplayEmptyCellsAs);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MARKERS):
+ {
+ rAttributes.setMarkers(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_HIGH):
+ {
+ rAttributes.setHigh(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_LOW):
+ {
+ rAttributes.setLow(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_FIRST):
+ {
+ rAttributes.setFirst(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_LAST):
+ {
+ rAttributes.setLast(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_NEGATIVE):
+ {
+ rAttributes.setNegative(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DISPLAY_X_AXIS):
+ {
+ rAttributes.setDisplayXAxis(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DISPLAY_HIDDEN):
+ {
+ rAttributes.setDisplayHidden(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MIN_AXIS_TYPE):
+ {
+ rAttributes.setMinAxisType(parseAxisType(rIter.toView()));
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MAX_AXIS_TYPE):
+ {
+ rAttributes.setMaxAxisType(parseAxisType(rIter.toView()));
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_RIGHT_TO_LEFT):
+ {
+ rAttributes.setRightToLeft(rIter.toBoolean());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MANUAL_MAX):
+ {
+ rAttributes.setManualMax(rIter.toDouble());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_MANUAL_MIN):
+ {
+ rAttributes.setManualMin(rIter.toDouble());
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_SERIES):
+ {
+ Color aColor;
+ sax::Converter::convertColor(aColor, rIter.toView());
+ rAttributes.setColorSeries(aColor);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_NEGATIVE):
+ {
+ Color aColor;
+ sax::Converter::convertColor(aColor, rIter.toView());
+ rAttributes.setColorNegative(aColor);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_AXIS):
+ {
+ Color aColor;
+ sax::Converter::convertColor(aColor, rIter.toView());
+ rAttributes.setColorAxis(aColor);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_MARKERS):
+ {
+ Color aColor;
+ sax::Converter::convertColor(aColor, rIter.toView());
+ rAttributes.setColorMarkers(aColor);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_FIRST):
+ {
+ Color aColor;
+ sax::Converter::convertColor(aColor, rIter.toView());
+ rAttributes.setColorFirst(aColor);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_LAST):
+ {
+ Color aColor;
+ sax::Converter::convertColor(aColor, rIter.toView());
+ rAttributes.setColorLast(aColor);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_HIGH):
+ {
+ Color aColor;
+ sax::Converter::convertColor(aColor, rIter.toView());
+ rAttributes.setColorHigh(aColor);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_COLOR_LOW):
+ {
+ Color aColor;
+ sax::Converter::convertColor(aColor, rIter.toView());
+ rAttributes.setColorLow(aColor);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void SparklineGroupsImportContext::fillSparklineAttributes(
+ SparklineImportData& rImportData, uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
+{
+ ScDocument* pDocument = GetScImport().GetDocument();
+
+ for (auto& rIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (rIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_CELL_ADDRESS):
+ {
+ sal_Int32 nOffset = 0;
+ ScRangeStringConverter::GetAddressFromString(
+ rImportData.m_aAddress, rIter.toString(), *pDocument,
+ formula::FormulaGrammar::CONV_OOO, nOffset);
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_DATA_RANGE):
+ {
+ ScRangeStringConverter::GetRangeListFromString(rImportData.m_aDataRangeList,
+ rIter.toString(), *pDocument,
+ formula::FormulaGrammar::CONV_OOO);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL SparklineGroupsImportContext::createFastChildContext(
+ sal_Int32 nElement, uno::Reference<xml::sax::XFastAttributeList> const& xAttrList)
+{
+ SvXMLImportContext* pContext = nullptr;
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_GROUP):
+ {
+ m_pCurrentSparklineGroup = std::make_shared<sc::SparklineGroup>();
+ fillSparklineGroupID(xAttrList);
+ fillSparklineGroupAttributes(xAttrList);
+ pContext = this;
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINES):
+ {
+ pContext = this;
+ break;
+ }
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE):
+ {
+ SparklineImportData& rImportData = m_aCurrentSparklineDataList.emplace_back();
+ fillSparklineAttributes(rImportData, xAttrList);
+ pContext = this;
+ break;
+ }
+ }
+
+ return pContext;
+}
+
+void SparklineGroupsImportContext::insertSparklines()
+{
+ ScDocument* pDocument = GetScImport().GetDocument();
+ for (auto const& rSparklineImportData : m_aCurrentSparklineDataList)
+ {
+ auto* pSparkline
+ = pDocument->CreateSparkline(rSparklineImportData.m_aAddress, m_pCurrentSparklineGroup);
+ pSparkline->setInputRange(rSparklineImportData.m_aDataRangeList);
+ }
+}
+
+void SAL_CALL SparklineGroupsImportContext::endFastElement(sal_Int32 nElement)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_GROUP):
+ {
+ insertSparklines();
+ m_pCurrentSparklineGroup.reset();
+ m_aCurrentSparklineDataList.clear();
+ break;
+ }
+ }
+}
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/SparklineGroupsImportContext.hxx b/sc/source/filter/xml/SparklineGroupsImportContext.hxx
new file mode 100644
index 000000000..b8161c965
--- /dev/null
+++ b/sc/source/filter/xml/SparklineGroupsImportContext.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <memory>
+#include "importcontext.hxx"
+#include "xmlimprt.hxx"
+#include <address.hxx>
+#include <rangelst.hxx>
+
+namespace sax_fastparser
+{
+class FastAttributeList;
+}
+
+namespace sc
+{
+class SparklineGroup;
+
+/** Transitional import data of a sparkline */
+struct SparklineImportData
+{
+ ScAddress m_aAddress;
+ ScRangeList m_aDataRangeList;
+};
+
+/** Handle the import of sparkline groups and sparklines */
+class SparklineGroupsImportContext : public ScXMLImportContext
+{
+private:
+ std::shared_ptr<sc::SparklineGroup> m_pCurrentSparklineGroup;
+ std::vector<SparklineImportData> m_aCurrentSparklineDataList;
+
+ void
+ fillSparklineGroupID(css::uno::Reference<css::xml::sax::XFastAttributeList> const& xAttrList);
+ void fillSparklineGroupAttributes(
+ css::uno::Reference<css::xml::sax::XFastAttributeList> const& xAttrList);
+ void fillSparklineAttributes(
+ SparklineImportData& rImportData,
+ css::uno::Reference<css::xml::sax::XFastAttributeList> const& xAttrList);
+
+ void insertSparklines();
+
+public:
+ SparklineGroupsImportContext(ScXMLImport& rImport);
+
+ css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ css::uno::Reference<css::xml::sax::XFastAttributeList> const& xAttrList) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+} // end sc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCalculationSettingsContext.cxx b/sc/source/filter/xml/XMLCalculationSettingsContext.cxx
new file mode 100644
index 000000000..43da1437e
--- /dev/null
+++ b/sc/source/filter/xml/XMLCalculationSettingsContext.cxx
@@ -0,0 +1,198 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLCalculationSettingsContext.hxx"
+#include "xmlimprt.hxx"
+#include <unonames.hxx>
+#include <docoptio.hxx>
+#include <document.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <sax/tools/converter.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLCalculationSettingsContext::ScXMLCalculationSettingsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ fIterationEpsilon(0.001),
+ nIterationCount(100),
+ nYear2000(1930),
+ eSearchType(utl::SearchParam::SearchType::Regexp),
+ bIsIterationEnabled(false),
+ bCalcAsShown(false),
+ bIgnoreCase(false),
+ bLookUpLabels(true),
+ bMatchWholeCell(true)
+{
+ aNullDate.Day = 30;
+ aNullDate.Month = 12;
+ aNullDate.Year = 1899;
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ if( IsXMLToken( aIter, XML_FALSE ) )
+ bIgnoreCase = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_PRECISION_AS_SHOWN ):
+ if( IsXMLToken( aIter, XML_TRUE ) )
+ bCalcAsShown = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_SEARCH_CRITERIA_MUST_APPLY_TO_WHOLE_CELL ):
+ if( IsXMLToken( aIter, XML_FALSE ) )
+ bMatchWholeCell = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_AUTOMATIC_FIND_LABELS ):
+ if( IsXMLToken( aIter, XML_FALSE ) )
+ bLookUpLabels = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_NULL_YEAR ):
+ sal_Int32 nTemp;
+ ::sax::Converter::convertNumber( nTemp, aIter.toView() );
+ nYear2000 = static_cast<sal_uInt16>(nTemp);
+ break;
+ case XML_ELEMENT( TABLE, XML_USE_REGULAR_EXPRESSIONS ):
+ // Overwrite only the default (regex true) value, not wildcard.
+ if( eSearchType == utl::SearchParam::SearchType::Regexp && IsXMLToken( aIter, XML_FALSE ) )
+ eSearchType = utl::SearchParam::SearchType::Normal;
+ break;
+ case XML_ELEMENT( TABLE, XML_USE_WILDCARDS ):
+ if( IsXMLToken( aIter, XML_TRUE ) )
+ eSearchType = utl::SearchParam::SearchType::Wildcard;
+ break;
+ }
+ }
+}
+
+ScXMLCalculationSettingsContext::~ScXMLCalculationSettingsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCalculationSettingsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT( TABLE, XML_NULL_DATE ))
+ pContext = new ScXMLNullDateContext(GetScImport(), pAttribList, this);
+ else if (nElement == XML_ELEMENT( TABLE, XML_ITERATION ))
+ pContext = new ScXMLIterationContext(GetScImport(), pAttribList, this);
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLCalculationSettingsContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!GetScImport().GetModel().is())
+ return;
+
+ uno::Reference <beans::XPropertySet> xPropertySet (GetScImport().GetModel(), uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ xPropertySet->setPropertyValue( SC_UNO_CALCASSHOWN, uno::Any(bCalcAsShown) );
+ xPropertySet->setPropertyValue( SC_UNO_IGNORECASE, uno::Any(bIgnoreCase) );
+ xPropertySet->setPropertyValue( SC_UNO_LOOKUPLABELS, uno::Any(bLookUpLabels) );
+ xPropertySet->setPropertyValue( SC_UNO_MATCHWHOLE, uno::Any(bMatchWholeCell) );
+ bool bWildcards, bRegex;
+ utl::SearchParam::ConvertToBool( eSearchType, bWildcards, bRegex);
+ xPropertySet->setPropertyValue( SC_UNO_REGEXENABLED, uno::Any(bRegex) );
+ xPropertySet->setPropertyValue( SC_UNO_WILDCARDSENABLED, uno::Any(bWildcards) );
+ xPropertySet->setPropertyValue( SC_UNO_ITERENABLED, uno::Any(bIsIterationEnabled) );
+ xPropertySet->setPropertyValue( SC_UNO_ITERCOUNT, uno::Any(nIterationCount) );
+ xPropertySet->setPropertyValue( SC_UNO_ITEREPSILON, uno::Any(fIterationEpsilon) );
+ xPropertySet->setPropertyValue( SC_UNO_NULLDATE, uno::Any(aNullDate) );
+ if (GetScImport().GetDocument())
+ {
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ ScDocOptions aDocOptions (GetScImport().GetDocument()->GetDocOptions());
+ aDocOptions.SetYear2000(nYear2000);
+ GetScImport().GetDocument()->SetDocOptions(aDocOptions);
+ }
+}
+
+ScXMLNullDateContext::ScXMLNullDateContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLCalculationSettingsContext* pCalcSet) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_DATE_VALUE ) ) );
+ if (aIter != rAttrList->end())
+ {
+ util::DateTime aDateTime;
+ if (::sax::Converter::parseDateTime(aDateTime, aIter.toView()))
+ {
+ util::Date aDate;
+ aDate.Day = aDateTime.Day;
+ aDate.Month = aDateTime.Month;
+ aDate.Year = aDateTime.Year;
+ pCalcSet->SetNullDate(aDate);
+ }
+ }
+}
+
+ScXMLNullDateContext::~ScXMLNullDateContext()
+{
+}
+
+ScXMLIterationContext::ScXMLIterationContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLCalculationSettingsContext* pCalcSet) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_STATUS ):
+ if (IsXMLToken(aIter, XML_ENABLE))
+ pCalcSet->SetIterationStatus(true);
+ break;
+ case XML_ELEMENT( TABLE, XML_STEPS ):
+ pCalcSet->SetIterationCount(aIter.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_MAXIMUM_DIFFERENCE ):
+ pCalcSet->SetIterationEpsilon( aIter.toDouble() );
+ break;
+ }
+ }
+}
+
+ScXMLIterationContext::~ScXMLIterationContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCalculationSettingsContext.hxx b/sc/source/filter/xml/XMLCalculationSettingsContext.hxx
new file mode 100644
index 000000000..45bcdfb6c
--- /dev/null
+++ b/sc/source/filter/xml/XMLCalculationSettingsContext.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <unotools/textsearch.hxx>
+#include <com/sun/star/util/Date.hpp>
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+
+class ScXMLCalculationSettingsContext : public ScXMLImportContext
+{
+ css::util::Date aNullDate;
+ double fIterationEpsilon;
+ sal_Int32 nIterationCount;
+ sal_uInt16 nYear2000;
+ utl::SearchParam::SearchType eSearchType;
+ bool bIsIterationEnabled;
+ bool bCalcAsShown;
+ bool bIgnoreCase;
+ bool bLookUpLabels;
+ bool bMatchWholeCell;
+
+public:
+ ScXMLCalculationSettingsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLCalculationSettingsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void SetNullDate(const css::util::Date& aDate) { aNullDate = aDate; }
+ void SetIterationStatus(const bool bValue) { bIsIterationEnabled = bValue; }
+ void SetIterationCount(const sal_Int32 nValue) { nIterationCount = nValue; }
+ void SetIterationEpsilon(const double fValue) { fIterationEpsilon = fValue; }
+ virtual void SAL_CALL endFastElement( sal_Int32 Element ) override;
+};
+
+class ScXMLNullDateContext : public ScXMLImportContext
+{
+public:
+ ScXMLNullDateContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLCalculationSettingsContext* pCalcSet);
+
+ virtual ~ScXMLNullDateContext() override;
+};
+
+class ScXMLIterationContext : public ScXMLImportContext
+{
+public:
+ ScXMLIterationContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLCalculationSettingsContext* pCalcSet);
+
+ virtual ~ScXMLIterationContext() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCellRangeSourceContext.cxx b/sc/source/filter/xml/XMLCellRangeSourceContext.cxx
new file mode 100644
index 000000000..72bdc7b90
--- /dev/null
+++ b/sc/source/filter/xml/XMLCellRangeSourceContext.cxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLCellRangeSourceContext.hxx"
+
+#include <sax/tools/converter.hxx>
+
+#include "xmlimprt.hxx"
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScMyImpCellRangeSource::ScMyImpCellRangeSource() :
+ nColumns( 0 ),
+ nRows( 0 ),
+ nRefresh( 0 )
+{
+}
+
+ScXMLCellRangeSourceContext::ScXMLCellRangeSourceContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScMyImpCellRangeSource* pCellRangeSource ) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ pCellRangeSource->sSourceStr = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_NAME ):
+ pCellRangeSource->sFilterName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_OPTIONS ):
+ pCellRangeSource->sFilterOptions = aIter.toString();
+ break;
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ pCellRangeSource->sURL = GetScImport().GetAbsoluteReference(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_LAST_COLUMN_SPANNED ):
+ {
+ sal_Int32 nValue;
+ if (::sax::Converter::convertNumber( nValue, aIter.toView(), 1 ))
+ pCellRangeSource->nColumns = nValue;
+ else
+ pCellRangeSource->nColumns = 1;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_LAST_ROW_SPANNED ):
+ {
+ sal_Int32 nValue;
+ if (::sax::Converter::convertNumber( nValue, aIter.toView(), 1 ))
+ pCellRangeSource->nRows = nValue;
+ else
+ pCellRangeSource->nRows = 1;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_REFRESH_DELAY ):
+ {
+ double fTime;
+ if (::sax::Converter::convertDuration( fTime, aIter.toView() ))
+ pCellRangeSource->nRefresh = std::max( static_cast<sal_Int32>(fTime * 86400.0), sal_Int32(0) );
+ }
+ break;
+ }
+ }
+}
+
+ScXMLCellRangeSourceContext::~ScXMLCellRangeSourceContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCellRangeSourceContext.hxx b/sc/source/filter/xml/XMLCellRangeSourceContext.hxx
new file mode 100644
index 000000000..9b6fa18d1
--- /dev/null
+++ b/sc/source/filter/xml/XMLCellRangeSourceContext.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+
+struct ScMyImpCellRangeSource
+{
+ OUString sSourceStr;
+ OUString sFilterName;
+ OUString sFilterOptions;
+ OUString sURL;
+ sal_Int32 nColumns;
+ sal_Int32 nRows;
+ sal_Int32 nRefresh;
+
+ ScMyImpCellRangeSource();
+};
+
+class ScXMLCellRangeSourceContext : public ScXMLImportContext
+{
+public:
+ ScXMLCellRangeSourceContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScMyImpCellRangeSource* pCellRangeSource
+ );
+ virtual ~ScXMLCellRangeSourceContext() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLChangeTrackingExportHelper.cxx b/sc/source/filter/xml/XMLChangeTrackingExportHelper.cxx
new file mode 100644
index 000000000..6195b95a7
--- /dev/null
+++ b/sc/source/filter/xml/XMLChangeTrackingExportHelper.cxx
@@ -0,0 +1,687 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLChangeTrackingExportHelper.hxx"
+#include "xmlexprt.hxx"
+#include "XMLConverter.hxx"
+#include <document.hxx>
+#include <chgtrack.hxx>
+#include <formulacell.hxx>
+#include <textuno.hxx>
+#include <rangeutl.hxx>
+#include <cellvalue.hxx>
+#include <editutil.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <sax/tools/converter.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstring.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScChangeTrackingExportHelper::ScChangeTrackingExportHelper(ScXMLExport& rTempExport)
+ : rExport(rTempExport),
+ pChangeTrack(nullptr)
+{
+ pChangeTrack = rExport.GetDocument() ? rExport.GetDocument()->GetChangeTrack() : nullptr;
+}
+
+ScChangeTrackingExportHelper::~ScChangeTrackingExportHelper()
+{
+}
+
+OUString ScChangeTrackingExportHelper::GetChangeID(const sal_uInt32 nActionNumber)
+{
+ return "ct" + OUString::number(nActionNumber);
+}
+
+void ScChangeTrackingExportHelper::GetAcceptanceState(const ScChangeAction* pAction)
+{
+ if (pAction->IsRejected())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ACCEPTANCE_STATE, XML_REJECTED);
+ else if (pAction->IsAccepted())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ACCEPTANCE_STATE, XML_ACCEPTED);
+}
+
+void ScChangeTrackingExportHelper::WriteBigRange(const ScBigRange& rBigRange, XMLTokenEnum aName)
+{
+ sal_Int64 nStartColumn;
+ sal_Int64 nEndColumn;
+ sal_Int64 nStartRow;
+ sal_Int64 nEndRow;
+ sal_Int64 nStartSheet;
+ sal_Int64 nEndSheet;
+ rBigRange.GetVars(nStartColumn, nStartRow, nStartSheet,
+ nEndColumn, nEndRow, nEndSheet);
+ if ((nStartColumn == nEndColumn) && (nStartRow == nEndRow) && (nStartSheet == nEndSheet))
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_COLUMN, OUString::number(nStartColumn));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ROW, OUString::number(nStartRow));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE, OUString::number(nStartSheet));
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START_COLUMN, OUString::number(nStartColumn));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START_ROW, OUString::number(nStartRow));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START_TABLE, OUString::number(nStartSheet));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END_COLUMN, OUString::number(nEndColumn));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END_ROW, OUString::number(nEndRow));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END_TABLE, OUString::number(nEndSheet));
+ }
+ SvXMLElementExport aBigRangeElem(rExport, XML_NAMESPACE_TABLE, aName, true, true);
+}
+
+void ScChangeTrackingExportHelper::WriteChangeInfo(const ScChangeAction* pAction)
+{
+ SvXMLElementExport aElemInfo (rExport, XML_NAMESPACE_OFFICE, XML_CHANGE_INFO, true, true);
+
+ {
+ SvXMLElementExport aCreatorElem( rExport, XML_NAMESPACE_DC,
+ XML_CREATOR, true,
+ false );
+ rExport.Characters(pAction->GetUser());
+ }
+
+ {
+ OUStringBuffer sDate;
+ ScXMLConverter::ConvertDateTimeToString(pAction->GetDateTimeUTC(), sDate);
+ SvXMLElementExport aDateElem( rExport, XML_NAMESPACE_DC,
+ XML_DATE, true,
+ false );
+ rExport.Characters(sDate.makeStringAndClear());
+ }
+
+ const OUString& sComment(pAction->GetComment());
+ if (!sComment.isEmpty())
+ {
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ bool bPrevCharWasSpace(true);
+ rExport.GetTextParagraphExport()->exportCharacterData(sComment, bPrevCharWasSpace);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteGenerated(const ScChangeAction* pGeneratedAction)
+{
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt32 nActionNumber(pGeneratedAction->GetActionNumber());
+ OSL_ENSURE(pChangeTrack->IsGenerated(nActionNumber), "a not generated action found");
+#endif
+ SvXMLElementExport aElemPrev(rExport, XML_NAMESPACE_TABLE, XML_CELL_CONTENT_DELETION, true, true);
+ WriteBigRange(pGeneratedAction->GetBigRange(), XML_CELL_ADDRESS);
+ OUString sValue = static_cast<const ScChangeActionContent*>(pGeneratedAction)->GetNewString(rExport.GetDocument());
+ WriteCell(static_cast<const ScChangeActionContent*>(pGeneratedAction)->GetNewCell(), sValue);
+}
+
+void ScChangeTrackingExportHelper::WriteDeleted(const ScChangeAction* pDeletedAction)
+{
+ sal_uInt32 nActionNumber(pDeletedAction->GetActionNumber());
+ if (pDeletedAction->GetType() == SC_CAT_CONTENT)
+ {
+ const ScChangeActionContent* pContentAction = static_cast<const ScChangeActionContent*>(pDeletedAction);
+ if (pContentAction)
+ {
+ if (!pChangeTrack->IsGenerated(nActionNumber))
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(nActionNumber));
+ SvXMLElementExport aElemPrev(rExport, XML_NAMESPACE_TABLE, XML_CELL_CONTENT_DELETION, true, true);
+ if (static_cast<const ScChangeActionContent*>(pDeletedAction)->IsTopContent() && pDeletedAction->IsDeletedIn())
+ {
+ OUString sValue = pContentAction->GetNewString(rExport.GetDocument());
+ WriteCell(pContentAction->GetNewCell(), sValue);
+ }
+ }
+ else
+ WriteGenerated(pContentAction);
+ }
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(nActionNumber));
+ SvXMLElementExport aElemPrev(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_DELETION, true, true);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteDepending(const ScChangeAction* pDependAction)
+{
+ sal_uInt32 nActionNumber(pDependAction->GetActionNumber());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(nActionNumber));
+
+ SvXMLElementExport aDependElem(rExport, XML_NAMESPACE_TABLE,
+ XML_DEPENDENCY, true, true);
+}
+
+void ScChangeTrackingExportHelper::WriteDependings(const ScChangeAction* pAction)
+{
+ if (pAction->HasDependent())
+ {
+ SvXMLElementExport aDependingsElem (rExport, XML_NAMESPACE_TABLE, XML_DEPENDENCIES, true, true);
+ const ScChangeActionLinkEntry* pEntry = pAction->GetFirstDependentEntry();
+ while (pEntry)
+ {
+ WriteDepending(pEntry->GetAction());
+ pEntry = pEntry->GetNext();
+ }
+ }
+ if (pAction->HasDeleted())
+ {
+ SvXMLElementExport aDependingsElem (rExport, XML_NAMESPACE_TABLE, XML_DELETIONS, true, true);
+ const ScChangeActionLinkEntry* pEntry = pAction->GetFirstDeletedEntry();
+ while (pEntry)
+ {
+ WriteDeleted(pEntry->GetAction());
+ pEntry = pEntry->GetNext();
+ }
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteEmptyCell()
+{
+ SvXMLElementExport aElemEmptyCell(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+}
+
+void ScChangeTrackingExportHelper::SetValueAttributes(const double& fValue, const OUString& sValue)
+{
+ bool bSetAttributes(false);
+ if (!sValue.isEmpty())
+ {
+ sal_uInt32 nIndex = 0;
+ double fTempValue = 0.0;
+ if (rExport.GetDocument() && rExport.GetDocument()->GetFormatTable()->IsNumberFormat(sValue, nIndex, fTempValue))
+ {
+ SvNumFormatType nType = rExport.GetDocument()->GetFormatTable()->GetType(nIndex);
+ if (nType & SvNumFormatType::DEFINED)
+ nType &= ~SvNumFormatType::DEFINED;
+ switch(nType)
+ {
+ case SvNumFormatType::DATE:
+ {
+ if ( rExport.GetMM100UnitConverter().setNullDate(rExport.GetModel()) )
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_DATE);
+ OUStringBuffer sBuffer;
+ rExport.GetMM100UnitConverter().convertDateTime(sBuffer, fTempValue);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_DATE_VALUE, sBuffer.makeStringAndClear());
+ bSetAttributes = true;
+ }
+ }
+ break;
+ case SvNumFormatType::TIME:
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_TIME);
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertDuration(sBuffer, fTempValue);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_TIME_VALUE, sBuffer.makeStringAndClear());
+ bSetAttributes = true;
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ if (!bSetAttributes)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT);
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertDouble(sBuffer, fValue);
+ OUString sNumValue(sBuffer.makeStringAndClear());
+ if (!sNumValue.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE, sNumValue);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteValueCell(const ScCellValue& rCell, const OUString& sValue)
+{
+ assert(rCell.meType == CELLTYPE_VALUE);
+
+ SetValueAttributes(rCell.mfValue, sValue);
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+}
+
+void ScChangeTrackingExportHelper::WriteStringCell(const ScCellValue& rCell)
+{
+ assert(rCell.meType == CELLTYPE_STRING);
+
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+ if (!rCell.mpString->isEmpty())
+ {
+ SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ bool bPrevCharWasSpace(true);
+ rExport.GetTextParagraphExport()->exportCharacterData(rCell.mpString->getString(), bPrevCharWasSpace);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteEditCell(const ScCellValue& rCell)
+{
+ assert(rCell.meType == CELLTYPE_EDIT);
+
+ OUString sString;
+ if (rCell.mpEditText)
+ sString = ScEditUtil::GetString(*rCell.mpEditText, rExport.GetDocument());
+
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+ if (rCell.mpEditText && !sString.isEmpty())
+ {
+ if (!pEditTextObj)
+ pEditTextObj = new ScEditEngineTextObj();
+ pEditTextObj->SetText(*rCell.mpEditText);
+ rExport.GetTextParagraphExport()->exportText(pEditTextObj, false, false);
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteFormulaCell(const ScCellValue& rCell, const OUString& sValue)
+{
+ assert(rCell.meType == CELLTYPE_FORMULA);
+
+ ScFormulaCell* pFormulaCell = rCell.mpFormula;
+ OUString sAddress;
+ const ScDocument* pDoc = rExport.GetDocument();
+ ScRangeStringConverter::GetStringFromAddress(sAddress, pFormulaCell->aPos, pDoc, ::formula::FormulaGrammar::CONV_OOO);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CELL_ADDRESS, sAddress);
+ const formula::FormulaGrammar::Grammar eGrammar = pDoc->GetStorageGrammar();
+ sal_uInt16 nNamespacePrefix = (eGrammar == formula::FormulaGrammar::GRAM_ODFF ? XML_NAMESPACE_OF : XML_NAMESPACE_OOOC);
+ OUString sFormula = pFormulaCell->GetFormula(eGrammar);
+ ScMatrixMode nMatrixFlag(pFormulaCell->GetMatrixFlag());
+ if (nMatrixFlag != ScMatrixMode::NONE)
+ {
+ if (nMatrixFlag == ScMatrixMode::Formula)
+ {
+ SCCOL nColumns;
+ SCROW nRows;
+ pFormulaCell->GetMatColsRows(nColumns, nRows);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED, OUString::number(nColumns));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED, OUString::number(nRows));
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MATRIX_COVERED, XML_TRUE);
+ }
+ OUString sMatrixFormula = sFormula.copy(1, sFormula.getLength() - 2);
+ OUString sQValue = rExport.GetNamespaceMap().GetQNameByKey( nNamespacePrefix, sMatrixFormula, false );
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue);
+ }
+ else
+ {
+ OUString sQValue = rExport.GetNamespaceMap().GetQNameByKey( nNamespacePrefix, sFormula, false );
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FORMULA, sQValue);
+ }
+ if (pFormulaCell->IsValue())
+ {
+ SetValueAttributes(pFormulaCell->GetValue(), sValue);
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ OUString sCellValue = pFormulaCell->GetString().getString();
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_CHANGE_TRACK_TABLE_CELL, true, true);
+ if (!sCellValue.isEmpty())
+ {
+ SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ bool bPrevCharWasSpace(true);
+ rExport.GetTextParagraphExport()->exportCharacterData(sCellValue, bPrevCharWasSpace);
+ }
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteCell(const ScCellValue& rCell, const OUString& sValue)
+{
+ if (rCell.isEmpty())
+ {
+ WriteEmptyCell();
+ return;
+ }
+
+ switch (rCell.meType)
+ {
+ case CELLTYPE_VALUE:
+ WriteValueCell(rCell, sValue);
+ break;
+ case CELLTYPE_STRING:
+ WriteStringCell(rCell);
+ break;
+ case CELLTYPE_EDIT:
+ WriteEditCell(rCell);
+ break;
+ case CELLTYPE_FORMULA:
+ WriteFormulaCell(rCell, sValue);
+ break;
+ default:
+ WriteEmptyCell();
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteContentChange(const ScChangeAction* pAction)
+{
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_CELL_CONTENT_CHANGE, true, true);
+ const ScChangeAction* pConstAction = pAction;
+ WriteBigRange(pConstAction->GetBigRange(), XML_CELL_ADDRESS);
+ WriteChangeInfo(pAction);
+ WriteDependings(pAction);
+ {
+ const ScChangeActionContent* pPrevAction = static_cast<const ScChangeActionContent*>(pAction)->GetPrevContent();
+ if (pPrevAction)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(pPrevAction->GetActionNumber()));
+ SvXMLElementExport aElemPrev(rExport, XML_NAMESPACE_TABLE, XML_PREVIOUS, true, true);
+ OUString sValue = static_cast<const ScChangeActionContent*>(pAction)->GetOldString(rExport.GetDocument());
+ WriteCell(static_cast<const ScChangeActionContent*>(pAction)->GetOldCell(), sValue);
+ }
+}
+
+void ScChangeTrackingExportHelper::AddInsertionAttributes(const ScChangeAction* pConstAction)
+{
+ sal_Int64 nPosition(0);
+ sal_Int64 nCount(0);
+ sal_Int64 nStartPosition(0);
+ sal_Int64 nEndPosition(0);
+ sal_Int64 nStartColumn;
+ sal_Int64 nEndColumn;
+ sal_Int64 nStartRow;
+ sal_Int64 nEndRow;
+ sal_Int64 nStartSheet;
+ sal_Int64 nEndSheet;
+ const ScBigRange& rBigRange = pConstAction->GetBigRange();
+ rBigRange.GetVars(nStartColumn, nStartRow, nStartSheet,
+ nEndColumn, nEndRow, nEndSheet);
+ switch (pConstAction->GetType())
+ {
+ case SC_CAT_INSERT_COLS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_COLUMN);
+ nStartPosition = nStartColumn;
+ nEndPosition = nEndColumn;
+ }
+ break;
+ case SC_CAT_INSERT_ROWS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_ROW);
+ nStartPosition = nStartRow;
+ nEndPosition = nEndRow;
+ }
+ break;
+ case SC_CAT_INSERT_TABS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_TABLE);
+ nStartPosition = nStartSheet;
+ nEndPosition = nEndSheet;
+ }
+ break;
+ default :
+ {
+ OSL_FAIL("wrong insertion type");
+ }
+ break;
+ }
+ nPosition = nStartPosition;
+ nCount = nEndPosition - nStartPosition + 1;
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_POSITION, OUString::number(nPosition));
+ OSL_ENSURE(nCount > 0, "wrong insertion count");
+ if (nCount > 1)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_COUNT, OUString::number(nCount));
+ }
+ if (pConstAction->GetType() != SC_CAT_INSERT_TABS)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE, OUString::number(nStartSheet));
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteInsertion(const ScChangeAction* pAction)
+{
+ AddInsertionAttributes(pAction);
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_INSERTION, true, true);
+ WriteChangeInfo(pAction);
+ WriteDependings(pAction);
+}
+
+void ScChangeTrackingExportHelper::AddDeletionAttributes(const ScChangeActionDel* pDelAction)
+{
+ sal_Int32 nPosition(0);
+ const ScBigRange& rBigRange = pDelAction->GetBigRange();
+ sal_Int64 nStartColumn(0);
+ sal_Int64 nEndColumn(0);
+ sal_Int64 nStartRow(0);
+ sal_Int64 nEndRow(0);
+ sal_Int64 nStartSheet(0);
+ sal_Int64 nEndSheet(0);
+ rBigRange.GetVars(nStartColumn, nStartRow, nStartSheet,
+ nEndColumn, nEndRow, nEndSheet);
+ switch (pDelAction->GetType())
+ {
+ case SC_CAT_DELETE_COLS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_COLUMN);
+ nPosition = nStartColumn;
+ }
+ break;
+ case SC_CAT_DELETE_ROWS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_ROW);
+ nPosition = nStartRow;
+ }
+ break;
+ case SC_CAT_DELETE_TABS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, XML_TABLE);
+ nPosition = nStartSheet;
+ }
+ break;
+ default :
+ {
+ OSL_FAIL("wrong deletion type");
+ }
+ break;
+ }
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_POSITION, OUString::number(nPosition));
+ if (pDelAction->GetType() == SC_CAT_DELETE_TABS)
+ return;
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE, OUString::number(nStartSheet));
+ if (!pDelAction->IsMultiDelete() || pDelAction->GetDx() || pDelAction->GetDy())
+ return;
+
+ const ScChangeAction* p = pDelAction->GetNext();
+ sal_Int32 nSlavesCount (1);
+ while (p)
+ {
+ if (p->GetType() != pDelAction->GetType())
+ break;
+ else
+ {
+ const ScChangeActionDel* pDel = static_cast<const ScChangeActionDel*>(p);
+ if ( (pDel->GetDx() > pDelAction->GetDx() || pDel->GetDy() > pDelAction->GetDy()) &&
+ pDel->GetBigRange() == pDelAction->GetBigRange() )
+ {
+ ++nSlavesCount;
+ p = p->GetNext();
+ }
+ else
+ break;
+ }
+ }
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MULTI_DELETION_SPANNED, OUString::number(nSlavesCount));
+}
+
+void ScChangeTrackingExportHelper::WriteCutOffs(const ScChangeActionDel* pAction)
+{
+ const ScChangeActionIns* pCutOffIns = pAction->GetCutOffInsert();
+ const ScChangeActionDelMoveEntry* pLinkMove = pAction->GetFirstMoveEntry();
+ if (!(pCutOffIns || pLinkMove))
+ return;
+
+ SvXMLElementExport aCutOffsElem (rExport, XML_NAMESPACE_TABLE, XML_CUT_OFFS, true, true);
+ if (pCutOffIns)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(pCutOffIns->GetActionNumber()));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_POSITION, OUString::number(pAction->GetCutOffCount()));
+ SvXMLElementExport aInsertCutOffElem (rExport, XML_NAMESPACE_TABLE, XML_INSERTION_CUT_OFF, true, true);
+ }
+ while (pLinkMove)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(pLinkMove->GetAction()->GetActionNumber()));
+ if (pLinkMove->GetCutOffFrom() == pLinkMove->GetCutOffTo())
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_POSITION, OUString::number(pLinkMove->GetCutOffFrom()));
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START_POSITION, OUString::number(pLinkMove->GetCutOffFrom()));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END_POSITION, OUString::number(pLinkMove->GetCutOffTo()));
+ }
+ SvXMLElementExport aMoveCutOffElem (rExport, XML_NAMESPACE_TABLE, XML_MOVEMENT_CUT_OFF, true, true);
+ pLinkMove = pLinkMove->GetNext();
+ }
+}
+
+void ScChangeTrackingExportHelper::WriteDeletion(ScChangeAction* pAction)
+{
+ ScChangeActionDel* pDelAction = static_cast<ScChangeActionDel*> (pAction);
+ AddDeletionAttributes(pDelAction);
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_DELETION, true, true);
+ WriteChangeInfo(pDelAction);
+ WriteDependings(pDelAction);
+ WriteCutOffs(pDelAction);
+}
+
+void ScChangeTrackingExportHelper::WriteMovement(const ScChangeAction* pAction)
+{
+ const ScChangeActionMove* pMoveAction = static_cast<const ScChangeActionMove*> (pAction);
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_MOVEMENT, true, true);
+ WriteBigRange(pMoveAction->GetFromRange(), XML_SOURCE_RANGE_ADDRESS);
+ WriteBigRange(pMoveAction->GetBigRange(), XML_TARGET_RANGE_ADDRESS);
+ WriteChangeInfo(pAction);
+ WriteDependings(pAction);
+}
+
+void ScChangeTrackingExportHelper::WriteRejection(const ScChangeAction* pAction)
+{
+ SvXMLElementExport aElemChange(rExport, XML_NAMESPACE_TABLE, XML_REJECTION, true, true);
+ WriteChangeInfo(pAction);
+ WriteDependings(pAction);
+}
+
+void ScChangeTrackingExportHelper::CollectCellAutoStyles(const ScCellValue& rCell)
+{
+ if (rCell.meType != CELLTYPE_EDIT)
+ return;
+
+ if (!pEditTextObj)
+ pEditTextObj = new ScEditEngineTextObj();
+
+ pEditTextObj->SetText(*rCell.mpEditText);
+ rExport.GetTextParagraphExport()->collectTextAutoStyles(pEditTextObj, false, false);
+}
+
+void ScChangeTrackingExportHelper::CollectActionAutoStyles(const ScChangeAction* pAction)
+{
+ if (pAction->GetType() != SC_CAT_CONTENT)
+ return;
+
+ if (pChangeTrack->IsGenerated(pAction->GetActionNumber()))
+ CollectCellAutoStyles(static_cast<const ScChangeActionContent*>(pAction)->GetNewCell());
+ else
+ {
+ CollectCellAutoStyles(static_cast<const ScChangeActionContent*>(pAction)->GetOldCell());
+ if (static_cast<const ScChangeActionContent*>(pAction)->IsTopContent() && pAction->IsDeletedIn())
+ CollectCellAutoStyles(static_cast<const ScChangeActionContent*>(pAction)->GetNewCell());
+ }
+}
+
+void ScChangeTrackingExportHelper::WorkWithChangeAction(ScChangeAction* pAction)
+{
+ if (pAction->GetType() == SC_CAT_NONE)
+ {
+ SAL_WARN("sc.filter", "WorkWithChangeAction: type is not writable");
+ return;
+ }
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ID, GetChangeID(pAction->GetActionNumber()));
+ GetAcceptanceState(pAction);
+ if (pAction->IsRejecting())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_REJECTING_CHANGE_ID, GetChangeID(pAction->GetRejectAction()));
+ if (pAction->GetType() == SC_CAT_CONTENT)
+ WriteContentChange(pAction);
+ else if (pAction->IsInsertType())
+ WriteInsertion(pAction);
+ else if (pAction->IsDeleteType())
+ WriteDeletion(pAction);
+ else if (pAction->GetType() == SC_CAT_MOVE)
+ WriteMovement(pAction);
+ else if (pAction->GetType() == SC_CAT_REJECT)
+ WriteRejection(pAction);
+ else
+ {
+ assert(false); // tdf#73335 this would create duplicate attributes
+ }
+ rExport.CheckAttrList();
+}
+
+void ScChangeTrackingExportHelper::CollectAutoStyles()
+{
+ if (!pChangeTrack)
+ return;
+
+ sal_uInt32 nCount (pChangeTrack->GetActionMax());
+ if (!nCount)
+ return;
+
+ ScChangeAction* pAction = pChangeTrack->GetFirst();
+ CollectActionAutoStyles(pAction);
+ ScChangeAction* pLastAction = pChangeTrack->GetLast();
+ while (pAction != pLastAction)
+ {
+ pAction = pAction->GetNext();
+ CollectActionAutoStyles(pAction);
+ }
+ pAction = pChangeTrack->GetFirstGenerated();
+ while (pAction)
+ {
+ CollectActionAutoStyles(pAction);
+ pAction = pAction->GetNext();
+ }
+}
+
+void ScChangeTrackingExportHelper::CollectAndWriteChanges()
+{
+ if (!pChangeTrack)
+ return;
+
+ SvXMLElementExport aCangeListElem(rExport, XML_NAMESPACE_TABLE, XML_TRACKED_CHANGES, true, true);
+ {
+ ScChangeAction* pAction = pChangeTrack->GetFirst();
+ if (pAction)
+ {
+ WorkWithChangeAction(pAction);
+ ScChangeAction* pLastAction = pChangeTrack->GetLast();
+ while (pAction != pLastAction)
+ {
+ pAction = pAction->GetNext();
+ WorkWithChangeAction(pAction);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLChangeTrackingExportHelper.hxx b/sc/source/filter/xml/XMLChangeTrackingExportHelper.hxx
new file mode 100644
index 000000000..be721de62
--- /dev/null
+++ b/sc/source/filter/xml/XMLChangeTrackingExportHelper.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/xmltoken.hxx>
+#include <rtl/ref.hxx>
+
+namespace com::sun::star::text
+{
+class XText;
+}
+
+class ScChangeAction;
+class ScChangeTrack;
+class ScXMLExport;
+struct ScCellValue;
+class ScChangeActionDel;
+class ScBigRange;
+class ScEditEngineTextObj;
+
+class ScChangeTrackingExportHelper
+{
+ ScXMLExport& rExport;
+
+ ScChangeTrack* pChangeTrack;
+ rtl::Reference<ScEditEngineTextObj> pEditTextObj;
+
+ static OUString GetChangeID(const sal_uInt32 nActionNumber);
+ void GetAcceptanceState(const ScChangeAction* pAction);
+
+ void WriteBigRange(const ScBigRange& rBigRange, xmloff::token::XMLTokenEnum aName);
+ void WriteChangeInfo(const ScChangeAction* pAction);
+ void WriteGenerated(const ScChangeAction* pDependAction);
+ void WriteDeleted(const ScChangeAction* pDependAction);
+ void WriteDepending(const ScChangeAction* pDependAction);
+ void WriteDependings(const ScChangeAction* pAction);
+
+ void WriteEmptyCell();
+ void SetValueAttributes(const double& fValue, const OUString& sValue);
+ void WriteValueCell(const ScCellValue& rCell, const OUString& sValue);
+ void WriteStringCell(const ScCellValue& rCell);
+ void WriteEditCell(const ScCellValue& rCell);
+ void WriteFormulaCell(const ScCellValue& rCell, const OUString& sValue);
+ void WriteCell(const ScCellValue& rCell, const OUString& sValue);
+
+ void WriteContentChange(const ScChangeAction* pAction);
+ void AddInsertionAttributes(const ScChangeAction* pAction);
+ void WriteInsertion(const ScChangeAction* pAction);
+ void AddDeletionAttributes(const ScChangeActionDel* pAction);
+ void WriteCutOffs(const ScChangeActionDel* pAction);
+ void WriteDeletion(ScChangeAction* pAction);
+ void WriteMovement(const ScChangeAction* pAction);
+ void WriteRejection(const ScChangeAction* pAction);
+
+ void CollectCellAutoStyles(const ScCellValue& rCell);
+ void CollectActionAutoStyles(const ScChangeAction* pAction);
+ void WorkWithChangeAction(ScChangeAction* pAction);
+
+public:
+ explicit ScChangeTrackingExportHelper(ScXMLExport& rExport);
+ ~ScChangeTrackingExportHelper();
+
+ void CollectAutoStyles();
+ void CollectAndWriteChanges();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLChangeTrackingImportHelper.cxx b/sc/source/filter/xml/XMLChangeTrackingImportHelper.cxx
new file mode 100644
index 000000000..9ae06a720
--- /dev/null
+++ b/sc/source/filter/xml/XMLChangeTrackingImportHelper.cxx
@@ -0,0 +1,808 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLChangeTrackingImportHelper.hxx"
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <tools/datetime.hxx>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <sax/tools/converter.hxx>
+
+constexpr OStringLiteral SC_CHANGE_ID_PREFIX = "ct";
+
+ScMyCellInfo::ScMyCellInfo(
+ const ScCellValue& rCell, const OUString& rFormulaAddress, const OUString& rFormula,
+ const formula::FormulaGrammar::Grammar eTempGrammar, const OUString& rInputString,
+ const double& rValue, const sal_uInt16 nTempType, const ScMatrixMode nTempMatrixFlag, const sal_Int32 nTempMatrixCols,
+ const sal_Int32 nTempMatrixRows ) :
+ maCell(rCell),
+ sFormulaAddress(rFormulaAddress),
+ sFormula(rFormula),
+ sInputString(rInputString),
+ fValue(rValue),
+ nMatrixCols(nTempMatrixCols),
+ nMatrixRows(nTempMatrixRows),
+ eGrammar( eTempGrammar),
+ nType(nTempType),
+ nMatrixFlag(nTempMatrixFlag)
+{
+}
+
+ScMyCellInfo::~ScMyCellInfo() {}
+
+const ScCellValue& ScMyCellInfo::CreateCell(ScDocument& rDoc)
+{
+ if (!maCell.isEmpty())
+ return maCell;
+
+ if (!sFormula.isEmpty() && !sFormulaAddress.isEmpty())
+ {
+ ScAddress aPos;
+ sal_Int32 nOffset(0);
+ ScRangeStringConverter::GetAddressFromString(aPos, sFormulaAddress, rDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset);
+ maCell.meType = CELLTYPE_FORMULA;
+ maCell.mpFormula = new ScFormulaCell(rDoc, aPos, sFormula, eGrammar, nMatrixFlag);
+ maCell.mpFormula->SetMatColsRows(static_cast<SCCOL>(nMatrixCols), static_cast<SCROW>(nMatrixRows));
+ }
+
+ if ((nType == css::util::NumberFormat::DATE || nType == css::util::NumberFormat::TIME) && sInputString.isEmpty())
+ {
+ sal_uInt32 nFormat(0);
+ if (nType == css::util::NumberFormat::DATE)
+ nFormat = rDoc.GetFormatTable()->GetStandardFormat( SvNumFormatType::DATE, ScGlobal::eLnge );
+ else if (nType == css::util::NumberFormat::TIME)
+ nFormat = rDoc.GetFormatTable()->GetStandardFormat( SvNumFormatType::TIME, ScGlobal::eLnge );
+ rDoc.GetFormatTable()->GetInputLineString(fValue, nFormat, sInputString);
+ }
+
+ return maCell;
+}
+
+ScMyBaseAction::ScMyBaseAction(const ScChangeActionType nTempActionType)
+ : nActionNumber(0),
+ nRejectingNumber(0),
+ nPreviousAction(0),
+ nActionType(nTempActionType),
+ nActionState(SC_CAS_VIRGIN)
+{
+}
+
+ScMyBaseAction::~ScMyBaseAction()
+{
+}
+
+ScMyInsAction::ScMyInsAction(const ScChangeActionType nActionTypeP)
+ : ScMyBaseAction(nActionTypeP)
+{
+}
+
+ScMyInsAction::~ScMyInsAction()
+{
+}
+
+ScMyDelAction::ScMyDelAction(const ScChangeActionType nActionTypeP)
+ : ScMyBaseAction(nActionTypeP),
+ nD(0)
+{
+}
+
+ScMyDelAction::~ScMyDelAction()
+{
+}
+
+ScMyMoveAction::ScMyMoveAction()
+ : ScMyBaseAction(SC_CAT_MOVE)
+{
+}
+
+ScMyMoveAction::~ScMyMoveAction()
+{
+}
+
+ScMyContentAction::ScMyContentAction()
+ : ScMyBaseAction(SC_CAT_CONTENT)
+{
+}
+
+ScMyContentAction::~ScMyContentAction()
+{
+}
+
+ScMyRejAction::ScMyRejAction()
+ : ScMyBaseAction(SC_CAT_REJECT)
+{
+}
+
+ScMyRejAction::~ScMyRejAction()
+{
+}
+
+ScXMLChangeTrackingImportHelper::ScXMLChangeTrackingImportHelper() :
+ pTrack(nullptr),
+ nMultiSpanned(0),
+ nMultiSpannedSlaveCount(0)
+{
+}
+
+ScXMLChangeTrackingImportHelper::~ScXMLChangeTrackingImportHelper()
+{
+}
+
+void ScXMLChangeTrackingImportHelper::StartChangeAction(const ScChangeActionType nActionType)
+{
+ OSL_ENSURE(!pCurrentAction, "a not inserted action");
+ switch (nActionType)
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_TABS:
+ {
+ pCurrentAction = std::make_unique<ScMyInsAction>(nActionType);
+ }
+ break;
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ {
+ pCurrentAction = std::make_unique<ScMyDelAction>(nActionType);
+ }
+ break;
+ case SC_CAT_MOVE:
+ {
+ pCurrentAction = std::make_unique<ScMyMoveAction>();
+ }
+ break;
+ case SC_CAT_CONTENT:
+ {
+ pCurrentAction = std::make_unique<ScMyContentAction>();
+ }
+ break;
+ case SC_CAT_REJECT:
+ {
+ pCurrentAction = std::make_unique<ScMyRejAction>();
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+sal_uInt32 ScXMLChangeTrackingImportHelper::GetIDFromString(std::string_view sID)
+{
+ sal_uInt32 nResult(0);
+ if (!sID.empty())
+ {
+ if (sID.substr(0, SC_CHANGE_ID_PREFIX.getLength()) == SC_CHANGE_ID_PREFIX)
+ {
+ sal_Int32 nValue;
+ ::sax::Converter::convertNumber(nValue, sID.substr(SC_CHANGE_ID_PREFIX.getLength()));
+ OSL_ENSURE(nValue > 0, "wrong change action ID");
+ nResult = nValue;
+ }
+ else
+ {
+ OSL_FAIL("wrong change action ID");
+ }
+ }
+ return nResult;
+}
+
+void ScXMLChangeTrackingImportHelper::SetActionInfo(const ScMyActionInfo& aInfo)
+{
+ pCurrentAction->aInfo = aInfo;
+ aUsers.insert(aInfo.sUser);
+}
+
+void ScXMLChangeTrackingImportHelper::SetPreviousChange(const sal_uInt32 nPreviousAction,
+ ScMyCellInfo* pCellInfo)
+{
+ OSL_ENSURE(pCurrentAction->nActionType == SC_CAT_CONTENT, "wrong action type");
+ ScMyContentAction* pAction = static_cast<ScMyContentAction*>(pCurrentAction.get());
+ pAction->nPreviousAction = nPreviousAction;
+ pAction->pCellInfo.reset( pCellInfo );
+}
+
+void ScXMLChangeTrackingImportHelper::SetPosition(const sal_Int32 nPosition, const sal_Int32 nCount, const sal_Int32 nTable)
+{
+ OSL_ENSURE(((pCurrentAction->nActionType != SC_CAT_MOVE) &&
+ (pCurrentAction->nActionType != SC_CAT_CONTENT) &&
+ (pCurrentAction->nActionType != SC_CAT_REJECT)), "wrong action type");
+ OSL_ENSURE(nCount > 0, "wrong count");
+ switch(pCurrentAction->nActionType)
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_DELETE_COLS:
+ {
+ pCurrentAction->aBigRange.Set(nPosition, ScBigRange::nRangeMin, nTable,
+ nPosition + nCount - 1, ScBigRange::nRangeMax, nTable);
+ }
+ break;
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_DELETE_ROWS:
+ {
+ pCurrentAction->aBigRange.Set(ScBigRange::nRangeMin, nPosition, nTable,
+ ScBigRange::nRangeMax, nPosition + nCount - 1, nTable);
+ }
+ break;
+ case SC_CAT_INSERT_TABS:
+ case SC_CAT_DELETE_TABS:
+ {
+ pCurrentAction->aBigRange.Set(ScBigRange::nRangeMin, ScBigRange::nRangeMin, nPosition,
+ ScBigRange::nRangeMax, ScBigRange::nRangeMax, nPosition + nCount - 1);
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::AddDeleted(const sal_uInt32 nID)
+{
+ pCurrentAction->aDeletedList.emplace_back( nID, nullptr );
+}
+
+void ScXMLChangeTrackingImportHelper::AddDeleted(const sal_uInt32 nID, std::unique_ptr<ScMyCellInfo> pCellInfo)
+{
+ pCurrentAction->aDeletedList.emplace_back( nID, std::move(pCellInfo) );
+}
+
+void ScXMLChangeTrackingImportHelper::SetMultiSpanned(const sal_Int16 nTempMultiSpanned)
+{
+ if (nTempMultiSpanned)
+ {
+ OSL_ENSURE(((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS)), "wrong action type");
+ nMultiSpanned = nTempMultiSpanned;
+ nMultiSpannedSlaveCount = 0;
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetInsertionCutOff(const sal_uInt32 nID, const sal_Int32 nPosition)
+{
+ if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ {
+ static_cast<ScMyDelAction*>(pCurrentAction.get())->pInsCutOff.reset( new ScMyInsertionCutOff(nID, nPosition) );
+ }
+ else
+ {
+ OSL_FAIL("wrong action type");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::AddMoveCutOff(const sal_uInt32 nID, const sal_Int32 nStartPosition, const sal_Int32 nEndPosition)
+{
+ if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ {
+ static_cast<ScMyDelAction*>(pCurrentAction.get())->aMoveCutOffs.push_back(ScMyMoveCutOff(nID, nStartPosition, nEndPosition));
+ }
+ else
+ {
+ OSL_FAIL("wrong action type");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetMoveRanges(const ScBigRange& aSourceRange, const ScBigRange& aTargetRange)
+{
+ if (pCurrentAction->nActionType == SC_CAT_MOVE)
+ {
+ static_cast<ScMyMoveAction*>(pCurrentAction.get())->pMoveRanges.reset( new ScMyMoveRanges(aSourceRange, aTargetRange) );
+ }
+ else
+ {
+ OSL_FAIL("wrong action type");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::GetMultiSpannedRange()
+{
+ if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ {
+ if (nMultiSpannedSlaveCount)
+ {
+ static_cast<ScMyDelAction*>(pCurrentAction.get())->nD = nMultiSpannedSlaveCount;
+ }
+ ++nMultiSpannedSlaveCount;
+ if (nMultiSpannedSlaveCount >= nMultiSpanned)
+ {
+ nMultiSpanned = 0;
+ nMultiSpannedSlaveCount = 0;
+ }
+ }
+ else
+ {
+ OSL_FAIL("wrong action type");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::AddGenerated(std::unique_ptr<ScMyCellInfo> pCellInfo, const ScBigRange& aBigRange)
+{
+ ScMyGenerated aGenerated { aBigRange, 0, std::move(pCellInfo) };
+ if (pCurrentAction->nActionType == SC_CAT_MOVE)
+ {
+ static_cast<ScMyMoveAction*>(pCurrentAction.get())->aGeneratedList.push_back(std::move(aGenerated));
+ }
+ else if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ {
+ static_cast<ScMyDelAction*>(pCurrentAction.get())->aGeneratedList.push_back(std::move(aGenerated));
+ }
+ else
+ {
+ OSL_FAIL("try to insert a generated action to a wrong action");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::EndChangeAction()
+{
+ if (!pCurrentAction)
+ {
+ OSL_FAIL("no current action");
+ return;
+ }
+
+ if ((pCurrentAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pCurrentAction->nActionType == SC_CAT_DELETE_ROWS))
+ GetMultiSpannedRange();
+
+ if (pCurrentAction->nActionNumber > 0)
+ aActions.push_back(std::move(pCurrentAction));
+ else
+ {
+ OSL_FAIL("no current action");
+ }
+
+ pCurrentAction = nullptr;
+}
+
+void ScXMLChangeTrackingImportHelper::ConvertInfo(const ScMyActionInfo& aInfo, OUString& rUser, DateTime& aDateTime)
+{
+ aDateTime = DateTime( aInfo.aDateTime);
+
+ // old files didn't store nanoseconds, enable again
+ if ( aInfo.aDateTime.NanoSeconds )
+ pTrack->SetTimeNanoSeconds( true );
+
+ const std::set<OUString>& rUsers = pTrack->GetUserCollection();
+ std::set<OUString>::const_iterator it = rUsers.find(aInfo.sUser);
+ if (it != rUsers.end())
+ {
+ // It's probably pointless to do this.
+ rUser = *it;
+ }
+ else
+ rUser = aInfo.sUser; // shouldn't happen
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateInsertAction(const ScMyInsAction* pAction)
+{
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionIns>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->aBigRange, aUser, aDateTime, sComment, pAction->nActionType);
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateDeleteAction(const ScMyDelAction* pAction)
+{
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionDel>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->aBigRange, aUser, aDateTime, sComment, pAction->nActionType, pAction->nD, pTrack);
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateMoveAction(const ScMyMoveAction* pAction)
+{
+ OSL_ENSURE(pAction->pMoveRanges, "no move ranges");
+ if (pAction->pMoveRanges)
+ {
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionMove>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->pMoveRanges->aTargetRange, aUser, aDateTime, sComment, pAction->pMoveRanges->aSourceRange , pTrack);
+ }
+ return nullptr;
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateRejectionAction(const ScMyRejAction* pAction)
+{
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionReject>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->aBigRange, aUser, aDateTime, sComment);
+}
+
+std::unique_ptr<ScChangeAction> ScXMLChangeTrackingImportHelper::CreateContentAction(const ScMyContentAction* pAction, ScDocument& rDoc)
+{
+ ScCellValue aCell;
+ OUString sInputString;
+ if (pAction->pCellInfo)
+ {
+ aCell = pAction->pCellInfo->CreateCell(rDoc);
+ sInputString = pAction->pCellInfo->sInputString;
+ }
+
+ DateTime aDateTime( Date(0), tools::Time(0) );
+ OUString aUser;
+ ConvertInfo(pAction->aInfo, aUser, aDateTime);
+
+ OUString sComment (pAction->aInfo.sComment);
+
+ return std::make_unique<ScChangeActionContent>(pAction->nActionNumber, pAction->nActionState, pAction->nRejectingNumber,
+ pAction->aBigRange, aUser, aDateTime, sComment, aCell, &rDoc, sInputString);
+}
+
+void ScXMLChangeTrackingImportHelper::CreateGeneratedActions(std::vector<ScMyGenerated>& rList, ScDocument& rDoc)
+{
+ for (ScMyGenerated & rGenerated : rList)
+ {
+ if (rGenerated.nID == 0)
+ {
+ ScCellValue aCell;
+ if (rGenerated.pCellInfo)
+ aCell = rGenerated.pCellInfo->CreateCell(rDoc);
+
+ if (!aCell.isEmpty())
+ {
+ rGenerated.nID = pTrack->AddLoadedGenerated(aCell, rGenerated.aBigRange, rGenerated.pCellInfo->sInputString);
+ OSL_ENSURE(rGenerated.nID, "could not insert generated action");
+ }
+ }
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetDeletionDependencies(ScMyDelAction* pAction, ScChangeActionDel* pDelAct)
+{
+ if (!pAction->aGeneratedList.empty())
+ {
+ OSL_ENSURE(((pAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pAction->nActionType == SC_CAT_DELETE_ROWS) ||
+ (pAction->nActionType == SC_CAT_DELETE_TABS)), "wrong action type");
+ if (pDelAct)
+ {
+ for (const ScMyGenerated & rGenerated : pAction->aGeneratedList)
+ {
+ OSL_ENSURE(rGenerated.nID, "a not inserted generated action");
+ pDelAct->SetDeletedInThis(rGenerated.nID, pTrack);
+ }
+ pAction->aGeneratedList.clear();
+ }
+ }
+ if (pAction->pInsCutOff)
+ {
+ OSL_ENSURE(((pAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pAction->nActionType == SC_CAT_DELETE_ROWS) ||
+ (pAction->nActionType == SC_CAT_DELETE_TABS)), "wrong action type");
+ ScChangeAction* pChangeAction = pTrack->GetAction(pAction->pInsCutOff->nID);
+ if (pChangeAction && pChangeAction->IsInsertType())
+ {
+ ScChangeActionIns* pInsAction = static_cast<ScChangeActionIns*>(pChangeAction);
+ if (pDelAct)
+ pDelAct->SetCutOffInsert(pInsAction, static_cast<sal_Int16>(pAction->pInsCutOff->nPosition));
+ }
+ else
+ {
+ OSL_FAIL("no cut off insert action");
+ }
+ }
+ if (pAction->aMoveCutOffs.empty())
+ return;
+
+ OSL_ENSURE(((pAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pAction->nActionType == SC_CAT_DELETE_ROWS) ||
+ (pAction->nActionType == SC_CAT_DELETE_TABS)), "wrong action type");
+ for (auto it = pAction->aMoveCutOffs.crbegin(); it != pAction->aMoveCutOffs.crend(); ++it)
+ {
+ const ScMyMoveCutOff & rCutOff = *it;
+ ScChangeAction* pChangeAction = pTrack->GetAction(rCutOff.nID);
+ if (pChangeAction && (pChangeAction->GetType() == SC_CAT_MOVE))
+ {
+ ScChangeActionMove* pMoveAction = static_cast<ScChangeActionMove*>(pChangeAction);
+ if (pDelAct)
+ pDelAct->AddCutOffMove(pMoveAction, static_cast<sal_Int16>(rCutOff.nStartPosition),
+ static_cast<sal_Int16>(rCutOff.nEndPosition));
+ }
+ else
+ {
+ OSL_FAIL("no cut off move action");
+ }
+ }
+ pAction->aMoveCutOffs.clear();
+}
+
+void ScXMLChangeTrackingImportHelper::SetMovementDependencies(ScMyMoveAction* pAction, ScChangeActionMove* pMoveAct)
+{
+ if (pAction->aGeneratedList.empty())
+ return;
+
+ if (pAction->nActionType == SC_CAT_MOVE)
+ {
+ if (pMoveAct)
+ {
+ for (const ScMyGenerated & rGenerated : pAction->aGeneratedList)
+ {
+ OSL_ENSURE(rGenerated.nID, "a not inserted generated action");
+ pMoveAct->SetDeletedInThis(rGenerated.nID, pTrack);
+ }
+ pAction->aGeneratedList.clear();
+ }
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetContentDependencies(const ScMyContentAction* pAction, ScChangeActionContent* pActContent, const ScDocument& rDoc)
+{
+ if (!pActContent || !pAction->nPreviousAction)
+ return;
+
+ OSL_ENSURE(pAction->nActionType == SC_CAT_CONTENT, "wrong action type");
+ ScChangeAction* pPrevAct = pTrack->GetAction(pAction->nPreviousAction);
+ if (!pPrevAct || pPrevAct->GetType() != SC_CAT_CONTENT)
+ return;
+
+ ScChangeActionContent* pPrevActContent = static_cast<ScChangeActionContent*>(pPrevAct);
+
+ pActContent->SetPrevContent(pPrevActContent);
+ pPrevActContent->SetNextContent(pActContent);
+ const ScCellValue& rOldCell = pActContent->GetOldCell();
+ if (rOldCell.isEmpty())
+ return;
+
+ pPrevActContent->SetNewCell(rOldCell, &rDoc, OUString());
+}
+
+void ScXMLChangeTrackingImportHelper::SetDependencies(ScMyBaseAction* pAction, ScDocument& rDoc)
+{
+ ScChangeAction* pAct = pTrack->GetAction(pAction->nActionNumber);
+ if (pAct)
+ {
+ if (!pAction->aDependencies.empty())
+ {
+ for (auto it = pAction->aDependencies.crbegin(); it != pAction->aDependencies.crend(); ++it)
+ pAct->AddDependent(*it, pTrack);
+ pAction->aDependencies.clear();
+ }
+ if (!pAction->aDeletedList.empty())
+ {
+ for (auto it = pAction->aDeletedList.crbegin(); it != pAction->aDeletedList.crend(); ++it)
+ {
+ const ScMyDeleted & rDeleted = *it;
+ pAct->SetDeletedInThis(rDeleted.nID, pTrack);
+ ScChangeAction* pDeletedAct = pTrack->GetAction(rDeleted.nID);
+ if ((pDeletedAct->GetType() == SC_CAT_CONTENT) && rDeleted.pCellInfo)
+ {
+ ScChangeActionContent* pContentAct = static_cast<ScChangeActionContent*>(pDeletedAct);
+ if (rDeleted.pCellInfo)
+ {
+ const ScCellValue& rCell = rDeleted.pCellInfo->CreateCell(rDoc);
+ if (!rCell.equalsWithoutFormat(pContentAct->GetNewCell()))
+ {
+ // #i40704# Don't overwrite SetNewCell result by calling SetNewValue,
+ // instead pass the input string to SetNewCell.
+ pContentAct->SetNewCell(rCell, &rDoc, rDeleted.pCellInfo->sInputString);
+ }
+ }
+ }
+ }
+ pAction->aDeletedList.clear();
+ }
+ if ((pAction->nActionType == SC_CAT_DELETE_COLS) ||
+ (pAction->nActionType == SC_CAT_DELETE_ROWS))
+ SetDeletionDependencies(static_cast<ScMyDelAction*>(pAction), static_cast<ScChangeActionDel*>(pAct));
+ else if (pAction->nActionType == SC_CAT_MOVE)
+ SetMovementDependencies(static_cast<ScMyMoveAction*>(pAction), static_cast<ScChangeActionMove*>(pAct));
+ else if (pAction->nActionType == SC_CAT_CONTENT)
+ SetContentDependencies(static_cast<ScMyContentAction*>(pAction), static_cast<ScChangeActionContent*>(pAct), rDoc);
+ }
+ else
+ {
+ OSL_FAIL("could not find the action");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::SetNewCell(const ScMyContentAction* pAction, ScDocument& rDoc)
+{
+ ScChangeAction* pChangeAction = pTrack->GetAction(pAction->nActionNumber);
+ if (!pChangeAction)
+ return;
+
+ assert(dynamic_cast<ScChangeActionContent*>(pChangeAction));
+ ScChangeActionContent* pChangeActionContent = static_cast<ScChangeActionContent*>(pChangeAction);
+ if (!pChangeActionContent->IsTopContent() || pChangeActionContent->IsDeletedIn())
+ return;
+
+ sal_Int64 nCol, nRow, nTab, nCol2, nRow2, nTab2;
+ pAction->aBigRange.GetVars(nCol, nRow, nTab, nCol2, nRow2, nTab2);
+ if ((nCol >= 0) && (nCol <= rDoc.MaxCol()) &&
+ (nRow >= 0) && (nRow <= rDoc.MaxRow()) &&
+ (nTab >= 0) && (nTab <= MAXTAB))
+ {
+ ScAddress aAddress (static_cast<SCCOL>(nCol),
+ static_cast<SCROW>(nRow),
+ static_cast<SCTAB>(nTab));
+ ScCellValue aCell;
+ aCell.assign(rDoc, aAddress);
+ if (!aCell.isEmpty())
+ {
+ ScCellValue aNewCell;
+ if (aCell.meType != CELLTYPE_FORMULA)
+ {
+ aNewCell = aCell;
+ pChangeActionContent->SetNewCell(aNewCell, &rDoc, OUString());
+ pChangeActionContent->SetNewValue(aCell, &rDoc);
+ }
+ else
+ {
+ ScMatrixMode nMatrixFlag = aCell.mpFormula->GetMatrixFlag();
+ // With GRAM_ODFF reference detection is faster on compilation.
+ /* FIXME: new cell should be created with a clone
+ * of the token array instead. Any reason why this
+ * wasn't done? */
+ OUString sFormula = aCell.mpFormula->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+
+ // #i87826# [Collaboration] Rejected move destroys formulas
+ // FIXME: adjust ScFormulaCell::GetFormula(), so that the right formula string
+ // is returned and no further string handling is necessary
+ OUString sFormula2;
+ if ( nMatrixFlag != ScMatrixMode::NONE )
+ {
+ sFormula2 = sFormula.copy( 2, sFormula.getLength() - 3 );
+ }
+ else
+ {
+ sFormula2 = sFormula.copy( 1 );
+ }
+
+ aNewCell.meType = CELLTYPE_FORMULA;
+ aNewCell.mpFormula = new ScFormulaCell(rDoc, aAddress, sFormula2,formula::FormulaGrammar::GRAM_ODFF, nMatrixFlag);
+ if (nMatrixFlag == ScMatrixMode::Formula)
+ {
+ SCCOL nCols;
+ SCROW nRows;
+ aCell.mpFormula->GetMatColsRows(nCols, nRows);
+ aNewCell.mpFormula->SetMatColsRows(nCols, nRows);
+ }
+ aNewCell.mpFormula->SetInChangeTrack(true);
+ pChangeActionContent->SetNewCell(aNewCell, &rDoc, OUString());
+ // #i40704# don't overwrite the formula string via SetNewValue()
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("wrong cell position");
+ }
+}
+
+void ScXMLChangeTrackingImportHelper::CreateChangeTrack(ScDocument* pDoc)
+{
+ if (!pDoc)
+ return;
+
+ pTrack = new ScChangeTrack(*pDoc, std::set(aUsers));
+ // old files didn't store nanoseconds, disable until encountered
+ pTrack->SetTimeNanoSeconds( false );
+
+ for (const auto & rAction : aActions)
+ {
+ std::unique_ptr<ScChangeAction> pAction;
+
+ switch (rAction->nActionType)
+ {
+ case SC_CAT_INSERT_COLS:
+ case SC_CAT_INSERT_ROWS:
+ case SC_CAT_INSERT_TABS:
+ {
+ pAction = CreateInsertAction(static_cast<ScMyInsAction*>(rAction.get()));
+ }
+ break;
+ case SC_CAT_DELETE_COLS:
+ case SC_CAT_DELETE_ROWS:
+ case SC_CAT_DELETE_TABS:
+ {
+ ScMyDelAction* pDelAct = static_cast<ScMyDelAction*>(rAction.get());
+ pAction = CreateDeleteAction(pDelAct);
+ CreateGeneratedActions(pDelAct->aGeneratedList, *pDoc);
+ }
+ break;
+ case SC_CAT_MOVE:
+ {
+ ScMyMoveAction* pMovAct = static_cast<ScMyMoveAction*>(rAction.get());
+ pAction = CreateMoveAction(pMovAct);
+ CreateGeneratedActions(pMovAct->aGeneratedList, *pDoc);
+ }
+ break;
+ case SC_CAT_CONTENT:
+ {
+ pAction = CreateContentAction(static_cast<ScMyContentAction*>(rAction.get()), *pDoc);
+ }
+ break;
+ case SC_CAT_REJECT:
+ {
+ pAction = CreateRejectionAction(static_cast<ScMyRejAction*>(rAction.get()));
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+
+ if (pAction)
+ pTrack->AppendLoaded(std::move(pAction));
+ else
+ {
+ OSL_FAIL("no action");
+ }
+ }
+ if (pTrack->GetLast())
+ pTrack->SetActionMax(pTrack->GetLast()->GetActionNumber());
+
+ auto aItr = aActions.begin();
+ while (aItr != aActions.end())
+ {
+ SetDependencies(aItr->get(), *pDoc);
+
+ if ((*aItr)->nActionType == SC_CAT_CONTENT)
+ ++aItr;
+ else
+ aItr = aActions.erase(aItr);
+ }
+
+ for (const auto& rxAction : aActions)
+ {
+ OSL_ENSURE(rxAction->nActionType == SC_CAT_CONTENT, "wrong action type");
+ SetNewCell(static_cast<ScMyContentAction*>(rxAction.get()), *pDoc);
+ }
+ aActions.clear();
+ if (aProtect.hasElements())
+ pTrack->SetProtection(aProtect);
+ else if (pDoc->GetChangeTrack() && pDoc->GetChangeTrack()->IsProtected())
+ pTrack->SetProtection(pDoc->GetChangeTrack()->GetProtection());
+
+ if ( pTrack->GetLast() )
+ pTrack->SetLastSavedActionNumber(pTrack->GetLast()->GetActionNumber());
+
+ pDoc->SetChangeTrack(std::unique_ptr<ScChangeTrack>(pTrack));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLChangeTrackingImportHelper.hxx b/sc/source/filter/xml/XMLChangeTrackingImportHelper.hxx
new file mode 100644
index 000000000..d7c9f3664
--- /dev/null
+++ b/sc/source/filter/xml/XMLChangeTrackingImportHelper.hxx
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <chgtrack.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+
+class ScDocument;
+class DateTime;
+enum class ScMatrixMode : sal_uInt8;
+
+struct ScMyActionInfo
+{
+ OUString sUser;
+ OUString sComment;
+ css::util::DateTime aDateTime;
+};
+
+struct ScMyCellInfo
+{
+ ScCellValue maCell;
+ OUString sFormulaAddress;
+ OUString sFormula;
+ OUString sInputString;
+ double fValue;
+ sal_Int32 nMatrixCols;
+ sal_Int32 nMatrixRows;
+ formula::FormulaGrammar::Grammar eGrammar;
+ sal_uInt16 nType;
+ ScMatrixMode nMatrixFlag;
+
+ ScMyCellInfo(
+ const ScCellValue& rCell, const OUString& sFormulaAddress, const OUString& sFormula,
+ const formula::FormulaGrammar::Grammar eGrammar, const OUString& sInputString,
+ const double& fValue, const sal_uInt16 nType, const ScMatrixMode nMatrixFlag,
+ const sal_Int32 nMatrixCols, const sal_Int32 nMatrixRows );
+ ~ScMyCellInfo();
+
+ const ScCellValue& CreateCell( ScDocument& rDoc );
+};
+
+struct ScMyDeleted
+{
+ sal_uInt32 nID = 0;
+ std::unique_ptr<ScMyCellInfo> pCellInfo;
+
+ ScMyDeleted(sal_uInt32 id, std::unique_ptr<ScMyCellInfo> p) : nID(id), pCellInfo(std::move(p)) {}
+};
+
+struct ScMyGenerated
+{
+ ScBigRange aBigRange;
+ sal_uInt32 nID = 0;
+ std::unique_ptr<ScMyCellInfo> pCellInfo;
+
+ ScMyGenerated(ScBigRange range, sal_uInt32 id, std::unique_ptr<ScMyCellInfo> p)
+ : aBigRange(range), nID(id), pCellInfo(std::move(p)) {}
+};
+
+struct ScMyInsertionCutOff
+{
+ sal_uInt32 nID;
+ sal_Int32 nPosition;
+
+ ScMyInsertionCutOff(const sal_uInt32 nTempID, const sal_Int32 nTempPosition) :
+ nID(nTempID), nPosition(nTempPosition) {}
+};
+
+struct ScMyMoveCutOff
+{
+ sal_uInt32 nID;
+ sal_Int32 nStartPosition;
+ sal_Int32 nEndPosition;
+
+ ScMyMoveCutOff(const sal_uInt32 nTempID, const sal_Int32 nStartPos, const sal_Int32 nEndPos) :
+ nID(nTempID), nStartPosition(nStartPos), nEndPosition(nEndPos) {}
+};
+
+struct ScMyMoveRanges
+{
+ ScBigRange aSourceRange;
+ ScBigRange aTargetRange;
+
+ ScMyMoveRanges(const ScBigRange& rSource, const ScBigRange& rTarget) :
+ aSourceRange(rSource), aTargetRange(rTarget) {}
+};
+
+struct ScMyBaseAction
+{
+ ScMyActionInfo aInfo;
+ ScBigRange aBigRange;
+ std::vector<sal_uInt32> aDependencies;
+ std::vector<ScMyDeleted> aDeletedList;
+ sal_uInt32 nActionNumber;
+ sal_uInt32 nRejectingNumber;
+ sal_uInt32 nPreviousAction;
+ ScChangeActionType nActionType;
+ ScChangeActionState nActionState;
+
+ explicit ScMyBaseAction(const ScChangeActionType nActionType);
+ virtual ~ScMyBaseAction();
+};
+
+struct ScMyInsAction : public ScMyBaseAction
+{
+ explicit ScMyInsAction(const ScChangeActionType nActionType);
+ virtual ~ScMyInsAction() override;
+};
+
+struct ScMyDelAction : public ScMyBaseAction
+{
+ std::vector<ScMyGenerated> aGeneratedList;
+ std::unique_ptr<ScMyInsertionCutOff> pInsCutOff;
+ std::vector<ScMyMoveCutOff> aMoveCutOffs;
+ sal_Int32 nD;
+
+ explicit ScMyDelAction(const ScChangeActionType nActionType);
+ virtual ~ScMyDelAction() override;
+};
+
+struct ScMyMoveAction : public ScMyBaseAction
+{
+ std::vector<ScMyGenerated> aGeneratedList;
+ std::unique_ptr<ScMyMoveRanges> pMoveRanges;
+
+ ScMyMoveAction();
+ virtual ~ScMyMoveAction() override;
+};
+
+struct ScMyContentAction : public ScMyBaseAction
+{
+ std::unique_ptr<ScMyCellInfo> pCellInfo;
+
+ ScMyContentAction();
+ virtual ~ScMyContentAction() override;
+};
+
+struct ScMyRejAction : public ScMyBaseAction
+{
+ ScMyRejAction();
+ virtual ~ScMyRejAction() override;
+};
+
+class ScXMLChangeTrackingImportHelper
+{
+ std::set<OUString> aUsers;
+ std::vector<std::unique_ptr<ScMyBaseAction>> aActions;
+ css::uno::Sequence<sal_Int8> aProtect;
+ ScChangeTrack* pTrack;
+ std::unique_ptr<ScMyBaseAction> pCurrentAction;
+ sal_Int16 nMultiSpanned;
+ sal_Int16 nMultiSpannedSlaveCount;
+
+private:
+ void ConvertInfo(const ScMyActionInfo& aInfo, OUString& rUser, DateTime& aDateTime);
+ std::unique_ptr<ScChangeAction> CreateInsertAction(const ScMyInsAction* pAction);
+ std::unique_ptr<ScChangeAction> CreateDeleteAction(const ScMyDelAction* pAction);
+ std::unique_ptr<ScChangeAction> CreateMoveAction(const ScMyMoveAction* pAction);
+ std::unique_ptr<ScChangeAction> CreateRejectionAction(const ScMyRejAction* pAction);
+ std::unique_ptr<ScChangeAction> CreateContentAction(const ScMyContentAction* pAction, ScDocument& rDoc);
+
+ void CreateGeneratedActions(std::vector<ScMyGenerated>& rList, ScDocument& rDoc);
+
+public:
+ ScXMLChangeTrackingImportHelper();
+ ~ScXMLChangeTrackingImportHelper();
+
+ void SetProtection(const css::uno::Sequence<sal_Int8>& rProtect) { aProtect = rProtect; }
+ void StartChangeAction(const ScChangeActionType nActionType);
+
+ static sal_uInt32 GetIDFromString(std::string_view sID);
+
+ void SetActionNumber(const sal_uInt32 nActionNumber) { pCurrentAction->nActionNumber = nActionNumber; }
+ void SetActionState(const ScChangeActionState nActionState) { pCurrentAction->nActionState = nActionState; }
+ void SetRejectingNumber(const sal_uInt32 nRejectingNumber) { pCurrentAction->nRejectingNumber = nRejectingNumber; }
+ void SetActionInfo(const ScMyActionInfo& aInfo);
+ void SetBigRange(const ScBigRange& aBigRange) { pCurrentAction->aBigRange = aBigRange; }
+ void SetPreviousChange(const sal_uInt32 nPreviousAction, ScMyCellInfo* pCellInfo);
+ void SetPosition(const sal_Int32 nPosition, const sal_Int32 nCount, const sal_Int32 nTable);
+ void AddDependence(const sal_uInt32 nID) { pCurrentAction->aDependencies.push_back(nID); }
+ void AddDeleted(const sal_uInt32 nID);
+ void AddDeleted(const sal_uInt32 nID, std::unique_ptr<ScMyCellInfo> pCellInfo);
+ void SetMultiSpanned(const sal_Int16 nMultiSpanned);
+ void SetInsertionCutOff(const sal_uInt32 nID, const sal_Int32 nPosition);
+ void AddMoveCutOff(const sal_uInt32 nID, const sal_Int32 nStartPosition, const sal_Int32 nEndPosition);
+ void SetMoveRanges(const ScBigRange& aSourceRange, const ScBigRange& aTargetRange);
+ void GetMultiSpannedRange();
+ void AddGenerated(std::unique_ptr<ScMyCellInfo> pCellInfo, const ScBigRange& aBigRange);
+
+ void EndChangeAction();
+
+ void SetDeletionDependencies(ScMyDelAction* pAction, ScChangeActionDel* pDelAct);
+ void SetMovementDependencies(ScMyMoveAction* pAction, ScChangeActionMove* pMoveAct);
+ void SetContentDependencies(const ScMyContentAction* pAction, ScChangeActionContent* pActContent, const ScDocument& rDoc);
+ void SetDependencies(ScMyBaseAction* pAction, ScDocument& rDoc);
+
+ void SetNewCell(const ScMyContentAction* pAction, ScDocument& rDoc);
+
+ void CreateChangeTrack(ScDocument* pDoc);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCodeNameProvider.cxx b/sc/source/filter/xml/XMLCodeNameProvider.cxx
new file mode 100644
index 000000000..063729d5c
--- /dev/null
+++ b/sc/source/filter/xml/XMLCodeNameProvider.cxx
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLCodeNameProvider.hxx"
+#include <document.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+
+using namespace com::sun::star;
+
+bool XMLCodeNameProvider::_getCodeName( const uno::Any& aAny, OUString& rCodeName )
+{
+ uno::Sequence<beans::PropertyValue> aProps;
+ if( !(aAny >>= aProps) )
+ return false;
+
+ for( const auto& rProp : std::as_const(aProps) )
+ {
+ if( rProp.Name == "CodeName" )
+ {
+ OUString sCodeName;
+ if( rProp.Value >>= sCodeName )
+ {
+ rCodeName = sCodeName;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+constexpr OUStringLiteral gsDocName( u"*doc*" );
+constexpr OUStringLiteral gsCodeNameProp( u"CodeName" );
+
+XMLCodeNameProvider::XMLCodeNameProvider( ScDocument* pDoc ) :
+ mpDoc( pDoc )
+{
+}
+
+XMLCodeNameProvider::~XMLCodeNameProvider()
+{
+}
+
+sal_Bool SAL_CALL XMLCodeNameProvider::hasByName( const OUString& aName )
+{
+ if( aName == gsDocName )
+ return !mpDoc->GetCodeName().isEmpty();
+
+ SCTAB nCount = mpDoc->GetTableCount();
+ OUString sSheetName, sCodeName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ if( mpDoc->GetName( i, sSheetName ) && sSheetName == aName )
+ {
+ mpDoc->GetCodeName( i, sCodeName );
+ return !sCodeName.isEmpty();
+ }
+ }
+
+ return false;
+}
+
+uno::Any SAL_CALL XMLCodeNameProvider::getByName( const OUString& aName )
+{
+ uno::Any aRet;
+ if( aName == gsDocName )
+ {
+ OUString sUCodeName( mpDoc->GetCodeName() );
+ aRet <<= uno::Sequence{ comphelper::makePropertyValue(gsCodeNameProp, sUCodeName) };
+ return aRet;
+ }
+
+ SCTAB nCount = mpDoc->GetTableCount();
+ OUString sSheetName, sCodeName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ if( mpDoc->GetName( i, sSheetName ) && sSheetName == aName )
+ {
+ mpDoc->GetCodeName( i, sCodeName );
+ aRet <<= uno::Sequence{ comphelper::makePropertyValue(gsCodeNameProp, sCodeName) };
+ return aRet;
+ }
+ }
+
+ return aRet;
+}
+
+uno::Sequence< OUString > SAL_CALL XMLCodeNameProvider::getElementNames( )
+{
+ SCTAB nCount = mpDoc->GetTableCount() + 1;
+ std::vector< OUString > aNames;
+ aNames.reserve(nCount);
+
+ if( !mpDoc->GetCodeName().isEmpty() )
+ aNames.push_back(gsDocName);
+
+ OUString sSheetName, sCodeName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ mpDoc->GetCodeName( i, sCodeName );
+ if (!sCodeName.isEmpty())
+ {
+ if( mpDoc->GetName( i, sSheetName ) )
+ aNames.push_back(sSheetName);
+ }
+ }
+
+ return comphelper::containerToSequence(aNames);
+}
+
+uno::Type SAL_CALL XMLCodeNameProvider::getElementType( )
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SAL_CALL XMLCodeNameProvider::hasElements()
+{
+ if( !mpDoc->GetCodeName().isEmpty() )
+ return true;
+
+ SCTAB nCount = mpDoc->GetTableCount();
+ OUString sSheetName, sCodeName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ mpDoc->GetCodeName( i, sCodeName );
+ if (!sCodeName.isEmpty() && mpDoc->GetName(i, sSheetName))
+ return true;
+ }
+
+ return false;
+}
+
+void XMLCodeNameProvider::set( const uno::Reference< container::XNameAccess>& xNameAccess, ScDocument *pDoc )
+{
+ uno::Any aAny;
+ OUString sDocName("*doc*");
+ OUString sCodeName;
+ if( xNameAccess->hasByName( sDocName ) )
+ {
+ aAny = xNameAccess->getByName( sDocName );
+ if( _getCodeName( aAny, sCodeName ) )
+ pDoc->SetCodeName( sCodeName );
+ }
+
+ SCTAB nCount = pDoc->GetTableCount();
+ OUString sSheetName;
+ for( SCTAB i = 0; i < nCount; i++ )
+ {
+ if( pDoc->GetName( i, sSheetName ) &&
+ xNameAccess->hasByName( sSheetName ) )
+ {
+ aAny = xNameAccess->getByName( sSheetName );
+ if( _getCodeName( aAny, sCodeName ) )
+ pDoc->SetCodeName( i, sCodeName );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLCodeNameProvider.hxx b/sc/source/filter/xml/XMLCodeNameProvider.hxx
new file mode 100644
index 000000000..79c057957
--- /dev/null
+++ b/sc/source/filter/xml/XMLCodeNameProvider.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <cppuhelper/implbase.hxx>
+
+namespace com::sun::star::container { class XNameAccess; }
+
+class ScDocument;
+
+class XMLCodeNameProvider : public ::cppu::WeakImplHelper< css::container::XNameAccess >
+{
+ ScDocument* mpDoc;
+
+ static bool _getCodeName( const css::uno::Any& aAny,
+ OUString& rCodeName );
+
+public:
+ explicit XMLCodeNameProvider(ScDocument* pDoc);
+ virtual ~XMLCodeNameProvider() override;
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ static void set( const css::uno::Reference< css::container::XNameAccess>& xNameAccess, ScDocument *pDoc );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLColumnRowGroupExport.cxx b/sc/source/filter/xml/XMLColumnRowGroupExport.cxx
new file mode 100644
index 000000000..55907616a
--- /dev/null
+++ b/sc/source/filter/xml/XMLColumnRowGroupExport.cxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLColumnRowGroupExport.hxx"
+#include "xmlexprt.hxx"
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <algorithm>
+
+using namespace xmloff::token;
+
+ScMyColumnRowGroup::ScMyColumnRowGroup()
+ : nField(0)
+ , nLevel(0)
+ , bDisplay(false)
+{
+}
+
+bool ScMyColumnRowGroup::operator<(const ScMyColumnRowGroup& rGroup) const
+{
+ if (rGroup.nField > nField)
+ return true;
+ else
+ if (rGroup.nField == nField && rGroup.nLevel > nLevel)
+ return true;
+ else
+ return false;
+}
+
+ScMyOpenCloseColumnRowGroup::ScMyOpenCloseColumnRowGroup(ScXMLExport& rTempExport, sal_uInt32 nToken)
+ : rExport(rTempExport),
+ rName(rExport.GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TABLE, GetXMLToken(XMLTokenEnum(nToken))))
+{
+}
+
+ScMyOpenCloseColumnRowGroup::~ScMyOpenCloseColumnRowGroup()
+{
+}
+
+void ScMyOpenCloseColumnRowGroup::NewTable()
+{
+ aTableStart.clear();
+ aTableEnd.clear();
+}
+
+void ScMyOpenCloseColumnRowGroup::AddGroup(const ScMyColumnRowGroup& aGroup, const sal_Int32 nEndField)
+{
+ aTableStart.push_back(aGroup);
+ aTableEnd.push_back(nEndField);
+}
+
+bool ScMyOpenCloseColumnRowGroup::IsGroupStart(const sal_Int32 nField)
+{
+ bool bGroupStart(false);
+ if (!aTableStart.empty())
+ {
+ // when used to find repeated rows at the beginning of a group,
+ // aTableStart may contain entries before nField. They must be skipped here
+ // (they will be used for OpenGroups later in the right order).
+
+ ScMyColumnRowGroupVec::iterator aItr = std::find_if_not(aTableStart.begin(), aTableStart.end(),
+ [&nField](const ScMyColumnRowGroup& rGroup) { return rGroup.nField < nField; });
+ bGroupStart = (aItr != aTableStart.end()) && (aItr->nField == nField);
+ }
+ return bGroupStart;
+}
+
+void ScMyOpenCloseColumnRowGroup::OpenGroup(const ScMyColumnRowGroup& rGroup)
+{
+ if (!rGroup.bDisplay)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, XML_FALSE);
+ rExport.StartElement( rName, true);
+}
+
+void ScMyOpenCloseColumnRowGroup::OpenGroups(const sal_Int32 nField)
+{
+ ScMyColumnRowGroupVec::iterator aItr(aTableStart.begin());
+ bool bReady(false);
+ while(!bReady && aItr != aTableStart.end())
+ {
+ if (aItr->nField == nField)
+ {
+ OpenGroup(*aItr);
+ aItr = aTableStart.erase(aItr);
+ }
+ else
+ bReady = true;
+ }
+}
+
+bool ScMyOpenCloseColumnRowGroup::IsGroupEnd(const sal_Int32 nField)
+{
+ return (!aTableEnd.empty()) && (aTableEnd.front() == nField);
+}
+
+void ScMyOpenCloseColumnRowGroup::CloseGroups(const sal_Int32 nField)
+{
+ ScMyFieldGroupVec::iterator aItr(aTableEnd.begin());
+ bool bReady(false);
+ while(!bReady && aItr != aTableEnd.end())
+ {
+ if (*aItr == nField)
+ {
+ rExport.EndElement( rName, true );
+ aItr = aTableEnd.erase(aItr);
+ }
+ else
+ bReady = true;
+ }
+}
+
+sal_Int32 ScMyOpenCloseColumnRowGroup::GetLast()
+{
+ sal_Int32 maximum(-1);
+ if (!aTableEnd.empty())
+ {
+ ScMyFieldGroupVec::iterator i(std::max_element(aTableEnd.begin(), aTableEnd.end()));
+ if (*i > maximum)
+ maximum = *i;
+ }
+ return maximum;
+}
+
+void ScMyOpenCloseColumnRowGroup::Sort()
+{
+ std::sort(aTableStart.begin(), aTableStart.end());
+ std::sort(aTableEnd.begin(), aTableEnd.end());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLColumnRowGroupExport.hxx b/sc/source/filter/xml/XMLColumnRowGroupExport.hxx
new file mode 100644
index 000000000..3d1a2ac4f
--- /dev/null
+++ b/sc/source/filter/xml/XMLColumnRowGroupExport.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+struct ScMyColumnRowGroup
+{
+ sal_Int32 nField;
+ sal_Int16 nLevel;
+ bool bDisplay;
+
+ ScMyColumnRowGroup();
+ bool operator< (const ScMyColumnRowGroup& rGroup) const;
+};
+
+typedef std::vector<ScMyColumnRowGroup> ScMyColumnRowGroupVec;
+typedef std::vector<sal_Int32> ScMyFieldGroupVec;
+
+class ScXMLExport;
+class ScMyOpenCloseColumnRowGroup
+{
+ ScXMLExport& rExport;
+ const OUString rName;
+ ScMyColumnRowGroupVec aTableStart;
+ ScMyFieldGroupVec aTableEnd;
+
+ void OpenGroup(const ScMyColumnRowGroup& rGroup);
+public:
+ ScMyOpenCloseColumnRowGroup(ScXMLExport& rExport, sal_uInt32 nToken);
+ ~ScMyOpenCloseColumnRowGroup();
+
+ void NewTable();
+ void AddGroup(const ScMyColumnRowGroup& aGroup, const sal_Int32 nEndField);
+ bool IsGroupStart(const sal_Int32 nField);
+ void OpenGroups(const sal_Int32 nField);
+ bool IsGroupEnd(const sal_Int32 nField);
+ void CloseGroups(const sal_Int32 nField);
+ sal_Int32 GetLast();
+ void Sort();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLConsolidationContext.cxx b/sc/source/filter/xml/XMLConsolidationContext.cxx
new file mode 100644
index 000000000..610a3e275
--- /dev/null
+++ b/sc/source/filter/xml/XMLConsolidationContext.cxx
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLConsolidationContext.hxx"
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include "xmlimprt.hxx"
+#include "XMLConverter.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScXMLConsolidationContext::ScXMLConsolidationContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ eFunction( SUBTOTAL_FUNC_NONE ),
+ bLinkToSource( false ),
+ bTargetAddr(false)
+{
+ rImport.LockSolarMutex();
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FUNCTION ):
+ eFunction = ScXMLConverter::GetSubTotalFuncFromString( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_CELL_RANGE_ADDRESSES ):
+ sSourceList = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_CELL_ADDRESS ):
+ {
+ sal_Int32 nOffset(0);
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ bTargetAddr = ScRangeStringConverter::GetAddressFromString(
+ aTargetAddr, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_USE_LABEL ):
+ sUseLabel = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_LINK_TO_SOURCE_DATA ):
+ bLinkToSource = IsXMLToken( aIter, XML_TRUE );
+ break;
+ }
+ }
+}
+
+ScXMLConsolidationContext::~ScXMLConsolidationContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+void SAL_CALL ScXMLConsolidationContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!bTargetAddr)
+ return;
+
+ std::unique_ptr<ScConsolidateParam> pConsParam(new ScConsolidateParam);
+ pConsParam->nCol = aTargetAddr.Col();
+ pConsParam->nRow = aTargetAddr.Row();
+ pConsParam->nTab = aTargetAddr.Tab();
+ pConsParam->eFunction = eFunction;
+
+ sal_uInt16 nCount = static_cast<sal_uInt16>(std::min( ScRangeStringConverter::GetTokenCount( sSourceList ), sal_Int32(0xFFFF) ));
+ if( nCount )
+ {
+ std::unique_ptr<ScArea[]> ppAreas(new ScArea[ nCount ]);
+ sal_Int32 nOffset = 0;
+ sal_uInt16 nIndex;
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ if ( !ScRangeStringConverter::GetAreaFromString(
+ ppAreas[ nIndex ], sSourceList, *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ) )
+ {
+ //! handle error
+ }
+ }
+
+ pConsParam->SetAreas( std::move(ppAreas), nCount );
+ }
+
+ pConsParam->bByCol = pConsParam->bByRow = false;
+ if( IsXMLToken(sUseLabel, XML_COLUMN ) )
+ pConsParam->bByCol = true;
+ else if( IsXMLToken( sUseLabel, XML_ROW ) )
+ pConsParam->bByRow = true;
+ else if( IsXMLToken( sUseLabel, XML_BOTH ) )
+ pConsParam->bByCol = pConsParam->bByRow = true;
+
+ pConsParam->bReferenceData = bLinkToSource;
+
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if( pDoc )
+ pDoc->SetConsolidateDlgData( std::move(pConsParam) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLConsolidationContext.hxx b/sc/source/filter/xml/XMLConsolidationContext.hxx
new file mode 100644
index 000000000..1843ce67e
--- /dev/null
+++ b/sc/source/filter/xml/XMLConsolidationContext.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <global.hxx>
+#include <address.hxx>
+#include "importcontext.hxx"
+
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLConsolidationContext : public ScXMLImportContext
+{
+private:
+ OUString sSourceList;
+ OUString sUseLabel;
+ ScAddress aTargetAddr;
+ ScSubTotalFunc eFunction;
+ bool bLinkToSource;
+ bool bTargetAddr;
+
+public:
+ ScXMLConsolidationContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList
+ );
+ virtual ~ScXMLConsolidationContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLConverter.cxx b/sc/source/filter/xml/XMLConverter.cxx
new file mode 100644
index 000000000..fd7dcb472
--- /dev/null
+++ b/sc/source/filter/xml/XMLConverter.cxx
@@ -0,0 +1,630 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLConverter.hxx"
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <tools/datetime.hxx>
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <rangeutl.hxx>
+#include <docuno.hxx>
+#include <generalfunction.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScDocument* ScXMLConverter::GetScDocument( const uno::Reference< frame::XModel >& xModel )
+{
+ if (xModel.is())
+ {
+ ScModelObj* pDocObj = comphelper::getFromUnoTunnel<ScModelObj>( xModel );
+ return pDocObj ? pDocObj->GetDocument() : nullptr;
+ }
+ return nullptr;
+}
+
+sheet::GeneralFunction ScXMLConverter::GetFunctionFromString( std::u16string_view sFunction )
+{
+ if( IsXMLToken(sFunction, XML_SUM ) )
+ return sheet::GeneralFunction_SUM;
+ if( IsXMLToken(sFunction, XML_AUTO ) )
+ return sheet::GeneralFunction_AUTO;
+ if( IsXMLToken(sFunction, XML_COUNT ) )
+ return sheet::GeneralFunction_COUNT;
+ if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
+ return sheet::GeneralFunction_COUNTNUMS;
+ if( IsXMLToken(sFunction, XML_PRODUCT ) )
+ return sheet::GeneralFunction_PRODUCT;
+ if( IsXMLToken(sFunction, XML_AVERAGE ) )
+ return sheet::GeneralFunction_AVERAGE;
+ if( IsXMLToken(sFunction, XML_MAX ) )
+ return sheet::GeneralFunction_MAX;
+ if( IsXMLToken(sFunction, XML_MIN ) )
+ return sheet::GeneralFunction_MIN;
+ if( IsXMLToken(sFunction, XML_STDEV ) )
+ return sheet::GeneralFunction_STDEV;
+ if( IsXMLToken(sFunction, XML_STDEVP ) )
+ return sheet::GeneralFunction_STDEVP;
+ if( IsXMLToken(sFunction, XML_VAR ) )
+ return sheet::GeneralFunction_VAR;
+ if( IsXMLToken(sFunction, XML_VARP ) )
+ return sheet::GeneralFunction_VARP;
+ return sheet::GeneralFunction_NONE;
+}
+
+ScGeneralFunction ScXMLConverter::GetFunctionFromString2( std::u16string_view sFunction )
+{
+ if( IsXMLToken(sFunction, XML_SUM ) )
+ return ScGeneralFunction::SUM;
+ if( IsXMLToken(sFunction, XML_AUTO ) )
+ return ScGeneralFunction::AUTO;
+ if( IsXMLToken(sFunction, XML_COUNT ) )
+ return ScGeneralFunction::COUNT;
+ if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
+ return ScGeneralFunction::COUNTNUMS;
+ if( IsXMLToken(sFunction, XML_PRODUCT ) )
+ return ScGeneralFunction::PRODUCT;
+ if( IsXMLToken(sFunction, XML_AVERAGE ) )
+ return ScGeneralFunction::AVERAGE;
+ if( IsXMLToken(sFunction, XML_MEDIAN ) )
+ return ScGeneralFunction::MEDIAN;
+ if( IsXMLToken(sFunction, XML_MAX ) )
+ return ScGeneralFunction::MAX;
+ if( IsXMLToken(sFunction, XML_MIN ) )
+ return ScGeneralFunction::MIN;
+ if( IsXMLToken(sFunction, XML_STDEV ) )
+ return ScGeneralFunction::STDEV;
+ if( IsXMLToken(sFunction, XML_STDEVP ) )
+ return ScGeneralFunction::STDEVP;
+ if( IsXMLToken(sFunction, XML_VAR ) )
+ return ScGeneralFunction::VAR;
+ if( IsXMLToken(sFunction, XML_VARP ) )
+ return ScGeneralFunction::VARP;
+ return ScGeneralFunction::NONE;
+}
+
+ScSubTotalFunc ScXMLConverter::GetSubTotalFuncFromString( std::u16string_view sFunction )
+{
+ if( IsXMLToken(sFunction, XML_SUM ) )
+ return SUBTOTAL_FUNC_SUM;
+ if( IsXMLToken(sFunction, XML_COUNT ) )
+ return SUBTOTAL_FUNC_CNT;
+ if( IsXMLToken(sFunction, XML_COUNTNUMS ) )
+ return SUBTOTAL_FUNC_CNT2;
+ if( IsXMLToken(sFunction, XML_PRODUCT ) )
+ return SUBTOTAL_FUNC_PROD;
+ if( IsXMLToken(sFunction, XML_AVERAGE ) )
+ return SUBTOTAL_FUNC_AVE;
+ if( IsXMLToken(sFunction, XML_MEDIAN ) )
+ return SUBTOTAL_FUNC_MED;
+ if( IsXMLToken(sFunction, XML_MAX ) )
+ return SUBTOTAL_FUNC_MAX;
+ if( IsXMLToken(sFunction, XML_MIN ) )
+ return SUBTOTAL_FUNC_MIN;
+ if( IsXMLToken(sFunction, XML_STDEV ) )
+ return SUBTOTAL_FUNC_STD;
+ if( IsXMLToken(sFunction, XML_STDEVP ) )
+ return SUBTOTAL_FUNC_STDP;
+ if( IsXMLToken(sFunction, XML_VAR ) )
+ return SUBTOTAL_FUNC_VAR;
+ if( IsXMLToken(sFunction, XML_VARP ) )
+ return SUBTOTAL_FUNC_VARP;
+ return SUBTOTAL_FUNC_NONE;
+}
+
+OUString ScXMLConverter::GetStringFromFunction(
+ sal_Int16 eFunction )
+{
+ OUString sFuncStr;
+ switch( eFunction )
+ {
+ case sheet::GeneralFunction2::AUTO: sFuncStr = GetXMLToken( XML_AUTO ); break;
+ case sheet::GeneralFunction2::AVERAGE: sFuncStr = GetXMLToken( XML_AVERAGE ); break;
+ case sheet::GeneralFunction2::MEDIAN: sFuncStr = GetXMLToken( XML_MEDIAN ); break;
+ case sheet::GeneralFunction2::COUNT: sFuncStr = GetXMLToken( XML_COUNT ); break;
+ case sheet::GeneralFunction2::COUNTNUMS: sFuncStr = GetXMLToken( XML_COUNTNUMS ); break;
+ case sheet::GeneralFunction2::MAX: sFuncStr = GetXMLToken( XML_MAX ); break;
+ case sheet::GeneralFunction2::MIN: sFuncStr = GetXMLToken( XML_MIN ); break;
+ case sheet::GeneralFunction2::NONE: sFuncStr = GetXMLToken( XML_NONE ); break;
+ case sheet::GeneralFunction2::PRODUCT: sFuncStr = GetXMLToken( XML_PRODUCT ); break;
+ case sheet::GeneralFunction2::STDEV: sFuncStr = GetXMLToken( XML_STDEV ); break;
+ case sheet::GeneralFunction2::STDEVP: sFuncStr = GetXMLToken( XML_STDEVP ); break;
+ case sheet::GeneralFunction2::SUM: sFuncStr = GetXMLToken( XML_SUM ); break;
+ case sheet::GeneralFunction2::VAR: sFuncStr = GetXMLToken( XML_VAR ); break;
+ case sheet::GeneralFunction2::VARP: sFuncStr = GetXMLToken( XML_VARP ); break;
+ default:
+ {
+ assert(false);
+ }
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sFuncStr, false );
+ return str;
+}
+
+OUString ScXMLConverter::GetStringFromFunction(
+ const ScSubTotalFunc eFunction )
+{
+ OUString sFuncStr;
+ switch( eFunction )
+ {
+ case SUBTOTAL_FUNC_AVE: sFuncStr = GetXMLToken( XML_AVERAGE ); break;
+ case SUBTOTAL_FUNC_MED: sFuncStr = GetXMLToken( XML_MEDIAN ); break;
+ case SUBTOTAL_FUNC_CNT: sFuncStr = GetXMLToken( XML_COUNT ); break;
+ case SUBTOTAL_FUNC_CNT2: sFuncStr = GetXMLToken( XML_COUNTNUMS ); break;
+ case SUBTOTAL_FUNC_MAX: sFuncStr = GetXMLToken( XML_MAX ); break;
+ case SUBTOTAL_FUNC_MIN: sFuncStr = GetXMLToken( XML_MIN ); break;
+ case SUBTOTAL_FUNC_NONE: sFuncStr = GetXMLToken( XML_NONE ); break;
+ case SUBTOTAL_FUNC_PROD: sFuncStr = GetXMLToken( XML_PRODUCT ); break;
+ case SUBTOTAL_FUNC_STD: sFuncStr = GetXMLToken( XML_STDEV ); break;
+ case SUBTOTAL_FUNC_STDP: sFuncStr = GetXMLToken( XML_STDEVP ); break;
+ case SUBTOTAL_FUNC_SUM: sFuncStr = GetXMLToken( XML_SUM ); break;
+ case SUBTOTAL_FUNC_SELECTION_COUNT: break;
+ // it is not needed as it is only a UI value and not document content
+
+ case SUBTOTAL_FUNC_VAR: sFuncStr = GetXMLToken( XML_VAR ); break;
+ case SUBTOTAL_FUNC_VARP: sFuncStr = GetXMLToken( XML_VARP ); break;
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sFuncStr, false );
+ return str;
+}
+
+sheet::DataPilotFieldOrientation ScXMLConverter::GetOrientationFromString(
+ std::u16string_view rString )
+{
+ if( IsXMLToken(rString, XML_COLUMN ) )
+ return sheet::DataPilotFieldOrientation_COLUMN;
+ if( IsXMLToken(rString, XML_ROW ) )
+ return sheet::DataPilotFieldOrientation_ROW;
+ if( IsXMLToken(rString, XML_PAGE ) )
+ return sheet::DataPilotFieldOrientation_PAGE;
+ if( IsXMLToken(rString, XML_DATA ) )
+ return sheet::DataPilotFieldOrientation_DATA;
+ return sheet::DataPilotFieldOrientation_HIDDEN;
+}
+
+OUString ScXMLConverter::GetStringFromOrientation(
+ const sheet::DataPilotFieldOrientation eOrientation )
+{
+ OUString sOrientStr;
+ switch( eOrientation )
+ {
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ sOrientStr = GetXMLToken( XML_HIDDEN );
+ break;
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ sOrientStr = GetXMLToken( XML_COLUMN );
+ break;
+ case sheet::DataPilotFieldOrientation_ROW:
+ sOrientStr = GetXMLToken( XML_ROW );
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ sOrientStr = GetXMLToken( XML_PAGE );
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ sOrientStr = GetXMLToken( XML_DATA );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sOrientStr, false );
+ return str;
+}
+
+ScDetectiveObjType ScXMLConverter::GetDetObjTypeFromString( std::u16string_view rString )
+{
+ if( IsXMLToken(rString, XML_FROM_SAME_TABLE ) )
+ return SC_DETOBJ_ARROW;
+ if( IsXMLToken(rString, XML_FROM_ANOTHER_TABLE ) )
+ return SC_DETOBJ_FROMOTHERTAB;
+ if( IsXMLToken(rString, XML_TO_ANOTHER_TABLE ) )
+ return SC_DETOBJ_TOOTHERTAB;
+ return SC_DETOBJ_NONE;
+}
+
+bool ScXMLConverter::GetDetOpTypeFromString( ScDetOpType& rDetOpType, std::u16string_view rString )
+{
+ if( IsXMLToken(rString, XML_TRACE_DEPENDENTS ) )
+ rDetOpType = SCDETOP_ADDSUCC;
+ else if( IsXMLToken(rString, XML_TRACE_PRECEDENTS ) )
+ rDetOpType = SCDETOP_ADDPRED;
+ else if( IsXMLToken(rString, XML_TRACE_ERRORS ) )
+ rDetOpType = SCDETOP_ADDERROR;
+ else if( IsXMLToken(rString, XML_REMOVE_DEPENDENTS ) )
+ rDetOpType = SCDETOP_DELSUCC;
+ else if( IsXMLToken(rString, XML_REMOVE_PRECEDENTS ) )
+ rDetOpType = SCDETOP_DELPRED;
+ else
+ return false;
+ return true;
+}
+
+OUString ScXMLConverter::GetStringFromDetObjType(
+ const ScDetectiveObjType eObjType )
+{
+ OUString sTypeStr;
+ switch( eObjType )
+ {
+ case SC_DETOBJ_ARROW:
+ sTypeStr = GetXMLToken( XML_FROM_SAME_TABLE );
+ break;
+ case SC_DETOBJ_FROMOTHERTAB:
+ sTypeStr = GetXMLToken( XML_FROM_ANOTHER_TABLE );
+ break;
+ case SC_DETOBJ_TOOTHERTAB:
+ sTypeStr = GetXMLToken( XML_TO_ANOTHER_TABLE );
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sTypeStr, false );
+ return str;
+}
+
+OUString ScXMLConverter::GetStringFromDetOpType(
+ const ScDetOpType eOpType )
+{
+ OUString sTypeStr;
+ switch( eOpType )
+ {
+ case SCDETOP_ADDSUCC:
+ sTypeStr = GetXMLToken( XML_TRACE_DEPENDENTS );
+ break;
+ case SCDETOP_ADDPRED:
+ sTypeStr = GetXMLToken( XML_TRACE_PRECEDENTS );
+ break;
+ case SCDETOP_ADDERROR:
+ sTypeStr = GetXMLToken( XML_TRACE_ERRORS );
+ break;
+ case SCDETOP_DELSUCC:
+ sTypeStr = GetXMLToken( XML_REMOVE_DEPENDENTS );
+ break;
+ case SCDETOP_DELPRED:
+ sTypeStr = GetXMLToken( XML_REMOVE_PRECEDENTS );
+ break;
+ }
+ OUString str;
+ ScRangeStringConverter::AssignString( str, sTypeStr, false );
+ return str;
+}
+
+void ScXMLConverter::ConvertCellRangeAddress(OUString& sFormula)
+{
+ OUStringBuffer sBuffer(sFormula.getLength());
+ bool bInQuotationMarks(false);
+ sal_Unicode chPrevious('=');
+ const sal_Unicode* p = sFormula.getStr();
+ const sal_Unicode* const pStop = p + sFormula.getLength();
+ for ( ; p < pStop; ++p)
+ {
+ const sal_Unicode c = *p;
+ if (c == '\'')
+ bInQuotationMarks = !bInQuotationMarks;
+ if (bInQuotationMarks)
+ sBuffer.append(c);
+ else if ((c != '.') ||
+ !((chPrevious == ':') || (chPrevious == ' ') || (chPrevious == '=')))
+ sBuffer.append(c);
+ chPrevious = c;
+ }
+
+ sFormula = sBuffer.makeStringAndClear();
+}
+
+void ScXMLConverter::ConvertDateTimeToString(const DateTime& aDateTime, OUStringBuffer& sDate)
+{
+ css::util::DateTime aAPIDateTime = aDateTime.GetUNODateTime();
+ ::sax::Converter::convertDateTime(sDate, aAPIDateTime, nullptr);
+}
+
+namespace {
+
+/** Enumerates different types of condition tokens. */
+enum ScXMLConditionTokenType
+{
+ XML_COND_TYPE_KEYWORD, /// Simple keyword without parentheses, e.g. 'and'.
+ XML_COND_TYPE_COMPARISON, /// Comparison rule, e.g. 'cell-content()<=2'.
+ XML_COND_TYPE_FUNCTION0, /// Function without parameters, e.g. 'cell-content-is-whole-number()'.
+ XML_COND_TYPE_FUNCTION1, /// Function with 1 parameter, e.g. 'is-true-formula(1+1=2)'.
+ XML_COND_TYPE_FUNCTION2 /// Function with 2 parameters, e.g. 'cell-content-is-between(1,2)'.
+};
+
+struct ScXMLConditionInfo
+{
+ ScXMLConditionToken meToken;
+ ScXMLConditionTokenType meType;
+ sheet::ValidationType meValidation;
+ sheet::ConditionOperator meOperator;
+ const char* mpcIdentifier;
+ sal_Int32 mnIdentLength;
+};
+
+const ScXMLConditionInfo spConditionInfos[] =
+{
+ { XML_COND_AND, XML_COND_TYPE_KEYWORD, sheet::ValidationType_ANY, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "and" ) },
+ { XML_COND_CELLCONTENT, XML_COND_TYPE_COMPARISON, sheet::ValidationType_ANY, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content" ) },
+ { XML_COND_ISBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_ANY, sheet::ConditionOperator_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-between" ) },
+ { XML_COND_ISNOTBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_ANY, sheet::ConditionOperator_NOT_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-not-between" ) },
+ { XML_COND_ISWHOLENUMBER, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_WHOLE, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-whole-number" ) },
+ { XML_COND_ISDECIMALNUMBER, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_DECIMAL, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-decimal-number" ) },
+ { XML_COND_ISDATE, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_DATE, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-date" ) },
+ { XML_COND_ISTIME, XML_COND_TYPE_FUNCTION0, sheet::ValidationType_TIME, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-time" ) },
+ { XML_COND_ISINLIST, XML_COND_TYPE_FUNCTION1, sheet::ValidationType_LIST, sheet::ConditionOperator_EQUAL, RTL_CONSTASCII_STRINGPARAM( "cell-content-is-in-list" ) },
+ { XML_COND_TEXTLENGTH, XML_COND_TYPE_COMPARISON, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_NONE, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length" ) },
+ { XML_COND_TEXTLENGTH_ISBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length-is-between" ) },
+ { XML_COND_TEXTLENGTH_ISNOTBETWEEN, XML_COND_TYPE_FUNCTION2, sheet::ValidationType_TEXT_LEN, sheet::ConditionOperator_NOT_BETWEEN, RTL_CONSTASCII_STRINGPARAM( "cell-content-text-length-is-not-between" ) },
+ { XML_COND_ISTRUEFORMULA, XML_COND_TYPE_FUNCTION1, sheet::ValidationType_CUSTOM, sheet::ConditionOperator_FORMULA, RTL_CONSTASCII_STRINGPARAM( "is-true-formula" ) }
+};
+
+void lclSkipWhitespace( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
+{
+ while( (rpcString < pcEnd) && (*rpcString <= ' ') ) ++rpcString;
+}
+
+const ScXMLConditionInfo* lclGetConditionInfo( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
+{
+ lclSkipWhitespace( rpcString, pcEnd );
+ /* Search the end of an identifier name; assuming that valid identifiers
+ consist of [a-z-] only. */
+ const sal_Unicode* pcIdStart = rpcString;
+ while( (rpcString < pcEnd) && (((*rpcString >= 'a') && (*rpcString <= 'z')) || (*rpcString == '-')) ) ++rpcString;
+ sal_Int32 nLength = static_cast< sal_Int32 >( rpcString - pcIdStart );
+
+ // search the table for an entry
+ if( nLength > 0 )
+ for(auto const &rInfo : spConditionInfos)
+ if((nLength == rInfo.mnIdentLength)
+ && (::rtl_ustr_ascii_shortenedCompare_WithLength(pcIdStart, nLength, rInfo.mpcIdentifier, nLength) == 0) )
+ return &rInfo;
+
+ return nullptr;
+}
+
+sheet::ConditionOperator lclGetConditionOperator( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
+{
+ // check for double-char operators
+ if( (rpcString + 1 < pcEnd) && (rpcString[ 1 ] == '=') )
+ {
+ sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
+ switch( *rpcString )
+ {
+ case '!': eOperator = sheet::ConditionOperator_NOT_EQUAL; break;
+ case '<': eOperator = sheet::ConditionOperator_LESS_EQUAL; break;
+ case '>': eOperator = sheet::ConditionOperator_GREATER_EQUAL; break;
+ }
+ if( eOperator != sheet::ConditionOperator_NONE )
+ {
+ rpcString += 2;
+ return eOperator;
+ }
+ }
+
+ // check for single-char operators
+ if( rpcString < pcEnd )
+ {
+ sheet::ConditionOperator eOperator = sheet::ConditionOperator_NONE;
+ switch( *rpcString )
+ {
+ case '=': eOperator = sheet::ConditionOperator_EQUAL; break;
+ case '<': eOperator = sheet::ConditionOperator_LESS; break;
+ case '>': eOperator = sheet::ConditionOperator_GREATER; break;
+ }
+ if( eOperator != sheet::ConditionOperator_NONE )
+ {
+ ++rpcString;
+ return eOperator;
+ }
+ }
+
+ return sheet::ConditionOperator_NONE;
+}
+
+/** Skips a literal string in a formula expression.
+
+ @param rpcString
+ (in-out) On call, must point to the first character of the string
+ following the leading string delimiter character. On return, points to
+ the trailing string delimiter character if existing, otherwise to
+ pcEnd.
+
+ @param pcEnd
+ The end of the string to parse.
+
+ @param cQuoteChar
+ The string delimiter character enclosing the string.
+ */
+void lclSkipExpressionString( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cQuoteChar )
+{
+ if( rpcString < pcEnd )
+ {
+ sal_Int32 nLength = static_cast< sal_Int32 >( pcEnd - rpcString );
+ sal_Int32 nNextQuote = ::rtl_ustr_indexOfChar_WithLength( rpcString, nLength, cQuoteChar );
+ if( nNextQuote >= 0 )
+ rpcString += nNextQuote;
+ else
+ rpcString = pcEnd;
+ }
+}
+
+/** Skips a formula expression. Processes embedded parentheses, braces, and
+ literal strings.
+
+ @param rpcString
+ (in-out) On call, must point to the first character of the expression.
+ On return, points to the passed end character if existing, otherwise to
+ pcEnd.
+
+ @param pcEnd
+ The end of the string to parse.
+
+ @param cEndChar
+ The termination character following the expression.
+ */
+void lclSkipExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
+{
+ while( rpcString < pcEnd )
+ {
+ if( *rpcString == cEndChar )
+ return;
+ switch( *rpcString )
+ {
+ case '(': lclSkipExpression( ++rpcString, pcEnd, ')' ); break;
+ case '{': lclSkipExpression( ++rpcString, pcEnd, '}' ); break;
+ case '"': lclSkipExpressionString( ++rpcString, pcEnd, '"' ); break;
+ case '\'': lclSkipExpressionString( ++rpcString, pcEnd, '\'' ); break;
+ }
+ if( rpcString < pcEnd ) ++rpcString;
+ }
+}
+
+/** Extracts a formula expression. Processes embedded parentheses, braces, and
+ literal strings.
+
+ @param rpcString
+ (in-out) On call, must point to the first character of the expression.
+ On return, points *behind* the passed end character if existing,
+ otherwise to pcEnd.
+
+ @param pcEnd
+ The end of the string to parse.
+
+ @param cEndChar
+ The termination character following the expression.
+ */
+/** Tries to skip an empty pair of parentheses (which may contain whitespace
+ characters).
+
+ @return
+ True on success, rpcString points behind the closing parentheses then.
+ */
+bool lclSkipEmptyParentheses( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd )
+{
+ if( (rpcString < pcEnd) && (*rpcString == '(') )
+ {
+ lclSkipWhitespace( ++rpcString, pcEnd );
+ if( (rpcString < pcEnd) && (*rpcString == ')') )
+ {
+ ++rpcString;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+void ScXMLConditionHelper::parseCondition(
+ ScXMLConditionParseResult& rParseResult, const OUString& rAttribute, sal_Int32 nStartIndex )
+{
+ rParseResult.meToken = XML_COND_INVALID;
+ if( (nStartIndex < 0) || (nStartIndex >= rAttribute.getLength()) ) return;
+
+ // try to find an identifier
+ const sal_Unicode* pcBegin = rAttribute.getStr();
+ const sal_Unicode* pcString = pcBegin + nStartIndex;
+ const sal_Unicode* pcEnd = pcBegin + rAttribute.getLength();
+ const ScXMLConditionInfo* pCondInfo = lclGetConditionInfo( pcString, pcEnd );
+ if( !pCondInfo )
+ return;
+
+ // insert default values into parse result (may be changed below)
+ rParseResult.meValidation = pCondInfo->meValidation;
+ rParseResult.meOperator = pCondInfo->meOperator;
+ // continue parsing dependent on token type
+ switch( pCondInfo->meType )
+ {
+ case XML_COND_TYPE_KEYWORD:
+ // nothing specific has to follow, success
+ rParseResult.meToken = pCondInfo->meToken;
+ break;
+
+ case XML_COND_TYPE_COMPARISON:
+ // format is <condition>()<operator><expression>
+ if( lclSkipEmptyParentheses( pcString, pcEnd ) )
+ {
+ rParseResult.meOperator = lclGetConditionOperator( pcString, pcEnd );
+ if( rParseResult.meOperator != sheet::ConditionOperator_NONE )
+ {
+ lclSkipWhitespace( pcString, pcEnd );
+ if( pcString < pcEnd )
+ {
+ rParseResult.meToken = pCondInfo->meToken;
+ // comparison must be at end of attribute, remaining text is the formula
+ rParseResult.maOperand1 = OUString( pcString, static_cast< sal_Int32 >( pcEnd - pcString ) );
+ }
+ }
+ }
+ break;
+
+ case XML_COND_TYPE_FUNCTION0:
+ // format is <condition>()
+ if( lclSkipEmptyParentheses( pcString, pcEnd ) )
+ rParseResult.meToken = pCondInfo->meToken;
+ break;
+
+ case XML_COND_TYPE_FUNCTION1:
+ // format is <condition>(<expression>)
+ if( (pcString < pcEnd) && (*pcString == '(') )
+ {
+ rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ')' );
+ if( !rParseResult.maOperand1.isEmpty() )
+ rParseResult.meToken = pCondInfo->meToken;
+ }
+ break;
+
+ case XML_COND_TYPE_FUNCTION2:
+ // format is <condition>(<expression1>,<expression2>)
+ if( (pcString < pcEnd) && (*pcString == '(') )
+ {
+ rParseResult.maOperand1 = getExpression( ++pcString, pcEnd, ',' );
+ if( !rParseResult.maOperand1.isEmpty() )
+ {
+ rParseResult.maOperand2 = getExpression( pcString, pcEnd, ')' );
+ if( !rParseResult.maOperand2.isEmpty() )
+ rParseResult.meToken = pCondInfo->meToken;
+ }
+ }
+ break;
+ }
+ rParseResult.mnEndIndex = static_cast< sal_Int32 >( pcString - pcBegin );
+}
+
+OUString ScXMLConditionHelper::getExpression( const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar )
+{
+ OUString aExp;
+ const sal_Unicode* pcExpStart = rpcString;
+ lclSkipExpression( rpcString, pcEnd, cEndChar );
+ if( rpcString < pcEnd )
+ {
+ aExp = OUString( pcExpStart, static_cast< sal_Int32 >( rpcString - pcExpStart ) ).trim();
+ ++rpcString;
+ }
+ return aExp;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLConverter.hxx b/sc/source/filter/xml/XMLConverter.hxx
new file mode 100644
index 000000000..ad29b1f50
--- /dev/null
+++ b/sc/source/filter/xml/XMLConverter.hxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <global.hxx>
+#include <detfunc.hxx>
+#include <detdata.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/sheet/ConditionOperator.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+#include <com/sun/star/sheet/GeneralFunction.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+
+namespace com::sun::star::frame { class XModel; }
+
+class ScDocument;
+class DateTime;
+enum class ScGeneralFunction;
+
+class ScXMLConverter
+{
+public:
+ ScXMLConverter() = delete;
+
+// helper methods
+ static ScDocument* GetScDocument(
+ const css::uno::Reference< css::frame::XModel >& xModel );
+
+// IMPORT: GeneralFunction / ScSubTotalFunc
+ static css::sheet::GeneralFunction
+ GetFunctionFromString(
+ std::u16string_view rString );
+ static ScGeneralFunction
+ GetFunctionFromString2(
+ std::u16string_view rString );
+
+ static ScSubTotalFunc GetSubTotalFuncFromString(
+ std::u16string_view rString );
+
+// EXPORT: GeneralFunctio2 / ScSubTotalFunc
+ static OUString GetStringFromFunction(
+ const sal_Int16 eFunction );
+ static OUString GetStringFromFunction(
+ const ScSubTotalFunc eFunction );
+
+// IMPORT: DataPilotFieldOrientation
+ static css::sheet::DataPilotFieldOrientation
+ GetOrientationFromString(
+ std::u16string_view rString );
+
+// EXPORT: DataPilotFieldOrientation
+ static OUString GetStringFromOrientation(
+ const css::sheet::DataPilotFieldOrientation eOrientation );
+
+// IMPORT: Detective
+ static ScDetectiveObjType
+ GetDetObjTypeFromString(
+ std::u16string_view rString );
+ static bool GetDetOpTypeFromString(
+ ScDetOpType& rDetOpType,
+ std::u16string_view rString );
+
+// EXPORT: Detective
+ static OUString GetStringFromDetObjType(
+ const ScDetectiveObjType eObjType );
+ static OUString GetStringFromDetOpType(
+ const ScDetOpType eOpType );
+
+// IMPORT: Formulas
+ static void ConvertCellRangeAddress(
+ OUString& sFormula);
+// EXPORT: Core Date Time
+ static void ConvertDateTimeToString(const DateTime& aDateTime, OUStringBuffer& sDate);
+
+};
+
+enum ScXMLConditionToken
+{
+ XML_COND_INVALID, /// Token not recognized.
+ XML_COND_AND, /// The 'and' token.
+ XML_COND_CELLCONTENT, /// The 'cell-content' token.
+ XML_COND_ISBETWEEN, /// The 'cell-content-is-between' token.
+ XML_COND_ISNOTBETWEEN, /// The 'cell-content-is-not-between' token.
+ XML_COND_ISWHOLENUMBER, /// The 'cell-content-is-whole-number' token.
+ XML_COND_ISDECIMALNUMBER, /// The 'cell-content-is-decimal-number' token.
+ XML_COND_ISDATE, /// The 'cell-content-is-date' token.
+ XML_COND_ISTIME, /// The 'cell-content-is-time' token.
+ XML_COND_ISINLIST, /// The 'cell-content-is-in-list' token.
+ XML_COND_TEXTLENGTH, /// The 'cell-content-text-length' token.
+ XML_COND_TEXTLENGTH_ISBETWEEN, /// The 'cell-content-text-length-is-between' token.
+ XML_COND_TEXTLENGTH_ISNOTBETWEEN, /// The 'cell-content-text-length-is-not-between' token.
+ XML_COND_ISTRUEFORMULA /// The 'is-true-formula' token.
+};
+
+/** Result of an attempt to parse a single condition in a 'condition' attribute
+ value of e.g. conditional formatting or data validation.
+ */
+struct ScXMLConditionParseResult
+{
+ ScXMLConditionToken meToken; /// The leading condition token.
+ css::sheet::ValidationType
+ meValidation; /// A data validation type if existing.
+ css::sheet::ConditionOperator
+ meOperator; /// A comparison operator if existing.
+ OUString maOperand1; /// First operand of the token or comparison value.
+ OUString maOperand2; /// Second operand of 'between' conditions.
+ sal_Int32 mnEndIndex; /// Index of first character following the condition.
+
+ ScXMLConditionParseResult()
+ : meToken(XML_COND_INVALID)
+ , meValidation(css::sheet::ValidationType_ANY)
+ , meOperator(css::sheet::ConditionOperator_NONE)
+ , mnEndIndex(-1)
+ {
+ }
+};
+
+namespace ScXMLConditionHelper
+{
+ /** Parses the next condition in a 'condition' attribute value of e.g.
+ conditional formatting or data validation.
+ */
+ void parseCondition(
+ ScXMLConditionParseResult& rParseResult,
+ const OUString& rAttribute,
+ sal_Int32 nStartIndex );
+
+ OUString getExpression(const sal_Unicode*& rpcString, const sal_Unicode* pcEnd, sal_Unicode cEndChar );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLDDELinksContext.cxx b/sc/source/filter/xml/XMLDDELinksContext.cxx
new file mode 100644
index 000000000..a44308da5
--- /dev/null
+++ b/sc/source/filter/xml/XMLDDELinksContext.cxx
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLDDELinksContext.hxx"
+#include "xmlimprt.hxx"
+#include <document.hxx>
+#include <scmatrix.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLDDELinksContext::ScXMLDDELinksContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // here are no attributes
+ rImport.LockSolarMutex();
+}
+
+ScXMLDDELinksContext::~ScXMLDDELinksContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDDELinksContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if ( nElement == XML_ELEMENT( TABLE, XML_DDE_LINK) )
+ pContext = new ScXMLDDELinkContext(GetScImport());
+
+ return pContext;
+}
+
+ScXMLDDELinkContext::ScXMLDDELinkContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport ),
+ nPosition(-1),
+ nColumns(0),
+ nRows(0),
+ nMode(SC_DDE_DEFAULT)
+{
+ // here are no attributes
+}
+
+ScXMLDDELinkContext::~ScXMLDDELinkContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDDELinkContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_DDE_SOURCE ):
+ pContext = new ScXMLDDESourceContext(GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ pContext = new ScXMLDDETableContext(GetScImport(), this);
+ break;
+ }
+
+ return pContext;
+}
+
+void ScXMLDDELinkContext::CreateDDELink()
+{
+ if (GetScImport().GetDocument() &&
+ !sApplication.isEmpty() &&
+ !sTopic.isEmpty() &&
+ !sItem.isEmpty())
+ {
+ GetScImport().GetDocument()->CreateDdeLink(sApplication, sTopic, sItem, nMode, ScMatrixRef());
+ size_t nPos;
+ if(GetScImport().GetDocument()->FindDdeLink(sApplication, sTopic, sItem, nMode, nPos))
+ nPosition = nPos;
+ else
+ {
+ nPosition = -1;
+ SAL_WARN("sc" , "DDE Link not inserted");
+ }
+ }
+}
+
+void ScXMLDDELinkContext::AddCellToRow(const ScDDELinkCell& aCell)
+{
+ aDDELinkRow.push_back(aCell);
+}
+
+void ScXMLDDELinkContext::AddRowsToTable(const sal_Int32 nRowsP)
+{
+ for (sal_Int32 i = 0; i < nRowsP; ++i)
+ aDDELinkTable.insert(aDDELinkTable.end(), aDDELinkRow.begin(), aDDELinkRow.end());
+ aDDELinkRow.clear();
+}
+
+void SAL_CALL ScXMLDDELinkContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (!(nPosition > -1 && nColumns && nRows))
+ return;
+
+ bool bSizeMatch = (static_cast<size_t>(nColumns * nRows) == aDDELinkTable.size());
+ OSL_ENSURE( bSizeMatch, "ScXMLDDELinkContext::EndElement: matrix dimension doesn't match cells count");
+ // Excel writes bad ODF in that it does not write the
+ // table:number-columns-repeated attribute of the
+ // <table:table-column> element, but apparently uses the number of
+ // <table:table-cell> elements within a <table:table-row> element to
+ // determine the column count instead. Be lenient ...
+ if (!bSizeMatch && nColumns == 1)
+ {
+ nColumns = aDDELinkTable.size() / nRows;
+ OSL_ENSURE( static_cast<size_t>(nColumns * nRows) == aDDELinkTable.size(),
+ "ScXMLDDELinkContext::EndElement: adapted matrix dimension doesn't match either");
+ }
+ ScMatrixRef pMatrix = new ScMatrix(static_cast<SCSIZE>(nColumns), static_cast<SCSIZE>(nRows), 0.0);
+ sal_Int32 nCol(0);
+ sal_Int32 nRow(-1);
+ sal_Int32 nIndex(0);
+
+ svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
+ for (const auto& rDDELinkCell : aDDELinkTable)
+ {
+ if (nIndex % nColumns == 0)
+ {
+ ++nRow;
+ nCol = 0;
+ }
+ else
+ ++nCol;
+
+ SCSIZE nScCol( static_cast< SCSIZE >( nCol ) );
+ SCSIZE nScRow( static_cast< SCSIZE >( nRow ) );
+ if( rDDELinkCell.bEmpty )
+ pMatrix->PutEmpty( nScCol, nScRow );
+ else if( rDDELinkCell.bString )
+ pMatrix->PutString(rPool.intern(rDDELinkCell.sValue), nScCol, nScRow);
+ else
+ pMatrix->PutDouble( rDDELinkCell.fValue, nScCol, nScRow );
+
+ ++nIndex;
+ }
+
+ GetScImport().GetDocument()->SetDdeLinkResultMatrix( static_cast< sal_uInt16 >( nPosition ), pMatrix );
+}
+
+ScXMLDDESourceContext::ScXMLDDESourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pTempDDELink) :
+ ScXMLImportContext( rImport ),
+ pDDELink(pTempDDELink)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( OFFICE, XML_DDE_APPLICATION ):
+ pDDELink->SetApplication(aIter.toString());
+ break;
+ case XML_ELEMENT( OFFICE, XML_DDE_TOPIC ):
+ pDDELink->SetTopic(aIter.toString());
+ break;
+ case XML_ELEMENT( OFFICE, XML_DDE_ITEM ):
+ pDDELink->SetItem(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_CONVERSION_MODE ):
+ if (IsXMLToken(aIter, XML_INTO_ENGLISH_NUMBER))
+ pDDELink->SetMode(SC_DDE_ENGLISH);
+ else if (IsXMLToken(aIter, XML_KEEP_TEXT))
+ pDDELink->SetMode(SC_DDE_TEXT);
+ else
+ pDDELink->SetMode(SC_DDE_DEFAULT);
+ break;
+ }
+ }
+}
+
+ScXMLDDESourceContext::~ScXMLDDESourceContext()
+{
+}
+
+void SAL_CALL ScXMLDDESourceContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pDDELink->CreateDDELink();
+}
+
+ScXMLDDETableContext::ScXMLDDETableContext( ScXMLImport& rImport,
+ ScXMLDDELinkContext* pTempDDELink) :
+ ScXMLImportContext( rImport ),
+ pDDELink(pTempDDELink)
+{
+ // here are no attributes
+}
+
+ScXMLDDETableContext::~ScXMLDDETableContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDDETableContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN ):
+ pContext = new ScXMLDDEColumnContext(GetScImport(), pAttribList, pDDELink);
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW ):
+ pContext = new ScXMLDDERowContext(GetScImport(), pAttribList, pDDELink);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDDEColumnContext::ScXMLDDEColumnContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink) :
+ ScXMLImportContext( rImport )
+{
+ if ( rAttrList.is() )
+ {
+ sal_Int32 nCols(1);
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ) ) );
+ if (aIter != rAttrList->end())
+ nCols = aIter.toInt32();
+
+ pDDELink->AddColumns(nCols);
+ }
+}
+
+ScXMLDDEColumnContext::~ScXMLDDEColumnContext()
+{
+}
+
+ScXMLDDERowContext::ScXMLDDERowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pTempDDELink) :
+ ScXMLImportContext( rImport ),
+ pDDELink(pTempDDELink),
+ nRows(1)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_NUMBER_ROWS_REPEATED ) ) );
+ if (aIter != rAttrList->end())
+ nRows = aIter.toInt32();
+
+ pDDELink->AddRows(nRows);
+ }
+}
+
+ScXMLDDERowContext::~ScXMLDDERowContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDDERowContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT( TABLE, XML_TABLE_CELL ))
+ pContext = new ScXMLDDECellContext(GetScImport(), pAttribList, pDDELink);
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDDERowContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pDDELink->AddRowsToTable(nRows);
+}
+
+ScXMLDDECellContext::ScXMLDDECellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pTempDDELink) :
+ ScXMLImportContext( rImport ),
+ fValue(),
+ nCells(1),
+ bString(true),
+ bString2(true),
+ bEmpty(true),
+ pDDELink(pTempDDELink)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( OFFICE, XML_VALUE_TYPE ):
+ if (IsXMLToken(aIter, XML_STRING))
+ bString = true;
+ else
+ bString = false;
+ break;
+ case XML_ELEMENT( OFFICE, XML_STRING_VALUE ):
+ sValue = aIter.toString();
+ bEmpty = false;
+ bString2 = true;
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE ):
+ fValue = aIter.toDouble();
+ bEmpty = false;
+ bString2 = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ):
+ nCells = aIter.toInt32();
+ break;
+ }
+ }
+}
+
+ScXMLDDECellContext::~ScXMLDDECellContext()
+{
+}
+
+void SAL_CALL ScXMLDDECellContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ OSL_ENSURE(bString == bString2, "something wrong with this type");
+ ScDDELinkCell aCell;
+ aCell.sValue = sValue;
+ aCell.fValue = fValue;
+ aCell.bEmpty = bEmpty;
+ aCell.bString = bString2;
+ for(sal_Int32 i = 0; i < nCells; ++i)
+ pDDELink->AddCellToRow(aCell);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLDDELinksContext.hxx b/sc/source/filter/xml/XMLDDELinksContext.hxx
new file mode 100644
index 000000000..a59978620
--- /dev/null
+++ b/sc/source/filter/xml/XMLDDELinksContext.hxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+#include <vector>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLDDELinksContext : public ScXMLImportContext
+{
+public:
+ ScXMLDDELinksContext( ScXMLImport& rImport);
+
+ virtual ~ScXMLDDELinksContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+struct ScDDELinkCell
+{
+ OUString sValue;
+ double fValue;
+ bool bString;
+ bool bEmpty;
+};
+
+typedef std::vector<ScDDELinkCell> ScDDELinkCells;
+
+class ScXMLDDELinkContext : public ScXMLImportContext
+{
+ ScDDELinkCells aDDELinkTable;
+ ScDDELinkCells aDDELinkRow;
+ OUString sApplication;
+ OUString sTopic;
+ OUString sItem;
+ sal_Int32 nPosition;
+ sal_Int32 nColumns;
+ sal_Int32 nRows;
+ sal_uInt8 nMode;
+
+public:
+ ScXMLDDELinkContext( ScXMLImport& rImport);
+
+ virtual ~ScXMLDDELinkContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void SetApplication(const OUString& sValue) { sApplication = sValue; }
+ void SetTopic(const OUString& sValue) { sTopic = sValue; }
+ void SetItem(const OUString& sValue) { sItem = sValue; }
+ void SetMode(const sal_uInt8 nValue) { nMode = nValue; }
+ void CreateDDELink();
+ void AddColumns(const sal_Int32 nValue) { nColumns += nValue; }
+ void AddRows(const sal_Int32 nValue) { nRows += nValue; }
+ void AddCellToRow(const ScDDELinkCell& aCell);
+ void AddRowsToTable(const sal_Int32 nRows);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDDESourceContext : public ScXMLImportContext
+{
+ ScXMLDDELinkContext* pDDELink;
+
+public:
+ ScXMLDDESourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDESourceContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDDETableContext : public ScXMLImportContext
+{
+ ScXMLDDELinkContext* pDDELink;
+
+public:
+ ScXMLDDETableContext( ScXMLImport& rImport,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDETableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDDEColumnContext : public ScXMLImportContext
+{
+public:
+ ScXMLDDEColumnContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDEColumnContext() override;
+};
+
+class ScXMLDDERowContext : public ScXMLImportContext
+{
+ ScXMLDDELinkContext* pDDELink;
+ sal_Int32 nRows;
+
+public:
+ ScXMLDDERowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDERowContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDDECellContext : public ScXMLImportContext
+{
+ OUString sValue;
+ double fValue;
+ sal_Int32 nCells;
+ bool bString;
+ bool bString2;
+ bool bEmpty;
+
+ ScXMLDDELinkContext* pDDELink;
+
+public:
+ ScXMLDDECellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDDELinkContext* pDDELink);
+
+ virtual ~ScXMLDDECellContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLDetectiveContext.cxx b/sc/source/filter/xml/XMLDetectiveContext.cxx
new file mode 100644
index 000000000..b6e3af33a
--- /dev/null
+++ b/sc/source/filter/xml/XMLDetectiveContext.cxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLDetectiveContext.hxx"
+
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include "xmlimprt.hxx"
+#include "XMLConverter.hxx"
+#include <rangeutl.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScMyImpDetectiveObj::ScMyImpDetectiveObj() :
+ eObjType( SC_DETOBJ_NONE ),
+ bHasError( false )
+{
+}
+
+bool ScMyImpDetectiveOp::operator<(const ScMyImpDetectiveOp& rDetOp) const
+{
+ return (nIndex < rDetOp.nIndex);
+}
+
+void ScMyImpDetectiveOpArray::Sort()
+{
+ aDetectiveOpList.sort();
+}
+
+bool ScMyImpDetectiveOpArray::GetFirstOp( ScMyImpDetectiveOp& rDetOp )
+{
+ if( aDetectiveOpList.empty() )
+ return false;
+ ScMyImpDetectiveOpList::iterator aItr = aDetectiveOpList.begin();
+ rDetOp = *aItr;
+ aDetectiveOpList.erase( aItr );
+ return true;
+}
+
+ScXMLDetectiveContext::ScXMLDetectiveContext(
+ ScXMLImport& rImport,
+ ScMyImpDetectiveObjVec* pNewDetectiveObjVec ) :
+ ScXMLImportContext( rImport ),
+ pDetectiveObjVec( pNewDetectiveObjVec )
+{
+}
+
+ScXMLDetectiveContext::~ScXMLDetectiveContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDetectiveContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_HIGHLIGHTED_RANGE ):
+ pContext = new ScXMLDetectiveHighlightedContext( GetScImport(), pAttribList, pDetectiveObjVec );
+ break;
+ case XML_ELEMENT( TABLE, XML_OPERATION ):
+ pContext = new ScXMLDetectiveOperationContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDetectiveHighlightedContext::ScXMLDetectiveHighlightedContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScMyImpDetectiveObjVec* pNewDetectiveObjVec ):
+ ScXMLImportContext( rImport ),
+ pDetectiveObjVec( pNewDetectiveObjVec ),
+ bValid( false )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_ADDRESS ):
+ {
+ sal_Int32 nOffset(0);
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ bValid = ScRangeStringConverter::GetRangeFromString( aDetectiveObj.aSourceRange, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DIRECTION ):
+ aDetectiveObj.eObjType = ScXMLConverter::GetDetObjTypeFromString( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTAINS_ERROR ):
+ aDetectiveObj.bHasError = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_MARKED_INVALID ):
+ {
+ if (IsXMLToken(aIter, XML_TRUE))
+ aDetectiveObj.eObjType = SC_DETOBJ_CIRCLE;
+ }
+ break;
+ }
+ }
+}
+
+ScXMLDetectiveHighlightedContext::~ScXMLDetectiveHighlightedContext()
+{
+}
+
+void SAL_CALL ScXMLDetectiveHighlightedContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ switch( aDetectiveObj.eObjType )
+ {
+ case SC_DETOBJ_ARROW:
+ case SC_DETOBJ_TOOTHERTAB:
+ break;
+ case SC_DETOBJ_FROMOTHERTAB:
+ case SC_DETOBJ_CIRCLE:
+ bValid = true;
+ break;
+ default:
+ bValid = false;
+ }
+ if( bValid )
+ pDetectiveObjVec->push_back( aDetectiveObj );
+}
+
+ScXMLDetectiveOperationContext::ScXMLDetectiveOperationContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ bHasType( false )
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ bHasType = ScXMLConverter::GetDetOpTypeFromString( aDetectiveOp.eOpType, aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_INDEX ):
+ {
+ sal_Int32 nValue;
+ if (::sax::Converter::convertNumber( nValue, aIter.toView(), 0 ))
+ aDetectiveOp.nIndex = nValue;
+ }
+ break;
+ }
+ }
+ }
+ aDetectiveOp.aPosition = rImport.GetTables().GetCurrentCellPos();
+}
+
+ScXMLDetectiveOperationContext::~ScXMLDetectiveOperationContext()
+{
+}
+
+void SAL_CALL ScXMLDetectiveOperationContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if( bHasType && (aDetectiveOp.nIndex >= 0) )
+ GetScImport().GetDetectiveOpArray()->AddDetectiveOp( aDetectiveOp );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLDetectiveContext.hxx b/sc/source/filter/xml/XMLDetectiveContext.hxx
new file mode 100644
index 000000000..8b0a63537
--- /dev/null
+++ b/sc/source/filter/xml/XMLDetectiveContext.hxx
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <detfunc.hxx>
+#include <detdata.hxx>
+#include "importcontext.hxx"
+
+#include <list>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+struct ScMyImpDetectiveObj
+{
+ ScRange aSourceRange;
+ ScDetectiveObjType eObjType;
+ bool bHasError;
+
+ ScMyImpDetectiveObj();
+};
+
+typedef ::std::vector< ScMyImpDetectiveObj > ScMyImpDetectiveObjVec;
+
+struct ScMyImpDetectiveOp
+{
+ ScAddress aPosition;
+ ScDetOpType eOpType;
+ sal_Int32 nIndex;
+
+ ScMyImpDetectiveOp()
+ : eOpType(SCDETOP_ADDSUCC)
+ , nIndex(-1)
+ {
+ }
+
+ bool operator<(const ScMyImpDetectiveOp& rDetOp) const;
+};
+
+typedef ::std::list< ScMyImpDetectiveOp > ScMyImpDetectiveOpList;
+
+class ScMyImpDetectiveOpArray
+{
+private:
+ ScMyImpDetectiveOpList aDetectiveOpList;
+
+public:
+ ScMyImpDetectiveOpArray() :
+ aDetectiveOpList() {}
+
+ void AddDetectiveOp( const ScMyImpDetectiveOp& rDetOp )
+ { aDetectiveOpList.push_back( rDetOp ); }
+
+ void Sort();
+ bool GetFirstOp( ScMyImpDetectiveOp& rDetOp );
+};
+
+class ScXMLDetectiveContext : public ScXMLImportContext
+{
+private:
+ ScMyImpDetectiveObjVec* pDetectiveObjVec;
+
+public:
+ ScXMLDetectiveContext(
+ ScXMLImport& rImport,
+ ScMyImpDetectiveObjVec* pNewDetectiveObjVec
+ );
+ virtual ~ScXMLDetectiveContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList
+ ) override;
+};
+
+class ScXMLDetectiveHighlightedContext : public ScXMLImportContext
+{
+private:
+ ScMyImpDetectiveObjVec* pDetectiveObjVec;
+ ScMyImpDetectiveObj aDetectiveObj;
+ bool bValid;
+
+public:
+ ScXMLDetectiveHighlightedContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScMyImpDetectiveObjVec* pNewDetectiveObjVec
+ );
+ virtual ~ScXMLDetectiveHighlightedContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDetectiveOperationContext : public ScXMLImportContext
+{
+private:
+ ScMyImpDetectiveOp aDetectiveOp;
+ bool bHasType;
+
+public:
+ ScXMLDetectiveOperationContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList
+ );
+ virtual ~ScXMLDetectiveOperationContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLEmptyContext.cxx b/sc/source/filter/xml/XMLEmptyContext.cxx
new file mode 100644
index 000000000..b2e85896c
--- /dev/null
+++ b/sc/source/filter/xml/XMLEmptyContext.cxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLEmptyContext.hxx"
+#include "xmlimprt.hxx"
+
+ScXMLEmptyContext::ScXMLEmptyContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+}
+
+ScXMLEmptyContext::~ScXMLEmptyContext()
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLEmptyContext::createFastChildContext( sal_Int32 /*nElement*/,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = new ScXMLEmptyContext( GetScImport() );
+
+ return pContext;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLEmptyContext.hxx b/sc/source/filter/xml/XMLEmptyContext.hxx
new file mode 100644
index 000000000..af5e838a7
--- /dev/null
+++ b/sc/source/filter/xml/XMLEmptyContext.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "importcontext.hxx"
+
+class ScXMLEmptyContext : public ScXMLImportContext
+{
+public:
+ ScXMLEmptyContext(ScXMLImport& rImport);
+
+ virtual ~ScXMLEmptyContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDDELinks.cxx b/sc/source/filter/xml/XMLExportDDELinks.cxx
new file mode 100644
index 000000000..795f47a66
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDDELinks.cxx
@@ -0,0 +1,159 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLExportDDELinks.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <sax/tools/converter.hxx>
+#include "xmlexprt.hxx"
+#include <unonames.hxx>
+#include <document.hxx>
+#include <scmatrix.hxx>
+#include <com/sun/star/sheet/XDDELink.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLExportDDELinks::ScXMLExportDDELinks(ScXMLExport& rTempExport)
+ : rExport(rTempExport)
+{
+}
+
+void ScXMLExportDDELinks::WriteCell(const ScMatrixValue& aVal, sal_Int32 nRepeat)
+{
+ bool bString = ScMatrix::IsNonValueType(aVal.nType);
+ bool bEmpty = ScMatrix::IsEmptyType(aVal.nType);
+
+ if (!bEmpty)
+ {
+ if (bString)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_STRING_VALUE, aVal.GetString().getString());
+ }
+ else
+ {
+ OUStringBuffer aBuf;
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT);
+ ::sax::Converter::convertDouble(aBuf, aVal.fVal);
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE, aBuf.makeStringAndClear());
+ }
+ }
+
+ if (nRepeat > 1)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, OUString::number(nRepeat));
+ }
+ SvXMLElementExport(rExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+}
+
+void ScXMLExportDDELinks::WriteTable(const sal_Int32 nPos)
+{
+ ScDocument* pDoc = rExport.GetDocument();
+ if (!pDoc)
+ return;
+
+ const ScMatrix* pMatrix = pDoc->GetDdeLinkResultMatrix(static_cast<sal_uInt16>(nPos));
+ if (!pMatrix)
+ return;
+
+ SCSIZE nCols, nRows;
+ pMatrix->GetDimensions(nCols, nRows);
+
+ SvXMLElementExport aTableElem(rExport, XML_NAMESPACE_TABLE, XML_TABLE, true, true);
+ if (nCols > 1)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, OUString::number(nCols));
+ }
+ {
+ SvXMLElementExport aElemCol(rExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true);
+ }
+
+ for (SCSIZE nRow = 0; nRow < nRows; ++nRow)
+ {
+ sal_Int32 nRepeat = 0;
+ ScMatrixValue aPrevVal;
+ SvXMLElementExport aElemRow(rExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
+ for (SCSIZE nCol = 0; nCol < nCols; ++nCol, ++nRepeat)
+ {
+ ScMatrixValue aVal = pMatrix->Get(nCol, nRow);
+ if (nCol > 0 && aVal != aPrevVal)
+ {
+ // Cell value differs. Flush the cell content.
+ WriteCell(aPrevVal, nRepeat);
+ nRepeat = 0;
+ }
+ aPrevVal = aVal;
+ }
+
+ WriteCell(aPrevVal, nRepeat);
+ }
+}
+
+void ScXMLExportDDELinks::WriteDDELinks(const uno::Reference<sheet::XSpreadsheetDocument>& xSpreadDoc)
+{
+ uno::Reference <beans::XPropertySet> xPropertySet (xSpreadDoc, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xIndex(xPropertySet->getPropertyValue(SC_UNO_DDELINKS), uno::UNO_QUERY);
+ if (!xIndex.is())
+ return;
+
+ sal_Int32 nCount = xIndex->getCount();
+ if (!nCount)
+ return;
+
+ SvXMLElementExport aElemDDEs(rExport, XML_NAMESPACE_TABLE, XML_DDE_LINKS, true, true);
+ for (sal_Int32 nDDELink = 0; nDDELink < nCount; ++nDDELink)
+ {
+ uno::Reference<sheet::XDDELink> xDDELink(xIndex->getByIndex(nDDELink), uno::UNO_QUERY);
+ if (xDDELink.is())
+ {
+ SvXMLElementExport aElemDDE(rExport, XML_NAMESPACE_TABLE, XML_DDE_LINK, true, true);
+ {
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_DDE_APPLICATION, xDDELink->getApplication());
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_DDE_TOPIC, xDDELink->getTopic());
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_DDE_ITEM, xDDELink->getItem());
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_AUTOMATIC_UPDATE, XML_TRUE);
+ sal_uInt8 nMode;
+ if (rExport.GetDocument() &&
+ rExport.GetDocument()->GetDdeLinkMode(nDDELink, nMode))
+ {
+ switch (nMode)
+ {
+ case SC_DDE_ENGLISH :
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONVERSION_MODE, XML_INTO_ENGLISH_NUMBER);
+ break;
+ case SC_DDE_TEXT :
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONVERSION_MODE, XML_KEEP_TEXT);
+ break;
+ }
+ }
+ SvXMLElementExport(rExport, XML_NAMESPACE_OFFICE, XML_DDE_SOURCE, true, true);
+ }
+ WriteTable(nDDELink);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDDELinks.hxx b/sc/source/filter/xml/XMLExportDDELinks.hxx
new file mode 100644
index 000000000..560f49587
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDDELinks.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+namespace com::sun::star::uno { template <class interface_type> class Reference; }
+namespace com::sun::star::sheet { class XSpreadsheetDocument; }
+
+class ScXMLExport;
+struct ScMatrixValue;
+
+class ScXMLExportDDELinks
+{
+ ScXMLExport& rExport;
+
+ void WriteCell(const ScMatrixValue& aVal, sal_Int32 nRepeat);
+ void WriteTable(const sal_Int32 nPos);
+public:
+ explicit ScXMLExportDDELinks(ScXMLExport& rExport);
+ void WriteDDELinks(const css::uno::Reference < css::sheet::XSpreadsheetDocument >& xSpreadDoc);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDataPilot.cxx b/sc/source/filter/xml/XMLExportDataPilot.cxx
new file mode 100644
index 000000000..cd6f26b11
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDataPilot.cxx
@@ -0,0 +1,901 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLExportDataPilot.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <sax/tools/converter.hxx>
+#include <rtl/math.hxx>
+#include <osl/diagnose.h>
+#include "xmlexprt.hxx"
+#include "XMLConverter.hxx"
+#include <document.hxx>
+#include <dpobject.hxx>
+#include <dociter.hxx>
+#include <attrib.hxx>
+#include <patattr.hxx>
+#include <scitems.hxx>
+#include <dpsave.hxx>
+#include <dpshttab.hxx>
+#include <dpsdbtab.hxx>
+#include <dpdimsave.hxx>
+#include <dputil.hxx>
+#include <rangeutl.hxx>
+#include <queryentry.hxx>
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReference.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/GeneralFunction2.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLExportDataPilot::ScXMLExportDataPilot(ScXMLExport& rTempExport)
+ : rExport(rTempExport),
+ pDoc( nullptr )
+{
+}
+
+OUString ScXMLExportDataPilot::getDPOperatorXML(
+ const ScQueryOp aFilterOperator, const utl::SearchParam::SearchType eSearchType)
+{
+ switch (aFilterOperator)
+ {
+ case SC_EQUAL :
+ {
+ if (eSearchType == utl::SearchParam::SearchType::Regexp)
+ return GetXMLToken(XML_MATCH);
+ else
+ return "=";
+ }
+ case SC_NOT_EQUAL :
+ {
+ if (eSearchType == utl::SearchParam::SearchType::Regexp)
+ return GetXMLToken(XML_NOMATCH);
+ else
+ return "!=";
+ }
+ case SC_BOTPERC :
+ return GetXMLToken(XML_BOTTOM_PERCENT);
+ case SC_BOTVAL :
+ return GetXMLToken(XML_BOTTOM_VALUES);
+ case SC_GREATER :
+ return ">";
+ case SC_GREATER_EQUAL :
+ return ">=";
+ case SC_LESS :
+ return "<";
+ case SC_LESS_EQUAL :
+ return "<=";
+ case SC_TOPPERC :
+ return GetXMLToken(XML_TOP_PERCENT);
+ case SC_TOPVAL :
+ return GetXMLToken(XML_TOP_VALUES);
+ default:
+ OSL_FAIL("This FilterOperator is not supported.");
+ }
+ return "=";
+}
+
+void ScXMLExportDataPilot::WriteDPCondition(const ScQueryEntry& aQueryEntry, bool bIsCaseSensitive,
+ utl::SearchParam::SearchType eSearchType)
+{
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(aQueryEntry.nField));
+ if (bIsCaseSensitive)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+ const ScQueryEntry::Item& rItem = aQueryEntry.GetQueryItem();
+ OUString aQueryStr = rItem.maString.getString();
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, aQueryStr);
+ }
+ else
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_NUMBER);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, aQueryStr);
+ }
+
+ if (aQueryEntry.IsQueryByEmpty())
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, GetXMLToken(XML_EMPTY));
+ }
+ else if (aQueryEntry.IsQueryByNonEmpty())
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, GetXMLToken(XML_NOEMPTY));
+ }
+ else
+ rExport.AddAttribute(
+ XML_NAMESPACE_TABLE, XML_OPERATOR,
+ getDPOperatorXML(aQueryEntry.eOp, eSearchType));
+
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_FILTER_CONDITION, true, true);
+}
+
+void ScXMLExportDataPilot::WriteDPFilter(const ScQueryParam& aQueryParam)
+{
+ SCSIZE nQueryEntryCount = aQueryParam.GetEntryCount();
+ if (nQueryEntryCount <= 0)
+ return;
+
+ bool bAnd(false);
+ bool bOr(false);
+ bool bHasEntries(true);
+ SCSIZE nEntries(0);
+ SCSIZE j;
+
+ for ( j = 0; (j < nQueryEntryCount) && bHasEntries; ++j)
+ {
+ ScQueryEntry aEntry = aQueryParam.GetEntry(j);
+ if (aEntry.bDoQuery)
+ {
+ if (nEntries > 0)
+ {
+ if (aEntry.eConnect == SC_AND)
+ bAnd = true;
+ else
+ bOr = true;
+ }
+ ++nEntries;
+ }
+ else
+ bHasEntries = false;
+ }
+ nQueryEntryCount = nEntries;
+ if (!nQueryEntryCount)
+ return;
+
+ if(!((aQueryParam.nCol1 == aQueryParam.nCol2) && (aQueryParam.nRow1 == aQueryParam.nRow2) &&
+ (static_cast<SCCOLROW>(aQueryParam.nCol1) == static_cast<SCCOLROW>(aQueryParam.nRow1)) &&
+ (aQueryParam.nCol1 == 0) && (aQueryParam.nTab == SCTAB_MAX)))
+ {
+ ScRange aConditionRange(aQueryParam.nCol1, aQueryParam.nRow1, aQueryParam.nTab,
+ aQueryParam.nCol2, aQueryParam.nRow2, aQueryParam.nTab);
+ OUString sConditionRange;
+ ScRangeStringConverter::GetStringFromRange( sConditionRange, aConditionRange, pDoc, ::formula::FormulaGrammar::CONV_OOO );
+ if (!sConditionRange.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONDITION_SOURCE_RANGE_ADDRESS, sConditionRange);
+ }
+ if (!aQueryParam.bDuplicate)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_DUPLICATES, XML_FALSE);
+ SvXMLElementExport aElemDPF(rExport, XML_NAMESPACE_TABLE, XML_FILTER, true, true);
+ rExport.CheckAttrList();
+ if (nQueryEntryCount == 1)
+ {
+ WriteDPCondition(aQueryParam.GetEntry(0), aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ else if (bOr && !bAnd)
+ {
+ SvXMLElementExport aElemOr(rExport, XML_NAMESPACE_TABLE, XML_FILTER_OR, true, true);
+ for (j = 0; j < nQueryEntryCount; ++j)
+ {
+ WriteDPCondition(aQueryParam.GetEntry(j), aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ }
+ else if (bAnd && !bOr)
+ {
+ SvXMLElementExport aElemAnd(rExport, XML_NAMESPACE_TABLE, XML_FILTER_AND, true, true);
+ for (j = 0; j < nQueryEntryCount; ++j)
+ {
+ WriteDPCondition(aQueryParam.GetEntry(j), aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ }
+ else
+ {
+ SvXMLElementExport aElemC(rExport, XML_NAMESPACE_TABLE, XML_FILTER_OR, true, true);
+ ScQueryEntry aPrevFilterField(aQueryParam.GetEntry(0));
+ ScQueryConnect aConnection = aQueryParam.GetEntry(1).eConnect;
+ bool bOpenAndElement;
+ OUString aName(rExport.GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TABLE, GetXMLToken(XML_FILTER_AND)));
+ if (aConnection == SC_AND)
+ {
+ rExport.StartElement( aName, true );
+ bOpenAndElement = true;
+ }
+ else
+ bOpenAndElement = false;
+ for (j = 1; j < nQueryEntryCount; ++j)
+ {
+ if (aConnection != aQueryParam.GetEntry(j).eConnect)
+ {
+ aConnection = aQueryParam.GetEntry(j).eConnect;
+ if (aQueryParam.GetEntry(j).eConnect == SC_AND)
+ {
+ rExport.StartElement( aName, true );
+ bOpenAndElement = true;
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ aPrevFilterField = aQueryParam.GetEntry(j);
+ if (j == nQueryEntryCount - 1)
+ {
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ rExport.EndElement(aName, true);
+ bOpenAndElement = false;
+ }
+ }
+ else
+ {
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ aPrevFilterField = aQueryParam.GetEntry(j);
+ if (bOpenAndElement)
+ {
+ rExport.EndElement(aName, true);
+ bOpenAndElement = false;
+ }
+ if (j == nQueryEntryCount - 1)
+ {
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ }
+ }
+ else
+ {
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ aPrevFilterField = aQueryParam.GetEntry(j);
+ if (j == nQueryEntryCount - 1)
+ WriteDPCondition(aPrevFilterField, aQueryParam.bCaseSens, aQueryParam.eSearchType);
+ }
+ }
+ }
+}
+
+void ScXMLExportDataPilot::WriteFieldReference(const ScDPSaveDimension* pDim)
+{
+ const sheet::DataPilotFieldReference* pRef = pDim->GetReferenceValue();
+ if (pRef)
+ {
+ OUString sValueStr;
+ switch (pRef->ReferenceType)
+ {
+ case sheet::DataPilotFieldReferenceType::NONE :
+ sValueStr = GetXMLToken(XML_NONE);
+ break;
+ case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE :
+ sValueStr = GetXMLToken(XML_MEMBER_DIFFERENCE);
+ break;
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE :
+ sValueStr = GetXMLToken(XML_MEMBER_PERCENTAGE);
+ break;
+ case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE :
+ sValueStr = GetXMLToken(XML_MEMBER_PERCENTAGE_DIFFERENCE);
+ break;
+ case sheet::DataPilotFieldReferenceType::RUNNING_TOTAL :
+ sValueStr = GetXMLToken(XML_RUNNING_TOTAL);
+ break;
+ case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE :
+ sValueStr = GetXMLToken(XML_ROW_PERCENTAGE);
+ break;
+ case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE :
+ sValueStr = GetXMLToken(XML_COLUMN_PERCENTAGE);
+ break;
+ case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE :
+ sValueStr = GetXMLToken(XML_TOTAL_PERCENTAGE);
+ break;
+ case sheet::DataPilotFieldReferenceType::INDEX :
+ sValueStr = GetXMLToken(XML_INDEX);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TYPE, sValueStr);
+
+ if (!pRef->ReferenceField.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NAME, pRef->ReferenceField);
+
+ if (pRef->ReferenceItemType == sheet::DataPilotFieldReferenceItemType::NAMED)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MEMBER_TYPE, XML_NAMED);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MEMBER_NAME, pRef->ReferenceItemName);
+ }
+ else
+ {
+ sValueStr.clear();
+ switch(pRef->ReferenceItemType)
+ {
+ case sheet::DataPilotFieldReferenceItemType::PREVIOUS :
+ sValueStr = GetXMLToken(XML_PREVIOUS);
+ break;
+ case sheet::DataPilotFieldReferenceItemType::NEXT :
+ sValueStr = GetXMLToken(XML_NEXT);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MEMBER_TYPE, sValueStr);
+ }
+ SvXMLElementExport aElemDPFR(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_FIELD_REFERENCE, true, true);
+ }
+ rExport.CheckAttrList();
+}
+
+void ScXMLExportDataPilot::WriteSortInfo(const ScDPSaveDimension* pDim)
+{
+ const sheet::DataPilotFieldSortInfo* pSortInfo = pDim->GetSortInfo();
+ if (!pSortInfo)
+ return;
+
+ if (pSortInfo->IsAscending)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORDER, XML_ASCENDING);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORDER, XML_DESCENDING);
+
+ OUString sValueStr;
+ switch (pSortInfo->Mode)
+ {
+ case sheet::DataPilotFieldSortMode::NONE:
+ sValueStr = GetXMLToken(XML_NONE);
+ break;
+ case sheet::DataPilotFieldSortMode::MANUAL:
+ sValueStr = GetXMLToken(XML_MANUAL);
+ break;
+ case sheet::DataPilotFieldSortMode::NAME:
+ sValueStr = GetXMLToken(XML_NAME);
+ break;
+ case sheet::DataPilotFieldSortMode::DATA:
+ sValueStr = GetXMLToken(XML_DATA);
+ if (!pSortInfo->Field.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_FIELD, pSortInfo->Field);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SORT_MODE, sValueStr);
+ SvXMLElementExport aElemDPLSI(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_SORT_INFO, true, true);
+}
+
+void ScXMLExportDataPilot::WriteAutoShowInfo(const ScDPSaveDimension* pDim)
+{
+ const sheet::DataPilotFieldAutoShowInfo* pAutoInfo = pDim->GetAutoShowInfo();
+ if (!pAutoInfo)
+ return;
+
+ if (pAutoInfo->IsEnabled)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ENABLED, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ENABLED, XML_FALSE);
+
+ OUString sValueStr;
+ switch (pAutoInfo->ShowItemsMode)
+ {
+ case sheet::DataPilotFieldShowItemsMode::FROM_TOP:
+ sValueStr = GetXMLToken(XML_FROM_TOP);
+ break;
+ case sheet::DataPilotFieldShowItemsMode::FROM_BOTTOM:
+ sValueStr = GetXMLToken(XML_FROM_BOTTOM);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_MEMBER_MODE, sValueStr);
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MEMBER_COUNT, OUString::number(pAutoInfo->ItemCount));
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_FIELD, pAutoInfo->DataField);
+
+ SvXMLElementExport aElemDPLAI(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_DISPLAY_INFO, true, true);
+}
+
+void ScXMLExportDataPilot::WriteLayoutInfo(const ScDPSaveDimension* pDim)
+{
+ const sheet::DataPilotFieldLayoutInfo* pLayoutInfo = pDim->GetLayoutInfo();
+ if (!pLayoutInfo)
+ return;
+
+ if (pLayoutInfo->AddEmptyLines)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ADD_EMPTY_LINES, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ADD_EMPTY_LINES, XML_FALSE);
+
+ OUString sValueStr;
+ switch (pLayoutInfo->LayoutMode)
+ {
+ case sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT:
+ sValueStr = GetXMLToken(XML_TABULAR_LAYOUT);
+ break;
+ case sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP:
+ sValueStr = GetXMLToken(XML_OUTLINE_SUBTOTALS_TOP);
+ break;
+ case sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM:
+ sValueStr = GetXMLToken(XML_OUTLINE_SUBTOTALS_BOTTOM);
+ break;
+ }
+ if (!sValueStr.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_LAYOUT_MODE, sValueStr);
+ SvXMLElementExport aElemDPLLI(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_LAYOUT_INFO, true, true);
+}
+
+void ScXMLExportDataPilot::WriteSubTotals(const ScDPSaveDimension* pDim)
+{
+ sal_Int32 nSubTotalCount = pDim->GetSubTotalsCount();
+ std::optional<OUString> pLayoutName;
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ // Export display names only for 1.2 extended or later.
+ pLayoutName = pDim->GetSubtotalName();
+
+ if (nSubTotalCount <= 0)
+ return;
+
+ SvXMLElementExport aElemSTs(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_SUBTOTALS, true, true);
+ rExport.CheckAttrList();
+ for (sal_Int32 nSubTotal = 0; nSubTotal < nSubTotalCount; nSubTotal++)
+ {
+ sal_Int16 nFunc = static_cast<sal_Int16>(pDim->GetSubTotalFunc(nSubTotal));
+ OUString sFunction = ScXMLConverter::GetStringFromFunction(nFunc);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FUNCTION, sFunction);
+ if (pLayoutName && nFunc == sheet::GeneralFunction2::AUTO)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE_EXT, XML_DISPLAY_NAME, *pLayoutName);
+ SvXMLElementExport aElemST(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_SUBTOTAL, true, true);
+ }
+}
+
+void ScXMLExportDataPilot::WriteMembers(const ScDPSaveDimension* pDim)
+{
+ const ScDPSaveDimension::MemberList &rMembers = pDim->GetMembers();
+ if (rMembers.empty())
+ return;
+
+ SvXMLElementExport aElemDPMs(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_MEMBERS, true, true);
+ rExport.CheckAttrList();
+ for (const auto& rpMember : rMembers)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, rpMember->GetName());
+
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ // Export display names only for ODF 1.2 extended or later.
+ const std::optional<OUString> & pLayoutName = rpMember->GetLayoutName();
+ if (pLayoutName)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE_EXT, XML_DISPLAY_NAME, *pLayoutName);
+ }
+
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertBool(sBuffer, rpMember->GetIsVisible());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, sBuffer.makeStringAndClear());
+ ::sax::Converter::convertBool(sBuffer, rpMember->GetShowDetails());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SHOW_DETAILS, sBuffer.makeStringAndClear());
+ SvXMLElementExport aElemDPM(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_MEMBER, true, true);
+ rExport.CheckAttrList();
+ }
+}
+
+void ScXMLExportDataPilot::WriteLevels(const ScDPSaveDimension* pDim)
+{
+ // #i114202# GetShowEmpty is only valid if HasShowEmpty is true.
+ if (pDim->HasShowEmpty())
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertBool(sBuffer, pDim->GetShowEmpty());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SHOW_EMPTY, sBuffer.makeStringAndClear());
+ }
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertBool(sBuffer, pDim->GetRepeatItemLabels());
+ rExport.AddAttribute(XML_NAMESPACE_CALC_EXT, XML_REPEAT_ITEM_LABELS, sBuffer.makeStringAndClear());
+ }
+ SvXMLElementExport aElemDPL(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_LEVEL, true, true);
+
+ WriteSubTotals(pDim);
+ WriteMembers(pDim);
+ WriteAutoShowInfo(pDim);
+ WriteSortInfo(pDim);
+ WriteLayoutInfo(pDim);
+ rExport.CheckAttrList();
+}
+
+void ScXMLExportDataPilot::WriteDatePart(sal_Int32 nPart)
+{
+ switch(nPart)
+ {
+ case css::sheet::DataPilotFieldGroupBy::SECONDS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_SECONDS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::MINUTES :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_MINUTES);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::HOURS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_HOURS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::DAYS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_DAYS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::MONTHS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_MONTHS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::QUARTERS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_QUARTERS);
+ }
+ break;
+ case css::sheet::DataPilotFieldGroupBy::YEARS :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUPED_BY, XML_YEARS);
+ }
+ break;
+ }
+}
+
+void ScXMLExportDataPilot::WriteNumGroupInfo(const ScDPNumGroupInfo& rGroupInfo)
+{
+ OSL_ENSURE(rGroupInfo.mbEnable, "group dimension should be enabled");
+ if (rGroupInfo.mbDateValues)
+ {
+ if (rGroupInfo.mbAutoStart)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_START, XML_AUTO);
+ else
+ {
+ OUStringBuffer sDate;
+ rExport.GetMM100UnitConverter().convertDateTime(sDate, rGroupInfo.mfStart);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_START, sDate.makeStringAndClear());
+ }
+ if (rGroupInfo.mbAutoEnd)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_END, XML_AUTO);
+ else
+ {
+ OUStringBuffer sDate;
+ rExport.GetMM100UnitConverter().convertDateTime(sDate, rGroupInfo.mfEnd);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_END, sDate.makeStringAndClear());
+ }
+ }
+ else
+ {
+ if (rGroupInfo.mbAutoStart)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START, XML_AUTO);
+ else
+ {
+ OUString sValue( ::rtl::math::doubleToUString( rGroupInfo.mfStart,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_START, sValue);
+ }
+ if (rGroupInfo.mbAutoEnd)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END, XML_AUTO);
+ else
+ {
+ OUString sValue( ::rtl::math::doubleToUString( rGroupInfo.mfEnd,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_END, sValue);
+ }
+ }
+ OUString sValue( ::rtl::math::doubleToUString( rGroupInfo.mfStep,
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true));
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_STEP, sValue);
+}
+
+void ScXMLExportDataPilot::WriteGroupDimAttributes(const ScDPSaveGroupDimension* pGroupDim)
+{
+ if (pGroupDim)
+ {
+ OUString aSrcFieldName = ScDPUtil::getSourceDimensionName(pGroupDim->GetSourceDimName());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SOURCE_FIELD_NAME, aSrcFieldName);
+ if (pGroupDim->GetDatePart())
+ {
+ WriteDatePart(pGroupDim->GetDatePart());
+ WriteNumGroupInfo(pGroupDim->GetDateInfo());
+ }
+ }
+}
+
+void ScXMLExportDataPilot::WriteNumGroupDim(const ScDPSaveNumGroupDimension* pNumGroupDim)
+{
+ if (pNumGroupDim)
+ {
+ if (pNumGroupDim->GetDatePart())
+ {
+ WriteDatePart(pNumGroupDim->GetDatePart());
+ WriteNumGroupInfo(pNumGroupDim->GetDateInfo());
+ }
+ else
+ {
+ WriteNumGroupInfo(pNumGroupDim->GetInfo());
+ }
+ }
+}
+
+void ScXMLExportDataPilot::WriteGroupDimElements(const ScDPSaveDimension* pDim, const ScDPDimensionSaveData* pDimData)
+{
+ const ScDPSaveGroupDimension* pGroupDim = nullptr;
+ const ScDPSaveNumGroupDimension* pNumGroupDim = nullptr;
+ if (pDimData)
+ {
+ pGroupDim = pDimData->GetNamedGroupDim(pDim->GetName());
+ pNumGroupDim = pDimData->GetNumGroupDim(pDim->GetName());
+ OSL_ENSURE((!pGroupDim || !pNumGroupDim), "there should be no NumGroup and Group at the same field");
+ if (pGroupDim)
+ WriteGroupDimAttributes(pGroupDim);
+ else if (pNumGroupDim)
+ WriteNumGroupDim(pNumGroupDim);
+ }
+ if (!(pGroupDim || pNumGroupDim))
+ return;
+
+ SvXMLElementExport aElemDPGs(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_GROUPS, true, true);
+ if (!pGroupDim)
+ return;
+
+ if (pGroupDim->GetDatePart())
+ return;
+
+ sal_Int32 nCount = pGroupDim->GetGroupCount();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ const ScDPSaveGroupItem& rGroup = pGroupDim->GetGroupByIndex( i );
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, rGroup.GetGroupName());
+ SvXMLElementExport aElemDPG(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_GROUP, true, true);
+ sal_Int32 nElemCount = rGroup.GetElementCount();
+ for(sal_Int32 j = 0; j < nElemCount; ++j)
+ {
+ const OUString* pElem = rGroup.GetElementByIndex(j);
+ if (pElem)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, *pElem);
+ SvXMLElementExport aElemDPM(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_GROUP_MEMBER, true, true);
+ }
+ }
+ }
+}
+
+void ScXMLExportDataPilot::WriteDimension(const ScDPSaveDimension* pDim, const ScDPDimensionSaveData* pDimData)
+{
+ OUString aSrcDimName = ScDPUtil::getSourceDimensionName(pDim->GetName());
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SOURCE_FIELD_NAME, aSrcDimName);
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ // Export display names only for ODF 1.2 extended or later.
+ const std::optional<OUString> & pLayoutName = pDim->GetLayoutName();
+ if (pLayoutName)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE_EXT, XML_DISPLAY_NAME, *pLayoutName);
+ }
+
+ if (pDim->IsDataLayout())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_IS_DATA_LAYOUT_FIELD, XML_TRUE);
+ sheet::DataPilotFieldOrientation eOrientation = pDim->GetOrientation();
+ OUString sValueStr = ScXMLConverter::GetStringFromOrientation(eOrientation);
+ if( !sValueStr.isEmpty() )
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORIENTATION, sValueStr );
+ if (pDim->GetUsedHierarchy() != 1)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_USED_HIERARCHY, OUString::number(pDim->GetUsedHierarchy()));
+ }
+ sValueStr = ScXMLConverter::GetStringFromFunction( static_cast<sal_Int16>(pDim->GetFunction()) );
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FUNCTION, sValueStr);
+
+ if (eOrientation == sheet::DataPilotFieldOrientation_PAGE)
+ {
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_IGNORE_SELECTED_PAGE, "true");
+ }
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SELECTED_PAGE, pDim->GetCurrentPage());
+ }
+
+ SvXMLElementExport aElemDPF(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_FIELD, true, true);
+ WriteLevels(pDim);
+ WriteFieldReference(pDim);
+ if( pDim->GetOrientation() != sheet::DataPilotFieldOrientation_DATA )
+ WriteGroupDimElements(pDim, pDimData);
+}
+
+void ScXMLExportDataPilot::WriteDimensions(const ScDPSaveData* pDPSave)
+{
+ const ScDPSaveData::DimsType& rDimensions = pDPSave->GetDimensions();
+ for (auto const& iter : rDimensions)
+ {
+ WriteDimension(iter.get(),
+ pDPSave->GetExistingDimensionData());
+ }
+}
+
+void ScXMLExportDataPilot::WriteGrandTotal(::xmloff::token::XMLTokenEnum eOrient, bool bVisible, const std::optional<OUString> & pGrandTotal)
+{
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, bVisible ? XML_TRUE : XML_FALSE);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORIENTATION, eOrient);
+ if (pGrandTotal)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE_EXT, XML_DISPLAY_NAME, *pGrandTotal);
+
+ SvXMLElementExport aElemGrandTotal(rExport, XML_NAMESPACE_TABLE_EXT, XML_DATA_PILOT_GRAND_TOTAL, true, true);
+}
+
+void ScXMLExportDataPilot::WriteDataPilots()
+{
+ pDoc = rExport.GetDocument();
+ if (!pDoc)
+ return;
+
+ ScDPCollection* pDPs = pDoc->GetDPCollection();
+ if (!pDPs)
+ return;
+
+ size_t nDPCount = pDPs->GetCount();
+ if (!nDPCount)
+ return;
+
+ SvXMLElementExport aElemDPs(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_TABLES, true, true);
+ rExport.CheckAttrList();
+ for (size_t i = 0; i < nDPCount; ++i)
+ {
+ ScDPSaveData* pDPSave = (*pDPs)[i].GetSaveData();
+ if (!pDPSave)
+ continue;
+
+ ScRange aOutRange((*pDPs)[i].GetOutRange());
+ OUString sTargetRangeAddress;
+ ScRangeStringConverter::GetStringFromRange( sTargetRangeAddress, aOutRange, pDoc, ::formula::FormulaGrammar::CONV_OOO );
+ ScDocAttrIterator aAttrItr(*pDoc, aOutRange.aStart.Tab(),
+ aOutRange.aStart.Col(), aOutRange.aStart.Row(),
+ aOutRange.aEnd.Col(), aOutRange.aEnd.Row());
+ SCCOL nCol;
+ SCROW nRow1, nRow2;
+ OUString sOUButtonList;
+ const ScPatternAttr* pAttr = aAttrItr.GetNext(nCol, nRow1, nRow2);
+ while (pAttr)
+ {
+ const ScMergeFlagAttr& rItem = pAttr->GetItem(ATTR_MERGE_FLAG);
+ if (rItem.HasPivotButton())
+ {
+ for (SCROW nButtonRow = nRow1; nButtonRow <= nRow2; ++nButtonRow)
+ {
+ ScAddress aButtonAddr(nCol, nButtonRow, aOutRange.aStart.Tab());
+ ScRangeStringConverter::GetStringFromAddress(
+ sOUButtonList, aButtonAddr, pDoc, ::formula::FormulaGrammar::CONV_OOO, ' ', true );
+ }
+ }
+ pAttr = aAttrItr.GetNext(nCol, nRow1, nRow2);
+ }
+ OUString sName((*pDPs)[i].GetName());
+ OUString sApplicationData((*pDPs)[i].GetTag());
+ bool bRowGrand = pDPSave->GetRowGrand();
+ bool bColumnGrand = pDPSave->GetColumnGrand();
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, sName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_APPLICATION_DATA, sApplicationData);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, sTargetRangeAddress);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_BUTTONS, sOUButtonList);
+ if (!(bRowGrand && bColumnGrand))
+ {
+ if (bRowGrand)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GRAND_TOTAL, XML_ROW);
+ else if (bColumnGrand)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GRAND_TOTAL, XML_COLUMN);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GRAND_TOTAL, XML_NONE);
+ }
+ if (pDPSave->GetIgnoreEmptyRows())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_IGNORE_EMPTY_ROWS, XML_TRUE);
+ if (pDPSave->GetRepeatIfEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_IDENTIFY_CATEGORIES, XML_TRUE);
+ if (!pDPSave->GetFilterButton())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SHOW_FILTER_BUTTON, XML_FALSE);
+ if (!pDPSave->GetDrillDown())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DRILL_DOWN_ON_DOUBLE_CLICK, XML_FALSE);
+ if ((*pDPs)[i].GetHeaderLayout())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_HEADER_GRID_LAYOUT, XML_TRUE);
+
+ SvXMLElementExport aElemDP(rExport, XML_NAMESPACE_TABLE, XML_DATA_PILOT_TABLE, true, true);
+
+ // grand total elements.
+
+ const std::optional<OUString> & pGrandTotalName = pDPSave->GetGrandTotalName();
+ if (pGrandTotalName && rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ // Use the new data-pilot-grand-total element.
+ if (bRowGrand && bColumnGrand)
+ {
+ WriteGrandTotal(XML_BOTH, true, pGrandTotalName);
+ }
+ else
+ {
+ WriteGrandTotal(XML_ROW, bRowGrand, pGrandTotalName);
+ WriteGrandTotal(XML_COLUMN, bColumnGrand, pGrandTotalName);
+ }
+ }
+
+ rExport.CheckAttrList();
+ if ((*pDPs)[i].IsSheetData())
+ {
+ const ScSheetSourceDesc* pSheetSource = (*pDPs)[i].GetSheetDesc();
+
+ if (rExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ if (pSheetSource->HasRangeName())
+ { // ODF 1.3 OFFICE-3665
+ // FIXME this was wrongly exported to TABLE namespace since 2011
+ // so continue doing that in ODF 1.2 extended, for now
+ rExport.AddAttribute(
+ XML_NAMESPACE_TABLE, XML_NAME, pSheetSource->GetRangeName());
+ }
+ }
+
+ OUString sCellRangeAddress;
+ ScRangeStringConverter::GetStringFromRange(
+ sCellRangeAddress, pSheetSource->GetSourceRange(), pDoc,
+ ::formula::FormulaGrammar::CONV_OOO);
+
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, sCellRangeAddress);
+ SvXMLElementExport aElemSCR(rExport, XML_NAMESPACE_TABLE, XML_SOURCE_CELL_RANGE, true, true);
+ rExport.CheckAttrList();
+ WriteDPFilter(pSheetSource->GetQueryParam());
+ }
+ else if ((*pDPs)[i].IsImportData())
+ {
+ const ScImportSourceDesc* pImpSource = (*pDPs)[i].GetImportSourceDesc();
+ switch (pImpSource->nType)
+ {
+ case sheet::DataImportMode_NONE : break;
+ case sheet::DataImportMode_QUERY :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, pImpSource->aDBName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_QUERY_NAME, pImpSource->aObject);
+ SvXMLElementExport aElemID(rExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_QUERY, true, true);
+ rExport.CheckAttrList();
+ }
+ break;
+ case sheet::DataImportMode_TABLE :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, pImpSource->aDBName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_TABLE_NAME, pImpSource->aObject);
+ SvXMLElementExport aElemID(rExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_TABLE, true, true);
+ rExport.CheckAttrList();
+ }
+ break;
+ case sheet::DataImportMode_SQL :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, pImpSource->aDBName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SQL_STATEMENT, pImpSource->aObject);
+ if (!pImpSource->bNative)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_PARSE_SQL_STATEMENT, XML_TRUE);
+ SvXMLElementExport aElemID(rExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_SQL, true, true);
+ rExport.CheckAttrList();
+ }
+ break;
+ default: break;
+ }
+ }
+ else if ((*pDPs)[i].IsServiceData())
+ {
+ const ScDPServiceDesc* pServSource = (*pDPs)[i].GetDPServiceDesc();
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, pServSource->aServiceName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SOURCE_NAME, pServSource->aParSource);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OBJECT_NAME, pServSource->aParName);
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_USER_NAME, pServSource->aParUser);
+ // #i111754# leave out password attribute as long as DataPilotSource doesn't specify the content
+ // rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_PASSWORD, OUString(pServSource->aParPass));
+ SvXMLElementExport aElemSD(rExport, XML_NAMESPACE_TABLE, XML_SOURCE_SERVICE, true, true);
+ rExport.CheckAttrList();
+ }
+ WriteDimensions(pDPSave);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDataPilot.hxx b/sc/source/filter/xml/XMLExportDataPilot.hxx
new file mode 100644
index 000000000..dcf4ff344
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDataPilot.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <optional>
+
+#include <rtl/ustring.hxx>
+#include <global.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <unotools/textsearch.hxx>
+
+class ScXMLExport;
+class ScDocument;
+class ScDPSaveDimension;
+class ScDPSaveData;
+class ScDPDimensionSaveData;
+class ScDPSaveGroupDimension;
+class ScDPSaveNumGroupDimension;
+struct ScDPNumGroupInfo;
+struct ScQueryParam;
+struct ScQueryEntry;
+
+class ScXMLExportDataPilot
+{
+ ScXMLExport& rExport;
+ ScDocument* pDoc;
+
+ static OUString getDPOperatorXML(const ScQueryOp aFilterOperator, const utl::SearchParam::SearchType eSearchType);
+ void WriteDPCondition(const ScQueryEntry& aQueryEntry, bool bIsCaseSensitive,
+ utl::SearchParam::SearchType eSearchType);
+ void WriteDPFilter(const ScQueryParam& aQueryParam);
+
+ void WriteFieldReference(const ScDPSaveDimension* pDim);
+ void WriteSortInfo(const ScDPSaveDimension* pDim);
+ void WriteAutoShowInfo(const ScDPSaveDimension* pDim);
+ void WriteLayoutInfo(const ScDPSaveDimension* pDim);
+ void WriteSubTotals(const ScDPSaveDimension* pDim);
+ void WriteMembers(const ScDPSaveDimension* pDim);
+ void WriteLevels(const ScDPSaveDimension* pDim);
+ void WriteDatePart(sal_Int32 nPart);
+ void WriteNumGroupInfo(const ScDPNumGroupInfo& pGroupInfo);
+ void WriteGroupDimAttributes(const ScDPSaveGroupDimension* pGroupDim);
+ void WriteGroupDimElements(const ScDPSaveDimension* pDim, const ScDPDimensionSaveData* pDimData);
+ void WriteNumGroupDim(const ScDPSaveNumGroupDimension* pNumGroupDim);
+ void WriteDimension(const ScDPSaveDimension* pDim, const ScDPDimensionSaveData* pDimData);
+ void WriteDimensions(const ScDPSaveData* pDPSave);
+
+ void WriteGrandTotal(::xmloff::token::XMLTokenEnum eOrient, bool bVisible, const std::optional<OUString> & pGrandTotal);
+
+public:
+ explicit ScXMLExportDataPilot(ScXMLExport& rExport);
+ void WriteDataPilots();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDatabaseRanges.cxx b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
new file mode 100644
index 000000000..61c01d236
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDatabaseRanges.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "XMLExportDatabaseRanges.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <sax/tools/converter.hxx>
+#include "xmlexprt.hxx"
+#include "XMLExportIterator.hxx"
+#include "XMLConverter.hxx"
+#include <unonames.hxx>
+#include <dbdata.hxx>
+#include <document.hxx>
+#include <globalnames.hxx>
+#include "XMLExportSharedData.hxx"
+#include <rangeutl.hxx>
+#include <subtotalparam.hxx>
+#include <queryparam.hxx>
+#include <queryentry.hxx>
+#include <sortparam.hxx>
+
+#include <svx/dataaccessdescriptor.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sheet/XDatabaseRanges.hpp>
+#include <com/sun/star/sheet/XDatabaseRange.hpp>
+#include <comphelper/extract.hxx>
+#include <osl/diagnose.h>
+
+#include <map>
+
+//! not found in unonames.hxx
+constexpr OUStringLiteral SC_USERLIST = u"UserList";
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+void writeSort(ScXMLExport& mrExport, const ScSortParam& aParam, const ScRange& aRange, const ScDocument* mpDoc)
+{
+ // Count sort items first.
+ size_t nSortCount = 0;
+ for (; nSortCount < aParam.GetSortKeyCount(); ++nSortCount)
+ {
+ if (!aParam.maKeyState[nSortCount].bDoSort)
+ break;
+ }
+
+ if (!nSortCount)
+ // Nothing to export.
+ return;
+
+ ScAddress aOutPos(aParam.nDestCol, aParam.nDestRow, aParam.nDestTab);
+
+ if (!aParam.aDataAreaExtras.mbCellFormats)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_BIND_STYLES_TO_CONTENT, XML_FALSE);
+
+ if (!aParam.bInplace)
+ {
+ OUString aStr;
+ ScRangeStringConverter::GetStringFromAddress(
+ aStr, aOutPos, mpDoc, ::formula::FormulaGrammar::CONV_OOO);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aStr);
+ }
+
+ if (aParam.bCaseSens)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+
+ mrExport.AddLanguageTagAttributes( XML_NAMESPACE_TABLE, XML_NAMESPACE_TABLE, aParam.aCollatorLocale, false);
+ if (!aParam.aCollatorAlgorithm.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ALGORITHM, aParam.aCollatorAlgorithm);
+
+ SvXMLElementExport aElemS(mrExport, XML_NAMESPACE_TABLE, XML_SORT, true, true);
+
+ SCCOLROW nFieldStart = aParam.bByRow ? aRange.aStart.Col() : aRange.aStart.Row();
+
+ for (size_t i = 0; i < nSortCount; ++i)
+ {
+ // Convert field value from absolute to relative.
+ SCCOLROW nField = aParam.maKeyState[i].nField - nFieldStart;
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(nField));
+
+ if (!aParam.maKeyState[i].bAscending)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORDER, XML_DESCENDING);
+
+ if (aParam.bUserDef)
+ {
+ OUString aBuf = SC_USERLIST + OUString::number(static_cast<sal_Int32>(aParam.nUserIndex));
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, aBuf);
+ }
+ else
+ {
+ // Right now we only support automatic field type. In the
+ // future we may support numeric or alphanumeric field type.
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_AUTOMATIC);
+ }
+
+ SvXMLElementExport aElemSb(mrExport, XML_NAMESPACE_TABLE, XML_SORT_BY, true, true);
+ }
+}
+
+ScXMLExportDatabaseRanges::ScXMLExportDatabaseRanges(ScXMLExport& rTempExport)
+ : rExport(rTempExport),
+ pDoc( nullptr )
+{
+}
+
+ScMyEmptyDatabaseRangesContainer ScXMLExportDatabaseRanges::GetEmptyDatabaseRanges()
+{
+ ScMyEmptyDatabaseRangesContainer aSkipRanges;
+ if (rExport.GetModel().is())
+ {
+ uno::Reference <beans::XPropertySet> xPropertySet (rExport.GetModel(), uno::UNO_QUERY);
+ if (xPropertySet.is())
+ {
+ uno::Reference <sheet::XDatabaseRanges> xDatabaseRanges(xPropertySet->getPropertyValue(SC_UNO_DATABASERNG), uno::UNO_QUERY);
+ rExport.CheckAttrList();
+ if (xDatabaseRanges.is())
+ {
+ const uno::Sequence <OUString> aRanges(xDatabaseRanges->getElementNames());
+ for (const OUString& sDatabaseRangeName : aRanges)
+ {
+ uno::Reference <sheet::XDatabaseRange> xDatabaseRange(xDatabaseRanges->getByName(sDatabaseRangeName), uno::UNO_QUERY);
+ if (xDatabaseRange.is())
+ {
+ uno::Reference <beans::XPropertySet> xDatabaseRangePropertySet (xDatabaseRange, uno::UNO_QUERY);
+ if (xDatabaseRangePropertySet.is() &&
+ ::cppu::any2bool(xDatabaseRangePropertySet->getPropertyValue(SC_UNONAME_STRIPDAT)))
+ {
+ const uno::Sequence <beans::PropertyValue> aImportProperties(xDatabaseRange->getImportDescriptor());
+ sheet::DataImportMode nSourceType = sheet::DataImportMode_NONE;
+ for (const auto& rProp : aImportProperties)
+ if ( rProp.Name == SC_UNONAME_SRCTYPE )
+ rProp.Value >>= nSourceType;
+ if (nSourceType != sheet::DataImportMode_NONE)
+ {
+ table::CellRangeAddress aArea = xDatabaseRange->getDataArea();
+ aSkipRanges.AddNewEmptyDatabaseRange(aArea);
+
+ // #105276#; set last row/column so default styles are collected
+ rExport.GetSharedData()->SetLastColumn(aArea.Sheet, aArea.EndColumn);
+ rExport.GetSharedData()->SetLastRow(aArea.Sheet, aArea.EndRow);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return aSkipRanges;
+}
+
+namespace {
+
+class WriteDatabaseRange
+{
+ ScXMLExport& mrExport;
+ ScDocument* mpDoc;
+ sal_Int32 mnCounter;
+ ScDBCollection::RangeType meRangeType;
+public:
+
+ WriteDatabaseRange(ScXMLExport& rExport, ScDocument* pDoc) :
+ mrExport(rExport), mpDoc(pDoc), mnCounter(0), meRangeType(ScDBCollection::GlobalNamed) {}
+
+ void setRangeType(ScDBCollection::RangeType eNew)
+ {
+ meRangeType = eNew;
+ }
+
+ void operator() (const ::std::pair<SCTAB, const ScDBData*>& r)
+ {
+ if (meRangeType != ScDBCollection::SheetAnonymous)
+ return;
+
+ // name
+ OUString aBuf = STR_DB_LOCAL_NONAME +
+ OUString::number(static_cast<sal_Int32>(r.first)); // appended number equals sheet index on import.
+
+ write(aBuf, *r.second);
+ }
+
+ void operator() (const ScDBData& rData)
+ {
+ if (meRangeType == ScDBCollection::GlobalAnonymous)
+ {
+ // name
+ OUString aBuf = STR_DB_GLOBAL_NONAME + OUString::number(++mnCounter); // 1-based, for entirely arbitrary reasons. The numbers are ignored on import.
+
+ write(aBuf, rData);
+ }
+ else if (meRangeType == ScDBCollection::GlobalNamed)
+ write(rData.GetName(), rData);
+ }
+
+ void operator() (std::unique_ptr<ScDBData> const& p)
+ {
+ return operator()(*p);
+ }
+
+private:
+ void write(const OUString& rName, const ScDBData& rData)
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, rName);
+
+ // range
+ ScRange aRange;
+ rData.GetArea(aRange);
+ OUString aRangeStr;
+ ScRangeStringConverter::GetStringFromRange(
+ aRangeStr, aRange, mpDoc, ::formula::FormulaGrammar::CONV_OOO);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aRangeStr);
+
+ // various boolean flags.
+ if (rData.HasImportSelection())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_IS_SELECTION, XML_TRUE);
+ if (rData.HasAutoFilter())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_FILTER_BUTTONS, XML_TRUE);
+ if (rData.IsKeepFmt())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ON_UPDATE_KEEP_STYLES, XML_TRUE);
+ if (rData.IsDoSize())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ON_UPDATE_KEEP_SIZE, XML_FALSE);
+ if (rData.IsStripData())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_HAS_PERSISTENT_DATA, XML_FALSE);
+
+ ScQueryParam aQueryParam;
+ rData.GetQueryParam(aQueryParam);
+ if (!aQueryParam.bHasHeader)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONTAINS_HEADER, XML_FALSE);
+
+ ScSortParam aSortParam;
+ rData.GetSortParam(aSortParam);
+ if (!aSortParam.bByRow)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORIENTATION, XML_COLUMN);
+
+ sal_Int32 nRefreshDelaySeconds = rData.GetRefreshDelaySeconds();
+ if (nRefreshDelaySeconds)
+ {
+ OUStringBuffer aBuf;
+ ::sax::Converter::convertDuration(aBuf,
+ static_cast<double>(nRefreshDelaySeconds) / 86400.0);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_REFRESH_DELAY, aBuf.makeStringAndClear());
+ }
+
+ SvXMLElementExport aElemDR(mrExport, XML_NAMESPACE_TABLE, XML_DATABASE_RANGE, true, true);
+
+ ScSortParam aParam;
+ rData.GetSortParam(aParam);
+
+ writeImport(rData);
+ writeFilter(rData);
+ writeSort(mrExport, aParam, aRange, mpDoc);
+ writeSubtotals(rData);
+ }
+
+ void writeImport(const ScDBData& rData)
+ {
+ ScImportParam aParam;
+ rData.GetImportParam(aParam);
+
+ OUString sDatabaseName;
+ OUString sConRes;
+
+ svx::ODataAccessDescriptor aDescriptor;
+ aDescriptor.setDataSource(aParam.aDBName);
+ if (aDescriptor.has(svx::DataAccessDescriptorProperty::DataSource))
+ {
+ sDatabaseName = aParam.aDBName;
+ }
+ else if (aDescriptor.has(svx::DataAccessDescriptorProperty::ConnectionResource))
+ {
+ sConRes = aParam.aDBName;
+ }
+
+ sheet::DataImportMode nSourceType = sheet::DataImportMode_NONE;
+ if (aParam.bImport)
+ {
+ if (aParam.bSql)
+ nSourceType = sheet::DataImportMode_SQL;
+ else if (aParam.nType == ScDbQuery)
+ nSourceType = sheet::DataImportMode_QUERY;
+ else
+ nSourceType = sheet::DataImportMode_TABLE;
+ }
+
+ switch (nSourceType)
+ {
+ case sheet::DataImportMode_NONE : break;
+ case sheet::DataImportMode_QUERY :
+ {
+ if (!sDatabaseName.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, sDatabaseName);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_QUERY_NAME, aParam.aStatement);
+ SvXMLElementExport aElemID(mrExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_QUERY, true, true);
+ if (!sConRes.isEmpty())
+ {
+ mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sConRes );
+ SvXMLElementExport aElemCR(mrExport, XML_NAMESPACE_FORM, XML_CONNECTION_RESOURCE, true, true);
+ }
+ }
+ break;
+ case sheet::DataImportMode_TABLE :
+ {
+ if (!sDatabaseName.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, sDatabaseName);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_TABLE_NAME, aParam.aStatement);
+ SvXMLElementExport aElemID(mrExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_TABLE, true, true);
+ if (!sConRes.isEmpty())
+ {
+ mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sConRes );
+ SvXMLElementExport aElemCR(mrExport, XML_NAMESPACE_FORM, XML_CONNECTION_RESOURCE, true, true);
+ }
+ }
+ break;
+ case sheet::DataImportMode_SQL :
+ {
+ if (!sDatabaseName.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATABASE_NAME, sDatabaseName);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_SQL_STATEMENT, aParam.aStatement);
+ if (!aParam.bNative)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_PARSE_SQL_STATEMENT, XML_TRUE);
+ SvXMLElementExport aElemID(mrExport, XML_NAMESPACE_TABLE, XML_DATABASE_SOURCE_SQL, true, true);
+ if (!sConRes.isEmpty())
+ {
+ mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sConRes );
+ SvXMLElementExport aElemCR(mrExport, XML_NAMESPACE_FORM, XML_CONNECTION_RESOURCE, true, true);
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ static OUString getOperatorXML(const ScQueryEntry& rEntry, utl::SearchParam::SearchType eSearchType)
+ {
+ switch (rEntry.eOp)
+ {
+ case SC_BEGINS_WITH:
+ return GetXMLToken(XML_BEGINS_WITH);
+ case SC_BOTPERC:
+ return GetXMLToken(XML_BOTTOM_PERCENT);
+ case SC_BOTVAL:
+ return GetXMLToken(XML_BOTTOM_VALUES);
+ case SC_CONTAINS:
+ return GetXMLToken(XML_CONTAINS);
+ case SC_DOES_NOT_BEGIN_WITH:
+ return GetXMLToken(XML_DOES_NOT_BEGIN_WITH);
+ case SC_DOES_NOT_CONTAIN:
+ return GetXMLToken(XML_DOES_NOT_CONTAIN);
+ case SC_DOES_NOT_END_WITH:
+ return GetXMLToken(XML_DOES_NOT_END_WITH);
+ case SC_ENDS_WITH:
+ return GetXMLToken(XML_ENDS_WITH);
+ case SC_EQUAL:
+ {
+ if (rEntry.IsQueryByEmpty())
+ return GetXMLToken(XML_EMPTY);
+ else if (rEntry.IsQueryByNonEmpty())
+ return GetXMLToken(XML_NOEMPTY);
+
+ if (eSearchType == utl::SearchParam::SearchType::Regexp)
+ return GetXMLToken(XML_MATCH);
+ else
+ return "=";
+ }
+ case SC_GREATER:
+ return ">";
+ case SC_GREATER_EQUAL:
+ return ">=";
+ case SC_LESS:
+ return "<";
+ case SC_LESS_EQUAL:
+ return "<=";
+ case SC_NOT_EQUAL:
+ if (eSearchType == utl::SearchParam::SearchType::Regexp)
+ return GetXMLToken(XML_NOMATCH);
+ else
+ return "!=";
+ case SC_TOPPERC:
+ return GetXMLToken(XML_TOP_PERCENT);
+ case SC_TOPVAL:
+ return GetXMLToken(XML_TOP_VALUES);
+ default:
+ ;
+ }
+ return "=";
+ }
+
+ class WriteSetItem
+ {
+ ScXMLExport& mrExport;
+ public:
+ explicit WriteSetItem(ScXMLExport& r) : mrExport(r) {}
+ void operator() (const ScQueryEntry::Item& rItem) const
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ SvXMLElementExport aElem(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_SET_ITEM, true, true);
+ }
+ };
+
+ void writeCondition(const ScQueryEntry& rEntry, SCCOLROW nFieldStart, bool bCaseSens,
+ utl::SearchParam::SearchType eSearchType)
+ {
+ const ScQueryEntry::QueryItemsType& rItems = rEntry.GetQueryItems();
+ if (rItems.empty())
+ {
+ OSL_FAIL("Query entry has no items at all! It must have at least one!");
+ return;
+ }
+
+ if (rItems.size() == 1)
+ {
+ // Single item condition.
+ const ScQueryEntry::Item& rItem = rItems.front();
+ if (rItem.meType == ScQueryEntry::ByString)
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
+ else if (rItem.meType == ScQueryEntry::ByDate)
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
+ else if (rItem.meType == ScQueryEntry::ByTextColor
+ || rItem.meType == ScQueryEntry::ByBackgroundColor)
+ {
+ if (mrExport.getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ if (rItem.meType == ScQueryEntry::ByTextColor)
+ mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_TYPE, XML_TEXT_COLOR);
+ else
+ mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_TYPE,
+ XML_BACKGROUND_COLOR);
+ }
+
+ OUString colorValue;
+ if (rItem.maColor == COL_AUTO) // tdf#142965
+ {
+ colorValue = rItem.meType == ScQueryEntry::ByTextColor
+ ? GetXMLToken(XML_WINDOW_FONT_COLOR)
+ : GetXMLToken(XML_TRANSPARENT);
+ }
+ else
+ {
+ OUStringBuffer buffer;
+ sax::Converter::convertColor(buffer, rItem.maColor);
+ colorValue = buffer.makeStringAndClear();
+ }
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, colorValue);
+ }
+ else
+ {
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, XML_NUMBER);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ }
+
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, getOperatorXML(rEntry, eSearchType));
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(rEntry.nField - nFieldStart));
+ if (bCaseSens)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+ SvXMLElementExport aElemC(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_CONDITION, true, true);
+ }
+ else
+ {
+ // Multi-item condition.
+ assert( rItems.size() > 1 && "rItems should have more than 1 element");
+
+ // Store the 1st value for backward compatibility.
+ const ScQueryEntry::Item& rItem = rItems.front();
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_VALUE, rItem.maString.getString());
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_OPERATOR, OUString("="));
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(rEntry.nField - nFieldStart));
+ if (bCaseSens)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+ SvXMLElementExport aElemC(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_CONDITION, true, true);
+
+ std::for_each(rItems.begin(), rItems.end(), WriteSetItem(mrExport));
+ }
+ }
+
+ void writeFilter(const ScDBData& rData)
+ {
+ ScQueryParam aParam;
+ rData.GetQueryParam(aParam);
+ size_t nCount = 0;
+ for (size_t n = aParam.GetEntryCount(); nCount < n; ++nCount)
+ {
+ if (!aParam.GetEntry(nCount).bDoQuery)
+ break;
+ }
+
+ if (!nCount)
+ // No filter criteria to save. Bail out.
+ return;
+
+ if (!aParam.bInplace)
+ {
+ OUString aAddrStr;
+ ScRangeStringConverter::GetStringFromAddress(
+ aAddrStr, ScAddress(aParam.nDestCol, aParam.nDestRow, aParam.nDestTab), mpDoc, ::formula::FormulaGrammar::CONV_OOO);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aAddrStr);
+ }
+
+ ScRange aAdvSource;
+ if (rData.GetAdvancedQuerySource(aAdvSource))
+ {
+ OUString aAddrStr;
+ ScRangeStringConverter::GetStringFromRange(
+ aAddrStr, aAdvSource, mpDoc, ::formula::FormulaGrammar::CONV_OOO);
+ if (!aAddrStr.isEmpty())
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONDITION_SOURCE_RANGE_ADDRESS, aAddrStr);
+ }
+
+ if (!aParam.bDuplicate)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_DUPLICATES, XML_FALSE);
+
+ SvXMLElementExport aElemF(mrExport, XML_NAMESPACE_TABLE, XML_FILTER, true, true);
+
+ bool bAnd = false;
+ bool bOr = false;
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (rEntry.eConnect == SC_AND)
+ bAnd = true;
+ else
+ bOr = true;
+ }
+
+ // Note that export field index values are relative to the first field.
+ ScRange aRange;
+ rData.GetArea(aRange);
+ SCCOLROW nFieldStart = aParam.bByRow ? aRange.aStart.Col() : aRange.aStart.Row();
+
+ if (bOr && !bAnd)
+ {
+ SvXMLElementExport aElemOr(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_OR, true, true);
+ for (size_t i = 0; i < nCount; ++i)
+ writeCondition(aParam.GetEntry(i), nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ else if (bAnd && !bOr)
+ {
+ SvXMLElementExport aElemAnd(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_AND, true, true);
+ for (size_t i = 0; i < nCount; ++i)
+ writeCondition(aParam.GetEntry(i), nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ else if (nCount == 1)
+ {
+ writeCondition(aParam.GetEntry(0), nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ else
+ {
+ SvXMLElementExport aElemC(mrExport, XML_NAMESPACE_TABLE, XML_FILTER_OR, true, true);
+ ScQueryEntry aPrevEntry = aParam.GetEntry(0);
+ ScQueryConnect eConnect = aParam.GetEntry(1).eConnect;
+ bool bOpenAndElement = false;
+ OUString aName = mrExport.GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TABLE, GetXMLToken(XML_FILTER_AND));
+
+ if (eConnect == SC_AND)
+ {
+ mrExport.StartElement(aName, true);
+ bOpenAndElement = true;
+ }
+ else
+ bOpenAndElement = false;
+
+ for (size_t i = 1; i < nCount; ++i)
+ {
+ const ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if (eConnect != rEntry.eConnect)
+ {
+ eConnect = rEntry.eConnect;
+ if (rEntry.eConnect == SC_AND)
+ {
+ mrExport.StartElement(aName, true );
+ bOpenAndElement = true;
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ aPrevEntry = rEntry;
+ if (i == nCount - 1)
+ {
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ mrExport.EndElement(aName, true);
+ bOpenAndElement = false;
+ }
+ }
+ else
+ {
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ aPrevEntry = rEntry;
+ if (bOpenAndElement)
+ {
+ mrExport.EndElement(aName, true);
+ bOpenAndElement = false;
+ }
+ if (i == nCount - 1)
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ }
+ else
+ {
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ aPrevEntry = rEntry;
+ if (i == nCount - 1)
+ writeCondition(aPrevEntry, nFieldStart, aParam.bCaseSens, aParam.eSearchType);
+ }
+ }
+ if(bOpenAndElement)
+ mrExport.EndElement(aName, true);
+ }
+ }
+
+ void writeSubtotals(const ScDBData& rData)
+ {
+ ScSubTotalParam aParam;
+ rData.GetSubTotalParam(aParam);
+
+ size_t nCount = 0;
+ for (; nCount < MAXSUBTOTAL; ++nCount)
+ {
+ if (!aParam.bGroupActive[nCount])
+ break;
+ }
+
+ if (!nCount)
+ return;
+
+ if (!aParam.bIncludePattern)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_BIND_STYLES_TO_CONTENT, XML_FALSE);
+
+ if (aParam.bPagebreak)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_PAGE_BREAKS_ON_GROUP_CHANGE, XML_TRUE);
+
+ if (aParam.bCaseSens)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_TRUE);
+
+ SvXMLElementExport aElemSTRs(mrExport, XML_NAMESPACE_TABLE, XML_SUBTOTAL_RULES, true, true);
+
+ if (aParam.bDoSort)
+ {
+ if (!aParam.bAscending)
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ORDER, XML_DESCENDING);
+
+ if (aParam.bUserDef)
+ {
+ OUString aBuf = SC_USERLIST + OUString::number(static_cast<sal_Int32>(aParam.nUserIndex));
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DATA_TYPE, aBuf);
+ }
+ SvXMLElementExport aElemSGs(mrExport, XML_NAMESPACE_TABLE, XML_SORT_GROUPS, true, true);
+ }
+
+ for (size_t i = 0; i < MAXSUBTOTAL; ++i)
+ {
+ if (!aParam.bGroupActive[i])
+ // We're done!
+ break;
+
+ sal_Int32 nFieldCol = static_cast<sal_Int32>(aParam.nField[i]);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_GROUP_BY_FIELD_NUMBER, OUString::number(nFieldCol));
+ SvXMLElementExport aElemSTR(mrExport, XML_NAMESPACE_TABLE, XML_SUBTOTAL_RULE, true, true);
+
+ for (SCCOL j = 0, n = aParam.nSubTotals[i]; j < n; ++j)
+ {
+ sal_Int32 nCol = static_cast<sal_Int32>(aParam.pSubTotals[i][j]);
+ ScSubTotalFunc eFunc = aParam.pFunctions[i][j];
+
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FIELD_NUMBER, OUString::number(nCol));
+ OUString aFuncStr = ScXMLConverter::GetStringFromFunction(eFunc);
+ mrExport.AddAttribute(XML_NAMESPACE_TABLE, XML_FUNCTION, aFuncStr);
+
+ SvXMLElementExport aElemSTF(mrExport, XML_NAMESPACE_TABLE, XML_SUBTOTAL_FIELD, true, true);
+ }
+ }
+ }
+};
+
+}
+
+void ScXMLExportDatabaseRanges::WriteDatabaseRanges()
+{
+ pDoc = rExport.GetDocument();
+ if (!pDoc)
+ return;
+
+ // Get sheet-local anonymous ranges.
+ SCTAB nTabCount = pDoc->GetTableCount();
+ std::map<SCTAB, const ScDBData*> aSheetDBs;
+ for (SCTAB i = 0; i < nTabCount; ++i)
+ {
+ const ScDBData* p = pDoc->GetAnonymousDBData(i);
+ if (p)
+ aSheetDBs.emplace(i, p);
+ }
+
+ bool bHasRanges = !aSheetDBs.empty();
+
+ // See if we have global ranges.
+ ScDBCollection* pDBCollection = pDoc->GetDBCollection();
+ if (pDBCollection)
+ {
+ if (!pDBCollection->getNamedDBs().empty() || !pDBCollection->getAnonDBs().empty())
+ bHasRanges = true;
+ }
+
+ if (!bHasRanges)
+ // No ranges to export. Bail out.
+ return;
+
+ SvXMLElementExport aElemDRs(rExport, XML_NAMESPACE_TABLE, XML_DATABASE_RANGES, true, true);
+
+ WriteDatabaseRange func(rExport, pDoc);
+
+ if (pDBCollection)
+ {
+ // Write global named ranges.
+ func.setRangeType(ScDBCollection::GlobalNamed);
+ const ScDBCollection::NamedDBs& rNamedDBs = pDBCollection->getNamedDBs();
+ ::std::for_each(rNamedDBs.begin(), rNamedDBs.end(), func);
+
+ // Add global anonymous DB ranges.
+ func.setRangeType(ScDBCollection::GlobalAnonymous);
+ const ScDBCollection::AnonDBs& rAnonDBs = pDBCollection->getAnonDBs();
+ ::std::for_each(rAnonDBs.begin(), rAnonDBs.end(), func);
+ }
+
+ // Write sheet-local ranges.
+ func.setRangeType(ScDBCollection::SheetAnonymous);
+ ::std::for_each(aSheetDBs.begin(), aSheetDBs.end(), func);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportDatabaseRanges.hxx b/sc/source/filter/xml/XMLExportDatabaseRanges.hxx
new file mode 100644
index 000000000..ed8f79497
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportDatabaseRanges.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+
+struct ScSortParam;
+class ScXMLExport;
+class ScDocument;
+class ScMyEmptyDatabaseRangesContainer;
+
+void writeSort(ScXMLExport& mrExport, const ScSortParam& aParam, const ScRange& aRange,
+ const ScDocument* mpDoc);
+
+class ScXMLExportDatabaseRanges
+{
+ ScXMLExport& rExport;
+ ScDocument* pDoc;
+
+public:
+ explicit ScXMLExportDatabaseRanges(ScXMLExport& rExport);
+ ScMyEmptyDatabaseRangesContainer GetEmptyDatabaseRanges();
+ void WriteDatabaseRanges();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportIterator.cxx b/sc/source/filter/xml/XMLExportIterator.cxx
new file mode 100644
index 000000000..f0032b22e
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportIterator.cxx
@@ -0,0 +1,731 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XCellRange.hpp>
+
+#include "XMLExportIterator.hxx"
+#include <dociter.hxx>
+#include "xmlexprt.hxx"
+#include "XMLExportSharedData.hxx"
+#include "XMLStylesExportHelper.hxx"
+#include <document.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+ScMyIteratorBase::ScMyIteratorBase()
+{
+}
+
+ScMyIteratorBase::~ScMyIteratorBase()
+{
+}
+
+void ScMyIteratorBase::UpdateAddress( ScAddress& rCellAddress )
+{
+ ScAddress aNewAddr( rCellAddress );
+ if( GetFirstAddress( aNewAddr ) )
+ {
+ if( ( aNewAddr.Tab() == rCellAddress.Tab() ) &&
+ ( ( aNewAddr.Row() < rCellAddress.Row() ) ||
+ ( ( aNewAddr.Row() == rCellAddress.Row() ) && ( aNewAddr.Col() < rCellAddress.Col() ) ) ) )
+ rCellAddress = aNewAddr;
+ }
+}
+
+inline bool ScMyShape::operator<(const ScMyShape& aShape) const
+{
+ return aAddress.lessThanByRow( aShape.aAddress );
+}
+
+ScMyShapesContainer::ScMyShapesContainer()
+{
+}
+
+ScMyShapesContainer::~ScMyShapesContainer()
+{
+}
+
+void ScMyShapesContainer::AddNewShape( const ScMyShape& aShape )
+{
+ aShapeList.push_back(aShape);
+}
+
+bool ScMyShapesContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aShapeList.empty() )
+ {
+ rCellAddress = aShapeList.begin()->aAddress;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyShapesContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.aShapeList.clear();
+
+ ScMyShapeList::iterator aItr(aShapeList.begin());
+ ScMyShapeList::iterator aEndItr(aShapeList.end());
+ while( (aItr != aEndItr) && (aItr->aAddress == rMyCell.maCellAddress) )
+ {
+ rMyCell.aShapeList.push_back(*aItr);
+ aItr = aShapeList.erase(aItr);
+ }
+ rMyCell.bHasShape = !rMyCell.aShapeList.empty();
+}
+
+void ScMyShapesContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyShapeList::iterator aItr = std::find_if_not(aShapeList.begin(), aShapeList.end(),
+ [&nSkip](const ScMyShape& rShape) { return rShape.aAddress.Tab() == nSkip; });
+ aShapeList.erase(aShapeList.begin(), aItr);
+}
+
+void ScMyShapesContainer::Sort()
+{
+ aShapeList.sort();
+}
+
+inline bool ScMyNoteShape::operator<(const ScMyNoteShape& aNote) const
+{
+ return aPos.lessThanByRow( aNote.aPos );
+}
+
+ScMyNoteShapesContainer::ScMyNoteShapesContainer()
+{
+}
+
+ScMyNoteShapesContainer::~ScMyNoteShapesContainer()
+{
+}
+
+void ScMyNoteShapesContainer::AddNewNote( const ScMyNoteShape& aNote )
+{
+ aNoteShapeList.push_back(aNote);
+}
+
+bool ScMyNoteShapesContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable = rCellAddress.Tab();
+ if( !aNoteShapeList.empty() )
+ {
+ rCellAddress = aNoteShapeList.begin()->aPos;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyNoteShapesContainer::SetCellData( ScMyCell& rMyCell )
+{
+ ScMyNoteShapeList::iterator aItr = std::find_if_not(aNoteShapeList.begin(), aNoteShapeList.end(),
+ [&rMyCell](const ScMyNoteShape& rNoteShape) { return rNoteShape.aPos == rMyCell.maCellAddress; });
+ aNoteShapeList.erase(aNoteShapeList.begin(), aItr);
+}
+
+void ScMyNoteShapesContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyNoteShapeList::iterator aItr = std::find_if_not(aNoteShapeList.begin(), aNoteShapeList.end(),
+ [&nSkip](const ScMyNoteShape& rNoteShape) { return rNoteShape.aPos.Tab() == nSkip; });
+ aNoteShapeList.erase(aNoteShapeList.begin(), aItr);
+}
+
+void ScMyNoteShapesContainer::Sort()
+{
+ aNoteShapeList.sort();
+}
+
+inline bool ScMyMergedRange::operator<(const ScMyMergedRange& aRange) const
+{
+ return aCellRange.aStart.lessThanByRow( aRange.aCellRange.aStart );
+}
+
+ScMyMergedRangesContainer::ScMyMergedRangesContainer()
+{
+}
+
+ScMyMergedRangesContainer::~ScMyMergedRangesContainer()
+{
+}
+
+void ScMyMergedRangesContainer::AddRange(const ScRange& rMergedRange)
+{
+ SCROW nStartRow( rMergedRange.aStart.Row() );
+ SCROW nEndRow( rMergedRange.aEnd.Row() );
+
+ ScMyMergedRange aRange;
+ aRange.bIsFirst = true;
+
+ aRange.aCellRange = rMergedRange;
+
+ aRange.aCellRange.aEnd.SetRow( nStartRow );
+ aRange.nRows = nEndRow - nStartRow + 1;
+ aRangeList.push_back( aRange );
+
+ aRange.bIsFirst = false;
+ aRange.nRows = 0;
+ for( SCROW nRow = nStartRow + 1; nRow <= nEndRow; ++nRow )
+ {
+ aRange.aCellRange.aStart.SetRow( nRow );
+ aRange.aCellRange.aEnd.SetRow( nRow );
+ aRangeList.push_back(aRange);
+ }
+}
+
+bool ScMyMergedRangesContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aRangeList.empty() )
+ {
+ rCellAddress = aRangeList.begin()->aCellRange.aStart;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyMergedRangesContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.bIsMergedBase = rMyCell.bIsCovered = false;
+ ScMyMergedRangeList::iterator aItr(aRangeList.begin());
+ if( aItr == aRangeList.end() )
+ return;
+
+ if( aItr->aCellRange.aStart != rMyCell.aCellAddress )
+ return;
+
+ rMyCell.aMergeRange = aItr->aCellRange;
+ if (aItr->bIsFirst)
+ rMyCell.aMergeRange.aEnd.SetRow( rMyCell.aMergeRange.aStart.Row() + aItr->nRows - 1 );
+ rMyCell.bIsMergedBase = aItr->bIsFirst;
+ rMyCell.bIsCovered = !aItr->bIsFirst;
+ if( aItr->aCellRange.aStart.Col() < aItr->aCellRange.aEnd.Col() )
+ {
+ aItr->aCellRange.aStart.IncCol( 1 );
+ aItr->bIsFirst = false;
+ }
+ else
+ aRangeList.erase(aItr);
+}
+
+void ScMyMergedRangesContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyMergedRangeList::iterator aItr = std::find_if_not(aRangeList.begin(), aRangeList.end(),
+ [&nSkip](const ScMyMergedRange& rRange) { return rRange.aCellRange.aStart.Tab() == nSkip; });
+ aRangeList.erase(aRangeList.begin(), aItr);
+}
+
+void ScMyMergedRangesContainer::Sort()
+{
+ aRangeList.sort();
+}
+
+bool ScMyAreaLink::Compare( const ScMyAreaLink& rAreaLink ) const
+{
+ return (GetRowCount() == rAreaLink.GetRowCount()) &&
+ (sFilter == rAreaLink.sFilter) &&
+ (sFilterOptions == rAreaLink.sFilterOptions) &&
+ (sURL == rAreaLink.sURL) &&
+ (sSourceStr == rAreaLink.sSourceStr);
+}
+
+inline bool ScMyAreaLink::operator<(const ScMyAreaLink& rAreaLink ) const
+{
+ return aDestRange.aStart.lessThanByRow( rAreaLink.aDestRange.aStart );
+}
+
+ScMyAreaLinksContainer::ScMyAreaLinksContainer()
+{
+}
+
+ScMyAreaLinksContainer::~ScMyAreaLinksContainer()
+{
+}
+
+bool ScMyAreaLinksContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aAreaLinkList.empty() )
+ {
+ rCellAddress = aAreaLinkList.begin()->aDestRange.aStart;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyAreaLinksContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.bHasAreaLink = false;
+ ScMyAreaLinkList::iterator aItr(aAreaLinkList.begin());
+ if( aItr == aAreaLinkList.end() )
+ return;
+
+ if( aItr->aDestRange.aStart != rMyCell.aCellAddress )
+ return;
+
+ rMyCell.bHasAreaLink = true;
+ rMyCell.aAreaLink = *aItr;
+ aItr = aAreaLinkList.erase( aItr );
+ bool bFound = true;
+ while (aItr != aAreaLinkList.end() && bFound)
+ {
+ if ( aItr->aDestRange.aStart == rMyCell.aCellAddress )
+ {
+ OSL_FAIL("more than one linked range on one cell");
+ aItr = aAreaLinkList.erase( aItr );
+ }
+ else
+ bFound = false;
+ }
+}
+
+void ScMyAreaLinksContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyAreaLinkList::iterator aItr = std::find_if_not(aAreaLinkList.begin(), aAreaLinkList.end(),
+ [&nSkip](const ScMyAreaLink& rAreaLink) { return rAreaLink.aDestRange.aStart.Tab() == nSkip; });
+ aAreaLinkList.erase(aAreaLinkList.begin(), aItr);
+}
+
+void ScMyAreaLinksContainer::Sort()
+{
+ aAreaLinkList.sort();
+}
+
+ScMyEmptyDatabaseRangesContainer::ScMyEmptyDatabaseRangesContainer()
+{
+}
+
+ScMyEmptyDatabaseRangesContainer::~ScMyEmptyDatabaseRangesContainer()
+{
+}
+
+void ScMyEmptyDatabaseRangesContainer::AddNewEmptyDatabaseRange(const table::CellRangeAddress& aCellRange)
+{
+ SCROW nStartRow(aCellRange.StartRow);
+ SCROW nEndRow(aCellRange.EndRow);
+ ScRange aRange( aCellRange.StartColumn, aCellRange.StartRow, aCellRange.Sheet,
+ aCellRange.EndColumn, aCellRange.EndRow, aCellRange.Sheet );
+ for( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow )
+ {
+ aRange.aStart.SetRow( nRow );
+ aRange.aEnd.SetRow( nRow );
+ aDatabaseList.push_back( aRange );
+ }
+}
+
+bool ScMyEmptyDatabaseRangesContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aDatabaseList.empty() )
+ {
+ rCellAddress = aDatabaseList.begin()->aStart;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyEmptyDatabaseRangesContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.bHasEmptyDatabase = false;
+ ScMyEmptyDatabaseRangeList::iterator aItr(aDatabaseList.begin());
+ if( aItr != aDatabaseList.end() )
+ {
+ if( aItr->aStart == rMyCell.aCellAddress )
+ {
+ rMyCell.bHasEmptyDatabase = true;
+ if( aItr->aStart.Col() < aItr->aEnd.Col() )
+ aItr->aStart.SetCol( aItr->aStart.Col() + 1 );
+ else
+ aDatabaseList.erase(aItr);
+ }
+ }
+}
+
+void ScMyEmptyDatabaseRangesContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyEmptyDatabaseRangeList::iterator aItr = std::find_if_not(aDatabaseList.begin(), aDatabaseList.end(),
+ [&nSkip](const ScRange& rDatabase) { return rDatabase.aStart.Tab() == nSkip; });
+ aDatabaseList.erase(aDatabaseList.begin(), aItr);
+}
+
+void ScMyEmptyDatabaseRangesContainer::Sort()
+{
+ aDatabaseList.sort();
+}
+
+inline bool ScMyDetectiveObj::operator<( const ScMyDetectiveObj& rDetObj) const
+{
+ return aPosition.lessThanByRow( rDetObj.aPosition );
+}
+
+ScMyDetectiveObjContainer::ScMyDetectiveObjContainer()
+{
+}
+
+ScMyDetectiveObjContainer::~ScMyDetectiveObjContainer()
+{
+}
+
+void ScMyDetectiveObjContainer::AddObject( ScDetectiveObjType eObjType, const SCTAB nSheet,
+ const ScAddress& rPosition, const ScRange& rSourceRange,
+ bool bHasError )
+{
+ if( !((eObjType == SC_DETOBJ_ARROW) ||
+ (eObjType == SC_DETOBJ_FROMOTHERTAB) ||
+ (eObjType == SC_DETOBJ_TOOTHERTAB) ||
+ (eObjType == SC_DETOBJ_CIRCLE)) )
+ return;
+
+ ScMyDetectiveObj aDetObj;
+ aDetObj.eObjType = eObjType;
+ if( eObjType == SC_DETOBJ_TOOTHERTAB )
+ aDetObj.aPosition = rSourceRange.aStart;
+ else
+ aDetObj.aPosition = rPosition;
+ aDetObj.aSourceRange = rSourceRange;
+
+ // #111064#; take the sheet where the object is found and not the sheet given in the ranges, because they are not always true
+ if (eObjType != SC_DETOBJ_FROMOTHERTAB)
+ {
+ // if the ObjType == SC_DETOBJ_FROMOTHERTAB then the SourceRange is not used and so it has not to be tested and changed
+ OSL_ENSURE(aDetObj.aPosition.Tab() == aDetObj.aSourceRange.aStart.Tab(), "It seems to be possible to have different sheets");
+ aDetObj.aSourceRange.aStart.SetTab( nSheet );
+ aDetObj.aSourceRange.aEnd.SetTab( nSheet );
+ }
+ aDetObj.aPosition.SetTab( nSheet );
+
+ aDetObj.bHasError = bHasError;
+ aDetectiveObjList.push_back( aDetObj );
+}
+
+bool ScMyDetectiveObjContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aDetectiveObjList.empty() )
+ {
+ rCellAddress = aDetectiveObjList.begin()->aPosition;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyDetectiveObjContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.aDetectiveObjVec.clear();
+ ScMyDetectiveObjList::iterator aItr(aDetectiveObjList.begin());
+ ScMyDetectiveObjList::iterator aEndItr(aDetectiveObjList.end());
+ while( (aItr != aEndItr) && (aItr->aPosition == rMyCell.aCellAddress) )
+ {
+ rMyCell.aDetectiveObjVec.push_back( *aItr );
+ aItr = aDetectiveObjList.erase( aItr );
+ }
+ rMyCell.bHasDetectiveObj = (!rMyCell.aDetectiveObjVec.empty());
+}
+
+void ScMyDetectiveObjContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyDetectiveObjList::iterator aItr = std::find_if_not(aDetectiveObjList.begin(), aDetectiveObjList.end(),
+ [&nSkip](const ScMyDetectiveObj& rDetectiveObj) { return rDetectiveObj.aPosition.Tab() == nSkip; });
+ aDetectiveObjList.erase(aDetectiveObjList.begin(), aItr);
+}
+
+void ScMyDetectiveObjContainer::Sort()
+{
+ aDetectiveObjList.sort();
+}
+
+inline bool ScMyDetectiveOp::operator<( const ScMyDetectiveOp& rDetOp) const
+{
+ return aPosition.lessThanByRow( rDetOp.aPosition );
+}
+
+ScMyDetectiveOpContainer::ScMyDetectiveOpContainer()
+{
+}
+
+ScMyDetectiveOpContainer::~ScMyDetectiveOpContainer()
+{
+}
+
+void ScMyDetectiveOpContainer::AddOperation( ScDetOpType eOpType, const ScAddress& rPosition, sal_uInt32 nIndex )
+{
+ ScMyDetectiveOp aDetOp;
+ aDetOp.eOpType = eOpType;
+ aDetOp.aPosition = rPosition;
+ aDetOp.nIndex = nIndex;
+ aDetectiveOpList.push_back( aDetOp );
+}
+
+bool ScMyDetectiveOpContainer::GetFirstAddress( ScAddress& rCellAddress )
+{
+ SCTAB nTable( rCellAddress.Tab() );
+ if( !aDetectiveOpList.empty() )
+ {
+ rCellAddress = aDetectiveOpList.begin()->aPosition;
+ return ( nTable == rCellAddress.Tab() );
+ }
+ return false;
+}
+
+void ScMyDetectiveOpContainer::SetCellData( ScMyCell& rMyCell )
+{
+ rMyCell.aDetectiveOpVec.clear();
+ ScMyDetectiveOpList::iterator aItr(aDetectiveOpList.begin());
+ ScMyDetectiveOpList::iterator aEndItr(aDetectiveOpList.end());
+ while( (aItr != aEndItr) && (aItr->aPosition == rMyCell.aCellAddress) )
+ {
+ rMyCell.aDetectiveOpVec.push_back( *aItr );
+ aItr = aDetectiveOpList.erase( aItr );
+ }
+ rMyCell.bHasDetectiveOp = (!rMyCell.aDetectiveOpVec.empty());
+}
+
+void ScMyDetectiveOpContainer::SkipTable(SCTAB nSkip)
+{
+ ScMyDetectiveOpList::iterator aItr = std::find_if_not(aDetectiveOpList.begin(), aDetectiveOpList.end(),
+ [&nSkip](const ScMyDetectiveOp& rDetectiveOp) { return rDetectiveOp.aPosition.Tab() == nSkip; });
+ aDetectiveOpList.erase(aDetectiveOpList.begin(), aItr);
+}
+
+void ScMyDetectiveOpContainer::Sort()
+{
+ aDetectiveOpList.sort();
+}
+
+ScMyCell::ScMyCell() :
+ pNote(nullptr),
+ nValidationIndex(-1),
+ nStyleIndex(-1),
+ nNumberFormat(-1),
+ nType(table::CellContentType_EMPTY),
+ bIsAutoStyle( false ),
+ bHasShape( false ),
+ bIsMergedBase( false ),
+ bIsCovered( false ),
+ bHasAreaLink( false ),
+ bHasEmptyDatabase( false ),
+ bHasDetectiveObj( false ),
+ bHasDetectiveOp( false ),
+ bIsMatrixBase( false ),
+ bIsMatrixCovered( false ),
+ bHasAnnotation( false )
+{
+}
+
+ScMyNotEmptyCellsIterator::ScMyNotEmptyCellsIterator(ScXMLExport& rTempXMLExport)
+ : pShapes(nullptr),
+ pNoteShapes(nullptr),
+ pEmptyDatabaseRanges(nullptr),
+ pMergedRanges(nullptr),
+ pAreaLinks(nullptr),
+ pDetectiveObj(nullptr),
+ pDetectiveOp(nullptr),
+ rExport(rTempXMLExport),
+ nCellCol(0),
+ nCellRow(0),
+ nCurrentTable(SCTAB_MAX)
+{
+}
+
+ScMyNotEmptyCellsIterator::~ScMyNotEmptyCellsIterator()
+{
+ Clear();
+}
+
+void ScMyNotEmptyCellsIterator::Clear()
+{
+ mpCellItr.reset();
+ pShapes = nullptr;
+ pNoteShapes = nullptr;
+ pMergedRanges = nullptr;
+ pAreaLinks = nullptr;
+ pEmptyDatabaseRanges = nullptr;
+ pDetectiveObj = nullptr;
+ pDetectiveOp = nullptr;
+ nCurrentTable = SCTAB_MAX;
+}
+
+void ScMyNotEmptyCellsIterator::UpdateAddress( ScAddress& rAddress )
+{
+ if (mpCellItr->GetPos(nCellCol, nCellRow))
+ {
+ rAddress.SetCol( nCellCol );
+ rAddress.SetRow( nCellRow );
+ }
+}
+
+void ScMyNotEmptyCellsIterator::SetCellData( ScMyCell& rMyCell, const ScAddress& rAddress )
+{
+ rMyCell.maBaseCell.clear();
+ rMyCell.aCellAddress = rAddress;
+ rMyCell.maCellAddress = rMyCell.aCellAddress;
+
+ if( ( nCellCol == rAddress.Col() ) && ( nCellRow == rAddress.Row() ) )
+ {
+ const ScRefCellValue* pCell = mpCellItr->GetNext(nCellCol, nCellRow);
+ if (pCell)
+ rMyCell.maBaseCell = *pCell;
+ }
+
+ rMyCell.bIsMatrixCovered = false;
+ rMyCell.bIsMatrixBase = false;
+
+ switch (rMyCell.maBaseCell.meType)
+ {
+ case CELLTYPE_VALUE:
+ rMyCell.nType = table::CellContentType_VALUE;
+ break;
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ rMyCell.nType = table::CellContentType_TEXT;
+ break;
+ case CELLTYPE_FORMULA:
+ rMyCell.nType = table::CellContentType_FORMULA;
+ break;
+ default:
+ rMyCell.nType = table::CellContentType_EMPTY;
+ }
+
+ if (rMyCell.maBaseCell.meType == CELLTYPE_FORMULA)
+ {
+ bool bIsMatrixBase = false;
+ if (rExport.IsMatrix(rMyCell.maCellAddress, rMyCell.aMatrixRange, bIsMatrixBase))
+ {
+ rMyCell.bIsMatrixBase = bIsMatrixBase;
+ rMyCell.bIsMatrixCovered = !bIsMatrixBase;
+ }
+ }
+}
+
+void ScMyNotEmptyCellsIterator::HasAnnotation(ScMyCell& aCell)
+{
+ aCell.bHasAnnotation = false;
+ ScPostIt* pNote = rExport.GetDocument()->GetNote(aCell.maCellAddress);
+
+ if(pNote)
+ {
+ aCell.bHasAnnotation = true;
+ aCell.pNote = pNote;
+ }
+}
+
+void ScMyNotEmptyCellsIterator::SetCurrentTable(const SCTAB nTable,
+ const uno::Reference<sheet::XSpreadsheet>& rxTable)
+{
+ aLastAddress.SetRow( 0 );
+ aLastAddress.SetCol( 0 );
+ aLastAddress.SetTab( nTable );
+ if (nCurrentTable == nTable)
+ return;
+
+ nCurrentTable = nTable;
+
+ mpCellItr.reset(
+ new ScHorizontalCellIterator(
+ *rExport.GetDocument(), nCurrentTable, 0, 0,
+ static_cast<SCCOL>(rExport.GetSharedData()->GetLastColumn(nCurrentTable)),
+ static_cast<SCROW>(rExport.GetSharedData()->GetLastRow(nCurrentTable))));
+
+ xTable.set(rxTable);
+ xCellRange.set(xTable);
+}
+
+void ScMyNotEmptyCellsIterator::SkipTable(SCTAB nSkip)
+{
+ // Skip entries for a sheet that is copied instead of saving normally.
+ // Cells are handled separately in SetCurrentTable.
+
+ if( pShapes )
+ pShapes->SkipTable(nSkip);
+ if( pNoteShapes )
+ pNoteShapes->SkipTable(nSkip);
+ if( pEmptyDatabaseRanges )
+ pEmptyDatabaseRanges->SkipTable(nSkip);
+ if( pMergedRanges )
+ pMergedRanges->SkipTable(nSkip);
+ if( pAreaLinks )
+ pAreaLinks->SkipTable(nSkip);
+ if( pDetectiveObj )
+ pDetectiveObj->SkipTable(nSkip);
+ if( pDetectiveOp )
+ pDetectiveOp->SkipTable(nSkip);
+}
+
+bool ScMyNotEmptyCellsIterator::GetNext(ScMyCell& aCell, ScFormatRangeStyles* pCellStyles)
+{
+ ScDocument* pDoc = rExport.GetDocument();
+ ScAddress aAddress( pDoc->MaxCol() + 1, pDoc->MaxRow() + 1, nCurrentTable );
+
+ UpdateAddress( aAddress );
+
+ if( pShapes )
+ pShapes->UpdateAddress( aAddress );
+ if( pNoteShapes )
+ pNoteShapes->UpdateAddress( aAddress );
+ if( pEmptyDatabaseRanges )
+ pEmptyDatabaseRanges->UpdateAddress( aAddress );
+ if( pMergedRanges )
+ pMergedRanges->UpdateAddress( aAddress );
+ if( pAreaLinks )
+ pAreaLinks->UpdateAddress( aAddress );
+ if( pDetectiveObj )
+ pDetectiveObj->UpdateAddress( aAddress );
+ if( pDetectiveOp )
+ pDetectiveOp->UpdateAddress( aAddress );
+
+ bool bFoundCell( ( aAddress.Col() <= pDoc->MaxCol() ) && ( aAddress.Row() <= pDoc->MaxRow() + 1 ) );
+ if( bFoundCell )
+ {
+ SetCellData( aCell, aAddress );
+ if( pShapes )
+ pShapes->SetCellData( aCell );
+ if( pNoteShapes )
+ pNoteShapes->SetCellData( aCell );
+ if( pEmptyDatabaseRanges )
+ pEmptyDatabaseRanges->SetCellData( aCell );
+ if( pMergedRanges )
+ pMergedRanges->SetCellData( aCell );
+ if( pAreaLinks )
+ pAreaLinks->SetCellData( aCell );
+ if( pDetectiveObj )
+ pDetectiveObj->SetCellData( aCell );
+ if( pDetectiveOp )
+ pDetectiveOp->SetCellData( aCell );
+
+ HasAnnotation( aCell );
+ bool bIsAutoStyle;
+ // Ranges before the previous cell are not needed by ExportFormatRanges anymore and can be removed
+ SCROW nRemoveBeforeRow = aLastAddress.Row();
+ aCell.nStyleIndex = pCellStyles->GetStyleNameIndex(aCell.maCellAddress.Tab(),
+ aCell.maCellAddress.Col(), aCell.maCellAddress.Row(),
+ bIsAutoStyle, aCell.nValidationIndex, aCell.nNumberFormat, nRemoveBeforeRow);
+ aLastAddress = aCell.aCellAddress;
+ aCell.bIsAutoStyle = bIsAutoStyle;
+
+ //#102799#; if the cell is in a DatabaseRange which should saved empty, the cell should have the type empty
+ if (aCell.bHasEmptyDatabase)
+ aCell.nType = table::CellContentType_EMPTY;
+ }
+ return bFoundCell;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportIterator.hxx b/sc/source/filter/xml/XMLExportIterator.hxx
new file mode 100644
index 000000000..33a0dfa88
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportIterator.hxx
@@ -0,0 +1,374 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <list>
+#include <com/sun/star/table/CellContentType.hpp>
+#include <detfunc.hxx>
+#include <detdata.hxx>
+#include <cellvalue.hxx>
+
+#include <memory>
+
+namespace com::sun::star::drawing { class XShape; }
+namespace com::sun::star::sheet { class XSpreadsheet; }
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::table { struct CellRangeAddress; }
+
+class ScPostIt;
+class ScHorizontalCellIterator;
+struct ScMyCell;
+class ScXMLExport;
+class ScFormatRangeStyles;
+
+class ScMyIteratorBase
+{
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) = 0;
+
+public:
+ ScMyIteratorBase();
+ virtual ~ScMyIteratorBase();
+
+ ScMyIteratorBase(ScMyIteratorBase const &) = default;
+ ScMyIteratorBase(ScMyIteratorBase &&) = default;
+ ScMyIteratorBase & operator =(ScMyIteratorBase const &) = default;
+ ScMyIteratorBase & operator =(ScMyIteratorBase &&) = default;
+
+ virtual void SetCellData( ScMyCell& rMyCell ) = 0;
+ virtual void Sort() = 0;
+
+ void UpdateAddress( ScAddress& rCellAddress );
+};
+
+struct ScMyShape
+{
+ ScAddress aAddress;
+ ScAddress aEndAddress;
+ sal_Int32 nEndX;
+ sal_Int32 nEndY;
+ bool bResizeWithCell;
+ css::uno::Reference<css::drawing::XShape> xShape;
+
+ bool operator<(const ScMyShape& aShape) const;
+};
+
+typedef std::list<ScMyShape> ScMyShapeList;
+
+class ScMyShapesContainer : public ScMyIteratorBase
+{
+private:
+ ScMyShapeList aShapeList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyShapesContainer();
+ virtual ~ScMyShapesContainer() override;
+
+ using ScMyIteratorBase::UpdateAddress;
+ void AddNewShape(const ScMyShape& aShape);
+ bool HasShapes() const { return !aShapeList.empty(); }
+ const ScMyShapeList& GetShapes() const { return aShapeList; }
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyNoteShape
+{
+ css::uno::Reference<css::drawing::XShape> xShape;
+ ScAddress aPos;
+
+ bool operator<(const ScMyNoteShape& aNote) const;
+};
+
+typedef std::list<ScMyNoteShape> ScMyNoteShapeList;
+
+class ScMyNoteShapesContainer : public ScMyIteratorBase
+{
+private:
+ ScMyNoteShapeList aNoteShapeList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyNoteShapesContainer();
+ virtual ~ScMyNoteShapesContainer() override;
+
+ using ScMyIteratorBase::UpdateAddress;
+ void AddNewNote(const ScMyNoteShape& aNote);
+ const ScMyNoteShapeList& GetNotes() const { return aNoteShapeList; }
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyMergedRange
+{
+ ScRange aCellRange;
+ sal_Int32 nRows;
+ bool bIsFirst;
+ bool operator<(const ScMyMergedRange& aRange) const;
+};
+
+typedef std::list<ScMyMergedRange> ScMyMergedRangeList;
+
+class ScMyMergedRangesContainer : public ScMyIteratorBase
+{
+private:
+ ScMyMergedRangeList aRangeList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyMergedRangesContainer();
+ virtual ~ScMyMergedRangesContainer() override;
+ void AddRange(const ScRange& rMergedRange);
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override; // + remove doublets
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyAreaLink
+{
+ OUString sFilter;
+ OUString sFilterOptions;
+ OUString sURL;
+ OUString sSourceStr;
+ ScRange aDestRange;
+ sal_Int32 nRefreshDelaySeconds;
+
+ ScMyAreaLink() : nRefreshDelaySeconds( 0 ) {}
+
+ sal_Int32 GetColCount() const { return aDestRange.aEnd.Col() - aDestRange.aStart.Col() + 1; }
+ sal_Int32 GetRowCount() const { return aDestRange.aEnd.Row() - aDestRange.aStart.Row() + 1; }
+
+ bool Compare( const ScMyAreaLink& rAreaLink ) const;
+ bool operator<(const ScMyAreaLink& rAreaLink ) const;
+};
+
+typedef ::std::list< ScMyAreaLink > ScMyAreaLinkList;
+
+class ScMyAreaLinksContainer : public ScMyIteratorBase
+{
+private:
+ ScMyAreaLinkList aAreaLinkList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyAreaLinksContainer();
+ virtual ~ScMyAreaLinksContainer() override;
+
+ void AddNewAreaLink( const ScMyAreaLink& rAreaLink )
+ { aAreaLinkList.push_back( rAreaLink ); }
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+typedef std::list<ScRange> ScMyEmptyDatabaseRangeList;
+
+class ScMyEmptyDatabaseRangesContainer : public ScMyIteratorBase
+{
+private:
+ ScMyEmptyDatabaseRangeList aDatabaseList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyEmptyDatabaseRangesContainer();
+ virtual ~ScMyEmptyDatabaseRangesContainer() override;
+
+ ScMyEmptyDatabaseRangesContainer(ScMyEmptyDatabaseRangesContainer const &) = default;
+ ScMyEmptyDatabaseRangesContainer(ScMyEmptyDatabaseRangesContainer &&) = default;
+ ScMyEmptyDatabaseRangesContainer & operator =(ScMyEmptyDatabaseRangesContainer const &) = default;
+ ScMyEmptyDatabaseRangesContainer & operator =(ScMyEmptyDatabaseRangesContainer &&) = default;
+
+ void AddNewEmptyDatabaseRange(const css::table::CellRangeAddress& aCellRangeAddress);
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyDetectiveObj
+{
+ ScAddress aPosition;
+ ScRange aSourceRange;
+ ScDetectiveObjType eObjType;
+ bool bHasError;
+ bool operator<(const ScMyDetectiveObj& rDetObj) const;
+};
+
+typedef ::std::list< ScMyDetectiveObj > ScMyDetectiveObjList;
+typedef ::std::vector< ScMyDetectiveObj > ScMyDetectiveObjVec;
+
+class ScMyDetectiveObjContainer : public ScMyIteratorBase
+{
+private:
+ ScMyDetectiveObjList aDetectiveObjList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyDetectiveObjContainer();
+ virtual ~ScMyDetectiveObjContainer() override;
+
+ void AddObject(
+ ScDetectiveObjType eObjType,
+ const SCTAB nSheet,
+ const ScAddress& rPosition,
+ const ScRange& rSourceRange,
+ bool bHasError );
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+struct ScMyDetectiveOp
+{
+ ScAddress aPosition;
+ ScDetOpType eOpType;
+ sal_Int32 nIndex;
+ bool operator<(const ScMyDetectiveOp& rDetOp) const;
+};
+
+typedef ::std::list< ScMyDetectiveOp > ScMyDetectiveOpList;
+typedef ::std::vector< ScMyDetectiveOp > ScMyDetectiveOpVec;
+
+class ScMyDetectiveOpContainer : public ScMyIteratorBase
+{
+private:
+ ScMyDetectiveOpList aDetectiveOpList;
+protected:
+ virtual bool GetFirstAddress( ScAddress& rCellAddress ) override;
+public:
+ ScMyDetectiveOpContainer();
+ virtual ~ScMyDetectiveOpContainer() override;
+
+ void AddOperation( ScDetOpType eOpType, const ScAddress& rPosition, sal_uInt32 nIndex );
+
+ using ScMyIteratorBase::UpdateAddress;
+ virtual void SetCellData( ScMyCell& rMyCell ) override;
+ virtual void Sort() override;
+ void SkipTable(SCTAB nSkip);
+};
+
+// contains data to export for the current cell position
+struct ScMyCell
+{
+ ScAddress maCellAddress; /// Use this instead of the UNO one.
+
+ ScAddress aCellAddress;
+ ScRange aMergeRange;
+ ScRange aMatrixRange;
+
+ ScMyAreaLink aAreaLink;
+ ScMyShapeList aShapeList;
+ ScMyDetectiveObjVec aDetectiveObjVec;
+ ScMyDetectiveOpVec aDetectiveOpVec;
+
+ ScPostIt* pNote;
+
+ sal_Int32 nValidationIndex;
+ sal_Int32 nStyleIndex;
+ sal_Int32 nNumberFormat;
+ css::table::CellContentType nType;
+
+ ScRefCellValue maBaseCell;
+
+ bool bIsAutoStyle;
+
+ bool bHasShape;
+ bool bIsMergedBase;
+ bool bIsCovered;
+ bool bHasAreaLink;
+ bool bHasEmptyDatabase;
+ bool bHasDetectiveObj;
+ bool bHasDetectiveOp;
+
+ bool bIsMatrixBase;
+ bool bIsMatrixCovered;
+ bool bHasAnnotation;
+
+ ScMyCell();
+};
+
+class ScMyNotEmptyCellsIterator
+{
+ ScMyNotEmptyCellsIterator(const ScMyNotEmptyCellsIterator&) = delete;
+ const ScMyNotEmptyCellsIterator& operator=(const ScMyNotEmptyCellsIterator&) = delete;
+
+ css::uno::Reference<css::sheet::XSpreadsheet> xTable;
+ css::uno::Reference<css::table::XCellRange> xCellRange;
+ ScAddress aLastAddress;
+
+ ScMyShapesContainer* pShapes;
+ ScMyNoteShapesContainer* pNoteShapes;
+ ScMyEmptyDatabaseRangesContainer* pEmptyDatabaseRanges;
+ ScMyMergedRangesContainer* pMergedRanges;
+ ScMyAreaLinksContainer* pAreaLinks;
+ ScMyDetectiveObjContainer* pDetectiveObj;
+ ScMyDetectiveOpContainer* pDetectiveOp;
+
+ ScXMLExport& rExport;
+ std::unique_ptr<ScHorizontalCellIterator> mpCellItr;
+
+ SCCOL nCellCol;
+ SCROW nCellRow;
+ SCTAB nCurrentTable;
+
+ void UpdateAddress( ScAddress& rAddress );
+ void SetCellData( ScMyCell& rMyCell, const ScAddress& rAddress );
+
+ void HasAnnotation( ScMyCell& aCell );
+public:
+ explicit ScMyNotEmptyCellsIterator(ScXMLExport& rExport);
+ ~ScMyNotEmptyCellsIterator();
+
+ void Clear();
+
+ void SetShapes(ScMyShapesContainer* pNewShapes)
+ { pShapes = pNewShapes; }
+ void SetNoteShapes(ScMyNoteShapesContainer* pNewNoteShapes)
+ { pNoteShapes = pNewNoteShapes; }
+ void SetEmptyDatabaseRanges(ScMyEmptyDatabaseRangesContainer* pNewEmptyDatabaseRanges)
+ { pEmptyDatabaseRanges = pNewEmptyDatabaseRanges; }
+ void SetMergedRanges(ScMyMergedRangesContainer* pNewMergedRanges)
+ { pMergedRanges = pNewMergedRanges; }
+ void SetAreaLinks(ScMyAreaLinksContainer* pNewAreaLinks)
+ { pAreaLinks = pNewAreaLinks; }
+ void SetDetectiveObj(ScMyDetectiveObjContainer* pNewDetectiveObj)
+ { pDetectiveObj = pNewDetectiveObj; }
+ void SetDetectiveOp(ScMyDetectiveOpContainer* pNewDetectiveOp)
+ { pDetectiveOp = pNewDetectiveOp; }
+
+ void SetCurrentTable(const SCTAB nTable,
+ const css::uno::Reference<css::sheet::XSpreadsheet>& rxTable);
+ void SkipTable(SCTAB nSkip);
+
+ bool GetNext(ScMyCell& aCell, ScFormatRangeStyles* pCellStyles);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportSharedData.cxx b/sc/source/filter/xml/XMLExportSharedData.cxx
new file mode 100644
index 000000000..3672edab9
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportSharedData.cxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLExportSharedData.hxx"
+#include "XMLExportIterator.hxx"
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+ScMySharedData::ScMySharedData(const sal_Int32 nTempTableCount) :
+ nLastColumns(nTempTableCount, 0),
+ nLastRows(nTempTableCount, 0),
+ pDetectiveObjContainer(new ScMyDetectiveObjContainer()),
+ nTableCount(nTempTableCount)
+{
+}
+
+ScMySharedData::~ScMySharedData()
+{
+ pShapesContainer.reset();
+ pTableShapes.reset();
+ pDrawPages.reset();
+ pDetectiveObjContainer.reset();
+ pNoteShapes.reset();
+}
+
+void ScMySharedData::SetLastColumn(const sal_Int32 nTable, const sal_Int32 nCol)
+{
+ if(nCol > nLastColumns[nTable]) nLastColumns[nTable] = nCol;
+}
+
+sal_Int32 ScMySharedData::GetLastColumn(const sal_Int32 nTable) const
+{
+ return nLastColumns[nTable];
+}
+
+void ScMySharedData::SetLastRow(const sal_Int32 nTable, const sal_Int32 nRow)
+{
+ if(nRow > nLastRows[nTable]) nLastRows[nTable] = nRow;
+}
+
+sal_Int32 ScMySharedData::GetLastRow(const sal_Int32 nTable) const
+{
+ return nLastRows[nTable];
+}
+
+void ScMySharedData::AddDrawPage(const ScMyDrawPage& aDrawPage, const sal_Int32 nTable)
+{
+ if (!pDrawPages)
+ pDrawPages.reset(new ScMyDrawPages(nTableCount, ScMyDrawPage()));
+ (*pDrawPages)[nTable] = aDrawPage;
+}
+
+void ScMySharedData::SetDrawPageHasForms(const sal_Int32 nTable, bool bHasForms)
+{
+ OSL_ENSURE(pDrawPages, "DrawPages not collected");
+ if (pDrawPages)
+ (*pDrawPages)[nTable].bHasForms = bHasForms;
+}
+
+uno::Reference<drawing::XDrawPage> ScMySharedData::GetDrawPage(const sal_Int32 nTable)
+{
+ OSL_ENSURE(pDrawPages, "DrawPages not collected");
+ if (pDrawPages)
+ return (*pDrawPages)[nTable].xDrawPage;
+ else
+ return uno::Reference<drawing::XDrawPage>();
+}
+
+bool ScMySharedData::HasForm(const sal_Int32 nTable, uno::Reference<drawing::XDrawPage>& xDrawPage)
+{
+ bool bResult(false);
+ if (pDrawPages)
+ {
+ if ((*pDrawPages)[nTable].bHasForms)
+ {
+ bResult = true;
+ xDrawPage = (*pDrawPages)[nTable].xDrawPage;
+ }
+ }
+ return bResult;
+}
+
+void ScMySharedData::AddNewShape(const ScMyShape& aMyShape)
+{
+ if (!pShapesContainer)
+ pShapesContainer.reset(new ScMyShapesContainer());
+ pShapesContainer->AddNewShape(aMyShape);
+}
+
+void ScMySharedData::SortShapesContainer()
+{
+ if (pShapesContainer)
+ pShapesContainer->Sort();
+}
+
+bool ScMySharedData::HasShapes() const
+{
+ return ((pShapesContainer && pShapesContainer->HasShapes()) ||
+ (pTableShapes && !pTableShapes->empty()));
+}
+
+void ScMySharedData::AddTableShape(const sal_Int32 nTable, const uno::Reference<drawing::XShape>& xShape)
+{
+ if (!pTableShapes)
+ pTableShapes.reset(new ScMyTableShapes(nTableCount));
+ (*pTableShapes)[nTable].push_back(xShape);
+}
+
+void ScMySharedData::AddNoteObj(const uno::Reference<drawing::XShape>& xShape, const ScAddress& rPos)
+{
+ if (!pNoteShapes)
+ pNoteShapes.reset( new ScMyNoteShapesContainer() );
+ ScMyNoteShape aNote;
+ aNote.xShape = xShape;
+ aNote.aPos = rPos;
+ pNoteShapes->AddNewNote(aNote);
+}
+
+void ScMySharedData::SortNoteShapes()
+{
+ if (pNoteShapes)
+ pNoteShapes->Sort();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLExportSharedData.hxx b/sc/source/filter/xml/XMLExportSharedData.hxx
new file mode 100644
index 000000000..3ed25d251
--- /dev/null
+++ b/sc/source/filter/xml/XMLExportSharedData.hxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <address.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <vector>
+#include <list>
+#include <memory>
+
+namespace com::sun::star::drawing { class XDrawPage; }
+namespace com::sun::star::drawing { class XShape; }
+
+struct ScMyDrawPage
+{
+ css::uno::Reference<css::drawing::XDrawPage> xDrawPage;
+ bool bHasForms;
+
+ ScMyDrawPage() : bHasForms(false) {}
+};
+
+typedef std::list< css::uno::Reference<css::drawing::XShape> > ScMyTableXShapes;
+typedef std::vector<ScMyTableXShapes> ScMyTableShapes;
+typedef std::vector<ScMyDrawPage> ScMyDrawPages;
+
+class ScMyShapesContainer;
+class ScMyDetectiveObjContainer;
+struct ScMyShape;
+class ScMyNoteShapesContainer;
+
+class ScMySharedData
+{
+ std::vector<sal_Int32> nLastColumns;
+ std::vector<sal_Int32> nLastRows;
+ std::unique_ptr<ScMyTableShapes> pTableShapes;
+ std::unique_ptr<ScMyDrawPages> pDrawPages;
+ std::unique_ptr<ScMyShapesContainer> pShapesContainer;
+ std::unique_ptr<ScMyDetectiveObjContainer> pDetectiveObjContainer;
+ std::unique_ptr<ScMyNoteShapesContainer> pNoteShapes;
+ sal_Int32 nTableCount;
+public:
+ explicit ScMySharedData(const sal_Int32 nTableCount);
+ ~ScMySharedData();
+
+ void SetLastColumn(const sal_Int32 nTable, const sal_Int32 nCol);
+ void SetLastRow(const sal_Int32 nTable, const sal_Int32 nRow);
+ sal_Int32 GetLastColumn(const sal_Int32 nTable) const;
+ sal_Int32 GetLastRow(const sal_Int32 nTable) const;
+ void AddDrawPage(const ScMyDrawPage& aDrawPage, const sal_Int32 nTable);
+ void SetDrawPageHasForms(const sal_Int32 nTable, bool bHasForms);
+ css::uno::Reference<css::drawing::XDrawPage> GetDrawPage(const sal_Int32 nTable);
+ bool HasDrawPage() const { return pDrawPages != nullptr; }
+ bool HasForm(const sal_Int32 nTable, css::uno::Reference<css::drawing::XDrawPage>& xDrawPage);
+ void AddNewShape(const ScMyShape& aMyShape);
+ void SortShapesContainer();
+ ScMyShapesContainer* GetShapesContainer() { return pShapesContainer.get(); }
+ bool HasShapes() const;
+ void AddTableShape(const sal_Int32 nTable, const css::uno::Reference<css::drawing::XShape>& xShape);
+ ScMyTableShapes* GetTableShapes() { return pTableShapes.get(); }
+ ScMyDetectiveObjContainer* GetDetectiveObjContainer() { return pDetectiveObjContainer.get(); }
+ void AddNoteObj(const css::uno::Reference<css::drawing::XShape>& xShape, const ScAddress& rPos);
+ void SortNoteShapes();
+ ScMyNoteShapesContainer* GetNoteShapes() { return pNoteShapes.get(); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLStylesExportHelper.cxx b/sc/source/filter/xml/XMLStylesExportHelper.cxx
new file mode 100644
index 000000000..81c30e424
--- /dev/null
+++ b/sc/source/filter/xml/XMLStylesExportHelper.cxx
@@ -0,0 +1,1066 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLStylesExportHelper.hxx"
+#include <tools/lineend.hxx>
+#include <unonames.hxx>
+#include "xmlexprt.hxx"
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/XMLEventExport.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XSheetCondition.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <comphelper/extract.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <sfx2/app.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScMyValidation::ScMyValidation()
+ : aAlertStyle(sheet::ValidationAlertStyle_STOP),
+ aValidationType(sheet::ValidationType_ANY),
+ aOperator(sheet::ConditionOperator_NONE),
+ nShowList(0),
+ bShowErrorMessage(false),
+ bShowInputMessage(false),
+ bIgnoreBlanks(false)
+{
+}
+
+bool ScMyValidation::IsEqual(const ScMyValidation& aVal) const
+{
+ return aVal.bIgnoreBlanks == bIgnoreBlanks &&
+ aVal.bShowInputMessage == bShowInputMessage &&
+ aVal.bShowErrorMessage == bShowErrorMessage &&
+ aVal.aBaseCell == aBaseCell &&
+ aVal.aAlertStyle == aAlertStyle &&
+ aVal.aValidationType == aValidationType &&
+ aVal.aOperator == aOperator &&
+ aVal.sErrorTitle == sErrorTitle &&
+ aVal.sInputTitle == sInputTitle &&
+ aVal.sErrorMessage == sErrorMessage &&
+ aVal.sInputMessage == sInputMessage &&
+ aVal.sFormula1 == sFormula1 &&
+ aVal.sFormula2 == sFormula2;
+}
+
+ScMyValidationsContainer::ScMyValidationsContainer()
+{
+}
+
+ScMyValidationsContainer::~ScMyValidationsContainer()
+{
+}
+
+void ScMyValidationsContainer::AddValidation(const uno::Any& aTempAny,
+ sal_Int32& nValidationIndex)
+{
+ uno::Reference<beans::XPropertySet> xPropertySet(aTempAny, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ OUString sErrorMessage;
+ xPropertySet->getPropertyValue(SC_UNONAME_ERRMESS) >>= sErrorMessage;
+ OUString sErrorTitle;
+ xPropertySet->getPropertyValue(SC_UNONAME_ERRTITLE) >>= sErrorTitle;
+ OUString sInputMessage;
+ xPropertySet->getPropertyValue(SC_UNONAME_INPMESS) >>= sInputMessage;
+ OUString sInputTitle;
+ xPropertySet->getPropertyValue(SC_UNONAME_INPTITLE) >>= sInputTitle;
+ bool bShowErrorMessage = ::cppu::any2bool(xPropertySet->getPropertyValue(SC_UNONAME_SHOWERR));
+ bool bShowInputMessage = ::cppu::any2bool(xPropertySet->getPropertyValue(SC_UNONAME_SHOWINP));
+ sheet::ValidationType aValidationType;
+ xPropertySet->getPropertyValue(SC_UNONAME_TYPE) >>= aValidationType;
+ if (!bShowErrorMessage && !bShowInputMessage && aValidationType == sheet::ValidationType_ANY &&
+ sErrorMessage.isEmpty() && sErrorTitle.isEmpty() && sInputMessage.isEmpty() && sInputTitle.isEmpty())
+ return;
+
+ ScMyValidation aValidation;
+ aValidation.sErrorMessage = sErrorMessage;
+ aValidation.sErrorTitle = sErrorTitle;
+ aValidation.sInputMessage = sInputMessage;
+ aValidation.sInputTitle = sInputTitle;
+ aValidation.bShowErrorMessage = bShowErrorMessage;
+ aValidation.bShowInputMessage = bShowInputMessage;
+ aValidation.aValidationType = aValidationType;
+ aValidation.bIgnoreBlanks = ::cppu::any2bool(xPropertySet->getPropertyValue(SC_UNONAME_IGNOREBL));
+ xPropertySet->getPropertyValue(SC_UNONAME_SHOWLIST) >>= aValidation.nShowList;
+ xPropertySet->getPropertyValue(SC_UNONAME_ERRALSTY) >>= aValidation.aAlertStyle;
+ uno::Reference<sheet::XSheetCondition> xCondition(xPropertySet, uno::UNO_QUERY);
+ if (xCondition.is())
+ {
+ aValidation.sFormula1 = xCondition->getFormula1();
+ aValidation.sFormula2 = xCondition->getFormula2();
+ aValidation.aOperator = xCondition->getOperator();
+ table::CellAddress aCellAddress= xCondition->getSourcePosition();
+ aValidation.aBaseCell = ScAddress( static_cast<SCCOL>(aCellAddress.Column), static_cast<SCROW>(aCellAddress.Row), aCellAddress.Sheet );
+ }
+ //ScMyValidationRange aValidationRange;
+ bool bEqualFound(false);
+ sal_Int32 i(0);
+ sal_Int32 nCount(aValidationVec.size());
+ while (i < nCount && !bEqualFound)
+ {
+ bEqualFound = aValidationVec[i].IsEqual(aValidation);
+ if (!bEqualFound)
+ ++i;
+ }
+ if (bEqualFound)
+ nValidationIndex = i;
+ else
+ {
+ sal_Int32 nNameIndex(nCount + 1);
+ OUString sCount(OUString::number(nNameIndex));
+ aValidation.sName += "val";
+ aValidation.sName += sCount;
+ aValidationVec.push_back(aValidation);
+ nValidationIndex = nCount;
+ }
+}
+
+OUString ScMyValidationsContainer::GetCondition(ScXMLExport& rExport, const ScMyValidation& aValidation)
+{
+ /* ATTENTION! Should the condition to not write sheet::ValidationType_ANY
+ * ever be changed, adapt the conditional call of
+ * MarkUsedExternalReferences() in
+ * ScTableValidationObj::ScTableValidationObj() accordingly! */
+ OUString sCondition;
+ if (aValidation.aValidationType != sheet::ValidationType_ANY)
+ {
+ switch (aValidation.aValidationType)
+ {
+ //case sheet::ValidationType_CUSTOM
+ case sheet::ValidationType_DATE :
+ sCondition += "cell-content-is-date()";
+ break;
+ case sheet::ValidationType_DECIMAL :
+ sCondition += "cell-content-is-decimal-number()";
+ break;
+ case sheet::ValidationType_LIST :
+ sCondition += "cell-content-is-in-list(" + aValidation.sFormula1 + ")";
+ break;
+ case sheet::ValidationType_TEXT_LEN :
+ if (aValidation.aOperator != sheet::ConditionOperator_BETWEEN &&
+ aValidation.aOperator != sheet::ConditionOperator_NOT_BETWEEN)
+ sCondition += "cell-content-text-length()";
+ break;
+ case sheet::ValidationType_TIME :
+ sCondition += "cell-content-is-time()";
+ break;
+ case sheet::ValidationType_WHOLE :
+ sCondition += "cell-content-is-whole-number()";
+ break;
+ case sheet::ValidationType_CUSTOM :
+ sCondition += "is-true-formula(" + aValidation.sFormula1 + ")";
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ if (aValidation.aValidationType != sheet::ValidationType_LIST &&
+ aValidation.aValidationType != sheet::ValidationType_CUSTOM &&
+ (!aValidation.sFormula1.isEmpty() ||
+ ((aValidation.aOperator == sheet::ConditionOperator_BETWEEN ||
+ aValidation.aOperator == sheet::ConditionOperator_NOT_BETWEEN) &&
+ !aValidation.sFormula2.isEmpty())))
+ {
+ if (aValidation.aValidationType != sheet::ValidationType_TEXT_LEN)
+ sCondition += " and ";
+ if (aValidation.aOperator != sheet::ConditionOperator_BETWEEN &&
+ aValidation.aOperator != sheet::ConditionOperator_NOT_BETWEEN)
+ {
+ if (aValidation.aValidationType != sheet::ValidationType_TEXT_LEN)
+ sCondition += "cell-content()";
+ switch (aValidation.aOperator)
+ {
+ case sheet::ConditionOperator_EQUAL :
+ sCondition += "=";
+ break;
+ case sheet::ConditionOperator_GREATER :
+ sCondition += ">";
+ break;
+ case sheet::ConditionOperator_GREATER_EQUAL :
+ sCondition += ">=";
+ break;
+ case sheet::ConditionOperator_LESS :
+ sCondition += "<";
+ break;
+ case sheet::ConditionOperator_LESS_EQUAL :
+ sCondition += "<=";
+ break;
+ case sheet::ConditionOperator_NOT_EQUAL :
+ sCondition += "!=";
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ sCondition += aValidation.sFormula1;
+ }
+ else
+ {
+ if (aValidation.aValidationType == sheet::ValidationType_TEXT_LEN)
+ {
+ if (aValidation.aOperator == sheet::ConditionOperator_BETWEEN)
+ sCondition += "cell-content-text-length-is-between(";
+ else
+ sCondition += "cell-content-text-length-is-not-between(";
+ }
+ else
+ {
+ if (aValidation.aOperator == sheet::ConditionOperator_BETWEEN)
+ sCondition += "cell-content-is-between(";
+ else
+ sCondition += "cell-content-is-not-between(";
+ }
+ sCondition += aValidation.sFormula1 + "," + aValidation.sFormula2 + ")";
+ }
+ }
+ else
+ if (aValidation.aValidationType == sheet::ValidationType_TEXT_LEN)
+ sCondition.clear();
+ }
+ if (!sCondition.isEmpty())
+ {
+ const formula::FormulaGrammar::Grammar eGrammar = rExport.GetDocument()->GetStorageGrammar();
+ sal_uInt16 nNamespacePrefix = (eGrammar == formula::FormulaGrammar::GRAM_ODFF ? XML_NAMESPACE_OF : XML_NAMESPACE_OOOC);
+ sCondition = rExport.GetNamespaceMap().GetQNameByKey( nNamespacePrefix, sCondition, false );
+ }
+
+ return sCondition;
+}
+
+OUString ScMyValidationsContainer::GetBaseCellAddress(const ScDocument* pDoc, const ScAddress& aCell)
+{
+ OUString sAddress;
+ ScRangeStringConverter::GetStringFromAddress( sAddress, aCell, pDoc, ::formula::FormulaGrammar::CONV_OOO );
+ return sAddress;
+}
+
+void ScMyValidationsContainer::WriteMessage(ScXMLExport& rExport,
+ const OUString& sTitle, const OUString& sOUMessage,
+ const bool bShowMessage, const bool bIsHelpMessage)
+{
+ if (!sTitle.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_TITLE, sTitle);
+ if (bShowMessage)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, XML_FALSE);
+ std::unique_ptr<SvXMLElementExport> pMessage;
+ if (bIsHelpMessage)
+ pMessage.reset(new SvXMLElementExport(rExport, XML_NAMESPACE_TABLE, XML_HELP_MESSAGE, true, true));
+ else
+ pMessage.reset(new SvXMLElementExport(rExport, XML_NAMESPACE_TABLE, XML_ERROR_MESSAGE, true, true));
+ if (sOUMessage.isEmpty())
+ return;
+
+ sal_Int32 i(0);
+ OUStringBuffer sTemp;
+ OUString sText(convertLineEnd(sOUMessage, LINEEND_LF));
+ bool bPrevCharWasSpace(true);
+ while(i < sText.getLength())
+ {
+ if( sText[i] == '\n')
+ {
+ SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ rExport.GetTextParagraphExport()->exportCharacterData(sTemp.makeStringAndClear(), bPrevCharWasSpace);
+ bPrevCharWasSpace = true; // reset for start of next paragraph
+ }
+ else
+ sTemp.append(sText[i]);
+ ++i;
+ }
+ if (!sTemp.isEmpty())
+ {
+ SvXMLElementExport aElemP(rExport, XML_NAMESPACE_TEXT, XML_P, true, false);
+ rExport.GetTextParagraphExport()->exportCharacterData(sTemp.makeStringAndClear(), bPrevCharWasSpace);
+ }
+}
+
+void ScMyValidationsContainer::WriteValidations(ScXMLExport& rExport)
+{
+ if (aValidationVec.empty())
+ return;
+
+ SvXMLElementExport aElemVs(rExport, XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATIONS, true, true);
+ for (const auto& rValidation : aValidationVec)
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, rValidation.sName);
+ OUString sCondition(GetCondition(rExport, rValidation));
+ if (!sCondition.isEmpty())
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_CONDITION, sCondition);
+ if (rValidation.bIgnoreBlanks)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ALLOW_EMPTY_CELL, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_ALLOW_EMPTY_CELL, XML_FALSE);
+ if (rValidation.aValidationType == sheet::ValidationType_LIST)
+ {
+ switch (rValidation.nShowList)
+ {
+ case sheet::TableValidationVisibility::INVISIBLE:
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_LIST, XML_NO);
+ break;
+ case sheet::TableValidationVisibility::UNSORTED:
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_LIST, XML_UNSORTED);
+ break;
+ case sheet::TableValidationVisibility::SORTEDASCENDING:
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_LIST, XML_SORT_ASCENDING);
+ break;
+ default:
+ OSL_FAIL("unknown ListType");
+ }
+ }
+ }
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_BASE_CELL_ADDRESS, GetBaseCellAddress(rExport.GetDocument(), rValidation.aBaseCell));
+ SvXMLElementExport aElemV(rExport, XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION, true, true);
+ if (rValidation.bShowInputMessage || !rValidation.sInputMessage.isEmpty() || !rValidation.sInputTitle.isEmpty())
+ {
+ WriteMessage(rExport, rValidation.sInputTitle, rValidation.sInputMessage, rValidation.bShowInputMessage, true);
+ }
+ if (rValidation.bShowErrorMessage || !rValidation.sErrorMessage.isEmpty() || !rValidation.sErrorTitle.isEmpty())
+ {
+ switch (rValidation.aAlertStyle)
+ {
+ case sheet::ValidationAlertStyle_INFO :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MESSAGE_TYPE, XML_INFORMATION);
+ WriteMessage(rExport, rValidation.sErrorTitle, rValidation.sErrorMessage, rValidation.bShowErrorMessage, false);
+ }
+ break;
+ case sheet::ValidationAlertStyle_WARNING :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MESSAGE_TYPE, XML_WARNING);
+ WriteMessage(rExport, rValidation.sErrorTitle, rValidation.sErrorMessage, rValidation.bShowErrorMessage, false);
+ }
+ break;
+ case sheet::ValidationAlertStyle_STOP :
+ {
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_MESSAGE_TYPE, XML_STOP);
+ WriteMessage(rExport, rValidation.sErrorTitle, rValidation.sErrorMessage, rValidation.bShowErrorMessage, false);
+ }
+ break;
+ case sheet::ValidationAlertStyle_MACRO :
+ {
+ {
+ //rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, aItr->sErrorTitle);
+ if (rValidation.bShowErrorMessage)
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_EXECUTE, XML_TRUE);
+ else
+ rExport.AddAttribute(XML_NAMESPACE_TABLE, XML_EXECUTE, XML_FALSE);
+ SvXMLElementExport aEMElem(rExport, XML_NAMESPACE_TABLE, XML_ERROR_MACRO, true, true);
+ }
+ {
+ // #i47525# for a script URL the type and the property name for the URL
+ // are both "Script", for a simple macro name the type is "StarBasic"
+ // and the property name is "MacroName".
+ bool bScriptURL = SfxApplication::IsXScriptURL( rValidation.sErrorTitle );
+
+ static const OUStringLiteral sScript(u"Script");
+ uno::Sequence<beans::PropertyValue> aSeq( comphelper::InitPropertySequence({
+ { "EventType", uno::Any(bScriptURL ? sScript : OUString("StarBasic")) },
+ { "Library", uno::Any(OUString()) },
+ { bScriptURL ? sScript : OUString("MacroName"), uno::Any(rValidation.sErrorTitle) }
+ }));
+ // 2) export the sequence
+ rExport.GetEventExport().ExportSingleEvent( aSeq, "OnError");
+ }
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ }
+}
+
+const OUString& ScMyValidationsContainer::GetValidationName(const sal_Int32 nIndex)
+{
+ OSL_ENSURE( o3tl::make_unsigned(nIndex) < aValidationVec.size(), "out of range" );
+ return aValidationVec[nIndex].sName;
+}
+
+sal_Int32 ScMyDefaultStyles::GetStyleNameIndex(const ScFormatRangeStyles* pCellStyles,
+ const sal_Int32 nTable, const sal_Int32 nPos,
+ const sal_Int32 i, bool& bIsAutoStyle)
+{
+ return pCellStyles->GetStyleNameIndex(nTable, i, nPos, bIsAutoStyle);
+}
+
+void ScMyDefaultStyles::FillDefaultStyles(const sal_Int32 nTable,
+ const sal_Int32 nLastRow, const sal_Int32 nLastCol,
+ const ScFormatRangeStyles* pCellStyles, ScDocument* pDoc)
+{
+ maColDefaults.clear();
+ maColDefaults.resize(nLastCol + 1);
+ if (!pDoc)
+ return ;
+
+ SCTAB nTab = static_cast<SCTAB>(nTable);
+ pDoc->CreateColumnIfNotExists(nTab, nLastCol);
+ sal_Int32 nPos;
+ ScMyDefaultStyleList* pDefaults = &maColDefaults;
+ bool bPrevAutoStyle(false);
+ bool bIsAutoStyle;
+ sal_Int32 nPrevIndex(0);
+ sal_Int32 nRepeat(0);
+ for (sal_Int32 i = nLastCol; i >= 0; --i)
+ {
+ pDoc->GetColDefault(nTab, static_cast<SCCOL>(i), static_cast<SCROW>(nLastRow), nPos);
+ if (!nRepeat)
+ {
+ nPrevIndex = GetStyleNameIndex(pCellStyles, nTab, nPos, i, bPrevAutoStyle);
+ (*pDefaults)[i].nIndex = nPrevIndex;
+ (*pDefaults)[i].bIsAutoStyle = bPrevAutoStyle;
+ nRepeat = 1;
+ }
+ else
+ {
+ sal_Int32 nIndex = GetStyleNameIndex(pCellStyles, nTab, nPos, i, bIsAutoStyle);
+ if ((nIndex != nPrevIndex) || (bIsAutoStyle != bPrevAutoStyle))
+ {
+ nRepeat = 1;
+ nPrevIndex = GetStyleNameIndex(pCellStyles, nTab, nPos, i, bPrevAutoStyle);
+ (*pDefaults)[i].nIndex = nPrevIndex;
+ (*pDefaults)[i].bIsAutoStyle = bPrevAutoStyle;
+ }
+ else
+ {
+ (*pDefaults)[i].nIndex = nPrevIndex;
+ (*pDefaults)[i].bIsAutoStyle = bPrevAutoStyle;
+ ++nRepeat;
+ if (nRepeat > 1)
+ (*pDefaults)[i].nRepeat = nRepeat;
+ }
+ }
+ }
+}
+
+ScMyRowFormatRange::ScMyRowFormatRange()
+ : nStartColumn(0),
+ nRepeatColumns(0),
+ nRepeatRows(0),
+ nIndex(-1),
+ nValidationIndex(-1),
+ bIsAutoStyle(true)
+{
+}
+
+bool ScMyRowFormatRange::operator< (const ScMyRowFormatRange& rRange) const
+{
+ return (nStartColumn < rRange.nStartColumn);
+}
+
+ScRowFormatRanges::ScRowFormatRanges()
+ : pColDefaults(nullptr),
+ nSize(0)
+{
+}
+
+ScRowFormatRanges::ScRowFormatRanges(const ScRowFormatRanges* pRanges)
+ : aRowFormatRanges(pRanges->aRowFormatRanges),
+ pColDefaults(pRanges->pColDefaults),
+ nSize(pRanges->nSize)
+{
+}
+
+ScRowFormatRanges::~ScRowFormatRanges()
+{
+}
+
+void ScRowFormatRanges::Clear()
+{
+ aRowFormatRanges.clear();
+ nSize = 0;
+}
+
+void ScRowFormatRanges::AddRange(const sal_Int32 nPrevStartCol, const sal_Int32 nRepeat, const sal_Int32 nPrevIndex,
+ const bool bPrevAutoStyle, const ScMyRowFormatRange& rFormatRange)
+{
+ sal_Int32 nIndex(-1);
+ if ((nPrevIndex != rFormatRange.nIndex) ||
+ (bPrevAutoStyle != rFormatRange.bIsAutoStyle))
+ nIndex = rFormatRange.nIndex;
+
+ bool bInserted(false);
+ if (!aRowFormatRanges.empty())
+ {
+ ScMyRowFormatRange& rRange(aRowFormatRanges.back());
+ if ((nPrevStartCol == (rRange.nStartColumn + rRange.nRepeatColumns))
+ && (rRange.bIsAutoStyle == rFormatRange.bIsAutoStyle) && (rRange.nIndex == nIndex)
+ && (rRange.nValidationIndex == rFormatRange.nValidationIndex))
+ {
+ if (rFormatRange.nRepeatRows < rRange.nRepeatRows)
+ rRange.nRepeatRows = rFormatRange.nRepeatRows;
+ rRange.nRepeatColumns += nRepeat;
+ bInserted = true;
+ }
+ }
+ if (!bInserted)
+ {
+ ScMyRowFormatRange aRange;
+ aRange.nStartColumn = nPrevStartCol;
+ aRange.nRepeatColumns = nRepeat;
+ aRange.nRepeatRows = rFormatRange.nRepeatRows;
+ aRange.nValidationIndex = rFormatRange.nValidationIndex;
+ aRange.bIsAutoStyle = rFormatRange.bIsAutoStyle;
+ aRange.nIndex = nIndex;
+ aRowFormatRanges.push_back(aRange);
+ ++nSize;
+ }
+}
+
+void ScRowFormatRanges::AddRange(const ScMyRowFormatRange& rFormatRange)
+{
+ OSL_ENSURE(pColDefaults, "no column defaults");
+ if (!pColDefaults)
+ return;
+ sal_Int32 nPrevIndex = -1;
+ bool bPrevAutoStyle = true;
+
+ sal_uInt32 nPrevStartCol(rFormatRange.nStartColumn);
+ OSL_ENSURE( static_cast<size_t>(nPrevStartCol) < pColDefaults->size(), "nPrevStartCol out of bounds");
+ sal_uInt32 nRepeat;
+ if (static_cast<size_t>(nPrevStartCol) < pColDefaults->size())
+ {
+ nRepeat = (*pColDefaults)[nPrevStartCol].nRepeat;
+ nPrevIndex = (*pColDefaults)[nPrevStartCol].nIndex;
+ bPrevAutoStyle = (*pColDefaults)[nPrevStartCol].bIsAutoStyle;
+ }
+ else
+ {
+ /* Again, this is to prevent out-of-bounds accesses, so FIXME
+ * elsewhere! */
+ if (pColDefaults->empty())
+ {
+ nRepeat = 1;
+ nPrevIndex = -1;
+ bPrevAutoStyle = false;
+ }
+ else
+ {
+ nRepeat = (*pColDefaults)[pColDefaults->size()-1].nRepeat;
+ nPrevIndex = (*pColDefaults)[pColDefaults->size()-1].nIndex;
+ bPrevAutoStyle = (*pColDefaults)[pColDefaults->size()-1].bIsAutoStyle;
+ }
+ }
+ sal_uInt32 nEnd = nPrevStartCol + rFormatRange.nRepeatColumns;
+ for(sal_uInt32 i = nPrevStartCol + nRepeat; i < nEnd && i < pColDefaults->size(); i += (*pColDefaults)[i].nRepeat)
+ {
+ OSL_ENSURE(sal_uInt32(nPrevStartCol + nRepeat) <= nEnd, "something went wrong");
+ if ((nPrevIndex != (*pColDefaults)[i].nIndex) ||
+ (bPrevAutoStyle != (*pColDefaults)[i].bIsAutoStyle))
+ {
+ AddRange(nPrevStartCol, nRepeat, nPrevIndex, bPrevAutoStyle, rFormatRange);
+ nPrevStartCol = i;
+ nRepeat = (*pColDefaults)[i].nRepeat;
+ nPrevIndex = (*pColDefaults)[i].nIndex;
+ bPrevAutoStyle = (*pColDefaults)[i].bIsAutoStyle;
+ }
+ else
+ nRepeat += (*pColDefaults)[i].nRepeat;
+ }
+ if (sal_uInt32(nPrevStartCol + nRepeat) > nEnd)
+ nRepeat = nEnd - nPrevStartCol;
+ AddRange(nPrevStartCol, nRepeat, nPrevIndex, bPrevAutoStyle, rFormatRange);
+
+}
+
+bool ScRowFormatRanges::GetNext(ScMyRowFormatRange& aFormatRange)
+{
+ ScMyRowFormatRangesList::iterator aItr(aRowFormatRanges.begin());
+ if (aItr != aRowFormatRanges.end())
+ {
+ aFormatRange = *aItr;
+ aRowFormatRanges.erase(aItr);
+ --nSize;
+ return true;
+ }
+ return false;
+}
+
+sal_Int32 ScRowFormatRanges::GetMaxRows() const
+{
+ sal_Int32 nMaxRows(0);
+ if (!aRowFormatRanges.empty())
+ {
+ auto aItr = std::min_element(aRowFormatRanges.begin(), aRowFormatRanges.end(),
+ [](const ScMyRowFormatRange& a, const ScMyRowFormatRange& b) { return a.nRepeatRows < b.nRepeatRows; });
+ nMaxRows = (*aItr).nRepeatRows;
+ }
+ else
+ {
+ OSL_FAIL("no ranges found");
+ }
+ return nMaxRows;
+}
+
+void ScRowFormatRanges::Sort()
+{
+ aRowFormatRanges.sort();
+}
+
+ScMyFormatRange::ScMyFormatRange()
+ : nStyleNameIndex(-1)
+ , nValidationIndex(-1)
+ , nNumberFormat(0)
+ , bIsAutoStyle(true)
+{
+}
+
+bool ScMyFormatRange::operator<(const ScMyFormatRange& rRange) const
+{
+ if (aRangeAddress.StartRow < rRange.aRangeAddress.StartRow)
+ return true;
+ else
+ if (aRangeAddress.StartRow == rRange.aRangeAddress.StartRow)
+ return (aRangeAddress.StartColumn < rRange.aRangeAddress.StartColumn);
+ else
+ return false;
+}
+
+ScFormatRangeStyles::ScFormatRangeStyles()
+ : pColDefaults(nullptr)
+{
+}
+
+ScFormatRangeStyles::~ScFormatRangeStyles()
+{
+}
+
+void ScFormatRangeStyles::AddNewTable(const sal_Int32 nTable)
+{
+ sal_Int32 nSize = aTables.size() - 1;
+ if (nTable > nSize)
+ for (sal_Int32 i = nSize; i < nTable; ++i)
+ {
+ aTables.emplace_back();
+ }
+}
+
+bool ScFormatRangeStyles::AddStyleName(OUString const & rString, sal_Int32& rIndex, const bool bIsAutoStyle)
+{
+ if (bIsAutoStyle)
+ {
+ aAutoStyleNames.push_back(rString);
+ rIndex = aAutoStyleNames.size() - 1;
+ return true;
+ }
+ else
+ {
+ sal_Int32 nCount(aStyleNames.size());
+ bool bFound(false);
+ sal_Int32 i(nCount - 1);
+ while ((i >= 0) && (!bFound))
+ {
+ if (aStyleNames.at(i) == rString)
+ bFound = true;
+ else
+ i--;
+ }
+ if (bFound)
+ {
+ rIndex = i;
+ return false;
+ }
+ else
+ {
+ aStyleNames.push_back(rString);
+ rIndex = aStyleNames.size() - 1;
+ return true;
+ }
+ }
+}
+
+sal_Int32 ScFormatRangeStyles::GetIndexOfStyleName(std::u16string_view rString, const OUString& rPrefix, bool& bIsAutoStyle)
+{
+ sal_Int32 nPrefixLength(rPrefix.getLength());
+ std::u16string_view sTemp(rString.substr(nPrefixLength));
+ sal_Int32 nIndex(o3tl::toInt32(sTemp));
+ if (nIndex > 0 && o3tl::make_unsigned(nIndex-1) < aAutoStyleNames.size() && aAutoStyleNames.at(nIndex - 1) == rString)
+ {
+ bIsAutoStyle = true;
+ return nIndex - 1;
+ }
+ else
+ {
+ sal_Int32 i(0);
+ bool bFound(false);
+ while (!bFound && o3tl::make_unsigned(i) < aStyleNames.size())
+ {
+ if (aStyleNames[i] == rString)
+ bFound = true;
+ else
+ ++i;
+ }
+ if (bFound)
+ {
+ bIsAutoStyle = false;
+ return i;
+ }
+ else
+ {
+ i = 0;
+ while (!bFound && o3tl::make_unsigned(i) < aAutoStyleNames.size())
+ {
+ if (aAutoStyleNames[i] == rString)
+ bFound = true;
+ else
+ ++i;
+ }
+ if (bFound)
+ {
+ bIsAutoStyle = true;
+ return i;
+ }
+ else
+ return -1;
+ }
+ }
+}
+
+sal_Int32 ScFormatRangeStyles::GetStyleNameIndex(const sal_Int32 nTable,
+ const sal_Int32 nColumn, const sal_Int32 nRow, bool& bIsAutoStyle) const
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ bIsAutoStyle = false;
+ if (o3tl::make_unsigned(nTable) >= aTables.size())
+ return -1;
+ for (const ScMyFormatRange & rFormatRange : aTables[nTable])
+ {
+ if ((rFormatRange.aRangeAddress.StartColumn <= nColumn) &&
+ (rFormatRange.aRangeAddress.EndColumn >= nColumn) &&
+ (rFormatRange.aRangeAddress.StartRow <= nRow) &&
+ (rFormatRange.aRangeAddress.EndRow >= nRow))
+ {
+ bIsAutoStyle = rFormatRange.bIsAutoStyle;
+ return rFormatRange.nStyleNameIndex;
+ }
+ }
+ return -1;
+}
+
+sal_Int32 ScFormatRangeStyles::GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nColumn, const sal_Int32 nRow,
+ bool& bIsAutoStyle, sal_Int32& nValidationIndex, sal_Int32& nNumberFormat, const sal_Int32 nRemoveBeforeRow)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ if (o3tl::make_unsigned(nTable) >= aTables.size())
+ return -1;
+ ScMyFormatRangeAddresses& rFormatRanges(aTables[nTable]);
+ ScMyFormatRangeAddresses::iterator aItr(rFormatRanges.begin());
+ ScMyFormatRangeAddresses::iterator aEndItr(rFormatRanges.end());
+ while (aItr != aEndItr)
+ {
+ if (((*aItr).aRangeAddress.StartColumn <= nColumn) &&
+ ((*aItr).aRangeAddress.EndColumn >= nColumn) &&
+ ((*aItr).aRangeAddress.StartRow <= nRow) &&
+ ((*aItr).aRangeAddress.EndRow >= nRow))
+ {
+ bIsAutoStyle = aItr->bIsAutoStyle;
+ nValidationIndex = aItr->nValidationIndex;
+ nNumberFormat = aItr->nNumberFormat;
+ OSL_ENSURE( o3tl::make_unsigned(nColumn) < pColDefaults->size(), "nColumn out of bounds");
+ if (o3tl::make_unsigned(nColumn) < pColDefaults->size() &&
+ ((*pColDefaults)[nColumn].nIndex != -1) &&
+ ((*pColDefaults)[nColumn].nIndex == (*aItr).nStyleNameIndex) &&
+ ((*pColDefaults)[nColumn].bIsAutoStyle == (*aItr).bIsAutoStyle))
+ return -1;
+ else
+ return (*aItr).nStyleNameIndex;
+ }
+ else
+ {
+ if ((*aItr).aRangeAddress.EndRow < nRemoveBeforeRow)
+ aItr = rFormatRanges.erase(aItr);
+ else
+ ++aItr;
+ }
+ }
+ return -1;
+}
+
+void ScFormatRangeStyles::GetFormatRanges(const sal_Int32 nStartColumn, const sal_Int32 nEndColumn, const sal_Int32 nRow,
+ const sal_Int32 nTable, ScRowFormatRanges* pRowFormatRanges)
+{
+ sal_Int32 nTotalColumns(nEndColumn - nStartColumn + 1);
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ ScMyFormatRangeAddresses& rFormatRanges(aTables[nTable]);
+ ScMyFormatRangeAddresses::iterator aItr(rFormatRanges.begin());
+ ScMyFormatRangeAddresses::iterator aEndItr(rFormatRanges.end());
+ sal_Int32 nColumns = 0;
+ while (aItr != aEndItr && nColumns < nTotalColumns)
+ {
+ if (((*aItr).aRangeAddress.StartRow <= nRow) &&
+ ((*aItr).aRangeAddress.EndRow >= nRow))
+ {
+ if ((((*aItr).aRangeAddress.StartColumn <= nStartColumn) &&
+ ((*aItr).aRangeAddress.EndColumn >= nStartColumn)) ||
+ (((*aItr).aRangeAddress.StartColumn <= nEndColumn) &&
+ ((*aItr).aRangeAddress.EndColumn >= nEndColumn)) ||
+ (((*aItr).aRangeAddress.StartColumn >= nStartColumn) &&
+ ((*aItr).aRangeAddress.EndColumn <= nEndColumn)))
+ {
+ ScMyRowFormatRange aRange;
+ aRange.nIndex = aItr->nStyleNameIndex;
+ aRange.nValidationIndex = aItr->nValidationIndex;
+ aRange.bIsAutoStyle = aItr->bIsAutoStyle;
+ if ((aItr->aRangeAddress.StartColumn < nStartColumn) &&
+ (aItr->aRangeAddress.EndColumn >= nStartColumn))
+ {
+ if (aItr->aRangeAddress.EndColumn >= nEndColumn)
+ aRange.nRepeatColumns = nTotalColumns;
+ else
+ aRange.nRepeatColumns = aItr->aRangeAddress.EndColumn - nStartColumn + 1;
+ aRange.nStartColumn = nStartColumn;
+ }
+ else if ((aItr->aRangeAddress.StartColumn >= nStartColumn) &&
+ (aItr->aRangeAddress.EndColumn <= nEndColumn))
+ {
+ aRange.nRepeatColumns = aItr->aRangeAddress.EndColumn - aItr->aRangeAddress.StartColumn + 1;
+ aRange.nStartColumn = aItr->aRangeAddress.StartColumn;
+ }
+ else if ((aItr->aRangeAddress.StartColumn >= nStartColumn) &&
+ (aItr->aRangeAddress.StartColumn <= nEndColumn) &&
+ (aItr->aRangeAddress.EndColumn > nEndColumn))
+ {
+ aRange.nRepeatColumns = nEndColumn - aItr->aRangeAddress.StartColumn + 1;
+ aRange.nStartColumn = aItr->aRangeAddress.StartColumn;
+ }
+ aRange.nRepeatRows = aItr->aRangeAddress.EndRow - nRow + 1;
+ pRowFormatRanges->AddRange(aRange);
+ nColumns += aRange.nRepeatColumns;
+ }
+ ++aItr;
+ }
+ else
+ if(aItr->aRangeAddress.EndRow < nRow)
+ aItr = rFormatRanges.erase(aItr);
+ else
+ ++aItr;
+ }
+ pRowFormatRanges->Sort();
+}
+
+void ScFormatRangeStyles::AddRangeStyleName(const table::CellRangeAddress& rCellRangeAddress,
+ const sal_Int32 nStringIndex, const bool bIsAutoStyle, const sal_Int32 nValidationIndex,
+ const sal_Int32 nNumberFormat)
+{
+ ScMyFormatRange aFormatRange;
+ aFormatRange.aRangeAddress = rCellRangeAddress;
+ aFormatRange.nStyleNameIndex = nStringIndex;
+ aFormatRange.nValidationIndex = nValidationIndex;
+ aFormatRange.nNumberFormat = nNumberFormat;
+ aFormatRange.bIsAutoStyle = bIsAutoStyle;
+ OSL_ENSURE(o3tl::make_unsigned(rCellRangeAddress.Sheet) < aTables.size(), "wrong table");
+ ScMyFormatRangeAddresses& rFormatRanges(aTables[rCellRangeAddress.Sheet]);
+ rFormatRanges.push_back(aFormatRange);
+}
+
+OUString & ScFormatRangeStyles::GetStyleNameByIndex(const sal_Int32 nIndex, const bool bIsAutoStyle)
+{
+ if (bIsAutoStyle)
+ return aAutoStyleNames[nIndex];
+ else
+ return aStyleNames[nIndex];
+}
+
+void ScFormatRangeStyles::Sort()
+{
+ for (auto & rTable : aTables)
+ rTable.sort();
+}
+
+ScColumnRowStylesBase::ScColumnRowStylesBase()
+{
+}
+
+ScColumnRowStylesBase::~ScColumnRowStylesBase()
+{
+}
+
+sal_Int32 ScColumnRowStylesBase::AddStyleName(const OUString & rString)
+{
+ aStyleNames.push_back(rString);
+ return aStyleNames.size() - 1;
+}
+
+sal_Int32 ScColumnRowStylesBase::GetIndexOfStyleName(std::u16string_view rString, const OUString& rPrefix)
+{
+ sal_Int32 nPrefixLength(rPrefix.getLength());
+ std::u16string_view sTemp(rString.substr(nPrefixLength));
+ sal_Int32 nIndex(o3tl::toInt32(sTemp));
+ if (nIndex > 0 && o3tl::make_unsigned(nIndex-1) < aStyleNames.size() && aStyleNames.at(nIndex - 1) == rString)
+ return nIndex - 1;
+ else
+ {
+ sal_Int32 i(0);
+ bool bFound(false);
+ while (!bFound && o3tl::make_unsigned(i) < aStyleNames.size())
+ {
+ if (aStyleNames.at(i) == rString)
+ bFound = true;
+ else
+ ++i;
+ }
+ if (bFound)
+ return i;
+ else
+ return -1;
+ }
+}
+
+OUString& ScColumnRowStylesBase::GetStyleNameByIndex(const sal_Int32 nIndex)
+{
+ return aStyleNames[nIndex];
+}
+
+ScColumnStyles::ScColumnStyles()
+{
+}
+
+ScColumnStyles::~ScColumnStyles()
+{
+}
+
+void ScColumnStyles::AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields)
+{
+ sal_Int32 nSize(aTables.size() - 1);
+ if (nTable > nSize)
+ for (sal_Int32 i = nSize; i < nTable; ++i)
+ {
+ ScMyColumnStyleVec aFieldsVec(nFields + 1, ScColumnStyle());
+ aTables.push_back(aFieldsVec);
+ }
+}
+
+sal_Int32 ScColumnStyles::GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nField,
+ bool& bIsVisible)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ if (o3tl::make_unsigned(nField) < aTables[nTable].size())
+ {
+ bIsVisible = aTables[nTable][nField].bIsVisible;
+ return aTables[nTable][nField].nIndex;
+ }
+ else
+ {
+ bIsVisible = aTables[nTable][aTables[nTable].size() - 1].bIsVisible;
+ return aTables[nTable][aTables[nTable].size() - 1].nIndex;
+ }
+}
+
+void ScColumnStyles::AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nField,
+ const sal_Int32 nStringIndex, const bool bIsVisible)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ OSL_ENSURE(aTables[nTable].size() >= o3tl::make_unsigned(nField), "wrong field");
+ ScColumnStyle aStyle;
+ aStyle.nIndex = nStringIndex;
+ aStyle.bIsVisible = bIsVisible;
+ if (aTables[nTable].size() == static_cast<sal_uInt32>(nField))
+ aTables[nTable].push_back(aStyle);
+ aTables[nTable][nField] = aStyle;
+}
+
+ScRowStyles::Cache::Cache() :
+ mnTable(-1), mnStart(-1), mnEnd(-1), mnStyle(-1) {}
+
+bool ScRowStyles::Cache::hasCache(sal_Int32 nTable, sal_Int32 nField) const
+{
+ return mnTable == nTable && mnStart <= nField && nField < mnEnd;
+}
+
+ScRowStyles::ScRowStyles()
+{
+}
+
+ScRowStyles::~ScRowStyles()
+{
+}
+
+void ScRowStyles::AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields)
+{
+ sal_Int32 nSize(aTables.size() - 1);
+ if (nTable > nSize)
+ for (sal_Int32 i = nSize; i < nTable; ++i)
+ {
+ aTables.push_back(std::make_unique<StylesType>(0, nFields+1, -1));
+ }
+}
+
+sal_Int32 ScRowStyles::GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nField)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ if (o3tl::make_unsigned(nTable) >= aTables.size())
+ return -1;
+
+ if (maCache.hasCache(nTable, nField))
+ // Cache hit !
+ return maCache.mnStyle;
+
+ StylesType& r = *aTables[nTable];
+ if (!r.is_tree_valid())
+ r.build_tree();
+ sal_Int32 nStyle(0);
+ sal_Int32 nStart(0), nEnd(0);
+ if (r.search_tree(nField, nStyle, &nStart, &nEnd).second)
+ {
+ // Cache this value for better performance.
+ maCache.mnTable = nTable;
+ maCache.mnStart = nStart;
+ maCache.mnEnd = nEnd;
+ maCache.mnStyle = nStyle;
+ return nStyle;
+ }
+
+ return -1;
+}
+
+void ScRowStyles::AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nField,
+ const sal_Int32 nStringIndex)
+{
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ StylesType& r = *aTables[nTable];
+ r.insert_back(nField, nField+1, nStringIndex);
+}
+
+void ScRowStyles::AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nStartField,
+ const sal_Int32 nStringIndex, const sal_Int32 nEndField)
+{
+ OSL_ENSURE( nStartField <= nEndField, "bad field range");
+ OSL_ENSURE(o3tl::make_unsigned(nTable) < aTables.size(), "wrong table");
+ StylesType& r = *aTables[nTable];
+ r.insert_back(nStartField, nEndField+1, nStringIndex);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLStylesExportHelper.hxx b/sc/source/filter/xml/XMLStylesExportHelper.hxx
new file mode 100644
index 000000000..844ffc418
--- /dev/null
+++ b/sc/source/filter/xml/XMLStylesExportHelper.hxx
@@ -0,0 +1,254 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <list>
+
+#include <address.hxx>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <com/sun/star/sheet/ConditionOperator.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+
+#include <mdds/flat_segment_tree.hpp>
+
+class ScDocument;
+class ScXMLExport;
+
+struct ScMyValidation
+{
+ OUString sName;
+ OUString sErrorMessage;
+ OUString sErrorTitle;
+ OUString sInputMessage;
+ OUString sInputTitle;
+ OUString sFormula1;
+ OUString sFormula2;
+ ScAddress aBaseCell;
+ css::sheet::ValidationAlertStyle aAlertStyle;
+ css::sheet::ValidationType aValidationType;
+ css::sheet::ConditionOperator aOperator;
+ sal_Int16 nShowList;
+ bool bShowErrorMessage;
+ bool bShowInputMessage;
+ bool bIgnoreBlanks;
+
+ ScMyValidation();
+
+ bool IsEqual(const ScMyValidation& aVal) const;
+};
+
+class ScMyValidationsContainer
+{
+private:
+ std::vector<ScMyValidation> aValidationVec;
+public:
+ ScMyValidationsContainer();
+ ~ScMyValidationsContainer();
+ void AddValidation(const css::uno::Any& aAny,
+ sal_Int32& nValidationIndex);
+ static OUString GetCondition(ScXMLExport& rExport, const ScMyValidation& aValidation);
+ static OUString GetBaseCellAddress(const ScDocument* pDoc, const ScAddress& aCell);
+ static void WriteMessage(ScXMLExport& rExport,
+ const OUString& sTitle, const OUString& sMessage,
+ const bool bShowMessage, const bool bIsHelpMessage);
+ void WriteValidations(ScXMLExport& rExport);
+ const OUString& GetValidationName(const sal_Int32 nIndex);
+};
+
+struct ScMyDefaultStyle
+{
+ sal_Int32 nIndex;
+ sal_Int32 nRepeat;
+ bool bIsAutoStyle;
+
+ ScMyDefaultStyle() : nIndex(-1), nRepeat(1),
+ bIsAutoStyle(true) {}
+};
+
+typedef std::vector<ScMyDefaultStyle> ScMyDefaultStyleList;
+
+class ScFormatRangeStyles;
+
+class ScMyDefaultStyles
+{
+ ScMyDefaultStyleList maColDefaults;
+
+ static sal_Int32 GetStyleNameIndex(const ScFormatRangeStyles* pCellStyles,
+ const sal_Int32 nTable, const sal_Int32 nPos,
+ const sal_Int32 i, bool& bIsAutoStyle);
+public:
+
+ void FillDefaultStyles(const sal_Int32 nTable,
+ const sal_Int32 nLastRow, const sal_Int32 nLastCol,
+ const ScFormatRangeStyles* pCellStyles, ScDocument* pDoc);
+
+ const ScMyDefaultStyleList& GetColDefaults() const { return maColDefaults; }
+};
+
+struct ScMyRowFormatRange
+{
+ sal_Int32 nStartColumn;
+ sal_Int32 nRepeatColumns;
+ sal_Int32 nRepeatRows;
+ sal_Int32 nIndex;
+ sal_Int32 nValidationIndex;
+ bool bIsAutoStyle;
+
+ ScMyRowFormatRange();
+ bool operator<(const ScMyRowFormatRange& rRange) const;
+};
+
+class ScRowFormatRanges
+{
+ typedef std::list<ScMyRowFormatRange> ScMyRowFormatRangesList;
+ ScMyRowFormatRangesList aRowFormatRanges;
+ const ScMyDefaultStyleList* pColDefaults;
+ sal_uInt32 nSize;
+
+ void AddRange(const sal_Int32 nPrevStartCol, const sal_Int32 nRepeat, const sal_Int32 nPrevIndex,
+ const bool bPrevAutoStyle, const ScMyRowFormatRange& rFormatRange);
+
+public:
+ ScRowFormatRanges();
+ explicit ScRowFormatRanges(const ScRowFormatRanges* pRanges);
+ ~ScRowFormatRanges();
+
+ void SetColDefaults(const ScMyDefaultStyleList* pDefaults) { pColDefaults = pDefaults; }
+ void Clear();
+ void AddRange(const ScMyRowFormatRange& rFormatRange);
+ bool GetNext(ScMyRowFormatRange& rFormatRange);
+ sal_Int32 GetMaxRows() const;
+ sal_Int32 GetSize() const { return nSize;}
+ void Sort();
+};
+
+
+struct ScMyFormatRange
+{
+ css::table::CellRangeAddress aRangeAddress;
+ sal_Int32 nStyleNameIndex;
+ sal_Int32 nValidationIndex;
+ sal_Int32 nNumberFormat;
+ bool bIsAutoStyle;
+
+ ScMyFormatRange();
+ bool operator< (const ScMyFormatRange& rRange) const;
+};
+
+class ScFormatRangeStyles
+{
+ typedef std::list<ScMyFormatRange> ScMyFormatRangeAddresses;
+ typedef std::vector<ScMyFormatRangeAddresses> ScMyFormatRangeListVec;
+
+ ScMyFormatRangeListVec aTables;
+ std::vector<OUString> aStyleNames;
+ std::vector<OUString> aAutoStyleNames;
+ const ScMyDefaultStyleList* pColDefaults;
+
+public:
+ ScFormatRangeStyles();
+ ~ScFormatRangeStyles();
+
+ void SetColDefaults(const ScMyDefaultStyleList* pDefaults) { pColDefaults = pDefaults; }
+ void AddNewTable(const sal_Int32 nTable);
+ bool AddStyleName(const OUString& rString, sal_Int32& rIndex, const bool bIsAutoStyle = true);
+ sal_Int32 GetIndexOfStyleName(std::u16string_view rString, const OUString& rPrefix, bool& bIsAutoStyle);
+ // does not delete ranges
+ sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nColumn, const sal_Int32 nRow,
+ bool& bIsAutoStyle) const;
+ // deletes not necessary ranges if wanted
+ sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nColumn, const sal_Int32 nRow,
+ bool& bIsAutoStyle, sal_Int32& nValidationIndex, sal_Int32& nNumberFormat, const sal_Int32 nRemoveBeforeRow);
+ void GetFormatRanges(const sal_Int32 nStartColumn, const sal_Int32 nEndColumn, const sal_Int32 nRow,
+ const sal_Int32 nTable, ScRowFormatRanges* pFormatRanges);
+ void AddRangeStyleName(const css::table::CellRangeAddress& rCellRangeAddress, const sal_Int32 nStringIndex,
+ const bool bIsAutoStyle, const sal_Int32 nValidationIndex, const sal_Int32 nNumberFormat);
+ OUString& GetStyleNameByIndex(const sal_Int32 nIndex, const bool bIsAutoStyle);
+ void Sort();
+};
+
+class ScColumnRowStylesBase
+{
+ std::vector<OUString> aStyleNames;
+
+public:
+ ScColumnRowStylesBase();
+ virtual ~ScColumnRowStylesBase();
+
+ virtual void AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields) = 0;
+ sal_Int32 AddStyleName(const OUString & rString);
+ sal_Int32 GetIndexOfStyleName(std::u16string_view rString, const OUString& rPrefix);
+ OUString& GetStyleNameByIndex(const sal_Int32 nIndex);
+};
+
+struct ScColumnStyle
+{
+ sal_Int32 nIndex;
+ bool bIsVisible;
+
+ ScColumnStyle() : nIndex(-1), bIsVisible(true) {}
+};
+
+class ScColumnStyles : public ScColumnRowStylesBase
+{
+ typedef std::vector<ScColumnStyle> ScMyColumnStyleVec;
+ typedef std::vector<ScMyColumnStyleVec> ScMyColumnVectorVec;
+ ScMyColumnVectorVec aTables;
+
+public:
+ ScColumnStyles();
+ virtual ~ScColumnStyles() override;
+
+ virtual void AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields) override;
+ sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nField,
+ bool& bIsVisible);
+ void AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nField, const sal_Int32 nStringIndex, const bool bIsVisible);
+};
+
+class ScRowStyles : public ScColumnRowStylesBase
+{
+ typedef ::mdds::flat_segment_tree<sal_Int32, sal_Int32> StylesType;
+ std::vector<std::unique_ptr<StylesType> > aTables;
+ struct Cache
+ {
+ sal_Int32 mnTable;
+ sal_Int32 mnStart;
+ sal_Int32 mnEnd;
+ sal_Int32 mnStyle;
+ Cache();
+
+ bool hasCache(sal_Int32 nTable, sal_Int32 nField) const;
+ };
+ Cache maCache;
+
+public:
+ ScRowStyles();
+ virtual ~ScRowStyles() override;
+
+ virtual void AddNewTable(const sal_Int32 nTable, const sal_Int32 nFields) override;
+ sal_Int32 GetStyleNameIndex(const sal_Int32 nTable, const sal_Int32 nField);
+ void AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nField, const sal_Int32 nStringIndex);
+ void AddFieldStyleName(const sal_Int32 nTable, const sal_Int32 nStartField, const sal_Int32 nStringIndex, const sal_Int32 nEndField);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLStylesImportHelper.cxx b/sc/source/filter/xml/XMLStylesImportHelper.cxx
new file mode 100644
index 000000000..e52466a54
--- /dev/null
+++ b/sc/source/filter/xml/XMLStylesImportHelper.cxx
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLStylesImportHelper.hxx"
+#include "xmlimprt.hxx"
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+using ::std::list;
+
+void ScMyStyleNumberFormats::AddStyleNumberFormat(const OUString& rStyleName, const sal_Int32 nNumberFormat)
+{
+ aSet.insert(ScMyStyleNumberFormat(rStyleName, nNumberFormat));
+}
+
+sal_Int32 ScMyStyleNumberFormats::GetStyleNumberFormat(const OUString& rStyleName)
+{
+ ScMyStyleNumberFormat aStyleNumberFormat(rStyleName);
+ ScMyStyleNumberFormatSet::iterator aItr(aSet.find(aStyleNumberFormat));
+ if (aItr == aSet.end())
+ return -1;
+ else
+ return aItr->nNumberFormat;
+}
+
+ScMyStyleRanges::ScMyStyleRanges()
+{
+}
+
+ScMyStyleRanges::~ScMyStyleRanges()
+{
+}
+
+void ScMyStyleRanges::AddRange(const ScRange& rRange, const sal_Int16 nType)
+{
+ switch (nType)
+ {
+ case util::NumberFormat::NUMBER:
+ {
+ if (!mpNumberList)
+ mpNumberList = std::make_shared<ScRangeList>();
+ mpNumberList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::TEXT:
+ {
+ if (!mpTextList)
+ mpTextList = std::make_shared<ScRangeList>();
+ mpTextList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::TIME:
+ {
+ if (!mpTimeList)
+ mpTimeList = std::make_shared<ScRangeList>();
+ mpTimeList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::DATETIME:
+ {
+ if (!mpDateTimeList)
+ mpDateTimeList = std::make_shared<ScRangeList>();
+ mpDateTimeList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::PERCENT:
+ {
+ if (!mpPercentList)
+ mpPercentList = std::make_shared<ScRangeList>();
+ mpPercentList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::LOGICAL:
+ {
+ if (!mpLogicalList)
+ mpLogicalList = std::make_shared<ScRangeList>();
+ mpLogicalList->AddAndPartialCombine(rRange);
+ }
+ break;
+ case util::NumberFormat::UNDEFINED:
+ {
+ if (!mpUndefinedList)
+ mpUndefinedList = std::make_shared<ScRangeList>();
+ mpUndefinedList->AddAndPartialCombine(rRange);
+ }
+ break;
+ default:
+ {
+ OSL_FAIL("wrong type");
+ }
+ break;
+ }
+}
+
+void ScMyStyleRanges::AddCurrencyRange(const ScRange& rRange, const std::optional<OUString> & pCurrency)
+{
+ if (!pCurrencyList)
+ pCurrencyList.reset( new ScMyCurrencyStylesSet );
+ ScMyCurrencyStyle aStyle;
+ if (pCurrency)
+ aStyle.sCurrency = *pCurrency;
+ auto itPair = pCurrencyList->insert(aStyle);
+ itPair.first->mpRanges->AddAndPartialCombine(rRange);
+}
+
+void ScMyStyleRanges::InsertCol(const sal_Int32 nCol, const sal_Int32 nTab)
+{
+ if (mpTextList)
+ mpTextList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpNumberList)
+ mpNumberList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpTimeList)
+ mpTimeList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpDateTimeList)
+ mpDateTimeList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpPercentList)
+ mpPercentList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpLogicalList)
+ mpLogicalList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+ if (mpUndefinedList)
+ mpUndefinedList->InsertCol(static_cast<SCCOL>(nTab), static_cast<SCTAB>(nCol));
+
+ if (pCurrencyList)
+ {
+ for (auto& rCurrency : *pCurrencyList)
+ {
+ rCurrency.mpRanges->InsertCol(static_cast<SCCOL>(nCol), static_cast<SCTAB>(nTab));
+ }
+ }
+}
+
+void ScMyStyleRanges::SetStylesToRanges(const ScRangeList& rRanges,
+ const OUString* pStyleName, const sal_Int16 nCellType,
+ const OUString* pCurrency, ScXMLImport& rImport)
+{
+ rImport.SetStyleToRanges(rRanges, pStyleName, nCellType, pCurrency);
+}
+
+void ScMyStyleRanges::SetStylesToRanges(const OUString* pStyleName, ScXMLImport& rImport)
+{
+ if (mpNumberList)
+ {
+ SetStylesToRanges(*mpNumberList, pStyleName, util::NumberFormat::NUMBER, nullptr, rImport);
+ mpNumberList.reset();
+ }
+ if (mpTextList)
+ {
+ SetStylesToRanges(*mpTextList, pStyleName, util::NumberFormat::TEXT, nullptr, rImport);
+ mpTextList.reset();
+ }
+ if (mpTimeList)
+ {
+ SetStylesToRanges(*mpTimeList, pStyleName, util::NumberFormat::TIME, nullptr, rImport);
+ mpTimeList.reset();
+ }
+ if (mpDateTimeList)
+ {
+ SetStylesToRanges(*mpDateTimeList, pStyleName, util::NumberFormat::DATETIME, nullptr, rImport);
+ mpDateTimeList.reset();
+ }
+ if (mpPercentList)
+ {
+ SetStylesToRanges(*mpPercentList, pStyleName, util::NumberFormat::PERCENT, nullptr, rImport);
+ mpPercentList.reset();
+ }
+ if (mpLogicalList)
+ {
+ SetStylesToRanges(*mpLogicalList, pStyleName, util::NumberFormat::LOGICAL, nullptr, rImport);
+ mpLogicalList.reset();
+ }
+ if (mpUndefinedList)
+ {
+ SetStylesToRanges(*mpUndefinedList, pStyleName, util::NumberFormat::UNDEFINED, nullptr, rImport);
+ mpUndefinedList.reset();
+ }
+ if (pCurrencyList)
+ {
+ for (const auto& rCurrency : *pCurrencyList)
+ {
+ SetStylesToRanges(*rCurrency.mpRanges, pStyleName, util::NumberFormat::CURRENCY, &rCurrency.sCurrency, rImport);
+ }
+ }
+}
+
+ScMyStylesImportHelper::ScMyStylesImportHelper(ScXMLImport& rTempImport)
+ :
+ aRowDefaultStyle(aCellStyles.end()),
+ rImport(rTempImport),
+ nCellType(0),
+ nPrevCellType(0),
+ bPrevRangeAdded(true)
+{
+}
+
+ScMyStylesImportHelper::~ScMyStylesImportHelper()
+{
+}
+
+void ScMyStylesImportHelper::ResetAttributes()
+{
+ pPrevStyleName = std::move(pStyleName);
+ pPrevCurrency = std::move(pCurrency);
+ nPrevCellType = nCellType;
+ nCellType = 0;
+}
+
+ScMyStylesMap::iterator ScMyStylesImportHelper::GetIterator(const OUString & rStyleName)
+{
+ auto it = aCellStyles.find(rStyleName);
+ if (it == aCellStyles.end())
+ it = aCellStyles.emplace_hint(it, rStyleName, std::make_unique<ScMyStyleRanges>());
+ return it;
+}
+
+void ScMyStylesImportHelper::AddDefaultRange(const ScRange& rRange)
+{
+ OSL_ENSURE(aRowDefaultStyle != aCellStyles.end(), "no row default style");
+ if (aRowDefaultStyle->first.isEmpty())
+ {
+ SCCOL nStartCol(rRange.aStart.Col());
+ SCCOL nEndCol(rRange.aEnd.Col());
+ if (aColDefaultStyles.size() > sal::static_int_cast<sal_uInt32>(nStartCol))
+ {
+ ScMyStylesMap::iterator aPrevItr(aColDefaultStyles[nStartCol]);
+ for (SCCOL i = nStartCol + 1; (i <= nEndCol) && (i < sal::static_int_cast<SCCOL>(aColDefaultStyles.size())); ++i)
+ {
+ if (aPrevItr != aColDefaultStyles[i])
+ {
+ OSL_ENSURE(aPrevItr != aCellStyles.end(), "no column default style");
+ ScRange aRange(rRange);
+ aRange.aStart.SetCol(nStartCol);
+ aRange.aEnd.SetCol(i - 1);
+ pPrevStyleName = aPrevItr->first;
+ AddSingleRange(aRange);
+ nStartCol = i;
+ aPrevItr = aColDefaultStyles[i];
+ }
+ }
+ if (aPrevItr != aCellStyles.end())
+ {
+ ScRange aRange(rRange);
+ aRange.aStart.SetCol(nStartCol);
+ pPrevStyleName = aPrevItr->first;
+ AddSingleRange(aRange);
+ }
+ else
+ {
+ OSL_FAIL("no column default style");
+ }
+ }
+ else
+ {
+ OSL_FAIL("too many columns");
+ }
+ }
+ else
+ {
+ pPrevStyleName = aRowDefaultStyle->first;
+ AddSingleRange(rRange);
+ }
+}
+
+void ScMyStylesImportHelper::AddSingleRange(const ScRange& rRange)
+{
+ ScMyStylesMap::iterator aItr(GetIterator(*pPrevStyleName));
+ if (nPrevCellType != util::NumberFormat::CURRENCY)
+ aItr->second->AddRange(rRange, nPrevCellType);
+ else
+ aItr->second->AddCurrencyRange(rRange, pPrevCurrency);
+}
+
+void ScMyStylesImportHelper::AddRange()
+{
+ if (pPrevStyleName && !pPrevStyleName->isEmpty())
+ AddSingleRange(aPrevRange);
+ else
+ AddDefaultRange(aPrevRange);
+ ResetAttributes();
+}
+
+void ScMyStylesImportHelper::AddColumnStyle(const OUString& sStyleName, const sal_Int32 nColumn, const sal_Int32 nRepeat)
+{
+ OSL_ENSURE(static_cast<sal_uInt32>(nColumn) == aColDefaultStyles.size(), "some columns are absent");
+ ScMyStylesMap::iterator aItr(GetIterator(sStyleName));
+ aColDefaultStyles.reserve(aColDefaultStyles.size() + nRepeat);
+ for (sal_Int32 i = 0; i < nRepeat; ++i)
+ aColDefaultStyles.push_back(aItr);
+}
+
+void ScMyStylesImportHelper::SetRowStyle(const OUString& sStyleName)
+{
+ aRowDefaultStyle = GetIterator(sStyleName);
+}
+
+void ScMyStylesImportHelper::SetAttributes(std::optional<OUString> pStyleNameP,
+ std::optional<OUString> pCurrencyP, const sal_Int16 nCellTypeP)
+{
+ pStyleName = std::move(pStyleNameP);
+ pCurrency = std::move(pCurrencyP);
+ nCellType = nCellTypeP;
+}
+
+void ScMyStylesImportHelper::AddRange(const ScRange& rRange)
+{
+ if (!bPrevRangeAdded)
+ {
+ bool bAddRange(false);
+ if (nCellType == nPrevCellType &&
+ pStyleName == pPrevStyleName &&
+ pCurrency == pPrevCurrency)
+ {
+ if (rRange.aStart.Row() == aPrevRange.aStart.Row())
+ {
+ if (rRange.aEnd.Row() == aPrevRange.aEnd.Row())
+ {
+ OSL_ENSURE(aPrevRange.aEnd.Col() + 1 == rRange.aStart.Col(), "something went wrong");
+ aPrevRange.aEnd.SetCol(rRange.aEnd.Col());
+ }
+ else
+ bAddRange = true;
+ }
+ else
+ {
+ if (rRange.aStart.Col() == aPrevRange.aStart.Col() &&
+ rRange.aEnd.Col() == aPrevRange.aEnd.Col())
+ {
+ OSL_ENSURE(aPrevRange.aEnd.Row() + 1 == rRange.aStart.Row(), "something went wrong");
+ aPrevRange.aEnd.SetRow(rRange.aEnd.Row());
+ }
+ else
+ bAddRange = true;
+ }
+ }
+ else
+ bAddRange = true;
+ if (bAddRange)
+ {
+ AddRange();
+ aPrevRange = rRange;
+ }
+ }
+ else
+ {
+ aPrevRange = rRange;
+ ResetAttributes();
+ bPrevRangeAdded = false;
+ }
+}
+
+void ScMyStylesImportHelper::AddCell(const ScAddress& rAddress)
+{
+ ScRange aScRange( rAddress, rAddress );
+ AddRange(aScRange);
+}
+
+void ScMyStylesImportHelper::InsertCol(const sal_Int32 nCol, const sal_Int32 nTab)
+{
+ ScXMLImport::MutexGuard aGuard(rImport);
+ for (auto& rCellStyle : aCellStyles)
+ {
+ rCellStyle.second->InsertCol(nCol, nTab);
+ }
+}
+
+void ScMyStylesImportHelper::EndTable()
+{
+ if (!bPrevRangeAdded)
+ {
+ AddRange();
+ bPrevRangeAdded = true;
+ }
+}
+
+void ScMyStylesImportHelper::SetStylesToRanges()
+{
+ for (auto& rCellStyle : aCellStyles)
+ {
+ rCellStyle.second->SetStylesToRanges(&rCellStyle.first, rImport);
+ }
+ aColDefaultStyles.clear();
+ aCellStyles.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLStylesImportHelper.hxx b/sc/source/filter/xml/XMLStylesImportHelper.hxx
new file mode 100644
index 000000000..6f51b4584
--- /dev/null
+++ b/sc/source/filter/xml/XMLStylesImportHelper.hxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rangelst.hxx>
+#include <rtl/ustring.hxx>
+
+#include <memory>
+#include <set>
+#include <map>
+#include <vector>
+#include <optional>
+
+class ScXMLImport;
+
+struct ScMyStyleNumberFormat
+{
+ OUString sStyleName;
+ sal_Int32 nNumberFormat;
+
+ explicit ScMyStyleNumberFormat(const OUString& rStyleName) :
+ sStyleName(rStyleName), nNumberFormat(-1) {}
+ ScMyStyleNumberFormat(const OUString& rStyleName, const sal_Int32 nFormat) :
+ sStyleName(rStyleName), nNumberFormat(nFormat) {}
+};
+
+struct LessStyleNumberFormat
+{
+ bool operator() (const ScMyStyleNumberFormat& rValue1, const ScMyStyleNumberFormat& rValue2) const
+ {
+ return rValue1.sStyleName < rValue2.sStyleName;
+ }
+};
+
+typedef std::set< ScMyStyleNumberFormat, LessStyleNumberFormat > ScMyStyleNumberFormatSet;
+
+class ScMyStyleNumberFormats
+{
+ ScMyStyleNumberFormatSet aSet;
+
+public:
+ void AddStyleNumberFormat(const OUString& rStyleName, const sal_Int32 nNumberFormat);
+ sal_Int32 GetStyleNumberFormat(const OUString& rStyleName);
+};
+
+struct ScMyCurrencyStyle
+{
+ OUString sCurrency;
+ std::shared_ptr<ScRangeList> mpRanges;
+
+ ScMyCurrencyStyle() :
+ mpRanges(std::make_shared<ScRangeList>())
+ {}
+};
+
+struct LessCurrencyStyle
+{
+ bool operator() (const ScMyCurrencyStyle& rValue1, const ScMyCurrencyStyle& rValue2) const
+ {
+ return rValue1.sCurrency < rValue2.sCurrency;
+ }
+};
+
+typedef std::set<ScMyCurrencyStyle, LessCurrencyStyle> ScMyCurrencyStylesSet;
+
+class ScMyStyleRanges
+{
+ std::shared_ptr<ScRangeList> mpTextList;
+ std::shared_ptr<ScRangeList> mpNumberList;
+ std::shared_ptr<ScRangeList> mpTimeList;
+ std::shared_ptr<ScRangeList> mpDateTimeList;
+ std::shared_ptr<ScRangeList> mpPercentList;
+ std::shared_ptr<ScRangeList> mpLogicalList;
+ std::shared_ptr<ScRangeList> mpUndefinedList;
+ std::unique_ptr<ScMyCurrencyStylesSet> pCurrencyList;
+
+ static void SetStylesToRanges(const ScRangeList& rList,
+ const OUString* pStyleName, const sal_Int16 nCellType,
+ const OUString* pCurrency, ScXMLImport& rImport);
+public:
+ ScMyStyleRanges();
+ ~ScMyStyleRanges();
+ void AddRange(const ScRange& rRange, const sal_Int16 nType);
+ void AddCurrencyRange(const ScRange& rRange, const std::optional<OUString> & pCurrency);
+ void InsertCol(const sal_Int32 nCol, const sal_Int32 nTab);
+ void SetStylesToRanges(const OUString* pStyleName, ScXMLImport& rImport);
+};
+
+/** map from style name to ScMyStyleRanges */
+typedef std::map<OUString, std::unique_ptr<ScMyStyleRanges>> ScMyStylesMap;
+
+class ScMyStylesImportHelper
+{
+ ScMyStylesMap aCellStyles;
+ std::vector<ScMyStylesMap::iterator> aColDefaultStyles;
+ ScMyStylesMap::iterator aRowDefaultStyle;
+ ScXMLImport& rImport;
+ std::optional<OUString>
+ pStyleName;
+ std::optional<OUString>
+ pPrevStyleName;
+ std::optional<OUString>
+ pCurrency;
+ std::optional<OUString>
+ pPrevCurrency;
+ ScRange aPrevRange;
+ sal_Int16 nCellType;
+ sal_Int16 nPrevCellType;
+ bool bPrevRangeAdded;
+
+ void ResetAttributes();
+ ScMyStylesMap::iterator GetIterator(const OUString & rStyleName);
+ void AddDefaultRange(const ScRange& rRange);
+ void AddSingleRange(const ScRange& rRange);
+ void AddRange();
+public:
+ explicit ScMyStylesImportHelper(ScXMLImport& rImport);
+ ~ScMyStylesImportHelper();
+ void AddColumnStyle(const OUString& rStyleName, const sal_Int32 nColumn, const sal_Int32 nRepeat);
+ void SetRowStyle(const OUString& rStyleName);
+ void SetAttributes(std::optional<OUString> pStyleName,
+ std::optional<OUString> pCurrency, const sal_Int16 nCellType);
+ void AddRange(const ScRange& rRange);
+ void AddCell(const ScAddress& rAddress);
+ void InsertCol(const sal_Int32 nCol, const sal_Int32 nTab); // a col is inserted before nCol
+ void EndTable();
+ void SetStylesToRanges();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableHeaderFooterContext.cxx b/sc/source/filter/xml/XMLTableHeaderFooterContext.cxx
new file mode 100644
index 000000000..99987a449
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableHeaderFooterContext.cxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/sheet/XHeaderFooterContent.hpp>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlimp.hxx>
+#include "XMLTableHeaderFooterContext.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <comphelper/extract.hxx>
+#include <sal/log.hxx>
+
+#include <unonames.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::beans;
+using namespace xmloff::token;
+
+
+XMLTableHeaderFooterContext::XMLTableHeaderFooterContext( SvXMLImport& rImport, sal_Int32 /*nElement*/,
+ const uno::Reference<
+ xml::sax::XFastAttributeList > & xAttrList,
+ const Reference < XPropertySet > & rPageStylePropSet,
+ bool bFooter, bool bLeft, bool bFirst ) :
+ SvXMLImportContext( rImport ),
+ xPropSet( rPageStylePropSet ),
+ bContainsLeft(false),
+ bContainsRight(false),
+ bContainsCenter(false)
+{
+ OUString sOn( bFooter ? OUString(SC_UNO_PAGE_FTRON) : OUString(SC_UNO_PAGE_HDRON) );
+ OUString sContent( bFooter ? OUString(SC_UNO_PAGE_RIGHTFTRCON) : OUString(SC_UNO_PAGE_RIGHTHDRCON) );
+ OUString sContentLeft( bFooter ? OUString(SC_UNO_PAGE_LEFTFTRCONT) : OUString(SC_UNO_PAGE_LEFTHDRCONT) );
+ OUString sContentFirst( bFooter ? OUString(SC_UNO_PAGE_FIRSTFTRCONT) : OUString(SC_UNO_PAGE_FIRSTHDRCONT) );
+ OUString sShareContent( bFooter ? OUString(SC_UNO_PAGE_FTRSHARED) : OUString(SC_UNO_PAGE_HDRSHARED) );
+ OUString sShareFirstContent( bFooter ? OUString(SC_UNO_PAGE_FIRSTFTRSHARED) : OUString(SC_UNO_PAGE_FIRSTHDRSHARED) );
+ bool bDisplay( true );
+ for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
+ {
+ if( aIter.getToken() == XML_ELEMENT(STYLE, XML_DISPLAY) )
+ bDisplay = IsXMLToken(aIter, XML_TRUE);
+ else
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ bool bOn(::cppu::any2bool(xPropSet->getPropertyValue( sOn )));
+ if( bLeft || bFirst )
+ {
+ const OUString sShare = bLeft ? sShareContent : sShareFirstContent;
+ if( bOn && bDisplay )
+ {
+ if( ::cppu::any2bool(xPropSet->getPropertyValue( sShare )) )
+ // Don't share headers any longer
+ xPropSet->setPropertyValue( sShare, uno::Any(false) );
+ }
+ else
+ {
+ if( !::cppu::any2bool(xPropSet->getPropertyValue( sShare )) )
+ // share headers
+ xPropSet->setPropertyValue( sShare, uno::Any(true) );
+ }
+ }
+ else
+ {
+ if ( bOn != bDisplay )
+ xPropSet->setPropertyValue( sOn, uno::Any(bDisplay) );
+ }
+ if (bLeft)
+ {
+ sCont = sContentLeft;
+ }
+ else if (bFirst)
+ {
+ sCont = sContentFirst;
+ xPropSet->setPropertyValue( sShareFirstContent, uno::Any(!bDisplay) );
+ }
+ else
+ {
+ sCont = sContent;
+ }
+ xPropSet->getPropertyValue( sCont ) >>= xHeaderFooterContent;
+}
+
+XMLTableHeaderFooterContext::~XMLTableHeaderFooterContext()
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTableHeaderFooterContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ if (xHeaderFooterContent.is())
+ {
+ uno::Reference < text::XText > xText;
+ switch (nElement)
+ {
+ case XML_ELEMENT(STYLE, XML_REGION_LEFT):
+ xText.set(xHeaderFooterContent->getLeftText());
+ bContainsLeft = true;
+ break;
+ case XML_ELEMENT(STYLE, XML_REGION_CENTER):
+ xText.set(xHeaderFooterContent->getCenterText());
+ bContainsCenter = true;
+ break;
+ case XML_ELEMENT(STYLE, XML_REGION_RIGHT):
+ xText.set(xHeaderFooterContent->getRightText());
+ bContainsRight = true;
+ break;
+ default: break;
+ }
+ if (xText.is())
+ {
+ xText->setString("");
+ uno::Reference < text::XTextCursor > xTempTextCursor(xText->createTextCursor());
+ return new XMLHeaderFooterRegionContext( GetImport(), xTempTextCursor);
+ }
+ }
+
+ if ( nElement == XML_ELEMENT(TEXT, XML_P) )
+ {
+ if (!xTextCursor.is())
+ {
+ if( xHeaderFooterContent.is() )
+ {
+ uno::Reference < text::XText > xText(xHeaderFooterContent->getCenterText());
+ xText->setString("");
+ xTextCursor.set(xText->createTextCursor());
+ xOldTextCursor.set(GetImport().GetTextImport()->GetCursor());
+ GetImport().GetTextImport()->SetCursor( xTextCursor );
+ bContainsCenter = true;
+ }
+ }
+ return
+ GetImport().GetTextImport()->CreateTextChildContext(GetImport(),
+ nElement,
+ xAttrList);
+ }
+
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ return nullptr;
+}
+
+void XMLTableHeaderFooterContext::endFastElement(sal_Int32 )
+{
+ if( GetImport().GetTextImport()->GetCursor().is() )
+ {
+ //GetImport().GetTextImport()->GetCursor()->gotoEnd(sal_False);
+ if( GetImport().GetTextImport()->GetCursor()->goLeft( 1, true ) )
+ {
+ GetImport().GetTextImport()->GetText()->insertString(
+ GetImport().GetTextImport()->GetCursorAsRange(), "",
+ true );
+ }
+ GetImport().GetTextImport()->ResetCursor();
+ }
+ if (xOldTextCursor.is())
+ GetImport().GetTextImport()->SetCursor(xOldTextCursor);
+ if (xHeaderFooterContent.is())
+ {
+ if (!bContainsLeft)
+ xHeaderFooterContent->getLeftText()->setString("");
+ if (!bContainsCenter)
+ xHeaderFooterContent->getCenterText()->setString("");
+ if (!bContainsRight)
+ xHeaderFooterContent->getRightText()->setString("");
+
+ xPropSet->setPropertyValue( sCont, uno::Any(xHeaderFooterContent) );
+ }
+}
+
+
+XMLHeaderFooterRegionContext::XMLHeaderFooterRegionContext( SvXMLImport& rImport,
+ uno::Reference< text::XTextCursor >& xCursor ) :
+ SvXMLImportContext( rImport ),
+ xTextCursor ( xCursor )
+{
+ xOldTextCursor.set(GetImport().GetTextImport()->GetCursor());
+ GetImport().GetTextImport()->SetCursor( xTextCursor );
+}
+
+XMLHeaderFooterRegionContext::~XMLHeaderFooterRegionContext()
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > XMLHeaderFooterRegionContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ if (nElement == XML_ELEMENT(TEXT, XML_P))
+ {
+ return GetImport().GetTextImport()->CreateTextChildContext(GetImport(),
+ nElement,
+ xAttrList);
+ }
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ return pContext;
+}
+
+void XMLHeaderFooterRegionContext::endFastElement(sal_Int32 )
+{
+ if( GetImport().GetTextImport()->GetCursor().is() )
+ {
+ //GetImport().GetTextImport()->GetCursor()->gotoEnd(sal_False);
+ if( GetImport().GetTextImport()->GetCursor()->goLeft( 1, true ) )
+ {
+ GetImport().GetTextImport()->GetText()->insertString(
+ GetImport().GetTextImport()->GetCursorAsRange(), "",
+ true );
+ }
+ GetImport().GetTextImport()->ResetCursor();
+ }
+ if (xOldTextCursor.is())
+ GetImport().GetTextImport()->SetCursor(xOldTextCursor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableHeaderFooterContext.hxx b/sc/source/filter/xml/XMLTableHeaderFooterContext.hxx
new file mode 100644
index 000000000..ccb6a154c
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableHeaderFooterContext.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <xmloff/xmlictxt.hxx>
+
+namespace com::sun::star {
+ namespace text { class XTextCursor; }
+ namespace beans { class XPropertySet; }
+}
+
+namespace com::sun::star::sheet { class XHeaderFooterContent; }
+
+class XMLTableHeaderFooterContext: public SvXMLImportContext
+{
+ css::uno::Reference< css::text::XTextCursor > xTextCursor;
+ css::uno::Reference< css::text::XTextCursor > xOldTextCursor;
+ css::uno::Reference< css::beans::XPropertySet > xPropSet;
+ css::uno::Reference< css::sheet::XHeaderFooterContent > xHeaderFooterContent;
+
+ OUString sCont;
+
+ bool bContainsLeft;
+ bool bContainsRight;
+ bool bContainsCenter;
+
+public:
+
+ XMLTableHeaderFooterContext( SvXMLImport& rImport, sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ const css::uno::Reference< css::beans::XPropertySet > & rPageStylePropSet,
+ bool bFooter, bool bLft, bool bFirst );
+
+ virtual ~XMLTableHeaderFooterContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class XMLHeaderFooterRegionContext: public SvXMLImportContext
+{
+private:
+ css::uno::Reference< css::text::XTextCursor >& xTextCursor;
+ css::uno::Reference< css::text::XTextCursor > xOldTextCursor;
+
+public:
+
+ XMLHeaderFooterRegionContext( SvXMLImport& rImport,
+ css::uno::Reference< css::text::XTextCursor >& xCursor );
+
+ virtual ~XMLHeaderFooterRegionContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableMasterPageExport.cxx b/sc/source/filter/xml/XMLTableMasterPageExport.cxx
new file mode 100644
index 000000000..61899e651
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableMasterPageExport.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XHeaderFooterContent.hpp>
+#include "XMLTableMasterPageExport.hxx"
+#include <comphelper/extract.hxx>
+#include <rtl/ref.hxx>
+#include <osl/diagnose.h>
+
+#include <unonames.hxx>
+#include "xmlexprt.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::beans;
+using namespace xmloff::token;
+
+XMLTableMasterPageExport::XMLTableMasterPageExport( ScXMLExport& rExp ) :
+ XMLTextMasterPageExport ( rExp )
+{
+}
+
+XMLTableMasterPageExport::~XMLTableMasterPageExport()
+{
+}
+
+void XMLTableMasterPageExport::exportHeaderFooterContent(
+ const Reference< XText >& rText,
+ bool bAutoStyles, bool bProgress )
+{
+ OSL_ENSURE( rText.is(), "There is the text" );
+
+ if( bAutoStyles )
+ GetExport().GetTextParagraphExport()
+ ->collectTextAutoStyles( rText, bProgress, false );
+ else
+ {
+ GetExport().GetTextParagraphExport()->exportTextDeclarations( rText );
+ GetExport().GetTextParagraphExport()->exportText( rText, bProgress, false );
+ }
+}
+
+void XMLTableMasterPageExport::exportHeaderFooter(const css::uno::Reference < css::sheet::XHeaderFooterContent >& xHeaderFooter,
+ const XMLTokenEnum aName,
+ const bool bDisplay)
+{
+ if( !xHeaderFooter.is() )
+ return;
+
+ sal_uInt16 nNameSpace = XML_NAMESPACE_STYLE;
+ if (aName == XML_HEADER_FIRST || aName == XML_FOOTER_FIRST)
+ {
+ // Since ODF 1.3 OFFICE-3789 or 1.2-extended.
+ auto const nVersion(GetExport().getSaneDefaultVersion());
+ if (nVersion <= SvtSaveOptions::ODFSVER_012)
+ return;
+ if (nVersion < SvtSaveOptions::ODFSVER_013)
+ nNameSpace = XML_NAMESPACE_LO_EXT;
+ }
+
+ Reference < XText > xCenter(xHeaderFooter->getCenterText());
+ Reference < XText > xLeft(xHeaderFooter->getLeftText());
+ Reference < XText > xRight(xHeaderFooter->getRightText());
+ if (!(xCenter.is() && xLeft.is() && xRight.is()))
+ return;
+
+ OUString sCenter (xCenter->getString());
+ OUString sLeft (xLeft->getString());
+ OUString sRight (xRight->getString());
+
+ if( !bDisplay )
+ GetExport().AddAttribute( XML_NAMESPACE_STYLE,
+ XML_DISPLAY, XML_FALSE );
+ SvXMLElementExport aElem( GetExport(), nNameSpace,
+ aName, true, true );
+ if (!sCenter.isEmpty() && sLeft.isEmpty() && sRight.isEmpty())
+ exportHeaderFooterContent( xCenter, false, false );
+ else
+ {
+ if (!sLeft.isEmpty())
+ {
+ SvXMLElementExport aSubElem( GetExport(), XML_NAMESPACE_STYLE,
+ XML_REGION_LEFT, true, true );
+ exportHeaderFooterContent( xLeft, false, false );
+ }
+ if (!sCenter.isEmpty())
+ {
+ SvXMLElementExport aSubElem( GetExport(), XML_NAMESPACE_STYLE,
+ XML_REGION_CENTER, true, true );
+ exportHeaderFooterContent( xCenter, false, false );
+ }
+ if (!sRight.isEmpty())
+ {
+ SvXMLElementExport aSubElem( GetExport(), XML_NAMESPACE_STYLE,
+ XML_REGION_RIGHT, true, true );
+ exportHeaderFooterContent( xRight, false, false );
+ }
+ }
+}
+
+void XMLTableMasterPageExport::exportMasterPageContent(
+ const Reference < XPropertySet > & rPropSet,
+ bool bAutoStyles )
+{
+ Reference < sheet::XHeaderFooterContent > xHeader(rPropSet->getPropertyValue( SC_UNO_PAGE_RIGHTHDRCON ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xHeaderLeft(rPropSet->getPropertyValue( SC_UNO_PAGE_LEFTHDRCONT ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xHeaderFirst(rPropSet->getPropertyValue( SC_UNO_PAGE_FIRSTHDRCONT ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xFooter(rPropSet->getPropertyValue( SC_UNO_PAGE_RIGHTFTRCON ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xFooterLeft(rPropSet->getPropertyValue( SC_UNO_PAGE_LEFTFTRCONT ), uno::UNO_QUERY);
+
+ Reference < sheet::XHeaderFooterContent > xFooterFirst(rPropSet->getPropertyValue( SC_UNO_PAGE_FIRSTFTRCONT ), uno::UNO_QUERY);
+
+ if( bAutoStyles )
+ {
+ if( xHeader.is() )
+ {
+ exportHeaderFooterContent( xHeader->getCenterText(), true, false );
+ exportHeaderFooterContent( xHeader->getLeftText(), true, false );
+ exportHeaderFooterContent( xHeader->getRightText(), true, false );
+ }
+ if( xHeaderLeft.is())
+ {
+ exportHeaderFooterContent( xHeaderLeft->getCenterText(), true, false );
+ exportHeaderFooterContent( xHeaderLeft->getLeftText(), true, false );
+ exportHeaderFooterContent( xHeaderLeft->getRightText(), true, false );
+ }
+ if( xHeaderFirst.is())
+ {
+ exportHeaderFooterContent( xHeaderFirst->getCenterText(), true, false );
+ exportHeaderFooterContent( xHeaderFirst->getLeftText(), true, false );
+ exportHeaderFooterContent( xHeaderFirst->getRightText(), true, false );
+ }
+ if( xFooter.is() )
+ {
+ exportHeaderFooterContent( xFooter->getCenterText(), true, false );
+ exportHeaderFooterContent( xFooter->getLeftText(), true, false );
+ exportHeaderFooterContent( xFooter->getRightText(), true, false );
+ }
+ if( xFooterLeft.is())
+ {
+ exportHeaderFooterContent( xFooterLeft->getCenterText(), true, false );
+ exportHeaderFooterContent( xFooterLeft->getLeftText(), true, false );
+ exportHeaderFooterContent( xFooterLeft->getRightText(), true, false );
+ }
+ if( xFooterFirst.is())
+ {
+ exportHeaderFooterContent( xFooterFirst->getCenterText(), true, false );
+ exportHeaderFooterContent( xFooterFirst->getLeftText(), true, false );
+ exportHeaderFooterContent( xFooterFirst->getRightText(), true, false );
+ }
+ }
+ else
+ {
+ bool bHeader(::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_HDRON )));
+
+ exportHeaderFooter(xHeader, XML_HEADER, bHeader );
+
+ bool bLeftHeader(!::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_HDRSHARED )) && bHeader);
+
+ exportHeaderFooter( xHeaderLeft, XML_HEADER_LEFT, bLeftHeader );
+
+ bool bFirstHeader(!::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_FIRSTHDRSHARED )) && bHeader);
+
+ exportHeaderFooter( xHeaderFirst, XML_HEADER_FIRST, bFirstHeader );
+
+ bool bFooter(::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_FTRON )));
+
+ exportHeaderFooter( xFooter, XML_FOOTER, bFooter );
+
+ bool bLeftFooter = (!::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_FTRSHARED )) && bFooter);
+
+ exportHeaderFooter( xFooterLeft, XML_FOOTER_LEFT, bLeftFooter );
+
+ bool bFirstFooter = (!::cppu::any2bool(rPropSet->getPropertyValue( SC_UNO_PAGE_FIRSTFTRSHARED )) && bFooter);
+
+ exportHeaderFooter( xFooterFirst, XML_FOOTER_FIRST, bFirstFooter );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableMasterPageExport.hxx b/sc/source/filter/xml/XMLTableMasterPageExport.hxx
new file mode 100644
index 000000000..2ae7a127e
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableMasterPageExport.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/XMLTextMasterPageExport.hxx>
+
+namespace com::sun::star {
+ namespace text { class XText; }
+}
+
+namespace com::sun::star::sheet { class XHeaderFooterContent; }
+
+class ScXMLExport;
+
+class XMLTableMasterPageExport : public XMLTextMasterPageExport
+{
+ void exportHeaderFooter(const css::uno::Reference < css::sheet::XHeaderFooterContent >& xHeaderFooter,
+ const xmloff::token::XMLTokenEnum aName,
+ const bool bDisplay);
+
+protected:
+ virtual void exportHeaderFooterContent(
+ const css::uno::Reference< css::text::XText >& rText,
+ bool bAutoStyles, bool bProgress = true ) override;
+
+ virtual void exportMasterPageContent(
+ const css::uno::Reference< css::beans::XPropertySet > & rPropSet,
+ bool bAutoStyles ) override;
+
+public:
+ explicit XMLTableMasterPageExport( ScXMLExport& rExp );
+ virtual ~XMLTableMasterPageExport() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapeImportHelper.cxx b/sc/source/filter/xml/XMLTableShapeImportHelper.cxx
new file mode 100644
index 000000000..8a6c4b4c3
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapeImportHelper.cxx
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLTableShapeImportHelper.hxx"
+#include "xmlimprt.hxx"
+#include <drwlayer.hxx>
+#include "xmlannoi.hxx"
+#include <rangeutl.hxx>
+#include <userdat.hxx>
+#include <docuno.hxx>
+#include <sheetdata.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+
+constexpr OUStringLiteral SC_LAYERID = u"LayerID";
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+XMLTableShapeImportHelper::XMLTableShapeImportHelper( ScXMLImport& rImp ) :
+ XMLShapeImportHelper(rImp, rImp.GetModel(), nullptr ),
+ pAnnotationContext(nullptr),
+ bOnTable(false)
+{
+}
+
+XMLTableShapeImportHelper::~XMLTableShapeImportHelper()
+{
+}
+
+void XMLTableShapeImportHelper::SetLayer(const uno::Reference<drawing::XShape>& rShape, SdrLayerID nLayerID, std::u16string_view sType)
+{
+ if ( sType == u"com.sun.star.drawing.ControlShape" )
+ nLayerID = SC_LAYER_CONTROLS;
+ if (nLayerID != SDRLAYER_NOTFOUND)
+ {
+ uno::Reference< beans::XPropertySet > xShapeProp( rShape, uno::UNO_QUERY );
+ if( xShapeProp.is() )
+ xShapeProp->setPropertyValue( SC_LAYERID, uno::Any(nLayerID.get()) );
+ }
+}
+
+// Attempt to find the topmost parent of the group, this is the one we apply
+// offsets to
+static uno::Reference< drawing::XShape > lcl_getTopLevelParent( const uno::Reference< drawing::XShape >& rShape )
+{
+ uno::Reference< container::XChild > xChild( rShape, uno::UNO_QUERY );
+ uno::Reference< drawing::XShape > xParent( xChild->getParent(), uno::UNO_QUERY );
+ if ( xParent.is() )
+ return lcl_getTopLevelParent( xParent );
+ return rShape;
+}
+
+void XMLTableShapeImportHelper::finishShape(
+ uno::Reference< drawing::XShape >& rShape,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ uno::Reference< drawing::XShapes >& rShapes )
+{
+ bool bNote = false;
+ XMLShapeImportHelper::finishShape( rShape, xAttrList, rShapes );
+ static_cast<ScXMLImport&>(mrImporter).LockSolarMutex();
+ ScMyTables& rTables = static_cast<ScXMLImport&>(mrImporter).GetTables();
+ if (rShapes == rTables.GetCurrentXShapes())
+ {
+ if (!pAnnotationContext)
+ {
+ ScDrawObjData aAnchor;
+ aAnchor.maStart = aStartCell;
+ awt::Point aStartPoint(rShape->getPosition());
+ aAnchor.maStartOffset = Point(aStartPoint.X, aStartPoint.Y);
+ aAnchor.mbResizeWithCell = false;
+
+ sal_Int32 nEndX(-1);
+ sal_Int32 nEndY(-1);
+ std::optional<OUString> xRangeList;
+ SdrLayerID nLayerID = SDRLAYER_NOTFOUND;
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ switch(aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_END_CELL_ADDRESS):
+ {
+ sal_Int32 nOffset(0);
+ ScDocument* pDoc = static_cast<ScXMLImport&>(mrImporter).GetDocument();
+ assert(pDoc);
+ ScRangeStringConverter::GetAddressFromString(aAnchor.maEnd, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset);
+ // When the cell end address is set, we let the shape resize with the cell
+ aAnchor.mbResizeWithCell = true;
+ break;
+ }
+ case XML_ELEMENT(TABLE, XML_END_X):
+ {
+ static_cast<ScXMLImport&>(mrImporter).
+ GetMM100UnitConverter().convertMeasureToCore(
+ nEndX, aIter.toView());
+ aAnchor.maEndOffset.setX( nEndX );
+ break;
+ }
+ case XML_ELEMENT(TABLE, XML_END_Y):
+ {
+ static_cast<ScXMLImport&>(mrImporter).
+ GetMM100UnitConverter().convertMeasureToCore(
+ nEndY, aIter.toView());
+ aAnchor.maEndOffset.setY( nEndY );
+ break;
+ }
+ case XML_ELEMENT(TABLE, XML_TABLE_BACKGROUND):
+ if (IsXMLToken(aIter, XML_TRUE))
+ nLayerID = SC_LAYER_BACK;
+ break;
+ case XML_ELEMENT(DRAW, XML_NOTIFY_ON_UPDATE_OF_RANGES):
+ xRangeList = aIter.toString();
+ break;
+ default: ;
+ }
+ }
+ SetLayer(rShape, nLayerID, rShape->getShapeType());
+
+ if (SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(rShape))
+ {
+ if (!bOnTable)
+ ScDrawLayer::SetCellAnchored(*pSdrObj, aAnchor);
+ else
+ ScDrawLayer::SetPageAnchored(*pSdrObj);
+ }
+
+ if (xRangeList)
+ {
+ // #i78086# If there are notification ranges, the ChartListener must be created
+ // also when anchored to the sheet
+ // -> call AddOLE with invalid cell position (checked in ScMyShapeResizer::ResizeShapes)
+
+ if (ScMyTables::IsOLE(rShape))
+ rTables.AddOLE(rShape, *xRangeList);
+ }
+ }
+ else // shape is annotation
+ {
+ // get the style names for stream copying
+ OUString aStyleName;
+ OUString aTextStyle;
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ const OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(DRAW, XML_STYLE_NAME):
+ aStyleName = sValue;
+ break;
+ case XML_ELEMENT(DRAW, XML_TEXT_STYLE_NAME):
+ aTextStyle = sValue;
+ break;
+ default:;
+ }
+ }
+
+ pAnnotationContext->SetShape(rShape, rShapes, aStyleName, aTextStyle);
+ bNote = true;
+ }
+ }
+ else //this are grouped shapes which should also get the layerid
+ {
+ uno::Reference< drawing::XShapes > xGroup( rShape, uno::UNO_QUERY );
+ // ignore the group ( within group ) object if it exists
+ if ( !bOnTable && !xGroup.is() )
+ {
+ // For cell anchored grouped shape we need to set the start
+ // position from the most top and left positioned shape(s) within
+ // the group
+ Point aStartPoint( rShape->getPosition().X,rShape->getPosition().Y );
+ uno::Reference< drawing::XShape > xChild( rShapes, uno::UNO_QUERY );
+ if (xChild)
+ {
+ if (SdrObject *pSdrObj = SdrObject::getSdrObjectFromXShape(lcl_getTopLevelParent(xChild)))
+ {
+ if ( ScDrawObjData* pAnchor = ScDrawLayer::GetObjData( pSdrObj ) )
+ {
+ if ( pAnchor->maStartOffset.getX() == 0 && pAnchor->maStartOffset.getY() == 0 )
+ pAnchor->maStartOffset = aStartPoint;
+ if ( aStartPoint.getX() < pAnchor->maStartOffset.getX() )
+ pAnchor->maStartOffset.setX( aStartPoint.getX() );
+ if ( aStartPoint.getY() < pAnchor->maStartOffset.getY() )
+ pAnchor->maStartOffset.setY( aStartPoint.getY() );
+ }
+ }
+ }
+ }
+ SdrLayerID nLayerID = SDRLAYER_NOTFOUND;
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ if (aIter.getToken() == XML_ELEMENT(TABLE, XML_TABLE_BACKGROUND))
+ {
+ if (IsXMLToken(aIter, XML_TRUE))
+ nLayerID = SC_LAYER_BACK;
+ break;
+ }
+ }
+ SetLayer(rShape, nLayerID, rShape->getShapeType());
+ }
+
+ if (!bNote)
+ {
+ // any shape other than a note prevents copying the sheet
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(mrImporter.GetModel())->GetSheetSaveData();
+ pSheetData->BlockSheet( rTables.GetCurrentSheet() );
+ }
+
+ static_cast<ScXMLImport&>(mrImporter).UnlockSolarMutex();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapeImportHelper.hxx b/sc/source/filter/xml/XMLTableShapeImportHelper.hxx
new file mode 100644
index 000000000..2f27233d3
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapeImportHelper.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/shapeimport.hxx>
+#include <svx/svdtypes.hxx>
+#include <address.hxx>
+
+class ScXMLImport;
+class ScXMLAnnotationContext;
+
+class XMLTableShapeImportHelper : public XMLShapeImportHelper
+{
+ ScAddress aStartCell;
+ ScXMLAnnotationContext* pAnnotationContext;
+ bool bOnTable;
+
+public:
+
+ explicit XMLTableShapeImportHelper( ScXMLImport& rImp );
+ virtual ~XMLTableShapeImportHelper() override;
+
+ static void SetLayer(const css::uno::Reference<css::drawing::XShape>& rShape, SdrLayerID nLayerID, std::u16string_view sType);
+ virtual void finishShape(css::uno::Reference< css::drawing::XShape >& rShape,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
+ css::uno::Reference< css::drawing::XShapes >& rShapes) override;
+
+ void SetCell (const ScAddress& rAddress) { aStartCell = rAddress; }
+ void SetOnTable (const bool bTempOnTable) { bOnTable = bTempOnTable; }
+ void SetAnnotation(ScXMLAnnotationContext* pAnnotation) { pAnnotationContext = pAnnotation; }
+
+ ScXMLAnnotationContext* GetAnnotationContext() const { return pAnnotationContext; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapeResizer.cxx b/sc/source/filter/xml/XMLTableShapeResizer.cxx
new file mode 100644
index 000000000..0ff302346
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapeResizer.cxx
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLTableShapeResizer.hxx"
+#include <document.hxx>
+#include "xmlimprt.hxx"
+#include <chartlis.hxx>
+#include <rangeutl.hxx>
+#include <compiler.hxx>
+#include <reftokenhelper.hxx>
+
+#include <osl/diagnose.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+
+#include <memory>
+#include <vector>
+
+using namespace ::com::sun::star;
+using ::std::unique_ptr;
+using ::std::vector;
+
+ScMyOLEFixer::ScMyOLEFixer(ScXMLImport& rTempImport)
+ : rImport(rTempImport),
+ pCollection(nullptr)
+{
+}
+
+ScMyOLEFixer::~ScMyOLEFixer()
+{
+}
+
+bool ScMyOLEFixer::IsOLE(const uno::Reference< drawing::XShape >& rShape)
+{
+ return rShape->getShapeType() == "com.sun.star.drawing.OLE2Shape";
+}
+
+void ScMyOLEFixer::CreateChartListener(ScDocument& rDoc,
+ const OUString& rName,
+ const OUString& rRangeList)
+{
+ if (rRangeList.isEmpty())
+ {
+ rDoc.AddOLEObjectToCollection(rName);
+ return;
+ }
+
+ OUString aRangeStr;
+ ScRangeStringConverter::GetStringFromXMLRangeString(aRangeStr, rRangeList, rDoc);
+ if (aRangeStr.isEmpty())
+ {
+ rDoc.AddOLEObjectToCollection(rName);
+ return;
+ }
+
+ if (!pCollection)
+ pCollection = rDoc.GetChartListenerCollection();
+
+ if (!pCollection)
+ return;
+
+ vector<ScTokenRef> aRefTokens;
+ const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep);
+ ScRefTokenHelper::compileRangeRepresentation(
+ aRefTokens, aRangeStr, rDoc, cSep, rDoc.GetGrammar());
+ if (aRefTokens.empty())
+ return;
+
+ OUString sName = !rName.isEmpty() ? rName : pCollection->getUniqueName(u"OLEFixer ");
+ ScChartListener* pCL(new ScChartListener(sName, rDoc, std::move(aRefTokens)));
+
+ //for loading binary files e.g.
+ //if we have the flat filter we need to set the dirty flag thus the visible charts get repainted
+ //otherwise the charts keep their first visual representation which was created at a moment where the calc itself was not loaded completely and is therefore incorrect
+ if( (rImport.getImportFlags() & SvXMLImportFlags::ALL) == SvXMLImportFlags::ALL )
+ pCL->SetDirty( true );
+ else
+ {
+ // #i104899# If a formula cell is already dirty, further changes aren't propagated.
+ // This can happen easily now that row heights aren't updated for all sheets.
+ rDoc.InterpretDirtyCells( *pCL->GetRangeList() );
+ }
+
+ bool bSuccess = pCollection->insert(pCL);
+ assert(bSuccess && "failed to insert listener"); (void)bSuccess;
+ pCL->StartListeningTo();
+}
+
+void ScMyOLEFixer::AddOLE(const uno::Reference <drawing::XShape>& rShape,
+ const OUString &rRangeList)
+{
+ ScMyToFixupOLE aShape;
+ aShape.xShape.set(rShape);
+ aShape.sRangeList = rRangeList;
+ aShapes.push_back(aShape);
+}
+
+void ScMyOLEFixer::FixupOLEs()
+{
+ if (aShapes.empty() || !rImport.GetModel().is())
+ return;
+
+ OUString sPersistName ("PersistName");
+ ScDocument* pDoc(rImport.GetDocument());
+
+ ScXMLImport::MutexGuard aGuard(rImport);
+
+ for (auto const& shape : aShapes)
+ {
+ // #i78086# also call CreateChartListener for invalid position (anchored to sheet)
+ if (!IsOLE(shape.xShape))
+ OSL_FAIL("Only OLEs should be in here now");
+
+ if (IsOLE(shape.xShape))
+ {
+ uno::Reference < beans::XPropertySet > xShapeProps ( shape.xShape, uno::UNO_QUERY );
+ uno::Reference < beans::XPropertySetInfo > xShapeInfo(xShapeProps->getPropertySetInfo());
+
+ OUString sName;
+ if (pDoc && xShapeProps.is() && xShapeInfo.is() && xShapeInfo->hasPropertyByName(sPersistName) &&
+ (xShapeProps->getPropertyValue(sPersistName) >>= sName))
+ CreateChartListener(*pDoc, sName, shape.sRangeList);
+ }
+ }
+ aShapes.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapeResizer.hxx b/sc/source/filter/xml/XMLTableShapeResizer.hxx
new file mode 100644
index 000000000..2455db7e8
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapeResizer.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <vector>
+
+namespace com::sun::star::drawing { class XShape; }
+
+class ScXMLImport;
+class ScChartListenerCollection;
+class ScDocument;
+
+struct ScMyToFixupOLE
+{
+ css::uno::Reference <css::drawing::XShape> xShape;
+ OUString sRangeList;
+};
+
+class ScMyOLEFixer
+{
+ ScXMLImport& rImport;
+ std::vector<ScMyToFixupOLE> aShapes;
+ ScChartListenerCollection* pCollection;
+
+ void CreateChartListener(ScDocument& rDoc,
+ const OUString& rName,
+ const OUString& rRangeList);
+public:
+ explicit ScMyOLEFixer(ScXMLImport& rImport);
+ ~ScMyOLEFixer();
+
+ static bool IsOLE(const css::uno::Reference< css::drawing::XShape >& rShape);
+ void AddOLE(const css::uno::Reference <css::drawing::XShape>& rShape,
+ const OUString &rRangeList);
+ void FixupOLEs();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapesContext.cxx b/sc/source/filter/xml/XMLTableShapesContext.cxx
new file mode 100644
index 000000000..676bc560f
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapesContext.cxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLTableShapesContext.hxx"
+#include "XMLTableShapeImportHelper.hxx"
+#include "xmlimprt.hxx"
+
+using namespace com::sun::star;
+
+ScXMLTableShapesContext::ScXMLTableShapesContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // here are no attributes
+}
+
+ScXMLTableShapesContext::~ScXMLTableShapesContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > ScXMLTableShapesContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ ScXMLImport& rXMLImport(GetScImport());
+ uno::Reference<drawing::XShapes> xShapes (rXMLImport.GetTables().GetCurrentXShapes());
+ if (xShapes.is())
+ {
+ XMLTableShapeImportHelper* pTableShapeImport(static_cast<XMLTableShapeImportHelper*>(rXMLImport.GetShapeImport().get()));
+ pTableShapeImport->SetOnTable(true);
+ return XMLShapeImportHelper::CreateGroupChildContext(
+ rXMLImport, nElement, xAttrList, xShapes);
+ }
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableShapesContext.hxx b/sc/source/filter/xml/XMLTableShapesContext.hxx
new file mode 100644
index 000000000..649fef106
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableShapesContext.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+class ScXMLImport;
+
+class ScXMLTableShapesContext : public ScXMLImportContext
+{
+public:
+ ScXMLTableShapesContext( ScXMLImport& rImport );
+
+ virtual ~ScXMLTableShapesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableSourceContext.cxx b/sc/source/filter/xml/XMLTableSourceContext.cxx
new file mode 100644
index 000000000..535f532dc
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableSourceContext.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "XMLTableSourceContext.hxx"
+#include "xmlimprt.hxx"
+#include <document.hxx>
+#include "xmlsubti.hxx"
+#include <tablink.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <sax/tools/converter.hxx>
+#include <com/sun/star/sheet/XSheetLinkable.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableSourceContext::ScXMLTableSourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ nRefresh(0),
+ nMode(sheet::SheetLinkMode_NORMAL)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ sLink = GetScImport().GetAbsoluteReference(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_NAME ):
+ sTableName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_NAME):
+ sFilterName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_OPTIONS ):
+ sFilterOptions = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_MODE ):
+ if (IsXMLToken(aIter, XML_COPY_RESULTS_ONLY))
+ nMode = sheet::SheetLinkMode_VALUE;
+ break;
+ case XML_ELEMENT( TABLE, XML_REFRESH_DELAY ):
+ double fTime;
+ if (::sax::Converter::convertDuration( fTime, aIter.toView() ))
+ nRefresh = std::max( static_cast<sal_Int32>(fTime * 86400.0), sal_Int32(0) );
+ break;
+ }
+ }
+}
+
+ScXMLTableSourceContext::~ScXMLTableSourceContext()
+{
+}
+
+void SAL_CALL ScXMLTableSourceContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (sLink.isEmpty())
+ return;
+
+ uno::Reference <sheet::XSheetLinkable> xLinkable (GetScImport().GetTables().GetCurrentXSheet(), uno::UNO_QUERY);
+ ScDocument* pDoc(GetScImport().GetDocument());
+ if (!(xLinkable.is() && pDoc))
+ return;
+
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ if (!pDoc->RenameTab( GetScImport().GetTables().GetCurrentSheet(),
+ GetScImport().GetTables().GetCurrentSheetName(), true/*bExternalDocument*/))
+ return;
+
+ sLink = ScGlobal::GetAbsDocName( sLink, pDoc->GetDocumentShell() );
+ if (sFilterName.isEmpty())
+ ScDocumentLoader::GetFilterName( sLink, sFilterName, sFilterOptions, false, false );
+
+ ScLinkMode nLinkMode = ScLinkMode::NONE;
+ if ( nMode == sheet::SheetLinkMode_NORMAL )
+ nLinkMode = ScLinkMode::NORMAL;
+ else if ( nMode == sheet::SheetLinkMode_VALUE )
+ nLinkMode = ScLinkMode::VALUE;
+
+ pDoc->SetLink( GetScImport().GetTables().GetCurrentSheet(),
+ nLinkMode, sLink, sFilterName, sFilterOptions,
+ sTableName, nRefresh );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTableSourceContext.hxx b/sc/source/filter/xml/XMLTableSourceContext.hxx
new file mode 100644
index 000000000..85bfb6929
--- /dev/null
+++ b/sc/source/filter/xml/XMLTableSourceContext.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/sheet/SheetLinkMode.hpp>
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLTableSourceContext : public ScXMLImportContext
+{
+ OUString sLink;
+ OUString sTableName;
+ OUString sFilterName;
+ OUString sFilterOptions;
+ sal_Int32 nRefresh;
+ css::sheet::SheetLinkMode nMode;
+
+public:
+ ScXMLTableSourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableSourceContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTrackedChangesContext.cxx b/sc/source/filter/xml/XMLTrackedChangesContext.cxx
new file mode 100644
index 000000000..b3dfb353c
--- /dev/null
+++ b/sc/source/filter/xml/XMLTrackedChangesContext.cxx
@@ -0,0 +1,1398 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include "XMLTrackedChangesContext.hxx"
+#include "XMLChangeTrackingImportHelper.hxx"
+#include "xmlimprt.hxx"
+#include "xmlconti.hxx"
+#include <formulacell.hxx>
+#include <textuno.hxx>
+#include <editutil.hxx>
+#include <document.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <sax/tools/converter.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <comphelper/base64.hxx>
+#include <com/sun/star/text/XTextCursor.hpp>
+#include <com/sun/star/text/ControlCharacter.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+namespace {
+
+class ScXMLChangeInfoContext : public ScXMLImportContext
+{
+ ScMyActionInfo aInfo;
+ OUStringBuffer sAuthorBuffer{32};
+ OUStringBuffer sDateTimeBuffer{32};
+ OUStringBuffer sCommentBuffer{64};
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+ sal_uInt32 nParagraphCount;
+
+public:
+ ScXMLChangeInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLBigRangeContext : public ScXMLImportContext
+{
+public:
+ ScXMLBigRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScBigRange& rBigRange);
+};
+
+class ScXMLCellContentDeletionContext : public ScXMLImportContext
+{
+ OUString sFormulaAddress;
+ OUString sFormula;
+ OUString sFormulaNmsp;
+ OUString sInputString;
+ ScBigRange aBigRange;
+ double fValue;
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+ ScCellValue maCell;
+ sal_uInt32 nID;
+ sal_Int32 nMatrixCols;
+ sal_Int32 nMatrixRows;
+ formula::FormulaGrammar::Grammar eGrammar;
+ sal_uInt16 nType;
+ ScMatrixMode nMatrixFlag;
+
+public:
+ ScXMLCellContentDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDependenceContext : public ScXMLImportContext
+{
+public:
+ ScXMLDependenceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+};
+
+class ScXMLDependingsContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLDependingsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLChangeDeletionContext : public ScXMLImportContext
+{
+public:
+ ScXMLChangeDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+};
+
+class ScXMLDeletionsContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLDeletionsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLChangeCellContext;
+
+class ScXMLChangeTextPContext : public ScXMLImportContext
+{
+ css::uno::Reference< css::xml::sax::XFastAttributeList> mxAttrList;
+ sal_Int32 mnElement;
+ OUStringBuffer sText;
+ ScXMLChangeCellContext* pChangeCellContext;
+ rtl::Reference<SvXMLImportContext>
+ pTextPContext;
+
+public:
+
+ ScXMLChangeTextPContext( ScXMLImport& rImport, sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeCellContext* pChangeCellContext);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL characters( const OUString& rChars ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class ScXMLChangeCellContext : public ScXMLImportContext
+{
+ ScCellValue& mrOldCell;
+
+ OUString sText;
+ OUString& rInputString;
+ rtl::Reference<ScEditEngineTextObj> mpEditTextObj;
+ double fValue;
+ sal_uInt16& rType;
+ bool bEmpty;
+ bool bFirstParagraph;
+ bool bString;
+ bool bFormula;
+
+public:
+ ScXMLChangeCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScCellValue& rOldCell, OUString& sAddress,
+ OUString& rFormula, OUString& rFormulaNmsp,
+ formula::FormulaGrammar::Grammar& rGrammar,
+ OUString& rInputString, double& fValue, sal_uInt16& nType,
+ ScMatrixMode& nMatrixFlag, sal_Int32& nMatrixCols, sal_Int32& nMatrixRows);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void CreateTextPContext(bool bIsNewParagraph);
+ bool IsEditCell() const { return mpEditTextObj.is(); }
+ void SetText(const OUString& sTempText) { sText = sTempText; }
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLPreviousContext : public ScXMLImportContext
+{
+ OUString sFormulaAddress;
+ OUString sFormula;
+ OUString sFormulaNmsp;
+ OUString sInputString;
+ double fValue;
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+ ScCellValue maOldCell;
+ sal_uInt32 nID;
+ sal_Int32 nMatrixCols;
+ sal_Int32 nMatrixRows;
+ formula::FormulaGrammar::Grammar eGrammar;
+ sal_uInt16 nType;
+ ScMatrixMode nMatrixFlag;
+
+public:
+ ScXMLPreviousContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLContentChangeContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+ ScBigRange aBigRange;
+
+public:
+ ScXMLContentChangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLInsertionContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLInsertionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLInsertionCutOffContext : public ScXMLImportContext
+{
+public:
+ ScXMLInsertionCutOffContext( ScXMLImport& rImport,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+};
+
+class ScXMLMovementCutOffContext : public ScXMLImportContext
+{
+public:
+ ScXMLMovementCutOffContext( ScXMLImport& rImport,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+};
+
+class ScXMLCutOffsContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLCutOffsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDeletionContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLMovementContext : public ScXMLImportContext
+{
+ ScBigRange aSourceRange;
+ ScBigRange aTargetRange;
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLMovementContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLRejectionContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLRejectionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+}
+
+ScXMLTrackedChangesContext::ScXMLTrackedChangesContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ rImport.LockSolarMutex();
+
+ if ( !rAttrList.is() )
+ return;
+
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_PROTECTION_KEY ) ) );
+ if (aIter != rAttrList->end())
+ {
+ if( !aIter.isEmpty() )
+ {
+ uno::Sequence<sal_Int8> aPass;
+ ::comphelper::Base64::decode( aPass, aIter.toString() );
+ pChangeTrackingImportHelper->SetProtection(aPass);
+ }
+ }
+}
+
+ScXMLTrackedChangesContext::~ScXMLTrackedChangesContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLTrackedChangesContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_CELL_CONTENT_CHANGE ):
+ pContext = new ScXMLContentChangeContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_INSERTION ):
+ pContext = new ScXMLInsertionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETION ):
+ pContext = new ScXMLDeletionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_MOVEMENT ):
+ pContext = new ScXMLMovementContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTION ):
+ pContext = new ScXMLRejectionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLChangeInfoContext::ScXMLChangeInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ aInfo(),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper),
+ nParagraphCount(0)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ sal_Int32 nToken = aIter.getToken();
+ if ( nToken == XML_ELEMENT( OFFICE, XML_CHG_AUTHOR ) )
+ sAuthorBuffer = aIter.toString();
+ else if ( nToken == XML_ELEMENT( OFFICE, XML_CHG_DATE_TIME ) )
+ sDateTimeBuffer = aIter.toString();
+ }
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLChangeInfoContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ if( nElement == XML_ELEMENT(DC, XML_CREATOR ) )
+ pContext = new ScXMLContentContext(GetScImport(), sAuthorBuffer);
+ else if( nElement == XML_ELEMENT(DC, XML_DATE ) )
+ pContext = new ScXMLContentContext(GetScImport(), sDateTimeBuffer);
+ else if (nElement == XML_ELEMENT(TEXT, XML_P))
+ {
+ if(nParagraphCount)
+ sCommentBuffer.append('\n');
+ ++nParagraphCount;
+ pContext = new ScXMLContentContext( GetScImport(), sCommentBuffer);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLChangeInfoContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ aInfo.sUser = sAuthorBuffer.makeStringAndClear();
+ ::sax::Converter::parseDateTime(aInfo.aDateTime, sDateTimeBuffer);
+ sDateTimeBuffer.setLength(0);
+ aInfo.sComment = sCommentBuffer.makeStringAndClear();
+ pChangeTrackingImportHelper->SetActionInfo(aInfo);
+}
+
+ScXMLBigRangeContext::ScXMLBigRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScBigRange& rBigRange ) :
+ ScXMLImportContext( rImport )
+{
+ bool bColumn(false);
+ bool bRow(false);
+ bool bTable(false);
+ sal_Int32 nColumn(0);
+ sal_Int32 nRow(0);
+ sal_Int32 nTable(0);
+ sal_Int32 nStartColumn(0);
+ sal_Int32 nEndColumn(0);
+ sal_Int32 nStartRow(0);
+ sal_Int32 nEndRow(0);
+ sal_Int32 nStartTable(0);
+ sal_Int32 nEndTable(0);
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_COLUMN ):
+ nColumn = aIter.toInt32();
+ bColumn = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_ROW ):
+ nRow = aIter.toInt32();
+ bRow = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ nTable = aIter.toInt32();
+ bTable = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_START_COLUMN ):
+ nStartColumn = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_END_COLUMN ):
+ nEndColumn = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_START_ROW ):
+ nStartRow = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_END_ROW ):
+ nEndRow = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_START_TABLE ):
+ nStartTable = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_END_TABLE ):
+ nEndTable = aIter.toInt32();
+ break;
+ }
+ }
+ }
+
+ if (bColumn)
+ nStartColumn = nEndColumn = nColumn;
+ if (bRow)
+ nStartRow = nEndRow = nRow;
+ if (bTable)
+ nStartTable = nEndTable = nTable;
+ rBigRange.Set(nStartColumn, nStartRow, nStartTable,
+ nEndColumn, nEndRow, nEndTable);
+}
+
+ScXMLCellContentDeletionContext::ScXMLCellContentDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper) :
+ ScXMLImportContext( rImport ),
+ fValue(0.0),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper),
+ nID(0),
+ nMatrixCols(0),
+ nMatrixRows(0),
+ eGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT),
+ nType(css::util::NumberFormat::ALL),
+ nMatrixFlag(ScMatrixMode::NONE)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_ID ) ) );
+ if (aIter != rAttrList->end())
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ }
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellContentDeletionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_CHANGE_TRACK_TABLE_CELL ):
+ pContext = new ScXMLChangeCellContext(GetScImport(), pAttribList,
+ maCell, sFormulaAddress, sFormula, sFormulaNmsp, eGrammar, sInputString, fValue, nType, nMatrixFlag, nMatrixCols, nMatrixRows );
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_ADDRESS ):
+ OSL_ENSURE(!nID, "a action with a ID should not contain a BigRange");
+ pContext = new ScXMLBigRangeContext(GetScImport(), pAttribList, aBigRange);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLCellContentDeletionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ std::unique_ptr<ScMyCellInfo> pCellInfo(new ScMyCellInfo(maCell, sFormulaAddress, sFormula, eGrammar, sInputString, fValue, nType,
+ nMatrixFlag, nMatrixCols, nMatrixRows));
+ if (nID)
+ pChangeTrackingImportHelper->AddDeleted(nID, std::move(pCellInfo));
+ else
+ pChangeTrackingImportHelper->AddGenerated(std::move(pCellInfo), aBigRange);
+}
+
+ScXMLDependenceContext::ScXMLDependenceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport )
+{
+ sal_uInt32 nID(0);
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_ID ) ) );
+ if (aIter != rAttrList->end())
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString(aIter.toView());
+ }
+ pChangeTrackingImportHelper->AddDependence(nID);
+}
+
+ScXMLDependingsContext::ScXMLDependingsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ // here are no attributes
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDependingsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ // #i80033# read both old (dependence) and new (dependency) elements
+ if (nElement == XML_ELEMENT( TABLE, XML_DEPENDENCE ) ||
+ nElement == XML_ELEMENT( TABLE, XML_DEPENDENCY ))
+ {
+ pContext = new ScXMLDependenceContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ }
+
+ return pContext;
+}
+
+ScXMLChangeDeletionContext::ScXMLChangeDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport )
+{
+ sal_uInt32 nID(0);
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_ID ) ) );
+ if (aIter != rAttrList->end())
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ }
+ pChangeTrackingImportHelper->AddDeleted(nID);
+}
+
+ScXMLDeletionsContext::ScXMLDeletionsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ // here are no attributes
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDeletionsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_CHANGE_DELETION ):
+ pContext = new ScXMLChangeDeletionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_CONTENT_DELETION ):
+ pContext = new ScXMLCellContentDeletionContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLChangeTextPContext::ScXMLChangeTextPContext( ScXMLImport& rImport,
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeCellContext* pTempChangeCellContext) :
+ ScXMLImportContext( rImport ),
+ mxAttrList(xAttrList),
+ mnElement(nElement),
+ pChangeCellContext(pTempChangeCellContext)
+{
+ // here are no attributes
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLChangeTextPContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ uno::Reference< xml::sax::XFastContextHandler > xContext;
+ if (IsTokenInNamespace(mnElement, XML_NAMESPACE_TEXT)
+ && (nElement & TOKEN_MASK) == XML_S
+ && !pTextPContext)
+ {
+ sal_Int32 nRepeat(0);
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(mxAttrList) )
+ {
+ if (aIter.getToken() == XML_ELEMENT(TEXT, XML_C))
+ nRepeat = aIter.toInt32();
+ else
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ if (nRepeat)
+ for (sal_Int32 j = 0; j < nRepeat; ++j)
+ sText.append(' ');
+ else
+ sText.append(' ');
+ }
+ else
+ {
+ if (!pChangeCellContext->IsEditCell())
+ pChangeCellContext->CreateTextPContext(false);
+ bool bWasContext (true);
+ if (!pTextPContext)
+ {
+ bWasContext = false;
+ pTextPContext = GetScImport().GetTextImport()->CreateTextChildContext(
+ GetScImport(), mnElement, mxAttrList);
+ }
+ if (pTextPContext)
+ {
+ if (!bWasContext)
+ pTextPContext->characters(sText.makeStringAndClear());
+ xContext = pTextPContext->createFastChildContext(nElement, xAttrList);
+ }
+ }
+
+ return xContext;
+}
+
+void ScXMLChangeTextPContext::characters( const OUString& rChars )
+{
+ if (!pTextPContext)
+ sText.append(rChars);
+ else
+ pTextPContext->characters(rChars);
+}
+
+void ScXMLChangeTextPContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ if (!pTextPContext)
+ pChangeCellContext->SetText(sText.makeStringAndClear());
+}
+
+ScXMLChangeCellContext::ScXMLChangeCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScCellValue& rOldCell, OUString& rAddress,
+ OUString& rFormula, OUString& rFormulaNmsp,
+ formula::FormulaGrammar::Grammar& rGrammar,
+ OUString& rTempInputString, double& rDateTimeValue, sal_uInt16& nType,
+ ScMatrixMode& nMatrixFlag, sal_Int32& nMatrixCols, sal_Int32& nMatrixRows )
+ : ScXMLImportContext( rImport )
+ , mrOldCell(rOldCell)
+ , rInputString(rTempInputString)
+ , fValue(0.0)
+ , rType(nType)
+ , bEmpty(true)
+ , bFirstParagraph(true)
+ , bString(true)
+ , bFormula(false)
+{
+ bool bIsMatrix(false);
+ bool bIsCoveredMatrix(false);
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FORMULA ):
+ bEmpty = false;
+ GetScImport().ExtractFormulaNamespaceGrammar( rFormula, rFormulaNmsp, rGrammar, aIter.toString() );
+ bFormula = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_ADDRESS ):
+ rAddress = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_MATRIX_COVERED ):
+ bIsCoveredMatrix = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixCols = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixRows = aIter.toInt32();
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE_TYPE ):
+ if (IsXMLToken(aIter, XML_FLOAT))
+ bString = false;
+ else if (IsXMLToken(aIter, XML_DATE))
+ {
+ rType = css::util::NumberFormat::DATE;
+ bString = false;
+ }
+ else if (IsXMLToken(aIter, XML_TIME))
+ {
+ rType = css::util::NumberFormat::TIME;
+ bString = false;
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE ):
+ fValue = aIter.toDouble();
+ bEmpty = false;
+ break;
+ case XML_ELEMENT( OFFICE, XML_DATE_VALUE ):
+ bEmpty = false;
+ if (GetScImport().GetMM100UnitConverter().setNullDate(GetScImport().GetModel()))
+ GetScImport().GetMM100UnitConverter().convertDateTime(rDateTimeValue, aIter.toView());
+ fValue = rDateTimeValue;
+ break;
+ case XML_ELEMENT( OFFICE, XML_TIME_VALUE ):
+ bEmpty = false;
+ ::sax::Converter::convertDuration(rDateTimeValue, aIter.toView());
+ fValue = rDateTimeValue;
+ }
+ }
+ }
+
+ if (bIsCoveredMatrix)
+ nMatrixFlag = ScMatrixMode::Reference;
+ else if (bIsMatrix && nMatrixRows && nMatrixCols)
+ nMatrixFlag = ScMatrixMode::Formula;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > ScXMLChangeCellContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ if (nElement == XML_ELEMENT(TEXT, XML_P))
+ {
+ bEmpty = false;
+ if (bFirstParagraph)
+ {
+ pContext = new ScXMLChangeTextPContext(GetScImport(), nElement, xAttrList, this);
+ bFirstParagraph = false;
+ }
+ else
+ {
+ if (!mpEditTextObj.is())
+ CreateTextPContext(true);
+ pContext = GetScImport().GetTextImport()->CreateTextChildContext(
+ GetScImport(), nElement, xAttrList);
+ }
+ }
+ else
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+
+ return pContext;
+}
+
+void ScXMLChangeCellContext::CreateTextPContext(bool bIsNewParagraph)
+{
+ if (!GetScImport().GetDocument())
+ return;
+
+ mpEditTextObj = new ScEditEngineTextObj();
+ mpEditTextObj->GetEditEngine()->SetEditTextObjectPool(GetScImport().GetDocument()->GetEditPool());
+ uno::Reference <text::XText> xText(mpEditTextObj);
+ if (xText.is())
+ {
+ uno::Reference<text::XTextCursor> xTextCursor(xText->createTextCursor());
+ if (bIsNewParagraph)
+ {
+ xText->setString(sText);
+ xTextCursor->gotoEnd(false);
+ xText->insertControlCharacter(xTextCursor, text::ControlCharacter::PARAGRAPH_BREAK, false);
+ }
+ GetScImport().GetTextImport()->SetCursor(xTextCursor);
+ }
+}
+
+void SAL_CALL ScXMLChangeCellContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!bEmpty)
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (mpEditTextObj.is())
+ {
+ if (GetImport().GetTextImport()->GetCursor().is())
+ {
+ //GetImport().GetTextImport()->GetCursor()->gotoEnd(sal_False);
+ if( GetImport().GetTextImport()->GetCursor()->goLeft( 1, true ) )
+ {
+ GetImport().GetTextImport()->GetText()->insertString(
+ GetImport().GetTextImport()->GetCursorAsRange(), "",
+ true );
+ }
+ }
+
+ // The cell will own the text object instance.
+ mrOldCell.meType = CELLTYPE_EDIT;
+ mrOldCell.mpEditText = mpEditTextObj->CreateTextObject().release();
+ GetScImport().GetTextImport()->ResetCursor();
+ mpEditTextObj.clear();
+ }
+ else
+ {
+ if (!bFormula)
+ {
+ if (!sText.isEmpty() && bString)
+ {
+ mrOldCell.meType = CELLTYPE_STRING;
+ mrOldCell.mpString = new svl::SharedString(pDoc->GetSharedStringPool().intern(sText));
+ }
+ else
+ {
+ mrOldCell.meType = CELLTYPE_VALUE;
+ mrOldCell.mfValue = fValue;
+ }
+ if (rType == css::util::NumberFormat::DATE || rType == css::util::NumberFormat::TIME)
+ rInputString = sText;
+ }
+ }
+ }
+ else
+ mrOldCell.clear();
+}
+
+ScXMLPreviousContext::ScXMLPreviousContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ fValue(0.0),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper),
+ nID(0),
+ nMatrixCols(0),
+ nMatrixRows(0),
+ eGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT),
+ nType(css::util::NumberFormat::ALL),
+ nMatrixFlag(ScMatrixMode::NONE)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_ID ) ) );
+ if (aIter != rAttrList->end())
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ }
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLPreviousContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if ( nElement == XML_ELEMENT( TABLE, XML_CHANGE_TRACK_TABLE_CELL ) )
+ pContext = new ScXMLChangeCellContext(GetScImport(), pAttribList,
+ maOldCell, sFormulaAddress, sFormula, sFormulaNmsp, eGrammar, sInputString, fValue, nType, nMatrixFlag, nMatrixCols, nMatrixRows);
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLPreviousContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->SetPreviousChange(nID, new ScMyCellInfo(maOldCell, sFormulaAddress, sFormula, eGrammar, sInputString,
+ fValue, nType, nMatrixFlag, nMatrixCols, nMatrixRows));
+}
+
+ScXMLContentChangeContext::ScXMLContentChangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(SC_CAT_CONTENT);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLContentChangeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_ADDRESS ):
+ pContext = new ScXMLBigRangeContext(GetScImport(), pAttribList, aBigRange);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_PREVIOUS ):
+ pContext = new ScXMLPreviousContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLContentChangeContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->SetBigRange(aBigRange);
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+ScXMLInsertionContext::ScXMLInsertionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ sal_Int32 nPosition(0);
+ sal_Int32 nCount(1);
+ sal_Int32 nTable(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+ ScChangeActionType nActionType(SC_CAT_INSERT_COLS);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_TYPE ):
+ if (IsXMLToken( aIter, XML_ROW ))
+ nActionType = SC_CAT_INSERT_ROWS;
+ else if (IsXMLToken( aIter, XML_TABLE ))
+ nActionType = SC_CAT_INSERT_TABS;
+ break;
+ case XML_ELEMENT( TABLE, XML_POSITION ):
+ nPosition = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ nTable = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_COUNT ):
+ nCount = aIter.toInt32();
+ break;
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(nActionType);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+ pChangeTrackingImportHelper->SetPosition(nPosition, nCount, nTable);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLInsertionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLInsertionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+ScXMLInsertionCutOffContext::ScXMLInsertionCutOffContext( ScXMLImport& rImport,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport )
+{
+ sal_uInt32 nID(0);
+ sal_Int32 nPosition(0);
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_ID):
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString(aIter.toView());
+ break;
+ case XML_ELEMENT(TABLE, XML_POSITION):
+ ::sax::Converter::convertNumber(nPosition, aIter.toView());
+ break;
+ }
+ }
+ pChangeTrackingImportHelper->SetInsertionCutOff(nID, nPosition);
+}
+
+
+ScXMLMovementCutOffContext::ScXMLMovementCutOffContext( ScXMLImport& rImport,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport )
+{
+ sal_uInt32 nID(0);
+ sal_Int32 nPosition(0);
+ sal_Int32 nStartPosition(0);
+ sal_Int32 nEndPosition(0);
+ bool bPosition(false);
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_ID):
+ nID = ScXMLChangeTrackingImportHelper::GetIDFromString(aIter.toView());
+ break;
+ case XML_ELEMENT(TABLE, XML_POSITION):
+ bPosition = true;
+ ::sax::Converter::convertNumber(nPosition, aIter.toView());
+ break;
+ case XML_ELEMENT(TABLE, XML_START_POSITION):
+ ::sax::Converter::convertNumber(nStartPosition, aIter.toView());
+ break;
+ case XML_ELEMENT(TABLE, XML_END_POSITION):
+ ::sax::Converter::convertNumber(nEndPosition, aIter.toView());
+ break;
+ }
+ }
+ if (bPosition)
+ nStartPosition = nEndPosition = nPosition;
+ pChangeTrackingImportHelper->AddMoveCutOff(nID, nStartPosition, nEndPosition);
+}
+
+ScXMLCutOffsContext::ScXMLCutOffsContext( ScXMLImport& rImport,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ // here are no attributes
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLCutOffsContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ if (nElement == XML_ELEMENT(TABLE, XML_INSERTION_CUT_OFF))
+ pContext = new ScXMLInsertionCutOffContext(GetScImport(), xAttrList, pChangeTrackingImportHelper);
+ else if (nElement == XML_ELEMENT(TABLE, XML_MOVEMENT_CUT_OFF))
+ pContext = new ScXMLMovementCutOffContext(GetScImport(), xAttrList, pChangeTrackingImportHelper);
+
+ return pContext;
+}
+
+ScXMLDeletionContext::ScXMLDeletionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ sal_Int32 nPosition(0);
+ sal_Int32 nMultiSpanned(0);
+ sal_Int32 nTable(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+ ScChangeActionType nActionType(SC_CAT_DELETE_COLS);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ sal_Int32 nToken = aIter.getToken();
+ switch (nToken)
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_TYPE ):
+ if (IsXMLToken( aIter, XML_ROW ))
+ nActionType = SC_CAT_DELETE_ROWS;
+ else if (IsXMLToken( aIter, XML_TABLE ))
+ nActionType = SC_CAT_DELETE_TABS;
+ break;
+ case XML_ELEMENT( TABLE, XML_POSITION ):
+ nPosition = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ nTable = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_MULTI_DELETION_SPANNED ):
+ nMultiSpanned = aIter.toInt32();
+ break;
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(nActionType);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+ pChangeTrackingImportHelper->SetPosition(nPosition, 1, nTable);
+ pChangeTrackingImportHelper->SetMultiSpanned(static_cast<sal_Int16>(nMultiSpanned));
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDeletionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_CUT_OFFS ):
+ case XML_ELEMENT( TABLE, XML_CUT_OFFS2 ): // cut_offs
+ pContext = new ScXMLCutOffsContext(GetScImport(), pChangeTrackingImportHelper);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDeletionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+ScXMLMovementContext::ScXMLMovementContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(SC_CAT_MOVE);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLMovementContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_RANGE_ADDRESS ):
+ pContext = new ScXMLBigRangeContext(GetScImport(), pAttribList, aSourceRange);
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ pContext = new ScXMLBigRangeContext(GetScImport(), pAttribList, aTargetRange);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLMovementContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->SetMoveRanges(aSourceRange, aTargetRange);
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+ScXMLRejectionContext::ScXMLRejectionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pTempChangeTrackingImportHelper ) :
+ ScXMLImportContext( rImport ),
+ pChangeTrackingImportHelper(pTempChangeTrackingImportHelper)
+{
+ sal_uInt32 nActionNumber(0);
+ sal_uInt32 nRejectingNumber(0);
+ ScChangeActionState nActionState(SC_CAS_VIRGIN);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ID ):
+ nActionNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ACCEPTANCE_STATE ):
+ if (IsXMLToken( aIter, XML_ACCEPTED ))
+ nActionState = SC_CAS_ACCEPTED;
+ else if (IsXMLToken( aIter, XML_REJECTED ))
+ nActionState = SC_CAS_REJECTED;
+ break;
+ case XML_ELEMENT( TABLE, XML_REJECTING_CHANGE_ID ):
+ nRejectingNumber = ScXMLChangeTrackingImportHelper::GetIDFromString( aIter.toView() );
+ break;
+ }
+ }
+ }
+
+ pChangeTrackingImportHelper->StartChangeAction(SC_CAT_MOVE);
+ pChangeTrackingImportHelper->SetActionNumber(nActionNumber);
+ pChangeTrackingImportHelper->SetActionState(nActionState);
+ pChangeTrackingImportHelper->SetRejectingNumber(nRejectingNumber);
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLRejectionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( OFFICE, XML_CHANGE_INFO ):
+ pContext = new ScXMLChangeInfoContext(GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DEPENDENCIES ):
+ pContext = new ScXMLDependingsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_DELETIONS ):
+ pContext = new ScXMLDeletionsContext(GetScImport(), pChangeTrackingImportHelper);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLRejectionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pChangeTrackingImportHelper->EndChangeAction();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/XMLTrackedChangesContext.hxx b/sc/source/filter/xml/XMLTrackedChangesContext.hxx
new file mode 100644
index 000000000..5ed8570ad
--- /dev/null
+++ b/sc/source/filter/xml/XMLTrackedChangesContext.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser
+{
+class FastAttributeList;
+}
+
+class ScXMLChangeTrackingImportHelper;
+
+class ScXMLTrackedChangesContext : public ScXMLImportContext
+{
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+ ScXMLTrackedChangesContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper);
+ virtual ~ScXMLTrackedChangesContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/cachedattraccess.cxx b/sc/source/filter/xml/cachedattraccess.cxx
new file mode 100644
index 000000000..b87fc014f
--- /dev/null
+++ b/sc/source/filter/xml/cachedattraccess.cxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "cachedattraccess.hxx"
+#include <document.hxx>
+
+ScXMLCachedRowAttrAccess::Cache::Cache() :
+ mnTab(-1), mnRow1(-1), mnRow2(-1), mbValue(false) {}
+
+bool ScXMLCachedRowAttrAccess::Cache::hasCache(sal_Int32 nTab, sal_Int32 nRow) const
+{
+ return mnTab == nTab && mnRow1 <= nRow && nRow <= mnRow2;
+}
+
+ScXMLCachedRowAttrAccess::ScXMLCachedRowAttrAccess(ScDocument* pDoc) :
+ mpDoc(pDoc) {}
+
+bool ScXMLCachedRowAttrAccess::rowHidden(sal_Int32 nTab, sal_Int32 nRow, sal_Int32& nEndRow)
+{
+ if (!maHidden.hasCache(nTab, nRow))
+ {
+ SCROW nRow1, nRow2;
+ maHidden.mbValue = mpDoc->RowHidden(
+ static_cast<SCROW>(nRow), static_cast<SCTAB>(nTab), &nRow1, &nRow2);
+ maHidden.mnTab = nTab;
+ maHidden.mnRow1 = static_cast<sal_Int32>(nRow1);
+ maHidden.mnRow2 = static_cast<sal_Int32>(nRow2);
+ }
+
+ nEndRow = maHidden.mnRow2;
+ return maHidden.mbValue;
+}
+
+bool ScXMLCachedRowAttrAccess::rowFiltered(sal_Int32 nTab, sal_Int32 nRow, sal_Int32& nEndRow)
+{
+ if (!maFiltered.hasCache(nTab, nRow))
+ {
+ SCROW nRow1, nRow2;
+ maFiltered.mbValue = mpDoc->RowFiltered(
+ static_cast<SCROW>(nRow), static_cast<SCTAB>(nTab), &nRow1, &nRow2);
+ maFiltered.mnTab = nTab;
+ maFiltered.mnRow1 = static_cast<sal_Int32>(nRow1);
+ maFiltered.mnRow2 = static_cast<sal_Int32>(nRow2);
+ }
+ nEndRow = maFiltered.mnRow2;
+ return maFiltered.mbValue;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/cachedattraccess.hxx b/sc/source/filter/xml/cachedattraccess.hxx
new file mode 100644
index 000000000..1af7c8b6b
--- /dev/null
+++ b/sc/source/filter/xml/cachedattraccess.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+class ScDocument;
+
+/**
+ * Wrapper for accessing hidden and filtered row attributes. It caches last
+ * accessed values for a current range, to avoid fetching values for every
+ * single row.
+ */
+class ScXMLCachedRowAttrAccess
+{
+ struct Cache
+ {
+ sal_Int32 mnTab;
+ sal_Int32 mnRow1;
+ sal_Int32 mnRow2;
+ bool mbValue;
+ Cache();
+ bool hasCache(sal_Int32 nTab, sal_Int32 nRow) const;
+ };
+
+public:
+ explicit ScXMLCachedRowAttrAccess(ScDocument* pDoc);
+
+ bool rowHidden(sal_Int32 nTab, sal_Int32 nRow, sal_Int32& nEndRow);
+ bool rowFiltered(sal_Int32 nTab, sal_Int32 nRow, sal_Int32& nEndRow);
+private:
+ Cache maHidden;
+ Cache maFiltered;
+ ScDocument* mpDoc;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/celltextparacontext.cxx b/sc/source/filter/xml/celltextparacontext.cxx
new file mode 100644
index 000000000..5bbe5f20d
--- /dev/null
+++ b/sc/source/filter/xml/celltextparacontext.cxx
@@ -0,0 +1,442 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "celltextparacontext.hxx"
+#include "xmlimprt.hxx"
+#include "xmlcelli.hxx"
+
+#include <comphelper/string.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLCellTextParaContext::ScXMLCellTextParaContext(
+ ScXMLImport& rImport, ScXMLTableRowCellContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void SAL_CALL ScXMLCellTextParaContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!maContent.isEmpty())
+ mrParentCxt.PushParagraphSpan(maContent, OUString());
+
+ mrParentCxt.PushParagraphEnd();
+}
+
+void SAL_CALL ScXMLCellTextParaContext::characters( const OUString& rChars )
+{
+ maContent += rChars;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellTextParaContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ if (!maContent.isEmpty())
+ {
+ mrParentCxt.PushParagraphSpan(maContent, OUString());
+ maContent.clear();
+ }
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_S ):
+ return new ScXMLCellFieldSContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_SPAN ):
+ return new ScXMLCellTextSpanContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_SHEET_NAME ):
+ return new ScXMLCellFieldSheetNameContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_DATE ):
+ return new ScXMLCellFieldDateContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_TITLE ):
+ return new ScXMLCellFieldTitleContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_A ):
+ return new ScXMLCellFieldURLContext(GetScImport(), *this);
+ case XML_ELEMENT( TEXT, XML_RUBY ):
+ return new ScXMLCellTextRubyContext(GetScImport(), *this);
+ default:
+ ;
+ }
+
+ return nullptr;
+}
+
+void ScXMLCellTextParaContext::PushSpan(const OUString& rSpan, const OUString& rStyleName)
+{
+ mrParentCxt.PushParagraphSpan(rSpan, rStyleName);
+}
+
+void ScXMLCellTextParaContext::PushFieldSheetName(const OUString& rStyleName)
+{
+ mrParentCxt.PushParagraphFieldSheetName(rStyleName);
+}
+
+void ScXMLCellTextParaContext::PushFieldDate(const OUString& rStyleName)
+{
+ mrParentCxt.PushParagraphFieldDate(rStyleName);
+}
+
+void ScXMLCellTextParaContext::PushFieldTitle(const OUString& rStyleName)
+{
+ mrParentCxt.PushParagraphFieldDocTitle(rStyleName);
+}
+
+void ScXMLCellTextParaContext::PushFieldURL(
+ const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame)
+{
+ mrParentCxt.PushParagraphFieldURL(rURL, rRep, rStyleName, rTargetFrame);
+}
+
+ScXMLCellTextSpanContext::ScXMLCellTextSpanContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void SAL_CALL ScXMLCellTextSpanContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TEXT, XML_STYLE_NAME ):
+ maStyleName = aIter.toString();
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void SAL_CALL ScXMLCellTextSpanContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ submitContentAndClear();
+}
+
+void SAL_CALL ScXMLCellTextSpanContext::characters( const OUString& rChars )
+{
+ maContent += rChars;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellTextSpanContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ submitContentAndClear();
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_SHEET_NAME ):
+ {
+ ScXMLCellFieldSheetNameContext* p = new ScXMLCellFieldSheetNameContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_DATE ):
+ {
+ ScXMLCellFieldDateContext* p = new ScXMLCellFieldDateContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_TITLE ):
+ {
+ ScXMLCellFieldTitleContext* p = new ScXMLCellFieldTitleContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_A ):
+ {
+ ScXMLCellFieldURLContext* p = new ScXMLCellFieldURLContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_S ):
+ {
+ ScXMLCellFieldSContext* p = new ScXMLCellFieldSContext(GetScImport(), mrParentCxt);
+ p->SetStyleName(maStyleName);
+ return p;
+ }
+ default:
+ ;
+ }
+
+ return nullptr;
+}
+
+void ScXMLCellTextSpanContext::submitContentAndClear()
+{
+ if (!maContent.isEmpty())
+ {
+ mrParentCxt.PushSpan(maContent, maStyleName);
+ maContent.clear();
+ }
+}
+
+ScXMLCellFieldSheetNameContext::ScXMLCellFieldSheetNameContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void ScXMLCellFieldSheetNameContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldSheetNameContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParentCxt.PushFieldSheetName(maStyleName);
+}
+
+ScXMLCellFieldDateContext::ScXMLCellFieldDateContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void ScXMLCellFieldDateContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldDateContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParentCxt.PushFieldDate(maStyleName);
+}
+
+ScXMLCellFieldTitleContext::ScXMLCellFieldTitleContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void ScXMLCellFieldTitleContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldTitleContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParentCxt.PushFieldTitle(maStyleName);
+}
+
+ScXMLCellFieldURLContext::ScXMLCellFieldURLContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void ScXMLCellFieldURLContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldURLContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ maURL = aIter.toString();
+ break;
+ case XML_ELEMENT( XLINK, XML_TYPE ):
+ // Ignored for now.
+ break;
+ case XML_ELEMENT( OFFICE, XML_TARGET_FRAME_NAME ):
+ maTargetFrame = aIter.toString();
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void SAL_CALL ScXMLCellFieldURLContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParentCxt.PushFieldURL(maURL, maRep, maStyleName, maTargetFrame);
+}
+
+void SAL_CALL ScXMLCellFieldURLContext::characters( const OUString& rChars )
+{
+ maRep += rChars;
+}
+
+ScXMLCellFieldSContext::ScXMLCellFieldSContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent),
+ mnCount(1)
+{
+}
+
+void ScXMLCellFieldSContext::SetStyleName(const OUString& rStyleName)
+{
+ maStyleName = rStyleName;
+}
+
+void SAL_CALL ScXMLCellFieldSContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TEXT, XML_C ):
+ mnCount = aIter.toInt32();
+ if (mnCount <= 0)
+ mnCount = 1; // worth a warning?
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void SAL_CALL ScXMLCellFieldSContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (mnCount)
+ PushSpaces();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellFieldSContext::createFastChildContext(
+ sal_Int32 /*nElement*/, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ // <text:s> does not have child elements, but ...
+ if (mnCount)
+ {
+ PushSpaces();
+ mnCount = 0;
+ }
+
+ return nullptr;
+}
+
+void ScXMLCellFieldSContext::PushSpaces()
+{
+ if (mnCount > 0)
+ {
+ if (mnCount == 1)
+ mrParentCxt.PushSpan(" ", maStyleName);
+ else
+ {
+ OUStringBuffer aBuf( mnCount);
+ comphelper::string::padToLength( aBuf, mnCount, ' ');
+ mrParentCxt.PushSpan( aBuf.makeStringAndClear(), maStyleName);
+ }
+ }
+}
+
+ScXMLCellTextRubyContext::ScXMLCellTextRubyContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLImportContext(rImport),
+ mrParentCxt(rParent)
+{
+}
+
+void SAL_CALL ScXMLCellTextRubyContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TEXT, XML_STYLE_NAME ):
+ // This is ruby style instead of text style.
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellTextRubyContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_RUBY_BASE ):
+ {
+ ScXMLCellRubyBaseContext* p = new ScXMLCellRubyBaseContext(GetScImport(), mrParentCxt);
+ return p;
+ }
+ case XML_ELEMENT( TEXT, XML_RUBY_TEXT ):
+ {
+ ScXMLCellRubyTextContext* p = new ScXMLCellRubyTextContext(GetScImport(), maRubyText, maRubyTextStyle);
+ return p;
+ }
+ default:
+ ;
+ }
+
+ return nullptr;
+}
+
+ScXMLCellRubyBaseContext::ScXMLCellRubyBaseContext(
+ ScXMLImport& rImport, ScXMLCellTextParaContext& rParent) :
+ ScXMLCellTextSpanContext( rImport, rParent),
+ mrParentCxt(rParent)
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLCellRubyBaseContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ submitContentAndClear();
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_SPAN ):
+ return new ScXMLCellTextSpanContext(GetScImport(), mrParentCxt);
+ default:
+ ;
+ }
+ return nullptr;
+}
+
+ScXMLCellRubyTextContext::ScXMLCellRubyTextContext(
+ ScXMLImport& rImport, OUString& rRubyText, OUString& rRubyTextStyle) :
+ ScXMLImportContext(rImport),
+ mrRubyText(rRubyText),
+ mrRubyTextStyle(rRubyTextStyle)
+{
+}
+
+void SAL_CALL ScXMLCellRubyTextContext::startFastElement( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TEXT, XML_STYLE_NAME ):
+ mrRubyTextStyle = aIter.toString();
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+void SAL_CALL ScXMLCellRubyTextContext::characters( const OUString& rChars )
+{
+ mrRubyText+= rChars;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/celltextparacontext.hxx b/sc/source/filter/xml/celltextparacontext.hxx
new file mode 100644
index 000000000..65b1036d6
--- /dev/null
+++ b/sc/source/filter/xml/celltextparacontext.hxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+class ScXMLImport;
+class ScXMLTableRowCellContext;
+
+/**
+ * This context handles <text:p> element inside <table:table-cell>.
+ */
+class ScXMLCellTextParaContext : public ScXMLImportContext
+{
+ ScXMLTableRowCellContext& mrParentCxt;
+ OUString maContent;
+public:
+ ScXMLCellTextParaContext(ScXMLImport& rImport, ScXMLTableRowCellContext& rParent);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void PushSpan(const OUString& rSpan, const OUString& rStyleName);
+ void PushFieldSheetName(const OUString& rStyleName);
+ void PushFieldDate(const OUString& rStyleName);
+ void PushFieldTitle(const OUString& rStyleName);
+ void PushFieldURL(const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame);
+};
+
+/**
+ * This context handles <text:span> element inside <text:p>.
+ */
+class ScXMLCellTextSpanContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+ OUString maContent;
+public:
+ ScXMLCellTextSpanContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ void submitContentAndClear();
+};
+
+/**
+ * This context handles <text:sheet-name> element inside <text:p>.
+ */
+class ScXMLCellFieldSheetNameContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+public:
+ ScXMLCellFieldSheetNameContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/**
+ * This context handles <text:date> element inside <text:p>.
+ */
+class ScXMLCellFieldDateContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+public:
+ ScXMLCellFieldDateContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/**
+ * This context handles <text:title> element inside <text:p>.
+ */
+class ScXMLCellFieldTitleContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+public:
+ ScXMLCellFieldTitleContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/**
+ * This context handles <text:a> element inside <text:p> or <text:span>.
+ */
+class ScXMLCellFieldURLContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+ OUString maURL;
+ OUString maRep;
+ OUString maTargetFrame;
+public:
+ ScXMLCellFieldURLContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+};
+
+/**
+ * This context handles <text:s> element inside <text:p> or <text:span>.
+ */
+class ScXMLCellFieldSContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maStyleName;
+ sal_Int32 mnCount;
+
+ void PushSpaces();
+public:
+ ScXMLCellFieldSContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ void SetStyleName(const OUString& rStyleName);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+/**
+ * This context handles <text:ruby> element inside <text:p>.
+ */
+class ScXMLCellTextRubyContext : public ScXMLImportContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+ OUString maRubyTextStyle;
+ OUString maRubyText;
+public:
+ ScXMLCellTextRubyContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+/**
+ * This context handles <text:ruby-base> element inside <text:ruby>.
+ */
+class ScXMLCellRubyBaseContext : public ScXMLCellTextSpanContext
+{
+ ScXMLCellTextParaContext& mrParentCxt;
+public:
+ ScXMLCellRubyBaseContext(ScXMLImport& rImport, ScXMLCellTextParaContext& rParent);
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+/**
+ * This context handles <text:ruby-text> element inside <text:ruby>.
+ */
+class ScXMLCellRubyTextContext : public ScXMLImportContext
+{
+ OUString& mrRubyText;
+ OUString& mrRubyTextStyle;
+public:
+ ScXMLCellRubyTextContext(ScXMLImport& rImport, OUString& rRubyText, OUString& rRubyTextStyle);
+
+ virtual void SAL_CALL startFastElement( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL characters( const OUString& aChars ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/datastreamimport.cxx b/sc/source/filter/xml/datastreamimport.cxx
new file mode 100644
index 000000000..f749e6178
--- /dev/null
+++ b/sc/source/filter/xml/datastreamimport.cxx
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "datastreamimport.hxx"
+#include "xmlimprt.hxx"
+
+#include <rangeutl.hxx>
+#include <importfilterdata.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <formula/grammar.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLDataStreamContext::ScXMLDataStreamContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext(rImport),
+ mbRefreshOnEmpty(false),
+ meInsertPos(sc::ImportPostProcessData::DataStream::InsertBottom)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch ( aIter.getToken() )
+ {
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ maURL = GetScImport().GetAbsoluteReference( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ sal_Int32 nOffset = 0;
+ if (!ScRangeStringConverter::GetRangeFromString(
+ maRange, aIter.toString(), *pDoc, formula::FormulaGrammar::CONV_OOO, nOffset))
+ maRange.SetInvalid();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_EMPTY_LINE_REFRESH ):
+ mbRefreshOnEmpty = IsXMLToken( aIter, XML_TRUE );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_INSERTION_POSITION ):
+ meInsertPos = IsXMLToken( aIter, XML_TOP ) ?
+ sc::ImportPostProcessData::DataStream::InsertTop :
+ sc::ImportPostProcessData::DataStream::InsertBottom;
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+ScXMLDataStreamContext::~ScXMLDataStreamContext() {}
+
+void SAL_CALL ScXMLDataStreamContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!maRange.IsValid())
+ // Range must be valid.
+ return;
+
+ sc::ImportPostProcessData* pData = GetScImport().GetPostProcessData();
+ if (!pData)
+ return;
+
+ pData->mpDataStream.reset(new sc::ImportPostProcessData::DataStream);
+ sc::ImportPostProcessData::DataStream& rData = *pData->mpDataStream;
+
+ rData.maURL = maURL;
+ rData.maRange = maRange;
+ rData.mbRefreshOnEmpty = mbRefreshOnEmpty;
+ rData.meInsertPos = meInsertPos;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/datastreamimport.hxx b/sc/source/filter/xml/datastreamimport.hxx
new file mode 100644
index 000000000..de6135488
--- /dev/null
+++ b/sc/source/filter/xml/datastreamimport.hxx
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+#include <importfilterdata.hxx>
+#include <address.hxx>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLDataStreamContext : public ScXMLImportContext
+{
+ OUString maURL;
+ ScRange maRange;
+ bool mbRefreshOnEmpty;
+ sc::ImportPostProcessData::DataStream::InsertPos meInsertPos;
+
+public:
+ ScXMLDataStreamContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLDataStreamContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/editattributemap.cxx b/sc/source/filter/xml/editattributemap.cxx
new file mode 100644
index 000000000..4b30edf14
--- /dev/null
+++ b/sc/source/filter/xml/editattributemap.cxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "editattributemap.hxx"
+
+#include <editeng/eeitem.hxx>
+#include <editeng/memberids.h>
+#include <xmloff/xmlnamespace.hxx>
+
+ScXMLEditAttributeMap::Entry const aEntries[] = {
+
+ { XML_NAMESPACE_FO, "color", "CharColor", EE_CHAR_COLOR, 0 },
+ { XML_NAMESPACE_STYLE, "font-charset", "CharFontCharSet", EE_CHAR_FONTINFO, MID_FONT_CHAR_SET },
+ { XML_NAMESPACE_STYLE, "font-charset-asian", "CharFontCharSetAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_CHAR_SET },
+ { XML_NAMESPACE_STYLE, "font-charset-complex", "CharFontCharSetComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_CHAR_SET },
+ { XML_NAMESPACE_FO, "font-family", "CharFontName", EE_CHAR_FONTINFO, MID_FONT_FAMILY_NAME },
+ { XML_NAMESPACE_STYLE, "font-family-asian", "CharFontNameAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_FAMILY_NAME },
+ { XML_NAMESPACE_STYLE, "font-family-complex", "CharFontNameComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_FAMILY_NAME },
+ { XML_NAMESPACE_STYLE, "font-family-generic", "CharFontFamily", EE_CHAR_FONTINFO, MID_FONT_FAMILY },
+ { XML_NAMESPACE_STYLE, "font-family-generic-asian", "CharFontFamilyAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_FAMILY },
+ { XML_NAMESPACE_STYLE, "font-family-generic-complex", "CharFontFamilyComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_FAMILY },
+ { XML_NAMESPACE_STYLE, "font-pitch", "CharFontPitch", EE_CHAR_FONTINFO, MID_FONT_PITCH },
+ { XML_NAMESPACE_STYLE, "font-pitch-asian", "CharFontPitchAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_PITCH },
+ { XML_NAMESPACE_STYLE, "font-pitch-complex", "CharFontPitchComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_PITCH },
+ { XML_NAMESPACE_FO, "font-size", "CharHeight", EE_CHAR_FONTHEIGHT, MID_FONTHEIGHT },
+ { XML_NAMESPACE_STYLE, "font-size-asian", "CharHeightAsian", EE_CHAR_FONTHEIGHT_CJK, MID_FONTHEIGHT },
+ { XML_NAMESPACE_STYLE, "font-size-complex", "CharHeightComplex", EE_CHAR_FONTHEIGHT_CTL, MID_FONTHEIGHT },
+ { XML_NAMESPACE_FO, "font-style", "CharPosture", EE_CHAR_ITALIC, MID_POSTURE },
+ { XML_NAMESPACE_STYLE, "font-style-asian", "CharPostureAsian", EE_CHAR_ITALIC_CJK, MID_POSTURE },
+ { XML_NAMESPACE_STYLE, "font-style-complex", "CharPostureComplex", EE_CHAR_ITALIC_CTL, MID_POSTURE },
+ { XML_NAMESPACE_STYLE, "font-style-name", "CharFontStyleName", EE_CHAR_FONTINFO, MID_FONT_STYLE_NAME },
+ { XML_NAMESPACE_STYLE, "font-style-name-asian", "CharFontStyleNameAsian", EE_CHAR_FONTINFO_CJK, MID_FONT_STYLE_NAME },
+ { XML_NAMESPACE_STYLE, "font-style-name-complex", "CharFontStyleNameComplex", EE_CHAR_FONTINFO_CTL, MID_FONT_STYLE_NAME },
+ { XML_NAMESPACE_FO, "font-weight", "CharWeight", EE_CHAR_WEIGHT, MID_WEIGHT },
+ { XML_NAMESPACE_STYLE, "font-weight-asian", "CharWeightAsian", EE_CHAR_WEIGHT_CJK, MID_WEIGHT },
+ { XML_NAMESPACE_STYLE, "font-weight-complex", "CharWeightComplex", EE_CHAR_WEIGHT_CTL, MID_WEIGHT },
+ { XML_NAMESPACE_STYLE, "text-overline-width", "CharOverline", EE_CHAR_OVERLINE, MID_TL_STYLE },
+ { XML_NAMESPACE_STYLE, "text-overline-color", "CharOverlineColor", EE_CHAR_OVERLINE, MID_TL_COLOR },
+ { XML_NAMESPACE_STYLE, "text-overline-color", "CharOverlineHasColor", EE_CHAR_OVERLINE, MID_TL_HASCOLOR },
+ { XML_NAMESPACE_STYLE, "text-underline-width", "CharUnderline", EE_CHAR_UNDERLINE, MID_TL_STYLE },
+ { XML_NAMESPACE_STYLE, "text-underline-color", "CharUnderlineColor", EE_CHAR_UNDERLINE, MID_TL_COLOR },
+ { XML_NAMESPACE_STYLE, "text-underline-color", "CharUnderlineHasColor", EE_CHAR_UNDERLINE, MID_TL_HASCOLOR },
+ { XML_NAMESPACE_STYLE, "text-line-through-mode", "CharWordMode", EE_CHAR_WLM, 0 },
+ { XML_NAMESPACE_STYLE, "text-line-through-type", "CharStrikeout", EE_CHAR_STRIKEOUT, MID_CROSS_OUT },
+ { XML_NAMESPACE_STYLE, "font-relief", "CharRelief", EE_CHAR_RELIEF, MID_RELIEF },
+ { XML_NAMESPACE_STYLE, "text-outline", "CharContoured", EE_CHAR_OUTLINE, 0 },
+ { XML_NAMESPACE_FO, "text-shadow", "CharShadowed", EE_CHAR_SHADOW, 0 },
+ { XML_NAMESPACE_FO, "letter-spacing", "CharKerning", EE_CHAR_KERNING, 0 },
+ { XML_NAMESPACE_STYLE, "letter-kerning", "CharAutoKerning", EE_CHAR_PAIRKERNING, 0 },
+ { XML_NAMESPACE_STYLE, "text-scale", "CharScaleWidth", EE_CHAR_FONTWIDTH, 0 },
+ { XML_NAMESPACE_STYLE, "text-position", "CharEscapement", EE_CHAR_ESCAPEMENT, MID_ESC },
+ { XML_NAMESPACE_STYLE, "text-position", "CharEscapementHeight", EE_CHAR_ESCAPEMENT, MID_ESC_HEIGHT },
+ { XML_NAMESPACE_STYLE, "text-emphasize", "CharEmphasis", EE_CHAR_EMPHASISMARK, MID_EMPHASIS },
+ // The following 3 "country" entries are just placeholders for language,
+ // country, script and rfc-language-tag, which all map to CharLocale,
+ // EE_CHAR_LANGUAGE and MID_LANG_LOCALE and are handled individually.
+ { XML_NAMESPACE_FO, "country", "CharLocale", EE_CHAR_LANGUAGE, MID_LANG_LOCALE },
+ { XML_NAMESPACE_STYLE, "country-asian", "CharLocaleAsian", EE_CHAR_LANGUAGE_CJK, MID_LANG_LOCALE },
+ { XML_NAMESPACE_STYLE, "country-complex", "CharLocaleComplex", EE_CHAR_LANGUAGE_CTL, MID_LANG_LOCALE },
+};
+
+ScXMLEditAttributeMap::ScXMLEditAttributeMap()
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aEntries); ++i)
+ {
+ maAPIEntries.emplace(
+ OUString::createFromAscii(aEntries[i].mpAPIName), &aEntries[i]);
+
+ maItemIDEntries.emplace(aEntries[i].mnItemID, &aEntries[i]);
+ }
+}
+
+const ScXMLEditAttributeMap::Entry* ScXMLEditAttributeMap::getEntryByAPIName(const OUString& rAPIName) const
+{
+ StrToEntriesType::const_iterator it = maAPIEntries.find(rAPIName);
+ return it == maAPIEntries.end() ? nullptr : it->second;
+}
+
+const ScXMLEditAttributeMap::Entry* ScXMLEditAttributeMap::getEntryByItemID(sal_uInt16 nItemID) const
+{
+ IndexToEntriesType::const_iterator it = maItemIDEntries.find(nItemID);
+ return it == maItemIDEntries.end() ? nullptr : it->second;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/editattributemap.hxx b/sc/source/filter/xml/editattributemap.hxx
new file mode 100644
index 000000000..713912191
--- /dev/null
+++ b/sc/source/filter/xml/editattributemap.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+#include <unordered_map>
+
+/**
+ * Provide mapping from ODF text formatting styles to EditEngine's, for
+ * rich-text cell content import.
+ */
+class ScXMLEditAttributeMap
+{
+public:
+ struct Entry
+ {
+ sal_uInt16 nmXMLNS;
+ const char* mpXMLName;
+ const char* mpAPIName;
+ sal_uInt16 mnItemID;
+ sal_uInt8 mnFlag;
+ };
+
+ ScXMLEditAttributeMap();
+
+ const Entry* getEntryByAPIName(const OUString& rAPIName) const;
+ const Entry* getEntryByItemID(sal_uInt16 nItemID) const;
+
+private:
+ typedef std::unordered_map<OUString, const Entry*> StrToEntriesType;
+ typedef std::unordered_map<sal_uInt16, const Entry*> IndexToEntriesType;
+ StrToEntriesType maAPIEntries;
+ IndexToEntriesType maItemIDEntries;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/importcontext.cxx b/sc/source/filter/xml/importcontext.cxx
new file mode 100644
index 000000000..ef493f23b
--- /dev/null
+++ b/sc/source/filter/xml/importcontext.cxx
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "importcontext.hxx"
+#include "xmlimprt.hxx"
+
+ScXMLImportContext::ScXMLImportContext(SvXMLImport& rImport ) :
+ SvXMLImportContext( rImport )
+{
+}
+
+ScXMLImport& ScXMLImportContext::GetScImport()
+{
+ return static_cast<ScXMLImport&>(GetImport());
+}
+
+const ScXMLImport& ScXMLImportContext::GetScImport() const
+{
+ return static_cast<const ScXMLImport&>(GetImport());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/importcontext.hxx b/sc/source/filter/xml/importcontext.hxx
new file mode 100644
index 000000000..d070433b2
--- /dev/null
+++ b/sc/source/filter/xml/importcontext.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <xmloff/xmlictxt.hxx>
+
+class ScXMLImport;
+
+/**
+ * This class exists only to provide GetScImport() to its derived classes.
+ */
+class ScXMLImportContext : public SvXMLImportContext
+{
+public:
+ ScXMLImportContext(SvXMLImport& rImport);
+
+protected:
+ ScXMLImport& GetScImport();
+ const ScXMLImport& GetScImport() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/pivotsource.cxx b/sc/source/filter/xml/pivotsource.cxx
new file mode 100644
index 000000000..0f7b07f5d
--- /dev/null
+++ b/sc/source/filter/xml/pivotsource.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "pivotsource.hxx"
+
+#include <dpsave.hxx>
+
+#include <algorithm>
+
+namespace sc {
+
+PivotTableSources::SelectedPages::SelectedPages( ScDPObject* pObj, SelectedPagesType&& rSelected ) :
+ mpDP(pObj), maSelectedPages(std::move(rSelected)) {}
+
+PivotTableSources::SheetSource::SheetSource( ScDPObject* pObj, const ScSheetSourceDesc& rDesc ) :
+ mpDP(pObj), maDesc(rDesc) {}
+
+PivotTableSources::DBSource::DBSource( ScDPObject* pObj, const ScImportSourceDesc& rDesc ) :
+ mpDP(pObj), maDesc(rDesc) {}
+
+PivotTableSources::ServiceSource::ServiceSource( ScDPObject* pObj, const ScDPServiceDesc& rDesc ) :
+ mpDP(pObj), maDesc(rDesc) {}
+
+PivotTableSources::PivotTableSources() {}
+
+void PivotTableSources::appendSheetSource( ScDPObject* pObj, const ScSheetSourceDesc& rDesc )
+{
+ maSheetSources.emplace_back(pObj, rDesc);
+}
+
+void PivotTableSources::appendDBSource( ScDPObject* pObj, const ScImportSourceDesc& rDesc )
+{
+ maDBSources.emplace_back(pObj, rDesc);
+}
+
+void PivotTableSources::appendServiceSource( ScDPObject* pObj, const ScDPServiceDesc& rDesc )
+{
+ maServiceSources.emplace_back(pObj, rDesc);
+}
+
+void PivotTableSources::appendSelectedPages( ScDPObject* pObj, SelectedPagesType&& rSelected )
+{
+ if (rSelected.empty())
+ return;
+
+ maSelectedPagesList.emplace_back(pObj, std::move(rSelected));
+}
+
+namespace {
+
+struct SelectedPageProcessor
+{
+ void operator() ( PivotTableSources::SelectedPages& rItem )
+ {
+ // Set selected pages after building all dimension members.
+ if (!rItem.mpDP)
+ return;
+
+ rItem.mpDP->BuildAllDimensionMembers();
+ ScDPSaveData* pSaveData = rItem.mpDP->GetSaveData();
+ if (!pSaveData)
+ return;
+
+ for (const auto& [rDimName, rSelected] : rItem.maSelectedPages)
+ {
+ ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName(rDimName);
+ if (!pDim)
+ continue;
+
+ pDim->SetCurrentPage(&rSelected);
+ }
+ }
+};
+
+struct PivotSheetDescSetter
+{
+ void operator() ( sc::PivotTableSources::SheetSource& rSrc )
+ {
+ ScDPObject* pObj = rSrc.mpDP;
+ pObj->SetSheetDesc(rSrc.maDesc);
+ }
+};
+
+struct PivotDBDescSetter
+{
+ void operator() ( sc::PivotTableSources::DBSource& rSrc )
+ {
+ ScDPObject* pObj = rSrc.mpDP;
+ pObj->SetImportDesc(rSrc.maDesc);
+ }
+};
+
+struct PivotServiceDataSetter
+{
+ void operator() ( sc::PivotTableSources::ServiceSource& rSrc )
+ {
+ ScDPObject* pObj = rSrc.mpDP;
+ pObj->SetServiceData(rSrc.maDesc);
+ }
+};
+
+}
+
+void PivotTableSources::process()
+{
+ std::for_each(maSheetSources.begin(), maSheetSources.end(), PivotSheetDescSetter());
+ std::for_each(maDBSources.begin(), maDBSources.end(), PivotDBDescSetter());
+ std::for_each(maServiceSources.begin(), maServiceSources.end(), PivotServiceDataSetter());
+ std::for_each(maSelectedPagesList.begin(), maSelectedPagesList.end(), SelectedPageProcessor());
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/pivotsource.hxx b/sc/source/filter/xml/pivotsource.hxx
new file mode 100644
index 000000000..bfffa7e1c
--- /dev/null
+++ b/sc/source/filter/xml/pivotsource.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <dpshttab.hxx>
+#include <dpsdbtab.hxx>
+#include <dpobject.hxx>
+
+#include <unordered_map>
+#include <vector>
+
+namespace sc
+{
+/**
+ * Store pivot table data that need to be post-processed at the end of the
+ * import.
+ */
+struct PivotTableSources
+{
+ typedef std::unordered_map<OUString, OUString> SelectedPagesType;
+
+ struct SelectedPages
+ {
+ ScDPObject* mpDP;
+ SelectedPagesType maSelectedPages;
+
+ SelectedPages(ScDPObject* pObj, SelectedPagesType&& rSelected);
+ };
+
+ struct SheetSource
+ {
+ ScDPObject* mpDP;
+ ScSheetSourceDesc maDesc;
+
+ SheetSource(ScDPObject* pObj, const ScSheetSourceDesc& rDesc);
+ };
+
+ struct DBSource
+ {
+ ScDPObject* mpDP;
+ ScImportSourceDesc maDesc;
+
+ DBSource(ScDPObject* pObj, const ScImportSourceDesc& rDesc);
+ };
+
+ struct ServiceSource
+ {
+ ScDPObject* mpDP;
+ ScDPServiceDesc maDesc;
+
+ ServiceSource(ScDPObject* pObj, const ScDPServiceDesc& rDesc);
+ };
+
+ std::vector<SelectedPages> maSelectedPagesList;
+ std::vector<SheetSource> maSheetSources;
+ std::vector<DBSource> maDBSources;
+ std::vector<ServiceSource> maServiceSources;
+
+ PivotTableSources();
+
+ void appendSheetSource(ScDPObject* pObj, const ScSheetSourceDesc& rDesc);
+ void appendDBSource(ScDPObject* pObj, const ScImportSourceDesc& rDesc);
+ void appendServiceSource(ScDPObject* pObj, const ScDPServiceDesc& rDesc);
+
+ void appendSelectedPages(ScDPObject* pObj, SelectedPagesType&& rSelected);
+
+ void process();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/sheetdata.cxx b/sc/source/filter/xml/sheetdata.cxx
new file mode 100644
index 000000000..51ae5c3aa
--- /dev/null
+++ b/sc/source/filter/xml/sheetdata.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ustring.hxx>
+#include <osl/diagnose.h>
+#include <xmloff/families.hxx>
+#include <xmloff/namespacemap.hxx>
+
+#include <algorithm>
+
+#include <sheetdata.hxx>
+
+ScSheetSaveData::ScSheetSaveData() :
+ mnStartTab( -1 ),
+ mnStartOffset( -1 ),
+ maPreviousNote( OUString(), OUString(), ScAddress::INITIALIZE_INVALID ),
+ mbInSupportedSave( false )
+{
+}
+
+ScSheetSaveData::~ScSheetSaveData()
+{
+}
+
+void ScSheetSaveData::AddCellStyle( const OUString& rName, const ScAddress& rCellPos )
+{
+ maCellStyles.emplace_back( rName, rCellPos );
+}
+
+void ScSheetSaveData::AddColumnStyle( const OUString& rName, const ScAddress& rCellPos )
+{
+ maColumnStyles.emplace_back( rName, rCellPos );
+}
+
+void ScSheetSaveData::AddRowStyle( const OUString& rName, const ScAddress& rCellPos )
+{
+ maRowStyles.emplace_back( rName, rCellPos );
+}
+
+void ScSheetSaveData::AddTableStyle( const OUString& rName, const ScAddress& rCellPos )
+{
+ maTableStyles.emplace_back( rName, rCellPos );
+}
+
+void ScSheetSaveData::HandleNoteStyles( const OUString& rStyleName, const OUString& rTextName, const ScAddress& rCellPos )
+{
+ // only consecutive duplicates (most common case) are filtered out here,
+ // the others are found when the styles are created
+
+ if ( rStyleName == maPreviousNote.maStyleName &&
+ rTextName == maPreviousNote.maTextStyle &&
+ rCellPos.Tab() == maPreviousNote.maCellPos.Tab() )
+ {
+ // already stored for the same sheet - ignore
+ return;
+ }
+
+ ScNoteStyleEntry aNewEntry( rStyleName, rTextName, rCellPos );
+ maPreviousNote = aNewEntry;
+ maNoteStyles.push_back( aNewEntry );
+}
+
+void ScSheetSaveData::AddNoteContentStyle( XmlStyleFamily nFamily, const OUString& rName, const ScAddress& rCellPos, const ESelection& rSelection )
+{
+ if ( nFamily == XmlStyleFamily::TEXT_PARAGRAPH )
+ maNoteParaStyles.emplace_back( rName, rCellPos, rSelection );
+ else
+ maNoteTextStyles.emplace_back( rName, rCellPos, rSelection );
+}
+
+void ScSheetSaveData::AddTextStyle( const OUString& rName, const ScAddress& rCellPos, const ESelection& rSelection )
+{
+ maTextStyles.emplace_back( rName, rCellPos, rSelection );
+}
+
+void ScSheetSaveData::BlockSheet( SCTAB nTab )
+{
+ if ( nTab >= static_cast<SCTAB>(maBlocked.size()) )
+ maBlocked.resize( nTab + 1, false ); // fill vector with "false" entries
+
+ maBlocked[nTab] = true;
+}
+
+bool ScSheetSaveData::IsSheetBlocked( SCTAB nTab ) const
+{
+ if ( nTab < static_cast<SCTAB>(maBlocked.size()) )
+ return maBlocked[nTab];
+ else
+ return false;
+}
+
+void ScSheetSaveData::AddStreamPos( SCTAB nTab, sal_Int32 nStartOffset, sal_Int32 nEndOffset )
+{
+ if ( nTab >= static_cast<SCTAB>(maStreamEntries.size()) )
+ maStreamEntries.resize( nTab + 1 );
+
+ maStreamEntries[nTab] = ScStreamEntry( nStartOffset, nEndOffset );
+}
+
+void ScSheetSaveData::StartStreamPos( SCTAB nTab, sal_Int32 nStartOffset )
+{
+ OSL_ENSURE( mnStartTab < 0, "StartStreamPos without EndStreamPos" );
+
+ mnStartTab = nTab;
+ mnStartOffset = nStartOffset;
+}
+
+void ScSheetSaveData::EndStreamPos( sal_Int32 nEndOffset )
+{
+ if ( mnStartTab >= 0 )
+ {
+ AddStreamPos( mnStartTab, mnStartOffset, nEndOffset );
+ mnStartTab = -1;
+ mnStartOffset = -1;
+ }
+}
+
+void ScSheetSaveData::GetStreamPos( SCTAB nTab, sal_Int32& rStartOffset, sal_Int32& rEndOffset ) const
+{
+ if ( nTab < static_cast<SCTAB>(maStreamEntries.size()) )
+ {
+ const ScStreamEntry& rEntry = maStreamEntries[nTab];
+ rStartOffset = rEntry.mnStartOffset;
+ rEndOffset = rEntry.mnEndOffset;
+ }
+ else
+ rStartOffset = rEndOffset = -1;
+}
+
+bool ScSheetSaveData::HasStreamPos( SCTAB nTab ) const
+{
+ sal_Int32 nStartOffset = -1;
+ sal_Int32 nEndOffset = -1;
+ GetStreamPos( nTab, nStartOffset, nEndOffset );
+ return ( nStartOffset >= 0 && nEndOffset >= 0 );
+}
+
+void ScSheetSaveData::ResetSaveEntries()
+{
+ maSaveEntries.clear();
+}
+
+void ScSheetSaveData::AddSavePos( SCTAB nTab, sal_Int32 nStartOffset, sal_Int32 nEndOffset )
+{
+ if ( nTab >= static_cast<SCTAB>(maSaveEntries.size()) )
+ maSaveEntries.resize( nTab + 1 );
+
+ maSaveEntries[nTab] = ScStreamEntry( nStartOffset, nEndOffset );
+}
+
+void ScSheetSaveData::UseSaveEntries()
+{
+ maStreamEntries = maSaveEntries;
+}
+
+void ScSheetSaveData::StoreInitialNamespaces( const SvXMLNamespaceMap& rNamespaces )
+{
+ // the initial namespaces are just removed from the list of loaded namespaces,
+ // so only an unordered_map of the prefixes is needed.
+
+ const NameSpaceHash& rNameHash = rNamespaces.GetAllEntries();
+ for (const auto& rEntry : rNameHash)
+ {
+ maInitialPrefixes.insert( rEntry.first );
+ }
+}
+
+void ScSheetSaveData::StoreLoadedNamespaces( const SvXMLNamespaceMap& rNamespaces )
+{
+ // store the loaded namespaces, so the prefixes in copied stream fragments remain valid
+
+ const NameSpaceHash& rNameHash = rNamespaces.GetAllEntries();
+ for (const auto& [rName, rEntry] : rNameHash)
+ {
+ // ignore the initial namespaces
+ if ( maInitialPrefixes.find( rName ) == maInitialPrefixes.end() )
+ {
+ maLoadedNamespaces.emplace_back( rEntry.sPrefix, rEntry.sName, rEntry.nKey );
+ }
+ }
+}
+
+static bool lcl_NameInHash( const NameSpaceHash& rNameHash, const OUString& rName )
+{
+ return std::any_of(rNameHash.begin(), rNameHash.end(),
+ [&rName](const NameSpaceHash::value_type& rEntry) { return rEntry.second.sName == rName; });
+}
+
+bool ScSheetSaveData::AddLoadedNamespaces( SvXMLNamespaceMap& rNamespaces ) const
+{
+ // Add the loaded namespaces to the name space map.
+
+ // look for conflicts
+ // (if the loaded namespaces were added first, this might not be necessary)
+ const NameSpaceHash& rNameHash = rNamespaces.GetAllEntries();
+ auto bConflict = std::any_of(maLoadedNamespaces.begin(), maLoadedNamespaces.end(),
+ [&rNameHash](const ScLoadedNamespaceEntry& rLoadedNamespace) {
+ NameSpaceHash::const_iterator aHashIter = rNameHash.find( rLoadedNamespace.maPrefix );
+
+ // same prefix, but different name: loaded namespaces can't be used
+ bool bNameConflict = (aHashIter != rNameHash.end()) && (aHashIter->second.sName != rLoadedNamespace.maName);
+
+ // a second prefix for the same name would confuse SvXMLNamespaceMap lookup,
+ // so this is also considered a conflict
+ bool bPrefixConflict = (aHashIter == rNameHash.end()) && lcl_NameInHash(rNameHash, rLoadedNamespace.maName);
+
+ return bNameConflict || bPrefixConflict;
+ });
+ if (bConflict)
+ return false;
+
+ // only if there were no conflicts, add the entries that aren't in the map already
+ // (the key is needed if the same namespace is added later within an element)
+ for (const auto& rLoadedNamespace : maLoadedNamespaces)
+ {
+ NameSpaceHash::const_iterator aHashIter = rNameHash.find( rLoadedNamespace.maPrefix );
+ if ( aHashIter == rNameHash.end() )
+ rNamespaces.Add( rLoadedNamespace.maPrefix, rLoadedNamespace.maName, rLoadedNamespace.mnKey );
+ }
+
+ return true; // success
+}
+
+void ScSheetSaveData::SetInSupportedSave( bool bSet )
+{
+ mbInSupportedSave = bSet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlannoi.cxx b/sc/source/filter/xml/xmlannoi.cxx
new file mode 100644
index 000000000..db12eb148
--- /dev/null
+++ b/sc/source/filter/xml/xmlannoi.cxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlannoi.hxx"
+#include "xmlimprt.hxx"
+#include "xmlconti.hxx"
+#include "XMLTableShapeImportHelper.hxx"
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLAnnotationData::ScXMLAnnotationData() :
+ mbUseShapePos( false ),
+ mbShown( false )
+{
+}
+
+ScXMLAnnotationData::~ScXMLAnnotationData()
+{
+}
+
+ScXMLAnnotationContext::ScXMLAnnotationContext( ScXMLImport& rImport,
+ sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLAnnotationData& rAnnotationData) :
+ ScXMLImportContext( rImport ),
+ mrAnnotationData( rAnnotationData )
+{
+ uno::Reference<drawing::XShapes> xLocalShapes (GetScImport().GetTables().GetCurrentXShapes());
+ if (xLocalShapes.is())
+ {
+ XMLTableShapeImportHelper* pTableShapeImport = static_cast<XMLTableShapeImportHelper*>(GetScImport().GetShapeImport().get());
+ pTableShapeImport->SetAnnotation(this);
+ pShapeContext.reset( XMLShapeImportHelper::CreateGroupChildContext(
+ GetScImport(), nElement, xAttrList, xLocalShapes, true) );
+ }
+
+ for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
+ {
+ switch( aIter.getToken() )
+ {
+ case XML_ELEMENT(OFFICE, XML_AUTHOR):
+ {
+ maAuthorBuffer = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_CREATE_DATE):
+ {
+ maCreateDateBuffer = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_CREATE_DATE_STRING):
+ {
+ maCreateDateStringBuffer = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_DISPLAY):
+ {
+ mrAnnotationData.mbShown = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT(SVG, XML_X):
+ case XML_ELEMENT(SVG_COMPAT, XML_X):
+ {
+ mrAnnotationData.mbUseShapePos = true;
+ }
+ break;
+ case XML_ELEMENT(SVG, XML_Y):
+ case XML_ELEMENT(SVG_COMPAT, XML_Y):
+ {
+ mrAnnotationData.mbUseShapePos = true;
+ }
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("xmloff", aIter);
+ }
+ }
+}
+
+ScXMLAnnotationContext::~ScXMLAnnotationContext()
+{
+}
+
+void ScXMLAnnotationContext::startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList)
+{
+ if (pShapeContext)
+ pShapeContext->startFastElement(nElement, xAttrList);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLAnnotationContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(DC, XML_CREATOR):
+ return new ScXMLContentContext(GetScImport(), maAuthorBuffer);
+ case XML_ELEMENT(DC, XML_DATE):
+ return new ScXMLContentContext(GetScImport(), maCreateDateBuffer);
+ case XML_ELEMENT(META, XML_DATE_STRING):
+ return new ScXMLContentContext(GetScImport(), maCreateDateStringBuffer);
+ }
+
+ if( pShapeContext )
+ {
+ auto p = pShapeContext->createFastChildContext(nElement, xAttrList);
+ if (p)
+ return p;
+ }
+
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ return nullptr;
+}
+
+void ScXMLAnnotationContext::characters( const OUString& rChars )
+{
+ maTextBuffer.append(rChars);
+}
+
+void ScXMLAnnotationContext::endFastElement(sal_Int32 nElement)
+{
+ if (pShapeContext)
+ {
+ pShapeContext->endFastElement(nElement);
+ pShapeContext.reset();
+ }
+
+ mrAnnotationData.maAuthor = maAuthorBuffer.makeStringAndClear();
+ mrAnnotationData.maCreateDate = maCreateDateBuffer.makeStringAndClear();
+ if (mrAnnotationData.maCreateDate.isEmpty())
+ mrAnnotationData.maCreateDate = maCreateDateStringBuffer.makeStringAndClear();
+ mrAnnotationData.maSimpleText = maTextBuffer.makeStringAndClear();
+
+ XMLTableShapeImportHelper* pTableShapeImport = static_cast<XMLTableShapeImportHelper*>(GetScImport().GetShapeImport().get());
+ pTableShapeImport->SetAnnotation(nullptr);
+}
+
+void ScXMLAnnotationContext::SetShape( const uno::Reference< drawing::XShape >& rxShape, const uno::Reference< drawing::XShapes >& rxShapes,
+ const OUString& rStyleName, const OUString& rTextStyle )
+{
+ mrAnnotationData.mxShape = rxShape;
+ mrAnnotationData.mxShapes = rxShapes;
+ mrAnnotationData.maStyleName = rStyleName;
+ mrAnnotationData.maTextStyle = rTextStyle;
+}
+
+void ScXMLAnnotationContext::AddContentStyle( XmlStyleFamily nFamily, const OUString& rName, const ESelection& rSelection )
+{
+ mrAnnotationData.maContentStyles.emplace_back( nFamily, rName, rSelection );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlannoi.hxx b/sc/source/filter/xml/xmlannoi.hxx
new file mode 100644
index 000000000..9889b5092
--- /dev/null
+++ b/sc/source/filter/xml/xmlannoi.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <xmloff/xmlictxt.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <editeng/editdata.hxx>
+#include "importcontext.hxx"
+
+#include <vector>
+
+class ScXMLImport;
+enum class XmlStyleFamily;
+namespace com::sun::star::drawing { class XShape; }
+namespace com::sun::star::drawing { class XShapes; }
+
+struct ScXMLAnnotationStyleEntry
+{
+ XmlStyleFamily mnFamily;
+ OUString maName;
+ ESelection maSelection;
+
+ ScXMLAnnotationStyleEntry( XmlStyleFamily nFam, const OUString& rNam, const ESelection& rSel ) :
+ mnFamily( nFam ),
+ maName( rNam ),
+ maSelection( rSel )
+ {
+ }
+};
+
+struct ScXMLAnnotationData
+{
+ css::uno::Reference< css::drawing::XShape >
+ mxShape;
+ css::uno::Reference< css::drawing::XShapes >
+ mxShapes;
+ OUString maAuthor;
+ OUString maCreateDate;
+ OUString maSimpleText;
+ OUString maStyleName;
+ OUString maTextStyle;
+ bool mbUseShapePos;
+ bool mbShown;
+ std::vector<ScXMLAnnotationStyleEntry> maContentStyles;
+
+ explicit ScXMLAnnotationData();
+ ~ScXMLAnnotationData();
+};
+
+class ScXMLAnnotationContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLAnnotationContext( ScXMLImport& rImport,
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList,
+ ScXMLAnnotationData& rAnnotationData);
+
+ virtual ~ScXMLAnnotationContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL startFastElement(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList) override;
+
+ virtual void SAL_CALL characters( const OUString& rChars ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ void SetShape(
+ const css::uno::Reference< css::drawing::XShape >& rxShape,
+ const css::uno::Reference< css::drawing::XShapes >& rxShapes,
+ const OUString& rStyleName, const OUString& rTextStyle );
+
+ void AddContentStyle( XmlStyleFamily nFamily, const OUString& rName, const ESelection& rSelection );
+
+private:
+ ScXMLAnnotationData& mrAnnotationData;
+ OUStringBuffer maTextBuffer;
+ OUStringBuffer maAuthorBuffer;
+ OUStringBuffer maCreateDateBuffer;
+ OUStringBuffer maCreateDateStringBuffer;
+ std::unique_ptr<SvXMLImportContext> pShapeContext;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlbodyi.cxx b/sc/source/filter/xml/xmlbodyi.cxx
new file mode 100644
index 000000000..2234f5697
--- /dev/null
+++ b/sc/source/filter/xml/xmlbodyi.cxx
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include <docuno.hxx>
+#include <sheetdata.hxx>
+
+#include "xmlbodyi.hxx"
+#include "xmltabi.hxx"
+#include "xmlnexpi.hxx"
+#include "xmldrani.hxx"
+#include "xmlimprt.hxx"
+#include "xmldpimp.hxx"
+#include "xmlcvali.hxx"
+#include "xmllabri.hxx"
+#include "xmlmappingi.hxx"
+#include "XMLConsolidationContext.hxx"
+#include "XMLDDELinksContext.hxx"
+#include "XMLCalculationSettingsContext.hxx"
+#include "XMLTrackedChangesContext.hxx"
+#include "XMLChangeTrackingImportHelper.hxx"
+#include "XMLEmptyContext.hxx"
+#include "XMLDetectiveContext.hxx"
+#include <scerrors.hxx>
+#include <tabprotection.hxx>
+#include "datastreamimport.hxx"
+#include <sax/fastattribs.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <rtl/math.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLBodyContext::ScXMLBodyContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ meHash1(PASSHASH_SHA1),
+ meHash2(PASSHASH_UNSPECIFIED),
+ bProtected(false),
+ bHadCalculationSettings(false),
+ pChangeTrackingImportHelper(nullptr)
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (pDoc)
+ {
+ // ODF 1.1 and earlier => GRAM_PODF; ODF 1.2 and later => GRAM_ODFF;
+ // no version => earlier than 1.2 => GRAM_PODF.
+ formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_ODFF;
+ const OUString& aVer( rImport.GetODFVersion());
+ sal_Int32 nLen = aVer.getLength();
+ SAL_INFO("sc.filter", "ScXMLBodyContext ODFVersion: nLen: " << nLen << " str : " << aVer);
+ if (!nLen)
+ eGrammar = formula::FormulaGrammar::GRAM_PODF;
+ else
+ {
+ // In case there was a micro version, e.g. "1.2.3", this would
+ // still yield major.minor, but pParsedEnd (5th parameter, not
+ // passed here) would point before string end upon return.
+ double fVer = ::rtl::math::stringToDouble( aVer, '.', 0 );
+ if (fVer < 1.2)
+ eGrammar = formula::FormulaGrammar::GRAM_PODF;
+ }
+ pDoc->SetStorageGrammar( eGrammar);
+ }
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &it : *rAttrList)
+ {
+ sal_Int32 nToken = it.getToken();
+ if( IsTokenInNamespace(nToken, XML_NAMESPACE_TABLE) )
+ {
+ const sal_Int32 nLocalToken = nToken & TOKEN_MASK;
+ if( nLocalToken == XML_STRUCTURE_PROTECTED )
+ bProtected = IsXMLToken( it, XML_TRUE );
+ else if ( nLocalToken == XML_PROTECTION_KEY )
+ sPassword = it.toString();
+ else if ( nLocalToken == XML_PROTECTION_KEY_DIGEST_ALGORITHM )
+ meHash1 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ else if ( nLocalToken == XML_PROTECTION_KEY_DIGEST_ALGORITHM_2 )
+ meHash2 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ }
+ else if ( nToken == XML_ELEMENT( LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2 ) )
+ {
+ meHash2 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ }
+ }
+}
+
+ScXMLBodyContext::~ScXMLBodyContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLBodyContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetScImport().GetModel())->GetSheetSaveData();
+ if ( pSheetData && pSheetData->HasStartPos() )
+ {
+ // stream part to copy ends before the next child element
+ sal_Int32 nEndOffset = GetScImport().GetByteOffset();
+ pSheetData->EndStreamPos( nEndOffset );
+ }
+
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_TRACKED_CHANGES ):
+ pChangeTrackingImportHelper = GetScImport().GetChangeTrackingImportHelper();
+ if (pChangeTrackingImportHelper)
+ pContext = new ScXMLTrackedChangesContext( GetScImport(), pAttribList, pChangeTrackingImportHelper);
+ break;
+ case XML_ELEMENT( TABLE, XML_CALCULATION_SETTINGS ):
+ pContext = new ScXMLCalculationSettingsContext( GetScImport(), pAttribList );
+ bHadCalculationSettings = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATIONS ):
+ pContext = new ScXMLContentValidationsContext( GetScImport() );
+ break;
+ case XML_ELEMENT( TABLE, XML_LABEL_RANGES ):
+ pContext = new ScXMLLabelRangesContext( GetScImport() );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE ):
+ if (GetScImport().GetTables().GetCurrentSheet() >= MAXTAB)
+ {
+ GetScImport().SetRangeOverflowType(SCWARN_IMPORT_SHEET_OVERFLOW);
+ pContext = new ScXMLEmptyContext(GetScImport() );
+ }
+ else
+ {
+ pContext = new ScXMLTableContext( GetScImport(), pAttribList );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_NAMED_EXPRESSIONS ):
+ pContext = new ScXMLNamedExpressionsContext (
+ GetScImport(),
+ std::make_shared<ScXMLNamedExpressionsContext::GlobalInserter>(GetScImport()) );
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_RANGES ):
+ pContext = new ScXMLDatabaseRangesContext ( GetScImport() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_MAPPINGS ):
+ pContext = new ScXMLMappingsContext(GetScImport());
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_RANGE ):
+ pContext = new ScXMLDatabaseRangeContext ( GetScImport(),
+ pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_TABLES ):
+ pContext = new ScXMLDataPilotTablesContext ( GetScImport() );
+ break;
+ case XML_ELEMENT( TABLE, XML_CONSOLIDATION ):
+ pContext = new ScXMLConsolidationContext ( GetScImport(), pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_DDE_LINKS ):
+ pContext = new ScXMLDDELinksContext ( GetScImport() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_STREAM_SOURCE ):
+ pContext = new ScXMLDataStreamContext(GetScImport(), pAttribList);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLBodyContext::characters(const OUString &)
+{
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetScImport().GetModel())->GetSheetSaveData();
+ if ( pSheetData && pSheetData->HasStartPos() )
+ {
+ // stream part to copy ends before any content (whitespace) within the spreadsheet element
+ sal_Int32 nEndOffset = GetScImport().GetByteOffset();
+ pSheetData->EndStreamPos( nEndOffset );
+ }
+ // otherwise ignore
+}
+
+void SAL_CALL ScXMLBodyContext::endFastElement(sal_Int32 nElement)
+{
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetScImport().GetModel())->GetSheetSaveData();
+ if ( pSheetData && pSheetData->HasStartPos() )
+ {
+ // stream part to copy ends before the closing tag of spreadsheet element
+ sal_Int32 nEndOffset = GetScImport().GetByteOffset();
+ pSheetData->EndStreamPos( nEndOffset );
+ }
+
+ if ( pSheetData )
+ {
+ // store the loaded namespaces (for the office:spreadsheet element),
+ // so the prefixes in copied stream fragments remain valid
+ const SvXMLNamespaceMap& rNamespaces = GetImport().GetNamespaceMap();
+ pSheetData->StoreLoadedNamespaces( rNamespaces );
+ }
+
+ if (!bHadCalculationSettings)
+ {
+ // #111055#; set calculation settings defaults if there is no calculation settings element
+ rtl::Reference<ScXMLCalculationSettingsContext> pContext( new ScXMLCalculationSettingsContext(GetScImport(), nullptr) );
+ pContext->endFastElement( nElement );
+ }
+
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+
+ ScMyImpDetectiveOpArray* pDetOpArray = GetScImport().GetDetectiveOpArray();
+ ScDocument* pDoc = GetScImport().GetDocument();
+ ScMyImpDetectiveOp aDetOp;
+
+ if (!(pDoc && GetScImport().GetModel().is()))
+ return;
+
+ if (pDetOpArray)
+ {
+ pDetOpArray->Sort();
+ while( pDetOpArray->GetFirstOp( aDetOp ) )
+ {
+ ScDetOpData aOpData( aDetOp.aPosition, aDetOp.eOpType );
+ pDoc->AddDetectiveOperation( aOpData );
+ }
+ }
+
+ if (pChangeTrackingImportHelper)
+ pChangeTrackingImportHelper->CreateChangeTrack(pDoc);
+
+ // #i37959# handle document protection after the sheet settings
+ if (!bProtected)
+ return;
+
+ ScDocProtection aProtection;
+ aProtection.setProtected(true);
+
+ uno::Sequence<sal_Int8> aPass;
+ if (!sPassword.isEmpty())
+ {
+ ::comphelper::Base64::decode(aPass, sPassword);
+ aProtection.setPasswordHash(aPass, meHash1, meHash2);
+ }
+
+ pDoc->SetDocProtection(&aProtection);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlbodyi.hxx b/sc/source/filter/xml/xmlbodyi.hxx
new file mode 100644
index 000000000..6db678017
--- /dev/null
+++ b/sc/source/filter/xml/xmlbodyi.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+#include <tabprotection.hxx>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLChangeTrackingImportHelper;
+
+class ScXMLBodyContext : public ScXMLImportContext
+{
+ OUString sPassword;
+ ScPasswordHash meHash1;
+ ScPasswordHash meHash2;
+ bool bProtected;
+ bool bHadCalculationSettings;
+
+ ScXMLChangeTrackingImportHelper* pChangeTrackingImportHelper;
+
+public:
+
+ ScXMLBodyContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLBodyContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ virtual void SAL_CALL characters(const OUString & aChars) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcelli.cxx b/sc/source/filter/xml/xmlcelli.cxx
new file mode 100644
index 000000000..a14424eda
--- /dev/null
+++ b/sc/source/filter/xml/xmlcelli.cxx
@@ -0,0 +1,1521 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <limits>
+#include <memory>
+#include "xmlcelli.hxx"
+#include "xmlimprt.hxx"
+#include "xmlannoi.hxx"
+#include <global.hxx>
+#include <cellvalue.hxx>
+#include <document.hxx>
+#include <docuno.hxx>
+#include <postit.hxx>
+#include <sheetdata.hxx>
+#include <cellform.hxx>
+#include <validat.hxx>
+#include <patattr.hxx>
+#include <scitems.hxx>
+#include <docpool.hxx>
+
+#include "XMLTableShapeImportHelper.hxx"
+#include "XMLStylesImportHelper.hxx"
+#include "celltextparacontext.hxx"
+#include "XMLCellRangeSourceContext.hxx"
+
+#include <arealink.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <scerrors.hxx>
+#include <editutil.hxx>
+#include <formulacell.hxx>
+#include "editattributemap.hxx"
+#include <tokenarray.hxx>
+#include <scmatrix.hxx>
+#include <documentimport.hxx>
+#include <externalrefmgr.hxx>
+
+#include <xmloff/maptype.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/prstylei.hxx>
+#include <xmloff/xmlimppr.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <sax/tools/converter.hxx>
+#include <sax/fastattribs.hxx>
+
+#include <com/sun/star/util/NumberFormat.hpp>
+
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <i18nlangtag/lang.h>
+
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableRowCellContext::ParaFormat::ParaFormat(const ScEditEngineDefaulter& rEditEngine) :
+ maItemSet(rEditEngine.GetEmptyItemSet()) {}
+
+ScXMLTableRowCellContext::Field::Field(std::unique_ptr<SvxFieldData> pData) : mpData(std::move(pData)) {}
+
+ScXMLTableRowCellContext::Field::~Field()
+{
+}
+
+ScXMLTableRowCellContext::ScXMLTableRowCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bTempIsCovered,
+ const sal_Int32 nTempRepeatedRows ) :
+ ScXMLImportContext( rImport ),
+ mpEditEngine(GetScImport().GetEditEngine()),
+ mnCurParagraph(0),
+ fValue(std::numeric_limits<double>::quiet_NaN()),
+ nMergedRows(1),
+ nMatrixRows(0),
+ nRepeatedRows(nTempRepeatedRows),
+ nMergedCols(1),
+ nMatrixCols(0),
+ nColsRepeated(1),
+ rXMLImport(rImport),
+ eGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT),
+ nCellType(util::NumberFormat::TEXT),
+ bIsMerged(false),
+ bIsMatrix(false),
+ bIsCovered(bTempIsCovered),
+ bIsEmpty(true),
+ mbNewValueType(false),
+ mbErrorValue(false),
+ bSolarMutexLocked(false),
+ bFormulaTextResult(false),
+ mbPossibleErrorCell(false),
+ mbCheckWithCompilerForError(false),
+ mbEditEngineHasText(false),
+ mbHasFormatRuns(false),
+ mbHasStyle(false),
+ mbPossibleEmptyDisplay(false)
+{
+ rXMLImport.GetTables().AddColumn(bTempIsCovered);
+
+ std::optional<OUString> xStyleName;
+ std::optional<OUString> xCurrencySymbol;
+ if ( rAttrList.is() )
+ {
+ for (auto &it : *rAttrList)
+ {
+ switch ( it.getToken() )
+ {
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ xStyleName = it.toString();
+ mbHasStyle = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATION_NAME ):
+ OSL_ENSURE(!maContentValidationName, "here should be only one Validation Name");
+ if (!it.isEmpty())
+ maContentValidationName = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_ROWS_SPANNED ):
+ bIsMerged = true;
+ nMergedRows = static_cast<SCROW>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_SPANNED ):
+ bIsMerged = true;
+ nMergedCols = static_cast<SCCOL>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixCols = static_cast<SCCOL>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED ):
+ bIsMatrix = true;
+ nMatrixRows = static_cast<SCROW>(it.toInt32());
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ):
+ nColsRepeated = static_cast<SCCOL>(
+ std::min<sal_Int32>( rImport.GetDocument()->GetSheetLimits().GetMaxColCount(),
+ std::max( it.toInt32(), static_cast<sal_Int32>(1) ) ));
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE_TYPE ):
+ nCellType = ScXMLImport::GetCellType(it.toCString(), it.getLength());
+ bIsEmpty = false;
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_VALUE_TYPE ):
+ if(it.isString( "error" ) )
+ mbErrorValue = true;
+ else
+ nCellType = ScXMLImport::GetCellType(it.toCString(), it.getLength());
+ bIsEmpty = false;
+ mbNewValueType = true;
+ break;
+ case XML_ELEMENT( OFFICE, XML_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ fValue = it.toDouble();
+ bIsEmpty = false;
+
+ //if office:value="0", let's get the text:p in case this is
+ //a special case in HasSpecialCaseFormulaText(). If it
+ //turns out not to be a special case, we'll use the 0 value.
+ if(fValue == 0.0)
+ bFormulaTextResult = true;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_DATE_VALUE ):
+ {
+ if (!it.isEmpty() && rXMLImport.SetNullDateOnUnitConverter())
+ {
+ rXMLImport.GetMM100UnitConverter().convertDateTime(fValue, it.toView());
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_TIME_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ ::sax::Converter::convertDuration(fValue, it.toView());
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_STRING_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ OSL_ENSURE(!maStringValue, "here should be only one string value");
+ maStringValue = it.toString();
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE , XML_BOOLEAN_VALUE ):
+ {
+ if (!it.isEmpty())
+ {
+ if ( IsXMLToken( it, XML_TRUE ) )
+ fValue = 1.0;
+ else if ( IsXMLToken( it, XML_FALSE ) )
+ fValue = 0.0;
+ else
+ fValue = it.toDouble();
+ bIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FORMULA ):
+ {
+ if (!it.isEmpty())
+ {
+ OSL_ENSURE(!maFormula, "here should be only one formula");
+ OUString aFormula, aFormulaNmsp;
+ rXMLImport.ExtractFormulaNamespaceGrammar( aFormula, aFormulaNmsp, eGrammar, it.toString() );
+ maFormula = FormulaWithNamespace(aFormula, aFormulaNmsp);
+ }
+ }
+ break;
+ case XML_ELEMENT( OFFICE, XML_CURRENCY ):
+ xCurrencySymbol = it.toString();
+ break;
+ default:
+ ;
+ }
+ }
+ }
+
+ if (maFormula)
+ {
+ if (nCellType == util::NumberFormat::TEXT)
+ bFormulaTextResult = true;
+ if(nCellType == util::NumberFormat::DATETIME)
+ nCellType = util::NumberFormat::UNDEFINED;
+ //if bIsEmpty is true at this point, then there is no office value.
+ //we must get the text:p (even if it is empty) in case this a special
+ //case in HasSpecialCaseFormulaText().
+ if(bIsEmpty)
+ bFormulaTextResult = true;
+ }
+ rXMLImport.GetStylesImportHelper()->SetAttributes(std::move(xStyleName), std::move(xCurrencySymbol), nCellType);
+}
+
+ScXMLTableRowCellContext::~ScXMLTableRowCellContext()
+{
+}
+
+void ScXMLTableRowCellContext::LockSolarMutex()
+{
+ if (!bSolarMutexLocked)
+ {
+ GetScImport().LockSolarMutex();
+ bSolarMutexLocked = true;
+ }
+}
+
+namespace {
+
+bool cellExists( const ScDocument& rDoc, const ScAddress& rCellPos )
+{
+ return( rCellPos.Col() >= 0 && rCellPos.Row() >= 0 &&
+ rCellPos.Col() <= rDoc.MaxCol() && rCellPos.Row() <= rDoc.MaxRow() );
+}
+
+}
+
+void ScXMLTableRowCellContext::PushParagraphSpan(const OUString& rSpan, const OUString& rStyleName)
+{
+ sal_Int32 nBegin = maParagraph.getLength();
+ sal_Int32 nEnd = nBegin + rSpan.getLength();
+ maParagraph.append(rSpan);
+
+ PushFormat(nBegin, nEnd, rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphField(std::unique_ptr<SvxFieldData> pData, const OUString& rStyleName)
+{
+ mbHasFormatRuns = true;
+ maFields.push_back(std::make_unique<Field>(std::move(pData)));
+ Field& rField = *maFields.back();
+
+ sal_Int32 nPos = maParagraph.getLength();
+ maParagraph.append('\1'); // Placeholder text for inserted field item.
+ rField.maSelection.nStartPara = mnCurParagraph;
+ rField.maSelection.nEndPara = mnCurParagraph;
+ rField.maSelection.nStartPos = nPos;
+ rField.maSelection.nEndPos = nPos+1;
+
+ PushFormat(nPos, nPos+1, rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushFormat(sal_Int32 nBegin, sal_Int32 nEnd, const OUString& rStyleName)
+{
+ if (rStyleName.isEmpty())
+ return;
+
+ // Get the style information from xmloff.
+ rtl::Reference<XMLPropertySetMapper> xMapper = GetImport().GetTextImport()->GetTextImportPropertySetMapper()->getPropertySetMapper();
+ if (!xMapper.is())
+ // We can't do anything without the mapper.
+ return;
+
+ sal_Int32 nEntryCount = xMapper->GetEntryCount();
+
+ SvXMLStylesContext* pAutoStyles = GetImport().GetAutoStyles();
+ if (!pAutoStyles)
+ return;
+
+ // Style name for text span corresponds with the name of an automatic style.
+ const XMLPropStyleContext* pStyle = dynamic_cast<const XMLPropStyleContext*>(
+ pAutoStyles->FindStyleChildContext(XmlStyleFamily::TEXT_TEXT, rStyleName));
+
+ if (!pStyle)
+ // No style by that name found.
+ return;
+
+ const std::vector<XMLPropertyState>& rProps = pStyle->GetProperties();
+ if (rProps.empty())
+ return;
+
+ const ScXMLEditAttributeMap& rEditAttrMap = GetScImport().GetEditAttributeMap();
+
+ mbHasFormatRuns = true;
+ maFormats.push_back(std::make_unique<ParaFormat>(*mpEditEngine));
+ ParaFormat& rFmt = *maFormats.back();
+ rFmt.maSelection.nStartPara = rFmt.maSelection.nEndPara = mnCurParagraph;
+ rFmt.maSelection.nStartPos = nBegin;
+ rFmt.maSelection.nEndPos = nEnd;
+
+ // Store the used text styles for export.
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(rXMLImport.GetModel())->GetSheetSaveData();
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ pSheetData->AddTextStyle(rStyleName, aCellPos, rFmt.maSelection);
+
+ std::unique_ptr<SfxPoolItem> pPoolItem;
+ sal_uInt16 nLastItemID = EE_CHAR_END + 1;
+
+ for (const auto& rProp : rProps)
+ {
+ if (rProp.mnIndex == -1 || rProp.mnIndex >= nEntryCount)
+ continue;
+
+ const OUString& rName = xMapper->GetEntryAPIName(rProp.mnIndex);
+ const ScXMLEditAttributeMap::Entry* pEntry = rEditAttrMap.getEntryByAPIName(rName);
+ if (!pEntry)
+ continue;
+
+ if (nLastItemID != pEntry->mnItemID && pPoolItem)
+ {
+ // Flush the last item when the item ID changes.
+ rFmt.maItemSet.Put(std::move(pPoolItem));
+ }
+
+ switch (pEntry->mnItemID)
+ {
+ case EE_CHAR_FONTINFO:
+ case EE_CHAR_FONTINFO_CJK:
+ case EE_CHAR_FONTINFO_CTL:
+ {
+ // Font properties need to be consolidated into a single item.
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxFontItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxWeightItem(WEIGHT_NORMAL, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxFontHeightItem(240, 100, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxPostureItem(ITALIC_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_UNDERLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxUnderlineItem(LINESTYLE_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_OVERLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxOverlineItem(LINESTYLE_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_COLOR:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxColorItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_WLM:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxWordLineModeItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_STRIKEOUT:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCrossedOutItem(STRIKEOUT_NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_RELIEF:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCharReliefItem(FontRelief::NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_OUTLINE:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxContourItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_SHADOW:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxShadowedItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_KERNING:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxKerningItem(0, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_PAIRKERNING:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxAutoKernItem(false, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_FONTWIDTH:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxCharScaleWidthItem(100, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_ESCAPEMENT:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxEscapementItem(pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_EMPHASISMARK:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxEmphasisMarkItem(FontEmphasisMark::NONE, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ case EE_CHAR_LANGUAGE:
+ case EE_CHAR_LANGUAGE_CJK:
+ case EE_CHAR_LANGUAGE_CTL:
+ {
+ if (!pPoolItem)
+ pPoolItem.reset(new SvxLanguageItem(LANGUAGE_DONTKNOW, pEntry->mnItemID));
+
+ pPoolItem->PutValue(rProp.maValue, pEntry->mnFlag);
+ }
+ break;
+ default:
+ ;
+ }
+
+ nLastItemID = pEntry->mnItemID;
+ }
+
+ if (pPoolItem)
+ rFmt.maItemSet.Put(std::move(pPoolItem));
+}
+
+OUString ScXMLTableRowCellContext::GetFirstParagraph() const
+{
+ if (!maFirstParagraph)
+ return mpEditEngine->GetText(0);
+
+ return *maFirstParagraph;
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldDate(const OUString& rStyleName)
+{
+ PushParagraphField(std::make_unique<SvxDateField>(), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldSheetName(const OUString& rStyleName)
+{
+ SCTAB nTab = GetScImport().GetTables().GetCurrentCellPos().Tab();
+ PushParagraphField(std::make_unique<SvxTableField>(nTab), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldDocTitle(const OUString& rStyleName)
+{
+ PushParagraphField(std::make_unique<SvxFileField>(), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphFieldURL(
+ const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame)
+{
+ OUString aAbsURL = GetScImport().GetAbsoluteReference(rURL);
+ std::unique_ptr<SvxURLField> pURLField(new SvxURLField(aAbsURL, rRep, SvxURLFormat::Repr));
+ pURLField->SetTargetFrame(rTargetFrame);
+ PushParagraphField(std::move(pURLField), rStyleName);
+}
+
+void ScXMLTableRowCellContext::PushParagraphEnd()
+{
+ // EditEngine always has at least one paragraph even when its content is empty.
+
+ if (mbEditEngineHasText)
+ {
+ if (maFirstParagraph)
+ {
+ // Flush the cached first paragraph first.
+ mpEditEngine->Clear();
+ mpEditEngine->SetTextCurrentDefaults(*maFirstParagraph);
+ maFirstParagraph.reset();
+ }
+ mpEditEngine->InsertParagraph(mpEditEngine->GetParagraphCount(), maParagraph.makeStringAndClear());
+ }
+ else if (mbHasFormatRuns)
+ {
+ mpEditEngine->Clear();
+ mpEditEngine->SetTextCurrentDefaults(maParagraph.makeStringAndClear());
+ mbEditEngineHasText = true;
+ }
+ else if (mnCurParagraph == 0)
+ {
+ maFirstParagraph = maParagraph.makeStringAndClear();
+ mbEditEngineHasText = true;
+ }
+
+ ++mnCurParagraph;
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLTableRowCellContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ bool bTextP(false);
+ switch (nElement)
+ {
+ case XML_ELEMENT( TEXT, XML_P ):
+ {
+ bIsEmpty = false;
+ bTextP = true;
+
+ pContext = new ScXMLCellTextParaContext(rXMLImport, *this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SUB_TABLE ):
+ {
+ SAL_WARN("sc", "ScXMLTableRowCellContext::createFastChildContext: subtables are not supported");
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DETECTIVE ):
+ {
+ bIsEmpty = false;
+ if (!pDetectiveObjVec)
+ pDetectiveObjVec.reset( new ScMyImpDetectiveObjVec );
+ pContext = new ScXMLDetectiveContext(
+ rXMLImport, pDetectiveObjVec.get() );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_SOURCE ):
+ {
+ bIsEmpty = false;
+ if (!pCellRangeSource)
+ pCellRangeSource.reset(new ScMyImpCellRangeSource());
+ pContext = new ScXMLCellRangeSourceContext(
+ rXMLImport, pAttribList, pCellRangeSource.get() );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_ANNOTATION):
+ {
+ bIsEmpty = false;
+ OSL_ENSURE(
+ !mxAnnotationData,
+ "ScXMLTableRowCellContext::CreateChildContext - multiple annotations in one cell");
+ mxAnnotationData.reset( new ScXMLAnnotationData );
+ pContext = new ScXMLAnnotationContext( rXMLImport, nElement,
+ xAttrList, *mxAnnotationData);
+ }
+ break;
+ }
+
+ if (!pContext && !bTextP)
+ {
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ uno::Reference<drawing::XShapes> xShapes (rXMLImport.GetTables().GetCurrentXShapes());
+ if (xShapes.is())
+ {
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if (aCellPos.Col() > pDoc->MaxCol())
+ aCellPos.SetCol(pDoc->MaxCol());
+ if (aCellPos.Row() > pDoc->MaxRow())
+ aCellPos.SetRow(pDoc->MaxRow());
+ XMLTableShapeImportHelper* pTableShapeImport =
+ static_cast< XMLTableShapeImportHelper* >( rXMLImport.GetShapeImport().get() );
+ pTableShapeImport->SetOnTable(false);
+ pTableShapeImport->SetCell(aCellPos);
+ pContext = XMLShapeImportHelper::CreateGroupChildContext(
+ rXMLImport, nElement, xAttrList, xShapes);
+ if (pContext)
+ {
+ bIsEmpty = false;
+ rXMLImport.ProgressBarIncrement();
+ }
+ }
+ }
+
+ return pContext;
+}
+
+void ScXMLTableRowCellContext::DoMerge( const ScAddress& rScAddress, const SCCOL nCols, const SCROW nRows )
+{
+ SCCOL mergeToCol = rScAddress.Col() + nCols;
+ SCROW mergeToRow = rScAddress.Row() + nRows;
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ bool bInBounds = rScAddress.Col() <= pDoc->MaxCol() && rScAddress.Row() <= pDoc->MaxRow() &&
+ mergeToCol <= pDoc->MaxCol() && mergeToRow <= pDoc->MaxRow();
+ if( bInBounds )
+ {
+ pDoc->DoMerge( rScAddress.Col(), rScAddress.Row(),
+ mergeToCol, mergeToRow, rScAddress.Tab() );
+ }
+}
+
+namespace {
+
+ScValidationMode validationTypeToMode( const sheet::ValidationType eVType )
+{
+ ScValidationMode eMode;
+ switch( eVType )
+ {
+ case sheet::ValidationType_WHOLE: eMode = SC_VALID_WHOLE; break;
+ case sheet::ValidationType_DECIMAL: eMode = SC_VALID_DECIMAL; break;
+ case sheet::ValidationType_DATE: eMode = SC_VALID_DATE; break;
+ case sheet::ValidationType_TIME: eMode = SC_VALID_TIME; break;
+ case sheet::ValidationType_TEXT_LEN: eMode = SC_VALID_TEXTLEN; break;
+ case sheet::ValidationType_LIST: eMode = SC_VALID_LIST; break;
+ case sheet::ValidationType_CUSTOM: eMode = SC_VALID_CUSTOM; break;
+ default: eMode = SC_VALID_ANY; break;
+ }
+ return eMode;
+}
+
+ScValidErrorStyle validAlertToValidError( const sheet::ValidationAlertStyle eVAlertStyle )
+{
+ ScValidErrorStyle eVErrStyle;
+ switch( eVAlertStyle )
+ {
+ case sheet::ValidationAlertStyle_STOP: eVErrStyle = SC_VALERR_STOP; break;
+ case sheet::ValidationAlertStyle_WARNING: eVErrStyle = SC_VALERR_WARNING; break;
+ case sheet::ValidationAlertStyle_MACRO: eVErrStyle = SC_VALERR_MACRO; break;
+ default: eVErrStyle = SC_VALERR_INFO; break;
+ //should INFO be the default? seems to be the most unobtrusive choice.
+ }
+ return eVErrStyle;
+}
+
+}
+
+void ScXMLTableRowCellContext::SetContentValidation( const ScRange& rScRange )
+{
+ if (!maContentValidationName)
+ return;
+
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScMyImportValidation aValidation;
+ aValidation.eGrammar1 = aValidation.eGrammar2 = pDoc->GetStorageGrammar();
+ if( !rXMLImport.GetValidation(*maContentValidationName, aValidation) )
+ return;
+
+ ScValidationData aScValidationData(
+ validationTypeToMode(aValidation.aValidationType),
+ ScConditionEntry::GetModeFromApi(aValidation.aOperator),
+ aValidation.sFormula1, aValidation.sFormula2, *pDoc, ScAddress(),
+ aValidation.sFormulaNmsp1, aValidation.sFormulaNmsp2,
+ aValidation.eGrammar1, aValidation.eGrammar2
+ );
+
+ aScValidationData.SetIgnoreBlank( aValidation.bIgnoreBlanks );
+ aScValidationData.SetListType( aValidation.nShowList );
+
+ // set strings for error / input even if disabled (and disable afterwards)
+ aScValidationData.SetInput( aValidation.sInputTitle, aValidation.sInputMessage );
+ if( !aValidation.bShowInputMessage )
+ aScValidationData.ResetInput();
+ aScValidationData.SetError( aValidation.sErrorTitle, aValidation.sErrorMessage, validAlertToValidError(aValidation.aAlertStyle) );
+ if( !aValidation.bShowErrorMessage )
+ aScValidationData.ResetError();
+
+ if( !aValidation.sBaseCellAddress.isEmpty() )
+ aScValidationData.SetSrcString( aValidation.sBaseCellAddress );
+
+ sal_uLong nIndex = pDoc->AddValidationEntry( aScValidationData );
+
+ ScPatternAttr aPattern( pDoc->GetPool() );
+ aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALIDDATA, nIndex ) );
+ if( rScRange.aStart == rScRange.aEnd ) //for a single cell
+ {
+ pDoc->ApplyPattern( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aStart.Tab(), aPattern );
+ }
+ else //for repeating cells
+ {
+ pDoc->ApplyPatternAreaTab( rScRange.aStart.Col(), rScRange.aStart.Row(),
+ rScRange.aEnd.Col(), rScRange.aEnd.Row(),
+ rScRange.aStart.Tab(), aPattern );
+ }
+
+ // is the below still needed?
+ // For now, any sheet with validity is blocked from stream-copying.
+ // Later, the validation names could be stored along with the style names.
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetImport().GetModel())->GetSheetSaveData();
+ pSheetData->BlockSheet( GetScImport().GetTables().GetCurrentSheet() );
+}
+
+void ScXMLTableRowCellContext::SetContentValidation( const ScAddress& rCellPos )
+{
+ SetContentValidation( ScRange(rCellPos, rCellPos) );
+}
+
+void ScXMLTableRowCellContext::SetAnnotation(const ScAddress& rPos)
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if (!pDoc || !mxAnnotationData)
+ return;
+
+ LockSolarMutex();
+
+ ScPostIt* pNote = nullptr;
+
+ uno::Reference< drawing::XShapes > xShapes = rXMLImport.GetTables().GetCurrentXShapes();
+ sal_Int32 nOldShapeCount = xShapes.is() ? xShapes->getCount() : 0;
+
+ OSL_ENSURE( !mxAnnotationData->mxShape.is() || mxAnnotationData->mxShapes.is(),
+ "ScXMLTableRowCellContext::SetAnnotation - shape without drawing page" );
+ if( mxAnnotationData->mxShape.is() && mxAnnotationData->mxShapes.is() )
+ {
+ OSL_ENSURE( mxAnnotationData->mxShapes.get() == xShapes.get(), "ScXMLTableRowCellContext::SetAnnotation - different drawing pages" );
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape(mxAnnotationData->mxShape);
+ OSL_ENSURE( pObject, "ScXMLTableRowCellContext::SetAnnotation - cannot get SdrObject from shape" );
+
+ /* Try to reuse the drawing object already created (but only if the
+ note is visible, and the object is a caption object). */
+ if( mxAnnotationData->mbShown && mxAnnotationData->mbUseShapePos && !comphelper::LibreOfficeKit::isActive())
+ {
+ if( SdrCaptionObj* pCaption = dynamic_cast< SdrCaptionObj* >( pObject ) )
+ {
+ OSL_ENSURE( !pCaption->GetLogicRect().IsEmpty(), "ScXMLTableRowCellContext::SetAnnotation - invalid caption rectangle" );
+ // create the cell note with the caption object
+ pNote = ScNoteUtil::CreateNoteFromCaption( *pDoc, rPos, pCaption );
+ // forget pointer to object (do not create note again below)
+ pObject = nullptr;
+ }
+ }
+
+ // drawing object has not been used to create a note -> use shape data
+ if( pObject )
+ {
+ // rescue settings from drawing object before the shape is removed
+ SfxItemSet aItemSet( pObject->GetMergedItemSet() );
+ std::optional<OutlinerParaObject> pOutlinerObj;
+ if (auto p = pObject->GetOutlinerParaObject())
+ pOutlinerObj = *p;
+ tools::Rectangle aCaptionRect;
+ if( mxAnnotationData->mbUseShapePos )
+ aCaptionRect = pObject->GetLogicRect();
+ // remove the shape from the drawing page, this invalidates pObject
+ mxAnnotationData->mxShapes->remove( mxAnnotationData->mxShape );
+ pObject = nullptr;
+ // update current number of existing objects
+ if( xShapes.is() )
+ nOldShapeCount = xShapes->getCount();
+
+ // an outliner object is required (empty note captions not allowed)
+ if (pOutlinerObj)
+ {
+ // create cell note with all data from drawing object
+ if(!comphelper::LibreOfficeKit::isActive())
+ {
+ pNote = ScNoteUtil::CreateNoteFromObjectData( *pDoc, rPos,
+ std::move(aItemSet), *pOutlinerObj,
+ aCaptionRect, mxAnnotationData->mbShown );
+ }
+ else
+ {
+ pNote = ScNoteUtil::CreateNoteFromObjectData( *pDoc, rPos,
+ std::move(aItemSet), *pOutlinerObj,
+ aCaptionRect, false );
+ }
+
+ }
+ }
+ }
+ else if( !mxAnnotationData->maSimpleText.isEmpty() )
+ {
+ // create note from simple text
+ pNote = ScNoteUtil::CreateNoteFromString( *pDoc, rPos,
+ mxAnnotationData->maSimpleText, mxAnnotationData->mbShown, false );
+ }
+
+ // set author and date
+ if( pNote )
+ {
+ double fDate;
+ if (rXMLImport.GetMM100UnitConverter().convertDateTime(fDate, mxAnnotationData->maCreateDate))
+ {
+ SvNumberFormatter* pNumForm = pDoc->GetFormatTable();
+ sal_uInt32 nfIndex = pNumForm->GetFormatIndex( NF_DATE_SYS_DDMMYYYY, LANGUAGE_SYSTEM );
+ OUString aDate;
+ const Color* pColor = nullptr;
+ pNumForm->GetOutputString( fDate, nfIndex, aDate, &pColor );
+ pNote->SetDate( aDate );
+ }
+ pNote->SetAuthor( mxAnnotationData->maAuthor );
+ }
+
+ // register a shape that has been newly created in the ScNoteUtil functions
+ if( xShapes.is() && (nOldShapeCount < xShapes->getCount()) )
+ {
+ uno::Reference< drawing::XShape > xShape;
+ rXMLImport.GetShapeImport()->shapeWithZIndexAdded( xShape, xShapes->getCount() );
+ }
+
+ // store the style names for stream copying
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(rXMLImport.GetModel())->GetSheetSaveData();
+ pSheetData->HandleNoteStyles( mxAnnotationData->maStyleName, mxAnnotationData->maTextStyle, rPos );
+
+ for (const auto& rContentStyle : mxAnnotationData->maContentStyles)
+ {
+ pSheetData->AddNoteContentStyle( rContentStyle.mnFamily, rContentStyle.maName, rPos, rContentStyle.maSelection );
+ }
+}
+
+// core implementation
+void ScXMLTableRowCellContext::SetDetectiveObj( const ScAddress& rPosition )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( !pDoc || !cellExists(*pDoc, rPosition) || !pDetectiveObjVec || pDetectiveObjVec->empty() )
+ return;
+
+ LockSolarMutex();
+ ScDetectiveFunc aDetFunc( *pDoc, rPosition.Tab() );
+ uno::Reference<container::XIndexAccess> xShapesIndex = rXMLImport.GetTables().GetCurrentXShapes(); // make draw page
+ for(const auto& rDetectiveObj : *pDetectiveObjVec)
+ {
+ aDetFunc.InsertObject( rDetectiveObj.eObjType, rPosition, rDetectiveObj.aSourceRange, rDetectiveObj.bHasError );
+ if (xShapesIndex.is())
+ {
+ sal_Int32 nShapes = xShapesIndex->getCount();
+ uno::Reference < drawing::XShape > xShape;
+ rXMLImport.GetShapeImport()->shapeWithZIndexAdded(xShape, nShapes);
+ }
+ }
+}
+
+// core implementation
+void ScXMLTableRowCellContext::SetCellRangeSource( const ScAddress& rPosition )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( !pDoc || !cellExists(*pDoc, rPosition) || !pCellRangeSource || pCellRangeSource->sSourceStr.isEmpty() ||
+ pCellRangeSource->sFilterName.isEmpty() || pCellRangeSource->sURL.isEmpty() )
+ return;
+
+ LockSolarMutex();
+ ScRange aDestRange( rPosition.Col(), rPosition.Row(), rPosition.Tab(),
+ rPosition.Col() + static_cast<SCCOL>(pCellRangeSource->nColumns - 1),
+ rPosition.Row() + static_cast<SCROW>(pCellRangeSource->nRows - 1), rPosition.Tab() );
+ OUString sFilterName( pCellRangeSource->sFilterName );
+ OUString sSourceStr( pCellRangeSource->sSourceStr );
+ ScAreaLink* pLink = new ScAreaLink( pDoc->GetDocumentShell(), pCellRangeSource->sURL,
+ sFilterName, pCellRangeSource->sFilterOptions, sSourceStr, aDestRange, pCellRangeSource->nRefresh );
+ sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
+ pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, pCellRangeSource->sURL, &sFilterName, &sSourceStr );
+}
+
+void ScXMLTableRowCellContext::SetFormulaCell(ScFormulaCell* pFCell) const
+{
+ if(!pFCell)
+ return;
+
+ bool bMayForceNumberformat = true;
+
+ if(mbErrorValue)
+ {
+ // don't do anything here
+ // we need to recalc anyway
+ }
+ else if( bFormulaTextResult && maStringValue )
+ {
+ if( !IsPossibleErrorString() )
+ {
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ pFCell->SetHybridString(pDoc->GetSharedStringPool().intern(*maStringValue));
+ pFCell->ResetDirty();
+ // A General format doesn't force any other format for a string
+ // result, don't attempt to recalculate this later.
+ bMayForceNumberformat = false;
+ }
+ }
+ else if (std::isfinite(fValue))
+ {
+ pFCell->SetHybridDouble(fValue);
+ if (mbPossibleEmptyDisplay && fValue == 0.0)
+ {
+ // Needs to be recalculated to propagate, otherwise would be
+ // propagated as empty string. So don't ResetDirty().
+ pFCell->SetHybridEmptyDisplayedAsString();
+ }
+ else
+ pFCell->ResetDirty();
+ }
+
+ if (bMayForceNumberformat)
+ // Re-calculate to get number format only when style is not set.
+ pFCell->SetNeedNumberFormat(!mbHasStyle);
+}
+
+void ScXMLTableRowCellContext::PutTextCell( const ScAddress& rCurrentPos,
+ const SCCOL nCurrentCol, const ::std::optional< OUString >& pOUText )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ bool bDoIncrement = true;
+ //matrix reference cells that contain text formula results;
+ //cell was already put in document, just need to set text here.
+ if( pDoc && rXMLImport.GetTables().IsPartOfMatrix(rCurrentPos) )
+ {
+ ScRefCellValue aCell(*pDoc, rCurrentPos);
+ bDoIncrement = aCell.meType == CELLTYPE_FORMULA;
+ if ( bDoIncrement )
+ {
+ ScFormulaCell* pFCell = aCell.mpFormula;
+ OUString aCellString;
+ if (maStringValue)
+ aCellString = *maStringValue;
+ else if (mbEditEngineHasText)
+ aCellString = GetFirstParagraph();
+ else if ( nCurrentCol > 0 && pOUText && !pOUText->isEmpty() )
+ aCellString = *pOUText;
+ else
+ bDoIncrement = false;
+
+ if(mbErrorValue)
+ bDoIncrement = false;
+
+ if(!aCellString.isEmpty())
+ {
+ if (bDoIncrement && !IsPossibleErrorString() && pFCell)
+ {
+ pFCell->SetHybridString(pDoc->GetSharedStringPool().intern(aCellString));
+ pFCell->ResetDirty();
+ }
+ else
+ {
+ ScAddress aTopLeftMatrixCell;
+ if (pFCell && pFCell->GetMatrixOrigin(*pDoc, aTopLeftMatrixCell))
+ {
+ ScFormulaCell* pMatrixCell = pDoc->GetFormulaCell(aTopLeftMatrixCell);
+ if (pMatrixCell)
+ pMatrixCell->SetDirty();
+ }
+ else
+ SAL_WARN("sc", "matrix cell without matrix");
+ }
+ }
+ }
+ }
+ else //regular text cells
+ {
+ ScDocumentImport& rDoc = rXMLImport.GetDoc();
+ if (maStringValue)
+ {
+ rDoc.setStringCell(rCurrentPos, *maStringValue);
+ bDoIncrement = true;
+ }
+ else if (mbEditEngineHasText)
+ {
+ if (maFirstParagraph)
+ {
+ // This is a normal text without format runs.
+ rDoc.setStringCell(rCurrentPos, *maFirstParagraph);
+ }
+ else
+ {
+ // This text either has format runs, has field(s), or consists of multiple lines.
+ for (const auto& rxFormat : maFormats)
+ mpEditEngine->QuickSetAttribs(rxFormat->maItemSet, rxFormat->maSelection);
+
+ for (const auto& rxField : maFields)
+ mpEditEngine->QuickInsertField(SvxFieldItem(*rxField->mpData, EE_FEATURE_FIELD), rxField->maSelection);
+
+ // This edit engine uses the SfxItemPool instance returned
+ // from pDoc->GetEditPool() to create the text object, which
+ // is a prerequisite for using this constructor of ScEditCell.
+ rDoc.setEditCell(rCurrentPos, mpEditEngine->CreateTextObject());
+ }
+ bDoIncrement = true;
+ }
+ else if ( nCurrentCol > 0 && pOUText && !pOUText->isEmpty() )
+ {
+ rDoc.setStringCell(rCurrentPos, *pOUText);
+ bDoIncrement = true;
+ }
+ else
+ bDoIncrement = false;
+ }
+
+ // #i56027# This is about setting simple text, not edit cells,
+ // so ProgressBarIncrement must be called with bEditCell = FALSE.
+ // Formatted text that is put into the cell by the child context
+ // is handled in AddCellsToTable() (bIsEmpty is true then).
+ if (bDoIncrement)
+ rXMLImport.ProgressBarIncrement();
+}
+
+void ScXMLTableRowCellContext::PutValueCell( const ScAddress& rCurrentPos )
+{
+ //matrix reference cells that contain value formula results;
+ //cell was already put in document, just need to set value here.
+ if( rXMLImport.GetTables().IsPartOfMatrix(rCurrentPos) )
+ {
+ ScRefCellValue aCell(*rXMLImport.GetDocument(), rCurrentPos);
+ if (aCell.meType == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pFCell = aCell.mpFormula;
+ SetFormulaCell(pFCell);
+ if (pFCell)
+ pFCell->SetNeedNumberFormat( true );
+ }
+ }
+ else //regular value cell
+ {
+ // fdo#62250 absent values are not NaN, set to 0.0
+ // PutValueCell() is called only for a known cell value type,
+ // bIsEmpty==false in all these cases, no sense to check it here.
+ if (!std::isfinite( fValue))
+ fValue = 0.0;
+
+ // #i62435# Initialize the value cell's script type if the default
+ // style's number format is latin-only. If the cell uses a different
+ // format, the script type will be reset when the style is applied.
+
+ rXMLImport.GetDoc().setNumericCell(rCurrentPos, fValue);
+ }
+ rXMLImport.ProgressBarIncrement();
+}
+
+namespace {
+
+bool isEmptyOrNote( const ScDocument* pDoc, const ScAddress& rCurrentPos )
+{
+ CellType eType = pDoc->GetCellType(rCurrentPos);
+ return (eType == CELLTYPE_NONE);
+}
+
+}
+
+void ScXMLTableRowCellContext::AddTextAndValueCell( const ScAddress& rCellPos,
+ const ::std::optional< OUString >& pOUText, ScAddress& rCurrentPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScMyTables& rTables = rXMLImport.GetTables();
+ bool bWasEmpty = bIsEmpty;
+ for (SCCOL i = 0; i < nColsRepeated; ++i)
+ {
+ rCurrentPos.SetCol( rCellPos.Col() + i );
+
+ // it makes no sense to import data after the last supported column
+ // fdo#58539 & gnome#627150
+ if(rCurrentPos.Col() > pDoc->MaxCol())
+ {
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ break;
+ }
+
+ if (i > 0)
+ rTables.AddColumn(false);
+ if (!bIsEmpty)
+ {
+ for (SCROW j = 0; j < nRepeatedRows; ++j)
+ {
+ rCurrentPos.SetRow( rCellPos.Row() + j );
+
+ // it makes no sense to import data after last supported row
+ // fdo#58539 & gnome#627150
+ if(rCurrentPos.Row() > pDoc->MaxRow())
+ {
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ break;
+ }
+
+ if( (rCurrentPos.Col() == 0) && (j > 0) )
+ {
+ rTables.AddRow();
+ rTables.AddColumn(false);
+ }
+ if( cellExists(*pDoc, rCurrentPos) )
+ {
+ if( !bIsCovered || isEmptyOrNote(pDoc, rCurrentPos) )
+ {
+ switch (nCellType)
+ {
+ case util::NumberFormat::TEXT:
+ {
+ PutTextCell( rCurrentPos, i, pOUText );
+ }
+ break;
+ case util::NumberFormat::NUMBER:
+ case util::NumberFormat::PERCENT:
+ case util::NumberFormat::CURRENCY:
+ case util::NumberFormat::TIME:
+ case util::NumberFormat::DATETIME:
+ case util::NumberFormat::LOGICAL:
+ {
+ PutValueCell( rCurrentPos );
+ }
+ break;
+ default:
+ {
+ OSL_FAIL("no cell type given");
+ }
+ break;
+ }
+ }
+
+ SetAnnotation( rCurrentPos );
+ SetDetectiveObj( rCurrentPos );
+ SetCellRangeSource( rCurrentPos );
+ }
+ else
+ {
+ if (!bWasEmpty || mxAnnotationData)
+ {
+ if (rCurrentPos.Row() > pDoc->MaxRow())
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ else
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((i == 0) && (rCellPos.Col() == 0))
+ {
+ for (sal_Int32 j = 1; j < nRepeatedRows; ++j)
+ {
+ rTables.AddRow();
+ rTables.AddColumn(false);
+ }
+ }
+ }
+ }
+}
+
+bool ScXMLTableRowCellContext::CellsAreRepeated() const
+{
+ return ( (nColsRepeated > 1) || (nRepeatedRows > 1) );
+}
+
+namespace {
+
+// from ScCellObj::GetOutputString_Imp(). all of it may not be necessary.
+OUString getOutputString( ScDocument* pDoc, const ScAddress& aCellPos )
+{
+ if (!pDoc)
+ return OUString();
+
+ ScRefCellValue aCell(*pDoc, aCellPos);
+ switch (aCell.meType)
+ {
+ case CELLTYPE_NONE:
+ return OUString();
+ case CELLTYPE_EDIT:
+ {
+ // GetString on EditCell replaces linebreaks with spaces;
+ // however here we need line breaks
+ const EditTextObject* pData = aCell.mpEditText;
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText(*pData);
+ return rEngine.GetText();
+ // also don't format EditCells per NumberFormatter
+ }
+ break;
+ default:
+ {
+ // like in GetString for document (column)
+ const Color* pColor;
+ sal_uInt32 nNumFmt = pDoc->GetNumberFormat(aCellPos);
+ return ScCellFormat::GetString(aCell, nNumFmt, &pColor, *pDoc->GetFormatTable(), *pDoc);
+ }
+ }
+}
+
+}
+
+void ScXMLTableRowCellContext::AddNonFormulaCell( const ScAddress& rCellPos )
+{
+ ::std::optional< OUString > pOUText;
+
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( nCellType == util::NumberFormat::TEXT )
+ {
+ if( !bIsEmpty && !maStringValue && !mbEditEngineHasText && cellExists(*pDoc, rCellPos) && CellsAreRepeated() )
+ pOUText = getOutputString(pDoc, rCellPos);
+
+ if (!mbEditEngineHasText && !pOUText && !maStringValue)
+ bIsEmpty = true;
+ }
+
+ ScAddress aCurrentPos( rCellPos );
+ if( mxAnnotationData || pDetectiveObjVec || pCellRangeSource ) // has special content
+ bIsEmpty = false;
+
+ AddTextAndValueCell( rCellPos, pOUText, aCurrentPos );
+
+ if( CellsAreRepeated() )
+ {
+ SCCOL nStartCol( std::min(rCellPos.Col(), pDoc->MaxCol()) );
+ SCROW nStartRow( std::min(rCellPos.Row(), pDoc->MaxRow()) );
+ SCCOL nEndCol( std::min<SCCOL>(rCellPos.Col() + nColsRepeated - 1, pDoc->MaxCol()) );
+ SCROW nEndRow( std::min(rCellPos.Row() + nRepeatedRows - 1, pDoc->MaxRow()) );
+ ScRange aScRange( nStartCol, nStartRow, rCellPos.Tab(), nEndCol, nEndRow, rCellPos.Tab() );
+ SetContentValidation( aScRange );
+ rXMLImport.GetStylesImportHelper()->AddRange( aScRange );
+ }
+ else if( cellExists(*pDoc, rCellPos) )
+ {
+ rXMLImport.GetStylesImportHelper()->AddCell(rCellPos);
+ SetContentValidation( rCellPos );
+ }
+}
+
+void ScXMLTableRowCellContext::PutFormulaCell( const ScAddress& rCellPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ ScDocumentImport& rDocImport = rXMLImport.GetDoc();
+
+ OUString aText = maFormula->first;
+
+ ScExternalRefManager::ApiGuard aExtRefGuard(*pDoc);
+
+ if ( aText.isEmpty() )
+ return;
+
+ // temporary formula string as string tokens
+ std::unique_ptr<ScTokenArray> pCode(new ScTokenArray(*pDoc));
+
+ // Check the special case of a single error constant without leading
+ // '=' and create an error formula cell without tokens.
+ FormulaError nError = GetScImport().GetFormulaErrorConstant(aText);
+ if (nError != FormulaError::NONE)
+ {
+ pCode->SetCodeError(nError);
+ }
+ else
+ {
+ // 5.2 and earlier wrote broken "Err:xxx" as formula to designate
+ // an error formula cell.
+ if (aText.startsWithIgnoreAsciiCase("Err:") && aText.getLength() <= 9 &&
+ ((nError =
+ GetScImport().GetFormulaErrorConstant( OUString::Concat("#ERR") + aText.subView(4) + "!")) != FormulaError::NONE))
+ {
+ pCode->SetCodeError(nError);
+ }
+ else
+ {
+ OUString aFormulaNmsp = maFormula->second;
+ if( eGrammar != formula::FormulaGrammar::GRAM_EXTERNAL )
+ aFormulaNmsp.clear();
+ pCode->AssignXMLString( aText, aFormulaNmsp );
+ rDocImport.getDoc().IncXMLImportedFormulaCount( aText.getLength() );
+ }
+ }
+
+ ScFormulaCell* pNewCell = new ScFormulaCell(*pDoc, rCellPos, std::move(pCode), eGrammar, ScMatrixMode::NONE);
+ SetFormulaCell(pNewCell);
+ rDocImport.setFormulaCell(rCellPos, pNewCell);
+}
+
+void ScXMLTableRowCellContext::AddFormulaCell( const ScAddress& rCellPos )
+{
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ if( cellExists(*pDoc, rCellPos) )
+ {
+ SetContentValidation( rCellPos );
+ SAL_WARN_IF((nColsRepeated != 1) || (nRepeatedRows != 1), "sc", "repeated cells with formula not possible now");
+ rXMLImport.GetStylesImportHelper()->AddCell(rCellPos);
+
+ //add matrix
+ if(bIsMatrix)
+ {
+ if (nMatrixCols > 0 && nMatrixRows > 0)
+ {
+ //matrix cells are put in the document, but we must set the
+ //value/text of each matrix cell later
+ rXMLImport.GetTables().AddMatrixRange(
+ rCellPos.Col(), rCellPos.Row(),
+ std::min<SCCOL>(rCellPos.Col() + nMatrixCols - 1, pDoc->MaxCol()),
+ std::min<SCROW>(rCellPos.Row() + nMatrixRows - 1, pDoc->MaxRow()),
+ maFormula->first, maFormula->second, eGrammar);
+
+ // Set the value/text of the top-left matrix position in its
+ // cached result. For import, we only need to set the correct
+ // matrix geometry and the value type of the top-left element.
+ ScFormulaCell* pFCell = pDoc->GetFormulaCell(rCellPos);
+ if (pFCell)
+ {
+ ScMatrixRef pMat(new ScMatrix(nMatrixCols, nMatrixRows));
+ if (bFormulaTextResult && maStringValue)
+ {
+ if (!IsPossibleErrorString())
+ {
+ pFCell->SetResultMatrix(
+ nMatrixCols, nMatrixRows, pMat, new formula::FormulaStringToken(
+ pDoc->GetSharedStringPool().intern( *maStringValue)));
+ pFCell->ResetDirty();
+ }
+ }
+ else if (std::isfinite(fValue))
+ {
+ pFCell->SetResultMatrix(
+ nMatrixCols, nMatrixRows, pMat, new formula::FormulaDoubleToken(fValue));
+ pFCell->ResetDirty();
+ }
+ }
+ }
+ }
+ else
+ PutFormulaCell( rCellPos );
+
+ SetAnnotation( rCellPos );
+ SetDetectiveObj( rCellPos );
+ SetCellRangeSource( rCellPos );
+ rXMLImport.ProgressBarIncrement();
+ }
+ else
+ {
+ if (rCellPos.Row() > pDoc->MaxRow())
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_ROW_OVERFLOW);
+ else
+ rXMLImport.SetRangeOverflowType(SCWARN_IMPORT_COLUMN_OVERFLOW);
+ }
+}
+
+//There are cases where a formula cell is exported with an office:value of 0 or
+//no office:value at all, but the formula cell will have a text:p value which
+//contains the intended formula result.
+//These cases include when a formula result:
+// - is blank
+// - has a constant error value beginning with "#" (such as "#VALUE!" or "#N/A")
+// - has an "Err:[###]" (where "[###]" is an error number)
+// Libreoffice 4.1+ with ODF1.2 extended write however calcext:value-type="error" in that case
+void ScXMLTableRowCellContext::HasSpecialCaseFormulaText()
+{
+ if (!mbEditEngineHasText)
+ return;
+
+ const OUString aStr = GetFirstParagraph();
+
+ if (mbNewValueType)
+ {
+ if (aStr.isEmpty())
+ mbPossibleEmptyDisplay = true;
+ return;
+ }
+
+ if (aStr.isEmpty())
+ {
+ mbPossibleErrorCell = true;
+ mbPossibleEmptyDisplay = true;
+ }
+ else if (aStr.startsWith("Err:"))
+ mbPossibleErrorCell = true;
+ else if (aStr.startsWith("#"))
+ mbCheckWithCompilerForError = true;
+}
+
+bool ScXMLTableRowCellContext::IsPossibleErrorString() const
+{
+ if(mbNewValueType && !mbErrorValue)
+ return false;
+ else if(mbNewValueType && mbErrorValue)
+ return true;
+ return mbPossibleErrorCell || (mbCheckWithCompilerForError && maStringValue &&
+ GetScImport().GetFormulaErrorConstant(*maStringValue) != FormulaError::NONE);
+}
+
+void SAL_CALL ScXMLTableRowCellContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ HasSpecialCaseFormulaText();
+ if( bFormulaTextResult && (mbPossibleErrorCell || mbCheckWithCompilerForError) )
+ {
+ maStringValue = GetFirstParagraph();
+ }
+
+ ScAddress aCellPos = rXMLImport.GetTables().GetCurrentCellPos();
+ if( aCellPos.Col() > 0 && nRepeatedRows > 1 )
+ aCellPos.SetRow( aCellPos.Row() - (nRepeatedRows - 1) );
+ if( bIsMerged )
+ DoMerge( aCellPos, nMergedCols - 1, nMergedRows - 1 );
+
+ if (maFormula)
+ AddFormulaCell(aCellPos);
+ else
+ AddNonFormulaCell(aCellPos);
+
+ //if LockSolarMutex got used, we presumably need to ensure an UnlockSolarMutex
+ if (bSolarMutexLocked)
+ {
+ GetScImport().UnlockSolarMutex();
+ bSolarMutexLocked = false;
+ }
+
+ bIsMerged = false;
+ nMergedCols = 1;
+ nMergedRows = 1;
+ nColsRepeated = 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcelli.hxx b/sc/source/filter/xml/xmlcelli.hxx
new file mode 100644
index 000000000..74fe71029
--- /dev/null
+++ b/sc/source/filter/xml/xmlcelli.hxx
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "XMLDetectiveContext.hxx"
+#include "importcontext.hxx"
+#include <formula/grammar.hxx>
+#include <svl/itemset.hxx>
+#include <editeng/editdata.hxx>
+
+#include <optional>
+#include <memory>
+#include <vector>
+
+class ScXMLImport;
+class ScFormulaCell;
+class ScEditEngineDefaulter;
+class SvxFieldData;
+struct ScXMLAnnotationData;
+struct ScMyImpCellRangeSource;
+
+class ScXMLTableRowCellContext : public ScXMLImportContext
+{
+ struct ParaFormat
+ {
+ SfxItemSet maItemSet;
+ ESelection maSelection;
+
+ explicit ParaFormat(const ScEditEngineDefaulter& rEditEngine);
+ };
+
+ struct Field
+ {
+ std::unique_ptr<SvxFieldData> mpData;
+ ESelection maSelection;
+
+ Field(const Field&) = delete;
+ const Field& operator=(const Field&) = delete;
+
+ explicit Field(std::unique_ptr<SvxFieldData> pData);
+ ~Field();
+ };
+
+ typedef std::vector<std::unique_ptr<Field> > FieldsType;
+ typedef std::pair<OUString, OUString> FormulaWithNamespace;
+
+ std::optional<FormulaWithNamespace> maFormula; /// table:formula attribute
+ std::optional<OUString> maStringValue; /// office:string-value attribute
+ std::optional<OUString> maContentValidationName;
+ std::optional<OUString> maFirstParagraph; /// unformatted first paragraph, for better performance.
+
+ ScEditEngineDefaulter* mpEditEngine;
+ OUStringBuffer maParagraph{32};
+ sal_Int32 mnCurParagraph;
+
+ std::vector<std::unique_ptr<ParaFormat> > maFormats;
+ FieldsType maFields;
+
+ std::unique_ptr< ScXMLAnnotationData > mxAnnotationData;
+ std::unique_ptr< ScMyImpDetectiveObjVec > pDetectiveObjVec;
+ std::unique_ptr< ScMyImpCellRangeSource > pCellRangeSource;
+ double fValue;
+ SCROW nMergedRows, nMatrixRows, nRepeatedRows;
+ SCCOL nMergedCols, nMatrixCols, nColsRepeated;
+ ScXMLImport& rXMLImport;
+ formula::FormulaGrammar::Grammar eGrammar;
+ sal_Int16 nCellType;
+ bool bIsMerged;
+ bool bIsMatrix;
+ bool bIsCovered;
+ bool bIsEmpty;
+ bool mbNewValueType;
+ bool mbErrorValue;
+ bool bSolarMutexLocked;
+ bool bFormulaTextResult;
+ bool mbPossibleErrorCell;
+ bool mbCheckWithCompilerForError;
+ bool mbEditEngineHasText;
+ bool mbHasFormatRuns;
+ bool mbHasStyle;
+ bool mbPossibleEmptyDisplay;
+
+ void DoMerge(const ScAddress& rScCellPos, const SCCOL nCols, const SCROW nRows);
+
+ void SetContentValidation( const ScRange& rScRange );
+ void SetContentValidation( const ScAddress& rScCellPos );
+
+ void LockSolarMutex();
+
+ bool CellsAreRepeated() const;
+
+ void SetFormulaCell ( ScFormulaCell* pFCell ) const;
+ void PutTextCell ( const ScAddress& rScCurrentPos, const SCCOL nCurrentCol,
+ const ::std::optional< OUString >& pOUText );
+ void PutValueCell ( const ScAddress& rScCurrentPos );
+ void AddTextAndValueCell ( const ScAddress& rScCellPos,
+ const ::std::optional< OUString >& pOUText, ScAddress& rScCurrentPos );
+ void AddNonFormulaCell ( const ScAddress& rScCellPos );
+ void PutFormulaCell ( const ScAddress& rScCurrentPos );
+ void AddFormulaCell ( const ScAddress& rScCellPos );
+
+ void HasSpecialCaseFormulaText();
+
+ bool IsPossibleErrorString() const;
+
+ void PushParagraphField(std::unique_ptr<SvxFieldData> pData, const OUString& rStyleName);
+
+ void PushFormat(sal_Int32 nBegin, sal_Int32 nEnd, const OUString& rStyleName);
+
+ OUString GetFirstParagraph() const;
+
+public:
+
+ ScXMLTableRowCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bIsCovered, const sal_Int32 nRepeatedRows );
+
+ virtual ~ScXMLTableRowCellContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void PushParagraphSpan(const OUString& rSpan, const OUString& rStyleName);
+ void PushParagraphFieldDate(const OUString& rStyleName);
+ void PushParagraphFieldSheetName(const OUString& rStyleName);
+ void PushParagraphFieldDocTitle(const OUString& rStyleName);
+ void PushParagraphFieldURL(const OUString& rURL, const OUString& rRep, const OUString& rStyleName, const OUString& rTargetFrame);
+ void PushParagraphEnd();
+
+ void SetAnnotation( const ScAddress& rPosition );
+ void SetDetectiveObj( const ScAddress& rPosition );
+ void SetCellRangeSource( const ScAddress& rPosition );
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcoli.cxx b/sc/source/filter/xml/xmlcoli.cxx
new file mode 100644
index 000000000..4e52dfa41
--- /dev/null
+++ b/sc/source/filter/xml/xmlcoli.cxx
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlcoli.hxx"
+#include "xmlimprt.hxx"
+#include "xmlstyli.hxx"
+#include <document.hxx>
+#include <docuno.hxx>
+#include <olinetab.hxx>
+#include <sheetdata.hxx>
+#include <unonames.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <comphelper/servicehelper.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableColContext::ScXMLTableColContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ nColCount(1),
+ sVisibility(GetXMLToken(XML_VISIBLE))
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NUMBER_COLUMNS_REPEATED ):
+ {
+ nColCount = std::max<sal_Int32>(aIter.toInt32(), 1);
+ nColCount = std::min<sal_Int32>(nColCount, rImport.GetDocument()->GetSheetLimits().GetMaxColCount() );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ {
+ sStyleName = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_VISIBILITY ):
+ {
+ sVisibility = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DEFAULT_CELL_STYLE_NAME ):
+ {
+ sCellStyleName = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLTableColContext::~ScXMLTableColContext()
+{
+}
+
+void SAL_CALL ScXMLTableColContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScXMLImport& rXMLImport = GetScImport();
+ ScDocument* pDoc = rXMLImport.GetDocument();
+ SCTAB nSheet = rXMLImport.GetTables().GetCurrentSheet();
+ sal_Int32 nCurrentColumn = rXMLImport.GetTables().GetCurrentColCount();
+ uno::Reference<sheet::XSpreadsheet> xSheet(rXMLImport.GetTables().GetCurrentXSheet());
+ if(xSheet.is())
+ {
+ sal_Int32 nLastColumn(nCurrentColumn + nColCount - 1);
+ if (nLastColumn > pDoc->MaxCol())
+ nLastColumn = pDoc->MaxCol();
+ if (nCurrentColumn > pDoc->MaxCol())
+ nCurrentColumn = pDoc->MaxCol();
+ uno::Reference<table::XColumnRowRange> xColumnRowRange (xSheet->getCellRangeByPosition(nCurrentColumn, 0, nLastColumn, 0), uno::UNO_QUERY);
+ if (xColumnRowRange.is())
+ {
+ uno::Reference <beans::XPropertySet> xColumnProperties(xColumnRowRange->getColumns(), uno::UNO_QUERY);
+ if (xColumnProperties.is())
+ {
+ if (!sStyleName.isEmpty())
+ {
+ XMLTableStylesContext *pStyles = static_cast<XMLTableStylesContext *>(rXMLImport.GetAutoStyles());
+ if ( pStyles )
+ {
+ XMLTableStyleContext* pStyle = const_cast<XMLTableStyleContext*>(static_cast<const XMLTableStyleContext *>(pStyles->FindStyleChildContext(
+ XmlStyleFamily::TABLE_COLUMN, sStyleName, true)));
+ if (pStyle)
+ {
+ pStyle->FillPropertySet(xColumnProperties);
+
+ if ( nSheet != pStyle->GetLastSheet() )
+ {
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(rXMLImport.GetModel())->GetSheetSaveData();
+ pSheetData->AddColumnStyle( sStyleName, ScAddress( static_cast<SCCOL>(nCurrentColumn), 0, nSheet ) );
+ pStyle->SetLastSheet(nSheet);
+ }
+ }
+ }
+ }
+ bool bValue(true);
+ if (!IsXMLToken(sVisibility, XML_VISIBLE))
+ bValue = false;
+ xColumnProperties->setPropertyValue(SC_UNONAME_CELLVIS, uno::Any(bValue));
+ }
+ }
+ }
+
+ // #i57915# ScXMLImport::SetStyleToRange can't handle empty style names.
+ // The default for a column if there is no attribute is the style "Default" (programmatic API name).
+ if ( sCellStyleName.isEmpty() )
+ sCellStyleName = "Default";
+
+ GetScImport().GetTables().AddColStyle(nColCount, sCellStyleName);
+}
+
+ScXMLTableColsContext::ScXMLTableColsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bTempHeader, const bool bTempGroup) :
+ ScXMLImportContext( rImport ),
+ nHeaderStartCol(0),
+ nHeaderEndCol(0),
+ nGroupStartCol(0),
+ nGroupEndCol(0),
+ bHeader(bTempHeader),
+ bGroup(bTempGroup),
+ bGroupDisplay(true)
+{
+ // don't have any attributes
+ if (bHeader)
+ nHeaderStartCol = rImport.GetTables().GetCurrentColCount();
+ else if (bGroup)
+ {
+ nGroupStartCol = rImport.GetTables().GetCurrentColCount();
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_DISPLAY ) ) );
+ if ( aIter != rAttrList->end() && IsXMLToken(aIter, XML_FALSE) )
+ bGroupDisplay = false;
+ }
+ }
+}
+
+ScXMLTableColsContext::~ScXMLTableColsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLTableColsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN_GROUP ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ false, true );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_COLUMNS ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ true, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMNS ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ false, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN ):
+ pContext = new ScXMLTableColContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLTableColsContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScXMLImport& rXMLImport = GetScImport();
+ if (bHeader)
+ {
+ nHeaderEndCol = rXMLImport.GetTables().GetCurrentColCount();
+ nHeaderEndCol--;
+ if (nHeaderStartCol <= nHeaderEndCol)
+ {
+ uno::Reference <sheet::XPrintAreas> xPrintAreas (rXMLImport.GetTables().GetCurrentXSheet(), uno::UNO_QUERY);
+ if (xPrintAreas.is())
+ {
+ if (!xPrintAreas->getPrintTitleColumns())
+ {
+ xPrintAreas->setPrintTitleColumns(true);
+ table::CellRangeAddress aColumnHeaderRange;
+ aColumnHeaderRange.StartColumn = nHeaderStartCol;
+ aColumnHeaderRange.EndColumn = nHeaderEndCol;
+ xPrintAreas->setTitleColumns(aColumnHeaderRange);
+ }
+ else
+ {
+ table::CellRangeAddress aColumnHeaderRange(xPrintAreas->getTitleColumns());
+ aColumnHeaderRange.EndColumn = nHeaderEndCol;
+ xPrintAreas->setTitleColumns(aColumnHeaderRange);
+ }
+ }
+ }
+ }
+ else if (bGroup)
+ {
+ SCTAB nSheet = rXMLImport.GetTables().GetCurrentSheet();
+ nGroupEndCol = rXMLImport.GetTables().GetCurrentColCount();
+ nGroupEndCol--;
+ if (nGroupStartCol <= nGroupEndCol)
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (pDoc)
+ {
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ ScOutlineTable* pOutlineTable = pDoc->GetOutlineTable(nSheet, true);
+ if (pOutlineTable)
+ {
+ ScOutlineArray& rColArray = pOutlineTable->GetColArray();
+ bool bResized;
+ rColArray.Insert(static_cast<SCCOL>(nGroupStartCol), static_cast<SCCOL>(nGroupEndCol), bResized, !bGroupDisplay);
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcoli.hxx b/sc/source/filter/xml/xmlcoli.hxx
new file mode 100644
index 000000000..204d9cd4b
--- /dev/null
+++ b/sc/source/filter/xml/xmlcoli.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLTableColContext : public ScXMLImportContext
+{
+ sal_Int32 nColCount;
+ OUString sStyleName;
+ OUString sVisibility;
+ OUString sCellStyleName;
+
+public:
+
+ ScXMLTableColContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLTableColContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLTableColsContext : public ScXMLImportContext
+{
+ sal_Int32 nHeaderStartCol;
+ sal_Int32 nHeaderEndCol;
+ sal_Int32 nGroupStartCol;
+ sal_Int32 nGroupEndCol;
+ bool bHeader;
+ bool bGroup;
+ bool bGroupDisplay;
+
+public:
+
+ ScXMLTableColsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bHeader, const bool bGroup);
+
+ virtual ~ScXMLTableColsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcondformat.cxx b/sc/source/filter/xml/xmlcondformat.cxx
new file mode 100644
index 000000000..77280be47
--- /dev/null
+++ b/sc/source/filter/xml/xmlcondformat.cxx
@@ -0,0 +1,1009 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include "xmlcondformat.hxx"
+#include "xmlimprt.hxx"
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <sal/log.hxx>
+
+#include <compiler.hxx>
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <document.hxx>
+#include <sax/tools/converter.hxx>
+#include <rangelst.hxx>
+#include <rangeutl.hxx>
+#include "XMLConverter.hxx"
+#include <stylehelper.hxx>
+#include <tokenarray.hxx>
+
+using namespace xmloff::token;
+
+ScXMLConditionalFormatsContext::ScXMLConditionalFormatsContext( ScXMLImport& rImport ):
+ ScXMLImportContext( rImport )
+{
+ GetScImport().SetNewCondFormatData();
+ GetScImport().GetDocument()->SetCondFormList(new ScConditionalFormatList(), GetScImport().GetTables().GetCurrentSheet());
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLConditionalFormatsContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_CONDITIONAL_FORMAT ):
+ pContext = new ScXMLConditionalFormatContext( GetScImport(), pAttribList, *this );
+ break;
+ }
+
+ return pContext;
+}
+
+IMPL_LINK(ScXMLConditionalFormatsContext, FormatDeletedHdl, ScConditionalFormat*, pFormat, void)
+{
+ mvCondFormatData.erase(std::remove_if(mvCondFormatData.begin(), mvCondFormatData.end(),
+ [pFormat](CondFormatData& r){ return r.mpFormat == pFormat; }),
+ mvCondFormatData.end());
+}
+
+void SAL_CALL ScXMLConditionalFormatsContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+
+ SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
+ ScConditionalFormatList* pCondFormatList = pDoc->GetCondFormList(nTab);
+ bool bDeleted = !pCondFormatList->CheckAllEntries(LINK(this, ScXMLConditionalFormatsContext, FormatDeletedHdl));
+
+ SAL_WARN_IF(bDeleted, "sc", "conditional formats have been deleted because they contained empty range info");
+
+ for (const auto& i : mvCondFormatData)
+ {
+ pDoc->AddCondFormatData( i.mpFormat->GetRange(), i.mnTab, i.mpFormat->GetKey() );
+ }
+}
+
+ScXMLConditionalFormatContext::ScXMLConditionalFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLConditionalFormatsContext& rParent ):
+ ScXMLImportContext( rImport ),
+ mrParent( rParent )
+{
+ OUString sRange;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_TARGET_RANGE_ADDRESS ):
+ sRange = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ ScRangeList aRangeList;
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ ScRangeStringConverter::GetRangeListFromString(aRangeList, sRange, *pDoc,
+ formula::FormulaGrammar::CONV_ODF);
+
+ mxFormat.reset(new ScConditionalFormat(0, pDoc));
+ mxFormat->SetRange(aRangeList);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLConditionalFormatContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_CONDITION ):
+ pContext = new ScXMLCondContext( GetScImport(), pAttribList, mxFormat.get() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_COLOR_SCALE ):
+ pContext = new ScXMLColorScaleFormatContext( GetScImport(), mxFormat.get() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_BAR ):
+ pContext = new ScXMLDataBarFormatContext( GetScImport(), pAttribList, mxFormat.get() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_ICON_SET ):
+ pContext = new ScXMLIconSetFormatContext( GetScImport(), pAttribList, mxFormat.get() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATE_IS ):
+ pContext = new ScXMLDateContext( GetScImport(), pAttribList, mxFormat.get() );
+ break;
+ default:
+ break;
+ }
+
+ return pContext;
+}
+
+static bool HasRelRefIgnoringSheet0Relative( ScDocument* pDoc, const ScTokenArray* pTokens, sal_uInt16 nRecursion = 0 )
+{
+ if (pTokens)
+ {
+ formula::FormulaTokenArrayPlainIterator aIter( *pTokens );
+ formula::FormulaToken* t;
+ for( t = aIter.Next(); t; t = aIter.Next() )
+ {
+ switch( t->GetType() )
+ {
+ case formula::svDoubleRef:
+ {
+ ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+ if ( rRef2.IsColRel() || rRef2.IsRowRel() || (rRef2.IsFlag3D() && rRef2.IsTabRel()) )
+ return true;
+ [[fallthrough]];
+ }
+
+ case formula::svSingleRef:
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if ( rRef1.IsColRel() || rRef1.IsRowRel() || (rRef1.IsFlag3D() && rRef1.IsTabRel()) )
+ return true;
+ }
+ break;
+
+ case formula::svIndex:
+ {
+ if( t->GetOpCode() == ocName ) // DB areas always absolute
+ if( ScRangeData* pRangeData = pDoc->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex()) )
+ if( (nRecursion < 42) && HasRelRefIgnoringSheet0Relative( pDoc, pRangeData->GetCode(), nRecursion + 1 ) )
+ return true;
+ }
+ break;
+
+ // #i34474# function result dependent on cell position
+ case formula::svByte:
+ {
+ switch( t->GetOpCode() )
+ {
+ case ocRow: // ROW() returns own row index
+ case ocColumn: // COLUMN() returns own column index
+ case ocSheet: // SHEET() returns own sheet index
+ case ocCell: // CELL() may return own cell address
+ return true;
+ default:
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+static bool HasOneSingleFullyRelativeReference( const ScTokenArray* pTokens, ScSingleRefData& rOffset )
+{
+ int nCount = 0;
+ if (pTokens)
+ {
+ formula::FormulaTokenArrayPlainIterator aIter( *pTokens );
+ formula::FormulaToken* t;
+ for( t = aIter.Next(); t; t = aIter.Next() )
+ {
+ switch( t->GetType() )
+ {
+ case formula::svSingleRef:
+ {
+ ScSingleRefData& rRef1 = *t->GetSingleRef();
+ if ( rRef1.IsColRel() && rRef1.IsRowRel() && !rRef1.IsFlag3D() && rRef1.IsTabRel() )
+ {
+ nCount++;
+ if (nCount == 1)
+ {
+ rOffset = rRef1;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ return nCount == 1;
+}
+
+void SAL_CALL ScXMLConditionalFormatContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+
+ SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
+ std::unique_ptr<ScConditionalFormat> pFormat(std::move(mxFormat));
+
+ bool bEligibleForCache = true;
+ bool bSingleRelativeReference = false;
+ ScSingleRefData aOffsetForSingleRelRef;
+ std::unique_ptr<ScTokenArray> pTokens;
+ for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx)
+ {
+ auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx);
+ auto pCondFormatEntry = dynamic_cast<const ScCondFormatEntry*>(pFormatEntry);
+
+ if (pCondFormatEntry == nullptr ||
+ (pCondFormatEntry->GetOperation() != ScConditionMode::Equal &&
+ pCondFormatEntry->GetOperation() != ScConditionMode::Direct))
+ {
+ bEligibleForCache = false;
+ break;
+ }
+
+ ScAddress aSrcPos;
+ OUString aSrcString = pCondFormatEntry->GetSrcString();
+ if ( !aSrcString.isEmpty() )
+ aSrcPos.Parse( aSrcString, *pDoc );
+ ScCompiler aComp( *pDoc, aSrcPos );
+ aComp.SetGrammar( formula::FormulaGrammar::GRAM_ODFF );
+ pTokens = aComp.CompileString( pCondFormatEntry->GetExpression(aSrcPos, 0), "" );
+ if (HasRelRefIgnoringSheet0Relative( pDoc, pTokens.get() ))
+ {
+ // In general not eligible, but some might be. We handle one very special case: When the
+ // conditional format has one entry, the reference position is the first cell of the
+ // range, and with a single fully relative reference in its expression. (Possibly these
+ // conditions could be loosened, but I am too tired to think on that right now.)
+ if (pFormat->size() == 1 &&
+ pFormat->GetRange().size() == 1 &&
+ pFormat->GetRange()[0].aStart == aSrcPos &&
+ HasOneSingleFullyRelativeReference( pTokens.get(), aOffsetForSingleRelRef ))
+ {
+ bSingleRelativeReference = true;
+ }
+ else
+ {
+ bEligibleForCache = false;
+ break;
+ }
+ }
+ }
+
+ if (bEligibleForCache)
+ {
+ for (auto& aCacheEntry : mrParent.maCache)
+ if (aCacheEntry.mnAge < SAL_MAX_INT64)
+ aCacheEntry.mnAge++;
+
+ for (auto& aCacheEntry : mrParent.maCache)
+ {
+ if (!aCacheEntry.mpFormat)
+ continue;
+
+ if (aCacheEntry.mpFormat->size() != pFormat->size())
+ continue;
+
+ // Check if the conditional format is identical to an existing one (but with different range) and can be shared
+ for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx)
+ {
+ auto pCacheFormatEntry = aCacheEntry.mpFormat->GetEntry(nFormatEntryIx);
+ auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx);
+ if (pCacheFormatEntry->GetType() != pFormatEntry->GetType() ||
+ pFormatEntry->GetType() != ScFormatEntry::Type::Condition)
+ break;
+
+ auto pCacheCondFormatEntry = static_cast<const ScCondFormatEntry*>(pCacheFormatEntry);
+ auto pCondFormatEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+
+ if (pCacheCondFormatEntry->GetStyle() != pCondFormatEntry->GetStyle())
+ break;
+
+ // Note That comparing the formulas of the ScConditionEntry at this stage is
+ // comparing just the *strings* of the formulas. For the bSingleRelativeReference
+ // case we compare the tokenized ("compiled") formulas.
+ if (bSingleRelativeReference)
+ {
+ if (aCacheEntry.mbSingleRelativeReference &&
+ pTokens->EqualTokens(aCacheEntry.mpTokens.get()))
+ ;
+ else
+ break;
+ }
+ else if (!pCacheCondFormatEntry->IsEqual(*pCondFormatEntry, /*bIgnoreSrcPos*/true))
+ {
+ break;
+ }
+ // If we get here on the last round through the for loop, we have a cache hit
+ if (nFormatEntryIx == pFormat->size() - 1)
+ {
+ // Mark cache entry as fresh, do necessary mangling of it and just return
+ aCacheEntry.mnAge = 0;
+ for (size_t k = 0; k < pFormat->GetRange().size(); ++k)
+ aCacheEntry.mpFormat->GetRangeList().Join(pFormat->GetRange()[k]);
+ return;
+ }
+ }
+ }
+ }
+
+ sal_uLong nIndex = pDoc->AddCondFormat(std::move(pFormat), nTab);
+ ScConditionalFormat* pInsertedFormat = pDoc->GetCondFormList(nTab)->GetFormat(nIndex);
+ assert(pInsertedFormat && pInsertedFormat->GetKey() == nIndex);
+
+ mrParent.mvCondFormatData.push_back( { pInsertedFormat, nTab } );
+
+ if (!bEligibleForCache)
+ return;
+
+ // Not found in cache, replace oldest cache entry
+ sal_Int64 nOldestAge = -1;
+ size_t nIndexOfOldest = 0;
+ for (auto& aCacheEntry : mrParent.maCache)
+ {
+ if (aCacheEntry.mnAge > nOldestAge)
+ {
+ nOldestAge = aCacheEntry.mnAge;
+ nIndexOfOldest = (&aCacheEntry - &mrParent.maCache.front());
+ }
+ }
+ mrParent.maCache[nIndexOfOldest].mpFormat = pInsertedFormat;
+ mrParent.maCache[nIndexOfOldest].mbSingleRelativeReference = bSingleRelativeReference;
+ mrParent.maCache[nIndexOfOldest].mpTokens = std::move(pTokens);
+ mrParent.maCache[nIndexOfOldest].mnAge = 0;
+}
+
+ScXMLConditionalFormatContext::~ScXMLConditionalFormatContext()
+{
+}
+
+ScXMLColorScaleFormatContext::ScXMLColorScaleFormatContext( ScXMLImport& rImport,
+ ScConditionalFormat* pFormat):
+ ScXMLImportContext( rImport ),
+ pColorScaleFormat(nullptr)
+{
+ pColorScaleFormat = new ScColorScaleFormat(GetScImport().GetDocument());
+ pFormat->AddEntry(pColorScaleFormat);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLColorScaleFormatContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_COLOR_SCALE_ENTRY ):
+ pContext = new ScXMLColorScaleFormatEntryContext( GetScImport(), pAttribList, pColorScaleFormat );
+ break;
+ default:
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataBarFormatContext::ScXMLDataBarFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat):
+ ScXMLImportContext( rImport ),
+ mpFormatData(nullptr),
+ mpParent(pFormat),
+ mnIndex(0)
+{
+ OUString sPositiveColor;
+ OUString sNegativeColor;
+ OUString sGradient;
+ OUString sAxisPosition;
+ OUString sShowValue;
+ OUString sAxisColor;
+ OUString sMinLength;
+ OUString sMaxLength;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_POSITIVE_COLOR ):
+ sPositiveColor = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_GRADIENT ):
+ sGradient = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_NEGATIVE_COLOR ):
+ sNegativeColor = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_AXIS_POSITION ):
+ sAxisPosition = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_SHOW_VALUE ):
+ sShowValue = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_AXIS_COLOR ):
+ sAxisColor = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_MIN_LENGTH ):
+ sMinLength = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_MAX_LENGTH ):
+ sMaxLength = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ ScDataBarFormat* pDataBarFormat = new ScDataBarFormat(rImport.GetDocument());
+ mpFormatData = new ScDataBarFormatData();
+ pDataBarFormat->SetDataBarData(mpFormatData);
+ if(!sGradient.isEmpty())
+ {
+ bool bGradient = true;
+ (void)sax::Converter::convertBool( bGradient, sGradient);
+ mpFormatData->mbGradient = bGradient;
+ }
+
+ if(!sPositiveColor.isEmpty())
+ {
+ sax::Converter::convertColor( mpFormatData->maPositiveColor, sPositiveColor );
+ }
+
+ if(!sNegativeColor.isEmpty())
+ {
+ // we might check here for 0xff0000 and don't write it
+ Color nColor;
+ sax::Converter::convertColor( nColor, sNegativeColor );
+ mpFormatData->mxNegativeColor = nColor;
+ }
+ else
+ mpFormatData->mbNeg = false;
+
+ if(!sAxisPosition.isEmpty())
+ {
+ if(sAxisPosition == "middle")
+ mpFormatData->meAxisPosition = databar::MIDDLE;
+ else if (sAxisPosition == "none")
+ mpFormatData->meAxisPosition = databar::NONE;
+ }
+
+ if(!sAxisColor.isEmpty())
+ {
+ sax::Converter::convertColor( mpFormatData->maAxisColor, sAxisColor );
+ }
+
+ if(!sShowValue.isEmpty())
+ {
+ bool bShowValue = true;
+ (void)sax::Converter::convertBool( bShowValue, sShowValue );
+ mpFormatData->mbOnlyBar = !bShowValue;
+ }
+
+ if (!sMinLength.isEmpty())
+ {
+ double nVal = sMinLength.toDouble();
+ mpFormatData->mnMinLength = nVal;
+ }
+
+ if (!sMaxLength.isEmpty())
+ {
+ double nVal = sMaxLength.toDouble();
+ if (nVal == 0.0)
+ nVal = 100.0;
+ mpFormatData->mnMaxLength = nVal;
+ }
+
+ pFormat->AddEntry(pDataBarFormat);
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLDataBarFormatContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_FORMATTING_ENTRY ):
+ case XML_ELEMENT( CALC_EXT, XML_DATA_BAR_ENTRY ):
+ {
+ ScColorScaleEntry* pEntry(nullptr);
+ pContext = new ScXMLFormattingEntryContext( GetScImport(), pAttribList, pEntry );
+ pEntry->SetRepaintCallback(mpParent);
+ if(mnIndex == 0)
+ {
+ mpFormatData->mpLowerLimit.reset(pEntry);
+ }
+ else if (mnIndex == 1)
+ {
+ mpFormatData->mpUpperLimit.reset(pEntry);
+ }
+ else
+ {
+ // data bars only support 2 entries
+ assert(false);
+ }
+ ++mnIndex;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLIconSetFormatContext::ScXMLIconSetFormatContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat):
+ ScXMLImportContext( rImport ),
+ mpParent(pFormat)
+{
+ OUString aIconSetType, sShowValue;
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_ICON_SET_TYPE ):
+ aIconSetType = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_SHOW_VALUE ):
+ sShowValue = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ const ScIconSetMap* pMap = ScIconSetFormat::g_IconSetMap;
+ ScIconSetType eType = IconSet_3Arrows;
+ for(; pMap->pName; ++pMap)
+ {
+ OUString aName = OUString::createFromAscii(pMap->pName);
+ if(aName ==aIconSetType)
+ {
+ eType = pMap->eType;
+ break;
+ }
+ }
+
+ ScIconSetFormat* pIconSetFormat = new ScIconSetFormat(GetScImport().GetDocument());
+ ScIconSetFormatData* pIconSetFormatData = new ScIconSetFormatData;
+
+ if(!sShowValue.isEmpty())
+ {
+ bool bShowValue = true;
+ (void)sax::Converter::convertBool( bShowValue, sShowValue );
+ pIconSetFormatData->mbShowValue = !bShowValue;
+ }
+
+ pIconSetFormatData->eIconSetType = eType;
+ pIconSetFormat->SetIconSetData(pIconSetFormatData);
+ pFormat->AddEntry(pIconSetFormat);
+
+ mpFormatData = pIconSetFormatData;
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL ScXMLIconSetFormatContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( CALC_EXT, XML_FORMATTING_ENTRY ):
+ {
+ ScColorScaleEntry* pEntry(nullptr);
+ pContext = new ScXMLFormattingEntryContext( GetScImport(), pAttribList, pEntry );
+ mpFormatData->m_Entries.emplace_back(pEntry);
+ pEntry->SetRepaintCallback(mpParent);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return pContext;
+}
+
+namespace {
+
+void GetConditionData(const OUString& rValue, ScConditionMode& eMode, OUString& rExpr1, OUString& rExpr2)
+{
+ if(rValue.startsWith("unique"))
+ {
+ eMode = ScConditionMode::NotDuplicate;
+ }
+ else if(rValue.startsWith("duplicate"))
+ {
+ eMode = ScConditionMode::Duplicate;
+ }
+ else if(rValue.startsWith("between"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 8;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ',');
+ rExpr2 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::Between;
+ }
+ else if(rValue.startsWith("not-between"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 12;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ',');
+ rExpr2 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::NotBetween;
+ }
+ else if(rValue.startsWith("<="))
+ {
+ rExpr1 = rValue.copy(2);
+ eMode = ScConditionMode::EqLess;
+ }
+ else if(rValue.startsWith(">="))
+ {
+ rExpr1 = rValue.copy(2);
+ eMode = ScConditionMode::EqGreater;
+ }
+ else if(rValue.startsWith("!="))
+ {
+ rExpr1 = rValue.copy(2);
+ eMode = ScConditionMode::NotEqual;
+ }
+ else if(rValue.startsWith("<"))
+ {
+ rExpr1 = rValue.copy(1);
+ eMode = ScConditionMode::Less;
+ }
+ else if(rValue.startsWith("="))
+ {
+ rExpr1 = rValue.copy(1);
+ eMode = ScConditionMode::Equal;
+ }
+ else if(rValue.startsWith(">"))
+ {
+ rExpr1 = rValue.copy(1);
+ eMode = ScConditionMode::Greater;
+ }
+ else if(rValue.startsWith("formula-is"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 11;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::Direct;
+ }
+ else if(rValue.startsWith("top-elements"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 13;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::Top10;
+ }
+ else if(rValue.startsWith("bottom-elements"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 16;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::Bottom10;
+ }
+ else if(rValue.startsWith("top-percent"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 12;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::TopPercent;
+ }
+ else if(rValue.startsWith("bottom-percent"))
+ {
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 15;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ eMode = ScConditionMode::BottomPercent;
+ }
+ else if(rValue.startsWith("above-average"))
+ {
+ eMode = ScConditionMode::AboveAverage;
+ }
+ else if(rValue.startsWith("below-average"))
+ {
+ eMode = ScConditionMode::BelowAverage;
+ }
+ else if(rValue.startsWith("above-equal-average"))
+ {
+ eMode = ScConditionMode::AboveEqualAverage;
+ }
+ else if(rValue.startsWith("below-equal-average"))
+ {
+ eMode = ScConditionMode::BelowEqualAverage;
+ }
+ else if(rValue.startsWith("is-error"))
+ {
+ eMode = ScConditionMode::Error;
+ }
+ else if(rValue.startsWith("is-no-error"))
+ {
+ eMode = ScConditionMode::NoError;
+ }
+ else if(rValue.startsWith("begins-with"))
+ {
+ eMode = ScConditionMode::BeginsWith;
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 12;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ }
+ else if(rValue.startsWith("ends-with"))
+ {
+ eMode = ScConditionMode::EndsWith;
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 10;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ }
+ else if(rValue.startsWith("contains-text"))
+ {
+ eMode = ScConditionMode::ContainsText;
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 14;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ }
+ else if(rValue.startsWith("not-contains-text"))
+ {
+ eMode = ScConditionMode::NotContainsText;
+ const sal_Unicode* pStr = rValue.getStr();
+ const sal_Unicode* pStart = pStr + 18;
+ const sal_Unicode* pEnd = pStr + rValue.getLength();
+ rExpr1 = ScXMLConditionHelper::getExpression( pStart, pEnd, ')');
+ }
+ else
+ eMode = ScConditionMode::NONE;
+}
+
+}
+
+ScXMLCondContext::ScXMLCondContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat ):
+ ScXMLImportContext( rImport )
+{
+ OUString sExpression;
+ OUString sStyle;
+ OUString sAddress;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_VALUE ):
+ sExpression = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_APPLY_STYLE_NAME ):
+ sStyle = ScStyleNameConversion::ProgrammaticToDisplayName(aIter.toString(), SfxStyleFamily::Para );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_BASE_CELL_ADDRESS ):
+ sAddress = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ OUString aExpr1;
+ OUString aExpr2;
+ ScConditionMode eMode;
+ GetConditionData(sExpression, eMode, aExpr1, aExpr2);
+
+ ScCondFormatEntry* pFormatEntry = new ScCondFormatEntry(eMode, aExpr1, aExpr2, *GetScImport().GetDocument(), ScAddress(), sStyle,
+ OUString(), OUString(), formula::FormulaGrammar::GRAM_ODFF, formula::FormulaGrammar::GRAM_ODFF);
+ pFormatEntry->SetSrcString(sAddress);
+
+ pFormat->AddEntry(pFormatEntry);
+}
+
+namespace {
+
+void setColorEntryType(std::u16string_view rType, ScColorScaleEntry* pEntry, const OUString& rFormula,
+ ScXMLImport& rImport)
+{
+ if(rType == u"minimum")
+ pEntry->SetType(COLORSCALE_MIN);
+ else if(rType == u"maximum")
+ pEntry->SetType(COLORSCALE_MAX);
+ else if(rType == u"percentile")
+ pEntry->SetType(COLORSCALE_PERCENTILE);
+ else if(rType == u"percent")
+ pEntry->SetType(COLORSCALE_PERCENT);
+ else if(rType == u"formula")
+ {
+ pEntry->SetType(COLORSCALE_FORMULA);
+ //position does not matter, only table is important
+ pEntry->SetFormula(rFormula, *rImport.GetDocument(), ScAddress(0,0,rImport.GetTables().GetCurrentSheet()), formula::FormulaGrammar::GRAM_ODFF);
+ }
+ else if(rType == u"auto-minimum")
+ pEntry->SetType(COLORSCALE_AUTO);
+ else if(rType == u"auto-maximum")
+ pEntry->SetType(COLORSCALE_AUTO);
+}
+
+}
+
+ScXMLColorScaleFormatEntryContext::ScXMLColorScaleFormatEntryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScColorScaleFormat* pFormat):
+ ScXMLImportContext( rImport )
+{
+ double nVal = 0;
+ Color aColor;
+
+ OUString sType;
+ OUString sVal;
+ OUString sColor;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_TYPE ):
+ sType = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_VALUE ):
+ sVal = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_COLOR ):
+ sColor = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ sax::Converter::convertColor(aColor, sColor);
+
+ if(!sVal.isEmpty())
+ sax::Converter::convertDouble(nVal, sVal);
+
+ auto pFormatEntry = new ScColorScaleEntry(nVal, aColor);
+ setColorEntryType(sType, pFormatEntry, sVal, GetScImport());
+ pFormat->AddEntry(pFormatEntry);
+}
+
+ScXMLFormattingEntryContext::ScXMLFormattingEntryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScColorScaleEntry*& pColorScaleEntry):
+ ScXMLImportContext( rImport )
+{
+ OUString sVal;
+ OUString sType;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_TYPE ):
+ sType = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_VALUE ):
+ sVal = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ double nVal = 0;
+ if(!sVal.isEmpty())
+ sax::Converter::convertDouble(nVal, sVal);
+
+ pColorScaleEntry = new ScColorScaleEntry(nVal, Color());
+ setColorEntryType(sType, pColorScaleEntry, sVal, GetScImport());
+}
+
+namespace {
+
+condformat::ScCondFormatDateType getDateFromString(const OUString& rString)
+{
+ if(rString == "today")
+ return condformat::TODAY;
+ else if(rString == "yesterday")
+ return condformat::YESTERDAY;
+ else if(rString == "tomorrow")
+ return condformat::TOMORROW;
+ else if(rString == "last-7-days")
+ return condformat::LAST7DAYS;
+ else if(rString == "this-week")
+ return condformat::THISWEEK;
+ else if(rString == "last-week")
+ return condformat::LASTWEEK;
+ else if(rString == "next-week")
+ return condformat::NEXTWEEK;
+ else if(rString == "this-month")
+ return condformat::THISMONTH;
+ else if(rString == "last-month")
+ return condformat::LASTMONTH;
+ else if(rString == "next-month")
+ return condformat::NEXTMONTH;
+ else if(rString == "this-year")
+ return condformat::THISYEAR;
+ else if(rString == "last-year")
+ return condformat::LASTYEAR;
+ else if(rString == "next-year")
+ return condformat::NEXTYEAR;
+
+ SAL_WARN("sc", "unknown date type: " << rString);
+ return condformat::TODAY;
+}
+
+}
+
+ScXMLDateContext::ScXMLDateContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat ):
+ ScXMLImportContext( rImport )
+{
+ OUString sDateType, sStyle;
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( CALC_EXT, XML_DATE ):
+ sDateType = aIter.toString();
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_STYLE ):
+ sStyle = ScStyleNameConversion::ProgrammaticToDisplayName(aIter.toString(), SfxStyleFamily::Para );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ ScCondDateFormatEntry* pFormatEntry = new ScCondDateFormatEntry(GetScImport().GetDocument());
+ pFormatEntry->SetStyleName(sStyle);
+ pFormatEntry->SetDateType(getDateFromString(sDateType));
+ pFormat->AddEntry(pFormatEntry);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcondformat.hxx b/sc/source/filter/xml/xmlcondformat.hxx
new file mode 100644
index 000000000..eaaf7440a
--- /dev/null
+++ b/sc/source/filter/xml/xmlcondformat.hxx
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <tools/link.hxx>
+#include "importcontext.hxx"
+#include <tokenarray.hxx>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScColorScaleFormat;
+class ScColorScaleEntry;
+struct ScDataBarFormatData;
+class ScConditionalFormat;
+struct ScIconSetFormatData;
+
+class ScXMLConditionalFormatsContext : public ScXMLImportContext
+{
+private:
+ struct CacheEntry
+ {
+ ScConditionalFormat* mpFormat = nullptr;
+ bool mbSingleRelativeReference;
+ std::unique_ptr<const ScTokenArray> mpTokens;
+ sal_Int64 mnAge = SAL_MAX_INT64;
+ };
+
+ struct CondFormatData
+ {
+ ScConditionalFormat* mpFormat;
+ SCTAB mnTab;
+ };
+
+ DECL_LINK(FormatDeletedHdl, ScConditionalFormat*, void);
+
+public:
+ ScXMLConditionalFormatsContext( ScXMLImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ std::array<CacheEntry, 4> maCache;
+
+ std::vector<CondFormatData> mvCondFormatData;
+};
+
+class ScXMLConditionalFormatContext : public ScXMLImportContext
+{
+public:
+ ScXMLConditionalFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLConditionalFormatsContext& rParent );
+
+ virtual ~ScXMLConditionalFormatContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+private:
+
+ std::unique_ptr<ScConditionalFormat> mxFormat;
+
+ ScXMLConditionalFormatsContext& mrParent;
+};
+
+class ScXMLColorScaleFormatContext : public ScXMLImportContext
+{
+public:
+ ScXMLColorScaleFormatContext( ScXMLImport& rImport,
+ ScConditionalFormat* pFormat);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+private:
+
+ ScColorScaleFormat* pColorScaleFormat;
+};
+
+class ScXMLDataBarFormatContext : public ScXMLImportContext
+{
+public:
+ ScXMLDataBarFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+private:
+
+ ScDataBarFormatData* mpFormatData;
+ ScConditionalFormat* mpParent;
+
+ sal_Int32 mnIndex;
+};
+
+class ScXMLIconSetFormatContext : public ScXMLImportContext
+{
+ ScIconSetFormatData* mpFormatData;
+ ScConditionalFormat* mpParent;
+public:
+
+ ScXMLIconSetFormatContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLColorScaleFormatEntryContext : public ScXMLImportContext
+{
+public:
+ ScXMLColorScaleFormatEntryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScColorScaleFormat* pFormat);
+};
+
+class ScXMLFormattingEntryContext : public ScXMLImportContext
+{
+public:
+ ScXMLFormattingEntryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScColorScaleEntry*& pData);
+};
+
+class ScXMLCondContext : public ScXMLImportContext
+{
+public:
+ ScXMLCondContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat);
+};
+
+class ScXMLDateContext : public ScXMLImportContext
+{
+public:
+ ScXMLDateContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScConditionalFormat* pFormat);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlconti.cxx b/sc/source/filter/xml/xmlconti.cxx
new file mode 100644
index 000000000..18adc82ff
--- /dev/null
+++ b/sc/source/filter/xml/xmlconti.cxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlconti.hxx"
+#include "xmlimprt.hxx"
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace xmloff::token;
+
+ScXMLContentContext::ScXMLContentContext( ScXMLImport& rImport,
+ OUStringBuffer& sTempValue) :
+ ScXMLImportContext( rImport ),
+ sValue(sTempValue)
+{
+}
+
+ScXMLContentContext::~ScXMLContentContext()
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLContentContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ if (nElement == XML_ELEMENT(TEXT, XML_S))
+ {
+ sal_Int32 nRepeat(0);
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ if (aIter.getToken() == XML_ELEMENT(TEXT, XML_C))
+ nRepeat = aIter.toInt32();
+ }
+ if (nRepeat)
+ for (sal_Int32 j = 0; j < nRepeat; ++j)
+ sValue.append(' ');
+ else
+ sValue.append(' ');
+ }
+
+ return new SvXMLImportContext( GetImport() );
+}
+
+void ScXMLContentContext::characters( const OUString& rChars )
+{
+ sValue.append(rChars);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlconti.hxx b/sc/source/filter/xml/xmlconti.hxx
new file mode 100644
index 000000000..3324bf201
--- /dev/null
+++ b/sc/source/filter/xml/xmlconti.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <rtl/ustrbuf.hxx>
+#include "importcontext.hxx"
+
+class ScXMLContentContext : public ScXMLImportContext
+{
+ OUStringBuffer& sValue;
+
+public:
+
+ ScXMLContentContext( ScXMLImport& rImport,
+ OUStringBuffer& sValue);
+
+ virtual ~ScXMLContentContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL characters( const OUString& rChars ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcvali.cxx b/sc/source/filter/xml/xmlcvali.cxx
new file mode 100644
index 000000000..6a5669b78
--- /dev/null
+++ b/sc/source/filter/xml/xmlcvali.cxx
@@ -0,0 +1,557 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlcvali.hxx"
+#include "xmlimprt.hxx"
+#include "xmlconti.hxx"
+#include <document.hxx>
+#include "XMLConverter.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/XMLEventsImportContext.hxx>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+using namespace ::formula;
+
+namespace {
+
+class ScXMLContentValidationContext : public ScXMLImportContext
+{
+ OUString sName;
+ OUString sHelpTitle;
+ OUString sHelpMessage;
+ OUString sErrorTitle;
+ OUString sErrorMessage;
+ OUString sErrorMessageType;
+ OUString sBaseCellAddress;
+ OUString sCondition;
+ sal_Int16 nShowList;
+ bool bAllowEmptyCell;
+ bool bDisplayHelp;
+ bool bDisplayError;
+
+ SvXMLImportContextRef xEventContext;
+
+ css::sheet::ValidationAlertStyle GetAlertStyle() const;
+ void SetFormula( OUString& rFormula, OUString& rFormulaNmsp, FormulaGrammar::Grammar& reGrammar,
+ const OUString& rCondition, const OUString& rGlobNmsp, FormulaGrammar::Grammar eGlobGrammar, bool bHasNmsp ) const;
+ void GetCondition( ScMyImportValidation& rValidation ) const;
+
+public:
+
+ ScXMLContentValidationContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetHelpMessage(const OUString& sTitle, const OUString& sMessage, const bool bDisplay);
+ void SetErrorMessage(const OUString& sTitle, const OUString& sMessage, const OUString& sMessageType, const bool bDisplay);
+ void SetErrorMacro(const bool bExecute);
+};
+
+class ScXMLHelpMessageContext : public ScXMLImportContext
+{
+ OUString sTitle;
+ OUStringBuffer sMessage;
+ sal_Int32 nParagraphCount;
+ bool bDisplay;
+
+ ScXMLContentValidationContext* pValidationContext;
+
+public:
+
+ ScXMLHelpMessageContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pValidationContext);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLErrorMessageContext : public ScXMLImportContext
+{
+ OUString sTitle;
+ OUStringBuffer sMessage;
+ OUString sMessageType;
+ sal_Int32 nParagraphCount;
+ bool bDisplay;
+
+ ScXMLContentValidationContext* pValidationContext;
+
+public:
+
+ ScXMLErrorMessageContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pValidationContext);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLErrorMacroContext : public ScXMLImportContext
+{
+ bool bExecute;
+ ScXMLContentValidationContext* pValidationContext;
+
+public:
+
+ ScXMLErrorMacroContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pValidationContext);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+}
+
+ScXMLContentValidationsContext::ScXMLContentValidationsContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // here are no attributes
+}
+
+ScXMLContentValidationsContext::~ScXMLContentValidationsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLContentValidationsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_CONTENT_VALIDATION ):
+ pContext = new ScXMLContentValidationContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLContentValidationContext::ScXMLContentValidationContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ nShowList(sheet::TableValidationVisibility::UNSORTED),
+ bAllowEmptyCell(true),
+ bDisplayHelp(false),
+ bDisplayError(false)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ sName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION ):
+ sCondition = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_BASE_CELL_ADDRESS ):
+ sBaseCellAddress = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_ALLOW_EMPTY_CELL ):
+ if (IsXMLToken(aIter, XML_FALSE))
+ bAllowEmptyCell = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_LIST ):
+ if (IsXMLToken(aIter, XML_NO))
+ {
+ nShowList = sheet::TableValidationVisibility::INVISIBLE;
+ }
+ else if (IsXMLToken(aIter, XML_UNSORTED))
+ {
+ nShowList = sheet::TableValidationVisibility::UNSORTED;
+ }
+ else if (IsXMLToken(aIter, XML_SORT_ASCENDING))
+ {
+ nShowList = sheet::TableValidationVisibility::SORTEDASCENDING;
+ }
+ else if (IsXMLToken(aIter, XML_SORTED_ASCENDING))
+ {
+ // Read old wrong value, fdo#72548
+ nShowList = sheet::TableValidationVisibility::SORTEDASCENDING;
+ }
+ break;
+ }
+ }
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLContentValidationContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_HELP_MESSAGE ):
+ pContext = new ScXMLHelpMessageContext( GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_ERROR_MESSAGE ):
+ pContext = new ScXMLErrorMessageContext( GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_ERROR_MACRO ):
+ pContext = new ScXMLErrorMacroContext( GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS):
+ pContext = new XMLEventsImportContext( GetImport() );
+ xEventContext = pContext;
+ }
+
+ return pContext;
+}
+
+sheet::ValidationAlertStyle ScXMLContentValidationContext::GetAlertStyle() const
+{
+ if (IsXMLToken(sErrorMessageType, XML_MACRO))
+ return sheet::ValidationAlertStyle_MACRO;
+ if (IsXMLToken(sErrorMessageType, XML_STOP))
+ return sheet::ValidationAlertStyle_STOP;
+ if (IsXMLToken(sErrorMessageType, XML_WARNING))
+ return sheet::ValidationAlertStyle_WARNING;
+ if (IsXMLToken(sErrorMessageType, XML_INFORMATION))
+ return sheet::ValidationAlertStyle_INFO;
+ // default for unknown
+ return sheet::ValidationAlertStyle_STOP;
+}
+
+void ScXMLContentValidationContext::SetFormula( OUString& rFormula, OUString& rFormulaNmsp, FormulaGrammar::Grammar& reGrammar,
+ const OUString& rCondition, const OUString& rGlobNmsp, FormulaGrammar::Grammar eGlobGrammar, bool bHasNmsp ) const
+{
+ reGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
+ if( bHasNmsp )
+ {
+ // the entire attribute contains a namespace: internal namespace not allowed
+ rFormula = rCondition;
+ rFormulaNmsp = rGlobNmsp;
+ reGrammar = eGlobGrammar;
+ }
+ else
+ {
+ // the attribute does not contain a namespace: try to find a namespace of an external grammar
+ GetScImport().ExtractFormulaNamespaceGrammar( rFormula, rFormulaNmsp, reGrammar, rCondition, true );
+ if( reGrammar != FormulaGrammar::GRAM_EXTERNAL )
+ reGrammar = eGlobGrammar;
+ }
+}
+
+void ScXMLContentValidationContext::GetCondition( ScMyImportValidation& rValidation ) const
+{
+ rValidation.aValidationType = sheet::ValidationType_ANY; // default if no condition is given
+ rValidation.aOperator = sheet::ConditionOperator_NONE;
+
+ if( sCondition.isEmpty() )
+ return;
+
+ // extract leading namespace from condition string
+ OUString aCondition, aConditionNmsp;
+ FormulaGrammar::Grammar eGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
+ GetScImport().ExtractFormulaNamespaceGrammar( aCondition, aConditionNmsp, eGrammar, sCondition );
+ bool bHasNmsp = aCondition.getLength() < sCondition.getLength();
+
+ // parse a condition from the attribute string
+ ScXMLConditionParseResult aParseResult;
+ ScXMLConditionHelper::parseCondition( aParseResult, aCondition, 0 );
+
+ /* Check the result. A valid value in aParseResult.meToken implies
+ that the other members of aParseResult are filled with valid data
+ for that token. */
+ bool bSecondaryPart = false;
+ switch( aParseResult.meToken )
+ {
+ case XML_COND_TEXTLENGTH: // condition is 'cell-content-text-length()<operator><expression>'
+ case XML_COND_TEXTLENGTH_ISBETWEEN: // condition is 'cell-content-text-length-is-between(<expression1>,<expression2>)'
+ case XML_COND_TEXTLENGTH_ISNOTBETWEEN: // condition is 'cell-content-text-length-is-not-between(<expression1>,<expression2>)'
+ case XML_COND_ISINLIST: // condition is 'cell-content-is-in-list(<expression>)'
+ case XML_COND_ISTRUEFORMULA: // condition is 'is-true-formula(<expression>)'
+ rValidation.aValidationType = aParseResult.meValidation;
+ rValidation.aOperator = aParseResult.meOperator;
+ break;
+
+ case XML_COND_ISWHOLENUMBER: // condition is 'cell-content-is-whole-number() and <condition>'
+ case XML_COND_ISDECIMALNUMBER: // condition is 'cell-content-is-decimal-number() and <condition>'
+ case XML_COND_ISDATE: // condition is 'cell-content-is-date() and <condition>'
+ case XML_COND_ISTIME: // condition is 'cell-content-is-time() and <condition>'
+ rValidation.aValidationType = aParseResult.meValidation;
+ bSecondaryPart = true;
+ break;
+
+ default:; // unacceptable or unknown condition
+ }
+
+ /* Parse the following 'and <condition>' part of some conditions. This
+ updates the members of aParseResult that will contain the operands
+ and comparison operator then. */
+ if( bSecondaryPart )
+ {
+ ScXMLConditionHelper::parseCondition( aParseResult, aCondition, aParseResult.mnEndIndex );
+ if( aParseResult.meToken == XML_COND_AND )
+ {
+ ScXMLConditionHelper::parseCondition( aParseResult, aCondition, aParseResult.mnEndIndex );
+ switch( aParseResult.meToken )
+ {
+ case XML_COND_CELLCONTENT: // condition is 'and cell-content()<operator><expression>'
+ case XML_COND_ISBETWEEN: // condition is 'and cell-content-is-between(<expression1>,<expression2>)'
+ case XML_COND_ISNOTBETWEEN: // condition is 'and cell-content-is-not-between(<expression1>,<expression2>)'
+ rValidation.aOperator = aParseResult.meOperator;
+ break;
+ default:; // unacceptable or unknown condition
+ }
+ }
+ }
+
+ // a validation type (date, integer) without a condition isn't possible
+ if( rValidation.aOperator == sheet::ConditionOperator_NONE )
+ rValidation.aValidationType = sheet::ValidationType_ANY;
+
+ // parse the formulas
+ if( rValidation.aValidationType != sheet::ValidationType_ANY )
+ {
+ SetFormula( rValidation.sFormula1, rValidation.sFormulaNmsp1, rValidation.eGrammar1,
+ aParseResult.maOperand1, aConditionNmsp, eGrammar, bHasNmsp );
+ SetFormula( rValidation.sFormula2, rValidation.sFormulaNmsp2, rValidation.eGrammar2,
+ aParseResult.maOperand2, aConditionNmsp, eGrammar, bHasNmsp );
+ }
+}
+
+void SAL_CALL ScXMLContentValidationContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ // #i36650# event-listeners element moved up one level
+ if (xEventContext.is())
+ {
+ XMLEventsImportContext* pEvents =
+ static_cast<XMLEventsImportContext*>(xEventContext.get());
+ uno::Sequence<beans::PropertyValue> aValues;
+ pEvents->GetEventSequence( "OnError", aValues );
+
+ auto pValue = std::find_if(std::cbegin(aValues), std::cend(aValues),
+ [](const beans::PropertyValue& rValue) {
+ return rValue.Name == "MacroName" || rValue.Name == "Script"; });
+ if (pValue != std::cend(aValues))
+ pValue->Value >>= sErrorTitle;
+ }
+
+ ScMyImportValidation aValidation;
+ aValidation.eGrammar1 = aValidation.eGrammar2 = GetScImport().GetDocument()->GetStorageGrammar();
+ aValidation.sName = sName;
+ aValidation.sBaseCellAddress = sBaseCellAddress;
+ aValidation.sInputTitle = sHelpTitle;
+ aValidation.sInputMessage = sHelpMessage;
+ aValidation.sErrorTitle = sErrorTitle;
+ aValidation.sErrorMessage = sErrorMessage;
+ GetCondition( aValidation );
+ aValidation.aAlertStyle = GetAlertStyle();
+ aValidation.bShowErrorMessage = bDisplayError;
+ aValidation.bShowInputMessage = bDisplayHelp;
+ aValidation.bIgnoreBlanks = bAllowEmptyCell;
+ aValidation.nShowList = nShowList;
+ GetScImport().AddValidation(aValidation);
+}
+
+void ScXMLContentValidationContext::SetHelpMessage(const OUString& sTitle, const OUString& sMessage, const bool bDisplay)
+{
+ sHelpTitle = sTitle;
+ sHelpMessage = sMessage;
+ bDisplayHelp = bDisplay;
+}
+
+void ScXMLContentValidationContext::SetErrorMessage(const OUString& sTitle, const OUString& sMessage,
+ const OUString& sMessageType, const bool bDisplay)
+{
+ sErrorTitle = sTitle;
+ sErrorMessage = sMessage;
+ sErrorMessageType = sMessageType;
+ bDisplayError = bDisplay;
+}
+
+void ScXMLContentValidationContext::SetErrorMacro(const bool bExecute)
+{
+ sErrorMessageType = "macro";
+ bDisplayError = bExecute;
+}
+
+ScXMLHelpMessageContext::ScXMLHelpMessageContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pTempValidationContext) :
+ ScXMLImportContext( rImport ),
+ nParagraphCount(0),
+ bDisplay(false)
+{
+ pValidationContext = pTempValidationContext;
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TITLE ):
+ sTitle = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY ):
+ bDisplay = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLHelpMessageContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch( nElement )
+ {
+ case XML_ELEMENT(TEXT, XML_P):
+ {
+ if(nParagraphCount)
+ sMessage.append('\n');
+ ++nParagraphCount;
+ pContext = new ScXMLContentContext( GetScImport(), sMessage );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLHelpMessageContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pValidationContext->SetHelpMessage(sTitle, sMessage.makeStringAndClear(), bDisplay);
+}
+
+ScXMLErrorMessageContext::ScXMLErrorMessageContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pTempValidationContext) :
+ ScXMLImportContext( rImport ),
+ nParagraphCount(0),
+ bDisplay(false)
+{
+ pValidationContext = pTempValidationContext;
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TITLE ):
+ sTitle = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_MESSAGE_TYPE ):
+ sMessageType = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY ):
+ bDisplay = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLErrorMessageContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch( nElement )
+ {
+ case XML_ELEMENT(TEXT, XML_P):
+ {
+ if(nParagraphCount)
+ sMessage.append('\n');
+ ++nParagraphCount;
+ pContext = new ScXMLContentContext( GetScImport(), sMessage);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLErrorMessageContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pValidationContext->SetErrorMessage(sTitle, sMessage.makeStringAndClear(), sMessageType, bDisplay);
+}
+
+ScXMLErrorMacroContext::ScXMLErrorMacroContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLContentValidationContext* pTempValidationContext) :
+ ScXMLImportContext( rImport ),
+ bExecute(false)
+{
+ pValidationContext = pTempValidationContext;
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ break;
+ case XML_ELEMENT( TABLE, XML_EXECUTE ):
+ bExecute = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > ScXMLErrorMacroContext::createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if (nElement == XML_ELEMENT(SCRIPT, XML_EVENTS))
+ {
+ pContext = new XMLEventsImportContext(GetImport());
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLErrorMacroContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pValidationContext->SetErrorMacro( bExecute );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlcvali.hxx b/sc/source/filter/xml/xmlcvali.hxx
new file mode 100644
index 000000000..b3fee13be
--- /dev/null
+++ b/sc/source/filter/xml/xmlcvali.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "importcontext.hxx"
+
+class ScXMLContentValidationsContext : public ScXMLImportContext
+{
+public:
+ ScXMLContentValidationsContext(ScXMLImport& rImport);
+
+ virtual ~ScXMLContentValidationsContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmldpimp.cxx b/sc/source/filter/xml/xmldpimp.cxx
new file mode 100644
index 000000000..307d55f7f
--- /dev/null
+++ b/sc/source/filter/xml/xmldpimp.cxx
@@ -0,0 +1,1536 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmldpimp.hxx"
+#include "xmlimprt.hxx"
+#include "xmlfilti.hxx"
+#include <document.hxx>
+#include <dpshttab.hxx>
+#include <dpsdbtab.hxx>
+#include <attrib.hxx>
+#include "XMLConverter.hxx"
+#include <dpdimsave.hxx>
+#include <rangeutl.hxx>
+#include <dpoutputgeometry.hxx>
+#include <generalfunction.hxx>
+
+#include "pivotsource.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+
+#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
+#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldLayoutMode.hpp>
+#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
+#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+using ::com::sun::star::uno::Reference;
+
+ScXMLDataPilotTablesContext::ScXMLDataPilotTablesContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // has no Attributes
+ rImport.LockSolarMutex();
+}
+
+ScXMLDataPilotTablesContext::~ScXMLDataPilotTablesContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotTablesContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_TABLE ) :
+ {
+ pContext = new ScXMLDataPilotTableContext( GetScImport(), pAttribList );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotTableContext::GrandTotalItem::GrandTotalItem() :
+ mbVisible(true) {}
+
+ScXMLDataPilotTableContext::ScXMLDataPilotTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ pDoc(GetScImport().GetDocument()),
+ pDPSave(new ScDPSaveData()),
+ nSourceType(SQL),
+ mnRowFieldCount(0),
+ mnColFieldCount(0),
+ mnPageFieldCount(0),
+ mnDataFieldCount(0),
+ mnDataLayoutType(sheet::DataPilotFieldOrientation_HIDDEN),
+ bIsNative(true),
+ bIgnoreEmptyRows(false),
+ bIdentifyCategories(false),
+ bTargetRangeAddress(false),
+ bSourceCellRange(false),
+ bShowFilter(true),
+ bDrillDown(true),
+ bHeaderGridLayout(false)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ {
+ sDataPilotTableName = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_APPLICATION_DATA ):
+ {
+ sApplicationData = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_GRAND_TOTAL ):
+ {
+ if (IsXMLToken(aIter, XML_BOTH))
+ {
+ maRowGrandTotal.mbVisible = true;
+ maColGrandTotal.mbVisible = true;
+ }
+ else if (IsXMLToken(aIter, XML_ROW))
+ {
+ maRowGrandTotal.mbVisible = true;
+ maColGrandTotal.mbVisible = false;
+ }
+ else if (IsXMLToken(aIter, XML_COLUMN))
+ {
+ maRowGrandTotal.mbVisible = false;
+ maColGrandTotal.mbVisible = true;
+ }
+ else
+ {
+ maRowGrandTotal.mbVisible = false;
+ maColGrandTotal.mbVisible = false;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_IGNORE_EMPTY_ROWS ):
+ {
+ bIgnoreEmptyRows = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_IDENTIFY_CATEGORIES ):
+ {
+ bIdentifyCategories = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ sal_Int32 nOffset(0);
+ assert(pDoc);
+ bTargetRangeAddress = ScRangeStringConverter::GetRangeFromString( aTargetRangeAddress, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_BUTTONS ):
+ {
+ sButtons = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SHOW_FILTER_BUTTON ):
+ {
+ bShowFilter = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DRILL_DOWN_ON_DOUBLE_CLICK ):
+ {
+ bDrillDown = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_HEADER_GRID_LAYOUT ):
+ {
+ bHeaderGridLayout = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotTableContext::~ScXMLDataPilotTableContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotTableContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_SQL ):
+ {
+ pContext = new ScXMLDPSourceSQLContext(GetScImport(), pAttribList, this);
+ nSourceType = SQL;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_TABLE ):
+ {
+ pContext = new ScXMLDPSourceTableContext(GetScImport(), pAttribList, this);
+ nSourceType = TABLE;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_QUERY ):
+ {
+ pContext = new ScXMLDPSourceQueryContext(GetScImport(), pAttribList, this);
+ nSourceType = QUERY;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_SERVICE ):
+ {
+ pContext = new ScXMLSourceServiceContext(GetScImport(), pAttribList, this);
+ nSourceType = SERVICE;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_GRAND_TOTAL ):
+ case XML_ELEMENT( TABLE_EXT, XML_DATA_PILOT_GRAND_TOTAL ):
+ {
+ pContext = new ScXMLDataPilotGrandTotalContext(GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_CELL_RANGE ):
+ {
+ pContext = new ScXMLSourceCellRangeContext(GetScImport(), pAttribList, this);
+ nSourceType = CELLRANGE;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_FIELD ):
+ pContext = new ScXMLDataPilotFieldContext(GetScImport(), pAttribList, this);
+ break;
+ }
+
+ return pContext;
+}
+
+namespace {
+
+const ScDPSaveDimension* getDimension(
+ const std::vector<const ScDPSaveDimension*>& rRowDims,
+ const std::vector<const ScDPSaveDimension*>& rColDims,
+ const std::vector<const ScDPSaveDimension*>& rPageDims,
+ ScDPOutputGeometry::FieldType eType, size_t nPos)
+{
+ switch (eType)
+ {
+ case ScDPOutputGeometry::Column:
+ {
+ if (rColDims.size() <= nPos)
+ return nullptr;
+
+ return rColDims[nPos];
+ }
+ case ScDPOutputGeometry::Row:
+ {
+ if (rRowDims.size() <= nPos)
+ return nullptr;
+
+ return rRowDims[nPos];
+ }
+ case ScDPOutputGeometry::Page:
+ {
+ if (rPageDims.size() <= nPos)
+ return nullptr;
+
+ return rPageDims[nPos];
+ }
+ case ScDPOutputGeometry::Data:
+ break;
+ case ScDPOutputGeometry::None:
+ break;
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+ScDPOutputGeometry::FieldType toFieldType(sheet::DataPilotFieldOrientation nOrient)
+{
+ switch (nOrient)
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ return ScDPOutputGeometry::Column;
+ case sheet::DataPilotFieldOrientation_DATA:
+ return ScDPOutputGeometry::Data;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ return ScDPOutputGeometry::Page;
+ case sheet::DataPilotFieldOrientation_ROW:
+ return ScDPOutputGeometry::Row;
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ break;
+ default:
+ break;
+ }
+ return ScDPOutputGeometry::None;
+}
+
+}
+
+void ScXMLDataPilotTableContext::SetButtons(ScDPObject* pDPObject)
+{
+ ScDPOutputGeometry aGeometry(aTargetRangeAddress, bShowFilter);
+ aGeometry.setColumnFieldCount(mnColFieldCount);
+ aGeometry.setRowFieldCount(mnRowFieldCount);
+ aGeometry.setPageFieldCount(mnPageFieldCount);
+ aGeometry.setDataFieldCount(mnDataFieldCount);
+ aGeometry.setDataLayoutType(toFieldType(mnDataLayoutType));
+ aGeometry.setHeaderLayout(bHeaderGridLayout);
+
+ std::vector<const ScDPSaveDimension*> aRowDims, aColDims, aPageDims;
+ pDPSave->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_ROW, aRowDims);
+ pDPSave->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_COLUMN, aColDims);
+ pDPSave->GetAllDimensionsByOrientation(sheet::DataPilotFieldOrientation_PAGE, aPageDims);
+
+ OUString sAddress;
+ sal_Int32 nOffset = 0;
+ while( nOffset >= 0 )
+ {
+ ScRangeStringConverter::GetTokenByOffset( sAddress, sButtons, nOffset );
+ if( nOffset >= 0 )
+ {
+ ScAddress aScAddress;
+ sal_Int32 nAddrOffset(0);
+ if (pDoc && ScRangeStringConverter::GetAddressFromString( aScAddress, sAddress, *pDoc, ::formula::FormulaGrammar::CONV_OOO, nAddrOffset ))
+ {
+ std::pair<ScDPOutputGeometry::FieldType, size_t> aBtnType = aGeometry.getFieldButtonType(aScAddress);
+ const ScDPSaveDimension* pDim = getDimension(
+ aRowDims, aColDims, aPageDims, aBtnType.first, aBtnType.second);
+
+ bool bDimension = pDim != nullptr;
+ bool bDataLayout = pDim && pDim->IsDataLayout();
+ bool bHasHidden = pDim && pDim->HasInvisibleMember();
+ bool bPageDim = pDim && pDim->GetOrientation() == sheet::DataPilotFieldOrientation_PAGE;
+
+ if (bPageDim)
+ {
+ // Page dimension needs 2 buttons.
+
+ pDoc->ApplyFlagsTab(aScAddress.Col(), aScAddress.Row(), aScAddress.Col(), aScAddress.Row(), aScAddress.Tab(), ScMF::Button);
+
+ ScMF nMFlag = ScMF::ButtonPopup;
+ if (bHasHidden)
+ nMFlag |= ScMF::HiddenMember;
+ pDoc->ApplyFlagsTab(aScAddress.Col()+1, aScAddress.Row(), aScAddress.Col()+1, aScAddress.Row(), aScAddress.Tab(), nMFlag);
+ }
+ else
+ {
+ ScMF nMFlag = ScMF::Button;
+ if (bDataLayout)
+ {
+ // Data layout dimension only has a plain button with no popup.
+ }
+ else if (bDimension)
+ {
+ // Normal dimension has a popup arrow button.
+ if (bHasHidden)
+ nMFlag |= ScMF::HiddenMember;
+
+ nMFlag |= ScMF::ButtonPopup;
+ }
+
+ pDoc->ApplyFlagsTab(aScAddress.Col(), aScAddress.Row(), aScAddress.Col(), aScAddress.Row(), aScAddress.Tab(), nMFlag);
+ }
+ }
+ }
+ }
+
+ pDPObject->RefreshAfterLoad();
+}
+
+void ScXMLDataPilotTableContext::SetSelectedPage( const OUString& rDimName, const OUString& rSelected )
+{
+ maSelectedPages.emplace(rDimName, rSelected);
+}
+
+void ScXMLDataPilotTableContext::AddDimension(ScDPSaveDimension* pDim)
+{
+ if (!pDPSave)
+ return;
+
+ if (pDim->IsDataLayout())
+ mnDataLayoutType = pDim->GetOrientation();
+
+ // if a dimension with that name has already been inserted,
+ // mark the new one as duplicate
+ if ( !pDim->IsDataLayout() &&
+ pDPSave->GetExistingDimensionByName(pDim->GetName()) )
+ pDim->SetDupFlag(true);
+
+ switch (pDim->GetOrientation())
+ {
+ case sheet::DataPilotFieldOrientation_ROW:
+ ++mnRowFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_COLUMN:
+ ++mnColFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_PAGE:
+ ++mnPageFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_DATA:
+ ++mnDataFieldCount;
+ break;
+ case sheet::DataPilotFieldOrientation_HIDDEN:
+ break;
+ default:
+ break;
+ }
+
+ pDPSave->AddDimension(pDim);
+}
+
+void ScXMLDataPilotTableContext::AddGroupDim(const ScDPSaveNumGroupDimension& aNumGroupDim)
+{
+ if (!pDPDimSaveData)
+ pDPDimSaveData.reset( new ScDPDimensionSaveData );
+ pDPDimSaveData->AddNumGroupDimension(aNumGroupDim);
+}
+
+void ScXMLDataPilotTableContext::AddGroupDim(const ScDPSaveGroupDimension& aGroupDim)
+{
+ if (!pDPDimSaveData)
+ pDPDimSaveData.reset( new ScDPDimensionSaveData );
+ pDPDimSaveData->AddGroupDimension(aGroupDim);
+}
+
+void SAL_CALL ScXMLDataPilotTableContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!bTargetRangeAddress)
+ return;
+
+ std::unique_ptr<ScDPObject> pDPObject(new ScDPObject(pDoc));
+ pDPObject->SetName(sDataPilotTableName);
+ pDPObject->SetTag(sApplicationData);
+ pDPObject->SetOutRange(aTargetRangeAddress);
+ pDPObject->SetHeaderLayout(bHeaderGridLayout);
+
+ sc::PivotTableSources& rPivotSources = GetScImport().GetPivotTableSources();
+
+ switch (nSourceType)
+ {
+ case SQL :
+ {
+ ScImportSourceDesc aImportDesc(pDoc);
+ aImportDesc.aDBName = sDatabaseName;
+ aImportDesc.aObject = sSourceObject;
+ aImportDesc.nType = sheet::DataImportMode_SQL;
+ aImportDesc.bNative = bIsNative;
+ rPivotSources.appendDBSource(pDPObject.get(), aImportDesc);
+ }
+ break;
+ case TABLE :
+ {
+ ScImportSourceDesc aImportDesc(pDoc);
+ aImportDesc.aDBName = sDatabaseName;
+ aImportDesc.aObject = sSourceObject;
+ aImportDesc.nType = sheet::DataImportMode_TABLE;
+ rPivotSources.appendDBSource(pDPObject.get(), aImportDesc);
+ }
+ break;
+ case QUERY :
+ {
+ ScImportSourceDesc aImportDesc(pDoc);
+ aImportDesc.aDBName = sDatabaseName;
+ aImportDesc.aObject = sSourceObject;
+ aImportDesc.nType = sheet::DataImportMode_QUERY;
+ rPivotSources.appendDBSource(pDPObject.get(), aImportDesc);
+ }
+ break;
+ case SERVICE :
+ {
+ ScDPServiceDesc aServiceDesc(sServiceName, sServiceSourceName, sServiceSourceObject,
+ sServiceUsername, sServicePassword);
+ rPivotSources.appendServiceSource(pDPObject.get(), aServiceDesc);
+ }
+ break;
+ case CELLRANGE :
+ {
+ if (bSourceCellRange)
+ {
+ ScSheetSourceDesc aSheetDesc(pDoc);
+ if (!sSourceRangeName.isEmpty())
+ // Range name takes precedence.
+ aSheetDesc.SetRangeName(sSourceRangeName);
+ else
+ aSheetDesc.SetSourceRange(aSourceCellRangeAddress);
+ aSheetDesc.SetQueryParam(aSourceQueryParam);
+ rPivotSources.appendSheetSource(pDPObject.get(), aSheetDesc);
+ }
+ }
+ break;
+ }
+
+ rPivotSources.appendSelectedPages(pDPObject.get(), std::unordered_map(maSelectedPages));
+
+ pDPSave->SetRowGrand(maRowGrandTotal.mbVisible);
+ pDPSave->SetColumnGrand(maColGrandTotal.mbVisible);
+ if (!maRowGrandTotal.maDisplayName.isEmpty())
+ // TODO: Right now, we only support one grand total name for both
+ // column and row totals. Take the value from the row total for
+ // now.
+ pDPSave->SetGrandTotalName(maRowGrandTotal.maDisplayName);
+
+ pDPSave->SetIgnoreEmptyRows(bIgnoreEmptyRows);
+ pDPSave->SetRepeatIfEmpty(bIdentifyCategories);
+ pDPSave->SetFilterButton(bShowFilter);
+ pDPSave->SetDrillDown(bDrillDown);
+ if (pDPDimSaveData)
+ pDPSave->SetDimensionData(pDPDimSaveData.get());
+ pDPObject->SetSaveData(*pDPSave);
+
+ ScDPCollection* pDPCollection = pDoc->GetDPCollection();
+
+ // #i94570# Names have to be unique, or the tables can't be accessed by API.
+ if ( pDPCollection->GetByName(pDPObject->GetName()) )
+ pDPObject->SetName( OUString() ); // ignore the invalid name, create a new name in AfterXMLLoading
+
+ SetButtons(pDPObject.get());
+
+ pDPCollection->InsertNewTable(std::move(pDPObject));
+}
+
+void ScXMLDataPilotTableContext::SetGrandTotal(
+ XMLTokenEnum eOrientation, bool bVisible, const OUString& rDisplayName)
+{
+ switch (eOrientation)
+ {
+ case XML_BOTH:
+ maRowGrandTotal.mbVisible = bVisible;
+ maRowGrandTotal.maDisplayName = rDisplayName;
+ maColGrandTotal.mbVisible = bVisible;
+ maColGrandTotal.maDisplayName = rDisplayName;
+ break;
+ case XML_ROW:
+ maRowGrandTotal.mbVisible = bVisible;
+ maRowGrandTotal.maDisplayName = rDisplayName;
+ break;
+ case XML_COLUMN:
+ maColGrandTotal.mbVisible = bVisible;
+ maColGrandTotal.maDisplayName = rDisplayName;
+ break;
+ default:
+ break;
+ }
+}
+
+ScXMLDPSourceSQLContext::ScXMLDPSourceSQLContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ pDataPilotTable->SetDatabaseName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_SQL_STATEMENT ):
+ pDataPilotTable->SetSourceObject(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_PARSE_SQL_STATEMENT ):
+ pDataPilotTable->SetNative(!IsXMLToken(aIter, XML_TRUE));
+ break;
+ }
+ }
+}
+
+ScXMLDPSourceSQLContext::~ScXMLDPSourceSQLContext()
+{
+}
+
+ScXMLDPSourceTableContext::ScXMLDPSourceTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ pDataPilotTable->SetDatabaseName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_NAME ):
+ case XML_ELEMENT( TABLE, XML_DATABASE_TABLE_NAME ):
+ pDataPilotTable->SetSourceObject(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLDPSourceTableContext::~ScXMLDPSourceTableContext()
+{
+}
+
+ScXMLDPSourceQueryContext::ScXMLDPSourceQueryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ pDataPilotTable->SetDatabaseName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_QUERY_NAME ):
+ pDataPilotTable->SetSourceObject(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLDPSourceQueryContext::~ScXMLDPSourceQueryContext()
+{
+}
+
+ScXMLSourceServiceContext::ScXMLSourceServiceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ pDataPilotTable->SetServiceName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_SOURCE_NAME ):
+ pDataPilotTable->SetServiceSourceName(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_OBJECT_NAME ):
+ pDataPilotTable->SetServiceSourceObject(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_USER_NAME ):
+ pDataPilotTable->SetServiceUsername(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_PASSWORD ):
+ pDataPilotTable->SetServicePassword(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLSourceServiceContext::~ScXMLSourceServiceContext()
+{
+}
+
+ScXMLDataPilotGrandTotalContext::ScXMLDataPilotGrandTotalContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTableContext ) :
+ ScXMLImportContext( rImport ),
+ mpTableContext(pTableContext),
+ meOrientation(NONE),
+ mbVisible(false)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DISPLAY ):
+ mbVisible = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_ORIENTATION ):
+ if (IsXMLToken(aIter, XML_BOTH))
+ meOrientation = BOTH;
+ else if (IsXMLToken(aIter, XML_ROW))
+ meOrientation = ROW;
+ else if (IsXMLToken(aIter, XML_COLUMN))
+ meOrientation = COLUMN;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_NAME ):
+ case XML_ELEMENT( TABLE_EXT, XML_DISPLAY_NAME ):
+ maDisplayName = aIter.toString();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotGrandTotalContext::~ScXMLDataPilotGrandTotalContext()
+{
+}
+
+ void SAL_CALL ScXMLDataPilotGrandTotalContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ XMLTokenEnum eOrient = XML_NONE;
+ switch (meOrientation)
+ {
+ case BOTH:
+ eOrient = XML_BOTH;
+ break;
+ case ROW:
+ eOrient = XML_ROW;
+ break;
+ case COLUMN:
+ eOrient = XML_COLUMN;
+ break;
+ default:
+ break;
+ }
+ mpTableContext->SetGrandTotal(eOrient, mbVisible, maDisplayName);
+}
+
+ScXMLSourceCellRangeContext::ScXMLSourceCellRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTempDataPilotTable) :
+ ScXMLImportContext( rImport ),
+ pDataPilotTable(pTempDataPilotTable)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_ADDRESS ):
+ {
+ ScRange aSourceRangeAddress;
+ sal_Int32 nOffset(0);
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ if (ScRangeStringConverter::GetRangeFromString( aSourceRangeAddress, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ))
+ pDataPilotTable->SetSourceCellRangeAddress(aSourceRangeAddress);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ pDataPilotTable->SetSourceRangeName(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLSourceCellRangeContext::~ScXMLSourceCellRangeContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSourceCellRangeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER ):
+ pContext = new ScXMLDPFilterContext(GetScImport(), pAttribList, pDataPilotTable);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotFieldContext::ScXMLDataPilotFieldContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTempDataPilotTable) :
+ ScXMLImportContext( rImport ),
+ pDataPilotTable(pTempDataPilotTable),
+ fStart(0.0),
+ fEnd(0.0),
+ fStep(0.0),
+ nUsedHierarchy(1),
+ nGroupPart(0),
+ nFunction(ScGeneralFunction::NONE),
+ nOrientation(sheet::DataPilotFieldOrientation_HIDDEN),
+ bSelectedPage(false),
+ bIsGroupField(false),
+ bDateValue(false),
+ bAutoStart(false),
+ bAutoEnd(false),
+ mbHasHiddenMember(false)
+{
+ bool bHasName = false;
+ bool bDataLayout = false;
+ bool bIgnoreSelectedPage = false;
+ OUString aDisplayName;
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_SOURCE_FIELD_NAME ):
+ sName = aIter.toString();
+ bHasName = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_NAME ):
+ case XML_ELEMENT( TABLE_EXT, XML_DISPLAY_NAME ):
+ aDisplayName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_IS_DATA_LAYOUT_FIELD ):
+ bDataLayout = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_FUNCTION ):
+ nFunction = ScXMLConverter::GetFunctionFromString2( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_ORIENTATION ):
+ nOrientation = ScXMLConverter::GetOrientationFromString( aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_SELECTED_PAGE ):
+ sSelectedPage = aIter.toString();
+ bSelectedPage = true;
+ break;
+ case XML_ELEMENT( LO_EXT, XML_IGNORE_SELECTED_PAGE ):
+ bIgnoreSelectedPage = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_USED_HIERARCHY ):
+ nUsedHierarchy = aIter.toInt32();
+ break;
+ }
+ }
+ }
+
+ // use the new extension elements
+ if (bIgnoreSelectedPage)
+ bSelectedPage = false;
+
+ if (bHasName)
+ {
+ xDim.reset(new ScDPSaveDimension(sName, bDataLayout));
+ if (!aDisplayName.isEmpty())
+ xDim->SetLayoutName(aDisplayName);
+ }
+}
+
+ScXMLDataPilotFieldContext::~ScXMLDataPilotFieldContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotFieldContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_LEVEL ):
+ pContext = new ScXMLDataPilotLevelContext(GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_FIELD_REFERENCE ):
+ pContext = new ScXMLDataPilotFieldReferenceContext(GetScImport(), pAttribList, this);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_GROUPS ):
+ pContext = new ScXMLDataPilotGroupsContext(GetScImport(), pAttribList, this);
+ break;
+ }
+
+ return pContext;
+}
+
+void ScXMLDataPilotFieldContext::AddMember(std::unique_ptr<ScDPSaveMember> pMember)
+{
+ if (xDim)
+ {
+ bool isVisible = pMember->GetIsVisible();
+ xDim->AddMember(std::move(pMember));
+ if (!isVisible)
+ // This member is hidden.
+ mbHasHiddenMember = true;
+ }
+}
+
+void ScXMLDataPilotFieldContext::SetSubTotalName(const OUString& rName)
+{
+ if (xDim)
+ xDim->SetSubtotalName(rName);
+}
+
+void ScXMLDataPilotFieldContext::AddGroup(::std::vector<OUString>&& rMembers, const OUString& rName)
+{
+ ScXMLDataPilotGroup aGroup;
+ aGroup.aMembers = std::move(rMembers);
+ aGroup.aName = rName;
+ aGroups.push_back(std::move(aGroup));
+}
+
+void SAL_CALL ScXMLDataPilotFieldContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!xDim)
+ return;
+
+ xDim->SetUsedHierarchy(nUsedHierarchy);
+ xDim->SetFunction(nFunction);
+ xDim->SetOrientation(nOrientation);
+ if (bSelectedPage)
+ {
+ pDataPilotTable->SetSelectedPage(xDim->GetName(), sSelectedPage);
+ }
+ pDataPilotTable->AddDimension(xDim.release());
+ if (!bIsGroupField)
+ return;
+
+ ScDPNumGroupInfo aInfo;
+ aInfo.mbEnable = true;
+ aInfo.mbDateValues = bDateValue;
+ aInfo.mbAutoStart = bAutoStart;
+ aInfo.mbAutoEnd = bAutoEnd;
+ aInfo.mfStart = fStart;
+ aInfo.mfEnd = fEnd;
+ aInfo.mfStep = fStep;
+ if (!sGroupSource.isEmpty())
+ {
+ ScDPSaveGroupDimension aGroupDim(sGroupSource, sName);
+ if (nGroupPart)
+ aGroupDim.SetDateInfo(aInfo, nGroupPart);
+ else
+ {
+ for (const auto& rGroup : aGroups)
+ {
+ ScDPSaveGroupItem aItem(rGroup.aName);
+ for (const auto& rMember : rGroup.aMembers)
+ {
+ aItem.AddElement(rMember);
+ }
+ aGroupDim.AddGroupItem(aItem);
+ }
+ }
+ pDataPilotTable->AddGroupDim(aGroupDim);
+ }
+ else //NumGroup
+ {
+ ScDPSaveNumGroupDimension aNumGroupDim(sName, aInfo);
+ if (nGroupPart)
+ aNumGroupDim.SetDateInfo(aInfo, nGroupPart);
+ pDataPilotTable->AddGroupDim(aNumGroupDim);
+ }
+}
+
+ScXMLDataPilotFieldReferenceContext::ScXMLDataPilotFieldReferenceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField) :
+ ScXMLImportContext( rImport )
+{
+ sheet::DataPilotFieldReference aReference;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TYPE ):
+ {
+ if (IsXMLToken(aIter, XML_NONE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::NONE;
+ else if (IsXMLToken(aIter, XML_MEMBER_DIFFERENCE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE;
+ else if (IsXMLToken(aIter, XML_MEMBER_PERCENTAGE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE;
+ else if (IsXMLToken(aIter, XML_MEMBER_PERCENTAGE_DIFFERENCE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
+ else if (IsXMLToken(aIter, XML_RUNNING_TOTAL))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::RUNNING_TOTAL;
+ else if (IsXMLToken(aIter, XML_ROW_PERCENTAGE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE;
+ else if (IsXMLToken(aIter, XML_COLUMN_PERCENTAGE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
+ else if (IsXMLToken(aIter, XML_TOTAL_PERCENTAGE))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE;
+ else if (IsXMLToken(aIter, XML_INDEX))
+ aReference.ReferenceType = sheet::DataPilotFieldReferenceType::INDEX;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FIELD_NAME ):
+ {
+ aReference.ReferenceField = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_MEMBER_TYPE ):
+ {
+ if (IsXMLToken(aIter, XML_NAMED))
+ aReference.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::NAMED;
+ else if (IsXMLToken(aIter, XML_PREVIOUS))
+ aReference.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::PREVIOUS;
+ else if (IsXMLToken(aIter, XML_NEXT))
+ aReference.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::NEXT;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_MEMBER_NAME ):
+ {
+ aReference.ReferenceItemName = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetFieldReference(aReference);
+}
+
+ScXMLDataPilotFieldReferenceContext::~ScXMLDataPilotFieldReferenceContext()
+{
+}
+
+ScXMLDataPilotLevelContext::ScXMLDataPilotLevelContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_SHOW_EMPTY ):
+ pDataPilotField->SetShowEmpty(IsXMLToken(aIter, XML_TRUE));
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_REPEAT_ITEM_LABELS ):
+ pDataPilotField->SetRepeatItemLabels(IsXMLToken(aIter, XML_TRUE));
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotLevelContext::~ScXMLDataPilotLevelContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotLevelContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_SUBTOTALS ):
+ pContext = new ScXMLDataPilotSubTotalsContext(GetScImport(), pDataPilotField);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_MEMBERS ):
+ pContext = new ScXMLDataPilotMembersContext(GetScImport(), pDataPilotField);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_DISPLAY_INFO ):
+ pContext = new ScXMLDataPilotDisplayInfoContext(GetScImport(), pAttribList, pDataPilotField);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_SORT_INFO ):
+ pContext = new ScXMLDataPilotSortInfoContext(GetScImport(), pAttribList, pDataPilotField);
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_LAYOUT_INFO ):
+ pContext = new ScXMLDataPilotLayoutInfoContext(GetScImport(), pAttribList, pDataPilotField);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotDisplayInfoContext::ScXMLDataPilotDisplayInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField) :
+ ScXMLImportContext( rImport )
+{
+ sheet::DataPilotFieldAutoShowInfo aInfo;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ENABLED ):
+ if (IsXMLToken(aIter, XML_TRUE))
+ aInfo.IsEnabled = true;
+ else
+ aInfo.IsEnabled = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_MEMBER_MODE ):
+ if (IsXMLToken(aIter, XML_FROM_TOP))
+ aInfo.ShowItemsMode = sheet::DataPilotFieldShowItemsMode::FROM_TOP;
+ else if (IsXMLToken(aIter, XML_FROM_BOTTOM))
+ aInfo.ShowItemsMode = sheet::DataPilotFieldShowItemsMode::FROM_BOTTOM;
+ break;
+ case XML_ELEMENT( TABLE, XML_MEMBER_COUNT ):
+ aInfo.ItemCount = aIter.toInt32();
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_FIELD ):
+ aInfo.DataField = aIter.toString();
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetAutoShowInfo(aInfo);
+}
+
+ScXMLDataPilotDisplayInfoContext::~ScXMLDataPilotDisplayInfoContext()
+{
+}
+
+ScXMLDataPilotSortInfoContext::ScXMLDataPilotSortInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField) :
+ ScXMLImportContext( rImport )
+{
+ sheet::DataPilotFieldSortInfo aInfo;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ORDER ):
+ if (IsXMLToken(aIter, XML_ASCENDING))
+ aInfo.IsAscending = true;
+ else if (IsXMLToken(aIter, XML_DESCENDING))
+ aInfo.IsAscending = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_SORT_MODE ):
+ if (IsXMLToken(aIter, XML_NONE))
+ aInfo.Mode = sheet::DataPilotFieldSortMode::NONE;
+ else if (IsXMLToken(aIter, XML_MANUAL))
+ aInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
+ else if (IsXMLToken(aIter, XML_NAME))
+ aInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
+ else if (IsXMLToken(aIter, XML_DATA))
+ aInfo.Mode = sheet::DataPilotFieldSortMode::DATA;
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_FIELD ):
+ aInfo.Field = aIter.toString();
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetSortInfo(aInfo);
+}
+
+ScXMLDataPilotSortInfoContext::~ScXMLDataPilotSortInfoContext()
+{
+}
+
+ScXMLDataPilotLayoutInfoContext::ScXMLDataPilotLayoutInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField) :
+ ScXMLImportContext( rImport )
+{
+ sheet::DataPilotFieldLayoutInfo aInfo;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_ADD_EMPTY_LINES ):
+ if (IsXMLToken(aIter, XML_TRUE))
+ aInfo.AddEmptyLines = true;
+ else
+ aInfo.AddEmptyLines = false;
+ break;
+ case XML_ELEMENT( TABLE, XML_LAYOUT_MODE ):
+ if (IsXMLToken(aIter, XML_TABULAR_LAYOUT))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::TABULAR_LAYOUT;
+ else if (IsXMLToken(aIter, XML_OUTLINE_SUBTOTALS_TOP))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_TOP;
+ else if (IsXMLToken(aIter, XML_OUTLINE_SUBTOTALS_BOTTOM))
+ aInfo.LayoutMode = sheet::DataPilotFieldLayoutMode::OUTLINE_SUBTOTALS_BOTTOM;
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetLayoutInfo(aInfo);}
+
+ScXMLDataPilotLayoutInfoContext::~ScXMLDataPilotLayoutInfoContext()
+{
+}
+
+ScXMLDataPilotSubTotalsContext::ScXMLDataPilotSubTotalsContext( ScXMLImport& rImport,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+
+ // has no attributes
+}
+
+ScXMLDataPilotSubTotalsContext::~ScXMLDataPilotSubTotalsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotSubTotalsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_SUBTOTAL ):
+ pContext = new ScXMLDataPilotSubTotalContext(GetScImport(), pAttribList, this);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDataPilotSubTotalsContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pDataPilotField->SetSubTotals(std::vector(maFunctions));
+ if (!maDisplayName.isEmpty())
+ pDataPilotField->SetSubTotalName(maDisplayName);
+}
+
+void ScXMLDataPilotSubTotalsContext::AddFunction(ScGeneralFunction nFunction)
+{
+ maFunctions.push_back(nFunction);
+}
+
+void ScXMLDataPilotSubTotalsContext::SetDisplayName(const OUString& rName)
+{
+ maDisplayName = rName;
+}
+
+ScXMLDataPilotSubTotalContext::ScXMLDataPilotSubTotalContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotSubTotalsContext* pDataPilotSubTotals) :
+ ScXMLImportContext( rImport )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FUNCTION ):
+ pDataPilotSubTotals->AddFunction( ScXMLConverter::GetFunctionFromString2( aIter.toString() ) );
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_NAME ):
+ case XML_ELEMENT( TABLE_EXT, XML_DISPLAY_NAME ):
+ pDataPilotSubTotals->SetDisplayName(aIter.toString());
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotSubTotalContext::~ScXMLDataPilotSubTotalContext()
+{
+}
+
+ScXMLDataPilotMembersContext::ScXMLDataPilotMembersContext( ScXMLImport& rImport,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+ // has no attributes
+}
+
+ScXMLDataPilotMembersContext::~ScXMLDataPilotMembersContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotMembersContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_PILOT_MEMBER ):
+ pContext = new ScXMLDataPilotMemberContext(GetScImport(), pAttribList, pDataPilotField);
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotMemberContext::ScXMLDataPilotMemberContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField),
+ bDisplay( true ),
+ bDisplayDetails( true ),
+ bHasName( false )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ sName = aIter.toString();
+ bHasName = true;
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_NAME ):
+ case XML_ELEMENT( TABLE_EXT, XML_DISPLAY_NAME ):
+ maDisplayName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY ):
+ bDisplay = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_SHOW_DETAILS ):
+ bDisplayDetails = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+ScXMLDataPilotMemberContext::~ScXMLDataPilotMemberContext()
+{
+}
+
+void SAL_CALL ScXMLDataPilotMemberContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (bHasName) // #i53407# don't check sName, empty name is allowed
+ {
+ std::unique_ptr<ScDPSaveMember> pMember(new ScDPSaveMember(sName));
+ if (!maDisplayName.isEmpty())
+ pMember->SetLayoutName(maDisplayName);
+ pMember->SetIsVisible(bDisplay);
+ pMember->SetShowDetails(bDisplayDetails);
+ pDataPilotField->AddMember(std::move(pMember));
+ }
+}
+
+ScXMLDataPilotGroupsContext::ScXMLDataPilotGroupsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+ OUString sGroupSource;
+ double fStart(0.0);
+ double fEnd(0.0);
+ double fStep(0.0);
+ sal_Int32 nGroupPart(0);
+ bool bDateValue(false);
+ bool bAutoStart(true);
+ bool bAutoEnd(true);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_SOURCE_FIELD_NAME :
+ {
+ sGroupSource = aIter.toString();
+ }
+ break;
+ case XML_DATE_START :
+ {
+ bDateValue = true;
+ if (IsXMLToken(aIter, XML_AUTO))
+ bAutoStart = true;
+ else
+ {
+ GetScImport().GetMM100UnitConverter().convertDateTime(fStart, aIter.toView());
+ bAutoStart = false;
+ }
+ }
+ break;
+ case XML_DATE_END :
+ {
+ bDateValue = true;
+ if (IsXMLToken(aIter, XML_AUTO))
+ bAutoEnd = true;
+ else
+ {
+ GetScImport().GetMM100UnitConverter().convertDateTime(fEnd, aIter.toView());
+ bAutoEnd = false;
+ }
+ }
+ break;
+ case XML_START :
+ {
+ if (IsXMLToken(aIter, XML_AUTO))
+ bAutoStart = true;
+ else
+ {
+ fStart = aIter.toDouble();
+ bAutoStart = false;
+ }
+ }
+ break;
+ case XML_END :
+ {
+ if (IsXMLToken(aIter, XML_AUTO))
+ bAutoEnd = true;
+ else
+ {
+ fEnd = aIter.toDouble();
+ bAutoEnd = false;
+ }
+ }
+ break;
+ case XML_STEP :
+ {
+ fStep = aIter.toDouble();
+ }
+ break;
+ case XML_GROUPED_BY :
+ {
+ if (IsXMLToken(aIter, XML_SECONDS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::SECONDS;
+ else if (IsXMLToken(aIter, XML_MINUTES))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::MINUTES;
+ else if (IsXMLToken(aIter, XML_HOURS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::HOURS;
+ else if (IsXMLToken(aIter, XML_DAYS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::DAYS;
+ else if (IsXMLToken(aIter, XML_MONTHS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::MONTHS;
+ else if (IsXMLToken(aIter, XML_QUARTERS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::QUARTERS;
+ else if (IsXMLToken(aIter, XML_YEARS))
+ nGroupPart = css::sheet::DataPilotFieldGroupBy::YEARS;
+ }
+ break;
+ }
+ }
+ }
+ pDataPilotField->SetGrouping(sGroupSource, fStart, fEnd, fStep, nGroupPart, bDateValue, bAutoStart, bAutoEnd);
+}
+
+ScXMLDataPilotGroupsContext::~ScXMLDataPilotGroupsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotGroupsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT( TABLE, XML_DATA_PILOT_GROUP ))
+ {
+ pContext = new ScXMLDataPilotGroupContext(GetScImport(), pAttribList, pDataPilotField);
+ }
+
+ return pContext;
+}
+
+ScXMLDataPilotGroupContext::ScXMLDataPilotGroupContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pTempDataPilotField) :
+ ScXMLImportContext( rImport ),
+ pDataPilotField(pTempDataPilotField)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_NAME ) ) );
+ if (aIter != rAttrList->end())
+ sName = aIter.toString();
+ }
+}
+
+ScXMLDataPilotGroupContext::~ScXMLDataPilotGroupContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDataPilotGroupContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT( TABLE, XML_DATA_PILOT_MEMBER ) ||
+ nElement == XML_ELEMENT( TABLE, XML_DATA_PILOT_GROUP_MEMBER ))
+ {
+ pContext = new ScXMLDataPilotGroupMemberContext(GetScImport(), pAttribList, this);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDataPilotGroupContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pDataPilotField->AddGroup(std::vector(aMembers), sName);
+}
+
+ScXMLDataPilotGroupMemberContext::ScXMLDataPilotGroupMemberContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotGroupContext* pTempDataPilotGroup) :
+ ScXMLImportContext( rImport ),
+ pDataPilotGroup(pTempDataPilotGroup)
+{
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_NAME ) ) );
+ if (aIter != rAttrList->end())
+ sName = aIter.toString();
+ }
+}
+
+ScXMLDataPilotGroupMemberContext::~ScXMLDataPilotGroupMemberContext()
+{
+}
+
+void SAL_CALL ScXMLDataPilotGroupMemberContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!sName.isEmpty())
+ pDataPilotGroup->AddMember(sName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmldpimp.hxx b/sc/source/filter/xml/xmldpimp.hxx
new file mode 100644
index 000000000..a7d5f607f
--- /dev/null
+++ b/sc/source/filter/xml/xmldpimp.hxx
@@ -0,0 +1,470 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <memory>
+#include <xmloff/xmltoken.hxx>
+
+#include <dpsave.hxx>
+#include <queryparam.hxx>
+#include "importcontext.hxx"
+
+#include <unordered_map>
+
+namespace com::sun::star::sheet { struct DataPilotFieldAutoShowInfo; }
+namespace com::sun::star::sheet { struct DataPilotFieldLayoutInfo; }
+namespace com::sun::star::sheet { struct DataPilotFieldReference; }
+namespace com::sun::star::sheet { struct DataPilotFieldSortInfo; }
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScDPSaveNumGroupDimension;
+class ScDPSaveGroupDimension;
+class ScDPObject;
+
+enum ScMySourceType
+{
+ SQL,
+ TABLE,
+ QUERY,
+ SERVICE,
+ CELLRANGE
+};
+
+class ScXMLDataPilotTablesContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotTablesContext( ScXMLImport& rImport);
+
+ virtual ~ScXMLDataPilotTablesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDataPilotTableContext : public ScXMLImportContext
+{
+ typedef std::unordered_map<OUString, OUString> SelectedPagesType;
+
+ struct GrandTotalItem
+ {
+ OUString maDisplayName;
+ bool mbVisible;
+ GrandTotalItem();
+ };
+ ScDocument* pDoc;
+ std::unique_ptr<ScDPSaveData> pDPSave;
+ std::unique_ptr<ScDPDimensionSaveData> pDPDimSaveData;
+ GrandTotalItem maRowGrandTotal;
+ GrandTotalItem maColGrandTotal;
+ OUString sDataPilotTableName;
+ OUString sApplicationData;
+ OUString sDatabaseName;
+ OUString sSourceObject;
+ OUString sServiceName;
+ OUString sServiceSourceName;
+ OUString sServiceSourceObject;
+ OUString sServiceUsername;
+ OUString sServicePassword;
+ OUString sButtons;
+ OUString sSourceRangeName;
+ ScRange aSourceCellRangeAddress;
+ ScRange aTargetRangeAddress;
+ ScQueryParam aSourceQueryParam;
+ ScMySourceType nSourceType;
+ sal_uInt32 mnRowFieldCount;
+ sal_uInt32 mnColFieldCount;
+ sal_uInt32 mnPageFieldCount;
+ sal_uInt32 mnDataFieldCount;
+ css::sheet::DataPilotFieldOrientation
+ mnDataLayoutType;
+ bool bIsNative:1;
+ bool bIgnoreEmptyRows:1;
+ bool bIdentifyCategories:1;
+ bool bTargetRangeAddress:1;
+ bool bSourceCellRange:1;
+ bool bShowFilter:1;
+ bool bDrillDown:1;
+ bool bHeaderGridLayout:1;
+
+ SelectedPagesType maSelectedPages;
+
+public:
+
+ ScXMLDataPilotTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLDataPilotTableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetGrandTotal(::xmloff::token::XMLTokenEnum eOrientation, bool bVisible, const OUString& rDisplayName);
+ void SetDatabaseName(const OUString& sValue) { sDatabaseName = sValue; }
+ void SetSourceObject(const OUString& sValue) { sSourceObject = sValue; }
+ void SetNative(bool bValue) { bIsNative = bValue; }
+ void SetServiceName(const OUString& sValue) { sServiceName = sValue; }
+ void SetServiceSourceName(const OUString& sValue) { sServiceSourceName = sValue; }
+ void SetServiceSourceObject(const OUString& sValue) { sServiceSourceObject = sValue; }
+ void SetServiceUsername(const OUString& sValue) { sServiceUsername = sValue; }
+ void SetServicePassword(const OUString& sValue) { sServicePassword = sValue; }
+ void SetSourceRangeName(const OUString& sValue) { sSourceRangeName = sValue; bSourceCellRange = true; }
+ void SetSourceCellRangeAddress(const ScRange& aValue) { aSourceCellRangeAddress = aValue; bSourceCellRange = true; }
+ void SetSourceQueryParam(const ScQueryParam& aValue) { aSourceQueryParam = aValue; }
+ void AddDimension(ScDPSaveDimension* pDim);
+ void AddGroupDim(const ScDPSaveNumGroupDimension& aNumGroupDim);
+ void AddGroupDim(const ScDPSaveGroupDimension& aGroupDim);
+ void SetButtons(ScDPObject* pDPObject);
+ void SetSelectedPage( const OUString& rDimName, const OUString& rSelected );
+};
+
+class ScXMLDPSourceSQLContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDPSourceSQLContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLDPSourceSQLContext() override;
+};
+
+class ScXMLDPSourceTableContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDPSourceTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLDPSourceTableContext() override;
+};
+
+class ScXMLDPSourceQueryContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDPSourceQueryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLDPSourceQueryContext() override;
+};
+
+class ScXMLSourceServiceContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLSourceServiceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLSourceServiceContext() override;
+};
+
+class ScXMLDataPilotGrandTotalContext : public ScXMLImportContext
+{
+ enum Orientation { COLUMN, ROW, BOTH, NONE };
+
+ ScXMLDataPilotTableContext* mpTableContext;
+ OUString maDisplayName;
+ Orientation meOrientation;
+ bool mbVisible;
+
+public:
+ ScXMLDataPilotGrandTotalContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTableContext );
+
+ virtual ~ScXMLDataPilotGrandTotalContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLSourceCellRangeContext : public ScXMLImportContext
+{
+ ScXMLDataPilotTableContext* pDataPilotTable;
+
+public:
+
+ ScXMLSourceCellRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLSourceCellRangeContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+struct ScXMLDataPilotGroup
+{
+ ::std::vector<OUString> aMembers;
+ OUString aName;
+};
+
+class ScXMLDataPilotFieldContext : public ScXMLImportContext
+{
+ ScXMLDataPilotTableContext* pDataPilotTable;
+ std::unique_ptr<ScDPSaveDimension> xDim;
+
+ ::std::vector<ScXMLDataPilotGroup> aGroups;
+ OUString sGroupSource;
+ OUString sSelectedPage;
+ OUString sName;
+ double fStart;
+ double fEnd;
+ double fStep;
+ sal_Int32 nUsedHierarchy;
+ sal_Int32 nGroupPart;
+ ScGeneralFunction nFunction;
+ css::sheet::DataPilotFieldOrientation
+ nOrientation;
+ bool bSelectedPage:1;
+ bool bIsGroupField:1;
+ bool bDateValue:1;
+ bool bAutoStart:1;
+ bool bAutoEnd:1;
+ bool mbHasHiddenMember:1; // TODO: import to document core
+
+public:
+
+ ScXMLDataPilotFieldContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pDataPilotTable);
+
+ virtual ~ScXMLDataPilotFieldContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetShowEmpty(const bool bValue) { if (xDim) xDim->SetShowEmpty(bValue); }
+ void SetRepeatItemLabels(const bool bSet) { if (xDim) xDim->SetRepeatItemLabels(bSet); }
+ void SetSubTotals(std::vector<ScGeneralFunction> && rFunctions) { if (xDim) xDim->SetSubTotals(std::move(rFunctions)); }
+ void AddMember(std::unique_ptr<ScDPSaveMember> pMember);
+ void SetSubTotalName(const OUString& rName);
+ void SetFieldReference(const css::sheet::DataPilotFieldReference& aRef) { if (xDim) xDim->SetReferenceValue(&aRef); }
+ void SetAutoShowInfo(const css::sheet::DataPilotFieldAutoShowInfo& aInfo) { if (xDim) xDim->SetAutoShowInfo(&aInfo); }
+ void SetSortInfo(const css::sheet::DataPilotFieldSortInfo& aInfo) { if (xDim) xDim->SetSortInfo(&aInfo); }
+ void SetLayoutInfo(const css::sheet::DataPilotFieldLayoutInfo& aInfo) { if (xDim) xDim->SetLayoutInfo(&aInfo); }
+ void SetGrouping(const OUString& rGroupSource, const double& rStart, const double& rEnd, const double& rStep,
+ sal_Int32 nPart, bool bDate, bool bAutoSt, bool bAutoE)
+ {
+ bIsGroupField = true;
+ sGroupSource = rGroupSource;
+ fStart = rStart;
+ fEnd = rEnd;
+ fStep = rStep;
+ nGroupPart = nPart;
+ bDateValue = bDate;
+ bAutoStart = bAutoSt;
+ bAutoEnd = bAutoE;
+ }
+ void AddGroup(::std::vector<OUString>&& rMembers, const OUString& rName);
+};
+
+class ScXMLDataPilotFieldReferenceContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotFieldReferenceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotFieldReferenceContext() override;
+};
+
+class ScXMLDataPilotLevelContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+public:
+
+ ScXMLDataPilotLevelContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotLevelContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDataPilotDisplayInfoContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotDisplayInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotDisplayInfoContext() override;
+};
+
+class ScXMLDataPilotSortInfoContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotSortInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotSortInfoContext() override;
+};
+
+class ScXMLDataPilotLayoutInfoContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotLayoutInfoContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotLayoutInfoContext() override;
+};
+
+class ScXMLDataPilotSubTotalsContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+ std::vector<ScGeneralFunction> maFunctions;
+ OUString maDisplayName;
+
+public:
+ ScXMLDataPilotSubTotalsContext( ScXMLImport& rImport,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotSubTotalsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ void AddFunction(ScGeneralFunction nFunction);
+ void SetDisplayName(const OUString& rName);
+};
+
+class ScXMLDataPilotSubTotalContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDataPilotSubTotalContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotSubTotalsContext* pDataPilotSubTotals);
+
+ virtual ~ScXMLDataPilotSubTotalContext() override;
+};
+
+class ScXMLDataPilotMembersContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+public:
+
+ ScXMLDataPilotMembersContext( ScXMLImport& rImport,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotMembersContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDataPilotMemberContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+ OUString sName;
+ OUString maDisplayName;
+ bool bDisplay;
+ bool bDisplayDetails;
+ bool bHasName;
+
+public:
+
+ ScXMLDataPilotMemberContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotMemberContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDataPilotGroupsContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+public:
+
+ ScXMLDataPilotGroupsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotGroupsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLDataPilotGroupContext : public ScXMLImportContext
+{
+ ScXMLDataPilotFieldContext* pDataPilotField;
+
+ OUString sName;
+ ::std::vector<OUString> aMembers;
+
+public:
+
+ ScXMLDataPilotGroupContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotFieldContext* pDataPilotField);
+
+ virtual ~ScXMLDataPilotGroupContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void AddMember(const OUString& sMember) { aMembers.push_back(sMember); }
+};
+
+class ScXMLDataPilotGroupMemberContext : public ScXMLImportContext
+{
+ ScXMLDataPilotGroupContext* pDataPilotGroup;
+ OUString sName;
+
+public:
+
+ ScXMLDataPilotGroupMemberContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotGroupContext* pDataPilotGroup);
+
+ virtual ~ScXMLDataPilotGroupMemberContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmldrani.cxx b/sc/source/filter/xml/xmldrani.cxx
new file mode 100644
index 000000000..5708c74d2
--- /dev/null
+++ b/sc/source/filter/xml/xmldrani.cxx
@@ -0,0 +1,812 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmldrani.hxx"
+#include "xmlimprt.hxx"
+#include "xmlfilti.hxx"
+#include "xmlsorti.hxx"
+#include <document.hxx>
+#include <globalnames.hxx>
+#include <dbdata.hxx>
+#include <datauno.hxx>
+#include <attrib.hxx>
+#include <unonames.hxx>
+#include "XMLConverter.hxx"
+#include <rangeutl.hxx>
+#include <dputil.hxx>
+#include <sortparam.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <sax/tools/converter.hxx>
+
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/table/TableOrientation.hpp>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLDatabaseRangesContext::ScXMLDatabaseRangesContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // has no attributes
+ rImport.LockSolarMutex();
+}
+
+ScXMLDatabaseRangesContext::~ScXMLDatabaseRangesContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDatabaseRangesContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_RANGE ):
+ {
+ pContext = new ScXMLDatabaseRangeContext( GetScImport(), pAttribList );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLDatabaseRangeContext::ScXMLDatabaseRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ mpQueryParam(new ScQueryParam),
+ sDatabaseRangeName(STR_DB_LOCAL_NONAME),
+ nSourceType(sheet::DataImportMode_NONE),
+ nRefresh(0),
+ nSubTotalsUserListIndex(0),
+ mbValidRange(true),
+ bContainsSort(false),
+ bContainsSubTotal(false),
+ bNative(true),
+ bIsSelection(false),
+ bKeepFormats(false),
+ bMoveCells(false),
+ bStripData(false),
+ bAutoFilter(false),
+ bSubTotalsBindFormatsToContent(false),
+ bSubTotalsIsCaseSensitive(false),
+ bSubTotalsInsertPageBreaks(false),
+ bSubTotalsSortGroups(false),
+ bSubTotalsEnabledUserList(false),
+ bSubTotalsAscending(true),
+ bFilterConditionSourceRange(false),
+ bHasHeader(true),
+ bByRow(true),
+ meRangeType(ScDBCollection::GlobalNamed)
+{
+ if( rAttrList.is() )
+ {
+ for( auto &aIter : *rAttrList )
+ {
+ switch( aIter.getToken() )
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ {
+ sDatabaseRangeName = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_IS_SELECTION ):
+ {
+ bIsSelection = IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ON_UPDATE_KEEP_STYLES ):
+ {
+ bKeepFormats = IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ON_UPDATE_KEEP_SIZE ):
+ {
+ bMoveCells = !IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_HAS_PERSISTENT_DATA ):
+ {
+ bStripData = !IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ORIENTATION ):
+ {
+ bByRow = !IsXMLToken( aIter, XML_COLUMN );
+ mpQueryParam->bByRow = bByRow;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONTAINS_HEADER ):
+ {
+ bHasHeader = IsXMLToken( aIter, XML_TRUE );
+ mpQueryParam->bHasHeader = bHasHeader;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_FILTER_BUTTONS ):
+ {
+ bAutoFilter = IsXMLToken( aIter, XML_TRUE );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ sal_Int32 nOffset = 0;
+ if (!ScRangeStringConverter::GetRangeFromString(
+ maRange, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset))
+ mbValidRange = false;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_REFRESH_DELAY ):
+ {
+ double fTime;
+ if (::sax::Converter::convertDuration( fTime, aIter.toView() ))
+ nRefresh = std::max( static_cast<sal_Int32>(fTime * 86400.0), sal_Int32(0) );
+ }
+ break;
+ }
+ }
+ }
+
+ mpQueryParam->nTab = maRange.aStart.Tab();
+ mpQueryParam->nCol1 = maRange.aStart.Col();
+ mpQueryParam->nRow1 = maRange.aStart.Row();
+ mpQueryParam->nCol2 = maRange.aEnd.Col();
+ mpQueryParam->nRow2 = maRange.aEnd.Row();
+
+ if (sDatabaseRangeName.startsWith(STR_DB_LOCAL_NONAME))
+ meRangeType = ScDBCollection::SheetAnonymous;
+ else if (sDatabaseRangeName.startsWith(STR_DB_GLOBAL_NONAME))
+ meRangeType = ScDBCollection::GlobalAnonymous;
+}
+
+ScXMLDatabaseRangeContext::~ScXMLDatabaseRangeContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDatabaseRangeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_SQL ):
+ {
+ pContext = new ScXMLSourceSQLContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_TABLE ):
+ {
+ pContext = new ScXMLSourceTableContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATABASE_SOURCE_QUERY ):
+ {
+ pContext = new ScXMLSourceQueryContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER ):
+ {
+ pContext = new ScXMLFilterContext(
+ GetScImport(), pAttribList, *mpQueryParam, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SORT ):
+ {
+ bContainsSort = true;
+ pContext = new ScXMLSortContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SUBTOTAL_RULES ):
+ {
+ bContainsSubTotal = true;
+ pContext = new ScXMLSubTotalRulesContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+std::unique_ptr<ScDBData> ScXMLDatabaseRangeContext::ConvertToDBData(const OUString& rName)
+{
+ if (!mbValidRange)
+ return nullptr;
+
+ ScDocument* pDoc = GetScImport().GetDocument();
+
+ ::std::unique_ptr<ScDBData> pData(
+ new ScDBData(rName, maRange.aStart.Tab(), maRange.aStart.Col(), maRange.aStart.Row(), maRange.aEnd.Col(), maRange.aEnd.Row(), bByRow, bHasHeader));
+
+ pData->SetAutoFilter(bAutoFilter);
+ pData->SetKeepFmt(bKeepFormats);
+ pData->SetDoSize(bMoveCells);
+ pData->SetStripData(bStripData);
+
+ pDoc->PrepareQuery(mpQueryParam->nTab, *mpQueryParam);
+
+ pData->SetQueryParam(*mpQueryParam);
+
+ if (bFilterConditionSourceRange)
+ {
+ pData->SetAdvancedQuerySource( &aFilterConditionSourceRangeAddress );
+ }
+
+ {
+ ScImportParam aParam;
+ aParam.bNative = bNative;
+ aParam.aDBName = sDatabaseName.isEmpty() ? sConnectionResource : sDatabaseName;
+ aParam.aStatement = sSourceObject;
+ switch (nSourceType)
+ {
+ case sheet::DataImportMode_NONE:
+ aParam.bImport = false;
+ break;
+ case sheet::DataImportMode_SQL:
+ aParam.bImport = true;
+ aParam.bSql = true;
+ break;
+ case sheet::DataImportMode_TABLE:
+ aParam.bImport = true;
+ aParam.bSql = false;
+ aParam.nType = ScDbTable;
+ break;
+ case sheet::DataImportMode_QUERY:
+ aParam.bImport = true;
+ aParam.bSql = false;
+ aParam.nType = ScDbQuery;
+ break;
+ default:
+ OSL_FAIL("Unknown data import mode");
+ aParam.bImport = false;
+ }
+ pData->SetImportParam(aParam);
+ }
+
+ if (bContainsSort)
+ {
+ size_t nOldSize = aSortSequence.getLength();
+ aSortSequence.realloc(nOldSize + 1);
+ beans::PropertyValue aProperty;
+ aProperty.Name = SC_UNONAME_ORIENT;
+ table::TableOrientation eOrient = mpQueryParam->bByRow ?
+ table::TableOrientation_ROWS : table::TableOrientation_COLUMNS;
+ aProperty.Value <<= eOrient;
+ aSortSequence.getArray()[nOldSize] = aProperty;
+ ScSortParam aParam;
+ ScSortDescriptor::FillSortParam(aParam, aSortSequence);
+
+ SCCOLROW nStartPos = aParam.bByRow ? maRange.aStart.Col() : maRange.aStart.Row();
+ for (size_t i = 0; i < aParam.GetSortKeyCount(); ++i)
+ {
+ if (!aParam.maKeyState[i].bDoSort)
+ break;
+ aParam.maKeyState[i].nField += nStartPos;
+ }
+
+ pData->SetSortParam(aParam);
+ }
+
+ if (bContainsSubTotal)
+ {
+ ScSubTotalParam aParam;
+ aParam.bIncludePattern = bSubTotalsBindFormatsToContent;
+ aParam.bUserDef = bSubTotalsEnabledUserList;
+ aParam.nUserIndex = nSubTotalsUserListIndex;
+ aParam.bPagebreak = bSubTotalsInsertPageBreaks;
+ aParam.bCaseSens = bSubTotalsIsCaseSensitive;
+ aParam.bDoSort = bSubTotalsSortGroups;
+ aParam.bAscending = bSubTotalsAscending;
+ size_t nPos = 0;
+ for (const auto& rSubTotalRule : aSubTotalRules)
+ {
+ if (nPos >= MAXSUBTOTAL)
+ break;
+
+ const uno::Sequence<sheet::SubTotalColumn>& rColumns = rSubTotalRule.aSubTotalColumns;
+ sal_Int32 nColCount = rColumns.getLength();
+ sal_Int16 nGroupColumn = rSubTotalRule.nSubTotalRuleGroupFieldNumber;
+ aParam.bGroupActive[nPos] = true;
+ aParam.nField[nPos] = static_cast<SCCOL>(nGroupColumn);
+
+ SCCOL nCount = static_cast<SCCOL>(nColCount);
+ aParam.nSubTotals[nPos] = nCount;
+ if (nCount != 0)
+ {
+ aParam.pSubTotals[nPos].reset(new SCCOL[nCount]);
+ aParam.pFunctions[nPos].reset(new ScSubTotalFunc[nCount]);
+
+ const sheet::SubTotalColumn* pAry = rColumns.getConstArray();
+ for (SCCOL i = 0; i < nCount; ++i)
+ {
+ aParam.pSubTotals[nPos][i] = static_cast<SCCOL>(pAry[i].Column);
+ aParam.pFunctions[nPos][i] = ScDPUtil::toSubTotalFunc(static_cast<ScGeneralFunction>(pAry[i].Function));
+ }
+ }
+ else
+ {
+ aParam.pSubTotals[nPos].reset();
+ aParam.pFunctions[nPos].reset();
+ }
+ ++nPos;
+ }
+
+ pData->SetSubTotalParam(aParam);
+ }
+
+ if (pData->HasImportParam() && !pData->HasImportSelection())
+ {
+ pData->SetRefreshDelay(nRefresh);
+ pData->SetRefreshHandler(pDoc->GetDBCollection()->GetRefreshHandler());
+ pData->SetRefreshControl(&pDoc->GetRefreshTimerControlAddress());
+ }
+
+ return pData;
+}
+
+namespace {
+
+bool setAutoFilterFlags(ScDocument& rDoc, const ScDBData& rData)
+{
+ if (!rData.HasAutoFilter())
+ return false;
+
+ // Set autofilter flags so that the buttons get displayed.
+ ScRange aRange;
+ rData.GetArea(aRange);
+ rDoc.ApplyFlagsTab(
+ aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aStart.Row(),
+ aRange.aStart.Tab(), ScMF::Auto);
+ return false;
+}
+
+}
+
+void SAL_CALL ScXMLDatabaseRangeContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (!pDoc)
+ return;
+
+ if (meRangeType == ScDBCollection::SheetAnonymous)
+ {
+ ::std::unique_ptr<ScDBData> pData(ConvertToDBData(STR_DB_LOCAL_NONAME));
+
+ if (pData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+
+ setAutoFilterFlags(*pDoc, *pData);
+ pDoc->SetAnonymousDBData(aRange.aStart.Tab(), std::move(pData));
+ }
+ return;
+ }
+ else if (meRangeType == ScDBCollection::GlobalAnonymous)
+ {
+ ::std::unique_ptr<ScDBData> pData(ConvertToDBData(STR_DB_GLOBAL_NONAME));
+
+ if (pData)
+ {
+ ScRange aRange;
+ pData->GetArea(aRange);
+
+ if (setAutoFilterFlags(*pDoc, *pData))
+ pDoc->SetAnonymousDBData(aRange.aStart.Tab(), std::move(pData));
+ else
+ pDoc->GetDBCollection()->getAnonDBs().insert(pData.release());
+ }
+ return;
+ }
+ else if (meRangeType == ScDBCollection::GlobalNamed)
+ {
+ ::std::unique_ptr<ScDBData> pData(ConvertToDBData(sDatabaseRangeName));
+
+ if (pData)
+ {
+ setAutoFilterFlags(*pDoc, *pData);
+ (void)pDoc->GetDBCollection()->getNamedDBs().insert(std::move(pData));
+ }
+ }
+}
+
+ScXMLSourceSQLContext::ScXMLSourceSQLContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ sDBName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_SQL_STATEMENT ):
+ pDatabaseRangeContext->SetSourceObject(aIter.toString());
+ break;
+ case XML_ELEMENT( TABLE, XML_PARSE_SQL_STATEMENT ):
+ pDatabaseRangeContext->SetNative(IsXMLToken(aIter, XML_TRUE));
+ break;
+ }
+ }
+ }
+ pDatabaseRangeContext->SetSourceType(sheet::DataImportMode_SQL);
+}
+
+ScXMLSourceSQLContext::~ScXMLSourceSQLContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSourceSQLContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if ( nElement == XML_ELEMENT( FORM, XML_CONNECTION_RESOURCE ) && sDBName.isEmpty() )
+ {
+ pContext = new ScXMLConResContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSourceSQLContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!sDBName.isEmpty())
+ pDatabaseRangeContext->SetDatabaseName(sDBName);
+}
+
+ScXMLSourceTableContext::ScXMLSourceTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ sDBName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_NAME ):
+ case XML_ELEMENT( TABLE, XML_DATABASE_TABLE_NAME ):
+ pDatabaseRangeContext->SetSourceObject(aIter.toString());
+ break;
+ }
+ }
+ }
+ pDatabaseRangeContext->SetSourceType(sheet::DataImportMode_TABLE);
+}
+
+ScXMLSourceTableContext::~ScXMLSourceTableContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSourceTableContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if ( nElement == XML_ELEMENT( FORM, XML_CONNECTION_RESOURCE ) && sDBName.isEmpty() )
+ {
+ pContext = new ScXMLConResContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSourceTableContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!sDBName.isEmpty())
+ pDatabaseRangeContext->SetDatabaseName(sDBName);
+}
+
+ScXMLSourceQueryContext::ScXMLSourceQueryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATABASE_NAME ):
+ sDBName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_QUERY_NAME ):
+ pDatabaseRangeContext->SetSourceObject(aIter.toString());
+ break;
+ }
+ }
+ }
+ pDatabaseRangeContext->SetSourceType(sheet::DataImportMode_QUERY);
+}
+
+ScXMLSourceQueryContext::~ScXMLSourceQueryContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSourceQueryContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if ( nElement == XML_ELEMENT( FORM, XML_CONNECTION_RESOURCE ) && sDBName.isEmpty() )
+ {
+ pContext = new ScXMLConResContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSourceQueryContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!sDBName.isEmpty())
+ pDatabaseRangeContext->SetDatabaseName(sDBName);
+}
+
+ScXMLConResContext::ScXMLConResContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext) :
+ ScXMLImportContext( rImport )
+{
+ OUString sConRes;
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( XLINK, XML_HREF ) ) );
+ if (aIter != rAttrList->end())
+ sConRes = aIter.toString();
+ }
+ if (!sConRes.isEmpty())
+ pDatabaseRangeContext->SetConnectionResource(sConRes);
+}
+
+ScXMLConResContext::~ScXMLConResContext()
+{
+}
+
+ScXMLSubTotalRulesContext::ScXMLSubTotalRulesContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_BIND_STYLES_TO_CONTENT ):
+ pDatabaseRangeContext->SetSubTotalsBindFormatsToContent(IsXMLToken(aIter, XML_TRUE));
+ break;
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ pDatabaseRangeContext->SetSubTotalsIsCaseSensitive(IsXMLToken(aIter, XML_TRUE));
+ break;
+ case XML_ELEMENT( TABLE, XML_PAGE_BREAKS_ON_GROUP_CHANGE ):
+ pDatabaseRangeContext->SetSubTotalsInsertPageBreaks(IsXMLToken(aIter, XML_TRUE));
+ break;
+ }
+ }
+}
+
+ScXMLSubTotalRulesContext::~ScXMLSubTotalRulesContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSubTotalRulesContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_SORT_GROUPS ):
+ {
+ pContext = new ScXMLSortGroupsContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SUBTOTAL_RULE ):
+ {
+ pContext = new ScXMLSubTotalRuleContext( GetScImport(), pAttribList, pDatabaseRangeContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLSortGroupsContext::ScXMLSortGroupsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext) :
+ ScXMLImportContext( rImport )
+{
+ pDatabaseRangeContext->SetSubTotalsSortGroups(true);
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ {
+ const OUString &sValue = aIter.toString();
+ if (sValue.getLength() > 8)
+ {
+ std::u16string_view sTemp = sValue.subView(0, 8);
+ if (sTemp == u"UserList")
+ {
+ pDatabaseRangeContext->SetSubTotalsEnabledUserList(true);
+ sTemp = sValue.subView(8);
+ pDatabaseRangeContext->SetSubTotalsUserListIndex(static_cast<sal_Int16>(o3tl::toInt32(sTemp)));
+ }
+ else
+ {
+ //if (IsXMLToken(aIter, XML_AUTOMATIC))
+ //aSortField.FieldType = util::SortFieldType_AUTOMATIC;
+ // is not supported by StarOffice
+ }
+ }
+ else
+ {
+ //if (IsXMLToken(aIter, XML_TEXT))
+ //aSortField.FieldType = util::SortFieldType_ALPHANUMERIC;
+ // is not supported by StarOffice
+ //else if (IsXMLToken(aIter, XML_NUMBER))
+ //aSortField.FieldType = util::SortFieldType_NUMERIC;
+ // is not supported by StarOffice
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ORDER ):
+ {
+ if (IsXMLToken(aIter, XML_ASCENDING))
+ pDatabaseRangeContext->SetSubTotalsAscending(true);
+ else
+ pDatabaseRangeContext->SetSubTotalsAscending(false);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLSortGroupsContext::~ScXMLSortGroupsContext()
+{
+}
+
+ScXMLSubTotalRuleContext::ScXMLSubTotalRuleContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext)
+{
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_GROUP_BY_FIELD_NUMBER ):
+ aSubTotalRule.nSubTotalRuleGroupFieldNumber = static_cast<sal_Int16>(aIter.toInt32());
+ break;
+ }
+ }
+ }
+}
+
+ScXMLSubTotalRuleContext::~ScXMLSubTotalRuleContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSubTotalRuleContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_SUBTOTAL_FIELD ):
+ {
+ pContext = new ScXMLSubTotalFieldContext( GetScImport(), pAttribList, this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSubTotalRuleContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (pDatabaseRangeContext)
+ pDatabaseRangeContext->AddSubTotalRule(aSubTotalRule);
+}
+
+ScXMLSubTotalFieldContext::ScXMLSubTotalFieldContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLSubTotalRuleContext* pTempSubTotalRuleContext) :
+ ScXMLImportContext( rImport ),
+ pSubTotalRuleContext(pTempSubTotalRuleContext)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FIELD_NUMBER ):
+ sFieldNumber = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_FUNCTION ):
+ sFunction = aIter.toString();
+ break;
+ }
+ }
+}
+
+ScXMLSubTotalFieldContext::~ScXMLSubTotalFieldContext()
+{
+}
+
+void SAL_CALL ScXMLSubTotalFieldContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ sheet::SubTotalColumn aSubTotalColumn;
+ aSubTotalColumn.Column = sFieldNumber.toInt32();
+ aSubTotalColumn.Function = ScXMLConverter::GetFunctionFromString( sFunction );
+ pSubTotalRuleContext->AddSubTotalColumn(aSubTotalColumn);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmldrani.hxx b/sc/source/filter/xml/xmldrani.hxx
new file mode 100644
index 000000000..a35074117
--- /dev/null
+++ b/sc/source/filter/xml/xmldrani.hxx
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <com/sun/star/sheet/DataImportMode.hpp>
+#include <com/sun/star/sheet/SubTotalColumn.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <dbdata.hxx>
+#include "importcontext.hxx"
+
+#include <memory>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+struct ScQueryParam;
+
+class ScXMLDatabaseRangesContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLDatabaseRangesContext( ScXMLImport& rImport );
+
+ virtual ~ScXMLDatabaseRangesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+struct ScSubTotalRule
+{
+ sal_Int16 nSubTotalRuleGroupFieldNumber;
+ css::uno::Sequence <css::sheet::SubTotalColumn> aSubTotalColumns;
+};
+
+class ScXMLDatabaseRangeContext : public ScXMLImportContext
+{
+ std::unique_ptr<ScQueryParam> mpQueryParam;
+ ScRange maRange;
+ OUString sDatabaseRangeName;
+ OUString sConnectionResource;
+ OUString sDatabaseName;
+ OUString sSourceObject;
+ css::uno::Sequence <css::beans::PropertyValue> aSortSequence;
+ std::vector < ScSubTotalRule > aSubTotalRules;
+ ScRange aFilterConditionSourceRangeAddress;
+ css::sheet::DataImportMode nSourceType;
+ sal_Int32 nRefresh;
+ sal_Int16 nSubTotalsUserListIndex;
+ bool mbValidRange;
+ bool bContainsSort;
+ bool bContainsSubTotal;
+ bool bNative;
+ bool bIsSelection; // TODO: import to document core
+ bool bKeepFormats;
+ bool bMoveCells;
+ bool bStripData;
+ bool bAutoFilter;
+ bool bSubTotalsBindFormatsToContent;
+ bool bSubTotalsIsCaseSensitive;
+ bool bSubTotalsInsertPageBreaks;
+ bool bSubTotalsSortGroups;
+ bool bSubTotalsEnabledUserList;
+ bool bSubTotalsAscending;
+ bool bFilterConditionSourceRange;
+ bool bHasHeader;
+ bool bByRow;
+ ScDBCollection::RangeType meRangeType;
+
+ std::unique_ptr<ScDBData> ConvertToDBData(const OUString& rName);
+
+public:
+
+ ScXMLDatabaseRangeContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLDatabaseRangeContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetDatabaseName(const OUString& sTempDatabaseName) { sDatabaseName = sTempDatabaseName; }
+ void SetConnectionResource(const OUString& sTempConRes) { sConnectionResource = sTempConRes; }
+ void SetSourceObject(const OUString& sTempSourceObject) { sSourceObject = sTempSourceObject; }
+ void SetSourceType(const css::sheet::DataImportMode nTempSourceType) { nSourceType = nTempSourceType; }
+ void SetNative(const bool bTempNative) { bNative = bTempNative; }
+ void SetSubTotalsBindFormatsToContent(const bool bTemp ) { bSubTotalsBindFormatsToContent = bTemp; }
+ void SetSubTotalsIsCaseSensitive(const bool bTemp) { bSubTotalsIsCaseSensitive = bTemp; }
+ void SetSubTotalsInsertPageBreaks(const bool bTemp) { bSubTotalsInsertPageBreaks = bTemp; }
+ void SetSubTotalsEnabledUserList(const bool bTemp) { bSubTotalsEnabledUserList = bTemp; }
+ void SetSubTotalsUserListIndex(const sal_Int16 nTemp) { nSubTotalsUserListIndex = nTemp; }
+ void SetSubTotalsAscending(const bool bTemp) { bSubTotalsAscending = bTemp; }
+ void SetSubTotalsSortGroups(const bool bTemp) { bSubTotalsSortGroups = bTemp; }
+ void AddSubTotalRule(const ScSubTotalRule& rRule) { aSubTotalRules.push_back(rRule); }
+ void SetSortSequence(const css::uno::Sequence <css::beans::PropertyValue>& aTempSortSequence) { aSortSequence = aTempSortSequence; }
+ void SetFilterConditionSourceRangeAddress(const ScRange& aRange) { aFilterConditionSourceRangeAddress = aRange;
+ bFilterConditionSourceRange = true; }
+};
+
+class ScXMLSourceSQLContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+ OUString sDBName;
+
+public:
+
+ ScXMLSourceSQLContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSourceSQLContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLSourceTableContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+ OUString sDBName;
+
+public:
+
+ ScXMLSourceTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSourceTableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLSourceQueryContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+ OUString sDBName;
+
+public:
+
+ ScXMLSourceQueryContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSourceQueryContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLConResContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLConResContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLConResContext() override;
+};
+
+class ScXMLSubTotalRulesContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+
+public:
+
+ ScXMLSubTotalRulesContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSubTotalRulesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLSortGroupsContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLSortGroupsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSortGroupsContext() override;
+};
+
+class ScXMLSubTotalRuleContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+ ScSubTotalRule aSubTotalRule;
+
+public:
+
+ ScXMLSubTotalRuleContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSubTotalRuleContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void AddSubTotalColumn(const css::sheet::SubTotalColumn& rSubTotalColumn)
+ {
+ aSubTotalRule.aSubTotalColumns.realloc(aSubTotalRule.aSubTotalColumns.getLength() + 1);
+ aSubTotalRule.aSubTotalColumns.getArray()[aSubTotalRule.aSubTotalColumns.getLength() - 1] = rSubTotalColumn;
+ }
+};
+
+class ScXMLSubTotalFieldContext : public ScXMLImportContext
+{
+ ScXMLSubTotalRuleContext* pSubTotalRuleContext;
+ OUString sFieldNumber;
+ OUString sFunction;
+
+public:
+
+ ScXMLSubTotalFieldContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLSubTotalRuleContext* pSubTotalRuleContext);
+
+ virtual ~ScXMLSubTotalFieldContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlexprt.cxx b/sc/source/filter/xml/xmlexprt.cxx
new file mode 100644
index 000000000..37b155bf9
--- /dev/null
+++ b/sc/source/filter/xml/xmlexprt.cxx
@@ -0,0 +1,5454 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include "xmlexprt.hxx"
+#include "XMLConverter.hxx"
+#include "xmlstyle.hxx"
+#include <unonames.hxx>
+#include <document.hxx>
+#include <olinetab.hxx>
+#include <formulacell.hxx>
+#include <rangenam.hxx>
+#include "XMLTableMasterPageExport.hxx"
+#include <drwlayer.hxx>
+#include "XMLExportDataPilot.hxx"
+#include "XMLExportDatabaseRanges.hxx"
+#include "XMLExportDDELinks.hxx"
+#include "XMLExportIterator.hxx"
+#include "XMLColumnRowGroupExport.hxx"
+#include "XMLStylesExportHelper.hxx"
+#include "XMLChangeTrackingExportHelper.hxx"
+#include <sheetdata.hxx>
+#include <docoptio.hxx>
+#include "XMLExportSharedData.hxx"
+#include <chgviset.hxx>
+#include <docuno.hxx>
+#include <textuno.hxx>
+#include <chartlis.hxx>
+#include <scitems.hxx>
+#include <docpool.hxx>
+#include <userdat.hxx>
+#include <chgtrack.hxx>
+#include <rangeutl.hxx>
+#include <postit.hxx>
+#include <externalrefmgr.hxx>
+#include <editutil.hxx>
+#include <tabprotection.hxx>
+#include "cachedattraccess.hxx"
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <cellvalue.hxx>
+#include <stylehelper.hxx>
+#include <edittextiterator.hxx>
+#include "editattributemap.hxx"
+#include <arealink.hxx>
+#include <datastream.hxx>
+#include <documentlinkmgr.hxx>
+#include <tokenstringcontext.hxx>
+#include <cellform.hxx>
+#include <datamapper.hxx>
+#include <datatransformation.hxx>
+#include "SparklineGroupsExport.hxx"
+#include <SparklineList.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/numehelp.hxx>
+#include <xmloff/txtparae.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/section.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/eeitem.hxx>
+#include <formula/errorcodes.hxx>
+#include <xmloff/xmlerror.hxx>
+#include <xmloff/XMLEventExport.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include <xmloff/ProgressBarHelper.hxx>
+#include <xmloff/table/XMLTableExport.hxx>
+
+#include <sax/tools/converter.hxx>
+#include <tools/fldunit.hxx>
+
+#include <rtl/ustring.hxx>
+
+#include <tools/color.hxx>
+#include <tools/diagnose_ex.h>
+#include <rtl/math.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <comphelper/base64.hxx>
+#include <comphelper/extract.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdocapt.hxx>
+#include <vcl/svapp.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/form/XFormsSupplier2.hpp>
+#include <com/sun/star/io/XActiveDataSource.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/sheet/XUsedAreaCursor.hpp>
+#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <com/sun/star/sheet/XUniqueCellFormatRangesSupplier.hpp>
+#include <com/sun/star/sheet/XLabelRange.hpp>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/sheet/XSheetCellCursor.hpp>
+#include <com/sun/star/sheet/XSheetCellRanges.hpp>
+#include <com/sun/star/sheet/XSheetLinkable.hpp>
+#include <com/sun/star/sheet/GlobalSheetSettings.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/util/XProtectable.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+
+#include "XMLCodeNameProvider.hxx"
+
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <memory>
+#include <vector>
+#include <vbahelper/vbaaccesshelper.hxx>
+#include <officecfg/Office/Common.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+
+
+//! not found in unonames.hxx
+constexpr OUStringLiteral SC_LAYERID = u"LayerID";
+
+#define SC_VIEWCHANGES_COUNT 13
+#define SC_SHOW_CHANGES 0
+#define SC_SHOW_ACCEPTED_CHANGES 1
+#define SC_SHOW_REJECTED_CHANGES 2
+#define SC_SHOW_CHANGES_BY_DATETIME 3
+#define SC_SHOW_CHANGES_BY_DATETIME_MODE 4
+#define SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME 5
+#define SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME 6
+#define SC_SHOW_CHANGES_BY_AUTHOR 7
+#define SC_SHOW_CHANGES_BY_AUTHOR_NAME 8
+#define SC_SHOW_CHANGES_BY_COMMENT 9
+#define SC_SHOW_CHANGES_BY_COMMENT_TEXT 10
+#define SC_SHOW_CHANGES_BY_RANGES 11
+#define SC_SHOW_CHANGES_BY_RANGES_LIST 12
+
+using namespace formula;
+using namespace com::sun::star;
+using namespace xmloff::token;
+using ::std::vector;
+using ::com::sun::star::uno::UNO_QUERY;
+
+namespace
+{
+OUString lcl_RangeSequenceToString(
+ const uno::Sequence< OUString > & rRanges,
+ const uno::Reference< chart2::data::XRangeXMLConversion > & xFormatConverter )
+{
+ OUStringBuffer aResult;
+ const sal_Int32 nMaxIndex( rRanges.getLength() - 1 );
+ const sal_Unicode cSep(' ');
+ for( sal_Int32 i=0; i<=nMaxIndex; ++i )
+ {
+ OUString aRange( rRanges[i] );
+ if( xFormatConverter.is())
+ aRange = xFormatConverter->convertRangeToXML( aRange );
+ aResult.append( aRange );
+ if( i < nMaxIndex )
+ aResult.append( cSep );
+ }
+ return aResult.makeStringAndClear();
+}
+
+OUString lcl_GetFormattedString(ScDocument* pDoc, const ScRefCellValue& rCell, const ScAddress& rAddr)
+{
+ // return text/edit cell string content, with line feeds in edit cells
+
+ if (!pDoc)
+ return OUString();
+
+ switch (rCell.meType)
+ {
+ case CELLTYPE_STRING:
+ {
+ const Color* pColor;
+ SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+
+ sal_uInt32 nFormat = pDoc->GetNumberFormat(rAddr);
+ return ScCellFormat::GetString(rCell, nFormat, &pColor, *pFormatter, *pDoc);
+ }
+ case CELLTYPE_EDIT:
+ {
+ const EditTextObject* pData = rCell.mpEditText;
+ if (!pData)
+ return OUString();
+
+ EditEngine& rEngine = pDoc->GetEditEngine();
+ rEngine.SetText(*pData);
+ return rEngine.GetText();
+ }
+ break;
+ default:
+ ;
+ }
+
+ return OUString();
+}
+
+} // anonymous namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLExporter", SvXMLExportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLMetaExporter", SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLStylesExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLStylesExporter", SvXMLExportFlags::STYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::FONTDECLS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLContentExporter", SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::CONTENT|SvXMLExportFlags::SCRIPTS|SvXMLExportFlags::FONTDECLS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLSettingsExporter", SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisExporter", SvXMLExportFlags::ALL|SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisMetaExporter", SvXMLExportFlags::META|SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisStylesExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisStylesExporter", SvXMLExportFlags::STYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisContentExporter", SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::CONTENT|SvXMLExportFlags::SCRIPTS|SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::OASIS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ScXMLExport(context, "com.sun.star.comp.Calc.XMLOasisSettingsExporter", SvXMLExportFlags::SETTINGS|SvXMLExportFlags::OASIS));
+}
+
+namespace {
+
+class ScXMLShapeExport : public XMLShapeExport
+{
+public:
+ explicit ScXMLShapeExport(SvXMLExport& rExp)
+ : XMLShapeExport(rExp,
+ // chain text attributes
+ XMLTextParagraphExport::CreateParaExtPropMapper(rExp))
+ {
+ }
+
+ /** is called before a shape element for the given XShape is exported */
+ virtual void onExport( const uno::Reference < drawing::XShape >& xShape ) override;
+};
+
+}
+
+void ScXMLShapeExport::onExport( const uno::Reference < drawing::XShape >& xShape )
+{
+ uno::Reference< beans::XPropertySet > xShapeProp( xShape, uno::UNO_QUERY );
+ if( xShapeProp.is() )
+ {
+ sal_Int16 nLayerID = 0;
+ if( (xShapeProp->getPropertyValue( SC_LAYERID ) >>= nLayerID) && (SdrLayerID(nLayerID) == SC_LAYER_BACK) )
+ GetExport().AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_BACKGROUND, XML_TRUE);
+ }
+}
+
+sal_Int16 ScXMLExport::GetMeasureUnit()
+{
+ css::uno::Reference<css::sheet::XGlobalSheetSettings> xProperties =
+ css::sheet::GlobalSheetSettings::create( comphelper::getProcessComponentContext() );
+ const FieldUnit eFieldUnit = static_cast<FieldUnit>(xProperties->getMetric());
+ return SvXMLUnitConverter::GetMeasureUnit(eFieldUnit);
+}
+
+ScXMLExport::ScXMLExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlag)
+: SvXMLExport(
+ rContext, implementationName, GetMeasureUnit(), XML_SPREADSHEET, nExportFlag ),
+ pDoc(nullptr),
+ nSourceStreamPos(0),
+ pCurrentCell(nullptr),
+ nOpenRow(-1),
+ nProgressCount(0),
+ nCurrentTable(0),
+ bHasRowHeader(false),
+ bRowHeaderOpen(false)
+{
+ if (getExportFlags() & SvXMLExportFlags::CONTENT)
+ {
+ pGroupColumns.reset( new ScMyOpenCloseColumnRowGroup(*this, XML_TABLE_COLUMN_GROUP) );
+ pGroupRows.reset( new ScMyOpenCloseColumnRowGroup(*this, XML_TABLE_ROW_GROUP) );
+ pColumnStyles.reset( new ScColumnStyles() );
+ pRowStyles.reset( new ScRowStyles() );
+ pRowFormatRanges.reset( new ScRowFormatRanges() );
+ pMergedRangesContainer.reset( new ScMyMergedRangesContainer() );
+ pValidationsContainer.reset( new ScMyValidationsContainer() );
+ mpCellsItr.reset(new ScMyNotEmptyCellsIterator(*this));
+ pDefaults.reset( new ScMyDefaultStyles );
+ }
+ pCellStyles.reset( new ScFormatRangeStyles() );
+
+ // document is not set here - create ScChangeTrackingExportHelper later
+
+ xScPropHdlFactory = new XMLScPropHdlFactory;
+ xCellStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScCellStylesProperties, xScPropHdlFactory, true);
+ xColumnStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScColumnStylesProperties, xScPropHdlFactory, true);
+ xRowStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScRowStylesProperties, xScPropHdlFactory, true);
+ xTableStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScTableStylesProperties, xScPropHdlFactory, true);
+ xCellStylesExportPropertySetMapper = new ScXMLCellExportPropertyMapper(xCellStylesPropertySetMapper);
+ xCellStylesExportPropertySetMapper->ChainExportMapper(XMLTextParagraphExport::CreateParaExtPropMapper(*this));
+ xColumnStylesExportPropertySetMapper = new ScXMLColumnExportPropertyMapper(xColumnStylesPropertySetMapper);
+ xRowStylesExportPropertySetMapper = new ScXMLRowExportPropertyMapper(xRowStylesPropertySetMapper);
+ xTableStylesExportPropertySetMapper = new ScXMLTableExportPropertyMapper(xTableStylesPropertySetMapper);
+
+ GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_CELL, XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME,
+ xCellStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX);
+ GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_COLUMN, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_NAME,
+ xColumnStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_PREFIX);
+ GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_ROW, XML_STYLE_FAMILY_TABLE_ROW_STYLES_NAME,
+ xRowStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_ROW_STYLES_PREFIX);
+ GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_TABLE, XML_STYLE_FAMILY_TABLE_TABLE_STYLES_NAME,
+ xTableStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_TABLE_STYLES_PREFIX);
+
+ if( !(getExportFlags() & (SvXMLExportFlags::STYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT)) )
+ return;
+
+ // This name is reserved for the external ref cache tables. This
+ // should not conflict with user-defined styles since this name is
+ // used for a table style which is not available in the UI.
+ sExternalRefTabStyleName = "ta_extref";
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_TABLE, sExternalRefTabStyleName);
+
+ sAttrName = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_NAME));
+ sAttrStyleName = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_STYLE_NAME));
+ sAttrColumnsRepeated = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_NUMBER_COLUMNS_REPEATED));
+ sAttrFormula = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_FORMULA));
+ sAttrStringValue = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_OFFICE, GetXMLToken(XML_STRING_VALUE));
+ sAttrValueType = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_OFFICE, GetXMLToken(XML_VALUE_TYPE));
+ sElemCell = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_CELL));
+ sElemCoveredCell = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_COVERED_TABLE_CELL));
+ sElemCol = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_COLUMN));
+ sElemRow = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_ROW));
+ sElemTab = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE));
+ sElemP = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
+}
+
+ScXMLExport::~ScXMLExport()
+{
+ pGroupColumns.reset();
+ pGroupRows.reset();
+ pColumnStyles.reset();
+ pRowStyles.reset();
+ pCellStyles.reset();
+ pRowFormatRanges.reset();
+ pMergedRangesContainer.reset();
+ pValidationsContainer.reset();
+ pChangeTrackingExportHelper.reset();
+ pDefaults.reset();
+ pNumberFormatAttributesExportHelper.reset();
+}
+
+void ScXMLExport::SetSourceStream( const uno::Reference<io::XInputStream>& xNewStream )
+{
+ xSourceStream = xNewStream;
+
+ if ( !xSourceStream.is() )
+ return;
+
+ // make sure it's a plain UTF-8 stream as written by OOo itself
+
+ const char pXmlHeader[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ sal_Int32 nLen = strlen(pXmlHeader);
+
+ uno::Sequence<sal_Int8> aFileStart(nLen);
+ sal_Int32 nRead = xSourceStream->readBytes( aFileStart, nLen );
+
+ if ( nRead != nLen || memcmp( aFileStart.getConstArray(), pXmlHeader, nLen ) != 0 )
+ {
+ // invalid - ignore stream, save normally
+ xSourceStream.clear();
+ }
+ else
+ {
+ // keep track of the bytes already read
+ nSourceStreamPos = nRead;
+
+ const ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->GetSheetSaveData();
+ if (pSheetData)
+ {
+ // add the loaded namespaces to the name space map
+
+ if ( !pSheetData->AddLoadedNamespaces( GetNamespaceMap_() ) )
+ {
+ // conflicts in the namespaces - ignore the stream, save normally
+ xSourceStream.clear();
+ }
+ }
+ }
+}
+
+sal_Int32 ScXMLExport::GetNumberFormatStyleIndex(sal_Int32 nNumFmt) const
+{
+ NumberFormatIndexMap::const_iterator itr = aNumFmtIndexMap.find(nNumFmt);
+ if (itr == aNumFmtIndexMap.end())
+ return -1;
+
+ return itr->second;
+}
+
+void ScXMLExport::CollectSharedData(SCTAB& nTableCount, sal_Int32& nShapesCount)
+{
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc(GetModel(), uno::UNO_QUERY);
+ if (!xSpreadDoc.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xIndex(xSpreadDoc->getSheets(), uno::UNO_QUERY);
+ if (!xIndex.is())
+ return;
+
+ nTableCount = xIndex->getCount();
+ if (!pSharedData)
+ pSharedData.reset(new ScMySharedData(nTableCount));
+
+ pCellStyles->AddNewTable(nTableCount - 1);
+
+ for (SCTAB nTable = 0; nTable < nTableCount; ++nTable)
+ {
+ nCurrentTable = sal::static_int_cast<sal_uInt16>(nTable);
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ if (!xDrawPageSupplier.is())
+ continue;
+
+ uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage());
+ ScMyDrawPage aDrawPage;
+ aDrawPage.bHasForms = false;
+ aDrawPage.xDrawPage.set(xDrawPage);
+ pSharedData->AddDrawPage(aDrawPage, nTable);
+ if (!xDrawPage.is())
+ continue;
+
+ sal_Int32 nShapes = xDrawPage->getCount();
+ for (sal_Int32 nShape = 0; nShape < nShapes; ++nShape)
+ {
+ uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShape), uno::UNO_QUERY);
+ if (!xShape.is())
+ continue;
+
+ uno::Reference<beans::XPropertySet> xShapeProp(xShape, uno::UNO_QUERY);
+ if (!xShapeProp.is())
+ continue;
+
+ sal_Int16 nLayerID = 0;
+ bool bExtracted = xShapeProp->getPropertyValue(SC_LAYERID) >>= nLayerID;
+ if (!bExtracted)
+ continue;
+
+ if ((SdrLayerID(nLayerID) == SC_LAYER_INTERN) || (SdrLayerID(nLayerID) == SC_LAYER_HIDDEN))
+ {
+ CollectInternalShape(xShape);
+ continue;
+ }
+
+ ++nShapesCount;
+
+ SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape);
+ if (!pSdrObj)
+ continue;
+
+ if (ScDrawObjData *pAnchor = ScDrawLayer::GetNonRotatedObjData(pSdrObj))
+ {
+ ScMyShape aMyShape;
+ aMyShape.aAddress = pAnchor->maStart;
+ SAL_WARN_IF(aMyShape.aAddress.Tab() != nTable, "sc", "not anchored to current sheet!");
+ aMyShape.aAddress.SetTab(nTable);
+ aMyShape.aEndAddress = pAnchor->maEnd;
+ aMyShape.aEndAddress.SetTab( nTable );
+ aMyShape.nEndX = pAnchor->maEndOffset.X();
+ aMyShape.nEndY = pAnchor->maEndOffset.Y();
+ aMyShape.xShape = xShape;
+ aMyShape.bResizeWithCell = ScDrawLayer::IsResizeWithCell(*pSdrObj);
+ pSharedData->AddNewShape(aMyShape);
+ pSharedData->SetLastColumn(nTable, pAnchor->maStart.Col());
+ pSharedData->SetLastRow(nTable, pAnchor->maStart.Row());
+ }
+ else
+ pSharedData->AddTableShape(nTable, xShape);
+ }
+ }
+}
+
+void ScXMLExport::CollectShapesAutoStyles(SCTAB nTableCount)
+{
+ // #i84077# To avoid compiler warnings about uninitialized aShapeItr,
+ // it's initialized using this dummy list. The iterator contains shapes
+ // from all sheets, so it can't be declared inside the nTable loop where
+ // it is used.
+ ScMyShapeList aDummyInitList;
+
+ pSharedData->SortShapesContainer();
+ pSharedData->SortNoteShapes();
+ const ScMyShapeList* pShapeList(nullptr);
+ ScMyShapeList::const_iterator aShapeItr = aDummyInitList.end();
+ if (pSharedData->GetShapesContainer())
+ {
+ pShapeList = &pSharedData->GetShapesContainer()->GetShapes();
+ aShapeItr = pShapeList->begin();
+ }
+ if (pSharedData->HasDrawPage())
+ {
+ for (SCTAB nTable = 0; nTable < nTableCount; ++nTable)
+ {
+ uno::Reference<drawing::XDrawPage> xDrawPage(pSharedData->GetDrawPage(nTable));
+
+ if (xDrawPage.is())
+ {
+ GetShapeExport()->seekShapes(xDrawPage);
+ uno::Reference< form::XFormsSupplier2 > xFormsSupplier( xDrawPage, uno::UNO_QUERY );
+ if( xFormsSupplier.is() && xFormsSupplier->hasForms() )
+ {
+ GetFormExport()->examineForms(xDrawPage);
+ pSharedData->SetDrawPageHasForms(nTable, true);
+ }
+ ScMyTableShapes* pTableShapes(pSharedData->GetTableShapes());
+ if (pTableShapes)
+ {
+ for (const auto& rxShape : (*pTableShapes)[nTable])
+ {
+ GetShapeExport()->collectShapeAutoStyles(rxShape);
+ IncrementProgressBar(false);
+ }
+ }
+ if (pShapeList)
+ {
+ ScMyShapeList::const_iterator aEndItr(pShapeList->end());
+ while ( aShapeItr != aEndItr && ( aShapeItr->aAddress.Tab() == nTable ) )
+ {
+ GetShapeExport()->collectShapeAutoStyles(aShapeItr->xShape);
+ IncrementProgressBar(false);
+ ++aShapeItr;
+ }
+ }
+ if (pSharedData->GetNoteShapes())
+ {
+ const ScMyNoteShapeList& rNoteShapes = pSharedData->GetNoteShapes()->GetNotes();
+ for (const auto& rNoteShape : rNoteShapes)
+ {
+ if ( rNoteShape.aPos.Tab() == nTable )
+ GetShapeExport()->collectShapeAutoStyles(rNoteShape.xShape);
+ }
+ }
+ }
+ }
+ }
+ pSharedData->SortNoteShapes(); // sort twice, because some more shapes are added
+}
+
+void ScXMLExport::ExportMeta_()
+{
+ sal_Int32 nCellCount(pDoc ? pDoc->GetCellCount() : 0);
+ SCTAB nTableCount(0);
+ sal_Int32 nShapesCount(0);
+ GetAutoStylePool()->ClearEntries();
+ CollectSharedData(nTableCount, nShapesCount);
+
+ uno::Sequence<beans::NamedValue> stats
+ {
+ { "TableCount", uno::Any(static_cast<sal_Int32>(nTableCount)) },
+ { "CellCount", uno::Any(nCellCount) },
+ { "ObjectCount", uno::Any(nShapesCount) }
+ };
+
+ // update document statistics at the model
+ uno::Reference<document::XDocumentPropertiesSupplier> xPropSup(GetModel(),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xPropSup->getDocumentProperties());
+ if (xDocProps.is()) {
+ xDocProps->setDocumentStatistics(stats);
+ }
+
+ // export document properties
+ SvXMLExport::ExportMeta_();
+}
+
+void ScXMLExport::ExportFontDecls_()
+{
+ GetFontAutoStylePool(); // make sure the pool is created
+ SvXMLExport::ExportFontDecls_();
+}
+
+table::CellRangeAddress ScXMLExport::GetEndAddress(const uno::Reference<sheet::XSpreadsheet>& xTable)
+{
+ table::CellRangeAddress aCellAddress;
+ uno::Reference<sheet::XSheetCellCursor> xCursor(xTable->createCursor());
+ uno::Reference<sheet::XUsedAreaCursor> xUsedArea (xCursor, uno::UNO_QUERY);
+ uno::Reference<sheet::XCellRangeAddressable> xCellAddress (xCursor, uno::UNO_QUERY);
+ if (xUsedArea.is() && xCellAddress.is())
+ {
+ xUsedArea->gotoEndOfUsedArea(true);
+ aCellAddress = xCellAddress->getRangeAddress();
+ }
+ return aCellAddress;
+}
+
+void ScXMLExport::GetAreaLinks( ScMyAreaLinksContainer& rAreaLinks )
+{
+ if (pDoc->GetLinkManager())
+ {
+ const sfx2::SvBaseLinks& rLinks = pDoc->GetLinkManager()->GetLinks();
+ for (const auto & rLink : rLinks)
+ {
+ ScAreaLink *pLink = dynamic_cast<ScAreaLink*>(rLink.get());
+ if (pLink)
+ {
+ ScMyAreaLink aAreaLink;
+ aAreaLink.aDestRange = pLink->GetDestArea();
+ aAreaLink.sSourceStr = pLink->GetSource();
+ aAreaLink.sFilter = pLink->GetFilter();
+ aAreaLink.sFilterOptions = pLink->GetOptions();
+ aAreaLink.sURL = pLink->GetFile();
+ aAreaLink.nRefreshDelaySeconds = pLink->GetRefreshDelaySeconds();
+ rAreaLinks.AddNewAreaLink( aAreaLink );
+ }
+ }
+ }
+ rAreaLinks.Sort();
+}
+
+// core implementation
+void ScXMLExport::GetDetectiveOpList( ScMyDetectiveOpContainer& rDetOp )
+{
+ if (!pDoc)
+ return;
+
+ ScDetOpList* pOpList(pDoc->GetDetOpList());
+ if( !pOpList )
+ return;
+
+ size_t nCount = pOpList->Count();
+ for (size_t nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ const ScDetOpData& rDetData = pOpList->GetObject( nIndex);
+ const ScAddress& rDetPos = rDetData.GetPos();
+ SCTAB nTab = rDetPos.Tab();
+ if ( nTab < pDoc->GetTableCount() )
+ {
+ rDetOp.AddOperation( rDetData.GetOperation(), rDetPos, static_cast<sal_uInt32>( nIndex) );
+
+ // cells with detective operations are written even if empty
+ pSharedData->SetLastColumn( nTab, rDetPos.Col() );
+ pSharedData->SetLastRow( nTab, rDetPos.Row() );
+ }
+ }
+ rDetOp.Sort();
+}
+
+void ScXMLExport::WriteSingleColumn(const sal_Int32 nRepeatColumns, const sal_Int32 nStyleIndex,
+ const sal_Int32 nIndex, const bool bIsAutoStyle, const bool bIsVisible)
+{
+ CheckAttrList();
+ // tdf#138466
+ if (nStyleIndex != -1)
+ AddAttribute(sAttrStyleName, pColumnStyles->GetStyleNameByIndex(nStyleIndex));
+ if (!bIsVisible)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_COLLAPSE);
+ if (nRepeatColumns > 1)
+ {
+ OUString sOUEndCol(OUString::number(nRepeatColumns));
+ AddAttribute(sAttrColumnsRepeated, sOUEndCol);
+ }
+ if (nIndex != -1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_DEFAULT_CELL_STYLE_NAME, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
+ SvXMLElementExport aElemC(*this, sElemCol, true, true);
+}
+
+void ScXMLExport::WriteColumn(const sal_Int32 nColumn, const sal_Int32 nRepeatColumns,
+ const sal_Int32 nStyleIndex, const bool bIsVisible)
+{
+ sal_Int32 nRepeat(1);
+ sal_Int32 nPrevIndex(pDefaults->GetColDefaults()[nColumn].nIndex);
+ bool bPrevAutoStyle(pDefaults->GetColDefaults()[nColumn].bIsAutoStyle);
+ for (sal_Int32 i = nColumn + 1; i < nColumn + nRepeatColumns; ++i)
+ {
+ if ((pDefaults->GetColDefaults()[i].nIndex != nPrevIndex) ||
+ (pDefaults->GetColDefaults()[i].bIsAutoStyle != bPrevAutoStyle))
+ {
+ WriteSingleColumn(nRepeat, nStyleIndex, nPrevIndex, bPrevAutoStyle, bIsVisible);
+ nPrevIndex = pDefaults->GetColDefaults()[i].nIndex;
+ bPrevAutoStyle = pDefaults->GetColDefaults()[i].bIsAutoStyle;
+ nRepeat = 1;
+ }
+ else
+ ++nRepeat;
+ }
+ WriteSingleColumn(nRepeat, nStyleIndex, nPrevIndex, bPrevAutoStyle, bIsVisible);
+}
+
+void ScXMLExport::OpenHeaderColumn()
+{
+ StartElement( XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true );
+}
+
+void ScXMLExport::CloseHeaderColumn()
+{
+ EndElement(XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true);
+}
+
+void ScXMLExport::ExportColumns(const sal_Int32 nTable, const ScRange& aColumnHeaderRange, const bool bHasColumnHeader)
+{
+ sal_Int32 nColsRepeated (1);
+ sal_Int32 nIndex;
+ sal_Int32 nPrevColumn(0);
+ bool bPrevIsVisible (true);
+ bool bWasHeader (false);
+ bool bIsClosed (true);
+ sal_Int32 nPrevIndex (-1);
+ sal_Int32 nColumn;
+ for (nColumn = 0; nColumn <= pSharedData->GetLastColumn(nTable); ++nColumn)
+ {
+ CheckAttrList();
+ bool bIsVisible(true);
+ nIndex = pColumnStyles->GetStyleNameIndex(nTable, nColumn, bIsVisible);
+
+ const bool bIsHeader = bHasColumnHeader && (aColumnHeaderRange.aStart.Col() <= nColumn) && (nColumn <= aColumnHeaderRange.aEnd.Col());
+ if (bIsHeader != bWasHeader)
+ {
+ if (bIsHeader)
+ {
+ if (nColumn > 0)
+ {
+ WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
+ if (pGroupColumns->IsGroupEnd(nColumn - 1))
+ pGroupColumns->CloseGroups(nColumn - 1);
+ }
+ bPrevIsVisible = bIsVisible;
+ nPrevIndex = nIndex;
+ nPrevColumn = nColumn;
+ nColsRepeated = 1;
+ if(pGroupColumns->IsGroupStart(nColumn))
+ pGroupColumns->OpenGroups(nColumn);
+ OpenHeaderColumn();
+ bWasHeader = true;
+ bIsClosed = false;
+ }
+ else
+ {
+ WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
+ CloseHeaderColumn();
+ if (pGroupColumns->IsGroupEnd(nColumn - 1))
+ pGroupColumns->CloseGroups(nColumn - 1);
+ if(pGroupColumns->IsGroupStart(nColumn))
+ pGroupColumns->OpenGroups(nColumn);
+ bPrevIsVisible = bIsVisible;
+ nPrevIndex = nIndex;
+ nPrevColumn = nColumn;
+ nColsRepeated = 1;
+ bWasHeader = false;
+ bIsClosed = true;
+ }
+ }
+ else if (nColumn == 0)
+ {
+ if (pGroupColumns->IsGroupStart(nColumn))
+ pGroupColumns->OpenGroups(nColumn);
+ bPrevIsVisible = bIsVisible;
+ nPrevIndex = nIndex;
+ }
+ else if ((bIsVisible == bPrevIsVisible) && (nIndex == nPrevIndex) &&
+ !pGroupColumns->IsGroupStart(nColumn) && !pGroupColumns->IsGroupEnd(nColumn - 1))
+ ++nColsRepeated;
+ else
+ {
+ WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
+ if (pGroupColumns->IsGroupEnd(nColumn - 1))
+ {
+ if (bIsHeader)
+ CloseHeaderColumn();
+ pGroupColumns->CloseGroups(nColumn - 1);
+ if (bIsHeader)
+ OpenHeaderColumn();
+ }
+ if (pGroupColumns->IsGroupStart(nColumn))
+ {
+ if (bIsHeader)
+ CloseHeaderColumn();
+ pGroupColumns->OpenGroups(nColumn);
+ if (bIsHeader)
+ OpenHeaderColumn();
+ }
+ bPrevIsVisible = bIsVisible;
+ nPrevIndex = nIndex;
+ nPrevColumn = nColumn;
+ nColsRepeated = 1;
+ }
+ }
+ WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
+ if (!bIsClosed)
+ CloseHeaderColumn();
+ if (pGroupColumns->IsGroupEnd(nColumn - 1))
+ pGroupColumns->CloseGroups(nColumn - 1);
+}
+
+void ScXMLExport::ExportExternalRefCacheStyles()
+{
+ sal_Int32 nEntryIndex = GetCellStylesPropertySetMapper()->FindEntryIndex(
+ "NumberFormat", XML_NAMESPACE_STYLE, u"data-style-name");
+
+ if (nEntryIndex < 0)
+ // No entry index for the number format is found.
+ return;
+
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ if (!pRefMgr->hasExternalData())
+ // No external reference data cached.
+ return;
+
+ // Export each unique number format used in the external ref cache.
+ vector<sal_uInt32> aNumFmts;
+ pRefMgr->getAllCachedNumberFormats(aNumFmts);
+ const OUString aDefaultStyle = OUString("Default").intern();
+ for (const auto& rNumFmt : aNumFmts)
+ {
+ sal_Int32 nNumFmt = static_cast<sal_Int32>(rNumFmt);
+
+ addDataStyle(nNumFmt);
+
+ uno::Any aVal;
+ aVal <<= nNumFmt;
+ vector<XMLPropertyState> aProps;
+ aVal <<= aDefaultStyle;
+ aProps.emplace_back(nEntryIndex, aVal);
+
+ OUString aName;
+ sal_Int32 nIndex;
+ if (GetAutoStylePool()->Add(aName, XmlStyleFamily::TABLE_CELL, aDefaultStyle, std::move(aProps)))
+ {
+ pCellStyles->AddStyleName(aName, nIndex);
+ }
+ else
+ {
+ bool bIsAuto;
+ nIndex = pCellStyles->GetIndexOfStyleName(
+ aName, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX, bIsAuto);
+ }
+
+ // store the number format to index mapping for later use.
+ aNumFmtIndexMap.emplace(nNumFmt, nIndex);
+ }
+}
+
+namespace {
+
+void handleFont(
+ SvXMLExport & rExport,
+ std::vector<XMLPropertyState>& rPropStates,
+ const SfxPoolItem* p, const rtl::Reference<XMLPropertySetMapper>& xMapper, std::u16string_view rXMLName )
+{
+ sal_Int32 nEntryCount = xMapper->GetEntryCount();
+
+ // Apparently font info needs special handling.
+ const SvxFontItem* pItem = static_cast<const SvxFontItem*>(p);
+
+ sal_Int32 nIndexFontName = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, rXMLName, 0);
+
+ if (nIndexFontName == -1 || nIndexFontName >= nEntryCount)
+ return;
+
+ OUString const sFamilyName(pItem->GetFamilyName());
+ OUString const sStyleName(pItem->GetStyleName());
+ auto const nFamily(pItem->GetFamily());
+ auto const nPitch(pItem->GetPitch());
+ auto const eEnc(pItem->GetCharSet());
+ OUString const sName(rExport.GetFontAutoStylePool()->Find(
+ sFamilyName, sStyleName, nFamily, nPitch, eEnc));
+ if (sName.isEmpty())
+ {
+ assert(false); // fallback to fo:font-family etc. probably not needed
+ }
+
+ rPropStates.emplace_back(nIndexFontName, uno::Any(sName));
+}
+
+const SvxFieldData* toXMLPropertyStates(
+ SvXMLExport & rExport,
+ std::vector<XMLPropertyState>& rPropStates, const std::vector<const SfxPoolItem*>& rSecAttrs,
+ const rtl::Reference<XMLPropertySetMapper>& xMapper, const ScXMLEditAttributeMap& rAttrMap )
+{
+ const SvxFieldData* pField = nullptr;
+ sal_Int32 nEntryCount = xMapper->GetEntryCount();
+ rPropStates.reserve(rSecAttrs.size());
+ for (const SfxPoolItem* p : rSecAttrs)
+ {
+ if (p->Which() == EE_FEATURE_FIELD)
+ {
+ pField = static_cast<const SvxFieldItem*>(p)->GetField();
+ continue;
+ }
+
+ const ScXMLEditAttributeMap::Entry* pEntry = rAttrMap.getEntryByItemID(p->Which());
+ if (!pEntry)
+ continue;
+
+ sal_Int32 nIndex = xMapper->GetEntryIndex(
+ pEntry->nmXMLNS, OUString::createFromAscii(pEntry->mpXMLName), 0);
+
+ if (nIndex == -1 || nIndex >= nEntryCount)
+ continue;
+
+ uno::Any aAny;
+ switch (p->Which())
+ {
+ case EE_CHAR_FONTINFO:
+ handleFont(rExport, rPropStates, p, xMapper, u"font-name");
+ break;
+ case EE_CHAR_FONTINFO_CJK:
+ handleFont(rExport, rPropStates, p, xMapper, u"font-name-asian");
+ break;
+ case EE_CHAR_FONTINFO_CTL:
+ handleFont(rExport, rPropStates, p, xMapper, u"font-name-complex");
+ break;
+ case EE_CHAR_WEIGHT:
+ case EE_CHAR_WEIGHT_CJK:
+ case EE_CHAR_WEIGHT_CTL:
+ {
+ if (!static_cast<const SvxWeightItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_FONTHEIGHT:
+ case EE_CHAR_FONTHEIGHT_CJK:
+ case EE_CHAR_FONTHEIGHT_CTL:
+ {
+ if (!static_cast<const SvxFontHeightItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_ITALIC:
+ case EE_CHAR_ITALIC_CJK:
+ case EE_CHAR_ITALIC_CTL:
+ {
+ if (!static_cast<const SvxPostureItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_UNDERLINE:
+ {
+ // Underline attribute needs to export multiple entries.
+ sal_Int32 nIndexStyle = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-style", 0);
+ if (nIndexStyle == -1 || nIndexStyle > nEntryCount)
+ break;
+
+ sal_Int32 nIndexWidth = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-width", 0);
+ if (nIndexWidth == -1 || nIndexWidth > nEntryCount)
+ break;
+
+ sal_Int32 nIndexType = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-type", 0);
+ if (nIndexType == -1 || nIndexType > nEntryCount)
+ break;
+
+ sal_Int32 nIndexColor = xMapper->FindEntryIndex("CharUnderlineColor", XML_NAMESPACE_STYLE, u"text-underline-color");
+ if (nIndexColor == -1 || nIndexColor > nEntryCount)
+ break;
+
+ sal_Int32 nIndexHasColor = xMapper->FindEntryIndex("CharUnderlineHasColor", XML_NAMESPACE_STYLE, u"text-underline-color");
+ if (nIndexHasColor == -1 || nIndexHasColor > nEntryCount)
+ break;
+
+ const SvxUnderlineItem* pUL = static_cast<const SvxUnderlineItem*>(p);
+ pUL->QueryValue(aAny, MID_TL_STYLE);
+ rPropStates.emplace_back(nIndexStyle, aAny);
+ rPropStates.emplace_back(nIndexType, aAny);
+ rPropStates.emplace_back(nIndexWidth, aAny);
+
+ pUL->QueryValue(aAny, MID_TL_COLOR);
+ rPropStates.emplace_back(nIndexColor, aAny);
+
+ pUL->QueryValue(aAny, MID_TL_HASCOLOR);
+ rPropStates.emplace_back(nIndexHasColor, aAny);
+ }
+ break;
+ case EE_CHAR_OVERLINE:
+ {
+ // Same with overline. Do just as we do with underline attributes.
+ sal_Int32 nIndexStyle = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-style", 0);
+ if (nIndexStyle == -1 || nIndexStyle > nEntryCount)
+ break;
+
+ sal_Int32 nIndexWidth = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-width", 0);
+ if (nIndexWidth == -1 || nIndexWidth > nEntryCount)
+ break;
+
+ sal_Int32 nIndexType = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-type", 0);
+ if (nIndexType == -1 || nIndexType > nEntryCount)
+ break;
+
+ sal_Int32 nIndexColor = xMapper->FindEntryIndex("CharOverlineColor", XML_NAMESPACE_STYLE, u"text-overline-color");
+ if (nIndexColor == -1 || nIndexColor > nEntryCount)
+ break;
+
+ sal_Int32 nIndexHasColor = xMapper->FindEntryIndex("CharOverlineHasColor", XML_NAMESPACE_STYLE, u"text-overline-color");
+ if (nIndexHasColor == -1 || nIndexHasColor > nEntryCount)
+ break;
+
+ const SvxOverlineItem* pOL = static_cast<const SvxOverlineItem*>(p);
+ pOL->QueryValue(aAny, MID_TL_STYLE);
+ rPropStates.emplace_back(nIndexStyle, aAny);
+ rPropStates.emplace_back(nIndexType, aAny);
+ rPropStates.emplace_back(nIndexWidth, aAny);
+
+ pOL->QueryValue(aAny, MID_TL_COLOR);
+ rPropStates.emplace_back(nIndexColor, aAny);
+
+ pOL->QueryValue(aAny, MID_TL_HASCOLOR);
+ rPropStates.emplace_back(nIndexHasColor, aAny);
+ }
+ break;
+ case EE_CHAR_COLOR:
+ {
+ if (!static_cast<const SvxColorItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ ::Color nColor;
+ if ( aAny >>= nColor )
+ {
+ sal_Int32 nIndexColor = ( nColor == COL_AUTO ) ? xMapper->GetEntryIndex(
+ XML_NAMESPACE_STYLE, GetXMLToken( XML_USE_WINDOW_FONT_COLOR ), 0 ) : nIndex;
+ rPropStates.emplace_back( nIndexColor, aAny );
+ }
+ }
+ break;
+ case EE_CHAR_WLM:
+ {
+ if (!static_cast<const SvxWordLineModeItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_STRIKEOUT:
+ {
+ if (!static_cast<const SvxCrossedOutItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_RELIEF:
+ {
+ if (!static_cast<const SvxCharReliefItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_OUTLINE:
+ {
+ if (!static_cast<const SvxContourItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_SHADOW:
+ {
+ if (!static_cast<const SvxShadowedItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_KERNING:
+ {
+ if (!static_cast<const SvxKerningItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_PAIRKERNING:
+ {
+ if (!static_cast<const SvxAutoKernItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_FONTWIDTH:
+ {
+ if (!static_cast<const SvxCharScaleWidthItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_ESCAPEMENT:
+ {
+ sal_Int32 nIndexEsc = xMapper->FindEntryIndex("CharEscapement", XML_NAMESPACE_STYLE, u"text-position");
+ if (nIndexEsc == -1 || nIndexEsc > nEntryCount)
+ break;
+
+ sal_Int32 nIndexEscHeight = xMapper->FindEntryIndex("CharEscapementHeight", XML_NAMESPACE_STYLE, u"text-position");
+ if (nIndexEscHeight == -1 || nIndexEscHeight > nEntryCount)
+ break;
+
+ const SvxEscapementItem* pEsc = static_cast<const SvxEscapementItem*>(p);
+
+ pEsc->QueryValue(aAny);
+ rPropStates.emplace_back(nIndexEsc, aAny);
+
+ pEsc->QueryValue(aAny, MID_ESC_HEIGHT);
+ rPropStates.emplace_back(nIndexEscHeight, aAny);
+
+ }
+ break;
+ case EE_CHAR_EMPHASISMARK:
+ {
+ if (!static_cast<const SvxEmphasisMarkItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ rPropStates.emplace_back(nIndex, aAny);
+ }
+ break;
+ case EE_CHAR_LANGUAGE:
+ case EE_CHAR_LANGUAGE_CJK:
+ case EE_CHAR_LANGUAGE_CTL:
+ {
+ if (!static_cast<const SvxLanguageItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
+ continue;
+
+ // Export multiple entries.
+ sal_Int32 nIndexLanguage, nIndexCountry, nIndexScript, nIndexTag;
+ switch (p->Which())
+ {
+ case EE_CHAR_LANGUAGE:
+ nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"language", 0);
+ nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"country", 0);
+ nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"script", 0);
+ nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag", 0);
+ break;
+ case EE_CHAR_LANGUAGE_CJK:
+ nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"language-asian", 0);
+ nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"country-asian", 0);
+ nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"script-asian", 0);
+ nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag-asian", 0);
+ break;
+ case EE_CHAR_LANGUAGE_CTL:
+ nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"language-complex", 0);
+ nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"country-complex", 0);
+ nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"script-complex", 0);
+ nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag-complex", 0);
+ break;
+ default:
+ nIndexLanguage = nIndexCountry = nIndexScript = nIndexTag = -1;
+ }
+ assert( nIndexLanguage >= 0 && nIndexCountry >= 0 && nIndexScript >= 0 && nIndexTag >= 0);
+ rPropStates.emplace_back( nIndexLanguage, aAny);
+ rPropStates.emplace_back( nIndexCountry, aAny);
+ rPropStates.emplace_back( nIndexScript, aAny);
+ rPropStates.emplace_back( nIndexTag, aAny);
+ }
+ break;
+ default:
+ continue;
+ }
+ }
+
+ return pField;
+}
+
+}
+
+void ScXMLExport::ExportCellTextAutoStyles(sal_Int32 nTable)
+{
+ if (!ValidTab(nTable))
+ return;
+
+ rtl::Reference<XMLPropertySetMapper> xMapper = GetTextParagraphExport()->GetTextPropMapper()->getPropertySetMapper();
+ rtl::Reference<SvXMLAutoStylePoolP> xStylePool = GetAutoStylePool();
+ const ScXMLEditAttributeMap& rAttrMap = GetEditAttributeMap();
+
+ sc::EditTextIterator aIter(*pDoc, nTable);
+ sal_Int32 nCellCount = 0;
+ for (const EditTextObject* pEdit = aIter.first(); pEdit; pEdit = aIter.next(), ++nCellCount)
+ {
+ std::vector<editeng::Section> aAttrs;
+ pEdit->GetAllSections(aAttrs);
+ if (aAttrs.empty())
+ continue;
+
+ for (const auto& rSec : aAttrs)
+ {
+ const std::vector<const SfxPoolItem*>& rSecAttrs = rSec.maAttributes;
+ if (rSecAttrs.empty())
+ // No formats applied to this section. Skip it.
+ continue;
+
+ std::vector<XMLPropertyState> aPropStates;
+ toXMLPropertyStates(*this, aPropStates, rSecAttrs, xMapper, rAttrMap);
+ if (!aPropStates.empty())
+ xStylePool->Add(XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
+ }
+ }
+
+ GetProgressBarHelper()->ChangeReference(GetProgressBarHelper()->GetReference() + nCellCount);
+}
+
+void ScXMLExport::WriteRowContent()
+{
+ ScMyRowFormatRange aRange;
+ sal_Int32 nIndex(-1);
+#if OSL_DEBUG_LEVEL > 0
+ sal_Int32 nPrevCol(0);
+#endif
+ sal_Int32 nCols(0);
+ sal_Int32 nPrevValidationIndex(-1);
+ bool bIsAutoStyle(true);
+ bool bIsFirst(true);
+ while (pRowFormatRanges->GetNext(aRange))
+ {
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE(bIsFirst || (!bIsFirst && (nPrevCol + nCols == aRange.nStartColumn)), "here are some columns missing");
+#endif
+ if (bIsFirst)
+ {
+ nIndex = aRange.nIndex;
+ nPrevValidationIndex = aRange.nValidationIndex;
+ bIsAutoStyle = aRange.bIsAutoStyle;
+ nCols = aRange.nRepeatColumns;
+ bIsFirst = false;
+#if OSL_DEBUG_LEVEL > 0
+ nPrevCol = aRange.nStartColumn;
+#endif
+ }
+ else
+ {
+ if (((aRange.nIndex == nIndex && aRange.bIsAutoStyle == bIsAutoStyle) ||
+ (aRange.nIndex == nIndex && nIndex == -1)) &&
+ nPrevValidationIndex == aRange.nValidationIndex)
+ nCols += aRange.nRepeatColumns;
+ else
+ {
+ if (nIndex != -1)
+ AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
+ if (nPrevValidationIndex > -1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(nPrevValidationIndex));
+ if (nCols > 1)
+ {
+ AddAttribute(sAttrColumnsRepeated, OUString::number(nCols));
+ }
+ SvXMLElementExport aElemC(*this, sElemCell, true, true);
+ nIndex = aRange.nIndex;
+ bIsAutoStyle = aRange.bIsAutoStyle;
+ nCols = aRange.nRepeatColumns;
+ nPrevValidationIndex = aRange.nValidationIndex;
+#if OSL_DEBUG_LEVEL > 0
+ nPrevCol = aRange.nStartColumn;
+#endif
+ }
+ }
+ }
+ if (!bIsFirst)
+ {
+ if (nIndex != -1)
+ AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
+ if (nPrevValidationIndex > -1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(nPrevValidationIndex));
+ if (nCols > 1)
+ {
+ AddAttribute(sAttrColumnsRepeated, OUString::number(nCols));
+ }
+ SvXMLElementExport aElemC(*this, sElemCell, true, true);
+ }
+}
+
+void ScXMLExport::WriteRowStartTag(
+ const sal_Int32 nIndex, const sal_Int32 nEqualRows,
+ bool bHidden, bool bFiltered)
+{
+ // tdf#143940
+ if (nIndex != -1)
+ AddAttribute(sAttrStyleName, pRowStyles->GetStyleNameByIndex(nIndex));
+ if (bHidden)
+ {
+ if (bFiltered)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_FILTER);
+ else
+ AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_COLLAPSE);
+ }
+ if (nEqualRows > 1)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, OUString::number(nEqualRows));
+ }
+
+ StartElement( sElemRow, true);
+}
+
+void ScXMLExport::OpenHeaderRows()
+{
+ StartElement( XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true);
+ bRowHeaderOpen = true;
+}
+
+void ScXMLExport::CloseHeaderRows()
+{
+ EndElement(XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true);
+}
+
+void ScXMLExport::OpenNewRow(
+ const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEqualRows,
+ bool bHidden, bool bFiltered)
+{
+ nOpenRow = nStartRow;
+ if (pGroupRows->IsGroupStart(nStartRow))
+ {
+ if (bHasRowHeader && bRowHeaderOpen)
+ CloseHeaderRows();
+ pGroupRows->OpenGroups(nStartRow);
+ if (bHasRowHeader && bRowHeaderOpen)
+ OpenHeaderRows();
+ }
+ if (bHasRowHeader && !bRowHeaderOpen && nStartRow >= aRowHeaderRange.aStart.Row() && nStartRow <= aRowHeaderRange.aEnd.Row())
+ {
+ if (nStartRow == aRowHeaderRange.aStart.Row())
+ OpenHeaderRows();
+ sal_Int32 nEquals;
+ if (aRowHeaderRange.aEnd.Row() < nStartRow + nEqualRows - 1)
+ nEquals = aRowHeaderRange.aEnd.Row() - nStartRow + 1;
+ else
+ nEquals = nEqualRows;
+ WriteRowStartTag(nIndex, nEquals, bHidden, bFiltered);
+ nOpenRow = nStartRow + nEquals - 1;
+ if (nEquals < nEqualRows)
+ {
+ CloseRow(nStartRow + nEquals - 1);
+ WriteRowStartTag(nIndex, nEqualRows - nEquals, bHidden, bFiltered);
+ nOpenRow = nStartRow + nEqualRows - 1;
+ }
+ }
+ else
+ WriteRowStartTag(nIndex, nEqualRows, bHidden, bFiltered);
+}
+
+void ScXMLExport::OpenAndCloseRow(
+ const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEqualRows,
+ bool bHidden, bool bFiltered)
+{
+ OpenNewRow(nIndex, nStartRow, nEqualRows, bHidden, bFiltered);
+ WriteRowContent();
+ CloseRow(nStartRow + nEqualRows - 1);
+ pRowFormatRanges->Clear();
+}
+
+void ScXMLExport::OpenRow(const sal_Int32 nTable, const sal_Int32 nStartRow, const sal_Int32 nRepeatRow, ScXMLCachedRowAttrAccess& rRowAttr)
+{
+ if (nRepeatRow > 1)
+ {
+ sal_Int32 nPrevIndex(0), nIndex;
+ bool bPrevHidden = false;
+ bool bPrevFiltered = false;
+ bool bHidden = false;
+ bool bFiltered = false;
+ sal_Int32 nEqualRows(1);
+ sal_Int32 nEndRow(nStartRow + nRepeatRow);
+ sal_Int32 nEndRowHidden = nStartRow - 1;
+ sal_Int32 nEndRowFiltered = nStartRow - 1;
+ sal_Int32 nRow;
+ for (nRow = nStartRow; nRow < nEndRow; ++nRow)
+ {
+ if (nRow == nStartRow)
+ {
+ nPrevIndex = pRowStyles->GetStyleNameIndex(nTable, nRow);
+ if (pDoc)
+ {
+ if (nRow > nEndRowHidden)
+ {
+ bPrevHidden = rRowAttr.rowHidden(nTable, nRow, nEndRowHidden);
+ bHidden = bPrevHidden;
+ }
+ if (nRow > nEndRowFiltered)
+ {
+ bPrevFiltered = rRowAttr.rowFiltered(nTable, nRow, nEndRowFiltered);
+ bFiltered = bPrevFiltered;
+ }
+ }
+
+ }
+ else
+ {
+ nIndex = pRowStyles->GetStyleNameIndex(nTable, nRow);
+ if (pDoc)
+ {
+ if (nRow > nEndRowHidden)
+ bHidden = rRowAttr.rowHidden(nTable, nRow, nEndRowHidden);
+ if (nRow > nEndRowFiltered)
+ bFiltered = rRowAttr.rowFiltered(nTable, nRow, nEndRowFiltered);
+ }
+ if (nIndex == nPrevIndex && bHidden == bPrevHidden && bFiltered == bPrevFiltered &&
+ !(bHasRowHeader && ((nRow == aRowHeaderRange.aStart.Row()) || (nRow - 1 == aRowHeaderRange.aEnd.Row()))) &&
+ !(pGroupRows->IsGroupStart(nRow)) &&
+ !(pGroupRows->IsGroupEnd(nRow - 1)))
+ ++nEqualRows;
+ else
+ {
+ assert(nPrevIndex >= 0 && "coverity#1438402");
+ ScRowFormatRanges* pTempRowFormatRanges = new ScRowFormatRanges(pRowFormatRanges.get());
+ OpenAndCloseRow(nPrevIndex, nRow - nEqualRows, nEqualRows, bPrevHidden, bPrevFiltered);
+ pRowFormatRanges.reset(pTempRowFormatRanges);
+ nEqualRows = 1;
+ nPrevIndex = nIndex;
+ bPrevHidden = bHidden;
+ bPrevFiltered = bFiltered;
+ }
+ }
+ }
+ assert(nPrevIndex >= 0 && "coverity#1438402");
+ OpenNewRow(nPrevIndex, nRow - nEqualRows, nEqualRows, bPrevHidden, bPrevFiltered);
+ }
+ else
+ {
+ sal_Int32 nIndex = pRowStyles->GetStyleNameIndex(nTable, nStartRow);
+ bool bHidden = false;
+ bool bFiltered = false;
+ if (pDoc)
+ {
+ sal_Int32 nEndRowHidden;
+ sal_Int32 nEndRowFiltered;
+ bHidden = rRowAttr.rowHidden(nTable, nStartRow, nEndRowHidden);
+ bFiltered = rRowAttr.rowFiltered(nTable, nStartRow, nEndRowFiltered);
+ }
+ assert(nIndex >= 0 && "coverity#1438402");
+ OpenNewRow(nIndex, nStartRow, 1, bHidden, bFiltered);
+ }
+ nOpenRow = nStartRow + nRepeatRow - 1;
+}
+
+void ScXMLExport::CloseRow(const sal_Int32 nRow)
+{
+ if (nOpenRow > -1)
+ {
+ EndElement(sElemRow, true);
+ if (bHasRowHeader && nRow == aRowHeaderRange.aEnd.Row())
+ {
+ CloseHeaderRows();
+ bRowHeaderOpen = false;
+ }
+ if (pGroupRows->IsGroupEnd(nRow))
+ {
+ if (bHasRowHeader && bRowHeaderOpen)
+ CloseHeaderRows();
+ pGroupRows->CloseGroups(nRow);
+ if (bHasRowHeader && bRowHeaderOpen)
+ OpenHeaderRows();
+ }
+ }
+ nOpenRow = -1;
+}
+
+void ScXMLExport::ExportFormatRanges(const sal_Int32 nStartCol, const sal_Int32 nStartRow,
+ const sal_Int32 nEndCol, const sal_Int32 nEndRow, const sal_Int32 nSheet)
+{
+ pRowFormatRanges->Clear();
+ ScXMLCachedRowAttrAccess aRowAttr(pDoc);
+ if (nStartRow == nEndRow)
+ {
+ pCellStyles->GetFormatRanges(nStartCol, nEndCol, nStartRow, nSheet, pRowFormatRanges.get());
+ if (nOpenRow == - 1)
+ OpenRow(nSheet, nStartRow, 1, aRowAttr);
+ WriteRowContent();
+ pRowFormatRanges->Clear();
+ }
+ else
+ {
+ if (nOpenRow > -1)
+ {
+ pCellStyles->GetFormatRanges(nStartCol, pSharedData->GetLastColumn(nSheet), nStartRow, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ CloseRow(nStartRow);
+ sal_Int32 nRows(1);
+ sal_Int32 nTotalRows(nEndRow - nStartRow + 1 - 1);
+ while (nRows < nTotalRows)
+ {
+ pRowFormatRanges->Clear();
+ pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
+ sal_Int32 nMaxRows = pRowFormatRanges->GetMaxRows();
+ OSL_ENSURE(nMaxRows, "something went wrong");
+ if (nMaxRows >= nTotalRows - nRows)
+ {
+ OpenRow(nSheet, nStartRow + nRows, nTotalRows - nRows, aRowAttr);
+ nRows += nTotalRows - nRows;
+ }
+ else
+ {
+ OpenRow(nSheet, nStartRow + nRows, nMaxRows, aRowAttr);
+ nRows += nMaxRows;
+ }
+ if (!pRowFormatRanges->GetSize())
+ pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ CloseRow(nStartRow + nRows - 1);
+ }
+ if (nTotalRows == 1)
+ CloseRow(nStartRow);
+ OpenRow(nSheet, nEndRow, 1, aRowAttr);
+ pRowFormatRanges->Clear();
+ pCellStyles->GetFormatRanges(0, nEndCol, nEndRow, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ }
+ else
+ {
+ sal_Int32 nRows(0);
+ sal_Int32 nTotalRows(nEndRow - nStartRow + 1 - 1);
+ while (nRows < nTotalRows)
+ {
+ pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
+ sal_Int32 nMaxRows = pRowFormatRanges->GetMaxRows();
+ OSL_ENSURE(nMaxRows, "something went wrong");
+ if (nMaxRows >= nTotalRows - nRows)
+ {
+ OpenRow(nSheet, nStartRow + nRows, nTotalRows - nRows, aRowAttr);
+ nRows += nTotalRows - nRows;
+ }
+ else
+ {
+ OpenRow(nSheet, nStartRow + nRows, nMaxRows, aRowAttr);
+ nRows += nMaxRows;
+ }
+ if (!pRowFormatRanges->GetSize())
+ pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ CloseRow(nStartRow + nRows - 1);
+ }
+ OpenRow(nSheet, nEndRow, 1, aRowAttr);
+ pRowFormatRanges->Clear();
+ pCellStyles->GetFormatRanges(0, nEndCol, nEndRow, nSheet, pRowFormatRanges.get());
+ WriteRowContent();
+ }
+ }
+}
+
+void ScXMLExport::GetColumnRowHeader(bool& rHasColumnHeader, ScRange& rColumnHeaderRange,
+ bool& rHasRowHeader, ScRange& rRowHeaderRange,
+ OUString& rPrintRanges) const
+{
+ uno::Reference <sheet::XPrintAreas> xPrintAreas (xCurrentTable, uno::UNO_QUERY);
+ if (!xPrintAreas.is())
+ return;
+
+ rHasRowHeader = xPrintAreas->getPrintTitleRows();
+ rHasColumnHeader = xPrintAreas->getPrintTitleColumns();
+ table::CellRangeAddress rTempRowHeaderRange = xPrintAreas->getTitleRows();
+ rRowHeaderRange = ScRange(rTempRowHeaderRange.StartColumn,
+ rTempRowHeaderRange.StartRow,
+ rTempRowHeaderRange.Sheet,
+ rTempRowHeaderRange.EndColumn,
+ rTempRowHeaderRange.EndRow,
+ rTempRowHeaderRange.Sheet);
+ table::CellRangeAddress rTempColumnHeaderRange = xPrintAreas->getTitleColumns();
+ rColumnHeaderRange = ScRange(rTempColumnHeaderRange.StartColumn,
+ rTempColumnHeaderRange.StartRow,
+ rTempColumnHeaderRange.Sheet,
+ rTempColumnHeaderRange.EndColumn,
+ rTempColumnHeaderRange.EndRow,
+ rTempColumnHeaderRange.Sheet);
+ uno::Sequence< table::CellRangeAddress > aRangeList( xPrintAreas->getPrintAreas() );
+ ScRangeStringConverter::GetStringFromRangeList( rPrintRanges, aRangeList, pDoc, FormulaGrammar::CONV_OOO );
+}
+
+void ScXMLExport::FillFieldGroup(ScOutlineArray* pFields, ScMyOpenCloseColumnRowGroup* pGroups)
+{
+ size_t nDepth = pFields->GetDepth();
+ for (size_t i = 0; i < nDepth; ++i)
+ {
+ size_t nFields = pFields->GetCount(i);
+ for (size_t j = 0; j < nFields; ++j)
+ {
+ ScMyColumnRowGroup aGroup;
+ const ScOutlineEntry* pEntry = pFields->GetEntry(i, j);
+ aGroup.nField = pEntry->GetStart();
+ aGroup.nLevel = static_cast<sal_Int16>(i);
+ aGroup.bDisplay = !(pEntry->IsHidden());
+ pGroups->AddGroup(aGroup, pEntry->GetEnd());
+ }
+ }
+ if (nDepth)
+ pGroups->Sort();
+}
+
+void ScXMLExport::FillColumnRowGroups()
+{
+ if (!pDoc)
+ return;
+
+ ScOutlineTable* pOutlineTable = pDoc->GetOutlineTable( static_cast<SCTAB>(nCurrentTable) );
+ if(pOutlineTable)
+ {
+ ScOutlineArray& rCols(pOutlineTable->GetColArray());
+ ScOutlineArray& rRows(pOutlineTable->GetRowArray());
+ FillFieldGroup(&rCols, pGroupColumns.get());
+ FillFieldGroup(&rRows, pGroupRows.get());
+ pSharedData->SetLastColumn(nCurrentTable, pGroupColumns->GetLast());
+ pSharedData->SetLastRow(nCurrentTable, pGroupRows->GetLast());
+ }
+}
+
+void ScXMLExport::SetBodyAttributes()
+{
+ if (!(pDoc && pDoc->IsDocProtected()))
+ return;
+
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STRUCTURE_PROTECTED, XML_TRUE);
+ OUStringBuffer aBuffer;
+ uno::Sequence<sal_Int8> aPassHash;
+ ScPasswordHash eHashUsed = PASSHASH_UNSPECIFIED;
+ const ScDocProtection* p = pDoc->GetDocProtection();
+ if (p)
+ {
+ if (p->hasPasswordHash(PASSHASH_SHA1))
+ {
+ aPassHash = p->getPasswordHash(PASSHASH_SHA1);
+ eHashUsed = PASSHASH_SHA1;
+ }
+ else if (p->hasPasswordHash(PASSHASH_SHA256))
+ {
+ aPassHash = p->getPasswordHash(PASSHASH_SHA256);
+ eHashUsed = PASSHASH_SHA256;
+ }
+ else if (p->hasPasswordHash(PASSHASH_XL, PASSHASH_SHA1))
+ {
+ aPassHash = p->getPasswordHash(PASSHASH_XL, PASSHASH_SHA1);
+ eHashUsed = PASSHASH_XL;
+ }
+ }
+ ::comphelper::Base64::encode(aBuffer, aPassHash);
+ if (aBuffer.isEmpty())
+ return;
+
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY, aBuffer.makeStringAndClear());
+ if (getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
+ return;
+
+ if (eHashUsed == PASSHASH_XL)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_XL));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+ }
+ else if (eHashUsed == PASSHASH_SHA1)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+ }
+ else if (eHashUsed == PASSHASH_SHA256)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA256));
+ }
+}
+
+static bool lcl_CopyStreamElement( const uno::Reference< io::XInputStream >& xInput,
+ const uno::Reference< io::XOutputStream >& xOutput,
+ sal_Int32 nCount )
+{
+ const sal_Int32 nBufSize = 16*1024;
+ uno::Sequence<sal_Int8> aSequence(nBufSize);
+
+ sal_Int32 nRemaining = nCount;
+ bool bFirst = true;
+
+ while ( nRemaining > 0 )
+ {
+ sal_Int32 nRead = xInput->readBytes( aSequence, std::min( nRemaining, nBufSize ) );
+ if (bFirst)
+ {
+ // safety check: Make sure the copied part actually points to the start of an element
+ if ( nRead < 1 || aSequence[0] != static_cast<sal_Int8>('<') )
+ {
+ return false; // abort and set an error
+ }
+ bFirst = false;
+ }
+ if (nRead == nRemaining)
+ {
+ // safety check: Make sure the copied part also ends at the end of an element
+ if ( aSequence[nRead-1] != static_cast<sal_Int8>('>') )
+ {
+ return false; // abort and set an error
+ }
+ }
+
+ if ( nRead == nBufSize )
+ {
+ xOutput->writeBytes( aSequence );
+ nRemaining -= nRead;
+ }
+ else
+ {
+ if ( nRead > 0 )
+ {
+ uno::Sequence<sal_Int8> aTempBuf( aSequence.getConstArray(), nRead );
+ xOutput->writeBytes( aTempBuf );
+ }
+ nRemaining = 0;
+ }
+ }
+ return true; // successful
+}
+
+static void lcl_SkipBytesInBlocks( const uno::Reference< io::XInputStream >& xInput, sal_Int32 nBytesToSkip )
+{
+ // skipBytes in zip stream is implemented as reading.
+ // For now, split into several calls to avoid allocating a large buffer.
+ // Later, skipBytes should be changed.
+
+ const sal_Int32 nMaxSize = 32*1024;
+
+ if ( nBytesToSkip > 0 )
+ {
+ sal_Int32 nRemaining = nBytesToSkip;
+ while ( nRemaining > 0 )
+ {
+ sal_Int32 nSkip = std::min( nRemaining, nMaxSize );
+ xInput->skipBytes( nSkip );
+ nRemaining -= nSkip;
+ }
+ }
+}
+
+void ScXMLExport::CopySourceStream( sal_Int32 nStartOffset, sal_Int32 nEndOffset, sal_Int32& rNewStart, sal_Int32& rNewEnd )
+{
+ uno::Reference<xml::sax::XDocumentHandler> xHandler = GetDocHandler();
+ uno::Reference<io::XActiveDataSource> xDestSource( xHandler, uno::UNO_QUERY );
+ if ( !xDestSource.is() )
+ return;
+
+ uno::Reference<io::XOutputStream> xDestStream = xDestSource->getOutputStream();
+ uno::Reference<io::XSeekable> xDestSeek( xDestStream, uno::UNO_QUERY );
+ if ( !xDestSeek.is() )
+ return;
+
+ // temporary: set same stream again to clear buffer
+ xDestSource->setOutputStream( xDestStream );
+
+ if ( getExportFlags() & SvXMLExportFlags::PRETTY )
+ {
+ const OString aOutStr("\n ");
+ uno::Sequence<sal_Int8> aOutSeq( reinterpret_cast<sal_Int8 const *>(aOutStr.getStr()), aOutStr.getLength() );
+ xDestStream->writeBytes( aOutSeq );
+ }
+
+ rNewStart = static_cast<sal_Int32>(xDestSeek->getPosition());
+
+ if ( nStartOffset > nSourceStreamPos )
+ lcl_SkipBytesInBlocks( xSourceStream, nStartOffset - nSourceStreamPos );
+
+ if ( !lcl_CopyStreamElement( xSourceStream, xDestStream, nEndOffset - nStartOffset ) )
+ {
+ // If copying went wrong, set an error.
+ // ScXMLImportWrapper then resets all stream flags, so the next save attempt will use normal saving.
+
+ uno::Sequence<OUString> aEmptySeq;
+ SetError(XMLERROR_CANCEL|XMLERROR_FLAG_SEVERE, aEmptySeq);
+ }
+ nSourceStreamPos = nEndOffset;
+
+ rNewEnd = static_cast<sal_Int32>(xDestSeek->getPosition());
+}
+
+const ScXMLEditAttributeMap& ScXMLExport::GetEditAttributeMap() const
+{
+ if (!mpEditAttrMap)
+ mpEditAttrMap.reset(new ScXMLEditAttributeMap);
+ return *mpEditAttrMap;
+}
+
+void ScXMLExport::RegisterDefinedStyleNames( const uno::Reference< css::sheet::XSpreadsheetDocument > & xSpreadDoc )
+{
+ ScFormatSaveData* pFormatData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetFormatSaveData();
+ auto xAutoStylePool = GetAutoStylePool();
+ for (const auto& rFormatInfo : pFormatData->maIDToName)
+ {
+ xAutoStylePool->RegisterDefinedName(XmlStyleFamily::TABLE_CELL, rFormatInfo.second);
+ }
+}
+
+void ScXMLExport::ExportContent_()
+{
+ nCurrentTable = 0;
+ if (!pSharedData)
+ {
+ SCTAB nTableCount(0);
+ sal_Int32 nShapesCount(0);
+ CollectSharedData(nTableCount, nShapesCount);
+ OSL_FAIL("no shared data set");
+ if (!pSharedData)
+ return;
+ }
+ ScXMLExportDatabaseRanges aExportDatabaseRanges(*this);
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ if ( !xSpreadDoc.is() )
+ return;
+
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetSheetSaveData();
+ if (pSheetData)
+ pSheetData->ResetSaveEntries();
+
+ uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ //_GetNamespaceMap().ClearQNamesCache();
+ pChangeTrackingExportHelper->CollectAndWriteChanges();
+ WriteCalculationSettings(xSpreadDoc);
+ sal_Int32 nTableCount(xIndex->getCount());
+ ScMyAreaLinksContainer aAreaLinks;
+ GetAreaLinks( aAreaLinks );
+ ScMyEmptyDatabaseRangesContainer aEmptyRanges(aExportDatabaseRanges.GetEmptyDatabaseRanges());
+ ScMyDetectiveOpContainer aDetectiveOpContainer;
+ GetDetectiveOpList( aDetectiveOpContainer );
+
+ pCellStyles->Sort();
+ pMergedRangesContainer->Sort();
+ pSharedData->GetDetectiveObjContainer()->Sort();
+
+ mpCellsItr->Clear();
+ mpCellsItr->SetShapes( pSharedData->GetShapesContainer() );
+ mpCellsItr->SetNoteShapes( pSharedData->GetNoteShapes() );
+ mpCellsItr->SetMergedRanges( pMergedRangesContainer.get() );
+ mpCellsItr->SetAreaLinks( &aAreaLinks );
+ mpCellsItr->SetEmptyDatabaseRanges( &aEmptyRanges );
+ mpCellsItr->SetDetectiveObj( pSharedData->GetDetectiveObjContainer() );
+ mpCellsItr->SetDetectiveOp( &aDetectiveOpContainer );
+
+ if (nTableCount > 0)
+ pValidationsContainer->WriteValidations(*this);
+ WriteTheLabelRanges( xSpreadDoc );
+ for (sal_Int32 nTable = 0; nTable < nTableCount; ++nTable)
+ {
+ sal_Int32 nStartOffset = -1;
+ sal_Int32 nEndOffset = -1;
+ if (pSheetData && pDoc && pDoc->IsStreamValid(static_cast<SCTAB>(nTable)) && !pDoc->GetChangeTrack())
+ pSheetData->GetStreamPos( nTable, nStartOffset, nEndOffset );
+
+ if ( nStartOffset >= 0 && nEndOffset >= 0 && xSourceStream.is() )
+ {
+ sal_Int32 nNewStart = -1;
+ sal_Int32 nNewEnd = -1;
+ CopySourceStream( nStartOffset, nEndOffset, nNewStart, nNewEnd );
+
+ // store position of copied sheet in output
+ pSheetData->AddSavePos( nTable, nNewStart, nNewEnd );
+
+ // skip iterator entries for this sheet
+ mpCellsItr->SkipTable(static_cast<SCTAB>(nTable));
+ }
+ else
+ {
+ uno::Reference<sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ WriteTable(nTable, xTable);
+ }
+ IncrementProgressBar(false);
+ }
+ }
+ WriteExternalRefCaches();
+ WriteNamedExpressions();
+ WriteDataStream();
+ aExportDatabaseRanges.WriteDatabaseRanges();
+ WriteExternalDataMapping();
+ ScXMLExportDataPilot aExportDataPilot(*this);
+ aExportDataPilot.WriteDataPilots();
+ WriteConsolidation();
+ ScXMLExportDDELinks aExportDDELinks(*this);
+ aExportDDELinks.WriteDDELinks(xSpreadDoc);
+ IncrementProgressBar(true, 0);
+ GetProgressBarHelper()->SetValue(GetProgressBarHelper()->GetReference());
+}
+
+void ScXMLExport::ExportStyles_( bool bUsed )
+{
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ if (xSpreadDoc.is())
+ RegisterDefinedStyleNames( xSpreadDoc);
+
+ if (!pSharedData)
+ {
+ SCTAB nTableCount(0);
+ sal_Int32 nShapesCount(0);
+ CollectSharedData(nTableCount, nShapesCount);
+ }
+ rtl::Reference<XMLCellStyleExport> aStylesExp(new XMLCellStyleExport(*this, GetAutoStylePool().get()));
+ if (GetModel().is())
+ {
+ uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
+ if (xMultiServiceFactory.is())
+ {
+ uno::Reference <beans::XPropertySet> xProperties(xMultiServiceFactory->createInstance("com.sun.star.sheet.Defaults"), uno::UNO_QUERY);
+ if (xProperties.is())
+ aStylesExp->exportDefaultStyle(xProperties, XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME, xCellStylesExportPropertySetMapper);
+ if (pSharedData->HasShapes())
+ {
+ GetShapeExport()->ExportGraphicDefaults();
+ }
+ }
+ collectDataStyles(false);
+ }
+ exportDataStyles();
+
+ aStylesExp->exportStyleFamily(OUString("CellStyles"),
+ OUString(XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME), xCellStylesExportPropertySetMapper, false, XmlStyleFamily::TABLE_CELL);
+
+ SvXMLExport::ExportStyles_(bUsed);
+}
+
+void ScXMLExport::AddStyleFromCells(const uno::Reference<beans::XPropertySet>& xProperties,
+ const uno::Reference<sheet::XSpreadsheet>& xTable,
+ sal_Int32 nTable, const OUString* pOldName)
+{
+ css::uno::Any aAny = xProperties->getPropertyValue("FormatID");
+ sal_uInt64 nKey = 0;
+ aAny >>= nKey;
+
+ //! pass xCellRanges instead
+ uno::Reference<sheet::XSheetCellRanges> xCellRanges( xProperties, uno::UNO_QUERY );
+
+ OUString sStyleName;
+ sal_Int32 nNumberFormat(-1);
+ sal_Int32 nValidationIndex(-1);
+ std::vector<XMLPropertyState> aPropStates(xCellStylesExportPropertySetMapper->Filter(*this, xProperties));
+ std::vector< XMLPropertyState >::iterator aItr(aPropStates.begin());
+ std::vector< XMLPropertyState >::iterator aEndItr(aPropStates.end());
+ sal_Int32 nCount(0);
+ while (aItr != aEndItr)
+ {
+ if (aItr->mnIndex != -1)
+ {
+ switch (xCellStylesPropertySetMapper->GetEntryContextId(aItr->mnIndex))
+ {
+ case CTF_SC_VALIDATION :
+ {
+ pValidationsContainer->AddValidation(aItr->maValue, nValidationIndex);
+ // this is not very slow, because it is most the last property or
+ // if it is not the last property it is the property before the last property,
+ // so in the worst case only one property has to be copied, but in the best case no
+ // property has to be copied
+ aItr = aPropStates.erase(aItr);
+ aEndItr = aPropStates.end(); // old aEndItr is invalidated!
+ }
+ break;
+ case CTF_SC_CELLSTYLE :
+ {
+ aItr->maValue >>= sStyleName;
+ aItr->mnIndex = -1;
+ ++aItr;
+ ++nCount;
+ }
+ break;
+ case CTF_SC_NUMBERFORMAT :
+ {
+ if (aItr->maValue >>= nNumberFormat)
+ addDataStyle(nNumberFormat);
+ ++aItr;
+ ++nCount;
+ }
+ break;
+ default:
+ {
+ ++aItr;
+ ++nCount;
+ }
+ break;
+ }
+ }
+ else
+ {
+ ++aItr;
+ ++nCount;
+ }
+ }
+ if (nCount == 1) // this is the CellStyle and should be removed if alone
+ aPropStates.clear();
+ if (nNumberFormat == -1)
+ xProperties->getPropertyValue(SC_UNONAME_NUMFMT) >>= nNumberFormat;
+ if (sStyleName.isEmpty())
+ return;
+
+ if (!aPropStates.empty())
+ {
+ sal_Int32 nIndex;
+ if (pOldName)
+ {
+ if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_CELL, sStyleName, std::move(aPropStates)))
+ {
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_CELL, *pOldName);
+ // add to pCellStyles, so the name is found for normal sheets
+ pCellStyles->AddStyleName(*pOldName, nIndex);
+ }
+ }
+ else
+ {
+ OUString sName;
+ bool bAdded = false;
+ if (nKey)
+ {
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ ScFormatSaveData* pFormatData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetFormatSaveData();
+ auto itr = pFormatData->maIDToName.find(nKey);
+ if (itr != pFormatData->maIDToName.end())
+ {
+ sName = itr->second;
+ bAdded = GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TABLE_CELL, sStyleName, std::move(aPropStates));
+ if (bAdded)
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_CELL, sName);
+ }
+ }
+ bool bIsAutoStyle(true);
+ if (bAdded || GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_CELL, sStyleName, std::move(aPropStates)))
+ {
+ pCellStyles->AddStyleName(sName, nIndex);
+ }
+ else
+ nIndex = pCellStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX, bIsAutoStyle);
+
+ const uno::Sequence<table::CellRangeAddress> aAddresses(xCellRanges->getRangeAddresses());
+ bool bGetMerge(true);
+ for (table::CellRangeAddress const & address : aAddresses)
+ {
+ pSharedData->SetLastColumn(nTable, address.EndColumn);
+ pSharedData->SetLastRow(nTable, address.EndRow);
+ pCellStyles->AddRangeStyleName(address, nIndex, bIsAutoStyle, nValidationIndex, nNumberFormat);
+ if (bGetMerge)
+ bGetMerge = GetMerged(&address, xTable);
+ }
+ }
+ }
+ else
+ {
+ OUString sEncodedStyleName(EncodeStyleName(sStyleName));
+ sal_Int32 nIndex(0);
+ pCellStyles->AddStyleName(sEncodedStyleName, nIndex, false);
+ if ( !pOldName )
+ {
+ const uno::Sequence<table::CellRangeAddress> aAddresses(xCellRanges->getRangeAddresses());
+ bool bGetMerge(true);
+ for (table::CellRangeAddress const & address : aAddresses)
+ {
+ if (bGetMerge)
+ bGetMerge = GetMerged(&address, xTable);
+ pCellStyles->AddRangeStyleName(address, nIndex, false, nValidationIndex, nNumberFormat);
+ if( sStyleName != "Default" || nValidationIndex != -1 )
+ {
+ pSharedData->SetLastColumn(nTable, address.EndColumn);
+ pSharedData->SetLastRow(nTable, address.EndRow);
+ }
+ }
+ }
+ }
+}
+
+void ScXMLExport::AddStyleFromColumn(const uno::Reference<beans::XPropertySet>& xColumnProperties,
+ const OUString* pOldName, sal_Int32& rIndex, bool& rIsVisible)
+{
+ std::vector<XMLPropertyState> aPropStates(xColumnStylesExportPropertySetMapper->Filter(*this, xColumnProperties));
+ if(aPropStates.empty())
+ return;
+
+ auto aItr = std::find_if(aPropStates.begin(), aPropStates.end(),
+ [this](const XMLPropertyState& rPropState) {
+ return xColumnStylesPropertySetMapper->GetEntryContextId(rPropState.mnIndex) == CTF_SC_ISVISIBLE; });
+ if (aItr != aPropStates.end())
+ {
+ aItr->maValue >>= rIsVisible;
+ }
+
+ const OUString sParent;
+ if (pOldName)
+ {
+ if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_COLUMN, sParent, std::move(aPropStates)))
+ {
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_COLUMN, *pOldName);
+ // add to pColumnStyles, so the name is found for normal sheets
+ rIndex = pColumnStyles->AddStyleName(*pOldName);
+ }
+ }
+ else
+ {
+ OUString sName;
+ if (GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_COLUMN, sParent, std::move(aPropStates)))
+ {
+ rIndex = pColumnStyles->AddStyleName(sName);
+ }
+ else
+ rIndex = pColumnStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_PREFIX);
+ }
+}
+
+void ScXMLExport::AddStyleFromRow(const uno::Reference<beans::XPropertySet>& xRowProperties,
+ const OUString* pOldName, sal_Int32& rIndex)
+{
+ std::vector<XMLPropertyState> aPropStates(xRowStylesExportPropertySetMapper->Filter(*this, xRowProperties));
+ if(aPropStates.empty())
+ return;
+
+ const OUString sParent;
+ if (pOldName)
+ {
+ if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_ROW, sParent, std::move(aPropStates)))
+ {
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_ROW, *pOldName);
+ // add to pRowStyles, so the name is found for normal sheets
+ rIndex = pRowStyles->AddStyleName(*pOldName);
+ }
+ }
+ else
+ {
+ OUString sName;
+ if (GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_ROW, sParent, std::move(aPropStates)))
+ {
+ rIndex = pRowStyles->AddStyleName(sName);
+ }
+ else
+ rIndex = pRowStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_ROW_STYLES_PREFIX);
+ }
+}
+
+static uno::Any lcl_GetEnumerated( uno::Reference<container::XEnumerationAccess> const & xEnumAccess, sal_Int32 nIndex )
+{
+ uno::Any aRet;
+ uno::Reference<container::XEnumeration> xEnum( xEnumAccess->createEnumeration() );
+ try
+ {
+ sal_Int32 nSkip = nIndex;
+ while ( nSkip > 0 )
+ {
+ (void) xEnum->nextElement();
+ --nSkip;
+ }
+ aRet = xEnum->nextElement();
+ }
+ catch (container::NoSuchElementException&)
+ {
+ // leave aRet empty
+ }
+ return aRet;
+}
+
+void ScXMLExport::collectAutoStyles()
+{
+ SvXMLExport::collectAutoStyles();
+
+ if (mbAutoStylesCollected)
+ return;
+
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ if (!xSpreadDoc.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
+ if (!xIndex.is())
+ return;
+
+ if (getExportFlags() & SvXMLExportFlags::CONTENT)
+ {
+ // Reserve the loaded cell style names.
+ RegisterDefinedStyleNames( xSpreadDoc);
+
+ // re-create automatic styles with old names from stored data
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetSheetSaveData();
+ if (pSheetData && pDoc)
+ {
+ // formulas have to be calculated now, to detect changed results
+ // (during normal save, they will be calculated anyway)
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
+ if (pDoc->IsStreamValid(nTab))
+ pDoc->InterpretDirtyCells(ScRange(0, 0, nTab, pDoc->MaxCol(), pDoc->MaxRow(), nTab));
+
+ // stored cell styles
+ const std::vector<ScCellStyleEntry>& rCellEntries = pSheetData->GetCellStyles();
+ for (const auto& rCellEntry : rCellEntries)
+ {
+ ScAddress aPos = rCellEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (bCopySheet)
+ {
+ uno::Reference <sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ uno::Reference <beans::XPropertySet> xProperties(
+ xTable->getCellByPosition( aPos.Col(), aPos.Row() ), uno::UNO_QUERY );
+
+ AddStyleFromCells(xProperties, xTable, nTable, &rCellEntry.maName);
+ }
+ }
+
+ // stored column styles
+ const std::vector<ScCellStyleEntry>& rColumnEntries = pSheetData->GetColumnStyles();
+ for (const auto& rColumnEntry : rColumnEntries)
+ {
+ ScAddress aPos = rColumnEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (bCopySheet)
+ {
+ uno::Reference<table::XColumnRowRange> xColumnRowRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ uno::Reference<table::XTableColumns> xTableColumns(xColumnRowRange->getColumns());
+ uno::Reference<beans::XPropertySet> xColumnProperties(xTableColumns->getByIndex( aPos.Col() ), uno::UNO_QUERY);
+
+ sal_Int32 nIndex(-1);
+ bool bIsVisible(true);
+ AddStyleFromColumn( xColumnProperties, &rColumnEntry.maName, nIndex, bIsVisible );
+ }
+ }
+
+ // stored row styles
+ const std::vector<ScCellStyleEntry>& rRowEntries = pSheetData->GetRowStyles();
+ for (const auto& rRowEntry : rRowEntries)
+ {
+ ScAddress aPos = rRowEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (bCopySheet)
+ {
+ uno::Reference<table::XColumnRowRange> xColumnRowRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ uno::Reference<table::XTableRows> xTableRows(xColumnRowRange->getRows());
+ uno::Reference<beans::XPropertySet> xRowProperties(xTableRows->getByIndex( aPos.Row() ), uno::UNO_QUERY);
+
+ sal_Int32 nIndex(-1);
+ AddStyleFromRow( xRowProperties, &rRowEntry.maName, nIndex );
+ }
+ }
+
+ // stored table styles
+ const std::vector<ScCellStyleEntry>& rTableEntries = pSheetData->GetTableStyles();
+ for (const auto& rTableEntry : rTableEntries)
+ {
+ ScAddress aPos = rTableEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (bCopySheet)
+ {
+ //! separate method AddStyleFromTable needed?
+ uno::Reference<beans::XPropertySet> xTableProperties(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ if (xTableProperties.is())
+ {
+ std::vector<XMLPropertyState> aPropStates(xTableStylesExportPropertySetMapper->Filter(*this, xTableProperties));
+ OUString sName( rTableEntry.maName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TABLE_TABLE, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_TABLE, sName);
+ }
+ }
+ }
+
+ // stored styles for notes
+
+ rtl::Reference<SvXMLExportPropertyMapper> xShapeMapper = XMLShapeExport::CreateShapePropMapper( *this );
+ GetShapeExport(); // make sure the graphics styles family is added
+
+ const std::vector<ScNoteStyleEntry>& rNoteEntries = pSheetData->GetNoteStyles();
+ for (const auto& rNoteEntry : rNoteEntries)
+ {
+ ScAddress aPos = rNoteEntry.maCellPos;
+ SCTAB nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( nTable );
+ if (bCopySheet)
+ {
+ //! separate method AddStyleFromNote needed?
+
+ ScPostIt* pNote = pDoc->GetNote(aPos);
+ OSL_ENSURE( pNote, "note not found" );
+ if (pNote)
+ {
+ SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
+ // all uno shapes are created anyway in CollectSharedData
+ uno::Reference<beans::XPropertySet> xShapeProperties( pDrawObj->getUnoShape(), uno::UNO_QUERY );
+ if (xShapeProperties.is())
+ {
+ if ( !rNoteEntry.maStyleName.isEmpty() )
+ {
+ std::vector<XMLPropertyState> aPropStates(xShapeMapper->Filter(*this, xShapeProperties));
+ OUString sName( rNoteEntry.maStyleName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::SD_GRAPHICS_ID, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::SD_GRAPHICS_ID, sName);
+ }
+ if ( !rNoteEntry.maTextStyle.isEmpty() )
+ {
+ std::vector<XMLPropertyState> aPropStates(
+ GetTextParagraphExport()->GetParagraphPropertyMapper()->Filter(*this, xShapeProperties));
+ OUString sName( rNoteEntry.maTextStyle );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_PARAGRAPH, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_PARAGRAPH, sName);
+ }
+ }
+ }
+ }
+ }
+
+ // note paragraph styles
+
+ rtl::Reference<SvXMLExportPropertyMapper> xParaPropMapper = GetTextParagraphExport()->GetParagraphPropertyMapper();
+
+ const std::vector<ScTextStyleEntry>& rNoteParaEntries = pSheetData->GetNoteParaStyles();
+ for (const auto& rNoteParaEntry : rNoteParaEntries)
+ {
+ ScAddress aPos = rNoteParaEntry.maCellPos;
+ SCTAB nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( nTable );
+ if (bCopySheet)
+ {
+ ScPostIt* pNote = pDoc->GetNote( aPos );
+ OSL_ENSURE( pNote, "note not found" );
+ if (pNote)
+ {
+ SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
+ uno::Reference<container::XEnumerationAccess> xCellText(pDrawObj->getUnoShape(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xParaProp(
+ lcl_GetEnumerated( xCellText, rNoteParaEntry.maSelection.nStartPara ), uno::UNO_QUERY );
+ if ( xParaProp.is() )
+ {
+ std::vector<XMLPropertyState> aPropStates(xParaPropMapper->Filter(*this, xParaProp));
+ OUString sName( rNoteParaEntry.maName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_PARAGRAPH, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_PARAGRAPH, sName);
+ }
+ }
+ }
+ }
+
+ // note text styles
+
+ rtl::Reference<SvXMLExportPropertyMapper> xTextPropMapper = XMLTextParagraphExport::CreateCharExtPropMapper( *this );
+
+ const std::vector<ScTextStyleEntry>& rNoteTextEntries = pSheetData->GetNoteTextStyles();
+ for (const auto& rNoteTextEntry : rNoteTextEntries)
+ {
+ ScAddress aPos = rNoteTextEntry.maCellPos;
+ SCTAB nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( nTable );
+ if (bCopySheet)
+ {
+ ScPostIt* pNote = pDoc->GetNote( aPos );
+ OSL_ENSURE( pNote, "note not found" );
+ if (pNote)
+ {
+ SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
+ uno::Reference<text::XSimpleText> xCellText(pDrawObj->getUnoShape(), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xCursorProp(xCellText->createTextCursor(), uno::UNO_QUERY);
+ ScDrawTextCursor* pCursor = comphelper::getFromUnoTunnel<ScDrawTextCursor>( xCursorProp );
+ if (pCursor)
+ {
+ pCursor->SetSelection( rNoteTextEntry.maSelection );
+
+ std::vector<XMLPropertyState> aPropStates(xTextPropMapper->Filter(*this, xCursorProp));
+ OUString sName( rNoteTextEntry.maName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_TEXT, sName);
+ }
+ }
+ }
+ }
+
+ // stored text styles
+
+ // Calling createTextCursor fires up editeng, which is very slow, and often subsequent style entries
+ // refer to the same cell, so cache it.
+ ScAddress aPrevPos;
+ uno::Reference<beans::XPropertySet> xPrevCursorProp;
+ const std::vector<ScTextStyleEntry>& rTextEntries = pSheetData->GetTextStyles();
+ for (const auto& rTextEntry : rTextEntries)
+ {
+ ScAddress aPos = rTextEntry.maCellPos;
+ sal_Int32 nTable = aPos.Tab();
+ bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
+ if (!bCopySheet)
+ continue;
+
+ //! separate method AddStyleFromText needed?
+ //! cache sheet object
+
+ uno::Reference<beans::XPropertySet> xCursorProp;
+ if (xPrevCursorProp && aPrevPos == aPos)
+ xCursorProp = xPrevCursorProp;
+ else
+ {
+ uno::Reference<table::XCellRange> xCellRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ uno::Reference<text::XSimpleText> xCellText(xCellRange->getCellByPosition(aPos.Col(), aPos.Row()), uno::UNO_QUERY);
+ xCursorProp.set(xCellText->createTextCursor(), uno::UNO_QUERY);
+ }
+ ScCellTextCursor* pCursor = comphelper::getFromUnoTunnel<ScCellTextCursor>( xCursorProp );
+ if (!pCursor)
+ continue;
+ pCursor->SetSelection( rTextEntry.maSelection );
+
+ std::vector<XMLPropertyState> aPropStates(xTextPropMapper->Filter(*this, xCursorProp));
+ OUString sName( rTextEntry.maName );
+ GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
+ GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_TEXT, sName);
+ xPrevCursorProp = xCursorProp;
+ aPrevPos = aPos;
+ }
+ }
+
+ ExportExternalRefCacheStyles();
+
+ if (!pSharedData)
+ {
+ SCTAB nTableCount(0);
+ sal_Int32 nShapesCount(0);
+ CollectSharedData(nTableCount, nShapesCount);
+ }
+ sal_Int32 nTableCount(xIndex->getCount());
+ pCellStyles->AddNewTable(nTableCount - 1);
+ CollectShapesAutoStyles(nTableCount);
+ for (sal_Int32 nTable = 0; nTable < nTableCount; ++nTable, IncrementProgressBar(false))
+ {
+ uno::Reference <sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
+ if (!xTable.is())
+ continue;
+
+ // table styles array must be complete, including copied tables - Add should find the stored style
+ uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
+ if (xTableProperties.is())
+ {
+ std::vector<XMLPropertyState> aPropStates(xTableStylesExportPropertySetMapper->Filter(*this, xTableProperties));
+ if(!aPropStates.empty())
+ {
+ OUString sName;
+ GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_TABLE, OUString(), std::move(aPropStates));
+ aTableStyles.push_back(sName);
+ }
+ }
+
+ // collect other auto-styles only for non-copied sheets
+ uno::Reference<sheet::XUniqueCellFormatRangesSupplier> xCellFormatRanges ( xTable, uno::UNO_QUERY );
+ if ( xCellFormatRanges.is() )
+ {
+ uno::Reference<container::XIndexAccess> xFormatRangesIndex(xCellFormatRanges->getUniqueCellFormatRanges());
+ if (xFormatRangesIndex.is())
+ {
+ sal_Int32 nFormatRangesCount(xFormatRangesIndex->getCount());
+ GetProgressBarHelper()->ChangeReference(GetProgressBarHelper()->GetReference() + nFormatRangesCount);
+ for (sal_Int32 nFormatRange = 0; nFormatRange < nFormatRangesCount; ++nFormatRange)
+ {
+ uno::Reference< sheet::XSheetCellRanges> xCellRanges(xFormatRangesIndex->getByIndex(nFormatRange), uno::UNO_QUERY);
+ if (xCellRanges.is())
+ {
+ uno::Reference <beans::XPropertySet> xProperties (xCellRanges, uno::UNO_QUERY);
+ if (xProperties.is())
+ {
+ AddStyleFromCells(xProperties, xTable, nTable, nullptr);
+ IncrementProgressBar(false);
+ }
+ }
+ }
+ }
+ }
+ uno::Reference<table::XColumnRowRange> xColumnRowRange (xTable, uno::UNO_QUERY);
+ if (xColumnRowRange.is() && pDoc)
+ {
+ pDoc->SyncColRowFlags();
+ uno::Reference<table::XTableColumns> xTableColumns(xColumnRowRange->getColumns());
+ if (xTableColumns.is())
+ {
+ sal_Int32 nColumns(pDoc->GetLastChangedColFlagsWidth(sal::static_int_cast<SCTAB>(nTable)));
+ pSharedData->SetLastColumn(nTable, nColumns);
+ table::CellRangeAddress aCellAddress(GetEndAddress(xTable));
+ if (aCellAddress.EndColumn > nColumns)
+ {
+ ++nColumns;
+ pColumnStyles->AddNewTable(nTable, aCellAddress.EndColumn);
+ }
+ else
+ pColumnStyles->AddNewTable(nTable, nColumns);
+ sal_Int32 nColumn = 0;
+ while (nColumn <= pDoc->MaxCol())
+ {
+ sal_Int32 nIndex(-1);
+ bool bIsVisible(true);
+ uno::Reference <beans::XPropertySet> xColumnProperties(xTableColumns->getByIndex(nColumn), uno::UNO_QUERY);
+ if (xColumnProperties.is())
+ {
+ AddStyleFromColumn( xColumnProperties, nullptr, nIndex, bIsVisible );
+ pColumnStyles->AddFieldStyleName(nTable, nColumn, nIndex, bIsVisible);
+ }
+ sal_Int32 nOld(nColumn);
+ nColumn = pDoc->GetNextDifferentChangedColFlagsWidth(sal::static_int_cast<SCTAB>(nTable), static_cast<SCCOL>(nColumn));
+ for (sal_Int32 i = nOld + 1; i < nColumn; ++i)
+ pColumnStyles->AddFieldStyleName(nTable, i, nIndex, bIsVisible);
+ }
+ if (aCellAddress.EndColumn > nColumns)
+ {
+ bool bIsVisible(true);
+ sal_Int32 nIndex(pColumnStyles->GetStyleNameIndex(nTable, nColumns, bIsVisible));
+ for (sal_Int32 i = nColumns + 1; i <= aCellAddress.EndColumn; ++i)
+ pColumnStyles->AddFieldStyleName(nTable, i, nIndex, bIsVisible);
+ }
+ }
+ uno::Reference<table::XTableRows> xTableRows(xColumnRowRange->getRows());
+ if (xTableRows.is())
+ {
+ sal_Int32 nRows(pDoc->GetLastChangedRowFlagsWidth(sal::static_int_cast<SCTAB>(nTable)));
+ pSharedData->SetLastRow(nTable, nRows);
+
+ pRowStyles->AddNewTable(nTable, pDoc->MaxRow());
+ sal_Int32 nRow = 0;
+ while (nRow <= pDoc->MaxRow())
+ {
+ sal_Int32 nIndex = 0;
+ uno::Reference <beans::XPropertySet> xRowProperties(xTableRows->getByIndex(nRow), uno::UNO_QUERY);
+ if(xRowProperties.is())
+ {
+ AddStyleFromRow( xRowProperties, nullptr, nIndex );
+ pRowStyles->AddFieldStyleName(nTable, nRow, nIndex);
+ }
+ sal_Int32 nOld(nRow);
+ nRow = pDoc->GetNextDifferentChangedRowFlagsWidth(sal::static_int_cast<SCTAB>(nTable), static_cast<SCROW>(nRow));
+ if (nRow > nOld + 1)
+ pRowStyles->AddFieldStyleName(nTable, nOld + 1, nIndex, nRow - 1);
+ }
+ }
+ }
+ ExportCellTextAutoStyles(nTable);
+ }
+
+ pChangeTrackingExportHelper->CollectAutoStyles();
+ }
+
+ if (getExportFlags() & SvXMLExportFlags::MASTERSTYLES)
+ GetPageExport()->collectAutoStyles(true);
+
+ mbAutoStylesCollected = true;
+}
+
+void ScXMLExport::ExportAutoStyles_()
+{
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
+ if (!xSpreadDoc.is())
+ return;
+
+ uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
+ if (!xIndex.is())
+ return;
+
+ collectAutoStyles();
+
+ if (getExportFlags() & SvXMLExportFlags::CONTENT)
+ {
+ GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_COLUMN);
+ GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_ROW);
+ GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_TABLE);
+ exportAutoDataStyles();
+ GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_CELL);
+
+ GetShapeExport()->exportAutoStyles();
+ GetFormExport()->exportAutoStyles( );
+
+ if (pDoc)
+ {
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ // #i100879# write the table style for cached tables only if there are cached tables
+ // (same logic as in ExportExternalRefCacheStyles)
+ if (pRefMgr->hasExternalData())
+ {
+ // Special table style for the external ref cache tables.
+ AddAttribute(XML_NAMESPACE_STYLE, XML_NAME, sExternalRefTabStyleName);
+ AddAttribute(XML_NAMESPACE_STYLE, XML_FAMILY, XML_TABLE);
+ SvXMLElementExport aElemStyle(*this, XML_NAMESPACE_STYLE, XML_STYLE, true, true);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY, XML_FALSE);
+ SvXMLElementExport aElemStyleTabProps(*this, XML_NAMESPACE_STYLE, XML_TABLE_PROPERTIES, true, true);
+ }
+ }
+ }
+
+ if (getExportFlags() & SvXMLExportFlags::MASTERSTYLES)
+ {
+ exportAutoDataStyles();
+ GetPageExport()->exportAutoStyles();
+ }
+
+ // #i30251#; only write Text Styles once
+
+ if ((getExportFlags() & SvXMLExportFlags::CONTENT) || (getExportFlags() & SvXMLExportFlags::MASTERSTYLES))
+ GetTextParagraphExport()->exportTextAutoStyles();
+}
+
+void ScXMLExport::ExportMasterStyles_()
+{
+ GetPageExport()->exportMasterStyles( true );
+}
+
+void ScXMLExport::CollectInternalShape( uno::Reference< drawing::XShape > const & xShape )
+{
+ // detective objects and notes
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape( xShape );
+ if( !pObject )
+ return;
+
+ // collect note caption objects from all layers (internal or hidden)
+ if( ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObject, static_cast< SCTAB >( nCurrentTable ) ) )
+ {
+ if(pDoc->GetNote(pCaptData->maStart))
+ {
+ pSharedData->AddNoteObj( xShape, pCaptData->maStart );
+
+ // #i60851# When the file is saved while editing a new note,
+ // the cell is still empty -> last column/row must be updated
+ OSL_ENSURE( pCaptData->maStart.Tab() == nCurrentTable, "invalid table in object data" );
+ pSharedData->SetLastColumn( nCurrentTable, pCaptData->maStart.Col() );
+ pSharedData->SetLastRow( nCurrentTable, pCaptData->maStart.Row() );
+ }
+ }
+ // other objects from internal layer only (detective)
+ else if( pObject->GetLayer() == SC_LAYER_INTERN )
+ {
+ ScDetectiveFunc aDetFunc( *pDoc, static_cast<SCTAB>(nCurrentTable) );
+ ScAddress aPosition;
+ ScRange aSourceRange;
+ bool bRedLine;
+ ScDetectiveObjType eObjType = aDetFunc.GetDetectiveObjectType(
+ pObject, nCurrentTable, aPosition, aSourceRange, bRedLine );
+ pSharedData->GetDetectiveObjContainer()->AddObject( eObjType, static_cast<SCTAB>(nCurrentTable), aPosition, aSourceRange, bRedLine );
+ }
+}
+
+bool ScXMLExport::GetMerged (const table::CellRangeAddress* pCellAddress,
+ const uno::Reference <sheet::XSpreadsheet>& xTable)
+{
+ bool bReady(false);
+ sal_Int32 nRow(pCellAddress->StartRow);
+ sal_Int32 nCol(pCellAddress->StartColumn);
+ sal_Int32 nEndRow(pCellAddress->EndRow);
+ sal_Int32 nEndCol(pCellAddress->EndColumn);
+ bool bRowInc(nEndRow > nRow);
+ while(!bReady && nRow <= nEndRow && nCol <= nEndCol)
+ {
+ uno::Reference<sheet::XSheetCellRange> xSheetCellRange(xTable->getCellRangeByPosition(nCol, nRow, nCol, nRow), uno::UNO_QUERY);
+ if (xSheetCellRange.is())
+ {
+ uno::Reference<sheet::XSheetCellCursor> xCursor(xTable->createCursorByRange(xSheetCellRange));
+ if(xCursor.is())
+ {
+ uno::Reference<sheet::XCellRangeAddressable> xCellAddress (xCursor, uno::UNO_QUERY);
+ xCursor->collapseToMergedArea();
+ table::CellRangeAddress aCellAddress2(xCellAddress->getRangeAddress());
+ ScRange aScRange( aCellAddress2.StartColumn, aCellAddress2.StartRow, aCellAddress2.Sheet,
+ aCellAddress2.EndColumn, aCellAddress2.EndRow, aCellAddress2.Sheet );
+
+ if ((aScRange.aEnd.Row() > nRow ||
+ aScRange.aEnd.Col() > nCol) &&
+ aScRange.aStart.Row() == nRow &&
+ aScRange.aStart.Col() == nCol)
+ {
+ pMergedRangesContainer->AddRange(aScRange);
+ pSharedData->SetLastColumn(aScRange.aEnd.Tab(), aScRange.aEnd.Col());
+ pSharedData->SetLastRow(aScRange.aEnd.Tab(), aScRange.aEnd.Row());
+ }
+ else
+ bReady = true;
+ }
+ }
+ if (!bReady)
+ {
+ if (bRowInc)
+ ++nRow;
+ else
+ ++nCol;
+ }
+ }
+ OSL_ENSURE(!(!bReady && nEndRow > nRow && nEndCol > nCol), "should not be possible");
+ return !bReady;
+}
+
+bool ScXMLExport::IsMatrix (const ScAddress& aCell,
+ ScRange& aCellAddress, bool& bIsFirst) const
+{
+ bIsFirst = false;
+
+ ScRange aMatrixRange;
+
+ if (pDoc && pDoc->GetMatrixFormulaRange(aCell, aMatrixRange))
+ {
+ aCellAddress = aMatrixRange;
+ if ((aCellAddress.aStart.Col() == aCell.Col() && aCellAddress.aStart.Row() == aCell.Row()) &&
+ (aCellAddress.aEnd.Col() > aCell.Col() || aCellAddress.aEnd.Row() > aCell.Row()))
+ {
+ bIsFirst = true;
+ return true;
+ }
+ else if (aCellAddress.aStart.Col() != aCell.Col() || aCellAddress.aStart.Row() != aCell.Row() ||
+ aCellAddress.aEnd.Col() != aCell.Col() || aCellAddress.aEnd.Row()!= aCell.Row())
+ return true;
+ else
+ {
+ bIsFirst = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScXMLExport::WriteTable(sal_Int32 nTable, const uno::Reference<sheet::XSpreadsheet>& xTable)
+{
+ if (!xTable.is())
+ return;
+
+ xCurrentTable.set(xTable);
+ uno::Reference<container::XNamed> xName (xTable, uno::UNO_QUERY );
+ if (!xName.is())
+ return;
+
+ nCurrentTable = sal::static_int_cast<sal_uInt16>( nTable );
+ OUString sOUTableName(xName->getName());
+ AddAttribute(sAttrName, sOUTableName);
+ AddAttribute(sAttrStyleName, aTableStyles[nTable]);
+
+ uno::Reference<util::XProtectable> xProtectable (xTable, uno::UNO_QUERY);
+ const ScTableProtection* pProtect = nullptr;
+ if (xProtectable.is() && xProtectable->isProtected())
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE);
+ if (pDoc)
+ {
+ pProtect = pDoc->GetTabProtection(nTable);
+ if (pProtect)
+ {
+ OUStringBuffer aBuffer;
+ ScPasswordHash eHashUsed = PASSHASH_UNSPECIFIED;
+ if (pProtect->hasPasswordHash(PASSHASH_SHA1))
+ {
+ ::comphelper::Base64::encode(aBuffer,
+ pProtect->getPasswordHash(PASSHASH_SHA1));
+ eHashUsed = PASSHASH_SHA1;
+ }
+ else if (pProtect->hasPasswordHash(PASSHASH_SHA256))
+ {
+ ::comphelper::Base64::encode(aBuffer,
+ pProtect->getPasswordHash(PASSHASH_SHA256));
+ eHashUsed = PASSHASH_SHA256;
+ }
+ else if (pProtect->hasPasswordHash(PASSHASH_XL, PASSHASH_SHA1))
+ {
+ // Double-hash this by SHA1 on top of the legacy xls hash.
+ uno::Sequence<sal_Int8> aHash = pProtect->getPasswordHash(PASSHASH_XL, PASSHASH_SHA1);
+ ::comphelper::Base64::encode(aBuffer, aHash);
+ eHashUsed = PASSHASH_XL;
+ }
+ if (!aBuffer.isEmpty())
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY, aBuffer.makeStringAndClear());
+ if (getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
+ {
+ if (eHashUsed == PASSHASH_XL)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_XL));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+ }
+ else if (eHashUsed == PASSHASH_SHA1)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA1));
+ }
+ else if (eHashUsed == PASSHASH_SHA256)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
+ ScPassHashHelper::getHashURI(PASSHASH_SHA256));
+ }
+ }
+ }
+ }
+ }
+ }
+ OUString sPrintRanges;
+ ScRange aColumnHeaderRange;
+ bool bHasColumnHeader;
+ GetColumnRowHeader(bHasColumnHeader, aColumnHeaderRange, bHasRowHeader, aRowHeaderRange, sPrintRanges);
+ if( !sPrintRanges.isEmpty() )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_PRINT_RANGES, sPrintRanges );
+ else if (pDoc && !pDoc->IsPrintEntireSheet(static_cast<SCTAB>(nTable)))
+ AddAttribute( XML_NAMESPACE_TABLE, XML_PRINT, XML_FALSE);
+ SvXMLElementExport aElemT(*this, sElemTab, true, true);
+
+ if (pProtect && pProtect->isProtected() && getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ if (pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_SELECT_PROTECTED_CELLS, XML_TRUE);
+ if (pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_SELECT_UNPROTECTED_CELLS, XML_TRUE);
+
+ if (pProtect->isOptionEnabled(ScTableProtection::INSERT_COLUMNS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_INSERT_COLUMNS, XML_TRUE);
+ if (pProtect->isOptionEnabled(ScTableProtection::INSERT_ROWS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_INSERT_ROWS, XML_TRUE);
+
+ if (pProtect->isOptionEnabled(ScTableProtection::DELETE_COLUMNS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_DELETE_COLUMNS, XML_TRUE);
+ if (pProtect->isOptionEnabled(ScTableProtection::DELETE_ROWS))
+ AddAttribute(XML_NAMESPACE_LO_EXT, XML_DELETE_ROWS, XML_TRUE);
+
+ OUString aElemName = GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_LO_EXT, GetXMLToken(XML_TABLE_PROTECTION));
+
+ SvXMLElementExport aElemProtected(*this, aElemName, true, true);
+ }
+
+ CheckAttrList();
+
+ if ( pDoc && pDoc->GetSheetEvents( static_cast<SCTAB>(nTable) ) &&
+ getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
+ {
+ // store sheet events
+ uno::Reference<document::XEventsSupplier> xSupplier(xTable, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xEvents = xSupplier->getEvents();
+ GetEventExport().ExportExt( xEvents );
+ }
+
+ WriteTableSource();
+ WriteScenario();
+ uno::Reference<drawing::XDrawPage> xDrawPage;
+ if (pSharedData->HasForm(nTable, xDrawPage) && xDrawPage.is())
+ {
+ ::xmloff::OOfficeFormsExport aForms(*this);
+ GetFormExport()->exportForms( xDrawPage );
+ bool bRet(GetFormExport()->seekPage( xDrawPage ));
+ OSL_ENSURE( bRet, "OFormLayerXMLExport::seekPage failed!" );
+ }
+ if (pSharedData->HasDrawPage())
+ {
+ GetShapeExport()->seekShapes(pSharedData->GetDrawPage(nTable));
+ WriteTableShapes();
+ }
+ table::CellRangeAddress aRange(GetEndAddress(xTable));
+ pSharedData->SetLastColumn(nTable, aRange.EndColumn);
+ pSharedData->SetLastRow(nTable, aRange.EndRow);
+ mpCellsItr->SetCurrentTable(static_cast<SCTAB>(nTable), xCurrentTable);
+ pGroupColumns->NewTable();
+ pGroupRows->NewTable();
+ FillColumnRowGroups();
+ if (bHasColumnHeader)
+ pSharedData->SetLastColumn(nTable, aColumnHeaderRange.aEnd.Col());
+ bRowHeaderOpen = false;
+ if (bHasRowHeader)
+ pSharedData->SetLastRow(nTable, aRowHeaderRange.aEnd.Row());
+ pDefaults->FillDefaultStyles(nTable, pSharedData->GetLastRow(nTable),
+ pSharedData->GetLastColumn(nTable), pCellStyles.get(), pDoc);
+ pRowFormatRanges->SetColDefaults(&pDefaults->GetColDefaults());
+ pCellStyles->SetColDefaults(&pDefaults->GetColDefaults());
+ ExportColumns(nTable, aColumnHeaderRange, bHasColumnHeader);
+ bool bIsFirst(true);
+ sal_Int32 nEqualCells(0);
+ ScMyCell aCell;
+ ScMyCell aPrevCell;
+ while (mpCellsItr->GetNext(aCell, pCellStyles.get()))
+ {
+ if (bIsFirst)
+ {
+ ExportFormatRanges(0, 0, aCell.maCellAddress.Col()-1, aCell.maCellAddress.Row(), nTable);
+ aPrevCell = aCell;
+ bIsFirst = false;
+ }
+ else
+ {
+ if ((aPrevCell.maCellAddress.Row() == aCell.maCellAddress.Row()) &&
+ (aPrevCell.maCellAddress.Col() + nEqualCells + 1 == aCell.maCellAddress.Col()))
+ {
+ if(IsCellEqual(aPrevCell, aCell))
+ ++nEqualCells;
+ else
+ {
+ WriteCell(aPrevCell, nEqualCells);
+ nEqualCells = 0;
+ aPrevCell = aCell;
+ }
+ }
+ else
+ {
+ WriteCell(aPrevCell, nEqualCells);
+ ExportFormatRanges(aPrevCell.maCellAddress.Col() + nEqualCells + 1, aPrevCell.maCellAddress.Row(),
+ aCell.maCellAddress.Col()-1, aCell.maCellAddress.Row(), nTable);
+ nEqualCells = 0;
+ aPrevCell = aCell;
+ }
+ }
+ }
+ if (!bIsFirst)
+ {
+ WriteCell(aPrevCell, nEqualCells);
+ ExportFormatRanges(aPrevCell.maCellAddress.Col() + nEqualCells + 1, aPrevCell.maCellAddress.Row(),
+ pSharedData->GetLastColumn(nTable), pSharedData->GetLastRow(nTable), nTable);
+ }
+ else
+ ExportFormatRanges(0, 0, pSharedData->GetLastColumn(nTable), pSharedData->GetLastRow(nTable), nTable);
+
+ CloseRow(pSharedData->GetLastRow(nTable));
+
+ if (!pDoc)
+ return;
+
+ // Export sheet-local named ranges.
+ ScRangeName* pRangeName = pDoc->GetRangeName(nTable);
+ if (pRangeName && !pRangeName->empty())
+ {
+ WriteNamedRange(pRangeName);
+ }
+
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ //export new conditional format information
+ ExportConditionalFormat(nTable);
+ exportSparklineGroups(nTable);
+ }
+}
+
+namespace {
+
+void writeContent(
+ ScXMLExport& rExport, const OUString& rStyleName, const OUString& rContent, const SvxFieldData* pField )
+{
+ std::unique_ptr<SvXMLElementExport> pElem;
+ if (!rStyleName.isEmpty())
+ {
+ // Formatted section with automatic style.
+ rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_STYLE_NAME, rStyleName);
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_SPAN));
+ pElem.reset(new SvXMLElementExport(rExport, aElemName, false, false));
+ }
+
+ if (pField)
+ {
+ // Write a field item.
+ OUString aFieldVal = ScEditUtil::GetCellFieldValue(*pField, rExport.GetDocument(), nullptr);
+ switch (pField->GetClassId())
+ {
+ case text::textfield::Type::URL:
+ {
+ // <text:a xlink:href="url" xlink:type="simple">value</text:a>
+
+ const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
+ const OUString& aURL = pURLField->GetURL();
+ rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, rExport.GetRelativeReference(aURL));
+ rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, "simple");
+ const OUString& aTargetFrame = pURLField->GetTargetFrame();
+ if (!aTargetFrame.isEmpty())
+ rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME, aTargetFrame);
+
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_A));
+ SvXMLElementExport aElem(rExport, aElemName, false, false);
+ rExport.Characters(aFieldVal);
+ }
+ break;
+ case text::textfield::Type::DATE:
+ {
+ // <text:date style:data-style-name="N2" text:date-value="YYYY-MM-DD">value</text:date>
+
+ Date aDate(Date::SYSTEM);
+ OUStringBuffer aBuf;
+ sal_Int32 nVal = aDate.GetYear();
+ aBuf.append(nVal);
+ aBuf.append('-');
+ nVal = aDate.GetMonth();
+ if (nVal < 10)
+ aBuf.append('0');
+ aBuf.append(nVal);
+ aBuf.append('-');
+ nVal = aDate.GetDay();
+ if (nVal < 10)
+ aBuf.append('0');
+ aBuf.append(nVal);
+ rExport.AddAttribute(XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, "N2");
+ rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_DATE_VALUE, aBuf.makeStringAndClear());
+
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_DATE));
+ SvXMLElementExport aElem(rExport, aElemName, false, false);
+ rExport.Characters(aFieldVal);
+ }
+ break;
+ case text::textfield::Type::DOCINFO_TITLE:
+ {
+ // <text:title>value</text:title>
+
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_TITLE));
+ SvXMLElementExport aElem(rExport, aElemName, false, false);
+ rExport.Characters(aFieldVal);
+ }
+ break;
+ case text::textfield::Type::TABLE:
+ {
+ // <text:sheet-name>value</text:sheet-name>
+
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_SHEET_NAME));
+ SvXMLElementExport aElem(rExport, aElemName, false, false);
+ rExport.Characters(aFieldVal);
+ }
+ break;
+ default:
+ rExport.Characters(aFieldVal);
+ }
+ }
+ else
+ rExport.Characters(rContent);
+}
+
+void flushParagraph(
+ ScXMLExport& rExport, std::u16string_view rParaText,
+ rtl::Reference<XMLPropertySetMapper> const & xMapper, rtl::Reference<SvXMLAutoStylePoolP> const & xStylePool,
+ const ScXMLEditAttributeMap& rAttrMap,
+ std::vector<editeng::Section>::const_iterator it, std::vector<editeng::Section>::const_iterator const & itEnd )
+{
+ OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
+ XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
+ SvXMLElementExport aElemP(rExport, aElemName, false, false);
+
+ for (; it != itEnd; ++it)
+ {
+ const editeng::Section& rSec = *it;
+
+ OUString aContent(rParaText.substr(rSec.mnStart, rSec.mnEnd - rSec.mnStart));
+
+ std::vector<XMLPropertyState> aPropStates;
+ const SvxFieldData* pField = toXMLPropertyStates(rExport, aPropStates, rSec.maAttributes, xMapper, rAttrMap);
+ OUString aStyleName = xStylePool->Find(XmlStyleFamily::TEXT_TEXT, OUString(), aPropStates);
+ writeContent(rExport, aStyleName, aContent, pField);
+ }
+}
+
+}
+
+void ScXMLExport::WriteCell(ScMyCell& aCell, sal_Int32 nEqualCellCount)
+{
+ // nEqualCellCount is the number of additional cells
+ SetRepeatAttribute(nEqualCellCount, (aCell.nType != table::CellContentType_EMPTY));
+
+ if (aCell.nStyleIndex != -1)
+ AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(aCell.nStyleIndex, aCell.bIsAutoStyle));
+ if (aCell.nValidationIndex > -1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(aCell.nValidationIndex));
+ const bool bIsFirstMatrixCell(aCell.bIsMatrixBase);
+ if (bIsFirstMatrixCell)
+ {
+ SCCOL nColumns( aCell.aMatrixRange.aEnd.Col() - aCell.aMatrixRange.aStart.Col() + 1 );
+ SCROW nRows( aCell.aMatrixRange.aEnd.Row() - aCell.aMatrixRange.aStart.Row() + 1 );
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED, OUString::number(nColumns));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED, OUString::number(nRows));
+ }
+ bool bIsEmpty(false);
+ switch (aCell.nType)
+ {
+ case table::CellContentType_EMPTY :
+ {
+ bIsEmpty = true;
+ }
+ break;
+ case table::CellContentType_VALUE :
+ {
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ aCell.nNumberFormat, aCell.maBaseCell.mfValue);
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ aCell.nNumberFormat, aCell.maBaseCell.mfValue, false, XML_NAMESPACE_CALC_EXT, false);
+ }
+ break;
+ case table::CellContentType_TEXT :
+ {
+ OUString sFormattedString(lcl_GetFormattedString(pDoc, aCell.maBaseCell, aCell.maCellAddress));
+ OUString sCellString = aCell.maBaseCell.getString(pDoc);
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ sCellString, sFormattedString);
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ sCellString, sFormattedString, false, XML_NAMESPACE_CALC_EXT);
+ }
+ break;
+ case table::CellContentType_FORMULA :
+ {
+ if (aCell.maBaseCell.meType == CELLTYPE_FORMULA)
+ {
+ const bool bIsMatrix(bIsFirstMatrixCell || aCell.bIsMatrixCovered);
+ ScFormulaCell* pFormulaCell = aCell.maBaseCell.mpFormula;
+ if (!bIsMatrix || bIsFirstMatrixCell)
+ {
+ if (!mpCompileFormulaCxt)
+ {
+ const formula::FormulaGrammar::Grammar eGrammar = pDoc->GetStorageGrammar();
+ mpCompileFormulaCxt.reset(new sc::CompileFormulaContext(*pDoc, eGrammar));
+ }
+
+ OUString aFormula = pFormulaCell->GetFormula(*mpCompileFormulaCxt);
+ sal_uInt16 nNamespacePrefix =
+ (mpCompileFormulaCxt->getGrammar() == formula::FormulaGrammar::GRAM_ODFF ? XML_NAMESPACE_OF : XML_NAMESPACE_OOOC);
+
+ if (!bIsMatrix)
+ {
+ AddAttribute(sAttrFormula, GetNamespaceMap().GetQNameByKey(nNamespacePrefix, aFormula, false));
+ }
+ else
+ {
+ AddAttribute(sAttrFormula, GetNamespaceMap().GetQNameByKey(nNamespacePrefix, aFormula.copy(1, aFormula.getLength()-2), false));
+ }
+ }
+ if (pFormulaCell->GetErrCode() != FormulaError::NONE)
+ {
+ AddAttribute(sAttrValueType, XML_STRING);
+ AddAttribute(sAttrStringValue, aCell.maBaseCell.getString(pDoc));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ //export calcext:value-type="error"
+ AddAttribute(XML_NAMESPACE_CALC_EXT,XML_VALUE_TYPE, OUString("error"));
+ }
+ }
+ else if (pFormulaCell->IsValue())
+ {
+ bool bIsStandard;
+ OUString sCurrency;
+ GetNumberFormatAttributesExportHelper()->GetCellType(aCell.nNumberFormat, sCurrency, bIsStandard);
+ if (pDoc)
+ {
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ aCell.nNumberFormat, pDoc->GetValue(aCell.maCellAddress));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
+ aCell.nNumberFormat, pDoc->GetValue(aCell.maCellAddress), false, XML_NAMESPACE_CALC_EXT, false );
+ }
+ }
+ }
+ else
+ {
+ if (!aCell.maBaseCell.getString(pDoc).isEmpty())
+ {
+ AddAttribute(sAttrValueType, XML_STRING);
+ AddAttribute(sAttrStringValue, aCell.maBaseCell.getString(pDoc));
+ if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
+ {
+ AddAttribute(XML_NAMESPACE_CALC_EXT,XML_VALUE_TYPE, XML_STRING);
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ OUString* pCellString(&sElemCell);
+ if (aCell.bIsCovered)
+ {
+ pCellString = &sElemCoveredCell;
+ }
+ else
+ {
+ if (aCell.bIsMergedBase)
+ {
+ SCCOL nColumns( aCell.aMergeRange.aEnd.Col() - aCell.aMergeRange.aStart.Col() + 1 );
+ SCROW nRows( aCell.aMergeRange.aEnd.Row() - aCell.aMergeRange.aStart.Row() + 1 );
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED, OUString::number(nColumns));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED, OUString::number(nRows));
+ }
+ }
+ SvXMLElementExport aElemC(*this, *pCellString, true, true);
+ CheckAttrList();
+ WriteAreaLink(aCell);
+ WriteAnnotation(aCell);
+ WriteDetective(aCell);
+
+ if (!bIsEmpty)
+ {
+ if (aCell.maBaseCell.meType == CELLTYPE_EDIT)
+ {
+ WriteEditCell(aCell.maBaseCell.mpEditText);
+ }
+ else if (aCell.maBaseCell.meType == CELLTYPE_FORMULA && aCell.maBaseCell.mpFormula->IsMultilineResult())
+ {
+ WriteMultiLineFormulaResult(aCell.maBaseCell.mpFormula);
+ }
+ else
+ {
+ SvXMLElementExport aElemP(*this, sElemP, true, false);
+
+ OUString aParaStr =
+ ScCellFormat::GetOutputString(*pDoc, aCell.maCellAddress, aCell.maBaseCell);
+
+ bool bPrevCharWasSpace = true;
+ GetTextParagraphExport()->exportCharacterData(aParaStr, bPrevCharWasSpace);
+ }
+ }
+ WriteShapes(aCell);
+ if (!bIsEmpty)
+ IncrementProgressBar(false);
+}
+
+void ScXMLExport::WriteEditCell(const EditTextObject* pText)
+{
+ rtl::Reference<XMLPropertySetMapper> xMapper = GetTextParagraphExport()->GetTextPropMapper()->getPropertySetMapper();
+ rtl::Reference<SvXMLAutoStylePoolP> xStylePool = GetAutoStylePool();
+ const ScXMLEditAttributeMap& rAttrMap = GetEditAttributeMap();
+
+ // Get raw paragraph texts first.
+ std::vector<OUString> aParaTexts;
+ sal_Int32 nParaCount = pText->GetParagraphCount();
+ aParaTexts.reserve(nParaCount);
+ for (sal_Int32 i = 0; i < nParaCount; ++i)
+ aParaTexts.push_back(pText->GetText(i));
+
+ // Get all section data and iterate through them.
+ std::vector<editeng::Section> aAttrs;
+ pText->GetAllSections(aAttrs);
+ std::vector<editeng::Section>::const_iterator itSec = aAttrs.begin(), itSecEnd = aAttrs.end();
+ std::vector<editeng::Section>::const_iterator itPara = itSec;
+ sal_Int32 nCurPara = 0; // current paragraph
+ for (; itSec != itSecEnd; ++itSec)
+ {
+ const editeng::Section& rSec = *itSec;
+ if (nCurPara == rSec.mnParagraph)
+ // Still in the same paragraph.
+ continue;
+
+ // Start of a new paragraph. Flush the old paragraph.
+ flushParagraph(*this, aParaTexts[nCurPara], xMapper, xStylePool, rAttrMap, itPara, itSec);
+ nCurPara = rSec.mnParagraph;
+ itPara = itSec;
+ }
+
+ flushParagraph(*this, aParaTexts[nCurPara], xMapper, xStylePool, rAttrMap, itPara, itSecEnd);
+}
+
+void ScXMLExport::WriteMultiLineFormulaResult(const ScFormulaCell* pCell)
+{
+ OUString aElemName = GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
+
+ OUString aResStr = pCell->GetResultString().getString();
+ const sal_Unicode* p = aResStr.getStr();
+ const sal_Unicode* pEnd = p + static_cast<size_t>(aResStr.getLength());
+ const sal_Unicode* pPara = p; // paragraph head.
+ for (; p != pEnd; ++p)
+ {
+ if (*p != '\n')
+ continue;
+
+ // flush the paragraph.
+ OUString aContent;
+ if (*pPara == '\n')
+ ++pPara;
+ if (p > pPara)
+ aContent = OUString(pPara, p-pPara);
+
+ SvXMLElementExport aElem(*this, aElemName, false, false);
+ Characters(aContent);
+
+ pPara = p;
+ }
+
+ OUString aContent;
+ if (*pPara == '\n')
+ ++pPara;
+ if (pEnd > pPara)
+ aContent = OUString(pPara, pEnd-pPara);
+
+ SvXMLElementExport aElem(*this, aElemName, false, false);
+ Characters(aContent);
+}
+
+void ScXMLExport::ExportShape(const uno::Reference < drawing::XShape >& xShape, awt::Point* pPoint)
+{
+ uno::Reference < beans::XPropertySet > xShapeProps ( xShape, uno::UNO_QUERY );
+ bool bIsChart( false );
+ if (xShapeProps.is())
+ {
+ sal_Int32 nZOrder = 0;
+ if (xShapeProps->getPropertyValue("ZOrder") >>= nZOrder)
+ {
+ AddAttribute(XML_NAMESPACE_DRAW, XML_ZINDEX, OUString::number(nZOrder));
+ }
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xShapeProps->getPropertySetInfo();
+ OUString sPropCLSID ("CLSID");
+ if( xPropSetInfo->hasPropertyByName( sPropCLSID ) )
+ {
+ OUString sCLSID;
+ if (xShapeProps->getPropertyValue( sPropCLSID ) >>= sCLSID)
+ {
+ if ( sCLSID.equalsIgnoreAsciiCase(GetChartExport()->getChartCLSID()) )
+ {
+ // we have a chart
+ OUString sRanges;
+ if ( pDoc )
+ {
+ OUString aChartName;
+ xShapeProps->getPropertyValue( "PersistName" ) >>= aChartName;
+ ScChartListenerCollection* pCollection = pDoc->GetChartListenerCollection();
+ if (pCollection)
+ {
+ ScChartListener* pListener = pCollection->findByName(aChartName);
+ if (pListener)
+ {
+ const ScRangeListRef& rRangeList = pListener->GetRangeList();
+ if ( rRangeList.is() )
+ {
+ ScRangeStringConverter::GetStringFromRangeList( sRanges, rRangeList.get(), pDoc, FormulaGrammar::CONV_OOO );
+ if ( !sRanges.isEmpty() )
+ {
+ bIsChart = true;
+ rtl::Reference<SvXMLAttributeList> pAttrList = new SvXMLAttributeList();
+ pAttrList->AddAttribute(
+ GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_DRAW, GetXMLToken( XML_NOTIFY_ON_UPDATE_OF_RANGES ) ), sRanges );
+ GetShapeExport()->exportShape( xShape, SEF_DEFAULT, pPoint, pAttrList.get() );
+ }
+ }
+ }
+ }
+ }
+
+ if ( sRanges.isEmpty() )
+ {
+ uno::Reference< frame::XModel > xChartModel;
+ if( ( xShapeProps->getPropertyValue( "Model" ) >>= xChartModel ) &&
+ xChartModel.is())
+ {
+ uno::Reference< chart2::XChartDocument > xChartDoc( xChartModel, uno::UNO_QUERY );
+ uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartModel, uno::UNO_QUERY );
+ if( xChartDoc.is() && xReceiver.is() &&
+ ! xChartDoc->hasInternalDataProvider())
+ {
+ // we have a chart that gets its data from Calc
+ bIsChart = true;
+ uno::Sequence< OUString > aRepresentations(
+ xReceiver->getUsedRangeRepresentations());
+ rtl::Reference<SvXMLAttributeList> pAttrList;
+ try
+ {
+ if (aRepresentations.hasElements())
+ {
+ // add the ranges used by the chart to the shape
+ // element to be able to start listening after
+ // load (when the chart is not yet loaded)
+ uno::Reference< chart2::data::XRangeXMLConversion > xRangeConverter( xChartDoc->getDataProvider(), uno::UNO_QUERY );
+ sRanges = lcl_RangeSequenceToString( aRepresentations, xRangeConverter );
+ pAttrList = new SvXMLAttributeList();
+ pAttrList->AddAttribute(
+ GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_DRAW, GetXMLToken(XML_NOTIFY_ON_UPDATE_OF_RANGES) ), sRanges );
+ }
+ }
+ catch (const lang::IllegalArgumentException&)
+ {
+ TOOLS_WARN_EXCEPTION("sc", "Exception in lcl_RangeSequenceToString - invalid range?");
+ }
+ GetShapeExport()->exportShape(xShape, SEF_DEFAULT, pPoint, pAttrList.get());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!bIsChart)
+ GetShapeExport()->exportShape(xShape, SEF_DEFAULT, pPoint);
+
+ IncrementProgressBar(false);
+}
+
+void ScXMLExport::WriteShapes(const ScMyCell& rMyCell)
+{
+ if( !(rMyCell.bHasShape && !rMyCell.aShapeList.empty() && pDoc) )
+ return;
+
+ // Reference point to turn absolute coordinates in reference point + offset. That happens in most
+ // cases in XMLShapeExport::ImpExportNewTrans_DecomposeAndRefPoint, which gets the absolute
+ // coordinates as translation from matrix in property "Transformation". For cell anchored shapes
+ // the reference point is left-top (in LTR mode) of that cell, which contains the shape.
+ tools::Rectangle aCellRectFull = pDoc->GetMMRect(
+ rMyCell.maCellAddress.Col(), rMyCell.maCellAddress.Row(), rMyCell.maCellAddress.Col(),
+ rMyCell.maCellAddress.Row(), rMyCell.maCellAddress.Tab(), false /*bHiddenAsZero*/);
+ awt::Point aPoint;
+ bool bNegativePage = pDoc->IsNegativePage(rMyCell.maCellAddress.Tab());
+ if (bNegativePage)
+ aPoint.X = aCellRectFull.Right();
+ else
+ aPoint.X = aCellRectFull.Left();
+ aPoint.Y = aCellRectFull.Top();
+
+ for (const auto& rShape : rMyCell.aShapeList)
+ {
+ if (rShape.xShape.is())
+ {
+ // The current object geometry is based on bHiddenAsZero=true, but ODF file format
+ // needs it as if there were no hidden rows or columns. We manipulate the geometry
+ // accordingly for writing xml markup and restore geometry later.
+ bool bNeedsRestore = false;
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rShape.xShape);
+ // Remember original geometry
+ std::unique_ptr<SdrObjGeoData> pGeoData;
+ if (pObj)
+ pGeoData = pObj->GetGeoData();
+
+ // Hiding row or column affects the shape based on its snap rect. So we need start and
+ // end cell address of snap rect. In case of a transformed shape, it is not in rMyCell.
+ ScAddress aSnapStartAddress = rMyCell.maCellAddress;
+ ScDrawObjData* pObjData = nullptr;
+ if (pObj)
+ {
+ pObjData = ScDrawLayer::GetObjData(pObj);
+ if (pObjData)
+ aSnapStartAddress = pObjData->maStart;
+ }
+
+ // In case rows or columns are hidden above or before the snap rect, move the shape to the
+ // position it would have, if these rows and columns are visible.
+ tools::Rectangle aRectFull = pDoc->GetMMRect(
+ aSnapStartAddress.Col(), aSnapStartAddress.Row(), aSnapStartAddress.Col(),
+ aSnapStartAddress.Row(), aSnapStartAddress.Tab(), false /*bHiddenAsZero*/);
+ tools::Rectangle aRectReduced = pDoc->GetMMRect(
+ aSnapStartAddress.Col(), aSnapStartAddress.Row(), aSnapStartAddress.Col(),
+ aSnapStartAddress.Row(), aSnapStartAddress.Tab(), true /*bHiddenAsZero*/);
+ const tools::Long nLeftDiff(aRectFull.Left() - aRectReduced.Left());
+ const tools::Long nTopDiff(aRectFull.Top() - aRectReduced.Top());
+ if (pObj && (abs(nLeftDiff) > 1 || abs(nTopDiff) > 1))
+ {
+ bNeedsRestore = true;
+ pObj->NbcMove(Size(nLeftDiff, nTopDiff));
+ }
+
+ // tdf#137033 In case the shape is anchored "To Cell (resize with cell)" hiding rows or
+ // columns inside the snap rect has not only changed size of the shape but rotate and shear
+ // angle too. We resize the shape to full size. That will recover the original angles too.
+ if (rShape.bResizeWithCell && pObjData) // implies pObj & aSnapStartAddress = pObjData->maStart
+ {
+ // Get original size from anchor
+ const Point aSnapStartOffset = pObjData->maStartOffset;
+ // In case of 'resize with cell' maEnd and maEndOffset should be valid.
+ const ScAddress aSnapEndAddress(pObjData->maEnd);
+ const Point aSnapEndOffset = pObjData->maEndOffset;
+ const tools::Rectangle aStartCellRect = pDoc->GetMMRect(
+ aSnapStartAddress.Col(), aSnapStartAddress.Row(), aSnapStartAddress.Col(),
+ aSnapStartAddress.Row(), aSnapStartAddress.Tab(), false /*bHiddenAsZero*/);
+ const tools::Rectangle aEndCellRect = pDoc->GetMMRect(
+ aSnapEndAddress.Col(), aSnapEndAddress.Row(), aSnapEndAddress.Col(),
+ aSnapEndAddress.Row(), aSnapEndAddress.Tab(), false /*bHiddenAsZero*/);
+ if (bNegativePage)
+ {
+ aRectFull.SetLeft(aEndCellRect.Right() - aSnapEndOffset.X());
+ aRectFull.SetRight(aStartCellRect.Right() - aSnapStartOffset.X());
+ }
+ else
+ {
+ aRectFull.SetLeft(aStartCellRect.Left() + aSnapStartOffset.X());
+ aRectFull.SetRight(aEndCellRect.Left() + aSnapEndOffset.X());
+ }
+ aRectFull.SetTop(aStartCellRect.Top() + aSnapStartOffset.Y());
+ aRectFull.SetBottom(aEndCellRect.Top() + aSnapEndOffset.Y());
+ aRectReduced = pObjData->getShapeRect();
+ if(abs(aRectFull.getWidth() - aRectReduced.getWidth()) > 1
+ || abs(aRectFull.getHeight() - aRectReduced.getHeight()) > 1)
+ {
+ bNeedsRestore = true;
+ Fraction aScaleWidth(aRectFull.getWidth(), aRectReduced.getWidth());
+ if (!aScaleWidth.IsValid())
+ aScaleWidth = Fraction(1.0);
+ Fraction aScaleHeight(aRectFull.getHeight(), aRectReduced.getHeight());
+ if (!aScaleHeight.IsValid())
+ aScaleHeight = Fraction(1.0);
+ pObj->NbcResize(pObj->GetRelativePos(), aScaleWidth, aScaleHeight);
+ }
+ }
+
+ // We only write the end address if we want the shape to resize with the cell
+ if ( rShape.bResizeWithCell &&
+ rShape.xShape->getShapeType() != "com.sun.star.drawing.CaptionShape" )
+ {
+ OUString sEndAddress;
+ ScRangeStringConverter::GetStringFromAddress(sEndAddress, rShape.aEndAddress, pDoc, FormulaGrammar::CONV_OOO);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_END_CELL_ADDRESS, sEndAddress);
+ OUStringBuffer sBuffer;
+ GetMM100UnitConverter().convertMeasureToXML(
+ sBuffer, rShape.nEndX);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_END_X, sBuffer.makeStringAndClear());
+ GetMM100UnitConverter().convertMeasureToXML(
+ sBuffer, rShape.nEndY);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_END_Y, sBuffer.makeStringAndClear());
+ }
+
+ // Correct above calculated reference point for some cases:
+ // a) For a RTL-sheet translate from matrix is not suitable, because the shape
+ // from xml (which is always LTR) is not mirrored to negative page but shifted.
+ // b) In case of horizontal mirrored, 'resize with cell' anchored custom shape, translate
+ // has wrong values. FixMe: Why is translate wrong?
+ // c) Measure lines do not use transformation matrix but use start and end point directly.
+ ScDrawObjData* pNRObjData = nullptr;
+ if (pObj && bNegativePage
+ && rShape.xShape->getShapeType() == "com.sun.star.drawing.MeasureShape")
+ {
+ // inverse of shift when import
+ tools::Rectangle aSnapRect = pObj->GetSnapRect();
+ aPoint.X = aSnapRect.Left() + aSnapRect.Right() - aPoint.X;
+ }
+ else if (pObj && (pNRObjData = ScDrawLayer::GetNonRotatedObjData(pObj))
+ && ((rShape.bResizeWithCell && pObj->GetObjIdentifier() == SdrObjKind::CustomShape
+ && static_cast<SdrObjCustomShape*>(pObj)->IsMirroredX())
+ || bNegativePage))
+ {
+ //In these cases we set reference Point = matrix translate - startOffset.
+ awt::Point aMatrixTranslate = rShape.xShape->getPosition();
+ aPoint.X = aMatrixTranslate.X - pNRObjData->maStartOffset.X();
+ aPoint.Y = aMatrixTranslate.Y - pNRObjData->maStartOffset.Y();
+ }
+
+ ExportShape(rShape.xShape, &aPoint);
+
+ // Restore object geometry
+ if (bNeedsRestore && pObj && pGeoData)
+ pObj->SetGeoData(*pGeoData);
+ }
+ }
+}
+
+void ScXMLExport::WriteTableShapes()
+{
+ ScMyTableShapes* pTableShapes(pSharedData->GetTableShapes());
+ if (!pTableShapes || (*pTableShapes)[nCurrentTable].empty())
+ return;
+
+ OSL_ENSURE(pTableShapes->size() > static_cast<size_t>(nCurrentTable), "wrong Table");
+ SvXMLElementExport aShapesElem(*this, XML_NAMESPACE_TABLE, XML_SHAPES, true, false);
+ for (const auto& rxShape : (*pTableShapes)[nCurrentTable])
+ {
+ if (rxShape.is())
+ {
+ if (pDoc->IsNegativePage(static_cast<SCTAB>(nCurrentTable)))
+ {
+ // RTL-mirroring refers to snap rectangle, not to logic rectangle, therefore cannot use
+ // getPosition() and getSize(), but need property "FrameRect" from rxShape or
+ // GetSnapRect() from associated SdrObject.
+ uno::Reference<beans::XPropertySet> xShapeProp(rxShape, uno::UNO_QUERY);
+ awt::Rectangle aFrameRect;
+ if (xShapeProp.is() && (xShapeProp->getPropertyValue("FrameRect") >>= aFrameRect))
+ {
+ // file format uses shape in LTR mode. newLeft = - oldRight = - (oldLeft + width).
+ // newTranslate = oldTranslate - refPoint, oldTranslate from transformation matrix,
+ // calculated in XMLShapeExport::exportShape common for all modules.
+ // oldTranslate.X = oldLeft ==> refPoint.X = 2 * oldLeft + width
+ awt::Point aRefPoint;
+ aRefPoint.X = 2 * aFrameRect.X + aFrameRect.Width - 1;
+ aRefPoint.Y = 0;
+ ExportShape(rxShape, &aRefPoint);
+ }
+ // else should not happen
+ }
+ else
+ ExportShape(rxShape, nullptr);
+ }
+ }
+ (*pTableShapes)[nCurrentTable].clear();
+}
+
+void ScXMLExport::WriteAreaLink( const ScMyCell& rMyCell )
+{
+ if( !rMyCell.bHasAreaLink )
+ return;
+
+ const ScMyAreaLink& rAreaLink = rMyCell.aAreaLink;
+ AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, rAreaLink.sSourceStr );
+ AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
+ AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(rAreaLink.sURL) );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_FILTER_NAME, rAreaLink.sFilter );
+ if( !rAreaLink.sFilterOptions.isEmpty() )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, rAreaLink.sFilterOptions );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_LAST_COLUMN_SPANNED, OUString::number(rAreaLink.GetColCount()) );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_LAST_ROW_SPANNED, OUString::number(rAreaLink.GetRowCount()) );
+ if( rAreaLink.nRefreshDelaySeconds )
+ {
+ OUStringBuffer sValue;
+ ::sax::Converter::convertDuration( sValue,
+ static_cast<double>(rAreaLink.nRefreshDelaySeconds) / 86400 );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_REFRESH_DELAY, sValue.makeStringAndClear() );
+ }
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_CELL_RANGE_SOURCE, true, true );
+}
+
+void ScXMLExport::exportAnnotationMeta( const uno::Reference < drawing::XShape >& xShape)
+{
+ ScPostIt* pNote = pCurrentCell->pNote;
+
+ if (!pNote)
+ return;
+
+ // TODO : notes
+ //is it still useful, as this call back is only called from ScXMLExport::WriteAnnotation
+ // and should be in sync with pCurrentCell
+ SdrCaptionObj* pNoteCaption = pNote->GetOrCreateCaption(pCurrentCell->maCellAddress);
+ uno::Reference<drawing::XShape> xCurrentShape( pNoteCaption->getUnoShape(), uno::UNO_QUERY );
+ if (xCurrentShape.get()!=xShape.get())
+ return;
+
+ const OUString& sAuthor(pNote->GetAuthor());
+ if (!sAuthor.isEmpty())
+ {
+ SvXMLElementExport aCreatorElem( *this, XML_NAMESPACE_DC,
+ XML_CREATOR, true,
+ false );
+ Characters(sAuthor);
+ }
+
+ const OUString& aDate(pNote->GetDate());
+ if (pDoc)
+ {
+ SvNumberFormatter* pNumForm = pDoc->GetFormatTable();
+ double fDate;
+ sal_uInt32 nfIndex = pNumForm->GetFormatIndex(NF_DATE_SYS_DDMMYYYY, LANGUAGE_SYSTEM);
+ if (pNumForm->IsNumberFormat(aDate, nfIndex, fDate))
+ {
+ OUStringBuffer sBuf;
+ GetMM100UnitConverter().convertDateTime(sBuf, fDate,true);
+ SvXMLElementExport aDateElem( *this, XML_NAMESPACE_DC,
+ XML_DATE, true,
+ false );
+ Characters(sBuf.makeStringAndClear());
+ }
+ else
+ {
+ SvXMLElementExport aDateElem( *this, XML_NAMESPACE_META,
+ XML_DATE_STRING, true,
+ false );
+ Characters(aDate);
+ }
+ }
+ else
+ {
+ SvXMLElementExport aDateElem( *this, XML_NAMESPACE_META,
+ XML_DATE_STRING, true,
+ false );
+ Characters(aDate);
+ }
+}
+
+void ScXMLExport::WriteAnnotation(const ScMyCell& rMyCell)
+{
+ ScPostIt* pNote = pDoc->GetNote(rMyCell.maCellAddress);
+ if (!pNote)
+ return;
+
+ if (pNote->IsCaptionShown())
+ AddAttribute(XML_NAMESPACE_OFFICE, XML_DISPLAY, XML_TRUE);
+
+ pCurrentCell = &rMyCell;
+
+ SdrCaptionObj* pNoteCaption = pNote->GetOrCreateCaption(rMyCell.maCellAddress);
+ if (pNoteCaption)
+ {
+ uno::Reference<drawing::XShape> xShape( pNoteCaption->getUnoShape(), uno::UNO_QUERY );
+ if (xShape.is())
+ GetShapeExport()->exportShape(xShape, SEF_DEFAULT|XMLShapeExportFlags::ANNOTATION);
+ }
+
+ pCurrentCell = nullptr;
+}
+
+void ScXMLExport::WriteDetective( const ScMyCell& rMyCell )
+{
+ if( !(rMyCell.bHasDetectiveObj || rMyCell.bHasDetectiveOp) )
+ return;
+
+ const ScMyDetectiveObjVec& rObjVec = rMyCell.aDetectiveObjVec;
+ const ScMyDetectiveOpVec& rOpVec = rMyCell.aDetectiveOpVec;
+ sal_Int32 nObjCount(rObjVec.size());
+ sal_Int32 nOpCount(rOpVec.size());
+ if( !(nObjCount || nOpCount) )
+ return;
+
+ SvXMLElementExport aDetElem( *this, XML_NAMESPACE_TABLE, XML_DETECTIVE, true, true );
+ OUString sString;
+ for(const auto& rObj : rObjVec)
+ {
+ if (rObj.eObjType != SC_DETOBJ_CIRCLE)
+ {
+ if( (rObj.eObjType == SC_DETOBJ_ARROW) || (rObj.eObjType == SC_DETOBJ_TOOTHERTAB))
+ {
+ ScRangeStringConverter::GetStringFromRange( sString, rObj.aSourceRange, pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, sString );
+ }
+ sString = ScXMLConverter::GetStringFromDetObjType( rObj.eObjType );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_DIRECTION, sString );
+ if( rObj.bHasError )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_CONTAINS_ERROR, XML_TRUE );
+ }
+ else
+ AddAttribute( XML_NAMESPACE_TABLE, XML_MARKED_INVALID, XML_TRUE );
+ SvXMLElementExport aRangeElem( *this, XML_NAMESPACE_TABLE, XML_HIGHLIGHTED_RANGE, true, true );
+ }
+ for(const auto& rOp : rOpVec)
+ {
+ OUString sOpString = ScXMLConverter::GetStringFromDetOpType( rOp.eOpType );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, sOpString );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_INDEX, OUString::number(rOp.nIndex) );
+ SvXMLElementExport aRangeElem( *this, XML_NAMESPACE_TABLE, XML_OPERATION, true, true );
+ }
+}
+
+void ScXMLExport::SetRepeatAttribute(sal_Int32 nEqualCellCount, bool bIncProgress)
+{
+ // nEqualCellCount is additional cells, so the attribute value is nEqualCellCount+1
+ if (nEqualCellCount > 0)
+ {
+ sal_Int32 nTemp(nEqualCellCount + 1);
+ OUString sOUEqualCellCount(OUString::number(nTemp));
+ AddAttribute(sAttrColumnsRepeated, sOUEqualCellCount);
+ if (bIncProgress)
+ IncrementProgressBar(false, nEqualCellCount);
+ }
+}
+
+bool ScXMLExport::IsEditCell(const ScMyCell& rCell)
+{
+ return rCell.maBaseCell.meType == CELLTYPE_EDIT;
+}
+
+bool ScXMLExport::IsCellEqual (const ScMyCell& aCell1, const ScMyCell& aCell2)
+{
+ bool bIsEqual = false;
+ if( !aCell1.bIsMergedBase && !aCell2.bIsMergedBase &&
+ aCell1.bIsCovered == aCell2.bIsCovered &&
+ !aCell1.bIsMatrixBase && !aCell2.bIsMatrixBase &&
+ aCell1.bIsMatrixCovered == aCell2.bIsMatrixCovered &&
+ aCell1.bHasAnnotation == aCell2.bHasAnnotation &&
+ !aCell1.bHasShape && !aCell2.bHasShape &&
+ aCell1.bHasAreaLink == aCell2.bHasAreaLink &&
+ !aCell1.bHasDetectiveObj && !aCell2.bHasDetectiveObj)
+ {
+ if( (aCell1.bHasAreaLink &&
+ (aCell1.aAreaLink.GetColCount() == 1) &&
+ (aCell2.aAreaLink.GetColCount() == 1) &&
+ aCell1.aAreaLink.Compare( aCell2.aAreaLink ) ) ||
+ !aCell1.bHasAreaLink )
+ {
+ if (!aCell1.bHasAnnotation)
+ {
+ if ((((aCell1.nStyleIndex == aCell2.nStyleIndex) && (aCell1.bIsAutoStyle == aCell2.bIsAutoStyle)) ||
+ ((aCell1.nStyleIndex == aCell2.nStyleIndex) && (aCell1.nStyleIndex == -1))) &&
+ aCell1.nValidationIndex == aCell2.nValidationIndex &&
+ aCell1.nType == aCell2.nType)
+ {
+ switch ( aCell1.nType )
+ {
+ case table::CellContentType_EMPTY :
+ {
+ bIsEqual = true;
+ }
+ break;
+ case table::CellContentType_VALUE :
+ {
+ // #i29101# number format may be different from column default styles,
+ // but can lead to different value types, so it must also be compared
+ bIsEqual = (aCell1.nNumberFormat == aCell2.nNumberFormat) &&
+ (aCell1.maBaseCell.mfValue == aCell2.maBaseCell.mfValue);
+ }
+ break;
+ case table::CellContentType_TEXT :
+ {
+ if (IsEditCell(aCell1) || IsEditCell(aCell2))
+ bIsEqual = false;
+ else
+ {
+ bIsEqual = (aCell1.maBaseCell.getString(pDoc) == aCell2.maBaseCell.getString(pDoc));
+ }
+ }
+ break;
+ case table::CellContentType_FORMULA :
+ {
+ bIsEqual = false;
+ }
+ break;
+ default :
+ {
+ bIsEqual = false;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return bIsEqual;
+}
+
+void ScXMLExport::WriteCalculationSettings(const uno::Reference <sheet::XSpreadsheetDocument>& xSpreadDoc)
+{
+ uno::Reference<beans::XPropertySet> xPropertySet(xSpreadDoc, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ bool bCalcAsShown (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_CALCASSHOWN) ));
+ bool bIgnoreCase (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_IGNORECASE) ));
+ bool bLookUpLabels (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_LOOKUPLABELS) ));
+ bool bMatchWholeCell (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_MATCHWHOLE) ));
+ bool bUseRegularExpressions (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_REGEXENABLED) ));
+ bool bUseWildcards (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_WILDCARDSENABLED) ));
+ if (bUseWildcards && bUseRegularExpressions)
+ bUseRegularExpressions = false; // mutually exclusive, wildcards take precedence
+ bool bIsIterationEnabled (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_ITERENABLED) ));
+ sal_uInt16 nYear2000 (pDoc ? pDoc->GetDocOptions().GetYear2000() : 0);
+ sal_Int32 nIterationCount(100);
+ xPropertySet->getPropertyValue( SC_UNO_ITERCOUNT ) >>= nIterationCount;
+ double fIterationEpsilon = 0;
+ xPropertySet->getPropertyValue( SC_UNO_ITEREPSILON ) >>= fIterationEpsilon;
+ util::Date aNullDate;
+ xPropertySet->getPropertyValue( SC_UNO_NULLDATE ) >>= aNullDate;
+ if (!(bCalcAsShown || bIgnoreCase || !bLookUpLabels || !bMatchWholeCell || !bUseRegularExpressions ||
+ bUseWildcards ||
+ bIsIterationEnabled || nIterationCount != 100 || !::rtl::math::approxEqual(fIterationEpsilon, 0.001) ||
+ aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899 || nYear2000 != 1930))
+ return;
+
+ if (bIgnoreCase)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_FALSE);
+ if (bCalcAsShown)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PRECISION_AS_SHOWN, XML_TRUE);
+ if (!bMatchWholeCell)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_SEARCH_CRITERIA_MUST_APPLY_TO_WHOLE_CELL, XML_FALSE);
+ if (!bLookUpLabels)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_AUTOMATIC_FIND_LABELS, XML_FALSE);
+ if (!bUseRegularExpressions)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_USE_REGULAR_EXPRESSIONS, XML_FALSE);
+ if (bUseWildcards)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_USE_WILDCARDS, XML_TRUE);
+ if (nYear2000 != 1930)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NULL_YEAR, OUString::number(nYear2000));
+ }
+ SvXMLElementExport aCalcSettings(*this, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true, true);
+ {
+ if (aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899)
+ {
+ OUStringBuffer sDate;
+ SvXMLUnitConverter::convertDateTime(sDate, 0.0, aNullDate);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_VALUE, sDate.makeStringAndClear());
+ SvXMLElementExport aElemNullDate(*this, XML_NAMESPACE_TABLE, XML_NULL_DATE, true, true);
+ }
+ if (bIsIterationEnabled || nIterationCount != 100 || !::rtl::math::approxEqual(fIterationEpsilon, 0.001))
+ {
+ if (bIsIterationEnabled)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STATUS, XML_ENABLE);
+ if (nIterationCount != 100)
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STEPS, OUString::number(nIterationCount));
+ }
+ if (!::rtl::math::approxEqual(fIterationEpsilon, 0.001))
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertDouble(sBuffer,
+ fIterationEpsilon);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_MAXIMUM_DIFFERENCE, sBuffer.makeStringAndClear());
+ }
+ SvXMLElementExport aElemIteration(*this, XML_NAMESPACE_TABLE, XML_ITERATION, true, true);
+ }
+ }
+}
+
+void ScXMLExport::WriteTableSource()
+{
+ uno::Reference <sheet::XSheetLinkable> xLinkable (xCurrentTable, uno::UNO_QUERY);
+ if (!(xLinkable.is() && GetModel().is()))
+ return;
+
+ sheet::SheetLinkMode nMode (xLinkable->getLinkMode());
+ if (nMode == sheet::SheetLinkMode_NONE)
+ return;
+
+ OUString sLink (xLinkable->getLinkUrl());
+ uno::Reference <beans::XPropertySet> xProps (GetModel(), uno::UNO_QUERY);
+ if (!xProps.is())
+ return;
+
+ uno::Reference <container::XIndexAccess> xIndex(xProps->getPropertyValue(SC_UNO_SHEETLINKS), uno::UNO_QUERY);
+ if (!xIndex.is())
+ return;
+
+ sal_Int32 nCount(xIndex->getCount());
+ if (!nCount)
+ return;
+
+ bool bFound(false);
+ uno::Reference <beans::XPropertySet> xLinkProps;
+ for (sal_Int32 i = 0; (i < nCount) && !bFound; ++i)
+ {
+ xLinkProps.set(xIndex->getByIndex(i), uno::UNO_QUERY);
+ if (xLinkProps.is())
+ {
+ OUString sNewLink;
+ if (xLinkProps->getPropertyValue(SC_UNONAME_LINKURL) >>= sNewLink)
+ bFound = sLink == sNewLink;
+ }
+ }
+ if (!(bFound && xLinkProps.is()))
+ return;
+
+ OUString sFilter;
+ OUString sFilterOptions;
+ OUString sTableName (xLinkable->getLinkSheetName());
+ sal_Int32 nRefresh(0);
+ xLinkProps->getPropertyValue(SC_UNONAME_FILTER) >>= sFilter;
+ xLinkProps->getPropertyValue(SC_UNONAME_FILTOPT) >>= sFilterOptions;
+ xLinkProps->getPropertyValue(SC_UNONAME_REFDELAY) >>= nRefresh;
+ if (sLink.isEmpty())
+ return;
+
+ AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
+ AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(sLink));
+ if (!sTableName.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_NAME, sTableName);
+ if (!sFilter.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_NAME, sFilter);
+ if (!sFilterOptions.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, sFilterOptions);
+ if (nMode != sheet::SheetLinkMode_NORMAL)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_MODE, XML_COPY_RESULTS_ONLY);
+ if( nRefresh )
+ {
+ OUStringBuffer sBuffer;
+ ::sax::Converter::convertDuration( sBuffer,
+ static_cast<double>(nRefresh) / 86400 );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_REFRESH_DELAY, sBuffer.makeStringAndClear() );
+ }
+ SvXMLElementExport aSourceElem(*this, XML_NAMESPACE_TABLE, XML_TABLE_SOURCE, true, true);
+}
+
+// core implementation
+void ScXMLExport::WriteScenario()
+{
+ if (!(pDoc && pDoc->IsScenario(static_cast<SCTAB>(nCurrentTable))))
+ return;
+
+ OUString sComment;
+ Color aColor;
+ ScScenarioFlags nFlags;
+ pDoc->GetScenarioData(static_cast<SCTAB>(nCurrentTable), sComment, aColor, nFlags);
+ if (!(nFlags & ScScenarioFlags::ShowFrame))
+ AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_BORDER, XML_FALSE);
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, aColor);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_BORDER_COLOR, aBuffer.makeStringAndClear());
+ if (!(nFlags & ScScenarioFlags::TwoWay))
+ AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_BACK, XML_FALSE);
+ if (!(nFlags & ScScenarioFlags::Attrib))
+ AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_STYLES, XML_FALSE);
+ if (nFlags & ScScenarioFlags::Value)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_FORMULAS, XML_FALSE);
+ if (nFlags & ScScenarioFlags::Protected)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE);
+ ::sax::Converter::convertBool(aBuffer,
+ pDoc->IsActiveScenario(static_cast<SCTAB>(nCurrentTable)));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_IS_ACTIVE, aBuffer.makeStringAndClear());
+ const ScRangeList* pRangeList = pDoc->GetScenarioRanges(static_cast<SCTAB>(nCurrentTable));
+ OUString sRangeListStr;
+ ScRangeStringConverter::GetStringFromRangeList( sRangeListStr, pRangeList, pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute(XML_NAMESPACE_TABLE, XML_SCENARIO_RANGES, sRangeListStr);
+ if (!sComment.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_COMMENT, sComment);
+ SvXMLElementExport aElem(*this, XML_NAMESPACE_TABLE, XML_SCENARIO, true, true);
+}
+
+void ScXMLExport::WriteTheLabelRanges( const uno::Reference< sheet::XSpreadsheetDocument >& xSpreadDoc )
+{
+ uno::Reference< beans::XPropertySet > xDocProp( xSpreadDoc, uno::UNO_QUERY );
+ if( !xDocProp.is() ) return;
+
+ sal_Int32 nCount(0);
+ uno::Reference< container::XIndexAccess > xColRangesIAccess(xDocProp->getPropertyValue( SC_UNO_COLLABELRNG ), uno::UNO_QUERY);
+ if( xColRangesIAccess.is() )
+ nCount += xColRangesIAccess->getCount();
+
+ uno::Reference< container::XIndexAccess > xRowRangesIAccess(xDocProp->getPropertyValue( SC_UNO_ROWLABELRNG ), uno::UNO_QUERY);
+ if( xRowRangesIAccess.is() )
+ nCount += xRowRangesIAccess->getCount();
+
+ if( nCount )
+ {
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_LABEL_RANGES, true, true );
+ WriteLabelRanges( xColRangesIAccess, true );
+ WriteLabelRanges( xRowRangesIAccess, false );
+ }
+}
+
+void ScXMLExport::WriteLabelRanges( const uno::Reference< container::XIndexAccess >& xRangesIAccess, bool bColumn )
+{
+ if( !xRangesIAccess.is() ) return;
+
+ sal_Int32 nCount(xRangesIAccess->getCount());
+ for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ uno::Reference< sheet::XLabelRange > xRange(xRangesIAccess->getByIndex( nIndex ), uno::UNO_QUERY);
+ if( xRange.is() )
+ {
+ OUString sRangeStr;
+ table::CellRangeAddress aCellRange( xRange->getLabelArea() );
+ ScRangeStringConverter::GetStringFromRange( sRangeStr, aCellRange, pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_LABEL_CELL_RANGE_ADDRESS, sRangeStr );
+ aCellRange = xRange->getDataArea();
+ ScRangeStringConverter::GetStringFromRange( sRangeStr, aCellRange, pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_DATA_CELL_RANGE_ADDRESS, sRangeStr );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_ORIENTATION, bColumn ? XML_COLUMN : XML_ROW );
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_LABEL_RANGE, true, true );
+ }
+ }
+}
+
+void ScXMLExport::WriteNamedExpressions()
+{
+ if (!pDoc)
+ return;
+ ScRangeName* pNamedRanges = pDoc->GetRangeName();
+ WriteNamedRange(pNamedRanges);
+}
+
+void ScXMLExport::WriteExternalDataMapping()
+{
+ if (!pDoc)
+ return;
+
+ if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
+ // Export this only for 1.2 extended and above.
+ return;
+
+ sc::ExternalDataMapper& rDataMapper = pDoc->GetExternalDataMapper();
+ auto& rDataSources = rDataMapper.getDataSources();
+
+ if (rDataSources.empty())
+ return;
+
+ SvXMLElementExport aMappings(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_MAPPINGS, true, true);
+ for (const auto& itr : rDataSources)
+ {
+ AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, itr.getURL());
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_PROVIDER, itr.getProvider());
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DATA_FREQUENCY, OUString::number(sc::ExternalDataSource::getUpdateFrequency()));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_ID, itr.getID());
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DATABASE_NAME, itr.getDBName());
+
+ SvXMLElementExport aMapping(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_MAPPING, true, true);
+ // Add the data transformations
+ WriteExternalDataTransformations(itr.getDataTransformation());
+ }
+}
+
+void ScXMLExport::WriteExternalDataTransformations(const std::vector<std::shared_ptr<sc::DataTransformation>>& aDataTransformations)
+{
+ SvXMLElementExport aTransformations(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_TRANSFORMATIONS, true, true);
+ for (auto& itr : aDataTransformations)
+ {
+ sc::TransformationType aTransformationType = itr->getTransformationType();
+
+ switch(aTransformationType)
+ {
+ case sc::TransformationType::DELETE_TRANSFORMATION:
+ {
+ // Delete Columns Transformation
+ std::shared_ptr<sc::ColumnRemoveTransformation> aDeleteTransformation = std::dynamic_pointer_cast<sc::ColumnRemoveTransformation>(itr);
+ std::set<SCCOL> aColumns = aDeleteTransformation->getColumns();
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_REMOVE_TRANSFORMATION, true, true);
+ for(auto& col : aColumns)
+ {
+ // Add Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::SPLIT_TRANSFORMATION:
+ {
+ std::shared_ptr<sc::SplitColumnTransformation> aSplitTransformation = std::dynamic_pointer_cast<sc::SplitColumnTransformation>(itr);
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(aSplitTransformation->getColumn()));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SEPARATOR, OUString::number(aSplitTransformation->getSeparator()));
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_SPLIT_TRANSFORMATION, true, true);
+ }
+ break;
+ case sc::TransformationType::MERGE_TRANSFORMATION:
+ {
+ // Merge Transformation
+ std::shared_ptr<sc::MergeColumnTransformation> aMergeTransformation = std::dynamic_pointer_cast<sc::MergeColumnTransformation>(itr);
+ std::set<SCCOL> aColumns = aMergeTransformation->getColumns();
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MERGE_STRING, aMergeTransformation->getMergeString());
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_MERGE_TRANSFORMATION, true, true);
+
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::SORT_TRANSFORMATION:
+ {
+ // Sort Transformation
+ std::shared_ptr<sc::SortTransformation> aSortTransformation = std::dynamic_pointer_cast<sc::SortTransformation>(itr);
+ ScSortParam aSortParam = aSortTransformation->getSortParam();
+ const sc::DocumentLinkManager& rMgr = pDoc->GetDocLinkManager();
+ const sc::DataStream* pStrm = rMgr.getDataStream();
+ if (!pStrm)
+ // No data stream.
+ return;
+
+ // Streamed range
+ ScRange aRange = pStrm->GetRange();
+
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_SORT_TRANSFORMATION, true, true);
+
+ writeSort(*this, aSortParam, aRange, pDoc);
+ }
+ break;
+ case sc::TransformationType::TEXT_TRANSFORMATION:
+ {
+ // Text Transformation
+ std::shared_ptr<sc::TextTransformation> aTextTransformation = std::dynamic_pointer_cast<sc::TextTransformation>(itr);
+
+ sc::TEXT_TRANSFORM_TYPE aTextTransformType = aTextTransformation->getTextTransformationType();
+
+ switch ( aTextTransformType )
+ {
+ case sc::TEXT_TRANSFORM_TYPE::TO_LOWER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_LOWERCASE);
+ break;
+ case sc::TEXT_TRANSFORM_TYPE::TO_UPPER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_UPPERCASE);
+ break;
+ case sc::TEXT_TRANSFORM_TYPE::CAPITALIZE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_CAPITALIZE);
+ break;
+ case sc::TEXT_TRANSFORM_TYPE::TRIM:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_TRIM);
+ break;
+ }
+
+ std::set<SCCOL> aColumns = aTextTransformation->getColumns();
+
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_TEXT_TRANSFORMATION, true, true);
+
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::AGGREGATE_FUNCTION:
+ {
+ // Aggregate Transformation
+ std::shared_ptr<sc::AggregateFunction> aAggregateFunction = std::dynamic_pointer_cast<sc::AggregateFunction>(itr);
+ std::set<SCCOL> aColumns = aAggregateFunction->getColumns();
+
+ sc::AGGREGATE_FUNCTION aAggregateType = aAggregateFunction->getAggregateType();
+
+ switch (aAggregateType)
+ {
+ case sc::AGGREGATE_FUNCTION::SUM:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SUM);
+ break;
+ case sc::AGGREGATE_FUNCTION::AVERAGE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_AVERAGE);
+ break;
+ case sc::AGGREGATE_FUNCTION::MIN:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MIN);
+ break;
+ case sc::AGGREGATE_FUNCTION::MAX:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MAX);
+ break;
+ }
+
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT,XML_COLUMN_AGGREGATE_TRANSFORMATION, true, true);
+
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::NUMBER_TRANSFORMATION:
+ {
+ // Number Transformation
+ std::shared_ptr<sc::NumberTransformation> aNumberTransformation = std::dynamic_pointer_cast<sc::NumberTransformation>(itr);
+
+ sc::NUMBER_TRANSFORM_TYPE aNumberTransformType = aNumberTransformation->getNumberTransformationType();
+
+ switch ( aNumberTransformType )
+ {
+ case sc::NUMBER_TRANSFORM_TYPE::ROUND:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::ROUND_UP:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND_UP);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::ROUND_DOWN:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND_DOWN);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::ABSOLUTE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ABS);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::LOG_E:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_LOG);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::LOG_10:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_LOG_10);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::CUBE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CUBE);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::SQUARE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SQUARE);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::SQUARE_ROOT:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SQUARE_ROOT);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::EXPONENT:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_EXPONENTIAL);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::IS_EVEN:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_EVEN);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::IS_ODD:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ODD);
+ break;
+ case sc::NUMBER_TRANSFORM_TYPE::SIGN:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SIGN);
+ break;
+ }
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_PRECISION, OUString::number(aNumberTransformation->getPrecision()));
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_NUMBER_TRANSFORMATION, true, true);
+
+ std::set<SCCOL> aColumns = aNumberTransformation->getColumn();
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::REMOVE_NULL_TRANSFORMATION:
+ {
+ // Replace Null Transformation
+ std::shared_ptr<sc::ReplaceNullTransformation> aReplaceNullTransformation = std::dynamic_pointer_cast<sc::ReplaceNullTransformation>(itr);
+ std::set<SCCOL> aColumns = aReplaceNullTransformation->getColumn();
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_REPLACE_STRING, aReplaceNullTransformation->getReplaceString());
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_REPLACENULL_TRANSFORMATION, true, true);
+
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ case sc::TransformationType::DATETIME_TRANSFORMATION:
+ {
+ // Number Transformation
+ std::shared_ptr<sc::DateTimeTransformation> aDateTimeTransformation = std::dynamic_pointer_cast<sc::DateTimeTransformation>(itr);
+
+ sc::DATETIME_TRANSFORMATION_TYPE aDateTimeTransformationType = aDateTimeTransformation->getDateTimeTransformationType();
+
+ switch ( aDateTimeTransformationType )
+ {
+ case sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DATE_STRING);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::YEAR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_YEAR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_YEAR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_YEAR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_YEAR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_YEAR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::MONTH:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MONTH);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::MONTH_NAME:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MONTH_NAME);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_MONTH:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_MONTH);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_MONTH:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_MONTH);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::DAY:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_WEEK:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY_OF_WEEK);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_YEAR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY_OF_YEAR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::QUARTER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_QUARTER);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_QUARTER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_QUARTER);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_QUARTER:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_QUARTER);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::TIME:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_TIME);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::HOUR:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_HOUR);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::MINUTE:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MINUTE);
+ break;
+ case sc::DATETIME_TRANSFORMATION_TYPE::SECOND:
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SECONDS);
+ break;
+ }
+
+ SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_DATETIME_TRANSFORMATION, true, true);
+
+ std::set<SCCOL> aColumns = aDateTimeTransformation->getColumn();
+ for(auto& col : aColumns)
+ {
+ // Columns
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
+ SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ScXMLExport::WriteDataStream()
+{
+ if (!pDoc)
+ return;
+
+ if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
+ // Export this only in experimental mode.
+ return;
+
+ if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
+ // Export this only for 1.2 extended and above.
+ return;
+
+ const sc::DocumentLinkManager& rMgr = pDoc->GetDocLinkManager();
+ const sc::DataStream* pStrm = rMgr.getDataStream();
+ if (!pStrm)
+ // No data stream.
+ return;
+
+ // Source URL
+ AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(pStrm->GetURL()));
+
+ // Streamed range
+ ScRange aRange = pStrm->GetRange();
+ OUString aRangeStr;
+ ScRangeStringConverter::GetStringFromRange(
+ aRangeStr, aRange, pDoc, formula::FormulaGrammar::CONV_OOO);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aRangeStr);
+
+ // Empty line refresh option.
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_EMPTY_LINE_REFRESH, pStrm->IsRefreshOnEmptyLine() ? XML_TRUE : XML_FALSE);
+
+ // New data insertion position. Either top of bottom. Default to bottom.
+ xmloff::token::XMLTokenEnum eInsertPosition = XML_BOTTOM;
+ if (pStrm->GetMove() == sc::DataStream::MOVE_DOWN)
+ eInsertPosition = XML_TOP;
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_INSERTION_POSITION, eInsertPosition);
+
+ SvXMLElementExport aElem(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_STREAM_SOURCE, true, true);
+}
+
+void ScXMLExport::WriteNamedRange(ScRangeName* pRangeName)
+{
+ //write a global or local ScRangeName
+ SvXMLElementExport aElemNEs(*this, XML_NAMESPACE_TABLE, XML_NAMED_EXPRESSIONS, true, true);
+ for (const auto& rxEntry : *pRangeName)
+ {
+ AddAttribute(sAttrName, rxEntry.second->GetName());
+
+ OUString sBaseCellAddress;
+ rxEntry.second->ValidateTabRefs();
+ ScRangeStringConverter::GetStringFromAddress( sBaseCellAddress, rxEntry.second->GetPos(), pDoc,
+ FormulaGrammar::CONV_OOO, ' ', false, ScRefFlags::ADDR_ABS_3D);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_BASE_CELL_ADDRESS, sBaseCellAddress);
+
+ OUString sSymbol = rxEntry.second->GetSymbol(pDoc->GetStorageGrammar());
+ OUString sTempSymbol(sSymbol);
+ ScRange aRange;
+ if (rxEntry.second->IsReference(aRange))
+ {
+
+ OUString sContent(sTempSymbol.copy(1, sTempSymbol.getLength() -2 ));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, sContent);
+
+ sal_Int32 nRangeType = rxEntry.second->GetUnoType();
+ OUStringBuffer sBufferRangeType;
+ if ((nRangeType & sheet::NamedRangeFlag::COLUMN_HEADER) == sheet::NamedRangeFlag::COLUMN_HEADER)
+ sBufferRangeType.append(GetXMLToken(XML_REPEAT_COLUMN));
+ if ((nRangeType & sheet::NamedRangeFlag::ROW_HEADER) == sheet::NamedRangeFlag::ROW_HEADER)
+ {
+ if (!sBufferRangeType.isEmpty())
+ sBufferRangeType.append(" ");
+ sBufferRangeType.append(GetXMLToken(XML_REPEAT_ROW));
+ }
+ if ((nRangeType & sheet::NamedRangeFlag::FILTER_CRITERIA) == sheet::NamedRangeFlag::FILTER_CRITERIA)
+ {
+ if (!sBufferRangeType.isEmpty())
+ sBufferRangeType.append(" ");
+ sBufferRangeType.append(GetXMLToken(XML_FILTER));
+ }
+ if ((nRangeType & sheet::NamedRangeFlag::PRINT_AREA) == sheet::NamedRangeFlag::PRINT_AREA)
+ {
+ if (!sBufferRangeType.isEmpty())
+ sBufferRangeType.append(" ");
+ sBufferRangeType.append(GetXMLToken(XML_PRINT_RANGE));
+ }
+ OUString sRangeType = sBufferRangeType.makeStringAndClear();
+ if (!sRangeType.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_RANGE_USABLE_AS, sRangeType);
+ SvXMLElementExport aElemNR(*this, XML_NAMESPACE_TABLE, XML_NAMED_RANGE, true, true);
+
+ }
+ else
+ {
+ AddAttribute(XML_NAMESPACE_TABLE, XML_EXPRESSION, sTempSymbol);
+ SvXMLElementExport aElemNE(*this, XML_NAMESPACE_TABLE, XML_NAMED_EXPRESSION, true, true);
+ }
+ }
+}
+
+void ScXMLExport::exportSparklineGroups(SCTAB nTable)
+{
+ sc::SparklineGroupsExport aSparklineGroupExport(*this, nTable);
+ aSparklineGroupExport.write();
+}
+
+namespace {
+
+OUString getCondFormatEntryType(const ScColorScaleEntry& rEntry, bool bFirst = true)
+{
+ switch(rEntry.GetType())
+ {
+ case COLORSCALE_MIN:
+ return "minimum";
+ case COLORSCALE_MAX:
+ return "maximum";
+ case COLORSCALE_PERCENT:
+ return "percent";
+ case COLORSCALE_PERCENTILE:
+ return "percentile";
+ case COLORSCALE_FORMULA:
+ return "formula";
+ case COLORSCALE_VALUE:
+ return "number";
+ case COLORSCALE_AUTO:
+ // only important for data bars
+ if(bFirst)
+ return "auto-minimum";
+ else
+ return "auto-maximum";
+ }
+ return OUString();
+}
+
+OUString getDateStringForType(condformat::ScCondFormatDateType eType)
+{
+ switch(eType)
+ {
+ case condformat::TODAY:
+ return "today";
+ case condformat::YESTERDAY:
+ return "yesterday";
+ case condformat::TOMORROW:
+ return "tomorrow";
+ case condformat::LAST7DAYS:
+ return "last-7-days";
+ case condformat::THISWEEK:
+ return "this-week";
+ case condformat::LASTWEEK:
+ return "last-week";
+ case condformat::NEXTWEEK:
+ return "next-week";
+ case condformat::THISMONTH:
+ return "this-month";
+ case condformat::LASTMONTH:
+ return "last-month";
+ case condformat::NEXTMONTH:
+ return "next-month";
+ case condformat::THISYEAR:
+ return "this-year";
+ case condformat::LASTYEAR:
+ return "last-year";
+ case condformat::NEXTYEAR:
+ return "next-year";
+ }
+
+ return OUString();
+}
+
+}
+
+void ScXMLExport::ExportConditionalFormat(SCTAB nTab)
+{
+ ScConditionalFormatList* pCondFormatList = pDoc->GetCondFormList(nTab);
+ if(!pCondFormatList)
+ return;
+
+ if (pCondFormatList->empty())
+ return;
+
+ SvXMLElementExport aElementCondFormats(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITIONAL_FORMATS, true, true);
+
+ for(const auto& rxCondFormat : *pCondFormatList)
+ {
+ OUString sRanges;
+ const ScRangeList& rRangeList = rxCondFormat->GetRange();
+ ScRangeStringConverter::GetStringFromRangeList( sRanges, &rRangeList, pDoc, formula::FormulaGrammar::CONV_OOO );
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TARGET_RANGE_ADDRESS, sRanges);
+ SvXMLElementExport aElementCondFormat(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITIONAL_FORMAT, true, true);
+ size_t nEntries = rxCondFormat->size();
+ for(size_t i = 0; i < nEntries; ++i)
+ {
+ const ScFormatEntry* pFormatEntry = rxCondFormat->GetEntry(i);
+ if(pFormatEntry->GetType()==ScFormatEntry::Type::Condition)
+ {
+ const ScCondFormatEntry* pEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+ OUStringBuffer aCond;
+ ScAddress aPos = pEntry->GetSrcPos();
+ switch(pEntry->GetOperation())
+ {
+ case ScConditionMode::Equal:
+ aCond.append('=');
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::Less:
+ aCond.append('<');
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::Greater:
+ aCond.append('>');
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::EqLess:
+ aCond.append("<=");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::EqGreater:
+ aCond.append(">=");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::NotEqual:
+ aCond.append("!=");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ break;
+ case ScConditionMode::Between:
+ aCond.append("between(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(',');
+ aCond.append(pEntry->GetExpression(aPos, 1, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(')');
+ break;
+ case ScConditionMode::NotBetween:
+ aCond.append("not-between(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(',');
+ aCond.append(pEntry->GetExpression(aPos, 1, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(')');
+ break;
+ case ScConditionMode::Duplicate:
+ aCond.append("duplicate");
+ break;
+ case ScConditionMode::NotDuplicate:
+ aCond.append("unique");
+ break;
+ case ScConditionMode::Direct:
+ aCond.append("formula-is(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(')');
+ break;
+ case ScConditionMode::Top10:
+ aCond.append("top-elements(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(")");
+ break;
+ case ScConditionMode::Bottom10:
+ aCond.append("bottom-elements(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(")");
+ break;
+ case ScConditionMode::TopPercent:
+ aCond.append("top-percent(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(")");
+ break;
+ case ScConditionMode::BottomPercent:
+ aCond.append("bottom-percent(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(")");
+ break;
+ case ScConditionMode::AboveAverage:
+ aCond.append("above-average");
+ break;
+ case ScConditionMode::BelowAverage:
+ aCond.append("below-average");
+ break;
+ case ScConditionMode::AboveEqualAverage:
+ aCond.append("above-equal-average");
+ break;
+ case ScConditionMode::BelowEqualAverage:
+ aCond.append("below-equal-average");
+ break;
+ case ScConditionMode::Error:
+ aCond.append("is-error");
+ break;
+ case ScConditionMode::NoError:
+ aCond.append("is-no-error");
+ break;
+ case ScConditionMode::BeginsWith:
+ aCond.append("begins-with(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(")");
+ break;
+ case ScConditionMode::EndsWith:
+ aCond.append("ends-with(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(")");
+ break;
+ case ScConditionMode::ContainsText:
+ aCond.append("contains-text(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(")");
+ break;
+ case ScConditionMode::NotContainsText:
+ aCond.append("not-contains-text(");
+ aCond.append(pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
+ aCond.append(")");
+ break;
+ case ScConditionMode::NONE:
+ continue;
+ default:
+ SAL_WARN("sc", "unimplemented conditional format export");
+ }
+ OUString sStyle = ScStyleNameConversion::DisplayToProgrammaticName(pEntry->GetStyle(), SfxStyleFamily::Para);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_APPLY_STYLE_NAME, sStyle);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, aCond.makeStringAndClear());
+
+ OUString sBaseAddress;
+ ScRangeStringConverter::GetStringFromAddress( sBaseAddress, aPos, pDoc,formula::FormulaGrammar::CONV_ODF );
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_BASE_CELL_ADDRESS, sBaseAddress);
+ SvXMLElementExport aElementCondEntry(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITION, true, true);
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
+ {
+ SvXMLElementExport aElementColorScale(*this, XML_NAMESPACE_CALC_EXT, XML_COLOR_SCALE, true, true);
+ const ScColorScaleFormat& rColorScale = static_cast<const ScColorScaleFormat&>(*pFormatEntry);
+ for(const auto& rxItem : rColorScale)
+ {
+ if(rxItem->GetType() == COLORSCALE_FORMULA)
+ {
+ OUString sFormula = rxItem->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
+ }
+ else
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(rxItem->GetValue()));
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*rxItem));
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, rxItem->GetColor());
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLOR, aBuffer.makeStringAndClear());
+ SvXMLElementExport aElementColorScaleEntry(*this, XML_NAMESPACE_CALC_EXT, XML_COLOR_SCALE_ENTRY, true, true);
+ }
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
+ {
+ const ScDataBarFormatData* pFormatData = static_cast<const ScDataBarFormat&>(*pFormatEntry).GetDataBarData();
+ if(!pFormatData->mbGradient)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_GRADIENT, XML_FALSE);
+ if(pFormatData->mbOnlyBar)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SHOW_VALUE, XML_FALSE);
+
+ if (pFormatData->mnMinLength != 0.0)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MIN_LENGTH, OUString::number(pFormatData->mnMinLength));
+
+ if (pFormatData->mnMaxLength != 0.0)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MAX_LENGTH, OUString::number(pFormatData->mnMaxLength));
+
+ if(pFormatData->mbNeg)
+ {
+ if(pFormatData->mxNegativeColor)
+ {
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, *pFormatData->mxNegativeColor);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_NEGATIVE_COLOR, aBuffer.makeStringAndClear());
+ }
+ else
+ {
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, COL_LIGHTRED);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_NEGATIVE_COLOR, aBuffer.makeStringAndClear());
+ }
+ }
+
+ if(pFormatData->meAxisPosition != databar::AUTOMATIC)
+ {
+ if(pFormatData->meAxisPosition == databar::NONE)
+ {
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_POSITION, OUString("none"));
+ }
+ else
+ {
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_POSITION, OUString("middle"));
+ }
+ }
+
+ OUStringBuffer aBuffer;
+ ::sax::Converter::convertColor(aBuffer, pFormatData->maPositiveColor);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_POSITIVE_COLOR, aBuffer.makeStringAndClear());
+
+ aBuffer.truncate();
+ ::sax::Converter::convertColor(aBuffer, pFormatData->maAxisColor);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_COLOR, aBuffer.makeStringAndClear());
+ SvXMLElementExport aElementDataBar(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_BAR, true, true);
+
+ {
+ if(pFormatData->mpLowerLimit->GetType() == COLORSCALE_FORMULA)
+ {
+ OUString sFormula = pFormatData->mpLowerLimit->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
+ }
+ else
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(pFormatData->mpLowerLimit->GetValue()));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*pFormatData->mpLowerLimit));
+ SvXMLElementExport aElementDataBarEntryLower(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
+ }
+
+ {
+ if(pFormatData->mpUpperLimit->GetType() == COLORSCALE_FORMULA)
+ {
+ OUString sFormula = pFormatData->mpUpperLimit->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
+ }
+ else
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(pFormatData->mpUpperLimit->GetValue()));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*pFormatData->mpUpperLimit, false));
+ SvXMLElementExport aElementDataBarEntryUpper(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
+ }
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
+ {
+ const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
+ OUString aIconSetName = OUString::createFromAscii(ScIconSetFormat::getIconSetName(rIconSet.GetIconSetData()->eIconSetType));
+ AddAttribute( XML_NAMESPACE_CALC_EXT, XML_ICON_SET_TYPE, aIconSetName );
+ if (rIconSet.GetIconSetData()->mbCustom)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM, OUString::boolean(true));
+
+ SvXMLElementExport aElementColorScale(*this, XML_NAMESPACE_CALC_EXT, XML_ICON_SET, true, true);
+
+ if (rIconSet.GetIconSetData()->mbCustom)
+ {
+ for (const auto& [rType, rIndex] : rIconSet.GetIconSetData()->maCustomVector)
+ {
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET_NAME, OUString::createFromAscii(ScIconSetFormat::getIconSetName(rType)));
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET_INDEX, OUString::number(rIndex));
+ SvXMLElementExport aCustomIcon(*this, XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET, true, true);
+ }
+
+ }
+
+ if(!rIconSet.GetIconSetData()->mbShowValue)
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SHOW_VALUE, XML_FALSE);
+ for (auto const& it : rIconSet)
+ {
+ if(it->GetType() == COLORSCALE_FORMULA)
+ {
+ OUString sFormula = it->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
+ }
+ else
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(it->GetValue()));
+
+ AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*it));
+ SvXMLElementExport aElementColorScaleEntry(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
+ }
+ }
+ else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
+ {
+ const ScCondDateFormatEntry& rDateFormat = static_cast<const ScCondDateFormatEntry&>(*pFormatEntry);
+ OUString aDateType = getDateStringForType(rDateFormat.GetDateType());
+ OUString aStyleName = ScStyleNameConversion::DisplayToProgrammaticName(rDateFormat.GetStyleName(), SfxStyleFamily::Para );
+ AddAttribute( XML_NAMESPACE_CALC_EXT, XML_STYLE, aStyleName);
+ AddAttribute( XML_NAMESPACE_CALC_EXT, XML_DATE, aDateType);
+ SvXMLElementExport aElementDateFormat(*this, XML_NAMESPACE_CALC_EXT, XML_DATE_IS, true, true);
+ }
+ }
+ }
+}
+
+void ScXMLExport::WriteExternalRefCaches()
+{
+ if (!pDoc)
+ return;
+
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ pRefMgr->resetSrcFileData(GetOrigFileName());
+ sal_uInt16 nCount = pRefMgr->getExternalFileCount();
+ for (sal_uInt16 nFileId = 0; nFileId < nCount; ++nFileId)
+ {
+ const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
+ if (!pUrl)
+ continue;
+
+ vector<OUString> aTabNames;
+ pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
+ if (aTabNames.empty())
+ continue;
+
+ for (const auto& rTabName : aTabNames)
+ {
+ ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, rTabName, false);
+ if (!pTable || !pTable->isReferenced())
+ continue;
+
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, "'" + *pUrl + "'#" + rTabName);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_PRINT, GetXMLToken(XML_FALSE));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, sExternalRefTabStyleName);
+ SvXMLElementExport aElemTable(*this, XML_NAMESPACE_TABLE, XML_TABLE, true, true);
+ {
+ const ScExternalRefManager::SrcFileData* pExtFileData = pRefMgr->getExternalFileData(nFileId);
+ if (pExtFileData)
+ {
+ OUString aRelUrl;
+ if (!pExtFileData->maRelativeName.isEmpty())
+ aRelUrl = pExtFileData->maRelativeName;
+ else
+ aRelUrl = GetRelativeReference(pExtFileData->maRelativeName);
+ AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
+ AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, aRelUrl);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_NAME, rTabName);
+ if (!pExtFileData->maFilterName.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_NAME, pExtFileData->maFilterName);
+ if (!pExtFileData->maFilterOptions.isEmpty())
+ AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, pExtFileData->maFilterOptions);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_MODE, XML_COPY_RESULTS_ONLY);
+ }
+ SvXMLElementExport aElemTableSource(*this, XML_NAMESPACE_TABLE, XML_TABLE_SOURCE, true, true);
+ }
+
+ // Determine maximum column count of used area, for repeated cells.
+ SCCOL nMaxColsUsed = 1; // assume that there is at least one cell somewhere...
+ vector<SCROW> aRows;
+ pTable->getAllRows(aRows);
+ for (SCROW nRow : aRows)
+ {
+ vector<SCCOL> aCols;
+ pTable->getAllCols(nRow, aCols);
+ if (!aCols.empty())
+ {
+ SCCOL nCol = aCols.back();
+ if (nMaxColsUsed <= nCol)
+ nMaxColsUsed = nCol + 1;
+ }
+ }
+
+ // Column definitions have to be present to make a valid file
+ {
+ if (nMaxColsUsed > 1)
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
+ OUString::number(nMaxColsUsed));
+ SvXMLElementExport aElemColumn(*this, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true);
+ }
+
+ // Write cache content for this table.
+ SCROW nLastRow = 0;
+ bool bFirstRow = true;
+ for (SCROW nRow : aRows)
+ {
+ if (bFirstRow)
+ {
+ if (nRow > 0)
+ {
+ if (nRow > 1)
+ {
+ OUString aVal = OUString::number(nRow);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, aVal);
+ }
+ SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nMaxColsUsed));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ }
+ }
+ else
+ {
+ SCROW nRowGap = nRow - nLastRow;
+ if (nRowGap > 1)
+ {
+ if (nRowGap > 2)
+ {
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nRowGap-1));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, aVal);
+ }
+ SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nMaxColsUsed));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ }
+ }
+ SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
+
+ vector<SCCOL> aCols;
+ pTable->getAllCols(nRow, aCols);
+ SCCOL nLastCol = 0;
+ bool bFirstCol = true;
+ for (SCCOL nCol : aCols)
+ {
+ if (bFirstCol)
+ {
+ if (nCol > 0)
+ {
+ if (nCol > 1)
+ {
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nCol));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
+ }
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ }
+ }
+ else
+ {
+ SCCOL nColGap = nCol - nLastCol;
+ if (nColGap > 1)
+ {
+ if (nColGap > 2)
+ {
+ OUString aVal = OUString::number(static_cast<sal_Int32>(nColGap-1));
+ AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
+ }
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ }
+ }
+
+ // Write out this cell.
+ sal_uInt32 nNumFmt = 0;
+ ScExternalRefCache::TokenRef pToken = pTable->getCell(nCol, nRow, &nNumFmt);
+ OUString aStrVal;
+ if (pToken)
+ {
+ sal_Int32 nIndex = GetNumberFormatStyleIndex(nNumFmt);
+ if (nIndex >= 0)
+ {
+ const OUString & aStyleName = pCellStyles->GetStyleNameByIndex(nIndex, true);
+ AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, aStyleName);
+ }
+
+ switch(pToken->GetType())
+ {
+ case svDouble:
+ {
+ AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT);
+ OUStringBuffer aVal;
+ aVal.append(pToken->GetDouble());
+ aStrVal = aVal.makeStringAndClear();
+ AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE, aStrVal);
+ }
+ break;
+ case svString:
+ {
+ AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
+ aStrVal = pToken->GetString().getString();
+ }
+ break;
+ default:
+ ;
+ }
+ }
+ SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
+ SvXMLElementExport aElemText(*this, XML_NAMESPACE_TEXT, XML_P, true, false);
+ Characters(aStrVal);
+
+ nLastCol = nCol;
+ bFirstCol = false;
+ }
+ nLastRow = nRow;
+ bFirstRow = false;
+ }
+ }
+ }
+}
+
+// core implementation
+void ScXMLExport::WriteConsolidation()
+{
+ if (!pDoc)
+ return;
+
+ const ScConsolidateParam* pCons(pDoc->GetConsolidateDlgData());
+ if( !pCons )
+ return;
+
+ OUString sStrData = ScXMLConverter::GetStringFromFunction( pCons->eFunction );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_FUNCTION, sStrData );
+
+ sStrData.clear();
+ for( sal_Int32 nIndex = 0; nIndex < pCons->nDataAreaCount; ++nIndex )
+ ScRangeStringConverter::GetStringFromArea( sStrData, pCons->pDataAreas[ nIndex ], pDoc, FormulaGrammar::CONV_OOO, ' ', true );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_SOURCE_CELL_RANGE_ADDRESSES, sStrData );
+
+ ScRangeStringConverter::GetStringFromAddress( sStrData, ScAddress( pCons->nCol, pCons->nRow, pCons->nTab ), pDoc, FormulaGrammar::CONV_OOO );
+ AddAttribute( XML_NAMESPACE_TABLE, XML_TARGET_CELL_ADDRESS, sStrData );
+
+ if( pCons->bByCol && !pCons->bByRow )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_COLUMN );
+ else if( !pCons->bByCol && pCons->bByRow )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_ROW );
+ else if( pCons->bByCol && pCons->bByRow )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_BOTH );
+
+ if( pCons->bReferenceData )
+ AddAttribute( XML_NAMESPACE_TABLE, XML_LINK_TO_SOURCE_DATA, XML_TRUE );
+
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_CONSOLIDATION, true, true );
+}
+
+SvXMLAutoStylePoolP* ScXMLExport::CreateAutoStylePool()
+{
+ return new ScXMLAutoStylePoolP(*this);
+}
+
+XMLPageExport* ScXMLExport::CreatePageExport()
+{
+ return new XMLTableMasterPageExport( *this );
+}
+
+void ScXMLExport::GetChangeTrackViewSettings(uno::Sequence<beans::PropertyValue>& rProps)
+{
+ ScChangeViewSettings* pViewSettings(GetDocument() ? GetDocument()->GetChangeViewSettings() : nullptr);
+ if (!pViewSettings)
+ return;
+
+ sal_Int32 nChangePos(rProps.getLength());
+ rProps.realloc(nChangePos + 1);
+ beans::PropertyValue* pProps(rProps.getArray());
+
+ uno::Sequence<beans::PropertyValue> aChangeProps(SC_VIEWCHANGES_COUNT);
+ beans::PropertyValue* pChangeProps(aChangeProps.getArray());
+ pChangeProps[SC_SHOW_CHANGES].Name = "ShowChanges";
+ pChangeProps[SC_SHOW_CHANGES].Value <<= pViewSettings->ShowChanges();
+ pChangeProps[SC_SHOW_ACCEPTED_CHANGES].Name = "ShowAcceptedChanges";
+ pChangeProps[SC_SHOW_ACCEPTED_CHANGES].Value <<= pViewSettings->IsShowAccepted();
+ pChangeProps[SC_SHOW_REJECTED_CHANGES].Name = "ShowRejectedChanges";
+ pChangeProps[SC_SHOW_REJECTED_CHANGES].Value <<= pViewSettings->IsShowRejected();
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME].Name = "ShowChangesByDatetime";
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME].Value <<= pViewSettings->HasDate();
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_MODE].Name = "ShowChangesByDatetimeMode";
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_MODE].Value <<= static_cast<sal_Int16>(pViewSettings->GetTheDateMode());
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME].Name = "ShowChangesByDatetimeFirstDatetime";
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME].Value <<= pViewSettings->GetTheFirstDateTime().GetUNODateTime();
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME].Name = "ShowChangesByDatetimeSecondDatetime";
+ pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME].Value <<= pViewSettings->GetTheLastDateTime().GetUNODateTime();
+ pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR].Name = "ShowChangesByAuthor";
+ pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR].Value <<= pViewSettings->HasAuthor();
+ pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR_NAME].Name = "ShowChangesByAuthorName";
+ pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR_NAME].Value <<= pViewSettings->GetTheAuthorToShow();
+ pChangeProps[SC_SHOW_CHANGES_BY_COMMENT].Name = "ShowChangesByComment";
+ pChangeProps[SC_SHOW_CHANGES_BY_COMMENT].Value <<= pViewSettings->HasComment();
+ pChangeProps[SC_SHOW_CHANGES_BY_COMMENT_TEXT].Name = "ShowChangesByCommentText";
+ pChangeProps[SC_SHOW_CHANGES_BY_COMMENT_TEXT].Value <<= pViewSettings->GetTheComment();
+ pChangeProps[SC_SHOW_CHANGES_BY_RANGES].Name = "ShowChangesByRanges";
+ pChangeProps[SC_SHOW_CHANGES_BY_RANGES].Value <<= pViewSettings->HasRange();
+ OUString sRangeList;
+ ScRangeStringConverter::GetStringFromRangeList(sRangeList, &(pViewSettings->GetTheRangeList()), GetDocument(), FormulaGrammar::CONV_OOO);
+ pChangeProps[SC_SHOW_CHANGES_BY_RANGES_LIST].Name = "ShowChangesByRangesList";
+ pChangeProps[SC_SHOW_CHANGES_BY_RANGES_LIST].Value <<= sRangeList;
+
+ pProps[nChangePos].Name = "TrackedChangesViewSettings";
+ pProps[nChangePos].Value <<= aChangeProps;
+}
+
+void ScXMLExport::GetViewSettings(uno::Sequence<beans::PropertyValue>& rProps)
+{
+ if (GetModel().is())
+ {
+ rProps.realloc(4);
+ beans::PropertyValue* pProps(rProps.getArray());
+ ScModelObj* pDocObj(comphelper::getFromUnoTunnel<ScModelObj>( GetModel() ));
+ if (pDocObj)
+ {
+ SfxObjectShell* pEmbeddedObj = pDocObj->GetEmbeddedObject();
+ if (pEmbeddedObj)
+ {
+ tools::Rectangle aRect(pEmbeddedObj->GetVisArea());
+ sal_uInt16 i(0);
+ pProps[i].Name = "VisibleAreaTop";
+ pProps[i].Value <<= static_cast<sal_Int32>(aRect.Top());
+ pProps[++i].Name = "VisibleAreaLeft";
+ pProps[i].Value <<= static_cast<sal_Int32>(aRect.Left());
+ pProps[++i].Name = "VisibleAreaWidth";
+ pProps[i].Value <<= static_cast<sal_Int32>(aRect.getWidth());
+ pProps[++i].Name = "VisibleAreaHeight";
+ pProps[i].Value <<= static_cast<sal_Int32>(aRect.getHeight());
+ }
+ }
+ }
+ GetChangeTrackViewSettings(rProps);
+}
+
+void ScXMLExport::GetConfigurationSettings(uno::Sequence<beans::PropertyValue>& rProps)
+{
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
+ if (!xMultiServiceFactory.is())
+ return;
+
+ uno::Reference <beans::XPropertySet> xProperties(xMultiServiceFactory->createInstance("com.sun.star.comp.SpreadsheetSettings"), uno::UNO_QUERY);
+ if (xProperties.is())
+ SvXMLUnitConverter::convertPropertySet(rProps, xProperties);
+
+ sal_Int32 nPropsToAdd = 0;
+ OUStringBuffer aTrackedChangesKey;
+ if (GetDocument() && GetDocument()->GetChangeTrack() && GetDocument()->GetChangeTrack()->IsProtected())
+ {
+ ::comphelper::Base64::encode(aTrackedChangesKey,
+ GetDocument()->GetChangeTrack()->GetProtection());
+ if (!aTrackedChangesKey.isEmpty())
+ ++nPropsToAdd;
+ }
+
+ bool bVBACompat = false;
+ uno::Reference <container::XNameAccess> xCodeNameAccess;
+ OSL_ENSURE( pDoc, "ScXMLExport::GetConfigurationSettings - no ScDocument!" );
+ // tdf#71271 - add code names regardless of VBA compatibility mode
+ if (pDoc)
+ {
+ // VBA compatibility mode
+ if (bVBACompat = pDoc->IsInVBAMode(); bVBACompat)
+ ++nPropsToAdd;
+
+ // code names
+ xCodeNameAccess = new XMLCodeNameProvider( pDoc );
+ if( xCodeNameAccess->hasElements() )
+ ++nPropsToAdd;
+ else
+ xCodeNameAccess.clear();
+ }
+
+ if( nPropsToAdd <= 0 )
+ return;
+
+ sal_Int32 nCount(rProps.getLength());
+ rProps.realloc(nCount + nPropsToAdd);
+ auto pProps = rProps.getArray();
+ if (!aTrackedChangesKey.isEmpty())
+ {
+ pProps[nCount].Name = "TrackedChangesProtectionKey";
+ pProps[nCount].Value <<= aTrackedChangesKey.makeStringAndClear();
+ ++nCount;
+ }
+ if( bVBACompat )
+ {
+ pProps[nCount].Name = "VBACompatibilityMode";
+ pProps[nCount].Value <<= bVBACompat;
+ ++nCount;
+ }
+ if( xCodeNameAccess.is() )
+ {
+ pProps[nCount].Name = "ScriptConfiguration";
+ pProps[nCount].Value <<= xCodeNameAccess;
+ ++nCount;
+ }
+}
+
+XMLShapeExport* ScXMLExport::CreateShapeExport()
+{
+ return new ScXMLShapeExport(*this);
+}
+
+XMLNumberFormatAttributesExportHelper* ScXMLExport::GetNumberFormatAttributesExportHelper()
+{
+ if (!pNumberFormatAttributesExportHelper)
+ pNumberFormatAttributesExportHelper.reset(new XMLNumberFormatAttributesExportHelper(GetNumberFormatsSupplier(), *this ));
+ return pNumberFormatAttributesExportHelper.get();
+}
+
+void ScXMLExport::CollectUserDefinedNamespaces(const SfxItemPool* pPool, sal_uInt16 nAttrib)
+{
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(nAttrib))
+ {
+ const SvXMLAttrContainerItem *pUnknown(static_cast<const SvXMLAttrContainerItem *>(pItem));
+ if( pUnknown->GetAttrCount() > 0 )
+ {
+ sal_uInt16 nIdx(pUnknown->GetFirstNamespaceIndex());
+ while( USHRT_MAX != nIdx )
+ {
+ if( (XML_NAMESPACE_UNKNOWN_FLAG & nIdx) != 0 )
+ {
+ const OUString& rPrefix = pUnknown->GetPrefix( nIdx );
+ // Add namespace declaration for unknown attributes if
+ // there aren't existing ones for the prefix used by the
+ // attributes
+ GetNamespaceMap_().Add( rPrefix,
+ pUnknown->GetNamespace( nIdx ) );
+ }
+ nIdx = pUnknown->GetNextNamespaceIndex( nIdx );
+ }
+ }
+ }
+
+ // #i66550# needed for 'presentation:event-listener' element for URLs in shapes
+ GetNamespaceMap_().Add(
+ GetXMLToken( XML_NP_PRESENTATION ),
+ GetXMLToken( XML_N_PRESENTATION ),
+ XML_NAMESPACE_PRESENTATION );
+}
+
+void ScXMLExport::IncrementProgressBar(bool bFlush, sal_Int32 nInc)
+{
+ nProgressCount += nInc;
+ if (bFlush || nProgressCount > 100)
+ {
+ GetProgressBarHelper()->Increment(nProgressCount);
+ nProgressCount = 0;
+ }
+}
+
+ErrCode ScXMLExport::exportDoc( enum XMLTokenEnum eClass )
+{
+ if( getExportFlags() & (SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::STYLES|
+ SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT) )
+ {
+ if (GetDocument())
+ {
+ // if source doc was Excel then
+ uno::Reference< frame::XModel > xModel = GetModel();
+ if ( xModel.is() )
+ {
+ auto pFoundShell = comphelper::getFromUnoTunnel<SfxObjectShell>(xModel);
+ if ( pFoundShell && ooo::vba::isAlienExcelDoc( *pFoundShell ) )
+ {
+ xRowStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScFromXLSRowStylesProperties, xScPropHdlFactory, true);
+ xRowStylesExportPropertySetMapper = new ScXMLRowExportPropertyMapper(xRowStylesPropertySetMapper);
+ GetAutoStylePool()->SetFamilyPropSetMapper( XmlStyleFamily::TABLE_ROW,
+ xRowStylesExportPropertySetMapper );
+ }
+ }
+ CollectUserDefinedNamespaces(GetDocument()->GetPool(), ATTR_USERDEF);
+ CollectUserDefinedNamespaces(GetDocument()->GetEditPool(), EE_PARA_XMLATTRIBS);
+ CollectUserDefinedNamespaces(GetDocument()->GetEditPool(), EE_CHAR_XMLATTRIBS);
+ ScDrawLayer* pDrawLayer = GetDocument()->GetDrawLayer();
+ if (pDrawLayer)
+ {
+ CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), EE_PARA_XMLATTRIBS);
+ CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), EE_CHAR_XMLATTRIBS);
+ CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), SDRATTR_XMLATTRIBUTES);
+ }
+
+ // sheet events use officeooo namespace
+ if( (getExportFlags() & SvXMLExportFlags::CONTENT) &&
+ getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
+ {
+ bool bAnySheetEvents = false;
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
+ if (pDoc->GetSheetEvents(nTab))
+ bAnySheetEvents = true;
+ if (bAnySheetEvents)
+ GetNamespaceMap_().Add(
+ GetXMLToken( XML_NP_OFFICE_EXT ),
+ GetXMLToken( XML_N_OFFICE_EXT ),
+ XML_NAMESPACE_OFFICE_EXT );
+ }
+ }
+ }
+ return SvXMLExport::exportDoc( eClass );
+}
+
+// XExporter
+void SAL_CALL ScXMLExport::setSourceDocument( const uno::Reference<lang::XComponent>& xComponent )
+{
+ SolarMutexGuard aGuard;
+ SvXMLExport::setSourceDocument( xComponent );
+
+ pDoc = ScXMLConverter::GetScDocument( GetModel() );
+ OSL_ENSURE( pDoc, "ScXMLExport::setSourceDocument - no ScDocument!" );
+ if (!pDoc)
+ throw lang::IllegalArgumentException();
+
+ // create ScChangeTrackingExportHelper after document is known
+ pChangeTrackingExportHelper.reset(new ScChangeTrackingExportHelper(*this));
+
+ // Set the document's storage grammar corresponding to the ODF version that
+ // is to be written.
+ SvtSaveOptions::ODFSaneDefaultVersion meODFDefaultVersion = getSaneDefaultVersion();
+ switch (meODFDefaultVersion)
+ {
+ // ODF 1.0 and 1.1 use GRAM_PODF, everything later or unspecified GRAM_ODFF
+ case SvtSaveOptions::ODFSVER_010:
+ case SvtSaveOptions::ODFSVER_011:
+ pDoc->SetStorageGrammar( formula::FormulaGrammar::GRAM_PODF);
+ break;
+ default:
+ pDoc->SetStorageGrammar( formula::FormulaGrammar::GRAM_ODFF);
+ }
+}
+
+// XFilter
+sal_Bool SAL_CALL ScXMLExport::filter( const css::uno::Sequence< css::beans::PropertyValue >& aDescriptor )
+{
+ SolarMutexGuard aGuard;
+ if (pDoc)
+ pDoc->EnableIdle(false);
+ bool bReturn(SvXMLExport::filter(aDescriptor));
+ if (pDoc)
+ pDoc->EnableIdle(true);
+ return bReturn;
+}
+
+void SAL_CALL ScXMLExport::cancel()
+{
+ SolarMutexGuard aGuard;
+ if (pDoc)
+ pDoc->EnableIdle(true);
+ SvXMLExport::cancel();
+}
+
+// XInitialization
+void SAL_CALL ScXMLExport::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ SolarMutexGuard aGuard;
+ SvXMLExport::initialize(aArguments);
+}
+
+// XUnoTunnel
+sal_Int64 SAL_CALL ScXMLExport::getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier )
+{
+ SolarMutexGuard aGuard;
+ return SvXMLExport::getSomething(aIdentifier);
+}
+
+void ScXMLExport::DisposingModel()
+{
+ SvXMLExport::DisposingModel();
+ pDoc = nullptr;
+ xCurrentTable = nullptr;
+}
+
+void ScXMLExport::SetSharedData(std::unique_ptr<ScMySharedData> pTemp) { pSharedData = std::move(pTemp); }
+
+std::unique_ptr<ScMySharedData> ScXMLExport::ReleaseSharedData() { return std::move(pSharedData); }
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlexprt.hxx b/sc/source/filter/xml/xmlexprt.hxx
new file mode 100644
index 000000000..8ab8901d4
--- /dev/null
+++ b/sc/source/filter/xml/xmlexprt.hxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <xmloff/xmlexp.hxx>
+#include <com/sun/star/table/CellRangeAddress.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+#include <address.hxx>
+
+#include <memory>
+#include <unordered_map>
+
+
+namespace com::sun::star {
+ namespace beans { class XPropertySet; }
+}
+
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::sheet { class XSpreadsheet; }
+namespace com::sun::star::sheet { class XSpreadsheetDocument; }
+
+namespace sc { class DataTransformation; }
+
+class ScOutlineArray;
+class SvXMLExportPropertyMapper;
+class ScMyMergedRangesContainer;
+class ScMyValidationsContainer;
+class ScMyNotEmptyCellsIterator;
+class ScChangeTrackingExportHelper;
+class ScColumnStyles;
+class ScRowStyles;
+class ScFormatRangeStyles;
+class ScRowFormatRanges;
+class ScMyOpenCloseColumnRowGroup;
+class ScMyAreaLinksContainer;
+class ScMyDetectiveOpContainer;
+struct ScMyCell;
+class ScDocument;
+class ScMySharedData;
+class ScMyDefaultStyles;
+class XMLNumberFormatAttributesExportHelper;
+class SfxItemPool;
+class ScXMLCachedRowAttrAccess;
+class ScRangeName;
+class ScXMLEditAttributeMap;
+class EditTextObject;
+class ScFormulaCell;
+
+namespace sc {
+
+class CompileFormulaContext;
+
+}
+
+
+class ScXMLExport : public SvXMLExport
+{
+ ScDocument* pDoc;
+ css::uno::Reference <css::sheet::XSpreadsheet> xCurrentTable;
+
+ css::uno::Reference<css::io::XInputStream> xSourceStream;
+ sal_Int32 nSourceStreamPos;
+
+ mutable std::unique_ptr<ScXMLEditAttributeMap> mpEditAttrMap;
+ std::unique_ptr<ScMyNotEmptyCellsIterator> mpCellsItr;
+ std::unique_ptr<sc::CompileFormulaContext> mpCompileFormulaCxt;
+ rtl::Reference < XMLPropertyHandlerFactory > xScPropHdlFactory;
+ rtl::Reference < XMLPropertySetMapper > xCellStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xColumnStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xRowStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xTableStylesPropertySetMapper;
+ rtl::Reference < SvXMLExportPropertyMapper > xCellStylesExportPropertySetMapper;
+ rtl::Reference < SvXMLExportPropertyMapper > xColumnStylesExportPropertySetMapper;
+ rtl::Reference < SvXMLExportPropertyMapper > xRowStylesExportPropertySetMapper;
+ rtl::Reference < SvXMLExportPropertyMapper > xTableStylesExportPropertySetMapper;
+ std::unique_ptr<XMLNumberFormatAttributesExportHelper> pNumberFormatAttributesExportHelper;
+ typedef std::unordered_map<sal_Int32, sal_Int32> NumberFormatIndexMap;
+ NumberFormatIndexMap aNumFmtIndexMap;
+ std::unique_ptr<ScMySharedData> pSharedData;
+ std::unique_ptr<ScColumnStyles> pColumnStyles;
+ std::unique_ptr<ScRowStyles> pRowStyles;
+ std::unique_ptr<ScFormatRangeStyles> pCellStyles;
+ std::unique_ptr<ScRowFormatRanges> pRowFormatRanges;
+ std::vector<OUString> aTableStyles;
+ ScRange aRowHeaderRange;
+ std::unique_ptr<ScMyOpenCloseColumnRowGroup> pGroupColumns;
+ std::unique_ptr<ScMyOpenCloseColumnRowGroup> pGroupRows;
+ std::unique_ptr<ScMyDefaultStyles> pDefaults;
+ const ScMyCell* pCurrentCell;
+
+ std::unique_ptr<ScMyMergedRangesContainer> pMergedRangesContainer;
+ std::unique_ptr<ScMyValidationsContainer> pValidationsContainer;
+ std::unique_ptr<ScChangeTrackingExportHelper> pChangeTrackingExportHelper;
+ OUString sExternalRefTabStyleName;
+ OUString sAttrName;
+ OUString sAttrStyleName;
+ OUString sAttrColumnsRepeated;
+ OUString sAttrFormula;
+ OUString sAttrValueType;
+ OUString sAttrStringValue;
+ OUString sElemCell;
+ OUString sElemCoveredCell;
+ OUString sElemCol;
+ OUString sElemRow;
+ OUString sElemTab;
+ OUString sElemP;
+ sal_Int32 nOpenRow;
+ sal_Int32 nProgressCount;
+ sal_uInt16 nCurrentTable;
+ bool bHasRowHeader;
+ bool bRowHeaderOpen;
+
+ sal_Int32 GetNumberFormatStyleIndex(sal_Int32 nNumFmt) const;
+ void CollectSharedData(SCTAB& nTableCount, sal_Int32& nShapesCount);
+ void CollectShapesAutoStyles(SCTAB nTableCount);
+ void RegisterDefinedStyleNames( const css::uno::Reference< css::sheet::XSpreadsheetDocument > & xSpreadDoc );
+ virtual void ExportFontDecls_() override;
+ virtual void ExportStyles_( bool bUsed ) override;
+ virtual void ExportAutoStyles_() override;
+ virtual void ExportMasterStyles_() override;
+ virtual void SetBodyAttributes() override;
+ virtual void ExportContent_() override;
+ virtual void ExportMeta_() override;
+
+ void CollectInternalShape( css::uno::Reference< css::drawing::XShape > const & xShape );
+
+ static css::table::CellRangeAddress GetEndAddress(const css::uno::Reference<css::sheet::XSpreadsheet>& xTable);
+ void GetAreaLinks( ScMyAreaLinksContainer& rAreaLinks );
+ void GetDetectiveOpList( ScMyDetectiveOpContainer& rDetOp );
+ void WriteSingleColumn(const sal_Int32 nRepeatColumns, const sal_Int32 nStyleIndex,
+ const sal_Int32 nIndex, const bool bIsAutoStyle, const bool bIsVisible);
+ void WriteColumn(const sal_Int32 nColumn, const sal_Int32 nRepeatColumns,
+ const sal_Int32 nStyleIndex, const bool bIsVisible);
+ void OpenHeaderColumn();
+ void CloseHeaderColumn();
+ void ExportColumns(const sal_Int32 nTable, const ScRange& aColumnHeaderRange, const bool bHasColumnHeader);
+ void ExportExternalRefCacheStyles();
+ void ExportCellTextAutoStyles(sal_Int32 nTable);
+ void ExportFormatRanges(const sal_Int32 nStartCol, const sal_Int32 nStartRow,
+ const sal_Int32 nEndCol, const sal_Int32 nEndRow, const sal_Int32 nSheet);
+ void WriteRowContent();
+ void WriteRowStartTag(const sal_Int32 nIndex, const sal_Int32 nEmptyRows, bool bHidden, bool bFiltered);
+ void OpenHeaderRows();
+ void CloseHeaderRows();
+ void OpenNewRow(const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEmptyRows,
+ bool bHidden, bool bFiltered);
+ void OpenAndCloseRow(const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEmptyRows,
+ bool bHidden, bool bFiltered);
+ void OpenRow(const sal_Int32 nTable, const sal_Int32 nStartRow, const sal_Int32 nRepeatRow, ScXMLCachedRowAttrAccess& rRowAttr);
+ void CloseRow(const sal_Int32 nRow);
+ void GetColumnRowHeader(bool& bHasColumnHeader, ScRange& aColumnHeaderRange,
+ bool& bHasRowHeader, ScRange& aRowHeaderRange, OUString& rPrintRanges) const;
+ static void FillFieldGroup(ScOutlineArray* pFields, ScMyOpenCloseColumnRowGroup* pGroups);
+ void FillColumnRowGroups();
+
+ bool GetMerged (const css::table::CellRangeAddress* pCellRange,
+ const css::uno::Reference <css::sheet::XSpreadsheet>& xTable);
+
+ void WriteTable(sal_Int32 nTable, const css::uno::Reference< css::sheet::XSpreadsheet>& xTable);
+ void WriteCell(ScMyCell& aCell, sal_Int32 nEqualCellCount);
+ void WriteEditCell(const EditTextObject* pText);
+ void WriteMultiLineFormulaResult(const ScFormulaCell* pCell);
+ void WriteAreaLink(const ScMyCell& rMyCell);
+ void WriteAnnotation(const ScMyCell& rMyCell);
+ void WriteDetective(const ScMyCell& rMyCell);
+ void ExportShape(const css::uno::Reference < css::drawing::XShape >& xShape, css::awt::Point* pPoint);
+ void WriteShapes(const ScMyCell& rMyCell);
+ void WriteTableShapes();
+ void SetRepeatAttribute(sal_Int32 nEqualCellCount, bool bIncProgress);
+
+ static bool IsEditCell(const ScMyCell& rCell);
+ bool IsCellEqual(const ScMyCell& aCell1, const ScMyCell& aCell2);
+
+ void WriteCalculationSettings(const css::uno::Reference <css::sheet::XSpreadsheetDocument>& xSpreadDoc);
+ void WriteTableSource();
+ void WriteScenario(); // core implementation
+ void WriteTheLabelRanges(const css::uno::Reference< css::sheet::XSpreadsheetDocument >& xSpreadDoc);
+ void WriteLabelRanges( const css::uno::Reference< css::container::XIndexAccess >& xRangesIAccess, bool bColumn );
+ void WriteNamedExpressions();
+ void WriteExternalDataMapping();
+ void WriteExternalDataTransformations(const std::vector<std::shared_ptr<sc::DataTransformation>>& aDataTransformations);
+ void WriteDataStream();
+ void WriteNamedRange(ScRangeName* pRangeName);
+ void exportSparklineGroups(SCTAB nTab);
+ void ExportConditionalFormat(SCTAB nTab);
+ void WriteExternalRefCaches();
+ void WriteConsolidation(); // core implementation
+
+ void CollectUserDefinedNamespaces(const SfxItemPool* pPool, sal_uInt16 nAttrib);
+
+ void AddStyleFromCells(
+ const css::uno::Reference< css::beans::XPropertySet >& xProperties,
+ const css::uno::Reference< css::sheet::XSpreadsheet >& xTable,
+ sal_Int32 nTable, const OUString* pOldName );
+ void AddStyleFromColumn(
+ const css::uno::Reference< css::beans::XPropertySet >& xColumnProperties,
+ const OUString* pOldName, sal_Int32& rIndex, bool& rIsVisible );
+ void AddStyleFromRow(
+ const css::uno::Reference< css::beans::XPropertySet >& xRowProperties,
+ const OUString* pOldName, sal_Int32& rIndex );
+
+ void IncrementProgressBar(bool bFlush, sal_Int32 nInc = 1);
+
+ void CopySourceStream( sal_Int32 nStartOffset, sal_Int32 nEndOffset, sal_Int32& rNewStart, sal_Int32& rNewEnd );
+
+ const ScXMLEditAttributeMap& GetEditAttributeMap() const;
+
+protected:
+ virtual SvXMLAutoStylePoolP* CreateAutoStylePool() override;
+ virtual XMLPageExport* CreatePageExport() override;
+ virtual XMLShapeExport* CreateShapeExport() override;
+ virtual XMLFontAutoStylePool* CreateFontAutoStylePool() override;
+public:
+ ScXMLExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLExportFlags nExportFlag);
+
+ virtual ~ScXMLExport() override;
+
+ void collectAutoStyles() override;
+
+ static sal_Int16 GetMeasureUnit();
+ ScDocument* GetDocument() { return pDoc; }
+ const ScDocument* GetDocument() const { return pDoc; }
+ bool IsMatrix (const ScAddress& aCell,
+ ScRange& aCellAddress, bool& bIsFirst) const;
+
+ const rtl::Reference < XMLPropertySetMapper >& GetCellStylesPropertySetMapper() const { return xCellStylesPropertySetMapper; }
+ const rtl::Reference < XMLPropertySetMapper >& GetTableStylesPropertySetMapper() const { return xTableStylesPropertySetMapper; }
+
+ void SetSourceStream( const css::uno::Reference<css::io::XInputStream>& xNewStream );
+
+ void GetChangeTrackViewSettings(css::uno::Sequence<css::beans::PropertyValue>& rProps);
+ virtual void GetViewSettings(css::uno::Sequence<css::beans::PropertyValue>& rProps) override;
+ virtual void GetConfigurationSettings(css::uno::Sequence<css::beans::PropertyValue>& rProps) override;
+
+ virtual void exportAnnotationMeta( const css::uno::Reference < css::drawing::XShape >& xShape) override;
+
+ void SetSharedData(std::unique_ptr<ScMySharedData> pTemp);
+ ScMySharedData* GetSharedData() { return pSharedData.get(); }
+ std::unique_ptr<ScMySharedData> ReleaseSharedData();
+ XMLNumberFormatAttributesExportHelper* GetNumberFormatAttributesExportHelper();
+
+ // Export the document.
+ virtual ErrCode exportDoc( enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID ) override;
+
+ // XExporter
+ virtual void SAL_CALL setSourceDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) override;
+
+ // XFilter
+ virtual sal_Bool SAL_CALL filter( const css::uno::Sequence< css::beans::PropertyValue >& aDescriptor ) override;
+ virtual void SAL_CALL cancel() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+ virtual void DisposingModel() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlexternaltabi.cxx b/sc/source/filter/xml/xmlexternaltabi.cxx
new file mode 100644
index 000000000..91bb218bb
--- /dev/null
+++ b/sc/source/filter/xml/xmlexternaltabi.cxx
@@ -0,0 +1,378 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlexternaltabi.hxx"
+#include "xmlimprt.hxx"
+#include "xmltabi.hxx"
+#include "xmlstyli.hxx"
+
+#include <document.hxx>
+#include <documentimport.hxx>
+
+#include <svl/sharedstringpool.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmluconv.hxx>
+
+#include <sax/tools/converter.hxx>
+
+#include <com/sun/star/util/NumberFormat.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::xmloff::token;
+
+using ::com::sun::star::uno::Reference;
+
+ScXMLExternalRefTabSourceContext::ScXMLExternalRefTabSourceContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLExternalTabData& rRefInfo ) :
+ ScXMLImportContext( rImport ),
+ mrExternalRefInfo(rRefInfo)
+{
+ using namespace ::xmloff::token;
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &it : *rAttrList)
+ {
+ sal_Int32 nAttrToken = it.getToken();
+ if ( nAttrToken == XML_ELEMENT( XLINK, XML_HREF ) )
+ maRelativeUrl = it.toString();
+ else if ( nAttrToken == XML_ELEMENT( TABLE, XML_TABLE_NAME ) )
+ // todo
+ ;
+ else if ( nAttrToken == XML_ELEMENT( TABLE, XML_FILTER_NAME ) )
+ maFilterName = it.toString();
+ else if ( nAttrToken == XML_ELEMENT( TABLE, XML_FILTER_OPTIONS ) )
+ maFilterOptions = it.toString();
+ }
+}
+
+ScXMLExternalRefTabSourceContext::~ScXMLExternalRefTabSourceContext()
+{
+}
+
+/**
+ * Make sure the URL is a valid relative URL, mainly to avoid storing
+ * absolute URL as relative URL by accident. For now, we only check the first
+ * three characters which are assumed to be always '../', because the relative
+ * URL for an external document is always in reference to the content.xml
+ * fragment of the original document.
+ */
+static bool lcl_isValidRelativeURL(const OUString& rUrl)
+{
+ sal_Int32 n = ::std::min( rUrl.getLength(), static_cast<sal_Int32>(3));
+ if (n < 3)
+ return false;
+ const sal_Unicode* p = rUrl.getStr();
+ for (sal_Int32 i = 0; i < n; ++i)
+ {
+ sal_Unicode c = p[i];
+ if (i < 2 && c != '.')
+ // the path must begin with '..'
+ return false;
+ else if (i == 2 && c != '/')
+ // a '/' path separator must follow
+ return false;
+ }
+ return true;
+}
+
+void SAL_CALL ScXMLExternalRefTabSourceContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (!pDoc)
+ return;
+
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ if (lcl_isValidRelativeURL(maRelativeUrl))
+ pRefMgr->setRelativeFileName(mrExternalRefInfo.mnFileId, maRelativeUrl);
+ pRefMgr->setFilterData(mrExternalRefInfo.mnFileId, maFilterName, maFilterOptions);
+}
+
+ScXMLExternalRefRowsContext::ScXMLExternalRefRowsContext(
+ ScXMLImport& rImport, ScXMLExternalTabData& rRefInfo ) :
+ ScXMLImportContext( rImport ),
+ mrExternalRefInfo(rRefInfo)
+{
+}
+
+ScXMLExternalRefRowsContext::~ScXMLExternalRefRowsContext()
+{
+}
+
+Reference< XFastContextHandler > SAL_CALL ScXMLExternalRefRowsContext::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& xAttrList )
+{
+ // #i101319# row elements inside group, rows or header-rows
+ // are treated like row elements directly in the table element
+
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(TABLE, XML_TABLE_ROW_GROUP):
+ case XML_ELEMENT(TABLE, XML_TABLE_HEADER_ROWS):
+ case XML_ELEMENT(TABLE, XML_TABLE_ROWS):
+ return new ScXMLExternalRefRowsContext(
+ GetScImport(), mrExternalRefInfo);
+ case XML_ELEMENT(TABLE, XML_TABLE_ROW):
+ return new ScXMLExternalRefRowContext(
+ GetScImport(), pAttribList, mrExternalRefInfo);
+ default:
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ }
+ return nullptr;
+}
+
+ScXMLExternalRefRowContext::ScXMLExternalRefRowContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLExternalTabData& rRefInfo ) :
+ ScXMLImportContext( rImport ),
+ mrScImport(rImport),
+ mrExternalRefInfo(rRefInfo),
+ mnRepeatRowCount(1)
+{
+ mrExternalRefInfo.mnCol = 0;
+
+ for (auto &it : *rAttrList)
+ {
+ switch (it.getToken())
+ {
+ case XML_ELEMENT(TABLE, XML_NUMBER_ROWS_REPEATED):
+ {
+ mnRepeatRowCount = std::max(it.toInt32(), static_cast<sal_Int32>(1));
+ }
+ break;
+ }
+ }
+}
+
+ScXMLExternalRefRowContext::~ScXMLExternalRefRowContext()
+{
+}
+
+Reference< XFastContextHandler > SAL_CALL ScXMLExternalRefRowContext::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& xAttrList )
+{
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (nElement == XML_ELEMENT(TABLE, XML_TABLE_CELL) || nElement == XML_ELEMENT(TABLE, XML_COVERED_TABLE_CELL))
+ return new ScXMLExternalRefCellContext(mrScImport, pAttribList, mrExternalRefInfo);
+
+ return nullptr;
+}
+
+void SAL_CALL ScXMLExternalRefRowContext::endFastElement( sal_Int32 /* nElement */ )
+{
+ ScExternalRefCache::TableTypeRef pTab = mrExternalRefInfo.mpCacheTable;
+
+ for (sal_Int32 i = 1; i < mnRepeatRowCount; ++i)
+ {
+ // Performance: duplicates of a non-existent row will still not exist.
+ // Don't find that out for every cell.
+ // External references often are a sparse matrix.
+ if (i == 1 && !pTab->hasRow( mrExternalRefInfo.mnRow))
+ {
+ mrExternalRefInfo.mnRow += mnRepeatRowCount;
+ return;
+ }
+
+ for (sal_Int32 j = 0; j < mrExternalRefInfo.mnCol; ++j)
+ {
+ ScExternalRefCache::TokenRef pToken = pTab->getCell(
+ static_cast<SCCOL>(j), static_cast<SCROW>(mrExternalRefInfo.mnRow));
+
+ if (pToken)
+ {
+ pTab->setCell(static_cast<SCCOL>(j),
+ static_cast<SCROW>(mrExternalRefInfo.mnRow+i), pToken);
+ }
+ }
+ }
+ mrExternalRefInfo.mnRow += mnRepeatRowCount;
+}
+
+ScXMLExternalRefCellContext::ScXMLExternalRefCellContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLExternalTabData& rRefInfo ) :
+ ScXMLImportContext( rImport ),
+ mrScImport(rImport),
+ mrExternalRefInfo(rRefInfo),
+ mfCellValue(0.0),
+ mnRepeatCount(1),
+ mnNumberFormat(-1),
+ mnCellType(css::util::NumberFormat::UNDEFINED),
+ mbIsNumeric(false),
+ mbIsEmpty(true)
+{
+ using namespace ::xmloff::token;
+
+ for (auto &it : *rAttrList)
+ {
+ switch ( it.getToken() )
+ {
+ case XML_ELEMENT(TABLE, XML_STYLE_NAME):
+ {
+ XMLTableStylesContext* pStyles = static_cast<XMLTableStylesContext*>(mrScImport.GetAutoStyles());
+ const XMLTableStyleContext* pStyle = static_cast<const XMLTableStyleContext*>(
+ pStyles->FindStyleChildContext(XmlStyleFamily::TABLE_CELL, it.toString(), true));
+ if (pStyle)
+ mnNumberFormat = const_cast<XMLTableStyleContext*>(pStyle)->GetNumberFormat();
+ }
+ break;
+ case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED):
+ {
+ mnRepeatCount = ::std::max( it.toInt32(), static_cast<sal_Int32>(1) );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
+ {
+ mnCellType = ScXMLImport::GetCellType( it.toCString(), it.getLength() );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_VALUE):
+ {
+ if ( !it.isEmpty() )
+ {
+ mfCellValue = it.toDouble();
+ mbIsNumeric = true;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_DATE_VALUE):
+ {
+ if ( !it.isEmpty() && mrScImport.SetNullDateOnUnitConverter() )
+ {
+ mrScImport.GetMM100UnitConverter().convertDateTime( mfCellValue, it.toView() );
+ mbIsNumeric = true;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_TIME_VALUE):
+ {
+ if ( !it.isEmpty() )
+ {
+ ::sax::Converter::convertDuration( mfCellValue, it.toView() );
+ mbIsNumeric = true;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_STRING_VALUE):
+ {
+ if ( !it.isEmpty() )
+ {
+ maCellString = it.toString();
+ mbIsNumeric = false;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE):
+ {
+ if ( !it.isEmpty() )
+ {
+ mfCellValue = IsXMLToken( it, XML_TRUE ) ? 1.0 : 0.0;
+ mbIsNumeric = true;
+ mbIsEmpty = false;
+ }
+ }
+ break;
+ default:
+ ;
+ }
+ }
+}
+
+ScXMLExternalRefCellContext::~ScXMLExternalRefCellContext()
+{
+}
+
+Reference< XFastContextHandler > SAL_CALL ScXMLExternalRefCellContext::createFastChildContext(
+ sal_Int32 nElement, const Reference< XFastAttributeList >& /*xAttrList*/ )
+{
+ if (nElement == XML_ELEMENT(TEXT, XML_P))
+ return new ScXMLExternalRefCellTextContext(mrScImport, *this);
+
+ return nullptr;
+}
+
+void SAL_CALL ScXMLExternalRefCellContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ if (!maCellString.isEmpty())
+ mbIsEmpty = false;
+
+ for (sal_Int32 i = 0; i < mnRepeatCount; ++i, ++mrExternalRefInfo.mnCol)
+ {
+ if (mbIsEmpty)
+ continue;
+
+ ScExternalRefCache::TokenRef aToken;
+ if (mbIsNumeric)
+ aToken.reset(new formula::FormulaDoubleToken(mfCellValue));
+ else
+ {
+ ScDocument& rDoc = mrScImport.GetDoc().getDoc();
+ svl::SharedString aSS = rDoc.GetSharedStringPool().intern(maCellString);
+ aToken.reset(new formula::FormulaStringToken(aSS));
+ }
+
+ sal_uInt32 nNumFmt = mnNumberFormat >= 0 ? static_cast<sal_uInt32>(mnNumberFormat) : 0;
+ mrExternalRefInfo.mpCacheTable->setCell(
+ static_cast<SCCOL>(mrExternalRefInfo.mnCol),
+ static_cast<SCROW>(mrExternalRefInfo.mnRow),
+ aToken, nNumFmt);
+ }
+}
+
+void ScXMLExternalRefCellContext::SetCellString(const OUString& rStr)
+{
+ maCellString = rStr;
+}
+
+ScXMLExternalRefCellTextContext::ScXMLExternalRefCellTextContext(
+ ScXMLImport& rImport,
+ ScXMLExternalRefCellContext& rParent ) :
+ ScXMLImportContext( rImport ),
+ mrParent(rParent)
+{
+}
+
+ScXMLExternalRefCellTextContext::~ScXMLExternalRefCellTextContext()
+{
+}
+
+void SAL_CALL ScXMLExternalRefCellTextContext::characters( const OUString& rChars )
+{
+ maCellStrBuf.append( rChars );
+}
+
+void SAL_CALL ScXMLExternalRefCellTextContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrParent.SetCellString( maCellStrBuf.makeStringAndClear() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlexternaltabi.hxx b/sc/source/filter/xml/xmlexternaltabi.hxx
new file mode 100644
index 000000000..94fa05574
--- /dev/null
+++ b/sc/source/filter/xml/xmlexternaltabi.hxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustrbuf.hxx>
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLImport;
+struct ScXMLExternalTabData;
+
+class ScXMLExternalRefTabSourceContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefTabSourceContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLExternalTabData& rRefInfo );
+
+ virtual ~ScXMLExternalRefTabSourceContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+private:
+ ScXMLExternalTabData& mrExternalRefInfo;
+
+ OUString maRelativeUrl;
+ OUString maFilterName;
+ OUString maFilterOptions;
+};
+
+class ScXMLExternalRefRowsContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefRowsContext( ScXMLImport& rImport,
+ ScXMLExternalTabData& rRefInfo );
+
+ virtual ~ScXMLExternalRefRowsContext() override;
+
+ virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+private:
+ ScXMLExternalTabData& mrExternalRefInfo;
+};
+
+class ScXMLExternalRefRowContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefRowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLExternalTabData& rRefInfo );
+
+ virtual ~ScXMLExternalRefRowContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+private:
+ ScXMLImport& mrScImport;
+ ScXMLExternalTabData& mrExternalRefInfo;
+ sal_Int32 mnRepeatRowCount;
+};
+
+class ScXMLExternalRefCellContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefCellContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLExternalTabData& rRefInfo );
+
+ virtual ~ScXMLExternalRefCellContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ void SetCellString(const OUString& rStr);
+
+private:
+ ScXMLImport& mrScImport;
+ ScXMLExternalTabData& mrExternalRefInfo;
+ OUString maCellString;
+ double mfCellValue;
+ sal_Int32 mnRepeatCount;
+ sal_Int32 mnNumberFormat;
+ sal_Int16 mnCellType; // TODO: import to document core
+ bool mbIsNumeric;
+ bool mbIsEmpty;
+};
+
+class ScXMLExternalRefCellTextContext : public ScXMLImportContext
+{
+public:
+ ScXMLExternalRefCellTextContext( ScXMLImport& rImport,
+ ScXMLExternalRefCellContext& rParent );
+
+ virtual ~ScXMLExternalRefCellTextContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+ virtual void SAL_CALL characters( const OUString& rChars ) override;
+
+private:
+ ScXMLExternalRefCellContext& mrParent;
+
+ OUStringBuffer maCellStrBuf;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlfilti.cxx b/sc/source/filter/xml/xmlfilti.cxx
new file mode 100644
index 000000000..7585ce0c5
--- /dev/null
+++ b/sc/source/filter/xml/xmlfilti.cxx
@@ -0,0 +1,784 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlfilti.hxx"
+#include "xmlimprt.hxx"
+#include "xmldrani.hxx"
+#include "xmldpimp.hxx"
+#include <rangeutl.hxx>
+#include <queryentry.hxx>
+#include <document.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <sax/tools/converter.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+using ::com::sun::star::uno::Reference;
+
+ScXMLFilterContext::ConnStackItem::ConnStackItem(bool bOr) : mbOr(bOr), mnCondCount(0) {}
+
+ScXMLFilterContext::ScXMLFilterContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScQueryParam& rParam,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ mrQueryParam(rParam),
+ pDatabaseRangeContext(pTempDatabaseRangeContext),
+ bSkipDuplicates(false),
+ bCopyOutputData(false),
+ bConditionSourceRange(false)
+{
+ ScDocument* pDoc(GetScImport().GetDocument());
+ assert(pDoc);
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ ScRange aScRange;
+ sal_Int32 nOffset(0);
+ if (ScRangeStringConverter::GetRangeFromString( aScRange, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ))
+ {
+ aOutputPosition = aScRange.aStart;
+ bCopyOutputData = true;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION_SOURCE_RANGE_ADDRESS ):
+ {
+ sal_Int32 nOffset(0);
+ if (ScRangeStringConverter::GetRangeFromString( aConditionSourceRangeAddress, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ) )
+ bConditionSourceRange = true;
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION_SOURCE ):
+ {
+ // not supported by StarOffice
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_DUPLICATES ):
+ {
+ bSkipDuplicates = !IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLFilterContext::~ScXMLFilterContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLFilterContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_AND ):
+ {
+ pContext = new ScXMLAndContext(
+ GetScImport(), mrQueryParam, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_OR ):
+ {
+ pContext = new ScXMLOrContext(
+ GetScImport(), mrQueryParam, this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLConditionContext(
+ GetScImport(), nElement, pAttribList, mrQueryParam, this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLFilterContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ mrQueryParam.bInplace = !bCopyOutputData;
+ mrQueryParam.bDuplicate = !bSkipDuplicates;
+
+ if (bCopyOutputData)
+ {
+ mrQueryParam.nDestCol = aOutputPosition.Col();
+ mrQueryParam.nDestRow = aOutputPosition.Row();
+ mrQueryParam.nDestTab = aOutputPosition.Tab();
+ }
+
+ if (bConditionSourceRange)
+ pDatabaseRangeContext->SetFilterConditionSourceRangeAddress(aConditionSourceRangeAddress);
+}
+
+void ScXMLFilterContext::OpenConnection(bool b)
+{
+ maConnStack.emplace_back(b);
+}
+
+void ScXMLFilterContext::CloseConnection()
+{
+ maConnStack.pop_back();
+}
+
+bool ScXMLFilterContext::GetConnection()
+{
+ // For condition items in each stack, the first one gets the connection of
+ // the last stack, while the rest of them get that of the current stack.
+
+ if (maConnStack.empty())
+ // This should never happen.
+ return true;
+
+ ConnStackItem& rItem = maConnStack.back();
+ if (rItem.mnCondCount)
+ // secondary item gets the current connection.
+ return rItem.mbOr;
+
+ // The next condition of this stack will get the current connection.
+ ++rItem.mnCondCount;
+
+ if (maConnStack.size() < 2)
+ // There is no last stack. Likely the first condition in the first
+ // stack whose connection is not used. Default in
+ // ScQueryEntry::eConnect is SC_AND, so return false (AND instead of
+ // OR) here. Otherwise, when saving the document again, we'd write a
+ // uselessly stacked
+ // <table:filter-or><table:filter-and>...</table:filter-and></table:filter-or>
+ // for two conditions connected with AND.
+ return false;
+
+ std::vector<ConnStackItem>::reverse_iterator itr = maConnStack.rbegin();
+ ++itr;
+ return itr->mbOr; // connection of the last stack.
+}
+
+ScXMLAndContext::ScXMLAndContext( ScXMLImport& rImport,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ mrQueryParam(rParam),
+ pFilterContext(pTempFilterContext)
+{
+ pFilterContext->OpenConnection(false);
+}
+
+ScXMLAndContext::~ScXMLAndContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLAndContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_OR ):
+ {
+ // not supported in StarOffice
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLConditionContext(
+ GetScImport(), nElement, pAttribList, mrQueryParam, pFilterContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLAndContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pFilterContext->CloseConnection();
+}
+
+ScXMLOrContext::ScXMLOrContext( ScXMLImport& rImport,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ mrQueryParam(rParam),
+ pFilterContext(pTempFilterContext)
+{
+ pFilterContext->OpenConnection(true);
+}
+
+ScXMLOrContext::~ScXMLOrContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLOrContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_AND ):
+ {
+ pContext = new ScXMLAndContext(
+ GetScImport(), mrQueryParam, pFilterContext);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLConditionContext(
+ GetScImport(), nElement, pAttribList, mrQueryParam, pFilterContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLOrContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pFilterContext->CloseConnection();
+}
+
+ScXMLConditionContext::ScXMLConditionContext(
+ ScXMLImport& rImport, sal_Int32 /*nElement*/,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ mrQueryParam(rParam),
+ pFilterContext(pTempFilterContext),
+ sDataType(GetXMLToken(XML_TEXT)),
+ nField(0),
+ bIsCaseSensitive(false)
+{
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FIELD_NUMBER ):
+ {
+ nField = aIter.toInt32();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ {
+ bIsCaseSensitive = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ case XML_ELEMENT( LO_EXT, XML_DATA_TYPE ):
+ {
+ sDataType = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_VALUE ):
+ {
+ sConditionValue = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_OPERATOR ):
+ {
+ sOperator = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLConditionContext::~ScXMLConditionContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLConditionContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_SET_ITEM ):
+ {
+ pContext = new ScXMLSetItemContext(
+ GetScImport(), nElement, pAttribList, *this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void ScXMLConditionContext::GetOperator(
+ std::u16string_view aOpStr, ScQueryParam& rParam, ScQueryEntry& rEntry)
+{
+ rParam.eSearchType = utl::SearchParam::SearchType::Normal;
+ if (IsXMLToken(aOpStr, XML_MATCH))
+ {
+ rParam.eSearchType = utl::SearchParam::SearchType::Regexp;
+ rEntry.eOp = SC_EQUAL;
+ }
+ else if (IsXMLToken(aOpStr, XML_NOMATCH))
+ {
+ rParam.eSearchType = utl::SearchParam::SearchType::Regexp;
+ rEntry.eOp = SC_NOT_EQUAL;
+ }
+ else if (aOpStr == u"=")
+ rEntry.eOp = SC_EQUAL;
+ else if (aOpStr == u"!=")
+ rEntry.eOp = SC_NOT_EQUAL;
+ else if (IsXMLToken(aOpStr, XML_BOTTOM_PERCENT))
+ rEntry.eOp = SC_BOTPERC;
+ else if (IsXMLToken(aOpStr, XML_BOTTOM_VALUES))
+ rEntry.eOp = SC_BOTVAL;
+ else if (IsXMLToken(aOpStr, XML_EMPTY))
+ rEntry.SetQueryByEmpty();
+ else if (aOpStr == u">")
+ rEntry.eOp = SC_GREATER;
+ else if (aOpStr == u">=")
+ rEntry.eOp = SC_GREATER_EQUAL;
+ else if (aOpStr == u"<")
+ rEntry.eOp = SC_LESS;
+ else if (aOpStr == u"<=")
+ rEntry.eOp = SC_LESS_EQUAL;
+ else if (IsXMLToken(aOpStr, XML_NOEMPTY))
+ rEntry.SetQueryByNonEmpty();
+ else if (IsXMLToken(aOpStr, XML_TOP_PERCENT))
+ rEntry.eOp = SC_TOPPERC;
+ else if (IsXMLToken(aOpStr, XML_TOP_VALUES))
+ rEntry.eOp = SC_TOPVAL;
+ else if (IsXMLToken(aOpStr, XML_CONTAINS))
+ rEntry.eOp = SC_CONTAINS;
+ else if (IsXMLToken(aOpStr, XML_DOES_NOT_CONTAIN))
+ rEntry.eOp = SC_DOES_NOT_CONTAIN;
+ else if (IsXMLToken(aOpStr, XML_BEGINS_WITH))
+ rEntry.eOp = SC_BEGINS_WITH;
+ else if (IsXMLToken(aOpStr, XML_DOES_NOT_BEGIN_WITH))
+ rEntry.eOp = SC_DOES_NOT_BEGIN_WITH;
+ else if (IsXMLToken(aOpStr, XML_ENDS_WITH))
+ rEntry.eOp = SC_ENDS_WITH;
+ else if (IsXMLToken(aOpStr, XML_DOES_NOT_END_WITH))
+ rEntry.eOp = SC_DOES_NOT_END_WITH;
+}
+
+void ScXMLConditionContext::AddSetItem(const ScQueryEntry::Item& rItem)
+{
+ maQueryItems.push_back(rItem);
+}
+
+void SAL_CALL ScXMLConditionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScQueryEntry& rEntry = mrQueryParam.AppendEntry();
+
+ // We currently don't support per-condition case sensitivity.
+ mrQueryParam.bCaseSens = bIsCaseSensitive;
+
+ rEntry.bDoQuery = true;
+ rEntry.eConnect = pFilterContext->GetConnection() ? SC_OR : SC_AND;
+
+ GetOperator(sOperator, mrQueryParam, rEntry);
+ SCCOLROW nStartPos = mrQueryParam.bByRow ? mrQueryParam.nCol1 : mrQueryParam.nRow1;
+ rEntry.nField = o3tl::saturating_add(nField, nStartPos);
+
+ if (maQueryItems.empty())
+ {
+ ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
+ if (IsXMLToken(sOperator, XML_EMPTY))
+ return;
+ if (IsXMLToken(sDataType, XML_NUMBER))
+ {
+ rItem.mfVal = sConditionValue.toDouble();
+ rItem.meType = ScQueryEntry::ByValue;
+ }
+ else if (IsXMLToken(sDataType, XML_TEXT_COLOR)
+ || IsXMLToken(sDataType, XML_BACKGROUND_COLOR))
+ {
+ rItem.meType = IsXMLToken(sDataType, XML_TEXT_COLOR) ? ScQueryEntry::ByTextColor
+ : ScQueryEntry::ByBackgroundColor;
+ if (IsXMLToken(sConditionValue, XML_TRANSPARENT)
+ || IsXMLToken(sConditionValue, XML_WINDOW_FONT_COLOR))
+ rItem.maColor = COL_AUTO;
+ else
+ sax::Converter::convertColor(rItem.maColor, sConditionValue);
+ }
+ else
+ {
+ svl::SharedStringPool& rPool = GetScImport().GetDocument()->GetSharedStringPool();
+ rItem.maString = rPool.intern(sConditionValue);
+ rItem.meType = ScQueryEntry::ByString;
+ }
+ }
+ else
+ rEntry.GetQueryItems().swap(maQueryItems);
+}
+
+ScXMLSetItemContext::ScXMLSetItemContext(
+ ScXMLImport& rImport, sal_Int32 /*nElement*/,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList, ScXMLConditionContext& rParent) :
+ ScXMLImportContext(rImport)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_VALUE ):
+ {
+ svl::SharedStringPool& rPool = GetScImport().GetDocument()->GetSharedStringPool();
+ ScQueryEntry::Item aItem;
+ aItem.maString = rPool.intern(aIter.toString());
+ aItem.meType = ScQueryEntry::ByString;
+ aItem.mfVal = 0.0;
+ rParent.AddSetItem(aItem);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLSetItemContext::~ScXMLSetItemContext()
+{
+}
+
+ScXMLDPFilterContext::ScXMLDPFilterContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTempDataPilotTableContext) :
+ ScXMLImportContext( rImport ),
+ pDataPilotTable(pTempDataPilotTableContext),
+ eSearchType(utl::SearchParam::SearchType::Normal),
+ nFilterFieldCount(0),
+ bSkipDuplicates(false),
+ bIsCaseSensitive(false),
+ bConnectionOr(true),
+ bNextConnectionOr(true)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ // not supported
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION_SOURCE_RANGE_ADDRESS ):
+ {
+ // not supported
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CONDITION_SOURCE ):
+ {
+ // not supported by StarOffice
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DISPLAY_DUPLICATES ):
+ {
+ bSkipDuplicates = !IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ }
+ }
+}
+
+ScXMLDPFilterContext::~ScXMLDPFilterContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDPFilterContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_AND ):
+ {
+ pContext = new ScXMLDPAndContext( GetScImport(), this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_OR ):
+ {
+ pContext = new ScXMLDPOrContext( GetScImport(), this);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLDPConditionContext( GetScImport(), nElement, pAttribList, this);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDPFilterContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ aFilterFields.eSearchType = eSearchType;
+ aFilterFields.bCaseSens = bIsCaseSensitive;
+ aFilterFields.bDuplicate = !bSkipDuplicates;
+
+ pDataPilotTable->SetSourceQueryParam(aFilterFields);
+}
+
+void ScXMLDPFilterContext::AddFilterField (const ScQueryEntry& aFilterField)
+{
+ aFilterFields.Resize(nFilterFieldCount + 1);
+ ScQueryEntry& rEntry(aFilterFields.GetEntry(nFilterFieldCount));
+ rEntry = aFilterField;
+ rEntry.bDoQuery = true;
+ ++nFilterFieldCount;
+}
+
+ScXMLDPAndContext::ScXMLDPAndContext( ScXMLImport& rImport,
+ ScXMLDPFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport )
+{
+ pFilterContext = pTempFilterContext;
+ pFilterContext->OpenConnection(false);
+}
+
+ScXMLDPAndContext::~ScXMLDPAndContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDPAndContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_OR ):
+ {
+ // not supported in StarOffice
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLDPConditionContext( GetScImport(), nElement, pAttribList, pFilterContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDPAndContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pFilterContext->CloseConnection();
+}
+
+ScXMLDPOrContext::ScXMLDPOrContext( ScXMLImport& rImport,
+ ScXMLDPFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ pFilterContext(pTempFilterContext)
+{
+ pFilterContext->OpenConnection(true);
+}
+
+ScXMLDPOrContext::~ScXMLDPOrContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLDPOrContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_FILTER_AND ):
+ {
+ pContext = new ScXMLDPAndContext( GetScImport(), pFilterContext);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_FILTER_CONDITION ):
+ {
+ pContext = new ScXMLDPConditionContext( GetScImport(), nElement, pAttribList, pFilterContext);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLDPOrContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pFilterContext->CloseConnection();
+}
+
+ScXMLDPConditionContext::ScXMLDPConditionContext( ScXMLImport& rImport,
+ sal_Int32 /*nElement*/,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDPFilterContext* pTempFilterContext) :
+ ScXMLImportContext( rImport ),
+ pFilterContext(pTempFilterContext),
+ sDataType(GetXMLToken(XML_TEXT)),
+ nField(0),
+ bIsCaseSensitive(false)
+{
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FIELD_NUMBER ):
+ {
+ nField = aIter.toInt32();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ {
+ bIsCaseSensitive = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ {
+ sDataType = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_VALUE ):
+ {
+ sConditionValue = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_OPERATOR ):
+ {
+ sOperator = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLDPConditionContext::~ScXMLDPConditionContext()
+{
+}
+
+void ScXMLDPConditionContext::getOperatorXML(
+ std::u16string_view sTempOperator, ScQueryOp& aFilterOperator, utl::SearchParam::SearchType& rSearchType)
+{
+ rSearchType = utl::SearchParam::SearchType::Normal;
+ if (IsXMLToken(sTempOperator, XML_MATCH))
+ {
+ rSearchType = utl::SearchParam::SearchType::Regexp;
+ aFilterOperator = SC_EQUAL;
+ }
+ else if (IsXMLToken(sTempOperator, XML_NOMATCH))
+ {
+ rSearchType = utl::SearchParam::SearchType::Regexp;
+ aFilterOperator = SC_NOT_EQUAL;
+ }
+ else if (sTempOperator == u"=")
+ aFilterOperator = SC_EQUAL;
+ else if (sTempOperator == u"!=")
+ aFilterOperator = SC_NOT_EQUAL;
+ else if (IsXMLToken(sTempOperator, XML_BOTTOM_PERCENT))
+ aFilterOperator = SC_BOTPERC;
+ else if (IsXMLToken(sTempOperator, XML_BOTTOM_VALUES))
+ aFilterOperator = SC_BOTVAL;
+ else if (sTempOperator == u">")
+ aFilterOperator = SC_GREATER;
+ else if (sTempOperator == u">=")
+ aFilterOperator = SC_GREATER_EQUAL;
+ else if (sTempOperator == u"<")
+ aFilterOperator = SC_LESS;
+ else if (sTempOperator == u"<=")
+ aFilterOperator = SC_LESS_EQUAL;
+ else if (IsXMLToken(sTempOperator, XML_TOP_PERCENT))
+ aFilterOperator = SC_TOPPERC;
+ else if (IsXMLToken(sTempOperator, XML_TOP_VALUES))
+ aFilterOperator = SC_TOPVAL;
+}
+
+void SAL_CALL ScXMLDPConditionContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ ScQueryEntry aFilterField;
+ aFilterField.nField = nField;
+ if (pFilterContext->GetConnection())
+ aFilterField.eConnect = SC_OR;
+ else
+ aFilterField.eConnect = SC_AND;
+ pFilterContext->SetIsCaseSensitive(bIsCaseSensitive);
+ if (IsXMLToken(sOperator, XML_EMPTY))
+ aFilterField.SetQueryByEmpty();
+ else if (IsXMLToken(sOperator, XML_NOEMPTY))
+ aFilterField.SetQueryByNonEmpty();
+ else
+ {
+ utl::SearchParam::SearchType eSearchType = utl::SearchParam::SearchType::Normal;
+ getOperatorXML(sOperator, aFilterField.eOp, eSearchType);
+ pFilterContext->SetSearchType(eSearchType);
+ ScQueryEntry::Item& rItem = aFilterField.GetQueryItem();
+ svl::SharedStringPool& rPool = GetScImport().GetDocument()->GetSharedStringPool();
+
+ if (IsXMLToken(sDataType, XML_NUMBER))
+ {
+ rItem.mfVal = sConditionValue.toDouble();
+ rItem.maString = rPool.intern(sConditionValue);
+ rItem.meType = ScQueryEntry::ByValue;
+ }
+ else
+ {
+ rItem.maString = rPool.intern(sConditionValue);
+ rItem.meType = ScQueryEntry::ByString;
+ rItem.mfVal = 0.0;
+ }
+ }
+ pFilterContext->AddFilterField(aFilterField);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlfilti.hxx b/sc/source/filter/xml/xmlfilti.hxx
new file mode 100644
index 000000000..02a2a9e6c
--- /dev/null
+++ b/sc/source/filter/xml/xmlfilti.hxx
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+#include <queryentry.hxx>
+#include <queryparam.hxx>
+
+#include <stack>
+#include <vector>
+
+class ScXMLImport;
+class ScXMLDatabaseRangeContext;
+class ScXMLDataPilotTableContext;
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLFilterContext : public ScXMLImportContext
+{
+ struct ConnStackItem
+ {
+ bool mbOr;
+ int mnCondCount;
+ explicit ConnStackItem(bool bOr);
+ };
+ ScQueryParam& mrQueryParam;
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+
+ ScAddress aOutputPosition;
+ ScRange aConditionSourceRangeAddress;
+ bool bSkipDuplicates;
+ bool bCopyOutputData;
+ bool bConditionSourceRange;
+ std::vector<ConnStackItem> maConnStack;
+
+public:
+
+ ScXMLFilterContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScQueryParam& rParam,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLFilterContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void OpenConnection(bool b);
+ void CloseConnection();
+ bool GetConnection();
+};
+
+class ScXMLAndContext : public ScXMLImportContext
+{
+ ScQueryParam& mrQueryParam;
+ ScXMLFilterContext* pFilterContext;
+
+public:
+
+ ScXMLAndContext( ScXMLImport& rImport,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLAndContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLOrContext : public ScXMLImportContext
+{
+ ScQueryParam& mrQueryParam;
+ ScXMLFilterContext* pFilterContext;
+
+public:
+
+ ScXMLOrContext( ScXMLImport& rImport,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLOrContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLConditionContext : public ScXMLImportContext
+{
+ ScQueryParam& mrQueryParam;
+ ScXMLFilterContext* pFilterContext;
+
+ ScQueryEntry::QueryItemsType maQueryItems;
+ OUString sDataType;
+ OUString sConditionValue;
+ OUString sOperator;
+ sal_Int32 nField;
+ bool bIsCaseSensitive;
+
+public:
+
+ ScXMLConditionContext( ScXMLImport& rImport, sal_Int32 nElement,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScQueryParam& rParam,
+ ScXMLFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLConditionContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ static void GetOperator(std::u16string_view aOpStr, ScQueryParam& rParam, ScQueryEntry& rEntry);
+ void AddSetItem(const ScQueryEntry::Item& rItem);
+};
+
+class ScXMLSetItemContext : public ScXMLImportContext
+{
+public:
+ ScXMLSetItemContext(ScXMLImport& rImport, sal_Int32 nElement,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLConditionContext& rParent);
+
+ virtual ~ScXMLSetItemContext() override;
+};
+
+// Datapilot (Core)
+
+class ScXMLDPFilterContext : public ScXMLImportContext
+{
+ ScXMLDataPilotTableContext* pDataPilotTable;
+
+ ScQueryParam aFilterFields;
+ utl::SearchParam::SearchType eSearchType;
+ sal_uInt8 nFilterFieldCount;
+ bool bSkipDuplicates:1;
+ bool bIsCaseSensitive:1;
+ bool bConnectionOr:1;
+ bool bNextConnectionOr:1;
+ ::std::stack<bool> aConnectionOrStack;
+
+public:
+
+ ScXMLDPFilterContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDataPilotTableContext* pTempDataPilotTableContext);
+
+ virtual ~ScXMLDPFilterContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void SetIsCaseSensitive(const bool bTemp) { bIsCaseSensitive = bTemp; }
+ void SetSearchType(const utl::SearchParam::SearchType eTemp)
+ {
+ if (eSearchType == utl::SearchParam::SearchType::Normal)
+ eSearchType = eTemp;
+ }
+
+ void OpenConnection(const bool bVal)
+ {
+ bool bTemp = bConnectionOr;
+ bConnectionOr = bNextConnectionOr;
+ bNextConnectionOr = bVal;
+ aConnectionOrStack.push(bTemp);
+ }
+
+ void CloseConnection()
+ {
+ bool bTemp;
+ if (aConnectionOrStack.empty())
+ bTemp = false;
+ else
+ {
+ bTemp = aConnectionOrStack.top();
+ aConnectionOrStack.pop();
+ }
+ bConnectionOr = bTemp;
+ bNextConnectionOr = bTemp;
+ }
+
+ bool GetConnection() { bool bTemp = bConnectionOr; bConnectionOr = bNextConnectionOr; return bTemp; }
+ void AddFilterField (const ScQueryEntry& aFilterField);
+};
+
+class ScXMLDPAndContext : public ScXMLImportContext
+{
+ ScXMLDPFilterContext* pFilterContext;
+public:
+
+ ScXMLDPAndContext( ScXMLImport& rImport,
+ ScXMLDPFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLDPAndContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDPOrContext : public ScXMLImportContext
+{
+ ScXMLDPFilterContext* pFilterContext;
+public:
+
+ ScXMLDPOrContext( ScXMLImport& rImport,
+ ScXMLDPFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLDPOrContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+class ScXMLDPConditionContext : public ScXMLImportContext
+{
+ ScXMLDPFilterContext* pFilterContext;
+
+ OUString sDataType;
+ OUString sConditionValue;
+ OUString sOperator;
+ sal_Int32 nField;
+ bool bIsCaseSensitive;
+
+public:
+
+ ScXMLDPConditionContext( ScXMLImport& rImport, sal_Int32 nElement,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDPFilterContext* pTempFilterContext);
+
+ virtual ~ScXMLDPConditionContext() override;
+
+ static void getOperatorXML(
+ std::u16string_view sTempOperator, ScQueryOp& aFilterOperator, utl::SearchParam::SearchType& rSearchType);
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlfonte.cxx b/sc/source/filter/xml/xmlfonte.cxx
new file mode 100644
index 000000000..ab8e42f4c
--- /dev/null
+++ b/sc/source/filter/xml/xmlfonte.cxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <editeng/eeitem.hxx>
+
+#include <xmloff/XMLFontAutoStylePool.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/editeng.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include "xmlexprt.hxx"
+#include <stlpool.hxx>
+#include <attrib.hxx>
+
+namespace {
+
+class ScXMLFontAutoStylePool_Impl: public XMLFontAutoStylePool
+{
+private:
+ // #i120077# remember owned pool
+ rtl::Reference<SfxItemPool> mpEditEnginePool;
+
+ void AddFontItems(const sal_uInt16* pWhichIds, sal_uInt8 nIdCount, const SfxItemPool* pItemPool, const bool bExportDefaults);
+
+public:
+ ScXMLFontAutoStylePool_Impl( ScXMLExport& rExport, bool bEmbedFonts);
+};
+
+}
+
+void ScXMLFontAutoStylePool_Impl::AddFontItems(const sal_uInt16* pWhichIds, sal_uInt8 nIdCount, const SfxItemPool* pItemPool, const bool bExportDefaults)
+{
+ for( sal_uInt16 i=0; i < nIdCount; ++i )
+ {
+ sal_uInt16 nWhichId(pWhichIds[i]);
+ if (bExportDefaults)
+ {
+ const SfxPoolItem* pItem = &pItemPool->GetDefaultItem(nWhichId);
+ const SvxFontItem *pFont(static_cast<const SvxFontItem *>(pItem));
+ Add( pFont->GetFamilyName(), pFont->GetStyleName(),
+ pFont->GetFamily(), pFont->GetPitch(),
+ pFont->GetCharSet() );
+ }
+ for (const SfxPoolItem* pItem : pItemPool->GetItemSurrogates( nWhichId ))
+ {
+ const SvxFontItem *pFont(static_cast<const SvxFontItem *>(pItem));
+ Add( pFont->GetFamilyName(), pFont->GetStyleName(),
+ pFont->GetFamily(), pFont->GetPitch(),
+ pFont->GetCharSet() );
+ }
+ }
+}
+
+ScXMLFontAutoStylePool_Impl::ScXMLFontAutoStylePool_Impl(ScXMLExport& rExportP, bool bEmbedFonts)
+ : XMLFontAutoStylePool(rExportP, bEmbedFonts)
+{
+ sal_uInt16 const aWhichIds[] { ATTR_FONT, ATTR_CJK_FONT,
+ ATTR_CTL_FONT };
+ sal_uInt16 const aEditWhichIds[] { EE_CHAR_FONTINFO, EE_CHAR_FONTINFO_CJK,
+ EE_CHAR_FONTINFO_CTL };
+ sal_uInt16 const aPageWhichIds[] { ATTR_PAGE_HEADERLEFT, ATTR_PAGE_FOOTERLEFT,
+ ATTR_PAGE_HEADERRIGHT, ATTR_PAGE_FOOTERRIGHT,
+ ATTR_PAGE_HEADERFIRST, ATTR_PAGE_FOOTERFIRST };
+
+ const SfxItemPool* pItemPool(rExportP.GetDocument()->GetPool());
+ AddFontItems(aWhichIds, 3, pItemPool, true);
+ const SfxItemPool* pEditPool(rExportP.GetDocument()->GetEditPool());
+ AddFontItems(aEditWhichIds, 3, pEditPool, false);
+
+ std::unique_ptr<SfxStyleSheetIterator> pItr = rExportP.GetDocument()->GetStyleSheetPool()->CreateIterator(SfxStyleFamily::Page);
+
+ m_bEmbedUsedOnly = rExportP.GetDocument()->IsEmbedUsedFontsOnly();
+ m_bEmbedLatinScript = rExportP.GetDocument()->IsEmbedFontScriptLatin();
+ m_bEmbedAsianScript = rExportP.GetDocument()->IsEmbedFontScriptAsian();
+ m_bEmbedComplexScript = rExportP.GetDocument()->IsEmbedFontScriptComplex();
+
+ if(!pItr)
+ return;
+
+ SfxStyleSheetBase* pStyle(pItr->First());
+
+ if(!pStyle)
+ return;
+
+ // #i120077# remember the SfxItemPool in member variable before usage. The
+ // local EditEngine will not take over ownership of the pool.
+ mpEditEnginePool = EditEngine::CreatePool();
+ EditEngine aEditEngine(mpEditEnginePool.get());
+
+ while (pStyle)
+ {
+ const SfxItemPool& rPagePool(pStyle->GetPool()->GetPool());
+
+ for (sal_uInt16 nPageWhichId : aPageWhichIds)
+ {
+ for (const SfxPoolItem* pItem : rPagePool.GetItemSurrogates( nPageWhichId ))
+ {
+ const ScPageHFItem* pPageItem = static_cast<const ScPageHFItem*>(pItem);
+ const EditTextObject* pLeftArea(pPageItem->GetLeftArea());
+ if (pLeftArea)
+ {
+ aEditEngine.SetText(*pLeftArea);
+ AddFontItems(aEditWhichIds, 3, mpEditEnginePool.get(), false);
+ }
+ const EditTextObject* pCenterArea(pPageItem->GetCenterArea());
+ if (pCenterArea)
+ {
+ aEditEngine.SetText(*pCenterArea);
+ AddFontItems(aEditWhichIds, 3, mpEditEnginePool.get(), false);
+ }
+ const EditTextObject* pRightArea(pPageItem->GetRightArea());
+ if (pRightArea)
+ {
+ aEditEngine.SetText(*pRightArea);
+ AddFontItems(aEditWhichIds, 3, mpEditEnginePool.get(), false);
+ }
+ }
+ }
+
+ pStyle = pItr->Next();
+ }
+}
+
+XMLFontAutoStylePool* ScXMLExport::CreateFontAutoStylePool()
+{
+ bool blockFontEmbedding = false;
+ // We write font info to both content.xml and styles.xml, but they are both
+ // written by different ScXMLExport instance, and would therefore write each
+ // font file twice without complicated checking for duplicates, so handle
+ // the embedding only in one of them.
+ if(!( getExportFlags() & SvXMLExportFlags::CONTENT ))
+ blockFontEmbedding = true;
+ if (!GetDocument()->IsEmbedFonts())
+ blockFontEmbedding = true;
+ return new ScXMLFontAutoStylePool_Impl( *this, !blockFontEmbedding );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlimprt.cxx b/sc/source/filter/xml/xmlimprt.cxx
new file mode 100644
index 000000000..b72cf35ff
--- /dev/null
+++ b/sc/source/filter/xml/xmlimprt.cxx
@@ -0,0 +1,1766 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <svl/numformat.hxx>
+
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/xmlmetai.hxx>
+#include <sfx2/objsh.hxx>
+#include <unotools/streamwrap.hxx>
+#include <xmloff/xmlscripti.hxx>
+#include <xmloff/XMLFontStylesContext.hxx>
+#include <xmloff/DocumentSettingsContext.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/numehelp.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlerror.hxx>
+#include <xmloff/ProgressBarHelper.hxx>
+#include <svx/svdpage.hxx>
+
+#include <svl/languageoptions.hxx>
+#include <editeng/editstat.hxx>
+#include <formula/errorcodes.hxx>
+#include <vcl/svapp.hxx>
+
+#include <appluno.hxx>
+#include "xmlimprt.hxx"
+#include "importcontext.hxx"
+#include <document.hxx>
+#include <docsh.hxx>
+#include <docuno.hxx>
+#include "xmlbodyi.hxx"
+#include "xmlstyli.hxx"
+#include <ViewSettingsSequenceDefines.hxx>
+#include <userdat.hxx>
+
+#include <compiler.hxx>
+
+#include "XMLConverter.hxx"
+#include "XMLDetectiveContext.hxx"
+#include "XMLTableShapeImportHelper.hxx"
+#include "XMLChangeTrackingImportHelper.hxx"
+#include <chgviset.hxx>
+#include "XMLStylesImportHelper.hxx"
+#include <sheetdata.hxx>
+#include <rangeutl.hxx>
+#include <formulaparserpool.hxx>
+#include <externalrefmgr.hxx>
+#include <editutil.hxx>
+#include "editattributemap.hxx"
+#include <documentimport.hxx>
+#include "pivotsource.hxx"
+#include <unonames.hxx>
+#include <numformat.hxx>
+#include <sizedev.hxx>
+#include <scdll.hxx>
+#include "xmlstyle.hxx"
+
+#include <comphelper/base64.hxx>
+#include <comphelper/extract.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/util/MalformedNumberFormatException.hpp>
+#include <com/sun/star/util/NumberFormat.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/sheet/NamedRangeFlag.hpp>
+#include <com/sun/star/sheet/XLabelRanges.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sheet/XSheetCellRangeContainer.hpp>
+#include <cellsuno.hxx>
+
+#include <memory>
+#include <utility>
+
+constexpr OUStringLiteral SC_LOCALE = u"Locale";
+constexpr OUStringLiteral SC_CURRENCYSYMBOL = u"CurrencySymbol";
+constexpr OUStringLiteral SC_REPEAT_ROW = u"repeat-row";
+constexpr OUStringLiteral SC_FILTER = u"filter";
+constexpr OUStringLiteral SC_PRINT_RANGE = u"print-range";
+
+using namespace com::sun::star;
+using namespace ::xmloff::token;
+using namespace ::formula;
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisImporter",
+ SvXMLImportFlags::ALL,
+ { "com.sun.star.comp.Calc.XMLOasisImporter" } ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisMetaImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisMetaImporter",
+ SvXMLImportFlags::META,
+ { "com.sun.star.comp.Calc.XMLOasisMetaImporter" } ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisStylesImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisStylesImporter",
+ SvXMLImportFlags::STYLES|SvXMLImportFlags::AUTOSTYLES|SvXMLImportFlags::MASTERSTYLES|SvXMLImportFlags::FONTDECLS,
+ { "com.sun.star.comp.Calc.XMLOasisStylesImporter" } ));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisContentImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisContentImporter",
+ SvXMLImportFlags::AUTOSTYLES|SvXMLImportFlags::CONTENT|SvXMLImportFlags::SCRIPTS|SvXMLImportFlags::FONTDECLS,
+ uno::Sequence< OUString > { "com.sun.star.comp.Calc.XMLOasisContentImporter" }));
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Calc_XMLOasisSettingsImporter_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(
+ new ScXMLImport(
+ context,
+ "com.sun.star.comp.Calc.XMLOasisSettingsImporter",
+ SvXMLImportFlags::SETTINGS,
+ { "com.sun.star.comp.Calc.XMLOasisSettingsImporter" } ));
+}
+
+namespace {
+
+// NB: virtually inherit so we can multiply inherit properly
+// in ScXMLFlatDocContext_Impl
+class ScXMLDocContext_Impl : public virtual SvXMLImportContext
+{
+protected:
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+
+public:
+ ScXMLDocContext_Impl( ScXMLImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+};
+
+}
+
+ScXMLDocContext_Impl::ScXMLDocContext_Impl( ScXMLImport& rImport ) :
+SvXMLImportContext( rImport )
+{
+}
+
+namespace {
+
+// context for flat file xml format
+class ScXMLFlatDocContext_Impl
+ : public ScXMLDocContext_Impl, public SvXMLMetaDocumentContext
+{
+public:
+
+ ScXMLFlatDocContext_Impl( ScXMLImport& i_rImport,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps);
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+};
+
+}
+
+ScXMLFlatDocContext_Impl::ScXMLFlatDocContext_Impl( ScXMLImport& i_rImport,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps) :
+SvXMLImportContext(i_rImport),
+ScXMLDocContext_Impl(i_rImport),
+SvXMLMetaDocumentContext(i_rImport, i_xDocProps)
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLFlatDocContext_Impl::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ if ( nElement == XML_ELEMENT( OFFICE, XML_META ) )
+ return SvXMLMetaDocumentContext::createFastChildContext( nElement, xAttrList );
+ else
+ return ScXMLDocContext_Impl::createFastChildContext( nElement, xAttrList );
+}
+
+namespace {
+
+class ScXMLBodyContext_Impl : public ScXMLImportContext
+{
+public:
+ ScXMLBodyContext_Impl( ScXMLImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+};
+
+}
+
+ScXMLBodyContext_Impl::ScXMLBodyContext_Impl( ScXMLImport& rImport ) :
+ScXMLImportContext( rImport )
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLBodyContext_Impl::createFastChildContext( sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+ return GetScImport().CreateBodyContext( pAttribList );
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLDocContext_Impl::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext(nullptr);
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( OFFICE, XML_BODY ):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::CONTENT)
+ pContext = new ScXMLBodyContext_Impl( GetScImport() );
+ break;
+ case XML_ELEMENT( OFFICE, XML_SCRIPTS ):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::SCRIPTS)
+ pContext = GetScImport().CreateScriptContext();
+ break;
+ case XML_ELEMENT( OFFICE, XML_SETTINGS ):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::SETTINGS)
+ pContext = new XMLDocumentSettingsContext(GetScImport());
+ break;
+ case XML_ELEMENT(OFFICE, XML_STYLES):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::STYLES)
+ pContext = GetScImport().CreateStylesContext( false);
+ break;
+ case XML_ELEMENT(OFFICE, XML_AUTOMATIC_STYLES):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::AUTOSTYLES)
+ pContext = GetScImport().CreateStylesContext( true);
+ break;
+ case XML_ELEMENT(OFFICE, XML_FONT_FACE_DECLS):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::FONTDECLS)
+ pContext = GetScImport().CreateFontDeclsContext();
+ break;
+ case XML_ELEMENT(OFFICE, XML_MASTER_STYLES):
+ if (GetScImport().getImportFlags() & SvXMLImportFlags::MASTERSTYLES)
+ pContext = new ScXMLMasterStylesContext( GetImport() );
+ break;
+ case XML_ELEMENT(OFFICE, XML_META):
+ SAL_INFO("sc", "XML_ELEMENT(OFFICE, XML_META): should not have come here, maybe document is invalid?");
+ break;
+ }
+
+ return pContext;
+}
+
+
+void ScXMLImport::SetPostProcessData( sc::ImportPostProcessData* p )
+{
+ mpPostProcessData = p;
+}
+
+sc::PivotTableSources& ScXMLImport::GetPivotTableSources()
+{
+ if (!mpPivotSources)
+ mpPivotSources.reset(new sc::PivotTableSources);
+
+ return *mpPivotSources;
+}
+
+SvXMLImportContext *ScXMLImport::CreateFastContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_STYLES ):
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_CONTENT ):
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_SETTINGS ):
+ pContext = new ScXMLDocContext_Impl( *this );
+ break;
+
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT_META ):
+ pContext = CreateMetaContext(nElement);
+ break;
+
+ case XML_ELEMENT( OFFICE, XML_DOCUMENT ):
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetModel(), uno::UNO_QUERY_THROW);
+ // flat OpenDocument file format
+ pContext = new ScXMLFlatDocContext_Impl( *this,
+ xDPS->getDocumentProperties());
+ break;
+ }
+
+ }
+
+ return pContext;
+}
+
+ScXMLImport::ScXMLImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLImportFlags nImportFlag,
+ const css::uno::Sequence< OUString > & sSupportedServiceNames)
+: SvXMLImport( rContext, implementationName, nImportFlag, sSupportedServiceNames ),
+ pDoc( nullptr ),
+ mpPostProcessData(nullptr),
+ aTables(*this),
+ nSolarMutexLocked(0),
+ nProgressCount(0),
+ nPrevCellType(0),
+ bLoadDoc( true ),
+ bNullDateSetted(false),
+ bSelfImportingXMLSet(false),
+ mbLockSolarMutex(true),
+ mbImportStyles(true),
+ mbHasNewCondFormatData(false)
+{
+ pStylesImportHelper.reset(new ScMyStylesImportHelper(*this));
+
+ xScPropHdlFactory = new XMLScPropHdlFactory;
+ xCellStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScCellStylesProperties, xScPropHdlFactory, false);
+ xColumnStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScColumnStylesProperties, xScPropHdlFactory, false);
+ xRowStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScRowStylesImportProperties, xScPropHdlFactory, false);
+ xTableStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScTableStylesImportProperties, xScPropHdlFactory, false);
+
+ // #i66550# needed for 'presentation:event-listener' element for URLs in shapes
+ GetNamespaceMap().Add(
+ GetXMLToken( XML_NP_PRESENTATION ),
+ GetXMLToken( XML_N_PRESENTATION ),
+ XML_NAMESPACE_PRESENTATION );
+}
+
+ScXMLImport::~ScXMLImport() noexcept
+{
+ pChangeTrackingImportHelper.reset();
+ pNumberFormatAttributesExportHelper.reset();
+ pStyleNumberFormats.reset();
+ pStylesImportHelper.reset();
+
+ m_aMyNamedExpressions.clear();
+ maMyLabelRanges.clear();
+ maValidations.clear();
+ pDetectiveOpArray.reset();
+
+ //call SvXMLImport dtor contents before deleting pSolarMutexGuard
+ cleanup();
+
+ pSolarMutexGuard.reset();
+}
+
+void ScXMLImport::initialize( const css::uno::Sequence<css::uno::Any>& aArguments )
+{
+ SvXMLImport::initialize(aArguments);
+
+ uno::Reference<beans::XPropertySet> xInfoSet = getImportInfo();
+ if (!xInfoSet.is())
+ return;
+
+ uno::Reference<beans::XPropertySetInfo> xInfoSetInfo = xInfoSet->getPropertySetInfo();
+ if (!xInfoSetInfo.is())
+ return;
+
+ if (xInfoSetInfo->hasPropertyByName(SC_UNO_ODS_LOCK_SOLAR_MUTEX))
+ xInfoSet->getPropertyValue(SC_UNO_ODS_LOCK_SOLAR_MUTEX) >>= mbLockSolarMutex;
+
+ if (xInfoSetInfo->hasPropertyByName(SC_UNO_ODS_IMPORT_STYLES))
+ xInfoSet->getPropertyValue(SC_UNO_ODS_IMPORT_STYLES) >>= mbImportStyles;
+}
+
+SvXMLImportContext *ScXMLImport::CreateFontDeclsContext()
+{
+ XMLFontStylesContext *pFSContext = new XMLFontStylesContext(
+ *this, osl_getThreadTextEncoding());
+ SetFontDecls(pFSContext);
+ SvXMLImportContext* pContext = pFSContext;
+ return pContext;
+}
+
+SvXMLImportContext *ScXMLImport::CreateStylesContext( bool bIsAutoStyle )
+{
+ SvXMLImportContext* pContext = new XMLTableStylesContext(
+ *this, bIsAutoStyle);
+
+ if (bIsAutoStyle)
+ SetAutoStyles(static_cast<SvXMLStylesContext*>(pContext));
+ else
+ SetStyles(static_cast<SvXMLStylesContext*>(pContext));
+
+ return pContext;
+}
+
+SvXMLImportContext *ScXMLImport::CreateBodyContext(const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+{
+ return new ScXMLBodyContext(*this, rAttrList);
+}
+
+SvXMLImportContext *ScXMLImport::CreateMetaContext(
+ const sal_Int32 /*nElement*/ )
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ if (getImportFlags() & SvXMLImportFlags::META)
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> const xDocProps(
+ (IsStylesOnlyMode()) ? nullptr : xDPS->getDocumentProperties());
+ pContext = new SvXMLMetaDocumentContext(*this, xDocProps);
+ }
+
+ return pContext;
+}
+
+SvXMLImportContext *ScXMLImport::CreateScriptContext()
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ if( !(IsStylesOnlyMode()) )
+ {
+ pContext = new XMLScriptContext( *this, GetModel() );
+ }
+
+ return pContext;
+}
+
+void ScXMLImport::SetStatistics(const uno::Sequence<beans::NamedValue> & i_rStats)
+{
+ static const char* s_stats[] =
+ { "TableCount", "CellCount", "ObjectCount", nullptr };
+
+ SvXMLImport::SetStatistics(i_rStats);
+
+ sal_uInt64 nCount(0);
+ for (const auto& rStat : i_rStats) {
+ for (const char** pStat = s_stats; *pStat != nullptr; ++pStat) {
+ if (rStat.Name.equalsAscii(*pStat)) {
+ sal_Int32 val = 0;
+ if (rStat.Value >>= val) {
+ nCount += val;
+ } else {
+ OSL_FAIL("ScXMLImport::SetStatistics: invalid entry");
+ }
+ }
+ }
+ }
+
+ if (nCount)
+ {
+ GetProgressBarHelper()->SetReference(nCount);
+ GetProgressBarHelper()->SetValue(0);
+ }
+}
+
+ScDocumentImport& ScXMLImport::GetDoc()
+{
+ return *mpDocImport;
+}
+
+sal_Int16 ScXMLImport::GetCellType(const char* rStrValue, const sal_Int32 nStrLength)
+{
+ sal_Int16 nCellType = util::NumberFormat::UNDEFINED;
+ if (rStrValue != nullptr)
+ {
+ switch (rStrValue[0])
+ {
+ case 'b':
+ if (nStrLength == 7 && !strcmp(rStrValue, "boolean"))
+ nCellType = util::NumberFormat::LOGICAL;
+ break;
+ case 'c':
+ if (nStrLength == 8 && !strcmp(rStrValue, "currency"))
+ nCellType = util::NumberFormat::CURRENCY;
+ break;
+ case 'd':
+ if (nStrLength == 4 && !strcmp(rStrValue, "date"))
+ nCellType = util::NumberFormat::DATETIME;
+ break;
+ case 'f':
+ if (nStrLength == 5 && !strcmp(rStrValue, "float"))
+ nCellType = util::NumberFormat::NUMBER;
+ break;
+ case 'p':
+ if (nStrLength == 10 && !strcmp(rStrValue, "percentage"))
+ nCellType = util::NumberFormat::PERCENT;
+ break;
+ case 's':
+ if (nStrLength == 6 && !strcmp(rStrValue, "string"))
+ nCellType = util::NumberFormat::TEXT;
+ break;
+ case 't':
+ if (nStrLength == 4 && !strcmp(rStrValue, "time"))
+ nCellType = util::NumberFormat::TIME;
+ break;
+ }
+ }
+
+ return nCellType;
+}
+
+XMLShapeImportHelper* ScXMLImport::CreateShapeImport()
+{
+ return new XMLTableShapeImportHelper(*this);
+}
+
+bool ScXMLImport::GetValidation(const OUString& sName, ScMyImportValidation& aValidation)
+{
+ auto aItr = std::find_if(maValidations.begin(), maValidations.end(),
+ [&sName](const ScMyImportValidation& rValidation) { return rValidation.sName == sName; });
+ if (aItr != maValidations.end())
+ {
+ // source position must be set as string,
+ // so sBaseCellAddress no longer has to be converted here
+ aValidation = *aItr;
+ return true;
+ }
+ return false;
+}
+
+void ScXMLImport::AddNamedExpression(SCTAB nTab, ScMyNamedExpression aNamedExp)
+{
+ SheetNamedExpMap::iterator itr = m_SheetNamedExpressions.find(nTab);
+ if (itr == m_SheetNamedExpressions.end())
+ {
+ // No chain exists for this sheet. Create one.
+ ::std::pair<SheetNamedExpMap::iterator, bool> r =
+ m_SheetNamedExpressions.insert(std::make_pair(nTab, ScMyNamedExpressions()));
+ if (!r.second)
+ // insertion failed.
+ return;
+
+ itr = r.first;
+ }
+ ScMyNamedExpressions& r = itr->second;
+ r.push_back(std::move(aNamedExp));
+}
+
+ScXMLChangeTrackingImportHelper* ScXMLImport::GetChangeTrackingImportHelper()
+{
+ if (!pChangeTrackingImportHelper)
+ pChangeTrackingImportHelper.reset(new ScXMLChangeTrackingImportHelper());
+ return pChangeTrackingImportHelper.get();
+}
+
+void ScXMLImport::InsertStyles()
+{
+ GetStyles()->CopyStylesToDoc(true);
+
+ // if content is going to be loaded with the same import, set bLatinDefaultStyle flag now
+ if ( getImportFlags() & SvXMLImportFlags::CONTENT )
+ ExamineDefaultStyle();
+}
+
+void ScXMLImport::ExamineDefaultStyle()
+{
+ if (pDoc)
+ {
+ // #i62435# after inserting the styles, check if the default style has a latin-script-only
+ // number format (then, value cells can be pre-initialized with western script type)
+
+ const ScPatternAttr* pDefPattern = pDoc->GetDefPattern();
+ if (pDefPattern && sc::NumFmtUtil::isLatinScript(*pDefPattern, *pDoc))
+ mpDocImport->setDefaultNumericScript(SvtScriptType::LATIN);
+ }
+}
+
+void ScXMLImport::SetChangeTrackingViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& rChangeProps)
+{
+ if (!pDoc)
+ return;
+
+ if (!rChangeProps.hasElements())
+ return;
+
+ ScXMLImport::MutexGuard aGuard(*this);
+ sal_Int16 nTemp16(0);
+ ScChangeViewSettings aViewSettings;
+ for (const auto& rChangeProp : rChangeProps)
+ {
+ OUString sName(rChangeProp.Name);
+ if (sName == "ShowChanges")
+ aViewSettings.SetShowChanges(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowAcceptedChanges")
+ aViewSettings.SetShowAccepted(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowRejectedChanges")
+ aViewSettings.SetShowRejected(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByDatetime")
+ aViewSettings.SetHasDate(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByDatetimeMode")
+ {
+ if (rChangeProp.Value >>= nTemp16)
+ aViewSettings.SetTheDateMode(static_cast<SvxRedlinDateMode>(nTemp16));
+ }
+ else if (sName == "ShowChangesByDatetimeFirstDatetime")
+ {
+ util::DateTime aDateTime;
+ if (rChangeProp.Value >>= aDateTime)
+ {
+ aViewSettings.SetTheFirstDateTime(::DateTime(aDateTime));
+ }
+ }
+ else if (sName == "ShowChangesByDatetimeSecondDatetime")
+ {
+ util::DateTime aDateTime;
+ if (rChangeProp.Value >>= aDateTime)
+ {
+ aViewSettings.SetTheLastDateTime(::DateTime(aDateTime));
+ }
+ }
+ else if (sName == "ShowChangesByAuthor")
+ aViewSettings.SetHasAuthor(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByAuthorName")
+ {
+ OUString sOUName;
+ if (rChangeProp.Value >>= sOUName)
+ {
+ aViewSettings.SetTheAuthorToShow(sOUName);
+ }
+ }
+ else if (sName == "ShowChangesByComment")
+ aViewSettings.SetHasComment(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByCommentText")
+ {
+ OUString sOUComment;
+ if (rChangeProp.Value >>= sOUComment)
+ {
+ aViewSettings.SetTheComment(sOUComment);
+ }
+ }
+ else if (sName == "ShowChangesByRanges")
+ aViewSettings.SetHasRange(::cppu::any2bool(rChangeProp.Value));
+ else if (sName == "ShowChangesByRangesList")
+ {
+ OUString sRanges;
+ if ((rChangeProp.Value >>= sRanges) && !sRanges.isEmpty())
+ {
+ ScRangeList aRangeList;
+ ScRangeStringConverter::GetRangeListFromString(
+ aRangeList, sRanges, *pDoc, FormulaGrammar::CONV_OOO);
+ aViewSettings.SetTheRangeList(aRangeList);
+ }
+ }
+ }
+ pDoc->SetChangeViewSettings(aViewSettings);
+}
+
+void ScXMLImport::SetViewSettings(const uno::Sequence<beans::PropertyValue>& aViewProps)
+{
+ sal_Int32 nHeight(0);
+ sal_Int32 nLeft(0);
+ sal_Int32 nTop(0);
+ sal_Int32 nWidth(0);
+ for (const auto& rViewProp : aViewProps)
+ {
+ OUString sName(rViewProp.Name);
+ if (sName == "VisibleAreaHeight")
+ rViewProp.Value >>= nHeight;
+ else if (sName == "VisibleAreaLeft")
+ rViewProp.Value >>= nLeft;
+ else if (sName == "VisibleAreaTop")
+ rViewProp.Value >>= nTop;
+ else if (sName == "VisibleAreaWidth")
+ rViewProp.Value >>= nWidth;
+ else if (sName == "TrackedChangesViewSettings")
+ {
+ uno::Sequence<beans::PropertyValue> aChangeProps;
+ if(rViewProp.Value >>= aChangeProps)
+ SetChangeTrackingViewSettings(aChangeProps);
+ }
+ }
+ if (!(nHeight && nWidth && GetModel().is()))
+ return;
+
+ ScModelObj* pDocObj(comphelper::getFromUnoTunnel<ScModelObj>( GetModel() ));
+ if (!pDocObj)
+ return;
+
+ SfxObjectShell* pEmbeddedObj = pDocObj->GetEmbeddedObject();
+ if (pEmbeddedObj)
+ {
+ tools::Rectangle aRect{ nLeft, nTop };
+ aRect.setWidth( nWidth );
+ aRect.setHeight( nHeight );
+ pEmbeddedObj->SetVisArea(aRect);
+ }
+}
+
+void ScXMLImport::SetConfigurationSettings(const uno::Sequence<beans::PropertyValue>& aConfigProps)
+{
+ if (!GetModel().is())
+ return;
+
+ uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
+ if (!xMultiServiceFactory.is())
+ return;
+
+ sal_Int32 nCount(aConfigProps.getLength());
+ css::uno::Sequence<css::beans::PropertyValue> aFilteredProps(nCount);
+ auto pFilteredProps = aFilteredProps.getArray();
+ sal_Int32 nFilteredPropsLen = 0;
+ for (sal_Int32 i = nCount - 1; i >= 0; --i)
+ {
+ if (aConfigProps[i].Name == "TrackedChangesProtectionKey")
+ {
+ OUString sKey;
+ if (aConfigProps[i].Value >>= sKey)
+ {
+ uno::Sequence<sal_Int8> aPass;
+ ::comphelper::Base64::decode(aPass, sKey);
+ if (aPass.hasElements())
+ {
+ if (pDoc->GetChangeTrack())
+ pDoc->GetChangeTrack()->SetProtection(aPass);
+ else
+ {
+ std::set<OUString> aUsers;
+ std::unique_ptr<ScChangeTrack> pTrack( new ScChangeTrack(*pDoc, std::move(aUsers)) );
+ pTrack->SetProtection(aPass);
+ pDoc->SetChangeTrack(std::move(pTrack));
+ }
+ }
+ }
+ }
+ // store the following items for later use (after document is loaded)
+ else if ((aConfigProps[i].Name == "VBACompatibilityMode") || (aConfigProps[i].Name == "ScriptConfiguration"))
+ {
+ uno::Reference< beans::XPropertySet > xImportInfo = getImportInfo();
+ if (xImportInfo.is())
+ {
+ uno::Reference< beans::XPropertySetInfo > xPropertySetInfo = xImportInfo->getPropertySetInfo();
+ if (xPropertySetInfo.is() && xPropertySetInfo->hasPropertyByName(aConfigProps[i].Name))
+ xImportInfo->setPropertyValue( aConfigProps[i].Name, aConfigProps[i].Value );
+ }
+ }
+ if (aConfigProps[i].Name != "LinkUpdateMode")
+ {
+ pFilteredProps[nFilteredPropsLen++] = aConfigProps[i];
+ }
+ }
+ aFilteredProps.realloc(nFilteredPropsLen);
+ uno::Reference <uno::XInterface> xInterface = xMultiServiceFactory->createInstance("com.sun.star.comp.SpreadsheetSettings");
+ uno::Reference <beans::XPropertySet> xProperties(xInterface, uno::UNO_QUERY);
+ if (xProperties.is())
+ SvXMLUnitConverter::convertPropertySet(xProperties, aFilteredProps);
+}
+
+sal_Int32 ScXMLImport::SetCurrencySymbol(const sal_Int32 nKey, std::u16string_view rCurrency)
+{
+ uno::Reference <util::XNumberFormatsSupplier> xNumberFormatsSupplier(GetNumberFormatsSupplier());
+ if (xNumberFormatsSupplier.is())
+ {
+ uno::Reference <util::XNumberFormats> xLocalNumberFormats(xNumberFormatsSupplier->getNumberFormats());
+ if (xLocalNumberFormats.is())
+ {
+ OUString sFormatString;
+ try
+ {
+ uno::Reference <beans::XPropertySet> xProperties(xLocalNumberFormats->getByKey(nKey));
+ if (xProperties.is())
+ {
+ lang::Locale aLocale;
+ if (GetDocument() && (xProperties->getPropertyValue(SC_LOCALE) >>= aLocale))
+ {
+ {
+ ScXMLImport::MutexGuard aGuard(*this);
+ LocaleDataWrapper aLocaleData( comphelper::getProcessComponentContext(), LanguageTag( aLocale) );
+ sFormatString = "#" +
+ aLocaleData.getNumThousandSep() +
+ "##0" +
+ aLocaleData.getNumDecimalSep() +
+ "00 [$" +
+ rCurrency +
+ "]";
+ }
+ sal_Int32 nNewKey = xLocalNumberFormats->queryKey(sFormatString, aLocale, true);
+ if (nNewKey == -1)
+ nNewKey = xLocalNumberFormats->addNew(sFormatString, aLocale);
+ return nNewKey;
+ }
+ }
+ }
+ catch ( const util::MalformedNumberFormatException& rException )
+ {
+ OUString sErrorMessage ="Error in Formatstring " +
+ sFormatString + " at position " +
+ OUString::number(rException.CheckPos);
+ uno::Sequence<OUString> aSeq { sErrorMessage };
+ uno::Reference<xml::sax::XLocator> xLocator;
+ SetError(XMLERROR_API | XMLERROR_FLAG_ERROR, aSeq, rException.Message, xLocator);
+ }
+ }
+ }
+ return nKey;
+}
+
+bool ScXMLImport::IsCurrencySymbol(const sal_Int32 nNumberFormat, const OUString& sCurrentCurrency, std::u16string_view sBankSymbol)
+{
+ uno::Reference <util::XNumberFormatsSupplier> xNumberFormatsSupplier(GetNumberFormatsSupplier());
+ if (xNumberFormatsSupplier.is())
+ {
+ uno::Reference <util::XNumberFormats> xLocalNumberFormats(xNumberFormatsSupplier->getNumberFormats());
+ if (xLocalNumberFormats.is())
+ {
+ try
+ {
+ uno::Reference <beans::XPropertySet> xNumberPropertySet(xLocalNumberFormats->getByKey(nNumberFormat));
+ if (xNumberPropertySet.is())
+ {
+ OUString sTemp;
+ if ( xNumberPropertySet->getPropertyValue(SC_CURRENCYSYMBOL) >>= sTemp)
+ {
+ if (sCurrentCurrency == sTemp)
+ return true;
+ // A release that saved an unknown currency may have
+ // saved the currency symbol of the number format
+ // instead of an ISO code bank symbol. In another
+ // release we may have a match for that. In this case
+ // sCurrentCurrency is the ISO code obtained through
+ // XMLNumberFormatAttributesExportHelper::GetCellType()
+ // and sBankSymbol is the currency symbol.
+ if (sCurrentCurrency.getLength() == 3 && sBankSymbol == sTemp)
+ return true;
+ // #i61657# This may be a legacy currency symbol that changed in the meantime.
+ if (SvNumberFormatter::GetLegacyOnlyCurrencyEntry( sCurrentCurrency, sBankSymbol) != nullptr)
+ return true;
+ // In the rare case that sCurrentCurrency is not the
+ // currency symbol, but a matching ISO code
+ // abbreviation instead that was obtained through
+ // XMLNumberFormatAttributesExportHelper::GetCellType(),
+ // check with the number format's symbol. This happens,
+ // for example, in the es_BO locale, where a legacy
+ // B$,BOB matched B$->BOP, which leads to
+ // sCurrentCurrency being BOP, and the previous call
+ // with BOP,BOB didn't find an entry, but B$,BOB will.
+ return SvNumberFormatter::GetLegacyOnlyCurrencyEntry( sTemp, sBankSymbol) != nullptr;
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL("Numberformat not found");
+ }
+ }
+ }
+ return false;
+}
+
+void ScXMLImport::SetType(const uno::Reference <beans::XPropertySet>& rProperties,
+ sal_Int32& rNumberFormat,
+ const sal_Int16 nCellType,
+ std::u16string_view rCurrency)
+{
+ if (!mbImportStyles)
+ return;
+
+ if ((nCellType == util::NumberFormat::TEXT) || (nCellType == util::NumberFormat::UNDEFINED))
+ return;
+
+ if (rNumberFormat == -1)
+ rProperties->getPropertyValue( SC_UNONAME_NUMFMT ) >>= rNumberFormat;
+ OSL_ENSURE(rNumberFormat != -1, "no NumberFormat");
+ bool bIsStandard;
+ // sCurrentCurrency may be the ISO code abbreviation if the currency
+ // symbol matches such, or if no match found the symbol itself!
+ OUString sCurrentCurrency;
+ sal_Int32 nCurrentCellType(
+ GetNumberFormatAttributesExportHelper()->GetCellType(
+ rNumberFormat, sCurrentCurrency, bIsStandard) & ~util::NumberFormat::DEFINED);
+ // If the (numeric) cell type (number, currency, date, time, boolean)
+ // is different from the format type then for some combinations we may
+ // have to apply a format, e.g. in case the generator deduced format
+ // from type and did not apply a format but we don't keep a dedicated
+ // type internally. Specifically this is necessary if the cell type is
+ // not number but the format type is (i.e. General). Currency cells
+ // need extra attention, see calls of ScXMLImport::IsCurrencySymbol()
+ // and description within there and ScXMLImport::SetCurrencySymbol().
+ if ((nCellType != nCurrentCellType) &&
+ (nCellType != util::NumberFormat::NUMBER) &&
+ (bIsStandard || (nCellType == util::NumberFormat::CURRENCY)))
+ {
+ if (!xNumberFormats.is())
+ {
+ uno::Reference <util::XNumberFormatsSupplier> xNumberFormatsSupplier(GetNumberFormatsSupplier());
+ if (xNumberFormatsSupplier.is())
+ xNumberFormats.set(xNumberFormatsSupplier->getNumberFormats());
+ }
+ if (xNumberFormats.is())
+ {
+ try
+ {
+ uno::Reference < beans::XPropertySet> xNumberFormatProperties(xNumberFormats->getByKey(rNumberFormat));
+ if (xNumberFormatProperties.is())
+ {
+ if (nCellType != util::NumberFormat::CURRENCY)
+ {
+ lang::Locale aLocale;
+ if ( xNumberFormatProperties->getPropertyValue(SC_LOCALE) >>= aLocale )
+ {
+ if (!xNumberFormatTypes.is())
+ xNumberFormatTypes.set(uno::Reference <util::XNumberFormatTypes>(xNumberFormats, uno::UNO_QUERY));
+ rProperties->setPropertyValue( SC_UNONAME_NUMFMT, uno::Any(xNumberFormatTypes->getStandardFormat(nCellType, aLocale)) );
+ }
+ }
+ else if (!rCurrency.empty() && !sCurrentCurrency.isEmpty())
+ {
+ if (sCurrentCurrency != rCurrency)
+ if (!IsCurrencySymbol(rNumberFormat, sCurrentCurrency, rCurrency))
+ rProperties->setPropertyValue( SC_UNONAME_NUMFMT, uno::Any(SetCurrencySymbol(rNumberFormat, rCurrency)));
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL("Numberformat not found");
+ }
+ }
+ }
+ else
+ {
+ if ((nCellType == util::NumberFormat::CURRENCY) && !rCurrency.empty() && !sCurrentCurrency.isEmpty() &&
+ sCurrentCurrency != rCurrency && !IsCurrencySymbol(rNumberFormat, sCurrentCurrency, rCurrency))
+ rProperties->setPropertyValue( SC_UNONAME_NUMFMT, uno::Any(SetCurrencySymbol(rNumberFormat, rCurrency)));
+ }
+}
+
+void ScXMLImport::SetStyleToRanges()
+{
+ if (!mbImportStyles)
+ return;
+
+ if (!sPrevStyleName.isEmpty())
+ {
+ uno::Reference <beans::XPropertySet> xProperties (xSheetCellRanges, uno::UNO_QUERY);
+ if (xProperties.is())
+ {
+ XMLTableStylesContext *pStyles(static_cast<XMLTableStylesContext *>(GetAutoStyles()));
+ XMLTableStyleContext* pStyle = nullptr;
+ if ( pStyles )
+ pStyle = const_cast<XMLTableStyleContext*>(static_cast<const XMLTableStyleContext *>(pStyles->FindStyleChildContext(
+ XmlStyleFamily::TABLE_CELL, sPrevStyleName, true)));
+ if (pStyle)
+ {
+ pStyle->FillPropertySet(xProperties);
+ // here needs to be the cond format import method
+ sal_Int32 nNumberFormat(pStyle->GetNumberFormat());
+ SetType(xProperties, nNumberFormat, nPrevCellType, sPrevCurrency);
+
+ css::uno::Any aAny = xProperties->getPropertyValue("FormatID");
+ sal_uInt64 nKey = 0;
+ if ((aAny >>= nKey) && nKey)
+ {
+ ScFormatSaveData* pFormatSaveData = comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->GetFormatSaveData();
+ pFormatSaveData->maIDToName.insert(std::pair<sal_uInt64, OUString>(nKey, sPrevStyleName));
+ }
+
+ // store first cell of first range for each style, once per sheet
+ uno::Sequence<table::CellRangeAddress> aAddresses(xSheetCellRanges->getRangeAddresses());
+ pStyle->ApplyCondFormat(aAddresses);
+ if ( aAddresses.hasElements() )
+ {
+ const table::CellRangeAddress& rRange = aAddresses[0];
+ if ( rRange.Sheet != pStyle->GetLastSheet() )
+ {
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->GetSheetSaveData();
+ pSheetData->AddCellStyle( sPrevStyleName,
+ ScAddress( static_cast<SCCOL>(rRange.StartColumn), static_cast<SCROW>(rRange.StartRow), static_cast<SCTAB>(rRange.Sheet) ) );
+ pStyle->SetLastSheet(rRange.Sheet);
+ }
+ }
+ }
+ else
+ {
+ xProperties->setPropertyValue(SC_UNONAME_CELLSTYL, uno::Any(GetStyleDisplayName( XmlStyleFamily::TABLE_CELL, sPrevStyleName )));
+ sal_Int32 nNumberFormat(GetStyleNumberFormats()->GetStyleNumberFormat(sPrevStyleName));
+ bool bInsert(nNumberFormat == -1);
+ SetType(xProperties, nNumberFormat, nPrevCellType, sPrevCurrency);
+ if (bInsert)
+ GetStyleNumberFormats()->AddStyleNumberFormat(sPrevStyleName, nNumberFormat);
+ }
+ }
+ }
+ if (GetModel().is())
+ {
+ uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
+ if (xMultiServiceFactory.is())
+ xSheetCellRanges.set(uno::Reference <sheet::XSheetCellRangeContainer>(
+ xMultiServiceFactory->createInstance("com.sun.star.sheet.SheetCellRanges"),
+ uno::UNO_QUERY));
+ }
+ OSL_ENSURE(xSheetCellRanges.is(), "didn't get SheetCellRanges");
+}
+
+void ScXMLImport::SetStyleToRanges(const ScRangeList& rRanges, const OUString* pStyleName,
+ const sal_Int16 nCellType, const OUString* pCurrency)
+{
+ if (!mbImportStyles)
+ return;
+
+ if (sPrevStyleName.isEmpty())
+ {
+ nPrevCellType = nCellType;
+ if (pStyleName)
+ sPrevStyleName = *pStyleName;
+ if (pCurrency)
+ sPrevCurrency = *pCurrency;
+ else if (!sPrevCurrency.isEmpty())
+ sPrevCurrency.clear();
+ }
+ else if ((nCellType != nPrevCellType) ||
+ ((pStyleName && *pStyleName != sPrevStyleName) ||
+ (!pStyleName && !sPrevStyleName.isEmpty())) ||
+ ((pCurrency && *pCurrency != sPrevCurrency) ||
+ (!pCurrency && !sPrevCurrency.isEmpty())))
+ {
+ SetStyleToRanges();
+ nPrevCellType = nCellType;
+ if (pStyleName)
+ sPrevStyleName = *pStyleName;
+ else if(!sPrevStyleName.isEmpty())
+ sPrevStyleName.clear();
+ if (pCurrency)
+ sPrevCurrency = *pCurrency;
+ else if(!sPrevCurrency.isEmpty())
+ sPrevCurrency.clear();
+ }
+
+ if (!xSheetCellRanges.is() && GetModel().is())
+ {
+ uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
+ if (xMultiServiceFactory.is())
+ xSheetCellRanges.set(uno::Reference <sheet::XSheetCellRangeContainer>(xMultiServiceFactory->createInstance("com.sun.star.sheet.SheetCellRanges"), uno::UNO_QUERY));
+ OSL_ENSURE(xSheetCellRanges.is(), "didn't get SheetCellRanges");
+
+ }
+ static_cast<ScCellRangesObj*>(xSheetCellRanges.get())->SetNewRanges(rRanges);
+}
+
+bool ScXMLImport::SetNullDateOnUnitConverter()
+{
+ if (!bNullDateSetted)
+ bNullDateSetted = GetMM100UnitConverter().setNullDate(GetModel());
+ OSL_ENSURE(bNullDateSetted, "could not set the null date");
+ return bNullDateSetted;
+}
+
+XMLNumberFormatAttributesExportHelper* ScXMLImport::GetNumberFormatAttributesExportHelper()
+{
+ if (!pNumberFormatAttributesExportHelper)
+ pNumberFormatAttributesExportHelper.reset(new XMLNumberFormatAttributesExportHelper(GetNumberFormatsSupplier()));
+ return pNumberFormatAttributesExportHelper.get();
+}
+
+ScMyStyleNumberFormats* ScXMLImport::GetStyleNumberFormats()
+{
+ if (!pStyleNumberFormats)
+ pStyleNumberFormats.reset(new ScMyStyleNumberFormats);
+ return pStyleNumberFormats.get();
+}
+
+void ScXMLImport::SetStylesToRangesFinished()
+{
+ SetStyleToRanges();
+ sPrevStyleName.clear();
+}
+
+// XImporter
+void SAL_CALL ScXMLImport::setTargetDocument( const css::uno::Reference< css::lang::XComponent >& xDoc )
+{
+ ScXMLImport::MutexGuard aGuard(*this);
+ SvXMLImport::setTargetDocument( xDoc );
+
+ uno::Reference<frame::XModel> xModel(xDoc, uno::UNO_QUERY);
+ pDoc = ScXMLConverter::GetScDocument( xModel );
+ OSL_ENSURE( pDoc, "ScXMLImport::setTargetDocument - no ScDocument!" );
+ if (!pDoc)
+ throw lang::IllegalArgumentException();
+
+ if (ScDocShell* pDocSh = static_cast<ScDocShell*>(pDoc->GetDocumentShell()))
+ pDocSh->SetInitialLinkUpdate( pDocSh->GetMedium());
+
+ mpDocImport.reset(new ScDocumentImport(*pDoc));
+ mpComp.reset(new ScCompiler(*pDoc, ScAddress(), formula::FormulaGrammar::GRAM_ODFF));
+
+ uno::Reference<document::XActionLockable> xActionLockable(xDoc, uno::UNO_QUERY);
+ if (xActionLockable.is())
+ xActionLockable->addActionLock();
+}
+
+// css::xml::sax::XDocumentHandler
+void SAL_CALL ScXMLImport::startDocument()
+{
+ ScXMLImport::MutexGuard aGuard(*this);
+ SvXMLImport::startDocument();
+ if (pDoc && !pDoc->IsImportingXML())
+ {
+ comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->BeforeXMLLoading();
+ bSelfImportingXMLSet = true;
+ }
+
+ // if content and styles are loaded with separate imports,
+ // set bLatinDefaultStyle flag at the start of the content import
+ SvXMLImportFlags nFlags = getImportFlags();
+ if ( ( nFlags & SvXMLImportFlags::CONTENT ) && !( nFlags & SvXMLImportFlags::STYLES ) )
+ ExamineDefaultStyle();
+
+ if (getImportFlags() & SvXMLImportFlags::CONTENT)
+ {
+ if (GetModel().is())
+ {
+ // store initial namespaces, to find the ones that were added from the file later
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->GetSheetSaveData();
+ const SvXMLNamespaceMap& rNamespaces = GetNamespaceMap();
+ pSheetData->StoreInitialNamespaces(rNamespaces);
+ }
+ }
+
+ uno::Reference< beans::XPropertySet > const xImportInfo( getImportInfo() );
+ uno::Reference< beans::XPropertySetInfo > const xPropertySetInfo(
+ xImportInfo.is() ? xImportInfo->getPropertySetInfo() : nullptr);
+ if (xPropertySetInfo.is())
+ {
+ OUString const sOrganizerMode(
+ "OrganizerMode");
+ if (xPropertySetInfo->hasPropertyByName(sOrganizerMode))
+ {
+ bool bStyleOnly(false);
+ if (xImportInfo->getPropertyValue(sOrganizerMode) >>= bStyleOnly)
+ {
+ bLoadDoc = !bStyleOnly;
+ }
+ }
+ }
+
+ UnlockSolarMutex();
+}
+
+sal_Int32 ScXMLImport::GetRangeType(const OUString& sRangeType)
+{
+ sal_Int32 nRangeType(0);
+ OUStringBuffer sBuffer;
+ sal_Int32 i = 0;
+ while (i <= sRangeType.getLength())
+ {
+ if ((i == sRangeType.getLength()) || (sRangeType[i] == ' '))
+ {
+ OUString sTemp = sBuffer.makeStringAndClear();
+ if (sTemp == "repeat-column")
+ nRangeType |= sheet::NamedRangeFlag::COLUMN_HEADER;
+ else if (sTemp == SC_REPEAT_ROW)
+ nRangeType |= sheet::NamedRangeFlag::ROW_HEADER;
+ else if (sTemp == SC_FILTER)
+ nRangeType |= sheet::NamedRangeFlag::FILTER_CRITERIA;
+ else if (sTemp == SC_PRINT_RANGE)
+ nRangeType |= sheet::NamedRangeFlag::PRINT_AREA;
+ }
+ else if (i < sRangeType.getLength())
+ sBuffer.append(sRangeType[i]);
+ ++i;
+ }
+ return nRangeType;
+}
+
+void ScXMLImport::SetLabelRanges()
+{
+ if (maMyLabelRanges.empty())
+ return;
+
+ uno::Reference <beans::XPropertySet> xPropertySet (GetModel(), uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ uno::Any aColAny = xPropertySet->getPropertyValue(SC_UNO_COLLABELRNG);
+ uno::Any aRowAny = xPropertySet->getPropertyValue(SC_UNO_ROWLABELRNG);
+
+ uno::Reference< sheet::XLabelRanges > xColRanges;
+ uno::Reference< sheet::XLabelRanges > xRowRanges;
+
+ if ( !(( aColAny >>= xColRanges ) && ( aRowAny >>= xRowRanges )) )
+ return;
+
+ table::CellRangeAddress aLabelRange;
+ table::CellRangeAddress aDataRange;
+
+ for (const auto& rLabelRange : maMyLabelRanges)
+ {
+ sal_Int32 nOffset1(0);
+ sal_Int32 nOffset2(0);
+ FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO;
+
+ assert(pDoc);
+ if (ScRangeStringConverter::GetRangeFromString( aLabelRange, rLabelRange.sLabelRangeStr, *pDoc, eConv, nOffset1 ) &&
+ ScRangeStringConverter::GetRangeFromString( aDataRange, rLabelRange.sDataRangeStr, *pDoc, eConv, nOffset2 ))
+ {
+ if ( rLabelRange.bColumnOrientation )
+ xColRanges->addNew( aLabelRange, aDataRange );
+ else
+ xRowRanges->addNew( aLabelRange, aDataRange );
+ }
+ }
+
+ maMyLabelRanges.clear();
+}
+
+namespace {
+
+class RangeNameInserter
+{
+ ScDocument& mrDoc;
+ ScRangeName& mrRangeName;
+
+public:
+ RangeNameInserter(ScDocument& rDoc, ScRangeName& rRangeName) :
+ mrDoc(rDoc), mrRangeName(rRangeName) {}
+
+ void operator() (const ScMyNamedExpression& p) const
+ {
+ using namespace formula;
+
+ const OUString& aType = p.sRangeType;
+ sal_uInt32 nUnoType = ScXMLImport::GetRangeType(aType);
+
+ ScRangeData::Type nNewType = ScRangeData::Type::Name;
+ if ( nUnoType & sheet::NamedRangeFlag::FILTER_CRITERIA ) nNewType |= ScRangeData::Type::Criteria;
+ if ( nUnoType & sheet::NamedRangeFlag::PRINT_AREA ) nNewType |= ScRangeData::Type::PrintArea;
+ if ( nUnoType & sheet::NamedRangeFlag::COLUMN_HEADER ) nNewType |= ScRangeData::Type::ColHeader;
+ if ( nUnoType & sheet::NamedRangeFlag::ROW_HEADER ) nNewType |= ScRangeData::Type::RowHeader;
+
+ // Insert a new name.
+ ScAddress aPos;
+ sal_Int32 nOffset = 0;
+ bool bSuccess = ScRangeStringConverter::GetAddressFromString(
+ aPos, p.sBaseCellAddress, mrDoc, FormulaGrammar::CONV_OOO, nOffset);
+
+ if (bSuccess)
+ {
+ OUString aContent = p.sContent;
+ if (!p.bIsExpression)
+ ScXMLConverter::ConvertCellRangeAddress(aContent);
+
+ ScRangeData* pData = new ScRangeData(
+ mrDoc, p.sName, aContent, aPos, nNewType, p.eGrammar);
+ mrRangeName.insert(pData);
+ }
+ }
+};
+
+}
+
+void ScXMLImport::SetNamedRanges()
+{
+ if (m_aMyNamedExpressions.empty())
+ return;
+
+ if (!pDoc)
+ return;
+
+ // Insert the namedRanges
+ ScRangeName* pRangeNames = pDoc->GetRangeName();
+ ::std::for_each(m_aMyNamedExpressions.begin(), m_aMyNamedExpressions.end(), RangeNameInserter(*pDoc, *pRangeNames));
+}
+
+void ScXMLImport::SetSheetNamedRanges()
+{
+ if (!pDoc)
+ return;
+
+ for (auto const& itr : m_SheetNamedExpressions)
+ {
+ const SCTAB nTab = itr.first;
+ ScRangeName* pRangeNames = pDoc->GetRangeName(nTab);
+ if (!pRangeNames)
+ continue;
+
+ const ScMyNamedExpressions& rNames = itr.second;
+ ::std::for_each(rNames.begin(), rNames.end(), RangeNameInserter(*pDoc, *pRangeNames));
+ }
+}
+
+void ScXMLImport::SetStringRefSyntaxIfMissing()
+{
+ if (!pDoc)
+ return;
+
+ ScCalcConfig aCalcConfig = pDoc->GetCalcConfig();
+
+ // Has any string ref syntax been imported?
+ // If not, we need to take action
+ if ( !aCalcConfig.mbHasStringRefSyntax )
+ {
+ aCalcConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_A1_XL_A1;
+ pDoc->SetCalcConfig(aCalcConfig);
+ }
+}
+
+void SAL_CALL ScXMLImport::endDocument()
+{
+ ScXMLImport::MutexGuard aGuard(*this);
+ if (getImportFlags() & SvXMLImportFlags::CONTENT)
+ {
+ if (GetModel().is())
+ {
+ mpDocImport->finalize();
+
+ uno::Reference<document::XViewDataSupplier> xViewDataSupplier(GetModel(), uno::UNO_QUERY);
+ if (xViewDataSupplier.is())
+ {
+ uno::Reference<container::XIndexAccess> xIndexAccess(xViewDataSupplier->getViewData());
+ if (xIndexAccess.is() && xIndexAccess->getCount() > 0)
+ {
+ uno::Sequence< beans::PropertyValue > aSeq;
+ if (xIndexAccess->getByIndex(0) >>= aSeq)
+ {
+ for (const auto& rProp : std::as_const(aSeq))
+ {
+ OUString sName(rProp.Name);
+ if (sName == SC_ACTIVETABLE)
+ {
+ OUString sTabName;
+ if(rProp.Value >>= sTabName)
+ {
+ SCTAB nTab(0);
+ if (pDoc->GetTable(sTabName, nTab))
+ {
+ pDoc->SetVisibleTab(nTab);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ SetLabelRanges();
+ SetNamedRanges();
+ SetSheetNamedRanges();
+ SetStringRefSyntaxIfMissing();
+ if (mpPivotSources)
+ // Process pivot table sources after the named ranges have been set.
+ mpPivotSources->process();
+ }
+ GetProgressBarHelper()->End(); // make room for subsequent SfxProgressBars
+ if (pDoc)
+ {
+ pDoc->CompileXML();
+
+ // After CompileXML, links must be completely changed to the new URLs.
+ // Otherwise, hasExternalFile for API wouldn't work (#i116940#),
+ // and typing a new formula would create a second link with the same "real" file name.
+ if (pDoc->HasExternalRefManager())
+ pDoc->GetExternalRefManager()->updateAbsAfterLoad();
+ }
+
+ // If the stream contains cells outside of the current limits, the styles can't be re-created,
+ // so stream copying is disabled then.
+ if (pDoc && GetModel().is() && !pDoc->HasRangeOverflow())
+ {
+ // set "valid stream" flags after loading (before UpdateRowHeights, so changed formula results
+ // in UpdateRowHeights can already clear the flags again)
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->GetSheetSaveData();
+
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
+ {
+ pDoc->SetDrawPageSize(nTab);
+ if (!pSheetData->IsSheetBlocked( nTab ))
+ pDoc->SetStreamValid( nTab, true );
+ }
+ }
+
+ // There are rows with optimal height which need to be updated
+ if (pDoc && !maRecalcRowRanges.empty())
+ {
+ bool bLockHeight = pDoc->IsAdjustHeightLocked();
+ if (bLockHeight)
+ {
+ pDoc->UnlockAdjustHeight();
+ }
+
+ ScSizeDeviceProvider aProv(static_cast<ScDocShell*>(pDoc->GetDocumentShell()));
+ ScDocRowHeightUpdater aUpdater(*pDoc, aProv.GetDevice(), aProv.GetPPTX(), aProv.GetPPTY(), &maRecalcRowRanges);
+ aUpdater.update();
+
+ if (bLockHeight)
+ {
+ pDoc->LockAdjustHeight();
+ }
+ }
+
+ // Initialize and set position and size of objects
+ if (pDoc && pDoc->GetDrawLayer())
+ {
+ ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
+ SCTAB nTabCount = pDoc->GetTableCount();
+ for (SCTAB nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ const SdrPage* pPage = pDrawLayer->GetPage(nTab);
+ if (!pPage)
+ continue;
+ bool bNegativePage = pDoc->IsNegativePage(nTab);
+ const size_t nCount = pPage->GetObjCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrObject* pObj = pPage->GetObj(i);
+ ScDrawObjData* pData
+ = ScDrawLayer::GetObjDataTab(pObj, nTab);
+ // Existence of pData means, that it is a cell anchored object
+ if (pData)
+ {
+ // Finish and correct import based on full size (no hidden row/col) and LTR
+ pDrawLayer->InitializeCellAnchoredObj(pObj, *pData);
+ // Adapt object to hidden row/col and RTL
+ pDrawLayer->RecalcPos(pObj, *pData, bNegativePage,
+ true /*bUpdateNoteCaptionPos*/);
+ }
+ }
+ }
+ }
+
+ aTables.FixupOLEs();
+ }
+ if (GetModel().is())
+ {
+ uno::Reference<document::XActionLockable> xActionLockable(GetModel(), uno::UNO_QUERY);
+ if (xActionLockable.is())
+ xActionLockable->removeActionLock();
+ }
+ SvXMLImport::endDocument();
+
+ if (pDoc)
+ {
+ pDoc->BroadcastUno(SfxHint(SfxHintId::ScClearCache));
+ }
+
+ if(pDoc && bSelfImportingXMLSet)
+ comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->AfterXMLLoading();
+}
+
+// XEventListener
+void ScXMLImport::DisposingModel()
+{
+ SvXMLImport::DisposingModel();
+ pDoc = nullptr;
+}
+
+ScXMLImport::MutexGuard::MutexGuard(ScXMLImport& rImport) :
+ mrImport(rImport)
+{
+ mrImport.LockSolarMutex();
+}
+
+ScXMLImport::MutexGuard::~MutexGuard()
+{
+ mrImport.UnlockSolarMutex();
+}
+
+void ScXMLImport::LockSolarMutex()
+{
+ // #i62677# When called from DocShell/Wrapper, the SolarMutex is already locked,
+ // so there's no need to allocate (and later delete) the SolarMutexGuard.
+ if (!mbLockSolarMutex)
+ {
+ DBG_TESTSOLARMUTEX();
+ return;
+ }
+
+ if (nSolarMutexLocked == 0)
+ {
+ OSL_ENSURE(!pSolarMutexGuard, "Solar Mutex is locked");
+ pSolarMutexGuard.reset(new SolarMutexGuard());
+ }
+ ++nSolarMutexLocked;
+}
+
+void ScXMLImport::UnlockSolarMutex()
+{
+ if (nSolarMutexLocked > 0)
+ {
+ nSolarMutexLocked--;
+ if (nSolarMutexLocked == 0)
+ {
+ OSL_ENSURE(pSolarMutexGuard, "Solar Mutex is always unlocked");
+ pSolarMutexGuard.reset();
+ }
+ }
+}
+
+sal_Int32 ScXMLImport::GetByteOffset() const
+{
+ sal_Int32 nOffset = -1;
+ uno::Reference<xml::sax::XLocator> xLocator = GetLocator();
+ uno::Reference<io::XSeekable> xSeek( xLocator, uno::UNO_QUERY ); //! should use different interface
+ if ( xSeek.is() )
+ nOffset = static_cast<sal_Int32>(xSeek->getPosition());
+ return nOffset;
+}
+
+void ScXMLImport::SetRangeOverflowType(ErrCode nType)
+{
+ // #i31130# Overflow is stored in the document, because the ScXMLImport object
+ // isn't available in ScXMLImportWrapper::ImportFromComponent when using the
+ // OOo->Oasis transformation.
+
+ if ( pDoc )
+ pDoc->SetRangeOverflowType( nType );
+}
+
+void ScXMLImport::ProgressBarIncrement()
+{
+ nProgressCount++;
+ if (nProgressCount > 100)
+ {
+ GetProgressBarHelper()->Increment(nProgressCount);
+ nProgressCount = 0;
+ }
+}
+
+void ScXMLImport::ExtractFormulaNamespaceGrammar(
+ OUString& rFormula, OUString& rFormulaNmsp, FormulaGrammar::Grammar& reGrammar,
+ const OUString& rAttrValue, bool bRestrictToExternalNmsp ) const
+{
+ // parse the attribute value, extract namespace ID, literal namespace, and formula string
+ rFormulaNmsp.clear();
+ sal_uInt16 nNsId = GetNamespaceMap().GetKeyByQName(rAttrValue, nullptr, &rFormula, &rFormulaNmsp, SvXMLNamespaceMap::QNameMode::AttrValue);
+
+ // check if we have an ODF formula namespace
+ if( !bRestrictToExternalNmsp ) switch( nNsId )
+ {
+ case XML_NAMESPACE_OOOC:
+ rFormulaNmsp.clear(); // remove namespace string for built-in grammar
+ reGrammar = FormulaGrammar::GRAM_PODF;
+ return;
+ case XML_NAMESPACE_OF:
+ rFormulaNmsp.clear(); // remove namespace string for built-in grammar
+ reGrammar = FormulaGrammar::GRAM_ODFF;
+ return;
+ }
+
+ /* Find default grammar for formulas without namespace. There may be
+ documents in the wild that stored no namespace in ODF 1.0/1.1. Use
+ GRAM_PODF then (old style ODF 1.0/1.1 formulas). The default for ODF
+ 1.2 and later without namespace is GRAM_ODFF (OpenFormula). */
+ FormulaGrammar::Grammar eDefaultGrammar =
+ (GetDocument()->GetStorageGrammar() == FormulaGrammar::GRAM_PODF) ?
+ FormulaGrammar::GRAM_PODF : FormulaGrammar::GRAM_ODFF;
+
+ /* Check if we have no namespace at all. The value XML_NAMESPACE_NONE
+ indicates that there is no colon. If the first character of the
+ attribute value is the equality sign, the value XML_NAMESPACE_UNKNOWN
+ indicates that there is a colon somewhere in the formula string. */
+ if( (nNsId == XML_NAMESPACE_NONE) || ((nNsId == XML_NAMESPACE_UNKNOWN) && (rAttrValue.toChar() == '=')) )
+ {
+ rFormula = rAttrValue; // return entire string as formula
+ reGrammar = eDefaultGrammar;
+ return;
+ }
+
+ /* Check if a namespace URL could be resolved from the attribute value.
+ Use that namespace only, if the Calc document knows an associated
+ external formula parser. This prevents that the range operator in
+ conjunction with defined names is confused as namespaces prefix, e.g.
+ in the expression 'table:A1' where 'table' is a named reference. */
+ if( ((nNsId & XML_NAMESPACE_UNKNOWN_FLAG) != 0) && !rFormulaNmsp.isEmpty() &&
+ GetDocument()->GetFormulaParserPool().hasFormulaParser( rFormulaNmsp ) )
+ {
+ reGrammar = FormulaGrammar::GRAM_EXTERNAL;
+ return;
+ }
+
+ /* All attempts failed (e.g. no namespace and no leading equality sign, or
+ an invalid namespace prefix), continue with the entire attribute value. */
+ rFormula = rAttrValue;
+ rFormulaNmsp.clear(); // remove any namespace string
+ reGrammar = eDefaultGrammar;
+}
+
+FormulaError ScXMLImport::GetFormulaErrorConstant( const OUString& rStr ) const
+{
+ if (!mpComp)
+ return FormulaError::NONE;
+
+ return mpComp->GetErrorConstant(rStr);
+}
+
+ScEditEngineDefaulter* ScXMLImport::GetEditEngine()
+{
+ if (!mpEditEngine)
+ {
+ mpEditEngine.reset(new ScEditEngineDefaulter(pDoc->GetEnginePool()));
+ mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ mpEditEngine->SetEditTextObjectPool(pDoc->GetEditPool());
+ mpEditEngine->SetUpdateLayout(false);
+ mpEditEngine->EnableUndo(false);
+ mpEditEngine->SetControlWord(mpEditEngine->GetControlWord() & ~EEControlBits::ALLOWBIGOBJS);
+ }
+ return mpEditEngine.get();
+}
+
+const ScXMLEditAttributeMap& ScXMLImport::GetEditAttributeMap() const
+{
+ if (!mpEditAttrMap)
+ mpEditAttrMap.reset(new ScXMLEditAttributeMap);
+ return *mpEditAttrMap;
+}
+
+void ScXMLImport::NotifyContainsEmbeddedFont()
+{
+ if (pDoc)
+ pDoc->SetEmbedFonts(true);
+}
+
+ScMyImpDetectiveOpArray* ScXMLImport::GetDetectiveOpArray()
+{
+ if (!pDetectiveOpArray)
+ pDetectiveOpArray.reset(new ScMyImpDetectiveOpArray());
+ return pDetectiveOpArray.get();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportFODS(SvStream &rStream)
+{
+ ScDLL::Init();
+
+ SfxObjectShellLock xDocSh(new ScDocShell);
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory());
+ uno::Reference<io::XInputStream> xStream(new ::utl::OSeekableInputStreamWrapper(rStream));
+ uno::Reference<uno::XInterface> xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW);
+
+ css::uno::Sequence<OUString> aUserData
+ {
+ "com.sun.star.comp.filter.OdfFlatXml",
+ "",
+ "com.sun.star.comp.Calc.XMLOasisImporter",
+ "com.sun.star.comp.Calc.XMLOasisExporter",
+ "",
+ "",
+ "true"
+ };
+ uno::Sequence<beans::PropertyValue> aAdaptorArgs(comphelper::InitPropertySequence(
+ {
+ { "UserData", uno::Any(aUserData) },
+ }));
+ css::uno::Sequence<uno::Any> aOuterArgs{ uno::Any(aAdaptorArgs) };
+
+ uno::Reference<lang::XInitialization> xInit(xInterface, uno::UNO_QUERY_THROW);
+ xInit->initialize(aOuterArgs);
+
+ uno::Reference<document::XImporter> xImporter(xInterface, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ {
+ { "InputStream", uno::Any(xStream) },
+ { "URL", uno::Any(OUString("private:stream")) },
+ }));
+ xImporter->setTargetDocument(xModel);
+
+ uno::Reference<document::XFilter> xFilter(xInterface, uno::UNO_QUERY_THROW);
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+ bool ret = xFilter->filter(aArgs);
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return ret;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportXLSX(SvStream &rStream)
+{
+ ScDLL::Init();
+
+ SfxObjectShellLock xDocSh(new ScDocShell);
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(comphelper::getProcessServiceFactory());
+ uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
+
+ uno::Reference<document::XFilter> xFilter(xMultiServiceFactory->createInstance("com.sun.star.comp.oox.xls.ExcelFilter"), uno::UNO_QUERY_THROW);
+
+ uno::Reference<document::XImporter> xImporter(xFilter, uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ {
+ { "InputStream", uno::Any(xStream) },
+ { "InputMode", uno::Any(true) },
+ }));
+ xImporter->setTargetDocument(xModel);
+
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+ bool ret = false;
+ try
+ {
+ ret = xFilter->filter(aArgs);
+ }
+ catch (const css::io::IOException&)
+ {
+ }
+ catch (const css::lang::WrappedTargetRuntimeException&)
+ {
+ }
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return ret;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlimprt.hxx b/sc/source/filter/xml/xmlimprt.hxx
new file mode 100644
index 000000000..79c2dd9e4
--- /dev/null
+++ b/sc/source/filter/xml/xmlimprt.hxx
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/xmlimp.hxx>
+#include <xmloff/xmlprmap.hxx>
+#include "xmlsubti.hxx"
+#include <formula/grammar.hxx>
+#include <dociter.hxx>
+
+#include <com/sun/star/sheet/ValidationAlertStyle.hpp>
+#include <com/sun/star/sheet/ValidationType.hpp>
+#include <com/sun/star/sheet/ConditionOperator.hpp>
+
+#include <memory>
+#include <map>
+#include <vector>
+#include <list>
+
+namespace com::sun::star::beans { class XPropertySet; }
+namespace com::sun::star::sheet { class XSheetCellRangeContainer; }
+namespace com::sun::star::table { struct CellRangeAddress; }
+namespace com::sun::star::util { class XNumberFormatTypes; }
+namespace com::sun::star::util { class XNumberFormats; }
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScCompiler;
+class ErrCode;
+class ScMyStyleNumberFormats;
+class XMLNumberFormatAttributesExportHelper;
+class ScEditEngineDefaulter;
+class ScDocumentImport;
+class ScMyImpDetectiveOpArray;
+class SdrPage;
+
+namespace sc {
+struct ImportPostProcessData;
+struct PivotTableSources;
+class ScDrawObjData;
+}
+
+
+class SvXMLTokenMap;
+class XMLShapeImportHelper;
+class ScXMLChangeTrackingImportHelper;
+class SolarMutexGuard;
+
+struct ScMyNamedExpression
+{
+ OUString sName;
+ OUString sContent;
+ OUString sContentNmsp;
+ OUString sBaseCellAddress;
+ OUString sRangeType;
+ formula::FormulaGrammar::Grammar eGrammar;
+ bool bIsExpression;
+};
+
+typedef ::std::list<ScMyNamedExpression> ScMyNamedExpressions;
+
+struct ScMyLabelRange
+{
+ OUString sLabelRangeStr;
+ OUString sDataRangeStr;
+ bool bColumnOrientation;
+};
+
+typedef std::list<ScMyLabelRange> ScMyLabelRanges;
+
+struct ScMyImportValidation
+{
+ OUString sName;
+ OUString sInputTitle;
+ OUString sInputMessage;
+ OUString sErrorTitle;
+ OUString sErrorMessage;
+ OUString sFormula1;
+ OUString sFormula2;
+ OUString sFormulaNmsp1;
+ OUString sFormulaNmsp2;
+ OUString sBaseCellAddress; // string is used directly
+ css::sheet::ValidationAlertStyle aAlertStyle;
+ css::sheet::ValidationType aValidationType;
+ css::sheet::ConditionOperator aOperator;
+ formula::FormulaGrammar::Grammar eGrammar1;
+ formula::FormulaGrammar::Grammar eGrammar2;
+ sal_Int16 nShowList;
+ bool bShowErrorMessage;
+ bool bShowInputMessage;
+ bool bIgnoreBlanks;
+};
+
+typedef std::vector<ScMyImportValidation> ScMyImportValidations;
+class ScMyStylesImportHelper;
+class ScXMLEditAttributeMap;
+
+class ScXMLImport: public SvXMLImport
+{
+ ScXMLImport(const ScXMLImport&) = delete;
+ const ScXMLImport& operator=(const ScXMLImport&) = delete;
+
+ typedef ::std::map<SCTAB, ScMyNamedExpressions> SheetNamedExpMap;
+
+ ScDocument* pDoc;
+ std::unique_ptr<ScDocumentImport> mpDocImport;
+ std::unique_ptr<ScCompiler> mpComp; // For error-checking of cached string cell values.
+ std::unique_ptr<ScEditEngineDefaulter> mpEditEngine;
+ std::unique_ptr<sc::PivotTableSources> mpPivotSources;
+
+ mutable std::unique_ptr<ScXMLEditAttributeMap> mpEditAttrMap;
+ std::unique_ptr<ScXMLChangeTrackingImportHelper> pChangeTrackingImportHelper;
+ std::unique_ptr<ScMyStylesImportHelper> pStylesImportHelper;
+
+ rtl::Reference < XMLPropertyHandlerFactory > xScPropHdlFactory;
+ rtl::Reference < XMLPropertySetMapper > xCellStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xColumnStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xRowStylesPropertySetMapper;
+ rtl::Reference < XMLPropertySetMapper > xTableStylesPropertySetMapper;
+
+ sc::ImportPostProcessData* mpPostProcessData; /// Lift cycle managed elsewhere, no need to delete.
+
+ ScMyTables aTables;
+
+ std::vector<ScDocRowHeightUpdater::TabRanges> maRecalcRowRanges;
+
+ ScMyNamedExpressions m_aMyNamedExpressions;
+ SheetNamedExpMap m_SheetNamedExpressions;
+
+ ScMyLabelRanges maMyLabelRanges;
+ ScMyImportValidations maValidations;
+ std::unique_ptr<ScMyImpDetectiveOpArray> pDetectiveOpArray;
+ std::unique_ptr<SolarMutexGuard> pSolarMutexGuard;
+
+ std::unique_ptr<XMLNumberFormatAttributesExportHelper> pNumberFormatAttributesExportHelper;
+ std::unique_ptr<ScMyStyleNumberFormats> pStyleNumberFormats;
+ css::uno::Reference <css::util::XNumberFormats> xNumberFormats;
+ css::uno::Reference <css::util::XNumberFormatTypes> xNumberFormatTypes;
+
+ css::uno::Reference <css::sheet::XSheetCellRangeContainer> xSheetCellRanges;
+
+ OUString sPrevStyleName;
+ OUString sPrevCurrency;
+ sal_uInt32 nSolarMutexLocked;
+ sal_Int32 nProgressCount;
+ sal_Int16 nPrevCellType;
+ bool bLoadDoc; // Load doc or styles only
+ bool bNullDateSetted;
+ bool bSelfImportingXMLSet;
+ bool mbLockSolarMutex;
+ bool mbImportStyles;
+ bool mbHasNewCondFormatData;
+
+protected:
+
+ // This method is called after the namespace map has been updated, but
+ // before a context for the current element has been pushed.
+ virtual SvXMLImportContext *CreateFastContext( sal_Int32 nElement,
+ const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual XMLShapeImportHelper* CreateShapeImport() override;
+
+public:
+ ScXMLImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString const & implementationName, SvXMLImportFlags nImportFlag,
+ const css::uno::Sequence< OUString > & sSupportedServiceNames = {});
+
+ virtual ~ScXMLImport() noexcept override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence<css::uno::Any>& aArguments ) override;
+
+ // namespace office
+ // NB: in contrast to other CreateFooContexts, this particular one handles
+ // the root element (i.e. office:document-meta)
+ SvXMLImportContext *CreateMetaContext( sal_Int32 nElement );
+ SvXMLImportContext *CreateFontDeclsContext();
+ SvXMLImportContext *CreateScriptContext();
+ SvXMLImportContext *CreateStylesContext( bool bAutoStyles );
+
+ SvXMLImportContext *CreateBodyContext(
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual void SetStatistics( const css::uno::Sequence< css::beans::NamedValue> & i_rStats) override;
+
+ ScDocumentImport& GetDoc();
+
+ ScDocument* GetDocument() { return pDoc; }
+ const ScDocument* GetDocument() const { return pDoc; }
+
+ ScMyTables& GetTables() { return aTables; }
+
+ std::vector<ScDocRowHeightUpdater::TabRanges>& GetRecalcRowRanges() { return maRecalcRowRanges; }
+
+ bool IsStylesOnlyMode() const { return !bLoadDoc; }
+
+ static sal_Int16 GetCellType(const char* rStrValue, const sal_Int32 nStrLength);
+
+ const rtl::Reference < XMLPropertySetMapper >& GetCellStylesPropertySetMapper() const { return xCellStylesPropertySetMapper; }
+ const rtl::Reference < XMLPropertySetMapper >& GetColumnStylesPropertySetMapper() const { return xColumnStylesPropertySetMapper; }
+ const rtl::Reference < XMLPropertySetMapper >& GetRowStylesPropertySetMapper() const { return xRowStylesPropertySetMapper; }
+ const rtl::Reference < XMLPropertySetMapper >& GetTableStylesPropertySetMapper() const { return xTableStylesPropertySetMapper; }
+
+ void SetPostProcessData( sc::ImportPostProcessData* p );
+ sc::ImportPostProcessData* GetPostProcessData() { return mpPostProcessData;}
+
+ sc::PivotTableSources& GetPivotTableSources();
+
+ void AddNamedExpression(ScMyNamedExpression aMyNamedExpression)
+ {
+ m_aMyNamedExpressions.push_back(std::move(aMyNamedExpression));
+ }
+
+ void AddNamedExpression(SCTAB nTab, ScMyNamedExpression aNamedExp);
+
+ void AddLabelRange(ScMyLabelRange aMyLabelRange)
+ {
+ maMyLabelRanges.push_back(std::move(aMyLabelRange));
+ }
+
+ void AddValidation(const ScMyImportValidation& rValidation) { maValidations.push_back(rValidation); }
+ bool GetValidation(const OUString& sName, ScMyImportValidation& aValidation);
+
+ ScMyImpDetectiveOpArray* GetDetectiveOpArray();
+
+ ScXMLChangeTrackingImportHelper* GetChangeTrackingImportHelper();
+ void InsertStyles();
+
+ void SetChangeTrackingViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& rChangeProps);
+ virtual void SetViewSettings(const css::uno::Sequence<css::beans::PropertyValue>& aViewProps) override;
+ virtual void SetConfigurationSettings(const css::uno::Sequence<css::beans::PropertyValue>& aConfigProps) override;
+
+ ScMyStylesImportHelper* GetStylesImportHelper() { return pStylesImportHelper.get(); }
+ sal_Int32 SetCurrencySymbol(const sal_Int32 nKey, std::u16string_view rCurrency);
+ bool IsCurrencySymbol(const sal_Int32 nNumberFormat, const OUString& sCurrencySymbol, std::u16string_view sBankSymbol);
+ void SetType(const css::uno::Reference <css::beans::XPropertySet>& rProperties,
+ sal_Int32& rNumberFormat,
+ const sal_Int16 nCellType,
+ std::u16string_view rCurrency);
+
+ void ProgressBarIncrement();
+
+ void SetNewCondFormatData() { mbHasNewCondFormatData = true; }
+ bool HasNewCondFormatData() const { return mbHasNewCondFormatData; }
+
+private:
+ void SetStyleToRanges();
+
+ void ExamineDefaultStyle();
+public:
+ void SetStyleToRanges(const ScRangeList& rRanges, const OUString* pStyleName,
+ const sal_Int16 nCellType, const OUString* pCurrency);
+ bool SetNullDateOnUnitConverter();
+ XMLNumberFormatAttributesExportHelper* GetNumberFormatAttributesExportHelper();
+ ScMyStyleNumberFormats* GetStyleNumberFormats();
+
+ void SetStylesToRangesFinished();
+
+ // XImporter
+ virtual void SAL_CALL setTargetDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) override;
+
+ virtual void SAL_CALL startDocument() override;
+ virtual void SAL_CALL endDocument() override;
+
+ virtual void DisposingModel() override;
+
+ /**
+ * Use this class to manage solar mutex locking instead of calling
+ * LockSolarMutex() and UnlockSolarMutex() directly.
+ */
+ class MutexGuard
+ {
+ public:
+ explicit MutexGuard(ScXMLImport& rImport);
+ ~MutexGuard();
+ private:
+ ScXMLImport& mrImport;
+ };
+ void LockSolarMutex();
+ void UnlockSolarMutex();
+
+ sal_Int32 GetByteOffset() const;
+
+ void SetRangeOverflowType(ErrCode nType);
+
+ static sal_Int32 GetRangeType(const OUString& sRangeType);
+ void SetNamedRanges();
+ void SetSheetNamedRanges();
+ void SetLabelRanges();
+ void SetStringRefSyntaxIfMissing();
+
+ /** Extracts the formula string, the formula grammar namespace URL, and a
+ grammar enum value from the passed formula attribute value.
+
+ @param rFormula
+ (out-parameter) Returns the plain formula string with the leading
+ equality sign if existing.
+
+ @param rFormulaNmsp
+ (out-parameter) Returns the URL of the formula grammar namespace if
+ the attribute value contains the prefix of an unknown namespace.
+
+ @param reGrammar
+ (out-parameter) Returns the exact formula grammar if the formula
+ is in a supported ODF format (e.g. FormulaGrammar::GRAM_PODF for
+ ODF 1.0/1.1 formulas, or FormulaGrammar::GRAM_ODFF for ODF 1.2
+ formulas a.k.a. OpenFormula). Returns the default storage grammar,
+ if the attribute value does not contain a namespace prefix. Returns
+ the special value FormulaGrammar::GRAM_EXTERNAL, if an unknown
+ namespace could be extracted from the formula which will be
+ contained in the parameter rFormulaNmsp then.
+
+ @param rAttrValue
+ The value of the processed formula attribute.
+
+ @param bRestrictToExternalNmsp
+ If set to true, only namespaces of external formula grammars will
+ be recognized. Internal namespace prefixes (e.g. 'oooc:' or 'of:'
+ will be considered to be part of the formula, e.g. an expression
+ with range operator.
+ */
+ void ExtractFormulaNamespaceGrammar(
+ OUString& rFormula,
+ OUString& rFormulaNmsp,
+ ::formula::FormulaGrammar::Grammar& reGrammar,
+ const OUString& rAttrValue,
+ bool bRestrictToExternalNmsp = false ) const;
+
+ FormulaError GetFormulaErrorConstant( const OUString& rStr ) const;
+
+ ScEditEngineDefaulter* GetEditEngine();
+ const ScXMLEditAttributeMap& GetEditAttributeMap() const;
+ virtual void NotifyContainsEmbeddedFont() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmllabri.cxx b/sc/source/filter/xml/xmllabri.cxx
new file mode 100644
index 000000000..e1289e4c1
--- /dev/null
+++ b/sc/source/filter/xml/xmllabri.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmllabri.hxx"
+#include <xmloff/xmltoken.hxx>
+#include "xmlimprt.hxx"
+#include <xmloff/xmlnamespace.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+ScXMLLabelRangesContext::ScXMLLabelRangesContext(
+ ScXMLImport& rImport ):
+ ScXMLImportContext( rImport )
+{
+ rImport.LockSolarMutex();
+}
+
+ScXMLLabelRangesContext::~ScXMLLabelRangesContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLLabelRangesContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext* pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_LABEL_RANGE ):
+ pContext = new ScXMLLabelRangeContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLLabelRangeContext::ScXMLLabelRangeContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ bColumnOrientation( false )
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_LABEL_CELL_RANGE_ADDRESS ):
+ sLabelRangeStr = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_CELL_RANGE_ADDRESS ):
+ sDataRangeStr = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_ORIENTATION ):
+ bColumnOrientation = IsXMLToken(aIter, XML_COLUMN );
+ break;
+ }
+ }
+}
+
+ScXMLLabelRangeContext::~ScXMLLabelRangeContext()
+{
+}
+
+void SAL_CALL ScXMLLabelRangeContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ // Label ranges must be stored as strings until all sheets are loaded
+ // (like named expressions).
+
+ ScMyLabelRange aLabelRange(
+ ScMyLabelRange{sLabelRangeStr, sDataRangeStr, bColumnOrientation});
+
+ GetScImport().AddLabelRange(std::move(aLabelRange));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmllabri.hxx b/sc/source/filter/xml/xmllabri.hxx
new file mode 100644
index 000000000..c61897654
--- /dev/null
+++ b/sc/source/filter/xml/xmllabri.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLLabelRangesContext : public ScXMLImportContext
+{
+public:
+ ScXMLLabelRangesContext(
+ ScXMLImport& rImport
+ );
+ virtual ~ScXMLLabelRangesContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList
+ ) override;
+};
+
+class ScXMLLabelRangeContext : public ScXMLImportContext
+{
+private:
+ OUString sLabelRangeStr;
+ OUString sDataRangeStr;
+ bool bColumnOrientation;
+
+public:
+ ScXMLLabelRangeContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList
+ );
+ virtual ~ScXMLLabelRangeContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlmappingi.cxx b/sc/source/filter/xml/xmlmappingi.cxx
new file mode 100644
index 000000000..9e9b23e36
--- /dev/null
+++ b/sc/source/filter/xml/xmlmappingi.cxx
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "xmlimprt.hxx"
+#include "xmlmappingi.hxx"
+#include "xmltransformationi.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <datamapper.hxx>
+#include <document.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLMappingsContext::ScXMLMappingsContext( ScXMLImport& rImport ) :
+ ScXMLImportContext( rImport )
+{
+ // has no attributes
+ rImport.LockSolarMutex();
+}
+
+ScXMLMappingsContext::~ScXMLMappingsContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLMappingsContext::createFastChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( CALC_EXT, XML_DATA_MAPPING ):
+ {
+ pContext = new ScXMLMappingContext( GetScImport(), pAttribList );
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_TRANSFORMATIONS):
+ {
+ pContext = new ScXMLTransformationsContext( GetScImport() );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLMappingContext::ScXMLMappingContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport )
+{
+ OUString aProvider;
+ OUString aID;
+ OUString aURL;
+ // OUString aFrequency;
+ OUString aDBName;
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( XLINK, XML_HREF ):
+ {
+ aURL = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_PROVIDER ):
+ {
+ aProvider = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_ID ):
+ {
+ aID = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATABASE_NAME ):
+ {
+ aDBName = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_DATA_FREQUENCY ):
+ {
+ }
+ break;
+ }
+ }
+ }
+
+ if (!aProvider.isEmpty())
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataMapper = pDoc->GetExternalDataMapper();
+ sc::ExternalDataSource aSource(aURL, aProvider, pDoc);
+ aSource.setID(aID);
+ aSource.setDBData(aDBName);
+ rDataMapper.insertDataSource(aSource);
+ }
+}
+
+ScXMLMappingContext::~ScXMLMappingContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataMapper = pDoc->GetExternalDataMapper();
+ auto& rDataSources = rDataMapper.getDataSources();
+ if(!rDataSources.empty())
+ rDataSources[0].refresh(pDoc, true);
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLMappingContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( CALC_EXT, XML_DATA_TRANSFORMATIONS):
+ {
+ pContext = new ScXMLTransformationsContext( GetScImport() );
+ }
+ break;
+ }
+
+ return pContext;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlmappingi.hxx b/sc/source/filter/xml/xmlmappingi.hxx
new file mode 100644
index 000000000..cc38d40db
--- /dev/null
+++ b/sc/source/filter/xml/xmlmappingi.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLMappingsContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLMappingsContext( ScXMLImport& rImport );
+
+ virtual ~ScXMLMappingsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+};
+
+class ScXMLMappingContext : public ScXMLImportContext
+{
+
+public:
+
+ ScXMLMappingContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLMappingContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlnexpi.cxx b/sc/source/filter/xml/xmlnexpi.cxx
new file mode 100644
index 000000000..cf2878032
--- /dev/null
+++ b/sc/source/filter/xml/xmlnexpi.cxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlnexpi.hxx"
+#include "xmlimprt.hxx"
+#include <document.hxx>
+
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLNamedExpressionsContext::GlobalInserter::GlobalInserter(ScXMLImport& rImport) : mrImport(rImport) {}
+
+void ScXMLNamedExpressionsContext::GlobalInserter::insert(ScMyNamedExpression aExp)
+{
+ mrImport.AddNamedExpression(std::move(aExp));
+}
+
+ScXMLNamedExpressionsContext::SheetLocalInserter::SheetLocalInserter(ScXMLImport& rImport, SCTAB nTab) :
+ mrImport(rImport), mnTab(nTab) {}
+
+void ScXMLNamedExpressionsContext::SheetLocalInserter::insert(ScMyNamedExpression aExp)
+{
+ mrImport.AddNamedExpression(mnTab, std::move(aExp));
+}
+
+ScXMLNamedExpressionsContext::ScXMLNamedExpressionsContext(
+ ScXMLImport& rImport,
+ std::shared_ptr<Inserter> pInserter ) :
+ ScXMLImportContext( rImport ),
+ mpInserter(std::move(pInserter))
+{
+ rImport.LockSolarMutex();
+}
+
+ScXMLNamedExpressionsContext::~ScXMLNamedExpressionsContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLNamedExpressionsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_NAMED_RANGE ):
+ pContext = new ScXMLNamedRangeContext(
+ GetScImport(), pAttribList, mpInserter.get() );
+ break;
+ case XML_ELEMENT( TABLE, XML_NAMED_EXPRESSION ):
+ pContext = new ScXMLNamedExpressionContext(
+ GetScImport(), pAttribList, mpInserter.get() );
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLNamedRangeContext::ScXMLNamedRangeContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLNamedExpressionsContext::Inserter* pInserter ) :
+ ScXMLImportContext( rImport )
+{
+ if (!pInserter)
+ return;
+
+ ScMyNamedExpression aNamedExpression;
+ // A simple table:cell-range-address is not a formula expression, stored
+ // without [] brackets but with dot, .A1
+ aNamedExpression.eGrammar = formula::FormulaGrammar::mergeToGrammar(
+ GetScImport().GetDocument()->GetStorageGrammar(),
+ formula::FormulaGrammar::CONV_OOO);
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ aNamedExpression.sName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_CELL_RANGE_ADDRESS ):
+ aNamedExpression.sContent = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_BASE_CELL_ADDRESS ):
+ aNamedExpression.sBaseCellAddress = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_RANGE_USABLE_AS ):
+ aNamedExpression.sRangeType = aIter.toString();
+ break;
+ }
+ }
+ }
+ aNamedExpression.bIsExpression = false;
+ pInserter->insert(std::move(aNamedExpression));
+}
+
+ScXMLNamedRangeContext::~ScXMLNamedRangeContext()
+{
+}
+
+ScXMLNamedExpressionContext::ScXMLNamedExpressionContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLNamedExpressionsContext::Inserter* pInserter ) :
+ ScXMLImportContext( rImport )
+{
+ if (!pInserter)
+ return;
+
+ ScMyNamedExpression aNamedExpression;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ aNamedExpression.sName = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_EXPRESSION ):
+ GetScImport().ExtractFormulaNamespaceGrammar(
+ aNamedExpression.sContent, aNamedExpression.sContentNmsp,
+ aNamedExpression.eGrammar, aIter.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_BASE_CELL_ADDRESS ):
+ aNamedExpression.sBaseCellAddress = aIter.toString();
+ break;
+ }
+ }
+ }
+ aNamedExpression.bIsExpression = true;
+ pInserter->insert(std::move(aNamedExpression));
+}
+
+ScXMLNamedExpressionContext::~ScXMLNamedExpressionContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlnexpi.hxx b/sc/source/filter/xml/xmlnexpi.hxx
new file mode 100644
index 000000000..1d53f174c
--- /dev/null
+++ b/sc/source/filter/xml/xmlnexpi.hxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <types.hxx>
+#include "importcontext.hxx"
+
+#include <memory>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+struct ScMyNamedExpression;
+
+class ScXMLNamedExpressionsContext : public ScXMLImportContext
+{
+public:
+
+ class Inserter
+ {
+ public:
+ virtual ~Inserter() {}
+ virtual void insert(ScMyNamedExpression aExp) = 0;
+ };
+
+ /**
+ * Global named expressions are inserted into ScXMLImport, which does the
+ * bulk insertion at the end of the import.
+ */
+ class GlobalInserter : public Inserter
+ {
+ public:
+ explicit GlobalInserter(ScXMLImport& rImport);
+ virtual void insert(ScMyNamedExpression aExp) override;
+ private:
+ ScXMLImport& mrImport;
+ };
+
+ /**
+ * Sheet local named expressions are inserted directly into ScRangeName
+ * instance of that sheet. TODO: the global ones should be inserted the
+ * same way for efficiency.
+ */
+ class SheetLocalInserter : public Inserter
+ {
+ public:
+ SheetLocalInserter(ScXMLImport& rImport, SCTAB nTab);
+ virtual void insert(ScMyNamedExpression aExp) override;
+ private:
+ ScXMLImport& mrImport;
+ SCTAB mnTab;
+ };
+
+ ScXMLNamedExpressionsContext(
+ ScXMLImport& rImport,
+ std::shared_ptr<Inserter> pInserter );
+
+ virtual ~ScXMLNamedExpressionsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+private:
+ std::shared_ptr<Inserter> mpInserter;
+};
+
+class ScXMLNamedRangeContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLNamedRangeContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLNamedExpressionsContext::Inserter* pInserter );
+
+ virtual ~ScXMLNamedRangeContext() override;
+};
+
+class ScXMLNamedExpressionContext : public ScXMLImportContext
+{
+public:
+
+ ScXMLNamedExpressionContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLNamedExpressionsContext::Inserter* pInserter );
+
+ virtual ~ScXMLNamedExpressionContext() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlrowi.cxx b/sc/source/filter/xml/xmlrowi.cxx
new file mode 100644
index 000000000..d510d5557
--- /dev/null
+++ b/sc/source/filter/xml/xmlrowi.cxx
@@ -0,0 +1,360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlrowi.hxx"
+#include "xmlimprt.hxx"
+#include "xmlcelli.hxx"
+#include "xmlstyli.hxx"
+#include "xmlstyle.hxx"
+#include <document.hxx>
+#include <docuno.hxx>
+#include <olinetab.hxx>
+#include <sheetdata.hxx>
+#include <documentimport.hxx>
+#include <unonames.hxx>
+
+#include <comphelper/extract.hxx>
+#include <unotools/configmgr.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <sax/fastattribs.hxx>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/table/XColumnRowRange.hpp>
+#include <com/sun/star/sheet/XPrintAreas.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <osl/diagnose.h>
+
+constexpr OUStringLiteral SC_ISFILTERED = u"IsFiltered";
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableRowContext::ScXMLTableRowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ sVisibility(GetXMLToken(XML_VISIBLE)),
+ nRepeatedRows(1),
+ bHasCell(false)
+{
+ OUString sCellStyleName;
+ if ( rAttrList.is() )
+ {
+ for (auto &it : *rAttrList)
+ {
+ switch (it.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ {
+ sStyleName = it.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_VISIBILITY ):
+ {
+ sVisibility = it.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_NUMBER_ROWS_REPEATED ):
+ {
+ nRepeatedRows = std::max( it.toInt32(), sal_Int32(1) );
+ nRepeatedRows = std::min( nRepeatedRows, rImport.GetDocument()->GetSheetLimits().GetMaxRowCount() );
+ if (utl::ConfigManager::IsFuzzing())
+ nRepeatedRows = std::min(nRepeatedRows, sal_Int32(1024));
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DEFAULT_CELL_STYLE_NAME ):
+ {
+ sCellStyleName = it.toString();
+ }
+ break;
+ /*case XML_ELEMENT( TABLE, XML_USE_OPTIMAL_HEIGHT ):
+ {
+ sOptimalHeight = it.toString();
+ }
+ break;*/
+ }
+ }
+ }
+
+ GetScImport().GetTables().AddRow();
+ GetScImport().GetTables().SetRowStyle(sCellStyleName);
+}
+
+ScXMLTableRowContext::~ScXMLTableRowContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLTableRowContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_CELL ):
+// if( IsInsertCellPossible() )
+ {
+ bHasCell = true;
+ pContext = new ScXMLTableRowCellContext( GetScImport(),
+ pAttribList, false, static_cast<SCROW>(nRepeatedRows)
+ //this
+ );
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_COVERED_TABLE_CELL ):
+// if( IsInsertCellPossible() )
+ {
+ bHasCell = true;
+ pContext = new ScXMLTableRowCellContext( GetScImport(),
+ pAttribList, true, static_cast<SCROW>(nRepeatedRows)
+ //this
+ );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLTableRowContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ ScXMLImport& rXMLImport(GetScImport());
+ ScDocument* pDoc(rXMLImport.GetDocument());
+ if (!bHasCell && nRepeatedRows > 1)
+ {
+ for (sal_Int32 i = 0; i < nRepeatedRows - 1; ++i) //one row is always added
+ GetScImport().GetTables().AddRow();
+ OSL_FAIL("it seems here is a nonvalid file; possible missing of table:table-cell element");
+ }
+ SCTAB nSheet = rXMLImport.GetTables().GetCurrentSheet();
+ sal_Int32 nCurrentRow(rXMLImport.GetTables().GetCurrentRow());
+ uno::Reference<sheet::XSpreadsheet> xSheet(rXMLImport.GetTables().GetCurrentXSheet());
+ if(!xSheet.is())
+ return;
+
+ sal_Int32 nFirstRow(nCurrentRow - nRepeatedRows + 1);
+ if (nFirstRow > pDoc->MaxRow())
+ nFirstRow = pDoc->MaxRow();
+ if (nCurrentRow > pDoc->MaxRow())
+ nCurrentRow = pDoc->MaxRow();
+ uno::Reference <table::XCellRange> xCellRange(xSheet->getCellRangeByPosition(0, nFirstRow, 0, nCurrentRow));
+ if (!xCellRange.is())
+ return;
+
+ uno::Reference<table::XColumnRowRange> xColumnRowRange (xCellRange, uno::UNO_QUERY);
+ if (!xColumnRowRange.is())
+ return;
+
+ uno::Reference <beans::XPropertySet> xRowProperties(xColumnRowRange->getRows(), uno::UNO_QUERY);
+ if (!xRowProperties.is())
+ return;
+
+ XMLTableStyleContext* ptmpStyle = nullptr;
+
+ if (!sStyleName.isEmpty())
+ {
+ XMLTableStylesContext *pStyles(static_cast<XMLTableStylesContext *>(rXMLImport.GetAutoStyles()));
+ if ( pStyles )
+ {
+ XMLTableStyleContext* pStyle(const_cast<XMLTableStyleContext*>(static_cast<const XMLTableStyleContext *>(pStyles->FindStyleChildContext(
+ XmlStyleFamily::TABLE_ROW, sStyleName, true))));
+ if (pStyle)
+ {
+ pStyle->FillPropertySet(xRowProperties);
+
+ if ( nSheet != pStyle->GetLastSheet() )
+ {
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(rXMLImport.GetModel())->GetSheetSaveData();
+ pSheetData->AddRowStyle( sStyleName, ScAddress( 0, static_cast<SCROW>(nFirstRow), nSheet ) );
+ pStyle->SetLastSheet(nSheet);
+ }
+
+ // for later checking of optimal row height
+ ptmpStyle = pStyle;
+ }
+ }
+ }
+ bool bVisible (true);
+ bool bFiltered (false);
+ if (IsXMLToken(sVisibility, XML_COLLAPSE))
+ {
+ bVisible = false;
+ }
+ else if (IsXMLToken(sVisibility, XML_FILTER))
+ {
+ bVisible = false;
+ bFiltered = true;
+ }
+ if (!bVisible)
+ {
+ rXMLImport.GetDoc().setRowsVisible(nSheet, nFirstRow, nCurrentRow, false);
+ }
+ if (bFiltered)
+ xRowProperties->setPropertyValue(SC_ISFILTERED, uno::Any(bFiltered));
+
+ uno::Any any = xRowProperties->getPropertyValue(SC_UNONAME_OHEIGHT);
+ bool bOptionalHeight = false;
+ any >>= bOptionalHeight;
+ if (bOptionalHeight)
+ {
+ // Save this row for later height update, only if we have no already optimal row heights
+ // If we have already optimal row heights, recalc only the first 200 row in case of optimal document loading
+ std::vector<ScDocRowHeightUpdater::TabRanges>& rRecalcRanges = rXMLImport.GetRecalcRowRanges();
+ while (static_cast<SCTAB>(rRecalcRanges.size()) <= nSheet)
+ {
+ rRecalcRanges.emplace_back(0, pDoc->MaxRow());
+ }
+ rRecalcRanges.at(nSheet).mnTab = nSheet;
+
+ // check that, we already have valid optimal row heights
+ if (nCurrentRow > 200 && ptmpStyle && !ptmpStyle->FindProperty(CTF_SC_ROWHEIGHT))
+ {
+ XMLPropertyState* pOptimalHeight = ptmpStyle->FindProperty(CTF_SC_ROWOPTIMALHEIGHT);
+ if (pOptimalHeight && ::cppu::any2bool(pOptimalHeight->maValue))
+ {
+ rRecalcRanges.at(nSheet).maRanges.setFalse(nFirstRow, nCurrentRow);
+ }
+ else
+ {
+ rRecalcRanges.at(nSheet).maRanges.setTrue(nFirstRow, nCurrentRow);
+ }
+ }
+ else
+ {
+ rRecalcRanges.at(nSheet).maRanges.setTrue(nFirstRow, nCurrentRow);
+ }
+ }
+}
+
+ScXMLTableRowsContext::ScXMLTableRowsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bTempHeader,
+ const bool bTempGroup ) :
+ ScXMLImportContext( rImport ),
+ nHeaderStartRow(0),
+ nGroupStartRow(0),
+ bHeader(bTempHeader),
+ bGroup(bTempGroup),
+ bGroupDisplay(true)
+{
+ // don't have any attributes
+ if (bHeader)
+ {
+ ScAddress aAddr = rImport.GetTables().GetCurrentCellPos();
+ nHeaderStartRow = aAddr.Row();
+ ++nHeaderStartRow;
+ }
+ else if (bGroup)
+ {
+ nGroupStartRow = rImport.GetTables().GetCurrentRow();
+ ++nGroupStartRow;
+ if ( rAttrList.is() )
+ {
+ auto aIter( rAttrList->find( XML_ELEMENT( TABLE, XML_DISPLAY ) ) );
+ if (aIter != rAttrList->end())
+ bGroupDisplay = IsXMLToken( aIter, XML_TRUE );
+ }
+ }
+}
+
+ScXMLTableRowsContext::~ScXMLTableRowsContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLTableRowsContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW_GROUP ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ false, true );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_ROWS ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ true, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROWS ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ false, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW ):
+ pContext = new ScXMLTableRowContext( GetScImport(), pAttribList );
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLTableRowsContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ ScXMLImport& rXMLImport(GetScImport());
+ if (bHeader)
+ {
+ SCROW nHeaderEndRow = rXMLImport.GetTables().GetCurrentRow();
+ if (nHeaderStartRow <= nHeaderEndRow)
+ {
+ uno::Reference <sheet::XPrintAreas> xPrintAreas (rXMLImport.GetTables().GetCurrentXSheet(), uno::UNO_QUERY);
+ if (xPrintAreas.is())
+ {
+ if (!xPrintAreas->getPrintTitleRows())
+ {
+ xPrintAreas->setPrintTitleRows(true);
+ table::CellRangeAddress aRowHeaderRange;
+ aRowHeaderRange.StartRow = nHeaderStartRow;
+ aRowHeaderRange.EndRow = nHeaderEndRow;
+ xPrintAreas->setTitleRows(aRowHeaderRange);
+ }
+ else
+ {
+ table::CellRangeAddress aRowHeaderRange(xPrintAreas->getTitleRows());
+ aRowHeaderRange.EndRow = nHeaderEndRow;
+ xPrintAreas->setTitleRows(aRowHeaderRange);
+ }
+ }
+ }
+ }
+ else if (bGroup)
+ {
+ SCROW nGroupEndRow = rXMLImport.GetTables().GetCurrentRow();
+ SCTAB nSheet(rXMLImport.GetTables().GetCurrentSheet());
+ if (nGroupStartRow <= nGroupEndRow)
+ {
+ ScDocument* pDoc(GetScImport().GetDocument());
+ if (pDoc)
+ {
+ ScXMLImport::MutexGuard aGuard(GetScImport());
+ ScOutlineTable* pOutlineTable(pDoc->GetOutlineTable(nSheet, true));
+ ScOutlineArray& rRowArray(pOutlineTable->GetRowArray());
+ bool bResized;
+ rRowArray.Insert(nGroupStartRow, nGroupEndRow, bResized, !bGroupDisplay);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlrowi.hxx b/sc/source/filter/xml/xmlrowi.hxx
new file mode 100644
index 000000000..981a74a59
--- /dev/null
+++ b/sc/source/filter/xml/xmlrowi.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <types.hxx>
+#include "importcontext.hxx"
+
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLTableRowContext : public ScXMLImportContext
+{
+ OUString sStyleName;
+ OUString sVisibility;
+ sal_Int32 nRepeatedRows;
+ bool bHasCell;
+
+public:
+
+ ScXMLTableRowContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableRowContext() override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+};
+
+class ScXMLTableRowsContext : public ScXMLImportContext
+{
+ SCROW nHeaderStartRow;
+ SCROW nGroupStartRow;
+ bool bHeader;
+ bool bGroup;
+ bool bGroupDisplay;
+
+public:
+
+ ScXMLTableRowsContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ const bool bHeader, const bool bGroup);
+
+ virtual ~ScXMLTableRowsContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsceni.cxx b/sc/source/filter/xml/xmlsceni.cxx
new file mode 100644
index 000000000..7a1c1848b
--- /dev/null
+++ b/sc/source/filter/xml/xmlsceni.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <document.hxx>
+#include "xmlimprt.hxx"
+#include "xmlsceni.hxx"
+#include <attrib.hxx>
+#include <rangeutl.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <sax/tools/converter.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTableScenarioContext::ScXMLTableScenarioContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ):
+ ScXMLImportContext( rImport ),
+ aBorderColor( COL_BLACK ),
+ bDisplayBorder( true ),
+ bCopyBack( true ),
+ bCopyStyles( true ),
+ bCopyFormulas( true ),
+ bIsActive( false ),
+ bProtected( false )
+{
+ rImport.LockSolarMutex();
+
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_DISPLAY_BORDER ):
+ bDisplayBorder = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_BORDER_COLOR ):
+ ::sax::Converter::convertColor(aBorderColor, aIter.toView());
+ break;
+ case XML_ELEMENT( TABLE, XML_COPY_BACK ):
+ bCopyBack = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_COPY_STYLES ):
+ bCopyStyles = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_COPY_FORMULAS ):
+ bCopyFormulas = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_IS_ACTIVE ):
+ bIsActive = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_SCENARIO_RANGES ):
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ ScRangeStringConverter::GetRangeListFromString(
+ aScenarioRanges, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO );
+ break;
+ }
+ case XML_ELEMENT( TABLE, XML_COMMENT ):
+ sComment = aIter.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTED ):
+ bProtected = IsXMLToken(aIter, XML_TRUE);
+ break;
+ }
+ }
+}
+
+ScXMLTableScenarioContext::~ScXMLTableScenarioContext()
+{
+ GetScImport().UnlockSolarMutex();
+}
+
+void SAL_CALL ScXMLTableScenarioContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ SCTAB nCurrTable( GetScImport().GetTables().GetCurrentSheet() );
+ ScDocument* pDoc(GetScImport().GetDocument());
+ if (!pDoc)
+ return;
+
+ pDoc->SetScenario( nCurrTable, true );
+ ScScenarioFlags nFlags( ScScenarioFlags::NONE );
+ if( bDisplayBorder )
+ nFlags |= ScScenarioFlags::ShowFrame;
+ if( bCopyBack )
+ nFlags |= ScScenarioFlags::TwoWay;
+ if( bCopyStyles )
+ nFlags |= ScScenarioFlags::Attrib;
+ if( !bCopyFormulas )
+ nFlags |= ScScenarioFlags::Value;
+ if( bProtected )
+ nFlags |= ScScenarioFlags::Protected;
+ pDoc->SetScenarioData( nCurrTable, sComment, aBorderColor, nFlags );
+ for( size_t i = 0; i < aScenarioRanges.size(); ++i )
+ {
+ ScRange const & rRange = aScenarioRanges[ i ];
+ pDoc->ApplyFlagsTab( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), nCurrTable, ScMF::Scenario );
+ }
+ pDoc->SetActiveScenario( nCurrTable, bIsActive );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsceni.hxx b/sc/source/filter/xml/xmlsceni.hxx
new file mode 100644
index 000000000..95f4b7180
--- /dev/null
+++ b/sc/source/filter/xml/xmlsceni.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <tools/color.hxx>
+#include <rangelst.hxx>
+#include "importcontext.hxx"
+
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLImport;
+
+class ScXMLTableScenarioContext : public ScXMLImportContext
+{
+private:
+ OUString sComment;
+ Color aBorderColor;
+ ScRangeList aScenarioRanges;
+ bool bDisplayBorder;
+ bool bCopyBack;
+ bool bCopyStyles;
+ bool bCopyFormulas;
+ bool bIsActive;
+ bool bProtected;
+
+public:
+
+ ScXMLTableScenarioContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableScenarioContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsorti.cxx b/sc/source/filter/xml/xmlsorti.cxx
new file mode 100644
index 000000000..4d33341df
--- /dev/null
+++ b/sc/source/filter/xml/xmlsorti.cxx
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmldrani.hxx"
+#include "xmlsorti.hxx"
+#include "xmlimprt.hxx"
+#include <convuno.hxx>
+#include <unonames.hxx>
+#include <rangeutl.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/util/SortField.hpp>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLSortContext::ScXMLSortContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext) :
+ ScXMLImportContext( rImport ),
+ pDatabaseRangeContext(pTempDatabaseRangeContext),
+ nUserListIndex(0),
+ bCopyOutputData(false),
+ bBindFormatsToContent(true),
+ bIsCaseSensitive(false),
+ bEnabledUserList(false)
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_BIND_STYLES_TO_CONTENT ):
+ {
+ bBindFormatsToContent = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_TARGET_RANGE_ADDRESS ):
+ {
+ ScRange aScRange;
+ sal_Int32 nOffset(0);
+ ScDocument* pDoc = GetScImport().GetDocument();
+ assert(pDoc);
+ if (ScRangeStringConverter::GetRangeFromString( aScRange, aIter.toString(), *pDoc, ::formula::FormulaGrammar::CONV_OOO, nOffset ))
+ {
+ ScUnoConversion::FillApiAddress( aOutputPosition, aScRange.aStart );
+ bCopyOutputData = true;
+ }
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_CASE_SENSITIVE ):
+ {
+ bIsCaseSensitive = IsXMLToken(aIter, XML_TRUE);
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_RFC_LANGUAGE_TAG ):
+ {
+ maLanguageTagODF.maRfcLanguageTag = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_LANGUAGE ):
+ {
+ maLanguageTagODF.maLanguage = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_SCRIPT ):
+ {
+ maLanguageTagODF.maScript = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_COUNTRY ):
+ {
+ maLanguageTagODF.maCountry = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ALGORITHM ):
+ {
+ sAlgorithm = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLSortContext::~ScXMLSortContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL ScXMLSortContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList )
+{
+ SvXMLImportContext *pContext(nullptr);
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ switch (nElement)
+ {
+ case XML_ELEMENT( TABLE, XML_SORT_BY ):
+ {
+ pContext = new ScXMLSortByContext( GetScImport(), nElement, pAttribList, this );
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLSortContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ sal_Int32 nAlgoLength(sAlgorithm.getLength());
+ sal_uInt8 i (0);
+ if (!maLanguageTagODF.isEmpty())
+ ++i;
+ if (nAlgoLength)
+ ++i;
+ uno::Sequence <beans::PropertyValue> aSortDescriptor(7 + i);
+ auto pSortDescriptor = aSortDescriptor.getArray();
+ pSortDescriptor[0].Name = SC_UNONAME_BINDFMT;
+ pSortDescriptor[0].Value <<= bBindFormatsToContent;
+ pSortDescriptor[1].Name = SC_UNONAME_COPYOUT;
+ pSortDescriptor[1].Value <<= bCopyOutputData;
+ pSortDescriptor[2].Name = SC_UNONAME_ISCASE;
+ pSortDescriptor[2].Value <<= bIsCaseSensitive;
+ pSortDescriptor[3].Name = SC_UNONAME_ISULIST;
+ pSortDescriptor[3].Value <<= bEnabledUserList;
+ pSortDescriptor[4].Name = SC_UNONAME_OUTPOS;
+ pSortDescriptor[4].Value <<= aOutputPosition;
+ pSortDescriptor[5].Name = SC_UNONAME_UINDEX;
+ pSortDescriptor[5].Value <<= nUserListIndex;
+ pSortDescriptor[6].Name = SC_UNONAME_SORTFLD;
+ pSortDescriptor[6].Value <<= aSortFields;
+ if (!maLanguageTagODF.isEmpty())
+ {
+ pSortDescriptor[7].Name = SC_UNONAME_COLLLOC;
+ pSortDescriptor[7].Value <<= maLanguageTagODF.getLanguageTag().getLocale( false);
+ }
+ if (nAlgoLength)
+ {
+ pSortDescriptor[6 + i].Name = SC_UNONAME_COLLALG;
+ pSortDescriptor[6 + i].Value <<= sAlgorithm;
+ }
+ pDatabaseRangeContext->SetSortSequence(aSortDescriptor);
+}
+
+void ScXMLSortContext::AddSortField(std::u16string_view sFieldNumber, const OUString& sDataType, std::u16string_view sOrder)
+{
+ util::SortField aSortField;
+ aSortField.Field = o3tl::toInt32(sFieldNumber);
+ if (IsXMLToken(sOrder, XML_ASCENDING))
+ aSortField.SortAscending = true;
+ else
+ aSortField.SortAscending = false;
+ if (sDataType.getLength() > 8)
+ {
+ std::u16string_view sTemp = sDataType.subView(0, 8);
+ if (sTemp == u"UserList")
+ {
+ bEnabledUserList = true;
+ sTemp = sDataType.subView(8);
+ nUserListIndex = static_cast<sal_Int16>(o3tl::toInt32(sTemp));
+ }
+ else
+ {
+ if (IsXMLToken(sDataType, XML_AUTOMATIC))
+ aSortField.FieldType = util::SortFieldType_AUTOMATIC;
+ }
+ }
+ else
+ {
+ if (IsXMLToken(sDataType, XML_TEXT))
+ aSortField.FieldType = util::SortFieldType_ALPHANUMERIC;
+ else if (IsXMLToken(sDataType, XML_NUMBER))
+ aSortField.FieldType = util::SortFieldType_NUMERIC;
+ }
+ aSortFields.realloc(aSortFields.getLength() + 1);
+ aSortFields.getArray()[aSortFields.getLength() - 1] = aSortField;
+}
+
+ScXMLSortByContext::ScXMLSortByContext( ScXMLImport& rImport,
+ sal_Int32 /*nElement*/,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLSortContext* pTempSortContext) :
+ ScXMLImportContext( rImport ),
+ pSortContext(pTempSortContext),
+ sDataType(GetXMLToken(XML_AUTOMATIC)),
+ sOrder(GetXMLToken(XML_ASCENDING))
+{
+ if ( !rAttrList.is() )
+ return;
+
+ for (auto &aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_FIELD_NUMBER ):
+ {
+ sFieldNumber = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_DATA_TYPE ):
+ {
+ sDataType = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_ORDER ):
+ {
+ sOrder = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLSortByContext::~ScXMLSortByContext()
+{
+}
+
+void SAL_CALL ScXMLSortByContext::endFastElement( sal_Int32 /*nElement*/ )
+{
+ pSortContext->AddSortField(sFieldNumber, sDataType, sOrder);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsorti.hxx b/sc/source/filter/xml/xmlsorti.hxx
new file mode 100644
index 000000000..5b927f2d5
--- /dev/null
+++ b/sc/source/filter/xml/xmlsorti.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <xmloff/languagetagodf.hxx>
+#include <com/sun/star/table/CellAddress.hpp>
+
+#include "importcontext.hxx"
+
+namespace com::sun::star::util { struct SortField; }
+namespace sax_fastparser { class FastAttributeList; }
+
+class ScXMLImport;
+class ScXMLDatabaseRangeContext;
+
+class ScXMLSortContext : public ScXMLImportContext
+{
+ ScXMLDatabaseRangeContext* pDatabaseRangeContext;
+
+ css::uno::Sequence <css::util::SortField> aSortFields;
+ css::table::CellAddress aOutputPosition;
+ LanguageTagODF maLanguageTagODF;
+ OUString sAlgorithm;
+ sal_Int16 nUserListIndex;
+ bool bCopyOutputData;
+ bool bBindFormatsToContent;
+ bool bIsCaseSensitive;
+ bool bEnabledUserList;
+
+public:
+
+ ScXMLSortContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLDatabaseRangeContext* pTempDatabaseRangeContext);
+
+ virtual ~ScXMLSortContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+
+ void AddSortField(std::u16string_view sFieldNumber, const OUString& sDataType, std::u16string_view sOrder);
+};
+
+class ScXMLSortByContext : public ScXMLImportContext
+{
+ ScXMLSortContext* pSortContext;
+
+ OUString sFieldNumber;
+ OUString sDataType;
+ OUString sOrder;
+
+public:
+
+ ScXMLSortByContext( ScXMLImport& rImport, sal_Int32 nElement,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList,
+ ScXMLSortContext* pTempSortContext);
+
+ virtual ~ScXMLSortByContext() override;
+
+ virtual void SAL_CALL endFastElement( sal_Int32 nElement ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlstyle.cxx b/sc/source/filter/xml/xmlstyle.cxx
new file mode 100644
index 000000000..aaacfecd2
--- /dev/null
+++ b/sc/source/filter/xml/xmlstyle.cxx
@@ -0,0 +1,1902 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlstyle.hxx"
+#include "xmlexprt.hxx"
+
+#include <rangeutl.hxx>
+#include <unonames.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltypes.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/contextid.hxx>
+#include <xmloff/txtprmap.hxx>
+#include <sax/tools/converter.hxx>
+#include <com/sun/star/util/CellProtection.hpp>
+#include <com/sun/star/table/CellOrientation.hpp>
+#include <com/sun/star/table/CellVertJustify2.hpp>
+#include <com/sun/star/table/CellHoriJustify.hpp>
+#include <com/sun/star/table/CellJustifyMethod.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/sheet/XSheetConditionalEntry.hpp>
+#include <com/sun/star/sheet/XSheetCondition.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <comphelper/extract.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+using namespace ::xmloff::token;
+using namespace ::formula;
+
+#define MAP(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_010, false }
+#define MAP_ODF13(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_013, false }
+// extensions import/export
+#define MAP_EXT(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_FUTURE_EXTENDED, false }
+// extensions import only
+#define MAP_EXT_I(name,prefix,token,type,context) { name, prefix, token, type, context, SvtSaveOptions::ODFSVER_FUTURE_EXTENDED, true }
+#define MAP_END() { nullptr, 0, XML_TOKEN_INVALID, 0, 0, SvtSaveOptions::ODFSVER_010, false }
+
+const XMLPropertyMapEntry aXMLScCellStylesProperties[] =
+{
+ MAP( SC_UNONAME_ASIANVERT, XML_NAMESPACE_STYLE, XML_GLYPH_ORIENTATION_VERTICAL, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_VERTICAL, 0),
+ MAP( SC_UNONAME_BOTTBORDER, XML_NAMESPACE_FO, XML_BORDER_BOTTOM, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_BOTTOMBORDER ),
+ MAP( SC_UNONAME_BOTTBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH_BOTTOM, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_BOTTOMBORDERWIDTH ),
+ MAP( SC_UNONAME_CELLBACK, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_COLORTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_CELLPRO, XML_NAMESPACE_STYLE, XML_CELL_PROTECT, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_CELLPROTECTION|MID_FLAG_MERGE_PROPERTY, 0 ),
+ MAP( SC_UNONAME_CELLPRO, XML_NAMESPACE_STYLE, XML_PRINT_CONTENT, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_PRINTCONTENT|MID_FLAG_MERGE_PROPERTY, 0 ),
+ MAP( SC_UNONAME_CELLSTYL, XML_NAMESPACE_STYLE, XML_STYLE, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_STRING, CTF_SC_CELLSTYLE ),
+ MAP( SC_UNONAME_CONDXML, XML_NAMESPACE_STYLE, XML_MAP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_STRING|MID_FLAG_SPECIAL_ITEM, CTF_SC_IMPORT_MAP ),
+ MAP( SC_UNONAME_CONDXML, XML_NAMESPACE_STYLE, XML_MAP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_STRING|MID_FLAG_SPECIAL_ITEM, CTF_SC_MAP ),
+ MAP( SC_UNONAME_DIAGONAL_BLTR, XML_NAMESPACE_STYLE, XML_DIAGONAL_BL_TR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_DIAGONALBLTR ),
+ MAP( SC_UNONAME_DIAGONAL_BLTR, XML_NAMESPACE_STYLE, XML_DIAGONAL_BL_TR_WIDTH, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_DIAGONALBLTRWIDTH ), // #i102690# for old files
+ MAP( SC_UNONAME_DIAGONAL_BLTR, XML_NAMESPACE_STYLE, XML_DIAGONAL_BL_TR_WIDTHS, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_DIAGONALBLTRWIDTHS ),
+ MAP( SC_UNONAME_DIAGONAL_TLBR, XML_NAMESPACE_STYLE, XML_DIAGONAL_TL_BR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_DIAGONALTLBR ),
+ MAP( SC_UNONAME_DIAGONAL_TLBR, XML_NAMESPACE_STYLE, XML_DIAGONAL_TL_BR_WIDTH, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_DIAGONALTLBRWIDTH ), // #i102690# for old files
+ MAP( SC_UNONAME_DIAGONAL_TLBR, XML_NAMESPACE_STYLE, XML_DIAGONAL_TL_BR_WIDTHS, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_DIAGONALTLBRWIDTHS ),
+ MAP( SC_UNONAME_CELLHJUS, XML_NAMESPACE_FO, XML_TEXT_ALIGN, XML_TYPE_PROP_PARAGRAPH|XML_SC_TYPE_HORIJUSTIFY|MID_FLAG_MERGE_PROPERTY, 0 ),
+ MAP( SC_UNONAME_CELLHJUS, XML_NAMESPACE_STYLE, XML_TEXT_ALIGN_SOURCE, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_HORIJUSTIFYSOURCE|MID_FLAG_MERGE_PROPERTY, 0 ),
+ MAP( SC_UNONAME_CELLHJUS, XML_NAMESPACE_STYLE, XML_REPEAT_CONTENT, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_HORIJUSTIFYREPEAT|MID_FLAG_MERGE_PROPERTY, 0 ),
+ // FIXME this was wrongly exported to STYLE namespace since 2013
+ MAP_EXT( SC_UNONAME_HYPERLINK, XML_NAMESPACE_STYLE, XML_HYPERLINK, XML_TYPE_PROP_TABLE_CELL | XML_TYPE_STRING | MID_FLAG_ELEMENT_ITEM, CTF_SC_HYPERLINK ),
+ MAP_EXT( SC_UNONAME_HYPERLINK, XML_NAMESPACE_LO_EXT, XML_HYPERLINK, XML_TYPE_PROP_TABLE_CELL | XML_TYPE_STRING | MID_FLAG_ELEMENT_ITEM, CTF_SC_HYPERLINK ),
+ MAP_EXT( SC_UNONAME_CELLHJUS_METHOD, XML_NAMESPACE_CSS3TEXT, XML_TEXT_JUSTIFY, XML_TYPE_PROP_PARAGRAPH|XML_SC_TYPE_HORIJUSTIFY_METHOD, 0 ),
+ MAP( SC_UNONAME_CELLTRAN, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_ISTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_WRAP, XML_NAMESPACE_FO, XML_WRAP_OPTION, XML_TYPE_PROP_TABLE_CELL|XML_SC_ISTEXTWRAPPED, 0 ),
+ MAP( SC_UNONAME_LEFTBORDER, XML_NAMESPACE_FO, XML_BORDER, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_ALLBORDER ),
+ MAP( SC_UNONAME_LEFTBORDER, XML_NAMESPACE_FO, XML_BORDER_LEFT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_LEFTBORDER ),
+ MAP( SC_UNONAME_LEFTBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_ALLBORDERWIDTH ),
+ MAP( SC_UNONAME_LEFTBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH_LEFT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_LEFTBORDERWIDTH ),
+ MAP( SC_UNONAME_NUMFMT, XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_NUMBER|MID_FLAG_SPECIAL_ITEM, CTF_SC_NUMBERFORMAT),
+ MAP( SC_UNONAME_ORIENT, XML_NAMESPACE_STYLE, XML_DIRECTION, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_ORIENTATION, 0 ),
+ MAP( SC_UNONAME_PBMARGIN, XML_NAMESPACE_FO, XML_PADDING, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_ALLPADDING ),
+ MAP( SC_UNONAME_PBMARGIN, XML_NAMESPACE_FO, XML_PADDING_BOTTOM, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_BOTTOMPADDING ),
+ MAP( SC_UNONAME_PINDENT, XML_NAMESPACE_FO, XML_MARGIN_LEFT, XML_TYPE_PROP_PARAGRAPH|XML_TYPE_MEASURE16, 0 ),
+ MAP( SC_UNONAME_PLMARGIN, XML_NAMESPACE_FO, XML_PADDING_LEFT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_LEFTPADDING ),
+ MAP( SC_UNONAME_PRMARGIN, XML_NAMESPACE_FO, XML_PADDING_RIGHT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_RIGHTPADDING ),
+ MAP( SC_UNONAME_PTMARGIN, XML_NAMESPACE_FO, XML_PADDING_TOP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_MEASURE, CTF_SC_TOPPADDING ),
+ MAP( SC_UNONAME_RIGHTBORDER, XML_NAMESPACE_FO, XML_BORDER_RIGHT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_RIGHTBORDER ),
+ MAP( SC_UNONAME_RIGHTBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH_RIGHT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_RIGHTBORDERWIDTH ),
+ MAP( SC_UNONAME_ROTANG, XML_NAMESPACE_STYLE, XML_ROTATION_ANGLE, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_ROTATEANGLE, 0 ),
+ MAP( SC_UNONAME_ROTREF, XML_NAMESPACE_STYLE, XML_ROTATION_ALIGN, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_ROTATEREFERENCE, 0),
+ MAP( SC_UNONAME_SHADOW, XML_NAMESPACE_STYLE, XML_SHADOW, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_TEXT_SHADOW, 0 ),
+ MAP( SC_UNONAME_SHRINK_TO_FIT, XML_NAMESPACE_STYLE, XML_SHRINK_TO_FIT, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BOOL, 0 ),
+ MAP( SC_UNO_STANDARDDEC, XML_NAMESPACE_STYLE, XML_DECIMAL_PLACES, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_NUMBER16, 0 ),
+ MAP( SC_UNONAME_TOPBORDER, XML_NAMESPACE_FO, XML_BORDER_TOP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER, CTF_SC_TOPBORDER ),
+ MAP( SC_UNONAME_TOPBORDER, XML_NAMESPACE_STYLE, XML_BORDER_LINE_WIDTH_TOP, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BORDER_WIDTH, CTF_SC_TOPBORDERWIDTH ),
+ MAP( SC_UNONAME_USERDEF, XML_NAMESPACE_TEXT, XML_XMLNS, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_ATTRIBUTE_CONTAINER | MID_FLAG_SPECIAL_ITEM, 0 ),
+ MAP( SC_UNONAME_VALIXML, XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION, XML_TYPE_PROP_TABLE_CELL|XML_TYPE_BUILDIN_CMP_ONLY, CTF_SC_VALIDATION ),
+ MAP( SC_UNONAME_CELLVJUS, XML_NAMESPACE_STYLE, XML_VERTICAL_ALIGN, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_VERTJUSTIFY, 0),
+ MAP_EXT_I( SC_UNONAME_CELLVJUS_METHOD, XML_NAMESPACE_STYLE, XML_VERTICAL_JUSTIFY, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_VERTJUSTIFY_METHOD, 0 ), // proposed ODF 1.2+
+ MAP_EXT( SC_UNONAME_CELLVJUS_METHOD, XML_NAMESPACE_LO_EXT, XML_VERTICAL_JUSTIFY, XML_TYPE_PROP_TABLE_CELL|XML_SC_TYPE_VERTJUSTIFY_METHOD, 0 ), // extension namespace
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScColumnStylesProperties[] =
+{
+ MAP( SC_UNONAME_MANPAGE, XML_NAMESPACE_FO, XML_BREAK_BEFORE, XML_TYPE_PROP_TABLE_COLUMN|XML_SC_TYPE_BREAKBEFORE, 0),
+ MAP( SC_UNONAME_CELLVIS, XML_NAMESPACE_TABLE, XML_DISPLAY, XML_TYPE_PROP_TABLE_COLUMN|XML_SC_TYPE_EQUAL|MID_FLAG_SPECIAL_ITEM, CTF_SC_ISVISIBLE ),
+ MAP( SC_UNONAME_CELLWID, XML_NAMESPACE_STYLE, XML_COLUMN_WIDTH, XML_TYPE_PROP_TABLE_COLUMN|XML_TYPE_MEASURE, 0 ),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScRowStylesImportProperties[] =
+{
+ // #i57867# Include background color (CellBackColor/IsCellBackgroundTransparent) for import only.
+ // Import and export should use the same map, with MID_FLAG_NO_PROPERTY_EXPORT for the background entries,
+ // but this doesn't work at the moment because SvXMLImportPropertyMapper compares MID_FLAG_NO_PROPERTY to 0.
+ // If this is changed (not for 2.0.x), a single map can be used again.
+
+ MAP( SC_UNONAME_CELLBACK, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_COLORTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_CELLHGT, XML_NAMESPACE_STYLE, XML_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_MEASURE, CTF_SC_ROWHEIGHT),
+ MAP( SC_UNONAME_CELLTRAN, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_ISTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_MANPAGE, XML_NAMESPACE_FO, XML_BREAK_BEFORE, XML_TYPE_PROP_TABLE_ROW|XML_SC_TYPE_BREAKBEFORE, CTF_SC_ROWBREAKBEFORE),
+ MAP( SC_UNONAME_OHEIGHT, XML_NAMESPACE_STYLE, XML_USE_OPTIMAL_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_BOOL, CTF_SC_ROWOPTIMALHEIGHT),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScRowStylesProperties[] =
+{
+ MAP( SC_UNONAME_CELLHGT, XML_NAMESPACE_STYLE, XML_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_MEASURE, CTF_SC_ROWHEIGHT),
+ MAP( SC_UNONAME_MANPAGE, XML_NAMESPACE_FO, XML_BREAK_BEFORE, XML_TYPE_PROP_TABLE_ROW|XML_SC_TYPE_BREAKBEFORE, CTF_SC_ROWBREAKBEFORE),
+ MAP( SC_UNONAME_OHEIGHT, XML_NAMESPACE_STYLE, XML_USE_OPTIMAL_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_BOOL, CTF_SC_ROWOPTIMALHEIGHT),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScFromXLSRowStylesProperties[] =
+{
+ MAP( SC_UNONAME_CELLHGT, XML_NAMESPACE_STYLE, XML_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_MEASURE, CTF_SC_ROWHEIGHT),
+ MAP( SC_UNONAME_MANPAGE, XML_NAMESPACE_FO, XML_BREAK_BEFORE, XML_TYPE_PROP_TABLE_ROW|XML_SC_TYPE_BREAKBEFORE, CTF_SC_ROWBREAKBEFORE),
+ MAP( SC_UNONAME_OHEIGHT, XML_NAMESPACE_STYLE, XML_USE_OPTIMAL_ROW_HEIGHT, XML_TYPE_PROP_TABLE_ROW|XML_TYPE_BOOL_FALSE, CTF_SC_ROWOPTIMALHEIGHT),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScTableStylesImportProperties[] =
+{
+ // #i57869# Include background color (CellBackColor/IsCellBackgroundTransparent) for import only.
+ // Import and export should use the same map, with MID_FLAG_NO_PROPERTY_EXPORT for the background entries,
+ // but this doesn't work at the moment because SvXMLImportPropertyMapper compares MID_FLAG_NO_PROPERTY to 0.
+ // If this is changed (not for 2.0.x), a single map can be used again.
+
+ MAP( SC_UNONAME_CELLBACK, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_CELLTRAN, XML_NAMESPACE_FO, XML_BACKGROUND_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_ISTRANSPARENT|MID_FLAG_MULTI_PROPERTY|MID_FLAG_MERGE_ATTRIBUTE, 0 ),
+ MAP( SC_UNONAME_CELLVIS, XML_NAMESPACE_TABLE, XML_DISPLAY, XML_TYPE_PROP_TABLE|XML_TYPE_BOOL, 0 ),
+ MAP( SC_UNONAME_PAGESTL, XML_NAMESPACE_STYLE, XML_MASTER_PAGE_NAME, XML_TYPE_PROP_TABLE|XML_TYPE_STRING|MID_FLAG_SPECIAL_ITEM, CTF_SC_MASTERPAGENAME ),
+ MAP( SC_UNONAME_TABLAYOUT, XML_NAMESPACE_STYLE, XML_WRITING_MODE, XML_TYPE_PROP_TABLE|XML_TYPE_TEXT_WRITING_MODE, 0 ),
+ MAP_ODF13( SC_UNONAME_TABCOLOR, XML_NAMESPACE_TABLE, XML_TAB_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORAUTO, 0 ),
+ MAP_ODF13( SC_UNONAME_TABCOLOR, XML_NAMESPACE_TABLE_EXT, XML_TAB_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORAUTO, 0 ),
+ MAP_END()
+};
+
+const XMLPropertyMapEntry aXMLScTableStylesProperties[] =
+{
+ MAP( SC_UNONAME_CELLVIS, XML_NAMESPACE_TABLE, XML_DISPLAY, XML_TYPE_PROP_TABLE|XML_TYPE_BOOL, 0 ),
+ MAP( SC_UNONAME_PAGESTL, XML_NAMESPACE_STYLE, XML_MASTER_PAGE_NAME, XML_TYPE_PROP_TABLE|XML_TYPE_STRING|MID_FLAG_SPECIAL_ITEM, CTF_SC_MASTERPAGENAME ),
+ MAP( SC_UNONAME_TABLAYOUT, XML_NAMESPACE_STYLE, XML_WRITING_MODE, XML_TYPE_PROP_TABLE|XML_TYPE_TEXT_WRITING_MODE, 0 ),
+ // ODF 1.3 OFFICE-2173
+ MAP_ODF13( SC_UNONAME_TABCOLOR, XML_NAMESPACE_TABLE, XML_TAB_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORAUTO, 0 ),
+ MAP_ODF13( SC_UNONAME_TABCOLOR, XML_NAMESPACE_TABLE_EXT, XML_TAB_COLOR, XML_TYPE_PROP_TABLE|XML_TYPE_COLORAUTO, 0 ),
+ MAP_END()
+};
+
+ScXMLCellExportPropertyMapper::ScXMLCellExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper )
+ : SvXMLExportPropertyMapper(rMapper)
+{
+}
+
+ScXMLCellExportPropertyMapper::~ScXMLCellExportPropertyMapper()
+{
+}
+
+void ScXMLCellExportPropertyMapper::ContextFilter(
+ bool bEnableFoFontFamily,
+ ::std::vector< XMLPropertyState >& rProperties,
+ const uno::Reference< beans::XPropertySet >& rPropSet ) const
+{
+ XMLPropertyState* pPadding = nullptr;
+ XMLPropertyState* pPadding_Bottom = nullptr;
+ XMLPropertyState* pPadding_Left = nullptr;
+ XMLPropertyState* pPadding_Right = nullptr;
+ XMLPropertyState* pPadding_Top = nullptr;
+
+ XMLPropertyState* pBorder = nullptr;
+ XMLPropertyState* pBorder_Bottom = nullptr;
+ XMLPropertyState* pBorder_Left = nullptr;
+ XMLPropertyState* pBorder_Right = nullptr;
+ XMLPropertyState* pBorder_Top = nullptr;
+ XMLPropertyState* pSWBorder = nullptr;
+ XMLPropertyState* pSWBorder_Bottom = nullptr;
+ XMLPropertyState* pSWBorder_Left = nullptr;
+ XMLPropertyState* pSWBorder_Right = nullptr;
+ XMLPropertyState* pSWBorder_Top = nullptr;
+
+ XMLPropertyState* pAllBorderWidthState = nullptr;
+ XMLPropertyState* pLeftBorderWidthState = nullptr;
+ XMLPropertyState* pRightBorderWidthState = nullptr;
+ XMLPropertyState* pTopBorderWidthState = nullptr;
+ XMLPropertyState* pBottomBorderWidthState = nullptr;
+ XMLPropertyState* pSWAllBorderWidthState = nullptr;
+ XMLPropertyState* pSWLeftBorderWidthState = nullptr;
+ XMLPropertyState* pSWRightBorderWidthState = nullptr;
+ XMLPropertyState* pSWTopBorderWidthState = nullptr;
+ XMLPropertyState* pSWBottomBorderWidthState = nullptr;
+ XMLPropertyState* pDiagonalTLBRWidthState = nullptr;
+ XMLPropertyState* pDiagonalBLTRWidthState = nullptr;
+
+ XMLPropertyState* pParaMarginLeft = nullptr;
+ XMLPropertyState* pParaMarginLeftRel = nullptr;
+ XMLPropertyState* pParaMarginRight = nullptr;
+ XMLPropertyState* pParaMarginRightRel = nullptr;
+ XMLPropertyState* pParaMarginTop = nullptr;
+ XMLPropertyState* pParaMarginTopRel = nullptr;
+ XMLPropertyState* pParaMarginBottom = nullptr;
+ XMLPropertyState* pParaMarginBottomRel = nullptr;
+
+ XMLPropertyState* pParaAdjust = nullptr;
+ XMLPropertyState* pParaAdjustLast = nullptr;
+
+ for( auto& rProperty : rProperties )
+ {
+ XMLPropertyState* propertyState = &rProperty;
+ if (propertyState->mnIndex != -1)
+ {
+ switch( getPropertySetMapper()->GetEntryContextId( propertyState->mnIndex ) )
+ {
+ case CTF_SC_ALLPADDING: pPadding = propertyState; break;
+ case CTF_SC_BOTTOMPADDING: pPadding_Bottom = propertyState; break;
+ case CTF_SC_LEFTPADDING: pPadding_Left = propertyState; break;
+ case CTF_SC_RIGHTPADDING: pPadding_Right = propertyState; break;
+ case CTF_SC_TOPPADDING: pPadding_Top = propertyState; break;
+ case CTF_SC_ALLBORDER: pBorder = propertyState; break;
+ case CTF_SC_LEFTBORDER: pBorder_Left = propertyState; break;
+ case CTF_SC_RIGHTBORDER: pBorder_Right = propertyState; break;
+ case CTF_SC_BOTTOMBORDER: pBorder_Bottom = propertyState; break;
+ case CTF_SC_TOPBORDER: pBorder_Top = propertyState; break;
+ case CTF_SC_ALLBORDERWIDTH: pAllBorderWidthState = propertyState; break;
+ case CTF_SC_LEFTBORDERWIDTH: pLeftBorderWidthState = propertyState; break;
+ case CTF_SC_RIGHTBORDERWIDTH: pRightBorderWidthState = propertyState; break;
+ case CTF_SC_TOPBORDERWIDTH: pTopBorderWidthState = propertyState; break;
+ case CTF_SC_BOTTOMBORDERWIDTH: pBottomBorderWidthState = propertyState; break;
+ case CTF_ALLBORDER: pSWBorder = propertyState; break;
+ case CTF_LEFTBORDER: pSWBorder_Left = propertyState; break;
+ case CTF_RIGHTBORDER: pSWBorder_Right = propertyState; break;
+ case CTF_BOTTOMBORDER: pSWBorder_Bottom = propertyState; break;
+ case CTF_TOPBORDER: pSWBorder_Top = propertyState; break;
+ case CTF_ALLBORDERWIDTH: pSWAllBorderWidthState = propertyState; break;
+ case CTF_LEFTBORDERWIDTH: pSWLeftBorderWidthState = propertyState; break;
+ case CTF_RIGHTBORDERWIDTH: pSWRightBorderWidthState = propertyState; break;
+ case CTF_TOPBORDERWIDTH: pSWTopBorderWidthState = propertyState; break;
+ case CTF_BOTTOMBORDERWIDTH: pSWBottomBorderWidthState = propertyState; break;
+ case CTF_SC_DIAGONALTLBR: break; //old diagonal line attribute names without "s" are only read, not written
+ case CTF_SC_DIAGONALTLBRWIDTH: pDiagonalTLBRWidthState = propertyState; break;
+ case CTF_SC_DIAGONALBLTR: break; //old diagonal line attribute names without "s" are only read, not written
+ case CTF_SC_DIAGONALBLTRWIDTH: pDiagonalBLTRWidthState = propertyState; break;
+ case CTF_SD_SHAPE_PARA_ADJUST: pParaAdjust = propertyState; break;
+ case CTF_PARA_ADJUSTLAST: pParaAdjustLast = propertyState; break;
+ case CTF_PARALEFTMARGIN: pParaMarginLeft = propertyState; break;
+ case CTF_PARALEFTMARGIN_REL: pParaMarginLeftRel = propertyState; break;
+ case CTF_PARARIGHTMARGIN: pParaMarginRight = propertyState; break;
+ case CTF_PARARIGHTMARGIN_REL: pParaMarginRightRel = propertyState; break;
+ case CTF_PARATOPMARGIN: pParaMarginTop = propertyState; break;
+ case CTF_PARATOPMARGIN_REL: pParaMarginTopRel = propertyState; break;
+ case CTF_PARABOTTOMMARGIN: pParaMarginBottom = propertyState; break;
+ case CTF_PARABOTTOMMARGIN_REL: pParaMarginBottomRel = propertyState; break;
+ }
+ }
+ }
+
+ if (pPadding && pPadding_Bottom && pPadding_Left && pPadding_Right && pPadding_Top)
+ {
+ sal_Int32 nBottom = 0, nTop = 0, nLeft = 0, nRight = 0;
+ if ((pPadding_Bottom->maValue >>= nBottom) &&
+ (pPadding_Left->maValue >>= nLeft) &&
+ (pPadding_Right->maValue >>= nRight) &&
+ (pPadding_Top->maValue >>= nTop))
+ {
+ if ((nBottom == nTop) && (nLeft == nRight) && (nTop == nLeft))
+ {
+ pPadding_Bottom->mnIndex = -1;
+ pPadding_Bottom->maValue.clear();
+ pPadding_Left->mnIndex = -1;
+ pPadding_Left->maValue.clear();
+ pPadding_Right->mnIndex = -1;
+ pPadding_Right->maValue.clear();
+ pPadding_Top->mnIndex = -1;
+ pPadding_Top->maValue.clear();
+ }
+ else
+ {
+ pPadding->mnIndex = -1;
+ pPadding->maValue.clear();
+ }
+ }
+ }
+ if( pBorder )
+ {
+ if( pBorder_Left && pBorder_Right && pBorder_Top && pBorder_Bottom )
+ {
+ table::BorderLine2 aLeft, aRight, aTop, aBottom;
+
+ pBorder_Left->maValue >>= aLeft;
+ pBorder_Right->maValue >>= aRight;
+ pBorder_Top->maValue >>= aTop;
+ pBorder_Bottom->maValue >>= aBottom;
+ if( aLeft.Color == aRight.Color && aLeft.InnerLineWidth == aRight.InnerLineWidth &&
+ aLeft.OuterLineWidth == aRight.OuterLineWidth && aLeft.LineDistance == aRight.LineDistance &&
+ aLeft.Color == aTop.Color && aLeft.InnerLineWidth == aTop.InnerLineWidth &&
+ aLeft.OuterLineWidth == aTop.OuterLineWidth && aLeft.LineDistance == aTop.LineDistance &&
+ aLeft.Color == aBottom.Color && aLeft.InnerLineWidth == aBottom.InnerLineWidth &&
+ aLeft.OuterLineWidth == aBottom.OuterLineWidth && aLeft.LineDistance == aBottom.LineDistance &&
+ aLeft.LineStyle == aRight.LineStyle && aLeft.LineStyle == aTop.LineStyle &&
+ aLeft.LineStyle == aBottom.LineStyle && aLeft.LineWidth == aRight.LineWidth &&
+ aLeft.LineWidth == aTop.LineWidth && aLeft.LineWidth == aBottom.LineWidth )
+ {
+ pBorder_Left->mnIndex = -1;
+ pBorder_Left->maValue.clear();
+ pBorder_Right->mnIndex = -1;
+ pBorder_Right->maValue.clear();
+ pBorder_Top->mnIndex = -1;
+ pBorder_Top->maValue.clear();
+ pBorder_Bottom->mnIndex = -1;
+ pBorder_Bottom->maValue.clear();
+ }
+ else
+ {
+ pBorder->mnIndex = -1;
+ pBorder->maValue.clear();
+ }
+ }
+ else
+ {
+ pBorder->mnIndex = -1;
+ pBorder->maValue.clear();
+ }
+ }
+ if( pAllBorderWidthState )
+ {
+ if( pLeftBorderWidthState && pRightBorderWidthState && pTopBorderWidthState && pBottomBorderWidthState )
+ {
+ table::BorderLine2 aLeft, aRight, aTop, aBottom;
+
+ pLeftBorderWidthState->maValue >>= aLeft;
+ pRightBorderWidthState->maValue >>= aRight;
+ pTopBorderWidthState->maValue >>= aTop;
+ pBottomBorderWidthState->maValue >>= aBottom;
+ if( aLeft.InnerLineWidth == aRight.InnerLineWidth && aLeft.OuterLineWidth == aRight.OuterLineWidth &&
+ aLeft.LineDistance == aRight.LineDistance && aLeft.InnerLineWidth == aTop.InnerLineWidth &&
+ aLeft.OuterLineWidth == aTop.OuterLineWidth && aLeft.LineDistance == aTop.LineDistance &&
+ aLeft.InnerLineWidth == aBottom.InnerLineWidth && aLeft.OuterLineWidth == aBottom.OuterLineWidth &&
+ aLeft.LineDistance == aBottom.LineDistance && aLeft.LineWidth == aRight.LineWidth &&
+ aLeft.LineWidth == aTop.LineWidth && aLeft.LineWidth == aBottom.LineWidth )
+ {
+ pLeftBorderWidthState->mnIndex = -1;
+ pLeftBorderWidthState->maValue.clear();
+ pRightBorderWidthState->mnIndex = -1;
+ pRightBorderWidthState->maValue.clear();
+ pTopBorderWidthState->mnIndex = -1;
+ pTopBorderWidthState->maValue.clear();
+ pBottomBorderWidthState->mnIndex = -1;
+ pBottomBorderWidthState->maValue.clear();
+ }
+ else
+ {
+ pAllBorderWidthState->mnIndex = -1;
+ pAllBorderWidthState->maValue.clear();
+ }
+ }
+ else
+ {
+ pAllBorderWidthState->mnIndex = -1;
+ pAllBorderWidthState->maValue.clear();
+ }
+ }
+
+ if (pParaAdjust)
+ {
+ pParaAdjust->mnIndex = -1;
+ pParaAdjust->maValue.clear();
+ }
+ if (pParaAdjustLast)
+ {
+ pParaAdjustLast->mnIndex = -1;
+ pParaAdjustLast->maValue.clear();
+ }
+ if (pSWBorder)
+ {
+ pSWBorder->mnIndex = -1;
+ pSWBorder->maValue.clear();
+ }
+ if (pSWBorder_Left)
+ {
+ pSWBorder_Left->mnIndex = -1;
+ pSWBorder_Left->maValue.clear();
+ }
+ if (pSWBorder_Right)
+ {
+ pSWBorder_Right->mnIndex = -1;
+ pSWBorder_Right->maValue.clear();
+ }
+ if (pSWBorder_Bottom)
+ {
+ pSWBorder_Bottom->mnIndex = -1;
+ pSWBorder_Bottom->maValue.clear();
+ }
+ if (pSWBorder_Top)
+ {
+ pSWBorder_Top->mnIndex = -1;
+ pSWBorder_Top->maValue.clear();
+ }
+ if (pSWAllBorderWidthState)
+ {
+ pSWAllBorderWidthState->mnIndex = -1;
+ pSWAllBorderWidthState->maValue.clear();
+ }
+ if (pSWLeftBorderWidthState)
+ {
+ pSWLeftBorderWidthState->mnIndex = -1;
+ pSWLeftBorderWidthState->maValue.clear();
+ }
+ if (pSWRightBorderWidthState)
+ {
+ pSWRightBorderWidthState->mnIndex = -1;
+ pSWRightBorderWidthState->maValue.clear();
+ }
+ if (pSWTopBorderWidthState)
+ {
+ pSWTopBorderWidthState->mnIndex = -1;
+ pSWTopBorderWidthState->maValue.clear();
+ }
+ if (pSWBottomBorderWidthState)
+ {
+ pSWBottomBorderWidthState->mnIndex = -1;
+ pSWBottomBorderWidthState->maValue.clear();
+ }
+
+ if (pParaMarginLeft)
+ {
+ pParaMarginLeft->mnIndex = -1;
+ pParaMarginLeft->maValue.clear();
+ }
+ if (pParaMarginLeftRel)
+ {
+ pParaMarginLeftRel->mnIndex = -1;
+ pParaMarginLeftRel->maValue.clear();
+ }
+ if (pParaMarginRight)
+ {
+ pParaMarginRight->mnIndex = -1;
+ pParaMarginRight->maValue.clear();
+ }
+ if (pParaMarginRightRel)
+ {
+ pParaMarginRightRel->mnIndex = -1;
+ pParaMarginRightRel->maValue.clear();
+ }
+ if (pParaMarginTop)
+ {
+ pParaMarginTop->mnIndex = -1;
+ pParaMarginTop->maValue.clear();
+ }
+ if (pParaMarginTopRel)
+ {
+ pParaMarginTopRel->mnIndex = -1;
+ pParaMarginTopRel->maValue.clear();
+ }
+ if (pParaMarginBottom)
+ {
+ pParaMarginBottom->mnIndex = -1;
+ pParaMarginBottom->maValue.clear();
+ }
+ if (pParaMarginBottomRel)
+ {
+ pParaMarginBottomRel->mnIndex = -1;
+ pParaMarginBottomRel->maValue.clear();
+ }
+
+ // #i102690# old diagonal line attribute names without "s" are only read, not written
+ if (pDiagonalTLBRWidthState)
+ {
+ pDiagonalTLBRWidthState->mnIndex = -1;
+ pDiagonalTLBRWidthState->maValue.clear();
+ }
+ if (pDiagonalBLTRWidthState)
+ {
+ pDiagonalBLTRWidthState->mnIndex = -1;
+ pDiagonalBLTRWidthState->maValue.clear();
+ }
+
+ SvXMLExportPropertyMapper::ContextFilter(bEnableFoFontFamily, rProperties, rPropSet);
+}
+
+/** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+void ScXMLCellExportPropertyMapper::handleSpecialItem(
+ SvXMLAttributeList& /* rAttrList */,
+ const XMLPropertyState& /* rProperty */,
+ const SvXMLUnitConverter& /* rUnitConverter */,
+ const SvXMLNamespaceMap& /* rNamespaceMap */,
+ const ::std::vector< XMLPropertyState > * /* pProperties */,
+ sal_uInt32 /* nIdx */ ) const
+{
+ // the SpecialItem NumberFormat must not be handled by this method
+ // the SpecialItem ConditionlaFormat must not be handled by this method
+ // the SpecialItem CharBackColor must not be handled by this method
+}
+void ScXMLCellExportPropertyMapper::handleElementItem(
+ SvXMLExport& rExport,
+ const XMLPropertyState& rProperty,
+ SvXmlExportFlags /* nFlags */,
+ const ::std::vector< XMLPropertyState > * /* pProperties */,
+ sal_uInt32 /* nIdx */) const
+{
+ sal_uInt32 nContextId = getPropertySetMapper()->GetEntryContextId( rProperty.mnIndex );
+ OUString sURL;
+ if ( ( nContextId == CTF_SC_HYPERLINK ) &&
+ ( rProperty.maValue >>= sURL ) &&
+ !sURL.isEmpty() )
+ {
+ rExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, sURL );
+ rExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE,
+ XML_SIMPLE );
+ sal_uInt32 nPropIndex = rProperty.mnIndex;
+ sal_uInt16 nPrefix = getPropertySetMapper()->GetEntryNameSpace( nPropIndex );
+ OUString sLocalName = getPropertySetMapper()->GetEntryXMLName( nPropIndex );
+ SvXMLElementExport aElem( rExport, nPrefix, sLocalName, true, true );
+ }
+}
+
+ScXMLRowExportPropertyMapper::ScXMLRowExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper )
+ : SvXMLExportPropertyMapper(rMapper)
+{
+}
+
+ScXMLRowExportPropertyMapper::~ScXMLRowExportPropertyMapper()
+{
+}
+
+void ScXMLRowExportPropertyMapper::ContextFilter(
+ bool /* bEnableFoFontFamily */,
+ ::std::vector< XMLPropertyState >& /* rProperties */,
+ const uno::Reference< beans::XPropertySet >& /* rPropSet */ ) const
+{
+ //#108550#; don't filter the height, so other applications know the calculated height
+}
+
+ScXMLColumnExportPropertyMapper::ScXMLColumnExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper )
+ : SvXMLExportPropertyMapper(rMapper)
+{
+}
+
+ScXMLColumnExportPropertyMapper::~ScXMLColumnExportPropertyMapper()
+{
+}
+
+/** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+void ScXMLColumnExportPropertyMapper::handleSpecialItem(
+ SvXMLAttributeList& /* rAttrList */,
+ const XMLPropertyState& /* rProperty */,
+ const SvXMLUnitConverter& /* rUnitConverter */,
+ const SvXMLNamespaceMap& /* rNamespaceMap */,
+ const ::std::vector< XMLPropertyState > * /* pProperties */,
+ sal_uInt32 /* nIdx */ ) const
+{
+ // the SpecialItem IsVisible must not be handled by this method
+}
+
+ScXMLTableExportPropertyMapper::ScXMLTableExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper )
+ : SvXMLExportPropertyMapper(rMapper)
+{
+}
+
+ScXMLTableExportPropertyMapper::~ScXMLTableExportPropertyMapper()
+{
+}
+
+/** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+void ScXMLTableExportPropertyMapper::handleSpecialItem(
+ SvXMLAttributeList& /* rAttrList */,
+ const XMLPropertyState& /* rProperty */,
+ const SvXMLUnitConverter& /* rUnitConverter */,
+ const SvXMLNamespaceMap& /* rNamespaceMap */,
+ const ::std::vector< XMLPropertyState > * /* pProperties */,
+ sal_uInt32 /* nIdx */ ) const
+{
+ // the SpecialItem PageStyle must not be handled by this method
+}
+
+void ScXMLAutoStylePoolP::exportStyleAttributes(
+ SvXMLAttributeList& rAttrList,
+ XmlStyleFamily nFamily,
+ const ::std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp
+ , const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const
+{
+ SvXMLAutoStylePoolP::exportStyleAttributes( rAttrList, nFamily, rProperties, rPropExp, rUnitConverter, rNamespaceMap );
+ if (nFamily == XmlStyleFamily::TABLE_CELL)
+ {
+ for(const auto& rProperty : rProperties)
+ {
+ rtl::Reference< XMLPropertySetMapper > aPropMapper(rScXMLExport.GetCellStylesPropertySetMapper());
+ sal_Int16 nContextID(aPropMapper->GetEntryContextId(rProperty.mnIndex));
+ switch (nContextID)
+ {
+ case CTF_SC_NUMBERFORMAT :
+ {
+ sal_Int32 nNumberFormat = 0;
+ if (rProperty.maValue >>= nNumberFormat)
+ {
+ OUString sAttrValue(rScXMLExport.getDataStyleName(nNumberFormat));
+ if (!sAttrValue.isEmpty())
+ {
+ GetExport().AddAttribute(
+ aPropMapper->GetEntryNameSpace(rProperty.mnIndex),
+ aPropMapper->GetEntryXMLName(rProperty.mnIndex),
+ sAttrValue );
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+ else if (nFamily == XmlStyleFamily::TABLE_TABLE)
+ {
+ for(const auto& rProperty : rProperties)
+ {
+ rtl::Reference< XMLPropertySetMapper > aPropMapper(rScXMLExport.GetTableStylesPropertySetMapper());
+ sal_Int16 nContextID(aPropMapper->GetEntryContextId(rProperty.mnIndex));
+ switch (nContextID)
+ {
+ case CTF_SC_MASTERPAGENAME :
+ {
+ OUString sName;
+ if (rProperty.maValue >>= sName)
+ {
+ GetExport().AddAttribute(
+ aPropMapper->GetEntryNameSpace(rProperty.mnIndex),
+ aPropMapper->GetEntryXMLName(rProperty.mnIndex),
+ GetExport().EncodeStyleName( sName ));
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+void ScXMLAutoStylePoolP::exportStyleContent(
+ const css::uno::Reference< css::xml::sax::XDocumentHandler > & rHandler,
+ XmlStyleFamily nFamily,
+ const std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp
+ , const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const
+{
+ SvXMLAutoStylePoolP::exportStyleContent( rHandler, nFamily, rProperties, rPropExp, rUnitConverter, rNamespaceMap );
+ if (nFamily != XmlStyleFamily::TABLE_CELL)
+ return;
+
+ for(const auto& rProperty : rProperties)
+ {
+ if (rProperty.mnIndex != -1)
+ {
+ sal_Int16 nContextID = rScXMLExport.GetCellStylesPropertySetMapper()->GetEntryContextId(rProperty.mnIndex);
+ switch (nContextID)
+ {
+ case CTF_SC_MAP :
+ {
+ uno::Reference<container::XIndexAccess> xIndex( rProperty.maValue, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ sal_Int32 nConditionCount(xIndex->getCount());
+ for (sal_Int32 nCondition = 0; nCondition < nConditionCount; ++nCondition)
+ {
+ uno::Reference <sheet::XSheetConditionalEntry> xSheetConditionalEntry(xIndex->getByIndex(nCondition), uno::UNO_QUERY);
+ if (xSheetConditionalEntry.is())
+ {
+ OUString sStyleName(xSheetConditionalEntry->getStyleName());
+ uno::Reference <sheet::XSheetCondition> xSheetCondition(xSheetConditionalEntry, uno::UNO_QUERY);
+ if (xSheetCondition.is())
+ {
+ sheet::ConditionOperator aOperator = xSheetCondition->getOperator();
+ if (aOperator != sheet::ConditionOperator_NONE)
+ {
+ if (aOperator == sheet::ConditionOperator_FORMULA)
+ {
+ OUString sCondition = "is-true-formula("
+ + xSheetCondition->getFormula1()
+ + ")";
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_CONDITION, sCondition);
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME, rScXMLExport.EncodeStyleName( sStyleName ));
+ OUString sOUBaseAddress;
+ ScDocument* pDoc = rScXMLExport.GetDocument();
+ ScRangeStringConverter::GetStringFromAddress( sOUBaseAddress,
+ xSheetCondition->getSourcePosition(), pDoc, FormulaGrammar::CONV_OOO );
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_BASE_CELL_ADDRESS, sOUBaseAddress);
+ SvXMLElementExport aMElem(rScXMLExport, XML_NAMESPACE_STYLE, XML_MAP, true, true);
+ }
+ else
+ {
+ OUString sCondition;
+ if (aOperator == sheet::ConditionOperator_BETWEEN ||
+ aOperator == sheet::ConditionOperator_NOT_BETWEEN)
+ {
+ if (aOperator == sheet::ConditionOperator_BETWEEN)
+ sCondition = "cell-content-is-between(";
+ else
+ sCondition = "cell-content-is-not-between(";
+ sCondition += xSheetCondition->getFormula1()
+ + ","
+ + xSheetCondition->getFormula2()
+ + ")";
+ }
+ else
+ {
+ sCondition = "cell-content()";
+ switch (aOperator)
+ {
+ case sheet::ConditionOperator_LESS:
+ sCondition += "<";
+ break;
+ case sheet::ConditionOperator_GREATER:
+ sCondition += ">";
+ break;
+ case sheet::ConditionOperator_LESS_EQUAL:
+ sCondition += "<=";
+ break;
+ case sheet::ConditionOperator_GREATER_EQUAL:
+ sCondition += ">=";
+ break;
+ case sheet::ConditionOperator_EQUAL:
+ sCondition += "=";
+ break;
+ case sheet::ConditionOperator_NOT_EQUAL:
+ sCondition += "!=";
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ sCondition += xSheetCondition->getFormula1();
+ }
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_CONDITION, sCondition);
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_APPLY_STYLE_NAME, rScXMLExport.EncodeStyleName( sStyleName ));
+ OUString sOUBaseAddress;
+ ScRangeStringConverter::GetStringFromAddress( sOUBaseAddress,
+ xSheetCondition->getSourcePosition(), rScXMLExport.GetDocument(), FormulaGrammar::CONV_OOO );
+ rScXMLExport.AddAttribute(XML_NAMESPACE_STYLE, XML_BASE_CELL_ADDRESS, sOUBaseAddress);
+ SvXMLElementExport aMElem(rScXMLExport, XML_NAMESPACE_STYLE, XML_MAP, true, true);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+ScXMLAutoStylePoolP::ScXMLAutoStylePoolP(ScXMLExport& rTempScXMLExport):
+ SvXMLAutoStylePoolP(rTempScXMLExport),
+ rScXMLExport(rTempScXMLExport)
+{
+}
+
+ScXMLAutoStylePoolP::~ScXMLAutoStylePoolP()
+{
+}
+
+XMLScPropHdlFactory::XMLScPropHdlFactory()
+{
+}
+
+XMLScPropHdlFactory::~XMLScPropHdlFactory()
+{
+}
+
+const XMLPropertyHandler* XMLScPropHdlFactory::GetPropertyHandler( sal_Int32 nType ) const
+{
+ nType &= MID_FLAG_MASK;
+
+ XMLPropertyHandler* pHdl(const_cast<XMLPropertyHandler*>(XMLPropertyHandlerFactory::GetPropertyHandler( nType )));
+ if(!pHdl)
+ {
+ switch(nType)
+ {
+ case XML_SC_TYPE_CELLPROTECTION :
+ {
+ pHdl = new XmlScPropHdl_CellProtection;
+ }
+ break;
+ case XML_SC_TYPE_PRINTCONTENT :
+ {
+ pHdl = new XmlScPropHdl_PrintContent;
+ }
+ break;
+ case XML_SC_TYPE_HORIJUSTIFY_METHOD:
+ case XML_SC_TYPE_VERTJUSTIFY_METHOD:
+ {
+ pHdl = new XmlScPropHdl_JustifyMethod;
+ }
+ break;
+ case XML_SC_TYPE_HORIJUSTIFY :
+ {
+ pHdl = new XmlScPropHdl_HoriJustify;
+ }
+ break;
+ case XML_SC_TYPE_HORIJUSTIFYSOURCE :
+ {
+ pHdl = new XmlScPropHdl_HoriJustifySource;
+ }
+ break;
+ case XML_SC_TYPE_HORIJUSTIFYREPEAT :
+ {
+ pHdl = new XmlScPropHdl_HoriJustifyRepeat;
+ }
+ break;
+ case XML_SC_TYPE_ORIENTATION :
+ {
+ pHdl = new XmlScPropHdl_Orientation;
+ }
+ break;
+ case XML_SC_TYPE_ROTATEANGLE :
+ {
+ pHdl = new XmlScPropHdl_RotateAngle;
+ }
+ break;
+ case XML_SC_TYPE_ROTATEREFERENCE :
+ {
+ pHdl = new XmlScPropHdl_RotateReference;
+ }
+ break;
+ case XML_SC_TYPE_VERTJUSTIFY :
+ {
+ pHdl = new XmlScPropHdl_VertJustify;
+ }
+ break;
+ case XML_SC_TYPE_BREAKBEFORE :
+ {
+ pHdl = new XmlScPropHdl_BreakBefore;
+ }
+ break;
+ case XML_SC_ISTEXTWRAPPED :
+ {
+ pHdl = new XmlScPropHdl_IsTextWrapped;
+ }
+ break;
+ case XML_SC_TYPE_EQUAL :
+ {
+ pHdl = new XmlScPropHdl_IsEqual;
+ }
+ break;
+ case XML_SC_TYPE_VERTICAL :
+ {
+ pHdl = new XmlScPropHdl_Vertical;
+ }
+ break;
+ }
+
+ if(pHdl)
+ PutHdlCache(nType, pHdl);
+ }
+
+ return pHdl;
+}
+
+XmlScPropHdl_CellProtection::~XmlScPropHdl_CellProtection()
+{
+}
+
+bool XmlScPropHdl_CellProtection::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ util::CellProtection aCellProtection1, aCellProtection2;
+
+ if((r1 >>= aCellProtection1) && (r2 >>= aCellProtection2))
+ {
+ return ((aCellProtection1.IsHidden == aCellProtection2.IsHidden) &&
+ (aCellProtection1.IsLocked == aCellProtection2.IsLocked) &&
+ (aCellProtection1.IsFormulaHidden == aCellProtection2.IsFormulaHidden));
+ }
+ return false;
+}
+
+bool XmlScPropHdl_CellProtection::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ util::CellProtection aCellProtection;
+ bool bDefault(false);
+ if (!rValue.hasValue())
+ {
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = true;
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsPrintHidden = false;
+ bDefault = true;
+ }
+ if ((rValue >>= aCellProtection) || bDefault)
+ {
+ if (IsXMLToken(rStrImpValue, XML_NONE))
+ {
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = false;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_HIDDEN_AND_PROTECTED))
+ {
+ aCellProtection.IsFormulaHidden = true;
+ aCellProtection.IsHidden = true;
+ aCellProtection.IsLocked = true;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_PROTECTED))
+ {
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = true;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_FORMULA_HIDDEN))
+ {
+ aCellProtection.IsFormulaHidden = true;
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = false;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ else
+ {
+ sal_Int32 i(0);
+ while (i < rStrImpValue.getLength() && rStrImpValue[i] != ' ')
+ ++i;
+ OUString sFirst(rStrImpValue.copy(0, i));
+ OUString sSecond(rStrImpValue.copy(i + 1));
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = false;
+ if ((IsXMLToken(sFirst, XML_PROTECTED)) || (IsXMLToken(sSecond, XML_PROTECTED)))
+ aCellProtection.IsLocked = true;
+ if ((IsXMLToken(sFirst, XML_FORMULA_HIDDEN)) || (IsXMLToken(sSecond, XML_FORMULA_HIDDEN)))
+ aCellProtection.IsFormulaHidden = true;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_CellProtection::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+ util::CellProtection aCellProtection;
+
+ if(rValue >>= aCellProtection)
+ {
+ if (!(aCellProtection.IsFormulaHidden || aCellProtection.IsHidden || aCellProtection.IsLocked))
+ {
+ rStrExpValue = GetXMLToken(XML_NONE);
+ bRetval = true;
+ }
+ else if (aCellProtection.IsHidden)
+ {
+ // #i105964# "Hide all" implies "Protected" in the UI, so it must be saved as "hidden-and-protected"
+ // even if "IsLocked" is not set in the CellProtection struct.
+ rStrExpValue = GetXMLToken(XML_HIDDEN_AND_PROTECTED);
+ bRetval = true;
+ }
+ else if (aCellProtection.IsLocked && !aCellProtection.IsFormulaHidden)
+ {
+ rStrExpValue = GetXMLToken(XML_PROTECTED);
+ bRetval = true;
+ }
+ else if (aCellProtection.IsFormulaHidden && !aCellProtection.IsLocked)
+ {
+ rStrExpValue = GetXMLToken(XML_FORMULA_HIDDEN);
+ bRetval = true;
+ }
+ else if (aCellProtection.IsFormulaHidden && aCellProtection.IsLocked)
+ {
+ rStrExpValue = GetXMLToken(XML_PROTECTED);
+ rStrExpValue += " ";
+ rStrExpValue += GetXMLToken(XML_FORMULA_HIDDEN);
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_PrintContent::~XmlScPropHdl_PrintContent()
+{
+}
+
+bool XmlScPropHdl_PrintContent::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ util::CellProtection aCellProtection1, aCellProtection2;
+
+ if((r1 >>= aCellProtection1) && (r2 >>= aCellProtection2))
+ {
+ return (aCellProtection1.IsPrintHidden == aCellProtection2.IsPrintHidden);
+ }
+ return false;
+}
+
+bool XmlScPropHdl_PrintContent::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+ util::CellProtection aCellProtection;
+ bool bDefault(false);
+ if (!rValue.hasValue())
+ {
+ aCellProtection.IsHidden = false;
+ aCellProtection.IsLocked = true;
+ aCellProtection.IsFormulaHidden = false;
+ aCellProtection.IsPrintHidden = false;
+ bDefault = true;
+ }
+ if ((rValue >>= aCellProtection) || bDefault)
+ {
+ bool bValue(false);
+ if (::sax::Converter::convertBool(bValue, rStrImpValue))
+ {
+ aCellProtection.IsPrintHidden = !bValue;
+ rValue <<= aCellProtection;
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_PrintContent::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ util::CellProtection aCellProtection;
+ if(rValue >>= aCellProtection)
+ {
+ OUStringBuffer sValue;
+ ::sax::Converter::convertBool(sValue, !aCellProtection.IsPrintHidden);
+ rStrExpValue = sValue.makeStringAndClear();
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_JustifyMethod::~XmlScPropHdl_JustifyMethod()
+{
+}
+
+bool XmlScPropHdl_JustifyMethod::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ sal_Int32 nVal1(0), nVal2(0);
+
+ if((r1 >>= nVal1) && (r2 >>= nVal2))
+ return (nVal1 == nVal2);
+ return false;
+}
+
+bool XmlScPropHdl_JustifyMethod::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval = false;
+
+ sal_Int32 nValue = table::CellJustifyMethod::AUTO;
+ if (IsXMLToken(rStrImpValue, XML_AUTO))
+ {
+ nValue = table::CellJustifyMethod::AUTO;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_DISTRIBUTE))
+ {
+ nValue = table::CellJustifyMethod::DISTRIBUTE;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else
+ bRetval = true;
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_JustifyMethod::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ sal_Int32 nVal(0);
+ bool bRetval = false;
+
+ if (rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellJustifyMethod::AUTO:
+ {
+ rStrExpValue = GetXMLToken(XML_AUTO);
+ bRetval = true;
+ }
+ break;
+ case table::CellJustifyMethod::DISTRIBUTE:
+ {
+ rStrExpValue = GetXMLToken(XML_DISTRIBUTE);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+ return bRetval;
+}
+
+XmlScPropHdl_HoriJustify::~XmlScPropHdl_HoriJustify()
+{
+}
+
+bool XmlScPropHdl_HoriJustify::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ table::CellHoriJustify aHoriJustify1, aHoriJustify2;
+
+ if((r1 >>= aHoriJustify1) && (r2 >>= aHoriJustify2))
+ return (aHoriJustify1 == aHoriJustify2);
+ return false;
+}
+
+bool XmlScPropHdl_HoriJustify::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ table::CellHoriJustify nValue = table::CellHoriJustify_LEFT;
+ rValue >>= nValue;
+ if (nValue != table::CellHoriJustify_REPEAT)
+ {
+ if (IsXMLToken(rStrImpValue, XML_START))
+ {
+ nValue = table::CellHoriJustify_LEFT;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_END))
+ {
+ nValue = table::CellHoriJustify_RIGHT;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_CENTER))
+ {
+ nValue = table::CellHoriJustify_CENTER;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_JUSTIFY))
+ {
+ nValue = table::CellHoriJustify_BLOCK;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ }
+ else
+ bRetval = true;
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_HoriJustify::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ table::CellHoriJustify nVal;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellHoriJustify_REPEAT:
+ case table::CellHoriJustify_LEFT:
+ {
+ rStrExpValue = GetXMLToken(XML_START);
+ bRetval = true;
+ }
+ break;
+ case table::CellHoriJustify_RIGHT:
+ {
+ rStrExpValue = GetXMLToken(XML_END);
+ bRetval = true;
+ }
+ break;
+ case table::CellHoriJustify_CENTER:
+ {
+ rStrExpValue = GetXMLToken(XML_CENTER);
+ bRetval = true;
+ }
+ break;
+ case table::CellHoriJustify_BLOCK:
+ {
+ rStrExpValue = GetXMLToken(XML_JUSTIFY);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_HoriJustifySource::~XmlScPropHdl_HoriJustifySource()
+{
+}
+
+bool XmlScPropHdl_HoriJustifySource::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ table::CellHoriJustify aHoriJustify1, aHoriJustify2;
+
+ if((r1 >>= aHoriJustify1) && (r2 >>= aHoriJustify2))
+ return (aHoriJustify1 == aHoriJustify2);
+ return false;
+}
+
+bool XmlScPropHdl_HoriJustifySource::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (IsXMLToken(rStrImpValue, XML_FIX))
+ {
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_VALUE_TYPE))
+ {
+ rValue <<= table::CellHoriJustify_STANDARD;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_HoriJustifySource::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ table::CellHoriJustify nVal;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ if (nVal == table::CellHoriJustify_STANDARD)
+ {
+ rStrExpValue = GetXMLToken(XML_VALUE_TYPE);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_FIX);
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_HoriJustifyRepeat::~XmlScPropHdl_HoriJustifyRepeat()
+{
+}
+
+bool XmlScPropHdl_HoriJustifyRepeat::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ table::CellHoriJustify aHoriJustify1, aHoriJustify2;
+
+ if((r1 >>= aHoriJustify1) && (r2 >>= aHoriJustify2))
+ return (aHoriJustify1 == aHoriJustify2);
+ return false;
+}
+
+bool XmlScPropHdl_HoriJustifyRepeat::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (IsXMLToken(rStrImpValue, XML_FALSE))
+ {
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_TRUE))
+ {
+ rValue <<= table::CellHoriJustify_REPEAT;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_HoriJustifyRepeat::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ table::CellHoriJustify nVal;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ if (nVal == table::CellHoriJustify_REPEAT)
+ {
+ rStrExpValue = GetXMLToken(XML_TRUE);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_FALSE);
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_Orientation::~XmlScPropHdl_Orientation()
+{
+}
+
+bool XmlScPropHdl_Orientation::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ table::CellOrientation aOrientation1, aOrientation2;
+
+ if((r1 >>= aOrientation1) && (r2 >>= aOrientation2))
+ return (aOrientation1 == aOrientation2);
+ return false;
+}
+
+bool XmlScPropHdl_Orientation::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ table::CellOrientation nValue;
+ if (IsXMLToken(rStrImpValue, XML_LTR))
+ {
+ nValue = table::CellOrientation_STANDARD;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_TTB))
+ {
+ nValue = table::CellOrientation_STACKED;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_Orientation::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ table::CellOrientation nVal;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellOrientation_STACKED :
+ {
+ rStrExpValue = GetXMLToken(XML_TTB);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ rStrExpValue = GetXMLToken(XML_LTR);
+ bRetval = true;
+ }
+ break;
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_RotateAngle::~XmlScPropHdl_RotateAngle()
+{
+}
+
+bool XmlScPropHdl_RotateAngle::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ sal_Int32 aAngle1 = 0, aAngle2 = 0;
+
+ if((r1 >>= aAngle1) && (r2 >>= aAngle2))
+ return (aAngle1 == aAngle2);
+ return false;
+}
+
+bool XmlScPropHdl_RotateAngle::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ sal_Int32 nValue;
+ if (::sax::Converter::convertNumber(nValue, rStrImpValue) && !o3tl::checked_multiply<sal_Int32>(nValue, 100, nValue))
+ {
+ rValue <<= nValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_RotateAngle::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ sal_Int32 nVal = 0;
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ rStrExpValue = OUString::number(nVal / 100);
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_RotateReference::~XmlScPropHdl_RotateReference()
+{
+}
+
+bool XmlScPropHdl_RotateReference::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ sal_Int32 aReference1(0), aReference2(0);
+
+ if((r1 >>= aReference1) && (r2 >>= aReference2))
+ return (aReference1 == aReference2);
+ return false;
+}
+
+bool XmlScPropHdl_RotateReference::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ sal_Int32 nValue;
+ if (IsXMLToken(rStrImpValue, XML_NONE))
+ {
+ nValue = table::CellVertJustify2::STANDARD;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_BOTTOM))
+ {
+ nValue = table::CellVertJustify2::BOTTOM;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_TOP))
+ {
+ nValue = table::CellVertJustify2::TOP;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_CENTER))
+ {
+ nValue = table::CellVertJustify2::CENTER;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_RotateReference::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ sal_Int32 nVal(0);
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellVertJustify2::BOTTOM :
+ {
+ rStrExpValue = GetXMLToken(XML_BOTTOM);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::CENTER :
+ {
+ rStrExpValue = GetXMLToken(XML_CENTER);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::STANDARD :
+ {
+ rStrExpValue = GetXMLToken(XML_NONE);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::TOP :
+ {
+ rStrExpValue = GetXMLToken(XML_TOP);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_VertJustify::~XmlScPropHdl_VertJustify()
+{
+}
+
+bool XmlScPropHdl_VertJustify::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ sal_Int32 aReference1(0), aReference2(0);
+
+ if((r1 >>= aReference1) && (r2 >>= aReference2))
+ return (aReference1 == aReference2);
+ return false;
+}
+
+bool XmlScPropHdl_VertJustify::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ sal_Int32 nValue;
+ if (IsXMLToken(rStrImpValue, XML_AUTOMATIC))
+ {
+ nValue = table::CellVertJustify2::STANDARD;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_BOTTOM))
+ {
+ nValue = table::CellVertJustify2::BOTTOM;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_TOP))
+ {
+ nValue = table::CellVertJustify2::TOP;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_MIDDLE))
+ {
+ nValue = table::CellVertJustify2::CENTER;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_JUSTIFY))
+ {
+ nValue = table::CellVertJustify2::BLOCK;
+ rValue <<= nValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_VertJustify::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ sal_Int32 nVal(0);
+ bool bRetval(false);
+
+ if(rValue >>= nVal)
+ {
+ switch (nVal)
+ {
+ case table::CellVertJustify2::BOTTOM :
+ {
+ rStrExpValue = GetXMLToken(XML_BOTTOM);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::CENTER :
+ {
+ rStrExpValue = GetXMLToken(XML_MIDDLE);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::STANDARD :
+ {
+ rStrExpValue = GetXMLToken(XML_AUTOMATIC);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::TOP :
+ {
+ rStrExpValue = GetXMLToken(XML_TOP);
+ bRetval = true;
+ }
+ break;
+ case table::CellVertJustify2::BLOCK :
+ {
+ rStrExpValue = GetXMLToken(XML_JUSTIFY);
+ bRetval = true;
+ }
+ break;
+ default:
+ {
+ // added to avoid warnings
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_BreakBefore::~XmlScPropHdl_BreakBefore()
+{
+}
+
+bool XmlScPropHdl_BreakBefore::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ bool aBreak1 = false, aBreak2 = false;
+
+ if((r1 >>= aBreak1) && (r2 >>= aBreak2))
+ return (aBreak1 == aBreak2);
+ return false;
+}
+
+bool XmlScPropHdl_BreakBefore::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ bool bValue;
+ if (IsXMLToken(rStrImpValue, XML_AUTO))
+ {
+ bValue = false;
+ rValue <<= bValue;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_PAGE))
+ {
+ bValue = true;
+ rValue <<= bValue;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_BreakBefore::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if(::cppu::any2bool(rValue))
+ {
+ rStrExpValue = GetXMLToken(XML_PAGE);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_AUTO);
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+XmlScPropHdl_IsTextWrapped::~XmlScPropHdl_IsTextWrapped()
+{
+}
+
+bool XmlScPropHdl_IsTextWrapped::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ return (::cppu::any2bool(r1) == ::cppu::any2bool(r2));
+}
+
+bool XmlScPropHdl_IsTextWrapped::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (IsXMLToken(rStrImpValue, XML_WRAP))
+ {
+ rValue <<= true;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_NO_WRAP))
+ {
+ rValue <<= false;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_IsTextWrapped::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (::cppu::any2bool(rValue))
+ {
+ rStrExpValue = GetXMLToken(XML_WRAP);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_NO_WRAP);
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_IsEqual::importXML( const OUString& /* rStrImpValue */,
+ css::uno::Any& /* rValue */,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ OSL_FAIL("should never be called");
+ return false;
+}
+
+bool XmlScPropHdl_IsEqual::exportXML( OUString& /* rStrExpValue */,
+ const css::uno::Any& /* rValue */,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ OSL_FAIL("should never be called");
+ return false;
+}
+
+XmlScPropHdl_Vertical::~XmlScPropHdl_Vertical()
+{
+}
+
+bool XmlScPropHdl_Vertical::equals(
+ const css::uno::Any& r1,
+ const css::uno::Any& r2 ) const
+{
+ return (::cppu::any2bool(r1) == ::cppu::any2bool(r2));
+}
+
+bool XmlScPropHdl_Vertical::importXML(
+ const OUString& rStrImpValue,
+ css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (IsXMLToken(rStrImpValue, XML_AUTO))
+ {
+ rValue <<= true;
+ bRetval = true;
+ }
+ else if (IsXMLToken(rStrImpValue, XML_0))
+ {
+ rValue <<= false;
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+bool XmlScPropHdl_Vertical::exportXML(
+ OUString& rStrExpValue,
+ const css::uno::Any& rValue,
+ const SvXMLUnitConverter& /* rUnitConverter */ ) const
+{
+ bool bRetval(false);
+
+ if (::cppu::any2bool(rValue))
+ {
+ rStrExpValue = GetXMLToken(XML_AUTO);
+ bRetval = true;
+ }
+ else
+ {
+ rStrExpValue = GetXMLToken(XML_0);
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlstyle.hxx b/sc/source/filter/xml/xmlstyle.hxx
new file mode 100644
index 000000000..4636ff4c4
--- /dev/null
+++ b/sc/source/filter/xml/xmlstyle.hxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <xmloff/maptype.hxx>
+#include <xmloff/xmlaustp.hxx>
+#include <xmloff/xmltypes.hxx>
+#include <xmloff/prhdlfac.hxx>
+#include <xmloff/xmlexppr.hxx>
+#include <xmloff/contextid.hxx>
+#include <xmloff/xmlprhdl.hxx>
+
+extern const XMLPropertyMapEntry aXMLScCellStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScColumnStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScRowStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScFromXLSRowStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScRowStylesImportProperties[];
+extern const XMLPropertyMapEntry aXMLScTableStylesProperties[];
+extern const XMLPropertyMapEntry aXMLScTableStylesImportProperties[];
+
+//CellStyles
+#define XML_SC_TYPE_CELLPROTECTION (XML_SC_TYPES_START + 1)
+#define XML_SC_TYPE_PRINTCONTENT (XML_SC_TYPES_START + 2)
+#define XML_SC_TYPE_HORIJUSTIFY (XML_SC_TYPES_START + 3)
+#define XML_SC_TYPE_HORIJUSTIFY_METHOD (XML_SC_TYPES_START + 4)
+#define XML_SC_TYPE_HORIJUSTIFYSOURCE (XML_SC_TYPES_START + 5)
+#define XML_SC_TYPE_HORIJUSTIFYREPEAT (XML_SC_TYPES_START + 6)
+#define XML_SC_TYPE_ORIENTATION (XML_SC_TYPES_START + 7)
+#define XML_SC_TYPE_ROTATEANGLE (XML_SC_TYPES_START + 8)
+#define XML_SC_TYPE_ROTATEREFERENCE (XML_SC_TYPES_START + 9)
+#define XML_SC_TYPE_BORDERLEFT (XML_SC_TYPES_START + 10)
+#define XML_SC_TYPE_BORDERRIGHT (XML_SC_TYPES_START + 11)
+#define XML_SC_TYPE_BORDERTOP (XML_SC_TYPES_START + 12)
+#define XML_SC_TYPE_BORDERBOTTOM (XML_SC_TYPES_START + 13)
+#define XML_SC_TYPE_VERTJUSTIFY (XML_SC_TYPES_START + 14)
+#define XML_SC_TYPE_VERTJUSTIFY_METHOD (XML_SC_TYPES_START + 15)
+#define XML_SC_ISTEXTWRAPPED (XML_SC_TYPES_START + 16)
+#define XML_SC_TYPE_EQUAL (XML_SC_TYPES_START + 17)
+#define XML_SC_TYPE_VERTICAL (XML_SC_TYPES_START + 18)
+
+// CTF_SC_HORIJUSTIFY (XML_SC_CTF_START + 1)
+// CTF_SC_HORIJUSTIFY_SOURCE (XML_SC_CTF_START + 2)
+#define CTF_SC_ALLPADDING (XML_SC_CTF_START + 3)
+#define CTF_SC_BOTTOMPADDING (XML_SC_CTF_START + 4)
+#define CTF_SC_LEFTPADDING (XML_SC_CTF_START + 5)
+#define CTF_SC_RIGHTPADDING (XML_SC_CTF_START + 6)
+#define CTF_SC_TOPPADDING (XML_SC_CTF_START + 7)
+#define CTF_SC_ALLBORDER (XML_SC_CTF_START + 8)
+#define CTF_SC_LEFTBORDER (XML_SC_CTF_START + 9)
+#define CTF_SC_RIGHTBORDER (XML_SC_CTF_START + 10)
+#define CTF_SC_TOPBORDER (XML_SC_CTF_START + 11)
+#define CTF_SC_BOTTOMBORDER (XML_SC_CTF_START + 12)
+#define CTF_SC_ALLBORDERWIDTH (XML_SC_CTF_START + 13)
+#define CTF_SC_LEFTBORDERWIDTH (XML_SC_CTF_START + 14)
+#define CTF_SC_RIGHTBORDERWIDTH (XML_SC_CTF_START + 15)
+#define CTF_SC_TOPBORDERWIDTH (XML_SC_CTF_START + 16)
+#define CTF_SC_BOTTOMBORDERWIDTH (XML_SC_CTF_START + 17)
+#define CTF_SC_NUMBERFORMAT (XML_SC_CTF_START + 18)
+#define CTF_SC_MAP (XML_SC_CTF_START + 19)
+// CTF_SC_PARAINDENT (XML_SC_CTF_START + 20)
+// CTF_SC_OLDTEXTBACKGROUND (XML_SC_CTF_START + 21)
+#define CTF_SC_IMPORT_MAP (XML_SC_CTF_START + 22)
+#define CTF_SC_CELLSTYLE (XML_SC_CTF_START + 23)
+#define CTF_SC_VALIDATION (XML_SC_CTF_START + 24)
+#define CTF_SC_DIAGONALTLBR (XML_SC_CTF_START + 25)
+#define CTF_SC_DIAGONALTLBRWIDTH (XML_SC_CTF_START + 26)
+#define CTF_SC_DIAGONALBLTR (XML_SC_CTF_START + 27)
+#define CTF_SC_DIAGONALBLTRWIDTH (XML_SC_CTF_START + 28)
+#define CTF_SC_DIAGONALTLBRWIDTHS (XML_SC_CTF_START + 29)
+#define CTF_SC_DIAGONALBLTRWIDTHS (XML_SC_CTF_START + 30)
+
+#define CTF_SC_ROWHEIGHT (XML_SC_CTF_START + 50)
+#define CTF_SC_ROWOPTIMALHEIGHT (XML_SC_CTF_START + 51)
+#define CTF_SC_ROWBREAKBEFORE (XML_SC_CTF_START + 52)
+#define CTF_SC_ISVISIBLE (XML_SC_CTF_START + 53)
+
+#define CTF_SC_MASTERPAGENAME (XML_SC_CTF_START + 53)
+#define CTF_SC_HYPERLINK (XML_SC_CTF_START + 54)
+
+//ColumnStyles
+#define XML_SC_TYPE_BREAKBEFORE (XML_SC_TYPES_START + 50)
+
+class ScXMLExport;
+
+class ScXMLCellExportPropertyMapper : public SvXMLExportPropertyMapper
+{
+protected:
+ /** Application-specific filter. By default do nothing. */
+ virtual void ContextFilter(
+ bool bEnableFoFontFamily,
+ ::std::vector< XMLPropertyState >& rProperties,
+ const css::uno::Reference<css::beans::XPropertySet >& rPropSet ) const override;
+public:
+ explicit ScXMLCellExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper );
+ virtual ~ScXMLCellExportPropertyMapper() override;
+ virtual void handleElementItem(
+ SvXMLExport& rExport,
+ const XMLPropertyState& rProperty,
+ SvXmlExportFlags nFlags,
+ const ::std::vector< XMLPropertyState > *pProperties,
+ sal_uInt32 nIdx ) const override;
+
+ /** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+ virtual void handleSpecialItem(
+ SvXMLAttributeList& rAttrList,
+ const XMLPropertyState& rProperty,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const ::std::vector< XMLPropertyState > *pProperties,
+ sal_uInt32 nIdx ) const override;
+};
+
+class ScXMLRowExportPropertyMapper : public SvXMLExportPropertyMapper
+{
+protected:
+ /** Application-specific filter. By default do nothing. */
+ virtual void ContextFilter(
+ bool bEnableFoFontFamily,
+ ::std::vector< XMLPropertyState >& rProperties,
+ const css::uno::Reference<css::beans::XPropertySet >& rPropSet ) const override;
+public:
+ explicit ScXMLRowExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper );
+ virtual ~ScXMLRowExportPropertyMapper() override;
+};
+
+class ScXMLColumnExportPropertyMapper : public SvXMLExportPropertyMapper
+{
+public:
+ explicit ScXMLColumnExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper );
+ virtual ~ScXMLColumnExportPropertyMapper() override;
+
+ /** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+ virtual void handleSpecialItem(
+ SvXMLAttributeList& rAttrList,
+ const XMLPropertyState& rProperty,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const ::std::vector< XMLPropertyState > *pProperties,
+ sal_uInt32 nIdx ) const override;
+};
+
+class ScXMLTableExportPropertyMapper : public SvXMLExportPropertyMapper
+{
+protected:
+public:
+ explicit ScXMLTableExportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper );
+ virtual ~ScXMLTableExportPropertyMapper() override;
+
+ /** this method is called for every item that has the MID_FLAG_SPECIAL_ITEM_EXPORT flag set */
+ virtual void handleSpecialItem(
+ SvXMLAttributeList& rAttrList,
+ const XMLPropertyState& rProperty,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap,
+ const ::std::vector< XMLPropertyState > *pProperties,
+ sal_uInt32 nIdx ) const override;
+};
+
+class ScXMLAutoStylePoolP : public SvXMLAutoStylePoolP
+{
+ ScXMLExport& rScXMLExport;
+
+ virtual void exportStyleAttributes(
+ SvXMLAttributeList& rAttrList,
+ XmlStyleFamily nFamily,
+ const ::std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp,
+ const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const override;
+
+ virtual void exportStyleContent(
+ const css::uno::Reference< css::xml::sax::XDocumentHandler > & rHandler,
+ XmlStyleFamily nFamily,
+ const ::std::vector< XMLPropertyState >& rProperties,
+ const SvXMLExportPropertyMapper& rPropExp
+ , const SvXMLUnitConverter& rUnitConverter,
+ const SvXMLNamespaceMap& rNamespaceMap
+ ) const override;
+
+public:
+ explicit ScXMLAutoStylePoolP(ScXMLExport& rScXMLExport);
+ virtual ~ScXMLAutoStylePoolP() override;
+};
+
+class XMLScPropHdlFactory : public XMLPropertyHandlerFactory
+{
+public:
+ XMLScPropHdlFactory();
+ virtual ~XMLScPropHdlFactory() override;
+ virtual const XMLPropertyHandler* GetPropertyHandler( sal_Int32 nType ) const override;
+};
+
+class XmlScPropHdl_CellProtection : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_CellProtection() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_PrintContent : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_PrintContent() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_JustifyMethod : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_JustifyMethod() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_HoriJustify : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_HoriJustify() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_HoriJustifySource : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_HoriJustifySource() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_HoriJustifyRepeat : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_HoriJustifyRepeat() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_Orientation : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_Orientation() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_RotateAngle : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_RotateAngle() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_RotateReference : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_RotateReference() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_VertJustify : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_VertJustify() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_BreakBefore : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_BreakBefore() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_IsTextWrapped : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_IsTextWrapped() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_IsEqual : public XMLPropertyHandler
+{
+public:
+ virtual bool equals( const css::uno::Any& /* r1 */, const css::uno::Any& /* r2 */ ) const override { return true; }
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+class XmlScPropHdl_Vertical : public XMLPropertyHandler
+{
+public:
+ virtual ~XmlScPropHdl_Vertical() override;
+ virtual bool equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const override;
+ virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+ virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlstyli.cxx b/sc/source/filter/xml/xmlstyli.cxx
new file mode 100644
index 000000000..96fc38cbc
--- /dev/null
+++ b/sc/source/filter/xml/xmlstyli.cxx
@@ -0,0 +1,1037 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlstyli.hxx"
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmlimppr.hxx>
+#include <xmloff/families.hxx>
+#include <xmloff/xmlnumfi.hxx>
+#include <xmloff/XMLGraphicsDefaultStyle.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <comphelper/extract.hxx>
+#include <xmloff/xmlprcon.hxx>
+#include "XMLTableHeaderFooterContext.hxx"
+#include "XMLConverter.hxx"
+#include "XMLTableShapeImportHelper.hxx"
+#include <sheetdata.hxx>
+#include "xmlannoi.hxx"
+#include <textuno.hxx>
+#include <cellsuno.hxx>
+#include "xmlstyle.hxx"
+
+#include <docuno.hxx>
+#include <unonames.hxx>
+#include <document.hxx>
+#include <conditio.hxx>
+#include <rangelst.hxx>
+
+#define XML_LINE_LEFT 0
+#define XML_LINE_RIGHT 1
+#define XML_LINE_TOP 2
+#define XML_LINE_BOTTOM 3
+
+#define XML_LINE_TLBR 0
+#define XML_LINE_BLTR 1
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace xmloff::token;
+using namespace ::formula;
+
+using com::sun::star::uno::UNO_QUERY;
+ScXMLCellImportPropertyMapper::ScXMLCellImportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper,
+ SvXMLImport& rImportP) :
+ SvXMLImportPropertyMapper( rMapper, rImportP )
+{
+}
+
+ScXMLCellImportPropertyMapper::~ScXMLCellImportPropertyMapper()
+{
+}
+
+void ScXMLCellImportPropertyMapper::finished(::std::vector< XMLPropertyState >& rProperties, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) const
+{
+ static const sal_Int16 aPaddingCTF[4] = { CTF_SC_LEFTPADDING, CTF_SC_RIGHTPADDING,
+ CTF_SC_TOPPADDING, CTF_SC_BOTTOMPADDING };
+ static const sal_Int16 aBorderCTF[4] = { CTF_SC_LEFTBORDER, CTF_SC_RIGHTBORDER,
+ CTF_SC_TOPBORDER, CTF_SC_BOTTOMBORDER };
+
+ SvXMLImportPropertyMapper::finished(rProperties, nStartIndex, nEndIndex);
+ XMLPropertyState* pAllPaddingProperty(nullptr);
+ XMLPropertyState* pPadding[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pNewPadding[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pAllBorderProperty = nullptr;
+ XMLPropertyState* pBorders[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pNewBorders[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pAllBorderWidthProperty = nullptr;
+ XMLPropertyState* pBorderWidths[4] = { nullptr, nullptr, nullptr, nullptr };
+ XMLPropertyState* pDiagBorders[2] = { nullptr };
+ XMLPropertyState* pOldDiagBorderWidths[2] = { nullptr }; // old attribute names without "s"
+ XMLPropertyState* pDiagBorderWidths[2] = { nullptr };
+
+ for (auto& rProperty : rProperties)
+ {
+ XMLPropertyState*property = &rProperty;
+ if (property->mnIndex != -1)
+ {
+ sal_Int16 nContextID = getPropertySetMapper()->GetEntryContextId(property->mnIndex);
+ switch (nContextID)
+ {
+ case CTF_SC_ALLPADDING : pAllPaddingProperty = property; break;
+ case CTF_SC_LEFTPADDING : pPadding[XML_LINE_LEFT] = property; break;
+ case CTF_SC_RIGHTPADDING : pPadding[XML_LINE_RIGHT] = property; break;
+ case CTF_SC_TOPPADDING : pPadding[XML_LINE_TOP] = property; break;
+ case CTF_SC_BOTTOMPADDING : pPadding[XML_LINE_BOTTOM] = property; break;
+ case CTF_SC_ALLBORDER : pAllBorderProperty = property; break;
+ case CTF_SC_LEFTBORDER : pBorders[XML_LINE_LEFT] = property; break;
+ case CTF_SC_RIGHTBORDER : pBorders[XML_LINE_RIGHT] = property; break;
+ case CTF_SC_TOPBORDER : pBorders[XML_LINE_TOP] = property; break;
+ case CTF_SC_BOTTOMBORDER : pBorders[XML_LINE_BOTTOM] = property; break;
+ case CTF_SC_ALLBORDERWIDTH : pAllBorderWidthProperty = property; break;
+ case CTF_SC_LEFTBORDERWIDTH : pBorderWidths[XML_LINE_LEFT] = property; break;
+ case CTF_SC_RIGHTBORDERWIDTH : pBorderWidths[XML_LINE_RIGHT] = property; break;
+ case CTF_SC_TOPBORDERWIDTH : pBorderWidths[XML_LINE_TOP] = property; break;
+ case CTF_SC_BOTTOMBORDERWIDTH : pBorderWidths[XML_LINE_BOTTOM] = property; break;
+ case CTF_SC_DIAGONALTLBR : pDiagBorders[XML_LINE_TLBR] = property; break;
+ case CTF_SC_DIAGONALBLTR : pDiagBorders[XML_LINE_BLTR] = property; break;
+ case CTF_SC_DIAGONALTLBRWIDTH : pOldDiagBorderWidths[XML_LINE_TLBR] = property; break;
+ case CTF_SC_DIAGONALTLBRWIDTHS : pDiagBorderWidths[XML_LINE_TLBR] = property; break;
+ case CTF_SC_DIAGONALBLTRWIDTH : pOldDiagBorderWidths[XML_LINE_BLTR] = property; break;
+ case CTF_SC_DIAGONALBLTRWIDTHS : pDiagBorderWidths[XML_LINE_BLTR] = property; break;
+ }
+ }
+ }
+ sal_uInt16 i;
+
+ // #i27594#; copy Value, but don't insert
+ if (pAllBorderWidthProperty)
+ pAllBorderWidthProperty->mnIndex = -1;
+ if (pAllBorderProperty)
+ pAllBorderProperty->mnIndex = -1;
+ if (pAllPaddingProperty)
+ pAllPaddingProperty->mnIndex = -1;
+
+ for (i = 0; i < 4; ++i)
+ {
+ if (pAllPaddingProperty && !pPadding[i])
+ pNewPadding[i] = new XMLPropertyState(maPropMapper->FindEntryIndex(aPaddingCTF[i]), pAllPaddingProperty->maValue);
+ if (pAllBorderProperty && !pBorders[i])
+ {
+ pNewBorders[i] = new XMLPropertyState(maPropMapper->FindEntryIndex(aBorderCTF[i]), pAllBorderProperty->maValue);
+ pBorders[i] = pNewBorders[i];
+ }
+ if( !pBorderWidths[i] )
+ pBorderWidths[i] = pAllBorderWidthProperty;
+ else
+ pBorderWidths[i]->mnIndex = -1;
+ if( pBorders[i] )
+ {
+ table::BorderLine2 aBorderLine;
+ pBorders[i]->maValue >>= aBorderLine;
+ if( pBorderWidths[i] )
+ {
+ // Merge style:border-line-width values to fo:border values. Do
+ // not override fo:border line width or line style with an
+ // empty value!
+ table::BorderLine2 aBorderLineWidth;
+ pBorderWidths[i]->maValue >>= aBorderLineWidth;
+ aBorderLine.OuterLineWidth = aBorderLineWidth.OuterLineWidth;
+ aBorderLine.InnerLineWidth = aBorderLineWidth.InnerLineWidth;
+ aBorderLine.LineDistance = aBorderLineWidth.LineDistance;
+ pBorders[i]->maValue <<= aBorderLine;
+ }
+ }
+ }
+ for( i = 0; i < 2; ++i )
+ {
+ if( pDiagBorders[i] && ( pDiagBorderWidths[i] || pOldDiagBorderWidths[i] ) )
+ {
+ table::BorderLine2 aBorderLine;
+ pDiagBorders[i]->maValue >>= aBorderLine;
+ table::BorderLine2 aBorderLineWidth;
+ if (pDiagBorderWidths[i])
+ pDiagBorderWidths[i]->maValue >>= aBorderLineWidth; // prefer new attribute
+ else
+ pOldDiagBorderWidths[i]->maValue >>= aBorderLineWidth;
+ aBorderLine.OuterLineWidth = aBorderLineWidth.OuterLineWidth;
+ aBorderLine.InnerLineWidth = aBorderLineWidth.InnerLineWidth;
+ aBorderLine.LineDistance = aBorderLineWidth.LineDistance;
+ pDiagBorders[i]->maValue <<= aBorderLine;
+ if (pDiagBorderWidths[i])
+ pDiagBorderWidths[i]->mnIndex = -1;
+ if (pOldDiagBorderWidths[i])
+ pOldDiagBorderWidths[i]->mnIndex = -1; // reset mnIndex for old and new attribute if both are present
+ }
+ }
+
+ for (i = 0; i < 4; ++i)
+ {
+ if (pNewPadding[i])
+ {
+ rProperties.push_back(*pNewPadding[i]);
+ delete pNewPadding[i];
+ }
+ if (pNewBorders[i])
+ {
+ rProperties.push_back(*pNewBorders[i]);
+ delete pNewBorders[i];
+ }
+ }
+}
+
+ScXMLRowImportPropertyMapper::ScXMLRowImportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper,
+ SvXMLImport& rImportP) :
+ SvXMLImportPropertyMapper( rMapper, rImportP )
+{
+}
+
+ScXMLRowImportPropertyMapper::~ScXMLRowImportPropertyMapper()
+{
+}
+
+void ScXMLRowImportPropertyMapper::finished(::std::vector< XMLPropertyState >& rProperties, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) const
+{
+ SvXMLImportPropertyMapper::finished(rProperties, nStartIndex, nEndIndex);
+ XMLPropertyState* pHeight(nullptr);
+ XMLPropertyState* pOptimalHeight(nullptr);
+ XMLPropertyState* pPageBreak(nullptr);
+ for (auto& rProperty : rProperties)
+ {
+ XMLPropertyState* property = &rProperty;
+ if (property->mnIndex != -1)
+ {
+ sal_Int16 nContextID = getPropertySetMapper()->GetEntryContextId(property->mnIndex);
+ switch (nContextID)
+ {
+ case CTF_SC_ROWHEIGHT : pHeight = property; break;
+ case CTF_SC_ROWOPTIMALHEIGHT : pOptimalHeight = property; break;
+ case CTF_SC_ROWBREAKBEFORE : pPageBreak = property; break;
+ }
+ }
+ }
+ if (pPageBreak)
+ {
+ if(!(::cppu::any2bool(pPageBreak->maValue)))
+ pPageBreak->mnIndex = -1;
+ }
+ if (pOptimalHeight)
+ {
+ if (::cppu::any2bool(pOptimalHeight->maValue))
+ {
+ if (pHeight)
+ {
+ // set the stored height, but keep "optimal" flag:
+ // pass the height value as OptimalHeight property (only allowed while loading!)
+ pOptimalHeight->maValue = pHeight->maValue;
+ pHeight->mnIndex = -1;
+ }
+ else
+ pOptimalHeight->mnIndex = -1;
+ }
+ }
+ else if (pHeight)
+ {
+ rProperties.emplace_back(maPropMapper->FindEntryIndex(CTF_SC_ROWOPTIMALHEIGHT), css::uno::Any(false));
+ }
+ // don't access pointers to rProperties elements after push_back!
+}
+
+namespace {
+
+class XMLTableCellPropsContext : public SvXMLPropertySetContext
+{
+ public:
+ XMLTableCellPropsContext(
+ SvXMLImport& rImport, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ sal_uInt32 nFamily,
+ ::std::vector< XMLPropertyState > &rProps,
+ const rtl::Reference < SvXMLImportPropertyMapper > &rMap);
+
+ using SvXMLPropertySetContext::createFastChildContext;
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
+ ::std::vector< XMLPropertyState > &rProperties,
+ const XMLPropertyState& rProp ) override;
+};
+
+}
+
+XMLTableCellPropsContext::XMLTableCellPropsContext(
+ SvXMLImport& rImport, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList >& xAttrList,
+ sal_uInt32 nFamily,
+ ::std::vector< XMLPropertyState > &rProps,
+ const rtl::Reference < SvXMLImportPropertyMapper > &rMap)
+ : SvXMLPropertySetContext( rImport, nElement, xAttrList, nFamily,
+ rProps, rMap )
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTableCellPropsContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
+ ::std::vector< XMLPropertyState > &rProperties,
+ const XMLPropertyState& rProp )
+{
+ // no need for a custom context or indeed a SvXMLTokenMap to grab just the
+ // single attribute ( href ) that we are interested in.
+ // still though, we will check namespaces etc.
+ if (nElement == XML_ELEMENT(STYLE, XML_HYPERLINK) ||
+ nElement == XML_ELEMENT(LO_EXT, XML_HYPERLINK) )
+ {
+ OUString sURL;
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ if ( aIter.getToken() == XML_ELEMENT(XLINK, XML_HREF) )
+ sURL = aIter.toString();
+ else
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ if ( !sURL.isEmpty() )
+ {
+ XMLPropertyState aProp( rProp );
+ aProp.maValue <<= sURL;
+ rProperties.push_back( aProp );
+ }
+ }
+ return SvXMLPropertySetContext::createFastChildContext( nElement, xAttrList, rProperties, rProp );
+}
+
+namespace {
+
+class ScXMLMapContext : public SvXMLImportContext
+{
+ OUString msApplyStyle;
+ OUString msCondition;
+ OUString msBaseCell;
+
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+public:
+
+ ScXMLMapContext(
+ SvXMLImport& rImport, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList );
+
+ ScCondFormatEntry* CreateConditionEntry();
+};
+
+}
+
+ScXMLMapContext::ScXMLMapContext(SvXMLImport& rImport, sal_Int32 /*nElement*/,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+ : SvXMLImportContext( rImport )
+{
+ for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
+ {
+ OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(STYLE, XML_CONDITION):
+ msCondition = sValue;
+ break;
+ case XML_ELEMENT(STYLE, XML_APPLY_STYLE_NAME):
+ msApplyStyle = GetImport().GetStyleDisplayName( XmlStyleFamily::TABLE_CELL, sValue);
+ break;
+ case XML_ELEMENT(STYLE, XML_BASE_CELL_ADDRESS):
+ msBaseCell = sValue;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ }
+}
+
+ScCondFormatEntry* ScXMLMapContext::CreateConditionEntry()
+{
+ OUString aCondition, aConditionNmsp;
+ FormulaGrammar::Grammar eGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
+ GetScImport().ExtractFormulaNamespaceGrammar( aCondition, aConditionNmsp, eGrammar, msCondition );
+ bool bHasNmsp = aCondition.getLength() < msCondition.getLength();
+
+ // parse a condition from the attribute string
+ ScXMLConditionParseResult aParseResult;
+ ScXMLConditionHelper::parseCondition( aParseResult, aCondition, 0 );
+
+ if( !bHasNmsp )
+ {
+ // the attribute does not contain a namespace: try to find a namespace of an external grammar
+ FormulaGrammar::Grammar eNewGrammar = FormulaGrammar::GRAM_UNSPECIFIED;
+ GetScImport().ExtractFormulaNamespaceGrammar( aCondition, aConditionNmsp, eNewGrammar, aCondition, true );
+ if( eNewGrammar != FormulaGrammar::GRAM_EXTERNAL )
+ eGrammar = eNewGrammar;
+ }
+
+ ScConditionMode eMode = ScConditionEntry::GetModeFromApi(aParseResult.meOperator);
+ ScDocument* pDoc = GetScImport().GetDocument();
+
+ ScCondFormatEntry* pEntry = new ScCondFormatEntry(eMode, aParseResult.maOperand1, aParseResult.maOperand2, *pDoc, ScAddress(), msApplyStyle,
+ OUString(), OUString(), eGrammar, eGrammar);
+
+ pEntry->SetSrcString(msBaseCell);
+ return pEntry;
+}
+
+void XMLTableStyleContext::SetAttribute( sal_Int32 nElement,
+ const OUString& rValue )
+{
+ switch(nElement & TOKEN_MASK)
+ {
+ case XML_DATA_STYLE_NAME:
+ sDataStyleName = rValue;
+ break;
+ case XML_MASTER_PAGE_NAME:
+ sPageStyle = rValue;
+ break;
+ default:
+ XMLPropStyleContext::SetAttribute( nElement, rValue );
+ }
+}
+
+
+XMLTableStyleContext::XMLTableStyleContext( ScXMLImport& rImport,
+ SvXMLStylesContext& rStyles, XmlStyleFamily nFamily, bool bDefaultStyle ) :
+ XMLPropStyleContext( rImport, rStyles, nFamily, bDefaultStyle ),
+ pStyles(&rStyles),
+ nNumberFormat(-1),
+ nLastSheet(-1),
+ bParentSet(false),
+ mpCondFormat(nullptr),
+ mbDeleteCondFormat(true)
+{
+}
+
+XMLTableStyleContext::~XMLTableStyleContext()
+{
+ if(mbDeleteCondFormat)
+ delete mpCondFormat;
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler > XMLTableStyleContext::createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
+{
+ css::uno::Reference< css::xml::sax::XFastContextHandler > xContext;
+
+ if( nElement == XML_ELEMENT(STYLE, XML_MAP) )
+ {
+ if(!mpCondFormat)
+ mpCondFormat = new ScConditionalFormat( 0, GetScImport().GetDocument() );
+ ScXMLMapContext* pMapContext = new ScXMLMapContext(GetImport(), nElement, xAttrList);
+ xContext = pMapContext;
+ mpCondFormat->AddEntry(pMapContext->CreateConditionEntry());
+ }
+ else if ( nElement == XML_ELEMENT(STYLE, XML_TABLE_CELL_PROPERTIES) )
+ {
+ rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap =
+ GetStyles()->GetImportPropertyMapper(
+ GetFamily() );
+ if( xImpPrMap.is() )
+ xContext = new XMLTableCellPropsContext( GetImport(), nElement,
+ xAttrList,
+ XML_TYPE_PROP_TABLE_CELL,
+ GetProperties(),
+ xImpPrMap );
+ }
+
+ if (!xContext)
+ xContext = XMLPropStyleContext::createFastChildContext( nElement, xAttrList );
+ return xContext;
+}
+
+void XMLTableStyleContext::ApplyCondFormat( const uno::Sequence<table::CellRangeAddress>& xCellRanges )
+{
+ if(!mpCondFormat || GetScImport().HasNewCondFormatData())
+ return;
+
+ ScRangeList aRangeList;
+ for(const table::CellRangeAddress& aAddress : xCellRanges)
+ {
+ ScRange aRange( aAddress.StartColumn, aAddress.StartRow, aAddress.Sheet, aAddress.EndColumn, aAddress.EndRow, aAddress.Sheet );
+ aRangeList.Join( aRange );
+ }
+
+ ScDocument* pDoc = GetScImport().GetDocument();
+ SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
+ ScConditionalFormatList* pFormatList = pDoc->GetCondFormList(nTab);
+ auto itr = std::find_if(pFormatList->begin(), pFormatList->end(),
+ [this](const std::unique_ptr<ScConditionalFormat>& rxFormat) { return rxFormat->EqualEntries(*mpCondFormat); });
+ if (itr != pFormatList->end())
+ {
+ ScRangeList& rRangeList = (*itr)->GetRangeList();
+ sal_uInt32 nCondId = (*itr)->GetKey();
+ size_t n = aRangeList.size();
+ for(size_t i = 0; i < n; ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ rRangeList.Join(rRange);
+ }
+
+ pDoc->AddCondFormatData( aRangeList, nTab, nCondId );
+ return;
+ }
+
+ if(mpCondFormat && mbDeleteCondFormat)
+ {
+ sal_uLong nIndex = pDoc->AddCondFormat(std::unique_ptr<ScConditionalFormat>(mpCondFormat), nTab );
+ mpCondFormat->SetKey(nIndex);
+ mpCondFormat->SetRange(aRangeList);
+
+ pDoc->AddCondFormatData( aRangeList, nTab, nIndex );
+ mbDeleteCondFormat = false;
+ }
+
+}
+
+void XMLTableStyleContext::FillPropertySet(
+ const uno::Reference< XPropertySet > & rPropSet )
+{
+ if (!IsDefaultStyle())
+ {
+ if (GetFamily() == XmlStyleFamily::TABLE_CELL)
+ {
+ if (!bParentSet)
+ {
+ AddProperty(CTF_SC_CELLSTYLE, uno::Any(GetImport().GetStyleDisplayName( XmlStyleFamily::TABLE_CELL, GetParentName() )));
+ bParentSet = true;
+ }
+ sal_Int32 nNumFmt = GetNumberFormat();
+ if (nNumFmt >= 0)
+ AddProperty(CTF_SC_NUMBERFORMAT, uno::Any(nNumFmt));
+ }
+ else if (GetFamily() == XmlStyleFamily::TABLE_TABLE)
+ {
+ if (!sPageStyle.isEmpty())
+ AddProperty(CTF_SC_MASTERPAGENAME, uno::Any(GetImport().GetStyleDisplayName( XmlStyleFamily::MASTER_PAGE, sPageStyle )));
+ }
+ }
+ XMLPropStyleContext::FillPropertySet(rPropSet);
+}
+
+void XMLTableStyleContext::SetDefaults()
+{
+ if ((GetFamily() == XmlStyleFamily::TABLE_CELL) && GetImport().GetModel().is())
+ {
+ uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetImport().GetModel(), uno::UNO_QUERY);
+ if (xMultiServiceFactory.is())
+ {
+ uno::Reference <beans::XPropertySet> xProperties(xMultiServiceFactory->createInstance("com.sun.star.sheet.Defaults"), uno::UNO_QUERY);
+ if (xProperties.is())
+ FillPropertySet(xProperties);
+ }
+ }
+}
+
+void XMLTableStyleContext::AddProperty(const sal_Int16 nContextID, const uno::Any& rValue)
+{
+ XMLPropertyState* property = FindProperty(nContextID);
+ if (property)
+ property->mnIndex = -1; // #i46996# remove old property, so it isn't double
+ sal_Int32 nIndex(static_cast<XMLTableStylesContext *>(pStyles)->GetIndex(nContextID));
+ OSL_ENSURE(nIndex != -1, "Property not found in Map");
+ XMLPropertyState aPropState(nIndex, rValue);
+ GetProperties().push_back(aPropState); // has to be inserted in a sort order later
+}
+
+XMLPropertyState* XMLTableStyleContext::FindProperty(const sal_Int16 nContextID)
+{
+ XMLPropertyState* pRet = nullptr;
+ rtl::Reference < XMLPropertySetMapper > xPrMap;
+ rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap =
+ pStyles->GetImportPropertyMapper( GetFamily() );
+ OSL_ENSURE( xImpPrMap.is(), "There is the import prop mapper" );
+ if( xImpPrMap.is() )
+ xPrMap = xImpPrMap->getPropertySetMapper();
+ if( xPrMap.is() )
+ {
+ auto aIter = std::find_if(GetProperties().begin(), GetProperties().end(),
+ [&xPrMap, &nContextID](const XMLPropertyState& rProp) {
+ return rProp.mnIndex != -1 && xPrMap->GetEntryContextId(rProp.mnIndex) == nContextID;
+ });
+ if (aIter != GetProperties().end())
+ pRet = &(*aIter);
+ }
+ return pRet;
+}
+
+sal_Int32 XMLTableStyleContext::GetNumberFormat()
+{
+ if (nNumberFormat < 0 && !sDataStyleName.isEmpty())
+ {
+ const SvXMLNumFormatContext* pStyle = static_cast<const SvXMLNumFormatContext*>(
+ pStyles->FindStyleChildContext(XmlStyleFamily::DATA_STYLE, sDataStyleName, true));
+
+ if (!pStyle)
+ {
+ XMLTableStylesContext* pMyStyles = static_cast<XMLTableStylesContext*>(GetScImport().GetStyles());
+ if (pMyStyles)
+ pStyle = static_cast<const SvXMLNumFormatContext*>(
+ pMyStyles->FindStyleChildContext(XmlStyleFamily::DATA_STYLE, sDataStyleName, true));
+ else
+ {
+ OSL_FAIL("not possible to get style");
+ }
+ }
+ if (pStyle)
+ nNumberFormat = const_cast<SvXMLNumFormatContext*>(pStyle)->GetKey();
+ }
+ return nNumberFormat;
+}
+
+SvXMLStyleContext *XMLTableStylesContext::CreateStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext *pStyle;
+ // use own wrapper for text and paragraph, to record style usage
+ if (nFamily == XmlStyleFamily::TEXT_PARAGRAPH || nFamily == XmlStyleFamily::TEXT_TEXT)
+ pStyle = new ScCellTextStyleContext( GetImport(),*this, nFamily );
+ else
+ pStyle = SvXMLStylesContext::CreateStyleStyleChildContext(
+ nFamily, nElement, xAttrList );
+
+ if (!pStyle)
+ {
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_CELL:
+ case XmlStyleFamily::TABLE_COLUMN:
+ case XmlStyleFamily::TABLE_ROW:
+ case XmlStyleFamily::TABLE_TABLE:
+ pStyle = new XMLTableStyleContext( GetScImport(), *this, nFamily );
+ break;
+ default: break;
+ }
+ }
+
+ return pStyle;
+}
+
+SvXMLStyleContext *XMLTableStylesContext::CreateDefaultStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext *pStyle(SvXMLStylesContext::CreateDefaultStyleStyleChildContext( nFamily, nElement,
+ xAttrList ));
+ if (!pStyle)
+ {
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_CELL:
+ pStyle = new XMLTableStyleContext( GetScImport(), *this, nFamily, true);
+ break;
+ case XmlStyleFamily::SD_GRAPHICS_ID:
+ pStyle = new XMLGraphicsDefaultStyle( GetScImport(), *this);
+ break;
+ default: break;
+ }
+ }
+
+ return pStyle;
+}
+
+constexpr OUStringLiteral gsCellStyleServiceName(u"com.sun.star.style.CellStyle");
+
+XMLTableStylesContext::XMLTableStylesContext( SvXMLImport& rImport,
+ const bool bTempAutoStyles )
+ : SvXMLStylesContext( rImport )
+ , nNumberFormatIndex(-1)
+ , nConditionalFormatIndex(-1)
+ , nCellStyleIndex(-1)
+ , nMasterPageNameIndex(-1)
+ , bAutoStyles(bTempAutoStyles)
+{
+}
+
+XMLTableStylesContext::~XMLTableStylesContext()
+{
+}
+
+void XMLTableStylesContext::endFastElement(sal_Int32 )
+{
+ if (bAutoStyles)
+ GetImport().GetTextImport()->SetAutoStyles( this );
+ else
+ GetScImport().InsertStyles();
+}
+
+rtl::Reference < SvXMLImportPropertyMapper >
+ XMLTableStylesContext::GetImportPropertyMapper(
+ XmlStyleFamily nFamily ) const
+{
+ rtl::Reference < SvXMLImportPropertyMapper > xMapper(SvXMLStylesContext::GetImportPropertyMapper(nFamily));
+
+ if (!xMapper.is())
+ {
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_CELL:
+ {
+ if( !xCellImpPropMapper.is() )
+ {
+ const_cast<XMLTableStylesContext *>(this)->xCellImpPropMapper =
+ new ScXMLCellImportPropertyMapper( GetScImport().GetCellStylesPropertySetMapper(), const_cast<SvXMLImport&>(GetImport()) );
+ xCellImpPropMapper->ChainImportMapper(XMLTextImportHelper::CreateParaExtPropMapper(const_cast<SvXMLImport&>(GetImport())));
+ }
+ xMapper = xCellImpPropMapper;
+ }
+ break;
+ case XmlStyleFamily::TABLE_COLUMN:
+ {
+ if( !xColumnImpPropMapper.is() )
+ const_cast<XMLTableStylesContext *>(this)->xColumnImpPropMapper =
+ new SvXMLImportPropertyMapper( GetScImport().GetColumnStylesPropertySetMapper(), const_cast<SvXMLImport&>(GetImport()) );
+ xMapper = xColumnImpPropMapper;
+ }
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ {
+ if( !xRowImpPropMapper.is() )
+ const_cast<XMLTableStylesContext *>(this)->xRowImpPropMapper =
+ new ScXMLRowImportPropertyMapper( GetScImport().GetRowStylesPropertySetMapper(), const_cast<SvXMLImport&>(GetImport()) );
+ xMapper = xRowImpPropMapper;
+ }
+ break;
+ case XmlStyleFamily::TABLE_TABLE:
+ {
+ if( !xTableImpPropMapper.is() )
+ const_cast<XMLTableStylesContext *>(this)->xTableImpPropMapper =
+ new SvXMLImportPropertyMapper( GetScImport().GetTableStylesPropertySetMapper(), const_cast<SvXMLImport&>(GetImport()) );
+ xMapper = xTableImpPropMapper;
+ }
+ break;
+ default: break;
+ }
+ }
+
+ return xMapper;
+}
+
+uno::Reference < XNameContainer >
+ XMLTableStylesContext::GetStylesContainer( XmlStyleFamily nFamily ) const
+{
+ uno::Reference < XNameContainer > xStyles(SvXMLStylesContext::GetStylesContainer(nFamily));
+ if (!xStyles.is())
+ {
+ OUString sName;
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_TABLE:
+ {
+ if( xTableStyles.is() )
+ xStyles.set(xTableStyles);
+ else
+ sName = "TableStyles";
+ }
+ break;
+ case XmlStyleFamily::TABLE_CELL:
+ {
+ if( xCellStyles.is() )
+ xStyles.set(xCellStyles);
+ else
+ sName = "CellStyles";
+ }
+ break;
+ case XmlStyleFamily::TABLE_COLUMN:
+ {
+ if( xColumnStyles.is() )
+ xStyles.set(xColumnStyles);
+ else
+ sName = "ColumnStyles";
+ }
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ {
+ if( xRowStyles.is() )
+ xStyles.set(xRowStyles);
+ else
+ sName = "RowStyles";
+ }
+ break;
+ default: break;
+ }
+ if( !xStyles.is() && !sName.isEmpty() && GetScImport().GetModel().is() )
+ {
+ uno::Reference< XStyleFamiliesSupplier > xFamiliesSupp(
+ GetScImport().GetModel(), UNO_QUERY );
+ if (xFamiliesSupp.is())
+ {
+ uno::Reference< XNameAccess > xFamilies(xFamiliesSupp->getStyleFamilies());
+
+ try
+ {
+ xStyles.set(xFamilies->getByName( sName ), uno::UNO_QUERY);
+ }
+ catch ( uno::Exception& )
+ {
+ // #i97680# Named table/column/row styles aren't supported, getByName will throw an exception.
+ // For better interoperability, these styles should then be handled as automatic styles.
+ // For now, NULL is returned (and the style is ignored).
+ }
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_TABLE:
+ const_cast<XMLTableStylesContext *>(this)->xTableStyles.set(xStyles);
+ break;
+ case XmlStyleFamily::TABLE_CELL:
+ const_cast<XMLTableStylesContext *>(this)->xCellStyles.set(xStyles);
+ break;
+ case XmlStyleFamily::TABLE_COLUMN:
+ const_cast<XMLTableStylesContext *>(this)->xColumnStyles.set(xStyles);
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ const_cast<XMLTableStylesContext *>(this)->xRowStyles.set(xStyles);
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+
+ return xStyles;
+}
+
+OUString XMLTableStylesContext::GetServiceName( XmlStyleFamily nFamily ) const
+{
+ OUString sServiceName(SvXMLStylesContext::GetServiceName(nFamily));
+ if (sServiceName.isEmpty())
+ {
+ switch( nFamily )
+ {
+ case XmlStyleFamily::TABLE_COLUMN:
+ sServiceName = XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_NAME;
+ break;
+ case XmlStyleFamily::TABLE_ROW:
+ sServiceName = XML_STYLE_FAMILY_TABLE_ROW_STYLES_NAME;
+ break;
+ case XmlStyleFamily::TABLE_CELL:
+ sServiceName = gsCellStyleServiceName;
+ break;
+ case XmlStyleFamily::TABLE_TABLE:
+ sServiceName = XML_STYLE_FAMILY_TABLE_TABLE_STYLES_NAME;
+ break;
+ default: break;
+ }
+ }
+ return sServiceName;
+}
+
+sal_Int32 XMLTableStylesContext::GetIndex(const sal_Int16 nContextID)
+{
+ if (nContextID == CTF_SC_CELLSTYLE)
+ {
+ if (nCellStyleIndex == -1)
+ nCellStyleIndex =
+ GetImportPropertyMapper(XmlStyleFamily::TABLE_CELL)->getPropertySetMapper()->FindEntryIndex(nContextID);
+ return nCellStyleIndex;
+ }
+ else if (nContextID == CTF_SC_NUMBERFORMAT)
+ {
+ if (nNumberFormatIndex == -1)
+ nNumberFormatIndex =
+ GetImportPropertyMapper(XmlStyleFamily::TABLE_CELL)->getPropertySetMapper()->FindEntryIndex(nContextID);
+ return nNumberFormatIndex;
+ }
+ else if (nContextID == CTF_SC_IMPORT_MAP)
+ {
+ if (nConditionalFormatIndex == -1)
+ nConditionalFormatIndex =
+ GetImportPropertyMapper(XmlStyleFamily::TABLE_CELL)->getPropertySetMapper()->FindEntryIndex(nContextID);
+ return nConditionalFormatIndex;
+ }
+ else if (nContextID == CTF_SC_MASTERPAGENAME)
+ {
+ if (nMasterPageNameIndex == -1)
+ nMasterPageNameIndex =
+ GetImportPropertyMapper(XmlStyleFamily::TABLE_TABLE)->getPropertySetMapper()->FindEntryIndex(nContextID);
+ return nMasterPageNameIndex;
+ }
+ else
+ return -1;
+}
+
+
+bool ScXMLMasterStylesContext::InsertStyleFamily( XmlStyleFamily ) const
+{
+ return true;
+}
+
+ScXMLMasterStylesContext::ScXMLMasterStylesContext( SvXMLImport& rImport ) :
+ SvXMLStylesContext( rImport )
+{
+}
+
+ScXMLMasterStylesContext::~ScXMLMasterStylesContext()
+{
+}
+
+SvXMLStyleContext *ScXMLMasterStylesContext::CreateStyleChildContext(
+ sal_Int32 nElement,
+ const uno::Reference< XFastAttributeList > & xAttrList )
+{
+ SvXMLStyleContext *pContext(nullptr);
+
+ if( nElement == XML_ELEMENT(STYLE, XML_MASTER_PAGE) &&
+ InsertStyleFamily( XmlStyleFamily::MASTER_PAGE ) )
+ pContext = new ScMasterPageContext(
+ GetImport(), nElement, xAttrList,
+ !GetImport().GetTextImport()->IsInsertMode() );
+
+ // any other style will be ignored here!
+
+ return pContext;
+}
+
+SvXMLStyleContext *ScXMLMasterStylesContext::CreateStyleStyleChildContext(
+ XmlStyleFamily /* nFamily */,
+ sal_Int32 /* nElement */,
+ const uno::Reference< XFastAttributeList > & /* xAttrList */ )
+{
+ return nullptr;
+}
+
+void ScXMLMasterStylesContext::endFastElement(sal_Int32 )
+{
+ FinishStyles(true);
+}
+
+
+ScMasterPageContext::ScMasterPageContext( SvXMLImport& rImport,
+ sal_Int32 nElement,
+ const uno::Reference< XFastAttributeList > & xAttrList,
+ bool bOverwrite ) :
+ XMLTextMasterPageContext( rImport, nElement, xAttrList, bOverwrite ),
+ bContainsRightHeader(false),
+ bContainsRightFooter(false)
+{
+}
+
+ScMasterPageContext::~ScMasterPageContext()
+{
+}
+
+SvXMLImportContext *ScMasterPageContext::CreateHeaderFooterContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ const bool bFooter,
+ const bool bLeft,
+ const bool bFirst )
+{
+ if (!bLeft)
+ {
+ if (bFooter)
+ bContainsRightFooter = true;
+ else
+ bContainsRightHeader = true;
+ }
+ if (!xPropSet.is())
+ xPropSet.set(GetStyle(), UNO_QUERY );
+ return new XMLTableHeaderFooterContext( GetImport(),
+ nElement,
+ xAttrList,
+ xPropSet,
+ bFooter, bLeft, bFirst );
+}
+
+void ScMasterPageContext::ClearContent(const OUString& rContent)
+{
+ if (!xPropSet.is())
+ xPropSet.set(GetStyle(), UNO_QUERY );
+
+ if (xPropSet.is())
+ {
+ uno::Reference < sheet::XHeaderFooterContent > xHeaderFooterContent(xPropSet->getPropertyValue( rContent ), uno::UNO_QUERY);
+ if (xHeaderFooterContent.is())
+ {
+ xHeaderFooterContent->getLeftText()->setString("");
+ xHeaderFooterContent->getCenterText()->setString("");
+ xHeaderFooterContent->getRightText()->setString("");
+ xPropSet->setPropertyValue( rContent, uno::Any(xHeaderFooterContent) );
+ }
+ }
+}
+
+void ScMasterPageContext::Finish( bool bOverwrite )
+{
+ XMLTextMasterPageContext::Finish(bOverwrite);
+ if (!bContainsRightFooter)
+ ClearContent(SC_UNO_PAGE_RIGHTFTRCON);
+ if (!bContainsRightHeader)
+ ClearContent(SC_UNO_PAGE_RIGHTHDRCON);
+}
+
+ScCellTextStyleContext::ScCellTextStyleContext( SvXMLImport& rImport,
+ SvXMLStylesContext& rStyles, XmlStyleFamily nFamily ) :
+ XMLTextStyleContext( rImport, rStyles, nFamily, false/*bDefaultStyle*/ ),
+ nLastSheet(-1)
+{
+}
+
+ScCellTextStyleContext::~ScCellTextStyleContext()
+{
+}
+
+void ScCellTextStyleContext::FillPropertySet( const uno::Reference<beans::XPropertySet>& xPropSet )
+{
+ XMLTextStyleContext::FillPropertySet( xPropSet );
+
+ ScXMLImport& rXMLImport = GetScImport();
+
+ ScCellTextCursor* pCellImp = comphelper::getFromUnoTunnel<ScCellTextCursor>( xPropSet );
+ if (pCellImp)
+ {
+ ScAddress aPos = pCellImp->GetCellObj().GetPosition();
+ if ( aPos.Tab() != nLastSheet )
+ {
+ ESelection aSel = pCellImp->GetSelection();
+
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetImport().GetModel())->GetSheetSaveData();
+ pSheetData->AddTextStyle( GetName(), aPos, aSel );
+
+ nLastSheet = aPos.Tab();
+ }
+ }
+ else if ( rXMLImport.GetTables().GetCurrentSheet() != nLastSheet )
+ {
+ ScDrawTextCursor* pDrawImp = comphelper::getFromUnoTunnel<ScDrawTextCursor>( xPropSet );
+ if (pDrawImp)
+ {
+ XMLTableShapeImportHelper* pTableShapeImport = static_cast<XMLTableShapeImportHelper*>(GetScImport().GetShapeImport().get());
+ ScXMLAnnotationContext* pAnnotationContext = pTableShapeImport->GetAnnotationContext();
+ if (pAnnotationContext)
+ {
+ pAnnotationContext->AddContentStyle( GetFamily(), GetName(), pDrawImp->GetSelection() );
+ nLastSheet = rXMLImport.GetTables().GetCurrentSheet();
+ }
+ }
+
+ // if it's a different shape, BlockSheet is called from XMLTableShapeImportHelper::finishShape
+ // formatted text in page headers/footers can be ignored
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlstyli.hxx b/sc/source/filter/xml/xmlstyli.hxx
new file mode 100644
index 000000000..48c401658
--- /dev/null
+++ b/sc/source/filter/xml/xmlstyli.hxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <vector>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/prstylei.hxx>
+#include <xmloff/xmlimppr.hxx>
+#include <xmloff/XMLTextMasterPageContext.hxx>
+#include <xmloff/txtstyli.hxx>
+#include "xmlimprt.hxx"
+
+class ScConditionalFormat;
+
+class ScXMLCellImportPropertyMapper : public SvXMLImportPropertyMapper
+{
+protected:
+
+public:
+
+ ScXMLCellImportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper,
+ SvXMLImport& rImport);
+ virtual ~ScXMLCellImportPropertyMapper() override;
+
+ /** This method is called when all attributes have been processed. It may be used to remove items that are incomplete */
+ virtual void finished(
+ ::std::vector< XMLPropertyState >& rProperties, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) const override;
+};
+
+class ScXMLRowImportPropertyMapper : public SvXMLImportPropertyMapper
+{
+protected:
+
+public:
+
+ ScXMLRowImportPropertyMapper(
+ const rtl::Reference< XMLPropertySetMapper >& rMapper,
+ SvXMLImport& rImport);
+ virtual ~ScXMLRowImportPropertyMapper() override;
+
+ /** This method is called when all attributes have been processed. It may be used to remove items that are incomplete */
+ virtual void finished(
+ ::std::vector< XMLPropertyState >& rProperties, sal_Int32 nStartIndex, sal_Int32 nEndIndex ) const override;
+};
+
+class XMLTableStyleContext : public XMLPropStyleContext
+{
+ OUString sDataStyleName;
+ OUString sPageStyle;
+ SvXMLStylesContext* pStyles;
+ sal_Int32 nNumberFormat;
+ SCTAB nLastSheet;
+ bool bParentSet;
+ ScConditionalFormat* mpCondFormat;
+ bool mbDeleteCondFormat;
+
+ const ScXMLImport& GetScImport() const { return static_cast<const ScXMLImport&>(GetImport()); }
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+
+protected:
+
+ virtual void SetAttribute( sal_Int32 nElement,
+ const OUString& rValue ) override;
+
+public:
+
+ XMLTableStyleContext( ScXMLImport& rImport,
+ SvXMLStylesContext& rStyles, XmlStyleFamily nFamily, bool bDefaultStyle = false );
+ virtual ~XMLTableStyleContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
+
+ virtual void FillPropertySet(const css::uno::Reference<
+ css::beans::XPropertySet > & rPropSet ) override;
+
+ virtual void SetDefaults() override;
+
+ void AddProperty(sal_Int16 nContextID, const css::uno::Any& aValue);
+ XMLPropertyState* FindProperty(const sal_Int16 nContextID);
+
+ sal_Int32 GetNumberFormat();// { return nNumberFormat; }
+
+ SCTAB GetLastSheet() const { return nLastSheet; }
+ void SetLastSheet(SCTAB nNew) { nLastSheet = nNew; }
+
+ void ApplyCondFormat( const css::uno::Sequence<css::table::CellRangeAddress>& xCellRanges );
+
+private:
+ using XMLPropStyleContext::SetStyle;
+};
+
+class XMLTableStylesContext : public SvXMLStylesContext
+{
+ css::uno::Reference< css::container::XNameContainer > xCellStyles;
+ css::uno::Reference< css::container::XNameContainer > xColumnStyles;
+ css::uno::Reference< css::container::XNameContainer > xRowStyles;
+ css::uno::Reference< css::container::XNameContainer > xTableStyles;
+ sal_Int32 nNumberFormatIndex;
+ sal_Int32 nConditionalFormatIndex;
+ sal_Int32 nCellStyleIndex;
+ sal_Int32 nMasterPageNameIndex;
+ bool bAutoStyles;
+
+ rtl::Reference < SvXMLImportPropertyMapper > xCellImpPropMapper;
+ rtl::Reference < SvXMLImportPropertyMapper > xColumnImpPropMapper;
+ rtl::Reference < SvXMLImportPropertyMapper > xRowImpPropMapper;
+ rtl::Reference < SvXMLImportPropertyMapper > xTableImpPropMapper;
+
+ const ScXMLImport& GetScImport() const { return static_cast<const ScXMLImport&>(GetImport()); }
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+
+protected:
+
+ // Create a style context.
+ using SvXMLStylesContext::CreateStyleStyleChildContext;
+ virtual SvXMLStyleContext *CreateStyleStyleChildContext(
+ XmlStyleFamily nFamily,
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ using SvXMLStylesContext::CreateDefaultStyleStyleChildContext;
+ virtual SvXMLStyleContext *CreateDefaultStyleStyleChildContext(
+ XmlStyleFamily nFamily, sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+public:
+
+ XMLTableStylesContext( SvXMLImport& rImport, bool bAutoStyles );
+ virtual ~XMLTableStylesContext() override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+
+ virtual rtl::Reference < SvXMLImportPropertyMapper > GetImportPropertyMapper(
+ XmlStyleFamily nFamily ) const override;
+ virtual css::uno::Reference< css::container::XNameContainer >
+ GetStylesContainer( XmlStyleFamily nFamily ) const override;
+ virtual OUString GetServiceName( XmlStyleFamily nFamily ) const override;
+
+ sal_Int32 GetIndex(const sal_Int16 nContextID);
+};
+
+class ScXMLMasterStylesContext : public SvXMLStylesContext
+{
+protected:
+ virtual SvXMLStyleContext *CreateStyleChildContext( sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ using SvXMLStylesContext::CreateStyleStyleChildContext;
+ virtual SvXMLStyleContext *CreateStyleStyleChildContext( XmlStyleFamily nFamily,
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+ virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override;
+
+public:
+
+ ScXMLMasterStylesContext( SvXMLImport& rImport );
+
+ virtual ~ScXMLMasterStylesContext() override;
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class ScMasterPageContext : public XMLTextMasterPageContext
+{
+ css::uno::Reference<css::beans::XPropertySet> xPropSet;
+ bool bContainsRightHeader;
+ bool bContainsRightFooter;
+
+ void ClearContent(const OUString& rContent);
+public:
+
+
+ ScMasterPageContext( SvXMLImport& rImport, sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ bool bOverwrite );
+ virtual ~ScMasterPageContext() override;
+
+ virtual SvXMLImportContext *CreateHeaderFooterContext(
+ sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList,
+ const bool bFooter,
+ const bool bLeft,
+ const bool bFirst ) override;
+
+ virtual void Finish( bool bOverwrite ) override;
+};
+
+class ScCellTextStyleContext : public XMLTextStyleContext
+{
+ sal_Int32 nLastSheet;
+
+ const ScXMLImport& GetScImport() const { return static_cast<const ScXMLImport&>(GetImport()); }
+ ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
+
+public:
+ ScCellTextStyleContext( SvXMLImport& rImport,
+ SvXMLStylesContext& rStyles, XmlStyleFamily nFamily );
+ virtual ~ScCellTextStyleContext() override;
+
+ // override FillPropertySet to store style information
+ virtual void FillPropertySet(
+ const css::uno::Reference< css::beans::XPropertySet > & rPropSet ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsubti.cxx b/sc/source/filter/xml/xmlsubti.cxx
new file mode 100644
index 000000000..a20b74a32
--- /dev/null
+++ b/sc/source/filter/xml/xmlsubti.cxx
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmlsubti.hxx"
+#include "xmlstyli.hxx"
+#include "xmlimprt.hxx"
+#include <document.hxx>
+#include "XMLConverter.hxx"
+#include <docuno.hxx>
+#include "XMLStylesImportHelper.hxx"
+#include <sheetdata.hxx>
+#include <tabprotection.hxx>
+#include <tokenarray.hxx>
+#include <documentimport.hxx>
+
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+
+using namespace com::sun::star;
+
+ScXMLTabProtectionData::ScXMLTabProtectionData() :
+ meHash1(PASSHASH_SHA1),
+ meHash2(PASSHASH_UNSPECIFIED),
+ mbProtected(false),
+ mbSelectProtectedCells(true),
+ mbSelectUnprotectedCells(true),
+ mbInsertColumns(false),
+ mbInsertRows(false),
+ mbDeleteColumns(false),
+ mbDeleteRows(false)
+{
+}
+
+ScMyTables::ScMyTables(ScXMLImport& rTempImport)
+ : rImport(rTempImport),
+ aFixupOLEs(rTempImport),
+ maCurrentCellPos(ScAddress::INITIALIZE_INVALID),
+ nCurrentColCount(0),
+ nCurrentDrawPage( -1 ),
+ nCurrentXShapes( -1 )
+{
+}
+
+ScMyTables::~ScMyTables()
+{
+}
+
+namespace {
+
+uno::Reference<sheet::XSpreadsheet> getCurrentSheet(const uno::Reference<frame::XModel>& xModel, SCTAB nSheet)
+{
+ uno::Reference<sheet::XSpreadsheet> xSheet;
+ uno::Reference<sheet::XSpreadsheetDocument> xSpreadDoc(xModel, uno::UNO_QUERY);
+ if (!xSpreadDoc.is())
+ return xSheet;
+
+ uno::Reference <sheet::XSpreadsheets> xSheets(xSpreadDoc->getSheets());
+ if (!xSheets.is())
+ return xSheet;
+
+ uno::Reference <container::XIndexAccess> xIndex(xSheets, uno::UNO_QUERY);
+ if (!xIndex.is())
+ return xSheet;
+
+ xSheet.set(xIndex->getByIndex(nSheet), uno::UNO_QUERY);
+ return xSheet;
+}
+
+}
+
+void ScMyTables::NewSheet(const OUString& sTableName, const OUString& sStyleName,
+ const ScXMLTabProtectionData& rProtectData)
+{
+ if (!rImport.GetModel().is())
+ return;
+
+ nCurrentColCount = 0;
+ sCurrentSheetName = sTableName;
+ //reset cols and rows for new sheet, but increment tab
+ maCurrentCellPos.SetCol(-1);
+ maCurrentCellPos.SetRow(-1);
+ maCurrentCellPos.SetTab(maCurrentCellPos.Tab() + 1);
+
+ maProtectionData = rProtectData;
+ ScDocument *pDoc = ScXMLConverter::GetScDocument(rImport.GetModel());
+
+ // The document contains one sheet when created. So for the first
+ // sheet, we only need to set its name.
+ if (maCurrentCellPos.Tab() > 0)
+ pDoc->AppendTabOnLoad(sTableName);
+ else
+ pDoc->SetTabNameOnLoad(maCurrentCellPos.Tab(), sTableName);
+
+ xCurrentSheet = getCurrentSheet(rImport.GetModel(), maCurrentCellPos.Tab());
+ if (xCurrentSheet.is())
+ {
+ // We need to set the current cell range here regardless of
+ // presence of style name.
+ SetTableStyle(sStyleName);
+ }
+}
+
+void ScMyTables::SetTableStyle(const OUString& sStyleName)
+{
+ //these uno calls are a bit difficult to remove, XMLTableStyleContext::FillPropertySet uses
+ //SvXMLImportPropertyMapper::FillPropertySet
+ if ( sStyleName.isEmpty() )
+ return;
+
+ // #i57869# All table style properties for all sheets are now applied here,
+ // before importing the contents.
+ // This is needed for the background color.
+ // Sheet visibility has special handling in ScDocFunc::SetTableVisible to
+ // allow hiding the first sheet.
+ // RTL layout is only remembered, not actually applied, so the shapes can
+ // be loaded before mirroring.
+
+ if ( !xCurrentSheet.is() )
+ return;
+
+ uno::Reference <beans::XPropertySet> xProperties(xCurrentSheet, uno::UNO_QUERY);
+ if ( !xProperties.is() )
+ return;
+
+ XMLTableStylesContext *pStyles = static_cast<XMLTableStylesContext *>(rImport.GetAutoStyles());
+ if ( pStyles )
+ {
+ XMLTableStyleContext* pStyle = const_cast<XMLTableStyleContext*>(static_cast<const XMLTableStyleContext *>(pStyles->FindStyleChildContext(
+ XmlStyleFamily::TABLE_TABLE, sStyleName, true)));
+ if ( pStyle )
+ {
+ pStyle->FillPropertySet(xProperties);
+
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(rImport.GetModel())->GetSheetSaveData();
+ pSheetData->AddTableStyle( sStyleName, ScAddress( 0, 0, maCurrentCellPos.Tab() ) );
+ }
+ }
+}
+
+void ScMyTables::AddRow()
+{
+ maCurrentCellPos.SetRow(maCurrentCellPos.Row() + 1);
+ maCurrentCellPos.SetCol(-1); //reset columns for new row
+}
+
+void ScMyTables::SetRowStyle(const OUString& rCellStyleName)
+{
+ rImport.GetStylesImportHelper()->SetRowStyle(rCellStyleName);
+}
+
+void ScMyTables::AddColumn(bool bIsCovered)
+{
+ maCurrentCellPos.SetCol( maCurrentCellPos.Col() + 1 );
+ //here only need to set column style if this is the first row and
+ //the cell is not covered.
+ if(maCurrentCellPos.Row() == 0 && !bIsCovered)
+ rImport.GetStylesImportHelper()->InsertCol(maCurrentCellPos.Col(), maCurrentCellPos.Tab());
+}
+
+void ScMyTables::DeleteTable()
+{
+ ScXMLImport::MutexGuard aGuard(rImport);
+
+ rImport.GetStylesImportHelper()->SetStylesToRanges();
+ rImport.SetStylesToRangesFinished();
+
+ maMatrixRangeList.RemoveAll();
+
+ if (!(rImport.GetDocument() && maProtectionData.mbProtected))
+ return;
+
+ uno::Sequence<sal_Int8> aHash;
+ ::comphelper::Base64::decode(aHash, maProtectionData.maPassword);
+
+ ScTableProtection aProtect;
+ aProtect.setProtected(maProtectionData.mbProtected);
+ aProtect.setPasswordHash(aHash, maProtectionData.meHash1, maProtectionData.meHash2);
+ aProtect.setOption(ScTableProtection::SELECT_LOCKED_CELLS, maProtectionData.mbSelectProtectedCells);
+ aProtect.setOption(ScTableProtection::SELECT_UNLOCKED_CELLS, maProtectionData.mbSelectUnprotectedCells);
+ aProtect.setOption(ScTableProtection::INSERT_COLUMNS, maProtectionData.mbInsertColumns);
+ aProtect.setOption(ScTableProtection::INSERT_ROWS, maProtectionData.mbInsertRows);
+ aProtect.setOption(ScTableProtection::DELETE_COLUMNS, maProtectionData.mbDeleteColumns);
+ aProtect.setOption(ScTableProtection::DELETE_ROWS, maProtectionData.mbDeleteRows);
+ rImport.GetDocument()->SetTabProtection(maCurrentCellPos.Tab(), &aProtect);
+}
+
+void ScMyTables::AddColStyle(const sal_Int32 nRepeat, const OUString& rCellStyleName)
+{
+ rImport.GetStylesImportHelper()->AddColumnStyle(rCellStyleName, nCurrentColCount, nRepeat);
+ nCurrentColCount += nRepeat;
+ SAL_WARN_IF(nCurrentColCount > rImport.GetDocument()->GetSheetLimits().GetMaxColCount(),
+ "sc", "more columns than fit into SCCOL");
+ nCurrentColCount = std::min<sal_Int32>( nCurrentColCount, rImport.GetDocument()->GetSheetLimits().GetMaxColCount() );
+}
+
+uno::Reference< drawing::XDrawPage > const & ScMyTables::GetCurrentXDrawPage()
+{
+ if( (maCurrentCellPos.Tab() != nCurrentDrawPage) || !xDrawPage.is() )
+ {
+ uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier( xCurrentSheet, uno::UNO_QUERY );
+ if( xDrawPageSupplier.is() )
+ xDrawPage.set(xDrawPageSupplier->getDrawPage());
+ nCurrentDrawPage = sal::static_int_cast<sal_Int16>(maCurrentCellPos.Tab());
+ }
+ return xDrawPage;
+}
+
+uno::Reference< drawing::XShapes > const & ScMyTables::GetCurrentXShapes()
+{
+ if( (maCurrentCellPos.Tab() != nCurrentXShapes) || !xShapes.is() )
+ {
+ xShapes = GetCurrentXDrawPage();
+ rImport.GetShapeImport()->startPage(xShapes);
+ rImport.GetShapeImport()->pushGroupForPostProcessing ( xShapes );
+ nCurrentXShapes = sal::static_int_cast<sal_Int16>(maCurrentCellPos.Tab());
+ }
+ return xShapes;
+}
+
+bool ScMyTables::HasDrawPage() const
+{
+ return (maCurrentCellPos.Tab() == nCurrentDrawPage) && xDrawPage.is();
+}
+
+bool ScMyTables::HasXShapes() const
+{
+ return (maCurrentCellPos.Tab() == nCurrentXShapes) && xShapes.is();
+}
+
+void ScMyTables::AddOLE(const uno::Reference <drawing::XShape>& rShape,
+ const OUString &rRangeList)
+{
+ aFixupOLEs.AddOLE(rShape, rRangeList);
+}
+
+void ScMyTables::AddMatrixRange(
+ const SCCOL nStartColumn, const SCROW nStartRow, const SCCOL nEndColumn, const SCROW nEndRow,
+ const OUString& rFormula, const OUString& rFormulaNmsp, const formula::FormulaGrammar::Grammar eGrammar)
+{
+ OSL_ENSURE(nEndRow >= nStartRow, "wrong row order");
+ OSL_ENSURE(nEndColumn >= nStartColumn, "wrong column order");
+ ScRange aScRange(
+ nStartColumn, nStartRow, maCurrentCellPos.Tab(),
+ nEndColumn, nEndRow, maCurrentCellPos.Tab()
+ );
+
+ maMatrixRangeList.push_back(aScRange);
+
+ ScDocumentImport& rDoc = rImport.GetDoc();
+ ScTokenArray aCode(rDoc.getDoc());
+ aCode.AssignXMLString( rFormula,
+ ((eGrammar == formula::FormulaGrammar::GRAM_EXTERNAL) ? rFormulaNmsp : OUString()));
+ rDoc.setMatrixCells(aScRange, aCode, eGrammar);
+ rDoc.getDoc().IncXMLImportedFormulaCount( rFormula.getLength() );
+}
+
+bool ScMyTables::IsPartOfMatrix(const ScAddress& rScAddress) const
+{
+ if (!maMatrixRangeList.empty())
+ return maMatrixRangeList.Contains(rScAddress);
+ return false;
+}
+
+SCCOL ScMyTables::GetCurrentColCount() const
+{
+ return std::min<sal_Int32>(nCurrentColCount, rImport.GetDocument()->MaxCol());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlsubti.hxx b/sc/source/filter/xml/xmlsubti.hxx
new file mode 100644
index 000000000..b0c6e7457
--- /dev/null
+++ b/sc/source/filter/xml/xmlsubti.hxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include "XMLTableShapeResizer.hxx"
+#include <formula/grammar.hxx>
+#include <tabprotection.hxx>
+#include <rangelst.hxx>
+
+namespace com::sun::star::drawing { class XDrawPage; }
+namespace com::sun::star::sheet { class XSpreadsheet; }
+namespace com::sun::star::table { class XCellRange; }
+namespace com::sun::star::drawing { class XShapes; }
+
+class ScXMLImport;
+
+struct ScXMLTabProtectionData
+{
+ OUString maPassword;
+ ScPasswordHash meHash1;
+ ScPasswordHash meHash2;
+ bool mbProtected;
+ bool mbSelectProtectedCells;
+ bool mbSelectUnprotectedCells;
+ bool mbInsertColumns;
+ bool mbInsertRows;
+ bool mbDeleteColumns;
+ bool mbDeleteRows;
+
+ ScXMLTabProtectionData();
+};
+
+class ScMyTables
+{
+private:
+ ScXMLImport& rImport;
+
+ ScMyOLEFixer aFixupOLEs;
+
+ css::uno::Reference< css::sheet::XSpreadsheet > xCurrentSheet;
+ css::uno::Reference< css::drawing::XDrawPage > xDrawPage;
+ css::uno::Reference < css::drawing::XShapes > xShapes;
+ OUString sCurrentSheetName;
+ ScAddress maCurrentCellPos;
+ ScRangeList maMatrixRangeList;
+ ScXMLTabProtectionData maProtectionData;
+ sal_Int32 nCurrentColCount;
+ sal_Int16 nCurrentDrawPage;
+ sal_Int16 nCurrentXShapes;
+
+ void SetTableStyle(const OUString& sStyleName);
+public:
+ explicit ScMyTables(ScXMLImport& rImport);
+ ~ScMyTables();
+ void NewSheet(const OUString& sTableName, const OUString& sStyleName,
+ const ScXMLTabProtectionData& rProtectData);
+ void AddRow();
+ void SetRowStyle(const OUString& rCellStyleName);
+ void AddColumn(bool bIsCovered);
+ void FixupOLEs() { aFixupOLEs.FixupOLEs(); }
+ static bool IsOLE(const css::uno::Reference< css::drawing::XShape >& rShape)
+ { return ScMyOLEFixer::IsOLE(rShape); }
+ void DeleteTable();
+ const ScAddress& GetCurrentCellPos() const { return maCurrentCellPos; };
+ void AddColStyle(const sal_Int32 nRepeat, const OUString& rCellStyleName);
+ ScXMLTabProtectionData& GetCurrentProtectionData() { return maProtectionData; }
+ const OUString& GetCurrentSheetName() const { return sCurrentSheetName; }
+ SCTAB GetCurrentSheet() const { return (maCurrentCellPos.Tab() >= 0) ? maCurrentCellPos.Tab() : 0; }
+ SCCOL GetCurrentColCount() const;
+ SCROW GetCurrentRow() const { return (maCurrentCellPos.Row() >= 0) ? maCurrentCellPos.Row() : 0; }
+ const css::uno::Reference< css::sheet::XSpreadsheet >&
+ GetCurrentXSheet() const { return xCurrentSheet; }
+ css::uno::Reference< css::drawing::XDrawPage > const &
+ GetCurrentXDrawPage();
+ css::uno::Reference< css::drawing::XShapes > const &
+ GetCurrentXShapes();
+ bool HasDrawPage() const;
+ bool HasXShapes() const;
+ void AddOLE(const css::uno::Reference <css::drawing::XShape>& rShape,
+ const OUString &rRangeList);
+
+ void AddMatrixRange( const SCCOL nStartColumn,
+ const SCROW nStartRow,
+ const SCCOL nEndColumn,
+ const SCROW nEndRow,
+ const OUString& rFormula,
+ const OUString& rFormulaNmsp,
+ const formula::FormulaGrammar::Grammar );
+ bool IsPartOfMatrix( const ScAddress& rScAddress) const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmltabi.cxx b/sc/source/filter/xml/xmltabi.cxx
new file mode 100644
index 000000000..217e08beb
--- /dev/null
+++ b/sc/source/filter/xml/xmltabi.cxx
@@ -0,0 +1,470 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "xmltabi.hxx"
+#include "xmlimprt.hxx"
+#include "xmlrowi.hxx"
+#include "xmlcoli.hxx"
+#include "xmlsceni.hxx"
+#include "xmlexternaltabi.hxx"
+#include "xmlnexpi.hxx"
+#include <document.hxx>
+#include <docuno.hxx>
+#include <olinetab.hxx>
+#include "XMLTableShapesContext.hxx"
+#include "XMLTableSourceContext.hxx"
+#include "XMLStylesImportHelper.hxx"
+#include <rangeutl.hxx>
+#include <externalrefmgr.hxx>
+#include <sheetdata.hxx>
+#include "xmlcondformat.hxx"
+#include "SparklineGroupsImportContext.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/XMLEventsImportContext.hxx>
+
+#include <tools/urlobj.hxx>
+#include <sax/fastattribs.hxx>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <comphelper/servicehelper.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::xml::sax::XAttributeList;
+
+/**
+ * Determine whether this table is an external reference cache from its
+ * name. There is currently no way of determining whether a table is a
+ * regular table or an external reference cache other than examining the
+ * name itself. We should probably introduce a new boolean value for
+ * table:table element and use it instead of doing this, to make it more
+ * reliable and future-proof.
+ *
+ * @param rName
+ *
+ * @return
+ */
+static bool lcl_isExternalRefCache(const OUString& rName, OUString& rUrl, OUString& rExtTabName)
+{
+ // 'file:///path/to/file.ods'#MySheet
+ // 'file:///path/to/file.ods'#MySheet with space
+ // 'file:///path/to/file's.ods'#Sheet (Notice the quote in the file name.
+ // That's allowed.)
+
+ if ( rName.toChar() != '\'' ) // initial quote
+ return false;
+
+ // #i114504# Other schemes besides "file:" are also allowed.
+ // CompareProtocolScheme is quick, only looks at the start of the string.
+ INetProtocol eProt = INetURLObject::CompareProtocolScheme( rName.subView(1) );
+ if ( eProt == INetProtocol::NotValid )
+ return false;
+
+ OUString aPrefix = INetURLObject::GetScheme( eProt );
+ sal_Int32 nPrefLen = aPrefix.getLength();
+
+ OUStringBuffer aUrlBuf, aTabNameBuf;
+ aUrlBuf.append( aPrefix );
+ sal_Int32 n = rName.getLength();
+ const sal_Unicode* p = rName.getStr();
+
+ bool bInUrl = true;
+ sal_Unicode cPrev = 0;
+ for (sal_Int32 i = nPrefLen+1; i < n; ++i) // start the loop after quote and prefix
+ {
+ const sal_Unicode c = p[i];
+ if (bInUrl)
+ {
+ // parsing file URL
+ if (c == '#')
+ {
+ if (cPrev != '\'')
+ return false;
+
+ rUrl = aUrlBuf.makeStringAndClear();
+ rUrl = rUrl.copy(0, rUrl.getLength()-1); // remove the trailing single-quote.
+ bInUrl = false;
+ }
+ else
+ aUrlBuf.append(c);
+ }
+ else
+ // parsing sheet name.
+ aTabNameBuf.append(c);
+
+ cPrev = c;
+ }
+
+ if (bInUrl)
+ return false;
+
+ if (aTabNameBuf.isEmpty())
+ return false;
+
+ rExtTabName = aTabNameBuf.makeStringAndClear();
+
+ return true;
+}
+
+ScXMLExternalTabData::ScXMLExternalTabData() :
+ mnRow(0), mnCol(0), mnFileId(0)
+{
+}
+
+ScXMLTableContext::ScXMLTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport ),
+ nStartOffset(-1),
+ bStartFormPage(false),
+ bPrintEntireSheet(true)
+{
+ // get start offset in file (if available)
+ nStartOffset = GetScImport().GetByteOffset();
+
+ ScXMLTabProtectionData aProtectData;
+ OUString sName;
+ OUString sStyleName;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &it : *rAttrList)
+ {
+ switch (it.getToken())
+ {
+ case XML_ELEMENT( TABLE, XML_NAME ):
+ sName = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_STYLE_NAME ):
+ sStyleName = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTED ):
+ aProtectData.mbProtected = IsXMLToken( it, XML_TRUE );
+ break;
+ case XML_ELEMENT( TABLE, XML_PRINT_RANGES ):
+ sPrintRanges = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTION_KEY ):
+ aProtectData.maPassword = it.toString();
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM ):
+ aProtectData.meHash1 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2 ):
+ case XML_ELEMENT( LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2 ):
+ aProtectData.meHash2 = ScPassHashHelper::getHashTypeFromURI( it.toString() );
+ break;
+ case XML_ELEMENT( TABLE, XML_PRINT ):
+ {
+ if ( IsXMLToken( it, XML_FALSE) )
+ bPrintEntireSheet = false;
+ }
+ break;
+ }
+
+ }
+ }
+
+ OUString aExtUrl, aExtTabName;
+ if (lcl_isExternalRefCache(sName, aExtUrl, aExtTabName))
+ {
+ // This is an external ref cache table.
+ pExternalRefInfo.reset(new ScXMLExternalTabData);
+ ScDocument* pDoc = GetScImport().GetDocument();
+ if (pDoc)
+ {
+ ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
+ pExternalRefInfo->mnFileId = pRefMgr->getExternalFileId(aExtUrl);
+ pExternalRefInfo->mpCacheTable = pRefMgr->getCacheTable(pExternalRefInfo->mnFileId, aExtTabName, true,
+ nullptr, &aExtUrl);
+ pExternalRefInfo->mpCacheTable->setWholeTableCached();
+ }
+ }
+ else
+ {
+ // This is a regular table.
+ GetScImport().GetTables().NewSheet(sName, sStyleName, aProtectData);
+ }
+}
+
+ScXMLTableContext::~ScXMLTableContext()
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+ ScXMLTableContext::createFastChildContext( sal_Int32 nElement,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ sax_fastparser::FastAttributeList *pAttribList =
+ &sax_fastparser::castToFastAttributeList( xAttrList );
+
+ if (pExternalRefInfo)
+ {
+ // We only care about the table-row and table-source elements for
+ // external cache data.
+ switch ( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW_GROUP ):
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_ROWS ):
+ case XML_ELEMENT( TABLE, XML_TABLE_ROWS ):
+ // #i101319# don't discard rows in groups or header (repeat range)
+ return new ScXMLExternalRefRowsContext(
+ GetScImport(), *pExternalRefInfo);
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW ):
+ return new ScXMLExternalRefRowContext(
+ GetScImport(), pAttribList, *pExternalRefInfo);
+ case XML_ELEMENT( TABLE, XML_TABLE_SOURCE ):
+ return new ScXMLExternalRefTabSourceContext(
+ GetScImport(), pAttribList, *pExternalRefInfo);
+ default:
+ ;
+ }
+ return nullptr;
+ }
+
+ SvXMLImportContext *pContext(nullptr);
+
+ switch ( nElement )
+ {
+ case XML_ELEMENT( TABLE, XML_NAMED_EXPRESSIONS ):
+ {
+ SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
+ pContext = new ScXMLNamedExpressionsContext(
+ GetScImport(),
+ std::make_shared<ScXMLNamedExpressionsContext::SheetLocalInserter>(GetScImport(), nTab));
+ }
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN_GROUP ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ false, true );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_COLUMNS ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ true, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMNS ):
+ pContext = new ScXMLTableColsContext( GetScImport(), pAttribList,
+ false, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_COLUMN ):
+ pContext = new ScXMLTableColContext( GetScImport(), pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_PROTECTION ):
+ case XML_ELEMENT( LO_EXT, XML_TABLE_PROTECTION ):
+ case XML_ELEMENT( OFFICE_EXT, XML_TABLE_PROTECTION ):
+ pContext = new ScXMLTableProtectionContext( GetScImport(), pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW_GROUP ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ false, true );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_HEADER_ROWS ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ true, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROWS ):
+ pContext = new ScXMLTableRowsContext( GetScImport(), pAttribList,
+ false, false );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_ROW ):
+ pContext = new ScXMLTableRowContext( GetScImport(), pAttribList );
+ break;
+ case XML_ELEMENT( TABLE, XML_TABLE_SOURCE ):
+ pContext = new ScXMLTableSourceContext( GetScImport(), pAttribList);
+ break;
+ case XML_ELEMENT( TABLE, XML_SCENARIO ):
+ pContext = new ScXMLTableScenarioContext( GetScImport(), pAttribList);
+ break;
+ case XML_ELEMENT( TABLE, XML_SHAPES ):
+ pContext = new ScXMLTableShapesContext( GetScImport() );
+ break;
+ case XML_ELEMENT( CALC_EXT, XML_CONDITIONAL_FORMATS ):
+ pContext = new ScXMLConditionalFormatsContext( GetScImport() );
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SPARKLINE_GROUPS):
+ pContext = new sc::SparklineGroupsImportContext(GetScImport());
+ break;
+ case XML_ELEMENT(OFFICE, XML_EVENT_LISTENERS):
+ case XML_ELEMENT(OFFICE_EXT, XML_EVENT_LISTENERS):
+ {
+ // use XEventsSupplier interface of the sheet
+ uno::Reference<document::XEventsSupplier> xSupplier( GetScImport().GetTables().GetCurrentXSheet(), uno::UNO_QUERY );
+ pContext = new XMLEventsImportContext( GetImport(), xSupplier );
+ }
+ break;
+ case XML_ELEMENT(OFFICE, XML_FORMS):
+ {
+ GetScImport().GetFormImport()->startPage(GetScImport().GetTables().GetCurrentXDrawPage());
+ bStartFormPage = true;
+ pContext = xmloff::OFormLayerXMLImport::createOfficeFormsContext( GetScImport() );
+ }
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN_ELEMENT("sc", nElement);
+ break;
+ }
+
+ return pContext;
+}
+
+void SAL_CALL ScXMLTableContext::endFastElement(sal_Int32 /*nElement*/)
+{
+ ScXMLImport::MutexGuard aMutexGuard(GetScImport());
+ ScXMLImport& rImport = GetScImport();
+ rImport.GetStylesImportHelper()->EndTable();
+ ScDocument* pDoc(rImport.GetDocument());
+ if (!pDoc)
+ return;
+
+ ScMyTables& rTables = rImport.GetTables();
+ SCTAB nCurTab = rTables.GetCurrentSheet();
+ // tdf#51022 process only print ranges of internal sheets
+ if (!pExternalRefInfo)
+ {
+ if (!sPrintRanges.isEmpty())
+ {
+ ScRangeList aRangeList;
+ ScRangeStringConverter::GetRangeListFromString(aRangeList, sPrintRanges, *pDoc, ::formula::FormulaGrammar::CONV_OOO);
+ size_t nCount = aRangeList.size();
+ for (size_t i = 0; i < nCount; i++)
+ {
+ pDoc->AddPrintRange(nCurTab, aRangeList[i]);
+ }
+ }
+ else if (!bPrintEntireSheet)
+ // Sheet has "print entire sheet" option by default. Remove it.
+ pDoc->ClearPrintRanges(nCurTab);
+ }
+
+ ScOutlineTable* pOutlineTable(pDoc->GetOutlineTable(nCurTab));
+ if (pOutlineTable)
+ {
+ ScOutlineArray& rColArray(pOutlineTable->GetColArray());
+ size_t nDepth = rColArray.GetDepth();
+ for (size_t i = 0; i < nDepth; ++i)
+ {
+ size_t nCount = rColArray.GetCount(i);
+ for (size_t j = 0; j < nCount; ++j)
+ {
+ const ScOutlineEntry* pEntry = rColArray.GetEntry(i, j);
+ if (pEntry->IsHidden())
+ rColArray.SetVisibleBelow(i, j, false);
+ }
+ }
+ ScOutlineArray& rRowArray(pOutlineTable->GetRowArray());
+ nDepth = rRowArray.GetDepth();
+ for (size_t i = 0; i < nDepth; ++i)
+ {
+ size_t nCount = rRowArray.GetCount(i);
+ for (size_t j = 0; j < nCount; ++j)
+ {
+ const ScOutlineEntry* pEntry = rRowArray.GetEntry(i, j);
+ if (pEntry->IsHidden())
+ rRowArray.SetVisibleBelow(i, j, false);
+ }
+ }
+ }
+ if (rTables.HasDrawPage())
+ {
+ if (rTables.HasXShapes())
+ {
+ rImport.GetShapeImport()->popGroupAndPostProcess();
+ uno::Reference < drawing::XShapes > xTempShapes(rTables.GetCurrentXShapes());
+ rImport.GetShapeImport()->endPage(xTempShapes);
+ }
+ if (bStartFormPage)
+ rImport.GetFormImport()->endPage();
+ }
+
+ rTables.DeleteTable();
+ rImport.ProgressBarIncrement();
+
+ // store stream positions
+ if (!pExternalRefInfo && nStartOffset >= 0 /* && nEndOffset >= 0 */)
+ {
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(rImport.GetModel())->GetSheetSaveData();
+ SCTAB nTab = rTables.GetCurrentSheet();
+ // pSheetData->AddStreamPos( nTab, nStartOffset, nEndOffset );
+ pSheetData->StartStreamPos( nTab, nStartOffset );
+ }
+}
+
+ScXMLTableProtectionContext::ScXMLTableProtectionContext(
+ ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList ) :
+ ScXMLImportContext( rImport )
+{
+ bool bSelectProtectedCells = false;
+ bool bSelectUnprotectedCells = false;
+ bool bInsertColumns = false;
+ bool bInsertRows = false;
+ bool bDeleteColumns = false;
+ bool bDeleteRows = false;
+
+ if ( rAttrList.is() )
+ {
+ for (auto &aIter : *rAttrList)
+ {
+ sal_Int32 nToken = aIter.getToken();
+ switch (nToken)
+ {
+ case XML_ELEMENT( TABLE, XML_SELECT_PROTECTED_CELLS ):
+ case XML_ELEMENT( OFFICE_EXT, XML_SELECT_PROTECTED_CELLS ):
+ case XML_ELEMENT( LO_EXT, XML_SELECT_PROTECTED_CELLS ):
+ bSelectProtectedCells = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( TABLE, XML_SELECT_UNPROTECTED_CELLS ):
+ case XML_ELEMENT( OFFICE_EXT, XML_SELECT_UNPROTECTED_CELLS ):
+ case XML_ELEMENT( LO_EXT, XML_SELECT_UNPROTECTED_CELLS ):
+ bSelectUnprotectedCells = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( LO_EXT, XML_INSERT_COLUMNS ):
+ bInsertColumns = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( LO_EXT, XML_INSERT_ROWS ):
+ bInsertRows = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( LO_EXT, XML_DELETE_COLUMNS ):
+ bDeleteColumns = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_ELEMENT( LO_EXT, XML_DELETE_ROWS ):
+ bDeleteRows = IsXMLToken(aIter, XML_TRUE);
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("sc", aIter);
+ }
+ }
+ }
+
+ ScXMLTabProtectionData& rProtectData = GetScImport().GetTables().GetCurrentProtectionData();
+ rProtectData.mbSelectProtectedCells = bSelectProtectedCells;
+ rProtectData.mbSelectUnprotectedCells = bSelectUnprotectedCells;
+ rProtectData.mbInsertColumns = bInsertColumns;
+ rProtectData.mbInsertRows = bInsertRows;
+ rProtectData.mbDeleteColumns = bDeleteColumns;
+ rProtectData.mbDeleteRows = bDeleteRows;
+}
+
+ScXMLTableProtectionContext::~ScXMLTableProtectionContext()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmltabi.hxx b/sc/source/filter/xml/xmltabi.hxx
new file mode 100644
index 000000000..524a4e87a
--- /dev/null
+++ b/sc/source/filter/xml/xmltabi.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+
+#include <externalrefmgr.hxx>
+#include "importcontext.hxx"
+
+#include <memory>
+
+namespace sax_fastparser { class FastAttributeList; }
+
+
+struct ScXMLExternalTabData
+{
+ ScExternalRefCache::TableTypeRef mpCacheTable;
+ sal_Int32 mnRow;
+ sal_Int32 mnCol;
+ sal_uInt16 mnFileId;
+
+ ScXMLExternalTabData();
+};
+
+class ScXMLTableContext : public ScXMLImportContext
+{
+ OUString sPrintRanges;
+ ::std::unique_ptr<ScXMLExternalTabData> pExternalRefInfo;
+ sal_Int32 nStartOffset;
+ bool bStartFormPage;
+ bool bPrintEntireSheet;
+
+public:
+
+ ScXMLTableContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableContext() override;
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext( sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList ) override;
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class ScXMLTableProtectionContext : public ScXMLImportContext
+{
+public:
+ ScXMLTableProtectionContext( ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList );
+
+ virtual ~ScXMLTableProtectionContext() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmltransformationi.cxx b/sc/source/filter/xml/xmltransformationi.cxx
new file mode 100644
index 000000000..4c53f30f8
--- /dev/null
+++ b/sc/source/filter/xml/xmltransformationi.cxx
@@ -0,0 +1,637 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "xmlimprt.hxx"
+#include "xmltransformationi.hxx"
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlnamespace.hxx>
+
+#include <datamapper.hxx>
+#include <document.hxx>
+
+using namespace com::sun::star;
+using namespace xmloff::token;
+
+ScXMLTransformationsContext::ScXMLTransformationsContext(ScXMLImport& rImport)
+ : ScXMLImportContext(rImport)
+{
+}
+
+ScXMLTransformationsContext::~ScXMLTransformationsContext() {}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLTransformationsContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ SvXMLImportContext* pContext = nullptr;
+ sax_fastparser::FastAttributeList* pAttribList
+ = &sax_fastparser::castToFastAttributeList(xAttrList);
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_REMOVE_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnRemoveContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_SPLIT_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnSplitContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_MERGE_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnMergeContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_SORT_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnSortContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_TEXT_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnTextContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_AGGREGATE_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnAggregateContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_NUMBER_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnNumberContext(GetScImport(), pAttribList);
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN_REPLACENULL_TRANSFORMATION):
+ {
+ pContext = new ScXMLColumnRemoveNullContext(GetScImport(), pAttribList);
+ }
+ break;
+ }
+
+ return pContext;
+}
+
+ScXMLColumnRemoveContext::ScXMLColumnRemoveContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& /*rAttrList*/)
+ : ScXMLImportContext(rImport)
+{
+}
+
+ScXMLColumnRemoveContext::~ScXMLColumnRemoveContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::ColumnRemoveTransformation>(std::set(maColumns)));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnRemoveContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ sax_fastparser::FastAttributeList* pAttribList
+ = &sax_fastparser::castToFastAttributeList(xAttrList);
+
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList = pAttribList;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnSplitContext::ScXMLColumnSplitContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+{
+ SCCOL mnCol = 0;
+ OUString cSeparator;
+
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ mnCol = aIter.toInt32();
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_SEPARATOR):
+ {
+ cSeparator = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+
+ if (mnCol > 0)
+ {
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::SplitColumnTransformation>(mnCol, cSeparator.toChar()));
+ }
+ }
+}
+
+ScXMLColumnSplitContext::~ScXMLColumnSplitContext() {}
+
+ScXMLColumnMergeContext::ScXMLColumnMergeContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+{
+ if (!rAttrList.is())
+ return;
+
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_MERGE_STRING):
+ {
+ maMergeString = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLColumnMergeContext::~ScXMLColumnMergeContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::MergeColumnTransformation>(std::set(maColumns), maMergeString));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnMergeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnSortContext::ScXMLColumnSortContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& /*rAttrList*/)
+ : ScXMLImportContext(rImport)
+{
+}
+
+ScXMLColumnSortContext::~ScXMLColumnSortContext() {}
+
+/*
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnSortContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+
+}
+*/
+
+ScXMLColumnTextContext::ScXMLColumnTextContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+ , maType(sc::TEXT_TRANSFORM_TYPE::TO_LOWER)
+{
+ OUString aType;
+
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ aType = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+
+ if (!aType.isEmpty())
+ {
+ if (aType == "lowercase")
+ maType = sc::TEXT_TRANSFORM_TYPE::TO_LOWER;
+ else if (aType == "uppercase")
+ maType = sc::TEXT_TRANSFORM_TYPE::TO_UPPER;
+ else if (aType == "capitalize")
+ maType = sc::TEXT_TRANSFORM_TYPE::CAPITALIZE;
+ else if (aType == "trim")
+ maType = sc::TEXT_TRANSFORM_TYPE::TRIM;
+ }
+}
+
+ScXMLColumnTextContext::~ScXMLColumnTextContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::TextTransformation>(std::set(maColumns), maType));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnTextContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnAggregateContext::ScXMLColumnAggregateContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+ , maType(sc::AGGREGATE_FUNCTION::SUM)
+{
+ OUString aType;
+
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ aType = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+
+ if (!aType.isEmpty())
+ {
+ if (aType == "sum")
+ maType = sc::AGGREGATE_FUNCTION::SUM;
+ else if (aType == "average")
+ maType = sc::AGGREGATE_FUNCTION::AVERAGE;
+ else if (aType == "min")
+ maType = sc::AGGREGATE_FUNCTION::MIN;
+ else if (aType == "max")
+ maType = sc::AGGREGATE_FUNCTION::MAX;
+ }
+}
+
+ScXMLColumnAggregateContext::~ScXMLColumnAggregateContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::AggregateFunction>(std::set(maColumns), maType));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnAggregateContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnNumberContext::ScXMLColumnNumberContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+ , maType(sc::NUMBER_TRANSFORM_TYPE::ROUND)
+ , maPrecision(0)
+{
+ OUString aType;
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ aType = aIter.toString();
+ }
+ break;
+ case XML_ELEMENT(CALC_EXT, XML_PRECISION):
+ {
+ maPrecision = aIter.toInt32();
+ }
+ break;
+ }
+ }
+ }
+
+ if (aType.isEmpty())
+ return;
+
+ if (aType == "round")
+ maType = sc::NUMBER_TRANSFORM_TYPE::ROUND;
+ else if (aType == "round-up")
+ maType = sc::NUMBER_TRANSFORM_TYPE::ROUND_UP;
+ else if (aType == "round-down")
+ maType = sc::NUMBER_TRANSFORM_TYPE::ROUND_DOWN;
+ else if (aType == "abs")
+ maType = sc::NUMBER_TRANSFORM_TYPE::ABSOLUTE;
+ else if (aType == "log")
+ maType = sc::NUMBER_TRANSFORM_TYPE::LOG_E;
+ else if (aType == "log-base-10")
+ maType = sc::NUMBER_TRANSFORM_TYPE::LOG_10;
+ else if (aType == "cube")
+ maType = sc::NUMBER_TRANSFORM_TYPE::CUBE;
+ else if (aType == "number-square")
+ maType = sc::NUMBER_TRANSFORM_TYPE::SQUARE;
+ else if (aType == "square-root")
+ maType = sc::NUMBER_TRANSFORM_TYPE::SQUARE_ROOT;
+ else if (aType == "exponential")
+ maType = sc::NUMBER_TRANSFORM_TYPE::EXPONENT;
+ else if (aType == "even")
+ maType = sc::NUMBER_TRANSFORM_TYPE::IS_EVEN;
+ else if (aType == "odd")
+ maType = sc::NUMBER_TRANSFORM_TYPE::IS_ODD;
+ else if (aType == "sign")
+ maType = sc::NUMBER_TRANSFORM_TYPE::SIGN;
+}
+
+ScXMLColumnNumberContext::~ScXMLColumnNumberContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::NumberTransformation>(std::set(maColumns), maType, maPrecision));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnNumberContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLColumnRemoveNullContext::ScXMLColumnRemoveNullContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+{
+ if (!rAttrList.is())
+ return;
+
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_REPLACE_STRING):
+ {
+ maReplaceString = aIter.toString();
+ }
+ break;
+ }
+ }
+}
+
+ScXMLColumnRemoveNullContext::~ScXMLColumnRemoveNullContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::ReplaceNullTransformation>(std::set(maColumns), maReplaceString));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL ScXMLColumnRemoveNullContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+ScXMLDateTimeContext::ScXMLDateTimeContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList)
+ : ScXMLImportContext(rImport)
+ , maType(sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING)
+{
+ if (rAttrList.is())
+ {
+ for (auto& aIter : *rAttrList)
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_TYPE):
+ {
+ aType = aIter.toString();
+ }
+ break;
+ }
+ }
+ }
+
+ if (aType.isEmpty())
+ return;
+
+ if (aType == "date-string")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING;
+ else if (aType == "year")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::YEAR;
+ else if (aType == "start-of-year")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::START_OF_YEAR;
+ else if (aType == "end-of-year")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::END_OF_YEAR;
+ else if (aType == "month")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::MONTH;
+ else if (aType == "month-name")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::MONTH_NAME;
+ else if (aType == "start-of-month")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::START_OF_MONTH;
+ else if (aType == "end-of-month")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::END_OF_MONTH;
+ else if (aType == "day")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::DAY;
+ else if (aType == "day-of-week")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_WEEK;
+ else if (aType == "day-of-year")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_YEAR;
+ else if (aType == "quarter")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::QUARTER;
+ else if (aType == "start-of-quarter")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::START_OF_QUARTER;
+ else if (aType == "end-of-quarter")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::END_OF_QUARTER;
+ else if (aType == "time")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::TIME;
+ else if (aType == "hour")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::HOUR;
+ else if (aType == "minute")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::MINUTE;
+ else if (aType == "seconds")
+ maType = sc::DATETIME_TRANSFORMATION_TYPE::SECOND;
+}
+
+ScXMLDateTimeContext::~ScXMLDateTimeContext()
+{
+ ScDocument* pDoc = GetScImport().GetDocument();
+ auto& rDataSources = pDoc->GetExternalDataMapper().getDataSources();
+ if (!rDataSources.empty())
+ {
+ rDataSources[rDataSources.size() - 1].AddDataTransformation(
+ std::make_shared<sc::DateTimeTransformation>(std::set(maColumns), maType));
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SAL_CALL ScXMLDateTimeContext::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(CALC_EXT, XML_COLUMN):
+ {
+ maColumns.insert(aIter.toInt32());
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ return new SvXMLImportContext(GetImport());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmltransformationi.hxx b/sc/source/filter/xml/xmltransformationi.hxx
new file mode 100644
index 000000000..9ee706620
--- /dev/null
+++ b/sc/source/filter/xml/xmltransformationi.hxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <datatransformation.hxx>
+
+#include "importcontext.hxx"
+
+namespace sax_fastparser
+{
+class FastAttributeList;
+}
+
+class ScXMLTransformationsContext : public ScXMLImportContext
+{
+public:
+ ScXMLTransformationsContext(ScXMLImport& rImport);
+
+ virtual ~ScXMLTransformationsContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnRemoveContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+
+public:
+ ScXMLColumnRemoveContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnRemoveContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnSplitContext : public ScXMLImportContext
+{
+public:
+ ScXMLColumnSplitContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnSplitContext() override;
+};
+
+class ScXMLColumnMergeContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+ OUString maMergeString;
+
+public:
+ ScXMLColumnMergeContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnMergeContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnSortContext : public ScXMLImportContext
+{
+public:
+ ScXMLColumnSortContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnSortContext() override;
+ /*
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+ */
+};
+
+class ScXMLColumnTextContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+ sc::TEXT_TRANSFORM_TYPE maType;
+
+public:
+ ScXMLColumnTextContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnTextContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnAggregateContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+ sc::AGGREGATE_FUNCTION maType;
+
+public:
+ ScXMLColumnAggregateContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnAggregateContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnNumberContext : public ScXMLImportContext
+{
+ sc::NUMBER_TRANSFORM_TYPE maType;
+ int maPrecision;
+ std::set<SCCOL> maColumns;
+
+public:
+ ScXMLColumnNumberContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnNumberContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLColumnRemoveNullContext : public ScXMLImportContext
+{
+ std::set<SCCOL> maColumns;
+ OUString maReplaceString;
+
+public:
+ ScXMLColumnRemoveNullContext(
+ ScXMLImport& rImport, const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLColumnRemoveNullContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class ScXMLDateTimeContext : public ScXMLImportContext
+{
+ OUString aType;
+ sc::DATETIME_TRANSFORMATION_TYPE maType;
+ std::set<SCCOL> maColumns;
+
+public:
+ ScXMLDateTimeContext(ScXMLImport& rImport,
+ const rtl::Reference<sax_fastparser::FastAttributeList>& rAttrList);
+
+ virtual ~ScXMLDateTimeContext() override;
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/xml/xmlwrap.cxx b/sc/source/filter/xml/xmlwrap.cxx
new file mode 100644
index 000000000..cdf2a53f7
--- /dev/null
+++ b/sc/source/filter/xml/xmlwrap.cxx
@@ -0,0 +1,998 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <officecfg/Office/Common.hxx>
+#include <vcl/errinf.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/objsh.hxx>
+#include <sot/storage.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/unreachable.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/xmlgrhlp.hxx>
+#include <svtools/sfxecode.hxx>
+#include <sfx2/frame.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/genericpropertyset.hxx>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+#include <sfx2/DocumentMetadataAccess.hxx>
+#include <comphelper/documentconstants.hxx>
+#include <xmloff/shapeexport.hxx>
+#include <svx/xmleohlp.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <document.hxx>
+#include <xmlwrap.hxx>
+#include "xmlimprt.hxx"
+#include "xmlexprt.hxx"
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <scerrors.hxx>
+#include "XMLExportSharedData.hxx"
+#include <docuno.hxx>
+#include <drwlayer.hxx>
+#include <sheetdata.hxx>
+#include "XMLCodeNameProvider.hxx"
+#include <docsh.hxx>
+#include <unonames.hxx>
+
+using namespace com::sun::star;
+using namespace css::uno;
+
+ScXMLImportWrapper::ScXMLImportWrapper( ScDocShell& rDocSh, SfxMedium* pM, const uno::Reference < embed::XStorage >& xStor ) :
+ mrDocShell(rDocSh),
+ rDoc(rDocSh.GetDocument()),
+ pMedium(pM),
+ xStorage(xStor)
+{
+ OSL_ENSURE( pMedium || xStorage.is(), "ScXMLImportWrapper: Medium or Storage must be set" );
+}
+
+uno::Reference <task::XStatusIndicator> ScXMLImportWrapper::GetStatusIndicator() const
+{
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+ if (pMedium)
+ {
+ SfxItemSet* pSet = pMedium->GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = pSet->GetItem<SfxUnoAnyItem>(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ xStatusIndicator.set(pItem->GetValue(), uno::UNO_QUERY);
+ }
+ }
+ return xStatusIndicator;
+}
+
+ErrCode ScXMLImportWrapper::ImportFromComponent(const uno::Reference<uno::XComponentContext>& xContext,
+ const uno::Reference<frame::XModel>& xModel,
+ xml::sax::InputSource& aParserInput,
+ const OUString& sComponentName, const OUString& sDocName,
+ const uno::Sequence<uno::Any>& aArgs,
+ bool bMustBeSuccessful)
+{
+ uno::Reference < io::XStream > xDocStream;
+ if ( !xStorage.is() && pMedium )
+ xStorage = pMedium->GetStorage();
+
+ bool bEncrypted = false;
+ OUString sStream(sDocName);
+ if( xStorage.is() )
+ {
+ try
+ {
+ if ( xStorage->hasByName(sDocName) && xStorage->isStreamElement( sDocName) )
+ xDocStream = xStorage->openStreamElement( sDocName, embed::ElementModes::READ );
+ else
+ return ERRCODE_NONE;
+
+ aParserInput.aInputStream = xDocStream->getInputStream();
+ uno::Reference < beans::XPropertySet > xSet( xDocStream, uno::UNO_QUERY );
+
+ uno::Any aAny = xSet->getPropertyValue("Encrypted");
+ aAny >>= bEncrypted;
+ }
+ catch( const packages::WrongPasswordException& )
+ {
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch( const packages::zip::ZipIOException& )
+ {
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch( const uno::Exception& )
+ {
+ return SCERR_IMPORT_UNKNOWN;
+ }
+ }
+ else
+ return SCERR_IMPORT_UNKNOWN;
+
+ // set Base URL
+ uno::Reference< beans::XPropertySet > xInfoSet;
+ if( aArgs.hasElements() )
+ aArgs.getConstArray()[0] >>= xInfoSet;
+ OSL_ENSURE( xInfoSet.is(), "missing property set" );
+ if( xInfoSet.is() )
+ {
+ xInfoSet->setPropertyValue( "StreamName", uno::Any( sStream ) );
+ }
+
+ ErrCode nReturn = ERRCODE_NONE;
+ rDoc.SetRangeOverflowType(ERRCODE_NONE); // is modified by the importer if limits are exceeded
+
+ uno::Reference<XInterface> xImportInterface =
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ sComponentName, aArgs, xContext );
+ SAL_WARN_IF( !xImportInterface, "sc", "can't get Calc importer " << sComponentName );
+ uno::Reference<document::XImporter> xImporter( xImportInterface, uno::UNO_QUERY );
+ if (xImporter.is())
+ xImporter->setTargetDocument( xModel );
+
+ ScXMLImport* pImporterImpl = dynamic_cast<ScXMLImport*>(xImporter.get());
+ if (pImporterImpl)
+ pImporterImpl->SetPostProcessData(&maPostProcessData);
+
+ // connect parser and filter
+ try
+ {
+ // xImportInterface is either ScXMLImport or an XMLTransformer subclass.
+ // ScXMLImport implements XFastParser, but XMLTransformer only implements XExtendedDocumentHandler
+
+ uno::Reference< xml::sax::XFastParser > xFastParser(xImportInterface, uno::UNO_QUERY);
+ if (xFastParser)
+ xFastParser->parseStream( aParserInput );
+ else
+ {
+ uno::Reference<xml::sax::XParser> xParser = xml::sax::Parser::create(xContext);
+ uno::Reference<css::xml::sax::XDocumentHandler> xDocumentHandler(xImportInterface, uno::UNO_QUERY);
+ xParser->setDocumentHandler( xDocumentHandler );
+ xParser->parseStream( aParserInput );
+ }
+ }
+ catch( const xml::sax::SAXParseException& r )
+ {
+ css::uno::Any ex( cppu::getCaughtException() );
+ // sax parser sends wrapped exceptions,
+ // try to find the original one
+ xml::sax::SAXException aSaxEx = *static_cast<xml::sax::SAXException const *>(&r);
+ bool bTryChild = true;
+
+ while( bTryChild )
+ {
+ xml::sax::SAXException aTmp;
+ if ( aSaxEx.WrappedException >>= aTmp )
+ aSaxEx = aTmp;
+ else
+ bTryChild = false;
+ }
+
+ packages::zip::ZipIOException aBrokenPackage;
+ if ( aSaxEx.WrappedException >>= aBrokenPackage )
+ return ERRCODE_IO_BROKENPACKAGE;
+ else if( bEncrypted )
+ nReturn = ERRCODE_SFX_WRONGPASSWORD;
+ else
+ {
+ SAL_WARN("sc.filter", "SAX parse exception caught while importing: " << exceptionToString(ex));
+
+ OUString sErr = OUString::number( r.LineNumber ) +
+ "," +
+ OUString::number( r.ColumnNumber );
+
+ if( !sDocName.isEmpty() )
+ {
+ nReturn = *new TwoStringErrorInfo(
+ (bMustBeSuccessful ? SCERR_IMPORT_FILE_ROWCOL
+ : SCWARN_IMPORT_FILE_ROWCOL),
+ sDocName, sErr,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ }
+ else
+ {
+ OSL_ENSURE( bMustBeSuccessful, "Warnings are not supported" );
+ nReturn = *new StringErrorInfo( SCERR_IMPORT_FORMAT_ROWCOL, sErr,
+ DialogMask::ButtonsOk | DialogMask::MessageError );
+ }
+ }
+ }
+ catch( const xml::sax::SAXException& r )
+ {
+ css::uno::Any ex( cppu::getCaughtException() );
+ packages::zip::ZipIOException aBrokenPackage;
+ if ( r.WrappedException >>= aBrokenPackage )
+ return ERRCODE_IO_BROKENPACKAGE;
+ else if( bEncrypted )
+ nReturn = ERRCODE_SFX_WRONGPASSWORD;
+ else
+ {
+ SAL_WARN("sc.filter", "SAX exception caught while importing: " << exceptionToString(ex));
+
+ nReturn = SCERR_IMPORT_FORMAT;
+ }
+ }
+ catch( const packages::zip::ZipIOException& )
+ {
+ TOOLS_WARN_EXCEPTION("sc.filter", "Zip exception caught while importing");
+
+ nReturn = ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch( const io::IOException& )
+ {
+ TOOLS_WARN_EXCEPTION("sc.filter", "IO exception caught while importing");
+
+ nReturn = SCERR_IMPORT_OPEN;
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sc.filter", "uno exception caught while importing");
+
+ nReturn = SCERR_IMPORT_UNKNOWN;
+ }
+
+ // #i31130# Can't use getImplementation here to get the ScXMLImport from xDocHandler,
+ // because when OOo 1.x files are loaded, xDocHandler is the OOo2OasisTransformer.
+ // So the overflow warning ErrorCode is now stored in the document.
+ // Export works differently, there getImplementation still works.
+
+ if (rDoc.HasRangeOverflow() && !nReturn)
+ nReturn = rDoc.GetRangeOverflowType();
+
+ // success!
+ return nReturn;
+}
+
+bool ScXMLImportWrapper::Import( ImportFlags nMode, ErrCode& rError )
+{
+ uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+
+ xml::sax::InputSource aParserInput;
+ if (pMedium)
+ aParserInput.sSystemId = pMedium->GetName();
+
+ if ( !xStorage.is() && pMedium )
+ xStorage = pMedium->GetStorage();
+
+ // get filter
+ uno::Reference<frame::XModel> xModel = mrDocShell.GetModel();
+
+ /** property map for export info set */
+ static comphelper::PropertyMapEntry const aImportInfoMap[] =
+ {
+ { OUString("ProgressRange"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressMax"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressCurrent"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("NumberStyles"), 0, cppu::UnoType<container::XNameAccess>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("PrivateData"), 0, cppu::UnoType<uno::XInterface>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BuildId"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("VBACompatibilityMode"), 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("ScriptConfiguration"), 0, cppu::UnoType<container::XNameAccess>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("OrganizerMode"), 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("SourceStorage"), 0, cppu::UnoType<embed::XStorage>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString(SC_UNO_ODS_LOCK_SOLAR_MUTEX), 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString(SC_UNO_ODS_IMPORT_STYLES), 0, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aImportInfoMap ) ) );
+
+ // No need to lock solar mutex when calling from the wrapper.
+ xInfoSet->setPropertyValue(SC_UNO_ODS_LOCK_SOLAR_MUTEX, uno::Any(false));
+
+ // ---- get BuildId from parent container if available
+
+ uno::Reference< container::XChild > xChild( xModel, uno::UNO_QUERY );
+ if( xChild.is() )
+ {
+ uno::Reference< beans::XPropertySet > xParentSet( xChild->getParent(), uno::UNO_QUERY );
+ if( xParentSet.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xParentSet->getPropertySetInfo() );
+ OUString sPropName("BuildId" );
+ if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(sPropName) )
+ {
+ xInfoSet->setPropertyValue( sPropName, xParentSet->getPropertyValue(sPropName) );
+ }
+ }
+ }
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator = GetStatusIndicator();
+ if (xStatusIndicator.is())
+ {
+ sal_Int32 nProgressRange(1000000);
+ xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange);
+ xInfoSet->setPropertyValue("ProgressRange", uno::Any(nProgressRange));
+ }
+
+ // Set base URI
+ OSL_ENSURE( pMedium, "There is no medium to get MediaDescriptor from!" );
+ OUString aBaseURL = pMedium ? pMedium->GetBaseURL() : OUString();
+ // needed for relative URLs, but in clipboard copy/paste there may be none
+ SAL_INFO_IF(aBaseURL.isEmpty(), "sc.filter", "ScXMLImportWrapper: no base URL");
+ OUString sPropName("BaseURI");
+ xInfoSet->setPropertyValue( sPropName, uno::Any( aBaseURL ) );
+
+ // TODO/LATER: do not do it for embedded links
+ OUString aName;
+ if (SfxObjectCreateMode::EMBEDDED == mrDocShell.GetCreateMode())
+ {
+ if ( pMedium && pMedium->GetItemSet() )
+ {
+ const SfxStringItem* pDocHierarchItem =
+ pMedium->GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME);
+ if ( pDocHierarchItem )
+ aName = pDocHierarchItem->GetValue();
+ }
+ else
+ aName = "dummyObjectName";
+
+ if( !aName.isEmpty() )
+ {
+ sPropName = "StreamRelPath";
+ xInfoSet->setPropertyValue( sPropName, uno::Any( aName ) );
+ }
+ }
+
+ if (mrDocShell.GetCreateMode() == SfxObjectCreateMode::ORGANIZER)
+ xInfoSet->setPropertyValue("OrganizerMode", uno::Any(true));
+
+ xInfoSet->setPropertyValue( "SourceStorage", uno::Any( xStorage ) );
+
+ bool bOasis = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 );
+
+ if ((nMode & ImportFlags::Metadata) && bOasis)
+ {
+ // RDF metadata: ODF >= 1.2
+ try
+ {
+ const uno::Reference< rdf::XDocumentMetadataAccess > xDMA(
+ xModel, uno::UNO_QUERY_THROW );
+ const uno::Reference< rdf::XURI > xBaseURI(
+ ::sfx2::createBaseURI( xContext, xModel, aBaseURL, aName ) );
+ uno::Reference<task::XInteractionHandler> xHandler =
+ mrDocShell.GetMedium()->GetInteractionHandler();
+ xDMA->loadMetadataFromStorage( xStorage, xBaseURI, xHandler );
+ }
+ catch ( const lang::WrappedTargetException & e)
+ {
+ ucb::InteractiveAugmentedIOException iaioe;
+ if ( e.TargetException >>= iaioe )
+ {
+ rError = SCERR_IMPORT_UNKNOWN;
+ }
+ else
+ {
+ rError = SCWARN_IMPORT_FEATURES_LOST;
+ }
+ }
+ catch ( const uno::Exception &)
+ {
+ rError = SCWARN_IMPORT_FEATURES_LOST;
+ }
+ }
+
+ // #i103539#: always read meta.xml for generator
+ ErrCode nMetaRetval(ERRCODE_NONE);
+ if (nMode & ImportFlags::Metadata)
+ {
+ uno::Sequence<uno::Any> aMetaArgs { Any(xInfoSet) };
+
+ SAL_INFO( "sc.filter", "meta import start" );
+
+ nMetaRetval = ImportFromComponent(
+ xContext, xModel, aParserInput,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisMetaImporter")
+ : OUString("com.sun.star.comp.Calc.XMLMetaImporter"),
+ "meta.xml", aMetaArgs, false);
+
+ SAL_INFO( "sc.filter", "meta import end" );
+ }
+
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+
+ uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver;
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper;
+
+ if( xStorage.is() )
+ {
+ xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, SvXMLGraphicHelperMode::Read );
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ xObjectHelper = SvXMLEmbeddedObjectHelper::Create(xStorage, mrDocShell, SvXMLEmbeddedObjectHelperMode::Read);
+ xObjectResolver = xObjectHelper.get();
+ }
+ uno::Sequence<uno::Any> aStylesArgs
+ {
+ Any(xInfoSet),
+ Any(xGraphicStorageHandler),
+ Any(xStatusIndicator),
+ Any(xObjectResolver)
+ };
+
+ ErrCode nSettingsRetval(ERRCODE_NONE);
+ if (nMode & ImportFlags::Settings)
+ {
+ // Settings must be loaded first because of the printer setting,
+ // which is needed in the page styles (paper tray).
+
+ uno::Sequence<uno::Any> aSettingsArgs { Any(xInfoSet) };
+
+ SAL_INFO( "sc.filter", "settings import start" );
+
+ nSettingsRetval = ImportFromComponent(
+ xContext, xModel, aParserInput,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisSettingsImporter")
+ : OUString("com.sun.star.comp.Calc.XMLSettingsImporter"),
+ "settings.xml", aSettingsArgs, false);
+
+ SAL_INFO( "sc.filter", "settings import end" );
+ }
+
+ ErrCode nStylesRetval(ERRCODE_NONE);
+ if (nMode & ImportFlags::Styles)
+ {
+ SAL_INFO( "sc.filter", "styles import start" );
+
+ nStylesRetval = ImportFromComponent(xContext, xModel, aParserInput,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisStylesImporter")
+ : OUString("com.sun.star.comp.Calc.XMLStylesImporter"),
+ "styles.xml",
+ aStylesArgs, true);
+
+ SAL_INFO( "sc.filter", "styles import end" );
+ }
+
+ ErrCode nDocRetval(ERRCODE_NONE);
+ if (nMode & ImportFlags::Content)
+ {
+ if (mrDocShell.GetCreateMode() == SfxObjectCreateMode::INTERNAL)
+ // We only need to import content for external link cache document.
+ xInfoSet->setPropertyValue(SC_UNO_ODS_IMPORT_STYLES, uno::Any(false));
+
+ uno::Sequence<uno::Any> aDocArgs
+ {
+ Any(xInfoSet),
+ Any(xGraphicStorageHandler),
+ Any(xStatusIndicator),
+ Any(xObjectResolver)
+ };
+
+ SAL_INFO( "sc.filter", "content import start" );
+
+ nDocRetval = ImportFromComponent(xContext, xModel, aParserInput,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisContentImporter")
+ : OUString("com.sun.star.comp.Calc.XMLContentImporter"),
+ "content.xml",
+ aDocArgs,
+ true);
+
+ SAL_INFO( "sc.filter", "content import end" );
+ }
+ if( xGraphicHelper.is() )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+
+ if( xObjectHelper.is() )
+ xObjectHelper->dispose();
+ xObjectHelper.clear();
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+
+ bool bRet = false;
+ if (nDocRetval)
+ {
+ rError = nDocRetval;
+ if (nDocRetval == SCWARN_IMPORT_RANGE_OVERFLOW ||
+ nDocRetval == SCWARN_IMPORT_ROW_OVERFLOW ||
+ nDocRetval == SCWARN_IMPORT_COLUMN_OVERFLOW ||
+ nDocRetval == SCWARN_IMPORT_SHEET_OVERFLOW)
+ bRet = true;
+ }
+ else if (nStylesRetval)
+ rError = nStylesRetval;
+ else if (nMetaRetval)
+ rError = nMetaRetval;
+ else if (nSettingsRetval)
+ rError = nSettingsRetval;
+ else
+ bRet = true;
+
+ ::svx::DropUnusedNamedItems(xModel);
+
+ // set BuildId on XModel for later OLE object loading
+ if( xInfoSet.is() )
+ {
+ uno::Reference< beans::XPropertySet > xModelSet( xModel, uno::UNO_QUERY );
+ if( xModelSet.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xModelSetInfo( xModelSet->getPropertySetInfo() );
+ OUString sBuildPropName("BuildId" );
+ if( xModelSetInfo.is() && xModelSetInfo->hasPropertyByName(sBuildPropName) )
+ {
+ xModelSet->setPropertyValue( sBuildPropName, xInfoSet->getPropertyValue(sBuildPropName) );
+ }
+ }
+
+ // Set Code Names
+ uno::Any aAny = xInfoSet->getPropertyValue("ScriptConfiguration");
+ uno::Reference <container::XNameAccess> xCodeNameAccess;
+ if( aAny >>= xCodeNameAccess )
+ XMLCodeNameProvider::set( xCodeNameAccess, &rDoc );
+
+ // VBA compatibility
+ bool bVBACompat = false;
+ if ( (xInfoSet->getPropertyValue("VBACompatibilityMode") >>= bVBACompat) && bVBACompat )
+ {
+ /* Set library container to VBA compatibility mode, this
+ forces loading the Basic project, which in turn creates the
+ VBA Globals object and does all related initialization. */
+ if ( xModelSet.is() ) try
+ {
+ uno::Reference< script::vba::XVBACompatibility > xVBACompat( xModelSet->getPropertyValue(
+ "BasicLibraries" ), uno::UNO_QUERY_THROW );
+ xVBACompat->setVBACompatibilityMode( true );
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+
+ // Don't test bStylesRetval and bMetaRetval, because it could be an older file which not contain such streams
+ return bRet;//!bStylesOnly ? bDocRetval : bStylesRetval;
+}
+
+static bool lcl_HasValidStream(const ScDocument& rDoc)
+{
+ SfxObjectShell* pObjSh = rDoc.GetDocumentShell();
+ if ( pObjSh->IsDocShared() )
+ return false; // never copy stream from shared file
+
+ // don't read remote file again
+ // (could instead re-use medium directly in that case)
+ SfxMedium* pSrcMed = rDoc.GetDocumentShell()->GetMedium();
+ if ( !pSrcMed || pSrcMed->IsRemote() )
+ return false;
+
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
+ if (rDoc.IsStreamValid(nTab))
+ return true;
+ return false;
+}
+
+bool ScXMLImportWrapper::ExportToComponent(const uno::Reference<uno::XComponentContext>& xContext,
+ const uno::Reference<frame::XModel>& xModel, const uno::Reference<xml::sax::XWriter>& xWriter,
+ const uno::Sequence<beans::PropertyValue>& aDescriptor, const OUString& sName,
+ const OUString& sMediaType, const OUString& sComponentName,
+ const uno::Sequence<uno::Any>& aArgs, std::unique_ptr<ScMySharedData>& pSharedData)
+{
+ bool bRet(false);
+ uno::Reference<io::XOutputStream> xOut;
+ uno::Reference<io::XStream> xStream;
+
+ if ( !xStorage.is() && pMedium )
+ xStorage = pMedium->GetOutputStorage();
+
+ if( xStorage.is() )
+ {
+ // #96807#; trunc stream before use, because it could be an existing stream
+ // and the new content could be shorter than the old content. In this case
+ // would not all be over written by the new content and the xml file
+ // would not be valid.
+ xStream = xStorage->openStreamElement( sName, embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ uno::Reference < beans::XPropertySet > xSet( xStream, uno::UNO_QUERY );
+ if (xSet.is())
+ {
+ xSet->setPropertyValue("MediaType", uno::Any(sMediaType));
+
+ // advise storage impl to use common encryption
+ xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", uno::Any(true) );
+ }
+
+ xOut = xStream->getOutputStream();
+ }
+
+ // set Base URL
+ uno::Reference< beans::XPropertySet > xInfoSet;
+ if( aArgs.hasElements() )
+ aArgs.getConstArray()[0] >>= xInfoSet;
+ OSL_ENSURE( xInfoSet.is(), "missing property set" );
+ if( xInfoSet.is() )
+ {
+ xInfoSet->setPropertyValue( "StreamName", uno::Any( sName ) );
+ }
+
+ xWriter->setOutputStream( xOut );
+
+ uno::Reference<document::XFilter> xFilter(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ sComponentName , aArgs, xContext ),
+ uno::UNO_QUERY );
+ OSL_ENSURE( xFilter.is(), "can't get exporter" );
+ uno::Reference<document::XExporter> xExporter( xFilter, uno::UNO_QUERY );
+ if (xExporter.is())
+ xExporter->setSourceDocument( xModel );
+
+ if ( xFilter.is() )
+ {
+ ScXMLExport* pExport = static_cast<ScXMLExport*>(comphelper::getFromUnoTunnel<SvXMLExport>(xFilter));
+ pExport->SetSharedData(std::move(pSharedData));
+
+ // if there are sheets to copy, get the source stream
+ if ( sName == "content.xml" && lcl_HasValidStream(rDoc) && ( pExport->getExportFlags() & SvXMLExportFlags::OASIS ) )
+ {
+ // old stream is still in this file's storage - open read-only
+
+ // #i106854# use the document's storage directly, without a temporary SfxMedium
+ uno::Reference<embed::XStorage> xTmpStorage = rDoc.GetDocumentShell()->GetStorage();
+ uno::Reference<io::XStream> xSrcStream;
+ uno::Reference<io::XInputStream> xSrcInput;
+
+ // #i108978# If an embedded object is saved and no events are notified, don't use the stream
+ // because without the ...DONE events, stream positions aren't updated.
+ ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(xModel)->GetSheetSaveData();
+ if (pSheetData && pSheetData->IsInSupportedSave())
+ {
+ try
+ {
+ if (xTmpStorage.is())
+ xSrcStream = xTmpStorage->openStreamElement( sName, embed::ElementModes::READ );
+ if (xSrcStream.is())
+ xSrcInput = xSrcStream->getInputStream();
+ }
+ catch ( const uno::Exception&)
+ {
+ // stream not available (for example, password protected) - save normally (xSrcInput is null)
+ }
+ }
+
+ pExport->SetSourceStream( xSrcInput );
+ bRet = xFilter->filter( aDescriptor );
+ pExport->SetSourceStream( uno::Reference<io::XInputStream>() );
+
+ // If there was an error, reset all stream flags, so the next save attempt will use normal saving.
+ // #i110692# For embedded objects, the stream may be unavailable for one save operation (m_pAntiImpl)
+ // and become available again later. But after saving normally once, the stream positions aren't
+ // valid anymore, so the flags also have to be reset if the stream wasn't available.
+ if ( !bRet || !xSrcInput.is() )
+ {
+ SCTAB nTabCount = rDoc.GetTableCount();
+ for (SCTAB nTab=0; nTab<nTabCount; nTab++)
+ rDoc.SetStreamValid(nTab, false);
+ }
+ }
+ else
+ bRet = xFilter->filter( aDescriptor );
+
+ pSharedData = pExport->ReleaseSharedData();
+ }
+
+ return bRet;
+}
+
+bool ScXMLImportWrapper::Export(bool bStylesOnly)
+{
+ // Prevent all broadcasting and repaints and notification of accessibility
+ // during mass creation of captions, which is a major bottleneck and not
+ // needed during Save.
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ bool bOldLock = bool();
+ if (pDrawLayer)
+ {
+ bOldLock = pDrawLayer->isLocked();
+ pDrawLayer->setLock(true);
+ }
+
+ rDoc.CreateAllNoteCaptions();
+
+ if (pDrawLayer)
+ pDrawLayer->setLock(bOldLock);
+
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ uno::Reference<xml::sax::XWriter> xWriter = xml::sax::Writer::create(xContext);
+
+ if ( !xStorage.is() && pMedium )
+ xStorage = pMedium->GetOutputStorage();
+
+ OUString sFileName;
+ if (pMedium)
+ sFileName = pMedium->GetName();
+ SfxObjectShell* pObjSh = rDoc.GetDocumentShell();
+ uno::Sequence<beans::PropertyValue> aDescriptor( comphelper::InitPropertySequence({
+ { "FileName", uno::Any(sFileName) }
+ }));
+
+ /** property map for export info set */
+ static comphelper::PropertyMapEntry const aExportInfoMap[] =
+ {
+ { OUString("ProgressRange"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressMax"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("ProgressCurrent"), 0, ::cppu::UnoType<sal_Int32>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("WrittenNumberStyles"), 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("UsePrettyPrinting"), 0, ::cppu::UnoType<sal_Bool>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StyleNames"), 0, cppu::UnoType<uno::Sequence<OUString>>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StyleFamilies"), 0, cppu::UnoType<uno::Sequence<sal_Int32>>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("TargetStorage"), 0, cppu::UnoType<embed::XStorage>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 },
+ };
+ uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aExportInfoMap ) ) );
+
+ if ( pObjSh && xStorage.is() )
+ {
+ uno::Reference<frame::XModel> xModel(pObjSh->GetModel());
+ // sorting wants to create undo actions
+ assert(SfxObjectCreateMode::STANDARD != pObjSh->GetCreateMode() || rDoc.GetDrawLayer()->IsUndoEnabled());
+ uno::Reference<drawing::XDrawPagesSupplier> const xDPS(xModel, uno::UNO_QUERY);
+ uno::Reference<container::XIndexAccess> const xDPs(xDPS->getDrawPages());
+ assert(xDPs.is());
+ for (auto i = xDPs->getCount(); 0 < i; )
+ {
+ --i;
+ uno::Reference<drawing::XShapes> const xDP(xDPs->getByIndex(i), uno::UNO_QUERY);
+ assert(xDP.is());
+ xmloff::FixZOrder(xDP,
+ [](uno::Reference<beans::XPropertySet> const& xShape)
+ {
+ sal_Int16 nLayerID(0);
+ xShape->getPropertyValue("LayerID") >>= nLayerID;
+ switch (nLayerID)
+ {
+ case sal_Int16(SC_LAYER_FRONT):
+ return 1;
+ case sal_Int16(SC_LAYER_BACK):
+ return 0;
+ case sal_Int16(SC_LAYER_INTERN):
+ return 2;
+ case sal_Int16(SC_LAYER_CONTROLS):
+ return 3;
+ case sal_Int16(SC_LAYER_HIDDEN):
+ return 1; // treat as equivalent to front
+ default:
+ O3TL_UNREACHABLE;
+ }
+ });
+ }
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator(GetStatusIndicator());
+ sal_Int32 nProgressRange(1000000);
+ if(xStatusIndicator.is())
+ xStatusIndicator->start(ScResId(STR_SAVE_DOC), nProgressRange);
+ xInfoSet->setPropertyValue("ProgressRange", uno::Any(nProgressRange));
+
+ bool bUsePrettyPrinting = officecfg::Office::Common::Save::Document::PrettyPrinting::get();
+ xInfoSet->setPropertyValue("UsePrettyPrinting", uno::Any(bUsePrettyPrinting));
+
+ xInfoSet->setPropertyValue( "TargetStorage", uno::Any( xStorage ) );
+
+ OSL_ENSURE( pMedium, "There is no medium to get MediaDescriptor from!" );
+ OUString aBaseURL = pMedium ? pMedium->GetBaseURL( true ) : OUString();
+ OUString sPropName("BaseURI");
+ xInfoSet->setPropertyValue( sPropName, uno::Any( aBaseURL ) );
+
+ // TODO/LATER: do not do it for embedded links
+ if( SfxObjectCreateMode::EMBEDDED == pObjSh->GetCreateMode() )
+ {
+ OUString aName("dummyObjectName");
+ if ( pMedium && pMedium->GetItemSet() )
+ {
+ const SfxStringItem* pDocHierarchItem =
+ pMedium->GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME);
+ if ( pDocHierarchItem )
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if( !aName.isEmpty() )
+ {
+ sPropName = "StreamRelPath";
+ xInfoSet->setPropertyValue( sPropName, uno::Any( aName ) );
+ }
+ }
+
+ OUString sTextMediaType("text/xml");
+ bool bMetaRet(pObjSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED);
+ bool bStylesRet (false);
+ bool bDocRet(false);
+ bool bSettingsRet(false);
+ std::unique_ptr<ScMySharedData> pSharedData;
+
+ bool bOasis = ( SotStorage::GetVersion( xStorage ) > SOFFICE_FILEFORMAT_60 );
+
+ // RDF metadata: ODF >= 1.2
+ if ( !bStylesOnly && bOasis )
+ {
+ const uno::Reference< beans::XPropertySet > xPropSet( xStorage, uno::UNO_QUERY_THROW );
+ try
+ {
+ OUString aVersion;
+ if (( xPropSet->getPropertyValue("Version") >>= aVersion )
+ && aVersion != ODFVER_010_TEXT
+ && aVersion != ODFVER_011_TEXT )
+ {
+ const uno::Reference< rdf::XDocumentMetadataAccess > xDMA(
+ xModel, uno::UNO_QUERY_THROW );
+ xDMA->storeMetadataToStorage( xStorage );
+ }
+ }
+ catch ( const beans::UnknownPropertyException &)
+ {
+ }
+ catch ( const uno::Exception &)
+ {
+ }
+ }
+
+ // meta export
+ if (!bStylesOnly && !bMetaRet)
+ {
+ uno::Sequence<uno::Any> aMetaArgs
+ {
+ Any(xInfoSet),
+ Any(xWriter),
+ Any(xStatusIndicator)
+ };
+
+ SAL_INFO( "sc.filter", "meta export start" );
+
+ bMetaRet = ExportToComponent(xContext, xModel, xWriter, aDescriptor,
+ "meta.xml",
+ sTextMediaType,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisMetaExporter")
+ : OUString("com.sun.star.comp.Calc.XMLMetaExporter"),
+ aMetaArgs, pSharedData);
+
+ SAL_INFO( "sc.filter", "meta export end" );
+ }
+
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ if( xStorage.is() )
+ {
+ xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, SvXMLGraphicHelperMode::Write );
+ xGraphicStorageHandler = xGraphicHelper.get();
+ }
+
+ auto xObjectHelper = SvXMLEmbeddedObjectHelper::Create(
+ xStorage, *pObjSh, SvXMLEmbeddedObjectHelperMode::Write);
+ uno::Reference<document::XEmbeddedObjectResolver> xObjectResolver(xObjectHelper);
+
+ // styles export
+
+ {
+ uno::Sequence<uno::Any> aStylesArgs
+ {
+ Any(xInfoSet),
+ Any(xGraphicStorageHandler),
+ Any(xStatusIndicator),
+ Any(xWriter),
+ Any(xObjectResolver)
+ };
+
+ SAL_INFO( "sc.filter", "styles export start" );
+
+ bStylesRet = ExportToComponent(xContext, xModel, xWriter, aDescriptor,
+ "styles.xml",
+ sTextMediaType,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisStylesExporter")
+ : OUString("com.sun.star.comp.Calc.XMLStylesExporter"),
+ aStylesArgs, pSharedData);
+
+ SAL_INFO( "sc.filter", "styles export end" );
+ }
+
+ // content export
+
+ if (!bStylesOnly)
+ {
+ uno::Sequence<uno::Any> aDocArgs
+ {
+ Any(xInfoSet),
+ Any(xGraphicStorageHandler),
+ Any(xStatusIndicator),
+ Any(xWriter),
+ Any(xObjectResolver)
+ };
+
+ SAL_INFO( "sc.filter", "content export start" );
+
+ bDocRet = ExportToComponent(xContext, xModel, xWriter, aDescriptor,
+ "content.xml",
+ sTextMediaType,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisContentExporter")
+ : OUString("com.sun.star.comp.Calc.XMLContentExporter"),
+ aDocArgs, pSharedData);
+
+ SAL_INFO( "sc.filter", "content export end" );
+ }
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+
+ if( xObjectHelper )
+ xObjectHelper->dispose();
+ xObjectHelper.clear();
+
+ // settings export
+
+ if (!bStylesOnly)
+ {
+ uno::Sequence<uno::Any> aSettingsArgs
+ {
+ Any(xInfoSet),
+ Any(xWriter),
+ Any(xStatusIndicator)
+ };
+
+ SAL_INFO( "sc.filter", "settings export start" );
+
+ bSettingsRet = ExportToComponent(xContext, xModel, xWriter, aDescriptor,
+ "settings.xml",
+ sTextMediaType,
+ bOasis ? OUString("com.sun.star.comp.Calc.XMLOasisSettingsExporter")
+ : OUString("com.sun.star.comp.Calc.XMLSettingsExporter"),
+ aSettingsArgs, pSharedData);
+
+ SAL_INFO( "sc.filter", "settings export end" );
+ }
+
+ pSharedData.reset();
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ return bStylesRet && ((!bStylesOnly && bDocRet && bMetaRet && bSettingsRet) || bStylesOnly);
+ }
+
+ // later: give string descriptor as parameter for doc type
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */