4 reasons to design your logo in CSS (and a few why you shouldn’t)

Intro

I recently changed the OddPrints logo on the website from an image to simple text styled with CSS and @font-face. To me it seems like the obvious thing to do but very few websites seem to employ the technique. I thought I’d explain my thoughts and I’d love to hear what you think…

1 – Sharper

This is the main reason. I recently bought a Retina MacBook Pro and the screen quality is amazing. I like to think I’m an optimist and I would normally just be happy with the obvious upgrade. The reality is that I mainly notice the apps and graphics that are not “retina ready” and just look soft and fuzzy. This in turn makes me feel dirty that I have to interact with something so dated and stupid. Here is an exaggerated example of what my logo looked like when zoomed in.

and now using @font-face…

For a programming geek, I am relatively late to stumping up the cash to join The Retina Revolution. Until now, I was blissfully unaware that work needed to be done to make your site shine on such a device. If you don’t own a retina device, pop in your local fruit shop, grab a retina macbook pro and browse a few websites. It won’t take you long to find a website that has a logo or icons that look noticibly fuzzy next to that the razor-sharp text in the body. I hope it’s not your site. People much smarter than me agree that you should make sure your logo and icons look good on such a display. The recent launch of the Chromebook Pixel should be further evidence that we’re on the brink of a future with more pixels.

2 – SEO

This should be obvious but images aren’t as simple as text when it comes to being indexed by Google. Keeping things as plain html markup means I don’t have to worry about sensible alt attributes or whatever. You can still cut-and-paste the text which is handy sometimes (but not so handy that it deserves its own section, jeez). This isn’t a big reason, more a bonus feature.

3 – Internationalisation

If you happen to write an awesome website that is used by people around the world, they probably want to read it in their language. I can’t be bothered to internationalise my sites when Google translate does such a good job. I recently saw a French blog post and this screenshot made me look twice:

Now, of course this could be seen as a bad thing if your logo is poorly translated or your branding is so important that you need full control of the exact appearance. As I’m writing this, the more I’m going off this point. It not like my url changes so why should my logo… thoughts please!

4 – Accessibility

<h1>Awesome Homepage</h1> is about as simple as it gets for html markup. If you’re blind or using some obscure text-based web browser, this is handy.

Cons

Legacy browser support is sketchy. Initial page load may be slower if using javascript or the font files are big. You may experience a flash of un-styled text in some browsers. These problems can all be reduced with crazy tweeks like only including the characters you need in the font file, or base64 encoding them in the css. Tools such as fontsquirrelGoogle’s web fonts (and loader) solve most things, but even that takes a little reading just to get your head around. This all boils down to the same con: It’s hard to get right. Of course, if you’re already using web fonts elsewhere on your site, you’ve already solved these problems ;)

Alternatives

If you still want to serve images, and I’m not putting forward a fantastic argument why you shouldn’t, I strongly recommend serving higher-resolution alternatives to displays that need them. It’s pretty simple to do with libraries such as retina.js. For example, when on my laptop, microsoft.com serves me a nice high resolution version of their logo. Fail to do this and your site just lost 5 subconscious quality points in your customer’s noggin.

You could also just upload your logo as a vector image. Similar problems with browser support but certainly a valid option.

Conclusion

Designing your logo as a simple font that can be rendered in a browser isn’t for everyone. Big companies pay designers to draw clever graphics that need more than just a font file, and many websites care more about appearing the same on as many devices and browsers as possible. If however you just want something that looks great on most platforms but you care more about impressing those visitors on modern, expensive devices, maybe a font-face/css/svg logo is for you.

How to track e-commerce sales in Google Analytics with the Google Wallet Java SDK

If you have your cart built up in javascript then just follow Google’s documentation. If you submit your Google Wallet cart server-side using the java SDK and want to track the sale using Google Analytics’ ecommerce feature, then this blog is for you…

Before you start, make sure you have enabled the ecommerce feature in Google Analytics.

In your Java code, you need to set the analyticsData (String) in the cart:

1
merchantflowSupport.setAnalyticsData(analyticsData);
merchantflowSupport.setAnalyticsData(analyticsData);

To get that analyticsData string, I pass it to my server with a query param using a bit of javascript:

1
2
3
4
5
6
7
8
9
$("#google-purchase-link").click(function(e){
    _gaq.push(function() {
        var pageTracker = _gaq._getAsyncTracker();
        setUrchinInputCode(pageTracker);
        console.log(getUrchinFieldValue());
        window.location = "/purchase/google?analyticsData=" + getUrchinFieldValue();
    });
    e.preventDefault();
});
$("#google-purchase-link").click(function(e){
    _gaq.push(function() {
        var pageTracker = _gaq._getAsyncTracker();
        setUrchinInputCode(pageTracker);
        console.log(getUrchinFieldValue());
        window.location = "/purchase/google?analyticsData=" + getUrchinFieldValue();
    });
    e.preventDefault();
});

