How to get an accessible PDF out of Google Docs (with free tools)

Today, I learned that Google Docs doesn't save accessible PDFs, even if you conscientiously wrote the doc accessibly. I.e. With the correct heading structure, lists that are actual lists, tables that are tables, figures, captions, alt text, oh my!

Instead, when exporting to PDF, Google Docs strips all accessibility-related information, resulting in an untagged PDF.

This was rather annoying to me, since I absolutely needed this particular document (a VPAT) to be accessible while in PDF form.

Poking around the interwebs, I came to the conclusion that most PDF accessibility remediation tools are one of the following:

  • Paid and expensive
    Adobe Acrobat Pro, I'm looking at you. To be fair, it isn't just Adobe that charges rather a lot.
  • Free, but don't work at all, or don't work very well
    Pave-PDF was an example that didn't work for me at all... even when it finally loaded my document.
  • Free, but insert watermarks, and possibly don't work
    PDFix allowed me to tag my PDF, but I couldn't quite trust that it was working. Especially since PDFix finds "no bugs" with a PDF that... has no tagged content. To be clear, a PDF with no tagged content is not accessible. Plus, it inserts watermarks.
    We'll get back to PDFix in a moment though - it does come in handy.
  • Free, very possibly good, but Windows only
    My work machine is a Mac.

Enough with the complaining - tell me how to get that accessible PDF!

It's really simple, but I didn't find anyone else laying out the exact steps, so here they are. Every article or answer I found assumed access to specific paid tools, which I don't have (MS Word, Acrobat Pro).

  1. Ensure your base Google Doc has been authored accessibly.
    If it isn't, make it so. I.e. use the correct heading structure, add captions to your images, etc. This is 95% of the work, "pre-done", almost. And if you need to fix stuff, it's easiest to fix it in your base Google Doc, rather than attempt it with any free tools.
  2. Export your base Google Doc as a MS Word .doc. Yup.
    Because interestingly enough, when you export a Google Doc as an MS Word file, it preserves all of that tasty tasty accessibility information that you've included.
  3. If you have access to MS Word, open the file, and THEN export it as a PDF.
    According to the interwebs, this should give you a nice, clean, accessible PDF.
  4. If you're like me, and don't have access to MS Word...
    Download the Adobe Acrobat extension for Chrome - it's free.
  5. In Adobe Acrobat extension for Chrome, choose the "Convert to PDF" option, and select your exported MS Word file.
  6. Wait for your file to convert, then download it.
  7. Congratulations! You now have a nice, accessible PDF!

    BUT WAIT... How can you be sure it worked?

  8. On a Mac, right-click your nice (and hopefully) accessible PDF, and "Get info".
  9. Look for "Tagged PDF" - if it says "Yes", it worked!

    But what if you want to look at the tags, to make sure they're all legit?
  10. Download and install PDFix.
    You're not going to use this to fix your PDF (because that'll make watermarks, among other issues), you're going to use it to check your PDF.
  11. Open your file in PDFix, and click on the "Tag" icon.
    This pretty much works exactly the same way as Adobe Acrobat Pro, except that it's free. ;)
  12. Check out the tags in your (hopefully) lovely, accessible PDF. :D

LLMs: The best software development tutor ever - with big caveats

I've recently started learning Python for fun, and I've manually copy-typed my way to my very first Streamlit app - CatGPT Nekomancer!

In the process, I've discovered some fascinating things about Large Language Models (LLMs) like ChatGPT, and how they fit into learning a new programming language.

I'm particularly tickled by how I (mostly) implemented CatGPT Nekomancer by blindly following instructions, and then used it to understand what I'd done. There's just something magical about making something, and then having it teach you how you made it.

LLMs are great at explaining what a piece of code does

This is because they're functioning purely as "translators". The translation task plays to the strengths of LLMs - statistical pattern matching. Good judgement is not needed, because we're not concerned with "how" or "should".

Here's an example of CatGPT explaining its source code, in response to the prompt "explain what this code does", followed by the code.

This explanation was a little too high-level for me. I wanted to really understand what each line of code was doing. This was easily fixed with a different prompt: "explain this code line by line to a novice programmer". This gave me exactly the level of detail I needed.
I've worked with some pretty great software developers from all over the world who aren't native English speakers. My partner and I are both bilingual, and we'd previously experimented a little with using LLMs as translators for recipes, where we found that if we were able to avoid the LLM's tendency to interpolate (aka "hallucinate" aka "lie"), they do much better at translating than Google Translate.

