1import copy
2
3import pyglet
4from pyglet.font import base
5from pyglet.image.codecs.wic import IWICBitmap, GUID_WICPixelFormat32bppBGR, WICDecoder, GUID_WICPixelFormat32bppBGRA, \
6    GUID_WICPixelFormat32bppPBGRA
7
8from pyglet import image
9import ctypes
10import math
11from pyglet import com
12from pyglet.libs.win32 import _kernel32 as kernel32
13from pyglet.libs.win32 import _ole32 as ole32
14from pyglet.libs.win32.constants import *
15from pyglet.libs.win32.types import *
16from ctypes import *
17import os
18import platform
19
20try:
21    dwrite = 'dwrite'
22
23    # System32 and SysWOW64 folders are opposite perception in Windows x64.
24    # System32 = x64 dll's | SysWOW64 = x86 dlls
25    # By default ctypes only seems to look in system32 regardless of Python architecture, which has x64 dlls.
26    if platform.architecture()[0] == '32bit':
27        if platform.machine().endswith('64'):  # Machine is 64 bit, Python is 32 bit.
28            dwrite = os.path.join(os.environ['WINDIR'], 'SysWOW64', 'dwrite.dll')
29
30    dwrite_lib = ctypes.windll.LoadLibrary(dwrite)
31except OSError as err:
32    # Doesn't exist? Should stop import of library.
33    pass
34
35
36def DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d):
37    return ord(d) << 24 | ord(c) << 16 | ord(b) << 8 | ord(a)
38
39
40DWRITE_FACTORY_TYPE = UINT
41DWRITE_FACTORY_TYPE_SHARED = 0
42DWRITE_FACTORY_TYPE_ISOLATED = 1
43
44DWRITE_FONT_WEIGHT = UINT
45DWRITE_FONT_WEIGHT_THIN = 100
46DWRITE_FONT_WEIGHT_EXTRA_LIGHT = 200
47DWRITE_FONT_WEIGHT_ULTRA_LIGHT = 200
48DWRITE_FONT_WEIGHT_LIGHT = 300
49DWRITE_FONT_WEIGHT_SEMI_LIGHT = 350
50DWRITE_FONT_WEIGHT_NORMAL = 400
51DWRITE_FONT_WEIGHT_REGULAR = 400
52DWRITE_FONT_WEIGHT_MEDIUM = 500
53DWRITE_FONT_WEIGHT_DEMI_BOLD = 600
54DWRITE_FONT_WEIGHT_SEMI_BOLD = 600
55DWRITE_FONT_WEIGHT_BOLD = 700
56DWRITE_FONT_WEIGHT_EXTRA_BOLD = 800
57DWRITE_FONT_WEIGHT_ULTRA_BOLD = 800
58DWRITE_FONT_WEIGHT_BLACK = 900
59DWRITE_FONT_WEIGHT_HEAVY = 900
60DWRITE_FONT_WEIGHT_EXTRA_BLACK = 950
61
62name_to_weight = {"thin": DWRITE_FONT_WEIGHT_THIN,
63                  "extralight": DWRITE_FONT_WEIGHT_EXTRA_LIGHT,
64                  "ultralight": DWRITE_FONT_WEIGHT_ULTRA_LIGHT,
65                  "light": DWRITE_FONT_WEIGHT_LIGHT,
66                  "semilight": DWRITE_FONT_WEIGHT_SEMI_LIGHT,
67                  "normal": DWRITE_FONT_WEIGHT_NORMAL,
68                  "regular": DWRITE_FONT_WEIGHT_REGULAR,
69                  "medium": DWRITE_FONT_WEIGHT_MEDIUM,
70                  "demibold": DWRITE_FONT_WEIGHT_DEMI_BOLD,
71                  "semibold": DWRITE_FONT_WEIGHT_SEMI_BOLD,
72                  "bold": DWRITE_FONT_WEIGHT_BOLD,
73                  "extrabold": DWRITE_FONT_WEIGHT_EXTRA_BOLD,
74                  "ultrabold": DWRITE_FONT_WEIGHT_ULTRA_BOLD,
75                  "black": DWRITE_FONT_WEIGHT_BLACK,
76                  "heavy": DWRITE_FONT_WEIGHT_HEAVY,
77                  "extrablack": DWRITE_FONT_WEIGHT_EXTRA_BLACK,
78                  }
79
80DWRITE_FONT_STRETCH = UINT
81DWRITE_FONT_STRETCH_UNDEFINED = 0
82DWRITE_FONT_STRETCH_ULTRA_CONDENSED = 1
83DWRITE_FONT_STRETCH_EXTRA_CONDENSED = 2
84DWRITE_FONT_STRETCH_CONDENSED = 3
85DWRITE_FONT_STRETCH_SEMI_CONDENSED = 4
86DWRITE_FONT_STRETCH_NORMAL = 5
87DWRITE_FONT_STRETCH_MEDIUM = 5
88DWRITE_FONT_STRETCH_SEMI_EXPANDED = 6
89DWRITE_FONT_STRETCH_EXPANDED = 7
90DWRITE_FONT_STRETCH_EXTRA_EXPANDED = 8
91
92name_to_stretch = {"undefined": DWRITE_FONT_STRETCH_UNDEFINED,
93                   "ultracondensed": DWRITE_FONT_STRETCH_ULTRA_CONDENSED,
94                   "extracondensed": DWRITE_FONT_STRETCH_EXTRA_CONDENSED,
95                   "condensed": DWRITE_FONT_STRETCH_CONDENSED,
96                   "semicondensed": DWRITE_FONT_STRETCH_SEMI_CONDENSED,
97                   "normal": DWRITE_FONT_STRETCH_NORMAL,
98                   "medium": DWRITE_FONT_STRETCH_MEDIUM,
99                   "semiexpanded": DWRITE_FONT_STRETCH_SEMI_EXPANDED,
100                   "expanded": DWRITE_FONT_STRETCH_EXPANDED,
101                   "extraexpanded": DWRITE_FONT_STRETCH_EXTRA_EXPANDED,
102                   }
103
104DWRITE_FONT_STYLE = UINT
105DWRITE_FONT_STYLE_NORMAL = 0
106DWRITE_FONT_STYLE_OBLIQUE = 1
107DWRITE_FONT_STYLE_ITALIC = 2
108
109name_to_style = {"normal": DWRITE_FONT_STYLE_NORMAL,
110                 "oblique": DWRITE_FONT_STYLE_OBLIQUE,
111                 "italic": DWRITE_FONT_STYLE_ITALIC}
112
113UINT8 = c_uint8
114UINT16 = c_uint16
115INT16 = c_int16
116INT32 = c_int32
117UINT32 = c_uint32
118UINT64 = c_uint64
119
120
121class DWRITE_TEXT_METRICS(ctypes.Structure):
122    _fields_ = (
123        ('left', FLOAT),
124        ('top', FLOAT),
125        ('width', FLOAT),
126        ('widthIncludingTrailingWhitespace', FLOAT),
127        ('height', FLOAT),
128        ('layoutWidth', FLOAT),
129        ('layoutHeight', FLOAT),
130        ('maxBidiReorderingDepth', UINT32),
131        ('lineCount', UINT32),
132    )
133
134
135class DWRITE_FONT_METRICS(ctypes.Structure):
136    _fields_ = (
137        ('designUnitsPerEm', UINT16),
138        ('ascent', UINT16),
139        ('descent', UINT16),
140        ('lineGap', INT16),
141        ('capHeight', UINT16),
142        ('xHeight', UINT16),
143        ('underlinePosition', INT16),
144        ('underlineThickness', UINT16),
145        ('strikethroughPosition', INT16),
146        ('strikethroughThickness', UINT16),
147    )
148
149
150class DWRITE_GLYPH_METRICS(ctypes.Structure):
151    _fields_ = (
152        ('leftSideBearing', INT32),
153        ('advanceWidth', UINT32),
154        ('rightSideBearing', INT32),
155        ('topSideBearing', INT32),
156        ('advanceHeight', UINT32),
157        ('bottomSideBearing', INT32),
158        ('verticalOriginY', INT32),
159    )
160
161
162class DWRITE_GLYPH_OFFSET(ctypes.Structure):
163    _fields_ = (
164        ('advanceOffset', FLOAT),
165        ('ascenderOffset', FLOAT),
166    )
167
168    def __repr__(self):
169        return f"DWRITE_GLYPH_OFFSET({self.advanceOffset}, {self.ascenderOffset})"
170
171
172class DWRITE_CLUSTER_METRICS(ctypes.Structure):
173    _fields_ = (
174        ('width', FLOAT),
175        ('length', UINT16),
176        ('canWrapLineAfter', UINT16, 1),
177        ('isWhitespace', UINT16, 1),
178        ('isNewline', UINT16, 1),
179        ('isSoftHyphen', UINT16, 1),
180        ('isRightToLeft', UINT16, 1),
181        ('padding', UINT16, 11),
182    )
183
184
185class IDWriteFontFace(com.pIUnknown):
186    _methods_ = [
187        ('GetType',
188         com.STDMETHOD()),
189        ('GetFiles',
190         com.STDMETHOD()),
191        ('GetIndex',
192         com.STDMETHOD()),
193        ('GetSimulations',
194         com.STDMETHOD()),
195        ('IsSymbolFont',
196         com.STDMETHOD()),
197        ('GetMetrics',
198         com.METHOD(c_void, POINTER(DWRITE_FONT_METRICS))),
199        ('GetGlyphCount',
200         com.STDMETHOD()),
201        ('GetDesignGlyphMetrics',
202         com.STDMETHOD(POINTER(UINT16), UINT32, POINTER(DWRITE_GLYPH_METRICS), BOOL)),
203        ('GetGlyphIndices',
204         com.STDMETHOD(POINTER(UINT32), UINT32, POINTER(UINT16))),
205        ('TryGetFontTable',
206         com.STDMETHOD(UINT32, c_void_p, POINTER(UINT32), c_void_p, POINTER(BOOL))),
207        ('ReleaseFontTable',
208         com.METHOD(c_void)),
209        ('GetGlyphRunOutline',
210         com.STDMETHOD()),
211        ('GetRecommendedRenderingMode',
212         com.STDMETHOD()),
213        ('GetGdiCompatibleMetrics',
214         com.STDMETHOD()),
215        ('GetGdiCompatibleGlyphMetrics',
216         com.STDMETHOD()),
217    ]
218
219
220IID_IDWriteFontFace1 = com.GUID(0xa71efdb4, 0x9fdb, 0x4838, 0xad, 0x90, 0xcf, 0xc3, 0xbe, 0x8c, 0x3d, 0xaf)
221
222
223class IDWriteFontFace1(IDWriteFontFace, com.pIUnknown):
224    _methods_ = [
225        ('GetMetric1',
226         com.STDMETHOD()),
227        ('GetGdiCompatibleMetrics1',
228         com.STDMETHOD()),
229        ('GetCaretMetrics',
230         com.STDMETHOD()),
231        ('GetUnicodeRanges',
232         com.STDMETHOD()),
233        ('IsMonospacedFont',
234         com.STDMETHOD()),
235        ('GetDesignGlyphAdvances',
236         com.METHOD(c_void, POINTER(DWRITE_FONT_METRICS))),
237        ('GetGdiCompatibleGlyphAdvances',
238         com.STDMETHOD()),
239        ('GetKerningPairAdjustments',
240         com.STDMETHOD(UINT32, POINTER(UINT16), POINTER(INT32))),
241        ('HasKerningPairs',
242         com.METHOD(BOOL)),
243        ('GetRecommendedRenderingMode1',
244         com.STDMETHOD()),
245        ('GetVerticalGlyphVariants',
246         com.STDMETHOD()),
247        ('HasVerticalGlyphVariants',
248         com.STDMETHOD())
249    ]
250
251
252DWRITE_SCRIPT_SHAPES = UINT
253DWRITE_SCRIPT_SHAPES_DEFAULT = 0
254
255
256class DWRITE_SCRIPT_ANALYSIS(ctypes.Structure):
257    _fields_ = (
258        ('script', UINT16),
259        ('shapes', DWRITE_SCRIPT_SHAPES),
260    )
261
262
263DWRITE_FONT_FEATURE_TAG = UINT
264
265
266class DWRITE_FONT_FEATURE(ctypes.Structure):
267    _fields_ = (
268        ('nameTag', DWRITE_FONT_FEATURE_TAG),
269        ('parameter', UINT32),
270    )
271
272
273class DWRITE_TYPOGRAPHIC_FEATURES(ctypes.Structure):
274    _fields_ = (
275        ('features', POINTER(DWRITE_FONT_FEATURE)),
276        ('featureCount', UINT32),
277    )
278
279
280class DWRITE_SHAPING_TEXT_PROPERTIES(ctypes.Structure):
281    _fields_ = (
282        ('isShapedAlone', UINT16, 1),
283        ('reserved1', UINT16, 1),
284        ('canBreakShapingAfter', UINT16, 1),
285        ('reserved', UINT16, 13),
286    )
287
288    def __repr__(self):
289        return f"DWRITE_SHAPING_TEXT_PROPERTIES({self.isShapedAlone}, {self.reserved1}, {self.canBreakShapingAfter})"
290
291
292class DWRITE_SHAPING_GLYPH_PROPERTIES(ctypes.Structure):
293    _fields_ = (
294        ('justification', UINT16, 4),
295        ('isClusterStart', UINT16, 1),
296        ('isDiacritic', UINT16, 1),
297        ('isZeroWidthSpace', UINT16, 1),
298        ('reserved', UINT16, 9),
299    )
300
301
302DWRITE_READING_DIRECTION = UINT
303DWRITE_READING_DIRECTION_LEFT_TO_RIGHT = 0
304
305
306class IDWriteTextAnalysisSource(com.IUnknown):
307    _methods_ = [
308        ('GetTextAtPosition',
309         com.METHOD(HRESULT, c_void_p, UINT32, POINTER(c_wchar_p), POINTER(UINT32))),
310        ('GetTextBeforePosition',
311         com.STDMETHOD(UINT32, c_wchar_p, POINTER(UINT32))),
312        ('GetParagraphReadingDirection',
313         com.METHOD(DWRITE_READING_DIRECTION)),
314        ('GetLocaleName',
315         com.STDMETHOD(c_void_p, UINT32, POINTER(UINT32), POINTER(c_wchar_p))),
316        ('GetNumberSubstitution',
317         com.STDMETHOD(UINT32, POINTER(UINT32), c_void_p)),
318    ]
319
320
321class IDWriteTextAnalysisSink(com.IUnknown):
322    _methods_ = [
323        ('SetScriptAnalysis',
324         com.STDMETHOD(c_void_p, UINT32, UINT32, POINTER(DWRITE_SCRIPT_ANALYSIS))),
325        ('SetLineBreakpoints',
326         com.STDMETHOD(UINT32, UINT32, c_void_p)),
327        ('SetBidiLevel',
328         com.STDMETHOD(UINT32, UINT32, UINT8, UINT8)),
329        ('SetNumberSubstitution',
330         com.STDMETHOD(UINT32, UINT32, c_void_p)),
331    ]
332
333
334class Run:
335    def __init__(self):
336        self.text_start = 0
337        self.text_length = 0
338        self.glyph_start = 0
339        self.glyph_count = 0
340        self.script = DWRITE_SCRIPT_ANALYSIS()
341        self.bidi = 0
342        self.isNumberSubstituted = False
343        self.isSideways = False
344
345        self.next_run = None
346
347    def ContainsTextPosition(self, textPosition):
348        return textPosition >= self.text_start and textPosition < self.text_start + self.text_length
349
350
351class TextAnalysis(com.COMObject):
352    _interfaces_ = [IDWriteTextAnalysisSource, IDWriteTextAnalysisSink]
353
354    def __init__(self):
355        super().__init__()
356        self._textstart = 0
357        self._textlength = 0
358        self._glyphstart = 0
359        self._glyphcount = 0
360        self._ptrs = []
361
362        self._script = None
363        self._bidi = 0
364        # self._sideways = False
365
366    def GenerateResults(self, analyzer, text, text_length):
367        self._text = text
368        self._textstart = 0
369        self._textlength = text_length
370        self._glyphstart = 0
371        self._glyphcount = 0
372
373        self._start_run = Run()
374        self._start_run.text_length = text_length
375
376        self._current_run = self._start_run
377
378        analyzer.AnalyzeScript(self, 0, text_length, self)
379
380    def SetScriptAnalysis(self, this, textPosition, textLength, scriptAnalysis):
381        # textPosition - The index of the first character in the string that the result applies to
382        # textLength - How many characters of the string from the index that the result applies to
383        # scriptAnalysis - The analysis information for all glyphs starting at position for length.
384        self.SetCurrentRun(textPosition)
385        self.SplitCurrentRun(textPosition)
386
387        while textLength > 0:
388            run, textLength = self.FetchNextRun(textLength)
389
390            run.script.script = scriptAnalysis[0].script
391            run.script.shapes = scriptAnalysis[0].shapes
392
393            self._script = run.script
394
395        return 0
396        # return 0x80004001
397
398    def GetTextBeforePosition(self, this):
399        raise Exception("Currently not implemented.")
400
401    def GetTextAtPosition(self, this, textPosition, textString, textLength):
402        # This method will retrieve a substring of the text in this layout
403        #   to be used in an analysis step.
404        # Arguments:
405        # textPosition - The index of the first character of the text to retrieve.
406        # textString - The pointer to the first character of text at the index requested.
407        # textLength - The characters available at/after the textString pointer (string length).
408        if textPosition >= self._textlength:
409            self._no_ptr = c_wchar_p(None)
410            textString[0] = self._no_ptr
411            textLength[0] = 0
412        else:
413            ptr = c_wchar_p(self._text[textPosition:])
414            self._ptrs.append(ptr)
415            textString[0] = ptr
416            textLength[0] = self._textlength - textPosition
417
418        return 0
419
420    def GetParagraphReadingDirection(self):
421        return 0
422
423    def GetLocaleName(self, this, textPosition, textLength, localeName):
424        self.__local_name = c_wchar_p("")  # TODO: Add more locales.
425        localeName[0] = self.__local_name
426        textLength[0] = self._textlength - textPosition
427        return 0
428
429    def GetNumberSubstitution(self):
430        return 0
431
432    def SetCurrentRun(self, textPosition):
433        if self._current_run and self._current_run.ContainsTextPosition(textPosition):
434            return
435
436    def SplitCurrentRun(self, textPosition):
437        if not self._current_run:
438            return
439
440        if textPosition <= self._current_run.text_start:
441            # Already first start of the run.
442            return
443
444        new_run = copy.copy(self._current_run)
445
446        new_run.next_run = self._current_run.next_run
447        self._current_run.next_run = new_run
448
449        splitPoint = textPosition - self._current_run.text_start
450        new_run.text_start += splitPoint
451        new_run.text_length -= splitPoint
452
453        self._current_run.text_length = splitPoint
454        self._current_run = new_run
455
456    def FetchNextRun(self, textLength):
457        original_run = self._current_run
458
459        if (textLength < self._current_run.text_length):
460            self.SplitCurrentRun(self._current_run.text_start + textLength)
461        else:
462            self._current_run = self._current_run.next_run
463
464        textLength -= original_run.text_length
465
466        return original_run, textLength
467
468
469class IDWriteTextAnalyzer(com.pIUnknown):
470    _methods_ = [
471        ('AnalyzeScript',
472         com.STDMETHOD(POINTER(IDWriteTextAnalysisSource), UINT32, UINT32, POINTER(IDWriteTextAnalysisSink))),
473        ('AnalyzeBidi',
474         com.STDMETHOD()),
475        ('AnalyzeNumberSubstitution',
476         com.STDMETHOD()),
477        ('AnalyzeLineBreakpoints',
478         com.STDMETHOD()),
479        ('GetGlyphs',
480         com.STDMETHOD(c_wchar_p, UINT32, IDWriteFontFace, BOOL, BOOL, POINTER(DWRITE_SCRIPT_ANALYSIS),
481                       c_wchar_p, c_void_p, POINTER(POINTER(DWRITE_TYPOGRAPHIC_FEATURES)), POINTER(UINT32),
482                       UINT32, UINT32, POINTER(UINT16), POINTER(DWRITE_SHAPING_TEXT_PROPERTIES),
483                       POINTER(UINT16), POINTER(DWRITE_SHAPING_GLYPH_PROPERTIES), POINTER(UINT32))),
484        ('GetGlyphPlacements',
485         com.STDMETHOD(c_wchar_p, POINTER(UINT16), POINTER(DWRITE_SHAPING_TEXT_PROPERTIES), UINT32, POINTER(UINT16),
486                       POINTER(DWRITE_SHAPING_GLYPH_PROPERTIES), UINT32, IDWriteFontFace, FLOAT, BOOL, BOOL,
487                       POINTER(DWRITE_SCRIPT_ANALYSIS), c_wchar_p, POINTER(DWRITE_TYPOGRAPHIC_FEATURES),
488                       POINTER(UINT32), UINT32, POINTER(FLOAT), POINTER(DWRITE_GLYPH_OFFSET))),
489        ('GetGdiCompatibleGlyphPlacements',
490         com.STDMETHOD()),
491    ]
492
493
494class IDWriteLocalizedStrings(com.pIUnknown):
495    _methods_ = [
496        ('GetCount',
497         com.METHOD(UINT32)),
498        ('FindLocaleName',
499         com.STDMETHOD(c_wchar_p, POINTER(UINT32), POINTER(BOOL))),
500        ('GetLocaleNameLength',
501         com.STDMETHOD(UINT32, POINTER(UINT32))),
502        ('GetLocaleName',
503         com.STDMETHOD(UINT32, c_wchar_p, UINT32)),
504        ('GetStringLength',
505         com.STDMETHOD(UINT32, POINTER(UINT32))),
506        ('GetString',
507         com.STDMETHOD(UINT32, c_wchar_p, UINT32)),
508    ]
509
510
511class IDWriteFontList(com.pIUnknown):
512    _methods_ = [
513        ('GetFontCollection',
514         com.STDMETHOD()),
515        ('GetFontCount',
516         com.STDMETHOD()),
517        ('GetFont',
518         com.STDMETHOD()),
519    ]
520
521
522class IDWriteFontFamily(IDWriteFontList, com.pIUnknown):
523    _methods_ = [
524        ('GetFamilyNames',
525         com.STDMETHOD(POINTER(IDWriteLocalizedStrings))),
526        ('GetFirstMatchingFont',
527         com.STDMETHOD(DWRITE_FONT_WEIGHT, DWRITE_FONT_STRETCH, DWRITE_FONT_STYLE, c_void_p)),
528        ('GetMatchingFonts',
529         com.STDMETHOD()),
530    ]
531
532
533class IDWriteFontFamily1(IDWriteFontFamily, IDWriteFontList, com.pIUnknown):
534    _methods_ = [
535        ('GetFontLocality',
536         com.STDMETHOD()),
537        ('GetFont1',
538         com.STDMETHOD()),
539        ('GetFontFaceReference',
540         com.STDMETHOD()),
541    ]
542
543
544class IDWriteFontFile(com.pIUnknown):
545    _methods_ = [
546        ('GetReferenceKey',
547         com.STDMETHOD()),
548        ('GetLoader',
549         com.STDMETHOD()),
550        ('Analyze',
551         com.STDMETHOD()),
552    ]
553
554
555class IDWriteFont(com.pIUnknown):
556    _methods_ = [
557        ('GetFontFamily',
558         com.STDMETHOD(POINTER(IDWriteFontFamily))),
559        ('GetWeight',
560         com.STDMETHOD()),
561        ('GetStretch',
562         com.STDMETHOD()),
563        ('GetStyle',
564         com.STDMETHOD()),
565        ('IsSymbolFont',
566         com.STDMETHOD()),
567        ('GetFaceNames',
568         com.STDMETHOD(POINTER(IDWriteLocalizedStrings))),
569        ('GetInformationalStrings',
570         com.STDMETHOD()),
571        ('GetSimulations',
572         com.STDMETHOD()),
573        ('GetMetrics',
574         com.STDMETHOD()),
575        ('HasCharacter',
576         com.STDMETHOD(UINT32, POINTER(BOOL))),
577        ('CreateFontFace',
578         com.STDMETHOD(POINTER(IDWriteFontFace))),
579    ]
580
581
582class IDWriteFont1(IDWriteFont, com.pIUnknown):
583    _methods_ = [
584        ('GetMetrics1',
585         com.STDMETHOD()),
586        ('GetPanose',
587         com.STDMETHOD()),
588        ('GetUnicodeRanges',
589         com.STDMETHOD()),
590        ('IsMonospacedFont',
591         com.STDMETHOD())
592    ]
593
594
595class IDWriteFontCollection(com.pIUnknown):
596    _methods_ = [
597        ('GetFontFamilyCount',
598         com.STDMETHOD()),
599        ('GetFontFamily',
600         com.STDMETHOD(UINT32, POINTER(IDWriteFontFamily))),
601        ('FindFamilyName',
602         com.STDMETHOD(c_wchar_p, POINTER(UINT), POINTER(BOOL))),
603        ('GetFontFromFontFace',
604         com.STDMETHOD()),
605    ]
606
607
608class IDWriteFontCollection1(IDWriteFontCollection, com.pIUnknown):
609    _methods_ = [
610        ('GetFontSet',
611         com.STDMETHOD()),
612        ('GetFontFamily1',
613         com.STDMETHOD(POINTER(IDWriteFontFamily1))),
614    ]
615
616
617DWRITE_TEXT_ALIGNMENT = UINT
618DWRITE_TEXT_ALIGNMENT_LEADING = 1
619DWRITE_TEXT_ALIGNMENT_TRAILING = 2
620DWRITE_TEXT_ALIGNMENT_CENTER = 3
621DWRITE_TEXT_ALIGNMENT_JUSTIFIED = 4
622
623
624class IDWriteTextFormat(com.pIUnknown):
625    _methods_ = [
626        ('SetTextAlignment',
627         com.STDMETHOD(DWRITE_TEXT_ALIGNMENT)),
628        ('SetParagraphAlignment',
629         com.STDMETHOD()),
630        ('SetWordWrapping',
631         com.STDMETHOD()),
632        ('SetReadingDirection',
633         com.STDMETHOD()),
634        ('SetFlowDirection',
635         com.STDMETHOD()),
636        ('SetIncrementalTabStop',
637         com.STDMETHOD()),
638        ('SetTrimming',
639         com.STDMETHOD()),
640        ('SetLineSpacing',
641         com.STDMETHOD()),
642        ('GetTextAlignment',
643         com.STDMETHOD()),
644        ('GetParagraphAlignment',
645         com.STDMETHOD()),
646        ('GetWordWrapping',
647         com.STDMETHOD()),
648        ('GetReadingDirection',
649         com.STDMETHOD()),
650        ('GetFlowDirection',
651         com.STDMETHOD()),
652        ('GetIncrementalTabStop',
653         com.STDMETHOD()),
654        ('GetTrimming',
655         com.STDMETHOD()),
656        ('GetLineSpacing',
657         com.STDMETHOD()),
658        ('GetFontCollection',
659         com.STDMETHOD()),
660        ('GetFontFamilyNameLength',
661         com.STDMETHOD()),
662        ('GetFontFamilyName',
663         com.STDMETHOD()),
664        ('GetFontWeight',
665         com.STDMETHOD()),
666        ('GetFontStyle',
667         com.STDMETHOD()),
668        ('GetFontStretch',
669         com.STDMETHOD()),
670        ('GetFontSize',
671         com.STDMETHOD()),
672        ('GetLocaleNameLength',
673         com.STDMETHOD()),
674        ('GetLocaleName',
675         com.STDMETHOD()),
676    ]
677
678
679class IDWriteTypography(com.pIUnknown):
680    _methods_ = [
681        ('AddFontFeature',
682         com.STDMETHOD(DWRITE_FONT_FEATURE)),
683        ('GetFontFeatureCount',
684         com.METHOD(UINT32)),
685        ('GetFontFeature',
686         com.STDMETHOD())
687    ]
688
689
690class DWRITE_TEXT_RANGE(ctypes.Structure):
691    _fields_ = (
692        ('startPosition', UINT32),
693        ('length', UINT32),
694    )
695
696
697class DWRITE_OVERHANG_METRICS(ctypes.Structure):
698    _fields_ = (
699        ('left', FLOAT),
700        ('top', FLOAT),
701        ('right', FLOAT),
702        ('bottom', FLOAT),
703    )
704
705
706class IDWriteTextLayout(IDWriteTextFormat, com.pIUnknown):
707    _methods_ = [
708        ('SetMaxWidth',
709         com.STDMETHOD()),
710        ('SetMaxHeight',
711         com.STDMETHOD()),
712        ('SetFontCollection',
713         com.STDMETHOD()),
714        ('SetFontFamilyName',
715         com.STDMETHOD()),
716        ('SetFontWeight',  # 30
717         com.STDMETHOD()),
718        ('SetFontStyle',
719         com.STDMETHOD()),
720        ('SetFontStretch',
721         com.STDMETHOD()),
722        ('SetFontSize',
723         com.STDMETHOD()),
724        ('SetUnderline',
725         com.STDMETHOD()),
726        ('SetStrikethrough',
727         com.STDMETHOD()),
728        ('SetDrawingEffect',
729         com.STDMETHOD()),
730        ('SetInlineObject',
731         com.STDMETHOD()),
732        ('SetTypography',
733         com.STDMETHOD(IDWriteTypography, DWRITE_TEXT_RANGE)),
734        ('SetLocaleName',
735         com.STDMETHOD()),
736        ('GetMaxWidth',  # 40
737         com.METHOD(FLOAT)),
738        ('GetMaxHeight',
739         com.METHOD(FLOAT)),
740        ('GetFontCollection2',
741         com.STDMETHOD()),
742        ('GetFontFamilyNameLength2',
743         com.STDMETHOD()),
744        ('GetFontFamilyName2',
745         com.STDMETHOD()),
746        ('GetFontWeight2',
747         com.STDMETHOD(UINT32, POINTER(DWRITE_FONT_WEIGHT), POINTER(DWRITE_TEXT_RANGE))),
748        ('GetFontStyle2',
749         com.STDMETHOD()),
750        ('GetFontStretch2',
751         com.STDMETHOD()),
752        ('GetFontSize2',
753         com.STDMETHOD()),
754        ('GetUnderline',
755         com.STDMETHOD()),
756        ('GetStrikethrough',
757         com.STDMETHOD(UINT32, POINTER(BOOL), POINTER(DWRITE_TEXT_RANGE))),
758        ('GetDrawingEffect',
759         com.STDMETHOD()),
760        ('GetInlineObject',
761         com.STDMETHOD()),
762        ('GetTypography',  # Always returns NULL without SetTypography being called.
763         com.STDMETHOD(UINT32, POINTER(IDWriteTypography), POINTER(DWRITE_TEXT_RANGE))),
764        ('GetLocaleNameLength1',
765         com.STDMETHOD()),
766        ('GetLocaleName1',
767         com.STDMETHOD()),
768        ('Draw',
769         com.STDMETHOD()),
770        ('GetLineMetrics',
771         com.STDMETHOD()),
772        ('GetMetrics',
773         com.STDMETHOD(POINTER(DWRITE_TEXT_METRICS))),
774        ('GetOverhangMetrics',
775         com.STDMETHOD(POINTER(DWRITE_OVERHANG_METRICS))),
776        ('GetClusterMetrics',
777         com.STDMETHOD(POINTER(DWRITE_CLUSTER_METRICS), UINT32, POINTER(UINT32))),
778        ('DetermineMinWidth',
779         com.STDMETHOD(POINTER(FLOAT))),
780        ('HitTestPoint',
781         com.STDMETHOD()),
782        ('HitTestTextPosition',
783         com.STDMETHOD()),
784        ('HitTestTextRange',
785         com.STDMETHOD()),
786    ]
787
788
789class IDWriteTextLayout1(IDWriteTextLayout, IDWriteTextFormat, com.pIUnknown):
790    _methods_ = [
791        ('SetPairKerning',
792         com.STDMETHOD()),
793        ('GetPairKerning',
794         com.STDMETHOD()),
795        ('SetCharacterSpacing',
796         com.STDMETHOD()),
797        ('GetCharacterSpacing',
798         com.STDMETHOD(UINT32, POINTER(FLOAT), POINTER(FLOAT), POINTER(FLOAT), POINTER(DWRITE_TEXT_RANGE))),
799    ]
800
801
802class IDWriteFontFileEnumerator(com.IUnknown):
803    _methods_ = [
804        ('MoveNext',
805         com.STDMETHOD(c_void_p, POINTER(BOOL))),
806        ('GetCurrentFontFile',
807         com.STDMETHOD(c_void_p, c_void_p)),
808    ]
809
810
811class IDWriteFontCollectionLoader(com.IUnknown):
812    _methods_ = [
813        ('CreateEnumeratorFromKey',
814         com.STDMETHOD(c_void_p, c_void_p, c_void_p, UINT32, POINTER(POINTER(IDWriteFontFileEnumerator)))),
815    ]
816
817
818class IDWriteFontFileStream(com.IUnknown):
819    _methods_ = [
820        ('ReadFileFragment',
821         com.STDMETHOD(c_void_p, POINTER(c_void_p), UINT64, UINT64, POINTER(c_void_p))),
822        ('ReleaseFileFragment',
823         com.STDMETHOD(c_void_p, c_void_p)),
824        ('GetFileSize',
825         com.STDMETHOD(c_void_p, POINTER(UINT64))),
826        ('GetLastWriteTime',
827         com.STDMETHOD(c_void_p, POINTER(UINT64))),
828    ]
829
830
831class MyFontFileStream(com.COMObject):
832    _interfaces_ = [IDWriteFontFileStream]
833
834    def __init__(self, data):
835        self._data = data
836        self._size = len(data)
837        self._ptrs = []
838
839    def AddRef(self, this):
840        return 1
841
842    def Release(self, this):
843        return 1
844
845    def QueryInterface(self, this, refiid, tester):
846        return 0
847
848    def ReadFileFragment(self, this, fragmentStart, fileOffset, fragmentSize, fragmentContext):
849        if fileOffset + fragmentSize > self._size:
850            return 0x80004005  # E_FAIL
851
852        fragment = self._data[fileOffset:]
853        buffer = (ctypes.c_ubyte * len(fragment)).from_buffer(bytearray(fragment))
854        ptr = cast(buffer, c_void_p)
855
856        self._ptrs.append(ptr)
857        fragmentStart[0] = ptr
858        fragmentContext[0] = None
859        return 0
860
861    def ReleaseFileFragment(self, this, fragmentContext):
862        return 0
863
864    def GetFileSize(self, this, fileSize):
865        fileSize[0] = self._size
866        return 0
867
868    def GetLastWriteTime(self, this, lastWriteTime):
869        return 0x80004001  # E_NOTIMPL
870
871
872class IDWriteFontFileLoader(com.IUnknown):
873    _methods_ = [
874        ('CreateStreamFromKey',
875         com.STDMETHOD(c_void_p, c_void_p, UINT32, POINTER(POINTER(IDWriteFontFileStream))))
876    ]
877
878
879class LegacyFontFileLoader(com.COMObject):
880    _interfaces_ = [IDWriteFontFileLoader]
881
882    def __init__(self):
883        self._streams = {}
884
885    def QueryInterface(self, this, refiid, tester):
886        return 0
887
888    def AddRef(self, this):
889        return 1
890
891    def Release(self, this):
892        return 1
893
894    def CreateStreamFromKey(self, this, fontfileReferenceKey, fontFileReferenceKeySize, fontFileStream):
895        convert_index = cast(fontfileReferenceKey, POINTER(c_uint32))
896
897        self._ptr = ctypes.cast(self._streams[convert_index.contents.value]._pointers[IDWriteFontFileStream],
898                                POINTER(IDWriteFontFileStream))
899        fontFileStream[0] = self._ptr
900        return 0
901
902    def SetCurrentFont(self, index, data):
903        self._streams[index] = MyFontFileStream(data)
904
905
906class MyEnumerator(com.COMObject):
907    _interfaces_ = [IDWriteFontFileEnumerator]
908
909    def __init__(self, factory, loader):
910        self.factory = cast(factory, IDWriteFactory)
911        self.key = "pyglet_dwrite"
912        self.size = len(self.key)
913        self.current_index = -1
914
915        self._keys = []
916        self._font_data = []
917        self._font_files = []
918        self._current_file = None
919
920        self._font_key_ref = create_unicode_buffer("none")
921        self._font_key_len = len(self._font_key_ref)
922
923        self._file_loader = loader
924
925    def AddFontData(self, fonts):
926        self._font_data = fonts
927
928    def MoveNext(self, this, hasCurrentFile):
929
930        self.current_index += 1
931        if self.current_index != len(self._font_data):
932            font_file = IDWriteFontFile()
933
934            self._file_loader.SetCurrentFont(self.current_index, self._font_data[self.current_index])
935
936            key = self.current_index
937
938            if not self.current_index in self._keys:
939                buffer = pointer(c_uint32(key))
940
941                ptr = cast(buffer, c_void_p)
942
943                self._keys.append(ptr)
944
945            self.factory.CreateCustomFontFileReference(self._keys[self.current_index],
946                                                       sizeof(buffer),
947                                                       self._file_loader,
948                                                       byref(font_file))
949
950            self._font_files.append(font_file)
951
952            hasCurrentFile[0] = 1
953        else:
954            hasCurrentFile[0] = 0
955
956        pass
957
958    def GetCurrentFontFile(self, this, fontFile):
959        fontFile = cast(fontFile, POINTER(IDWriteFontFile))
960        fontFile[0] = self._font_files[self.current_index]
961        return 0
962
963
964class LegacyCollectionLoader(com.COMObject):
965    _interfaces_ = [IDWriteFontCollectionLoader]
966
967    def __init__(self, factory, loader):
968        self._enumerator = MyEnumerator(factory, loader)
969
970    def AddFontData(self, fonts):
971        self._enumerator.AddFontData(fonts)
972
973    def AddRef(self, this):
974        self._i = 1
975        return 1
976
977    def Release(self, this):
978        self._i = 0
979        return 1
980
981    def QueryInterface(self, this, refiid, tester):
982        return 0
983
984    def CreateEnumeratorFromKey(self, this, factory, key, key_size, enumerator):
985        self._ptr = ctypes.cast(self._enumerator._pointers[IDWriteFontFileEnumerator],
986                                POINTER(IDWriteFontFileEnumerator))
987
988        enumerator[0] = self._ptr
989        return 0
990
991
992IID_IDWriteFactory = com.GUID(0xb859ee5a, 0xd838, 0x4b5b, 0xa2, 0xe8, 0x1a, 0xdc, 0x7d, 0x93, 0xdb, 0x48)
993
994
995class IDWriteFactory(com.pIUnknown):
996    _methods_ = [
997        ('GetSystemFontCollection',
998         com.STDMETHOD(POINTER(IDWriteFontCollection), BOOL)),
999        ('CreateCustomFontCollection',
1000         com.STDMETHOD(POINTER(IDWriteFontCollectionLoader), c_void_p, UINT32, POINTER(IDWriteFontCollection))),
1001        ('RegisterFontCollectionLoader',
1002         com.STDMETHOD(POINTER(IDWriteFontCollectionLoader))),
1003        ('UnregisterFontCollectionLoader',
1004         com.STDMETHOD(POINTER(IDWriteFontCollectionLoader))),
1005        ('CreateFontFileReference',
1006         com.STDMETHOD(c_wchar_p, c_void_p, POINTER(IDWriteFontFile))),
1007        ('CreateCustomFontFileReference',
1008         com.STDMETHOD(c_void_p, UINT32, POINTER(IDWriteFontFileLoader), POINTER(IDWriteFontFile))),
1009        ('CreateFontFace',
1010         com.STDMETHOD()),
1011        ('CreateRenderingParams',
1012         com.STDMETHOD()),
1013        ('CreateMonitorRenderingParams',
1014         com.STDMETHOD()),
1015        ('CreateCustomRenderingParams',
1016         com.STDMETHOD()),
1017        ('RegisterFontFileLoader',
1018         com.STDMETHOD(c_void_p)),  # Ambigious as newer is a pIUnknown and legacy is IUnknown.
1019        ('UnregisterFontFileLoader',
1020         com.STDMETHOD(POINTER(IDWriteFontFileLoader))),
1021        ('CreateTextFormat',
1022         com.STDMETHOD(c_wchar_p, IDWriteFontCollection, DWRITE_FONT_WEIGHT, DWRITE_FONT_STYLE, DWRITE_FONT_STRETCH,
1023                       FLOAT, c_wchar_p, POINTER(IDWriteTextFormat))),
1024        ('CreateTypography',
1025         com.STDMETHOD(POINTER(IDWriteTypography))),
1026        ('GetGdiInterop',
1027         com.STDMETHOD()),
1028        ('CreateTextLayout',
1029         com.STDMETHOD(c_wchar_p, UINT32, IDWriteTextFormat, FLOAT, FLOAT, POINTER(IDWriteTextLayout))),
1030        ('CreateGdiCompatibleTextLayout',
1031         com.STDMETHOD()),
1032        ('CreateEllipsisTrimmingSign',
1033         com.STDMETHOD()),
1034        ('CreateTextAnalyzer',
1035         com.STDMETHOD(POINTER(IDWriteTextAnalyzer))),
1036        ('CreateNumberSubstitution',
1037         com.STDMETHOD()),
1038        ('CreateGlyphRunAnalysis',
1039         com.STDMETHOD()),
1040    ]
1041
1042
1043IID_IDWriteFactory1 = com.GUID(0x30572f99, 0xdac6, 0x41db, 0xa1, 0x6e, 0x04, 0x86, 0x30, 0x7e, 0x60, 0x6a)
1044
1045
1046class IDWriteFactory1(IDWriteFactory, com.pIUnknown):
1047    _methods_ = [
1048        ('GetEudcFontCollection',
1049         com.STDMETHOD()),
1050        ('CreateCustomRenderingParams',
1051         com.STDMETHOD()),
1052    ]
1053
1054
1055class IDWriteFontFallback(com.pIUnknown):
1056    _methods_ = [
1057        ('MapCharacters',
1058         com.STDMETHOD(POINTER(IDWriteTextAnalysisSource), UINT32, UINT32, POINTER(IDWriteFontCollection), c_wchar_p,
1059                       DWRITE_FONT_WEIGHT, DWRITE_FONT_STYLE, DWRITE_FONT_STRETCH, POINTER(UINT32),
1060                       POINTER(IDWriteFont),
1061                       POINTER(FLOAT))),
1062    ]
1063
1064
1065class IDWriteFactory2(IDWriteFactory1, com.pIUnknown):
1066    _methods_ = [
1067        ('GetSystemFontFallback',
1068         com.STDMETHOD(POINTER(IDWriteFontFallback))),
1069        ('CreateFontFallbackBuilder',
1070         com.STDMETHOD()),
1071        ('TranslateColorGlyphRun',
1072         com.STDMETHOD()),
1073        ('CreateCustomRenderingParams',
1074         com.STDMETHOD()),
1075        ('CreateGlyphRunAnalysis',
1076         com.STDMETHOD()),
1077    ]
1078
1079
1080class IDWriteFontSet(com.pIUnknown):
1081    _methods_ = [
1082        ('GetFontCount',
1083         com.STDMETHOD()),
1084        ('GetFontFaceReference',
1085         com.STDMETHOD()),
1086        ('FindFontFaceReference',
1087         com.STDMETHOD()),
1088        ('FindFontFace',
1089         com.STDMETHOD()),
1090        ('GetPropertyValues',
1091         com.STDMETHOD()),
1092        ('GetPropertyOccurrenceCount',
1093         com.STDMETHOD()),
1094        ('GetMatchingFonts',
1095         com.STDMETHOD()),
1096        ('GetMatchingFonts',
1097         com.STDMETHOD()),
1098    ]
1099
1100
1101class IDWriteFontSetBuilder(com.pIUnknown):
1102    _methods_ = [
1103        ('AddFontFaceReference',
1104         com.STDMETHOD()),
1105        ('AddFontFaceReference',
1106         com.STDMETHOD()),
1107        ('AddFontSet',
1108         com.STDMETHOD()),
1109        ('CreateFontSet',
1110         com.STDMETHOD(POINTER(IDWriteFontSet))),
1111    ]
1112
1113
1114class IDWriteFontSetBuilder1(IDWriteFontSetBuilder, com.pIUnknown):
1115    _methods_ = [
1116        ('AddFontFile',
1117         com.STDMETHOD(IDWriteFontFile)),
1118    ]
1119
1120
1121class IDWriteFactory3(IDWriteFactory2, com.pIUnknown):
1122    _methods_ = [
1123        ('CreateGlyphRunAnalysis',
1124         com.STDMETHOD()),
1125        ('CreateCustomRenderingParams',
1126         com.STDMETHOD()),
1127        ('CreateFontFaceReference',
1128         com.STDMETHOD()),
1129        ('CreateFontFaceReference',
1130         com.STDMETHOD()),
1131        ('GetSystemFontSet',
1132         com.STDMETHOD()),
1133        ('CreateFontSetBuilder',
1134         com.STDMETHOD(POINTER(IDWriteFontSetBuilder))),
1135        ('CreateFontCollectionFromFontSet',
1136         com.STDMETHOD(IDWriteFontSet, POINTER(IDWriteFontCollection1))),
1137        ('GetSystemFontCollection3',
1138         com.STDMETHOD()),
1139        ('GetFontDownloadQueue',
1140         com.STDMETHOD()),
1141        ('GetSystemFontSet',
1142         com.STDMETHOD()),
1143    ]
1144
1145
1146class IDWriteFactory4(IDWriteFactory3, com.pIUnknown):
1147    _methods_ = [
1148        ('TranslateColorGlyphRun',
1149         com.STDMETHOD()),
1150        ('ComputeGlyphOrigins',
1151         com.STDMETHOD()),
1152    ]
1153
1154
1155class IDWriteInMemoryFontFileLoader(com.pIUnknown):
1156    _methods_ = [
1157        ('CreateStreamFromKey',
1158         com.STDMETHOD()),
1159        ('CreateInMemoryFontFileReference',
1160         com.STDMETHOD(IDWriteFactory, c_void_p, UINT, c_void_p, POINTER(IDWriteFontFile))),
1161        ('GetFileCount',
1162         com.STDMETHOD()),
1163    ]
1164
1165
1166IID_IDWriteFactory5 = com.GUID(0x958DB99A, 0xBE2A, 0x4F09, 0xAF, 0x7D, 0x65, 0x18, 0x98, 0x03, 0xD1, 0xD3)
1167
1168
1169class IDWriteFactory5(IDWriteFactory4, IDWriteFactory3, IDWriteFactory2, IDWriteFactory1, IDWriteFactory,
1170                      com.pIUnknown):
1171    _methods_ = [
1172        ('CreateFontSetBuilder1',
1173         com.STDMETHOD(POINTER(IDWriteFontSetBuilder1))),
1174        ('CreateInMemoryFontFileLoader',
1175         com.STDMETHOD(POINTER(IDWriteInMemoryFontFileLoader))),
1176        ('CreateHttpFontFileLoader',
1177         com.STDMETHOD()),
1178        ('AnalyzeContainerType',
1179         com.STDMETHOD())
1180    ]
1181
1182
1183class DWRITE_GLYPH_RUN(ctypes.Structure):
1184    _fields_ = (
1185        ('fontFace', IDWriteFontFace),
1186        ('fontEmSize', FLOAT),
1187        ('glyphCount', UINT32),
1188        ('glyphIndices', POINTER(UINT16)),
1189        ('glyphAdvances', POINTER(FLOAT)),
1190        ('glyphOffsets', POINTER(DWRITE_GLYPH_OFFSET)),
1191        ('isSideways', BOOL),
1192        ('bidiLevel', UINT32),
1193    )
1194
1195
1196DWriteCreateFactory = dwrite_lib.DWriteCreateFactory
1197DWriteCreateFactory.restype = HRESULT
1198DWriteCreateFactory.argtypes = [DWRITE_FACTORY_TYPE, com.REFIID, POINTER(com.pIUnknown)]
1199
1200
1201class D2D_POINT_2F(Structure):
1202    _fields_ = (
1203        ('x', FLOAT),
1204        ('y', FLOAT),
1205    )
1206
1207
1208class D2D1_RECT_F(Structure):
1209    _fields_ = (
1210        ('left', FLOAT),
1211        ('top', FLOAT),
1212        ('right', FLOAT),
1213        ('bottom', FLOAT),
1214    )
1215
1216
1217class D2D1_COLOR_F(Structure):
1218    _fields_ = (
1219        ('r', FLOAT),
1220        ('g', FLOAT),
1221        ('b', FLOAT),
1222        ('a', FLOAT),
1223    )
1224
1225
1226class ID2D1Resource(com.pIUnknown):
1227    _methods_ = [
1228        ('GetFactory',
1229         com.STDMETHOD()),
1230    ]
1231
1232
1233class ID2D1Brush(ID2D1Resource, com.pIUnknown):
1234    _methods_ = [
1235        ('SetOpacity',
1236         com.STDMETHOD()),
1237        ('SetTransform',
1238         com.STDMETHOD()),
1239        ('GetOpacity',
1240         com.STDMETHOD()),
1241        ('GetTransform',
1242         com.STDMETHOD()),
1243    ]
1244
1245
1246class ID2D1SolidColorBrush(ID2D1Brush, ID2D1Resource, com.pIUnknown):
1247    _methods_ = [
1248        ('SetColor',
1249         com.STDMETHOD()),
1250        ('GetColor',
1251         com.STDMETHOD()),
1252    ]
1253
1254
1255D2D1_TEXT_ANTIALIAS_MODE = UINT
1256D2D1_TEXT_ANTIALIAS_MODE_DEFAULT = 0
1257D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE = 1
1258D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE = 2
1259D2D1_TEXT_ANTIALIAS_MODE_ALIASED = 3
1260
1261D2D1_RENDER_TARGET_TYPE = UINT
1262D2D1_RENDER_TARGET_TYPE_DEFAULT = 0
1263D2D1_RENDER_TARGET_TYPE_SOFTWARE = 1
1264D2D1_RENDER_TARGET_TYPE_HARDWARE = 2
1265
1266D2D1_FEATURE_LEVEL = UINT
1267D2D1_FEATURE_LEVEL_DEFAULT = 0
1268
1269D2D1_RENDER_TARGET_USAGE = UINT
1270D2D1_RENDER_TARGET_USAGE_NONE = 0
1271D2D1_RENDER_TARGET_USAGE_FORCE_BITMAP_REMOTING = 1
1272D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE = 2
1273
1274DXGI_FORMAT = UINT
1275DXGI_FORMAT_UNKNOWN = 0
1276
1277D2D1_ALPHA_MODE = UINT
1278D2D1_ALPHA_MODE_UNKNOWN = 0
1279D2D1_ALPHA_MODE_PREMULTIPLIED = 1
1280D2D1_ALPHA_MODE_STRAIGHT = 2
1281D2D1_ALPHA_MODE_IGNORE = 3
1282
1283D2D1_DRAW_TEXT_OPTIONS = UINT
1284D2D1_DRAW_TEXT_OPTIONS_NO_SNAP = 0x00000001
1285D2D1_DRAW_TEXT_OPTIONS_CLIP = 0x00000002
1286D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT = 0x00000004
1287D2D1_DRAW_TEXT_OPTIONS_DISABLE_COLOR_BITMAP_SNAPPING = 0x00000008
1288D2D1_DRAW_TEXT_OPTIONS_NONE = 0x00000000
1289D2D1_DRAW_TEXT_OPTIONS_FORCE_DWORD = 0xffffffff
1290
1291DWRITE_MEASURING_MODE = UINT
1292DWRITE_MEASURING_MODE_NATURAL = 0
1293DWRITE_MEASURING_MODE_GDI_CLASSIC = 1
1294DWRITE_MEASURING_MODE_GDI_NATURAL = 2
1295
1296
1297class D2D1_PIXEL_FORMAT(Structure):
1298    _fields_ = (
1299        ('format', DXGI_FORMAT),
1300        ('alphaMode', D2D1_ALPHA_MODE),
1301    )
1302
1303
1304class D2D1_RENDER_TARGET_PROPERTIES(Structure):
1305    _fields_ = (
1306        ('type', D2D1_RENDER_TARGET_TYPE),
1307        ('pixelFormat', D2D1_PIXEL_FORMAT),
1308        ('dpiX', FLOAT),
1309        ('dpiY', FLOAT),
1310        ('usage', D2D1_RENDER_TARGET_USAGE),
1311        ('minLevel', D2D1_FEATURE_LEVEL),
1312    )
1313
1314
1315DXGI_FORMAT_B8G8R8A8_UNORM = 87
1316
1317pixel_format = D2D1_PIXEL_FORMAT()
1318pixel_format.format = DXGI_FORMAT_UNKNOWN
1319pixel_format.alphaMode = D2D1_ALPHA_MODE_UNKNOWN
1320
1321default_target_properties = D2D1_RENDER_TARGET_PROPERTIES()
1322default_target_properties.type = D2D1_RENDER_TARGET_TYPE_DEFAULT
1323default_target_properties.pixelFormat = pixel_format
1324default_target_properties.dpiX = 0.0
1325default_target_properties.dpiY = 0.0
1326default_target_properties.usage = D2D1_RENDER_TARGET_USAGE_NONE
1327default_target_properties.minLevel = D2D1_FEATURE_LEVEL_DEFAULT
1328
1329
1330class ID2D1RenderTarget(ID2D1Resource, com.pIUnknown):
1331    _methods_ = [
1332        ('CreateBitmap',
1333         com.STDMETHOD()),
1334        ('CreateBitmapFromWicBitmap',
1335         com.STDMETHOD()),
1336        ('CreateSharedBitmap',
1337         com.STDMETHOD()),
1338        ('CreateBitmapBrush',
1339         com.STDMETHOD()),
1340        ('CreateSolidColorBrush',
1341         com.STDMETHOD(POINTER(D2D1_COLOR_F), c_void_p, POINTER(ID2D1SolidColorBrush))),
1342        ('CreateGradientStopCollection',
1343         com.STDMETHOD()),
1344        ('CreateLinearGradientBrush',
1345         com.STDMETHOD()),
1346        ('CreateRadialGradientBrush',
1347         com.STDMETHOD()),
1348        ('CreateCompatibleRenderTarget',
1349         com.STDMETHOD()),
1350        ('CreateLayer',
1351         com.STDMETHOD()),
1352        ('CreateMesh',
1353         com.STDMETHOD()),
1354        ('DrawLine',
1355         com.STDMETHOD()),
1356        ('DrawRectangle',
1357         com.STDMETHOD()),
1358        ('FillRectangle',
1359         com.STDMETHOD()),
1360        ('DrawRoundedRectangle',
1361         com.STDMETHOD()),
1362        ('FillRoundedRectangle',
1363         com.STDMETHOD()),
1364        ('DrawEllipse',
1365         com.STDMETHOD()),
1366        ('FillEllipse',
1367         com.STDMETHOD()),
1368        ('DrawGeometry',
1369         com.STDMETHOD()),
1370        ('FillGeometry',
1371         com.STDMETHOD()),
1372        ('FillMesh',
1373         com.STDMETHOD()),
1374        ('FillOpacityMask',
1375         com.STDMETHOD()),
1376        ('DrawBitmap',
1377         com.STDMETHOD()),
1378        ('DrawText',
1379         com.STDMETHOD(c_wchar_p, UINT, IDWriteTextFormat, POINTER(D2D1_RECT_F), ID2D1Brush, D2D1_DRAW_TEXT_OPTIONS,
1380                       DWRITE_MEASURING_MODE)),
1381        ('DrawTextLayout',
1382         com.METHOD(c_void, D2D_POINT_2F, IDWriteTextLayout, ID2D1Brush, UINT32)),
1383        ('DrawGlyphRun',
1384         com.METHOD(c_void, D2D_POINT_2F, POINTER(DWRITE_GLYPH_RUN), ID2D1Brush, UINT32)),
1385        ('SetTransform',
1386         com.METHOD(c_void)),
1387        ('GetTransform',
1388         com.STDMETHOD()),
1389        ('SetAntialiasMode',
1390         com.STDMETHOD()),
1391        ('GetAntialiasMode',
1392         com.STDMETHOD()),
1393        ('SetTextAntialiasMode',
1394         com.METHOD(c_void, D2D1_TEXT_ANTIALIAS_MODE)),
1395        ('GetTextAntialiasMode',
1396         com.STDMETHOD()),
1397        ('SetTextRenderingParams',
1398         com.STDMETHOD()),
1399        ('GetTextRenderingParams',
1400         com.STDMETHOD()),
1401        ('SetTags',
1402         com.STDMETHOD()),
1403        ('GetTags',
1404         com.STDMETHOD()),
1405        ('PushLayer',
1406         com.STDMETHOD()),
1407        ('PopLayer',
1408         com.STDMETHOD()),
1409        ('Flush',
1410         com.STDMETHOD()),
1411        ('SaveDrawingState',
1412         com.STDMETHOD()),
1413        ('RestoreDrawingState',
1414         com.STDMETHOD()),
1415        ('PushAxisAlignedClip',
1416         com.STDMETHOD()),
1417        ('PopAxisAlignedClip',
1418         com.STDMETHOD()),
1419        ('Clear',
1420         com.METHOD(c_void, POINTER(D2D1_COLOR_F))),
1421        ('BeginDraw',
1422         com.METHOD(c_void)),
1423        ('EndDraw',
1424         com.STDMETHOD(c_void_p, c_void_p)),
1425        ('GetPixelFormat',
1426         com.STDMETHOD()),
1427        ('SetDpi',
1428         com.STDMETHOD()),
1429        ('GetDpi',
1430         com.STDMETHOD()),
1431        ('GetSize',
1432         com.STDMETHOD()),
1433        ('GetPixelSize',
1434         com.STDMETHOD()),
1435        ('GetMaximumBitmapSize',
1436         com.STDMETHOD()),
1437        ('IsSupported',
1438         com.STDMETHOD()),
1439    ]
1440
1441
1442IID_ID2D1Factory = com.GUID(0x06152247, 0x6f50, 0x465a, 0x92, 0x45, 0x11, 0x8b, 0xfd, 0x3b, 0x60, 0x07)
1443
1444
1445class ID2D1Factory(com.pIUnknown):
1446    _methods_ = [
1447        ('ReloadSystemMetrics',
1448         com.STDMETHOD()),
1449        ('GetDesktopDpi',
1450         com.STDMETHOD()),
1451        ('CreateRectangleGeometry',
1452         com.STDMETHOD()),
1453        ('CreateRoundedRectangleGeometry',
1454         com.STDMETHOD()),
1455        ('CreateEllipseGeometry',
1456         com.STDMETHOD()),
1457        ('CreateGeometryGroup',
1458         com.STDMETHOD()),
1459        ('CreateTransformedGeometry',
1460         com.STDMETHOD()),
1461        ('CreatePathGeometry',
1462         com.STDMETHOD()),
1463        ('CreateStrokeStyle',
1464         com.STDMETHOD()),
1465        ('CreateDrawingStateBlock',
1466         com.STDMETHOD()),
1467        ('CreateWicBitmapRenderTarget',
1468         com.STDMETHOD(IWICBitmap, POINTER(D2D1_RENDER_TARGET_PROPERTIES), POINTER(ID2D1RenderTarget))),
1469        ('CreateHwndRenderTarget',
1470         com.STDMETHOD()),
1471        ('CreateDxgiSurfaceRenderTarget',
1472         com.STDMETHOD()),
1473        ('CreateDCRenderTarget',
1474         com.STDMETHOD()),
1475    ]
1476
1477
1478d2d_lib = ctypes.windll.d2d1
1479
1480D2D1_FACTORY_TYPE = UINT
1481D2D1_FACTORY_TYPE_SINGLE_THREADED = 0
1482D2D1_FACTORY_TYPE_MULTI_THREADED = 1
1483
1484D2D1CreateFactory = d2d_lib.D2D1CreateFactory
1485D2D1CreateFactory.restype = HRESULT
1486D2D1CreateFactory.argtypes = [D2D1_FACTORY_TYPE, com.REFIID, c_void_p, c_void_p]
1487
1488# We need a WIC factory to make this work. Make sure one is in the initialized decoders.
1489wic_decoder = None
1490for decoder in pyglet.image.codecs.get_decoders():
1491    if isinstance(decoder, WICDecoder):
1492        wic_decoder = decoder
1493
1494if not wic_decoder:
1495    raise Exception("Cannot use DirectWrite without a WIC Decoder")
1496
1497
1498class DirectWriteGlyphRenderer(base.GlyphRenderer):
1499    antialias_mode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
1500    draw_options = D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT
1501
1502    def __init__(self, font):
1503        self._render_target = None
1504        self._bitmap = None
1505        self._brush = None
1506        self._bitmap_dimensions = (0, 0)
1507        super(DirectWriteGlyphRenderer, self).__init__(font)
1508        self.font = font
1509
1510        self._analyzer = IDWriteTextAnalyzer()
1511        self.font._write_factory.CreateTextAnalyzer(byref(self._analyzer))
1512
1513        self._text_analysis = TextAnalysis()
1514
1515    def render_to_image(self, text, width, height):
1516        """This process takes Pyglet out of the equation and uses only DirectWrite to shape and render text.
1517        This may allows more accurate fonts (bidi, rtl, etc) in very special circumstances."""
1518        text_buffer = create_unicode_buffer(text)
1519
1520        text_layout = IDWriteTextLayout()
1521        self.font._write_factory.CreateTextLayout(
1522            text_buffer,
1523            len(text_buffer),
1524            self.font._text_format,
1525            width,  # Doesn't affect bitmap size.
1526            height,
1527            byref(text_layout)
1528        )
1529
1530        layout_metrics = DWRITE_TEXT_METRICS()
1531        text_layout.GetMetrics(byref(layout_metrics))
1532
1533        width, height = int(math.ceil(layout_metrics.width)), int(math.ceil(layout_metrics.height))
1534
1535        bitmap = IWICBitmap()
1536        wic_decoder._factory.CreateBitmap(width, height,
1537                                          GUID_WICPixelFormat32bppPBGRA,
1538                                          WICBitmapCacheOnDemand,
1539                                          byref(bitmap))
1540
1541        rt = ID2D1RenderTarget()
1542        d2d_factory.CreateWicBitmapRenderTarget(bitmap, default_target_properties, byref(rt))
1543
1544        # Font aliasing rendering quality.
1545        rt.SetTextAntialiasMode(self.antialias_mode)
1546
1547        if not self._brush:
1548            self._brush = ID2D1SolidColorBrush()
1549
1550        rt.CreateSolidColorBrush(white, None, byref(self._brush))
1551
1552        # This offsets the characters if needed.
1553        point = D2D_POINT_2F(0, 0)
1554
1555        rt.BeginDraw()
1556
1557        rt.Clear(transparent)
1558
1559        rt.DrawTextLayout(point,
1560                          text_layout,
1561                          self._brush,
1562                          self.draw_options)
1563
1564        rt.EndDraw(None, None)
1565
1566        rt.Release()
1567
1568        image_data = wic_decoder.get_image(bitmap)
1569
1570        return image_data
1571
1572    def get_string_info(self, text):
1573
1574        """Converts a string of text into a list of indices and advances used for shaping."""
1575        text_length = len(text.encode('utf-16-le')) // 2
1576
1577        # Unicode buffer splits each two byte chars into separate indices.
1578        text_buffer = create_unicode_buffer(text, text_length)
1579
1580        # Analyze the text.
1581        # noinspection PyTypeChecker
1582        self._text_analysis.GenerateResults(self._analyzer, text_buffer, len(text_buffer))
1583
1584        # Formula for text buffer size from Microsoft.
1585        max_glyph_size = int(3 * text_length / 2 + 16)
1586
1587        length = text_length
1588        clusters = (UINT16 * length)()
1589        text_props = (DWRITE_SHAPING_TEXT_PROPERTIES * length)()
1590        indices = (UINT16 * max_glyph_size)()
1591        glyph_props = (DWRITE_SHAPING_GLYPH_PROPERTIES * max_glyph_size)()
1592        actual_count = UINT32()
1593
1594        self._analyzer.GetGlyphs(text_buffer,
1595                                 length,
1596                                 self.font.font_face,
1597                                 False,  # sideways
1598                                 False,  # righttoleft
1599                                 self._text_analysis._script,  # scriptAnalysis
1600                                 None,  # localName
1601                                 None,  # numberSub
1602                                 None,  # typo features
1603                                 None,  # feature range length
1604                                 0,  # feature range
1605                                 max_glyph_size,  # max glyph size
1606                                 clusters,  # cluster map
1607                                 text_props,  # text props
1608                                 indices,  # glyph indices
1609                                 glyph_props,  # glyph pops
1610                                 byref(actual_count)  # glyph count
1611                                 )
1612
1613        advances = (FLOAT * length)()
1614        offsets = (DWRITE_GLYPH_OFFSET * length)()
1615        self._analyzer.GetGlyphPlacements(text_buffer,
1616                                          clusters,
1617                                          text_props,
1618                                          text_length,
1619                                          indices,
1620                                          glyph_props,
1621                                          actual_count,
1622                                          self.font.font_face,
1623                                          self.font._font_metrics.designUnitsPerEm,
1624                                          False, False,
1625                                          self._text_analysis._script,
1626                                          self.font.locale,
1627                                          None,
1628                                          None,
1629                                          0,
1630                                          advances,
1631                                          offsets)
1632
1633        return text_buffer, actual_count.value, indices, advances, offsets, clusters
1634
1635    def get_glyph_metrics(self, font_face, indices, count):
1636        """Returns a list of tuples with the following metrics per indice:
1637            (glyph width, glyph height, lsb, advanceWidth)
1638        """
1639        glyph_metrics = (DWRITE_GLYPH_METRICS * count)()
1640        font_face.GetDesignGlyphMetrics(indices, count, glyph_metrics, False)
1641
1642        metrics_out = []
1643        i = 0
1644        for metric in glyph_metrics:
1645            glyph_width = (metric.advanceWidth - metric.leftSideBearing - metric.rightSideBearing)
1646
1647            # width must have a minimum of 1. For example, spaces are actually 0 width, still need glyph bitmap size.
1648            if glyph_width == 0:
1649                glyph_width = 1
1650
1651            glyph_height = (metric.advanceHeight - metric.topSideBearing - metric.bottomSideBearing)
1652
1653            lsb = metric.leftSideBearing
1654
1655            bsb = metric.bottomSideBearing
1656
1657            advance_width = metric.advanceWidth
1658
1659            metrics_out.append((glyph_width, glyph_height, lsb, advance_width, bsb))
1660            i += 1
1661
1662        return metrics_out
1663
1664    def _get_single_glyph_run(self, font_face, size, indices, advances, offsets, sideways, bidi):
1665        run = DWRITE_GLYPH_RUN(
1666            font_face,
1667            size,
1668            1,
1669            indices,
1670            advances,
1671            offsets,
1672            sideways,
1673            bidi
1674        )
1675        return run
1676
1677    def render_single_glyph(self, font_face, indice, advance, offset, metrics):
1678        """Renders a single glyph using D2D DrawGlyphRun"""
1679        glyph_width, glyph_height, lsb, font_advance, bsb = metrics  # We use a shaped advance instead of the fonts.
1680
1681        # Slicing an array turns it into a python object. Maybe a better way to keep it a ctypes value?
1682        new_indice = (UINT16 * 1)(indice)
1683        new_advance = (FLOAT * 1)(advance)
1684
1685        run = self._get_single_glyph_run(font_face,
1686                                         self.font._real_size,
1687                                         new_indice,  # indice,
1688                                         new_advance,  # advance,
1689                                         pointer(offset),  # offset,
1690                                         False,
1691                                         False)
1692
1693        render_width = int(math.ceil((glyph_width) * self.font.font_scale_ratio))
1694        render_offset_x = int(math.floor(abs(lsb * self.font.font_scale_ratio)))
1695        if lsb < 0:
1696            # Negative LSB: we shift the layout rect to the right
1697            # Otherwise we will cut the left part of the glyph
1698            render_offset_x = -(render_offset_x)
1699
1700        # Create new bitmap.
1701        # TODO: We can probably adjust bitmap/baseline to reduce the whitespace and save a lot of texture space.
1702        # Note: Floating point precision makes this a giant headache, will need to be solved for this approach.
1703        self._create_bitmap(render_width + 1,  # Add 1, sometimes AA can add an extra pixel or so.
1704                            int(math.ceil(self.font.max_glyph_height)))
1705
1706        # Glyphs are drawn at the baseline, and with LSB, so we need to offset it based on top left position.
1707        # Offsets are actually based on pixels somehow???
1708        baseline_offset = D2D_POINT_2F(-render_offset_x - offset.advanceOffset,
1709                                       self.font.ascent + offset.ascenderOffset)
1710
1711        self._render_target.BeginDraw()
1712
1713        self._render_target.Clear(transparent)
1714
1715        self._render_target.DrawGlyphRun(baseline_offset,
1716                                         run,
1717                                         self._brush,
1718                                         DWRITE_MEASURING_MODE_NATURAL)
1719
1720        self._render_target.EndDraw(None, None)
1721        image = wic_decoder.get_image(self._bitmap)
1722
1723        glyph = self.font.create_glyph(image)
1724
1725        glyph.set_bearings(self.font.descent, render_offset_x,
1726                           advance * self.font.font_scale_ratio,
1727                           offset.advanceOffset * self.font.font_scale_ratio,
1728                           offset.ascenderOffset * self.font.font_scale_ratio)
1729
1730        return glyph
1731
1732    def render_using_layout(self, text):
1733        """This will render text given the built in DirectWrite layout. This process allows us to take
1734        advantage of color glyphs and fallback handling that is built into DirectWrite.
1735        This can also handle shaping and many other features if you want to render directly to a texture."""
1736        text_layout = self.font.create_text_layout(text)
1737
1738        layout_metrics = DWRITE_TEXT_METRICS()
1739        text_layout.GetMetrics(byref(layout_metrics))
1740
1741        self._create_bitmap(int(math.ceil(layout_metrics.width)),
1742                            int(math.ceil(layout_metrics.height)))
1743
1744        # This offsets the characters if needed.
1745        point = D2D_POINT_2F(0, 0)
1746
1747        self._render_target.BeginDraw()
1748
1749        self._render_target.Clear(transparent)
1750
1751        self._render_target.DrawTextLayout(point,
1752                                           text_layout,
1753                                           self._brush,
1754                                           self.draw_options)
1755
1756        self._render_target.EndDraw(None, None)
1757
1758        image = wic_decoder.get_image(self._bitmap)
1759
1760        glyph = self.font.create_glyph(image)
1761        glyph.set_bearings(self.font.descent, 0, int(math.ceil(layout_metrics.width)))
1762        return glyph
1763
1764    def _create_bitmap(self, width, height):
1765        """Creates a bitmap using Direct2D and WIC."""
1766        # Create a new bitmap, try to re-use the bitmap as much as we can to minimize creations.
1767        if self._bitmap_dimensions[0] != width or self._bitmap_dimensions[1] != height:
1768            # If dimensions aren't the same, release bitmap to create new ones.
1769            if self._bitmap:
1770                self._bitmap.Release()
1771
1772            self._bitmap = IWICBitmap()
1773            wic_decoder._factory.CreateBitmap(width, height,
1774                                              GUID_WICPixelFormat32bppPBGRA,
1775                                              WICBitmapCacheOnDemand,
1776                                              byref(self._bitmap))
1777
1778            self._render_target = ID2D1RenderTarget()
1779            d2d_factory.CreateWicBitmapRenderTarget(self._bitmap, default_target_properties, byref(self._render_target))
1780
1781            # Font aliasing rendering quality.
1782            self._render_target.SetTextAntialiasMode(self.antialias_mode)
1783
1784            if not self._brush:
1785                self._brush = ID2D1SolidColorBrush()
1786                self._render_target.CreateSolidColorBrush(white, None, byref(self._brush))
1787
1788
1789class Win32DirectWriteFont(base.Font):
1790    # To load fonts from files, we need to produce a custom collection.
1791    _custom_collection = None
1792
1793    # Shared loader values
1794    _write_factory = None  # Factory required to run any DirectWrite interfaces.
1795    _font_loader = None
1796
1797    # Windows 10 loader values.
1798    _font_builder = None
1799    _font_set = None
1800
1801    # Legacy loader values
1802    _font_collection_loader = None
1803    _font_cache = []
1804    _font_loader_key = None
1805
1806    _default_name = 'Segoe UI'  # Default font for Win7/10.
1807
1808    _glyph_renderer = None
1809
1810    glyph_renderer_class = DirectWriteGlyphRenderer
1811    texture_internalformat = pyglet.gl.GL_RGBA
1812
1813    def __init__(self, name, size, bold=False, italic=False, stretch=False, dpi=None, locale=None):
1814        self._advance_cache = {}  # Stores glyph's by the indice and advance.
1815
1816        super(Win32DirectWriteFont, self).__init__()
1817
1818        if not name:
1819            name = self._default_name
1820
1821        self._font_index, self._collection = self.get_collection(name)
1822        assert self._collection is not None, "Font: {} not found in loaded or system font collection.".format(name)
1823
1824        self._name = name
1825        self.bold = bold
1826        self.size = size
1827        self.italic = italic
1828        self.stretch = stretch
1829        self.dpi = dpi
1830        self.locale = locale
1831
1832        if self.locale is None:
1833            self.locale = ""
1834            self.rtl = False  # Right to left should be handled by pyglet?
1835            # TODO: Use system locale string?
1836
1837        if self.dpi is None:
1838            self.dpi = 96
1839
1840        # From DPI to DIP (Device Independent Pixels) which is what the fonts rely on.
1841        self._real_size = (self.size * self.dpi) // 72
1842
1843        if self.bold:
1844            if type(self.bold) is str:
1845                self._weight = name_to_weight[self.bold]
1846            else:
1847                self._weight = DWRITE_FONT_WEIGHT_BOLD
1848        else:
1849            self._weight = DWRITE_FONT_WEIGHT_NORMAL
1850
1851        if self.italic:
1852            if type(self.italic) is str:
1853                self._style = name_to_style[self.italic]
1854            else:
1855                self._style = DWRITE_FONT_STYLE_ITALIC
1856        else:
1857            self._style = DWRITE_FONT_STYLE_NORMAL
1858
1859        if self.stretch:
1860            if type(self.stretch) is str:
1861                self._stretch = name_to_stretch[self.stretch]
1862            else:
1863                self._stretch = DWRITE_FONT_STRETCH_EXPANDED
1864        else:
1865            self._stretch = DWRITE_FONT_STRETCH_NORMAL
1866
1867        # Create the text format this font will use permanently.
1868        # Could technically be recreated, but will keep to be inline with other font objects.
1869        self._text_format = IDWriteTextFormat()
1870        self._write_factory.CreateTextFormat(self._name,
1871                                             self._collection,
1872                                             self._weight,
1873                                             self._style,
1874                                             self._stretch,
1875                                             self._real_size,
1876                                             create_unicode_buffer(self.locale),
1877                                             byref(self._text_format))
1878
1879        # All this work just to get a font face and it's metrics!
1880        font_family = IDWriteFontFamily1()
1881        self._collection.GetFontFamily(self._font_index, byref(font_family))
1882
1883        write_font = IDWriteFont()
1884        font_family.GetFirstMatchingFont(self._weight,
1885                                         self._stretch,
1886                                         self._style,
1887                                         byref(write_font))
1888
1889        font_face = IDWriteFontFace()
1890        write_font.CreateFontFace(byref(font_face))
1891
1892        self.font_face = IDWriteFontFace1()
1893        font_face.QueryInterface(IID_IDWriteFontFace1, byref(self.font_face))
1894
1895        self._font_metrics = DWRITE_FONT_METRICS()
1896        self.font_face.GetMetrics(byref(self._font_metrics))
1897
1898        self.font_scale_ratio = (self._real_size / self._font_metrics.designUnitsPerEm)
1899
1900        self.ascent = self._font_metrics.ascent * self.font_scale_ratio
1901        self.descent = self._font_metrics.descent * self.font_scale_ratio
1902
1903        self.max_glyph_height = (self._font_metrics.ascent + self._font_metrics.descent) * self.font_scale_ratio
1904        self.line_gap = self._font_metrics.lineGap * self.font_scale_ratio
1905
1906    @property
1907    def name(self):
1908        return self._name
1909
1910    def render_to_image(self, text, width=10000, height=80):
1911        """This process takes Pyglet out of the equation and uses only DirectWrite to shape and render text.
1912        This may allow more accurate fonts (bidi, rtl, etc) in very special circumstances at the cost of
1913        additional texture space.
1914
1915        :Parameters:
1916            `text` : str
1917                String of text to render.
1918
1919        :rtype: `ImageData`
1920        :return: An image of the text.
1921        """
1922        if not self._glyph_renderer:
1923            self._glyph_renderer = self.glyph_renderer_class(self)
1924
1925        return self._glyph_renderer.render_to_image(text, width, height)
1926
1927    def copy_glyph(self, glyph, advance, offset):
1928        """This takes the existing glyph texture and puts it into a new Glyph with a new advance.
1929        Texture memory is shared between both glyphs."""
1930        new_glyph = base.Glyph(glyph.x, glyph.y, glyph.z, glyph.width, glyph.height, glyph.owner)
1931        new_glyph.set_bearings(glyph.baseline,
1932                               glyph.lsb,
1933                               advance * self.font_scale_ratio,
1934                               offset.advanceOffset * self.font_scale_ratio,
1935                               offset.ascenderOffset * self.font_scale_ratio)
1936        return new_glyph
1937
1938    def get_glyphs(self, text):
1939        if not self._glyph_renderer:
1940            self._glyph_renderer = self.glyph_renderer_class(self)
1941
1942        text_buffer, actual_count, indices, advances, offsets, clusters = self._glyph_renderer.get_string_info(text)
1943
1944        metrics = self._glyph_renderer.get_glyph_metrics(self.font_face, indices, actual_count)
1945
1946        glyphs = []
1947        for i in range(actual_count):
1948            indice = indices[i]
1949            if indice == 0:
1950                # If an indice is 0, it will return no glyph. In this case we attempt to render leveraging
1951                # the built in text layout from MS. Which depending on version can use fallback fonts and other tricks
1952                # to possibly get something of use.
1953                formatted_clusters = clusters[:]
1954
1955                # Some glyphs can be more than 1 char. We use the clusters to determine how many of an index exist.
1956                text_length = formatted_clusters.count(i)
1957
1958                # Amount of glyphs don't always match 1:1 with text as some can be substituted or omitted. Get
1959                # actual text buffer index.
1960                text_index = formatted_clusters.index(i)
1961
1962                # Get actual text based on the index and length.
1963                actual_text = text_buffer[text_index:text_index + text_length]
1964
1965                # Since we can't store as indice 0 without overriding, we have to store as text
1966                if actual_text not in self.glyphs:
1967                    glyph = self._glyph_renderer.render_using_layout(text_buffer[text_index:text_index + text_length])
1968                    self.glyphs[actual_text] = glyph
1969
1970                glyphs.append(self.glyphs[actual_text])
1971            else:
1972                # Glyphs can vary depending on shaping. We will cache it by indice, advance, and offset.
1973                # Possible to just cache without offset and set them each time. This may be faster?
1974                if indice in self.glyphs:
1975                    advance_key = (indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)
1976                    if advance_key in self._advance_cache:
1977                        glyph = self._advance_cache[advance_key]
1978                    else:
1979                        glyph = self.copy_glyph(self.glyphs[indice], advances[i], offsets[i])
1980                        self._advance_cache[advance_key] = glyph
1981                else:
1982                    glyph = self._glyph_renderer.render_single_glyph(self.font_face, indice, advances[i], offsets[i],
1983                                                                     metrics[i])
1984                    self.glyphs[indice] = glyph
1985                    self._advance_cache[(indice, advances[i], offsets[i].advanceOffset, offsets[i].ascenderOffset)] = glyph
1986
1987                glyphs.append(glyph)
1988
1989        return glyphs
1990
1991    def create_text_layout(self, text):
1992        text_buffer = create_unicode_buffer(text)
1993
1994        text_layout = IDWriteTextLayout()
1995        hr = self._write_factory.CreateTextLayout(text_buffer,
1996                                                  len(text_buffer),
1997                                                  self._text_format,
1998                                                  10000,  # Doesn't affect bitmap size.
1999                                                  80,
2000                                                  byref(text_layout)
2001                                                  )
2002
2003        return text_layout
2004
2005    @classmethod
2006    def _initialize_direct_write(cls):
2007        """ All direct write fonts needs factory access as well as the loaders."""
2008        if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
2009            cls._write_factory = IDWriteFactory5()
2010            DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, IID_IDWriteFactory5, byref(cls._write_factory))
2011        else:
2012            # Windows 7 and 8 we need to create our own font loader, collection, enumerator, file streamer... Sigh.
2013            cls._write_factory = IDWriteFactory()
2014            DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, IID_IDWriteFactory, byref(cls._write_factory))
2015
2016    @classmethod
2017    def _initialize_custom_loaders(cls):
2018        """Initialize the loaders needed to load custom fonts."""
2019        if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
2020            # Windows 10 finally has a built in loader that can take data and make a font out of it w/ COMs.
2021            cls._font_loader = IDWriteInMemoryFontFileLoader()
2022            cls._write_factory.CreateInMemoryFontFileLoader(byref(cls._font_loader))
2023            cls._write_factory.RegisterFontFileLoader(cls._font_loader)
2024
2025            # Used for grouping fonts together.
2026            cls._font_builder = IDWriteFontSetBuilder1()
2027            cls._write_factory.CreateFontSetBuilder1(byref(cls._font_builder))
2028        else:
2029            cls._font_loader = LegacyFontFileLoader()
2030
2031            # Note: RegisterFontLoader takes a pointer. However, for legacy we implement our own callback interface.
2032            # Therefore we need to pass to the actual pointer directly.
2033            cls._write_factory.RegisterFontFileLoader(cls._font_loader.pointers[IDWriteFontFileLoader])
2034
2035            cls._font_collection_loader = LegacyCollectionLoader(cls._write_factory, cls._font_loader)
2036            cls._write_factory.RegisterFontCollectionLoader(cls._font_collection_loader)
2037
2038            cls._font_loader_key = cast(create_unicode_buffer("legacy_font_loader"), c_void_p)
2039
2040    @classmethod
2041    def add_font_data(cls, data):
2042        if not cls._write_factory:
2043            cls._initialize_direct_write()
2044
2045        if not cls._font_loader:
2046            cls._initialize_custom_loaders()
2047
2048        if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
2049            font_file = IDWriteFontFile()
2050            hr = cls._font_loader.CreateInMemoryFontFileReference(cls._write_factory,
2051                                                                  data,
2052                                                                  len(data),
2053                                                                  None,
2054                                                                  byref(font_file))
2055
2056            hr = cls._font_builder.AddFontFile(font_file)
2057            if hr != 0:
2058                raise Exception("This font file data is not not a font or unsupported.")
2059
2060            # We have to rebuild collection everytime we add a font.
2061            # No way to add fonts to the collection once the FontSet and Collection are created.
2062            # Release old one and renew.
2063            if cls._custom_collection:
2064                cls._font_set.Release()
2065                cls._custom_collection.Release()
2066
2067            cls._font_set = IDWriteFontSet()
2068            cls._font_builder.CreateFontSet(byref(cls._font_set))
2069
2070            cls._custom_collection = IDWriteFontCollection1()
2071            cls._write_factory.CreateFontCollectionFromFontSet(cls._font_set, byref(cls._custom_collection))
2072        else:
2073            cls._font_cache.append(data)
2074
2075            # If a collection exists, we need to completely remake the collection, delete everything and start over.
2076            if cls._custom_collection:
2077                cls._custom_collection = None
2078
2079                cls._write_factory.UnregisterFontCollectionLoader(cls._font_collection_loader)
2080                cls._write_factory.UnregisterFontFileLoader(cls._font_loader)
2081
2082                cls._font_loader = LegacyFontFileLoader()
2083                cls._font_collection_loader = LegacyCollectionLoader(cls._write_factory, cls._font_loader)
2084
2085                cls._write_factory.RegisterFontCollectionLoader(cls._font_collection_loader)
2086                cls._write_factory.RegisterFontFileLoader(cls._font_loader.pointers[IDWriteFontFileLoader])
2087
2088            cls._font_collection_loader.AddFontData(cls._font_cache)
2089
2090            cls._custom_collection = IDWriteFontCollection()
2091
2092            cls._write_factory.CreateCustomFontCollection(cls._font_collection_loader,
2093                                                          cls._font_loader_key,
2094                                                          sizeof(cls._font_loader_key),
2095                                                          byref(cls._custom_collection))
2096
2097    @classmethod
2098    def get_collection(cls, font_name):
2099        """Returns which collection this font belongs to (system or custom collection), as well as it's index in the
2100        collection."""
2101        if not cls._write_factory:
2102            cls._initialize_direct_write()
2103
2104        """Returns a collection the font_name belongs to."""
2105        font_index = UINT()
2106        font_exists = BOOL()
2107
2108        # Check custom loaded font collections.
2109        if cls._custom_collection:
2110            cls._custom_collection.FindFamilyName(create_unicode_buffer(font_name),
2111                                                  byref(font_index),
2112                                                  byref(font_exists))
2113
2114            if font_exists.value:
2115                return font_index.value, cls._custom_collection
2116
2117        # Check if font is in the system collection.
2118        # Do not cache these values permanently as system font collection can be updated during runtime.
2119        if not font_exists.value:
2120            sys_collection = IDWriteFontCollection()
2121            cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
2122            sys_collection.FindFamilyName(create_unicode_buffer(font_name),
2123                                          byref(font_index),
2124                                          byref(font_exists))
2125
2126            if font_exists.value:
2127                return font_index.value, sys_collection
2128
2129        # Font does not exist in either custom or system.
2130        return None, None
2131
2132    @classmethod
2133    def have_font(cls, name):
2134        if cls.get_collection(name)[0] is not None:
2135            return True
2136
2137        return False
2138
2139    @classmethod
2140    def get_font_face(cls, name):
2141        # Check custom collection.
2142        collection = None
2143        font_index = UINT()
2144        font_exists = BOOL()
2145
2146        # Check custom collection.
2147        if cls._custom_collection:
2148            cls._custom_collection.FindFamilyName(create_unicode_buffer(name),
2149                                                  byref(font_index),
2150                                                  byref(font_exists))
2151
2152            collection = cls._custom_collection
2153
2154        if font_exists.value == 0:
2155            sys_collection = IDWriteFontCollection()
2156            cls._write_factory.GetSystemFontCollection(byref(sys_collection), 1)
2157            sys_collection.FindFamilyName(create_unicode_buffer(name),
2158                                          byref(font_index),
2159                                          byref(font_exists))
2160
2161            collection = sys_collection
2162
2163        if font_exists:
2164            font_family = IDWriteFontFamily()
2165            collection.GetFontFamily(font_index, byref(font_family))
2166
2167            write_font = IDWriteFont()
2168            font_family.GetFirstMatchingFont(DWRITE_FONT_WEIGHT_NORMAL,
2169                                             DWRITE_FONT_STRETCH_NORMAL,
2170                                             DWRITE_FONT_STYLE_NORMAL,
2171                                             byref(write_font))
2172
2173            font_face = IDWriteFontFace1()
2174            write_font.CreateFontFace(byref(font_face))
2175
2176            return font_face
2177
2178        return None
2179
2180
2181d2d_factory = ID2D1Factory()
2182hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, IID_ID2D1Factory, None, byref(d2d_factory))
2183
2184WICBitmapCreateCacheOption = UINT
2185WICBitmapNoCache = 0
2186WICBitmapCacheOnDemand = 0x1
2187WICBitmapCacheOnLoad = 0x2
2188
2189transparent = D2D1_COLOR_F(0.0, 0.0, 0.0, 0.0)
2190white = D2D1_COLOR_F(1.0, 1.0, 1.0, 1.0)
2191