"smallstr",
"smallvec",
"thiserror",
+ "toml 0.9.5",
"unicase",
"unicode-linebreak",
"unicode-properties",
"serde",
]
+[[package]]
+name = "serde_spanned"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "sha1"
version = "0.10.6"
"cfg-expr",
"heck",
"pkg-config",
- "toml",
+ "toml 0.8.22",
"version-compare",
]
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
dependencies = [
"serde",
- "serde_spanned",
- "toml_datetime",
+ "serde_spanned 0.6.8",
+ "toml_datetime 0.6.9",
"toml_edit",
]
+[[package]]
+name = "toml"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned 1.0.0",
+ "toml_datetime 0.7.0",
+ "toml_parser",
+ "toml_writer",
+ "winnow",
+]
+
[[package]]
name = "toml_datetime"
version = "0.6.9"
"serde",
]
+[[package]]
+name = "toml_datetime"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "toml_edit"
version = "0.22.26"
dependencies = [
"indexmap",
"serde",
- "serde_spanned",
- "toml_datetime",
+ "serde_spanned 0.6.8",
+ "toml_datetime 0.6.9",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
+dependencies = [
"winnow",
]
+[[package]]
+name = "toml_writer"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
+
[[package]]
name = "tower"
version = "0.4.13"
unicode-properties = "0.1.3"
unicode-segmentation = "1.12.0"
serde_json = "1.0.141"
+toml = "0.9.5"
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.48.0", features = ["Win32_Globalization"] }
(group, values)
}
+ pub fn to_pivot_table(&self) -> PivotTable {
+ let (group, data) = self.to_pivot_rows();
+ PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data(
+ data.into_iter()
+ .enumerate()
+ .filter(|(_row, value)| !value.is_empty())
+ .map(|(row, value)| ([row], value)),
+ )
+ }
+
+ pub fn all_pivot_tables(&self) -> Vec<PivotTable> {
+ let mut pivot_tables = Vec::new();
+ pivot_tables.push(self.to_pivot_table());
+ pivot_tables.push(self.output_variables().to_pivot_table());
+ pivot_tables.extend(self.output_value_labels().to_pivot_table());
+ pivot_tables.extend(self.output_mrsets().to_pivot_table());
+ pivot_tables.extend(self.output_attributes().to_pivot_table());
+ pivot_tables.extend(self.output_variable_sets().to_pivot_table());
+ pivot_tables
+ }
+
pub fn short_names(&self) -> Vec<SmallVec<[Identifier; 1]>> {
struct PickShortName<'a> {
variable_name: &'a Identifier,
(Axis3::Y, Dimension::new(names)),
(Axis3::X, Dimension::new(attributes)),
])
+ .with_title("Variables")
.with_show_empty();
for (var_index, variable) in self.dictionary.variables.iter().enumerate() {
for (field, field_index) in &columns {
use encoding_rs::Encoding;
use pspp::{
crypto::EncryptedFile,
+ output::{
+ driver::{Config, Driver, DriverType},
+ Details, Item, Text,
+ },
sys::{
self,
raw::{infer_encoding, records::Compression, Decoder, Magic, Reader, Record},
};
use serde::Serialize;
use std::{
+ cell::RefCell,
+ ffi::OsStr,
+ fmt::{Display, Write as _},
fs::File,
io::{stdout, BufReader, Write},
path::{Path, PathBuf},
- str,
+ rc::Rc,
+ str::{self, FromStr},
+ sync::Arc,
};
use thiserror::Error as ThisError;
use zeroize::Zeroizing;
/// Show SPSS system file dictionary and data.
#[derive(Args, Clone, Debug)]
struct Show {
+ /// File to show.
+ #[arg(required = true)]
+ input_file: PathBuf,
+
+ /// Output file name. If omitted, output is written to stdout.
+ output_file: Option<PathBuf>,
+
+ /// Output driver configuration options.
+ #[arg(short = 'o')]
+ output_options: Vec<String>,
+
/// Maximum number of cases to print.
#[arg(long = "data", default_value_t = 0)]
max_cases: u64,
- /// Files to show.
- #[arg(required = true)]
- files: Vec<PathBuf>,
-
/// What to show.
#[arg(short, long, value_enum, default_value_t)]
mode: Mode,
/// Output format.
- #[arg(long, value_enum, default_value_t)]
- format: ShowFormat,
+ #[arg(long, value_parser = ShowFormat::from_str)]
+ format: Option<ShowFormat>,
/// The encoding to use.
#[arg(long, value_parser = parse_encoding)]
encoding: Option<&'static Encoding>,
}
-impl Show {
- fn run(self) -> Result<()> {
- for file in &self.files {
- self.show(file)?;
+enum Output {
+ Driver {
+ driver: Rc<RefCell<Box<dyn Driver>>>,
+ mode: Mode,
+ },
+ Json {
+ writer: Rc<RefCell<Box<dyn Write>>>,
+ pretty: bool,
+ },
+}
+
+impl Output {
+ fn show_json<T>(&self, value: &T) -> Result<()>
+ where
+ T: Serialize,
+ {
+ match self {
+ Self::Driver { mode, driver: _ } => {
+ Err(anyhow!("Mode '{mode}' only supports output as JSON."))
+ }
+ Self::Json { writer, pretty } => {
+ let mut writer = writer.borrow_mut();
+ match pretty {
+ true => serde_json::to_writer_pretty(&mut *writer, value)?,
+ false => serde_json::to_writer(&mut *writer, value)?,
+ };
+ writeln!(writer)?;
+ Ok(())
+ }
+ }
+ }
+
+ fn warn(&self, warning: &impl Display) {
+ match self {
+ Output::Driver { driver, .. } => {
+ driver
+ .borrow_mut()
+ .write(&Arc::new(Item::from(Text::new_log(warning.to_string()))));
+ }
+ Output::Json { .. } => {
+ #[derive(Serialize)]
+ struct Warning {
+ warning: String,
+ }
+ let warning = Warning {
+ warning: warning.to_string(),
+ };
+ let _ = self.show_json(&warning);
+ }
}
- Ok(())
}
- fn show(&self, file_name: &Path) -> Result<()> {
- let reader = File::open(file_name)?;
+}
+
+impl Show {
+ fn run(self) -> Result<()> {
+ let format = if let Some(format) = self.format {
+ format
+ } else if let Some(output_file) = &self.output_file {
+ ShowFormat::from_str(
+ output_file
+ .extension()
+ .unwrap_or(OsStr::new(""))
+ .to_str()
+ .unwrap_or(""),
+ )
+ .map_err(|_| {
+ anyhow!(
+ "{}: no default output format for file name",
+ output_file.display()
+ )
+ })?
+ } else {
+ ShowFormat::Json
+ };
+
+ let output = if let ShowFormat::Output(driver) = format {
+ let mut config = String::new();
+
+ #[derive(Serialize)]
+ struct DriverConfig {
+ driver: DriverType,
+ }
+ config.push_str(&toml::to_string_pretty(&DriverConfig { driver }).unwrap());
+
+ if let Some(file) = &self.output_file {
+ #[derive(Serialize)]
+ struct File<'a> {
+ file: &'a Path,
+ }
+ let file = File {
+ file: file.as_path(),
+ };
+ let toml_file = toml::to_string_pretty(&file).unwrap();
+ config.push_str(&toml_file);
+
+ for option in &self.output_options {
+ writeln!(&mut config, "{option}").unwrap();
+ }
+ }
+
+ let config: Config = toml::from_str(&config)?;
+ Output::Driver {
+ mode: self.mode,
+ driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::new(&config)?))),
+ }
+ } else {
+ Output::Json {
+ pretty: format == ShowFormat::Json,
+ writer: if let Some(output_file) = &self.output_file {
+ Rc::new(RefCell::new(Box::new(File::create(output_file)?)))
+ } else {
+ Rc::new(RefCell::new(Box::new(stdout())))
+ },
+ }
+ };
+
+ let reader = File::open(&self.input_file)?;
let reader = BufReader::new(reader);
- let mut reader = Reader::new(reader, Box::new(|warning| println!("{warning}")))?;
+ let mut reader = Reader::new(reader, Box::new(|warning| output.warn(&warning)))?;
match self.mode {
Mode::Identify => {
return Ok(());
}
Mode::Raw => {
- self.show_json(reader.header())?;
+ output.show_json(reader.header())?;
for record in reader.records() {
- self.show_json(&record?)?;
+ output.show_json(&record?)?;
}
for (_index, case) in (0..self.max_cases).zip(reader.cases()) {
- self.show_json(&case?)?;
+ output.show_json(&case?)?;
}
}
Mode::Decoded => {
let records: Vec<Record> = reader.records().collect::<Result<Vec<_>, _>>()?;
let encoding = match self.encoding {
Some(encoding) => encoding,
- None => infer_encoding(&records, &mut |e| eprintln!("{e}"))?,
+ None => infer_encoding(&records, &mut |e| output.warn(&e))?,
};
- let mut decoder = Decoder::new(encoding, |e| eprintln!("{e}"));
+ let mut decoder = Decoder::new(encoding, |e| output.warn(&e));
for record in records {
- self.show_json(&record.decode(&mut decoder))?;
+ output.show_json(&record.decode(&mut decoder))?;
}
}
Mode::Parsed => {
let records: Vec<Record> = reader.records().collect::<Result<Vec<_>, _>>()?;
let encoding = match self.encoding {
Some(encoding) => encoding,
- None => infer_encoding(&records, &mut |e| eprintln!("{e}"))?,
+ None => infer_encoding(&records, &mut |e| output.warn(&e))?,
};
- let mut decoder = Decoder::new(encoding, |e| eprintln!("{e}"));
+ let mut decoder = Decoder::new(encoding, |e| output.warn(&e));
let records = Records::from_raw(records, &mut decoder);
let (dictionary, metadata, cases) = records
.decode(
reader.header().clone().decode(&mut decoder),
reader.cases(),
encoding,
- |e| eprintln!("{e}"),
+ |e| output.warn(&e),
)
.into_parts();
- self.show_json(&dictionary)?;
- self.show_json(&metadata)?;
- for (_index, case) in (0..self.max_cases).zip(cases) {
- self.show_json(&case?)?;
+ match &output {
+ Output::Driver { driver, mode: _ } => {
+ driver
+ .borrow_mut()
+ .write(&Arc::new(Item::new(metadata.to_pivot_table())));
+ driver
+ .borrow_mut()
+ .write(&Arc::new(Item::new(Details::Group(
+ dictionary
+ .all_pivot_tables()
+ .into_iter()
+ .map(|pivot_table| Arc::new(Item::new(pivot_table)))
+ .collect(),
+ ))));
+ }
+ Output::Json { .. } => {
+ output.show_json(&dictionary)?;
+ output.show_json(&metadata)?;
+ for (_index, case) in (0..self.max_cases).zip(cases) {
+ output.show_json(&case?)?;
+ }
+ }
}
}
}
Ok(())
}
- fn show_json<T>(&self, value: &T) -> Result<()>
- where
- T: Serialize,
- {
- match self.format {
- ShowFormat::Json => serde_json::to_writer_pretty(stdout(), value)?,
- ShowFormat::Ndjson => serde_json::to_writer(stdout(), value)?,
- };
- println!();
- Ok(())
- }
}
#[derive(Subcommand, Clone, Debug)]
Parsed,
}
-#[derive(Clone, Copy, Debug, Default, ValueEnum)]
+impl Mode {
+ fn as_str(&self) -> &'static str {
+ match self {
+ Mode::Identify => "identify",
+ Mode::Raw => "raw",
+ Mode::Decoded => "decoded",
+ Mode::Parsed => "parsed",
+ }
+ }
+}
+
+impl Display for Mode {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.as_str())
+ }
+}
+
+#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize)]
+#[serde(rename_all = "snake_case")]
enum ShowFormat {
/// Pretty-printed JSON.
#[default]
Json,
/// Newline-delimited JSON.
Ndjson,
+ Output(DriverType),
+}
+
+#[derive(ThisError, Debug)]
+#[error("{0}: unknown format")]
+struct UnknownFormat(String);
+
+impl FromStr for ShowFormat {
+ type Err = UnknownFormat;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ if s.eq_ignore_ascii_case("json") {
+ Ok(Self::Json)
+ } else if s.eq_ignore_ascii_case("ndjson") {
+ Ok(Self::Ndjson)
+ } else if let Ok(driver_type) = DriverType::from_str(s, true) {
+ Ok(Self::Output(driver_type))
+ } else {
+ Err(UnknownFormat(String::from(s)))
+ }
+ }
}
fn main() -> Result<()> {
pager::{CairoPageStyle, CairoPager},
},
driver::Driver,
- page::Setup,
+ page::PageSetup,
pivot::{Color, Coord2, FontStyle},
Item,
};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct CairoConfig {
/// Output file name.
- pub path: PathBuf,
+ pub file: PathBuf,
/// Page setup.
- #[serde(flatten)]
- pub setup: Setup,
+ pub page_setup: Option<PageSetup>,
}
impl CairoConfig {
pub fn new(path: impl AsRef<Path>) -> Self {
Self {
- path: path.as_ref().to_path_buf(),
- setup: Setup::default(),
+ file: path.as_ref().to_path_buf(),
+ page_setup: None,
}
}
}
(inches * 72.0 * SCALE as f64).max(0.0).round() as usize
}
- let printable = config.setup.printable_size();
+ let default_page_setup;
+ let page_setup = match &config.page_setup {
+ Some(page_setup) => page_setup,
+ None => {
+ default_page_setup = PageSetup::default();
+ &default_page_setup
+ }
+ };
+ let printable = page_setup.printable_size();
let page_style = CairoPageStyle {
margins: EnumMap::from_fn(|axis| {
[
- scale(config.setup.margins[axis][0]),
- scale(config.setup.margins[axis][1]),
+ scale(page_setup.margins[axis][0]),
+ scale(page_setup.margins[axis][1]),
]
}),
- headings: config.setup.headings.clone(),
- initial_page_number: config.setup.initial_page_number,
+ headings: page_setup.headings.clone(),
+ initial_page_number: page_setup.initial_page_number,
};
let size = Coord2::new(scale(printable[Axis2::X]), scale(printable[Axis2::Y]));
let font = FontStyle {
font,
fg: Color::BLACK,
use_system_colors: false,
- object_spacing: scale(config.setup.object_spacing),
+ object_spacing: scale(page_setup.object_spacing),
font_resolution: 72.0,
};
let surface = PdfSurface::new(
- config.setup.paper[Axis2::X] * 72.0,
- config.setup.paper[Axis2::Y] * 72.0,
- &config.path,
+ page_setup.paper[Axis2::X] * 72.0,
+ page_setup.paper[Axis2::Y] * 72.0,
+ &config.file,
)?;
Ok(Self {
fsm_style: Arc::new(fsm_style),
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CsvConfig {
- path: PathBuf,
+ file: PathBuf,
#[serde(flatten)]
options: CsvOptions,
}
impl CsvDriver {
pub fn new(config: &CsvConfig) -> std::io::Result<Self> {
Ok(Self {
- file: File::create(&config.path)?,
+ file: File::create(&config.file)?,
options: config.options.clone(),
n_items: 0,
})
use std::{borrow::Cow, sync::Arc};
+use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use crate::output::{
cairo::{CairoConfig, CairoDriver},
csv::{CsvConfig, CsvDriver},
html::{HtmlConfig, HtmlDriver},
+ spv::{SpvConfig, SpvDriver},
text::{TextConfig, TextDriver},
};
-use super::{page::Setup, Item};
-
-#[derive(Clone, Debug, Serialize, Deserialize)]
-#[serde(tag = "driver", rename_all = "snake_case")]
-pub enum Config {
- Text(TextConfig),
- Pdf(CairoConfig),
- Html(HtmlConfig),
- Csv(CsvConfig),
-}
-
-impl dyn Driver {
- fn new(config: &Config) -> anyhow::Result<Box<Self>> {
- match config {
- Config::Text(text_config) => Ok(Box::new(TextDriver::new(text_config)?)),
- Config::Pdf(cairo_config) => Ok(Box::new(CairoDriver::new(cairo_config)?)),
- Config::Html(html_config) => Ok(Box::new(HtmlDriver::new(html_config)?)),
- Config::Csv(csv_config) => Ok(Box::new(CsvDriver::new(csv_config)?)),
- }
- }
-}
+use super::{page::PageSetup, Item};
// An output driver.
pub trait Driver {
fn write(&mut self, item: &Arc<Item>);
/// Returns false if the driver doesn't support page setup.
- fn setup(&mut self, page_setup: &Setup) -> bool {
+ fn setup(&mut self, page_setup: &PageSetup) -> bool {
let _ = page_setup;
false
}
}
}
-/*
-/// An abstract way for the output subsystem to create an output driver.
-trait DriverFactory {
- /// The file extension, without the leading dot, e.g. "pdf".
- fn extension(&self) -> OsString;
+impl Driver for Box<dyn Driver> {
+ fn name(&self) -> Cow<'static, str> {
+ (&**self).name()
+ }
- /// The default file name, including extension.
- ///
- /// If this is `-`, that implies that by default output will be directed to
- /// stdout.
- fn default_file_name(&self) -> PathBuf;
+ fn write(&mut self, item: &Arc<Item>) {
+ (&mut **self).write(item);
+ }
- /// Creates a new output driver of this class. `name` and `type` should be
- /// passed directly to output_driver_init.
- ///
- /// It is up to the driver class to decide how to interpret `options`. The
- /// create function should delete pairs that it understands from `options`,
- /// because the caller may issue errors about unknown options for any pairs
- /// that remain.
- fn create(&self, file_handle: (),
+ fn setup(&mut self, page_setup: &PageSetup) -> bool {
+ (&mut **self).setup(page_setup)
+ }
+
+ fn flush(&mut self) {
+ (&mut **self).flush();
+ }
+
+ fn handles_show(&self) -> bool {
+ (&**self).handles_show()
+ }
- enum settings_output_devices type,
- struct driver_options *);
+ fn handles_groups(&self) -> bool {
+ (&**self).handles_groups()
+ }
+}
+#[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(tag = "driver", rename_all = "snake_case")]
+pub enum Config {
+ Text(TextConfig),
+ Pdf(CairoConfig),
+ Html(HtmlConfig),
+ Csv(CsvConfig),
+ Spv(SpvConfig),
+}
+
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, ValueEnum)]
+#[serde(rename_all = "snake_case")]
+pub enum DriverType {
+ Text,
+ Pdf,
+ Html,
+ Csv,
+ Spv,
+}
+
+impl dyn Driver {
+ pub fn new(config: &Config) -> anyhow::Result<Box<Self>> {
+ match config {
+ Config::Text(text_config) => Ok(Box::new(TextDriver::new(text_config)?)),
+ Config::Pdf(cairo_config) => Ok(Box::new(CairoDriver::new(cairo_config)?)),
+ Config::Html(html_config) => Ok(Box::new(HtmlDriver::new(html_config)?)),
+ Config::Csv(csv_config) => Ok(Box::new(CsvDriver::new(csv_config)?)),
+ Config::Spv(spv_config) => Ok(Box::new(SpvDriver::new(spv_config)?)),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use serde::Serialize;
+
+ use crate::output::driver::Config;
+
+ #[test]
+ fn toml() {
+ let config = r#"driver = "text"
+file = "filename.text"
+"#;
+ let toml: Config = toml::from_str(config).unwrap();
+ println!("{}", toml::to_string_pretty(&toml).unwrap());
+
+ #[derive(Serialize)]
+ struct Map<'a> {
+ file: &'a str,
+ }
+ println!(
+ "{}",
+ toml::to_string_pretty(&Map { file: "filename" }).unwrap()
+ );
+ }
}
-*/
Landscape,
}
+/// Chart size.
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ChartSize {
+ /// Size specified in the chart itself.
#[default]
AsIs,
+
+ /// Full page.
FullHeight,
+
+ /// Half-page.
HalfHeight,
+
+ /// Quarter-page.
QuarterHeight,
}
pub struct Heading(pub Vec<Paragraph>);
#[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Setup {
+#[serde(default)]
+pub struct PageSetup {
+ /// Page number of first page.
pub initial_page_number: i32,
/// Paper size in inches.
/// Margin width in inches.
pub margins: EnumMap<Axis2, [f64; 2]>,
+ /// Portrait or landscape.
pub orientation: Orientation,
/// Space between objects, in inches.
pub object_spacing: f64,
+ /// Size of charts.
pub chart_size: ChartSize,
/// Header and footer.
pub headings: [Heading; 2],
}
-impl Default for Setup {
+impl Default for PageSetup {
fn default() -> Self {
Self {
initial_page_number: 1,
}
}
-impl Setup {
+impl PageSetup {
pub fn printable_size(&self) -> EnumMap<Axis2, f64> {
EnumMap::from_fn(|axis| self.paper[axis] - self.margins[axis][0] - self.margins[axis][1])
}
if let Some(dir) = std::env::var_os("PSPP_TEST_SPV_DIR") {
let writer = File::create(Path::new(&dir).join(name).with_extension("spv")).unwrap();
- SpvDriver::new(writer).write(&item);
+ SpvDriver::for_writer(writer).write(&item);
}
}
use std::{
borrow::Cow,
fmt::Write as _,
+ fs::File,
io::{Cursor, Seek, Write},
iter::{repeat, repeat_n},
+ path::PathBuf,
sync::Arc,
};
writer::Writer as XmlWriter,
ElementWriter,
};
+use serde::{Deserialize, Serialize};
use smallstr::SmallString;
use zip::{result::ZipResult, write::SimpleFileOptions, ZipWriter};
format::{Format, Type},
output::{
driver::Driver,
+ page::{Heading, PageSetup},
pivot::{
Area, AreaStyle, Axis2, Axis3, Border, BorderStyle, BoxBorder, Category, CellStyle,
Color, Dimension, FontStyle, Footnote, FootnoteMarkerPosition, FootnoteMarkerType,
)
}
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct SpvConfig {
+ /// Output file name.
+ pub file: PathBuf,
+
+ /// Page setup.
+ pub page_setup: Option<PageSetup>,
+}
+
pub struct SpvDriver<W>
where
W: Write + Seek,
needs_page_break: bool,
next_table_id: u64,
next_heading_id: u64,
+ page_setup: Option<PageSetup>,
+}
+
+impl SpvDriver<File> {
+ pub fn new(config: &SpvConfig) -> std::io::Result<Self> {
+ let mut driver = Self::for_writer(File::create(&config.file)?);
+ if let Some(page_setup) = &config.page_setup {
+ driver = driver.with_page_setup(page_setup.clone());
+ }
+ Ok(driver)
+ }
}
impl<W> SpvDriver<W>
where
W: Write + Seek,
{
- pub fn new(writer: W) -> Self {
+ pub fn for_writer(writer: W) -> Self {
let mut writer = ZipWriter::new(writer);
writer
.start_file("META-INF/MANIFEST.MF", SimpleFileOptions::default())
needs_page_break: false,
next_table_id: 1,
next_heading_id: 1,
+ page_setup: None,
+ }
+ }
+
+ pub fn with_page_setup(self, page_setup: PageSetup) -> Self {
+ Self {
+ page_setup: Some(page_setup),
+ ..self
}
}
.write_inner_content(|w| {
w.create_element("label")
.write_text_content(BytesText::new("Output"))?;
- // XXX page setup
+ if let Some(page_setup) = self.page_setup.take() {
+ write_page_setup(&page_setup, w)?;
+ }
self.write_item(item, w);
Ok(())
})
}
}
+fn write_page_setup<X>(page_setup: &PageSetup, writer: &mut XmlWriter<X>) -> std::io::Result<()>
+where
+ X: Write,
+{
+ fn inches<'a>(x: f64) -> Cow<'a, str> {
+ Cow::from(format!("{:.2}in", x))
+ }
+
+ writer
+ .create_element("vps:pageSetup")
+ .with_attribute((
+ "initial-page-number",
+ Cow::from(format!("{}", page_setup.initial_page_number)),
+ ))
+ .with_attribute((
+ "chart-size",
+ match page_setup.chart_size {
+ super::page::ChartSize::AsIs => "as-is",
+ super::page::ChartSize::FullHeight => "full-height",
+ super::page::ChartSize::HalfHeight => "half-height",
+ super::page::ChartSize::QuarterHeight => "quarter-height",
+ },
+ ))
+ .with_attribute(("margin-left", inches(page_setup.margins[Axis2::X][0])))
+ .with_attribute(("margin-right", inches(page_setup.margins[Axis2::X][1])))
+ .with_attribute(("margin-top", inches(page_setup.margins[Axis2::Y][0])))
+ .with_attribute(("margin-bottom", inches(page_setup.margins[Axis2::Y][1])))
+ .with_attribute(("paper-height", inches(page_setup.paper[Axis2::Y])))
+ .with_attribute(("paper-width", inches(page_setup.paper[Axis2::X])))
+ .with_attribute((
+ "reference-orientation",
+ match page_setup.orientation {
+ crate::output::page::Orientation::Portrait => "portrait",
+ crate::output::page::Orientation::Landscape => "landscape",
+ },
+ ))
+ .with_attribute((
+ "space-after",
+ Cow::from(format!("{:.1}pt", page_setup.object_spacing * 72.0)),
+ ))
+ .write_inner_content(|w| {
+ write_page_heading(&page_setup.headings[0], "vps:pageHeader", w)?;
+ write_page_heading(&page_setup.headings[1], "vps:pageFooter", w)?;
+ Ok(())
+ })?;
+ Ok(())
+}
+
+fn write_page_heading<X>(
+ heading: &Heading,
+ name: &str,
+ writer: &mut XmlWriter<X>,
+) -> std::io::Result<()>
+where
+ X: Write,
+{
+ let element = writer.create_element(name);
+ if !heading.0.is_empty() {
+ element.write_inner_content(|w| {
+ w.create_element("vps:pageParagraph")
+ .write_inner_content(|w| {
+ for paragraph in &heading.0 {
+ w.create_element("vtx:text")
+ .with_attribute(("text", "title"))
+ .write_text_content(BytesText::new(¶graph.markup))?;
+ }
+ Ok(())
+ })?;
+ Ok(())
+ })?;
+ }
+ Ok(())
+}
+
fn maybe_with_attribute<'a, 'b, W, I>(
element: ElementWriter<'a, W>,
attr: Option<I>,
options: TextRendererOptions,
}
-#[derive(Clone, Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+#[serde(default)]
pub struct TextRendererOptions {
/// Enable bold and underline in output?
pub emphasis: bool,
/// Page width.
- pub width: usize,
+ pub width: Option<usize>,
/// ASCII or Unicode
pub boxes: Boxes,
}
-impl Default for TextRendererOptions {
- fn default() -> Self {
- Self {
- emphasis: false,
- width: usize::MAX,
- boxes: Boxes::default(),
- }
- }
-}
-
pub struct TextRenderer {
/// Enable bold and underline in output?
emphasis: bool,
impl TextRenderer {
pub fn new(config: &TextRendererOptions) -> Self {
+ let width = config.width.unwrap_or(usize::MAX);
Self {
emphasis: config.emphasis,
- width: config.width,
+ width,
min_hbreak: 20,
box_chars: config.boxes.box_chars(),
n_objects: 0,
params: Params {
- size: Coord2::new(config.width, usize::MAX),
+ size: Coord2::new(width, usize::MAX),
font_size: EnumMap::from_fn(|_| 1),
line_widths: EnumMap::from_fn(|stroke| if stroke == Stroke::None { 0 } else { 1 }),
px_size: None,
format::{Error as FormatError, Format, UncheckedFormat},
hexfloat::HexFloat,
identifier::{Error as IdError, Identifier},
- output::pivot::{Group, Value},
+ output::pivot::{Axis3, Dimension, Group, PivotTable, Value},
sys::{
raw::{
self, infer_encoding,
(group, values)
}
+ pub fn to_pivot_table(&self) -> PivotTable {
+ let (group, data) = self.to_pivot_rows();
+ PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data(
+ data.into_iter()
+ .enumerate()
+ .filter(|(_row, value)| !value.is_empty())
+ .map(|(row, value)| ([row], value)),
+ )
+ }
+
fn decode(header: &FileHeader<String>, headers: &Records, mut warn: impl FnMut(Error)) -> Self {
let header = &header;
let creation_date = NaiveDate::parse_from_str(&header.creation_date, "%e %b %y")
use binrw::Endian;
use encoding_rs::UTF_8;
+use itertools::Itertools;
use crate::{
crypto::EncryptedFile,
/// Tests the most basic kind of writing a system file, just writing a few
/// numeric variables and cases.
-#[test]
-fn write_numeric() {
- for (compression, compression_string) in [
- (None, "uncompressed"),
- (Some(Compression::Simple), "simple"),
- (Some(Compression::ZLib), "zlib"),
+fn write_numeric(compression: Option<Compression>, compression_string: &str) {
+ let mut dictionary = Dictionary::new(UTF_8);
+ for i in 0..4 {
+ let name = Identifier::new(format!("variable{i}")).unwrap();
+ dictionary
+ .add_var(Variable::new(name, VarWidth::Numeric, UTF_8))
+ .unwrap();
+ }
+ let mut cases = WriteOptions::reproducible(compression)
+ .write_writer(&dictionary, Cursor::new(Vec::new()))
+ .unwrap();
+ for case in [
+ [1, 1, 1, 2],
+ [1, 1, 2, 30],
+ [1, 2, 1, 8],
+ [1, 2, 2, 20],
+ [2, 1, 1, 2],
+ [2, 1, 2, 22],
+ [2, 2, 1, 1],
+ [2, 2, 2, 3],
] {
- let mut dictionary = Dictionary::new(UTF_8);
- for i in 0..4 {
- let name = Identifier::new(format!("variable{i}")).unwrap();
- dictionary
- .add_var(Variable::new(name, VarWidth::Numeric, UTF_8))
- .unwrap();
- }
- let mut cases = WriteOptions::reproducible(compression)
- .write_writer(&dictionary, Cursor::new(Vec::new()))
+ cases
+ .write_case(
+ case.into_iter()
+ .map(|number| Datum::<&str>::Number(Some(number as f64))),
+ )
.unwrap();
- for case in [
- [1, 1, 1, 2],
- [1, 1, 2, 30],
- [1, 2, 1, 8],
- [1, 2, 2, 20],
- [2, 1, 1, 2],
- [2, 1, 2, 22],
- [2, 2, 1, 1],
- [2, 2, 2, 3],
- ] {
- cases
- .write_case(
- case.into_iter()
- .map(|number| Datum::<&str>::Number(Some(number as f64))),
- )
- .unwrap();
- }
- let sysfile = cases.finish().unwrap().unwrap().into_inner();
- let expected_filename = PathBuf::from(&format!(
- "src/sys/testdata/write-numeric-{compression_string}.expected"
- ));
- let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
- test_sysfile(Cursor::new(sysfile), &expected, &expected_filename);
}
+ let sysfile = cases.finish().unwrap().unwrap().into_inner();
+ let expected_filename = PathBuf::from(&format!(
+ "src/sys/testdata/write-numeric-{compression_string}.expected"
+ ));
+ let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
+ test_sysfile(Cursor::new(sysfile), &expected, &expected_filename);
}
-/// Tests writing string data.
#[test]
-fn write_string_data() {
- for (compression, compression_string) in [
- (None, "uncompressed"),
- (Some(Compression::Simple), "simple"),
- (Some(Compression::ZLib), "zlib"),
- ] {
- let mut dictionary = Dictionary::new(UTF_8);
- dictionary
- .add_var(Variable::new(
- Identifier::new("s1").unwrap(),
- VarWidth::String(1),
- UTF_8,
- ))
- .unwrap();
+fn write_numeric_uncompressed() {
+ write_numeric(None, "uncompressed");
+}
- dictionary
- .add_var(Variable::new(
- Identifier::new("s2").unwrap(),
- VarWidth::String(2),
- UTF_8,
- ))
- .unwrap();
+#[test]
+fn write_numeric_simple() {
+ write_numeric(Some(Compression::Simple), "simple");
+}
- dictionary
- .add_var(Variable::new(
- Identifier::new("s3").unwrap(),
- VarWidth::String(3),
- UTF_8,
- ))
- .unwrap();
+#[test]
+fn write_numeric_zlib() {
+ write_numeric(Some(Compression::ZLib), "zlib");
+}
- dictionary
- .add_var(Variable::new(
- Identifier::new("s4").unwrap(),
- VarWidth::String(9),
- UTF_8,
- ))
- .unwrap();
+/// Tests writing string data.
+fn write_string(compression: Option<Compression>, compression_string: &str) {
+ let mut dictionary = Dictionary::new(UTF_8);
+ dictionary
+ .add_var(Variable::new(
+ Identifier::new("s1").unwrap(),
+ VarWidth::String(1),
+ UTF_8,
+ ))
+ .unwrap();
- dictionary
- .add_var(Variable::new(
- Identifier::new("s566").unwrap(),
- VarWidth::String(566),
- UTF_8,
- ))
- .unwrap();
+ dictionary
+ .add_var(Variable::new(
+ Identifier::new("s2").unwrap(),
+ VarWidth::String(2),
+ UTF_8,
+ ))
+ .unwrap();
- let mut cases = WriteOptions::reproducible(compression)
- .write_writer(&dictionary, Cursor::new(Vec::new()))
- .unwrap();
- for case in [
+ dictionary
+ .add_var(Variable::new(
+ Identifier::new("s3").unwrap(),
+ VarWidth::String(3),
+ UTF_8,
+ ))
+ .unwrap();
+
+ dictionary
+ .add_var(Variable::new(
+ Identifier::new("s4").unwrap(),
+ VarWidth::String(9),
+ UTF_8,
+ ))
+ .unwrap();
+
+ dictionary
+ .add_var(Variable::new(
+ Identifier::new("s566").unwrap(),
+ VarWidth::String(566),
+ UTF_8,
+ ))
+ .unwrap();
+
+ let mut cases = WriteOptions::reproducible(compression)
+ .write_writer(&dictionary, Cursor::new(Vec::new()))
+ .unwrap();
+ for case in [
["1", "1", "1", "xyzzyquux", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\n"],
["1", "2", "1", "8", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"],
] {
.write_case(case.into_iter().map(|s| Datum::String(s)))
.unwrap();
}
- let sysfile = cases.finish().unwrap().unwrap().into_inner();
- let expected_filename = PathBuf::from(&format!(
- "src/sys/testdata/write-string-{compression_string}.expected"
- ));
- let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
- test_sysfile(Cursor::new(sysfile), &expected, &expected_filename);
- }
+ let sysfile = cases.finish().unwrap().unwrap().into_inner();
+ let expected_filename = PathBuf::from(&format!(
+ "src/sys/testdata/write-string-{compression_string}.expected"
+ ));
+ let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
+ test_sysfile(Cursor::new(sysfile), &expected, &expected_filename);
+}
+
+#[test]
+fn write_string_uncompressed() {
+ write_string(None, "uncompressed");
+}
+
+#[test]
+fn write_string_simple() {
+ write_string(Some(Compression::Simple), "simple");
+}
+
+#[test]
+fn write_string_zlib() {
+ write_string(Some(Compression::ZLib), "zlib");
}
fn test_raw_sysfile(name: &str) {
let output = match ReadOptions::new(|warning| warnings.push(warning)).open_reader(sysfile) {
Ok(system_file) => {
let (dictionary, metadata, cases) = system_file.into_parts();
- let (group, data) = metadata.to_pivot_rows();
- let metadata_table = PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data(
- data.into_iter()
- .enumerate()
- .filter(|(_row, value)| !value.is_empty())
- .map(|(row, value)| ([row], value)),
- );
- let (group, data) = dictionary.to_pivot_rows();
- let dictionary_table = PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data(
- data.into_iter()
- .enumerate()
- .filter(|(_row, value)| !value.is_empty())
- .map(|(row, value)| ([row], value)),
- );
+
let mut output = Vec::new();
output.extend(
warnings
.into_iter()
- .map(|warning| Arc::new(Item::from(Text::new_log(warning.to_string())))),
+ .map(|warning| Item::from(Text::new_log(warning.to_string()))),
);
- output.push(Arc::new(metadata_table.into()));
- output.push(Arc::new(dictionary_table.into()));
- output.push(Arc::new(
- dictionary.output_variables().to_pivot_table().into(),
- ));
- if let Some(pt) = dictionary.output_value_labels().to_pivot_table() {
- output.push(Arc::new(pt.into()));
- }
- if let Some(pt) = dictionary.output_mrsets().to_pivot_table() {
- output.push(Arc::new(pt.into()));
- }
- if let Some(pt) = dictionary.output_attributes().to_pivot_table() {
- output.push(Arc::new(pt.into()));
- }
- if let Some(pt) = dictionary.output_variable_sets().to_pivot_table() {
- output.push(Arc::new(pt.into()));
- }
+ output.push(metadata.to_pivot_table().into());
+ output.extend(dictionary.all_pivot_tables().into_iter().map_into());
let variables =
Group::new("Variable").with_multiple(dictionary.variables.iter().map(|var| &**var));
let mut case_numbers = Group::new("Case").with_label_shown();
);
}
Err(error) => {
- output.push(Arc::new(Item::from(Text::new_log(error.to_string()))));
+ output.push(Item::from(Text::new_log(error.to_string())));
}
}
}
pt.insert(&[column_number, row_number], datum);
}
}
- output.push(Arc::new(pt.into()));
+ output.push(pt.into());
}
- Item::new(Details::Group(output))
+ Item::new(Details::Group(output.into_iter().map(Arc::new).collect()))
}
Err(error) => Item::new(Details::Text(Box::new(Text::new_log(error.to_string())))),
};
│Variables│ 3│
╰─────────┴────────────────────────╯
+ Variables
╭──────────────┬────────┬─────┬─────────────────┬──────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role │Width│Alignment│Print Format│Write Format│Missing Values│
├──────────────┼────────┼─────┼─────────────────┼──────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭──────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 5│
╰─────────┴────────────────────────╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 5│
╰─────────┴────────────────────────╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 5│
╰─────────┴────────────────────────╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│ │Last line of documents │
╰─────────┴───────────────────────────────────────────────────────╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│4│
╰─────────┴─╯
+ Variables
╭────────────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────────────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭──────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 1│
╰─────────┴────────────────────────╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 4│
╰─────────┴──────────────╯
+ Variables
╭─┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│4│
╰─────────┴─╯
+ Variables
╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 5│
╰─────────┴──────────────────────────────╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────────────────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│ Missing Values │
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────────────────────────┤
│Variables│4│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│4│
╰─────────┴─╯
+ Variables
╭──────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 7│
╰─────────┴────────────────────────╯
+ Variables
╭─────────────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────────────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│ │One line of documents│
╰─────────┴─────────────────────╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 16│
╰─────────┴────────────────────────╯
+ Variables
╭──┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 16│
╰─────────┴────────────────────────╯
+ Variables
╭──┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 0│
╰─────────┴────────────────────────╯
+ Variables
│Variables│4│
╰─────────┴─╯
+ Variables
╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 5│
╰─────────┴────────────────────────╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│5│
╰─────────┴─╯
+ Variables
╭────────────────────────────────────────────────────┬────────┬────────────────────────────────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────────────────────────────────────────────────────┼────────┼────────────────────────────────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 4│
╰─────────┴──────────────────────────────╯
+ Variables
╭─┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 2│
╰─────────┴────────────────────────╯
+ Variables
╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 17│
╰─────────┴────────────────────────╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 12│
╰─────────┴────────────────────────╯
+ Variables
╭─┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 12│
╰─────────┴────────────────────────╯
+ Variables
╭─┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 21│
╰─────────┴──────────────────────────────╯
+ Variables
╭────────────────────────────────┬────────┬────────────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬───────────────────────────╮
│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│ Missing Values │
├────────────────────────────────┼────────┼────────────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼───────────────────────────┤
│Variables│ 7│
╰─────────┴────────────────────────╯
+ Variables
╭─┬────────┬─────┬─────────────────┬─────────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role │Width│Alignment│Print Format│Write Format│Missing Values│
├─┼────────┼─────┼─────────────────┼─────────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 10│
╰─────────┴────────────────────────╯
+ Variables
╭──┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 10│
╰─────────┴────────────────────────╯
+ Variables
╭──┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 2│
╰─────────┴────────────────────────╯
+ Variables
╭──────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│2│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│4│
╰─────────┴─╯
+ Variables
╭─────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│4│
╰─────────┴─╯
+ Variables
╭─────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│4│
╰─────────┴─╯
+ Variables
╭─────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│5│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│5│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│5│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 2│
╰─────────┴────────────────────────╯
+ Variables
╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│ Label │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 5│
╰─────────┴────────────────────────╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│ 5│
╰─────────┴────────────────────────╯
+ Variables
╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
│Variables│1│
╰─────────┴─╯
+ Variables
╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
│ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