CGPDFDictionaryRef to something useful - iphone

A little bit of background:
I'm appending an image to an already existing pdf file. I found out that there isn't a way to just add a page to it, you must recreate the whole pdf, then remove the old one then move the new one. (If that is wrong please tell me, and it will save me a lot of headaches.) I've accomplished that part, but I'm now trying to copy of the Auxiliary Info (Title, Author, Keys, etc). Problem is there are so many type problems that the current method I'm using is to get the CGPDFDocumentRef's CGPDFDictionaryRef, then call CGPDFDictionaryApplierFunction. In the C function I pass it, I am extracting each key to an NSMutableDictionary so that I can then do something with the values and not have them locked in this terrible CGPDF format.
So basically my question would be:
Is there a better way of doing this? I've stared at so many documentation files I couldn't imagine missing anything, but really hope I have because the amount of workaround I'm having to do is getting absurd.

I've completed my workaround. I would still like to know if there is any better way to do this, so if you know of one or have any suggestions I'm willing to try them out.
Notes: I only check for bools, ints, strings, and arrays because according to Apple's documentation thats all that should be in the Auxiliary Information in a PDF. The array bit is untested, because I dont have a PDF with an array in the Aux Info. If someone would like to test that for me or link to a pdf with one in it I will gladly test it out.
First, in the class header create an id selfClass outside the #interface tags so that the C functions called by CGPDFDictionaryApplyFunction can access the current class. Also add an NSDictionary *auxInfo for storing the information. Once extracted, the NS type can be easily cast like this:
CFDictionaryRef newDictionary = (CFDictionaryRef)[self auxInfo];
I was actually done last night but thought I had to do another round of looping to convert from NS to CF, forgetting they were token-free bridged. So there you have it, hope everyone benefits from my labors. Ask questions if you need clarification. And once again, if there is an easier way to do this, if only an optimization of my code, please say so. I know this isn't a very elegant way to do this but it works for now.
- (void)extractPDFDictionary:(CGPDFDocumentRef)pdf{
NSLog(#"extractingPDFDictionary");
CGPDFDictionaryRef oldDict = CGPDFDocumentGetInfo(pdf);
CGPDFDictionaryApplyFunction(oldDict, copyDictionaryValues, NULL);
}
void copyDictionaryValues (const char *key, CGPDFObjectRef object, void *info) {
NSLog(#"key: %s", key);
CGPDFObjectType type = CGPDFObjectGetType(object);
switch (type) {
case kCGPDFObjectTypeString: {
CGPDFStringRef objectString;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &objectString)) {
NSString *tempStr = (NSString *)CGPDFStringCopyTextString(objectString);
[[selfClass auxInfo] setObject:tempStr
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
[tempStr release];
NSLog(#"set string value");
}
}
case kCGPDFObjectTypeInteger: {
CGPDFInteger objectInteger;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeInteger, &objectInteger)) {
[[selfClass auxInfo] setObject:[NSNumber numberWithInt:objectInteger]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
NSLog(#"set int value");
}
}
case kCGPDFObjectTypeBoolean: {
CGPDFBoolean objectBool;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeBoolean, &objectBool)) {
[[selfClass auxInfo] setObject:[NSNumber numberWithBool:objectBool]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
NSLog(#"set boolean value");
}
}
case kCGPDFObjectTypeArray : {
CGPDFArrayRef objectArray;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeArray, &objectArray)) {
NSArray *tempArr = [selfClass copyPDFArray:objectArray];
[[selfClass auxInfo] setObject:tempArr
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
[tempArr release];
NSLog(#"set array value");
}
}
}
}
- (NSArray *)copyPDFArray:(CGPDFArrayRef)arr{
int i = 0;
NSMutableArray *temp = [[NSMutableArray alloc] init];
for(i=0; i<CGPDFArrayGetCount(arr); i++){
CGPDFObjectRef object;
CGPDFArrayGetObject(arr, i, &object);
CGPDFObjectType type = CGPDFObjectGetType(object);
switch(type){
case kCGPDFObjectTypeString: {
CGPDFStringRef objectString;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &objectString)) {
NSString *tempStr = (NSString *)CGPDFStringCopyTextString(objectString);
[temp addObject:tempStr];
[tempStr release];
}
}
case kCGPDFObjectTypeInteger: {
CGPDFInteger objectInteger;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeInteger, &objectInteger)) {
[temp addObject:[NSNumber numberWithInt:objectInteger]];
}
}
case kCGPDFObjectTypeBoolean: {
CGPDFBoolean objectBool;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeBoolean, &objectBool)) {
[temp addObject:[NSNumber numberWithBool:objectBool]];
}
}
case kCGPDFObjectTypeArray : {
CGPDFArrayRef objectArray;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeArray, &objectArray)) {
NSArray *tempArr = [selfClass copyPDFArray:objectArray];
[temp addObject:tempArr];
[tempArr release];
}
}
}
}
return temp;
}

