views:

249

answers:

1

I am attempting to create thumbnail images for a CGPDFDocument.
In the PDF Document Parsing section of the Quartz Programming Guide, there is the following code:

CGPDFDictionaryRef d; CGPDFStreamRef stream; // represents a sequence of bytes d = CGPDFPageGetDictionary(page); // check for thumbnail data if (CGPDFDictionaryGetStream (d, “Thumb”, &stream)){ // get the data if it exists data = CGPDFStreamCopyData (stream, &format);

The next step would be to use data to create the image.
Sugar answers the similar question of extracting images from PDF here: http://stackoverflow.com/questions/2475450/extracting-images-from-a-pdf/3584939#3584939

I am attempting to use the decodeValuesFromImageDictionary() and getImageRef() functions listed in his answer to create a UIImage to represent my thumbnail.

My problem is that the image I obtain has wrong colors and wrong dimensions, except when I set the CGColorSpaceRef argument of the CGImageCreate() function to CGColorSpaceCreateDeviceGray(), in which case I get the (correct) greyscale representation of the thumbnail which of course is not what I want.
I know from inspecting the thumbnail stream dictionary that the image format is CGPDFDataFormatRaw and the ColorSpace is DeviceRGB. I also know that two filters (ASCII85Decode and FlateDecode) are applied to the image, even though I am not sure if this is of any significance.

Any suggestions or insight as to why this happens and what to do to fix it is greatly appreciated!

A: 

mona m in code provided by link you specified contains commented line:

//      cgColorSpace = colorSpaceFromPDFArray(colorSpaceArray);

Instead of analyzing colorSpaceArray and getting accurate Color Space a CGColorSpaceCreateDeviceRGB is assigned to cgColorSpace variable which is wrong.

Here is an implementation of missing method, I found it in the internet.

CGColorSpaceRef colorSpaceFromPDFArray(CGPDFArrayRef colorSpaceArray){
CGColorSpaceRef       cgColorSpace = NULL, alternateColorSpace = NULL;
CGPDFStreamRef        stream;
const char            *colorSpaceName = NULL, *alternateColorSpaceName = NULL;
CGPDFInteger        numberOfComponents;
CGPDFDictionaryRef    dict;
bool                retrieved;
CGFloat                *range;
CGPDFArrayRef        rangeArray;

if (CGPDFArrayGetName(colorSpaceArray, 0, &colorSpaceName)) {
    if (strcmp(colorSpaceName, "ICCBased") == 0) {
        if (CGPDFArrayGetStream(colorSpaceArray, 1, &stream)) {
            dict = CGPDFStreamGetDictionary(stream);

            // First obtain the alternate color space if present
            if (CGPDFDictionaryGetName(dict, "Alternate",  &alternateColorSpaceName)) {
                if (strcmp(alternateColorSpaceName, "DeviceRGB") == 0) {
                    alternateColorSpace = CGColorSpaceCreateDeviceRGB();
                } else if (strcmp(alternateColorSpaceName, "DeviceGray") == 
                           0) {
                    alternateColorSpace = CGColorSpaceCreateDeviceGray();
                } else if (strcmp(alternateColorSpaceName, "DeviceCMYK") == 
                           0) {
                    alternateColorSpace = CGColorSpaceCreateDeviceCMYK();
                }
            }

            // Obtain the preferential color space
            CGPDFDataFormat        dataFormat;
            CFDataRef            colorSpaceDataPtr = 
            CGPDFStreamCopyData(stream, &dataFormat);

            if (dataFormat == CGPDFDataFormatRaw) {
                CGDataProviderRef    profile = 
                CGDataProviderCreateWithCFData(colorSpaceDataPtr);

                retrieved = CGPDFDictionaryGetInteger(dict, "N", 
                                                      &numberOfComponents);

                // Deduce an alternate color space if we don't have one 
                //already
                if (alternateColorSpace == NULL) {
                    switch (numberOfComponents) {
                        case 1:
                            alternateColorSpace = CGColorSpaceCreateDeviceGray();
                            break;
                        case 3:
                            alternateColorSpace = CGColorSpaceCreateDeviceRGB();
                            break;
                        case 4:
                            alternateColorSpace = CGColorSpaceCreateDeviceCMYK();
                            break;
                        default:
                            break;
                    }
                }

                range = malloc(numberOfComponents * 2 * sizeof(CGFloat));
                if (!CGPDFDictionaryGetArray(dict, "Range", &rangeArray)) {
                    for (int i = 0; i < numberOfComponents * 2; i += 2) {
                        range[i] = (i % 2 == 0) ? 0.0 : 1.0;
                    }
                } else {
                    size_t count = CGPDFArrayGetCount(rangeArray);
                    for (int i = 0; i < count; i++) {
                        (void)CGPDFArrayGetNumber(rangeArray, i, &range[i]);
                    }

                }


                cgColorSpace = CGColorSpaceCreateICCBased(numberOfComponents, range, profile, 
                                           alternateColorSpace);
                CGDataProviderRelease(profile);
                free(range);
                if (cgColorSpace) {
                    // Since we have a preferential color space, we no 
                    //longer need the hang on to the alternate color space
                    CGColorSpaceRelease(alternateColorSpace);
                } else {
                    cgColorSpace = alternateColorSpace;
                }

            } else if (dataFormat == CGPDFDataFormatJPEGEncoded) {
                //
            } else if (dataFormat == CGPDFDataFormatJPEG2000) {
                //
            }
        }
    } else if (strcmp(colorSpaceName, "Indexed") == 0) {
        CGColorSpaceRef baseSpace;
        CGPDFArrayRef    base = NULL;
        CGPDFInteger    highValue = 0;
        CGPDFStreamRef    stream = NULL;
        CGPDFStringRef    string;
        const unsigned char *chars;
        const char        *namedColorSpaceName;

        if (CGPDFArrayGetArray(colorSpaceArray, 1, &base)) {
            baseSpace = colorSpaceFromPDFArray(base);
        } else if (CGPDFArrayGetName(colorSpaceArray, 1, 
                                     &namedColorSpaceName)) {
            if (strcmp(namedColorSpaceName, "DeviceRGB") == 0) {
                baseSpace = CGColorSpaceCreateDeviceRGB();
            } else if (strcmp(namedColorSpaceName, "DeviceGray") == 0) {
                baseSpace = CGColorSpaceCreateDeviceGray();
            } else if (strcmp(namedColorSpaceName, "DeviceCMYK") == 0) {
                baseSpace = CGColorSpaceCreateDeviceCMYK();
            }
        }

        retrieved = CGPDFArrayGetInteger(colorSpaceArray, 2, &highValue);

        if (CGPDFArrayGetStream(colorSpaceArray, 3, &stream)) {
            chars = CFDataGetBytePtr(CGPDFStreamCopyData(stream, NULL));
        } else if (CGPDFArrayGetString(colorSpaceArray, 3, &string)) {
            chars = CGPDFStringGetBytePtr(string);
        } else {

            // TODO: Raise some error state?
        }

        cgColorSpace = CGColorSpaceCreateIndexed(baseSpace, highValue, 
                                                 chars);
    }
}

return (CGColorSpaceRef)CFMakeCollectable(cgColorSpace);

}

But unfortunately I am getting some grey dots on image at the end.

Koteg