pdf_writing_tools 0.0.11 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45529182709a12f3e41c914f9cdd36583589ca618570fe2f7b8fcdae6fe24305
4
- data.tar.gz: a594ff7382a41a60c07f540d4d77656be8221b6d2429d52e8237593bfa86d742
3
+ metadata.gz: abc0c5dd8900c730c7f016b794bafa2da0fd3bb107e381f62289517c64c3d7ad
4
+ data.tar.gz: e73fe29e7dc36b1c743709bfa1d18e4af47775b740960b895346f07f10484810
5
5
  SHA512:
6
- metadata.gz: 597f10537e24f2b42318e5471a8ddd0222bd16789c044fbd6a47aeb0ab8b226557f534cd84c9104dffbdf460dd53df5f136faef3801d28c9d1ee8e58f5f2dc82
7
- data.tar.gz: 4c6dd9d236317f5d65e51c8557e4dd1d93ae301aa856fea195d45090c07829c4457e85f6fbd4a11509ad5a87acd969ee82f93ce451d201e6ea24aa4eb2ac6954
6
+ metadata.gz: ce5ef7db92f5d41bd438f3d6119b6761c1b96dd248f6fa5a712c898af3a2827cdad2e3b097d40c10ab0b9f61185d80675805458af4ae8423d6ddc40a7739bca9
7
+ data.tar.gz: d4ce2bef3b3eef7f8b2919f4dfb6a40ff95b4fc75520777e7aed312b5cc9c82095a42f46ceeef62e242d5c2e24af5fe4950b290916d7884d5bf9af8759b078f4
data/assets/bullet.png ADDED
Binary file
@@ -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
@@ -30,24 +42,25 @@ module PdfWritingTools
30
42
  actions_list += PdfWritingToolsProcess.process_xml_obj(child, [])
31
43
  end
32
44
  end
33
- actions_list
45
+ actions_list + PdfWritingToolsActions.tr_action + PdfWritingToolsActions.tr_action + actions_list + actions_list
34
46
  end
35
47
 
36
48
 
37
- ######## Disegna una cella di dimensioni fissate, nella data posizione. ######
38
- # E' Possibile indicare il colore del bordo, dello sfondo, del font e della
49
+ ######## Disegna una cella di dimensioni fissate, nella data posizione. ######
50
+ # E' Possibile indicare il colore del bordo, dello sfondo, del font e della
39
51
  # relativa dimensione (oltre a tutta un'altra serie di parametri (opts), vedere prima
40
52
  # parte della funzione)
41
- # La cella, viene disegnata "globalmente", rispetto al pdf, ossia NON relativamente
53
+ # La cella, viene disegnata "globalmente", rispetto al pdf, ossia NON relativamente
42
54
  # ad altri contenitori.
43
- # x e y, indicano rispettivamente le coordinate x e y del vertice in alto a
55
+ # x e y, indicano rispettivamente le coordinate x e y del vertice in alto a
44
56
  # sinistra del box.
45
- # Il vertice è relativo al margine superiore del pdf, contrariamente a quanto
46
- # accade quando si utilizzano le primitive prawn (dove il margine di riferimento è
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 è
47
59
  # quello basso della pagina).
48
- # Bisogna pertanto prestare attenzione quando si mischiano primitive prawn
60
+ # Bisogna pertanto prestare attenzione quando si dovessero mischiare primitive prawn
49
61
  # con queste funzioni.
50
- def self.draw_cell_fixed_height(pdf, x, y, w, h, t, opts={})
62
+ def self.draw_cell_fixed_height(pdf, x, y, w, h, t, opts={}, auto_y=false, no_background=false)
63
+ t = (t.class == String ? [{text: t}] : t)
51
64
  font_size = opts[:font_size] || 10
52
65
  style = opts[:style] || :normal
53
66
  align = opts[:align] || :left
@@ -63,6 +76,8 @@ module PdfWritingTools
63
76
  pdf_width = opts[:pdf_width] || 21.cm
64
77
 
65
78
  result = ''
79
+ y_pos1 = auto_y ? pdf.cursor : (pdf_height - y)
80
+ y_pos2 = auto_y ? pdf.cursor - top_padding : (pdf_height - y - top_padding)
66
81
 
67
82
  pdf.canvas do
68
83
  pdf.line_width = 0.5
@@ -71,26 +86,311 @@ module PdfWritingTools
71
86
  pdf.fill_color(background_color)
72
87
 
73
88
  # Disegna lo sfondo della cella
74
- pdf.fill_rectangle([x, pdf_height - y], w, h)
89
+ if no_background
90
+ else
91
+ pdf.fill_rectangle([x, y_pos1], w, h)
92
+ end
75
93
 
76
94
  pdf.stroke_color border_color
