Hour registration using ImageMagick

By Ghost on Thursday 2 July 2009 20:21 - Comments (5)
Category: General tricks, Views: 16.024

For my work I have to provide weekly work slips stating the exact hours I have worked in order to get paid. For this, I am required to use an existing template in pdf format which I have to print, fill in, sign and mail, fax or e-mail back to HQ. At the office, my supervisor has to check and sign the document before forwarding it to the payroll company by fax. I can imagine that at the payroll company the form is yet again converted to dead tree format, before being copied into their own system by an accountant.

Apart from the unnecessary paper and ink that goes to waste every month, I found the whole process I had to go through every week a bit too time consuming. I have to retrieve and print the pdf form, find a pen to write with, fill in the form, sign it, scan it back in (and scale back the image so it won't be a whopping 3MB jpeg), and mail it to my supervisor. After three times going through this whole process, I had enough and wrote a Python script that would assemble a complete form for me using ImageMagick.

Creating the script is essentially straightforward: it just creates a massive parameter list for ImageMagicks 'convert' and executes it. The parameters tell the program to place different pieces of text and imagery on top of the already existing form, so a filled in form will be generated.

All of the data can be either added manually beforehand (my own address, my signature) or generated based on a single date and the actual hours I have worked (form number, current week, date of the days, total hours worked). I only need to enter the total hours for every day of the week I have worked and the script uses the current date for the number of the week. If I need to generate the form for another week, I can supply my own date.

Now, generating the form is a snap. No hassle with printing, filling in and scanning, I just have to enter the hours and run the script. The following image is the result (I have altered the template a bit to remove the logo and information about the payroll company).

http://tweakers.net/ext/f/5Ob0pZ7UTjKdtBnc18DJmPOL/thumb.jpg

I have included the script below, so feel free to use it as a basis for your own version. It expects a template called 'werkbriefje.pdf' and an image called 'paraaf.png' to use as a signature.


Python:
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/python
import os
from datetime import date
from datetime import timedelta

######################################################
# User supplied data goes here
######################################################

dateinweek = date.today()
# Uncomment for a specialised date
#dateinweek = date(2009, 5, 27)
hoursmade = (   0,      # Monday
                0,      # Tuesday
                5,      # Wednesday
                0,      # Thursday
                2,      # Friday
                8,      # Saturday
                0)      # Sunday
travelcosts = 0

######################################################

def firstDayOfWeek(dateinweek):
    count = dateinweek.weekday()
    delta = timedelta(count)
    return dateinweek - delta

def realWeekandYear(date):
    week = int(date.strftime("%W")) + 1
    year = int(date.strftime("%Y"))
    if week is 53:
        week -= 52
        year += 1
    return (week, year)

def getTableCoordinates(row):
    return (270, 654 + 43 * row)

def printString(x, y, text):
    return ' -draw "text %d,%d \'%s\'"' % (x, y, text)

def printDay(factdate, dayofweek, hournum):
    hours = hournum
    if hours is 0:
        return ''
    delta = timedelta(dayofweek)
    day = (factdate + delta).strftime("%d-%m")
    loc = getTableCoordinates(dayofweek)
    return printString(loc[0], loc[1], "%s     %s" % (day, hours))

def printTotal(hours):
    if hours is 0:
        return ''
    loc = getTableCoordinates(7)
    return printString(loc[0] + 99,loc[1], "%s" % hours)

def printHoursMade(factdate, hourlist):
    text = ""
    count = 0
    total = 0
    for hour in hourlist:
        text += printDay(factdate, count, hour)
        total += hour
        count += 1
    text += printTotal(total)
    return text

def printAddress(address):
    addressStart = (180, 380)
    addressIncr = (0, 43)

    text = ""
    printLoc = addressStart
    for line in address:
        text += printString(printLoc[0], printLoc[1], line)
        printLoc = (printLoc[0] + addressIncr[0], printLoc[1] + addressIncr[1])

    return text

def printFactcode(factdate):
    factcode = (278, 274)
    text = "%d / %d" % realWeekandYear(factdate)
    return printString(factcode[0], factcode[1], text)

def printTravelcosts(costs):
    if costs is 0:
        return ''
    travelloc = (220, 1065)
    return printString(travelloc[0], travelloc[1], costs)

def printAutograph():
    image = "paraaf.png"
    size = (300, 150)
    loc = (450, 1125)
    return "-draw 'image src-over %d,%d %d,%d %s'" % (loc[0], loc[1], size[0], size[1], image)


factdate = firstDayOfWeek(dateinweek)

address_me = ("My Name", "My Address", "ZIP City")
file = "werkbriefje-%d-%d.jpg" % (realWeekandYear(factdate)[1],realWeekandYear(factdate)[0])

drawcmds = ""
drawcmds += printAutograph()
drawcmds += printFactcode(factdate)
drawcmds += printAddress(address_me)
drawcmds += printHoursMade(factdate, hoursmade)
drawcmds += printTravelcosts(travelcosts)

command = "convert -units PixelsPerInch -density 150x150 werkbriefje.pdf %s %s" % (drawcmds, file)
os.system(command)

Volgende: Why people are allowed to stick with Windows 07-'09 Why people are allowed to stick with Windows
Volgende: Driven by unit testing 04-'09 Driven by unit testing

Comments


By Tweakers user Alex), Thursday 2 July 2009 21:15

Funny that you did it this way... but is it also accepted by your employer? I know some of 'm aren't very keen on doing things not exactly according to the plan...

By Tweakers user TeeDee, Thursday 2 July 2009 21:46

What's wrong with downloading the pdf template, create some PDF formfields*, fill it in (via script or manually) and then mail it. Saves a lot of cpu cycles and hassle.

Code could be (pseudo):

code:
1
2
3
4
5
foreach(PdfFormField field in Werkbriefje.pdf)
{
    field.value = "yourValue";
}
SendMail(WerkBriefje.pdf);



* the creation of the fields is done via Adobe Acrobat Professional or any other PDF authoring software.

[Comment edited on Thursday 2 July 2009 21:46]


By Tweakers user Alex), Thursday 2 July 2009 22:07

What I meant... some people want it handwritten, and not typed in. I can't understand why but that's the way it is...

By Tweakers user Ghost, Thursday 2 July 2009 23:32

@Alex:
So far no one has complained about this, although I do admit I had expected some resistance when I first handed in my generated form :P. I think that there is no problem since at least two pair of eyes besides my own are checking the document, so mistakes will not go unnoticed.

@TeeDee:
You are probably correct that editing the form with a PDF editor once and then code an additional script to fill in the fields might be more cpu friendly, although that would cost me more time than I thought was necessary ;).

The difference is that I do not have Adobe Acrobat Professional and do not know about any Linux equivalent. Also, I do not know how to create a script that uses these form fields, apart from just ripping the pdf apart with regular expressions. I figured I could write the script quicker using ImageMagick, than first having to look for the required tools and knowledge.

[Comment edited on Thursday 2 July 2009 23:52]


By Gilles, Friday 3 July 2009 07:19

Nice script, ugly worksheet ;)

Comments are closed