SPSS Viewer File Format
***********************

SPSS Viewer or .spv files, here called SPV files, are written by SPSS 16 and later to represent the contents of its output editor. This chapter documents the format, based on examination of a corpus of about 500 files from a variety of sources.  This description is detailed enough to read SPV files, but probably not enough to write them.

SPSS 15 and earlier versions use a completely different output format based on the Microsoft Compound Document Format.  This format is not documented here.

An SPV file is a Zip archive that can be read with zipinfo and unzip and similar programs.  The final member in the Zip archive is a file named META-INF/MANIFEST.MF.  This structure makes SPV files resemble Java ``JAR'' files (and ODF files), but whereas a JAR manifest contains a sequence of colon-delimited key/value pairs, an SPV manifest contains the string allowPivoting=true, without a new-line.  (This string may be the best way to identify an SPV file; it is invariant across the corpus.)

The rest of the members in an SPV file's Zip archive fall into two categories: structure and detail members.  Structure member names begin with outputViewerNNNNNNNNNN, where each N is a decimal digit, and end with .xml, and often include the string _heading in between.  Each of these members represents some kind of output item (a table, a heading, a block of text, etc.) or a group of them.  The member whose output goes at the beginning of the document is numbered 0, the next member in the output is numbered 1, and so on.

Structure members contain XML.  This XML is sometimes self-contained, but it often references detail members in the Zip archive, which named as follows:

PREFIX_table.xml and PREFIX_tableData.bin
PREFIX_lightTableData.bin
     The structure of a table plus its data.  Older SPV files pair a PREFIX_table.xml file that describes the table's structure with a binary PREFIX_tableData.bin file that gives its data.  Newer SPV files (the majority of those in the corpus) instead include a single PREFIX_lightTableData.bin file that incorporates both into a single binary format.

PREFIX_warning.xml and PREFIX_warningData.bin
PREFIX_lightWarningData.bin
     Same format used for tables, with a different name.

PREFIX_notes.xml and PREFIX_notesData.bin
PREFIX_lightNotesData.bin
     Same format used for tables, with a different name.

PREFIX_chartData.bin and PREFIX_chart.xml
     The structure of a chart plus its data.  Charts do not have a ``light'' format.

PREFIX_pmml.scf
PREFIX_stats.scf

PREFIX_model.xml
     Not yet investigated.  The corpus contains few examples.

The PREFIX in the names of the detail members is typically an 11-digit decimal number that increases for each item, tending to skip values.  Older SPV files use different naming conventions.  Structure member refer to detail members by name, and so their exact names do not matter to readers as long as they are unique.

Structure Member Format
=======================

Structure members' XML files claim conformance with a collection of XML Schemas.  These schemas are distributed, under a nonfree license, with SPSS binaries.  Fortunately, the schemas are not necessary to understand the structure members.  To a degree, the schemas can even be deceptive because they document elements and attributes that are not in the corpus and do not document elements and attributes that are commonly found there.

Structure members use a different XML namespace for each schema, but these namespaces are not entirely consistent.  In some SPV files, for example, the viewer-tree schema is associated with namespace http://xml.spss.com/spss/viewer-tree and in others with http://xml.spss.com/spss/viewer/viewer-tree (note the additional viewer/).  Under either name, the schema URIs are not resolvable to obtain the schemas themselves.

One may ignore all of the above in interpreting a structure member. The actual XML has a simple and straightforward form that does not require a reader to take schemas or namespaces into account.

The elements found in structure members are documented below.  For each element, we note the possible parent elements and the element's contents.  The contents are specified as pseudo-regular expressions with the following conventions:

text
     XML text content.

CDATA
     XML CDATA content.

element
     The named element.

(...)
     Grouping multiple elements.

[X]
     An optional X.

A | B
     A choice between A and B.

X*
     Zero or more X.

For a diagram illustrating the hierarchy of elements within an SPV structure member, please refer to a PDF version of the manual.

