pd4ml 1.0.1 → 1.1.2
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 +18 -6
- data/docs/api.md +155 -3
- data/docs/guide.md +177 -16
- data/exe/pd4ml +6 -1
- data/lib/pd4ml/bridge.rb +1 -1
- data/lib/pd4ml/installer.rb +45 -6
- data/lib/pd4ml/java.rb +22 -2
- data/lib/pd4ml/pd4ml-bridge.jar +0 -0
- data/lib/pd4ml/pd4ml.rb +31 -1
- data/lib/pd4ml/version.rb +5 -5
- data/lib/pd4ml.rb +13 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f3f328b0ad4e184f8d1b35bdf80f51c1e2708798e4e782a765e3c71d5d98ebc
|
|
4
|
+
data.tar.gz: 133a2003170431c83600e3d2eb7bdae3f12cb4af14c9edf365e38e95b9a9b25b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fdd66e32394009f7a81e1a6cc5e51798bd0d798bde2463c0c5a2a81bf30b5c945afd1723a5a7fa2909e842a801606fdcfa7e5bdbedd9f43c46388d694525e6ff
|
|
7
|
+
data.tar.gz: f95260d4d7e77b25563a7f23f3ad00d58c6ed0a7e9f77b11d13f9a36e128e6878f951811b67e35d69bea5c4d2ee9f38a5772bab0e4d685540a7886e3535ad9db
|
data/README.md
CHANGED
|
@@ -29,11 +29,17 @@ Requirements:
|
|
|
29
29
|
- Ruby 3.1 or later.
|
|
30
30
|
- Java 8 or later, found through `PD4ML_JAVA`, `JAVA_HOME` or the `PATH`.
|
|
31
31
|
|
|
32
|
-
PD4ML itself is downloaded from <https://pd4ml.com/maven2>: the most recent
|
|
33
|
-
Bouncy Castle jars it needs for encryption and signing. Every file is checked
|
|
34
|
-
checksum. The download happens with `pd4ml install`, or on first use
|
|
35
|
-
turns that off). Builds are stored per user
|
|
36
|
-
or `PD4ML_HOME`).
|
|
32
|
+
PD4ML itself is downloaded from <https://pd4ml.com/maven2>: by default the most recent release from
|
|
33
|
+
4.2.0 on, plus the Bouncy Castle jars it needs for encryption and signing. Every file is checked
|
|
34
|
+
against its SHA-1 checksum. The download happens with `pd4ml install`, or on first use
|
|
35
|
+
(`PD4ML_AUTO_INSTALL=0` turns that off). Builds are stored per user
|
|
36
|
+
(`%LOCALAPPDATA%\pd4ml-ruby`, `~/.cache/pd4ml-ruby`, or `PD4ML_HOME`).
|
|
37
|
+
|
|
38
|
+
The gem's PD4ML 4.2 API (`read_eml`/`read_mht`, `add_allowed_resource_location`, XMP
|
|
39
|
+
`add_metadata`, the DOM API) needs PD4ML 4.2. Until PD4ML 4.2.0 is released, the default is
|
|
40
|
+
therefore the most recent 4.2.x development snapshot (4.2.0-SNAPSHOT). Once 4.2.0 is out, new
|
|
41
|
+
installs take it, and `pd4ml install` updates existing ones. `pd4ml install 4.1.2` installs the
|
|
42
|
+
current 4.1 release instead (the rest of the API works with it). See [Install PD4ML](docs/guide.md#22-install-pd4ml).
|
|
37
43
|
|
|
38
44
|
To use a PD4ML jar of your own, set `PD4ML_CLASSPATH`, set `PD4ML_CLASSPATH_FILE`, or call
|
|
39
45
|
`Pd4ml.configure(classpath: [...])`.
|
|
@@ -51,10 +57,16 @@ The gem adds Ruby I/O on top: paths, URLs, binary Strings and IO objects in and
|
|
|
51
57
|
for progress listeners. Several processes can share one bridge server: run `pd4ml serve` and set
|
|
52
58
|
`PD4ML_BRIDGE=tcp://127.0.0.1:8723`.
|
|
53
59
|
|
|
60
|
+
With PD4ML 4.2, the gem also reads e-mail messages and saved web pages (`read_eml`, `read_mht`; DMS
|
|
61
|
+
and UA editions),
|
|
62
|
+
and gives access to the parsed document before it is written (`get_document`, `layout`: the DOM
|
|
63
|
+
API of the DMS and UA editions, whose lists come back as Ruby Arrays). See the
|
|
64
|
+
[guide](docs/guide.md#516-e-mail-messages-and-saved-web-pages).
|
|
65
|
+
|
|
54
66
|
## Command line
|
|
55
67
|
|
|
56
68
|
```
|
|
57
|
-
pd4ml install [VERSION] [--snapshots] download PD4ML (or update it
|
|
69
|
+
pd4ml install [VERSION] [--snapshots] download PD4ML (default: 4.2, see above) or update it
|
|
58
70
|
pd4ml list versions in the repository
|
|
59
71
|
pd4ml version the PD4ML version in use
|
|
60
72
|
pd4ml convert in.html out.pdf [--page-size A4 --landscape --spec pdf/a-2b ...]
|
data/docs/api.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
_Generated by `tools/gen_api.rb` from the PD4ML Java classes - do not edit._
|
|
4
4
|
|
|
5
|
-
Every public PD4ML class is a Ruby class in the `Pd4ml` module. Methods can be called by their Java name (`setPageSize`) or Ruby-style (`set_page_size`; `isDemoMode` as `demo_mode?`); both are listed below. Constants and static fields are Ruby constants or class methods (`Pd4ml::PageSize::A4`, `Pd4ml::LayoutEngine.v42`), public instance fields are read with `obj[:name]`. Java types appear as Ruby types: binary Strings for `byte[]` and `InputStream`, a path or URL String for `URL`/`File`, `JavaObject` for any other Java object. Descriptions of each method are in the [PD4ML manual](https://pd4ml.com/docs/pd4ml-manual.html).
|
|
5
|
+
Every public PD4ML class is a Ruby class in the `Pd4ml` module. Methods can be called by their Java name (`setPageSize`) or Ruby-style (`set_page_size`; `isDemoMode` as `demo_mode?`); both are listed below. Constants and static fields are Ruby constants or class methods (`Pd4ml::PageSize::A4`, `Pd4ml::LayoutEngine.v42`), public instance fields are read with `obj[:name]`. Java types appear as Ruby types: binary Strings for `byte[]` and `InputStream`, a path or URL String for `URL`/`File`, `JavaObject` for any other Java object. The DOM API classes (`Pd4ml::Dom`: `PD4ML#get_document` and `PD4ML#layout` return them; v42 layout engine, DMS and UA editions) return Java lists and sets as Ruby Arrays, maps as Hashes. Descriptions of each method are in the [PD4ML manual](https://pd4ml.com/docs/pd4ml-manual.html).
|
|
6
6
|
|
|
7
7
|
## Pd4ml::PD4ML
|
|
8
8
|
|
|
@@ -78,6 +78,8 @@ keep_trailing_blank_pages?() -> true/false # isKeepTrailingBlankPages
|
|
|
78
78
|
test_termination() -> nil # testTermination
|
|
79
79
|
get_height(width: Integer) -> Integer # getHeight
|
|
80
80
|
get_document_title() -> String # getDocumentTitle
|
|
81
|
+
layout() -> Dom::Layout # layout
|
|
82
|
+
get_document() -> Dom::Document # getDocument
|
|
81
83
|
set_rendering_hints(hints: Hash) -> nil # setRenderingHints
|
|
82
84
|
dump(mode: Integer) -> String # dump
|
|
83
85
|
protect_physical_unit_dimensions(keep_physical_unit_dimensions: true/false) -> nil # protectPhysicalUnitDimensions
|
|
@@ -85,10 +87,12 @@ Pd4ml::PD4ML.translate_to_pt(mm: Float) -> Float # translateToPt
|
|
|
85
87
|
Pd4ml::PD4ML.predict_scale(page_margins: PageMargins, page_size: PageSize, user_space: Integer) -> Float # predictScale
|
|
86
88
|
Pd4ml::PD4ML.predict_page_height(page_margins: PageMargins, page_size: PageSize, user_space: Integer) -> Integer # predictPageHeight
|
|
87
89
|
set_dynamic_data(map: Hash) -> nil # setDynamicData
|
|
90
|
+
add_allowed_resource_location(locations: String) -> nil # addAllowedResourceLocation
|
|
88
91
|
set_param(key: String, value: String) -> nil # setParam
|
|
89
92
|
get_last_render_info(key: String) -> Object # getLastRenderInfo
|
|
90
93
|
output_range(description: String) -> nil # outputRange
|
|
91
94
|
add_metadata(key: String, value: String, hide: true/false) -> nil # addMetadata
|
|
95
|
+
add_metadata(namespace_uri: String, prefix: String, key: String, value: String) -> nil # addMetadata
|
|
92
96
|
apply_kerning(enable: true/false) -> nil # applyKerning
|
|
93
97
|
enable_ligatures(enable: true/false) -> nil # enableLigatures
|
|
94
98
|
get_cache() -> Object # getCache
|
|
@@ -119,7 +123,6 @@ async_resource_loader(thread_pool_size: Integer) -> nil # asyncResourceLoader
|
|
|
119
123
|
| `Pd4ml::Constants::PD4ML_EXTRA_RESOURCE_LOADERS` | String | static read-only |
|
|
120
124
|
| `Pd4ml::Constants::PD4ML_DOCUMENT_VIEW_MODE` | String | static read-only |
|
|
121
125
|
| `Pd4ml::Constants::PD4ML_DOCUMENT_DATE` | String | static read-only |
|
|
122
|
-
| `Pd4ml::Constants::PD4ML_WORD_EDITABLE` | String | static read-only |
|
|
123
126
|
| `Pd4ml::Constants::PD4ML_DOCUMENT_XMP_DATE` | String | static read-only |
|
|
124
127
|
| `Pd4ml::Constants::PD4ML_FAKE_BROWSER` | String | static read-only |
|
|
125
128
|
| `Pd4ml::Constants::PD4ML_CACHE_ENABLE` | String | static read-only |
|
|
@@ -398,6 +401,7 @@ requires_metadata() -> true/false # requiresMetadata
|
|
|
398
401
|
requires_output_internet() -> true/false # requiresOutputInternet
|
|
399
402
|
allows_external_hyperlinking() -> true/false # allowsExternalHyperlinking
|
|
400
403
|
requires_reliable_unicode_semantics() -> true/false # requiresReliableUnicodeSemantics
|
|
404
|
+
has_pdf_version() -> true/false # hasPdfVersion
|
|
401
405
|
get_pdf_version_label() -> String # getPdfVersionLabel
|
|
402
406
|
get_pdf_ext_level() -> Integer # getPdfExtLevel
|
|
403
407
|
get_pdfa_version() -> Integer # getPdfaVersion
|
|
@@ -450,7 +454,7 @@ Pd4ml::PdfDocumentBase.compute_scope(scope: String, page_num: Integer) -> Array<
|
|
|
450
454
|
|
|
451
455
|
## Pd4ml::PdfDocument
|
|
452
456
|
|
|
453
|
-
`com.pd4ml.PdfDocument`
|
|
457
|
+
`com.pd4ml.PdfDocument` — also the methods of `Pd4ml::PdfDocumentBase`
|
|
454
458
|
|
|
455
459
|
```ruby
|
|
456
460
|
Pd4ml::PdfDocument.new(bytes: String (binary), password: String)
|
|
@@ -544,3 +548,151 @@ error?() -> true/false # isError
|
|
|
544
548
|
warning?() -> true/false # isWarning
|
|
545
549
|
get_message() -> String # getMessage
|
|
546
550
|
```
|
|
551
|
+
|
|
552
|
+
## Pd4ml::Dom::Node
|
|
553
|
+
|
|
554
|
+
`com.pd4ml.dom.Node` (DOM API)
|
|
555
|
+
|
|
556
|
+
```ruby
|
|
557
|
+
get_node_name() -> String # getNodeName
|
|
558
|
+
get_parent_node() -> Dom::Node # getParentNode
|
|
559
|
+
get_parent_element() -> Dom::Element # getParentElement
|
|
560
|
+
get_owner_document() -> Dom::Document # getOwnerDocument
|
|
561
|
+
get_child_nodes() -> Array<Dom::Node> # getChildNodes
|
|
562
|
+
has_child_nodes() -> true/false # hasChildNodes
|
|
563
|
+
get_first_child() -> Dom::Node # getFirstChild
|
|
564
|
+
get_last_child() -> Dom::Node # getLastChild
|
|
565
|
+
get_next_sibling() -> Dom::Node # getNextSibling
|
|
566
|
+
get_previous_sibling() -> Dom::Node # getPreviousSibling
|
|
567
|
+
get_text_content() -> String # getTextContent
|
|
568
|
+
set_text_content(text: String) -> nil # setTextContent
|
|
569
|
+
append_child(child: Dom::Node) -> Dom::Node # appendChild
|
|
570
|
+
insert_before(child: Dom::Node, ref: Dom::Node) -> Dom::Node # insertBefore
|
|
571
|
+
replace_child(replacement: Dom::Node, old: Dom::Node) -> Dom::Node # replaceChild
|
|
572
|
+
remove_child(child: Dom::Node) -> Dom::Node # removeChild
|
|
573
|
+
remove() -> nil # remove
|
|
574
|
+
clone_node(deep: true/false) -> Dom::Node # cloneNode
|
|
575
|
+
contains(other: Dom::Node) -> true/false # contains
|
|
576
|
+
to_html() -> String # toHtml
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
## Pd4ml::Dom::Document
|
|
580
|
+
|
|
581
|
+
`com.pd4ml.dom.Document` (DOM API) — also the methods of `Pd4ml::Dom::Node`
|
|
582
|
+
|
|
583
|
+
```ruby
|
|
584
|
+
get_document_element() -> Dom::Element # getDocumentElement
|
|
585
|
+
get_head() -> Dom::Element # getHead
|
|
586
|
+
get_body() -> Dom::Element # getBody
|
|
587
|
+
get_doctype() -> Dom::DocumentType # getDoctype
|
|
588
|
+
quirks_mode?() -> true/false # isQuirksMode
|
|
589
|
+
get_base_uri() -> String # getBaseURI
|
|
590
|
+
get_title() -> String # getTitle
|
|
591
|
+
get_element_by_id(id: String) -> Dom::Element # getElementById
|
|
592
|
+
get_elements_by_tag_name(name: String) -> Array<Dom::Element> # getElementsByTagName
|
|
593
|
+
get_elements_by_class_name(names: String) -> Array<Dom::Element> # getElementsByClassName
|
|
594
|
+
query_selector(selectors: String) -> Dom::Element # querySelector
|
|
595
|
+
query_selector_all(selectors: String) -> Array<Dom::Element> # querySelectorAll
|
|
596
|
+
create_element(tag_name: String) -> Dom::Element # createElement
|
|
597
|
+
create_text_node(data: String) -> Dom::Text # createTextNode
|
|
598
|
+
create_comment(data: String) -> Dom::Comment # createComment
|
|
599
|
+
set_text_content(text: String) -> nil # setTextContent
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
## Pd4ml::Dom::Element
|
|
603
|
+
|
|
604
|
+
`com.pd4ml.dom.Element` (DOM API) — also the methods of `Pd4ml::Dom::Node`
|
|
605
|
+
|
|
606
|
+
```ruby
|
|
607
|
+
get_tag_name() -> String # getTagName
|
|
608
|
+
get_attribute(name: String) -> String # getAttribute
|
|
609
|
+
has_attribute(name: String) -> true/false # hasAttribute
|
|
610
|
+
set_attribute(name: String, value: String) -> nil # setAttribute
|
|
611
|
+
remove_attribute(name: String) -> nil # removeAttribute
|
|
612
|
+
get_attributes() -> Hash # getAttributes
|
|
613
|
+
get_id() -> String # getId
|
|
614
|
+
set_id(id: String) -> nil # setId
|
|
615
|
+
get_class_name() -> String # getClassName
|
|
616
|
+
set_class_name(class_name: String) -> nil # setClassName
|
|
617
|
+
get_class_list() -> Array<String> # getClassList
|
|
618
|
+
has_class(name: String) -> true/false # hasClass
|
|
619
|
+
add_class(name: String) -> nil # addClass
|
|
620
|
+
remove_class(name: String) -> nil # removeClass
|
|
621
|
+
toggle_class(name: String) -> true/false # toggleClass
|
|
622
|
+
get_children() -> Array<Dom::Element> # getChildren
|
|
623
|
+
get_first_element_child() -> Dom::Element # getFirstElementChild
|
|
624
|
+
get_last_element_child() -> Dom::Element # getLastElementChild
|
|
625
|
+
get_next_element_sibling() -> Dom::Element # getNextElementSibling
|
|
626
|
+
get_previous_element_sibling() -> Dom::Element # getPreviousElementSibling
|
|
627
|
+
get_elements_by_tag_name(name: String) -> Array<Dom::Element> # getElementsByTagName
|
|
628
|
+
get_elements_by_class_name(names: String) -> Array<Dom::Element> # getElementsByClassName
|
|
629
|
+
query_selector(selectors: String) -> Dom::Element # querySelector
|
|
630
|
+
query_selector_all(selectors: String) -> Array<Dom::Element> # querySelectorAll
|
|
631
|
+
matches(selectors: String) -> true/false # matches
|
|
632
|
+
closest(selectors: String) -> Dom::Element # closest
|
|
633
|
+
get_inner_html() -> String # getInnerHTML
|
|
634
|
+
set_inner_html(html: String) -> nil # setInnerHTML
|
|
635
|
+
get_outer_html() -> String # getOuterHTML
|
|
636
|
+
set_outer_html(html: String) -> nil # setOuterHTML
|
|
637
|
+
set_text_content(text: String) -> nil # setTextContent
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
## Pd4ml::Dom::Text
|
|
641
|
+
|
|
642
|
+
`com.pd4ml.dom.Text` (DOM API) — also the methods of `Pd4ml::Dom::Node`
|
|
643
|
+
|
|
644
|
+
```ruby
|
|
645
|
+
get_data() -> String # getData
|
|
646
|
+
set_data(data: String) -> nil # setData
|
|
647
|
+
whitespace?() -> true/false # isWhitespace
|
|
648
|
+
set_text_content(text: String) -> nil # setTextContent
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
## Pd4ml::Dom::Comment
|
|
652
|
+
|
|
653
|
+
`com.pd4ml.dom.Comment` (DOM API) — also the methods of `Pd4ml::Dom::Node`
|
|
654
|
+
|
|
655
|
+
```ruby
|
|
656
|
+
get_data() -> String # getData
|
|
657
|
+
set_data(data: String) -> nil # setData
|
|
658
|
+
get_text_content() -> String # getTextContent
|
|
659
|
+
set_text_content(text: String) -> nil # setTextContent
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## Pd4ml::Dom::DocumentType
|
|
663
|
+
|
|
664
|
+
`com.pd4ml.dom.DocumentType` (DOM API) — also the methods of `Pd4ml::Dom::Node`
|
|
665
|
+
|
|
666
|
+
```ruby
|
|
667
|
+
get_name() -> String # getName
|
|
668
|
+
get_public_id() -> String # getPublicId
|
|
669
|
+
get_system_id() -> String # getSystemId
|
|
670
|
+
set_text_content(text: String) -> nil # setTextContent
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
## Pd4ml::Dom::Layout
|
|
674
|
+
|
|
675
|
+
`com.pd4ml.dom.Layout` (DOM API)
|
|
676
|
+
|
|
677
|
+
```ruby
|
|
678
|
+
get_page_count() -> Integer # getPageCount
|
|
679
|
+
get_page_width(page: Integer) -> Float # getPageWidth
|
|
680
|
+
get_page_height(page: Integer) -> Float # getPageHeight
|
|
681
|
+
get_page_number(page: Integer) -> Integer # getPageNumber
|
|
682
|
+
get_boxes(element: Dom::Element) -> Array<Dom::LayoutBox> # getBoxes
|
|
683
|
+
get_pages(element: Dom::Element) -> Array<Integer> # getPages
|
|
684
|
+
get_computed_style(element: Dom::Element, property: String) -> String # getComputedStyle
|
|
685
|
+
element_at(page: Integer, x: Float, y: Float) -> Dom::Element # elementAt
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
## Pd4ml::Dom::LayoutBox
|
|
689
|
+
|
|
690
|
+
`com.pd4ml.dom.LayoutBox` (DOM API)
|
|
691
|
+
|
|
692
|
+
```ruby
|
|
693
|
+
get_page() -> Integer # getPage
|
|
694
|
+
get_x() -> Float # getX
|
|
695
|
+
get_y() -> Float # getY
|
|
696
|
+
get_width() -> Float # getWidth
|
|
697
|
+
get_height() -> Float # getHeight
|
|
698
|
+
```
|
data/docs/guide.md
CHANGED
|
@@ -34,7 +34,7 @@ The generated reference of every class and method is [api.md](api.md).
|
|
|
34
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
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
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.
|
|
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.21](#521-licensing). |
|
|
38
38
|
|
|
39
39
|
Check Java with `java -version`. On a server without Java, install a JRE package (for example
|
|
40
40
|
`apt install openjdk-21-jre-headless`) and, if it is not on the `PATH`, set `JAVA_HOME`. In
|
|
@@ -62,9 +62,9 @@ The gem contains the Ruby library and the bridge only. The PD4ML library itself
|
|
|
62
62
|
PD4ML Maven repository (<https://pd4ml.com/maven2>), in one of two ways.
|
|
63
63
|
|
|
64
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
|
|
66
|
-
|
|
67
|
-
is installed, this is all it takes:
|
|
65
|
+
([2.3](#23-other-ways-to-provide-pd4ml)), the first use of PD4ML in a process downloads PD4ML 4.2
|
|
66
|
+
([which build](#which-build)) before starting Java. A warning on standard error reports it. So
|
|
67
|
+
once the gem is installed, this is all it takes:
|
|
68
68
|
|
|
69
69
|
<!-- no-test -->
|
|
70
70
|
```ruby
|
|
@@ -82,7 +82,7 @@ pd4ml install # with Bundler: bundle exec pd4ml install
|
|
|
82
82
|
|
|
83
83
|
Either way, the gem:
|
|
84
84
|
|
|
85
|
-
- downloads
|
|
85
|
+
- downloads PD4ML 4.2 ([which build](#which-build));
|
|
86
86
|
- downloads the Bouncy Castle jars PD4ML needs for encryption and signing, from Maven Central;
|
|
87
87
|
- checks every file against its published SHA-1 checksum;
|
|
88
88
|
- stores them in a per-user cache directory: `%LOCALAPPDATA%\pd4ml-ruby` on Windows,
|
|
@@ -91,12 +91,29 @@ Either way, the gem:
|
|
|
91
91
|
The gem then uses that build. Later runs do not check for newer PD4ML releases: run
|
|
92
92
|
`pd4ml install` again to update. Updating the gem does not change the PD4ML build.
|
|
93
93
|
|
|
94
|
+
<a id="which-build"></a>**Which build.** The gem needs PD4ML 4.2 for its 4.2 API: `read_eml` and
|
|
95
|
+
`read_mht`, `add_allowed_resource_location`, `add_metadata` (XMP), and the DOM API
|
|
96
|
+
(`get_document`, `layout`), as well as for the v42 layout engine ([4.3](#43-layout-engines)). So a
|
|
97
|
+
plain `pd4ml install` (and the first use) takes:
|
|
98
|
+
|
|
99
|
+
- the most recent PD4ML release from 4.2.0 on;
|
|
100
|
+
- while 4.2.0 is not released yet, the most recent 4.2.x **development snapshot** (today
|
|
101
|
+
4.2.0-SNAPSHOT), because no release has the 4.2 API yet.
|
|
102
|
+
|
|
103
|
+
Once 4.2.0 is released, new installs take it by themselves, and `pd4ml install` updates an
|
|
104
|
+
existing snapshot install to it. Snapshots of later versions (4.3, 5.0) are never taken by
|
|
105
|
+
default; `--snapshots` asks for them.
|
|
106
|
+
|
|
107
|
+
The rest of the API works with older builds too: to stay on the current 4.1 release, install it
|
|
108
|
+
explicitly (`pd4ml install 4.1.2`); the 4.2 methods then raise `Pd4ml::JavaError`
|
|
109
|
+
(`NoSuchMethodException`).
|
|
110
|
+
|
|
94
111
|
| Option | |
|
|
95
112
|
|---|---|
|
|
96
|
-
| *(none)* | the most recent 4.x
|
|
113
|
+
| *(none)* | the most recent release from 4.2.0 on, else the most recent 4.2.x snapshot ([which build](#which-build)), or an update to it |
|
|
97
114
|
| `--snapshots` | the most recent 4.x build, including development snapshots |
|
|
98
|
-
| `pd4ml install 4.
|
|
99
|
-
| `--jakarta` |
|
|
115
|
+
| `pd4ml install 4.1.2` | an exact version (any release or snapshot the repository lists) |
|
|
116
|
+
| `--jakarta` | for Jakarta EE / Servlet 6 environments: a `-jakarta` variant if one fits; PD4ML 4.2 has none (its builds contain the Jakarta tag library), so the same 4.2 build (an older `-jakarta` build by version, e.g. `pd4ml install 4.0.20fx3-jakarta`) |
|
|
100
117
|
| `--home DIR` | install into `DIR` (default: `PD4ML_HOME`, else the cache directory) |
|
|
101
118
|
| `--force` | download again |
|
|
102
119
|
| `--no-bouncycastle` | skip the Bouncy Castle jars (encryption and signing will not work) |
|
|
@@ -252,6 +269,10 @@ such a build:
|
|
|
252
269
|
- **Objects**: a Java object comes back as a `Pd4ml::JavaObject` with all its Java methods.
|
|
253
270
|
Public fields: `obj[:width]` and `obj[:width] = value`. `to_s` is Java's `toString()`,
|
|
254
271
|
`enum_name` an enum constant's name, `java_class` the class name.
|
|
272
|
+
- **Collections**: a Java `List` or `Map` generally comes back as a `JavaObject` (`size`,
|
|
273
|
+
`get(i)`). The DOM API ([5.18](#518-modifying-the-document-before-conversion-dom-api)) is the
|
|
274
|
+
exception: its lists and sets come back as Ruby Arrays and its maps as Hashes. `call_java` returns
|
|
275
|
+
them as Java objects.
|
|
255
276
|
- **Values**: Strings, Integers, Floats (`100.0` goes to Java as a float), `true`/`false`, `nil`,
|
|
256
277
|
Symbols (as strings: `engine: :v40`), Arrays, Hashes (as Java `Map`s).
|
|
257
278
|
- **Binary data**: a binary String (`File.binread`, `"...".b`, encoding ASCII-8BIT) goes to Java
|
|
@@ -529,9 +550,8 @@ The image formats are `PNG8`, `PNG24`, `TIFF` and `TIFF_COMPRESSED` (constants i
|
|
|
529
550
|
The TIFF formats need Java 9 or later.
|
|
530
551
|
The v42 engine does not support the multi-page TIFF formats yet; use the v40 engine for them.
|
|
531
552
|
|
|
532
|
-
With the v42 engine, DOCX/RTF output
|
|
533
|
-
|
|
534
|
-
`pd.set_param(Pd4ml::Constants::PD4ML_WORD_EDITABLE, 'true')`.
|
|
553
|
+
With the v42 engine, DOCX/RTF output is *editable*: positioned content becomes text boxes
|
|
554
|
+
and form fields, not pictures.
|
|
535
555
|
|
|
536
556
|
### 5.12 Interactive forms and multi-column layout
|
|
537
557
|
|
|
@@ -583,6 +603,7 @@ pd.set_log_level(1) # 0 (silent) .. 5
|
|
|
583
603
|
pd.set_author_name('Reporting service')
|
|
584
604
|
pd.set_document_title('Monthly statement')
|
|
585
605
|
pd.add_metadata('StatementId', '2026-10', false)
|
|
606
|
+
pd.add_metadata('http://www.w3.org/ns/tdmrep/', 'tdm', 'reservation', '1') # XMP, a namespace of your own
|
|
586
607
|
pd.read_html_string('<h1>Statement</h1>')
|
|
587
608
|
pd.write_pdf('statement.pdf')
|
|
588
609
|
pages = pd.get_last_render_info(Pd4ml::Constants::PD4ML_TOTAL_PAGES).to_i
|
|
@@ -593,6 +614,14 @@ itself. An error it raises comes out of the call that caused it, as a `Pd4ml::Ca
|
|
|
593
614
|
The v42 engine currently reports fewer progress stages than v40. More in
|
|
594
615
|
[extensibility and diagnostics](https://pd4ml.com/docs/#12.-extensibility-and-diagnostics).
|
|
595
616
|
|
|
617
|
+
`add_metadata(key, value, hide)` adds a custom property to the PDF's XMP metadata, which viewers
|
|
618
|
+
show among the custom document properties (`hide`: true keeps it out of their view).
|
|
619
|
+
`add_metadata(namespace_uri, prefix, key, value)` (PD4ML 4.2) adds an XMP property in a
|
|
620
|
+
namespace of your own; the example writes `<tdm:reservation>1</tdm:reservation>`, which tells
|
|
621
|
+
text and data mining crawlers the rights are reserved (the W3C TDM Reservation Protocol). In PDF/A
|
|
622
|
+
output, PD4ML declares the namespace in a PDF/A extension schema. A reserved prefix (`rdf`, `xmp`,
|
|
623
|
+
`dc`, `pdf`, `pd4ml`...) raises `Pd4ml::JavaError` (`IllegalArgumentException`).
|
|
624
|
+
|
|
596
625
|
### 5.15 Working with existing PDFs
|
|
597
626
|
|
|
598
627
|
```ruby
|
|
@@ -621,7 +650,136 @@ PD4ML's PDF toolkit (signing, optimisation, XFDF) is described in
|
|
|
621
650
|
[the PDF post-processing toolkit](https://pd4ml.com/docs/#15.-the-pdf-post-processing-toolkit)
|
|
622
651
|
and is reachable through `Pd4ml::Java.type(...)`.
|
|
623
652
|
|
|
624
|
-
### 5.16
|
|
653
|
+
### 5.16 E-mail messages and saved web pages
|
|
654
|
+
|
|
655
|
+
> **Editions.** E-mail and saved web page input — `read_eml`/`read_mht`, and `read_html` for input it
|
|
656
|
+
> recognizes as one of them — is part of the PD4ML **DMS and UA editions**. With an STD or PRO license it
|
|
657
|
+
> works, but the output carries the "PD4ML EVALUATION MODE" banners.
|
|
658
|
+
|
|
659
|
+
PD4ML 4.2 reads e-mail messages (`.eml`) and web pages saved as a single file (`.mht`, `.mhtml`)
|
|
660
|
+
and writes them to any output format:
|
|
661
|
+
|
|
662
|
+
```ruby
|
|
663
|
+
require 'pd4ml'
|
|
664
|
+
|
|
665
|
+
pd = Pd4ml::PD4ML.new
|
|
666
|
+
pd.read_eml('message.eml') # a path or URL, or the message itself: File.binread, an IO
|
|
667
|
+
pd.write_pdf('message.pdf') # the attachments are embedded in the PDF
|
|
668
|
+
|
|
669
|
+
page = Pd4ml::PD4ML.new
|
|
670
|
+
page.read_mht('page.mht') # a saved web page, with its images, styles and fonts
|
|
671
|
+
page.write_pdf('page.pdf')
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
- A message becomes a block with From, Date, To, Cc, Subject and the attachments, followed by its
|
|
675
|
+
body: the HTML version if there is one, otherwise the plain text, with the images it embeds. The
|
|
676
|
+
attached files are embedded in the PDF next to their names (PDF/A-1 and PDF/A-2 and the other
|
|
677
|
+
outputs keep only the names).
|
|
678
|
+
- A saved page shows as it was saved, with the resources saved in the file; its links lead where
|
|
679
|
+
they led on the original page.
|
|
680
|
+
- `read_html` recognizes these files too, by the `.eml`, `.mht` or `.mhtml` ending or by how the
|
|
681
|
+
content starts. For mail from outside your control, use `read_eml` and `read_mht`.
|
|
682
|
+
- Of the resources a file refers to but does not contain, only `https://` ones are loaded.
|
|
683
|
+
`add_allowed_resource_location` ([5.17](#517-untrusted-html-restricting-resource-loading))
|
|
684
|
+
replaces that list. A file read from a path or URL must then be within the allowed locations
|
|
685
|
+
itself; one passed as content (a binary String or IO) need not be.
|
|
686
|
+
- Scripts do not run, the content of frames is not shown, and encrypted (S/MIME, PGP) messages
|
|
687
|
+
are not decrypted.
|
|
688
|
+
|
|
689
|
+
See [converting e-mail and saved web pages](https://pd4ml.com/docs/features/converting-e-mail-and-saved-web-pages.html).
|
|
690
|
+
|
|
691
|
+
### 5.17 Untrusted HTML: restricting resource loading
|
|
692
|
+
|
|
693
|
+
HTML from users, customers or other systems decides what PD4ML loads: `<img src="file:/etc/...">`
|
|
694
|
+
reads local files, `http://169.254.169.254/...` calls services inside your network.
|
|
695
|
+
`add_allowed_resource_location` (PD4ML 4.2) limits loading to the locations you list:
|
|
696
|
+
|
|
697
|
+
```ruby
|
|
698
|
+
require 'pd4ml'
|
|
699
|
+
|
|
700
|
+
user_html = '<p>Comment <img src="file:/etc/hosts"> <img src="http://169.254.169.254/latest/meta-data/"></p>'
|
|
701
|
+
pd = Pd4ml::PD4ML.new
|
|
702
|
+
pd.add_allowed_resource_location('https://cdn.example.com/assets/')
|
|
703
|
+
pd.add_allowed_resource_location('https://*.example.org/, file:/opt/app/templates/')
|
|
704
|
+
pd.read_html_string(user_html, 'https://cdn.example.com/assets/')
|
|
705
|
+
pd.write_pdf('comment.pdf') # without the two images
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
- `locations` is a comma-separated list of URL prefixes; each call adds to the list. `*` matches
|
|
709
|
+
any characters except `/` (one host name or path segment). The comparison ignores case, and a
|
|
710
|
+
path containing `..` is always rejected.
|
|
711
|
+
- It covers style sheets, images, web fonts and attachments, and the document itself when it is
|
|
712
|
+
read from a path or URL. `data:` URIs, `java:` resources and the font directories of `use_ttf`
|
|
713
|
+
are not affected.
|
|
714
|
+
- A rejected resource is left out and the conversion goes on;
|
|
715
|
+
`set_error_policy(Pd4ml::ErrorPolicy::PEDANTIC)` makes it fail with a `Pd4ml::JavaError`
|
|
716
|
+
instead, and `set_log_level(Pd4ml::Constants::LOG_CACHE)` logs what was rejected.
|
|
717
|
+
- With no location added, resources load from anywhere. Call it before `read_html`.
|
|
718
|
+
|
|
719
|
+
See [restricting resource loading for untrusted HTML](https://pd4ml.com/docs/features/restricting-resource-loading-for-untrusted-html.html).
|
|
720
|
+
|
|
721
|
+
### 5.18 Modifying the document before conversion (DOM API)
|
|
722
|
+
|
|
723
|
+
With the v42 layout engine, the document `read_html` parsed can be read, queried with CSS
|
|
724
|
+
selectors and changed before it is written; `layout` lays it out into pages without writing it
|
|
725
|
+
and tells where each element ended up.
|
|
726
|
+
|
|
727
|
+
> The DOM API (`get_document`, `layout` and the `Pd4ml::Dom` classes) is part of the **DMS and UA
|
|
728
|
+
> editions** of PD4ML 4.2. Without a license it can be evaluated (the output carries the
|
|
729
|
+
> evaluation watermark); with an STD or PRO license it works, but the output of a document it was
|
|
730
|
+
> used on carries "PD4ML EVALUATION MODE" banners. See [editions](https://pd4ml.com/products/).
|
|
731
|
+
|
|
732
|
+
```ruby
|
|
733
|
+
require 'pd4ml'
|
|
734
|
+
|
|
735
|
+
pd = Pd4ml::PD4ML.new # the v42 engine
|
|
736
|
+
pd.read_html_string(<<~HTML)
|
|
737
|
+
<h1 id="title">Invoice</h1>
|
|
738
|
+
<table class="items"><tr><td>Consulting</td><td class="price">1 200,00</td></tr></table>
|
|
739
|
+
<img src="logo.png">
|
|
740
|
+
<div id="totals">Total: <span id="total"></span></div>
|
|
741
|
+
HTML
|
|
742
|
+
|
|
743
|
+
doc = pd.get_document
|
|
744
|
+
doc.get_element_by_id('total').set_text_content('1 250,00 EUR')
|
|
745
|
+
doc.query_selector_all('img:not([alt])').each { |img| img.set_attribute('alt', '') } # a Ruby Array
|
|
746
|
+
doc.query_selector('table.items').add_class('striped')
|
|
747
|
+
note = doc.create_element('p')
|
|
748
|
+
note.set_inner_html('Payable within <b>30 days</b>.')
|
|
749
|
+
doc.get_body.append_child(note)
|
|
750
|
+
|
|
751
|
+
layout = pd.layout # the pages as write_pdf would make them
|
|
752
|
+
totals = doc.get_element_by_id('totals')
|
|
753
|
+
totals.set_attribute('style', 'page-break-before: always') if layout.get_pages(totals).size > 1
|
|
754
|
+
layout.get_boxes(totals).each do |box|
|
|
755
|
+
puts format('page %d: %.1f, %.1f, %.1f x %.1f pt', box.get_page, box.get_x, box.get_y, box.get_width, box.get_height)
|
|
756
|
+
end
|
|
757
|
+
puts "#{layout.get_page_count} page(s), total in #{layout.get_computed_style(totals, 'color')}"
|
|
758
|
+
pd.write_pdf('invoice.pdf')
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
| | Methods (Ruby-style; the Java names work too) |
|
|
762
|
+
|---|---|
|
|
763
|
+
| `Dom::Document` (`pd.get_document`) | `get_element_by_id`, `query_selector`, `query_selector_all`, `get_elements_by_tag_name`, `get_elements_by_class_name`, `get_head`, `get_body`, `get_title`, `create_element`, `create_text_node`, `create_comment` |
|
|
764
|
+
| `Dom::Node` (every node) | `get_parent_node`, `get_child_nodes`, `get_first_child`, `get_next_sibling`..., `append_child`, `insert_before`, `replace_child`, `remove_child`, `remove`, `clone_node`, `contains`, `get_text_content`, `set_text_content`, `to_html` |
|
|
765
|
+
| `Dom::Element` | `get_attribute`, `set_attribute`, `remove_attribute`, `get_attributes` (a Hash), `get_id`, `get_class_list`, `add_class`, `remove_class`, `toggle_class`, `get_inner_html`, `set_inner_html`, `get_outer_html`, `set_outer_html`, `matches`, `closest`, `get_children`... and the queries of `Document` within the element |
|
|
766
|
+
| `Dom::Layout` (`pd.layout`) | `get_page_count`, `get_page_width(page)`, `get_page_height(page)`, `get_page_number(page)`, `get_boxes(element)`, `get_pages(element)`, `get_computed_style(element, property)`, `element_at(page, x, y)` |
|
|
767
|
+
| `Dom::LayoutBox` | `get_page`, `get_x`, `get_y`, `get_width`, `get_height` (PDF points from the top left corner of the page) |
|
|
768
|
+
|
|
769
|
+
- Lists (`query_selector_all`, `get_child_nodes`, `get_boxes`, `get_pages`...) come back as Ruby
|
|
770
|
+
Arrays, `get_attributes` as a Hash. They are snapshots: removing or moving nodes while going
|
|
771
|
+
through one is safe.
|
|
772
|
+
- Two objects for the same node are `==` (Java `equals`), but not necessarily the same object.
|
|
773
|
+
- A layout is a snapshot too: after changing the document, call `layout` again. Pages are
|
|
774
|
+
numbered from 1.
|
|
775
|
+
- Change the document before the first output; every later write lays out the changed document.
|
|
776
|
+
- `get_document` and `layout` raise `Pd4ml::JavaError` (`IllegalStateException`) before
|
|
777
|
+
`read_html` or with the v40 engine.
|
|
778
|
+
|
|
779
|
+
The full method list is in [api.md](api.md#pd4mldomnode); the PD4ML guide is
|
|
780
|
+
[modifying the HTML document before conversion](https://pd4ml.com/docs/features/modifying-the-html-document-before-conversion.html).
|
|
781
|
+
|
|
782
|
+
### 5.19 Web applications
|
|
625
783
|
|
|
626
784
|
Rails controller, with the HTML from a view:
|
|
627
785
|
|
|
@@ -658,7 +816,7 @@ To pay the cost of starting Java at boot instead of on the first request, touch
|
|
|
658
816
|
initializer (`Pd4ml.version`). Each Puma worker process starts its own bridge; set
|
|
659
817
|
`PD4ML_BRIDGE` to share one bridge server between them.
|
|
660
818
|
|
|
661
|
-
### 5.
|
|
819
|
+
### 5.20 Batch conversion
|
|
662
820
|
|
|
663
821
|
```ruby
|
|
664
822
|
require 'pd4ml'
|
|
@@ -674,7 +832,7 @@ end
|
|
|
674
832
|
One bridge serves the whole loop. To convert in parallel, run several processes (each with its
|
|
675
833
|
own bridge), or point them all at one bridge server.
|
|
676
834
|
|
|
677
|
-
### 5.
|
|
835
|
+
### 5.21 Licensing
|
|
678
836
|
|
|
679
837
|
<!-- no-test -->
|
|
680
838
|
```ruby
|
|
@@ -709,8 +867,9 @@ Without a license, PD4ML is fully functional but marks its output. See
|
|
|
709
867
|
differently than in the browser, set `set_html_width` to the width the page was designed for.
|
|
710
868
|
- **Index fonts once** with `-configure.fonts`, instead of pointing `use_ttf` at a large
|
|
711
869
|
unindexed directory on every conversion.
|
|
712
|
-
- **Restrict what documents can load** when converting untrusted HTML
|
|
713
|
-
`
|
|
870
|
+
- **Restrict what documents can load** when converting untrusted HTML, with
|
|
871
|
+
`add_allowed_resource_location` ([5.17](#517-untrusted-html-restricting-resource-loading)); also
|
|
872
|
+
consider `set_param(Pd4ml::Constants::PD4ML_DISABLE_EXTERNAL_ATTACHMENTS, 'true')`.
|
|
714
873
|
- **Pin the build in production.** Use `pd4ml install <version>` or a `PD4ML_CLASSPATH` to a
|
|
715
874
|
jar you keep, rather than following the latest release.
|
|
716
875
|
|
|
@@ -739,6 +898,7 @@ Without a license, PD4ML is fully functional but marks its output. See
|
|
|
739
898
|
|---|---|
|
|
740
899
|
| `Pd4ml::PD4ML` | the converter: `Pd4ml::PD4ML.new(license:, html_width:, engine:)`; every Java method of `com.pd4ml.PD4ML` |
|
|
741
900
|
| `PageSize`, `PageMargins`, `Units`, `Dimensions`, `Constants`, `LayoutEngine`, `ErrorPolicy`, `PdfSpec`, `PdfDocument`, `ProgressListener`, `StatusMessage`, `ResourceProvider`, `CustomTag` | the PD4ML Java classes ([api.md](api.md)) |
|
|
901
|
+
| `Dom::Document`, `Dom::Node`, `Dom::Element`, `Dom::Text`, `Dom::Comment`, `Dom::DocumentType`, `Dom::Layout`, `Dom::LayoutBox` | the DOM API classes, from `pd.get_document` and `pd.layout` ([5.18](#518-modifying-the-document-before-conversion-dom-api)); their lists come back as Arrays |
|
|
742
902
|
| `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
903
|
| `version`, `layout_engines?`, `to_url(location)`, `pdf_spec(keyword)` | runtime helpers |
|
|
744
904
|
| `configure(**options)`, `shutdown` | the bridge: `classpath:`, `java:`, `jvm_options:`, `server:`, `log_file:` |
|
|
@@ -752,6 +912,7 @@ Without a license, PD4ML is fully functional but marks its output. See
|
|
|
752
912
|
|---|---|
|
|
753
913
|
| `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
914
|
| `read_html_string(html, base_url = nil)` | the document as a String |
|
|
915
|
+
| `read_eml(source)`, `read_mht(source)` | an e-mail message, a saved web page ([5.16](#516-e-mail-messages-and-saved-web-pages)), DMS and UA editions; `source` as for `read_html` |
|
|
755
916
|
| `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
917
|
| `write_docx(target = nil)`, `write_rtf(target = nil, rtf_wmf: false)` | as `write_pdf` |
|
|
757
918
|
| `render_as_images(format)`, `render_png` | pages as encoded images (binary Strings); `render_as_images(dir, name, format)` writes files |
|
data/exe/pd4ml
CHANGED
|
@@ -5,11 +5,16 @@
|
|
|
5
5
|
require 'optparse'
|
|
6
6
|
require_relative '../lib/pd4ml'
|
|
7
7
|
|
|
8
|
+
MINIMUM = Pd4ml::Installer::MINIMUM_VERSION
|
|
9
|
+
LINE = MINIMUM.split('.')[0, 2].join('.')
|
|
8
10
|
USAGE = <<~TEXT
|
|
9
11
|
usage: pd4ml <command> [options]
|
|
10
12
|
|
|
11
13
|
install [VERSION] [--snapshots] [--jakarta] [--home DIR] [--force] [--no-bouncycastle]
|
|
12
|
-
download a PD4ML build
|
|
14
|
+
download a PD4ML build, or update to it. Default: the most recent
|
|
15
|
+
release from #{MINIMUM} on; until #{MINIMUM} is out, the most recent #{LINE}.x
|
|
16
|
+
snapshot (the gem's #{LINE} API needs PD4ML #{LINE}). --snapshots: the most
|
|
17
|
+
recent 4.* build. VERSION: an exact one, e.g. 4.1.2
|
|
13
18
|
list [--snapshots] [--jakarta]
|
|
14
19
|
the versions in the PD4ML repository (* the installed one)
|
|
15
20
|
version the PD4ML version the gem runs (downloads it on first use)
|
data/lib/pd4ml/bridge.rb
CHANGED
|
@@ -66,7 +66,7 @@ module Pd4ml
|
|
|
66
66
|
unless entries
|
|
67
67
|
entries = Installer.installed_classpath
|
|
68
68
|
if entries.empty? && Installer.auto_install?
|
|
69
|
-
warn
|
|
69
|
+
warn "pd4ml: no PD4ML build installed yet: downloading PD4ML #{Installer::MINIMUM_VERSION} or later " \
|
|
70
70
|
"(once; 'pd4ml install' updates it, PD4ML_AUTO_INSTALL=0 turns this off)"
|
|
71
71
|
Installer.install
|
|
72
72
|
entries = Installer.installed_classpath
|
data/lib/pd4ml/installer.rb
CHANGED
|
@@ -9,9 +9,10 @@ require 'time'
|
|
|
9
9
|
module Pd4ml
|
|
10
10
|
# Downloads a PD4ML build from the PD4ML Maven repository and makes it the gem's default:
|
|
11
11
|
#
|
|
12
|
-
# pd4ml install the most recent 4
|
|
12
|
+
# pd4ml install the most recent release from MINIMUM_VERSION (4.2.0) on;
|
|
13
|
+
# until there is one, the most recent 4.2.x snapshot
|
|
13
14
|
# pd4ml install --snapshots the most recent 4.* build, snapshots included
|
|
14
|
-
# pd4ml install 4.
|
|
15
|
+
# pd4ml install 4.1.2 an exact version
|
|
15
16
|
#
|
|
16
17
|
# or Pd4ml.install from code. The jar is stored in the user's cache directory (PD4ML_HOME to
|
|
17
18
|
# choose another) with the Bouncy Castle jars PD4ML needs for encryption and signing, every
|
|
@@ -26,6 +27,9 @@ module Pd4ml
|
|
|
26
27
|
BOUNCYCASTLE = %w[bcprov-jdk18on bcpkix-jdk18on bcutil-jdk18on].freeze
|
|
27
28
|
MANIFEST = 'installed.json'
|
|
28
29
|
VERSION_PATTERN = /\A(\d+)\.(\d+)\.(\d+)(?:fx(\d+)(?:a(\d+))?)?(-jakarta)?(-SNAPSHOT)?\z/
|
|
30
|
+
# The oldest PD4ML a plain install takes: the gem's PD4ML 4.2 API (read_eml/read_mht,
|
|
31
|
+
# add_allowed_resource_location, add_metadata, the DOM API) needs it.
|
|
32
|
+
MINIMUM_VERSION = '4.2.0'
|
|
29
33
|
|
|
30
34
|
module_function
|
|
31
35
|
|
|
@@ -54,20 +58,55 @@ module Pd4ml
|
|
|
54
58
|
sort_versions(candidates).last
|
|
55
59
|
end
|
|
56
60
|
|
|
61
|
+
# The version a plain install takes: the most recent release of the minimum's major line (4.*)
|
|
62
|
+
# at least `minimum`; while there is none, the most recent snapshot of the minimum's line
|
|
63
|
+
# (4.2.x for 4.2.0); while there is none of those either (the -jakarta variant has no 4.2
|
|
64
|
+
# build yet), the most recent 4.* release, as before. So installs move to the release once it
|
|
65
|
+
# is out, and snapshots of later lines (or a 5.x) are never picked by accident. With
|
|
66
|
+
# `jakarta`, a -jakarta variant by that rule if there is one, else the plain build of the same
|
|
67
|
+
# rule: PD4ML 4.2 publishes no -jakarta variant, its builds contain the Jakarta tag library
|
|
68
|
+
# (com.pd4ml.taglib.jakarta); only without either, the most recent -jakarta release.
|
|
69
|
+
def default_version(versions, jakarta: false, minimum: MINIMUM_VERSION)
|
|
70
|
+
min = version_key(minimum)
|
|
71
|
+
(jakarta ? [true, false] : [false]).each do |variant|
|
|
72
|
+
candidates = versions.select do |v|
|
|
73
|
+
k = version_key(v)
|
|
74
|
+
k && k[0] == min[0] && v.include?('-jakarta') == variant
|
|
75
|
+
end
|
|
76
|
+
releases = candidates.select { |v| !v.end_with?('-SNAPSHOT') && (version_key(v) <=> min) >= 0 }
|
|
77
|
+
return sort_versions(releases).last unless releases.empty?
|
|
78
|
+
|
|
79
|
+
snapshots = candidates.select { |v| v.end_with?('-SNAPSHOT') && version_key(v)[0, 2] == min[0, 2] }
|
|
80
|
+
return sort_versions(snapshots).last unless snapshots.empty?
|
|
81
|
+
end
|
|
82
|
+
select_version(versions, major: min[0], jakarta: jakarta)
|
|
83
|
+
end
|
|
84
|
+
|
|
57
85
|
# The versions the repository lists.
|
|
58
86
|
def available_versions(repository = REPOSITORY)
|
|
59
|
-
fetch("#{repository}/#{GROUP_PATH}/maven-metadata.xml")
|
|
87
|
+
parse_versions(fetch("#{repository}/#{GROUP_PATH}/maven-metadata.xml"))
|
|
60
88
|
end
|
|
61
89
|
|
|
90
|
+
# The versions a maven-metadata.xml document lists.
|
|
91
|
+
def parse_versions(xml) = xml.scan(%r{<version>([^<]+)</version>}).flatten.map(&:strip)
|
|
92
|
+
|
|
62
93
|
# ------------------------------------------------------------------------------ install
|
|
63
94
|
|
|
64
95
|
# Downloads a PD4ML build (unless already there) and makes it the default one; returns the
|
|
65
|
-
# jar's path. version: 'latest' (the most recent
|
|
66
|
-
# recent 4
|
|
96
|
+
# jar's path. version: 'latest' (default_version: the most recent release from MINIMUM_VERSION
|
|
97
|
+
# on, else the most recent 4.2.x snapshot; with snapshots: true, the most recent 4.* build) or
|
|
98
|
+
# an exact one such as '4.1.2' or '4.0.23fx2'.
|
|
67
99
|
def install(version = 'latest', snapshots: false, jakarta: false, repository: REPOSITORY, home: nil,
|
|
68
100
|
force: false, quiet: false, bouncycastle: true)
|
|
69
101
|
home ||= default_home
|
|
70
|
-
|
|
102
|
+
if version == 'latest'
|
|
103
|
+
versions = available_versions(repository)
|
|
104
|
+
version = if snapshots
|
|
105
|
+
select_version(versions, snapshots: true, jakarta: jakarta)
|
|
106
|
+
else
|
|
107
|
+
default_version(versions, jakarta: jakarta)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
71
110
|
name = jar_name(version, repository)
|
|
72
111
|
jar = File.join(home, name)
|
|
73
112
|
download("#{repository}/#{GROUP_PATH}/#{version}/#{name}", jar, force, quiet)
|
data/lib/pd4ml/java.rb
CHANGED
|
@@ -42,7 +42,9 @@ module Pd4ml
|
|
|
42
42
|
|
|
43
43
|
# A Java object living in the bridge. Call its Java methods by their Java names or in Ruby
|
|
44
44
|
# style (`size.rotate`, `pd.set_page_size(...)`, `pd.demo_mode?`). Public fields: `obj[:width]`
|
|
45
|
-
# / `obj[:width] = v`. Released in Java when garbage collected.
|
|
45
|
+
# / `obj[:width] = v`. Released in Java when garbage collected. The DOM API's objects
|
|
46
|
+
# (com.pd4ml.dom) return their Java lists and sets as Ruby Arrays, maps as Hashes
|
|
47
|
+
# (`doc.query_selector_all('p').each { ... }`); `call_java` returns them as they are.
|
|
46
48
|
class JavaObject < BasicObject
|
|
47
49
|
def initialize(bridge, handle, java_class, enum_name)
|
|
48
50
|
@bridge = bridge
|
|
@@ -110,7 +112,9 @@ module Pd4ml
|
|
|
110
112
|
::Kernel.raise ::NoMethodError, "set Java fields with obj[:#{name.to_s.chomp('=')}] = value" if name.to_s.end_with?('=')
|
|
111
113
|
::Kernel.raise ::ArgumentError, 'Java methods take no block' if block
|
|
112
114
|
|
|
113
|
-
@bridge.call(self, Naming.resolve(@bridge, @java_class, name, false), args)
|
|
115
|
+
result = @bridge.call(self, Naming.resolve(@bridge, @java_class, name, false), args)
|
|
116
|
+
# the DOM API's lists, sets and maps are snapshots: Ruby Arrays and Hashes
|
|
117
|
+
@java_class.start_with?('com.pd4ml.dom.') ? Java.to_ruby(result) : result
|
|
114
118
|
end
|
|
115
119
|
|
|
116
120
|
def respond_to_missing?(_name, _include_private = false) = true
|
|
@@ -205,5 +209,21 @@ module Pd4ml
|
|
|
205
209
|
|
|
206
210
|
Bridge.get.proxy(iface, &block)
|
|
207
211
|
end
|
|
212
|
+
|
|
213
|
+
# A Java collection (List, Set...) as a Ruby Array and a Java Map as a Hash, in one or two
|
|
214
|
+
# bridge calls; any other value as it is.
|
|
215
|
+
def self.to_ruby(value)
|
|
216
|
+
return value unless value.is_a?(JavaObject) && value.java_class.start_with?('java.util.')
|
|
217
|
+
|
|
218
|
+
if value.instance_of_java?('java.util.Collection')
|
|
219
|
+
value.call_java('toArray')
|
|
220
|
+
elsif value.instance_of_java?('java.util.Map')
|
|
221
|
+
keys = value.call_java('keySet').call_java('toArray')
|
|
222
|
+
values = value.call_java('values').call_java('toArray')
|
|
223
|
+
keys.zip(values).to_h
|
|
224
|
+
else
|
|
225
|
+
value
|
|
226
|
+
end
|
|
227
|
+
end
|
|
208
228
|
end
|
|
209
229
|
end
|
data/lib/pd4ml/pd4ml-bridge.jar
CHANGED
|
Binary file
|
data/lib/pd4ml/pd4ml.rb
CHANGED
|
@@ -5,7 +5,9 @@ module Pd4ml
|
|
|
5
5
|
# engine by default. Every Java method is available by its Java name or in Ruby style
|
|
6
6
|
# (`set_page_size`, `add_style`, `use_ttf`); these take Ruby I/O: read_html (a path, URL or
|
|
7
7
|
# binary String/IO), read_html_string, write_pdf/write_docx/write_rtf (the document returned as
|
|
8
|
-
# a binary String, or written to a path or IO), render_as_images and monitor_progress_with (a block)
|
|
8
|
+
# a binary String, or written to a path or IO), render_as_images and monitor_progress_with (a block);
|
|
9
|
+
# read_eml/read_mht (e-mail messages, saved web pages) as read_html. get_document and layout (the
|
|
10
|
+
# DOM API, v42 engine) return com.pd4ml.dom objects, whose lists come back as Ruby Arrays.
|
|
9
11
|
#
|
|
10
12
|
# pd = Pd4ml::PD4ML.new
|
|
11
13
|
# pd.set_page_size(Pd4ml::PageSize::A4)
|
|
@@ -117,6 +119,24 @@ module Pd4ml
|
|
|
117
119
|
end
|
|
118
120
|
alias readHtmlString read_html_string
|
|
119
121
|
|
|
122
|
+
# Reads an e-mail message (.eml) to convert: its body, HTML if it has one, with the images it
|
|
123
|
+
# embeds, after a block with From, Date, To, Cc, Subject and the attachments, which are
|
|
124
|
+
# embedded in the PDF. `source` as for read_html: a path or URL, or the message itself (a
|
|
125
|
+
# binary String or IO). Of resources outside the message only https:// ones are loaded;
|
|
126
|
+
# add_allowed_resource_location replaces that. read_html recognizes messages too; this is the
|
|
127
|
+
# way for mail from outside your control. PD4ML 4.2 and later.
|
|
128
|
+
def read_eml(source)
|
|
129
|
+
read_mime('readEML', source)
|
|
130
|
+
end
|
|
131
|
+
alias readEML read_eml
|
|
132
|
+
|
|
133
|
+
# Reads a web page saved as a single file (.mht, .mhtml), with the images, style sheets and
|
|
134
|
+
# fonts saved in it; `source` as for read_eml. PD4ML 4.2 and later.
|
|
135
|
+
def read_mht(source)
|
|
136
|
+
read_mime('readMHT', source)
|
|
137
|
+
end
|
|
138
|
+
alias readMHT read_mht
|
|
139
|
+
|
|
120
140
|
# -------------------------------------------------------------------------------- output
|
|
121
141
|
|
|
122
142
|
# Writes the PDF: to `target` (a path or an IO), or returned as a binary String. spec: a
|
|
@@ -175,6 +195,16 @@ module Pd4ml
|
|
|
175
195
|
|
|
176
196
|
private
|
|
177
197
|
|
|
198
|
+
def read_mime(method, source)
|
|
199
|
+
arg = if location?(source)
|
|
200
|
+
Pd4ml.to_url(source)
|
|
201
|
+
else
|
|
202
|
+
source.is_a?(JavaObject) ? source : Pd4ml.binary(source)
|
|
203
|
+
end
|
|
204
|
+
Bridge.get.call(@java, method, [arg])
|
|
205
|
+
nil
|
|
206
|
+
end
|
|
207
|
+
|
|
178
208
|
def location?(source)
|
|
179
209
|
source.is_a?(Pathname) || source.is_a?(URI::Generic) ||
|
|
180
210
|
(source.is_a?(String) && source.encoding != Encoding::BINARY && !source.lstrip.start_with?('<')) ||
|
data/lib/pd4ml/version.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Pd4ml
|
|
4
|
-
VERSION = '1.
|
|
5
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pd4ml
|
|
4
|
+
VERSION = '1.1.2'
|
|
5
|
+
end
|
data/lib/pd4ml.rb
CHANGED
|
@@ -35,6 +35,19 @@ module Pd4ml
|
|
|
35
35
|
ResourceProvider = Java.type('com.pd4ml.ResourceProvider')
|
|
36
36
|
CustomTag = Java.type('com.pd4ml.CustomTag')
|
|
37
37
|
|
|
38
|
+
# The DOM API (com.pd4ml.dom, PD4ML 4.2, v42 engine; DMS and UA editions): PD4ML#get_document and
|
|
39
|
+
# PD4ML#layout return its objects. Their Java lists and sets come back as Ruby Arrays, maps as Hashes.
|
|
40
|
+
module Dom
|
|
41
|
+
Node = Java.type('com.pd4ml.dom.Node')
|
|
42
|
+
Document = Java.type('com.pd4ml.dom.Document')
|
|
43
|
+
Element = Java.type('com.pd4ml.dom.Element')
|
|
44
|
+
Text = Java.type('com.pd4ml.dom.Text')
|
|
45
|
+
Comment = Java.type('com.pd4ml.dom.Comment')
|
|
46
|
+
DocumentType = Java.type('com.pd4ml.dom.DocumentType')
|
|
47
|
+
Layout = Java.type('com.pd4ml.dom.Layout')
|
|
48
|
+
LayoutBox = Java.type('com.pd4ml.dom.LayoutBox')
|
|
49
|
+
end
|
|
50
|
+
|
|
38
51
|
FORMATS = %w[PDF PDFA PDFUA].freeze
|
|
39
52
|
|
|
40
53
|
class << self
|