Archive | Printer Module RSS for this section

DMP Photo Booth 1.0

Well, the day has come and gone. DMP Photo Booth’s final test on June 21st went off without issue, and DMP Photo Booth has left Beta and is now considered “production ready”. The initial 1.0 release can be found on GitHub.

The significance of June 21st is the very reason DMP Photo Booth was created; the 21st is the day of my wedding. My wife wanted a photo booth for the reception. We looked into renting a photo booth, but it turns out that they run around $1,000. I turned to open source. Some quick googling turned up some options, but they were all personal projects or out of date. Sure I could get somebody else’s project working, but what’s the fun in that? I decided that we didn’t need to rent one, or download one, I could build it!

In late 2013, I set to work in earnest. I had a couple of months of downtime in school, and since I’m not currently working it was the perfect time. I decided I had three main objectives for this project: get some arduino experience, get some GTK+ experience, and do this all as portably as possible. I had initially decided to mostly ignore GLib and focus on GTK, but slowly I grew to appreciate GLib for what it is: the standard library that C never had. First I used GModule to handle shared libraries in a portable manner. Next I decided to use GLib primitives to keep from having to deal with cross-platform type wonkiness. Next, having grown tired of dealing with return codes, I refactored the project to use GLib’s exception replacement: GError.

Lessons Learned

It’s not all roses and puppies though. There are certainly things I’d do differently. DMP Photo Booth is developed in an Object Oriented style, passing opaque structs with “method” functions that operate on them. Each component of the program are organized into their own source file with file scoped globals scattered throughout. Said globals are protected by mutexes to create a semblance of thread safety. That said, threading issues have been a major thorn in my side. Long story short: I regret this design choice. While I still feel that this is the correct way to structure C code, and that if globals are required, this is the correct way to handle them; I feel that I should have made more of an effort to limit side effects. Recently, I’ve spent some time doing functional programming, and if I could do it again I’d try to write in a more functional style. Fortunately for me, this is something that a little refactoring could help with.

Additionally, one thing I thought would be a major help is something that began to be a major thorn in my side: NetBeans. As the size of the project grew, NetBeans got slower and slower. It seemed that I spent more time fiddling with IDE settings than actually coding. Even worse is that the IDE-generated makefile is so convoluted that it’s extremely difficult to modify by hand in a satisfying way. I’ve always coded with and IDE so I wouldn’t have even considered not using one, but then I spent some time with Haskell. One of Haskell’s “problems” is that it doesn’t have good IDE support. It doesn’t seem like any IDE really handles it well, so most people use Emacs. Personally, I haven’t really warmed up to Emacs, but GEdit has syntax highlighting for Haskell and a built-in terminal for GHCI. GEdit also has syntax highlighting for C. Next time, I will seriously consider using a lighter-weight text editor for a C project. All this said, I think NetBeans for Java remains the way to go.

What’s Next

Like any program, version 1.0 is just one of many versions. There certainly remains a lot of work to do with DMP Photo Booth. Some major items you are likely to see whenever I get around to working on DMP Photo Booth some more:

Options Dialog

I think anybody who has seen it will agree: the options dialog in DMP Photo Booth is bad. It’s poorly organized, and kind of wonky. Personally, I modify settings using the .rc file, which is telling. This is certainly a high-priority improvement.

Functional Refactor

Like I said above, the code could use a pass to limit side effects. Funtions need to have their side effects limited, and globals need to be eliminated unless absolutely necessary. However, C is not a functional language. While one could argue that function pointers enable functional programming in C, this is a very pedantic argument. I won’t be going crazy with functional programming techniques. There will be no Monads, or for loops being turned into mappings of function pointers.

Optional Module API

An idea I’ve had on the back burner for a while is an optional module API. This would be used for very specific quality-of-life things. For instance, a module could provide a GTK widget to be shown in the options dialog. Any module that doesn’t want to implement any or all of the optional API can just ignore it. The module loading function will gracefully handle the dlsym failure, just treating it as it is: declining to implement the API. I have no plans to change the current existing API, so all you module developers can rest easy!

User Interface Module

It occurred to me that it might be good to have a UI module. This would provide the UI, and wouldn’t be tied to the trigger/printer/camera module start/stop system. This module would be loaded at startup and unloaded on shutdown. This would allow the Photo Booth to use different widget toolkits: QT, Curses, Cocoa, WinForms, or whatever else. Under this scheme, the current GTK+ interface would be abstracted into the reference UI Module.

DMP Photo Booth: Release Candidate 1

It’s been a long time coming, but the day is almost here: the day DMP Photo Booth is officially released.

Following last week’s successful stress test, I’ve been doing some last-minute touch-up work. I’ve been preparing my laptop for the big day, and finding and squashing some bugs.

DMP Photo Booth RC1 using my fancy new dark GTK theme

DMP Photo Booth RC1 using my fancy new dark GTK theme

Now things are coming along, and I feel the time has come to proceed to RC1. This means that barring any major new show stoppers, DMP Photo Booth will proceed to version 1.0 on June 22.

You can find the latest release here. On that page you’ll find the latest source for DMP Photo Booth and the reference modules, as well as a pre-compiled version for Debian/AMD64. Just extract the tarball into a folder and double-click the executable and you’re off! It comes pre-configured with sane defaults.

Moving forward, I plan to work on a “GTK Trigger Module”. This will just show a window with a button you can click to trigger the photo session. I understand that not everybody feels like constructing an Arduino thingamabober, and that this is surely the only thing preventing DMP Photo Booth from going viral on a global scale. Hopefully this is done by 1.0, but if not it will likely make it into a version 1.0.1, to be released shortly after 1.0.

DMP Photo Booth: To The Test

It’s been a long year leading up to this, but last week DMP Photo Booth saw its first time out in the wild.

Last weekend my fiancée had her bachelorette party. Since it was No Boys Allowed, I wouldn’t be able to babysit the Photo Booth. Luckily for me the event went off largely without issue.


I spent the week leading up to the event writing documentation. Channeling my past life as an IT professional, I wrote up an HTML page documenting the use of the Photo Booth and some common issues. This documentation will probably get uploaded to either this site or github soon. I need to strip out some stuff specifically relating to my computer first.

After that and a quick walkthrough the night before, it was go time. As the appointed hour arrived, I watched my phone for calls. The good news is that none came. The Photo Booth performed as advertised, with only a few minor difficulties caused by my computer. The only issue with the actual Photo Booth itself that was reported to me is that there is a slight delay between a picture being taken and the trigger counting down. An issue has been opened against this in Github.

The final stretch is here now. The wedding is on the 21st, and the Photo Booth must be complete by then. Honestly, if the day were tomorrow I’d be confident the Photo Booth would work. However, there is always room for polish. There remains bugs to be squashed, documentation to be finalized, and packaging to be done.

Yet Another Aeson Tutorial

Lately I’ve been trying to make sense of Aeson, a prominent JSON parsing library for Haskell. If you do some googling you’ll see two competing notions: 1) Aeson is a powerful library, but it’s documentation is terrible and 2) about 10,000 Aeson tutorials. Even the Aeson hackage page has a huge “How To Use This Library” section with several examples. These examples usually take the form of:

“Make a type for your JSON data!”

data Contrived = Contrived { field1 :: String , field2 :: String } deriving (Show)

“Write a FromJSON instance!”

instance FromJSON Contrived where parseJSON (Object v) = Contrived <$> v .: "field1" <*> v .: "field2"


I’ll spare you the long form, other people have done it already and I’m sure you’ve seen it already. What I quickly noticed was that this contrived example wasn’t quite cutting it. I’ve had some challenges that I’ve had to overcome. I’d like to share some of the wisdom I’ve accumulated on this subject.

Nested JSON

The first Problem I’ve run into is the nested JSON. Let’s take a look at an example:

{ "error": { "message": "Message describing the error", "type": "OAuthException", "code": 190 , "error_subcode": 460 } }

That is an example of an exception that can get returned by any Facebook Graph API call. You’ll notice that the exception data is actually contained in a nested JSON object. If passed to a parseJSON function, the only field retrievable by operator .: is “error”, which returns the JSON object. We could define two types and two instances for this like:

data EXTopLevel = EXTopLevel { getEx :: FBException } deriving (Show) data FBException = FBException { exMsg :: String , exType :: String , exCode :: Int , exSubCode :: Maybe Int } deriving (Show) instance FromJSON EXTopLevel where parseJSON (Object v) = EXTopLevel <$> v .: "error" instance FromJSON FBException where parseJSON (Object v) = FBException <$> v .: "message" <*> v .: "type" <*> v .: "code" <*> v .:? "error_subcode"

