pdf-service 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -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,3 @@
1
+ module PDFService
2
+ Error = Class.new(StandardError)
3
+ 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,11 @@
1
+ require 'pdf_service/web_app'
2
+
3
+ module PDFService
4
+ module Setup
5
+ def self.web_app
6
+ logger = Logger.new(STDOUT)
7
+ logger.level = Logger::INFO
8
+ PDFService::WebApp.new(pdf_service: PDFService.with_config(logger: logger))
9
+ end
10
+ end
11
+ 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,3 @@
1
+ module PDFService
2
+ VERSION = '0.0.1'.freeze
3
+ 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: []