2 * Methods for dealing with opentype font tables
4 * Copyright 2014 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "dwrite_private.h"
25 WINE_DEFAULT_DEBUG_CHANNEL(dwrite
);
27 #define MS_TTCF_TAG DWRITE_MAKE_OPENTYPE_TAG('t','t','c','f')
28 #define MS_OTTO_TAG DWRITE_MAKE_OPENTYPE_TAG('O','T','T','O')
30 #ifdef WORDS_BIGENDIAN
31 #define GET_BE_WORD(x) (x)
32 #define GET_BE_DWORD(x) (x)
34 #define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
35 #define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)))
64 } CMAP_EncodingRecord
;
69 CMAP_EncodingRecord tables
[1];
76 } CMAP_SegmentedCoverage_group
;
84 CMAP_SegmentedCoverage_group groups
[1];
85 } CMAP_SegmentedCoverage
;
96 } CMAP_SegmentMapping_0
;
98 enum OPENTYPE_CMAP_TABLE_FORMAT
100 OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING
= 4,
101 OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE
= 12
104 /* PANOSE is 10 bytes in size, need to pack the structure properly */
105 #include "pshpack2.h"
121 USHORT lowestRecPPEM
;
122 SHORT direction_hint
;
124 SHORT glyphdata_format
;
131 SHORT underlinePosition
;
132 SHORT underlineThickness
;
144 USHORT usWeightClass
;
147 SHORT ySubscriptXSize
;
148 SHORT ySubscriptYSize
;
149 SHORT ySubscriptXOffset
;
150 SHORT ySubscriptYOffset
;
151 SHORT ySuperscriptXSize
;
152 SHORT ySuperscriptYSize
;
153 SHORT ySuperscriptXOffset
;
154 SHORT ySuperscriptYOffset
;
155 SHORT yStrikeoutSize
;
156 SHORT yStrikeoutPosition
;
159 ULONG ulUnicodeRange1
;
160 ULONG ulUnicodeRange2
;
161 ULONG ulUnicodeRange3
;
162 ULONG ulUnicodeRange4
;
165 USHORT usFirstCharIndex
;
166 USHORT usLastCharIndex
;
167 /* According to the Apple spec, original version didn't have the below fields,
168 * version numbers were taken from the OpenType spec.
170 /* version 0 (TrueType 1.5) */
171 USHORT sTypoAscender
;
172 USHORT sTypoDescender
;
176 /* version 1 (TrueType 1.66) */
177 ULONG ulCodePageRange1
;
178 ULONG ulCodePageRange2
;
179 /* version 2 (OpenType 1.2) */
182 USHORT usDefaultChar
;
201 TT_NameRecord nameRecord
[1];
204 enum TT_NAME_WINDOWS_ENCODING_ID
206 TT_NAME_WINDOWS_ENCODING_SYMBOL
= 0,
207 TT_NAME_WINDOWS_ENCODING_UCS2
,
208 TT_NAME_WINDOWS_ENCODING_SJIS
,
209 TT_NAME_WINDOWS_ENCODING_PRC
,
210 TT_NAME_WINDOWS_ENCODING_BIG5
,
211 TT_NAME_WINDOWS_ENCODING_WANSUNG
,
212 TT_NAME_WINDOWS_ENCODING_JOHAB
,
213 TT_NAME_WINDOWS_ENCODING_RESERVED1
,
214 TT_NAME_WINDOWS_ENCODING_RESERVED2
,
215 TT_NAME_WINDOWS_ENCODING_RESERVED3
,
216 TT_NAME_WINDOWS_ENCODING_UCS4
219 enum OPENTYPE_STRING_ID
221 OPENTYPE_STRING_COPYRIGHT_NOTICE
= 0,
222 OPENTYPE_STRING_FAMILY_NAME
,
223 OPENTYPE_STRING_SUBFAMILY_NAME
,
224 OPENTYPE_STRING_UNIQUE_IDENTIFIER
,
225 OPENTYPE_STRING_FULL_FONTNAME
,
226 OPENTYPE_STRING_VERSION_STRING
,
227 OPENTYPE_STRING_POSTSCRIPT_FONTNAME
,
228 OPENTYPE_STRING_TRADEMARK
,
229 OPENTYPE_STRING_MANUFACTURER
,
230 OPENTYPE_STRING_DESIGNER
,
231 OPENTYPE_STRING_DESCRIPTION
,
232 OPENTYPE_STRING_VENDOR_URL
,
233 OPENTYPE_STRING_DESIGNER_URL
,
234 OPENTYPE_STRING_LICENSE_DESCRIPTION
,
235 OPENTYPE_STRING_LICENSE_INFO_URL
,
236 OPENTYPE_STRING_RESERVED_ID15
,
237 OPENTYPE_STRING_PREFERRED_FAMILY_NAME
,
238 OPENTYPE_STRING_PREFERRED_SUBFAMILY_NAME
,
239 OPENTYPE_STRING_COMPATIBLE_FULLNAME
,
240 OPENTYPE_STRING_SAMPLE_TEXT
,
241 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
,
242 OPENTYPE_STRING_WWS_FAMILY_NAME
,
243 OPENTYPE_STRING_WWS_SUBFAMILY_NAME
246 static const UINT16 dwriteid_to_opentypeid
[DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME
+1] =
248 (UINT16
)-1, /* DWRITE_INFORMATIONAL_STRING_NONE is not used */
249 OPENTYPE_STRING_COPYRIGHT_NOTICE
,
250 OPENTYPE_STRING_VERSION_STRING
,
251 OPENTYPE_STRING_TRADEMARK
,
252 OPENTYPE_STRING_MANUFACTURER
,
253 OPENTYPE_STRING_DESIGNER
,
254 OPENTYPE_STRING_DESIGNER_URL
,
255 OPENTYPE_STRING_DESCRIPTION
,
256 OPENTYPE_STRING_VENDOR_URL
,
257 OPENTYPE_STRING_LICENSE_DESCRIPTION
,
258 OPENTYPE_STRING_LICENSE_INFO_URL
,
259 OPENTYPE_STRING_FAMILY_NAME
,
260 OPENTYPE_STRING_SUBFAMILY_NAME
,
261 OPENTYPE_STRING_PREFERRED_FAMILY_NAME
,
262 OPENTYPE_STRING_PREFERRED_SUBFAMILY_NAME
,
263 OPENTYPE_STRING_SAMPLE_TEXT
,
264 OPENTYPE_STRING_FULL_FONTNAME
,
265 OPENTYPE_STRING_POSTSCRIPT_FONTNAME
,
266 OPENTYPE_STRING_POSTSCRIPT_CID_NAME
269 HRESULT
opentype_analyze_font(IDWriteFontFileStream
*stream
, UINT32
* font_count
, DWRITE_FONT_FILE_TYPE
*file_type
, DWRITE_FONT_FACE_TYPE
*face_type
, BOOL
*supported
)
271 /* TODO: Do font validation */
272 const void *font_data
;
277 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, &font_data
, 0, sizeof(TTC_Header_V1
), &context
);
283 *file_type
= DWRITE_FONT_FILE_TYPE_UNKNOWN
;
285 *face_type
= DWRITE_FONT_FACE_TYPE_UNKNOWN
;
288 if (DWRITE_MAKE_OPENTYPE_TAG(tag
[0], tag
[1], tag
[2], tag
[3]) == MS_TTCF_TAG
)
290 const TTC_Header_V1
*header
= font_data
;
291 *font_count
= GET_BE_DWORD(header
->numFonts
);
292 *file_type
= DWRITE_FONT_FILE_TYPE_TRUETYPE_COLLECTION
;
294 *face_type
= DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION
;
297 else if (GET_BE_DWORD(*(DWORD
*)font_data
) == 0x10000)
300 *file_type
= DWRITE_FONT_FILE_TYPE_TRUETYPE
;
302 *face_type
= DWRITE_FONT_FACE_TYPE_TRUETYPE
;
305 else if (DWRITE_MAKE_OPENTYPE_TAG(tag
[0], tag
[1], tag
[2], tag
[3]) == MS_OTTO_TAG
)
307 *file_type
= DWRITE_FONT_FILE_TYPE_CFF
;
310 IDWriteFontFileStream_ReleaseFileFragment(stream
, context
);
314 HRESULT
opentype_get_font_table(IDWriteFontFileStream
*stream
, DWRITE_FONT_FACE_TYPE type
, UINT32 font_index
, UINT32 tag
,
315 const void **table_data
, void **table_context
, UINT32
*table_size
, BOOL
*found
)
318 TTC_SFNT_V1
*font_header
= NULL
;
320 TT_TableRecord
*table_record
= NULL
;
321 void *table_record_context
;
322 int table_count
, table_offset
= 0;
327 if (type
== DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION
) {
328 const TTC_Header_V1
*ttc_header
;
330 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&ttc_header
, 0, sizeof(*ttc_header
), &ttc_context
);
332 table_offset
= GET_BE_DWORD(ttc_header
->OffsetTable
[0]);
333 if (font_index
>= GET_BE_DWORD(ttc_header
->numFonts
))
336 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&font_header
, table_offset
, sizeof(*font_header
), &sfnt_context
);
337 IDWriteFontFileStream_ReleaseFileFragment(stream
, ttc_context
);
341 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&font_header
, 0, sizeof(*font_header
), &sfnt_context
);
346 table_count
= GET_BE_WORD(font_header
->numTables
);
347 table_offset
+= sizeof(*font_header
);
348 for (i
= 0; i
< table_count
; i
++)
350 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, (const void**)&table_record
, table_offset
, sizeof(*table_record
), &table_record_context
);
353 if (DWRITE_MAKE_OPENTYPE_TAG(table_record
->tag
[0], table_record
->tag
[1], table_record
->tag
[2], table_record
->tag
[3]) == tag
)
355 IDWriteFontFileStream_ReleaseFileFragment(stream
, table_record_context
);
356 table_offset
+= sizeof(*table_record
);
359 IDWriteFontFileStream_ReleaseFileFragment(stream
, sfnt_context
);
360 if (SUCCEEDED(hr
) && i
< table_count
)
362 int offset
= GET_BE_DWORD(table_record
->offset
);
363 int length
= GET_BE_DWORD(table_record
->length
);
364 IDWriteFontFileStream_ReleaseFileFragment(stream
, table_record_context
);
367 *table_size
= length
;
368 hr
= IDWriteFontFileStream_ReadFileFragment(stream
, table_data
, offset
, length
, table_context
);
378 static int compare_group(const void *a
, const void* b
)
380 const DWORD
*chr
= a
;
381 const CMAP_SegmentedCoverage_group
*group
= b
;
383 if (*chr
< GET_BE_DWORD(group
->startCharCode
))
385 if (*chr
> GET_BE_DWORD(group
->endCharCode
))
390 static void CMAP4_GetGlyphIndex(CMAP_SegmentMapping_0
* format
, UINT32 utf32c
, UINT16
*pgi
)
397 int segment_count
= GET_BE_WORD(format
->segCountX2
)/2;
398 /* This is correct because of the padding before startCode */
399 startCode
= (WORD
*)((BYTE
*)format
+ sizeof(CMAP_SegmentMapping_0
) + (sizeof(WORD
) * segment_count
));
400 idDelta
= (SHORT
*)(((BYTE
*)startCode
) + (sizeof(WORD
) * segment_count
));
401 idRangeOffset
= (WORD
*)(((BYTE
*)idDelta
) + (sizeof(WORD
) * segment_count
));
404 while(GET_BE_WORD(format
->endCode
[segment
]) < 0xffff)
406 if (utf32c
<= GET_BE_WORD(format
->endCode
[segment
]))
410 if (segment
>= segment_count
)
412 TRACE("Segment %i of %i\n",segment
, segment_count
);
413 if (GET_BE_WORD(startCode
[segment
]) > utf32c
)
415 TRACE("In range %i -> %i\n", GET_BE_WORD(startCode
[segment
]), GET_BE_WORD(format
->endCode
[segment
]));
416 if (GET_BE_WORD(idRangeOffset
[segment
]) == 0)
418 *pgi
= (SHORT
)(GET_BE_WORD(idDelta
[segment
])) + utf32c
;
422 WORD ro
= GET_BE_WORD(idRangeOffset
[segment
])/2;
423 WORD co
= (utf32c
- GET_BE_WORD(startCode
[segment
]));
424 WORD
*index
= (WORD
*)((BYTE
*)&idRangeOffset
[segment
] + (ro
+ co
));
425 *pgi
= GET_BE_WORD(*index
);
429 static void CMAP12_GetGlyphIndex(CMAP_SegmentedCoverage
* format
, UINT32 utf32c
, UINT16
*pgi
)
431 CMAP_SegmentedCoverage_group
*group
;
433 group
= bsearch(&utf32c
, format
->groups
, GET_BE_DWORD(format
->nGroups
),
434 sizeof(CMAP_SegmentedCoverage_group
), compare_group
);
438 DWORD offset
= utf32c
- GET_BE_DWORD(group
->startCharCode
);
439 *pgi
= GET_BE_DWORD(group
->startGlyphID
) + offset
;
443 void opentype_cmap_get_glyphindex(void *data
, UINT32 utf32c
, UINT16
*pgi
)
445 CMAP_Header
*CMAP_Table
= data
;
450 for (i
= 0; i
< GET_BE_WORD(CMAP_Table
->numTables
); i
++)
455 if (GET_BE_WORD(CMAP_Table
->tables
[i
].platformID
) != 3)
458 table
= (WORD
*)(((BYTE
*)CMAP_Table
) + GET_BE_DWORD(CMAP_Table
->tables
[i
].offset
));
459 type
= GET_BE_WORD(*table
);
460 TRACE("table type %i\n", type
);
464 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING
:
465 CMAP4_GetGlyphIndex((CMAP_SegmentMapping_0
*) table
, utf32c
, pgi
);
467 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE
:
468 CMAP12_GetGlyphIndex((CMAP_SegmentedCoverage
*) table
, utf32c
, pgi
);
471 TRACE("table type %i unhandled.\n", type
);
478 static UINT32
opentype_cmap_get_unicode_ranges_count(const CMAP_Header
*CMAP_Table
)
483 for (i
= 0; i
< GET_BE_WORD(CMAP_Table
->numTables
); i
++) {
487 if (GET_BE_WORD(CMAP_Table
->tables
[i
].platformID
) != 3)
490 table
= (WORD
*)(((BYTE
*)CMAP_Table
) + GET_BE_DWORD(CMAP_Table
->tables
[i
].offset
));
491 type
= GET_BE_WORD(*table
);
492 TRACE("table type %i\n", type
);
496 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING
:
498 CMAP_SegmentMapping_0
*format
= (CMAP_SegmentMapping_0
*)table
;
499 count
+= GET_BE_WORD(format
->segCountX2
)/2;
502 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE
:
504 CMAP_SegmentedCoverage
*format
= (CMAP_SegmentedCoverage
*)table
;
505 count
+= GET_BE_DWORD(format
->nGroups
);
509 FIXME("table type %i unhandled.\n", type
);
516 HRESULT
opentype_cmap_get_unicode_ranges(void *data
, UINT32 max_count
, DWRITE_UNICODE_RANGE
*ranges
, UINT32
*count
)
518 CMAP_Header
*CMAP_Table
= data
;
524 *count
= opentype_cmap_get_unicode_ranges_count(CMAP_Table
);
526 for (i
= 0; i
< GET_BE_WORD(CMAP_Table
->numTables
) && k
< max_count
; i
++)
532 if (GET_BE_WORD(CMAP_Table
->tables
[i
].platformID
) != 3)
535 table
= (WORD
*)(((BYTE
*)CMAP_Table
) + GET_BE_DWORD(CMAP_Table
->tables
[i
].offset
));
536 type
= GET_BE_WORD(*table
);
537 TRACE("table type %i\n", type
);
541 case OPENTYPE_CMAP_TABLE_SEGMENT_MAPPING
:
543 CMAP_SegmentMapping_0
*format
= (CMAP_SegmentMapping_0
*)table
;
544 UINT16 segment_count
= GET_BE_WORD(format
->segCountX2
)/2;
545 UINT16
*startCode
= (WORD
*)((BYTE
*)format
+ sizeof(CMAP_SegmentMapping_0
) + (sizeof(WORD
) * segment_count
));
547 for (j
= 0; j
< segment_count
&& GET_BE_WORD(format
->endCode
[j
]) < 0xffff && k
< max_count
; j
++, k
++) {
548 ranges
[k
].first
= GET_BE_WORD(startCode
[j
]);
549 ranges
[k
].last
= GET_BE_WORD(format
->endCode
[j
]);
553 case OPENTYPE_CMAP_TABLE_SEGMENTED_COVERAGE
:
555 CMAP_SegmentedCoverage
*format
= (CMAP_SegmentedCoverage
*)table
;
556 for (j
= 0; j
< GET_BE_DWORD(format
->nGroups
) && k
< max_count
; j
++, k
++) {
557 ranges
[k
].first
= GET_BE_DWORD(format
->groups
[j
].startCharCode
);
558 ranges
[k
].last
= GET_BE_DWORD(format
->groups
[j
].endCharCode
);
563 FIXME("table type %i unhandled.\n", type
);
567 return *count
> max_count
? E_NOT_SUFFICIENT_BUFFER
: S_OK
;
570 VOID
get_font_properties(LPCVOID os2
, LPCVOID head
, LPCVOID post
, DWRITE_FONT_METRICS
*metrics
, DWRITE_FONT_STRETCH
*stretch
, DWRITE_FONT_WEIGHT
*weight
, DWRITE_FONT_STYLE
*style
)
572 TT_OS2_V2
*tt_os2
= (TT_OS2_V2
*)os2
;
573 TT_HEAD
*tt_head
= (TT_HEAD
*)head
;
574 TT_POST
*tt_post
= (TT_POST
*)post
;
576 /* default stretch, weight and style to normal */
577 *stretch
= DWRITE_FONT_STRETCH_NORMAL
;
578 *weight
= DWRITE_FONT_WEIGHT_NORMAL
;
579 *style
= DWRITE_FONT_STYLE_NORMAL
;
581 memset(metrics
, 0, sizeof(*metrics
));
583 /* DWRITE_FONT_STRETCH enumeration values directly match font data values */
586 if (GET_BE_WORD(tt_os2
->usWidthClass
) <= DWRITE_FONT_STRETCH_ULTRA_EXPANDED
)
587 *stretch
= GET_BE_WORD(tt_os2
->usWidthClass
);
589 *weight
= GET_BE_WORD(tt_os2
->usWeightClass
);
590 TRACE("stretch=%d, weight=%d\n", *stretch
, *weight
);
592 metrics
->ascent
= GET_BE_WORD(tt_os2
->sTypoAscender
);
593 metrics
->descent
= GET_BE_WORD(tt_os2
->sTypoDescender
);
594 metrics
->lineGap
= GET_BE_WORD(tt_os2
->sTypoLineGap
);
595 metrics
->capHeight
= GET_BE_WORD(tt_os2
->sCapHeight
);
596 metrics
->xHeight
= GET_BE_WORD(tt_os2
->sxHeight
);
597 metrics
->strikethroughPosition
= GET_BE_WORD(tt_os2
->yStrikeoutPosition
);
598 metrics
->strikethroughThickness
= GET_BE_WORD(tt_os2
->yStrikeoutSize
);
603 USHORT macStyle
= GET_BE_WORD(tt_head
->macStyle
);
604 metrics
->designUnitsPerEm
= GET_BE_WORD(tt_head
->unitsPerEm
);
605 if (macStyle
& 0x0002)
606 *style
= DWRITE_FONT_STYLE_ITALIC
;
612 metrics
->underlinePosition
= GET_BE_WORD(tt_post
->underlinePosition
);
613 metrics
->underlineThickness
= GET_BE_WORD(tt_post
->underlineThickness
);
617 HRESULT
opentype_get_font_strings_from_id(const void *table_data
, DWRITE_INFORMATIONAL_STRING_ID id
, IDWriteLocalizedStrings
**strings
)
619 const TT_NAME_V0
*header
;
620 BYTE
*storage_area
= 0;
630 hr
= create_localizedstrings(strings
);
631 if (FAILED(hr
)) return hr
;
634 storage_area
= (LPBYTE
)table_data
+ GET_BE_WORD(header
->stringOffset
);
635 count
= GET_BE_WORD(header
->count
);
637 name_id
= dwriteid_to_opentypeid
[id
];
640 for (i
= 0; i
< count
; i
++) {
641 const TT_NameRecord
*record
= &header
->nameRecord
[i
];
642 USHORT lang_id
, length
, offset
, encoding
, platform
;
644 if (GET_BE_WORD(record
->nameID
) != name_id
)
649 /* Right now only accept unicode and windows encoded fonts */
650 platform
= GET_BE_WORD(record
->platformID
);
651 if (platform
!= 0 && platform
!= 3) {
652 FIXME("platform %i not supported\n", platform
);
656 lang_id
= GET_BE_WORD(record
->languageID
);
657 length
= GET_BE_WORD(record
->length
);
658 offset
= GET_BE_WORD(record
->offset
);
659 encoding
= GET_BE_WORD(record
->encodingID
);
661 if (lang_id
< 0x8000) {
662 WCHAR locale
[LOCALE_NAME_MAX_LENGTH
];
670 case TT_NAME_WINDOWS_ENCODING_SYMBOL
:
671 case TT_NAME_WINDOWS_ENCODING_UCS2
:
673 case TT_NAME_WINDOWS_ENCODING_SJIS
:
676 case TT_NAME_WINDOWS_ENCODING_PRC
:
679 case TT_NAME_WINDOWS_ENCODING_BIG5
:
682 case TT_NAME_WINDOWS_ENCODING_WANSUNG
:
685 case TT_NAME_WINDOWS_ENCODING_JOHAB
:
689 FIXME("encoding %d not handled.\n", encoding
);
694 DWORD len
= MultiByteToWideChar(codepage
, 0, (LPSTR
)(storage_area
+ offset
), length
, NULL
, 0);
695 name_string
= heap_alloc(sizeof(WCHAR
) * len
);
696 MultiByteToWideChar(codepage
, 0, (LPSTR
)(storage_area
+ offset
), length
, name_string
, len
);
701 length
/= sizeof(WCHAR
);
702 name_string
= heap_strdupnW((LPWSTR
)(storage_area
+ offset
), length
);
703 for (i
= 0; i
< length
; i
++)
704 name_string
[i
] = GET_BE_WORD(name_string
[i
]);
707 if (!LCIDToLocaleName(MAKELCID(lang_id
, SORT_DEFAULT
), locale
, sizeof(locale
)/sizeof(WCHAR
), 0)) {
708 static const WCHAR enusW
[] = {'e','n','-','u','s',0};
709 FIXME("failed to get locale name for lcid=0x%08x\n", MAKELCID(lang_id
, SORT_DEFAULT
));
710 strcpyW(locale
, enusW
);
713 TRACE("string %s for locale %s found\n", debugstr_w(name_string
), debugstr_w(locale
));
714 add_localizedstring(*strings
, locale
, name_string
);
715 heap_free(name_string
);
718 FIXME("handle NAME format 1");
724 IDWriteLocalizedStrings_Release(*strings
);