From 899e9eccd459ad7ec03c2f79631c7abe45b4dd03 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Mon, 6 Feb 2012 19:40:36 +0000 Subject: [PATCH 01/17] Clean up dealloc There's no need to test for nil Objective-C objects. --- epub/JTPepub.m | 50 +++++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/epub/JTPepub.m b/epub/JTPepub.m index 17c7350..aedb872 100644 --- a/epub/JTPepub.m +++ b/epub/JTPepub.m @@ -258,6 +258,7 @@ - (NSArray *)contributorsWithOPFRole:(NSString *)role } return results; } + - (NSArray *)editors { // If editors has been set, return it. @@ -267,6 +268,7 @@ - (NSArray *)editors editors = [[self contributorsWithOPFRole:@"edt"] retain]; return editors; } + - (NSArray *)illustrators { // If illustrators has been set, return it. @@ -276,6 +278,7 @@ - (NSArray *)illustrators illustrators = [[self contributorsWithOPFRole:@"ill"] retain]; return illustrators; } + - (NSArray *)translators { // If translators has been set, return it. @@ -285,6 +288,7 @@ - (NSArray *)translators translators = [[self contributorsWithOPFRole:@"trl"] retain]; return translators; } + - (NSArray *)creators { // If creators has been set, return it. @@ -397,6 +401,7 @@ - (NSImage *)cover return cover; } + - (NSString *)synopsis { // If the synopsis has been set, return it. @@ -425,6 +430,7 @@ - (NSString *)synopsis return synopsis; } + - (NSDate *)publicationDate { if (publicationDate) { @@ -456,6 +462,7 @@ - (NSDate *)publicationDate return publicationDate; } + - (NSString *)isbn { // If the ISBN has been set, return it. @@ -492,6 +499,7 @@ - (NSString *)isbn ISBN = @""; return ISBN; } + - (NSString *)drm { // If the DRM scheme has been set, return it. @@ -544,42 +552,22 @@ - (NSString *)drm } - (void)dealloc { - if (epubFile) { - [epubFile release]; - } - if (title) { - [title release]; - } - if (publisher) { - [publisher release]; - } + [epubFile release]; + [title release]; + [publisher release]; [authors release]; - if (creators) { - [creators release]; - } + [creators release]; [editors release]; [illustrators release]; [translators release]; - if (opfXML) { - [opfXML release]; - } - if (cover) { - [cover release]; - } - if (synopsis) { - [synopsis release]; - } - if (ISBN) { - [ISBN release]; - } + [opfXML release]; + [cover release]; + [synopsis release]; + [ISBN release]; [drm release]; - if (rootFilePath) { - [rootFilePath release]; - } - if (publicationDate) { - [publicationDate release]; - } - + [rootFilePath release]; + [publicationDate release]; + [super dealloc]; } From 9dcf42f7dfe4838235f8b2371270cccc4b2a5e41 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Mon, 6 Feb 2012 19:57:06 +0000 Subject: [PATCH 02/17] Fix comment typos --- epub/JTPepub.m | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/epub/JTPepub.m b/epub/JTPepub.m index aedb872..b38fd6d 100644 --- a/epub/JTPepub.m +++ b/epub/JTPepub.m @@ -83,7 +83,7 @@ - (BOOL)openEPUBFile:(NSString*)fileName { NSString *rootFileType = [[[rootFile objectAtIndex:0] attributeForName:@"media-type"] stringValue]; - // This code is all designed arround oebps+xml epubs, DTBook is unsuported. + // This code is all designed arround oebps+xml epubs, DTBook is unsupported. if([rootFileType caseInsensitiveCompare:@"application/oebps-package+xml"] == NSOrderedSame) { rootFilePath = [[[[rootFile objectAtIndex:0] attributeForName:@"full-path"] stringValue] retain]; }else{ @@ -135,7 +135,7 @@ - (NSString *)title // Check the array isn't empty. if ([metaElements count] == 0) { - // No title found + // No s found title = @""; return title; } @@ -165,7 +165,7 @@ - (NSString *)publisher // Check the array isn't empty. if ([metaElements count] == 0) { - // No publisher found + // No s found publisher = @""; return publisher; } @@ -181,14 +181,14 @@ - (NSArray *)creatorsWithOPFRole:(NSString *)role { NSError *xmlError = nil; - // scan for a element + // scan for a element NSArray *metaElements = [opfXML nodesForXPath:@"//dc:creator" namespaces:xmlns error:&xmlError]; // Check the array isn't empty. if ([metaElements count] == 0) { - // No dc:contributor found + // No s found return [NSArray array]; } NSMutableArray *results = [NSMutableArray array]; @@ -234,7 +234,7 @@ - (NSArray *)contributorsWithOPFRole:(NSString *)role // Check the array isn't empty. if ([metaElements count] == 0) { - // No dc:contributor found + // No s found return [NSArray array]; } NSMutableArray *results = [NSMutableArray array]; @@ -308,7 +308,7 @@ - (NSArray *)creators // Check the array isn't empty. if ([metaElements count] == 0) { - // No title found return an empty array + // No s found return an empty array creators = [[NSArray alloc] initWithObjects:@"", nil]; [creators retain]; @@ -419,7 +419,7 @@ - (NSString *)synopsis // Check the array isn't empty. if ([metaElements count] == 0) { - // No title found + // No s found synopsis = @""; return synopsis; } @@ -447,7 +447,7 @@ - (NSDate *)publicationDate // Check the array isn't empty. if ([metaElements count] == 0) { - // No date found + // No s found return nil; } // Find the date of publication. @@ -474,7 +474,7 @@ - (NSString *)isbn // Otherwise load it. NSError *xmlError = nil; - // scan for a element + // scan for a element NSArray *metaElements = [opfXML nodesForXPath:@"//dc:identifier" namespaces:xmlns error:&xmlError]; From d8950871b404bec8a9be48d3780364509d0fac30 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Mon, 6 Feb 2012 21:15:39 +0000 Subject: [PATCH 03/17] Detect B&N DRM Barnes & Noble use a variant of Adept DRM. One obvious difference seems to be that contents of contains a barnesandnoble.com URL. This change switches from looking at to and checking the URL contents. The adeptXML object cannot be released if we want to look inside a returned node, so we now release that later on. The existing tests still pass. --- epub/JTPepub.m | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/epub/JTPepub.m b/epub/JTPepub.m index b38fd6d..e8fa481 100644 --- a/epub/JTPepub.m +++ b/epub/JTPepub.m @@ -506,20 +506,28 @@ - (NSString *)drm if (drm) { return drm; } - // Adobe Adept DRM has "META-INF/rights.xml", containing with an adobe.com URL. + // Adobe Adept DRM has "META-INF/rights.xml", containing . + // B&N have an with "barnesandnoble.com" somewhere inside. + // Adobe uses a variety of other URLs. if ([epubFile testForNamedFile:@"META-INF/rights.xml"]) { NSData *adept = [epubFile dataForNamedFile:@"META-INF/rights.xml"]; NSError *xmlError; GDataXMLDocument *adeptXML = [[GDataXMLDocument alloc] initWithData:adept options:0 error:&xmlError]; - NSArray *urls = [adeptXML nodesForXPath:@"//adept:licenseURL" + NSArray *urls = [adeptXML nodesForXPath:@"//adept:operatorURL" namespaces:xmlns error:&xmlError]; - [adeptXML release]; if ([urls count] > 0) { - // should probably check for adobe.com here... - drm = @"Adobe"; + NSString *url = [[urls lastObject] stringValue]; + NSRange match = [url rangeOfString:@"barnesandnoble" options:NSCaseInsensitiveSearch]; + if (match.location != NSNotFound) { + drm = @"Barnes & Noble"; + } else { + drm = @"Adobe"; + } + [adeptXML release]; return drm; } + [adeptXML release]; } // Apple Fairplay DRM has "META-INF/sinf.xml" containing . if ([epubFile testForNamedFile:@"META-INF/sinf.xml"]) { From a610b83e8dd7bb262ac3c018a07a4a3ec126e48d Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sat, 11 Feb 2012 20:55:22 +0000 Subject: [PATCH 04/17] Test for contributors without roles The test file has Foo which should not match any translator/illustrator/editor. --- EpubTests/EpubTests.h | 1 + EpubTests/EpubTests.m | 9 +++++++++ EpubTests/badcontributor.epub | Bin 0 -> 1816 bytes epub.xcodeproj/project.pbxproj | 4 ++++ 4 files changed, 14 insertions(+) create mode 100644 EpubTests/badcontributor.epub diff --git a/EpubTests/EpubTests.h b/EpubTests/EpubTests.h index 35b5933..c9c3c74 100644 --- a/EpubTests/EpubTests.h +++ b/EpubTests/EpubTests.h @@ -13,6 +13,7 @@ @interface EpubTests : SenTestCase { JTPepub *untitledFile; JTPepub *metadataFile; + JTPepub *badcontributorFile; JTPepub *adeptFile; JTPepub *fairplayFile; JTPepub *koboFile; diff --git a/EpubTests/EpubTests.m b/EpubTests/EpubTests.m index 9c07f95..f7136fb 100644 --- a/EpubTests/EpubTests.m +++ b/EpubTests/EpubTests.m @@ -18,11 +18,13 @@ - (void)setUp NSBundle *thisBundle = [NSBundle bundleForClass:[self class]]; NSString *untitled = [thisBundle pathForResource:@"Untitled" ofType:@"epub"]; NSString *metadata = [thisBundle pathForResource:@"metadata" ofType:@"epub"]; + NSString *badcontributor = [thisBundle pathForResource:@"badcontributor" ofType:@"epub"]; NSString *adept = [thisBundle pathForResource:@"fake-adept" ofType:@"epub"]; NSString *fairplay = [thisBundle pathForResource:@"fake-fairplay" ofType:@"epub"]; NSString *kobo = [thisBundle pathForResource:@"fake-kobo" ofType:@"epub"]; untitledFile = [[JTPepub alloc] initWithFile:untitled]; metadataFile = [[JTPepub alloc] initWithFile:metadata]; + badcontributorFile = [[JTPepub alloc] initWithFile:badcontributor]; adeptFile = [[JTPepub alloc] initWithFile:adept]; fairplayFile = [[JTPepub alloc] initWithFile:fairplay]; koboFile = [[JTPepub alloc] initWithFile:kobo]; @@ -33,6 +35,7 @@ - (void)tearDown [koboFile release]; [fairplayFile release]; [adeptFile release]; + [badcontributorFile release]; [metadataFile release]; [untitledFile release]; @@ -105,6 +108,12 @@ - (void)testThreeAuthors STAssertEqualObjects([actual objectAtIndex:2], expected2, @"Third author should be %@ but is %@", [actual objectAtIndex:2], expected2); } +- (void)testBadContributor +{ + NSArray *actual = [badcontributorFile translators]; + STAssertTrue([actual count] == 0, @"No translators expected"); +} + -(void)testNoDRM { NSString *actual = [untitledFile drm]; diff --git a/EpubTests/badcontributor.epub b/EpubTests/badcontributor.epub new file mode 100644 index 0000000000000000000000000000000000000000..a2d0fdfd90dbc9a71910b6bd4b0b8bf5631f011c GIT binary patch literal 1816 zcmWIWW@Zs#0D&D#y&dusSL=XyAj|>8xtY1CC6xuKi3J5YnaPPInfZD8sRgA;+Etka z0Z^SBV4dn8me1M()WOWiz#s;seO*Hwbv^yu^po@RN)j{kQj7E|a&uz)4rU!T5O8__ zk89)gB<;PTiZ=y3)@g3}%Mcp3Tj|oe4NoThsSOqPxxM$SQGVK&f^Nr}N0V$PHy(e- zz;yECi%hc*->UNFC%lk;JEQAs%W3=krJB+a;z2o^&v)eW?1|8i6oYOScWPSP;o6Dx|$=btUjcCAk~H&ODQ)Lc+|cINhH%iB+6Y{f^?r91CJB@@p7KYaG9`4!qOfk@I z)wDC-|9<`Y(i~p5Vdrq#0`jW5jV&44Ji+}4@FFqXZvF+qt!`t%PT7M+e z9$k2lZVymKH~GeNbKQjFp-VSxy5G2cmF|~FwTL-7 z=j5a(iN?yj5y*3VXsL5MJo89$cO&nzl>43>7t3e&oO-n9=+U1s=t{_A|}Bl#ImEA13D z&qyn8_0YAsbwlc4;jEU&C5kbXwfj`80?o}DKK+xHl5}XAw9B^Hton6N+uZVv+h0l4 z*1ryUA!kvxI73J!ah1pu{s3=ACJ|=br8qF0z+g!uh=P^r2wm7qaEKlTh9!+wKqmI` z9Ni4`(h*^X5-`Fr%1U(Y=vfY-od;OlLbO9OAi7rcgn`hykOig{dol^|W@Q7ZW(7h! Kpev8Cf_MPNn(!(B literal 0 HcmV?d00001 diff --git a/epub.xcodeproj/project.pbxproj b/epub.xcodeproj/project.pbxproj index 2c3d9ae..13a9368 100644 --- a/epub.xcodeproj/project.pbxproj +++ b/epub.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 674D09CC14CF297E00127A36 /* NSArray+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 674D09CA14CF297E00127A36 /* NSArray+HTML.m */; }; 674D09D014CF2C6800127A36 /* NSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 674D09CE14CF2C6800127A36 /* NSString+HTML.h */; }; 674D09D114CF2C6800127A36 /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 674D09CF14CF2C6800127A36 /* NSString+HTML.m */; }; + 67543ADF14E70B1600A42752 /* badcontributor.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67543ADE14E70B1600A42752 /* badcontributor.epub */; }; 6755959714DDB5D000D04B6D /* metadata.epub in Resources */ = {isa = PBXBuildFile; fileRef = 6755959614DDB5D000D04B6D /* metadata.epub */; }; 675FAB3D14D9B8760065603A /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 675FAB3C14D9B8760065603A /* SenTestingKit.framework */; }; 675FAB3F14D9B8760065603A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 675FAB3E14D9B8760065603A /* Cocoa.framework */; }; @@ -68,6 +69,7 @@ 674D09CA14CF297E00127A36 /* NSArray+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSArray+HTML.m"; sourceTree = ""; }; 674D09CE14CF2C6800127A36 /* NSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+HTML.h"; sourceTree = ""; }; 674D09CF14CF2C6800127A36 /* NSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+HTML.m"; sourceTree = ""; }; + 67543ADE14E70B1600A42752 /* badcontributor.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = badcontributor.epub; sourceTree = ""; }; 6755959614DDB5D000D04B6D /* metadata.epub */ = {isa = PBXFileReference; lastKnownFileType = file; name = metadata.epub; path = ../epub/metadata.epub; sourceTree = ""; }; 675FAB3B14D9B8760065603A /* EpubTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EpubTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 675FAB3C14D9B8760065603A /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; @@ -180,6 +182,7 @@ 67BE43E914DEC832003C7DFC /* Test Files */ = { isa = PBXGroup; children = ( + 67543ADE14E70B1600A42752 /* badcontributor.epub */, D7838884135499ED00E7937B /* Untitled.epub */, 6755959614DDB5D000D04B6D /* metadata.epub */, 67BE43EB14DEC886003C7DFC /* fake-adept.epub */, @@ -391,6 +394,7 @@ 67BE43EE14DEC886003C7DFC /* fake-adept.epub in Resources */, 67BE43EF14DEC886003C7DFC /* fake-fairplay.epub in Resources */, 67BE43F014DEC886003C7DFC /* fake-kobo.epub in Resources */, + 67543ADF14E70B1600A42752 /* badcontributor.epub in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; From d3955409dd695067d0529024d2e35a5ecc728b76 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sat, 11 Feb 2012 20:53:58 +0000 Subject: [PATCH 05/17] Use the role in the path queries directly Instead of requesting all nodes and then iterating looking for some role, query the role attribute explicitly. This avoids problems matching elements without roles set. --- epub/JTPepub.m | 54 ++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/epub/JTPepub.m b/epub/JTPepub.m index e8fa481..bb84863 100644 --- a/epub/JTPepub.m +++ b/epub/JTPepub.m @@ -182,7 +182,8 @@ - (NSArray *)creatorsWithOPFRole:(NSString *)role NSError *xmlError = nil; // scan for a element - NSArray *metaElements = [opfXML nodesForXPath:@"//dc:creator" + NSString *query = [NSString stringWithFormat:@"//dc:creator[@opf:role='%@']", role]; + NSArray *metaElements = [opfXML nodesForXPath:query namespaces:xmlns error:&xmlError]; @@ -195,19 +196,15 @@ - (NSArray *)creatorsWithOPFRole:(NSString *)role // Fast enumerate over meta elements for(id item in metaElements) { - NSString *itemID = [[item attributeForName:@"role"] stringValue]; - - if([itemID caseInsensitiveCompare:role] == NSOrderedSame) { - // The name should be in the item contents. - // If the element contents is empty, look in the file-as attribute - // instead. If that's not there either, skip this item. - if ([[item stringValue] isEqualToString:@""]) { - NSString *fileAs = [[item attributeForName:@"file-as"] stringValue]; - if (![fileAs isEqualToString:@""]) - [results addObject:fileAs]; - } else { - [results addObject:[item stringValue]]; - } + // The name should be in the item contents. + // If the element contents is empty, look in the file-as attribute + // instead. If that's not there either, skip this item. + if ([[item stringValue] isEqualToString:@""]) { + NSString *fileAs = [[item attributeForName:@"file-as"] stringValue]; + if (![fileAs isEqualToString:@""]) + [results addObject:fileAs]; + } else { + [results addObject:[item stringValue]]; } } return results; @@ -228,10 +225,11 @@ - (NSArray *)contributorsWithOPFRole:(NSString *)role NSError *xmlError = nil; // scan for a element - NSArray *metaElements = [opfXML nodesForXPath:@"//dc:contributor" + NSString *query = [NSString stringWithFormat:@"//dc:contributor[@opf:role='%@']", role]; + NSArray *metaElements = [opfXML nodesForXPath:query namespaces:xmlns error:&xmlError]; - + // Check the array isn't empty. if ([metaElements count] == 0) { // No s found @@ -239,21 +237,17 @@ - (NSArray *)contributorsWithOPFRole:(NSString *)role } NSMutableArray *results = [NSMutableArray array]; // Fast enumerate over meta elements - for(id item in metaElements) + for (id item in metaElements) { - NSString *itemID = [[item attributeForName:@"role"] stringValue]; - - if([itemID caseInsensitiveCompare:role] == NSOrderedSame) { - // The name should be in the item contents. - // If the element contents is empty, look in the file-as attribute - // instead. If that's not there either, skip this item. - if ([[item stringValue] isEqualToString:@""]) { - NSString *fileAs = [[item attributeForName:@"file-as"] stringValue]; - if (![fileAs isEqualToString:@""]) - [results addObject:fileAs]; - } else { - [results addObject:[item stringValue]]; - } + // The name should be in the item contents. + // If the element contents is empty, look in the file-as attribute + // instead. If that's not there either, skip this item. + if ([[item stringValue] isEqualToString:@""]) { + NSString *fileAs = [[item attributeForName:@"file-as"] stringValue]; + if (![fileAs isEqualToString:@""]) + [results addObject:fileAs]; + } else { + [results addObject:[item stringValue]]; } } return results; From 31bb6cab592e46afbfdc8fd4112617d1f598980a Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sat, 11 Feb 2012 21:34:16 +0000 Subject: [PATCH 06/17] Use bare element Creators may not have opt:role='aut'. (I have some books like this) --- EpubTests/badcontributor.epub | Bin 1816 -> 1819 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/EpubTests/badcontributor.epub b/EpubTests/badcontributor.epub index a2d0fdfd90dbc9a71910b6bd4b0b8bf5631f011c..dd3551182328aff661b973c7a936598b16a3e637 100644 GIT binary patch delta 1043 zcmbQiH=BhE&v=e{`We?R}VzdPx!Z>;yf0|r`) zi>2BHRTs2Q^tx_Yx{WJOQ&RW(r0s6be)kiXlpoc5y|dz}sesM8<8#ceebY>S{@F5Q z(|RZ4D>|KvgpF%mJ1@UF>$pHAW3^U2tFxbcPi5_tlFfb>cHI!1RGP5!$u#{JAzj>i z%bj1J3^K@A=zQzMBZIV(tBY>dFFM2bLeX_stD;w4Wx$laMUEAI@o{qXmk;q;mHM5W zGQE05UZ1Cc`~n`n<0_na{ne6l>YTRp=bd^|aV!0eaMk9`r?v&y6nM5=EDKn=Iqk!% zrS(OEU5pmfy3!?dcb0F9OqKsVyWnxfyY>2aUjKR;aJuwuK^|*{{;oCRNAFb23+PX{ ztYOo_sXFWY*@~d8yZ&fSF|gPEX1FG`C}uTR@_K{Rh013cEQGUNRvdMyUH#XcJ?P_s zecwBt)!x@SsM~Z?#3OpqBX+-%(w{w5Z6`OW3v9Nk?^}#Xg~Jc9WC7i^F1;0~oX34$nV4EiVPe<{~Br z23}xn`@1>?1nZaNC+p=USM*Lg>vz~dpyjqpy2^MwmT{3c{LIyG$zy3FjZZkFq^ zseaugUS1npzsp>FGF6|?n{T#`JN?U6S{8Xnl+Scfd^|o6!du+~2d}k0l|628c&0KkXCdFg-I35%pl)Nwa zb~!`);e*##W$deo&VOjVXSU0Q;s-4E6h4?{&O34MV0~%3Z%wDzkNlp4ZR+7g=~MjH zNhEJFS`v{rktgWPZK>tC(Jxk>a{6r7$CO`^Z1i4@iz#2I_15ma#!4cObvp8#&jp0) z?OvAi%0%X0u}HmU?zb(w881ZBz@rytB!=W&Z@5)&$?V$8Ab^*7*A6xjA)Ay#IEER=dFSL(9Kjh_b)-gdI7G zHm_w-W&~!x$(LA7Kokd?6IwP5XETz=7M0-K3nZ2_vP@pVCZhmSvpTYW`%9ph6)?(W fU<$x$mat6T%OWQh;LXYg5@!WMJD{TDtRNl${FlQB delta 1005 zcmbQuH-nESz?+#xgaHJ0ES<<>!2~4Tp7HKj>g`a>CKa`tk%6INvL&NT{cHn)w)b^h zz3XHJ5{~;xDqP+&ea>W?1|8i6oYOScWPSP;o6Dx|$=btUj zcCAk~H&ODQ)Lc+|cINhH%iB+6Y{9i-v566`qCU;w_)dZ zx96XBwEoJQ@A{Ij?_%Ek)Qf-XRxdsr?y>FUUBlb*+gg7l)E-@UkZ+ay%9%xC?8RSR zv@O_I9}>1u${|RTMKZ?SaEW`|-UA(PW*;wm8I@vQ8-Bat=Rfgh+xD52UNW0^EuxWk z(bvC=W`qlVx@4p3|0s4&VcOFgv-8UgU-uuL$+23vzN|cQ$(CiyZgkIdwAfh25G80O z8g2Tq;M2p0EVoW@-2Yv9Z{Pc#Ngl_Nx=iFeKemeP39ze=+Iyhok>r&%ev?#smy4yp zeDmkz^ZKG^pLko`c6!G5?A>@krK{_3~23(`zBK@55r40~o{J z4!U1ClCprYI-7}sffpF-{;o~|!TKfn$$ELo6}>_6`Ar4__1}X_YTH*Yc)^olbocNi zU*8n|`f8moYAF|_Y}DReUf2v!_Gcx_8K(Kq7CoR8H;2zgvCU8ZLE*{j9>aNp zZ`Byu4<8IQx-TCaocr+fetGr_P6vzT^gY%Jy7Oy`mf6Jmd+z@gnfJTBFNx~qSvqUm zzr;?JnbTC=Vgij;79WkwGFhm$J7%f+R$1SaM~{CSe3Eo})*9#+2W@wS8_KGjhCa z-oT>F2+Vtvud Date: Sat, 11 Feb 2012 21:36:09 +0000 Subject: [PATCH 07/17] Check for without opf:role attribute --- EpubTests/EpubTests.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/EpubTests/EpubTests.m b/EpubTests/EpubTests.m index f7136fb..963a5eb 100644 --- a/EpubTests/EpubTests.m +++ b/EpubTests/EpubTests.m @@ -114,6 +114,12 @@ - (void)testBadContributor STAssertTrue([actual count] == 0, @"No translators expected"); } +- (void)testBadAuthor +{ + NSArray *actual = [badcontributorFile authors]; + STAssertTrue([actual count] == 1, @"1 author expected"); +} + -(void)testNoDRM { NSString *actual = [untitledFile drm]; From d7092a1dbde2d39eb65e96247d544c69a1d4adcf Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sat, 11 Feb 2012 21:39:52 +0000 Subject: [PATCH 08/17] Revert the change for finding creators The opf:role attribute is optional, and I've seen books without it on dc:creator. This change restores the previous code which matches those role-less creators. It does that in a surprising way - itemID is nil for those elements, and it turns out the caseInsensitiveCompare: method returns NSOrderedSame for that. --- epub/JTPepub.m | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/epub/JTPepub.m b/epub/JTPepub.m index bb84863..10d78e4 100644 --- a/epub/JTPepub.m +++ b/epub/JTPepub.m @@ -182,8 +182,7 @@ - (NSArray *)creatorsWithOPFRole:(NSString *)role NSError *xmlError = nil; // scan for a element - NSString *query = [NSString stringWithFormat:@"//dc:creator[@opf:role='%@']", role]; - NSArray *metaElements = [opfXML nodesForXPath:query + NSArray *metaElements = [opfXML nodesForXPath:@"//dc:creator" namespaces:xmlns error:&xmlError]; @@ -196,15 +195,19 @@ - (NSArray *)creatorsWithOPFRole:(NSString *)role // Fast enumerate over meta elements for(id item in metaElements) { - // The name should be in the item contents. - // If the element contents is empty, look in the file-as attribute - // instead. If that's not there either, skip this item. - if ([[item stringValue] isEqualToString:@""]) { - NSString *fileAs = [[item attributeForName:@"file-as"] stringValue]; - if (![fileAs isEqualToString:@""]) - [results addObject:fileAs]; - } else { - [results addObject:[item stringValue]]; + NSString *itemID = [[item attributeForName:@"role"] stringValue]; + + if ([itemID caseInsensitiveCompare:role] == NSOrderedSame) { + // The name should be in the item contents. + // If the element contents is empty, look in the file-as attribute + // instead. If that's not there either, skip this item. + if ([[item stringValue] isEqualToString:@""]) { + NSString *fileAs = [[item attributeForName:@"file-as"] stringValue]; + if (![fileAs isEqualToString:@""]) + [results addObject:fileAs]; + } else { + [results addObject:[item stringValue]]; + } } } return results; From 02d2fc146bdffa95766fb9b845050e20542bacaa Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sat, 11 Feb 2012 21:43:25 +0000 Subject: [PATCH 09/17] Remove the odd (null) resource This seemed to appear as a blank line in the list of resources for EpubTests. Without it, everything still builds OK and tests OK. --- epub.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/epub.xcodeproj/project.pbxproj b/epub.xcodeproj/project.pbxproj index 13a9368..bb120b1 100644 --- a/epub.xcodeproj/project.pbxproj +++ b/epub.xcodeproj/project.pbxproj @@ -15,7 +15,6 @@ 6755959714DDB5D000D04B6D /* metadata.epub in Resources */ = {isa = PBXBuildFile; fileRef = 6755959614DDB5D000D04B6D /* metadata.epub */; }; 675FAB3D14D9B8760065603A /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 675FAB3C14D9B8760065603A /* SenTestingKit.framework */; }; 675FAB3F14D9B8760065603A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 675FAB3E14D9B8760065603A /* Cocoa.framework */; }; - 675FAB4914D9B8760065603A /* (null) in Resources */ = {isa = PBXBuildFile; }; 675FAB4C14D9B8760065603A /* EpubTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 675FAB4B14D9B8760065603A /* EpubTests.m */; }; 675FAB5214D9BD5D0065603A /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D76675C514CECDFF003850E7 /* libxml2.dylib */; }; 675FAB5314D9BD6D0065603A /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D727FC8313D771B200CB5355 /* libz.dylib */; }; @@ -388,7 +387,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 675FAB4914D9B8760065603A /* (null) in Resources */, 67D3E1DC14D9C8160064FDDF /* Untitled.epub in Resources */, 6755959714DDB5D000D04B6D /* metadata.epub in Resources */, 67BE43EE14DEC886003C7DFC /* fake-adept.epub in Resources */, From b7898cdfaa8d9b94e826fd57211d41a46e2b6d30 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sat, 11 Feb 2012 21:46:27 +0000 Subject: [PATCH 10/17] Move the original 2 test books into the EpubTests directory --- {epub => EpubTests}/Untitled.epub | Bin {epub => EpubTests}/metadata.epub | Bin epub.xcodeproj/project.pbxproj | 16 ++++++++-------- 3 files changed, 8 insertions(+), 8 deletions(-) rename {epub => EpubTests}/Untitled.epub (100%) rename {epub => EpubTests}/metadata.epub (100%) diff --git a/epub/Untitled.epub b/EpubTests/Untitled.epub similarity index 100% rename from epub/Untitled.epub rename to EpubTests/Untitled.epub diff --git a/epub/metadata.epub b/EpubTests/metadata.epub similarity index 100% rename from epub/metadata.epub rename to EpubTests/metadata.epub diff --git a/epub.xcodeproj/project.pbxproj b/epub.xcodeproj/project.pbxproj index bb120b1..08acbd6 100644 --- a/epub.xcodeproj/project.pbxproj +++ b/epub.xcodeproj/project.pbxproj @@ -12,7 +12,8 @@ 674D09D014CF2C6800127A36 /* NSString+HTML.h in Headers */ = {isa = PBXBuildFile; fileRef = 674D09CE14CF2C6800127A36 /* NSString+HTML.h */; }; 674D09D114CF2C6800127A36 /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 674D09CF14CF2C6800127A36 /* NSString+HTML.m */; }; 67543ADF14E70B1600A42752 /* badcontributor.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67543ADE14E70B1600A42752 /* badcontributor.epub */; }; - 6755959714DDB5D000D04B6D /* metadata.epub in Resources */ = {isa = PBXBuildFile; fileRef = 6755959614DDB5D000D04B6D /* metadata.epub */; }; + 675F07F014E7190700FEFD90 /* metadata.epub in Resources */ = {isa = PBXBuildFile; fileRef = 675F07EE14E7190700FEFD90 /* metadata.epub */; }; + 675F07F114E7190700FEFD90 /* Untitled.epub in Resources */ = {isa = PBXBuildFile; fileRef = 675F07EF14E7190700FEFD90 /* Untitled.epub */; }; 675FAB3D14D9B8760065603A /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 675FAB3C14D9B8760065603A /* SenTestingKit.framework */; }; 675FAB3F14D9B8760065603A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 675FAB3E14D9B8760065603A /* Cocoa.framework */; }; 675FAB4C14D9B8760065603A /* EpubTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 675FAB4B14D9B8760065603A /* EpubTests.m */; }; @@ -30,7 +31,6 @@ 67BE43EE14DEC886003C7DFC /* fake-adept.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67BE43EB14DEC886003C7DFC /* fake-adept.epub */; }; 67BE43EF14DEC886003C7DFC /* fake-fairplay.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67BE43EC14DEC886003C7DFC /* fake-fairplay.epub */; }; 67BE43F014DEC886003C7DFC /* fake-kobo.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67BE43ED14DEC886003C7DFC /* fake-kobo.epub */; }; - 67D3E1DC14D9C8160064FDDF /* Untitled.epub in Resources */ = {isa = PBXBuildFile; fileRef = D7838884135499ED00E7937B /* Untitled.epub */; }; D727FC8413D771B200CB5355 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D727FC8313D771B200CB5355 /* libz.dylib */; }; D749581E14DD800D0048D619 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D78388591354890700E7937B /* InfoPlist.strings */; }; D749582414DD82360048D619 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = D749582614DD82360048D619 /* Localizable.strings */; }; @@ -69,7 +69,8 @@ 674D09CE14CF2C6800127A36 /* NSString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+HTML.h"; sourceTree = ""; }; 674D09CF14CF2C6800127A36 /* NSString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+HTML.m"; sourceTree = ""; }; 67543ADE14E70B1600A42752 /* badcontributor.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = badcontributor.epub; sourceTree = ""; }; - 6755959614DDB5D000D04B6D /* metadata.epub */ = {isa = PBXFileReference; lastKnownFileType = file; name = metadata.epub; path = ../epub/metadata.epub; sourceTree = ""; }; + 675F07EE14E7190700FEFD90 /* metadata.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = metadata.epub; sourceTree = ""; }; + 675F07EF14E7190700FEFD90 /* Untitled.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = Untitled.epub; sourceTree = ""; }; 675FAB3B14D9B8760065603A /* EpubTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EpubTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 675FAB3C14D9B8760065603A /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; 675FAB3E14D9B8760065603A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; @@ -102,7 +103,6 @@ D78388731354891800E7937B /* ZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipArchive.h; sourceTree = ""; }; D78388741354891800E7937B /* ZipArchive.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ZipArchive.mm; sourceTree = ""; }; D783888213548ADA00E7937B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; - D7838884135499ED00E7937B /* Untitled.epub */ = {isa = PBXFileReference; lastKnownFileType = file; name = Untitled.epub; path = ../epub/Untitled.epub; sourceTree = ""; }; D78388921354AA7600E7937B /* crypt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crypt.h; sourceTree = ""; }; D78388931354AA7600E7937B /* ioapi.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ioapi.c; sourceTree = ""; }; D78388941354AA7600E7937B /* ioapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ioapi.h; sourceTree = ""; }; @@ -181,9 +181,9 @@ 67BE43E914DEC832003C7DFC /* Test Files */ = { isa = PBXGroup; children = ( + 675F07EF14E7190700FEFD90 /* Untitled.epub */, + 675F07EE14E7190700FEFD90 /* metadata.epub */, 67543ADE14E70B1600A42752 /* badcontributor.epub */, - D7838884135499ED00E7937B /* Untitled.epub */, - 6755959614DDB5D000D04B6D /* metadata.epub */, 67BE43EB14DEC886003C7DFC /* fake-adept.epub */, 67BE43EC14DEC886003C7DFC /* fake-fairplay.epub */, 67BE43ED14DEC886003C7DFC /* fake-kobo.epub */, @@ -387,12 +387,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 67D3E1DC14D9C8160064FDDF /* Untitled.epub in Resources */, - 6755959714DDB5D000D04B6D /* metadata.epub in Resources */, 67BE43EE14DEC886003C7DFC /* fake-adept.epub in Resources */, 67BE43EF14DEC886003C7DFC /* fake-fairplay.epub in Resources */, 67BE43F014DEC886003C7DFC /* fake-kobo.epub in Resources */, 67543ADF14E70B1600A42752 /* badcontributor.epub in Resources */, + 675F07F014E7190700FEFD90 /* metadata.epub in Resources */, + 675F07F114E7190700FEFD90 /* Untitled.epub in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 379f3f4ad246cd4a0674510cc60123286da290b7 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sun, 12 Feb 2012 08:28:20 +0000 Subject: [PATCH 11/17] Simply unit test output The failing assertions print a reasonable amount about the actual/expected values, so adding more in the description is superfluous. --- EpubTests/EpubTests.m | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/EpubTests/EpubTests.m b/EpubTests/EpubTests.m index 963a5eb..2270d78 100644 --- a/EpubTests/EpubTests.m +++ b/EpubTests/EpubTests.m @@ -46,14 +46,14 @@ - (void)testTitle { NSString *actual = [untitledFile title]; NSString *expected = @"Test Document"; - STAssertEqualObjects(actual, expected, @"Title should be %@ but is %@", actual, expected); + STAssertEqualObjects(actual, expected, @"title is wrong"); } - (void)testTitleWithAmpersand { NSString *actual = [metadataFile title]; NSString *expected = @"This & That"; - STAssertEqualObjects(actual, expected, @"Title should be %@ but is %@", actual, expected); + STAssertEqualObjects(actual, expected, @"title is wrong"); } - (void)testAuthors @@ -61,14 +61,14 @@ - (void)testAuthors NSArray *actual = [untitledFile authors]; NSString *expected = @"Test Author"; STAssertTrue([actual count] == 1, @"1 author expected"); - STAssertEqualObjects([actual lastObject], expected, @"Author should be %@ but is %@", [actual lastObject], expected); + STAssertEqualObjects([actual lastObject], expected, @"author is wrong"); } - (void)testISBN { NSString *actual = [untitledFile isbn]; NSString *expected = @"123456789"; - STAssertEqualObjects(actual, expected, @"ISBN should be %@ but is %@", actual, expected); + STAssertEqualObjects(actual, expected, @"ISBN is wrong"); } - (void)testDate @@ -77,7 +77,7 @@ - (void)testDate timeZone:nil locale:[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]]; NSString *expected = @"2011"; - STAssertEqualObjects(actual, expected, @"Date should be %@ but is %@", actual, expected); + STAssertEqualObjects(actual, expected, @"date is wrong"); } - (void)testTranslator @@ -85,7 +85,7 @@ - (void)testTranslator NSArray *actual = [untitledFile translators]; NSString *expected = @"Test Translator"; STAssertTrue([actual count] == 1, @"1 translator expected"); - STAssertEqualObjects([actual lastObject], expected, @"Translator should be %@ but is %@", [actual lastObject], expected); + STAssertEqualObjects([actual lastObject], expected, @"translator is wrong"); } - (void)testIllustrator @@ -93,7 +93,7 @@ - (void)testIllustrator NSArray *actual = [untitledFile illustrators]; NSString *expected = @"Test Illustrator"; STAssertTrue([actual count] == 1, @"1 illustrator expected"); - STAssertEqualObjects([actual lastObject], expected, @"Illustrator should be %@ but is %@", [actual lastObject], expected); + STAssertEqualObjects([actual lastObject], expected, @"illustrator is wrong"); } - (void)testThreeAuthors @@ -103,11 +103,12 @@ - (void)testThreeAuthors NSString *expected1 = @"Second Author"; NSString *expected2 = @"Third Author"; STAssertTrue([actual count] == 3, @"3 authors expected"); - STAssertEqualObjects([actual objectAtIndex:0], expected0, @"First author should be %@ but is %@", [actual objectAtIndex:0], expected0); - STAssertEqualObjects([actual objectAtIndex:1], expected1, @"Second author should be %@ but is %@", [actual objectAtIndex:1], expected1); - STAssertEqualObjects([actual objectAtIndex:2], expected2, @"Third author should be %@ but is %@", [actual objectAtIndex:2], expected2); + STAssertEqualObjects([actual objectAtIndex:0], expected0, @"First author is wrong"); + STAssertEqualObjects([actual objectAtIndex:1], expected1, @"Second author is wrong"); + STAssertEqualObjects([actual objectAtIndex:2], expected2, @"Third author is wrong"); } +#pragma mark Test lack of opf:role - (void)testBadContributor { NSArray *actual = [badcontributorFile translators]; @@ -120,28 +121,29 @@ - (void)testBadAuthor STAssertTrue([actual count] == 1, @"1 author expected"); } --(void)testNoDRM +#pragma mark Test DRM +- (void)testNoDRM { NSString *actual = [untitledFile drm]; NSString *expected = @""; STAssertEqualObjects(actual, expected, @"Untitled file has wrong DRM"); } --(void)testAdobeDRM +- (void)testAdobeDRM { NSString *actual = [adeptFile drm]; NSString *expected = @"Adobe"; STAssertEqualObjects(actual, expected, @"fake-adept file has wrong DRM"); } --(void)testAppleDRM +- (void)testAppleDRM { NSString *actual = [fairplayFile drm]; NSString *expected = @"Apple"; STAssertEqualObjects(actual, expected, @"fake-fairplay file has wrong DRM"); } --(void)testKoboDRM +- (void)testKoboDRM { NSString *actual = [koboFile drm]; NSString *expected = @"Kobo"; From a3d3ab5bdd2c0a0111c5c2247347331a0c0f8622 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sun, 12 Feb 2012 08:34:41 +0000 Subject: [PATCH 12/17] Add tests for present and absent covers We expect nil from -cover if there is no detectable cover. --- EpubTests/EpubTests.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/EpubTests/EpubTests.m b/EpubTests/EpubTests.m index 2270d78..b77ac6c 100644 --- a/EpubTests/EpubTests.m +++ b/EpubTests/EpubTests.m @@ -149,4 +149,17 @@ - (void)testKoboDRM NSString *expected = @"Kobo"; STAssertEqualObjects(actual, expected, @"fake-kobo file has wrong DRM"); } + +#pragma mark Test covers +- (void)testUntitledCover +{ + NSImage *actual = [untitledFile cover]; + STAssertNotNil(actual, @"Cover not found"); +} + +- (void)testMissingCover +{ + NSImage *actual = [metadataFile cover]; + STAssertNil(actual, @"Cover not missing"); +} @end From 6ac021a80c10b00b04cc41050b1e0c066b7a7679 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Sun, 12 Feb 2012 08:45:17 +0000 Subject: [PATCH 13/17] Test for Barnes and Noble DRM The fake file is the same as the fake-adept one, except that the operatorURL has been edited to include barnesandnoble.com. The extra test succeeds. --- EpubTests/EpubTests.h | 1 + EpubTests/EpubTests.m | 10 ++++++++++ EpubTests/fake-bn.epub | Bin 0 -> 4227 bytes epub.xcodeproj/project.pbxproj | 4 ++++ 4 files changed, 15 insertions(+) create mode 100644 EpubTests/fake-bn.epub diff --git a/EpubTests/EpubTests.h b/EpubTests/EpubTests.h index c9c3c74..677f7ef 100644 --- a/EpubTests/EpubTests.h +++ b/EpubTests/EpubTests.h @@ -15,6 +15,7 @@ JTPepub *metadataFile; JTPepub *badcontributorFile; JTPepub *adeptFile; + JTPepub *bnFile; JTPepub *fairplayFile; JTPepub *koboFile; } diff --git a/EpubTests/EpubTests.m b/EpubTests/EpubTests.m index b77ac6c..920c37f 100644 --- a/EpubTests/EpubTests.m +++ b/EpubTests/EpubTests.m @@ -20,12 +20,14 @@ - (void)setUp NSString *metadata = [thisBundle pathForResource:@"metadata" ofType:@"epub"]; NSString *badcontributor = [thisBundle pathForResource:@"badcontributor" ofType:@"epub"]; NSString *adept = [thisBundle pathForResource:@"fake-adept" ofType:@"epub"]; + NSString *bn = [thisBundle pathForResource:@"fake-bn" ofType:@"epub"]; NSString *fairplay = [thisBundle pathForResource:@"fake-fairplay" ofType:@"epub"]; NSString *kobo = [thisBundle pathForResource:@"fake-kobo" ofType:@"epub"]; untitledFile = [[JTPepub alloc] initWithFile:untitled]; metadataFile = [[JTPepub alloc] initWithFile:metadata]; badcontributorFile = [[JTPepub alloc] initWithFile:badcontributor]; adeptFile = [[JTPepub alloc] initWithFile:adept]; + bnFile = [[JTPepub alloc] initWithFile:bn]; fairplayFile = [[JTPepub alloc] initWithFile:fairplay]; koboFile = [[JTPepub alloc] initWithFile:kobo]; } @@ -34,6 +36,7 @@ - (void)tearDown { [koboFile release]; [fairplayFile release]; + [bnFile release]; [adeptFile release]; [badcontributorFile release]; [metadataFile release]; @@ -136,6 +139,13 @@ - (void)testAdobeDRM STAssertEqualObjects(actual, expected, @"fake-adept file has wrong DRM"); } +- (void)testBarnesAndNobleDRM +{ + NSString *actual = [bnFile drm]; + NSString *expected = @"Barnes & Noble"; + STAssertEqualObjects(actual, expected, @"fake-bn file has wrong DRM"); +} + - (void)testAppleDRM { NSString *actual = [fairplayFile drm]; diff --git a/EpubTests/fake-bn.epub b/EpubTests/fake-bn.epub new file mode 100644 index 0000000000000000000000000000000000000000..785b1d128897ae3a7504000a5683f728d773355a GIT binary patch literal 4227 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 Date: Mon, 13 Feb 2012 20:25:56 +0000 Subject: [PATCH 14/17] Detect and report expiring library books The only library books I have are from Overdrive (via the London Libraries Consortium), which use Adept DRM. The rights.xml file contains elements. So while parsing Adept DRM, we look for elements and build a date if possible. The parsing of the date string is moved to a category on NSDate; the parsing of dates in ePub is reasonably well defined and it is not what Cocoa call "natural language". I switched the publication date parsing to using this category as well. It is not known if the DRM expiry dates are formatted the same as dates elsewhere in ePubs. The expiry date is rendered into the HTML. Tests of the date parser are added. Tests of a fake library book are added as well. --- EpubTests/EpubTests.h | 1 + EpubTests/EpubTests.m | 38 ++++++++++++++++++++++++++++ EpubTests/fake-library.epub | Bin 0 -> 4317 bytes epub.xcodeproj/project.pbxproj | 14 +++++++++++ epub/GeneratePreviewForURL.m | 18 +++++++++++--- epub/JTPepub.h | 3 ++- epub/JTPepub.m | 27 +++++++++++++++++++- epub/NSDate+OPF.h | 13 ++++++++++ epub/NSDate+OPF.m | 40 ++++++++++++++++++++++++++++++ epub/en.lproj/Localizable.strings | 1 + 10 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 EpubTests/fake-library.epub create mode 100644 epub/NSDate+OPF.h create mode 100644 epub/NSDate+OPF.m diff --git a/EpubTests/EpubTests.h b/EpubTests/EpubTests.h index 677f7ef..5ba4de8 100644 --- a/EpubTests/EpubTests.h +++ b/EpubTests/EpubTests.h @@ -18,5 +18,6 @@ JTPepub *bnFile; JTPepub *fairplayFile; JTPepub *koboFile; + JTPepub *libraryFile; } @end diff --git a/EpubTests/EpubTests.m b/EpubTests/EpubTests.m index 920c37f..fc88fd4 100644 --- a/EpubTests/EpubTests.m +++ b/EpubTests/EpubTests.m @@ -8,6 +8,7 @@ #import "EpubTests.h" #import "JTPepub.h" +#import "NSDate+OPF.h" @implementation EpubTests @@ -23,6 +24,7 @@ - (void)setUp NSString *bn = [thisBundle pathForResource:@"fake-bn" ofType:@"epub"]; NSString *fairplay = [thisBundle pathForResource:@"fake-fairplay" ofType:@"epub"]; NSString *kobo = [thisBundle pathForResource:@"fake-kobo" ofType:@"epub"]; + NSString *library = [thisBundle pathForResource:@"fake-library" ofType:@"epub"]; untitledFile = [[JTPepub alloc] initWithFile:untitled]; metadataFile = [[JTPepub alloc] initWithFile:metadata]; badcontributorFile = [[JTPepub alloc] initWithFile:badcontributor]; @@ -30,10 +32,12 @@ - (void)setUp bnFile = [[JTPepub alloc] initWithFile:bn]; fairplayFile = [[JTPepub alloc] initWithFile:fairplay]; koboFile = [[JTPepub alloc] initWithFile:kobo]; + libraryFile = [[JTPepub alloc] initWithFile:library]; } - (void)tearDown { + [libraryFile release]; [koboFile release]; [fairplayFile release]; [bnFile release]; @@ -111,6 +115,22 @@ - (void)testThreeAuthors STAssertEqualObjects([actual objectAtIndex:2], expected2, @"Third author is wrong"); } +#pragma mark Test date parsing +- (void)testDateParsing +{ + NSDate *d; + NSArray *goodDates = [NSArray arrayWithObjects: + @"2012", @"2012-02", @"2012-02-13", + @"2012-02-13T19:49Z", + @"2012-02-13T19:49+0100", + nil]; + for (id date in goodDates) { + d = [NSDate dateFromOPFString:date]; + STAssertNotNil(d, @"%@ should parse", date); + } + +} + #pragma mark Test lack of opf:role - (void)testBadContributor { @@ -160,6 +180,24 @@ - (void)testKoboDRM STAssertEqualObjects(actual, expected, @"fake-kobo file has wrong DRM"); } +- (void)testUnexpiringAdobe +{ + NSDate *actual = [adeptFile expiryDate]; + STAssertNil(actual, @"fake-adept should not expire"); +} + +- (void)testUnexpiringApple +{ + NSDate *actual = [fairplayFile expiryDate]; + STAssertNil(actual, @"fake-fairplay should not expire"); +} + +- (void)testExpiringAdobe +{ + NSDate *actual = [libraryFile expiryDate]; + STAssertNotNil(actual, @"fake-library does not expire"); +} + #pragma mark Test covers - (void)testUntitledCover { diff --git a/EpubTests/fake-library.epub b/EpubTests/fake-library.epub new file mode 100644 index 0000000000000000000000000000000000000000..1a7bc85221560478d92e7ea751ddc1a4acf7046a GIT binary patch literal 4317 zcmai%2T&7v8^%MI4obVD3DUt3kRl+xNDD=JhtPY3&=C|AJTQQOp!6zDdH^LfX`zD% z(go?#6r_dvCFh)n-+kV)ZRd!7=RZO4b*P1kkIz=Ck5c(@8JLd_}K4kpinnwTN{|Or-z_D)Ca;J z=nU1tP`@*_VgEgXiO=6CR+p z0Jz=TtIlFy_X9mLtydX0pGWOI?3MM*=VdgezvEUt80FUfDoQUcPQ}+%iO;3FNE-i0 zW#m1n_lw|`lic%@c0|MwmzfNixfW`L9t|I>hCF%tQe#Ni<2ovMuX`F{xFuj!m$rEoL@MNs3ux#K04of6r8bsnO@N6sr0DNhAf(l zQ>RHP^XL}IOQuKYB8}<|n&Vb4wu)$pmHNSIzdk(6xgAS-mNRAf{uu%ao?H3;v*}*D zVd?CW)-NB(9#q!Rm`5#TI?CQ{e(yx96?-qnN+%-$rlVXe+!nB@6>;!XJFV?&jLoSiM(JP= zv6R{sUEFFd$gOsIt^5?y-uG#-!QsE-a=zhR@Bb*Y+fCi*F)j-7Kn6T;S6nf8TREzc zNtx2-sAXYl>O<+Zk^dwsdLUlbaDM0b+eY0G>qOk{@M|VfOBsRoWt4qJ*4e_rc(hcH zCBvc(1@T*vOZW~w+hn53X@k_v>>I;ONIQR#e4d_ETh;`wC(o^Dd<;9fNfqDqB|=`3 z-OHoXCOcLlrDvZyXpCKO3{5+KB1K6buvgyk4TT;IpmsdB6v@=$mOXQu{pOFSV?ck{ zVPmp5b2O$l_k6@+{C!hV{b|`k%JdW&%9>567OCKD*e5B@8F8Z`e_b7#9uY+r2UahtEddB}CrtE2K*~@z8r}3=#B~=ZTjuF z$^d-33apomta+jpvQ?s9#2e>33_N(w9AhBgU()@%J{vD5^^MNp9&3MV2VXV>0k4 zS+!foZMWj-GI!Uw@(7;h6@C>$VeR8iwbCI-!h^*-0n+Bk&$Zjassnn3z1sM3L2gXjRj@<4w!{~QX@b4ixQy<1;GD z=M<~a*W$&v$WF|qF5RT_C3s3uV*#QX(Au%pSx*+fM4t%nyssJt%cpu z20XoywPsT;iBA0Ex!)c+^^;KBeoK+ucTD#eaGQq7y72Fga+>IB9rdDN}!=%F+4iriHL9UD(!ptt%>; zb3)6o7rZxI>&r48xH$SEG6_6c$n0zJI}}}L;wnj~B=;^Qu!z?t7KR`}E4oG!j%0H{ ztC2WGkrb*TR4keyH}0l_MwWxbAuen|a6^qT@0dyISF^_maXTnM3m}n?<>N^Ba#cL9 zqLV2AYWML4`?H}V{4M1{c^UZD z^4`lT4~g}}rM)IrhH;TBpm>5Ay$6)6Y&}jwNPoRdoVuo@A*!ieS2s1^ylBfFynbtz zV5(4cWuEYrOO1?rjD%Dlac5neU56Pbu4bq88`;&;(FkOH4DM8*Pcn~aKfDCmF zPSTT_*2g2H((fGN!D9i>wWaCnDB19!43TvnScY6fL?L;XI8o~I@nex=b3FhW!+Y=g z%65w%jtOTOYmWN%hc?J$&Zfv(=FPGoCsvwS1wz7hb zo}j+HKTPmXZ-d16r~02qJNEUzeS%{SSPp%xd5FLad}TZUfcggnYn1FgU_ei(!=ESx zsDw85`Y$W?DB)d9Du_yX_8V=I6@NvyL&9}j!_s1P{yldjtMvxXtNY;op}6L9ArZU$ z(j$irsRNe5xs8A|(;dd?tkQ1h%iiiOswIdbZ%GKPw61U#NFDJ(POBU=v;s-qQ^-&z zZ%hm;=i)s{H~4QTi6({kp%YgT!Tzzwe-p7S~yo%|M^W48P8HocTW z6@?DZ14yOTvxYYZxvt^W@noi}4O1=gH*GKurQ$rRk61>r8y3!H>VC^Ik)mpQS>j$$ z5LzYr-3u5~jk-2elEq+$VH>r>z#Jc`xBF(Am7RA9PnJtPkvpT(?}HhWi(B>~a6m3( zb(q+X)7x%H&{4byc6F%!{=D0|eEVZq(Wx-R)}qkV-pqnuQ~Kxyjdj@Eek0Fv(`9G! zz}IOzhg4s9g&<9{qANmMk48^2_KQfA#Us(B5TcX#d>ZrG`!C4vwVkkm0% z^51maZSt(4S%pu~@Cr1}CuDv|+4;D}t=VNMKj1PDa9NvtV0Z!@z8SjQhw@`P6={?=L3E@8|JQ=NI07!poO4{=YrVA_R~_r`9Fkpg^a-~z3J~bE zdI*>l!pjawgm9UWCyHwAujzfRCYek5ZIzNsXJfZBN^E>hpG{3!^~)b7S9yhfClVEs zmZgSNY95ps3sjeM^xH+P++*Hqea|;h_LS3W z9cz#7FQ{`olXJ-~4!ds~HY4d$k=r@9b8r(LsA%eJloD`*pT3}+BBbxGQ)930S4R8= znMRKp8Pu1G)ERcNY~T8=h8q%+v7{Y~2IxEbtf`#iafkiuZEHnlrt5htw1b|zwM-2$aW zHR{)e5!pA?hY>IG)NHD@j}A6g4eY0IhDV0{L+_6hk|K|+v!=_?HLek7mUm=ZuOuqm zfkQ%dG;nZP@O~9E8UAdW7!3h=53sj?$4-LpvGc$B+;`fq0_H`UA%^y=xcUFt{|s&y z*`}BU|6pH?aTqW*#$8x6Ho{$$;Gg5-I~V|<0&L1l69ax89vC2Ycw7KtRl2CcKS#&K zm4^KQ{xnE1*#9v^E@J=Lk}hH&kp8vK9}VjNYl=nwylpNbLF9iSf3=Bz^#=k%tR_U5 N(;Cw|9VxI+{{eeHKV1L- literal 0 HcmV?d00001 diff --git a/epub.xcodeproj/project.pbxproj b/epub.xcodeproj/project.pbxproj index 06d13c0..3f0c2d0 100644 --- a/epub.xcodeproj/project.pbxproj +++ b/epub.xcodeproj/project.pbxproj @@ -31,6 +31,10 @@ 67BE43EE14DEC886003C7DFC /* fake-adept.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67BE43EB14DEC886003C7DFC /* fake-adept.epub */; }; 67BE43EF14DEC886003C7DFC /* fake-fairplay.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67BE43EC14DEC886003C7DFC /* fake-fairplay.epub */; }; 67BE43F014DEC886003C7DFC /* fake-kobo.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67BE43ED14DEC886003C7DFC /* fake-kobo.epub */; }; + 67E3075214E995510004C1D8 /* fake-library.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67E3075114E995510004C1D8 /* fake-library.epub */; }; + 67E3077314E9A24A0004C1D8 /* NSDate+OPF.h in Headers */ = {isa = PBXBuildFile; fileRef = 67E3077114E9A24A0004C1D8 /* NSDate+OPF.h */; }; + 67E3077414E9A24A0004C1D8 /* NSDate+OPF.m in Sources */ = {isa = PBXBuildFile; fileRef = 67E3077214E9A24A0004C1D8 /* NSDate+OPF.m */; }; + 67E3077514E9A24A0004C1D8 /* NSDate+OPF.m in Sources */ = {isa = PBXBuildFile; fileRef = 67E3077214E9A24A0004C1D8 /* NSDate+OPF.m */; }; 67F77F9214E7B25000D9D71D /* fake-bn.epub in Resources */ = {isa = PBXBuildFile; fileRef = 67F77F9114E7B25000D9D71D /* fake-bn.epub */; }; D727FC8413D771B200CB5355 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = D727FC8313D771B200CB5355 /* libz.dylib */; }; D749581E14DD800D0048D619 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = D78388591354890700E7937B /* InfoPlist.strings */; }; @@ -85,6 +89,9 @@ 67BE43EB14DEC886003C7DFC /* fake-adept.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fake-adept.epub"; sourceTree = ""; }; 67BE43EC14DEC886003C7DFC /* fake-fairplay.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fake-fairplay.epub"; sourceTree = ""; }; 67BE43ED14DEC886003C7DFC /* fake-kobo.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fake-kobo.epub"; sourceTree = ""; }; + 67E3075114E995510004C1D8 /* fake-library.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fake-library.epub"; sourceTree = ""; }; + 67E3077114E9A24A0004C1D8 /* NSDate+OPF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+OPF.h"; sourceTree = ""; }; + 67E3077214E9A24A0004C1D8 /* NSDate+OPF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDate+OPF.m"; sourceTree = ""; }; 67F77F9114E7B25000D9D71D /* fake-bn.epub */ = {isa = PBXFileReference; lastKnownFileType = file; path = "fake-bn.epub"; sourceTree = ""; }; D727FC8313D771B200CB5355 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; D749582514DD82360048D619 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -190,6 +197,7 @@ 67F77F9114E7B25000D9D71D /* fake-bn.epub */, 67BE43EC14DEC886003C7DFC /* fake-fairplay.epub */, 67BE43ED14DEC886003C7DFC /* fake-kobo.epub */, + 67E3075114E995510004C1D8 /* fake-library.epub */, ); name = "Test Files"; sourceTree = ""; @@ -250,6 +258,8 @@ 674D09CA14CF297E00127A36 /* NSArray+HTML.m */, 674D09CE14CF2C6800127A36 /* NSString+HTML.h */, 674D09CF14CF2C6800127A36 /* NSString+HTML.m */, + 67E3077114E9A24A0004C1D8 /* NSDate+OPF.h */, + 67E3077214E9A24A0004C1D8 /* NSDate+OPF.m */, D78388611354890700E7937B /* main.c */, D76675C414CECD57003850E7 /* Supporting Code */, D78388681354891800E7937B /* ZipArchive */, @@ -315,6 +325,7 @@ 674D09CB14CF297E00127A36 /* NSArray+HTML.h in Headers */, 674D09D014CF2C6800127A36 /* NSString+HTML.h in Headers */, D76675C114CECD50003850E7 /* GDataXMLNode.h in Headers */, + 67E3077314E9A24A0004C1D8 /* NSDate+OPF.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -397,6 +408,7 @@ 675F07F014E7190700FEFD90 /* metadata.epub in Resources */, 675F07F114E7190700FEFD90 /* Untitled.epub in Resources */, 67F77F9214E7B25000D9D71D /* fake-bn.epub in Resources */, + 67E3075214E995510004C1D8 /* fake-library.epub in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -455,6 +467,7 @@ 675FAB5614D9BEFE0065603A /* NSArray+HTML.m in Sources */, 675FAB5714D9BF030065603A /* NSString+HTML.m in Sources */, 675FAB5514D9BEFB0065603A /* GDataXMLNode.m in Sources */, + 67E3077514E9A24A0004C1D8 /* NSDate+OPF.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -474,6 +487,7 @@ 674D09CC14CF297E00127A36 /* NSArray+HTML.m in Sources */, 674D09D114CF2C6800127A36 /* NSString+HTML.m in Sources */, D76675C214CECD50003850E7 /* GDataXMLNode.m in Sources */, + 67E3077414E9A24A0004C1D8 /* NSDate+OPF.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/epub/GeneratePreviewForURL.m b/epub/GeneratePreviewForURL.m index 2057eea..a0134f6 100644 --- a/epub/GeneratePreviewForURL.m +++ b/epub/GeneratePreviewForURL.m @@ -174,10 +174,20 @@ OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, } if (![[epubFile drm] isEqualToString:@""]) { [metadata appendFormat:@"%@:%@\n", - [pluginBundle localizedStringForKey:@"drm" - value:@"DRM Scheme" - table:nil], - [[epubFile drm] escapedString]]; + [pluginBundle localizedStringForKey:@"drm" + value:@"DRM Scheme" + table:nil], + [[epubFile drm] escapedString]]; + } + if ([epubFile expiryDate]) { + NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease]; + [formatter setDateStyle:NSDateFormatterMediumStyle]; + + [metadata appendFormat:@"%@:%@\n", + [pluginBundle localizedStringForKey:@"expiryDate" + value:@"expiry date" + table:nil], + [formatter stringFromDate:[epubFile expiryDate]]]; } if (![metadata isEqualToString:@""]) { [metadata insertString:@"\n" atIndex:0]; diff --git a/epub/JTPepub.h b/epub/JTPepub.h index a4a51b1..4d191a0 100644 --- a/epub/JTPepub.h +++ b/epub/JTPepub.h @@ -30,7 +30,7 @@ NSImage *cover; NSDate *publicationDate; NSString *drm; - + NSDate *expiryDate; } - (id) initWithFile:(NSString *)fileName; - (BOOL)openEPUBFile:(NSString*)fileName; @@ -47,5 +47,6 @@ - (NSDate *)publicationDate; - (NSString *)isbn; - (NSString *)drm; +- (NSDate *)expiryDate; @end diff --git a/epub/JTPepub.m b/epub/JTPepub.m index 10d78e4..9437ffe 100644 --- a/epub/JTPepub.m +++ b/epub/JTPepub.m @@ -7,6 +7,7 @@ // #import "JTPepub.h" +#import "NSDate+OPF.h" @interface JTPepub (Private) @end @@ -452,7 +453,7 @@ - (NSDate *)publicationDate for(id item in metaElements) { if([[[item attributeForName:@"event"] stringValue] caseInsensitiveCompare:@"publication"] == NSOrderedSame) { - publicationDate = [NSDate dateWithNaturalLanguageString:[item stringValue]]; + publicationDate = [NSDate dateFromOPFString:[item stringValue]]; [publicationDate retain]; } } @@ -521,7 +522,19 @@ - (NSString *)drm } else { drm = @"Adobe"; } + + // Also try to extract an expiry date + NSArray *dates = [adeptXML nodesForXPath:@"//adept:until" + namespaces:xmlns + error:&xmlError]; + // looks like 2012-02-21T07:23:19Z + // but try parsing the full range of formats anyway + [expiryDate release]; + expiryDate = [NSDate dateFromOPFString:[[dates lastObject] stringValue]]; + [expiryDate retain]; + [adeptXML release]; + return drm; } [adeptXML release]; @@ -555,6 +568,17 @@ - (NSString *)drm drm = @""; return drm; } + +- (NSDate *)expiryDate +{ + // If the expiry date has been set, return it. + if (expiryDate) { + return expiryDate; + } + (void)[self drm]; + return expiryDate; +} + - (void)dealloc { [epubFile release]; @@ -570,6 +594,7 @@ - (void)dealloc [synopsis release]; [ISBN release]; [drm release]; + [expiryDate release]; [rootFilePath release]; [publicationDate release]; diff --git a/epub/NSDate+OPF.h b/epub/NSDate+OPF.h new file mode 100644 index 0000000..874fea8 --- /dev/null +++ b/epub/NSDate+OPF.h @@ -0,0 +1,13 @@ +// +// NSDate+OPF.h +// epub +// +// Created by Chris Ridd on 13/02/2012. +// Copyright (c) 2012 Imperial College. All rights reserved. +// + +#import + +@interface NSDate (OPF) ++ (NSDate *)dateFromOPFString:(NSString *)s; +@end diff --git a/epub/NSDate+OPF.m b/epub/NSDate+OPF.m new file mode 100644 index 0000000..cc60afa --- /dev/null +++ b/epub/NSDate+OPF.m @@ -0,0 +1,40 @@ +// +// NSDate+OPF.m +// epub +// +// Created by Chris Ridd on 13/02/2012. +// Copyright (c) 2012 Chris Ridd. All rights reserved. +// + +#import "NSDate+OPF.h" + +@implementation NSDate (OPF) + +// Dates in OPF can be in a variety of formats. +// The formats are described in http://www.w3.org/TR/NOTE-datetime +// The format strings used by Cocoa are described in http://www.unicode.org/reports/tr35/#Date_Format_Patterns ++ (NSDate *)dateFromOPFString:(NSString *)s +{ + NSDate *date = nil; + NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease]; + NSArray *formats = [NSArray arrayWithObjects: + @"yyyy", + @"yyyy-MM", + @"yyyy-MM-dd", + @"yyyy-MM-dd'T'HH:mm'Z'", + @"yyyy-MM-dd'T'HH:mmZ", + @"yyyy-MM-dd'T'HH:mm:ss'Z'", + @"yyyy-MM-dd'T'HH:mm:ssZ", + nil]; + + for (id format in formats) { + [formatter setDateFormat:format]; + date = [formatter dateFromString:s]; + if (date) + return date; + } + return nil; +} + + +@end diff --git a/epub/en.lproj/Localizable.strings b/epub/en.lproj/Localizable.strings index 5e1b463..f068854 100644 --- a/epub/en.lproj/Localizable.strings +++ b/epub/en.lproj/Localizable.strings @@ -11,6 +11,7 @@ "synopsis" = "Synopsis"; "pubDate" = "Publication Date"; "drm" = "DRM Scheme"; +"expiryDate" = "Expiry Date"; "illustrator" = "Illustrator"; "illustrators" = "Illustrators"; "editor" = "Editor"; From 7f2f32b53cd2c537776538274d5cd53949e01b3b Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Mon, 13 Feb 2012 21:38:05 +0000 Subject: [PATCH 15/17] Fix copyright --- epub/NSDate+OPF.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epub/NSDate+OPF.h b/epub/NSDate+OPF.h index 874fea8..2cc49e7 100644 --- a/epub/NSDate+OPF.h +++ b/epub/NSDate+OPF.h @@ -3,7 +3,7 @@ // epub // // Created by Chris Ridd on 13/02/2012. -// Copyright (c) 2012 Imperial College. All rights reserved. +// Copyright (c) 2012 Chris Ridd. All rights reserved. // #import From e7131d705ef5ca8d7cf613cc0a6a8b39abf5363f Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Mon, 13 Feb 2012 22:08:57 +0000 Subject: [PATCH 16/17] Correct the file extension --- README.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index 85868b9..451a96e 100644 --- a/README.markdown +++ b/README.markdown @@ -4,12 +4,12 @@ Designed to extract the cover images from ePub files to use as the file icon, an This generator reads the various parameters directly from the ePub contents - so it will work on books that haven't been imported into iTunes. -**Note**: When used on DRM protected files (Adobe ADEPT or iBooks Fairplay), metadata will only be read from the unencrypted part of the ePub. Typically this means no cover image will be shown. +**Note**: When used on DRM protected files (Adobe, iBooks, Kobo, Barnes & Noble), metadata will only be read from the unencrypted part of the ePub. Typically this means no cover image will be shown. ## Installation -Place the ePub.quicklook generator file into `/Library/QuickLook` (for all users) or `~/Library/QuickLook` (for the current user only). +Place the ePub.qlgenerator file into `/Library/QuickLook` (for all users) or `~/Library/QuickLook` (for the current user only). ### Conflict with other quicklook generators -Under some circumstances, epub.quicklook can conflict with other quicklook generators (notably BetterZipQL under OS X 10.6 and earlier). To fix this, rename epub.quicklook to come before the conflicting plugin alphabetically (AA_epub.quicklook should work). +Under some circumstances, epub.qlgenerator can conflict with other quicklook generators (notably BetterZipQL under OS X 10.6 and earlier). To fix this, rename epub.qlgenerator to come before the conflicting plugin alphabetically (AA_epub.qlgenerator should work). From 38bf70d076eafa56a01024319596961ae03fc695 Mon Sep 17 00:00:00 2001 From: Chris Ridd Date: Fri, 17 Feb 2012 18:39:37 +0000 Subject: [PATCH 17/17] Filesystem names might not just be UTF-8 Using NSString's -fileSystemRepresentation method returns a string that accurately reflects what OS X can open. There are some differences when you start using accented characters in filenames. Zip files as used by epubs really do have UTF-8 filenames, so the calls to open files inside an epub are not changed. Unit tests still succeed. --- epub/ZipArchive/ZipArchive.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epub/ZipArchive/ZipArchive.mm b/epub/ZipArchive/ZipArchive.mm index e674c0a..2fdaa04 100644 --- a/epub/ZipArchive/ZipArchive.mm +++ b/epub/ZipArchive/ZipArchive.mm @@ -40,7 +40,7 @@ -(id) initWithZipFile:(NSString *)fileName { [fileName retain]; - _zipFile = unzOpen([fileName UTF8String]); + _zipFile = unzOpen([fileName fileSystemRepresentation]); if(_zipFile) { return self; }else{