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 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,7 @@
1
+ module Prawn
2
+ module Extras
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace Prawn::Extras
5
+ end
6
+ end
7
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ module Prawn
2
+ module Extras
3
+ VERSION = '0.1.0'.freeze
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'prawn/extras/engine'
2
+ require 'prawn/document'
3
+ require 'prawn/extras/box'
4
+ require 'prawn/extras/text'
5
+ require 'prawn/extras/grid'
6
+ require 'prawn/extras/dynamic_repeater'
7
+
8
+ module Prawn
9
+ module Extras
10
+ end
11
+ end
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: []