fingerprintjs

Форк
0
151 строка · 4.5 Кб
1
import { releaseEventLoop } from '../utils/async'
2
import { withIframe } from '../utils/dom'
3

4
// We use m or w because these two characters take up the maximum width.
5
// And we use a LLi so that the same matching fonts can get separated.
6
const testString = 'mmMwWLliI0O&1'
7

8
// We test using 48px font size, we may use any size. I guess larger the better.
9
const textSize = '48px'
10

11
// A font will be compared against all the three default fonts.
12
// And if for any default fonts it doesn't match, then that font is available.
13
const baseFonts = ['monospace', 'sans-serif', 'serif'] as const
14

15
const fontList = [
16
  // This is android-specific font from "Roboto" family
17
  'sans-serif-thin',
18
  'ARNO PRO',
19
  'Agency FB',
20
  'Arabic Typesetting',
21
  'Arial Unicode MS',
22
  'AvantGarde Bk BT',
23
  'BankGothic Md BT',
24
  'Batang',
25
  'Bitstream Vera Sans Mono',
26
  'Calibri',
27
  'Century',
28
  'Century Gothic',
29
  'Clarendon',
30
  'EUROSTILE',
31
  'Franklin Gothic',
32
  'Futura Bk BT',
33
  'Futura Md BT',
34
  'GOTHAM',
35
  'Gill Sans',
36
  'HELV',
37
  'Haettenschweiler',
38
  'Helvetica Neue',
39
  'Humanst521 BT',
40
  'Leelawadee',
41
  'Letter Gothic',
42
  'Levenim MT',
43
  'Lucida Bright',
44
  'Lucida Sans',
45
  'Menlo',
46
  'MS Mincho',
47
  'MS Outlook',
48
  'MS Reference Specialty',
49
  'MS UI Gothic',
50
  'MT Extra',
51
  'MYRIAD PRO',
52
  'Marlett',
53
  'Meiryo UI',
54
  'Microsoft Uighur',
55
  'Minion Pro',
56
  'Monotype Corsiva',
57
  'PMingLiU',
58
  'Pristina',
59
  'SCRIPTINA',
60
  'Segoe UI Light',
61
  'Serifa',
62
  'SimHei',
63
  'Small Fonts',
64
  'Staccato222 BT',
65
  'TRAJAN PRO',
66
  'Univers CE 55 Medium',
67
  'Vrinda',
68
  'ZWAdobeF',
69
] as const
70

71
// kudos to http://www.lalit.org/lab/javascript-css-font-detect/
72
export default function getFonts(): Promise<string[]> {
73
  // Running the script in an iframe makes it not affect the page look and not be affected by the page CSS. See:
74
  // https://github.com/fingerprintjs/fingerprintjs/issues/592
75
  // https://github.com/fingerprintjs/fingerprintjs/issues/628
76
  return withIframe(async (_, { document }) => {
77
    const holder = document.body
78
    holder.style.fontSize = textSize
79

80
    // div to load spans for the default fonts and the fonts to detect
81
    const spansContainer = document.createElement('div')
82
    spansContainer.style.setProperty('visibility', 'hidden', 'important')
83

84
    const defaultWidth: Partial<Record<string, number>> = {}
85
    const defaultHeight: Partial<Record<string, number>> = {}
86

87
    // creates a span where the fonts will be loaded
88
    const createSpan = (fontFamily: string) => {
89
      const span = document.createElement('span')
90
      const { style } = span
91
      style.position = 'absolute'
92
      style.top = '0'
93
      style.left = '0'
94
      style.fontFamily = fontFamily
95
      span.textContent = testString
96
      spansContainer.appendChild(span)
97
      return span
98
    }
99

100
    // creates a span and load the font to detect and a base font for fallback
101
    const createSpanWithFonts = (fontToDetect: string, baseFont: string) => {
102
      return createSpan(`'${fontToDetect}',${baseFont}`)
103
    }
104

105
    // creates spans for the base fonts and adds them to baseFontsDiv
106
    const initializeBaseFontsSpans = () => {
107
      return baseFonts.map(createSpan)
108
    }
109

110
    // creates spans for the fonts to detect and adds them to fontsDiv
111
    const initializeFontsSpans = () => {
112
      // Stores {fontName : [spans for that font]}
113
      const spans: Record<string, HTMLSpanElement[]> = {}
114

115
      for (const font of fontList) {
116
        spans[font] = baseFonts.map((baseFont) => createSpanWithFonts(font, baseFont))
117
      }
118

119
      return spans
120
    }
121

122
    // checks if a font is available
123
    const isFontAvailable = (fontSpans: HTMLElement[]) => {
124
      return baseFonts.some(
125
        (baseFont, baseFontIndex) =>
126
          fontSpans[baseFontIndex].offsetWidth !== defaultWidth[baseFont] ||
127
          fontSpans[baseFontIndex].offsetHeight !== defaultHeight[baseFont],
128
      )
129
    }
130

131
    // create spans for base fonts
132
    const baseFontsSpans = initializeBaseFontsSpans()
133

134
    // create spans for fonts to detect
135
    const fontsSpans = initializeFontsSpans()
136

137
    // add all the spans to the DOM
138
    holder.appendChild(spansContainer)
139

140
    await releaseEventLoop()
141

142
    // get the default width for the three base fonts
143
    for (let index = 0; index < baseFonts.length; index++) {
144
      defaultWidth[baseFonts[index]] = baseFontsSpans[index].offsetWidth // width for the default font
145
      defaultHeight[baseFonts[index]] = baseFontsSpans[index].offsetHeight // height for the default font
146
    }
147

148
    // check available fonts
149
    return fontList.filter((font) => isFontAvailable(fontsSpans[font]))
150
  })
151
}
152

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.