77
- pdf.stroke_rectangle([x, pdf_height - y], w, h)
95
+ pdf.stroke_rectangle([x, y_pos1], w, h)
78
96
 
79
97
  # Colore del testo nella cella
80
98
  pdf.fill_color(font_color)
81
- # Disegno il testo contenuto nella cella
82
- result = pdf.text_box(
83
- t,
84
- at: [x + left_padding, pdf_height - y - top_padding],
85
- width: w - left_padding - right_padding,
86
- height: h - top_padding - bottom_padding,
87
- size: font_size,
88
- style: style,
89
- align: align,
90
- valign: valign
91
- )
99
+
100
+ at = [x + left_padding, y_pos2]
101
+ width = w - left_padding - right_padding
102
+ height = h - top_padding - bottom_padding + 1.cm
103
+ result = pdf.formatted_text_box(t, width: width, height: height, at: at, size: font_size, style: style, align: align, valign: valign)
92
104
  end
93
105
 
94
106
  result
95
107
  end
108
+
109
+ # Disegna una cella di altezza variabile nella data posizione, in funzione del contenuto.
110
+ # E' possibile indicare il colore del bordo, dello sfondo della cella e del font e della
111
+ # relativa dimensione del testo ivi contenuto (oltre ad altri parametri, vedi prima parte
112
+ # della funzione).
113
+ # Produce un dizionario, con l'altezza della cella disegnata ed eventuale testo non disegnato
114
+ # (es. nel caso di sopraggiunto fine pagina).
115
+ # Il campo draw_simulation, serve ad evitare che la cella venga realmente disegnata nel pdf.
116
+ # In questo modo, posso ottenere l'altezza, eventuale testo "avanzato" e prendere decisioni che
117
+ # non dipendano da un'unica cella, ma da un gruppo di celle, come ad esempio quando devo
118
+ # disegnare la riga di una tabella che essendo composta da più celle, ha un altezza
119
+ # che dipende dall'altezza massima delle celle "autoridimensionanti" che la compongono.
120
+ def self.draw_cell_auto_height(pdf, draw_simulation, x, y, w, t, opts={}, auto_y=false, no_background=false)
121
+ font_size = opts[:font_size] || 10
122
+ style = opts[:style] || :normal
123
+ align = opts[:align] || :left
124
+ valign = opts[:valign] || :top
125
+ font_color = opts[:font_color] || "000000"
126
+ border_color = opts[:border_color] || "000000"
127
+ background_color = opts[:background_color] || "FFFFFF"
128
+ left_padding = opts[:left_padding] || 5#
129
+ right_padding = opts[:right_padding] || 5#
130
+ top_padding = opts[:top_padding] || 5#
131
+ bottom_padding = opts[:bottom_padding] || 5#
132
+ pdf_height = opts[:pdf_height] || 297.mm #
133
+ pdf_width = opts[:pdf_width] || 21.cm #
134
+
135
+ t = (t.class == String ? [{text: t, size: font_size, color: font_color}] : t)
136
+
137
+
138
+
139
+ result = 0
140
+ y_pos1 = auto_y ? (pdf.cursor - top_padding) : (pdf_height - y - top_padding)
141
+ y_pos2 = auto_y ? pdf.cursor : (pdf_height - y)
142
+
143
+ pdf.canvas do
144
+ # Non utilizzo l'helper (pdf.formatted_text_box), in quanto scriverebbe direttamente nel pdf
145
+ # ma io ho bisogno di conoscere l'altezza del box di testo formattato, prima di scriverlo, in
146
+ # modo da poter disegnare PRIMA lo sfondo
147
+
148
+
149
+ b = Prawn::Text::Formatted::Box.new(
150
+ t,
151
+ {:at => [x + left_padding, y_pos1 ],
152
+ :width => w - left_padding-right_padding,
153
+ :size => font_size,
154
+ :style => style,
155
+ :overflow => :expand,
156
+ :document => pdf,
157
+ :align => align,
158
+ :valign => valign}) #valign ha un comportamento da indagare, mi fa uscire il testo fuori dal box
159
+
160
+ # Effettuo il render, ma in modalità "prova" (dry_run = true), così posso conoscere quale sarà
161
+ # l'altezza del box prima di disegnarlo
162
+
163
+ text_overflow = b.render(:dry_run => true)
164
+ #text_overflow = text_overflow[:text] #### Temporaneo
165
+
166
+ # Altezza del box, non ancora disegnato
167
+ h = b.height
168
+
169
+ if not draw_simulation
170
+ # Se non sono in simulazione...
171
+ # ... ora che conosco quale sarà l'altezza del box di testo, posso disegnare lo sfondo
172
+ pdf.fill_color(background_color) # colore dello sfondo
173
+ pdf.stroke_color(border_color) # Colore del bordo
174
+
175
+
176
+
177
+ if no_background
178
+ else
179
+ pdf.fill_and_stroke_rectangle([x, y_pos2], (w + left_padding + right_padding), (h+top_padding+bottom_padding))
180
+ end
181
+ # ... e infine, sopra lo sfondo disegno il testo
182
+ pdf.fill_color(font_color) # colore del testo
183
+ text_overflow = b.render()
184
+
185
+ # text_overflow è l'eventuale testo avanzato
186
+ end
187
+
188
+ # La cella potrebbe non riuscire ad espandersi a sufficienza (troppo vicina al
189
+ # fine pagine, quindi può essere che del testo "avanzi" )
190
+ result = {height: h + top_padding + bottom_padding, overflow: text_overflow}
191
+ end
192
+
193
+ result
194
+ end
195
+
196
+
197
+ def self.draw_row_fixed_height(pdf, x, y, widths, height, texts, opts={}, auto_y=false)
198
+ font_sizes = opts[:font_sizes] || [10]
199
+ styles = opts[:styles] || [:normal]
200
+ alignments = opts[:alignments] || [:left]
201
+ valignments = opts[:valignments] || [:top]
202
+ font_colors = opts[:font_colors] || ["000000"]
203
+ border_colors = opts[:border_colors] || ["000000"]
204
+ background_colors = opts[:background_colors] || ["FFFFFF"]
205
+ paddings = opts[:paddings] || [{left_padding: 5, right_padding: 5, top_padding: 5, bottom_padding: 5}]
206
+ pdf_height = opts[:pdf_height] || 297.mm
207
+ pdf_width = opts[:pdf_width] || 21.cm
208
+
209
+ offset = 0
210
+
211
+ widths.each_with_index do |width, i|
212
+ font_size = font_sizes[i] || font_sizes[0]
213
+ style = styles[i] || styles[0]
214
+ align = alignments[i] || alignments[0]
215
+ valign = valignments[i] || valignments[0]
216
+ font_color = font_colors[i] || font_colors[0]
217
+ border_color = border_colors[i] || border_colors[0]
218
+ background_color = background_colors[i] || background_colors[0]
219
+ padding = paddings[i] || paddings[0]
220
+
221
+ cell_opts = {}
222
+
223
+ cell_opts[:font_size] = font_size
224
+ cell_opts[:style] = style
225
+ cell_opts[:align] = align
226
+ cell_opts[:valign] = valign
227
+ cell_opts[:font_color] = font_color
228
+ cell_opts[:border_color] = border_color
229
+ cell_opts[:background_color] = background_color
230
+ cell_opts[:left_padding] = padding[:left_padding]
231
+ cell_opts[:right_padding] = padding[:right_padding]
232
+ cell_opts[:top_padding] = padding[:top_padding]
233
+ cell_opts[:bottom_padding] = padding[:bottom_padding]
234
+ cell_opts[:pdf_width] = 21.cm
235
+ cell_opts[:pdf_height] = 297.mm
236
+
237
+ draw_cell_fixed_height( pdf, x+offset, y, width, height, texts[i], cell_opts, auto_y)
238
+
239
+ offset += width
240
+ end
241
+ pdf.cursor = pdf.cursor - height
242
+ end
243
+
244
+ # Produce la y su pdf, dove disegnare la prossima riga
245
+ def self.draw_row_auto_height(pdf, draw_simulation, x, y, widths, texts, opts = {}, auto_y)
246
+ font_sizes = opts[:font_sizes] || [10]
247
+ styles = opts[:styles] || [:normal]
248
+ alignments = opts[:alignments] || [:left]
249
+ valignments = opts[:valignments] || [:top]
250
+ font_colors = opts[:font_colors] || ["000000"]
251
+ border_colors = opts[:border_colors] || ["000000"]
252
+ background_colors = opts[:background_colors] || ["FFFFFF"]
253
+ paddings = opts[:paddings] || [{left_padding: 5, right_padding: 5, top_padding: 5, bottom_padding: 5}]
254
+ pdf_height = opts[:pdf_height] || 297.mm
255
+ pdf_width = opts[:pdf_width] || 21.cm
256
+ pdf_margin_top = opts[:pdf_margin_top] || 2.cm
257
+ pdf_margin_bottom = opts[:pdf_margin_bottom] || 2.cm
258
+ pdf_margin_left = opts[:pdf_margin_left] || 2.cm
259
+ pdf_margin_right = opts[:pdf_margin_right] || 2.cm
260
+
261
+ new_y = pdf_margin_top
262
+
263
+ loop do
264
+
265
+ max_cell_height = 0
266
+
267
+ # 1 - Cerco l'altezza della riga
268
+ # Per prima cosa, simulo il disegno delle varie celle che compongono la riga.
269
+ # In questo modo, posso sapere quale sarà la massima altezza raggiunta da una
270
+ # cella, ossia l'altezza che deve avere la riga
271
+ offset = 0
272
+ widths.each_with_index do |width, i|
273
+ font_size = font_sizes[i] || font_sizes[0]
274
+ style = styles[i] || styles[0]
275
+ align = alignments[i] || alignments[0]
276
+ valign = valignments[i] || valignments[0]
277
+ font_color = font_colors[i] || font_colors[0]
278
+ border_color = border_colors[i] || border_colors[0]
279
+ background_color = background_colors[i] || background_colors[0]
280
+ padding = paddings[i] || paddings[0]
281
+
282
+ cell_opts = {}
283
+ cell_opts[:font_size] = font_size
284
+ cell_opts[:style] = style
285
+ cell_opts[:align] = align
286
+ cell_opts[:valign] = valign
287
+ cell_opts[:font_color] = font_color
288
+ cell_opts[:border_color] = border_color
289
+ cell_opts[:background_color] = background_color
290
+ cell_opts[:left_padding] = padding[:left_padding]
291
+ cell_opts[:right_padding] = padding[:right_padding]
292
+ cell_opts[:top_padding] = padding[:top_padding]
293
+ cell_opts[:bottom_padding] = padding[:bottom_padding]
294
+ cell_opts[:pdf_height] = 297.mm
295
+ cell_opts[:pdf_weigth] = 21.cm
296
+
297
+ r = draw_cell_auto_height(pdf, draw_simulation = true, x+offset, y, width, texts[i], cell_opts, auto_y)
298
+
299
+ if r[:height] > max_cell_height
300
+ max_cell_height = r[:height]
301
+ end
302
+
303
+ offset += width
304
+ end
305
+
306
+ # Non voglio che la riga ecceda il margine di pagina, quindi se così fosse
307
+ # ricalcolo l'altezza di riga per non superare tale margine
308
+ if y + max_cell_height > pdf_height - pdf_margin_bottom
309
+ max_cell_height = pdf_height - pdf_margin_bottom - y
310
+ end
311
+
312
+ # A seguito della simulazione, ho ottenuto l'altezza di riga...
313
+ # ... ora passo al disegno vero e proprie delle celle...
314
+ offset = 0
315
+ rtexts = []
316
+ widths.each_with_index do |width, i|
317
+ font_size = font_sizes[i] || font_sizes[0]
318
+ style = styles[i] || styles[0]
319
+ align = alignments[i] || alignments[0]
320
+ valign = valignments[i] || valignments[0]
321
+ font_color = font_colors[i] || font_colors[0]
322
+ border_color = border_colors[i] || border_colors[0]
323
+ background_color = background_colors[i] || background_colors[0]
324
+ padding = paddings[i] || paddings[0]
325
+
326
+ cell_opts = {}
327
+ cell_opts[:font_size] = font_size
328
+ cell_opts[:style] = style
329
+ cell_opts[:align] = align
330
+ cell_opts[:valign] = valign
331
+ cell_opts[:font_color] = font_color
332
+ cell_opts[:border_color] = border_color
333
+ cell_opts[:background_color] = background_color
334
+ cell_opts[:left_padding] = padding[:left_padding]
335
+ cell_opts[:right_padding] = padding[:right_padding]
336
+ cell_opts[:top_padding] = padding[:top_padding]
337
+ cell_opts[:bottom_padding] = padding[:bottom_padding]
338
+ cell_opts[:pdf_height] = 297.mm
339
+ cell_opts[:pdf_weigth] = 21.cm
340
+
341
+ # ...disegno le celle vere e proprie ad altezza fissa, con l'altezza
342
+ # ricavata dal passo precedente.
343
+ r = draw_cell_fixed_height(pdf, x+offset, y, width, max_cell_height, texts[i], opts, auto_y)
344
+
345
+ # Gli eventuali "testi residui" (es. raggiunto fine pagina) li raccolgo
346
+ # nel seguente array
347
+ rtexts << r
348
+
349
+ offset += width
350
+ end
351
+
352
+ texts = rtexts
353
+
354
+ # Se nei testi residui, sono presenti solo stringhe vuote, posso uscire dal
355
+ # loop, altrimenti, devo cambiare pagina e disegnare una nuova riga con i
356
+ # testi rimanenti
357
+ if !check_no_empty_string_presence(texts)
358
+ new_y = y + max_cell_height
359
+ break
360
+ else
361
+ pdf.start_new_page
362
+ y = pdf_margin_top # Posiziono il cursore ad inizio della nuova pagina
363
+ end
364
+ end
365
+
366
+ pdf.cursor = pdf_height - new_y
367
+
368
+ return new_y
369
+ end
370
+
371
+ # Se tutte le stringhe nella lista sono "" (stringa vuota) produce false
372
+ # altrimenti produce true.
373
+ def self.check_no_empty_string_presence(string_list)
374
+ string_list.each do |string_element|
375
+ if string_element != "" and string_element != []
376
+ return true
377
+ end
378
+ end
379
+
380
+ return false
381
+ end
382
+
383
+ # Dato il percorso file_path, genera un documento xml, a cui sono stati rimossi gli
384
+ # a capo e gli spazi multipli
385
+ def self.get_cleaned_xml_from_file(file_path)
386
+ text = File.read(file_path)
387
+
388
+ # Rimuove gli a capo e le tabulazioni sostituendoli con uno spazio
389
+ text = text.gsub(/\n|\t/, ' ')
390
+
391
+ # Rimuove le spaziature multiple sostituendole con uno spazio
392
+ text = text.gsub(/\s+/, ' ')
393
+
394
+ Nokogiri::XML(text)
395
+ end
96
396
  end
@@ -1,9 +1,54 @@
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 (eseguire singolarmente le action di
11
+ # scrittura testo ad esempio, rallenta molto la scrittura del pdf).
12
+ # Ogni singola action, viene poi eseguita, utilizzando le funzioni di prawn.
13
+
14
+ # Nella parte PUBLIC di questa classe, inserire solo metodi che generino action (ma non le eseguono)
15
+ # La classe PdfWritingToolsProcess, userà i metodi pubblic di questa classe per generare
16
+ # una list di action. Fornendo la lista di actions al metodo
17
+ # PdfWritingToolsActions.execute_actions, verrà dato il via all'esecuzione delle actions...
18
+
19
+ # Nella parte PRIVATE di questa classe, inserire solo metodi che eseguano le action (
20
+ # ossia che realizzino la scrittura del pdf)
21
+ # ... le singole actions, verrano eseguite tramite i metodi privati di questa classe
22
+
1
23
  module PdfWritingToolsActions
24
+ # Esegue le azioni, andando a concatenare quelle "contigue" nella lista,
25
+ # che riguardano la scrittura di testo.
26
+ # La lista actions, in sostanza comprende più azioni. Quelle contigue che riguardano
27
+ # la scrittura di testo, non vanno eseguite singolarmente, ma devono essere "fuse"
28
+ # e poi eseguite. Le action che riguardano la "scrittura" di un immagine invece, non
29
+ # possono essere fuse, pertanto vanno eseguite singolarmente
30
+ def self.execute_actions(pdf, actions, last_actn_name, data)
31
+ actions.each do |action|
32
+ if action[:action_name] == :draw_formatted_text
33
+ last_actn_name, data = text_action(pdf, action, last_actn_name, data)
34
+ elsif action[:action_name] == :draw_image
35
+ last_actn_name, data = img_action(pdf, action, last_actn_name, data)
36
+ elsif action[:action_name] == :draw_tr
37
+ last_actn_name, data = draw_tr(pdf, action, last_actn_name, data)
38
+ end
39
+ end
40
+
41
+ # Disegno l'ultima action della lista
42
+ execute_action(pdf, last_actn_name, data)
43
+ end
44
+
2
45
  # Questa action contiene istruzioni per disegnare un "a capo" nel PDF
3
46
  def self.new_line_action
4
47
  [{ action_name: :draw_formatted_text, data: [{ text: "\n" }] }]
5
48
  end
6
49
 
50
+ # Per "atomic_text" si intende testo che non contiene ulteriore "struttura"
51
+ # pesudo html
7
52
  def self.atomic_text_action(atomic_text)
8
53
  [{ action_name: :draw_formatted_text, data: [{ text: atomic_text }] }]
9
54
  end
@@ -22,64 +67,172 @@ module PdfWritingToolsActions
22
67
  end
23
68
 
24
69
  # Questa action, contiene istruzioni per disegnare un "bullet", ossia
25
- # un oggetto grafico, tipo un segno di spunta accanto all'elemnto di
70
+ # un oggetto grafico, tipo un segno di spunta accanto all'elemento di
26
71
  # una lista
27
72
  def self.bullet_action
28
73
  [
29
74
  {
30
- action_name: :draw_image, data:
75
+ action_name: :draw_image,
76
+ data:
31
77
  {
32
- url: 'public/bullet.png' # Pallino
78
+ url: File.expand_path("../../assets/bullet.png", __FILE__)
79
+ # Pallino
33
80
  }
34
81
  }
35
82
  ]
36
83
  end
37
84
 
38
- # Esegue last_action, oppure concatena i dati di action e di last_action
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]
85
+ def self.tr_action(xml_obj=nil, widths=nil)
86
+ text = "<tr><td>Uno</td><td>Due</td><td>Tre</td><td>Quattro</td></tr>"
87
+ widths = [2.cm, 2.cm, 3.cm, 4.cm]
88
+ xml_obj = Nokogiri::XML(text)
89
+ [
90
+ {
91
+ action_name: :draw_tr,
92
+ data:
93
+ {
94
+ xml_obj: xml_obj,
95
+ widths: widths
96
+ }
97
+ }
98
+ ]
49
99
  end
