+@cartouche
+@format
+LightMember @result{}
+ Header Title
+ Caption Footnotes
+ Fonts Formats Borders PrintSettings TableSettings
+ Dimensions Data
+ 01
+@end format
+@end cartouche
+
+The following sections go into more detail.
+
+@menu
+* SPV Light Member Header::
+* SPV Light Member Title::
+* PSV Light Member Caption::
+* SPV Light Member Footnotes::
+* SPV Light Member Fonts::
+* SPV Light Member Borders::
+* SPV Light Member Print Settings::
+* SPV Light Member Table Settings::
+* SPV Light Member Formats::
+* SPV Light Member Dimensions::
+* SPV Light Member Categories::
+* SPV Light Member Data::
+* SPV Light Member Value::
+* SPV Light Member ValueMod::
+@end menu
+
+@node SPV Light Member Header
+@subsection Header
+
+An SPV light member begins with a 39-byte header:
+
+@cartouche
+@format
+Header @result{}
+ 01 00
+ (i1 @math{|} i3)[@t{version}]
+ 01 bool*4 int
+ int[@t{min-column-width}] int[@t{max-column-width}]
+ int[@t{min-row-height}] int[@t{max-row-height}]
+ int64[@t{table-id}]
+@end format
+@end cartouche
+
+@code{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(@dots{}) and v3(@dots{}) for
+version-specific formatting (as described previously).
+
+@code{table-id} is a binary version of the @code{tableId} attribute in
+the structure member that refers to the detail member. For example,
+if @code{tableId} is @code{-4122591256483201023}, then @code{table-id}
+would be 0xc6c99d183b300001.
+
+The meaning of the other variable parts of the header is not known.
+
+@node SPV Light Member Title
+@subsection Title
+
+@cartouche
+@format
+Title @result{}
+ Value[@t{title1}] 01?
+ Value[@t{c}] 01? 31
+ Value[@t{title2}] 01?
+@end format
+@end cartouche
+
+The Title, which follows the Header, specifies the pivot table's title
+twice, as @code{title1} and @code{title2}. In the corpus, they are
+always the same.
+
+Whereas the Value in @code{title1} and in @code{title2} are
+appropriate for presentation, and localized to the user's language,
+@code{c} is in English, sometimes less specific, and sometimes less
+well formatted. For example, for a frequency table, @code{title1} and
+@code{title2} name the variable and @code{c} is simply ``Frequencies''.
+
+@node PSV Light Member Caption
+@subsection Caption
+
+@cartouche
+@format
+Caption @result{} Caption1 Caption2
+Caption1 @result{} 31 Value @math{|} 58
+Caption2 @result{} 31 Value @math{|} 58
+@end format
+@end cartouche
+
+The Caption, if present, is shown below the table. Caption2 is
+normally present. Caption1 is only rarely nonempty; it might reflect
+user editing of the caption.
+
+@node SPV Light Member Footnotes
+@subsection Footnotes
+
+@cartouche
+@format
+Footnotes @result{} int[@t{n}] Footnote*[@t{n}]
+Footnote @result{} Value[@t{text}] (58 @math{|} 31 Value[@t{marker}]) byte*4
+@end format
+@end cartouche
+
+Each footnote has @code{text} and an optional customer @code{marker}
+(such as @samp{*}).
+
+@node SPV Light Member Fonts
+@subsection Fonts
+
+@cartouche
+@format
+Fonts @result{} 00 Font*8
+Font @result{}
+ byte[@t{index}] 31
+ string[@t{typeface}] float[@t{size}] int[@t{style}] bool[@t{underline}]
+ int[@t{halign}] int[@t{valign}]
+ string[@t{fgcolor}] string[@t{bgcolor}]
+ byte[@t{alternate}] string[@t{altfg}] string[@t{altbg}]
+ v3(int[@t{left-margin}] int[@t{right-margin}] int[@t{top-margin}] int[@t{bottom-margin}])
+@end format
+@end cartouche
+
+Each Font represents the font style for a different element, in the
+following order: title, caption, footer, corner, column
+labels, row labels, data, and layers.
+
+@code{index} is the 1-based index of the Font, i.e. 1 for the first
+Font, through 8 for the final Font.
+
+@code{typeface} is the string name of the font. In the corpus, this
+is @code{SansSerif} in over 99% of instances and @code{Times New
+Roman} in the rest.
+
+@code{size} is the size of the font, in points. The most common size
+in the corpus is 12 points.
+
+@code{style} is a bit mask. Bit 0 (with value 1) is set for bold, bit
+1 (with value 2) is set for italic.
+
+@code{underline} is 1 if the font is underlined, 0 otherwise.
+
+@code{halign} specifies horizontal alignment: 0 for center, 2 for
+left, 4 for right, 61453 for decimal, 64173 for mixed. Mixed
+alignment varies according to type: string data is left-justified,
+numbers and most other formats are right-justified.
+
+@code{valign} specifies vertical alignment: 0 for center, 1 for top, 3
+for bottom.
+
+@code{fgcolor} and @code{bgcolor} are the foreground color and
+background color, respectively. In the corpus, these are always
+@code{#000000} and @code{#ffffff}, respectively.
+
+@code{alternate} is 01 if rows should alternate colors, 00 if all rows
+should be the same color. When @code{alternate} is 01, @code{altfg}
+and @code{altbg} specify the colors for the alternate rows.
+
+@node SPV Light Member Borders
+@subsection Borders
+
+@cartouche
+@format
+Borders @result{}
+ b1[@t{endian}]
+ be32[@t{n-borders}] Border*[@t{n-borders}]
+ bool[@t{show-grid-lines}]
+ 00 00 00
+
+Border @result{}
+ be32[@t{border-type}]
+ be32[@t{stroke-type}]
+ be32[@t{color}]
+@end format
+@end cartouche
+
+The Borders reflect how borders between regions are drawn.
+
+The fixed value of @code{endian} can be used to validate the
+endianness.
+
+@code{show-grid-lines} is 1 to draw grid lines, otherwise 0.
+
+Each Border describes one kind of border. @code{n-borders} seems to
+always be 19. Each @code{border-type} appears once in order, and they
+correspond to the following borders:
+
+@table @asis
+@item 0
+Title.
+@item 1@dots{}4
+Left, top, right, and bottom outer frame.
+@item 5@dots{}8
+Left, top, right, and bottom inner frame.
+@item 9, 10
+Left and top of data area.
+@item 11, 12
+Horizontal and vertical dimension rows.
+@item 13, 14
+Horizontal and vertical dimension columns.
+@item 15, 16
+Horizontal and vertical category rows.
+@item 17, 18
+Horizontal and vertical category columns.
+@end table
+
+@code{stroke-type} describes how a border is drawn, as one of:
+
+@table @asis
+@item 0
+No line.
+@item 1
+Solid line.
+@item 2
+Dashed line.
+@item 3
+Thick line.
+@item 4
+Thin line.
+@item 5
+Double line.
+@end table
+
+@code{color} is an RGB color. Bits 24--31 are alpha, bits 16--23 are
+red, 8--15 are green, 0--7 are blue. An alpha of 255 indicates an
+opaque color, therefore opaque black is 0xff000000.
+
+@node SPV Light Member Print Settings
+@subsection Print Settings
+
+@cartouche
+@format
+PrintSettings @result{}
+ b1[@t{endian}]
+ bool[@t{layers}]
+ bool[@t{paginate-layers}]
+ bool[@t{fit-width}]
+ bool[@t{fit-length}]
+ bool[@t{top-continuation}]
+ bool[@t{bottom-continuation}]
+ be32[@t{n-orphan-lines}]
+ bestring[@t{continuation-string}]
+@end format
+@end cartouche
+
+The PrintSettings reflect settings for printing. The fixed value of
+@code{endian} can be used to validate the endianness.
+
+@code{layers} is 1 to print all layers, 0 to print only the visible
+layers.
+
+@code{paginate-layers} is 1 to print each layer at the start of a new
+page, 0 otherwise.
+
+@code{fit-width} and @code{fit-length} control whether the table is
+shrunk to fit within a page's width or length, respectively.
+
+@code{n-orphan-lines} is the minimum number of rows or columns to put
+in one part of a table that is broken across pages.
+
+If @code{top-continuation} is 1, then @code{continuation-string} is
+printed at the top of a page when a table is broken across pages for
+printing; similarly for @code{bottom-continuation} and the bottom of a
+page. Usually, @code{continuation-string} is empty.
+
+@node SPV Light Member Table Settings
+@subsection Table Settings
+
+@cartouche
+@format
+TableSettings @result{}
+ be32[@t{endian}]
+ be32
+ be32[@t{current-layer}]
+ bool[@t{omit-empty}]
+ bool[@t{show-row-labels-in-corner}]
+ bool[@t{show-alphabetic-markers}]
+ bool[@t{footnote-marker-position}]
+ v3(
+ byte
+ be32[@t{n}] byte*[@t{n}]
+ bestring
+ bestring[@t{table-look}]
+ 00...
+ )
+@end format
+@end cartouche
+
+The TableSettings reflect display settings. The fixed value of
+@code{endian} can be used to validate the endianness.
+
+@code{current-layer} is the displayed layer.
+
+If @code{omit-empty} is 1, empty rows or columns (ones with nothing in
+any cell) are hidden; otherwise, they are shown.
+
+If @code{show-row-labels-in-corner} is 1, then row labels are shown in
+the upper left corner; otherwise, they are shown nested.
+
+If @code{show-alphabetic-markers} is 1, markers are shown as letters
+(e.g. @samp{a}, @samp{b}, @samp{c}, @dots{}); otherwise, they are
+shown as numbers starting from 1.
+
+When @code{footnote-marker-position} is 1, footnote markers are shown
+as superscripts, otherwise as subscripts.
+
+@code{table-look} is the name of a SPSS ``TableLook'' table style,
+such as ``Default'' or ``Academic''; it is often empty.
+
+TableSettings ends with an arbitrary number of null bytes.
+
+@node SPV Light Member Formats
+@subsection Formats
+
+@cartouche
+@format
+Formats @result{}
+ int[@t{n4}] int*[@t{n4}]
+ string[@t{encoding}]
+ (i0 @math{|} i-1) (00 @math{|} 01) 00 (00 @math{|} 01)
+ int
+ byte[@t{decimal}] byte[@t{grouping}]
+ int[@t{n-ccs}] string*[@t{n-ccs}]
+ v1(i0)
+ v3(count(count(X5) count(X6)))
+
+X5 @result{} byte*33 int[@t{n}] int*[@t{n}]
+X6 @result{}
+ 01 00 (03 @math{|} 04) 00 00 00
+ string[@t{command}] string[@t{subcommand}]
+ string[@t{language}] string[@t{charset}] string[@t{locale}]
+ (00 @math{|} 01) 00 (00 @math{|} 01) (00 @math{|} 01)
+ int
+ byte[@t{decimal}] byte[@t{grouping}]
+ byte*8 01
+ (string[@t{dataset}] string[@t{data file}] i0 int i0)?
+ int[@t{n-ccs}] string*[@t{n-ccs}]
+ 2e (00 @math{|} 01) (i2000000 i0)?
+@end format
+@end cartouche
+
+Observed values of @code{n4} vary from 0 to 17. Out of 7,060 examples
+in the corpus, it is nonzero only 36 times.
+
+@code{encoding} is a character encoding, usually a Windows code page
+such as @code{en_US.windows-1252} or @code{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.
+
+@code{decimal} is the decimal point character. The observed values
+are @samp{.} and @samp{,}.
+
+@code{grouping} is the grouping character. Usually, it is @samp{,} if
+@code{decimal} is @samp{.}, and vice versa. Other observed values are
+@samp{'} (apostrophe), @samp{ } (space), and zero (presumably
+indicating that digits should not be grouped).
+
+@code{n-ccs} is observed as either 0 or 5. When it is 5, the
+following strings are CCA through CCE format strings. @xref{Custom
+Currency Formats,,, pspp, PSPP}. Most commonly these are all
+@code{-,,,} but other strings occur.
+
+@node SPV Light Member Dimensions
+@subsection Dimensions
+
+A pivot table presents multidimensional data. A Dimension identifies
+the categories associated with each dimension.
+
+@cartouche
+@format
+Dimensions @result{} int[@t{n-dims}] Dimension*[@t{n-dims}]
+Dimension @result{} Value[@t{name}] DimUnknown int[@t{n-categories}] Category*[@t{n-categories}]
+DimUnknown @result{}
+ byte[@t{d1}]
+ (00 @math{|} 01 @math{|} 02)[@t{d2}]
+ (i0 @math{|} i2)[@t{d3}]
+ (00 @math{|} 01)[@t{d4}]
+ (00 @math{|} 01)[@t{d5}]
+ 01
+ int[@t{d6}]
+@end format
+@end cartouche
+
+@code{name} is the name of the dimension, e.g. @code{Variables},
+@code{Statistics}, or a variable name.
+
+@code{d1} is usually 0 but many other values have been observed.
+
+@code{d3} is 2 over 99% of the time.
+
+@code{d5} is 0 over 99% of the time.
+
+@code{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.
+
+@node SPV Light Member Categories
+@subsection Categories
+
+Categories are arranged in a tree. Only the leaf nodes in the tree
+are really categories; the others just serve as grouping constructs.
+
+@cartouche
+@format
+Category @result{} Value[@t{name}] (Leaf @math{|} Group)
+Leaf @result{} 00 00 00 i2 int[@t{index}] i0
+Group @result{}
+ (00 @math{|} 01)[@t{merge}] 00 01 (i0 @math{|} i2)[@t{data}]
+ i-1 int[@t{n-subcategories}] Category*[@t{n-subcategories}]
+@end format
+@end cartouche
+
+@code{name} is the name of the category (or group).
+
+A Leaf represents a leaf category. The Leaf's @code{index} is a
+nonnegative integer less than @code{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 @code{n-subcategories} is
+positive, but a few Groups with @code{n-subcategories} 0 has been
+observed.
+
+If a Group's @code{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 @code{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 @code{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.
+
+@node SPV Light Member Data
+@subsection Data
+
+The final part of an SPV light member contains the actual data.
+
+@cartouche
+@format
+Data @result{}
+ int[@t{layers}] int[@t{rows}] int[@t{columns}] int*[@t{n-dimensions}]
+ int[@t{n-data}] Datum*[@t{n-data}]
+Datum @result{} int64[@t{index}] v3(00?) Value
+@end format
+@end cartouche
+
+The values of @code{layers}, @code{rows}, and @code{columns} each
+specifies the number of dimensions displayed in layers, rows, and
+columns, respectively. Any of them may be zero. Their values sum to
+@code{n-dimensions} from Dimensions (@pxref{SPV Light Member
+Dimensions}).
+
+The @code{n-dimensions} integers are a permutation of the 0-based
+dimension numbers. The first @code{layers} integers specify each of
+the dimensions represented by layers, the next @code{rows} integers
+specify the dimensions represented by rows, and the final
+@code{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 @code{index} and a Value. Suppose there are
+@math{d} dimensions and dimension @math{i}, @math{0 \le i < d}, has
+@math{n_i} categories. Consider the datum at coordinates @math{x_i},
+@math{0 \le i < d}, and note that @math{0 \le x_i < n_i}. Then the
+index is calculated by the following algorithm:
+
+@display
+let @i{index} = 0
+for each @math{i} from 0 to @math{d - 1}:
+ @i{index} = (@math{n_i \times} @i{index}) @math{+} @math{x_i}
+@end display
+
+For example, suppose there are 3 dimensions with 3, 4, and 5
+categories, respectively. The datum at coordinates (1, 2, 3) has
+index @math{5 \times (4 \times (3 \times 0 + 1) + 2) + 3 = 33}.
+
+@node SPV Light Member Value
+@subsection Value
+
+Value is used throughout the SPV light member format. It boils down
+to a number or a string.
+
+@cartouche
+@format
+Value @result{} 00? 00? 00? 00? RawValue
+RawValue @result{}
+ 01 ValueMod int[@t{format}] double[@t{x}]
+ @math{|} 02 ValueMod int[@t{format}] double[@t{x}]
+ string[@t{varname}] string[@t{vallab}] (01 @math{|} 02 @math{|} 03)
+ @math{|} 03 string[@t{local}] ValueMod string[@t{id}] string[@t{c}] (00 @math{|} 01)[@t{type}]
+ @math{|} 04 ValueMod int[@t{format}] string[@t{vallab}] string[@t{varname}]
+ (01 @math{|} 02 @math{|} 03) string[@t{s}]
+ @math{|} 05 ValueMod string[@t{varname}] string[@t{varlabel}] (01 @math{|} 02 @math{|} 03)
+ @math{|} ValueMod string[@t{format}] int[@t{n-args}] Argument*[@t{n-args}]
+Argument @result{}
+ i0 Value
+ @math{|} int[@t{x}] i0 Value*[@t{x}@math{+}1] /* @t{x} @math{>} 0 */
+@end format
+@end cartouche
+
+There are several possible encodings, which one can distinguish by the
+first nonzero byte in the encoding.
+
+@table @asis
+@item 01
+The numeric value @code{x}, intended to be presented to the user
+formatted according to @code{format}, which is in the format described
+for system files. @xref{System File Output Formats}, for details.
+Most commonly, @code{format} has width 40 (the maximum).
+
+An @code{x} with the maximum negative double value @code{-DBL_MAX}
+represents the system-missing value SYSMIS. (HIGHEST and LOWEST have
+not been observed.) @xref{System File Format}, for more about these
+special values.
+
+@item 02
+Similar to @code{01}, with the additional information that @code{x} is
+a value of variable @code{varname} and has value label @code{vallab}.
+Both @code{varname} and @code{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.
+
+@item 03
+A text string, in two forms: @code{c} is in English, and sometimes
+abbreviated or obscure, and @code{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, @code{local} is more
+appropriate for a user interface, e.g.@: @code{c} of ``Not a PxP table
+for MCN...'' versus @code{local} of ``Computed only for a PxP table,
+where P must be greater than 1.''
+
+@code{c} and @code{local} are always either both empty or both
+nonempty.
+
+@code{id} is a brief identifying string whose form seems to resemble a
+programming language identifier, e.g.@: @code{cumulative_percent} or
+@code{factor_14}. It is not unique.
+
+@code{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, @code{id} is always the empty string; in the latter case,
+@code{id} is still sometimes empty.
+
+@item 04
+The string value @code{s}, intended to be presented to the user
+formatted according to @code{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.
+
+@code{s} is a value of variable @code{varname} and has value label
+@code{vallab}. @code{varname} is never empty but @code{vallab} is
+commonly empty.
+
+The meaning of the final byte is unknown.
+
+@item 05
+Variable @code{varname}, which is rarely observed as empty in the
+corpus, with variable label @code{varlabel}, which is often empty.
+
+The meaning of the final byte is unknown.
+
+@item 31 or 58
+(These bytes begin a ValueMod.) A format string, analogous to
+@code{printf}, followed by one or more Arguments, each of which has
+one or more values. The format string uses the following syntax:
+
+@table @code
+@item \%
+@itemx \:
+@itemx \[
+@itemx \]
+Each of these expands to the character following @samp{\\}, to escape
+characters that have special meaning in format strings. These are
+effective inside and outside the @code{[@dots{}]} syntax forms
+described below.
+
+@item \n
+Expands to a new-line, inside or outside the @code{[@dots{}]} forms
+described below.
+
+@item ^@var{i}
+Expands to a formatted version of argument @var{i}, which must have
+only a single value. For example, @code{^1} expands to the first
+argument's @code{value}.
+
+@item [:@var{a}:]@var{i}
+Expands @var{a} for each of the values in @var{i}. @var{a}
+should contain one or more @code{^@var{j}} conversions, which are
+drawn from the values for argument @var{i} in order. Some examples
+from the corpus:
+
+@table @code
+@item [:^1:]1
+All of the values for the first argument, concatenated.
+
+@item [:^1\n:]1
+Expands to the values for the first argument, each followed by
+a new-line.
+
+@item [:^1 = ^2:]2
+Expands to @code{@var{x} = @var{y}} where @var{x} is the second
+argument's first value and @var{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.)
+@end table
+
+@item [@var{a}:@var{b}:]@var{i}
+This extends the previous form so that the first values are expanded
+using @var{a} and later values are expanded using @var{b}. For an
+unknown reason, within @var{a} the @code{^@var{j}} conversions are
+instead written as @code{%@var{j}}. Some examples from the corpus:
+
+@table @code
+@item [%1:*^1:]1
+Expands to all of the values for the first argument, separated by
+@samp{*}.
+
+@item [%1 = %2:, ^1 = ^2:]1
+Given appropriate values for the first argument, expands to @code{X =
+1, Y = 2, Z = 3}.
+
+@item [%1:, ^1:]1
+Given appropriate values, expands to @code{1, 2, 3}.
+@end table
+@end table
+
+The format string is localized to the user's locale.
+@end table
+
+@node SPV Light Member ValueMod
+@subsection ValueMod
+
+A ValueMod can specify special modifications to a Value.
+
+@cartouche
+@format
+ValueMod @result{}
+ 31 i0 (i0 @math{|} i1 string[@t{subscript}])
+ v1(00 (i1 @math{|} i2) 00 00 int 00 00)
+ v3(count(FormatString Style ValueModUnknown))
+ @math{|} 31 int[@t{n-refs}] int16*[@t{n-refs}] Format
+ @math{|} 58
+Style @result{} 58 @math{|} 31 01? 00? 00? 00? 01 string[@t{fgcolor}] string[@t{bgcolor}] string[@t{typeface}] byte
+Format @result{} 00 00 count(FormatString Style 58)
+FormatString @result{} count((i0 (58 @math{|} 31 string))?)
+ValueModUnknown @result{} 58 @math{|} 31 i0 i0 i0 i0 01 00 (01 @math{|} 02 @math{|} 08) 00 08 00 0a 00)
+@end format
+@end cartouche
+
+A ValueMod that begins with ``31 i0'' specifies a string to append to
+the main text of the Value, as a subscript. The subscript text is a
+brief indicator, e.g.@: @samp{a} or @samp{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.
+
+A ValueMod that begins with 31 followed by a nonzero ``int'' specifies
+a footnote or footnotes that the Value references. Footnote markers
+are shown appended to the main text of the Value, as superscripts.
+
+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.
+
+@node SPV Legacy Detail Member Binary Format
+@section 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
+variables, 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:
+
+@table @asis
+@item vAF(@var{x})
+In a version 0xaf legacy member, @var{x}; in other versions, nothing.
+(The legacy member header indicates the version; see below.)
+
+@item vB0(@var{x})
+In a version 0xb0 legacy member, @var{x}; in other versions, nothing.
+@end table
+
+A legacy detail member @file{.bin} has the following overall format:
+
+@cartouche
+@format
+LegacyBinary @result{}
+ 00 byte[@t{version}] int16[@t{n-sources}] int[@t{member-size}]
+ Metadata*[@t{n-sources}] Data*[@t{n-sources}]
+@end format
+@end cartouche
+
+@code{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 @code{n-sources} data sources, each of
+which has Metadata and Data.
+
+@code{member-size} is the size of the legacy binary member, in bytes.
+
+The following sections go into more detail.
+
+@menu
+* SPV Legacy Member Metadata::
+* SPV Legacy Member Data::
+@end menu
+
+@node SPV Legacy Member Metadata
+@subsection Metadata
+
+@cartouche
+@format
+Metadata @result{}
+ int[@t{n-data}] int[@t{n-variables}] int[@t{offset}]
+ vAF(byte*32[@t{source-name}])
+ vB0(byte*64[@t{source-name}] int[@t{x}])
+@end format
+@end cartouche
+
+A data source has @code{n-variables} variables, each with
+@code{n-data} data values.
+
+@code{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 @code{tableData} for pivot table data or @code{source0} for
+chart data.
+
+A given Metadata's @code{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 (@pxref{SPV Legacy Member Data}).
+
+The meaning of @code{x} in version 0xb0 is unknown.
+
+@node SPV Legacy Member Data
+@subsection Data
+
+@cartouche
+@format
+Data @result{} NumericData*[@t{n-variables}] StringData?
+NumericData @result{} byte*288[@t{variable-name}] double*[@t{n-data}]
+@end format
+@end cartouche
+
+Data follow the Metadata in the legacy binary format, with sources in
+the same order. Each NumericSeries begins with a @code{variable-name}
+that generally indicates its role in the pivot table, e.g.@: ``cell'',
+``cellFormat'', ``dimension0categories'', ``dimension0group0'',
+followed by the numeric data, one double per datum. A double with the
+maximum negative double @code{-DBL_MAX} represents the system-missing
+value SYSMIS.
+
+@cartouche
+@format
+StringData @result{} i1 string[@t{source-name}] Pairs Labels
+
+Pairs @result{} int[@t{n-string-vars}] PairSeries*[@t{n-string-vars}]
+PairVar @result{} string[@t{pair-var-name}] int[@t{n-pairs}] Pair*[@t{n-pairs}]
+Pair @result{} int[@t{i}] int[@t{j}]
+
+Labels @result{} int[@t{n-labels}] Label*[@t{n-labels}]
+Label @result{} int[@t{frequency}] int[@t{s}]
+@end format
+@end cartouche
+
+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 NumericData, 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 source, 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. @code{n-string-vars} is
+the number of variables in the source that include string data. More
+precisely, it is the 1-based index of the last variable in the source
+that includes any string data; thus, it would be 4 if there are 5
+variables and only the fourth one includes string data.
+
+Each PairVar consists a sequence of 0 or more Pair nonterminals, each
+of which maps from a 0-based index within variable @code{i} to a
+0-based label index @code{j}, e.g.@: pair @code{i} = 2, @code{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 @code{s}. Each label also includes a
+@code{frequency} that reports the number of pairs that reference it
+(although this is not useful).
+
+@node SPV Legacy Detail Member XML Format
+@section Legacy Detail Member XML Format
+
+This format is still under investigation.
+
+The design of the detail XML format is not what one would end up with
+for describing pivot tables. This is because it is a special case
+of a much more general format (``visualization XML'' or ``VizML'')
+that can describe a wide range of visualizations. Most of this
+generality is overkill for tables, and so we end up with a funny
+subset of a general-purpose format.
+
+The important elements of the detail XML format are:
+
+@itemize @bullet
+@item
+Variables. Variables in detail XML roughly correspond to the
+dimensions in a light detail member. There is one variable for each
+dimension, plus one variable for each level of labeling along an axis.
+
+The bulk of variables are defined with @code{sourceVariable} elements.
+The data for these variables comes from the associated
+@code{tableData.bin} member. Some variables are defined, with
+@code{derivedVariable} elements, as a constant or in terms of a
+mapping function from a source variable.
+
+@item
+Assignment of variables to axes. A variable can appear as columns, or
+rows, or layers. The @code{faceting} element and its sub-elements
+describe this assignment.
+@end itemize
+
+All elements have an optional @code{id} attribute. In practice many
+elements are assigned @code{id} attributes that are never referenced.
+
+@menu
+* SPV Detail visualization Element::
+* SPV Detail userSource Element::
+* SPV Detail sourceVariable Element::
+* SPV Detail derivedVariable Element::
+* SPV Detail extension Element::
+* SPV Detail graph Element::
+* SPV Detail location Element::
+* SPV Detail coordinates Element::
+* SPV Detail faceting Element::
+* SPV Detail facetLayout Element::
+@end menu
+
+@node SPV Detail visualization Element
+@subsection The @code{visualization} Element
+
+@format
+Parent: Document root
+Contents:
+ extension?
+ userSource
+ (sourceVariable @math{|} derivedVariable)@math{+}
+ graph
+ labelFrame@math{+}
+ container?
+ style@math{+}
+ layerController?
+@end format
+
+This element has the following attributes.
+
+@defvr {Required} creator
+The version of the software that created this SPV file, as a string of
+the form @code{xxyyzz}, which represents software version xx.yy.zz,
+e.g.@: @code{160001} is version 16.0.1. The corpus includes major
+versions 16 through 19.
+@end defvr
+
+@defvr {Required} date
+The date on the which the file was created, as a string of the form
+@code{YYYY-MM-DD}.
+@end defvr
+
+@defvr {Required} lang
+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.@:
+@code{en-US}, @code{en-GB}, @code{el-GR}, @code{sr-Cryl-RS}.
+@end defvr
+
+@defvr {Required} name
+The title of the pivot table, localized to the output language.
+@end defvr
+
+@defvr {Required} style
+The @code{id} of a @code{style} element (@pxref{SPV Detail style
+element}). This is the base style for the entire pivot table. In
+every example in the corpus, the value is @code{visualizationStyle}
+and the corresponding @code{style} element has no attributes other
+than @code{id}.
+@end defvr
+
+@defvr {Required} type
+A floating-point number. The meaning is unknown.
+@end defvr
+
+@defvr {Required} version
+The visualization schema version number. In the corpus, the value is
+one of 2.4, 2.5, 2.7, and 2.8.
+@end defvr
+
+@node SPV Detail userSource Element
+@subsection The @code{userSource} Element
+
+Parent: @code{visualization} @*
+Contents:
+
+This element has the following attributes.
+
+@defvr {Optional} missing
+Always @code{listwise}.
+@end defvr
+
+@node SPV Detail sourceVariable Element
+@subsection The @code{sourceVariable} Element
+
+Parent: @code{visualization} @*
+Contents: @code{extension}* (@code{format} @math{|} @code{stringFormat})?
+
+This element defines a variable whose values can be used elsewhere in
+the visualization. It ties this element's @code{id} to a variable
+from the @file{tableData.bin} member that corresponds to this
+@file{.xml}.
+
+This element has the following attributes.
+
+@defvr {Required} categorical
+Always set to @code{true}.
+@end defvr
+
+@defvr {Required} source
+Always set to @code{tableData}, the @code{source-name} in the
+corresponding @file{tableData.bin} member (@pxref{SPV Legacy Member
+Metadata}).
+@end defvr
+
+@defvr {Required} sourceName
+The name of a variable within the source, the @code{variable-name} in
+the corresponding @file{tableData.bin} member (@pxref{SPV Legacy
+Member Data}).
+@end defvr
+
+@defvr {Optional} dependsOn
+The @code{variable-name} of a variable linked to this one, so that a
+viewer can work with them together. For a group variable, this is the
+name of the corresponding categorical variable.
+@end defvr
+
+@defvr {Optional} label
+The variable label, if any
+@end defvr
+
+@defvr {Optional} labelVariable
+The @code{variable-name} of a variable whose string values correspond
+one-to-one with the values of this variable and are suitable for use
+as value labels.
+@end defvr
+
+@node SPV Detail derivedVariable Element
+@subsection The @code{derivedVariable} Element
+
+Parent: @code{visualization} @*
+Contents: @code{extension}* (@code{format} @math{|} @code{stringFormat} @code{valueMapEntry}*)
+
+Like @code{sourceVariable}, this element defines a variable whose
+values can be used elsewhere in the visualization. Instead of being
+read from a data source, the variable's data are defined by a
+mathematical expression.
+
+This element has the following attributes.
+
+@defvr {Required} categorical
+Always set to @code{true}.
+@end defvr
+
+@defvr {Required} value
+An expression that defines the variable's value. In theory this could
+be an arbitrary expression in terms of constants, functions, and other
+variables, e.g.@: @math{(@var{var1} + @var{var2}) / 2}. In practice,
+the corpus contains only the following forms of expressions:
+
+@table @code
+@item constant(@var{number})
+@itemx constant(@var{variable})
+A constant. The meaning when a variable is named is unknown.
+Sometimes the ``variable name'' has spaces in it.
+
+@item map(@var{variable})
+Transforms the values in the named @var{variable} using the
+@code{valueMapEntry}s contained within the element.
+@end table
+@end defvr
+
+@defvr {Optional} dependsOn
+The @code{variable-name} of a variable linked to this one, so that a
+viewer can work with them together. For a group variable, this is the
+name of the corresponding categorical variable.
+@end defvr
+
+@menu
+* SPV Detail valueMapEntry Element::
+@end menu
+
+@node SPV Detail valueMapEntry Element
+@subsubsection The @code{valueMapEntry} Element
+
+Parent: @code{derivedVariable} @*
+Contents: empty
+
+A @code{valueMapEntry} element defines a mapping from one or more
+values of a source expression to a target value. (In the corpus, the
+source expression is always just the name of a variable.) Each target
+value requires a separate @code{valueMapEntry}. If multiple source
+values map to the same target value, they can be combined or separate.
+
+@code{valueMapEntry} has the following attributes.
+
+@defvr {Required} from
+A source value, or multiple source values separated by semicolons,
+e.g.@: @code{0} or @code{13;14;15;16}.
+@end defvr
+
+@defvr {Required} to
+The target value.
+@end defvr
+
+@node SPV Detail extension Element
+@subsection The @code{extension} Element
+
+This is a general-purpose ``extension'' element. Readers that don't
+understand a given extension should be able to safely ignore it. The
+attributes on this element, and their meanings, vary based on the
+context. Each known usage is described separately below. The current
+extensions use attributes exclusively, without any nested elements.
+
+@subsubheading @code{visualization} Parent Element
+
+With @code{visualization} as its parent element, @code{extension} has
+the following attributes.
+
+@defvr {Optional} numRows
+An integer that presumably defines the number of rows in the displayed
+pivot table.
+@end defvr
+
+@defvr {Optional} showGridline
+Always set to @code{false} in the corpus.
+@end defvr
+
+@defvr {Optional} minWidthSet
+@defvrx {Optional} maxWidthSet
+Always set to @code{true} in the corpus.
+@end defvr
+
+@subsubheading @code{container} Parent Element
+
+With @code{container} as its parent element, @code{extension} has the
+following attributes.
+
+@defvr {Required} combinedFootnotes
+Always set to @code{true} in the corpus.
+@end defvr
+
+@subsubheading @code{sourceVariable} and @code{derivedVariable} Parent Element
+
+With @code{sourceVariable} or @code{derivedVariable} as its parent
+element, @code{extension} has the following attributes. A given
+parent element often contains several @code{extension} elements that
+specify the meaning of the source data's variables or sources, e.g.@:
+