# LVR format: a hinário as a JSON file

This document describes the **`.lvr`** format: a JSON structure that represents a single **hinário** (hymn book). **O Livro**, the hinário app, reads and writes these files; the **Hinario Publisher** reads them to produce print-ready PDF layouts.

**Rules:**
- **Order only:** The order of songs in the hinário is determined **only** by the order of the root `songs` array. There are no IDs used to order or link songs.
- **Optional Nossa ID:** The field `noss_id` (on hinario and on each song) is **optional** and means *only* the NossaIrmandade.com website ID. Omit it if you don’t have it (e.g. when creating from a PDF).

---

## 1. Root structure

The file must be a single JSON object with two keys:

| Key | Type | Description |
|-----|------|-------------|
| **`hinario`** | object | Metadata for the hinário (title, author). |
| **`songs`** | array | List of songs **in display order**. Index 0 = first song, index 1 = second, etc. |

**Minimal valid file:**

```json
{
  "hinario": {
    "title": "Name of the hinário",
    "recebidopor": "Author or 'received by' name"
  },
  "songs": [
    {
      "hnumero": "1",
      "title": "First song title",
      "recebidopor": "Author name",
      "words": [{ "l": [{ "t": "First line of lyrics" }] }]
    }
  ]
}
```

---

## 2. `hinario` object

Contains metadata for the whole hinário.

| Field | Required | Description | Where to find in PDF | How to handle |
|-------|----------|-------------|----------------------|---------------|
| **`title`** | yes | Full title of the hinário. | **Cover.** Often mixed with the author name on the same line or in the same block. | Use the title **verbatim from the cover**; never build it from author names. Propose it and **ask the user to confirm** before creating the file. |
| **`recebidopor`** | yes | “Received by” / author (e.g. "Mestre Irineu"). | **Cover.** Some hinários are compilations and have no single author. | Propose a value; if there is no clear author, use empty string `""`. **Ask the user to confirm** before creating the file. |
| **`noss_id`** | no | NossaIrmandade.com hinário ID. | Not in PDF. | Omit if unknown. |

**Example:**

```json
"hinario": {
  "title": "O Cruzeiro",
  "recebidopor": "Mestre Irineu",
  "noss_id": "14"
}
```

---

## 3. `songs` array

One object per song, **in the exact order** they appear in the hinário. No other ordering or linking is used.

### 3.1 Required song fields

| Field | Type | Description | Where to find in PDF | How to handle |
|-------|------|-------------|----------------------|---------------|
| **`hnumero`** | string | Song number in this hinário (e.g. `"1"`, `"2"`, `"36"`, `"1a"`). | Usually at the top of the song, often with the title. | Use the number as shown; strip it from the title (see `title`). It is printed verbatim, so alphanumeric numbers are allowed. |
| **`title`** | string | Song title. | **Top of the page**, at the head of each song. Often in **UPPERCASE** and **underlined**. Frequently appears with a running number (e.g. "1. TITLE" or "36 – TITLE"). | **Remove the number** from the title; store only the title text. Store it in **Title Case with every word capitalized** (`"Examine A Consciência"`); normalize UPPERCASE headings to that form, but never change the words or their accents. Every song has a title. Songs can span **multiple pages**; keep one song object and continue its `words` across pages. A page whose title carries a continuation marker such as `"(continued...)"` is **not a new song**: strip the marker and append its stanzas to the previous song. |
| **`recebidopor`** | string | Author / “received by” for this song. | **Optional.** Usually **left under the title**, sometimes with the label "Recebido por", "Received by", or just the name. | Store **only the name**: strip labels ("Recebido por") and position markers such as `"#84"`. Use empty string `""` if not present. In **compilations** (hinário without author), if only one name appears under the title, it is **`recebidopor`**, not `ofereceupara`. |
| **`words`** | array | Lyrics: list of stanzas (see below). | Body of the song below the title/author block. | Split into stanzas and lines; use `r`, `s`, `p`, `lnotation` as in §4. |

### 3.2 Optional song fields

