jekyll-favicon-generator 0.1.0
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 +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: []
|