Task 749: .UOS File Format

Task 749: .UOS File Format

The .UOS file format is the spreadsheet component of the Uniform Office Format (UOF), an open standard developed in China for office applications. It is defined under the GB/T 20916-2007 standard and uses a compressed XML-based structure similar to OpenDocument Format (ODF) and Office Open XML (OOXML). The format consists of a ZIP container with specific internal files and XML schemas.

  1. List of properties of this file format intrinsic to its file system:

The .UOS format is a ZIP archive, so its intrinsic properties include the ZIP header magic number (PK\x03\x04) and compression method. The internal file system-like structure requires the following key files and properties:

  • mimetype: A text file containing the MIME type "application/uof+spreadsheet".
  • META-INF/manifest.xml: An XML file listing all files in the archive with their MIME types and paths.
  • Object 1/document.xml: The main content XML file, rooted with the uof:UOF element, including attributes such as uof:version (e.g., "1.0" or "2.0"), uof:language (e.g., "cn"), and uof:locID (e.g., "u0000"). This file uses namespaces like http://schemas.uof.org/cn/2003/uof and references schemas like uof.xsd for validation.
  • Object 1/metadata.xml: XML file containing document metadata, such as creator, creation date, and modification date.
  • Object 1/styles.xml: XML file defining styles for the spreadsheet.
  • Optional directories like Object 1/Pictures for embedded images or other resources.
  • Schema references: The format relies on XML schemas (e.g., uof.xsd, spreadsheet.xsd) for element definitions, including spreadsheet-specific elements like <表:电子表格> for sheets, rows, cells, formulas, and formatting attributes.

These properties ensure the file's integrity, compatibility, and parsability as a spreadsheet document.

  1. Two direct download links for files of format .UOS:

Due to the rarity of publicly available .UOS files, the following links provide sample UOF files (closely related, as .UOS is a subset of UOF for spreadsheets). These can be used for testing:

  1. Ghost blog embedded HTML JavaScript for drag and drop .UOS file dump:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>UOS File Property Dumper</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
    <style>
        #drop-area {
            border: 2px dashed #ccc;
            padding: 20px;
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="drop-area">Drag and drop a .UOS file here</div>
    <pre id="output"></pre>
    <script>
        const dropArea = document.getElementById('drop-area');
        const output = document.getElementById('output');

        dropArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            dropArea.style.borderColor = '#000';
        });

        dropArea.addEventListener('dragleave', () => {
            dropArea.style.borderColor = '#ccc';
        });

        dropArea.addEventListener('drop', (e) => {
            e.preventDefault();
            dropArea.style.borderColor = '#ccc';
            const file = e.dataTransfer.files[0];
            if (file.name.endsWith('.uos')) {
                const reader = new FileReader();
                reader.onload = async (event) => {
                    try {
                        const zip = await JSZip.loadAsync(event.target.result);
                        output.textContent = 'Properties:\n';
                        // List files (intrinsic structure)
                        zip.forEach((relativePath) => {
                            output.textContent += `File: ${relativePath}\n`;
                        });
                        // Extract and parse document.xml
                        const documentXml = await zip.file('Object 1/document.xml')?.async('string');
                        if (documentXml) {
                            const parser = new DOMParser();
                            const xmlDoc = parser.parseFromString(documentXml, 'application/xml');
                            const root = xmlDoc.documentElement;
                            output.textContent += `\nRoot Element: ${root.tagName}\n`;
                            for (let attr of root.attributes) {
                                output.textContent += `${attr.name}: ${attr.value}\n`;
                            }
                        } else {
                            output.textContent += 'No document.xml found.\n';
                        }
                        // Extract mimetype
                        const mimetype = await zip.file('mimetype')?.async('string');
                        if (mimetype) {
                            output.textContent += `\nMimetype: ${mimetype}\n`;
                        }
                    } catch (err) {
                        output.textContent = `Error: ${err.message}`;
                    }
                };
                reader.readAsArrayBuffer(file);
            } else {
                output.textContent = 'Please drop a .UOS file.';
            }
        });
    </script>
</body>
</html>
  1. Python class for .UOS file handling:
import zipfile
import xml.etree.ElementTree as ET

class UOSHandler:
    def __init__(self, filename):
        self.filename = filename
        self.properties = {}

    def read(self):
        with zipfile.ZipFile(self.filename, 'r') as zf:
            # List files
            self.properties['files'] = zf.namelist()
            # Read mimetype
            if 'mimetype' in zf.namelist():
                self.properties['mimetype'] = zf.read('mimetype').decode('utf-8')
            # Read and parse document.xml
            if 'Object 1/document.xml' in zf.namelist():
                document_xml = zf.read('Object 1/document.xml').decode('utf-8')
                root = ET.fromstring(document_xml)
                self.properties['root_element'] = root.tag
                self.properties['root_attributes'] = root.attrib

    def write(self, output_filename):
        # For demonstration, create a minimal .UOS (not full write support)
        with zipfile.ZipFile(output_filename, 'w', zipfile.ZIP_DEFLATED) as zf:
            zf.writestr('mimetype', self.properties.get('mimetype', 'application/uof+spreadsheet'))
            # Add placeholder document.xml
            xml_content = '<uof:UOF xmlns:uof="http://schemas.uof.org/cn/2003/uof" uof:version="1.0" uof:language="cn" uof:locID="u0000"></uof:UOF>'
            zf.writestr('Object 1/document.xml', xml_content)
            # Add META-INF/manifest.xml placeholder
            manifest = '<manifest:manifest xmlns:manifest="http://schemas.uof.org/cn/2003/manifest"></manifest:manifest>'
            zf.writestr('META-INF/manifest.xml', manifest)

    def print_properties(self):
        print("Properties:")
        for key, value in self.properties.items():
            print(f"{key}: {value}")

