jpeg2pdf 0.12
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.
- 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
|