The heading Element
-------------------

Parent: Document root or heading

Contents: [pageSetup] label (container | heading)*

The root of a structure member is a heading, which represents a section of output beginning with a title (the label) and ordinarily followed by content containers or further nested (sub)-sections of output.

The document root heading, only, may also contain a pageSetup element.

The following attributes have been observed on both document root and nested heading elements.

 -- Optional: creator-version
     The version of the software that created this SPV file.  A string of the form xxyyzzww represents software version xx.yy.zz.ww, e.g. 21000001 is version 21.0.0.1.  Trailing pairs of zeros are sometimes omitted, so that 21, 210000, and 21000000 are all version 21.0.0.0 (and the corpus contains all three of those forms).

The following attributes have been observed on document root heading elements only:

 -- Optional: creator
     The directory in the file system of the software that created this SPV file.

 -- Optional: creation-date-time
     The date and time at which the SPV file was written, in a locale-specific format, e.g. Friday, May 16, 2014 6:47:37 PM PDT or lunedì 17 marzo 2014 3.15.48 CET or even Friday, December 5, 2014 5:00:19 o'clock PM EST.

 -- Optional: lockReader
     Whether a reader should be allowed to edit the output.  The possible values are true and false, but the corpus only contains false.

 -- Optional: schemaLocation
     This is actually an XML Namespace attribute.  A reader may ignore it.

The following attributes have been observed only on nested heading elements:

 -- Required: commandName
     The locale-invariant name of the command that produced the output, e.g. Frequencies, T-Test, Non Par Corr.

 -- Optional: visibility
     To what degree the output represented by the element is visible.  The only observed value is collapsed.

 -- Optional: locale
     The locale used for output, in Windows format, which is similar to the format used in Unix with the underscore replaced by a hyphen, e.g. en-US, en-GB, el-GR, sr-Cryl-RS.

 -- Optional: olang
     The output language, e.g. en, it, es, de, pt-BR.

The label Element
-----------------

Parent: heading or container

Contents: text

Every heading and container holds a label as its first child.  The root heading in a structure member always contains the string ``Output''.  Otherwise, the text in label describes what it labels, often by naming the statistical procedure that was executed, e.g. ``Frequencies'' or ``T-Test''.  Labels are often very generic, especially within a container, e.g. ``Title'' or ``Warnings'' or ``Notes''.  Label text is localized according to the output language, e.g. in Italian a frequency table procedure is labeled ``Frequenze''.

The corpus contains one example of an empty label, one that contains no text.

This element has no attributes.

The container Element
---------------------

Parent: heading

Contents: label [table | text]

A container serves to label a table or a text item.

This element has the following attributes.

 -- Required: visibility
     Either visible or hidden, this indicates whether the container's content is displayed.

 -- Optional: text-align
     Presumably indicates the alignment of text within the container.  The only observed value is left.  Observed with nested table and text elements.

 -- Optional: width
     The width of the container in the form Npx, e.g. 1097px.

The text Element (Inside container)
-----------------------------------

Parent: container

Contents: html

This text element is nested inside a container.  There is a different text element that is nested inside a pageParagraph.

This element has the following attributes.

 -- Required: type
     One of title, log, or text.

 -- Optional: commandName
     As on the heading element.  For output not specific to a command, this is simply log.  The corpus contains one example of where commandName is present but set to the empty string.

 -- Optional: creator-version
     As on the heading element.

The html Element
----------------

Parent: text

Contents: CDATA

The CDATA contains an HTML document.  In some cases, the document starts with <html> and ends with </html; in others the html element is implied.  Generally the HTML includes a head element with a CSS stylesheet.  The HTML body often begins with <BR>.  The actual content ranges from trivial to simple: just discarding the CSS and tags yields readable results.

This element has the following attributes.

 -- Required: lang
     This always contains en in the corpus.

The table Element
-----------------

Parent: container

Contents: tableStructure

This element has the following attributes.

 -- Required: commandName
     As on the heading element.

 -- Required: type
     One of table, note, or warning.

 -- Required: subType
     The locale-invariant name for the particular kind of output that this table represents in the procedure.  This can be the same as commandName e.g. Frequencies, or different, e.g. Case Processing Summary.  Generic subtypes Notes and Warnings are often used.

 -- Required: tableId
     A number that uniquely identifies the table within the SPV file, typically a large negative number such as -4147135649387905023.

 -- Optional: creator-version
     As on the heading element.  In the corpus, this is only present for version 21 and up and always includes all 8 digits.

The tableStructure Element
--------------------------

Parent: table

Contents: dataPath

This element has no attributes.

The dataPath Element
--------------------

Parent: tableStructure

Contents: text

Contains the name of the Zip member that holds the table details, e.g. 0000000001437_lightTableData.bin.

This element has no attributes.

The pageSetup Element
---------------------

Parent: heading

Contents: pageHeader pageFooter

This element has the following attributes.

 -- Required: initial-page-number
     Always 1.

 -- Optional: chart-size
     Always as-is or a localization (!) of it (e.g. dimensione attuale, Wie vorgegeben).

 -- Optional: margin-left
 -- Optional: margin-right
 -- Optional: margin-top
 -- Optional: margin-bottom
     Margin sizes in the form SIZEin, e.g. 0.25in.

 -- Optional: paper-height
 -- Optional: paper-width
     Paper sizes in the form SIZEin, e.g. 8.5in by 11in for letter paper or 8.267in by 11.692in for A4 paper.

 -- Optional: reference-orientation
     Always 0deg.

 -- Optional: space-after
     Always 12pt.

The pageHeader and pageFooter Elements
--------------------------------------

Parent: pageSetup

Contents: pageParagraph*

This element has no attributes.

The pageParagraph Element
-------------------------

Parent: pageHeader or pageFooter

Contents: text

Text to go at the top or bottom of a page, respectively.

This element has no attributes.

The text Element (Inside pageParagraph)
---------------------------------------

Parent: pageParagraph

Contents: [CDATA]

This text element is nested inside a pageParagraph.  There is a different text element that is nested inside a container.

The element is either empty, or contains CDATA that holds almost-XHTML text: in the corpus, either an html or p element.  It is almost-XHTML because the html element designates the default namespace as http://xml.spss.com/spss/viewer/viewer-tree instead of an XHTML namespace, and because the CDATA can contain substitution variables: &[Page] for the page number and &[PageTitle] for the page title.

Typical contents (indented for clarity):

     <html xmlns="http://xml.spss.com/spss/viewer/viewer-tree">
         <head></head>
         <body>
             <p style="text-align:right; margin-top: 0">Page &[Page]</p>
         </body>
     </html>

This element has the following attributes.

 -- Required: type
     Always text.

Light Detail Member Format
==========================

This section describes the format of ``light'' detail .bin members.  These members have a binary format which we describe here in terms of a context-free grammar using the following conventions:

NonTerminal => ...
     Nonterminals have CamelCaps names, and => indicates a production.  The right-hand side of a production is often broken across multiple lines.  Break points are chosen for aesthetics only and have no semantic significance.

00, 01, ..., ff.
     Bytes with fixed values are written in hexadecimal:

i0, i1, ..., i9, i10, i11, ...
     32-bit integers with fixed values are written in decimal, prefixed by i.

byte
     An arbitrary byte.

int
     An arbitrary 32-bit integer.

double
     An arbitrary 64-bit IEEE floating-point number.

string
     A 32-bit integer followed by the specified number of bytes of character data.  (The encoding is indicated by the Formats nonterminal.)

X?
     X is optional, e.g. 00? is an optional zero byte.

X*N
     X is repeated N times, e.g. byte*10 for ten arbitrary bytes.

X[NAME]
     Gives X the specified NAME.  Names are used in textual explanations.  They are also used, also bracketed, to indicate counts, e.g. int[n] byte*[n] for a 32-bit integer followed by the specified number of arbitrary bytes.

