Friday, March 10, 2017

Google Acquires AppBridge Software

At the Google Cloud Next conference yesterday it was announced that Google has acquired AppBridge. I've been at AppBridge for just over a year and I'm thrilled to be a part of this Vancouver software success story.

AppBridge is a tremendous addition to the Google solutions because the founders designed the AppBridge Transformation Suite from the ground up for performance and the cloud. AppBridge is already handling the world's largest G Suite and Google Drive migrations.

Here is a quote from Google's announcement:

"Migrating to the cloud can be complex. It's not just your files that need to be moved; permissions also need to map correctly; content likely needs to be reorganized, and some data probably needs to be archived. To address that challenge, today we are announcing the acquisition of AppBridge, an enterprise-grade, G Suite migration tool that helps organizations seamlessly migrate from their on-prem, cloud-based and hybrid solutions to Google Drive.

With AppBridge, your organization can migrate files effortlessly to G Suite from your existing file servers or content management systems like SharePoint, or from many other cloud platforms you might be using. File permissions are also brought over when you migrate, which means your team's file access remains unchanged and your data stays safe. We’re working together with AppBridge to bring them into the G Suite team. Stay tuned for more information in the near future."

As for me, I'm going to take some time to think about what I want for my next adventure, but this one was certainly memorable.

TechCrunch Article: Google acquires AppBridge to help enterprises move their files to its cloud services

Wednesday, February 15, 2017

Google Sheets Split into Columns

I'm very much appreciating Google Sheets' split into columns feature. Ya, I know, it's not like it hasn't been done before, but man it's nice to have when you need it.

To split text into columns using separator options (including space, comma, custom, etc.):
  1. Open a spreadsheet in Google Sheets.
  2. Paste the data you want to split into columns.
  3. In the bottom right corner of your data, click the Paste icon.
  4. Click Split text to columns. Your data will split into different columns.
  5. To change the delimiter, in the separator box, click Comma to open the dropdown of options.

Wednesday, January 18, 2017

Building Video Games with My Daughter and Lego

I asked my (then) 4-year-old daughter if she wanted to make a video game, and her response was exactly what I expected, "We can't make a game." "Actually, " I said, "we can! Want to do it?" She emphatically said yes, so we set out to make an iOS game. Of course, I had already done some playing around with Unity 3D, so I knew that a 2D game wouldn't be too much work.

I wanted her to have the best experience possible, so I devised a plan to take her ideas and convert them into the game without changing much along the way. I used a sample 2D game as a starting point so we wouldn't have to spend too much time writing code before she saw her creations in a working game. Note that if you don't deploy your app to the store, you can do all of this for free (plus the cost of Lego of course).

We started by drawing out the main elements of the game by hand. Here are the villain and hero characters as my daughter drew them.

After that, it was off to the Lego store so we could get enough 2x2 pieces to build whatever we wanted. Here's the villain Lego version my daughter created from her original drawing.

We designed the rest of the game elements including a tree and an egg, and then I used photos of the Lego concepts to convert her creations to simple sprite versions. Here's another character before reducing the pixels down to a simple sprite.

My daughter building Lego versions of the game sprites.

This is a screenshot from the first build I deployed to my iPhone (just for fun, I threw in a photo of my daughter as the player). Not bad for a 4-year-old with some Lego!

We had a great time on this project and I was promoted to write this post when she recently asked if we could play the game again.

Wednesday, December 28, 2016

How GitHub Shows My Second Child Arrived

I happened to notice this funny visual representation of the 2015 birth of my son on my GitHub profile. When anyone asks me questions about having kids, I'll have to show them this.

I'm pasting in a screenshot of my profile page below. The heat map is pretty clear about my son arriving in September. There's one outlier green square after his arrival--which I assume was me just committing every change I had made before his birth.

Tuesday, November 01, 2016

BIA Woman Indiegogo Campaign is Live!

Two of my friends have set out to create the perfect pants for female athletes. Today they launched their Indiegogo campaign for BIA Woman's perfect pants. Check it out and help support female athletes everywhere!

biawoman perfect pants

"We took it to other athletes – our clients, training partners, friends, and competitors. We listened to the problems they were struggling with and then asked ourselves the most important question of all: How can we make a difference?
BIA WOMAN ATHLETICS was born. We partnered with a team of ingenious fabric and design experts, recruited high-performance women athletes across multiple disciplines to test our products and came up with the best line of workout clothing designed by and for athletic women."

Saturday, October 15, 2016

