jekyll-favicon-generator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/jekyll-favicon-generator/configuration.rb +62 -0
- data/lib/jekyll-favicon-generator/generator.rb +46 -0
- data/lib/jekyll-favicon-generator/hook.rb +8 -0
- data/lib/jekyll-favicon-generator/ico.rb +152 -0
- data/lib/jekyll-favicon-generator/icon.rb +113 -0
- data/lib/jekyll-favicon-generator/manifest.rb +49 -0
- data/lib/jekyll-favicon-generator/site.webmanifest +17 -0
- data/lib/jekyll-favicon-generator/size.rb +23 -0
- data/lib/jekyll-favicon-generator/svg.rb +29 -0
- data/lib/jekyll-favicon-generator/tag.rb +30 -0
- data/lib/jekyll-favicon-generator/utilities.rb +67 -0
- data/lib/jekyll-favicon-generator/version.rb +5 -0
- data/lib/jekyll-favicon-generator/vips.rb +21 -0
- data/lib/jekyll-favicon-generator.rb +11 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 24b3fb7b124c03e2d0d939905a0552466982346dce15bb6e354d40060ae446c3
|
4
|
+
data.tar.gz: 912d66b372081c250437f18c8d73f9310965171c0d0415573b887534d9a502a0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 96127209a335b1bee8edd0ef228e74ace1aa2914b7a51ff118e32f76ac380261a81baf6a9f64c8e774c136929a4b4354b2cc1984c795bab8d1dbac8e2549f4e5
|
7
|
+
data.tar.gz: 41eba8d3dccbe4f2fe2f6b399d0650436e9b46757dc655d40b1cd4f3a3aacfc65b8ab0a46083b010676c8179fd62d816879811d1bcc6982517505ebc96fd86ce
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllFaviconGenerator
|
4
|
+
class Configuration < Jekyll::Configuration
|
5
|
+
# Default options. Overridden by values in _config.yml.
|
6
|
+
# Strings rather than symbols are used for compatibility with YAML.
|
7
|
+
DEFAULTS = {
|
8
|
+
"source" => nil,
|
9
|
+
"destination" => "assets/icons",
|
10
|
+
"color" => "#000000",
|
11
|
+
"background" => "#ffffff",
|
12
|
+
"icons" => [
|
13
|
+
{
|
14
|
+
"file" => "favicon.ico",
|
15
|
+
"size" => "16,24,32,48",
|
16
|
+
"ref" => nil,
|
17
|
+
},
|
18
|
+
{
|
19
|
+
"file" => "favicon-16.png",
|
20
|
+
"size" => 16,
|
21
|
+
"ref" => "link/icon",
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"file" => "favicon-32.png",
|
25
|
+
"size" => 32,
|
26
|
+
"ref" => "link/icon",
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"file" => "favicon.svg",
|
30
|
+
"ref" => "link/icon",
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"file" => "apple-touch-icon.png",
|
34
|
+
"size" => 180,
|
35
|
+
"ref" => "link/apple-touch-icon",
|
36
|
+
},
|
37
|
+
{
|
38
|
+
"file" => "android-chrome-192.png",
|
39
|
+
"size" => 192,
|
40
|
+
"ref" => "manifest",
|
41
|
+
},
|
42
|
+
{
|
43
|
+
"file" => "android-chrome-512.png",
|
44
|
+
"size" => 512,
|
45
|
+
"ref" => "manifest",
|
46
|
+
},
|
47
|
+
],
|
48
|
+
}.freeze
|
49
|
+
|
50
|
+
class << self
|
51
|
+
# Static: Produce a Configuration ready for use in a Site.
|
52
|
+
# It takes the input, fills in the defaults where values do not exist.
|
53
|
+
#
|
54
|
+
# user_config - a Hash or Configuration of overrides.
|
55
|
+
#
|
56
|
+
# Returns a Configuration filled with defaults.
|
57
|
+
def from(user_config)
|
58
|
+
Jekyll::Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll-favicon-generator/icon"
|
4
|
+
require "jekyll-favicon-generator/utilities"
|
5
|
+
require "jekyll-favicon-generator/vips"
|
6
|
+
require "jekyll-favicon-generator/manifest"
|
7
|
+
|
8
|
+
module JekyllFaviconGenerator
|
9
|
+
class Generator < Jekyll::Generator
|
10
|
+
include Utilities
|
11
|
+
|
12
|
+
def initialize(site)
|
13
|
+
@site = site
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate(site)
|
17
|
+
@site = site
|
18
|
+
|
19
|
+
debug "Using libvips #{Vips.version}"
|
20
|
+
|
21
|
+
unless file_exists? source
|
22
|
+
error "File #{source} not found!"
|
23
|
+
return
|
24
|
+
end
|
25
|
+
info "Generating favicons from #{source}"
|
26
|
+
|
27
|
+
@site.static_files.push(*icons)
|
28
|
+
@site.pages.push(*manifests)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def icons
|
34
|
+
@icons ||= config["icons"].filter_map { |icon| Icon.new(@site, icon) if icon["file"] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def manifests
|
38
|
+
return @manifests if @manifests
|
39
|
+
|
40
|
+
@manifests = []
|
41
|
+
manifest_icons = icons.select { |icon| icon.ref == :manifest }
|
42
|
+
@manifests << Manifest.new(@site, manifest_icons) unless manifest_icons.empty?
|
43
|
+
@manifests
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll-favicon-generator/utilities"
|
4
|
+
|
5
|
+
Jekyll::Hooks.register :site, :after_init do |site|
|
6
|
+
# Adds the source image to the exclude list so it's not copied directly to the output
|
7
|
+
site.exclude.push JekyllFaviconGenerator::UtilityClass.new(site).source
|
8
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "vips"
|
4
|
+
require "bit-struct"
|
5
|
+
|
6
|
+
module JekyllFaviconGenerator
|
7
|
+
module Ico
|
8
|
+
# ICO file format references:
|
9
|
+
# https://en.wikipedia.org/wiki/ICO_(file_format)
|
10
|
+
# https://en.wikipedia.org/wiki/BMP_file_format
|
11
|
+
# https://docs.microsoft.com/en-us/previous-versions/ms997538(v=msdn.10)
|
12
|
+
# https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
|
13
|
+
# Various ICO files from various websites and generated by other software
|
14
|
+
|
15
|
+
extend self
|
16
|
+
|
17
|
+
def img_to_ico(src, ico, sizes)
|
18
|
+
File.open(ico, "wb") do |f|
|
19
|
+
f.write icon_dir sizes
|
20
|
+
|
21
|
+
sizes.each do |width|
|
22
|
+
height = width
|
23
|
+
f.write bitmap_info_header width, height
|
24
|
+
|
25
|
+
img = ::Vips::Image.thumbnail src, width, :height => height
|
26
|
+
img = img.flip(:vertical) # Flip the image since bitmap is stored bottom to top
|
27
|
+
img = img.colourspace :srgb # Ensure srgb colourspace
|
28
|
+
f.write bitmap_xor_mask img, width, height
|
29
|
+
f.write bitmap_and_mask img, width, height
|
30
|
+
end
|
31
|
+
end
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
BYTE = 8
|
38
|
+
WORD = 2 * BYTE
|
39
|
+
DWORD = 4 * BYTE
|
40
|
+
LONG = 4 * BYTE
|
41
|
+
BPP_COUNT = 32
|
42
|
+
|
43
|
+
# Generate the ICONDIR structure for the ICO file
|
44
|
+
def icon_dir(sizes)
|
45
|
+
count = sizes.length
|
46
|
+
offset = IconDir.round_byte_length + count * IconDirEntry.round_byte_length
|
47
|
+
|
48
|
+
dir = IconDir.new.tap do |id|
|
49
|
+
id.type = 1 # 1 => ICO
|
50
|
+
id.id_count = count
|
51
|
+
end.to_s
|
52
|
+
|
53
|
+
entries = sizes.map do |size|
|
54
|
+
entry = icon_dir_entry size, offset
|
55
|
+
offset += entry.bytes_in_res
|
56
|
+
entry.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
dir + entries.join
|
60
|
+
end
|
61
|
+
|
62
|
+
def icon_dir_entry(size, offset)
|
63
|
+
xor_rowsize = rowsize size * BPP_COUNT
|
64
|
+
and_rowsize = rowsize size
|
65
|
+
bytes = BitMapInfoHeader.round_byte_length + xor_rowsize * size + and_rowsize * size
|
66
|
+
|
67
|
+
IconDirEntry.new.tap do |ide|
|
68
|
+
ide.width = size
|
69
|
+
ide.height = size
|
70
|
+
ide.color_count = 0 # 0 => no color palette
|
71
|
+
ide.planes = 1
|
72
|
+
ide.bit_count = BPP_COUNT
|
73
|
+
ide.bytes_in_res = bytes
|
74
|
+
ide.image_offset = offset
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def bitmap_info_header(width, height)
|
79
|
+
BitMapInfoHeader.new.tap do |bih|
|
80
|
+
bih.ih_size = BitMapInfoHeader.round_byte_length # size is only the header size
|
81
|
+
bih.width = width
|
82
|
+
bih.height = 2 * height # must be double height, even if no AND mask is included
|
83
|
+
bih.planes = 1
|
84
|
+
bih.bit_count = BPP_COUNT
|
85
|
+
bih.compression = 0 # 0 => none
|
86
|
+
bih.size_image = width * height * BPP_COUNT / BYTE
|
87
|
+
bih.clr_used = 0 # 0 since we don't use a palette
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def bitmap_xor_mask(img, width, _height)
|
92
|
+
xor_rowsize = rowsize(width * BPP_COUNT)
|
93
|
+
arr = img.to_enum.map do |row|
|
94
|
+
# Colors are stored in ARGB format with LE order, so BGRA in the array
|
95
|
+
# Also pad rows to the required size
|
96
|
+
row.map { |r, g, b, a| [b, g, r, a] }.fill([0, 0, 0, 0], row.length..(xor_rowsize / 4 - 1))
|
97
|
+
end
|
98
|
+
arr.flatten.pack("C*")
|
99
|
+
end
|
100
|
+
|
101
|
+
def bitmap_and_mask(img, width, _height)
|
102
|
+
and_rowsize = rowsize width
|
103
|
+
arr = img.to_enum.map do |row|
|
104
|
+
# Convert alpha to 1-bit and pad rows to the required size
|
105
|
+
row.map { |_r, _g, _b, a| a.zero? && 1 || 0 }.fill(0, row.length..(and_rowsize * BYTE - 1))
|
106
|
+
end
|
107
|
+
[arr.flatten.join].pack("B*")
|
108
|
+
end
|
109
|
+
|
110
|
+
# Given a size in bits, return the size in bytes, rounded up to a multiple of 4 bytes
|
111
|
+
def rowsize(bit_size)
|
112
|
+
(bit_size + 31) / 32 * 4
|
113
|
+
end
|
114
|
+
|
115
|
+
class IconDir < BitStruct
|
116
|
+
default_options :endian => :little
|
117
|
+
|
118
|
+
pad :reserved, WORD
|
119
|
+
unsigned :type, WORD
|
120
|
+
unsigned :id_count, WORD
|
121
|
+
end
|
122
|
+
|
123
|
+
class IconDirEntry < BitStruct
|
124
|
+
default_options :endian => :little
|
125
|
+
|
126
|
+
unsigned :width, BYTE
|
127
|
+
unsigned :height, BYTE
|
128
|
+
unsigned :color_count, BYTE
|
129
|
+
pad :reserved, BYTE
|
130
|
+
unsigned :planes, WORD
|
131
|
+
unsigned :bit_count, WORD
|
132
|
+
unsigned :bytes_in_res, DWORD
|
133
|
+
unsigned :image_offset, DWORD
|
134
|
+
end
|
135
|
+
|
136
|
+
class BitMapInfoHeader < BitStruct
|
137
|
+
default_options :endian => :little
|
138
|
+
|
139
|
+
unsigned :ih_size, DWORD
|
140
|
+
signed :width, LONG
|
141
|
+
signed :height, LONG
|
142
|
+
unsigned :planes, WORD
|
143
|
+
unsigned :bit_count, WORD
|
144
|
+
unsigned :compression, DWORD
|
145
|
+
unsigned :size_image, DWORD
|
146
|
+
signed :x_pels_per_meter, LONG, :default => 3780 # 96 dpi (usually ignored)
|
147
|
+
signed :y_pels_per_meter, LONG, :default => 3780 # 96 dpi (usually ignored)
|
148
|
+
unsigned :clr_used, DWORD
|
149
|
+
unsigned :clr_important, DWORD, :default => 0 # 0 important colours (usually ignored)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll-favicon-generator/ico"
|
4
|
+
require "jekyll-favicon-generator/utilities"
|
5
|
+
require "jekyll-favicon-generator/size"
|
6
|
+
require "jekyll-favicon-generator/svg"
|
7
|
+
require "jekyll-favicon-generator/vips"
|
8
|
+
|
9
|
+
require "fileutils"
|
10
|
+
|
11
|
+
module JekyllFaviconGenerator
|
12
|
+
class Icon < Jekyll::StaticFile
|
13
|
+
include Utilities
|
14
|
+
|
15
|
+
def initialize(site, icon)
|
16
|
+
@site = site
|
17
|
+
@icon = icon
|
18
|
+
|
19
|
+
super site, site.source, dest_dir, @icon["file"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def write(dest)
|
23
|
+
dest = destination dest
|
24
|
+
src = @site.in_source_dir source
|
25
|
+
|
26
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
27
|
+
debug "Writing #{File.basename dest}"
|
28
|
+
|
29
|
+
case type
|
30
|
+
when :png
|
31
|
+
Vips.img_to_png src, dest, size.to_i
|
32
|
+
when :ico
|
33
|
+
Ico.img_to_ico src, dest, size.to_a
|
34
|
+
when :svg
|
35
|
+
Svg.optimize src, dest
|
36
|
+
else
|
37
|
+
warn "Unknown format for #{File.basename dest}, skipping"
|
38
|
+
false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_tag(url)
|
43
|
+
return if ref.nil? || ref == :manifest
|
44
|
+
|
45
|
+
attrs = attributes(url)
|
46
|
+
unless tag_name && attrs
|
47
|
+
warn "Unknown ref type for #{File.basename @icon["file"]}, skipping"
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
attrs = attrs.map { |k, v| "#{k}=#{v.to_s.encode(:xml => :attr)}" }
|
52
|
+
|
53
|
+
"<#{tag_name} #{attrs.join(" ")}>"
|
54
|
+
end
|
55
|
+
|
56
|
+
def type
|
57
|
+
@type ||= TYPES[File.extname(name).downcase] || :unknown
|
58
|
+
end
|
59
|
+
|
60
|
+
def mime
|
61
|
+
@mime ||= MIMES[type]
|
62
|
+
end
|
63
|
+
|
64
|
+
def size
|
65
|
+
@size ||= Size.new @icon["size"]
|
66
|
+
end
|
67
|
+
|
68
|
+
def ref
|
69
|
+
@ref ||= REFS[@icon["ref"]&.downcase]
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
TYPES = {
|
75
|
+
".ico" => :ico,
|
76
|
+
".png" => :png,
|
77
|
+
".svg" => :svg,
|
78
|
+
}.freeze
|
79
|
+
|
80
|
+
MIMES = {
|
81
|
+
:ico => "image/x-icon",
|
82
|
+
:png => "image/png",
|
83
|
+
:svg => "image/svg+xml",
|
84
|
+
}.freeze
|
85
|
+
|
86
|
+
REFS = {
|
87
|
+
"link/icon" => :link_icon,
|
88
|
+
"link/apple-touch-icon" => :link_apple,
|
89
|
+
"manifest" => :manifest,
|
90
|
+
"webmanifest" => :manifest,
|
91
|
+
}.freeze
|
92
|
+
|
93
|
+
def tag_name
|
94
|
+
@tag_name ||= case ref
|
95
|
+
when :link_icon, :link_apple
|
96
|
+
"link"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def attributes(url)
|
101
|
+
case ref
|
102
|
+
when :link_icon
|
103
|
+
{ :rel => "icon", :href => url, :sizes => (type == :svg ? "any" : size.to_s) }
|
104
|
+
when :link_apple
|
105
|
+
{ :rel => "apple-touch-icon", :href => url, :sizes => size.to_s }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def dest_dir
|
110
|
+
ref ? super : ""
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll-favicon-generator/utilities"
|
4
|
+
|
5
|
+
module JekyllFaviconGenerator
|
6
|
+
class Manifest < Jekyll::Page
|
7
|
+
include Utilities
|
8
|
+
|
9
|
+
def initialize(site, icons)
|
10
|
+
@site = site
|
11
|
+
@base = site.source
|
12
|
+
@dir = ""
|
13
|
+
@name = MANIFEST_NAME
|
14
|
+
@path = manifest_template
|
15
|
+
|
16
|
+
process(name)
|
17
|
+
read_yaml(site.source, name)
|
18
|
+
|
19
|
+
data.default_proc = proc do |_, key|
|
20
|
+
site.frontmatter_defaults.find(relative_path, type, key)
|
21
|
+
end
|
22
|
+
data.merge! icon_data icons
|
23
|
+
|
24
|
+
Jekyll::Hooks.trigger :pages, :post_init, self
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
MANIFEST_NAME = "site.webmanifest"
|
30
|
+
|
31
|
+
def manifest_template
|
32
|
+
@manifest_template ||= if file_exists? MANIFEST_NAME
|
33
|
+
site.in_source_dir MANIFEST_NAME
|
34
|
+
else
|
35
|
+
File.expand_path MANIFEST_NAME, __dir__
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def icon_data(icons)
|
40
|
+
data = {}
|
41
|
+
data["icons"] = icons.map do |icon|
|
42
|
+
{ "url" => icon.url, "size" => icon.size.to_s, "mime" => icon.mime }
|
43
|
+
end
|
44
|
+
data["color"] = config["color"]
|
45
|
+
data["background"] = config["background"]
|
46
|
+
data
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
"name": "{{ site.title | default: site.name | default: "" }}",
|
3
|
+
"short_name": "{{ site.title | default: site.name | default: "" }}",
|
4
|
+
"icons": [
|
5
|
+
{%- for icon in page.icons %}
|
6
|
+
{
|
7
|
+
"src": "{{ icon.url | relative_url }}",
|
8
|
+
"sizes": "{{ icon.size }}",
|
9
|
+
"type": "{{ icon.mime }}"
|
10
|
+
}
|
11
|
+
{%- unless forloop.last %},{% endunless %}
|
12
|
+
{%- endfor %}
|
13
|
+
],
|
14
|
+
"theme_color": "{{ page.color | default: "#ffffff" }}",
|
15
|
+
"background_color": "{{ page.background | default: "#000000" }}",
|
16
|
+
"display": "standalone"
|
17
|
+
}
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JekyllFaviconGenerator
|
4
|
+
class Size
|
5
|
+
def initialize(size_str)
|
6
|
+
@size_arr = size_str.to_s.split(",").map(&:to_i)
|
7
|
+
@size_arr.reject!(&:zero?)
|
8
|
+
@size_arr = [16] if @size_arr.nil? || @size_arr.empty?
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_a
|
12
|
+
@size_arr
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_i
|
16
|
+
@size_arr[0]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@size_arr.map { |s| "#{s}x#{s}" }.join " "
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll-favicon-generator/utilities"
|
4
|
+
|
5
|
+
require "nokogiri"
|
6
|
+
require "svg_optimizer"
|
7
|
+
|
8
|
+
module JekyllFaviconGenerator
|
9
|
+
module Svg
|
10
|
+
include Utilities
|
11
|
+
extend self
|
12
|
+
|
13
|
+
def optimize(src, dest)
|
14
|
+
unless File.extname(src).casecmp(".svg").zero?
|
15
|
+
warn "Unsupported #{File.extname(src).downcase} -> .svg for #{dest}"
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
|
19
|
+
xml = Nokogiri::XML File.read(src) { |config| config.recover.noent }
|
20
|
+
|
21
|
+
SvgOptimizer::DEFAULT_PLUGINS.each { |plugin| plugin.new(xml).process }
|
22
|
+
|
23
|
+
File.open(dest, "w") do |file|
|
24
|
+
file << xml.root.to_xml(:indent => 0).delete("\n")
|
25
|
+
end
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll-favicon-generator/icon"
|
4
|
+
require "jekyll-favicon-generator/manifest"
|
5
|
+
require "jekyll-favicon-generator/utilities"
|
6
|
+
|
7
|
+
module JekyllFaviconGenerator
|
8
|
+
class Tag < Liquid::Tag
|
9
|
+
# Use Jekyll's native relative_url filter
|
10
|
+
include Jekyll::Filters::URLFilters
|
11
|
+
include Utilities
|
12
|
+
|
13
|
+
def render(context)
|
14
|
+
# Jekyll::Filters::URLFilters requires `@context` to be set in the environment.
|
15
|
+
@context = context
|
16
|
+
@site = context.registers[:site]
|
17
|
+
|
18
|
+
tags = @site.static_files.filter_map do |icon|
|
19
|
+
icon.render_tag relative_url icon.url if icon.is_a? Icon
|
20
|
+
end
|
21
|
+
tags += @site.pages.filter_map do |manifest|
|
22
|
+
"<link rel=\"manifest\" href=\"#{relative_url manifest.url}\">" if manifest.is_a? Manifest
|
23
|
+
end
|
24
|
+
|
25
|
+
tags.join("\n")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Liquid::Template.register_tag("favicons", JekyllFaviconGenerator::Tag)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll-favicon-generator/configuration"
|
4
|
+
|
5
|
+
module JekyllFaviconGenerator
|
6
|
+
module Utilities
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# Logging functions
|
10
|
+
|
11
|
+
LOGGER_PREFIX = "Favicon Generator:"
|
12
|
+
|
13
|
+
def debug(topic = LOGGER_PREFIX, msg) # rubocop:disable Style/OptionalArguments
|
14
|
+
Jekyll.logger.debug topic, msg
|
15
|
+
end
|
16
|
+
|
17
|
+
def info(topic = LOGGER_PREFIX, msg) # rubocop:disable Style/OptionalArguments
|
18
|
+
Jekyll.logger.info topic, msg
|
19
|
+
end
|
20
|
+
|
21
|
+
def warn(topic = LOGGER_PREFIX, msg) # rubocop:disable Style/OptionalArguments
|
22
|
+
Jekyll.logger.warn topic, msg
|
23
|
+
end
|
24
|
+
|
25
|
+
def error(topic = LOGGER_PREFIX, msg) # rubocop:disable Style/OptionalArguments
|
26
|
+
Jekyll.logger.error topic, msg
|
27
|
+
end
|
28
|
+
|
29
|
+
def abort_with(topic = LOGGER_PREFIX, msg) # rubocop:disable Style/OptionalArguments
|
30
|
+
Jekyll.logger.abort_with topic, msg
|
31
|
+
end
|
32
|
+
|
33
|
+
# Parameters & defaults
|
34
|
+
|
35
|
+
def config
|
36
|
+
@config ||= Configuration.from @site.config["favicon-generator"] || {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def source
|
40
|
+
@source ||= config["source"] || find_source
|
41
|
+
end
|
42
|
+
|
43
|
+
def dest_dir
|
44
|
+
@dest_dir ||= config["destination"] || ""
|
45
|
+
end
|
46
|
+
|
47
|
+
# File utilities
|
48
|
+
|
49
|
+
def file_exists?(file)
|
50
|
+
File.file? @site.in_source_dir(file)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_source
|
54
|
+
[".svg", ".png"].map { |ext| "favicon#{ext}" }.find { |file| file_exists? file }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Class for accessing utility functions without instantiating anything bigger
|
59
|
+
|
60
|
+
class UtilityClass
|
61
|
+
include Utilities
|
62
|
+
|
63
|
+
def initialize(site)
|
64
|
+
@site = site
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "vips"
|
4
|
+
|
5
|
+
require "jekyll-favicon-generator/utilities"
|
6
|
+
|
7
|
+
module JekyllFaviconGenerator
|
8
|
+
module Vips
|
9
|
+
include Utilities
|
10
|
+
|
11
|
+
def self.version
|
12
|
+
"#{::Vips.version(0)}.#{::Vips.version(1)}.#{::Vips.version(2)}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.img_to_png(src, png, size)
|
16
|
+
img = ::Vips::Image.thumbnail src, size, :height => size
|
17
|
+
img.pngsave png
|
18
|
+
true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jekyll"
|
4
|
+
|
5
|
+
require "jekyll-favicon-generator/generator"
|
6
|
+
require "jekyll-favicon-generator/hook"
|
7
|
+
require "jekyll-favicon-generator/tag"
|
8
|
+
|
9
|
+
module JekyllFaviconGenerator
|
10
|
+
autoload :VERSION, "jekyll-favicon-generator/version"
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-favicon-generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Lucas Jansen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-06-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bit-struct
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.17'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.17'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jekyll
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.7'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '5.0'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '3.7'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '5.0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: nokogiri
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.12'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.12'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: ruby-vips
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.1'
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.1'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: svg_optimizer
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.2'
|
82
|
+
type: :runtime
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.2'
|
89
|
+
description: A simple and fast Jekyll plugin to generate favicons from a single source
|
90
|
+
image.
|
91
|
+
email:
|
92
|
+
executables: []
|
93
|
+
extensions: []
|
94
|
+
extra_rdoc_files: []
|
95
|
+
files:
|
96
|
+
- lib/jekyll-favicon-generator.rb
|
97
|
+
- lib/jekyll-favicon-generator/configuration.rb
|
98
|
+
- lib/jekyll-favicon-generator/generator.rb
|
99
|
+
- lib/jekyll-favicon-generator/hook.rb
|
100
|
+
- lib/jekyll-favicon-generator/ico.rb
|
101
|
+
- lib/jekyll-favicon-generator/icon.rb
|
102
|
+
- lib/jekyll-favicon-generator/manifest.rb
|
103
|
+
- lib/jekyll-favicon-generator/site.webmanifest
|
104
|
+
- lib/jekyll-favicon-generator/size.rb
|
105
|
+
- lib/jekyll-favicon-generator/svg.rb
|
106
|
+
- lib/jekyll-favicon-generator/tag.rb
|
107
|
+
- lib/jekyll-favicon-generator/utilities.rb
|
108
|
+
- lib/jekyll-favicon-generator/version.rb
|
109
|
+
- lib/jekyll-favicon-generator/vips.rb
|
110
|
+
homepage: https://github.com/staticintlucas/jekyll-favicon-generator
|
111
|
+
licenses:
|
112
|
+
- MIT
|
113
|
+
metadata:
|
114
|
+
homepage_uri: https://github.com/staticintlucas/jekyll-favicon-generator
|
115
|
+
source_code_uri: https://github.com/staticintlucas/jekyll-favicon-generator
|
116
|
+
changelog_uri: https://github.com/staticintlucas/jekyll-favicon-generator/blob/main/CHANGELOG.md
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 2.6.0
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubygems_version: 3.3.15
|
133
|
+
signing_key:
|
134
|
+
specification_version: 4
|
135
|
+
summary: Favicon generator for Jekyll.
|
136
|
+
test_files: []
|