hexapdf 0.21.0 → 0.23.0

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +79 -1
  3. data/Rakefile +1 -1
  4. data/lib/hexapdf/cli/form.rb +30 -3
  5. data/lib/hexapdf/cli/inspect.rb +18 -5
  6. data/lib/hexapdf/cli/modify.rb +23 -3
  7. data/lib/hexapdf/composer.rb +24 -2
  8. data/lib/hexapdf/dictionary_fields.rb +1 -1
  9. data/lib/hexapdf/document/destinations.rb +396 -0
  10. data/lib/hexapdf/document.rb +38 -89
  11. data/lib/hexapdf/encryption/aes.rb +9 -5
  12. data/lib/hexapdf/layout/frame.rb +8 -9
  13. data/lib/hexapdf/layout/style.rb +280 -7
  14. data/lib/hexapdf/layout/text_box.rb +10 -2
  15. data/lib/hexapdf/layout/text_layouter.rb +6 -1
  16. data/lib/hexapdf/revision.rb +8 -1
  17. data/lib/hexapdf/revisions.rb +151 -50
  18. data/lib/hexapdf/task/optimize.rb +21 -11
  19. data/lib/hexapdf/type/acro_form/form.rb +11 -5
  20. data/lib/hexapdf/type/acro_form/text_field.rb +8 -0
  21. data/lib/hexapdf/type/catalog.rb +9 -1
  22. data/lib/hexapdf/type/image.rb +47 -3
  23. data/lib/hexapdf/type/names.rb +13 -0
  24. data/lib/hexapdf/type/xref_stream.rb +2 -1
  25. data/lib/hexapdf/utils/sorted_tree_node.rb +3 -1
  26. data/lib/hexapdf/version.rb +1 -1
  27. data/lib/hexapdf/writer.rb +15 -2
  28. data/test/hexapdf/document/test_destinations.rb +338 -0
  29. data/test/hexapdf/encryption/test_aes.rb +8 -0
  30. data/test/hexapdf/encryption/test_security_handler.rb +2 -2
  31. data/test/hexapdf/layout/test_frame.rb +15 -1
  32. data/test/hexapdf/layout/test_text_box.rb +16 -0
  33. data/test/hexapdf/layout/test_text_layouter.rb +7 -0
  34. data/test/hexapdf/task/test_optimize.rb +17 -4
  35. data/test/hexapdf/test_composer.rb +24 -1
  36. data/test/hexapdf/test_dictionary_fields.rb +1 -1
  37. data/test/hexapdf/test_document.rb +30 -133
  38. data/test/hexapdf/test_parser.rb +1 -1
  39. data/test/hexapdf/test_revision.rb +14 -0
  40. data/test/hexapdf/test_revisions.rb +137 -29
  41. data/test/hexapdf/test_writer.rb +43 -14
  42. data/test/hexapdf/type/acro_form/test_form.rb +2 -1
  43. data/test/hexapdf/type/acro_form/test_text_field.rb +17 -0
  44. data/test/hexapdf/type/test_catalog.rb +8 -0
  45. data/test/hexapdf/type/test_image.rb +45 -9
  46. data/test/hexapdf/type/test_names.rb +20 -0
  47. data/test/hexapdf/type/test_xref_stream.rb +2 -1
  48. data/test/hexapdf/utils/test_sorted_tree_node.rb +11 -1
  49. metadata +6 -3
@@ -581,9 +581,22 @@ module HexaPDF
581
581
  #
582
582
  # The font to be used, must be set to a valid font wrapper object before it can be used.
583
583
  #
584
+ # HexaPDF::Composer handles this property specially in that it resolves a set string or array
585
+ # to a font wrapper object before doing else with the style object.
586
+ #
584
587
  # This is the only style property without a default value!
585
588
  #
586
589
  # See: HexaPDF::Content::Canvas#font
