idml 0.9.5 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 955ca35f14b9460085c65187586817bf655085d3f814660e790c7050e1abbe5f
4
- data.tar.gz: 1aa96f29ecc97594128d6f88a245e46383efc99f0ff4d9f6797947cd8fe361a9
3
+ metadata.gz: 76ef12540a7d7d1805189664f88fc0c5b400b8d1240fbc495239ddb35f64822c
4
+ data.tar.gz: 941893920b3e23a32c196de4286a64f10edb0e377bb716a5287ba110bc3052bd
5
5
  SHA512:
6
- metadata.gz: 638e2df6de7b160a8f7c64703309986f32644e29ada18f9c8f611b967e80ee4e9fdcf84cc7687ecd364b6aa964bcbb5831c4c279a841dd203b412f39d710fbc1
7
- data.tar.gz: 9ef18bb1487ed2d6206c2c3aef6fe4ef699a12db584c2052557343144ae3f38d32cbcc275972114b2b46b1cc660c1cb24525befcd616462c33f9ab5ec52fd6e0
6
+ metadata.gz: 95da409c1987bac0a36ab73793c02e349d3060f313f3f4fc2670f22efffd89bd2d5f0bcfba4096dab595cf39a7843134ee0fc4f06f9e4136d477c65f44870586
7
+ data.tar.gz: a74c7d9a0a1ccd22e57308890a0f6b257e9ec2d3aab7d3d8a407c23b4b52f466cd2c39f86be48d4b89e912e26fcd3a582dbdf27455d8db49a405219ede022357
@@ -0,0 +1,29 @@
1
+ # TODO PDF 122: Vertical mode completion — Latin rotation, vertical ruby
2
+
3
+ ## Status: COMPLETE — implemented 2026-08-20
4
+
5
+ ## Problem
6
+
7
+ The vertical writing path (TODO 13, 2026-08-19) stacked every glyph
8
+ upright: Latin letters and digits rendered sideways-wrong (rotated
9
+ in appearance relative to convention), and ruby annotations were
10
+ not emitted at all in vertical mode.
11
+
12
+ ## What was done
13
+
14
+ - Latin rotation: non-CJK glyphs in vertical text render rotated
15
+ 90° clockwise (the vertical-writing convention for Latin runs),
16
+ via a graphics-state transform (q → translate → rotate −90° →
17
+ text at origin → Q). CJK glyphs stay upright.
18
+ - Vertical ruby: runs carrying RubyString emit the annotation
19
+ stacked vertically alongside the base glyphs — to the right of
20
+ the column by default, to the left for RubyPosition values
21
+ containing "Below" (mirroring the horizontal above/below
22
+ convention under rotation). RubyFontSize defaults to half the
23
+ base size.
24
+
25
+ ## Known limitations
26
+
27
+ - Tate-chu-yoko (horizontal digit groups inside vertical text) and
28
+ mojikumi spacing classes remain documented stretch goals
29
+ (TODO 13).
@@ -232,6 +232,8 @@ module Idml
232
232
  positioned.each do |glyph|
233
233
  emit_vertical_glyph(canvas, glyph, run, context, size)
234
234
  end
235
+ emit_vertical_ruby(canvas, run, positioned, layout_frame,
236
+ size, context)
235
237
  end
236
238
  []
237
239
  end
@@ -246,21 +248,80 @@ module Idml
246
248
 
247
249
  # Emits one upright glyph at its vertical position with the
248
250
  # run's character styling.
251
+ # Emits one glyph in vertical mode: CJK upright; Latin
252
+ # (non-CJK) rotated 90° clockwise per the vertical-writing
253
+ # convention, via a graphics-state transform.
249
254
  def self.emit_vertical_glyph(canvas, positioned, run, context,
250
255
  size)
251
256
  text = [positioned.codepoint].pack("U")
252
- text_kwargs = CharacterStyle.text_kwargs(
253
- run,
254
- {
255
- at: [positioned.x, positioned.y],
256
- font: font_for_run(run, context),
257
- size: size,
258
- },
259
- )
260
- canvas.text(text, **text_kwargs)
257
+ if TextEngine::CjkLayout.cjk?(positioned.codepoint)
258
+ text_kwargs = CharacterStyle.text_kwargs(
259
+ run,
260
+ {
261
+ at: [positioned.x, positioned.y],
262
+ font: font_for_run(run, context),
263
+ size: size,
264
+ },
265
+ )
266
+ canvas.text(text, **text_kwargs)
267
+ else
268
+ canvas.save_graphics_state do
269
+ # 90° clockwise: exact matrix (rotate would emit
270
+ # cos/sin float noise like 6.1e-17).
271
+ canvas.concat(0, -1, 1, 0, positioned.x, positioned.y)
272
+ text_kwargs = CharacterStyle.text_kwargs(
273
+ run,
274
+ {
275
+ at: [0, 0],
276
+ font: font_for_run(run, context),
277
+ size: size,
278
+ },
279
+ )
280
+ canvas.text(text, **text_kwargs)
281
+ end
282
+ end
261
283
  end
262
284
  private_class_method :emit_vertical_glyph
263
285
 
286
+ # Emits the run's ruby alongside its vertical glyphs:
287
+ # stacked top-to-bottom beside the column — right side by
288
+ # default, left for Below* RubyPosition values (the
289
+ # horizontal above/below convention mirrored under
290
+ # rotation).
291
+ def self.emit_vertical_ruby(canvas, run, positioned, frame,
292
+ base_size, context)
293
+ ruby_text = run.ruby_string
294
+ return if ruby_text.nil? || ruby_text.empty?
295
+
296
+ ruby_size = run.ruby_font_size ||
297
+ (base_size * RUBY_SIZE_FACTOR)
298
+ x = ruby_vertical_x(positioned.first, run, base_size)
299
+ y = frame.y + frame.height - (frame.inset_top || 0)
300
+ ruby_text.each_codepoint do |codepoint|
301
+ text_kwargs = CharacterStyle.text_kwargs(
302
+ run,
303
+ {
304
+ at: [x, y - ruby_size],
305
+ font: font_for_run(run, context),
306
+ size: ruby_size,
307
+ },
308
+ )
309
+ canvas.text([codepoint].pack("U"), **text_kwargs)
310
+ y -= ruby_size
311
+ end
312
+ end
313
+ private_class_method :emit_vertical_ruby
314
+
315
+ def self.ruby_vertical_x(positioned, run, base_size)
316
+ offset = base_size * 0.9
317
+ if run.ruby_position.to_s.include?("Below")
318
+ positioned.x - offset
319
+ else
320
+ positioned.x + offset
321
+ end
322
+ end
323
+ private_class_method :ruby_vertical_x
324
+
264
325
  def self.chain_head?(frame)
265
326
  frame.previous_text_frame.nil? || frame.previous_text_frame == "n"
266
327
  end
data/lib/idml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Idml
4
- VERSION = "0.9.5"
4
+ VERSION = "0.9.6"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: idml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-08-20 00:00:00.000000000 Z
11
+ date: 2026-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -180,6 +180,7 @@ files:
180
180
  - TODO.pdf/12-idml-to-pdf-pipeline.md
181
181
  - TODO.pdf/120-start-paragraph-breaks.md
182
182
  - TODO.pdf/121-cell-edge-strokes.md
183
+ - TODO.pdf/122-vertical-latin-ruby.md
183
184
  - TODO.pdf/13-cjk-text-layout.md
184
185
  - TODO.pdf/14-page-item-element-models.md
185
186
  - TODO.pdf/15-wire-spread-children.md