/* -*- mode: C++; tab-width: 4; c-basic-offset: 4; -*- */ /* AbiSource Application Framework * Copyright (C) 1998 AbiSource, Inc. * Copyright (C) 2001-2003 Hubert Figuiere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #import <Cocoa/Cocoa.h> #include "ut_string.h" #include "ut_types.h" #include "ut_assert.h" #include "ut_debugmsg.h" #include "xap_CocoaFont.h" /*******************************************************************/ NSTextStorage *XAP_CocoaFont::s_fontMetricsTextStorage = nil; NSLayoutManager *XAP_CocoaFont::s_fontMetricsLayoutManager = nil; NSTextContainer *XAP_CocoaFont::s_fontMetricsTextContainer = nil; /*******************************************************************/ XAP_CocoaFont::XAP_CocoaFont() : GR_Font(), m_font(nil), m_fontForCache(nil), m_fontProps(nil), _m_coverage(NULL) { m_hashKey = ""; _resetMetricsCache(); m_rfRemap = rf_None; } XAP_CocoaFont::XAP_CocoaFont(NSFont* font) : GR_Font(), m_font(nil), m_fontForCache(nil), m_fontProps(nil), _m_coverage(NULL) { m_hashKey = [[font fontName] UTF8String]; m_font = font; [m_font retain]; _resetMetricsCache(); m_rfRemap = remapFont(m_font); } XAP_CocoaFont::XAP_CocoaFont(const XAP_CocoaFont & copy) : GR_Font(copy), m_font(nil), m_fontForCache(nil), m_fontProps(nil), _m_coverage(NULL) { m_hashKey = copy.hashKey(); m_font = [copy.getNSFont() copy]; _resetMetricsCache(); m_rfRemap = remapFont(m_font); } XAP_CocoaFont::~XAP_CocoaFont() { [m_font release]; [m_fontForCache release]; [m_fontProps release]; DELETEP(_m_coverage); } UT_uint32 XAP_CocoaFont::getSize(void) { if (_m_size == 0) { _m_size = (UT_uint32)[m_font pointSize]; } return _m_size; } const char * XAP_CocoaFont::getName(void) { return [[m_font fontName] UTF8String]; } /* metrics get cached for efficiency. These calls are MUCH CHEAPER than an AppKit call since Font is persistant inside the XAP_CocoaFont, the is almost no cost doing that it should be intersting to see wether it is not even faster to initialize the cache upon construction. */ void XAP_CocoaFont::_resetMetricsCache() { _m_ascent = _m_descent = _m_height = 0.0; _m_size = 0; DELETEP(_m_coverage); _m_coverage = NULL; } float XAP_CocoaFont::getAscent() { if (_m_ascent == 0.0) { _m_ascent = [m_font ascender]; } return _m_ascent; } float XAP_CocoaFont::getDescent() { if (_m_descent == 0.0) { _m_descent = -[m_font descender]; } return _m_descent; } float XAP_CocoaFont::getHeight() { if (_m_height == 0.0) { _m_height = [m_font defaultLineHeightForFont]; } return _m_height; } void XAP_CocoaFont::getCoverage(UT_NumberVector& coverage) { UT_uint32 i, begin; bool lastState = false; bool currentState = false; if (_m_coverage) { xxx_UT_DEBUGMSG(("getCoverage(): return cached coverage\n")); coverage = *_m_coverage; return; } _m_coverage = new UT_NumberVector(10); const char *bitmap = static_cast<const char*>([[[m_font coveredCharacterSet] bitmapRepresentation] bytes]); for (i = 0; i < 0xffff; i++) { currentState = (bitmap[i >> 3] & (((unsigned int)1) << (i & 7))); if (currentState != lastState) { xxx_UT_DEBUGMSG(("getCoverage(): changing state at %lu\n", i)); if (currentState) { begin = i; xxx_UT_DEBUGMSG(("getCoverage(): begin range\n")); } else { _m_coverage->push_back(begin); _m_coverage->push_back(i - begin); xxx_UT_DEBUGMSG(("getCoverage(): adding range %lu - %lu\n", begin, i - begin)); } lastState = currentState; } } coverage = *_m_coverage; } // UT_Rect of glyph in Logical units. // rec.left = bearing Left (distance from origin to start) // rec.width = width of the glyph // rec.top = distance from the origin to the top of the glyph // rec.height = total height of the glyph bool XAP_CocoaFont::glyphBox(UT_UCS4Char g, UT_Rect & rec) const { // FIXME: Write the code for this! UT_ASSERT(0); return false; } UT_sint32 XAP_CocoaFont::measureUnremappedCharForCache(UT_UCSChar cChar) const { if (m_fontForCache == nil) { m_fontForCache = [[NSFontManager sharedFontManager] convertFont:m_font toSize:GR_CharWidthsCache::CACHE_FONT_SIZE]; m_fontProps = [[NSMutableDictionary alloc] init]; [m_fontProps setObject:m_fontForCache forKey:NSFontAttributeName]; } return _measureChar (cChar, m_fontForCache); } #ifdef LAYOUT_CONTAINER_WIDTH #undef LAYOUT_CONTAINER_WIDTH #endif #define LAYOUT_CONTAINER_WIDTH 10000.0f void XAP_CocoaFont::_initMetricsLayouts(void) { s_fontMetricsTextStorage = [[NSTextStorage alloc] init]; s_fontMetricsLayoutManager = [[NSLayoutManager alloc] init]; [s_fontMetricsTextStorage addLayoutManager:s_fontMetricsLayoutManager]; s_fontMetricsTextContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(LAYOUT_CONTAINER_WIDTH, 1000)]; [s_fontMetricsTextContainer setLineFragmentPadding:0]; [s_fontMetricsLayoutManager addTextContainer:s_fontMetricsTextContainer]; } UT_sint32 XAP_CocoaFont::_measureChar(UT_UCSChar cChar, NSFont * font) const { if (!s_fontMetricsTextStorage) { _initMetricsLayouts(); } if (!s_fontMetricsTextStorage) { UT_ASSERT(s_fontMetricsTextStorage); return 0; } UT_uint32 charWidth = 0; unichar c2 = remapChar(cChar, remapFont(font)); if (NSString * aString = [[NSString alloc] initWithCharacters:&c2 length:1]) { if (NSAttributedString * attributedString = [[NSAttributedString alloc] initWithString:aString attributes:m_fontProps]) { [s_fontMetricsTextStorage setAttributedString:attributedString]; NSRange glyphRange = [s_fontMetricsLayoutManager glyphRangeForTextContainer:s_fontMetricsTextContainer]; if (glyphRange.length) { NSRect rect = [s_fontMetricsLayoutManager boundingRectForGlyphRange:glyphRange inTextContainer:s_fontMetricsTextContainer]; if (rect.size.width < LAYOUT_CONTAINER_WIDTH) { charWidth = static_cast<UT_uint32>(rect.size.width); } } [attributedString release]; } [aString release]; } return charWidth; } typedef struct { UT_UCS4Char unicode; const char * description; } UT_SpecialCharacter; static UT_SpecialCharacter s_unicode_extra[] = { { 0x00A0, "Non-breaking space" }, { 0x2206, "Increment (Delta)" }, { 0x2126, "Ohm sign (Omega)" }, { 0x00B5, "Micro sign (mu)" }, { 0x2044, "Fraction slash" } }; /* Unicode mapping for Symbol (provided by Adobe, available from http://www.unicode.org/) */ static UT_UCS4Char s_unicode_4_Symbol_lo[95] = { 0x0020, /*0x00A0,*/0x0021, 0x2200, 0x0023, 0x2203, 0x0025, 0x0026, 0x220B, 0x0028, 0x0029, 0x2217, 0x002B, 0x002C, 0x2212, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x2245, 0x0391, 0x0392, 0x03A7, 0x0394, /*0x2206,*/0x0395, 0x03A6, 0x0393, 0x0397, 0x0399, 0x03D1, 0x039A, 0x039B, 0x039C, 0x039D, 0x039F, 0x03A0, 0x0398, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03C2, 0x03A9, /*0x2126,*/0x039E, 0x03A8, 0x0396, 0x005B, 0x2234, 0x005D, 0x22A5, 0x005F, 0xF8E5, 0x03B1, 0x03B2, 0x03C7, 0x03B4, 0x03B5, 0x03C6, 0x03B3, 0x03B7, 0x03B9, 0x03D5, 0x03BA, 0x03BB,/*0x00B5,*/ 0x03BC, 0x03BD, 0x03BF, 0x03C0, 0x03B8, 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03D6, 0x03C9, 0x03BE, 0x03C8, 0x03B6, 0x007B, 0x007C, 0x007D, 0x223C }; static UT_UCS4Char s_unicode_4_Symbol_hi[95] = { 0x20AC, 0x03D2, 0x2032, 0x2264,/*0x2044,*/ 0x2215, 0x221E, 0x0192, 0x2663, 0x2666, 0x2665, 0x2660, 0x2194, 0x2190, 0x2191, 0x2192, 0x2193, 0x00B0, 0x00B1, 0x2033, 0x2265, 0x00D7, 0x221D, 0x2202, 0x2022, 0x00F7, 0x2260, 0x2261, 0x2248, 0x2026, 0xF8E6, 0xF8E7, 0x21B5, 0x2135, 0x2111, 0x211C, 0x2118, 0x2297, 0x2295, 0x2205, 0x2229, 0x222A, 0x2283, 0x2287, 0x2284, 0x2282, 0x2286, 0x2208, 0x2209, 0x2220, 0x2207, 0xF6DA, 0xF6D9, 0xF6DB, 0x220F, 0x221A, 0x22C5, 0x00AC, 0x2227, 0x2228, 0x21D4, 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x25CA, 0x2329, 0xF8E8, 0xF8E9, 0xF8EA, 0x2211, 0xF8EB, 0xF8EC, 0xF8ED, 0xF8EE, 0xF8EF, 0xF8F0, 0xF8F1, 0xF8F2, 0xF8F3, 0xF8F4, 0x00f0, 0x232A, 0x222B, 0x2320, 0xF8F5, 0x2321, 0xF8F6, 0xF8F7, 0xF8F8, 0xF8F9, 0xF8FA, 0xF8FB, 0xF8FC, 0xF8FD, 0xF8FE }; /* Unicode mapping for Wingdings font, from http://www.alanwood.net/ with a couple of changes (not sure about 0xAA which I've changed * from a four-point star to a heart; is this a Dingbats vs Wingdings difference?) */ static UT_UCS4Char s_unicode_4_Wingdings[224] = { 0x0020, 0x270F, 0x2702, 0x2701, 0x0024, 0x0025, 0x0026, 0x0027, 0x260E, 0x2706, 0x2709, 0x261E, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x2713, 0x0034, 0x0035, 0x231B, 0x2328, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x2707, 0x270D, 0x0040, 0x270C, 0x0042, 0x0043, 0x0044, 0x261C, 0x261E, 0x261D, 0x261F, 0x0049, 0x263A, 0x004B, 0x2639, 0x004D, 0x2620, 0x2690, 0x0050, 0x2708, 0x263C, 0x2605, 0x2744, 0x0055, 0x271E, 0x0057, 0x2720, 0x2721, 0x262A, 0x262F, 0x0950, 0x2638, 0x2648, 0x2649, 0x264A, 0x264B, 0x254C, 0x264D, 0x264E, 0x264F, 0x2650, 0x2651, 0x2652, 0x2653, 0x0026, 0x0026, 0x25CF, 0x274D, 0x25A0, 0x25A1, 0x0070, 0x2751, 0x2752, 0x27A4, 0x2666, 0x25C6, 0x2756, 0x0077, 0x2327, 0x2353, 0x2318, 0x2740, 0x273F, 0x275D, 0x275E, 0x25AF, 0x24EA, 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x24FF, 0x2776, 0x2777, 0x2778, 0x2779, 0x277A, 0x277B, 0x277C, 0x277D, 0x277E, 0x277F, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x00B7, 0x2022, 0x25AA, 0x25CB, 0x00A2, 0x00A3, 0x25C9, 0x25CE, 0x00A6, 0x25AA, 0x25FB, 0x2666, 0x2665, 0x2605, 0x2736, 0x2734, 0x2739, 0x2735, 0x00B0, 0x2316, 0x2727, 0x2311, 0x00B4, 0x272A, 0x2730, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x232B, 0x2326, 0x00D7, 0x27A2, 0x00D9, 0x00DA, 0x00DB, 0x27B2, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x2794, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x21E6, 0x21E8, 0x21E7, 0x21E9, 0x2B04, 0x21F3, 0x2B00, 0x2B01, 0x2B03, 0x2B02, 0x25AD, 0x25AB, 0x2717, 0x2713, 0x2612, 0x2611, 0x00FF }; XAP_CocoaFont::RemapFont XAP_CocoaFont::remapFont(NSFont * font) { RemapFont rf = rf_None; if (font) { if ([[font familyName] isEqualToString:@"Symbol"]) { rf = rf_Symbols; } else if ([[font familyName] isEqualToString:@"Wingdings" ] || [[font familyName] isEqualToString:@"Dingbats" ] || [[font familyName] isEqualToString:@"Webdings" ] || [[font familyName] isEqualToString:@"Zapf Dingbats"]) { rf = rf_Dings; } } return rf; } UT_UCS4Char XAP_CocoaFont::remapChar(UT_UCS4Char charCode, RemapFont rf) { UT_UCS4Char remappedCharCode = charCode; switch (rf) { case rf_Symbols: if ((charCode & 0xff) == charCode) { if (charCode & 0x80) { charCode &= 0x7f; if ((charCode & 0x60) && (charCode != 0x7f)) remappedCharCode = s_unicode_4_Symbol_hi[charCode - 0x20]; } else { if ((charCode & 0x60) && (charCode != 0x7f)) remappedCharCode = s_unicode_4_Symbol_lo[charCode - 0x20]; } } break; case rf_Dings: if ((charCode & 0xff) == charCode) { if (charCode & 0xe0) remappedCharCode = s_unicode_4_Wingdings[charCode - 0x20]; } break; default: break; } return remappedCharCode; }