prawn-extras 0.1.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +32 -0
- data/Rakefile +22 -0
- data/lib/prawn/extras/box.rb +176 -0
- data/lib/prawn/extras/dynamic_repeater.rb +87 -0
- data/lib/prawn/extras/engine.rb +7 -0
- data/lib/prawn/extras/grid.rb +88 -0
- data/lib/prawn/extras/text.rb +127 -0
- data/lib/prawn/extras/version.rb +5 -0
- data/lib/prawn/extras.rb +11 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0674038f691b219cab7a8e3974ff74d97bba0c95
|
4
|
+
data.tar.gz: 996793d3a1e293886c9553b5cdbb6205ada35039
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5f4dc02abfc5e4163615539cda0f3423b3f1dfb2ac14dfd9449e0a3ac5f24697cb2c78a91ec7e4b6bd8eeec1e4801a95d12bfeae7c9ed0e053ba13611b506749
|
7
|
+
data.tar.gz: 977b5273cd914f7bdaa72a502afeaf5e09e1e466879d7f4cabe946fc7d6c2d78affea2046de91d74d762d75b9230f04fdce905bef1a7983dc923e54214216f04
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Rodrigo Castro
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Prawn::Extras
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
Just install it and all extra stuff below will be available!
|
6
|
+
|
7
|
+
TODO: List all the great extra stuff I just mentioned
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'prawn-extras'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
```bash
|
18
|
+
$ bundle
|
19
|
+
```
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
```bash
|
23
|
+
$ gem install prawn-extras
|
24
|
+
```
|
25
|
+
|
26
|
+
## Contributing
|
27
|
+
All contributions are welcome, please raise an issue for any problems or
|
28
|
+
suggestions.
|
29
|
+
|
30
|
+
## License
|
31
|
+
The gem is available as open source under the terms of the
|
32
|
+
[MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Prawn::Extras'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Extras
|
3
|
+
module Box
|
4
|
+
delegate :top_left, :width, :height, to: :bounds
|
5
|
+
|
6
|
+
def last_created_box
|
7
|
+
@last_created_box ||= nil
|
8
|
+
end
|
9
|
+
|
10
|
+
# Retorna um valor absoluto de width correspondente a porcentagem indicada,
|
11
|
+
# nos limites do relatório, que é entre '0' e 'bounds.width'.
|
12
|
+
# O parâmetro value deve ser um valor entre 0 e 100.
|
13
|
+
# Ex: chamando percent_w(50) fora de um bounding_box: 50% da width da página
|
14
|
+
# Ex: chamando percent_w(9) em um bounding_box: 9% da width do bounding_box
|
15
|
+
def percent_w(value)
|
16
|
+
# Faz o clamp no valor para garantir que ele fique entre 0% e 100%
|
17
|
+
clamped_value = [0, value, 100].sort[1]
|
18
|
+
bounds.width * (clamped_value / 100.0)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Retorna um valor absoluto de height correspondente a porcentagem indicada,
|
22
|
+
# nos limites do relatório, que é entre 'bounds.height' e '0'.
|
23
|
+
# O parâmetro value deve ser um valor entre 0 e 100.
|
24
|
+
# Ex: chamando percent_h(50) fora de um bounding_box: 50% da height da página
|
25
|
+
# Ex: chamando percent_h(9) em um bounding_box: 9% da height do bounding_box
|
26
|
+
def percent_h(value)
|
27
|
+
# Faz o clamp no valor para garantir que ele fique entre 0% e 100%
|
28
|
+
clamped_value = [0, value, 100].sort[1]
|
29
|
+
bounds.height * (clamped_value / 100.0)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Retorna um valor absoluto de height correspondente à height restante abaixo
|
33
|
+
# de base_height. base_height deve ser um objeto do tipo Bounds
|
34
|
+
def remaining_height(base_height)
|
35
|
+
base_height.anchor[1] - bounds.anchor[1]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Cria um bounding_box. Esse método é basicamente um alias de bounding_box,
|
39
|
+
# exceto que oferece mais flexibilidade e facilidade na entrada dos
|
40
|
+
# parâmetros, permitindo expressar width e height como porcentagens, ex:
|
41
|
+
# box(top_left, '25%', '100%') {}
|
42
|
+
def box(position, width, height, options = {})
|
43
|
+
size = build_size_options(position, width, height)
|
44
|
+
box = bounding_box(position, size) do
|
45
|
+
padding(options[:padding] || [0, 0, 0, 0]) { yield if block_given? }
|
46
|
+
end
|
47
|
+
@last_created_box = box unless options[:dont_track]
|
48
|
+
box
|
49
|
+
end
|
50
|
+
|
51
|
+
# Cria um novo bounding_box posicionado à direita do bounding_box pai.
|
52
|
+
# O comportamento é o mesmo de chamar 'bounding_box', e aceita um bloco.
|
53
|
+
# Este método retorna o bounding_box criado, para ser referenciado depois.
|
54
|
+
# O parâmetro espaçamento é opcional e indica o tamanho do espaço entre a
|
55
|
+
# direita do pai e a esquerda do novo bounding_box.
|
56
|
+
def box_beside(origin_box, width, height, options = {}, &block)
|
57
|
+
posicao = position_beside(origin_box, options[:gutter] || 0)
|
58
|
+
box(posicao, width, height, options, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Cria um novo bounding_box posicionado à direita do último box criado.
|
62
|
+
# Funciona como o método acima box_a_direita_de, exceto que ele pega o box
|
63
|
+
# automaticamente. Caso não exista um último box criado (esse foi o primeiro),
|
64
|
+
# ele irá posicionar o box no top_left.
|
65
|
+
def box_beside_previous(width, height, options = {}, &block)
|
66
|
+
box_beside(last_created_box, width, height, options, &block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Cria um novo bounding_box posicionado abaixo do bounding_box pai.
|
70
|
+
# O comportamento é o mesmo de chamar 'bounding_box', e aceita um bloco.
|
71
|
+
# Este método retorna o bounding_box criado, para ser referenciado depois.
|
72
|
+
# O parâmetro espaçamento é opcional e indica o tamanho do espaço entre o
|
73
|
+
# limite inferior do pai e o topo do novo bounding_box.
|
74
|
+
def box_below(origin_box, width, height, options = {}, &block)
|
75
|
+
posicao = position_below(origin_box, options[:gutter] || 0)
|
76
|
+
box(posicao, width, height, options, &block)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Cria um novo bounding_box posicionado abaixo do último box criado.
|
80
|
+
# Funciona como o método acima box_abaixo_de, exceto que ele pega o box
|
81
|
+
# automaticamente. Caso não exista um último box criado (esse foi o primeiro),
|
82
|
+
# ele irá posicionar o box no top_left.
|
83
|
+
def box_below_previous(width, height, opcoes = {}, &block)
|
84
|
+
box_below(last_created_box, width, height, opcoes, &block)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Retorna uma posição (para um novo bounding_box), imediatamente à direita
|
88
|
+
# de um bounding_box pai, alinhado verticalmente ao topo do pai.
|
89
|
+
# O parâmetro espaçamento é opcional e indica o tamanho do espaço entre a
|
90
|
+
# direita do pai e a esquerda dessa nova posição.
|
91
|
+
def position_beside(origin_box, gutter = 0)
|
92
|
+
correct_origin = Array(origin_box).first
|
93
|
+
return top_left if origin_box.nil?
|
94
|
+
diff = [gutter - bounds.anchor[0], -bounds.anchor[1]]
|
95
|
+
sum_dimensions(correct_origin.absolute_top_right, diff)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Retorna uma posição (para um novo bounding_box), imediatamente abaixo de um
|
99
|
+
# bounding_box pai, alinhado horizontalmente com a esquerda do pai.
|
100
|
+
# O parâmetro espaçamento é opcional e indica o tamanho do espaço entre o
|
101
|
+
# limite inferior do pai e o topo dessa nova posição.
|
102
|
+
def position_below(origin_box, gutter = 0)
|
103
|
+
correct_origin = Array(origin_box).first
|
104
|
+
return top_left if correct_origin.nil?
|
105
|
+
diff = [-@margin[1], -gutter.to_f - @margin[2]]
|
106
|
+
sum_dimensions(correct_origin.absolute_bottom_left, diff)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Cria um bounding box para agir como se fosse um padding (igual ao do CSS).
|
110
|
+
# Tamanho pode ser um número, que será aplicado aos quatro lados, ou pode ser
|
111
|
+
# um array com 4 valores, para o padding de cima, direita, baixo e esquerda,
|
112
|
+
# nessa ordem.
|
113
|
+
def padding(values)
|
114
|
+
values = build_padding_values(values)
|
115
|
+
posicao = padding_position(values)
|
116
|
+
width, height = padding_size(values)
|
117
|
+
bounding_box(posicao, width: width, height: height) { yield }
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
|
122
|
+
# Checa se o valor da width é uma string e contém o caractere %. Se tiver,
|
123
|
+
# fazer o cálculo correto da width, senão, retornar a mesma width.
|
124
|
+
# O cálculo da width é relativo ao espaço horizontal global. Para fazer ele
|
125
|
+
# ser relativo ao espaço restante, basta adicionar um 'l' depois de '%'.
|
126
|
+
# Espaço restante é o espaço que sobrou após a posição passada no parâmetro,
|
127
|
+
# ou seja, se o pai ocupar metade da width da página, o valor em porcentagem
|
128
|
+
# será relativo aos 50% livres, e não à width total da página.
|
129
|
+
def t_width(position, width)
|
130
|
+
return width unless width.to_s.include? '%'
|
131
|
+
valor = percent_w(width.to_f) # Valor percentual global
|
132
|
+
return valor unless width.to_s.include? 'l' # 'l' de 'local'
|
133
|
+
valor * (1.0 - (position.first / bounds.width)) # Valor percentual relativo
|
134
|
+
end
|
135
|
+
|
136
|
+
# Checa se o valor da height é uma string e contém o caractere %. Se tiver,
|
137
|
+
# fazer o cálculo correto da width, senão, retornar a mesma height.
|
138
|
+
# O cálculo da height é relativo ao espaço vertical global. Para fazer ele
|
139
|
+
# ser relativo ao espaço restante, basta adicionar um 'l' depois de '%'.
|
140
|
+
# Espaço restante é o espaço que sobrou após a posição passada no parâmetro,
|
141
|
+
# ou seja, se o pai ocupar metade da height da página, o valor em porcentagem
|
142
|
+
# será relativo aos 50% livres, e não à height total da página.
|
143
|
+
def t_height(position, height)
|
144
|
+
return height unless height.to_s.include? '%'
|
145
|
+
valor = percent_h(height.to_f) # Valor percentual global
|
146
|
+
return valor unless height.to_s.include? 'l' # 'l' de 'local'
|
147
|
+
valor * (position.last / bounds.height) # Valor percentual relativo
|
148
|
+
end
|
149
|
+
|
150
|
+
def build_padding_values(values)
|
151
|
+
values = [values.to_i] * 4 unless values.is_a? Array
|
152
|
+
values.map(&:to_i)[0..3]
|
153
|
+
end
|
154
|
+
|
155
|
+
def padding_position(values)
|
156
|
+
[values[3], bounds.top - values[0]]
|
157
|
+
end
|
158
|
+
|
159
|
+
def padding_size(values)
|
160
|
+
horizontal_padding = values[1] + values[3]
|
161
|
+
vertical_padding = values[0] + values[2]
|
162
|
+
[bounds.width - horizontal_padding, bounds.height - vertical_padding]
|
163
|
+
end
|
164
|
+
|
165
|
+
def sum_dimensions(dim_a, dim_b)
|
166
|
+
[dim_a, dim_b].transpose.map { |x| x.reduce(:+) }
|
167
|
+
end
|
168
|
+
|
169
|
+
def build_size_options(position, width, height)
|
170
|
+
{ width: t_width(position, width), height: t_height(position, height) }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
Prawn::Document.include Prawn::Extras::Box
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Extras
|
3
|
+
# ==============================================================================
|
4
|
+
#
|
5
|
+
# Esse módulo contém helpers para facilitar a inserção de conteúdo dinâmico
|
6
|
+
# em repeaters que são dinâmicos.
|
7
|
+
#
|
8
|
+
# Esses métodos controlam um hash de valores mapeados pelo número da página.
|
9
|
+
# Utilizar esse repeater_values é necessário, pois ao contrário do repeater
|
10
|
+
# normal, que o Prawn renderiza na hora e mantém o mesmo até o final, com o
|
11
|
+
# repeater dinâmico ele deixa para renderizá-lo no final de tudo, por isso
|
12
|
+
# se o valor de alguma variável mudar no meio da geração do relatório, o
|
13
|
+
# repeater dinâmico não terá acesso ao valor anterior.
|
14
|
+
|
15
|
+
# Exemplo de utilização: Deseja-se agrupar alunos por curso, e cada vez que
|
16
|
+
# o curso muda há uma quebra de página, de forma que o nome do curso deve
|
17
|
+
# ser impresso no cabeçalho do relatório, sempre o curso do grupo atual de
|
18
|
+
# alunos.
|
19
|
+
#
|
20
|
+
# Nesse caso, no cabeçalho terá algo do tipo:
|
21
|
+
# 'text(valor_na_pagina(:nome_curso, page_number))'
|
22
|
+
#
|
23
|
+
# E, dentro do '.each' que itera sobre os cursos, algo do tipo:
|
24
|
+
# cursos.each do |curso|
|
25
|
+
# start_new_line
|
26
|
+
# set_valor_na_pagina(:nome_curso, curso, page_number)
|
27
|
+
# /* Tabela de alunos */
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# ==========================================================================
|
31
|
+
module DynamicRepeater
|
32
|
+
# Saves a named value for a specific page of the generated PDF. This will
|
33
|
+
# save the value for the specified page down to the first page of the
|
34
|
+
# document (page 1), or until there's already another value saved for a
|
35
|
+
# previous page.
|
36
|
+
#
|
37
|
+
# Example:
|
38
|
+
#
|
39
|
+
# store_value_in_page(:name, 'John', 3)
|
40
|
+
#
|
41
|
+
# This will save the value "John" at the :name key for the pages 1, 2
|
42
|
+
# and 3. Any subsequent calls to this method (for the same key) will not
|
43
|
+
# override these values.
|
44
|
+
#
|
45
|
+
def store_value_in_page(key, value, page = page_number)
|
46
|
+
page.downto(1).each do |page_index|
|
47
|
+
next if repeater_values(key).keys.include?[page_index]
|
48
|
+
repeater_values(key)[page_index] = value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the value for a key at a specific page. If the page is greater
|
53
|
+
# than the highest saved page, the highest value is returned.
|
54
|
+
#
|
55
|
+
# Examples:
|
56
|
+
#
|
57
|
+
# save_repeater_value(:name, 'John', 3)
|
58
|
+
# save_repeater_value(:name, 'Jane', 5)
|
59
|
+
#
|
60
|
+
# value_in_page(:name, 1) => "John"
|
61
|
+
# value_in_page(:name, 2) => "John"
|
62
|
+
# value_in_page(:name, 3) => "John"
|
63
|
+
# value_in_page(:name, 4) => "Jane"
|
64
|
+
# value_in_page(:name, 5) => "Jane"
|
65
|
+
# value_in_page(:name, 6) => "Jane"
|
66
|
+
# value_in_page(:name, -1) => ""
|
67
|
+
#
|
68
|
+
def value_in_page(key, page, default_value = '')
|
69
|
+
repeater_values(key)[[page, max_index(key).min]] || default_value
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def max_index(key)
|
75
|
+
repeater_values(key).keys.max
|
76
|
+
end
|
77
|
+
|
78
|
+
def repeater_values(key)
|
79
|
+
@repeater_values ||= {}
|
80
|
+
@repeater_values[key] ||= {}
|
81
|
+
@repeater_values[key]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Prawn::Document.include Prawn::Extras::DynamicRepeater
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Extras
|
3
|
+
# Módulo para gerar um grid de células de tamanhos definidos. Parecido com uma
|
4
|
+
# tabela HTML, mas com maior facilidade e flexibilidade para fazer colspans.
|
5
|
+
|
6
|
+
# Não deve ser usado para dados tabulares, para isso deve-se usar o Prawn Table.
|
7
|
+
#
|
8
|
+
# O método gerar grid gera um grid com o número de linhas e colunas passados nos
|
9
|
+
# parâmetros. O tamanho será 100% do bounding_box que ele se encontra, exceto
|
10
|
+
# quando houver um padding, nesse caso o tamanho será o do box menos o padding.
|
11
|
+
#
|
12
|
+
# Para gerar um grid de, por exemplo, 4 colunas e 3 linhas, chame:
|
13
|
+
#
|
14
|
+
# gerar_grid(4, 3, opcoes_que_sao_opcionais_e_podem_ser_omitidas) do
|
15
|
+
# campo_grid(linha, coluna, titulo, texto)
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Para gerar cada célula individualmente, use o método campo_grid ou os métodos
|
19
|
+
# derivados desse. O parâmetro linha é a linha do grid aonde o campo vai ficar,
|
20
|
+
# e o parâmetro colunas pode ser tanto um número quando um array com 2
|
21
|
+
# elementos. Se for um número, a coluna vai ser a do número, mas se for um
|
22
|
+
# array, como por exemplo [0, 2], significa que a célula vai ocupar as colunas
|
23
|
+
# 0, 1 e 2 da linha selecionada. Isso é equivalente ao colspan do HTML.
|
24
|
+
#
|
25
|
+
# Ilustração de um grid de 4 colunas, 3 linhas com um padding de, por exemplo,
|
26
|
+
# 10pt (para exemplificar, digamos que 10pt = 1 linha de comentário):
|
27
|
+
#
|
28
|
+
# Bounding Box:
|
29
|
+
# |------------------------------------------------------------------------|
|
30
|
+
# | Grid: |
|
31
|
+
# | |---------------|---------------|---------------|---------------| |
|
32
|
+
# | | Cell 1 | Cell 2 | ... | Index: [0, 3] | |
|
33
|
+
# | |---------------|---------------|---------------|---------------| |
|
34
|
+
# | | Index: [1, 0] | Index: [1, 1] | Index: [1, 2] | Index: [1, 3] | |
|
35
|
+
# | |---------------|---------------|---------------|---------------| |
|
36
|
+
# | | Index: [2, 0] | Index: [2, 1] | Index: [2, 2] | Index: [2, 3] | |
|
37
|
+
# | |---------------|---------------|---------------|---------------| |
|
38
|
+
# | |
|
39
|
+
# |------------------------------------------------------------------------|
|
40
|
+
#
|
41
|
+
# Exemplos: gerar células no grid acima:
|
42
|
+
#
|
43
|
+
# campo_grid(0, 1) ==> Corresponde à Célula 2
|
44
|
+
# campo_grid(1, [0, 3]) ==> Corresponde a toda a segunda linha
|
45
|
+
#
|
46
|
+
# Na hora de gerar o grid, algumas opções podem ser passadas:
|
47
|
+
#
|
48
|
+
# Padding: Adiciona um padding no grid inteiro em relação ao bonding_box que
|
49
|
+
# contém o grid.
|
50
|
+
# Leading: Usa o leading escolhido no grid. O leading é resetado para o valor
|
51
|
+
# anterior quando o grid terminar de ser definido.
|
52
|
+
# Gutter: O espaço entre as células, em pts. Funciona como o cellspacing da tag
|
53
|
+
# <table> do HTML.
|
54
|
+
module Grid
|
55
|
+
include Text
|
56
|
+
include Box
|
57
|
+
|
58
|
+
def define_grid_block(columns, rows, options = {})
|
59
|
+
options = build_grid_options(columns, rows, options)
|
60
|
+
padding(options.delete(:padding)) do
|
61
|
+
leading = options.delete(:leading)
|
62
|
+
define_grid(options)
|
63
|
+
save_leading(leading) { yield }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def grid_cell(row, columns)
|
68
|
+
columns = [columns] * 2 unless columns.is_a? Array
|
69
|
+
grid([row, columns[0]], [row, columns[1]]).bounding_box { yield }
|
70
|
+
end
|
71
|
+
|
72
|
+
def text_grid_cell(row, columns, label_or_text, text = nil)
|
73
|
+
grid_cell(row, columns) do
|
74
|
+
titled_text(label_or_text, text) if text
|
75
|
+
text_box(label_or_text) unless text
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def build_grid_options(columns, rows, options)
|
82
|
+
options.merge(columns: columns, rows: rows)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
Prawn::Document.include Prawn::Extras::Grid
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Prawn
|
2
|
+
module Extras
|
3
|
+
module Text
|
4
|
+
# Changes the font family, style, size and leading. When a block is used,
|
5
|
+
# the font is applied transactionally and is rolled back when the block
|
6
|
+
# exits.
|
7
|
+
#
|
8
|
+
# The accepted options are:
|
9
|
+
#
|
10
|
+
# family: A string that can be one of the 14 built-in fonts supported by
|
11
|
+
# PDF, or the location of a TTF file. The Font::AFM::BUILT_INS array
|
12
|
+
# specifies the valid built in font values.
|
13
|
+
#
|
14
|
+
# leading: A number that sets the document-wide text leading.
|
15
|
+
#
|
16
|
+
# size: A number indicating the font size, in points.
|
17
|
+
#
|
18
|
+
# style: The font style. To use the :style option you need to map those
|
19
|
+
# font styles to their respective font files. See font_families for
|
20
|
+
# more information.
|
21
|
+
#
|
22
|
+
def switch_font(options)
|
23
|
+
return font_and_leading(options) unless block_given?
|
24
|
+
save_leading { font_and_leading(options) { yield } }
|
25
|
+
end
|
26
|
+
|
27
|
+
# Sets the font to the same as before, but removing italic or bold style.
|
28
|
+
# All options from set_font may also be used here.
|
29
|
+
def regular_font(options = {}, &block)
|
30
|
+
switch_font(options.merge(style: :normal), &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets the font to the same as before, but applying the bold style.
|
34
|
+
# All options from set_font may also be used here.
|
35
|
+
def bold_font(options = {}, &block)
|
36
|
+
switch_font(options.merge(style: :bold), &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Sets the font to the same as before, but applying the italic style.
|
40
|
+
# All options from set_font may also be used here.
|
41
|
+
def italic_font(options = {}, &block)
|
42
|
+
switch_font(options.merge(style: :italic), &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Transactionally changes the fill color, rolling back the previous color
|
46
|
+
# when the block exits.
|
47
|
+
def save_color(new_color)
|
48
|
+
current_color = fill_color
|
49
|
+
fill_color(new_color)
|
50
|
+
yield
|
51
|
+
fill_color current_color
|
52
|
+
end
|
53
|
+
|
54
|
+
# Outputs a horizontal line, similar to the HMTL equivalent <hr>, at the
|
55
|
+
# current cursor position.
|
56
|
+
#
|
57
|
+
# The padding parameter is optional, and when specified sets a
|
58
|
+
# horizontal padding before and after the sides of the line with the
|
59
|
+
# corresponding size in points. This reduces the width of the line by a
|
60
|
+
# factor of 2 * padding.
|
61
|
+
#
|
62
|
+
# This method returns the Document, and therefore is chainable with other
|
63
|
+
# Document methods.
|
64
|
+
#
|
65
|
+
def horizontal_line(padding = 0)
|
66
|
+
left_position = [padding, cursor]
|
67
|
+
right_position = [bounds.width - padding, cursor]
|
68
|
+
stroke_line(left_position, right_position)
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
# Outputs one or more vertical lines, at the specified horizontal
|
73
|
+
# position, going all the way from the top to the bottom of the current
|
74
|
+
# bounds object.
|
75
|
+
#
|
76
|
+
# This method returns the Document, and therefore is chainable with other
|
77
|
+
# Document methods.
|
78
|
+
#
|
79
|
+
def vertical_line(*horizontal_positions)
|
80
|
+
horizontal_positions.each do |horiz_pos|
|
81
|
+
stroke_line([horiz_pos, percent_h(100)], [horiz_pos, 0])
|
82
|
+
end
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
# Outputs the text prepended with a bold title. It is possible to change
|
87
|
+
# the title to italic by specifying the appropriate :styles option.
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
#
|
91
|
+
# titled_text('Name', 'John') => "Name: John", where "Name" is bold
|
92
|
+
#
|
93
|
+
def titled_text(title, text, options = {})
|
94
|
+
style = options.delete(:styles) || [:bold]
|
95
|
+
title_options = { styles: style, text: "#{t(title)}: " }
|
96
|
+
formatted_text_box([title_options, { text: t(text) }], options)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Alias to the corresponding i18n method, with the additional caveat that,
|
100
|
+
# if a String is passed, the same string will be returned. It will only
|
101
|
+
# try to translate the text if text_or_key parameter is a Symbol.
|
102
|
+
#
|
103
|
+
# It also namespaces the i18n context to the @i18n_scope variable.
|
104
|
+
#
|
105
|
+
def t(text_or_key, options = {})
|
106
|
+
return text_or_key.to_s unless text_or_key.is_a?(Symbol)
|
107
|
+
I18n.t(text_or_key, { scope: @i18n_scope }.merge(options))
|
108
|
+
end
|
109
|
+
|
110
|
+
protected
|
111
|
+
|
112
|
+
def save_leading(new_leading = nil)
|
113
|
+
leading = default_leading
|
114
|
+
default_leading(new_leading) if new_leading.present?
|
115
|
+
yield
|
116
|
+
default_leading(leading)
|
117
|
+
end
|
118
|
+
|
119
|
+
def font_and_leading(options, &block)
|
120
|
+
default_leading(options.delete(:leading)) if options[:leading]
|
121
|
+
font(options[:family] || 'Helvetica', options, &block)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
Prawn::Document.include Prawn::Extras::Text
|
data/lib/prawn/extras.rb
ADDED
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: prawn-extras
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rodrigo Castro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: prawn
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Extra functions for the great Prawn gem
|
56
|
+
email:
|
57
|
+
- rod.c.azevedo@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- MIT-LICENSE
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- lib/prawn/extras.rb
|
66
|
+
- lib/prawn/extras/box.rb
|
67
|
+
- lib/prawn/extras/dynamic_repeater.rb
|
68
|
+
- lib/prawn/extras/engine.rb
|
69
|
+
- lib/prawn/extras/grid.rb
|
70
|
+
- lib/prawn/extras/text.rb
|
71
|
+
- lib/prawn/extras/version.rb
|
72
|
+
homepage: https://github.com/roooodcastro/prawn-extras
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.5.1
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Extra functions for the great Prawn gem
|
96
|
+
test_files: []
|