pdf-service 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|