I use the async version of Google Analytics, jQuery, and Jersey so I’ve skipped that stuff because that’s not important and you probably do it differently. If you use the old ga.js and GWT, then this blog post may help you better.

If you get stuck, I use this solution for OddPrints.com which is open-source so that should fill any gaps.

Build a super fast PC in 2013 for just £566

Looking to build a new PC? Want to build one that will play nicely with Windows or Linux or could even be a Hackintosh? Making sure you choose the right combination of components is important and a pain to research. If you’re feeling lazy, here’s my shopping list for the machine I built that I’m using right now.

By the way, it’s faaaaaast ;)

CPU – Intel Ivy Bridge (i5-3570K) - £169
SSD – SanDisk 120GB (SDSSDX) - £73
HDD – Seagate 1TB (ST31000524AS) - £59
RAM – Corsair 4x4GB (CMZ16GX3M4A1600C9B) - £67
DVD – Sony (AD-7280S-0B) - £17
PSU – Corsair (CX430V2) - £34
Case – Corsair (Carbide 400R) - £79
Motherboard – Gigabyte (Z77-DS3H) – £68

Total price – £566

I’ve been using this machine for a couple of months now and it is a pleasure to use. Here are some notes:

  • It boots into Ubuntu in about 10 seconds.
  • I’ve not overclocked my build, because it’s already faster than me anyway.
  • Don’t skip the SSD, it’s essential for a fast machine!
  • It supports mSATA, but don’t be tempted, just stick with a separate SSD. Keep your OS and apps on the SSD, and your files on the HDD.
  • I’m using the stock CPU fan and it works well and is quiet.
  • The case is pretty big. I prefer that but a much smaller one would be fine.
  • I don’t play games so this build doesn’t have a graphics card. The onboard graphics are more than good enough for me (no problems with HD video etc.). No doubt I’ll buy one in a year or two when I need to drive a 50″ retina display ;)
  • View all the parts in this Amazon wish list.
  • You don’t need any special brackets for the SSD, this case already has mounting screws for it.
  • You don’t need any thermal paste, it all just clicks together.

Got any improvements or updates? Post in the comments below.

Connecting to EC2 from Chrome’s Secure Shell using only a PEM file

Gee, what a title. I know this post is going to be popular.

First, you need to generate your public key from your private key like this:

ssh-keygen -y -f yourkey.pem > yourkey.pub
Then, in Secure Shell, select the ”Import…” link to bring up a file picker. You must import two files for each identity.  A private key and a public key.  For example, you would select both “yourkey.pem” and ”yourkey.pub”.
It should look something like this:
Then just connect! Once connected, you can bookmark the page for instant access.

Importing photos from a Canon EOS camera with Ubuntu 12.10

For some reason, when I try to connect my Canon EOS 7D in Linux I get this error:

Unable to mount Canon Digital Camera
Error initialising camera: -1: Unspecified error

I get no love from lsusb:

matt@beast:~$ lsusb
Bus 001 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 002 Device 002: ID 8087:0024 Intel Corp. Integrated Rate Matching Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 045e:0745 Microsoft Corp. Nano Transceiver v1.0 for Bluetooth

Thanks to a comment in this thread I tried a different usb port and now it works. Huzzah. It seems to be a problem with the usb 3 ports, no idea why.

I hope this post helps someone out :)

Allow worldwide shipping with Google Checkout/Wallet java SDK

Google’s java SDK for interacting with the checkout API is not a fun experience. It’s generated from the WSDL using JAXB which has a unique ability to generate client interfaces that no human could ever “design”.

It’s not actually that hard to write code with, you just have to think like a deranged machine.

By default, the only allowed shipping destination will be your country. If you wish to allow worldwide shipping, you have to do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
ApiContext apiContext = new ApiContext(environment, "yourMerchantId", "yourMerchantKey", "GBP");
CheckoutShoppingCartBuilder cartBuilder = apiContext.cartPoster().makeCart();
CheckoutShoppingCart cart = cartBuilder.build();
FlatRateShipping frs = new FlatRateShipping();
frs.setName("flat rate");
 
