Edit and Export Multi-page PDFs with GIMP [2019]

GIMP is a great tool for editing PDFs. It is quick and easy. Especially if you are already familiar with the photo editing software GIMP.

GIMP – GNU Image Manipulation Program

Unfortunately, editing and exporting multi-page PDFs if a convoluted process. I will walk you through the steps below

Step 1. Import Multi-page PDF into GIMP

Click File > Open. Select your PDF. Hold down [SHIFT] while you click each pages of your PDF. Then click Import.

Step 2. Edit your PDF as an image

Each page of your PDF will be converted into an image. You can turn the visibility of each layer on/off so you can see and edit each page.

GIMP does not have an special support for editing a PDF. Just edit as an image.

Step 3. Export Multi-page PDF

First, export as a .mng file. The default export settings worked for me.

Step 4. Convert .MNG to .PDF

First, install imagemagick.

sudo apt-get install imagemagick

Second, run this command to convert:

convert -reverse document.mng document.pdf

Note: You may get an error when running this command

convert-im6.q16: not authorized `document.pdf' @ error/constitute.c/WriteImage/1037.

If you get this error, here’s how to fix it:

sudo mv /etc/ImageMagick-6/policy.xml /etc/ImageMagick-6/policy.xmlout

That command disables some security settings you might want. To set things back, run this command:

sudo mv /etc/ImageMagick-6/policy.xmlout /etc/ImageMagick-6/policy.xml


That step-by-step guide walks you though importing, editing, and exporting multi-page PDFs in GIMP. We also address a common error you might encounter. If there is anything we missed, please let me know. Have a great day!

[Solved] WordPress Update Not Working

I was having trouble updating plugins are the Wordress core.


  • WordPress not updating to the latest version. The lastest version is 5.0.3. After the one-click update, WordPress would say “Welcome to WordPress 5.0.2”
  • Updated Yoast from version 9.3 to 9.4. But Yoast is still trying to load all of the js assets from 9.3 which do not exist anymore.

Clearing the Cache

There are couple other blog posts detailing how to clear various caches including:

  • Browser cache
  • Cloudflare cache
  • WP Cache plugin

None of these worked. After some trial and error, I found the files were cached in the PHP opcache. In some versions of PHP, byte code is cached.

To clear the byte code, simple add this line to the function.php file of one of your plugins:


Reload the page once, then remove that line. You should be good to go.

[Solved] Add Recaptcha to Registration Form on Laravel 5.7 w/ Spark 7.0

Laravel Spark is a great for setting up user management, subscriptions, and an API quickly. Without Spark, these steps would take weeks. With spark it takes only a few hours.

But Spark does have some issues: mainly the documentation. For such and large system—particularly one that is paid ($99 per site)—there should be better documentation.


Adding custom fields to the registration form is supposed to be easy. This customization is explicitly documented. However the steps do not work for added a Recaptcha to the registration form.

Normally, you would not need a captcha on the registration form because users have to pay to sign up. However, our app has a free tier. Another solution to this problem is requiring email verification. But email verification has its own problems.

Regardless, this is a problems others have had as well.

What is the issue

There are three problems with using Recaptcha on the registration page:

    1. Spark registration page does not support custom fields with dashes in the name. This is not documented. The Recaptcha custom field is called “g-recaptcha-response”.
    2. The Recaptcha field is created dynamically which does not work with the Vue powered form that ships with Spark.
    3. Spark registration calls the validator twice which causes Recaptcha validation to fail the second time.

The solution

  1. Remove dashes from the field name. Call it “grecaptcharesponse” instead of “g-recaptcha-response”.
  2. Manually put the recaptcha verification token into the “grecatpcharesponse” field before submitting the form
  3. Only call the validator once

Step by Step

  1. Create recaptcha validator
    php artisan make:rule Recaptcha
  2. Install Guzzle
    composer require guzzlehttp/guzzle
  3. Paste this into app/Rules/Recaptcha.php

namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
use GuzzleHttp\Client;

class Recaptcha implements Rule
    public function passes($attribute, $value)
        // this hack prevents the validator from failing the second time it is called.
        if (isset($GLOBALS['G_RECAPTCHA_RESULT']))
            return $GLOBALS['G_RECAPTCHA_RESULT'];

        $client = new Client();

        $response = $client->post(
                    'secret' => env('RECAPTCHA_PRIVATE_KEY'),
                    'response' => $value

        $body = json_decode((string)$response->getBody());
        $GLOBALS['G_RECAPTCHA_RESULT'] = $body->success;
        return $body->success;

    public function message()
        return 'Check the box "I\'m not a robot"';
  1. Include the validator we just created at the top of your SparkServiceProvider.
    use App\Rules\Recaptcha;
  2. Add this to the booted method of your SparkServiceProvider:
Spark::validateUsersWith(function () {
    return [
        'name' => 'required|max:255',
        'grecaptcharesponse' => ['required', new Recaptcha],
        'email' => 'required|email|max:255|unique:users',
        'password' => 'required|confirmed|min:6',
        'terms' => 'required|accepted',
  1. Add this code to resources/js/app.js
Spark.forms.register = {
    grecaptcharesponse: 'abc'
  1. Add this to resources/views/vendor/spark/auth/register-common-form.blade.php where you want the recaptcha form to appear
<div class="form-group row">
        <script src='https://www.google.com/recaptcha/api.js'></script>
    <label class="col-md-4 col-form-label text-md-right">Are you human?</label>

    <div class="col-md-6">
        <div class="g-recaptcha" data-sitekey="{{env('RECAPTCHA_PUBLIC_KEY')}}"></div>

        <span class="invalid-feedback" style="display: block;">
            @{{ registerForm.errors.get('grecaptcharesponse') }}
  1. Add this just before the closing head tag in your layout file:
  2. Update “sendRegistration” method in resources/js/spark/auth/register-stripe.js
sendRegistration() {
    if ($('#g-recaptcha-response').val())
        this.registerForm.grecaptcharesponse = $('#g-recaptcha-response').val();
        // put something here instead of an empty string so you get the 
        // Recaptcha error message instead of the "required" error message.
        this.registerForm.grecaptcharesponse = "abc";

    Spark.post('/register', this.registerForm)
        .then(response => {
            window.location = response.redirect;


That ended up being a little longer than I intended. Other gotchas you may encounter:

  • Laravel has recently moved the location of the assets files so your file structure may be different
  • You will need to publish the Spark vendor files in order to edit them

You can see this in action at BitcoinAbuse.com.

[Solved] Boot from USB on Dell XPS 13 9370

Ubuntu works very well on XPS laptops, but it is not so easy getting the laptop to boot from a USB.

Step 1: Change POST Behavior from “Fastboot” to “Thorough”

  1. At boot, Press the F2 key (or alternately press the F12 key then select the option to enter the BIOS setup).
  2. In POST Behavior, Select – Fastboot the select the Thorough option.

Step 2: Put USB in the right USB Type-C port

This is the main trick I have been unable to find documented anywhere else. The BIOS only recognizes bootable media in the right USB port.

  1. Create bootable Ubuntu USB (Or any other linux distro)
  2. Put USB in the right USB Type-C port. Only the right USB port will be detected in the boot options.
  3. Reboot the system, press F12.
  4. Select your USB in the boot options.

[Solved] Tmobile Moto G6 phone will not send or receive MMS messages

I had this problem on a my new phone. Found the solution on a T-Mobile forum but it was difficult to find. This post is to link back to the correct solution so it’s easier to find and to save the correct solution:


The problem has to do with the phone’s APN settings – which need to be tweaked.

1. Access your Android phone’s settings (gray settings icon that looks like a gear).

2. On the settings menu – select “Network & Internet”

3. On the “Network and Internet” menu – select “mobile network” (which should say “t-mobile” beneath the words “mobile network”)

4. On the “mobile network” menu – select “Access Point Names” (often abbreviated APN).

5. On the “APNs” menu – you should see two options: “T-mobile US” and “Tmobile MMS”. There should be a green dot next to “T-Mobile US”. Select that “T-Mobile US” option (NOT the Tmobile MMS).

6. This will open an “edit access point” menu, and you will need to make the following edits:

First – click on the MMSC option and add the following text: mms.msg.eng.t-mobile.com/mms/wapenc

Second – click on the “APN type” option. It will probably say something like: “default,supl,hipri,fota” (without the quotation marks). You will need to tweak the end of it so the whole thing instead reads “default,supl,hipri,fota,mms” (without the quotation marks). As indicated here – there are no spaces between these commas.

Third – after making these two adjustments to your APN – make sure you click on the menu dots (three vertical dots) at the top right hand part of the “edit access point” menu. This will open up a drop down menu – and then you should select “Save”. If you don’t do this – the settings will not take effect.

Thanks to cflaniken3.

Configuring Sublime for PHP Development

Word Separators

One of PHP’s most distinctive features is the dollar sign. All variables in PHP start with ‘$’.


$var = 123;

In Sublime, if you double click on a variable name to select it the ‘$’ prefix will not get selected.

To fix this you can edit Sublime’s “word_separators” setting.

To change this setting, go to Preferences -> Settings. Then paste this in your user settings and save.

"word_separators": "./\\()\"'-:,.;<>~!@#%^&*|+=[]{}`~?",