A | B
     Either A or B.

(X)
     Parentheses are used for grouping to make precedence clear, especially in the presence of |, e.g. in 00 (01 | 02 | 03) 00.

count(X)
     A 32-bit integer that indicates the number of bytes in X, followed by X itself.

v1(X)
     In a version 1 .bin member, X; in version 3, nothing. (The .bin header indicates the version.)

v3(X)
     In a version 3 .bin member, X; in version 1, nothing.

All integer and floating-point values in this format use little-endian byte order.

A ``light'' detail member .bin consists of a number of sections concatenated together, terminated by a byte 01:

     LightMember => Header Title Caption Footnotes Fonts Formats Dimensions Data 01

The following sections go into more detail.

Header
------

An SPV file begins with an 39-byte header:

     Header =>
         01 00
         (i1 | i3)[version]
         01 (00 | 01) byte*21 00 00
         int[table-id] byte*4

version is a version number that affects the interpretation of some of the other data in the member.  We will refer to ``version 1'' and ``version 3'' later on and use v1(...) and v3(...) for version-specific formatting (as described previously).

table-id is a binary version of the tableId attribute in the structure member that refers to the detail member.  For example, if tableId is -4154297861994971133, then table-id would be 0xdca00003.

The meaning of the other variable parts of the header is not known.

Title
-----

     Title =>
         Value[title1] 01?
         Value[c] 01? 31
         Value[title2] 01? 00? 58

The Title, which follows the Header, specifies the pivot table's title twice, as title1 and title2.  In the corpus, they are always the same.

Whereas the Value in title1 and in title2 are appropriate for presentation, and localized to the user's language, c is in English, sometimes less specific, and sometimes less well formatted.  For example, for a frequency table, title1 and title2 name the variable and c is simply ``Frequencies''.

Caption
-------

     Caption => 58 | 31 Value[caption]

The caption, if presented, is shown below the table.

Footnotes
---------

     Footnotes => int[n] Footnote*[n]
     Footnote => Value[text] (58 | 31 Value[marker]) byte*4

Each footnote has text and an optional customer marker (such as *).

Fonts
-----

     Fonts => 00 Font*8
     Font =>
         byte[index] 31 string[typeface] 00 00
         (10 | 20 | 40 | 50 | 70 | 80)[f1] 41
         (i0 | i1 | i2)[f2] 00
         (i0 | i2 | i64173)[f3]
         (i0 | i1 | i2 | i3)[f4]
         string[fgcolor] string[bgcolor] i0 i0 00
         v3(int[f5] int[f6] int[f7] int[f8]))

Each Font represents the font style for a different element, in the following order: title, caption, footnote, row labels, column labels, corner labels, data, and layers.

index is the 1-based index of the Font, i.e. 1 for the first Font, through 8 for the final Font.

typeface is the string name of the font.  In the corpus, this is SansSerif in over 99% of instances and Times New Roman in the rest.

fgcolor and bgcolor are the foreground color and background color, respectively.  In the corpus, these are always #000000 and #ffffff, respectively.

The meaning of the remaining data is unknown.  It seems likely to include font sizes, horizontal and vertical alignment, attributes such as bold or italic, and margins.

The table below lists the values observed in the corpus.  When a cell contains a single value, then 99+% of the corpus contains that value. When a cell contains a pair of values, then the first value is seen in about two-thirds of the corpus and the second value in about the remaining one-third.  In fonts that include multiple pairs, values are correlated, that is, for font 3, f5 = 24, f6 = 24, f7 = 2 appears about two-thirds of the time, as does the combination of f4 = 0, f6 = 10 for font 7.