My Blog's Most Popular Search Term: Microsoft Bob

This goes into the wacky Internet category. I just happened to glance at the "Search Keywords" stats for my blog in the Blogger admin console and noticed the single most popular search term (since I started the blog around 2005) that has led people to the geeklit blog is "Microsoft Bob."

What's possibly even more bizarre is that number two is "barcelona aquarium." Really? Out of all the pages out there, people are searching for an aquarium and thinking, "ya, geeklit sounds like the right site for that."

Very strange people...

Sunday, September 18, 2016

Why I Just Bought a MacBook Pro

I wrote a post in 2014 entitled, Why I Just Bought a Chromebook, so it may seem odd that I'm now writing this post. To be clear, I'm not backing off my favourable position on Chromebooks at all. They're great and they can be a lot more affordable than other laptops... like the MacBook Pro, for example. MacBooks are overpriced; I feel that's a pretty defensible comment. So why would I buy one?

The answer is actually quite simple and surprisingly non-technical. I have spent my entire career working in software, but I've never bought an Apple computer of any type. I've been using my wife's old MacBook for years, but I've never experienced the best that Apple has to offer and I always wondered what I was missing. If you combine that with the current popularity of Macs--especially amongst web developers--and I just decided it was time. I've run many versions of Windows. I've used FreeBSD and some flavours of Linux (including Ubuntu on my Chromebook), but it's not until now that I can say that I've really had the full Mac experience.

Unfortunately, this version of the MacBook is missing some of the hardware niceties that I enjoyed in the older MacBook I used. For example, the MagSafe connector for the power cord is gone, there is no SD card reader, no light to show the battery is charging, and no external lights to show how much of the battery remains. Those were all useful. Most people have heard about the lack of ports on this new laptop--I've given a nod to that controversy by including a dongle in the image above. Almost everything requires a dongle which is a pain. One notable exception is the headphone jack. Until the iPhone 7, the MacBook Pro actually does have a headphone jack which is nice. I'll have to see how much of an issue this is (or isn't) for me. So far, I've only purchased one dongle--for USB devices. That will cover a lot for me. If I really have issues, I'll get one of the third-party adaptors that adds ports to the machine. (Update: I saw a pretty cool "HyperDrive" one that adds an SD card reader, USB port and HDMI port.)

Of course, there are lots of improvements as well. The screen is much better, the battery life is improved and everything just generally runs faster on the newer processor and RAM. I also really like the keyboard. It has a very satisfying click which reminds me of a mechanical keyboard.

I haven't had the machine very long, so this isn't a proper review. At this point, I'm feeling good about the purchase. Yes, I do miss some MacBook features, but the Pro machine is much faster than my old one and allows me to project to Apple TV. The other one was too old to offer that feature--which gives you an idea of just how big a step us this was in hardware. 

Sunday, August 14, 2016

Angular 2 ng serve or ng build Permissions Error

Update: If you're running macOS, you should just use Homebrew from the start and you'll likely avoid these issues.

If you grab an Angular 2 project from the web, you might find that you run into permissions errors after you install it.

For example, these commands are a common example of a simple project install. (This example uses yarn, but NPM would have similar results.) The addition of 'sudo' is common on Macs, but it can cause the permissions problem.

$ sudo npm install -g angular-cli
$ sudo npm install -g yarn
$ sudo yarn install

The problem occurs when you try to run $ ng serve (or $ ng build); you see an error such as this one:

EACCES: permission denied, open '/Users/cawood/GitHub/test/node_modules/arr-flatten/index.js'
Error: EACCES: permission denied, open '/Users/cawood/GitHub/test/node_modules/arr-flatten/index.js'
    at Error (native)
    at Object.fs.openSync (fs.js:640:18)
    at Object.fs.readFileSync (fs.js:508:33)
    at Object.Module._extensions..js (module.js:578:20)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.require (module.js:497:17)
    at require (internal/module.js:20:19)
    at Object. (/Users/cawood/GitHub/mix/node_modules/arr-diff/index.js:10:15) 

The error occurs because the install ran as the admin user. The quick solution is to give the current user sufficient rights to the folder structure in your project. This example is heavy-handed, but it's fine for prototypes. These commands change the permissions recursively for everything (files and folders) in the test directory which is the one that contains the Angular 2 project.

$ sudo chmod -R +rwx test 
$ cd test 
$ ng serve

Tuesday, July 05, 2016

Switching to Google Cloud Platform Storage

After staring down an upcoming bill for website hosting, I quickly decided to switch my website content (including most of the images on this blog) to Google Cloud Platform (GCP) storage buckets. GCP storage buckets for static websites are much cheaper and I liked the idea of having all my content being managed by Google's cloud services.

To setup static content in GCP storage buckets, follow the steps outlined in the Google Cloud article, Hosting a Static Website.

To serve content from your custom domain, you'll need FTP access to upload a file to your current hosting storage. This is how Google verifies that you own the domain.

Once you've verified your domain, you create a storage bucket named the same as your domain (e.g., and then upload your files to the storage bucket. There are two ways to upload content, you can use the dead easy Storage Browser web interface, or you can use gsutil. gsutil is a Python app that enables command line access to GCP.

Even if you use the web UI to upload your files, you'll probably still want to use gsutil for some additional actions. For example, to set files as public, you have to go into every folder using the UI (it's labourious to say the least), but gsutil provides a simple command to accomplish the same thing extremely quickly. This one command recursively sets every file in the bucket to public read: gsutil -m acl set -R -a public-read gs://bucketname

After you've got your files uploaded and set to public, you can test the links from the Storage Browser and then your bucket is ready to go. The last step is to go to your domain registrar's site and add a CNAME entry for GCP storage. For example, points to After adding the alias, you can verify it's working by using the free CNAME lookup tool on

That's it! You're now saving money and your files are being managed by someone else. Very nice.

Note that if you're moving from Windows hosting to GCP, you might be switching from an IIS web server on Windows to a Linux backend. This means that your new Linux driven URLs will be case sensitive. This actually broke some of my image links because I hadn't matched the case of the file path exactly in the HTML and IIS doesn't care about such details.

Update: I saved over $120 by not renewing my hosting plan and my first Google Cloud Platform monthly bill was 0.08c.

Friday, June 17, 2016

Google Celebrates "Lady Day"

"Ms. Porat is the CFO, not the lady CFO." -  Danielle Ginach 

The women of Google have come up with a clever, tongue-in-cheek way to raise awareness about gender equality after an investor made a sexist remark at the company's annual shareholders meeting last week.

As my daughter grows up, I have become more and more aware of the issues women face in the workplace. Please read the Business Insider article about Google's Lady Day for the full story.

Wednesday, May 04, 2016

Spam iCloud Calendar Event Ads

Arggghhhh, now some waste of skin has figured out how to spam people by sending iCloud calendar invites to random email addresses. I just got an ad for Ray-ban sunglasses in my calendar. I like to refer to Spam that's sent by real people to the wrong address as "Spork." I'm not sure what to call this new type. Maybe "Spam Lite" or "Spam Singles"--both of which are real things BTW. Ya, Spam Singles sounds good, but moving on...

Thanks to user duozmo on for the solution. To prevent this type of spam:
Log into iCloud on the web, open your calendar and go to ⚙  > Preferences. Under Advanced, you’ll find an option to receive calendar invitations by email instead of straight into your calendar.

This should allow you to delete calendar spam like regular email spam.

The other annoyance is that you can't delete or modify these since only the organizer can do that. To remove any spam you get this way.

1. Open the Calendar app from your Mac (not the iCloud web interface)
2. Create a new calendar (e.g., called "Junk")
3. Right-click the spam event and choose the Calendar option. Select "Junk" from the list of calendars and the malicious event will be moved.
4. Delete the Junk calendar entirely (without notifying).

Tuesday, April 26, 2016

Chromebooks to run almost every Android app?

Oh let it be true... please let this be true: Chromebooks may soon be able to run almost every Android app.

From the article, "Chromebook users will soon get access to the 'more than a million' games and apps on the Google Play Store."

This would be huge. I've already written about how much I enjoy my Chromebook, but having full access to the massive Google Play store would be unbelievable. As more and more software is written in app form, 'old school' operating systems are becoming less and less relevant. ChromeOS is a fantastic bridge between having to do everything on a tiny screen and running a heavy O/S on a larger machine.

Wednesday, March 30, 2016

Why I Switched from Apple Music to Google Play Music

When Rdio was shut down, I successfully used Move to Apple Music to copy my playlists into a free trial of Apple Music. However, after using Apple Music for a few months, I've run into more snags than I can handle, and I've decided to migrate to Google Play Music.

Google Play Music

Fortunately, just like my previous switch, there is a way to get my playlists over to the new platform. To copy playlists from Apple Music to Google Play Music, I'm using Although it seems to have the same issue I encountered in the last move--not choosing the same recording of the songs--it's certainly better than redoing all that time intensive work of creating the lists. The premium version costs a few dollars, but it's worth it.

