thinreports 0.8.0 → 0.8.1

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: 52d5b58f89d69995f780cad5fee9ee7d9476ba48
4
- data.tar.gz: 1ac6e9e07a7fb3776137cd9d4663fdbae832418e
3
+ metadata.gz: 84effddcf79a2fdea2e5fbaa87f1f8fb5e170c47
4
+ data.tar.gz: e4e954c45566cc49b69fd0bb7a46700e5970ac93
5
5
  SHA512:
6
- metadata.gz: 5bd6e15835d02834f428649d474bcdf398e1e550f406652c1827539628e2e83b1aa4e8a550e2003d1521ed950f011dbb9b858101bde330c2eb44322a68eb6dad
7
- data.tar.gz: 25ec1244b98b2da6558b05ab5e36650993574c26aae541ed077934169e4f9ae0154b2a0e7153bedfa668e8f3107ca586fd0cac1b67cab461854559f3c12929de
6
+ metadata.gz: 5f542af1a90cdcb26fb7ba24cb94fad484367b5e9dba8f6ebf527b5c5307a3fca546dd25b6e9f71ec32c0c37a2e93ea9c71a2baefa3641c60d46f47316a43e7c
7
+ data.tar.gz: dfad16c6e2cf3afae658344c6dbd20817a77408c3cc4d81e51fef0d520a20ec95050bc2141117bbdae8cc59a49d2fad28ced5f5a9935c9e0e56177477293373a
data/.travis.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  language: ruby
2
2
 
3
+ sudo: false
3
4
  rvm:
4
5
  - 1.9.3
5
6
  - 2.0.0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.8.1
