jpeg2pdf 0.12
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +37 -0
- data/README +162 -0
- data/bin/jpeg2pdf +102 -0
- data/bin/test.rb +111 -0
- data/install.rb +1360 -0
- data/jpeg2pdf.gemspec +18 -0
- data/lib/image_size.rb +291 -0
- data/lib/jpeg2pdf.rb +84 -0
- data/lib/rpdf.rb +48 -0
- data/lib/rpdf/crossreftable.rb +33 -0
- data/lib/rpdf/document.rb +61 -0
- data/lib/rpdf/documentcatalog.rb +18 -0
- data/lib/rpdf/imagedictionary.rb +23 -0
- data/lib/rpdf/page.rb +21 -0
- data/lib/rpdf/pagetree.rb +24 -0
- data/lib/rpdf/pdfarray.rb +35 -0
- data/lib/rpdf/pdfboolean.rb +28 -0
- data/lib/rpdf/pdfdictionary.rb +51 -0
- data/lib/rpdf/pdfinteger.rb +17 -0
- data/lib/rpdf/pdfname.rb +35 -0
- data/lib/rpdf/pdfnull.rb +19 -0
- data/lib/rpdf/pdfnumeric.rb +28 -0
- data/lib/rpdf/pdfobject.rb +60 -0
- data/lib/rpdf/pdfstream.rb +32 -0
- data/lib/rpdf/pdfstring.rb +38 -0
- data/lib/rpdf/rectangle.rb +15 -0
- data/lib/rpdf/trailer.rb +30 -0
- data/lib/rpdf/util.rb +41 -0
- data/lib/rpdf/version.rb +31 -0
- metadata +71 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rpdf/crossreftable'
|
2
|
+
require 'rpdf/documentcatalog'
|
3
|
+
require 'rpdf/pagetree'
|
4
|
+
require 'rpdf/trailer'
|
5
|
+
require 'rpdf/version'
|
6
|
+
|
7
|
+
module RPDF
|
8
|
+
class Document
|
9
|
+
attr_reader :pdf_version, :pagetree, :root, :xrefs
|
10
|
+
|
11
|
+
def initialize(pdf, pdf_version = VERSION_DEFAULT)
|
12
|
+
@pdf = pdf
|
13
|
+
@pdf_version = pdf_version
|
14
|
+
@objects = Array.new
|
15
|
+
@pagetree = PageTree.new(self)
|
16
|
+
@root = DocumentCatalog.new(self)
|
17
|
+
@xrefs = CrossRefTable.new
|
18
|
+
|
19
|
+
# File Header [PDFRef15 p68]
|
20
|
+
bytes = @pdf_version.to_pdf
|
21
|
+
bytes << "����\n" # Ensure file transfer applications detect the file as binary. [PDFRef15 p69]
|
22
|
+
@numwritten = bytes.size
|
23
|
+
@pdf.write(bytes)
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def new_id(object)
|
28
|
+
@objects << object
|
29
|
+
@objects.size
|
30
|
+
end
|
31
|
+
|
32
|
+
def size
|
33
|
+
@objects.size
|
34
|
+
end
|
35
|
+
|
36
|
+
def write(object)
|
37
|
+
objectbytes = object.to_pdf
|
38
|
+
@xrefs.addUsedEntry(@numwritten, object.object_number) # position of the next object in the PDF file
|
39
|
+
@pdf.write(objectbytes)
|
40
|
+
@numwritten = @numwritten + objectbytes.size
|
41
|
+
end
|
42
|
+
|
43
|
+
def close_pdf
|
44
|
+
trailer = Trailer.new(self)
|
45
|
+
bytes = String.new
|
46
|
+
|
47
|
+
# File Body [PDFRef15 p69]
|
48
|
+
@objects.each { |object|
|
49
|
+
object.invalidate unless object.invalidated?
|
50
|
+
}
|
51
|
+
|
52
|
+
trailer.startxref = @numwritten
|
53
|
+
|
54
|
+
# Cross-Reference Table [PDFRef15 p69]
|
55
|
+
@pdf.write(@xrefs.to_pdf)
|
56
|
+
|
57
|
+
# File Trailer [PDFRef15 p72]
|
58
|
+
@pdf.write(trailer.to_pdf)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rpdf/pdfdictionary'
|
2
|
+
require 'rpdf/document'
|
3
|
+
|
4
|
+
module RPDF
|
5
|
+
|
6
|
+
# Document Catalog Object
|
7
|
+
# [PDFRef15 p113]
|
8
|
+
class DocumentCatalog < PDFDictionary
|
9
|
+
|
10
|
+
def initialize(document)
|
11
|
+
super({ :Type => :Catalog,
|
12
|
+
:Pages => document.pagetree },
|
13
|
+
document)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rpdf/pdfdictionary'
|
2
|
+
|
3
|
+
module RPDF
|
4
|
+
|
5
|
+
# Image Dictionary Object
|
6
|
+
# [PDFRef15 p303]
|
7
|
+
class ImageDictionary < PDFDictionary
|
8
|
+
|
9
|
+
# colorspace: :DeviceGrey, :DeviceRGB or :DeviceCMYK
|
10
|
+
# bpc: 8, 12 or 16
|
11
|
+
def initialize(width, height, colorspace, bpc, document=nil)
|
12
|
+
super({ :Type => :XObject,
|
13
|
+
:Subtype => :Image,
|
14
|
+
:Width => width,
|
15
|
+
:Height => height,
|
16
|
+
:ColorSpace => colorspace,
|
17
|
+
:BitsPerComponent => bpc },
|
18
|
+
document)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/rpdf/page.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rpdf/pdfdictionary'
|
2
|
+
|
3
|
+
module RPDF
|
4
|
+
|
5
|
+
# Page Object
|
6
|
+
# [PDFRef15 p118]
|
7
|
+
class Page < PDFDictionary
|
8
|
+
|
9
|
+
def initialize(document, parent)
|
10
|
+
super({ :Type => :Page,
|
11
|
+
:Parent => parent,
|
12
|
+
:Resources => {},
|
13
|
+
:MediaBox => Rectangle.new(0, 0, 612, 792) },
|
14
|
+
document)
|
15
|
+
|
16
|
+
parent.add_page(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rpdf/pdfdictionary'
|
2
|
+
|
3
|
+
module RPDF
|
4
|
+
|
5
|
+
# Page Tree Object
|
6
|
+
# [PDFRef15 p116]
|
7
|
+
class PageTree < PDFDictionary
|
8
|
+
|
9
|
+
def initialize(document, parent=nil)
|
10
|
+
super({ :Type => :Pages,
|
11
|
+
:Parent => parent,
|
12
|
+
:Kids => [],
|
13
|
+
:Count => 0 },
|
14
|
+
document)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_page(page)
|
18
|
+
self[:Kids] << page
|
19
|
+
self[:Count] = self[:Count].value + 1
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rpdf/pdfobject'
|
2
|
+
require 'rpdf/util'
|
3
|
+
|
4
|
+
module RPDF
|
5
|
+
|
6
|
+
# Array Object
|
7
|
+
# [PDFRef15 p34]
|
8
|
+
class PDFArray < PDFObject
|
9
|
+
attr_reader :array
|
10
|
+
|
11
|
+
def initialize(array, document=nil)
|
12
|
+
raise Exception.new("PDFArray is not an array") unless array.kind_of?(Array)
|
13
|
+
@array = Array.new
|
14
|
+
array.each { |object|
|
15
|
+
@array << RPDF.topdfobject(object, document)
|
16
|
+
}
|
17
|
+
super(document)
|
18
|
+
end
|
19
|
+
|
20
|
+
def <<(element)
|
21
|
+
@array << RPDF.topdfobject(element)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
bytes = "[ "
|
26
|
+
@array.each { |element|
|
27
|
+
bytes << element.to_ref # to_ref -> reference to indirect object
|
28
|
+
bytes << " "
|
29
|
+
}
|
30
|
+
bytes << "]"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rpdf/pdfobject'
|
2
|
+
|
3
|
+
module RPDF
|
4
|
+
|
5
|
+
# Boolean Object: true or false
|
6
|
+
# [PDFRef15 p28]
|
7
|
+
class PDFBoolean < PDFObject
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
attr_reader :value
|
11
|
+
|
12
|
+
def initialize(value, document=nil)
|
13
|
+
raise Exception.new("PDFBoolean not true nor false") unless (value.kind_of?(TrueClass) or value.kind_of?(FalseClass))
|
14
|
+
@value = value
|
15
|
+
super(document)
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other)
|
19
|
+
@value <=> other.value
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
@value.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rpdf/pdfobject'
|
2
|
+
require 'rpdf/util'
|
3
|
+
|
4
|
+
module RPDF
|
5
|
+
|
6
|
+
# Dictionary Object
|
7
|
+
# [PDFRef15 p35]
|
8
|
+
class PDFDictionary < PDFObject
|
9
|
+
attr_reader :dict
|
10
|
+
|
11
|
+
def initialize(dict, document=nil)
|
12
|
+
raise Exception.new("PDFDictionary is not a hash") unless dict.kind_of?(Hash)
|
13
|
+
@dict = Hash.new
|
14
|
+
dict.each { |key, value|
|
15
|
+
pdfkey = RPDF.topdfobject(key)
|
16
|
+
pdfvalue = RPDF.topdfobject(value)
|
17
|
+
raise Exception.new("PDFDictionary key must be a name") unless pdfkey.kind_of?(PDFName)
|
18
|
+
@dict[pdfkey] = pdfvalue
|
19
|
+
}
|
20
|
+
super(document)
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](key)
|
24
|
+
pdfkey = RPDF.topdfobject(key)
|
25
|
+
raise Exception.new("PDFDictionary key must be a name") unless pdfkey.kind_of?(PDFName)
|
26
|
+
@dict[pdfkey]
|
27
|
+
end
|
28
|
+
|
29
|
+
def []=(key, value)
|
30
|
+
pdfkey = RPDF.topdfobject(key)
|
31
|
+
pdfvalue = RPDF.topdfobject(value)
|
32
|
+
raise Exception.new("PDFDictionary key must be a name") unless pdfkey.kind_of?(PDFName)
|
33
|
+
@dict[pdfkey] = pdfvalue
|
34
|
+
end
|
35
|
+
|
36
|
+
# Update dictionary with other dictionary
|
37
|
+
def update(other)
|
38
|
+
@dict.update(other.dict)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
bytes = "<< "
|
43
|
+
@dict.each { |key, value|
|
44
|
+
bytes << "#{key.to_s} #{value.to_ref}\n" # to_ref -> reference to indirect object
|
45
|
+
}
|
46
|
+
bytes << ">>\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rpdf/pdfnumeric'
|
2
|
+
|
3
|
+
module RPDF
|
4
|
+
|
5
|
+
# Integer Object
|
6
|
+
# This is not a real PDF Object, as PDF knows only Numerics.
|
7
|
+
# [PDFRef15 p28]
|
8
|
+
class PDFInteger < PDFNumeric
|
9
|
+
|
10
|
+
def initialize(value, document=nil)
|
11
|
+
raise Exception.new("PDFInteger is not an integer") unless value.kind_of?(Integer)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/rpdf/pdfname.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rpdf/pdfobject'
|
2
|
+
require 'rpdf/util'
|
3
|
+
|
4
|
+
module RPDF
|
5
|
+
|
6
|
+
# Name Object
|
7
|
+
# [PDFRef15 p32]
|
8
|
+
class PDFName < PDFObject
|
9
|
+
attr_reader :value
|
10
|
+
|
11
|
+
def initialize(value, document=nil)
|
12
|
+
raise Exception.new("PDFName is not a string") unless value.kind_of?(String)
|
13
|
+
raise Exception.new("PDFName has whitespace") if RPDF.includes_whitespace?(value)
|
14
|
+
raise Exception.new("PDFName has delimiter") if RPDF.includes_delimiter?(value)
|
15
|
+
@value = value
|
16
|
+
super(document)
|
17
|
+
end
|
18
|
+
|
19
|
+
def hash
|
20
|
+
@value.hash
|
21
|
+
end
|
22
|
+
|
23
|
+
# To make PDFName possible to use as a key in a PDFDictionary
|
24
|
+
def eql?(other)
|
25
|
+
@value.eql?(other.value)
|
26
|
+
end
|
27
|
+
|
28
|
+
# TODO: 2-digit hexadecimal code for characters outside the range 33 (!) to 126 (~) [PDFRef15 p33]
|
29
|
+
def to_s
|
30
|
+
"/#{@value}"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
data/lib/rpdf/pdfnull.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rpdf/pdfobject'
|
2
|
+
|
3
|
+
module RPDF
|
4
|
+
|
5
|
+
# Numeric Object
|
6
|
+
# [PDFRef15 p28]
|
7
|
+
class PDFNumeric < PDFObject
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
attr_reader :value
|
11
|
+
|
12
|
+
def initialize(value, document=nil)
|
13
|
+
raise Exception.new("PDFNumeric is not a number") unless value.kind_of?(Numeric)
|
14
|
+
@value = value
|
15
|
+
super(document)
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other)
|
19
|
+
@value <=> other.value
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
@value.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module RPDF
|
2
|
+
|
3
|
+
# An object which is a part of a PDF document. [PDFRef15 p27]
|
4
|
+
class PDFObject
|
5
|
+
|
6
|
+
attr_reader :object_number, :generation_number
|
7
|
+
|
8
|
+
# Make an indirect object if a document is passed, otherwise a direct object.
|
9
|
+
def initialize(document=nil)
|
10
|
+
if document
|
11
|
+
@object_number = document.new_id(self)
|
12
|
+
@document = document
|
13
|
+
else
|
14
|
+
@object_number = 0 # The object number of an indirect object should be positive
|
15
|
+
end
|
16
|
+
@generation_number = 0
|
17
|
+
@invalidated = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def indirect?
|
21
|
+
@object_number > 0
|
22
|
+
end
|
23
|
+
|
24
|
+
# Subclasses has to call this in their invalidate method if they override it
|
25
|
+
def invalidate
|
26
|
+
@document.write(self)
|
27
|
+
@invalidated = true
|
28
|
+
end
|
29
|
+
|
30
|
+
# true if the object has already been written to the PDF stream
|
31
|
+
def invalidated?
|
32
|
+
@invalidated
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return a representation suitable to write to a PDF file
|
36
|
+
# [PDFRef15 p39]
|
37
|
+
def to_pdf
|
38
|
+
if indirect?
|
39
|
+
bytes = "#{@object_number} #{@generation_number} obj\n"
|
40
|
+
bytes << to_s << "\n"
|
41
|
+
bytes << "endobj\n\n"
|
42
|
+
else
|
43
|
+
to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return an indirect reference to this object if it's indirect,
|
48
|
+
# return the bytestream otherwise.
|
49
|
+
# [PDFRef15 p39]
|
50
|
+
def to_ref
|
51
|
+
if indirect?
|
52
|
+
"#{@object_number} #{@generation_number} R"
|
53
|
+
else
|
54
|
+
to_s
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rpdf/pdfdictionary'
|
2
|
+
require 'rpdf/pdfobject'
|
3
|
+
|
4
|
+
module RPDF
|
5
|
+
|
6
|
+
# Stream Object
|
7
|
+
# TODO: stream dictionary
|
8
|
+
# [PDFRef15 p36]
|
9
|
+
class PDFStream < PDFObject
|
10
|
+
attr_reader :data, :dict
|
11
|
+
|
12
|
+
def initialize(data, document=nil)
|
13
|
+
@data = data
|
14
|
+
@dict = PDFDictionary.new({ :Length => data.size })
|
15
|
+
super(document)
|
16
|
+
end
|
17
|
+
|
18
|
+
def invalidate
|
19
|
+
super
|
20
|
+
@data = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
bytes = @dict.to_pdf
|
25
|
+
bytes << "stream\n"
|
26
|
+
bytes << data
|
27
|
+
bytes << "\nendstream"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|