1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2/* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 21#include <osx/salinst.h> 22#include <quartz/utils.h> 23#include <quartz/salgdi.h> 24 25#include "a11ytextattributeswrapper.h" 26 27#include <com/sun/star/accessibility/AccessibleTextType.hpp> 28#include <com/sun/star/awt/FontUnderline.hpp> 29#include <com/sun/star/awt/FontWeight.hpp> 30#include <com/sun/star/awt/FontStrikeout.hpp> 31#include <com/sun/star/lang/IllegalArgumentException.hpp> 32#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> 33#include <com/sun/star/text/TextMarkupType.hpp> 34#include <com/sun/star/style/ParagraphAdjust.hpp> 35 36namespace css_awt = ::com::sun::star::awt; 37using namespace ::com::sun::star::accessibility; 38using namespace ::com::sun::star::beans; 39using namespace ::com::sun::star::lang; 40using namespace ::com::sun::star::uno; 41 42// cannot use NSFontDescriptor as it has no notion of explicit NSUn{bold,italic}FontMask 43@interface AquaA11yFontDescriptor : NSObject 44{ 45 NSString *_name; 46 NSFontTraitMask _traits; 47 CGFloat _size; 48} 49-(void)setName:(NSString*)name; 50-(void)setBold:(NSFontTraitMask)bold; 51-(void)setItalic:(NSFontTraitMask)italic; 52-(void)setSize:(CGFloat)size; 53-(NSFont*)font; 54@end 55 56@implementation AquaA11yFontDescriptor 57- (id)init 58{ 59 if((self = [super init])) 60 { 61 _name = nil; 62 _traits = 0; 63 _size = 0.0; 64 } 65 return self; 66} 67 68- (id)initWithDescriptor:(AquaA11yFontDescriptor*)descriptor { 69 if((self = [super init])) 70 { 71 _name = [descriptor->_name retain]; 72 _traits = descriptor->_traits; 73 _size = descriptor->_size; 74 } 75 return self; 76} 77 78- (void)dealloc { 79 [_name release]; 80 [super dealloc]; 81} 82 83-(void)setName:(NSString*)name { 84 if (_name != name) { 85 [name retain]; 86 [_name release]; 87 _name = name; 88 } 89} 90 91-(void)setBold:(NSFontTraitMask)bold { 92 _traits &= ~(NSBoldFontMask | NSUnboldFontMask); 93 _traits |= bold & (NSBoldFontMask | NSUnboldFontMask); 94}; 95 96-(void)setItalic:(NSFontTraitMask)italic { 97 _traits &= ~(NSItalicFontMask | NSUnitalicFontMask); 98 _traits |= italic & (NSItalicFontMask | NSUnitalicFontMask); 99}; 100 101-(void)setSize:(CGFloat)size { _size = size; } 102 103-(NSFont*)font { 104 return [[NSFontManager sharedFontManager] fontWithFamily:_name traits:_traits weight:0 size:_size]; 105} 106@end 107 108@implementation AquaA11yTextAttributesWrapper : NSObject 109 110+(int)convertUnderlineStyle:(PropertyValue)property { 111 int underlineStyle = NSUnderlineStyleNone; 112 sal_Int16 value = 0; 113 property.Value >>= value; 114 if ( value != ::css_awt::FontUnderline::NONE 115 && value != ::css_awt::FontUnderline::DONTKNOW) { 116 underlineStyle = NSUnderlineStyleSingle; 117 } 118 return underlineStyle; 119} 120 121+(int)convertBoldStyle:(PropertyValue)property { 122 int boldStyle = NSUnboldFontMask; 123 float value = 0; 124 property.Value >>= value; 125 if ( value == ::css_awt::FontWeight::SEMIBOLD 126 || value == ::css_awt::FontWeight::BOLD 127 || value == ::css_awt::FontWeight::ULTRABOLD 128 || value == ::css_awt::FontWeight::BLACK ) { 129 boldStyle = NSBoldFontMask; 130 } 131 return boldStyle; 132} 133 134+(int)convertItalicStyle:(PropertyValue)property { 135 int italicStyle = NSUnitalicFontMask; 136 ::css_awt::FontSlant value = property.Value.get< ::css_awt::FontSlant>(); 137 if ( value == ::css_awt::FontSlant_ITALIC ) { 138 italicStyle = NSItalicFontMask; 139 } 140 return italicStyle; 141} 142 143+(BOOL)isStrikethrough:(PropertyValue)property { 144 BOOL strikethrough = NO; 145 sal_Int16 value = 0; 146 property.Value >>= value; 147 if ( value != ::css_awt::FontStrikeout::NONE 148 && value != ::css_awt::FontStrikeout::DONTKNOW ) { 149 strikethrough = YES; 150 } 151 return strikethrough; 152} 153 154+(BOOL)convertBoolean:(PropertyValue)property { 155 BOOL myBoolean = NO; 156 bool value = false; 157 property.Value >>= value; 158 if ( value ) { 159 myBoolean = YES; 160 } 161 return myBoolean; 162} 163 164+(NSNumber *)convertShort:(PropertyValue)property { 165 sal_Int16 value = 0; 166 property.Value >>= value; 167 return [ NSNumber numberWithShort: value ]; 168} 169 170+(void)addColor:(Color)nColor forAttribute:(NSString *)attribute andRange:(NSRange)range toString:(NSMutableAttributedString *)string { 171 if( nColor == COL_TRANSPARENT ) 172 return; 173 const RGBAColor aRGBAColor( nColor); 174 CGColorRef aColorRef = CGColorCreate ( CGColorSpaceCreateWithName ( kCGColorSpaceGenericRGB ), aRGBAColor.AsArray() ); 175 [ string addAttribute: attribute value: reinterpret_cast<id>(aColorRef) range: range ]; 176 CGColorRelease( aColorRef ); 177} 178 179+(void)addFont:(NSFont *)font toString:(NSMutableAttributedString *)string forRange:(NSRange)range { 180 if ( font != nil ) { 181 NSDictionary * fontDictionary = [ NSDictionary dictionaryWithObjectsAndKeys: 182 [ font fontName ], NSAccessibilityFontNameKey, 183 [ font familyName ], NSAccessibilityFontFamilyKey, 184 [ font displayName ], NSAccessibilityVisibleNameKey, 185 [ NSNumber numberWithFloat: [ font pointSize ] ], NSAccessibilityFontSizeKey, 186 nil 187 ]; 188 [ string addAttribute: NSAccessibilityFontTextAttribute 189 value: fontDictionary 190 range: range 191 ]; 192 } 193} 194 195+(void)applyAttributesFrom:(Sequence < PropertyValue >)attributes toString:(NSMutableAttributedString *)string forRange:(NSRange)range fontDescriptor:(AquaA11yFontDescriptor*)fontDescriptor { 196 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ]; 197 // constants 198 static const OUString attrUnderline("CharUnderline"); 199 static const OUString attrBold("CharWeight"); 200 static const OUString attrFontname("CharFontName"); 201 static const OUString attrItalic("CharPosture"); 202 static const OUString attrHeight("CharHeight"); 203 static const OUString attrStrikethrough("CharStrikeout"); 204 static const OUString attrShadow("CharShadowed"); 205 static const OUString attrUnderlineColor("CharUnderlineColor"); 206 static const OUString attrUnderlineHasColor("CharUnderlineHasColor"); 207 static const OUString attrForegroundColor("CharColor"); 208 static const OUString attrBackgroundColor("CharBackColor"); 209 static const OUString attrSuperscript("CharEscapement"); 210 static const OUString attrTextAlignment("ParaAdjust"); 211 // vars 212 sal_Int32 underlineColor = 0; 213 BOOL underlineHasColor = NO; 214 // add attributes to string 215 for ( int attrIndex = 0; attrIndex < attributes.getLength(); attrIndex++ ) { 216 PropertyValue property = attributes [ attrIndex ]; 217 // TODO: NSAccessibilityMisspelledTextAttribute, NSAccessibilityAttachmentTextAttribute, NSAccessibilityLinkTextAttribute 218 // NSAccessibilityStrikethroughColorTextAttribute is unsupported by UNP-API 219 if ( property.Value.hasValue() ) { 220 if ( property.Name.equals ( attrUnderline ) ) { 221 int style = [ AquaA11yTextAttributesWrapper convertUnderlineStyle: property ]; 222 if ( style != NSUnderlineStyleNone ) { 223 [ string addAttribute: NSAccessibilityUnderlineTextAttribute value: [ NSNumber numberWithInt: style ] range: range ]; 224 } 225 } else if ( property.Name.equals ( attrFontname ) ) { 226 OUString fontname; 227 property.Value >>= fontname; 228 [fontDescriptor setName:CreateNSString(fontname)]; 229 } else if ( property.Name.equals ( attrBold ) ) { 230 [fontDescriptor setBold:[AquaA11yTextAttributesWrapper convertBoldStyle:property]]; 231 } else if ( property.Name.equals ( attrItalic ) ) { 232 [fontDescriptor setItalic:[AquaA11yTextAttributesWrapper convertItalicStyle:property]]; 233 } else if ( property.Name.equals ( attrHeight ) ) { 234 float size; 235 property.Value >>= size; 236 [fontDescriptor setSize:size]; 237 } else if ( property.Name.equals ( attrStrikethrough ) ) { 238 if ( [ AquaA11yTextAttributesWrapper isStrikethrough: property ] ) { 239 [ string addAttribute: NSAccessibilityStrikethroughTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ]; 240 } 241 } else if ( property.Name.equals ( attrShadow ) ) { 242 if ( [ AquaA11yTextAttributesWrapper convertBoolean: property ] ) { 243 [ string addAttribute: NSAccessibilityShadowTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ]; 244 } 245 } else if ( property.Name.equals ( attrUnderlineColor ) ) { 246 property.Value >>= underlineColor; 247 } else if ( property.Name.equals ( attrUnderlineHasColor ) ) { 248 underlineHasColor = [ AquaA11yTextAttributesWrapper convertBoolean: property ]; 249 } else if ( property.Name.equals ( attrForegroundColor ) ) { 250 [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityForegroundColorTextAttribute andRange: range toString: string ]; 251 } else if ( property.Name.equals ( attrBackgroundColor ) ) { 252 [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityBackgroundColorTextAttribute andRange: range toString: string ]; 253 } else if ( property.Name.equals ( attrSuperscript ) ) { 254 // values < zero mean subscript 255 // values > zero mean superscript 256 // this is true for both NSAccessibility-API and UNO-API 257 NSNumber * number = [ AquaA11yTextAttributesWrapper convertShort: property ]; 258 if ( [ number shortValue ] != 0 ) { 259 [ string addAttribute: NSAccessibilitySuperscriptTextAttribute value: number range: range ]; 260 } 261 } else if ( property.Name.equals ( attrTextAlignment ) ) { 262 sal_Int32 alignment; 263 property.Value >>= alignment; 264 NSNumber *textAlignment = nil; 265SAL_WNODEPRECATED_DECLARATIONS_PUSH 266 // 'NSCenterTextAlignment' is deprecated: first deprecated in macOS 10.12 267 // 'NSJustifiedTextAlignment' is deprecated: first deprecated in macOS 10.12 268 // 'NSLeftTextAlignment' is deprecated: first deprecated in macOS 10.12 269 // 'NSRightTextAlignment' is deprecated: first deprecated in macOS 10.12 270 switch(static_cast<css::style::ParagraphAdjust>(alignment)) { 271 case css::style::ParagraphAdjust_RIGHT : textAlignment = [NSNumber numberWithInteger:NSRightTextAlignment] ; break; 272 case css::style::ParagraphAdjust_CENTER: textAlignment = [NSNumber numberWithInteger:NSCenterTextAlignment] ; break; 273 case css::style::ParagraphAdjust_BLOCK : textAlignment = [NSNumber numberWithInteger:NSJustifiedTextAlignment]; break; 274 case css::style::ParagraphAdjust_LEFT : 275 default : textAlignment = [NSNumber numberWithInteger:NSLeftTextAlignment] ; break; 276 } 277SAL_WNODEPRECATED_DECLARATIONS_POP 278 NSDictionary *paragraphStyle = [NSDictionary dictionaryWithObjectsAndKeys:textAlignment, @"AXTextAlignment", textAlignment, @"AXVisualTextAlignment", nil]; 279 [string addAttribute:@"AXParagraphStyle" value:paragraphStyle range:range]; 280 } 281 } 282 } 283 // add underline information 284 if ( underlineHasColor ) { 285 [ AquaA11yTextAttributesWrapper addColor: underlineColor forAttribute: NSAccessibilityUnderlineColorTextAttribute andRange: range toString: string ]; 286 } 287 // add font information 288 NSFont * font = [fontDescriptor font]; 289 [AquaA11yTextAttributesWrapper addFont:font toString:string forRange:range]; 290 [ pool release ]; 291} 292 293+(void)addMarkup:(XAccessibleTextMarkup*)markup withType:(long)type toString:(NSMutableAttributedString*)string inRange:(NSRange)range { 294 const long markupCount = markup->getTextMarkupCount(type); 295 for (long markupIndex = 0; markupIndex < markupCount; ++markupIndex) { 296 TextSegment markupSegment = markup->getTextMarkup(markupIndex, type); 297 NSRange markupRange = NSMakeRange(markupSegment.SegmentStart, markupSegment.SegmentEnd - markupSegment.SegmentStart); 298 markupRange = NSIntersectionRange(range, markupRange); 299 if (markupRange.length > 0) { 300 markupRange.location -= range.location; 301 switch(type) { 302 case css::text::TextMarkupType::SPELLCHECK: { 303 [string addAttribute:NSAccessibilityMisspelledTextAttribute value:[NSNumber numberWithBool:YES] range:markupRange]; 304 [string addAttribute:@"AXMarkedMisspelled" value:[NSNumber numberWithBool:YES] range:markupRange]; 305 break; 306 } 307 } 308 } 309 } 310} 311 312+(void)addMarkup:(XAccessibleTextMarkup*)markup toString:(NSMutableAttributedString*)string inRange:(NSRange)range { 313 [AquaA11yTextAttributesWrapper addMarkup:markup withType:css::text::TextMarkupType::SPELLCHECK toString:string inRange:range]; 314} 315 316+(NSMutableAttributedString *)createAttributedStringForElement:(AquaA11yWrapper *)wrapper inOrigRange:(id)origRange { 317 static const Sequence < OUString > emptySequence; 318 // vars 319 NSMutableAttributedString * string = nil; 320 int loc = [ origRange rangeValue ].location; 321 int len = [ origRange rangeValue ].length; 322 int endIndex = loc + len; 323 int currentIndex = loc; 324 try { 325 NSString * myString = CreateNSString ( [ wrapper accessibleText ] -> getText() ); // TODO: dirty fix for i87817 326 string = [ [ NSMutableAttributedString alloc ] initWithString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ]; 327 if ( [ wrapper accessibleTextAttributes ] && [myString characterAtIndex:0] != 57361) { // TODO: dirty fix for i87817 328 [ string beginEditing ]; 329 // add default attributes for whole string 330 Sequence < PropertyValue > defaultAttributes = [ wrapper accessibleTextAttributes ] -> getDefaultAttributes ( emptySequence ); 331 AquaA11yFontDescriptor *defaultFontDescriptor = [[AquaA11yFontDescriptor alloc] init]; 332 [ AquaA11yTextAttributesWrapper applyAttributesFrom: defaultAttributes toString: string forRange: NSMakeRange ( 0, len ) fontDescriptor: defaultFontDescriptor ]; 333 // add attributes for attribute run(s) 334 while ( currentIndex < endIndex ) { 335 TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( currentIndex, AccessibleTextType::ATTRIBUTE_RUN ); 336 int endOfRange = endIndex > textSegment.SegmentEnd ? textSegment.SegmentEnd : endIndex; 337 NSRange rangeForAttributeRun = NSMakeRange ( currentIndex - loc , endOfRange - currentIndex ); 338 // add run attributes 339 Sequence < PropertyValue > attributes = [ wrapper accessibleTextAttributes ] -> getRunAttributes ( currentIndex, emptySequence ); 340 AquaA11yFontDescriptor *fontDescriptor = [[AquaA11yFontDescriptor alloc] initWithDescriptor:defaultFontDescriptor]; 341 [ AquaA11yTextAttributesWrapper applyAttributesFrom: attributes toString: string forRange: rangeForAttributeRun fontDescriptor: fontDescriptor ]; 342 [fontDescriptor release]; 343 currentIndex = textSegment.SegmentEnd; 344 } 345 [defaultFontDescriptor release]; 346 if ([wrapper accessibleTextMarkup]) 347 [AquaA11yTextAttributesWrapper addMarkup:[wrapper accessibleTextMarkup] toString:string inRange:[origRange rangeValue]]; 348 [ string endEditing ]; 349 } 350 } catch ( IllegalArgumentException & ) { 351 // empty 352 } catch ( IndexOutOfBoundsException & ) { 353 // empty 354 } catch ( RuntimeException& ) { 355 // at least don't crash 356 } 357 return string; 358} 359 360@end 361 362/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 363