ShippingRestrictions shippingRestrictions = new ShippingRestrictions();
AllowedAreas allowedAreas = new AllowedAreas();
allowedAreas.getUsStateAreaOrUsZipAreaOrUsCountryArea().add(new WorldArea());
shippingRestrictions.setAllowedAreas(allowedAreas);
frs.setShippingRestrictions(shippingRestrictions);
 
...
ApiContext apiContext = new ApiContext(environment, "yourMerchantId", "yourMerchantKey", "GBP");
CheckoutShoppingCartBuilder cartBuilder = apiContext.cartPoster().makeCart();
CheckoutShoppingCart cart = cartBuilder.build();
FlatRateShipping frs = new FlatRateShipping();
frs.setName("flat rate");

ShippingRestrictions shippingRestrictions = new ShippingRestrictions();
AllowedAreas allowedAreas = new AllowedAreas();
allowedAreas.getUsStateAreaOrUsZipAreaOrUsCountryArea().add(new WorldArea());
shippingRestrictions.setAllowedAreas(allowedAreas);
frs.setShippingRestrictions(shippingRestrictions);

...

I just posted this because I could not find one example on the internet. Crazy I know, but now there is one.

How to add zero-padding to a folder full of numbered files

Have you got a folder of files that look like this?
file_1.png
file_2.png
file_3.png
file_10.png
file_11.png
file_12.png
file_100.png

But what you really want this:
file_001.png
file_002.png
file_003.png
file_010.png
file_011.png
file_012.png
file_100.png

Then, my linux-loving friend, run this:
rename 's/\d+/sprintf("%03d",$&)/e' *.png

Fun and games with the new PayPal java SDK

Up until now I’ve handled my payments for stolencamerafinder and OddPrints with Google Wallet. It’s not fantastic api (I’ve even had to fix it) but it’s pretty straight forward. There’s good documentation and the SDK saves you from writing all that boilerplate for talking to a web service.

I’ve had quite a few requests for allowing PayPal so I’ve spent the last couple of days flicking through their documentation and was quickly shown that:

The X.Commerce Developer Tools team announces the stable release of new SDKs for PayPal APIs after several months in beta testing.

Great news. A stable release. It even includes some work that allows it to run on Google App Engine.

Time to crack on, right, where’s the documentation. Oh. There isn’t any because it has:

Self-documented classes that describe the APIs

Ok, fine, I’m cool with that. I’ve downloaded the SDK, added it to my project. Now all I have to do is rummage through the packages to find the class that will serve as my entry point to the API. Tell you what, I’ll give you a head start, here’s the package it’s in.

Yep, there it is, buried amongst 172 generated classes, you want the PayPalAPIInterfaceServiceService. What a name. I’ve gone through the JAXB pain far too many times in my java career and although I don’t find it particularly difficult any more, it is still impossible not to vomit over my keyboard each time I’m forced to code against it.

This is (my first attempt at) a very basic call to the api:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
        PayPalAPIInterfaceServiceService paypal;
        try {
            InputStream is = req
                    .getSession()
                    .getServletContext()
                    .getResourceAsStream(
                            "/WEB-INF/paypal-sdk-config.properties");
 
            paypal = new PayPalAPIInterfaceServiceService(is);
        } catch (FileNotFoundException e) {
            // Urgh, I have to catch a checked exception that can only be thrown
            // by the other, overloaded, file-based constructor. Nice work
            // PayPal.
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
 
        SetExpressCheckoutReq setExpressCheckoutReq = new SetExpressCheckoutReq();
        SetExpressCheckoutRequestType setExpressCheckoutRequestType = new SetExpressCheckoutRequestType();
        SetExpressCheckoutRequestDetailsType setExpressCheckoutRequestDetails = new SetExpressCheckoutRequestDetailsType();
 
        List paypalItems = Lists.newArrayList();
 
        PaymentDetailsItemType paypalItem1 = new PaymentDetailsItemType();
        BasicAmountType amount1 = new BasicAmountType();
        amount1.setValue("0.50");
        paypalItem1.setAmount(amount1);
        paypalItem1.setQuantity(1);
        paypalItems.add(paypalItem1);
 
        PaymentDetailsItemType paypalItem2 = new PaymentDetailsItemType();
        BasicAmountType amount2 = new BasicAmountType();
        amount2.setValue("0.25");
        paypalItem2.setAmount(amount2);
        paypalItem2.setQuantity(1);
        paypalItems.add(paypalItem2);
 
        PaymentDetailsType paymentDetails = new PaymentDetailsType();
        paymentDetails.setPaymentDetailsItem(paypalItems);
 
        setExpressCheckoutRequestDetails.setPaymentDetails(Lists
                .newArrayList(paymentDetails));
        setExpressCheckoutRequestDetails
                .setCancelURL("http://oddprints/cancel");
        setExpressCheckoutRequestDetails
                .setReturnURL("http://oddprints/checkout");
 
        BasicAmountType totalAmount = new BasicAmountType();
        totalAmount.setValue("0.75");
        totalAmount.setCurrencyID(CurrencyCodeType.USD);
        
        // not this, this is deprecated (but not deprecated in the code)
        // setExpressCheckoutRequestDetails.setOrderTotal(totalAmount);
 
        // set both of these instead
        paymentDetails.setItemTotal(totalAmount);
        paymentDetails.setOrderTotal(totalAmount);
 
        setExpressCheckoutRequestType
                .setSetExpressCheckoutRequestDetails(setExpressCheckoutRequestDetails);
        setExpressCheckoutReq
                .setSetExpressCheckoutRequest(setExpressCheckoutRequestType);
        SetExpressCheckoutResponseType response = null;
 
        try {
            response = paypal.setExpressCheckout(setExpressCheckoutReq);
        } catch (SSLConfigurationException e) {
            // TODO
        } catch (InvalidCredentialException e) {
            // TODO
        } catch (HttpErrorException e) {
            // TODO
        } catch (InvalidResponseDataException e) {
            // TODO
        } catch (ClientActionRequiredException e) {
            // TODO
        } catch (MissingCredentialException e) {
            // TODO
        } catch (OAuthException e) {
            // TODO
        } catch (IOException e) {
            // TODO
        } catch (InterruptedException e) {
            // TODO
        } catch (ParserConfigurationException e) {
            // TODO
        } catch (SAXException e) {
            // TODO
        }
        PayPalAPIInterfaceServiceService paypal;
        try {
            InputStream is = req
                    .getSession()
                    .getServletContext()
                    .getResourceAsStream(
                            "/WEB-INF/paypal-sdk-config.properties");

            paypal = new PayPalAPIInterfaceServiceService(is);
        } catch (FileNotFoundException e) {
            // Urgh, I have to catch a checked exception that can only be thrown
            // by the other, overloaded, file-based constructor. Nice work
            // PayPal.
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        SetExpressCheckoutReq setExpressCheckoutReq = new SetExpressCheckoutReq();
        SetExpressCheckoutRequestType setExpressCheckoutRequestType = new SetExpressCheckoutRequestType();
        SetExpressCheckoutRequestDetailsType setExpressCheckoutRequestDetails = new SetExpressCheckoutRequestDetailsType();

        List paypalItems = Lists.newArrayList();

        PaymentDetailsItemType paypalItem1 = new PaymentDetailsItemType();
        BasicAmountType amount1 = new BasicAmountType();
        amount1.setValue("0.50");
        paypalItem1.setAmount(amount1);
        paypalItem1.setQuantity(1);
        paypalItems.add(paypalItem1);

        PaymentDetailsItemType paypalItem2 = new PaymentDetailsItemType();
        BasicAmountType amount2 = new BasicAmountType();
        amount2.setValue("0.25");
        paypalItem2.setAmount(amount2);
        paypalItem2.setQuantity(1);
        paypalItems.add(paypalItem2);

        PaymentDetailsType paymentDetails = new PaymentDetailsType();
        paymentDetails.setPaymentDetailsItem(paypalItems);

        setExpressCheckoutRequestDetails.setPaymentDetails(Lists
                .newArrayList(paymentDetails));
        setExpressCheckoutRequestDetails
                .setCancelURL("http://oddprints/cancel");
        setExpressCheckoutRequestDetails
                .setReturnURL("http://oddprints/checkout");

        BasicAmountType totalAmount = new BasicAmountType();
        totalAmount.setValue("0.75");
        totalAmount.setCurrencyID(CurrencyCodeType.USD);
        
        // not this, this is deprecated (but not deprecated in the code)
        // setExpressCheckoutRequestDetails.setOrderTotal(totalAmount);

        // set both of these instead
        paymentDetails.setItemTotal(totalAmount);
        paymentDetails.setOrderTotal(totalAmount);

        setExpressCheckoutRequestType
                .setSetExpressCheckoutRequestDetails(setExpressCheckoutRequestDetails);
        setExpressCheckoutReq
                .setSetExpressCheckoutRequest(setExpressCheckoutRequestType);
        SetExpressCheckoutResponseType response = null;

        try {
            response = paypal.setExpressCheckout(setExpressCheckoutReq);
        } catch (SSLConfigurationException e) {
            // TODO
        } catch (InvalidCredentialException e) {
            // TODO
        } catch (HttpErrorException e) {
            // TODO
        } catch (InvalidResponseDataException e) {
            // TODO
        } catch (ClientActionRequiredException e) {
            // TODO
        } catch (MissingCredentialException e) {
            // TODO
        } catch (OAuthException e) {
            // TODO
        } catch (IOException e) {
            // TODO
        } catch (InterruptedException e) {
            // TODO
        } catch (ParserConfigurationException e) {
            // TODO
        } catch (SAXException e) {
            // TODO
        }

Bleurgh. Having to write this kind of shit gives java a really bad name. Is there really any improvement over just calling the API without any SDK? There are so many things wrong with this that I’m surely going to miss stuff:

  • Declaring you throw a checked expection that you don’t throw.
  • Inconsistent naming.
  • The insane object nesting generated by JAXB .(SetExpressCheckoutReq>SetExpressCheckoutRequestType>SetExpressCheckoutRequestDetailsType etc.).
  • Who cares we’re using a strongly typed language, let’s pass doubles in strings and just document what’s valid – “Must not exceed $10,000 USD in any currency. No currency symbol. Decimal separator must be a period (.), and the thousands separator must be a comma (,).”
  • Redundantly having to pass the total for the whole basket.

Matt, keep calm, breath deeply. It’s ok. You can wrap this cruft up behind a nice clean bit of code and you never have to look at it again…

Phew, let’s run it.

Bang. That threw a ClientActionRequiredException. Looking at the response I can see “XML syntax error”. No more help than that. Time to take a look at the xml that was generated and I quickly spot that it’s not legal:

<ebl:Amount0.5</ebl:Amount>

What? I’m using an SDK and it still generates duff xml. I was pretty pissed off at this point and lost my cool. I glanced at the source code and thought I’d found a bug so fired off a bug report.

It turned out it wasn’t so much a bug, just an example of why munging xml by hand is a bad idea. It was my fault for not sending the currency code for each and every item in the basket.

Now we can add one more thing to our list or gripes:

  • Having to pass the currency code for each item in basket.

I didn’t want this post to be a rant. No-one wants to read that, I’m just frustrated that it’s taken me two days to get to the simplest end-to-end test. If anyone at PayPal reads this, please hide away the JAXB objects and expose a cleaner set of interfaces for the java SDK. It’s the sort of thing I would normally write myself and open-source, but I feel in this case, it’s not something that would benefit the people so much as it would benefit PayPal.

The Bristol Two Towers Running Challenge

Fancy a running challenge around Bristol that’ll test your legs and your navigation? All you have to do is touch both Cabot Tower and Troopers Hill Tower (er, chimney) and finish where you started.

Troopers Hill Tower

Cabot Tower

The rules are pretty simple:

  1. You have to touch both Cabot Tower and Troopers Hill Tower. Any order.
  2. You must start and finish at the same place (of your choice).
  3. No pausing of your watch (for traffic etc.).

If you take on the challenge, let me know in the comments and I’ll add your results to the table.

Leaderboard:

Date Name Time Route
10th March, 2013 Matt Burns 54:10 GPS
2nd August, 2012 Justin Ainsworth 54:54
13th October, 2012 Neil 1:08:33 GPS
13th October, 2012 Rob 1:09:06 GPS
13th October, 2012 Mike 1:09:14 GPS
17th February, 2013 Steve Knight 1:18:00
25th September, 2012 Olly 1:26:29 GPS

How to install Google Voice

Oh jeez, really? A blog post about doing something so easy? Well, yes because it took me about an hour of messing around and so if this helps save one person from the pain, then it’s worth writing. If you’re in a rush or already bored, jump to the short answer.

If you use gmail, you may see the option in the Chat widget to “Call phone”:

Call phone link

Great. Sounds fun, let’s try it:

No plug-in installed

Oh dear. We don’t have the plug-in installed. We are told “Please download the voice plug-in to make a call.”

Here comes the problem. The link takes you to a download page that has javascript errors preventing your from completing the download:

Bad js

Refused to display document because display forbidden by X-Frame-Options. Uncaught TypeError: Object [object DOMWindow] has no method ‘onInstallButtonClicked’

Hmm, ok, what next? May I suggest at this point you don’t waste your time trying a different browser, then a different operating system, then a different machine. It’s stupid, you’re not thinking clearly. Take a break and drink some water.

The problem was with the initial link: http://www.google.com/intl/en-GB/chat/voice/. (I assume you will get a different link depending on your language/location.)
 
Thankfully, a working page does exist, you just have to find it. https://www.google.com/chat/video. Just go there, the page looks different, has no errors, and allows you to call people without resorting to Skype.