ProPresenter 7 .pro File Format Specification
Version: 1.1
Target Audience: AI agents, automated parsers, developers
Proto Source: greyshirtguy/ProPresenter7-Proto v7.16.2 (MIT License)
1. Overview
File Format
- Extension:
.pro
- Binary Format: Protocol Buffers (Google protobuf v3)
- Top-level Message:
rv.data.Presentation (defined in presentation.proto)
- Proto Definitions: greyshirtguy/ProPresenter7-Proto v7.16.2 (MIT)
Known Limitations
- Binary Fidelity: Round-trip decode→encode fails on all reference files. Proto definitions are incomplete; unknown fields are lost during serialization.
- Workaround: Preserve original binary data if exact binary reproduction is required.
File Validity
- Empty files (0 bytes): Invalid. Throw exception.
- Songs without arrangements: Valid. 17 out of 169 reference files have no arrangements.
- Non-song presentations: Files like ANKUENDIGUNGEN, MODERATION, THEMA have groups/slides but may lack text elements.
2. Song Structure
Hierarchy Diagram
Presentation (rv.data.Presentation)
├── name (string, field 1)
├── uuid (rv.data.UUID, field 5)
├── cue_groups[] (rv.data.Presentation.CueGroup, field 12) ← Groups
│ ├── group (rv.data.Group, field 1)
│ │ ├── name (string, field 2)
│ │ ├── uuid (rv.data.UUID, field 1)
│ │ └── color (rv.data.Color, field 3) [optional]
│ └── cue_identifiers[] (rv.data.UUID, field 2) ← Slide UUID references
├── cues[] (rv.data.Cue, field 13) ← Slides
│ ├── uuid (rv.data.UUID, field 1)
│ └── actions[0] (rv.data.Action, field 10)
│ └── slide (rv.data.Action.SlideType, field 23)
│ └── presentation (rv.data.PresentationSlide, field 2)
│ └── base_slide (rv.data.Slide, field 1)
│ └── elements[] (rv.data.Slide.Element, field 1)
│ └── element (rv.data.Graphics.Element, field 1)
│ ├── name (string, field 2) ← Label like "Orginal", "Deutsch"
│ └── text (rv.data.Graphics.Text, field 13)
│ └── rtf_data (bytes, field 3) ← RTF-encoded text
└── arrangements[] (rv.data.Presentation.Arrangement, field 11)
├── name (string, field 2)
├── uuid (rv.data.UUID, field 1)
└── group_identifiers[] (rv.data.UUID, field 3) ← Group UUID references
Navigation Paths
To access slide text:
Presentation
→ cues[i]
→ actions[0]
→ slide
→ presentation
→ base_slide
→ elements[j]
→ element
→ text.rtf_data
To access group metadata:
Presentation
→ cue_groups[i]
→ group
→ name, uuid, color
To access arrangement order:
Presentation
→ arrangements[i]
→ group_identifiers[]
3. Fields Reference
Presentation (rv.data.Presentation)
| Field Path |
Protobuf Type |
Field Number |
Description |
application_info |
rv.data.ApplicationInfo |
1 |
Platform and application version info |
uuid |
rv.data.UUID |
2 |
Unique identifier for the presentation |
name |
string |
3 |
Song title (e.g., "Amazing Grace") |
last_date_used |
rv.data.Timestamp |
4 |
Last date the song was used |
last_modified_date |
rv.data.Timestamp |
5 |
Last modification date |
category |
string |
6 |
Optional category label |
notes |
string |
7 |
Optional notes |
background |
rv.data.Background |
8 |
Background color/image |
selected_arrangement |
rv.data.UUID |
10 |
UUID of the currently selected arrangement |
arrangements[] |
rv.data.Presentation.Arrangement |
11 |
Array of arrangements |
cue_groups[] |
rv.data.Presentation.CueGroup |
12 |
Array of groups (song parts) |
cues[] |
rv.data.Cue |
13 |
Array of slides |
ccli |
rv.data.Presentation.CCLI |
14 |
CCLI licensing metadata |
timeline |
rv.data.Presentation.Timeline |
17 |
Timeline with duration |
music_key |
string |
22 |
Music key (rarely used) |
music |
rv.data.Presentation.Music |
23 |
Music key scale data |
Presentation.CCLI
CCLI (Christian Copyright Licensing International) metadata. Present in 157 out of 168 reference files.
| Field Path |
Protobuf Type |
Field Number |
Description |
author |
string |
1 |
Song author(s) (e.g., "Joel Houston, Matt Crocker") |
artist_credits |
string |
2 |
Artist credits (rarely used) |
song_title |
string |
3 |
CCLI song title |
publisher |
string |
4 |
Publisher (e.g., "2012 Hillsong Music Publishing") |
copyright_year |
uint32 |
5 |
Copyright year (e.g., 2012) |
song_number |
uint32 |
6 |
CCLI song number (e.g., 6428767) |
display |
bool |
7 |
Whether to display CCLI info |
album |
string |
8 |
Album name (rarely used) |
artwork |
bytes |
9 |
Album artwork (rarely used) |
Presentation.CueGroup
| Field Path |
Protobuf Type |
Field Number |
Description |
group |
rv.data.Group |
1 |
Group metadata (name, uuid, color) |
cue_identifiers[] |
rv.data.UUID |
2 |
Array of slide UUIDs in this group |
Group (rv.data.Group)
| Field Path |
Protobuf Type |
Field Number |
Description |
uuid |
rv.data.UUID |
1 |
Unique identifier for the group |
name |
string |
2 |
Display name (e.g., "Verse 1", "Chorus") |
color |
rv.data.Color |
3 |
Optional RGBA color (float values 0.0-1.0) |
Presentation.Arrangement
| Field Path |
Protobuf Type |
Field Number |
Description |
uuid |
rv.data.UUID |
1 |
Unique identifier for the arrangement |
name |
string |
2 |
Arrangement name (e.g., "normal", "test2") |
group_identifiers[] |
rv.data.UUID |
3 |
Ordered array of group UUIDs |
Cue (rv.data.Cue)
| Field Path |
Protobuf Type |
Field Number |
Description |
uuid |
rv.data.UUID |
1 |
Unique identifier for the slide |
actions[] |
rv.data.Action |
10 |
Array of actions (slides use actions[0]) |
Action (rv.data.Action)
| Field Path |
Protobuf Type |
Field Number |
Description |
slide |
rv.data.Action.SlideType |
23 |
Slide data (oneof field) |
Action.SlideType
| Field Path |
Protobuf Type |
Field Number |
Description |
presentation |
rv.data.PresentationSlide |
2 |
Presentation slide (oneof field) |
PresentationSlide (rv.data.PresentationSlide)
| Field Path |
Protobuf Type |
Field Number |
Description |
base_slide |
rv.data.Slide |
1 |
Base slide containing elements |
Slide (rv.data.Slide)
| Field Path |
Protobuf Type |
Field Number |
Description |
elements[] |
rv.data.Slide.Element |
1 |
Array of slide elements |
Slide.Element
| Field Path |
Protobuf Type |
Field Number |
Description |
element |
rv.data.Graphics.Element |
1 |
Graphics element wrapper |
Graphics.Element
| Field Path |
Protobuf Type |
Field Number |
Description |
uuid |
rv.data.UUID |
1 |
Unique identifier for the element |
name |
string |
2 |
User-defined label (e.g., "Orginal", "Deutsch") |
text |
rv.data.Graphics.Text |
13 |
Text data (optional) |
Graphics.Text
| Field Path |
Protobuf Type |
Field Number |
Description |
rtf_data |
bytes |
3 |
RTF-encoded text content |
4. Groups
Definition
Groups represent song parts (Verse 1, Verse 2, Chorus, Bridge, Ending, etc.). They define logical sections of a song.
Characteristics
- Names: User-defined strings. Not standardized. Examples: "Verse 1", "Strophe 1", "Refrain", "Ending".
- Slide References: Each group contains an ordered array of slide UUIDs (
cue_identifiers).
- Color: Optional RGBA color (float values 0.0-1.0 for red, green, blue, alpha).
- Special Groups: COPYRIGHT, BLANK — treated as regular groups (no special handling required).
Example (Test.pro)
- Verse 1 → 1 slide
- Verse 2 → 1 slide
- Chorus → 2 slides
- Ending → 1 slide
Access Pattern
foreach ($presentation->getCueGroups() as $cueGroup) {
$group = $cueGroup->getGroup();
$name = $group->getName();
$uuid = $group->getUuid()->getString();
$slideUuids = [];
foreach ($cueGroup->getCueIdentifiers() as $uuid) {
$slideUuids[] = $uuid->getString();
}
}
5. Slides
Definition
Slides are individual presentation frames. Each slide can contain multiple elements (text, shapes, media).
Navigation Path
Cue → actions[0] → slide → presentation → base_slide → elements[]
Text Elements
- Location:
base_slide.elements[] contains Slide.Element wrappers.
- Graphics Element: Each
Slide.Element wraps a Graphics.Element.
- Text Data:
Graphics.Element.text.rtf_data contains RTF-encoded text.
- Element Name:
Graphics.Element.name is a user-defined label (e.g., "Orginal", "Deutsch").
Slides Without Text
Some slides contain only media (images, videos) or shapes. These slides have elements[] with no text field set.
UUID References
Groups reference slides by UUID. Use Cue.uuid to match slides to group references.
Example (Test.pro)
- 5 slides total
- Chorus group → 2 slides (UUIDs referenced in
cue_identifiers)
6. Arrangements
Definition
Arrangements define the order and selection of groups for a presentation. They specify which groups appear and in what sequence.
Characteristics
- Group References: Ordered array of group UUIDs (
group_identifiers).
- Repetition: The same group UUID can appear multiple times (e.g., Chorus repeated 3 times).
- Optional: Songs may have 0 or more arrangements.
- No Arrangements: 17 out of 169 reference files have no arrangements. This is valid.
Example (Test.pro)
- Arrangement "normal": Verse 1 → Chorus → Verse 2 → Chorus → Ending
- Arrangement "test2": Verse 1 → Verse 2 → Chorus
Access Pattern
foreach ($presentation->getArrangements() as $arrangement) {
$name = $arrangement->getName();
$groupUuids = [];
foreach ($arrangement->getGroupIdentifiers() as $uuid) {
$groupUuids[] = $uuid->getString();
}
}
7. Translations
Definition
Multiple elements[] per slide represent multiple text layers. The first element is the original text; subsequent elements are translations.
Characteristics
- Element Count: 1 element = no translation. 2+ elements = translation present.
- Element Names: User-defined labels (e.g., "Orginal", "Deutsch", "Text", "Text 2").
- Label Patterns: 3 known patterns observed:
- "Orginal" / "Deutsch"
- "Text" / "Text 2"
- No specific naming (generic labels)
- Not Standardized: Element names are arbitrary strings. Do NOT assume fixed labels.
Detection
$textElements = [];
foreach ($baseSlide->getElements() as $slideElement) {
$graphicsElement = $slideElement->getElement();
if ($graphicsElement !== null && $graphicsElement->hasText()) {
$textElements[] = $graphicsElement;
}
}
$hasTranslation = count($textElements) >= 2;
$originalText = $textElements[0]->getText()->getRtfData();
$translationText = $textElements[1]->getText()->getRtfData() ?? null;
Example (Test.pro)
- Slide 1: 2 text elements → "Orginal" (German), "Deutsch" (English translation)
- Element Names: User-defined, not standardized
8. Edge Cases
Empty Files
- Size: 0 bytes
- Validity: Invalid
- Action: Throw exception
Songs Without Arrangements
- Frequency: 17 out of 169 reference files
- Validity: Valid
- Behavior:
arrangements[] is empty. Groups and slides still exist.
Non-Song Presentations
- Examples: ANKUENDIGUNGEN, MODERATION, THEMA
- Characteristics: Have groups and slides but may lack text elements.
- Validity: Valid
Slides Without Text
- Characteristics:
elements[] contains shapes, media, or other non-text elements.
- Detection:
Graphics.Element.hasText() returns false.
- Validity: Valid
COPYRIGHT and BLANK Groups
- Treatment: Regular groups (no special handling required).
- Validity: Valid
9. RTF Text Format
Format Variant
- Type: Apple CocoaRTF 2761
- Encoding: Windows-1252 (ANSI codepage 1252)
Structure
{\rtf1\ansi\ansicpg1252\cocoartf2761
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0
\f0\fs96 \cf1 \CocoaLigature0 TEXT STARTS HERE}
- Text Start: After
\CocoaLigature0 (space after 0 is the delimiter).
- Soft Returns:
\ + newline character = line break within slide.
- Paragraph Breaks:
\par = paragraph break.
Character Encoding
Windows-1252 Hex Escapes
- Format:
\'xx where xx is a hex byte value.
- Examples:
\'fc → ü (U+00FC)
\'f6 → ö (U+00F6)
\'e4 → ä (U+00E4)
\'df → ß (U+00DF)
Unicode Escapes
- Format:
\uN? where N is a decimal codepoint, ? is an ANSI fallback character.
- Examples:
\u8364? → € (U+20AC)
\u8220? → " (U+201C)
\u8221? → " (U+201D)
- Negative Values: RTF uses signed 16-bit integers. Negative values are converted:
codepoint + 65536.
Control Words
- Format:
\word[N] followed by space or non-alpha character.
- Common Words:
\par → paragraph break
\CocoaLigature0 → text start marker
\f0, \fs96, \cf1 → formatting (font, size, color)
- Delimiter: Space after control word is consumed (not part of text).
Escaped Characters
\{ → {
\} → }
\\ → \ (or soft return in ProPresenter context)
Example RTF
{\rtf1\ansi\ansicpg1252\cocoartf2761
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\pard\tx560\pardirnatural\partightenfactor0
\f0\fs96 \cf1 \CocoaLigature0 Gro\'dfe Gnade\
Amazing Grace}
Plain Text Output:
Große Gnade
Amazing Grace
10. PHP Parser Usage
Installation
composer require propresenter/parser
Read a Song
use ProPresenter\Parser\ProFileReader;
$song = ProFileReader::read('path/to/song.pro');
Access Song Metadata
// Song name and UUID
$name = $song->getName(); // "Amazing Grace"
$uuid = $song->getUuid(); // "A1B2C3D4-..."
// CCLI metadata
$author = $song->getCcliAuthor(); // "Joel Houston, Matt Crocker"
$title = $song->getCcliSongTitle(); // "Oceans (Where Feet May Fail)"
$publisher = $song->getCcliPublisher(); // "2012 Hillsong Music Publishing"
$year = $song->getCcliCopyrightYear(); // 2012
$number = $song->getCcliSongNumber(); // 6428767
$display = $song->getCcliDisplay(); // true
$credits = $song->getCcliArtistCredits(); // ""
$album = $song->getCcliAlbum(); // ""
// Other metadata
$category = $song->getCategory(); // ""
$notes = $song->getNotes(); // ""
$selectedArr = $song->getSelectedArrangementUuid(); // "uuid-string"
// Groups, Slides, Arrangements
$groups = $song->getGroups(); // Group[]
$slides = $song->getSlides(); // Slide[]
$arrangements = $song->getArrangements(); // Arrangement[]
Access Groups
foreach ($song->getGroups() as $group) {
$name = $group->getName(); // "Verse 1"
$uuid = $group->getUuid(); // "E5F6G7H8-..."
$color = $group->getColor(); // ['r' => 1.0, 'g' => 0.0, 'b' => 0.0, 'a' => 1.0] or null
$slideUuids = $group->getSlideUuids(); // ["uuid1", "uuid2", ...]
}
Access Slides
foreach ($song->getSlides() as $slide) {
$uuid = $slide->getUuid();
$plainText = $slide->getPlainText(); // Extracted from first text element
// Check for translation
if ($slide->hasTranslation()) {
$translation = $slide->getTranslation();
$translatedText = $translation->getPlainText();
}
// Access all text elements
foreach ($slide->getTextElements() as $textElement) {
$name = $textElement->getName(); // "Orginal", "Deutsch", etc.
$rtf = $textElement->getRtfData(); // Raw RTF bytes
$plain = $textElement->getPlainText(); // Extracted plain text
}
}
Access Arrangements
foreach ($song->getArrangements() as $arrangement) {
$name = $arrangement->getName(); // "normal"
$groupUuids = $arrangement->getGroupUuids(); // ["uuid1", "uuid2", "uuid1", ...]
// Resolve groups
$groups = $song->getGroupsForArrangement($arrangement);
foreach ($groups as $group) {
echo $group->getName() . "\n";
}
}
Access Slides for a Group
$group = $song->getGroupByName("Chorus");
$slides = $song->getSlidesForGroup($group);
foreach ($slides as $slide) {
echo $slide->getPlainText() . "\n";
}
Modify and Write
use ProPresenter\Parser\ProFileWriter;
// Modify song metadata
$song->setName("New Song Title");
$song->setCategory("Worship");
$song->setNotes("Use acoustic intro");
// Modify CCLI metadata
$song->setCcliAuthor("Author Name");
$song->setCcliSongTitle("Song Title");
$song->setCcliPublisher("Publisher");
$song->setCcliCopyrightYear(2024);
$song->setCcliSongNumber(12345);
$song->setCcliDisplay(true);
// Modify group
$group = $song->getGroupByName("Verse 1");
$group->setName("Strophe 1");
// Write to file
ProFileWriter::write($song, 'output.pro');
Error Handling
try {
$song = ProFileReader::read('song.pro');
} catch (\RuntimeException $e) {
// File not found, empty file, or invalid protobuf
echo "Error: " . $e->getMessage();
}
$song = ProFileReader::read('song.pro');
foreach ($song->getGroups() as $group) {
echo "Group: " . $group->getName() . "\n";
$slides = $song->getSlidesForGroup($group);
foreach ($slides as $slide) {
echo " Original: " . $slide->getPlainText() . "\n";
if ($slide->hasTranslation()) {
echo " Translation: " . $slide->getTranslation()->getPlainText() . "\n";
}
}
}
Example: Create Arrangement
$song = ProFileReader::read('song.pro');
// Get group UUIDs
$verse1 = $song->getGroupByName("Verse 1");
$chorus = $song->getGroupByName("Chorus");
$verse2 = $song->getGroupByName("Verse 2");
// Create new arrangement
$arrangement = new Arrangement(new \Rv\Data\Presentation\Arrangement());
$arrangement->setName("custom");
$arrangement->setGroupUuids([
$verse1->getUuid(),
$chorus->getUuid(),
$verse2->getUuid(),
$chorus->getUuid(),
]);
// Add to song (requires direct protobuf access)
$song->getPresentation()->getArrangements()[] = $arrangement->getProto();
ProFileWriter::write($song, 'output.pro');
Generate a New Song
use ProPresenter\Parser\ProFileGenerator;
$song = ProFileGenerator::generate(
'Amazing Grace',
[
[
'name' => 'Verse 1',
'color' => [0.13, 0.59, 0.95, 1.0],
'slides' => [
['text' => 'Amazing grace, how sweet the sound'],
['text' => 'That saved a wretch like me'],
],
],
[
'name' => 'Chorus',
'color' => [0.95, 0.27, 0.27, 1.0],
'slides' => [
['text' => 'I once was lost, but now am found'],
],
],
],
[
['name' => 'normal', 'groupNames' => ['Verse 1', 'Chorus', 'Verse 1']],
],
[
'author' => 'John Newton',
'song_title' => 'Amazing Grace',
'copyright_year' => 1779,
],
);
// Write to file
ProFileGenerator::generateAndWrite('output.pro', 'Amazing Grace', $groups, $arrangements, $ccli);
Generate a Song with Translations
$song = ProFileGenerator::generate(
'Oceans',
[
[
'name' => 'Verse 1',
'color' => [0.13, 0.59, 0.95, 1.0],
'slides' => [
[
'text' => 'You call me out upon the waters',
'translation' => 'Du rufst mich auf das Wasser',
],
],
],
],
[
['name' => 'normal', 'groupNames' => ['Verse 1']],
],
);
Appendix: Test.pro Structure
Groups (4)
- Verse 1 → 2 slides
- Verse 2 → 1 slide
- Chorus → 1 slide
- Ending → 1 slide
Slides (5)
- Slides 1-2: Verse 1 text (2 text elements each: "Orginal", "Deutsch")
- Slide 3: Verse 2 text (2 text elements)
- Slide 4: Chorus text (2 text elements)
- Slide 5: Ending text (2 text elements, with translations)
Arrangements (2)
- normal: Chorus → Verse 1 → Chorus → Verse 2 → Chorus
- test2: Verse 1 → Chorus → Verse 2 → Chorus
Appendix: Reference Statistics
- Total Files: 169
- Parseable Files: 168
- Empty Files: 1 (invalid)
- Files Without Arrangements: 17 (valid)
- Files With CCLI Data: 157 out of 168
- Binary Fidelity: 0 files pass round-trip decode→encode (proto definitions incomplete)
Appendix: Proto Field Numbers Quick Reference
| Message |
Field |
Number |
| Presentation |
application_info |
1 |
| Presentation |
uuid |
2 |
| Presentation |
name |
3 |
| Presentation |
last_date_used |
4 |
| Presentation |
last_modified_date |
5 |
| Presentation |
category |
6 |
| Presentation |
notes |
7 |
| Presentation |
selected_arrangement |
10 |
| Presentation |
arrangements |
11 |
| Presentation |
cue_groups |
12 |
| Presentation |
cues |
13 |
| Presentation |
ccli |
14 |
| Presentation |
timeline |
17 |
| Presentation |
music_key |
22 |
| Presentation |
music |
23 |
| Presentation.CCLI |
author |
1 |
| Presentation.CCLI |
artist_credits |
2 |
| Presentation.CCLI |
song_title |
3 |
| Presentation.CCLI |
publisher |
4 |
| Presentation.CCLI |
copyright_year |
5 |
| Presentation.CCLI |
song_number |
6 |
| Presentation.CCLI |
display |
7 |
| Presentation.CCLI |
album |
8 |
| Presentation.CCLI |
artwork |
9 |
| CueGroup |
group |
1 |
| CueGroup |
cue_identifiers |
2 |
| Group |
uuid |
1 |
| Group |
name |
2 |
| Group |
color |
3 |
| Arrangement |
uuid |
1 |
| Arrangement |
name |
2 |
| Arrangement |
group_identifiers |
3 |
| Cue |
uuid |
1 |
| Cue |
actions |
10 |
| Action |
slide |
23 |
| Action.SlideType |
presentation |
2 |
| PresentationSlide |
base_slide |
1 |
| Slide |
elements |
1 |
| Slide.Element |
element |
1 |
| Graphics.Element |
uuid |
1 |
| Graphics.Element |
name |
2 |
| Graphics.Element |
text |
13 |
| Graphics.Text |
rtf_data |
3 |
End of Specification