# Example usage:
# handler = UOSHandler('example.uos')
# handler.read()
# handler.print_properties()
# handler.write('new.uos')
  1. Java class for .UOS file handling:
import java.io.*;
import java.util.zip.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;

public class UOSHandler {
    private String filename;
    private String mimetype;
    private String rootElement;
    private java.util.Map<String, String> rootAttributes = new java.util.HashMap<>();
    private java.util.List<String> files = new java.util.ArrayList<>();

    public UOSHandler(String filename) {
        this.filename = filename;
    }

    public void read() throws Exception {
        try (ZipFile zf = new ZipFile(filename)) {
            java.util.Enumeration<? extends ZipEntry> entries = zf.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                files.add(entry.getName());
            }
            ZipEntry mimetypeEntry = zf.getEntry("mimetype");
            if (mimetypeEntry != null) {
                InputStream is = zf.getInputStream(mimetypeEntry);
                mimetype = new String(is.readAllBytes(), "UTF-8");
            }
            ZipEntry documentEntry = zf.getEntry("Object 1/document.xml");
            if (documentEntry != null) {
                InputStream is = zf.getInputStream(documentEntry);
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document doc = builder.parse(is);
                Element root = doc.getDocumentElement();
                rootElement = root.getTagName();
                NamedNodeMap attrs = root.getAttributes();
                for (int i = 0; i < attrs.getLength(); i++) {
                    Attr attr = (Attr) attrs.item(i);
                    rootAttributes.put(attr.getName(), attr.getValue());
                }
            }
        }
    }

    public void write(String outputFilename) throws Exception {
        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputFilename))) {
            zos.putNextEntry(new ZipEntry("mimetype"));
            zos.write(mimetype != null ? mimetype.getBytes("UTF-8") : "application/uof+spreadsheet".getBytes("UTF-8"));
            zos.closeEntry();
            zos.putNextEntry(new ZipEntry("Object 1/document.xml"));
            String xmlContent = "<uof:UOF xmlns:uof=\"http://schemas.uof.org/cn/2003/uof\" uof:version=\"1.0\" uof:language=\"cn\" uof:locID=\"u0000\"></uof:UOF>";
            zos.write(xmlContent.getBytes("UTF-8"));
            zos.closeEntry();
            zos.putNextEntry(new ZipEntry("META-INF/manifest.xml"));
            String manifest = "<manifest:manifest xmlns:manifest=\"http://schemas.uof.org/cn/2003/manifest\"></manifest:manifest>";
            zos.write(manifest.getBytes("UTF-8"));
            zos.closeEntry();
        }
    }

    public void printProperties() {
        System.out.println("Properties:");
        System.out.println("Files: " + files);
        System.out.println("Mimetype: " + mimetype);
        System.out.println("Root Element: " + rootElement);
        System.out.println("Root Attributes: " + rootAttributes);
    }

    // Example usage:
    // public static void main(String[] args) throws Exception {
    //     UOSHandler handler = new UOSHandler("example.uos");
    //     handler.read();
    //     handler.printProperties();
    //     handler.write("new.uos");
    // }
}
  1. JavaScript class for .UOS file handling:
const JSZip = require('jszip'); // Assume Node.js environment with jszip installed

class UOSHandler {
    constructor(filename) {
        this.filename = filename;
        this.properties = {};
    }

    async read() {
        const fs = require('fs');
        const buffer = fs.readFileSync(this.filename);
        const zip = await JSZip.loadAsync(buffer);
        this.properties.files = [];
        zip.forEach((relativePath) => {
            this.properties.files.push(relativePath);
        });
        const mimetype = await zip.file('mimetype')?.async('string');
        if (mimetype) {
            this.properties.mimetype = mimetype;
        }
        const documentXml = await zip.file('Object 1/document.xml')?.async('string');
        if (documentXml) {
            const parser = new DOMParser();
            const xmlDoc = parser.parseFromString(documentXml, 'application/xml');
            const root = xmlDoc.documentElement;
            this.properties.rootElement = root.tagName;
            this.properties.rootAttributes = {};
            for (let attr of root.attributes) {
                this.properties.rootAttributes[attr.name] = attr.value;
            }
        }
    }