50
100
 
51
- # Mentre la "scrittura/disegno" del testo, puo' essere "concatenato", nel caso
52
- # delle immagini no. Una action sulla immagine, pertanto interrompe la
53
- # possibilita' di concatenare i data delle action di testo. Pertanto, eseguo
54
- # in ogni caso last_action, a meno che last_action non sia nil (ossia action
55
- # e' la prima azione della lista)
56
- def self.img_action(pdf, action, last_action_name, data)
57
- execute_action(pdf, last_action_name, data) unless last_action_name.nil?
58
- [:draw_image, action[:data]]
59
- end
60
101
 
61
- # Esegue un azione, andando a scrivere del testo nel PDF oppure un'immagine
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)
102
+
103
+ private
104
+
105
+ # Funzione di Prawn che devo capir bene come utilizzare per la generazione
106
+ # delle tabelle
107
+
108
+ ####### FORMATTED TEXT BOX (formatted_text_box)
109
+ # formatted_text_box
110
+ # https://prawnpdf.org/docs/0.11.1/Prawn/Text/Formatted.html
111
+
112
+ # Accetta due parametri: un array di dizionari e (facoltativo) un dizionario di opzioni
113
+
114
+ # ARRAY DI DIZIONARI
115
+ # Ciascun dizionario, contiene il testo da disegnare e informazioni su come
116
+ # formattarlo.
117
+
118
+ # DIZIONARIO DI OPZIONI
119
+
120
+ # Come disegnare testo formattato
121
+ # Posso usare due metodi
122
+
123
+ # (entrambi i metodi alla fine, forniscono informazioni sull'altezza del BOX
124
+ # disegnato e sul testo eventualmente non disegnato)
125
+
126
+ ##### METODO 1 - In un'unica fase
127
+ # Utilizzo una funzione di convenienza che disegna immediatamente nel pdf:
128
+ # formatted_text_box
129
+
130
+ ##### METODO 2 - In due fasi
131
+ # - Genero un oggetto con gli stessi parametri di formatted_text_box:
132
+ # oggetto = Prawn::Text::Formatted.new(array_di_dizionari, dizionario_di_opzioni)
133
+ # - Eseguo il rendering dell'oggetto
134
+ # formatted_text_not_rendered_or_overflow = oggetto.render(:dry_run => false)
135
+ # oggetto.height # altezza del box
136
+ #
137
+ # :dry_run true o false, mi permette di scrivere nel pdf o simulare la scrittura
138
+ # così da avere informazioni sulla dimensione del box da disegnare senza disegnarlo
139
+ # effettivamente. Perché?
140
+ # Supponiamo di dover disegnare la riga di una tabella, dove ciascuna cella
141
+ # della riga, contenga del testo formattato. Le celle della riga possono avere
142
+ # larghezze differenti, ma devono avere tutte la stessa altezza... ma quale
143
+ # altezza? Se definisco un'altezza fissa, c'è la possibilità che testo in
144
+ # eccesso all'interno di una cella venga tagliato, oppure che ci sia troppo
145
+ # spazio non utilizzato in tutte le celle (il che è visivamente brutto).
146
+ # Se utilizzo l'autoridimensionamento del formatted_text_box, offerto da prawn,
147
+ # avrò ciascuna cella con un altezza differente... Utilizzando invece la
148
+ # "simulazione", prima disegno (in modalità simulata :dry_run => true) la
149
+ # riga delle tabella con celle ad altezza autoridimensionata, in questo modo
150
+ # posso capire qual'è l'altezza di cella massima e se una di queste celle
151
+ # provoca un salto pagina (nel disegno di box autoridimensionati, l'Altezza
152
+ # del box, non eccede il salto pagina ed il testo che non può essere disegnato
153
+ # a causa del salto pagina, mi viene restituito dalla funzione di rendering).
154
+ # Successivamente disegno la riga vera e propria,
155
+ # questa volta, indicando un'altezza fissa per ciascuna cella (l'altezza massima
156
+ # di cella ottenuta nella simulazione).
157
+
158
+ # Esegue un'azione, andando a scrivere del testo nel PDF, oppure un'immagine
159
+ def self.execute_action(pdf, action_name, data)
160
+ if action_name == :draw_formatted_text
161
+ pdf.formatted_text(data, align: :left)
162
+ elsif action_name == :draw_image
163
+ # Il testo disegnato dopo un immagine, non appare allineato. Per questo
164
+ # motivo, disegno l'immagine un' po' prima di dove è previsto (spostando il cursore)
165
+ # per poi ripristinare la posizione originaria del cursore.
166
+
167
+ # L'operazione di cui sopra, non va eseguita a fondo pagina, altrimenti,
168
+ # il salto pagina automatico crea problemi sulla realizzazione del pdf,
169
+ # quindi...
170
+
171
+ # Se la posizione del cursore (misurata, dal fondo margine di pagina)
172
+ # è inferiore ad un cm, mi sposto direttamente sulla nuova pagina..
173
+ if pdf.cursor <= 1.cm
174
+ pdf.start_new_page
175
+ end
176
+
177
+ current_cursor_position = pdf.cursor
178
+ # Anticipo il disegno dell'immagine spostando il cursore di n unità verso
179
+ # l'alto
180
+ n = 0
181
+ pdf.move_cursor_to(current_cursor_position - n)
182
+
183
+ # Disegno l'immagine (al momento questo metodo disegna solo il "bullet" degli elenchi)
184
+ pdf.image(data[:url], height: 6, width: 6)
185
+
186
+ # Ripristino la posizione originaria del cursore, in modo che il testo
187
+ # sia allineato all'immagine preceente
188
+ pdf.move_cursor_to(current_cursor_position)
189
+ elsif action_name == :draw_tr
190
+ xml_obj = data[:xml_obj]
191
+ widths = data[:widths]
192
+
193
+ current_cursor_position = 297.mm - pdf.cursor
194
+
195
+ PdfWritingTools::draw_row_auto_height(pdf, false, 2.cm, current_cursor_position, widths, ["uno", "due", "tre", "quattro"], opts = {}, true)
196
+ end
70
197
  end
