blitz-pdf 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -6
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/licenses/Noto-OFL.txt +94 -0
- package/licenses/NotoCJK-OFL.txt +92 -0
- package/licenses/NotoEmoji-OFL.txt +93 -0
- package/licenses/Unifont-OFL.txt +90 -0
- package/licenses/font-sources.json +106 -0
- package/package.json +1 -1
- package/src/types.ts +2 -1
- package/vendor/README.md +19 -0
- package/vendor/fontique/Cargo.toml +194 -0
- package/vendor/fontique/LICENSE-APACHE +201 -0
- package/vendor/fontique/LICENSE-MIT +25 -0
- package/vendor/fontique/README.md +54 -0
- package/vendor/fontique/examples/generic_families.rs +37 -0
- package/vendor/fontique/src/attributes.rs +42 -0
- package/vendor/fontique/src/backend/android.rs +218 -0
- package/vendor/fontique/src/backend/coretext.rs +206 -0
- package/vendor/fontique/src/backend/dwrite.rs +395 -0
- package/vendor/fontique/src/backend/fontconfig.rs +844 -0
- package/vendor/fontique/src/backend/mod.rs +75 -0
- package/vendor/fontique/src/charmap.rs +127 -0
- package/vendor/fontique/src/collection/mod.rs +1016 -0
- package/vendor/fontique/src/collection/query.rs +392 -0
- package/vendor/fontique/src/fallback.rs +293 -0
- package/vendor/fontique/src/family.rs +111 -0
- package/vendor/fontique/src/family_name.rs +153 -0
- package/vendor/fontique/src/font.rs +562 -0
- package/vendor/fontique/src/generic.rs +36 -0
- package/vendor/fontique/src/impl_fontconfig.rs +159 -0
- package/vendor/fontique/src/lib.rs +87 -0
- package/vendor/fontique/src/matching.rs +483 -0
- package/vendor/fontique/src/scan.rs +312 -0
- package/vendor/fontique/src/script.rs +271 -0
- package/vendor/fontique/src/source.rs +108 -0
- package/vendor/fontique/src/source_cache.rs +254 -0
- package/wasm/blitz_pdf_bg.wasm +0 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
// Copyright 2024 the Parley Authors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
3
|
+
|
|
4
|
+
use alloc::string::String;
|
|
5
|
+
use alloc::sync::Arc;
|
|
6
|
+
use alloc::{vec, vec::Vec};
|
|
7
|
+
use hashbrown::HashMap;
|
|
8
|
+
use std::{
|
|
9
|
+
ffi::{OsString, c_void},
|
|
10
|
+
os::windows::ffi::OsStringExt,
|
|
11
|
+
path::PathBuf,
|
|
12
|
+
};
|
|
13
|
+
use windows::Win32::Foundation::STATUS_NOT_FOUND;
|
|
14
|
+
use windows::{
|
|
15
|
+
Win32::Graphics::DirectWrite::{
|
|
16
|
+
DWRITE_FACTORY_TYPE_SHARED, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL,
|
|
17
|
+
DWRITE_FONT_WEIGHT_REGULAR, DWRITE_READING_DIRECTION,
|
|
18
|
+
DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, DWriteCreateFactory, IDWriteFactory,
|
|
19
|
+
IDWriteFactory2, IDWriteFont, IDWriteFontCollection, IDWriteFontFace, IDWriteFontFallback,
|
|
20
|
+
IDWriteFontFamily, IDWriteFontFile, IDWriteLocalFontFileLoader, IDWriteNumberSubstitution,
|
|
21
|
+
IDWriteTextAnalysisSource, IDWriteTextAnalysisSource_Impl,
|
|
22
|
+
},
|
|
23
|
+
core::{Interface, OutRef, PCWSTR, implement},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
use super::{
|
|
27
|
+
FallbackKey, FamilyId, FamilyInfo, FamilyNameMap, FontInfo, GenericFamily, GenericFamilyMap,
|
|
28
|
+
ScriptExt, SourcePathMap,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const DEFAULT_GENERIC_FAMILIES: &[(GenericFamily, &[&str])] = &[
|
|
32
|
+
(GenericFamily::Serif, &["Times New Roman"]),
|
|
33
|
+
(GenericFamily::SansSerif, &["Arial"]),
|
|
34
|
+
(GenericFamily::Monospace, &["Consolas"]),
|
|
35
|
+
(GenericFamily::Cursive, &["Comic Sans MS"]),
|
|
36
|
+
(GenericFamily::Fantasy, &["Impact"]),
|
|
37
|
+
(GenericFamily::SystemUi, &["Segoe UI"]),
|
|
38
|
+
(GenericFamily::Emoji, &["Segoe UI Emoji"]),
|
|
39
|
+
(GenericFamily::Math, &["Cambria Math"]),
|
|
40
|
+
(GenericFamily::FangSong, &["FangSong"]),
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/// Raw access to the collection of local system fonts.
|
|
44
|
+
pub(crate) struct SystemFonts {
|
|
45
|
+
pub(crate) name_map: Arc<FamilyNameMap>,
|
|
46
|
+
pub(crate) generic_families: Arc<GenericFamilyMap>,
|
|
47
|
+
source_cache: SourcePathMap,
|
|
48
|
+
family_map: HashMap<FamilyId, Option<FamilyInfo>>,
|
|
49
|
+
dwrite_fonts: DWriteSystemFonts,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
impl SystemFonts {
|
|
53
|
+
pub(crate) fn new() -> Self {
|
|
54
|
+
let dwrite_fonts = DWriteSystemFonts::new(false).unwrap();
|
|
55
|
+
let mut name_map = FamilyNameMap::default();
|
|
56
|
+
for family in dwrite_fonts.families() {
|
|
57
|
+
if let Some(names) = family.names() {
|
|
58
|
+
let [first_name, other_names @ ..] = names.as_slice() else {
|
|
59
|
+
continue;
|
|
60
|
+
};
|
|
61
|
+
let id = name_map.get_or_insert(first_name).id();
|
|
62
|
+
for other_name in other_names {
|
|
63
|
+
name_map.add_alias(id, other_name);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
let mut generic_families = GenericFamilyMap::default();
|
|
68
|
+
for (family, names) in DEFAULT_GENERIC_FAMILIES {
|
|
69
|
+
generic_families.set(
|
|
70
|
+
*family,
|
|
71
|
+
names
|
|
72
|
+
.iter()
|
|
73
|
+
.filter_map(|name| name_map.get(name))
|
|
74
|
+
.map(|name| name.id()),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
Self {
|
|
78
|
+
name_map: Arc::new(name_map),
|
|
79
|
+
generic_families: Arc::new(generic_families),
|
|
80
|
+
source_cache: SourcePathMap::default(),
|
|
81
|
+
family_map: HashMap::default(),
|
|
82
|
+
dwrite_fonts,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
pub(crate) fn family(&mut self, id: FamilyId) -> Option<FamilyInfo> {
|
|
87
|
+
match self.family_map.get(&id) {
|
|
88
|
+
Some(Some(family)) => return Some(family.clone()),
|
|
89
|
+
Some(None) => return None,
|
|
90
|
+
_ => {}
|
|
91
|
+
}
|
|
92
|
+
let name = self.name_map.get_by_id(id)?;
|
|
93
|
+
let mut fonts = smallvec::SmallVec::<[FontInfo; 4]>::default();
|
|
94
|
+
if let Some(family) = self.dwrite_fonts.family_by_name(name.name()) {
|
|
95
|
+
for font in family.fonts() {
|
|
96
|
+
if let Some(font) = FontInfo::from_dwrite(&font, &mut self.source_cache)
|
|
97
|
+
&& !fonts
|
|
98
|
+
.iter()
|
|
99
|
+
.any(|f| f.source().id() == font.source().id() && f.index() == font.index())
|
|
100
|
+
{
|
|
101
|
+
fonts.push(font);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if !fonts.is_empty() {
|
|
105
|
+
let family = FamilyInfo::new(name.clone(), fonts);
|
|
106
|
+
self.family_map.insert(id, Some(family.clone()));
|
|
107
|
+
return Some(family);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
self.family_map.insert(id, None);
|
|
111
|
+
None
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
pub(crate) fn fallback(&mut self, key: impl Into<FallbackKey>) -> Option<FamilyId> {
|
|
115
|
+
// DirectWrite does not have a function to get the default font for a script and locale pair
|
|
116
|
+
// so here we provide a sample of the intended script instead.
|
|
117
|
+
let key = key.into();
|
|
118
|
+
let text = key.script().sample()?;
|
|
119
|
+
let locale = key.locale_str();
|
|
120
|
+
let family_name = self.dwrite_fonts.family_name_for_text(text, locale)?;
|
|
121
|
+
self.name_map.get(&family_name).map(|name| name.id())
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
impl FontInfo {
|
|
126
|
+
fn from_dwrite(font: &DWriteFont, paths: &mut SourcePathMap) -> Option<Self> {
|
|
127
|
+
let path = font.file_path()?;
|
|
128
|
+
let index = font.index();
|
|
129
|
+
let data = paths.get_or_insert(&path);
|
|
130
|
+
Self::from_source(data, index)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
struct DWriteSystemFonts {
|
|
135
|
+
collection: IDWriteFontCollection,
|
|
136
|
+
fallback: Option<IDWriteFontFallback>,
|
|
137
|
+
map_buf: Vec<u16>,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
impl DWriteSystemFonts {
|
|
141
|
+
fn new(update: bool) -> windows_core::Result<Self> {
|
|
142
|
+
unsafe {
|
|
143
|
+
let factory = DWriteCreateFactory::<IDWriteFactory>(DWRITE_FACTORY_TYPE_SHARED)?;
|
|
144
|
+
let mut collection: Option<IDWriteFontCollection> = None;
|
|
145
|
+
factory.GetSystemFontCollection(&mut collection, update)?;
|
|
146
|
+
let collection = collection.ok_or_else(|| {
|
|
147
|
+
windows_core::Error::new(STATUS_NOT_FOUND.to_hresult(), "no collection")
|
|
148
|
+
})?;
|
|
149
|
+
let fallback = factory
|
|
150
|
+
.cast::<IDWriteFactory2>()
|
|
151
|
+
.and_then(|f| f.GetSystemFontFallback())
|
|
152
|
+
.ok();
|
|
153
|
+
Ok(Self {
|
|
154
|
+
collection,
|
|
155
|
+
fallback,
|
|
156
|
+
map_buf: vec![],
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
fn get(&self, index: u32) -> Option<DWriteFontFamily> {
|
|
162
|
+
unsafe {
|
|
163
|
+
self.collection
|
|
164
|
+
.GetFontFamily(index)
|
|
165
|
+
.ok()
|
|
166
|
+
.map(DWriteFontFamily)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fn family_by_name(&self, name: &str) -> Option<DWriteFontFamily> {
|
|
171
|
+
let mut index = 0;
|
|
172
|
+
#[expect(
|
|
173
|
+
clippy::default_trait_access,
|
|
174
|
+
reason = "The type this resolves to doesn't seem to be nameable."
|
|
175
|
+
)]
|
|
176
|
+
// The documentation for `FindFamilyName` seems to be:
|
|
177
|
+
// https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/Graphics/DirectWrite/trait.IDWriteFontCollection_Impl.html#tymethod.FindFamilyName
|
|
178
|
+
// And we need a value of type BOOL, which you'll notice is allegedly not actually public (i.e. it has no link).
|
|
179
|
+
let mut exists = Default::default();
|
|
180
|
+
let mut name_buf = smallvec::SmallVec::<[u16; 128]>::default();
|
|
181
|
+
name_buf.extend(name.encode_utf16());
|
|
182
|
+
name_buf.push(0);
|
|
183
|
+
unsafe {
|
|
184
|
+
self.collection
|
|
185
|
+
.FindFamilyName(PCWSTR::from_raw(name_buf.as_ptr()), &mut index, &mut exists)
|
|
186
|
+
.ok()?;
|
|
187
|
+
}
|
|
188
|
+
self.get(index)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
fn families(&self) -> impl Iterator<Item = DWriteFontFamily> {
|
|
192
|
+
let this = self.collection.clone();
|
|
193
|
+
unsafe {
|
|
194
|
+
let count = this.GetFontFamilyCount();
|
|
195
|
+
(0..count).filter_map(move |index| this.GetFontFamily(index).ok().map(DWriteFontFamily))
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
fn family_name_for_text(&mut self, text: &str, locale: Option<&str>) -> Option<String> {
|
|
200
|
+
self.map_buf.clear();
|
|
201
|
+
self.map_buf.extend(text.encode_utf16());
|
|
202
|
+
let text_len = self.map_buf.len();
|
|
203
|
+
if let Some(locale) = locale {
|
|
204
|
+
self.map_buf.extend(locale.encode_utf16());
|
|
205
|
+
}
|
|
206
|
+
self.map_buf.push(0);
|
|
207
|
+
let (text, locale) = self.map_buf.split_at(text_len);
|
|
208
|
+
let source: IDWriteTextAnalysisSource = TextSource { text, locale }.into();
|
|
209
|
+
unsafe {
|
|
210
|
+
let mut cur_offset = 0;
|
|
211
|
+
while cur_offset < text_len {
|
|
212
|
+
if let Some(fallback) = self.fallback.as_ref() {
|
|
213
|
+
let mut mapped_len = 0;
|
|
214
|
+
let mut mapped_font = None;
|
|
215
|
+
if fallback
|
|
216
|
+
.MapCharacters(
|
|
217
|
+
&source,
|
|
218
|
+
cur_offset as u32,
|
|
219
|
+
(text_len - cur_offset) as u32,
|
|
220
|
+
&self.collection,
|
|
221
|
+
None,
|
|
222
|
+
DWRITE_FONT_WEIGHT_REGULAR,
|
|
223
|
+
DWRITE_FONT_STYLE_NORMAL,
|
|
224
|
+
DWRITE_FONT_STRETCH_NORMAL,
|
|
225
|
+
&mut mapped_len,
|
|
226
|
+
&mut mapped_font,
|
|
227
|
+
&mut 1.0,
|
|
228
|
+
)
|
|
229
|
+
.is_ok()
|
|
230
|
+
&& let Some(font) = mapped_font
|
|
231
|
+
{
|
|
232
|
+
let family = font.GetFontFamily().ok()?;
|
|
233
|
+
let names = family.GetFamilyNames().ok()?;
|
|
234
|
+
let name_len = names.GetStringLength(0).ok()? as usize;
|
|
235
|
+
let mut name_buf = smallvec::SmallVec::<[u16; 128]>::default();
|
|
236
|
+
name_buf.resize(name_len + 1, 0);
|
|
237
|
+
names.GetString(0, &mut name_buf).ok()?;
|
|
238
|
+
name_buf.pop();
|
|
239
|
+
return Some(String::from_utf16_lossy(&name_buf));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
cur_offset += 1;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
None
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
#[derive(Clone)]
|
|
250
|
+
struct DWriteFontFamily(IDWriteFontFamily);
|
|
251
|
+
|
|
252
|
+
impl DWriteFontFamily {
|
|
253
|
+
fn names(&self) -> Option<Vec<String>> {
|
|
254
|
+
let mut names = vec![];
|
|
255
|
+
unsafe {
|
|
256
|
+
let family_names = self.0.GetFamilyNames().ok()?;
|
|
257
|
+
let count = family_names.GetCount();
|
|
258
|
+
let mut buf = vec![];
|
|
259
|
+
for i in 0..count {
|
|
260
|
+
let Ok(len) = family_names.GetStringLength(i) else {
|
|
261
|
+
continue;
|
|
262
|
+
};
|
|
263
|
+
buf.clear();
|
|
264
|
+
buf.resize(len as usize + 1, 0);
|
|
265
|
+
if family_names.GetString(i, &mut buf).is_err() {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
buf.pop();
|
|
269
|
+
names.push(String::from_utf16_lossy(&buf));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
Some(names)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
fn fonts(&self) -> impl Iterator<Item = DWriteFont> {
|
|
276
|
+
let this = self.0.clone();
|
|
277
|
+
unsafe {
|
|
278
|
+
let count = self.0.GetFontCount();
|
|
279
|
+
(0..count).filter_map(move |index| {
|
|
280
|
+
let font = this.GetFont(index).ok()?;
|
|
281
|
+
// We don't want fonts with simulations
|
|
282
|
+
if font.GetSimulations().0 != 0 {
|
|
283
|
+
return None;
|
|
284
|
+
}
|
|
285
|
+
DWriteFont::new(&font)
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Note, this is a font face. We don't care about the font.
|
|
292
|
+
#[derive(Clone)]
|
|
293
|
+
struct DWriteFont(IDWriteFontFace);
|
|
294
|
+
|
|
295
|
+
impl DWriteFont {
|
|
296
|
+
fn new(font: &IDWriteFont) -> Option<Self> {
|
|
297
|
+
unsafe { Some(Self(font.CreateFontFace().ok()?)) }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
fn file_path(&self) -> Option<PathBuf> {
|
|
301
|
+
unsafe {
|
|
302
|
+
// We only care about fonts with a single file.
|
|
303
|
+
let mut file: Option<IDWriteFontFile> = None;
|
|
304
|
+
self.0.GetFiles(&mut 1, Some(&mut file)).ok()?;
|
|
305
|
+
let file = file?;
|
|
306
|
+
// Now the ugly stuff...
|
|
307
|
+
let mut ref_key: *mut c_void = core::ptr::null_mut();
|
|
308
|
+
let mut ref_key_size: u32 = 0;
|
|
309
|
+
file.GetReferenceKey(&mut ref_key, &mut ref_key_size).ok()?;
|
|
310
|
+
let loader = file.GetLoader().ok()?;
|
|
311
|
+
let local_loader: IDWriteLocalFontFileLoader = loader.cast().ok()?;
|
|
312
|
+
let file_path_len = local_loader
|
|
313
|
+
.GetFilePathLengthFromKey(ref_key, ref_key_size)
|
|
314
|
+
.ok()?;
|
|
315
|
+
let mut file_path_buf = vec![0; file_path_len as usize + 1];
|
|
316
|
+
local_loader
|
|
317
|
+
.GetFilePathFromKey(ref_key, ref_key_size, &mut file_path_buf)
|
|
318
|
+
.ok()?;
|
|
319
|
+
if let Some(&0) = file_path_buf.last() {
|
|
320
|
+
file_path_buf.pop();
|
|
321
|
+
}
|
|
322
|
+
Some(PathBuf::from(OsString::from_wide(&file_path_buf)))
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
fn index(&self) -> u32 {
|
|
327
|
+
unsafe { self.0.GetIndex() }
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
#[implement(IDWriteTextAnalysisSource)]
|
|
332
|
+
struct TextSource<'a> {
|
|
333
|
+
text: &'a [u16],
|
|
334
|
+
locale: &'a [u16],
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
impl IDWriteTextAnalysisSource_Impl for TextSource_Impl<'_> {
|
|
338
|
+
fn GetLocaleName(
|
|
339
|
+
&self,
|
|
340
|
+
textposition: u32,
|
|
341
|
+
textlength: *mut u32,
|
|
342
|
+
localename: *mut *mut u16,
|
|
343
|
+
) -> windows::core::Result<()> {
|
|
344
|
+
unsafe {
|
|
345
|
+
*textlength = (self.text.len() as u32).saturating_sub(textposition);
|
|
346
|
+
*localename = core::mem::transmute::<*const u16, *mut u16>(self.locale.as_ptr());
|
|
347
|
+
}
|
|
348
|
+
Ok(())
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
fn GetNumberSubstitution(
|
|
352
|
+
&self,
|
|
353
|
+
textposition: u32,
|
|
354
|
+
textlength: *mut u32,
|
|
355
|
+
numbersubstitution: OutRef<'_, IDWriteNumberSubstitution>,
|
|
356
|
+
) -> windows::core::Result<()> {
|
|
357
|
+
numbersubstitution.write(None)?;
|
|
358
|
+
unsafe {
|
|
359
|
+
*textlength = (self.text.len() as u32).saturating_sub(textposition);
|
|
360
|
+
}
|
|
361
|
+
Ok(())
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
fn GetParagraphReadingDirection(&self) -> DWRITE_READING_DIRECTION {
|
|
365
|
+
DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
fn GetTextAtPosition(
|
|
369
|
+
&self,
|
|
370
|
+
textposition: u32,
|
|
371
|
+
textstring: *mut *mut u16,
|
|
372
|
+
textlength: *mut u32,
|
|
373
|
+
) -> windows::core::Result<()> {
|
|
374
|
+
unsafe {
|
|
375
|
+
let text = self.text.get(textposition as usize..).unwrap_or_default();
|
|
376
|
+
*textlength = text.len() as _;
|
|
377
|
+
*textstring = core::mem::transmute::<*const u16, *mut u16>(text.as_ptr());
|
|
378
|
+
}
|
|
379
|
+
Ok(())
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
fn GetTextBeforePosition(
|
|
383
|
+
&self,
|
|
384
|
+
textposition: u32,
|
|
385
|
+
textstring: *mut *mut u16,
|
|
386
|
+
textlength: *mut u32,
|
|
387
|
+
) -> windows::core::Result<()> {
|
|
388
|
+
unsafe {
|
|
389
|
+
let text = self.text.get(..textposition as usize).unwrap_or_default();
|
|
390
|
+
*textlength = text.len() as _;
|
|
391
|
+
*textstring = core::mem::transmute::<*const u16, *mut u16>(text.as_ptr());
|
|
392
|
+
}
|
|
393
|
+
Ok(())
|
|
394
|
+
}
|
|
395
|
+
}
|