1 use super::*;
2 use std::fmt;
3 use std::ffi::CString;
4 use crate::ffi::wchar_t;
5 use std::iter::repeat;
6 use std::ptr;
7 use std::mem;
8 use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
9 use foreign_types::{ForeignType, ForeignTypeRef};
10 
11 foreign_type! {
12     /// This represents owned Multi Localized Unicode type. Most methods are implemented on `MLURef`.
13     /// This is a borrwed Multi Localized Unicode type. It holds Unicode strings associated with `Locale`.
14     pub unsafe type MLU {
15         type CType = ffi::MLU;
16         fn drop = ffi::cmsMLUfree;
17     }
18 }
19 
20 impl MLU {
21     /// Allocates an empty multilocalized unicode object.
new(items: usize) -> Self22     pub fn new(items: usize) -> Self {
23         unsafe {
24             let handle = ffi::cmsMLUalloc(ptr::null_mut(), items as u32);
25             assert!(!handle.is_null());
26             MLU::from_ptr(handle)
27         }
28     }
29 }
30 
31 impl MLURef {
32     /// Fills an ASCII (7 bit) entry for the given Language and country.
set_text_ascii(&mut self, text: &str, locale: Locale) -> bool33     pub fn set_text_ascii(&mut self, text: &str, locale: Locale) -> bool {
34         let cstr = CString::new(text).unwrap();
35         unsafe {
36             ffi::cmsMLUsetASCII(self.as_ptr(),
37                 locale.language_ptr(),
38                 locale.country_ptr(),
39                 cstr.as_ptr()) != 0
40         }
41     }
42 
43     /// Fills a UNICODE wide char (16 bit) entry for the given Language and country.
set_text(&mut self, text: &str, locale: Locale) -> bool44     pub fn set_text(&mut self, text: &str, locale: Locale) -> bool {
45         let chars: Vec<_> = text.chars()
46             .map(|c| c as wchar_t)
47             .chain(repeat(0 as wchar_t).take(1))
48             .collect();
49 
50         unsafe {
51             ffi::cmsMLUsetWide(self.as_ptr(),
52                 locale.language_ptr(),
53                 locale.country_ptr(),
54                 chars[..].as_ptr()) != 0
55         }
56     }
57 
58     /// Gets an ASCII (7 bit) entry for the given Language and country.
text_ascii(&self, locale: Locale) -> LCMSResult<CString>59     pub fn text_ascii(&self, locale: Locale) -> LCMSResult<CString> {
60         let len = unsafe {
61             ffi::cmsMLUgetASCII(self.as_ptr(),
62                 locale.language_ptr(),
63                 locale.country_ptr(),
64                 ptr::null_mut(), 0)
65         };
66         if len == 0 {
67             return Err(Error::MissingData);
68         }
69         let mut buf = vec![0u8; len as usize];
70         unsafe {
71             ffi::cmsMLUgetASCII(self.as_ptr(),
72                 locale.language_ptr(),
73                 locale.country_ptr(),
74                 buf[..].as_ptr() as *mut _, len);
75             if let Some(0) = buf.pop() { // terminating zero
76                 for c in &mut buf {
77                     if *c > 127 {*c = b'?'}
78                 }
79                 CString::new(buf).map_err(|_| Error::InvalidString)
80             } else {
81                 Err(Error::InvalidString)
82             }
83         }
84     }
85 
86     /// Gets a Unicode entry for the given Language and country
text(&self, locale: Locale) -> LCMSResult<String>87     pub fn text(&self, locale: Locale) -> LCMSResult<String> {
88         let len_bytes = unsafe {
89             ffi::cmsMLUgetWide(self.as_ptr(),
90                 locale.language_ptr(),
91                 locale.country_ptr(),
92                 ptr::null_mut(), 0)
93         };
94         let len_wchars = len_bytes as usize / mem::size_of::<wchar_t>();
95         if len_wchars == 0 || (len_bytes&1) != 0 {
96             return Err(Error::MissingData);
97         }
98         let mut buf = vec![0 as wchar_t; len_wchars];
99         unsafe {
100             ffi::cmsMLUgetWide(self.as_ptr(),
101                 locale.language_ptr(),
102                 locale.country_ptr(),
103                 buf[..].as_ptr() as *mut wchar_t, len_bytes);
104             if let Some(0) = buf.pop() { // terminating zero
105                 Ok(decode_utf16(buf.into_iter().map(|c| c as u16))
106                    .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
107                    .collect())
108             } else {
109                 Err(Error::InvalidString)
110             }
111         }
112     }
113 
114     /// Obtains the translations stored in a given multilocalized unicode object.
tanslations(&self) -> Vec<Locale>115     pub fn tanslations(&self) -> Vec<Locale> {
116         let count = unsafe { ffi::cmsMLUtranslationsCount(self.as_ptr()) };
117         let mut out = Vec::with_capacity(count as usize);
118         for i in 0..count {
119             let mut locale = Locale::none();
120             if unsafe {
121                 ffi::cmsMLUtranslationsCodes(self.as_ptr(), i,
122                     locale.language_ptr_mut(),
123                     locale.country_ptr_mut()) != 0
124             } {
125                 out.push(locale);
126             }
127         }
128         out
129     }
130 
131     /// Obtains the translation rule for given multilocalized unicode object.
tanslation(&self, locale: Locale) -> LCMSResult<Locale>132     pub fn tanslation(&self, locale: Locale) -> LCMSResult<Locale> {
133         let mut out = Locale::none();
134         if unsafe {
135             ffi::cmsMLUgetTranslation(self.as_ptr(),
136                 locale.language_ptr(),
137                 locale.country_ptr(),
138                 out.language_ptr_mut(),
139                 out.country_ptr_mut()) != 0
140         } {
141             Ok(out)
142         } else {
143             Err(Error::MissingData)
144         }
145     }
146 }
147 
148 impl fmt::Debug for MLURef {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result149     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150         let t = self.text(Locale::none());
151         write!(f, "MLU({:?} {:?})", if let Ok(ref t) = t { &t } else { "None" }, self.tanslations())
152     }
153 }
154 
155 #[test]
mlu()156 fn mlu() {
157     let _ = MLU::new(0);
158     let mut m = MLU::new(1);
159     assert!(m.set_text("Hello 世界!", Locale::none()));
160     assert_eq!(Ok("Hello 世界!".to_owned()), m.text(Locale::none()));
161     assert!(!m.set_text_ascii("エッロル", Locale::none()));
162 
163     let mut m = MLU::new(1);
164     assert!(m.set_text_ascii("OK", Locale::none()));
165     assert_eq!(Ok(CString::new("OK").unwrap()), m.text_ascii(Locale::none()));
166 }
167