71
- end
72
198
 
73
- # Esegue le azioni, andando a concatenare quelle "contigue" nella lista,
74
- # che riguardano la scrittura di testo
75
- def self.execute_actions(pdf, actions, last_actn_name, data)
76
- actions.each do |action|
77
- if action[:action_name] == :draw_formatted_text
78
- last_actn_name, data = text_action(pdf, action, last_actn_name, data)
79
- elsif action[:action_name] == :draw_image
80
- last_actn_name, data = img_action(pdf, action, last_actn_name, data)
199
+ # Esegue last_action, oppure concatena i dati di action e di last_action
200
+ # action: azione "corrente"
201
+ # last_action_name: nome dell'azione che precede l'azione corrente
202
+ # data: dati dell'azione che precede l'azione corrente
203
+ def self.text_action(pdf, action, last_action_name, data)
204
+ if last_action_name.nil?
205
+ # Prima azione della lista (last_action_name = nil), quindi per ora
206
+ # nessun concatenamento di dati
207
+ data = action[:data]
208
+ elsif last_action_name == :draw_formatted_text
209
+ # La action attuale richiede di "disegnare" testo formattato, come l'ultima azione
210
+ # quindi, concateno i dati di questa action, con quelli dell'ultima action
211
+ data += action[:data]
212
+ else
213
+ # La action attuale, differisce dall'ultima, quindi eseguo l'ultima action (che sarà
214
+ # un'immagine visto che quella attuale è il disegno di testo) e produco i dati di
215
+ # quella corrente
216
+ execute_action(pdf, last_action_name, data)
217
+ data = action[:data]
81
218
  end
