CONTENT * Double Encode Commas and Slashes For Cloudinary Text Overlays ** TL;DRAdding text to images in Cloudinary is super handy. But, there's a couple bugs that'll cause you grief if you don't know about them. Specifically:1. Commas must be double encoded in order to work2. The same goes for forward slashes Two `.replaceAll()` statements tacked on to a standard `encodeURIComponent` in JavaScript will do the trick:#+begin_src js :results output :wrap example :cmd "node --input-type=module < "const text = 'a,b,c/d/e'const cloudinary_encoded = encodeURIComponent(text) .replaceAll(/%2C/g, '%252C') .replaceAll(/%2F/g, '%252F')const parts = [ `https://res.cloudinary.com/demo`, `image/upload/w_180,h_80,o_0`, `l_text:Ubuntu_32:${cloudinary_encoded}`, `fl_layer_apply/horses.png` ]console.log(parts.join('/'))#+end_src#+RESULTS:#+begin_examplehttps://res.cloudinary.com/demo/image/upload/w_180,h_80,o_0/l_text:Ubuntu_32:a%252Cb%252Cc%252Fd%252Fe/fl_layer_apply/horses.png#+end_exampleIn Python, the fix looks like this:#+begin_src python :results output :wrap exampleimport urllib.parsetext = 'f,g,h/i/j'cloudinary_encoded = urllib.parse.quote(text)cloudinary_encoded = cloudinary_encoded.replace('%2C', '%252C') cloudinary_encoded = cloudinary_encoded.replace('/', '%252F')parts = [ 'https://res.cloudinary.com/demo', 'image/upload/w_180,h_80,o_0', f'l_text:Ubuntu_32:{cloudinary_encoded}', 'fl_layer_apply/horses.png' ]print("/".join(parts))#+end_src#+RESULTS:#+begin_examplehttps://res.cloudinary.com/demo/image/upload/w_180,h_80,o_0/l_text:Ubuntu_32:f%252Cg%252Ch%252Fi%252Fj/fl_layer_apply/horses.png#+end_exampleFiguring out what was happening involved lots of WTF moments. I hope these snippets save you from that.-a** DETAILSI use Cloudinary to automatically generate the Open Graph (OG) social media images for my sites. The process is mostly straight forward. Just use the standard tools to encode your text for a URL and you're off to the races... until you use a comma... or a slash.Cloudinary uses commas to separate commands for their system. For example, a URL with `w_1200,h_630` uses the comma to separate the commands to set the width and height from each other. Using a comma directly in a text string would confuse the system. It would think a new command was coming then melt itself when it tired to figure out how to execute your text as a command.Ideally, encoding a comma with standard language tools would fix this. For example, running a comma through the default tools turns it into the string `%2C`. This is the same type of thing it does with spaces which get turned into `%20`. While Cloudinary handles the spaces and other encodings properly it pukes on the `%2C` version of a comma. ** FIXESTwo workarounds for the issue are:1. Double Encoding (as shown in the snippets above)2. Switching to an encoded version of an alternate comma character (i.e. `%E2%80%9A`)In either case, adding extra handling to the encoding is required. I go with the double encoding option because it sticks with the standard comma character. Cloudinary offers [[https://www.alanwsmith.com/posts/google-fonts-you-can-use-in-cloudinary-transformations--26mqi8ovvtka][a bunch of fonts]]. While I'd like to think the alternate comma is available in all of them my experience throws red flags considering that option. The specifics of the double encoding are to turn this:#+begin_example%2C#+end_exampleinto this:#+begin_example%252C#+end_exampleI do that with the replaceAll/replace methods show above.** ALSO THE SLASHThe comma isn't the only character with an issue. The same type of bug affects the forward slash (i.e. `/`). Happily, the same double encoding approach fixes it by turning this:#+begin_example%2F#+end_exampleInto this:#+begin_example%252F#+end_example** CHECKING THE RESTI ran into the commas first and fixed them. A while later, I ran into the slashes. Those are fixed now, but I don't want to be surprised again. So, I put this together to check other symbols.#+NAME: checker#+begin_src python :results outputimport urllib.parseline = """, ` ~ ! @ # $ % ^ & * ( ) . { } < > ? / : ; ' " | \ - _ [ ] = +"""for part in line.split(): encoded = urllib.parse.quote(part) encoded = encoded.replace('%2C', '%252C') encoded = encoded.replace('/', '%252F') img_url = f"""https://res.cloudinary.com/demo/image/upload/w_100,h_100,o_0/l_text:Ubuntu_32_bold:{encoded}/fl_layer_apply/horses.jpg""" print(f"""