PDF viewer

Has someone implemented a pdf viewer in his JUCE project? What solution did you choose?
I need to view a pdf in a component. I cannot use an external application here.

Out of curiosity, I see that the web component can view pdf. Why cannot we have this feature separately?

Rendering a PDF via WebBrowserComponent is a hack since not all native web backends (which is all WebBrowserComponent is) necessarily support PDF viewing. That’s (almost definitely) why it’s not an official feature.

PDF parsing and rendering is complicated, and AFAIK there aren’t any good framework solutions for displaying them in JUCE. I ran into this roadblock myself a while back and ended up exporting parts as SVG graphics and interleaving it with text from the PDF since I had full control of the document (it was an about document). It was extremely awful and I don’t recommend it.

1 Like

Have you considered using a NSImageView inside a NSViewComponent, on OSX, to display the pdf pages?

You can also use CoreGraphics to open and display pdf documents. On Windows there is no such functionality i’m afraid.
https://developer.apple.com/documentation/coregraphics/cgpdfdocument?language=objc

I can display the PDF in a JUCE component now, thanks to the NSViewComponent.
But that does not fit what I need, as the component cannot be overlayed by any JUCE drawing, and I need to draw on top of it. This is for music sheet annotations.
I could maybe convert a NSImage to a juce::Image. It seems there is no tool for that in JUCE. Any idea?

https://forum.juce.com/t/nsimage-to-juce-image

Some years ago I was looking into this as well for sheet music display.
I came across the MuPDF software as a possible option to integrate with a Juce application: https://mupdf.com/
Maybe that is also an option (I think they have an open source and a commercial license)…

4 Likes

Thanks, yes, I saw it a while ago, and since I had compilation errors, I called Next. But I just gave it another try. The errors actually got fixed easy and it seems to do the job, and since it gives me the pixel data, it can easily be used with JUCE.

I am doing nicely on OSX now, using native tools. But I will have to go Windows at some point. MuPdf seems to be the best option.

Edit: it seems to be lacking documentation regarding the use of the libraries.

Could you share how exactly you managed to get that to work?

It was actually very easy!

In case anyone wants to do that: so far I can recommend it! Follow the instructions here to build the library: How to build MuPDF

  • git clone --recursive git://git.ghostscript.com/mupdf.git
  • git submodule update --init (inside working copy)

Compile and install using

  • make prefix=/usr/local install

That worked for me with not trouble what so ever (even on M1 Mac)! Please not, that on M1 /usr/local is no longer used and in order to prevent “everything” from falling apart, I symlinked /usr/local/bin into the new home brew directory. You may need to take this step into account. I just installed this library into a different directory, no trouble what so ever.

Getting the example to run: (https://mupdf.com/docs/examples/example.c) This is probably all you need to just render pdfs in your JUCE app.

  • gcc -I/usr/local/include -o example /usr/local/share/doc/mupdf/examples/example.c /usr/local/lib/libmupdf.a /usr/local/lib/libmupdf-third.a -lm
    I had to adjust the name of “libmupdf-thid”. The tutorial misses the dash char. Adjust the include and library path according to your directory you installed it into using make.

Getting that to work with JUCE in Xcode
You need to perform three steps:

  • Add the /usr/local/include (or whatever) to your header search path
  • Add the /usr/local/lib dir to your library search path
  • Add the two libraries (.a files) to your linking in the build phases.

Making use of that in juce::Component
As the example shows, you end up with a bit map. I converted that manually into an Image (with two for loops – also in the example) and then draw this thing onto the graphics. From here on you unleashed the full power of JUCE when dealing with actually drawing this thing (interpolation, manipulation, etc.)
The only thing I’m still not sure about is the zoom factor. As you can see from the example, you basically have to lock in your maximal resolution when you set the zoom factor. For my full score pdfs, 300 was more than enough, 200 might be good enough as well.
I’ll write back again if I found an elegant way to calculate the zoom factor.

1 Like

for the people interested in using the macosx native pdf reader, here is the main method, i believe it can give enough clue to get it done:

std::shared_ptr<juce::Image> PDFReader::generateImagePage(int aPage, int aHeight) {

    NSPDFImageRep * pdfImageRep = (NSPDFImageRep *)mPdfImageRep;

    float lRatio = fmin(1.f, aHeight / (float)pdfImageRep.size.height);
    NSSize lSize;
    lSize.height = pdfImageRep.size.height * lRatio;
    lSize.width = pdfImageRep.size.width * lRatio;

    [pdfImageRep setCurrentPage:aPage];
    NSImage* scaledImage = [NSImage imageWithSize:lSize flipped:NO drawingHandler:^BOOL(NSRect dstRect) {
        [pdfImageRep drawInRect:dstRect];
        return YES;
    }];

    std::shared_ptr<juce::Image> lImage = std::make_shared<juce::Image>(createJuceImage(scaledImage));
    return lImage;
}

mPdfImageRep being initialised with

    NSString* pdfPath = @(aFile.getFullPathName().toStdString().c_str()) ;
    NSData* pdfData = [NSData dataWithContentsOfFile:pdfPath];
    mPdfImageRep = [NSPDFImageRep imageRepWithData:pdfData];
1 Like

Its indeed very easy to read a PDF on OSX. You can also just use the CoreGraphics c api if you don’t want to go the Objective-c way. You can then use the CGPDFDocumentCreateWithURL and its friends.
Still find it very odd that Windows does not have any a simple API like this, this is typical a thing an os framework should provide.