219
+
220
+ [:draw_formatted_text, data]
221
+ end
222
+
223
+ # Mentre la "scrittura/disegno" del testo, puo' essere "concatenato", nel caso
224
+ # delle immagini no. Una action sull' immagine pertanto, interrompe la
225
+ # possibilita' di concatenare i data delle action di testo. Quindi, eseguo
226
+ # in ogni caso last_action, a meno che last_action non sia nil (ossia action
227
+ # e' la prima azione della lista)
228
+ def self.img_action(pdf, action, last_action_name, data)
229
+ execute_action(pdf, last_action_name, data) unless last_action_name.nil?
230
+ [:draw_image, action[:data]]
231
+ end
232
+
233
+
234
+ def self.draw_tr(pdf, action, last_action_name, data)
235
+ execute_action(pdf, last_action_name, data) unless last_action_name.nil?
236
+ [:draw_tr, action[:data]]
82
237
  end
83
- execute_action(pdf, last_actn_name, data)
84
- end
85
238
  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,13 +58,14 @@ 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
64
-
65
+
65
66
  if idx
66
67
  PdfWritingToolsActions.new_line_action + PdfWritingToolsActions.atomic_text_action(idx) + PdfWritingToolsActions.indent_action(4) + actions_list
67
- else
68
+ else
68
69
  PdfWritingToolsActions.new_line_action + PdfWritingToolsActions.bullet_action + PdfWritingToolsActions.indent_action(4) + actions_list
