osf 0.1.0

Parser for Open Screenplay Format (OSF) files used by Fade In Pro screenwriting software
Documentation
# osf

[![Crates.io](https://img.shields.io/crates/v/osf.svg)](https://crates.io/crates/osf)
[![Documentation](https://docs.rs/osf/badge.svg)](https://docs.rs/osf)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

A Rust parser for [Open Screenplay Format](https://github.com/OpenScreenplayFormat/osf-sdk) (OSF) files — the native format of [Fade In Pro](https://www.fadeinpro.com) screenwriting software.

## Features

- Parse `.fadein` files (ZIP archives containing OSF XML)
- Parse raw OSF XML directly
- Supports all OSF versions (v1.2, v2.x, v4.0)
- Extracts title page metadata, scenes, characters, and locations
- Reconstructs formatted screenplay text with standard indentation
- Serde `Serialize` on all output types
- Zero unsafe code

## Supported Versions

| Version | Attribute Convention | Title Page Source |
|---------|---------------------|-------------------|
| 1.2     | `basestylename`     | `<info>` attributes |
| 2.0/2.1 | `baseStyleName` (camelCase) | `<titlepage>` bookmarks |
| 4.0     | `basestyle` (snake_case)    | `<titlepage>` bookmarks |

## Usage

Add to your `Cargo.toml`:

```toml
[dependencies]
osf = "0.1"
```

### Parse a `.fadein` file

```rust
let data = std::fs::read("screenplay.fadein")?;
let doc = osf::parse(&data)?;

println!("Title: {:?}", doc.title_page.title);
println!("Authors: {:?}", doc.title_page.authors);

for scene in &doc.scenes {
    println!("Scene {}: {}", scene.number, scene.heading);
}
```

### Parse raw OSF XML

```rust
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<document type="Open Screenplay Format document" version="40">
  <info uuid="abc" pagecount="1"/>
  <settings/><styles/>
  <paragraphs>
    <para><style basestyle="Scene Heading"/><text>INT. OFFICE - DAY</text></para>
    <para><style basestyle="Action"/><text>A desk. A chair.</text></para>
    <para><style basestyle="Character"/><text>SARAH</text></para>
    <para><style basestyle="Dialogue"/><text>We need to talk.</text></para>
  </paragraphs>
  <titlepage/><lists/>
</document>"#;

let doc = osf::parse(xml.as_bytes())?;
assert_eq!(doc.scenes.len(), 1);
assert_eq!(doc.scenes[0].heading, "INT. OFFICE - DAY");
```

### Access extracted metadata

```rust
let doc = osf::parse(&data)?;

// Title page
println!("Title: {:?}", doc.title_page.title);
println!("Authors: {:?}", doc.title_page.authors);
println!("Draft: {:?}", doc.title_page.draft);
println!("Contact: {:?}", doc.title_page.contact);

// Characters and locations from <lists>
println!("Characters: {:?}", doc.characters);
println!("Locations: {:?}", doc.locations);

// Raw formatted text (for downstream processing)
println!("{}", doc.raw_text);
```

## Output Types

| Type | Description |
|------|-------------|
| `OsfDocument` | Top-level parsed document with all extracted data |
| `OsfVersion` | Detected format version (V1, V2, V4) |
| `TitlePage` | Title, authors, draft, contact, copyright |
| `Scene` | Scene number, heading, page, body text |
| `ParaStyle` | Paragraph type (SceneHeading, Action, Character, Dialogue, etc.) |

## About Open Screenplay Format

OSF is an open, application-independent XML format for screenwriting. `.fadein` files are ZIP archives containing a `document.xml` file in OSF format. The format defines eight built-in paragraph styles that map to standard screenplay elements:

- **Scene Heading** — slug lines (`INT. OFFICE - DAY`)
- **Action** — description and stage direction
- **Character** — character name above dialogue
- **Parenthetical** — actor direction (`(whispering)`)
- **Dialogue** — spoken lines
- **Transition**`CUT TO:`, `FADE IN:`, etc.
- **Shot** — camera direction

## License

MIT — see [LICENSE](LICENSE) for details.