spv: Encode and decode AHEX format in light binary members.
[pspp] / doc / dev / spv-file-format.texi
index e8db27c4b96b3da0faba710a13fa32e1521873b4..0aa53083b95456eb63ef92298565bdee111416ea 100644 (file)
@@ -14,7 +14,7 @@
 SPSS Viewer or @file{.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 3,000 files from a variety of sources.  This description is
+about 8,000 files from a variety of sources.  This description is
 detailed enough to both read and write SPV files.
 
 SPSS 15 and earlier versions instead use @file{.spo} files, which have
@@ -29,7 +29,12 @@ 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 @samp{allowPivoting=true}, without a
 new-line.  PSPP uses this string to identify an SPV file; it is
-invariant across the corpus.
+invariant across the corpus.@footnote{SPV files always begin with the
+7-byte sequence 50 4b 03 04 14 00 08, but this is not a useful magic
+number because most Zip archives start the same way.}@footnote{SPSS
+writes @file{META-INF/MANIFEST.MF} to every SPV file, but it does not
+read it or even require it to exist, so using different contents,
+e.g.@: as @samp{allowingPivot=false} has no effect.}
 
 The rest of the members in an SPV file's Zip archive fall into two
 categories: @dfn{structure} and @dfn{detail} members.  Structure
@@ -132,7 +137,7 @@ container
    :page-break-before=(always)?
    :text-align=(left | center)?
    :width=dimension
-=> label (table | container_text | graph | model | object | image)
+=> label (table | container_text | graph | model | object | image | tree)
 @end example
 
 Each attribute specification begins with @samp{:} followed by the
@@ -153,11 +158,23 @@ Either @code{true} or @code{false}.
 @item dimension
 A floating-point number followed by a unit, e.g.@: @code{10pt}.  Units
 in the corpus include @code{in} (inch), @code{pt} (points, 72/inch),
-@code{px} (``device-independent pixels'', 96/inch), and @code{cm}.
-The corpus also contains localized names for units: @code{인치} for
-inch, @code{пт} for points, and @code{см} for centimeters.  If the
-unit is omitted then points should be assumed.  The number and unit
-may be separated by white space.
+@code{px} (``device-independent pixels'', 96/inch), and @code{cm}.  If
+the unit is omitted then points should be assumed.  The number and
+unit may be separated by white space.
+
+The corpus also includes localized names for units.  A reader must
+understand these to properly interpret the dimension:
+
+@table @asis
+@item inch
+@code{인치}, @code{pol.}, @code{cala}, @code{cali}
+
+@item point
+@code{пт}
+
+@item centimeter
+@code{см}
+@end table
 
 @item real
 A floating-point number.
@@ -277,7 +294,8 @@ information, and the CSS from the embedded HTML:
 * SPV Structure table Element::
 * SPV Structure graph Element::
 * SPV Structure model Element::
-* SPV Structure dataPath and path Elements::
+* SPV Structure tree Element::
+* SPV Structure Path Elements::
 * SPV Structure pageSetup Element::
 * SPV Structure @code{text} Element (Inside @code{pageParagraph})::
 @end menu
@@ -304,7 +322,7 @@ heading
 @end example
 
 The root of a structure member is a @code{heading}, which represents a
-section of output beginning with a title (the @code{label}) and
+section of output beginning with a @code{label} and
 ordinarily followed by content containers or further nested
 (sub)-sections of output.  Unlike heading elements in HTML and other
 common document formats, which precede the content that they head,
@@ -343,8 +361,8 @@ December 5, 2014 5:00:19 o'clock PM EST}.
 
 @defvr {Attribute} @code{lockReader}
 Whether a reader should be allowed to edit the output.  The possible
-values are @code{true} and @code{false}, but the corpus only contains
-@code{false}.
+values are @code{true} and @code{false}.  The value @code{false} is by
+far the most common.
 @end defvr
 
 @defvr {Attribute} @code{schemaLocation}
@@ -383,17 +401,21 @@ label => TEXT
 @end example
 
 Every @code{heading} and @code{container} holds a @code{label} as its
-first child.  The root @code{heading} in a structure member always
-contains the string ``Output'' (localized).  Otherwise, the text in
-@code{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 @code{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 a few examples of empty labels, ones that contain
-no text.
+first child.  The label text is what appears in the outline pane of
+the GUI's viewer window.  PSPP also puts it into the outline of PDF
+output.  The label text doesn't appear in the output itself.
+
+The text in @code{label} describes what it labels, often by naming the
+statistical procedure that was executed, e.g.@: ``Frequencies'' or
+``T-Test''.  The root @code{heading} in a structure member is normally
+``Output''.  Labels are often very generic, especially within a
+@code{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 user can edit labels to be anything they want.  The corpus
+contains a few examples of empty labels, ones that contain no text,
+probably as a result of user editing.
 
 @node SPV Structure container Element
 @subsection The @code{container} Element
@@ -404,7 +426,7 @@ container
    :page-break-before=(always)?
    :text-align=(left | center)?
    :width=dimension
-=> label (table | container_text | graph | model | object | image)
+=> label (table | container_text | graph | model | object | image | tree)
 @end example
 
 A @code{container} serves to contain and label a @code{table},
@@ -495,9 +517,30 @@ inclusive.
 
 The CSS in the corpus is simple.  To understand it, a parser only
 needs to be able to skip white space, @code{<!--}, and @code{-->}, and
-parse style only for @code{p} elements.  Only @code{font-weight},
-@code{font-style}, @code{font-decoration}, @code{font-family}, and
-@code{font-size} matter.
+parse style only for @code{p} elements.  Only the following properties
+matter:
+
+@table @code
+@item color
+In the form @code{@var{rr}@var{gg}@var{bb}}, e.g. @code{000000}, with
+no leading @samp{#}.
+
+@item font-weight
+Either @code{bold} or @code{normal}.
+
+@item font-style
+Either @code{italic} or @code{normal}.
+
+@item text-decoration
+Either @code{underline} or @code{normal}.
+
+@item font-family
+A font name, commonly @code{Monospaced} or @code{SansSerif}.
+
+@item font-size
+Values claim to be in points, e.g.@: @code{14pt}, but the values are
+actually in ``device-independent pixels'' (px), at 96/inch.
+@end table
 
 This element has the following attributes.
 
@@ -525,7 +568,7 @@ table
    :type[table_type]=(table | note | warning)
 => tableProperties? tableStructure
 
-tableStructure => path? dataPath
+tableStructure => path? dataPath csvPath?
 @end example
 
 This element has the following attributes.
@@ -573,7 +616,9 @@ graph
    :editor?
    :refMapId?
    :refMapURI?
-=> dataPath? path
+   :csvFileIds?
+   :csvFileNames?
+=> dataPath? path csvPath?
 @end example
 
 This element represents a graph.  The @code{dataPath} and @code{path}
@@ -581,12 +626,21 @@ elements name the Zip members that give the details of the graph.
 Normally, both elements are present; there is only one counterexample
 in the corpus.
 
+@code{csvPath} only appears in one SPV file in the corpus, for two
+graphs.  In these two cases, @code{dataPath}, @code{path}, and
+@code{csvPath} all appear.  These @code{csvPath} name Zip members with
+names of the form @file{@var{number}_csv.bin}, where @var{number} is a
+many-digit number and the same as the @code{csvFileIds}.  The named
+Zip members are CSV text files (despite the @file{.bin} extension).
+The CSV files are encoded in UTF-8 and begin with a U+FEFF byte-order
+marker.
+
 @node SPV Structure model Element
 @subsection The @code{model} Element
 
 @example
 model
-   :PMMLContainerId
+   :PMMLContainerId?
    :PMMLId
    :StatXMLContainerId
    :VDPId
@@ -594,7 +648,7 @@ model
    :commandName
    :creator-version
    :mainViewName
-=> ViZml? path | pmmlContainerPath statsContainerPath
+=> ViZml? dataPath? path | pmmlContainerPath statsContainerPath
 
 pmmlContainerPath => TEXT
 
@@ -614,13 +668,31 @@ strings, and @code{path} names an Zip member that contains XML.
 Alternatively, @code{pmmlContainerPath} and @code{statsContainerPath}
 name Zip members with @file{.scf} extension.
 
-@node SPV Structure dataPath and path Elements
-@subsection The @code{dataPath} and @code{path} Elements
+@node SPV Structure tree Element
+@subsection The @code{tree} Element
+
+@example
+tree
+   :commandName
+   :creator-version
+   :name
+   :type
+=> dataPath path
+@end example
+
+This element represents a tree.  The @code{dataPath} and @code{path}
+elements name the Zip members that give the details of the tree.
+The details are unexplored.
+
+@node SPV Structure Path Elements
+@subsection Path Elements
 
 @example
 dataPath => TEXT
 
 path => TEXT
+
+csvPath => TEXT
 @end example
 
 These element contain the name of the Zip members that hold details
@@ -799,17 +871,17 @@ A byte with value 0 or 1.
 
 @item int16
 @itemx be16
-A 16-bit integer in little-endian or big-endian byte order,
+A 16-bit unsigned integer in little-endian or big-endian byte order,
 respectively.
 
 @item int32
 @itemx be32
-A 32-bit integer in little-endian or big-endian byte order,
+A 32-bit unsigned integer in little-endian or big-endian byte order,
 respectively.
 
 @item int64
 @itemx be64
-A 64-bit integer in little-endian or big-endian byte order,
+A 64-bit unsigned integer in little-endian or big-endian byte order,
 respectively.
 
 @item double
@@ -820,9 +892,9 @@ A 32-bit IEEE floating-point number.
 
 @item string
 @itemx bestring
-A 32-bit integer, in little-endian or big-endian byte order,
-respectively, followed by the specified number of bytes of character
-data.  (The encoding is indicated by the Formats nonterminal.)
+A 32-bit unsigned integer, in little-endian or big-endian byte order,
+respectively, followed by the specified number of bytes of UTF-8
+encoded character data.
 
 @item @var{x}?
 @var{x} is optional, e.g.@: 00? is an optional zero byte.
@@ -846,8 +918,9 @@ in the presence of @math{|}, e.g.@: in 00 (01 @math{|} 02 @math{|} 03)
 
 @item count(@var{x})
 @itemx becount(@var{x})
-A 32-bit integer, in little-endian or big-endian byte order, respectively,
-that indicates the number of bytes in @var{x}, followed by @var{x} itself.
+A 32-bit unsigned integer, in little-endian or big-endian byte order,
+respectively, that indicates the number of bytes in @var{x}, followed
+by @var{x} itself.
 
 @item v1(@var{x})
 In a version 1 @file{.bin} member, @var{x}; in version 3, nothing.
@@ -988,12 +1061,19 @@ The @code{caption}, if present, is shown below the table.
 
 @example
 Footnotes => int32[n-footnotes] Footnote*[n-footnotes]
-Footnote => Value[text] (58 @math{|} 31 Value[marker]) byte*4
+Footnote => Value[text] (58 @math{|} 31 Value[marker]) int32[show]
 @end example
 
 Each footnote has @code{text} and an optional custom @code{marker}
 (such as @samp{*}).
 
+The syntax for Value would allow footnotes (and their markers) to
+reference other footnotes, but in practice this doesn't work.
+
+@code{show} is a 32-bit signed integer.  It is positive to show the
+footnote or negative to hide it.  Its magnitude is often 1, and in
+other cases tends to be the number of references to the footnote.
+
 @node SPV Light Member Areas
 @subsection Areas
 
@@ -1222,8 +1302,9 @@ user-specified Keeps.  They seems to indicate a conversion from rows
 or columns to pixel or point offsets.
 
 @code{notes} is a text string that contains user-specified notes.  It
-is displayed when the user hovers the cursor over the table, like
-``alt text'' on a webpage.  It is not printed.  It is usually empty.
+is displayed when the user hovers the cursor over the table, like text
+in the @code{title} attribute in HTML.  It is not printed.  It is
+usually empty.
 
 @code{table-look} is the name of a SPSS ``TableLook'' table style,
 such as ``Default'' or ``Academic''; it is often empty.
@@ -1255,9 +1336,8 @@ If @code{n-widths} is nonzero, then the accompanying integers are
 column widths as manually adjusted by the user.
 
 @code{locale} is a locale including an encoding, 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{en_US.windows-1252} or @code{it_IT.windows-1252}.  The encoding
+string (like other strings in the member) is encoded in UTF-8.
 
 @code{epoch} is the year that starts the epoch.  A 2-digit year is
 interpreted as belonging to the 100 years beginning at the epoch.  The
@@ -1289,7 +1369,7 @@ Y1 =>
     string[language] string[charset] string[locale]
     bool bool bool bool
     Y0
-Y2 => CustomCurrency byte[missing] bool[x16]
+Y2 => CustomCurrency byte[missing] bool[x17]
 @end example
 
 @code{command} describes the statistical procedure that generated the
@@ -1310,7 +1390,7 @@ a missing value.  It is always observed as @samp{.}.
 X0 repeats @code{decimal}, @code{grouping}, CustomCurrency, and
 @code{missing} already included in Formats.
 
-A writer may safely use false for @code{x16}.
+A writer may safely use false for @code{x17}.
 
 @subsubheading X1
 
@@ -1318,14 +1398,16 @@ X1 only appears in version 3 members.
 
 @example
 X1 =>
-    00 byte[x14] bool[x15]
+    bool
+    byte[show-title]
+    bool[x16]
     byte[lang]
     byte[show-variables]
     byte[show-values]
-    int32[x17] int32[x18]
+    int32[x18] int32[x19]
     00*17
-    bool[x19]
-    01
+    bool[x20]
+    bool[show-caption]
 @end example
 
 @code{lang} may indicate the language in use.  Some values seem to be
@@ -1345,8 +1427,13 @@ means to display the value, 2 to display the value label when
 available, 3 to display both.  Again, the most common value is 0,
 which probably means to use a global default.
 
-A writer may safely use 1 for @code{x14}, false for @code{x15}, -1 for
-@code{x17} and @code{x18}, and false for @code{x19}.
+@code{show-title} is 1 to show the caption, 10 to hide it.
+
+@code{show-caption} is true to show the caption, false to hide it.
+
+A writer may safely use false for @code{x14}, false
+for @code{x16}, -1 for @code{x18} and @code{x19}, and false for
+@code{x20}.
 
 @subsubheading X2
 
@@ -1381,12 +1468,12 @@ X3 only appears in version 3 members.
 
 @example
 X3 =>
-    01 00 byte[x20] 00 00 00
+    01 00 byte[x21] 00 00 00
     Y1
     double[small] 01
     (string[dataset] string[datafile] i0 int32[date] i0)?
     Y2
-    (int32 i0)?
+    (int32[x22] i0)?
 @end example
 
 @code{date} is a date, as seconds since the epoch, i.e.@: since
@@ -1399,8 +1486,11 @@ X3 repeats @code{decimal}, @code{grouping}, CustomCurrency, and
 @code{command-local}, @code{language}, @code{charset}, and
 @code{locale} have the same meaning as in X0.
 
-@code{small} is a small real number, e.g.@: .001.  Numbers smaller
-than this in absolute value are displayed in scientific notation.
+@code{small} is a small real number.  In the corpus, it overwhelmingly
+takes the value 0.0001, with zero occasionally seen.  Nonzero numbers
+with format 40 (@pxref{SPV Light Member Value}) whose magnitudes are
+smaller than displayed in scientific notation.  (Thus, a @code{small}
+of zero prevents scientific notation from being chosen.)
 
 Sometimes @code{dataset}, @code{datafile}, and @code{date} are present
 and other times they are absent.  The reader can distinguish by
@@ -1408,8 +1498,10 @@ assuming that they are present and then checking whether the
 presumptive @code{dataset} contains a null byte (a valid string never
 will).
 
-A writer may safely use 4 for @code{x20} and omit the optional bytes
-at the end.
+@code{x22} is usually 0 or 2000000.
+
+A writer may safely use 4 for @code{x21} and omit @code{x22} and the
+other optional bytes at the end.
 
 @node SPV Light Member Dimensions
 @subsection Dimensions
@@ -1470,7 +1562,7 @@ are really categories; the others just serve as grouping constructs.
 Category => Value[name] (Leaf @math{|} Group)
 Leaf => 00 00 00 i2 int32[leaf-index] i0
 Group =>
-    bool[merge] 00 01 int32[x22]
+    bool[merge] 00 01 int32[x23]
     i-1 int32[n-subcategories] Category*[n-subcategories]
 @end example
 
@@ -1504,7 +1596,7 @@ nested!)
 (For writing an SPV file, there is no need to use the @code{merge}
 feature unless it is convenient.)
 
-A Group's @code{x22} appears to be i2 when all of the categories
+A Group's @code{x23} 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.  A writer
@@ -1592,9 +1684,14 @@ 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, except that format 40 is a synonym for F format
-instead of MTIME.  @xref{System File Output Formats}, for details.
+formatted according to @code{format}, which is about the same as the
+format described for system files (@pxref{System File Output
+Formats}).  The exception is that format 40 is not MTIME but instead
+approximately a synonym for F format with a different rule for whether
+a value is shown in scientific notation: a value in format 40 is shown
+in scientific notation if and only if it is nonzero and its magnitude
+is less than @code{small} (@pxref{SPV Light Member Formats}).
+
 Most commonly, @code{format} has width 40 (the maximum).
 
 An @code{x} with the maximum negative double value @code{-DBL_MAX}
@@ -1639,8 +1736,9 @@ case, @code{id} is always the empty string; in the latter case,
 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.
+like A16.39 or A255.127 or A134.1, so readers should probably entirely
+disregard the format.  PSPP only checks @code{format} to distinguish
+AHEX format.
 
 @code{s} is a value of variable @code{var-name} and has value label
 @code{value-label}.  @code{var-name} is never empty but
@@ -1745,11 +1843,11 @@ ValueMod =>
     58
   @math{|} 31
     int32[n-refs] int16*[n-refs]
-    (i0 | i1 string[subscript])
+    int32[n-subscripts] string*[n-subscripts]
     v1(00 (i1 | i2) 00? 00? int32 00? 00?)
     v3(count(TemplateString StylePair))
 
-TemplateString => count((count((i0 58)?) (58 @math{|} 31 string[id]))?)
+TemplateString => count((count((i0 (58 @math{|} 31 55))?) (58 @math{|} 31 string[id]))?)
 
 StylePair =>
     (31 FontStyle | 58)
@@ -1774,10 +1872,11 @@ Each of the @code{n-refs} integers is a reference to a Footnote
 markers are shown appended to the main text of the Value, as
 superscripts.
 
-The @code{subscript}, if present, is 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.
+The @code{subscripts}, if present, are strings to append to the main
+text of the Value, as subscripts.  Each subscript text is a brief
+indicator, e.g.@: @samp{a} or @samp{b}, with its meaning indicated by
+the table caption.  When multiple subscripts are present, they are
+displayed separated by commas.
 
 The @code{id} inside the TemplateString, if present, is a template
 string for substitutions using the syntax explained previously.  It
@@ -1969,8 +2068,10 @@ An XML Schema for VizML is available, distributed with SPSS binaries,
 under a nonfree license.  It contains documentation that is
 occasionally helpful.
 
-See @file{src/output/spv/detail-xml.grammar} in the PSPP source tree
-for the full grammar that it uses for parsing.
+This section describes the detail XML format using the same notation
+already used for the structure XML format (@pxref{SPV Structure Member
+Format}).  See @file{src/output/spv/detail-xml.grammar} in the PSPP
+source tree for the full grammar that it uses for parsing.
 
 The important elements of the detail XML format are:
 
@@ -2625,7 +2726,7 @@ tableLayout
    :fitCells=(ticks both)?
 => EMPTY
 @end example
-               
+
 The @code{facetLayout} element and its descendants control styling for
 the table.
 
@@ -2696,7 +2797,7 @@ Always observed as @code{0pt}.
 
 Each @code{facetLevel} contains an @code{axis}, which in turn may
 contain a @code{label} for the @code{facetLevel} (@pxref{SPV Detail
-label Element}) and does contain a @code{majorTicks} element.  
+label Element}) and does contain a @code{majorTicks} element.
 
 @defvr {Attribute} labelAngle
 Normally 0.  The value -90 causes inner column or outer row labels to
@@ -3573,6 +3674,7 @@ XML, which has the following @code{tableProperties} element:
 
 @example
 tableProperties
+   :name?
 => generalProperties footnoteProperties cellFormatProperties borderProperties printingProperties
 
 generalProperties
@@ -3603,6 +3705,7 @@ style
    :font-size?
    :font-style=(regular | italic)?
    :font-weight=(regular | bold)?
+   :font-underline=(none | underline)?
    :labelLocationVertical=(positive | negative | center)?
    :margin-bottom=dimension?
    :margin-left=dimension?
@@ -3630,3 +3733,6 @@ printingProperties
    :printEachLayerOnSeparatePage=bool?
 => EMPTY
 @end example
+
+The @code{name} attribute appears only in standalone @file{.stt} files
+(@pxref{SPSS TableLook STT Format}).