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 |