font   f1   f2   f3   f4   f5   f6   f7   f8
1   40   1       0     0   8   10/11     1   8
2   40   0       2     1   8   10/11     1   1
3   40   0       2   1   24/11   24/ 8   2/3   4
4   40   0       2     3   8   10/11     1   1
5   40   0       0     1   8   10/11     1   4
6   40   0       2     1   8   10/11     1   4
7   40   0   64173   0/1   8   10/11     1   1
8   40   0       2     3   8   10/11     1    4

Formats
-------

     Formats =>
         int[n1] byte*[n1]
         int[n2] byte*[n2]
         int[n3] byte*[n3]
         int[n4] int*[n4]
         string[encoding]
         (i0 | i-1) (00 | 01) 00 (00 | 01)
         int
         byte[decimal] byte[grouping]
         int[n-ccs] string*[n-ccs]
         v1(i0)
         v3(count(count(X5) count(X6)))

     X5 => byte*33 int[n] int*[n]
     X6 =>
         01 00 (03 | 04) 00 00 00
         string[command] string[subcommand]
         string[language] string[charset] string[locale]
         (00 | 01) 00 (00 | 01) (00 | 01)
         int
         byte[decimal] byte[grouping]
         byte*8 01
         (string[dataset] string[datafile] i0 int i0)?
         int[n-ccs] string*[n-ccs]
         2e (00 | 01) (i2000000 i0)?

In every example in the corpus, n1 is 240.  The meaning of the bytes that follow it is unknown.

In every example in the corpus, n2 is 18 and the bytes that follow it are 00 00 00 01 00 00 00 00 00 00 00 00 00 02 00 00 00 00.  The meaning of these bytes is unknown.

In every example in the corpus for version 1, n3 is 16 and the bytes that follow it are 00 00 00 01 00 00 00 01 00 00 00 00 01 01 01 01.  In version 3, observed n3 varies from 117 to 150, and its bytes include a 1-byte count at offset 0x34.  When the count is nonzero, a text string of that length at offset 0x35 is the name of a ``TableLook'', e.g. ``Default'' or ``Academic''.

Observed values of n4 vary from 0 to 17.  Out of 7,060 examples in the corpus, it is nonzero only 36 times.

encoding is a character encoding, usually a Windows code page such as en_US.windows-1252 or it_IT.windows-1252.  The rest of the character strings in the member use this encoding.  The encoding string is itself encoded in US-ASCII.

decimal is the decimal point character.  The observed values are . and ,.

grouping is the grouping character.  Usually, it is , if decimal is ., and vice versa.  Other observed values are ' (apostrophe),   (space), and zero (presumably indicating that digits should not be grouped).

n-ccs is observed as either 0 or 5.  When it is 5, the following strings are CCA through CCE format strings.  See Custom Currency Formats (PSPP).  Most commonly these are all -,,, but other strings occur.

Dimensions
----------

A pivot table presents multidimensional data.  A Dimension identifies the categories associated with each dimension.

     Dimensions => int[n-dims] Dimension*[n-dims]
     Dimension => Value[name] DimUnknown int[n-categories] Category*[n-categories]
     DimUnknown =>
         byte[d1]
         (00 | 01 | 02)[d2]
         (i0 | i2)[d3]
         (00 | 01)[d4]
         (00 | 01)[d5]
         01
         int[d6]

name is the name of the dimension, e.g. Variables, Statistics, or a variable name.

d1 is usually 0 but many other values have been observed.

d3 is 2 over 99% of the time.

d5 is 0 over 99% of the time.

d6 is either -1 or the 0-based index of the dimension, e.g. 0 for the first dimension, 1 for the second, and so on.  The latter is the case 98% of the time in the corpus.

Categories
----------

Categories are arranged in a tree.  Only the leaf nodes in the tree are really categories; the others just serve as grouping constructs.

     Category => Value[name] (Leaf | Group)
     Leaf => 00 00 00 i2 int[index] i0
     Group =>
         (00 | 01)[merge] 00 01 (i0 | i2)[data]
         i-1 int[n-subcategories] Category*[n-subcategories]

name is the name of the category (or group).

