hexapdf 0.43.0 → 0.45.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/examples/027-composer_optional_content.rb +6 -4
- data/examples/030-pdfa.rb +13 -11
- data/lib/hexapdf/composer.rb +23 -0
- data/lib/hexapdf/content/canvas.rb +3 -3
- data/lib/hexapdf/content/canvas_composer.rb +1 -0
- data/lib/hexapdf/document/files.rb +7 -2
- data/lib/hexapdf/document/layout.rb +15 -3
- data/lib/hexapdf/document/metadata.rb +12 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/layout/box.rb +180 -66
- data/lib/hexapdf/layout/box_fitter.rb +1 -0
- data/lib/hexapdf/layout/column_box.rb +18 -28
- data/lib/hexapdf/layout/container_box.rb +6 -6
- data/lib/hexapdf/layout/frame.rb +13 -94
- data/lib/hexapdf/layout/image_box.rb +4 -4
- data/lib/hexapdf/layout/list_box.rb +13 -31
- data/lib/hexapdf/layout/style.rb +8 -4
- data/lib/hexapdf/layout/table_box.rb +55 -58
- data/lib/hexapdf/layout/text_box.rb +84 -71
- data/lib/hexapdf/layout/text_fragment.rb +1 -1
- data/lib/hexapdf/layout/text_layouter.rb +7 -8
- data/lib/hexapdf/parser.rb +5 -2
- data/lib/hexapdf/rectangle.rb +4 -4
- data/lib/hexapdf/type/file_specification.rb +9 -5
- data/lib/hexapdf/type/form.rb +2 -2
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas_composer.rb +13 -8
- data/test/hexapdf/document/test_files.rb +5 -0
- data/test/hexapdf/document/test_layout.rb +16 -0
- data/test/hexapdf/document/test_metadata.rb +21 -0
- data/test/hexapdf/layout/test_box.rb +93 -37
- data/test/hexapdf/layout/test_box_fitter.rb +7 -0
- data/test/hexapdf/layout/test_column_box.rb +7 -13
- data/test/hexapdf/layout/test_container_box.rb +1 -1
- data/test/hexapdf/layout/test_frame.rb +7 -46
- data/test/hexapdf/layout/test_image_box.rb +14 -6
- data/test/hexapdf/layout/test_list_box.rb +26 -27
- data/test/hexapdf/layout/test_table_box.rb +47 -54
- data/test/hexapdf/layout/test_text_box.rb +83 -83
- data/test/hexapdf/test_composer.rb +20 -5
- data/test/hexapdf/test_parser.rb +8 -0
- data/test/hexapdf/test_serializer.rb +1 -0
- data/test/hexapdf/type/test_file_specification.rb +2 -1
- metadata +2 -2
@@ -75,7 +75,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
75
75
|
describe "fit" do
|
76
76
|
it "fits a single box" do
|
77
77
|
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 20, height: 10))
|
78
|
-
cell.fit(100, 100, @frame)
|
78
|
+
assert(cell.fit(100, 100, @frame).success?)
|
79
79
|
assert_equal(100, cell.width)
|
80
80
|
assert_equal(22, cell.height)
|
81
81
|
assert_equal(32, cell.preferred_width)
|
@@ -84,7 +84,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
84
84
|
|
85
85
|
it "fits a single box with horizontal aligning not being :left" do
|
86
86
|
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 20, height: 10, align: :center))
|
87
|
-
cell.fit(100, 100, @frame)
|
87
|
+
assert(cell.fit(100, 100, @frame).success?)
|
88
88
|
assert_equal(66, cell.preferred_width)
|
89
89
|
end
|
90
90
|
|
@@ -92,7 +92,7 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
92
92
|
box1 = HexaPDF::Layout::Box.create(width: 20, height: 10)
|
93
93
|
box2 = HexaPDF::Layout::Box.create(width: 50, height: 15)
|
94
94
|
cell = create_cell(children: [box1, box2])
|
95
|
-
cell.fit(100, 100, @frame)
|
95
|
+
assert(cell.fit(100, 100, @frame).success?)
|
96
96
|
assert_equal(100, cell.width)
|
97
97
|
assert_equal(37, cell.height)
|
98
98
|
assert_equal(62, cell.preferred_width)
|
@@ -103,23 +103,30 @@ describe HexaPDF::Layout::TableBox::Cell do
|
|
103
103
|
box1 = HexaPDF::Layout::Box.create(width: 20, height: 10, align: :center)
|
104
104
|
box2 = HexaPDF::Layout::Box.create(width: 50, height: 15)
|
105
105
|
cell = create_cell(children: [box1, box2])
|
106
|
-
cell.fit(100, 100, @frame)
|
106
|
+
assert(cell.fit(100, 100, @frame).success?)
|
107
107
|
assert_equal(66, cell.preferred_width)
|
108
108
|
end
|
109
109
|
|
110
110
|
it "fits the cell even if it has no content" do
|
111
111
|
cell = create_cell(children: nil)
|
112
|
-
cell.fit(100, 100, @frame)
|
112
|
+
assert(cell.fit(100, 100, @frame).success?)
|
113
113
|
assert_equal(100, cell.width)
|
114
114
|
assert_equal(12, cell.height)
|
115
115
|
assert_equal(12, cell.preferred_width)
|
116
116
|
assert_equal(12, cell.preferred_height)
|
117
117
|
end
|
118
118
|
|
119
|
-
it "doesn't fit
|
119
|
+
it "doesn't fit children that are too big" do
|
120
|
+
cell = create_cell(children: HexaPDF::Layout::Box.create(width: 300, height: 20))
|
121
|
+
assert(cell.fit(100, 100, @frame).failure?)
|
122
|
+
cell = create_cell(children: [HexaPDF::Layout::Box.create(width: 300, height: 20)])
|
123
|
+
assert(cell.fit(100, 100, @frame).failure?)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "doesn't fit anything if the available width or height are too small even if there are no children" do
|
120
127
|
cell = create_cell(children: nil)
|
121
|
-
|
122
|
-
|
128
|
+
assert(cell.fit(10, 100, @frame).failure?)
|
129
|
+
assert(cell.fit(100, 10, @frame).failure?)
|
123
130
|
end
|
124
131
|
end
|
125
132
|
|
@@ -370,8 +377,8 @@ describe HexaPDF::Layout::TableBox do
|
|
370
377
|
HexaPDF::Layout::TableBox.new(cells: [@fixed_size_boxes[0, 2], @fixed_size_boxes[2, 2]], **kwargs)
|
371
378
|
end
|
372
379
|
|
373
|
-
def check_box(box,
|
374
|
-
|
380
|
+
def check_box(box, fit_status, width, height, cell_data = nil)
|
381
|
+
assert_equal(fit_status, box.fit(@frame.available_width, @frame.available_height, @frame).status)
|
375
382
|
assert_equal(width, box.width, "box width")
|
376
383
|
assert_equal(height, box.height, "box height")
|
377
384
|
if cell_data
|
@@ -431,8 +438,8 @@ describe HexaPDF::Layout::TableBox do
|
|
431
438
|
footer = lambda {|_| [[nil]] }
|
432
439
|
box = create_box(header: header, footer: footer, cells: [[nil], [nil]],
|
433
440
|
cell_style: {background_color: 'black'})
|
434
|
-
|
435
|
-
box_a, box_b = box.split
|
441
|
+
assert(box.fit(100, 40, @frame).overflow?)
|
442
|
+
box_a, box_b = box.split
|
436
443
|
assert_same(box_a, box)
|
437
444
|
assert_equal('black', box_b.header_cells[0, 0].style.background_color)
|
438
445
|
assert_equal('black', box_b.footer_cells[0, 0].style.background_color)
|
@@ -482,23 +489,13 @@ describe HexaPDF::Layout::TableBox do
|
|
482
489
|
assert_equal(61, box.height)
|
483
490
|
end
|
484
491
|
|
485
|
-
it "cannot fit the table if the
|
486
|
-
box = create_box(width: 200)
|
487
|
-
refute(box.fit(@frame.available_width, @frame.available_height, @frame))
|
488
|
-
end
|
489
|
-
|
490
|
-
it "cannot fit the table if the available height smaller than the initial height" do
|
491
|
-
box = create_box(height: 200)
|
492
|
-
refute(box.fit(@frame.available_width, @frame.available_height, @frame))
|
493
|
-
end
|
494
|
-
|
495
|
-
it "cannot fit the table if the specified column widths are smaller than the available width" do
|
492
|
+
it "cannot fit the table if the specified column widths are greater than the available width" do
|
496
493
|
box = create_box(column_widths: [200])
|
497
|
-
|
494
|
+
assert(box.fit(@frame.available_width, @frame.available_height, @frame).failure?)
|
498
495
|
end
|
499
496
|
|
500
497
|
it "fits a simple table" do
|
501
|
-
check_box(create_box,
|
498
|
+
check_box(create_box, :success, 160, 45,
|
502
499
|
[[0, 0, 79.5, 22], [79.5, 0, 79.5, 22], [0, 22, 79.5, 22], [79.5, 22, 79.5, 22]])
|
503
500
|
end
|
504
501
|
|
@@ -507,7 +504,7 @@ describe HexaPDF::Layout::TableBox do
|
|
507
504
|
[{col_span: 2, row_span: 2, content: @fixed_size_boxes[3]}, *@fixed_size_boxes[4, 2]],
|
508
505
|
[{row_span: 2, content: @fixed_size_boxes[6]}, @fixed_size_boxes[7]],
|
509
506
|
@fixed_size_boxes[8, 3]]
|
510
|
-
check_box(create_box(cells: cells),
|
507
|
+
check_box(create_box(cells: cells), :success, 160, 89,
|
511
508
|
[[0, 0, 39.75, 22], [39.75, 0, 79.5, 22], [39.75, 0, 79.50, 22], [119.25, 0, 39.75, 22],
|
512
509
|
[0, 22, 79.5, 44], [0, 22, 79.5, 44], [79.5, 22, 39.75, 22], [119.25, 22, 39.75, 22],
|
513
510
|
[0, 22, 79.5, 44], [0, 22, 79.5, 44], [79.5, 44, 39.75, 44], [119.25, 44, 39.75, 22],
|
@@ -518,7 +515,7 @@ describe HexaPDF::Layout::TableBox do
|
|
518
515
|
result = [[0, 0, 80, 10], [80, 0, 80, 10], [0, 10, 80, 10], [80, 10, 80, 10]]
|
519
516
|
header = lambda {|_| [@fixed_size_boxes[10, 2], @fixed_size_boxes[12, 2]] }
|
520
517
|
box = create_box(header: header, cell_style: {padding: 0, border: {width: 0}})
|
521
|
-
box = check_box(box,
|
518
|
+
box = check_box(box, :success, 160, 40, result)
|
522
519
|
assert_equal(result, cell_infos(box.header_cells))
|
523
520
|
end
|
524
521
|
|
@@ -526,7 +523,7 @@ describe HexaPDF::Layout::TableBox do
|
|
526
523
|
result = [[0, 0, 80, 10], [80, 0, 80, 10], [0, 10, 80, 10], [80, 10, 80, 10]]
|
527
524
|
footer = lambda {|_| [@fixed_size_boxes[10, 2], @fixed_size_boxes[12, 2]] }
|
528
525
|
box = create_box(footer: footer, cell_style: {padding: 0, border: {width: 0}})
|
529
|
-
box = check_box(box,
|
526
|
+
box = check_box(box, :success, 160, 40, result)
|
530
527
|
assert_equal(result, cell_infos(box.footer_cells))
|
531
528
|
end
|
532
529
|
|
@@ -535,14 +532,22 @@ describe HexaPDF::Layout::TableBox do
|
|
535
532
|
cell_creator = lambda {|_| [@fixed_size_boxes[10, 2], @fixed_size_boxes[12, 2]] }
|
536
533
|
box = create_box(header: cell_creator, footer: cell_creator,
|
537
534
|
cell_style: {padding: 0, border: {width: 0}})
|
538
|
-
box = check_box(box,
|
535
|
+
box = check_box(box, :success, 160, 60, result)
|
539
536
|
assert_equal(result, cell_infos(box.header_cells))
|
540
537
|
assert_equal(result, cell_infos(box.footer_cells))
|
541
538
|
end
|
542
539
|
|
540
|
+
it "fails if the header or footer rows don't fit" do
|
541
|
+
cells_creator = lambda {|_| [@fixed_size_boxes[10, 2]] }
|
542
|
+
[{header: cells_creator}, {footer: cells_creator}].each do |args|
|
543
|
+
box = create_box(**args, cell_style: {padding: 0, border: {width: 0}})
|
544
|
+
assert(box.fit(100, 15, @frame).failure?)
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
543
548
|
it "partially fits a table if not enough height is available" do
|
544
549
|
box = create_box(height: 10, cell_style: {padding: 0, border: {width: 0}})
|
545
|
-
check_box(box,
|
550
|
+
check_box(box, :overflow, 160, 10,
|
546
551
|
[[0, 0, 80, 10], [80, 0, 80, 10], [nil, nil, 80, 0], [nil, nil, 0, 0]])
|
547
552
|
end
|
548
553
|
end
|
@@ -550,8 +555,8 @@ describe HexaPDF::Layout::TableBox do
|
|
550
555
|
describe "split" do
|
551
556
|
it "splits the table if some rows could not be fit into the available region" do
|
552
557
|
box = create_box
|
553
|
-
|
554
|
-
box_a, box_b = box.split
|
558
|
+
assert(box.fit(100, 25, @frame).overflow?)
|
559
|
+
box_a, box_b = box.split
|
555
560
|
assert_same(box_a, box)
|
556
561
|
assert(box_b.split_box?)
|
557
562
|
|
@@ -567,8 +572,8 @@ describe HexaPDF::Layout::TableBox do
|
|
567
572
|
[HexaPDF::Layout::Box.new(width: 20, height: 150, &@draw_block)]]
|
568
573
|
box = create_box(cells: cells)
|
569
574
|
|
570
|
-
|
571
|
-
box_a, box_b = box.split
|
575
|
+
assert(box.fit(100, 100, @frame).overflow?)
|
576
|
+
box_a, box_b = box.split
|
572
577
|
assert_same(box_a, box)
|
573
578
|
assert(box_b.split_box?)
|
574
579
|
assert_equal(0, box_a.start_row_index)
|
@@ -576,8 +581,8 @@ describe HexaPDF::Layout::TableBox do
|
|
576
581
|
assert_equal(1, box_b.start_row_index)
|
577
582
|
assert_equal(-1, box_b.last_fitted_row_index)
|
578
583
|
|
579
|
-
|
580
|
-
box_c, box_d = box_b.split
|
584
|
+
assert(box_b.fit(100, 100, @frame).failure?)
|
585
|
+
box_c, box_d = box_b.split
|
581
586
|
assert_nil(box_c)
|
582
587
|
assert_same(box_d, box_b)
|
583
588
|
assert(box_d.split_box?)
|
@@ -585,24 +590,12 @@ describe HexaPDF::Layout::TableBox do
|
|
585
590
|
assert_equal(-1, box_d.last_fitted_row_index)
|
586
591
|
end
|
587
592
|
|
588
|
-
it "splits the table if the header or footer rows don't fit" do
|
589
|
-
cells_creator = lambda {|_| [@fixed_size_boxes[10, 2]] }
|
590
|
-
[{header: cells_creator}, {footer: cells_creator}].each do |args|
|
591
|
-
box = create_box(**args)
|
592
|
-
box.cells.style(padding: 0, border: {width: 0})
|
593
|
-
refute(box.fit(100, 25, @frame))
|
594
|
-
box_a, box_b = box.split(100, 25, @frame)
|
595
|
-
assert_nil(box_a)
|
596
|
-
assert_same(box_b, box)
|
597
|
-
end
|
598
|
-
end
|
599
|
-
|
600
593
|
it "splits a table with a header or a footer" do
|
601
594
|
cells_creator = lambda {|_| [@fixed_size_boxes[10, 2]] }
|
602
595
|
[{header: cells_creator}, {footer: cells_creator}].each do |args|
|
603
596
|
box = create_box(**args)
|
604
|
-
|
605
|
-
box_a, box_b = box.split
|
597
|
+
assert(box.fit(100, 50, @frame).overflow?)
|
598
|
+
box_a, box_b = box.split
|
606
599
|
assert_same(box_a, box)
|
607
600
|
|
608
601
|
assert_equal(0, box_a.start_row_index)
|
@@ -681,9 +674,9 @@ describe HexaPDF::Layout::TableBox do
|
|
681
674
|
|
682
675
|
it "correctly works for split boxes" do
|
683
676
|
box = create_box(cell_style: {padding: 0, border: {width: 0}})
|
684
|
-
|
685
|
-
_, split_box = box.split
|
686
|
-
assert(split_box.fit(100, 100, @frame))
|
677
|
+
assert(box.fit(100, 10, @frame).overflow?)
|
678
|
+
_, split_box = box.split
|
679
|
+
assert(split_box.fit(100, 100, @frame).success?)
|
687
680
|
|
688
681
|
box.draw(@canvas, 20, 10)
|
689
682
|
split_box.draw(@canvas, 0, 50)
|
@@ -714,7 +707,7 @@ describe HexaPDF::Layout::TableBox do
|
|
714
707
|
box = create_box(header: lambda {|_| [@fixed_size_boxes[10, 1]] },
|
715
708
|
footer: lambda {|_| [@fixed_size_boxes[12, 1]] },
|
716
709
|
cell_style: {padding: 0, border: {width: 0}})
|
717
|
-
box.fit(100, 100, @frame)
|
710
|
+
assert(box.fit(100, 100, @frame).success?)
|
718
711
|
box.draw(@canvas, 20, 10)
|
719
712
|
operators = [[:save_graphics_state],
|
720
713
|
[:concatenate_matrix, [1, 0, 0, 1, 20, 40]],
|
@@ -36,39 +36,40 @@ describe HexaPDF::Layout::TextBox do
|
|
36
36
|
describe "fit" do
|
37
37
|
it "fits into a rectangular area" do
|
38
38
|
box = create_box([@inline_box] * 5, style: {padding: 10})
|
39
|
-
assert(box.fit(100, 100, @frame))
|
39
|
+
assert(box.fit(100, 100, @frame).success?)
|
40
40
|
assert_equal(70, box.width)
|
41
41
|
assert_equal(30, box.height)
|
42
42
|
end
|
43
43
|
|
44
44
|
it "respects the set width and height" do
|
45
|
-
box = create_box([@inline_box], width:
|
46
|
-
|
47
|
-
|
45
|
+
box = create_box([@inline_box] * 5, width: 44, height: 50,
|
46
|
+
style: {padding: 10, text_align: :right, text_valign: :bottom})
|
47
|
+
assert(box.fit(100, 100, @frame).success?)
|
48
|
+
assert_equal(44, box.width)
|
48
49
|
assert_equal(50, box.height)
|
49
|
-
assert_equal([10], box.instance_variable_get(:@result).lines.map(&:width))
|
50
|
+
assert_equal([20, 20, 10], box.instance_variable_get(:@result).lines.map(&:width))
|
50
51
|
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
53
|
+
describe "style option last_line_gap" do
|
54
|
+
it "is taken into account" do
|
55
|
+
box = create_box([@inline_box] * 5, style: {last_line_gap: true, line_spacing: :double})
|
56
|
+
assert(box.fit(100, 100, @frame).success?)
|
57
|
+
assert_equal(50, box.width)
|
58
|
+
assert_equal(20, box.height)
|
59
|
+
end
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
61
|
+
it "will have no effect for fixed-height boxes" do
|
62
|
+
box = create_box([@inline_box] * 5, height: 40, style: {last_line_gap: true, line_spacing: :double})
|
63
|
+
assert(box.fit(100, 100, @frame).success?)
|
64
|
+
assert_equal(50, box.width)
|
65
|
+
assert_equal(40, box.height)
|
66
|
+
end
|
66
67
|
end
|
67
68
|
|
68
|
-
it "uses the whole available width when aligning to the center or
|
69
|
-
[:center, :right].each do |align|
|
69
|
+
it "uses the whole available width when aligning to the center, right or justified" do
|
70
|
+
[:center, :right, :justify].each do |align|
|
70
71
|
box = create_box([@inline_box], style: {text_align: align})
|
71
|
-
assert(box.fit(100, 100, @frame))
|
72
|
+
assert(box.fit(100, 100, @frame).success?)
|
72
73
|
assert_equal(100, box.width)
|
73
74
|
end
|
74
75
|
end
|
@@ -76,91 +77,89 @@ describe HexaPDF::Layout::TextBox do
|
|
76
77
|
it "uses the whole available height when vertically aligning to the center or bottom" do
|
77
78
|
[:center, :bottom].each do |valign|
|
78
79
|
box = create_box([@inline_box], style: {text_valign: valign})
|
79
|
-
assert(box.fit(100, 100, @frame))
|
80
|
+
assert(box.fit(100, 100, @frame).success?)
|
80
81
|
assert_equal(100, box.height)
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
84
|
-
it "
|
85
|
+
it "can fit part of the box" do
|
85
86
|
box = create_box([@inline_box] * 20, height: 15)
|
86
|
-
|
87
|
-
box.style.overflow = :truncate
|
88
|
-
assert(box.fit(100, 100, @frame))
|
89
|
-
|
90
|
-
box = create_box([@inline_box] * 20, style: {overflow: :truncate})
|
91
|
-
refute(box.fit(100, 15, @frame))
|
92
|
-
end
|
93
|
-
|
94
|
-
it "can't fit the text box if the set width is bigger than the available width" do
|
95
|
-
box = create_box([@inline_box], width: 101)
|
96
|
-
refute(box.fit(100, 100, @frame))
|
87
|
+
assert(box.fit(100, 100, @frame).overflow?)
|
97
88
|
end
|
98
89
|
|
99
|
-
it "
|
100
|
-
|
101
|
-
|
90
|
+
it "correctly handles text indentation for split boxes" do
|
91
|
+
[{}, {position: :flow}].each do |styles|
|
92
|
+
box = create_box([@inline_box] * 202, style: {text_indent: 50, **styles})
|
93
|
+
assert(box.fit(100, 100, @frame).overflow?)
|
94
|
+
_, box_b = box.split
|
95
|
+
assert_equal(107, box_b.instance_variable_get(:@items).length)
|
96
|
+
assert(box_b.fit(100, 100, @frame).overflow?)
|
97
|
+
_, box_b = box_b.split
|
98
|
+
assert_equal(7, box_b.instance_variable_get(:@items).length)
|
99
|
+
end
|
102
100
|
end
|
103
|
-
end
|
104
101
|
|
105
|
-
|
106
|
-
it "works for an empty text box" do
|
102
|
+
it "fits an empty text box" do
|
107
103
|
box = create_box([])
|
108
|
-
|
104
|
+
assert(box.fit(100, 100, @frame).success?)
|
109
105
|
end
|
110
106
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
107
|
+
describe "position :flow" do
|
108
|
+
it "fits into the frame's outline" do
|
109
|
+
@frame.remove_area(Geom2D::Rectangle(0, 80, 20, 20))
|
110
|
+
@frame.remove_area(Geom2D::Rectangle(80, 70, 20, 20))
|
111
|
+
box = create_box([@inline_box] * 20, style: {position: :flow})
|
112
|
+
assert(box.fit(100, 100, @frame).success?)
|
113
|
+
assert_equal(100, box.width)
|
114
|
+
assert_equal(30, box.height)
|
115
|
+
end
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
it "respects a set initial height" do
|
118
|
+
box = create_box([@inline_box] * 20, height: 13, style: {position: :flow})
|
119
|
+
assert(box.fit(100, 100, @frame).overflow?)
|
120
|
+
assert_equal(100, box.width)
|
121
|
+
assert_equal(13, box.height)
|
122
|
+
end
|
120
123
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
+
it "respects top/bottom padding/border" do
|
125
|
+
@frame.remove_area(Geom2D::Rectangle(0, 80, 20, 20))
|
126
|
+
box = create_box([@inline_box] * 20, style: {position: :flow, padding: 10, border: {width: 2}})
|
127
|
+
assert(box.fit(100, 100, @frame).success?)
|
128
|
+
assert_equal(124, box.width)
|
129
|
+
assert_equal(54, box.height)
|
130
|
+
assert_equal([80, 100, 20], box.instance_variable_get(:@result).lines.map(&:width))
|
131
|
+
end
|
132
|
+
end
|
124
133
|
|
125
|
-
|
126
|
-
|
134
|
+
it "fails if no item of the text box fits due to the width" do
|
135
|
+
box = create_box([@inline_box])
|
136
|
+
assert(box.fit(5, 20, @frame).failure?)
|
127
137
|
end
|
128
138
|
|
129
|
-
it "
|
130
|
-
box = create_box([@inline_box]
|
131
|
-
box.fit(
|
132
|
-
box.instance_variable_set(:@width, 50.00000000006)
|
133
|
-
box.instance_variable_set(:@height, 10.00000000003)
|
134
|
-
assert_equal([box], box.split(50, 10, @frame))
|
139
|
+
it "fails if no item of the text box fits due to the height" do
|
140
|
+
box = create_box([@inline_box])
|
141
|
+
assert(box.fit(20, 5, @frame).failure?)
|
135
142
|
end
|
143
|
+
end
|
136
144
|
|
145
|
+
describe "split" do
|
137
146
|
it "splits the box if necessary when using non-flowing text" do
|
138
147
|
box = create_box([@inline_box] * 10)
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
refute(
|
143
|
-
assert(
|
144
|
-
assert_equal(5,
|
148
|
+
box.fit(50, 10, @frame)
|
149
|
+
box_a, box_b = box.split
|
150
|
+
assert_same(box, box_a)
|
151
|
+
refute(box_a.split_box?)
|
152
|
+
assert(box_b.split_box?)
|
153
|
+
assert_equal(5, box_b.instance_variable_get(:@items).length)
|
145
154
|
end
|
146
155
|
|
147
156
|
it "splits the box if necessary when using flowing text that results in a wider box" do
|
148
157
|
@frame.remove_area(Geom2D::Polygon.new([[0, 100], [50, 100], [50, 10], [0, 10]]))
|
149
158
|
box = create_box([@inline_box] * 60, style: {position: :flow})
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
assert_equal(5,
|
154
|
-
end
|
155
|
-
|
156
|
-
it "correctly handles text indentation for split boxes" do
|
157
|
-
[{}, {position: :flow}].each do |styles|
|
158
|
-
box = create_box([@inline_box] * 202, style: {text_indent: 50, **styles})
|
159
|
-
boxes = box.split(100, 100, @frame)
|
160
|
-
assert_equal(107, boxes[1].instance_variable_get(:@items).length)
|
161
|
-
boxes = boxes[1].split(100, 100, @frame)
|
162
|
-
assert_equal(7, boxes[1].instance_variable_get(:@items).length)
|
163
|
-
end
|
159
|
+
box.fit(50, 100, @frame)
|
160
|
+
box_a, box_b = box.split
|
161
|
+
assert_same(box, box_a)
|
162
|
+
assert_equal(5, box_b.instance_variable_get(:@items).length)
|
164
163
|
end
|
165
164
|
end
|
166
165
|
|
@@ -196,12 +195,12 @@ describe HexaPDF::Layout::TextBox do
|
|
196
195
|
@frame.remove_area(Geom2D::Rectangle(0, 0, 40, 100))
|
197
196
|
box = create_box([@inline_box], style: {position: :flow, border: {width: 1}})
|
198
197
|
box.fit(60, 100, @frame)
|
199
|
-
box.draw(@canvas, 0,
|
198
|
+
box.draw(@canvas, 0, 88)
|
200
199
|
assert_operators(@canvas.contents, [[:save_graphics_state],
|
201
|
-
[:append_rectangle, [40,
|
200
|
+
[:append_rectangle, [40, 88, 12, 12]],
|
202
201
|
[:clip_path_non_zero],
|
203
202
|
[:end_path],
|
204
|
-
[:append_rectangle, [40.5,
|
203
|
+
[:append_rectangle, [40.5, 88.5, 11.0, 11.0]],
|
205
204
|
[:stroke_path],
|
206
205
|
[:restore_graphics_state],
|
207
206
|
[:save_graphics_state],
|
@@ -218,6 +217,7 @@ describe HexaPDF::Layout::TextBox do
|
|
218
217
|
|
219
218
|
it "draws nothing onto the canvas if the box is empty" do
|
220
219
|
box = create_box([])
|
220
|
+
box.fit(100, 100, @frame)
|
221
221
|
box.draw(@canvas, 5, 5)
|
222
222
|
assert_operators(@canvas.contents, [])
|
223
223
|
end
|
@@ -119,6 +119,13 @@ describe HexaPDF::Composer do
|
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
|
+
describe "styles" do
|
123
|
+
it "delegates to layout.styles" do
|
124
|
+
@composer.styles(base: {font_size: 30}, other: {font_size: 40})
|
125
|
+
assert_equal([:base, :other], @composer.document.layout.styles.keys)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
122
129
|
describe "page_style" do
|
123
130
|
it "returns the page style if no argument or block is given" do
|
124
131
|
page_style = @composer.page_style(:default)
|
@@ -225,8 +232,9 @@ describe HexaPDF::Composer do
|
|
225
232
|
first_page_contents = @composer.canvas.contents
|
226
233
|
@composer.draw_box(create_box(height: 400))
|
227
234
|
|
228
|
-
box = create_box
|
229
|
-
box.define_singleton_method(:
|
235
|
+
box = create_box
|
236
|
+
box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
|
237
|
+
box.define_singleton_method(:split_content) do |*|
|
230
238
|
[box, HexaPDF::Layout::Box.new(height: 100) {}]
|
231
239
|
end
|
232
240
|
@composer.draw_box(box)
|
@@ -235,7 +243,7 @@ describe HexaPDF::Composer do
|
|
235
243
|
[:concatenate_matrix, [1, 0, 0, 1, 36, 405.889764]],
|
236
244
|
[:restore_graphics_state],
|
237
245
|
[:save_graphics_state],
|
238
|
-
[:concatenate_matrix, [1, 0, 0, 1, 36,
|
246
|
+
[:concatenate_matrix, [1, 0, 0, 1, 36, 36]],
|
239
247
|
[:restore_graphics_state]])
|
240
248
|
assert_operators(@composer.canvas.contents,
|
241
249
|
[[:save_graphics_state],
|
@@ -263,13 +271,20 @@ describe HexaPDF::Composer do
|
|
263
271
|
[:restore_graphics_state]])
|
264
272
|
end
|
265
273
|
|
274
|
+
it "handles truncated boxes correctly" do
|
275
|
+
box = create_box(height: 400, style: {overflow: :truncate})
|
276
|
+
box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
|
277
|
+
assert_same(box, @composer.draw_box(box))
|
278
|
+
end
|
279
|
+
|
266
280
|
it "returns the last drawn box" do
|
267
281
|
box = create_box(height: 400)
|
268
282
|
assert_same(box, @composer.draw_box(box))
|
269
283
|
|
270
|
-
box = create_box(height: 400)
|
271
284
|
split_box = create_box(height: 100)
|
272
|
-
box
|
285
|
+
box = create_box
|
286
|
+
box.define_singleton_method(:fit_content) {|*| fit_result.overflow! }
|
287
|
+
box.define_singleton_method(:split_content) {|*| [box, split_box] }
|
273
288
|
assert_same(split_box, @composer.draw_box(box))
|
274
289
|
end
|
275
290
|
|
data/test/hexapdf/test_parser.rb
CHANGED
@@ -358,6 +358,14 @@ describe HexaPDF::Parser do
|
|
358
358
|
it "finds the startxref anywhere in file" do
|
359
359
|
create_parser("startxref\n5\n%%EOF" << "\nhallo" * 5000)
|
360
360
|
assert_equal(5, @parser.startxref_offset)
|
361
|
+
end
|
362
|
+
|
363
|
+
it "handles the case where %%EOF is the on the 1. line of the 1024 byte search block" do
|
364
|
+
create_parser("startxref\n5\n%%EOF\n" << "h" * 1018)
|
365
|
+
assert_equal(5, @parser.startxref_offset)
|
366
|
+
end
|
367
|
+
|
368
|
+
it "handles the case where %%EOF is the on the 2. line of the 1024 byte search block" do
|
361
369
|
create_parser("startxref\n5\n%%EOF\n" << "h" * 1017)
|
362
370
|
assert_equal(5, @parser.startxref_offset)
|
363
371
|
end
|
@@ -89,6 +89,7 @@ describe HexaPDF::Serializer do
|
|
89
89
|
assert_serialized('/ ', :"")
|
90
90
|
assert_serialized('/H#c3#b6#c3#9fgang', :Hößgang)
|
91
91
|
assert_serialized('/H#e8lp', "H\xE8lp".force_encoding('BINARY').intern)
|
92
|
+
assert_serialized('/#00#09#0a#0c#0d#20', :"\x00\t\n\f\r ")
|
92
93
|
end
|
93
94
|
|
94
95
|
it "serializes arrays" do
|
@@ -97,10 +97,11 @@ describe HexaPDF::Type::FileSpecification do
|
|
97
97
|
end
|
98
98
|
|
99
99
|
it "embeds the given file and registers it with the global name registry" do
|
100
|
-
stream = @obj.embed(@file.path)
|
100
|
+
stream = @obj.embed(@file.path, mime_type: 'application/xml')
|
101
101
|
assert_equal(stream, @obj[:EF][:UF])
|
102
102
|
assert_equal(stream, @obj[:EF][:F])
|
103
103
|
assert_equal(File.basename(@file.path), @obj.path)
|
104
|
+
assert_equal(:'application/xml', stream[:Subtype])
|
104
105
|
assert_equal(@obj, @doc.catalog[:Names][:EmbeddedFiles].find_entry(@obj.path))
|
105
106
|
assert_equal(:FlateDecode, stream[:Filter])
|
106
107
|
assert_equal('embed-test', stream.stream)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hexapdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.45.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Leitner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cmdparse
|