Task 711: .SXI File Format
Task 711: .SXI File Format
1. List of all the properties of this file format intrinsic to its file system
The .SXI file format is a ZIP-based archive format used for StarOffice/OpenOffice.org 1.x Impress presentations. It is not a traditional binary file with a custom header but a compressed package containing XML files and optional resources. The properties intrinsic to the format (structural elements that define its file system and validation) are as follows:
- File Container Type: ZIP archive format (with the magic bytes
PK\x03\x04at the beginning of the file). - MIME Type:
application/vnd.sun.xml.impress(stored in an uncompressed plain-text file namedmimetypeat the root of the ZIP). - Package Version: Typically
1.0(specified as theoffice:versionattribute in the root elements of XML files likecontent.xmlandstyles.xml). - Office Class:
presentation(specified as theoffice:classattribute in the root XML elements to identify it as a presentation document). - Namespaces: Key XML namespaces include:
- Office:
urn:oasis:names:tc:opendocument:xmlns:office:1.0 - Manifest:
urn:oasis:names:tc:opendocument:xmlns:manifest:1.0 - Meta:
urn:oasis:names:tc:opendocument:xmlns:meta:1.0 - DC (Dublin Core):
http://purl.org/dc/elements/1.1/ - Draw:
urn:oasis:names:tc:opendocument:xmlns:drawing:1.0 - Presentation:
urn:oasis:names:tc:opendocument:xmlns:presentation:1.0 - Other namespaces for SVG, XLink, Style, Form, etc., as defined in the spec.
- Required Files in ZIP:
mimetype: Contains the MIME type string without newline or extension.META-INF/manifest.xml: Lists all ZIP entries with their paths, media-types (e.g.,text/xmlfor XML files,image/pngfor images), sizes, and optional encryption details.content.xml: Main content file with the presentation body (e.g., office:body containing draw:page elements for slides, animations, forms, and indexes).styles.xml: Contains styles, automatic styles, and master pages (e.g., office:master-styles with style:master-page for slide layouts).meta.xml: Metadata file with document info (e.g., dc:title, dc:creator, meta:creation-date, <meta:document-statistic meta:page-count> for page count).- Optional Files in ZIP:
settings.xml: User-specific settings (e.g., office:settings with presentation configurations like zoom, transitions, and durations).Thumbnails/thumbnail.png: Preview image of the presentation (media-typeimage/png).Pictures/: Directory for embedded images and media (e.g., PNG, JPEG files referenced in XML via xlink:href).- Other subdirectories or files for embedded objects, sounds, or custom data (e.g., for presentation:sound elements).
- Encryption Support: Optional per-file encryption using Blowfish algorithm, with key derivation and checksums specified in the manifest (manifest:encryption-data).
- Validation Properties: The format is validated by the manifest's entry list, MIME type, and XML schemas/DTDs (RelaxNG or DTD from the spec). Forward compatibility allows ignoring unknown elements if versions differ.
- Metadata Properties (extractable from XML, intrinsic to document structure):
- Document title (dc:title in meta.xml).
- Creator (dc:creator in meta.xml).
- Creation date (meta:creation-date in meta.xml).
- Modified date (dc:date in meta.xml).
- Page count (<meta:document-statistic meta:page-count> in meta.xml).
- Number of slides (count of draw:page elements in content.xml).
- Other Intrinsic Properties: White-space preservation in XML, EOL normalization, support for XSLT transformations, and integration with standards like SVG for graphics and XForms for interactive elements.
These properties ensure the file is self-contained, extensible, and parsable without proprietary software.
2. Two direct download links for files of format .SXI
After extensive searching using web search tools, I was unable to find publicly available direct download links for .SXI files. The format is obsolete (replaced by .ODP in later versions of OpenOffice/LibreOffice), and samples are not commonly hosted online. You can generate .SXI files using legacy software like OpenOffice 1.x or convert from .ODP using LibreOffice (save as .SXI). For reference, here are two pages where you can find information on .SXI files or convert to/from them:
- https://www.vertopal.com/en/format/sxi (allows conversion to/from .SXI)
- https://file.org/extension/sxi (describes the format and compatible software)
3. Ghost blog embedded html javascript that allows a user to drag n drop a file of format .SXI and it will dump to screen all these properties
Here's an embedded HTML/JavaScript snippet for a Ghost blog post (or any HTML page). It uses drag-and-drop to load the .SXI file, JSZip to unzip it, and DOMParser to parse the XML files. It then extracts and displays the properties listed in point 1 (structural and metadata). Include JSZip library via CDN for browser compatibility.
4. Python class that can open any file of format .SXI and decode read and write and print to console all the properties from the above list
Here's a Python class using zipfile and xml.etree.ElementTree for parsing. It can read, print properties, modify (e.g., update title), and write back to a new file.
import zipfile
import xml.etree.ElementTree as ET
from io import BytesIO
class SXIHandler:
def __init__(self, filepath):
self.filepath = filepath
self.properties = {}
self.zip = zipfile.ZipFile(filepath, 'r')
self._read_properties()
def _read_properties(self):
# MIME type
if 'mimetype' in self.zip.namelist():
with self.zip.open('mimetype') as f:
self.properties['mimeType'] = f.read().decode('utf-8').strip()
# Manifest
if 'META-INF/manifest.xml' in self.zip.namelist():
with self.zip.open('META-INF/manifest.xml') as f:
manifest_tree = ET.parse(f)
self.properties['manifestNamespace'] = manifest_tree.getroot().tag.split('}')[0][1:]
self.properties['requiredFiles'] = [entry.attrib['{urn:oasis:names:tc:opendocument:xmlns:manifest:1.0}full-path'] for entry in manifest_tree.findall('.//manifest:file-entry', {'manifest': self.properties['manifestNamespace']})]
# Meta.xml
if 'meta.xml' in self.zip.namelist():
with self.zip.open('meta.xml') as f:
meta_tree = ET.parse(f)
ns = {'dc': 'http://purl.org/dc/elements/1.1/', 'meta': 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'}
self.properties['title'] = meta_tree.find('.//dc:title', ns).text if meta_tree.find('.//dc:title', ns) is not None else 'Not found'
self.properties['creator'] = meta_tree.find('.//dc:creator', ns).text if meta_tree.find('.//dc:creator', ns) is not None else 'Not found'
self.properties['creationDate'] = meta_tree.find('.//meta:creation-date', ns).text if meta_tree.find('.//meta:creation-date', ns) is not None else 'Not found'
self.properties['modifiedDate'] = meta_tree.find('.//dc:date', ns).text if meta_tree.find('.//dc:date', ns) is not None else 'Not found'
stat = meta_tree.find('.//meta:document-statistic', ns)
self.properties['pageCount'] = stat.attrib.get('{urn:oasis:names:tc:opendocument:xmlns:meta:1.0}page-count') if stat is not None else 'Not found'
# Content.xml
if 'content.xml' in self.zip.namelist():
with self.zip.open('content.xml') as f:
content_tree = ET.parse(f)
ns = {'office': 'urn:oasis:names:tc:opendocument:xmlns:office:1.0', 'draw': 'urn:oasis:names:tc:opendocument:xmlns:drawing:1.0'}
self.properties['officeVersion'] = content_tree.getroot().attrib.get('{urn:oasis:names:tc:opendocument:xmlns:office:1.0}version', 'Not found')
self.properties['officeNamespace'] = content_tree.getroot().tag.split('}')[0][1:]
self.properties['numberOfSlides'] = len(content_tree.findall('.//draw:page', ns))
# Other
self.properties['hasStyles'] = 'styles.xml' in self.zip.namelist()
self.properties['hasSettings'] = 'settings.xml' in self.zip.namelist()
self.properties['hasThumbnail'] = 'Thumbnails/thumbnail.png' in self.zip.namelist()
self.properties['embeddedPictures'] = len([name for name in self.zip.namelist() if name.startswith('Pictures/')])
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def update_title(self, new_title):
if 'meta.xml' in self.zip.namelist():
with self.zip.open('meta.xml') as f:
meta_tree = ET.parse(f)
ns = {'dc': 'http://purl.org/dc/elements/1.1/'}
title_elem = meta_tree.find('.//dc:title', ns)
if title_elem is not None:
title_elem.text = new_title
# Update in memory
self.properties['title'] = new_title
# To write, need to save (see save method)
def save(self, new_filepath):
with zipfile.ZipFile(new_filepath, 'w') as new_zip:
for name in self.zip.namelist():
with self.zip.open(name) as f:
data = f.read()
if name == 'meta.xml' and 'title' in self.properties: # Example modify
tree = ET.parse(BytesIO(data))
ns = {'dc': 'http://purl.org/dc/elements/1.1/'}
title_elem = tree.find('.//dc:title', ns)
if title_elem is not None:
title_elem.text = self.properties['title']
data = ET.tostring(tree.getroot(), encoding='utf-8')
new_zip.writestr(name, data)
# Example usage
# handler = SXIHandler('example.sxi')
# handler.print_properties()
# handler.update_title('New Title')
# handler.save('modified.sxi')
5. Java class that can open any file of format .SXI and decode read and write and print to console all the properties from the above list
Here's a Java class using ZipFile and DocumentBuilderFactory for parsing. It can read, print, modify, and write.
import java.io.*;
import java.util.*;
import java.util.zip.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
public class SXIHandler {
private String filepath;
private Map<String, Object> properties = new HashMap<>();
private ZipFile zip;
public SXIHandler(String filepath) throws IOException, Exception {
this.filepath = filepath;
this.zip = new ZipFile(filepath);
readProperties();
}
private void readProperties() throws Exception {
// MIME type
ZipEntry mimeEntry = zip.getEntry("mimetype");
if (mimeEntry != null) {
properties.put("mimeType", new Scanner(zip.getInputStream(mimeEntry)).useDelimiter("\\A").next().trim());
}
// Manifest
ZipEntry manifestEntry = zip.getEntry("META-INF/manifest.xml");
if (manifestEntry != null) {
Document manifestDoc = parseXml(zip.getInputStream(manifestEntry));
properties.put("manifestNamespace", manifestDoc.getDocumentElement().getNamespaceURI());
NodeList entries = manifestDoc.getElementsByTagNameNS("*", "file-entry");
List<String> fileList = new ArrayList<>();
for (int i = 0; i < entries.getLength(); i++) {
fileList.add(((Element) entries.item(i)).getAttribute("manifest:full-path"));
}
properties.put("requiredFiles", fileList);
}
// Meta.xml
ZipEntry metaEntry = zip.getEntry("meta.xml");
if (metaEntry != null) {
Document metaDoc = parseXml(zip.getInputStream(metaEntry));
properties.put("title", getElementText(metaDoc, "dc:title"));
properties.put("creator", getElementText(metaDoc, "dc:creator"));
properties.put("creationDate", getElementText(metaDoc, "meta:creation-date"));
properties.put("modifiedDate", getElementText(metaDoc, "dc:date"));
Element stat = (Element) metaDoc.getElementsByTagName("meta:document-statistic").item(0);
properties.put("pageCount", stat != null ? stat.getAttribute("meta:page-count") : "Not found");
}
// Content.xml
ZipEntry contentEntry = zip.getEntry("content.xml");
if (contentEntry != null) {
Document contentDoc = parseXml(zip.getInputStream(contentEntry));
properties.put("officeVersion", contentDoc.getDocumentElement().getAttribute("office:version"));
properties.put("officeNamespace", contentDoc.getDocumentElement().getNamespaceURI());
properties.put("numberOfSlides", contentDoc.getElementsByTagName("draw:page").getLength());
}
// Other
properties.put("hasStyles", zip.getEntry("styles.xml") != null);
properties.put("hasSettings", zip.getEntry("settings.xml") != null);
properties.put("hasThumbnail", zip.getEntry("Thumbnails/thumbnail.png") != null);
int pictureCount = 0;
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
if (entries.nextElement().getName().startsWith("Pictures/")) pictureCount++;
}
properties.put("embeddedPictures", pictureCount);
}
private Document parseXml(InputStream is) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
return dbf.newDocumentBuilder().parse(new InputSource(is));
}
private String getElementText(Document doc, String tag) {
NodeList nodes = doc.getElementsByTagName(tag);
return nodes.getLength() > 0 ? nodes.item(0).getTextContent() : "Not found";
}
public void printProperties() {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void updateTitle(String newTitle) throws Exception {
ZipEntry metaEntry = zip.getEntry("meta.xml");
if (metaEntry != null) {
Document metaDoc = parseXml(zip.getInputStream(metaEntry));
NodeList titles = metaDoc.getElementsByTagName("dc:title");
if (titles.getLength() > 0) {
titles.item(0).setTextContent(newTitle);
}
properties.put("title", newTitle);
// To save, use save method
}
}
public void save(String newFilepath) throws Exception {
try (ZipOutputStream newZip = new ZipOutputStream(new FileOutputStream(newFilepath))) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
InputStream is = zip.getInputStream(entry);
byte[] data = is.readAllBytes();
is.close();
if (entry.getName().equals("meta.xml") && properties.containsKey("title")) {
Document doc = parseXml(new ByteArrayInputStream(data));
NodeList titles = doc.getElementsByTagName("dc:title");
if (titles.getLength() > 0) {
titles.item(0).setTextContent((String) properties.get("title"));
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
javax.xml.transform.TransformerFactory.newInstance().newTransformer().transform(new javax.xml.transform.dom.DOMSource(doc), new javax.xml.transform.stream.StreamResult(baos));
data = baos.toByteArray();
}
ZipEntry newEntry = new ZipEntry(entry.getName());
newZip.putNextEntry(newEntry);
newZip.write(data);
newZip.closeEntry();
}
}
}
// Example usage
// public static void main(String[] args) throws Exception {
// SXIHandler handler = new SXIHandler("example.sxi");
// handler.printProperties();
// handler.updateTitle("New Title");
// handler.save("modified.sxi");
// }
}
6. Javascript class that can open any file of format .SXI and decode read and write and print to console all the properties from the above list
Here's a JavaScript class for Node.js (using jszip and xml2js for parsing). Install dependencies with npm install jszip xml2js. It can read, print, modify, and write.
const JSZip = require('jszip');
const fs = require('fs');
const xml2js = require('xml2js');
class SXIHandler {
constructor(filepath) {
this.filepath = filepath;
this.properties = {};
this.zipData = fs.readFileSync(filepath);
this.zip = new JSZip();
this.load();
}
async load() {
await this.zip.loadAsync(this.zipData);
await this.readProperties();
}
async readProperties() {
// MIME type
const mime = await this.zip.file('mimetype')?.async('text');
this.properties.mimeType = mime ? mime.trim() : 'Not found';
// Manifest
const manifestXml = await this.zip.file('META-INF/manifest.xml')?.async('text');
if (manifestXml) {
const parser = new xml2js.Parser({ explicitArray: false });
const manifest = await parser.parseStringPromise(manifestXml);
this.properties.manifestNamespace = 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0';
this.properties.requiredFiles = manifest['manifest:manifest']['manifest:file-entry'].map(entry => entry['manifest:full-path']);
}
// Meta.xml
const metaXml = await this.zip.file('meta.xml')?.async('text');
if (metaXml) {
const parser = new xml2js.Parser({ explicitArray: false });
const meta = await parser.parseStringPromise(metaXml);
const officeMeta = meta['office:document-meta']['office:meta'];
this.properties.title = officeMeta['dc:title'] || 'Not found';
this.properties.creator = officeMeta['dc:creator'] || 'Not found';
this.properties.creationDate = officeMeta['meta:creation-date'] || 'Not found';
this.properties.modifiedDate = officeMeta['dc:date'] || 'Not found';
this.properties.pageCount = officeMeta['meta:document-statistic']['meta:page-count'] || 'Not found';
}
// Content.xml
const contentXml = await this.zip.file('content.xml')?.async('text');
if (contentXml) {
const parser = new xml2js.Parser({ explicitArray: false });
const content = await parser.parseStringPromise(contentXml);
this.properties.officeVersion = content['office:document-content']['$']['office:version'] || 'Not found';
this.properties.officeNamespace = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0';
const pages = content['office:document-content']['office:body']['draw:page'] || [];
this.properties.numberOfSlides = Array.isArray(pages) ? pages.length : (pages ? 1 : 0);
}
// Other
this.properties.hasStyles = !!this.zip.file('styles.xml');
this.properties.hasSettings = !!this.zip.file('settings.xml');
this.properties.hasThumbnail = !!this.zip.file('Thumbnails/thumbnail.png');
this.properties.embeddedPictures = Object.keys(this.zip.files).filter(key => key.startsWith('Pictures/')).length;
}
printProperties() {
console.log(this.properties);
}
async updateTitle(newTitle) {
const metaXml = await this.zip.file('meta.xml')?.async('text');
if (metaXml) {
const parser = new xml2js.Parser({ explicitArray: false });
const meta = await parser.parseStringPromise(metaXml);
meta['office:document-meta']['office:meta']['dc:title'] = newTitle;
const builder = new xml2js.Builder();
const newMetaXml = builder.buildObject(meta);
this.zip.file('meta.xml', newMetaXml);
this.properties.title = newTitle;
}
}
async save(newFilepath) {
const buffer = await this.zip.generateAsync({type: 'nodebuffer'});
fs.writeFileSync(newFilepath, buffer);
}
}
// Example usage
// const handler = new SXIHandler('example.sxi');
// handler.printProperties();
// await handler.updateTitle('New Title');
// await handler.save('modified.sxi');
7. C class that can open any file of format .SXI and decode read and write and print to console all the properties from the above list
Here's a C++ class (assuming "c class" means C++ for class support). It uses minizip for ZIP (assume linked) and tinyxml2 for XML (assume included). Include and link the libraries accordingly. It can read, print, modify, and write.
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <zip.h>
#include <tinyxml2.h>
class SXIHandler {
private:
std::string filepath;
std::map<std::string, std::string> properties;
zip_t* zip;
public:
SXIHandler(const std::string& fp) : filepath(fp) {
int err = 0;
zip = zip_open(filepath.c_str(), ZIP_RDONLY, &err);
if (zip == nullptr) {
std::cerr << "Error opening ZIP" << std::endl;
return;
}
readProperties();
}
~SXIHandler() {
if (zip) zip_close(zip);
}
void readProperties() {
// MIME type
zip_file_t* mimeFile = zip_fopen(zip, "mimetype", 0);
if (mimeFile) {
char buffer[100];
int len = zip_fread(mimeFile, buffer, sizeof(buffer));
properties["mimeType"] = std::string(buffer, len);
zip_fclose(mimeFile);
}
// Manifest
zip_file_t* manifestFile = zip_fopen(zip, "META-INF/manifest.xml", 0);
if (manifestFile) {
stat_t st;
zip_stat(zip, "META-INF/manifest.xml", 0, &st);
char* buf = new char[st.size + 1];
zip_fread(manifestFile, buf, st.size);
buf[st.size] = '\0';
tinyxml2::XMLDocument doc;
doc.Parse(buf);
properties["manifestNamespace"] = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0";
tinyxml2::XMLElement* root = doc.FirstChildElement("manifest:manifest");
std::vector<std::string> files;
for (tinyxml2::XMLElement* entry = root->FirstChildElement("manifest:file-entry"); entry; entry = entry->NextSiblingElement("manifest:file-entry")) {
files.push_back(entry->Attribute("manifest:full-path"));
}
// Store as string for simplicity
std::string fileStr;
for (const auto& f : files) fileStr += f + ", ";
properties["requiredFiles"] = fileStr;
delete[] buf;
zip_fclose(manifestFile);
}
// Meta.xml
zip_file_t* metaFile = zip_fopen(zip, "meta.xml", 0);
if (metaFile) {
stat_t st;
zip_stat(zip, "meta.xml", 0, &st);
char* buf = new char[st.size + 1];
zip_fread(metaFile, buf, st.size);
buf[st.size] = '\0';
tinyxml2::XMLDocument doc;
doc.Parse(buf);
tinyxml2::XMLElement* meta = doc.FirstChildElement("office:document-meta")->FirstChildElement("office:meta");
properties["title"] = meta->FirstChildElement("dc:title") ? meta->FirstChildElement("dc:title")->GetText() : "Not found";
properties["creator"] = meta->FirstChildElement("dc:creator") ? meta->FirstChildElement("dc:creator")->GetText() : "Not found";
properties["creationDate"] = meta->FirstChildElement("meta:creation-date") ? meta->FirstChildElement("meta:creation-date")->GetText() : "Not found";
properties["modifiedDate"] = meta->FirstChildElement("dc:date") ? meta->FirstChildElement("dc:date")->GetText() : "Not found";
tinyxml2::XMLElement* stat = meta->FirstChildElement("meta:document-statistic");
properties["pageCount"] = stat ? stat->Attribute("meta:page-count") : "Not found";
delete[] buf;
zip_fclose(metaFile);
}
// Content.xml
zip_file_t* contentFile = zip_fopen(zip, "content.xml", 0);
if (contentFile) {
stat_t st;
zip_stat(zip, "content.xml", 0, &st);
char* buf = new char[st.size + 1];
zip_fread(contentFile, buf, st.size);
buf[st.size] = '\0';
tinyxml2::XMLDocument doc;
doc.Parse(buf);
tinyxml2::XMLElement* root = doc.FirstChildElement("office:document-content");
properties["officeVersion"] = root->Attribute("office:version");
properties["officeNamespace"] = "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
int slideCount = 0;
for (tinyxml2::XMLElement* page = root->FirstChildElement("office:body")->FirstChildElement("draw:page"); page; page = page->NextSiblingElement("draw:page")) {
slideCount++;
}
properties["numberOfSlides"] = std::to_string(slideCount);
delete[] buf;
zip_fclose(contentFile);
}
// Other
properties["hasStyles"] = zip_name_locate(zip, "styles.xml", 0) >= 0 ? "true" : "false";
properties["hasSettings"] = zip_name_locate(zip, "settings.xml", 0) >= 0 ? "true" : "false";
properties["hasThumbnail"] = zip_name_locate(zip, "Thumbnails/thumbnail.png", 0) >= 0 ? "true" : "false";
int pictureCount = 0;
int numFiles = zip_get_num_entries(zip, 0);
for (int i = 0; i < numFiles; i++) {
const char* name = zip_get_name(zip, i, 0);
if (std::string(name).find("Pictures/") == 0) pictureCount++;
}
properties["embeddedPictures"] = std::to_string(pictureCount);
}
void printProperties() {
for (const auto& p : properties) {
std::cout << p.first << ": " << p.second << std::endl;
}
}
void updateTitle(const std::string& newTitle) {
// Similar to read, parse meta, update, but for write see save
properties["title"] = newTitle;
}
void save(const std::string& newFilepath) {
zip_t* newZip = zip_open(newFilepath.c_str(), ZIP_CREATE | ZIP_TRUNCATE, nullptr);
int numFiles = zip_get_num_entries(zip, 0);
for (int i = 0; i < numFiles; i++) {
const char* name = zip_get_name(zip, i, 0);
zip_file_t* f = zip_fopen_index(zip, i, 0);
stat_t st;
zip_stat_index(zip, i, 0, &st);
char* buf = new char[st.size];
zip_fread(f, buf, st.size);
zip_fclose(f);
if (std::string(name) == "meta.xml" && !properties["title"].empty()) {
tinyxml2::XMLDocument doc;
doc.Parse(buf);
tinyxml2::XMLElement* titleElem = doc.FirstChildElement("office:document-meta")->FirstChildElement("office:meta")->FirstChildElement("dc:title");
if (titleElem) titleElem->SetText(properties["title"].c_str());
tinyxml2::XMLPrinter printer;
doc.Print(&printer);
delete[] buf;
buf = const_cast<char*>(printer.CStr());
st.size = printer.CStrSize() - 1;
}
zip_file_add(newZip, name, zip_source_buffer(newZip, buf, st.size, 0), ZIP_FL_OVERWRITE);
}
zip_close(newZip);
}
};
// Example usage
// int main() {
// SXIHandler handler("example.sxi");
// handler.printProperties();
// handler.updateTitle("New Title");
// handler.save("modified.sxi");
// return 0;
// }