Skip to content

Commit fafc481

Browse files
committed
Merge branch 'develop'
2 parents 5e1979a + ee81565 commit fafc481

File tree

6 files changed

+1118
-0
lines changed

6 files changed

+1118
-0
lines changed

Diff for: .gitignore

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
2+
# Created by https://www.gitignore.io/api/node,macos
3+
4+
### macOS ###
5+
*.DS_Store
6+
.AppleDouble
7+
.LSOverride
8+
9+
# Icon must end with two \r
10+
Icon
11+
12+
# Thumbnails
13+
._*
14+
15+
# Files that might appear in the root of a volume
16+
.DocumentRevisions-V100
17+
.fseventsd
18+
.Spotlight-V100
19+
.TemporaryItems
20+
.Trashes
21+
.VolumeIcon.icns
22+
.com.apple.timemachine.donotpresent
23+
24+
# Directories potentially created on remote AFP share
25+
.AppleDB
26+
.AppleDesktop
27+
Network Trash Folder
28+
Temporary Items
29+
.apdisk
30+
31+
### Node ###
32+
# Logs
33+
logs
34+
*.log
35+
npm-debug.log*
36+
yarn-debug.log*
37+
yarn-error.log*
38+
39+
# Runtime data
40+
pids
41+
*.pid
42+
*.seed
43+
*.pid.lock
44+
45+
# Directory for instrumented libs generated by jscoverage/JSCover
46+
lib-cov
47+
48+
# Coverage directory used by tools like istanbul
49+
coverage
50+
51+
# nyc test coverage
52+
.nyc_output
53+
54+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
55+
.grunt
56+
57+
# Bower dependency directory (https://bower.io/)
58+
bower_components
59+
60+
# node-waf configuration
61+
.lock-wscript
62+
63+
# Compiled binary addons (http://nodejs.org/api/addons.html)
64+
build/Release
65+
66+
# Dependency directories
67+
node_modules/
68+
jspm_packages/
69+
70+
# Typescript v1 declaration files
71+
typings/
72+
73+
# Optional npm cache directory
74+
.npm
75+
76+
# Optional eslint cache
77+
.eslintcache
78+
79+
# Optional REPL history
80+
.node_repl_history
81+
82+
# Output of 'npm pack'
83+
*.tgz
84+
85+
# Yarn Integrity file
86+
.yarn-integrity
87+
88+
# dotenv environment variables file
89+
.env
90+
91+
92+
# End of https://www.gitignore.io/api/node,macos
93+
94+
95+
build/

Diff for: README.md

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
Notes Sametime Emojis
2+
=====================
3+
4+
Since Sametime doesn't have full support for UTF-8 emojis and only ships with a very "basic" emoticon palette, I
5+
decided to create one myself.
6+
7+
Currently these emoji sets are supported:
8+
9+
- Apple
10+
- Google
11+
- Twitter
12+
- Facebook
13+
14+
If you miss your favourite set, feel free to submit a pull request and add the ones you like.
15+
16+
17+
Installation
18+
------------
19+
20+
To download the emoji palette, just head to the releases section and download the set you want to use.
21+
22+
Installation is pretty straight forward. Just follow these steps:
23+
24+
1. Extract the archive you've downloaded somewhere where you can find it again. In it, you'll find more ZIPs.
25+
I've split up the whole set into multiple smaller palettes grouped by category to make it more useable inside
26+
Notes since there are a lot of single images (one for each emoji) and Notes apperently can't handle that...
27+
2. Open IBM Notes and head to _File → Preferences_.
28+
3. Go to the section _Sametime → Emoticon Palette_.
29+
4. Create a new palette for each ZIP inside the archive you've downloaded, click _Import_ and select the corresponding
30+
ZIP file.
31+
5. Enjoy! 🎉
32+
33+
34+
Build it yourself
35+
-----------------
36+
37+
If you want to have the emojis in a specific size (currently you can download only a 16px and 32px variant) you can
38+
build the palettes yourself. All you need is a standard Node.JS environment. Just clone the repository, edit the
39+
`main.js` file and modify `targetConfiguration.targets` to your likings.
40+
41+
``` js
42+
// Target Configuration.
43+
const targetConfiguration = {
44+
folder: path.join(__dirname, 'build'),
45+
targets: [
46+
// Specify the targets to build here!
47+
],
48+
}
49+
```
50+
51+
Each target object contains of the following two values. You can adjust the size however you like (keep in mind that
52+
the original images are 32x32 in size, so choosing something bigger than that worsens the quality of the resulting
53+
images).
54+
55+
``` js
56+
{
57+
set: 'apple|google|twitter|facebook',
58+
size: 32,
59+
}
60+
```
61+
62+
To build all targets, just run the following command:
63+
64+
``` bash
65+
$ npm run-script build
66+
```
67+
68+
69+
Contribute
70+
----------
71+
72+
If you haven't noticed already, the code is far from perfect and everything but optimized. Since this was just a quick
73+
project I haven't put too much attention to that. If you'd like to help with this, feel free to submit a pull request.
74+
75+
76+
------------------------------------------------------------------------------------------------------------------------
77+
78+
Copyright (C) Nils Weber // Licensed under MIT
79+

