Case Study  ·  Document Architecture  ·  Special Education

Production Infrastructure
for Special Education
Publishing

A client needed 100 phonics workbooks for children with learning disabilities by September. Before a single page could go to press, the production system had to be engineered from scratch — InDesign style taxonomies, GREP automation, and VBA normalization scripts working in sequence.

Role

Document Architect

Tools

InDesign · Word · VBA

Scale

400+ pieces

Typeface

FS Me

Context

100 workbooks. One quarter. Zero margin for manual error.

The brief was straightforward on the surface: produce a series of phonics workbooks for children with dyslexia and other learning disabilities, ready for September delivery. Content would arrive as Word documents — some converted from PDF, others drafted directly — and the final output would be print-ready InDesign files, consistently typeset across every piece.

What the brief didn't account for was the scope of what "consistent" actually requires at volume. With 100+ workbooks, any manual formatting decision made once becomes a decision that has to be made 100 times. A single style applied by hand to 400 pages is 400 opportunities for deviation. The only viable approach was to make the document do the work — to build a system so precise that correct output was the path of least resistance.

On scope

The original brief called for 100 workbooks. The actual content inventory, once reviewed, exceeded 400 individual pieces. Architecting for the stated scope would have left the production pipeline unable to absorb the real one. The infrastructure had to be built for the actual scale, even before that scale was formally acknowledged.

400+
Actual pieces in content inventory
35+
Named paragraph styles in taxonomy
20+
Character styles, including dyslexia-aware variants
3
Automation layers: VBA → JSX → GREP

Typography Decision

The typeface was a clinical choice, not an aesthetic one.

Workbooks for children with dyslexia are often set in typefaces designed specifically for the diagnosis — rounded, friendly, and immediately legible as "special needs" material. The problem with most of these faces is that they read as childish to the people using them. A ten-year-old who already knows she reads differently does not need a font that announces it.

FS Me, designed by Fontsmith in consultation with Mencap, solves this differently. It is a serious, professionally-proportioned typeface that happens to have been engineered for cognitive accessibility. Every character is drawn to be unambiguous: the lowercase b and d are distinct enough that a child prone to mirroring cannot confuse them. The same applies to p and q, n and u. No two letterforms are mirror images of each other.

FS Me is what you use when you want to respect the reader — not signal that you've made accommodations for them.

This matters for the production system because the typeface decision propagates through every style in the taxonomy. Character styles like CS_AbcPrint_Dotted and CS_AbcPrint_Arrow_Lined — which drive the actual tracing letter rendering — depend on a base font that maintains legibility even at the large display sizes used in tracing exercises. FS Me holds at every scale.

System Architecture

Three automation layers, working in sequence.

The production pipeline moves content through three distinct transformation stages before a page is ever typeset. Each layer handles a specific class of problem — document normalization, structural mapping, and character-level formatting — so that no stage is burdened with work that belongs to another.

Layer 1
Word / VBA
ConvertAllTextContainersToTables() Shape → Table normalization Legacy frame removal PDF-to-Word artifact cleanup
Layer 2
InDesign / JSX
Extract_Metadata.jsx Style-driven text detection Anchored frame creation Batch-safe backward iteration
Layer 3
GREP Styles
CS_Grid_Lines → \t CS_Grid_Invisible → (?<=^)\t|(?<=^\p{L})\t CS_AbcPrint_Solid → ^\p{L} Zero manual intervention

The key design decision is that each layer outputs content the next layer can consume without inspection. A Word document that passes through the VBA normalization script arrives in InDesign without floating text boxes, legacy frames, or conversion artifacts. The JSX script can then handle structural repositioning before GREP handles character-level automation. By the time a human touches the document, it is already correctly styled.

Style System

A naming taxonomy built for a team that doesn't exist yet.

Every style in the system follows a strict naming convention. Paragraph styles use a prefix that identifies their content function: H1_ through H4_ for headings, Body_ for body content variants, Master_ for running headers and footers. Character styles use CS_ throughout. Object styles follow Object_.

