Genogram Generator draws a client's family / safeguarding diagram following the social-work genogram convention and exports it for tribunal bundles, Local Authority referrals, statements, or letters. Matter-agnostic, asylum family composition, settlement sponsor chain, child-safeguarding all use the same tool.
.genogram.json sidecar, or a PDF this tool exported with its data embedded.~/Work/00 Open Cases/Case Summaries/ as <ref>.genogram.json), or a plain-prose paragraph for a witness statement. PNG and PDF both carry a key to the symbols this genogram uses.The panel at the top of the Build tab reads two shapes, mixed freely, and builds every person and line from them.
Melania Trump is Donald Trump's wife, born 1970 in Manchester, UK. The client is whoever the sentences are about. Sentences carry sex, dates of birth and death, and places. was instead of is means the person has died.Donald Trump βΆ adopted parent of βΆ Barron Trump, Donald Trump β· divorced β· Ivana Trump with start 1977 Β· end 1992 on the line below, Donald Trump hostile Ivana Trump. Rows connect any two people, not only the client, and carry the exact edge kind, which a sentence cannot.Both round trip: what the tool prints, it can read back. Paste both and they merge on the name, so the paragraph in the witness statement plus the relationship list rebuilds the whole diagram.
Sex is never taken from a name. It comes from the relationship word (mother female), so anyone reached only through a row draws as a dashed square until you set it. The one exception is a bare placeholder name like Mother or Unknown Father, which is a role word standing in for a name; the report lists every one it read that way.
The report under the panel says what was built, what was inferred (a placeholder parent added so an aunt has a side to hang from, two children sharing a date of birth drawn as twins), and every line that was not used. Nothing is dropped in silence. Building replaces what is already entered, so download the sidecar JSON first if you want to keep it.
The full social-work convention: every shape, line variant, overlay kind, and key code, is embedded below. It is the distilled version of the published Genogram Explanation PDF (07 October 2022) used by Local Authority safeguarding teams. Edit at gmiau-specs/GENOGRAM-GUIDE.md; immigrationfyi-tools update refreshes the embedding.
# Genogram Convention: Reference Distilled from "Genogram Explanation" (07/10/2022). Source PDF at `gmiau-specs/source-docs/genogram-guide-07102022.pdf`. This is the social-work / safeguarding convention; it is what colleagues and the Local Authority will expect when a genogram is filed with a case. A **genogram** (a structured family tree) shows current family relationships across **at least three generations**: it is intended to make clear who is in the family, who lives with the child, and what the significant medical, health and emotional dynamics are. Only **family members** belong on a genogram. Friends, professionals, and supporters belong on an *ecomap*, not here. Each person should carry at minimum: **name** and **date of birth (not age)**. Other dates, marriage, divorce / separation, death: are added where known. Significant life events may be noted next to the person. --- ## 1. Layout rules - Orientation: **landscape**. - Within each couple: **male on the LEFT, female on the RIGHT**, joined by a horizontal line. - Children: drawn **below** the parental line, **oldest on the LEFT, youngest on the RIGHT**. - Generations stack vertically: **oldest generation at the TOP, youngest at the BOTTOM**. Typically four tiers, great-grandparents, grandparents, parents (and parents' siblings), child(ren). --- ## 2. Person symbols ``` Male Female Non-binary Unborn baby (D-shape) ____ (triangle; show EDD) Trans FM Trans MF (square outer, (circle outer, circle inner) square inner) Lesbian Gay (circle with (square with inverted triangle) inverted triangle) ``` - **Deceased**: large **X** across the symbol. - **Abortion**: triangle with an **X** through it. - **Miscarriage**: triangle with an **X** through it (drawn smaller / with a single stroke variation; see PDF p.4). - **Stillborn**: an X-crossed shape drawn smaller than a live-born symbol (square for male, circle for female). - **Pet**: **diamond** with the pet's name beside it. (Pets are included so households are accurately represented.) --- ## 3. Couple-line variants The horizontal line between two partners encodes the relationship status. Date labels go **on or just under** the line. ``` Married. Label: m.MM.YYYY (or m.DD.MM.YYYY) / Separated, legal procedure for divorce has started. Γ Separated, no longer living together. // Divorced. Label: m.YYYY d.YYYY X One partner died during the marriage. (Different from the "no longer living together" X, this is on the line, the deceased's symbol carries the across-shape X too.) β β β β β β β β Cohabiting / civil partnership. Label: start date. β β β β / β β β Cohabiting couple separated (living apart). β β β β / β β β 2018β2021 Civil-partnership couple (with dates) or officially separated; both start 01.02.18 and end dates labelled. β 25.06.21 ``` --- ## 4. Parentβchild line variants The vertical line(s) from the parental line down to each child encode the legal / biological tie. ``` Biological child, solid line. + dashes Adopted child: a solid line and a dashed line together (one represents biological origin, one represents adoptive parents). Foster child, dashed line. ``` Twins / multiples share a single descender that fans out at the bottom: ``` non-identical identical triplets twins twins (a fan of 3+) (horizontal bar between the twins) ``` --- ## 5. Household boundary A **free-form loop drawn around** a group of people indicates who is **living in the same household**. The loop may include half-siblings, step-children, pets, whoever sleeps under the same roof. There is no formal shape; it just encloses the relevant symbols. --- ## 6. Key information (next to each person) These abbreviations are drawn **next to the person's symbol** (not inside it) to flag significant health / risk / status information. The PDF stresses this list is a **guide only**, not exhaustive. ``` LD Learning Disability PD Physical Disability SD Sensory Disability MH Mental Health AM Alcohol Misuse SM Substance Misuse D Depression P In Prison CAR Child At Risk SI Severe Illness LI Lifelong Illness SA Sexual Abuse PA Physical Abuse A Anxiety (or: experience of being Abused as a Child) EA Emotional Abuse ``` Multiple codes are separated by spaces / commas, e.g. `LD MH D`. --- ## 7. Emotional-relationship overlay These are drawn as **arrows** or as **plain lines** between two people's symbols, *in addition* to the structural family lines. Arrows point **from the perpetrator to the victim**. ### Abuse / harm (coloured arrows) ``` v v v v v v Violence (red, wavy + arrow) w w w w w w Physical Abuse (blue, wavy + arrow) w w w w w w Emotional Abuse (light green, wavy + arrow) w w w w w w Sexual Abuse (purple, DOUBLE wavy + arrow) w w w w w w Hostile relationship (light blue, wavy + arrow) β β β β β β Neglect (black, DASHED arrow) ``` A **double-headed arrow** ( ) between two people indicates **abuse in both directions** (mutual). ### Closeness / quality (no arrowheads, plain lines between symbols) ``` Very close relationship (double line) Close relationship (single line) β β β β β β β β Distant / poor (dashed line) ``` --- ## 8. Examples (from the PDF) The PDF includes three worked example genograms (PDF pp.10β14). They illustrate, in combination: - a four-generation tree with X-crossed deceased grandparents, multiple marriages with `m.YYYY d.YYYY` labels, biological + adopted + foster children, identical twins, a household boundary loop, plus the violence / hostile / sexual-abuse arrows; - couples shown with cohabiting (dashed) and married (solid) lines, including separated-then-cohabiting transitions; - pets shown as diamonds inside the household loop; - key-info codes (LD, P, CAR, AM) rendered next to the relevant person's symbol. The genogram is a snapshot, it documents the **current** family as known to the worker, with as much accuracy as can reasonably be obtained. Information that is unknown is shown with `?` (e.g. `b.?` or `m.19??`); it is not fabricated.
localStorage; reopening this file restores it. Use Download JSON for a persistent sidecar.Confidentiality: runs entirely in your browser, nothing is uploaded. The sidecar JSON is plaintext; store it under the case's Case Summaries/ folder, same as every other case sidecar.
Paste a paragraph, a list of relationships, or both, and the genogram is built from it. Everyone and every line is created for you; nothing is guessed silently, and the report below says what was read and what was not.
Two shapes are understood, mixed freely, one per line or flowing in a paragraph.
Sentences, exactly what the Export tab writes: Melania Trump is Donald Trump's wife, born 1970 in Manchester, UK. Β· Barron Trump is Donald Trump's son, born 2006-07-28. Β· Fred Trump was Donald Trump's father, born 1905 and died 1999.
Relationship rows, exactly what the list on the right prints: Donald Trump βΆ biological parent of βΆ Barron Trump Β· Donald Trump β· divorced β· Ivana Trump then start 1977 Β· end 1992 on the next line Β· Donald Trump hostile Ivana Trump.
Sentences carry sex, dates and places; rows carry any pair of people and the exact edge kind. Sex is never taken from a name.
Mark exactly one person as Client. Add everyone the genogram needs.
Each row connects two people. Couple = horizontal line; Parent Child = vertical line; Overlay = on-top abuse / closeness arrow.
PNG renders the current genogram at print resolution; PDF wraps it on A4 with the canonical PRIVATE & CONFIDENTIAL header, a footer band (case ref Β· date Β· page), and an optional DRAFT watermark (toggle in Config). Both carry a key to the symbols used in this genogram: on the sheet where it fits, on its own final page where it does not. Turn it off in Config.
(same filename for either orientation)Downloads <ref>.genogram.json for the case's Case Summaries/ folder. The case-summary file itself is never touched. v1 Family Tree sidecars (<ref>.family-tree.json) can be imported.
A single paragraph walking the family outwards from the client. Drop into a witness statement or covering letter; edit as needed. Overlay edges (abuse / closeness) are deliberately NOT emitted, they're safeguarding metadata, not biographical fact. This paragraph pastes back into Build from written text on the Build tab and rebuilds the same diagram, so the family can live in the statement rather than in a sidecar.
Household-boundary freeform loop Β· distinct step-parent dashed-double line Β· EB Garamond inline Β· qpdf-wasm AES-256 encryption Β· auto-import from gmiau-case-summary/1. The PDF here is unencrypted; encrypt downstream via pdf-encrypt.html if needed.
Simple: rounded rectangles for everyone, plain solid couple / parent lines, deceased shown with a single diagonal slash, no key codes, no abuse / closeness overlay. Filenames say Family Tree. Genogram: the published social-work convention: shapes by sex / trans / orientation, X-across deceased, key codes, abuse / closeness overlay arrows. Filenames say Genogram. The data model is the same in both modes: switching never loses what you've entered.
Under the name is the layout in the social-work guide: name, then dates, then the place, in a line under the symbol. Above the symbol follows the Standard Genogram Format instead and sets the place apart from the name, in its own colour above the symbol and to the right of centre, with any key codes to the left. Route around the text takes the couple and parent lines below and around each label block, so no line crosses a name, a date or a place of birth; Straight through is the older behaviour, which ran the line across and relied on the white halo behind the text.
The key explains only the symbols this genogram actually uses. In the PNG it sits under the diagram; in the PDF it shares the sheet when there is room and takes its own final page when there is not.
Adds the plain-prose paragraph from the ↓ Export tab as its own page after the diagram, set as real text at 11 pt so it can be selected, searched and copied out of the bundle rather than read off a picture. It runs to more than one page if the family needs it.
Embedding attaches the full genogram to the PDF as a file, so the PDF reopens in this tool. It is the whole record: notes, key codes and the abuse / closeness overlay edges, whether or not the diagram draws them. Anyone holding the file can extract it. For an exhibit that leaves the office, either encrypt it (AES-256, passphrase on the ↓ Export tab) or set this to Leave out. A passphrase cannot be recovered: lose it and the data in that PDF is gone.
Orientation is chosen at export time via the two PDF buttons on the
Export tab. Filenames follow EXPORT-SPEC.md Β§1:
YYMMDD CLIENTREF SEQ Genogram.{pdf,png}.
The sidecar JSON uses <ref>.genogram.json.
Both formats are unencrypted: pipe through pdf-encrypt.html if AES-256 is needed.
Syncs across every GMIAU Shell tool via ifyi_hide_guide_tab + ifyi_hide_config_tab.
The current GENOGRAM-SPEC.md is inlined at build time. Edit the spec at gmiau-specs/GENOGRAM-SPEC.md and run immigrationfyi-tools update to refresh.
# Genogram Generator: Tool Spec
**Version 2.3 Β· 2026-08-29**
`genogram-generator.html` is a GMIAU Shell tool for drawing a client's
**genogram**, a family / relationship diagram following the
social-work convention summarised in `GENOGRAM-GUIDE.md`. The output is
intended to drop into a tribunal bundle exhibit, into a safeguarding
referral, or into a witness statement (as a paragraph of plain prose).
This tool **supersedes the Family Tree tool** that shipped on
2026-05-14. The Family Tree tool used rectangles for every person and
collapsed all couple relationships into one line type; Genogram
Generator follows the published social-work convention (different
shapes by sex, distinct couple-line variants, parent-child line types,
key-info codes, abuse / closeness overlay).
Cross-referenced specs:
- `gmiau-specs/GENOGRAM-GUIDE.md`, the social-work convention itself
(distilled from the published Genogram Explanation PDF). Embedded
verbatim in the Guide tab via `@@SPEC:GENOGRAM-GUIDE@@`.
- `gmiau-specs/GMIAU-STYLE-GUIDE.md`, shell scaffold, tabs, settings,
themed scrollbars, panel width (Β§4A), Guide-tab visibility (Β§2C).
- `gmiau-specs/PDF-OUTPUT-SPEC.md`, A4 margins, fonts, page setup.
- `gmiau-specs/GMIAU-ICONS-SPEC.md`, icon design.
- `gmiau-specs/EXPORT-SPEC.md`: export filename convention.
Source PDF: `gmiau-specs/source-docs/genogram-guide-07102022.pdf`.
---
## 1. Purpose
One tool, one diagram. The caseworker enters every relevant person
plus each pairwise relationship; the tool lays them out generationally
(top-down) and exports:
1. **A4 PDF** (landscape default) for a bundle exhibit, with the
canonical PDF protection pattern (PRIVATE & CONFIDENTIAL header,
optional DRAFT watermark), see Β§5.
2. **PNG** at 2Γ device pixels, white background, see Β§5.1.
3. **Interactive HTML SVG** in the Diagram tab (pan / zoom).
4. **Sidecar JSON** at
`<open_cases>/Case Summaries/<ref>.genogram.json`, see Β§6.
5. **Plain-prose paragraph** for pasting into a witness statement or
letter, see Β§7. It reads back in: Β§7.2.
The PNG and the PDF both carry a **key to the symbols the genogram
uses**, see Β§5.2. The PDF also carries the genogram **data** (Β§5.3),
optionally encrypted, so it can be reopened; and optionally the prose
paragraph as text pages (Β§5.4).
Entry is by hand or by pasting written text (Β§7.2). No GEDCOM, no
auto-import from case summary, no household boundary, no radial layout
(the v1.2 radial-layout stub is removed, top-down is the only laid-out
form and matches the convention).
Anything in `GENOGRAM-GUIDE.md` that is **not** implemented here is
listed in Β§11 as backlog.
---
## 2. Tabs
Per `GMIAU-STYLE-GUIDE.md` Β§2A (tab-btn style), Β§2C (Guide / Config
hideable toggles). Minimum-5 ordering. Primary tool tab
(`build`) is default-active per the 2026-05-13 reversal
(`feedback_guide_default_flip`).
| Order | Tab | id | Default-active | Hideable |
|-------|---------------|--------------|----------------|----------|
| 1 | Guide | `guide` | no | yes |
| 2 | Build | `build` | **yes** | no |
| 3 | Diagram | `diagram` | no | no |
| 4 | Export | `export` | no | no |
| 5 | Config | `config` | no | yes |
| 6 | Settings | `settings` | no | no |
| 7 | Index | (link) | n/a | no |
`window.IFYI_PRIMARY_TAB = 'build'`.
The **Guide tab** holds: a hand-written intro to using the tool, then a
`<details>`-wrapped `@@SPEC:GENOGRAM-GUIDE@@` block holding the
inlined social-work convention.
The **Settings tab** holds the canonical 6 toggles (Mode, Theme,
App-font Family, App-font Size, Hide Guide, Hide Config) plus a
`<details>`-wrapped `@@SPEC:GENOGRAM-SPEC@@` block holding this
spec.
---
## 3. Data model
### 3.1 Person
```ts
type Person = {
id: string; // 'p1', 'p2', β¦ (stable across a session)
role: 'client' // exactly one Person should have role='client'
| 'spouse'
| 'parent'
| 'child'
| 'sibling'
| 'grandparent'
| 'maternal-aunt' | 'paternal-aunt'
| 'maternal-uncle' | 'paternal-uncle'
| 'cousin'
| 'in-law'
| 'pet'
| 'other';
name: string;
// Symbol attributes
sex: 'M' | 'F' | 'NB' | '?'; // displayed shape (square/circle/D/?)
trans?: 'F-to-M' | 'M-to-F'; // renders nested inner shape
orientation?: 'gay' | 'lesbian'; // renders inverted-triangle inset
// Life status
status: 'alive' | 'deceased' | 'unborn' | 'abortion' | 'miscarriage'
| 'stillborn' | 'unknown';
dob?: string; // 'YYYY-MM-DD' | 'YYYY' | '', never required
dod?: string; // date of death (only if status='deceased')
edd?: string; // expected delivery date (only if status='unborn')
// Optional context
location?: string; // 'Kabul, Afghanistan' / 'UK' / etc.
keyCodes?: string; // free-text, space-or-comma separated: 'LD MH D'
// rendered next to (not inside) the symbol. See Β§3.4.
notes?: string; // free text, risk profile, witness availability, etc.
// Multiple births
multipleId?: string; // shared group id ('m1', 'm2', β¦), siblings with
// the same multipleId render as a fan-of-N descender
multipleKind?: 'identical' | 'fraternal'; // identical adds a horizontal bar
};
```
The `role` field is **descriptive labelling only**. Graph structure
comes from the Relationships list. Pets render as a small diamond per
the convention; everything else uses the Β§3.3 shape table.
### 3.2 Relationship
```ts
type Relationship =
| { type: 'couple'; a: PersonId; b: PersonId;
kind: 'married' | 'separated-legal' | 'separated-living-apart'
| 'divorced' | 'widowed' | 'cohabiting'
| 'cohabiting-separated' | 'civil-separated';
startDate?: string; // m.YYYY or m.DD.MM.YYYY for married/cohabiting
endDate?: string; // d.YYYY for divorced/widowed
}
| { type: 'parent'; parent: PersonId; child: PersonId;
kind: 'biological' | 'adopted' | 'foster';
}
| { type: 'sibling'; a: PersonId; b: PersonId; } // shorthand, see Β§3.5
| { type: 'overlay'; a: PersonId; b: PersonId;
kind: 'violence' | 'physical-abuse' | 'emotional-abuse'
| 'sexual-abuse' | 'hostile' | 'neglect'
| 'very-close' | 'close' | 'distant';
direction: 'a-to-b' | 'b-to-a' | 'both' | 'none';
// direction='none' is the only valid choice for very-close / close /
// distant (plain lines, no arrowhead). For the abuse + neglect
// kinds, direction must be 'a-to-b', 'b-to-a', or 'both'.
};
```
Note the v1.2 v2.0 changes:
- `spouse` + `partner` a single `couple` with a `kind` field.
- `adoptive-parent` / `step-parent` a single `parent` with a `kind`
field (`biological` / `adopted` / `foster`). "Step-parent" is not in
the social-work convention as a distinct line type; render a
step-parent as a `couple` edge plus a `parent` edge of kind
`adopted` where the step-relationship is legally formalised, else
leave the parent edge absent. (Backlog item: explicit step-parent
dashed-double-line.)
- New `overlay` edge type carries the abuse / closeness lines that
sit on top of the structural diagram.
### 3.3 Shape table (rendered)
| sex | trans | orientation | Renders |
|-----|------------|-------------|--------------------------------------------------|
| M |, |: | Square |
| F |: |: | Circle (oval) |
| NB |: |: | D-shape (Word's "Flowchart: Delay") |
| ? |: |: | A question mark inside a dashed square |
| M |, | gay | Square with inverted-triangle () inset |
| F |, | lesbian | Circle with inverted-triangle () inset |
| M | `F-to-M` |, | Square outer, circle inner (born-female) |
| F | `M-to-F` |, | Circle outer, square inner (born-male) |
A `status='unborn'` symbol is **always** a triangle regardless of sex.
A `status='deceased'` symbol carries a large `X` across whatever shape
the Β§3.3 table would otherwise draw. `status='abortion'` and
`status='miscarriage'` render an X-through small triangle.
`status='stillborn'` renders an X-through small square (M) or oval (F).
### 3.4 keyCodes
A free-text field. The tool does not validate against the
GUIDE Β§6 list, caseworkers should follow it but the convention is
self-documenting and stable extensions (e.g. `ASD`) are routine.
Rendered as a short line of text **above** the person's symbol, in the
shell's muted text colour. Comma-separated codes are accepted but
displayed verbatim.
### 3.5 Sibling shorthand
`{ type: 'sibling', a, b }` is shorthand for "share at least one
parent" when the user hasn't entered grandparent rows. The diagram
engine treats two people with at least one common `parent` edge as
siblings automatically, sibling rows are only needed when no common
parent has been entered.
### 3.6 Sidecar JSON shape
```json
{
"_v": 2,
"_tool": "genogram-generator",
"_exportedAt": "2026-05-15T14:21:00.000Z",
"case_ref": "RB12345",
"people": [ { "id": "p1", "role": "client", "name": "β¦", β¦ } ],
"relationships": [ { "type": "couple", "a": "p1", "b": "p2", β¦ }, β¦ ],
"options": {
"show_dob": true,
"show_location": true,
"show_status": true,
"show_keycodes": true,
"show_overlay": true
}
}
```
The `_v: 2` bump distinguishes Genogram sidecars from v1 Family Tree
sidecars (which had `_tool: "family-tree"`). v1 sidecars are not
auto-migrated in v2.0, the import will accept them but warn that
fields are missing.
---
## 4. Diagram engine
Top-down only. The v1.2 radial layout is removed.
### 4.1 Tier assignment
BFS from the client (tier 0). Each `parent` edge moves +1 toward the
parent (older), each `parent` edge from the other side moves β1
(younger). Couple partners share a tier.
### 4.2 Within-tier ordering
1. **Couples sit adjacent**: the male symbol is on the LEFT, the
female symbol on the RIGHT, joined by a horizontal couple-line.
Non-binary partners take the position dictated by the partner's sex
(NB M NB on the right; NB F NB on the left; NB NB
insertion order).
2. **Siblings cluster** and are sorted **DOB-ascending, left-to-right**
(oldest on the LEFT). Siblings without a DOB hold their insertion
order at the right end of the cluster.
3. **Stable order** otherwise, preserve insertion order when
underdetermined.
### 4.2a Sub-row wrapping (large families)
A tier only ever grows sideways, so a wide generation is fitted
width-first on A4 and most of the page height goes unused, which is
what made names in a large family unreadable. A tier is therefore
allowed to wrap into **stacked sub-rows**, spending that height to buy
back scale.
Wrapping is automatic and conservative:
- It is attempted **only** when the whole diagram would otherwise print
below **9 pt** on a single A4 sheet. At or above that, the strict
one-row-per-generation layout of Β§4.1 is kept untouched.
- Candidate sub-row widths are tried widest-first, and a narrower split
must beat the incumbent by **more than 3%** (and the unwrapped layout
by more than 5%) to be adopted. The layout closest to the convention
therefore wins ties.
- A **couple is never split** across sub-rows: partners travel as one
unit.
- A sibling group too wide for one sub-row is given **exclusive**
sub-rows, which keeps the margin to its left clear.
- The parent bus (Β§4.3) then draws **one bus per sub-row**, the lower
ones fed by a vertical drop-line running down that clear left margin.
Worked example: 14 siblings on a 92-unit pitch is 1260 units wide and
prints names at 5.6 pt. Wrapped to 2 sub-rows of 7 it is 616 Γ 369 and
prints at 10.8 pt.
### 4.3 Edges
- **Couple line**: horizontal, between the two symbols. The Β§4.6 table
governs stroke style (solid / dashed) and stroke decorations
(single slash, X, double slash, etc.).
- **Parentβchild line**: vertical line down from the midpoint of the
couple-line (or from a single parent's symbol) to a horizontal
*sibling bus*; from the bus, one vertical descender per child. The
Β§4.7 table governs stroke style (solid / dashed / solid+dashed).
- **Twins**: a single descender that fans out into N legs at the
bottom. `multipleKind='identical'` adds a horizontal bar joining
the legs.
- **Overlay edges**: drawn ON TOP of the structural diagram, after all
structural lines, with their own per-kind colour and dash pattern.
Overlay edges may bend around symbols (route via simple orthogonal
segments), straight lines are acceptable if no symbol is on the
direct path.
### 4.4 Symbol sizing and label fitting
Each symbol is 64 Γ 44 SVG units, on a column pitch of 92 (`BOX_W +
H_GAP`). Labels:
- **Name** on line 1 (under the symbol).
- **`b.YYYY-MM-DD`** on line 2 (replaces line 2 with `b.?` if absent),
with `d.YYYY-MM-DD` appended when deceased.
- **Location** on line 3 (hidden when `options.show_location=false`).
- **keyCodes** is rendered ABOVE the symbol, line 0, when present.
Every label is **measured and wrapped to 86 units** (the column pitch
less a 6-unit gutter), never cut at a fixed character count, a
character count cannot know how wide the text is, so it both truncated
readable names and let long ones overlap the next column. Widths are
measured against the export font (Helvetica / Arial) so screen and PDF
agree.
Those sizes are **SVG user units, not UI type**, and every one of them is
set in exactly one place: `LBL_SPEC`. Each label is drawn with an inline
`font-size` taken straight from the spec it was measured against, so no
stylesheet can move a label away from the width the fitter reserved for
it. The diagram CSS and `_EXPORT_SVG_CSS` are additionally wrapped in
`/* ifyi:fixed-px */ β¦ /* ifyi:/fixed-px */`, which tells `build-www.py`
to leave them out of its px-to-`var(--app-font-size)` rescale.
Without the fence the built site rescaled the location line from 8 to
`calc(var(--app-font-size) * 0.53)`: 10.6px on screen at the default base
of 20, and 16px in the exported SVG, where `--app-font-size` is not
defined at all and the `calc()` collapses to the inherited default. The
place of birth then printed larger than the person's name and ran over
the next column. Anything the tool both measures and prints belongs
inside the fence.
- Name wraps to at most **2 lines**, breaking on spaces.
- The `b.` / `d.` pair sits on one line when it fits, otherwise one
date per line. A date is never truncated.
- Location wraps to at most 2 lines; keyCodes stay on one.
- Only a single unbreakable word longer than the column is ellipsized.
- Row height is derived from the tallest label block in that row, so
rows are exactly as tall as their content needs.
Couple-line labels sit in the 28-unit gap between the two symbols, so
they are stacked one part per line and shrunk (8 px down to a 6 px
floor) to fit that gap rather than run across both symbols. They are
year-level (`m.YYYY`, `m.YYYY d.YYYY`), a full ISO date cannot be set
legibly in that gap, and the exact day is carried by the Prose tab.
### 4.4a Structural lines are routed around the text
A halo behind a label (Β§4.6b) stops a crossing line from striking a glyph out,
but on a document that goes to a tribunal a line through a place of birth still
reads as a mistake. The couple and parent-child lines are therefore routed
around the label blocks rather than over them. There is a 28-unit gutter
between columns and labels are capped at 86 on a 92 pitch, so a vertical always
has somewhere to go.
- Every label is measured into a band: the block under the symbol, and the line
above it. `_labelBands()` collects them; `_vClearX()` tests a vertical
against them.
- A family's bus sits **below** the parents' whole label block, not at the
bottom edge of their symbols, so the horizontal cannot cross a name, a date
or a place of birth.
- The stub from the parents down to that bus runs straight when its x is clear.
A couple's is the midpoint between two adjacent symbols, which is the gutter,
so it always is. A single parent's is the centre of their own label block, so
it is not: the stub leaves along the bottom edge of the symbol, which is above
all text, into whichever gutter is nearer the children, and descends there.
- Sub-row and sibling drop lines run down the gutter centre (`H_GAP / 2` left of
the leftmost box) and step a further column left if that is occupied. A
sibling bus sits above whatever the row carries above its boxes, not at a
fixed 14 units, which a two-line place name grew past.
- What sits above a symbol is held off its centre: key codes end 3 units left of
it, the place of birth (when set above) starts 3 units right. Each side may
use half the label width plus half the column gap (`SIDE_W`), and the place
is allowed two lines, stacked upward. They are measured as **two** bands, one
per side, never one union: a single band would cover the centre, which is the
whole point of holding them apart. That empty centre is what lets a descender
reach the top of the box without crossing a word, and is why the descender now
meets the symbol instead of stopping short above the key codes.
Overlay edges (Β§4.8) are **not** routed. They are point-to-point marks about a
relationship rather than lines joining family members, and they keep the halo.
`Straight through` in Config restores the older behaviour in full: no bands are
built, the bus returns to the bottom edge of the symbols, and every line is
drawn direct.
### 4.4b Where the place of birth goes
Two positions, both defensible, chosen in Config:
- **Under the name** (default) is the layout in the social-work guide: name,
then `b.` / `d.`, then the place, stacked under the symbol. This is what a
Local Authority reader expects.
- **Above the symbol** follows the Standard Genogram Format (McGoldrick, 2023),
which sets the place apart from the name in its own colour rather than
stacking it with the dates. It is drawn above the symbol and right of centre,
in slate (`#2a6b8a` in print), with any key codes to the left of centre on
the same line, capped at half the label width.
### 4.5 Deceased / abortion / miscarriage / stillborn
X is stroked at 1.5Γ the regular symbol stroke weight, across the
full extent of whatever shape the symbol uses, corner to corner.
### 4.6 Couple-line variants
| `kind` | Stroke | Decoration | Label format |
|--------------------------|----------|-------------------------------------|---------------------|
| `married` | solid | none | `m.MM.YYYY` |
| `separated-legal` | solid | single `/` slash through the line | (start date if any) |
| `separated-living-apart` | solid | `Γ` cross through the line | (start date if any) |
| `divorced` | solid | double `//` slashes | `m.YYYY d.YYYY` |
| `widowed` | solid | `X` on the line | `m.YYYY d.YYYY` |
| `cohabiting` | dashed | none | start date |
| `cohabiting-separated` | dashed | single `/` slash | start date |
| `civil-separated` | dashed | `/` slash | `YYYY-YYYY` range |
### 4.7 Parent-child variants
| `kind` | Stroke |
|---------------|------------------------------------------------|
| `biological` | solid |
| `adopted` | one solid descender + one dashed descender |
| `foster` | dashed |
### 4.8 Overlay variants
| `kind` | Colour | Stroke | Arrow |
|------------------|--------------|---------------------|---------------|
| `violence` | red | wavy + solid base | yes |
| `physical-abuse` | blue | wavy + solid base | yes |
| `emotional-abuse`| light green | wavy + solid base | yes |
| `sexual-abuse` | purple | DOUBLE wavy | yes |
| `hostile` | light blue | wavy + solid base | yes |
| `neglect` | black | dashed | yes |
| `very-close` | black | DOUBLE solid | no |
| `close` | black | solid | no |
| `distant` | black | dashed | no |
`direction='both'` renders a double-headed arrow.
### 4.6a Couple labels and decorations are placed clear of symbols
A second spouse is laid out beside the first, not either side of the client,
so that couple's line spans **across** the intervening symbol. Symbols are
drawn after the couple edges, so anything placed at the line's midpoint ends
up behind a white shape: on 2026-08-29 an `m.1977` was printing as a clipped
smudge above a circle. `_clearSpan()` therefore finds the widest part of the
line that no symbol covers, and both the label and the stroke decoration are
placed there. The label is sized to that gap, not to the whole span.
### 4.6b Every label carries a white halo
`paint-order: stroke fill` with a 2.4px stroke in the surface colour, on
every node and couple label. A structural or overlay edge crossing a label
then passes visually behind the text instead of striking it out, a
`very-close` overlay was ruling through a client's mother's name, place of
birth and date of birth all at once. This is cheaper and more reliable than
routing every edge around every label, and it covers couple lines, parent
buses and overlays alike. It is in both the in-app CSS and
`_EXPORT_SVG_CSS`, so screen and print agree.
The halo only works if the text paints last, and the overlay pass runs after
the labels, so every `<text>` is re-appended to the end of the SVG once the
overlays are drawn. Without that an abuse or closeness arrow drew straight over
a name with the halo underneath it, doing nothing. Structural edges are routed
around the labels instead (Β§4.4a); the halo is what covers the overlays, which
are point-to-point marks about a relationship rather than lines joining family
members and so are deliberately left direct.
### 4.9 Pan / zoom
Native SVG via `viewBox` mutation. Mouse-wheel zooms, click-drag pans,
double-click resets. Touch support is not required in v2.0.
---
## 5. PDF export
Per `ref_ifyi_pdf_protection` and `feedback_private_and_confidential_glyph`:
- **Page**: A4 (297 Γ 210 mm). Orientation is chosen at export time, the Export tab has two buttons, ** Landscape PDF** and ** Portrait
PDF**. No Config toggle (removed in v2.0.1).
- **Fit to page**: the exporter always uses the FULL content bbox
(via `SVGSVGElement.getBBox()`), independent of whatever pan / zoom
the user has left in the Diagram tab. Aspect-preserving fit centres
the diagram between the header and footer with 12 mm horizontal
margins.
- **Readability floor and multi-sheet spill**: names are held at a
minimum of **8 pt** (raised from 6 pt on 2026-08-29: the exhibit is a
legal document, read on paper and often photocopied, so the floor is set
where it survives that rather than at the smallest legible size). If the diagram cannot fit one sheet at that
size, it is **tiled across up to 9 A4 sheets** rather than shrunk
further. Tiles are spread evenly over the content, so neighbouring
sheets overlap slightly and the far edge is covered exactly. Past 9
sheets the exporter falls back to a single fitted sheet. Each sheet
repeats the header and is titled `β¦ (sheet N of M)`; the footer
numbers them `Page N of M`.
- **Orientation hint**: because Β§4.2a optimises the layout for
whichever orientation suits the diagram, the Export tab shows the
sheet count and resulting name size for BOTH orientations, marking
the better one as recommended.
- **Header**: `PRIVATE & CONFIDENTIAL` (ampersand, never "AND") centred
at the top, ~12 pt Helvetica bold (EB Garamond when inlined, see
Β§11).
- **Watermark**: `DRAFT` diagonal stamp, light grey, 25% opacity
(Config toggle, off by default).
- **Footer band**: case ref, date generated, page number. No GMIAU
logo on the genogram page itself.
- **Encryption**: deferred. v2.0 ships an unencrypted PDF; pipe
through `pdf-encrypt.html` (qpdf-wasm AES-256) if the bundle
requires it.
- **Filename**: `YYMMDD CLIENTREF SEQ Genogram.pdf` per
`EXPORT-SPEC.md Β§1` (single-space separator), built via the
canonical `_exportName()` helper inlined in the tool.
### 5.2 Key to symbols
A genogram is only readable by someone who already knows the convention, and
a tribunal panel does not. Every PNG and PDF therefore carries a key.
- **Only what is used.** The key is built by walking the entered people and
relationships: a plain family tree of four gets four entries, a safeguarding
genogram gets the couple-line variants, the overlay kinds and the multiple
birth it actually contains. It is never the whole vocabulary.
- **Drawn by the diagram's own code.** Each vignette calls `_drawSymbol`,
`_drawCoupleLine` or `_drawOverlay`, so a key entry cannot drift away from
the symbol it explains. The parent-child entry draws the sibling bus and a
descender, not a bare horizontal line, which would read as a couple line.
- **Layout.** Rows of `vignette | label`, at most 8 rows per column and at
most 3 columns, in a bordered block titled `Key to symbols`. A closing
italic line, `An overlay arrow points at the person on the receiving end.`,
appears only when a directional overlay is present.
- **PNG**: composed under the diagram in the same image, centred, with a
10 px gutter at 1Γ (`_stackPng`).
- **PDF**: the diagram is fitted to the **whole page first**, and the key
only shares that sheet when doing so shrinks the diagram by no more than
5% (`KEY_MAX_COST`). Otherwise it takes its own final page, headed
`<title>, key to symbols` and numbered in the same `Page N of M`
sequence. **A second sheet is the normal outcome, and that is correct**:
reserving the key's space before fitting was tried on 2026-08-29 and
reverted the same day, because it bought a one-sheet exhibit by dropping
the names from 15.3 pt to 9.8 pt. On a legal document the size of the
names beats the page count (RB).
- **Scale**: 0.3 mm per SVG unit, about 8 pt labels, when it shares a sheet.
On its own page it is scaled up to fill the sheet (capped at 2.2Γ), and
the column count is chosen to maximise that scale: one tall column
usually wins, because a page has far more height going spare than width.
- **Source line**: the key closes with
`Symbols follow the standard social-work genogram convention (Genogram
Explanation, 7 October 2022).` A reader of the bundle is entitled to know
which standard the symbols follow, and the convention is not this tool's
invention. Above it, when a directional overlay is present, sits
`An overlay arrow points at the person on the receiving end.`
- Config toggle `ifyi_gg_show_key`, default on.
### 5.3 The genogram data inside the PDF
A PDF exported before 2026-08-29 carries **nothing but a picture**: the
diagram is one raster image at 150β200 dpi and the only real text is the
`PRIVATE & CONFIDENTIAL` header, the `Genogram: <client>` title and the
footer band. No metadata, no attachments. A family cannot be read back out
of one, and no amount of work on the importer will change that.
From 2.3 the sidecar travels with the file, as a **standard PDF embedded
file attachment** (`/Names /EmbeddedFiles` in the catalog), named
`<ref>.genogram.json`. Acrobat lists it in its attachments pane, `pdfdetach`
extracts it, and pdf.js `getAttachments()` reads it back.
- **What is in it**: the **whole** sidecar, people, notes, key codes and the
abuse / closeness overlay edges, whether or not the diagram is currently
drawing them. Decided by RB on 2026-08-29, so a PDF always reopens as
exactly the genogram it came from.
- **What that means**: the file carries safeguarding metadata that is not
visible on the page, and anyone holding it can extract it. Config toggle
`ifyi_gg_embed` (default on) turns it off for an exhibit that should be a
picture only.
- **Encryption** (`ifyi_gg_embed_encrypt`, default off): AES-GCM-256 with a
PBKDF2-SHA256 key at 210,000 iterations, 16-byte salt, 12-byte IV, all
base64 in a self-describing envelope. The attachment is then named
`<ref>.genogram.encrypted.json`. WebCrypto only, no library; `file://`
counts as a secure context so it works in the offline twin. The
passphrase is typed on the Export tab and is **never persisted**, it is
a secret, not a setting: and cannot be recovered: lose it and the data
in that PDF is gone.
- **Implementation note**: jsPDF 2.5.1 has no attachment API. It publishes
`putCatalog` from *inside* the catalog dictionary, which is exactly where
`/Names` belongs, and `postPutResources` before it, where the stream and
Filespec objects can be written. Those two hooks are all this needs. The
payload is forced to pure ASCII so its byte length equals its string
length and the stream needs no encoding declaration.
Reading one back is Β§6.
### 5.4 The paragraph in the PDF
Config toggle `ifyi_gg_pdf_prose`, default **off**. When on, the Β§7
plain-prose paragraph is added as its own page or pages after the diagram
and before the key, headed `<title>, the family in words`.
It is set as **real text** at 11 pt on a 5.6 mm lead, not as an image, so it
can be selected, searched and copied out of the bundle. It is laid out
before the page count is fixed, so the footers number every sheet correctly,
and it runs to as many pages as the family needs.
### 5.1 PNG export
- **Resolution**: 2Γ CSS-px scale of the current SVG viewBox.
- **Inline CSS**: the tool clones the live SVG and injects a fixed
light palette before serialising (`_EXPORT_SVG_CSS` constant) so
the export is independent of the in-app theme.
- **Filename**: `YYMMDD CLIENTREF SEQ Genogram.png`.
- **Library**: none, pure DOM (`new Image(); canvas.toBlob`).
---
## 6. Sidecar JSON I/O
Write-back target:
```
<open_cases>/Case Summaries/<ref>.genogram.json
```
`<open_cases>` resolves from the One File config (`paths.openCases`)
when loaded, else falls back to `~/Work/00 Open Cases/`. The
case-summary JSON itself is **untouched**.
Import in the Build tab's ` Import sidecar or PDF` button reads that path
(via `<input type=file>` since FSA is disabled on `file://`). On
save, the tool offers a download with the canonical filename.
**It also accepts a PDF.** If the PDF carries an embedded sidecar (Β§5.3) it
is loaded, decrypting first where the envelope says so, a wrong passphrase
is refused with a plain message and nothing is imported. If it carries no
payload, the tool does **not** fail silently: it reads the first page's text
and offers the only two facts a data-less genogram PDF contains, the client
name from the `Genogram: <client>` title and the case ref from the footer,
so the caseworker gets a started genogram rather than a dead end. A PDF this
tool did not write is rejected outright.
Both routes share `applySidecarObject()`, so a JSON sidecar and a PDF behave
identically once the object is in hand.
**v1 Family Tree sidecars** (`<ref>.family-tree.json`, `_v: 1`) are
accepted on import; missing v2 fields are filled with defaults
(`sex: '?'`, `status: 'alive'`, `couple.kind: 'married'`,
`parent.kind: 'biological'`). The tool surfaces a one-time banner
when a v1 sidecar is imported so the caseworker knows to revisit the
new fields.
---
## 7. Plain-prose generator
Generates a single paragraph by walking the graph from the client
outwards. Sentence templates:
| Edge type from client | Sentence |
|------------------------------------------------|---------------------------------------------------------------------------|
| Couple, married, alive | "{X} is the {client}'s {wife\|husband}, born {dob} in {location}." |
| Couple, divorced | "{X} is the {client}'s ex-{wife\|husband}, married {m} and divorced {d}." |
| Couple, cohabiting | "{X} is the {client}'s partner, in a relationship since {start}." |
| Parent (mother / father by sex) | "{X} is the {client}'s {mother\|father}, born {dob} in {location}." |
| Parent, deceased | "{X} was the {client}'s {mother\|father}, born {dob} and died {dod}." |
| Parent, adopted | "{X} adopted the {client}, β¦" |
| Parent, foster | "{X} is the {client}'s foster {mother\|father}, β¦" |
| Child (biological) | "{X} is the {client}'s {daughter\|son\|child}, born {dob}." |
| Sibling | "{X} is the {client}'s {sister\|brother\|sibling}, born {dob}." |
| Aunt/uncle (derived: sibling of a parent) | "{X} is the {client}'s {maternal\|paternal} {aunt\|uncle}, β¦" |
Order: couple parents children siblings aunts/uncles
grandparents others. Punctuation uses commas + ` and ` (no Oxford
comma, UK style). Each person is emitted at most once.
Overlay edges are NOT emitted in prose (they're confidential
safeguarding metadata, not biographical fact).
### 7.2 Reading text back in
The Build tab's **Build from written text** panel is the inverse of the two
things this tool prints, so both round trip. Either shape may be pasted, and
they may be mixed in one go; they merge on the person's name.
**1. Prose sentences**, the Β§7 output shape:
```
{Name} {is|was} {Client}'s {relationship}[, {clauses}].
```
Clauses are found by keyword, longest first: `in a relationship since`,
`who died`, `born`, `died`, `married`, `divorced`, `separated since`, `in`.
They are NOT split on commas or on ` and `, because `born 1912 in Trinidad
and Tobago and died 2000` has to survive intact.
`was` means the person has died. `in Unknown Location` (and `Unknown`,
`Not known`, `No fixed abode`, `N/A`) is read as no location.
**2. Relationship rows**, what `_relSummary()` + `_relMeta()` print in the
Build tab list:
```
Anna βΆ biological parent of βΆ Maxwell
Anna β· separated-living-apart β· Hassan
start 1996 Β· end 1998
Anna hostile Hassan
```
The ASCII a keyboard produces (`->`, `<->`, `<-`, `--`) is accepted for each
glyph. A `start β¦ Β· end β¦` line applies to the row above it. Rows connect any
two people, not only the client, and carry the exact edge `kind`, which a
sentence cannot express. The overlay arrow gives the `direction`; the three
closeness kinds are forced to `direction: 'none'` per Β§3.2 whatever was typed.
**The client** is the name in the possessive, most often. With rows alone
there is no possessive, so it is the most connected person. Either way the
choice is reported.
**Sex comes from the relationship noun** (`mother` F), never from a name: a
name is not evidence of sex and a wrong shape on a tribunal exhibit is worse
than an admitted unknown. The single exception is a bare placeholder name
(`Mother`, `Unknown Father`, `Aunt`), which is a role word standing in for a
name; each one is listed in the report.
**Derived structure.** An aunt or uncle becomes a sibling of the named parent
on that side, and a grandparent a parent of it; where that parent has not
been entered, an `Unknown Mother` / `Unknown Father` placeholder is created
and reported. A grandparent with no maternal / paternal side is left
unlinked rather than attached to a guess. Children of the client sharing a
full `YYYY-MM-DD` date of birth are given a shared `multipleId` and
`multipleKind: 'fraternal'`, identical is not something the words can
evidence.
**Nothing is dropped in silence.** The panel reports what was built, every
inference (placeholder parents, twins, placeholder-name sexes, dropped
duplicate edges) and every line that could not be used, with the line quoted.
Two live couple edges on the client are flagged rather than silently
converted to a divorce.
Applying a parse **replaces** the current people and relationships, behind a
confirm. Merging a re-paste into an edited diagram would double every person
whose name had been tidied up in between.
The reading rules are pinned by `tests/test_genogram_parse.py`, which
extracts the block from this tool and runs `tests/genogram-parse.test.mjs`
against it, so the test reads the shipped code rather than a copy.
### 7.1 Placeholder convention (form inputs)
Per `REFERENCES-SPEC.md Β§104-105`, form placeholders use the canonical
example strings directly (no `e.g.` prefix). Seeded placeholders:
person name **`David Pountney`**, location **`Manchester, UK`**, case
ref **`RB12345`**.
---
## 8. Settings (canonical 6)
The tool inherits the canonical 6 toggles unchanged (Mode, Theme,
App-font Family, App-font Size, Hide Guide, Hide Config). No tool-local
matter-toggles in the Settings tab.
The Settings tab also includes a `<details>` block with the inlined
`@@SPEC:GENOGRAM-SPEC@@` (this file) for in-tool reference.
---
## 9. Config (per-tool)
| Field | Default | Notes |
|--------------------------------|--------------|------------------------------------------------------------------------|
| Show DOB | on | matter-toggle |
| Show place of birth | on | " (`ifyi_gg_show_loc`) |
| Place of birth position | under | matter-toggle: Under the name / Above the symbol (`ifyi_gg_loc_placement`). See Β§4.4b. |
| Place of birth weight | muted | matter-toggle: Match the print / Faint (`ifyi_gg_loc_tone`). Muted is `--text-muted` on screen, `#555` in print; faint is the older `--text-faint` / `#777`. |
| Family lines | clear | matter-toggle: Route around the text / Straight through (`ifyi_gg_edge_routing`). See Β§4.4a. |
| Show status | on | controls whether status markers (`X`, triangle for unborn) render |
| Show keyCodes | on | " |
| Show overlay | on | hide the abuse / closeness overlay for non-safeguarding exhibits |
| Key to symbols on the export | on | matter-toggle (`ifyi_gg_show_key`). See Β§5.2. |
| Include the written paragraph | off | matter-toggle (`ifyi_gg_pdf_prose`). See Β§5.4. |
| Genogram data inside the PDF | on | matter-toggle (`ifyi_gg_embed`). See Β§5.3. |
| Encrypt that data | off | matter-toggle (`ifyi_gg_embed_encrypt`). Passphrase is on the Export tab, never persisted. |
| DRAFT watermark | off | matter-toggle: On / Off (`ifyi_gg_pdf_draft`). Orientation lives on the Export tab, no Config toggle. |
All persist to `localStorage` under namespaced keys `ifyi_gg_<setting>`.
---
## 10. File layout
```
immigrationfyi-tools/source/genogram-generator.html edit here
immigrationfyi-tools/offline/genogram-generator-offline.html regen by build-all-offline.py
gmiau-specs/GENOGRAM-SPEC.md this file
gmiau-specs/GENOGRAM-GUIDE.md social-work convention, inlined in Guide tab
gmiau-specs/source-docs/genogram-guide-07102022.pdf original PDF
gmiau-specs/icons/genogram-generator.{svg,png} icon (frame + glyph)
```
Vault docs:
```
~/Notes/Resources/Cheatsheets/Genogram Generator Cheatsheet.md
~/Notes/Resources/Reference/Genogram Generator Reference.md
```
GitHub Pages mirror (Filer / Bundle Builder / Evidence Exhibitor /
Auditor companion): `ryanbestford.github.io/tools/genogram-generator.html`,
encrypted via `~/Projects/rjb/tools/publish.fish`.
---
## 11. Backlog (deferred from v2.0)
- **Household boundary**, freeform loop drawn around co-resident
symbols (GUIDE Β§5). Postponed because freeform-path UX in SVG is
awkward; a rectangular bounding-box approximation may land in v2.1.
- **Step-parent line**, distinct dashed-double-line style.
- **EB Garamond** inline for PDF body text (currently Helvetica).
- **qpdf-wasm AES-256** PDF encryption (pipe through `pdf-encrypt.html`
in the meantime).
- **Auto-import from `gmiau-case-summary/1`**, read parents, spouse,
children from the case-summary JSON to pre-fill the Build tab.
- **GEDCOM import / export**, unsupported; would need its own spec.
---
## 12. Versioning
- **2.3 Β· 2026-08-29**, **The PDF carries its own data** (Β§5.3): the full
sidecar as a standard embedded file attachment, optionally AES-256
encrypted with a passphrase, so an exported PDF reopens in the tool.
Import (Β§6) now takes a PDF as well as a JSON sidecar, and tells the
caseworker plainly when a PDF predates this and carries only a picture,
offering the client name and case ref it can still read. **Legibility**,
this being a legal document: the name floor is raised to 8 pt, the key no
longer shrinks the diagram to share a sheet (Β§5.2), a key alone on a page
is scaled up to fill it, couple labels and decorations are placed clear of
symbols (Β§4.6a), and every label carries a white halo so no edge can rule
through it (Β§4.6b). The key now cites its source. New: the prose paragraph
as text pages in the PDF (Β§5.4). Adds pdf.js 3.11.174, which the offline
builder inlines along with its worker; the twin grows from 0.6 MB to
2.3 MB.
- **2.2 Β· 2026-08-29**, **Build from written text** (Β§7.2): the Build tab
reads back the Export tab's prose paragraph and the Build tab's own
relationship rows, mixed, and builds the People and Relationships from
them, reporting every inference and every unused line. **Key to symbols**
(Β§5.2) on every PNG and PDF, listing only the symbols the genogram
actually uses; on the PDF, room for it is reserved before the diagram is
fitted, so a one-sheet exhibit stays one sheet. New Config toggle
`ifyi_gg_show_key` (default on). Parser rules pinned by
`tests/test_genogram_parse.py`.
- **2.1 Β· 2026-05-15**, Render-mode toggle added to the Config tab: **Simple (family tree)** vs **Genogram (procedure)**, persisted to `localStorage.ifyi_gg_render_mode` (default: `genogram`). Simple mode renders rounded rectangles for every person regardless of sex / trans / orientation, draws plain solid couple / parent lines (no per-kind decorations or dashing), uses the original diagonal-slash for deceased (no X-across-shape), and skips key-codes + overlay edges entirely. Couple labels condense to `m.YYYY Β· div.YYYY`. Export filenames + PDF title switch to `Family Tree`. Data model is unchanged, switching modes never loses entered data; sidecar JSON does not store the mode (Settings-level preference per [[ref_ifyi_one_file_config]]-style stickiness, not per-case).
- **2.0.1 Β· 2026-05-15**, Export-tab PDF buttons split into ** Landscape PDF** + ** Portrait PDF** (orientation chosen at export, not via Config). Export now uses `SVGSVGElement.getBBox()` to fit the FULL content (the previous version inherited the user's pan / zoom, which clipped the leftmost column in `RB12345.genogram.json`). Diagram-tab **Fit** button and `dblclick` now call `fitToContent()` directly (no full re-render).
- **2.0 Β· 2026-05-15**, Genogram Generator. Renamed from Family Tree;
conventions adopted from `gmiau-specs/source-docs/genogram-guide-07102022.pdf`.
Visual core (shapes by sex, deceased X, twins/triplets, trans
nesting, gay/lesbian inverted-triangle) + couple-line variants
(married/separated/divorced/widowed/cohabiting/civil-separated) +
parent-child line types (biological/adopted/foster) + male-left /
oldest-left layout + keyCodes field + emotional-relationship
overlay (7 kinds Γ 4 directions). Sidecar `_v` bumps to 2; v1
sidecars are accepted on import with defaults filled.
Prior versions of the predecessor tool (Family Tree v1.0 / v1.1 / v1.2,
2026-05-13 2026-05-14) are archived in
`gmiau-backup/genogram-rename-2026-05-15/` for reference.