590
+ #
591
+ # Examples:
592
+ #
593
+ # #>pdf-composer100
594
+ # composer.text("Helvetica", font: composer.document.fonts.add("Helvetica"))
595
+ # composer.text("Courier", font: "Courier") # works only with composer
596
+ #
597
+ # helvetica_bold = composer.document.fonts.add("Helvetica", variant: :bold)
598
+ # composer.text("Helvetica Bold", font: helvetica_bold)
599
+ # composer.text("Courier Bold", font: ["Courier", variant: :bold]) # only composer
587
600
 
588
601
  ##
589
602
  # :method: font_size
@@ -593,6 +606,12 @@ module HexaPDF
593
606
  # The font size, defaults to 10.
594
607
  #
595
608
  # See: HexaPDF::Content::Canvas#font_size
609
+ #
610
+ # Examples:
611
+ #
612
+ # #>pdf-composer100
613
+ # composer.text("Default size")
614
+ # composer.text("Larger size", font_size: 20)
596
615
 
597
616
  ##
598
617
  # :method: character_spacing
@@ -602,6 +621,11 @@ module HexaPDF
602
621
  # The character spacing, defaults to 0 (i.e. no additional character spacing).
603
622
  #
604
623
  # See: HexaPDF::Content::Canvas#character_spacing
624
+ #
625
+ # Examples:
626
+ #
627
+ # #>pdf-composer100
628
+ # composer.text("More spacing between characters", character_spacing: 1)
605
629
 
606
630
  ##
607
631
  # :method: word_spacing
@@ -611,6 +635,11 @@ module HexaPDF
611
635
  # The word spacing, defaults to 0 (i.e. no additional word spacing).
612
636
  #
613
637
  # See: HexaPDF::Content::Canvas#word_spacing
638
+ #
639
+ # Examples:
640
+ #
641
+ # #>pdf-composer100
642
+ # composer.text("More word spacing", word_spacing: 20)
614
643
 
615
644
  ##
616
645
  # :method: horizontal_scaling
@@ -620,6 +649,11 @@ module HexaPDF
620
649
  # The horizontal scaling, defaults to 100 (in percent, i.e. normal scaling).
621
650
  #
622
651
  # See: HexaPDF::Content::Canvas#horizontal_scaling
652
+ #
653
+ # Examples:
654
+ #
655
+ # #>pdf-composer100
656
+ # composer.text("Horizontal scaling", horizontal_scaling: 150)
623
657
 
624
658
  ##
625
659
  # :method: text_rise
@@ -629,6 +663,11 @@ module HexaPDF
629
663
  # The text rise, i.e. the vertical offset from the baseline, defaults to 0.
630
664
  #
631
665
  # See: HexaPDF::Content::Canvas#text_rise
666
+ #
667
+ # Examples:
668
+ #
669
+ # #>pdf-composer100
670
+ # composer.formatted_text(["Normal", {text: "Up in the air", text_rise: 5}])
632
671
 
633
672
  ##
634
673
  # :method: font_features
@@ -641,6 +680,13 @@ module HexaPDF
641
680
  # Each feature to be applied is indicated by a key with a truthy value.
642
681
  #
643
682
  # See: HexaPDF::Layout::TextShaper#shape_text for available features.
683
+ #
684
+ # Examples:
685
+ #
686
+ # #>pdf-composer100
687
+ # composer.style(:base, font: ["Times", custom_encoding: true], font_size: 30)
688
+ # composer.text("Test flight")
689
+ # composer.text("Test flight", font_features: {kern: true, liga: true})
644
690
 
645
691
  ##
646
692
  # :method: text_rendering_mode
@@ -652,6 +698,11 @@ module HexaPDF
652
698
  # rendering mode value.
653
699
  #
654
700
  # See: HexaPDF::Content::Canvas#text_rendering_mode
701
+ #
702
+ # Examples:
703
+ #
704
+ # #>pdf-composer100
705
+ # composer.text("Test flight", font_size: 40, text_rendering_mode: :stroke)
655
706
 
656
707
  ##
657
708
  # :method: subscript
@@ -661,6 +712,11 @@ module HexaPDF
661
712
  # Render the text as subscript, i.e. lower and in a smaller font size; defaults to false.
662
713
  #
663
714
  # If superscript is set, it will be deactivated.
715
+ #
716
+ # Examples:
717
+ #
718
+ # #>pdf-composer100
719
+ # composer.formatted_text(["Some ", {text: "subscript text", subscript: true}])
664
720
 
665
721
  ##
666
722
  # :method: superscript
@@ -670,6 +726,11 @@ module HexaPDF
670
726
  # Render the text as superscript, i.e. higher and in a smaller font size; defaults to false.
671
727
  #
672
728
  # If subscript is set, it will be deactivated.
729
+ #
730
+ # Examples:
731
+ #
732
+ # #>pdf-composer100
733
+ # composer.formatted_text(["Some ", {text: "superscript text", superscript: true}])
673
734
 
674
735
  ##
675
736
  # :method: underline
@@ -677,6 +738,11 @@ module HexaPDF
677
738
  # underline(enable = false)
678
739
  #
679
740
  # Renders a line underneath the text; defaults to false.
741
+ #
742
+ # Examples:
743
+ #
744
+ # #>pdf-composer100
745
+ # composer.text("Underlined text", underline: true)
680
746
 
681
747
  ##
682
748
  # :method: strikeout
@@ -684,6 +750,11 @@ module HexaPDF
684
750
  # strikeout(enable = false)
685
751
  #
686
752
  # Renders a line through the text; defaults to false.
753
+ #
754
+ # Examples:
755
+ #
756
+ # #>pdf-composer100
757
+ # composer.text("Strikeout text", strikeout: true)
687
758
 
688
759
  ##
689
760
  # :method: fill_color
@@ -693,6 +764,11 @@ module HexaPDF
693
764
  # The color used for filling (e.g. text), defaults to black.
694
765
  #
695
766
  # See: HexaPDF::Content::Canvas#fill_color
767
+ #
768
+ # Examples:
769
+ #
770
+ # #>pdf-composer100
771
+ # composer.text("This is some red text", fill_color: "red")
696
772
 
697
773
  ##
698
774
  # :method: fill_alpha
@@ -703,6 +779,11 @@ module HexaPDF
703
779
  # opaque).
704
780
  #
705
781
  # See: HexaPDF::Content::Canvas#opacity
782
+ #
783
+ # Examples:
784
+ #
785
+ # #>pdf-composer100
786
+ # composer.text("This is some semi-transparent text", fill_alpha: 0.5)
706
787
 
707
788
  ##
708
789
  # :method: stroke_color
@@ -712,6 +793,12 @@ module HexaPDF
712
793
  # The color used for stroking (e.g. text outlines), defaults to black.
713
794
  #
714
795
  # See: HexaPDF::Content::Canvas#stroke_color
796
+ #
797
+ # Examples:
798
+ #
799
+ # #>pdf-composer100
800
+ # composer.text("Stroked text", font_size: 40, stroke_color: "red",
801
+ # text_rendering_mode: :stroke)
715
802
 
716
803
  ##
717
804
  # :method: stroke_alpha
@@ -722,6 +809,12 @@ module HexaPDF
722
809
  # 100% opaque).
723
810
  #
724
811
  # See: HexaPDF::Content::Canvas#opacity
812
+ #
813
+ # Examples:
814
+ #
815
+ # #>pdf-composer100
816
+ # composer.text("Stroked text", font_size: 40, stroke_alpha: 0.5,
817
+ # text_rendering_mode: :stroke)
725
818
 
726
819
  ##
727
820
  # :method: stroke_width
@@ -731,6 +824,12 @@ module HexaPDF
731
824
  # The line width used for stroking operations (e.g. text outlines), defaults to 1.
732
825
  #
733
826
  # See: HexaPDF::Content::Canvas#line_width
827
+ #
828
+ # Examples:
829
+ #
830
+ # #>pdf-composer100
831
+ # composer.text("Stroked text", font_size: 40, stroke_width: 2,
832
+ # text_rendering_mode: :stroke)
734
833
 
735
834
  ##
736
835
  # :method: stroke_cap_style
@@ -741,6 +840,12 @@ module HexaPDF
741
840
  # returned values is always a normalized line cap style value.
742
841
  #
743
842
  # See: HexaPDF::Content::Canvas#line_cap_style
843
+ #
844
+ # Examples:
845
+ #
846
+ # #>pdf-composer100
847
+ # composer.text("Stroked text", font_size: 40, stroke_cap_style: :round,
848
+ # text_rendering_mode: :stroke)
744
849
 
745
850
  ##
746
851
  # :method: stroke_join_style
@@ -751,6 +856,12 @@ module HexaPDF
751
856
  # The returned values is always a normalized line joine style value.
752
857
  #
753
858
  # See: HexaPDF::Content::Canvas#line_join_style
859
+ #
860
+ # Examples:
861
+ #
862
+ # #>pdf-composer100
863
+ # composer.text("Stroked text", font_size: 40, stroke_join_style: :bevel,
864
+ # text_rendering_mode: :stroke)
754
865
 
755
866
  ##
756
867
  # :method: stroke_miter_limit
@@ -761,6 +872,12 @@ module HexaPDF
761
872
  # :miter, defaults to 10.0.
762
873
  #
763
874
  # See: HexaPDF::Content::Canvas#miter_limit
875
+ #
876
+ # Examples:
877
+ #
878
+ # #>pdf-composer100
879
+ # composer.text("Stroked text", font_size: 40, stroke_join_style: :bevel,
880
+ # stroke_miter_limit: 1, text_rendering_mode: :stroke)
764
881
 
765
882
  ##
766
883
  # :method: stroke_dash_pattern
@@ -771,6 +888,12 @@ module HexaPDF
771
888
  # line.
772
889
  #
773
890
  # See: HexaPDF::Content::Canvas#line_dash_pattern
891
+ #
892
+ # Examples:
893
+ #
894
+ # #>pdf-composer100
895
+ # composer.text("Stroked text", font_size: 40, stroke_dash_pattern: [4, 2],
896
+ # text_rendering_mode: :stroke)
774
897
 
775
898
  ##
776
899
  # :method: align
@@ -785,19 +908,42 @@ module HexaPDF
785
908
  # :center:: Center the text horizontally.
786
909
  # :right:: Right-align the text, i.e. the left side is rugged.
787
910
  # :justify:: Justify the text, except for those lines that end in a hard line break.
911
+ #
912
+ # Examples:
913
+ #
914
+ # #>pdf-composer100
915
+ # text = "Lorem ipsum dolor sit amet. " * 2
916
+ # composer.style(:base, border: {width: 1})
917
+ # composer.text(text, align: :left)
918
+ # composer.text(text, align: :center)
919
+ # composer.text(text, align: :right)
920
+ # composer.text(text, align: :justify)
788
921
 
789
922
  ##
790
923
  # :method: valign
791
924
  # :call-seq:
792
925
  # valign(direction = nil)
793
926
  #
794
- # The vertical alignment of items (normally text) inside a box, defaults to :top.
927
+ # The vertical alignment of items (normally text) inside a text box, defaults to :top.
928
+ #
929
+ # For :center and :bottom alignment the box will fill the whole available height. If this is
930
+ # not wanted, an explicit height will need to be set for the box.
931
+ #
932
+ # This property is ignored when using position :flow for a text box.
795
933
  #
796
934
  # Possible values:
797
935
  #
798
936
  # :top:: Vertically align the items to the top of the box.
799
937
  # :center:: Vertically align the items in the center of the box.
800
938
  # :bottom:: Vertically align the items to the bottom of the box.
939
+ #
940
+ # Examples:
941
+ #
942
+ # #>pdf-composer100
943
+ # composer.style(:base, border: {width: 1})
944
+ # composer.text("Top aligned", height: 20, valign: :top)
945
+ # composer.text("Center aligned", height: 20, valign: :center)
946
+ # composer.text("Bottom aligned", valign: :bottom)
801
947
 
802
948
  ##
803
949
  # :method: text_indent
@@ -805,6 +951,12 @@ module HexaPDF
805
951
  # text_indent(amount = nil)
806
952
  #
807
953
  # The indentation to be used for the first line of a sequence of text lines, defaults to 0.
954
+ #
955
+ # Examples:
956
+ #
957
+ # #>pdf-composer100
958
+ # composer.text("This is some longer text that wraps around in two lines.",
959
+ # text_indent: 10)
808
960
 
809
961
  ##
810
962
  # :method: line_spacing
@@ -819,7 +971,20 @@ module HexaPDF
819
971
  # * Using two positional arguments +type+ and +value+.
820
972
  # * Or a hash with the keys +type+ and +value+.
821
973
  #
974
+ # Note that the last line has no additional spacing after it by default. Set #last_line_gap
975
+ # for adding such a spacing.
976
+ #
822
977
  # See LineSpacing for supported types of line spacing.
978
+ #
979
+ # Examples:
980
+ #
981
+ # #>pdf-composer100
982
+ # composer.text("This is some longer text that wraps around in two lines.",
983
+ # line_spacing: 1.5)
984
+ # composer.text("This is some longer text that wraps around in two lines.",
985
+ # line_spacing: :double)
986
+ # composer.text("This is some longer text that wraps around in two lines.",
987
+ # line_spacing: {type: :proportional, value: 1.2})
823
988
 
824
989
  ##
825
990
  # :method: last_line_gap
@@ -827,6 +992,13 @@ module HexaPDF
827
992
  # last_line_gap(enable = false)
828
993
  #
829
994
  # Add an appropriately sized gap after the last line of text if enabled, defaults to false.
995
+ #
996
+ # Examples:
997
+ #
998
+ # #>pdf-composer100
999
+ # composer.text("This is some longer text that wraps around in two lines.",
1000
+ # line_spacing: 1.5, last_line_gap: true)
1001
+ # composer.text("There is spacing above this line due to last_line_gap.")
830
1002
 
831
1003
  ##
832
1004
  # :method: background_color
@@ -834,6 +1006,11 @@ module HexaPDF
834
1006
  # background_color(color = nil)
835
1007
  #
836
1008
  # The color used for backgrounds, defaults to +nil+ (i.e. no background).
1009
+ #
1010
+ # Examples:
1011
+ #
1012
+ # #>pdf-composer100
1013
+ # composer.text("Some text here", background_color: "lightgrey")
837
1014
 
838
1015
  ##
839
1016
  # :method: background_alpha
@@ -844,6 +1021,11 @@ module HexaPDF
844
1021
  # opaque).
845
1022
  #
846
1023
  # See: HexaPDF::Content::Canvas#opacity
1024
+ #
1025
+ # Examples:
1026
+ #
1027
+ # #>pdf-composer100
1028
+ # composer.text("Some text here", background_color: "red", background_alpha: 0.5)
847
1029
 
848
1030
  ##
849
1031
  # :method: padding
@@ -851,6 +1033,13 @@ module HexaPDF
851
1033
  # padding(value = nil)
852
1034
  #
853
1035
  # The padding between the border and the contents, defaults to 0 for all four sides.
1036
+ #
1037
+ # See Style::Quad#set for information on how to set the values.
1038
+ #
1039
+ # Examples:
1040
+ #
1041
+ # #>pdf-composer100
1042
+ # composer.text("Some text here", padding: 10, border: {width: 1})
854
1043
 
855
1044
  ##
856
1045
  # :method: margin
@@ -858,29 +1047,83 @@ module HexaPDF
858
1047
  # margin(value = nil)
859
1048
  #
860
1049
  # The margin around a box, defaults to 0 for all four sides.
1050
+ #
1051
+ # See Style::Quad#set for information on how to set the values.
1052
+ #
1053
+ # Examples:
1054
+ #
1055
+ # #>pdf-composer100
1056
+ # composer.text("Some text here", margin: [5, 10], position: :float,
1057
+ # border: {width: 1})
1058
+ # composer.text("Text starts after floating box and continues below it, " \
1059
+ # "respecting the margin.", position: :flow)
861
1060
 
862
1061
  ##
863
1062
  # :method: border
864
1063
  # :call-seq:
865
1064
  # border(value = nil)
866
1065
  #
867
- # The border around the contents, defaults to no border.
1066
+ # The border around the contents, defaults to no border for all four sides.
1067
+ #
1068
+ # The value has to be a hash containing any of the keys :width, :color and :style. The width,
1069
+ # color and style of the border can be set independently for each side (see Style::Quad#set).
1070
+ #
1071
+ # See Border for more details.
1072
+ #
1073
+ # Examples:
1074
+ #
1075
+ # #>pdf-composer100
1076
+ # composer.text("Some text here", border: {
1077
+ # width: [6, 3],
1078
+ # color: ["green", "blue", "orange"],
1079
+ # style: [:solid, :dashed]
1080
+ # })
868
1081
 
869
1082
  ##
870
1083
  # :method: overlays
871
1084
  # :call-seq:
872
1085
  # overlays(layers = nil)
873
1086
  #
874
- # A Layers object containing all the layers that should be drawn over the box; defaults to no
875
- # layers being drawn.
1087
+ # A Style::Layers object containing all the layers that should be drawn over the box; defaults
1088
+ # to no layers being drawn.
1089
+ #
1090
+ # The +layers+ argument needs to be an array of layer objects. To define a layer either use a
1091
+ # callable object taking the canvas and the box as arguments; or use a pre-defined layer using
1092
+ # an array of the form [:layer_name, **options]. See Style::Layers for details.
1093
+ #
1094
+ # Examples:
1095
+ #
1096
+ # #>pdf-composer100
1097
+ # composer.text("Some text here", overlays: [
1098
+ # lambda do |canvas, box|
1099
+ # canvas.stroke_color("red").opacity(stroke_alpha: 0.5).
1100
+ # line_width(5).line(0, 0, box.width, box.height).stroke
1101
+ # end,
1102
+ # [:link, uri: "https://hexapdf.gettalong.org"]
1103
+ # ])
876
1104
 
877
1105
  ##
878
1106
  # :method: underlays
879
1107
  # :call-seq:
880
1108
  # underlays(layers = nil)
881
1109
  #
882
- # A Layers object containing all the layers that should be drawn under the box; defaults to no
883
- # layers being drawn.
1110
+ # A Style::Layers object containing all the layers that should be drawn under the box;
1111
+ # defaults to no layers being drawn.
1112
+ #
1113
+ # The +layers+ argument needs to be an array of layer objects. To define a layer either use a
1114
+ # callable object taking the canvas and the box as arguments; or use a pre-defined layer using
1115
+ # an array of the form [:layer_name, **options]. See Style::Layers for details.
1116
+ #
1117
+ # Examples:
1118
+ #
1119
+ # #>pdf-composer100
1120
+ # composer.text("Some text here", underlays: [
1121
+ # lambda do |canvas, box|
1122
+ # canvas.stroke_color("red").opacity(stroke_alpha: 0.5).
1123
+ # line_width(5).line(0, 0, box.width, box.height).stroke
1124
+ # end,
1125
+ # [:link, uri: "https://hexapdf.gettalong.org"]
1126
+ # ])
884
1127
 
885
1128
  ##
886
1129
  # :method: position
@@ -904,6 +1147,8 @@ module HexaPDF
904
1147
  #
905
1148
  # :absolute:: Position the box at an absolute position relative to the frame. The coordinates
906
1149
  # are given via the position hint.
1150
+ #
1151
+ # See #position_hint for examples
907
1152
 
908
1153
  ##
909
1154
  # :method: position_hint
@@ -918,19 +1163,47 @@ module HexaPDF
918
1163
  # :default::
919
1164
  #
920
1165
  # :left:: (default) Align the box to the left side of the available region.
921
- # :right:: Align the box to the right side of the available region.
922
1166
  # :center:: Horizontally center the box in the available region.
1167
+ # :right:: Align the box to the right side of the available region.
1168
+ #
1169
+ # Examples:
1170
+ #
1171
+ # #>pdf-composer100
1172
+ # composer.text("Left", border: {width: 1})
1173
+ # draw_current_frame_shape("red")
1174
+ # composer.text("Center", position_hint: :center, border: {width: 1})
1175
+ # draw_current_frame_shape("blue")
1176
+ # composer.text("Right", position_hint: :right, border: {width: 1})
1177
+ # draw_current_frame_shape("green")
923
1178
  #
924
1179
  # :float::
925
1180
  #
926
1181
  # :left:: (default) Float the box to the left side of the available region.
1182
+ # :center:: Float the box to the center of the available region.
927
1183
  # :right:: Float the box to the right side of the available region.
928
1184
  #
1185
+ # Examples:
1186
+ #
1187
+ # #>pdf-composer100
1188
+ # composer.style(:base, position: :float, border: {width: 1})
1189
+ # composer.text("Left", position_hint: :left)
1190
+ # draw_current_frame_shape("red")
1191
+ # composer.text("Center", position_hint: :center)
1192
+ # draw_current_frame_shape("blue")
1193
+ # composer.text("Right", position_hint: :right)
1194
+ # draw_current_frame_shape("green")
1195
+ #
929
1196
  # :absolute::
930
1197
  #
931
1198
  # An array with the x- and y-coordinates of the bottom left corner of the absolutely
932
1199
  # positioned box. The coordinates are taken as being relative to the bottom left corner of
933
1200
  # the frame into which the box is drawn.
1201
+ #
1202
+ # Examples:
1203
+ #
1204
+ # #>pdf-composer100
1205
+ # composer.text("Absolute", position: :absolute, position_hint: [30, 40])
1206
+ # draw_current_frame_shape("red")
934
1207
 
935
1208
  [
936
1209
  [:font, "raise HexaPDF::Error, 'No font set'"],
@@ -77,8 +77,16 @@ module HexaPDF
77
77
  height = (@initial_height > 0 ? @initial_height : available_height) - @height
78
78
  @tl.fit(@items, width, height)
79
79
  end
80
- @width += (@initial_width > 0 ? width : @result.lines.max_by(&:width)&.width || 0)
81
- @height += (@initial_height > 0 ? height : @result.height)
80
+ @width += if @initial_width > 0 || style.align == :center || style.align == :right
81
+ width
82
+ else
83
+ @result.lines.max_by(&:width)&.width || 0
84
+ end
85
+ @height += if @initial_height > 0 || style.valign == :center || style.valign == :bottom
86
+ height
87
+ else
88
+ @result.height
89
+ end
82
90
  if style.last_line_gap && @result.lines.last
83
91
  @height += style.line_spacing.gap(@result.lines.last, @result.lines.last)
84
92
  end
@@ -825,7 +825,12 @@ module HexaPDF
825
825
  end
826
826
 
827
827
  unless lines.empty?
828
- lines.first.y_offset += initial_baseline_offset(lines, height, actual_height)
828
+ # Apply baseline offset only for non-variable width text
829
+ lines.first.y_offset += if width_block.arity == 1
830
+ lines.first.y_max
831
+ else
832
+ initial_baseline_offset(lines, height, actual_height)
833
+ end
829
834
  end
830
835
 
831
836
  Result.new(status, lines, rest)
@@ -82,6 +82,7 @@ module HexaPDF
82
82
  @loader = xref_section && (block || loader)
83
83
  @xref_section = xref_section || XRefSection.new
84
84
  @objects = HexaPDF::Utils::ObjectHash.new
85
+ @all_objects_loaded = false
85
86
  end
86
87
 
87
88
  # Returns the next free object number for adding an object to this revision.
@@ -209,7 +210,7 @@ module HexaPDF
209
210
  def each(only_loaded: false)
210
211
  return to_enum(__method__, only_loaded: only_loaded) unless block_given?
211
212
 
212
- if defined?(@all_objects_loaded) || only_loaded
213
+ if @all_objects_loaded || only_loaded
213
214
  @objects.each {|_oid, _gen, data| yield(data) }
214
215
  else
215
216
  seen = {}
@@ -256,6 +257,12 @@ module HexaPDF
256
257
  self
257
258
  end
258
259
 
260
+ # Resets the revision by deleting all loaded and added objects from it.
261
+ def reset_objects
262
+ @objects = HexaPDF::Utils::ObjectHash.new
263
+ @all_objects_loaded = false
264
+ end
265
+
259
266
  private
260
267
 
261
268
  # Loads a single object from the associated cross-reference section.