Notice the ‘$’ is missing.

Caps Lock to CTRL

When Caps Lock is pressed it is almost always on accident. On the rare occasion I need something in all caps, it is simpler to hold the SHIFT key.

On the other hand, I use CTRL key constantly. One of the first customization I make on new computers is remapping the caps lock key to CTRL.

You can do this on a Mac in System Preferences -> Keyboard -> Modifier Keys. And on Linux key remapping can be accomplished by using the Gnome Tweak Tool.

On Windows, key modification is a bit more difficult. On Windows, you need to modify the registry. You can do this manually or there are small applications to help remap keys.

Making ‘$’ Easier to Type

Finally, because of the dollar signs position it is not the easiest key to type. I am looking for an optimization to make that character faster and easier to type. I have not found a good solution yet.

[Solved] Install Hipchat 4 on Fedora 23

Hipchat has documentation for installing on Debian-based distros but not on RPM-based distros like Fedora and RHEL.

However, there is an undocumented repository for RPM. The undocumented RPM repository is: https://atlassian.artifactoryonline.com/atlassian/hipchat-yum-client

To install the repository:

sudo dnf config-manager --add-repo https://atlassian.artifactoryonline.com/atlassian/hipchat-yum-client

Next, to install Hipchat 4:

sudo dnf install hipchat4 --nogpg

SSH Tunnel Local Port to Local Port on Remote Machine

Reducing your attack surface is an important part of network security. If you are running a service that only needs to be accessed from localhost, you should not allow connections from the outside world. On my web servers, only 3 ports are open:

  • 22 for SSH
  • 80 for http
  • 442 for https

All other ports are filtered. This significantly reduces the number of possible attack vectors. Other services such as MySQL (port 3306) are only accessible from the private network. To access MySQL from a remote machine you can SSH tunnel to the database server and then connect to the database on localhost. Most database clients like MySQL Workbench support SSH Tunneled Connections.

However, I came across a problem when I wanted to run a python script locally to update my remote database server. There is a library for connecting directly to a MySQL database with Python, but not for connecting through an SSH Tunnel. Another option would be to run the python script on the remote server, but in my case I did not want to do that.

To solve this, we will have to manage the SSH Tunnel ourselves. After building the SSH Tunnel, you will be able to connect to a port on your local machine and the connection will be sent to a different port on the remote machine’s loopback interface.

If you use key authentication to SSH, run this command:

ssh -nNT -L 1234:localhost:3306 REMOTE_SERVER

If you are using password Authentication, run this command:

ssh -fNT -L 1234:localhost:3306 REMOTE_SERVER

That’s it. The command will go to the background and you can connect to localhost:1234 as if it were locahost:3306 on the remote server.

Final Notes

Test the SSH Tunnel

There are a few final notes when using this command. First, to test that your tunnel is working, try to connect to MySQL on the remote server by running this command on the local machine:

mysql -u REMOTE_DB_USER -h -P 1234 -p

You must to set the host (using the -h flag) to If you leave the host blank or set it to localhost, MySQL uses sockets and thus ignores the port flag (-P).

Close the SSH Tunnel

How do we close the SSH Tunnel? When you want to destroy the SSH Tunnel you can run the following command. It will kill a process that is listening to port 1234:

fuser -k 1234/tcp

Keep SSH Tunnel Alive