In this case, you could decode to a EXTopLevel, and call getEx to get the actual exception. However, it doesn’t take a doctor of computer science to see that this is silly. Nobody needs the top-level object, and this is a silly amount of boilerplate. The solution? We can use our friend the bind operator. Aeson Objects are instances of Monad, and it turns out that it’s bind function allows us to drill down into objects. We can re-implement that mess above simply as:

data FBException = FBException { exMsg :: String , exType :: String , exCode :: Int , exSubCode :: Maybe Int } deriving (Show) instance FromJSON FBException where parseJSON (Object v) = FBException <$> (e >>= (.: "message")) <*> (e >>= (.: "type")) <*> (e >>= (.: "code")) <*> (e >>= (.:? "error_subcode")) where e = (v .: "error")

Much better right? I thought so too.

Types With Multiple Constructors

The next problem I’d like to talk about is using types with multiple constructors. Let’s take a look at an example:

{ "value": "EVERYONE" }

When creating a new album of facebook, the API needs you to set a privacy on the album. This setting is set using this JSON object. This would seem to be a trivial case for Aeson:

data Privacy = Privacy { value :: String } deriving (Show) instance FromJSON Privacy where parseJSON (Object v) = Privacy <$> v .: "value" <*>

Unfortunately, the following is not a valid privacy setting:

"{ "value": "NOT_THE_NSA" }

However, our Privacy type would allow that. In reality, this should be an enumeration:

data Privacy = Everyone | AllFriends | FriendsOfFriends | Self

But how would you write a FromJSON instance for that? The method we’ve been using doesn’t work, and parseJSON takes and returns magical internal types that you can’t really do anything with. I was at a loss for a while, and even considered using the method I posted above. Finally, the answer hit me. Like many things in Haskell, the answer was stupidly simple; just define a function to create the privacy object, and use that in the parseJSON function instead of the type constructor! My solution looks like this:

instance FromJSON Privacy where parseJSON (Object v) = createPrivacy v .: "value" createPrivacy :: String -> Privacy createPrivacy "EVERYONE" = Everyone createPrivacy "ALL_FRIENDS" = AllFriends createPrivacy "FRIENDS_OF_FRIENDS" = FriendsOfFriends createPrivacy "SELF" = Self createPrivacy _ = error "Invalid privacy setting!"

If the parseJSON needs a singular function to create a type, and you have more than one type constructor for your type, you can wrap your type constructors in one function that picks and uses the correct one. Your type function doesn’t need to know about Aeson; Aeson magically turns it’s parser into whatever type your function calls for.

Doesn’t Play Nice With Others

For the last few weeks, I’ve been looking into making a Printer Module in Haskell. I must say, it’s been a pretty miserable experience. Not the Haskell part, that was ok. No, my issue is more basic. It seems that Haskell doesn’t like to share.

My plan was to build a module in Haskell to do the printer logic, then link that module as a library into C, which will be imported by the Core as normal. A preliminary look about the internet confirms that this is supported behavior. There are a few trivial examples peppered throughout the internet; so I set to work, confident that this was a solvable problem.

Giving Cabal A Shot

Cabal is Haskell’s package management program. In addition to this, it serves as Haskell’s answer to make. With a simple call to:

cabal init

You are presented with a series of questions about your package. After filling out the form (and selecting library), Cabal creates a Setup.hs file. Calling:

runhaskell Setup.hs configure runhaskell Setup.hs build

…produces a .a library for your package. Success, right? Unfortunately when you try to load that you get a linker error stating something to the effect of “can not be used when making a shared object; recompile with -fPIC“. After hours of research I have determined that this is because the Haskell’s libraries have not been compiled using the -fPIC, which prevents them from being used with a static library.

Trying GHC

The Glasgow Haskell Compiler can be used to compile libraries directly. Having given up on cabal, I decided to try to cut out the middle man and use GHC. After much tinkering, I came up with a Makefile that worked, which I will preserve here for posterity:

COMPILER=ghc HS_RTS=HSrts-ghc7.6.3 all: "${COMPILER}" --make -no-hs-main -dynamic \ -l${HS_RTS} -shared -fPIC dmp_printer_module.c \ DmpPrinterModule.hs -o ${OUTPUT}

This makefile compiles any required Haskell scripts, as well as a C “glue” source file that initializes and finalizes the Haskell environment. More on what goes in that file can be found in the GHC documentation.

Cool, good to go, right? Wrong.

