Class: Ucode::Glyphs::Writer
- Inherits:
-
Object
- Object
- Ucode::Glyphs::Writer
- Includes:
- Repo::AtomicWrites
- Defined in:
- lib/ucode/glyphs/writer.rb
Overview
Writes ‘glyph.svg` for every codepoint in a block by orchestrating the per-block pipeline: render PDF page → detect grid → extract each cell → write atomic file.
The Writer is page-driven: the caller hands it a ‘page_map` (`{ page_num => first_cp_on_that_page }`) so the writer knows what codepoint each detected cell anchor corresponds to. This is the one piece of state the Writer can’t derive on its own — pdftocairo converts the row’s codepoint labels to outlined glyphs, so they aren’t readable as text.
Idempotent: re-runs are no-ops via ‘Repo::AtomicWrites` (byte comparison; same content is skipped). Safe to re-run on the whole output tree.
Atomic: writes go through ‘<path>.tmp` + rename. A crash mid- write leaves either the old file or no file, never a truncated one.
**Placeholder for assigned codepoints with no glyph**: when a codepoint is listed in ‘block.codepoint_ids` but no cell is found on any rendered page, a small placeholder SVG is written so the site can render a “no official glyph” badge. Counted in the tally as `placeholder`.
Pure-ish: takes a renderer instance (defaults to the first available system renderer) and a fetcher; both are injectable for tests. The only I/O is the renderer, the writer’s output_root, and any optional cache.
Instance Method Summary collapse
-
#initialize(output_root:, renderer: PageRenderer.default, parallel_workers: 4) ⇒ Writer
constructor
A new instance of Writer.
-
#write_all(specs) ⇒ Hash
Drain a list of block-spec hashes through the worker pool.
-
#write_block(block:, pdf_path:, page_map:, strict: false) ⇒ Hash
Process every page in ‘page_map`, writing glyph.svg for each codepoint that (a) falls inside the block’s range and (b) has a detectable glyph on the page.
-
#write_page(block:, pdf_path:, page_num:, first_cp:) ⇒ Hash
Render one page, detect its grid, write every cell whose codepoint falls inside ‘block`’s range.
Methods included from Repo::AtomicWrites
#same_content?, #to_pretty_json, #write_atomic
Constructor Details
#initialize(output_root:, renderer: PageRenderer.default, parallel_workers: 4) ⇒ Writer
Returns a new instance of Writer.
54 55 56 57 58 |
# File 'lib/ucode/glyphs/writer.rb', line 54 def initialize(output_root:, renderer: PageRenderer.default, parallel_workers: 4) @output_root = Pathname.new(output_root) @renderer = renderer @parallel_workers = parallel_workers end |
Instance Method Details
#write_all(specs) ⇒ Hash
Drain a list of block-spec hashes through the worker pool. Each spec has the same shape as #write_block’s kwargs:
{ block:, pdf_path:, page_map: }
128 129 130 131 132 |
# File 'lib/ucode/glyphs/writer.rb', line 128 def write_all(specs) return drain_inline(specs) if @parallel_workers <= 1 drain_threaded(specs) end |
#write_block(block:, pdf_path:, page_map:, strict: false) ⇒ Hash
Process every page in ‘page_map`, writing glyph.svg for each codepoint that (a) falls inside the block’s range and (b) has a detectable glyph on the page.
72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/ucode/glyphs/writer.rb', line 72 def write_block(block:, pdf_path:, page_map:, strict: false) unless pdf_path && Pathname.new(pdf_path).exist? raise_missing_pdf!(block, pdf_path) if strict return placeholder_pass(block, zero_tally.tap { |h| h[:no_grid] = 1 }) end tally = zero_tally page_map.each do |page_num, first_cp| merge_tally!(tally, write_page(block: block, pdf_path: pdf_path, page_num: page_num, first_cp: first_cp)) end placeholder_pass(block, tally) end |
#write_page(block:, pdf_path:, page_num:, first_cp:) ⇒ Hash
Render one page, detect its grid, write every cell whose codepoint falls inside ‘block`’s range.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/ucode/glyphs/writer.rb', line 94 def write_page(block:, pdf_path:, page_num:, first_cp:) svg_doc = render_page(pdf_path, page_num) return no_grid_tally unless svg_doc grid = GridDetector.detect(svg_doc, block_first_cp: first_cp) return no_grid_tally unless grid counts = zero_tally extractor = CellExtractor.new(svg_doc) grid.rows.times do |row| grid.columns.times do |col| cp = grid.codepoint_at(row, col) next unless cp && block.covers?(cp) cell_svg = extractor.extract(grid, cp) if cell_svg.nil? counts[:empty] += 1 next end written = write_glyph(block, cp, cell_svg) counts[written ? :written : :skipped] += 1 end end counts end |