combine_pdf 0.2.21 → 0.2.27
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +54 -0
- data/LICENSE.txt +2 -1
- data/README.md +30 -0
- data/lib/combine_pdf.rb +12 -18
- data/lib/combine_pdf/api.rb +156 -153
- data/lib/combine_pdf/basic_writer.rb +41 -53
- data/lib/combine_pdf/decrypt.rb +235 -228
- data/lib/combine_pdf/filter.rb +79 -90
- data/lib/combine_pdf/fonts.rb +451 -459
- data/lib/combine_pdf/page_methods.rb +864 -877
- data/lib/combine_pdf/parser.rb +649 -578
- data/lib/combine_pdf/pdf_protected.rb +377 -218
- data/lib/combine_pdf/pdf_public.rb +490 -462
- data/lib/combine_pdf/renderer.rb +157 -163
- data/lib/combine_pdf/version.rb +1 -1
- data/test/automated +79 -0
- data/test/console +4 -4
- data/test/named_dest +84 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b28c525adfbcb7025e3510ede89ba70593ee92cc
|
4
|
+
data.tar.gz: bcd134da186dadebdc27839c2ce09f3133119763
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a61cd27e67327472a645bccb8f4f63c7e472d65ae51b122727e7eddca4ff9d58ead490030a55dc991450df2ae2f41343845f19cf587d5aff21ca7a93dcb0ba79
|
7
|
+
data.tar.gz: b5bfe0b958dd21ae305698b1e4376940a1fe0055f79cfbf75d8a0d003ba7de57dfb8b2d9cd15e39fc6dcac8277ff9f766fc9163154f4b39f4ec9b67ebf5f5c55
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,60 @@
|
|
2
2
|
|
3
3
|
***
|
4
4
|
|
5
|
+
Change log v.0.2.27
|
6
|
+
|
7
|
+
**Fix**: Fixed an issue where a `nil` outline count would cause PDF merger to fail.
|
8
|
+
|
9
|
+
**Fix**: Fixed an issue where `nil` data would cause the named destination rebuilding process to quit early, leaving some of the data unprocessed. Credit to Stefan Leitner (@sLe1tner) for exposing the issue.
|
10
|
+
|
11
|
+
**Feature**: PDF outlines are now merged and named destination links are preserved (both in the outlines and the page content). Credit to Stefan Leitner (@sLe1tner) for this feature.
|
12
|
+
|
13
|
+
***
|
14
|
+
|
15
|
+
Change log v.0.2.26
|
16
|
+
|
17
|
+
**Fix**: Merged PR #72, fixing a typo in the parser that caused incorrect byte substitution to corrupt certain PDF data (adversely effecting encrypted PDFs). Credit to Gyuchang Jun (@gyuchang) for the fix.
|
18
|
+
|
19
|
+
***
|
20
|
+
|
21
|
+
Change log v.0.2.25
|
22
|
+
|
23
|
+
**Fix**: Fixed issue #71, merging PDF outline that exist but have 0 entries fails and raises an exception. Credit to @Kagetsuki for exposing the issue.
|
24
|
+
|
25
|
+
***
|
26
|
+
|
27
|
+
Change log v.0.2.24
|
28
|
+
|
29
|
+
**Fix**: Fixed an issue with PDF Catalog and PDF Page property inheritance that could cause corrupted PDF output (invalid PDF data). Credit to @Kagetsuki for opening an issue that let to this discovery.
|
30
|
+
|
31
|
+
**Fix**: Fixed an issue with the parser where (ignored) empty strings would cause incorrect alignment when converting PDF dictionary objects from an Array to a Hash, mixing up keys and values. Credit to @Kagetsuki for opening an issue that let to this discovery.
|
32
|
+
|
33
|
+
**Fix**: more fixes and refinements to the PDF Names dictionary with better named destination support and document navigation support.
|
34
|
+
|
35
|
+
***
|
36
|
+
|
37
|
+
Change log v.0.2.23
|
38
|
+
|
39
|
+
**Fix**: fixed an issue introduced in v.0.2.22, where name dictionary conflict resolution would result in corrupted PDF files. The issue was caused because the name conflict resolution wasn't updated to handle the changes in the new reference linking algorithm used by the parser. During this fix, the whole name dictionary algorithm was re-written, providing better support for named destinations, links and (future feature) ToCs. Credit to Kevin Shen (@kevshin2) for exposing the issue.
|
40
|
+
|
41
|
+
***
|
42
|
+
|
43
|
+
Change log v.0.2.22 (yanked)
|
44
|
+
|
45
|
+
**Fix**: fixed an issue with PDF font importing (registering).
|
46
|
+
|
47
|
+
**Fix**: fixed issue #65 where some form data (radio buttons) could be lost. Credit to @joshirashmics for exposing the issue.
|
48
|
+
|
49
|
+
**Fix**: fixed an issue where empty names would be ignored by the parser (who knew they existed...).
|
50
|
+
|
51
|
+
**Fix**: Possible fix for issue #66 (similar to PR #61)... Credit to Serafeim Maroulis (@Reyko) and Kevin Shen (@kevshin2) for exposing the issue.
|
52
|
+
|
53
|
+
**Update**: Rewrote some internal algorithms, avoiding recursive logic and optimizing against excessive stack stress.
|
54
|
+
|
55
|
+
**Feature**: Credit to Joel Williams (@joelw) for providing `CombinePDF.load` and `CombinePDF.parse` customization, allowing optional content errors to be ignored - taking the risk of a corrupt PDF instead of raising an exception (hey, loading PDF data with optional content sometimes works).
|
56
|
+
|
57
|
+
***
|
58
|
+
|
5
59
|
Change log v.0.2.21
|
6
60
|
|
7
61
|
**Fix**: fix for issue #54 and #59 (duplicate), discovered by @iggant (Anton Kolodii), related to name conflict resolution and page resources. The issue would cause and error (exception) to occur when attempting to merge pages with specific resource structures. Credit to @cw6365 (Chris Ward) and @DenKey (Den) as well.
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -14,6 +14,24 @@ gem install combine_pdf
|
|
14
14
|
|
15
15
|
## Known Limitations
|
16
16
|
|
17
|
+
Quick rundown:
|
18
|
+
|
19
|
+
* When reading PDF Forms, some form data might be lost. I tried fixing this to the best of my ability, but I'm not sure it all works just yet.
|
20
|
+
|
21
|
+
* When combining PDF Forms, form data might be unified. If you're combining two PDF files with form data, the data might be unified. I couldn't fix this, but frankly, I kinda liked the issue... it's almost a feature.
|
22
|
+
|
23
|
+
* When unifying the same TOC data more then once, one of the references will be unified with the other.
|
24
|
+
|
25
|
+
* Links and named destinations (i.e., a link in the PDF to a web page or a different page in the same PDF) might break. Again, I tried fixing this, but some of it depends on the TOC and some of it is susceptible to conflicts between files.
|
26
|
+
|
27
|
+
Also, some links and data (URL links and PDF "Named Destinations") are stored at the root of a PDF and they aren't linked back to from the page.
|
28
|
+
|
29
|
+
For this reason, some links will be lost when ripping pages out of PDF files and merging them with another PDF.
|
30
|
+
|
31
|
+
* Some encrypted PDF files (usually the ones you can't view without a password) will fail quietly instead of noisily.
|
32
|
+
|
33
|
+
* Sometimes the CombinePDF will raise an exception even if the PDF could be parsed (i.e., when PDF optional content exists)... I find it better to err on the side of caution, although for optional content PDFs it is avoidable using `CombinePDF.load(pdf_file, allow_optional_content: true)`.
|
34
|
+
|
17
35
|
CombinePDF is written natively in Ruby and should (presumably) work on all Ruby platforms that follow Ruby 2.0 compatibility.
|
18
36
|
|
19
37
|
However, PDF files are quite complex creatures and no guaranty is provided.
|
@@ -122,6 +140,16 @@ headers 'content-type' => "application/pdf"
|
|
122
140
|
|
123
141
|
If you prefer to save the PDF data to a file, you can always use the `save` method as we did in our earlier examples.
|
124
142
|
|
143
|
+
Some PDF files contain optional content sections which cannot always be merged reliably. By default, an exception is
|
144
|
+
raised if one of these files are detected. You can optionally pass an `allow_optional_content` parameter to the
|
145
|
+
`PDFParser.new`, `CombinePDF.load` and `CombinePDF.parse` methods:
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
new_pdf = CombinePDF.new
|
149
|
+
new_pdf << CombinePDF.load(pdf_file, allow_optional_content: true)
|
150
|
+
attachments.each { |att| new_pdf << CombinePDF.load(att, allow_optional_content: true) }
|
151
|
+
```
|
152
|
+
|
125
153
|
Demo
|
126
154
|
====
|
127
155
|
|
@@ -148,6 +176,8 @@ The code itself should be very straight forward, but feel free to ask whatever y
|
|
148
176
|
Credit
|
149
177
|
======
|
150
178
|
|
179
|
+
Stefan Leitner (@sLe1tner) wrote the outline merging code supporting PDFs which contain a ToC.
|
180
|
+
|
151
181
|
Caige Nichols wrote an amazing RC4 gem which I used in my code.
|
152
182
|
|
153
183
|
I wanted to install the gem, but I had issues with the internet and ended up copying the code itself into the combine_pdf_decrypt class file.
|
data/lib/combine_pdf.rb
CHANGED
@@ -6,25 +6,23 @@ require 'strscan'
|
|
6
6
|
require 'matrix'
|
7
7
|
require 'set'
|
8
8
|
|
9
|
-
#require the RC4 Gem
|
9
|
+
# require the RC4 Gem
|
10
10
|
require 'rc4'
|
11
11
|
|
12
|
-
|
13
|
-
load
|
14
|
-
load
|
15
|
-
load
|
16
|
-
load
|
17
|
-
load
|
18
|
-
load
|
19
|
-
load
|
20
|
-
load
|
21
|
-
load
|
22
|
-
load "combine_pdf/pdf_protected.rb"
|
12
|
+
load 'combine_pdf/api.rb'
|
13
|
+
load 'combine_pdf/renderer.rb'
|
14
|
+
load 'combine_pdf/page_methods.rb'
|
15
|
+
load 'combine_pdf/basic_writer.rb'
|
16
|
+
load 'combine_pdf/decrypt.rb'
|
17
|
+
load 'combine_pdf/fonts.rb'
|
18
|
+
load 'combine_pdf/filter.rb'
|
19
|
+
load 'combine_pdf/parser.rb'
|
20
|
+
load 'combine_pdf/pdf_public.rb'
|
21
|
+
load 'combine_pdf/pdf_protected.rb'
|
23
22
|
|
24
23
|
# load "combine_pdf/operations.rb"
|
25
24
|
|
26
|
-
load
|
27
|
-
|
25
|
+
load 'combine_pdf/version.rb'
|
28
26
|
|
29
27
|
# This is a pure ruby library to combine/merge, stmap/overlay and number PDF files - as well as to create tables (ment for indexing combined files).
|
30
28
|
#
|
@@ -128,11 +126,8 @@ load "combine_pdf/version.rb"
|
|
128
126
|
#
|
129
127
|
# MIT
|
130
128
|
module CombinePDF
|
131
|
-
|
132
129
|
end
|
133
130
|
|
134
|
-
|
135
|
-
|
136
131
|
#########################################################
|
137
132
|
# this file is part of the CombinePDF library and the code
|
138
133
|
# is subject to the same license (MIT).
|
@@ -181,4 +176,3 @@ end
|
|
181
176
|
### gives 0.000000 0.000000 1.010000 ( 1.147135)
|
182
177
|
### file merge FAILS with 1,000 empty pages (undecrypted)
|
183
178
|
####### gives: 0.000000 0.000000 1.770000 ( 1.775513) with draft. file size 4.9MB (!!!)
|
184
|
-
|
data/lib/combine_pdf/api.rb
CHANGED
@@ -1,167 +1,170 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
|
+
module CombinePDF
|
4
|
+
module_function
|
3
5
|
|
6
|
+
# Create an empty PDF object or create a PDF object from a file (parsing the file).
|
7
|
+
# file_name:: is the name of a file to be parsed.
|
8
|
+
def load(file_name = '', options = {})
|
9
|
+
raise TypeError, "couldn't parse data, expecting type String" unless file_name.is_a?(String) || file_name.is_a?(Pathname)
|
10
|
+
return PDF.new if file_name == ''
|
11
|
+
PDF.new(PDFParser.new(IO.read(file_name, mode: 'rb').force_encoding(Encoding::ASCII_8BIT), options))
|
12
|
+
end
|
4
13
|
|
14
|
+
# creats a new PDF object.
|
15
|
+
#
|
16
|
+
# Combine PDF will check to see if `string` is a filename.
|
17
|
+
# If it's a file name, it will attempt to load the PDF file using `CombinePDF.load`. Otherwise it will attempt parsing `string` using `CombinePDF.parse`.
|
18
|
+
#
|
19
|
+
# If the string is empty it will return a new PDF object (the same as parse).
|
20
|
+
#
|
21
|
+
# For both performance and code readability reasons, `CombinePDF.load` and `CombinePDF.parse` should be preffered unless creating a new PDF object.
|
22
|
+
def new(string = false)
|
23
|
+
return PDF.new unless string
|
24
|
+
raise TypeError, "couldn't create PDF object, expecting type String" unless string.is_a?(String) || string.is_a?(Pathname)
|
25
|
+
begin
|
26
|
+
(begin
|
27
|
+
File.file? string
|
28
|
+
rescue
|
29
|
+
false
|
30
|
+
end) ? load(string) : parse(string)
|
31
|
+
rescue => e
|
32
|
+
raise 'General PDF error - Use CombinePDF.load or CombinePDF.parse for a non-general error message (the requested file was not found OR the string received is not a valid PDF stream OR the file was found but not valid).'
|
33
|
+
end
|
34
|
+
end
|
5
35
|
|
36
|
+
# Create a PDF object from a raw PDF data (parsing the data).
|
37
|
+
# data:: is a string that represents the content of a PDF file.
|
38
|
+
def parse(data, options = {})
|
39
|
+
raise TypeError, "couldn't parse and data, expecting type String" unless data.is_a? String
|
40
|
+
PDF.new(PDFParser.new(data, options))
|
41
|
+
end
|
6
42
|
|
7
|
-
|
8
|
-
|
43
|
+
# makes a PDFWriter object
|
44
|
+
#
|
45
|
+
# PDFWriter objects reresent an empty page and have the method "textbox"
|
46
|
+
# that adds content to that page.
|
47
|
+
#
|
48
|
+
# PDFWriter objects are used internally for numbering pages (by creating a PDF page
|
49
|
+
# with the page number and "stamping" it over the existing page).
|
50
|
+
#
|
51
|
+
# ::mediabox an Array representing the size of the PDF document. defaults to: [0.0, 0.0, 612.0, 792.0] (US Letter)
|
52
|
+
#
|
53
|
+
# if the page is PDFWriter object as a stamp, the final size will be that of the original page.
|
54
|
+
def create_page(mediabox = [0, 0, 612.0, 792.0])
|
55
|
+
PDFWriter.new mediabox
|
56
|
+
end
|
9
57
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
58
|
+
# makes a PDF object containing a table
|
59
|
+
#
|
60
|
+
# all the pages in this PDF object are PDFWriter objects and are
|
61
|
+
# writable using the texbox function (should you wish to add a title, or more info)
|
62
|
+
#
|
63
|
+
# the main intended use of this method is to create indexes (a table of contents) for merged data.
|
64
|
+
#
|
65
|
+
# example:
|
66
|
+
# pdf = CombinePDF.create_table headers: ["header 1", "another header"], table_data: [ ["this is one row", "with two columns"] , ["this is another row", "also two columns", "the third will be ignored"] ]
|
67
|
+
# pdf.save "table_file.pdf"
|
68
|
+
#
|
69
|
+
# accepts a Hash with any of the following keys as well as any of the Page_Methods#textbox options:
|
70
|
+
# headers:: an Array of strings with the headers (will be repeated every page).
|
71
|
+
# table_data:: as Array of Arrays, each containing a string for each column. the first row sets the number of columns. extra columns will be ignored.
|
72
|
+
# font:: a registered or standard font name (see Page_Methods). defaults to nil (:Helvetica).
|
73
|
+
# header_font:: a registered or standard font name for the headers (see Page_Methods). defaults to nil (the font for all the table rows).
|
74
|
+
# max_font_size:: the maximum font size. if the string doesn't fit, it will be resized. defaults to 14.
|
75
|
+
# column_widths:: an array of relative column widths ([1,2] will display only the first two columns, the second twice as big as the first). defaults to nil (even widths).
|
76
|
+
# header_color:: the header color. defaults to [0.8, 0.8, 0.8] (light gray).
|
77
|
+
# main_color:: main row color. defaults to nil (transparent / white).
|
78
|
+
# alternate_color:: alternate row color. defaults to [0.95, 0.95, 0.95] (very light gray).
|
79
|
+
# font_color:: font color. defaults to [0,0,0] (black).
|
80
|
+
# border_color:: border color. defaults to [0,0,0] (black).
|
81
|
+
# border_width:: border width in PDF units. defaults to 1.
|
82
|
+
# header_align:: the header text alignment within each column (:right, :left, :center). defaults to :center.
|
83
|
+
# row_align:: the row text alignment within each column. defaults to :left (:right for RTL table).
|
84
|
+
# direction:: the table's writing direction (:ltr or :rtl). this reffers to the direction of the columns and doesn't effect text (rtl text is automatically recognized). defaults to :ltr.
|
85
|
+
# max_rows:: the number of rows per page, INCLUDING the header row. deafults to 25.
|
86
|
+
# page_size:: the size of the page in PDF points. defaults to [0, 0, 595.3, 841.9] (A4).
|
87
|
+
def create_table(options = {})
|
88
|
+
options[:max_rows] = options[:rows_per_page] if options[:rows_per_page]
|
35
89
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
#
|
47
|
-
# PDFWriter objects are used internally for numbering pages (by creating a PDF page
|
48
|
-
# with the page number and "stamping" it over the existing page).
|
49
|
-
#
|
50
|
-
# ::mediabox an Array representing the size of the PDF document. defaults to: [0.0, 0.0, 612.0, 792.0] (US Letter)
|
51
|
-
#
|
52
|
-
# if the page is PDFWriter object as a stamp, the final size will be that of the original page.
|
53
|
-
def create_page(mediabox = [0, 0, 612.0, 792.0])
|
54
|
-
PDFWriter.new mediabox
|
55
|
-
end
|
90
|
+
page_size = options[:page_size] || [0, 0, 595.3, 841.9]
|
91
|
+
table = PDF.new
|
92
|
+
page = nil
|
93
|
+
until options[:table_data].empty?
|
94
|
+
page = create_page page_size
|
95
|
+
page.write_table options
|
96
|
+
table << page
|
97
|
+
end
|
98
|
+
table
|
99
|
+
end
|
56
100
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# writable using the texbox function (should you wish to add a title, or more info)
|
61
|
-
#
|
62
|
-
# the main intended use of this method is to create indexes (a table of contents) for merged data.
|
63
|
-
#
|
64
|
-
# example:
|
65
|
-
# pdf = CombinePDF.create_table headers: ["header 1", "another header"], table_data: [ ["this is one row", "with two columns"] , ["this is another row", "also two columns", "the third will be ignored"] ]
|
66
|
-
# pdf.save "table_file.pdf"
|
67
|
-
#
|
68
|
-
# accepts a Hash with any of the following keys as well as any of the Page_Methods#textbox options:
|
69
|
-
# headers:: an Array of strings with the headers (will be repeated every page).
|
70
|
-
# table_data:: as Array of Arrays, each containing a string for each column. the first row sets the number of columns. extra columns will be ignored.
|
71
|
-
# font:: a registered or standard font name (see Page_Methods). defaults to nil (:Helvetica).
|
72
|
-
# header_font:: a registered or standard font name for the headers (see Page_Methods). defaults to nil (the font for all the table rows).
|
73
|
-
# max_font_size:: the maximum font size. if the string doesn't fit, it will be resized. defaults to 14.
|
74
|
-
# column_widths:: an array of relative column widths ([1,2] will display only the first two columns, the second twice as big as the first). defaults to nil (even widths).
|
75
|
-
# header_color:: the header color. defaults to [0.8, 0.8, 0.8] (light gray).
|
76
|
-
# main_color:: main row color. defaults to nil (transparent / white).
|
77
|
-
# alternate_color:: alternate row color. defaults to [0.95, 0.95, 0.95] (very light gray).
|
78
|
-
# font_color:: font color. defaults to [0,0,0] (black).
|
79
|
-
# border_color:: border color. defaults to [0,0,0] (black).
|
80
|
-
# border_width:: border width in PDF units. defaults to 1.
|
81
|
-
# header_align:: the header text alignment within each column (:right, :left, :center). defaults to :center.
|
82
|
-
# row_align:: the row text alignment within each column. defaults to :left (:right for RTL table).
|
83
|
-
# direction:: the table's writing direction (:ltr or :rtl). this reffers to the direction of the columns and doesn't effect text (rtl text is automatically recognized). defaults to :ltr.
|
84
|
-
# max_rows:: the number of rows per page, INCLUDING the header row. deafults to 25.
|
85
|
-
# page_size:: the size of the page in PDF points. defaults to [0, 0, 595.3, 841.9] (A4).
|
86
|
-
def create_table(options = {})
|
87
|
-
options[:max_rows] = options[:rows_per_page] if options[:rows_per_page]
|
101
|
+
def new_table(options = {})
|
102
|
+
create_table options
|
103
|
+
end
|
88
104
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
105
|
+
# calculate a CTM value for a specific transformation.
|
106
|
+
#
|
107
|
+
# this could be used to apply transformation in #textbox and to convert visual
|
108
|
+
# rotation values into actual rotation transformation.
|
109
|
+
#
|
110
|
+
# this method accepts a Hash containing any of the following parameters:
|
111
|
+
#
|
112
|
+
# deg:: the clockwise rotation to be applied, in degrees
|
113
|
+
# tx:: the x translation to be applied.
|
114
|
+
# ty:: the y translation to be applied.
|
115
|
+
# sx:: the x scaling to be applied.
|
116
|
+
# sy:: the y scaling to be applied.
|
117
|
+
#
|
118
|
+
# * scaling will be applied after the transformation is applied.
|
119
|
+
#
|
120
|
+
def calc_ctm(parameters)
|
121
|
+
p = { deg: 0, tx: 0, ty: 0, sx: 1, sy: 1 }.merge parameters
|
122
|
+
r = p[:deg] * Math::PI / 180
|
123
|
+
s = Math.sin(r)
|
124
|
+
c = Math.cos(r)
|
125
|
+
# start with tranlation matrix
|
126
|
+
m = Matrix[[1, 0, 0], [0, 1, 0], [p[:tx], p[:ty], 1]]
|
127
|
+
# then rotate
|
128
|
+
m *= Matrix[[c, s, 0], [-s, c, 0], [0, 0, 1]] if parameters[:deg]
|
129
|
+
# then scale
|
130
|
+
m *= Matrix[[p[:sx], 0, 0], [0, p[:sy], 0], [0, 0, 1]] if parameters[:sx] || parameters[:sy]
|
131
|
+
# flaten array and round to 6 digits
|
132
|
+
m.to_a.flatten.values_at(0, 1, 3, 4, 6, 7).map! { |f| f.round 6 }
|
133
|
+
end
|
102
134
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
#
|
118
|
-
def calc_ctm parameters
|
119
|
-
p = {deg: 0, tx: 0, ty: 0, sx: 1, sy: 1}.merge parameters
|
120
|
-
r = p[:deg] * Math::PI / 180
|
121
|
-
s = Math.sin(r)
|
122
|
-
c = Math.cos(r)
|
123
|
-
# start with tranlation matrix
|
124
|
-
m = Matrix[ [1,0,0], [0,1,0], [ p[:tx], p[:ty], 1] ]
|
125
|
-
# then rotate
|
126
|
-
m = m * Matrix[ [c, s, 0], [-s, c, 0], [0, 0, 1]] if parameters[:deg]
|
127
|
-
# then scale
|
128
|
-
m = m * Matrix[ [p[:sx], 0, 0], [0, p[:sy], 0], [0,0,1] ] if parameters[:sx] || parameters[:sy]
|
129
|
-
# flaten array and round to 6 digits
|
130
|
-
m.to_a.flatten.values_at(0,1,3,4,6,7).map! {|f| f.round 6}
|
131
|
-
end
|
135
|
+
# adds a correctly formatted font object to the font library.
|
136
|
+
#
|
137
|
+
# registered fonts will remain in the library and will only be embeded in
|
138
|
+
# PDF objects when they are used by PDFWriter objects (for example, for numbering pages).
|
139
|
+
#
|
140
|
+
# this function enables plug-ins to expend the font functionality of CombinePDF.
|
141
|
+
#
|
142
|
+
# font_name:: a Symbol with the name of the font. if the fonts exists in the library, it will be overwritten!
|
143
|
+
# font_metrics:: a Hash of font metrics, of the format char => {wx: char_width, boundingbox: [left_x, buttom_y, right_x, top_y]} where char == character itself (i.e. " " for space). The Hash should contain a special value :missing for the metrics of missing characters. an optional :wy might be supported in the future, for up to down fonts.
|
144
|
+
# font_pdf_object:: a Hash in the internal format recognized by CombinePDF, that represents the font object.
|
145
|
+
# font_cmap:: a CMap dictionary Hash) which maps unicode characters to the hex CID for the font (i.e. {"a" => "61", "z" => "7a" }).
|
146
|
+
def register_font(font_name, font_metrics, font_pdf_object, font_cmap = nil)
|
147
|
+
Fonts.register_font font_name, font_metrics, font_pdf_object, font_cmap
|
148
|
+
end
|
132
149
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
150
|
+
# adds an existing font (from any PDF Object) to the font library.
|
151
|
+
#
|
152
|
+
# returns the font on success or false on failure.
|
153
|
+
#
|
154
|
+
# example:
|
155
|
+
# fonts = CombinePDF.new("japanese_fonts.pdf").fonts(true)
|
156
|
+
# CombinePDF.register_font_from_pdf_object :david, fonts[0]
|
157
|
+
#
|
158
|
+
# VERY LIMITTED SUPPORT:
|
159
|
+
# - at the moment it only imports Type0 fonts.
|
160
|
+
# - also, to extract the Hash of the actual font object you were looking for, is not a trivial matter. I do it on the console.
|
161
|
+
# font_name:: a Symbol with the name of the font registry. if the fonts exists in the library, it will be overwritten!
|
162
|
+
# font_object:: a Hash in the internal format recognized by CombinePDF, that represents the font object.
|
163
|
+
def register_existing_font(font_name, font_object)
|
164
|
+
Fonts.register_font_from_pdf_object font_name, font_object
|
165
|
+
end
|
147
166
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
#
|
152
|
-
# example:
|
153
|
-
# fonts = CombinePDF.new("japanese_fonts.pdf").fonts(true)
|
154
|
-
# CombinePDF.register_font_from_pdf_object :david, fonts[0]
|
155
|
-
#
|
156
|
-
# VERY LIMITTED SUPPORT:
|
157
|
-
# - at the moment it only imports Type0 fonts.
|
158
|
-
# - also, to extract the Hash of the actual font object you were looking for, is not a trivial matter. I do it on the console.
|
159
|
-
# font_name:: a Symbol with the name of the font registry. if the fonts exists in the library, it will be overwritten!
|
160
|
-
# font_object:: a Hash in the internal format recognized by CombinePDF, that represents the font object.
|
161
|
-
def register_existing_font font_name, font_object
|
162
|
-
Fonts.register_font_from_pdf_object font_name, font_object
|
163
|
-
end
|
164
|
-
def register_font_from_pdf_object font_name, font_object
|
165
|
-
register_existing_font font_name, font_object
|
166
|
-
end
|
167
|
+
def register_font_from_pdf_object(font_name, font_object)
|
168
|
+
register_existing_font font_name, font_object
|
169
|
+
end
|
167
170
|
end
|