Finally, when using SSH Tunnels you may want to configure  SSH to keep the session alive so your connection does not drop unexpectedly. SSH does not close connections after any length of time, but router’s remove inactive connections from the NAT tables periodically. To prevent this from happening, add the following code to the top of your SSH configuration file:

Host *

    # Send keep-alive packet every 60 seconds
    ServerAliveInterval 60

    # Send keep-alive packet only 60 times (1 hour)
    ServerAliveCountMax 60

Adjust ServerAliveInerval  and ServerAliveMaxCount  as desired. SSH configuration file is found in ~/.ssh/config

Solved: Laravel 5 Calling the Wrong Controller

I recently encountered an issue that took me far longer to solve that it should have. Laravel is an excellent framework for building PHP websites. I have used it to build several sites now and it is by far the best framework I have seen. The latest version, Laravel 5, is powerful and well documented. With Composer, Laravel is easily extendable and allows you to pull in common php packages.

Up to this point, I had used Composer to pull in packages and manage dependencies without really knowing how it worked. Unfortunately, due to my lack of understanding, Composer’s Autoload feature caused an issue that was very hard to debug.

In the site I was making, I created a new configuration file that was used to dynamically set routes so I could create new sections and add those sections to the navigation menus in one step.

Next, I created a template controller that I would simply copy, rename, and start working with for each of these new sections.

Everything was working fine until I used Composer to install a new package on my production system. I installed Guzzle to which is need to interface with MailGun. When you install a new package Composer updates composer.json, installs the package, and reruns dump-autoload.

After setting up email, I thought the site was ready for production. All the sudden, one of my many dynamically created sections was not working. For some reason, the route for this one section was calling the Template Controller instead of the proper controller.

First, I though one of Laravel’s many layers of caching was the issue. I disabled Laravel’s route caching, configuration caching and view caching. I even turned off mod_pagespeed and CloudFlare just to be sure no caching was the problem. But it still was not working.

I decided to hard code the routes to skip over my dynamic route generator. But still for just one section the Template Controller was being called instead of the proper controller. I checked the routes file again and again to ensure there was no earlier route being called.

Eventually, I removed the Template Controller entirely and the section in question would error out saying TemplateController.php was not found. But how was this controller being called? The section was working before. Everything was working on my development machine. Just in production, this one section of the site was not working. For some reason, this route:

['as' => 'quarters', 'uses' => 'Content\QuartersController@index']);

would call the TemplateController@index

At this point I had completely deleted the Template Controller. Something must be calling Template Controller somewhere.

So I decide to SSH into my production server and run:

$ grep -R 'TemplateController' public_html/

That grep command searches the entire web directory recursively for the string ‘TemplateController.’


As you can see, the string was first found in an error log file. But the final match led to the solution.

We can see a file called `vendor/composer/autoload_classmap.php` contains a line that seems to be mapping the `QuartersController` class to the `TemplateControllers` class. How did that get there?

It turns out, that when I created the `TemplateController` file, I did it by copying the `QuartersController` and stripping out all of the code specific the that section. Unfortunately, I forgot to change the name of the class in the `TemplatesController.php` file. I accidentally left that class name as `QuartersController`.

Now, that should not be an issue. I am not even using the `TemplatesController` class. It is only there for a starting point when creating new sections.

What I did not know at the time is when your run composer dump-autoload , composer scans your entire project’s source code and creates a `autoload_classmap.php` file. The classmap files allows the application to more quickly find the file containing a particular class.

Because I had two classes with the same name (one of them not being used), composer mapped to the wrong file. I changed the TemplateController.php class name, reran dump-autoload and everything worked again!

This error only surfaced after installing Guzzle because after a package install, composer runs dump-autoload automatically.

The moral here is that you can only use a tool without knowing how it works for so long before it comes back to bite you.

Alan’s Exif Viewer

Last week I launched exif.alanreed.org an online Exif viewer. This tool allows you to view the metadata embedded within images such as brand, model, and serial number of the camera. Some cameras even store the latitude and longitude of where the image was taken. Cell phones especially store tons of good information in Exif.

I wanted to make this tool for a while and finally got the chance during a 20 hour drive to Orlando, Fl for the annual HTCIA conference.

Right now the tool only works for images, but in the future I hope to add the ability to extract metadata from other file types such as pdf, docx, and exe.