| Field | Description | Where to find in PDF |
|-------|-------------|----------------------|
| **`hinario1`** | Hinário name (for display; can match `hinario.title`). | Same as hinário title. |
| **`numero1`** | Same as `hnumero` (redundant but allowed). | Same as song number. |
| **`notation`** | Musical/form notation. **Lowercase**; several values joined with `" - "` (e.g. `"valsa"`, `"marcha"`, `"2x - valsa"`). | A short, often bold label near or to the right of the title. It is **never** a lyric line; join it when it wraps over several printed lines. |
| **`ofereceupara`** | “Offered to” dedication. Store **only the name** (`"Madrinha Rita"`), never the label. | Text like “Ofereceu para”, “Offered to”, or sometimes only a name in parentheses. **Important:** In compilations (hinário without author), if only one name appears, use **`recebidopor`**, not `ofereceupara`. Use `ofereceupara` only for an explicit dedication (e.g. “Ofereceu para X"). **When the hinário has one clear author** but many songs show a **(different) name** under the title: **ask the user** whether these are dedications and should go in **`ofereceupara`**; if so, suggest filling all **`recebidopor`** with the **hinário author name**. |
| **`missing`** | Set to `1` if this slot is a placeholder (song missing). | — |
| **`wm`** | Set to `1` if title exists but lyrics are missing. | — |
| **`noss_id`** | NossaIrmandade.com hymn ID. | Not in PDF. Omit if unknown. |

### 3.3 `words` (lyrics)

`words` is an array of **stanzas**. Each stanza is an object with **exactly one key**:

- **`l`** (array): the lines in that stanza. Each element of `l` is a **line object** (see §4). The stanza object must **not** have any other keys used for line-level data.

**Important — repetition bars (`r`) and related fields belong on each line, not on the stanza:**

- **Correct:** Put `r`, `s`, `p`, and `lnotation` **only on the line objects** inside `l`. Each line that has a repetition bar next to it in the PDF gets its own `"r": "1"` (or `"2"`, `"3"`) on that line object.
- **Wrong:** Do **not** put `r` (or `s`, `p`, `lnotation`) on the stanza object. A stanza has only `l` (array of line objects). Readers ignore `r` on the stanza, so the repetition bars will not appear.
- **Wrong:** Do **not** write the blank line itself as a line object. `{ "t": "" }` is not a line — `l` holds only lines that carry text. A blank between two blocks is either a **new stanza object** or **`p`: true** on the line after it (§5). An empty line object survives into the reader's storage, renders as a gap that neither a stanza break nor an `s` explains, and is written back out unchanged on the next export.

**Correct (r on each line):**

```json
{ "l": [
  { "t": "Contagiou, contagiou", "r": "1" },
  { "t": "Alegrou, alegrou", "r": "1" },
  { "t": "Plain line without bar" }
] }
```

**Wrong (r on stanza — do not do this):**

```json
{ "r": "1", "l": [{ "t": "Line one" }, { "t": "Line two" }] }
```

**Minimal song (one stanza, one line):**

```json
{
  "hnumero": "1",
  "title": "Song title",
  "recebidopor": "Author",
  "words": [
    { "l": [{ "t": "Line one" }, { "t": "Line two" }] }
  ]
}
```

**Full song example:**

```json
{
  "hnumero": "36",
  "title": "Examine A Consciência",
  "recebidopor": "Mestre Irineu",
  "ofereceupara": "Madrinha Rita",
  "hinario1": "O Cruzeiro",
  "numero1": "36",
  "notation": "valsa",
  "words": [
    {
      "l": [
        { "t": "Examine a consciência", "r": "1" },
        { "t": "Examine direitinho", "r": "1" }
      ]
    },
    {
      "l": [
        { "t": "Chamo de um a um" },
        { "t": "A todos eu mostro o caminho" }
      ]
    }
  ],
  "noss_id": "226"
}
```

---

## 4. Line object (inside `words[].l`)

Each element of `l` is a line object.

| Field | Required | Description |
|-------|----------|-------------|
| **`t`** | yes | The line text. |
| **`r`** | no | Number of repetition bars (vertical lines) next to this line: `"1"`, `"2"`, or `"3"`. See §5. |
| **`lnotation`** | no | Short line notation (e.g. dance or style) that appears **left of the repetition lines**. See §5. |
| **`s`** | no | Set to `true` when the repetition bar **breaks** after this line (line is interrupted). Breaks the **innermost** bar; see `sdepth`. See §5. |
| **`sdepth`** | no | Integer ≥ 2: how many bars break after this line, counted from the **innermost**. Only together with `s: true`, never greater than `r`. Omit it for the normal case (1). See §5. |
| **`p`** | no | Set to `true` for a soft paragraph break **before** this line (e.g. blank line that does not break the stanza). See §5. |

**Types are strict.** `r` must be a JSON **string** (`"1"`, `"2"`, `"3"`) — never the number `1`. `s` and `p` must be JSON `true`. A file with `"r": 1` shows every bar in O Livro and **no bar at all** in the printed PDF, with no error.

---

## 5. Repetition lines and line notation (from PDF)

**Attention — visual analysis required:** Repetition bars (vertical lines) are **graphical elements** in the layout. Always determine the value of `r` by **inspecting the visual layout** of the PDF or image (where the lines are drawn, how many, and where they break). Do **not** rely on text extraction alone: these lines have no text representation and are often **lost** during plain-text conversion. If you only have access to plain text without visual/layout metadata, **inform the user** that repetition bars (`r`) cannot be detected and may be missing in the generated file.

**Ignore default patterns:** Do **not** assume that every stanza has repetition bars. Many songs have refrains or stanzas with no lines next to the text.

**Negative scan (no hallucinated lines):** Assume that **no line has repetition** unless you see proof. Only set **`r`** when you actually see a **physical, black vertical line** to the left of the text in the PDF or image. If the line is missing, **`r` is forbidden**—even if the lyrics or singing suggest a repeat. AIs often hallucinate lines where there are none; resist that. Evidence is visual only. A marker such as `(2x)` or `(bis)` never produces `r`: next to the title it belongs in `notation`, inside the lyrics it stays part of `t`.

**Scanned pages.** On a scan the bars are rarely clean black: they may be grey, faint, broken into segments, or tilted along with the page. Judge them by **position and extent** — a repetition bar sits in the left margin of the text block, at the same horizontal distance on every page, and runs parallel to the lines it belongs to. Do **not** count the edge of the page or of a printed frame, the dark band of the binding gutter, text or rules showing through from the back of the sheet, or specks and scanning artefacts. When a stroke is too faint to judge, omit `r`: a missing bar is a smaller error than an invented one.

**Continuation pages carry no bars.** Every song starts at the top of a page, so each page is either a song's **first page** — it carries a title at the top — or a **continuation page** of the song before it. When a song runs over more than one page, the print **repeats** the repetition bars at the top of each continuation page, so that a reader who has turned the leaf still sees the bracket. In `.lvr` there are no pages and nothing to turn, so those repeated bars describe nothing. **Read bars on first pages only.** On a continuation page, **ignore every vertical bar you see**: those lines get no `r`. Two clarifications:

- Do **not** mark the page edge with `s`. Since the lines on the next page carry no `r`, the run ends there by itself; `s` marks an interruption between two lines that both still have a bar, which cannot happen across a page break.
- Text, stanzas and lines continue across the page as usual (§3.1). Only the bars are dropped. On a continuation page a blank line is therefore always a **new stanza** — `p` needs a bar spanning the gap, and there is none.

**Important for AI models — when input is plain text:** If the input is **plain text only** (e.g. text extraction), the graphical repetition lines (`r`) are **physically absent**. Then:

1. **Stop condition:** Never create a `.lvr` file from **text alone** when a PDF or image is available. You must analyze the **visual layer** (pixels/layout) to determine `r`.
2. **Logical inference (fallback):** Only when visual analysis is **not possible** (e.g. user supplied plain text only), you may use **text markers** such as `(2x)`, `(3x)`, or `(bis)` to set `r`. In that case, the Pre-Analysis Report must explicitly mark these as **“derived from text”**, not from layout.
3. **Inheritance:** If a stanza is marked by vertical lines in the layout, **every line object** within that stanza gets the **same** `r` value (e.g. `"1"`), unless the line **visually breaks** before that line (then use `s`: true on the line where it breaks).

**Line-by-line scan:** For each song, look at the **left column** next to the text. Count the number of vertical lines **per text line exactly** (0, 1, 2, or 3). That is your value for **`r`**. Omit `r` when there are no lines (0).

In the PDF, **vertical lines** to the left of the strophes indicate **which lines are repeated** when singing. Map them as follows:

- **Number of lines:** Next to a text line there may be **no line**, **one**, **two**, or **three** vertical lines. Store that count in **`r`**: `"1"`, `"2"`, or `"3"`. No line → omit `r`. Look for **double** lines (thin pair) for `r`: `"2"` and **triple** lines for `r`: `"3"`.
- **Splits (`s`):** A line that is **briefly interrupted** within a stanza (even a tiny gap between two text lines) = **`s`: true** on the **upper** line. The vertical bar runs across several text lines (connected) or is **interrupted**; when it **stops** after a given text line, set **`s`: true** on that line object. **Rule:** Interruption of the bar within a stanza → `s`: true. `s` breaks the **innermost** bar of that line. **`s` never appears on the last line of a stanza** — a bar ends at a stanza boundary by itself (0 of 1,311 corpus uses sit there).
- **Breaking more than the innermost bar (`sdepth`):** When two or more bars stop together, add **`sdepth`** with the number of bars that break, counted from the innermost: `{ "t": "…", "r": "2", "s": true, "sdepth": 2 }` ends **both** bars. Omit it for the normal case — `s: true` alone means depth 1, which is what all 43 corpus uses of `s` on an `r >= 2` line mean. Never greater than `r`.
- **Paragraphs (`p`) and stanza boundaries:** **Rule:** White space with **no text** (blank line or visual gap) = **new stanza object**. Do not put the next block of text in the same stanza. Exception: if a vertical line **continues across** that gap (bar clearly spans it), keep one stanza and set **`p`: true** on the **first line after the gap**. If the bar **stops** at the gap, start a **new stanza**. `p` only adds vertical space before the line; it **never** ends a repetition bar.
- **The blank line is never a line object.** There are exactly two ways to write a blank: a **new stanza**, or **`p`: true** on the line after it. An object whose `t` is empty, missing or whitespace-only is **never** written — not between stanzas, not inside one, not to add extra space. The corpus holds 0 of them in 78 512 lines. Watch for the case that produces them most often: a **short interruption of the bar between two text lines** is **`s`**, not a blank line. A reader may repair the file, but the repair has to guess between the two legal encodings and can never recover the `s` you meant.
- **Line notation:** To the **left of the repetition lines**, there is sometimes a **short label** (e.g. dance name or style). Store it in **`lnotation`** on the corresponding text line.

**Example lines:**

```json
{ "t": "Plain line" }
{ "t": "Chorus line", "r": "1" }
{ "t": "Line with bar break", "r": "1", "s": true }
{ "t": "New paragraph", "p": true }
{ "t": "Line with notation", "r": "1", "lnotation": "Valsa" }
```

---

## 6. Creating a .lvr from a PDF (workflow for AI)

**Analyze the PDF visually, not only as text.** Layout elements (repetition bars, line grouping, paragraph breaks) are part of the **graphical layout**. Use the rendered pages or images to decide stanza boundaries, line breaks, and especially the number and position of repetition bars (`r`). Pure text extraction from PDF often drops these elements and produces an incorrect or incomplete `.lvr`.

**Negative constraint — layout over logic:** Set **`r` NEVER** based on text content or assumptions. If **no black vertical line** is visible to the left of the line **in the image**, **`r` must be omitted**—even if the text says “(2x)”, “(3x)”, or suggests a repeat. Layout is the only source of truth for `r`.

1. **Extract structure from the PDF (visually) — visual primacy:** The **graphical arrangement** of vertical lines in the PDF has **absolute priority** over the text structure. **Scan the left column of each page line by line** before processing the song text — but on **first pages** only: a page that continues the song before it repeats its bars, and those repeats mean nothing (§5). Then identify the hinário title and author on the **cover** (§2). Identify the list of songs in order; for each song, get number, title, author/dedication, and lyrics from the **top of the page** and body (§3). Use the **visual layout** (including vertical repetition lines) to set `r`, `s`, and `p` on the correct **line objects** (§5). Use “Where to find in PDF” and “How to handle” in the tables above.
2. **Confirm with the user:** Propose `hinario.title` and `hinario.recebidopor` (or empty author for compilations). **Ask the user to confirm** before generating the file. **If the hinário is clearly attributed to one author** but many songs have a **(different) name** under the title: **ask the user** whether those names are **dedications** and should be stored in **`ofereceupara`**. If yes, **suggest filling all song `recebidopor`** fields with the **hinário author name** (and put the per-song names in `ofereceupara`). **If the PDF contains songs in different languages** (e.g. Portuguese and English): check whether they are **translations** of other songs in the PDF or **entirely different songs**.
   - **Translations:** **Ask the user** whether the translations should be **omitted** from the final `.lvr` or **included**. If included, **never merge** the language versions into one song; always keep each language version as a **separate song object**.
   - **Not translations:** Treat them as normal, independent songs. **Do not ask** the user about them.
3. **Build the root object:** Create `hinario` with confirmed `title` and `recebidopor` (add `noss_id` only if you have the NossaIrmandade.com hinário ID).
4. **Build the `songs` array:** For each song, in order, create an object with at least `hnumero`, `title`, `recebidopor`, and `words`. Strip the number from the title. Use `ofereceupara` only for explicit dedications; in compilations, a single name is `recebidopor`. **Follow the page exactly:** no spelling corrections and no accent changes (`"Tetéo"` stays `"Tetéo"`); if a stanza is printed twice, it appears twice. Optionally add `notation`, `hinario1`, `numero1`, `noss_id`.
5. **Build `words` for each song:** Split lyrics into stanzas and lines. Each stanza is an object with **only** the key `l` (array of line objects). For **each line object** in `l`, set `r` from the number of vertical repetition lines next to that line, `s` when a bar stops after that line, `p` for soft paragraph breaks, and `lnotation` for labels left of the bars (§5). Do not put `r` on the stanza. **Continuation pages:** read bars on a song's **first page** only; drop every bar on its continuation pages (§5). **Bar continuity — when to start a new stanza:** A new stanza object `{ "l": [...] }` may **only** be created when (a) a **vertical bar visually ends** (no line continues), or (b) there is a **clear text gap with no continuous bar** across it. If a bar **spans a blank line**, do **not** start a new stanza; use **`p`: true** on the **first line after the gap**, within the **same** stanza. **Validation of repetitions:** For each song, check: (a) Text hints (e.g. “(valsa)”, “(3x)”) not reflected in the layout? (b) Lines in the layout not mentioned in the text? **Rule:** The `.lvr` format encodes the **layout**, not the singing. If a line is present in the layout, set `r`. If no line is present, leave `r` empty—even if the text suggests a repetition. **Validation of blanks:** For each song, check that no line object has an empty or whitespace-only `t`. If you produced one, go back to the PDF and decide what it actually shows: a **gap in the bar between two text lines, no blank line** → `s`: true on the upper line; a **blank line with the bar running across it** → `p`: true on the line after it; a **blank line with no bar across it** → a new stanza object. Delete the empty line object in every case.
6. **Output:** Serialize the object as JSON (no trailing comma, valid UTF-8). Save as `.lvr`. **Deliver it as a downloadable file** named `<hinario_name>.lvr` — not as JSON text inside the reply.
