Embedded Markdown images

In another topic recently in this forum, another user requested a feature whereby DEVONthink would (1) copy images to a user-designated folder when images are dragged over a Markdown document and then (2) put a Markdown link to the image at the point where the user dropped the image over the document.

I’d like to propose a related capability: an option to have DEVONthink embed the image in the document using data: URIs and Markdown reference syntax when you paste an image at a point or drag an image to a document. Here’s an example of what I mean using DEVONthink 7 (which already supports the necessary syntax and rendering). On the left side of this example, the image is referenced using the normal syntax ![image title][tag], and the value for tag is placed at the end of the file, but instead of being a URL to an external location, the reference contains the base64-encoded image data right there in the file.

I put a copy of this example file here.

This approach has disadvantages; for one, the embedded image data is huge, and for another, it’s extremely error-prone to manipulate the image data once it’s pasted into the file. But, the approach has advantages too: the image and the content are all together in one file, and the behavior of pasting an image into the document (if supported by DEVONthink via the user interface) would resemble the behavior of pasting an image into (e.g.) RTF documents and also the behavior of some other software applications. Importantly, note that this is entirely a user-interface addition: nothing needs to be changed in the handling of Markdown because DEVONthink already supports the syntax and the rendering, as demonstrated above. Indeed, you could write AppleScript to do this now, although I haven’t tried. (Clarification added 2021-05-09: The overall behavior would require scanning the document for existing references to make sure the new reference tag/identifier is unique in the file, then pasting the image data at the end of the file, and finally, inserting a markdown-style reference at the point of insertion. My comment about AppleScript is about the overall process, not just converting images.)

I realize this is not for everyone, which is why I’m suggesting it’s only an option, set in the user preferences. I think it would have benefits when creating short, quick notes, when images are small and not worth saving as separate files.

@pete31 to the rescue?

Copy an image,
run script,
paste in Markdown

-- Set clipboard to base64 encoded image string

-- Copy an image, run script, paste in Markdown

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use scripting additions

	set thePasteboard to current application's NSPasteboard's generalPasteboard()
	set thePasteboardItems to thePasteboard's pasteboardItems()
	if thePasteboardItems's |count|() = 0 then error "Empty clippboard"
	set thisPasteboardItem to thePasteboardItems's objectAtIndex:0
	set theImageData to thisPasteboardItem's dataForType:(current application's NSPasteboardTypeTIFF)
	if theImageData = missing value then
		set theImageURLString to thisPasteboardItem's stringForType:(current application's NSPasteboardTypeFileURL)
		set theImageURL to current application's |NSURL|'s URLWithString:theImageURLString
		set theImageData to current application's NSData's dataWithContentsOfURL:theImageURL
	end if
	set theImageData_encoded to theImageData's base64EncodedDataWithOptions:0
	set theImageDataString to current application's NSString's alloc()'s initWithData:(theImageData_encoded) encoding:(current application's NSUTF8StringEncoding)
	set theMarkdownImageString to (current application's NSString's stringWithString:"data:image/png;base64,")'s stringByAppendingString:theImageDataString
	thePasteboard's clearContents()
	(thePasteboard's setString:theMarkdownImageString forType:(current application's NSPasteboardTypeString))
on error error_message number error_number
	display alert "Error: Handler \"Hmm\"" message error_message as warning
	error number -128
end try

@chrillek LOL, and @pete31, I really appreciate your seemingly inexhaustible willingness to parachute in, AppleScript in hand :slight_smile:. However, I fear my original request is unclear and misunderstood as being only about pasting images in base64 format. I apologize and will edit the posting. There is a bit more to the idea. I actually already have a method to convert an image on the clipboard to base64 [1], and can do the process you describe (copy image, convert clipboard, paste). I’m hoping DEVONthink would implement a more complete solution that would work something like this:

  1. user has an image on the clipboard
  2. user clicks somewhere in the document at the point they want it inserted, or drags an image to the document and releases the mouse button at the point they want it inserted
  3. DT pops up a dialog asking the user for a caption for the image, with a pop-up dialog that includes a cancel button. If the user cancels at that point, the process is interrupted.
  4. DT looks through the markdown document for reference-style links, and stores the id’s of any links found
  5. DT goes to the end of the file, inserts a blank line, and inserts the base64-converted image data using the syntax [id]:data:image. The id needs to be unique in the file. One way to do that is to use a base name, say embeddedimage, and add a number to it to make the identifier unique in the file based on what DT found in step #3. Example: if the file already contains embeddedimage1, then DT would use embeddedimage2.
  6. DT inserts a markdown-style link at the point where the user clicked in step #1, using the format ![caption][id], where caption is the string that the user typed in step #2.

What I meant in my original posting is that, in principle, one could write AppleScript to do most of this entire process, not just the base64 encoding part. (And no, this is not a request for @pete31 to write it :slight_smile: :-).) The thing is, I don’t think there is a way to implement the part in step #2 where the user could drag-and-drop an image over the document. And since DT already has a different behavior when the user drops an image over a document, DT would need to introduce a user-configurable preference to let the user choose between creating a link and embedding an image as the behavior in that situation.

[1] My current method of converting images to base64 uses a shell script and macOS command-line utilities. I invoke it via a KM macro. I think I’ll switch to this AppleScript-based approach by @pete31, so again, thank you!

I understood what you’re after. It of course wasn’t meant as replacement for your request, should have made that clear. Only DEVONthink’s developers can do what you want. :slight_smile:

That’s something I wanted to try for a very long time. As far as I know it seems to be quite difficult (for a scripter).

+ 1 for your request

1 Like