Couldn’t Find That Dyn Library

So, I started adding things to the module to make sure it doesn’t break. After adding some dependencies, and trying to recomplie, I start seeing this error:

Could not find module `[SOME_MODULE]' Perhaps you haven't installed the "dyn" libraries for package `[SOME_PACKAGE]'?

After more hours of research, it turns out that for a module to be used in a shared library, it must be compiled as one. Seems logical, but that would imply that all module developers have to go through this nightmare. And the developers of any dependencies they use have to have done so. And so on…

Since even Prelude hadn’t done so, I set off to figure this out. After poking about, it turns out that Debian provides a package ghc-dynamic, which provides the dyn libraries from Base. I installed it, and things were checking out. However, the dependencies I was using still did not work.

After some more reasearch, I found a suggestion that I re-install all my Cabal packages using the --enable-shared flag, which would provide me with my dyn libraries. I gave it a shot, but since my dependencies’ dependencies hadn’t done so, I got the same errors.

Some more research suggested that I could delete the .ghc folder in my home folder, then re-install all Cabal packages. This would force them to rebuild. However, I encountered the same issues.

The Man In Black Fled Across The Desert…

I’m beginning to feel a bit like Roland, ceaselessly chasing after the Dark Tower. Every time I get there, my journey starts over again. I clear one roadblock, and there’s another there waiting for me.

It seems like there isn’t any real interest in calling Haskell from C, and I must say that I am extremely disappointed. Calling C from Haskell works great, but when asked to share its toys with C, Haskell takes its ball and goes home.

I’m sure it’s possible to do meaningful work in Haskell, and call that from C. However, the amount of work I would have to do to attain that goal is not something I’m willing to accept. For this reason I am shelving Haskell for the time being. Maybe I’ll pick it again for some other project, but it’s not a good fit for DMP Photo Booth.

DMP Photo Booth: Crunch Time

Over the last few months, I’ve become distracted. As anybody who’s ever committed to one project can probably tell you: it stops being exciting. What was once your pride and joy becomes the daily grind. Things get stale. As was the case with me, I suspect that this happens for most people when development of new features ends and the focus shifts to bug fixes.

I became distracted. My mind began to wander to the next thing, which in my case ended up being Haskell. I began learning Haskell, and it was definitely educational. I learned a lot with Haskell, and I plan to stick with it so that when I list it on my resume, I don’t get destroyed on a whiteboard test. Then came The Great Matrix Affair of 2014; I got overwhelmed at school. I spent so much time studying and doing homework that I couldn’t muster up the motivation to program. Things fell by the wayside, as you can see in my blog post output for February. Luckily for me, that is done, and I have the next two months free to program!

What Remains To Be Done?

It’s been a good 6 – 8 weeks since I’ve really focused on DMP Photo Booth, so the first order of business is the figure out what needs to get done. After doing some brainstorming I’ve settled on a list:

Finish The Trigger

I’ve mostly completed the trigger, but it doesn’t work. The button is soldered wrong, and while it was magically working for a while, it has since stopped. I need to fix the wiring issue, and then drill a hole in the box to put it through. After that and maybe a quick coat of paint it will be complete.

This particular task is due by Friday. I have a series of VIP demos coming up, the first of which is Saturday morning. A few of my cousins are coming in from out of town on Saturday to do wedding stuff, and I want to show it off then. While my cousin Allen is an engineer, and can appreciate a breadboard mockup of what will Totally Become A Real Thing, it certainly won’t be impressive. My cousin Laraine will likely be less amused, but I’m sure I’ll get a pat on the head for my “hard work”. Due to this, it’s important that the trigger be done before then.

Facebook Printer Module

The reference suite of modules was planned to be: a Trigger Module using my Arduino implementation, a Printer Module using CUPS, a Camera Module using LibGPhoto2, and a Lua module for each so that modules can be created using Lua. Of these, only the Lua Printer Module remains to be done. Since creating a Lua module is a trivial task (and not terribly important to be honest), this is a very low priority item.

However, the current Printer Module requires a physical printer. This might not always be ideal, since printers are big and heavy. What if you just want to bring a laptop and a camera and have a photo booth? My fiancée is having just this sort of situation; she wants to use the photo booth at her bachelorette party, but who wants to lug a huge printer to a hotel room? To solve this, I’ve promised her a Facebook Printer Module.

