Fallback handling for $image widget

Give this a try:

\procedure object(src, width:"100%", height:"100%",params,fallback:"cannot load <<src>>")
<$let type={{{ [<src>get[type]] }}}
       pre={{{ [[data:]] [<type>] [[;base64,]] +[join[]] }}}
       uri={{{ [<src>get[_canonical_uri]] ~[<src>get[text]addprefix<pre>] ~[<src>prefix[http]] }}}>
<% if [<uri>!match[]] %>
<object data={{{ [<uri>addsuffix<params>] }}} type={{{ [<src>get[type]] }}}
   style.width=<<width>> style.height=<<height>>>
   <<fallback>>
</object>
<%else%>
   <<fallback>>
<%endif%>
</$let>
\end

Usage:

<<object src:"test.pdf" height:"50vh">>

Notes:

  • This procedure can handle ANY type of embeddable object, not just PDFs
  • src can be any of the following:
    • the title of a tiddler containing a _canonical_uri field
    • the title of a tiddler containing embedded base64-encoded binary data
    • an external URL starting with http:// or https://
  • height and width are specified using CSS units
  • params are optional. Use params:"#toolbar=0&scrollbar=0" to hide the PDF toolbar and scrollbar controls
  • fallback can be any wiki content you want displayed if the embed fails to load
    • You could use this to specify a secondary <<embed ...>> macro to display a fallback PDF

How it works:

  • get the tiddler’s type field (if any)
  • construct a prefix (e.g., data:application/pdf,base64,) to use if the tiddler contains embedded base64-encoded binary data.
  • get the uri which is either a _canonical_uri field value or the tiddler’s base64-encoded content (with the added prefix), or a direct external reference to a URL
  • if a URI was retrieved, append the params and embed it. If the embed fails to load, show the fallback content.
  • if a URI was not retrieved, just show the fallback content.

This might not be a 100% perfect solution, but it should handle most use-cases.

Let me now how it goes…

enjoy,
-e

2 Likes

Addendum:

If you are only concerned with a “fallback facility for PDF files” (i.e., only using direct external references), then you can skip the entire \procedure definition and just write something like this:

<object data="test.pdf" style="width:100%;height:50vh;">
   <object data="other.pdf" style="width:100%;height:50vh;">
      cannot load PDF
   </object>
</object>

Note that you can nest <object> elements as many deep as you need, so you could have multiple levels of fallback content.
-e

3 Likes

Thank you @EricShulman for both of your suggestions. Now I see what @TW_Tones was hinting at in an earlier reply.

Is there any documentation for having the same tiddler nested inside itself and activating when the first one fails? I would like to understand it a bit better but can not find anything.

bobj

@EricShulman wont work for me I’m afraid.

tiddler with canonical_uri

Code of procedure, note some <$text displays to see values of variables

Output on calling tiddler

As you can see, the type and urivariables are not being set.

Can’t figure out why.

BTW what are the ~ characters for in the uri={{{ [<src>get[_canonical_uri]] ~[<src>get[text]addprefix<pre>] ~[<src>prefix[http]] }}} statement?

First off… good work in using $text widgets to show the actual parameter values… that is a very useful technique for debugging macro code, and I use it all the time.

Now… as to the specific problem:

I think you’ve made an error in how you are invoking the <<object>> macro. Since you are using a _canonical_uri to point to the external PDF file, the src value you pass to the macro should be the title of the tiddler containing the _canonical_uri field. From your first snapshot it appears that your source tiddler is named:

A Brief History of Employment of Australian Artillery 1871-2021

but your test output shows that you passed in a src parameter of:

A_Brief_History_of_Employment_of_Australian_Artillery_1871_2021.

Since a tiddler title matching that src value does not exist, the macro is unable to retrieve a type field value or a _canonical_uri field value, so it does the default fallback handling which displays the “cannot load …” message.

regarding your “BTW” question: the ~ character is a short form of the :else filter run prefix. Thus, the uri filter basically says:

  • Try to get a _canonical_uri from the src tiddler.
    If there is no such tiddler or it doesn’t have a _canonical_uri value, then
  • Try to get the tiddler’s text field and add the “data:…” prefix.
    If there is no such tiddler or the text field is blank, then
  • Use the src value as an external URI… but ONLY if it starts with http (which covers both http:// and https://)

Note: since you intend to use a relative external URI (i.e., “Media/Ephemera/…”) then you should change that last filter run to just ~[<src>] (i.e., remove the check for an “http” prefix).

Let me know how it goes…

-e

1 Like

@EricShulman , still doesn’t work. The get[type] does not return anything. If I use the filter tab in advanced search, typing [title[A_Brief_History_of_Employment_of_Australian_Artillery_1871_2021]get[type]]

does not return anything even though type is specified as application/pdf. But if I type [title[testpdf]get[type]] it returns abc where abc was a test value I inserted into its type field during debugging.

If I try that code on any other tiddler, I get the type result as specified, ie, image/jpeg for images, etc.

Seems like it doesn’t like type of application/pdf

Addendum: There is somethiing fishy going on. I edited the testpdf tiddler trying different tiddler titles as part of debugging. Returning it back to A Brief History of Employment of Australian Artillery 1871-2021, the type variable is not set but the pdf will not load.

Screenshot 2025-08-16 at 12.04.20

bobj

bobj

As I noted in my previous reply, the src param should not have the underscores since your tiddler title doesn’t have them. Your last test, as shown in your last snapshot using the correct src param value DOES seem to be getting the type field value and calculating the pre value.

However… did you also read my “note” at the end of the last post:

Note: since you intend to use a relative external URI (i.e., “Media/Ephemera/…”) then you should change that last filter run to just ~[<src>] (i.e., remove the check for an “http” prefix).

Thus, the line of code for calculating the URI value should be:

uri={{{ [<src>get[_canonical_uri]] ~[<src>get[text]addprefix<pre>] ~[<src>] }}}>

-e

@EricShulman that appears to work now, at least for my quick test. Will test further and let you know.

I have also kept the prefix[http] statement in the uri calculation and added ~[<src>] so that it hopefully will handle both cases of with and without the http prefix.

I assume that for the fallback, I replace the <<fallback>> parameter with the fallback URL?

Everytime you provide a work around, I learn a bit more so thank you for your patience.

bobj

Using just ~[<src>] will handle both cases (with or without the http prefix), so you don’t need the separate filter run for ~[<src>prefix[http]].

If you want the fallback to attempt to embed a different source, then you would set the fallback parameter value to a complete alternative call to <<object>>. Something like this:

<<object
   src:"SomeTiddlerTitle"
   height:50vh
   fallback:"<<object src:'https://...' height:50vh>>"
>>

Also, keep in mind that if you are only using external PDFs (i.e., no tiddlers with _canonical_uri or base64-encoded binary content), then you can entirely skip the <<object>> macro, and just use nested HTML <object> elements, as previously desribed here: Fallback handling for $image widget - #23 by EricShulman

-e

@EricShulman can you explain why, if I change the procedure call to displayPDF, nothing gets displayed but if I return it to object the pdf appears again?

I thought I’d try and merge this pdf procedure in to the image fallback procedure but I guess I may not be able to do that.

bobj

Exactly what did you change?

The name used in the \procedure somenamehere(...) definition and the name used in the <<somenamehere ...>> invocation must be the same (i.e, somenamehere), so you need to make sure you change both of them.

Note also that, within the code I provided, the <object data=...>...</object> syntax refers to a standard HTML element and should NOT be changed.

-e