idml 0.8.1 → 0.8.2

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: 987636748becfcad9dafe0f703801848e6c4e1bffa12a59b7cd0034424b3628a
4
- data.tar.gz: bcd103a17bab68ca38a9cd2534aa97f937aa10f560c79283bd5e1efd585be22e
3
+ metadata.gz: 27df11baaefb3de6bfc6198404a9b3ad10b54b53de8a44cbe58658afdfdf4d4b
4
+ data.tar.gz: 4274cf4d1869bab1457da1427d8ba96d27cf64ced0140174923b7492ee283dd8
5
5
  SHA512:
6
- metadata.gz: d7eb62a48b163312832120f0d475b4b7ced9b1364c457677135026f1e75b84d36d53faa834c7ec36ecd87b4e9daacd727c0f1e75639a87919b98f507c56fde01
7
- data.tar.gz: 470a0a62f1855b42369f0a81f099982a89a50f4070ad981a23d66693f6724e3fdf8c04441fb274c702871163e94c96804a27b4d7651ec5eea5de83cd435c791d
6
+ metadata.gz: '02936232be8a99effaf2f417d765281700c8f6b89004bab18e462d29d8b60b12259a715780fd0c2c1b68d41e5f82cd087b2933753551c72f3235d5cfbe3e3640'
7
+ data.tar.gz: e92764f18f109a4ae66dabb4ebfcb3a991cd2926c22e7836de42e99d7c1b45e931cd0aa4f22678193564fb82bdbf24eeccd593405a1ada0322cda7f5ce54f32d
@@ -1,13 +1,12 @@
1
1
  # TODO PDF 103: Drop caps (DropCapLines, DropCapCharacters)
2
2
 
3
- ## Status: PARTIAL — `Render::DropCap` helper computes drop-cap
4
- geometry (font_size = base × lines, width via font_metrics.measure_text,
5
- height = leading × lines). TextFrameRenderer.emit_drop_cap emits the
6
- drop cap as an enlarged glyph at the paragraph's top-left when the
7
- paragraph declares DropCapLines/DropCapCharacters > 0 and is the
8
- chain's first paragraph.
9
-
10
- Wrap-around text is NOT yet implemented — the drop cap renders as an
3
+ ## Status: DONE — `Render::DropCap` helper computes drop-cap geometry.
4
+ TextFrameRenderer.emit_drop_cap emits the enlarged glyph, strips the
5
+ drop cap characters from the first run (no double render), and
6
+ applies reduced wrap width for the first `drop_cap_lines` runs so
7
+ text wraps around the drop cap. Approximation: treats each run as
8
+ one line (real per-line tracking would require line-count-aware
9
+ wrapping).
11
10
  enlarged glyph; subsequent paragraph text flows normally and may
12
11
  overlap the drop cap. Real wrap-around (indent lines 1..M to the
13
12
  right of the drop cap) requires per-line wrap-width control in
@@ -306,18 +306,44 @@ module Idml
306
306
 
307
307
  emit_paragraph_top_rule(canvas, paragraph, context, cursor_y,
308
308
  frame_left, frame_right, first_paragraph)
309
- emit_drop_cap(canvas, paragraph, context, frame_left, cursor_y,
310
- first_paragraph)
309
+ drop_cap = emit_drop_cap(canvas, paragraph, context, frame_left,
310
+ cursor_y, first_paragraph)
311
311
 