69
70
  end
70
71
  end
@@ -76,19 +77,66 @@ module PdfWritingToolsProcess
76
77
  xml_obj.children.each do |child|
77
78
  actions_list += process_xml_obj(child, properties)
78
79
  end
80
+
79
81
  PdfWritingToolsActions.new_line_action + actions_list
80
82
  end
81
83
 
84
+ # Al momento assumo che h1 contenga solo testo, poi eventualmente
85
+ # renderla ricorsiva permettendo a h1 di contenere altri tag.
86
+ # Sebbene l'xml che sto processando non sia vero html, voglio cercare di dargli
87
+ # il più possibile lo stesso comportamento. All'interno di h1, possono essere
88
+ # presenti solo elementi inline. Al momento non controllo questa cosa, quindi preferisco
89
+ # tagliare la testa al toro, assumento che h1 contenga solo testo.
82
90
  def self.process_xml_tag_h1(xml_obj, properties)
83
91
  actions_list = process_xml_text(xml_obj.child, [:bold], 16, true)
84
- PdfWritingToolsActions.new_line_action + actions_list + PdfWritingToolsActions.new_line_action * 2
92
+
93
+ PdfWritingToolsActions.new_line_action + actions_list + PdfWritingToolsActions.new_line_action * 2
85
94
  end
