Task 709: .SXD File Format
Task 709: .SXD File Format
1. List of Properties Intrinsic to the .SXD File Format
The .SXD file format is a ZIP-compressed package containing XML-based subdocuments and resources, used for vector drawings in OpenOffice.org Draw (version 1.x) and StarOffice Draw. It is the precursor to the OpenDocument Graphics (.ODG) format. The specifications are defined in the OpenOffice.org XML File Format Technical Reference Manual (December 2002), which outlines the package structure, XML elements, attributes, and DTD entities. The format uses namespaces like office, draw, style, presentation, dr3d, and svg. Key intrinsic properties (structural components, MIME type, validation rules, and core elements/attributes) are listed below. These are "intrinsic to its file system" in the sense of the format's internal structure, MIME identification, and XML schema properties that define how the file is organized, validated, and processed on disk or in memory.
Package-Level Properties (ZIP Structure)
- Container Format: ZIP archive (magic bytes: PK\003\004).
- MIME Type: application/vnd.sun.xml.draw (stored in plain text file
mimetypeas the first entry in the ZIP, uncompressed). - Root Document Class: office:class="drawing" (in root elements of main XML files).
- Version Attribute: office:version="1.0" (for DTD validation; forward-compatible processing ignores unknown elements/attributes).
- Required Subfiles:
mimetype: Plain text MIME type.content.xml: Main drawing content (root:<office:document-content>).styles.xml: Styles and graphic definitions (root:<office:document-styles>).meta.xml: Metadata (root:<office:document-meta>).settings.xml: Application/view settings (root:<office:document-settings>).META-INF/manifest.xml: Package manifest (root:<manifest:manifest>, lists files with MIME types, optional encryption).- Optional Directories/Subfiles:
Pictures/: Embedded binary images (e.g., PNG, JPEG; referenced via xlink:href).Basic/: StarBasic macros (XML scripts).Dialogs/: Custom dialogs (XML).thumbnails/thumbnail.png: Preview image.Obj*/: Embedded objects (e.g., charts in XML or binary).- Encryption Support: Optional per-file (in manifest:
<manifest:encryption-data>, algorithms like Blowfish, key derivation like PBKDF2). - White-Space/EOL Handling: XML 1.0 compliant; spaces normalized to #x20 in attributes; EOL to LF (#xA); CR as .
- Encoding: UTF-8 (multilingual support).
XML Schema Properties (Key Elements and Attributes)
These define the drawing-specific content, styles, and metadata. DTD entities include types like %coordinate;, %length;, %points;, %color;, %boolean;, etc. Core elements are from the draw namespace.
- Metadata Properties (meta.xml):
<dc:title>,<dc:creator>,<dc:date>,<dc:language>,<meta:creation-date>,<meta:document-statistic>(attributes: meta:object-count, meta:page-count).- Settings Properties (settings.xml):
<config:config-item-set>for views (e.g., window position, zoom), printers, presentation configs (e.g., animation speed).- Styles Properties (styles.xml):
<office:styles>,<office:automatic-styles>,<office:master-styles>.- Graphic styles:
<style:style style:family="graphic">with<style:properties>(e.g., draw:fill-color, draw:stroke, svg:stroke-width). - Layer set:
<draw:layer-set> <draw:layer draw:name="..." draw:protected="..."/>. - Master page:
<style:master-page style:name="..." draw:style-name="..." style:page-master-name="...">(includes headers, footers, backgrounds). - Graphic types:
<draw:gradient>,<draw:hatch>,<draw:marker>,<draw:stroke-dash>. - Content Properties (content.xml):
<office:body>containing<draw:page draw:name="..." draw:master-page-name="..." presentation:presentation-page-layout-name="...">.- Shapes:
%shapes;entity includes<draw:rect>,<draw:line>,<draw:polyline>,<draw:polygon>,<draw:path>,<draw:circle>,<draw:ellipse>,<draw:connector>,<draw:caption>,<draw:measure>,<draw:control>,<draw:page-thumbnail>,<draw:g>(grouping). - Common shape attributes: svg:x/y (position), svg:width/height (size), draw:transform (rotation/scale), draw:style-name, draw:layer-name, draw:z-index, draw:id.
- Presentation elements:
<presentation:animations>,<presentation:notes>,<presentation:show-shape>, etc. (attributes: presentation:effect, presentation:speed). - 3D elements:
<dr3d:scene>,<dr3d:light>,<dr3d:sphere>,<dr3d:cube>,<dr3d:extrude>. - Text in shapes:
%draw-text;entity (e.g.,<text:p>,<text:list>). - Forms:
<office:forms> <form:form>. - Manifest Properties (META-INF/manifest.xml):
<manifest:file-entry manifest:media-type="..." manifest:full-path="...">for each file.- Validation Properties:
- DTD-based (office.dtd implied); entities for types (e.g., %vector3D; for 3D positions).
- Forward compatibility: Ignore unknown content; preserve alien attributes in
<style:properties>.
These properties ensure the file's integrity, portability, and editability without proprietary tools.
2. Two Direct Download Links for .SXD Files
- http://fr.openoffice.org/Documentation/Outils/milifred3.sxd (a macro-enabled drawing tool for generating graph paper).
- http://fr.openoffice.org/Documentation/Outils/FairePart-Naissance.sxd (a sample birth announcement drawing).
3. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .SXD File Dump
Here's an embedded HTML page with JavaScript that allows drag-and-drop of a .SXD file. It uses JSZip to unzip the file and DOMParser to parse XML, then dumps all properties (package files, key XML elements/attributes from meta/content/styles/settings) to the screen in a readable format.
Drag and Drop .SXD File Here
4. Python Class for .SXD Handling
import zipfile
import xml.etree.ElementTree as ET
from io import BytesIO
class SXDHandler:
def __init__(self, filepath):
self.filepath = filepath
self.zip = None
self.properties = {}
def open(self):
self.zip = zipfile.ZipFile(self.filepath, 'r')
def decode_read(self):
if not self.zip:
self.open()
self.properties = {
'package_files': self.zip.namelist(),
'mimetype': self.zip.read('mimetype').decode('utf-8').strip() if 'mimetype' in self.zip.namelist() else None,
}
for fname in ['meta.xml', 'content.xml', 'styles.xml', 'settings.xml']:
if fname in self.zip.namelist():
xml_content = self.zip.read(fname)
root = ET.fromstring(xml_content)
self.properties[fname] = {
'root': root.tag,
'attributes': dict(root.attrib),
}
# Extract sample props
if fname == 'meta.xml':
self.properties[fname]['title'] = root.find('.//{http://purl.org/dc/elements/1.1/}title').text if root.find('.//{http://purl.org/dc/elements/1.1/}title') is not None else None
self.properties[fname]['creator'] = root.find('.//{http://purl.org/dc/elements/1.1/}creator').text if root.find('.//{http://purl.org/dc/elements/1.1/}creator') is not None else None
elif fname == 'content.xml':
pages = root.findall('.//{http://openoffice.org/2000/drawing}page')
self.properties[fname]['page_count'] = len(pages)
self.properties[fname]['shapes_count'] = len(root.findall('.//{http://openoffice.org/2000/drawing}*'))
elif fname == 'styles.xml':
masters = root.findall('.//{http://openoffice.org/2000/style}master-page')
self.properties[fname]['master_pages'] = len(masters)
elif fname == 'settings.xml':
configs = root.findall('.//{http://openoffice.org/2000/office}config-item-set')
self.properties[fname]['config_sets'] = len(configs)
def print_properties(self):
if not self.properties:
self.decode_read()
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, new_filepath=None):
if not self.zip:
raise ValueError("No file opened")
# For simplicity, copy existing and modify example (e.g., add meta title)
with zipfile.ZipFile(new_filepath or self.filepath, 'w') as new_zip:
for item in self.zip.infolist():
data = self.zip.read(item.filename)
if item.filename == 'meta.xml':
root = ET.fromstring(data)
title_el = root.find('.//{http://purl.org/dc/elements/1.1/}title')
if title_el is not None:
title_el.text = 'Modified Title'
data = ET.tostring(root, encoding='utf-8', xml_declaration=True)
new_zip.writestr(item, data)
def close(self):
if self.zip:
self.zip.close()
# Usage example:
# handler = SXDHandler('example.sxd')
# handler.open()
# handler.decode_read()
# handler.print_properties()
# handler.write('modified.sxd')
# handler.close()
5. Java Class for .SXD Handling
import java.io.*;
import java.util.zip.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
public class SXDHandler {
private String filepath;
private ZipFile zip;
private java.util.Map<String, Object> properties = new java.util.HashMap<>();
public SXDHandler(String filepath) {
this.filepath = filepath;
}
public void open() throws IOException {
zip = new ZipFile(filepath);
}
public void decodeRead() throws IOException, ParserConfigurationException, SAXException {
if (zip == null) open();
java.util.Enumeration<? extends ZipEntry> entries = zip.entries();
java.util.List<String> packageFiles = new java.util.ArrayList<>();
while (entries.hasMoreElements()) {
packageFiles.add(entries.nextElement().getName());
}
properties.put("package_files", packageFiles);
ZipEntry mimetypeEntry = zip.getEntry("mimetype");
if (mimetypeEntry != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(zip.getInputStream(mimetypeEntry)));
properties.put("mimetype", reader.readLine().trim());
}
String[] filesToParse = {"meta.xml", "content.xml", "styles.xml", "settings.xml"};
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
for (String fname : filesToParse) {
ZipEntry entry = zip.getEntry(fname);
if (entry != null) {
Document doc = builder.parse(zip.getInputStream(entry));
Element root = doc.getDocumentElement();
java.util.Map<String, Object> fileProps = new java.util.HashMap<>();
fileProps.put("root", root.getTagName());
NamedNodeMap attrs = root.getAttributes();
java.util.Map<String, String> attrMap = new java.util.HashMap<>();
for (int i = 0; i < attrs.getLength(); i++) {
Node attr = attrs.item(i);
attrMap.put(attr.getNodeName(), attr.getNodeValue());
}
fileProps.put("attributes", attrMap);
// Extract sample props
if (fname.equals("meta.xml")) {
fileProps.put("title", getTextContent(doc, "dc:title"));
fileProps.put("creator", getTextContent(doc, "dc:creator"));
} else if (fname.equals("content.xml")) {
NodeList pages = doc.getElementsByTagNameNS("http://openoffice.org/2000/drawing", "page");
fileProps.put("page_count", pages.getLength());
NodeList shapes = doc.getElementsByTagNameNS("http://openoffice.org/2000/drawing", "*");
fileProps.put("shapes_count", shapes.getLength());
} else if (fname.equals("styles.xml")) {
NodeList masters = doc.getElementsByTagNameNS("http://openoffice.org/2000/style", "master-page");
fileProps.put("master_pages", masters.getLength());
} else if (fname.equals("settings.xml")) {
NodeList configs = doc.getElementsByTagNameNS("http://openoffice.org/2000/office", "config-item-set");
fileProps.put("config_sets", configs.getLength());
}
properties.put(fname, fileProps);
}
}
}
private String getTextContent(Document doc, String tag) {
NodeList nodes = doc.getElementsByTagName(tag);
return (nodes.getLength() > 0) ? nodes.item(0).getTextContent() : null;
}
public void printProperties() {
if (properties.isEmpty()) {
try {
decodeRead();
} catch (Exception e) {
e.printStackTrace();
}
}
properties.forEach((key, value) -> System.out.println(key + ": " + value));
}
public void write(String newFilepath) throws IOException, ParserConfigurationException, SAXException {
if (zip == null) throw new IllegalStateException("No file opened");
try (ZipOutputStream newZip = new ZipOutputStream(new FileOutputStream(newFilepath))) {
java.util.Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
newZip.putNextEntry(new ZipEntry(entry.getName()));
InputStream is = zip.getInputStream(entry);
if (entry.getName().equals("meta.xml")) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(is);
Node title = doc.getElementsByTagName("dc:title").item(0);
if (title != null) title.setTextContent("Modified Title");
javax.xml.transform.TransformerFactory tf = javax.xml.transform.TransformerFactory.newInstance();
javax.xml.transform.Transformer trans = tf.newTransformer();
javax.xml.transform.dom.DOMSource source = new javax.xml.transform.dom.DOMSource(doc);
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
trans.transform(source, new javax.xml.transform.stream.StreamResult(baos));
newZip.write(baos.toByteArray());
} else {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
newZip.write(buffer, 0, len);
}
}
newZip.closeEntry();
is.close();
}
}
}
public void close() throws IOException {
if (zip != null) zip.close();
}
// Usage example:
// public static void main(String[] args) throws Exception {
// SXDHandler handler = new SXDHandler("example.sxd");
// handler.open();
// handler.decodeRead();
// handler.printProperties();
// handler.write("modified.sxd");
// handler.close();
// }
}
6. JavaScript Class for .SXD Handling
const JSZip = require('jszip'); // For Node.js; use browser script for client-side
const { DOMParser } = require('xmldom'); // For Node.js
class SXDHandler {
constructor(filepath) {
this.filepath = filepath;
this.zip = null;
this.properties = {};
}
async open() {
const fs = require('fs');
const data = fs.readFileSync(this.filepath);
this.zip = await JSZip.loadAsync(data);
}
async decodeRead() {
if (!this.zip) await this.open();
this.properties.package_files = Object.keys(this.zip.files);
const mimetype = await this.zip.file('mimetype')?.async('string');
if (mimetype) this.properties.mimetype = mimetype.trim();
const parser = new DOMParser();
const filesToParse = ['meta.xml', 'content.xml', 'styles.xml', 'settings.xml'];
for (const fname of filesToParse) {
const content = await this.zip.file(fname)?.async('string');
if (content) {
const doc = parser.parseFromString(content, 'application/xml');
const root = doc.documentElement;
this.properties[fname] = {
root: root.tagName,
attributes: {},
};
for (let i = 0; i < root.attributes.length; i++) {
const attr = root.attributes[i];
this.properties[fname].attributes[attr.name] = attr.value;
}
// Extract sample props
if (fname === 'meta.xml') {
this.properties[fname].title = doc.getElementsByTagName('dc:title')[0]?.textContent || null;
this.properties[fname].creator = doc.getElementsByTagName('dc:creator')[0]?.textContent || null;
} else if (fname === 'content.xml') {
const pages = doc.getElementsByTagNameNS('http://openoffice.org/2000/drawing', 'page');
this.properties[fname].page_count = pages.length;
const shapes = doc.getElementsByTagNameNS('http://openoffice.org/2000/drawing', '*');
this.properties[fname].shapes_count = shapes.length;
} else if (fname === 'styles.xml') {
const masters = doc.getElementsByTagNameNS('http://openoffice.org/2000/style', 'master-page');
this.properties[fname].master_pages = masters.length;
} else if (fname === 'settings.xml') {
const configs = doc.getElementsByTagNameNS('http://openoffice.org/2000/office', 'config-item-set');
this.properties[fname].config_sets = configs.length;
}
}
}
}
printProperties() {
if (Object.keys(this.properties).length === 0) {
this.decodeRead().then(() => console.log(this.properties));
} else {
console.log(this.properties);
}
}
async write(newFilepath) {
if (!this.zip) throw new Error('No file opened');
// Modify example: change meta title
let metaContent = await this.zip.file('meta.xml')?.async('string');
if (metaContent) {
const doc = new DOMParser().parseFromString(metaContent, 'application/xml');
const title = doc.getElementsByTagName('dc:title')[0];
if (title) title.textContent = 'Modified Title';
const serializer = new (require('xmldom').XMLSerializer)();
metaContent = serializer.serializeToString(doc);
this.zip.file('meta.xml', metaContent);
}
const buffer = await this.zip.generateAsync({type: 'nodebuffer'});
const fs = require('fs');
fs.writeFileSync(newFilepath, buffer);
}
close() {
this.zip = null;
}
}
// Usage example (Node.js):
// const handler = new SXDHandler('example.sxd');
// await handler.open();
// await handler.decodeRead();
// handler.printProperties();
// await handler.write('modified.sxd');
// handler.close();
7. C Class (Struct) for .SXD Handling
Note: C implementation uses libzip and libxml2 (assume installed). This is a basic struct-based class-like approach.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zip.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
typedef struct {
char* filepath;
zip_t* zip;
// Simplified properties: use a map-like array for demo
char* properties[10][2]; // key-value pairs, limited
int prop_count;
} SXDHandler;
SXDHandler* sxd_create(const char* filepath) {
SXDHandler* handler = malloc(sizeof(SXDHandler));
handler->filepath = strdup(filepath);
handler->zip = NULL;
handler->prop_count = 0;
return handler;
}
int sxd_open(SXDHandler* handler) {
int err = 0;
handler->zip = zip_open(handler->filepath, ZIP_RDONLY, &err);
return (handler->zip != NULL) ? 0 : -1;
}
void add_property(SXDHandler* handler, const char* key, const char* value) {
if (handler->prop_count < 10) {
handler->properties[handler->prop_count][0] = strdup(key);
handler->properties[handler->prop_count][1] = strdup(value);
handler->prop_count++;
}
}
int sxd_decode_read(SXDHandler* handler) {
if (!handler->zip) if (sxd_open(handler) != 0) return -1;
// Package files (simplified, print count)
zip_int64_t num_entries = zip_get_num_entries(handler->zip, 0);
char buf[32];
snprintf(buf, sizeof(buf), "%lld", num_entries);
add_property(handler, "package_files_count", buf);
// Mimetype
zip_file_t* f = zip_fopen(handler->zip, "mimetype", 0);
if (f) {
char mime[256];
zip_fread(f, mime, 255);
mime[255] = '\0';
add_property(handler, "mimetype", strtok(mime, "\n"));
zip_fclose(f);
}
// Parse XML files (simplified, check existence and root)
const char* files[] = {"meta.xml", "content.xml", "styles.xml", "settings.xml"};
for (int i = 0; i < 4; i++) {
zip_stat_t sb;
if (zip_stat(handler->zip, files[i], 0, &sb) == 0) {
char* content = malloc(sb.size + 1);
f = zip_fopen(handler->zip, files[i], 0);
zip_fread(f, content, sb.size);
content[sb.size] = '\0';
zip_fclose(f);
xmlDocPtr doc = xmlReadMemory(content, sb.size, NULL, NULL, 0);
if (doc) {
xmlNodePtr root = xmlDocGetRootElement(doc);
if (root) {
char key[64];
snprintf(key, sizeof(key), "%s_root", files[i]);
add_property(handler, key, (char*)root->name);
// Sample: for meta, get title
if (strcmp(files[i], "meta.xml") == 0) {
xmlNodePtr cur = root->children;
while (cur) {
if (xmlStrcmp(cur->name, (const xmlChar*)"title") == 0) {
add_property(handler, "meta_title", (char*)xmlNodeGetContent(cur));
break;
}
cur = cur->next;
}
}
}
xmlFreeDoc(doc);
}
free(content);
}
}
return 0;
}
void sxd_print_properties(SXDHandler* handler) {
if (handler->prop_count == 0) sxd_decode_read(handler);
for (int i = 0; i < handler->prop_count; i++) {
printf("%s: %s\n", handler->properties[i][0], handler->properties[i][1]);
}
}
int sxd_write(SXDHandler* handler, const char* new_filepath) {
if (!handler->zip) return -1;
zip_t* new_zip = zip_open(new_filepath, ZIP_CREATE | ZIP_TRUNCATE, NULL);
if (!new_zip) return -1;
zip_int64_t num = zip_get_num_entries(handler->zip, 0);
for (zip_int64_t i = 0; i < num; i++) {
const char* name = zip_get_name(handler->zip, i, 0);
zip_stat_t sb;
zip_stat_index(handler->zip, i, 0, &sb);
char* data = malloc(sb.size);
zip_file_t* f = zip_fopen_index(handler->zip, i, 0);
zip_fread(f, data, sb.size);
zip_fclose(f);
if (strcmp(name, "meta.xml") == 0) {
// Modify title
xmlDocPtr doc = xmlReadMemory(data, sb.size, NULL, NULL, 0);
xmlNodePtr title = xmlNewNode(NULL, (xmlChar*)"title");
xmlNodeSetContent(title, (xmlChar*)"Modified Title");
// Simplified: replace entire meta.xml with new minimal one for demo
xmlFreeDoc(doc);
free(data);
data = strdup("<office:document-meta><dc:title>Modified Title</dc:title></office:document-meta>");
sb.size = strlen(data);
}
zip_source_t* src = zip_source_buffer(new_zip, data, sb.size, 1);
zip_file_add(new_zip, name, src, ZIP_FL_OVERWRITE);
}
zip_close(new_zip);
return 0;
}
void sxd_close(SXDHandler* handler) {
if (handler->zip) zip_close(handler->zip);
for (int i = 0; i < handler->prop_count; i++) {
free(handler->properties[i][0]);
free(handler->properties[i][1]);
}
free(handler->filepath);
free(handler);
}
// Usage example:
// int main() {
// SXDHandler* handler = sxd_create("example.sxd");
// sxd_open(handler);
// sxd_decode_read(handler);
// sxd_print_properties(handler);
// sxd_write(handler, "modified.sxd");
// sxd_close(handler);
// return 0;
// }