Half-Elf on Tech

Thoughts From a Professional Lesbian

Tag: images

  • Small Hugo, Big Images

    Small Hugo, Big Images

    In working on my Hugo powered gallery, I ran into some interesting issues, one of which was from my theme.

    I use a Bootstrap powered theme called Hinode. And Hinode is incredibly powerful, but it’s also very complicated and confusing, as Hugo’s documentation is still in the alpha stage. It’s like the early days of other web apps, which means a lot of what I’m trying to do is trial and error. Don’t ask me when I learned about errorf, okay?

    My primary issues are all about images, sizing and filing them.

    Image Sizes

    When you make a gallery, logically you want to save the large image as the zoom in, right? Click to embiggen. The problem is, in Hinode, you can load an image in a few ways:

    1. Use the standard old img tag
    2. Call the default Hugo shortcode of {{< figure >}}
    3. Call a Hinode shortcode of {{< image >}}
    4. Use a partial

    Now, that last one is a little weird, but basically you can’t us a shortcode inside a theme file. While WordPress has a do_shortcode() method, you use partial calls in Hugo. And you have to know not only the exact file, but if your theme even uses partials! Some don’t, and you’re left reconstructing the whole thing.

    Hinode has the shortcodes in partials and I love them for it! To call an image using the partial, it looks like this:

                        {{- partial "assets/image.html" (dict
                                 "url" $imgsrc
                                 "ratio" "1x1"
                                 "wrapper" "mx-auto"
                                 "title" $title)
                         -}}
    

    That call will generate webp versions of my image, saved to static image folder (which is a post of its own), and have the source sets so it’s handy and responsive.

    What it isn’t is resized. Meaning if I used that code, I would end up with the actual huge ass image used. Now, imagine I have a gallery with 30 images. That’s 30 big ass images. Not good. Not good for speed, not good for anyone.

    I ended up making my own version of assets/image.html (called lightbox-image.html) and in there I have this code:

     {{ with resources.Get $imagefile }}
         {{ $image    = .Fill "250x250" }}
         {{ $imagesrc = $image.RelPermalink }}
     {{ end }}
    

    If the file is local, which is what that get call is doing, it uses the file ($imagefile is the ‘path’ to the file) to make a 250×250 sized version and then grabs that new permalink to use.

    {{ if $imagefile }}
         &lt;img src="{{ $imagesrc }}" class="img-fluid img-lightbox" alt="{{ $title }}" data-toggle="lightbox" data-gallery="gallery" data-src="{{ $imagefile }}">
     {{ end }}
    

    Boom!

    This skips over all the responsive resizing, but then again I don’t need that when I’m making a gallery, do I?

    Remote Image Sizes

    Now let’s add in a wrinkle. What if it’s a remote image? What if I passed a URL of a remote image? For this, you need to know that on build, that Hinode code will download the image locally. Local images load faster. I can’t use the same get, I need the remote get, but now I have a new issue!

    Where are the images saved? In the img folder. No subfolders, the one folder. And I have hundreds of images to add.

    Mathematically speaking, you can put about four billion files in a folder before it’s an issue for the computers. But if you’ve ever tried to find a specific file to check in a folder that large, you’ve seriously reconsidered your career trajectory. And practically speaking, the more files, the slower the processing.

    Anyone else remember when GoDaddy announced a maximum of 1024 files in a folder on their shared hosting? While I question the long term efficacy of that, I do try to limit my files. I know that using the get/remote get calls with tack on a randomized name at the end, but I’d like them to be organized.

    Since I’m calling all my files from my assets server (assets.example.com), I can organize them there and replicate that in my build. And my method to do that is as follows:

         {{ if eq $image "" }}
             {{- $imageurl = . | absURL -}}
             {{- $imagesrc = . | absURL -}}
     
             {{ $dir := (urls.Parse $imageurl).Path }}
    
             {{ with resources.GetRemote $imageurl | resources.Copy $dir }}
                 {{ with .Err }}
                     {{ warnf "%s" . }}
                 {{ else }}
                     {{ $image    = . }}
                     {{ $imageurl = $image.Permalink }}
                     {{ $image    = $image.Fill "250x250" }}
                     {{ $imagesrc = $image.RelPermalink }}
                 {{ end }}
             {{ end }}
         {{ end }}
    

    I know that shit is weird. It pairs off the earlier code. If you don’t create the image variable, then you know the image wasn’t local. So I started by getting the image url from ‘this’ (that’s what the period is) as an absolute url. Then I used the path of the url to generate my local folder path! When I use the Copy command with the pipe, it will automatically use that as the destination.

    Conclusion

    You can totally make images that are resized in Hugo. While I wish that was easier to do, most people aren’t as worried as I am about storing the images on their repository, so it’s less of an issue. Also galleries on Hugo are fairly rare.

    I’m starting some work now with Hinode for better gallery-esque support, and we’ll see where that goes. Maybe I’ll get a patch in there!

  • Alternate Symbols

    Alternate Symbols

    I like to remotely host my SVGs and then call them in my code. For the most part, this works well, and I wrote out some basic code to check if they’re defined and accessible before displaying.

    But what if you wanted your fallback to be a font-icon?

    And remember, you want your code to be DRY as a bone. So no repeating chunks of code all over the place, please and thank you.

    The Code

    There is but ONE requirement here. You have to define HALFELF_SYMBOLICONS_PATH as the path to your SVGs. In my case, mine is something like http://my-cool-icons.objects-us-west-1.dream.io/svgs/ because I’m using DreamObjects for them. Any cloud host works great for this, mind you.

    function halfelf_symbols( $svg = 'square.svg', $fontawesome = 'fa-square' ) {	
    
    	$icon = '<i class="fa ' . $fontawesome . '" aria-hidden="true"></i>';
    
    	if ( defined( 'HALFELF_SYMBOLICONS_PATH' ) ) {
    		$response      = wp_remote_get( HALFELF_SYMBOLICONS_PATH );
    		$response_code = wp_remote_retrieve_response_code( $response );
    		
    		if ( $response_code == '200' ) {
    			$get_svg      = wp_remote_get( LP_SYMBOLICONS_PATH . $svg );
    			$response_svg = wp_remote_retrieve_response_code( $get_svg );
    			$icon         = ( $response_svg == '200' )? $get_svg['body'] : 'square.svg';
    		}
    	}
    
    	return $icon;
    }
    

    This checks for the existence of the server used and if the actual icon exists before it sets things.

    Usage Example

    In my code, I define it like this:

    $icon  = halfelf_symbols( 'users.svg', 'fa-users' );
    $title = '<span role="img" aria-label="users" title="Users" class="TEMPLATE users">' . $icon . '</span>';
    

    This sets the icon and then calls the span which will help make this more accessibility friendly. I could have coded the span into the function, but since I often have it all dynamically generated, it worked more sustainably this way.

    In this example, I call it like this:

    the_archive_title( '<h1 class="page-title">' . $title . '</h1>' );
    

    And voila. Icons in my page title.

  • Still Don’t Disable Right Click

    Still Don’t Disable Right Click

    Back in 2011, I wrote about not disabling right-click. That page still gets a reasonable amount of traffic and it's time for a brief revisit.

    The Only Way to Protect Data Is Not To Have It Online

    Let's start with the lesson everyone needs to learn. There remains but one and only one way to protect your data online. There is only one sure-fire way to make sure your photos aren't stolen, your videos aren't leaked, and your content isn't ripped off. Don't put anything online that you're not alright with having taken from you. This sucks. I know. This is pretty horrid life advice, but the fact is that as soon as you put something up on the Internet, and people like it, they will take it. Worst, they'll claim it as their own. I really hate that one.

    You Can Still Protect Content

    This isn't all bad news. You can still have content that is protected from re-use, it not actual theft, but you have to be intelligent about it. You have to think about what you're protecting and why. Protecting all the images that viewers see on your site is a lost cause. There are just too many ways to download them.  Instead, it's a matter of cutting your losses, protecting only what must be protected, and then intelligently guarding what's left. And here are my simple rules for content protection:
    1. Watermark images you don't want reused
    2. Server protect folders that store downloadable data (i.e. .htaccess )
    3. Hide the URLs for downloadable data
    That's it. Three rules.

    But What About…

    No. What? You want me to talk about how Instagram protects images from right-clicks and, thus, downloads? You want me to point out that even Wix tells you how to protect right-click? You think I should tell people how Getty images uses code to watermark and right-click protect? It doesn't matter. As Wix so rightly points out, anyone who knows how to view source code could get the images anyway. And trust me, people who want your images will work hard and learn that. They'll quickly figure out how to get around it. You can get around Getty images, but they do the most important thing of all. They know you're going to take their low-resolution images. They're okay with that loss kind of – I don't recommend it as they'd sued people over re-use. But they will absolutely take you down if you 'steal' their high resolution images, because the only way to do that is to make an account with them and purchase. They out and out hide the images from anyone who didn't buy them. They're locked behind a user-account. The lesson you can take away is this: The only winning move? Is not to play.
  • Cleaning up SVGs

    Cleaning up SVGs

    If you were here on Friday, I talked about the headache of using SVGs on AMP pages. In that post I mentioned that one of the fixes was to ‘clean’ the SVG file.

    Why would be bother? The simple answer is that smaller files are better. But as we learned with AMP being picky, it’s also because we want our files to be fully compatible with all browsers and servers. SVG standards are important, after all.

    So let’s clean!

    Remove Headers

    If your SVG starts with this, just remove it:

    <!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN">
    

    Remove Less Standard Tags

    I wouldn’t have realized these weren’t super standard if it hadn’t been for AMP alerts. But these aren’t:

    xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
    

    Again, remove them. But also you want to remove tags like <g i:extraneous="self"> and that’s tricky because you’ll usually see it like this:

    <g i:extraneous="self">
        <g>
            [Image stuff]
        </g>
    </g>
    

    If you use a regular expression like I did, be very cautious. I checked every single one of my 700 SVGs after the edits to make sure they still worked. There were cases where the only call to the <g> tag was with the extraneous stuff, so I had to very cautiously replace. I missed about 40.

    Remove Illustrator’s Extra Shit

    Here’s where you’re going to save the most space. First remove the requiredExtensions stuff:

    	<foreignObject requiredExtensions="&ns_ai;" x="0" y="0" width="1" height="1">
    		<i:pgfRef  xlink:href="#adobe_illustrator_pgf">
    		</i:pgfRef>
    	</foreignObject>
    

    And then remove the big hunk of PGF:

    <i:pgf  id="adobe_illustrator_pgf">(.*)</i:pgf>
    

    I can’t show you the actual content. It’s huge. It’s way huge. It’s up to 20k huge.

    Remove Switch

    If you’re not using the switch, you want to remove it.

    <switch>
    	<g i:extraneous="self">
    		<rect fill="#231F20" width="28" height="28"/>
    	</g>
    </switch>
    

    End Result?

    A 29 KB file is 4KB. And that is a faster internet.

  • Can AMP and SVGs Be Friends?

    Can AMP and SVGs Be Friends?

    Spoiler: Not at the moment, no.

    Warning: Fix errors on your AMP pages

    On January first, I got a scary email with that subject:

    Google systems have detected that some of your AMP pages do not meet our guidelines and will therefore not show in Google Search AMP-related features.

    Yikes! I ran over to Google Webmaster Tools and saw I had well over 400 pages with critical issues. Happy New Year to me, right? Looking at them, most of the problems were exactly the same:

    • The tag ‘pgfref’ is disallowed.
    • The tag ‘pgf’ is disallowed.
    • The attribute ‘i:extraneous’ may not appear in tag ‘g’.
    • The attribute ‘xmlns:x’ may not appear in tag ‘svg’.
    • The tag ‘switch’ is disallowed.

    Right away, I knew this was from my SVGs. On this site, I use SVGs instead of PNGs because they look crisper on retina screens and they can be colored on the fly. Basically I really like the power of SVGs. And, as far as I could tell, SVGs worked on AMP pages. I read the details on SVGs in AMP/HTML and it’s listed there, after all.

    But in reading the errors and looking at my SVGs in a text editor, I quickly saw what was wrong.

    Most SVGs have Extra Data

    I use the Block Bundle from Symbolicons for my SVGs. They’re awesome, but once in a while I have to edit them and clean up some extraneous data within. Adobe Illustrator, for example, puts in a lot of junk at the top of my SVGs:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    

    But in the case of the SVG having a problem, it had … well. This:

    <?xml version="1.0" encoding="utf-8"?>
    <!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN">
    <svg version="1.1" id="Layer_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
    	 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 28 28"
    	 enable-background="new 0 0 28 28" xml:space="preserve">
    <switch>
    	<foreignObject requiredExtensions="&ns_ai;" x="0" y="0" width="1" height="1">
    		<i:pgfRef  xlink:href="#adobe_illustrator_pgf">
    		</i:pgfRef>
    	</foreignObject>
    	<g i:extraneous="self">
    		<g>
    			<defs>
    				<rect id="SVGID_1_" width="28" height="28"/>
    			</defs>
    			<clipPath id="SVGID_2_">
    				<use xlink:href="#SVGID_1_"  overflow="visible"/>
    			</clipPath>
    			<path clip-path="url(#SVGID_2_)" fill="#231F20" d="M24.286,20h-6.757c-1.229,0-2.259,0.69-2.568,1.615
    				[...]
    				C26,20.651,25.234,20,24.286,20"/>
    			<path clip-path="url(#SVGID_2_)" fill="#231F20" d="M27,16H1c-0.553,0-1,0.447-1,1c0,0.553,0.447,1,1,1h26c0.553,0,1-0.447,1-1
    				C28,16.447,27.553,16,27,16"/>
    			<path clip-path="url(#SVGID_2_)" fill="#231F20" d="M21.699,2.046c-0.384-1.711-1.927-2.513-3.427-1.781l-1.544,0.752
    				c-1.5,0.732-3.956,0.732-5.456,0L9.728,0.265c-1.5-0.732-3.042,0.07-3.427,1.781L4.29,10h19.42L21.699,2.046z"/>
    			<polygon clip-path="url(#SVGID_2_)" fill="#231F20" points="23.935,11 4.066,11 3.167,15 24.833,15 			"/>
    		</g>
    	</g>
    </switch>
    <i:pgf  id="adobe_illustrator_pgf">
    	<![CDATA[
    		eJzVfelCMsuu6H0B3gFUBGTqidGZyQkVxXlCkFb5RMAG1trf+XGf/aaq56G6qxs859y99nJBd5FUpVKpJJVUopFWO73fH/fENJ9hwqFotCqJ3dlYKofx0/DRcDifziT0KH6ZCLOFDAON9o+KHaXhjShNB+NRGb1i8csG+nX8eCz9DV92J59dcZgIxxPw4mowG4rwajr5m5n03xMqNvh5rTuDN3y2mGWFcL4slMKtU/S6O/qnO50O/[...]==	
    	]]>	
    	<![CDATA[	
    		k3D32Uw5IYV5ef6oHOpoLUjz3J9shIS0J71kk7fOSM8qidXrybCJkYaiFrTT1Zd8hYT0cOeiMLpxRiok7pKbb+enTkhD0elK40XQ0VqQMo3KyR4BaX51bSQltglI71+YxvtZGyMFHrOMdeUgsrXOHneuHJEeHHJXRPJyDztXrxgpsH2vriNF62Vzeis9zXeaCG3CPqvxe/5xyCYAqTC2stJzk1HJ24rFLEhzue/OBCMNRS1oEdKO9Pw5uiIg3evmC4011hHpy+7rpRPSUBTP6hof33xyGqskbUWep6uf4qUz0otqYudn7bvphDS5OSzvYKRIWlrHml9d/Zk/[...]==	
    	]]>	
    	<![CDATA[	
    		dcrskeHXSl9Zs0fmmNYjE7Jee2ZNHFkrryzktsCJTQbLwl96jj3t62SdzndBkZ6zVmZj/odmsSu/jxf2yKA0NKvjIcB9Ssf+PTJOGakoDW1RjwzOzrPccReQMDna5Bz1vnFCsMsxMT3HV3KOdspTbs2seai711GPaBoaZRl0udXFcxJPKCIZtFubPKyWycki94aZd+TyRXzxoTmE7OkeRUoOLV9EqEIXTPxpuxMSzTlNMLBnXp0xaNZ6N5SPvDofsesEKwkRhiqFxDMWA9GmQMXJlGHrcYdz1Lb3fX10Z2UU+XA09/[...]++foLQ
    	]]>
    </i:pgf>
    </svg>
    

    I’ve truncated the code because it’s insanely long. In fact, it’s so long that with all of it, the file is 25k larger!

    Let me explain. The SVG there has ‘switch’ code. The switch code is awesome because it can change what’s displayed based on what the file detects. For example if I wanted to make different text show based on the detected system language, I would do this:

    <switch>
        <g systemLanguage="en-UK">
            <text x="10" y="20">I say, bravo!</text>
        </g>
        <g systemLanguage="en">
            <text x="10" y="20">Arright!</text>
        </g>
    </switch>    
    

    The problem is that AMP? Doesn’t like that.

    AMP Doesn’t Trust SVGs

    AMP really doesn’t like fancy things. This makes sense. It’s meant to be faster for mobile browsers, so it’s streamlined and simplified. Fine. It wants simple SVGs. Obviously one fix here is I could clean up all my SVGs. The other one would be to do what AMP suggests and that is to whitelist attributes. Except you can’t do it. Yet.

    There is a reason for this, and it’s similar to the same reason WordPress doesn’t (yet) allow SVGs to be uploaded by default. Unlike images, SVG files contain code. An user who can upload an SVG has the ability to upload code files which can be executed in the victim’s browser. Picture this. An attacker uploads an image to a forum. If the forum links to the image with <img> then the end-user’s browsers load the SVG and run the code. Yaaaaaay.

    SVGs are dangerous. And to help this, AMP says SVGs with special tags are not welcome.

    The Fix?

    Edit your SVGs or, if you’re me, ditch the images all together. I decided to go with the ditching since I wanted the pages to load fast. It’s the point of AMP after all.

  • Optimizing Images

    Optimizing Images

    After running a regeneration of my images (due to changing things on my theme) my Gtmetrix score dropped from an A to a D! In looking at why, I saw it was telling me my images should be optimized.

    Best to get on that, eh?

    The easiest way is to install jpegoptim on the server:

    $ yum install jpegoptim
    

    And then to run a compression on my images:

    $ jpegoptim IMAGE.jpg
    

    Anyone fancy running that on a few thousand images? Hell no. We have a couple options here. One is to go into each folder and run this:

    $ jpegoptim *.jpeg *.jpg
    

    The other is to sit in the image root folder and run this:

    $ find . -type f -name '*.jp?g' -exec jpegoptim {} --strip-all \;
    

    I picked --strip-all since that removes all the image meta data. While I would never want to consider that on a photoblog, I needed to compress everything and, for some reason, unless I stripped that data I didn’t get smaller sizes. For this case, it wasn’t an issue.

    What about PNGs? Use optipng ($ yum install optipng) and run this:

    $ find . -type f -name '*.png' -exec optipng *.png \;
    

    Going forward, I used Homebrew to install those locally and compress my images better, though I’m usually pretty good about remember that part. Well. I thought I was.