    async write(outputFilename) {
        const zip = new JSZip();
        zip.file('mimetype', this.properties.mimetype || 'application/uof+spreadsheet');
        const xmlContent = '<uof:UOF xmlns:uof="http://schemas.uof.org/cn/2003/uof" uof:version="1.0" uof:language="cn" uof:locID="u0000"></uof:UOF>';
        zip.file('Object 1/document.xml', xmlContent);
        const manifest = '<manifest:manifest xmlns:manifest="http://schemas.uof.org/cn/2003/manifest"></manifest:manifest>';
        zip.file('META-INF/manifest.xml', manifest);
        const buffer = await zip.generateAsync({type: 'nodebuffer'});
        const fs = require('fs');
        fs.writeFileSync(outputFilename, buffer);
    }

    printProperties() {
        console.log('Properties:');
        console.log(this.properties);
    }
}

// Example usage:
// const handler = new UOSHandler('example.uos');
// await handler.read();
// handler.printProperties();
// await handler.write('new.uos');
  1. C class (using C++ for class support, with libzip and libxml2 assumed available):
#include <iostream>
#include <zip.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <string>
#include <vector>
#include <map>

class UOSHandler {
private:
    std::string filename;
    std::string mimetype;
    std::string rootElement;
    std::map<std::string, std::string> rootAttributes;
    std::vector<std::string> files;

public:
    UOSHandler(const std::string& fn) : filename(fn) {}

    int read() {
        zip_t* z = zip_open(filename.c_str(), ZIP_RDONLY, nullptr);
        if (!z) return 1;

        zip_int64_t numEntries = zip_get_num_entries(z, 0);
        for (zip_int64_t i = 0; i < numEntries; ++i) {
            const char* name = zip_get_name(z, i, 0);
            if (name) files.push_back(name);
        }

        zip_file_t* mimetypeFile = zip_fopen(z, "mimetype", 0);
        if (mimetypeFile) {
            char buf[1024];
            zip_int64_t len = zip_fread(mimetypeFile, buf, sizeof(buf) - 1);
            if (len > 0) {
                buf[len] = '\0';
                mimetype = buf;
            }
            zip_fclose(mimetypeFile);
        }

        zip_file_t* documentFile = zip_fopen(z, "Object 1/document.xml", 0);
        if (documentFile) {
            std::string xmlContent;
            char buf[1024];
            zip_int64_t len;
            while ((len = zip_fread(documentFile, buf, sizeof(buf))) > 0) {
                xmlContent.append(buf, len);
            }
            zip_fclose(documentFile);

            xmlDocPtr doc = xmlParseMemory(xmlContent.c_str(), xmlContent.size());
            if (doc) {
                xmlNodePtr root = xmlDocGetRootElement(doc);
                if (root) {
                    rootElement = reinterpret_cast<const char*>(root->name);
                    for (xmlAttrPtr attr = root->properties; attr; attr = attr->next) {
                        std::string name = reinterpret_cast<const char*>(attr->name);
                        std::string value = reinterpret_cast<const char*>(attr->children->content);
                        rootAttributes[name] = value;
                    }
                }
                xmlFreeDoc(doc);
            }
        }
        zip_close(z);
        return 0;
    }

    int write(const std::string& outputFilename) {
        zip_t* z = zip_open(outputFilename.c_str(), ZIP_CREATE | ZIP_TRUNCATE, nullptr);
        if (!z) return 1;

        zip_source_t* src = zip_source_buffer(z, mimetype.c_str(), mimetype.size(), 0);
        zip_file_add(z, "mimetype", src, ZIP_FL_ENC_UTF_8);

        std::string xmlContent = "<uof:UOF xmlns:uof=\"http://schemas.uof.org/cn/2003/uof\" uof:version=\"1.0\" uof:language=\"cn\" uof:locID=\"u0000\"></uof:UOF>";
        src = zip_source_buffer(z, xmlContent.c_str(), xmlContent.size(), 0);
        zip_file_add(z, "Object 1/document.xml", src, ZIP_FL_ENC_UTF_8);

        std::string manifest = "<manifest:manifest xmlns:manifest=\"http://schemas.uof.org/cn/2003/manifest\"></manifest:manifest>";
        src = zip_source_buffer(z, manifest.c_str(), manifest.size(), 0);
        zip_file_add(z, "META-INF/manifest.xml", src, ZIP_FL_ENC_UTF_8);

        zip_close(z);
        return 0;
    }

    void printProperties() {
        std::cout << "Properties:" << std::endl;
        std::cout << "Files:" << std::endl;
        for (const auto& f : files) std::cout << f << std::endl;
        std::cout << "Mimetype: " << mimetype << std::endl;
        std::cout << "Root Element: " << rootElement << std::endl;
        std::cout << "Root Attributes:" << std::endl;
        for (const auto& attr : rootAttributes) {
            std::cout << attr.first << ": " << attr.second << std::endl;
        }
    }
};

// Example usage:
// int main() {
//     UOSHandler handler("example.uos");
//     handler.read();
//     handler.printProperties();
//     handler.write("new.uos");
//     return 0;
// }