pd4ml 1.0.0 → 1.0.1
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/README.md +4 -9
- data/docs/guide.md +780 -782
- data/lib/pd4ml/bridge.rb +362 -362
- data/lib/pd4ml/version.rb +5 -5
- metadata +1 -1
data/docs/guide.md
CHANGED
|
@@ -1,782 +1,780 @@
|
|
|
1
|
-
# PD4ML for Ruby — User Guide
|
|
2
|
-
|
|
3
|
-
`pd4ml` brings [PD4ML](https://pd4ml.com) to Ruby: it converts HTML and CSS into **PDF**,
|
|
4
|
-
**PDF/A**, **PDF/UA**, **ZUGFeRD/Factur-X** invoices, **DOCX**, **RTF** and **page images**, and
|
|
5
|
-
gives Ruby applications the complete public PD4ML API.
|
|
6
|
-
|
|
7
|
-
PD4ML is a Java library. The gem runs it in a small Java *bridge* process that Ruby talks to.
|
|
8
|
-
Every PD4ML class and method can be called from Ruby, by its Java name or Ruby-style
|
|
9
|
-
(`set_page_size`), and the gem adds Ruby I/O on top: paths, binary strings and IO objects
|
|
10
|
-
instead of Java streams.
|
|
11
|
-
|
|
12
|
-
The PD4ML manual, which this guide refers to throughout, is at <https://pd4ml.com/docs/>.
|
|
13
|
-
The generated reference of every class and method is [api.md](api.md).
|
|
14
|
-
|
|
15
|
-
**Contents**
|
|
16
|
-
|
|
17
|
-
1. [Prerequisites](#1-prerequisites)
|
|
18
|
-
2. [Installation](#2-installation)
|
|
19
|
-
3. [Quick start](#3-quick-start)
|
|
20
|
-
4. [How it works](#4-how-it-works)
|
|
21
|
-
5. [Use cases](#5-use-cases)
|
|
22
|
-
6. [Tips and hints](#6-tips-and-hints)
|
|
23
|
-
7. [Troubleshooting](#7-troubleshooting)
|
|
24
|
-
8. [API reference](#8-api-reference)
|
|
25
|
-
9. [Further reading](#9-further-reading)
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## 1. Prerequisites
|
|
30
|
-
|
|
31
|
-
| Requirement | Notes |
|
|
32
|
-
|---|---|
|
|
33
|
-
| **Ruby 3.1 or later** | MRI on any platform Ruby and Java run on. |
|
|
34
|
-
| **Java 8 or later** | A JDK or JRE; an LTS release (17, 21) is recommended. The gem finds it through `PD4ML_JAVA`, then `JAVA_HOME`, then `java` on the `PATH`. |
|
|
35
|
-
| **PD4ML** | Downloaded automatically on first use, or ahead of time with `pd4ml install` (see below); or any PD4ML build you point the gem at. |
|
|
36
|
-
| **Network access** | Only for downloading PD4ML. Conversions need none, unless the HTML references remote resources. |
|
|
37
|
-
| **A PD4ML license** | Optional for evaluation. Without one, PD4ML runs in evaluation mode and watermarks its output. See [Licensing](https://pd4ml.com/docs/#3.-licensing) and [5.18](#518-licensing). |
|
|
38
|
-
|
|
39
|
-
Check Java with `java -version`. On a server without Java, install a JRE package (for example
|
|
40
|
-
`apt install openjdk-21-jre-headless`) and, if it is not on the `PATH`, set `JAVA_HOME`. In
|
|
41
|
-
containers, use an image with both Ruby and Java, or add a JRE to the Ruby image.
|
|
42
|
-
|
|
43
|
-
## 2. Installation
|
|
44
|
-
|
|
45
|
-
### 2.1 Install the gem
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
gem 'pd4ml'
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
The gem
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
PD4ML
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
|
99
|
-
|
|
100
|
-
|
|
|
101
|
-
| `--
|
|
102
|
-
| `
|
|
103
|
-
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
>
|
|
127
|
-
>
|
|
128
|
-
>
|
|
129
|
-
>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
- **
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
`
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
`
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
pd =
|
|
352
|
-
pd.
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
pd
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
pd
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
pd
|
|
467
|
-
pd.
|
|
468
|
-
|
|
469
|
-
pd.
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
pd.
|
|
525
|
-
pd.
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
pd
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
news
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
pd
|
|
565
|
-
pd.
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
pd.
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
pd.
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
pd.
|
|
585
|
-
pd.
|
|
586
|
-
pd.
|
|
587
|
-
pd.
|
|
588
|
-
pd.
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
Pd4ml.
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
Pd4ml.
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
pd
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
- **
|
|
699
|
-
|
|
700
|
-
- **
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
|
723
|
-
|
|
724
|
-
|
|
|
725
|
-
| `
|
|
726
|
-
|
|
|
727
|
-
|
|
|
728
|
-
|
|
|
729
|
-
|
|
|
730
|
-
|
|
|
731
|
-
|
|
|
732
|
-
| `
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
| | |
|
|
741
|
-
|
|
742
|
-
| `
|
|
743
|
-
| `
|
|
744
|
-
| `
|
|
745
|
-
| `version`, `
|
|
746
|
-
| `
|
|
747
|
-
| `
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
|
754
|
-
|
|
755
|
-
| `
|
|
756
|
-
| `
|
|
757
|
-
| `
|
|
758
|
-
| `
|
|
759
|
-
| `
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
`
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
- [
|
|
777
|
-
- [
|
|
778
|
-
- [
|
|
779
|
-
- [
|
|
780
|
-
- [
|
|
781
|
-
- [PDF post-processing toolkit](https://pd4ml.com/docs/#15.-the-pdf-post-processing-toolkit)
|
|
782
|
-
- [Java examples on GitHub](https://github.com/zxfr/pd4ml-examples)
|
|
1
|
+
# PD4ML for Ruby — User Guide
|
|
2
|
+
|
|
3
|
+
`pd4ml` brings [PD4ML](https://pd4ml.com) to Ruby: it converts HTML and CSS into **PDF**,
|
|
4
|
+
**PDF/A**, **PDF/UA**, **ZUGFeRD/Factur-X** invoices, **DOCX**, **RTF** and **page images**, and
|
|
5
|
+
gives Ruby applications the complete public PD4ML API.
|
|
6
|
+
|
|
7
|
+
PD4ML is a Java library. The gem runs it in a small Java *bridge* process that Ruby talks to.
|
|
8
|
+
Every PD4ML class and method can be called from Ruby, by its Java name or Ruby-style
|
|
9
|
+
(`set_page_size`), and the gem adds Ruby I/O on top: paths, binary strings and IO objects
|
|
10
|
+
instead of Java streams.
|
|
11
|
+
|
|
12
|
+
The PD4ML manual, which this guide refers to throughout, is at <https://pd4ml.com/docs/>.
|
|
13
|
+
The generated reference of every class and method is [api.md](api.md).
|
|
14
|
+
|
|
15
|
+
**Contents**
|
|
16
|
+
|
|
17
|
+
1. [Prerequisites](#1-prerequisites)
|
|
18
|
+
2. [Installation](#2-installation)
|
|
19
|
+
3. [Quick start](#3-quick-start)
|
|
20
|
+
4. [How it works](#4-how-it-works)
|
|
21
|
+
5. [Use cases](#5-use-cases)
|
|
22
|
+
6. [Tips and hints](#6-tips-and-hints)
|
|
23
|
+
7. [Troubleshooting](#7-troubleshooting)
|
|
24
|
+
8. [API reference](#8-api-reference)
|
|
25
|
+
9. [Further reading](#9-further-reading)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Prerequisites
|
|
30
|
+
|
|
31
|
+
| Requirement | Notes |
|
|
32
|
+
|---|---|
|
|
33
|
+
| **Ruby 3.1 or later** | MRI on any platform Ruby and Java run on. |
|
|
34
|
+
| **Java 8 or later** | A JDK or JRE; an LTS release (17, 21) is recommended. The gem finds it through `PD4ML_JAVA`, then `JAVA_HOME`, then `java` on the `PATH`. |
|
|
35
|
+
| **PD4ML** | Downloaded automatically on first use, or ahead of time with `pd4ml install` (see below); or any PD4ML build you point the gem at. |
|
|
36
|
+
| **Network access** | Only for downloading PD4ML. Conversions need none, unless the HTML references remote resources. |
|
|
37
|
+
| **A PD4ML license** | Optional for evaluation. Without one, PD4ML runs in evaluation mode and watermarks its output. See [Licensing](https://pd4ml.com/docs/#3.-licensing) and [5.18](#518-licensing). |
|
|
38
|
+
|
|
39
|
+
Check Java with `java -version`. On a server without Java, install a JRE package (for example
|
|
40
|
+
`apt install openjdk-21-jre-headless`) and, if it is not on the `PATH`, set `JAVA_HOME`. In
|
|
41
|
+
containers, use an image with both Ruby and Java, or add a JRE to the Ruby image.
|
|
42
|
+
|
|
43
|
+
## 2. Installation
|
|
44
|
+
|
|
45
|
+
### 2.1 Install the gem
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
gem install pd4ml
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
or, with Bundler, `gem 'pd4ml'` in the `Gemfile` and `bundle install`. The gem has no gem
|
|
52
|
+
dependencies and installs the `pd4ml` command.
|
|
53
|
+
|
|
54
|
+
The gem is published on RubyGems as [`pd4ml`](https://rubygems.org/gems/pd4ml). The same files
|
|
55
|
+
are on GitHub, <https://github.com/zxfr/pd4ml-ruby>, with each version's `.gem` attached to its
|
|
56
|
+
[release](https://github.com/zxfr/pd4ml-ruby/releases); Bundler can also take it from there
|
|
57
|
+
(`gem 'pd4ml', git: 'https://github.com/zxfr/pd4ml-ruby.git'`).
|
|
58
|
+
|
|
59
|
+
### 2.2 Install PD4ML
|
|
60
|
+
|
|
61
|
+
The gem contains the Ruby library and the bridge only. The PD4ML library itself comes from the
|
|
62
|
+
PD4ML Maven repository (<https://pd4ml.com/maven2>), in one of two ways.
|
|
63
|
+
|
|
64
|
+
**Automatically, on first use.** If no PD4ML build is installed or configured
|
|
65
|
+
([2.3](#23-other-ways-to-provide-pd4ml)), the first use of PD4ML in a process downloads the most
|
|
66
|
+
recent PD4ML 4.x release before starting Java. A warning on standard error reports it. So once the gem
|
|
67
|
+
is installed, this is all it takes:
|
|
68
|
+
|
|
69
|
+
<!-- no-test -->
|
|
70
|
+
```ruby
|
|
71
|
+
require 'pd4ml'
|
|
72
|
+
|
|
73
|
+
pdf = Pd4ml.html_to_pdf('<h1>Hello</h1>'.b) # the first call downloads PD4ML (once)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Ahead of time, with the `pd4ml` command.** This is better for servers, CI and container
|
|
77
|
+
images, where a download in the middle of a request is unwelcome:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
pd4ml install # with Bundler: bundle exec pd4ml install
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Either way, the gem:
|
|
84
|
+
|
|
85
|
+
- downloads the most recent PD4ML 4.x release;
|
|
86
|
+
- downloads the Bouncy Castle jars PD4ML needs for encryption and signing, from Maven Central;
|
|
87
|
+
- checks every file against its published SHA-1 checksum;
|
|
88
|
+
- stores them in a per-user cache directory: `%LOCALAPPDATA%\pd4ml-ruby` on Windows,
|
|
89
|
+
`~/.cache/pd4ml-ruby` elsewhere, or `PD4ML_HOME`.
|
|
90
|
+
|
|
91
|
+
The gem then uses that build. Later runs do not check for newer PD4ML releases: run
|
|
92
|
+
`pd4ml install` again to update. Updating the gem does not change the PD4ML build.
|
|
93
|
+
|
|
94
|
+
| Option | |
|
|
95
|
+
|---|---|
|
|
96
|
+
| *(none)* | the most recent 4.x release, or an update to it |
|
|
97
|
+
| `--snapshots` | the most recent 4.x build, including development snapshots |
|
|
98
|
+
| `pd4ml install 4.0.23fx2` | an exact version |
|
|
99
|
+
| `--jakarta` | the `-jakarta` variant (for Jakarta EE / Servlet 6 environments) |
|
|
100
|
+
| `--home DIR` | install into `DIR` (default: `PD4ML_HOME`, else the cache directory) |
|
|
101
|
+
| `--force` | download again |
|
|
102
|
+
| `--no-bouncycastle` | skip the Bouncy Castle jars (encryption and signing will not work) |
|
|
103
|
+
| `pd4ml list` | list the available versions |
|
|
104
|
+
|
|
105
|
+
The same is available from Ruby, which suits an application's boot (a Rails initializer):
|
|
106
|
+
|
|
107
|
+
<!-- no-test -->
|
|
108
|
+
```ruby
|
|
109
|
+
require 'pd4ml'
|
|
110
|
+
|
|
111
|
+
Pd4ml.install # downloads only when that build is not there yet
|
|
112
|
+
puts Pd4ml.installed_version, Pd4ml.installed_classpath
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
To check the installation, run `pd4ml version`.
|
|
116
|
+
|
|
117
|
+
**Turning the automatic download off.** Set `PD4ML_AUTO_INSTALL=0`, for example on servers that
|
|
118
|
+
must not reach the internet, or where the build is provisioned separately. The gem then raises
|
|
119
|
+
`Pd4ml::BridgeError` until a build is installed or configured. The automatic download never runs
|
|
120
|
+
when a build is configured through [2.3](#23-other-ways-to-provide-pd4ml).
|
|
121
|
+
|
|
122
|
+
> **Services and containers**: the account the application runs as must see the installed
|
|
123
|
+
> build. Install it as that account, or install into a shared directory and set `PD4ML_HOME`
|
|
124
|
+
> for the application too. In a Dockerfile:
|
|
125
|
+
>
|
|
126
|
+
> ```
|
|
127
|
+
> RUN bundle install && PD4ML_HOME=/opt/pd4ml bundle exec pd4ml install
|
|
128
|
+
> ENV PD4ML_HOME=/opt/pd4ml PD4ML_AUTO_INSTALL=0
|
|
129
|
+
> ```
|
|
130
|
+
|
|
131
|
+
### 2.3 Other ways to provide PD4ML
|
|
132
|
+
|
|
133
|
+
The gem looks for PD4ML in this order:
|
|
134
|
+
|
|
135
|
+
1. `Pd4ml.configure(classpath: [...])`, called before the first conversion;
|
|
136
|
+
2. the `PD4ML_CLASSPATH` environment variable: jars and class directories separated by `;`
|
|
137
|
+
(Windows) or `:` (elsewhere), with `dir/*` for all jars of a directory;
|
|
138
|
+
3. the `PD4ML_CLASSPATH_FILE` environment variable: a file with one entry per line;
|
|
139
|
+
4. the build `pd4ml install` (or the first use) downloaded.
|
|
140
|
+
|
|
141
|
+
Typical reasons to use them:
|
|
142
|
+
|
|
143
|
+
- **Offline or air-gapped servers**: run `pd4ml install --home ./pd4ml-libs` on a machine with
|
|
144
|
+
network access, copy the directory, and set `PD4ML_HOME` and `PD4ML_AUTO_INSTALL=0` on the
|
|
145
|
+
target.
|
|
146
|
+
- **A licensed or specific jar**: `PD4ML_CLASSPATH=/opt/pd4ml/pd4ml.jar:/opt/pd4ml/lib/*`.
|
|
147
|
+
- **A PD4ML development build** (for the v42 layout engine, see [4.3](#43-layout-engines)): a
|
|
148
|
+
classpath file listing the build's classes and its Maven dependencies, in
|
|
149
|
+
`PD4ML_CLASSPATH_FILE`.
|
|
150
|
+
|
|
151
|
+
## 3. Quick start
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
require 'pd4ml'
|
|
155
|
+
|
|
156
|
+
pd = Pd4ml::PD4ML.new
|
|
157
|
+
pd.set_page_size(Pd4ml::PageSize::A4)
|
|
158
|
+
pd.read_html_string('<h1>Hello, PDF</h1><p>Generated with <b>PD4ML</b>.</p>')
|
|
159
|
+
pd.write_pdf('hello.pdf')
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Or in one call:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
require 'pd4ml'
|
|
166
|
+
|
|
167
|
+
pdf = Pd4ml.html_to_pdf('<h1>Invoice 42</h1>'.b)
|
|
168
|
+
puts pdf[0, 5] # %PDF-
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 4. How it works
|
|
172
|
+
|
|
173
|
+
### 4.1 The two-phase model
|
|
174
|
+
|
|
175
|
+
A document is **read once and written any number of times**:
|
|
176
|
+
|
|
177
|
+
- `read_html` parses the document and lays it out;
|
|
178
|
+
- each `write_pdf`, `write_docx`, `write_rtf` or `render_as_images` call then produces one output
|
|
179
|
+
from that same layout.
|
|
180
|
+
|
|
181
|
+
Settings (page size, headers, fonts...) go before `read_html`. See
|
|
182
|
+
[the two-phase model](https://pd4ml.com/docs/#6.1-the-two-phase-model).
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
require 'pd4ml'
|
|
186
|
+
|
|
187
|
+
pd = Pd4ml::PD4ML.new
|
|
188
|
+
pd.read_html_string('<h1>Quarterly report</h1>')
|
|
189
|
+
pd.write_pdf('report.pdf')
|
|
190
|
+
pd.write_docx('report.docx')
|
|
191
|
+
thumbnails = pd.render_png # PNG images, one binary String per page
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 4.2 The bridge and threads
|
|
195
|
+
|
|
196
|
+
- **One bridge per process.** The first use of PD4ML starts a Java bridge process. It serves
|
|
197
|
+
every conversion of the Ruby process, from any thread, and ends with it. Starting Java takes a
|
|
198
|
+
fraction of a second; after that, converting an ordinary document takes milliseconds.
|
|
199
|
+
- **Threads.** The bridge is thread-safe: use one `Pd4ml::PD4ML` per document (each thread its
|
|
200
|
+
own) and convert from as many threads as you like (Puma, Sidekiq). The conversions themselves
|
|
201
|
+
run one at a time, so for parallel throughput use several processes, or point them all at one
|
|
202
|
+
bridge server (next item). Each Puma or Unicorn worker process starts its own bridge on first
|
|
203
|
+
use; there is nothing to set up before forking.
|
|
204
|
+
- **Shared bridge server.** Several processes, or short-lived ones (rake tasks, CLI tools), can
|
|
205
|
+
use one long-running bridge instead of each starting Java:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
pd4ml serve --port 8723 # keep it running (systemd, a sidecar container, foreman)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Then set `PD4ML_BRIDGE=tcp://127.0.0.1:8723`, or call
|
|
212
|
+
`Pd4ml.configure(server: 'tcp://127.0.0.1:8723')`. The server listens on the loopback
|
|
213
|
+
interface only, and each connection has its own objects.
|
|
214
|
+
- **JVM options** (memory, system properties): set `PD4ML_JVM_OPTIONS="-Xmx2g"`, or call
|
|
215
|
+
`Pd4ml.configure(jvm_options: ['-Xmx2g'])` before the first use. Java runs headless.
|
|
216
|
+
- **The bridge's console output** is discarded unless `PD4ML_BRIDGE_LOG=path` (or
|
|
217
|
+
`Pd4ml.configure(log_file: ...)`) keeps it. `Pd4ml.shutdown` stops the bridge; the next use
|
|
218
|
+
starts it again.
|
|
219
|
+
|
|
220
|
+
### 4.3 Layout engines
|
|
221
|
+
|
|
222
|
+
PD4ML 4.2 introduces a second HTML layout engine, **v42**, written to match current browsers
|
|
223
|
+
closely (flexbox, grid, modern CSS). `Pd4ml::PD4ML.new` uses it by default:
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
require 'pd4ml'
|
|
227
|
+
|
|
228
|
+
if Pd4ml.layout_engines?
|
|
229
|
+
pd = Pd4ml::PD4ML.new # v42
|
|
230
|
+
legacy = Pd4ml::PD4ML.new(engine: 'v40') # the previous engine
|
|
231
|
+
puts pd.engine, legacy.engine
|
|
232
|
+
end
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Builds before 4.2 have one engine only; this includes the current public release, 4.1.x. With
|
|
236
|
+
such a build:
|
|
237
|
+
|
|
238
|
+
- the gem uses that build's engine and warns once;
|
|
239
|
+
- `Pd4ml.layout_engines?` returns false;
|
|
240
|
+
- asking for an engine explicitly raises.
|
|
241
|
+
|
|
242
|
+
### 4.4 Ruby and Java values
|
|
243
|
+
|
|
244
|
+
- **Method names**: Java methods can be called by their Java names (`setPageSize`, `readHTML`)
|
|
245
|
+
or Ruby-style: `set_page_size`, `use_ttf` (for `useTTF`), `demo_mode?` (for `isDemoMode`).
|
|
246
|
+
Ruby-style names are matched to the real Java names, acronyms included.
|
|
247
|
+
- **Classes**: the PD4ML classes are Ruby classes in the `Pd4ml` module. Constants are Ruby
|
|
248
|
+
constants (`Pd4ml::PageSize::A4`, `Pd4ml::Constants::AllowPrint`); lower-case static fields and
|
|
249
|
+
static methods are class methods (`Pd4ml::LayoutEngine.v42`,
|
|
250
|
+
`Pd4ml::PdfSpec.get_instance('pdf/a-2b')`); constructors use `new`. Any other Java class:
|
|
251
|
+
`Pd4ml::Java.type('java.util.Locale')`.
|
|
252
|
+
- **Objects**: a Java object comes back as a `Pd4ml::JavaObject` with all its Java methods.
|
|
253
|
+
Public fields: `obj[:width]` and `obj[:width] = value`. `to_s` is Java's `toString()`,
|
|
254
|
+
`enum_name` an enum constant's name, `java_class` the class name.
|
|
255
|
+
- **Values**: Strings, Integers, Floats (`100.0` goes to Java as a float), `true`/`false`, `nil`,
|
|
256
|
+
Symbols (as strings: `engine: :v40`), Arrays, Hashes (as Java `Map`s).
|
|
257
|
+
- **Binary data**: a binary String (`File.binread`, `"...".b`, encoding ASCII-8BIT) goes to Java
|
|
258
|
+
as `byte[]`; an IO is read. Java's `byte[]` comes back as a binary String. Text Strings (UTF-8)
|
|
259
|
+
go to Java as text.
|
|
260
|
+
- Java objects are released when Ruby garbage-collects them.
|
|
261
|
+
|
|
262
|
+
### 4.5 Exceptions
|
|
263
|
+
|
|
264
|
+
Java exceptions are raised as `Pd4ml::JavaError`, which knows the Java class and its superclasses:
|
|
265
|
+
|
|
266
|
+
```ruby
|
|
267
|
+
require 'pd4ml'
|
|
268
|
+
|
|
269
|
+
begin
|
|
270
|
+
pd = Pd4ml::PD4ML.new
|
|
271
|
+
pd.read_html('file:///no/such/document.html')
|
|
272
|
+
pd.write_pdf
|
|
273
|
+
rescue Pd4ml::JavaError => e
|
|
274
|
+
raise unless e.instance_of_java?('java.io.IOException')
|
|
275
|
+
|
|
276
|
+
puts "cannot read the document: #{e.java_message}"
|
|
277
|
+
end
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`e.java_class`, `e.causes` and `e.java_trace` hold the details. Other errors:
|
|
281
|
+
|
|
282
|
+
| Error | Raised when |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `Pd4ml::BridgeError` | the bridge itself fails: no Java, no PD4ML build, or the process ended |
|
|
285
|
+
| `Pd4ml::CallbackError` | a Ruby block raised, such as a progress listener; the original error is `cause` |
|
|
286
|
+
| `Pd4ml::InstallError` | downloading PD4ML fails |
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## 5. Use cases
|
|
291
|
+
|
|
292
|
+
The snippets below are complete and runnable. Files they read (`report.html`...) are assumed to
|
|
293
|
+
exist in the working directory.
|
|
294
|
+
|
|
295
|
+
### 5.1 HTML string to PDF
|
|
296
|
+
|
|
297
|
+
This is the common case for web applications: render HTML with your template engine (ERB, Haml,
|
|
298
|
+
Slim, a Rails view) and convert it in memory.
|
|
299
|
+
|
|
300
|
+
```ruby
|
|
301
|
+
require 'pd4ml'
|
|
302
|
+
|
|
303
|
+
html = '<html><body><h1>Order 1001</h1><p>Thank you for your order.</p></body></html>'
|
|
304
|
+
|
|
305
|
+
pd = Pd4ml::PD4ML.new
|
|
306
|
+
pd.read_html_string(html)
|
|
307
|
+
pdf = pd.write_pdf # the PDF as a binary String
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`write_pdf`, `write_docx` and `write_rtf` return the document as a binary String when given no
|
|
311
|
+
target. Given a path (a String or Pathname) they write the file, given an IO (a `File`, a
|
|
312
|
+
`StringIO`, a Rack body) they write into it.
|
|
313
|
+
|
|
314
|
+
### 5.2 Files and URLs
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
require 'pd4ml'
|
|
318
|
+
|
|
319
|
+
pd = Pd4ml::PD4ML.new
|
|
320
|
+
pd.read_html('report.html') # also: 'https://example.com/report', a Pathname, a URI
|
|
321
|
+
pd.write_pdf('report.pdf')
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Relative links** (`<img src="logo.png">`, `<link href="style.css">`) resolve against the
|
|
325
|
+
document's own location when it is read from a path or URL. When the HTML comes from a string,
|
|
326
|
+
binary String or IO, pass the base location. Otherwise images and stylesheets go missing without
|
|
327
|
+
an error (see [reading the source](https://pd4ml.com/docs/#6.2-reading-the-source)):
|
|
328
|
+
|
|
329
|
+
```ruby
|
|
330
|
+
require 'pd4ml'
|
|
331
|
+
|
|
332
|
+
html = '<link rel="stylesheet" href="style.css"><p class="note">Styled</p>'
|
|
333
|
+
pd = Pd4ml::PD4ML.new
|
|
334
|
+
pd.read_html_string(html, '.') # a directory, file path or URL
|
|
335
|
+
pd.write_pdf('styled.pdf')
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
A text String that does not start with `<` is taken as a path or URL; content always goes in as a
|
|
339
|
+
binary String (`File.binread`), an IO, or through `read_html_string`. For a document in a known
|
|
340
|
+
encoding other than UTF-8, call `pd.read_html(File.binread(path), base, 'windows-1251')`.
|
|
341
|
+
|
|
342
|
+
### 5.3 Page size, orientation, margins and scaling
|
|
343
|
+
|
|
344
|
+
```ruby
|
|
345
|
+
require 'pd4ml'
|
|
346
|
+
|
|
347
|
+
pd = Pd4ml::PD4ML.new
|
|
348
|
+
pd.set_page_size(Pd4ml::PageSize::LETTER.rotate) # landscape
|
|
349
|
+
pd.set_page_margins(Pd4ml::PageMargins.new(15, 20, 15, 20, Pd4ml::Units::MM)) # left, top, right, bottom
|
|
350
|
+
pd.set_html_width(1000) # the virtual browser width the page is laid out in
|
|
351
|
+
pd.read_html_string("<table width='100%'><tr><td>wide content</td></tr></table>")
|
|
352
|
+
pd.write_pdf('landscape.pdf')
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The layout is scaled to the page: `scale = printable width / html width`, so a smaller HTML width
|
|
356
|
+
makes everything larger. Related controls:
|
|
357
|
+
|
|
358
|
+
- `adjust_html_width(true)` lays the page out at the document's own natural width.
|
|
359
|
+
- `fit_page_vertically(Pd4ml::Constants::CENTER_ALIGN)` shrinks the whole document onto a
|
|
360
|
+
single page.
|
|
361
|
+
- `set_page_size` and `set_page_margins` accept a page *scope* (`'1'`, `'2+'`, `'odd'`...) as a
|
|
362
|
+
second argument.
|
|
363
|
+
|
|
364
|
+
More in [page layout](https://pd4ml.com/docs/#7.-page-layout-size-margins-and-scaling).
|
|
365
|
+
|
|
366
|
+
For a custom size, use `Pd4ml::PageSize.new(100.0, 150.0, Pd4ml::Units::MM)`. For a named size,
|
|
367
|
+
use `Pd4ml::PageSize.get_instance('a5')` (the name in lower case).
|
|
368
|
+
|
|
369
|
+
### 5.4 Headers, footers and page numbers
|
|
370
|
+
|
|
371
|
+
Headers and footers are HTML fragments. PD4ML fills in `$[page]`, `$[total]` and `$[title]` on
|
|
372
|
+
each page, and `set_dynamic_data` adds placeholders of your own. `$[...]` is safe in both single-
|
|
373
|
+
and double-quoted Ruby strings (only `#{...}` is interpolated).
|
|
374
|
+
|
|
375
|
+
```ruby
|
|
376
|
+
require 'pd4ml'
|
|
377
|
+
|
|
378
|
+
pd = Pd4ml::PD4ML.new
|
|
379
|
+
pd.set_dynamic_data({ 'customer' => 'Acme Corp.', 'invoice' => 'INV-2026-0031' })
|
|
380
|
+
pd.set_page_header("<div style='font-size:9pt'><b>$[title]</b></div>", 30, '1+')
|
|
381
|
+
pd.set_page_footer(
|
|
382
|
+
"<div style='font-size:8pt; text-align:right'>" \
|
|
383
|
+
'Invoice $[invoice] — $[customer] — page $[page] of $[total]</div>',
|
|
384
|
+
24, '1+'
|
|
385
|
+
)
|
|
386
|
+
pd.read_html_string("<title>Invoice</title><h1>Invoice</h1>#{'<p>Line item</p>' * 80}")
|
|
387
|
+
pd.write_pdf('invoice.pdf')
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
The second argument is the area height in pixels. The third is the page scope: `'1'` (first
|
|
391
|
+
page), `'2+'` (all but the first), `'odd'`, `'even'`, `'3-7'`... You can also write headers and
|
|
392
|
+
footers inline in the HTML with PD4ML's extension tags. See
|
|
393
|
+
[headers, footers, watermarks and backgrounds](https://pd4ml.com/docs/#8.-headers-footers-watermarks-and-backgrounds)
|
|
394
|
+
and the [usage examples](https://pd4ml.com/docs/usage-examples.html#set-page-header-and-footer).
|
|
395
|
+
|
|
396
|
+
### 5.5 Watermarks and page backgrounds
|
|
397
|
+
|
|
398
|
+
```ruby
|
|
399
|
+
require 'pd4ml'
|
|
400
|
+
|
|
401
|
+
pd = Pd4ml::PD4ML.new
|
|
402
|
+
pd.set_watermark("<span style='font-size:72pt; color:#c00'>DRAFT</span>",
|
|
403
|
+
120.0, 350.0, # left, top (px)
|
|
404
|
+
0.15, # opacity
|
|
405
|
+
45.0, # rotation, degrees
|
|
406
|
+
1.0, # scale
|
|
407
|
+
true, true, # visible on screen, in print
|
|
408
|
+
'1+') # pages
|
|
409
|
+
pd.set_page_background("<div style='background:#f7f7f2; height:100%'></div>", '1+')
|
|
410
|
+
pd.read_html_string('<h1>Proposal</h1>')
|
|
411
|
+
pd.write_pdf('draft.pdf')
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### 5.6 Fonts
|
|
415
|
+
|
|
416
|
+
PD4ML's built-in fonts cover Unicode broadly. For a specific typeface (a corporate font, CJK
|
|
417
|
+
coverage), give it a font directory:
|
|
418
|
+
|
|
419
|
+
<!-- no-test -->
|
|
420
|
+
```ruby
|
|
421
|
+
require 'pd4ml'
|
|
422
|
+
|
|
423
|
+
pd = Pd4ml::PD4ML.new
|
|
424
|
+
pd.use_ttf('/opt/fonts', true) # a directory of TTF/OTF fonts (indexed: see below)
|
|
425
|
+
pd.embed_ttfs(true, false) # embed only the glyphs used
|
|
426
|
+
pd.apply_kerning(true)
|
|
427
|
+
pd.enable_ligatures(true)
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Index a font directory once, ahead of time. PD4ML then reads the index (`pd4fonts.properties`)
|
|
431
|
+
instead of scanning all the fonts on every conversion
|
|
432
|
+
([font indexing](https://pd4ml.com/docs/#4.3-font-indexing)):
|
|
433
|
+
|
|
434
|
+
```
|
|
435
|
+
java -jar <pd4ml jar> -configure.fonts /opt/fonts
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
`ruby -rpd4ml -e 'puts Pd4ml.installed_jar'` prints the jar's path. CSS `@font-face` web fonts in
|
|
439
|
+
the document work as well.
|
|
440
|
+
|
|
441
|
+
### 5.7 Bookmarks, links and tables of contents
|
|
442
|
+
|
|
443
|
+
```ruby
|
|
444
|
+
require 'pd4ml'
|
|
445
|
+
|
|
446
|
+
pd = Pd4ml::PD4ML.new
|
|
447
|
+
pd.generate_bookmarks_from_headings(true) # one PDF bookmark per <h1>..<h6>
|
|
448
|
+
pd.enable_hyperlinks(true) # <a href> become clickable links (the default)
|
|
449
|
+
pd.read_html_string("<h1>Introduction</h1><p>...</p><h1 style='page-break-before:always'>Details</h1>")
|
|
450
|
+
pd.write_pdf('bookmarked.pdf')
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
PD4ML's `<pd4ml:toc>` tag generates a table of contents with page numbers from the headings; see
|
|
454
|
+
the [usage examples](https://pd4ml.com/docs/usage-examples.html#add-toc). For explicit page
|
|
455
|
+
breaks, use CSS `page-break-before` / `break-before`, or `<pd4ml:page.break>`.
|
|
456
|
+
|
|
457
|
+
### 5.8 PDF/A and PDF/UA
|
|
458
|
+
|
|
459
|
+
```ruby
|
|
460
|
+
require 'pd4ml'
|
|
461
|
+
|
|
462
|
+
pd = Pd4ml::PD4ML.new
|
|
463
|
+
pd.read_html_string("<html lang='en'><title>Annual report</title><h1>Annual report</h1>")
|
|
464
|
+
pd.write_pdf('archive.pdf', spec: Pd4ml::PdfSpec::PDFA_3B) # archival
|
|
465
|
+
pd.write_pdf('accessible.pdf', spec: Pd4ml::PdfSpec::PDFUA_1) # accessible
|
|
466
|
+
pd.write_pdf('both.pdf', spec: Pd4ml::PdfSpec::PDF_1_7.combine(Pd4ml::PdfSpec::PDFA_2A))
|
|
467
|
+
pd.write_pdf('keyword.pdf', spec: 'pdf/a-2b') # by keyword
|
|
468
|
+
|
|
469
|
+
status = pd.get_last_render_info(Pd4ml::Constants::PD4ML_PDFA_STATUS) # after writing
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
`Pd4ml::PdfSpec.list_keywords` lists the keywords; an unknown one raises `ArgumentError`
|
|
473
|
+
(`PdfSpec.get_instance` alone would fall back to PDF 1.7 without an error).
|
|
474
|
+
|
|
475
|
+
- PDF/A embeds all fonts.
|
|
476
|
+
- PDF/UA needs well-structured HTML: real headings, `alt` texts, `lang`, table headers.
|
|
477
|
+
Combine it with `pd.set_error_policy(Pd4ml::ErrorPolicy::PEDANTIC)` to be told when the source
|
|
478
|
+
does not satisfy the standard.
|
|
479
|
+
|
|
480
|
+
Details: [PDF/A, PDF/UA and e-invoicing](https://pd4ml.com/docs/#11.2-pdfa-pdfua-and-e-invoicing-formats).
|
|
481
|
+
|
|
482
|
+
### 5.9 ZUGFeRD / Factur-X e-invoices
|
|
483
|
+
|
|
484
|
+
`PdfSpec::ZUGFeRD` and `PdfSpec::FacturX` select the PDF/A-3 container the standards require.
|
|
485
|
+
The HTML attaches the invoice XML with PD4ML's attachment tag. The
|
|
486
|
+
[ZUGFeRD and Factur-X page](https://pd4ml.com/docs/features/zugferd-and-factur-x-pdf-invoices.html)
|
|
487
|
+
has the complete recipe, including the required attachment names.
|
|
488
|
+
|
|
489
|
+
<!-- no-test -->
|
|
490
|
+
```ruby
|
|
491
|
+
require 'pd4ml'
|
|
492
|
+
|
|
493
|
+
pd = Pd4ml::PD4ML.new
|
|
494
|
+
pd.read_html('invoice.html') # contains the <pd4ml:attachment> of factur-x.xml
|
|
495
|
+
pd.write_pdf('invoice.pdf', spec: Pd4ml::PdfSpec::FacturX)
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### 5.10 Passwords and permissions
|
|
499
|
+
|
|
500
|
+
```ruby
|
|
501
|
+
require 'pd4ml'
|
|
502
|
+
|
|
503
|
+
c = Pd4ml::Constants
|
|
504
|
+
pd = Pd4ml::PD4ML.new
|
|
505
|
+
pd.set_permissions('owner-secret', c::AllowPrint | c::AllowCopy, c::AES_256BIT)
|
|
506
|
+
pd.read_html_string('<p>Confidential</p>')
|
|
507
|
+
pd.write_pdf('protected.pdf')
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Combine the `Allow*` flags with `|`, or start from `Constants::DefaultPermissions` and remove
|
|
511
|
+
flags with `& ~`. Encryption needs the Bouncy Castle jars, which the installation downloads. See
|
|
512
|
+
[permissions and encryption](https://pd4ml.com/docs/#11.1-permissions-and-encryption).
|
|
513
|
+
|
|
514
|
+
### 5.11 Word (DOCX, RTF) and images
|
|
515
|
+
|
|
516
|
+
```ruby
|
|
517
|
+
require 'pd4ml'
|
|
518
|
+
|
|
519
|
+
pd = Pd4ml::PD4ML.new
|
|
520
|
+
pd.read_html_string('<h1>Letter</h1><p>Dear customer, ...</p>')
|
|
521
|
+
pd.write_docx('letter.docx')
|
|
522
|
+
pd.write_rtf('letter.rtf')
|
|
523
|
+
pages = pd.render_png # a PNG per page
|
|
524
|
+
tiffs = pd.render_as_images(Pd4ml::Constants::TIFF) # one TIFF per page
|
|
525
|
+
pd.render_as_images('pages', 'page', Pd4ml::Constants::PNG24) # page files in a directory
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
The image formats are `PNG8`, `PNG24`, `TIFF` and `TIFF_COMPRESSED` (constants in `Constants`).
|
|
529
|
+
The TIFF formats need Java 9 or later.
|
|
530
|
+
The v42 engine does not support the multi-page TIFF formats yet; use the v40 engine for them.
|
|
531
|
+
|
|
532
|
+
With the v42 engine, DOCX/RTF output can be made *editable*: positioned content becomes text
|
|
533
|
+
boxes and form fields instead of pictures. To enable it, call
|
|
534
|
+
`pd.set_param(Pd4ml::Constants::PD4ML_WORD_EDITABLE, 'true')`.
|
|
535
|
+
|
|
536
|
+
### 5.12 Interactive forms and multi-column layout
|
|
537
|
+
|
|
538
|
+
```ruby
|
|
539
|
+
require 'pd4ml'
|
|
540
|
+
|
|
541
|
+
pd = Pd4ml::PD4ML.new
|
|
542
|
+
pd.generate_forms(true, nil) # <input>/<select>/<textarea> become PDF form fields
|
|
543
|
+
pd.read_html_string("<form>Name: <input name='name' size='30'></form>")
|
|
544
|
+
pd.write_pdf('form.pdf')
|
|
545
|
+
|
|
546
|
+
news = Pd4ml::PD4ML.new
|
|
547
|
+
news.generate_multicolumn(2, 20, true) # two columns, 20 mm apart
|
|
548
|
+
news.read_html_string("<h1>Newsletter</h1>#{'<p>Lorem ipsum dolor sit amet.</p>' * 200}")
|
|
549
|
+
news.write_pdf('newsletter.pdf')
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
See [forms](https://pd4ml.com/docs/#10.1-interactive-pdf-forms) and
|
|
553
|
+
[multi-column pages](https://pd4ml.com/docs/#10.2-multi-column-pages). Multi-column pages need
|
|
554
|
+
PD4ML 4.0.24 / 4.1.2 or later.
|
|
555
|
+
|
|
556
|
+
### 5.13 Adding CSS and HTML programmatically
|
|
557
|
+
|
|
558
|
+
```ruby
|
|
559
|
+
require 'pd4ml'
|
|
560
|
+
|
|
561
|
+
pd = Pd4ml::PD4ML.new
|
|
562
|
+
pd.add_style('body { font-family: sans-serif } h1 { color: #036 }', true) # true: after the document's styles
|
|
563
|
+
pd.inject_html("<div style='font-size:8pt'>Generated by the reporting service</div>", false) # before </body>
|
|
564
|
+
pd.read_html_string('<h1>Report</h1>')
|
|
565
|
+
pd.write_pdf('report-styled.pdf')
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
`add_style` also takes the URL of a stylesheet: `pd.add_style(Pd4ml.to_url('print.css'), true)`.
|
|
569
|
+
See [injecting HTML](https://pd4ml.com/docs/#10.3-injecting-html-and-custom-resource-providers)
|
|
570
|
+
and [adding styles](https://pd4ml.com/docs/usage-examples.html#add-style-programmatically).
|
|
571
|
+
|
|
572
|
+
### 5.14 Progress, diagnostics and metadata
|
|
573
|
+
|
|
574
|
+
```ruby
|
|
575
|
+
require 'pd4ml'
|
|
576
|
+
|
|
577
|
+
pd = Pd4ml::PD4ML.new
|
|
578
|
+
pd.monitor_progress_with do |message_id, percent, note, msec|
|
|
579
|
+
puts format('%3d%% %s (%d ms)', percent, note, msec)
|
|
580
|
+
end
|
|
581
|
+
pd.set_error_policy(Pd4ml::ErrorPolicy::NORMAL) # NONE, RELAXED, NORMAL, PEDANTIC, STRICT
|
|
582
|
+
pd.set_log_level(1) # 0 (silent) .. 5
|
|
583
|
+
pd.set_author_name('Reporting service')
|
|
584
|
+
pd.set_document_title('Monthly statement')
|
|
585
|
+
pd.add_metadata('StatementId', '2026-10', false)
|
|
586
|
+
pd.read_html_string('<h1>Statement</h1>')
|
|
587
|
+
pd.write_pdf('statement.pdf')
|
|
588
|
+
pages = pd.get_last_render_info(Pd4ml::Constants::PD4ML_TOTAL_PAGES).to_i
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
The block runs on the calling thread while the conversion runs in Java, and it may call PD4ML
|
|
592
|
+
itself. An error it raises comes out of the call that caused it, as a `Pd4ml::CallbackError`.
|
|
593
|
+
The v42 engine currently reports fewer progress stages than v40. More in
|
|
594
|
+
[extensibility and diagnostics](https://pd4ml.com/docs/#12.-extensibility-and-diagnostics).
|
|
595
|
+
|
|
596
|
+
### 5.15 Working with existing PDFs
|
|
597
|
+
|
|
598
|
+
```ruby
|
|
599
|
+
require 'pd4ml'
|
|
600
|
+
|
|
601
|
+
cover = Pd4ml.html_to_pdf("<h1 style='margin-top:300px; text-align:center'>Annual Report</h1>".b)
|
|
602
|
+
body = Pd4ml.html_to_pdf('<h2>Chapter 1</h2><p>...</p>'.b)
|
|
603
|
+
|
|
604
|
+
doc = Pd4ml.open_pdf(cover) # a binary String, IO, path or URL (+ password)
|
|
605
|
+
doc.append(Pd4ml.open_pdf(body)) # pages of another document after these
|
|
606
|
+
doc.set_title('Annual Report')
|
|
607
|
+
Pd4ml.write_pdf(doc, 'annual-report.pdf')
|
|
608
|
+
|
|
609
|
+
stamp = Pd4ml.html_to_pdf("<p style='color:#c00; font-size:40pt'>COPY</p>".b)
|
|
610
|
+
original = Pd4ml.open_pdf('annual-report.pdf')
|
|
611
|
+
original.overlay(Pd4ml.open_pdf(stamp), 128) # draw over every page, half opaque (0..255)
|
|
612
|
+
Pd4ml.write_pdf(original, 'annual-report-copy.pdf', password: 'secret')
|
|
613
|
+
|
|
614
|
+
puts Pd4ml.open_pdf('annual-report.pdf').get_number_of_pages
|
|
615
|
+
both = Pd4ml.merge_pdfs(cover, body) # two documents appended in one call
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
`underlay` draws another document *under* the pages (a letterhead, for example);
|
|
619
|
+
`delete_page`, `get_page_size`, `get_metadata` and `dump_outlines` inspect and edit a document.
|
|
620
|
+
PD4ML's PDF toolkit (signing, optimisation, XFDF) is described in
|
|
621
|
+
[the PDF post-processing toolkit](https://pd4ml.com/docs/#15.-the-pdf-post-processing-toolkit)
|
|
622
|
+
and is reachable through `Pd4ml::Java.type(...)`.
|
|
623
|
+
|
|
624
|
+
### 5.16 Web applications
|
|
625
|
+
|
|
626
|
+
Rails controller, with the HTML from a view:
|
|
627
|
+
|
|
628
|
+
<!-- no-test -->
|
|
629
|
+
```ruby
|
|
630
|
+
class InvoicesController < ApplicationController
|
|
631
|
+
def show
|
|
632
|
+
invoice = Invoice.find(params[:id])
|
|
633
|
+
html = render_to_string(:show, layout: 'pdf', locals: { invoice: invoice })
|
|
634
|
+
pd = Pd4ml::PD4ML.new
|
|
635
|
+
pd.read_html_string(html, Rails.public_path.to_s) # base: where images/CSS live
|
|
636
|
+
send_data pd.write_pdf, type: 'application/pdf', disposition: 'inline',
|
|
637
|
+
filename: "invoice-#{invoice.number}.pdf"
|
|
638
|
+
end
|
|
639
|
+
end
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
Sinatra:
|
|
643
|
+
|
|
644
|
+
<!-- no-test -->
|
|
645
|
+
```ruby
|
|
646
|
+
require 'sinatra'
|
|
647
|
+
require 'pd4ml'
|
|
648
|
+
|
|
649
|
+
get '/report.pdf' do
|
|
650
|
+
pd = Pd4ml::PD4ML.new
|
|
651
|
+
pd.read_html_string(erb(:report), settings.public_folder)
|
|
652
|
+
content_type 'application/pdf'
|
|
653
|
+
pd.write_pdf
|
|
654
|
+
end
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
To pay the cost of starting Java at boot instead of on the first request, touch PD4ML in an
|
|
658
|
+
initializer (`Pd4ml.version`). Each Puma worker process starts its own bridge; set
|
|
659
|
+
`PD4ML_BRIDGE` to share one bridge server between them.
|
|
660
|
+
|
|
661
|
+
### 5.17 Batch conversion
|
|
662
|
+
|
|
663
|
+
```ruby
|
|
664
|
+
require 'pd4ml'
|
|
665
|
+
|
|
666
|
+
Dir.glob('*.html').each do |source|
|
|
667
|
+
pd = Pd4ml::PD4ML.new # one converter per document
|
|
668
|
+
pd.read_html(source)
|
|
669
|
+
pd.write_pdf(source.sub(/\.html\z/, '.pdf'))
|
|
670
|
+
puts "written #{source}"
|
|
671
|
+
end
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
One bridge serves the whole loop. To convert in parallel, run several processes (each with its
|
|
675
|
+
own bridge), or point them all at one bridge server.
|
|
676
|
+
|
|
677
|
+
### 5.18 Licensing
|
|
678
|
+
|
|
679
|
+
<!-- no-test -->
|
|
680
|
+
```ruby
|
|
681
|
+
require 'pd4ml'
|
|
682
|
+
|
|
683
|
+
pd = Pd4ml::PD4ML.new(license: 'YOUR-SERIAL-NUMBER') # or a license file URL; or pd4ml.lic in the working dir / classpath
|
|
684
|
+
puts 'unlicensed: output is watermarked' if pd.demo_mode?
|
|
685
|
+
puts "PD4ML #{Pd4ml.version}#{pd.pro? ? ' pro' : ''}"
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
Without a license, PD4ML is fully functional but marks its output. See
|
|
689
|
+
[licensing](https://pd4ml.com/docs/#3.-licensing).
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
693
|
+
## 6. Tips and hints
|
|
694
|
+
|
|
695
|
+
- **Start the bridge early.** The first use starts Java. Touch PD4ML when the application
|
|
696
|
+
boots, or use a bridge server for short-lived processes.
|
|
697
|
+
- **One converter per document.** `Pd4ml::PD4ML.new` is cheap; reuse the bridge, not the object.
|
|
698
|
+
- **Set the base location for HTML from strings.** Missing images and unstyled output are almost
|
|
699
|
+
always caused by unresolved relative links ([details](https://pd4ml.com/docs/#6.2-reading-the-source)).
|
|
700
|
+
- **Settings go before `read_html`.** Page size, headers, fonts and styles apply to the layout
|
|
701
|
+
`read_html` makes; writes afterwards only choose the output format.
|
|
702
|
+
- **Write several formats from one read.** PDF, DOCX and page images from a single `read_html`
|
|
703
|
+
cost one layout.
|
|
704
|
+
- **Binary in, binary out.** Pass content as a binary String (`File.binread`, `.b`) or an IO;
|
|
705
|
+
results are binary Strings, ready for `File.binwrite` or `send_data`.
|
|
706
|
+
- **Print CSS works.** `@page { size: A4 landscape; margin: 15mm }`, `@media print` and
|
|
707
|
+
`page-break-*` / `break-*` are honoured. They are often simpler than API calls.
|
|
708
|
+
- **Control the scale with the HTML width.** If text is too small, or a layout wraps
|
|
709
|
+
differently than in the browser, set `set_html_width` to the width the page was designed for.
|
|
710
|
+
- **Index fonts once** with `-configure.fonts`, instead of pointing `use_ttf` at a large
|
|
711
|
+
unindexed directory on every conversion.
|
|
712
|
+
- **Restrict what documents can load** when converting untrusted HTML. Use `set_param` with
|
|
713
|
+
`Constants::PD4ML_ALLOWED_RESOURCE_LOCATION` and `PD4ML_DISABLE_EXTERNAL_ATTACHMENTS`.
|
|
714
|
+
- **Pin the build in production.** Use `pd4ml install <version>` or a `PD4ML_CLASSPATH` to a
|
|
715
|
+
jar you keep, rather than following the latest release.
|
|
716
|
+
|
|
717
|
+
## 7. Troubleshooting
|
|
718
|
+
|
|
719
|
+
| Symptom | Cause and remedy |
|
|
720
|
+
|---|---|
|
|
721
|
+
| `Pd4ml::BridgeError: PD4ML not found` | No build is configured and the automatic download is off (`PD4ML_AUTO_INSTALL=0`) or failed: run `pd4ml install`, or set `PD4ML_CLASSPATH`. |
|
|
722
|
+
| `Pd4ml::BridgeError: cannot start java` / "closed the connection" | Java is missing or failed. Check `java -version`, set `JAVA_HOME` or `PD4ML_JAVA`; `PD4ML_BRIDGE_LOG=file` keeps Java's output. |
|
|
723
|
+
| `Pd4ml::BridgeError: cannot connect to the PD4ML bridge` | The bridge server is not running, or `PD4ML_BRIDGE` names another port. |
|
|
724
|
+
| A pause and a "downloading" warning on the first conversion | The automatic first-use download ([2.2](#22-install-pd4ml)). Run `pd4ml install` at deployment instead. |
|
|
725
|
+
| `Errno::ENOENT` reading the document | A text String is taken as a path: pass HTML content with `read_html_string`, or as a binary String (`.b`). |
|
|
726
|
+
| Images or styles missing | Relative links have no base location, or the server cannot reach the resources (proxies, authentication). See `set_cookie` and `set_http_options`. |
|
|
727
|
+
| Output is watermarked | No license is installed ([licensing](https://pd4ml.com/docs/#3.-licensing)). |
|
|
728
|
+
| Squares instead of characters | The fonts lack those glyphs. Provide fonts with `use_ttf` ([fonts](https://pd4ml.com/docs/#9.-fonts-bookmarks-and-hyperlinks)). |
|
|
729
|
+
| `NoClassDefFoundError: org/bouncycastle/...` with `set_permissions` | The Bouncy Castle jars are missing. Install without `--no-bouncycastle`, or add them to the classpath. |
|
|
730
|
+
| `NoClassDefFoundError: com/sun/media/jai/...` with `render_as_images` / `render_png` | PD4ML 4.1.1 and earlier need the Java Advanced Imaging jars for page images. Use a newer PD4ML build, or add `jai-core` and `jai-codec` 1.1.3 (from the JBoss Maven repository) to the classpath. |
|
|
731
|
+
| Warning "has no v42 layout engine" | The PD4ML build predates 4.2, so the conversion uses its own engine. Use a 4.2 build for v42. |
|
|
732
|
+
| `Pd4ml::JavaError: ... NoSuchMethodException` | No Java method of that name takes those argument types. Check [api.md](api.md); pass floats as `10.0`, binary data as a binary String. |
|
|
733
|
+
|
|
734
|
+
## 8. API reference
|
|
735
|
+
|
|
736
|
+
### Module `Pd4ml`
|
|
737
|
+
|
|
738
|
+
| | |
|
|
739
|
+
|---|---|
|
|
740
|
+
| `Pd4ml::PD4ML` | the converter: `Pd4ml::PD4ML.new(license:, html_width:, engine:)`; every Java method of `com.pd4ml.PD4ML` |
|
|
741
|
+
| `PageSize`, `PageMargins`, `Units`, `Dimensions`, `Constants`, `LayoutEngine`, `ErrorPolicy`, `PdfSpec`, `PdfDocument`, `ProgressListener`, `StatusMessage`, `ResourceProvider`, `CustomTag` | the PD4ML Java classes ([api.md](api.md)) |
|
|
742
|
+
| `html_to_pdf(source, target, base_url:, page_size:, page_margins:, spec:, engine:, html_width:)`, `open_pdf(source, password)`, `write_pdf(doc, target, password:, permissions:)`, `merge_pdfs(a, b, target)` | one-call conversion and PDF documents |
|
|
743
|
+
| `version`, `layout_engines?`, `to_url(location)`, `pdf_spec(keyword)` | runtime helpers |
|
|
744
|
+
| `configure(**options)`, `shutdown` | the bridge: `classpath:`, `java:`, `jvm_options:`, `server:`, `log_file:` |
|
|
745
|
+
| `install(version, **options)`, `installed_jar`, `installed_classpath`, `installed_version`, `available_versions` | PD4ML builds |
|
|
746
|
+
| `Java.type(name)`, `Java.proxy(iface) { \|method, args\| ... }` | any Java class, Java interfaces implemented by Ruby blocks |
|
|
747
|
+
| `JavaError`, `BridgeError`, `CallbackError`, `InstallError` | errors |
|
|
748
|
+
|
|
749
|
+
### `Pd4ml::PD4ML` methods with Ruby additions
|
|
750
|
+
|
|
751
|
+
| Method | |
|
|
752
|
+
|---|---|
|
|
753
|
+
| `read_html(source, base_url = nil, encoding = nil)` | `source`: a path or URL (String, Pathname, URI), a binary String or IO, or a Java `URL`/`InputStream` |
|
|
754
|
+
| `read_html_string(html, base_url = nil)` | the document as a String |
|
|
755
|
+
| `write_pdf(target = nil, spec: nil)` | `target`: a path or IO; none: returns a binary String. `spec`: a `PdfSpec`, a keyword (`'pdf/a-2b'`) or `'PDF'`/`'PDFA'`/`'PDFUA'` |
|
|
756
|
+
| `write_docx(target = nil)`, `write_rtf(target = nil, rtf_wmf: false)` | as `write_pdf` |
|
|
757
|
+
| `render_as_images(format)`, `render_png` | pages as encoded images (binary Strings); `render_as_images(dir, name, format)` writes files |
|
|
758
|
+
| `monitor_progress_with { \|message_id, progress, note, msec\| ... }` | a block, or a Java `ProgressListener` |
|
|
759
|
+
| `use_html_renderer(engine, html_width: nil, params: nil)` | the layout engine by name; `pd.engine` tells which one is in use |
|
|
760
|
+
|
|
761
|
+
The Java names of these methods (`readHTML`, `writePDF`...) work too. All other methods are those
|
|
762
|
+
of the Java class `com.pd4ml.PD4ML` ([api.md](api.md#pd4mlpd4ml),
|
|
763
|
+
[Java API](https://pd4ml.com/docs/#6.-the-java-api)); static ones are called on the class
|
|
764
|
+
(`Pd4ml::PD4ML.get_version`). The Java object itself is `pd.java`.
|
|
765
|
+
|
|
766
|
+
`ResourceProvider` and `CustomTag` are extended in **Java**: register yours with
|
|
767
|
+
`add_custom_resource_provider` / `add_custom_tag_handler`, and put its classes on the classpath.
|
|
768
|
+
Java *interfaces* (such as `ProgressListener`) can be implemented in Ruby with
|
|
769
|
+
`Pd4ml::Java.proxy`. Examples: [resource loaders](https://github.com/zxfr/pd4ml-examples/blob/master/src/main/java/advanced/A005AddCustomResourceLoader.java),
|
|
770
|
+
[custom tags](https://github.com/zxfr/pd4ml-examples/blob/master/src/main/java/advanced/A008AddingCustomTagRenderer.java).
|
|
771
|
+
|
|
772
|
+
## 9. Further reading
|
|
773
|
+
|
|
774
|
+
- [PD4ML documentation](https://pd4ml.com/docs/) and the full [programmer's manual](https://pd4ml.com/docs/pd4ml-manual.html)
|
|
775
|
+
- [Usage examples](https://pd4ml.com/docs/usage-examples.html)
|
|
776
|
+
- [HTML5 element support matrix](https://pd4ml.com/docs/html5-element-support-matrix.html)
|
|
777
|
+
- [Command-line tool reference](https://pd4ml.com/docs/pd4cmd-command-line-reference.html)
|
|
778
|
+
- [ZUGFeRD and Factur-X invoices](https://pd4ml.com/docs/features/zugferd-and-factur-x-pdf-invoices.html)
|
|
779
|
+
- [PDF post-processing toolkit](https://pd4ml.com/docs/#15.-the-pdf-post-processing-toolkit)
|
|
780
|
+
- [Java examples on GitHub](https://github.com/zxfr/pd4ml-examples)
|