I find your idea charming, though I’m afraid it will make it easier for people to shoot themselves in the foot. You mentioned the pitfalls and drawbacks already… At least, the whole process should keep the images in their original format and at their original places.

A guy can dream, can’t he? :wink:

The weird thing is, I thought that I came across a Markdown editor that supports this kind of embedding, but I cannot find it now. Editors like Typora make it easy to paste images into a document, but they copy the image to some designated location, which is not the same thing. (However, one can manually do the base64 approach and it will work.)

In searching just now, I did accidentally come across a couple of related tools. Just mentioning them here in case someone finds them useful:

(As an aside, I actually dislike Markdown and wish it hadn’t become so popular that every software application is now turning into a Markdown editor. Aside from Markdown not being a well-defined, formal standard, it conflates two things: a plain-text markup syntax for writing by humans, and a document storage format. The two don’t have to be the same, and IMHO the problems we face with embedding images is a perfect example of why it would have been better to define a separate format for the file used to store documents written in Markdown syntax. It would have been easy to define a simple container format in terms of, say, a Zip archive. I’m not saying this just because I was involved in doing something similar for another purpose; Word .docx are exactly this, and OmniOutliner uses this approach, and there are other examples. It would allow attachments to be bundled easily, without compromising the ability to use Markdown as a simple markup syntax for writing. But instead, we’re all storing Markdown as flat text files and going through contortions to do something as simple as paste an image into a document.)


Where do you see Markdown advocated as a “document storage format”?

Embedding images comes with its own problems, including out of control file sizes.
Linking is a much more affordable option which allows for flexibility and late-binding changes to be easily made.

The big advantage of MD over the formats you mention is obviously it being free (and not in the sense of docx with its hundreds of pages of definitions) and also simple.
Implementing a docx compatible program is a hell of a lot of work for a moving target. MD in contrast is simple and probably easily implemented. And it separates semantics from representation, which docx does not do.

I agree that it’s not a well defined standard, of course. But it standing on the shoulders of n/groff, (La)TeX and other such pure text formats makes it useful for me. I do not really care about images in my writing: They come at a much later stage and it’s other people who deal with them in Indesign and similar software.

Where do you see Markdown advocated as a “document storage format”?

I’m not sure if the sense of your question is focused on the advocate part, or the concept of Markdown as a document storage format. The latter is simple: pretty much everyone stores content written in Markdown syntax in a text file. With respect to the former, I guess it’s not “advocated” explicitly so much as a pervasive assumption. Gruber, in his 2004 blog post, wrote this:

Thus, “Markdown” is two things: (1) a plain text formatting syntax; and (2) a software tool, written in Perl, that converts the plain text formatting to HTML.

There’s an implicit assumption that the document storage format is a plain-text file that uses Markdown syntax.

Regarding your point about embedded images coming with problems, I do agree, and I did mention this point in my original posting. I think the embedded-image approach is something that can be helpful in some situations to overcome the (to me) higher cost of managing images as separate files. Having a user preference to control the behavior (and making the default be linking instead of embedding) would be an important complement to this new feature, if it were added.

Implementing a docx compatible program is a hell of a lot of work for a moving target. MD in contrast is simple and probably easily implemented. And it separates semantics from representation, which docx does not do.

I was afraid mentioning docx would be a distraction. I agree completely with you on these points; I only mentioned docx as an example of using a zip file to encapsulate things, that’s all :slight_smile:.

Markdown is undeniably an excellent choice for DT and a lot of other things. And DEVONthink’s recent updates, using MultiMarkdown syntax, transclusion, and so many other things, is just fantastic. I’m a committed DEVONthink user.

Handling images, though, remains a sore point (IMHO). Setting aside my rant upthread, my original point in this discussion topic is to try to find a way to improve this.

To this day, I use a group structure to house my Markdown document and it’s resources, including images. Often I will put them in an images subgroup as well. Neat, clean, and portable when using relative links.

1 Like

@BLUEFROG I use this approach too sometimes (and it was a former posting of yours that made me try it in the first place). Sometimes, though, it’s difficult to do that. For example, the ability to use markdown files for DT annotations on an item is great, but the current scheme only allows for using a single file, not a group or a script to create a more complex structure. The annotation file created by Dt is put into a single group with all other annotations for other documents, which means that if you want to do something involving images (e.g., if you want to include an image clipped from the document within the text of your annotations), then unless you want to dump them into the same group containing all your annotations, you have to manually create a new group, move the Dt-created annotation file into it, save the clipped image as a separate file, etc. That’s a notable amount to hassle that will break your concentration and flow (or it does mine, anyway). By contrast, if I could copy a region of my screen and paste it into the same annotation file, things would just go more smoothly.

@cgrunenberg: perhaps this is another use case where Annotation files would be better created in the same group as the original file.

Err, except, in my case this wouldn’t work either :-(. I use a smart group to create a single list of all the PDFs located in a hierarchy of folders created by another application (Zotero), and when I create an annotation on a PDF file, I’m looking at that PDF file from the smart group. Creating the annotation file in the same group as the original file (here, the PDF) would put it in the indexed folder multiple nested levels down, buried somewhere in a structure that is not at all like the way the rest of my DT database is organized – definitely not a desirable location for the annotation file, in my case.

(Yeah, I know … all these special needs and requests. Users are never satisfied.)

Have to disagree that Markdown has anything to do with being a “document storage format”. Its only a plain text markup language (as you also noted). The only thing Markdown gives is a format to create links to images, ie: more markup. After that all bets are off.

But, some apps support TextBundle to do exactly what youre talking about. http://textbundle.org/


It seems very good to add support for TextBundle format

1 Like