The idea is that when dmp_pm_print() is called, the photo strip will be uploaded to facebook. While I know this sort of thing can be done, I haven’t really researched it much. If it turns out that you have to pay facebook money to get this sort of access, I will find a hosting service that is free. Maybe Photobucket, or Dropbox, or whatever. The important thing is that the photo strips will end up in some central location on the internet so that anybody at the party can download the strip later. Ideally, this central location would be a facebook gallery so people can tag themselvs and be all Web 2.0.

My fiancée’s bachelorette party is in May, so this project isn’t a burning priority. However, this represents the most substantial addition of new functionality remaining to be done, so I plan to work it sooner rather than later. Code will be posted on my Github as it evolves, and like DMP Photo Booth will be available under the GPLv3.

Mac Support

To this point, all my development has been done in Linux. First using Ubuntu, and now using Debian. However, most people don’t use Linux. While Linux is the main target platform for DMP Photo Booth, I have been coding as portably as possible, so it should be little effort to port the application to Mac. Over the next few months, I’ll be making sure it compiles and runs correctly my old Macbook. My Macbook is vintage 2010, as such it is only running Snow Leopard. Therefore if anybody in the audience is a Mac user, and has a current version of OSX, feel free to give it a shot as well and let me know how it goes.

Ideally, my fiancée will bringing the Macbook to her bachelorette party and not my development laptop, therefore this project is due at the same time as the Facebook Printer Module, in May.


Finally, bugs. I need to identify them. I need to squash them. And I need unit tests.

After making a big show about being a good person and doing unit tests, I promptly lost the faith. Soon after implementing those first tests, I made a major change to how I handled errors in my code. Suddenly, all my tests were broken, and I was faced with the choice: rewrite them all, or delete them. After some thought I decided that my tests weren’t that great and that I’d probably change something again and break them all again. So I deleted them.

I’ve got to say, I miss those tests. There has been more than a few times where I’d refactored something and wasn’t sure if it still worked. Sure, it seems to work, but does it really? If I had unit tests in place, I could say with a greater degree of certainty that they do. Plus, “it sounds like a lot of work” is not a good reason not to do something, so it won’t be one for me.

On top of that, I’ll be identifying and squashing bugs the old fashioned way: by trying stuff. I’ve already found a few doozies, and I’m sure I’ll find more. As I find them I’m going to post them on the bug tracker for DMP Photo Booth on Github. I do this for a few reasons: 1) it will provide a public centralized repository of open issues so that I don’t lose or forget about them. 2) I would like others to post bugs here. If I post them here and show that I am fixing them, I feel it will establish confidence that it is looked at and acted upon.

This is due when DMP Photo Booth goes to version 1.0. That is planned to be on June 21, the day of my wedding. DMP Photo Booth will get a good solid night of work as the 80 or so people attending put it through its paces. Assuming all goes to plan with minimal issues, DMP Photo Booth will be declared to be out of Beta. That said, to be truly version 1.0, unit tests must be in place and “all bugs must be fixed”.


Currently, DMP Photo Booth is available in source form only. No end-user ever had to compile Microsoft Office; I don’t feel they should have to compile DMP Photo Booth.

To that end, binary distributions will be available for Linux and Mac when DMP Photo Booth goes to version 1.0.

Got My Work Cut Out For Me

It’s a long list to be sure, but I have 4 months to focus on it. However, I’ve decided that I should spend some time focusing on other things too, so that I don’t burn out. To that end, I plan to spend 1 day per week focusing on learning new technologies, and 1 day per week keeping my old skills sharp.

For new technologies, this means things like learning more Haskell, and other languages or frameworks. Whatever strikes my fancy. For old skills this means practicing with Lua and Java, and maybe even C++ if I’m feeling particularly masochistic that day. This will likely take the form of blog posts on topics relating to these, so stay tuned!

Last Train Out Of Cairo

After recovering from Christmas, and the terrible events of late 2013, it’s time to put my nose back to the grindstone with the printer module. My latest task: make the printer module not be terrible.

What’s so bad about the printer module, you ask? The short answer is all the things. All bad. Every single one of them. It’s slow. It doesn’t print right. It consumes way too much resources. It makes my cat sad.

The Brotherhood Of The Printer Developer

It all started when trying to figure out this whole printing thing. It turns out that printer development is one of those secret development clubs. There are no tutorials, the API documentation leaves something to be desired, and printing in Linux is bad in general. In short, it’d be easier to join the Illuminati than to infiltrate the Dark Cabal of Printer Developers.