312
+ effective_runs = prepare_runs_for_drop_cap(runs, drop_cap)
313
+ dc_width = drop_cap&.wrap_offset || 0
314
+ dc_lines = drop_cap&.lines || 0
315
+
316
+ rendered, cursor_y, char_cursor = iterate_paragraph_runs(
317
+ canvas, paragraph, effective_runs, context, layout_frame,
318
+ font, wrap_width, cursor_y, bottom_limit, char_cursor,
319
+ first_paragraph, para_attrs, dc_width, dc_lines
320
+ )
321
+
322
+ remaining_runs = effective_runs[rendered..]
323
+ cursor_y = emit_paragraph_bottom_rule(canvas, paragraph, context,
324
+ cursor_y, frame_left,
325
+ frame_right, para_attrs,
326
+ remaining_runs)
327
+ [remaining_runs, cursor_y, char_cursor]
328
+ end
329
+ private_class_method :render_runs_for_paragraph
330
+
331
+ def self.iterate_paragraph_runs(canvas, paragraph, runs, context,
332
+ layout_frame, font, wrap_width,
333
+ cursor_y, bottom_limit, char_cursor,
334
+ first_paragraph, para_attrs,
335
+ dc_width, dc_lines)
312
336
  rendered_count = 0
313
337
  runs.each do |run|
314
338
  break if cursor_y < bottom_limit
315
339
 
316
340
  space_before = paragraph_space_before(para_attrs, first_paragraph,
317
341
  rendered_count)
342
+ run_wrap = drop_cap_wrap_width(wrap_width, dc_width,
343
+ dc_lines, rendered_count)
318
344
  positioned, next_y = layout_run(
319
345
  canvas, run, paragraph, context, layout_frame, font,
320
- wrap_width, cursor_y, space_before,
346
+ run_wrap, cursor_y, space_before,
321
347
  para_attrs[:first_line_indent], para_attrs[:left_indent],
322
348
  char_cursor
323
349
  )
@@ -325,15 +351,43 @@ module Idml
325
351
  cursor_y = next_y
326
352
  rendered_count += 1
327
353
  end
354
+ [rendered_count, cursor_y, char_cursor]
355
+ end
356
+ private_class_method :iterate_paragraph_runs
328
357
 
329
- remaining_runs = runs[rendered_count..]
330
- cursor_y = emit_paragraph_bottom_rule(canvas, paragraph, context,
331
- cursor_y, frame_left,
332
- frame_right, para_attrs,
333
- remaining_runs)
334
- [remaining_runs, cursor_y, char_cursor]
358
+ # Strips drop cap characters from the first run so they don't
359
+ # render twice (once as the enlarged drop cap, once as normal
360
+ # text). Returns a new run list with the first run's text
361
+ # adjusted. If stripping empties the first run, removes it.
362
+ def self.prepare_runs_for_drop_cap(runs, drop_cap)
363
+ return runs unless drop_cap
364
+ return runs if runs.empty?
365
+
366
+ stripped_count = drop_cap.text.length
367
+ first = runs.first
368
+ return runs if first.text.length < stripped_count
369
+
370
+ remaining_text = first.text[stripped_count..]
371
+ return runs.drop(1) if remaining_text.nil? || remaining_text.empty?
372
+
373
+ stripped_run = first.dup
374
+ stripped_run.text = remaining_text
375
+ [stripped_run] + runs[1..]
335
376
  end
336
- private_class_method :render_runs_for_paragraph
377
+ private_class_method :prepare_runs_for_drop_cap
378
+
379
+ # Returns reduced wrap width for runs within the drop cap zone
380
+ # (first `drop_cap_lines` runs). After the zone, returns full
381
+ # width. Approximation: treats each run as one line. Proper
382
+ # implementation would track actual line count per run.
383
+ def self.drop_cap_wrap_width(full_width, drop_cap_width,
384
+ drop_cap_lines, run_index)
385
+ return full_width if drop_cap_width.zero? || drop_cap_lines.zero?
386
+ return full_width if run_index >= drop_cap_lines
387
+
388
+ full_width - drop_cap_width
389
+ end
390
+ private_class_method :drop_cap_wrap_width
337
391
 
338
392
  # Renders the drop cap for a paragraph (when declared and the
339
393
  # first paragraph of the chain). Wrap-around text is not yet
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.8.1"
4
+ VERSION = "0.8.2"
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.8.1
4
+ version: 0.8.2
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-11 00:00:00.000000000 Z
11
+ date: 2026-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal