hexapdf 0.17.1 → 0.17.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 +4 -4
- data/CHANGELOG.md +1024 -0
- data/LICENSE +29 -0
- data/README.md +129 -0
- data/Rakefile +109 -0
- data/agpl-3.0.txt +661 -0
- data/examples/001-hello_world.rb +16 -0
- data/examples/002-graphics.rb +275 -0
- data/examples/003-arcs.rb +50 -0
- data/examples/004-optimizing.rb +23 -0
- data/examples/005-merging.rb +27 -0
- data/examples/006-standard_pdf_fonts.rb +73 -0
- data/examples/007-truetype.rb +42 -0
- data/examples/008-show_char_bboxes.rb +55 -0
- data/examples/009-text_layouter_alignment.rb +47 -0
- data/examples/010-text_layouter_inline_boxes.rb +64 -0
- data/examples/011-text_layouter_line_wrapping.rb +57 -0
- data/examples/012-text_layouter_styling.rb +122 -0
- data/examples/013-text_layouter_shapes.rb +176 -0
- data/examples/014-text_in_polygon.rb +60 -0
- data/examples/015-boxes.rb +76 -0
- data/examples/016-frame_automatic_box_placement.rb +90 -0
- data/examples/017-frame_text_flow.rb +60 -0
- data/examples/018-composer.rb +44 -0
- data/examples/019-acro_form.rb +88 -0
- data/examples/emoji-smile.png +0 -0
- data/examples/emoji-wink.png +0 -0
- data/examples/machupicchu.jpg +0 -0
- data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +66 -0
- data/lib/hexapdf/content/graphic_object/geom2d.rb +13 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/data/aes-test-vectors/CBCGFSbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCGFSbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCKeySbox-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarKey-256-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-128-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-192-encrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-decrypt.data.gz +0 -0
- data/test/data/aes-test-vectors/CBCVarTxt-256-encrypt.data.gz +0 -0
- data/test/data/fonts/Ubuntu-Title.ttf +0 -0
- data/test/data/images/cmyk.jpg +0 -0
- data/test/data/images/fillbytes.jpg +0 -0
- data/test/data/images/gray.jpg +0 -0
- data/test/data/images/greyscale-1bit.png +0 -0
- data/test/data/images/greyscale-2bit.png +0 -0
- data/test/data/images/greyscale-4bit.png +0 -0
- data/test/data/images/greyscale-8bit.png +0 -0
- data/test/data/images/greyscale-alpha-8bit.png +0 -0
- data/test/data/images/greyscale-trns-8bit.png +0 -0
- data/test/data/images/greyscale-with-gamma1.0.png +0 -0
- data/test/data/images/greyscale-with-gamma1.5.png +0 -0
- data/test/data/images/indexed-1bit.png +0 -0
- data/test/data/images/indexed-2bit.png +0 -0
- data/test/data/images/indexed-4bit.png +0 -0
- data/test/data/images/indexed-8bit.png +0 -0
- data/test/data/images/indexed-alpha-4bit.png +0 -0
- data/test/data/images/indexed-alpha-8bit.png +0 -0
- data/test/data/images/rgb.jpg +0 -0
- data/test/data/images/truecolour-8bit.png +0 -0
- data/test/data/images/truecolour-alpha-8bit.png +0 -0
- data/test/data/images/truecolour-gama-chrm-8bit.png +0 -0
- data/test/data/images/truecolour-srgb-8bit.png +0 -0
- data/test/data/images/ycck.jpg +0 -0
- data/test/data/minimal.pdf +44 -0
- data/test/data/standard-security-handler/README +9 -0
- data/test/data/standard-security-handler/bothpwd-aes-128bit-V4.pdf +44 -0
- data/test/data/standard-security-handler/bothpwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/bothpwd-arc4-40bit-V1.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-aes-256bit-V5.pdf +0 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/nopwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-aes-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/ownerpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V2.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/ownerpwd-arc4-40bit-V1.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-128bit-V4.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-aes-256bit-V5.pdf +43 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V2.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-128bit-V4.pdf +0 -0
- data/test/data/standard-security-handler/userpwd-arc4-40bit-V1.pdf +43 -0
- data/test/hexapdf/common_tokenizer_tests.rb +236 -0
- data/test/hexapdf/content/common.rb +39 -0
- data/test/hexapdf/content/graphic_object/test_arc.rb +102 -0
- data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +90 -0
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
- data/test/hexapdf/content/graphic_object/test_solid_arc.rb +86 -0
- data/test/hexapdf/content/test_canvas.rb +1279 -0
- data/test/hexapdf/content/test_color_space.rb +176 -0
- data/test/hexapdf/content/test_graphics_state.rb +151 -0
- data/test/hexapdf/content/test_operator.rb +619 -0
- data/test/hexapdf/content/test_parser.rb +99 -0
- data/test/hexapdf/content/test_processor.rb +163 -0
- data/test/hexapdf/content/test_transformation_matrix.rb +64 -0
- data/test/hexapdf/document/test_files.rb +72 -0
- data/test/hexapdf/document/test_fonts.rb +60 -0
- data/test/hexapdf/document/test_images.rb +72 -0
- data/test/hexapdf/document/test_pages.rb +130 -0
- data/test/hexapdf/encryption/common.rb +87 -0
- data/test/hexapdf/encryption/test_aes.rb +129 -0
- data/test/hexapdf/encryption/test_arc4.rb +39 -0
- data/test/hexapdf/encryption/test_fast_aes.rb +17 -0
- data/test/hexapdf/encryption/test_fast_arc4.rb +12 -0
- data/test/hexapdf/encryption/test_identity.rb +21 -0
- data/test/hexapdf/encryption/test_ruby_aes.rb +23 -0
- data/test/hexapdf/encryption/test_ruby_arc4.rb +20 -0
- data/test/hexapdf/encryption/test_security_handler.rb +380 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +322 -0
- data/test/hexapdf/filter/common.rb +53 -0
- data/test/hexapdf/filter/test_ascii85_decode.rb +59 -0
- data/test/hexapdf/filter/test_ascii_hex_decode.rb +38 -0
- data/test/hexapdf/filter/test_crypt.rb +21 -0
- data/test/hexapdf/filter/test_encryption.rb +24 -0
- data/test/hexapdf/filter/test_flate_decode.rb +44 -0
- data/test/hexapdf/filter/test_lzw_decode.rb +52 -0
- data/test/hexapdf/filter/test_predictor.rb +219 -0
- data/test/hexapdf/filter/test_run_length_decode.rb +32 -0
- data/test/hexapdf/font/cmap/test_parser.rb +102 -0
- data/test/hexapdf/font/cmap/test_writer.rb +66 -0
- data/test/hexapdf/font/encoding/test_base.rb +45 -0
- data/test/hexapdf/font/encoding/test_difference_encoding.rb +29 -0
- data/test/hexapdf/font/encoding/test_glyph_list.rb +59 -0
- data/test/hexapdf/font/encoding/test_zapf_dingbats_encoding.rb +16 -0
- data/test/hexapdf/font/test_cmap.rb +104 -0
- data/test/hexapdf/font/test_encoding.rb +27 -0
- data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +186 -0
- data/test/hexapdf/font/test_type1_wrapper.rb +107 -0
- data/test/hexapdf/font/true_type/common.rb +17 -0
- data/test/hexapdf/font/true_type/table/common.rb +27 -0
- data/test/hexapdf/font/true_type/table/test_cmap.rb +47 -0
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +141 -0
- data/test/hexapdf/font/true_type/table/test_directory.rb +30 -0
- data/test/hexapdf/font/true_type/table/test_glyf.rb +58 -0
- data/test/hexapdf/font/true_type/table/test_head.rb +56 -0
- data/test/hexapdf/font/true_type/table/test_hhea.rb +26 -0
- data/test/hexapdf/font/true_type/table/test_hmtx.rb +30 -0
- data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
- data/test/hexapdf/font/true_type/table/test_loca.rb +33 -0
- data/test/hexapdf/font/true_type/table/test_maxp.rb +50 -0
- data/test/hexapdf/font/true_type/table/test_name.rb +76 -0
- data/test/hexapdf/font/true_type/table/test_os2.rb +55 -0
- data/test/hexapdf/font/true_type/table/test_post.rb +78 -0
- data/test/hexapdf/font/true_type/test_builder.rb +42 -0
- data/test/hexapdf/font/true_type/test_font.rb +116 -0
- data/test/hexapdf/font/true_type/test_optimizer.rb +26 -0
- data/test/hexapdf/font/true_type/test_subsetter.rb +73 -0
- data/test/hexapdf/font/true_type/test_table.rb +48 -0
- data/test/hexapdf/font/type1/common.rb +6 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +65 -0
- data/test/hexapdf/font/type1/test_font.rb +104 -0
- data/test/hexapdf/font/type1/test_font_metrics.rb +22 -0
- data/test/hexapdf/font/type1/test_pfb_parser.rb +37 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +43 -0
- data/test/hexapdf/font_loader/test_from_file.rb +36 -0
- data/test/hexapdf/font_loader/test_standard14.rb +33 -0
- data/test/hexapdf/image_loader/test_jpeg.rb +93 -0
- data/test/hexapdf/image_loader/test_pdf.rb +47 -0
- data/test/hexapdf/image_loader/test_png.rb +259 -0
- data/test/hexapdf/layout/test_box.rb +154 -0
- data/test/hexapdf/layout/test_frame.rb +350 -0
- data/test/hexapdf/layout/test_image_box.rb +73 -0
- data/test/hexapdf/layout/test_inline_box.rb +71 -0
- data/test/hexapdf/layout/test_line.rb +206 -0
- data/test/hexapdf/layout/test_style.rb +790 -0
- data/test/hexapdf/layout/test_text_box.rb +140 -0
- data/test/hexapdf/layout/test_text_fragment.rb +375 -0
- data/test/hexapdf/layout/test_text_layouter.rb +758 -0
- data/test/hexapdf/layout/test_text_shaper.rb +62 -0
- data/test/hexapdf/layout/test_width_from_polygon.rb +109 -0
- data/test/hexapdf/task/test_dereference.rb +51 -0
- data/test/hexapdf/task/test_optimize.rb +162 -0
- data/test/hexapdf/test_composer.rb +258 -0
- data/test/hexapdf/test_configuration.rb +93 -0
- data/test/hexapdf/test_data_dir.rb +32 -0
- data/test/hexapdf/test_dictionary.rb +340 -0
- data/test/hexapdf/test_dictionary_fields.rb +269 -0
- data/test/hexapdf/test_document.rb +641 -0
- data/test/hexapdf/test_filter.rb +100 -0
- data/test/hexapdf/test_importer.rb +106 -0
- data/test/hexapdf/test_object.rb +258 -0
- data/test/hexapdf/test_parser.rb +645 -0
- data/test/hexapdf/test_pdf_array.rb +169 -0
- data/test/hexapdf/test_rectangle.rb +73 -0
- data/test/hexapdf/test_reference.rb +50 -0
- data/test/hexapdf/test_revision.rb +188 -0
- data/test/hexapdf/test_revisions.rb +196 -0
- data/test/hexapdf/test_serializer.rb +195 -0
- data/test/hexapdf/test_stream.rb +274 -0
- data/test/hexapdf/test_tokenizer.rb +80 -0
- data/test/hexapdf/test_type.rb +18 -0
- data/test/hexapdf/test_writer.rb +140 -0
- data/test/hexapdf/test_xref_section.rb +61 -0
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +795 -0
- data/test/hexapdf/type/acro_form/test_button_field.rb +308 -0
- data/test/hexapdf/type/acro_form/test_choice_field.rb +220 -0
- data/test/hexapdf/type/acro_form/test_field.rb +259 -0
- data/test/hexapdf/type/acro_form/test_form.rb +357 -0
- data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
- data/test/hexapdf/type/acro_form/test_text_field.rb +201 -0
- data/test/hexapdf/type/acro_form/test_variable_text_field.rb +88 -0
- data/test/hexapdf/type/actions/test_launch.rb +24 -0
- data/test/hexapdf/type/actions/test_uri.rb +23 -0
- data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
- data/test/hexapdf/type/annotations/test_text.rb +34 -0
- data/test/hexapdf/type/annotations/test_widget.rb +225 -0
- data/test/hexapdf/type/test_annotation.rb +97 -0
- data/test/hexapdf/type/test_catalog.rb +48 -0
- data/test/hexapdf/type/test_cid_font.rb +61 -0
- data/test/hexapdf/type/test_file_specification.rb +141 -0
- data/test/hexapdf/type/test_font.rb +67 -0
- data/test/hexapdf/type/test_font_descriptor.rb +61 -0
- data/test/hexapdf/type/test_font_simple.rb +176 -0
- data/test/hexapdf/type/test_font_true_type.rb +31 -0
- data/test/hexapdf/type/test_font_type0.rb +120 -0
- data/test/hexapdf/type/test_font_type1.rb +142 -0
- data/test/hexapdf/type/test_font_type3.rb +26 -0
- data/test/hexapdf/type/test_form.rb +120 -0
- data/test/hexapdf/type/test_image.rb +261 -0
- data/test/hexapdf/type/test_info.rb +9 -0
- data/test/hexapdf/type/test_object_stream.rb +117 -0
- data/test/hexapdf/type/test_page.rb +598 -0
- data/test/hexapdf/type/test_page_tree_node.rb +315 -0
- data/test/hexapdf/type/test_resources.rb +209 -0
- data/test/hexapdf/type/test_trailer.rb +116 -0
- data/test/hexapdf/type/test_xref_stream.rb +143 -0
- data/test/hexapdf/utils/test_bit_field.rb +63 -0
- data/test/hexapdf/utils/test_bit_stream.rb +69 -0
- data/test/hexapdf/utils/test_graphics_helpers.rb +37 -0
- data/test/hexapdf/utils/test_lru_cache.rb +22 -0
- data/test/hexapdf/utils/test_object_hash.rb +120 -0
- data/test/hexapdf/utils/test_pdf_doc_encoding.rb +18 -0
- data/test/hexapdf/utils/test_sorted_tree_node.rb +239 -0
- data/test/test_helper.rb +58 -0
- metadata +263 -3
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# # Graphics Primitives
|
|
2
|
+
#
|
|
3
|
+
# This example shows many of the operations that the canvas implementation
|
|
4
|
+
# allows.
|
|
5
|
+
#
|
|
6
|
+
# Note that the PDF canvas has its origin in the bottom left corner of the page.
|
|
7
|
+
# This means the coordinate (100, 50) is 100 PDF points from the left side and
|
|
8
|
+
# 50 PDF points from the bottom. One PDF point is equal to 1/72 inch.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# : `ruby graphics.rb`
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
require 'hexapdf'
|
|
15
|
+
|
|
16
|
+
doc = HexaPDF::Document.new
|
|
17
|
+
page = doc.pages.add
|
|
18
|
+
canvas = page.canvas
|
|
19
|
+
|
|
20
|
+
# Draws the shape that is used to showcase the transformations in the given
|
|
21
|
+
# color.
|
|
22
|
+
def transformation_shape(canvas, *color)
|
|
23
|
+
canvas.stroke_color(*color)
|
|
24
|
+
canvas.polygon(0, 0, 0, 80, 30, 50, 60, 80, 60, 0, 30, 30)
|
|
25
|
+
canvas.line(-30, 0, 30, 0)
|
|
26
|
+
canvas.line(0, 30, 0, -30)
|
|
27
|
+
canvas.stroke
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Basic transformations: translate, scale, rotate, skew
|
|
31
|
+
canvas.translate(0, 710) do
|
|
32
|
+
normal_color = [0.7, 0.7, 0.3]
|
|
33
|
+
transformed_color = [0.3, 0.7, 0.7]
|
|
34
|
+
|
|
35
|
+
canvas.translate(50, 0) do
|
|
36
|
+
transformation_shape(canvas, normal_color)
|
|
37
|
+
canvas.translate(40, 40) { transformation_shape(canvas, transformed_color) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
canvas.translate(180, 0) do
|
|
41
|
+
transformation_shape(canvas, normal_color)
|
|
42
|
+
canvas.scale(1.7, 1.3) { transformation_shape(canvas, transformed_color) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
canvas.translate(330, 0) do
|
|
46
|
+
transformation_shape(canvas, normal_color)
|
|
47
|
+
canvas.rotate(30) { transformation_shape(canvas, transformed_color) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
canvas.translate(430, 0) do
|
|
51
|
+
transformation_shape(canvas, normal_color)
|
|
52
|
+
canvas.skew(15, 30) { transformation_shape(canvas, transformed_color) }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Draws a thin white line over a thick black line.
|
|
57
|
+
def dual_lines(canvas)
|
|
58
|
+
canvas.stroke_color(0)
|
|
59
|
+
canvas.line_width = 15
|
|
60
|
+
yield
|
|
61
|
+
canvas.stroke
|
|
62
|
+
canvas.stroke_color(1.0)
|
|
63
|
+
canvas.line_width = 1
|
|
64
|
+
yield
|
|
65
|
+
canvas.stroke
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Graphics state: line width, line cap style, line join style, miter limit,
|
|
69
|
+
# line dash pattern
|
|
70
|
+
canvas.translate(0, 550) do
|
|
71
|
+
canvas.translate(50, 0) do
|
|
72
|
+
[1, 5, 10, 15].each_with_index do |i, index|
|
|
73
|
+
canvas.stroke_color(0)
|
|
74
|
+
canvas.line_width(i)
|
|
75
|
+
canvas.line(20 * index, 0, 20 * index, 100)
|
|
76
|
+
canvas.stroke
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
canvas.translate(150, 0) do
|
|
81
|
+
0.upto(2) do |i|
|
|
82
|
+
canvas.line_cap_style = i
|
|
83
|
+
dual_lines(canvas) { canvas.line(20 * i, 0, 20 * i, 100) }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
canvas.translate(230, 0) do
|
|
88
|
+
0.upto(2) do |i|
|
|
89
|
+
canvas.line_join_style = i
|
|
90
|
+
dual_lines(canvas) { canvas.polyline(0, 30 * i, 40, 50 + 30 * i, 80, 30 * i) }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
canvas.translate(350, 0) do
|
|
95
|
+
canvas.line_join_style = :miter
|
|
96
|
+
canvas.miter_limit = 1
|
|
97
|
+
dual_lines(canvas) { canvas.polyline(0, 0, 20, 80, 40, 0) }
|
|
98
|
+
canvas.miter_limit = 10
|
|
99
|
+
dual_lines(canvas) { canvas.polyline(60, 0, 80, 80, 100, 0) }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
canvas.translate(490, 0) do
|
|
103
|
+
canvas.line_width(1)
|
|
104
|
+
[[[1, 1]],
|
|
105
|
+
[[3, 1]],
|
|
106
|
+
[[3, 3]],
|
|
107
|
+
[[5, 1, 1, 1, 1, 1]],
|
|
108
|
+
[[3, 5], 6]].each_with_index do |(value, phase), index|
|
|
109
|
+
canvas.line_dash_pattern(value, phase || 0)
|
|
110
|
+
canvas.line(20 * index, 0, 20 * index, 100)
|
|
111
|
+
canvas.stroke
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Basic shapes: line, polyline, (rounded) rectangle, (rounded) polygon, circle, ellipse
|
|
117
|
+
canvas.translate(0, 420) do
|
|
118
|
+
canvas.line(50, 0, 50, 100)
|
|
119
|
+
canvas.polyline(80, 0, 80, 20, 70, 30, 90, 40, 70, 50, 90, 60, 70, 70, 80, 80, 80, 100)
|
|
120
|
+
canvas.rectangle(110, 0, 50, 100)
|
|
121
|
+
canvas.rectangle(180, 0, 50, 100, radius: 20)
|
|
122
|
+
canvas.polygon(250, 0, 250, 100, 280, 70, 310, 100, 310, 0, 280, 30)
|
|
123
|
+
canvas.polygon(330, 0, 330, 100, 360, 70, 390, 100, 390, 0, 360, 30, radius: 20)
|
|
124
|
+
canvas.circle(440, 50, 30)
|
|
125
|
+
canvas.ellipse(520, 50, a: 30, b: 15, inclination: 45)
|
|
126
|
+
canvas.stroke
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Various arcs w/wo filling, using the Canvas#arc method as well as directly
|
|
130
|
+
# working with the arc objects
|
|
131
|
+
canvas.translate(0, 320) do
|
|
132
|
+
canvas.arc(50, 50, a: 10, start_angle: -60, end_angle: 115)
|
|
133
|
+
canvas.arc(100, 50, a: 40, b: 20, start_angle: -60, end_angle: 115)
|
|
134
|
+
canvas.arc(180, 50, a: 40, b: 20, start_angle: -60, end_angle: 115, inclination: 45)
|
|
135
|
+
canvas.stroke
|
|
136
|
+
|
|
137
|
+
canvas.fill_color(0.4, 0.3, 0.4)
|
|
138
|
+
canvas.arc(250, 50, a: 10, start_angle: -60, end_angle: 115)
|
|
139
|
+
canvas.arc(300, 50, a: 40, b: 20, start_angle: -60, end_angle: 115)
|
|
140
|
+
canvas.arc(380, 50, a: 40, b: 20, start_angle: -60, end_angle: 115, inclination: 45)
|
|
141
|
+
canvas.fill
|
|
142
|
+
|
|
143
|
+
arc = canvas.graphic_object(:arc, cx: 450, cy: 50, a: 30, b: 30,
|
|
144
|
+
start_angle: -30, end_angle: 105)
|
|
145
|
+
canvas.fill_color(0.4, 0.3, 0.4)
|
|
146
|
+
canvas.move_to(450, 50)
|
|
147
|
+
canvas.line_to(*arc.start_point)
|
|
148
|
+
arc.curves.each {|x, y, hash| canvas.curve_to(x, y, **hash)}
|
|
149
|
+
canvas.fill
|
|
150
|
+
arc.configure(start_angle: 105, end_angle: -30)
|
|
151
|
+
canvas.fill_color(0.3, 0.7, 0.7)
|
|
152
|
+
canvas.move_to(450, 50)
|
|
153
|
+
canvas.line_to(*arc.start_point)
|
|
154
|
+
arc.curves.each {|x, y, hash| canvas.curve_to(x, y, **hash)}
|
|
155
|
+
canvas.fill
|
|
156
|
+
|
|
157
|
+
arc = canvas.graphic_object(:arc, cx: 530, cy: 50, a: 40, b: 20,
|
|
158
|
+
start_angle: -30, end_angle: 105)
|
|
159
|
+
canvas.fill_color(0.4, 0.3, 0.4)
|
|
160
|
+
canvas.move_to(530, 50)
|
|
161
|
+
canvas.line_to(*arc.start_point)
|
|
162
|
+
arc.curves.each {|x, y, hash| canvas.curve_to(x, y, **hash)}
|
|
163
|
+
canvas.fill
|
|
164
|
+
arc.configure(start_angle: 105, end_angle: -30)
|
|
165
|
+
canvas.fill_color(0.7, 0.7, 0.3)
|
|
166
|
+
canvas.move_to(530, 50)
|
|
167
|
+
canvas.line_to(*arc.start_point)
|
|
168
|
+
arc.curves.each {|x, y, hash| canvas.curve_to(x, y, **hash)}
|
|
169
|
+
canvas.fill
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Draws a circle and two half circles inside with different directions.
|
|
173
|
+
def shapes_to_paint(canvas)
|
|
174
|
+
canvas.line_width = 2
|
|
175
|
+
canvas.arc(50, 50, a: 50)
|
|
176
|
+
canvas.arc(50, 60, a: 25, end_angle: 180, clockwise: false)
|
|
177
|
+
canvas.arc(50, 40, a: 25, end_angle: 180, clockwise: true)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Draws arrows showing the direction of the #shapes_to_paint
|
|
181
|
+
def arrows(canvas)
|
|
182
|
+
canvas.line_width = 1
|
|
183
|
+
canvas.polyline(95, 45, 100, 50, 105, 45)
|
|
184
|
+
canvas.polyline(55, 105, 50, 100, 55, 95)
|
|
185
|
+
canvas.polyline(-5, 55, 0, 50, 5, 55)
|
|
186
|
+
canvas.polyline(45, 5, 50, 0, 45, -5)
|
|
187
|
+
canvas.polyline(55, 90, 50, 85, 55, 80)
|
|
188
|
+
canvas.polyline(55, 20, 50, 15, 55, 10)
|
|
189
|
+
canvas.stroke
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Path painting and clipping operations: stroke, close and stroke, fill nonzero,
|
|
193
|
+
# fill even-odd, fill nonzero and stroke, fill even-odd and stroke, close and
|
|
194
|
+
# fill nonzero and stroke, close fill even-odd and stroke, clip even-odd, clip
|
|
195
|
+
# nonzero
|
|
196
|
+
canvas.translate(0, 190) do
|
|
197
|
+
canvas.fill_color(0.3, 0.7, 0.7)
|
|
198
|
+
|
|
199
|
+
[
|
|
200
|
+
[:stroke], [:close_stroke], [:fill, :nonzero], [:fill, :even_odd],
|
|
201
|
+
[:fill_stroke, :nonzero], [:fill_stroke, :even_odd],
|
|
202
|
+
[:close_fill_stroke, :nonzero], [:close_fill_stroke, :even_odd]
|
|
203
|
+
].each_with_index do |op, index|
|
|
204
|
+
row = (1 - (index / 4))
|
|
205
|
+
column = index % 4
|
|
206
|
+
x = 50 + 80 * column
|
|
207
|
+
y = 80 * row
|
|
208
|
+
canvas.transform(0.6, 0, 0, 0.6, x, y) do
|
|
209
|
+
shapes_to_paint(canvas)
|
|
210
|
+
canvas.send(*op)
|
|
211
|
+
arrows(canvas)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
canvas.fill_color(0.7, 0.7, 0.3)
|
|
216
|
+
|
|
217
|
+
[:even_odd, :nonzero].each_with_index do |op, index|
|
|
218
|
+
canvas.translate(370 + 110 * index, 20) do
|
|
219
|
+
canvas.circle(50, 50, 50)
|
|
220
|
+
canvas.circle(50, 50, 20)
|
|
221
|
+
canvas.clip_path(op)
|
|
222
|
+
canvas.end_path
|
|
223
|
+
canvas.rectangle(0, 0, 100, 100, radius: 100)
|
|
224
|
+
canvas.fill_stroke
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Some composite shapes, an image and a form XObject
|
|
230
|
+
canvas.translate(0, 80) do
|
|
231
|
+
canvas.fill_color(0.3, 0.7, 0.7)
|
|
232
|
+
canvas.rectangle(50, 0, 80, 80, radius: 80)
|
|
233
|
+
canvas.fill
|
|
234
|
+
|
|
235
|
+
solid = canvas.graphic_object(:solid_arc, cx: 190, cy: 40, inner_a: 20, inner_b: 15,
|
|
236
|
+
outer_a: 40, outer_b: 30, start_angle: 10, end_angle: 130)
|
|
237
|
+
|
|
238
|
+
canvas.line_width(0.5)
|
|
239
|
+
canvas.opacity(fill_alpha: 0.5, stroke_alpha: 0.2) do
|
|
240
|
+
canvas.fill_color('AA8888').draw(solid).fill_stroke
|
|
241
|
+
canvas.fill_color('88AA88').draw(solid, start_angle: 130, end_angle: 220).fill_stroke
|
|
242
|
+
canvas.fill_color('8888AA').draw(solid, start_angle: 220, end_angle: 10).fill_stroke
|
|
243
|
+
|
|
244
|
+
solid.configure(inner_a: 0, inner_b: 0, outer_a: 40, outer_b: 40, cx: 290)
|
|
245
|
+
canvas.fill_color('AA8888').draw(solid, start_angle: 10, end_angle: 130).fill_stroke
|
|
246
|
+
canvas.fill_color('88AA88').draw(solid, start_angle: 130, end_angle: 220).fill_stroke
|
|
247
|
+
canvas.fill_color('8888AA').draw(solid, start_angle: 220, end_angle: 10).fill_stroke
|
|
248
|
+
|
|
249
|
+
canvas.image(File.join(__dir__, 'machupicchu.jpg'), at: [350, 0], height: 80)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# A simple rainbow color band
|
|
254
|
+
canvas.translate(0, 20) do
|
|
255
|
+
canvas.line_width = 6
|
|
256
|
+
freq = 0.1
|
|
257
|
+
0.upto(100) do |i|
|
|
258
|
+
r = Math.sin(freq * i) * 127 + 128
|
|
259
|
+
g = Math.sin(freq * i + 2) * 127 + 128
|
|
260
|
+
b = Math.sin(freq * i + 4) * 127 + 128
|
|
261
|
+
canvas.stroke_color(r.to_i, g.to_i, b.to_i)
|
|
262
|
+
canvas.line(50 + i * 5, 0, 50 + i * 5, 40)
|
|
263
|
+
canvas.stroke
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Reusing the already draw graphics for an XObject
|
|
268
|
+
# Note that converting the page to a form XObject automatically closes all open
|
|
269
|
+
# graphics states, therefore this can't be inside the above Canvas#translate
|
|
270
|
+
# call
|
|
271
|
+
form = doc.add(page.to_form_xobject(reference: false))
|
|
272
|
+
canvas.rectangle(480, 80, form.box.width * (100 / form.box.height.to_f), 100).stroke
|
|
273
|
+
canvas.xobject(form, at: [480, 80], height: 100)
|
|
274
|
+
|
|
275
|
+
doc.write('graphics.pdf', optimize: true)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# # Arcs and Solid Arcs
|
|
2
|
+
#
|
|
3
|
+
# This example shows how to use the graphic objects `:arc` and `:solid_arc` to
|
|
4
|
+
# draw simple pie charts.
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# : `ruby arcs.rb`
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
require 'hexapdf'
|
|
11
|
+
|
|
12
|
+
doc = HexaPDF::Document.new
|
|
13
|
+
page = doc.pages.add
|
|
14
|
+
canvas = page.canvas
|
|
15
|
+
|
|
16
|
+
radius = 75
|
|
17
|
+
|
|
18
|
+
# Left pie chart
|
|
19
|
+
center = [page.box.width * 0.25, page.box.height * 0.85]
|
|
20
|
+
pie = canvas.graphic_object(:solid_arc, cx: center[0], cy: center[1],
|
|
21
|
+
outer_a: radius, outer_b: radius)
|
|
22
|
+
canvas.fill_color('ddddff')
|
|
23
|
+
canvas.draw(pie, start_angle: 30, end_angle: 110).fill
|
|
24
|
+
canvas.fill_color('ffdddd')
|
|
25
|
+
canvas.draw(pie, start_angle: 110, end_angle: 130).fill
|
|
26
|
+
canvas.fill_color('ddffdd')
|
|
27
|
+
canvas.draw(pie, start_angle: 130, end_angle: 30).fill
|
|
28
|
+
|
|
29
|
+
arc = canvas.graphic_object(:arc, cx: center[0], cy: center[1],
|
|
30
|
+
a: radius, b: radius)
|
|
31
|
+
canvas.stroke_color('0000ff')
|
|
32
|
+
canvas.draw(arc, start_angle: 30, end_angle: 110).stroke
|
|
33
|
+
canvas.stroke_color('ff0000')
|
|
34
|
+
canvas.draw(arc, start_angle: 110, end_angle: 130).stroke
|
|
35
|
+
canvas.stroke_color('00ff00')
|
|
36
|
+
canvas.draw(arc, start_angle: 130, end_angle: 30).stroke
|
|
37
|
+
|
|
38
|
+
# Right pie chart
|
|
39
|
+
center = [page.box.width * 0.75, page.box.height * 0.85]
|
|
40
|
+
canvas.stroke_color('777777')
|
|
41
|
+
pie = canvas.graphic_object(:solid_arc, cx: center[0], cy: center[1],
|
|
42
|
+
outer_a: radius, outer_b: radius)
|
|
43
|
+
canvas.fill_color('ddddff')
|
|
44
|
+
canvas.draw(pie, start_angle: 30, end_angle: 110).fill_stroke
|
|
45
|
+
canvas.fill_color('ffdddd')
|
|
46
|
+
canvas.draw(pie, start_angle: 110, end_angle: 130).fill_stroke
|
|
47
|
+
canvas.fill_color('ddffdd')
|
|
48
|
+
canvas.draw(pie, start_angle: 130, end_angle: 30).fill_stroke
|
|
49
|
+
|
|
50
|
+
doc.write('arcs.pdf', optimize: true)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# # Optimizing a PDF File
|
|
2
|
+
#
|
|
3
|
+
# This example shows how to read a PDF file, optimize it and write it
|
|
4
|
+
# out again.
|
|
5
|
+
#
|
|
6
|
+
# The heavy work is done by the `:optimize` task which allows control
|
|
7
|
+
# over which aspects should be optimized. See [HexaPDF::Task::Optimize]
|
|
8
|
+
# for detailed information.
|
|
9
|
+
#
|
|
10
|
+
# The hexapdf binary provides an optimization command which does some
|
|
11
|
+
# additional operations like optimizing the page tree.
|
|
12
|
+
#
|
|
13
|
+
# Usage:
|
|
14
|
+
# : `ruby optimizing.rb INPUT.PDF`
|
|
15
|
+
#
|
|
16
|
+
|
|
17
|
+
require 'hexapdf'
|
|
18
|
+
|
|
19
|
+
HexaPDF::Document.open(ARGV.shift) do |doc|
|
|
20
|
+
doc.task(:optimize, compact: true, object_streams: :generate,
|
|
21
|
+
compress_pages: false)
|
|
22
|
+
doc.write('optimizing.pdf')
|
|
23
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# # Merging PDF Files
|
|
2
|
+
#
|
|
3
|
+
# Merging of PDF files can be done in various ways of sophistication.
|
|
4
|
+
#
|
|
5
|
+
# The easiest way, which this example shows, just imports the pages of
|
|
6
|
+
# the source files into the target file. This preserves the page
|
|
7
|
+
# contents themselves but nothing else.
|
|
8
|
+
#
|
|
9
|
+
# For example, named destinations are not properly handled by the code.
|
|
10
|
+
# Sometimes other things like attached files or a document outline
|
|
11
|
+
# should also be preserved.
|
|
12
|
+
#
|
|
13
|
+
# The hexapdf binary provides a command for merging files which does
|
|
14
|
+
# the merging in a more sophisticated way.
|
|
15
|
+
#
|
|
16
|
+
# Usage:
|
|
17
|
+
# : `ruby merging.rb INPUT1.PDF INPUT2.PDF ...`
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
require 'hexapdf'
|
|
21
|
+
|
|
22
|
+
target = HexaPDF::Document.new
|
|
23
|
+
ARGV.each do |file|
|
|
24
|
+
pdf = HexaPDF::Document.open(file)
|
|
25
|
+
pdf.pages.each {|page| target.pages << target.import(page)}
|
|
26
|
+
end
|
|
27
|
+
target.write("2.merging.pdf", optimize: true)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# # Standard PDF Fonts
|
|
2
|
+
#
|
|
3
|
+
# This example shows all characters that are available in the standard 14 PDF
|
|
4
|
+
# fonts.
|
|
5
|
+
#
|
|
6
|
+
# The standard 14 PDF fonts are those fonts that all PDF reading/viewing
|
|
7
|
+
# applications need to support. They only provide a limited set of glyphs but
|
|
8
|
+
# have the advantage that they don't need to be embedded.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# : `ruby standard_pdf_fonts.rb`
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
require 'hexapdf'
|
|
15
|
+
|
|
16
|
+
def base_encoding_for_font(font)
|
|
17
|
+
case font.font_name
|
|
18
|
+
when 'Symbol', 'ZapfDingbats'
|
|
19
|
+
font.encoding
|
|
20
|
+
else
|
|
21
|
+
HexaPDF::Font::Encoding.for_name(:WinAnsiEncoding)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
doc = HexaPDF::Document.new
|
|
26
|
+
|
|
27
|
+
HexaPDF::FontLoader::Standard14::MAPPING.each do |font_name, mapping|
|
|
28
|
+
mapping.each_key do |variant|
|
|
29
|
+
canvas = doc.pages.add.canvas
|
|
30
|
+
canvas.font("Helvetica", size: 14)
|
|
31
|
+
canvas.text("#{font_name} #{variant != :none ? variant : ''}", at: [100, 800])
|
|
32
|
+
|
|
33
|
+
canvas.font(font_name, size: 14, variant: variant)
|
|
34
|
+
canvas.leading = 20
|
|
35
|
+
font = canvas.font
|
|
36
|
+
encoding = base_encoding_for_font(font.wrapped_font)
|
|
37
|
+
used_glyphs = []
|
|
38
|
+
|
|
39
|
+
# Showing the glyphs of the WinAnsi or built-in encoding
|
|
40
|
+
canvas.move_text_cursor(offset: [100, 750])
|
|
41
|
+
(2..15).each do |y|
|
|
42
|
+
data = []
|
|
43
|
+
(0..15).each do |x|
|
|
44
|
+
code = y * 16 + x
|
|
45
|
+
glyph = font.glyph(encoding.name(code))
|
|
46
|
+
glyph = font.glyph(:space) if glyph.id == font.wrapped_font.missing_glyph_id
|
|
47
|
+
used_glyphs << glyph.name
|
|
48
|
+
data << glyph << -(2000 - glyph.width)
|
|
49
|
+
end
|
|
50
|
+
canvas.show_glyphs(data)
|
|
51
|
+
canvas.move_text_cursor
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Showing the remaining glyphs
|
|
55
|
+
canvas.move_text_cursor(offset: [0, -40], absolute: false)
|
|
56
|
+
glyphs = font.wrapped_font.metrics.character_metrics.keys.select do |k|
|
|
57
|
+
Symbol === k
|
|
58
|
+
end.sort - used_glyphs
|
|
59
|
+
canvas.font(font_name, size: 14, variant: variant, custom_encoding: true)
|
|
60
|
+
font = canvas.font
|
|
61
|
+
glyphs.each_slice(16).with_index do |slice, index|
|
|
62
|
+
data = []
|
|
63
|
+
slice.each do |name|
|
|
64
|
+
glyph = font.glyph(name)
|
|
65
|
+
data << glyph << -(2000 - glyph.width)
|
|
66
|
+
end
|
|
67
|
+
canvas.show_glyphs(data)
|
|
68
|
+
canvas.move_text_cursor
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
doc.write("standard_pdf_fonts.pdf", optimize: true)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# # TrueType Fonts
|
|
2
|
+
#
|
|
3
|
+
# This example displays all glyphs of a TrueType font and shows that using a
|
|
4
|
+
# TrueType font with HexaPDF is very similar to using one of the standard PDF
|
|
5
|
+
# fonts.
|
|
6
|
+
#
|
|
7
|
+
# Before a TrueType font can be used, HexaPDF needs to be made aware of it. This
|
|
8
|
+
# is done by setting the configuration option 'font.map'. For one-off usage of a
|
|
9
|
+
# font file, the file name itself can also be used.
|
|
10
|
+
#
|
|
11
|
+
# Once that is done the [HexaPDF::Content::Canvas#font] method can be used as
|
|
12
|
+
# usual.
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# : `ruby truetype.rb [FONT_FILE]`
|
|
16
|
+
#
|
|
17
|
+
|
|
18
|
+
require 'hexapdf'
|
|
19
|
+
|
|
20
|
+
doc = HexaPDF::Document.new
|
|
21
|
+
font_file = ARGV.shift || File.join(__dir__, '../test/data/fonts/Ubuntu-Title.ttf')
|
|
22
|
+
wrapper = doc.fonts.add(font_file)
|
|
23
|
+
max_gid = wrapper.wrapped_font[:maxp].num_glyphs
|
|
24
|
+
|
|
25
|
+
255.times do |page|
|
|
26
|
+
break unless page * 256 < max_gid
|
|
27
|
+
canvas = doc.pages.add.canvas
|
|
28
|
+
canvas.font("Helvetica", size: 10)
|
|
29
|
+
canvas.text("Font: #{wrapper.wrapped_font.full_name}", at: [50, 825])
|
|
30
|
+
|
|
31
|
+
canvas.font(font_file, size: 15)
|
|
32
|
+
16.times do |y|
|
|
33
|
+
canvas.move_text_cursor(offset: [50, 800 - y * 50], absolute: true)
|
|
34
|
+
canvas.show_glyphs((0..15).map do |i|
|
|
35
|
+
gid = page * 256 + y * 16 + i
|
|
36
|
+
glyph = wrapper.glyph(gid)
|
|
37
|
+
gid >= max_gid ? [] : [glyph, -(2000 - glyph.width)]
|
|
38
|
+
end.flatten!)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
doc.write("truetype.pdf", optimize: true)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# # Show Character Bounding Boxes
|
|
2
|
+
#
|
|
3
|
+
# This examples shows how to process the contents of a page. It finds all
|
|
4
|
+
# characters on a page and surrounds them with their bounding box. Additionally,
|
|
5
|
+
# all consecutive text runs are also surrounded by a box.
|
|
6
|
+
#
|
|
7
|
+
# The code provides two ways of generating the boxes. The commented part of
|
|
8
|
+
# `ShowTextProcessor#show_text` uses a polyline since some characters may be
|
|
9
|
+
# transforemd (rotated or skewed). The un-commented part uses rectangles which
|
|
10
|
+
# is faster and correct for most but not all cases.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# : `ruby show_char_boxes.rb INPUT.PDF`
|
|
14
|
+
#
|
|
15
|
+
|
|
16
|
+
require 'hexapdf'
|
|
17
|
+
|
|
18
|
+
class ShowTextProcessor < HexaPDF::Content::Processor
|
|
19
|
+
|
|
20
|
+
def initialize(page)
|
|
21
|
+
super()
|
|
22
|
+
@canvas = page.canvas(type: :overlay)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def show_text(str)
|
|
26
|
+
boxes = decode_text_with_positioning(str)
|
|
27
|
+
return if boxes.string.empty?
|
|
28
|
+
|
|
29
|
+
@canvas.line_width = 1
|
|
30
|
+
@canvas.stroke_color(224, 0, 0)
|
|
31
|
+
# Polyline for transformed characters
|
|
32
|
+
#boxes.each {|box| @canvas.polyline(*box.points).close_subpath.stroke}
|
|
33
|
+
# Using rectangles is faster but not 100% correct
|
|
34
|
+
boxes.each do |box|
|
|
35
|
+
x, y = *box.lower_left
|
|
36
|
+
tx, ty = *box.upper_right
|
|
37
|
+
@canvas.rectangle(x, y, tx - x, ty - y).stroke
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@canvas.line_width = 0.5
|
|
41
|
+
@canvas.stroke_color(0, 224, 0)
|
|
42
|
+
@canvas.polyline(*boxes.lower_left, *boxes.lower_right,
|
|
43
|
+
*boxes.upper_right, *boxes.upper_left).close_subpath.stroke
|
|
44
|
+
end
|
|
45
|
+
alias :show_text_with_positioning :show_text
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
doc = HexaPDF::Document.open(ARGV.shift)
|
|
50
|
+
doc.pages.each_with_index do |page, index|
|
|
51
|
+
puts "Processing page #{index + 1}"
|
|
52
|
+
processor = ShowTextProcessor.new(page)
|
|
53
|
+
page.process_contents(processor)
|
|
54
|
+
end
|
|
55
|
+
doc.write('show_char_boxes.pdf', optimize: true)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# # Text Layouter - Alignment
|
|
2
|
+
#
|
|
3
|
+
# The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text
|
|
4
|
+
# inside a rectangular area, with various horizontal and vertical alignment
|
|
5
|
+
# options.
|
|
6
|
+
#
|
|
7
|
+
# The text can be aligned horizontally by setting [HexaPDF::Layout::Style#align]
|
|
8
|
+
# and vertically by [HexaPDF::Layout::Style#valign]. In this example, a sample
|
|
9
|
+
# text is laid out in all possible combinations.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# : `ruby text_layouter_alignment.rb`
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
require 'hexapdf'
|
|
16
|
+
|
|
17
|
+
sample_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
|
18
|
+
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
|
|
19
|
+
enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
|
20
|
+
aliquip ex ea commodo consequat. at".tr("\n", ' ')
|
|
21
|
+
|
|
22
|
+
doc = HexaPDF::Document.new
|
|
23
|
+
canvas = doc.pages.add.canvas
|
|
24
|
+
canvas.font("Times", size: 10, variant: :bold)
|
|
25
|
+
|
|
26
|
+
width = 100
|
|
27
|
+
height = 150
|
|
28
|
+
y_base = 800
|
|
29
|
+
tf = HexaPDF::Layout::TextFragment.create(sample_text,
|
|
30
|
+
font: doc.fonts.add("Times"))
|
|
31
|
+
tl = HexaPDF::Layout::TextLayouter.new
|
|
32
|
+
|
|
33
|
+
[:left, :center, :right, :justify].each_with_index do |align, x_index|
|
|
34
|
+
x = x_index * (width + 20) + 70
|
|
35
|
+
canvas.text(align.to_s, at: [x + 40, y_base + 15])
|
|
36
|
+
|
|
37
|
+
[:top, :center, :bottom].each_with_index do |valign, y_index|
|
|
38
|
+
y = y_base - (height + 30) * y_index
|
|
39
|
+
canvas.text(valign.to_s, at: [20, y - height / 2]) if x_index == 0
|
|
40
|
+
|
|
41
|
+
tl.style.align(align).valign(valign)
|
|
42
|
+
tl.fit([tf], width, height).draw(canvas, x, y)
|
|
43
|
+
canvas.stroke_color(128, 0, 0).rectangle(x, y, width, -height).stroke
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
doc.write("text_layouter_alignment.pdf", optimize: true)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# # Text Layouter - Inline Boxes
|
|
2
|
+
#
|
|
3
|
+
# The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text
|
|
4
|
+
# mixed with inline boxes.
|
|
5
|
+
#
|
|
6
|
+
# Inline boxes are used for showing graphics that follow the flow of the text.
|
|
7
|
+
# This means that their horizontal and their general vertical position is
|
|
8
|
+
# determined by the text layout functionality. However, inline boxes may be
|
|
9
|
+
# vertically aligned to various positions, like the baseline, the top/bottom of
|
|
10
|
+
# the text and the top/bottom of the line.
|
|
11
|
+
#
|
|
12
|
+
# This example shows some text containing emoticons that are replaced with their
|
|
13
|
+
# graphical representation, with normal smileys being aligned to the baseline
|
|
14
|
+
# and winking smileys to the top of the line.
|
|
15
|
+
#
|
|
16
|
+
# An inline box is a simple wrapper around a generic box that adheres to the
|
|
17
|
+
# necessary interface. Therefore they don't do any drawing operations themselves
|
|
18
|
+
# but delegate to their wrapped box. This means, for example, that inline boxes
|
|
19
|
+
# can use background colors or borders without doing anything special.
|
|
20
|
+
#
|
|
21
|
+
# Usage:
|
|
22
|
+
# : `ruby text_layouter_inline_boxes.rb`
|
|
23
|
+
#
|
|
24
|
+
|
|
25
|
+
require 'hexapdf'
|
|
26
|
+
|
|
27
|
+
include HexaPDF::Layout
|
|
28
|
+
|
|
29
|
+
sample_text = "Lorem ipsum :-) dolor sit amet, consectetur adipiscing
|
|
30
|
+
;-) elit, sed do eiusmod tempor incididunt :-) ut labore et dolore magna
|
|
31
|
+
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
|
32
|
+
laboris nisi ut aliquip ex ea commodo consequat ;-). Duis aute irure
|
|
33
|
+
dolor in reprehenderit in voluptate velit esse cillum :-) dolore eu
|
|
34
|
+
fugiat nulla pariatur. ".tr("\n", ' ') * 4
|
|
35
|
+
|
|
36
|
+
doc = HexaPDF::Document.new
|
|
37
|
+
emoji_smile = doc.images.add(File.join(__dir__, "emoji-smile.png"))
|
|
38
|
+
emoji_wink = doc.images.add(File.join(__dir__, "emoji-wink.png"))
|
|
39
|
+
size = 10
|
|
40
|
+
|
|
41
|
+
items = sample_text.split(/(:-\)|;-\))/).map do |part|
|
|
42
|
+
case part
|
|
43
|
+
when ':-)'
|
|
44
|
+
InlineBox.create(width: size * 2, height: size * 2, content_box: true,
|
|
45
|
+
background_color: [162, 234, 247], padding: 2) do |canvas, box|
|
|
46
|
+
canvas.image(emoji_smile, at: [0, 0], width: box.content_width)
|
|
47
|
+
end
|
|
48
|
+
when ';-)'
|
|
49
|
+
InlineBox.create(width: size, height: size, content_box: true,
|
|
50
|
+
valign: :top, padding: 5, margin: [0, 10],
|
|
51
|
+
border: {width: [1, 2], color: [200, 40]}) do |canvas, box|
|
|
52
|
+
canvas.image(emoji_wink, at: [0, 0], width: box.content_width)
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
TextFragment.create(part, font: doc.fonts.add("Times"), font_size: 18)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
layouter = TextLayouter.new
|
|
60
|
+
layouter.style.align = :justify
|
|
61
|
+
layouter.style.line_spacing(:proportional, 1.5)
|
|
62
|
+
layouter.fit(items, 500, 700).draw(doc.pages.add.canvas, 50, 800)
|
|
63
|
+
|
|
64
|
+
doc.write("text_layouter_inline_boxes.pdf")
|