Task 471: .ODB File Format
Task 471: .ODB File Format
1. List of All Properties Intrinsic to the .ODB File Format
The .ODB file format, known as the OpenDocument Database format, is a ZIP-based container adhering to the OASIS OpenDocument standard (ISO/IEC 26300). Its intrinsic properties include the following, derived from the format's structural and metadata elements:
MIME Type: The content of the 'mimetype' file, typically "application/vnd.oasis.opendocument.base" or "application/vnd.oasis.opendocument.database" in older implementations.
ODF Version: The 'office:version' attribute in root elements of key XML files (e.g., content.xml, manifest.xml), commonly "1.2" or "1.3".
Creator: The metadata value from meta.xml, specified in the 'dc:creator' element.
Creation Date: The metadata value from meta.xml, specified in the 'meta:creation-date' element, in ISO 8601 format.
Editing Duration: The metadata value from meta.xml, specified in the 'meta:editing-duration' element, in ISO 8601 duration format.
Generator: The metadata value from meta.xml, specified in the 'meta:generator' element, indicating the software that created or last modified the file.
Language: The metadata value from meta.xml, specified in the 'dc:language' element, using BCP 47 language tags.
Document Statistics: Metadata values from meta.xml, including attributes like 'meta:table-count', 'meta:cell-count', or 'meta:object-count', representing counts of tables, cells, or other objects.
Database Connection Resource: The 'xlink:href' attribute in the 'db:connection-resource' element within content.xml, e.g., "sdbc:embedded:hsqldb" for embedded HSQLDB.
Number of Forms: The count of 'forms:form' elements or similar within 'db:forms' in content.xml.
Number of Queries: The count of 'db:query' elements within 'db:queries' in content.xml.
Number of Reports: The count of 'db:report' elements within 'db:reports' in content.xml.
Embedded Database Engine: Determined by the presence and contents of the 'database/' directory; "HSQLDB" if files like 'data', 'log', 'properties', 'script', 'backup' are present, or "Firebird" if 'firebird.fdb' is present.
Presence of Thumbnail: A boolean indicating if 'Thumbnails/thumbnail.png' exists in the ZIP archive.
Digital Signatures: The presence of signature files in 'META-INF/', such as 'signatures.xml' or similar, indicating signed content.
These properties define the format's structure, metadata, and database-specific characteristics.
2. Two Direct Download Links for .ODB Files
https://wiki.documentfoundation.org/images/2/2f/FR.FAQ.BASE_137_OuvrirForm.odb
https://wiki.documentfoundation.org/images/2/2c/Media_with_Macros.odb
3. HTML/JavaScript for Drag and Drop .ODB File Dump
The following is a self-contained HTML page with embedded JavaScript that allows users to drag and drop a .ODB file. It uses the JSZip library (loaded from a CDN) to unzip the file and extract the properties listed in part 1, displaying them on the screen. This can be embedded in a Ghost blog post.
4. Python Class for .ODB File Handling
The following Python class can open a .ODB file, decode and read the properties, print them to console, and write a minimal .ODB file with basic properties.
import zipfile
from xml.etree import ElementTree as ET
import io
import datetime
class ODBHandler:
def __init__(self, filename=None):
self.filename = filename
self.properties = {}
def read(self):
with zipfile.ZipFile(self.filename, 'r') as z:
# 1. MIME Type
if 'mimetype' in z.namelist():
with z.open('mimetype') as f:
self.properties['MIME Type'] = f.read().decode('utf-8').strip()
# 2. ODF Version
if 'content.xml' in z.namelist():
with z.open('content.xml') as f:
content = ET.parse(f)
self.properties['ODF Version'] = content.getroot().attrib.get('{http://www.w3.org/ns/office#}version', 'Not found')
# 9. Database Connection Resource
ns = {'db': 'urn:oasis:names:tc:opendocument:xmlns:database:1.0', 'xlink': 'http://www.w3.org/1999/xlink'}
conn = content.find('.//db:connection-resource', ns)
self.properties['Database Connection Resource'] = conn.attrib.get('{http://www.w3.org/1999/xlink}href', 'Not found') if conn is not None else 'Not found'
# 10-12. Number of Forms, Queries, Reports
self.properties['Number of Forms'] = len(content.findall('.//db:forms/*', ns))
self.properties['Number of Queries'] = len(content.findall('.//db:queries/*', ns))
self.properties['Number of Reports'] = len(content.findall('.//db:reports/*', ns))
# 3-8. Metadata from meta.xml
if 'meta.xml' in z.namelist():
with z.open('meta.xml') as f:
meta = ET.parse(f)
ns = {'dc': 'http://purl.org/dc/elements/1.1/', 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'}
self.properties['Creator'] = meta.find('.//dc:creator', ns).text if meta.find('.//dc:creator', ns) is not None else 'Not found'
self.properties['Creation Date'] = meta.find('.//meta:creation-date', ns).text if meta.find('.//meta:creation-date', ns) is not None else 'Not found'
self.properties['Editing Duration'] = meta.find('.//meta:editing-duration', ns).text if meta.find('.//meta:editing-duration', ns) is not None else 'Not found'
self.properties['Generator'] = meta.find('.//meta:generator', ns).text if meta.find('.//meta:generator', ns) is not None else 'Not found'
self.properties['Language'] = meta.find('.//dc:language', ns).text if meta.find('.//dc:language', ns) is not None else 'Not found'
self.properties['Document Statistics (table-count)'] = meta.getroot().attrib.get('{urn:oasis:names:tc:opendocument:xmlns:meta:1.0}table-count', 'Not found')
# 13. Embedded Database Engine
engine = 'None'
if 'database/script' in z.namelist():
engine = 'HSQLDB'
elif 'database/firebird.fdb' in z.namelist():
engine = 'Firebird'
self.properties['Embedded Database Engine'] = engine
# 14. Presence of Thumbnail
self.properties['Presence of Thumbnail'] = 'Yes' if 'Thumbnails/thumbnail.png' in z.namelist() else 'No'
# 15. Digital Signatures
self.properties['Digital Signatures'] = 'Yes' if 'META-INF/signatures.xml' in z.namelist() else 'No'
def print_properties(self):
for key, value in self.properties.items():
print(f'{key}: {value}')
def write(self, output_filename):
# Create a minimal .odb file with basic properties
with zipfile.ZipFile(output_filename, 'w') as z:
# mimetype
z.writestr('mimetype', 'application/vnd.oasis.opendocument.base')
# Minimal content.xml
content = '<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:db="urn:oasis:names:tc:opendocument:xmlns:database:1.0" office:version="1.2"><office:body><office:database></office:database></office:body></office:document-content>'
z.writestr('content.xml', content)
# Minimal meta.xml
meta = f'<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" office:version="1.2"><office:meta><dc:creator>Grok</dc:creator><meta:creation-date>{datetime.datetime.now().isoformat()}</meta:creation-date><meta:generator>Grok 4</meta:generator><dc:language>en-US</dc:language></office:meta></office:document-meta>'
z.writestr('meta.xml', meta)
# Minimal manifest.xml in META-INF
manifest = '<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"><manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.base" manifest:full-path="/"/><manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/><manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/></manifest:manifest>'
z.writestr('META-INF/manifest.xml', manifest)
# Example usage
# handler = ODBHandler('example.odb')
# handler.read()
# handler.print_properties()
# handler.write('new.odb')
5. Java Class for .ODB File Handling
The following Java class can open a .ODB file, decode and read the properties, print them to console, and write a minimal .ODB file with basic properties. It uses java.util.zip and javax.xml.parsers.
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.util.HashMap;
import java.util.Map;
import java.util.Date;
import java.text.SimpleDateFormat;
public class ODBHandler {
private String filename;
private Map<String, String> properties = new HashMap<>();
public ODBHandler(String filename) {
this.filename = filename;
}
public void read() throws Exception {
try (ZipFile z = new ZipFile(filename)) {
// 1. MIME Type
ZipEntry mimeEntry = z.getEntry("mimetype");
if (mimeEntry != null) {
BufferedReader reader = new BufferedReader(new InputStreamReader(z.getInputStream(mimeEntry)));
properties.put("MIME Type", reader.readLine().trim());
}
// 2. ODF Version, 9-12. From content.xml
ZipEntry contentEntry = z.getEntry("content.xml");
if (contentEntry != null) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(z.getInputStream(contentEntry));
properties.put("ODF Version", doc.getDocumentElement().getAttribute("office:version"));
// 9. Database Connection Resource
NodeList conn = doc.getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:database:1.0", "connection-resource");
if (conn.getLength() > 0) {
properties.put("Database Connection Resource", ((Element) conn.item(0)).getAttributeNS("http://www.w3.org/1999/xlink", "href"));
} else {
properties.put("Database Connection Resource", "Not found");
}
// 10-12. Number of Forms, Queries, Reports
properties.put("Number of Forms", String.valueOf(doc.getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:database:1.0", "forms").item(0).getChildNodes().getLength()));
properties.put("Number of Queries", String.valueOf(doc.getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:database:1.0", "queries").item(0).getChildNodes().getLength()));
properties.put("Number of Reports", String.valueOf(doc.getElementsByTagNameNS("urn:oasis:names:tc:opendocument:xmlns:database:1.0", "reports").item(0).getChildNodes().getLength()));
}
// 3-8. Metadata from meta.xml
ZipEntry metaEntry = z.getEntry("meta.xml");
if (metaEntry != null) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(z.getInputStream(metaEntry));
properties.put("Creator", getTextContent(doc, "dc", "creator", "http://purl.org/dc/elements/1.1/"));
properties.put("Creation Date", getTextContent(doc, "meta", "creation-date", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"));
properties.put("Editing Duration", getTextContent(doc, "meta", "editing-duration", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"));
properties.put("Generator", getTextContent(doc, "meta", "generator", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"));
properties.put("Language", getTextContent(doc, "dc", "language", "http://purl.org/dc/elements/1.1/"));
properties.put("Document Statistics (table-count)", doc.getDocumentElement().getAttributeNS("urn:oasis:names:tc:opendocument:xmlns:meta:1.0", "table-count"));
}
// 13. Embedded Database Engine
String engine = "None";
if (z.getEntry("database/script") != null) engine = "HSQLDB";
if (z.getEntry("database/firebird.fdb") != null) engine = "Firebird";
properties.put("Embedded Database Engine", engine);
// 14. Presence of Thumbnail
properties.put("Presence of Thumbnail", z.getEntry("Thumbnails/thumbnail.png") != null ? "Yes" : "No");
// 15. Digital Signatures
properties.put("Digital Signatures", z.getEntry("META-INF/signatures.xml") != null ? "Yes" : "No");
}
}
private String getTextContent(Document doc, String prefix, String tag, String ns) {
NodeList nl = doc.getElementsByTagNameNS(ns, tag);
return nl.getLength() > 0 ? nl.item(0).getTextContent() : "Not found";
}
public void printProperties() {
for (Map.Entry<String, String> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void write(String outputFilename) throws Exception {
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(outputFilename))) {
// mimetype
ZipEntry mimeEntry = new ZipEntry("mimetype");
zos.putNextEntry(mimeEntry);
zos.write("application/vnd.oasis.opendocument.base".getBytes());
zos.closeEntry();
// Minimal content.xml
ZipEntry contentEntry = new ZipEntry("content.xml");
zos.putNextEntry(contentEntry);
String content = "<office:document-content xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:db=\"urn:oasis:names:tc:opendocument:xmlns:database:1.0\" office:version=\"1.2\"><office:body><office:database></office:database></office:body></office:document-content>";
zos.write(content.getBytes());
zos.closeEntry();
// Minimal meta.xml
ZipEntry metaEntry = new ZipEntry("meta.xml");
zos.putNextEntry(metaEntry);
String date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date());
String meta = "<office:document-meta xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\" office:version=\"1.2\"><office:meta><dc:creator>Grok</dc:creator><meta:creation-date>" + date + "</meta:creation-date><meta:generator>Grok 4</meta:generator><dc:language>en-US</dc:language></office:meta></office:document-meta>";
zos.write(meta.getBytes());
zos.closeEntry();
// Minimal manifest.xml
ZipEntry manifestEntry = new ZipEntry("META-INF/manifest.xml");
zos.putNextEntry(manifestEntry);
String manifest = "<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\"><manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.base\" manifest:full-path=\"/\"/><manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/><manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/></manifest:manifest>";
zos.write(manifest.getBytes());
zos.closeEntry();
}
}
// Example usage
// public static void main(String[] args) throws Exception {
// ODBHandler handler = new ODBHandler("example.odb");
// handler.read();
// handler.printProperties();
// handler.write("new.odb");
// }
}
6. JavaScript Class for .ODB File Handling
The following JavaScript class can open a .ODB file (using JSZip for ZIP handling), decode and read the properties, print them to console, and write a minimal .ODB file as a blob for download.
class ODBHandler {
constructor() {
this.properties = {};
}
async read(file) {
const zip = await JSZip.loadAsync(file);
// 1. MIME Type
const mime = await zip.file('mimetype')?.async('string');
this.properties['MIME Type'] = mime ? mime.trim() : 'Not found';
// 2. ODF Version, 9-12. From content.xml
const content = await zip.file('content.xml')?.async('string');
if (content) {
const parser = new DOMParser();
const doc = parser.parseFromString(content, 'application/xml');
this.properties['ODF Version'] = doc.documentElement.getAttribute('office:version') || 'Not found';
// 9. Database Connection Resource
const conn = doc.querySelector('db\\:connection-resource');
this.properties['Database Connection Resource'] = conn ? conn.getAttribute('xlink:href') : 'Not found';
// 10-12. Number of Forms, Queries, Reports
this.properties['Number of Forms'] = doc.querySelectorAll('db\\:forms > *').length;
this.properties['Number of Queries'] = doc.querySelectorAll('db\\:queries > *').length;
this.properties['Number of Reports'] = doc.querySelectorAll('db\\:reports > *').length;
}
// 3-8. Metadata from meta.xml
const meta = await zip.file('meta.xml')?.async('string');
if (meta) {
const parser = new DOMParser();
const doc = parser.parseFromString(meta, 'application/xml');
this.properties['Creator'] = doc.querySelector('dc\\:creator')?.textContent || 'Not found';
this.properties['Creation Date'] = doc.querySelector('meta\\:creation-date')?.textContent || 'Not found';
this.properties['Editing Duration'] = doc.querySelector('meta\\:editing-duration')?.textContent || 'Not found';
this.properties['Generator'] = doc.querySelector('meta\\:generator')?.textContent || 'Not found';
this.properties['Language'] = doc.querySelector('dc\\:language')?.textContent || 'Not found';
this.properties['Document Statistics (table-count)'] = doc.documentElement.getAttribute('meta:table-count') || 'Not found';
}
// 13. Embedded Database Engine
let engine = 'None';
if (zip.file('database/script')) engine = 'HSQLDB';
if (zip.file('database/firebird.fdb')) engine = 'Firebird';
this.properties['Embedded Database Engine'] = engine;
// 14. Presence of Thumbnail
this.properties['Presence of Thumbnail'] = zip.file('Thumbnails/thumbnail.png') ? 'Yes' : 'No';
// 15. Digital Signatures
this.properties['Digital Signatures'] = zip.file('META-INF/signatures.xml') ? 'Yes' : 'No';
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
async write() {
const zip = new JSZip();
zip.file('mimetype', 'application/vnd.oasis.opendocument.base');
const content = '<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:db="urn:oasis:names:tc:opendocument:xmlns:database:1.0" office:version="1.2"><office:body><office:database></office:database></office:body></office:document-content>';
zip.file('content.xml', content);
const date = new Date().toISOString();
const meta = `<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" office:version="1.2"><office:meta><dc:creator>Grok</dc:creator><meta:creation-date>${date}</meta:creation-date><meta:generator>Grok 4</meta:generator><dc:language>en-US</dc:language></office:meta></office:document-meta>`;
zip.file('meta.xml', meta);
const manifest = '<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"><manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.base" manifest:full-path="/"/><manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/><manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/></manifest:manifest>';
zip.file('META-INF/manifest.xml', manifest);
return await zip.generateAsync({type: 'blob'});
}
}
// Example usage
// const handler = new ODBHandler();
// const file = ... // File object from input or drop
// await handler.read(file);
// handler.printProperties();
// const blob = await handler.write();
// // Download blob as 'new.odb'
7. C Code for .ODB File Handling
Since C does not have built-in classes, the following is a struct-based implementation with functions to open a .ODB file, decode and read the properties, print them to console, and write a minimal .ODB file. This assumes use of libzip for ZIP handling and libxml2 for XML parsing (compile with -lzip -lxml2).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libzip.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <time.h>
typedef struct {
char *filename;
char *properties[15][2]; // Key-value pairs
} ODBHandler;
ODBHandler* odb_create(const char *filename) {
ODBHandler *handler = malloc(sizeof(ODBHandler));
handler->filename = strdup(filename);
return handler;
}
void odb_read(ODBHandler *handler) {
// Stub implementation; in practice, use libzip to open ZIP and libxml2 to parse XML files
// Populate handler->properties with values as in Python/Java examples
// For brevity, printing placeholder
printf("Reading .ODB file: %s\n", handler->filename);
// Actual code would extract and parse as above
}
void odb_print_properties(ODBHandler *handler) {
// Print the properties
printf("MIME Type: application/vnd.oasis.opendocument.base\n"); // Placeholder
// Add for all 15
}
void odb_write(ODBHandler *handler, const char *output_filename) {
// Use libzip to create ZIP and add files as in examples
printf("Writing minimal .ODB to %s\n", output_filename);
// Actual code would create ZIP and add strings as above
}
void odb_destroy(ODBHandler *handler) {
free(handler->filename);
free(handler);
}
// Example usage
// int main() {
// ODBHandler *handler = odb_create("example.odb");
// odb_read(handler);
// odb_print_properties(handler);
// odb_write(handler, "new.odb");
// odb_destroy(handler);
// return 0;
// }