86
95
 
96
+ # Al momento assumo che span contenga solo testo, poi eventualmente
97
+ # renderla ricorsiva permettendo a span di contenere altri tag.
98
+ # Sebbene l'xml che sto processando non sia vero html, voglio cercare di dargli
99
+ # il più possibile lo stesso comportamento. All'interno di span, possono essere
100
+ # presenti solo elementi inline. Al momento non controllo questa cosa, quindi preferisco
101
+ # tagliare la testa al toro, assumento che span contenga solo testo.
102
+ def self.process_xml_tag_span(xml_obj, properties)
103
+ span_styles_attributes = string_to_attributes(xml_obj[:style])
104
+
105
+ color = "#000000"
106
+
107
+ if span_styles_attributes.has_key?("color")
108
+ color = span_styles_attributes["color"]
109
+ end
110
+
111
+ actions_list = process_xml_text(xml_obj.child, properties, size=12, upcase=false, color)
112
+ end
113
+
114
+
115
+
116
+ def self.process_xml_tag_table(xml_obj, properties)
117
+ # Processo i figli della tabella che devono essere due
118
+ # thead
119
+ # tbody
120
+ xml_obj.children.each do |child|
121
+ end
122
+
123
+ p xml_obj[:width]
124
+ p 20.cm / 7.0
125
+ []
126
+ end
127
+
128
+
129
+ def self.process_xml_tag_tr(xml_obj, properties)
130
+
131
+
132
+ end
133
+
134
+
87
135
  # Produce le actions necessarie per disegnare nel PDF un determinato
88
136
  # "tag"
89
- def self.process_xml_obj(xml_obj, properties)
137
+ def self.process_xml_obj(xml_obj, properties, attributes={})
90
138
  case xml_obj.name
91
- when 'text', 'b', 'i', 'ul', 'li', 'p', 'h1', 'ol'
139
+ when 'text', 'b', 'i', 'ul', 'li', 'p', 'h1', 'ol', 'span', 'table'
92
140
  @process_xml_tag_table[xml_obj.name].call(xml_obj, properties)
93
141
  when 'br'
94
142
  PdfWritingToolsActions.new_line_action
@@ -97,15 +145,29 @@ module PdfWritingToolsProcess
97
145
  end
98
146
  end
99
147
 
148
+ def self.string_to_attributes(s)
149
+ result = {}
150
+
151
+ s.split(";").each do |el|
152
+ e = el.split(":")
153
+ result[e[0]] = e[1]
154
+ end
155
+
156
+ result
157
+ end
158
+
100
159
  @process_xml_tag_table =
101
160
  {
102
161
  'text' => method(:process_xml_text),
162
+ 'span' => method(:process_xml_tag_span),
103
163
  'b' => method(:process_xml_tag_b),
104
164
  'i' => method(:process_xml_tag_i),
105
165
  'ul' => method(:process_xml_tag_ul),
106
166
  'ol' => method(:process_xml_tag_ol),
107
167
  'li' => method(:process_xml_tag_li),
108
168
  'p' => method(:process_xml_tag_p),
109
- 'h1' => method(:process_xml_tag_h1)
169
+ 'h1' => method(:process_xml_tag_h1),
170
+ 'table' => method(:process_xml_tag_table),
171
+ 'tr' => method(:process_xml_tag_tr)
110
172
  }
111
173
  end
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.11
4
+ version: 0.0.14
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-28 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.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: 2024-01-05 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
@@ -1,3 +0,0 @@
1
- = PdfWritingTools
2
-
3
- This project rocks and uses MIT-LICENSE.