pageflow-linkmap-page 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -7
- data/app/jobs/pageflow/linkmap_page/process_source_image_file_job.rb +15 -0
- data/app/models/pageflow/linkmap_page/color_map_file.rb +85 -0
- data/app/models/pageflow/linkmap_page/masked_image_file.rb +51 -0
- data/app/models/pageflow/linkmap_page/processed_image_file.rb +60 -0
- data/db/migrate/20171106151700_create_masked_image_files.rb +21 -0
- data/db/migrate/20180111145100_create_color_map_files.rb +24 -0
- data/lib/pageflow/linkmap_page/engine.rb +16 -0
- data/lib/pageflow/linkmap_page/images/palette.png +0 -0
- data/lib/pageflow/linkmap_page/paperclip_processors/color_mask.rb +53 -0
- data/lib/pageflow/linkmap_page/paperclip_processors/colors.rb +213 -0
- data/lib/pageflow/linkmap_page/paperclip_processors/image_dimensions.rb +21 -0
- data/lib/pageflow/linkmap_page/paperclip_processors/invoke_callback.rb +12 -0
- data/lib/pageflow/linkmap_page/paperclip_tempfile.rb +18 -0
- data/lib/pageflow/linkmap_page/progress.rb +43 -0
- data/lib/pageflow/linkmap_page/version.rb +1 -1
- data/lib/tasks/pageflow_linkmap_page_tasks.rake +149 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 292a7ccb48c2c4a81d69edfee98d6567861b6df965ab6ff59d29384da534bcc8
|
4
|
+
data.tar.gz: 168b15ee2a7da3d2803e2d3ffac8625e3f2d154036e94b725efb261452f086b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5d6b1ddc936609028ccb8ef0a5c6c3ff0cd08ad122bfc8b9b0d9fb2e503d46c6d41df21c52b2eea16bb6cbad64b285f76ab8bdd0cbac43a5a42edce51cddc9c
|
7
|
+
data.tar.gz: 1699af6c8ab0e2719df1006f5f16e8947ae52860f7a0a7e863fa2e3c4cfa933b9045f28241cc35dfa3642284422ef57729857bbc56cef52a6afae69a0c27550f
|
data/CHANGELOG.md
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
-
### Version 1.
|
3
|
+
### Version 1.5.0
|
4
4
|
|
5
|
-
|
5
|
+
2019-01-23
|
6
6
|
|
7
|
-
[Compare changes](https://github.com/codevise/pageflow-linkmap-page/compare/1-
|
7
|
+
[Compare changes](https://github.com/codevise/pageflow-linkmap-page/compare/1-4-stable...v1.5.0)
|
8
8
|
|
9
|
-
- Add
|
10
|
-
([#
|
11
|
-
[#45](https://github.com/codevise/pageflow-linkmap-page/pull/45))
|
9
|
+
- Add Rake-Task to migrate existing linkmap pages to 2.0-version
|
10
|
+
([#47](https://github.com/codevise/pageflow-linkmap-page/pull/47))
|
12
11
|
|
13
12
|
See
|
14
|
-
[1-
|
13
|
+
[1-4-stable branch](https://github.com/codevise/pageflow-linkmap-page/blob/1-4-stable/CHANGELOG.md)
|
15
14
|
for previous changes.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
class ProcessSourceImageFileJob
|
4
|
+
@queue = :resizing
|
5
|
+
|
6
|
+
extend StateMachineJob
|
7
|
+
|
8
|
+
def self.perform_with_result(file, _options)
|
9
|
+
file.attachment = file.source_image_file.attachment
|
10
|
+
file.save!
|
11
|
+
:ok
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
class ColorMapFile < ProcessedImageFile
|
4
|
+
belongs_to :source_image_file, class_name: 'Pageflow::ImageFile'
|
5
|
+
|
6
|
+
# Prevent anti aliasing. Otherwise, when processing color map
|
7
|
+
# images, borders between areas are blurred.
|
8
|
+
SOURCE_FILE_OPTIONS = '-filter point'.freeze
|
9
|
+
|
10
|
+
PALETTE_PATH = Pageflow::LinkmapPage::Engine.root
|
11
|
+
.join('lib', 'pageflow', 'linkmap_page', 'images', 'palette.png').freeze
|
12
|
+
|
13
|
+
CONVERT_OPTIONS = [
|
14
|
+
'-quality 70',
|
15
|
+
'-interlace Plane',
|
16
|
+
'-dither None',
|
17
|
+
'-colors 64',
|
18
|
+
"-remap #{PALETTE_PATH}",
|
19
|
+
].join(' ').freeze
|
20
|
+
|
21
|
+
has_attached_file(:attachment,
|
22
|
+
Pageflow.config.paperclip_s3_default_options
|
23
|
+
.merge(styles: {
|
24
|
+
remapped: {
|
25
|
+
processors: [:thumbnail],
|
26
|
+
format: 'png',
|
27
|
+
geometry: GEOMETRY,
|
28
|
+
source_file_options: SOURCE_FILE_OPTIONS,
|
29
|
+
convert_options: CONVERT_OPTIONS
|
30
|
+
},
|
31
|
+
sprite: {
|
32
|
+
processors: [
|
33
|
+
:thumbnail,
|
34
|
+
:pageflow_linkmap_page_image_dimensions,
|
35
|
+
:pageflow_linkmap_page_image_colors
|
36
|
+
],
|
37
|
+
format: 'png',
|
38
|
+
geometry: '1024x1024>',
|
39
|
+
source_file_options: SOURCE_FILE_OPTIONS,
|
40
|
+
convert_options: CONVERT_OPTIONS,
|
41
|
+
progress_callback: :update_processing_progress
|
42
|
+
}
|
43
|
+
}))
|
44
|
+
|
45
|
+
do_not_validate_attachment_file_type :attachment
|
46
|
+
|
47
|
+
serialize :attachment_colors
|
48
|
+
|
49
|
+
def sprite_url
|
50
|
+
attachment.url(:sprite)
|
51
|
+
end
|
52
|
+
|
53
|
+
def width
|
54
|
+
attachment_width
|
55
|
+
end
|
56
|
+
|
57
|
+
def height
|
58
|
+
attachment_height
|
59
|
+
end
|
60
|
+
|
61
|
+
def url
|
62
|
+
attachment.url(:remapped)
|
63
|
+
end
|
64
|
+
|
65
|
+
def processed_attachment
|
66
|
+
attachment.styles[:remapped]
|
67
|
+
end
|
68
|
+
|
69
|
+
def present_colors
|
70
|
+
attachment_colors ? attachment_colors.keys : []
|
71
|
+
end
|
72
|
+
|
73
|
+
def bounding_box_for_color(color)
|
74
|
+
attachment_colors[color]
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def update_processing_progress(percent)
|
80
|
+
update_column(:processing_progress, percent)
|
81
|
+
touch
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
class MaskedImageFile < ProcessedImageFile
|
4
|
+
belongs_to :color_map_file, class_name: 'Pageflow::LinkmapPage::ColorMapFile'
|
5
|
+
|
6
|
+
STYLES = lambda do |attachment|
|
7
|
+
masked_image_file = attachment.instance
|
8
|
+
|
9
|
+
masked_image_file.present_styles.each_with_object({}) do |color, result|
|
10
|
+
result[color.to_sym] = {
|
11
|
+
processors: [
|
12
|
+
:thumbnail,
|
13
|
+
:pageflow_linkmap_page_color_mask,
|
14
|
+
:pageflow_linkmap_page_invoke_callback
|
15
|
+
],
|
16
|
+
color_map_attachment: masked_image_file.color_map_file.processed_attachment,
|
17
|
+
format: 'png',
|
18
|
+
geometry: GEOMETRY,
|
19
|
+
callback: :update_processing_progress
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
has_attached_file(:attachment,
|
25
|
+
Pageflow.config.paperclip_s3_default_options
|
26
|
+
.merge(styles: STYLES))
|
27
|
+
|
28
|
+
do_not_validate_attachment_file_type :attachment
|
29
|
+
|
30
|
+
def url_for_color(color)
|
31
|
+
attachment.url(color.to_sym)
|
32
|
+
end
|
33
|
+
|
34
|
+
def for_color(color)
|
35
|
+
attachment.styles[color.to_sym]
|
36
|
+
end
|
37
|
+
|
38
|
+
def present_styles
|
39
|
+
color_map_file.present_colors
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def update_processing_progress(style)
|
45
|
+
update_column(:processing_progress,
|
46
|
+
(present_styles.index(style.to_s) + 1) * 100 / present_styles.size)
|
47
|
+
touch
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
class ProcessedImageFile < ActiveRecord::Base
|
4
|
+
self.abstract_class = true
|
5
|
+
|
6
|
+
include Pageflow::UploadedFile
|
7
|
+
|
8
|
+
belongs_to :source_image_file, class_name: 'Pageflow::ImageFile'
|
9
|
+
|
10
|
+
GEOMETRY = '1920x1080^'.freeze
|
11
|
+
|
12
|
+
state_machine initial: 'not_processed' do
|
13
|
+
extend StateMachineJob::Macro
|
14
|
+
|
15
|
+
state 'not_processed'
|
16
|
+
state 'processing'
|
17
|
+
state 'processed'
|
18
|
+
|
19
|
+
event :process do
|
20
|
+
transition 'not_processed' => 'processing'
|
21
|
+
transition 'processing_failed' => 'processing'
|
22
|
+
end
|
23
|
+
|
24
|
+
job ProcessSourceImageFileJob do
|
25
|
+
on_enter 'processing'
|
26
|
+
result ok: 'processed'
|
27
|
+
result error: 'processing_failed'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def url
|
32
|
+
''
|
33
|
+
end
|
34
|
+
|
35
|
+
def original_url
|
36
|
+
''
|
37
|
+
end
|
38
|
+
|
39
|
+
def retry!
|
40
|
+
process!
|
41
|
+
end
|
42
|
+
|
43
|
+
def publish!
|
44
|
+
process!
|
45
|
+
end
|
46
|
+
|
47
|
+
def retryable?
|
48
|
+
processing_failed?
|
49
|
+
end
|
50
|
+
|
51
|
+
def ready?
|
52
|
+
processed?
|
53
|
+
end
|
54
|
+
|
55
|
+
def basename
|
56
|
+
'unused'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateMaskedImageFiles < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :pageflow_linkmap_page_masked_image_files do |t|
|
4
|
+
t.belongs_to :entry, index: true
|
5
|
+
t.string :state
|
6
|
+
t.string :rights
|
7
|
+
t.integer :parent_file_id
|
8
|
+
t.string :parent_file_model_type
|
9
|
+
|
10
|
+
t.belongs_to :source_image_file
|
11
|
+
t.belongs_to :color_map_file
|
12
|
+
t.string :attachment_file_name
|
13
|
+
t.integer :processing_progress, default: 0, null: false
|
14
|
+
|
15
|
+
t.timestamps
|
16
|
+
|
17
|
+
t.index([:parent_file_id, :parent_file_model_type],
|
18
|
+
name: 'index_masked_image_files_on_parent_id_and_parent_model_type')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class CreateColorMapFiles < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :pageflow_linkmap_page_color_map_files do |t|
|
4
|
+
t.belongs_to :entry, index: true
|
5
|
+
t.string :state
|
6
|
+
t.string :rights
|
7
|
+
t.integer :parent_file_id
|
8
|
+
t.string :parent_file_model_type
|
9
|
+
|
10
|
+
t.string :attachment_file_name
|
11
|
+
t.text :attachment_colors
|
12
|
+
t.integer :attachment_width
|
13
|
+
t.integer :attachment_height
|
14
|
+
t.integer :processing_progress, default: 0, null: false
|
15
|
+
|
16
|
+
t.belongs_to :source_image_file
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
|
20
|
+
t.index([:parent_file_id, :parent_file_model_type],
|
21
|
+
name: 'index_color_map_files_on_parent_id_and_parent_model_type')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -8,6 +8,22 @@ module Pageflow
|
|
8
8
|
config.autoload_paths << File.join(config.root, 'lib')
|
9
9
|
config.i18n.load_path += Dir[config.root.join('config', 'locales', '**', '*.yml').to_s]
|
10
10
|
|
11
|
+
initializer 'pageflow_linkmap_page.paperclip' do
|
12
|
+
Paperclip.configure do |config|
|
13
|
+
config.register_processor(:pageflow_linkmap_page_image_colors,
|
14
|
+
Pageflow::LinkmapPage::PaperclipProcessors::Colors)
|
15
|
+
|
16
|
+
config.register_processor(:pageflow_linkmap_page_color_mask,
|
17
|
+
Pageflow::LinkmapPage::PaperclipProcessors::ColorMask)
|
18
|
+
|
19
|
+
config.register_processor(:pageflow_linkmap_page_invoke_callback,
|
20
|
+
Pageflow::LinkmapPage::PaperclipProcessors::InvokeCallback)
|
21
|
+
|
22
|
+
config.register_processor(:pageflow_linkmap_page_image_dimensions,
|
23
|
+
Pageflow::LinkmapPage::PaperclipProcessors::ImageDimensions)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
11
27
|
config.generators do |g|
|
12
28
|
g.test_framework :rspec,:fixture => false
|
13
29
|
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
Binary file
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
module PaperclipProcessors
|
4
|
+
class ColorMask < Paperclip::Processor
|
5
|
+
CONVERT_COMMAND = [
|
6
|
+
# Create mask by making all non-matching colors in color map
|
7
|
+
# image transparent
|
8
|
+
':color_map',
|
9
|
+
'+transparent :color',
|
10
|
+
# Make all areas transparent in source that are transparent in mask
|
11
|
+
':source',
|
12
|
+
'-compose src-in -composite',
|
13
|
+
# Write result to output file
|
14
|
+
':dest'
|
15
|
+
].join(' ').freeze
|
16
|
+
|
17
|
+
def make
|
18
|
+
with_destination_tempfile do |dest|
|
19
|
+
with_color_map do |color_map_path|
|
20
|
+
convert(CONVERT_COMMAND,
|
21
|
+
color: "##{options[:style]}",
|
22
|
+
color_map: color_map_path,
|
23
|
+
source: File.expand_path(file.path),
|
24
|
+
dest: File.expand_path(dest.path))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def with_destination_tempfile
|
32
|
+
current_format = File.extname(file.path)
|
33
|
+
basename = File.basename(file.path, current_format)
|
34
|
+
|
35
|
+
dest = Tempfile.new([basename, '.png'])
|
36
|
+
dest.binmode
|
37
|
+
|
38
|
+
yield dest
|
39
|
+
|
40
|
+
dest
|
41
|
+
end
|
42
|
+
|
43
|
+
def with_color_map(&block)
|
44
|
+
Pageflow::LinkmapPage::PaperclipTempfile.for(color_map_attachment, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def color_map_attachment
|
48
|
+
options.fetch(:color_map_attachment)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
module PaperclipProcessors
|
4
|
+
class Colors < Paperclip::Processor
|
5
|
+
def make
|
6
|
+
with_progress(steps: 2) do |progress|
|
7
|
+
colors = unique_colors(file) - ['000000']
|
8
|
+
boxes = color_bounding_boxes_ignoring_single_pixels(file, colors, progress)
|
9
|
+
|
10
|
+
attachment.instance_write('colors', boxes)
|
11
|
+
make_color_sprite(file, boxes.keys, progress)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def unique_colors(file)
|
18
|
+
ConvertOutput.parse_unique_colors(convert(':source -unique-colors txt:-',
|
19
|
+
source: file.path))
|
20
|
+
end
|
21
|
+
|
22
|
+
def color_bounding_boxes_ignoring_single_pixels(file, colors, overall_progress)
|
23
|
+
overall_progress.divide(steps: colors.size) do |progress|
|
24
|
+
boxes = colors.each_with_object({}) do |color, result|
|
25
|
+
result[color] = color_bounding_box_ignoring_single_pixels(file, color)
|
26
|
+
progress.step
|
27
|
+
end
|
28
|
+
|
29
|
+
remove_boxes_for_ignored_colors(boxes)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_boxes_for_ignored_colors(boxes)
|
34
|
+
boxes.delete_if do |_, box|
|
35
|
+
box[:width].zero? && box[:height].zero?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
COLOR_MASK_ARGS = [
|
40
|
+
# Make transparent areas black
|
41
|
+
'-background black -flatten',
|
42
|
+
# Make all non matching colors black
|
43
|
+
'-fill black +opaque :color',
|
44
|
+
# Replace color with white - required for morphology
|
45
|
+
'-fill white -opaque :color',
|
46
|
+
# Remove shapes that are smaller than 3x3 pixels
|
47
|
+
'-morphology Open Square:1',
|
48
|
+
# Make black areas transparent
|
49
|
+
'-transparent black',
|
50
|
+
# Restore original color
|
51
|
+
'-fill :color -opaque white',
|
52
|
+
].join(' ').freeze
|
53
|
+
|
54
|
+
TRIM_CONVERT_ARGS = [
|
55
|
+
# Make all non-matching colors in color map image
|
56
|
+
# transparent
|
57
|
+
':source',
|
58
|
+
COLOR_MASK_ARGS,
|
59
|
+
# Create a transparent border
|
60
|
+
'-bordercolor none',
|
61
|
+
'-border 1x1',
|
62
|
+
# Trim transparent region.
|
63
|
+
'-trim',
|
64
|
+
].join(' ').freeze
|
65
|
+
|
66
|
+
TRIM_INFO_CONVERT_ARGS = [
|
67
|
+
TRIM_CONVERT_ARGS,
|
68
|
+
# Write info to stdout
|
69
|
+
'info:-'
|
70
|
+
].join(' ').freeze
|
71
|
+
|
72
|
+
def color_bounding_box_ignoring_single_pixels(file, color)
|
73
|
+
box = ConvertOutput.parse_trim(convert(TRIM_INFO_CONVERT_ARGS,
|
74
|
+
color: "##{color}",
|
75
|
+
source: file.path))
|
76
|
+
ignore_added_border_in_coordinated(box)
|
77
|
+
end
|
78
|
+
|
79
|
+
def ignore_added_border_in_coordinated(box)
|
80
|
+
{
|
81
|
+
left: box[:left] - 1,
|
82
|
+
top: box[:top] - 1,
|
83
|
+
width: box[:width],
|
84
|
+
height: box[:height]
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def make_color_sprite(file, colors, overall_progress)
|
89
|
+
with_destination_tempfile do |sprite_tempfile|
|
90
|
+
with_tempfiles(colors) do |color_tempfiles|
|
91
|
+
overall_progress.divide(steps: colors.size + 1) do |progress|
|
92
|
+
color_tempfiles.each do |color, color_tempfile|
|
93
|
+
make_color_filtered_image(file, color, color_tempfile)
|
94
|
+
progress.step
|
95
|
+
end
|
96
|
+
|
97
|
+
make_sprite(color_tempfiles, sprite_tempfile)
|
98
|
+
progress.step
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
COLOR_FILTER_CONVERT_ARGS = [
|
105
|
+
TRIM_CONVERT_ARGS,
|
106
|
+
# Crop image to trimmed area
|
107
|
+
'+repage',
|
108
|
+
':dest'
|
109
|
+
].join(' ').freeze
|
110
|
+
|
111
|
+
def make_color_filtered_image(file, color, destination_tempfile)
|
112
|
+
convert(COLOR_FILTER_CONVERT_ARGS,
|
113
|
+
color: "##{color}",
|
114
|
+
source: File.expand_path(file.path),
|
115
|
+
dest: File.expand_path(destination_tempfile.path))
|
116
|
+
end
|
117
|
+
|
118
|
+
def make_sprite(files, destination_tempfile)
|
119
|
+
inputs = files.each_with_object({}) do |(key, tempfile), result|
|
120
|
+
result["input_#{key}"] = File.expand_path(tempfile.path)
|
121
|
+
end
|
122
|
+
|
123
|
+
placeholders = inputs.keys.sort.map { |key| ":#{key}" }
|
124
|
+
|
125
|
+
Paperclip.run('montage',
|
126
|
+
[
|
127
|
+
*placeholders,
|
128
|
+
'-background transparent',
|
129
|
+
"-tile #{inputs.size}x",
|
130
|
+
'-mode Concatenate',
|
131
|
+
':output'
|
132
|
+
].join(' '),
|
133
|
+
inputs.merge(output: File.expand_path(destination_tempfile.path)))
|
134
|
+
end
|
135
|
+
|
136
|
+
def with_destination_tempfile
|
137
|
+
dest = create_tempfile
|
138
|
+
yield dest
|
139
|
+
dest
|
140
|
+
end
|
141
|
+
|
142
|
+
def with_tempfiles(keys)
|
143
|
+
files = keys.each_with_object({}) do |key, result|
|
144
|
+
result[key] = create_tempfile(key)
|
145
|
+
end
|
146
|
+
|
147
|
+
yield files
|
148
|
+
ensure
|
149
|
+
files.each_value do |file|
|
150
|
+
file.close
|
151
|
+
file.unlink
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def create_tempfile(suffix = nil)
|
156
|
+
current_format = File.extname(file.path)
|
157
|
+
basename = File.basename(file.path, current_format)
|
158
|
+
|
159
|
+
dest = Tempfile.new([[basename, suffix].compact.join('-'), '.png'])
|
160
|
+
dest.binmode
|
161
|
+
|
162
|
+
dest
|
163
|
+
end
|
164
|
+
|
165
|
+
def with_progress(steps:)
|
166
|
+
progress = Pageflow::LinkmapPage::Progress.new(steps: steps) do |percent|
|
167
|
+
if options[:progress_callback]
|
168
|
+
attachment.instance.send(options[:progress_callback], percent)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
yield progress
|
173
|
+
end
|
174
|
+
|
175
|
+
module ConvertOutput
|
176
|
+
HEX_COLOR_REGEXP = /#[0-9a-f]{6,8}/i
|
177
|
+
TRANSPARENT = '#00000000'.freeze
|
178
|
+
|
179
|
+
module_function
|
180
|
+
|
181
|
+
def parse_unique_colors(output)
|
182
|
+
output
|
183
|
+
.split("\n")
|
184
|
+
.reject { |line| line.starts_with?('#') }
|
185
|
+
.map { |line| line[HEX_COLOR_REGEXP] }
|
186
|
+
.reject { |color| color == TRANSPARENT }
|
187
|
+
.map { |color| color.tr('#', '') }
|
188
|
+
.map(&:downcase)
|
189
|
+
.sort
|
190
|
+
end
|
191
|
+
|
192
|
+
def parse_trim(output)
|
193
|
+
trimmed_size, size_and_offset = output.split(' ')[2, 3]
|
194
|
+
width, height = trimmed_size.split('x')
|
195
|
+
|
196
|
+
if size_and_offset.ends_with?('-1-1')
|
197
|
+
return {left: 0, top: 0, width: 0, height: 0}
|
198
|
+
end
|
199
|
+
|
200
|
+
_size, left, top = size_and_offset.split('+')
|
201
|
+
|
202
|
+
{
|
203
|
+
left: left.to_i,
|
204
|
+
top: top.to_i,
|
205
|
+
width: width.to_i,
|
206
|
+
height: height.to_i
|
207
|
+
}
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
module PaperclipProcessors
|
4
|
+
class ImageDimensions < Paperclip::Processor
|
5
|
+
def make
|
6
|
+
store_dimensions(file)
|
7
|
+
file
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def store_dimensions(file)
|
13
|
+
geometry = Paperclip::Geometry.from_file(file)
|
14
|
+
|
15
|
+
attachment.instance_write('width', geometry.width)
|
16
|
+
attachment.instance_write('height', geometry.height)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
module PaperclipTempfile
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def for(attachment)
|
7
|
+
tempfile = Paperclip.io_adapters.for(attachment)
|
8
|
+
|
9
|
+
begin
|
10
|
+
yield(tempfile.path)
|
11
|
+
ensure
|
12
|
+
tempfile.close
|
13
|
+
tempfile.unlink
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Pageflow
|
2
|
+
module LinkmapPage
|
3
|
+
class Progress
|
4
|
+
def initialize(steps:, &block)
|
5
|
+
@total_steps = steps
|
6
|
+
@block = block
|
7
|
+
@counter = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def step
|
11
|
+
return if counter >= total_steps
|
12
|
+
@counter += 1
|
13
|
+
report(current_percent)
|
14
|
+
end
|
15
|
+
|
16
|
+
def divide(steps:)
|
17
|
+
return if counter >= total_steps
|
18
|
+
|
19
|
+
sub_progress = Progress.new(steps: steps) do |percent|
|
20
|
+
if percent == 100
|
21
|
+
step
|
22
|
+
else
|
23
|
+
report(current_percent + percent / total_steps)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
yield sub_progress
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :block, :total_steps, :counter
|
33
|
+
|
34
|
+
def current_percent
|
35
|
+
100.0 / total_steps * counter
|
36
|
+
end
|
37
|
+
|
38
|
+
def report(percent)
|
39
|
+
@block.call(percent)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
namespace :pageflow_linkmap_page do
|
2
|
+
desc 'Migrate to server generated files'
|
3
|
+
task migrate_to_masked_image_files: :environment do
|
4
|
+
Pageflow::LinkmapPage::MaskedImageFilesMigrator.run
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Pageflow
|
9
|
+
module LinkmapPage
|
10
|
+
module MaskedImageFilesMigrator
|
11
|
+
extend self
|
12
|
+
|
13
|
+
def run
|
14
|
+
Resque.inline = true
|
15
|
+
total = pages_with_mask_image.count
|
16
|
+
|
17
|
+
pages_with_mask_image.find_each.with_index do |page, index|
|
18
|
+
puts "== Migrating page #{page.id} (#{index}/#{total})"
|
19
|
+
color_map_file = create_files(page)
|
20
|
+
|
21
|
+
if color_map_file
|
22
|
+
puts '-- Migrating mask perma ids...'
|
23
|
+
migrate_mask_perma_ids(page, color_map_file)
|
24
|
+
end
|
25
|
+
|
26
|
+
page.save!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def create_files(page)
|
33
|
+
color_map_image_file =
|
34
|
+
ImageFile.find_by_id(page.configuration['linkmap_color_map_image_id'])
|
35
|
+
|
36
|
+
if color_map_image_file
|
37
|
+
revision = page.chapter.storyline.revision
|
38
|
+
|
39
|
+
puts "-- Color map file for image file #{color_map_image_file.id}"
|
40
|
+
|
41
|
+
color_map_file = color_map_file_for(color_map_image_file, revision)
|
42
|
+
page.configuration['linkmap_color_map_file_id'] = color_map_file.id
|
43
|
+
|
44
|
+
hover_image_file = ImageFile.find_by_id(page.configuration['hover_image_id'])
|
45
|
+
|
46
|
+
if hover_image_file
|
47
|
+
puts "-- Masked image file for hover image file #{hover_image_file.id}"
|
48
|
+
|
49
|
+
masked_image_file = masked_image_file_for(hover_image_file, color_map_file, revision)
|
50
|
+
page.configuration['linkmap_masked_hover_image_id'] = masked_image_file.id
|
51
|
+
end
|
52
|
+
|
53
|
+
visited_image_file = ImageFile.find_by_id(page.configuration['visited_image_id'])
|
54
|
+
|
55
|
+
if visited_image_file
|
56
|
+
puts "-- Masked image file for visited image file #{visited_image_file.id}"
|
57
|
+
|
58
|
+
masked_image_file = masked_image_file_for(visited_image_file, color_map_file, revision)
|
59
|
+
page.configuration['linkmap_masked_visited_image_id'] = masked_image_file.id
|
60
|
+
end
|
61
|
+
|
62
|
+
color_map_file
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def color_map_file_for(image_file, revision)
|
67
|
+
color_map_file = ColorMapFile.find_or_create_by(source_image_file_id: image_file.id) do |c|
|
68
|
+
c.entry_id = revision.entry_id
|
69
|
+
end
|
70
|
+
|
71
|
+
unless color_map_file.processed?
|
72
|
+
puts ' Processing...'
|
73
|
+
|
74
|
+
color_map_file.process!
|
75
|
+
color_map_file.reload
|
76
|
+
end
|
77
|
+
|
78
|
+
revision.file_usages.find_or_create_by(file: color_map_file)
|
79
|
+
color_map_file
|
80
|
+
end
|
81
|
+
|
82
|
+
def masked_image_file_for(image_file, color_map_file, revision)
|
83
|
+
masked_image_file = MaskedImageFile.find_or_create_by(source_image_file: image_file,
|
84
|
+
color_map_file: color_map_file) do |m|
|
85
|
+
m.entry_id = revision.entry_id
|
86
|
+
end
|
87
|
+
|
88
|
+
unless masked_image_file.processed?
|
89
|
+
puts ' Processing...'
|
90
|
+
|
91
|
+
masked_image_file.process!
|
92
|
+
masked_image_file.reload
|
93
|
+
end
|
94
|
+
|
95
|
+
revision.file_usages.find_or_create_by(file: masked_image_file)
|
96
|
+
masked_image_file
|
97
|
+
end
|
98
|
+
|
99
|
+
def migrate_mask_perma_ids(page, color_map_file)
|
100
|
+
areas = page.configuration['linkmap_areas'] || []
|
101
|
+
|
102
|
+
page.configuration['linkmap_areas'] = areas.map do |area_attributes|
|
103
|
+
mask_perma_id = area_attributes['mask_perma_id']
|
104
|
+
|
105
|
+
if mask_perma_id && !area_attributes['color_map_component_id']
|
106
|
+
sprite_id = page.configuration.fetch('linkmap_masks').fetch('id')
|
107
|
+
colors =
|
108
|
+
page
|
109
|
+
.configuration
|
110
|
+
.fetch('linkmap_masks')
|
111
|
+
.fetch('c')
|
112
|
+
.fetch('c')
|
113
|
+
.map { |component| component['c'] }
|
114
|
+
|
115
|
+
area_attributes.merge(color_map_component_id: convert_mask_perma_id(mask_perma_id,
|
116
|
+
colors,
|
117
|
+
color_map_file,
|
118
|
+
sprite_id))
|
119
|
+
else
|
120
|
+
area_attributes
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def convert_mask_perma_id(perma_id, colors, color_map_file, sprite_id)
|
126
|
+
target_sprite_id, color_index = perma_id.split(':')
|
127
|
+
|
128
|
+
if target_sprite_id.to_i == sprite_id
|
129
|
+
"#{color_map_file.id}:#{nearest_color(color_map_file, *colors[color_index.to_i])}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def nearest_color(color_map_file, r, g, b)
|
134
|
+
color_map_file.present_colors.min_by do |color|
|
135
|
+
(color[0..1].to_i(16) - r).abs +
|
136
|
+
(color[2..3].to_i(16) - g).abs +
|
137
|
+
(color[4..5].to_i(16) - b).abs
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def pages_with_mask_image
|
142
|
+
Page
|
143
|
+
.where(template: 'linkmap_page')
|
144
|
+
.where('configuration LIKE "%linkmap_color_map_image_id%"')
|
145
|
+
.includes(chapter: {storyline: :revision})
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pageflow-linkmap-page
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Codevise Solutions Ltd.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pageflow
|
@@ -253,7 +253,11 @@ files:
|
|
253
253
|
- app/assets/stylesheets/pageflow/linkmap_page/themes/default/scroll_indicators.scss
|
254
254
|
- app/controllers/pageflow/linkmap_page/mask_sprites_controller.rb
|
255
255
|
- app/helpers/pageflow/linkmap_page/areas_helper.rb
|
256
|
+
- app/jobs/pageflow/linkmap_page/process_source_image_file_job.rb
|
257
|
+
- app/models/pageflow/linkmap_page/color_map_file.rb
|
256
258
|
- app/models/pageflow/linkmap_page/mask_sprite.rb
|
259
|
+
- app/models/pageflow/linkmap_page/masked_image_file.rb
|
260
|
+
- app/models/pageflow/linkmap_page/processed_image_file.rb
|
257
261
|
- app/views/pageflow/linkmap_page/areas/_div.html.erb
|
258
262
|
- app/views/pageflow/linkmap_page/page.html.erb
|
259
263
|
- bin/rspec
|
@@ -263,12 +267,22 @@ files:
|
|
263
267
|
- config/routes.rb
|
264
268
|
- config/spring.rb
|
265
269
|
- db/migrate/20170330201200_create_mask_sprites.rb
|
270
|
+
- db/migrate/20171106151700_create_masked_image_files.rb
|
271
|
+
- db/migrate/20180111145100_create_color_map_files.rb
|
266
272
|
- lib/generators/pageflow_linkmap_page/install/install_generator.rb
|
267
273
|
- lib/pageflow-linkmap-page.rb
|
268
274
|
- lib/pageflow/linkmap_page/engine.rb
|
275
|
+
- lib/pageflow/linkmap_page/images/palette.png
|
269
276
|
- lib/pageflow/linkmap_page/page_type.rb
|
277
|
+
- lib/pageflow/linkmap_page/paperclip_processors/color_mask.rb
|
278
|
+
- lib/pageflow/linkmap_page/paperclip_processors/colors.rb
|
279
|
+
- lib/pageflow/linkmap_page/paperclip_processors/image_dimensions.rb
|
280
|
+
- lib/pageflow/linkmap_page/paperclip_processors/invoke_callback.rb
|
281
|
+
- lib/pageflow/linkmap_page/paperclip_tempfile.rb
|
270
282
|
- lib/pageflow/linkmap_page/plugin.rb
|
283
|
+
- lib/pageflow/linkmap_page/progress.rb
|
271
284
|
- lib/pageflow/linkmap_page/version.rb
|
285
|
+
- lib/tasks/pageflow_linkmap_page_tasks.rake
|
272
286
|
- pageflow-linkmap-page.gemspec
|
273
287
|
- spec/controllers/pageflow/linkmap_page/mask_sprites_controller_spec.rb
|
274
288
|
- spec/helpers/pageflow/linkmap_page/areas_helper_spec.rb
|
@@ -295,7 +309,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
295
309
|
version: '0'
|
296
310
|
requirements: []
|
297
311
|
rubyforge_project:
|
298
|
-
rubygems_version: 2.7.
|
312
|
+
rubygems_version: 2.7.8
|
299
313
|
signing_key:
|
300
314
|
specification_version: 4
|
301
315
|
summary: Pageflow page type for a page that contains customizable link areas
|