1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include "osx/salinst.h"
22 #include "quartz/utils.h"
23 #include "quartz/salgdi.h"
25 #include "a11ytextattributeswrapper.h"
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>
36 namespace css_awt = ::com::sun::star::awt;
37 using namespace ::com::sun::star::accessibility;
38 using namespace ::com::sun::star::beans;
39 using namespace ::com::sun::star::lang;
40 using namespace ::com::sun::star::uno;
42 // cannot use NSFontDescriptor as it has no notion of explicit NSUn{bold,italic}FontMask
43 @interface AquaA11yFontDescriptor : NSObject
46 NSFontTraitMask _traits;
49 -(void)setName:(NSString*)name;
50 -(void)setBold:(NSFontTraitMask)bold;
51 -(void)setItalic:(NSFontTraitMask)italic;
52 -(void)setSize:(CGFloat)size;
56 @implementation AquaA11yFontDescriptor
59 if((self = [super init]))
68 - (id)initWithDescriptor:(AquaA11yFontDescriptor*)descriptor {
69 if((self = [super init]))
71 _name = [descriptor->_name retain];
72 _traits = descriptor->_traits;
73 _size = descriptor->_size;
83 -(void)setName:(NSString*)name {
91 -(void)setBold:(NSFontTraitMask)bold {
92 _traits &= ~(NSBoldFontMask | NSUnboldFontMask);
93 _traits |= bold & (NSBoldFontMask | NSUnboldFontMask);
96 -(void)setItalic:(NSFontTraitMask)italic {
97 _traits &= ~(NSItalicFontMask | NSUnitalicFontMask);
98 _traits |= italic & (NSItalicFontMask | NSUnitalicFontMask);
101 -(void)setSize:(CGFloat)size { _size = size; }
104 return [[NSFontManager sharedFontManager] fontWithFamily:_name traits:_traits weight:0 size:_size];
108 @implementation AquaA11yTextAttributesWrapper : NSObject
110 +(int)convertUnderlineStyle:(PropertyValue)property {
111 #if MACOSX_SDK_VERSION >= 1090
112 int underlineStyle = NSUnderlineStyleNone;
114 int underlineStyle = NSNoUnderlineStyle;
117 property.Value >>= value;
118 if ( value != ::css_awt::FontUnderline::NONE
119 && value != ::css_awt::FontUnderline::DONTKNOW) {
120 #if MACOSX_SDK_VERSION >= 1090
121 underlineStyle = NSUnderlineStyleSingle;
123 underlineStyle = NSSingleUnderlineStyle;
126 return underlineStyle;
129 +(int)convertBoldStyle:(PropertyValue)property {
130 int boldStyle = NSUnboldFontMask;
132 property.Value >>= value;
133 if ( value == ::css_awt::FontWeight::SEMIBOLD
134 || value == ::css_awt::FontWeight::BOLD
135 || value == ::css_awt::FontWeight::ULTRABOLD
136 || value == ::css_awt::FontWeight::BLACK ) {
137 boldStyle = NSBoldFontMask;
142 +(int)convertItalicStyle:(PropertyValue)property {
143 int italicStyle = NSUnitalicFontMask;
144 ::css_awt::FontSlant value = property.Value.get< ::css_awt::FontSlant>();
145 if ( value == ::css_awt::FontSlant_ITALIC ) {
146 italicStyle = NSItalicFontMask;
151 +(BOOL)isStrikethrough:(PropertyValue)property {
152 BOOL strikethrough = NO;
154 property.Value >>= value;
155 if ( value != ::css_awt::FontStrikeout::NONE
156 && value != ::css_awt::FontStrikeout::DONTKNOW ) {
159 return strikethrough;
162 +(BOOL)convertBoolean:(PropertyValue)property {
165 property.Value >>= value;
172 +(NSNumber *)convertShort:(PropertyValue)property {
174 property.Value >>= value;
175 return [ NSNumber numberWithShort: value ];
178 +(void)addColor:(SalColor)nSalColor forAttribute:(NSString *)attribute andRange:(NSRange)range toString:(NSMutableAttributedString *)string {
179 if( nSalColor == COL_TRANSPARENT )
181 const RGBAColor aRGBAColor( nSalColor);
182 CGColorRef aColorRef = CGColorCreate ( CGColorSpaceCreateWithName ( kCGColorSpaceGenericRGB ), aRGBAColor.AsArray() );
183 [ string addAttribute: attribute value: reinterpret_cast<id>(aColorRef) range: range ];
184 CGColorRelease( aColorRef );
187 +(void)addFont:(NSFont *)font toString:(NSMutableAttributedString *)string forRange:(NSRange)range {
189 NSDictionary * fontDictionary = [ NSDictionary dictionaryWithObjectsAndKeys:
190 [ font fontName ], NSAccessibilityFontNameKey,
191 [ font familyName ], NSAccessibilityFontFamilyKey,
192 [ font displayName ], NSAccessibilityVisibleNameKey,
193 [ NSNumber numberWithFloat: [ font pointSize ] ], NSAccessibilityFontSizeKey,
196 [ string addAttribute: NSAccessibilityFontTextAttribute
197 value: fontDictionary
203 +(void)applyAttributesFrom:(Sequence < PropertyValue >)attributes toString:(NSMutableAttributedString *)string forRange:(NSRange)range fontDescriptor:(AquaA11yFontDescriptor*)fontDescriptor {
204 NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
206 static const OUString attrUnderline("CharUnderline");
207 static const OUString attrBold("CharWeight");
208 static const OUString attrFontname("CharFontName");
209 static const OUString attrItalic("CharPosture");
210 static const OUString attrHeight("CharHeight");
211 static const OUString attrStrikethrough("CharStrikeout");
212 static const OUString attrShadow("CharShadowed");
213 static const OUString attrUnderlineColor("CharUnderlineColor");
214 static const OUString attrUnderlineHasColor("CharUnderlineHasColor");
215 static const OUString attrForegroundColor("CharColor");
216 static const OUString attrBackgroundColor("CharBackColor");
217 static const OUString attrSuperscript("CharEscapement");
218 static const OUString attrTextAlignment("ParaAdjust");
220 sal_Int32 underlineColor = 0;
221 BOOL underlineHasColor = NO;
222 // add attributes to string
223 for ( int attrIndex = 0; attrIndex < attributes.getLength(); attrIndex++ ) {
224 PropertyValue property = attributes [ attrIndex ];
225 // TODO: NSAccessibilityMisspelledTextAttribute, NSAccessibilityAttachmentTextAttribute, NSAccessibilityLinkTextAttribute
226 // NSAccessibilityStrikethroughColorTextAttribute is unsupported by UNP-API
227 if ( property.Value.hasValue() ) {
228 if ( property.Name.equals ( attrUnderline ) ) {
229 int style = [ AquaA11yTextAttributesWrapper convertUnderlineStyle: property ];
230 #if MACOSX_SDK_VERSION >= 1090
231 if ( style != NSUnderlineStyleNone ) {
233 if ( style != NSNoUnderlineStyle ) {
235 [ string addAttribute: NSAccessibilityUnderlineTextAttribute value: [ NSNumber numberWithInt: style ] range: range ];
237 } else if ( property.Name.equals ( attrFontname ) ) {
239 property.Value >>= fontname;
240 [fontDescriptor setName:CreateNSString(fontname)];
241 } else if ( property.Name.equals ( attrBold ) ) {
242 [fontDescriptor setBold:[AquaA11yTextAttributesWrapper convertBoldStyle:property]];
243 } else if ( property.Name.equals ( attrItalic ) ) {
244 [fontDescriptor setItalic:[AquaA11yTextAttributesWrapper convertItalicStyle:property]];
245 } else if ( property.Name.equals ( attrHeight ) ) {
247 property.Value >>= size;
248 [fontDescriptor setSize:size];
249 } else if ( property.Name.equals ( attrStrikethrough ) ) {
250 if ( [ AquaA11yTextAttributesWrapper isStrikethrough: property ] ) {
251 [ string addAttribute: NSAccessibilityStrikethroughTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ];
253 } else if ( property.Name.equals ( attrShadow ) ) {
254 if ( [ AquaA11yTextAttributesWrapper convertBoolean: property ] ) {
255 [ string addAttribute: NSAccessibilityShadowTextAttribute value: [ NSNumber numberWithBool: YES ] range: range ];
257 } else if ( property.Name.equals ( attrUnderlineColor ) ) {
258 property.Value >>= underlineColor;
259 } else if ( property.Name.equals ( attrUnderlineHasColor ) ) {
260 underlineHasColor = [ AquaA11yTextAttributesWrapper convertBoolean: property ];
261 } else if ( property.Name.equals ( attrForegroundColor ) ) {
262 [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityForegroundColorTextAttribute andRange: range toString: string ];
263 } else if ( property.Name.equals ( attrBackgroundColor ) ) {
264 [ AquaA11yTextAttributesWrapper addColor: property.Value.get<sal_Int32>() forAttribute: NSAccessibilityBackgroundColorTextAttribute andRange: range toString: string ];
265 } else if ( property.Name.equals ( attrSuperscript ) ) {
266 // values < zero mean subscript
267 // values > zero mean superscript
268 // this is true for both NSAccessibility-API and UNO-API
269 NSNumber * number = [ AquaA11yTextAttributesWrapper convertShort: property ];
270 if ( [ number shortValue ] != 0 ) {
271 [ string addAttribute: NSAccessibilitySuperscriptTextAttribute value: number range: range ];
273 } else if ( property.Name.equals ( attrTextAlignment ) ) {
275 property.Value >>= alignment;
276 NSNumber *textAlignment = nil;
277 SAL_WNODEPRECATED_DECLARATIONS_PUSH
278 // 'NSCenterTextAlignment' is deprecated: first deprecated in macOS 10.12
279 // 'NSJustifiedTextAlignment' is deprecated: first deprecated in macOS 10.12
280 // 'NSLeftTextAlignment' is deprecated: first deprecated in macOS 10.12
281 // 'NSRightTextAlignment' is deprecated: first deprecated in macOS 10.12
282 switch((css::style::ParagraphAdjust)alignment) {
283 case css::style::ParagraphAdjust_RIGHT : textAlignment = [NSNumber numberWithInteger:NSRightTextAlignment] ; break;
284 case css::style::ParagraphAdjust_CENTER: textAlignment = [NSNumber numberWithInteger:NSCenterTextAlignment] ; break;
285 case css::style::ParagraphAdjust_BLOCK : textAlignment = [NSNumber numberWithInteger:NSJustifiedTextAlignment]; break;
286 case css::style::ParagraphAdjust_LEFT :
287 default : textAlignment = [NSNumber numberWithInteger:NSLeftTextAlignment] ; break;
289 SAL_WNODEPRECATED_DECLARATIONS_POP
290 NSDictionary *paragraphStyle = [NSDictionary dictionaryWithObjectsAndKeys:textAlignment, @"AXTextAlignment", textAlignment, @"AXVisualTextAlignment", nil];
291 [string addAttribute:@"AXParagraphStyle" value:paragraphStyle range:range];
295 // add underline information
296 if ( underlineHasColor ) {
297 [ AquaA11yTextAttributesWrapper addColor: underlineColor forAttribute: NSAccessibilityUnderlineColorTextAttribute andRange: range toString: string ];
299 // add font information
300 NSFont * font = [fontDescriptor font];
301 [AquaA11yTextAttributesWrapper addFont:font toString:string forRange:range];
305 +(void)addMarkup:(XAccessibleTextMarkup*)markup withType:(long)type toString:(NSMutableAttributedString*)string inRange:(NSRange)range {
306 const long markupCount = markup->getTextMarkupCount(type);
307 for (long markupIndex = 0; markupIndex < markupCount; ++markupIndex) {
308 TextSegment markupSegment = markup->getTextMarkup(markupIndex, type);
309 NSRange markupRange = NSMakeRange(markupSegment.SegmentStart, markupSegment.SegmentEnd - markupSegment.SegmentStart);
310 markupRange = NSIntersectionRange(range, markupRange);
311 if (markupRange.length > 0) {
312 markupRange.location -= range.location;
314 case css::text::TextMarkupType::SPELLCHECK: {
315 [string addAttribute:NSAccessibilityMisspelledTextAttribute value:[NSNumber numberWithBool:YES] range:markupRange];
316 [string addAttribute:@"AXMarkedMisspelled" value:[NSNumber numberWithBool:YES] range:markupRange];
324 +(void)addMarkup:(XAccessibleTextMarkup*)markup toString:(NSMutableAttributedString*)string inRange:(NSRange)range {
325 [AquaA11yTextAttributesWrapper addMarkup:markup withType:(css::text::TextMarkupType::SPELLCHECK) toString:string inRange:range];
328 +(NSMutableAttributedString *)createAttributedStringForElement:(AquaA11yWrapper *)wrapper inOrigRange:(id)origRange {
329 static const Sequence < OUString > emptySequence;
331 NSMutableAttributedString * string = nil;
332 int loc = [ origRange rangeValue ].location;
333 int len = [ origRange rangeValue ].length;
334 int endIndex = loc + len;
335 int currentIndex = loc;
337 NSString * myString = CreateNSString ( [ wrapper accessibleText ] -> getText() ); // TODO: dirty fix for i87817
338 string = [ [ NSMutableAttributedString alloc ] initWithString: CreateNSString ( [ wrapper accessibleText ] -> getTextRange ( loc, loc + len ) ) ];
339 if ( [ wrapper accessibleTextAttributes ] && [myString characterAtIndex:0] != 57361) { // TODO: dirty fix for i87817
340 [ string beginEditing ];
341 // add default attributes for whole string
342 Sequence < PropertyValue > defaultAttributes = [ wrapper accessibleTextAttributes ] -> getDefaultAttributes ( emptySequence );
343 AquaA11yFontDescriptor *defaultFontDescriptor = [[AquaA11yFontDescriptor alloc] init];
344 [ AquaA11yTextAttributesWrapper applyAttributesFrom: defaultAttributes toString: string forRange: NSMakeRange ( 0, len ) fontDescriptor: defaultFontDescriptor ];
345 // add attributes for attribute run(s)
346 while ( currentIndex < endIndex ) {
347 TextSegment textSegment = [ wrapper accessibleText ] -> getTextAtIndex ( currentIndex, AccessibleTextType::ATTRIBUTE_RUN );
348 int endOfRange = endIndex > textSegment.SegmentEnd ? textSegment.SegmentEnd : endIndex;
349 NSRange rangeForAttributeRun = NSMakeRange ( currentIndex - loc , endOfRange - currentIndex );
350 // add run attributes
351 Sequence < PropertyValue > attributes = [ wrapper accessibleTextAttributes ] -> getRunAttributes ( currentIndex, emptySequence );
352 AquaA11yFontDescriptor *fontDescriptor = [[AquaA11yFontDescriptor alloc] initWithDescriptor:defaultFontDescriptor];
353 [ AquaA11yTextAttributesWrapper applyAttributesFrom: attributes toString: string forRange: rangeForAttributeRun fontDescriptor: fontDescriptor ];
354 [fontDescriptor release];
355 currentIndex = textSegment.SegmentEnd;
357 [defaultFontDescriptor release];
358 if ([wrapper accessibleTextMarkup])
359 [AquaA11yTextAttributesWrapper addMarkup:[wrapper accessibleTextMarkup] toString:string inRange:[origRange rangeValue]];
360 [ string endEditing ];
362 } catch ( IllegalArgumentException & e ) {
364 } catch ( IndexOutOfBoundsException & e ) {
366 } catch ( RuntimeException& ) {
367 // at least don't crash
374 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */