Task 567: .PPSX File Format

Task 567: .PPSX File Format

File Format Specifications for .PPSX

The .PPSX file format is part of the Office Open XML (OOXML) standard, defined in ISO/IEC 29500 (specifically Parts 1 and 4 for PresentationML and DrawingML). It is an XML-based format compressed in a ZIP container using Open Packaging Conventions (OPC). .PPSX is identical in structure to .PPTX but is configured as a slideshow (it opens directly in Slide Show view in compatible software like Microsoft PowerPoint). The key difference is in the content type declaration in [Content_Types].xml, which uses application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml for .PPSX. The format supports slides, themes, media, animations, transitions, and metadata. It is backward-compatible with .PPS files and is the default slideshow format since PowerPoint 2007.

  1. List of All Properties Intrinsic to the .PPSX File Format

These are the metadata properties stored within the file's structure (in docProps/core.xml for core properties and docProps/app.xml for extended properties). They are intrinsic to the OOXML format and can be read/written without external dependencies. Core properties are based on Dublin Core standards; extended properties are application-specific (for PowerPoint in this case).

Core Properties (from docProps/core.xml):

  • category: The categorization of the document content.
  • contentStatus: The status of the content (e.g., "Draft", "Final").
  • contentType: The type of content (not to be confused with MIME type).
  • created: The date and time the document was created.
  • creator: The name of the document's author.
  • description: A textual description or abstract of the document.
  • identifier: A unique identifier for the document.
  • keywords: Keywords associated with the document.
  • language: The primary language of the document.
  • lastModifiedBy: The name of the last user to modify the document.
  • lastPrinted: The date and time the document was last printed.
  • modified: The date and time the document was last modified.
  • revision: The revision number of the document.
  • subject: The subject or topic of the document.
  • title: The title of the document.
  • version: The version number of the document.

Extended Properties (from docProps/app.xml, specific to PowerPoint):

  • Template: The name of the template used.
  • Manager: The name of the manager associated with the document.
  • Company: The company or organization name.
  • Pages: The number of pages (for print views).
  • Words: The word count.
  • Characters: The character count (excluding spaces).
  • PresentationFormat: The intended presentation format (e.g., "On-screen", "35mm").
  • Lines: The line count.
  • Paragraphs: The paragraph count.
  • Slides: The number of slides.
  • Notes: The number of notes pages.
  • TotalTime: The total editing time in minutes.
  • HiddenSlides: The number of hidden slides.
  • MultimediaClips: The number of multimedia clips (e.g., audio/video).
  • ScaleCrop: A boolean indicating if scaling/cropping is applied.
  • HeadingPairs: A vector of heading pairs (for outline structure).
  • TitlesOfParts: Titles of document parts (e.g., slide titles).
  • LinksUpToDate: A boolean indicating if links are up to date.
  • CharactersWithSpaces: The character count including spaces.
  • SharedDocument: A boolean indicating if the document is shared.
  • HyperlinkBase: The base URL for hyperlinks.
  • HyperlinkList: A list of hyperlinks.
  • HyperlinksChanged: A boolean indicating if hyperlinks have changed.
  • DigitalSignature: Information about digital signatures.
  • Application: The name of the application that created the document (e.g., "Microsoft Office PowerPoint").
  • ApplicationVersion: The version of the application.
  • DocumentSecurity: The security level (0-4, where 0 is none).

These properties are stored in XML files within the ZIP archive and can be decoded/encoded programmatically.

  1. Two Direct Download Links for .PPSX Files
  1. Ghost Blog Embedded HTML JavaScript for Drag-and-Drop .PPSX Property Dump

This is an embeddable HTML snippet with JavaScript (using JSZip for ZIP handling) that can be placed in a Ghost blog post. It creates a drag-and-drop area; when a .PPSX file is dropped, it extracts and displays all the above properties on the screen. Include JSZip via CDN for simplicity.

Drag and drop a .PPSX file here
  1. Python Class for .PPSX Property Handling

This class uses built-in zipfile and xml.etree.ElementTree to open, read, decode, print, and write properties. To write, it updates the XML and re-zips.

import zipfile
import xml.etree.ElementTree as ET
from io import BytesIO

