libis-format 1.3.8.1 → 2.0.0
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/.coveralls.yml +2 -0
- data/.gitignore +20 -0
- data/.travis.yml +70 -0
- data/Gemfile +0 -12
- data/README.md +2 -2
- data/Rakefile +8 -0
- data/base/Dockerfile +35 -0
- data/base/Dockerfile.alpine +20 -0
- data/base/Dockerfile.rvm +56 -0
- data/base/rework_path +20 -0
- data/bin/{pdf_tool → pdf_copy} +2 -3
- data/data/PDFA_def.ps +3 -3
- data/data/eciRGB_v2.icc +0 -0
- data/data/types.yml +4 -17
- data/docker_cfg.yml +1 -0
- data/lib/libis/format/cli/convert.rb +4 -4
- data/lib/libis/format/cli/prompt_helper.rb +24 -32
- data/lib/libis/format/command_line.rb +3 -2
- data/lib/libis/format/config.rb +23 -19
- data/lib/libis/format/converter/audio_converter.rb +31 -56
- data/lib/libis/format/converter/base.rb +36 -16
- data/lib/libis/format/converter/chain.rb +32 -62
- data/lib/libis/format/converter/fop_pdf_converter.rb +8 -4
- data/lib/libis/format/converter/image_assembler.rb +82 -0
- data/lib/libis/format/converter/image_converter.rb +45 -248
- data/lib/libis/format/converter/image_splitter.rb +80 -0
- data/lib/libis/format/converter/image_watermarker.rb +261 -0
- data/lib/libis/format/converter/jp2_converter.rb +38 -36
- data/lib/libis/format/converter/office_converter.rb +28 -22
- data/lib/libis/format/converter/pdf_assembler.rb +66 -0
- data/lib/libis/format/converter/pdf_converter.rb +52 -200
- data/lib/libis/format/converter/pdf_optimizer.rb +70 -0
- data/lib/libis/format/converter/pdf_splitter.rb +65 -0
- data/lib/libis/format/converter/pdf_watermarker.rb +110 -0
- data/lib/libis/format/converter/repository.rb +13 -7
- data/lib/libis/format/converter/spreadsheet_converter.rb +16 -10
- data/lib/libis/format/converter/video_converter.rb +73 -109
- data/lib/libis/format/converter/xslt_converter.rb +11 -13
- data/lib/libis/format/converter.rb +1 -1
- data/lib/libis/format/identifier.rb +46 -44
- data/lib/libis/format/info.rb +27 -0
- data/lib/libis/format/library.rb +147 -0
- data/lib/libis/format/tool/droid.rb +30 -29
- data/lib/libis/format/tool/extension_identification.rb +26 -24
- data/lib/libis/format/tool/{ff_mpeg.rb → ffmpeg.rb} +10 -17
- data/lib/libis/format/tool/fido.rb +27 -22
- data/lib/libis/format/tool/file_tool.rb +24 -11
- data/lib/libis/format/tool/fop_pdf.rb +14 -25
- data/lib/libis/format/tool/identification_tool.rb +40 -38
- data/lib/libis/format/tool/office_to_pdf.rb +18 -30
- data/lib/libis/format/tool/pdf_copy.rb +47 -0
- data/lib/libis/format/tool/pdf_merge.rb +19 -25
- data/lib/libis/format/tool/pdf_optimizer.rb +19 -22
- data/lib/libis/format/tool/pdf_split.rb +33 -6
- data/lib/libis/format/tool/pdf_to_pdfa.rb +31 -45
- data/lib/libis/format/tool/pdfa_validator.rb +30 -24
- data/lib/libis/format/tool/spreadsheet_to_ods.rb +18 -29
- data/lib/libis/format/tool.rb +3 -4
- data/lib/libis/format/version.rb +1 -3
- data/lib/libis/format/yaml_loader.rb +71 -0
- data/lib/libis/format.rb +7 -5
- data/lib/libis-format.rb +0 -2
- data/libis-format.gemspec +19 -29
- data/tools/PdfTool.jar +0 -0
- data/tools/pdfbox/pdfbox-app-2.0.13.jar +0 -0
- data/tools/pdfbox/{preflight-app-3.0.3.jar → preflight-app-2.0.13.jar} +0 -0
- metadata +83 -152
- data/data/AdobeRGB1998.icc +0 -0
- data/lib/libis/format/converter/email_converter.rb +0 -51
- data/lib/libis/format/tool/eml_to_pdf.rb +0 -168
- data/lib/libis/format/tool/mail_to_pdf.rb +0 -447
- data/lib/libis/format/tool/msg_to_pdf.rb +0 -154
- data/lib/libis/format/tool/pdf_tool.rb +0 -52
- data/lib/libis/format/type_database.rb +0 -156
- data/lib/libis/format/type_database_impl.rb +0 -153
- data/tools/pdf2pdfa +0 -395
- data/tools/pdfbox/pdfbox-app-3.0.3.jar +0 -0
- /data/bin/{droid_tool → droid} +0 -0
- /data/bin/{fido_tool → fido} +0 -0
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
# rubocop:disable Style/Documentation, Metrics/*
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require 'base64'
|
|
5
|
-
require 'cgi'
|
|
6
|
-
require 'pdfkit'
|
|
7
|
-
require 'time'
|
|
8
|
-
require 'fileutils'
|
|
9
|
-
require 'pathname'
|
|
10
|
-
require 'libis/format/config'
|
|
11
|
-
|
|
12
|
-
module Libis
|
|
13
|
-
module Format
|
|
14
|
-
module Tool
|
|
15
|
-
HEADER_STYLE = <<~HTML
|
|
16
|
-
<style>
|
|
17
|
-
.header-table {
|
|
18
|
-
margin: 0 0 10px 0;
|
|
19
|
-
padding: 0;
|
|
20
|
-
font-family: Arial, Helvetica, sans-serif;
|
|
21
|
-
}
|
|
22
|
-
.header-table table {
|
|
23
|
-
width: 100%;
|
|
24
|
-
}
|
|
25
|
-
.header-name {
|
|
26
|
-
padding-right: 5px;
|
|
27
|
-
color: #9E9E9E;
|
|
28
|
-
text-align: right;
|
|
29
|
-
vertical-align: top;
|
|
30
|
-
font-size: 12px;
|
|
31
|
-
}
|
|
32
|
-
.header-value {
|
|
33
|
-
font-size: 12px;
|
|
34
|
-
width: 99%;
|
|
35
|
-
}
|
|
36
|
-
.header_fields {
|
|
37
|
-
background: white;
|
|
38
|
-
margin: 0;
|
|
39
|
-
border: 1px solid #DDD;
|
|
40
|
-
border-radius: 3px;
|
|
41
|
-
padding: 8px;
|
|
42
|
-
box-sizing: border-box;
|
|
43
|
-
}
|
|
44
|
-
</style>
|
|
45
|
-
HTML
|
|
46
|
-
|
|
47
|
-
HEADER_TABLE_TEMPLATE = <<~HTML
|
|
48
|
-
<div class="header-table">
|
|
49
|
-
<table class="header_fields">
|
|
50
|
-
<tbody>
|
|
51
|
-
%s
|
|
52
|
-
</tbody>
|
|
53
|
-
</table>
|
|
54
|
-
</div>
|
|
55
|
-
HTML
|
|
56
|
-
|
|
57
|
-
HEADER_FIELD_TEMPLATE = <<~HTML
|
|
58
|
-
<tr>
|
|
59
|
-
<td class="header-name">%s</td>
|
|
60
|
-
<td class="header-value">%s</td>
|
|
61
|
-
</tr>
|
|
62
|
-
HTML
|
|
63
|
-
|
|
64
|
-
HTML_WRAPPER_TEMPLATE = <<~HTML
|
|
65
|
-
<!DOCTYPE html>
|
|
66
|
-
<html>
|
|
67
|
-
<head>
|
|
68
|
-
<style>
|
|
69
|
-
body {
|
|
70
|
-
font-size: 12px;
|
|
71
|
-
}
|
|
72
|
-
</style>
|
|
73
|
-
<title>title</title>
|
|
74
|
-
</head>
|
|
75
|
-
<body>
|
|
76
|
-
<pre>
|
|
77
|
-
%s
|
|
78
|
-
</pre>
|
|
79
|
-
</body>
|
|
80
|
-
</html>
|
|
81
|
-
HTML
|
|
82
|
-
|
|
83
|
-
HTML_BODY_TEMPLATE = <<~HTML
|
|
84
|
-
<!DOCTYPE html>
|
|
85
|
-
<html>
|
|
86
|
-
<head>
|
|
87
|
-
<style>
|
|
88
|
-
body {
|
|
89
|
-
font-size: 12px;
|
|
90
|
-
}
|
|
91
|
-
</style>
|
|
92
|
-
<title>title</title>
|
|
93
|
-
</head>
|
|
94
|
-
<body>
|
|
95
|
-
%s
|
|
96
|
-
</body>
|
|
97
|
-
</html>
|
|
98
|
-
HTML
|
|
99
|
-
|
|
100
|
-
ATTACHMENT_STYLE = <<~HTML
|
|
101
|
-
<style>
|
|
102
|
-
.attachment-list {
|
|
103
|
-
border: 1px solid #DDD;
|
|
104
|
-
margin: 0 0 10px 0;
|
|
105
|
-
padding: 0;
|
|
106
|
-
font-family: Arial, Helvetica, sans-serif;
|
|
107
|
-
}
|
|
108
|
-
.attachment-list ul {
|
|
109
|
-
list-style: disclosure-closed;
|
|
110
|
-
}
|
|
111
|
-
.attachment-list li {
|
|
112
|
-
font-size: 12px;
|
|
113
|
-
padding-left: 1em;
|
|
114
|
-
}
|
|
115
|
-
</style>
|
|
116
|
-
HTML
|
|
117
|
-
|
|
118
|
-
ATTACHMENT_LIST_TEMPLATE = <<~HTML
|
|
119
|
-
<div class="attachment-list">
|
|
120
|
-
<ul>
|
|
121
|
-
%s
|
|
122
|
-
</ul>
|
|
123
|
-
</div>
|
|
124
|
-
HTML
|
|
125
|
-
|
|
126
|
-
HTML_DOCTYPE_TEMPLATE = '<!DOCTYPE html>%s'
|
|
127
|
-
ATTACHMENT_ITEM_TEMPLATE = '<li>%s</li>'
|
|
128
|
-
|
|
129
|
-
IMG_CID_PLAIN_REGEX = /\[cid:(.*?)\]/im
|
|
130
|
-
IMG_CID_HTML_REGEX = /cid:([^"]*)/im
|
|
131
|
-
|
|
132
|
-
class MailToPdf
|
|
133
|
-
include ::Libis::Tools::Logger
|
|
134
|
-
|
|
135
|
-
def self.installed?
|
|
136
|
-
File.exist?(Libis::Format::Config[:wkhtmltopdf])
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def self.run(source, target, **options)
|
|
140
|
-
new.run source, target, **options
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def run(source, target, **options)
|
|
144
|
-
# Preliminary checks
|
|
145
|
-
@warnings = []
|
|
146
|
-
|
|
147
|
-
# PDF creation options
|
|
148
|
-
@pdf_options = {
|
|
149
|
-
page_size: 'A4',
|
|
150
|
-
margin_top: '10mm',
|
|
151
|
-
margin_bottom: '10mm',
|
|
152
|
-
margin_left: '10mm',
|
|
153
|
-
margin_right: '10mm',
|
|
154
|
-
# image_quality: 100,
|
|
155
|
-
# viewport_size: '2480x3508',
|
|
156
|
-
dpi: 300
|
|
157
|
-
}.merge options
|
|
158
|
-
|
|
159
|
-
# Check if source file exists
|
|
160
|
-
raise "File #{source} does not exist" unless File.exist?(source)
|
|
161
|
-
|
|
162
|
-
# Open the email
|
|
163
|
-
email = open_email(source)
|
|
164
|
-
|
|
165
|
-
# Convert the email message to PDF
|
|
166
|
-
result = email_to_pdf(email, target, root_msg: true)
|
|
167
|
-
|
|
168
|
-
# Close email message
|
|
169
|
-
close_email(email)
|
|
170
|
-
|
|
171
|
-
result
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
protected
|
|
175
|
-
|
|
176
|
-
def email_to_pdf(msg, target, root_msg: false)
|
|
177
|
-
# Make sure the target directory exists
|
|
178
|
-
outdir = File.dirname(target)
|
|
179
|
-
FileUtils.mkdir_p(outdir)
|
|
180
|
-
|
|
181
|
-
# Process the message body
|
|
182
|
-
# ------------------------
|
|
183
|
-
body = get_body(msg)
|
|
184
|
-
|
|
185
|
-
# Process headers
|
|
186
|
-
# ---------------
|
|
187
|
-
headers, headers_html = get_headers(msg)
|
|
188
|
-
|
|
189
|
-
# Add header section to the HTML body
|
|
190
|
-
body = add_headers_to_body(body, headers_html)
|
|
191
|
-
|
|
192
|
-
# Embed inline images
|
|
193
|
-
# -------------------
|
|
194
|
-
attachments = msg.attachments
|
|
195
|
-
used_files = embed_inline_attachments(body, attachments)
|
|
196
|
-
|
|
197
|
-
# Save other attachments
|
|
198
|
-
# ----------------------
|
|
199
|
-
attachments_dir = "#{target}.attachments"
|
|
200
|
-
|
|
201
|
-
files = save_attachments(attachments, attachments_dir, used_files)
|
|
202
|
-
|
|
203
|
-
# Add attachment section to the HTML body
|
|
204
|
-
body = add_attachments_to_body(body, files, attachments_dir)
|
|
205
|
-
|
|
206
|
-
# Save the HTML body as a .html file next to the PDF for debugging purposes
|
|
207
|
-
File.open("#{target}.html", 'wb') { |f| f.write(body) }
|
|
208
|
-
|
|
209
|
-
# Create PDF
|
|
210
|
-
# ----------
|
|
211
|
-
write_target_file(body, get_subject(msg), target)
|
|
212
|
-
|
|
213
|
-
files = [target] + files if File.exist?(target)
|
|
214
|
-
|
|
215
|
-
if root_msg
|
|
216
|
-
p = Pathname(File.dirname(files.first))
|
|
217
|
-
files.drop(1).each do |f|
|
|
218
|
-
(headers[:attachments] ||= []) << Pathname.new(f).relative_path_from(p).to_s
|
|
219
|
-
end
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
{
|
|
223
|
-
command: { status: 0 },
|
|
224
|
-
files:,
|
|
225
|
-
headers:,
|
|
226
|
-
warnings: @warnings
|
|
227
|
-
}
|
|
228
|
-
rescue StandardError => e
|
|
229
|
-
raise unless root_msg
|
|
230
|
-
|
|
231
|
-
close_email(msg) if msg
|
|
232
|
-
{
|
|
233
|
-
command: { status: -1 },
|
|
234
|
-
files: [],
|
|
235
|
-
headers: {},
|
|
236
|
-
errors: [
|
|
237
|
-
{
|
|
238
|
-
error: e.message,
|
|
239
|
-
error_class: e.class.name,
|
|
240
|
-
error_trace: e.backtrace
|
|
241
|
-
}
|
|
242
|
-
],
|
|
243
|
-
warnings: @warnings
|
|
244
|
-
}
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
def get_body(msg)
|
|
248
|
-
body = get_body_html(msg)
|
|
249
|
-
|
|
250
|
-
body = HTML_BODY_TEMPLATE % body unless /<body[^>]*>/i.match?(body)
|
|
251
|
-
body = HTML_DOCTYPE_TEMPLATE % body unless /<!DOCTYPE html/i.match?(body)
|
|
252
|
-
body.sub!(%r{<title>title</title>}, "<title>#{get_subject(msg)}</title>")
|
|
253
|
-
|
|
254
|
-
body
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def add_headers_to_body(body, headers_html)
|
|
258
|
-
encoding = body.encoding
|
|
259
|
-
return body if headers_html.empty?
|
|
260
|
-
|
|
261
|
-
b = body.downcase
|
|
262
|
-
|
|
263
|
-
# Insert header block styles
|
|
264
|
-
if b.include?('</head>')
|
|
265
|
-
# if head exists, append the style block
|
|
266
|
-
body.gsub!(%r{</head>}i, "#{HEADER_STYLE}</head>")
|
|
267
|
-
elsif b.include?('<head/>')
|
|
268
|
-
# empty head, replace with the style block
|
|
269
|
-
body.gsub!(%r{<head/>}i, "<head>#{HEADER_STYLE}</head>")
|
|
270
|
-
else
|
|
271
|
-
# otherwise insert a head section before the body tag
|
|
272
|
-
body.gsub!(/<body/i, "<head>#{HEADER_STYLE}</head><body")
|
|
273
|
-
end
|
|
274
|
-
# Add the headers html table as first element in the body section
|
|
275
|
-
body.gsub!(/<body[^>]*>/i) { |m| "#{m}#{HEADER_TABLE_TEMPLATE % headers_html.encode(encoding)}" }
|
|
276
|
-
body
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
def hdr_html(key, value)
|
|
280
|
-
if key.is_a?(String) && value.is_a?(String) && !value.empty?
|
|
281
|
-
return format(HEADER_FIELD_TEMPLATE, key,
|
|
282
|
-
CGI.escapeHTML(value))
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
''
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
def embed_inline_attachments(body, attachments)
|
|
289
|
-
used_files = []
|
|
290
|
-
|
|
291
|
-
# First process plaintext cid entries
|
|
292
|
-
body.gsub!(IMG_CID_PLAIN_REGEX) do |_match|
|
|
293
|
-
data = get_inline_attachment_data(attachments, ::Regexp.last_match(1))
|
|
294
|
-
if data
|
|
295
|
-
used_files << ::Regexp.last_match(1)
|
|
296
|
-
"<img src=\"data:#{data[:mime_type]};base64,#{data[:base64]}\"/>"
|
|
297
|
-
else
|
|
298
|
-
'<img src=""/>'
|
|
299
|
-
end
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
# Then process HTML img tags with CID entries
|
|
303
|
-
body.gsub!(IMG_CID_HTML_REGEX) do |_match|
|
|
304
|
-
data = get_inline_attachment_data(attachments, ::Regexp.last_match(1))
|
|
305
|
-
if data
|
|
306
|
-
used_files << ::Regexp.last_match(1)
|
|
307
|
-
"data:#{data[:mime_type]};base64,#{data[:base64]}"
|
|
308
|
-
else
|
|
309
|
-
''
|
|
310
|
-
end
|
|
311
|
-
end
|
|
312
|
-
|
|
313
|
-
used_files
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
def save_attachments(attachments, outdir, used_files)
|
|
317
|
-
files = []
|
|
318
|
-
|
|
319
|
-
digits = ((attachments.count + 1) / 10) + 1
|
|
320
|
-
i = 1
|
|
321
|
-
|
|
322
|
-
get_attachments(attachments, used_files).each do |attachment|
|
|
323
|
-
prefix = "#{format('%0*d', digits, i)}-"
|
|
324
|
-
|
|
325
|
-
info = get_attachment_info(attachment)
|
|
326
|
-
|
|
327
|
-
if info[:embedded_msg]
|
|
328
|
-
sub_msg = info[:embedded_msg]
|
|
329
|
-
file = File.join(outdir, "#{prefix}#{info[:filename].tr('/', '_')}.msg.pdf")
|
|
330
|
-
|
|
331
|
-
result = email_to_pdf(sub_msg, file, root_msg: false)
|
|
332
|
-
|
|
333
|
-
if (e = result[:error])
|
|
334
|
-
raise e
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
files += result[:files]
|
|
338
|
-
elsif info[:data]
|
|
339
|
-
file = File.join(outdir, "#{prefix}#{info[:filename].tr('/', '_')}")
|
|
340
|
-
FileUtils.mkdir_p(File.dirname(file))
|
|
341
|
-
File.open(file, 'wb') { |f| f.write(info[:data]) }
|
|
342
|
-
files << file
|
|
343
|
-
else
|
|
344
|
-
@warnings << "Attachment #{info[:filename]} cannot be extracted"
|
|
345
|
-
next
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
i += 1
|
|
349
|
-
end
|
|
350
|
-
files
|
|
351
|
-
end
|
|
352
|
-
|
|
353
|
-
def add_attachments_to_body(body, files, attachments_dir)
|
|
354
|
-
return body if files.empty?
|
|
355
|
-
|
|
356
|
-
b = body.downcase
|
|
357
|
-
|
|
358
|
-
# Insert attachment block styles
|
|
359
|
-
if b.include?('</head>')
|
|
360
|
-
# if head exists, append the style block
|
|
361
|
-
body.gsub!(%r{</head>}i, "#{ATTACHMENT_STYLE}</head>")
|
|
362
|
-
elsif b.include?('<head/>')
|
|
363
|
-
# empty head, replace with the style block
|
|
364
|
-
body.gsub!(%r{<head/>}i, "<head>#{ATTACHMENT_STYLE}</head>")
|
|
365
|
-
else
|
|
366
|
-
# otherwise insert a head section before the body tag
|
|
367
|
-
body.gsub!(/<body/i, "<head>#{ATTACHMENT_STYLE}</head><body")
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
# Filter files to only include those that are in the attachments directory
|
|
371
|
-
# and map them to relative paths from the attachments directory
|
|
372
|
-
items = files.filter_map do |f|
|
|
373
|
-
Pathname.new(f).relative_path_from(Pathname.new(attachments_dir)).to_s if File.dirname(f) == attachments_dir
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
# Create the attachment item HTML
|
|
377
|
-
items = items.map do |f|
|
|
378
|
-
format(ATTACHMENT_ITEM_TEMPLATE, f, File.basename(f))
|
|
379
|
-
end.join("\n")
|
|
380
|
-
|
|
381
|
-
# Create the attachment list HTML
|
|
382
|
-
attachments_html = ATTACHMENT_LIST_TEMPLATE % items
|
|
383
|
-
|
|
384
|
-
# make sure the attachments_html is encoded in the same encoding as the body
|
|
385
|
-
attachments_html = attachments_html.encode(body.encoding)
|
|
386
|
-
|
|
387
|
-
# Add the attachments html list after the headers
|
|
388
|
-
# if there are no headers, then add it at the beginning of the body section
|
|
389
|
-
body.sub!(%r{<div class="header-table">.*?</div>}im) { |m| "#{m}#{attachments_html}" } ||
|
|
390
|
-
body.gsub!(/<body[^>]*>/i) { |m| "#{m}#{attachments_html}" }
|
|
391
|
-
|
|
392
|
-
body
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
def get_attachments(attachments, used_files)
|
|
396
|
-
get_file_attachments(attachments, used_files) + get_mail_attachments(attachments)
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
def write_target_file(body, title, target)
|
|
400
|
-
kit = PDFKit.new(body, title: title || 'message', **@pdf_options)
|
|
401
|
-
pdf = kit.to_pdf
|
|
402
|
-
File.open(target, 'wb') { |f| f.write(pdf) }
|
|
403
|
-
end
|
|
404
|
-
|
|
405
|
-
# ---------------------------------------
|
|
406
|
-
# Methods to be implemented by subclasses
|
|
407
|
-
# ---------------------------------------
|
|
408
|
-
def open_email(_source)
|
|
409
|
-
raise NotImplementedError, 'Subclasses must implement the open_email method'
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
def close_email(_email)
|
|
413
|
-
raise NotImplementedError, 'Subclasses must implement the close_email method'
|
|
414
|
-
end
|
|
415
|
-
|
|
416
|
-
def get_body_html(_msg)
|
|
417
|
-
raise NotImplementedError, 'Subclasses must implement the get_body_html method'
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
def get_subject(_msg)
|
|
421
|
-
raise NotImplementedError, 'Subclasses must implement the get_subject method'
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
def get_headers(_msg)
|
|
425
|
-
raise NotImplementedError, 'Subclasses must implement the get_headers method'
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
def get_inline_attachment_data(_attachments, _cid)
|
|
429
|
-
raise NotImplementedError, 'Subclasses must implement the get_inline_attachment_data method'
|
|
430
|
-
end
|
|
431
|
-
|
|
432
|
-
def get_file_attachments(_attachments, _used_files)
|
|
433
|
-
raise NotImplementedError, 'Subclasses must implement the get_file_attachments method'
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
def get_mail_attachments(_attachments)
|
|
437
|
-
raise NotImplementedError, 'Subclasses must implement the get_mail_attachments method'
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
def get_attachment_info(_attachment)
|
|
441
|
-
raise NotImplementedError, 'Subclasses must implement the get_attachment_info method'
|
|
442
|
-
end
|
|
443
|
-
end
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
end
|
|
447
|
-
# rubocop:enable Style/Documentation, Metrics/*
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# rubocop:disable Style/Documentation, Metrics/*
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require_relative 'mail_to_pdf'
|
|
5
|
-
|
|
6
|
-
require 'msg_extractor'
|
|
7
|
-
require 'word_wrap'
|
|
8
|
-
|
|
9
|
-
module Libis
|
|
10
|
-
module Format
|
|
11
|
-
module Tool
|
|
12
|
-
class MsgToPdf < MailToPdf
|
|
13
|
-
protected
|
|
14
|
-
|
|
15
|
-
def open_email(source)
|
|
16
|
-
msg = nil
|
|
17
|
-
|
|
18
|
-
# Open the message
|
|
19
|
-
msg = MsgExtractor.open(source)
|
|
20
|
-
|
|
21
|
-
unless msg.is_a?(MsgExtractor::Message)
|
|
22
|
-
raise "File #{File.basename(source)} is not an Outlook message but a #{msg.class.name}"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
msg
|
|
26
|
-
rescue StandardError => e
|
|
27
|
-
raise "Failed to open message: #{e.message}"
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def close_email(_msg)
|
|
31
|
-
true
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def get_body_html(msg)
|
|
35
|
-
# Get the body of the message in HTML
|
|
36
|
-
body = msg.html_body
|
|
37
|
-
|
|
38
|
-
# Embed plain body in HTML as a fallback
|
|
39
|
-
body ||= HTML_WRAPPER_TEMPLATE % WordWrap.ww(msg.body, 120, false)
|
|
40
|
-
|
|
41
|
-
# Worst case, just create empty body
|
|
42
|
-
body ||= HTML_WRAPPER_TEMPLATE % ''
|
|
43
|
-
|
|
44
|
-
# # Check and fix the character encoding
|
|
45
|
-
# begin
|
|
46
|
-
# # Try to encode into UTF-8
|
|
47
|
-
# body.encode!('UTF-8', universal_newline: true)
|
|
48
|
-
# rescue Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError
|
|
49
|
-
# begin
|
|
50
|
-
# # If it fails, the text may be in Windows' Latin1 (ISO-8859-1)
|
|
51
|
-
# body.force_encoding('ISO-8859-1').encode!('UTF-8', universal_newline: true)
|
|
52
|
-
# rescue Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError => e
|
|
53
|
-
# # If that fails too, log a warning and replace the invalid/unknown with a ? character.
|
|
54
|
-
# @warnings << "#{e.class}: #{e.message}"
|
|
55
|
-
# body.encode!('UTF-8', universal_newline: true, invalid: :replace, undef: :replace)
|
|
56
|
-
# end
|
|
57
|
-
# end
|
|
58
|
-
|
|
59
|
-
body
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def get_subject(msg)
|
|
63
|
-
msg.subject || ''
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def get_headers(msg)
|
|
67
|
-
headers = {}
|
|
68
|
-
html = ''
|
|
69
|
-
|
|
70
|
-
%w[From To Cc Subject Date].each do |key|
|
|
71
|
-
value = find_hdr(msg, key)
|
|
72
|
-
next unless value
|
|
73
|
-
|
|
74
|
-
if key.casecmp('Date').zero? && value.is_a?(Time)
|
|
75
|
-
headers[key.downcase.to_sym] = value.iso8601
|
|
76
|
-
html += hdr_html(key, value.rfc2822)
|
|
77
|
-
else
|
|
78
|
-
headers[key.downcase.to_sym] = value
|
|
79
|
-
html += hdr_html(key, value)
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
[headers, html]
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def get_inline_attachment_data(attachments, cid)
|
|
87
|
-
attachments.each do |attachment|
|
|
88
|
-
next unless attachment.content_id == cid
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
mime_type: attachment.mime_type,
|
|
92
|
-
base64: Base64.encode64(attachment.data).gsub(/[\r\n]/, '')
|
|
93
|
-
}
|
|
94
|
-
end
|
|
95
|
-
nil
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def get_file_attachments(attachments, _used_files)
|
|
99
|
-
attachments.select do |attachment|
|
|
100
|
-
!attachment.content_id && !attachment.embedded_message? && attachment.filename
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def get_mail_attachments(attachments)
|
|
105
|
-
attachments.select(&:embedded_message?)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def get_attachment_info(attachment)
|
|
109
|
-
if attachment.embedded_message?
|
|
110
|
-
|
|
111
|
-
{
|
|
112
|
-
embedded_msg: attachment.data,
|
|
113
|
-
filename: attachment.message&.subject || 'email'
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
elsif attachment.filename
|
|
117
|
-
|
|
118
|
-
{
|
|
119
|
-
data: attachment.data,
|
|
120
|
-
filename: attachment.filename
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
else
|
|
124
|
-
{
|
|
125
|
-
filename: attachment.mime_type || 'unknown'
|
|
126
|
-
}
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
private
|
|
131
|
-
|
|
132
|
-
def find_hdr(msg, key)
|
|
133
|
-
value = if key.casecmp('From').zero?
|
|
134
|
-
msg.sender
|
|
135
|
-
else
|
|
136
|
-
msg.send(key.downcase.to_sym)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
if value.is_a?(Array)
|
|
140
|
-
value.compact.empty? ? nil : value.compact.map(&:to_s).join(', ')
|
|
141
|
-
|
|
142
|
-
elsif value.is_a?(Time)
|
|
143
|
-
value.localtime
|
|
144
|
-
|
|
145
|
-
else
|
|
146
|
-
value.to_s
|
|
147
|
-
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
# rubocop:enable Style/Documentation, Metrics/*
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'os'
|
|
4
|
-
|
|
5
|
-
require 'libis/tools/extend/string'
|
|
6
|
-
require 'libis/tools/logger'
|
|
7
|
-
require 'libis/tools/command'
|
|
8
|
-
|
|
9
|
-
require 'libis/format/config'
|
|
10
|
-
|
|
11
|
-
module Libis
|
|
12
|
-
module Format
|
|
13
|
-
module Tool
|
|
14
|
-
class PdfTool
|
|
15
|
-
include ::Libis::Tools::Logger
|
|
16
|
-
|
|
17
|
-
def self.installed?
|
|
18
|
-
result = Libis::Tools::Command.run(Libis::Format::Config[:java_cmd], '-version')
|
|
19
|
-
return false unless (result[:status]).zero?
|
|
20
|
-
|
|
21
|
-
File.exist?(Libis::Format::Config[:pdf_tool])
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def self.run(command, source, target, *options)
|
|
25
|
-
new.run command, source, target, *options
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def run(command, source, target, *options)
|
|
29
|
-
if OS.java?
|
|
30
|
-
# TODO: import library and execute in current VM. For now do exactly as in MRI.
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
timeout = Libis::Format::Config[:timeouts][:pdf_tool]
|
|
34
|
-
args = [
|
|
35
|
-
Libis::Format::Config[:java_cmd],
|
|
36
|
-
'-jar', Libis::Format::Config[:pdf_tool],
|
|
37
|
-
[command],
|
|
38
|
-
'-i', source,
|
|
39
|
-
'-o', target,
|
|
40
|
-
options,
|
|
41
|
-
].flatten
|
|
42
|
-
|
|
43
|
-
result = Libis::Tools::Command.run(*args, timeout: , kill_after: timeout * 2)
|
|
44
|
-
|
|
45
|
-
result[:err] << "#{self.class} took too long (> #{timeout} seconds) to complete" if result[:timeout]
|
|
46
|
-
|
|
47
|
-
result
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|