So why go through the hassle of switching streaming services again? Here are the "why not" reasons:

  • The cost is the same for the family plan, so price isn't an issue.
  • I can copy over my playlists (with a bit of clean up required)
  • I thought Apple Music would work seamlessly with iTunes. For example, I expected to be able to freely mix my content from iTunes and the streaming service. That's not the case. There appears to be no synergy there.

The deal breaker was the flexibility of Apple Music--or rather, the lack of flexibility. I simply cannot use Apply Music on enough devices in my house. Here are the devices/options I tested:

  • Windows PC: works via iTunes
  • MacBook: works via iTunes
  • iPhone 6: works via Apple Music App
  • iPhone 3 (my daughter plays with it): no app, does not work at all
  • Android Tablet (Kindle Fire): no app, does not work at all
  • Nexus 5x: works via Apple Music App
  • Chromebook: does not work at all

Obviously, some of these are more important than others, but the fact remains that Google Play Music works fine on every one of these devices. Furthermore, it does not require any install to run on a PC/Mac/Linux/ChromeOS browser; I really miss the web listening experience that I had with Rdio and I want that back. In the end, it was an easy decision to make the switch and go Google.

Saturday, February 06, 2016

Google Drive API JavaScript App Running in App Engine

I worked through the JavaScript quickstart guide of the Google Drive V3 API and modified it slightly so I could host and run the whole thing in the Google App Engine . I suggest you work through the quickstart once (following all the steps) before you read on--unless you're already familiar with App Engine and the Google Cloud Console, it may be difficult to follow what needs to change. The quickstart is a simple one-page app that allows the user to authorize access to their Google Drive and then shows ten files and/or folders in that Drive.

Why would you want to do this? It's great to be able to create your app that uses the Google Apps APIs, but even better is to host that app within the Google App Engine so that you don't have to worry about maintaining any of the infrastructure that runs your app. You also get the benefit of sophisticated App Engine features such as performance scaling.

