Epub阅读器0.0.1
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
//
|
||||
// WRBookmark.m
|
||||
// WeRead (微信读书)
|
||||
// Reverse-engineered implementation reconstruction
|
||||
//
|
||||
// Detailed pseudo-code based on binary analysis (many NSString ivars,
|
||||
// WRBook, WRMPReview, NSArray, NSDictionary, NSMutableSet) and
|
||||
// contextual knowledge of WeRead's bookmark/annotation system.
|
||||
//
|
||||
|
||||
#import "WRBookmark.h"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
static NSDictionary<NSString *, UIColor *> *sColorMap = nil;
|
||||
|
||||
@implementation WRBookmark
|
||||
|
||||
#pragma mark - Class Initialization
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sColorMap = @{
|
||||
@"yellow" : [UIColor colorWithRed:1.0 green:0.9 blue:0.3 alpha:0.4],
|
||||
@"blue" : [UIColor colorWithRed:0.3 green:0.6 blue:1.0 alpha:0.4],
|
||||
@"red" : [UIColor colorWithRed:1.0 green:0.3 blue:0.3 alpha:0.4],
|
||||
@"green" : [UIColor colorWithRed:0.3 green:0.9 blue:0.4 alpha:0.4],
|
||||
@"purple" : [UIColor colorWithRed:0.7 green:0.3 blue:0.9 alpha:0.4],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Factory Methods
|
||||
|
||||
+ (instancetype)bookmarkWithBookId:(NSString *)bookId
|
||||
chapterUid:(NSString *)chapterUid
|
||||
offset:(NSInteger)offset
|
||||
text:(NSString *)text
|
||||
type:(WRBookmarkType)type
|
||||
{
|
||||
WRBookmark *bm = [[WRBookmark alloc] init];
|
||||
bm.bookId = bookId;
|
||||
bm.chapterUid = chapterUid;
|
||||
bm.chapterOffset = offset;
|
||||
bm.markText = text;
|
||||
bm.type = type;
|
||||
bm.createTime = [[NSDate date] timeIntervalSince1970];
|
||||
bm.updateTime = bm.createTime;
|
||||
bm.isSynced = NO;
|
||||
|
||||
// Generate a unique bookmark ID
|
||||
bm.bookmarkId = [[NSUUID UUID] UUIDString];
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
||||
+ (instancetype)highlightWithBookId:(NSString *)bookId
|
||||
chapterUid:(NSString *)chapterUid
|
||||
startPos:(NSInteger)startPos
|
||||
endPos:(NSInteger)endPos
|
||||
text:(NSString *)text
|
||||
colorStyle:(NSString *)colorStyle
|
||||
{
|
||||
WRBookmark *bm = [self bookmarkWithBookId:bookId
|
||||
chapterUid:chapterUid
|
||||
offset:startPos
|
||||
text:text
|
||||
type:WRBookmarkTypeHighlight];
|
||||
bm.startPos = startPos;
|
||||
bm.endPos = endPos;
|
||||
bm.rangeLength = endPos - startPos;
|
||||
bm.colorStyle = colorStyle ?: @"yellow";
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
||||
#pragma mark - Serialization
|
||||
|
||||
- (NSDictionary *)toDictionary
|
||||
{
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
||||
|
||||
if (_bookmarkId) dict[@"bookmarkId"] = _bookmarkId;
|
||||
if (_bookId) dict[@"bookId"] = _bookId;
|
||||
if (_chapterUid) dict[@"chapterUid"] = _chapterUid;
|
||||
dict[@"chapterOffset"] = @(_chapterOffset);
|
||||
dict[@"chapterIndex"] = @(_chapterIndex);
|
||||
if (_markText) dict[@"markText"] = _markText;
|
||||
if (_noteContent) dict[@"noteContent"] = _noteContent;
|
||||
dict[@"type"] = @(_type);
|
||||
if (_colorStyle) dict[@"colorStyle"] = _colorStyle;
|
||||
dict[@"startPos"] = @(_startPos);
|
||||
dict[@"endPos"] = @(_endPos);
|
||||
dict[@"rangeLength"] = @(_rangeLength);
|
||||
if (_anchorId) dict[@"anchorId"] = _anchorId;
|
||||
if (_rangeKey) dict[@"rangeKey"] = _rangeKey;
|
||||
dict[@"createTime"] = @(_createTime);
|
||||
dict[@"updateTime"] = @(_updateTime);
|
||||
dict[@"isSynced"] = @(_isSynced);
|
||||
if (_syncKey) dict[@"syncKey"] = _syncKey;
|
||||
if (_rangeInfo) dict[@"rangeInfo"] = _rangeInfo;
|
||||
if (_extraMetadata) dict[@"extraMetadata"] = _extraMetadata;
|
||||
|
||||
if (_tags.count > 0) {
|
||||
dict[@"tags"] = [_tags allObjects];
|
||||
}
|
||||
|
||||
return [dict copy];
|
||||
}
|
||||
|
||||
+ (nullable instancetype)fromDictionary:(NSDictionary *)dict
|
||||
{
|
||||
if (!dict) return nil;
|
||||
|
||||
WRBookmark *bm = [[WRBookmark alloc] init];
|
||||
|
||||
bm.bookmarkId = dict[@"bookmarkId"];
|
||||
bm.bookId = dict[@"bookId"];
|
||||
bm.chapterUid = dict[@"chapterUid"];
|
||||
bm.chapterOffset = [dict[@"chapterOffset"] integerValue];
|
||||
bm.chapterIndex = [dict[@"chapterIndex"] integerValue];
|
||||
bm.markText = dict[@"markText"];
|
||||
bm.noteContent = dict[@"noteContent"];
|
||||
bm.type = [dict[@"type"] integerValue];
|
||||
bm.colorStyle = dict[@"colorStyle"];
|
||||
bm.startPos = [dict[@"startPos"] integerValue];
|
||||
bm.endPos = [dict[@"endPos"] integerValue];
|
||||
bm.rangeLength = [dict[@"rangeLength"] integerValue];
|
||||
bm.anchorId = dict[@"anchorId"];
|
||||
bm.rangeKey = dict[@"rangeKey"];
|
||||
bm.createTime = [dict[@"createTime"] doubleValue];
|
||||
bm.updateTime = [dict[@"updateTime"] doubleValue];
|
||||
bm.isSynced = [dict[@"isSynced"] boolValue];
|
||||
bm.syncKey = dict[@"syncKey"];
|
||||
bm.rangeInfo = dict[@"rangeInfo"];
|
||||
bm.extraMetadata = dict[@"extraMetadata"];
|
||||
|
||||
NSArray *tags = dict[@"tags"];
|
||||
if (tags) {
|
||||
bm.tags = [NSMutableSet setWithArray:tags];
|
||||
}
|
||||
|
||||
return bm;
|
||||
}
|
||||
|
||||
- (nullable NSData *)toJSONData
|
||||
{
|
||||
NSDictionary *dict = [self toDictionary];
|
||||
return [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
|
||||
}
|
||||
|
||||
+ (nullable instancetype)fromJSONData:(NSData *)data
|
||||
{
|
||||
if (!data) return nil;
|
||||
|
||||
NSError *error = nil;
|
||||
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data
|
||||
options:0
|
||||
error:&error];
|
||||
if (!dict || ![dict isKindOfClass:[NSDictionary class]]) return nil;
|
||||
|
||||
return [self fromDictionary:dict];
|
||||
}
|
||||
|
||||
#pragma mark - Display
|
||||
|
||||
- (NSString *)displaySummary
|
||||
{
|
||||
switch (_type) {
|
||||
case WRBookmarkTypeHighlight:
|
||||
return [NSString stringWithFormat:@"[Highlight] %@", _markText ?: @""];
|
||||
case WRBookmarkTypeUnderline:
|
||||
return [NSString stringWithFormat:@"[Underline] %@", _markText ?: @""];
|
||||
case WRBookmarkTypeMark:
|
||||
return [NSString stringWithFormat:@"[Bookmark] Chapter %@", _chapterUid ?: @""];
|
||||
case WRBookmarkTypeNote:
|
||||
return [NSString stringWithFormat:@"[Note] %@", _noteContent ?: _markText ?: @""];
|
||||
case WRBookmarkTypePencil:
|
||||
return @"[Pencil Note]";
|
||||
default:
|
||||
return _markText ?: @"";
|
||||
}
|
||||
}
|
||||
|
||||
- (UIColor *)highlightUIColor
|
||||
{
|
||||
if (!_colorStyle) {
|
||||
return sColorMap[@"yellow"] ?: [UIColor yellowColor];
|
||||
}
|
||||
return sColorMap[_colorStyle] ?: [UIColor yellowColor];
|
||||
}
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<WRBookmark: %@ type=%ld book=%@ ch=%@ '%@'>",
|
||||
_bookmarkId, (long)_type, _bookId, _chapterUid,
|
||||
[_markText substringToIndex:MIN(30, _markText.length)]];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object
|
||||
{
|
||||
if (self == object) return YES;
|
||||
if (![object isKindOfClass:[WRBookmark class]]) return NO;
|
||||
|
||||
WRBookmark *other = (WRBookmark *)object;
|
||||
return [self.bookmarkId isEqualToString:other.bookmarkId];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return self.bookmarkId.hash;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user