pdf_watermark 0.1.6 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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