Photographic Picture Frame in CSS

For my fun project -- homepage Gasthaus Schütz -- I used the photograph of a real picture frame which fits elastically to its contents. Then, I did it by hand, and it was hefty puzzling.

This week I toyed around and made a Tcl/Tk script that does both: slice the photography, create the CSS file, and make a test page (HTML) if not yet done.

Here an example for the CSS source:

.bilderrahmen {
  padding: 84px 99px 92px 101px;
  min-width: 400px;
  min-height: 246px;
  display: inline-block;
    url(topleft.png) 0px 0px no-repeat,
    url(top.png) 101px 0px no-repeat,
    url(topright.png) 100% 0px no-repeat,
    url(left.png) 0px 84px no-repeat,
    url(center.png) 101px 84px no-repeat,
    url(right.png) 100% 84px no-repeat,
    url(bottomleft.png) 0px 100% no-repeat,
    url(bottom.png) 101px 100% no-repeat,
    url(bottomright.png) 100% 100% no-repeat;
    101px 84px,
    calc(100% - 200px) 84px,
    99px 84px,
    101px calc(100% - 176px),
    calc(100% - 200px) calc(100% - 176px),
    99px calc(100% - 176px),
    101px 92px,
    calc(100% - 200px) 92px,
    99px 92px;

If you are comfortable with CSS, you can do some fine-tuning.

Preconditions: the ImageMagick utility convert must be available in search path.

KPV Just a note, you really don't need ImageMagick and convert--you can do it all natively. To handle non-png images, just package require Img; to crop images, just use the following script:

proc CropImage {img x y width height filename} {
    set iwidth [image width $img]
    set iheight [image height $img]
    set x1 [expr {min($x + $width, $iwidth)}]
    set y1 [expr {min($y + $height, $iheight)}]

    set crop [image create photo -width $width -height $height]
    $crop copy $img -from $x $y $x1 $y1
    $crop write $filename
    image delete $crop

Here the script:


# file: gen-css-frame.tcl

# usage: wish gen-css-frame.tcl (Your photograph)

package require Tk
bind [winfo class .] <Destroy> exit

lassign $argv picture

if {[string tolower [file extension $picture]] ne ".png"} then {
  exec convert $picture [file rootname $picture].png
  set picture [file rootname $picture].png

set htmlSource {
  <title>Testing Page</title>
  <link rel="stylesheet" href="rahmen.css" />  
  <p class="bilderrahmen">
  Non eram nescius, Brute, cum, quae summis ingeniis 
  exquisitaque doctrina philosophi Graeco sermone tractavissent, 
  ea Latinis litteris mandaremus, fore ut hic noster labor in 
  varias reprehensiones incurreret. nam quibusdam, et iis 
  quidem non admodum indoctis, totum hoc displicet philosophari. 
  quidam autem non tam id reprehendunt, si remissius agatur, 
  sed tantum studium tamque multam operam ponendam in eo non 
  arbitrantur. erunt etiam, et ii quidem eruditi Graecis litteris, 
  contemnentes Latinas, qui se dicant in Graecis legendis operam 
  malle consumere. postremo aliquos futuros suspicor, qui me ad 
  alias litteras vocent, genus hoc scribendi, etsi sit elegans, 
  personae tamen et dignitatis esse negent. 

namespace import tcl::mathop::* tcl::mathfunc::*
pack [canvas .c] -expand yes -fill both
place [button .c.b -text "Generate CSS" -command calcCSS]\
  -anchor s -relx 0.5 -rely 1

# image

image create photo screen -file $picture
.c create image 10 10 -image screen -anchor nw
set width [image width screen]
set height [image height screen]
wm geometry . [+ $width 20]x[+ $height 100]+0+0
wm title . [file tail $picture]

# labels

.c create text 10 [+ $height 20]\
  -anchor nw -text Geometry -tags text
bind .c <<Copy>> {
  clipboard clear
  clipboard append [%W itemcget text -text]

# crop lines

.c create line 10 0 10 [+ $height 20]\
  -fill red -tags "left crop"
.c create line [+ $width 10] 0 [+ $width 10] [+ $height 20]\
  -fill red -tags "right crop"
.c create line 0 10 [+ $width 20] 10\
  -fill red -tags "top crop"
.c create line 0 [+ $height 10] [+ $width 20] [+ $height 10]\
  -fill red -tags "bottom crop"

bind .c <Enter> {.c itemconfigure crop -width 5}
bind .c <Leave> {.c itemconfigure crop -width 1}

.c itemconfigure crop -width 5

lassign {0 0 0 0} pointerx pointery dx dy

proc basicBinding {{canvas .c}} {
  global width height
  bind $canvas <1> {
    focus %W
    set pointerx %x
    set pointery %y
  bind $canvas <B1-Motion> {
    set dx [- %x $pointerx]
    set dy [- %y $pointery]
    set pointerx %x
    set pointery %y
  $canvas itemconfigure crop -width 5
  set x0 [lindex [$canvas coords left] 0]
  if {$x0 < 10} then {
    $canvas move left [- 10 $x0] 0
  set y0 [lindex [$canvas coords top] 1]
  if {$y0 < 10} then {
    $canvas move top 0 [- 10 $y0]
  set x1 [lindex [$canvas coords right] 0]
  if {$x1 > $width + 10} then {
    $canvas move right [- $width -10 $x1] 0
  set y1 [lindex [$canvas coords bottom] 1]
  if {$y1 > $height + 10} then {
    $canvas move bottom 0 [- $height -10 $y1]
  set left [expr {int([lindex [$canvas coords left] 0] - 10)}]
  set right [expr {int([lindex [$canvas coords right] 0] - 10)}]
  set top [expr {int([lindex [$canvas coords top] 1] - 10)}]
  set bottom [expr {int([lindex [$canvas coords bottom] 1] - 10)}]
  lappend text [- $right $left] [- $bottom $top] $left $top
  $canvas itemconfigure text -text crop=[join $text :]

bind .c <ButtonRelease> basicBinding

.c bind left <1> {
  .c itemconfigure crop -width 1
  bind .c <B1-Motion> {+
    .c move left $dx 0
  bind .c <ButtonRelease> {
    .c bind left <B1-Motion> ""

.c bind right <1> {
  .c itemconfigure crop -width 1
  bind .c <B1-Motion> {+
    .c move right $dx 0
  bind .c <ButtonRelease> {
    .c bind right <B1-Motion> ""

.c bind top <1> {
  .c itemconfigure crop -width 1
  bind .c <B1-Motion> {+
    .c move top 0 $dy
  bind .c <ButtonRelease> {
    .c bind top <B1-Motion> ""

.c bind bottom <1> {
  .c itemconfigure crop -width 1
  bind .c <B1-Motion> {+
    .c move bottom 0 $dy
  bind .c <ButtonRelease> {
    .c bind bottom <B1-Motion> ""

proc calcCSS {} {
  global width height picture htmlSource
  set path [file dirname $picture]
  set y0 [- [int [lindex [.c coords top] 1]] 10]
  set x0 [- [int [lindex [.c coords left] 0]] 10]
  set x1 [- [int [lindex [.c coords right] 0]] 10]
  set y1 [- [int [lindex [.c coords bottom] 1]] 10]
  set left $x0
  set right [- $width $x1]
  set top $y0
  set bottom [- $height $y1]
  # top line
  exec convert $picture -crop\
    ${left}x${top}+0+0                      $path/topleft.png
  exec convert $picture -crop\
    [- $width $left $right]x$top+$left+0    $path/top.png
  exec convert $picture -crop\
    ${right}x$top+[- $width $right]+0       $path/topright.png
  # center line                             --------------------
  exec convert $picture -crop\
    ${left}x[- $height $top $bottom]+0+$top $path/left.png
  exec convert $picture -crop\
    [- $width $left $right]x[- $height $top $bottom]+$left+$top\
  exec convert $picture -crop\
    ${right}x[- $height $top $bottom]+[- $width $right]+$top\
  # bottom line                             --------------------
  exec convert $picture -crop\
    ${left}x$bottom+0+[- $height $bottom]   $path/bottomleft.png
  exec convert $picture -crop\
    [- $width $left $right]x$bottom+$left+[- $height $bottom]\
  exec convert $picture -crop\
    ${right}x$bottom+[- $width $right]+[- $height $bottom]\
  lappend css\
    ".bilderrahmen \{"\
    "padding: ${top}px ${right}px ${bottom}px ${left}px;"\
    "min-width: [- $width $left $right]px;"\
    "min-height: [- $height $top $bottom]px;"\
    "display: inline-block;"
  lappend background background:\
    "url(topleft.png) 0px 0px no-repeat,"\
    "url(top.png) ${left}px 0px no-repeat,"\
    "url(topright.png) 100% 0px no-repeat,"\
    "url(left.png) 0px ${top}px no-repeat,"\
    "url(center.png) ${left}px ${top}px no-repeat,"\
    "url(right.png) 100% ${top}px no-repeat,"\
    "url(bottomleft.png) 0px 100% no-repeat,"\
    "url(bottom.png) ${left}px 100% no-repeat,"\
    "url(bottomright.png) 100% 100% no-repeat;"
  lappend backgroundsize background-size:\
    "${left}px ${top}px,"\
    "calc(100% - [+ $left $right]px) ${top}px,"\
    "${right}px ${top}px,"\
    "${left}px calc(100% - [+ $top $bottom]px),"\
    "calc(100% - [+ $left $right]px) calc(100% - [+ $top $bottom]px),"\
    "${right}px calc(100% - [+ $top $bottom]px),"\
    "${left}px ${bottom}px,"\
    "calc(100% - [+ $left $right]px) ${bottom}px,"\
    "${right}px ${bottom}px;"
  lappend css\
    [join $background "\n    "]\
    [join $backgroundsize "\n    "]
  set css [join $css "\n  "]\n\}
  set ch0 [open $path/rahmen.css w]
  puts $ch0 $css
  close $ch0
  if {![file exists $path/testpage.htm]} then {
    set ch1 [open $path/testpage.htm w]
    puts $ch1 $htmlSource
    close $ch1

Have fun!