A Leaf represents a leaf category.  The Leaf's index is a nonnegative integer less than n-categories in the Dimension in which the Category is nested (directly or indirectly).

A Group represents a Group of nested categories.  Usually a Group contains at least one Category, so that n-subcategories is positive, but a few Groups with n-subcategories 0 has been observed.

If a Group's merge is 00, the most common value, then the group is really a distinct group that should be represented as such in the visual representation and user interface.  If merge is 01, the categories in this group should be shown and treated as if they were direct children of the group's containing group (or if it has no parent group, then direct children of the dimension), and this group's name is irrelevant and should not be displayed.  (Merged groups can be nested!)

A Group's data appears to be i2 when all of the categories within a group are leaf categories that directly represent data values for a variable (e.g. in a frequency table or crosstabulation, a group of values in a variable being tabulated) and i0 otherwise.

Data
----

The final part of an SPV light member contains the actual data.

     Data =>
         int[layers] int[rows] int[columns] int*[n-dimensions]
         int[n-data] Datum*[n-data]
     Datum => int64[index] v3(00?) Value

The values of layers, rows, and columns each specifies the number of dimensions displayed in layers, rows, and columns, respectively.  Any of them may be zero.  Their values sum to n-dimensions from Dimensions (see SPV Light Member Dimensions).

The n-dimensions integers are a permutation of the 0-based dimension numbers.  The first layers integers specify each of the dimensions represented by layers, the next rows integers specify the dimensions represented by rows, and the final columns integers specify the dimensions represented by columns. When there is more than one dimension of a given kind, the inner dimensions are given first.

The format of a Datum varies slightly from version 1 to version 3: in version 1 it allows for an extra optional 00 byte.

A Datum consists of an index and a Value.  Suppose there are d dimensions and dimension i, 0 \le i < d, has n_i categories.  Consider the datum at coordinates x_i, 0 \le i < d, and note that 0 \le x_i < n_i.  Then the index is calculated by the following algorithm:

     let index = 0
     for each i from 0 to d - 1:
         index = (n_i \times index) + x_i

For example, suppose there are 3 dimensions with 3, 4, and 5 categories, respectively.  The datum at coordinates (1, 2, 3) has index 5 \times (4 \times (3 \times 0 + 1) + 2) + 3 = 33.

Value
-----

Value is used throughout the SPV light member format.  It boils down to a number or a string.

     Value => 00? 00? 00? 00? RawValue
     RawValue =>
         01 ValueMod int[format] double[x]
       | 02 ValueMod int[format] double[x]
         string[varname] string[vallab] (01 | 02 | 03)
       | 03 string[local] ValueMod string[id] string[c] (00 | 01)[type]
       | 04 ValueMod int[format] string[vallab] string[varname]
         (01 | 02 | 03) string[s]
       | 05 ValueMod string[varname] string[varlabel] (01 | 02 | 03)
       | ValueMod string[format] int[n-args] Argument*[n-args]
     Argument =>
         i0 Value
       | int[x] i0 Value*[x+1]      /* x > 0 */

There are several possible encodings, which one can distinguish by the first nonzero byte in the encoding.

01
     The numeric value x, intended to be presented to the user formatted according to format, which is in the format described for system files.  See System File Output Formats, for details. Most commonly, format has width 40 (the maximum).

     An x with the maximum negative double value -DBL_MAX represents the system-missing value SYSMIS.  (HIGHEST and LOWEST have not been observed.)  See System File Format, for more about these special values.

02
     Similar to 01, with the additional information that x is a value of variable varname and has value label vallab. Both varname and vallab can be the empty string, the latter very commonly.

     The meaning of the final byte is unknown.  Possibly it is connected to whether the value or the label should be displayed.

