joshdholtz / DeckUI

Swift DSL for writing slide decks in Xcode
MIT License
601 stars 27 forks source link

Add export options #9

Open tahirmt opened 1 year ago

tahirmt commented 1 year ago

I think it would be nice to be able to export the deck to a PDF to share after the presentation with people.

joshdholtz commented 1 year ago

Yes! I don't know the best way to do this yet but totally agree with this 😊

I attempted to detach the DSL as far away from SwiftUI as I could so that a PDF or HTML or whatever could be generated from the Deck definition without needing to use SwiftUI.

I might still use SwiftUI to generate the PDF but I also might not but I would love this feature!

0111b commented 1 year ago

I can prototype something. There is ImageRenderer class but it is available only on SwiftUI 4 (macOS 13/ iOS16). So I probably do something with NSView.dataWithPDF/Layer.renderInContext

joshdholtz commented 1 year ago

I'd be fine with limiting the PDF export to macOS 13 and iOS16 if its much easier 😇

0111b commented 1 year ago

I made some digging here and prototyped approach described above. I believe this not gonna work because of the following reasons:

I think we need to write own render engine for DSL with the help of CGContext.init(consumer:mediaBox:auxiliaryInfo:) and related API's.

mortenbekditlevsen commented 1 year ago

Thanks for an awesome library! :-)

Yesterday we held a local Swift Developer meetup in Aarhus, Denmark - and I used DeckUI for my presentations. :-)

It was a really good experience - I made a small Playground app on my iPad and presented my slides from there - and exported it to the mac to have a backup for presenting from there.

Today I wanted to share the slides - and while sharing the code is also fun, I thought I would have a go at PDF generation.

My shot at this included:

  1. Have the Presenter take an optional Binding in order to let the user of DeckUI control the current slide index from the outside.
  2. Have a value on deck for returning the number of slides (I don't know where this should be located, likely not on the deck since this required executing the closure creating the slides in order to just get the slide count)
  3. Use the following snippet to render the PDF from 'outside' of DeckUI
func render() -> URL {

    let url = URL.documentsDirectory.appending(path: "output.pdf")

    var box = CGRect(x: 0, y: 0, width: 1366, height: 1024)

    guard let pdf = CGContext(url as CFURL, mediaBox: &box, nil) else {
        return url
    }

    for index in 0..<deck.slideCount {
        let renderer = ImageRenderer(content:
            Presenter(deck: deck,
                      defaultResolution: (width: 1366, height: 1024),
                      showCamera: false,
                      index: .constant(index)
                      )
                .frame(width: 1366, height: 1024)
                .preferredColorScheme(.dark)
                .colorScheme(.dark)
        )
        renderer.render { size, context in
            pdf.beginPDFPage(nil)
            context(pdf)
            pdf.endPDFPage()
        }
    }
    pdf.closePDF()
    return url
}
  1. Code samples included in SwiftUI ScrollView was not rendered, so I temporarily disabled wrapping them in a ScrollView. I'm uncertain about how this would be handled best...

It can likely be optimized to not having to recreate the Presenter on each iteration of the loop and instead have this functionality internally.

I'd love to help implementing this feature into the library. I don't know if any of the contributors have ideas about how to handle stuff like the ScrollView disabling...