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 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: []