pdf_writing_tools 0.0.11 → 0.0.14

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 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.