This isn't organizational preference — it's forward planning. A style system that reads as arbitrary to the person who built it becomes completely opaque to the next person who inherits it. Explicit taxonomy means any production assistant, at any point, can identify what a style does from its name alone without opening the style options panel.

Style preview — InDesign quick apply strip

InDesign style preview strip
InDesign Paragraph Styles panel — 35+ named styles
Paragraph Styles35+ styles · Body_ / H1–H4_ / Master_ taxonomy
InDesign Character Styles panel — 20+ CS_ prefixed styles
Character Styles20+ styles · CS_ prefix · accessibility variants
InDesign Object Styles panel
Object StylesContainer and callout system for instructional modules
InDesign GREP Style options — Body_Tracing_Practice_Singles
GREP Styles — Body_Tracing_Practice_SinglesCS_Grid_Lines / CS_Grid_Invisible / CS_AbcPrint_Solid embedded in paragraph style

The GREP styles embedded in Body_Tracing_Practice_Singles illustrate the system's core logic. Three character styles are applied automatically based on the position of content within each cell — tab characters become grid lines, the first letter of each cell receives the solid tracing letterform, and invisible grid characters are hidden at line starts. A content editor types the letter. The system formats it correctly.

Automation — Word / VBA

Layer 1: normalizing the intake document.

Content arriving as Word documents — particularly those converted from PDF — carries a predictable class of artifacts: floating text boxes, legacy frames, shapes with text, all of which exist outside Word's normal document flow and cannot be reliably mapped to InDesign styles on import. Before the InDesign pipeline could process any file, these anomalies had to be normalized.

The VBA module iterates backwards through every shape and legacy frame in the active document, extracts the text content, replaces the floating container with a properly anchored 1×1 table, and deletes the original. The result is a document with no floating content — only proper table cells and inline text the InDesign import script can process reliably.

ConvertAllTextContainersToTables.bas
Sub ConvertAllTextContainersToTables() Dim doc As Document Dim shp As Shape Dim frm As Frame Dim rng As Range Dim tbl As Table Dim txt As String Dim i As Long Set doc = ActiveDocument Application.ScreenUpdating = False ' 1. Convert Text Boxes and Shapes with Text For i = doc.Shapes.Count To 1 Step -1 Set shp = doc.Shapes(i) On Error Resume Next If shp.TextFrame.HasText Then txt = shp.TextFrame.TextRange.Text If Len(txt) > 1 Then txt = Left(txt, Len(txt) - 1) Set rng = shp.Anchor Set tbl = doc.Tables.Add(Range:=rng, NumRows:=1, NumColumns:=1) tbl.Borders.Enable = True tbl.Cell(1, 1).Range.Text = txt shp.Delete End If On Error GoTo 0 Next i ' 2. Convert Legacy Frames (PDF-to-Word conversions) For i = doc.Frames.Count To 1 Step -1 Set frm = doc.Frames(i) txt = frm.Range.Text If Len(txt) > 1 Then txt = Left(txt, Len(txt) - 1) Set rng = frm.Range rng.Collapse Direction:=wdCollapseStart Set tbl = doc.Tables.Add(Range:=rng, NumRows:=1, NumColumns:=1) tbl.Borders.Enable = True tbl.Cell(1, 1).Range.Text = txt frm.Delete Next i Application.ScreenUpdating = True MsgBox "Conversion complete! Please review your document's layout.", vbInformation End Sub

The loop iterates backwards (from Count to 1) because deleting items while iterating forwards causes index shifting that silently skips elements. The trailing paragraph mark removal handles a Word quirk: every shape's text content carries a hidden paragraph character that would otherwise appear as a spurious line break inside the replacement table cell.

Automation — InDesign / JSX

Layer 2: repositioning lesson metadata from flow to anchored frame.

In the workbook layout, each lesson carries a block of metadata — lesson number, subject area, objective tags — that appears above the lesson heading in the source document but needs to be displayed as a right-aligned callout box anchored to the heading in the final layout. Placing this manually across 400+ pieces would be untenable. The JSX script does it automatically, driven entirely by the paragraph styles already applied in Layer 1.

