1 /* 2 * Copyright (C) 2009 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #import "WebPDFDocumentExtras.h" 27 28 #import "WebTypesInternal.h" 29 #import <JavaScriptCore/Vector.h> 30 #import <JavaScriptCore/RetainPtr.h> 31 #import <PDFKit/PDFDocument.h> 32 #import <objc/objc-runtime.h> 33 34 #if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) 35 @interface PDFDocument (Internal) 36 - (CGPDFDocumentRef)documentRef; 37 @end 38 #endif 39 40 static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector<CGPDFObjectRef>& values) 41 { 42 CGPDFArrayRef names; 43 if (CGPDFDictionaryGetArray(subtree, "Names", &names)) { 44 size_t nameCount = CGPDFArrayGetCount(names) / 2; 45 for (size_t i = 0; i < nameCount; ++i) { 46 CGPDFObjectRef object; 47 CGPDFArrayGetObject(names, 2 * i + 1, &object); 48 values.append(object); 49 } 50 return; 51 } 52 53 CGPDFArrayRef kids; 54 if (!CGPDFDictionaryGetArray(subtree, "Kids", &kids)) 55 return; 56 57 size_t kidCount = CGPDFArrayGetCount(kids); 58 for (size_t i = 0; i < kidCount; ++i) { 59 CGPDFDictionaryRef kid; 60 if (!CGPDFArrayGetDictionary(kids, i, &kid)) 61 continue; 62 appendValuesInPDFNameSubtreeToVector(kid, values); 63 } 64 } 65 66 static void getAllValuesInPDFNameTree(CGPDFDictionaryRef tree, Vector<CGPDFObjectRef>& allValues) 67 { 68 appendValuesInPDFNameSubtreeToVector(tree, allValues); 69 } 70 71 NSArray *allScriptsInPDFDocument(PDFDocument *document) 72 { 73 NSMutableArray *scripts = [NSMutableArray array]; 74 CGPDFDocumentRef pdfDocument = [document documentRef]; 75 if (!pdfDocument) 76 return scripts; 77 78 CGPDFDictionaryRef pdfCatalog = CGPDFDocumentGetCatalog(pdfDocument); 79 if (!pdfCatalog) 80 return scripts; 81 82 // Get the dictionary of all document-level name trees. 83 CGPDFDictionaryRef namesDictionary; 84 if (!CGPDFDictionaryGetDictionary(pdfCatalog, "Names", &namesDictionary)) 85 return scripts; 86 87 // Get the document-level "JavaScript" name tree. 88 CGPDFDictionaryRef javaScriptNameTree; 89 if (!CGPDFDictionaryGetDictionary(namesDictionary, "JavaScript", &javaScriptNameTree)) 90 return scripts; 91 92 // The names are aribtrary. We are only interested in the values. 93 Vector<CGPDFObjectRef> objects; 94 getAllValuesInPDFNameTree(javaScriptNameTree, objects); 95 size_t objectCount = objects.size(); 96 97 for (size_t i = 0; i < objectCount; ++i) { 98 CGPDFDictionaryRef javaScriptAction; 99 if (!CGPDFObjectGetValue(reinterpret_cast<CGPDFObjectRef>(objects[i]), kCGPDFObjectTypeDictionary, &javaScriptAction)) 100 continue; 101 102 // A JavaScript action must have an action type of "JavaScript". 103 const char* actionType; 104 if (!CGPDFDictionaryGetName(javaScriptAction, "S", &actionType) || strcmp(actionType, "JavaScript")) 105 continue; 106 107 const UInt8* bytes = 0; 108 CFIndex length; 109 CGPDFStreamRef stream; 110 CGPDFStringRef string; 111 RetainPtr<CFDataRef> data; 112 if (CGPDFDictionaryGetStream(javaScriptAction, "JS", &stream)) { 113 CGPDFDataFormat format; 114 data.adoptCF(CGPDFStreamCopyData(stream, &format)); 115 if (!data) 116 continue; 117 bytes = CFDataGetBytePtr(data.get()); 118 length = CFDataGetLength(data.get()); 119 } else if (CGPDFDictionaryGetString(javaScriptAction, "JS", &string)) { 120 bytes = CGPDFStringGetBytePtr(string); 121 length = CGPDFStringGetLength(string); 122 } 123 if (!bytes) 124 continue; 125 126 NSStringEncoding encoding = (length > 1 && bytes[0] == 0xFE && bytes[1] == 0xFF) ? NSUnicodeStringEncoding : NSUTF8StringEncoding; 127 NSString *script = [[NSString alloc] initWithBytes:bytes length:length encoding:encoding]; 128 [scripts addObject:script]; 129 [script release]; 130 } 131 132 return scripts; 133 } 134