gonzalezreal / swift-markdown-ui

Display and customize Markdown text in SwiftUI
MIT License
2.26k stars 267 forks source link

NSTextAttachment not rendering when using NSAttributedString(document:, style:) #37

Closed santi-g-s closed 2 years ago

santi-g-s commented 3 years ago

Hi,

I have a use case in my app where I need to extract the NSAttributedString from markdown text. I wanted to use the parser included in MarkdownUI as I think it performs better than others out there. However, I'm struggling to understand why images aren't working properly. It was my understanding that they were included in the NSAttributedString as NSTextAttachments.

Below is a simple test to highlight the issue. Is there any way you could shed some light on this?

Many thanks again!

import SwiftUI
import MarkdownUI
import AttributedText

#if os(macOS)
public typealias PlatformFont = NSFont
#else
public typealias PlatformFont = UIFont
#endif

struct ContentView: View {

    var text = """
    # Let's see if this works

    Image should appear here:
    ![image](https://endlessicons.com/wp-content/uploads/2012/11/apple-icon.png)
    """

    let markdownStyle = MarkdownStyle(font: PlatformFont.system(size: 12))

    var body: some View {
        VStack{

            AttributedText(NSAttributedString(document: Document(text), style: markdownStyle))

            Markdown(Document(text)).markdownStyle(markdownStyle)

        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
gonzalezreal commented 3 years ago

Hi @santi-g-s,

This is the expected behaviour. Notice that the NSAttributedString extension does not load images asynchronously. Instead, it provides an optional attachments parameter that you can use to associate existing text attachments with image URL strings:

https://github.com/gonzalezreal/MarkdownUI/blob/079e08576d506fbc42184f175b2ca8a898da9a30/Sources/MarkdownUI/Shared/NSAttributedString%2BMarkdown.swift#L14

The Markdown view uses a MarkdownRenderer object, which performs all the asynchronous image loading and then calls the NSAttributedString extension providing the text attachments.

https://github.com/gonzalezreal/MarkdownUI/blob/079e08576d506fbc42184f175b2ca8a898da9a30/Sources/MarkdownUI/Shared/MarkdownRenderer.swift#L37-L60

santi-g-s commented 3 years ago

Thank you so much @gonzalezreal. I really appreciate your patience and help with this.

To my understanding, I should most likely use MarkdownRenderer.attributedString to extract the attributedString with loaded NSTextAttachments. However, when I try to access MarkdownRenderer I get a "Cannot find type 'MarkdownRenderer' in scope" error. Is there something else I need to import?

Many thanks, Santiago.

gonzalezreal commented 3 years ago

No problem!

The reason you're seeing that error is because the MarkdownRenderer class is not public. Can you elaborate on your use case and why you need to use the NSAttributedString extension instead of the Markdown view?

santi-g-s commented 3 years ago

Well I want to be able to export to PDF and word docx. Currently I create the word docx via NSAttributedString which then gets converted to the required DocumentType data using NSAttributedString.data(). The PDF is created using UISimpleTextPrintFormatter which also takes an NSAttributedString as an input.

Elsewhere in my app I need the unformatted plain text of the markdown output which I get from NSAttributedString.string.

Would it be possible to make it public? I realise I could use Down or some other libraries out there but I would prefer to keep it with MarkdownUI and use it's MarkdownRenderer.

Hope that's helpful.

Many thanks.

gonzalezreal commented 3 years ago

I am not sure is a good idea to use this library for your use case. MarkdownUI solves the problem of rendering markdown in a SwiftUI view tree, which is very different of what you want to achieve.

It's true that you can use the NSAttributedString extension for your use case, but you will need to provide the image downloading and text attachment logic. Notice that MarkdownRenderer is doing other things besides downloading the images, like resolving relative urls to an environment-provided base url or instantiating a special NSTextAttachment subclass that resizes the image to the width of the view, which is not necessarily tailored to a more generic rendering approach like in your case.