class PPSXHandler:
    def __init__(self, filepath):
        self.filepath = filepath
        self.core_props = {}
        self.ext_props = {}
        self._load_properties()

    def _load_properties(self):
        with zipfile.ZipFile(self.filepath, 'r') as zf:
            if 'docProps/core.xml' in zf.namelist():
                core_xml = zf.read('docProps/core.xml').decode('utf-8')
                core_root = ET.fromstring(core_xml)
                cp_ns = {'cp': 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties',
                         'dc': 'http://purl.org/dc/elements/1.1/'}
                for prop in ['category', 'contentStatus', 'contentType', 'created', 'creator', 'description', 'identifier', 'keywords', 'language', 'lastModifiedBy', 'lastPrinted', 'modified', 'revision', 'subject', 'title', 'version']:
                    elem = core_root.find(f'.//cp:{prop}', cp_ns) or core_root.find(f'.//dc:{prop}', cp_ns)
                    self.core_props[prop] = elem.text if elem is not None else 'N/A'
            if 'docProps/app.xml' in zf.namelist():
                app_xml = zf.read('docProps/app.xml').decode('utf-8')
                app_root = ET.fromstring(app_xml)
                ep_ns = {'ep': 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties'}
                for prop in ['Template', 'Manager', 'Company', 'Pages', 'Words', 'Characters', 'PresentationFormat', 'Lines', 'Paragraphs', 'Slides', 'Notes', 'TotalTime', 'HiddenSlides', 'MultimediaClips', 'ScaleCrop', 'HeadingPairs', 'TitlesOfParts', 'LinksUpToDate', 'CharactersWithSpaces', 'SharedDocument', 'HyperlinkBase', 'HyperlinkList', 'HyperlinksChanged', 'DigitalSignature', 'Application', 'ApplicationVersion', 'DocumentSecurity']:
                    elem = app_root.find(f'.//ep:{prop}', ep_ns)
                    self.ext_props[prop] = elem.text if elem is not None else 'N/A'

    def print_properties(self):
        print("Core Properties:")
        for key, value in self.core_props.items():
            print(f"{key}: {value}")
        print("\nExtended Properties:")
        for key, value in self.ext_props.items():
            print(f"{key}: {value}")

    def write_property(self, prop_name, new_value, is_core=True):
        with zipfile.ZipFile(self.filepath, 'r') as zf_in:
            mem_file = BytesIO()
            with zipfile.ZipFile(mem_file, 'w', zipfile.ZIP_DEFLATED) as zf_out:
                for item in zf_in.infolist():
                    data = zf_in.read(item.filename)
                    if (is_core and item.filename == 'docProps/core.xml') or (not is_core and item.filename == 'docProps/app.xml'):
                        root = ET.fromstring(data)
                        ns = {'cp': 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties',
                              'dc': 'http://purl.org/dc/elements/1.1/',
                              'ep': 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties'}
                        elem = root.find(f'.//*{{{list(ns.values())[0] if is_core else list(ns.values())[2]}}}{prop_name}', ns)
                        if elem is not None:
                            elem.text = new_value
                        data = ET.tostring(root, encoding='utf-8', xml_declaration=True)
                    zf_out.writestr(item, data)
            mem_file.seek(0)
            with open(self.filepath, 'wb') as f:
                f.write(mem_file.read())
        self._load_properties()  # Reload after write

# Example usage:
# handler = PPSXHandler('example.ppsx')
# handler.print_properties()
# handler.write_property('title', 'New Title')
  1. Java Class for .PPSX Property Handling

This class uses java.util.zip and javax.xml.parsers to handle ZIP and XML.

import java.io.*;
import java.util.zip.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.InputSource;

public class PPSXHandler {
    private String filepath;
    private java.util.Map<String, String> coreProps = new java.util.HashMap<>();
    private java.util.Map<String, String> extProps = new java.util.HashMap<>();

    public PPSXHandler(String filepath) {
        this.filepath = filepath;
        loadProperties();
    }

    private void loadProperties() {
        try (ZipFile zf = new ZipFile(filepath)) {
            ZipEntry coreEntry = zf.getEntry("docProps/core.xml");
            if (coreEntry != null) {
                Document doc = parseXml(zf.getInputStream(coreEntry));
                String[] props = {"category", "contentStatus", "contentType", "created", "creator", "description", "identifier", "keywords", "language", "lastModifiedBy", "lastPrinted", "modified", "revision", "subject", "title", "version"};
                for (String prop : props) {
                    Node node = doc.getElementsByTagNameNS("http://schemas.openxmlformats.org/package/2006/metadata/core-properties", prop).item(0);
                    if (node == null) node = doc.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", prop).item(0);
                    coreProps.put(prop, node != null ? node.getTextContent() : "N/A");
                }
            }
            ZipEntry appEntry = zf.getEntry("docProps/app.xml");
            if (appEntry != null) {
                Document doc = parseXml(zf.getInputStream(appEntry));
                String[] props = {"Template", "Manager", "Company", "Pages", "Words", "Characters", "PresentationFormat", "Lines", "Paragraphs", "Slides", "Notes", "TotalTime", "HiddenSlides", "MultimediaClips", "ScaleCrop", "HeadingPairs", "TitlesOfParts", "LinksUpToDate", "CharactersWithSpaces", "SharedDocument", "HyperlinkBase", "HyperlinkList", "HyperlinksChanged", "DigitalSignature", "Application", "ApplicationVersion", "DocumentSecurity"};
                for (String prop : props) {
                    Node node = doc.getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties", prop).item(0);
                    extProps.put(prop, node != null ? node.getTextContent() : "N/A");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Document parseXml(InputStream is) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        return builder.parse(new InputSource(is));
    }

    public void printProperties() {
        System.out.println("Core Properties:");
        coreProps.forEach((key, value) -> System.out.println(key + ": " + value));
        System.out.println("\nExtended Properties:");
        extProps.forEach((key, value) -> System.out.println(key + ": " + value));
    }

    public void writeProperty(String propName, String newValue, boolean isCore) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            try (ZipFile zfIn = new ZipFile(filepath); ZipOutputStream zos = new ZipOutputStream(baos)) {
                java.util.Enumeration<? extends ZipEntry> entries = zfIn.entries();
                while (entries.hasMoreElements()) {
                    ZipEntry entry = entries.nextElement();
                    InputStream is = zfIn.getInputStream(entry);
                    zos.putNextEntry(new ZipEntry(entry.getName()));
                    if ((isCore && entry.getName().equals("docProps/core.xml")) || (!isCore && entry.getName().equals("docProps/app.xml"))) {
                        Document doc = parseXml(is);
                        Node node = doc.getElementsByTagNameNS(isCore ? "http://schemas.openxmlformats.org/package/2006/metadata/core-properties" : "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties", propName).item(0);
                        if (node == null && isCore) node = doc.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", propName).item(0);
                        if (node != null) node.setTextContent(newValue);
                        TransformerFactory tf = TransformerFactory.newInstance();
                        Transformer t = tf.newTransformer();
                        t.transform(new DOMSource(doc), new StreamResult(new OutputStreamWriter(zos, "UTF-8")));
                    } else {
                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = is.read(buffer)) > 0) {
                            zos.write(buffer, 0, len);
                        }
                    }
                    zos.closeEntry();
                    is.close();
                }
            }
            try (FileOutputStream fos = new FileOutputStream(filepath)) {
                fos.write(baos.toByteArray());
            }
            loadProperties();  // Reload
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Example usage:
    // public static void main(String[] args) {
    //     PPSXHandler handler = new PPSXHandler("example.ppsx");
    //     handler.printProperties();
    //     handler.writeProperty("title", "New Title", true);
    // }
}
  1. JavaScript Class for .PPSX Property Handling

This class uses JSZip for browser/node. For node, require 'jszip' and 'fs'.

const JSZip = require('jszip');  // For Node.js; in browser, use CDN
const fs = require('fs');  // For Node.js

class PPSXHandler {
  constructor(filepath) {
    this.filepath = filepath;
    this.coreProps = {};
    this.extProps = {};
  }

  async loadProperties() {
    const data = fs.readFileSync(this.filepath);
    const zip = await JSZip.loadAsync(data);
    const coreXml = await zip.file('docProps/core.xml')?.async('string');
    if (coreXml) {
      const parser = new DOMParser();
      const doc = parser.parseFromString(coreXml, 'application/xml');
      const props = ['category', 'contentStatus', 'contentType', 'created', 'creator', 'description', 'identifier', 'keywords', 'language', 'lastModifiedBy', 'lastPrinted', 'modified', 'revision', 'subject', 'title', 'version'];
      props.forEach(prop => {
        let elem = doc.querySelector(`[xmlns="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"] > ${prop}`) || doc.querySelector(`[xmlns="http://purl.org/dc/elements/1.1/"] > ${prop}`);
        this.coreProps[prop] = elem ? elem.textContent : 'N/A';
      });
    }
    const appXml = await zip.file('docProps/app.xml')?.async('string');
    if (appXml) {
      const parser = new DOMParser();
      const doc = parser.parseFromString(appXml, 'application/xml');
      const props = ['Template', 'Manager', 'Company', 'Pages', 'Words', 'Characters', 'PresentationFormat', 'Lines', 'Paragraphs', 'Slides', 'Notes', 'TotalTime', 'HiddenSlides', 'MultimediaClips', 'ScaleCrop', 'HeadingPairs', 'TitlesOfParts', 'LinksUpToDate', 'CharactersWithSpaces', 'SharedDocument', 'HyperlinkBase', 'HyperlinkList', 'HyperlinksChanged', 'DigitalSignature', 'Application', 'ApplicationVersion', 'DocumentSecurity'];
      props.forEach(prop => {
        let elem = doc.querySelector(`[xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"] > ${prop}`);
        this.extProps[prop] = elem ? elem.textContent : 'N/A';
      });
    }
  }

  printProperties() {
    console.log('Core Properties:');
    Object.entries(this.coreProps).forEach(([key, value]) => console.log(`${key}: ${value}`));
    console.log('\nExtended Properties:');
    Object.entries(this.extProps).forEach(([key, value]) => console.log(`${key}: ${value}`));
  }

  async writeProperty(propName, newValue, isCore = true) {
    const data = fs.readFileSync(this.filepath);
    const zip = await JSZip.loadAsync(data);
    const xmlPath = isCore ? 'docProps/core.xml' : 'docProps/app.xml';
    let xml = await zip.file(xmlPath)?.async('string');
    if (xml) {
      const parser = new DOMParser();
      const doc = parser.parseFromString(xml, 'application/xml');
      let elem = doc.querySelector(`${propName}`);
      if (elem) elem.textContent = newValue;
      const serializer = new XMLSerializer();
      zip.file(xmlPath, serializer.serializeToString(doc));
    }
    const newData = await zip.generateAsync({type: 'nodebuffer'});
    fs.writeFileSync(this.filepath, newData);
    await this.loadProperties();  // Reload
  }
}

// Example usage (Node.js):
// const handler = new PPSXHandler('example.ppsx');
// await handler.loadProperties();
// handler.printProperties();
// await handler.writeProperty('title', 'New Title');
  1. C++ Class for .PPSX Property Handling

This uses libzip for ZIP handling and tinyxml2 for XML (assume included via headers). Compile with -lzip -ltinyxml2.

#include <iostream>
#include <map>
#include <string>
#include <zip.h>
#include <tinyxml2.h>

class PPSXHandler {
private:
    std::string filepath;
    std::map<std::string, std::string> coreProps;
    std::map<std::string, std::string> extProps;

    void loadProperties() {
        zip_t* za = zip_open(filepath.c_str(), ZIP_RDONLY, nullptr);
        if (!za) return;

        struct zip_stat st;
        zip_stat_init(&st);

        // Core
        if (zip_stat(za, "docProps/core.xml", 0, &st) == 0) {
            char* contents = new char[st.size + 1];
            zip_file_t* f = zip_fopen(za, "docProps/core.xml", 0);
            zip_fread(f, contents, st.size);
            contents[st.size] = '\0';
            zip_fclose(f);
            tinyxml2::XMLDocument doc;
            doc.Parse(contents);
            const char* props[] = {"category", "contentStatus", "contentType", "created", "creator", "description", "identifier", "keywords", "language", "lastModifiedBy", "lastPrinted", "modified", "revision", "subject", "title", "version"};
            for (auto prop : props) {
                tinyxml2::XMLElement* elem = doc.FirstChildElement("cp:coreProperties")->FirstChildElement(prop);
                if (!elem) elem = doc.FirstChildElement("dc:coreProperties")->FirstChildElement(prop);  // Fallback namespace
                coreProps[prop] = elem ? elem->GetText() : "N/A";
            }
            delete[] contents;
        }

        // Extended
        if (zip_stat(za, "docProps/app.xml", 0, &st) == 0) {
            char* contents = new char[st.size + 1];
            zip_file_t* f = zip_fopen(za, "docProps/app.xml", 0);
            zip_fread(f, contents, st.size);
            contents[st.size] = '\0';
            zip_fclose(f);
            tinyxml2::XMLDocument doc;
            doc.Parse(contents);
            const char* props[] = {"Template", "Manager", "Company", "Pages", "Words", "Characters", "PresentationFormat", "Lines", "Paragraphs", "Slides", "Notes", "TotalTime", "HiddenSlides", "MultimediaClips", "ScaleCrop", "HeadingPairs", "TitlesOfParts", "LinksUpToDate", "CharactersWithSpaces", "SharedDocument", "HyperlinkBase", "HyperlinkList", "HyperlinksChanged", "DigitalSignature", "Application", "ApplicationVersion", "DocumentSecurity"};
            for (auto prop : props) {
                tinyxml2::XMLElement* elem = doc.FirstChildElement("Properties")->FirstChildElement(prop);
                extProps[prop] = elem ? elem->GetText() : "N/A";
            }
            delete[] contents;
        }

        zip_close(za);
    }

public:
    PPSXHandler(const std::string& fp) : filepath(fp) {
        loadProperties();
    }

    void printProperties() {
        std::cout << "Core Properties:" << std::endl;
        for (const auto& pair : coreProps) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
        std::cout << "\nExtended Properties:" << std::endl;
        for (const auto& pair : extProps) {
            std::cout << pair.first << ": " << pair.second << std::endl;
        }
    }

    void writeProperty(const std::string& propName, const std::string& newValue, bool isCore = true) {
        zip_t* zaIn = zip_open(filepath.c_str(), ZIP_RDONLY, nullptr);
        if (!zaIn) return;

        std::string tempFile = filepath + ".tmp";
        zip_t* zaOut = zip_open(tempFile.c_str(), ZIP_CREATE | ZIP_TRUNCATE, nullptr);
        if (!zaOut) {
            zip_close(zaIn);
            return;
        }

        int numEntries = zip_get_num_entries(zaIn, 0);
        for (int i = 0; i < numEntries; ++i) {
            const char* name = zip_get_name(zaIn, i, 0);
            struct zip_stat st;
            zip_stat_index(zaIn, i, 0, &st);
            char* contents = new char[st.size + 1];
            zip_file_t* f = zip_fopen_index(zaIn, i, 0);
            zip_fread(f, contents, st.size);
            contents[st.size] = '\0';
            zip_fclose(f);

            bool modify = (isCore && std::string(name) == "docProps/core.xml") || (!isCore && std::string(name) == "docProps/app.xml");
            if (modify) {
                tinyxml2::XMLDocument doc;
                doc.Parse(contents);
                tinyxml2::XMLElement* elem = doc.FirstChildElement(isCore ? "cp:coreProperties" : "Properties")->FirstChildElement(propName.c_str());
                if (elem) elem->SetText(newValue.c_str());
                tinyxml2::XMLPrinter printer;
                doc.Print(&printer);
                delete[] contents;
                contents = const_cast<char*>(printer.CStr());
                st.size = printer.Size();
            }

            zip_source_t* src = zip_source_buffer(zaOut, contents, st.size, 0);
            zip_file_add(zaOut, name, src, ZIP_FL_OVERWRITE);
        }

        zip_close(zaOut);
        zip_close(zaIn);
        std::remove(filepath.c_str());
        std::rename(tempFile.c_str(), filepath.c_str());
        loadProperties();  // Reload
    }
};

// Example usage:
// int main() {
//     PPSXHandler handler("example.ppsx");
//     handler.printProperties();
//     handler.writeProperty("title", "New Title");
//     return 0;
// }