pdf_writing_tools 0.0.10 → 0.0.13
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/assets/bullet.png +0 -0
- data/lib/pdf_writing_tools.rb +350 -0
- data/lib/pdf_writing_tools_actions.rb +109 -45
- data/lib/pdf_writing_tools_process.rb +44 -5
- metadata +6 -34
- data/MIT-LICENSE +0 -20
- data/README.rdoc +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a14c40e1817a741cae4260b4e37b76bbcd86bc8e079414756794eea8ad0f0ce
|
4
|
+
data.tar.gz: fdecb3e874950d20978c58048126d88c2c52c14021925cfaec09b7f702c4c574
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e8cff4cb4d7cae857f1abbd87a8509ff13ec0ed6b3c04880851cbacaede5057dd2f5222c2fd2bcc9bb0ed485b3d9d67abdfd1c090e38265c0fa35af92c3fcf3
|
7
|
+
data.tar.gz: e1b38b8925c34e2b148e4b32ba3b47ed83d10c69ffc36ea279ff54206b154b173597768ed5d28e0bc51c8943a89a052354a9b3f6750ab266e7f339933cb24f31
|
data/assets/bullet.png
ADDED
Binary file
|
data/lib/pdf_writing_tools.rb
CHANGED
@@ -11,7 +11,19 @@ module PdfWritingTools
|
|
11
11
|
# li (elemento di lista non ordinata)
|
12
12
|
# b (grassetto)
|
13
13
|
# i (italico)
|
14
|
+
# span
|
14
15
|
# Altri tag non in elenco, vengono ignorati o causano errore
|
16
|
+
# Il tag <nothtml> ha lo scopo di sottolineare, che questo markup language non è html, anche
|
17
|
+
# se programmando, cerco di renderlo il più simile possibile all'html.
|
18
|
+
|
19
|
+
# L'oggetto xml, viene letto ricorsivamente. Si crea una lista, contenente
|
20
|
+
# dei dizionari. Ciascun dizionario contiene:
|
21
|
+
# Il nome di un'azione: :action_name
|
22
|
+
# Una lista "data", contenente un dizionario con al suo interno le specifiche
|
23
|
+
# da dare a prawn, per "disegnare" del testo o per disegnare un'immagine
|
24
|
+
#
|
25
|
+
# Le p
|
26
|
+
|
15
27
|
def self.draw_xml_object(pdf, xml_object)
|
16
28
|
# Ottengo una lista di azioni, ciascuna delle quali, quando eseguita,
|
17
29
|
# permette di disegnare una parte del documento xml all'interno del pdf
|
@@ -32,4 +44,342 @@ module PdfWritingTools
|
|
32
44
|
end
|
33
45
|
actions_list
|
34
46
|
end
|
47
|
+
|
48
|
+
|
49
|
+
######## Disegna una cella di dimensioni fissate, nella data posizione. ######
|
50
|
+
# E' Possibile indicare il colore del bordo, dello sfondo, del font e della
|
51
|
+
# relativa dimensione (oltre a tutta un'altra serie di parametri (opts), vedere prima
|
52
|
+
# parte della funzione)
|
53
|
+
# La cella, viene disegnata "globalmente", rispetto al pdf, ossia NON relativamente
|
54
|
+
# ad altri contenitori.
|
55
|
+
# x e y, indicano rispettivamente le coordinate x e y del vertice in alto a
|
56
|
+
# sinistra del box.
|
57
|
+
# Il vertice è relativo al margine superiore del pdf, contrariamente a quanto
|
58
|
+
# accade quando si utilizzano le primitive prawn (dove il margine di riferimento è
|
59
|
+
# quello basso della pagina).
|
60
|
+
# Bisogna pertanto prestare attenzione quando si dovessero mischiare primitive prawn
|
61
|
+
# con queste funzioni.
|
62
|
+
def self.draw_cell_fixed_height(pdf, x, y, w, h, t, opts={})
|
63
|
+
t = (t.class == String ? [{text: t}] : t)
|
64
|
+
font_size = opts[:font_size] || 10
|
65
|
+
style = opts[:style] || :normal
|
66
|
+
align = opts[:align] || :left
|
67
|
+
valign = opts[:valign] || :top
|
68
|
+
font_color = opts[:font_color] || '000000'
|
69
|
+
border_color = opts[:border_color] || '000000'
|
70
|
+
background_color = opts[:background_color] || 'FFFFFF'
|
71
|
+
left_padding = opts[:left_padding] || 5
|
72
|
+
right_padding = opts[:right_padding] || 5
|
73
|
+
top_padding = opts[:top_padding] || 5
|
74
|
+
bottom_padding = opts[:bottom_padding] || 5
|
75
|
+
pdf_height = opts[:pdf_height] || 297.mm
|
76
|
+
pdf_width = opts[:pdf_width] || 21.cm
|
77
|
+
|
78
|
+
result = ''
|
79
|
+
|
80
|
+
pdf.canvas do
|
81
|
+
pdf.line_width = 0.5
|
82
|
+
|
83
|
+
# Colore di sfondo della cella
|
84
|
+
pdf.fill_color(background_color)
|
85
|
+
|
86
|
+
# Disegna lo sfondo della cella
|
87
|
+
pdf.fill_rectangle([x, pdf_height - y], w, h)
|
88
|
+
|
89
|
+
pdf.stroke_color border_color
|
90
|
+
pdf.stroke_rectangle([x, pdf_height - y], w, h)
|
91
|
+
|
92
|
+
# Colore del testo nella cella
|
93
|
+
pdf.fill_color(font_color)
|
94
|
+
# Disegno il testo contenuto nella cella
|
95
|
+
if false
|
96
|
+
result = pdf.text_box(
|
97
|
+
t,
|
98
|
+
at: [x + left_padding, pdf_height - y - top_padding],
|
99
|
+
width: w - left_padding - right_padding,
|
100
|
+
height: h - top_padding - bottom_padding,
|
101
|
+
size: font_size,
|
102
|
+
style: style,
|
103
|
+
align: align,
|
104
|
+
valign: valign
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
if true
|
109
|
+
at = [x + left_padding, pdf_height - y - top_padding]
|
110
|
+
width = w - left_padding - right_padding
|
111
|
+
height = h - top_padding - bottom_padding + 1.cm
|
112
|
+
|
113
|
+
pdf.formatted_text_box(t, width: width, height: height, at: at)
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
result
|
119
|
+
end
|
120
|
+
|
121
|
+
# Disegna una cella di altezza variabile nella data posizione, in funzione del contenuto.
|
122
|
+
# E' possibile indicare il colore del bordo, dello sfondo della cella e del font e della
|
123
|
+
# relativa dimensione del testo ivi contenuto (oltre ad altri parametri, vedi prima parte
|
124
|
+
# della funzione).
|
125
|
+
# Produce un dizionario, con l'altezza della cella disegnata ed eventuale testo non disegnato
|
126
|
+
# (es. nel caso di sopraggiunto fine pagina).
|
127
|
+
# Il campo draw_simulation, serve ad evitare che la cella venga realmente disegnata nel pdf.
|
128
|
+
# In questo modo, posso ottenere l'altezza, eventuale testo "avanzato" e prendere decisioni che
|
129
|
+
# non dipendano da un'unica cella, ma da un gruppo di celle, come ad esempio quando devo
|
130
|
+
# disegnare la riga di una tabella che essendo composta da più celle, ha un altezza
|
131
|
+
# che dipende dall'altezza massima delle celle "autoridimensionanti" che la compongono.
|
132
|
+
def self.draw_cell_auto_height(pdf, draw_simulation, x, y, w, t, opts={})
|
133
|
+
font_size = opts[:font_size] || 10
|
134
|
+
style = opts[:style] || :normal
|
135
|
+
align = opts[:align] || :left
|
136
|
+
valign = opts[:align] || :top
|
137
|
+
font_color = opts[:font_color] || "000000"
|
138
|
+
border_color = opts[:border_color] || "000000"
|
139
|
+
background_color = opts[:background_color] || "FFFFFF"
|
140
|
+
left_padding = opts[:left_padding] || 5
|
141
|
+
right_padding = opts[:right_padding] || 5
|
142
|
+
top_padding = opts[:top_padding] || 5
|
143
|
+
bottom_padding = opts[:bottom_padding] || 5
|
144
|
+
pdf_height = opts[:pdf_height] || 297.mm
|
145
|
+
pdf_width = opts[:pdf_width] || 21.cm
|
146
|
+
|
147
|
+
result = 0
|
148
|
+
|
149
|
+
pdf.canvas do
|
150
|
+
# Non utilizzo l'helper (pdf.text_box), in quanto scriverebbe direttamente nel pdf
|
151
|
+
# ma io ho bisogno di conoscere l'altezza del box di testo, prima di scriverlo, in
|
152
|
+
# modo da poter disegnare PRIMA lo sfondo
|
153
|
+
b = Prawn::Text::Box.new(
|
154
|
+
t || "",
|
155
|
+
{:at => [x + left_padding, pdf_height - y - top_padding],
|
156
|
+
:width => w - left_padding-right_padding,
|
157
|
+
:size => font_size,
|
158
|
+
:style => style,
|
159
|
+
:overflow => :expand,
|
160
|
+
:document => pdf})
|
161
|
+
|
162
|
+
# Effettuo il render, ma in modalità "prova" (dry_run = true), così posso conoscere quale sarà
|
163
|
+
# l'altezza del box prima di disegnarlo
|
164
|
+
|
165
|
+
text_overflow = b.render(:dry_run => true)
|
166
|
+
|
167
|
+
# Altezza del box, non ancora disegnato
|
168
|
+
h = b.height
|
169
|
+
|
170
|
+
if not draw_simulation
|
171
|
+
# Se non sono in simulazione...
|
172
|
+
# ... ora che conosco quale sarà l'altezza del box di testo, posso disegnare lo sfondo
|
173
|
+
pdf.fill_color(background_color) # colore dello sfondo
|
174
|
+
pdf.stroke_color(border_color) # Colore del bordo
|
175
|
+
|
176
|
+
pdf.fill_and_stroke_rectangle([x,pdf_height-y], (w + left_padding + right_padding), (h+top_padding+bottom_padding))
|
177
|
+
|
178
|
+
# ... e infine, sopra lo sfondo disegno il testo
|
179
|
+
pdf.fill_color(font_color) # colore del testo
|
180
|
+
text_overflow = b.render()
|
181
|
+
|
182
|
+
# text_overflow è l'eventuale testo avanzato
|
183
|
+
end
|
184
|
+
|
185
|
+
# La cella potrebbe non riuscire ad espandersi a sufficienza (troppo vicina al
|
186
|
+
# fine pagine, quindi può essere che del testo "avanzi" )
|
187
|
+
result = {height: h + top_padding + bottom_padding, overflow: text_overflow}
|
188
|
+
end
|
189
|
+
|
190
|
+
result
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
def self.draw_row_fixed_height(pdf, x, y, widths, height, texts, opts={})
|
195
|
+
font_sizes = opts[:font_sizes] || [10]
|
196
|
+
styles = opts[:styles] || [:normal]
|
197
|
+
alignments = opts[:alignments] || [:left]
|
198
|
+
valignments = opts[:valignments] || [:top]
|
199
|
+
font_colors = opts[:font_colors] || ["000000"]
|
200
|
+
border_colors = opts[:border_colors] || ["000000"]
|
201
|
+
background_colors = opts[:background_colors] || ["FFFFFF"]
|
202
|
+
paddings = opts[:paddings] || [{left_padding: 5, right_padding: 5, top_padding: 5, bottom_padding: 5}]
|
203
|
+
pdf_height = opts[:pdf_height] || 297.mm
|
204
|
+
pdf_width = opts[:pdf_width] || 21.cm
|
205
|
+
|
206
|
+
offset = 0
|
207
|
+
|
208
|
+
widths.each_with_index do |width, i|
|
209
|
+
font_size = font_sizes[i] || font_sizes[0]
|
210
|
+
style = styles[i] || styles[0]
|
211
|
+
align = alignments[i] || alignments[0]
|
212
|
+
valign = valignments[i] || valignments[0]
|
213
|
+
font_color = font_colors[i] || font_colors[0]
|
214
|
+
border_color = border_colors[i] || border_colors[0]
|
215
|
+
background_color = background_colors[i] || background_colors[0]
|
216
|
+
padding = paddings[i] || paddings[0]
|
217
|
+
|
218
|
+
cell_opts = {}
|
219
|
+
|
220
|
+
cell_opts[:font_size] = font_size
|
221
|
+
cell_opts[:style] = style
|
222
|
+
cell_opts[:align] = align
|
223
|
+
cell_opts[:valign] = valign
|
224
|
+
cell_opts[:font_color] = font_color
|
225
|
+
cell_opts[:border_color] = border_color
|
226
|
+
cell_opts[:background_color] = background_color
|
227
|
+
cell_opts[:left_padding] = padding[:left_padding]
|
228
|
+
cell_opts[:right_padding] = padding[:right_padding]
|
229
|
+
cell_opts[:top_padding] = padding[:top_padding]
|
230
|
+
cell_opts[:bottom_padding] = padding[:bottom_padding]
|
231
|
+
cell_opts[:pdf_width] = 21.cm
|
232
|
+
cell_opts[:pdf_height] = 297.mm
|
233
|
+
|
234
|
+
draw_cell_fixed_height( pdf, x+offset, y, width, height, texts[i], cell_opts)
|
235
|
+
|
236
|
+
offset += width
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Produce la y su pdf, dove disegnare la prossima riga
|
241
|
+
def self.draw_row_auto_height(pdf, draw_simulation, x, y, widths, texts, opts = {})
|
242
|
+
font_sizes = opts[:font_sizes] || [10]
|
243
|
+
styles = opts[:styles] || [:normal]
|
244
|
+
alignments = opts[:alignments] || [:left]
|
245
|
+
valignments = opts[:valignments] || [:top]
|
246
|
+
font_colors = opts[:font_colors] || ["000000"]
|
247
|
+
border_colors = opts[:border_colors] || ["000000"]
|
248
|
+
background_colors = opts[:background_colors] || ["FFFFFF"]
|
249
|
+
paddings = opts[:paddings] || [{left_padding: 5, right_padding: 5, top_padding: 5, bottom_padding: 5}]
|
250
|
+
pdf_height = opts[:pdf_height] || 297.mm
|
251
|
+
pdf_width = opts[:pdf_width] || 21.cm
|
252
|
+
pdf_margin_top = opts[:pdf_margin_top] || 2.cm
|
253
|
+
pdf_margin_bottom = opts[:pdf_margin_bottom] || 2.cm
|
254
|
+
pdf_margin_left = opts[:pdf_margin_left] || 2.cm
|
255
|
+
pdf_margin_right = opts[:pdf_margin_right] || 2.cm
|
256
|
+
|
257
|
+
new_y = pdf_margin_top
|
258
|
+
|
259
|
+
loop do
|
260
|
+
|
261
|
+
max_cell_height = 0
|
262
|
+
|
263
|
+
# 1 - Cerco l'altezza della riga
|
264
|
+
# Per prima cosa, simulo il disegno delle varie celle che compongono la riga.
|
265
|
+
# In questo modo, posso sapere quale sarà la massima altezza raggiunta da una
|
266
|
+
# cella, ossia l'altezza che deve avere la riga
|
267
|
+
offset = 0
|
268
|
+
widths.each_with_index do |width, i|
|
269
|
+
font_size = font_sizes[i] || font_sizes[0]
|
270
|
+
style = styles[i] || styles[0]
|
271
|
+
align = alignments[i] || alignments[0]
|
272
|
+
valign = valignments[i] || valignments[0]
|
273
|
+
font_color = font_colors[i] || font_colors[0]
|
274
|
+
border_color = border_colors[i] || border_colors[0]
|
275
|
+
background_color = background_colors[i] || background_colors[0]
|
276
|
+
padding = paddings[i] || paddings[0]
|
277
|
+
|
278
|
+
cell_opts = {}
|
279
|
+
cell_opts[:font_size] = font_size
|
280
|
+
cell_opts[:style] = style
|
281
|
+
cell_opts[:align] = align
|
282
|
+
cell_opts[:valign] = valign
|
283
|
+
cell_opts[:font_color] = font_color
|
284
|
+
cell_opts[:border_color] = border_color
|
285
|
+
cell_opts[:background_color] = background_color
|
286
|
+
cell_opts[:left_padding] = padding[:left_padding]
|
287
|
+
cell_opts[:right_padding] = padding[:right_padding]
|
288
|
+
cell_opts[:top_padding] = padding[:top_padding]
|
289
|
+
cell_opts[:bottom_padding] = padding[:bottom_padding]
|
290
|
+
cell_opts[:pdf_height] = 297.mm
|
291
|
+
cell_opts[:pdf_weigth] = 21.cm
|
292
|
+
|
293
|
+
r = draw_cell_auto_height(pdf, draw_simulation = true, x+offset, y, width, texts[i], cell_opts)
|
294
|
+
|
295
|
+
if r[:height] > max_cell_height
|
296
|
+
max_cell_height = r[:height]
|
297
|
+
end
|
298
|
+
|
299
|
+
offset += width
|
300
|
+
end
|
301
|
+
|
302
|
+
if y + max_cell_height > pdf_height - pdf_margin_bottom
|
303
|
+
max_cell_height = pdf_height - pdf_margin_bottom - y
|
304
|
+
end
|
305
|
+
|
306
|
+
# A seguito della simulazione, ho ottenuto l'altezza di riga...
|
307
|
+
# ... ora passo al disegno vero e proprie delle celle...
|
308
|
+
offset = 0
|
309
|
+
rtexts = []
|
310
|
+
widths.each_with_index do |width, i|
|
311
|
+
font_size = font_sizes[i] || font_sizes[0]
|
312
|
+
style = styles[i] || styles[0]
|
313
|
+
align = alignments[i] || alignments[0]
|
314
|
+
valign = valignments[i] || valignments[0]
|
315
|
+
font_color = font_colors[i] || font_colors[0]
|
316
|
+
border_color = border_colors[i] || border_colors[0]
|
317
|
+
background_color = background_colors[i] || background_colors[0]
|
318
|
+
padding = paddings[i] || paddings[0]
|
319
|
+
|
320
|
+
cell_opts = {}
|
321
|
+
cell_opts[:font_size] = font_size
|
322
|
+
cell_opts[:style] = style
|
323
|
+
cell_opts[:align] = align
|
324
|
+
cell_opts[:valign] = valign
|
325
|
+
cell_opts[:font_color] = font_color
|
326
|
+
cell_opts[:border_color] = border_color
|
327
|
+
cell_opts[:background_color] = background_color
|
328
|
+
cell_opts[:left_padding] = padding[:left_padding]
|
329
|
+
cell_opts[:right_padding] = padding[:right_padding]
|
330
|
+
cell_opts[:top_padding] = padding[:top_padding]
|
331
|
+
cell_opts[:bottom_padding] = padding[:bottom_padding]
|
332
|
+
cell_opts[:pdf_height] = 297.mm
|
333
|
+
cell_opts[:pdf_weigth] = 21.cm
|
334
|
+
|
335
|
+
# ...disegno le celle vere e proprie ad altezza fissa, con l'altezza
|
336
|
+
# ricavata dal passo precedente.
|
337
|
+
r = draw_cell_fixed_height(pdf, x+offset, y, width, max_cell_height, texts[i], opts)
|
338
|
+
|
339
|
+
# Gli eventuali "testi residui" (es. raggiunto fine pagina) li raccolgo
|
340
|
+
# nel seguente array
|
341
|
+
rtexts << r
|
342
|
+
|
343
|
+
offset += width
|
344
|
+
end
|
345
|
+
|
346
|
+
texts = rtexts
|
347
|
+
|
348
|
+
# Se nei testi residui, sono presenti solo stringhe vuote, posso uscire dal
|
349
|
+
# loop, altrimenti, devo cambiare pagina e disegnare una nuova riga con i
|
350
|
+
# testi rimanenti
|
351
|
+
if !check_no_empty_string_presence(texts)
|
352
|
+
new_y = y + max_cell_height
|
353
|
+
break
|
354
|
+
else
|
355
|
+
pdf.start_new_page
|
356
|
+
y = pdf_margin_top # Posiziono il cursore ad inizio della nuova pagina
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
return new_y
|
361
|
+
end
|
362
|
+
|
363
|
+
def self.check_no_empty_string_presence(string_list)
|
364
|
+
string_list.each do |string_element|
|
365
|
+
if string_element != ""
|
366
|
+
return true
|
367
|
+
end
|
368
|
+
end
|
369
|
+
return false
|
370
|
+
end
|
371
|
+
|
372
|
+
# Dato il percorso file_path, genera un documento xml, a cui sono stati rimossi gli
|
373
|
+
# a capo e gli spazi multipli
|
374
|
+
def self.get_cleaned_xml_from_file(file_path)
|
375
|
+
text = File.read(file_path)
|
376
|
+
|
377
|
+
# Rimuove gli a capo e le tabulazioni sostituendoli con uno spazio
|
378
|
+
text = text.gsub(/\n|\t/, ' ')
|
379
|
+
|
380
|
+
# Rimuove le spaziature multiple sostituendole con uno spazio
|
381
|
+
text = text.gsub(/\s+/, ' ')
|
382
|
+
|
383
|
+
Nokogiri::XML(text)
|
384
|
+
end
|
35
385
|
end
|
@@ -1,4 +1,43 @@
|
|
1
|
+
# Questo modulo, si occupa di
|
2
|
+
|
3
|
+
|
4
|
+
# Le action, definiscono, cosa deve essere fatto (scritto, colorato, disegnato) nel pdf
|
5
|
+
# La forma di una action è una coppia (nome_action, dati_per_la_action)
|
6
|
+
# L'esecuzione di una action consiste nel realizzare la action usando i dati ad essa associati
|
7
|
+
# Quando l'oggetto xml che rappresenta il mio documento pdf da realizzare viene processato,
|
8
|
+
# viene creata una catena (lista) di action.
|
9
|
+
# Alcune action consecutive, possono essere "fuse" tra loro in un'unica action, così da
|
10
|
+
# efficientare il processo di scrittura del file pdf.
|
11
|
+
# Ogni singola action, viene poi eseguita, utilizzando le funzioni di prawn.
|
12
|
+
|
13
|
+
# Nella parte PUBLIC di questa classe, inserire solo metodi che generino action
|
14
|
+
# La classe PdfWritingToolsProcess, userà i metodi pubblic di questa classe per generare
|
15
|
+
# una list di action. Fornendo la lista di actions al metodo
|
16
|
+
# PdfWritingToolsActions.execute_actions, verrà dato il via all'esecuzione delle actions...
|
17
|
+
|
18
|
+
# Nella parte PRIVATE di questa classe, inserire solo metodi che eseguano le action
|
19
|
+
# ... le singole actions, verrano eseguite tramite i metodi privati di questa classe
|
20
|
+
|
1
21
|
module PdfWritingToolsActions
|
22
|
+
# Esegue le azioni, andando a concatenare quelle "contigue" nella lista,
|
23
|
+
# che riguardano la scrittura di testo.
|
24
|
+
# La lista actions, in sostanza comprende più azioni. Quelle contigue che riguardano
|
25
|
+
# la scrittura di testo, non vanno eseguite singolarmente, ma devono essere "fuse"
|
26
|
+
# e poi eseguite. Le action che riguardano la "scrittura" di un immagine invece, non
|
27
|
+
# possono essere fuse, pertanto vanno eseguite singolarmente
|
28
|
+
def self.execute_actions(pdf, actions, last_actn_name, data)
|
29
|
+
actions.each do |action|
|
30
|
+
if action[:action_name] == :draw_formatted_text
|
31
|
+
last_actn_name, data = text_action(pdf, action, last_actn_name, data)
|
32
|
+
elsif action[:action_name] == :draw_image
|
33
|
+
last_actn_name, data = img_action(pdf, action, last_actn_name, data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Disegno l'ultima action della lista
|
38
|
+
execute_action(pdf, last_actn_name, data)
|
39
|
+
end
|
40
|
+
|
2
41
|
# Questa action contiene istruzioni per disegnare un "a capo" nel PDF
|
3
42
|
def self.new_line_action
|
4
43
|
[{ action_name: :draw_formatted_text, data: [{ text: "\n" }] }]
|
@@ -22,64 +61,89 @@ module PdfWritingToolsActions
|
|
22
61
|
end
|
23
62
|
|
24
63
|
# Questa action, contiene istruzioni per disegnare un "bullet", ossia
|
25
|
-
# un oggetto grafico, tipo un segno di spunta accanto all'
|
64
|
+
# un oggetto grafico, tipo un segno di spunta accanto all'elemento di
|
26
65
|
# una lista
|
27
66
|
def self.bullet_action
|
28
67
|
[
|
29
68
|
{
|
30
69
|
action_name: :draw_image, data:
|
31
70
|
{
|
32
|
-
url:
|
71
|
+
url: File.expand_path("../../assets/bullet.png", __FILE__)
|
72
|
+
# Pallino
|
33
73
|
}
|
34
74
|
}
|
35
75
|
]
|
36
76
|
end
|
37
77
|
|
38
|
-
|
39
|
-
def self.text_action(pdf, action, last_action_name, data)
|
40
|
-
if last_action_name.nil?
|
41
|
-
data = action[:data]
|
42
|
-
elsif last_action_name == :draw_formatted_text
|
43
|
-
data += action[:data]
|
44
|
-
else
|
45
|
-
execute_action(pdf, last_action_name, data)
|
46
|
-
data = action[:data]
|
47
|
-
end
|
48
|
-
[:draw_formatted_text, data]
|
49
|
-
end
|
78
|
+
private
|
50
79
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
80
|
+
# Esegue un'azione, andando a scrivere del testo nel PDF, oppure un'immagine
|
81
|
+
def self.execute_action(pdf, action_name, data)
|
82
|
+
if action_name == :draw_formatted_text
|
83
|
+
pdf.formatted_text(data, align: :left)
|
84
|
+
elsif action_name == :draw_image
|
85
|
+
# Il testo disegnato dopo un immagine, non appare allineato. Per questo
|
86
|
+
# motivo, disegno l'immagine un' po' prima di dove è previsto (spostando il cursore)
|
87
|
+
# per poi ripristinare la posizione originaria del cursore.
|
88
|
+
|
89
|
+
# L'operazione di cui sopra, non va eseguita a fondo pagina, altrimenti,
|
90
|
+
# il salto pagina automatico crea problemi sulla realizzazione del pdf,
|
91
|
+
# quindi...
|
92
|
+
|
93
|
+
# Se la posizione del cursore (misurata, dal fondo margine di pagina)
|
94
|
+
# è inferiore ad un cm, mi sposto direttamente sulla nuova pagina..
|
95
|
+
if pdf.cursor <= 1.cm
|
96
|
+
pdf.start_new_page
|
97
|
+
end
|
98
|
+
|
99
|
+
current_cursor_position = pdf.cursor
|
100
|
+
# Anticipo il disegno dell'immagine spostando il cursore di n unità verso
|
101
|
+
# l'alto
|
102
|
+
n = 0
|
103
|
+
pdf.move_cursor_to(current_cursor_position - n)
|
104
|
+
|
105
|
+
# Disegno l'immagine (al momento questo metodo disegna solo il "bullet" degli elenchi)
|
106
|
+
pdf.image(data[:url], height: 6, width: 6)
|
107
|
+
|
108
|
+
# Ripristino la posizione originaria del cursore, in modo che il testo
|
109
|
+
# sia allineato all'immagine preceente
|
110
|
+
pdf.move_cursor_to(current_cursor_position)
|
111
|
+
elsif action_name == :draw_rectangle
|
60
112
|
|
61
|
-
|
62
|
-
def self.execute_action(pdf, action_name, data)
|
63
|
-
if action_name == :draw_formatted_text
|
64
|
-
pdf.formatted_text(data, align: :left)
|
65
|
-
elsif action_name == :draw_image
|
66
|
-
current_cursor_position = pdf.cursor
|
67
|
-
pdf.move_cursor_to(current_cursor_position - 3)
|
68
|
-
pdf.image(data[:url], height: 6, width: 6)
|
69
|
-
pdf.move_cursor_to(current_cursor_position)
|
113
|
+
end
|
70
114
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
if
|
78
|
-
|
79
|
-
|
80
|
-
|
115
|
+
|
116
|
+
# Esegue last_action, oppure concatena i dati di action e di last_action
|
117
|
+
# action: azione "corrente"
|
118
|
+
# last_action_name: nome dell'azione che precede l'azione corrente
|
119
|
+
# data: dati dell'azione che precede l'azione corrente
|
120
|
+
def self.text_action(pdf, action, last_action_name, data)
|
121
|
+
if last_action_name.nil?
|
122
|
+
# Prima azione della lista (last_action_name = nil), quindi per ora
|
123
|
+
# nessun concatenamento di dati
|
124
|
+
data = action[:data]
|
125
|
+
elsif last_action_name == :draw_formatted_text
|
126
|
+
# La action attuale richiede di "disegnare" testo formattato, come l'ultima azione
|
127
|
+
# quindi, concateno i dati di questa action, con quelli dell'ultima action
|
128
|
+
data += action[:data]
|
129
|
+
else
|
130
|
+
# La action attuale, differisce dall'ultima, quindi eseguo l'ultima action (che sarà
|
131
|
+
# un'immagine visto che quella attuale è il disegno di testo) e produco i dati di
|
132
|
+
# quella corrente
|
133
|
+
execute_action(pdf, last_action_name, data)
|
134
|
+
data = action[:data]
|
81
135
|
end
|
136
|
+
|
137
|
+
[:draw_formatted_text, data]
|
82
138
|
end
|
83
|
-
|
84
|
-
|
85
|
-
|
139
|
+
|
140
|
+
# Mentre la "scrittura/disegno" del testo, puo' essere "concatenato", nel caso
|
141
|
+
# delle immagini no. Una action sull' immagine pertanto, interrompe la
|
142
|
+
# possibilita' di concatenare i data delle action di testo. Quindi, eseguo
|
143
|
+
# in ogni caso last_action, a meno che last_action non sia nil (ossia action
|
144
|
+
# e' la prima azione della lista)
|
145
|
+
def self.img_action(pdf, action, last_action_name, data)
|
146
|
+
execute_action(pdf, last_action_name, data) unless last_action_name.nil?
|
147
|
+
[:draw_image, action[:data]]
|
148
|
+
end
|
149
|
+
end
|
@@ -23,8 +23,8 @@ module PdfWritingToolsProcess
|
|
23
23
|
|
24
24
|
# Produce la "action" che permette di disegnare nel pdf, il testo con le
|
25
25
|
# proprieta' specificate in proprerties
|
26
|
-
def self.process_xml_text(xml_obj, properties, size = 12, upcase = false)
|
27
|
-
data = { text: (upcase ? xml_obj.text.upcase : xml_obj.text) + ' ', styles: properties, size: size }
|
26
|
+
def self.process_xml_text(xml_obj, properties, size = 12, upcase = false, color = "#000000")
|
27
|
+
data = { text: (upcase ? xml_obj.text.upcase : xml_obj.text) + ' ', styles: properties, size: size, color: color[1..-1] }
|
28
28
|
[{ action_name: :draw_formatted_text, data: [data] }]
|
29
29
|
end
|
30
30
|
|
@@ -58,6 +58,7 @@ module PdfWritingToolsProcess
|
|
58
58
|
# lista indicato da li
|
59
59
|
def self.process_xml_tag_li(xml_obj, _properties, idx=nil)
|
60
60
|
actions_list = []
|
61
|
+
|
61
62
|
xml_obj.children.each do |child|
|
62
63
|
actions_list += process_xml_obj(child, [])
|
63
64
|
end
|
@@ -67,7 +68,7 @@ module PdfWritingToolsProcess
|
|
67
68
|
else
|
68
69
|
PdfWritingToolsActions.new_line_action + PdfWritingToolsActions.bullet_action + PdfWritingToolsActions.indent_action(4) + actions_list
|
69
70
|
end
|
70
|
-
end
|
71
|
+
end
|
71
72
|
|
72
73
|
# Produce le "actions" che permettono di disegnare nel PDF, il contenuto
|
73
74
|
# del tag p
|
@@ -79,16 +80,42 @@ module PdfWritingToolsProcess
|
|
79
80
|
PdfWritingToolsActions.new_line_action + actions_list
|
80
81
|
end
|
81
82
|
|
83
|
+
# Al momento assumo che h1 contenga solo testo, poi eventualmente
|
84
|
+
# renderla ricorsiva permettendo a h1 di contenere altri tag.
|
85
|
+
# Sebbene l'xml che sto processando non sia vero html, voglio cercare di dargli
|
86
|
+
# il più possibile lo stesso comportamento. All'interno di h1, possono essere
|
87
|
+
# presenti solo elementi inline. Al momento non controllo questa cosa, quindi preferisco
|
88
|
+
# tagliare la testa al toro, assumento che h1 contenga solo testo.
|
82
89
|
def self.process_xml_tag_h1(xml_obj, properties)
|
83
90
|
actions_list = process_xml_text(xml_obj.child, [:bold], 16, true)
|
84
91
|
PdfWritingToolsActions.new_line_action + actions_list + PdfWritingToolsActions.new_line_action * 2
|
85
92
|
end
|
86
93
|
|
94
|
+
# Al momento assumo che span contenga solo testo, poi eventualmente
|
95
|
+
# renderla ricorsiva permettendo a span di contenere altri tag.
|
96
|
+
# Sebbene l'xml che sto processando non sia vero html, voglio cercare di dargli
|
97
|
+
# il più possibile lo stesso comportamento. All'interno di span, possono essere
|
98
|
+
# presenti solo elementi inline. Al momento non controllo questa cosa, quindi preferisco
|
99
|
+
# tagliare la testa al toro, assumento che span contenga solo testo.
|
100
|
+
def self.process_xml_tag_span(xml_obj, properties)
|
101
|
+
span_styles_attributes = string_to_attributes(xml_obj[:style])
|
102
|
+
|
103
|
+
color = "#000000"
|
104
|
+
|
105
|
+
if span_styles_attributes.has_key?("color")
|
106
|
+
color = span_styles_attributes["color"]
|
107
|
+
end
|
108
|
+
|
109
|
+
actions_list = process_xml_text(xml_obj.child, properties, size=12, upcase=false, color)
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
|
87
114
|
# Produce le actions necessarie per disegnare nel PDF un determinato
|
88
115
|
# "tag"
|
89
|
-
def self.process_xml_obj(xml_obj, properties)
|
116
|
+
def self.process_xml_obj(xml_obj, properties, attributes={})
|
90
117
|
case xml_obj.name
|
91
|
-
when 'text', 'b', 'i', 'ul', 'li', 'p', 'h1', 'ol'
|
118
|
+
when 'text', 'b', 'i', 'ul', 'li', 'p', 'h1', 'ol', 'span'
|
92
119
|
@process_xml_tag_table[xml_obj.name].call(xml_obj, properties)
|
93
120
|
when 'br'
|
94
121
|
PdfWritingToolsActions.new_line_action
|
@@ -97,9 +124,21 @@ module PdfWritingToolsProcess
|
|
97
124
|
end
|
98
125
|
end
|
99
126
|
|
127
|
+
def self.string_to_attributes(s)
|
128
|
+
result = {}
|
129
|
+
|
130
|
+
s.split(";").each do |el|
|
131
|
+
e = el.split(":")
|
132
|
+
result[e[0]] = e[1]
|
133
|
+
end
|
134
|
+
|
135
|
+
result
|
136
|
+
end
|
137
|
+
|
100
138
|
@process_xml_tag_table =
|
101
139
|
{
|
102
140
|
'text' => method(:process_xml_text),
|
141
|
+
'span' => method(:process_xml_tag_span),
|
103
142
|
'b' => method(:process_xml_tag_b),
|
104
143
|
'i' => method(:process_xml_tag_i),
|
105
144
|
'ul' => method(:process_xml_tag_ul),
|
metadata
CHANGED
@@ -1,53 +1,24 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pdf_writing_tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- JSalvo1978
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-07
|
12
|
-
dependencies:
|
13
|
-
|
14
|
-
name: rails
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 4.2.8
|
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.8
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: sqlite3
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
description: Plugin for pdf writing
|
11
|
+
date: 2023-08-07 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Plugin for write pdf in a simplified manner, using prawn as backend
|
42
14
|
email:
|
43
15
|
- gianmario.salvetti@gmail.com
|
44
16
|
executables: []
|
45
17
|
extensions: []
|
46
18
|
extra_rdoc_files: []
|
47
19
|
files:
|
48
|
-
- MIT-LICENSE
|
49
|
-
- README.rdoc
|
50
20
|
- Rakefile
|
21
|
+
- assets/bullet.png
|
51
22
|
- lib/pdf_writing_tools.rb
|
52
23
|
- lib/pdf_writing_tools/green-check-mark-sign-addition-icon-vector-13230189.jpg
|
53
24
|
- lib/pdf_writing_tools/version.rb
|
@@ -96,6 +67,7 @@ post_install_message:
|
|
96
67
|
rdoc_options: []
|
97
68
|
require_paths:
|
98
69
|
- lib
|
70
|
+
- assets
|
99
71
|
required_ruby_version: !ruby/object:Gem::Requirement
|
100
72
|
requirements:
|
101
73
|
- - ">="
|
data/MIT-LICENSE
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
Copyright 2018 Gianmario Salvetti
|
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.rdoc
DELETED