03
     A text string, in two forms: c is in English, and sometimes abbreviated or obscure, and local is localized to the user's locale.  In an English-language locale, the two strings are often the same, and in the cases where they differ, local is more appropriate for a user interface, e.g. c of ``Not a PxP table for MCN...'' versus local of ``Computed only for a PxP table, where P must be greater than 1.''

     c and local are always either both empty or both nonempty.

     id is a brief identifying string whose form seems to resemble a programming language identifier, e.g. cumulative_percent or factor_14.  It is not unique.

     type is 00 for text taken from user input, such as syntax fragment, expressions, file names, data set names, and 01 for fixed text strings such as names of procedures or statistics.  In the former case, id is always the empty string; in the latter case, id is still sometimes empty.

04
     The string value s, intended to be presented to the user formatted according to format.  The format for a string is not too interesting, and the corpus contains many clearly invalid formats like A16.39 or A255.127 or A134.1, so readers should probably ignore the format entirely.

     s is a value of variable varname and has value label vallab.  varname is never empty but vallab is commonly empty.

     The meaning of the final byte is unknown.

05
     Variable varname, which is rarely observed as empty in the corpus, with variable label varlabel, which is often empty.

     The meaning of the final byte is unknown.

31 or 58
     (These bytes begin a ValueMod.)  A format string, analogous to printf, followed by one or more Arguments, each of which has one or more values.  The format string uses the following syntax:

     \%
     \:
     \[
     \]
          Each of these expands to the character following \\, to escape characters that have special meaning in format strings.  These are effective inside and outside the [...]  syntax forms described below.

     \n
          Expands to a new-line, inside or outside the [...] forms described below.

     ^I
          Expands to a formatted version of argument I, which must have only a single value.  For example, ^1 expands to the first argument's value.

     [:A:]I
          Expands A for each of the values in I.  A should contain one or more ^J conversions, which are drawn from the values for argument I in order.  Some examples from the corpus:

          [:^1:]1
               All of the values for the first argument, concatenated.

          [:^1\n:]1
               Expands to the values for the first argument, each followed by a new-line.

          [:^1 = ^2:]2
               Expands to X = Y where X is the second argument's first value and Y is its second value.  (This would be used only if the argument has two values.  If there were more values, the second and third values would be directly concatenated, which would look funny.)

     [A:B:]I
          This extends the previous form so that the first values are expanded using A and later values are expanded using B.  For an unknown reason, within A the ^J conversions are instead written as %J.  Some examples from the corpus:

          [%1:*^1:]1
               Expands to all of the values for the first argument, separated by *.

          [%1 = %2:, ^1 = ^2:]1
               Given appropriate values for the first argument, expands to X = 1, Y = 2, Z = 3.

          [%1:, ^1:]1
               Given appropriate values, expands to 1, 2, 3.

     The format string is localized to the user's locale.

ValueMod
--------

A ValueMod can specify special modifications to a Value.

     ValueMod =>
         31 i0 (i0 | i1 string[subscript])
         v1(00 (i1 | i2) 00 00 int 00 00)
         v3(count(FormatString Style ValueModUnknown))
       | 31 i1 int[footnote-number] Format
       | 31 i2 (00 | 01 | 02) 00 (i1 | i2 | i3) Format
       | 31 i3 00 00 01 00 i2 Format
       | 58
     Style => 58 | 31 01? 00? 00? 00? 01 string[fgcolor] string[bgcolor] string[typeface] byte
     Format => 00 00 count(FormatString Style 58)
     FormatString => count((i0 (58 | 31 string))?)
     ValueModUnknown => 58 | 31 i0 i0 i0 i0 01 00 (01 | 02 | 08) 00 08 00 0a 00)

The footnote-number, if present, specifies a footnote that the Value references.  The footnote's marker is shown appended to the main text of the Value, as a superscript.

The subscript, if present, specifies a string to append to the main text of the Value, as a subscript.  The subscript text is a brief indicator, e.g. a or a,b, with its meaning indicated by the table caption.  In this usage, subscripts are similar to footnotes; one apparent difference is that a Value can only reference one footnote but a subscript can list more than one letter.

