Instantly share code, notes, and snippets.

@gettalong /README.md
Last active Aug 30, 2015

Embed
What would you like to do?
HexaPDF Graphics Primitives

This is a demo program showing the graphics primitives for drawing on PDF content streams or modifying the graphics state.

The following primitives are used:

    1. row: Coordinate system transformations (translate, scale, rotate, skew)
    1. row: Graphics state parameters for stroking (line width, line cap style, line join style, miter limit, line dash pattern)
    1. row: Basic shapes (line, polyline, rectangle, rounded rectangle, polygon, rounded polygon, circle, ellipse)
    1. row: Additional shapes (circular arc, elliptical arc wo/w inclination, composite arcs)
  • 5./6. row: Path painting (first four columns) and clipping path (last column) operations
    1. row: A square with a corner radius equal to the length of its sides, a composite elliptical annulus, a pie chart, a picture and all of the previous encapsulated as form XObject and then drawn
    1. row: An RGB color rainbow, just because
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment