|
require 'hexapdf/pdf/document' |
|
require 'hexapdf/pdf/content/canvas' |
|
|
|
doc = HexaPDF::PDF::Document.new |
|
page = doc.pages.add_page |
|
canvas = HexaPDF::PDF::Content::Canvas.new(page, content: :replace) |
|
|
|
def shape(canvas, *color) |
|
canvas.stroke_color(*color) |
|
canvas.polygon(0, 0, 0, 80, 30, 50, 60, 80, 60, 0, 30, 30) |
|
canvas.line(-30, 0, 30, 0) |
|
canvas.line(0, 30, 0, -30) |
|
canvas.stroke |
|
end |
|
|
|
# Basic transformations: translate, scale, rotate, skew |
|
canvas.translate(0, 710) do |
|
nc = [0.7, 0.7, 0.3] |
|
tc = [0.3, 0.7, 0.7] |
|
|
|
canvas.translate(50, 0) do |
|
shape(canvas, nc) |
|
canvas.translate(40, 40) { shape(canvas, tc) } |
|
end |
|
|
|
canvas.translate(180, 0) do |
|
shape(canvas, nc) |
|
canvas.scale(1.7, 1.3) { shape(canvas, tc) } |
|
end |
|
|
|
canvas.translate(330, 0) do |
|
shape(canvas, nc) |
|
canvas.rotate(30) { shape(canvas, tc) } |
|
end |
|
|
|
canvas.translate(430, 0) do |
|
shape(canvas, nc) |
|
canvas.skew(15, 30) { shape(canvas, tc) } |
|
end |
|
end |
|
|
|
def bold_lines(canvas) |
|
canvas.stroke_color(0) |
|
canvas.line_width = 15 |
|
yield |
|
canvas.stroke |
|
canvas.stroke_color(1.0) |
|
canvas.line_width = 1 |
|
yield |
|
canvas.stroke |
|
end |
|
|
|
# Graphics state: line width, line cap style, line join style, miter limit, line dash pattern |
|
canvas.translate(0, 550) do |
|
canvas.translate(50, 0) do |
|
[1, 5, 10, 15].each_with_index do |i, index| |
|
canvas.stroke_color(0) |
|
canvas.line_width(i) |
|
canvas.line(20*index, 0, 20*index, 100) |
|
canvas.stroke |
|
end |
|
end |
|
|
|
canvas.translate(150, 0) do |
|
0.upto(2) do |i| |
|
canvas.line_cap_style = i |
|
bold_lines(canvas) { canvas.line(20*i, 0, 20*i, 100) } |
|
end |
|
end |
|
|
|
canvas.translate(230, 0) do |
|
0.upto(2) do |i| |
|
canvas.line_join_style = i |
|
bold_lines(canvas) { canvas.polyline(0, 30*i, 40, 50 + 30*i, 80, 30*i) } |
|
end |
|
end |
|
|
|
canvas.translate(350, 0) do |
|
canvas.line_join_style = :miter |
|
[1, 10].each_with_index do |i, index| |
|
canvas.miter_limit = i |
|
bold_lines(canvas) { canvas.polyline(60*index, 0, 60*index + 20, 80, 60*index + 40, 0) } |
|
end |
|
end |
|
|
|
canvas.translate(490, 0) do |
|
canvas.line_width(1) |
|
[[[1, 1]], [[3, 1]], [[3, 3]], [[5, 1, 1, 1, 1, 1]], [[3, 5], 6]].each_with_index do |(value, phase), index| |
|
canvas.line_dash_pattern(value, phase || 0) |
|
canvas.line(20*index, 0, 20*index, 100) |
|
canvas.stroke |
|
end |
|
end |
|
end |
|
|
|
# Basic shapes: line, polyline, (rounded) rectangle, (rounded) polygon, circle, ellipse |
|
canvas.translate(0, 420) do |
|
canvas.line(50, 0, 50, 100) |
|
canvas.polyline(80, 0, 70, 10, 90, 20, 70, 30, 90, 40, 70, 50, 90, 60, 70, 70, 80, 80, 80, 100) |
|
canvas.rectangle(110, 0, 50, 100) |
|
canvas.rectangle(180, 0, 50, 100, radius: 20) |
|
canvas.polygon(250, 0, 250, 100, 280, 70, 310, 100, 310, 0, 280, 30) |
|
canvas.polygon(330, 0, 330, 100, 360, 70, 390, 100, 390, 0, 360, 30, radius: 20) |
|
canvas.circle(440, 50, 30) |
|
canvas.ellipse(520, 50, a: 30, b: 15, inclination: 45) |
|
canvas.stroke |
|
end |
|
|
|
# Various arcs w/wo filling |
|
canvas.translate(0, 320) do |
|
canvas.arc(50, 50, a: 10, start_angle: -60, end_angle: 115) |
|
canvas.arc(100, 50, a: 40, b: 20, start_angle: -60, end_angle: 115) |
|
canvas.arc(180, 50, a: 40, b: 20, start_angle: -60, end_angle: 115, inclination: 45) |
|
canvas.stroke |
|
|
|
canvas.fill_color(0.4, 0.3, 0.4) |
|
canvas.arc(250, 50, a: 10, start_angle: -60, end_angle: 115) |
|
canvas.arc(300, 50, a: 40, b: 20, start_angle: -60, end_angle: 115) |
|
canvas.arc(380, 50, a: 40, b: 20, start_angle: -60, end_angle: 115, inclination: 45) |
|
canvas.fill |
|
|
|
arc = canvas.graphic_object(:arc, cx: 450, cy: 50, a: 30, b: 30, start_angle: -30, end_angle: 105) |
|
canvas.fill_color(0.4, 0.3, 0.4) |
|
canvas.move_to(450, 50) |
|
canvas.line_to(arc.start_point) |
|
arc.curves.each {|c| canvas.curve_to(*c)} |
|
canvas.fill |
|
arc.configure(start_angle: 105, end_angle: -30) |
|
canvas.fill_color(0.3, 0.7, 0.7) |
|
canvas.move_to(450, 50) |
|
canvas.line_to(arc.start_point) |
|
arc.curves.each {|c| canvas.curve_to(*c)} |
|
canvas.fill |
|
|
|
arc = canvas.graphic_object(:arc, cx: 530, cy: 50, a: 40, b: 20, start_angle: -30, end_angle: 105) |
|
canvas.fill_color(0.4, 0.3, 0.4) |
|
canvas.move_to(530, 50) |
|
canvas.line_to(arc.start_point) |
|
arc.curves.each {|c| canvas.curve_to(*c)} |
|
canvas.fill |
|
arc.configure(start_angle: 105, end_angle: -30) |
|
canvas.fill_color(0.7, 0.7, 0.3) |
|
canvas.move_to(530, 50) |
|
canvas.line_to(arc.start_point) |
|
arc.curves.each {|c| canvas.curve_to(*c)} |
|
canvas.fill |
|
end |
|
|
|
def shapes_to_paint(canvas) |
|
canvas.line_width = 2 |
|
canvas.arc(50, 50, a: 50) |
|
canvas.arc(50, 60, a: 25, end_angle: 180, sweep: true) |
|
canvas.arc(50, 40, a: 25, end_angle: 180, sweep: false) |
|
end |
|
|
|
def arrows(canvas) |
|
canvas.line_width = 1 |
|
canvas.polyline(95, 45, 100, 50, 105, 45) |
|
canvas.polyline(55, 105, 50, 100, 55, 95) |
|
canvas.polyline(-5, 55, 0, 50, 5, 55) |
|
canvas.polyline(45, 5, 50, 0, 45, -5) |
|
canvas.polyline(55, 90, 50, 85, 55, 80) |
|
canvas.polyline(55, 20, 50, 15, 55, 10) |
|
canvas.stroke |
|
end |
|
|
|
# Path painting and clipping operations |
|
canvas.translate(0, 190) do |
|
canvas.fill_color(0.3, 0.7, 0.7) |
|
|
|
[[:stroke], [:close_stroke], [:fill, :nonzero], [:fill, :even_odd], |
|
[:fill_stroke, :nonzero], [:fill_stroke, :even_odd], |
|
[:close_fill_stroke, :nonzero], [:close_fill_stroke, :even_odd], |
|
].each_with_index do |op, index| |
|
row = (1 - (index / 4)) |
|
column = index % 4 |
|
x = 50 + 80*column |
|
y = 80 * row |
|
canvas.transform(0.6, 0, 0, 0.6, x, y) do |
|
shapes_to_paint(canvas) |
|
canvas.send(*op) |
|
arrows(canvas) |
|
end |
|
end |
|
|
|
canvas.fill_color(0.7, 0.7, 0.3) |
|
|
|
[:even_odd, :nonzero].each_with_index do |op, index| |
|
canvas.translate(370 + 110*index, 20) do |
|
canvas.circle(50, 50, 50) |
|
canvas.circle(50, 50, 20) |
|
canvas.clip_path(op) |
|
canvas.end_path |
|
canvas.rectangle(0, 0, 100, 100, radius: 100) |
|
canvas.fill_stroke |
|
end |
|
end |
|
|
|
end |
|
|
|
|
|
# Some composite shapes, images and form XObjects |
|
canvas.translate(0, 80) do |
|
canvas.fill_color(0.3, 0.7, 0.7) |
|
canvas.rectangle(50, 0, 80, 80, radius: 80) |
|
canvas.fill |
|
|
|
solid = canvas.graphic_object(:solid_arc, cx: 190, cy: 40, inner_a: 20, inner_b: 15, outer_a: 40, outer_b: 30, |
|
start_angle: 10, end_angle: 130) |
|
canvas.line_width(0.5) |
|
canvas.fill_color('AA8888').draw(solid).fill_stroke |
|
canvas.fill_color('88AA88').draw(solid, start_angle: 130, end_angle: 220).fill_stroke |
|
canvas.fill_color('8888AA').draw(solid, start_angle: 220, end_angle: 10).fill_stroke |
|
|
|
solid.configure(inner_a: 0, inner_b: 0, outer_a: 40, outer_b: 40, cx: 290) |
|
canvas.fill_color('AA8888').draw(solid, start_angle: 10, end_angle: 130).fill_stroke |
|
canvas.fill_color('88AA88').draw(solid, start_angle: 130, end_angle: 220).fill_stroke |
|
canvas.fill_color('8888AA').draw(solid, start_angle: 220, end_angle: 10).fill_stroke |
|
|
|
canvas.image(File.join(__dir__, 'machupicchu.jpg'), at: [350, 0], height: 80) |
|
|
|
form = doc.add(page.to_form_xobject) |
|
canvas.rectangle(480, 0, form.box.width * (100 / form.box.height.to_f), 100).stroke |
|
canvas.xobject(form, at: [480, 0], height: 100) |
|
end |
|
|
|
# A simple rainbow color band |
|
canvas.translate(0, 20) do |
|
canvas.line_width = 10 |
|
freq = 0.3 |
|
0.upto(50) do |i| |
|
r = Math.sin(freq * i) * 127 + 128 |
|
g = Math.sin(freq * i + 2) * 127 + 128 |
|
b = Math.sin(freq * i + 4) * 127 + 128 |
|
canvas.stroke_color(r.to_i, g.to_i, b.to_i) |
|
canvas.line(50 + i*10, 0, 50 + i*10, 40) |
|
canvas.stroke |
|
end |
|
end |
|
|
|
doc.write('graphics.pdf') |