The steps in the Drive V3 quickstart ( will get you most of the way, but with a few changes, you can serve the app from the Google App Engine, which is super powerful and a more modern mechanism for running a web application.

One thing you'll have to change is the URL that your client ID will accept. The quickstart expects you to host and run the app on your local machine, and therefore use http://localhost as the URL. You can enter that when you work through the tutorial, but you'll have to change the URL for "Authorized JavaScript origins" later to the URL for your particular app. However, you won't have that URL until you try running your app from the Google Cloud Shell.

Your client ID should look something like this (I've edited it so as not to disclose my client ID):

Here are some notes to clarify how to get the quickstart running in Google App Engine from the Google Developer Console:

  • You'll likely want to create your own GitHub repo and clone it to your local machine.
  • When you get to the step in the tutorial, you can connect your app project to your new Git repo. You'll have to authorize Google App Engine to use your repo. By connecting the two, it is super easy to manage (and even edit) your code from within the Google Developer Console.
  • Create the sample file (with code provided) in the local repo and push it to master that is synching with GitHub.
  • Don't forget to change your client ID in the sample code: var CLIENT_ID = '';
  • Once you have everything ready. Make sure you have your new quickstart project selected and then open the developer cloud console. The console is a Linux based shell.
  • Change directory (CD) to the src directory for your quickstart project.

cawood@definite-destiny-999999:~$ cd src/definite-destiny-999999/master

  • Start a web server using Python to host your application: python -m SimpleHTTPServer 8080

cawood@definite-destiny-999999:~/src/definite-destiny-999999/master$ python -m SimpleHTTPServer 8080
Serving HTTP on port 8080 ... - - [12/Jan/2016 00:11:56] "GET /?authuser=0/ HTTP/1.1" 200 - - - [12/Jan/2016 00:11:58] "GET /quickstart.html HTTP/1.1" 200 -

  • Open the Web preview from the console window in port 8080. When you navigate to the quickstart.html file, you will get the URL you need to add to your client ID credentials. The Web preview option is in the top-left corner of the cloud console. 
  • Find your client ID in the credentials section of the dev console and edit it to add the URL you just opened in web preview. (To stop the app (quit the web server), press Ctrl+C.)

That's it! You should now have the quickstart running in Google App Engine! Sweet.

Note: I do not cover deploying your app for production use. Perhaps that can be a future post.

Note: If you've made a change and pushed it from your local machine, but the file isn't updating, try closing and reopening the console. I've found that it doesn't pick up changes to the files unless I do this.

I've made the project public on GitHub:

Wednesday, January 13, 2016

I AM CARDBOARD VR Url Not Recognized Setup Error

For Christmas, I picked up a Google Cardboard viewer. It's the easiest (read: cheapest) way I know to get a virtual reality headset for home use. When I co-wrote Augmented Reality: A Practical Guide for the pragmatic programmers (in 2006), headsets for augmented reality or virtual reality were expensive and bulky.

That certainly has changed. Google cardboard devices are literally made of cardboard. There are lots of choices. I got the I AM CARDBOARD® 45mm Focal Length Virtual Reality Google Cardboard. So far, I'm impressed with the inexpensive device. Enabling clicking in the virtual user interface by moving a magnet on the side of the device is ingenious.

Unfortunately, I quickly ran into a problem with the set up. When asked to use my phone's camera to grab the QR code on the cardboard, I received the error "URL not recognized" or "Problem in parsing the Url." QR codes represent a URL and if that URL changes, they can break. The solution was to find the manufacturer's website and search for a QR code there. In this case, I found a working QR code for the I AM CARDBOARD. Now I'm looking forward to trying out more of the apps with my daughter.

Update: My daughter loved it! I shot a video of her reaction that I'm sharing with family. So cool.

BTW: from the site -- "Calibrate your VR headset using the QR code found in this listing images. To do so, open the Google Cardboard app, select "Configuration" menu option, click on "Switch viewer" and scan the QR code."

Friday, December 04, 2015

My Brother's D&D Kickstarter Campaign

The World of Myrr is a Kickstarter campaign that my brother put together. It's a detailed and play tested campaign world for Dungeons and Dragons Fifth Edition

If you know any D&D fans, let them know this new campaign is out there.

He even gave me credit in the video for helping with the World of Myrr Wordpress site and various other technical questions. :)

Monday, November 16, 2015

Aquatic Informatics Named to Deloitte Technology Fast 50

My employer, Aquatic Informatics, was named the 37th fastest growing technology company in Canada and only one of three cleantech leaders on Deloitte’s Technology Fast 50™ List. Very cool for the Vancouver software scene.

Tuesday, October 06, 2015

Copying Playlists from Rdio to Apple Music

Even before it was announced that Rdio was in financial trouble, and even before we learned that Pandora was buying parts of Rdio to shut it down, I wondered if I should move to a different music streaming service. I enjoy Rdio, but I couldn't shake the nagging feeling that I had backed the wrong horse and eventually my hard work on playlists would vanish. Because of this, I was reluctant to spend too much time creating and fine tuning my Rdio playlists.

Fortunately, there's a solution, I successfully used Move to Apple Music to copy my playlists into my free trial of Apple Music--I still haven't decided if Apple's service is the way to go. This playlist migration from Rdio to Apple Music cost me $4.99 and it was well worth it.

I used an old MacBook for the playlist migration and it took about 12-16 hours. Yes, that's right about 16 hours. During that time, I couldn't use the machine for much because it was tying up the internet connection. I had about 2800 songs because it counts Rdio's "My collection" as a playlist.

Move to Apple Music allowed me to export a copy of my playlists (in XML), then upload the playlists I want into Apple Music. It also provides a list of the songs it couldn't find on Apple Music. In my case it was over 200, but when I checked into it, it turned out that most of the songs are available on Apple Music, they're just listed differently (e.g., the same song on a different album version). Be aware, iTunes will actually refuse to sync a playlist if there's a song included that can't be found, so you'll have to check them once you have finished the import.

Note that I did try the free version of STAMP, which copies playlists from Rdio, Google Music, or Spotify to Apply Music or Spotify. I couldn't get STAMP to work and it required move work to run than Move to Apple Music. It also costs a few bucks more.

Update: After the import, I noticed a lot of the songs are not the album version. I haven't tested this theory, but I think I know why it's happening. When a live version, or a remix of a song is available, it's a common format to add "(live...)" or "(...remix)" to the song title. I believe the open bracket results in these versions appearing first in the Apple Music API search results. The upshot is you'll need to go and switch the versions if you're not a fan of live versions and remixes. 

Tuesday, September 08, 2015

Araxis File and Directory Comparison Utility

As part of my role, I'm writing, editing, and managing technical product documentation. One of the challenges in that area is incremental translation to multiple languages. Running a Microsoft Word compare between two versions of a document is easy, so any documentation written in Word can be sent to a translator and the compare will show what's changed—and therefore what needs to be translated since the previous translation. However, some of our documentation is written in XHTML so that it can be compiled to CHM and also provided as web-based help with our server product. How we cost-effectively manage an incremental translation update on these files has been an open question. I now have a reasonable solution.

Free tools such as KDiff will perform a differential analysis on files and even show exactly which lines have changed. However, KDiff doesn't provide any ability to separate the changed files from the ones that don't need to be translated again. Fortunately, there is another application called Araxis Merge (Windows and MacOS supported) that does allow for this ability. At the time of writing, it's $129 for one standard licence.

Like Kdiff, you can run a recursive directory diff to find all the changes in all the files. However, once you have done your folder comparison, you can also select all the altered files (using Select Rows > Selected Changed) and copy them to a new "specified folder." Using this method, I was able to grab all of the XHTML files that have changed since we last did a complete document translation.

Note that this method DOES NOT copy new files. You will need to go through manually and copy the files that have been added. These files appear green in the Araxis folder comparison view.

And if that's not enough, you can even generate an HTML report of all the file changes with links to reports on the changes in each file… so cool.

I did run into some hiccups in Araxis, For example, a bug that often causes the right-click context menu to not appear and consequently hang the application. However, I was able to work around it and get my work done.

Tuesday, August 04, 2015

DocBook 5 Install and Setup for Windows

It has been a long time since I used a system such as DocBook. When writing Pragmatic Augmented Reality I used the Pragmatic PML framework, which is essentially a simplified version of DocBook.

Currently, I only want to set up DocBook to publish HTML--I may be adding PDF later. Finding the installation and set up instructions for DocBook on Windows was not easy. I discovered a couple of old (and short) versions of this and that's where I started. 

Unlike most Windows applications, there is no installer for DocBook. Installing is the same as downloading the executables (and other files), copying them to the location you want and then adding that location to your Windows Path. The Windows DocBook setup essentially has three pieces: 
  1. DocBook DTD (optional)
  2. DocBook XSL stylesheets
  3. Tools: 
    • libxml2
    • libxsl 
    • iconv
    • zlib1
1. Unless you're using a tool that supports DocBook (such as XMetal), you'll need to use one or more of the DocBook schemas. To do this, download and unzip the DocBook XML 5.x.x DTD from: This is optional since the stylesheets reference web versions of the DTD by default and you can edit DocBook files in any XML/text editor--I prefer Sublime Text because it's cross-platform and awesome.

2. Download and unzip the DocBook XSL 1.x.x stylesheets from:

I copied the files into C:\docbook\xsl

3. Download the tools. You can get versions of all 4 from here:

A. XSLTPROC: Download xsltproc: libxslt-1.1.26.win32 from

Unzip the folders and copy the files to c:\windows or another location that's in your Windows path. I prefer to copy them somewhere else (e.g., C:\docbook and add that directory to the path variable):

B. LIBXML2: I downloaded libxml2-2.7.8.win32.

If you want the newer libxml2 kit to convert your XML to HTML, you can get it from:, or But I'd try to get the old one working first. I haven't bothered to upgrade.

C. ICONV: I download iconv-1.9.2.win32

D: ZLIB1: I downloaded zlib-1.2.5.

After grabbing the tools, make sure you set up your Windows Path Variable. To set up the path for my system, I appended this: C:\docbook\libxslt-1.0.9-bin\bin;C:\docbook\libxml2-2.7.8.win32\bin;C:\docbook\iconv-1.9.2.win32\bin;C:\docbook\zlib-1.2.5\bin;

Once you have the tools installed. Run this command: xsltproc -version

The results should look something like this:

stephen.cawood@OLIFANTS /s/GitHub
$ xsltproc -version
Using libxml 20708, libxslt 10126 and libexslt 815
xsltproc was compiled against libxml 20706, libxslt 10126 and libexslt 815
libxslt 10126 was compiled against libxml 20706
libexslt 815 was compiled against libxml 20706

You will need a sample file to test your setup. Start with something dead simple like this (testDocBook.xml):

And the successful output should like like the image below.

To add a CSS stylesheet to your HTML output, use a command similar to this:

stephen.cawood@OLIFANTS /c/docbook
$ xsltproc --output outputFile.html --stringparam html.stylesheet help.css /c/docbook/docbook-xsl
-1.79.0/html/docbook.xsl testDocBook.xml

Note: If you want to build PDFs, you'll need to download  and install FOP for Windows.