The script searches the document for every paragraph styled H1_Lesson_Number, looks at the paragraphs immediately above it, and if they carry the H1_Metadata style, captures the full contiguous block. It then creates an anchored text frame at the lesson heading's insertion point, moves the metadata text into it, positions it right-aligned with a custom Y-offset, applies the Object_Metadata_Box object style, and auto-fits the frame to its content.

Extract_Metadata.jsx
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Anchor Metadata to Lesson"); function main() { var doc = app.activeDocument; // 1. Define your exact styles var lessonStyle = doc.paragraphStyles.itemByName("H1_Lesson_Number"); var metaStyle = doc.paragraphStyles.itemByName("H1_Metadata"); var objStyle = doc.objectStyles.itemByName("Object_Metadata_Box"); // Safety checks — fail loudly, fail early if (!lessonStyle.isValid || !metaStyle.isValid) { alert("Error: Check paragraph style names."); return; } // 2. Find all lesson number paragraphs app.findTextPreferences = NothingEnum.nothing; app.findTextPreferences.appliedParagraphStyle = lessonStyle; var lessonHeaders = doc.findText(); // 3. Work backwards — safe to delete/move without index drift for (var i = lessonHeaders.length - 1; i >= 0; i--) { var lessonHead = lessonHeaders[i]; var story = lessonHead.parentStory; var prevParaIdx = lessonHead.paragraphs[0].index - 1; if (prevParaIdx >= 0) { var prevPara = story.characters[prevParaIdx].paragraphs[0]; if (prevPara.appliedParagraphStyle == metaStyle) { // Walk upward to find the full contiguous metadata block var firstMeta = prevPara; while (firstMeta.index > 0) { var test = story.characters[firstMeta.index - 1].paragraphs[0]; if (test.appliedParagraphStyle == metaStyle) { firstMeta = test; } else { break; } } // Capture the full block as a single text range var textToMove = story.texts.itemByRange( firstMeta.insertionPoints[0], prevPara.insertionPoints[-1] ); // Create anchored frame at the lesson heading insertion point var anchorPt = lessonHead.insertionPoints[0]; var newFrame = anchorPt.textFrames.add(); // Move metadata into the new frame, position and style it textToMove.move(LocationOptions.AFTER, newFrame.insertionPoints[0]); with(newFrame.anchoredObjectSettings) { anchoredPosition = AnchorPosition.CUSTOM_ANCHORED_OBJECT; horizontalReferencePoint = AnchoredRelativeTo.TEXT_FRAME; horizontalAlignment = HorizontalAlignment.RIGHT_ALIGN; verticalReferencePoint = VerticallyRelativeTo.LINE_BASELINE; anchorXoffset = 0; anchorYoffset = -15; pinPosition = false; } if (objStyle.isValid) newFrame.appliedObjectStyle = objStyle; newFrame.fit(FitOptions.FRAME_TO_CONTENT); } } } }

The backwards iteration matters here for the same reason as the VBA: moving text out of a story changes character indices downstream. The upward walk through contiguous H1_Metadata paragraphs ensures that multi-line metadata blocks are captured as a single unit — subject area, objective, and lesson tags all move together into one anchored frame, not separately. The entire operation runs wrapped in UndoModes.ENTIRE_SCRIPT, so it's reversible as a single undo step.

What This Work Is

Infrastructure is invisible until you don't have it.

None of the systems documented here produce a single printed page on their own. They are the precondition for printing correctly at scale — the difference between a production run that requires one person and one that requires a team, between a revision that takes ten minutes and one that takes a day.

The project paused before the first workbooks went to press. The architecture, however, was complete. Everything documented here represents a production pipeline capable of processing 400+ pieces with consistent output, zero manual style application, and a typeface system engineered for the specific cognitive needs of the intended readers.

The right time to build the system is before the production pressure arrives. After it arrives, you're maintaining a system you no longer have time to design.