The Format, if present, is a format string for substitutions using the syntax explained previously.  It appears to be an English-language version of the localized format string in the Value in which the Format is nested.

The Style, if present, changes the style for this individual Value.

Legacy Detail Member Binary Format
==================================

Whereas the light binary format represents everything about a given pivot table, the legacy binary format conceptually consists of a number of named sources, each of which consists of a number of named series, each of which is a 1-dimensional array of numbers or strings or a mix.  Thus, the legacy binary member format is quite simple.

This section uses the same context-free grammar notation as in the previous section, with the following additions:

vAF(X)
     In a version 0xaf legacy member, X; in other versions, nothing. (The legacy member header indicates the version; see below.)

vB0(X)
     In a version 0xb0 legacy member, X; in other versions, nothing.

A legacy detail member .bin has the following overall format:

     LegacyBinary =>
         00 byte[version] int16[n-sources] int[member-size]
         Metadata*[n-sources] Data*[n-sources]

version is a version number that affects the interpretation of some of the other data in the member.  Versions 0xaf and 0xb0 are known.  We will refer to ``version 0xaf'' and ``version 0xb0'' members later on.

A legacy member consists of n-sources data sources, each of which has Metadata and Data.

member-size is the size of the legacy binary member, in bytes.

The following sections go into more detail.

Metadata
--------

     Metadata =>
         int[per-series] int[n-series] int[offset]
         vAF(byte*32[source-name])
         vB0(byte*64[source-name] int[x])

A data source consists of n-series series of data, with per-series data values per series.

source-name is a 32- or 64-byte string padded on the right with zero bytes.  The names that appear in the corpus are very generic, usually tableData or source0.

A given Metadata's offset is the offset, in bytes, from the beginning of the member to the start of the corresponding Data.  This allows programs to skip to the beginning of the data for a particular source; it is also important to determine whether a source includes any string data (see SPV Legacy Member Data).

The meaning of x in version 0xb0 is unknown.

Data
----

     Data => NumericData StringData?
     NumericData => NumericSeries*[n-series]
     NumericSeries => byte*288[series-name] double*[per-series]

Data follow the Metadata in the legacy binary format, with sources in the same order.  Each NumericSeries begins with a series-name that generally indicates its role in the pivot table, e.g. ``cell'', ``cellFormat'', ``dimension0categories'', ``dimension0group0'', followed by the numeric data, one double per element in the series.  A double with the maximum negative double -DBL_MAX represents the system-missing value SYSMIS.

     StringData => i1 string[source-name] Pairs Labels

     Pairs => int[n-string-series] PairSeries*[n-string-series]
     PairSeries => string[pair-series-name] int[n-pairs] Pair*[n-pairs]
     Pair => int[i] int[j]

     Labels => int[n-labels] Label*[n-labels]
     Label => int[frequency] int[s]

A source may include a mix of numeric and string data values.  When a source includes any string data, the data values that are strings are set to SYSMIS in the NumericSeries, and StringData follows the NumericData.  A source that contains no string data omits the StringData.  To reliably determine whether a source includes StringData, the reader should check whether the offset following the NumericData is the offset of the next series, as indicated by its Metadata (or the end of the member, in the case of the last source).

StringData repeats the name of the source (from Metadata).

The string data overlays the numeric data.  n-string-series is the number of series within the source that include string data.  More precisely, it is the 1-based index of the last series in the source that includes any string data; thus, it would be 4 if there are 5 series and only the fourth one includes string data.

Each PairSeries consists a sequence of 0 or more Pair nonterminals, each of which maps from a 0-based index within series i to a 0-based label index j, e.g. pair i = 2, j = 3, means that the third data value (with value SYSMIS) is to be replaced by the string of the fourth Label.

The labels themselves follow the pairs.  The valuable part of each label is the string s.  Each label also includes a frequency that reports the number of pairs that reference it (although this is not useful).

Legacy Detail Member XML Format
===============================

This format is still under investigation.