Skip to main content

keynote-parser

PyTest & Ruff

keynote-parser is a Python module for unpacking and re-packing Apple Keynote .key files. It supports Keynote files generated by Keynote version 14.4 (current as of April 2025).

Keynote uses a proprietary, compressed binary format to store its presentations. This format is comprised of a zip file containing images and videos, as well as Snappy-compressed Protobuf .iwa files containing metadata, text, and all other definitions used in the presentation.

keynote-parser unpacks these component files into .yaml files in a directory, making them editable by text editors and/or scripts, then allows re-packing of these files into a working Keynote archive.

What could you use this for? Well, I use it to allow versioning of Keynote files in Git, which makes diffs more understandable (rather than binary), as well as modifying text in Keynote files in response to external scripts. (e.g.: figures that update from databases before giving a presentation)

Installation

uv pip install keynote-parser

Usage

# Unpack MyPresentation.key into ./MyPresentation/
keynote-parser unpack MyPresentation.key

# Re-pack ./MyPresentation/ into MyPresentation.out.key
keynote-parser pack ./MyPresentation/

# List the files within a Keynote archive
keynote-parser ls MyPresentation.key

# Dump a particular .iwa file into its yaml representation on stdout
keynote-parser cat MyPresentation.key /Index/Slide-00001.iwa

# Replace text within a Keynote file in-place
keynote-parser replace MyPresentation.key --find "hello world" --replace "hello dolly"

Formats

keynote-parser supports reading a list of replacements from a JSON file passed in as --replacements. This file must have the form:

{
  "replacements": [
    {
      "find": "regexp to search for",
      "replace": "string to replace with"
    },
    ...
  ]
}

This argument can be passed to keynote-parser replace to replace text in a Keynote file in-place. It can also be passed to keynote-parser pack to pack a directory into a Keynote file, replacing text along the way.

Replacing Images

The replacements json format can also be used to replace images in a Keynote file. To do so:

  • Use the keynote-parser ls command to determine the name of the image to replace.
  • Set the find pattern to the image's name, with the -\d\d\d suffix removed.
  • Set the replace field to the local path to the replacement image.

keynote-parser will automatically rescale the replacement image to fit all of the sizes of the target image.

Compatibility

Note that between Keynote 10.2 and Keynote 11.2, a number of Protobuf definitions used by Keynote have changed names. keynote-parser does not yet support backwards compatibility: it can only read .key files as the currently-supported Keynote version would, and will write .yaml output with keys that match the current names of the keys. This means that .yaml files generated with older versions of keynote-parser may not be readable by with v1.11.2.1 or higher of keynote-parser.

Until this issue is fixed (if it's ever fixed) - to properly read .yaml files created by older versions of keynote-parser:

  • use an older version of keynote-parser to read the file
  • write a .key (or .iwa) file with that older version
  • upgrade keynote-parser
  • read that resulting .iwa file in the newer version of keynote-parser

Updates

As keynote-parser includes Protobuf definitions extracted from a copy of Keynote, new versions of Keynote will inevitably create .key files that cannot be read by keynote-parser.

Rebuilding the generated code

The generated Protobuf code is not checked in, so a fresh clone needs to build it once before the tests will run:

python dumper/run.py

This compiles the .proto files already in protos/versions/ using a pinned copy of protoc, downloaded into .protoc/ on first use. The pin matters: the generated code is only guaranteed to load under a google.protobuf at least as new as the protoc that produced it, so building with an arbitrary protoc from $PATH (Homebrew ships a very recent one) produces code that fails to import with:

google.protobuf.runtime_version.VersionError: Detected incompatible Protobuf
Gencode/Runtime versions ... Runtime version cannot be older than the linked
gencode version.

To use a specific protoc anyway, pass --protoc /path/to/protoc.

Adding support for a new version of Keynote

As keynote-parser includes Protobuf definitions extracted from a copy of Keynote, new versions of Keynote will inevitably create .key files that cannot be read by keynote-parser. As new versions of Keynote are released, updates to keynote-parser can be made automatically by running the following on a macOS machine with the new Keynote installed:

python dumper/run.py --app-path /Applications/Keynote.app

This extracts the .proto files and message registry from the app bundle into protos/versions/<version>/, then compiles them as above. It requires a codesigning identity in Keychain Access (the app bundle has to be re-signed before its mapping can be read).

Note that the extraction step depends on Protobuf internals that were removed in protobuf 4, so it must be run in an environment with protobuf<4 installed — see the dependency header at the top of dumper/run.py, or run it with uv:

uv run --script dumper/run.py --app-path /Applications/Keynote.app

Recompiling the checked-in .proto files (the command in the previous section) has no such constraint and works with any supported protobuf.

Troubleshooting

Unable to complete installation due to snappy-c.h not found.

snappy/snappymodule.cc:31:10: fatal error: 'snappy-c.h' file not found

This means you're missing the Snappy libraries. Install Snappy via whatever method your OS supports. e.g. brew install snappy, sudo apt-get install libsnappy-dev, etc.

Credits

keynote-parser was built by Peter Sobot but heavily based on prior work by Sean Patrick O'Brien. A copy of O'Brien's format documentation is included in the docs folder for posterity.

License

All code in this repository is licensed under the MIT License.

Copyright 2019-2025 Peter Sobot

Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

keynote_parser-1.14.4.1.tar.gz (201.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

keynote_parser-1.14.4.1-py3-none-any.whl (224.5 kB view details)

Uploaded Python 3

File details

Details for the file keynote_parser-1.14.4.1.tar.gz.

File metadata

  • Download URL: keynote_parser-1.14.4.1.tar.gz
  • Upload date:
  • Size: 201.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for keynote_parser-1.14.4.1.tar.gz
Algorithm Hash digest
SHA256 9245bbdb239339404abb62c2ab6273ee1d43328f2d23a36f28cb206b07abcae1
MD5 dcf2e9fb481463de9a386668b6c23766
BLAKE2b-256 2ce0e78da5ffd9bf787c38d227f5e44e902d15ad9a5e9fda2d434df62b733770

See more details on using hashes here.

File details

Details for the file keynote_parser-1.14.4.1-py3-none-any.whl.

File metadata

File hashes

Hashes for keynote_parser-1.14.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0b873875ac72db8af3782c69d5074f1f1d00d04b770f40655abe3669e7abf18a
MD5 73b9f92b548abc2c6ac3c0184390d9d8
BLAKE2b-256 9c62edb658b2033e2ce12adfdb240fbb0562b3d5d237dfa133fbb56ee035fcc8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page