idml 0.8.0 → 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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 27df11baaefb3de6bfc6198404a9b3ad10b54b53de8a44cbe58658afdfdf4d4b
|
|
4
|
+
data.tar.gz: 4274cf4d1869bab1457da1427d8ba96d27cf64ced0140174923b7492ee283dd8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '02936232be8a99effaf2f417d765281700c8f6b89004bab18e462d29d8b60b12259a715780fd0c2c1b68d41e5f82cd087b2933753551c72f3235d5cfbe3e3640'
|
|
7
|
+
data.tar.gz: e92764f18f109a4ae66dabb4ebfcb3a991cd2926c22e7836de42e99d7c1b45e931cd0aa4f22678193564fb82bdbf24eeccd593405a1ada0322cda7f5ce54f32d
|
data/TODO.pdf/103-drop-caps.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# TODO PDF 103: Drop caps (DropCapLines, DropCapCharacters)
|
|
2
2
|
|
|
3
|
-
## Status:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# TODO PDF 115: Bullet and numbered list rendering
|
|
2
2
|
|
|
3
|
-
## Status: DONE
|
|
4
|
-
(
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
## Status: DONE — markers are prepended inline AND a hanging indent
|
|
4
|
+
(18pt) is applied so wrapped lines align with text after the marker.
|
|
5
|
+
The indent is approximate (fixed width) since tab-stop measurement
|
|
6
|
+
requires font metrics at style-resolution time; future refinement
|
|
7
|
+
would measure the actual marker width.
|
|
7
8
|
|
|
8
9
|
## Problem
|
|
9
10
|
|
|
@@ -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,
|
|
310
|
-
|
|
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
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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 :
|
|
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
|
|
@@ -249,9 +249,12 @@ module Idml
|
|
|
249
249
|
end
|
|
250
250
|
private_class_method :alignment_for
|
|
251
251
|
|
|
252
|
+
HANGING_INDENT_WIDTH = 18.0
|
|
253
|
+
|
|
252
254
|
# Prepends the list marker (bullet glyph or numbered expression)
|
|
253
255
|
# to the paragraph's first run when the paragraph is part of
|
|
254
|
-
# a list.
|
|
256
|
+
# a list. Also applies a hanging indent so wrapped lines align
|
|
257
|
+
# with the text after the marker, not with the marker itself.
|
|
255
258
|
def self.prepend_list_marker(paragraph)
|
|
256
259
|
marker = ListMarker.marker_for(paragraph)
|
|
257
260
|
return unless marker
|
|
@@ -259,9 +262,21 @@ module Idml
|
|
|
259
262
|
|
|
260
263
|
first_run = paragraph.runs.first
|
|
261
264
|
first_run.text = "#{marker}#{first_run.text}"
|
|
265
|
+
apply_hanging_indent(paragraph)
|
|
262
266
|
end
|
|
263
267
|
private_class_method :prepend_list_marker
|
|
264
268
|
|
|
269
|
+
# Adjusts left_indent and first_line_indent for a hanging
|
|
270
|
+
# indent effect: first line starts at the original left edge
|
|
271
|
+
# (with the marker), wrapped lines indent by HANGING_INDENT_WIDTH.
|
|
272
|
+
def self.apply_hanging_indent(paragraph)
|
|
273
|
+
current_left = paragraph.left_indent || 0
|
|
274
|
+
current_first = paragraph.first_line_indent || 0
|
|
275
|
+
paragraph.left_indent = current_left + HANGING_INDENT_WIDTH
|
|
276
|
+
paragraph.first_line_indent = current_first - HANGING_INDENT_WIDTH
|
|
277
|
+
end
|
|
278
|
+
private_class_method :apply_hanging_indent
|
|
279
|
+
|
|
265
280
|
# Concatenate runs into a single block. Used when the renderer
|
|
266
281
|
# can't handle per-run styling (e.g., no font metrics available).
|
|
267
282
|
def self.concatenate(runs)
|
data/lib/idml/version.rb
CHANGED
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.
|
|
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
|
+
date: 2026-08-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bigdecimal
|