thinreports 0.8.0 → 0.8.1

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: 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