pdf_watermark 0.1.6 → 0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 523bc19fdfbb123b16f40b89259eae28c0857920
4
- data.tar.gz: 24971ecaa3890c432f2adc4555078117c12d95e7
3
+ metadata.gz: a4b337c7d1c0f23cf1345a9feb4081b8e80c3ff3
4
+ data.tar.gz: 09f3589967afc9d1e17b593b59769d882dfbe5c1
5
5
  SHA512:
6
- metadata.gz: 751aeef5401b8ea5275a9fd5e689e603fcb0dcf5e507ff46a6b0abc5b67760b5744b1678eefd979a072ea6df58cdb6aea559d6f578c0913b7212ceee3782a909
7
- data.tar.gz: 7460fae2f2873174361d7dae45e82e47114477249cb78adaf7135bf03b4594459af05fe1cc051da96f79f6f286e56ad5d8007649effed7dc83a292ed5af28f60
6
+ metadata.gz: b0a10564a127ece015b0b14f26c66d4fba19478b9f0e453b57ad2eaef52b4c277a271f7defff1983fc527f3872912c264e9a36d88c926fc76a3c25cec48a1a9e
7
+ data.tar.gz: 43dfbdb95c8711ed2c0a058185878f060e560fdc42e5115a2357a2b9adfc4d67b6cacf4182cc70b99fcab9084113f7a28fa8b7730221acecac50c2487c038bd2
data/.gitignore CHANGED
@@ -8,5 +8,6 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  /.idea
11
- /spec/data/result.pdf
11
+ /spec/data/*
12
+ !/spec/data/test.pdf
12
13
  *.gem
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source 'https://ruby.taobao.org/'
1
+ source 'https://gems.ruby-china.org/'
2
2
  # Specify your gem's dependencies in pdf_watermark.gemspec
3
3
  gemspec
@@ -0,0 +1,47 @@
1
+ require 'hexapdf/font/true_type/font'
2
+ require 'hexapdf/content/canvas'
3
+ module PdfWatermark
4
+ module Font
5
+ def width_of(string, font_size)
6
+ scale = font_size / 1000.0
7
+ string.codepoints.inject(9) do |memo, code|
8
+ memo + character_width(code)
9
+ end * scale
10
+ end
11
+
12
+ def character_width(code)
13
+ return 0 unless glyph_index[code]
14
+ return 0.0 if code == 10
15
+ self[:hmtx][glyph_index[code]].advance_width * scale_factor
16
+ end
17
+
18
+ def glyph_index
19
+ @glyph_index ||= self[:cmap].preferred_table
20
+ end
21
+
22
+ def scale_factor
23
+ @scale_factor ||= 1000.0 / self[:head].units_per_em
24
+ end
25
+ end
26
+
27
+ module Canvas
28
+ def width_of(string, font_size = nil)
29
+ self.font.wrapped_font.width_of(string, font_size || self.graphics_state.font_size) +
30
+ (string.length * self.graphics_state.character_spacing)
31
+ end
32
+ end
33
+
34
+ module FontDescriptor
35
+ def perform_validation
36
+ descent = self[:Descent]
37
+ if descent && descent > 0
38
+ self[:Descent] = - descent
39
+ end
40
+ super
41
+ end
42
+ end
43
+ end
44
+
45
+ HexaPDF::Font::TrueType::Font.include(PdfWatermark::Font)
46
+ HexaPDF::Content::Canvas.include(PdfWatermark::Canvas)
47
+ HexaPDF::Type::FontDescriptor.prepend(PdfWatermark::FontDescriptor)
@@ -1,3 +1,3 @@
1
1
  module PdfWatermark
2
- VERSION = "0.1.6"
2
+ VERSION = "0.2"
3
3
  end
@@ -0,0 +1,57 @@
1
+ require 'hexapdf'
2
+ require 'hexapdf/utils/math_helpers'
3
+ require 'pdf_watermark/font'
4
+ module PdfWatermark
5
+ module WaterMark
6
+ class Base
7
+ include HexaPDF::Utils::MathHelpers
8
+
9
+ def initialize(mark_string, source_path, options)
10
+ @source_path = source_path
11
+ @options = options
12
+ source_size = page_size
13
+ @content_width = source_size[0] - (options[:margin][1] + options[:margin][3])
14
+ @content_height = source_size[1] - (options[:margin][0] + options[:margin][2])
15
+ @angle = options[:angle] == :diagonal ? rad_to_deg(Math.atan(@content_height.to_f/@content_width.to_f)) : options[:angle]
16
+ @mark_string = mark_string
17
+
18
+ @font_size = @options[:font_size]
19
+ if @font_size.is_a?(String)
20
+ if @font_size =~ /(\d+[.]?\d*)%/
21
+ @font_size = ($1.to_f / 100) * @content_width
22
+ @font_size = [@font_size, @options[:max_font_size]].min
23
+ @font_size = [@font_size, @options[:min_font_size]].max
24
+ else
25
+ @font_size = @font_size.to_i
26
+ end
27
+ end
28
+ @options[:font]
29
+ @x = @options[:x] || 0
30
+ @y = @options[:y] || @content_height
31
+
32
+ @font = load_font(@options[:font], :watermark_font)
33
+ end
34
+
35
+
36
+ protected
37
+
38
+ def load_font(font_path, name = nil)
39
+ font_name = name.nil? ? File.basename(font_path, '.ttf') : name
40
+ map = {}
41
+ map[font_name] = { none: font_path }
42
+ document.config['font.map'].merge!(map)
43
+
44
+ document.fonts.load(font_name).wrapped_font
45
+ end
46
+
47
+ def page_size
48
+ box = document.pages[0].box.value
49
+ [box[2] - box[0], box[3] - box[1]]
50
+ end
51
+
52
+ def document
53
+ @document ||= HexaPDF::Document.open(@source_path)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,42 @@
1
+ require 'pdf_watermark/water_mark/base'
2
+ module PdfWatermark
3
+ module WaterMark
4
+ class Repeated < PdfWatermark::WaterMark::Base
5
+
6
+ def initialize(*args)
7
+ super(*args)
8
+ end
9
+
10
+ def render
11
+ document.pages.each do |page|
12
+ canvas = page.canvas(type: :overlay)
13
+ canvas.font(:watermark_font, size: @font_size)
14
+ canvas.fill_color(@options[:font_color])
15
+ box_height = @font_size
16
+ box_width = canvas.width_of(@mark_string)
17
+ temp_y = @y
18
+ indent = false
19
+ offset_x = box_width + @options[:repeat_offset] * @font_size
20
+ offset_y = box_height + @options[:repeat_offset] * @font_size
21
+
22
+
23
+ canvas.opacity(fill_alpha: @options[:transparent]) do
24
+ canvas.rotate(@angle, origin: [@content_width/2, @content_height/2]) do
25
+ while temp_y > 0 do
26
+ temp_x = indent ? (offset_x / 2.0) : 0
27
+ while temp_x <= @content_width
28
+ canvas.text(@mark_string, at: [temp_x, temp_y])
29
+ temp_x += offset_x
30
+ end
31
+ temp_y -= offset_y
32
+ indent = !indent
33
+ end
34
+ end
35
+ end
36
+ end
37
+ document
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,50 @@
1
+ require 'pdf_watermark/water_mark/base'
2
+ module PdfWatermark
3
+ module WaterMark
4
+ class Single < PdfWatermark::WaterMark::Base
5
+
6
+ def initialize(*args)
7
+ super(*args)
8
+ end
9
+
10
+ def render
11
+ document.pages.each do |page|
12
+ canvas = page.canvas(type: :overlay)
13
+ canvas.fill_color(@options[:font_color])
14
+ canvas.font(:watermark_font, size: 18)
15
+ rad = deg_to_rad(@angle)
16
+
17
+ if Math.tan(rad) < @content_height.to_f/@content_width.to_f
18
+ max_text_width = (@content_width/Math.cos(rad)).abs
19
+ else
20
+ max_text_width = (@content_height/Math.sin(rad)).abs
21
+ end
22
+ # if user does not provide the font size, scale to fill the whole page
23
+ if @font_size
24
+ canvas.font_size(@font_size)
25
+ else
26
+ @font_size = calculated_font_size(canvas, @mark_string, @options[:max_font_size], max_text_width)
27
+ canvas.font_size(@font_size)
28
+ @x = (@content_width - canvas.width_of(@mark_string))/2 + @options[:margin][3]
29
+ @y = @content_height / 2 + @options[:margin][2]
30
+ end
31
+ canvas.opacity(fill_alpha: @options[:transparent]) do
32
+ canvas.rotate(@angle, origin: [@content_width/2, @content_height/2]) do
33
+ canvas.text(@mark_string, at: [@x, @y])
34
+ end
35
+ end
36
+ end
37
+ document
38
+ end
39
+
40
+ def calculated_font_size(canvas, string, max_font, max_width)
41
+ text_width = canvas.width_of(string, max_font)
42
+ if text_width > max_width
43
+ (max_width - canvas.graphics_state.character_spacing * string.size) / string.size
44
+ else
45
+ max_font
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/pdf_watermark.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  require "pdf_watermark/version"
2
- require 'pdf_watermark/water_mark'
3
- require 'combine_pdf'
2
+ require 'pdf_watermark/water_mark/repeated'
3
+ require 'pdf_watermark/water_mark/single'
4
4
  module PdfWatermark
5
5
 
6
+ include HexaPDF::Encryption::StandardSecurityHandler::Permissions
7
+
6
8
  LIB_DIR = File.dirname(File.realpath(__FILE__))
7
- BASEDIR = File.join(LIB_DIR, '..')
8
- FONT_DIR = File.join(LIB_DIR, '..', 'fonts')
9
+ BASEDIR = File.realpath File.join(LIB_DIR, '..')
10
+ FONT_DIR = File.realpath File.join(LIB_DIR, '..', 'fonts')
9
11
  REPEAT_X_OFFSET = 80
10
12
  REPEAT_Y_OFFSET = 80
11
13
 
@@ -26,23 +28,25 @@ module PdfWatermark
26
28
  }
27
29
  options = default.merge(options)
28
30
 
29
- source_pdf = CombinePDF.load(source, allow_optional_content: true)
30
- source_size = page_size(source_pdf.pages[0])
31
+ if options[:mode].to_sym == :repeat
32
+ document = PdfWatermark::WaterMark::Repeated.new(mark_string, source, options).render
33
+ else
34
+ document = PdfWatermark::WaterMark::Single.new(mark_string, source, options).render
35
+ end
31
36
 
32
- options[:source_size] = source_size
33
- options[:content_width] ||= source_size[0] - (options[:margin][1] + options[:margin][3])
34
- options[:content_height] ||= source_size[1] - (options[:margin][0] + options[:margin][2])
37
+ if options[:read_only]
38
+ permissions = 1
39
+ document.encrypt(name: :Standard, owner_password: options[:password], permissions: permissions)
40
+ end
35
41
 
36
- wm = WaterMark.new(mark_string, options).render
37
- water_mark_pdf = CombinePDF.parse(wm, allow_optional_content: true).pages[0]
38
42
 
39
- source_pdf.pages.each do |page|
40
- page << water_mark_pdf
41
- end
42
- if destination.nil?
43
- source_pdf.to_pdf
43
+ if destination
44
+ document.write(destination)
44
45
  else
45
- source_pdf.save destination
46
+ StringIO.open('', 'wb') do |io|
47
+ document.write(io)
48
+ io.string
49
+ end
46
50
  end
47
51
  end
48
52
 
@@ -4,27 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'pdf_watermark/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "pdf_watermark"
8
- spec.version = PdfWatermark::VERSION
9
- spec.authors = ["bruce"]
10
- spec.email = ["shibocuhk@gmail.com"]
7
+ spec.name = "pdf_watermark"
8
+ spec.version = PdfWatermark::VERSION
9
+ spec.authors = ["bruce"]
10
+ spec.email = ["shibocuhk@gmail.com"]
11
11
 
12
- spec.summary = %q{pdf watermark}
13
- spec.description = %q{add watermark to pdf files}
14
- spec.homepage = "http://bruceshi.me"
15
- spec.license = "MIT"
12
+ spec.summary = %q{pdf watermark}
13
+ spec.description = %q{add watermark to pdf files}
14
+ spec.homepage = "http://bruceshi.me"
15
+ spec.license = "MIT"
16
16
 
17
17
  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
18
  # delete this section to allow pushing this gem to any host.
19
19
 
20
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
- spec.bindir = "exe"
22
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ spec.bindir = "exe"
22
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ['lib']
24
24
  spec.test_files = Dir['spec/*_spec.rb']
25
25
 
26
- spec.add_runtime_dependency('prawn', '~> 2.1', '>= 2.1.0')
27
- spec.add_runtime_dependency('combine_pdf', '~> 0.2.34')
26
+ spec.add_runtime_dependency('hexapdf', '~> 0.3')
28
27
 
29
28
  spec.add_development_dependency('bundler', '~> 1.10')
30
29
  spec.add_development_dependency('rake', '~> 10.0')
metadata CHANGED
@@ -1,49 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pdf_watermark
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - bruce
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-27 00:00:00.000000000 Z
11
+ date: 2017-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: prawn
14
+ name: hexapdf
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.1'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 2.1.0
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '2.1'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 2.1.0
33
- - !ruby/object:Gem::Dependency
34
- name: combine_pdf
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: 0.2.34
19
+ version: '0.3'
40
20
  type: :runtime
41
21
  prerelease: false
42
22
  version_requirements: !ruby/object:Gem::Requirement
43
23
  requirements:
44
24
  - - "~>"
45
25
  - !ruby/object:Gem::Version
46
- version: 0.2.34
26
+ version: '0.3'
47
27
  - !ruby/object:Gem::Dependency
48
28
  name: bundler
49
29
  requirement: !ruby/object:Gem::Requirement
@@ -147,8 +127,11 @@ files:
147
127
  - bin/setup
148
128
  - fonts/SourceHanSansSC-Regular.ttf
149
129
  - lib/pdf_watermark.rb
130
+ - lib/pdf_watermark/font.rb
150
131
  - lib/pdf_watermark/version.rb
151
- - lib/pdf_watermark/water_mark.rb
132
+ - lib/pdf_watermark/water_mark/base.rb
133
+ - lib/pdf_watermark/water_mark/repeated.rb
134
+ - lib/pdf_watermark/water_mark/single.rb
152
135
  - pdf_watermark.gemspec
153
136
  homepage: http://bruceshi.me
154
137
  licenses:
@@ -170,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
153
  version: '0'
171
154
  requirements: []
172
155
  rubyforge_project:
173
- rubygems_version: 2.4.5
156
+ rubygems_version: 2.6.8
174
157
  signing_key:
175
158
  specification_version: 4
176
159
  summary: pdf watermark
@@ -1,129 +0,0 @@
1
- require 'prawn'
2
- module PdfWatermark
3
- class WaterMark
4
- include Prawn::View
5
-
6
- def initialize(mark_string, options)
7
- @options = options
8
- @content_width = options[:content_width]
9
- @content_height = options[:content_height]
10
- @angle = options[:angle] == :diagonal ? rad_to_degree(Math.atan(@content_height.to_f/@content_width.to_f)) : options[:angle]
11
- @mark_string = mark_string
12
- @font_size = @options[:font_size]
13
- if @font_size.is_a?(String)
14
- if @font_size =~ /(\d+[.]?\d*)%/
15
- @font_size = ($1.to_f / 100) * @content_width
16
- @font_size = [@font_size, @options[:max_font_size]].min
17
- @font_size = [@font_size, @options[:min_font_size]].max
18
- else
19
- @font_size = @font_size.to_i
20
- end
21
- end
22
-
23
- @font = @options[:font]
24
- @x = @options[:x] || 0
25
- @y = @options[:y] || @content_height
26
-
27
- end
28
-
29
- def render
30
- case @options[:mode]
31
- when :fill
32
- fill_content
33
- when :repeat
34
- repeat_content
35
- else
36
- repeat_content
37
- end
38
- document.render
39
- end
40
-
41
- def fill_content
42
- @max_text_width = @content_width
43
-
44
- rad = degree_to_rad(@angle)
45
- if Math.tan(rad) < @content_height.to_f/@content_width.to_f
46
- @max_text_width = (@content_width/Math.cos(rad)).abs
47
- else
48
- @max_text_width = (@content_height/Math.sin(rad)).abs
49
- end
50
- @font_size ||= calculated_font_size(@mark_string, @options[:max_font_size], @max_text_width)
51
-
52
- font(@options[:font]) do
53
- bounding_box([@x, @y], width: @content_width, height: @content_height) do
54
- rotate @angle, origin: [@content_width/2, @content_height/2] do
55
- transparent(@options[:transparent]) do
56
- formatted_text_box(
57
- [{ text: @mark_string, color: @options[:font_color] }],
58
- at: [@x - (@max_text_width - @content_width) / 2, @y],
59
- width: @max_text_width, height: @content_height,
60
- align: @options[:align], valign: @options[:valign],
61
- size: @font_size,
62
- )
63
- end
64
- end
65
- end
66
- end
67
- end
68
-
69
- def repeat_content
70
- font(@font) do
71
- transparent(@options[:transparent]) do
72
- box_height = @font_size
73
- box_width = text_width(@mark_string, @font_size)
74
- temp_y = @y
75
- indent = false
76
- offset_x = box_width + @options[:repeat_offset] * @font_size
77
- offset_y = box_height + @options[:repeat_offset] * @font_size
78
- bounding_box([@x, @y], width: @content_width, height: @content_height) do
79
- while temp_y > 0 do
80
- rotate @angle, origin: [@content_width/2, @content_height/2] do
81
- temp_x = indent ? (offset_x / 2.0) : 0
82
- while temp_x <= @content_width
83
- formatted_text_box(
84
- [{ text: @mark_string, color: @options[:font_color] }],
85
- at: [temp_x, temp_y],
86
- width: box_width, height: box_height,
87
- align: @options[:align], valign: @options[:valign],
88
- size: @font_size,
89
- )
90
- temp_x += offset_x
91
- end
92
- end
93
- temp_y -= offset_y
94
- indent = !indent
95
- end
96
- end
97
- end
98
- end
99
- end
100
-
101
-
102
- protected
103
- def text_width(text, size)
104
- document.width_of(text, size: size, margin: 0, left_margin: 0, right_margin: 0).ceil
105
-
106
- end
107
-
108
- def calculated_font_size(text, size, max)
109
- text_width = self.text_width(text, size)
110
- if text_width > max
111
- (max / text.size).floor
112
- else
113
- size
114
- end
115
- end
116
-
117
- def document
118
- @document ||= Prawn::Document.new(page_size: @options[:source_size], margin: @options[:margin], font: @font)
119
- end
120
-
121
- def degree_to_rad(angle)
122
- angle * Math::PI / 180
123
- end
124
-
125
- def rad_to_degree(rad)
126
- rad * 180 / Math::PI
127
- end
128
- end
129
- end