[Libreoffice-commits] .: 4 commits - misc/test-files.sh src/xlsparser.py src/xlsrecord.py src/xlsstream.py
Kohei Yoshida
kohei at kemper.freedesktop.org
Tue Oct 25 14:11:51 PDT 2011
misc/test-files.sh | 16 +
src/xlsparser.py | 150 ++++++++++++----
src/xlsrecord.py | 489 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/xlsstream.py | 39 ++--
4 files changed, 642 insertions(+), 52 deletions(-)
New commits:
commit d433336619d71185141c47cbe6c77c7103c812cf
Merge: 06a8b38... 0263e78...
Author: Kohei Yoshida <kohei.yoshida at suse.com>
Date: Tue Oct 25 17:10:04 2011 -0400
Merge branch 'tilarids-master'
commit 0263e7846fdd7aac6180f9f385070edf32f96c64
Author: Sergey Kishchenko <voidwrk at gmail.com>
Date: Tue Sep 27 15:32:59 2011 +0300
Charts dump was improved (several new records, etc)
diff --git a/src/xlsparser.py b/src/xlsparser.py
index c4b0f1c..322e9b2 100644
--- a/src/xlsparser.py
+++ b/src/xlsparser.py
@@ -67,7 +67,7 @@ class BaseParser(object):
return Seq(self, other)
def safeParse(parser, stream):
- print "TRACE:[%s,%s]" % (str(parser), str(stream.tokens[stream.currentIndex]))
+ #print "TRACE:[%s,%s]" % (str(parser), str(stream.tokens[stream.currentIndex]))
parsed = None
try:
@@ -178,7 +178,7 @@ class OneOf(BaseParser):
def parse(self, stream):
for parser in self.__parsers:
- parsed = safeParse(parser, stream)
+ parsed = getParsedOrNone(parser, stream)
if not parsed is None:
return parsed
raise ParseException("No suitable options: [%s]" % ','.join(str(x) for x in self.__parsers))
@@ -374,7 +374,8 @@ class Chart3DBarShape(BaseParser):
class PieFormat(BaseParser):
PARSER = Term(xlsrecord.PieFormat)
-class SerFmt(BaseParser): pass
+class SerFmt(BaseParser):
+ PARSER = Term(xlsrecord.SerFmt)
class MarkerFormat(BaseParser):
PARSER = Term(xlsrecord.MarkerFormat)
@@ -389,17 +390,25 @@ class FontX(BaseParser):
PARSER = Term(xlsrecord.FontX)
class AlRuns(BaseParser): pass
-class ObjectLink(BaseParser): pass
-class DataLabExtContents(BaseParser): pass
+
+class ObjectLink(BaseParser):
+ PARSER = Term(xlsrecord.ObjectLink)
+
+class DataLabExtContents(BaseParser):
+ PARSER = Term(xlsrecord.DataLabExtContents)
+
class CrtLayout12(BaseParser): pass
class CRTMLFRT(BaseParser): pass
class TEXTPROPS(BaseParser): pass
+class AttachedLabel(BaseParser):
+ PARSER = Term(xlsrecord.AttachedLabel)
+
class ATTACHEDLABEL(BaseParser):
#ATTACHEDLABEL = Text Begin Pos [FontX] [AlRuns] AI [FRAME] [ObjectLink] [DataLabExtContents]
#[CrtLayout12] [TEXTPROPS] [CRTMLFRT] End
- PARSER = Group('attached-label', Req(Text()) << Req(Begin()) << Req(Pos()) << FontX() << AlRuns() << Req(AI()) <<
+ PARSER = Group('attached-label-struct', Req(Text()) << Req(Begin()) << Req(Pos()) << FontX() << AlRuns() << Req(AI()) <<
Opt(FRAME()) << ObjectLink() << DataLabExtContents() << CrtLayout12() << TEXTPROPS() << CRTMLFRT() << Req(End()))
class SS(BaseParser):
@@ -407,10 +416,16 @@ class SS(BaseParser):
#[GELFRAME] [MarkerFormat] [AttachedLabel] *2SHAPEPROPS [CRTMLFRT] End
PARSER = Group('ss', Seq(Req(DataFormat()), Req(Begin()), Chart3DBarShape(),
Opt(Seq(Req(LineFormat()), Req(AreaFormat()), Req(PieFormat()))),
- SerFmt(), Opt(GELFRAME()), MarkerFormat(), Opt(ATTACHEDLABEL()), # ATTACHEDLABEL was used instead of AttachedLabel
+ SerFmt(), Opt(GELFRAME()), MarkerFormat(), AttachedLabel(),
Many('shape-props-list', SHAPEPROPS(), max=2), CRTMLFRT(),
Req(End())))
+class StartBlock(BaseParser):
+ PARSER = Term(xlsrecord.StartBlock)
+
+class EndBlock(BaseParser):
+ PARSER = Term(xlsrecord.EndBlock)
+
class SERIESFORMAT(BaseParser):
#SERIESFORMAT = Series Begin 4AI *SS (SerToCrt / (SerParent (SerAuxTrend / SerAuxErrBar)))
#*(LegendException [Begin ATTACHEDLABEL [TEXTPROPS] End]) End
@@ -419,7 +434,7 @@ class SERIESFORMAT(BaseParser):
Many('legend-exceptions', Group('legend-exception-root',
Seq(Req(LegendException()),
Seq(Req(Begin()), Req(ATTACHEDLABEL()), TEXTPROPS(), Req(End()))))) <<
- Req(End()))
+ EndBlock() << Req(End()))
@@ -472,11 +487,6 @@ class ContinueFrt12(BaseParser): pass
class ChartFrtInfo(BaseParser):
PARSER = Term(xlsrecord.ChartFrtInfo)
-class StartBlock(BaseParser):
- PARSER = Term(xlsrecord.StartBlock)
-
-class EndBlock(BaseParser):
- PARSER = Term(xlsrecord.EndBlock)
class AXS(BaseParser):
# AXS = [IFmtRecord] [Tick] [FontX] *4(AxisLine LineFormat) [AreaFormat] [GELFRAME]
@@ -524,7 +534,9 @@ class AXES(BaseParser):
class ChartFormat(BaseParser):
PARSER = Term(xlsrecord.ChartFormat)
-class BobPop(BaseParser): pass
+class BobPop(BaseParser):
+ PARSER = Term(xlsrecord.BobPop)
+
class BobPopCustom(BaseParser): pass
class Bar(BaseParser):
@@ -533,18 +545,26 @@ class Bar(BaseParser):
class Line(BaseParser):
PARSER = Term(xlsrecord.CHLine)
-class Pie(BaseParser):
+class Pie(BaseParser):
PARSER = Term(xlsrecord.CHPie)
-class Area(BaseParser): pass
-class Scatter(BaseParser): pass
+class Area(BaseParser):
+ PARSER = Term(xlsrecord.CHArea)
+
+class Scatter(BaseParser):
+ PARSER = Term(xlsrecord.CHScatter)
+
class Radar(BaseParser):
PARSER = Term(xlsrecord.CHRadar)
class RadarArea(BaseParser): pass
-class Surf(BaseParser): pass
-class SeriesList(BaseParser): pass
+class Surf(BaseParser):
+ PARSER = Term(xlsrecord.CHSurf)
+
+class SeriesList(BaseParser):
+ PARSER = Term(xlsrecord.SeriesList)
+
class Chart3d(BaseParser):
PARSER = Term(xlsrecord.Chart3d)
@@ -569,20 +589,32 @@ class CrtLine(BaseParser):
PARSER = Term(xlsrecord.CrtLine)
class CrtLayout12A(BaseParser): pass
-class DAT(BaseParser): pass
+
+class Dat(BaseParser):
+ PARSER = Term(xlsrecord.Dat)
+
+class DAT(BaseParser):
+ #DAT = Dat Begin LD End
+ PARSER = Group('dat-root', Req(Dat()) << Req(Begin()) << Req(LD()) << Req(End()))
+
class CRT(BaseParser):
# It seems 2DROPBAR should be considered *2DROPBAR
#CRT = ChartFormat Begin (Bar / Line / (BopPop [BopPopCustom]) / Pie / Area / Scatter / Radar /
#RadarArea / Surf) CrtLink [SeriesList] [Chart3d] [LD] [*2DROPBAR] *4(CrtLine LineFormat)
#*2DFTTEXT [DataLabExtContents] [SS] *4SHAPEPROPS End
+ # It seems there are optional StartBlock and EndBlock on the last line:
+ #*2DFTTEXT [StartBlock] [DataLabExtContents] [SS] *4SHAPEPROPS [EndBlock] End
+
+
PARSER = Group('crt', Req(ChartFormat()) << Req(Begin()) << OneOf(Bar(), Line(), Opt(Seq(Req(BobPop()), BobPopCustom())),
Pie(), Area(), Scatter(), Radar(),
RadarArea(), Surf()) <<
Req(CrtLink()) << SeriesList() << Chart3d() << Opt(LD()) << Many('drop-bars', DROPBAR(), max=2) <<
Many('crt-lines', Seq(Req(CrtLine()),
Req(LineFormat()))) << Many('dft-texts', DFTTEXT()) <<
- DataLabExtContents() << Opt(SS()) << Many('shape-props-list', SHAPEPROPS(), max=4) << Req(End()))
+ StartBlock() << DataLabExtContents() << Opt(SS()) <<
+ Many('shape-props-list', SHAPEPROPS(), max=4) << EndBlock() << Req(End()))
class AXISPARENT(BaseParser):
# Original:
diff --git a/src/xlsrecord.py b/src/xlsrecord.py
index 880e3db..c846d5e 100644
--- a/src/xlsrecord.py
+++ b/src/xlsrecord.py
@@ -124,6 +124,15 @@ def dumpCfrtid(cfrtid):
'end': cfrtid.end}
+class FrtHeader(object):
+ def __init__ (self, rt, flags):
+ self.rt = rt
+ self.flags = flags
+
+def dumpFrtHeader(header):
+ return {'rt': header.rt,
+ 'flags': header.flags}
+
class BaseRecordHandler(globals.ByteStream):
def __init__ (self, header, size, bytes, strmData):
@@ -235,6 +244,9 @@ Like parseBytes(), the derived classes must overwrite this method."""
def readCFRTID (self):
return CFRTID(self.readUnsignedInt(2),self.readUnsignedInt(2))
+ def readFrtHeader (self):
+ return FrtHeader(self.readUnsignedInt(2), self.readUnsignedInt(2))
+
class AutofilterInfo(BaseRecordHandler):
def __parseBytes (self):
@@ -3611,6 +3623,25 @@ class DataFormat(BaseRecordHandler):
'yi': self.yi,
'iss': self.iss})
+class SerFmt(BaseRecordHandler):
+ def __parseBytes(self):
+ flags = self.readUnsignedInt(2)
+ self.smoothedLine = (flags & 0x001) != 0
+ self.bubbles3D = (flags & 0x002) != 0
+ self.arShadow = (flags & 0x004) != 0
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Smoothed line: %s" % self.getTrueFalse(self.smoothedLine))
+ self.appendLine("3D bubbles: %s" % self.getTrueFalse(self.bubbles3D))
+ self.appendLine("With shadow: %s" % self.getTrueFalse(self.arShadow))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('ser-fmt', {'smoothed-line': self.smoothedLine,
+ 'bubbles-3d': self.bubbles3D,
+ 'ar-shadow': self.arShadow})
+
class ChartFormat(BaseRecordHandler):
def __parseBytes(self):
reserved1 = self.readUnsignedInt(4)
@@ -3630,6 +3661,37 @@ class ChartFormat(BaseRecordHandler):
return ('chart-format', {'varied': self.varied,
'icrt': self.icrt})
+class DataLabExtContents(BaseRecordHandler):
+ def __parseBytes(self):
+ self.header = self.readFrtHeader()
+ flags = self.readUnsignedInt(2)
+ self.serName = (flags & 0x001) != 0 # A
+ self.catName = (flags & 0x002) != 0 # B
+ self.value = (flags & 0x004) != 0 # C
+ self.percent = (flags & 0x008) != 0 # D
+ self.bubSizes = (flags & 0x010) != 0 # E
+ self.sep = self.readUnicodeString()
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Header: %s, %s" % (str(self.header.rt), str(self.header.flags)))
+ self.appendLine("Display series name: %s" % self.getTrueFalse(self.serName))
+ self.appendLine("Display categories name: %s" % self.getTrueFalse(self.catName))
+ self.appendLine("Display value: %s" % self.getTrueFalse(self.value))
+ self.appendLine("Is a percent: %s" % self.getTrueFalse(self.percent))
+ self.appendLine("Display bubble size: %s" % self.getTrueFalse(self.bubSizes))
+ self.appendLine("Separator: %s" % str(self.sep))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('datalab-ext-contents', {'ser-name': self.serName,
+ 'cat-name': self.catName,
+ 'value': self.value,
+ 'percent': self.percent,
+ 'bub-sizes': self.bubSizes,
+ 'sep': self.sep},
+ [('header', dumpFrtHeader(self.header))])
+
class ChartFrtInfo(BaseRecordHandler):
def __parseBytes(self):
self.headerOld = self.readUnsignedInt(4)
@@ -3744,7 +3806,6 @@ class DropBar(BaseRecordHandler):
def parseBytes (self):
self.__parseBytes()
self.appendLine('Gap: %s' % str(self.gap))
- # TODO: dump all data
def dumpData(self):
self.__parseBytes()
@@ -3757,12 +3818,58 @@ class CrtLine(BaseRecordHandler):
def parseBytes (self):
self.__parseBytes()
self.appendLine('ID: %s' % str(self.id))
- # TODO: dump all data
def dumpData(self):
self.__parseBytes()
return ('crt-line', {'id': self.id})
+class ObjectLink(BaseRecordHandler):
+ def __parseBytes(self):
+ self.linkObj = self.readUnsignedInt(2)
+ self.linkVar1 = self.readUnsignedInt(2)
+ self.linkVar2 = self.readUnsignedInt(2)
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine('Link object: %s' % str(self.linkObj))
+ self.appendLine('Link var1: %s' % str(self.linkVar1))
+ self.appendLine('Link var2: %s' % str(self.linkVar2))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('object-link', {'link-obj': self.linkObj,
+ 'link-var1': self.linkVar1,
+ 'link-var2': self.linkVar2})
+
+class AttachedLabel(BaseRecordHandler):
+ def __parseBytes(self):
+ flag = self.readUnsignedInt(2)
+ self.showValue = (flag & 0x0001) != 0 # A
+ self.showPercent = (flag & 0x0002) != 0 # B
+ self.showLabelAndPerc = (flag & 0x0004) != 0 # C
+ unused = (flag & 0x0008) != 0 # D
+ self.showLabel = (flag & 0x0010) != 0 # E
+ self.showBubbleSizes = (flag & 0x0020) != 0 # F
+ self.showSeriesName = (flag & 0x0040) != 0 # G
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Show value: %s" % self.getTrueFalse(self.showValue))
+ self.appendLine("Show percent: %s" % self.getTrueFalse(self.showPercent))
+ self.appendLine("Show label and percent: %s" % self.getTrueFalse(self.showLabelAndPerc))
+ self.appendLine("Show bubble sizes: %s" % self.getTrueFalse(self.showBubbleSizes))
+ self.appendLine("Show series name: %s" % self.getTrueFalse(self.showSeriesName))
+ # TODO: dump all data
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('attached-label', {'show-value': self.showValue,
+ 'show-percent': self.showPercent,
+ 'show-label-and-perc': self.showLabelAndPerc,
+ 'show-label': self.showLabel,
+ 'show-bubble-sizes': self.showBubbleSizes,
+ 'show-series-name': self.showSeriesName})
+
class Chart3d(BaseRecordHandler):
def __parseBytes(self):
self.rot = self.readSignedInt(2)
@@ -3853,6 +3960,74 @@ class AxisParent(BaseRecordHandler):
self.__parseBytes()
return ('axis-parent', {'iax': self.iax})
+class BobPop(BaseRecordHandler):
+ def __parseBytes(self):
+ self.pst = self.readUnsignedInt(1)
+ self.autoSplit = self.readUnsignedInt(1)
+ self.split = self.readUnsignedInt(2)
+ self.splitPos = self.readSignedInt(2)
+ self.splitPercent = self.readSignedInt(2)
+ self.pie2Size = self.readSignedInt(2)
+ self.gap = self.readSignedInt(1)
+ self.splitValue = self.readDouble()
+
+ flag = self.readUnsignedInt(2)
+ self.hasShadow = (flag & 0x0001) != 0 # A
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('bobpop', {'pst': self.pst,
+ 'auto-split': self.autoSplit,
+ 'split': self.split,
+ 'split-pos': self.splitPos,
+ 'split-percent': self.splitPercent,
+ 'pie-2-size': self.pie2Size,
+ 'gap': self.gap,
+ 'split-balue': self.splitValue,
+ 'has-shadow': self.hasShadow})
+
+ def parseBytes (self):
+ self.__parseBytes()
+
+ self.appendLine("Chart group type: %s" % str(self.pst))
+ self.appendLine("Auto split: %s" % self.getTrueFalse(self.autoSplit))
+ self.appendLine("Split type: %s" % str(self.split))
+ if self.split == 0x0: # pos
+ self.appendLine("Split pos: %s" % str(self.splitPos))
+ elif self.split == 0x1: # value
+ self.appendLine("Split value: %s" % str(self.splitValue))
+ elif self.split == 0x2: # percent
+ self.appendLine("Split percent: %s" % str(self.splitPercent))
+ else:
+ self.appendLine("Custom split is specified in BopPopCustom record that follows")
+
+ self.appendLine("Size of a secondary pie/bar: %s" % str(self.pie2Size))
+ self.appendLine("Gap: %s" % str(self.gap))
+ self.appendLine("Has shadow: %s" % self.getTrueFalse(self.hasShadow))
+
+class Dat(BaseRecordHandler):
+ def __parseBytes(self):
+ flag = self.readUnsignedInt(2)
+ self.hasBordHorz = (flag & 0x0001) != 0 # A
+ self.hasBordVert = (flag & 0x0002) != 0 # B
+ self.hasBordOutline = (flag & 0x0004) != 0 # C
+ self.showSeriesKey = (flag & 0x0008) != 0 # D
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('dat', {'has-bord-horz': self.hasBordHorz,
+ 'has-bord-vert': self.hasBordVert,
+ 'has-bord-outline': self.hasBordOutline,
+ 'show-series-key': self.showSeriesKey})
+
+ def parseBytes (self):
+ self.__parseBytes()
+
+ self.appendLine("Has horizontal borders: %s" % self.getTrueFalse(self.hasBordHorz))
+ self.appendLine("Has vertical borders: %s" % self.getTrueFalse(self.hasBordVert))
+ self.appendLine("Has outline borders: %s" % self.getTrueFalse(self.hasBordOutline))
+ self.appendLine("Show series key: %s" % self.getTrueFalse(self.showSeriesKey))
+
class AxcExt(BaseRecordHandler):
def __parseBytes (self):
self.catMin = self.readUnsignedInt(2)
@@ -3928,6 +4103,26 @@ class Tick(BaseRecordHandler):
[('rgb', dumpRgb(self.rgb)),
('icv', dumpIcv(self.icv))])
+class SeriesList(BaseRecordHandler):
+ def __parseBytes(self):
+ self.cser = self.readUnsignedInt(2)
+ self.series = []
+ for x in xrange(self.cser):
+ self.series.append(self.readUnsignedInt(2))
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Series count : %s" % str(self.cser))
+ for x in self.series:
+ self.appendLine("Series id : %s" % str(x))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('series-list',
+ {'cser': self.cser},
+ map(lambda x: ('series-index',x),
+ self.series))
+
class AxisLine(BaseRecordHandler):
def __parseBytes(self):
self.id = self.readUnsignedInt(2)
@@ -4353,8 +4548,68 @@ class CHRadar(BaseRecordHandler):
self.__parseBytes()
return ('radar', {'rdr-ax-lab': self.rdrAxLab,
'has-shadow': self.hasShadow})
+
+class CHSurf(BaseRecordHandler):
+ def __parseBytes (self):
+ flags = self.readUnsignedInt(2)
+ self.fillSurface = (flags & 0x0001) != 0 # A
+ self.phongShade3D = (flags & 0x0002) != 0 # B
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Surface has a fill: %s"%self.getYesNo(self.fillSurface))
+ self.appendLine("3D Phong shading: %s"%self.getYesNo(self.phongShade3D))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('surf', {'fill-surface': self.fillSurface,
+ 'phong-shade-3d': self.phongShade3D})
+class CHArea(BaseRecordHandler):
+ def __parseBytes (self):
+ flags = self.readUnsignedInt(2)
+ self.stacked = (flags & 0x0001) != 0 # A
+ self.f100 = (flags & 0x0002) != 0 # B
+ self.hasShadow = (flags & 0x0002) != 0 # B
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Is stacked: %s"%self.getYesNo(self.stacked))
+ self.appendLine("Data points are percentage of sum: %s"%self.getYesNo(self.f100))
+ self.appendLine("Has shadow: %s"%self.getYesNo(self.hasShadow))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('surf', {'stacked': self.stacked,
+ 'f100': self.f100,
+ 'has-shadow': self.hasShadow})
+
+class CHScatter(BaseRecordHandler):
+ def __parseBytes (self):
+ self.bubbleSizeRatio = self.readUnsignedInt(2)
+ self.bubbleSize = self.readUnsignedInt(2)
+ flags = self.readUnsignedInt(2)
+ self.bubbles = (flags & 0x0001) != 0 # A
+ self.showNegBubbles = (flags & 0x0002) != 0 # B
+ self.hasShadow = (flags & 0x0004) != 0 # C
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Bubble size ratio: %s" % str(self.bubbleSizeRatio))
+ self.appendLine("Bubble size: %s" % str(self.bubbleSize))
+ self.appendLine("Is a bubble chart group: %s"%self.getYesNo(self.bubbles))
+ self.appendLine("Show negative bubbles: %s"%self.getYesNo(self.showNegBubbles))
+ self.appendLine("Data points have shadow: %s"%self.getYesNo(self.hasShadow))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('pie', {'bubble-size-ratio': self.bubbleSizeRatio,
+ 'bubble-size': self.bubbleSize,
+ 'bubble': self.bubbles,
+ 'show-neg-bubbles': self.showNegBubbles,
+ 'has-shadow': self.hasShadow})
+
class CHPie(BaseRecordHandler):
def __parseBytes (self):
self.start = self.readUnsignedInt(2)
diff --git a/src/xlsstream.py b/src/xlsstream.py
index 68bb59c..944346b 100644
--- a/src/xlsstream.py
+++ b/src/xlsstream.py
@@ -233,6 +233,7 @@ recData = {
0x0864: ["SXADDL", "Pivot Table Additional Info", xlsrecord.SXAddlInfo],
0x0867: ["FEATHEADR", "Shared Feature Header", xlsrecord.FeatureHeader],
0x0868: ["RANGEPROTECTION", "Protected Range on Protected Sheet"],
+ 0x086B: ["DATALABEXTCONTENTS", "Contents of an extended data label", xlsrecord.DataLabExtContents],
0x087D: ["XFEXT", "XF Extension"],
0x088C: ["COMPAT12", "Compatibility Checker 12"],
0x088E: ["TABLESTYLES", "Table Styles"],
@@ -250,15 +251,16 @@ recData = {
0x1009: ["CHMARKERFORMAT", "Color, size, and shape of the markers", xlsrecord.MarkerFormat],
0x100A: ["AREAFORMAT", "Patterns and Colors in Filled Region of Chart", xlsrecord.AreaFormat],
0x100B: ["CHPIEFORMAT", "Distance of a data point from the center", xlsrecord.PieFormat],
- 0x100C: ["CHATTACHEDLABEL", "?"],
+ 0x100C: ["CHATTACHEDLABEL", "Properties of a data label", xlsrecord.AttachedLabel],
0x100D: ["SERIESTEXT", "Series Category Name or Title Text in Chart", xlsrecord.SeriesText],
0x1014: ["CHTYPEGROUP", "Properties of a chart group", xlsrecord.ChartFormat],
0x1015: ["LEGEND", "Legend Properties", xlsrecord.Legend],
- 0x1017: ["CHBAR, CHCOLUMN", "?", xlsrecord.CHBar],
- 0x1018: ["CHLINE", "?", xlsrecord.CHLine],
+ 0x1016: ["SERIESLIST", "Series of the chart", xlsrecord.SeriesList],
+ 0x1017: ["CHBAR, CHCOLUMN", "Bar chart group", xlsrecord.CHBar],
+ 0x1018: ["CHLINE", "Line chart group", xlsrecord.CHLine],
0x1019: ["CHPIE", "Pie/Doughnut chart group", xlsrecord.CHPie],
- 0x101A: ["CHAREA", "?"],
- 0x101B: ["CHSCATTER", "?"],
+ 0x101A: ["CHAREA", "Area chart group", xlsrecord.CHArea],
+ 0x101B: ["CHSCATTER", "Scatter/Bubble chart group", xlsrecord.CHScatter],
0x101C: ["CHCHARTLINE", "Specifies the presence of lines", xlsrecord.CrtLine],
0x101D: ["CHAXIS", "Chart Axis", xlsrecord.CHAxis],
0x101E: ["CHTICK", "Attributes of the axis labels and tick marks", xlsrecord.Tick],
@@ -269,7 +271,7 @@ recData = {
0x1024: ["DEFAULTTEXT", "Default Text", xlsrecord.DefaultText],
0x1025: ["TEXT", "Label Properties", xlsrecord.Text],
0x1026: ["CHFONT", "Font for a given text element", xlsrecord.FontX],
- 0x1027: ["CHOBJECTLINK", "?"],
+ 0x1027: ["CHOBJECTLINK", "Object on a chart that is linked to a text", xlsrecord.ObjectLink],
0x1032: ["FRAME", "Type, Size and Position of the Frame around A Chart", xlsrecord.Frame],
0x1033: ["BEGIN", "Start of Chart Sheet Substream", xlsrecord.Begin],
0x1034: ["END", "End of Chart Sheet Substream", xlsrecord.End],
@@ -278,7 +280,7 @@ recData = {
0x103C: ["CHPICFORMAT", "?"],
0x103D: ["CHDROPBAR", "Attributes of the up/down bars between multiple series", xlsrecord.DropBar],
0x103E: ["CHRADARLINE", "Radar chart group", xlsrecord.CHRadar],
- 0x103F: ["CHSURFACE", "?"],
+ 0x103F: ["CHSURFACE", "Surface chart group", xlsrecord.CHRadar],
0x1040: ["CHRADARAREA", "?"],
0x1041: ["CHAXESSET", "Properties of an axis group", xlsrecord.AxisParent],
0x1044: ["CHPROPERTIES", "Properties of a chart(2.4.261)", xlsrecord.CHProperties],
@@ -292,11 +294,12 @@ recData = {
0x1050: ["CHFORMATRUNS", "?"],
0x1051: ["BRAI", "Data Source of A Chart", xlsrecord.Brai],
0x105B: ["CHSERERRORBAR", "?"],
- 0x105D: ["CHSERIESFORMAT", "?"],
+ 0x105D: ["CHSERIESFORMAT", "Series properties", xlsrecord.SerFmt],
0x105F: ["CH3DDATAFORMAT", "Shape of the data points(2.4.47)", xlsrecord.Chart3DBarShape],
0x1060: ["FBI", "Font Information for Chart", xlsrecord.Fbi],
- 0x1061: ["CHPIEEXT", "?"],
+ 0x1061: ["CHPIEEXT", "Pie/bar of pie chart group", xlsrecord.BobPop],
0x1062: ["AXCEXT", "Additional extension properties of a date axis(2.4.9)", xlsrecord.AxcExt],
+ 0x1063: ["DAT", "Options of the data table(2.4.73)", xlsrecord.Dat],
0x1064: ["PLOTGROWTH", "Font Scaling Information in the Plot Area", xlsrecord.PlotGrowth],
0x1065: ["CHSIINDEX*", "Part of a group of records which specify the data of a chart", xlsrecord.SIIndex],
0x1066: ["CHESCHERFORMAT", "Properties of a fill pattern", xlsrecord.GelFrame]
commit 273385b124263f1b3142335b671fec2d0bfe5b3d
Author: Sergey Kishchenko <voidwrk at gmail.com>
Date: Thu Sep 22 21:02:35 2011 +0300
Several records were added; GelFrame is not parsed as a MsoDrawing for now :(
diff --git a/src/xlsparser.py b/src/xlsparser.py
index 1e04ce2..c4b0f1c 100644
--- a/src/xlsparser.py
+++ b/src/xlsparser.py
@@ -243,8 +243,10 @@ class LeftMargin(MarginBaseParser): pass
class RightMargin(MarginBaseParser): pass
class TopMargin(MarginBaseParser): pass
class BottomMargin(MarginBaseParser): pass
-class Pls(MarginBaseParser): pass
-class Continue(MarginBaseParser): pass
+class Pls(BaseParser):
+ PARSER = Term(xlsrecord.Pls)
+
+class Continue(BaseParser): pass
class Setup(BaseParser):
PARSER = Term(xlsrecord.Setup)
@@ -319,13 +321,22 @@ class LineFormat(BaseParser):
class AreaFormat(BaseParser):
PARSER = Term(xlsrecord.AreaFormat)
+
+class PICF(BaseParser): pass # PICF = Begin PicF End
+
+class GelFrame(BaseParser):
+ PARSER = Term(xlsrecord.GelFrame)
+
+class GELFRAME(BaseParser):
+ #GELFRAME = 1*2GelFrame *Continue [PICF]
+ PARSER = Group('gel-frame-root', Many('gel-frame-list', GelFrame(), min=1, max=2) <<
+ Many('continue-list', Continue()) << Opt(PICF()))
-class GELFRAME(BaseParser): pass
class SHAPEPROPS(BaseParser): pass
class FRAME(BaseParser):
PARSER = Group('frame', Req(Frame()) << Req(Begin()) << Req(LineFormat()) << Req(AreaFormat()) <<
- GELFRAME() << SHAPEPROPS() << Req(End()))
+ Opt(GELFRAME()) << Opt(SHAPEPROPS()) << Req(End()))
class Scl(BaseParser):
@@ -396,7 +407,7 @@ class SS(BaseParser):
#[GELFRAME] [MarkerFormat] [AttachedLabel] *2SHAPEPROPS [CRTMLFRT] End
PARSER = Group('ss', Seq(Req(DataFormat()), Req(Begin()), Chart3DBarShape(),
Opt(Seq(Req(LineFormat()), Req(AreaFormat()), Req(PieFormat()))),
- SerFmt(), GELFRAME(), MarkerFormat(), Opt(ATTACHEDLABEL()), # ATTACHEDLABEL was used instead of AttachedLabel
+ SerFmt(), Opt(GELFRAME()), MarkerFormat(), Opt(ATTACHEDLABEL()), # ATTACHEDLABEL was used instead of AttachedLabel
Many('shape-props-list', SHAPEPROPS(), max=2), CRTMLFRT(),
Req(End())))
@@ -472,19 +483,19 @@ class AXS(BaseParser):
# *4SHAPEPROPS [TextPropsStream *ContinueFrt12]
PARSER = Group('axs', IFmtRecord() << Tick() << FontX() <<
Many('axis-lines', Seq(Req(AxisLine()), Req(LineFormat())), max=4) <<
- AreaFormat() << GELFRAME() << Many('shape-props-list', SHAPEPROPS(), max=4) <<
+ AreaFormat() << Opt(GELFRAME()) << Many('shape-props-list', SHAPEPROPS(), max=4) <<
Opt(Seq(Req(TextPropsStream()), Many('continue-frt12-list', ContinueFrt12()))))
class IVAXIS(BaseParser):
# original ABNF:
# IVAXIS = Axis Begin [CatSerRange] AxcExt [CatLab] AXS [CRTMLFRT] End
# it seems it's usual too have several future records indicators just after AxcExt and before the End:
- # IVAXIS = Axis Begin [CatSerRange] AxcExt [ChartFrtInfo *StartBlock] [CatLab] AXS [CRTMLFRT] [EndBlock] End
+ # IVAXIS = Axis Begin [CatSerRange] AxcExt ([ChartFrtInfo] *StartBlock) [CatLab] AXS [CRTMLFRT] [EndBlock] End
PARSER = Group('ivaxis', Req(Axis()) << Req(Begin()) << CatSerRange() << Req(AxcExt()) <<
- Group('future', Opt(Seq(Req(ChartFrtInfo()),
- Many('start-blocks', StartBlock())))) << CatLab() <<
- Req(AXS()) << CRTMLFRT() << EndBlock() << Req(End()))
+ Group('future', Seq(ChartFrtInfo(),
+ Many('start-blocks', StartBlock()))) << CatLab() <<
+ Req(AXS()) << Opt(CRTMLFRT()) << EndBlock() << Req(End()))
class ValueRange(BaseParser):
PARSER = Term(xlsrecord.CHValueRange)
@@ -496,12 +507,15 @@ class DVAXIS(BaseParser):
PARSER = Group('dvaxis', Req(Axis()) << Req(Begin()) << ValueRange() << AXM() << Req(AXS()) << CRTMLFRT() <<
Req(End()))
-class SERIESAXIS(BaseParser): pass #SERIESAXIS = Axis Begin [CatSerRange] AXS [CRTMLFRT] End
+class SERIESAXIS(BaseParser):
+ #SERIESAXIS = Axis Begin [CatSerRange] AXS [CRTMLFRT] End
+ PARSER = Group('series-axis', Req(Axis()) << Req(Begin()) << CatSerRange() <<
+ Req(AXS()) << Opt(CRTMLFRT()) << Req(End()))
class AXES(BaseParser):
#AXES = [IVAXIS DVAXIS [SERIESAXIS] / DVAXIS DVAXIS] *3ATTACHEDLABEL [PlotArea FRAME]
# TODO: recheck it. The rule above leaks some brackets :(
- PARSER = Group('axes', Seq(OneOf(Seq(Req(IVAXIS()), Req(DVAXIS()), SERIESAXIS()),
+ PARSER = Group('axes', Seq(OneOf(Seq(Req(IVAXIS()), Req(DVAXIS()), Opt(SERIESAXIS())),
Seq(Req(DVAXIS()), Req(DVAXIS()))),
Many('attached-labels', ATTACHEDLABEL(), max=3),
Opt(Seq(Req(PlotArea()), Req(FRAME())))))
@@ -519,10 +533,14 @@ class Bar(BaseParser):
class Line(BaseParser):
PARSER = Term(xlsrecord.CHLine)
-class Pie(BaseParser): pass
+class Pie(BaseParser):
+ PARSER = Term(xlsrecord.CHPie)
+
class Area(BaseParser): pass
class Scatter(BaseParser): pass
-class Radar(BaseParser): pass
+class Radar(BaseParser):
+ PARSER = Term(xlsrecord.CHRadar)
+
class RadarArea(BaseParser): pass
class Surf(BaseParser): pass
class SeriesList(BaseParser): pass
@@ -539,19 +557,29 @@ class LD(BaseParser):
PARSER = Group('ld', Req(Legend()) << Req(Begin()) << Req(Pos()) << Req(ATTACHEDLABEL()) <<
Opt(FRAME()) << CrtLayout12() << TEXTPROPS() << CRTMLFRT() << Req(End()))
-class TWODROPBAR(BaseParser): pass
-class CrtLine(BaseParser): pass
+class DropBar(BaseParser):
+ PARSER = Term(xlsrecord.DropBar)
+
+class DROPBAR(BaseParser):
+ # DROPBAR = DropBar Begin LineFormat AreaFormat [GELFRAME] [SHAPEPROPS] End
+ PARSER = Group('drop-bar-root', Req(DropBar()) << Req(Begin()) << Req(LineFormat()) <<
+ Req(AreaFormat()) << Opt(GELFRAME()) << Opt(SHAPEPROPS()) <<
+ Req(End()))
+class CrtLine(BaseParser):
+ PARSER = Term(xlsrecord.CrtLine)
+
class CrtLayout12A(BaseParser): pass
class DAT(BaseParser): pass
class CRT(BaseParser):
+ # It seems 2DROPBAR should be considered *2DROPBAR
#CRT = ChartFormat Begin (Bar / Line / (BopPop [BopPopCustom]) / Pie / Area / Scatter / Radar /
- #RadarArea / Surf) CrtLink [SeriesList] [Chart3d] [LD] [2DROPBAR] *4(CrtLine LineFormat)
+ #RadarArea / Surf) CrtLink [SeriesList] [Chart3d] [LD] [*2DROPBAR] *4(CrtLine LineFormat)
#*2DFTTEXT [DataLabExtContents] [SS] *4SHAPEPROPS End
- PARSER = Group('crt', Req(ChartFormat()) << Req(Begin()) << OneOf(Bar(), Line(), Seq(Req(BobPop()), BobPopCustom()),
+ PARSER = Group('crt', Req(ChartFormat()) << Req(Begin()) << OneOf(Bar(), Line(), Opt(Seq(Req(BobPop()), BobPopCustom())),
Pie(), Area(), Scatter(), Radar(),
RadarArea(), Surf()) <<
- Req(CrtLink()) << SeriesList() << Chart3d() << Opt(LD()) << TWODROPBAR() <<
+ Req(CrtLink()) << SeriesList() << Chart3d() << Opt(LD()) << Many('drop-bars', DROPBAR(), max=2) <<
Many('crt-lines', Seq(Req(CrtLine()),
Req(LineFormat()))) << Many('dft-texts', DFTTEXT()) <<
DataLabExtContents() << Opt(SS()) << Many('shape-props-list', SHAPEPROPS(), max=4) << Req(End()))
diff --git a/src/xlsrecord.py b/src/xlsrecord.py
index fa51c6a..880e3db 100644
--- a/src/xlsrecord.py
+++ b/src/xlsrecord.py
@@ -3737,6 +3737,32 @@ class Chart3DBarShape(BaseRecordHandler):
return ('chart-3dbar-shape', {'riser': self.riser,
'taper': self.taper})
+class DropBar(BaseRecordHandler):
+ def __parseBytes(self):
+ self.gap = self.readSignedInt(2)
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine('Gap: %s' % str(self.gap))
+ # TODO: dump all data
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('drop-bar', {'gap': self.gap})
+
+class CrtLine(BaseRecordHandler):
+ def __parseBytes(self):
+ self.id = self.readUnsignedInt(2)
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine('ID: %s' % str(self.id))
+ # TODO: dump all data
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('crt-line', {'id': self.id})
+
class Chart3d(BaseRecordHandler):
def __parseBytes(self):
self.rot = self.readSignedInt(2)
@@ -4311,8 +4337,46 @@ class CHLine(BaseRecordHandler):
return ('line', {'stacked': self.stacked,
'percent': self.percent,
'shadow': self.shadow})
+
+class CHRadar(BaseRecordHandler):
+ def __parseBytes (self):
+ flags = self.readUnsignedInt(2)
+ self.rdrAxLab = (flags & 0x0001) != 0 # A
+ self.hasShadow = (flags & 0x0002) != 0 # B
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Display category labels: %s"%self.getYesNo(self.rdrAxLab))
+ self.appendLine("Data markers have shadow: %s"%self.getYesNo(self.hasShadow))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('radar', {'rdr-ax-lab': self.rdrAxLab,
+ 'has-shadow': self.hasShadow})
+class CHPie(BaseRecordHandler):
+ def __parseBytes (self):
+ self.start = self.readUnsignedInt(2)
+ self.donut = self.readUnsignedInt(2)
+ flags = self.readUnsignedInt(2)
+ self.hasShadow = (flags & 0x0001) != 0 # A
+ self.showLdrLines = (flags & 0x0002) != 0 # B
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Start angle: %s"%self.getYesNo(self.start))
+ self.appendLine("Size of center hole: %s"%self.getYesNo(self.donut))
+ self.appendLine("Data points have shadow: %s"%self.getYesNo(self.hasShadow))
+ self.appendLine("Show leader lines: %s"%self.getYesNo(self.showLdrLines))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('pie', {'start': self.start,
+ 'donut': self.donut,
+ 'has-shadow': self.hasShadow,
+ 'show-ldr-Lines': self.showLdrLines})
+
class Brai(BaseRecordHandler):
destTypes = [
@@ -4420,3 +4484,27 @@ class MSODrawingSelection(BaseRecordHandler):
self.__parseBytes()
self.msodHdl.fillModel(model)
+class GelFrame(BaseRecordHandler):
+ def __parseBytes (self):
+ self.msodHdl = msodraw.MSODrawHandler(self.bytes, self)
+
+ def parseBytes (self):
+ self.__parseBytes()
+ # it seems there are errors in msodraw parser :(
+ #self.msodHdl.parseBytes()
+
+ def dumpData(self):
+ # we don't dump data!
+ return ('gel-frame', {'bytes': globals.getRawBytes(self.bytes, False, False)})
+
+
+class Pls(BaseRecordHandler):
+ def __parseBytes (self):
+ # DEVMODE is quite a big structure so we don't parse it
+ pass
+
+ def parseBytes (self):
+ self.__parseBytes()
+
+ def dumpData(self):
+ return ('devmode', {'bytes': globals.getRawBytes(self.bytes, False, False)})
diff --git a/src/xlsstream.py b/src/xlsstream.py
index 67c6ee3..68bb59c 100644
--- a/src/xlsstream.py
+++ b/src/xlsstream.py
@@ -73,7 +73,7 @@ recData = {
0x0040: ["BACKUP", "Save Backup Version of the File"],
0x0041: ["PANE", "Number of Panes and Their Position"],
0x0042: ["CODEPAGE/CODENAME", "Default Code Page/VBE Object Name"],
- 0x004D: ["PLS", "Environment-Specific Print Record"],
+ 0x004D: ["PLS", "Environment-Specific Print Record", xlsrecord.Pls],
0x0050: ["DCON", "Data Consolidation Information"],
0x0051: ["DCONREF", "Data Consolidation References", xlsrecord.DConRef],
0x0052: ["DCONNAME", "Data Consolidation Named References", xlsrecord.DConName],
@@ -256,10 +256,10 @@ recData = {
0x1015: ["LEGEND", "Legend Properties", xlsrecord.Legend],
0x1017: ["CHBAR, CHCOLUMN", "?", xlsrecord.CHBar],
0x1018: ["CHLINE", "?", xlsrecord.CHLine],
- 0x1019: ["CHPIE", "?"],
+ 0x1019: ["CHPIE", "Pie/Doughnut chart group", xlsrecord.CHPie],
0x101A: ["CHAREA", "?"],
0x101B: ["CHSCATTER", "?"],
- 0x001C: ["CHCHARTLINE", "?"],
+ 0x101C: ["CHCHARTLINE", "Specifies the presence of lines", xlsrecord.CrtLine],
0x101D: ["CHAXIS", "Chart Axis", xlsrecord.CHAxis],
0x101E: ["CHTICK", "Attributes of the axis labels and tick marks", xlsrecord.Tick],
0x101F: ["CHVALUERANGE", "Chart Axis Value Range", xlsrecord.CHValueRange],
@@ -276,8 +276,8 @@ recData = {
0x1035: ["CHPLOTFRAME", "Chart Plot Frame (indicates the frame that follows)", xlsrecord.PlotArea],
0x103A: ["CHCHART3D", "Attributes of the 3-D plot area", xlsrecord.Chart3d],
0x103C: ["CHPICFORMAT", "?"],
- 0x103D: ["CHDROPBAR", "?"],
- 0x103E: ["CHRADARLINE", "?"],
+ 0x103D: ["CHDROPBAR", "Attributes of the up/down bars between multiple series", xlsrecord.DropBar],
+ 0x103E: ["CHRADARLINE", "Radar chart group", xlsrecord.CHRadar],
0x103F: ["CHSURFACE", "?"],
0x1040: ["CHRADARAREA", "?"],
0x1041: ["CHAXESSET", "Properties of an axis group", xlsrecord.AxisParent],
@@ -299,7 +299,7 @@ recData = {
0x1062: ["AXCEXT", "Additional extension properties of a date axis(2.4.9)", xlsrecord.AxcExt],
0x1064: ["PLOTGROWTH", "Font Scaling Information in the Plot Area", xlsrecord.PlotGrowth],
0x1065: ["CHSIINDEX*", "Part of a group of records which specify the data of a chart", xlsrecord.SIIndex],
- 0x1066: ["CHESCHERFORMAT", "?"]
+ 0x1066: ["CHESCHERFORMAT", "Properties of a fill pattern", xlsrecord.GelFrame]
}
recDataRev = {
commit 689527a0425446bfedce9a7cc2d322bbdafcb996
Author: Sergey Kishchenko <voidwrk at gmail.com>
Date: Thu Sep 22 17:32:02 2011 +0300
Simple future records support was added
diff --git a/misc/test-files.sh b/misc/test-files.sh
index 164952d..5b4c1bc 100755
--- a/misc/test-files.sh
+++ b/misc/test-files.sh
@@ -9,7 +9,21 @@ then
fi
for x in `find $test_dir -name \*.xls`; do
- (python xls-dump.py $x | grep -v "rror inter" > /dev/null) || echo "Flat dump failed for" $x
+ touch tmp
+ python xls-dump.py $x > tmp
+ ret_val=$?
+ if [ $ret_val -eq 0 ]
+ then
+ grep "rror inter" tmp > /dev/null
+ if [ $? -eq 0 ]
+ then
+ echo "Flat dump failed for" $x "- failure in xlsrecord parse"
+ fi
+ else
+ echo "Flat dump failed for" $x "- unrecognised failure"
+ fi
+
+ rm tmp
python xls-dump.py --dump-mode=xml $x > /dev/null || echo "Xml dump failed for" $x
python xls-dump.py --dump-mode=cxml $x > /dev/null || echo "CXml dump failed for" $x
done
diff --git a/src/xlsparser.py b/src/xlsparser.py
index d105f24..1e04ce2 100644
--- a/src/xlsparser.py
+++ b/src/xlsparser.py
@@ -67,7 +67,7 @@ class BaseParser(object):
return Seq(self, other)
def safeParse(parser, stream):
- #print "TRACE:[%s,%s]" % (str(parser), str(stream.tokens[stream.currentIndex]))
+ print "TRACE:[%s,%s]" % (str(parser), str(stream.tokens[stream.currentIndex]))
parsed = None
try:
@@ -443,7 +443,8 @@ class CatSerRange(BaseParser):
class AxcExt(BaseParser):
PARSER = Term(xlsrecord.AxcExt)
-class CatLab(BaseParser): pass
+class CatLab(BaseParser):
+ PARSER = Term(xlsrecord.CatLab)
class IFmtRecord(BaseParser): pass
@@ -457,6 +458,15 @@ class AxisLine(BaseParser):
class TextPropsStream(BaseParser): pass
class ContinueFrt12(BaseParser): pass
+class ChartFrtInfo(BaseParser):
+ PARSER = Term(xlsrecord.ChartFrtInfo)
+
+class StartBlock(BaseParser):
+ PARSER = Term(xlsrecord.StartBlock)
+
+class EndBlock(BaseParser):
+ PARSER = Term(xlsrecord.EndBlock)
+
class AXS(BaseParser):
# AXS = [IFmtRecord] [Tick] [FontX] *4(AxisLine LineFormat) [AreaFormat] [GELFRAME]
# *4SHAPEPROPS [TextPropsStream *ContinueFrt12]
@@ -466,9 +476,15 @@ class AXS(BaseParser):
Opt(Seq(Req(TextPropsStream()), Many('continue-frt12-list', ContinueFrt12()))))
class IVAXIS(BaseParser):
+ # original ABNF:
# IVAXIS = Axis Begin [CatSerRange] AxcExt [CatLab] AXS [CRTMLFRT] End
+ # it seems it's usual too have several future records indicators just after AxcExt and before the End:
+ # IVAXIS = Axis Begin [CatSerRange] AxcExt [ChartFrtInfo *StartBlock] [CatLab] AXS [CRTMLFRT] [EndBlock] End
+
PARSER = Group('ivaxis', Req(Axis()) << Req(Begin()) << CatSerRange() << Req(AxcExt()) <<
- CatLab() << Req(AXS()) << CRTMLFRT() << Req(End()))
+ Group('future', Opt(Seq(Req(ChartFrtInfo()),
+ Many('start-blocks', StartBlock())))) << CatLab() <<
+ Req(AXS()) << CRTMLFRT() << EndBlock() << Req(End()))
class ValueRange(BaseParser):
PARSER = Term(xlsrecord.CHValueRange)
@@ -510,7 +526,10 @@ class Radar(BaseParser): pass
class RadarArea(BaseParser): pass
class Surf(BaseParser): pass
class SeriesList(BaseParser): pass
-class Chart3d(BaseParser): pass
+
+class Chart3d(BaseParser):
+ PARSER = Term(xlsrecord.Chart3d)
+
class Legend(BaseParser):
PARSER = Term(xlsrecord.Legend)
@@ -538,9 +557,13 @@ class CRT(BaseParser):
DataLabExtContents() << Opt(SS()) << Many('shape-props-list', SHAPEPROPS(), max=4) << Req(End()))
class AXISPARENT(BaseParser):
- #AXISPARENT = AxisParent Begin Pos [AXES] 1*4CRT End
+ # Original:
+ # AXISPARENT = AxisParent Begin Pos [AXES] 1*4CRT End
+ # It seems AXISPARENT can have EndBlock before End:
+ # AXISPARENT = AxisParent Begin Pos [AXES] 1*4CRT [EndBlock] End
PARSER = Group('axis-root', Req(AxisParent()) << Req(Begin()) << Req(Pos()) <<
Opt(AXES()) << Many('crt-list', CRT(), min=1, max=4) <<
+ EndBlock() <<
Req(End()))
@@ -552,6 +575,7 @@ class CHARTFORMATS(BaseParser):
#CHARTFOMATS = Chart Begin *2FONTLIST Scl PlotGrowth [FRAME] *SERIESFORMAT *SS ShtProps
#*2DFTTEXT AxesUsed 1*2AXISPARENT [CrtLayout12A] [DAT] *ATTACHEDLABEL [CRTMLFRT]
#*([DataLabExt StartObject] ATTACHEDLABEL [EndObject]) [TEXTPROPS] *2CRTMLFRT End
+ # It seems it is possible to have optional EndBlock just before an end
PARSER = Group('chart-fmt', Req(Chart()) << Req(Begin()) << Many('font-lists', FONTLIST(), max=2) <<
Req(Scl()) << Req(PlotGrowth()) << Opt(FRAME()) << Many('series-fmt-list', SERIESFORMAT()) <<
Many('ss-list', SS()) << Req(ShtProps()) << Many('dft-texts', DFTTEXT(), max=2) <<
@@ -561,7 +585,7 @@ class CHARTFORMATS(BaseParser):
Req(StartObject()))),
Req(ATTACHEDLABEL()),
EndObject())) <<
- Opt(TEXTPROPS()) << Many('crtmlfrt-list', CRTMLFRT()) << Req(End()))
+ Opt(TEXTPROPS()) << Many('crtmlfrt-list', CRTMLFRT()) << EndBlock() << Req(End()))
class Dimensions(BaseParser):
PARSER = Term(xlsrecord.Dimensions)
diff --git a/src/xlsrecord.py b/src/xlsrecord.py
index 21f0beb..fa51c6a 100644
--- a/src/xlsrecord.py
+++ b/src/xlsrecord.py
@@ -112,7 +112,18 @@ class ICV(object):
def dumpIcv(icv):
return {'value': icv.value}
-
+
+class CFRTID(object):
+ def __init__ (self, start, end):
+ self.start = start
+ self.end = end
+
+
+def dumpCfrtid(cfrtid):
+ return {'start': cfrtid.start,
+ 'end': cfrtid.end}
+
+
class BaseRecordHandler(globals.ByteStream):
def __init__ (self, header, size, bytes, strmData):
@@ -221,6 +232,9 @@ Like parseBytes(), the derived classes must overwrite this method."""
def readICV (self):
return ICV(self.readUnsignedInt(2))
+ def readCFRTID (self):
+ return CFRTID(self.readUnsignedInt(2),self.readUnsignedInt(2))
+
class AutofilterInfo(BaseRecordHandler):
def __parseBytes (self):
@@ -3606,7 +3620,7 @@ class ChartFormat(BaseRecordHandler):
flags = self.readUnsignedInt(2)
self.varied = (flags & 0x001) != 0 # A
self.icrt = self.readUnsignedInt(2)
-
+
def parseBytes (self):
self.__parseBytes()
# TODO: dump all data
@@ -3616,6 +3630,99 @@ class ChartFormat(BaseRecordHandler):
return ('chart-format', {'varied': self.varied,
'icrt': self.icrt})
+class ChartFrtInfo(BaseRecordHandler):
+ def __parseBytes(self):
+ self.headerOld = self.readUnsignedInt(4)
+ self.verOriginator = self.readUnsignedInt(1)
+ self.verWriter = self.readUnsignedInt(1)
+ self.cCFRTID = self.readUnsignedInt(2)
+ self.cfrtids = []
+ for x in xrange(self.cCFRTID):
+ self.cfrtids.append(self.readCFRTID())
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Header old: %s" % str(self.headerOld))
+ self.appendLine("verOriginator (version of app that created the file): %s" % str(self.verOriginator))
+ self.appendLine("verWriter (version of app that last saved the file): %s" % str(self.verWriter))
+ self.appendLine("Count of CFRTID records: %s" % str(self.cCFRTID))
+ for cfrtid in self.cfrtids:
+ self.appendLine("CFRTID: [%s, %s]" % (cfrtid.start, cfrtid.end))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('chart-frt-info', {'header-old': self.headerOld,
+ 'ver-originator': self.verOriginator,
+ 'ver-writer': self.verWriter,
+ 'cfrtid-count': self.cCFRTID},
+ [('cfrtid-list', map(lambda x: ('cfrtid', dumpCfrtid(x)),
+ self.cfrtids))])
+
+class StartBlock(BaseRecordHandler):
+ def __parseBytes(self):
+ self.headerOld = self.readUnsignedInt(4)
+ self.objectKind = self.readUnsignedInt(2)
+ self.objectContext = self.readUnsignedInt(2)
+ self.objectInstance1 = self.readUnsignedInt(2)
+ self.objectInstance2 = self.readUnsignedInt(2)
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Header old: %s" % str(self.headerOld))
+ self.appendLine("Object kind: %s" % str(self.objectKind))
+ self.appendLine("Object context: %s" % str(self.objectContext))
+ self.appendLine("Object instance 1: %s" % str(self.objectInstance1))
+ self.appendLine("Object instance 2: %s" % str(self.objectInstance2))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('start-block', {'header-old': self.headerOld,
+ 'object-kind': self.objectKind,
+ 'object-context': self.objectContext,
+ 'object-instance1': self.objectInstance1,
+ 'object-instance2': self.objectInstance2})
+
+class EndBlock(BaseRecordHandler):
+ def __parseBytes(self):
+ self.headerOld = self.readUnsignedInt(4)
+ self.objectKind = self.readUnsignedInt(2)
+ unused = self.readUnsignedInt(2)
+ unused = self.readUnsignedInt(2)
+ unused = self.readUnsignedInt(2)
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Header old: %s" % str(self.headerOld))
+ self.appendLine("Object kind: %s" % str(self.objectKind))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('end-block', {'header-old': self.headerOld,
+ 'object-kind': self.objectKind})
+
+class CatLab(BaseRecordHandler):
+ def __parseBytes(self):
+ self.headerOld = self.readUnsignedInt(4)
+ self.offset = self.readUnsignedInt(2)
+ self.at = self.readUnsignedInt(2)
+ flags = self.readUnsignedInt(2)
+ self.autoCatLabelReal = (flags & 0x0001) != 0 # A
+ reserved = self.readUnsignedInt(2)
+
+ def parseBytes (self):
+ self.__parseBytes()
+ self.appendLine("Header old: %s" % str(self.headerOld))
+ self.appendLine("Offset: %s" % str(self.offset))
+ self.appendLine("At(alignment): %s" % str(self.at))
+ self.appendLine("Auto category label real: %s" % str(self.autoCatLabelReal))
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('catlab', {'header-old': self.headerOld,
+ 'offset': self.offset,
+ 'at': self.at,
+ 'auto-catlabel-real': self.autoCatLabelReal})
+
class Chart3DBarShape(BaseRecordHandler):
def __parseBytes(self):
self.riser = self.readUnsignedInt(1)
@@ -3630,6 +3737,41 @@ class Chart3DBarShape(BaseRecordHandler):
return ('chart-3dbar-shape', {'riser': self.riser,
'taper': self.taper})
+class Chart3d(BaseRecordHandler):
+ def __parseBytes(self):
+ self.rot = self.readSignedInt(2)
+ self.elev = self.readSignedInt(2)
+ self.dist = self.readSignedInt(2)
+ self.height = self.readUnsignedInt(2) # TODO: it can be a signed int too
+ self.depth = self.readUnsignedInt(2)
+ self.gap = self.readUnsignedInt(2)
+
+ flag = self.readUnsignedInt(2)
+ self.perspective = (flag & 0x0001) != 0 # A
+ self.cluster = (flag & 0x0002) != 0 # B
+ self.scaling = (flag & 0x0004) != 0 # C
+ reserved = (flag & 0x0008) != 0 # D
+ self.notPieChart = (flag & 0x0010) != 0 # E
+ self.walls2D = (flag & 0x0020) != 0 # F
+
+ def parseBytes (self):
+ self.__parseBytes()
+ # TODO: dump all data
+
+ def dumpData(self):
+ self.__parseBytes()
+ return ('chart-3d', {'rot': self.rot,
+ 'elev': self.elev,
+ 'dist': self.dist,
+ 'height': self.height,
+ 'depth': self.depth,
+ 'gap': self.gap,
+ 'perspective': self.perspective,
+ 'cluster': self.cluster,
+ 'scaling': self.scaling,
+ 'not-pie-chart': self.notPieChart,
+ 'walls-2d': self.walls2D})
+
class SerToCrt(BaseRecordHandler):
def __parseBytes(self):
self.id = self.readUnsignedInt(2)
diff --git a/src/xlsstream.py b/src/xlsstream.py
index ceb6eeb..67c6ee3 100644
--- a/src/xlsstream.py
+++ b/src/xlsstream.py
@@ -223,6 +223,10 @@ recData = {
0x0802: ["QSISXTAG", "Pivot Table and Query Table Extensions", xlsrecord.PivotQueryTableEx],
0x0809: ["BOF", "Beginning of File", xlsrecord.BOF],
0x0810: ["SXVIEWEX9", "Pivot Table Extensions", xlsrecord.SXViewEx9],
+ 0x0850: ["CHARTFRTINFO", "Versions of the application that edited the file", xlsrecord.ChartFrtInfo],
+ 0x0852: ["CHSTARTBLOCK", "Specifies the beginning of future records", xlsrecord.StartBlock],
+ 0x0853: ["CHENDBLOCK", "Specifies the end of future records", xlsrecord.EndBlock],
+ 0x0856: ["CATLAB", "Attributes of axis label", xlsrecord.CatLab],
0x0858: ["CHPIVOTREF", "Pivot Chart Reference"],
0x0862: ["SHEETLAYOUT", "Tab Color below Sheet Name"],
0x0863: ["BOOKEXT", "Extra Book Info"],
@@ -270,7 +274,7 @@ recData = {
0x1033: ["BEGIN", "Start of Chart Sheet Substream", xlsrecord.Begin],
0x1034: ["END", "End of Chart Sheet Substream", xlsrecord.End],
0x1035: ["CHPLOTFRAME", "Chart Plot Frame (indicates the frame that follows)", xlsrecord.PlotArea],
- 0x103A: ["CHCHART3D", "?"],
+ 0x103A: ["CHCHART3D", "Attributes of the 3-D plot area", xlsrecord.Chart3d],
0x103C: ["CHPICFORMAT", "?"],
0x103D: ["CHDROPBAR", "?"],
0x103E: ["CHRADARLINE", "?"],
More information about the Libreoffice-commits
mailing list