So when my partner asked, "What if we get it to explain in Croatian? This would have been HUGE for me when I was learning," it was a no-brainer to give it a try with this prompt: "explain this code line by line to a novice programmer, in Croatian." For Croatian, at least, my partner verified that CatGPT's translation and explanation was "brilliant".

I can also ask the LLM to explain specific parts of the code that I don't understand, and learn new concepts that way. For example, I wasn't familiar with the concept of f-strings in Python, which I encountered when working on a different Python experiment. Thanks to CatGPT, I was very quickly able to understand that f-strings are strings that can hold expressions - nifty! I particularly love how the LLM fits so seamlessly into my personal learning "flow". Instead of having to go off to the greater interwebs to trawl through answers about f-strings in Python to figure it out from there, I have my own personal tutor.

LLMs increase the value that good software developers bring to the table

It's generally agreed - at least among software development managers and similar roles - that a good developer can pick up a new language pretty quickly and competently. That's because the core of what makes a good developer isn't the knowledge of a particular language. Rather, it's their grasp of transferrable concepts, frameworks, and understanding of best practices as principles. It's not about rote memorisation - it's about good judgement powered by understanding and experience.

Before LLMs exploded on the scene, a good developer could have everything I listed above, but when picking up a new language, or working with one they're rusty at, there'd still be a lag due to needing to learn the basics of the language (syntax, etc). With LLMs that lag is much smaller, allowing the developer to bring their strengths to bear much faster.

Caveat 1: LLMs are bad at advising on how a feature or function should be implemented

LLMs are based on statistical pattern matching, which makes them great at translation. It's also what makes them bad at anything that requires judgement calls based on a larger and often ambiguous context. They're not always wrong about "shoulds", they're just right far less often than the average human developer.

I believe that this is also what makes LLMs very weak at software development stuff that's presentational, or isn't primarily about logic. HTML, CSS, and web accessibility all fall into the bucket of not being about logic, as well as operating in a large and ambiguous context. It also probably doesn't help that LLMs have ingested the interwebs, and even today, there's probably loads more sites styling button text with <span> tags than sites using the correct approach. It's not like the LLM can tell which approach is better. After all, it's not thinking - it's pattern matching based on statistics.

Caveat 2: LLMs can't coach or mentor

Real "personalisation" is needed for coaching and mentoring. Both of these require human judgement and experience about the subject matter, as well as the individual receiving the coaching or mentoring. They also require (arguably to a lesser extent) a wish on the part of the coach or mentor for the person they're working with to learn and succeed. The simulated thing that we've come to call "personalisation" (e.g. a script grabbing your name from a database) does not and cannot work in this context.

The key to using LLMs effectively when learning software development: Know what you need

If you need explanations on what a piece of code does, LLMs are a great and reliable help. Even more so if you're not a native English speaker - LLMs can function as your own personal translator for both language and code.

If you need good advice on what you should implement, then LLMs aren't going to help much. Quite the opposite. Since they lack judgement (indeed, they do NOT judge), what they come up with is likely to be misguided at best.

It all comes down to the age-old "common sense" wisdom of using the right tools for the job. :)


I'm proud to say that today, I came up with a new term for what I usually call "pplshit"! :D

Now, I call it "pplshit" because it doesn't come naturally to me, and it's not something I love doing. I've gotten decent at it over the years, within certain boundaries - enough so that I can provide some degree of training/coaching in that area.

Which has lead me to the realisation that when I talk to someone who likes doing that stuff, or I'm trying to train or coach someone to be better at it, then the name "pplshit" is probably not the most inspiring one.

And so I I will now henceforth call "pplshit"... "peoplecraft"!

The art of working within organisational and personal environments and dynamics to nudge and bind disparate teams and stakeholders into effectively collaborating on shared goals.

Snow on the sahara

"Ma'am, I think we have a problem. This is a still from some footage captured by Midge Ourney. You know that Nat Geo photographer who's on shooting on location in Jebil park for the next three weeks."

"Is this some kind of joke?"

"No Ma'am. We've got multiple reliable corroborating witnesses. Quite a few of them are park rangers."

"But that's impossible! Even with climate change. It's got to be a hoax."

"Yes, that's what I thought too. But last night, one of our best people sent me this. They spotted this woman in Toual el-Hadhali and managed to snap a photo. Don't worry, they weren't spotted

"And you're sure this isn't a coincidence? Maybe some kids were having one of those dress-up party things, what do they call it, co-playing?"

"Afraid not."

"All right, thank you. Looks like we have a situation here. Just when things were finally starting to calm down too."

Vegan cheese foam

But Nugget, aren't you a carnivore who loves cream?

Yes, I is! The nugget wasn't setting out to create a vegan cheese foam, it just sort of happened based on other requirements.

The foam is really stable, pretty fuss-free to put together, AND it tastes like cheese foam! All the cheese foam recipes I found either required planning ahead (softened cream cheese), or wouldn't taste like cheese at all. I like coconut milk, and I like maple syrup. I am completely unconvinced that the combination of the two tastes like any sort of cheese. :P


  • Must not require planning (pft, wait for cream cheese to soften, pft).
  • Should ideally use ingredients with a long shelf life.
  • Must be brainlessly easy to assemble. Also fast.


  • 50 ml soy milk (I recommend Bonsoy, or Vitasoy manufactured in Taiwan)
  • 2 tsp white sugar (don't use a syrup, white sugar gives this its structure)
  • 1/4 tsp salt (or to taste)
  • 1/2 tsp nutritional yeast <-- this is what makes it taste like cheese

Make it!

  1. Put all ingredients in a microwave safe container, tall enough to whip the stuff in (you want a cheese foam after all).
  2. Stir until combined.
  3. Warm in microwave in 15s intervals (you want it warm, or hot, not boiling).
  4. Whip / froth with milk frother (I recommend Aerolatte) about 20-30s, you should have a stable foam.
  5. Pour on top of drink!

Notes on ingredients

  • Bonsoy, and the Vitasoy made in Taiwan do not contain oil. Soy milk w/o oil is what I grew up with, and I intensely dislike the mouthfeel of plant "milks" that have added oil.
  • You can probably substitute another plant milk for soy milk, but ideally choose one w/o a strong flavour.
  • If possible, choose a plant milk w/o the added oil (this could just be my prejudiced young age imprinting speaking haha).
  • Nutritional yeast can be found a most health food stores. It makes things taste like cheese w/o cheese being added. It's shelf stable, and doesn't need to be refrigerated.
  • Don't combine cow milk AND soy milk foam. The cow milk in the drink will kill the foam structure of the soy milk foam really fast.
  • You can substitute plant milk for cold heavy cream, whipped to soft peaks.
    When using heavy cream, don't heat it in the microwave, or it won't whip happily. Just stir the ingredients in, then whip. You will need to carefully spoon and ladle the foam onto your drink, and it may not stay stable for long if your drink is hot. Use dairy in your drink too (see previous point about cow milk killing soy milk foam).
  • Aerolatte milk frothers are awesome. I've had mine for almost 10 years now. I bought another one from a diff brand to use in the office a few years ago, and it's terrible. It doesn't make nice froths! 

It's a bit depressing that the co-bot-art that's mostly bot is better than I ever was...

...but I was never that good anyway. Oh wells!

Final composite

Manual retouching and merging by the nugget.


Prompt: dark skinned magpie woman wearing intricate silver jewelry, trending on artstation, uplight

Real-ESRGAN Inference Demo

This is actually GFPGAN - for some reason the Colab page seems to be titled differently. GFPGAN is for face restoration.

Midjourney is amazeballs. Prompt was "crow girl, trending on artstation".

Midjourney + light retouching

Midjourney isn't great at noses, so that was where the retouching was needed. Very simple job of masking the original nose with the retouched nose.

Midjourney original

Retouched version

Derived from running the original through an image restoration generated adversarial network - Real-ESRGAN Inference Demo. (This is actually GFPGAN - for some reason the Colab page seems to be titled differently. GFPGAN is for face restoration.)

 ^If you want to use this, you need to log into your Google account, and make a copy of it. Not 100% sure you have to make a copy, but you do need to be logged into your Google account.

Print quality is just another AI away

Cupscale to the rescue! Haven't added the output here, for obvious reasons. But after running the retouched version through Cupscale, I ended up with a 60MB PNG file that's super sharp even at 100%. No artifacts.

Bear and Nugget

I drew these years ago as part of our submission to Immigration for the bear's partner sponsorship visa. In Australia, Immigration requires you to write essays about each other, and "your life together". I figured essays must get kinda boring, so I added cartoons too.

...and then after a loooooooooooooooong pause (laziness, the sponsored visa was approved ages ago), here's a new one! I really like pruning and weeding. <.<; The observant will notice that in 6 years, we both grew 2 extra fingers...