Ondrej Hudecek

// mixed notes

My projects updates, Aug 2026

Since last time, my little tool text-to-svg.com got a lot of updates. 

  • I updated the UI a bit - the old version was really... old school, I would say. Okay, it was ugly.
  • I also added a "Code to SVG" functionality which I needed for my own purposes
  • I added an option to upload your own font. 
  • Finally, I added a "Share" functionality - It creates a public link, which you can send to someone.

Lately I have been working more and more on svghelper.com, which got a lot of new tools:

  • batch converters to JPG/PNG
  • converters to DXF and STL
  • SVG table generator
  • SVG grid generator

My third little project. compare-fonts.com got no real updates, because I also have to pay the bills somehow. But I have my plans with it.

These are just my little tools. Working on them makes me happy. That's their purpose.

Good bye, Windows

Windows was my go-to OS for 25-30 years (Amiga OS before that), I see no problem with it, it just works and while it has some annoying quirks, it gets the job done and that's all that matters to me. But, my working laptop runs Windows 10 and the update to 11 did not go well. I spent almost a whole day trying everything I could to just update, but I always ended up with "This PC does not meet Windows 11 system requirements". This is bullshit - the machine is i7 11th gen, has 16 GB of RAM and plenty of disk space.

I had enough. I made a backup of important data, formatted the drive and installed Debian 13 with XFCE. I was very afraid to do it, to be honest. I never really used Linux on desktop for more than... an hour, maybe. And every time, I returned to the safe, familiar Windows environment. But this time, I went through all the "hey, how does this work?" steps, I knew that there is no Windows to go back to :-). In the end, it was not difficult at all. I was actually surprised, how better the OS is compared to Windows.

It's WAY faster. It's almost unbelievable how snappy and immediate everything is (even after installing a lot of stuff). The UI is not really beautiful, but it is consistent and predictable. Because of the combination of a lightweight fast OS, configurable key shortcuts and insanely powerful command line, my productivity went up just because of changing operating system. I never really noticed how Windows slowed me down until I switched to something else.

So, goodbye Windows. It was fun.

Maybe I'll come back when you get your shit together, but I have my doubts.

Redmi Note 14 Pro sucks

Writing again, after almost a year. Too much going on.

Anyway, Redmi Note 14 Pro sucks. I had the older generation (12 or 13 I think) and it was awesome. It was a good mix of cheap, useful and not-going-in-my-way. So when I accidentaly dropped it and broke the display I didn't even think about it and bought the newer generation. Oh boy...

The phone just sucks. The camera is so shitty it's almost funny. The system feels a lot slower than on the previous generation and there are all these bugs (like disappearing icons). 

I will use this phone as long as I can tolerate it (or drop it to the floor, accidentaly), but it's getting more and more annoying. 

What happened, Xiaomi?

I started growing herbs

The digital world is overwhelming. I need some kind of analog activity, ideally one where something is done manually with my hands. So I started growing herbs, among other things.

My wife is an avid florist so she helps me and gives me tips, but since I'm an arrogant developer, I want to figure everything out myself anyway :) I admit that I asked AI a couple of times. Don't tell my wife.

Making a website screenshot with chrome-php

For a small project I am building, I need to programatically create website screenshots. Should be easy, right? Just use chrome-php, create a headless browser instance, load the webpage and take a screenshot. 

Well, no.

Taking the actual screenshot is easy, but it has some quirks:

  1. the script needs to remove any cookie consent dialogs, popups, panels
  2. it needs to deal with lazy loading, ie. programatically scroll through the whole webpage to display any lazy-loaded content (for example images)
  3. it needs to wait until all the dynamically loaded content is done loading (wait for network idle)
  4. it needs to deal with off-screen content (probably just crop it off)
  5. probably a few more things I haven't come across yet

For now, I am using chrome-php and it works for 95 % of situations, but on some websites (for example, smashingmagazine.com), there is a couple of content blocks missing in the screenshot. I honestly don't know why. 

Versus loading the webpage in browser:

 

The content is not dynamically loaded (when I open the website in browser and disable Javascript, it still displays normally). 

This is a mystery to me.

Gridfinity is a godsend

For a long time, I was searching for a good system to organize storage spaces. Something that could neatly sort small-ish items, while still being flexible enough to adapt as my needs changed.

Then I discovered Gridfinity. I first came across it (probably on MakerWorld) while browsing for print models, and it instantly caught my attention. The concept is brilliant - modular, versatile, and packed with countless designs and layout options. Best of all, it can be rearranged or expanded at any time, making it future-proof. And if I ever need more, I can simply print additional base grids or components.

Also, it's very economic - each component only uses a few grams of filament, which means a single 1 kg spool can produce a huge number of them.

Finally got the ESP32-CAM working

This was a headache. The RHYX M21-45 camera seems not to be compatible with the usual Arduino libraries and I had to do too much googling and trying advice from AI (to no avail, surprisingly). Finally I got it working by actually downgrading the AI Thinker ESP32-CAM library to some previous version (I think the 3.3.0 did not work).

It looks like a mess, but it works. I'll have to 3d print a nice enclosure and I'll probably use this as a very simple timelapse camera - I set it to take a picture every 30 minutes and send it my backend API endpoint.

Text-to-svg.com updates

I worked on my little online tool text-to-svg.com a bit - made some updates, mainly the support for PDF export. That was fun to work on. 

Also, it's gaining a bit of traction - currently, the site gets around 250-300 visitors a day. That's not bad for such a tiny tool with such a narrow target audience 🤷‍♂️

I started a new mini web tool - compare-fonts.com

Today I launched compare-fonts.com - a mini tool to visually compare two fonts (for now, only generic and Google fonts supported). It allows you to select left/right font, write any text, and visually compare the two texts/fonts. You can also download an image with both texts. 

I bought a 3D printer

Welcome home, Bambulab A1 🤩 Bought the version with AMS because why not. 

Now... how the heck do I rewind the filament from a paper spool to a bambulab spool?

Edit: This is how. Cool!

Edit 2: What to do with filament poop? 

 

Reading XLSX in PHP without a library

I needed to read values from XLSX table. Existing PHP libraries seemed unnecessary heavy to me and I know that XLSX file basically just a ZIP archive so I thought that the data could be read directly from the XML files inside.

Here is the code. It reads data from the first sheet in the test.xlsx file and puts the (two dimensional) data into $rows variable.

$file = 'test.xlsx';

$zip = new ZipArchive();
if (!$zip->open($filePath) die('Failed to open file');

$sharedStringsXML = $zip->getFromName("xl/sharedStrings.xml");
$sheetXML = $zip->getFromName("xl/worksheets/sheet1.xml");

$sharedStrings = [];
if ($sharedStringsXML !== false) {
    $sharedStringsData = simplexml_load_string($sharedStringsXML);
    foreach ($sharedStringsData->si as $si) {
        $sharedStrings[] = (string)$si->t;
    }
}

$rowNumber = 0;
$rows = [];
$lastChar = false;
if ($sheetXML !== false) {
    $sheetData = simplexml_load_string($sheetXML)->sheetData;
    foreach ($sheetData->row as $row) {
        $rowNumber++;
        $rowData = [];
        foreach ($row->c as $cell) {
            $value = null;
            $r = (string)$cell['r'];
            $char = substr($r, 0, 1);
            if($lastChar) {
                $c1 = ord($char) - 65;
                $c2 = ord($lastChar) - 65;
                $diff = $c1 - $c2;
                if($diff > 1) {
                    for($i = 1; $i < $diff; $i++) {
                        $rowData[] = '';
                    }
                }
            }
            $lastChar = $char;
            $type = (string)$cell['t'];
            if ($type === 's') {
                $value = $sharedStrings[(int)$cell->v];
            } else {
                $value = (string)$cell->v;
            }
            $rowData[] = $value;
        }
        $rows[] = $rowData;
    }
}
$zip->close();

Thin scrollbar, visible on hover

In some situations, the default browser scrollbars are too bulky and ugly. I did not know that there is an option to make them thinner, also visible only on hover:

.scrollbar {
    scrollbar-color: transparent transparent;
    scrollbar-width: thin;
}
.scrollbar.visible, .scrollbar:hover {
    scrollbar-color: #aaa transparent;
}

Thanks, Reddit!

PHP - sort strings with accents

Let's have an array with some strings:

$data = [
    'Abcd',
    'Efgh',
    'Červený trpaslík',
    'Another one',
    'Špenát',
    'Železo',
    'Zelenina'
];

I need to sort that. When using asort($data);, the strings with accents are moved to the end of the array:

Abcd
Another one
Efgh
Zelenina
Červený trpaslík
Špenát
Železo

To be able to sort correctly with the accents, let's use the PHP Collator-family functions:

usort($tags,
    function($a,$b) {
        $coll = collator_create( 'cs_CZ' );
        $aSortKey = collator_get_sort_key($coll, $a);
        $bSortKey = collator_get_sort_key($coll, $b);
        return $aSortKey >= $bSortKey;
    }
);

Now the data is sorted correctly:

Abcd
Another one
Červený trpaslík
Efgh
Špenát
Zelenina
Železo

Multilingual website and browser language

The proposal is to create a multilingual website, using a main domain in .COM format accompanied by respective national domains for each language (.CZ, .PL, .DE, etc.).

The aim is for all national websites to redirect to the .COM domain, serving as the "primary" version in English. When a visitor lands on the .COM site with a browser language different from English, a popup should appear, offering the national website in their chosen language.

The first step involves redirecting all national websites to the main website. For instance, to redirect a .CZ website using mod_rewrite:

RewriteCond %{HTTP_HOST} ^website.cz$ [OR]
RewriteCond %{HTTP_HOST} ^www.website.cz$
RewriteRule (.*)$ https://www.website.com/$1 [R=301,L]

Then, detect a visitor language:

$visitorLanguage = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2); 
$allowedLanguages = ['en', 'de', 'fr', 'it', 'cs', 'sk'];
if(!in_array($visitorLanguage, $allowedLanguages)) $visitorLanguage = 'en';
define('VISITOR_LANGUAGE', $visitorLanguage);

CCompare it with the current website's locale (for instance, utilizing the $websiteLocale variable initialized from a cookie/URL fragment):

if($visitorLanguage != $websiteLocale) {
  // ... show a message to the visitor
  // offering a redirect to website with his language
}

PHP multibyte string functions are slow(er)

A reminder for myself: Only use mb_ functions in PHP when necessary, as they are significantly slower than their non-multibyte alternatives.

For example, replacing mb_substr with substr (where multibyte is not needed) resulted in 3x - 6x faster execution.

WSL CPU usage, killing the task

From time to time, the wslservice.exe task spikes at around 80-90 % CPU usage. It can't be killed by closing the WSL terminal window.

I have to launch an elevated command prompt (or Powershell) and execute this command to kill the process:

taskkill /F /im wslservice.exe

Then I can launch it again without the CPU spike.

Composer timeout

When installing Laravel 10 using this command:

composer create-project laravel/laravel myproject

Composer started its job but timed out after ~5 minutes.

Fixed by adding two parameters:

composer --prefer-dist --no-dev create-project laravel/laravel myproject

Alternatively, it's possible to allow a longer time to run.

composer config --global process-timeout 2000

Laravel, Tailwind and Vite - a few notes

The combination of Laravel, Tailwind and Vite has made my work a lot easier. Especially replacing the slow Webpack (Laravel Mix) with Vite was a great productivity boost, since the building times are much, MUCH, shorter.

I had to do just some small tweaks to make things work:

1. After installing Laravel, I had to install Tailwind using the official guide, this is pretty easy and straightforward.

2. Then I had to edit the package.json file and add the \"watch\" NPM script to be able to watch the CSS file changes and build the CSS on every change.

...
"scripts": {
    "dev": "vite",
    "build": "vite build",
    "watch": "vite build --watch"
},
...

3. After adding the @vite(['resources/css/app.css', 'resources/js/app.js']) code to the HEAD section of my template, it still didn't work. When viewing the source code, I noticed that the <link> for the CSS still pointed at the local dev server with 5173 port.

After some searching, I found the solution - I needed to delete the public/hot file.

After that, everything is working!