flyingsaucer4r 0.7
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/.gemtest +0 -0
- data/History.txt +13 -0
- data/Manifest.txt +10 -0
- data/README.txt +38 -0
- data/Rakefile +25 -0
- data/lib/flying-saucer-core-9.0.2.jar +0 -0
- data/lib/flying-saucer-pdf-9.0.2.jar +0 -0
- data/lib/flyingsaucer4r.rb +90 -0
- data/lib/itext-2.1.7.02.jar +0 -0
- data/lib/pdffilter.rb +37 -0
- data/test/test_pdffilter.rb +167 -0
- metadata +91 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9fc5c40751267820b64eae3bc0edce1f8b7e69e8
|
4
|
+
data.tar.gz: c6f8848e36f4107e5f43476af2ba0a9f718a6dfd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 13417560be87263d1bd60692a3733782546604cbba831a5d79bc3460dc4c733611e62d88bd0c3cbf54af1eea10235d7363591dc3befea703e13340994473f532
|
7
|
+
data.tar.gz: 1abd33645b6acd4696eade70eae6b44920323179c7dad979e26d3f6184c0a60fa2dd359b99db3b8d9731a56003e131e3ff9f67f00c08e7702ced6dfde12a5685
|
data/.gemtest
ADDED
File without changes
|
data/History.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
=== 0.6 / 2009-10-13
|
2
|
+
|
3
|
+
* Pull out PDF creation from filter
|
4
|
+
|
5
|
+
=== 0.2.20081112 / 2008-12-05
|
6
|
+
|
7
|
+
* Add debug setting to log the XHTML Flying Saucer sees
|
8
|
+
* Treat absolute URIs as relative for better Rails compatibility
|
9
|
+
|
10
|
+
=== 0.1.20081112 / 2008-12-03
|
11
|
+
|
12
|
+
* Initial version
|
13
|
+
|
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
= flyingsaucer4r
|
2
|
+
|
3
|
+
== DESCRIPTION:
|
4
|
+
|
5
|
+
The Flying Saucer gem gives JRuby access to the Flying Saucer XHTML renderer.
|
6
|
+
It also provides Rails integration to simplify sending PDFs generated from
|
7
|
+
XHTML to the browser.
|
8
|
+
|
9
|
+
== SYNOPSIS:
|
10
|
+
|
11
|
+
To add Flying Saucer to the JRuby classpath:
|
12
|
+
|
13
|
+
require 'flyingsaucer4r'
|
14
|
+
|
15
|
+
To use within Rails:
|
16
|
+
|
17
|
+
require 'pdffilter'
|
18
|
+
|
19
|
+
class ApplicationController < ActionController::Base
|
20
|
+
after_filter PDFFilter
|
21
|
+
|
22
|
+
...
|
23
|
+
end
|
24
|
+
|
25
|
+
Now any request with a :format of 'pdf' will be turned into a PDF using Flying
|
26
|
+
Saucer on the way out to the browser.
|
27
|
+
|
28
|
+
== REQUIREMENTS:
|
29
|
+
|
30
|
+
* None (but the Rails integration only makes sense if you are using Rails)
|
31
|
+
|
32
|
+
== INSTALL:
|
33
|
+
|
34
|
+
jruby -S gem install pbrant-flyingsaucer4r
|
35
|
+
|
36
|
+
== LICENSE:
|
37
|
+
|
38
|
+
Copyright (c) 2008 Consolidated Court Automation Programs
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
DEPENDENT_JARS = [ 'itext-2_0_8_02.jar' ]
|
6
|
+
|
7
|
+
STATIC_JAR_DIR = ENV['STATIC_JAR_DIR'] || "../StaticJars"
|
8
|
+
|
9
|
+
Hoe.spec('flyingsaucer4r') do |p|
|
10
|
+
developer 'CCAP Web Team', 'CCAP_Web_Team@wicourts.gov'
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Copies dependent JARs from StaticJars"
|
14
|
+
task :cp_dependent_jars do
|
15
|
+
rm_f 'lib/*.jar'
|
16
|
+
DEPENDENT_JARS.each do |jar|
|
17
|
+
cp "#{STATIC_JAR_DIR}/#{jar}", 'lib'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Updates Flying Saucer JAR with the latest drop from StaticJars"
|
22
|
+
task :update_fs_jar => :cp_dependent_jars do
|
23
|
+
latest = Dir.glob("#{STATIC_JAR_DIR}/xhtmlrenderer*.jar").sort[-1]
|
24
|
+
cp latest, 'lib'
|
25
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'enumerator'
|
3
|
+
|
4
|
+
Dir.glob(File.join(File.dirname(__FILE__), "*.jar")) { |jar| require File.basename(jar) }
|
5
|
+
|
6
|
+
module FlyingSaucer4R
|
7
|
+
VERSION = '0.7'
|
8
|
+
|
9
|
+
class UserAgent < org.xhtmlrenderer.pdf.ITextUserAgent
|
10
|
+
def initialize(output_device)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def resolveURI(uri)
|
15
|
+
if uri =~ /^\//
|
16
|
+
super(uri[1..uri.length])
|
17
|
+
else
|
18
|
+
super(uri)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
alias :resolve_uri :resolveURI
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.create_pdf(xhtml, base_path, logger = nil)
|
25
|
+
estimated_pdf_length = xhtml.length
|
26
|
+
output = java.io.ByteArrayOutputStream.new(estimated_pdf_length)
|
27
|
+
|
28
|
+
begin
|
29
|
+
dom = create_java_dom(xhtml, logger)
|
30
|
+
render_pdf(dom, base_path, output)
|
31
|
+
ensure
|
32
|
+
output.close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def self.provide_context(doc, line_no)
|
38
|
+
mark_selected = line_no != -1
|
39
|
+
line_no = 1 if line_no == -1
|
40
|
+
result = doc.enum_for(:each_line).enum_for(:each_with_index).inject([]) do |memo, pair|
|
41
|
+
line, current = pair
|
42
|
+
current += 1
|
43
|
+
diff = line_no - current
|
44
|
+
if diff.abs < 20
|
45
|
+
format_string = if line_no == current && mark_selected
|
46
|
+
"==> %4d %s"
|
47
|
+
else
|
48
|
+
" %4d %s"
|
49
|
+
end
|
50
|
+
memo << sprintf(format_string, current, line)
|
51
|
+
elsif current > line_no
|
52
|
+
break memo
|
53
|
+
end
|
54
|
+
|
55
|
+
memo
|
56
|
+
end
|
57
|
+
|
58
|
+
result.join('')
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.create_java_dom(s, logger)
|
62
|
+
begin
|
63
|
+
builder = javax.xml.parsers.DocumentBuilderFactory.new_instance.new_document_builder
|
64
|
+
builder.parse(java.io.ByteArrayInputStream.new(s.to_java_bytes))
|
65
|
+
rescue NativeException => e
|
66
|
+
java_e = e.cause
|
67
|
+
if java_e.is_a?(org.xml.sax.SAXParseException)
|
68
|
+
context = provide_context(s, java_e.line_number)
|
69
|
+
logger.info("Unable to parse XHTML at line #{java_e.line_number}, column #{java_e.column_number}: #{java_e.message}\n#{context}") if logger
|
70
|
+
end
|
71
|
+
raise e
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.render_pdf(dom, base_path, output)
|
76
|
+
renderer = org.xhtmlrenderer.pdf.ITextRenderer.new
|
77
|
+
agent = UserAgent.new(renderer.output_device)
|
78
|
+
agent.shared_context = renderer.shared_context
|
79
|
+
renderer.shared_context.user_agent_callback = agent
|
80
|
+
renderer.set_document(dom, path_to_url(base_path))
|
81
|
+
renderer.layout
|
82
|
+
|
83
|
+
renderer.create_pdf(output)
|
84
|
+
String.from_java_bytes(output.to_byte_array)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.path_to_url(path)
|
88
|
+
java.io.File.new(path).to_uri.to_url.to_string
|
89
|
+
end
|
90
|
+
end
|
Binary file
|
data/lib/pdffilter.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'flyingsaucer4r'
|
2
|
+
require 'java'
|
3
|
+
require 'enumerator'
|
4
|
+
|
5
|
+
class PDFFilter
|
6
|
+
class <<self
|
7
|
+
def debug=(b)
|
8
|
+
@debug = b
|
9
|
+
end
|
10
|
+
|
11
|
+
def debug?
|
12
|
+
@debug
|
13
|
+
end
|
14
|
+
|
15
|
+
def filter(controller)
|
16
|
+
format = controller.request.parameters[:format]
|
17
|
+
return unless format && format.to_sym == :pdf
|
18
|
+
|
19
|
+
controller.logger.debug("Rendering XHTML to PDF:\n" + controller.response.body) if debug?
|
20
|
+
|
21
|
+
pdf = FlyingSaucer4R.create_pdf(
|
22
|
+
controller.response.body,
|
23
|
+
File.join(Rails.public_path, 'placeholder.html'),
|
24
|
+
controller.logger)
|
25
|
+
|
26
|
+
controller.response.content_type = 'application/pdf'
|
27
|
+
add_ie6_pdf_over_ssl_headers(controller.response.headers)
|
28
|
+
controller.response.body = pdf
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def add_ie6_pdf_over_ssl_headers(headers)
|
33
|
+
headers["Cache-Control"] ||= 'maxage=3600'
|
34
|
+
headers["Pragma"] ||= 'public'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'shoulda'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'mocha'
|
5
|
+
|
6
|
+
lib_dir = "#{File.dirname(__FILE__)}/../lib"
|
7
|
+
$: << lib_dir unless $:.include?(lib_dir)
|
8
|
+
require 'pdffilter'
|
9
|
+
|
10
|
+
class Rails
|
11
|
+
def self.public_path
|
12
|
+
File.dirname(__FILE__) + "/public"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class LoggerStub
|
17
|
+
def debug(message)
|
18
|
+
@debug_message = message
|
19
|
+
end
|
20
|
+
|
21
|
+
def last_debug_message
|
22
|
+
@debug_message
|
23
|
+
end
|
24
|
+
|
25
|
+
def info(message)
|
26
|
+
@info_message = message
|
27
|
+
end
|
28
|
+
|
29
|
+
def last_info_message
|
30
|
+
@info_message
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class PDFFilterTest < Test::Unit::TestCase
|
35
|
+
def setup
|
36
|
+
@controller = Struct.new(:request, :response, :logger).new
|
37
|
+
end
|
38
|
+
|
39
|
+
context "An HTML request" do
|
40
|
+
should "exit right away" do
|
41
|
+
@controller.request = stub(:parameters => { :format => "html" })
|
42
|
+
assert_nil PDFFilter.filter(@controller)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "An request with no specified format" do
|
47
|
+
should "exit right away" do
|
48
|
+
@controller.request = stub(:parameters => {})
|
49
|
+
assert_nil PDFFilter.filter(@controller)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "A PDF request with an image" do
|
54
|
+
setup do
|
55
|
+
standard_pdf("<html><body><h1><img src='images/apple.jpg' />Hello from Flying Saucer!</h1></body></html>")
|
56
|
+
end
|
57
|
+
|
58
|
+
should "render something" do
|
59
|
+
assert_not_nil PDFFilter.filter(@controller)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def standard_pdf(content)
|
64
|
+
@controller.request = stub(:parameters => { :format => "pdf" })
|
65
|
+
response_mock = mock()
|
66
|
+
@controller.response = response_mock
|
67
|
+
@controller.logger = LoggerStub.new
|
68
|
+
response_mock.expects(:body).returns(content).at_least_once
|
69
|
+
response_mock.expects(:content_type=).with('application/pdf').once
|
70
|
+
@headers = {}
|
71
|
+
response_mock.expects(:headers).returns(@headers).once
|
72
|
+
response_mock.expects(:body=).once
|
73
|
+
end
|
74
|
+
|
75
|
+
def invalid_xhtml(content)
|
76
|
+
@controller.request = stub(:parameters => { :format => "pdf" })
|
77
|
+
response_mock = mock()
|
78
|
+
@controller.response = response_mock
|
79
|
+
@controller.logger = LoggerStub.new
|
80
|
+
response_mock.expects(:body).returns(content).at_least_once
|
81
|
+
end
|
82
|
+
|
83
|
+
context "Malformed XHTML" do
|
84
|
+
should "report the line number of invalid XHTML with context" do
|
85
|
+
invalid_xhtml(<<-EOF)
|
86
|
+
<html>
|
87
|
+
<body>
|
88
|
+
<h1>Hello from Flying Saucer!
|
89
|
+
</body>
|
90
|
+
</html>
|
91
|
+
EOF
|
92
|
+
assert_raise NativeException do
|
93
|
+
PDFFilter.filter(@controller)
|
94
|
+
end
|
95
|
+
assert_match %r[Unable to parse XHTML at line 4], @controller.logger.last_info_message
|
96
|
+
assert_match %r[==>\s+4], @controller.logger.last_info_message
|
97
|
+
end
|
98
|
+
|
99
|
+
should "complain about XML that isn't" do
|
100
|
+
invalid_xhtml(<<-EOF)
|
101
|
+
some text
|
102
|
+
that
|
103
|
+
really
|
104
|
+
isn't XML
|
105
|
+
EOF
|
106
|
+
assert_raise NativeException do
|
107
|
+
PDFFilter.filter(@controller)
|
108
|
+
end
|
109
|
+
assert_match %r[==>\s+1 some text], @controller.logger.last_info_message
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "A PDF request" do
|
114
|
+
setup do
|
115
|
+
standard_pdf("<html><body><h1>Hello from Flying Saucer!</h1></body></html>")
|
116
|
+
end
|
117
|
+
|
118
|
+
should "render something" do
|
119
|
+
assert_not_nil PDFFilter.filter(@controller)
|
120
|
+
end
|
121
|
+
|
122
|
+
should("render something that looks like a PDF") do
|
123
|
+
assert_match(/%PDF/, PDFFilter.filter(@controller))
|
124
|
+
end
|
125
|
+
|
126
|
+
should "add headers for ie6" do
|
127
|
+
PDFFilter.filter(@controller)
|
128
|
+
assert_equal 'public', @headers["Pragma"]
|
129
|
+
assert_equal 'maxage=3600', @headers["Cache-Control"]
|
130
|
+
end
|
131
|
+
|
132
|
+
should "not overwrite existing ie6 header" do
|
133
|
+
@headers['Pragma'] = 'private'
|
134
|
+
PDFFilter.filter(@controller)
|
135
|
+
assert_equal 'private', @headers["Pragma"]
|
136
|
+
end
|
137
|
+
|
138
|
+
should "not log debug message if debug turned off" do
|
139
|
+
PDFFilter.filter(@controller)
|
140
|
+
assert_nil @controller.logger.last_debug_message
|
141
|
+
end
|
142
|
+
|
143
|
+
should "log XHTML content if debug turned on" do
|
144
|
+
old_debug = PDFFilter.debug?
|
145
|
+
PDFFilter.debug = true
|
146
|
+
begin
|
147
|
+
PDFFilter.filter(@controller)
|
148
|
+
assert_match(/<html>/, @controller.logger.last_debug_message)
|
149
|
+
ensure
|
150
|
+
PDFFilter.debug = old_debug
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context FlyingSaucer4R::UserAgent do
|
156
|
+
setup { @user_agent = FlyingSaucer4R::UserAgent.new(org.xhtmlrenderer.pdf.ITextOutputDevice.new(1)) }
|
157
|
+
|
158
|
+
should "treat absolute URIs as relative (Java method name)" do
|
159
|
+
assert_equal @user_agent.resolveURI('stylesheets/aim.css'), @user_agent.resolveURI('/stylesheets/aim.css')
|
160
|
+
end
|
161
|
+
|
162
|
+
should "treat absolute URIs as relative (Ruby method name)" do
|
163
|
+
assert_equal @user_agent.resolve_uri('stylesheets/aim.css'), @user_agent.resolve_uri('/stylesheets/aim.css')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flyingsaucer4r
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.7'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- CCAP Web Team
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-05-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rdoc
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '4.0'
|
25
|
+
prerelease: false
|
26
|
+
type: :development
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: hoe
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.13'
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '3.13'
|
39
|
+
prerelease: false
|
40
|
+
type: :development
|
41
|
+
description: |-
|
42
|
+
The Flying Saucer gem gives JRuby access to the Flying Saucer XHTML renderer.
|
43
|
+
It also provides Rails integration to simplify sending PDFs generated from
|
44
|
+
XHTML to the browser.
|
45
|
+
email:
|
46
|
+
- CCAP_Web_Team@wicourts.gov
|
47
|
+
executables: []
|
48
|
+
extensions: []
|
49
|
+
extra_rdoc_files:
|
50
|
+
- History.txt
|
51
|
+
- Manifest.txt
|
52
|
+
- README.txt
|
53
|
+
files:
|
54
|
+
- History.txt
|
55
|
+
- Manifest.txt
|
56
|
+
- README.txt
|
57
|
+
- Rakefile
|
58
|
+
- lib/flying-saucer-core-9.0.2.jar
|
59
|
+
- lib/flying-saucer-pdf-9.0.2.jar
|
60
|
+
- lib/flyingsaucer4r.rb
|
61
|
+
- lib/itext-2.1.7.02.jar
|
62
|
+
- lib/pdffilter.rb
|
63
|
+
- test/test_pdffilter.rb
|
64
|
+
- .gemtest
|
65
|
+
homepage:
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options:
|
71
|
+
- --main
|
72
|
+
- README.txt
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 2.1.9
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: The Flying Saucer gem gives JRuby access to the Flying Saucer XHTML renderer
|
91
|
+
test_files: []
|