2018-08-27 19:44:42 +03:00
|
|
|
/**
|
|
|
|
* Tae Won Ha - http://taewon.de - @hataewon
|
|
|
|
* See LICENSE
|
|
|
|
*/
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
import XCTest
|
|
|
|
import Nimble
|
|
|
|
|
|
|
|
@testable import NvimView
|
|
|
|
|
|
|
|
class TypesetterWithoutLigaturesTest: XCTestCase {
|
|
|
|
|
|
|
|
func testSimpleAsciiChars() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:44:42 +03:00
|
|
|
nvimCells: emojiMarked(["a", "b", "c"]),
|
2018-08-27 19:51:34 +03:00
|
|
|
startColumn: 10,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(2))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(3))
|
|
|
|
expect(run.positions).to(equal(
|
2018-08-29 09:40:01 +03:00
|
|
|
(10..<13).map {
|
|
|
|
CGPoint(x: offset.x + CGFloat($0) * defaultWidth, y: offset.y)
|
|
|
|
}
|
2018-08-27 19:51:34 +03:00
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[1],
|
|
|
|
xPosition: offset.x + 13 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testAccentedChars() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:44:42 +03:00
|
|
|
nvimCells: emojiMarked(["ü", "î", "ñ"]),
|
2018-08-27 19:51:34 +03:00
|
|
|
startColumn: 20,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(2))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
let run = runs[0]
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(3))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.positions).to(equal(
|
2018-08-29 09:40:01 +03:00
|
|
|
(20..<23).map {
|
|
|
|
CGPoint(x: offset.x + CGFloat($0) * defaultWidth, y: offset.y)
|
|
|
|
}
|
2018-08-27 19:51:34 +03:00
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[1],
|
|
|
|
xPosition: offset.x + 23 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testCombiningChars() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:51:34 +03:00
|
|
|
nvimCells: emojiMarked(
|
|
|
|
["a", "a\u{1DC1}", "a\u{032A}", "a\u{034B}", "b", "c"]
|
|
|
|
),
|
|
|
|
startColumn: 10,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(6))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
var run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(1))
|
|
|
|
expect(run.positions).to(equal(
|
2018-08-27 19:51:34 +03:00
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 10 * defaultWidth, y: offset.y)
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
run = runs[1]
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(courierNew))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions[0])
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(equal(CGPoint(x: offset.x + 11 * defaultWidth, y: offset.y)))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.positions[1].x)
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(beCloseTo(offset.x + 11 * defaultWidth + 0.003, within: 0.001))
|
2018-08-30 18:48:49 +03:00
|
|
|
expect(run.positions[1].y).to(beCloseTo(offset.y + 0.305, within: 0.001))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[2]
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(defaultFont))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions[0])
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(equal(CGPoint(x: offset.x + 12 * defaultWidth, y: offset.y)))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.positions[1].x)
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(beCloseTo(offset.x + 12 * defaultWidth, within: 0.001))
|
2018-08-30 18:48:49 +03:00
|
|
|
expect(run.positions[1].y).to(beCloseTo(offset.y - 0.279, within: 0.001))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[3]
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(monaco))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions[0])
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(equal(CGPoint(x: offset.x + 13 * defaultWidth, y: offset.y)))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.positions[1].x)
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(beCloseTo(offset.x + 13 * defaultWidth + 7.804, within: 0.001))
|
2018-08-30 18:48:49 +03:00
|
|
|
expect(run.positions[1].y).to(beCloseTo(offset.y + 2.446, within: 0.001))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[4]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 14 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 15 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[5],
|
|
|
|
xPosition: offset.x + 16 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testSimpleEmojis() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:44:42 +03:00
|
|
|
nvimCells: asciiMarked(["a", "b", "\u{1F600}", "", "\u{1F377}", ""]),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(3))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
var run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 2 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[1]
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(emoji))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 3 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 5 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[2], xPosition: offset.x + 7 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testEmojisWithFitzpatrickModifier() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:44:42 +03:00
|
|
|
nvimCells: asciiMarked(["a", "\u{1F476}", "", "\u{1F3FD}", ""]),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(3))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
var run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(1))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[1]
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(emoji))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 2 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[2], xPosition: offset.x + 6 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testHangul() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:44:42 +03:00
|
|
|
nvimCells: asciiMarked(["a", "b", "하", "", "태", "", "원", ""]),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(3))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
var run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 2 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[1]
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(gothic))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 3 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 5 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 7 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[2], xPosition: offset.x + 9 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testHanja() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:44:42 +03:00
|
|
|
nvimCells: asciiMarked(["a", "b", "河", "", "泰", "", "元", ""]),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(3))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
var run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 2 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[1]
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(gothic))
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 3 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 5 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 7 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[2], xPosition: offset.x + 9 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testOthers() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:44:42 +03:00
|
|
|
nvimCells: emojiMarked(["a", "\u{10437}", "\u{1F14}"]),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(4))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
var run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(1))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y)
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[1]
|
|
|
|
expect(run.font).to(equal(baskerville))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 2 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
run = runs[2]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(1))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 3 * defaultWidth, y: offset.y),
|
2018-08-27 19:51:34 +03:00
|
|
|
]
|
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[3], xPosition: offset.x + 4 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testSimpleLigatureChars() {
|
2018-08-27 20:38:50 +03:00
|
|
|
let runs = typesetter.fontGlyphRunsWithoutLigatures(
|
2018-08-27 19:44:42 +03:00
|
|
|
nvimCells: emojiMarked(["a", "-", "-", ">", "a"]),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: fira,
|
|
|
|
cellWidth: firaWidth
|
|
|
|
)
|
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(runs).to(haveCount(2))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(fira))
|
|
|
|
expect(run.glyphs).to(equal([133, 1023, 1023, 1148, 133]))
|
|
|
|
expect(run.positions).to(equal(
|
2018-08-29 09:40:01 +03:00
|
|
|
(1..<6).map {
|
|
|
|
CGPoint(x: offset.x + CGFloat($0) * firaWidth, y: offset.y)
|
|
|
|
}
|
2018-08-27 19:51:34 +03:00
|
|
|
))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[1], xPosition: offset.x + 6 * firaWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
private func assertAsciiMarker(run: FontGlyphRun, xPosition: CGFloat) {
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(defaultFont))
|
2018-08-29 09:40:01 +03:00
|
|
|
expect(run.positions).to(equal([CGPoint(x: xPosition, y: offset.y)]))
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
2018-08-27 19:51:34 +03:00
|
|
|
private func assertEmojiMarker(run: FontGlyphRun, xPosition: CGFloat) {
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.font).to(equal(emoji))
|
2018-08-29 09:40:01 +03:00
|
|
|
expect(run.positions).to(equal([CGPoint(x: xPosition, y: offset.y)]))
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
class TypesetterWithLigaturesTest: XCTestCase {
|
2018-08-27 19:44:42 +03:00
|
|
|
|
|
|
|
func testSimpleAsciiChars() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
2018-08-29 09:40:01 +03:00
|
|
|
nvimUtf16Cells: utf16Chars(emojiMarked(
|
|
|
|
Array(repeating: "a", count: 20)
|
|
|
|
)),
|
2018-08-27 19:44:42 +03:00
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(2))
|
|
|
|
|
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
2018-08-29 09:40:01 +03:00
|
|
|
expect(run.glyphs).to(haveCount(20))
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.positions).to(equal(
|
2018-08-29 09:40:01 +03:00
|
|
|
(1..<21).map {
|
|
|
|
CGPoint(x: offset.x + CGFloat($0) * defaultWidth, y: offset.y)
|
|
|
|
}
|
2018-08-27 19:44:42 +03:00
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[1],
|
|
|
|
xPosition: offset.x + 21 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testAccentedChars() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
nvimUtf16Cells: utf16Chars(emojiMarked(["ü", "î", "ñ"])),
|
|
|
|
startColumn: 10,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
|
|
|
|
expect(runs).to(haveCount(2))
|
|
|
|
|
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(3))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 10 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 11 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 12 * defaultWidth, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[1], xPosition: offset.x + 13 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testCombiningChars() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
nvimUtf16Cells: utf16Chars(
|
|
|
|
emojiMarked(["a\u{1DC1}", "a\u{032A}", "a\u{034B}"])
|
|
|
|
),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(4))
|
|
|
|
|
|
|
|
// The positions of the combining characters are copied from print outputs
|
|
|
|
// and they are visually checked by drawing them and inspecting them...
|
|
|
|
var run = runs[0]
|
|
|
|
expect(run.font).to(equal(courierNew))
|
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions[0])
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(equal(CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y)))
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.positions[1].x)
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(beCloseTo(offset.x + 1 * defaultWidth + 0.003, within: 0.001))
|
2018-08-30 18:48:49 +03:00
|
|
|
expect(run.positions[1].y).to(beCloseTo(offset.y + 0.305, within: 0.001))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
|
|
|
run = runs[1]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions[0])
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(equal(CGPoint(x: offset.x + 2 * defaultWidth, y: offset.y)))
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.positions[1].x)
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(beCloseTo(offset.x + 2 * defaultWidth, within: 0.001))
|
2018-08-30 18:48:49 +03:00
|
|
|
expect(run.positions[1].y).to(beCloseTo(offset.y - 0.279, within: 0.001))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
|
|
|
run = runs[2]
|
|
|
|
expect(run.font).to(equal(monaco))
|
|
|
|
expect(run.glyphs).to(haveCount(2))
|
|
|
|
expect(run.positions[0])
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(equal(CGPoint(x: offset.x + 3 * defaultWidth, y: offset.y)))
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.positions[1].x)
|
2018-08-29 09:40:01 +03:00
|
|
|
.to(beCloseTo(offset.x + 3 * defaultWidth + 7.804, within: 0.001))
|
2018-08-30 18:48:49 +03:00
|
|
|
expect(run.positions[1].y).to(beCloseTo(offset.y + 2.446, within: 0.001))
|
2018-08-27 19:44:42 +03:00
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[3], xPosition: offset.x + 4 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testSimpleEmojis() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
nvimUtf16Cells: utf16Chars(
|
|
|
|
asciiMarked(["\u{1F600}", "", "\u{1F377}", ""])
|
|
|
|
),
|
|
|
|
startColumn: 0,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(2))
|
|
|
|
|
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(emoji))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 0, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 2 * defaultWidth, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[1], xPosition: offset.x + 4 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testEmojisWithFitzpatrickModifier() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
// Neovim does not yet seem to support the Fitzpatrick modifiers:
|
|
|
|
// It sends the following instead of ["\u{1F476}\u{1F3FD}", ""].
|
|
|
|
// We render it together anyway and treat it as a 4-cell character.
|
|
|
|
nvimUtf16Cells: utf16Chars(
|
|
|
|
asciiMarked(["\u{1F476}", "", "\u{1F3FD}", ""])
|
|
|
|
),
|
|
|
|
startColumn: 0,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(2))
|
|
|
|
|
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(emoji))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 0, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[1], xPosition: offset.x + 4 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testEmojisWithZeroWidthJoiner() {
|
|
|
|
// Neovim does not yet seem to support Emojis composed by zero-width-joiner:
|
|
|
|
// If it did, we'd render it correctly.
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
nvimUtf16Cells: utf16Chars(asciiMarked(
|
|
|
|
[
|
|
|
|
"\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F467}", "",
|
|
|
|
]
|
|
|
|
)),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(2))
|
|
|
|
|
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(emoji))
|
|
|
|
expect(run.glyphs).to(haveCount(1))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[1], xPosition: offset.x + 3 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testHangul() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
nvimUtf16Cells: utf16Chars(
|
|
|
|
asciiMarked(["하", "", "태", "", "원", ""])
|
|
|
|
),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(2))
|
|
|
|
|
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(gothic))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 3 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 5 * defaultWidth, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[1], xPosition: offset.x + 7 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testHanja() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
nvimUtf16Cells: utf16Chars(
|
|
|
|
asciiMarked(["河", "", "泰", "", "元", ""])
|
|
|
|
),
|
|
|
|
startColumn: 1,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(2))
|
|
|
|
|
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(gothic))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 3 * defaultWidth, y: offset.y),
|
|
|
|
CGPoint(x: offset.x + 5 * defaultWidth, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertAsciiMarker(run: runs[1], xPosition: offset.x + 7 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testOthers() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
nvimUtf16Cells: utf16Chars(
|
|
|
|
emojiMarked(["\u{10437}", "\u{1F14}"])
|
|
|
|
),
|
|
|
|
startColumn: 0,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: defaultFont,
|
|
|
|
cellWidth: defaultWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(3))
|
|
|
|
|
|
|
|
var run = runs[0]
|
|
|
|
expect(run.font).to(equal(baskerville))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 0, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
|
|
|
|
run = runs[1]
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: offset.x + 1 * defaultWidth, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[2], xPosition: offset.x + 2 * defaultWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func testSimpleLigatureChars() {
|
|
|
|
let runs = typesetter.fontGlyphRunsWithLigatures(
|
|
|
|
nvimUtf16Cells: utf16Chars(
|
2018-08-27 19:51:34 +03:00
|
|
|
emojiMarked(["-", "-", ">", "a"])
|
2018-08-27 19:44:42 +03:00
|
|
|
),
|
|
|
|
startColumn: 0,
|
2018-08-29 09:40:01 +03:00
|
|
|
offset: offset,
|
2018-08-27 19:44:42 +03:00
|
|
|
font: fira,
|
|
|
|
cellWidth: firaWidth
|
|
|
|
)
|
|
|
|
expect(runs).to(haveCount(2))
|
|
|
|
|
|
|
|
let run = runs[0]
|
|
|
|
expect(run.font).to(equal(fira))
|
|
|
|
// Ligatures of popular monospace fonts like Fira Code seem to be composed
|
|
|
|
// of multiple characters with the same advance as other normal characters.
|
2018-08-27 19:51:34 +03:00
|
|
|
expect(run.glyphs).to(equal([1614, 1614, 1063, 133]))
|
2018-08-27 19:44:42 +03:00
|
|
|
expect(run.positions).to(equal(
|
2018-08-29 09:40:01 +03:00
|
|
|
(0..<4).map {
|
|
|
|
CGPoint(x: offset.x + CGFloat($0) * firaWidth, y: offset.y)
|
|
|
|
}
|
2018-08-27 19:44:42 +03:00
|
|
|
))
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
self.assertEmojiMarker(run: runs[1], xPosition: offset.x + 4 * firaWidth)
|
2018-08-27 19:44:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private func assertAsciiMarker(run: FontGlyphRun, xPosition: CGFloat) {
|
|
|
|
expect(run.font).to(equal(defaultFont))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: xPosition, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
private func assertEmojiMarker(run: FontGlyphRun, xPosition: CGFloat) {
|
|
|
|
expect(run.font).to(equal(emoji))
|
|
|
|
expect(run.positions).to(equal(
|
|
|
|
[
|
2018-08-29 09:40:01 +03:00
|
|
|
CGPoint(x: xPosition, y: offset.y),
|
2018-08-27 19:44:42 +03:00
|
|
|
]
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private let defaultFont = NSFont(name: "Menlo", size: 13)!
|
|
|
|
private let fira = NSFont(name: "FiraCode-Regular", size: 13)!
|
|
|
|
private let courierNew = NSFont(name: "Courier New", size: 13)!
|
|
|
|
private let monaco = NSFont(name: "Monaco", size: 13)!
|
|
|
|
private let emoji = NSFont(name: "AppleColorEmoji", size: 13)!
|
|
|
|
private let gothic = NSFont(name: "Apple SD Gothic Neo", size: 13)!
|
|
|
|
private let baskerville = NSFont(name: "Baskerville", size: 13)!
|
|
|
|
|
|
|
|
private let defaultWidth = FontUtils
|
|
|
|
.cellSize(of: defaultFont, linespacing: 1).width
|
|
|
|
private let firaWidth = FontUtils.cellSize(of: fira, linespacing: 1).width
|
|
|
|
|
2018-08-29 09:40:01 +03:00
|
|
|
private let offset = CGPoint(x: 7, y: 8)
|
2018-08-27 19:44:42 +03:00
|
|
|
|
|
|
|
private let typesetter = Typesetter()
|
|
|
|
|
|
|
|
private func asciiMarked(_ strings: [String]) -> [String] {
|
|
|
|
return strings + ["a"]
|
|
|
|
}
|
|
|
|
|
|
|
|
private func emojiMarked(_ strings: [String]) -> [String] {
|
|
|
|
return strings + ["\u{1F600}"]
|
|
|
|
}
|
|
|
|
|
|
|
|
private func utf16Chars(_ array: [String]) -> [[Unicode.UTF16.CodeUnit]] {
|
|
|
|
return array.map { Array($0.utf16) }
|
|
|
|
}
|