├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── bench.rs ├── src ├── error.rs ├── lib.rs ├── tables │ ├── cmap.rs │ ├── glyf.rs │ ├── head.rs │ ├── hhea.rs │ ├── hmtx.rs │ ├── loca.rs │ ├── maxp.rs │ └── mod.rs ├── types.rs └── utils.rs └── tests ├── Tuffy_Bold.ttf ├── Tuffy_Bold_LICENSE.txt └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.dylib 8 | *.dSYM 9 | *.dll 10 | *.rlib 11 | *.dummy 12 | *.exe 13 | *-test 14 | /bin/main 15 | /bin/test-internal 16 | /bin/test-external 17 | /doc/ 18 | /target/ 19 | /build/ 20 | /.rust/ 21 | rusti.sh 22 | Cargo.lock 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "piston-truetype" 3 | version = "0.0.1" 4 | 5 | [dependencies] 6 | libc = "0.2.0" 7 | byteorder = "0.4.2" 8 | 9 | [dev-dependencies] 10 | expectest = "0.4.0" 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 PistonDevelopers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # truetype 2 | A library for reading fonts from the TrueType format 3 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | extern crate piston_truetype; 5 | 6 | use piston_truetype::*; 7 | 8 | #[bench] 9 | fn font_initialization(bencher: &mut test::Bencher) { 10 | let bs = include_bytes!("../tests/Tuffy_Bold.ttf"); 11 | let data = test::black_box(&bs[..]); 12 | bencher.bytes = data.len() as u64; 13 | bencher.iter(|| { 14 | let f = FontInfo::new_with_offset(data, 0).ok().expect("Failed to load font"); 15 | test::black_box(f) 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | 2 | use std::fmt; 3 | use byteorder; 4 | 5 | /// An Error type. 6 | #[derive(Debug, PartialEq, Clone, Copy)] 7 | pub enum Error { 8 | Malformed, 9 | MissingTable, 10 | HHEAVersionIsNotSupported, 11 | HEADVersionIsNotSupported, 12 | MAXPVersionIsNotSupported, 13 | CMAPEncodingSubtableIsNotSupported, 14 | CMAPFormatIsNotSupported, 15 | UnknownLocationFormat, 16 | } 17 | 18 | impl fmt::Display for Error { 19 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 20 | use std::error::Error; 21 | f.write_str(self.description()) 22 | } 23 | } 24 | 25 | impl ::std::error::Error for Error { 26 | fn description(&self) -> &str { 27 | match *self { 28 | Error::Malformed => "malformed data", 29 | Error::MissingTable => "missing table", 30 | Error::HHEAVersionIsNotSupported => "hhea version is not supported", 31 | Error::HEADVersionIsNotSupported => "head version is not supported", 32 | Error::MAXPVersionIsNotSupported => "maxp version is not supported", 33 | Error::CMAPEncodingSubtableIsNotSupported => "cmap encoding subtable is not supported", 34 | Error::CMAPFormatIsNotSupported => "cmap format is not supported", 35 | Error::UnknownLocationFormat => "unknown index to glyph map format", 36 | } 37 | } 38 | } 39 | 40 | impl From for Error { 41 | fn from(_: byteorder::Error) -> Self { 42 | Error::Malformed 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // stb_truetype.h - v1.08 - public domain 2 | // authored from 2009-2015 by Sean Barrett / RAD Game Tools 3 | // 4 | // This library processes TrueType files: 5 | // parse files 6 | // extract glyph metrics 7 | // extract glyph shapes 8 | // render glyphs to one-channel bitmaps with antialiasing (box filter) 9 | // 10 | // Todo: 11 | // non-MS cmaps 12 | // crashproof on bad data 13 | // hinting? (no longer patented) 14 | // cleartype-style AA? 15 | // optimize: use simple memory allocator for intermediates 16 | // optimize: build edge-list directly from curves 17 | // optimize: rasterize directly from curves? 18 | // 19 | // ADDITIONAL CONTRIBUTORS 20 | // 21 | // Mikko Mononen: compound shape support, more cmap formats 22 | // Tor Andersson: kerning, subpixel rendering 23 | // 24 | // Bug/warning reports/fixes: 25 | // "Zer" on mollyrocket (with fix) 26 | // Cass Everitt 27 | // stoiko (Haemimont Games) 28 | // Brian Hook 29 | // Walter van Niftrik 30 | // David Gow 31 | // David Given 32 | // Ivan-Assen Ivanov 33 | // Anthony Pesch 34 | // Johan Duparc 35 | // Hou Qiming 36 | // Fabian "ryg" Giesen 37 | // Martins Mozeiko 38 | // Cap Petschulat 39 | // Omar Cornut 40 | // github:aloucks 41 | // Peter LaValle 42 | // Sergey Popov 43 | // Giumo X. Clanjor 44 | // Higor Euripedes 45 | // 46 | // Misc other: 47 | // Ryan Gordon 48 | // 49 | // VERSION HISTORY 50 | // 51 | // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges 52 | // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; 53 | // variant PackFontRanges to pack and render in separate phases; 54 | // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); 55 | // fixed an assert() bug in the new rasterizer 56 | // replace assert() with STBTT_assert() in new rasterizer 57 | // 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) 58 | // also more precise AA rasterizer, except if shapes overlap 59 | // remove need for STBTT_sort 60 | // 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC 61 | // 1.04 (2015-04-15) typo in example 62 | // 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes 63 | // 64 | // Full history can be found at the end of this file. 65 | // 66 | // LICENSE 67 | // 68 | // This software is in the public domain. Where that dedication is not 69 | // recognized, you are granted a perpetual, irrevocable license to copy, 70 | // distribute, and modify this file as you see fit. 71 | // 72 | // USAGE 73 | // 74 | // Include this file in whatever places neeed to refer to it. In ONE C/C++ 75 | // file, write: 76 | // #define STB_TRUETYPE_IMPLEMENTATION 77 | // before the #include of this file. This expands out the actual 78 | // implementation into that C/C++ file. 79 | // 80 | // To make the implementation private to the file that generates the implementation, 81 | // #define STBTT_STATIC 82 | // 83 | // Simple 3D API (don't ship this, but it's fine for tools and quick start) 84 | // stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture 85 | // stbtt_GetBakedQuad() -- compute quad to draw for a given char 86 | // 87 | // Improved 3D API (more shippable): 88 | // #include "stb_rect_pack.h" -- optional, but you really want it 89 | // stbtt_PackBegin() 90 | // stbtt_PackSetOversample() -- for improved quality on small fonts 91 | // stbtt_PackFontRanges() -- pack and renders 92 | // stbtt_PackEnd() 93 | // stbtt_GetPackedQuad() 94 | // 95 | // "Load" a font file from a memory buffer (you have to keep the buffer loaded) 96 | // stbtt_InitFont() 97 | // stbtt_GetFontOffsetForIndex() -- use for TTC font collections 98 | // 99 | // Render a unicode codepoint to a bitmap 100 | // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap 101 | // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide 102 | // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be 103 | // 104 | // Character advance/positioning 105 | // stbtt_GetCodepointHMetrics() 106 | // stbtt_GetFontVMetrics() 107 | // stbtt_GetCodepointKernAdvance() 108 | // 109 | // Starting with version 1.06, the rasterizer was replaced with a new, 110 | // faster and generally-more-precise rasterizer. The new rasterizer more 111 | // accurately measures pixel coverage for anti-aliasing, except in the case 112 | // where multiple shapes overlap, in which case it overestimates the AA pixel 113 | // coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If 114 | // this turns out to be a problem, you can re-enable the old rasterizer with 115 | // #define STBTT_RASTERIZER_VERSION 1 116 | // which will incur about a 15% speed hit. 117 | // 118 | // ADDITIONAL DOCUMENTATION 119 | // 120 | // Immediately after this block comment are a series of sample programs. 121 | // 122 | // After the sample programs is the "header file" section. This section 123 | // includes documentation for each API function. 124 | // 125 | // Some important concepts to understand to use this library: 126 | // 127 | // Codepoint 128 | // Characters are defined by unicode codepoints, e.g. 65 is 129 | // uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is 130 | // the hiragana for "ma". 131 | // 132 | // Glyph 133 | // A visual character shape (every codepoint is rendered as 134 | // some glyph) 135 | // 136 | // Glyph index 137 | // A font-specific integer ID representing a glyph 138 | // 139 | // Baseline 140 | // Glyph shapes are defined relative to a baseline, which is the 141 | // bottom of uppercase characters. Characters extend both above 142 | // and below the baseline. 143 | // 144 | // Current Point 145 | // As you draw text to the screen, you keep track of a "current point" 146 | // which is the origin of each character. The current point's vertical 147 | // position is the baseline. Even "baked fonts" use this model. 148 | // 149 | // Vertical Font Metrics 150 | // The vertical qualities of the font, used to vertically position 151 | // and space the characters. See docs for stbtt_GetFontVMetrics. 152 | // 153 | // Font Size in Pixels or Points 154 | // The preferred interface for specifying font sizes in stb_truetype 155 | // is to specify how tall the font's vertical extent should be in pixels. 156 | // If that sounds good enough, skip the next paragraph. 157 | // 158 | // Most font APIs instead use "points", which are a common typographic 159 | // measurement for describing font size, defined as 72 points per inch. 160 | // stb_truetype provides a point API for compatibility. However, true 161 | // "per inch" conventions don't make much sense on computer displays 162 | // since they different monitors have different number of pixels per 163 | // inch. For example, Windows traditionally uses a convention that 164 | // there are 96 pixels per inch, thus making 'inch' measurements have 165 | // nothing to do with inches, and thus effectively defining a point to 166 | // be 1.333 pixels. Additionally, the TrueType font data provides 167 | // an explicit scale factor to scale a given font's glyphs to points, 168 | // but the author has observed that this scale factor is often wrong 169 | // for non-commercial fonts, thus making fonts scaled in points 170 | // according to the TrueType spec incoherently sized in practice. 171 | // 172 | // ADVANCED USAGE 173 | // 174 | // Quality: 175 | // 176 | // - Use the functions with Subpixel at the end to allow your characters 177 | // to have subpixel positioning. Since the font is anti-aliased, not 178 | // hinted, this is very import for quality. (This is not possible with 179 | // baked fonts.) 180 | // 181 | // - Kerning is now supported, and if you're supporting subpixel rendering 182 | // then kerning is worth using to give your text a polished look. 183 | // 184 | // Performance: 185 | // 186 | // - Convert Unicode codepoints to glyph indexes and operate on the glyphs; 187 | // if you don't do this, stb_truetype is forced to do the conversion on 188 | // every call. 189 | // 190 | // - There are a lot of memory allocations. We should modify it to take 191 | // a temp buffer and allocate from the temp buffer (without freeing), 192 | // should help performance a lot. 193 | // 194 | // NOTES 195 | // 196 | // The system uses the raw data found in the .ttf file without changing it 197 | // and without building auxiliary data structures. This is a bit inefficient 198 | // on little-endian systems (the data is big-endian), but assuming you're 199 | // caching the bitmaps or glyph shapes this shouldn't be a big deal. 200 | // 201 | // It appears to be very hard to programmatically determine what font a 202 | // given file is in a general way. I provide an API for this, but I don't 203 | // recommend it. 204 | // 205 | // 206 | // SOURCE STATISTICS (based on v0.6c, 2050 LOC) 207 | // 208 | // Documentation & header file 520 LOC \___ 660 LOC documentation 209 | // Sample code 140 LOC / 210 | // Truetype parsing 620 LOC ---- 620 LOC TrueType 211 | // Software rasterization 240 LOC \ . 212 | // Curve tesselation 120 LOC \__ 550 LOC Bitmap creation 213 | // Bitmap management 100 LOC / 214 | // Baked bitmap interface 70 LOC / 215 | // Font name matching & access 150 LOC ---- 150 216 | // C runtime library abstraction 60 LOC ---- 60 217 | // 218 | // 219 | // PERFORMANCE MEASUREMENTS FOR 1.06: 220 | // 221 | // 32-bit 64-bit 222 | // Previous release: 8.83 s 7.68 s 223 | // Pool allocations: 7.72 s 6.34 s 224 | // Inline sort : 6.54 s 5.65 s 225 | // New rasterizer : 5.63 s 5.00 s 226 | 227 | ////////////////////////////////////////////////////////////////////////////// 228 | ////////////////////////////////////////////////////////////////////////////// 229 | //// 230 | //// SAMPLE PROGRAMS 231 | //// 232 | // 233 | // Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless 234 | // 235 | 236 | ////////////////////////////////////////////////////////////////////////////// 237 | ////////////////////////////////////////////////////////////////////////////// 238 | //// 239 | //// INTEGRATION WITH YOUR CODEBASE 240 | //// 241 | //// The following sections allow you to supply alternate definitions 242 | //// of C library functions used by stb_truetype. 243 | 244 | extern crate byteorder; 245 | extern crate libc; 246 | 247 | #[cfg(test)] 248 | #[macro_use(expect)] 249 | extern crate expectest; 250 | 251 | use std::ptr::{ null, null_mut }; 252 | use std::mem::size_of; 253 | use std::slice; 254 | use byteorder::{BigEndian, ByteOrder}; 255 | use libc::{ c_void, free, malloc, size_t, c_char }; 256 | use tables::{HHEA, HEAD, MAXP, HMTX, LOCA, CMAP, GLYF, GlyphData}; 257 | 258 | mod error; 259 | mod tables; 260 | mod types; 261 | mod utils; 262 | 263 | pub use error::Error; 264 | 265 | pub type Result = ::std::result::Result; 266 | 267 | // #define STBTT_ifloor(x) ((int) floor(x)) 268 | fn ifloor(x: f32) -> isize { 269 | x.floor() as isize 270 | } 271 | 272 | macro_rules! STBTT_malloc { 273 | ($x:expr) => { 274 | malloc($x) 275 | } 276 | } 277 | 278 | // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h 279 | // #define STBTT_malloc(x,u) ((void)(u),malloc(x)) 280 | 281 | macro_rules! STBTT_free { 282 | ($x:expr) => { 283 | free($x) 284 | } 285 | } 286 | // #define STBTT_free(x,u) ((void)(u),free(x)) 287 | 288 | macro_rules! STBTT_assert { 289 | ($x:expr) => { 290 | assert!($x) 291 | } 292 | } 293 | 294 | // #define STBTT_assert(x) assert(x) 295 | 296 | use libc::strlen as STBTT_strlen; 297 | 298 | // #define STBTT_strlen(x) strlen(x) 299 | 300 | use std::ptr::copy as STBTT_memcpy; 301 | 302 | // #define STBTT_memcpy memcpy 303 | 304 | // #define STBTT_memset memset 305 | 306 | fn memset(buf: *mut c_void, b: u8, count: usize) { 307 | let buf = buf as *mut u8; 308 | for idx in 0..count { 309 | unsafe { 310 | *buf.offset(idx as isize) = b; 311 | } 312 | } 313 | } 314 | 315 | /////////////////////////////////////////////////////////////////////////////// 316 | /////////////////////////////////////////////////////////////////////////////// 317 | //// 318 | //// INTERFACE 319 | //// 320 | //// 321 | 322 | ////////////////////////////////////////////////////////////////////////////// 323 | // 324 | // TEXTURE BAKING API 325 | // 326 | // If you use this API, you only have to call two functions ever. 327 | // 328 | 329 | pub struct BakedChar { 330 | // coordinates of bbox in bitmap 331 | x0: u16, 332 | y0: u16, 333 | x1: u16, 334 | y1: u16, 335 | xoff: f32, 336 | yoff: f32, 337 | xadvance: f32, 338 | } 339 | 340 | pub struct AlignedQuad { 341 | // top-left 342 | x0: f32, 343 | y0: f32, 344 | s0: f32, 345 | t0: f32, 346 | // bottom-right 347 | x1: f32, 348 | y1: f32, 349 | s1: f32, 350 | t1: f32, 351 | } 352 | 353 | ////////////////////////////////////////////////////////////////////////////// 354 | // 355 | // NEW TEXTURE BAKING API 356 | // 357 | // This provides options for packing multiple fonts into one atlas, not 358 | // perfectly but better than nothing. 359 | 360 | pub struct PackedChar { 361 | // coordinates of bbox in bitmap 362 | x0: u16, 363 | y0: u16, 364 | x1: u16, 365 | y1: u16, 366 | xoff: f32, 367 | yoff: f32, 368 | xadvance: f32, 369 | xoff2: f32, 370 | yoff2: f32, 371 | } 372 | 373 | // TODO: Macro 374 | // #define STBTT_POINT_SIZE(x) (-(x)) 375 | 376 | pub struct PackRange { 377 | font_size: f32, 378 | // if non-zero, then the chars are continuous, and this is the first codepoint 379 | first_unicode_codepoint_in_range: isize, 380 | // if non-zero, then this is an array of unicode codepoints 381 | array_of_unicode_codepoints: *const isize, 382 | num_chars: isize, 383 | // output 384 | chardata_for_range: *mut PackedChar, 385 | // don't set these, they're used internally 386 | h_oversample: u8, 387 | v_oversample: u8, 388 | } 389 | 390 | // this is an opaque structure that you shouldn't mess with which holds 391 | // all the context needed from PackBegin to PackEnd. 392 | pub struct PackContext { 393 | user_allocator_context: *const (), 394 | pack_info: *mut c_void, 395 | width: isize, 396 | height: isize, 397 | stride_in_bytes: isize, 398 | padding: isize, 399 | h_oversample: usize, 400 | v_oversample: usize, 401 | pixels: *mut u8, 402 | nodes: *mut c_void, 403 | } 404 | 405 | ////////////////////////////////////////////////////////////////////////////// 406 | // 407 | // FONT LOADING 408 | // 409 | // 410 | 411 | // The following structure is defined publically so you can declare one on 412 | // the stack or as a global or etc, but you should treat it as opaque. 413 | pub struct FontInfo<'a> { 414 | // pointer to .ttf file 415 | data: &'a [u8], 416 | // offset of start of font 417 | fontstart: usize, 418 | 419 | hhea: HHEA, 420 | head: HEAD, 421 | hmtx: HMTX, 422 | loca: LOCA, 423 | cmap: CMAP, 424 | glyf: GLYF, 425 | 426 | // table locations as offset from start of .ttf 427 | _glyf: usize, 428 | kern: usize, 429 | } 430 | 431 | impl<'a> FontInfo<'a> { 432 | // Given an offset into the file that defines a font, this function builds 433 | // the necessary cached info for the rest of the system. 434 | pub fn new_with_offset(data: &[u8], fontstart: usize) -> Result { 435 | use utils::{find_table_offset, find_required_table_offset}; 436 | 437 | let hhea = try!(HHEA::from_data(&data, 438 | try!(find_required_table_offset(data, fontstart, b"hhea")))); 439 | 440 | let head = try!(HEAD::from_data(&data, 441 | try!(find_required_table_offset(data, fontstart, b"head")))); 442 | 443 | let maxp = try!(MAXP::from_data(&data, 444 | try!(find_required_table_offset(data, fontstart, b"maxp")))); 445 | 446 | let hmtx = try!(HMTX::from_data(&data, 447 | try!(find_required_table_offset(data, fontstart, b"hmtx")), 448 | hhea.num_of_long_hor_metrics(), 449 | maxp.num_glyphs())); 450 | 451 | let loca = try!(LOCA::from_data(&data, 452 | try!(find_required_table_offset(data, fontstart, b"loca")), 453 | maxp.num_glyphs(), 454 | head.location_format())); 455 | 456 | let cmap = try!(CMAP::from_data(&data, 457 | try!(find_required_table_offset(data, fontstart, b"cmap")))); 458 | 459 | let _glyf = try!(find_required_table_offset(data, fontstart, b"glyf")); 460 | let glyf = try!(GLYF::from_data(&data, _glyf, loca.size_of_glyf_table())); 461 | 462 | let kern = try!(find_table_offset(data, fontstart, b"kern")).unwrap_or(0); 463 | 464 | let info = FontInfo { 465 | data: data, 466 | fontstart: fontstart, 467 | hhea: hhea, 468 | head: head, 469 | hmtx: hmtx, 470 | loca: loca, 471 | cmap: cmap, 472 | glyf: glyf, 473 | _glyf: _glyf, 474 | kern: kern, 475 | }; 476 | 477 | Ok(info) 478 | } 479 | 480 | // computes a scale factor to produce a font whose "height" is 'pixels' tall. 481 | // Height is measured as the distance from the highest ascender to the lowest 482 | // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics 483 | // and computing: 484 | // scale = pixels / (ascent - descent) 485 | // so if you prefer to measure height by the ascent only, use a similar calculation. 486 | pub fn scale_for_pixel_height(&self, height: f32) -> f32 { 487 | height / (self.hhea.ascent() - self.hhea.descent()) as f32 488 | } 489 | 490 | /// computes a scale factor to produce a font whose EM size is mapped to 491 | /// 'pixels' tall. This is probably what traditional APIs compute, but 492 | /// I'm not positive. 493 | pub fn scale_for_mapping_em_to_pixels(&self, pixels: f32) -> f32 { 494 | pixels / self.head.units_per_em() 495 | } 496 | 497 | /// Returns the offset to the location of the glyph in the font. 498 | /// 499 | /// Returns `None` if `i` is out of bounds or if the font does not contain 500 | /// an outline for the glyph at index `i`. 501 | pub fn offset_for_glyph_at_index(&self, i: usize) -> Option { 502 | self.loca.offset_for_glyph_at_index(i).map(|c| c + self._glyf) 503 | } 504 | 505 | /// Returns an index for character `code` in a `loca` font table. 506 | /// 507 | /// Returns 0 (special glyph representing a missing character) in other 508 | /// cases. 509 | pub fn glyph_index_for_code(&self, code: usize) -> usize { 510 | self.cmap.index_for_code(code).unwrap_or(0) 511 | } 512 | 513 | pub fn glyph_data_for_glyph_at_index(&self, i: usize) -> GlyphData { 514 | let offset = self.loca.offset_for_glyph_at_index(i).unwrap_or(0); 515 | self.glyf.glyph_data(offset) 516 | } 517 | } 518 | 519 | ////////////////////////////////////////////////////////////////////////////// 520 | // 521 | // CHARACTER TO GLYPH-INDEX CONVERSIOn 522 | 523 | ////////////////////////////////////////////////////////////////////////////// 524 | // 525 | // CHARACTER PROPERTIES 526 | // 527 | 528 | ////////////////////////////////////////////////////////////////////////////// 529 | // 530 | // GLYPH SHAPES (you probably don't need these, but they have to go before 531 | // the bitmaps for C declaration-order reasons) 532 | // 533 | 534 | #[derive(Eq, PartialEq, Copy, Clone)] 535 | pub enum Cmd { 536 | Move=1, 537 | Line=2, 538 | Curve=3 539 | } 540 | 541 | type VertexType = i16; 542 | #[derive(Copy, Clone)] 543 | pub struct Vertex { 544 | x: i16, 545 | y: i16, 546 | cx: i16, 547 | cy: i16, 548 | type_: Cmd, 549 | flags: u8, 550 | } 551 | 552 | // @TODO: don't expose this structure 553 | pub struct Bitmap 554 | { 555 | w: isize, 556 | h: isize, 557 | stride: isize, 558 | pixels: *mut u8, 559 | } 560 | 561 | ////////////////////////////////////////////////////////////////////////////// 562 | // 563 | // Finding the right font... 564 | // 565 | // You should really just solve this offline, keep your own tables 566 | // of what font is what, and don't try to get it out of the .ttf file. 567 | // That's because getting it out of the .ttf file is really hard, because 568 | // the names in the file can appear in many possible encodings, in many 569 | // possible languages, and e.g. if you need a case-insensitive comparison, 570 | // the details of that depend on the encoding & language in a complex way 571 | // (actually underspecified in truetype, but also gigantic). 572 | // 573 | // But you can use the provided functions in two possible ways: 574 | // stbtt_FindMatchingFont() will use *case-sensitive* comparisons on 575 | // unicode-encoded names to try to find the font you want; 576 | // you can run this before calling stbtt_InitFont() 577 | // 578 | // stbtt_GetFontNameString() lets you get any of the various strings 579 | // from the file yourself and do your own comparisons on them. 580 | // You have to have called stbtt_InitFont() first. 581 | 582 | // const STBTT_MACSTYLE_DONTCARE: u8 = 0; 583 | // const STBTT_MACSTYLE_BOLD: u8 = 1; 584 | // const STBTT_MACSTYLE_ITALIC: u8 = 2; 585 | // const STBTT_MACSTYLE_UNDERSCORE: u8 = 4; 586 | // const STBTT_MACSTYLE_NONE: u8 = 8; // <= not same as 0, this makes us check the bitfield is 0 587 | 588 | /* 589 | enum STBTT_MS_LANG { // language_id for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... 590 | // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs 591 | ENGLISH =0x0409, ITALIAN =0x0410, 592 | CHINESE =0x0804, JAPANESE =0x0411, 593 | DUTCH =0x0413, KOREAN =0x0412, 594 | FRENCH =0x040c, RUSSIAN =0x0419, 595 | GERMAN =0x0407, // TODO: Duplicate, SPANISH =0x0409, 596 | HEBREW =0x040d, SWEDISH =0x041D 597 | } 598 | */ 599 | 600 | /* 601 | enum STBTT_MAC_LANG { // language_id for STBTT_PLATFORM_ID_MAC 602 | ENGLISH =0 , JAPANESE =11, 603 | ARABIC =12, KOREAN =23, 604 | DUTCH =4 , RUSSIAN =32, 605 | FRENCH =1 , SPANISH =6 , 606 | GERMAN =2 , SWEDISH =5 , 607 | HEBREW =10, CHINESE_SIMPLIFIED =33, 608 | ITALIAN =3 , LANG_CHINESE_TRAD =19 609 | } 610 | */ 611 | 612 | /////////////////////////////////////////////////////////////////////////////// 613 | /////////////////////////////////////////////////////////////////////////////// 614 | //// 615 | //// IMPLEMENTATION 616 | //// 617 | //// 618 | 619 | // Can not be > 255. 620 | const STBTT_MAX_OVERSAMPLE: usize = 8; 621 | 622 | // const STBTT_RASTERIZER_VERSION: u8 = 2; 623 | 624 | ////////////////////////////////////////////////////////////////////////// 625 | // 626 | // accessors to parse data from file 627 | // 628 | 629 | // on platforms that don't allow misaligned reads, if we want to allow 630 | // truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE 631 | 632 | macro_rules! ttCHAR { 633 | ($p:expr) => { 634 | *($p as *const i8) 635 | } 636 | } 637 | 638 | // #define ttCHAR(p) (* (stbtt_int8 *) (p)) 639 | // TODO: Macro. 640 | // #define ttFixed(p) ttLONG(p) 641 | 642 | // TODO: Find out what is right to do with big or small endian. 643 | 644 | macro_rules! ttUSHORT { 645 | ($p:expr) => { 646 | BigEndian::read_u16(slice::from_raw_parts($p, 2)) 647 | } 648 | } 649 | 650 | macro_rules! ttSHORT { 651 | ($p:expr) => { 652 | BigEndian::read_i16(slice::from_raw_parts($p, 2)) 653 | } 654 | } 655 | 656 | macro_rules! ttULONG { 657 | ($p:expr) => { 658 | BigEndian::read_u32(slice::from_raw_parts($p, 4)) 659 | } 660 | } 661 | 662 | macro_rules! ttLONG { 663 | ($p:expr) => { 664 | BigEndian::read_i32(slice::from_raw_parts($p, 4)) 665 | } 666 | } 667 | 668 | macro_rules! stbtt_tag4 { 669 | ($p:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr) => { 670 | *$p.offset(0) == ($c0) && *$p.offset(1) == ($c1) && *$p.offset(2) == ($c2) && *$p.offset(3) == ($c3) 671 | } 672 | } 673 | 674 | // #define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) 675 | 676 | macro_rules! stbtt_tag { 677 | ($p:expr, $s:expr) => { 678 | stbtt_tag4!($p,*$s.offset(0),*$s.offset(1),*$s.offset(2),*$s.offset(3)) 679 | } 680 | } 681 | 682 | // #define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) 683 | 684 | pub unsafe fn isfont(font: *const u8) -> isize { 685 | // check the version number 686 | if stbtt_tag4!(font, '1' as u8,0,0,0) { return 1; } // TrueType 1 687 | if stbtt_tag!(font, "typ1".as_ptr()) { return 1; } // TrueType with type 1 font -- we don't support this! 688 | if stbtt_tag!(font, "OTTO".as_ptr()) { return 1; } // OpenType with CFF 689 | if stbtt_tag4!(font, 0,1,0,0) { return 1; } // OpenType 1.0 690 | return 0; 691 | } 692 | 693 | // Each .ttf/.ttc file may have more than one font. Each font has a sequential 694 | // index number starting from 0. Call this function to get the font offset for 695 | // a given index; it returns -1 if the index is out of range. A regular .ttf 696 | // file will only define one font and it always be at offset 0, so it will 697 | // return '0' for index 0, and -1 for all other indices. You can just skip 698 | // this step if you know it's that kind of font. 699 | pub unsafe fn get_font_offset_for_index( 700 | font_collection: *const u8, 701 | index: isize 702 | ) -> i32 { 703 | // if it's just a font, there's only one valid index 704 | if isfont(font_collection) != 0 { 705 | return if index == 0 { 0 } else { -1 }; 706 | } 707 | 708 | // check if it's a TTC 709 | if stbtt_tag!(font_collection, "ttcf".as_ptr()) { 710 | // version 1? 711 | if ttULONG!(font_collection.offset(4)) == 0x00010000 712 | || ttULONG!(font_collection.offset(4)) == 0x00020000 { 713 | let n: i32 = ttLONG!(font_collection.offset(8)); 714 | if index >= n as isize { 715 | return -1; 716 | } 717 | return ttULONG!(font_collection.offset(12+index*4)) as i32; 718 | } 719 | } 720 | return -1; 721 | } 722 | 723 | pub unsafe fn get_codepoint_shape( 724 | info: *const FontInfo, 725 | unicode_codepoint: isize, 726 | vertices: *mut *mut Vertex 727 | ) -> isize { 728 | assert!(unicode_codepoint >= 0); 729 | get_glyph_shape(info, (*info).glyph_index_for_code(unicode_codepoint as usize) as isize, vertices) 730 | } 731 | 732 | pub unsafe fn stbtt_setvertex( 733 | v: *mut Vertex, 734 | type_: Cmd, 735 | x: i32, 736 | y: i32, 737 | cx: i32, 738 | cy: i32 739 | ) { 740 | (*v).type_ = type_; 741 | (*v).x = x as i16; 742 | (*v).y = y as i16; 743 | (*v).cx = cx as i16; 744 | (*v).cy = cy as i16; 745 | } 746 | 747 | pub unsafe fn close_shape( 748 | vertices: *mut Vertex, 749 | mut num_vertices: isize, 750 | was_off: isize, 751 | start_off: isize, 752 | sx: i32, 753 | sy: i32, 754 | scx: i32, 755 | scy: i32, 756 | cx: i32, 757 | cy: i32 758 | ) -> isize { 759 | if start_off != 0 { 760 | if was_off != 0 { 761 | stbtt_setvertex(vertices.offset(num_vertices), 762 | Cmd::Curve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); 763 | num_vertices += 1; 764 | } 765 | stbtt_setvertex(vertices.offset(num_vertices), Cmd::Curve, sx,sy,scx,scy); 766 | num_vertices += 1; 767 | } else { 768 | if was_off != 0 { 769 | stbtt_setvertex(vertices.offset(num_vertices), Cmd::Curve,sx,sy,cx,cy); 770 | num_vertices += 1; 771 | } else { 772 | stbtt_setvertex(vertices.offset(num_vertices), Cmd::Line,sx,sy,0,0); 773 | num_vertices += 1; 774 | } 775 | } 776 | return num_vertices; 777 | } 778 | 779 | // returns # of vertices and fills *vertices with the pointer to them 780 | // these are expressed in "unscaled" coordinates 781 | // 782 | // The shape is a series of countours. Each one starts with 783 | // a STBTT_moveto, then consists of a series of mixed 784 | // STBTT_lineto and STBTT_curveto segments. A lineto 785 | // draws a line from previous endpoint to its x,y; a curveto 786 | // draws a quadratic bezier from previous endpoint to 787 | // its x,y, using cx,cy as the bezier control point. 788 | pub unsafe fn get_glyph_shape( 789 | info: *const FontInfo, 790 | glyph_index: isize, 791 | pvertices: *mut *mut Vertex 792 | ) -> isize { 793 | let number_of_contours: i16; 794 | let end_pts_of_contours: *const u8; 795 | let data: *const u8 = (*info).data.as_ptr(); 796 | let mut vertices: *mut Vertex=null_mut(); 797 | let mut num_vertices: isize =0; 798 | let g = (*info).offset_for_glyph_at_index(glyph_index as usize).map(|c| c as isize).unwrap_or(-1); 799 | 800 | *pvertices = null_mut(); 801 | 802 | if g < 0 { return 0; } 803 | 804 | number_of_contours = ttSHORT!(data.offset(g)); 805 | 806 | if number_of_contours > 0 { 807 | let mut flags: u8 =0; 808 | let mut flagcount: u8; 809 | let ins: i32; 810 | let mut j: i32 =0; 811 | let m: i32; 812 | let n: i32; 813 | let mut next_move: i32; 814 | let mut was_off: i32 =0; 815 | let off: i32; 816 | let mut start_off: i32 =0; 817 | let mut x: i32; 818 | let mut y: i32; 819 | let mut cx: i32; 820 | let mut cy: i32; 821 | let mut sx: i32; 822 | let mut sy: i32; 823 | let mut scx: i32; 824 | let mut scy: i32; 825 | let mut points: *const u8; 826 | end_pts_of_contours = data.offset(g + 10); 827 | ins = ttUSHORT!(data.offset(g + 10 + number_of_contours as isize * 2)) as i32; 828 | points = data.offset(g + 10 + number_of_contours as isize * 2 + 2 + ins as isize); 829 | 830 | n = 1+ttUSHORT!(end_pts_of_contours.offset(number_of_contours as isize *2-2)) as i32; 831 | 832 | m = n + 2*number_of_contours as i32; // a loose bound on how many vertices we might need 833 | vertices = STBTT_malloc!(m as usize * size_of::()) as *mut Vertex; 834 | if vertices == null_mut() { 835 | return 0; 836 | } 837 | 838 | next_move = 0; 839 | flagcount=0; 840 | 841 | // in first pass, we load uninterpreted data into the allocated array 842 | // above, shifted to the end of the array so we won't overwrite it when 843 | // we create our final data starting from the front 844 | 845 | off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated 846 | 847 | // first load flags 848 | 849 | for i in 0..n { 850 | if flagcount == 0 { 851 | flags = *points; 852 | points = points.offset(1); 853 | if (flags & 8) != 0 { 854 | flagcount = *points; 855 | points = points.offset(1); 856 | } 857 | } else { 858 | flagcount -= 1; 859 | } 860 | (*vertices.offset(off as isize +i as isize)).flags = flags; 861 | } 862 | // now load x coordinates 863 | x=0; 864 | for i in 0..n { 865 | flags = (*vertices.offset(off as isize + i as isize)).flags; 866 | if (flags & 2) != 0 { 867 | let dx: i16 = *points as i16; 868 | points = points.offset(1); 869 | x += if (flags & 16) != 0 { dx as i32 } else { -dx as i32 }; // ??? 870 | } else { 871 | if (flags & 16) == 0 { 872 | x = x + BigEndian::read_i16(slice::from_raw_parts(points, 2)) as i32; 873 | points = points.offset(2); 874 | } 875 | } 876 | (*vertices.offset(off as isize +i as isize)).x = x as i16; 877 | } 878 | 879 | // now load y coordinates 880 | y=0; 881 | for i in 0..n { 882 | flags = (*vertices.offset(off as isize + i as isize)).flags; 883 | if (flags & 4) != 0 { 884 | let dy: i16 = *points as i16; 885 | points = points.offset(1); 886 | y += if (flags & 32) != 0 { dy as i32 } else { -dy as i32 }; // ??? 887 | } else { 888 | if (flags & 32) == 0 { 889 | y = y + BigEndian::read_i16(slice::from_raw_parts(points, 2)) as i32; 890 | points = points.offset(2); 891 | } 892 | } 893 | (*vertices.offset(off as isize +i as isize)).y = y as i16; 894 | } 895 | 896 | // now convert them to our format 897 | num_vertices=0; 898 | sx = 0; sy = 0; 899 | cx = 0; cy = 0; 900 | scx = 0; scy = 0; 901 | let mut i_iter = (0..n).into_iter(); 902 | let mut i = 0; 903 | while { if let Some(v) = i_iter.next() { i = v; true } else { false } } { 904 | flags = (*vertices.offset(off as isize +i as isize)).flags; 905 | x = (*vertices.offset(off as isize +i as isize)).x as i32; 906 | y = (*vertices.offset(off as isize +i as isize)).y as i32; 907 | if next_move == i { 908 | if i != 0 { 909 | num_vertices = close_shape(vertices, 910 | num_vertices, was_off as isize, start_off as isize, sx,sy,scx,scy,cx,cy); 911 | } 912 | 913 | // now start the new one 914 | start_off = (1 - (flags & 1)) as i32; 915 | if start_off != 0 { 916 | // if we start off with an off-curve point, then when we need to find a point on the curve 917 | // where we can start, and we need to save some state for when we wraparound. 918 | scx = x; 919 | scy = y; 920 | if (*vertices.offset(off as isize +i as isize +1)).type_ == Cmd::Line { 921 | // next point is also a curve point, so interpolate an on-point curve 922 | sx = (x + (*vertices.offset(off as isize +i as isize +1)).x as i32) >> 1; 923 | sy = (y + (*vertices.offset(off as isize +i as isize +1)).y as i32) >> 1; 924 | } else { 925 | // otherwise just use the next point as our start point 926 | sx = (*vertices.offset(off as isize +i as isize +1)).x as i32; 927 | sy = (*vertices.offset(off as isize +i as isize +1)).y as i32; 928 | i_iter.next(); // we're using point i+1 as the starting point, so skip it 929 | } 930 | } else { 931 | sx = x; 932 | sy = y; 933 | } 934 | stbtt_setvertex(vertices.offset(num_vertices), Cmd::Move,sx,sy,0,0); 935 | num_vertices += 1; 936 | was_off = 0; 937 | next_move = 1 + ttUSHORT!(end_pts_of_contours.offset(j as isize *2)) as i32; 938 | j += 1; 939 | } else { 940 | if (flags & 1) == 0 { // if it's a curve 941 | if was_off != 0 { // two off-curve control points in a row means interpolate an on-curve midpoint 942 | stbtt_setvertex(vertices.offset(num_vertices), 943 | Cmd::Curve, (cx+x)>>1, (cy+y)>>1, cx, cy); 944 | num_vertices += 1; 945 | } 946 | cx = x; 947 | cy = y; 948 | was_off = 1; 949 | } else { 950 | if was_off != 0 { 951 | stbtt_setvertex(vertices.offset(num_vertices), Cmd::Curve, x,y, cx, cy); 952 | num_vertices += 1; 953 | } else { 954 | stbtt_setvertex(vertices.offset(num_vertices), Cmd::Line, x,y,0,0); 955 | num_vertices += 1; 956 | } 957 | was_off = 0; 958 | } 959 | } 960 | } 961 | num_vertices = close_shape(vertices, num_vertices, was_off as isize, start_off as isize, sx,sy,scx,scy,cx,cy); 962 | } else if number_of_contours == -1 { 963 | // Compound shapes. 964 | let mut more: isize = 1; 965 | let mut comp: *const u8 = data.offset(g + 10); 966 | num_vertices = 0; 967 | vertices = null_mut(); 968 | while more != 0 { 969 | let flags: u16; 970 | let gidx: u16; 971 | let comp_num_verts: isize; 972 | let mut comp_verts: *mut Vertex = null_mut(); 973 | let tmp: *mut Vertex; 974 | let mut mtx: [f32; 6] = [1.0,0.0,0.0,1.0,0.0,0.0]; 975 | let m: f32; 976 | let n: f32; 977 | 978 | flags = ttSHORT!(comp) as u16; comp=comp.offset(2); 979 | gidx = ttSHORT!(comp) as u16; comp=comp.offset(2); 980 | 981 | if (flags & 2) != 0 { // XY values 982 | if (flags & 1) != 0 { // shorts 983 | mtx[4] = ttSHORT!(comp) as f32; comp=comp.offset(2); 984 | mtx[5] = ttSHORT!(comp) as f32; comp=comp.offset(2); 985 | } else { 986 | mtx[4] = ttCHAR!(comp) as f32; comp=comp.offset(1); 987 | mtx[5] = ttCHAR!(comp) as f32; comp=comp.offset(1); 988 | } 989 | } 990 | else { 991 | // @TODO handle matching point 992 | unimplemented!(); 993 | } 994 | if (flags & (1<<3)) != 0 { // WE_HAVE_A_SCALE 995 | let v = ttSHORT!(comp) as f32 /16384.0; comp=comp.offset(2); 996 | mtx[0] = v; 997 | mtx[3] = v; 998 | mtx[1] = 0.0; 999 | mtx[2] = 0.0; 1000 | } else if (flags & (1<<6)) != 0 { // WE_HAVE_AN_X_AND_YSCALE 1001 | mtx[0] = ttSHORT!(comp) as f32 /16384.0; comp=comp.offset(2); 1002 | mtx[1] = 0.0; 1003 | mtx[2] = 0.0; 1004 | mtx[3] = ttSHORT!(comp) as f32 /16384.0; comp=comp.offset(2); 1005 | } else if (flags & (1<<7)) != 0 { // WE_HAVE_A_TWO_BY_TWO 1006 | mtx[0] = ttSHORT!(comp) as f32 /16384.0; comp=comp.offset(2); 1007 | mtx[1] = ttSHORT!(comp) as f32 /16384.0; comp=comp.offset(2); 1008 | mtx[2] = ttSHORT!(comp) as f32 /16384.0; comp=comp.offset(2); 1009 | mtx[3] = ttSHORT!(comp) as f32 /16384.0; comp=comp.offset(2); 1010 | } 1011 | 1012 | // Find transformation scales. 1013 | m = (mtx[0]*mtx[0] + mtx[1]*mtx[1]).sqrt(); 1014 | n = (mtx[2]*mtx[2] + mtx[3]*mtx[3]).sqrt(); 1015 | 1016 | // Get indexed glyph. 1017 | comp_num_verts = get_glyph_shape(info, gidx as isize, &mut comp_verts); 1018 | if comp_num_verts > 0 { 1019 | // Transform vertices. 1020 | for i in 0..comp_num_verts { 1021 | let v: *mut Vertex = comp_verts.offset(i); 1022 | let mut x: VertexType; 1023 | let mut y: VertexType; 1024 | x=(*v).x; y=(*v).y; 1025 | (*v).x = (m as f32 * (mtx[0]*x as f32 + mtx[2]*y as f32 + mtx[4])) as VertexType; 1026 | (*v).y = (n as f32 * (mtx[1]*x as f32 + mtx[3]*y as f32 + mtx[5])) as VertexType; 1027 | x=(*v).cx; y=(*v).cy; 1028 | (*v).cx = (m as f32 * (mtx[0]*x as f32 + mtx[2]*y as f32 + mtx[4])) as VertexType; 1029 | (*v).cy = (n as f32 * (mtx[1]*x as f32 + mtx[3]*y as f32 + mtx[5])) as VertexType; 1030 | } 1031 | // Append vertices. 1032 | tmp = STBTT_malloc!((num_vertices+comp_num_verts) as usize *size_of::()) 1033 | as *mut Vertex; 1034 | if tmp == null_mut() { 1035 | if vertices != null_mut() { STBTT_free!(vertices as *mut c_void); } 1036 | if comp_verts != null_mut() { STBTT_free!(comp_verts as *mut c_void); } 1037 | return 0; 1038 | } 1039 | if num_vertices > 0 { 1040 | STBTT_memcpy(tmp, vertices, 1041 | num_vertices as usize *size_of::()); 1042 | } 1043 | STBTT_memcpy(tmp.offset(num_vertices), comp_verts, 1044 | comp_num_verts as usize *size_of::()); 1045 | if vertices != null_mut() { STBTT_free!(vertices as *mut c_void); } 1046 | vertices = tmp; 1047 | STBTT_free!(comp_verts as *mut c_void); 1048 | num_vertices += comp_num_verts; 1049 | } 1050 | // More components ? 1051 | more = (flags & (1<<5)) as isize; 1052 | } 1053 | } else if number_of_contours < 0 { 1054 | // @TODO other compound variations? 1055 | unimplemented!(); 1056 | } else { 1057 | // numberOfCounters == 0, do nothing 1058 | } 1059 | 1060 | *pvertices = vertices; 1061 | return num_vertices; 1062 | } 1063 | 1064 | pub unsafe fn get_glyph_kern_advance( 1065 | info: *mut FontInfo, 1066 | glyph1: isize, 1067 | glyph2: isize 1068 | ) -> isize { 1069 | let data: *const u8 = (*info).data.as_ptr().offset((*info).kern as isize); 1070 | let needle: u32; 1071 | let mut straw: u32; 1072 | let mut l: isize; 1073 | let mut r: isize; 1074 | let mut m: isize; 1075 | 1076 | // we only look at the first table. it must be 'horizontal' and format 0. 1077 | if (*info).kern == 0 { 1078 | return 0; 1079 | } 1080 | if ttUSHORT!(data.offset(2)) < 1 { // number of tables, need at least 1 1081 | return 0; 1082 | } 1083 | if ttUSHORT!(data.offset(8)) != 1 { // horizontal flag must be set in format 1084 | return 0; 1085 | } 1086 | 1087 | l = 0; 1088 | r = ttUSHORT!(data.offset(10)) as isize - 1; 1089 | needle = (glyph1 << 16 | glyph2) as u32; 1090 | while l <= r { 1091 | m = (l + r) >> 1; 1092 | straw = ttULONG!(data.offset(18+(m*6))); // note: unaligned read 1093 | if needle < straw { 1094 | r = m - 1; 1095 | } 1096 | else if needle > straw { 1097 | l = m + 1; 1098 | } else { 1099 | return ttSHORT!(data.offset(22+(m*6))) as isize; 1100 | } 1101 | } 1102 | return 0; 1103 | } 1104 | 1105 | // an additional amount to add to the 'advance' value between ch1 and ch2 1106 | pub unsafe fn get_codepoint_kern_advance( 1107 | info: *mut FontInfo, 1108 | ch1: isize, 1109 | ch2: isize 1110 | ) -> isize { 1111 | if (*info).kern == 0 { // if no kerning table, don't waste time looking up both codepoint->glyphs 1112 | return 0; 1113 | } 1114 | assert!(ch1 >= 0 && ch2 >= 0); 1115 | let i1 = (*info).glyph_index_for_code(ch1 as usize) as isize; 1116 | let i2 = (*info).glyph_index_for_code(ch2 as usize) as isize; 1117 | get_glyph_kern_advance(info, i1, i2) 1118 | } 1119 | 1120 | // frees the data allocated above 1121 | 1122 | ////////////////////////////////////////////////////////////////////////////// 1123 | // 1124 | // BITMAP RENDERING 1125 | // 1126 | pub unsafe fn free_shape(_info: *const FontInfo, v: *mut Vertex) 1127 | { 1128 | STBTT_free!(v as *mut c_void); 1129 | } 1130 | 1131 | ////////////////////////////////////////////////////////////////////////////// 1132 | // 1133 | // antialiasing software rasterizer 1134 | // 1135 | ////////////////////////////////////////////////////////////////////////////// 1136 | // 1137 | // Rasterizer 1138 | 1139 | struct HheapChunk { 1140 | next: *mut HheapChunk 1141 | } 1142 | 1143 | pub struct Hheap 1144 | { 1145 | head: *mut HheapChunk, 1146 | first_free: *mut (), 1147 | num_remaining_in_head_chunk: isize, 1148 | } 1149 | 1150 | pub unsafe fn hheap_alloc( 1151 | hh: *mut Hheap, 1152 | size: size_t 1153 | ) -> *const () { 1154 | if (*hh).first_free != null_mut() { 1155 | let p: *mut () = (*hh).first_free; 1156 | (*hh).first_free = *(p as *mut *mut ()); 1157 | return p; 1158 | } else { 1159 | if (*hh).num_remaining_in_head_chunk == 0 { 1160 | let count: isize = if size < 32 { 1161 | 2000 1162 | } else { 1163 | if size < 128 { 800 } else { 100 } 1164 | }; 1165 | let c: *mut HheapChunk = STBTT_malloc!( 1166 | size_of::() + size * count as usize) 1167 | as *mut HheapChunk; 1168 | if c == null_mut() { 1169 | return null(); 1170 | } 1171 | (*c).next = (*hh).head; 1172 | (*hh).head = c; 1173 | (*hh).num_remaining_in_head_chunk = count; 1174 | } 1175 | (*hh).num_remaining_in_head_chunk -= 1; 1176 | return ((*hh).head as *const u8).offset(size as isize * (*hh).num_remaining_in_head_chunk) 1177 | as *const (); 1178 | } 1179 | } 1180 | 1181 | pub unsafe fn hheap_free(hh: *mut Hheap, p: *mut ()) { 1182 | *(p as *mut *mut ()) = (*hh).first_free; 1183 | (*hh).first_free = p; 1184 | } 1185 | 1186 | pub unsafe fn hheap_cleanup(hh: *mut Hheap) { 1187 | let mut c: *mut HheapChunk = (*hh).head; 1188 | while c != null_mut() { 1189 | let n: *mut HheapChunk = (*c).next; 1190 | STBTT_free!(c as *mut c_void); 1191 | c = n; 1192 | } 1193 | } 1194 | 1195 | #[derive(Copy, Clone)] 1196 | pub struct Edge { 1197 | x0: f32, 1198 | y0: f32, 1199 | x1: f32, 1200 | y1: f32, 1201 | invert: isize, 1202 | } 1203 | 1204 | pub struct ActiveEdge { 1205 | next: *mut ActiveEdge, 1206 | // TODO: Conditional compilation. 1207 | // #if STBTT_RASTERIZER_VERSION==1 1208 | // int x,dx; 1209 | // float ey; 1210 | // int direction; 1211 | // #elif STBTT_RASTERIZER_VERSION==2 1212 | fx: f32, 1213 | fdx: f32, 1214 | fdy: f32, 1215 | direction: f32, 1216 | sy: f32, 1217 | ey: f32, 1218 | // #else 1219 | // #error "Unrecognized value of STBTT_RASTERIZER_VERSION" 1220 | // #endif 1221 | } 1222 | 1223 | // TODO: Conditional compilation. 1224 | // #if STBTT_RASTERIZER_VERSION == 1 1225 | // #define STBTT_FIXSHIFT 10 1226 | // #define STBTT_FIX (1 << STBTT_FIXSHIFT) 1227 | // #define STBTT_FIXMASK (STBTT_FIX-1) 1228 | 1229 | /* 1230 | static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point) 1231 | { 1232 | stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z)); 1233 | float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 1234 | if (!z) return z; 1235 | 1236 | // round dx down to avoid overshooting 1237 | if (dxdy < 0) 1238 | z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); 1239 | else 1240 | z->dx = STBTT_ifloor(STBTT_FIX * dxdy); 1241 | 1242 | z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount 1243 | z->x -= off_x * STBTT_FIX; 1244 | 1245 | z->ey = e->y1; 1246 | z->next = 0; 1247 | z->direction = e->invert ? 1 : -1; 1248 | return z; 1249 | } 1250 | */ 1251 | // #elif STBTT_RASTERIZER_VERSION == 2 1252 | pub unsafe fn new_active( 1253 | hh: *mut Hheap, 1254 | e: *mut Edge, 1255 | off_x: isize, 1256 | start_point: f32 1257 | ) -> *mut ActiveEdge { 1258 | let z: *mut ActiveEdge = hheap_alloc( 1259 | hh, size_of::()) 1260 | as *mut ActiveEdge; 1261 | let dxdy: f32 = ((*e).x1 - (*e).x0) / ((*e).y1 - (*e).y0); 1262 | //STBTT_assert(e->y0 <= start_point); 1263 | if z == null_mut() { return z; } 1264 | (*z).fdx = dxdy; 1265 | (*z).fdy = if dxdy != 0.0 { 1.0/dxdy } else { 0.0 }; 1266 | (*z).fx = (*e).x0 + dxdy * (start_point - (*e).y0); 1267 | (*z).fx -= off_x as f32; 1268 | (*z).direction = if (*e).invert != 0 { 1.0 } else { -1.0 }; 1269 | (*z).sy = (*e).y0; 1270 | (*z).ey = (*e).y1; 1271 | (*z).next = null_mut(); 1272 | return z; 1273 | } 1274 | // #else 1275 | // #error "Unrecognized value of STBTT_RASTERIZER_VERSION" 1276 | // #endif 1277 | 1278 | // TODO: Conditional compilation. 1279 | /* 1280 | #if STBTT_RASTERIZER_VERSION == 1 1281 | // note: this routine clips fills that extend off the edges... ideally this 1282 | // wouldn't happen, but it could happen if the truetype glyph bounding boxes 1283 | // are wrong, or if the user supplies a too-small bitmap 1284 | static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) 1285 | { 1286 | // non-zero winding fill 1287 | int x0=0, w=0; 1288 | 1289 | while (e) { 1290 | if (w == 0) { 1291 | // if we're currently at zero, we need to record the edge start point 1292 | x0 = e->x; w += e->direction; 1293 | } else { 1294 | int x1 = e->x; w += e->direction; 1295 | // if we went to zero, we need to draw 1296 | if (w == 0) { 1297 | int i = x0 >> STBTT_FIXSHIFT; 1298 | int j = x1 >> STBTT_FIXSHIFT; 1299 | 1300 | if (i < len && j >= 0) { 1301 | if (i == j) { 1302 | // x0,x1 are the same pixel, so compute combined coverage 1303 | scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); 1304 | } else { 1305 | if (i >= 0) // add antialiasing for x0 1306 | scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); 1307 | else 1308 | i = -1; // clip 1309 | 1310 | if (j < len) // add antialiasing for x1 1311 | scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); 1312 | else 1313 | j = len; // clip 1314 | 1315 | for (++i; i < j; ++i) // fill pixels between x0 and x1 1316 | scanline[i] = scanline[i] + (stbtt_uint8) max_weight; 1317 | } 1318 | } 1319 | } 1320 | } 1321 | 1322 | e = e->next; 1323 | } 1324 | } 1325 | 1326 | static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y) 1327 | { 1328 | stbtt__hheap hh = { 0, 0, 0 }; 1329 | stbtt__active_edge *active = NULL; 1330 | int y,j=0; 1331 | int max_weight = (255 / vsubsample); // weight per vertical scanline 1332 | int s; // vertical subsample index 1333 | unsigned char scanline_data[512], *scanline; 1334 | 1335 | if (result->w > 512) 1336 | scanline = (unsigned char *) STBTT_malloc(result->w); 1337 | else 1338 | scanline = scanline_data; 1339 | 1340 | y = off_y * vsubsample; 1341 | e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; 1342 | 1343 | while (j < result->h) { 1344 | STBTT_memset(scanline, 0, result->w); 1345 | for (s=0; s < vsubsample; ++s) { 1346 | // find center of pixel for this scanline 1347 | float scan_y = y + 0.5f; 1348 | stbtt__active_edge **step = &active; 1349 | 1350 | // update all active edges; 1351 | // remove all active edges that terminate before the center of this scanline 1352 | while (*step) { 1353 | stbtt__active_edge * z = *step; 1354 | if (z->ey <= scan_y) { 1355 | *step = z->next; // delete from list 1356 | STBTT_assert(z->direction); 1357 | z->direction = 0; 1358 | stbtt__hheap_free(&hh, z); 1359 | } else { 1360 | z->x += z->dx; // advance to position for current scanline 1361 | step = &((*step)->next); // advance through list 1362 | } 1363 | } 1364 | 1365 | // resort the list if needed 1366 | for(;;) { 1367 | int changed=0; 1368 | step = &active; 1369 | while (*step && (*step)->next) { 1370 | if ((*step)->x > (*step)->next->x) { 1371 | stbtt__active_edge *t = *step; 1372 | stbtt__active_edge *q = t->next; 1373 | 1374 | t->next = q->next; 1375 | q->next = t; 1376 | *step = q; 1377 | changed = 1; 1378 | } 1379 | step = &(*step)->next; 1380 | } 1381 | if (!changed) break; 1382 | } 1383 | 1384 | // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline 1385 | while (e->y0 <= scan_y) { 1386 | if (e->y1 > scan_y) { 1387 | stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y); 1388 | // find insertion point 1389 | if (active == NULL) 1390 | active = z; 1391 | else if (z->x < active->x) { 1392 | // insert at front 1393 | z->next = active; 1394 | active = z; 1395 | } else { 1396 | // find thing to insert AFTER 1397 | stbtt__active_edge *p = active; 1398 | while (p->next && p->next->x < z->x) 1399 | p = p->next; 1400 | // at this point, p->next->x is NOT < z->x 1401 | z->next = p->next; 1402 | p->next = z; 1403 | } 1404 | } 1405 | ++e; 1406 | } 1407 | 1408 | // now process all active edges in XOR fashion 1409 | if (active) 1410 | stbtt__fill_active_edges(scanline, result->w, active, max_weight); 1411 | 1412 | ++y; 1413 | } 1414 | STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); 1415 | ++j; 1416 | } 1417 | 1418 | stbtt__hheap_cleanup(&hh); 1419 | 1420 | if (scanline != scanline_data) 1421 | STBTT_free(scanline); 1422 | } 1423 | */ 1424 | // #elif STBTT_RASTERIZER_VERSION == 2 1425 | 1426 | // the edge passed in here does not cross the vertical line at x or the vertical line at x+1 1427 | // (i.e. it has already been clipped to those) 1428 | pub unsafe fn handle_clipped_edge( 1429 | scanline: *mut f32, 1430 | x: isize, 1431 | e: *mut ActiveEdge, 1432 | mut x0: f32, 1433 | mut y0: f32, 1434 | mut x1: f32, 1435 | mut y1: f32 1436 | ) { 1437 | if y0 == y1 { return; } 1438 | STBTT_assert!(y0 < y1); 1439 | STBTT_assert!((*e).sy <= (*e).ey); 1440 | if y0 > (*e).ey { return; } 1441 | if y1 < (*e).sy { return; } 1442 | if y0 < (*e).sy { 1443 | x0 += (x1-x0) * ((*e).sy - y0) / (y1-y0); 1444 | y0 = (*e).sy; 1445 | } 1446 | if y1 > (*e).ey { 1447 | x1 += (x1-x0) * ((*e).ey - y1) / (y1-y0); 1448 | y1 = (*e).ey; 1449 | } 1450 | 1451 | if x0 == x as f32 { 1452 | STBTT_assert!(x1 <= x as f32 +1.0); 1453 | } 1454 | else if x0 == x as f32 +1.0 { 1455 | STBTT_assert!(x1 >= x as f32); 1456 | } 1457 | else if x0 <= x as f32 { 1458 | STBTT_assert!(x1 <= x as f32); 1459 | } 1460 | else if x0 >= x as f32 +1.0 { 1461 | STBTT_assert!(x1 >= x as f32 +1.0); 1462 | } 1463 | else { 1464 | STBTT_assert!(x1 >= x as f32 && x1 <= x as f32 +1.0); 1465 | } 1466 | 1467 | if x0 <= x as f32 && x1 <= x as f32 { 1468 | *scanline.offset(x) += (*e).direction * (y1-y0); 1469 | } 1470 | else if x0 >= x as f32 +1.0 && x1 >= x as f32 +1.0 {} 1471 | else { 1472 | STBTT_assert!(x0 >= x as f32 && x0 <= x as f32 +1.0 && x1 >= x as f32 && x1 <= x as f32 +1.0); 1473 | *scanline.offset(x) += (*e).direction * (y1-y0) * (1.0-((x0-x as f32)+(x1-x as f32))/2.0); // coverage = 1 - average x position 1474 | } 1475 | } 1476 | 1477 | pub unsafe fn fill_active_edges_new( 1478 | scanline: *mut f32, 1479 | scanline_fill: *mut f32, 1480 | len: isize, 1481 | mut e: *mut ActiveEdge, 1482 | y_top: f32 1483 | ) { 1484 | let y_bottom: f32 = y_top+1.0; 1485 | 1486 | while e != null_mut() { 1487 | // brute force every pixel 1488 | 1489 | // compute intersection points with top & bottom 1490 | STBTT_assert!((*e).ey >= y_top); 1491 | 1492 | if (*e).fdx == 0.0 { 1493 | let x0: f32 = (*e).fx; 1494 | if x0 < len as f32 { 1495 | if x0 >= 0.0 { 1496 | handle_clipped_edge(scanline,x0 as isize,e, x0,y_top, x0,y_bottom); 1497 | handle_clipped_edge(scanline_fill.offset(-1),x0 as isize +1,e, x0,y_top, x0,y_bottom); 1498 | } else { 1499 | handle_clipped_edge(scanline_fill.offset(-1),0,e, x0,y_top, x0,y_bottom); 1500 | } 1501 | } 1502 | } else { 1503 | let mut x0: f32 = (*e).fx; 1504 | let dx: f32 = (*e).fdx; 1505 | let xb: f32 = x0 + dx; 1506 | let mut x_top: f32; 1507 | let mut x_bottom: f32; 1508 | let mut sy0: f32; 1509 | let mut sy1: f32; 1510 | let mut dy: f32 = (*e).fdy; 1511 | STBTT_assert!((*e).sy <= y_bottom && (*e).ey >= y_top); 1512 | 1513 | // compute endpoints of line segment clipped to this scanline (if the 1514 | // line segment starts on this scanline. x0 is the intersection of the 1515 | // line with y_top, but that may be off the line segment. 1516 | if (*e).sy > y_top { 1517 | x_top = x0 + dx * ((*e).sy - y_top); 1518 | sy0 = (*e).sy; 1519 | } else { 1520 | x_top = x0; 1521 | sy0 = y_top; 1522 | } 1523 | if (*e).ey < y_bottom { 1524 | x_bottom = x0 + dx * ((*e).ey - y_top); 1525 | sy1 = (*e).ey; 1526 | } else { 1527 | x_bottom = xb; 1528 | sy1 = y_bottom; 1529 | } 1530 | 1531 | if x_top >= 0.0 1532 | && x_bottom >= 0.0 1533 | && x_top < len as f32 1534 | && x_bottom < len as f32 { 1535 | // from here on, we don't have to range check x values 1536 | 1537 | if x_top as isize == x_bottom as isize { 1538 | let height: f32; 1539 | // simple case, only spans one pixel 1540 | let x = x_top as isize; 1541 | height = sy1 - sy0; 1542 | STBTT_assert!(x >= 0 && x < len); 1543 | *scanline.offset(x) += (*e).direction * (1.0-((x_top - x as f32) + (x_bottom-x as f32))/2.0) * height; 1544 | *scanline_fill.offset(x) += (*e).direction * height; // everything right of this pixel is filled 1545 | } else { 1546 | let x1: isize; 1547 | let x2: isize; 1548 | let mut y_crossing: f32; 1549 | let step: f32; 1550 | let sign: f32; 1551 | let mut area: f32; 1552 | // covers 2+ pixels 1553 | if x_top > x_bottom { 1554 | // flip scanline vertically; signed area is the same 1555 | let mut t: f32; 1556 | sy0 = y_bottom - (sy0 - y_top); 1557 | sy1 = y_bottom - (sy1 - y_top); 1558 | t = sy0; 1559 | sy0 = sy1; 1560 | sy1 = t; 1561 | t = x_bottom; 1562 | x_bottom = x_top; 1563 | x_top = t; 1564 | dy = -dy; 1565 | x0 = xb; 1566 | } 1567 | 1568 | x1 = x_top as isize; 1569 | x2 = x_bottom as isize; 1570 | // compute intersection with y axis at x1+1 1571 | y_crossing = (x1 as f32 +1.0 - x0) * dy + y_top; 1572 | 1573 | sign = (*e).direction; 1574 | // area of the rectangle covered from y0..y_crossing 1575 | area = sign * (y_crossing-sy0); 1576 | // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) 1577 | (*scanline.offset(x1)) += area * (1.0-((x_top - x1 as f32)+(x1+1-x1) as f32)/2.0); 1578 | 1579 | step = sign * dy; 1580 | for x in x1 + 1..x2 { 1581 | (*scanline.offset(x)) += area + step/2.0; 1582 | area += step; 1583 | } 1584 | y_crossing += dy * (x2 - (x1+1)) as f32; 1585 | 1586 | STBTT_assert!(area.abs() <= 1.01); 1587 | 1588 | (*scanline.offset(x2)) += area + sign * (1.0-((x2-x2) as f32 1589 | +(x_bottom-x2 as f32))/2.0) * (sy1-y_crossing); 1590 | 1591 | (*scanline_fill.offset(x2)) += sign * (sy1-sy0); 1592 | } 1593 | } else { 1594 | // if edge goes outside of box we're drawing, we require 1595 | // clipping logic. since this does not match the intended use 1596 | // of this library, we use a different, very slow brute 1597 | // force implementation 1598 | for x in 0..len { 1599 | // cases: 1600 | // 1601 | // there can be up to two intersections with the pixel. any intersection 1602 | // with left or right edges can be handled by splitting into two (or three) 1603 | // regions. intersections with top & bottom do not necessitate case-wise logic. 1604 | // 1605 | // the old way of doing this found the intersections with the left & right edges, 1606 | // then used some simple logic to produce up to three segments in sorted order 1607 | // from top-to-bottom. however, this had a problem: if an x edge was epsilon 1608 | // across the x border, then the corresponding y position might not be distinct 1609 | // from the other y segment, and it might ignored as an empty segment. to avoid 1610 | // that, we need to explicitly produce segments based on x positions. 1611 | 1612 | // rename variables to clear pairs 1613 | let y0: f32 = y_top; 1614 | let x1: f32 = x as f32; 1615 | let x2: f32 = x as f32 +1.0 as f32; 1616 | let x3: f32 = xb; 1617 | let y3: f32 = y_bottom; 1618 | let y1: f32; 1619 | let y2: f32; 1620 | 1621 | // x = e->x + e->dx * (y-y_top) 1622 | // (y-y_top) = (x - e->x) / e->dx 1623 | // y = (x - e->x) / e->dx + y_top 1624 | y1 = (x as f32 - x0) / dx + y_top; 1625 | y2 = (x as f32+1.0 - x0) / dx + y_top; 1626 | 1627 | if x0 < x1 && x3 > x2 { // three segments descending down-right 1628 | handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 1629 | handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); 1630 | handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 1631 | } else if x3 < x1 && x0 > x2 { // three segments descending down-left 1632 | handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 1633 | handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); 1634 | handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 1635 | } else if x0 < x1 && x3 > x1 { // two segments across x, down-right 1636 | handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 1637 | handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 1638 | } else if x3 < x1 && x0 > x1 { // two segments across x, down-left 1639 | handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); 1640 | handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); 1641 | } else if x0 < x2 && x3 > x2 { // two segments across x+1, down-right 1642 | handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 1643 | handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 1644 | } else if x3 < x2 && x0 > x2 { // two segments across x+1, down-left 1645 | handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); 1646 | handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); 1647 | } else { // one segment 1648 | handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); 1649 | } 1650 | } 1651 | } 1652 | } 1653 | e = (*e).next; 1654 | } 1655 | } 1656 | 1657 | // directly AA rasterize edges w/o supersampling 1658 | pub unsafe fn rasterize_sorted_edges( 1659 | result: *mut Bitmap, 1660 | mut e: *mut Edge, 1661 | n: isize, 1662 | _vsubsample: isize, 1663 | off_x: isize, 1664 | off_y: isize 1665 | ) { 1666 | let mut hh: Hheap = Hheap { 1667 | head: null_mut(), 1668 | first_free: null_mut(), 1669 | num_remaining_in_head_chunk: 0, 1670 | }; 1671 | let mut active: *mut ActiveEdge = null_mut(); 1672 | let mut y: isize; 1673 | let mut j: isize =0; 1674 | let mut scanline_data: [f32; 129] = [0.0; 129]; 1675 | let scanline: *mut f32; 1676 | let scanline2: *mut f32; 1677 | 1678 | if (*result).w > 64 { 1679 | scanline = STBTT_malloc!(((*result).w*2+1) as usize * size_of::()) as *mut f32; 1680 | } else { 1681 | scanline = scanline_data.as_mut_ptr(); 1682 | } 1683 | 1684 | scanline2 = scanline.offset((*result).w); 1685 | 1686 | y = off_y; 1687 | (*e.offset(n)).y0 = (off_y + (*result).h) as f32 + 1.0; 1688 | 1689 | while j < (*result).h { 1690 | // find center of pixel for this scanline 1691 | let scan_y_top: f32 = y as f32 + 0.0; 1692 | let scan_y_bottom: f32 = y as f32 + 1.0; 1693 | let mut step: *mut *mut ActiveEdge = &mut active; 1694 | 1695 | memset(scanline as *mut c_void, 0, (*result).w as usize * size_of::()); 1696 | memset(scanline2 as *mut c_void, 0, 1697 | ((*result).w+1) as usize * size_of::()); 1698 | 1699 | // update all active edges; 1700 | // remove all active edges that terminate before the top of this scanline 1701 | while (*step) != null_mut() { 1702 | // Location B. 1703 | let z: *mut ActiveEdge = *step; 1704 | if (*z).ey <= scan_y_top { 1705 | *step = (*z).next; // delete from list 1706 | STBTT_assert!((*z).direction != 0.0); 1707 | (*z).direction = 0.0; 1708 | hheap_free(&mut hh, z as *mut ()); 1709 | } else { 1710 | step = &mut ((**step).next); // advance through list 1711 | } 1712 | } 1713 | 1714 | // insert all edges that start before the bottom of this scanline 1715 | while (*e).y0 <= scan_y_bottom { 1716 | if (*e).y0 != (*e).y1 { 1717 | let z: *mut ActiveEdge = new_active( 1718 | &mut hh, e, off_x, scan_y_top); 1719 | STBTT_assert!((*z).ey >= scan_y_top); 1720 | // insert at front 1721 | (*z).next = active; 1722 | active = z; 1723 | } 1724 | e = e.offset(1); 1725 | } 1726 | 1727 | // now process all active edges 1728 | if active != null_mut() { 1729 | fill_active_edges_new(scanline, scanline2.offset(1), (*result).w, 1730 | active, scan_y_top); 1731 | } 1732 | 1733 | { 1734 | let mut sum: f32 = 0.0; 1735 | for i in 0..(*result).w { 1736 | let mut k: f32; 1737 | let mut m: isize; 1738 | sum += *scanline2.offset(i); 1739 | k = *scanline.offset(i) + sum; 1740 | k = k.abs() as f32 * 255.0 as f32 + 0.5; 1741 | m = k as isize; 1742 | if m > 255 { m = 255; } 1743 | *(*result).pixels.offset(j*(*result).stride + i) = m as u8; 1744 | } 1745 | } 1746 | // advance all the edges 1747 | step = &mut active; 1748 | while *step != null_mut() { 1749 | let z: *mut ActiveEdge = *step; 1750 | (*z).fx += (*z).fdx; // advance to position for current scanline 1751 | step = &mut ((**step).next); // advance through list 1752 | } 1753 | 1754 | y += 1; 1755 | j += 1; 1756 | } 1757 | 1758 | hheap_cleanup(&mut hh); 1759 | 1760 | if scanline != scanline_data.as_mut_ptr() { 1761 | STBTT_free!(scanline as *mut c_void); 1762 | } 1763 | } 1764 | // #else 1765 | // #error "Unrecognized value of STBTT_RASTERIZER_VERSION" 1766 | // #endif 1767 | 1768 | macro_rules! STBTT__COMPARE { 1769 | ($a:expr, $b:expr) => { 1770 | ($a).y0 < ($b).y0 1771 | } 1772 | } 1773 | 1774 | // #define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) 1775 | 1776 | pub unsafe fn sort_edges_ins_sort( 1777 | p: *mut Edge, 1778 | n: isize 1779 | ) { 1780 | let mut j: isize; 1781 | for i in 1..n { 1782 | let t: Edge = *p.offset(i); 1783 | let a: *const Edge = &t; 1784 | j = i; 1785 | while j > 0 { 1786 | let b: *const Edge = p.offset(j-1); 1787 | let c = STBTT__COMPARE!((*a),(*b)); 1788 | if !c { break; } 1789 | *p.offset(j) = *p.offset(j-1); 1790 | j -= 1; 1791 | } 1792 | if i != j { 1793 | (*p.offset(j)) = t; 1794 | } 1795 | } 1796 | } 1797 | 1798 | pub unsafe fn sort_edges_quicksort(mut p: *mut Edge, mut n: isize) 1799 | { 1800 | /* threshhold for transitioning to insertion sort */ 1801 | while n > 12 { 1802 | let mut t: Edge; 1803 | let c01: bool; 1804 | let c12: bool; 1805 | let c: bool; 1806 | let m: isize; 1807 | let mut i: isize; 1808 | let mut j: isize; 1809 | 1810 | /* compute median of three */ 1811 | m = n >> 1; 1812 | c01 = STBTT__COMPARE!((*p.offset(0)),(*p.offset(m))); 1813 | c12 = STBTT__COMPARE!((*p.offset(m)),(*p.offset(n-1))); 1814 | /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ 1815 | if c01 != c12 { 1816 | /* otherwise, we'll need to swap something else to middle */ 1817 | let z: isize; 1818 | c = STBTT__COMPARE!((*p.offset(0)),(*p.offset(n-1))); 1819 | /* 0>mid && midn => n; 0 0 */ 1820 | /* 0n: 0>n => 0; 0 n */ 1821 | z = if c == c12 { 0 } else { n-1 }; 1822 | t = *p.offset(z); 1823 | *p.offset(z) = *p.offset(m); 1824 | *p.offset(m) = t; 1825 | } 1826 | /* now p[m] is the median-of-three */ 1827 | /* swap it to the beginning so it won't move around */ 1828 | t = *p.offset(0); 1829 | *p.offset(0) = *p.offset(m); 1830 | *p.offset(m) = t; 1831 | 1832 | /* partition loop */ 1833 | i=1; 1834 | j=n-1; 1835 | loop { 1836 | /* handling of equality is crucial here */ 1837 | /* for sentinels & efficiency with duplicates */ 1838 | loop { 1839 | if !STBTT__COMPARE!((*p.offset(i)), (*p.offset(0))) { break; } 1840 | i += 1; 1841 | } 1842 | loop { 1843 | if !STBTT__COMPARE!((*p.offset(0)), (*p.offset(j))) { break; } 1844 | j -= 1; 1845 | } 1846 | /* make sure we haven't crossed */ 1847 | if i >= j { break; } 1848 | t = *p.offset(i); 1849 | *p.offset(i) = *p.offset(j); 1850 | *p.offset(j) = t; 1851 | 1852 | i += 1; 1853 | j -= 1; 1854 | } 1855 | /* recurse on smaller side, iterate on larger */ 1856 | if j < (n-i) { 1857 | sort_edges_quicksort(p,j); 1858 | p = p.offset(i); 1859 | n = n-i; 1860 | } else { 1861 | sort_edges_quicksort(p.offset(i), n-i); 1862 | n = j; 1863 | } 1864 | } 1865 | } 1866 | 1867 | pub unsafe fn sort_edges(p: *mut Edge, n: isize) { 1868 | sort_edges_quicksort(p, n); 1869 | sort_edges_ins_sort(p, n); 1870 | } 1871 | 1872 | pub struct Point 1873 | { 1874 | x: f32, 1875 | y: f32, 1876 | } 1877 | 1878 | unsafe fn rasterize_( 1879 | result: *mut Bitmap, 1880 | pts: *mut Point, 1881 | wcount: *mut isize, 1882 | windings: isize, 1883 | scale_x: f32, 1884 | scale_y: f32, 1885 | shift_x: f32, 1886 | shift_y: f32, 1887 | off_x: isize, 1888 | off_y: isize, 1889 | invert: isize 1890 | ) { 1891 | let y_scale_inv: f32 = if invert != 0 { -scale_y } else { scale_y }; 1892 | let e: *mut Edge; 1893 | let mut n: isize; 1894 | let mut j: isize; 1895 | let mut m: isize; 1896 | // TODO: Conditional compilation. 1897 | // #if STBTT_RASTERIZER_VERSION == 1 1898 | // int vsubsample = result->h < 8 ? 15 : 5; 1899 | // #elif STBTT_RASTERIZER_VERSION == 2 1900 | let vsubsample: isize = 1; 1901 | // #else 1902 | // #error "Unrecognized value of STBTT_RASTERIZER_VERSION" 1903 | // #endif 1904 | // vsubsample should divide 255 evenly; otherwise we won't reach full opacity 1905 | 1906 | // now we have to blow out the windings into explicit edge lists 1907 | n = 0; 1908 | for i in 0..windings { 1909 | n = n + *wcount.offset(i); 1910 | } 1911 | 1912 | e = STBTT_malloc!(size_of::() * (n+1) as usize) 1913 | as *mut Edge; // add an extra one as a sentinel 1914 | if e == null_mut() { return }; 1915 | n = 0; 1916 | 1917 | m=0; 1918 | for i in 0..windings { 1919 | let p: *const Point = pts.offset(m); 1920 | m += *wcount.offset(i); 1921 | j = *wcount.offset(i)-1; 1922 | for k in 0..(*wcount.offset(i)) { 1923 | let mut a: isize=k; 1924 | let mut b: isize =j; 1925 | // skip the edge if horizontal 1926 | if (*p.offset(j)).y != (*p.offset(k)).y { 1927 | // add edge from j to k to the list 1928 | (*e.offset(n)).invert = 0; 1929 | if if invert != 0 { (*p.offset(j)).y > (*p.offset(k)).y } 1930 | else { (*p.offset(j)).y < (*p.offset(k)).y } { 1931 | (*e.offset(n)).invert = 1; 1932 | a=j; 1933 | b=k; 1934 | } 1935 | (*e.offset(n)).x0 = (*p.offset(a)).x * scale_x + shift_x; 1936 | (*e.offset(n)).y0 = ((*p.offset(a)).y * y_scale_inv + shift_y) * vsubsample as f32; 1937 | (*e.offset(n)).x1 = (*p.offset(b)).x * scale_x + shift_x; 1938 | (*e.offset(n)).y1 = ((*p.offset(b)).y * y_scale_inv + shift_y) * vsubsample as f32; 1939 | 1940 | n += 1; 1941 | } 1942 | j = k; 1943 | } 1944 | } 1945 | 1946 | // now sort the edges by their highest point (should snap to integer, and then by x) 1947 | //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); 1948 | sort_edges(e, n); 1949 | 1950 | // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule 1951 | rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y); 1952 | 1953 | STBTT_free!(e as *mut c_void); 1954 | } 1955 | 1956 | pub unsafe fn add_point( 1957 | points: *mut Point, 1958 | n: isize, 1959 | x: f32, 1960 | y: f32 1961 | ) { 1962 | if points == null_mut() { return; } // during first pass, it's unallocated 1963 | (*points.offset(n)).x = x; 1964 | (*points.offset(n)).y = y; 1965 | } 1966 | 1967 | // tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching 1968 | pub unsafe fn tesselate_curve( 1969 | points: *mut Point, 1970 | num_points: *mut isize, 1971 | x0: f32, 1972 | y0: f32, 1973 | x1: f32, 1974 | y1: f32, 1975 | x2: f32, 1976 | y2: f32, 1977 | objspace_flatness_squared: f32, 1978 | n: isize 1979 | ) -> isize { 1980 | // midpoint 1981 | let mx: f32 = (x0 + 2.0*x1 + x2)/4.0; 1982 | let my: f32 = (y0 + 2.0*y1 + y2)/4.0; 1983 | // versus directly drawn line 1984 | let dx: f32 = (x0+x2)/2.0 - mx; 1985 | let dy: f32 = (y0+y2)/2.0 - my; 1986 | if n > 16 { // 65536 segments on one curve better be enough! 1987 | return 1; 1988 | } 1989 | if dx*dx+dy*dy > objspace_flatness_squared { // half-pixel error allowed... need to be smaller if AA 1990 | tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0,(y0+y1)/2.0, mx,my, objspace_flatness_squared,n+1); 1991 | tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0,(y1+y2)/2.0, x2,y2, objspace_flatness_squared,n+1); 1992 | } else { 1993 | add_point(points, *num_points,x2,y2); 1994 | *num_points = *num_points+1; 1995 | } 1996 | return 1; 1997 | } 1998 | 1999 | // returns number of contours 2000 | pub unsafe fn flatten_curves( 2001 | vertices: *mut Vertex, 2002 | num_verts: isize, 2003 | objspace_flatness: f32, 2004 | contour_lengths: *mut *mut isize, 2005 | num_contours: *mut isize, 2006 | ) -> *mut Point { 2007 | let mut points: *mut Point = null_mut(); 2008 | let mut num_points: isize =0; 2009 | 2010 | let objspace_flatness_squared: f32 = objspace_flatness * objspace_flatness; 2011 | let mut n: isize =0; 2012 | let mut start: isize =0; 2013 | 2014 | // count how many "moves" there are to get the contour count 2015 | for i in 0..num_verts { 2016 | if (*vertices.offset(i)).type_ == Cmd::Move { 2017 | n += 1; 2018 | } 2019 | } 2020 | 2021 | *num_contours = n; 2022 | if n == 0 { return null_mut(); } 2023 | 2024 | *contour_lengths = STBTT_malloc!(size_of::() * n as usize) as *mut isize; 2025 | 2026 | if *contour_lengths == null_mut() { 2027 | *num_contours = 0; 2028 | return null_mut(); 2029 | } 2030 | 2031 | 'error: loop { 2032 | // make two passes through the points so we don't need to realloc 2033 | for pass in 0..2 { 2034 | let mut x: f32=0.0; 2035 | let mut y: f32=0.0; 2036 | if pass == 1 { 2037 | points = STBTT_malloc!(num_points as usize * size_of::()) 2038 | as *mut Point; 2039 | if points == null_mut() { 2040 | break 'error; 2041 | }; 2042 | } 2043 | num_points = 0; 2044 | n= -1; 2045 | for i in 0..num_verts { 2046 | match (*vertices.offset(i)).type_ { 2047 | Cmd::Move => { 2048 | // start the next contour 2049 | if n >= 0 { 2050 | *(*contour_lengths).offset(n) = num_points - start; 2051 | } 2052 | n += 1; 2053 | start = num_points; 2054 | 2055 | x = (*vertices.offset(i)).x as f32; 2056 | y = (*vertices.offset(i)).y as f32; 2057 | add_point(points, num_points, x,y); 2058 | num_points += 1; 2059 | } 2060 | Cmd::Line => { 2061 | x = (*vertices.offset(i)).x as f32; 2062 | y = (*vertices.offset(i)).y as f32; 2063 | add_point(points, num_points, x, y); 2064 | num_points += 1; 2065 | } 2066 | Cmd::Curve => { 2067 | tesselate_curve(points, &mut num_points, x,y, 2068 | (*vertices.offset(i)).cx as f32, (*vertices.offset(i)).cy as f32, 2069 | (*vertices.offset(i)).x as f32, (*vertices.offset(i)).y as f32, 2070 | objspace_flatness_squared, 0); 2071 | x = (*vertices.offset(i)).x as f32; 2072 | y = (*vertices.offset(i)).y as f32; 2073 | } 2074 | } 2075 | } 2076 | *(*contour_lengths).offset(n) = num_points - start; 2077 | } 2078 | return points; 2079 | } // 'error 2080 | 2081 | STBTT_free!(points as *mut c_void); 2082 | STBTT_free!(*contour_lengths as *mut c_void); 2083 | *contour_lengths = null_mut(); 2084 | *num_contours = 0; 2085 | return null_mut(); 2086 | } 2087 | 2088 | // rasterize a shape with quadratic beziers into a bitmap 2089 | pub unsafe fn rasterize( 2090 | // 1-channel bitmap to draw into 2091 | result: *mut Bitmap, 2092 | // allowable error of curve in pixels 2093 | flatness_in_pixels: f32, 2094 | // array of vertices defining shape 2095 | vertices: *mut Vertex, 2096 | // number of vertices in above array 2097 | num_verts: isize, 2098 | // scale applied to input vertices 2099 | scale_x: f32, 2100 | scale_y: f32, 2101 | // translation applied to input vertices 2102 | shift_x: f32, 2103 | shift_y: f32, 2104 | // another translation applied to input 2105 | x_off: isize, 2106 | y_off: isize, 2107 | // if non-zero, vertically flip shape 2108 | invert: isize 2109 | ) { 2110 | let scale: f32 = if scale_x > scale_y { scale_y } else { scale_x }; 2111 | let mut winding_count: isize = 0; 2112 | let mut winding_lengths: *mut isize = null_mut(); 2113 | let windings: *mut Point = flatten_curves(vertices, num_verts, 2114 | flatness_in_pixels / scale, &mut winding_lengths, &mut winding_count); 2115 | if windings != null_mut() { 2116 | rasterize_(result, windings, winding_lengths, winding_count, 2117 | scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert); 2118 | STBTT_free!(winding_lengths as *mut c_void); 2119 | STBTT_free!(windings as *mut c_void); 2120 | } 2121 | } 2122 | 2123 | // frees the bitmap allocated below 2124 | pub unsafe fn free_bitmap(bitmap: *mut u8) 2125 | { 2126 | STBTT_free!(bitmap as *mut c_void); 2127 | } 2128 | 2129 | pub unsafe fn get_glyph_bitmap_subpixel( 2130 | info: *const FontInfo, 2131 | mut scale_x: f32, 2132 | mut scale_y: f32, 2133 | shift_x: f32, 2134 | shift_y: f32, 2135 | glyph: isize, 2136 | width: *mut isize, 2137 | height: *mut isize, 2138 | xoff: *mut isize, 2139 | yoff: *mut isize 2140 | ) -> *mut u8 { 2141 | let mut vertices: *mut Vertex = null_mut(); 2142 | let num_verts: isize = get_glyph_shape(info, glyph, &mut vertices); 2143 | 2144 | if scale_x == 0.0 { scale_x = scale_y; } 2145 | if scale_y == 0.0 { 2146 | if scale_x == 0.0 { return null_mut(); } 2147 | scale_y = scale_x; 2148 | } 2149 | 2150 | let glyph_data = (*info).glyph_data_for_glyph_at_index(glyph as usize); 2151 | let bbox = glyph_data.bitmap_box_subpixel(scale_x, scale_y, shift_x, shift_y).unwrap_or_default(); 2152 | 2153 | // now we get the size 2154 | let mut gbm = Bitmap 2155 | { 2156 | w: (bbox.x1 - bbox.x0) as isize, 2157 | h: (bbox.y1 - bbox.y0) as isize, 2158 | stride: 0, 2159 | pixels: null_mut(), 2160 | }; 2161 | 2162 | if width != null_mut() { *width = gbm.w; } 2163 | if height != null_mut() { *height = gbm.h; } 2164 | if xoff != null_mut() { *xoff = bbox.x0 as isize; } 2165 | if yoff != null_mut() { *yoff = bbox.y0 as isize; } 2166 | 2167 | if gbm.w != 0 && gbm.h != 0 { 2168 | gbm.pixels = STBTT_malloc!((gbm.w * gbm.h) as usize) as *mut u8; 2169 | if gbm.pixels != null_mut() { 2170 | gbm.stride = gbm.w; 2171 | 2172 | rasterize(&mut gbm, 0.35, 2173 | vertices, num_verts, scale_x, scale_y, shift_x, shift_y, bbox.x0 as isize, bbox.y0 as isize, 2174 | 1); 2175 | } 2176 | } 2177 | STBTT_free!(vertices as *mut c_void); 2178 | return gbm.pixels; 2179 | } 2180 | 2181 | // the following functions are equivalent to the above functions, but operate 2182 | // on glyph indices instead of Unicode codepoints (for efficiency) 2183 | 2184 | pub unsafe fn get_glyph_bitmap( 2185 | info: *const FontInfo, 2186 | scale_x: f32, 2187 | scale_y: f32, 2188 | glyph: isize, 2189 | width: *mut isize, 2190 | height: *mut isize, 2191 | xoff: *mut isize, 2192 | yoff: *mut isize 2193 | ) -> *const u8 { 2194 | return get_glyph_bitmap_subpixel(info, scale_x, scale_y, 2195 | 0.0, 0.0, glyph, width, height, xoff, yoff); 2196 | } 2197 | 2198 | pub unsafe fn make_glyph_bitmap_subpixel( 2199 | info: *const FontInfo, 2200 | output: *mut u8, 2201 | out_w: isize, 2202 | out_h: isize, 2203 | out_stride: isize, 2204 | scale_x: f32, 2205 | scale_y: f32, 2206 | shift_x: f32, 2207 | shift_y: f32, 2208 | glyph: isize 2209 | ) { 2210 | let mut vertices: *mut Vertex = null_mut(); 2211 | let num_verts: isize = get_glyph_shape(info, glyph, &mut vertices); 2212 | 2213 | let glyph_data = (*info).glyph_data_for_glyph_at_index(glyph as usize); 2214 | let bbox = glyph_data.bitmap_box_subpixel(scale_x, scale_y, shift_x, shift_y).unwrap_or_default(); 2215 | 2216 | let mut gbm: Bitmap = Bitmap 2217 | { 2218 | w: out_w, 2219 | h: out_h, 2220 | stride: out_stride, 2221 | pixels: output, 2222 | }; 2223 | 2224 | if gbm.w != 0 && gbm.h != 0 { 2225 | rasterize(&mut gbm, 0.35, vertices, num_verts, 2226 | scale_x, scale_y, shift_x, shift_y, bbox.x0 as isize,bbox.y0 as isize, 1); 2227 | } 2228 | 2229 | STBTT_free!(vertices as *mut c_void); 2230 | } 2231 | 2232 | pub unsafe fn make_glyph_bitmap( 2233 | info: *const FontInfo, 2234 | output: *mut u8, 2235 | out_w: isize, 2236 | out_h: isize, 2237 | out_stride: isize, 2238 | scale_x: f32, 2239 | scale_y: f32, 2240 | glyph: isize 2241 | ) { 2242 | make_glyph_bitmap_subpixel(info, output, out_w, out_h, 2243 | out_stride, scale_x, scale_y, 0.0,0.0, glyph); 2244 | } 2245 | 2246 | // the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel 2247 | // shift for the character 2248 | pub unsafe fn get_codepoint_bitmap_subpixel( 2249 | info: *const FontInfo, 2250 | scale_x: f32, 2251 | scale_y: f32, 2252 | shift_x: f32, 2253 | shift_y: f32, 2254 | codepoint: isize, 2255 | width: *mut isize, 2256 | height: *mut isize, 2257 | xoff: *mut isize, 2258 | yoff: *mut isize 2259 | ) -> *mut u8 { 2260 | assert!(codepoint >= 0); 2261 | let i = (*info).glyph_index_for_code(codepoint as usize) as isize; 2262 | get_glyph_bitmap_subpixel(info, scale_x, scale_y,shift_x,shift_y, i, width,height,xoff,yoff) 2263 | } 2264 | 2265 | // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel 2266 | // shift for the character 2267 | pub unsafe fn make_codepoint_bitmap_subpixel( 2268 | info: *const FontInfo, 2269 | output: *mut u8, 2270 | out_w: isize, 2271 | out_h: isize, 2272 | out_stride: isize, 2273 | scale_x: f32, 2274 | scale_y: f32, 2275 | shift_x: f32, 2276 | shift_y: f32, 2277 | codepoint: isize 2278 | ) { 2279 | assert!(codepoint >= 0); 2280 | let i = (*info).glyph_index_for_code(codepoint as usize) as isize; 2281 | make_glyph_bitmap_subpixel(info, output, out_w, out_h, 2282 | out_stride, scale_x, scale_y, shift_x, shift_y, i); 2283 | } 2284 | 2285 | // allocates a large-enough single-channel 8bpp bitmap and renders the 2286 | // specified character/glyph at the specified scale into it, with 2287 | // antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). 2288 | // *width & *height are filled out with the width & height of the bitmap, 2289 | // which is stored left-to-right, top-to-bottom. 2290 | // 2291 | // xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap 2292 | pub unsafe fn get_codepoint_bitmap( 2293 | info: *const FontInfo, 2294 | scale_x: f32, 2295 | scale_y: f32, 2296 | codepoint: isize, 2297 | width: *mut isize, 2298 | height: *mut isize, 2299 | xoff: *mut isize, 2300 | yoff: *mut isize 2301 | ) -> *mut u8 { 2302 | return get_codepoint_bitmap_subpixel(info, scale_x, scale_y, 2303 | 0.0,0.0, codepoint, width,height,xoff,yoff); 2304 | } 2305 | 2306 | // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap 2307 | // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap 2308 | // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the 2309 | // width and height and positioning info for it first. 2310 | pub unsafe fn make_codepoint_bitmap( 2311 | info: *const FontInfo, 2312 | output: *mut u8, 2313 | out_w: isize, 2314 | out_h: isize, 2315 | out_stride: isize, 2316 | scale_x: f32, 2317 | scale_y: f32, 2318 | codepoint: isize 2319 | ) { 2320 | make_codepoint_bitmap_subpixel(info, output, out_w, out_h, 2321 | out_stride, scale_x, scale_y, 0.0,0.0, codepoint); 2322 | } 2323 | 2324 | ////////////////////////////////////////////////////////////////////////////// 2325 | // 2326 | // bitmap baking 2327 | // 2328 | 2329 | // if return is positive, the first unused row of the bitmap 2330 | // if return is negative, returns the negative of the number of characters that fit 2331 | // if return is 0, no characters fit and no rows were used 2332 | // This uses a very crappy packing. 2333 | pub unsafe fn bake_font_bitmap( 2334 | data: &[u8], offset: usize, // font location (use offset=0 for plain .ttf) 2335 | pixel_height: f32, // height of font in pixels 2336 | pixels: *mut u8, pw: isize, ph: isize, // bitmap to be filled in 2337 | first_char: isize, num_chars: isize, // characters to bake 2338 | chardata: *mut BakedChar 2339 | ) -> Result { 2340 | let scale: f32; 2341 | let mut x: isize; 2342 | let mut y: isize; 2343 | let mut bottom_y: isize; 2344 | let f: FontInfo = try!(FontInfo::new_with_offset(data, offset)); 2345 | memset(pixels as *mut _ as *mut c_void, 0, (pw*ph) as usize); // background of 0 around pixels 2346 | x=1; 2347 | y=1; 2348 | bottom_y = 1; 2349 | 2350 | scale = f.scale_for_pixel_height(pixel_height); 2351 | 2352 | for i in 0..num_chars { 2353 | let g = f.glyph_index_for_code((first_char + i) as usize) as isize; 2354 | let glyph_data = f.glyph_data_for_glyph_at_index(g as usize); 2355 | let bbox = glyph_data.bitmap_box(scale, scale).unwrap_or_default(); 2356 | let metric = f.hmtx.hmetric_for_glyph_at_index(g as usize); 2357 | 2358 | let gw = (bbox.x1 - bbox.x0) as isize; 2359 | let gh = (bbox.y1 - bbox.y0) as isize; 2360 | if x + gw + 1 >= pw { 2361 | y = bottom_y; 2362 | x = 1; // advance to next row 2363 | } 2364 | if y + gh + 1 >= ph { // check if it fits vertically AFTER potentially moving to next row 2365 | return Ok(-i); 2366 | } 2367 | STBTT_assert!(x+gw < pw); 2368 | STBTT_assert!(y+gh < ph); 2369 | make_glyph_bitmap(&f, pixels.offset(x+y*pw), gw,gh,pw, scale,scale, g); 2370 | (*chardata.offset(i)).x0 = x as u16; 2371 | (*chardata.offset(i)).y0 = y as u16; 2372 | (*chardata.offset(i)).x1 = (x + gw) as u16; 2373 | (*chardata.offset(i)).y1 = (y + gh) as u16; 2374 | (*chardata.offset(i)).xadvance = scale * metric.advance_width as f32; 2375 | (*chardata.offset(i)).xoff = bbox.x0 as f32; 2376 | (*chardata.offset(i)).yoff = bbox.y0 as f32; 2377 | x = x + gw + 1; 2378 | if y+gh+1 > bottom_y { 2379 | bottom_y = y+gh+1; 2380 | } 2381 | } 2382 | return Ok(bottom_y); 2383 | } 2384 | 2385 | // Call GetBakedQuad with char_index = 'character - first_char', and it 2386 | // creates the quad you need to draw and advances the current position. 2387 | // 2388 | // The coordinate system used assumes y increases downwards. 2389 | // 2390 | // Characters will extend both above and below the current position; 2391 | // see discussion of "BASELINE" above. 2392 | // 2393 | // It's inefficient; you might want to c&p it and optimize it. 2394 | pub unsafe fn get_baked_quad( 2395 | chardata: *mut BakedChar, 2396 | pw: isize, 2397 | ph: isize, 2398 | // character to display 2399 | char_index: isize, 2400 | // pointers to current position in screen pixel space 2401 | xpos: *mut f32, 2402 | ypos: *const f32, 2403 | q: *mut AlignedQuad, // output: quad to draw 2404 | opengl_fillrule: isize 2405 | ) 2406 | { 2407 | let d3d_bias: f32 = if opengl_fillrule != 0 { 0.0 } else { -0.5 }; 2408 | let ipw: f32 = 1.0 / pw as f32; 2409 | let iph = 1.0 / ph as f32; 2410 | let b: *mut BakedChar = chardata.offset(char_index); 2411 | let round_x = ifloor((*xpos + (*b).xoff) + 0.5); 2412 | let round_y = ifloor((*ypos + (*b).yoff) + 0.5); 2413 | 2414 | (*q).x0 = round_x as f32 + d3d_bias; 2415 | (*q).y0 = round_y as f32 + d3d_bias; 2416 | (*q).x1 = round_x as f32 + (*b).x1 as f32 - (*b).x0 as f32 + d3d_bias; 2417 | (*q).y1 = round_y as f32 + (*b).y1 as f32 - (*b).y0 as f32 + d3d_bias; 2418 | 2419 | (*q).s0 = (*b).x0 as f32 * ipw; 2420 | (*q).t0 = (*b).y0 as f32 * iph; 2421 | (*q).s1 = (*b).x1 as f32 * ipw; 2422 | (*q).t1 = (*b).y1 as f32 * iph; 2423 | 2424 | *xpos += (*b).xadvance; 2425 | } 2426 | 2427 | ////////////////////////////////////////////////////////////////////////////// 2428 | // 2429 | // rectangle packing replacement routines if you don't have stb_rect_pack.h 2430 | // 2431 | 2432 | // TODO: Not sure which is the right one, see comments below. 2433 | macro_rules! STBTT__NOTUSED { 2434 | ($v:expr) => {} 2435 | } 2436 | 2437 | // #ifndef STB_RECT_PACK_VERSION 2438 | // #ifdef _MSC_VER 2439 | // #define STBTT__NOTUSED(v) (void)(v) 2440 | // #else 2441 | // #define STBTT__NOTUSED(v) (void)sizeof(v) 2442 | // #endif 2443 | 2444 | type Coord = isize; 2445 | 2446 | //////////////////////////////////////////////////////////////////////////////////// 2447 | // // 2448 | // // 2449 | // COMPILER WARNING ?!?!? // 2450 | // // 2451 | // // 2452 | // if you get a compile warning due to these symbols being defined more than // 2453 | // once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // 2454 | // // 2455 | //////////////////////////////////////////////////////////////////////////////////// 2456 | 2457 | pub struct Context 2458 | { 2459 | width: isize, 2460 | height: isize, 2461 | x: isize, 2462 | y: isize, 2463 | bottom_y: isize, 2464 | } 2465 | 2466 | #[allow(dead_code)] 2467 | pub struct Node 2468 | { 2469 | x: u8, 2470 | } 2471 | 2472 | #[allow(dead_code)] 2473 | pub struct Rect 2474 | { 2475 | x: Coord, 2476 | y: Coord, 2477 | id: isize, 2478 | w: isize, 2479 | h: isize, 2480 | was_packed: isize 2481 | } 2482 | 2483 | pub unsafe fn stbrp_init_target( 2484 | con: *mut Context, 2485 | pw: isize, 2486 | ph: isize, 2487 | _nodes: *mut Node, 2488 | _num_nodes: isize 2489 | ) { 2490 | (*con).width = pw; 2491 | (*con).height = ph; 2492 | (*con).x = 0; 2493 | (*con).y = 0; 2494 | (*con).bottom_y = 0; 2495 | STBTT__NOTUSED!(nodes); 2496 | STBTT__NOTUSED!(num_nodes); 2497 | } 2498 | 2499 | pub unsafe fn stbrp_pack_rects( 2500 | con: *mut Context, 2501 | rects: *mut Rect, 2502 | num_rects: isize 2503 | ) { 2504 | for i in 0..num_rects { 2505 | if (*con).x + (*rects.offset(i)).w > (*con).width { 2506 | (*con).x = 0; 2507 | (*con).y = (*con).bottom_y; 2508 | } 2509 | if (*con).y + (*rects.offset(i)).h > (*con).height { 2510 | break; 2511 | } 2512 | (*rects.offset(i)).x = (*con).x; 2513 | (*rects.offset(i)).y = (*con).y; 2514 | (*rects.offset(i)).was_packed = 1; 2515 | (*con).x += (*rects.offset(i)).w; 2516 | if (*con).y + (*rects.offset(i)).h > (*con).bottom_y { 2517 | (*con).bottom_y = (*con).y + (*rects.offset(i)).h; 2518 | } 2519 | } 2520 | // TODO: Weird boundary conditions. 2521 | // for ( ; i < num_rects; ++i) 2522 | // rects[i].was_packed = 0; 2523 | } 2524 | // #endif 2525 | 2526 | ////////////////////////////////////////////////////////////////////////////// 2527 | // 2528 | // bitmap baking 2529 | // 2530 | // This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If 2531 | // stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. 2532 | 2533 | // Initializes a packing context stored in the passed-in PackContext. 2534 | // Future calls using this context will pack characters into the bitmap passed 2535 | // in here: a 1-channel bitmap that is weight x height. stride_in_bytes is 2536 | // the distance from one row to the next (or 0 to mean they are packed tightly 2537 | // together). "padding" is the amount of padding to leave between each 2538 | // character (normally you want '1' for bitmaps you'll use as textures with 2539 | // bilinear filtering). 2540 | // 2541 | // Returns 0 on failure, 1 on success. 2542 | pub unsafe fn pack_begin( 2543 | spc: *mut PackContext, 2544 | pixels: *mut u8, 2545 | pw: isize, 2546 | ph: isize, 2547 | stride_in_bytes: isize, 2548 | padding: isize, 2549 | alloc_context: *const () 2550 | ) -> isize 2551 | { 2552 | let context: *mut Context = STBTT_malloc!( 2553 | size_of::()) as *mut Context; 2554 | let num_nodes: isize = pw - padding; 2555 | let nodes: *mut Node = STBTT_malloc!( 2556 | size_of::() * num_nodes as usize) as *mut Node; 2557 | 2558 | if context == null_mut() || nodes == null_mut() { 2559 | if context != null_mut() { STBTT_free!(context as *mut c_void); } 2560 | if nodes != null_mut() { STBTT_free!(nodes as *mut c_void); } 2561 | return 0; 2562 | } 2563 | 2564 | (*spc).user_allocator_context = alloc_context; 2565 | (*spc).width = pw; 2566 | (*spc).height = ph; 2567 | (*spc).pixels = pixels; 2568 | (*spc).pack_info = context as *mut c_void; 2569 | (*spc).nodes = nodes as *mut c_void; 2570 | (*spc).padding = padding; 2571 | (*spc).stride_in_bytes = if stride_in_bytes != 0 { stride_in_bytes } else { pw }; 2572 | (*spc).h_oversample = 1; 2573 | (*spc).v_oversample = 1; 2574 | 2575 | stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); 2576 | 2577 | if pixels != null_mut() { 2578 | memset(pixels as *mut c_void, 0, (pw*ph) as usize); // background of 0 around pixels 2579 | } 2580 | 2581 | return 1; 2582 | } 2583 | 2584 | // Cleans up the packing context and frees all memory. 2585 | pub unsafe fn pack_end(spc: *mut PackContext) 2586 | { 2587 | STBTT_free!((*spc).nodes); 2588 | STBTT_free!((*spc).pack_info); 2589 | } 2590 | 2591 | // Oversampling a font increases the quality by allowing higher-quality subpixel 2592 | // positioning, and is especially valuable at smaller text sizes. 2593 | // 2594 | // This function sets the amount of oversampling for all following calls to 2595 | // stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given 2596 | // pack context. The default (no oversampling) is achieved by h_oversample=1 2597 | // and v_oversample=1. The total number of pixels required is 2598 | // h_oversample*v_oversample larger than the default; for example, 2x2 2599 | // oversampling requires 4x the storage of 1x1. For best results, render 2600 | // oversampled textures with bilinear filtering. Look at the readme in 2601 | // stb/tests/oversample for information about oversampled fonts 2602 | // 2603 | // To use with PackFontRangesGather etc., you must set it before calls 2604 | // call to PackFontRangesGatherRects. 2605 | pub unsafe fn pack_set_oversampling( 2606 | spc: *mut PackContext, 2607 | h_oversample: usize, 2608 | v_oversample: usize) 2609 | { 2610 | STBTT_assert!(h_oversample <= STBTT_MAX_OVERSAMPLE); 2611 | STBTT_assert!(v_oversample <= STBTT_MAX_OVERSAMPLE); 2612 | if h_oversample <= STBTT_MAX_OVERSAMPLE { 2613 | (*spc).h_oversample = h_oversample; 2614 | } 2615 | if v_oversample <= STBTT_MAX_OVERSAMPLE { 2616 | (*spc).v_oversample = v_oversample; 2617 | } 2618 | } 2619 | 2620 | const STBTT__OVER_MASK: usize = (STBTT_MAX_OVERSAMPLE-1); 2621 | 2622 | pub unsafe fn h_prefilter( 2623 | mut pixels: *mut u8, 2624 | w: isize, 2625 | h: isize, 2626 | stride_in_bytes: isize, 2627 | kernel_width: usize 2628 | ) { 2629 | let mut buffer: [u8; STBTT_MAX_OVERSAMPLE] = [0; STBTT_MAX_OVERSAMPLE]; 2630 | let safe_w: isize = w - kernel_width as isize; 2631 | for _ in 0..h { 2632 | let mut total: usize; 2633 | memset(&mut buffer[0] as *mut _ as *mut c_void, 0, kernel_width); 2634 | 2635 | total = 0; 2636 | 2637 | // make kernel_width a constant in common cases so compiler can optimize out the divide 2638 | match kernel_width { 2639 | 2 => { 2640 | for i in 0..safe_w { 2641 | total = total + *pixels.offset(i) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2642 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i); 2643 | *pixels.offset(i) = (total / 2) as u8; 2644 | } 2645 | } 2646 | 3 => { 2647 | for i in 0..safe_w { 2648 | total += *pixels.offset(i) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2649 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i); 2650 | *pixels.offset(i) = (total / 3) as u8; 2651 | } 2652 | } 2653 | 4 => { 2654 | for i in 0..safe_w { 2655 | total += *pixels.offset(i) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2656 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i); 2657 | *pixels.offset(i) = (total / 4) as u8; 2658 | } 2659 | } 2660 | 5 => { 2661 | for i in 0..safe_w { 2662 | total += *pixels.offset(i) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2663 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i); 2664 | *pixels.offset(i) = (total / 5) as u8; 2665 | } 2666 | } 2667 | _ => { 2668 | for i in 0..safe_w { 2669 | total += *pixels.offset(i) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2670 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i); 2671 | *pixels.offset(i) = (total / kernel_width) as u8; 2672 | } 2673 | } 2674 | } 2675 | 2676 | for i in safe_w..w { 2677 | STBTT_assert!(*pixels.offset(i) == 0); 2678 | total -= buffer[i as usize & STBTT__OVER_MASK] as usize; 2679 | *pixels.offset(i) = (total / kernel_width) as u8; 2680 | } 2681 | 2682 | pixels = pixels.offset(stride_in_bytes); 2683 | } 2684 | } 2685 | 2686 | pub unsafe fn v_prefilter( 2687 | mut pixels: *mut u8, 2688 | w: isize, 2689 | h: isize, 2690 | stride_in_bytes: isize, 2691 | kernel_width: usize 2692 | ) { 2693 | let mut buffer: [u8; STBTT_MAX_OVERSAMPLE] = [0; STBTT_MAX_OVERSAMPLE]; 2694 | let safe_h: isize = h - kernel_width as isize; 2695 | for _ in 0..w { 2696 | let mut total: usize; 2697 | memset(&mut buffer[0] as *mut _ as *mut c_void, 0, kernel_width); 2698 | 2699 | total = 0; 2700 | 2701 | // make kernel_width a constant in common cases so compiler can optimize out the divide 2702 | match kernel_width { 2703 | 2 => { 2704 | for i in 0..safe_h { 2705 | total += *pixels.offset(i*stride_in_bytes) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2706 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i*stride_in_bytes); 2707 | *pixels.offset(i*stride_in_bytes) = (total / 2) as u8; 2708 | } 2709 | } 2710 | 3 => { 2711 | for i in 0..safe_h { 2712 | total += *pixels.offset(i*stride_in_bytes) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2713 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i*stride_in_bytes); 2714 | *pixels.offset(i*stride_in_bytes) = (total / 3) as u8; 2715 | } 2716 | } 2717 | 4 => { 2718 | for i in 0..safe_h { 2719 | total += *pixels.offset(i*stride_in_bytes) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2720 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i*stride_in_bytes); 2721 | *pixels.offset(i*stride_in_bytes) = (total / 4) as u8; 2722 | } 2723 | } 2724 | 5 => { 2725 | for i in 0..safe_h { 2726 | total += *pixels.offset(i*stride_in_bytes) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2727 | buffer[(i as usize +kernel_width) & STBTT__OVER_MASK] = *pixels.offset(i*stride_in_bytes); 2728 | *pixels.offset(i*stride_in_bytes) = (total / 5) as u8; 2729 | } 2730 | } 2731 | _ => { 2732 | for i in 0..safe_h { 2733 | total += *pixels.offset(i*stride_in_bytes) as usize - buffer[i as usize & STBTT__OVER_MASK] as usize; 2734 | buffer[(i+kernel_width as isize) as usize & STBTT__OVER_MASK] = *pixels.offset(i*stride_in_bytes); 2735 | *pixels.offset(i*stride_in_bytes) = (total / kernel_width) as u8; 2736 | } 2737 | } 2738 | } 2739 | 2740 | for i in safe_h..h { 2741 | STBTT_assert!(*pixels.offset(i*stride_in_bytes) == 0); 2742 | total -= buffer[i as usize & STBTT__OVER_MASK] as usize; 2743 | *pixels.offset(i*stride_in_bytes) = (total / kernel_width) as u8; 2744 | } 2745 | 2746 | pixels = pixels.offset(1); 2747 | } 2748 | } 2749 | 2750 | pub fn oversample_shift(oversample: isize) -> f32 2751 | { 2752 | if oversample == 0 { 2753 | return 0.0; 2754 | } 2755 | 2756 | // The prefilter is a box filter of width "oversample", 2757 | // which shifts phase by (oversample - 1)/2 pixels in 2758 | // oversampled space. We want to shift in the opposite 2759 | // direction to counter this. 2760 | return -(oversample - 1) as f32 / (2.0 * oversample as f32); 2761 | } 2762 | 2763 | // - stbtt_PackFontRangesGatherRects 2764 | // - stbtt_PackFontRangesPackRects 2765 | // - stbtt_PackFontRangesRenderIntoRects 2766 | // 2767 | // Calling these functions in sequence is roughly equivalent to calling 2768 | // stbtt_PackFontRanges(). If you more control over the packing of multiple 2769 | // fonts, or if you want to pack custom data into a font texture, take a look 2770 | // at the source to of stbtt_PackFontRanges() and create a custom version 2771 | // using these functions, e.g. call GatherRects multiple times, 2772 | // building up a single array of rects, then call PackRects once, 2773 | // then call RenderIntoRects repeatedly. This may result in a 2774 | // better packing than calling PackFontRanges multiple times 2775 | // (or it may not). 2776 | 2777 | // rects array must be big enough to accommodate all characters in the given ranges 2778 | pub unsafe fn pack_font_ranges_gather_rects( 2779 | spc: *mut PackContext, 2780 | info: *mut FontInfo, 2781 | ranges: *mut PackRange, 2782 | num_ranges: isize, 2783 | rects: *mut Rect 2784 | ) -> isize { 2785 | let mut k: isize; 2786 | 2787 | k=0; 2788 | for i in 0..num_ranges { 2789 | let fh: f32 = (*ranges.offset(i)).font_size; 2790 | let scale = if fh > 0.0 { 2791 | (*info).scale_for_pixel_height(fh) 2792 | } else { 2793 | (*info).scale_for_mapping_em_to_pixels(-fh) 2794 | }; 2795 | (*ranges.offset(i)).h_oversample = (*spc).h_oversample as u8; 2796 | (*ranges.offset(i)).v_oversample = (*spc).v_oversample as u8; 2797 | for j in 0..(*ranges.offset(i)).num_chars { 2798 | let codepoint: isize = if (*ranges.offset(i)).array_of_unicode_codepoints == null() { 2799 | (*ranges.offset(i)).first_unicode_codepoint_in_range + j 2800 | } else { 2801 | *(*ranges.offset(i)).array_of_unicode_codepoints.offset(j) 2802 | }; 2803 | assert!(codepoint >= 0); 2804 | let glyph = (*info).glyph_index_for_code(codepoint as usize) as isize; 2805 | let glyph_data = (*info).glyph_data_for_glyph_at_index(glyph as usize); 2806 | let bbox = glyph_data.bitmap_box( 2807 | scale * (*spc).h_oversample as f32, 2808 | scale * (*spc).v_oversample as f32).unwrap_or_default(); 2809 | 2810 | (*rects.offset(k)).w = ((bbox.x1-bbox.x0) as isize + (*spc).padding as isize + (*spc).h_oversample as isize -1) as Coord; 2811 | (*rects.offset(k)).h = ((bbox.y1-bbox.y0) as isize + (*spc).padding as isize + (*spc).v_oversample as isize -1) as Coord; 2812 | k += 1; 2813 | } 2814 | } 2815 | 2816 | return k; 2817 | } 2818 | 2819 | // rects array must be big enough to accommodate all characters in the given ranges 2820 | pub unsafe fn pack_font_ranges_render_into_rects( 2821 | spc: *mut PackContext, 2822 | info: *mut FontInfo, 2823 | ranges: *mut PackRange, 2824 | num_ranges: isize, 2825 | rects: *mut Rect 2826 | ) -> isize { 2827 | let mut k: isize; 2828 | let mut return_value: isize = 1; 2829 | 2830 | // save current values 2831 | let old_h_over: isize = (*spc).h_oversample as isize; 2832 | let old_v_over: isize = (*spc).v_oversample as isize; 2833 | 2834 | k = 0; 2835 | for i in 0..num_ranges { 2836 | let fh: f32 = (*ranges.offset(i)).font_size; 2837 | let scale = if fh > 0.0 { 2838 | (*info).scale_for_pixel_height(fh) 2839 | } else { 2840 | (*info).scale_for_mapping_em_to_pixels(-fh) 2841 | }; 2842 | let recip_h: f32; 2843 | let recip_v: f32; 2844 | let sub_x: f32; 2845 | let sub_y: f32; 2846 | (*spc).h_oversample = (*ranges.offset(i)).h_oversample as usize; 2847 | (*spc).v_oversample = (*ranges.offset(i)).v_oversample as usize; 2848 | recip_h = 1.0 / (*spc).h_oversample as f32; 2849 | recip_v = 1.0 / (*spc).v_oversample as f32; 2850 | sub_x = oversample_shift((*spc).h_oversample as isize); 2851 | sub_y = oversample_shift((*spc).v_oversample as isize); 2852 | for j in 0..(*ranges.offset(i)).num_chars { 2853 | let r: *mut Rect = rects.offset(k); 2854 | if (*r).was_packed != 0 { 2855 | let bc: *mut PackedChar = (*ranges.offset(i)).chardata_for_range.offset(j); 2856 | 2857 | let codepoint: isize = 2858 | if (*ranges.offset(i)).array_of_unicode_codepoints == null() { 2859 | (*ranges.offset(i)).first_unicode_codepoint_in_range + j 2860 | } else { 2861 | (*(*ranges.offset(i)).array_of_unicode_codepoints.offset(j)) 2862 | }; 2863 | assert!(codepoint >= 0); 2864 | let glyph = (*info).glyph_index_for_code(codepoint as usize) as isize; 2865 | let pad: Coord = (*spc).padding as Coord; 2866 | 2867 | // pad on left and top 2868 | (*r).x += pad; 2869 | (*r).y += pad; 2870 | (*r).w -= pad; 2871 | (*r).h -= pad; 2872 | 2873 | let glyph_data = (*info).glyph_data_for_glyph_at_index(glyph as usize); 2874 | let bbox = glyph_data.bitmap_box( 2875 | scale * (*spc).h_oversample as f32, 2876 | scale * (*spc).v_oversample as f32).unwrap_or_default(); 2877 | 2878 | make_glyph_bitmap_subpixel(info, 2879 | (*spc).pixels.offset((*r).x + (*r).y*(*spc).stride_in_bytes), 2880 | (*r).w - (*spc).h_oversample as isize +1, 2881 | (*r).h - (*spc).v_oversample as isize +1, 2882 | (*spc).stride_in_bytes, 2883 | scale * (*spc).h_oversample as f32, 2884 | scale * (*spc).v_oversample as f32, 2885 | 0.0,0.0, 2886 | glyph); 2887 | 2888 | if (*spc).h_oversample > 1 { 2889 | h_prefilter((*spc).pixels.offset((*r).x + (*r).y*(*spc).stride_in_bytes), 2890 | (*r).w, (*r).h, (*spc).stride_in_bytes, 2891 | (*spc).h_oversample); 2892 | } 2893 | 2894 | if (*spc).v_oversample > 1 { 2895 | v_prefilter((*spc).pixels.offset((*r).x + (*r).y*(*spc).stride_in_bytes), 2896 | (*r).w, (*r).h, (*spc).stride_in_bytes, 2897 | (*spc).v_oversample); 2898 | } 2899 | 2900 | assert!(glyph >= 0); 2901 | let metric = (*info).hmtx.hmetric_for_glyph_at_index(glyph as usize); 2902 | 2903 | (*bc).x0 = (*r).x as u16; 2904 | (*bc).y0 = (*r).y as u16; 2905 | (*bc).x1 = ((*r).x + (*r).w) as u16; 2906 | (*bc).y1 = ((*r).y + (*r).h) as u16; 2907 | (*bc).xadvance = scale * metric.advance_width as f32; 2908 | (*bc).xoff = bbox.x0 as f32 * recip_h + sub_x; 2909 | (*bc).yoff = bbox.y0 as f32 * recip_v + sub_y; 2910 | (*bc).xoff2 = (bbox.x0 as isize + (*r).w) as f32 * recip_h + sub_x; 2911 | (*bc).yoff2 = (bbox.y0 as isize + (*r).h) as f32 * recip_v + sub_y; 2912 | } else { 2913 | return_value = 0; // if any fail, report failure 2914 | } 2915 | 2916 | k += 1; 2917 | } 2918 | } 2919 | 2920 | // restore original values 2921 | (*spc).h_oversample = old_h_over as usize; 2922 | (*spc).v_oversample = old_v_over as usize; 2923 | 2924 | return return_value; 2925 | } 2926 | 2927 | pub unsafe fn pack_font_ranges_pack_rects( 2928 | spc: *mut PackContext, 2929 | rects: *mut Rect, 2930 | num_rects: isize) 2931 | { 2932 | stbrp_pack_rects((*spc).pack_info as *mut Context, rects, num_rects); 2933 | } 2934 | 2935 | // Creates character bitmaps from multiple ranges of characters stored in 2936 | // ranges. This will usually create a better-packed bitmap than multiple 2937 | // calls to stbtt_PackFontRange. Note that you can call this multiple 2938 | // times within a single PackBegin/PackEnd. 2939 | pub unsafe fn pack_font_ranges( 2940 | spc: *mut PackContext, 2941 | fontdata: &[u8], 2942 | font_index: isize, 2943 | ranges: *mut PackRange, 2944 | num_ranges: isize 2945 | ) -> Result 2946 | { 2947 | let mut n: isize; 2948 | //stbrp_context *context = (stbrp_context *) spc->pack_info; 2949 | let rects: *mut Rect; 2950 | 2951 | // flag all characters as NOT packed 2952 | for i in 0..num_ranges { 2953 | for j in 0..(*ranges.offset(i)).num_chars { 2954 | (*(*ranges.offset(i)).chardata_for_range.offset(j)).x0 = 0; 2955 | (*(*ranges.offset(i)).chardata_for_range.offset(j)).y0 = 0; 2956 | (*(*ranges.offset(i)).chardata_for_range.offset(j)).x1 = 0; 2957 | (*(*ranges.offset(i)).chardata_for_range.offset(j)).y1 = 0; 2958 | } 2959 | } 2960 | 2961 | n = 0; 2962 | for i in 0..num_ranges { 2963 | n += (*ranges.offset(i)).num_chars; 2964 | } 2965 | 2966 | rects = STBTT_malloc!(size_of::() * n as usize) 2967 | as *mut Rect; 2968 | if rects == null_mut() { 2969 | return Ok(0); 2970 | } 2971 | 2972 | let mut info = try!(FontInfo::new_with_offset(fontdata, get_font_offset_for_index(fontdata.as_ptr(),font_index) as usize)); 2973 | 2974 | n = pack_font_ranges_gather_rects(spc, &mut info, ranges, num_ranges, rects); 2975 | 2976 | pack_font_ranges_pack_rects(spc, rects, n); 2977 | 2978 | let return_value = pack_font_ranges_render_into_rects(spc, &mut info, ranges, num_ranges, rects); 2979 | 2980 | STBTT_free!(rects as *mut c_void); 2981 | return Ok(return_value); 2982 | } 2983 | 2984 | // Creates character bitmaps from the font_index'th font found in fontdata (use 2985 | // font_index=0 if you don't know what that is). It creates num_chars_in_range 2986 | // bitmaps for characters with unicode values starting at first_unicode_char_in_range 2987 | // and increasing. Data for how to render them is stored in chardata_for_range; 2988 | // pass these to stbtt_GetPackedQuad to get back renderable quads. 2989 | // 2990 | // font_size is the full height of the character from ascender to descender, 2991 | // as computed by stbtt_ScaleForPixelHeight. To use a point size as computed 2992 | // by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() 2993 | // and pass that result as 'font_size': 2994 | // ..., 20 , ... // font max minus min y is 20 pixels tall 2995 | // ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall 2996 | pub unsafe fn pack_font_range( 2997 | spc: *mut PackContext, 2998 | fontdata: &[u8], 2999 | font_index: isize, 3000 | font_size: f32, 3001 | first_unicode_codepoint_in_range: isize, 3002 | num_chars_in_range: isize, 3003 | chardata_for_range: *mut PackedChar 3004 | ) -> Result { 3005 | let mut range: PackRange = PackRange { 3006 | first_unicode_codepoint_in_range: first_unicode_codepoint_in_range, 3007 | array_of_unicode_codepoints: null(), 3008 | num_chars: num_chars_in_range, 3009 | chardata_for_range: chardata_for_range, 3010 | font_size: font_size, 3011 | v_oversample: 0, 3012 | h_oversample: 0, 3013 | }; 3014 | pack_font_ranges(spc, fontdata, font_index, &mut range, 1) 3015 | } 3016 | 3017 | pub unsafe fn get_packed_quad( 3018 | chardata: *mut PackedChar, 3019 | pw: isize, 3020 | ph: isize, 3021 | // character to display 3022 | char_index: isize, 3023 | // pointers to current position in screen pixel space 3024 | xpos: *mut f32, 3025 | ypos: *mut f32, 3026 | // output: quad to draw 3027 | q: *mut AlignedQuad, 3028 | align_to_integer: isize 3029 | ) { 3030 | let ipw: f32 = 1.0 / pw as f32; 3031 | let iph: f32 = 1.0 / ph as f32; 3032 | let b: *const PackedChar = chardata.offset(char_index); 3033 | 3034 | if align_to_integer != 0 { 3035 | let x = ((*xpos + (*b).xoff) + 0.5).floor(); 3036 | let y = ((*ypos + (*b).yoff) + 0.5).floor(); 3037 | (*q).x0 = x; 3038 | (*q).y0 = y; 3039 | (*q).x1 = x + (*b).xoff2 - (*b).xoff; 3040 | (*q).y1 = y + (*b).yoff2 - (*b).yoff; 3041 | } else { 3042 | (*q).x0 = *xpos + (*b).xoff; 3043 | (*q).y0 = *ypos + (*b).yoff; 3044 | (*q).x1 = *xpos + (*b).xoff2; 3045 | (*q).y1 = *ypos + (*b).yoff2; 3046 | } 3047 | 3048 | (*q).s0 = (*b).x0 as f32 * ipw; 3049 | (*q).t0 = (*b).y0 as f32 * iph; 3050 | (*q).s1 = (*b).x1 as f32 * ipw; 3051 | (*q).t1 = (*b).y1 as f32 * iph; 3052 | 3053 | *xpos += (*b).xadvance; 3054 | } 3055 | 3056 | 3057 | ////////////////////////////////////////////////////////////////////////////// 3058 | // 3059 | // font name matching -- recommended not to use this 3060 | // 3061 | 3062 | // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string 3063 | pub unsafe fn compare_utf8_to_utf16_bigendian_prefix( 3064 | s1: *const u8, 3065 | len1: i32, 3066 | mut s2: *const u8, 3067 | mut len2: i32 3068 | ) -> i32 { 3069 | let mut i: i32 =0; 3070 | 3071 | // convert utf16 to utf8 and compare the results while converting 3072 | while len2 != 0 { 3073 | let ch: u16 = *s2.offset(0) as u16 *256 + *s2.offset(1) as u16; 3074 | if ch < 0x80 { 3075 | if i >= len1 { return -1; } 3076 | if *s1.offset(i as isize) != ch as u8 { return -1; } 3077 | i += 1; 3078 | } else if ch < 0x800 { 3079 | if i+1 >= len1 { return -1; } 3080 | if *s1.offset(i as isize) != (0xc0 + (ch >> 6)) as u8 { return -1; } 3081 | i += 1; 3082 | if *s1.offset(i as isize) != (0x80 + (ch & 0x3f)) as u8 { return -1; } 3083 | i += 1; 3084 | } else if ch >= 0xd800 && ch < 0xdc00 { 3085 | let c: u32; 3086 | let ch2: u16 = *s2.offset(2) as u16 *256 + *s2.offset(3) as u16; 3087 | if i+3 >= len1 { return -1; } 3088 | c = ((ch - 0xd800) << 10) as u32 + (ch2 - 0xdc00) as u32 + 0x10000; 3089 | if *s1.offset(i as isize) != (0xf0 + (c >> 18)) as u8 { return -1; } 3090 | i += 1; 3091 | if *s1.offset(i as isize) != (0x80 + ((c >> 12) & 0x3f)) as u8 { return -1; } 3092 | i += 1; 3093 | if *s1.offset(i as isize) != (0x80 + ((c >> 6) & 0x3f)) as u8 { return -1; } 3094 | i += 1; 3095 | if *s1.offset(i as isize) != (0x80 + ((c ) & 0x3f)) as u8 { return -1; } 3096 | i += 1; 3097 | s2 = s2.offset(2); // plus another 2 below 3098 | len2 -= 2; 3099 | } else if ch >= 0xdc00 && ch < 0xe000 { 3100 | return -1; 3101 | } else { 3102 | if i+2 >= len1 { return -1; } 3103 | if *s1.offset(i as isize) != (0xe0 + (ch >> 12)) as u8 { return -1; } 3104 | i += 1; 3105 | if *s1.offset(i as isize) != (0x80 + ((ch >> 6) & 0x3f)) as u8 { return -1; } 3106 | i += 1; 3107 | if *s1.offset(i as isize) != (0x80 + ((ch ) & 0x3f)) as u8 { return -1; } 3108 | i += 1; 3109 | } 3110 | s2 = s2.offset(2); 3111 | len2 -= 2; 3112 | } 3113 | return i; 3114 | } 3115 | 3116 | // returns 1/0 whether the first string interpreted as utf8 is identical to 3117 | // the second string interpreted as big-endian utf16... useful for strings from next func 3118 | pub unsafe fn compare_utf8_to_utf16_bigendian( 3119 | s1: *const u8, 3120 | len1: isize, 3121 | s2: *const u8, 3122 | len2: isize 3123 | ) -> isize { 3124 | return (len1 == compare_utf8_to_utf16_bigendian_prefix( 3125 | s1 as *const u8, len1 as i32, s2 as *const u8, len2 as i32) as isize) as isize; 3126 | } 3127 | 3128 | // returns the string (which may be big-endian double byte, e.g. for unicode) 3129 | // and puts the length in bytes in *length. 3130 | // 3131 | // some of the values for the IDs are below; for more see the truetype spec: 3132 | // http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html 3133 | // http://www.microsoft.com/typography/otspec/name.htm 3134 | // 3135 | // returns results in whatever encoding you request... but note that 2-byte encodings 3136 | // will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare 3137 | pub unsafe fn get_font_name_string( 3138 | font: *const FontInfo, 3139 | length: *mut isize, 3140 | platform_id: isize, 3141 | encoding_id: isize, 3142 | language_id: isize, 3143 | name_id: isize 3144 | ) -> *const u8 { 3145 | let count: i32; 3146 | let string_offset: i32; 3147 | let fc: *const u8 = (*font).data.as_ptr(); 3148 | let offset: u32 = (*font).fontstart as u32; 3149 | let nm: u32 = utils::find_table(fc, offset, b"name"); 3150 | if nm == 0 { return null(); } 3151 | 3152 | count = ttUSHORT!(fc.offset(nm as isize +2)) as i32; 3153 | string_offset = nm as i32 + ttUSHORT!(fc.offset(nm as isize +4)) as i32; 3154 | for i in 0..count as u32 { 3155 | let loc: u32 = nm + 6 + 12 * i; 3156 | if platform_id == ttUSHORT!(fc.offset(loc as isize +0)) as isize && encoding_id == ttUSHORT!(fc.offset(loc as isize +2)) as isize 3157 | && language_id == ttUSHORT!(fc.offset(loc as isize +4)) as isize && name_id == ttUSHORT!(fc.offset(loc as isize +6)) as isize { 3158 | *length = ttUSHORT!(fc.offset(loc as isize +8)) as isize; 3159 | return (fc.offset(string_offset as isize +ttUSHORT!(fc.offset(loc as isize +10)) as isize)) as *const u8; 3160 | } 3161 | } 3162 | return null(); 3163 | } 3164 | 3165 | pub unsafe fn matchpair( 3166 | fc: *mut u8, 3167 | nm: u32, 3168 | name: *mut u8, 3169 | nlen: i32, 3170 | target_id: i32, 3171 | next_id: i32 3172 | ) -> isize { 3173 | let count: u32 = ttUSHORT!(fc.offset(nm as isize +2)) as u32; 3174 | let string_offset: i32 = nm as i32 + ttUSHORT!(fc.offset(nm as isize +4)) as i32; 3175 | 3176 | for i in 0..count as u32 { 3177 | let loc: u32 = nm + 6 + 12 * i; 3178 | let id: i32 = ttUSHORT!(fc.offset(loc as isize +6)) as i32; 3179 | if id == target_id { 3180 | // find the encoding 3181 | let platform: i32 = ttUSHORT!(fc.offset(loc as isize +0)) as i32; 3182 | let encoding: i32 = ttUSHORT!(fc.offset(loc as isize +2)) as i32; 3183 | let language: i32 = ttUSHORT!(fc.offset(loc as isize +4)) as i32; 3184 | 3185 | // is this a Unicode encoding? 3186 | if platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10) { 3187 | let mut slen: i32 = ttUSHORT!(fc.offset(loc as isize +8)) as i32; 3188 | let mut off: i32 = ttUSHORT!(fc.offset(loc as isize +10)) as i32; 3189 | 3190 | // check if there's a prefix match 3191 | let mut matchlen: i32 = compare_utf8_to_utf16_bigendian_prefix( 3192 | name, nlen, fc.offset(string_offset as isize + off as isize),slen); 3193 | if matchlen >= 0 { 3194 | // check for target_id+1 immediately following, with same encoding & language 3195 | if i+1 < count && ttUSHORT!(fc.offset(loc as isize +12+6)) == next_id as u16 3196 | && ttUSHORT!(fc.offset(loc as isize +12)) == platform as u16 3197 | && ttUSHORT!(fc.offset(loc as isize +12+2)) == encoding as u16 3198 | && ttUSHORT!(fc.offset(loc as isize +12+4)) == language as u16 { 3199 | slen = ttUSHORT!(fc.offset(loc as isize +12+8)) as i32; 3200 | off = ttUSHORT!(fc.offset(loc as isize +12+10)) as i32; 3201 | if slen == 0 { 3202 | if matchlen == nlen { 3203 | return 1; 3204 | } 3205 | } else if matchlen < nlen && *name.offset(matchlen as isize) == ' ' as u8 { 3206 | matchlen += 1; 3207 | if compare_utf8_to_utf16_bigendian( 3208 | (name.offset(matchlen as isize)) as *mut u8, (nlen - matchlen) as isize, 3209 | (fc.offset(string_offset as isize + off as isize)) as *mut u8,slen as isize) != 0 { 3210 | return 1; 3211 | } 3212 | } 3213 | } else { 3214 | // if nothing immediately following 3215 | if matchlen == nlen { 3216 | return 1; 3217 | } 3218 | } 3219 | } 3220 | } 3221 | 3222 | // @TODO handle other encodings 3223 | } 3224 | } 3225 | return 0; 3226 | } 3227 | 3228 | pub unsafe fn matches( 3229 | fc: *mut u8, 3230 | offset: u32, 3231 | name: *mut u8, 3232 | flags: i32 3233 | ) -> isize { 3234 | let nlen: i32 = STBTT_strlen(name as *mut c_char) as i32; 3235 | let nm: u32; 3236 | let hd: u32; 3237 | if isfont(fc.offset(offset as isize)) == 0 { return 0; } 3238 | 3239 | // check italics/bold/underline flags in macStyle... 3240 | if flags != 0 { 3241 | hd = utils::find_table(fc, offset, b"head"); 3242 | if (ttUSHORT!(fc.offset(hd as isize + 44)) & 7) != (flags as u16 & 7) { return 0; } 3243 | } 3244 | 3245 | nm = utils::find_table(fc, offset, b"name"); 3246 | if nm == 0 { return 0; } 3247 | 3248 | if flags != 0 { 3249 | // if we checked the macStyle flags, then just check the family and ignore the subfamily 3250 | if matchpair(fc, nm, name, nlen, 16, -1) != 0 { return 1; } 3251 | if matchpair(fc, nm, name, nlen, 1, -1) != 0 { return 1; } 3252 | if matchpair(fc, nm, name, nlen, 3, -1) != 0 { return 1; } 3253 | } else { 3254 | if matchpair(fc, nm, name, nlen, 16, 17) != 0 { return 1; } 3255 | if matchpair(fc, nm, name, nlen, 1, 2) != 0 { return 1; } 3256 | if matchpair(fc, nm, name, nlen, 3, -1) != 0 { return 1; } 3257 | } 3258 | 3259 | return 0; 3260 | } 3261 | 3262 | // returns the offset (not index) of the font that matches, or -1 if none 3263 | // if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". 3264 | // if you use any other flag, use a font name like "Arial"; this checks 3265 | // the 'macStyle' header field; i don't know if fonts set this consistently 3266 | pub unsafe fn find_matching_font( 3267 | font_collection: *const u8, 3268 | name_utf8: *const u8, 3269 | flags: i32 3270 | ) -> i32 { 3271 | for i in 0.. { 3272 | let off: i32 = get_font_offset_for_index(font_collection, i); 3273 | if off < 0 { return off; } 3274 | if matches(font_collection as *mut u8, 3275 | off as u32, name_utf8 as *mut u8, flags) != 0 { 3276 | return off; 3277 | } 3278 | } 3279 | return 0; 3280 | } 3281 | 3282 | // #endif // STB_TRUETYPE_IMPLEMENTATION 3283 | 3284 | 3285 | // FULL VERSION HISTORY 3286 | // 3287 | // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges 3288 | // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; 3289 | // allow PackFontRanges to pack and render in separate phases; 3290 | // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); 3291 | // fixed an assert() bug in the new rasterizer 3292 | // replace assert() with STBTT_assert() in new rasterizer 3293 | // 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) 3294 | // also more precise AA rasterizer, except if shapes overlap 3295 | // remove need for STBTT_sort 3296 | // 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC 3297 | // 1.04 (2015-04-15) typo in example 3298 | // 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes 3299 | // 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ 3300 | // 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match 3301 | // non-oversampled; STBTT_POINT_SIZE for packed case only 3302 | // 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling 3303 | // 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) 3304 | // 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platform_id 3305 | // 0.8b (2014-07-07) fix a warning 3306 | // 0.8 (2014-05-25) fix a few more warnings 3307 | // 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back 3308 | // 0.6c (2012-07-24) improve documentation 3309 | // 0.6b (2012-07-20) fix a few more warnings 3310 | // 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, 3311 | // stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty 3312 | // 0.5 (2011-12-09) bugfixes: 3313 | // subpixel glyph renderer computed wrong bounding box 3314 | // first vertex of shape can be off-curve (FreeSans) 3315 | // 0.4b (2011-12-03) fixed an error in the font baking example 3316 | // 0.4 (2011-12-01) kerning, subpixel rendering (tor) 3317 | // bugfixes for: 3318 | // codepoint-to-glyph conversion using table fmt=12 3319 | // codepoint-to-glyph conversion using table fmt=4 3320 | // stbtt_GetBakedQuad with non-square texture (Zer) 3321 | // updated Hello World! sample to use kerning and subpixel 3322 | // fixed some warnings 3323 | // 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) 3324 | // userdata, malloc-from-userdata, non-zero fill (stb) 3325 | // 0.2 (2009-03-11) Fix unsigned/signed char warnings 3326 | // 0.1 (2009-03-09) First public release 3327 | // 3328 | -------------------------------------------------------------------------------- /src/tables/cmap.rs: -------------------------------------------------------------------------------- 1 | 2 | use Error; 3 | use Result; 4 | use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; 5 | use utils::{read_u16_from_raw_data, read_i16_from_raw_data}; 6 | 7 | #[derive(Debug)] 8 | pub struct CMAP { 9 | encoding_subtable: EncodingSubtable, 10 | format: Format, 11 | } 12 | 13 | /// A character code mapping table. 14 | /// 15 | /// The `cmap` table maps character codes to glyph indices. 16 | impl CMAP { 17 | /// Returns `cmap` font table. 18 | /// 19 | /// Attempts to read `data` starting from `offset` position. 20 | /// 21 | /// # Errors 22 | /// Returns error in the following cases: 23 | /// - if there is not enough data to read 24 | /// - if encoding subtable is not supported 25 | /// - if format is not supported 26 | pub fn from_data(data: &[u8], offset: usize) -> Result { 27 | 28 | if offset >= data.len() || offset + 4 > data.len() { 29 | return Err(Error::Malformed); 30 | } 31 | 32 | // +2 skip version field. 33 | let number_subtables = BigEndian::read_u16(&data[offset + 2..]) as usize; 34 | let subtables_data = &data[offset + 4..]; 35 | if number_subtables * (2 + 2 + 4) > data.len() { 36 | return Err(Error::Malformed); 37 | } 38 | 39 | let mut encoding_subtables: Vec<_> = (0..number_subtables).filter_map(|n| { 40 | let z = n as usize * 8; 41 | let platform_id = BigEndian::read_u16(&subtables_data[z + 0..]); 42 | let platform_specific_id = BigEndian::read_u16(&subtables_data[z + 2..]); 43 | let offset = BigEndian::read_u32(&subtables_data[z + 4..]); 44 | Platform::new(platform_id, platform_specific_id).map(|platform| { 45 | EncodingSubtable { platform: platform, offset: offset} 46 | }) 47 | }).collect(); 48 | 49 | encoding_subtables.sort_by(|a, b| a.order().cmp(&b.order())); 50 | 51 | if encoding_subtables.is_empty() { 52 | return Err(Error::CMAPEncodingSubtableIsNotSupported); 53 | } 54 | 55 | let encoding_subtable = encoding_subtables.first().unwrap().clone(); 56 | let format = try!(Format::from_data(data, offset + encoding_subtable.offset as usize)); 57 | 58 | Ok(CMAP { 59 | encoding_subtable: encoding_subtable, 60 | format: format, 61 | }) 62 | } 63 | 64 | /// Returns an index for character `code` in a `loca` font table. 65 | pub fn index_for_code(&self, code: usize) -> Option { 66 | self.format.index_for_code(code) 67 | } 68 | } 69 | 70 | #[derive(Debug, PartialEq, Clone, Copy)] 71 | struct EncodingSubtable { 72 | platform: Platform, 73 | offset: u32, 74 | } 75 | 76 | impl EncodingSubtable { 77 | /// Defines an order in which the encoding subtables should be selected. 78 | fn order(&self) -> u32 { 79 | use self::Platform::*; 80 | use self::UnicodeEncodingId::*; 81 | use self::MicrosoftEncodingId::*; 82 | 83 | match self.platform { 84 | Unicode(Unicode20) => 0, 85 | Unicode(Unicode20BMPOnly) => 1, 86 | Unicode(Version11Semantics) => 1, 87 | Unicode(DefaultSemantics) => 1, 88 | Microsoft(UnicodeUCS4) => 2, 89 | Microsoft(UnicodeUCS2) => 3, 90 | Microsoft(Symbol) => 4, 91 | _ => 10, 92 | } 93 | } 94 | } 95 | 96 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 97 | enum Platform { 98 | Unicode(UnicodeEncodingId), 99 | Microsoft(MicrosoftEncodingId), 100 | } 101 | 102 | impl Platform { 103 | fn new(platform_id: u16, platform_specific_id: u16) -> Option { 104 | use self::Platform::*; 105 | use self::UnicodeEncodingId::*; 106 | use self::MicrosoftEncodingId::*; 107 | 108 | match platform_id { 109 | 0 => match platform_specific_id { 110 | 0 => Some(Unicode(DefaultSemantics)), 111 | 1 => Some(Unicode(Version11Semantics)), 112 | 3 => Some(Unicode(Unicode20BMPOnly)), 113 | 4 => Some(Unicode(Unicode20)), 114 | 5 => Some(Unicode(UnicodeVariationSequences)), 115 | 6 => Some(Unicode(FullUnicodeCoverage)), 116 | _ => None, 117 | }, 118 | 3 => match platform_specific_id { 119 | 0 => Some(Microsoft(Symbol)), 120 | 1 => Some(Microsoft(UnicodeUCS2)), 121 | 2 => Some(Microsoft(ShiftJIS)), 122 | 3 => Some(Microsoft(PRC)), 123 | 4 => Some(Microsoft(BigFive)), 124 | 5 => Some(Microsoft(Johab)), 125 | 10 => Some(Microsoft(UnicodeUCS4)), 126 | _ => None, 127 | }, 128 | _ => None, 129 | } 130 | } 131 | } 132 | 133 | #[repr(u16)] 134 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 135 | enum UnicodeEncodingId { 136 | DefaultSemantics = 0, 137 | Version11Semantics = 1, 138 | Unicode20BMPOnly = 3, 139 | Unicode20 = 4, 140 | UnicodeVariationSequences = 5, 141 | FullUnicodeCoverage = 6, 142 | } 143 | 144 | #[repr(u16)] 145 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 146 | enum MicrosoftEncodingId { 147 | Symbol = 0, 148 | UnicodeUCS2 = 1, 149 | ShiftJIS = 2, 150 | PRC = 3, 151 | BigFive = 4, 152 | Johab = 5, 153 | UnicodeUCS4 = 10 154 | } 155 | 156 | #[derive(Debug)] 157 | enum Format { 158 | F0(Format0), 159 | F4(Format4), 160 | F6(Format6), 161 | F1213(Format1213), 162 | } 163 | 164 | impl Format { 165 | fn from_data(data: &[u8], offset: usize) -> Result { 166 | use self::Format::*; 167 | if offset + 2 > data.len() { 168 | return Err(Error::Malformed); 169 | } 170 | 171 | let format = BigEndian::read_u16(&data[offset..]); 172 | match format { 173 | 0 => Ok(F0(try!(Format0::from_data(data, offset)))), 174 | 4 => Ok(F4(try!(Format4::from_data(data, offset)))), 175 | 6 => Ok(F6(try!(Format6::from_data(data, offset)))), 176 | 12 | 13 => Ok(F1213(try!(Format1213::from_data(data, offset)))), 177 | _ => Err(Error::CMAPFormatIsNotSupported), 178 | } 179 | } 180 | 181 | fn index_for_code(&self, code: usize) -> Option { 182 | use self::Format::*; 183 | match *self { 184 | F0(ref f) => f.index_for_code(code), 185 | F4(ref f) => f.index_for_code(code), 186 | F6(ref f) => f.index_for_code(code), 187 | F1213(ref f) => f.index_for_code(code), 188 | } 189 | } 190 | } 191 | 192 | #[derive(Debug)] 193 | struct Format0 { 194 | format: u16, 195 | length: u16, 196 | language: u16, 197 | glyph_index_array: Vec, 198 | } 199 | 200 | impl Format0 { 201 | fn from_data(data: &[u8], offset: usize) -> Result { 202 | const SIZE: usize = 262; 203 | if offset + SIZE > data.len() { 204 | return Err(Error::Malformed); 205 | } 206 | 207 | let format = BigEndian::read_u16(&data[offset..]); 208 | let length = BigEndian::read_u16(&data[offset + 2..]); 209 | 210 | if length as usize != SIZE { 211 | return Err(Error::Malformed); 212 | } 213 | let language = BigEndian::read_u16(&data[offset + 4..]); 214 | 215 | Ok(Format0 { 216 | format: format, 217 | length: length, 218 | language: language, 219 | glyph_index_array: data[offset + 6..SIZE].to_owned(), 220 | }) 221 | } 222 | 223 | fn index_for_code(&self, code: usize) -> Option { 224 | self.glyph_index_array.get(code).map(|&i| i as usize) 225 | } 226 | } 227 | 228 | #[derive(Debug, Default)] 229 | struct Format4 { 230 | format: u16, 231 | length: u16, 232 | language: u16, 233 | seg_count_x2: u16, 234 | search_range: u16, 235 | entry_selector: u16, 236 | range_shift: u16, 237 | end_code: Vec, 238 | reserved_pad: u16, 239 | start_code: Vec, 240 | id_delta: Vec, 241 | id_range_offset: Vec, 242 | glyph_index_array: Vec, 243 | } 244 | 245 | impl Format4 { 246 | fn from_data(data: &[u8], offset: usize) -> Result { 247 | if offset + 2 * 8 > data.len() { 248 | return Err(Error::Malformed); 249 | } 250 | 251 | let mut z = offset; 252 | let mut f = Format4::default(); 253 | f.format = BigEndian::read_u16(&data[z..]); 254 | z += 2; 255 | f.length = BigEndian::read_u16(&data[z..]); 256 | z += 2; 257 | f.language = BigEndian::read_u16(&data[z..]); 258 | z += 2; 259 | f.seg_count_x2 = BigEndian::read_u16(&data[z..]); 260 | z += 2; 261 | f.search_range = BigEndian::read_u16(&data[z..]); 262 | z += 2; 263 | f.entry_selector = BigEndian::read_u16(&data[z..]); 264 | z += 2; 265 | f.range_shift = BigEndian::read_u16(&data[z..]); 266 | z += 2; 267 | 268 | 269 | // Check that length is correct. 270 | if (f.length as usize) < 2 * 8 + f.seg_count_x2 as usize * 4 { 271 | return Err(Error::Malformed); 272 | } 273 | 274 | f.end_code = data[z..z + f.seg_count_x2 as usize].to_owned(); 275 | z += f.seg_count_x2 as usize; 276 | f.reserved_pad = BigEndian::read_u16(&data[z..]); 277 | z += 2; 278 | f.start_code = data[z..z + f.seg_count_x2 as usize].to_owned(); 279 | z += f.seg_count_x2 as usize; 280 | f.id_delta = data[z..z + f.seg_count_x2 as usize].to_owned(); 281 | z += f.seg_count_x2 as usize; 282 | f.id_range_offset = data[z..z + f.seg_count_x2 as usize].to_owned(); 283 | z += f.seg_count_x2 as usize; 284 | f.glyph_index_array = data[z..z + f.length as usize].to_owned(); 285 | 286 | Ok(f) 287 | } 288 | 289 | fn index_for_code(&self, code: usize) -> Option { 290 | if code >= 0xffff { 291 | return None; 292 | } 293 | 294 | let mut r = (None, None); // Just to reduce indentation. 295 | for i in 0..self.end_code.len() / 2 { 296 | if BigEndian::read_u16(&self.end_code[i * 2..]) as usize >= code { 297 | r = (self.segment_at_index(i), Some(i)); 298 | break; 299 | } 300 | } 301 | 302 | if let (Some(s), Some(i)) = r { 303 | if s.start_code <= code { 304 | if s.id_range_offset == 0 { 305 | // TODO: Investigate, probably should be: (id_delta + code) % 0xffff 306 | return Some((s.id_delta + code as isize) as usize); 307 | } 308 | let index = s.id_range_offset / 2 + (code - s.start_code) - (self.seg_count() - i); 309 | if let Some(glyph_id) = read_u16_from_raw_data(&self.glyph_index_array, index) { 310 | if glyph_id != 0 { 311 | return Some((glyph_id as isize + s.id_delta) as usize); 312 | } 313 | } 314 | } 315 | } 316 | 317 | None 318 | } 319 | 320 | fn seg_count(&self) -> usize { 321 | self.seg_count_x2 as usize / 2 322 | } 323 | 324 | fn segment_at_index(&self, i: usize) -> Option { 325 | let s = read_u16_from_raw_data(&self.start_code, i); 326 | let e = read_u16_from_raw_data(&self.end_code, i); 327 | let d = read_i16_from_raw_data(&self.id_delta, i); 328 | let r = read_u16_from_raw_data(&self.id_range_offset, i); 329 | if let (Some(s), Some(e), Some(d), Some(r)) = (s, e, d, r) { 330 | Some(Format4Segment { 331 | start_code: s as usize, 332 | end_code: e as usize, 333 | id_delta: d as isize, 334 | id_range_offset: r as usize, 335 | }) 336 | } else { 337 | None 338 | } 339 | } 340 | } 341 | 342 | #[derive(Debug)] 343 | struct Format4Segment { 344 | start_code: usize, 345 | end_code: usize, 346 | id_delta: isize, 347 | id_range_offset: usize, 348 | } 349 | 350 | #[derive(Debug)] 351 | struct Format6 { 352 | format: u16, 353 | length: u16, 354 | language: u16, 355 | first_code: u16, 356 | entry_count: u16, 357 | raw_glyph_index_array: Vec, 358 | } 359 | 360 | impl Format6 { 361 | fn from_data(data: &[u8], offset: usize) -> Result { 362 | if offset + 2 * 5 > data.len() { 363 | return Err(Error::Malformed); 364 | } 365 | 366 | let format = BigEndian::read_u16(&data[offset..]); 367 | let length = BigEndian::read_u16(&data[offset + 2..]); 368 | let language = BigEndian::read_u16(&data[offset + 4..]); 369 | let first_code = BigEndian::read_u16(&data[offset + 6..]); 370 | let entry_count = BigEndian::read_u16(&data[offset + 8..]); 371 | 372 | let size = entry_count as usize * 2; 373 | if offset + 2 * 5 + size > data.len() { 374 | return Err(Error::Malformed); 375 | } 376 | 377 | Ok(Format6 { 378 | format: format, 379 | length: length, 380 | language: language, 381 | first_code: first_code, 382 | entry_count: entry_count, 383 | raw_glyph_index_array: data[offset + 2 * 5..size].to_owned(), 384 | }) 385 | } 386 | 387 | fn index_for_code(&self, code: usize) -> Option { 388 | let first_code = self.first_code as usize; 389 | let entry_count = self.entry_count as usize; 390 | if code < first_code || code >= first_code + entry_count { 391 | None 392 | } else { 393 | let offset = (code - first_code) * 2; 394 | if offset >= self.raw_glyph_index_array.len() { 395 | None 396 | } else { 397 | Some(BigEndian::read_u16(&self.raw_glyph_index_array[offset..]) as usize) 398 | } 399 | } 400 | } 401 | } 402 | 403 | #[derive(Debug, Copy, Clone)] 404 | struct GroupFormat1213 { 405 | start_char_code: u32, 406 | end_char_code: u32, 407 | start_glyph_code: u32, 408 | } 409 | 410 | #[derive(Debug, Default)] 411 | struct Format1213 { 412 | format: u32, 413 | length: u32, 414 | language: u32, 415 | n_groups: u32, 416 | groups: Vec, 417 | } 418 | 419 | impl Format1213 { 420 | fn from_data(data: &[u8], offset: usize) -> Result { 421 | if offset + 4 * 4 > data.len() { 422 | return Err(Error::Malformed); 423 | } 424 | 425 | let mut f = Format1213::default(); 426 | f.format = BigEndian::read_u32(&data[offset..]); 427 | f.length = BigEndian::read_u32(&data[offset + 4..]); 428 | f.language = BigEndian::read_u32(&data[offset + 8..]); 429 | f.n_groups = BigEndian::read_u32(&data[offset + 12..]); 430 | 431 | if offset + f.n_groups as usize * 12 > data.len() { 432 | return Err(Error::Malformed); 433 | } 434 | 435 | let data = &data[offset + 4 * 4..]; 436 | for n in 0..f.n_groups { 437 | let z = n as usize * 3 * 4; 438 | let sc = BigEndian::read_u32(&data[z..]); 439 | let ec = BigEndian::read_u32(&data[z + 4..]); 440 | let sg = BigEndian::read_u32(&data[z + 8..]); 441 | f.groups.push(GroupFormat1213 { 442 | start_char_code: sc, 443 | end_char_code: ec, 444 | start_glyph_code: sg 445 | }); 446 | } 447 | 448 | Ok(f) 449 | } 450 | 451 | fn index_for_code(&self, code: usize) -> Option { 452 | use std::cmp::Ordering::*; 453 | 454 | let group = self.groups.binary_search_by(|group| { 455 | if code < group.start_char_code as usize { 456 | Greater 457 | } else if code > group.end_char_code as usize { 458 | Less 459 | } else { 460 | Equal 461 | } 462 | }).ok().map(|i| self.groups[i]); 463 | 464 | group.map(|group| { 465 | if self.format == 12 << 16 { // format 12.0 466 | code - group.start_char_code as usize + group.start_glyph_code as usize 467 | } else { 468 | group.start_glyph_code as usize 469 | } 470 | }) 471 | } 472 | } 473 | 474 | #[cfg(test)] 475 | mod tests { 476 | use super::*; 477 | use expectest::prelude::*; 478 | 479 | #[test] 480 | fn smoke() { 481 | let data = ::utils::read_file("tests/Tuffy_Bold.ttf"); 482 | let offset = ::utils::find_table_offset(&data, 0, b"cmap").unwrap().unwrap(); 483 | 484 | let cmap = CMAP::from_data(&data, offset).unwrap(); 485 | 486 | expect!(cmap.index_for_code('a' as usize)).to(be_some().value(68)); 487 | expect!(cmap.index_for_code('л' as usize)).to(be_some().value(487)); 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /src/tables/glyf.rs: -------------------------------------------------------------------------------- 1 | 2 | use Error; 3 | use Result; 4 | use types::BBox; 5 | use std::io::Cursor; 6 | use byteorder::{BigEndian, ReadBytesExt}; 7 | 8 | #[derive(Debug)] 9 | pub struct GLYF { 10 | bytes: Vec, 11 | } 12 | 13 | impl GLYF { 14 | pub fn from_data(data: &[u8], offset: usize, size: usize) -> Result { 15 | if offset + size > data.len() { 16 | return Err(Error::Malformed); 17 | } 18 | 19 | Ok(GLYF { 20 | bytes: data[offset..offset + size].to_owned(), 21 | }) 22 | } 23 | 24 | /// Returns instance of `GlyphData` starting from `offset` position. 25 | /// 26 | /// `offset` could be taken from the `loca` font table. 27 | pub fn glyph_data(&self, offset: usize) -> GlyphData { 28 | let z = if offset >= self.bytes.len() { 0 } else { offset }; 29 | GlyphData { bytes: &self.bytes[z..] } 30 | } 31 | } 32 | 33 | /// Contains data for the glyph. 34 | #[derive(Debug)] 35 | pub struct GlyphData<'a> { 36 | bytes: &'a [u8], 37 | } 38 | 39 | impl<'a> GlyphData<'a> { 40 | /// Returns the number of contours in the glyph. 41 | pub fn number_of_contours(&self) -> isize { 42 | Cursor::new(self.bytes).read_i16::().ok().unwrap_or(0) as isize 43 | } 44 | 45 | /// Returns `true` if nothing is drawn for this glyph. 46 | #[allow(dead_code)] 47 | pub fn is_empty(&self) -> bool { 48 | self.number_of_contours() == 0 49 | } 50 | 51 | /// Returns the bounding box of the glyph. 52 | #[allow(dead_code)] 53 | pub fn bounding_box(&self) -> Option { 54 | if self.bytes.len() < 5 * 2 { 55 | return None; 56 | } 57 | 58 | let mut cursor = Cursor::new(&self.bytes[2..]); 59 | let x0 = cursor.read_i16::().unwrap() as i32; 60 | let y0 = cursor.read_i16::().unwrap() as i32; 61 | let x1 = cursor.read_i16::().unwrap() as i32; 62 | let y1 = cursor.read_i16::().unwrap() as i32; 63 | Some(BBox { x0: x0, y0: y0, x1: x1, y1: y1 }) 64 | } 65 | 66 | /// Same as `bitmap_box`, but you can specify a subpixel shift 67 | /// for the character. 68 | pub fn bitmap_box_subpixel(&self, scale_x: f32, scale_y: f32, 69 | shift_x: f32, shift_y: f32) -> Option 70 | { 71 | self.bounding_box().map(|bbox| { 72 | // Move to integral bboxes (treating pixels as little squares, 73 | // what pixels get touched)? 74 | BBox { 75 | x0: (bbox.x0 as f32 * scale_x + shift_x).floor() as i32, 76 | y0: (-bbox.y1 as f32 * scale_y + shift_y).floor() as i32, 77 | x1: (bbox.x1 as f32 * scale_x + shift_x).ceil() as i32, 78 | y1: (-bbox.y0 as f32 * scale_y + shift_y).ceil() as i32, 79 | } 80 | }) 81 | } 82 | 83 | /// Returns the bbox of the bitmap centered around the glyph origin; so the 84 | /// bitmap width is x1-x0, height is y1-y0, and location to place 85 | /// the bitmap top left is (leftSideBearing*scale, y0). 86 | /// (Note that the bitmap uses y-increases-down, but the shape uses 87 | /// y-increases-up, so this is inverted.) 88 | pub fn bitmap_box(&self, scale_x: f32, scale_y: f32) -> Option { 89 | self.bitmap_box_subpixel(scale_x, scale_y, 0.0, 0.0) 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod tests { 95 | use super::*; 96 | use tables::{MAXP, HEAD, LOCA}; 97 | 98 | #[test] 99 | fn smoke() { 100 | let data = ::utils::read_file("tests/Tuffy_Bold.ttf"); 101 | let maxp_offset = ::utils::find_table_offset(&data, 0, b"maxp").unwrap().unwrap(); 102 | let glyphs = MAXP::from_data(&data, maxp_offset).unwrap().num_glyphs(); 103 | let head_offset = ::utils::find_table_offset(&data, 0, b"head").unwrap().unwrap(); 104 | let format = HEAD::from_data(&data, head_offset).unwrap().location_format(); 105 | let loca_offset = ::utils::find_table_offset(&data, 0, b"loca").unwrap().unwrap(); 106 | let loca = LOCA::from_data(&data, loca_offset, glyphs, format).unwrap(); 107 | 108 | let glyf_offset = ::utils::find_table_offset(&data, 0, b"glyf").unwrap().unwrap(); 109 | let _ = GLYF::from_data(&data, glyf_offset, loca.size_of_glyf_table()).unwrap(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/tables/head.rs: -------------------------------------------------------------------------------- 1 | 2 | use types::Fixed; 3 | use Error; 4 | use Result; 5 | use types::{BBox, LocationFormat}; 6 | use std::io::Cursor; 7 | use byteorder::{BigEndian, ReadBytesExt}; 8 | 9 | /// A font header. 10 | /// 11 | /// The 'head' table contains global information about the font. 12 | #[derive(Debug, Default)] 13 | pub struct HEAD { 14 | version: Fixed, 15 | font_revision: Fixed, 16 | check_sum_adjustment: u32, 17 | magic_number: u32, 18 | flags: u16, 19 | units_per_em: u16, 20 | created: i64, 21 | modified: i64, 22 | x_min: i16, 23 | y_min: i16, 24 | x_max: i16, 25 | y_max: i16, 26 | mac_style: u16, 27 | lowest_rec_ppem: u16, 28 | font_direction_hint: i16, 29 | index_to_loc_format: u16, // In TrueType Reference Manual this field marked 30 | // as `i16` but we changed it to `u16` 31 | // since it can hold only 0 or 1. 32 | glyph_data_format: i16, 33 | } 34 | 35 | impl HEAD { 36 | /// Returns `head` font table. 37 | /// 38 | /// Attempts to read `data` starting from `offset` position. 39 | /// 40 | /// # Errors 41 | /// Returns error if there is not enough data to read or version of 42 | /// the `head` font table is not supported. 43 | pub fn from_data(data: &[u8], offset: usize) -> Result { 44 | if offset >= data.len() { 45 | return Err(Error::Malformed); 46 | } 47 | 48 | let mut cursor = Cursor::new(&data[offset..]); 49 | let version = Fixed(try!(cursor.read_i32::())); 50 | if version != Fixed(0x00010000) { 51 | return Err(Error::HEADVersionIsNotSupported); 52 | } 53 | 54 | let mut head = HEAD::default(); 55 | head.version = version; 56 | head.font_revision = Fixed(try!(cursor.read_i32::())); 57 | head.check_sum_adjustment = try!(cursor.read_u32::()); 58 | head.magic_number = try!(cursor.read_u32::()); 59 | head.flags = try!(cursor.read_u16::()); 60 | head.units_per_em = try!(cursor.read_u16::()); 61 | head.created = try!(cursor.read_i64::()); 62 | head.modified = try!(cursor.read_i64::()); 63 | head.x_min = try!(cursor.read_i16::()); 64 | head.y_min = try!(cursor.read_i16::()); 65 | head.x_max = try!(cursor.read_i16::()); 66 | head.y_max = try!(cursor.read_i16::()); 67 | head.mac_style = try!(cursor.read_u16::()); 68 | head.lowest_rec_ppem = try!(cursor.read_u16::()); 69 | head.font_direction_hint = try!(cursor.read_i16::()); 70 | head.index_to_loc_format = try!(cursor.read_u16::()); 71 | if head.index_to_loc_format > 1 { 72 | return Err(Error::UnknownLocationFormat); 73 | } 74 | head.glyph_data_format = try!(cursor.read_i16::()); 75 | 76 | Ok(head) 77 | } 78 | 79 | #[cfg(test)] 80 | fn bytes(&self) -> Vec { 81 | use byteorder::WriteBytesExt; 82 | 83 | let mut data = vec![]; 84 | data.write_i32::(self.version.0).unwrap(); 85 | data.write_i32::(self.font_revision.0).unwrap(); 86 | data.write_u32::(self.check_sum_adjustment).unwrap(); 87 | data.write_u32::(self.magic_number).unwrap(); 88 | data.write_u16::(self.flags).unwrap(); 89 | data.write_u16::(self.units_per_em).unwrap(); 90 | data.write_i64::(self.created).unwrap(); 91 | data.write_i64::(self.modified).unwrap(); 92 | data.write_i16::(self.x_min).unwrap(); 93 | data.write_i16::(self.y_min).unwrap(); 94 | data.write_i16::(self.x_max).unwrap(); 95 | data.write_i16::(self.y_max).unwrap(); 96 | data.write_u16::(self.mac_style).unwrap(); 97 | data.write_u16::(self.lowest_rec_ppem).unwrap(); 98 | data.write_i16::(self.font_direction_hint).unwrap(); 99 | data.write_u16::(self.index_to_loc_format).unwrap(); 100 | data.write_i16::(self.glyph_data_format).unwrap(); 101 | data 102 | } 103 | 104 | /// Returns the number of units per em for the font. 105 | /// 106 | /// This value should be a power of 2. Its range is from 64 through 16384. 107 | pub fn units_per_em(&self) -> f32 { 108 | self.units_per_em as f32 109 | } 110 | 111 | /// Returns the bounding box around all possible characters. 112 | #[allow(dead_code)] 113 | pub fn bounding_box(&self) -> BBox { 114 | BBox { 115 | x0: self.x_min as i32, 116 | y0: self.y_min as i32, 117 | x1: self.x_max as i32, 118 | y1: self.y_max as i32 119 | } 120 | } 121 | 122 | /// Returns the type of offset format used in the index to loc ('loca') table. 123 | pub fn location_format(&self) -> LocationFormat { 124 | match self.index_to_loc_format { 125 | 0 => LocationFormat::Short, 126 | 1 => LocationFormat::Long, 127 | _ => unreachable!(), 128 | } 129 | } 130 | } 131 | 132 | #[cfg(test)] 133 | mod tests { 134 | use super::*; 135 | use Error::*; 136 | use expectest::prelude::*; 137 | 138 | const SIZE: usize = 4 * 4 + 2 * 2 + 8 * 2 + 2 * 9; 139 | 140 | #[test] 141 | fn smoke() { 142 | let data = ::utils::read_file("tests/Tuffy_Bold.ttf"); 143 | let offset = ::utils::find_table_offset(&data, 0, b"head").unwrap().unwrap(); 144 | 145 | let head = HEAD::from_data(&data, offset).unwrap(); 146 | assert_eq!(head.bytes(), &data[offset..offset + SIZE]); 147 | 148 | let mut head = HEAD::default(); 149 | expect!(HEAD::from_data(&head.bytes(), 0)).to(be_err().value(HEADVersionIsNotSupported)); 150 | 151 | head.version = ::types::Fixed(0x00010000); 152 | head.index_to_loc_format = 2; 153 | expect!(HEAD::from_data(&head.bytes(), 0)).to(be_err().value(UnknownLocationFormat)); 154 | 155 | expect!(HEAD::from_data(&data, data.len())).to(be_err().value(Malformed)); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/tables/hhea.rs: -------------------------------------------------------------------------------- 1 | 2 | use types::Fixed; 3 | use Error; 4 | use Result; 5 | use std::io::Cursor; 6 | use byteorder::{BigEndian, ReadBytesExt}; 7 | 8 | /// A horizontal header. 9 | /// 10 | /// This table contains information needed to layout fonts whose characters 11 | /// are written horizontally, that is, either left to right or right to left. 12 | /// 13 | /// The table provides such properties as: `ascent`, `descent` and `line_gap`, 14 | /// these are expressed in unscaled coordinates, so you must multiply by 15 | /// the scale factor for a given size. You can advance the vertical position by 16 | /// `ascent - descent + line_gap`. 17 | #[derive(Debug, Default)] 18 | pub struct HHEA { 19 | version: Fixed, 20 | ascent: i16, 21 | descent: i16, 22 | line_gap: i16, 23 | advance_width_max: u16, 24 | min_left_side_bearing: i16, 25 | min_right_side_bearing: i16, 26 | x_max_extent: i16, 27 | caret_slope_rise: i16, 28 | caret_slope_run: i16, 29 | caret_offset: i16, 30 | reserved1: i16, 31 | reserved2: i16, 32 | reserved3: i16, 33 | reserved4: i16, 34 | metric_data_format: i16, 35 | num_of_long_hor_metrics: u16, 36 | } 37 | 38 | impl HHEA { 39 | /// Returns `hhea` font table. 40 | /// 41 | /// Attempts to read `data` starting from `offset` position. 42 | /// 43 | /// # Errors 44 | /// Returns error if there is not enough data to read or version of 45 | /// the `hhea` font table is not supported. 46 | pub fn from_data(data: &[u8], offset: usize) -> Result { 47 | if offset >= data.len() { 48 | return Err(Error::Malformed); 49 | } 50 | 51 | let mut cursor = Cursor::new(&data[offset..]); 52 | let version = Fixed(try!(cursor.read_i32::())); 53 | if version != Fixed(0x00010000) { 54 | return Err(Error::HHEAVersionIsNotSupported); 55 | } 56 | 57 | let mut hhea = HHEA::default(); 58 | hhea.version = version; 59 | hhea.ascent = try!(cursor.read_i16::()); 60 | hhea.descent = try!(cursor.read_i16::()); 61 | hhea.line_gap = try!(cursor.read_i16::()); 62 | hhea.advance_width_max = try!(cursor.read_u16::()); 63 | hhea.min_left_side_bearing = try!(cursor.read_i16::()); 64 | hhea.min_right_side_bearing = try!(cursor.read_i16::()); 65 | hhea.x_max_extent = try!(cursor.read_i16::()); 66 | hhea.caret_slope_rise = try!(cursor.read_i16::()); 67 | hhea.caret_slope_run = try!(cursor.read_i16::()); 68 | hhea.caret_offset = try!(cursor.read_i16::()); 69 | hhea.reserved1 = try!(cursor.read_i16::()); 70 | hhea.reserved2 = try!(cursor.read_i16::()); 71 | hhea.reserved3 = try!(cursor.read_i16::()); 72 | hhea.reserved4 = try!(cursor.read_i16::()); 73 | hhea.metric_data_format = try!(cursor.read_i16::()); 74 | // TODO: Add error handling. Seems like `num_of_long_hor_metrics` 75 | // should be >= 1. This is requirement for `hmtx` font table. 76 | hhea.num_of_long_hor_metrics = try!(cursor.read_u16::()); 77 | 78 | Ok(hhea) 79 | } 80 | 81 | #[cfg(test)] 82 | fn bytes(&self) -> Vec { 83 | use byteorder::WriteBytesExt; 84 | 85 | let mut data = vec![]; 86 | data.write_i32::(self.version.0).unwrap(); 87 | data.write_i16::(self.ascent).unwrap(); 88 | data.write_i16::(self.descent).unwrap(); 89 | data.write_i16::(self.line_gap).unwrap(); 90 | data.write_u16::(self.advance_width_max).unwrap(); 91 | data.write_i16::(self.min_left_side_bearing).unwrap(); 92 | data.write_i16::(self.min_right_side_bearing).unwrap(); 93 | data.write_i16::(self.x_max_extent).unwrap(); 94 | data.write_i16::(self.caret_slope_rise).unwrap(); 95 | data.write_i16::(self.caret_slope_run).unwrap(); 96 | data.write_i16::(self.caret_offset).unwrap(); 97 | data.write_i16::(self.reserved1).unwrap(); 98 | data.write_i16::(self.reserved2).unwrap(); 99 | data.write_i16::(self.reserved3).unwrap(); 100 | data.write_i16::(self.reserved4).unwrap(); 101 | data.write_i16::(self.metric_data_format).unwrap(); 102 | data.write_u16::(self.num_of_long_hor_metrics).unwrap(); 103 | data 104 | } 105 | 106 | /// Distance from baseline of highest ascender. 107 | pub fn ascent(&self) -> i32 { 108 | self.ascent as i32 109 | } 110 | 111 | /// Distance from baseline of lowest descender (i.e. it is typically negative). 112 | pub fn descent(&self) -> i32 { 113 | self.descent as i32 114 | } 115 | 116 | /// The spacing between one row's descent and the next row's ascent. 117 | #[allow(dead_code)] 118 | pub fn line_gap(&self) -> i32 { 119 | self.line_gap as i32 120 | } 121 | 122 | /// The number of advance widths in metrics table. 123 | pub fn num_of_long_hor_metrics(&self) -> u32 { 124 | self.num_of_long_hor_metrics as u32 125 | } 126 | } 127 | 128 | #[cfg(test)] 129 | mod tests { 130 | use super::*; 131 | use Error::*; 132 | use expectest::prelude::*; 133 | 134 | const SIZE: usize = 16 * 2 + 4; 135 | 136 | #[test] 137 | fn smoke() { 138 | let data = ::utils::read_file("tests/Tuffy_Bold.ttf"); 139 | let offset = ::utils::find_table_offset(&data, 0, b"hhea").unwrap().unwrap(); 140 | 141 | let hhea = HHEA::from_data(&data, offset).unwrap(); 142 | assert_eq!(hhea.bytes(), &data[offset..offset + SIZE]); 143 | 144 | let hhea = HHEA::default(); 145 | expect!(HHEA::from_data(&hhea.bytes(), 0)).to(be_err().value(HHEAVersionIsNotSupported)); 146 | 147 | expect!(HHEA::from_data(&data, data.len())).to(be_err().value(Malformed)); 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /src/tables/hmtx.rs: -------------------------------------------------------------------------------- 1 | 2 | use Error; 3 | use Result; 4 | use std::io::Cursor; 5 | use byteorder::{BigEndian, ReadBytesExt}; 6 | 7 | /// A record of horizontal metrics. 8 | #[derive(Debug, PartialEq, Clone, Copy)] 9 | pub struct LongHorizontalMetric { 10 | /// The offset from the current horizontal position to the next horizontal 11 | /// position. 12 | pub advance_width: u16, 13 | /// The offset from the current horizontal position to the left edge 14 | /// of the character. 15 | pub left_side_bearing: i16, 16 | } 17 | 18 | /// A table of horizontal metrics. 19 | /// 20 | /// The 'hmtx' table contains metric information for the horizontal layout 21 | /// each of the glyphs in the font. 22 | #[derive(Debug, Default)] 23 | pub struct HMTX { 24 | metrics: Vec, 25 | left_side_bearings: Vec, 26 | } 27 | 28 | impl HMTX { 29 | /// Returns `hmtx` font table. 30 | /// 31 | /// Attempts to read `data` starting from `offset` position. 32 | /// `metrics` is a number of long horizontal metrics taken from `hhea` 33 | /// font table. 34 | /// `glyphs` is a number of glyphs in the font. 35 | /// 36 | /// # Errors 37 | /// Returns error if there is not enough data to read or the number of 38 | /// `metrics` is greater than the number of `glyphs`. 39 | pub fn from_data(data: &[u8], offset: usize, metrics: u32, glyphs: u32) -> Result { 40 | if offset >= data.len() { 41 | return Err(Error::Malformed); 42 | } 43 | if metrics > glyphs { 44 | return Err(Error::Malformed); 45 | } 46 | let bearings = glyphs - metrics; 47 | 48 | let mut hmtx = HMTX { 49 | metrics: Vec::with_capacity(metrics as usize), 50 | left_side_bearings: Vec::with_capacity(bearings as usize), 51 | }; 52 | 53 | let mut cursor = Cursor::new(&data[offset..]); 54 | for _ in 0..metrics { 55 | let w = try!(cursor.read_u16::()); 56 | let b = try!(cursor.read_i16::()); 57 | hmtx.metrics.push(LongHorizontalMetric { advance_width: w, left_side_bearing: b }); 58 | } 59 | 60 | for _ in 0..bearings { 61 | hmtx.left_side_bearings.push(try!(cursor.read_i16::())); 62 | } 63 | 64 | Ok(hmtx) 65 | } 66 | 67 | #[cfg(test)] 68 | fn bytes(&self) -> Vec { 69 | use byteorder::WriteBytesExt; 70 | 71 | let mut data = vec![]; 72 | for metric in &self.metrics { 73 | data.write_u16::(metric.advance_width).unwrap(); 74 | data.write_i16::(metric.left_side_bearing).unwrap(); 75 | } 76 | for &bearing in &self.left_side_bearings { 77 | data.write_i16::(bearing).unwrap(); 78 | } 79 | data 80 | } 81 | 82 | /// Returns a horizontal metric for a glyph at a given index. 83 | pub fn hmetric_for_glyph_at_index(&self, i: usize) -> LongHorizontalMetric { 84 | if let Some(&metric) = self.metrics.get(i) { 85 | metric 86 | } else { 87 | // It's safe to `unwrap` here, since valid font should contain 88 | // at least one entry of horizontal metrics. 89 | let mut metric = *self.metrics.last().unwrap(); 90 | if let Some(&lsb) = self.left_side_bearings.get(i - self.metrics.len()) { 91 | metric.left_side_bearing = lsb; 92 | } 93 | metric 94 | } 95 | } 96 | } 97 | 98 | #[cfg(test)] 99 | mod tests { 100 | use super::*; 101 | use Error::*; 102 | use tables::{HHEA, MAXP}; 103 | use expectest::prelude::*; 104 | 105 | #[test] 106 | fn smoke() { 107 | let data = ::utils::read_file("tests/Tuffy_Bold.ttf"); 108 | let hhea_offset = ::utils::find_table_offset(&data, 0, b"hhea").unwrap().unwrap(); 109 | let metrics = HHEA::from_data(&data, hhea_offset).unwrap().num_of_long_hor_metrics(); 110 | let maxp_offset = ::utils::find_table_offset(&data, 0, b"maxp").unwrap().unwrap(); 111 | let glyphs = MAXP::from_data(&data, maxp_offset).unwrap().num_glyphs(); 112 | 113 | let size = (metrics * 4 + (glyphs - metrics) * 2) as usize; 114 | let hmtx_offset = ::utils::find_table_offset(&data, 0, b"hmtx").unwrap().unwrap(); 115 | let hmtx = HMTX::from_data(&data, hmtx_offset, metrics, glyphs).unwrap(); 116 | assert_eq!(hmtx.bytes(), &data[hmtx_offset..hmtx_offset + size]); 117 | 118 | expect!(HMTX::from_data(&data, data.len(), metrics, glyphs)).to(be_err().value(Malformed)); 119 | expect!(HMTX::from_data(&data, hmtx_offset, 1, 0)).to(be_err().value(Malformed)); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/tables/loca.rs: -------------------------------------------------------------------------------- 1 | 2 | use Error; 3 | use Result; 4 | use types::LocationFormat; 5 | use std::io::Cursor; 6 | use byteorder::{BigEndian, ReadBytesExt}; 7 | 8 | /// A location table. 9 | /// 10 | /// The 'loca' table stores the offsets to the locations of the glyphs 11 | /// in the font relative to the beginning of the 'glyf' table. 12 | #[derive(Debug)] 13 | pub struct LOCA { 14 | offsets: Vec, 15 | format: LocationFormat, 16 | } 17 | 18 | impl Default for LOCA { 19 | fn default() -> Self { 20 | LOCA { offsets: Vec::new(), format: LocationFormat::Short } 21 | } 22 | } 23 | 24 | impl LOCA { 25 | /// Returns `loca` font table. 26 | /// 27 | /// Attempts to read `data` starting from `offset` position. 28 | /// `glyphs` is a number of glyphs in the font. 29 | /// `lf` is a location format. 30 | /// 31 | /// # Errors 32 | /// Returns error if there is not enough data to read. 33 | pub fn from_data(data: &[u8], offset: usize, glyphs: u32, lf: LocationFormat) -> Result { 34 | if offset >= data.len() { 35 | return Err(Error::Malformed); 36 | } 37 | 38 | let count = glyphs + 1; 39 | let mut loca = LOCA { 40 | offsets: Vec::with_capacity(count as usize), 41 | format: lf, 42 | }; 43 | 44 | let mut cursor = Cursor::new(&data[offset..]); 45 | match loca.format { 46 | LocationFormat::Short => { 47 | for _ in 0..count { 48 | loca.offsets.push(try!(cursor.read_u16::()) as u32 * 2); 49 | } 50 | }, 51 | LocationFormat::Long => { 52 | for _ in 0..count { 53 | loca.offsets.push(try!(cursor.read_u32::())); 54 | } 55 | }, 56 | } 57 | 58 | Ok(loca) 59 | } 60 | 61 | #[cfg(test)] 62 | fn bytes(&self) -> Vec { 63 | use byteorder::WriteBytesExt; 64 | 65 | let mut data = vec![]; 66 | match self.format { 67 | LocationFormat::Short => { 68 | for offset in &self.offsets { 69 | data.write_u16::((offset / 2) as u16).unwrap(); 70 | } 71 | }, 72 | LocationFormat::Long => { 73 | for offset in &self.offsets { 74 | data.write_u32::(*offset).unwrap(); 75 | } 76 | }, 77 | } 78 | data 79 | } 80 | 81 | /// Returns the offset to the location of the glyph in the font 82 | /// relative to the beginning of the 'glyf' table. 83 | /// 84 | /// Returns `None` if `i` is out of bounds or if the font does not contain 85 | /// an outline for the glyph at index `i`. 86 | pub fn offset_for_glyph_at_index(&self, i: usize) -> Option { 87 | if let (Some(c), Some(n)) = (self.offsets.get(i), self.offsets.get(i + 1)) { 88 | if c == n { None } else { Some(*c as usize) } 89 | } else { 90 | None 91 | } 92 | } 93 | 94 | /// Returns the size of the `glyf` font table in bytes. 95 | pub fn size_of_glyf_table(&self) -> usize { 96 | self.offsets.get(self.offsets.len() - 1).map(|&n| n as usize).unwrap_or(0) 97 | } 98 | } 99 | 100 | #[cfg(test)] 101 | mod tests { 102 | use super::*; 103 | use Error::*; 104 | use tables::{MAXP, HEAD}; 105 | use types::LocationFormat; 106 | use expectest::prelude::*; 107 | 108 | #[test] 109 | fn smoke() { 110 | let data = ::utils::read_file("tests/Tuffy_Bold.ttf"); 111 | let maxp_offset = ::utils::find_table_offset(&data, 0, b"maxp").unwrap().unwrap(); 112 | let glyphs = MAXP::from_data(&data, maxp_offset).unwrap().num_glyphs(); 113 | let head_offset = ::utils::find_table_offset(&data, 0, b"head").unwrap().unwrap(); 114 | let format = HEAD::from_data(&data, head_offset).unwrap().location_format(); 115 | 116 | let size = ((glyphs + 1) * format.entry_size()) as usize; 117 | let loca_offset = ::utils::find_table_offset(&data, 0, b"loca").unwrap().unwrap(); 118 | let loca = LOCA::from_data(&data, loca_offset, glyphs, format).unwrap(); 119 | assert_eq!(loca.bytes(), &data[loca_offset..loca_offset + size]); 120 | 121 | expect!(LOCA::from_data(&data, data.len(), glyphs, format)).to(be_err().value(Malformed)); 122 | } 123 | 124 | #[test] 125 | fn loca_format_short() { 126 | let data = &[0, 50, 0, 100, 0, 200]; 127 | let loca = LOCA::from_data(data, 0, 2, LocationFormat::Short).unwrap(); 128 | expect!(loca.bytes()).to(be_equal_to(data)); 129 | expect!(loca.offsets).to(be_equal_to([50 * 2, 100 * 2, 200 * 2])); 130 | } 131 | 132 | #[test] 133 | fn loca_format_long() { 134 | let data = &[0, 0, 0, 50, 0, 0, 0, 100, 0, 0, 0, 200]; 135 | let loca = LOCA::from_data(data, 0, 2, LocationFormat::Long).unwrap(); 136 | expect!(loca.bytes()).to(be_equal_to(data)); 137 | expect!(loca.offsets).to(be_equal_to([50, 100, 200])); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/tables/maxp.rs: -------------------------------------------------------------------------------- 1 | 2 | use types::Fixed; 3 | use Error; 4 | use Result; 5 | use std::io::Cursor; 6 | use byteorder::{BigEndian, ReadBytesExt}; 7 | 8 | /// A maximum profile. 9 | /// 10 | /// The 'maxp' table establishes the memory requirements for a font. 11 | /// TODO: implement parsing of 1.0 version of the table. 12 | #[derive(Debug, Default)] 13 | pub struct MAXP { 14 | version: Fixed, 15 | num_glyphs: u16, 16 | } 17 | 18 | impl MAXP { 19 | /// Returns `maxp` font table. 20 | /// 21 | /// Attempts to read `data` starting from `offset` position. 22 | /// 23 | /// # Errors 24 | /// Returns error if there is not enough data to read or version of 25 | /// the `maxp` font table is not supported. 26 | pub fn from_data(data: &[u8], offset: usize) -> Result { 27 | if offset >= data.len() { 28 | return Err(Error::Malformed); 29 | } 30 | 31 | let mut cursor = Cursor::new(&data[offset..]); 32 | let version = Fixed(try!(cursor.read_i32::())); 33 | match version { 34 | Fixed(0x00010000) | Fixed(0x00005000) => { 35 | let mut maxp = MAXP::default(); 36 | maxp.version = version; 37 | maxp.num_glyphs = try!(cursor.read_u16::()); 38 | Ok(maxp) 39 | }, 40 | _ => Err(Error::MAXPVersionIsNotSupported), 41 | } 42 | } 43 | 44 | #[cfg(test)] 45 | fn bytes(&self) -> Vec { 46 | use byteorder::WriteBytesExt; 47 | 48 | let mut data = vec![]; 49 | data.write_i32::(self.version.0).unwrap(); 50 | data.write_u16::(self.num_glyphs).unwrap(); 51 | data 52 | } 53 | 54 | /// Returns the number of glyphs in the font. 55 | pub fn num_glyphs(&self) -> u32 { 56 | self.num_glyphs as u32 57 | } 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use super::*; 63 | use Error::*; 64 | use expectest::prelude::*; 65 | 66 | const SIZE: usize = 4 + 2; 67 | 68 | #[test] 69 | fn smoke() { 70 | let data = ::utils::read_file("tests/Tuffy_Bold.ttf"); 71 | let offset = ::utils::find_table_offset(&data, 0, b"maxp").unwrap().unwrap(); 72 | 73 | let maxp = MAXP::from_data(&data, offset).unwrap(); 74 | assert_eq!(maxp.bytes(), &data[offset..offset + SIZE]); 75 | 76 | let maxp = MAXP::default(); 77 | expect!(MAXP::from_data(&maxp.bytes(), 0)).to(be_err().value(MAXPVersionIsNotSupported)); 78 | 79 | expect!(MAXP::from_data(&data, data.len())).to(be_err().value(Malformed)); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/tables/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | mod hhea; 3 | mod head; 4 | mod maxp; 5 | mod hmtx; 6 | mod loca; 7 | mod cmap; 8 | mod glyf; 9 | 10 | pub use self::hhea::HHEA; 11 | pub use self::head::HEAD; 12 | pub use self::maxp::MAXP; 13 | pub use self::hmtx::{HMTX, LongHorizontalMetric}; 14 | pub use self::loca::LOCA; 15 | pub use self::cmap::CMAP; 16 | pub use self::glyf::{GLYF, GlyphData}; 17 | 18 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | 2 | /// A bounding box type. 3 | #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] 4 | pub struct BBox { 5 | pub x0: i32, 6 | pub y0: i32, 7 | pub x1: i32, 8 | pub y1: i32, 9 | } 10 | 11 | #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] 12 | pub struct Fixed(pub i32); 13 | 14 | /// Indicates the type of offset format used in the index to loc ('loca') table. 15 | /// 16 | /// Taken from `indexToLocFormat` field of the `head` font table. 17 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 18 | pub enum LocationFormat { 19 | Short, 20 | Long, 21 | } 22 | 23 | impl LocationFormat { 24 | /// Returns size in bytes of the one entry in the `loca` font table. 25 | #[cfg(test)] 26 | pub fn entry_size(&self) -> u32 { 27 | match *self { 28 | LocationFormat::Short => 2, 29 | LocationFormat::Long => 4, 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | 2 | use Error; 3 | use Result; 4 | use byteorder::{BigEndian, ByteOrder}; 5 | 6 | /// Attempts to find the table offset in `data` for a font table `tag` 7 | /// starting from a `fontstart` offset. 8 | pub fn find_table_offset(data: &[u8], fontstart: usize, tag: &[u8; 4]) -> Result> { 9 | let tabledir = fontstart + 12; 10 | if tabledir >= data.len() { 11 | return Err(Error::Malformed); 12 | } 13 | 14 | let num_tables = BigEndian::read_u16(&data[fontstart + 4..]) as usize; 15 | for table_chunk in data[tabledir..].chunks(16).take(num_tables) { 16 | if table_chunk.len()==16 && prefix_is_tag(table_chunk, tag) { 17 | return Ok(Some(BigEndian::read_u32(&table_chunk[8..12]) as usize)); 18 | } 19 | } 20 | return Ok(None); 21 | } 22 | 23 | /// Attempts to find the table offset in `data` for a required font table `tag` 24 | /// starting from a `fontstart` offset. 25 | pub fn find_required_table_offset(data: &[u8], fontstart: usize, tag: &[u8; 4]) -> Result { 26 | match try!(find_table_offset(data, fontstart, tag)) { 27 | Some(offset) => Ok(offset), 28 | None => Err(Error::MissingTable) 29 | } 30 | } 31 | 32 | /// Compatibility with unsafe code. TODO: Remove as soon as possible. 33 | pub unsafe fn find_table(data: *const u8, fontstart: u32, tag: &[u8; 4]) -> u32 { 34 | let slice = ::std::slice::from_raw_parts(data, 1024); // DANGER: Don't care about size. 35 | find_table_offset(slice, fontstart as usize, tag).unwrap_or(None).unwrap_or(0) as u32 36 | } 37 | 38 | /// Checks that perfix of `bs` is equal to `tag`. 39 | pub fn prefix_is_tag(bs: &[u8], tag: &[u8; 4]) -> bool { 40 | bs.len()>=4 && bs[0]==tag[0] && bs[1]==tag[1] && bs[2]==tag[2] && bs[3]==tag[3] 41 | } 42 | 43 | pub fn read_u16_from_raw_data(data: &[u8], index: usize) -> Option { 44 | if index * 2 < data.len() { 45 | Some(BigEndian::read_u16(&data[index * 2..])) 46 | } else { 47 | None 48 | } 49 | } 50 | 51 | pub fn read_i16_from_raw_data(data: &[u8], index: usize) -> Option { 52 | if index * 2 < data.len() { 53 | Some(BigEndian::read_i16(&data[index * 2..])) 54 | } else { 55 | None 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | pub fn read_file(path: &str) -> Vec { 61 | use std::fs::{self, File}; 62 | use std::io::{Read}; 63 | use std::path::PathBuf; 64 | 65 | let path = PathBuf::from(path); 66 | assert!(fs::metadata(&path).is_ok()); 67 | let mut file = File::open(&path).unwrap(); 68 | let mut buffer = Vec::new(); 69 | file.read_to_end(&mut buffer).unwrap(); 70 | buffer 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests { 75 | use super::*; 76 | use expectest::prelude::*; 77 | 78 | #[test] 79 | fn test_prefix_is_tag() { 80 | assert!(prefix_is_tag(b"abcde", b"abcd")); 81 | assert!(!prefix_is_tag(b"abc", b"abcd")); 82 | assert!(!prefix_is_tag(b"abcc", b"abcd")); 83 | } 84 | 85 | #[test] 86 | fn test_read_u16_from_raw_data() { 87 | let data: &[u8] = &[0, 1, 0, 3]; 88 | expect!(read_u16_from_raw_data(data, 0)).to(be_some().value(1)); 89 | expect!(read_u16_from_raw_data(data, 1)).to(be_some().value(3)); 90 | expect!(read_u16_from_raw_data(data, 2)).to(be_none()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/Tuffy_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/truetype/32f3aaeacd1302f6a273d14cf49bbf3e72546062/tests/Tuffy_Bold.ttf -------------------------------------------------------------------------------- /tests/Tuffy_Bold_LICENSE.txt: -------------------------------------------------------------------------------- 1 | We, the copyright holders of this work, hereby release it into the 2 | public domain. This applies worldwide. 3 | 4 | In case this is not legally possible, 5 | 6 | We grant any entity the right to use this work for any purpose, without 7 | any conditions, unless such conditions are required by law. 8 | 9 | Thatcher Ulrich http://tulrich.com 10 | Karoly Barta bartakarcsi@gmail.com 11 | Michael Evans http://www.evertype.com 12 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate piston_truetype; 2 | 3 | use std::ptr::{null_mut}; 4 | use piston_truetype::*; 5 | 6 | fn expect_glyph(letter: char, expected: String) { 7 | unsafe { 8 | let bs = include_bytes!("Tuffy_Bold.ttf"); 9 | let s = 20.0; 10 | 11 | let mut w = 0; 12 | let mut h = 0; 13 | 14 | let offset = get_font_offset_for_index(bs.as_ptr(),0) as usize; 15 | 16 | let font = FontInfo::new_with_offset(&bs[..], offset).ok().expect("Failed to load font"); 17 | let scale = font.scale_for_pixel_height(s); 18 | let bitmap = get_codepoint_bitmap(&font, 0.0,scale, letter as isize, &mut w, &mut h, null_mut(),null_mut()); 19 | 20 | let mut result = String::new(); 21 | for j in 0..h { 22 | for i in 0..w { 23 | result.push([' ', '.', ':', 'i', 'o', 'V', 'M', '@'][((*bitmap.offset(j*w+i))>>5) as usize]); 24 | } 25 | result.push('\n'); 26 | } 27 | 28 | if result != expected { 29 | println!("\n{:?}", expected); 30 | println!("{:?}", result); 31 | panic!("The `A` is malformed.\n\n\nExpected:\n{}|\n\nGot:\n{}|", result, expected); 32 | } 33 | } 34 | } 35 | 36 | 37 | #[test] 38 | fn draw_capital_a() { 39 | expect_glyph('A', String::new() + 40 | " VMM \n" + 41 | " @@@i \n" + 42 | " i@@@M \n" + 43 | " M@o@@. \n" + 44 | " .@@.V@o \n" + 45 | " o@M i@@ \n" + 46 | " M@i .@@. \n" + 47 | " .@@@@@@@o \n" + 48 | " o@@@@@@@@ \n" + 49 | " @@o .@@: \n" + 50 | ":@@. M@V \n" + 51 | "V@M i@@ \n" ); 52 | } 53 | 54 | #[test] 55 | fn draw_capital_g() { 56 | expect_glyph('G', String::new() + 57 | " . \n" + 58 | " o@@@V. \n" + 59 | " M@@M@@@ \n" + 60 | " i@@: Vo. \n" + 61 | " @@o \n" + 62 | ".@@. \n" + 63 | ".@@ .oooo:\n" + 64 | ".@@ .@@@@i\n" + 65 | " @@: iiM@i\n" + 66 | " M@o @@.\n" + 67 | " i@@: i@@ \n" + 68 | " M@@MM@@: \n" + 69 | " o@@@@: \n" + 70 | " . \n" ); 71 | } 72 | --------------------------------------------------------------------------------