Just by reading the CUPS API documentation, it’s not difficult to hash out a simple hello world type printer application. However, as anybody who has ever printed something knows, printing has lots of knobs to fiddle. However, the CUPS API does not seem to have functions corresponding to things like paper size and DPI.

During my research, I managed to turn up all of one StackOverflow post on the topic. The gist of it being “you set that up yourself using PostScript and send that to CUPS.” It also provides a sample implementation using Cairo.

Seems Reasonable

I decided to give it a shot. If nothing else, it would be a good introduction to the Cairo library for me. In my youth, I was fond of using Java’s Graphics2D library to make all sorts of fancy UI elements. In slightly oversimplified terms: Cairo is the GTK equivilent to Graphics2D. This isn’t entirely accurate: Cairo is a vector graphics library that GTK just happens to have adopted. Cairo is very usable outside the context of GTK; it can author a variety of file types including pdf and postscript.

I decided I’d use Cairo to author postscript within the printer module.

The Implementation

cairo_surface_t * base = cairo_ps_surface_create( "[temp_file].ps", [WIDTH], [HEIGHT]); cairo_surface_t * image = cairo_image_surface_create_from_png( "[photo_strip_filename]");

First, I create 2 cairo_surface_t pointers. A Cairo surface is sort of like a canvas that you paint on. For those of you familiar with Java’s Graphics2D, you can think of it like your Graphics2D instance. cairo_surface_t is the base class of all Cairo surfaces, there are a variety of surface types for things like PostScript, PDF, PNG, X Windows, or whatever else. The first surface is an empty PostScript surface that represents our finished product. The second surface is created using our .png formatted photo strips.

cairo_t * working = cairo_create(base);

If a cairo_surface_t is your canvas, then cairo_t is your brush. Think of it like a Java Stroke object. Right here, we are creating a new cairo_t from our base Cairo surface.

cairo_set_operator(working, CAIRO_OPERATOR_DEST_OVER); cairo_set_source_surface(working, image, 0, 0); cairo_paint(working);

The basic idea is that you apply operations to a cairo_t, then you apply your cairo_t to a cairo_surface_t. Here, we are compositing our PNG surface over our PostScript surface. First we set the operator of our cairo_t to composite over the top. Next, we set the cairo_t to have our image Surface as its source surface. Finally, we call cairo_paint which will apply our cairo_t to the base surface.


This call saves our PostScript file.

cairo_surface_destroy(base); cairo_surface_destroy(image); cairo_destroy(working);

No C function is complete without a bunch of cleanup at the end. Here we call cairo_surface_destroy to free our cairo_surface_t pointers and then we call cairo_destroy to free our cairo_t pointer.

PS: You’re Doing It Wrong

That all seemed pretty great right? I thought so too. Here’s the problem: go check out that PostScript file you just created. Notice how it is 200 MB? Yeah…

It turns out that enormous PostScript files is a common problem. While we could just make sure to delete this file when we’re done, we’re still creating this gigantic file, then shoving it down our printer’s throat. My printer is on WiFi, so it takes a good 2 minutes to print this file, and brings my computer to a crawl while it’s doing it. No user is going to want to wait 2 minutes for their photo strip to print.

The second problem is actually a “feature” of PostScript. PostScript is a document layout language, and due to this the printer will take your PostScript file’s word for what it wants done. This sounds nice, until you realize that Cairo isn’t actually a PostScript authoring library. Cairo’s ability to tune a PostScript file is pretty limited. Specifically, this is a problem for things like DPI. I’m trying to print high quality images at 600 DPI. However, Cairo can’t set this in the PostScript, so the printer ends up spitting out a massively blown up copy of the image. This will not do…

The Solution

So, I’m back to square one. PostScript is a dead-end, and CUPS won’t let me customize my job. What to do…

I thought back to my hello world printer application. I was able to print a random image that was blown up to the size of my paper. What if I print an image that’s exactly the correct size for my paper? I gave it a shot, and sure enough there was no scaling issues!

I can set my printer to 600 DPI, then print my (600 * 4) x (600 * 6) image and it just works, just like Some Guy promised it would. All is once again well in the world.

Plus, I got some Cairo experience under my belt. Look forward to fancy curved lines and gradients in future versions of DMP Photo Booth! (Joking, I promise)

%d bloggers like this: