Converting E-mail Messages and Saved Web Pages
PD4ML reads e-mail messages (.eml) and web pages saved as a single file (.mht, .mhtml) as well as HTML, and writes them to any output format.
PD4ML pd4ml = new PD4ML();
pd4ml.readHTML(new File("message.eml").toURI().toURL()); // or page.mht
pd4ml.writePDF(new FileOutputStream("message.pdf"));
readHTML(...) recognizes these files, from a URL or a stream: by the .eml, .mht or .mhtml ending of the URL path, and otherwise by how the content starts. Input counts as a message or a saved page if all its lines up to the first empty one are headers, with Content-Type or MIME-Version among them, or From together with Date, Received or Message-ID. A saved page is told from a message by Snapshot-Content-Location, by a From with "Saved by", or by multipart/related with no From and To.
If you know what the input is, say so; this is the way to go for mail from outside your control:
pd4ml.readEML(inputStream); // or readEML(url)
pd4ml.readMHT(inputStream); // or readMHT(url)
Recognition is a judgment on the first lines of the input. A file made to escape it is read as ordinary HTML, without the protection described below.
Pd4Cmd takes the same files as its source:
java -Djava.awt.headless=true -jar pd4ml.jar "file:message.eml" 800 A4 -out message.pdf
Available from PD4ML v4.2.0, with both layout engines.
What ends up in the document
E-mail message
- A block with From, Date, To, Cc and Subject, and the names and sizes of the attachments. Bcc is left out.
- The body: the HTML version if the message has one, otherwise the plain text. Parts of a
multipart/mixedmessage follow each other. - Images the message embeds (
cid:references, in<img>and in CSS), and images it carries as inline parts. - A forwarded message attached to it (
message/rfc822), after the body, with its own block.
Saved web page
- The page, with the images, style sheets and fonts saved in the file, found by
Content-IDorContent-Location, also from inside the style sheets. There is no block of headers. - Links that were relative on the original page lead to where they led there, as the page's original
httporhttpslocation becomes its base.
Subject, or the page's own title if there is none, becomes the title of the document. The charset of each part, encoded headers (RFC 2047, RFC 2231), base64, quoted-printable and format=flowed text are handled, and so is damaged input: bare line feeds, a missing closing boundary, a mislabeled charset.
The attachments are embedded
The files attached to a message are embedded in the PDF, each next to its name in the block at the top of the document. An attachment comes from the sender, and the PDF hands it on to whoever opens it, executables included.
Signature parts (smime.p7s) are not attachments and are left out; images the body displays are not attachments either.
Only PDF has embedded files, and PDF/A-1 and PDF/A-2 do not allow them: these and the other outputs keep the list of names.
A message cannot embed anything else by itself: <pd4ml:attachment> and <pd4ml:include> tags in its HTML are dropped.
Resources outside of the file
Of the images, style sheets and fonts a file refers to but does not contain, only those at https:// locations are loaded. Local files (<img src="file:/...">), relative references that the file does not resolve itself and http:// locations are not.
A remote image in a message tells its sender when and where the message was opened. To change what loads, list the locations with addAllowedResourceLocation(...), which replaces this default. As for any document read from a URL, the location of the file itself must then be on the list, unless you read it from a stream:
pd4ml.addAllowedResourceLocation("https://cdn.example.com/"); // only this server
pd4ml.readEML(new FileInputStream("message.eml"));
With Pd4Cmd, use -param pd4ml.allowed.resource.location "https://cdn.example.com/".
The list covers everything else PD4ML loads during the conversion too, such as an image in a header you set with setPageHeader(...): allow its location as well, or embed the image as a data: URI. Fonts from useTTF() are yours and are not affected. See Restricting Resource Loading for Untrusted HTML.
Limitations
- Frames: the content of
<iframe>and<frame>elements is not shown, as with HTML input. - Scripts do not run: the page shows as it was saved, without what its scripts would have changed.
- Word page headers and footers: a page saved by Word keeps its body; the headers and footers Word stores in parts of their own are not shown.
- Encrypted messages: the content of an S/MIME or PGP encrypted message is not decrypted, so there is no body to show.
- Fonts: text in scripts the built-in fonts do not cover needs
useTTF(...), as with HTML input.