Diff for: lib.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// Notes Sametime Emojis
3+
// lib.js
4+
//
5+
// (C) Nils Weber | nilsweb
6+
// Code licensed under MIT
7+
//
8+
9+
10+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11+
12+
13+
const fs = require('fs')
14+
const path = require('path')
15+
16+
17+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
18+
19+
20+
const deleteDirRecursive = (directory) => {
21+
if (!fs.existsSync(directory)) return
22+
fs.readdirSync(directory)
23+
.map(file => path.join(directory, file))
24+
.forEach(newPath => {
25+
if (fs.lstatSync(newPath).isDirectory())
26+
deleteDirRecursive(newPath)
27+
else
28+
fs.unlinkSync(newPath)
29+
})
30+
fs.rmdirSync(directory)
31+
}
32+
33+
34+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
35+
36+
37+
module.exports = {
38+
deleteDirRecursive,
39+
}
40+
41+
42+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
43+

Diff for: main.js

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//
2+
// Notes Sametime Emojis
3+
// main.js
4+
//
5+
// (C) Nils Weber | nilsweb
6+
// Code licensed under MIT
7+
//
8+
9+
10+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
11+
12+
13+
const fs = require('fs')
14+
const path = require('path')
15+
16+
const sharp = require('sharp')
17+
const xml = require('xml')
18+
const emojiData = require('emoji-datasource')
19+
const archiver = require('archiver')
20+
21+
const lib = require('./lib')
22+
23+
24+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
25+
26+
27+
// Target Configuration.
28+
const targetConfiguration = {
29+
folder: path.join(__dirname, 'build'),
30+
targets: [
31+
{ set: 'apple', size: 16 },
32+
{ set: 'google', size: 16 },
33+
{ set: 'twitter', size: 16 },
34+
{ set: 'facebook', size: 16 },
35+
{ set: 'apple', size: 32 },
36+
{ set: 'google', size: 32 },
37+
{ set: 'twitter', size: 32 },
38+
{ set: 'facebook', size: 32 },
39+
],
40+
}
41+
42+
// Source Configuration.
43+
const sourceConfiguration = {
44+
getPathForTarget: target => path.join(__dirname, 'node_modules/emoji-datasource/img', target, 'sheets/32.png'),
45+
emojiSize: 32,
46+
emojiPadding: 1,
47+
spritesheetCache: { }
48+
}
49+
50+
51+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
52+
53+
54+
// Get all emojis with name.
55+
const emojis = emojiData.filter(emoji => emoji.name !== null && emoji.category !== 'Skin Tones')
56+
57+
// Group emojis by category.
58+
const categorizedEmojis = Array.from(new Set(emojis.map(emoji => emoji.category)))
59+
.map(category => ({ name: category, emojis: emojis.filter(emoji => emoji.category === category) }))
60+
.map(category => {
61+
const skinVariations = category.emojis
62+
.filter(emoji => emoji.skin_variations)
63+
.map(emoji => Object.values(emoji.skin_variations).map(skinVariation => ({ ...skinVariation, category: category.name, name: emoji.name })))
64+
.reduce((acc, current) => acc.concat(current), [])
65+
category.emojis.push(...skinVariations)
66+
return category
67+
})
68+
69+
// Create build directory if it doesn't exist.
70+
if (!fs.existsSync(targetConfiguration.folder)) fs.mkdirSync(targetConfiguration.folder)
71+
72+
73+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
74+
75+
76+
// Process each target.
77+
targetConfiguration.targets.forEach(target => {
78+
79+
// Create target path (where the finished emojis will be saved).
80+
const targetPath = path.join(targetConfiguration.folder, `${target.set}_${target.size}px`)
81+
82+
// Delete output directory, if it exists.
83+
lib.deleteDirRecursive(targetPath)
84+
85+
// Create target build directory.
86+
fs.mkdirSync(targetPath)
87+
88+
// Load spritesheet for current target (if it hasn't been loeaded before) and cache it.
89+
if (!sourceConfiguration.spritesheetCache[target.set])
90+
sourceConfiguration.spritesheetCache[target.set] = sharp(sourceConfiguration.getPathForTarget(target.set))
91+
92+
// Get spritesheet for current target.
93+
const spritesheet = sourceConfiguration.spritesheetCache[target.set]
94+
95+
// Generate individual emoji images.
96+
categorizedEmojis.forEach(async (category) => {
97+
98+
// Create target archive and write stream.
99+
const categoryArchive = archiver('zip')
100+
categoryArchive.pipe(fs.createWriteStream(path.join(targetPath, `${category.name}.zip`)))
101+
102+
// Process all emojis in this category.
103+
const emojiBufferPromises = category.emojis.map(async (emoji) => {
104+
105+
// Calcualte the actual size per emoji (relative to the entire spritesheet).
106+
const emojiOffset = sourceConfiguration.emojiSize + (sourceConfiguration.emojiPadding * 2)
107+
108+
// Get the image buffer for the current emoji.
109+
const buffer = await spritesheet
110+
.clone()
111+
.extract({
112+
left: emoji.sheet_x * emojiOffset + sourceConfiguration.emojiPadding,
113+
top: emoji.sheet_y * emojiOffset + sourceConfiguration.emojiPadding,
114+
width: sourceConfiguration.emojiSize,
115+
height: sourceConfiguration.emojiSize,
116+
})
117+
.resize(target.size)
118+
.png()
119+
.toBuffer()
120+
121+
return { name: emoji.image, buffer }
122+
})
123+
124+
// Add all emoji buffers to the archive.
125+
const emojiBuffers = await Promise.all(emojiBufferPromises)
126+
emojiBuffers.forEach(({ buffer, name }) => categoryArchive.append(buffer, { name }))
127+
128+
// Generate XML files for palettes.
129+
const xmlObject = {}
130+
xmlObject.palette = category.emojis.map(emoji => ({
131+
item: [
132+
{ name: { _cdata: emoji.name } },
133+
{ imgfile: emoji.image },
134+
{ imgWidth: targetConfiguration.emojiSize },
135+
{ imgHeight: targetConfiguration.emojiSize },
136+
{ alttext: { _cdata: emoji.name } },
137+
{ keyboard: emoji.short_name ? { _cdata: `:${emoji.short_name}:` } : undefined },
138+
]
139+
}))
140+
141+
// Generate XML string and apoend it to the archive.
142+
const xmlString = xml(xmlObject, { declaration: true })
143+
categoryArchive.append(xmlString, { name: 'palette.xml' })
144+
145+
// FInalize the archive
146+
categoryArchive.finalize()
147+
})
148+
})
149+
150+
151+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
152+

0 commit comments

Comments
 (0)