UNPKG

19.6 kBJavaScriptView Raw
1/* eslint-disable newline-per-chained-call */
2'use strict';
3
4const assert = require('assert');
5const fs = require('fs');
6const path = require('path');
7const ucs2 = require('punycode').ucs2;
8
9const SVGIcons2SVGFontStream = require('../src/index.js');
10const StringDecoder = require('string_decoder').StringDecoder;
11const SVGIconsDirStream = require('../src/iconsdir');
12const streamtest = require('streamtest');
13
14const neatequal = require('neatequal');
15const codepoint = require('./expected/test-codepoint.json');
16
17// Helpers
18function generateFontToFile(options, done, fileSuffix, startUnicode, files) {
19 // eslint-disable-line
20 const dest = path.join(
21 __dirname,
22 'results',
23 `${options.fontName + (fileSuffix || '')}.svg`
24 );
25
26 options.log = () => {};
27 options.round = options.round || 1e3;
28 const svgFontStream = new SVGIcons2SVGFontStream(options);
29
30 svgFontStream.pipe(fs.createWriteStream(dest)).on('finish', () => {
31 assert.equal(
32 fs.readFileSync(dest, { encoding: 'utf8' }),
33 fs.readFileSync(
34 path.join(
35 __dirname,
36 'expected',
37 `${options.fontName + (fileSuffix || '')}.svg`
38 ),
39 { encoding: 'utf8' }
40 )
41 );
42 done();
43 });
44
45 new SVGIconsDirStream(
46 files || path.join(__dirname, 'fixtures', options.fontName),
47 {
48 startUnicode: startUnicode || 0xe001,
49 }
50 ).pipe(svgFontStream);
51}
52
53function generateFontToMemory(options, done, files, startUnicode) {
54 let content = '';
55 const decoder = new StringDecoder('utf8');
56
57 options.log = () => {};
58 options.round = options.round || 1e3;
59
60 options.callback = glyphs => {
61 const fontName = options.fontName;
62
63 neatequal(glyphs, codepoint[fontName]);
64 };
65
66 const svgFontStream = new SVGIcons2SVGFontStream(options);
67
68 svgFontStream.on('data', chunk => {
69 content += decoder.write(chunk);
70 });
71
72 svgFontStream.on('finish', () => {
73 assert.equal(
74 content,
75 fs.readFileSync(
76 path.join(__dirname, 'expected', `${options.fontName}.svg`),
77 { encoding: 'utf8' }
78 )
79 );
80 done();
81 });
82
83 new SVGIconsDirStream(
84 files || path.join(__dirname, 'fixtures', options.fontName),
85 {
86 startUnicode: startUnicode || 0xe001,
87 }
88 ).pipe(svgFontStream);
89}
90
91// Tests
92describe('Generating fonts to files', () => {
93 it('should work for simple SVG', done => {
94 generateFontToFile(
95 {
96 fontName: 'originalicons',
97 },
98 done
99 );
100 });
101
102 it('should work for simple fixedWidth and normalize option', done => {
103 generateFontToFile(
104 {
105 fontName: 'originalicons',
106 fixedWidth: true,
107 normalize: true,
108 },
109 done,
110 'n'
111 );
112 });
113
114 it('should work for simple SVG', done => {
115 generateFontToFile(
116 {
117 fontName: 'cleanicons',
118 },
119 done
120 );
121 });
122
123 it('should work for simple SVG and custom ascent', done => {
124 generateFontToFile(
125 {
126 fontName: 'cleanicons',
127 ascent: 100,
128 },
129 done,
130 '-ascent'
131 );
132 });
133
134 it('should work for simple SVG and custom properties', done => {
135 generateFontToFile(
136 {
137 fontName: 'cleanicons',
138 fontStyle: 'italic',
139 fontWeight: 'bold',
140 },
141 done,
142 '-stw'
143 );
144 });
145
146 it('should work for codepoint mapped SVG icons', done => {
147 generateFontToFile(
148 {
149 fontName: 'prefixedicons',
150 callback: () => {},
151 },
152 done
153 );
154 });
155
156 it('should work with multipath SVG icons', done => {
157 generateFontToFile(
158 {
159 fontName: 'multipathicons',
160 },
161 done
162 );
163 });
164
165 it('should work with simple shapes SVG icons', done => {
166 generateFontToFile(
167 {
168 fontName: 'shapeicons',
169 },
170 done
171 );
172 });
173
174 it('should work with variable height icons', done => {
175 generateFontToFile(
176 {
177 fontName: 'variableheighticons',
178 },
179 done
180 );
181 });
182
183 it('should work with variable height icons and the normalize option', done => {
184 generateFontToFile(
185 {
186 fontName: 'variableheighticons',
187 normalize: true,
188 },
189 done,
190 'n'
191 );
192 });
193
194 it('should work with variable width icons', done => {
195 generateFontToFile(
196 {
197 fontName: 'variablewidthicons',
198 },
199 done
200 );
201 });
202
203 it('should work with centered variable width icons and the fixed width option', done => {
204 generateFontToFile(
205 {
206 fontName: 'variablewidthicons',
207 fixedWidth: true,
208 centerHorizontally: true,
209 },
210 done,
211 'n'
212 );
213 });
214
215 it('should work with a font id', done => {
216 generateFontToFile(
217 {
218 fontName: 'variablewidthicons',
219 fixedWidth: true,
220 centerHorizontally: true,
221 fontId: 'plop',
222 },
223 done,
224 'id'
225 );
226 });
227
228 it('should work with scaled icons', done => {
229 generateFontToFile(
230 {
231 fontName: 'scaledicons',
232 fixedWidth: true,
233 centerHorizontally: true,
234 fontId: 'plop',
235 },
236 done
237 );
238 });
239
240 it('should not display hidden paths', done => {
241 generateFontToFile(
242 {
243 fontName: 'hiddenpathesicons',
244 },
245 done
246 );
247 });
248
249 it('should work with real world icons', done => {
250 generateFontToFile(
251 {
252 fontName: 'realicons',
253 },
254 done
255 );
256 });
257
258 it('should work with rendering test SVG icons', done => {
259 generateFontToFile(
260 {
261 fontName: 'rendricons',
262 },
263 done
264 );
265 });
266
267 it('should work with a single SVG icon', done => {
268 generateFontToFile(
269 {
270 fontName: 'singleicon',
271 },
272 done
273 );
274 });
275
276 it('should work with transformed SVG icons', done => {
277 generateFontToFile(
278 {
279 fontName: 'transformedicons',
280 },
281 done
282 );
283 });
284
285 it('should work when horizontally centering SVG icons', done => {
286 generateFontToFile(
287 {
288 fontName: 'tocentericons',
289 centerHorizontally: true,
290 },
291 done
292 );
293 });
294
295 it('should work with a icons with path with fill none', done => {
296 generateFontToFile(
297 {
298 fontName: 'pathfillnone',
299 },
300 done
301 );
302 });
303
304 it('should work with shapes with rounded corners', done => {
305 generateFontToFile(
306 {
307 fontName: 'roundedcorners',
308 },
309 done
310 );
311 });
312
313 it('should work with a lot of icons', done => {
314 generateFontToFile(
315 {
316 fontName: 'lotoficons',
317 },
318 done,
319 '',
320 0,
321 [
322 'tests/fixtures/cleanicons/account.svg',
323 'tests/fixtures/cleanicons/arrow-down.svg',
324 'tests/fixtures/cleanicons/arrow-left.svg',
325 'tests/fixtures/cleanicons/arrow-right.svg',
326 'tests/fixtures/cleanicons/arrow-up.svg',
327 'tests/fixtures/cleanicons/basket.svg',
328 'tests/fixtures/cleanicons/close.svg',
329 'tests/fixtures/cleanicons/minus.svg',
330 'tests/fixtures/cleanicons/plus.svg',
331 'tests/fixtures/cleanicons/search.svg',
332 'tests/fixtures/hiddenpathesicons/sound--off.svg',
333 'tests/fixtures/hiddenpathesicons/sound--on.svg',
334 'tests/fixtures/multipathicons/kikoolol.svg',
335 'tests/fixtures/originalicons/mute.svg',
336 'tests/fixtures/originalicons/sound.svg',
337 'tests/fixtures/originalicons/speaker.svg',
338 'tests/fixtures/realicons/diegoliv.svg',
339 'tests/fixtures/realicons/hannesjohansson.svg',
340 'tests/fixtures/realicons/roelvanhitum.svg',
341 'tests/fixtures/realicons/safety-icon.svg',
342 'tests/fixtures/realicons/sb-icon.svg',
343 'tests/fixtures/realicons/settings-icon.svg',
344 'tests/fixtures/realicons/track-icon.svg',
345 'tests/fixtures/realicons/web-icon.svg',
346 'tests/fixtures/roundedcorners/roundedrect.svg',
347 'tests/fixtures/shapeicons/circle.svg',
348 'tests/fixtures/shapeicons/ellipse.svg',
349 'tests/fixtures/shapeicons/lines.svg',
350 'tests/fixtures/shapeicons/polygon.svg',
351 'tests/fixtures/shapeicons/polyline.svg',
352 'tests/fixtures/shapeicons/rect.svg',
353 'tests/fixtures/tocentericons/bottomleft.svg',
354 'tests/fixtures/tocentericons/center.svg',
355 'tests/fixtures/tocentericons/topright.svg',
356 ]
357 );
358 });
359
360 it('should work with rotated rectangle icon', done => {
361 generateFontToFile(
362 {
363 fontName: 'rotatedrectangle',
364 },
365 done
366 );
367 });
368
369 /**
370 * Issue #6
371 * icon by @paesku
372 * https://github.com/nfroidure/svgicons2svgfont/issues/6#issuecomment-125545925
373 */
374 it('should work with complicated nested transforms', done => {
375 generateFontToFile(
376 {
377 fontName: 'paesku',
378 round: 1e3,
379 },
380 done
381 );
382 });
383
384 /**
385 * Issue #76
386 * https://github.com/nfroidure/svgicons2svgfont/issues/76#issue-259831969
387 */
388 it('should work with transform=translate(x) without y', done => {
389 generateFontToFile(
390 {
391 fontName: 'translatex',
392 round: 1e3,
393 },
394 done
395 );
396 });
397
398 it('should work when only rx is present', done => {
399 generateFontToFile(
400 {
401 fontName: 'onlywithrx',
402 },
403 done
404 );
405 });
406
407 it('should work when only ry is present', done => {
408 generateFontToFile(
409 {
410 fontName: 'onlywithry',
411 },
412 done
413 );
414 });
415});
416
417describe('Generating fonts to memory', () => {
418 it('should work for simple SVG', done => {
419 generateFontToMemory(
420 {
421 fontName: 'originalicons',
422 },
423 done
424 );
425 });
426
427 it('should work for simple SVG', done => {
428 generateFontToMemory(
429 {
430 fontName: 'cleanicons',
431 },
432 done
433 );
434 });
435
436 it('should work for codepoint mapped SVG icons', done => {
437 generateFontToMemory(
438 {
439 fontName: 'prefixedicons',
440 },
441 done
442 );
443 });
444
445 it('should work with multipath SVG icons', done => {
446 generateFontToMemory(
447 {
448 fontName: 'multipathicons',
449 },
450 done
451 );
452 });
453
454 it('should work with simple shapes SVG icons', done => {
455 generateFontToMemory(
456 {
457 fontName: 'shapeicons',
458 },
459 done
460 );
461 });
462});
463
464describe('Using options', () => {
465 it('should work with fixedWidth option set to true', done => {
466 generateFontToFile(
467 {
468 fontName: 'originalicons',
469 fixedWidth: true,
470 },
471 done,
472 '2'
473 );
474 });
475
476 it('should work with custom fontHeight option', done => {
477 generateFontToFile(
478 {
479 fontName: 'originalicons',
480 fontHeight: 800,
481 },
482 done,
483 '3'
484 );
485 });
486
487 it('should work with custom descent option', done => {
488 generateFontToFile(
489 {
490 fontName: 'originalicons',
491 descent: 200,
492 },
493 done,
494 '4'
495 );
496 });
497
498 it('should work with fixedWidth set to true and with custom fontHeight option', done => {
499 generateFontToFile(
500 {
501 fontName: 'originalicons',
502 fontHeight: 800,
503 fixedWidth: true,
504 },
505 done,
506 '5'
507 );
508 });
509
510 it(
511 'should work with fixedWidth and centerHorizontally set to true and with' +
512 ' custom fontHeight option',
513 done => {
514 generateFontToFile(
515 {
516 fontName: 'originalicons',
517 fontHeight: 800,
518 fixedWidth: true,
519 centerHorizontally: true,
520 round: 1e5,
521 },
522 done,
523 '6'
524 );
525 }
526 );
527
528 it(
529 'should work with fixedWidth, normalize and centerHorizontally set to' +
530 ' true and with custom fontHeight option',
531 done => {
532 generateFontToFile(
533 {
534 fontName: 'originalicons',
535 fontHeight: 800,
536 normalize: true,
537 fixedWidth: true,
538 centerHorizontally: true,
539 round: 1e5,
540 },
541 done,
542 '7'
543 );
544 }
545 );
546
547 it(
548 'should work with fixedWidth, normalize and centerHorizontally set to' +
549 ' true and with a large custom fontHeight option',
550 done => {
551 generateFontToFile(
552 {
553 fontName: 'originalicons',
554 fontHeight: 5000,
555 normalize: true,
556 fixedWidth: true,
557 centerHorizontally: true,
558 round: 1e5,
559 },
560 done,
561 '8'
562 );
563 }
564 );
565
566 it('should work with nested icons', done => {
567 generateFontToFile(
568 {
569 fontName: 'nestedicons',
570 },
571 done,
572 '',
573 0xea01
574 );
575 });
576});
577
578describe('Passing code points', () => {
579 it('should work with multiple unicode values for a single icon', done => {
580 const svgIconStream = fs.createReadStream(
581 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
582 );
583
584 const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
585 let content = '';
586 const decoder = new StringDecoder('utf8');
587
588 svgIconStream.metadata = {
589 name: 'account',
590 unicode: ['\uE001', '\uE002'],
591 };
592
593 svgFontStream.on('data', chunk => {
594 content += decoder.write(chunk);
595 });
596
597 svgFontStream.on('finish', () => {
598 assert.equal(
599 content,
600 fs.readFileSync(
601 path.join(__dirname, 'expected', 'cleanicons-multi.svg'),
602 { encoding: 'utf8' }
603 )
604 );
605 done();
606 });
607 svgFontStream.write(svgIconStream);
608 svgFontStream.end();
609 });
610
611 it('should work with ligatures', done => {
612 const svgIconStream = fs.createReadStream(
613 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
614 );
615 const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
616 let content = '';
617 const decoder = new StringDecoder('utf8');
618
619 svgIconStream.metadata = {
620 name: 'account',
621 unicode: ['\uE001\uE002'],
622 };
623
624 svgFontStream.on('data', chunk => {
625 content += decoder.write(chunk);
626 });
627
628 svgFontStream.on('finish', () => {
629 assert.equal(
630 content,
631 fs.readFileSync(
632 path.join(__dirname, 'expected', 'cleanicons-lig.svg'),
633 { encoding: 'utf8' }
634 )
635 );
636 done();
637 });
638 svgFontStream.write(svgIconStream);
639 svgFontStream.end();
640 });
641
642 it('should work with high code points', done => {
643 const svgIconStream = fs.createReadStream(
644 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
645 );
646 const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
647 let content = '';
648 const decoder = new StringDecoder('utf8');
649
650 svgIconStream.metadata = {
651 name: 'account',
652 unicode: [ucs2.encode([0x1f63a])],
653 };
654
655 svgFontStream.on('data', chunk => {
656 content += decoder.write(chunk);
657 });
658
659 svgFontStream.on('finish', () => {
660 assert.equal(
661 content,
662 fs.readFileSync(
663 path.join(__dirname, 'expected', 'cleanicons-high.svg'),
664 { encoding: 'utf8' }
665 )
666 );
667 done();
668 });
669 svgFontStream.write(svgIconStream);
670 svgFontStream.end();
671 });
672});
673
674describe('Providing bad glyphs', () => {
675 it('should fail when not providing glyph name', done => {
676 const svgIconStream = fs.createReadStream(
677 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
678 );
679
680 svgIconStream.metadata = {
681 unicode: '\uE001',
682 };
683 new SVGIcons2SVGFontStream({ round: 1e3 })
684 .on('error', err => {
685 assert.equal(err instanceof Error, true);
686 assert.equal(
687 err.message,
688 'Please provide a name for the glyph at index 0'
689 );
690 done();
691 })
692 .write(svgIconStream);
693 });
694
695 it('should fail when not providing codepoints', done => {
696 const svgIconStream = fs.createReadStream(
697 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
698 );
699
700 svgIconStream.metadata = {
701 name: 'test',
702 };
703 new SVGIcons2SVGFontStream({ round: 1e3 })
704 .on('error', err => {
705 assert.equal(err instanceof Error, true);
706 assert.equal(
707 err.message,
708 'Please provide a codepoint for the glyph "test"'
709 );
710 done();
711 })
712 .write(svgIconStream);
713 });
714
715 it('should fail when providing unicode value with duplicates', done => {
716 const svgIconStream = fs.createReadStream(
717 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
718 );
719
720 svgIconStream.metadata = {
721 name: 'test',
722 unicode: ['\uE002', '\uE002'],
723 };
724 new SVGIcons2SVGFontStream({ round: 1e3 })
725 .on('error', err => {
726 assert.equal(err instanceof Error, true);
727 assert.equal(
728 err.message,
729 'Given codepoints for the glyph "test" contain duplicates.'
730 );
731 done();
732 })
733 .write(svgIconStream);
734 });
735
736 it('should fail when providing the same codepoint twice', done => {
737 const svgIconStream = fs.createReadStream(
738 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
739 );
740 const svgIconStream2 = fs.createReadStream(
741 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
742 );
743 const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
744
745 svgIconStream.metadata = {
746 name: 'test',
747 unicode: '\uE002',
748 };
749 svgIconStream2.metadata = {
750 name: 'test2',
751 unicode: '\uE002',
752 };
753 svgFontStream.on('error', err => {
754 assert.equal(err instanceof Error, true);
755 assert.equal(
756 err.message,
757 'The glyph "test2" codepoint seems to be used already elsewhere.'
758 );
759 done();
760 });
761 svgFontStream.write(svgIconStream);
762 svgFontStream.write(svgIconStream2);
763 });
764
765 it('should fail when providing the same name twice', done => {
766 const svgIconStream = fs.createReadStream(
767 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
768 );
769 const svgIconStream2 = fs.createReadStream(
770 path.join(__dirname, 'fixtures', 'cleanicons', 'account.svg')
771 );
772 const svgFontStream = new SVGIcons2SVGFontStream({ round: 1e3 });
773
774 svgIconStream.metadata = {
775 name: 'test',
776 unicode: '\uE001',
777 };
778 svgIconStream2.metadata = {
779 name: 'test',
780 unicode: '\uE002',
781 };
782 svgFontStream.on('error', err => {
783 assert.equal(err instanceof Error, true);
784 assert.equal(err.message, 'The glyph name "test" must be unique.');
785 done();
786 });
787 svgFontStream.write(svgIconStream);
788 svgFontStream.write(svgIconStream2);
789 });
790
791 it('should fail when providing bad pathdata', done => {
792 const svgIconStream = fs.createReadStream(
793 path.join(__dirname, 'fixtures', 'badicons', 'pathdata.svg')
794 );
795
796 svgIconStream.metadata = {
797 name: 'test',
798 unicode: ['\uE002'],
799 };
800 new SVGIcons2SVGFontStream({ round: 1e3 })
801 .on('error', err => {
802 assert.equal(err instanceof Error, true);
803 assert.equal(
804 err.message,
805 'Got an error parsing the glyph "test":' +
806 ' Expected a flag, got "120" at index "23".'
807 );
808 done();
809 })
810 .on('end', () => {
811 done();
812 })
813 .write(svgIconStream);
814 });
815
816 it('should fail when providing bad XML', done => {
817 const svgIconStream = streamtest.v2.fromChunks(['bad', 'xml']);
818
819 svgIconStream.metadata = {
820 name: 'test',
821 unicode: ['\uE002'],
822 };
823 new SVGIcons2SVGFontStream({ round: 1e3 })
824 .on('error', err => {
825 assert.equal(err instanceof Error, true);
826 assert.equal(
827 err.message,
828 'Non-whitespace before first tag.\nLine: 0\nColumn: 1\nChar: b'
829 );
830 done();
831 })
832 .write(svgIconStream);
833 });
834});