2
+
3
+ ### Add OPTIONAL feature for converting a palette-based PNG w. transparency
4
+
5
+ This feature is **DISABLED** by default. Please see [PR#32](https://github.com/thinreports/thinreports-generator/pull/32) for further details.
6
+
1
7
  ## 0.8.0
2
8
 
3
9
  This release is a stepping stone to next major version 1.0.0 release.
@@ -113,11 +119,13 @@ See [Issue #18](https://github.com/thinreports/thinreports-generator/issues/18)
113
119
  # New setter, same as `page.item(:text_block).value = 'tblock value'`
114
120
  page[:text_block] = 'tblock value'
115
121
  # New getter, same as `page.item(:text_block).value`
116
- page[:text_block] # => "tblock value"
122
+ page[:text_block] # => <Tblock>
123
+ page[:text_block].value # => "tblock value"
117
124
  page.item(:text_block).value # => "tblock value"
118
125
 
119
126
  page[:image_block] = '/path/to/image.png'
120
- page[:image_block] # => "/path/to/image.png"
127
+ page[:image_block].src # => "/path/to/image.png"
128
+ page.item(:image_block).src # => "/path/to/image.png"
121
129
  ```
122
130
 
123
131
  See [Issue #22](https://github.com/thinreports/thinreports-generator/issues/22) for further details.
@@ -0,0 +1,9 @@
1
+ # coding: utf-8
2
+
3
+ example :palleted_png, 'Rendering a palleted PNG with transparency' do |t|
4
+ report = Thinreports::Report.new layout: t.layout_filename
5
+ report.start_new_page do |page|
6
+ page.item(:image).src = t.resource('palleted_png.png')
7
+ end
8
+ report.generate filename: t.output_filename
9
+ end
@@ -0,0 +1 @@
1
+ {"version":"0.8.2","config":{"title":"","option":{},"page":{"paper-type":"A4","orientation":"portrait","margin-top":"20","margin-bottom":"20","margin-left":"20","margin-right":"20"}},"svg":"<svg width=\"595.2\" height=\"841.8\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" preserveAspectRatio=\"none\" viewBox=\"0 0 595.2 841.8\"><g class=\"canvas\"><image image-rendering=\"optimizeQuality\" preserveAspectRatio=\"none\" class=\"s-image\" x-display=\"true\" x-id=\"\" id=\"goog_604222137\" x=\"20\" y=\"20\" x-natural-width=\"200\" x-natural-height=\"200\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:href=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAD1BMVEX/AAD/AAD/AAD/AAD////Icf73AAAAA3RSTlO/fz8qtTX3AAAAAWJLR0QEj2jZUQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAMtJREFUeNrtzwERgEAAw7AH/HtGBr2RKGjPM+J8HWDESJyRGiM1RmqM1BipMVJjpMZIjZEaIzVGaozUGKkxUmOkxkiNkRojNUZqjNQYqTFSszMCAAAAAAAAAAAA/3SNMFJjpMZIjZEaIzVGaozUGKkxUmOkxkiNkRojNUZqjNQYqTFSY6TGSI2RGiM1RmqM1Bip2Rm5RxipMVJjpMZIjZEaIzVGaozUGKkxUmOkxkiNkRojNUZqjNQYqTFSY6TGSI2RGiM1RmqM1MyMvMkE6mEsuD8jAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE0LTEwLTA5VDEwOjM1OjI1KzAyOjAwxhRVNQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNC0xMC0wOVQxMDozNToyNSswMjowMLdJ7YkAAAAASUVORK5CYII=\" width=\"200\" height=\"200\"/><!--SHAPE{\"type\":\"s-iblock\",\"id\":\"image\",\"display\":\"true\",\"desc\":null,\"box\":{\"x\":278.2,\"y\":20,\"width\":266.7,\"height\":223.7},\"position-x\":\"left\",\"position-y\":\"top\",\"svg\":{\"tag\":\"image\",\"attrs\":{\"x\":278.2,\"y\":20,\"width\":266.7,\"height\":223.7}}}SHAPE--><!--LAYOUT<g xmlns=\"http://www.w3.org/2000/svg\" class=\"s-iblock\" x-display=\"true\" id=\"goog_604222139\" x-id=\"image\" x-width=\"266.7\" x-height=\"223.7\" x-left=\"278.2\" x-top=\"20\"><rect class=\"s-iblock-box\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"0.2\" width=\"266.7\" height=\"223.7\" x=\"278.2\" y=\"20\"/><image width=\"16\" height=\"16\" class=\"s-iblock-mark\" opacity=\"0.5\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:href=\"assets/icons/x-image-mark.svg\" display=\"inline\" x=\"403.2\" y=\"123\"/><text class=\"s-iblock-id\" font-size=\"10.5\" font-family=\"Helvetica\" font-weight=\"normal\" font-style=\"normal\" text-decoration=\"none\" text-anchor=\"start\" kerning=\"auto\" stroke=\"none\" fill=\"#0096fd\" fill-opacity=\"1\" x=\"282.2\" y=\"31\">image</text></g>LAYOUT--></g></svg>","state":{"layout-guide":[]}}
@@ -15,6 +15,7 @@ module Thinreports
15
15
  class Configuration
16
16
  def initialize
17
17
  @fallback_fonts = []
18
+ @convert_palleted_transparency_png = false
18
19
  end
19
20
 
20
21
  # @return [Array<String>]
@@ -33,6 +34,19 @@ module Thinreports
33
34
  @fallback_fonts = font_names.is_a?(Array) ? font_names : [font_names]
34
35
  end
35
36
 
37
+ # @return [Boolean]
38
+ # @see https://github.com/thinreports/thinreports-generator/pull/32
39
+ attr_reader :convert_palleted_transparency_png
40
+
41
+ # @param [Boolean]
42
+ # @example
43
+ # config.convert_palleted_transparency_png = true
44
+ # config.convert_palleted_transparency_png = false # default
45
+ # @see https://github.com/thinreports/thinreports-generator/pull/32
46
+ def convert_palleted_transparency_png=(enable)
47
+ @convert_palleted_transparency_png = enable
48
+ end
49
+
36
50
  # @return [Thinreports::Generator::Configuration]
37
51
  def generator
38
52
  @generator ||= Thinreports::Generator::Configuration.new
@@ -30,8 +30,9 @@ module Thinreports
30
30
  # @param [Thinreports::Core::Shape::Basic::Internal] shape
31
31
  def draw_shape_image(shape)
32
32
  x, y, w, h = shape.style.svg_attrs.values_at('x', 'y', 'width', 'height')
33
- base64image(extract_base64_string(shape.style.svg_attrs['xlink:href']),
34
- x, y, w, h)
33
+
34
+ image_type, image_data = extract_base64_string(shape.style.svg_attrs['xlink:href'])
35
+ base64image(image_type, image_data, x, y, w, h)
35
36
  end
36
37
 
37
38
  # @param [Thinreports::Core::Shape::ImageBlock::Internal] shape
@@ -116,9 +116,10 @@ module Thinreports
116
116
  end
117
117
 
118
118
  # @param [String] xlink
119
- # @return [String]
119
+ # @return [Array<string>] returns ['image/png', '<base64 data>']
120
120
  def extract_base64_string(xlink)
121
- xlink.sub(%r{^data:image/[a-z]+?;base64,}, '')
121
+ _, type, data = xlink.match(%r|^data:(image/[a-z]+?);base64,(.+)|).to_a
122
+ [type, data]
122
123
  end
123
124
  end
124
125
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'tempfile'
4
4
  require 'base64'
5
+ require 'digest/md5'
6
+ require 'chunky_png'
5
7
 
6
8
  module Thinreports
7
9
  module Generator
@@ -17,14 +19,15 @@ module Thinreports
17
19
  pdf.image(filename, at: pos(x, y), width: w, height: h)
18
20
  end
19
21
 
22
+ # @param [String] image_type Mime-type of image
20
23
  # @param [String] base64
21
24
  # @param [Numeric, Strng] x
22
25
  # @param [Numeric, Strng] y
23
26
  # @param [Numeric, Strng] w
24
27
  # @param [Numeric, Strng] h
25
- def base64image(base64, x, y, w, h)
26
- image = create_temp_imagefile(base64)
27
- image(image.path, x, y, w, h)
28
+ def base64image(image_type, base64_data, x, y, w, h)
29
+ image_path = normalize_image_from_base64(image_type, base64_data)
30
+ image(image_path, x, y, w, h)
28
31
  end
29
32
 
30
33
  # @param file (see Prawn#image)
@@ -36,34 +39,126 @@ module Thinreports
36
39
  # @option options [:left, :center, :right] :position_x (:left)
37
40
  # @option options [:top, :center, :bottom] :position_y (:top)
38
41
  def image_box(file, x, y, w, h, options = {})
42
+ image_path = normalize_image_from_file(file)
43
+
39
44
  w, h = s2f(w, h)
40
45
  pdf.bounding_box(pos(x, y), width: w, height: h) do
41
- pdf.image(file, position: options[:position_x] || :left,
42
- vposition: options[:position_y] || :top,
43
- auto_fit: [w, h])
46
+ pdf.image(image_path, position: options[:position_x] || :left,
47
+ vposition: options[:position_y] || :top,
48
+ auto_fit: [w, h])
44
49
  end
45
50
  end
46
51
 
47
- private
52
+ def normalize_image_from_base64(image_type, base64_string)
53
+ raw_image_data = Base64.decode64(base64_string)
54
+
55
+ image_data = if png_conversion_enabled? && image_type == 'image/png'
56
+ png_normalizer = PNGNormalizer.load_blob(raw_image_data)
57
+
58
+ if png_normalizer.need_normalize?
59
+ png_normalizer.normalize
60
+ else
61
+ raw_image_data
62
+ end
63
+ else
64
+ raw_image_data
65
+ end
66
+
67
+ image_id = Digest::MD5.hexdigest(base64_string)
68
+ create_temp_imagefile(image_id, image_data)
69
+ end
70
+
71
+ def normalize_image_from_file(filename)
72
+ extname = File.extname(filename)
73
+
74
+ return filename unless png_conversion_enabled?
75
+ return filename unless extname.downcase == '.png' || extname.empty?
76
+
77
+ return temp_image_registry[filename] if temp_image_registry.key?(filename)
78
+
79
+ png_normalizer = PNGNormalizer.load_file(filename)
80
+
81
+ # Returns the original filename when the image is not PNG.
82
+ return filename if png_normalizer.nil?
83
+ return filename unless png_normalizer.need_normalize?
84
+
85
+ image_id = filename
86
+ image_data = png_normalizer.normalize
87
+
88
+ create_temp_imagefile(image_id, image_data)
89
+ end
48
90
 
49
91
  def clean_temp_images
50
- temp_image_registry.each {|tmp| tmp.unlink }
92
+ temp_image_registry.each_value do |image_path|
93
+ File.delete(image_path) if File.exists?(image_path)
94
+ end
51
95
  end
52
96
 
53
97
  def temp_image_registry
54
- @temp_image_registry ||= []
98
+ @temp_image_registry ||= {}
55
99
  end
56
100
 
57
- # @param [String] base64
58
- # @return [Tempfile]
59
- def create_temp_imagefile(base64)
60
- file = Tempfile.new('trg-tmp-img')
61
- file.binmode
62
- file.write(Base64.decode64(base64))
63
- temp_image_registry << file
64
- file
65
- ensure
66
- file.close
101
+ # @param [String] image_id
102
+ # @param [String] image_data
103
+ # @return [String] imagefile path
104
+ def create_temp_imagefile(image_id, image_data)
105
+ temp_image_registry[image_id] ||= begin
106
+ file = Tempfile.new('temp-image')
107
+ file.binmode
108
+ file.write(image_data)
109
+ file.path
110
+ ensure
111
+ file.close
112
+ end
113
+ temp_image_registry[image_id]
114
+ end
115
+
116
+ def png_conversion_enabled?
117
+ Thinreports.config.convert_palleted_transparency_png
118
+ end
119
+
120
+ class PNGNormalizer
121
+ def self.load_file(filename)
122
+ image = ChunkyPNG::Image.from_file(filename)
123
+ datastream = ChunkyPNG::Datastream.from_file(filename)
124
+ self.new image, datastream
125
+ rescue ChunkyPNG::SignatureMismatch
126
+ # Returns nil if image is not PNG.
127
+ nil
128
+ end
129
+
130
+ def self.load_blob(data)
131
+ image = ChunkyPNG::Image.from_blob(data)
132
+ datastream = ChunkyPNG::Datastream.from_blob(data)
133
+ self.new image, datastream
134
+ rescue ChunkyPNG::SignatureMismatch
135
+ # Returns nil if image is not PNG.
136
+ nil
137
+ end
138
+
139
+ def initialize(image, datastream)
140
+ @image = image
141
+ @datastream = datastream
142
+ end
143
+
144
+ def need_normalize?
145
+ pallete_based? && transparency_chunk?
146
+ end
147
+
148
+ def normalize
149
+ @image.to_blob(color_mode: ChunkyPNG::COLOR_TRUECOLOR_ALPHA)
150
+ end
151
+
152
+ private
153
+
154
+ def pallete_based?
155
+ color_mode, depth = @image.palette.best_color_settings
156
+ color_mode == ChunkyPNG::COLOR_INDEXED
157
+ end
158
+
159
+ def transparency_chunk?
160
+ !@datastream.transparency_chunk.nil?
161
+ end
67
162
  end
68
163
  end
69
164
 
@@ -78,8 +78,9 @@ module Thinreports
78
78
  # @see #draw_svg_rect
79
79
  def draw_svg_image(elm)
80
80
  x, y, w, h = element_attributes_values_at(elm, 'x', 'y', 'width', 'height')
81
- base64image(extract_base64_string(elm.attributes['xlink:href']),
82
- x, y, w, h)
81
+
82
+ image_type, image_data = extract_base64_string(elm.attributes['xlink:href'])
83
+ base64image(image_type, image_data, x, y, w, h)
83
84
  end
84
85
 
85
86
  # @param [Hash] attributes
@@ -87,9 +87,6 @@ module Thinreports
87
87
  def initialize(options = {})
88
88
  @internal = Report::Internal.new(self, options)
89
89
  @start_page_number = 1
90
-
91
- @page_create_handler = nil
92
- @generate_handler = nil
93
90
  end
94
91
 
95
92
  # @yield [page]
@@ -12,7 +12,6 @@ module Thinreports
12
12
  attr_reader :events
13
13
 
14
14
  attr_accessor :page_create_handler
15
- attr_accessor :generate_handler
16
15
 
17
16
  # @param [Thinreports::Report::Base] report
18
17
  # @param options (see Thinreports::Report::Base#initialize)
@@ -28,7 +27,6 @@ module Thinreports
28
27
  @page_count = 0
29
28
 
30
29
  @page_create_handler = nil
31
- @generate_handler = nil
32
30
 
33
31
  @events = Report::Events.new
34
32
  end
@@ -63,11 +61,22 @@ module Thinreports
63
61
  finalize_current_page
64
62
  @finalized = true
65
63
 
66
- # [DEPRECATION] Please use Report::Base#on_generate callback instead.
64
+ # [DEPRECATION] You can do the same implements as the :generate Event
65
+ # with the following code:
66
+ #
67
+ # report = Thinreports::Report.new layout: 'foo.tlf'
68
+ # report.start_new_page do |page|
69
+ # page.item(:price).value = 100000
70
+ # end
71
+ #
72
+ # report.pages.each do |page|
73
+ # page[:title] = 'Common Title'
74
+ # end
75
+ #
76
+ # report.generate filename: 'foo.pdf'
77
+ #
67
78
  events.dispatch(Report::Events::Event.new(:generate,
68
79
  @report, nil, pages))
69
-
70
- @generate_handler.call(pages) if @generate_handler
71
80
  end
72
81
  end
73
82
 
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
3
  module Thinreports
4
- VERSION = '0.8.0'
4
+ VERSION = '0.8.1'
5
5
  end
Binary file
Binary file
Binary file
Binary file
Binary file
data/test/test_helper.rb CHANGED
@@ -8,6 +8,7 @@ require 'mocha/mini_test'
8
8
 
9
9
  require 'digest/sha1'
10
10
  require 'pathname'
11
+ require 'chunky_png'
11
12
  require 'thinreports'
12
13
 
13
14
  module Thinreports::TestHelper
@@ -48,6 +49,10 @@ module Thinreports::TestHelper
48
49
  ROOT.join('data', filename).to_s
49
50
  end
50
51
 
52
+ def read_data_file(filename)
53
+ File.read(data_file(filename))
54
+ end
55
+
51
56
  def temp_file(extname = 'pdf')
52
57
  filename = (('a'..'z').to_a + (0..9).to_a).shuffle[0, 8].join + ".#{extname}"
53
58
  temp_path.join(filename).to_s
@@ -4,11 +4,11 @@ require 'test_helper'
4
4
 
5
5
  class Thinreports::Generator::PDF::Graphics::TestAttributes < Minitest::Test
6
6
  include Thinreports::TestHelper
7
-
7
+
8
8
  def setup
9
9
  @pdf = Thinreports::Generator::PDF::Document.new
10
10
  end
11
-
11
+
12
12
  def test_common_graphic_attrs_should_return_converted_Hash_as_attributes
13
13
  result = @pdf.common_graphic_attrs('stroke' => '#ff0000',
14
14
  'stroke-width' => '1',
@@ -16,41 +16,41 @@ class Thinreports::Generator::PDF::Graphics::TestAttributes < Minitest::Test
16
16
  assert_equal result.values_at(:stroke, :stroke_width, :fill),
17
17
  ['#ff0000', '1', '#0000ff']
18
18
  end
19
-
19
+
20
20
  def test_common_graphic_attrs_should_return_the_stroke_dash_attribute_into_an_array
21
21
  result = @pdf.common_graphic_attrs('stroke-dasharray' => '3,5')
22
22
  assert_equal result[:stroke_dash], %w( 3 5 )
23
23
  end
24
-
24
+
25
25
  def test_common_graphic_attrs_should_return_nil_as_a_value_of_stroke_dash_attribute_when_value_is_none
26
26
  result = @pdf.common_graphic_attrs('stroke-dasharray' => 'none')
27
27
  assert_nil result[:stroke_dash]
28
28
  end
29
-
29
+
30
30
  def test_common_graphic_attrs_should_return_nil_as_a_value_of_stroke_dash_attribute_when_value_is_empty
31
31
  result = @pdf.common_graphic_attrs({})
32
32
  assert_nil result[:stroke_dash]
33
33
  end
34
-
34
+
35
35
  def test_common_graphic_attrs_should_set_0_as_a_value_of_stroke_width_attribute_when_opacity_is_0
36
36
  result = @pdf.common_graphic_attrs('stroke-width' => '1',
37
37
  'stroke-opacity' => '0')
38
38
  assert_equal result[:stroke_width], 0
39
39
  end
40
-
40
+
41
41
  def test_common_graphic_attrs_with_block
42
42
  result = @pdf.common_graphic_attrs('stroke' => '#ff0000') do |attrs|
43
43
  attrs[:stroke].gsub!(/0/, 'f')
44
44
  end
45
45
  assert_equal result[:stroke], '#ffffff'
46
46
  end
47
-
47
+
48
48
  def test_text_align_should_return_converted_value_type_of_Symbol
49
49
  aligns = ['start', 'middle', 'end', nil]
50
50
  assert_equal aligns.map {|a| @pdf.text_align(a) },
51
51
  [:left, :center, :right, :left]
52
52
  end
53
-
53
+
54
54
  def test_text_valign_should_return_converted_value_type_of_Symbol
55
55
  valigns = ['top', 'center', 'bottom', nil]
56
56
  assert_equal valigns.map {|a| @pdf.text_valign(a) },
@@ -62,76 +62,76 @@ class Thinreports::Generator::PDF::Graphics::TestAttributes < Minitest::Test
62
62
  assert_equal @pdf.text_word_wrap('none'), :none
63
63
  assert_equal @pdf.text_word_wrap(''), :none
64
64
  end
65
-
65
+
66
66
  def test_extract_base64_string
67
67
  base64 = 'data:image/png;base64,iVBORw0KGg1+/AAy/plYlzil'
68
68
  assert_equal @pdf.extract_base64_string(base64),
69
- 'iVBORw0KGg1+/AAy/plYlzil'
69
+ ['image/png', 'iVBORw0KGg1+/AAy/plYlzil']
70
70
  end
71
-
71
+
72
72
  def test_font_styles_should_return_bold_style_when_font_weight_is_bold
73
73
  assert_equal @pdf.font_styles('font-weight' => 'bold'), [:bold]
74
74
  end
75
-
75
+
76
76
  def test_font_styles_should_return_italic_style_when_font_style_is_italic
77
77
  assert_equal @pdf.font_styles('font-style' => 'italic'), [:italic]
78
78
  end
79
-
79
+
80
80
  def test_font_styles_should_return_underline_and_strikethrough_style_via_text_decoration
81
81
  assert_equal @pdf.font_styles('text-decoration' => 'underline line-through'),
82
82
  [:underline, :strikethrough]
83
83
  end
84
-
84
+
85
85
  def test_common_text_attrs_should_return_the_value_of_font_family_as_font
86
86
  result = @pdf.common_text_attrs('font-family' => 'IPAMincho')
87
87
  assert_equal result[:font], 'IPAMincho'
88
88
  end
89
-
89
+
90
90
  def test_common_text_attrs_should_return_the_value_of_font_size_as_size
91
91
  result = @pdf.common_text_attrs('font-size' => '12')
92
92
  assert_equal result[:size], '12'
93
93
  end
94
-
94
+
95
95
  def test_common_text_attrs_should_return_the_value_of_fill_color_as_color
96
96
  result = @pdf.common_text_attrs('fill' => '#000000')
97
97
  assert_equal result[:color], '#000000'
98
98
  end
99
-
99
+
100
100
  def test_common_text_attrs_should_return_the_value_of_text_anchor_as_align
101
101
  result = @pdf.common_text_attrs('text-anchor' => 'middle')
102
102
  assert_equal result[:align], :center
103
103
  end
104
-
104
+
105
105
  def test_common_text_attrs_should_return_the_value_of_text_styles_as_styles
106
106
  result = @pdf.common_text_attrs('font-weight' => 'bold')
107
107
  assert_equal result[:styles], [:bold]
108
108
  end
109
-
109
+
110
110
  def test_common_text_attrs_should_return_the_value_of_letter_spacing_as_letter_spacing
111
111
  result = @pdf.common_text_attrs('letter-spacing' => '5')
112
112
  assert_equal result[:letter_spacing], '5'
113
113
  end
114
-
114
+
115
115
  def test_common_text_attrs_should_return_the_value_of_kerning_as_letter_spacing
116
116
  result = @pdf.common_text_attrs('kerning' => '10')
117
117
  assert_equal result[:letter_spacing], '10'
118
118
  end
119
-
119
+
120
120
  def test_common_text_attrs_with_block
121
121
  result = @pdf.common_text_attrs('fill' => '#000000') do |attrs|
122
122
  attrs[:color].gsub!(/0/, 'f')
123
123
  end
124
124
  assert_equal result[:color], '#ffffff'
125
125
  end
126
-
126
+
127
127
  def test_text_letter_spacing_should_return_raw_value
128
128
  assert_equal @pdf.text_letter_spacing('10'), '10'
129
129
  end
130
-
130
+
131
131
  def test_text_letter_spacing_should_return_nil_when_value_is_normal
132
132
  assert_nil @pdf.text_letter_spacing('normal')
133
133
  end
134
-
134
+
135
135
  def test_text_letter_spacing_should_return_nil_when_value_is_auto
136
136
  assert_nil @pdf.text_letter_spacing('auto')
137
137
  end
@@ -0,0 +1,150 @@
1
+ # coding: utf-8
2
+
3
+ require 'test_helper'
4
+
5
+ require 'base64'
6
+ require 'digest/md5'
7
+
8
+ class Thinreports::Generator::PDF::Graphics::TestImage < Minitest::Test
9
+ include Thinreports::TestHelper
10
+
11
+ class TestImage
12
+ include Thinreports::Generator::PDF::Graphics
13
+ end
14
+
15
+ def test_normalize_image_from_base64_in_disabled
16
+ Thinreports.config.convert_palleted_transparency_png = false
17
+
18
+ test_image = TestImage.new
19
+ base64_image = Base64.encode64(read_data_file('image_normal.png'))
20
+
21
+ # Should not be normalized
22
+ TestImage::PNGNormalizer.stubs(:load_blob).never
23
+ test_image.normalize_image_from_base64('image/png', base64_image)
24
+
25
+ image_registry = test_image.temp_image_registry
26
+ assert_equal 1, image_registry.count
27
+ end
28
+
29
+ def test_normalize_image_from_file_in_disabled
30
+ Thinreports.config.convert_palleted_transparency_png = false
31
+
32
+ original_image_path = data_file('image_pallete_based.png')
33
+
34
+ test_image = TestImage.new
35
+ returned_image_path = test_image.normalize_image_from_file(original_image_path)
36
+
37
+ # Should not be normalized
38
+ assert_empty test_image.temp_image_registry
39
+ assert_same original_image_path, returned_image_path
40
+ end
41
+
42
+ def test_normalize_image_from_base64_with_normal_images
43
+ Thinreports.config.convert_palleted_transparency_png = true
44
+
45
+ [
46
+ ['image/png', 'image_normal.png'],
47
+ ['image/jpeg', 'image_normal.jpg']
48
+ ]
49
+ .each do |(image_type, image_file)|
50
+ base64_normal_image = Base64.encode64(read_data_file(image_file))
51
+
52
+ test_image = TestImage.new
53
+
54
+ normalized_image_path = test_image.normalize_image_from_base64(image_type, base64_normal_image)
55
+
56
+ image_registry = test_image.temp_image_registry
57
+ assert_equal 1, image_registry.count
58
+
59
+ image_id = Digest::MD5.hexdigest(base64_normal_image)
60
+ assert_includes image_registry.keys, image_id
61
+ assert_equal normalized_image_path, image_registry[image_id]
62
+
63
+ assert equal_image(data_file(image_file), normalized_image_path)
64
+
65
+ assert_same normalized_image_path,
66
+ test_image.normalize_image_from_base64(image_type, base64_normal_image)
67
+ end
68
+ end
69
+
70
+ def test_normalize_image_from_base64_with_palette_transparency_png
71
+ Thinreports.config.convert_palleted_transparency_png = true
72
+
73
+ base64_palleted_png = Base64.encode64(read_data_file('image_pallete_based.png'))
74
+
75
+ test_image = TestImage.new
76
+
77
+ normalized_image_path = test_image.normalize_image_from_base64('image/png', base64_palleted_png)
78
+
79
+ image_registry = test_image.temp_image_registry
80
+ assert_equal 1, image_registry.count
81
+
82
+ image_id = Digest::MD5.hexdigest(base64_palleted_png)
83
+ assert_includes image_registry.keys, image_id
84
+ assert_equal normalized_image_path, image_registry[image_id]
85
+
86
+ refute equal_image(data_file('image_pallete_based.png'), normalized_image_path)
87
+
88
+ assert_not_palette_based_transparency_png File.read(normalized_image_path)
89
+
90
+ # 2nd time
91
+ assert_same normalized_image_path,
92
+ test_image.normalize_image_from_base64('image/png', base64_palleted_png)
93
+ assert_equal 1, test_image.temp_image_registry.count
94
+ end
95
+
96
+ def test_normalize_image_from_file_with_normal_images
97
+ Thinreports.config.convert_palleted_transparency_png = true
98
+
99
+ [
100
+ data_file('image_normal.png'),
101
+ data_file('iamge_normal.jpg'),
102
+ data_file('image_normal_png_noext'),
103
+ data_file('image_normal_jpg_noext')
104
+ ]
105
+ .each do |original_image_path|
106
+ test_image = TestImage.new
107
+ image_path = test_image.normalize_image_from_file(original_image_path)
108
+
109
+ assert_equal original_image_path, image_path
110
+ assert_empty test_image.temp_image_registry
111
+ end
112
+ end
113
+
114
+ def test_normalize_image_from_file_with_palette_transparency_png
115
+ Thinreports.config.convert_palleted_transparency_png = true
116
+
117
+ original_image_path = data_file('image_pallete_based.png')
118
+
119
+ test_image = TestImage.new
120
+
121
+ normalized_image_path = test_image.normalize_image_from_file(original_image_path)
122
+ image_registry = test_image.temp_image_registry
123
+
124
+ assert_equal 1, image_registry.count
125
+ assert_includes image_registry.keys, original_image_path
126
+
127
+ refute equal_image(original_image_path, normalized_image_path)
128
+
129
+ assert_not_palette_based_transparency_png File.read(normalized_image_path)
130
+
131
+ # It should never called in 2nd time or subsequent.
132
+ TestImage::PNGNormalizer.stubs(:load_file).never
133
+ assert_same normalized_image_path,
134
+ test_image.normalize_image_from_file(original_image_path)
135
+ end
136
+
137
+ def assert_not_palette_based_transparency_png(data)
138
+ image_data = ChunkyPNG::Image.from_blob(data)
139
+ datastream = ChunkyPNG::Datastream.from_blob(data)
140
+
141
+ assert_equal ChunkyPNG::COLOR_INDEXED, image_data.palette.best_color_settings.first
142
+ assert_equal nil, datastream.transparency_chunk
143
+ end
144
+
145
+ def equal_image(expect_path, actual_path)
146
+ expect_image = File.binread(expect_path)
147
+ actual_image = File.binread(actual_path)
148
+ expect_image == actual_image
149
+ end
150
+ end
@@ -20,6 +20,14 @@ class Thinreports::TestConfig < Minitest::Test
20
20
  end
21
21
  end
22
22
 
23
+ def test_convert_palleted_transparency_png
24
+ config = Thinreports::Configuration.new
25
+ assert_equal false, config.convert_palleted_transparency_png
26
+
27
+ config.convert_palleted_transparency_png = true
28
+ assert_equal true, config.convert_palleted_transparency_png
29
+ end
30
+
23
31
  def test_fallback_fonts
24
32
  config = Thinreports::Configuration.new
25
33
 
data/thinreports.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
24
24
  s.require_paths = ['lib']
25
25
 
26
26
  s.add_dependency 'prawn', '1.3.0'
27
+ s.add_dependency 'chunky_png', '~> 1.3'
27
28
 
28
29
  s.add_development_dependency 'bundler', ['>= 1.0.0']
29
30
  s.add_development_dependency 'minitest', ['>= 5.0.0']
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinreports
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matsukei Co.,Ltd.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-30 00:00:00.000000000 Z
11
+ date: 2015-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prawn
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.3.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: chunky_png
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -133,6 +147,9 @@ files:
133
147
  - examples/list_page_number/list_page_number.tlf
134
148
  - examples/page_number/page_number.rb
135
149
  - examples/page_number/page_number.tlf
150
+ - examples/palleted_png/palleted_png.png
151
+ - examples/palleted_png/palleted_png.rb
152
+ - examples/palleted_png/palleted_png.tlf
136
153
  - examples/password_setting/password_setting.rb
137
154
  - examples/password_setting/password_setting.tlf
138
155
  - examples/report_callbacks/report_callbacks.rb
@@ -248,6 +265,11 @@ files:
248
265
  - lib/thinreports/report/page.rb
249
266
  - lib/thinreports/version.rb
250
267
  - test/data/font.ttf
268
+ - test/data/image_normal.jpg
269
+ - test/data/image_normal.png
270
+ - test/data/image_normal_jpg_noext
271
+ - test/data/image_normal_png_noext
272
+ - test/data/image_pallete_based.png
251
273
  - test/data/layout_block.tlf
252
274
  - test/data/layout_list.tlf
253
275
  - test/data/layout_list_noheader.tlf
@@ -301,6 +323,7 @@ files:
301
323
  - test/unit/core/test_shape.rb
302
324
  - test/unit/core/test_utils.rb
303
325
  - test/unit/generator/pdf/document/graphics/test_attributes.rb
326
+ - test/unit/generator/pdf/document/graphics/test_image.rb
304
327
  - test/unit/generator/pdf/document/graphics/test_text.rb
305
328
  - test/unit/generator/pdf/document/test_draw_shape.rb
306
329
  - test/unit/generator/pdf/document/test_font.rb
@@ -345,12 +368,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
345
368
  version: '0'
346
369
  requirements: []
347
370
  rubyforge_project:
348
- rubygems_version: 2.4.6
371
+ rubygems_version: 2.4.5
349
372
  signing_key:
350
373
  specification_version: 4
351
374
  summary: An open source report generation tool for Ruby.
352
375
  test_files:
353
376
  - test/data/font.ttf
377
+ - test/data/image_normal.jpg
378
+ - test/data/image_normal.png
379
+ - test/data/image_normal_jpg_noext
380
+ - test/data/image_normal_png_noext
381
+ - test/data/image_pallete_based.png
354
382
  - test/data/layout_block.tlf
355
383
  - test/data/layout_list.tlf
356
384
  - test/data/layout_list_noheader.tlf
@@ -404,6 +432,7 @@ test_files:
404
432
  - test/unit/core/test_shape.rb
405
433
  - test/unit/core/test_utils.rb
406
434
  - test/unit/generator/pdf/document/graphics/test_attributes.rb
435
+ - test/unit/generator/pdf/document/graphics/test_image.rb
407
436
  - test/unit/generator/pdf/document/graphics/test_text.rb
408
437
  - test/unit/generator/pdf/document/test_draw_shape.rb
409
438
  - test/unit/generator/pdf/document/test_font.rb