You might accomplish what you are describing by drawing into CGPDFContext, but if you have a complex PDF, you may be attempting something beyond what the API was intended for. You might take a look at section 3.4 of the PDF spec and see what you're getting into.

Related

Printing array inside array

I have class Building in which i have one class member NSMutableArray *subnode.I have declared one more array buildingArray which stores the element of type Building.I have tried to print Building class object as shown in following code.But goes in only first for loop.
Second for loop of subnode array is not executing . Is this proper way of printing the object having the array as a one of its class member.
code:
for(Building *b in buildingArray)
{
NSLog(#"inside building array");
for(NSString *str in b.subnode)
{
NSLog(#"inside subnode array");
}
}
If this is for debugging purposes, I would recommend trying the following: Every object that inherits from NSObject inherits its description method.
Add this to Building.m:
#implementation Building
- (NSString *)description {
NSMutableString *description = [NSMutableString stringWithString:[super description]];
// add the following lines for any relevant properties
// [description appendFormat:#", materials == %#", materials];
// then have the subnode print itself:
[description appendFormat:#", subnode == %#", subnode];
return description;
}
#end
You can then print the entire buildingArray by simply calling the following code:
NSLog(#"buildingArray == %#", buildingArray);
for(Building *b in buildingArray)
{
NSLog(#"inside building array");
NSMutableArray *temp = [NSMutableArray arrayWithArray:b.subnode]
for(id *str in temp)
{
NSLog(#"inside subnode array");
}
}
this should work. happy coding :)
Your code seems to be ok. Just check if the array (subnode) is allocated and initialized. Also check if it has some values in it. I have used similar code and it works for me.
Change following code. I don't know what do you mean buy Building . So i just used id instead of Building to avoid any type of confusion.
for(id b in buildingArray)
{
NSLog(#"inside building array");
NSArray *temp = b;
for(NSString *str in temp)
{
NSLog(#"inside subnode array");
}
}
Hope, this will help you;

How to display Xpath on the iPhone

I'm trying to extract the weather information from here using Xpath on the iPhone. As of now it parses all the data but I'm stuck on how to extract the content and display it in a table.
This is what I have so far:
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[ #"http://aviationweather.gov/adds/metars/?station_ids=1234&std_trans=translated&chk_metars=on&hoursStr=most+recent+only&submitmet=Submit"stringByReplacingOccurrencesOfString:#"1234" withString:self.title]]];
TFHpple * doc = [[TFHpple alloc] initWithHTMLData:data];
NSArray * elements = [doc searchWithXPathQuery:#"//table[1]//tr"];
NSLog(#"%#", elements);
TFHppleElement * element = [elements objectAtIndex:0];
[element content]; // Tag's innerHTML
[element tagName]; // "a"
[element attributes]; // NSDictionary of href, class, id, etc.
[element objectForKey:#"href"]; // Easy access to single attribute
If anybody needs to see what its outputting so far, let me know.
Thanks,
Andrew
I had the same issue I got to the point your at and didn't no where to go but I end up implementing this code. Hope it helps there is still little bits need to make it work correctly but do to the nature of the app I have developed this is all I can give you. its not much more its just the actual implementation into your code that you need really.
#import "XPathQuery.h"
NSMutableArray *weatherArray = [[NSMutableArray arrayWithArray:0]retain]; // Initilize the NSMutableArray can also be done with just an NSArray but you will have to change the populateArray method.
NSString *xPathLookupQuery = #"//table[1]//tr"; // Path in xml
nodes = PerformXMLXPathQuery(data, xPathLookupQuery); // Pass the data in that you need to search through
[self populateArray:weatherArray fromNodes:nodes]; // To populate multiple values into array.
session = [[self fetchContent:nodes] retain]; // To populate a single value and returns value.
- (void)populateArray:(NSMutableArray *)array fromNodes:(NSArray *)nodes
{
for (NSDictionary *node in nodes) {
for (id key in node) {
if ([key isEqualToString:#"nodeContent"]) {
[array addObject:[node objectForKey:key]];
}
}
}
}
You only need either the above code or below code unless you want both.
- (NSString *)fetchContent:(NSArray *)nodes
{
NSString *result = #"";
for (NSDictionary *node in nodes) {
for (id key in node) {
if([key isEqualToString:#"nodeContent"]) {
result = [node objectForKey:key];
}
}
}
return result;
}

How do I get the index of an object in an NSArray using string value?

I want to get the index of an object within the NSMutableArray of categories.
The category object has an attribute "category_title" and I want to be able to get the index by passing the value of category_title.
I have looked through the docs and can't find a simple way to go about this.
NSArray does not guarantee that you can only store one copy of a given object, so you have to make sure that you handle that yourself (or use NSOrderedSet).
That said, there are a couple approaches here. If your category objects implement isEqual: to match category_title, then you can just use -indexOfObject:.
If you can't do that (because the category objects use a different definition of equality), use -indexOfObjectPassingTest:. It takes a block in which you can do whatever test you want to define your "test" - in this case, testing category_title string equality.
Note that these are all declared for NSArray, so you won't see them if you are only looking at the NSMutableArray header/documentation.
EDIT: Code sample. This assumes objects of class CASCategory with an NSString property categoryTitle (I can't bring myself to put underscores in an ivar name :-):
CASCategory *cat1 = [[CASCategory alloc] init];
[cat1 setCategoryTitle:#"foo"];
CASCategory *cat2 = [[CASCategory alloc] init];
[cat2 setCategoryTitle:#"bar"];
CASCategory *cat3 = [[CASCategory alloc] init];
[cat3 setCategoryTitle:#"baz"];
NSMutableArray *array = [NSMutableArray arrayWithObjects:cat1, cat2, cat3, nil];
[cat1 release];
[cat2 release];
[cat3 release];
NSUInteger barIndex = [array indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
if ([[(CASCategory *)obj categoryTitle] isEqualToString:#"bar"]) {
*stop = YES;
return YES;
}
return NO;
}];
if (barIndex != NSNotFound) {
NSLog(#"The title of category at index %lu is %#", barIndex, [[array objectAtIndex:barIndex] categoryTitle]);
}
else {
NSLog(#"Not found");
}
Not sure that I understand the question but something like this might work (assuming the Mutable Array contains objects of Class "Category"):
int indx;
bool chk;
for (Category *aCategory in theArray)
{
chk = ([[aCategory category_title] isEqualToString:#"valOfCategoryTitle"])
if ( chk )
indx = [theArray indexOfObject:aCategory];
}
Try this code much more simpler:-
int f = [yourArray indexOfObject:#"yourString"];

Sort NSMutableDictionary keys by object?

Okeh. Here is the deal:
Have have a NSMutualDictionary with words as keys (say names). The value objects is a NSNumber (like rating)
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:[NSNumber intValue:1] forKey:#"Melvin"];
[dictionary setObject:[NSNumber intValue:2] forKey:#"John"];
[dictionary setObject:[NSNumber intValue:3] forKey:#"Esben"];
I want to sort them with the highest ratings first.
I know I'm going to do it like this:
[searchWords keysSortedByValueUsingSelector:#selector(intCompare:)];
But not sure how to implement intCompare. (the compare method)
Can anybody point me in the right direction?
- (NSComparisonResult) intCompare:(NSString *) other
{
//What to do here?
}
I want to get an NSArray with {Esben, John, Melvin}.
Since the objects you put into the dictionary are NSNumber instances you should change the method signature a bit. But the full implementation is really easy:
-(NSComparisonResult)intCompare:(NSNumber*)otherNumber {
return [self compare:otherNumber];
}
In fact I see no reason as to why you need to do your own intCompare: method, when you could go with the compare: that NSNumber already has.
These constants are used to indicate how items in a request are ordered.
enum {
NSOrderedAscending = -1,
NSOrderedSame,
NSOrderedDescending
};
typedef NSInteger NSComparisonResult;
That is taken from Apple's dev documentataion on Data types... now all you have to do is check which one is bigger. All of this is done for you though. Simply pass in #selector(compare:) and that should do it. As your values are NSNumbers and NSNumber implements the compare: function. Which is what you want :)
NSArray *sortedArray = [searchWords sortedArrayUsingSelector:#selector(compare:) ];
or you might well as used, here is the implementation of your intCompare selector
- (NSComparisonResult) intCompare:(NSString *) other
{
int myValue = [self intValue];
int otherValue = [other intValue];
if (myValue == otherValue) return NSOrderedSame;
return (myValue < otherValue ? NSOrderedAscending : NSOrderedDescending);
}

Problem with leaking NSStrings

My code leaks but I do not know exactly what am I doing wrong. Simply I have a function that takes array with NSStrings and outputs NSString formatted as CSV.
Here is my code:
-(NSString*)generateCSVfromArray: (NSMutableArray*) reportEntries {
NSString* accumulator = [NSString stringWithString:#""];
for (NSString* string in reportEntries) {
NSString* temp = [accumulator stringByAppendingString:string];
accumulator = temp;
if (![string isEqualToString:#"\n"]) {
NSString* temp = [accumulator stringByAppendingString:#";"];
accumulator = temp;
}
}
return accumulator;
}
When I check leaks in Instruments it turns out that many string objects leaked. I managed to isolate the problem to the method above. Can you please help me and point what am I doing wrong?
I don't believe you're leaking any strings in this method. Why do you think this is the method to blame? Remember that Instruments will tell you where the object was created, but this has little to do with where it is leaked. Run the Static Analyzer for more help with that (Cmd-Shift-A).
This method is wildly inefficient, though. You're creating a ton of temporary strings. You could write this much more efficiently like this:
-(NSString*)generateCSVfromArray:(NSArray*)reportEntries {
NSMutableString* accumulator = [NSMutableString string];
for (NSString* string in reportEntries) {
[accumulator appendString:string];
if (![string isEqualToString:#"\n"]) {
[accumulator appendString:#";"];
}
}
return accumulator;
}
There are of course very good CSV writers already available. Search for "Cocoa CSV." But I assume you want this specialized algorithm.