pdf-service 0.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 +7 -0
- data/lib/pdf_service.rb +26 -0
- data/lib/pdf_service/config.rb +17 -0
- data/lib/pdf_service/error.rb +3 -0
- data/lib/pdf_service/phantom_js_renderer.rb +26 -0
- data/lib/pdf_service/phantomjs_rasterize.js +48 -0
- data/lib/pdf_service/service.rb +35 -0
- data/lib/pdf_service/setup.rb +11 -0
- data/lib/pdf_service/utils/null_logger.rb +17 -0
- data/lib/pdf_service/utils/tempfile_service.rb +16 -0
- data/lib/pdf_service/version.rb +3 -0
- data/lib/pdf_service/web_app.rb +43 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c68e174f54c08dcf6e7cb9717b0bb1f9c961016c
|
4
|
+
data.tar.gz: 6fc105d8bbce42a338d0dee58c9b23b67e7a4b53
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 535afcffe1197183dc970bae0cbb709de93ca06e07bf3dbb3975851e81a59252f7edd2cac161695d3555c13207ff1da965be3154ea999d40a2f121c6678951f6
|
7
|
+
data.tar.gz: d25c8b21e997e1ca49de05a2575e79b46d5e61cd69d9527a1c585f7d609cc8594dd6bf18b41ea6c6c5bc5d1b97ed490bdd3e6dcfea964ecf5cacc27f5d74442e
|
data/lib/pdf_service.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'shellwords'
|
3
|
+
require 'logger'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'digest/sha2'
|
6
|
+
require 'pdf_service/error'
|
7
|
+
require 'pdf_service/config'
|
8
|
+
require 'pdf_service/utils/tempfile_service'
|
9
|
+
require 'pdf_service/utils/null_logger'
|
10
|
+
require 'pdf_service/phantom_js_renderer'
|
11
|
+
require 'pdf_service/service'
|
12
|
+
|
13
|
+
|
14
|
+
module PDFService
|
15
|
+
ROOT_PATH = Pathname(__FILE__).dirname
|
16
|
+
DefaultConfiguration = Config.new
|
17
|
+
|
18
|
+
|
19
|
+
def self.with_config(config)
|
20
|
+
Service.new(Config[config])
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.render(html)
|
24
|
+
with_config(DefaultConfiguration).render(html)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PDFService
|
2
|
+
class Config
|
3
|
+
def initialize(logger: Logger.new(STDOUT), tmp_path: '.')
|
4
|
+
@logger = logger
|
5
|
+
@tmp_path = tmp_path
|
6
|
+
end
|
7
|
+
attr_reader :logger
|
8
|
+
attr_reader :tmp_path
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def [](value)
|
12
|
+
return value if value.kind_of?(self)
|
13
|
+
new(value.to_h)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
module PDFService
|
3
|
+
class PhantomJSRenderer
|
4
|
+
RASTERIZE_SCRIPT_PATH = Pathname(__FILE__).dirname.join('phantomjs_rasterize.js').to_s.freeze
|
5
|
+
|
6
|
+
def initialize(script: RASTERIZE_SCRIPT_PATH, logger: NullLogger.new, binary: 'phantomjs')
|
7
|
+
@script = script.freeze
|
8
|
+
@logger = logger
|
9
|
+
@binary = binary.freeze
|
10
|
+
Kernel.warn("Warning! PhantomJS is not available in PATH #{@binary}") if `which #{binary}`.strip.empty?
|
11
|
+
end
|
12
|
+
attr_reader :logger
|
13
|
+
|
14
|
+
def rasterize(url, output, format: 'A4')
|
15
|
+
options = [url, output, format]
|
16
|
+
phantomjs(@script, url, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def phantomjs(script_path, _, options = [])
|
21
|
+
command = Shellwords.join([@binary, script_path, *options.map(&:to_s)])
|
22
|
+
logger.debug('pdf renderer command: ' << command)
|
23
|
+
system(command) || raise(::PDFService::Error.new("could not render PDF file. phantomjs exited with exit code = 0"))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
var page = require('webpage').create(),
|
2
|
+
system = require('system'),
|
3
|
+
address, output, size;
|
4
|
+
|
5
|
+
if (system.args.length < 3 || system.args.length > 5) {
|
6
|
+
console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]');
|
7
|
+
console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
|
8
|
+
console.log(' image (png/jpg output) examples: "1920px" entire page, window width 1920px');
|
9
|
+
console.log(' "800px*600px" window, clipped to 800x600');
|
10
|
+
phantom.exit(1);
|
11
|
+
} else {
|
12
|
+
address = system.args[1];
|
13
|
+
output = system.args[2];
|
14
|
+
page.viewportSize = { width: 600, height: 600 };
|
15
|
+
if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
|
16
|
+
size = system.args[3].split('*');
|
17
|
+
page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
|
18
|
+
: { format: system.args[3], orientation: 'portrait', margin: '1cm' };
|
19
|
+
} else if (system.args.length > 3 && system.args[3].substr(-2) === "px") {
|
20
|
+
size = system.args[3].split('*');
|
21
|
+
if (size.length === 2) {
|
22
|
+
pageWidth = parseInt(size[0], 10);
|
23
|
+
pageHeight = parseInt(size[1], 10);
|
24
|
+
page.viewportSize = { width: pageWidth, height: pageHeight };
|
25
|
+
page.clipRect = { top: 0, left: 0, width: pageWidth, height: pageHeight };
|
26
|
+
} else {
|
27
|
+
console.log("size:", system.args[3]);
|
28
|
+
pageWidth = parseInt(system.args[3], 10);
|
29
|
+
pageHeight = parseInt(pageWidth * 3/4, 10); // it's as good an assumption as any
|
30
|
+
console.log ("pageHeight:",pageHeight);
|
31
|
+
page.viewportSize = { width: pageWidth, height: pageHeight };
|
32
|
+
}
|
33
|
+
}
|
34
|
+
if (system.args.length > 4) {
|
35
|
+
page.zoomFactor = system.args[4];
|
36
|
+
}
|
37
|
+
page.open(address, function (status) {
|
38
|
+
if (status !== 'success') {
|
39
|
+
console.log('Unable to load the address!');
|
40
|
+
phantom.exit();
|
41
|
+
} else {
|
42
|
+
window.setTimeout(function () {
|
43
|
+
page.render(output);
|
44
|
+
phantom.exit();
|
45
|
+
}, 200);
|
46
|
+
}
|
47
|
+
});
|
48
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module PDFService
|
2
|
+
class Service
|
3
|
+
def initialize(config)
|
4
|
+
@tempfiles = Utils::TempfileService.new(root: config.tmp_path).freeze
|
5
|
+
@renderer = PhantomJSRenderer.new(logger: config.logger).freeze
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :tempfiles
|
9
|
+
attr_reader :renderer
|
10
|
+
|
11
|
+
def render(html)
|
12
|
+
tempfiles.use(extension: 'html') { |input_file|
|
13
|
+
input_file.write(html)
|
14
|
+
render_url("file://#{input_file.expand_path.to_s.strip}")
|
15
|
+
}
|
16
|
+
rescue Error => ex
|
17
|
+
raise ex
|
18
|
+
rescue => ex
|
19
|
+
raise Error.new(ex)
|
20
|
+
end
|
21
|
+
|
22
|
+
def render_url(url)
|
23
|
+
tempfiles.use(extension: 'pdf') { |output_file|
|
24
|
+
|
25
|
+
renderer.rasterize(url, output_file.expand_path.to_s)
|
26
|
+
|
27
|
+
return output_file.read
|
28
|
+
}
|
29
|
+
rescue Error => ex
|
30
|
+
raise ex
|
31
|
+
rescue => ex
|
32
|
+
raise Error.new(ex)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module PDFService
|
4
|
+
module Utils
|
5
|
+
class NullLogger
|
6
|
+
def initialize(*args)
|
7
|
+
@level = Logger::WARN
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :level
|
11
|
+
|
12
|
+
[:warn, :info, :error, :fatal, :debug].each do |level|
|
13
|
+
define_method("#{level}") { |*args|}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module PDFService
|
2
|
+
module Utils
|
3
|
+
class TempfileService
|
4
|
+
def initialize(root: Pathname('.'))
|
5
|
+
@root = Pathname(root)
|
6
|
+
end
|
7
|
+
|
8
|
+
def use(name: SecureRandom.hex, extension: 'tmp')
|
9
|
+
path = Pathname('.').expand_path.join("#{name}.#{extension}")
|
10
|
+
yield(path)
|
11
|
+
ensure
|
12
|
+
path.unlink if path.exist?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'logger'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module PDFService
|
6
|
+
class WebApp < Sinatra::Base
|
7
|
+
def initialize(pdf_service:)
|
8
|
+
super()
|
9
|
+
@pdf_service = pdf_service.freeze
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :pdf_service
|
13
|
+
VALID_SCHEMES = %w(http https).freeze
|
14
|
+
|
15
|
+
get '/' do
|
16
|
+
url = params[:url].to_s
|
17
|
+
begin
|
18
|
+
url = URI.parse(url.to_s)
|
19
|
+
|
20
|
+
unless VALID_SCHEMES.include?(url.scheme)
|
21
|
+
return ErrorResponse("Invalid url scheme #{url.scheme}://. Valid schemes is #{VALID_SCHEMES}")
|
22
|
+
end
|
23
|
+
PDFResponse(pdf_service.render_url(url))
|
24
|
+
rescue => ex
|
25
|
+
STDERR.puts(ex.inspect)
|
26
|
+
ErrorResponse('Internal Server Error')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
post('/') {
|
32
|
+
PDFResponse(@pdf_service.render(request.body.read))
|
33
|
+
}
|
34
|
+
|
35
|
+
def ErrorResponse(message, status: 500)
|
36
|
+
[status, {'Content-Type' => 'text/plain'}, [message]]
|
37
|
+
end
|
38
|
+
|
39
|
+
def PDFResponse(pdf_data, status: 200)
|
40
|
+
[status, {'Content-Type' => 'application/pdf'}, [pdf_data]]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pdf-service
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ruby Coder
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-30 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Much longer explanation of the example!
|
14
|
+
email:
|
15
|
+
- rubycoder@example.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/pdf_service.rb
|
21
|
+
- lib/pdf_service/config.rb
|
22
|
+
- lib/pdf_service/error.rb
|
23
|
+
- lib/pdf_service/phantom_js_renderer.rb
|
24
|
+
- lib/pdf_service/phantomjs_rasterize.js
|
25
|
+
- lib/pdf_service/service.rb
|
26
|
+
- lib/pdf_service/setup.rb
|
27
|
+
- lib/pdf_service/utils/null_logger.rb
|
28
|
+
- lib/pdf_service/utils/tempfile_service.rb
|
29
|
+
- lib/pdf_service/version.rb
|
30
|
+
- lib/pdf_service/web_app.rb
|
31
|
+
homepage:
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.4.5.1
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Small service the uses PhantomJS to turn HTML into PDF
|
55
|
+
test_files: []
|