From adf1534322f3137ea3379dcb72df96957ba2c41d Mon Sep 17 00:00:00 2001 From: Damian Carrillo Date: Fri, 12 Jul 2013 14:31:02 -0500 Subject: [PATCH] Merging from cwc/master which includes a bit of accessibility support --- .../Bypass/Bypass.xcodeproj/project.pbxproj | 56 +- .../Bypass/Bypass/BPAccessibilityElement.h | 25 + .../Bypass/Bypass/BPAccessibilityElement.m | 30 + .../Bypass/Bypass/BPAccessibilityVisitor.h | 34 ++ .../Bypass/Bypass/BPAccessibilityVisitor.m | 147 +++++ .../Bypass/BPAttributedStringConverter.m | 402 ------------- ...gConverter.h => BPAttributedTextVisitor.h} | 22 +- .../Bypass/Bypass/BPAttributedTextVisitor.m | 562 ++++++++++++++++++ .../ios/Bypass/Bypass/BPDisplaySettings.h | 5 +- .../ios/Bypass/Bypass/BPDisplaySettings.m | 91 ++- platform/ios/Bypass/Bypass/BPElement.mm | 2 +- platform/ios/Bypass/Bypass/BPElementWalker.h | 46 ++ platform/ios/Bypass/Bypass/BPElementWalker.m | 80 +++ .../ios/Bypass/Bypass/BPMarkdownPageView.m | 2 +- platform/ios/Bypass/Bypass/BPMarkdownView.h | 2 +- platform/ios/Bypass/Bypass/BPMarkdownView.m | 113 +++- platform/ios/Bypass/Bypass/BPTextVisitor.h | 28 + platform/ios/Bypass/Bypass/BPTextVisitor.m | 67 +++ platform/ios/Bypass/Bypass/Bypass.h | 2 +- .../xcshareddata/BypassSample.xccheckout | 39 ++ .../BypassSample/BPTextViewController.mm | 9 +- 21 files changed, 1251 insertions(+), 513 deletions(-) create mode 100644 platform/ios/Bypass/Bypass/BPAccessibilityElement.h create mode 100644 platform/ios/Bypass/Bypass/BPAccessibilityElement.m create mode 100644 platform/ios/Bypass/Bypass/BPAccessibilityVisitor.h create mode 100644 platform/ios/Bypass/Bypass/BPAccessibilityVisitor.m delete mode 100644 platform/ios/Bypass/Bypass/BPAttributedStringConverter.m rename platform/ios/Bypass/Bypass/{BPAttributedStringConverter.h => BPAttributedTextVisitor.h} (60%) create mode 100644 platform/ios/Bypass/Bypass/BPAttributedTextVisitor.m create mode 100644 platform/ios/Bypass/Bypass/BPElementWalker.h create mode 100644 platform/ios/Bypass/Bypass/BPElementWalker.m create mode 100644 platform/ios/Bypass/Bypass/BPTextVisitor.h create mode 100644 platform/ios/Bypass/Bypass/BPTextVisitor.m create mode 100644 platform/ios/BypassSample/BypassSample.xcodeproj/project.xcworkspace/xcshareddata/BypassSample.xccheckout diff --git a/platform/ios/Bypass/Bypass.xcodeproj/project.pbxproj b/platform/ios/Bypass/Bypass.xcodeproj/project.pbxproj index a7820139..2d1ea1dd 100644 --- a/platform/ios/Bypass/Bypass.xcodeproj/project.pbxproj +++ b/platform/ios/Bypass/Bypass.xcodeproj/project.pbxproj @@ -7,8 +7,13 @@ objects = { /* Begin PBXBuildFile section */ - 58D9DFA414A385A9337D153E /* BPDisplaySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 58D9DE2F0735C1CEB3500EFF /* BPDisplaySettings.m */; }; 5E007AEB16FA17D200F8CFFD /* BPMarkdownView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E007AEA16FA17D100F8CFFD /* BPMarkdownView.m */; }; + 5E58C58817908CE800BACAC0 /* BPAccessibilityElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E58C58517908CE800BACAC0 /* BPAccessibilityElement.m */; }; + 5E58C58917908CE800BACAC0 /* BPAccessibilityVisitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E58C58717908CE800BACAC0 /* BPAccessibilityVisitor.m */; }; + 5E58C58C17908CFC00BACAC0 /* BPAttributedTextVisitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E58C58B17908CFC00BACAC0 /* BPAttributedTextVisitor.m */; }; + 5E58C58F17908D2000BACAC0 /* BPElementWalker.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E58C58E17908D2000BACAC0 /* BPElementWalker.m */; }; + 5E58C59217908D3300BACAC0 /* BPTextVisitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E58C59117908D3200BACAC0 /* BPTextVisitor.m */; }; + 5E58C59617908E9900BACAC0 /* BPDisplaySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E58C59517908E9900BACAC0 /* BPDisplaySettings.m */; }; 5E5BC81C16E004FA00165503 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E5BC81B16E004FA00165503 /* Foundation.framework */; }; 5E5BC82116E004FA00165503 /* Bypass.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5E5BC82016E004FA00165503 /* Bypass.h */; }; 5E5BC82B16E004FA00165503 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E5BC82A16E004FA00165503 /* SenTestingKit.framework */; }; @@ -22,7 +27,6 @@ 5E5BC87B16E11F7300165503 /* BPElementTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E5BC87A16E11F7300165503 /* BPElementTests.mm */; }; 5E5BC87F16E134C200165503 /* BPDocumentTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E5BC87E16E134C200165503 /* BPDocumentTests.mm */; }; 5E5BC88216E1448C00165503 /* BPParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E5BC88116E1448C00165503 /* BPParserTests.m */; }; - 5E5BC88716E15C5800165503 /* BPAttributedStringConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E5BC88616E15C5800165503 /* BPAttributedStringConverter.m */; }; 5E5BC89116E79B2E00165503 /* array.c in Sources */ = {isa = PBXBuildFile; fileRef = 5E5BC88B16E79B2E00165503 /* array.c */; }; 5E5BC89216E79B2E00165503 /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 5E5BC88D16E79B2E00165503 /* buffer.c */; }; 5E5BC89316E79B2E00165503 /* markdown.c in Sources */ = {isa = PBXBuildFile; fileRef = 5E5BC88F16E79B2E00165503 /* markdown.c */; }; @@ -57,10 +61,20 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 58D9D454C9F2BD2E7F81B4B8 /* BPDisplaySettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPDisplaySettings.h; sourceTree = ""; }; - 58D9DE2F0735C1CEB3500EFF /* BPDisplaySettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPDisplaySettings.m; sourceTree = ""; }; 5E007AE916FA17D100F8CFFD /* BPMarkdownView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPMarkdownView.h; sourceTree = ""; }; 5E007AEA16FA17D100F8CFFD /* BPMarkdownView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPMarkdownView.m; sourceTree = ""; }; + 5E58C58417908CE800BACAC0 /* BPAccessibilityElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPAccessibilityElement.h; sourceTree = ""; }; + 5E58C58517908CE800BACAC0 /* BPAccessibilityElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPAccessibilityElement.m; sourceTree = ""; }; + 5E58C58617908CE800BACAC0 /* BPAccessibilityVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPAccessibilityVisitor.h; sourceTree = ""; }; + 5E58C58717908CE800BACAC0 /* BPAccessibilityVisitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPAccessibilityVisitor.m; sourceTree = ""; }; + 5E58C58A17908CFC00BACAC0 /* BPAttributedTextVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPAttributedTextVisitor.h; sourceTree = ""; }; + 5E58C58B17908CFC00BACAC0 /* BPAttributedTextVisitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPAttributedTextVisitor.m; sourceTree = ""; }; + 5E58C58D17908D2000BACAC0 /* BPElementWalker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPElementWalker.h; sourceTree = ""; }; + 5E58C58E17908D2000BACAC0 /* BPElementWalker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPElementWalker.m; sourceTree = ""; }; + 5E58C59017908D3200BACAC0 /* BPTextVisitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPTextVisitor.h; sourceTree = ""; }; + 5E58C59117908D3200BACAC0 /* BPTextVisitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPTextVisitor.m; sourceTree = ""; }; + 5E58C59417908E9900BACAC0 /* BPDisplaySettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPDisplaySettings.h; sourceTree = ""; }; + 5E58C59517908E9900BACAC0 /* BPDisplaySettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPDisplaySettings.m; sourceTree = ""; }; 5E5BC81816E004FA00165503 /* libBypass.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBypass.a; sourceTree = BUILT_PRODUCTS_DIR; }; 5E5BC81B16E004FA00165503 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 5E5BC81F16E004FA00165503 /* Bypass-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bypass-Prefix.pch"; sourceTree = ""; }; @@ -81,8 +95,6 @@ 5E5BC87D16E12E3100165503 /* BPDocumentPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPDocumentPrivate.h; sourceTree = ""; }; 5E5BC87E16E134C200165503 /* BPDocumentTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BPDocumentTests.mm; sourceTree = ""; }; 5E5BC88116E1448C00165503 /* BPParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPParserTests.m; sourceTree = ""; }; - 5E5BC88516E15C5800165503 /* BPAttributedStringConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BPAttributedStringConverter.h; sourceTree = ""; }; - 5E5BC88616E15C5800165503 /* BPAttributedStringConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPAttributedStringConverter.m; sourceTree = ""; }; 5E5BC88B16E79B2E00165503 /* array.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = array.c; path = ../../../../dep/libsoldout/array.c; sourceTree = ""; }; 5E5BC88C16E79B2E00165503 /* array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = array.h; path = ../../../../dep/libsoldout/array.h; sourceTree = ""; }; 5E5BC88D16E79B2E00165503 /* buffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = buffer.c; path = ../../../../dep/libsoldout/buffer.c; sourceTree = ""; }; @@ -124,6 +136,17 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5E58C58317908CA300BACAC0 /* Accessibility */ = { + isa = PBXGroup; + children = ( + 5E58C58417908CE800BACAC0 /* BPAccessibilityElement.h */, + 5E58C58517908CE800BACAC0 /* BPAccessibilityElement.m */, + 5E58C58617908CE800BACAC0 /* BPAccessibilityVisitor.h */, + 5E58C58717908CE800BACAC0 /* BPAccessibilityVisitor.m */, + ); + name = Accessibility; + sourceTree = ""; + }; 5E5BC80F16E004FA00165503 = { isa = PBXGroup; children = ( @@ -160,6 +183,7 @@ 5E5BC82016E004FA00165503 /* Bypass.h */, 5E5BC85116E0066E00165503 /* BPParser.h */, 5E5BC85216E0066E00165503 /* BPParser.mm */, + 5E58C58317908CA300BACAC0 /* Accessibility */, 6BE4F72216F0E90500670C16 /* Model */, 6BE4F72416F0E99000670C16 /* View */, 5E5BC86716E007AC00165503 /* Soldout */, @@ -226,16 +250,20 @@ 6BE4F72216F0E90500670C16 /* Model */ = { isa = PBXGroup; children = ( - 5E5BC88516E15C5800165503 /* BPAttributedStringConverter.h */, - 5E5BC88616E15C5800165503 /* BPAttributedStringConverter.m */, + 5E58C58A17908CFC00BACAC0 /* BPAttributedTextVisitor.h */, + 5E58C58B17908CFC00BACAC0 /* BPAttributedTextVisitor.m */, + 5E58C59417908E9900BACAC0 /* BPDisplaySettings.h */, + 5E58C59517908E9900BACAC0 /* BPDisplaySettings.m */, 5E5BC87116E0083D00165503 /* BPDocument.h */, 5E5BC87216E0083D00165503 /* BPDocument.mm */, 5E5BC87D16E12E3100165503 /* BPDocumentPrivate.h */, 5E5BC87416E0085000165503 /* BPElement.h */, 5E5BC87516E0085000165503 /* BPElement.mm */, 5E5BC87C16E1222A00165503 /* BPElementPrivate.h */, - 58D9DE2F0735C1CEB3500EFF /* BPDisplaySettings.m */, - 58D9D454C9F2BD2E7F81B4B8 /* BPDisplaySettings.h */, + 5E58C58D17908D2000BACAC0 /* BPElementWalker.h */, + 5E58C58E17908D2000BACAC0 /* BPElementWalker.m */, + 5E58C59017908D3200BACAC0 /* BPTextVisitor.h */, + 5E58C59117908D3200BACAC0 /* BPTextVisitor.m */, ); name = Model; sourceTree = ""; @@ -350,18 +378,22 @@ buildActionMask = 2147483647; files = ( 5E5BC85316E0066E00165503 /* BPParser.mm in Sources */, + 5E58C58817908CE800BACAC0 /* BPAccessibilityElement.m in Sources */, 5E5BC87316E0083D00165503 /* BPDocument.mm in Sources */, + 5E58C58C17908CFC00BACAC0 /* BPAttributedTextVisitor.m in Sources */, 5E5BC87616E0085000165503 /* BPElement.mm in Sources */, - 5E5BC88716E15C5800165503 /* BPAttributedStringConverter.m in Sources */, 5E5BC89116E79B2E00165503 /* array.c in Sources */, + 5E58C59617908E9900BACAC0 /* BPDisplaySettings.m in Sources */, 5E5BC89216E79B2E00165503 /* buffer.c in Sources */, + 5E58C58F17908D2000BACAC0 /* BPElementWalker.m in Sources */, 5E5BC89316E79B2E00165503 /* markdown.c in Sources */, 5E5BC89A16E79B6200165503 /* document.cpp in Sources */, + 5E58C59217908D3300BACAC0 /* BPTextVisitor.m in Sources */, 5E5BC89B16E79B6200165503 /* element.cpp in Sources */, 5E5BC89C16E79B6200165503 /* parser.cpp in Sources */, 6BE4F72716F0E9D100670C16 /* BPMarkdownPageView.m in Sources */, 5E007AEB16FA17D200F8CFFD /* BPMarkdownView.m in Sources */, - 58D9DFA414A385A9337D153E /* BPDisplaySettings.m in Sources */, + 5E58C58917908CE800BACAC0 /* BPAccessibilityVisitor.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/platform/ios/Bypass/Bypass/BPAccessibilityElement.h b/platform/ios/Bypass/Bypass/BPAccessibilityElement.h new file mode 100644 index 00000000..bdcd3ca4 --- /dev/null +++ b/platform/ios/Bypass/Bypass/BPAccessibilityElement.h @@ -0,0 +1,25 @@ +// +// BPAccessibilityElement.h +// Bypass +// +// Created by Damian Carrillo on 3/22/13. +// Copyright 2013 Uncodin, Inc. +// +// 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. +// + +#import + +@interface BPAccessibilityElement : UIAccessibilityElement +@property (assign, nonatomic) NSRange textRange; +@end diff --git a/platform/ios/Bypass/Bypass/BPAccessibilityElement.m b/platform/ios/Bypass/Bypass/BPAccessibilityElement.m new file mode 100644 index 00000000..7ed20550 --- /dev/null +++ b/platform/ios/Bypass/Bypass/BPAccessibilityElement.m @@ -0,0 +1,30 @@ +// +// BPAccessibilityElement.m +// Bypass +// +// Created by Damian Carrillo on 3/22/13. +// Copyright 2013 Uncodin, Inc. +// +// 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. +// + +#import "BPAccessibilityElement.h" + +@implementation BPAccessibilityElement + +- (BOOL)isAccessibilityElement +{ + return YES; +} + +@end diff --git a/platform/ios/Bypass/Bypass/BPAccessibilityVisitor.h b/platform/ios/Bypass/Bypass/BPAccessibilityVisitor.h new file mode 100644 index 00000000..0a2e31b8 --- /dev/null +++ b/platform/ios/Bypass/Bypass/BPAccessibilityVisitor.h @@ -0,0 +1,34 @@ +// +// BPAccessibilityVisitor.h +// Bypass +// +// Created by Damian Carrillo on 3/22/13. +// Copyright 2013 Uncodin, Inc. +// +// 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. +// + +#import +#import "BPElementWalker.h" + +@interface BPAccessibilityVisitor : NSObject + +/*! + * \brief Creates and initializes an accessibility visitor to represent an item in the specified container. + */ +- (id)initWithAccessibilityContainer:(id)container; + +- (NSArray *)accessibleElements; +- (NSArray *)linkIndices; + +@end diff --git a/platform/ios/Bypass/Bypass/BPAccessibilityVisitor.m b/platform/ios/Bypass/Bypass/BPAccessibilityVisitor.m new file mode 100644 index 00000000..4073d466 --- /dev/null +++ b/platform/ios/Bypass/Bypass/BPAccessibilityVisitor.m @@ -0,0 +1,147 @@ +// +// BPAccessibilityVisitor.m +// Bypass +// +// Created by Damian Carrillo on 3/22/13. +// Copyright 2013 Uncodin, Inc. +// +// 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. +// + +#import +#import "BPAccessibilityElement.h" +#import "BPAccessibilityVisitor.h" +#import "BPElement.h" + +@implementation BPAccessibilityVisitor +{ + NSUInteger _elementIndex; + id _accessibilityContainer; + NSMutableArray *_accumulatedAccessibilityElements; + NSArray *_accessibleElements; + NSMutableArray *_accumulatedLinkIndices; + NSArray *_linkIndices; + BPAccessibilityElement* currentElement; +} + +- (id)init +{ + [NSException raise:@"Use initWithAccessibilityContainer:" format:@"Use initWithAccessibilityContainer:"]; + + return self; +} + +- (id)initWithAccessibilityContainer:(id)accessibilityContainer +{ + self = [super init]; + + if (self != nil) { + _elementIndex = 0; + _accessibilityContainer = accessibilityContainer; + _accumulatedAccessibilityElements = [[NSMutableArray alloc] init]; + _accumulatedLinkIndices = [[NSMutableArray alloc] init]; + } + + return self; +} + +- (void)elementWalker:(BPElementWalker *)elementWalker + willVisitElement:(BPElement *)element + withTextRange:(NSRange)textRange +{ + // do nothing +} + +- (void)finishCurrentElement +{ + [_accumulatedAccessibilityElements addObject:currentElement]; + _elementIndex++; + currentElement = nil; +} + +- (int)elementWalker:(BPElementWalker *)elementWalker + didVisitElement:(BPElement *)element + withTextRange:(NSRange)textRange +{ + if ([element text] == nil || [[element text] isEqualToString:@"\n"]) { + // Element is structural and won't need an accessibility element + return 0; + } + + if ([self isAccessibilityDelimiter:element]) { + // Finish the current accessibility element and prep for a new one to be created + if (currentElement != nil) { + [self finishCurrentElement]; + } + } + + if (currentElement == nil) { + currentElement = [[BPAccessibilityElement alloc] initWithAccessibilityContainer:_accessibilityContainer]; + + [currentElement setAccessibilityValue:@""]; + } + + [currentElement setAccessibilityValue:[currentElement.accessibilityValue stringByAppendingString:element.text]]; + [currentElement setAccessibilityLabel:[currentElement accessibilityValue]]; + + // Determine appropriate accessibility traits based on the element type + + UIAccessibilityTraits accessibilityTraits = UIAccessibilityTraitStaticText; + + if ([[element parentElement] elementType] == BPHeader) { + // Header text has a parent element of type BPHeader + accessibilityTraits |= UIAccessibilityTraitHeader; + } + + if ([element elementType] == BPLink) { + accessibilityTraits |= UIAccessibilityTraitLink; + + [_accumulatedLinkIndices addObject:@(_elementIndex)]; + } + + [currentElement setAccessibilityTraits:accessibilityTraits]; + + if ([self isAccessibilityDelimiter:element]) { + // Finish adding the current accessibility element and prep for a new one on the next iteration + [self finishCurrentElement]; + } + + return 0; +} + +- (BOOL)isAccessibilityDelimiter:(BPElement*) element +{ + return [[element parentElement] elementType] == BPHeader || + [element elementType] == BPLink || + [[element parentElement] elementType] == BPListItem; +} + +- (NSArray *)accessibleElements +{ + if (_accessibleElements == nil) { + _accessibleElements = [NSArray arrayWithArray:_accumulatedAccessibilityElements]; + } + + return _accessibleElements; +} + +- (NSArray *)linkIndices +{ + if (_linkIndices == nil) { + _linkIndices = [NSArray arrayWithArray:_accumulatedLinkIndices]; + } + + return _linkIndices; +} + +@end diff --git a/platform/ios/Bypass/Bypass/BPAttributedStringConverter.m b/platform/ios/Bypass/Bypass/BPAttributedStringConverter.m deleted file mode 100644 index b1604665..00000000 --- a/platform/ios/Bypass/Bypass/BPAttributedStringConverter.m +++ /dev/null @@ -1,402 +0,0 @@ -// -// BPAttributedStringRenderer.m -// Bypass -// -// Created by Damian Carrillo on 3/1/13. -// Copyright 2013 Uncodin, Inc. -// -// 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. -// - -#import -#import "BPAttributedStringConverter.h" -#import "BPDisplaySettings.h" - -NSString *const BPLinkStyleAttributeName = @"NSLinkAttributeName"; - -@interface BPAttributedStringConverter () -@property(nonatomic) BOOL renderedFirstParagraph; -@end - -@implementation BPAttributedStringConverter - -#pragma mark Lifecycle - -- (id)init { - if ((self = [super init])) { - _displaySettings = [[BPDisplaySettings alloc] init]; - } - - return self; -} - -#pragma mark Rendering - -- (NSAttributedString *)convertDocument:(BPDocument *)document -{ - NSMutableAttributedString *target = [[NSMutableAttributedString alloc] init]; - - for (BPElement *element in [document elements]) { - [self convertElement:element toTarget:target]; - } - - [target addAttribute:NSForegroundColorAttributeName value:[_displaySettings defaultColor] range:NSMakeRange(0, target.length)]; - - return target; -} - - -- (void)convertElement:(BPElement *)element toTarget:(NSMutableAttributedString *)target -{ - // Capture the starting point of the effective range to apply attributes to - - NSRange effectiveRange; - effectiveRange.location = [target length]; - - BPElementType elementType = [element elementType]; - - // Render span elements immediately, and for some block-level elements, insert special - // characters - - if (elementType == BPList) { - if ([[element parentElement] elementType] == BPListItem) { - [self insertNewlineIntoTarget:target]; - } - } else if (elementType == BPAutoLink) { - [self renderLinkElement:element toTarget:target]; - } else if (elementType == BPCodeSpan) { - [self renderCodeSpanElement:element toTarget:target]; - } else if (elementType == BPDoubleEmphasis) { - [self renderBoldElement:element toTarget:target]; - } else if (elementType == BPEmphasis) { - [self renderItalicElement:element toTarget:target]; - } else if (elementType == BPImage) { - // Currently not supported - } else if (elementType == BPLineBreak) { - [self renderLineBreak:element toTarget:target]; - } else if (elementType == BPLink) { - [self renderLinkElement:element toTarget:target]; - } else if (elementType == BPRawHTMLTag) { - // Currently not supported - } else if (elementType == BPTripleEmphasis) { - [self renderBoldItalicElement:element toTarget:target]; - } else if (elementType == BPText) { - [self renderTextElement:element toTarget:target]; - } else if (elementType == BPStrikethrough) { - [self renderStruckthroughElement:element toTarget:target]; - } - - // Render children of this particular element recursively - - for (BPElement *childElement in [element childElements]) { - [self convertElement:childElement toTarget:target]; - } - - // Capture the end of the range - - effectiveRange.length = [target length] - effectiveRange.location; - - // Follow up with some types of block-level elements and apply properties en masse. - - if (elementType == BPParagraph) { - [self renderParagraphElement:element inRange:effectiveRange toTarget:target]; - } else if (elementType == BPHeader) { - [self renderHeaderElement:element inRange:effectiveRange toTarget:target]; - } else if (elementType == BPListItem) { - [self renderListItemElement:element inRange:effectiveRange toTarget:target]; - } else if (elementType == BPBlockCode) { - [self renderBlockCodeElement:element inRange:effectiveRange toTarget:target]; - } else if (elementType == BPBlockQuote) { - [self renderBlockQuoteElement:element inRange:effectiveRange toTarget:target]; - } - - if ([element isBlockElement] - && ![[element parentElement] isBlockElement] - && ![[[element parentElement] parentElement] isBlockElement]) { - [self insertNewlineIntoTarget:target]; - } -} - -#pragma mark Character Insertion - -- (void)insertNewlineIntoTarget:(NSMutableAttributedString *)target -{ - [target appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@"\n"]]; -} - -#pragma mark Span Element Rendering - -- (void)renderSpanElement:(BPElement *)element - withFont:(UIFont *)font - toTarget:(NSMutableAttributedString *)target -{ - [self renderSpanElement:element - withFont:font - attributes:[NSMutableDictionary dictionary] - toTarget:target]; -} - -- (void)renderSpanElement:(BPElement *)element - withFont:(UIFont *)font - attributes:(NSMutableDictionary *)attributes - toTarget:(NSMutableAttributedString *)target -{ - - if(font == nil) - { - NSLog(@"%@", [element debugDescription]); - return; - } - - attributes[NSFontAttributeName] = font; - - NSString *text; - - if ([[element parentElement] elementType] == BPBlockCode) { - - // Preserve whitespace within a code block - - text = [element text]; - } else { - text = [[element text] stringByReplacingOccurrencesOfString:@"\n" withString:@" "]; - } - - if (text != nil) { - NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text - attributes:attributes]; - [target appendAttributedString:attributedText]; - } -} - -- (void)renderTextElement:(BPElement *)element toTarget:(NSMutableAttributedString *)target -{ - [self renderSpanElement:element withFont:[_displaySettings defaultFont] toTarget:target]; -} - -- (void)renderBoldItalicElement:(BPElement *)element - toTarget:(NSMutableAttributedString *)target -{ - [self renderSpanElement:element withFont:[_displaySettings boldItalicFont] toTarget:target]; -} - -- (void)renderBoldElement:(BPElement *)element - toTarget:(NSMutableAttributedString *)target -{ - [self renderSpanElement:element withFont:[_displaySettings boldFont] toTarget:target]; -} - -- (void)renderItalicElement:(BPElement *)element - toTarget:(NSMutableAttributedString *)target -{ - [self renderSpanElement:element withFont:[_displaySettings italicFont] toTarget:target]; -} - -- (void)renderStruckthroughElement:(BPElement *)element - toTarget:(NSMutableAttributedString *)target -{ - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - attributes[NSStrikethroughStyleAttributeName] = @(1); - [self renderSpanElement:element - withFont:[_displaySettings defaultFont] - attributes:attributes - toTarget:target]; -} - -- (void)renderCodeSpanElement:(BPElement *)element - toTarget:(NSMutableAttributedString *)target -{ - [self renderSpanElement:element withFont:[_displaySettings monospaceFont] toTarget:target]; -} - -- (void)renderLinkElement:(BPElement *)element - toTarget:(NSMutableAttributedString *)target -{ - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - attributes[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle); - attributes[NSForegroundColorAttributeName] = [_displaySettings linkColor]; - attributes[BPLinkStyleAttributeName] = element[@"link"]; - [self renderSpanElement:element - withFont:[_displaySettings defaultFont] - attributes:attributes - toTarget:target]; -} - -- (void)renderLineBreak:(BPElement *)element - toTarget:(NSMutableAttributedString *)target -{ - [self insertNewlineIntoTarget:target]; -} - -#pragma mark Block Element Rendering - -- (void)renderBlockQuoteElement:(BPElement *)element - inRange:(NSRange)effectiveRange - toTarget:(NSMutableAttributedString *)target -{ - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - attributes[NSFontAttributeName] = [_displaySettings quoteFont]; - attributes[NSForegroundColorAttributeName] = [_displaySettings quoteColor]; - - NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; - [paragraphStyle setParagraphSpacing:[_displaySettings paragraphSpacingHeading]]; - [paragraphStyle setFirstLineHeadIndent:[_displaySettings quoteIndentation]]; - [paragraphStyle setHeadIndent:[_displaySettings quoteIndentation]]; - [paragraphStyle setTailIndent:-[_displaySettings quoteIndentation]]; - attributes[NSParagraphStyleAttributeName] = paragraphStyle; - - [target addAttributes:attributes range:effectiveRange]; -} - -- (void)renderBlockCodeElement:(BPElement *)element - inRange:(NSRange)effectiveRange - toTarget:(NSMutableAttributedString *)target -{ - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - attributes[NSFontAttributeName] = [_displaySettings monospaceFont]; - attributes[NSForegroundColorAttributeName] = [_displaySettings codeColor]; - - NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; - [paragraphStyle setParagraphSpacing:[_displaySettings paragraphSpacingCode]]; - [paragraphStyle setFirstLineHeadIndent:[_displaySettings codeIndentation]]; - [paragraphStyle setHeadIndent:[_displaySettings codeIndentation]]; - [paragraphStyle setTailIndent:-[_displaySettings codeIndentation]]; - attributes[NSParagraphStyleAttributeName] = paragraphStyle; - - [target addAttributes:attributes range:effectiveRange]; - [self insertNewlineIntoTarget:target]; -} - -- (void)renderParagraphElement:(BPElement *)element - inRange:(NSRange)effectiveRange - toTarget:(NSMutableAttributedString *)target -{ - NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; - [paragraphStyle setParagraphSpacing:[_displaySettings paragraphSpacing]]; - [paragraphStyle setLineSpacing:[_displaySettings paragraphLineSpacing]]; - [paragraphStyle setFirstLineHeadIndent:[_displaySettings paragraphFirstLineHeadIndent]]; - - if(!self.renderedFirstParagraph) { - [paragraphStyle setFirstLineHeadIndent:[_displaySettings firstParagraphFirstLineHeadIndent]]; - self.renderedFirstParagraph = true; - } - [paragraphStyle setHeadIndent:[_displaySettings paragraphHeadIndent]]; - - NSDictionary *attributes = @{NSParagraphStyleAttributeName : paragraphStyle}; - [target addAttributes:attributes range:effectiveRange]; -} - -- (void)renderListItemElement:(BPElement *)element - inRange:(NSRange)effectiveRange - toTarget:(NSMutableAttributedString *)target -{ - NSUInteger level = 0; - BPElement *inspectedElement = [[element parentElement] parentElement]; - NSMutableString *indentation = [NSMutableString string]; - - while ([inspectedElement elementType] == BPList - || [inspectedElement elementType] == BPListItem) { - if ([inspectedElement elementType] == BPList) { - [indentation appendString:@"\t"]; - ++level; - } - - inspectedElement = [inspectedElement parentElement]; - } - - UIColor *bulletColor; - - switch (level % 3) { - case 1: - bulletColor = [UIColor grayColor]; - break; - case 2: - bulletColor = [UIColor lightGrayColor]; - break; - default: - bulletColor = [UIColor blackColor]; - break; - } - - NSDictionary *bulletAttributes = @{ - NSFontAttributeName : [_displaySettings monospaceFont], - NSForegroundColorAttributeName : bulletColor - }; - - NSAttributedString *attributedBullet; - attributedBullet = [[NSAttributedString alloc] initWithString:@"• " - attributes:bulletAttributes]; - [target insertAttributedString:attributedBullet atIndex:effectiveRange.location]; - - - NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; - [paragraphStyle setLineSpacing:[_displaySettings lineSpacingSmall]]; - - NSDictionary *indentationAttributes = @{ - NSFontAttributeName : [UIFont systemFontOfSize:[_displaySettings bulletIndentation]], - NSParagraphStyleAttributeName : paragraphStyle - }; - - NSAttributedString *attributedIndentation; - attributedIndentation = [[NSAttributedString alloc] initWithString:indentation - attributes:indentationAttributes]; - [target insertAttributedString:attributedIndentation atIndex:effectiveRange.location]; - - if (([[[element parentElement] parentElement] elementType] != BPListItem) - || (element != [[[element parentElement] childElements] lastObject])) { - [self insertNewlineIntoTarget:target]; - } -} - -- (void)renderHeaderElement:(BPElement *)element - inRange:(NSRange)effectiveRange - toTarget:(NSMutableAttributedString *)target -{ - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; - [paragraphStyle setParagraphSpacing:[_displaySettings paragraphSpacingHeading]]; - [paragraphStyle setLineSpacing:[_displaySettings paragraphLineSpacingHeading]]; - [paragraphStyle setFirstLineHeadIndent:[_displaySettings headerFirstLineHeadIndent]]; - [paragraphStyle setHeadIndent:[_displaySettings headerHeadIndent]]; - attributes[NSParagraphStyleAttributeName] = paragraphStyle; - - // Override font weight and size attributes (but preserve all other attributes) - - switch ([element[@"level"] integerValue]) { - case 1: - attributes[NSFontAttributeName] = [_displaySettings h1Font]; - break; - case 2: - [paragraphStyle setParagraphSpacing:[_displaySettings paragraphSpacingH2]]; - attributes[NSFontAttributeName] = [_displaySettings h2Font]; - break; - case 3: - attributes[NSFontAttributeName] = [_displaySettings h3Font]; - break; - case 4: - attributes[NSFontAttributeName] = [_displaySettings h4Font]; - break; - case 5: - attributes[NSFontAttributeName] = [_displaySettings h5Font]; - break; - case 6: - attributes[NSFontAttributeName] = [_displaySettings h6Font]; - break; - default: - attributes[NSFontAttributeName] = [_displaySettings defaultFont]; - break; - } - - [target addAttributes:attributes range:effectiveRange]; -} - -@end diff --git a/platform/ios/Bypass/Bypass/BPAttributedStringConverter.h b/platform/ios/Bypass/Bypass/BPAttributedTextVisitor.h similarity index 60% rename from platform/ios/Bypass/Bypass/BPAttributedStringConverter.h rename to platform/ios/Bypass/Bypass/BPAttributedTextVisitor.h index 974cd6b6..aea8b7b0 100644 --- a/platform/ios/Bypass/Bypass/BPAttributedStringConverter.h +++ b/platform/ios/Bypass/Bypass/BPAttributedTextVisitor.h @@ -1,8 +1,8 @@ // -// BPAttributedStringRenderer.h +// BPAttributedTextVisitor.h // Bypass // -// Created by Damian Carrillo on 3/1/13. +// Created by Damian Carrillo on 3/22/13. // Copyright 2013 Uncodin, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,20 +18,16 @@ // limitations under the License. // +#import +#import #import -#import "BPDocument.h" +#import "Bypass.h" +#import "BPElementWalker.h" -@class BPDisplaySettings; +OBJC_EXPORT NSString* const BPLinkStyleAttributeName; -OBJC_EXPORT NSString *const BPLinkStyleAttributeName; +@interface BPAttributedTextVisitor : NSObject -/*! - \brief Renders a Bypass Document to an `NSAttributedString`. - */ -@interface BPAttributedStringConverter : NSObject - -@property(nonatomic, strong) BPDisplaySettings *displaySettings; - -- (NSAttributedString *)convertDocument:(BPDocument *)document; +@property NSMutableAttributedString* attributedText; @end diff --git a/platform/ios/Bypass/Bypass/BPAttributedTextVisitor.m b/platform/ios/Bypass/Bypass/BPAttributedTextVisitor.m new file mode 100644 index 00000000..2684e265 --- /dev/null +++ b/platform/ios/Bypass/Bypass/BPAttributedTextVisitor.m @@ -0,0 +1,562 @@ +// +// BPAttributedTextVisitor.m +// Bypass +// +// Created by Damian Carrillo on 3/22/13. +// Copyright 2013 Uncodin, Inc. +// +// 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. +// + +#import "BPAttributedTextVisitor.h" + +NSString *const BPLinkStyleAttributeName = @"NSLinkAttributeName"; + +static const CGFloat kBulletIndentation = 13.0f; +static const CGFloat kCodeIndentation = 10.0f; +static const CGFloat kQuoteIndentation = 23.0f; +static const CGFloat kLineSpacingSmall = 1.2f; +static const CGFloat kParagraphSpacingLarge = 20.0f; +static const CGFloat kParagraphSpacingSmall = 10.0f; +static const CGFloat kParagraphSpacingNone = 0.0f; + +@implementation BPAttributedTextVisitor +{ + CTFontRef _defaultFont; + CTFontRef _boldFont; + CTFontRef _italicFont; + CTFontRef _boldItalicFont; + CTFontRef _monospaceFont; + CTFontRef _quoteFont; + CTFontRef _h1Font; + CTFontRef _h2Font; + CTFontRef _h3Font; + CTFontRef _h4Font; + CTFontRef _h5Font; + CTFontRef _h6Font; +} + +- (id)init +{ + self = [super init]; + + if (self != nil) { + _attributedText = [[NSMutableAttributedString alloc] init]; + } + + return self; +} + +- (void)elementWalker:(BPElementWalker *)elementWalker + willVisitElement:(BPElement *)element + withTextRange:(NSRange)textRange +{ + // do nothing +} + +- (int)elementWalker:(BPElementWalker *)elementWalker + didVisitElement:(BPElement *)element + withTextRange:(NSRange)textRange +{ + return [self convertElement:element toTarget:_attributedText range:textRange]; +} + +- (void)dealloc +{ + if (_defaultFont != NULL) CFRelease(_defaultFont); + if (_boldFont != NULL) CFRelease(_boldFont); + if (_italicFont != NULL) CFRelease(_italicFont); + if (_boldItalicFont != NULL) CFRelease(_boldItalicFont); + if (_monospaceFont != NULL) CFRelease(_monospaceFont); + if (_quoteFont != NULL) CFRelease(_quoteFont); + if (_h1Font != NULL) CFRelease(_h1Font); + if (_h2Font != NULL) CFRelease(_h2Font); + if (_h3Font != NULL) CFRelease(_h3Font); + if (_h4Font != NULL) CFRelease(_h4Font); + if (_h5Font != NULL) CFRelease(_h5Font); + if (_h6Font != NULL) CFRelease(_h6Font); +} + +#pragma mark Fonts + +- (UIFont *)UIFontFromCTFont:(CTFontRef)ctFont +{ + NSString *fontName; + fontName = (__bridge_transfer NSString *) CTFontCopyName(ctFont, kCTFontPostScriptNameKey); + + CGFloat fontSize = CTFontGetSize(ctFont); + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + return font; +} + +- (CTFontRef)defaultFont +{ + if (_defaultFont == NULL) { + CGFloat systemFontSize = [UIFont systemFontSize]; + UIFont *systemFont = [UIFont systemFontOfSize:systemFontSize]; + CFStringRef systemFontName = (__bridge CFStringRef) [systemFont fontName]; + + _defaultFont = CTFontCreateWithName(systemFontName, systemFontSize, NULL); + } + + return _defaultFont; +} + +- (CTFontRef)boldFont +{ + if (_boldFont == NULL) { + _boldFont = CTFontCreateCopyWithSymbolicTraits([self defaultFont], + 0.f, + NULL, + kCTFontBoldTrait, + kCTFontBoldTrait); + } + + return _boldFont; +} + +- (CTFontRef)italicFont +{ + if (_italicFont == NULL) { + _italicFont = CTFontCreateCopyWithSymbolicTraits([self defaultFont], + 0.f, + NULL, + kCTFontItalicTrait, + kCTFontItalicTrait); + } + + return _italicFont; +} + +- (CTFontRef)boldItalicFont +{ + if (_boldItalicFont == NULL) { + CTFontSymbolicTraits traits = kCTFontBoldTrait | kCTFontItalicTrait; + CTFontSymbolicTraits mask = kCTFontBoldTrait | kCTFontItalicTrait; + _boldItalicFont = CTFontCreateCopyWithSymbolicTraits([self defaultFont], + 0.f, + NULL, + traits, + mask); + } + + return _boldItalicFont; +} + +- (CTFontRef)monospaceFont +{ + if (_monospaceFont == NULL) { + _monospaceFont = CTFontCreateWithName(CFSTR("Courier"), + CTFontGetSize([self defaultFont]) - 2, NULL); + } + + return _monospaceFont; +} + +- (CTFontRef)quoteFont +{ + if (_quoteFont == NULL) { + _quoteFont = CTFontCreateWithName(CFSTR("Marion-Italic"), + CTFontGetSize([self defaultFont]) + 2, NULL); + } + + return _quoteFont; +} + +- (CTFontRef)h1Font +{ + if (_h1Font == NULL) { + _h1Font = CTFontCreateWithName(CFSTR("HelveticaNeue-CondensedBold"), + CTFontGetSize([self defaultFont]) * 2, NULL); + } + + return _h1Font; +} + +- (CTFontRef)h2Font +{ + if (_h2Font == NULL) { + _h2Font = CTFontCreateWithName(CFSTR("HelveticaNeue-CondensedBold"), + CTFontGetSize([self defaultFont]) * 1.8, NULL); + } + + return _h2Font; +} + +- (CTFontRef)h3Font +{ + if (_h3Font == NULL) { + _h3Font = CTFontCreateWithName(CFSTR("HelveticaNeue-CondensedBold"), + CTFontGetSize([self defaultFont]) * 1.6, NULL); + } + + return _h3Font; +} + +- (CTFontRef)h4Font +{ + if (_h4Font == NULL) { + _h4Font = CTFontCreateWithName(CFSTR("HelveticaNeue-CondensedBold"), + CTFontGetSize([self defaultFont]) * 1.4, NULL); + } + + return _h4Font; +} + +- (CTFontRef)h5Font +{ + if (_h5Font == NULL) { + _h5Font = CTFontCreateWithName(CFSTR("HelveticaNeue-CondensedBold"), + CTFontGetSize([self defaultFont]) * 1.2, NULL); + } + + return _h5Font; +} + +- (CTFontRef)h6Font +{ + if (_h6Font == NULL) { + _h6Font = CTFontCreateWithName(CFSTR("HelveticaNeue-CondensedBold"), + CTFontGetSize([self defaultFont]) * 1, NULL); + } + + return _h6Font; +} + +#pragma mark Rendering + +- (int)convertElement:(BPElement *)element + toTarget:(NSMutableAttributedString *)target + range:(NSRange)effectiveRange +{ + int insertedCharacters = 0; + + BPElementType elementType = [element elementType]; + + // Render span elements and insert special characters for block elements + if (elementType == BPList) { + if ([[element parentElement] elementType] == BPListItem) { + insertedCharacters += [self insertNewlineIntoTarget:target atIndex:effectiveRange.location]; + } + } else if (elementType == BPAutoLink) { + [self renderLinkElement:element toTarget:target]; + } else if (elementType == BPCodeSpan) { + [self renderCodeSpanElement:element toTarget:target]; + } else if (elementType == BPDoubleEmphasis) { + [self renderBoldElement:element toTarget:target]; + } else if (elementType == BPEmphasis) { + [self renderItalicElement:element toTarget:target]; + } else if (elementType == BPImage) { + // Currently not supported + } else if (elementType == BPLineBreak) { + [self renderLineBreak:element toTarget:target]; + } else if (elementType == BPLink) { + [self renderLinkElement:element toTarget:target]; + } else if (elementType == BPRawHTMLTag) { + // Currently not supported + } else if (elementType == BPTripleEmphasis) { + [self renderBoldItalicElement:element toTarget:target]; + } else if (elementType == BPText) { + [self renderTextElement:element toTarget:target]; + } else if (elementType == BPParagraph) { + [self renderParagraphElement:element inRange:effectiveRange toTarget:target]; + } else if (elementType == BPHeader) { + [self renderHeaderElement:element inRange:effectiveRange toTarget:target]; + } else if (elementType == BPListItem) { + insertedCharacters += [self renderListItemElement:element inRange:effectiveRange toTarget:target]; + } else if (elementType == BPBlockCode) { + insertedCharacters += [self renderBlockCodeElement:element inRange:effectiveRange toTarget:target]; + } else if (elementType == BPBlockQuote) { + [self renderBlockQuoteElement:element inRange:effectiveRange toTarget:target]; + } + + if ([element isBlockElement] + && ![[element parentElement] isBlockElement] + && ![[[element parentElement] parentElement] isBlockElement]) { + insertedCharacters += [self appendNewlineOntoTarget:target]; + } + + return insertedCharacters; +} + +#pragma mark Character Insertion + +- (int)insertNewlineIntoTarget:(NSMutableAttributedString*) target + atIndex:(int) index +{ + [target insertAttributedString:[[NSMutableAttributedString alloc] initWithString:@"\n"] atIndex:index]; + + return 1; +} + +- (int)appendNewlineOntoTarget:(NSMutableAttributedString *)target +{ + [target appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@"\n"]]; + + return 1; +} + +- (int)insertBulletIntoTarget:(NSMutableAttributedString*) target + color:(UIColor*) bulletColor + atIndex:(int)index +{ + NSDictionary *bulletAttributes = @{ + NSFontAttributeName : [self UIFontFromCTFont:[self monospaceFont]], + NSForegroundColorAttributeName : bulletColor + }; + + NSAttributedString *attributedBullet; + attributedBullet = [[NSAttributedString alloc] initWithString:@"• " + attributes:bulletAttributes]; + + [target insertAttributedString:attributedBullet atIndex:index]; + + return 2; +} + +#pragma mark Span Element Rendering + +- (void)renderSpanElement:(BPElement *)element + withFont:(CTFontRef)font + toTarget:(NSMutableAttributedString *)target +{ + [self renderSpanElement:element + withFont:font + attributes:[NSMutableDictionary dictionary] + toTarget:target]; +} + +- (void)renderSpanElement:(BPElement *)element + withFont:(CTFontRef)font + attributes:(NSMutableDictionary *)attributes + toTarget:(NSMutableAttributedString *)target +{ + attributes[NSFontAttributeName] = [self UIFontFromCTFont:font]; + + NSString *text; + + if ([[element parentElement] elementType] == BPBlockCode) { + + // Preserve whitespace within a code block + + text = [element text]; + } else { + text = [[element text] stringByReplacingOccurrencesOfString:@"\n" withString:@" "]; + } + + NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text + attributes:attributes]; + [target appendAttributedString:attributedText]; +} + +- (void)renderTextElement:(BPElement *)element toTarget:(NSMutableAttributedString *)target +{ + [self renderSpanElement:element withFont:[self defaultFont] toTarget:target]; +} + +- (void)renderBoldItalicElement:(BPElement *)element + toTarget:(NSMutableAttributedString *)target +{ + [self renderSpanElement:element withFont:[self boldItalicFont] toTarget:target]; +} + +- (void)renderBoldElement:(BPElement *)element + toTarget:(NSMutableAttributedString *)target +{ + [self renderSpanElement:element withFont:[self boldFont] toTarget:target]; +} + +- (void)renderItalicElement:(BPElement *)element + toTarget:(NSMutableAttributedString *)target +{ + [self renderSpanElement:element withFont:[self italicFont] toTarget:target]; +} + +- (void)renderCodeSpanElement:(BPElement *)element + toTarget:(NSMutableAttributedString *)target +{ + [self renderSpanElement:element withFont:[self monospaceFont] toTarget:target]; +} + +- (void)renderLinkElement:(BPElement *)element + toTarget:(NSMutableAttributedString *)target +{ + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + attributes[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle); + attributes[NSForegroundColorAttributeName] = [UIColor blueColor]; + attributes[BPLinkStyleAttributeName] = element[@"link"]; + [self renderSpanElement:element + withFont:_defaultFont + attributes:attributes toTarget:target]; +} + +- (void)renderLineBreak:(BPElement *)element + toTarget:(NSMutableAttributedString *)target +{ + [self appendNewlineOntoTarget:target]; +} + +#pragma mark Block Element Rendering + +- (void)renderBlockQuoteElement:(BPElement *)element + inRange:(NSRange)effectiveRange + toTarget:(NSMutableAttributedString *)target +{ + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self quoteFont]]; + + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setParagraphSpacing:kParagraphSpacingSmall]; + [paragraphStyle setFirstLineHeadIndent:kQuoteIndentation]; + [paragraphStyle setHeadIndent:kQuoteIndentation]; + [paragraphStyle setTailIndent:-kQuoteIndentation]; + attributes[NSParagraphStyleAttributeName] = paragraphStyle; + + [target addAttributes:attributes range:effectiveRange]; +} + +- (int)renderBlockCodeElement:(BPElement *)element + inRange:(NSRange)effectiveRange + toTarget:(NSMutableAttributedString *)target +{ + int insertedCharacters = 0; + + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self monospaceFont]]; + attributes[NSForegroundColorAttributeName] = [UIColor grayColor]; + + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setParagraphSpacing:kParagraphSpacingNone]; + [paragraphStyle setFirstLineHeadIndent:kCodeIndentation]; + [paragraphStyle setHeadIndent:kCodeIndentation]; + [paragraphStyle setTailIndent:-kCodeIndentation]; + attributes[NSParagraphStyleAttributeName] = paragraphStyle; + + [target addAttributes:attributes range:effectiveRange]; + + insertedCharacters += [self appendNewlineOntoTarget:target]; + + return insertedCharacters; +} + +- (void)renderParagraphElement:(BPElement *)element + inRange:(NSRange)effectiveRange + toTarget:(NSMutableAttributedString *)target +{ + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setParagraphSpacing:kParagraphSpacingLarge]; + [paragraphStyle setLineSpacing:1.1f]; + + NSDictionary *attributes = @{NSParagraphStyleAttributeName : paragraphStyle}; + [target addAttributes:attributes range:effectiveRange]; +} + +- (int)renderListItemElement:(BPElement *)element + inRange:(NSRange)effectiveRange + toTarget:(NSMutableAttributedString *)target +{ + int insertedCharacters = 0; + + NSUInteger level = 0; + BPElement *inspectedElement = [[element parentElement] parentElement]; + NSMutableString *indentation = [NSMutableString string]; + + while ([inspectedElement elementType] == BPList + || [inspectedElement elementType] == BPListItem) { + if ([inspectedElement elementType] == BPList) { + [indentation appendString:@"\t"]; + ++level; + } + + inspectedElement = [inspectedElement parentElement]; + } + + UIColor *bulletColor; + + switch (level % 3) { + case 1: + bulletColor = [UIColor grayColor]; + break; + case 2: + bulletColor = [UIColor lightGrayColor]; + break; + default: + bulletColor = [UIColor blackColor]; + break; + } + + insertedCharacters += [self insertBulletIntoTarget:target color:bulletColor atIndex:effectiveRange.location]; + + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setLineSpacing:kLineSpacingSmall]; + + NSDictionary *indentationAttributes = @{ + NSFontAttributeName : [UIFont systemFontOfSize:kBulletIndentation], + NSParagraphStyleAttributeName : paragraphStyle + }; + + NSAttributedString *attributedIndentation; + attributedIndentation = [[NSAttributedString alloc] initWithString:indentation + attributes:indentationAttributes]; + [target insertAttributedString:attributedIndentation atIndex:effectiveRange.location]; + insertedCharacters += [attributedIndentation length]; + + if (([[[element parentElement] parentElement] elementType] != BPListItem) + || (element != [[[element parentElement] childElements] lastObject])) { + insertedCharacters += [self appendNewlineOntoTarget:target]; + } + + return insertedCharacters; +} + +- (void)renderHeaderElement:(BPElement *)element + inRange:(NSRange)effectiveRange + toTarget:(NSMutableAttributedString *)target +{ + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + [paragraphStyle setParagraphSpacing:kParagraphSpacingSmall]; + + attributes[NSParagraphStyleAttributeName] = paragraphStyle; + + // Override font weight and size attributes (but preserve all other attributes) + + switch ([element[@"level"] integerValue]) { + case 1: + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self h1Font]]; + break; + case 2: + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self h2Font]]; + break; + case 3: + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self h3Font]]; + break; + case 4: + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self h4Font]]; + break; + case 5: + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self h5Font]]; + break; + case 6: + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self h6Font]]; + break; + default: + attributes[NSFontAttributeName] = [self UIFontFromCTFont:[self defaultFont]]; + break; + } + + [target addAttributes:attributes range:effectiveRange]; +} + + +@end diff --git a/platform/ios/Bypass/Bypass/BPDisplaySettings.h b/platform/ios/Bypass/Bypass/BPDisplaySettings.h index d2bb6917..8f94ef25 100644 --- a/platform/ios/Bypass/Bypass/BPDisplaySettings.h +++ b/platform/ios/Bypass/Bypass/BPDisplaySettings.h @@ -5,8 +5,7 @@ // To change the template use AppCode | Preferences | File Templates. // - -#import +#import #import @interface BPDisplaySettings : NSObject @@ -51,4 +50,4 @@ @property(nonatomic) CGFloat headerFirstLineHeadIndent; @property(nonatomic) CGFloat headerHeadIndent; -@end +@end \ No newline at end of file diff --git a/platform/ios/Bypass/Bypass/BPDisplaySettings.m b/platform/ios/Bypass/Bypass/BPDisplaySettings.m index b0704f69..867a99f9 100644 --- a/platform/ios/Bypass/Bypass/BPDisplaySettings.m +++ b/platform/ios/Bypass/Bypass/BPDisplaySettings.m @@ -6,7 +6,6 @@ // -#import "BPAttributedStringConverter.h" #import #import "BPDisplaySettings.h" @@ -15,54 +14,54 @@ @implementation BPDisplaySettings - (id)init { - self = [super init]; - if (self) { - CGFloat systemFontSize = [UIFont systemFontSize]; - - self.defaultFont = [UIFont systemFontOfSize:systemFontSize]; - self.boldFont = [UIFont boldSystemFontOfSize:systemFontSize]; - self.italicFont = [UIFont italicSystemFontOfSize:systemFontSize]; - self.monospaceFont = [UIFont fontWithName:@"Courier" size:systemFontSize - 2.f]; - self.quoteFont = [UIFont fontWithName:@"Marion-Italic" size:systemFontSize - 2.f]; - self.h1Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 2.f]; - self.h2Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 1.8f]; - self.h3Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 1.6f]; - self.h4Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 1.4f]; - self.h5Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 1.2f]; - self.h6Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize]; - - self.defaultColor = [UIColor blackColor]; - self.quoteColor = [UIColor darkGrayColor]; - self.codeColor = [UIColor grayColor]; - self.linkColor = [UIColor blueColor]; - self.bulletIndentation = 13.0f; - self.codeIndentation = 10.0f; - self.quoteIndentation = 23.0f; - self.paragraphSpacing = 20.0f; - self.paragraphSpacingHeading = 10.0f; - self.paragraphSpacingCode = 0.0f; - - self.paragraphLineSpacing = 1.2f; - self.paragraphLineSpacingHeading = 1.2f; - - } - return self; + self = [super init]; + if (self) { + CGFloat systemFontSize = [UIFont systemFontSize]; + + self.defaultFont = [UIFont systemFontOfSize:systemFontSize]; + self.boldFont = [UIFont boldSystemFontOfSize:systemFontSize]; + self.italicFont = [UIFont italicSystemFontOfSize:systemFontSize]; + self.monospaceFont = [UIFont fontWithName:@"Courier" size:systemFontSize - 2.f]; + self.quoteFont = [UIFont fontWithName:@"Marion-Italic" size:systemFontSize - 2.f]; + self.h1Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 2.f]; + self.h2Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 1.8f]; + self.h3Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 1.6f]; + self.h4Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 1.4f]; + self.h5Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize * 1.2f]; + self.h6Font = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:systemFontSize]; + + self.defaultColor = [UIColor blackColor]; + self.quoteColor = [UIColor darkGrayColor]; + self.codeColor = [UIColor grayColor]; + self.linkColor = [UIColor blueColor]; + self.bulletIndentation = 13.0f; + self.codeIndentation = 10.0f; + self.quoteIndentation = 23.0f; + self.paragraphSpacing = 20.0f; + self.paragraphSpacingHeading = 10.0f; + self.paragraphSpacingCode = 0.0f; + + self.paragraphLineSpacing = 1.2f; + self.paragraphLineSpacingHeading = 1.2f; + + } + return self; } - (UIFont *)boldItalicFont { - if (_boldItalicFont == nil) { - CTFontRef defaultFontRef = [self newCTFontRefFromUIFont:self.defaultFont]; - CTFontSymbolicTraits traits = kCTFontBoldTrait | kCTFontItalicTrait; - CTFontSymbolicTraits mask = kCTFontBoldTrait | kCTFontItalicTrait; - CTFontRef boldItalicFontRef = CTFontCreateCopyWithSymbolicTraits(defaultFontRef, 0.f, NULL, traits, mask); - assert(boldItalicFontRef != NULL); - - _boldItalicFont = [self UIFontFromCTFont:defaultFontRef]; - CFRelease(defaultFontRef); - } - - return _boldItalicFont; + if (_boldItalicFont == nil) { + CTFontRef defaultFontRef = [self newCTFontRefFromUIFont:self.defaultFont]; + CTFontSymbolicTraits traits = kCTFontBoldTrait | kCTFontItalicTrait; + CTFontSymbolicTraits mask = kCTFontBoldTrait | kCTFontItalicTrait; + CTFontRef boldItalicFontRef = CTFontCreateCopyWithSymbolicTraits(defaultFontRef, 0.f, NULL, traits, mask); + assert(boldItalicFontRef != NULL); + + _boldItalicFont = [self UIFontFromCTFont:defaultFontRef]; + CFRelease(defaultFontRef); + } + + return _boldItalicFont; } #pragma mark - Private @@ -79,4 +78,4 @@ - (CTFontRef)newCTFontRefFromUIFont:(UIFont *)font { return CTFontCreateWithName((__bridge CFStringRef)font.fontName, font.pointSize, NULL); } -@end +@end \ No newline at end of file diff --git a/platform/ios/Bypass/Bypass/BPElement.mm b/platform/ios/Bypass/Bypass/BPElement.mm index 1e58bf46..69249659 100644 --- a/platform/ios/Bypass/Bypass/BPElement.mm +++ b/platform/ios/Bypass/Bypass/BPElement.mm @@ -54,7 +54,7 @@ @interface BPElement () @implementation BPElement { - Bypass::Element _element; + Bypass::Element _element; NSString *_text; NSDictionary *_attributes; __weak BPElement *_parentElement; diff --git a/platform/ios/Bypass/Bypass/BPElementWalker.h b/platform/ios/Bypass/Bypass/BPElementWalker.h new file mode 100644 index 00000000..e4a97ec5 --- /dev/null +++ b/platform/ios/Bypass/Bypass/BPElementWalker.h @@ -0,0 +1,46 @@ +// +// BPElementWalker.h +// Bypass +// +// Created by Damian Carrillo on 3/22/13. +// Copyright 2013 Uncodin, Inc. +// +// 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. +// + +#import + +@class BPElement; +@class BPDocument; +@protocol BPElementVisitor; + +@interface BPElementWalker : NSObject + +- (void)addElementVisitor:(id)elementVisitor; +- (void)walkDocument:(BPDocument *)document; + +@end + +@protocol BPElementVisitor +@required + +- (void)elementWalker:(BPElementWalker *)elementWalker + willVisitElement:(BPElement *)element + withTextRange:(NSRange)textRange; + +// Returns the number of characters added to the element's text. If characters were removed, the return value should be negative. +- (int)elementWalker:(BPElementWalker *)elementWalker + didVisitElement:(BPElement *)element + withTextRange:(NSRange)textRange; + +@end \ No newline at end of file diff --git a/platform/ios/Bypass/Bypass/BPElementWalker.m b/platform/ios/Bypass/Bypass/BPElementWalker.m new file mode 100644 index 00000000..5e4d97b6 --- /dev/null +++ b/platform/ios/Bypass/Bypass/BPElementWalker.m @@ -0,0 +1,80 @@ +// +// BPElementWalker.m +// Bypass +// +// Created by Damian Carrillo on 3/22/13. +// Copyright 2013 Uncodin, Inc. +// +// 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. +// + +#import "BPDocument.h" +#import "BPElementWalker.h" + +@implementation BPElementWalker +{ + NSMutableArray *_elementVisitors; + NSUInteger _location; +} + +- (id)init +{ + self = [super init]; + + if (self != nil) { + _elementVisitors = [[NSMutableArray alloc] init]; + } + + return self; +} + +- (void)addElementVisitor:(id)elementVisitor +{ + [_elementVisitors addObject:elementVisitor]; +} + +- (void)walkDocument:(BPDocument *)document +{ + _location = 0; + + for (BPElement *element in [document elements]) { + [self walkSubtreeWithRootElement:element]; + } +} + +- (void)walkSubtreeWithRootElement:(BPElement *)rootElement +{ + NSRange textRange; + textRange.location = _location; + + // Process child elements first + for (BPElement *element in [rootElement childElements]) { + [self walkSubtreeWithRootElement:element]; + } + + // Set effective range of this element + _location += [[rootElement text] length]; + textRange.length = _location - textRange.location; + + // Prepare to visit element + for (id elementVisitor in _elementVisitors) { + [elementVisitor elementWalker:self willVisitElement:rootElement withTextRange:textRange]; + } + + // Visit element + for (id elementVisitor in _elementVisitors) { + _location += [elementVisitor elementWalker:self didVisitElement:rootElement withTextRange:textRange]; + } +} + +@end \ No newline at end of file diff --git a/platform/ios/Bypass/Bypass/BPMarkdownPageView.m b/platform/ios/Bypass/Bypass/BPMarkdownPageView.m index b134257c..51541d5f 100644 --- a/platform/ios/Bypass/Bypass/BPMarkdownPageView.m +++ b/platform/ios/Bypass/Bypass/BPMarkdownPageView.m @@ -18,7 +18,7 @@ // limitations under the License. // -#import "BPAttributedStringConverter.h" +#import "BPAttributedTextVisitor.h" #import "BPDocument.h" #import "BPMarkdownPageView.h" diff --git a/platform/ios/Bypass/Bypass/BPMarkdownView.h b/platform/ios/Bypass/Bypass/BPMarkdownView.h index c7390bd7..2b58d80e 100644 --- a/platform/ios/Bypass/Bypass/BPMarkdownView.h +++ b/platform/ios/Bypass/Bypass/BPMarkdownView.h @@ -62,4 +62,4 @@ - (void)markdownView:(BPMarkdownView *)markdownView didHaveLinkTapped:(NSString *)link; -@end +@end \ No newline at end of file diff --git a/platform/ios/Bypass/Bypass/BPMarkdownView.m b/platform/ios/Bypass/Bypass/BPMarkdownView.m index aa765c9b..e1c66ff6 100644 --- a/platform/ios/Bypass/Bypass/BPMarkdownView.m +++ b/platform/ios/Bypass/Bypass/BPMarkdownView.m @@ -19,11 +19,13 @@ // #import -#import "BPAttributedStringConverter.h" #import "BPMarkdownView.h" #import "BPMarkdownPageView.h" #import "BPParser.h" #import "BPDisplaySettings.h" +#import "BPElementWalker.h" +#import "BPAttributedTextVisitor.h" +#import "BPAccessibilityVisitor.h" /* * The standard margin of the UIKit views. This value was based on human inspection. @@ -51,14 +53,41 @@ * */ static CFArrayRef -BPCreatePageFrames(CGSize pageSize, CGSize *suggestedContentSizeOut, CFAttributedStringRef attributedText) -{ +BPCreatePageFrames(BPDocument *document, + CGSize pageSize, + CGSize *suggestedContentSizeOut, + NSAttributedString **attributedTextOut, + NSArray **accessibleElementsOut, + id accessibilityContainer) { + BPElementWalker* walker = [[BPElementWalker alloc] init]; + + BPAttributedTextVisitor* textVisitor = [[BPAttributedTextVisitor alloc] init]; + [walker addElementVisitor:textVisitor]; + + BPAccessibilityVisitor* accessVisitor = [[BPAccessibilityVisitor alloc] initWithAccessibilityContainer:accessibilityContainer]; + [walker addElementVisitor:accessVisitor]; + + [walker walkDocument:document]; + + *attributedTextOut = textVisitor.attributedText; + *accessibleElementsOut = accessVisitor.accessibleElements; + + CFAttributedStringRef attrText; + attrText = (__bridge CFAttributedStringRef) *attributedTextOut; + + CFIndex len = CFAttributedStringGetLength(attrText); + CFMutableAttributedStringRef mutableAttributedText; + mutableAttributedText = CFAttributedStringCreateMutableCopy(kCFAllocatorDefault, + len, + attrText); + CFMutableArrayRef frames = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); CTFramesetterRef framesetter; - framesetter = CTFramesetterCreateWithAttributedString(attributedText); - + framesetter = CTFramesetterCreateWithAttributedString(mutableAttributedText); + CFRelease(mutableAttributedText); + CGRect pageRect = CGRectMake(0.f, 0.f, pageSize.width, pageSize.height); CGSize constraints = CGSizeMake(CGRectGetWidth(pageRect), CGFLOAT_MAX); @@ -68,7 +97,7 @@ NULL, constraints, &fitRange); - *suggestedContentSizeOut = suggestedSize; // TODO:ContentSize is too small + *suggestedContentSizeOut = suggestedSize; pageRect.size.height = MIN(pageSize.height, suggestedSize.height); @@ -96,12 +125,13 @@ @interface BPMarkdownView () +#import "BPElementWalker.h" + +@interface BPTextVisitor : NSObject + +- (NSString *)text; + +@end diff --git a/platform/ios/Bypass/Bypass/BPTextVisitor.m b/platform/ios/Bypass/Bypass/BPTextVisitor.m new file mode 100644 index 00000000..64050b14 --- /dev/null +++ b/platform/ios/Bypass/Bypass/BPTextVisitor.m @@ -0,0 +1,67 @@ +// +// BPTextVisitor.m +// Bypass +// +// Created by Damian Carrillo on 3/22/13. +// Copyright 2013 Uncodin, Inc. +// +// 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. +// + +#import "BPTextVisitor.h" +#import "BPElement.h" + +@implementation BPTextVisitor +{ + NSMutableString *_accumulatedText; + NSString *_text; +} + +- (id)init +{ + self = [super init]; + + if (self != nil) { + _accumulatedText = [[NSMutableString alloc] init]; + _text = nil; + } + + return self; +} + +- (void)elementWalker:(BPElementWalker *)elementWalker + willVisitElement:(BPElement *)element + withTextRange:(NSRange)textRange +{ + // do nothing +} + +- (int)elementWalker:(BPElementWalker *)elementWalker + didVisitElement:(BPElement *)element + withTextRange:(NSRange)textRange +{ + [_accumulatedText appendString:[element text]]; + + return 0; +} + +- (NSString *)text +{ + if (_text == nil) { + _text = [NSString stringWithString:_accumulatedText]; + } + + return _text; +} + +@end diff --git a/platform/ios/Bypass/Bypass/Bypass.h b/platform/ios/Bypass/Bypass/Bypass.h index 419d77f3..62baa023 100644 --- a/platform/ios/Bypass/Bypass/Bypass.h +++ b/platform/ios/Bypass/Bypass/Bypass.h @@ -18,9 +18,9 @@ // limitations under the License. // -#import "BPAttributedStringConverter.h" #import "BPElement.h" #import "BPDocument.h" #import "BPMarkdownView.h" #import "BPParser.h" #import "BPDisplaySettings.h" +#import "BPAttributedTextVisitor.h" \ No newline at end of file diff --git a/platform/ios/BypassSample/BypassSample.xcodeproj/project.xcworkspace/xcshareddata/BypassSample.xccheckout b/platform/ios/BypassSample/BypassSample.xcodeproj/project.xcworkspace/xcshareddata/BypassSample.xccheckout new file mode 100644 index 00000000..750c7579 --- /dev/null +++ b/platform/ios/BypassSample/BypassSample.xcodeproj/project.xcworkspace/xcshareddata/BypassSample.xccheckout @@ -0,0 +1,39 @@ + + + + + IDESourceControlProjectIdentifier + 314B6ABD-5720-40E9-8860-7FBD13210F3B + IDESourceControlProjectName + BypassSample + IDESourceControlProjectOriginsDictionary + + A0DF3507-AD53-4E77-9C1A-1E3B26091AA0 + ssh://github.com/damiancarrillo/bypass.git + + IDESourceControlProjectPath + platform/ios/BypassSample/BypassSample.xcodeproj/project.xcworkspace + IDESourceControlProjectRelativeInstallPathDictionary + + A0DF3507-AD53-4E77-9C1A-1E3B26091AA0 + ../../../../.. + + IDESourceControlProjectURL + ssh://github.com/damiancarrillo/bypass.git + IDESourceControlProjectVersion + 110 + IDESourceControlProjectWCCIdentifier + A0DF3507-AD53-4E77-9C1A-1E3B26091AA0 + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + A0DF3507-AD53-4E77-9C1A-1E3B26091AA0 + IDESourceControlWCCName + bypass + + + + diff --git a/platform/ios/BypassSample/BypassSample/BPTextViewController.mm b/platform/ios/BypassSample/BypassSample/BPTextViewController.mm index 820c8b1d..4781b1c8 100644 --- a/platform/ios/BypassSample/BypassSample/BPTextViewController.mm +++ b/platform/ios/BypassSample/BypassSample/BPTextViewController.mm @@ -66,8 +66,13 @@ - (void)viewWillAppear:(BOOL)animated BPParser *parser = [[BPParser alloc] init]; BPDocument *document = [parser parse:sample]; - BPAttributedStringConverter *converter = [[BPAttributedStringConverter alloc] init]; - NSAttributedString *attributedText = [converter convertDocument:document]; + BPAttributedTextVisitor* textVisitor = [BPAttributedTextVisitor new]; + + BPElementWalker* walker = [BPElementWalker new]; + [walker addElementVisitor:textVisitor]; + [walker walkDocument:document]; + + NSAttributedString *attributedText = textVisitor.attributedText; // Warning: The attributed text is being set on a simple UITextView out of convenience. After this has been done, // Bypass' custom text attributes have been stripped. We save a copy to use as a point of reference for