1 //! PostScript string identifiers. 2 3 /// PostScript string identifier (SID). 4 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] 5 pub struct StringId(u16); 6 7 impl StringId { 8 /// Creates an identifier from a 16-bit unsigned integer. new(raw: u16) -> Self9 pub const fn new(raw: u16) -> Self { 10 Self(raw) 11 } 12 13 /// Returns the underlying identifier as a 16-bit unsigned integer. to_u16(self) -> u1614 pub const fn to_u16(self) -> u16 { 15 self.0 16 } 17 18 /// Resolves the identifier as a standard string. 19 /// 20 /// If the identifer represents a standard string, returns `Ok(string)`, 21 /// otherwise returns `Err(index)` with the index that should be used to 22 /// retrieve the string from the CFF string INDEX. 23 /// 24 /// The standard string set is available in the section 25 /// "Appendix A - Standard Strings" at <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf>. standard_string(self) -> Result<Latin1String<'static>, usize>26 pub fn standard_string(self) -> Result<Latin1String<'static>, usize> { 27 let ix = self.0 as usize; 28 if let Some(string) = STANDARD_STRINGS.get(ix) { 29 // The standard strings are all ASCII so it's safe to intepret them 30 // as Latin-1. This is verified in a unit test. 31 Ok(Latin1String::new(string.as_bytes())) 32 } else { 33 Err(ix - STANDARD_STRINGS.len()) 34 } 35 } 36 } 37 38 impl From<i32> for StringId { from(value: i32) -> Self39 fn from(value: i32) -> Self { 40 Self::new(value as u16) 41 } 42 } 43 44 /// Reference to a Latin-1 encoded string. 45 /// 46 /// Strings stored in all PostScript defined fonts are usually ASCII but are 47 /// technically encoded in Latin-1. This type wraps the raw string data to 48 /// prevent attempts to decode as UTF-8. 49 /// 50 /// This implements `PartialEq<&str>` to support easy comparison with UTF-8 51 /// strings. 52 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 53 pub struct Latin1String<'a> { 54 chars: &'a [u8], 55 } 56 57 impl<'a> Latin1String<'a> { 58 /// Creates a new Latin-1 encoded string reference from the given bytes, 59 /// with each representing a character. new(chars: &'a [u8]) -> Self60 pub const fn new(chars: &'a [u8]) -> Self { 61 Self { chars } 62 } 63 64 /// Returns an iterator over the characters of the string. 65 /// 66 /// This simply converts each byte to `char`. chars(&self) -> impl Iterator<Item = char> + Clone + 'a67 pub fn chars(&self) -> impl Iterator<Item = char> + Clone + 'a { 68 self.chars.iter().map(|b| *b as char) 69 } 70 } 71 72 impl PartialEq<&str> for Latin1String<'_> { eq(&self, other: &&str) -> bool73 fn eq(&self, other: &&str) -> bool { 74 self.chars().eq(other.chars()) 75 } 76 } 77 78 impl std::fmt::Display for Latin1String<'_> { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result79 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 80 for ch in self.chars() { 81 write!(f, "{}", ch)?; 82 } 83 Ok(()) 84 } 85 } 86 87 /// The PostScript standard string set. 88 /// 89 /// See "Appendix A - Standard Strings" in <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf> 90 pub const STANDARD_STRINGS: &[&str] = &[ 91 ".notdef", 92 "space", 93 "exclam", 94 "quotedbl", 95 "numbersign", 96 "dollar", 97 "percent", 98 "ampersand", 99 "quoteright", 100 "parenleft", 101 "parenright", 102 "asterisk", 103 "plus", 104 "comma", 105 "hyphen", 106 "period", 107 "slash", 108 "zero", 109 "one", 110 "two", 111 "three", 112 "four", 113 "five", 114 "six", 115 "seven", 116 "eight", 117 "nine", 118 "colon", 119 "semicolon", 120 "less", 121 "equal", 122 "greater", 123 "question", 124 "at", 125 "A", 126 "B", 127 "C", 128 "D", 129 "E", 130 "F", 131 "G", 132 "H", 133 "I", 134 "J", 135 "K", 136 "L", 137 "M", 138 "N", 139 "O", 140 "P", 141 "Q", 142 "R", 143 "S", 144 "T", 145 "U", 146 "V", 147 "W", 148 "X", 149 "Y", 150 "Z", 151 "bracketleft", 152 "backslash", 153 "bracketright", 154 "asciicircum", 155 "underscore", 156 "quoteleft", 157 "a", 158 "b", 159 "c", 160 "d", 161 "e", 162 "f", 163 "g", 164 "h", 165 "i", 166 "j", 167 "k", 168 "l", 169 "m", 170 "n", 171 "o", 172 "p", 173 "q", 174 "r", 175 "s", 176 "t", 177 "u", 178 "v", 179 "w", 180 "x", 181 "y", 182 "z", 183 "braceleft", 184 "bar", 185 "braceright", 186 "asciitilde", 187 "exclamdown", 188 "cent", 189 "sterling", 190 "fraction", 191 "yen", 192 "florin", 193 "section", 194 "currency", 195 "quotesingle", 196 "quotedblleft", 197 "guillemotleft", 198 "guilsinglleft", 199 "guilsinglright", 200 "fi", 201 "fl", 202 "endash", 203 "dagger", 204 "daggerdbl", 205 "periodcentered", 206 "paragraph", 207 "bullet", 208 "quotesinglbase", 209 "quotedblbase", 210 "quotedblright", 211 "guillemotright", 212 "ellipsis", 213 "perthousand", 214 "questiondown", 215 "grave", 216 "acute", 217 "circumflex", 218 "tilde", 219 "macron", 220 "breve", 221 "dotaccent", 222 "dieresis", 223 "ring", 224 "cedilla", 225 "hungarumlaut", 226 "ogonek", 227 "caron", 228 "emdash", 229 "AE", 230 "ordfeminine", 231 "Lslash", 232 "Oslash", 233 "OE", 234 "ordmasculine", 235 "ae", 236 "dotlessi", 237 "lslash", 238 "oslash", 239 "oe", 240 "germandbls", 241 "onesuperior", 242 "logicalnot", 243 "mu", 244 "trademark", 245 "Eth", 246 "onehalf", 247 "plusminus", 248 "Thorn", 249 "onequarter", 250 "divide", 251 "brokenbar", 252 "degree", 253 "thorn", 254 "threequarters", 255 "twosuperior", 256 "registered", 257 "minus", 258 "eth", 259 "multiply", 260 "threesuperior", 261 "copyright", 262 "Aacute", 263 "Acircumflex", 264 "Adieresis", 265 "Agrave", 266 "Aring", 267 "Atilde", 268 "Ccedilla", 269 "Eacute", 270 "Ecircumflex", 271 "Edieresis", 272 "Egrave", 273 "Iacute", 274 "Icircumflex", 275 "Idieresis", 276 "Igrave", 277 "Ntilde", 278 "Oacute", 279 "Ocircumflex", 280 "Odieresis", 281 "Ograve", 282 "Otilde", 283 "Scaron", 284 "Uacute", 285 "Ucircumflex", 286 "Udieresis", 287 "Ugrave", 288 "Yacute", 289 "Ydieresis", 290 "Zcaron", 291 "aacute", 292 "acircumflex", 293 "adieresis", 294 "agrave", 295 "aring", 296 "atilde", 297 "ccedilla", 298 "eacute", 299 "ecircumflex", 300 "edieresis", 301 "egrave", 302 "iacute", 303 "icircumflex", 304 "idieresis", 305 "igrave", 306 "ntilde", 307 "oacute", 308 "ocircumflex", 309 "odieresis", 310 "ograve", 311 "otilde", 312 "scaron", 313 "uacute", 314 "ucircumflex", 315 "udieresis", 316 "ugrave", 317 "yacute", 318 "ydieresis", 319 "zcaron", 320 "exclamsmall", 321 "Hungarumlautsmall", 322 "dollaroldstyle", 323 "dollarsuperior", 324 "ampersandsmall", 325 "Acutesmall", 326 "parenleftsuperior", 327 "parenrightsuperior", 328 "twodotenleader", 329 "onedotenleader", 330 "zerooldstyle", 331 "oneoldstyle", 332 "twooldstyle", 333 "threeoldstyle", 334 "fouroldstyle", 335 "fiveoldstyle", 336 "sixoldstyle", 337 "sevenoldstyle", 338 "eightoldstyle", 339 "nineoldstyle", 340 "commasuperior", 341 "threequartersemdash", 342 "periodsuperior", 343 "questionsmall", 344 "asuperior", 345 "bsuperior", 346 "centsuperior", 347 "dsuperior", 348 "esuperior", 349 "isuperior", 350 "lsuperior", 351 "msuperior", 352 "nsuperior", 353 "osuperior", 354 "rsuperior", 355 "ssuperior", 356 "tsuperior", 357 "ff", 358 "ffi", 359 "ffl", 360 "parenleftinferior", 361 "parenrightinferior", 362 "Circumflexsmall", 363 "hyphensuperior", 364 "Gravesmall", 365 "Asmall", 366 "Bsmall", 367 "Csmall", 368 "Dsmall", 369 "Esmall", 370 "Fsmall", 371 "Gsmall", 372 "Hsmall", 373 "Ismall", 374 "Jsmall", 375 "Ksmall", 376 "Lsmall", 377 "Msmall", 378 "Nsmall", 379 "Osmall", 380 "Psmall", 381 "Qsmall", 382 "Rsmall", 383 "Ssmall", 384 "Tsmall", 385 "Usmall", 386 "Vsmall", 387 "Wsmall", 388 "Xsmall", 389 "Ysmall", 390 "Zsmall", 391 "colonmonetary", 392 "onefitted", 393 "rupiah", 394 "Tildesmall", 395 "exclamdownsmall", 396 "centoldstyle", 397 "Lslashsmall", 398 "Scaronsmall", 399 "Zcaronsmall", 400 "Dieresissmall", 401 "Brevesmall", 402 "Caronsmall", 403 "Dotaccentsmall", 404 "Macronsmall", 405 "figuredash", 406 "hypheninferior", 407 "Ogoneksmall", 408 "Ringsmall", 409 "Cedillasmall", 410 "questiondownsmall", 411 "oneeighth", 412 "threeeighths", 413 "fiveeighths", 414 "seveneighths", 415 "onethird", 416 "twothirds", 417 "zerosuperior", 418 "foursuperior", 419 "fivesuperior", 420 "sixsuperior", 421 "sevensuperior", 422 "eightsuperior", 423 "ninesuperior", 424 "zeroinferior", 425 "oneinferior", 426 "twoinferior", 427 "threeinferior", 428 "fourinferior", 429 "fiveinferior", 430 "sixinferior", 431 "seveninferior", 432 "eightinferior", 433 "nineinferior", 434 "centinferior", 435 "dollarinferior", 436 "periodinferior", 437 "commainferior", 438 "Agravesmall", 439 "Aacutesmall", 440 "Acircumflexsmall", 441 "Atildesmall", 442 "Adieresissmall", 443 "Aringsmall", 444 "AEsmall", 445 "Ccedillasmall", 446 "Egravesmall", 447 "Eacutesmall", 448 "Ecircumflexsmall", 449 "Edieresissmall", 450 "Igravesmall", 451 "Iacutesmall", 452 "Icircumflexsmall", 453 "Idieresissmall", 454 "Ethsmall", 455 "Ntildesmall", 456 "Ogravesmall", 457 "Oacutesmall", 458 "Ocircumflexsmall", 459 "Otildesmall", 460 "Odieresissmall", 461 "OEsmall", 462 "Oslashsmall", 463 "Ugravesmall", 464 "Uacutesmall", 465 "Ucircumflexsmall", 466 "Udieresissmall", 467 "Yacutesmall", 468 "Thornsmall", 469 "Ydieresissmall", 470 "001.000", 471 "001.001", 472 "001.002", 473 "001.003", 474 "Black", 475 "Bold", 476 "Book", 477 "Light", 478 "Medium", 479 "Regular", 480 "Roman", 481 "Semibold", 482 ]; 483 484 #[cfg(test)] 485 mod tests { 486 use super::{Latin1String, StringId, STANDARD_STRINGS}; 487 488 #[test] lets_latin1()489 fn lets_latin1() { 490 let latin1 = Latin1String::new(&[223, 214, 209, 208]); 491 let utf8 = "ßÖÑÐ"; 492 assert_ne!(latin1.chars, utf8.as_bytes()); 493 assert_eq!(latin1, utf8); 494 } 495 496 #[test] standard_strings()497 fn standard_strings() { 498 for (i, &std_string) in STANDARD_STRINGS.iter().enumerate() { 499 let sid = StringId::new(i as _); 500 let latin1 = sid.standard_string().unwrap(); 501 // Ensure we can compare directly with &str 502 assert_eq!(latin1, std_string); 503 // Ensure our to_string() conversion works (via the Display impl) 504 assert_eq!(latin1.to_string(), std_string); 505 } 506 } 507 508 #[test] not_a_standard_string()509 fn not_a_standard_string() { 510 let sid = StringId::new(STANDARD_STRINGS.len() as _); 511 assert!(sid.standard_string().is_err()); 512 assert_eq!(sid.standard_string().unwrap_err(), 0); 513 } 514 } 515