NSImage to PDFPage in Tiger?

  • Hi,

    The only thing holding back the Universal version of my app from being
    on a par
    with the Leopard version is a replacement for the PDFPage method -
    initWithImage.

    I don't seem to be able to find a recipe for generating a PDFPage from a
    NSImage using methods available in the 10.4 Universal SDK that give
    the same
    results as the -initWithImage method in PDFPage. ie generate a PDFPage
    from
    a NSImage.

    Could someone point me in the right direction?

    I guess if it were trivial they would not have added the method to
    Leopard...

    Jerry
  • On Dec 21, 2007, at 11:56 AM, Jerry LeVan wrote:

    > I don't seem to be able to find a recipe for generating a PDFPage
    > from a
    > NSImage using methods available in the 10.4 Universal SDK that give
    > the same
    > results as the -initWithImage method in PDFPage. ie generate a
    > PDFPage from
    > a NSImage.
    >
    > Could someone point me in the right direction?

    I haven't tried this, but the first thing I would try would be to put
    the NSImage into an NSImageView, then call -dataWithPDFInsideRect: to
    turn the image into a PDF, then create a PDFDocument with the data,
    and then get the resulting PDFPage from the PDFDocument... This is, of
    course, assuming the NSImage has only a bitmap representation.

    Nick Zitzmann
    <http://www.chronosnet.com/>
  • On Dec 22, 2007, at 3:29 PM, Nick Zitzmann wrote:

    >
    > On Dec 21, 2007, at 11:56 AM, Jerry LeVan wrote:
    >
    >> I don't seem to be able to find a recipe for generating a PDFPage
    >> from a
    >> NSImage using methods available in the 10.4 Universal SDK that give
    >> the same
    >> results as the -initWithImage method in PDFPage. ie generate a
    >> PDFPage from
    >> a NSImage.
    >>
    >> Could someone point me in the right direction?
    >
    >
    > I haven't tried this, but the first thing I would try would be to
    > put the NSImage into an NSImageView, then call -
    > dataWithPDFInsideRect: to turn the image into a PDF, then create a
    > PDFDocument with the data, and then get the resulting PDFPage from
    > the PDFDocument... This is, of course, assuming the NSImage has only
    > a bitmap representation.
    >
    > Nick Zitzmann
    > <http://www.chronosnet.com/>
    >

    It turned out to be a bit nasty since QuartzFilterManager is not
    available in SDK 10.4
    but a few minutes ago I ran the following code and it generated a nice
    small pdf (1.8 MB) out
    of a twenty four page graphic novel ( 24 jpgs)

    Sorry for the formatting, I need to make a cleanup pass over the code...

    #ifdef UNIVERSAL
    -(IBAction) MakePDF:(id)sender
    {
    // lets try to make some pdf
    NSImage * theImage;
    NSString *outputFile;  // user selected file name
    NSString * theCommand = @"/System/Library/Printers/Libraries/
    quartzfilter";
    NSString * theFilter = [NSHomeDirectory() stringByAppendingString:@"/
    Library/Filters/ImageBrowser.qfilter"];

    NSString *tmpFile = [NSTemporaryDirectory() stringByAppendingString:
    [[NSProcessInfo processInfo] globallyUniqueString]];
    //NSLog(@"tmpName: %@",tmpName);

    // get the filename for output
    NSSavePanel * panel= [NSSavePanel savePanel];
    [panel setRequiredFileType:@"pdf"];
    if( [panel runModal] ==NSFileHandlingPanelOKButton){
      outputFile =  [NSString stringWithString:[panel filename]]; //
    need to use "files"?
    }else return;

    NSMutableArray *args = [NSMutableArray array];
    [args addObject:tmpFile];
    [args addObject:theFilter];
    [args addObject:outputFile];

    NSBundle *thisBundle = [NSBundle bundleForClass:[self class]];
    NSString * emptypdf = [thisBundle pathForResource:@"Empty.pdf"
    ofType:nil] ;
    PDFDocument * myBook = [[PDFDocument alloc] initWithData:[NSData
    dataWithContentsOfFile:emptypdf]];
    [myBook removePageAtIndex:0];

    //Load the progress counter
    int cnt= [fileNameList count];
      if(!thePanel) {
        [NSBundle loadNibNamed: @"FileCount" owner:self];
      }
      [thePanel orderFront:self];
      [progressInd setUsesThreadedAnimation:YES];
      [progressInd startAnimation:self];

      int fcnt = [fileNameList count];

    int i;
    for(i=0;i< cnt;i++) {
    theImage =[ [NSImage alloc] initWithContentsOfFile: [fileNameList
    objectAtIndex:i]];
    NSImageView    *pdfView = [[NSImageView alloc]
          initWithFrame:NSMakeRect(0,0,
          [theImage size].width,
          [theImage size].height)];

    [pdfView setImage:theImage];
    NSData    *pdfData;
    pdfData = [pdfView dataWithPDFInsideRect:[pdfView bounds]];
    PDFDocument * tmpPage = [[PDFDocument alloc] initWithData: pdfData];
    [myBook insertPage:[ tmpPage pageAtIndex: 0] atIndex: i];
    [pdfView release];
    [tmpPage release];
    [theImage release];
    [filesLeft setIntValue: --fcnt];
    [filesLeft displayIfNeeded];
      }
    [progressInd stopAnimation:self];
        [thePanel orderOut:self];    // hide the panel

    if(!theSavePanel) [NSBundle loadNibNamed:@"writePDF" owner: self];
    [theSavePanel orderFront:self];
    [saveInd setUsesThreadedAnimation:YES];
    [saveInd startAnimation:self];

    BOOL writeOK;
    /* This won't work in the Universal SDK
    NSArray *filters = [QuartzFilterManager filtersInDomains:[NSArray
    arrayWithObject:@"QuartzFilterPDFWorkflowDomain"]];
    int search =  [self findSpecialFilter: filters byName:@"ImageBrowser"];
    if ( search != -1 ) {
      QuartzFilter *f = [filters objectAtIndex:search];
      NSDictionary * optionsDictWithQuartzFilter = [NSDictionary
    dictionaryWithObject: f forKey:@"QuartzFilter"];
      writeOK=[myBook writeToFile:outputFile
    withOptions:optionsDictWithQuartzFilter  ];
    }
    else {
         writeOK=[myBook writeToFile:outputFile];
    }
    */
    writeOK=[myBook writeToFile:tmpFile];
            // run special filter to compress jpgs and lighten images
    [self runCommand: theCommand withArguments:args];
            NSFileManager *manager = [NSFileManager defaultManager];
    if ([manager fileExistsAtPath:tmpFile ])
         [manager removeFileAtPath:tmpFile handler:nil];

    [saveInd stopAnimation:self];
    [theSavePanel orderOut:self];

    if(writeOK)
           NSRunInformationalAlertPanel(@"Write Complete.
    ",outputFile,@"OK",nil,nil);
        else
            NSRunInformationalAlertPanel(@"Write Failed.
    ",outputFile,@"OK",nil,nil);

    [myBook release];
    }

    #else
    -(IBAction) MakePDF:(id)sender
  • On Dec 21, 2007, at 11:56 AM, Jerry LeVan wrote:
    > The only thing holding back the Universal version of my app from
    > being on a par
    > with the Leopard version is a replacement for the PDFPage method -
    > initWithImage.

    There is sample code about (PDFCalendar I think) that shows you a way
    to do this using a PDFPage subclass. The PDFPage's draw and bounds
    methods are overridden,  The bounds method will return the image
    bounds and the draw method displays the image.  This "PDFPage" object
    can be added to a PDFDocument.

    john calhoun—
  • On Jan 2, 2008, at 4:25 PM, John Calhoun wrote:

    > On Dec 21, 2007, at 11:56 AM, Jerry LeVan wrote:
    >> The only thing holding back the Universal version of my app from
    >> being on a par
    >> with the Leopard version is a replacement for the PDFPage method -
    >> initWithImage.
    >
    > There is sample code about (PDFCalendar I think) that shows you a
    > way to do this using a PDFPage subclass. The PDFPage's draw and
    > bounds methods are overridden,  The bounds method will return the
    > image bounds and the draw method displays the image.  This "PDFPage"
    > object can be added to a PDFDocument.
    >
    > john calhoun—

    Hi John,

    Someone suggested using dataWithPDFInsideRect... that seems to work
      //Tiger code
    NSImageView * pdfView;
    NSData    *pdfData;
    NSMutableArray * tmpPage = [NSMutableArray array];
    int i;
    for(i=0;i< cnt;i++) {
    theImage =[ [NSImage alloc]
                          initWithContentsOfFile: [fileNameList
    objectAtIndex:i]];
    pdfView = [[NSImageView alloc]
          initWithFrame:NSMakeRect(0,0,
          [theImage size].width,
          [theImage size].height)];

    [pdfView setImage:theImage];
    pdfData = [pdfView dataWithPDFInsideRect:[pdfView bounds]];
    [tmpPage addObject:  [[[PDFDocument alloc] initWithData: pdfData]
    autorelease]];
    [myBook insertPage:[ [tmpPage objectAtIndex:i] pageAtIndex:0]
    atIndex:i];
    [pdfView release];
    [theImage release];
    [filesLeft setIntValue: --fcnt];
    [filesLeft displayIfNeeded];
      }

    For reasons that are not clear to me I had to keep the "pages" until I
    was
    finished with the PDF document in Tiger. For Leopard this was not
    necessary...
          // Leopard Code
    for (i=0; i< cnt ; i++) {
        nextImage = [[NSImage alloc] initWithContentsOfFile:(NSString*)
    [fileNameList objectAtIndex:i]];
        nextPDFPage = [[PDFPage alloc] initWithImage:nextImage];
        [myBook insertPage: nextPDFPage atIndex: i];
        [nextImage release];
        [nextPDFPage release];
    [filesLeft setIntValue: --fcnt];
    [filesLeft displayIfNeeded];
    }

    Jerry
  • On Jan 2, 2008, at 1:40 PM, Jerry LeVan wrote:
    > For reasons that are not clear to me I had to keep the "pages" until
    > I was
    > finished with the PDF document in Tiger. For Leopard this was not
    > necessary...

    This was a bug in PDFKit in Tiger. My recollection is that when you
    added a PDFPage to a document, PDFDocument made a copy of the PDFPage
    and retained that, but did not tell the copy of its new owning document.

    Recall that PDFPage has a -[document] method that returns the "ownnig"
    document.  To accomplish this a PDFPage has a weak reference to the
    PDFDocument that it is a part of.  A private call to -[PDFPage
    setDocument:] is made by the PDFDocument when you add a page.  The bug
    is that while a copy of the page is created and retained, -
    [setDocument:] is called on the original PDFPage.  I'm not quite clear
    on why you should have to keep the original pages around however....

    A workaround for Tiger might be to call the private -[setDocument:]
    call yourself after adding the page to a document.  Something like:

    [someDocument insertPage: originalPage atIndex: i];
    [[someDocument pageAtIndex: i] setDocument: someDocument];

    This should have no ill effect on Leopard.

    john calhoun—
  • On 03.01.2008, at 22:41, John Calhoun wrote:
    > A workaround for Tiger might be to call the private -[setDocument:]
    > call yourself after adding the page to a document.  Something like:
    >
    > [someDocument insertPage: originalPage atIndex: i];
    > [[someDocument pageAtIndex: i] setDocument: someDocument];
    >
    > This should have no ill effect on Leopard.

      OTOH, you may as well check the system version and only do it on
    Tiger. That way, you won't have to worry that it may break future
    versions...

    Cheers,
    -- M. Uli Kusterer
    "The Witnesses of TeachText are everywhere..."
    http://www.zathras.de
  • On Jan 9, 2008, at 12:44 PM, Uli Kusterer wrote:

    > On 03.01.2008, at 22:41, John Calhoun wrote:
    >> A workaround for Tiger might be to call the private -[setDocument:]
    >> call yourself after adding the page to a document.  Something like:
    >>
    >> [someDocument insertPage: originalPage atIndex: i];
    >> [[someDocument pageAtIndex: i] setDocument: someDocument];
    >>
    >> This should have no ill effect on Leopard.
    >
    >
    > OTOH, you may as well check the system version and only do it on
    > Tiger. That way, you won't have to worry that it may break future
    > versions...

    The only Tiger system that I have available is on a PPC G4, the
    suggested fix did not work
    there... ( but did no harm on my Leopard box, except for an undefined
    value at compile time)

    Jerry
  • On Jan 9, 2008, at 10:21 AM, Jerry LeVan wrote:
    > The only Tiger system that I have available is on a PPC G4, the
    > suggested fix did not work
    > there... ( but did no harm on my Leopard box, except for an
    > undefined value at compile time)

    I'm afraid I lost the original problem.  Can you describe the bug?

    John Calhoun
  • On Jan 14, 2008, at 6:20 PM, John Calhoun wrote:

    >
    > On Jan 9, 2008, at 10:21 AM, Jerry LeVan wrote:
    >> The only Tiger system that I have available is on a PPC G4, the
    >> suggested fix did not work
    >> there... ( but did no harm on my Leopard box, except for an
    >> undefined value at compile time)
    >
    > I'm afraid I lost the original problem.  Can you describe the bug?
    >
    > John Calhoun

    Hi John,

    The following works on my wife's PPC running 10.4.<latestVersion>
    Hi John,

    Someone suggested using dataWithPDFInsideRect... that seems to work
    //Tiger code
    NSImageView * pdfView;
    NSData    *pdfData;
    NSMutableArray * tmpPage = [NSMutableArray array];
    int i;
    for(i=0;i< cnt;i++) {
    theImage =[ [NSImage alloc]
                        initWithContentsOfFile: [fileNameList
    objectAtIndex:i]];
    pdfView = [[NSImageView alloc]
          initWithFrame:NSMakeRect(0,0,
          [theImage size].width,
          [theImage size].height)];

    [pdfView setImage:theImage];
    pdfData = [pdfView dataWithPDFInsideRect:[pdfView bounds]];
    [tmpPage addObject:  [[[PDFDocument alloc] initWithData: pdfData]
    autorelease]];
    [myBook insertPage:[ [tmpPage objectAtIndex:i] pageAtIndex:0]
    atIndex:i];
    [pdfView release];
    [theImage release];
    [filesLeft setIntValue: --fcnt];
    [filesLeft displayIfNeeded];
      }

    Here tmpPage is an array that holds each page of the created PDF
    Document....
    If I replace tmpPage by
        PDFDocument *tmpPage;
    so the the same tmpPage is used to add the next object to the myBook
    PDFDocument then
    as soon as the second page is added myBook becomes corrupted (tmpPage
    is released each pass
    through the loop).

    You suggested:

    A workaround for Tiger might be to call the private -[setDocument:]
    call yourself after adding the page to a document.  Something like:

    [someDocument insertPage: originalPage atIndex: i];
    [[someDocument pageAtIndex: i] setDocument: someDocument];

    This caused a crash on the PPC...
previous month december 2007 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
Go to today