From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- third_party/rust/core-text/.cargo-checksum.json | 1 + third_party/rust/core-text/COPYRIGHT | 5 + third_party/rust/core-text/Cargo.toml | 39 + third_party/rust/core-text/LICENSE-APACHE | 201 ++++ third_party/rust/core-text/LICENSE-MIT | 25 + third_party/rust/core-text/README.md | 3 + third_party/rust/core-text/src/font.rs | 1160 ++++++++++++++++++++ third_party/rust/core-text/src/font_collection.rs | 136 +++ third_party/rust/core-text/src/font_descriptor.rs | 441 ++++++++ third_party/rust/core-text/src/font_manager.rs | 76 ++ third_party/rust/core-text/src/frame.rs | 94 ++ third_party/rust/core-text/src/framesetter.rs | 94 ++ third_party/rust/core-text/src/lib.rs | 34 + third_party/rust/core-text/src/line.rs | 125 +++ third_party/rust/core-text/src/run.rs | 159 +++ .../rust/core-text/src/string_attributes.rs | 19 + 16 files changed, 2612 insertions(+) create mode 100644 third_party/rust/core-text/.cargo-checksum.json create mode 100644 third_party/rust/core-text/COPYRIGHT create mode 100644 third_party/rust/core-text/Cargo.toml create mode 100644 third_party/rust/core-text/LICENSE-APACHE create mode 100644 third_party/rust/core-text/LICENSE-MIT create mode 100644 third_party/rust/core-text/README.md create mode 100644 third_party/rust/core-text/src/font.rs create mode 100644 third_party/rust/core-text/src/font_collection.rs create mode 100644 third_party/rust/core-text/src/font_descriptor.rs create mode 100644 third_party/rust/core-text/src/font_manager.rs create mode 100644 third_party/rust/core-text/src/frame.rs create mode 100644 third_party/rust/core-text/src/framesetter.rs create mode 100644 third_party/rust/core-text/src/lib.rs create mode 100644 third_party/rust/core-text/src/line.rs create mode 100644 third_party/rust/core-text/src/run.rs create mode 100644 third_party/rust/core-text/src/string_attributes.rs (limited to 'third_party/rust/core-text') diff --git a/third_party/rust/core-text/.cargo-checksum.json b/third_party/rust/core-text/.cargo-checksum.json new file mode 100644 index 0000000000..bd8382ff8c --- /dev/null +++ b/third_party/rust/core-text/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"COPYRIGHT":"ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a","Cargo.toml":"4d4220f78f2823ddc045b0664ac88627bbe3d3bd3dac79539b96c193aa5bfbbc","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3","README.md":"98d25015857a430aac32f34bdc979a1a66e672a0ea42c5f92dd9cfe23c1fccfd","src/font.rs":"67acc8a7a9329e7b59d7addccf8d32cb705587573ac4d736df10714b590a7d82","src/font_collection.rs":"2bd992032aa1dda4042c8e28974a5708a7ddcd60f3fbd1d58499c1e7b54ae615","src/font_descriptor.rs":"df229425f3f425af441cb51c4fcd4b8eb7f758bbe34bd2437f0bf56dff7d42c9","src/font_manager.rs":"f936404cfa76fb4e467b3233e328a50a2068ae2370407245b62528cb4b948bb1","src/frame.rs":"1fb9434eab2460abc5d882e8ff228a6376b2557fc6c2483a41e0a3495724ca02","src/framesetter.rs":"13e34b4111cee5f023aa05e2220d2a6f102e96fd18c51a356992bffd6c9fc7c1","src/lib.rs":"eeb19facf14bb50870c2481bc370d7e008d5755e7d5cbc10de7891ceab28db2e","src/line.rs":"592a5eb6b5d14f3e4cceb449ee935a4385ce364988f23c483a8c36dd02be4e34","src/run.rs":"fd3838ea31da8fd71a33b256aa397192d96094b457489dc6c91ac190130727c8","src/string_attributes.rs":"398ccc9fcf6238bc9a88f33bb52237a458f0d7149b9688cfee8957775768a6ff"},"package":"c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5"} \ No newline at end of file diff --git a/third_party/rust/core-text/COPYRIGHT b/third_party/rust/core-text/COPYRIGHT new file mode 100644 index 0000000000..8b7291ad28 --- /dev/null +++ b/third_party/rust/core-text/COPYRIGHT @@ -0,0 +1,5 @@ +Licensed under the Apache License, Version 2.0 or the MIT license +, at your +option. All files in the project carrying such notice may not be +copied, modified, or distributed except according to those terms. diff --git a/third_party/rust/core-text/Cargo.toml b/third_party/rust/core-text/Cargo.toml new file mode 100644 index 0000000000..9cd6eee7e9 --- /dev/null +++ b/third_party/rust/core-text/Cargo.toml @@ -0,0 +1,39 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +name = "core-text" +version = "20.1.0" +authors = ["The Servo Project Developers"] +description = "Bindings to the Core Text framework." +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/servo/core-foundation-rs" + +[package.metadata.docs.rs] +all-features = true +default-target = "x86_64-apple-darwin" + +[dependencies.core-foundation] +version = "0.9" + +[dependencies.core-graphics] +version = "0.23.0" + +[dependencies.foreign-types] +version = "0.5" + +[dependencies.libc] +version = "0.2" + +[features] +default = ["mountainlion"] +mountainlion = [] diff --git a/third_party/rust/core-text/LICENSE-APACHE b/third_party/rust/core-text/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/core-text/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/core-text/LICENSE-MIT b/third_party/rust/core-text/LICENSE-MIT new file mode 100644 index 0000000000..807526f57f --- /dev/null +++ b/third_party/rust/core-text/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2012-2013 Mozilla Foundation + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/core-text/README.md b/third_party/rust/core-text/README.md new file mode 100644 index 0000000000..312fe3325d --- /dev/null +++ b/third_party/rust/core-text/README.md @@ -0,0 +1,3 @@ +# core-text-rs + +[![Build Status](https://travis-ci.com/servo/core-text-rs.svg?branch=master)](https://travis-ci.com/servo/core-text-rs) diff --git a/third_party/rust/core-text/src/font.rs b/third_party/rust/core-text/src/font.rs new file mode 100644 index 0000000000..0d303a63fe --- /dev/null +++ b/third_party/rust/core-text/src/font.rs @@ -0,0 +1,1160 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use font_descriptor; +use font_descriptor::{CTFontDescriptor, CTFontDescriptorRef, CTFontOrientation}; +use font_descriptor::{CTFontSymbolicTraits, CTFontTraits, SymbolicTraitAccessors, TraitAccessors}; +use font_manager::create_font_descriptor; + +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{CFIndex, CFOptionFlags, CFType, CFTypeID, CFTypeRef, TCFType}; +use core_foundation::data::{CFData, CFDataRef}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::number::CFNumber; +use core_foundation::string::{CFString, CFStringRef, UniChar}; +use core_foundation::url::{CFURLRef, CFURL}; +use core_graphics::base::CGFloat; +use core_graphics::context::CGContext; +use core_graphics::font::{CGFont, CGGlyph}; +use core_graphics::geometry::{CGAffineTransform, CGPoint, CGRect, CGSize}; +use core_graphics::path::CGPath; + +use foreign_types::ForeignType; +use libc::{self, size_t}; +use std::os::raw::c_void; +use std::ptr; + +type CGContextRef = *mut ::CType; +type CGFontRef = *mut ::CType; +type CGPathRef = *mut ::CType; + +pub type CTFontUIFontType = u32; +// kCTFontNoFontType: CTFontUIFontType = -1; +pub const kCTFontUserFontType: CTFontUIFontType = 0; +pub const kCTFontUserFixedPitchFontType: CTFontUIFontType = 1; +pub const kCTFontSystemFontType: CTFontUIFontType = 2; +pub const kCTFontEmphasizedSystemFontType: CTFontUIFontType = 3; +pub const kCTFontSmallSystemFontType: CTFontUIFontType = 4; +pub const kCTFontSmallEmphasizedSystemFontType: CTFontUIFontType = 5; +pub const kCTFontMiniSystemFontType: CTFontUIFontType = 6; +pub const kCTFontMiniEmphasizedSystemFontType: CTFontUIFontType = 7; +pub const kCTFontViewsFontType: CTFontUIFontType = 8; +pub const kCTFontApplicationFontType: CTFontUIFontType = 9; +pub const kCTFontLabelFontType: CTFontUIFontType = 10; +pub const kCTFontMenuTitleFontType: CTFontUIFontType = 11; +pub const kCTFontMenuItemFontType: CTFontUIFontType = 12; +pub const kCTFontMenuItemMarkFontType: CTFontUIFontType = 13; +pub const kCTFontMenuItemCmdKeyFontType: CTFontUIFontType = 14; +pub const kCTFontWindowTitleFontType: CTFontUIFontType = 15; +pub const kCTFontPushButtonFontType: CTFontUIFontType = 16; +pub const kCTFontUtilityWindowTitleFontType: CTFontUIFontType = 17; +pub const kCTFontAlertHeaderFontType: CTFontUIFontType = 18; +pub const kCTFontSystemDetailFontType: CTFontUIFontType = 19; +pub const kCTFontEmphasizedSystemDetailFontType: CTFontUIFontType = 20; +pub const kCTFontToolbarFontType: CTFontUIFontType = 21; +pub const kCTFontSmallToolbarFontType: CTFontUIFontType = 22; +pub const kCTFontMessageFontType: CTFontUIFontType = 23; +pub const kCTFontPaletteFontType: CTFontUIFontType = 24; +pub const kCTFontToolTipFontType: CTFontUIFontType = 25; +pub const kCTFontControlContentFontType: CTFontUIFontType = 26; + +pub type CTFontTableTag = u32; +// TODO: create bindings for enum with 'chars' values + +pub type CTFontTableOptions = u32; +pub const kCTFontTableOptionsNoOptions: CTFontTableOptions = 0; +pub const kCTFontTableOptionsExcludeSynthetic: CTFontTableOptions = 1 << 0; + +pub type CTFontOptions = CFOptionFlags; +pub const kCTFontOptionsDefault: CTFontOptions = 0; +pub const kCTFontOptionsPreventAutoActivation: CTFontOptions = 1 << 0; +pub const kCTFontOptionsPreferSystemFont: CTFontOptions = 1 << 2; + +pub enum CTFontNameSpecifier { + Copyright, + Family, + SubFamily, + Style, + Unique, + Full, + Version, + PostScript, + Trademark, + Manufacturer, + Designer, + Description, + VendorURL, + DesignerURL, + License, + LicenseURL, + SampleText, + PostScriptCID, +} + +impl From for CFStringRef { + fn from(val: CTFontNameSpecifier) -> Self { + unsafe { + match val { + CTFontNameSpecifier::Copyright => kCTFontCopyrightNameKey, + CTFontNameSpecifier::Family => kCTFontFamilyNameKey, + CTFontNameSpecifier::SubFamily => kCTFontSubFamilyNameKey, + CTFontNameSpecifier::Style => kCTFontStyleNameKey, + CTFontNameSpecifier::Unique => kCTFontUniqueNameKey, + CTFontNameSpecifier::Full => kCTFontFullNameKey, + CTFontNameSpecifier::Version => kCTFontVersionNameKey, + CTFontNameSpecifier::PostScript => kCTFontPostScriptNameKey, + CTFontNameSpecifier::Trademark => kCTFontTrademarkNameKey, + CTFontNameSpecifier::Manufacturer => kCTFontManufacturerNameKey, + CTFontNameSpecifier::Designer => kCTFontDesignerNameKey, + CTFontNameSpecifier::Description => kCTFontDescriptionNameKey, + CTFontNameSpecifier::VendorURL => kCTFontVendorURLNameKey, + CTFontNameSpecifier::DesignerURL => kCTFontDesignerURLNameKey, + CTFontNameSpecifier::License => kCTFontLicenseNameKey, + CTFontNameSpecifier::LicenseURL => kCTFontLicenseURLNameKey, + CTFontNameSpecifier::SampleText => kCTFontSampleTextNameKey, + CTFontNameSpecifier::PostScriptCID => kCTFontPostScriptCIDNameKey, + } + } + } +} + +#[repr(C)] +pub struct __CTFont(c_void); + +pub type CTFontRef = *const __CTFont; + +declare_TCFType! { + CTFont, CTFontRef +} +impl_TCFType!(CTFont, CTFontRef, CTFontGetTypeID); +impl_CFTypeDescription!(CTFont); + +unsafe impl Send for CTFont {} +unsafe impl Sync for CTFont {} + +pub fn new_from_CGFont(cgfont: &CGFont, pt_size: f64) -> CTFont { + unsafe { + let font_ref = CTFontCreateWithGraphicsFont( + cgfont.as_ptr() as *mut _, + pt_size as CGFloat, + ptr::null(), + ptr::null(), + ); + CTFont::wrap_under_create_rule(font_ref) + } +} + +pub fn new_from_CGFont_with_variations( + cgfont: &CGFont, + pt_size: f64, + variations: &CFDictionary, +) -> CTFont { + unsafe { + let font_desc = font_descriptor::new_from_variations(variations); + let font_ref = CTFontCreateWithGraphicsFont( + cgfont.as_ptr() as *mut _, + pt_size as CGFloat, + ptr::null(), + font_desc.as_concrete_TypeRef(), + ); + CTFont::wrap_under_create_rule(font_ref) + } +} + +pub fn new_from_descriptor(desc: &CTFontDescriptor, pt_size: f64) -> CTFont { + unsafe { + let font_ref = CTFontCreateWithFontDescriptor( + desc.as_concrete_TypeRef(), + pt_size as CGFloat, + ptr::null(), + ); + CTFont::wrap_under_create_rule(font_ref) + } +} + +pub fn new_from_descriptor_and_options( + desc: &CTFontDescriptor, + pt_size: f64, + options: CTFontOptions, +) -> CTFont { + unsafe { + let font_ref = CTFontCreateWithFontDescriptorAndOptions( + desc.as_concrete_TypeRef(), + pt_size as CGFloat, + ptr::null(), + options, + ); + CTFont::wrap_under_create_rule(font_ref) + } +} + +pub fn new_from_buffer(buffer: &[u8]) -> Result { + let ct_font_descriptor = create_font_descriptor(buffer)?; + Ok(new_from_descriptor(&ct_font_descriptor, 16.0)) +} + +pub fn new_from_name(name: &str, pt_size: f64) -> Result { + unsafe { + let name: CFString = name.parse().unwrap(); + let font_ref = + CTFontCreateWithName(name.as_concrete_TypeRef(), pt_size as CGFloat, ptr::null()); + if font_ref.is_null() { + Err(()) + } else { + Ok(CTFont::wrap_under_create_rule(font_ref)) + } + } +} + +pub fn new_from_name_and_options( + name: &str, + pt_size: f64, + options: CTFontOptions, +) -> Result { + unsafe { + let name: CFString = name.parse().unwrap(); + let font_ref = CTFontCreateWithNameAndOptions( + name.as_concrete_TypeRef(), + pt_size as CGFloat, + ptr::null(), + options, + ); + if font_ref.is_null() { + Err(()) + } else { + Ok(CTFont::wrap_under_create_rule(font_ref)) + } + } +} + +pub fn new_ui_font_for_language( + ui_type: CTFontUIFontType, + size: f64, + language: Option, +) -> CTFont { + unsafe { + let font_ref = CTFontCreateUIFontForLanguage( + ui_type, + size, + language + .as_ref() + .map(|x| x.as_concrete_TypeRef()) + .unwrap_or(std::ptr::null()), + ); + if font_ref.is_null() { + // CTFontCreateUIFontForLanguage can fail, but is unlikely to do so during + // normal usage (if you pass a bad ui_type it will). To make things more + // convenient, just panic if it fails. + panic!(); + } else { + CTFont::wrap_under_create_rule(font_ref) + } + } +} + +impl CTFont { + // Properties + pub fn symbolic_traits(&self) -> CTFontSymbolicTraits { + unsafe { CTFontGetSymbolicTraits(self.0) } + } +} + +impl CTFont { + // Creation methods + pub fn copy_to_CGFont(&self) -> CGFont { + unsafe { + let cgfont_ref = CTFontCopyGraphicsFont(self.0, ptr::null_mut()); + CGFont::from_ptr(cgfont_ref as *mut _) + } + } + + pub fn copy_descriptor(&self) -> CTFontDescriptor { + unsafe { + let desc = CTFontCopyFontDescriptor(self.0); + CTFontDescriptor::wrap_under_create_rule(desc) + } + } + + pub fn clone_with_font_size(&self, size: f64) -> CTFont { + unsafe { + let font_ref = + CTFontCreateCopyWithAttributes(self.0, size as CGFloat, ptr::null(), ptr::null()); + CTFont::wrap_under_create_rule(font_ref) + } + } + + pub fn clone_with_symbolic_traits( + &self, + trait_value: CTFontSymbolicTraits, + trait_mask: CTFontSymbolicTraits, + ) -> Option { + unsafe { + let font_ref = CTFontCreateCopyWithSymbolicTraits( + self.0, + 0.0, + ptr::null(), + trait_value, + trait_mask, + ); + if font_ref.is_null() { + None + } else { + Some(CTFont::wrap_under_create_rule(font_ref)) + } + } + } + + // Names + pub fn get_string_by_name_key(&self, name_key: CTFontNameSpecifier) -> Option { + unsafe { + let result = CTFontCopyName(self.as_concrete_TypeRef(), name_key.into()); + if result.is_null() { + None + } else { + Some(CFString::wrap_under_create_rule(result).to_string()) + } + } + } + + pub fn family_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::Family); + value.expect("Fonts should always have a family name.") + } + + pub fn face_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::SubFamily); + value.expect("Fonts should always have a face name.") + } + + pub fn unique_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::Unique); + value.expect("Fonts should always have a unique name.") + } + + pub fn postscript_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::PostScript); + value.expect("Fonts should always have a PostScript name.") + } + + pub fn display_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::Full); + value.expect("Fonts should always have a PostScript name.") + } + + pub fn style_name(&self) -> String { + let value = self.get_string_by_name_key(CTFontNameSpecifier::Style); + value.expect("Fonts should always have a style name.") + } + + pub fn all_traits(&self) -> CTFontTraits { + unsafe { CTFontTraits::wrap_under_create_rule(CTFontCopyTraits(self.0)) } + } + + // Font metrics + pub fn ascent(&self) -> CGFloat { + unsafe { CTFontGetAscent(self.0) } + } + + pub fn descent(&self) -> CGFloat { + unsafe { CTFontGetDescent(self.0) } + } + + pub fn underline_thickness(&self) -> CGFloat { + unsafe { CTFontGetUnderlineThickness(self.0) } + } + + pub fn underline_position(&self) -> CGFloat { + unsafe { CTFontGetUnderlinePosition(self.0) } + } + + pub fn slant_angle(&self) -> CGFloat { + unsafe { CTFontGetSlantAngle(self.0) } + } + + pub fn cap_height(&self) -> CGFloat { + unsafe { CTFontGetCapHeight(self.0) } + } + + pub fn bounding_box(&self) -> CGRect { + unsafe { CTFontGetBoundingBox(self.0) } + } + + pub fn leading(&self) -> CGFloat { + unsafe { CTFontGetLeading(self.0) } + } + + pub fn units_per_em(&self) -> libc::c_uint { + unsafe { CTFontGetUnitsPerEm(self.0) } + } + + pub fn x_height(&self) -> CGFloat { + unsafe { CTFontGetXHeight(self.0) } + } + + pub fn pt_size(&self) -> CGFloat { + unsafe { CTFontGetSize(self.0) } + } + + pub fn get_glyph_with_name(&self, glyph_name: &str) -> CGGlyph { + let glyph_name = CFString::new(glyph_name); + unsafe { CTFontGetGlyphWithName(self.0, glyph_name.as_concrete_TypeRef()) } + } + + pub unsafe fn get_glyphs_for_characters( + &self, + characters: *const UniChar, + glyphs: *mut CGGlyph, + count: CFIndex, + ) -> bool { + CTFontGetGlyphsForCharacters(self.0, characters, glyphs, count) + } + + pub unsafe fn get_advances_for_glyphs( + &self, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + advances: *mut CGSize, + count: CFIndex, + ) -> f64 { + CTFontGetAdvancesForGlyphs(self.0, orientation, glyphs, advances, count) + } + + pub unsafe fn get_vertical_translations_for_glyphs( + &self, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + translations: *mut CGSize, + count: CFIndex, + ) { + CTFontGetVerticalTranslationsForGlyphs(self.0, orientation, glyphs, translations, count) + } + + pub fn get_font_table(&self, tag: u32) -> Option { + unsafe { + let result = CTFontCopyTable( + self.0, + tag as CTFontTableTag, + kCTFontTableOptionsExcludeSynthetic, + ); + if result.is_null() { + None + } else { + Some(CFData::wrap_under_create_rule(result)) + } + } + } + + pub fn get_available_font_tables(&self) -> Option> { + unsafe { + let result = CTFontCopyAvailableTables(self.0, kCTFontTableOptionsExcludeSynthetic); + if result.is_null() { + None + } else { + Some(TCFType::wrap_under_create_rule(result)) + } + } + } + + pub fn get_bounding_rects_for_glyphs( + &self, + orientation: CTFontOrientation, + glyphs: &[CGGlyph], + ) -> CGRect { + unsafe { + CTFontGetBoundingRectsForGlyphs( + self.as_concrete_TypeRef(), + orientation, + glyphs.as_ptr(), + ptr::null_mut(), + glyphs.len() as CFIndex, + ) + } + } + + pub fn draw_glyphs(&self, glyphs: &[CGGlyph], positions: &[CGPoint], context: CGContext) { + assert_eq!(glyphs.len(), positions.len()); + unsafe { + CTFontDrawGlyphs( + self.as_concrete_TypeRef(), + glyphs.as_ptr(), + positions.as_ptr(), + glyphs.len() as size_t, + context.as_ptr(), + ) + } + } + + pub fn url(&self) -> Option { + unsafe { + let result = CTFontCopyAttribute(self.0, kCTFontURLAttribute); + if result.is_null() { + None + } else { + Some(CFURL::wrap_under_create_rule(result as CFURLRef)) + } + } + } + + pub fn get_variation_axes(&self) -> Option>> { + unsafe { + let axes = CTFontCopyVariationAxes(self.0); + if axes.is_null() { + return None; + } + Some(TCFType::wrap_under_create_rule(axes)) + } + } + + pub fn create_path_for_glyph( + &self, + glyph: CGGlyph, + matrix: &CGAffineTransform, + ) -> Result { + unsafe { + let path = CTFontCreatePathForGlyph(self.0, glyph, matrix); + if path.is_null() { + Err(()) + } else { + Ok(CGPath::from_ptr(path)) + } + } + } + + #[inline] + pub fn glyph_count(&self) -> CFIndex { + unsafe { CTFontGetGlyphCount(self.0) } + } +} + +// Helper methods +pub fn debug_font_names(font: &CTFont) { + fn get_key(font: &CTFont, key: CTFontNameSpecifier) -> String { + font.get_string_by_name_key(key).unwrap() + } + + println!( + "kCTFontFamilyNameKey: {}", + get_key(font, CTFontNameSpecifier::Family) + ); + println!( + "kCTFontSubFamilyNameKey: {}", + get_key(font, CTFontNameSpecifier::SubFamily) + ); + println!( + "kCTFontStyleNameKey: {}", + get_key(font, CTFontNameSpecifier::Style) + ); + println!( + "kCTFontUniqueNameKey: {}", + get_key(font, CTFontNameSpecifier::Unique) + ); + println!( + "kCTFontFullNameKey: {}", + get_key(font, CTFontNameSpecifier::Full) + ); + println!( + "kCTFontPostScriptNameKey: {}", + get_key(font, CTFontNameSpecifier::PostScript) + ); +} + +pub fn debug_font_traits(font: &CTFont) { + let sym = font.symbolic_traits(); + println!("kCTFontItalicTrait: {}", sym.is_italic()); + println!("kCTFontBoldTrait: {}", sym.is_bold()); + println!("kCTFontExpandedTrait: {}", sym.is_expanded()); + println!("kCTFontCondensedTrait: {}", sym.is_condensed()); + println!("kCTFontMonoSpaceTrait: {}", sym.is_monospace()); + + let traits = font.all_traits(); + println!("kCTFontWeightTrait: {}", traits.normalized_weight()); + println!("kCTFontWidthTrait: {}", traits.normalized_width()); + // println!("kCTFontSlantTrait: {}", traits.normalized_slant()); +} + +#[cfg(feature = "mountainlion")] +pub fn cascade_list_for_languages( + font: &CTFont, + language_pref_list: &CFArray, +) -> CFArray { + unsafe { + let font_collection_ref = CTFontCopyDefaultCascadeListForLanguages( + font.as_concrete_TypeRef(), + language_pref_list.as_concrete_TypeRef(), + ); + CFArray::wrap_under_create_rule(font_collection_ref) + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + /* + * CTFont.h + */ + + /* Name Specifier Constants */ + static kCTFontCopyrightNameKey: CFStringRef; + static kCTFontFamilyNameKey: CFStringRef; + static kCTFontSubFamilyNameKey: CFStringRef; + static kCTFontStyleNameKey: CFStringRef; + static kCTFontUniqueNameKey: CFStringRef; + static kCTFontFullNameKey: CFStringRef; + static kCTFontVersionNameKey: CFStringRef; + static kCTFontPostScriptNameKey: CFStringRef; + static kCTFontTrademarkNameKey: CFStringRef; + static kCTFontManufacturerNameKey: CFStringRef; + static kCTFontDesignerNameKey: CFStringRef; + static kCTFontDescriptionNameKey: CFStringRef; + static kCTFontVendorURLNameKey: CFStringRef; + static kCTFontDesignerURLNameKey: CFStringRef; + static kCTFontLicenseNameKey: CFStringRef; + static kCTFontLicenseURLNameKey: CFStringRef; + static kCTFontSampleTextNameKey: CFStringRef; + static kCTFontPostScriptCIDNameKey: CFStringRef; + + #[cfg(test)] + static kCTFontVariationAxisIdentifierKey: CFStringRef; + //static kCTFontVariationAxisMinimumValueKey: CFStringRef; + #[cfg(test)] + static kCTFontVariationAxisMaximumValueKey: CFStringRef; + //static kCTFontVariationAxisDefaultValueKey: CFStringRef; + //static kCTFontVariationAxisNameKey: CFStringRef; + + //static kCTFontFeatureTypeIdentifierKey: CFStringRef; + //static kCTFontFeatureTypeNameKey: CFStringRef; + //static kCTFontFeatureTypeExclusiveKey: CFStringRef; + //static kCTFontFeatureTypeSelectorsKey: CFStringRef; + //static kCTFontFeatureSelectorIdentifierKey: CFStringRef; + //static kCTFontFeatureSelectorNameKey: CFStringRef; + //static kCTFontFeatureSelectorDefaultKey: CFStringRef; + //static kCTFontFeatureSelectorSettingKey: CFStringRef; + + static kCTFontURLAttribute: CFStringRef; + + // N.B. Unlike most Cocoa bindings, this extern block is organized according + // to the documentation's Functions By Task listing, because there so many functions. + + /* Creating Fonts */ + fn CTFontCreateWithName( + name: CFStringRef, + size: CGFloat, + matrix: *const CGAffineTransform, + ) -> CTFontRef; + fn CTFontCreateWithNameAndOptions( + name: CFStringRef, + size: CGFloat, + matrix: *const CGAffineTransform, + options: CTFontOptions, + ) -> CTFontRef; + fn CTFontCreateWithFontDescriptor( + descriptor: CTFontDescriptorRef, + size: CGFloat, + matrix: *const CGAffineTransform, + ) -> CTFontRef; + fn CTFontCreateWithFontDescriptorAndOptions( + descriptor: CTFontDescriptorRef, + size: CGFloat, + matrix: *const CGAffineTransform, + options: CTFontOptions, + ) -> CTFontRef; + fn CTFontCreateUIFontForLanguage( + uiType: CTFontUIFontType, + size: CGFloat, + language: CFStringRef, + ) -> CTFontRef; + fn CTFontCreateCopyWithAttributes( + font: CTFontRef, + size: CGFloat, + matrix: *const CGAffineTransform, + attributes: CTFontDescriptorRef, + ) -> CTFontRef; + fn CTFontCreateCopyWithSymbolicTraits( + font: CTFontRef, + size: CGFloat, + matrix: *const CGAffineTransform, + symTraitValue: CTFontSymbolicTraits, + symTraitMask: CTFontSymbolicTraits, + ) -> CTFontRef; + //fn CTFontCreateCopyWithFamily + //fn CTFontCreateForString + + /* Getting Font Data */ + fn CTFontCopyFontDescriptor(font: CTFontRef) -> CTFontDescriptorRef; + fn CTFontCopyAttribute(font: CTFontRef, attribute: CFStringRef) -> CFTypeRef; + fn CTFontGetSize(font: CTFontRef) -> CGFloat; + //fn CTFontGetMatrix + fn CTFontGetSymbolicTraits(font: CTFontRef) -> CTFontSymbolicTraits; + fn CTFontCopyTraits(font: CTFontRef) -> CFDictionaryRef; + + /* Getting Font Names */ + //fn CTFontCopyPostScriptName(font: CTFontRef) -> CFStringRef; + //fn CTFontCopyFamilyName(font: CTFontRef) -> CFStringRef; + //fn CTFontCopyFullName(font: CTFontRef) -> CFStringRef; + //fn CTFontCopyDisplayName(font: CTFontRef) -> CFStringRef; + fn CTFontCopyName(font: CTFontRef, nameKey: CFStringRef) -> CFStringRef; + //fn CTFontCopyLocalizedName(font: CTFontRef, nameKey: CFStringRef, + // language: *CFStringRef) -> CFStringRef; + #[cfg(feature = "mountainlion")] + fn CTFontCopyDefaultCascadeListForLanguages( + font: CTFontRef, + languagePrefList: CFArrayRef, + ) -> CFArrayRef; + + /* Working With Encoding */ + //fn CTFontCopyCharacterSet + //fn CTFontGetStringEncoding + //fn CTFontCopySupportedLanguages + + /* Getting Font Metrics */ + fn CTFontGetAscent(font: CTFontRef) -> CGFloat; + fn CTFontGetDescent(font: CTFontRef) -> CGFloat; + fn CTFontGetLeading(font: CTFontRef) -> CGFloat; + fn CTFontGetUnitsPerEm(font: CTFontRef) -> libc::c_uint; + fn CTFontGetGlyphCount(font: CTFontRef) -> CFIndex; + fn CTFontGetBoundingBox(font: CTFontRef) -> CGRect; + fn CTFontGetUnderlinePosition(font: CTFontRef) -> CGFloat; + fn CTFontGetUnderlineThickness(font: CTFontRef) -> CGFloat; + fn CTFontGetSlantAngle(font: CTFontRef) -> CGFloat; + fn CTFontGetCapHeight(font: CTFontRef) -> CGFloat; + fn CTFontGetXHeight(font: CTFontRef) -> CGFloat; + + /* Getting Glyph Data */ + fn CTFontCreatePathForGlyph( + font: CTFontRef, + glyph: CGGlyph, + matrix: *const CGAffineTransform, + ) -> CGPathRef; + fn CTFontGetGlyphWithName(font: CTFontRef, glyphName: CFStringRef) -> CGGlyph; + fn CTFontGetBoundingRectsForGlyphs( + font: CTFontRef, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + boundingRects: *mut CGRect, + count: CFIndex, + ) -> CGRect; + fn CTFontGetAdvancesForGlyphs( + font: CTFontRef, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + advances: *mut CGSize, + count: CFIndex, + ) -> libc::c_double; + fn CTFontGetVerticalTranslationsForGlyphs( + font: CTFontRef, + orientation: CTFontOrientation, + glyphs: *const CGGlyph, + translations: *mut CGSize, + count: CFIndex, + ); + + /* Working With Font Variations */ + fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef; + //fn CTFontCopyVariation + + /* Getting Font Features */ + //fn CTFontCopyFeatures + //fn CTFontCopyFeatureSettings + + /* Working with Glyphs */ + fn CTFontGetGlyphsForCharacters( + font: CTFontRef, + characters: *const UniChar, + glyphs: *mut CGGlyph, + count: CFIndex, + ) -> bool; + fn CTFontDrawGlyphs( + font: CTFontRef, + glyphs: *const CGGlyph, + positions: *const CGPoint, + count: size_t, + context: CGContextRef, + ); + //fn CTFontGetLigatureCaretPositions + + /* Converting Fonts */ + fn CTFontCopyGraphicsFont(font: CTFontRef, attributes: *mut CTFontDescriptorRef) -> CGFontRef; + fn CTFontCreateWithGraphicsFont( + graphicsFont: CGFontRef, + size: CGFloat, + matrix: *const CGAffineTransform, + attributes: CTFontDescriptorRef, + ) -> CTFontRef; + //fn CTFontGetPlatformFont + //fn CTFontCreateWithPlatformFont + //fn CTFontCreateWithQuickdrawInstance + + /* Getting Font Table Data */ + fn CTFontCopyAvailableTables(font: CTFontRef, options: CTFontTableOptions) -> CFArrayRef; + fn CTFontCopyTable( + font: CTFontRef, + table: CTFontTableTag, + options: CTFontTableOptions, + ) -> CFDataRef; + + fn CTFontGetTypeID() -> CFTypeID; +} + +#[test] +fn copy_font() { + use std::io::Read; + let mut f = std::fs::File::open("/System/Library/Fonts/ZapfDingbats.ttf").unwrap(); + let mut font_data = Vec::new(); + f.read_to_end(&mut font_data).unwrap(); + let desc = crate::font_manager::create_font_descriptor(&font_data).unwrap(); + let font = new_from_descriptor(&desc, 12.); + drop(desc); + let desc = font.copy_descriptor(); + drop(font); + let font = new_from_descriptor(&desc, 14.); + assert_eq!(font.family_name(), "Zapf Dingbats"); +} + +#[cfg(test)] +fn macos_version() -> (i32, i32, i32) { + use std::io::Read; + + // This is the same approach that Firefox uses for detecting versions + let file = "/System/Library/CoreServices/SystemVersion.plist"; + let mut f = std::fs::File::open(file).unwrap(); + let mut system_version_data = Vec::new(); + f.read_to_end(&mut system_version_data).unwrap(); + + use core_foundation::propertylist; + let (list, _) = propertylist::create_with_data( + core_foundation::data::CFData::from_buffer(&system_version_data), + propertylist::kCFPropertyListImmutable, + ) + .unwrap(); + let k = unsafe { propertylist::CFPropertyList::wrap_under_create_rule(list) }; + + let dict = unsafe { + std::mem::transmute::<_, CFDictionary>( + k.downcast::().unwrap(), + ) + }; + + let version = dict + .find(&CFString::new("ProductVersion").as_CFType()) + .as_ref() + .unwrap() + .downcast::() + .unwrap() + .to_string(); + + match version + .split('.') + .map(|x| x.parse().unwrap()) + .collect::>()[..] + { + [a, b, c] => (a, b, c), + [a, b] => (a, b, 0), + _ => panic!(), + } +} + +#[test] +fn copy_system_font() { + use crate::*; + + let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None); + let big = small.clone_with_font_size(20.); + + // ensure that we end up with different fonts for the different sizes before 10.15 + if macos_version() < (10, 15, 0) { + assert_ne!(big.url(), small.url()); + } else { + assert_eq!(big.url(), small.url()); + } + + let ps = small.postscript_name(); + let desc = small.copy_descriptor(); + + // check that we can construct a new vesion by descriptor + let ctfont = new_from_descriptor(&desc, 20.); + assert_eq!(big.postscript_name(), ctfont.postscript_name()); + + // check that we can construct a new version by attributes + let attr = desc.attributes(); + let desc_from_attr = font_descriptor::new_from_attributes(&attr); + let font_from_attr = new_from_descriptor(&desc_from_attr, 19.); + assert_eq!(font_from_attr.postscript_name(), small.postscript_name()); + + // on newer versions of macos we can't construct by name anymore + if macos_version() < (10, 13, 0) { + let ui_font_by_name = new_from_name(&small.postscript_name(), 19.).unwrap(); + assert_eq!(ui_font_by_name.postscript_name(), small.postscript_name()); + + let ui_font_by_name = new_from_name(&small.postscript_name(), 20.).unwrap(); + assert_eq!(ui_font_by_name.postscript_name(), small.postscript_name()); + + let ui_font_by_name = new_from_name(&big.postscript_name(), 20.).unwrap(); + assert_eq!(ui_font_by_name.postscript_name(), big.postscript_name()); + } + + // but we can still construct the CGFont by name + let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap(); + let cgfont = new_from_CGFont(&cgfont, 0.); + println!("{:?}", cgfont); + let desc = cgfont.copy_descriptor(); + let matching = unsafe { + crate::font_descriptor::CTFontDescriptorCreateMatchingFontDescriptor( + desc.as_concrete_TypeRef(), + std::ptr::null(), + ) + }; + let matching = unsafe { CTFontDescriptor::wrap_under_create_rule(matching) }; + + println!("{:?}", cgfont.copy_descriptor()); + assert!(desc + .attributes() + .find(CFString::from_static_string("NSFontSizeAttribute")) + .is_some()); + + println!("{:?}", matching); + println!( + "{:?}", + matching + .attributes() + .find(CFString::from_static_string("NSFontSizeAttribute")) + ); + + assert!(matching + .attributes() + .find(CFString::from_static_string("NSFontSizeAttribute")) + .is_none()); + + assert_eq!(small.postscript_name(), cgfont.postscript_name()); +} + +// Tests what happens when variations have values not inbetween min and max +#[test] +fn out_of_range_variations() { + use crate::*; + + let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None); + + let axes = small.get_variation_axes(); + if macos_version() < (10, 12, 0) { + assert!(axes.is_none()); + return; + } + let axes = axes.unwrap(); + let mut vals = Vec::new(); + dbg!(&axes); + for axis in axes.iter() { + let tag = axis + .find(unsafe { kCTFontVariationAxisIdentifierKey }) + .unwrap() + .downcast::() + .unwrap() + .to_i64() + .unwrap(); + let max = axis + .find(unsafe { kCTFontVariationAxisMaximumValueKey }) + .unwrap() + .downcast::() + .unwrap() + .to_f64() + .unwrap(); + vals.push((CFNumber::from(tag), CFNumber::from(max + 1.))); + } + let vals_dict = CFDictionary::from_CFType_pairs(&vals); + let variation_attribute = + unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontVariationAttribute) }; + let attrs_dict = CFDictionary::from_CFType_pairs(&[(variation_attribute.clone(), vals_dict)]); + let ct_var_font_desc = small + .copy_descriptor() + .create_copy_with_attributes(attrs_dict.to_untyped()) + .unwrap(); + let variation_font = crate::font::new_from_descriptor(&ct_var_font_desc, 19.); + let var_desc = variation_font.copy_descriptor(); + let var_attrs = var_desc.attributes(); + dbg!(&var_attrs); + // attributes greater than max are dropped on macOS <= 11 + // on macOS 12 they seem to be preserved as is. + let var_attrs = var_attrs.find(variation_attribute); + if macos_version() >= (12, 0, 0) && macos_version() < (13, 0, 0) { + let var_attrs = var_attrs.unwrap().downcast::().unwrap(); + assert!(!var_attrs.is_empty()); + let var_attrs: CFDictionary = unsafe { std::mem::transmute(var_attrs) }; + // attributes greater than max remain + for axis in axes.iter() { + let tag = axis + .find(unsafe { kCTFontVariationAxisIdentifierKey }) + .unwrap(); + let max = axis + .find(unsafe { kCTFontVariationAxisMaximumValueKey }) + .unwrap() + .downcast::() + .unwrap() + .to_f64() + .unwrap(); + let val = var_attrs + .find(tag.clone()) + .unwrap() + .downcast::() + .unwrap() + .to_f64() + .unwrap(); + assert_eq!(val, max + 1.); + } + } else if macos_version() >= (10, 15, 0) { + assert!(var_attrs.is_none()); + } else { + let var_attrs = var_attrs.unwrap().downcast::().unwrap(); + assert!(var_attrs.is_empty()); + } +} + +#[test] +fn equal_descriptor_different_font() { + use crate::*; + + let variation_attribute = + unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontVariationAttribute) }; + let size_attribute = + unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontSizeAttribute) }; + + let sys_font = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None); + + // but we can still construct the CGFont by name + let create_vars = |desc| { + let vals: Vec<(CFNumber, CFNumber)> = + vec![(CFNumber::from(0x6f70737a), CFNumber::from(17.))]; + let vals_dict = CFDictionary::from_CFType_pairs(&vals); + let attrs_dict = + CFDictionary::from_CFType_pairs(&[(variation_attribute.clone(), vals_dict)]); + let size_attrs_dict = + CFDictionary::from_CFType_pairs(&[(size_attribute.clone(), CFNumber::from(120.))]); + dbg!(&desc); + let from_font_desc = new_from_descriptor(&desc, 120.).copy_descriptor(); + let resized_font_desc = desc + .create_copy_with_attributes(size_attrs_dict.to_untyped()) + .unwrap(); + if macos_version() >= (11, 0, 0) { + assert_eq!(from_font_desc, resized_font_desc); + } else { + // we won't have a name if we're using system font desc + if from_font_desc + .attributes() + .find(unsafe { font_descriptor::kCTFontNameAttribute }) + .is_none() + { + // it's surprising that desc's are the not equal but the attributes are + assert_ne!(from_font_desc, resized_font_desc); + assert_eq!( + from_font_desc.attributes().to_untyped(), + resized_font_desc.attributes().to_untyped() + ); + } else if macos_version() >= (10, 13, 0) { + // this is unsurprising + assert_ne!(from_font_desc, resized_font_desc); + assert_ne!( + from_font_desc.attributes().to_untyped(), + resized_font_desc.attributes().to_untyped() + ); + } else { + assert_ne!(from_font_desc, resized_font_desc); + assert_eq!( + from_font_desc.attributes().to_untyped(), + resized_font_desc.attributes().to_untyped() + ); + } + } + + let from_font_desc = from_font_desc + .create_copy_with_attributes(attrs_dict.to_untyped()) + .unwrap(); + let resized_font_desc = resized_font_desc + .create_copy_with_attributes(attrs_dict.to_untyped()) + .unwrap(); + (from_font_desc, resized_font_desc) + }; + + // setting the variation works properly if we use system font desc + let (from_font_desc, resized_font_desc) = create_vars(sys_font.copy_descriptor()); + assert_eq!(from_font_desc, resized_font_desc); + assert!(resized_font_desc + .attributes() + .find(variation_attribute.clone()) + .is_some()); + + // but doesn't if we refer to it by name + let ps = sys_font.postscript_name(); + let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap(); + let ctfont = new_from_CGFont(&cgfont, 0.); + + let (from_font_desc, resized_font_desc) = create_vars(ctfont.copy_descriptor()); + if macos_version() >= (10, 15, 0) { + assert_ne!(from_font_desc, resized_font_desc); + } + + if macos_version() >= (10, 13, 0) { + assert!(from_font_desc + .attributes() + .find(variation_attribute.clone()) + .is_some()); + if macos_version() >= (11, 0, 0) { + assert!(resized_font_desc + .attributes() + .find(variation_attribute) + .is_none()); + } else { + assert!(resized_font_desc + .attributes() + .find(variation_attribute) + .is_some()); + }; + } +} + +#[test] +fn system_font_variation() { + use crate::*; + + let small = new_ui_font_for_language(kCTFontSystemDetailFontType, 19., None); + + // but we can still construct the CGFont by name + let ps = small.postscript_name(); + let cgfont = CGFont::from_name(&CFString::new(&ps)).unwrap(); + let cgfont = new_from_CGFont(&cgfont, 0.); + let desc = cgfont.copy_descriptor(); + + let vals: Vec<(CFNumber, CFNumber)> = + vec![(CFNumber::from(0x6f70737a /* opsz */), CFNumber::from(17.))]; + let vals_dict = CFDictionary::from_CFType_pairs(&vals); + let variation_attribute = + unsafe { CFString::wrap_under_get_rule(font_descriptor::kCTFontVariationAttribute) }; + let attrs_dict = CFDictionary::from_CFType_pairs(&[(variation_attribute, vals_dict)]); + let ct_var_font_desc = desc + .create_copy_with_attributes(attrs_dict.to_untyped()) + .unwrap(); + let attrs = ct_var_font_desc.attributes(); + let var_attr = attrs.find(CFString::from_static_string("NSCTFontVariationAttribute")); + if macos_version() >= (11, 0, 0) { + // the variation goes away + assert!(var_attr.is_none()); + } else { + assert!(var_attr.is_some()); + } + + dbg!(ct_var_font_desc); +} + +#[test] +fn ui_font() { + // pass some garbagey inputs + new_ui_font_for_language( + kCTFontSystemDetailFontType, + 10000009., + Some(CFString::from("Gofld")), + ); +} diff --git a/third_party/rust/core-text/src/font_collection.rs b/third_party/rust/core-text/src/font_collection.rs new file mode 100644 index 0000000000..bf5822249a --- /dev/null +++ b/third_party/rust/core-text/src/font_collection.rs @@ -0,0 +1,136 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use font_descriptor; +use font_descriptor::{CTFontDescriptor, CTFontDescriptorCreateMatchingFontDescriptors}; +use font_manager::{ + CTFontManagerCopyAvailableFontFamilyNames, CTFontManagerCopyAvailablePostScriptNames, +}; + +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{CFTypeID, TCFType}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::number::CFNumber; +use core_foundation::set::CFSet; +use core_foundation::string::{CFString, CFStringRef}; + +use std::os::raw::c_void; + +#[repr(C)] +pub struct __CTFontCollection(c_void); + +pub type CTFontCollectionRef = *const __CTFontCollection; + +declare_TCFType! { + CTFontCollection, CTFontCollectionRef +} +impl_TCFType!( + CTFontCollection, + CTFontCollectionRef, + CTFontCollectionGetTypeID +); +impl_CFTypeDescription!(CTFontCollection); + +impl CTFontCollection { + pub fn get_descriptors(&self) -> Option> { + // surprise! this function follows the Get rule, despite being named *Create*. + // So we have to addRef it to avoid CTFontCollection from double freeing it later. + unsafe { + let font_descriptors = CTFontCollectionCreateMatchingFontDescriptors(self.0); + if font_descriptors.is_null() { + // This returns null if there are no matching font descriptors. + None + } else { + Some(CFArray::wrap_under_get_rule(font_descriptors)) + } + } + } +} + +pub fn new_from_descriptors(descs: &CFArray) -> CTFontCollection { + unsafe { + let key = CFString::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption); + let value = CFNumber::from(1i64); + let options = CFDictionary::from_CFType_pairs(&[(key.as_CFType(), value.as_CFType())]); + let font_collection_ref = CTFontCollectionCreateWithFontDescriptors( + descs.as_concrete_TypeRef(), + options.as_concrete_TypeRef(), + ); + CTFontCollection::wrap_under_create_rule(font_collection_ref) + } +} + +pub fn create_for_all_families() -> CTFontCollection { + unsafe { + let key = CFString::wrap_under_get_rule(kCTFontCollectionRemoveDuplicatesOption); + let value = CFNumber::from(1i64); + let options = CFDictionary::from_CFType_pairs(&[(key.as_CFType(), value.as_CFType())]); + let font_collection_ref = + CTFontCollectionCreateFromAvailableFonts(options.as_concrete_TypeRef()); + CTFontCollection::wrap_under_create_rule(font_collection_ref) + } +} + +pub fn create_for_family(family: &str) -> Option { + use font_descriptor::kCTFontFamilyNameAttribute; + + unsafe { + let family_attr = CFString::wrap_under_get_rule(kCTFontFamilyNameAttribute); + let family_name: CFString = family.parse().unwrap(); + let specified_attrs = + CFDictionary::from_CFType_pairs(&[(family_attr.clone(), family_name.as_CFType())]); + + let wildcard_desc: CTFontDescriptor = + font_descriptor::new_from_attributes(&specified_attrs); + let mandatory_attrs = CFSet::from_slice(&[family_attr.as_CFType()]); + let matched_descs = CTFontDescriptorCreateMatchingFontDescriptors( + wildcard_desc.as_concrete_TypeRef(), + mandatory_attrs.as_concrete_TypeRef(), + ); + if matched_descs.is_null() { + return None; + } + let matched_descs = CFArray::wrap_under_create_rule(matched_descs); + // I suppose one doesn't even need the CTFontCollection object at this point. + // But we stick descriptors into and out of it just to provide a nice wrapper API. + Some(new_from_descriptors(&matched_descs)) + } +} + +pub fn get_family_names() -> CFArray { + unsafe { CFArray::wrap_under_create_rule(CTFontManagerCopyAvailableFontFamilyNames()) } +} + +pub fn get_postscript_names() -> CFArray { + unsafe { CFArray::wrap_under_create_rule(CTFontManagerCopyAvailablePostScriptNames()) } +} + +extern "C" { + /* + * CTFontCollection.h + */ + + static kCTFontCollectionRemoveDuplicatesOption: CFStringRef; + + //fn CTFontCollectionCreateCopyWithFontDescriptors(original: CTFontCollectionRef, + // descriptors: CFArrayRef, + // options: CFDictionaryRef) -> CTFontCollectionRef; + fn CTFontCollectionCreateFromAvailableFonts(options: CFDictionaryRef) -> CTFontCollectionRef; + // this stupid function doesn't actually do any wildcard expansion; + // it just chooses the best match. Use + // CTFontDescriptorCreateMatchingDescriptors instead. + fn CTFontCollectionCreateMatchingFontDescriptors(collection: CTFontCollectionRef) + -> CFArrayRef; + fn CTFontCollectionCreateWithFontDescriptors( + descriptors: CFArrayRef, + options: CFDictionaryRef, + ) -> CTFontCollectionRef; + //fn CTFontCollectionCreateMatchingFontDescriptorsSortedWithCallback; + fn CTFontCollectionGetTypeID() -> CFTypeID; +} diff --git a/third_party/rust/core-text/src/font_descriptor.rs b/third_party/rust/core-text/src/font_descriptor.rs new file mode 100644 index 0000000000..e5506d2162 --- /dev/null +++ b/third_party/rust/core-text/src/font_descriptor.rs @@ -0,0 +1,441 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use core_foundation::array::CFArrayRef; +use core_foundation::base::{CFType, CFTypeID, CFTypeRef, TCFType}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::number::{CFNumber, CFNumberRef}; +use core_foundation::set::CFSetRef; +use core_foundation::string::{CFString, CFStringRef}; +use core_foundation::url::{CFURLRef, CFURL}; +use core_graphics::base::CGFloat; + +use std::os::raw::c_void; +use std::path::PathBuf; + +/* +* CTFontTraits.h +*/ +// actually, these are extern enums +pub type CTFontFormat = u32; +pub const kCTFontFormatUnrecognized: CTFontFormat = 0; +pub const kCTFontFormatOpenTypePostScript: CTFontFormat = 1; +pub const kCTFontFormatOpenTypeTrueType: CTFontFormat = 2; +pub const kCTFontFormatTrueType: CTFontFormat = 3; +pub const kCTFontFormatPostScript: CTFontFormat = 4; +pub const kCTFontFormatBitmap: CTFontFormat = 5; + +pub const kCTFontClassMaskShift: u32 = 28; + +pub type CTFontSymbolicTraits = u32; +pub const kCTFontItalicTrait: CTFontSymbolicTraits = 1 << 0; +pub const kCTFontBoldTrait: CTFontSymbolicTraits = 1 << 1; +pub const kCTFontExpandedTrait: CTFontSymbolicTraits = 1 << 5; +pub const kCTFontCondensedTrait: CTFontSymbolicTraits = 1 << 6; +pub const kCTFontMonoSpaceTrait: CTFontSymbolicTraits = 1 << 10; +pub const kCTFontVerticalTrait: CTFontSymbolicTraits = 1 << 11; +pub const kCTFontUIOptimizedTrait: CTFontSymbolicTraits = 1 << 12; +pub const kCTFontColorGlyphsTrait: CTFontSymbolicTraits = 1 << 13; +pub const kCTFontClassMaskTrait: CTFontSymbolicTraits = 15 << kCTFontClassMaskShift; + +pub trait SymbolicTraitAccessors { + fn is_italic(&self) -> bool; + fn is_bold(&self) -> bool; + fn is_expanded(&self) -> bool; + fn is_condensed(&self) -> bool; + fn is_monospace(&self) -> bool; + fn is_vertical(&self) -> bool; +} + +impl SymbolicTraitAccessors for CTFontSymbolicTraits { + fn is_italic(&self) -> bool { + (*self & kCTFontItalicTrait) != 0 + } + fn is_bold(&self) -> bool { + (*self & kCTFontBoldTrait) != 0 + } + fn is_expanded(&self) -> bool { + (*self & kCTFontExpandedTrait) != 0 + } + fn is_condensed(&self) -> bool { + (*self & kCTFontCondensedTrait) != 0 + } + fn is_monospace(&self) -> bool { + (*self & kCTFontMonoSpaceTrait) != 0 + } + fn is_vertical(&self) -> bool { + (*self & kCTFontVerticalTrait) != 0 + } +} + +pub type CTFontStylisticClass = u32; +pub const kCTFontUnknownClass: CTFontStylisticClass = 0 << kCTFontClassMaskShift; +pub const kCTFontOldStyleSerifsClass: CTFontStylisticClass = 1 << kCTFontClassMaskShift; +pub const kCTFontTransitionalSerifsClass: CTFontStylisticClass = 2 << kCTFontClassMaskShift; +pub const kCTFontModernSerifsClass: CTFontStylisticClass = 3 << kCTFontClassMaskShift; +pub const kCTFontClarendonSerifsClass: CTFontStylisticClass = 4 << kCTFontClassMaskShift; +pub const kCTFontSlabSerifsClass: CTFontStylisticClass = 5 << kCTFontClassMaskShift; +pub const kCTFontFreeformSerifsClass: CTFontStylisticClass = 7 << kCTFontClassMaskShift; +pub const kCTFontSansSerifClass: CTFontStylisticClass = 8 << kCTFontClassMaskShift; +pub const kCTFontOrnamentalsClass: CTFontStylisticClass = 9 << kCTFontClassMaskShift; +pub const kCTFontScriptsClass: CTFontStylisticClass = 10 << kCTFontClassMaskShift; +pub const kCTFontSymbolicClass: CTFontStylisticClass = 12 << kCTFontClassMaskShift; + +pub trait StylisticClassAccessors { + fn is_serif(&self) -> bool; + fn is_sans_serif(&self) -> bool; + fn is_script(&self) -> bool; + fn is_fantasy(&self) -> bool; + fn is_symbols(&self) -> bool; +} + +impl StylisticClassAccessors for CTFontStylisticClass { + fn is_serif(&self) -> bool { + let any_serif_class = kCTFontOldStyleSerifsClass + | kCTFontTransitionalSerifsClass + | kCTFontModernSerifsClass + | kCTFontClarendonSerifsClass + | kCTFontSlabSerifsClass + | kCTFontFreeformSerifsClass; + + (*self & any_serif_class) != 0 + } + + fn is_sans_serif(&self) -> bool { + (*self & kCTFontSansSerifClass) != 0 + } + + fn is_script(&self) -> bool { + (*self & kCTFontScriptsClass) != 0 + } + + fn is_fantasy(&self) -> bool { + (*self & kCTFontOrnamentalsClass) != 0 + } + + fn is_symbols(&self) -> bool { + (*self & kCTFontSymbolicClass) != 0 + } +} + +pub type CTFontAttributes = CFDictionary; + +pub type CTFontTraits = CFDictionary; + +pub trait TraitAccessors { + fn symbolic_traits(&self) -> CTFontSymbolicTraits; + fn normalized_weight(&self) -> f64; + fn normalized_width(&self) -> f64; + fn normalized_slant(&self) -> f64; +} + +trait TraitAccessorPrivate { + fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber; +} + +impl TraitAccessorPrivate for CTFontTraits { + fn extract_number_for_key(&self, key: CFStringRef) -> CFNumber { + let cftype = self.get(key); + cftype.downcast::().unwrap() + } +} + +impl TraitAccessors for CTFontTraits { + fn symbolic_traits(&self) -> CTFontSymbolicTraits { + unsafe { + let number = self.extract_number_for_key(kCTFontSymbolicTrait); + number.to_i64().unwrap() as u32 + } + } + + fn normalized_weight(&self) -> f64 { + unsafe { + let number = self.extract_number_for_key(kCTFontWeightTrait); + number.to_f64().unwrap() + } + } + + fn normalized_width(&self) -> f64 { + unsafe { + let number = self.extract_number_for_key(kCTFontWidthTrait); + number.to_f64().unwrap() + } + } + + fn normalized_slant(&self) -> f64 { + unsafe { + let number = self.extract_number_for_key(kCTFontSlantTrait); + number.to_f64().unwrap() + } + } +} + +/* +* CTFontDescriptor.h +*/ +pub type CTFontOrientation = u32; +pub const kCTFontDefaultOrientation: CTFontOrientation = 0; // deprecated since macOS 10.11, iOS 9 +pub const kCTFontOrientationDefault: CTFontOrientation = 0; // introduced in macOS 10.8, iOS 6 +pub const kCTFontHorizontalOrientation: CTFontOrientation = 1; // deprecated since macOS 10.11, iOS 9 +pub const kCTFontOrientationHorizontal: CTFontOrientation = 1; // introduced in macOS 10.8, iOS 6 +pub const kCTFontVerticalOrientation: CTFontOrientation = 2; // deprecated since macOS 10.11, iOS 9 +pub const kCTFontOrientationVertical: CTFontOrientation = 2; // introduced in macOS 10.8, iOS 6 + +pub type CTFontPriority = u32; +pub const kCTFontPrioritySystem: CTFontPriority = 10000; +pub const kCTFontPriorityNetwork: CTFontPriority = 20000; +pub const kCTFontPriorityComputer: CTFontPriority = 30000; +pub const kCTFontPriorityUser: CTFontPriority = 40000; +pub const kCTFontPriorityDynamic: CTFontPriority = 50000; +pub const kCTFontPriorityProcess: CTFontPriority = 60000; + +#[repr(C)] +pub struct __CTFontDescriptor(c_void); + +pub type CTFontDescriptorRef = *const __CTFontDescriptor; + +declare_TCFType! { + CTFontDescriptor, CTFontDescriptorRef +} +impl_TCFType!( + CTFontDescriptor, + CTFontDescriptorRef, + CTFontDescriptorGetTypeID +); +impl_CFTypeDescription!(CTFontDescriptor); + +// "Font objects (CTFont, CTFontDescriptor, and associated objects) can be used +// simultaneously by multiple operations, work queues, or threads." +unsafe impl Send for CTFontDescriptor {} +unsafe impl Sync for CTFontDescriptor {} + +impl CTFontDescriptor { + fn get_string_attribute(&self, attribute: CFStringRef) -> Option { + unsafe { + let value = CTFontDescriptorCopyAttribute(self.0, attribute); + if value.is_null() { + return None; + } + + let value = CFType::wrap_under_create_rule(value); + assert!(value.instance_of::()); + let s = CFString::wrap_under_get_rule(value.as_CFTypeRef() as CFStringRef); + Some(s.to_string()) + } + } +} + +impl CTFontDescriptor { + pub fn family_name(&self) -> String { + unsafe { + let value = self.get_string_attribute(kCTFontFamilyNameAttribute); + value.expect("A font must have a non-null family name.") + } + } + + pub fn font_name(&self) -> String { + unsafe { + let value = self.get_string_attribute(kCTFontNameAttribute); + value.expect("A font must have a non-null name.") + } + } + + pub fn style_name(&self) -> String { + unsafe { + let value = self.get_string_attribute(kCTFontStyleNameAttribute); + value.expect("A font must have a non-null style name.") + } + } + + pub fn display_name(&self) -> String { + unsafe { + let value = self.get_string_attribute(kCTFontDisplayNameAttribute); + value.expect("A font must have a non-null display name.") + } + } + + pub fn font_format(&self) -> Option { + unsafe { + let value = CTFontDescriptorCopyAttribute(self.0, kCTFontFormatAttribute); + if value.is_null() { + return None; + } + + let value = CFType::wrap_under_create_rule(value); + assert!(value.instance_of::()); + let format = CFNumber::wrap_under_get_rule(value.as_CFTypeRef() as CFNumberRef); + format.to_i32().map(|x| x as CTFontFormat) + } + } + + pub fn font_path(&self) -> Option { + unsafe { + let value = CTFontDescriptorCopyAttribute(self.0, kCTFontURLAttribute); + if value.is_null() { + return None; + } + + let value = CFType::wrap_under_create_rule(value); + assert!(value.instance_of::()); + let url = CFURL::wrap_under_get_rule(value.as_CFTypeRef() as CFURLRef); + url.to_path() + } + } + + pub fn traits(&self) -> CTFontTraits { + unsafe { + let value = CTFontDescriptorCopyAttribute(self.0, kCTFontTraitsAttribute); + assert!(!value.is_null()); + let value = CFType::wrap_under_create_rule(value); + assert!(value.instance_of::()); + CFDictionary::wrap_under_get_rule(value.as_CFTypeRef() as CFDictionaryRef) + } + } + + pub fn create_copy_with_attributes(&self, attr: CFDictionary) -> Result { + unsafe { + let desc = CTFontDescriptorCreateCopyWithAttributes( + self.as_concrete_TypeRef(), + attr.as_concrete_TypeRef(), + ); + if desc.is_null() { + return Err(()); + } + Ok(CTFontDescriptor::wrap_under_create_rule(desc)) + } + } + + pub fn attributes(&self) -> CFDictionary { + unsafe { + let attrs = CTFontDescriptorCopyAttributes(self.as_concrete_TypeRef()); + CFDictionary::wrap_under_create_rule(attrs) + } + } +} + +pub fn new_from_attributes(attributes: &CFDictionary) -> CTFontDescriptor { + unsafe { + let result: CTFontDescriptorRef = + CTFontDescriptorCreateWithAttributes(attributes.as_concrete_TypeRef()); + CTFontDescriptor::wrap_under_create_rule(result) + } +} + +pub fn new_from_variations(variations: &CFDictionary) -> CTFontDescriptor { + unsafe { + let var_key = CFString::wrap_under_get_rule(kCTFontVariationAttribute); + let var_val = CFType::wrap_under_get_rule(variations.as_CFTypeRef()); + let attributes = CFDictionary::from_CFType_pairs(&[(var_key, var_val)]); + new_from_attributes(&attributes) + } +} + +pub fn new_from_postscript_name(name: &CFString) -> CTFontDescriptor { + unsafe { + let result: CTFontDescriptorRef = + CTFontDescriptorCreateWithNameAndSize(name.as_concrete_TypeRef(), 0.0); + CTFontDescriptor::wrap_under_create_rule(result) + } +} + +pub fn debug_descriptor(desc: &CTFontDescriptor) { + println!("family: {}", desc.family_name()); + println!("name: {}", desc.font_name()); + println!("style: {}", desc.style_name()); + println!("display: {}", desc.display_name()); + println!("path: {:?}", desc.font_path()); + desc.show(); +} + +extern "C" { + /* + * CTFontTraits.h + */ + + // font trait constants + pub static kCTFontSymbolicTrait: CFStringRef; + pub static kCTFontWeightTrait: CFStringRef; + pub static kCTFontWidthTrait: CFStringRef; + pub static kCTFontSlantTrait: CFStringRef; + + /* + * CTFontDescriptor.h + */ + + // font attribute constants. Note that the name-related attributes + // here are somewhat flaky. Servo creates CTFont instances and + // then uses CTFontCopyName to get more fine-grained names. + pub static kCTFontURLAttribute: CFStringRef; // value: CFURLRef + pub static kCTFontNameAttribute: CFStringRef; // value: CFStringRef + pub static kCTFontDisplayNameAttribute: CFStringRef; // value: CFStringRef + pub static kCTFontFamilyNameAttribute: CFStringRef; // value: CFStringRef + pub static kCTFontStyleNameAttribute: CFStringRef; // value: CFStringRef + pub static kCTFontTraitsAttribute: CFStringRef; + pub static kCTFontVariationAttribute: CFStringRef; + pub static kCTFontSizeAttribute: CFStringRef; + pub static kCTFontMatrixAttribute: CFStringRef; + pub static kCTFontCascadeListAttribute: CFStringRef; + pub static kCTFontCharacterSetAttribute: CFStringRef; + pub static kCTFontLanguagesAttribute: CFStringRef; + pub static kCTFontBaselineAdjustAttribute: CFStringRef; + pub static kCTFontMacintoshEncodingsAttribute: CFStringRef; + pub static kCTFontFeaturesAttribute: CFStringRef; + pub static kCTFontFeatureSettingsAttribute: CFStringRef; + pub static kCTFontFixedAdvanceAttribute: CFStringRef; + pub static kCTFontOrientationAttribute: CFStringRef; + pub static kCTFontFormatAttribute: CFStringRef; + pub static kCTFontRegistrationScopeAttribute: CFStringRef; + pub static kCTFontPriorityAttribute: CFStringRef; + pub static kCTFontEnabledAttribute: CFStringRef; + + pub fn CTFontDescriptorCopyAttribute( + descriptor: CTFontDescriptorRef, + attribute: CFStringRef, + ) -> CFTypeRef; + pub fn CTFontDescriptorCopyAttributes(descriptor: CTFontDescriptorRef) -> CFDictionaryRef; + pub fn CTFontDescriptorCopyLocalizedAttribute( + descriptor: CTFontDescriptorRef, + attribute: CFStringRef, + language: *mut CFStringRef, + ) -> CFTypeRef; + pub fn CTFontDescriptorCreateCopyWithAttributes( + original: CTFontDescriptorRef, + attributes: CFDictionaryRef, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateCopyWithFeature( + original: CTFontDescriptorRef, + featureTypeIdentifier: CFNumberRef, + featureSelectorIdentifier: CFNumberRef, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateCopyWithVariation( + original: CTFontDescriptorRef, + variationIdentifier: CFNumberRef, + variationValue: CGFloat, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateMatchingFontDescriptor( + descriptor: CTFontDescriptorRef, + mandatoryAttributes: CFSetRef, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateWithAttributes(attributes: CFDictionaryRef) + -> CTFontDescriptorRef; + pub fn CTFontDescriptorCreateWithNameAndSize( + name: CFStringRef, + size: CGFloat, + ) -> CTFontDescriptorRef; + pub fn CTFontDescriptorGetTypeID() -> CFTypeID; +} + +extern "C" { + pub fn CTFontDescriptorCreateMatchingFontDescriptors( + descriptor: CTFontDescriptorRef, + mandatoryAttributes: CFSetRef, + ) -> CFArrayRef; +} diff --git a/third_party/rust/core-text/src/font_manager.rs b/third_party/rust/core-text/src/font_manager.rs new file mode 100644 index 0000000000..dff0f8d0fb --- /dev/null +++ b/third_party/rust/core-text/src/font_manager.rs @@ -0,0 +1,76 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef}; +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::TCFType; +use core_foundation::data::{CFData, CFDataRef}; +use core_foundation::string::CFString; +use core_foundation::url::CFURLRef; + +pub fn copy_available_font_family_names() -> CFArray { + unsafe { TCFType::wrap_under_create_rule(CTFontManagerCopyAvailableFontFamilyNames()) } +} + +pub fn create_font_descriptor(buffer: &[u8]) -> Result { + let cf_data = CFData::from_buffer(buffer); + unsafe { + let ct_font_descriptor_ref = + CTFontManagerCreateFontDescriptorFromData(cf_data.as_concrete_TypeRef()); + if ct_font_descriptor_ref.is_null() { + return Err(()); + } + Ok(CTFontDescriptor::wrap_under_create_rule( + ct_font_descriptor_ref, + )) + } +} + +pub fn create_font_descriptor_with_data(data: CFData) -> Result { + unsafe { + let ct_font_descriptor_ref = + CTFontManagerCreateFontDescriptorFromData(data.as_concrete_TypeRef()); + if ct_font_descriptor_ref.is_null() { + return Err(()); + } + Ok(CTFontDescriptor::wrap_under_create_rule( + ct_font_descriptor_ref, + )) + } +} + +extern "C" { + /* + * CTFontManager.h + */ + + // Incomplete function bindings are mostly related to CoreText font matching, which + // we implement in a platform-independent manner using FontMatcher. + + //pub fn CTFontManagerCompareFontFamilyNames + pub fn CTFontManagerCopyAvailableFontURLs() -> CFArrayRef; + pub fn CTFontManagerCopyAvailableFontFamilyNames() -> CFArrayRef; + pub fn CTFontManagerCopyAvailablePostScriptNames() -> CFArrayRef; + pub fn CTFontManagerCreateFontDescriptorsFromURL(fileURL: CFURLRef) -> CFArrayRef; + pub fn CTFontManagerCreateFontDescriptorFromData(data: CFDataRef) -> CTFontDescriptorRef; + //pub fn CTFontManagerCreateFontRequestRunLoopSource + //pub fn CTFontManagerEnableFontDescriptors + //pub fn CTFontManagerGetAutoActivationSetting + //pub fn CTFontManagerGetScopeForURL + //pub fn CTFontManagerGetAutoActivationSetting + //pub fn CTFontManagerGetScopeForURL + pub fn CTFontManagerIsSupportedFont(fontURL: CFURLRef) -> bool; + //pub fn CTFontManagerRegisterFontsForURL + //pub fn CTFontManagerRegisterFontsForURLs + //pub fn CTFontManagerRegisterGraphicsFont + //pub fn CTFontManagerSetAutoActivationSetting + //pub fn CTFontManagerUnregisterFontsForURL + //pub fn CTFontManagerUnregisterFontsForURLs + //pub fn CTFontManagerUnregisterGraphicsFont +} diff --git a/third_party/rust/core-text/src/frame.rs b/third_party/rust/core-text/src/frame.rs new file mode 100644 index 0000000000..03fcee902a --- /dev/null +++ b/third_party/rust/core-text/src/frame.rs @@ -0,0 +1,94 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::line::CTLine; +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::base::{CFRange, CFTypeID, TCFType}; +use core_graphics::context::{CGContext, CGContextRef}; +use core_graphics::geometry::CGPoint; +use core_graphics::path::{CGPath, SysCGPathRef}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use std::os::raw::c_void; + +#[repr(C)] +pub struct __CTFrame(c_void); + +pub type CTFrameRef = *const __CTFrame; + +declare_TCFType! { + CTFrame, CTFrameRef +} +impl_TCFType!(CTFrame, CTFrameRef, CTFrameGetTypeID); +impl_CFTypeDescription!(CTFrame); + +impl CTFrame { + /// The `CGPath` used to create this `CTFrame`. + pub fn get_path(&self) -> CGPath { + unsafe { CGPath::from_ptr(CTFrameGetPath(self.as_concrete_TypeRef())).clone() } + } + + /// Returns an owned copy of the underlying lines. + /// + /// Each line is retained, and will remain valid past the life of this `CTFrame`. + pub fn get_lines(&self) -> Vec { + unsafe { + let array_ref = CTFrameGetLines(self.as_concrete_TypeRef()); + let array: CFArray = CFArray::wrap_under_get_rule(array_ref); + array + .iter() + .map(|l| CTLine::wrap_under_get_rule(l.as_concrete_TypeRef())) + .collect() + } + } + + /// Return the origin of each line in a given range. + /// + /// If no range is provided, returns the origin of each line in the frame. + /// + /// If the length of the range is 0, returns the origin of all lines from + /// the range's start to the end. + /// + /// The origin is the position relative to the path used to create this `CTFFrame`; + /// to get the path use [`get_path`]. + /// + /// [`get_path`]: #method.get_path + pub fn get_line_origins(&self, range: impl Into>) -> Vec { + let range = range.into().unwrap_or_else(|| CFRange::init(0, 0)); + let len = match range.length { + // range length of 0 means 'all remaining lines' + 0 => unsafe { + let array_ref = CTFrameGetLines(self.as_concrete_TypeRef()); + let array: CFArray = CFArray::wrap_under_get_rule(array_ref); + array.len() - range.location + }, + n => n, + }; + let len = len.max(0) as usize; + let mut out = vec![CGPoint::new(0., 0.); len]; + unsafe { + CTFrameGetLineOrigins(self.as_concrete_TypeRef(), range, out.as_mut_ptr()); + } + out + } + + pub fn draw(&self, context: &CGContextRef) { + unsafe { + CTFrameDraw(self.as_concrete_TypeRef(), context.as_ptr()); + } + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTFrameGetTypeID() -> CFTypeID; + fn CTFrameGetLines(frame: CTFrameRef) -> CFArrayRef; + fn CTFrameDraw(frame: CTFrameRef, context: *mut ::CType); + fn CTFrameGetLineOrigins(frame: CTFrameRef, range: CFRange, origins: *mut CGPoint); + fn CTFrameGetPath(frame: CTFrameRef) -> SysCGPathRef; +} diff --git a/third_party/rust/core-text/src/framesetter.rs b/third_party/rust/core-text/src/framesetter.rs new file mode 100644 index 0000000000..dacb540b85 --- /dev/null +++ b/third_party/rust/core-text/src/framesetter.rs @@ -0,0 +1,94 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::frame::{CTFrame, CTFrameRef}; +use core_foundation::attributed_string::CFAttributedStringRef; +use core_foundation::base::{CFRange, CFTypeID, TCFType}; +use core_foundation::dictionary::CFDictionaryRef; +use core_graphics::geometry::CGSize; +use core_graphics::path::{CGPath, CGPathRef}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use std::os::raw::c_void; +use std::ptr::null; + +#[repr(C)] +pub struct __CTFramesetter(c_void); + +pub type CTFramesetterRef = *const __CTFramesetter; + +declare_TCFType! { + CTFramesetter, CTFramesetterRef +} +impl_TCFType!(CTFramesetter, CTFramesetterRef, CTFramesetterGetTypeID); +impl_CFTypeDescription!(CTFramesetter); + +impl CTFramesetter { + pub fn new_with_attributed_string(string: CFAttributedStringRef) -> Self { + unsafe { + let ptr = CTFramesetterCreateWithAttributedString(string); + CTFramesetter::wrap_under_create_rule(ptr) + } + } + + pub fn create_frame(&self, string_range: CFRange, path: &CGPathRef) -> CTFrame { + unsafe { + let ptr = CTFramesetterCreateFrame( + self.as_concrete_TypeRef(), + string_range, + path.as_ptr(), + null(), + ); + + CTFrame::wrap_under_create_rule(ptr) + } + } + + /// Suggest an appropriate frame size for displaying a text range. + /// + /// Returns a tuple containing an appropriate size (that should be smaller + /// than the provided constraints) as well as the range of text that fits in + /// this frame. + pub fn suggest_frame_size_with_constraints( + &self, + string_range: CFRange, + frame_attributes: CFDictionaryRef, + constraints: CGSize, + ) -> (CGSize, CFRange) { + unsafe { + let mut fit_range = CFRange::init(0, 0); + let size = CTFramesetterSuggestFrameSizeWithConstraints( + self.as_concrete_TypeRef(), + string_range, + frame_attributes, + constraints, + &mut fit_range, + ); + (size, fit_range) + } + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTFramesetterGetTypeID() -> CFTypeID; + fn CTFramesetterCreateWithAttributedString(string: CFAttributedStringRef) -> CTFramesetterRef; + fn CTFramesetterCreateFrame( + framesetter: CTFramesetterRef, + string_range: CFRange, + path: *mut ::CType, + attributes: *const c_void, + ) -> CTFrameRef; + fn CTFramesetterSuggestFrameSizeWithConstraints( + framesetter: CTFramesetterRef, + string_range: CFRange, + frame_attributes: CFDictionaryRef, + constraints: CGSize, + fitRange: *mut CFRange, + ) -> CGSize; +} diff --git a/third_party/rust/core-text/src/lib.rs b/third_party/rust/core-text/src/lib.rs new file mode 100644 index 0000000000..be394b020d --- /dev/null +++ b/third_party/rust/core-text/src/lib.rs @@ -0,0 +1,34 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "core_text"] +#![crate_type = "rlib"] +#![allow(non_snake_case)] + +/*! +Many of these functions will add objects to the autorelease pool. +If you don't have one this will cause leaks. +*/ + +extern crate foreign_types; +extern crate libc; + +#[macro_use] +extern crate core_foundation; +extern crate core_graphics; + +pub mod font; +pub mod font_collection; +pub mod font_descriptor; +pub mod font_manager; +pub mod frame; +pub mod framesetter; +pub mod line; +pub mod run; +pub mod string_attributes; diff --git a/third_party/rust/core-text/src/line.rs b/third_party/rust/core-text/src/line.rs new file mode 100644 index 0000000000..b2645aaf5c --- /dev/null +++ b/third_party/rust/core-text/src/line.rs @@ -0,0 +1,125 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core_foundation::array::{CFArray, CFArrayRef}; +use core_foundation::attributed_string::CFAttributedStringRef; +use core_foundation::base::{CFIndex, CFRange, CFTypeID, TCFType}; +use core_graphics::base::CGFloat; +use core_graphics::context::CGContext; +use core_graphics::geometry::{CGPoint, CGRect}; +use foreign_types::ForeignType; +use run::CTRun; +use std::os::raw::c_void; + +#[repr(C)] +pub struct __CTLine(c_void); + +pub type CTLineRef = *const __CTLine; + +declare_TCFType! { + CTLine, CTLineRef +} +impl_TCFType!(CTLine, CTLineRef, CTLineGetTypeID); +impl_CFTypeDescription!(CTLine); + +/// Metrics for a given line. +pub struct TypographicBounds { + pub width: CGFloat, + pub ascent: CGFloat, + pub descent: CGFloat, + pub leading: CGFloat, +} + +impl CTLine { + pub fn new_with_attributed_string(string: CFAttributedStringRef) -> Self { + unsafe { + let ptr = CTLineCreateWithAttributedString(string); + CTLine::wrap_under_create_rule(ptr) + } + } + + pub fn glyph_runs(&self) -> CFArray { + unsafe { TCFType::wrap_under_get_rule(CTLineGetGlyphRuns(self.0)) } + } + + pub fn get_string_range(&self) -> CFRange { + unsafe { CTLineGetStringRange(self.as_concrete_TypeRef()) } + } + + pub fn draw(&self, context: &CGContext) { + unsafe { CTLineDraw(self.as_concrete_TypeRef(), context.as_ptr()) } + } + + pub fn get_image_bounds(&self, context: &CGContext) -> CGRect { + unsafe { CTLineGetImageBounds(self.as_concrete_TypeRef(), context.as_ptr()) } + } + + pub fn get_typographic_bounds(&self) -> TypographicBounds { + let mut ascent = 0.0; + let mut descent = 0.0; + let mut leading = 0.0; + unsafe { + let width = CTLineGetTypographicBounds( + self.as_concrete_TypeRef(), + &mut ascent, + &mut descent, + &mut leading, + ); + TypographicBounds { + width, + ascent, + descent, + leading, + } + } + } + + pub fn get_string_index_for_position(&self, position: CGPoint) -> CFIndex { + unsafe { CTLineGetStringIndexForPosition(self.as_concrete_TypeRef(), position) } + } + + pub fn get_string_offset_for_string_index(&self, charIndex: CFIndex) -> CGFloat { + unsafe { + CTLineGetOffsetForStringIndex(self.as_concrete_TypeRef(), charIndex, std::ptr::null()) + } + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTLineGetTypeID() -> CFTypeID; + fn CTLineGetGlyphRuns(line: CTLineRef) -> CFArrayRef; + fn CTLineGetStringRange(line: CTLineRef) -> CFRange; + + // Creating Lines + fn CTLineCreateWithAttributedString(string: CFAttributedStringRef) -> CTLineRef; + + // Drawing the Line + fn CTLineDraw(line: CTLineRef, context: *const core_graphics::sys::CGContext); + + // Measuring Lines + fn CTLineGetImageBounds( + line: CTLineRef, + context: *const core_graphics::sys::CGContext, + ) -> CGRect; + fn CTLineGetTypographicBounds( + line: CTLineRef, + ascent: *mut CGFloat, + descent: *mut CGFloat, + leading: *mut CGFloat, + ) -> CGFloat; + + // Getting Line Positioning + fn CTLineGetStringIndexForPosition(line: CTLineRef, position: CGPoint) -> CFIndex; + fn CTLineGetOffsetForStringIndex( + line: CTLineRef, + charIndex: CFIndex, + secondaryOffset: *const CGFloat, + ) -> CGFloat; +} diff --git a/third_party/rust/core-text/src/run.rs b/third_party/rust/core-text/src/run.rs new file mode 100644 index 0000000000..f84fb46cb7 --- /dev/null +++ b/third_party/rust/core-text/src/run.rs @@ -0,0 +1,159 @@ +// Copyright 2013 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core_foundation::base::{CFIndex, CFRange, CFType, CFTypeID, TCFType}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::string::CFString; +use core_graphics::font::CGGlyph; +use core_graphics::geometry::CGPoint; +use std::borrow::Cow; +use std::os::raw::c_void; +use std::slice; + +#[repr(C)] +pub struct __CTRun(c_void); + +pub type CTRunRef = *const __CTRun; + +declare_TCFType! { + CTRun, CTRunRef +} +impl_TCFType!(CTRun, CTRunRef, CTRunGetTypeID); +impl_CFTypeDescription!(CTRun); + +impl CTRun { + pub fn attributes(&self) -> Option> { + unsafe { + let attrs = CTRunGetAttributes(self.0); + if attrs.is_null() { + return None; + } + Some(TCFType::wrap_under_get_rule(attrs)) + } + } + pub fn glyph_count(&self) -> CFIndex { + unsafe { CTRunGetGlyphCount(self.0) } + } + + pub fn glyphs(&self) -> Cow<[CGGlyph]> { + unsafe { + // CTRunGetGlyphsPtr can return null under some not understood circumstances. + // If it does the Apple documentation tells us to allocate our own buffer and call + // CTRunGetGlyphs + let count = CTRunGetGlyphCount(self.0); + let glyphs_ptr = CTRunGetGlyphsPtr(self.0); + if !glyphs_ptr.is_null() { + Cow::from(slice::from_raw_parts(glyphs_ptr, count as usize)) + } else { + let mut vec = Vec::with_capacity(count as usize); + // "If the length of the range is set to 0, then the copy operation will continue + // from the start index of the range to the end of the run" + CTRunGetGlyphs(self.0, CFRange::init(0, 0), vec.as_mut_ptr()); + vec.set_len(count as usize); + Cow::from(vec) + } + } + } + + pub fn positions(&self) -> Cow<[CGPoint]> { + unsafe { + // CTRunGetPositionsPtr can return null under some not understood circumstances. + // If it does the Apple documentation tells us to allocate our own buffer and call + // CTRunGetPositions + let count = CTRunGetGlyphCount(self.0); + let positions_ptr = CTRunGetPositionsPtr(self.0); + if !positions_ptr.is_null() { + Cow::from(slice::from_raw_parts(positions_ptr, count as usize)) + } else { + let mut vec = Vec::with_capacity(count as usize); + // "If the length of the range is set to 0, then the copy operation will continue + // from the start index of the range to the end of the run" + CTRunGetPositions(self.0, CFRange::init(0, 0), vec.as_mut_ptr()); + vec.set_len(count as usize); + Cow::from(vec) + } + } + } + + pub fn string_indices(&self) -> Cow<[CFIndex]> { + unsafe { + // CTRunGetStringIndicesPtr can return null under some not understood circumstances. + // If it does the Apple documentation tells us to allocate our own buffer and call + // CTRunGetStringIndices + let count = CTRunGetGlyphCount(self.0); + let indices_ptr = CTRunGetStringIndicesPtr(self.0); + if !indices_ptr.is_null() { + Cow::from(slice::from_raw_parts(indices_ptr, count as usize)) + } else { + let mut vec = Vec::with_capacity(count as usize); + // "If the length of the range is set to 0, then the copy operation will continue + // from the start index of the range to the end of the run" + CTRunGetStringIndices(self.0, CFRange::init(0, 0), vec.as_mut_ptr()); + vec.set_len(count as usize); + Cow::from(vec) + } + } + } +} + +#[test] +fn create_runs() { + use core_foundation::attributed_string::CFMutableAttributedString; + use font; + use line::*; + use string_attributes::*; + let mut string = CFMutableAttributedString::new(); + string.replace_str(&CFString::new("Food"), CFRange::init(0, 0)); + let len = string.char_len(); + unsafe { + string.set_attribute( + CFRange::init(0, len), + kCTFontAttributeName, + &font::new_from_name("Helvetica", 16.).unwrap(), + ); + } + let line = CTLine::new_with_attributed_string(string.as_concrete_TypeRef()); + let runs = line.glyph_runs(); + assert_eq!(runs.len(), 1); + for run in runs.iter() { + assert_eq!(run.glyph_count(), 4); + let font = run + .attributes() + .unwrap() + .get(CFString::new("NSFont")) + .downcast::() + .unwrap(); + assert_eq!(font.pt_size(), 16.); + + let positions = run.positions(); + assert_eq!(positions.len(), 4); + assert!(positions[0].x < positions[1].x); + + let glyphs = run.glyphs(); + assert_eq!(glyphs.len(), 4); + assert_ne!(glyphs[0], glyphs[1]); + assert_eq!(glyphs[1], glyphs[2]); + + let indices = run.string_indices(); + assert_eq!(indices.as_ref(), &[0, 1, 2, 3]); + } +} + +#[link(name = "CoreText", kind = "framework")] +extern "C" { + fn CTRunGetTypeID() -> CFTypeID; + fn CTRunGetAttributes(run: CTRunRef) -> CFDictionaryRef; + fn CTRunGetGlyphCount(run: CTRunRef) -> CFIndex; + fn CTRunGetPositionsPtr(run: CTRunRef) -> *const CGPoint; + fn CTRunGetPositions(run: CTRunRef, range: CFRange, buffer: *const CGPoint); + fn CTRunGetStringIndicesPtr(run: CTRunRef) -> *const CFIndex; + fn CTRunGetStringIndices(run: CTRunRef, range: CFRange, buffer: *const CFIndex); + fn CTRunGetGlyphsPtr(run: CTRunRef) -> *const CGGlyph; + fn CTRunGetGlyphs(run: CTRunRef, range: CFRange, buffer: *const CGGlyph); +} diff --git a/third_party/rust/core-text/src/string_attributes.rs b/third_party/rust/core-text/src/string_attributes.rs new file mode 100644 index 0000000000..4f38ced4b9 --- /dev/null +++ b/third_party/rust/core-text/src/string_attributes.rs @@ -0,0 +1,19 @@ +use core_foundation::string::CFStringRef; + +extern "C" { + pub static kCTCharacterShapeAttributeName: CFStringRef; + pub static kCTFontAttributeName: CFStringRef; + pub static kCTKernAttributeName: CFStringRef; + pub static kCTLigatureAttributeName: CFStringRef; + pub static kCTForegroundColorAttributeName: CFStringRef; + pub static kCTForegroundColorFromContextAttributeName: CFStringRef; + pub static kCTParagraphStyleAttributeName: CFStringRef; + pub static kCTStrokeWidthAttributeName: CFStringRef; + pub static kCTStrokeColorAttributeName: CFStringRef; + pub static kCTSuperscriptAttributeName: CFStringRef; + pub static kCTUnderlineColorAttributeName: CFStringRef; + pub static kCTUnderlineStyleAttributeName: CFStringRef; + pub static kCTVerticalFormsAttributeName: CFStringRef; + pub static kCTGlyphInfoAttributeName: CFStringRef; + pub static kCTRunDelegateAttributeName: CFStringRef; +} -- cgit v1.2.3