I've lately been working on writing an ereader in Qt, with Android in mind. I let the project sit for a few weeks because of a combination of personal life events and bugs in the (very much still in development) Android port of Qt, but a couple of days ago I achieved the milestone of being able to read a commercially-released book on my Galaxy Tab.
Why work on an ereader, one might well ask? Amazon already have one for Android, and it's quite nice. Well, one thing that always annoys me reading ebooks is the somewhat....variable quality of the spellchecking; it seems quite common for them to be poorly spelled compared to printed books. This is a hundredfold more true of public-domain books from places like Project Gutenberg that have been OCR-scanned and tossed up on the site. How nice it would be, then, to be able to edit the book in situ and correct such errors. I plan to allow this, not by physically editing the ebook file (I'm wary of the legal implications of modifying a copyrighted work) but by storing an overlay file which in essence says 'word 20 of paragraph 32 should be 'mistake' and not 'mistaek''. As a side benefit, I can also provide a filter ability which can automatically convert all spellings of a word from one form to another - 'any time you see 'color', substitute 'colour'' - it always feels a little weird to me, as a Briton resident in the US, to buy a book by a British author whose spelling has been Americanised. I'm not yet at the point of doing any of this; right now I'm happy simply to be able to read the text as written, but I figured I might as well document what I've done to get to that point.
The reader, like the Kindle, reads Mobipocket files. These were originally designed to be read on Palm OS devices, and are stored in Palm-style database file. These consist of some headers and a series of blocks of data, each of which can be individually compressed with a form of Lempel-Ziv encoding (similar to what gzip uses, for instance). Mobipocket books consist of header information including optional tags for things like ISBN numbers, followed by the blocks that comprise the actual book (compressed, each block limited to 4k compressed size), followed by any images, each in its own block (required to be GIF format). The book text itself is HTML 3.2 with some customisations for purposes such as referring to images. The first image in the book is by convention the book cover. Books bought from Amazon are encrypted; while that encryption has been cracked and I could probably add support for reading them, I haven't because I'm not sure of the legal implications, even though it would only be used for books I've legitimately purchased.
I've written a 'bookshelf' that looks in a standard directory (currently fixed by platform; ~/Documents on desktop Linux, /sdcard/kindle on Android), sniffs all files in it to see if they're ebooks it can read, and displays those books in a list. Choosing one fires up a Page widget which actually displays (currently) the img and p tags from the book.
Doing this is more involved than one might think. Firstly there's the challenge of turning those compressed buckets into an uncompressed text stream. I accomplish this by writing a custom QIODevice (the underlying abstraction for Qt's files, network sockets etc) which wraps the document text and uncompresses it on the fly. Since it's a QIODevice I can also easily write the uncompressed text to disc. The Mobipocket header includes information on the character format used in the book (generally either Latin-1 or UTF-8) so I can construct a QTextStream that will do the appropriate conversions.
The book being HTML 3.2, Qt's inbuilt XML parser naturally chokes on it (I really can't blame it), so I had to write my own simple HTML parser. I've written it in more of a SAX than a DOM style (i.e. it parses the HTML stream incrementally rather than all at once) because by the nature of the thing an ereader is only concerned with displaying a small part of the document at a time; parsing the whole book on opening it would both take time at startup and needlessly consume RAM.
The stream is then split into Elements, each representing a block to be displayed on the screen; currently image and paragraph elements, the latter consisting of string fragments, which are collections of words with the same attributes (bold, italic, etc). Each element can report a size to the page layout algorithm and render itself; paragraph elements are careful not to render text where it would be partially cut off by the bottom of the page. Currently, the page renders as many elements as it can before hitting the bottom of the page; hitting the right of the window to go to the next page moves them 'up' by the height of the page then resumes rendering beginning with the element that was at the end of the last page.
Unfortunately, since I don't want to keep all elements parsed forever, I don't really have a good way to go back at present; an ereader that only goes forwards is a bit limited! I'm trying to figure out both how to handle this and how best to store position within the book, bearing in mind these problems -
- The page can be resized both during reading and between runs of the application (including on Android; think of rotating the tablet, for instance). These will cause paragraphs to reflow, taking up more or less lines on the page.
- There are ways to put pagebreaks into the book, which I do not yet support but will need to to handle, for example, the end of chapters.
I think the way I'm going to approach it is by viewing the book as a very very tall virtual screen of fixed width; so elements will have a virtual y coordinate starting at 0 and going down all the way to the end of the book, divided into pages every height-of-window pixels. The page itself acts as a window onto this virtual screen; going backwards will involve putting the page back by its height, then reparsing the book from the beginning until reaching elements that are visible on the page. I'll have to evaluate this for speed; it may be worth cacheing the previous page or two's elements since generally readers don't go much further back than that.
As for resizing, as well as storing the virtual y coordinate of the page, I'll keep track of the topmost element on the page (and in the case of a paragraph the word within it). Resizing involves repaginating and parsing from the beginning until that element and word appear on a page, then displaying that page.
Source for the app is at https://github.com/jotheberlock/reader
Thursday, August 18, 2011
Saturday, April 16, 2011
Baby steps in electronics
My first self-designed circuit - though it's a bit of a stretch to call it that. I've got 12v coming into the near side of the breadboard from a NiMH battery (and I am a bad engineer for using the same colour wire for live and ground there, oops). That's being shunted through an adjustable switching regulator to provide 7.2v on the power rails on the far side.
The reason for this is I've gotten the superstructure off my new RC tank, removed the built-in RC controller and am getting close to testing it out with the motor controllers I used in the old tank. I need 12V because basically all the electronics I intend to put on the rover (Fit-PC2, Kinect, Arbotix and AX-12 servos) want that; however, unlike my old tank which ran on 9.6v and could tolerate 12, this one is based on 7.2v.
I could shunt 12v into the motor controllers (they'll take up to about that much) and rely on PWM to bring the effective voltage down to something the motor can handle without burning up - but I'm a programmer, and therefore I trust hardware more than my software, during development at least. This way there's no way I can mess up and explode my motors, and as a side effect I imagine the regulator will prevent too much EMI getting back and messing with the PC.
The reason for this is I've gotten the superstructure off my new RC tank, removed the built-in RC controller and am getting close to testing it out with the motor controllers I used in the old tank. I need 12V because basically all the electronics I intend to put on the rover (Fit-PC2, Kinect, Arbotix and AX-12 servos) want that; however, unlike my old tank which ran on 9.6v and could tolerate 12, this one is based on 7.2v.
I could shunt 12v into the motor controllers (they'll take up to about that much) and rely on PWM to bring the effective voltage down to something the motor can handle without burning up - but I'm a programmer, and therefore I trust hardware more than my software, during development at least. This way there's no way I can mess up and explode my motors, and as a side effect I imagine the regulator will prevent too much EMI getting back and messing with the PC.
Saturday, April 9, 2011
Fitting a touchscreen to the EeePC 701
I have an old netbook I acquired second-hand as a robot controller a couple of years ago - it's an EEE PC 701. I thought it might be a cool hack to fit a touchscreen to it, so I ordered a cheap resistive one from DealExtreme. A week and a half long boat trip from China later, it arrived last week.
Picture of what I got
It consists of a small USB hub with display controller (bottom of the picture) and what amounts to a glass plate with a ribbon connector.
Installing it turned out not to be all that much hassle - you have to remove six screws from the front of the LCD casing and pop the bezel off. This takes a little bit of effort with some sort of flat blunt tool, in my case a dinner knife -
Case reveals its secrets
then I slipped the touch panel in there and used sticky tape to secure it to the screen. It's hard to get good pictures, unfortunately, since I didn't take the bezel completely off.
Seated touchpanel
I unplugged the camera up at the top (it's horrible and I never use it), put that connector into the hub/controller's in port, put the ribbon connector into the display's connector, and that was it (let a veil be discreetly drawn over my first attempt, where I failed to distinguish 'USB IN' and 'USB 3' on the circuit board). The controller is tucked under the left speaker grille; it causes a slight bulge when the bezel is screwed back on, but I gather this is normal.
After some unsuccessful attempts with the Kubuntu 10.04 I had installed to get it to properly recognise the touchscreen, I decided to embrace the bleeding edge of technology and install Kubuntu 11.04 Beta - the world of touchscreen input advances swiftly in Linux these days. Unfortunately my display connector was mis-soldered (not an uncommon problem apparently) resulting in inversion of the X axis, but this proved easy to solve temporarily by messing around with the xinput command-line program. A bit more challenging was calibrating the thing - this is an eGalax USB-HID touchscreen, which the standard tslib/ts_calibrate stuff doesn't seem to want to talk to. I managed to hunt down this program, however, which did the trick nicely - it calibrated my screen, altered the running server's parameters on the fly, and gave me a configuration snippet to put in /etc/X11/xorg.conf.d which worked just fine. Why it's not in Kubuntu's apt repository yet I'm not sure, it's pretty useful.
I also gave android-x86 Gingerbread a go from a live USB stick, just to see what would happen, but it didn't recognise the touchscreen at all. That might be something I have a go at fixing down the line, but I figured I'd done enough for now.
Still to do with this netbook, at some point, is taking the bottom half apart so I can try reseating the display connector; my screen sometimes goes all white and flickery, especially if there's a lot of black onscreen, which googling seems to indicate is a hardware problem. Also still to do is figuring out if I want to make and attach some kind of stylus holder.
Picture of what I got
It consists of a small USB hub with display controller (bottom of the picture) and what amounts to a glass plate with a ribbon connector.
Installing it turned out not to be all that much hassle - you have to remove six screws from the front of the LCD casing and pop the bezel off. This takes a little bit of effort with some sort of flat blunt tool, in my case a dinner knife -
Case reveals its secrets
then I slipped the touch panel in there and used sticky tape to secure it to the screen. It's hard to get good pictures, unfortunately, since I didn't take the bezel completely off.
Seated touchpanel
I unplugged the camera up at the top (it's horrible and I never use it), put that connector into the hub/controller's in port, put the ribbon connector into the display's connector, and that was it (let a veil be discreetly drawn over my first attempt, where I failed to distinguish 'USB IN' and 'USB 3' on the circuit board). The controller is tucked under the left speaker grille; it causes a slight bulge when the bezel is screwed back on, but I gather this is normal.
After some unsuccessful attempts with the Kubuntu 10.04 I had installed to get it to properly recognise the touchscreen, I decided to embrace the bleeding edge of technology and install Kubuntu 11.04 Beta - the world of touchscreen input advances swiftly in Linux these days. Unfortunately my display connector was mis-soldered (not an uncommon problem apparently) resulting in inversion of the X axis, but this proved easy to solve temporarily by messing around with the xinput command-line program. A bit more challenging was calibrating the thing - this is an eGalax USB-HID touchscreen, which the standard tslib/ts_calibrate stuff doesn't seem to want to talk to. I managed to hunt down this program, however, which did the trick nicely - it calibrated my screen, altered the running server's parameters on the fly, and gave me a configuration snippet to put in /etc/X11/xorg.conf.d which worked just fine. Why it's not in Kubuntu's apt repository yet I'm not sure, it's pretty useful.
I also gave android-x86 Gingerbread a go from a live USB stick, just to see what would happen, but it didn't recognise the touchscreen at all. That might be something I have a go at fixing down the line, but I figured I'd done enough for now.
Still to do with this netbook, at some point, is taking the bottom half apart so I can try reseating the display connector; my screen sometimes goes all white and flickery, especially if there's a lot of black onscreen, which googling seems to indicate is a hardware problem. Also still to do is figuring out if I want to make and attach some kind of stylus holder.
Thursday, March 24, 2011
My mobile object system
This is what I've been tinkering with for the last couple of weeks. Basically, it's a distributed-object system with persistence based on top of Qt. It allows me to write objects that can be moved from computer to computer and automatically be persisted to disc (or wherever else) - so for example I can log into a site on my tablet in a web browser then move that browser to my desktop PC, keeping my login credentials and page position - something similar to what Engadget calls the continuous client.
Code tarball is here - I've been building it using android-qt's branch of qtcreator on Kubuntu 10.10. It's very definitely a prototype and does not (obviously) represent production-quality code.
Detailed description is in the tarball and also here.
Code tarball is here - I've been building it using android-qt's branch of qtcreator on Kubuntu 10.10. It's very definitely a prototype and does not (obviously) represent production-quality code.
Detailed description is in the tarball and also here.
Purpose of this blog
I'm a British software engineer living in Ann Arbor, Michigan. My day job involves embedded Linux and Qt in an automotive context, but I get up to a variety of hobby projects in my spare time, mostly related to Linux, robotics and Android. I decided having a dedicated blog to document such things without boring my non-technical friends to tears might be a good idea.
First up, I'll be posting shortly about my project of the last few weeks, a system for cross-platform mobile objects based on Qt.
First up, I'll be posting shortly about my project of the last few weeks, a system for cross-platform mobile objects based on Qt.
Subscribe to:
Posts (Atom)