Make BASE64 data from Tk 'photo' image files (GIF,PGM,PPM,PNG)

uniquename - 2013aug21

I have seen postings on this site where someone is asking how to make 'base64' data --- to use in a Tk GUI script. The usual reply is to use a 'base64' utility from 'tcllib'. But I am running on Linux. I already have a 'base64' command at my disposal --- and probably anyone running a Linux/Unix/BSD system has the 'base64' command (or a suitable substitute) available to them.

(Since the Apple Mac OS is based on a BSD OS, Mac users probably also have such a command available to them.)

The Tk 'image create photo -data' command will accept image data of several types of 'raster image' files --- GIF, PGM (Portable Gray Map), PPM (Portable Pixel Map), and, starting with Tk 8.6, PNG.

(I guess I am making an assumption here. I have not tried PNG files, since I do not have Tcl-Tk 8.6 installed. DKF could probably verify whether the 'image create photo -data' command will accept base64 PNG data.)

Although I could go through a sequence of 'manual' manipulations in a command terminal window to capture 'base64' data from these types of image files, I decided I would much rather make a Tk 'wrapper' for the 'base64' command --- to make the process 'a walk in the park'.

For the GUI, I needed (or wanted) several features:

** an entry field for filename, along with a 'Browse...' button --- to use to fetch a filename via the 'tk_getOpenFile' utility.

** a canvas on which to display the image --- as a 'preview'.

** a button to use to generate the 'base64' data from the image file.

** a label on the GUI in which to display the dimensions of the image file.

** a 'Help' button on the GUI, by which to provide information on the extent of the capabilities of this utility

I set about making such a GUI, and after several iterations, I ended up with the GUI seen in the following image.

genBASE64dataFromPhotoImg_teapotPPM_screenshot_504x398.jpg

This teapot image is from one of the PPM files in a Tk demo directory ---

   /usr/share/doc/tk8.5/examples/images/

on my Linux installation.

Here is an example with a PGM file from another demo directory:

genBASE64dataFromPhotoImg_sphericalGradientPGM_screenshot_503x398.jpg

But PPM and PGM files are hard to find these days. My original intention for this utility was to make base64 data from GIF files. (And, if I ever install Tcl-Tk 8.6, from PNG files.)

Here is an example with a GIF file that I made from an image from Vetter's page Rotated Text Font.

genBASE64dataFromPhotoImg_rotated-textGIF_screenshot_503x217.jpg

I chose this image because it suggests that one could make base64 data from GIF files of rotated alphanumeric characters.

When the GUI first pops up, the canvas area is empty. After you choose a file (such as the gimp-ball GIF file seen in the following image), the image is displayed on the canvas.

genBASE64dataFromPhotoImg_gimp-ballANDscite-editor_screenshot_656x436.jpg

Clicking on the 'GenBASE64data' button results in a popup of the base64 data in a text-file editor (or text-file browser, like 'xpg' [L1 ]) --- depending on what the user chooses to use in the 'wrapper' script for the 'base64' command. The code for that wrapper shell script is provided below. You can choose from several commented examples there --- or provide your own favorite text-file browser/editor. In this image, the SciTE text editor is being used.


The 'Help' button on the GUI provides pretty complete and detailed help for using the GUI --- including some of the information in the paragraphs above.


The code

Below, I provide the Tk script code for this 'BASE64 data generator' utility.

I follow my usual 'canonical' structure for Tk code for this Tk script:

  0) Set general window & widget parms (win-name, win-position,
     win-color-scheme, fonts, widget-geometry-parms, win-size-control,
     text-array-for-labels-etc).

  1a) Define ALL frames (and sub-frames, if any).
  1b) Pack   ALL frames and sub-frames.

  2) Define & pack all widgets in the frames, frame by frame.
              Within each frame, define ALL the widgets.
              Then pack the widgets.

  3) Define keyboard and mouse/touchpad/touch-sensitive-screen action
     BINDINGS, if needed.

  4) Define PROCS, if needed.

  5) Additional GUI initialization (typically with one or more of
     the procs), if needed.

This Tk coding structure is discussed in more detail on the page A Canonical Structure for Tk Code --- and variations.

This structure makes it easy for me to find code sections --- while generating and testing a Tk script, and when looking for code snippets to include in other scripts (code re-use).

I call your attention to step-zero. One new thing that I have started doing recently is using a text-array for text in labels, buttons, and other widgets in the GUI. This can make it easier for people to internationalize my scripts. I will be using a text-array like this in most of my scripts in the future.


Experimenting with the GUI

As in all my scripts that use the 'pack' geometry manager (which is all of my 100-plus scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', '-expand' --- on all of the 'pack' commands for the frames and widgets.

That helps me when I am initially testing the behavior of a GUI (the various widgets within it) as I resize the main window.

I think that I have used a nice choice of the 'pack' parameters. The label and button widgets stay fixed in size and relative-location as the window is re-sized --- the filename entry field x-expands whenever the window is x-expanded --- while the canvas area expands/contracts whenever the window is re-sized.

You can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various frames and widgets --- to get the widget behavior that you want.

___

Additional experimentation: You might want to change the fonts used for the various GUI widgets. For example, you could change '-weight' from 'bold' to 'normal' --- or '-slant' from 'roman' to 'italic'. Or change font families.

In fact, you may NEED to change the font families, because the families I used may not be available on your computer --- and the default font that the 'wish' interpreter chooses may not be very pleasing.

I use variables to set geometry parameters of widgets --- parameters such as border-widths and padding. And I have included the '-relief' parameter on the definitions of frames and widgets. Feel free to experiment with those 'appearance' parameters as well.


Some features in the code

That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing.

You can look at the top of the PROCS section of the code to see a list of the procs used in this script, along with brief descriptions of how they are called and what they do.

The procs are

   'get_img_filename' - called by the 'Browse...' button,
                        to get the filename of an image (GIF,PNG,...) file
                        and 
                        then place the image on the canvas.

   'put_img_on_canvas' - called by proc 'get_img_filename' (and perhaps
                         by some bindings), to place the image on the
                         canvas, for the current filename

  'gen_base64_data'   - called by the 'GenBASE64data' button,
                        to run the 'base64' command on the file and
                        show the text output in a popup GUI (a
                        text-file editor or browser).

  'popup_msgVarWithScroll' - called by the 'Help' button,
                             to show text in variable $HELPtext.

It is my hope that the copious comments in the code will help Tcl-Tk coding 'newbies' get started in making GUI's like this.

Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch videos of people sailing into the sides of cliffs, in trucks and on motorcycles and bicycles.


 Code for Tk script 'make_BASE64data_fromTkPhotoIMAGEfile.tk' :
#!/usr/bin/wish -f
##+###########################################################################
##
## SCRIPT: make_BASE64data_fromTkPhotoIMAGEfile.tk
##
## PURPOSE: This script is designed to select an image file supported by
##          the Tk create 'photo' function (such as a GIF,PGM,PPM, or PNG file)
##          and invoke the 'base64' command (available on Linux/Unix/BSD)
##          to create base64 data --- which is shown in a popup window,
##          such as a text-editor window or the window of a text file
##          browser such as 'xpg'.
##
##          For 8.5.x (or some earlier) versions of the 'wish' interpreter,
##          the image file selected may be a GIF file. The image file selected
##          may be a PNG file, when using 8.6.x versions of the 'wish'
##          interpreter.
##
## THE GUI:
##          This script provides a Tk GUI with the following widgets.
##
##         0) There are some BUTTONS such as 'Exit', 'Help' and 'GenBASE64data'.
##
##         1) There is a FILENAME-ENTRY FIELD and 'Browse ...' BUTTON with
##            which to get an image file, from which to generate the
##            'base64' data.
##
##         2) There is a (scrollable) CANVAS widget on which the image
##            may be shown (previewed).
##
##+#######################################################################
## 'CANONICAL' STRUCTURE OF THIS CODE:
##
##  0) Set general window and widget parms (win-name, win-position,
##     win-color-scheme, fonts, win-min-size, text-array-for-labels-etc).
##  1a) Define ALL frames (and sub-frames, if any).
##  1b) Pack   all the frames.
##  2) Define & pack all widgets in the frames, frame by frame.
##
##  3) Define BINDINGS for key and mouse/touchpad/touch-screen 'events',
##     if needed.
##  4) Define PROCS, if needed.
##  5) Additional GUI initialization (typically with one or more of
##     the procs), if needed.
##
##+#################################
## Some detail of the code structure of this particular script:
##
##  1a) Define ALL frames:
## 
##      Top-level :
##       'fRbuttons'  - to contain 'Exit' and 'Help' buttons.
##       'fRfile'     - to contain a triplet: label-entry-button widgets
##       'fRcanvas'   - to contain a canvas widget, with scrollbars
##
##      Sub-frames: none
##
##  1b) Pack ALL frames.
##
##  2) Define & pack all widgets in the frames -- basically going through
##     frames & their interiors in left-to-right and/or top-to-bottom order.
##
##  3) Define bindings:
##         - Button1-release  on the filename entry field
##         - Return key press on the filename entry widget
##
##  4) Define procs:
##        - a proc to get the image filename
##        - a proc to call the 'base64' command and put the output in
##                             a text-editor or text-browser window
##        - a proc to present the help text
##
##  5) Additional GUI initialization: none needed
##
##+#######################################################################
## DEVELOPED WITH: Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october, 'Karmic Koala')
##
##   $ wish
##   % puts "$tcl_version $tk_version"
##
## showed
##     8.5 8.5
## but this script should work in most previous 8.x versions, and probably
## even in some 7.x versions (if font handling is made 'old-style').
##+#######################################################################
## MAINTENANCE HISTORY:
## Started by: Blaise Montandon 2013aug14 Started development, on Ubuntu 9.10,
##                                        based on a couple of my other
##                                        scripts that
##                                        a) get an image file and put its
##                                           image on a scrollable canvas
##                                        b) 'exec' a command and put its
##                                           text output in a text editor.
## Changed by: Blaise Montandon 2013aug21 Added the proc to show $HELPtext. 
##                                        Added 'labelIMGSIZE' label.
##+########################################################################

##+#######################################################################
## Set WINDOW TITLE and POSITION.
##+#######################################################################

wm title    . "Generate BASE64 data from a Tk 'photo' image file (GIF,PNG,...)"
wm iconname . "BASE64data"

wm geometry . +15+30


##+######################################################
## Set the COLOR SCHEME for the window and its widgets ---
## such as entry field background color.
##+######################################################

tk_setPalette "#e0e0e0"

set entryBKGD "#ffffff"
# set listboxBKGD "#ffffff"


##+########################################################
## Set (temp) FONT NAMES.
##
## We use a VARIABLE-WIDTH font for text on LABEL and
## BUTTON widgets.
##
## We use a FIXED-WIDTH font for the text in ENTRY, LISTBOX,
## and TEXT (or MESSAGE) widgets.
##+########################################################

font create fontTEMP_varwidth \
   -family {comic sans ms} \
   -size -14 \
   -weight bold \
   -slant roman

font create fontTEMP_SMALL_varwidth \
   -family {comic sans ms} \
   -size -12 \
   -weight bold \
   -slant roman

## Some other possible (similar) variable width fonts:
##  Arial
##  Bitstream Vera Sans
##  DejaVu Sans
##  Droid Sans
##  FreeSans
##  Liberation Sans
##  Nimbus Sans L
##  Trebuchet MS
##  Verdana


font create fontTEMP_fixedwidth  \
   -family {liberation mono} \
   -size -14 \
   -weight bold \
   -slant roman

font create fontTEMP_SMALL_fixedwidth  \
   -family {liberation mono} \
   -size -12 \
   -weight bold \
   -slant roman

## Some other possible fixed width fonts (esp. on Linux):
##  Andale Mono
##  Bitstream Vera Sans Mono
##  Courier 10 Pitch
##  DejaVu Sans Mono
##  Droid Sans Mono
##  FreeMono
##  Nimbus Mono L
##  TlwgMono


##+###########################################################
## Set variables for GEOMETRIC attributes of various widgets.
## (e.g. width and height of canvas, and padding for Buttons)
##+###########################################################

set initCanWidthPx  300
set initCanHeightPx 300

set minCanWidthPx  24
set minCanHeightPx 24

# set BDwidthPx_canvas 2
  set BDwidthPx_canvas 0


## BUTTON widget geom settings:

set PADXpx_button 0
set PADYpx_button 0
set BDwidthPx_button 2


## LABEL widget geom settings:

set PADXpx_label 0
set PADYpx_label 0
set BDwidthPx_label 2


## ENTRY widget geom settings:

set BDwidthPx_entry 2
set initEntryWidthChars 20


## LISTBOX geom settings:

# set BDwidthPx_listbox 2
# set initListboxWidthChars 30
# set initListboxHeightChars 8


##+######################################################
## Set a MIN-SIZE of the window (roughly).
##
## We set approx MIN WIDTH of the window based on a width
## allowance for the label-entry-button widgets that
## prompt for the image file.
##
## We set the approx MIN HEIGHT of the window based on
## allowance for
##     1 char   high for the 'fRbuttons' frame
##     1 char   high for the 'fRfile'    frame
##    24 pixels high for the 'fRcanvas' frame.
##+######################################################

set minWinWidthPx [font measure fontTEMP_fixedwidth \
   "Img filename (GIF,PNG,...):  a-filename-goes-here  Browse..."]

## Add some pixels to account for right-left-size of window-manager
## decoration (about 8 pixels) --- and add some pixels for 
## frame/widget borders (about 3 widgets x 4 pixels/widget):

set minWinWidthPx [expr {20 + $minWinWidthPx}]

## Get approx min-HEIGHT for the window allowing
##     1 char   high for the 'fRbuttons' frame
##     1 char   high for the 'fRfile'    frame
##    24 pixels high for the 'fRcanvas' frame.
##
## Add about 20 pixels for top-bottom window decoration --
## and about 3 frames x 4 pixels/frame for frame/widget borders.

set charHeightPx [font metrics fontTEMP_fixedwidth -linespace]

set minWinHeightPx [expr {( 2 * $charHeightPx ) + 24 + 32 }]

## FOR TESTING:
#   puts "minWinWidthPx = $minWinWidthPx"
#   puts "minWinHeightPx = $minWinHeightPx"

wm minsize . $minWinWidthPx $minWinHeightPx

## We allow the window to be resizable and we pack the canvas with
## '-fill both' so that the canvas can be enlarged by enlarging the
## window.

## If you want to make the window un-resizable, 
## you can use the following statement.
#  wm resizable . 0 0


##+####################################################################
## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI.
##     NOTE: This can aid INTERNATIONALIZATION. This array can
##           be set according to a nation/region parameter.
##+####################################################################

## if { "$VARlocale" == "en"}

set aRtext(buttonEXIT)    "Exit"
set aRtext(buttonHELP)    "Help"
set aRtext(buttonBASE64)  "GenBASE64data"

set aRtext(labelFILE)     "Img filename (GIF,PNG,...) :"
set aRtext(buttonBROWSE)  "Browse..."

## END OF  if { "$VARlocale" == "en"}


##+################################################################
## DEFINE *ALL* THE FRAMES:
##
##   Top-level : '.fRbuttons'  '.fRfile'  'fRcanvas'
##
##   Sub-frames: none
##+################################################################

## FOR TESTING: (of resizing of frames during window resizing):
# set feRELIEF_frame raised
# set feBDwidth_frame 2

set RELIEF_frame flat
set BDwidth_frame 0

frame .fRbuttons   -relief $RELIEF_frame  -bd $BDwidth_frame
frame .fRfile      -relief $RELIEF_frame  -bd $BDwidth_frame
frame .fRcanvas    -relief raised         -bd 2


##+##############################
## PACK ALL the FRAMES. 
##+##############################

pack .fRbuttons \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRfile \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRcanvas \
   -side top \
   -anchor nw \
   -fill both \
   -expand 1


##+#################################
## In FRAME '.fRbuttons' -
## DEFINE-and-PACK BUTTON widgets
## --- 'Exit','Help'.
##+#################################

button .fRbuttons.buttEXIT \
   -text "$aRtext(buttonEXIT)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {exit}

button .fRbuttons.buttHELP \
   -text "$aRtext(buttonHELP)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {popup_msgVarWithScroll .topHelp $HELPtext}

button .fRbuttons.buttBASE64 \
   -text "$aRtext(buttonBASE64)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {gen_base64_data}

## The text for this label will be provided
## by the 'put_img_on_canvas' proc.

label .fRbuttons.labelIMGSIZE \
   -text "" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief  flat \
   -bd 0

## Pack the widgets of frame 'fRbuttons'.

pack .fRbuttons.buttEXIT \
     .fRbuttons.buttHELP \
     .fRbuttons.buttBASE64 \
     .fRbuttons.labelIMGSIZE \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+###############################
## In FRAME '.fRfile' -
## DEFINE-and-PACK 3 widgets -
## LABEL, ENTRY, BUTTON:
##+###############################

label .fRfile.labelFILE \
   -text "$aRtext(labelFILE)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief flat \
   -bd 0

set ENTRYfilename ""

entry .fRfile.entFILENAME \
   -textvariable ENTRYfilename \
   -bg $entryBKGD \
   -font fontTEMP_fixedwidth \
   -width $initEntryWidthChars \
   -relief sunken \
   -bd $BDwidthPx_entry

button .fRfile.buttBROWSE \
   -text "Browse ..." \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {get_img_filename}

## Pack the widgets in frame 'fRfile'.

pack .fRfile.labelFILE \
   -side left \
   -anchor w \
   -fill none \
   -expand 0

pack .fRfile.entFILENAME \
   -side left \
   -anchor w \
   -fill x \
   -expand 1

pack .fRfile.buttBROWSE \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+###############################
## In FRAME '.fRcanvas' -
## DEFINE-and-PACK a CANVAS widget,
## with SCROLLBARS.
##+###############################
## We set highlightthickness & borderwidth of the canvas to
## zero, as suggested on page 558, Chapter 37, 'The Canvas
## Widget', in the 4th edition of the book 'Practical
## Programming in Tcl and Tk'.
##+######################################################

canvas .fRcanvas.can \
   -width $initCanWidthPx \
   -height $initCanHeightPx \
   -relief flat \
   -highlightthickness 0 \
   -borderwidth 0 \
   -yscrollcommand ".fRcanvas.scrbary set" \
   -xscrollcommand ".fRcanvas.scrbarx set"

scrollbar .fRcanvas.scrbary \
   -orient vertical -command ".fRcanvas.can yview"

scrollbar .fRcanvas.scrbarx \
   -orient horizontal -command ".fRcanvas.can xview"


## Pack the widgets in frame 'fRcanvas'.
## (Pack the scrollbars before the canvas so that
##  the canvas does not fill the available area first.)

pack .fRcanvas.scrbary \
   -side right \
   -anchor e \
   -fill y \
   -expand 0

pack .fRcanvas.scrbarx \
   -side bottom \
   -anchor sw \
   -fill x \
   -expand 0

pack .fRcanvas.can \
   -side top \
   -anchor nw \
   -fill both \
   -expand 1


##+##################################################
## END OF DEFINITION of the GUI widgets.
##+##################################################
## Start of BINDINGS, PROCS, Added-GUI-INIT sections.
##+##################################################

##+#######################################################################
##+#######################################################################
##  BINDINGS SECTION:
##
##   - For MB1-release in the image filename entry field,
##         use the ENTRYfilename to create a Tk 'photo' image structure
##         and put it on the canvas.
##
##   - For Enter-key-press when focus is in the image filename entry field,
##         use the ENTRYfilename to create a Tk 'photo' image structure
##         and put it on the canvas.            
##
## These 2 bindings are DEACTIVATED. We put the create-img-on-canvas
## function in the 'get_img_filename' proc, which is called by the
## 'Browse...' button.
##+#######################################################################


##COMMENTED for now:
if {0} {

bind .fRfile.entFILENAME <ButtonRelease-1>  {
   put_img_on_canvas
}

bind .fRfile.entFILENAME <Return>  {
   put_img_on_canvas
}

}
## END OF if {0} --- to DEACIVATE these bindings.


##+#####################################################################
##+#####################################################################
## DEFINE PROCS SECTION:
##
##   'get_img_filename' - called by the 'Browse...' button,
##                        to get the filename of an image (GIF,PNG,...) file
##                        and 
##                        then place the image on the canvas.
##
##   'put_img_on_canvas' - called by proc 'get_img_filename' (and perhaps
##                         by some bindings), to place the image on
##                         canvas, for the current filename
##
##  'gen_base64_data'   - called by the 'GenBASE64data' button,
##                        to run the 'base64' command on the file and
##                        show the text output in a popup GUI (a
##                        text-file editor or browser).
##
##  'popup_msgVarWithScroll' - called by the 'Help' button,
##                          to show text in var $HELPtext
##
##+#####################################################################


##+#####################################################################
## proc 'get_img_filename'
##
## PURPOSE: To get the name of an image file (GIF/PNG) and put the
##          filename into global var 'ENTRYfilename'. Then
##          create a Tk 'photo' structure from the image file,
##          and put the image on the canvas.
##
## CALLED BY: the '-command' option of the 'Browse ...' button.
##+#####################################################################

# set curDIR "$env(HOME)"
  set curDIR [pwd]

proc get_img_filename {} {

   global ENTRYfilename env curDIR img1 aRtext

   ## Load data from an OBJ file

   set fName [tk_getOpenFile -parent . -title "$aRtext(labelFILE)" \
      -initialdir "$curDIR" ]

   ## FOR TESTING:
   #   puts "fName : $fName"

   if {"$fName" == ""} {return}

   if {[file exists "$fName"]} {
      set ENTRYfilename "$fName"
      set curDIR [ get_chars_before_last / in "$ENTRYfilename" ]
      
      put_img_on_canvas
   }
   ## END OF if file-exists

}
## END OF proc 'get_img_filename'


##+#####################################################################
## proc 'put_img_on_canvas'
##
## PURPOSE: From the filename in var 'ENTRYfilename',
##          create a Tk 'photo' structure from the image file,
##          and put the image on the canvas.
##
## CALLED BY: the proc 'get_img_filename'
##+#####################################################################

proc put_img_on_canvas {} {

   global ENTRYfilename img1
   # global env curDIR

   # image create photo img1 -file "$ENTRYfilename"
   set img1 [image create photo -file "$ENTRYfilename"]

   ## FOR TESTING:
   #  puts "get_img_filename - img1: $img1"

   .fRcanvas.can delete all

   # .fRcanvas.can create image 0 0 -anchor nw -image img1
   .fRcanvas.can create image 0 0 -anchor nw -image $img1

   set imgWIDTHpx  [image width $img1]
   set imgHEIGHTpx [image height $img1]
   .fRbuttons.labelIMGSIZE configure \
      -text "ImgSize (pixels): ${imgWIDTHpx}x$imgHEIGHTpx"

   ## Make the canvas scrollbars usable for large images.

   .fRcanvas.can configure -scrollregion "0 0 $imgWIDTHpx $imgHEIGHTpx"

}
## END OF proc 'put_img_on_canvas'


######################################################################
## Proc 'get_chars_before_last' -
######################################################################
## INPUT:  A character and a string.
##         Note: The "in" parameter is there only for clarity.
##
## OUTPUT: Returns all of the characters in the string "strng" that
##         are BEFORE the last occurence of the characater "char".
##
## CALLED BY: proc 'get_img_filename'
##
#####################################################################

proc get_chars_before_last { char in strng } {

   set endIDX [ expr [string last $char $strng ] - 1 ]
   set output [ string range $strng 0 $endIDX ]

   ## FOR TESTING:
   # puts "From 'get_chars_before_last' proc:"
   # puts "STRING: $strng"
   # puts "CHAR: $char"
   # puts "RANGE up to LAST CHAR - start: 0   endIDX: $endIDX"

   return $output

}
## END OF 'get_chars_before_last' PROCEDURE


##+###################################################################
## Proc 'gen_base64_data'
##
## PURPOSE: Run a script that applies the 'base64' command to the
##          image file and shows the resulting text in a popup GUI
##          text-editor or text-browser window.
##
## CALLED BY: the '-command' option of the '...BASE64...' button.
##+###################################################################

proc gen_base64_data {} {

   global ENTRYfilename curDIR

   if {"$ENTRYfilename" == ""} {return}

   exec ./sho_base64_data_from_imgfile.sh $ENTRYfilename &

}
## END OF proc 'gen_base64_data'


##+########################################################################
## PROC 'popup_msgVarWithScroll'
##+########################################################################
## PURPOSE: Report help or error conditions to the user.
##
##       We do not use focus,grab,tkwait in this proc,
##       because we use it to show help when the GUI is idle,
##       and we may want the user to be able to keep the Help
##       window open while doing some other things with the GUI
##       such as putting a filename in the filename entry field
##       or clicking on a radiobutton.
##
##       For a similar proc with focus-grab-tkwait added,
##       see the proc 'popup_msgVarWithScroll_wait' in a
##       3DterrainGeneratorExaminer Tk script.
##
## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk',
##            4th edition, by Welch, Jones, Hobbs.
##
## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg)
##            and a variable holding text (many lines, if needed).
##
## CALLED BY: 'help' button
##+########################################################################
## To have more control over the formatting of the message (esp.
## words per line), we use this 'toplevel-text' method, 
## rather than the 'tk_dialog' method -- like on page 574 of the book 
## by Hattie Schroeder & Mike Doyel,'Interactive Web Applications
## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor".
##+########################################################################

proc popup_msgVarWithScroll { toplevName VARtext } {

   ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global.
   ## global env

   # bell
   # bell
  
   #################################################
   ## Set VARwidth & VARheight from $VARtext.
   #################################################
   ## To get VARheight,
   ##    split at '\n' (newlines) and count 'lines'.
   #################################################
 
   set VARlist [ split $VARtext "\n" ]

   ## For testing:
   #  puts "VARlist: $VARlist"

   set VARheight [ llength $VARlist ]

   ## For testing:
   #  puts "VARheight: $VARheight"


   #################################################
   ## To get VARwidth,
   ##    loop through the 'lines' getting length
   ##     of each; save max.
   #################################################

   set VARwidth 0

   #############################################
   ## LOOK AT EACH LINE IN THE LIST.
   #############################################
   foreach line $VARlist {

      #############################################
      ## Get the length of the line.
      #############################################
      set LINEwidth [ string length $line ]

      if { $LINEwidth > $VARwidth } {
         set VARwidth $LINEwidth 
      }

   }
   ## END OF foreach line $VARlist

   ## For testing:
   #   puts "VARwidth: $VARwidth"


   ###############################################################
   ## NOTE: VARwidth works for a fixed-width font used for the
   ##       text widget ... BUT the programmer may need to be
   ##       careful that the contents of VARtext are all
   ##       countable characters by the 'string length' command.
   ###############################################################


   #####################################
   ## SETUP 'TOP LEVEL' HELP WINDOW.
   #####################################

   catch {destroy $toplevName}
   toplevel  $toplevName

   # wm geometry $toplevName 600x400+100+50

   wm geometry $toplevName +100+50

   wm title     $toplevName "Note"
   # wm title   $toplevName "Note to $env(USER)"

   wm iconname  $toplevName "Note"


   #####################################
   ## In the frame '$toplevName' -
   ## DEFINE THE TEXT WIDGET and
   ## its two scrollbars --- and
   ## DEFINE an OK BUTTON widget.
   #####################################

   if {$VARheight > 10} {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_varwidth \
         -width  $VARwidth \
         -height $VARheight \
         -bg "#f0f0f0" \
         -relief raised \
         -bd 2 \
         -yscrollcommand "$toplevName.scrolly set" \
         -xscrollcommand "$toplevName.scrollx set"

      scrollbar $toplevName.scrolly \
         -orient vertical \
         -command "$toplevName.text yview"

      scrollbar $toplevName.scrollx \
         -orient horizontal \
         -command "$toplevName.text xview"
   } else {
      text $toplevName.text \
         -wrap none \
         -font fontTEMP_varwidth \
         -width  $VARwidth \
         -height $VARheight \
         -bg "#f0f0f0" \
         -relief raised \
         -bd 2 
   }

   button $toplevName.butt \
      -text "OK" \
      -font fontTEMP_varwidth \
      -command  "destroy $toplevName"

   ###############################################
   ## PACK *ALL* the widgets in frame '$toplevName'.
   ###############################################

   ## Pack the bottom button BEFORE the
   ## bottom x-scrollbar widget,

   pack  $toplevName.butt \
      -side bottom \
      -anchor center \
      -fill none \
      -expand 0


   if {$VARheight > 10} {
      ## Pack the scrollbars BEFORE the text widget,
      ## so that the text does not monopolize the space.

      pack $toplevName.scrolly \
         -side right \
         -anchor center \
         -fill y \
         -expand 0

      ## DO NOT USE '-expand 1' HERE on the Y-scrollbar.
      ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS
      ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA.
                
      pack $toplevName.scrollx \
         -side bottom \
         -anchor center \
         -fill x  \
         -expand 0

      ## DO NOT USE '-expand 1' HERE on the X-scrollbar.
      ## THAT KEEPS THE TEXT AREA FROM EXPANDING.

      pack $toplevName.text \
         -side top \
         -anchor center \
         -fill both \
         -expand 1
   } else {
      pack $toplevName.text \
         -side top \
         -anchor center \
         -fill both \
         -expand 1
   }


   #####################################
   ## LOAD MSG INTO TEXT WIDGET.
   #####################################

   ##  $toplevName.text delete 1.0 end
 
   $toplevName.text insert end $VARtext
   
   $toplevName.text configure -state disabled
  
}
## END OF PROC 'popup_msgVarWithScroll'


##+########################
## END of PROC definitions.
##+########################
## Set HELPtext variable.
##+########################

set HELPtext "\
*** HELP for this utility to make BASE64 data from a Tk 'photo' image file ***

This utility script is designed to select an image file supported by the
Tk create 'photo' function (such as a GIF or PGM or PPM or PNG file)
and invoke the 'base64' command (available on Linux/Unix/BSD)
to create base64 data.

The 'base64' data is shown in a popup window, such as a text-editor
window or the window of a text file browser such as 'xpg'.

---------

For 8.5.x (or some earlier) versions of the 'wish' interpreter,
the image file selected may be a GIF (or PGM or PPM) file.

The image file selected may be a PNG file, when using 8.6.x versions
of the 'wish' interpreter.

**********************
AUXILIARY SHELL SCRIPT:

When the user clicks on the '$aRtext(buttonBASE64)' button,
a shell script named

        sho_base64_data_from_imgfile.sh

(in a directory with this Tk GUI script) executes to run the 'base64'
Linux/Unix/BSD command on the selected image file and show the text
output from 'base64' in a popup GUI (a text-file editor or text-file browser).

That shell script can be edited to change the text-editor/browser 
that is used --- and to change the location and/or name of the
temporary text file that is created.

**************************************************
SOME SOURCES OF Tk 'photo' FILES (PGM,PPM,GIF,PNG):

In case you are looking for files to test this utility --- or to
convert for use in Tk apps ...

Here are some typical directories of *PGM* files on Linux installations:

   /usr/share/gimp/2.0/gimpressionist/Brushes/
   /usr/lib/pymodules/python2.5/openshot/transitions/
   /usr/share/kde4/apps/kdenlive/lumas/

These directories may be available if you have the GIMP image editor
and/or the OpenShot and Kdenlive movie editors installed.

---

For *PPM* files, you can try directories like

   /usr/share/doc/tk8.5/examples/images/
   /usr/share/gimp/2.0/gimpressionist/Brushes/

---

For *GIF* files, you can try directories like

   /usr/share/doc/tk8.5/examples/images/
   /usr/share/tcltk/tk8.5/images/

   as well as

   /usr/lib/firefox-3.6.16/res/
   /usr/lib/openoffice/basis3.1/share/config/wizard/web/images/
   /usr/lib/openoffice/basis3.1/share/gallery/
   /usr/lib/openoffice/basis3.1/share/template/en-US/wizard/bitmap/
   /usr/lib/python2.4/idlelib/Icons/
   /usr/lib/seamonkey-2.0.11/res/
   /usr/lib/xulrunner-1.9.1.16/res/
   /usr/share/apps/ksgmltools2/docbook/xsl/images/
   /usr/share/couchdb/www/image/
   /usr/share/cups/doc-root/images/
   /usr/share/doc/dvd+rw-tools/
   /usr/share/doc/kbd/dvorak/
   /usr/share/doc/perlmagick/examples/demo/
   /usr/share/gthumb/albumthemes/
   /usr/share/kde4/apps/ksgmltools2/docbook/xsl/images/
   /usr/share/sunbird/res/
   /usr/share/thunderbird/res/
   /usr/share/webkit-1.0/webinspector/Images/
   /usr/share/zenmap/pixmaps/

---

For *PNG* files, you can try a Tk 8.6 directory or directories like


   /usr/lib/erlang/lib/wings-1.0.1/textures/
   /usr/lib/firefox-3.6.16/icons/
   /usr/lib/firefox-3.6.16/res/
   /usr/lib/firefox-addons/extensions/
   /usr/lib/openoffice/basis3.1/share/gallery/htmlexpo/
   /usr/lib/pymodules/python2.6/openshot/blender/icons/
   /usr/lib/pymodules/python2.6/openshot/effects/icons/
   /usr/lib/pymodules/python2.6/openshot/images/
   /usr/lib/pymodules/python2.6/openshot/themes/
   /usr/lib/pymodules/python2.6/openshot/transitions/
   /usr/lib/pymodules/python2.6/openshot/uploads/logos/
   /usr/lib/pymodules/python2.6/openshot/windows/ui/icons/
   /usr/lib/seamonkey-2.0.11/chrome/icons/
   /usr/lib/seamonkey-2.0.11/extensions/
   /usr/lib/seamonkey-2.0.11/res/html/
   /usr/lib/seamonkey-2.0.11/searchplugins/
   /usr/lib/xulrunner-1.9.2.16/chrome/icons/default/
   /usr/lib/xulrunner-1.9.2.16/icons/
   /usr/lib/xulrunner-1.9.2.16/res/
   /usr/local/share/icons/hicolor/128x128/apps/
   /usr/local/share/icons/hicolor/128x128/mimetypes/
   /usr/share/abiword-2.6/clipart/
   /usr/share/aeskulap/images/
   /usr/share/agave/pixmaps/
   /usr/share/app-install/icons/  (many hundreds)
   /usr/share/apps/guarddog/pics/
   /usr/share/apps/karbon/icons/
   /usr/share/apps/kchart/icons/
   /usr/share/apps/kchart/pics/
   /usr/share/apps/kdeprint/icons/
   /usr/share/apps/kdeprint/pics/
   /usr/share/apps/kdeprint/
   /usr/share/apps/kdeui/about/
   /usr/share/apps/kdeui/pics/
   /usr/share/apps/kdewidgets/pics/
   /usr/share/apps/kexi/icons/
   /usr/share/apps/khtml/icons/
   /usr/share/apps/kivio/icons/
   /usr/share/apps/kivio/pics/
   /usr/share/apps/kivio/stencils/
   /usr/share/apps/kmfsystray/icons/
   /usr/share/apps/kmfsystray/pics/
   /usr/share/apps/kmyfirewall/icons/
   /usr/share/apps/kmyfirewall/pics/
   /usr/share/apps/koffice/icons/
   /usr/share/apps/koffice/pics/
   /usr/share/apps/kpresenter/...
   /usr/share/apps/krita/...
   /usr/share/apps/ksgmltools2/docbook/xsl/images/
   /usr/share/apps/kspread/...
   /usr/share/apps/kstyle/pixmaps/riscos/
   /usr/share/apps/kudesigner/icons/
   /usr/share/apps/kword/icons/
   /usr/share/ardour2/icons/
   /usr/share/brasero/icons/hicolor/
   /usr/share/cheese/...
   /usr/share/compiz/...
   /usr/share/couchdb/www/image/
   /usr/share/cups/doc-root/images/
   /usr/share/decibel-audio-player/pix/
   /usr/share/devede/
   /usr/share/diff-ext/icons/diff-ext/
   /usr/share/doc/bless/user/figures/
   ...
   ...
   /usr/share/filezilla/resources/...
   ...
   /usr/share/gimp/2.0/images/
   /usr/share/gimp/2.0/themes/Default/images/preferences/
   ...
   /usr/share/gnome-about/...
   /usr/share/gnome-applets/...
   /usr/share/gnome-control-center/pixmaps/
   /usr/share/gnome-doc-utils/...
   /usr/share/gnome-games/...
   /usr/share/gnome-media/icons/...
   /usr/share/gnome-nettool/pixmaps/
   /usr/share/gnome-panel/pixmaps/
   /usr/share/gnome-power-manager/icons/...
   ...
   /usr/share/gnome/help/...  (many hundreds)
   ...
   /usr/share/gthumb/albumthemes/
   /usr/share/gthumb/glade/
   /usr/share/gtk-doc/html/...
   /usr/share/gtkhtml-3.14/icons/
   /usr/share/hplip/data/images/
   /usr/share/icons/...      (many, many hundreds)
   /usr/share/inkscape/tutorials/
   /usr/share/k3d/icons/
   /usr/share/k3d/ngui/pixmap/
   /usr/share/k3d/ngui/rasterized/
   ...
   /usr/share/kde4/apps/...  (many)
   /usr/share/ktoon/themes/default/...
   ...
   /usr/share/libindicator/icons/hicolor/
   /usr/share/lives/icons/
   /usr/share/locale/l10n/...
   /usr/share/m17n/icons/...
   ...
   /usr/share/mplayer/skins/
   /usr/share/nautilus/icons/
   /usr/share/nautilus/patterns/
   ...
   /usr/share/pixmaps/...  (many)
   ...
   /usr/share/povray/...
   /usr/share/pyshared/openshot/...
   /usr/share/python-wxgtk2.8/Editra/pixmaps/
   /usr/share/qt4/doc/src/images/    (many hundreds)
   /usr/share/rhythmbox/icons/hicolor/...
   ...
   /usr/share/scribus/...
   /usr/share/skype/avatars/
   /usr/share/smplayer/themes/... (many)
   /usr/share/synaptic/...
   /usr/share/themes/...  (many)
   ...
   /usr/share/tomboy/icons/hicolor/...
   ...
   /usr/share/transmission/web/images/...
   ...
   /usr/share/ubuntu-docs/libs/...
   /usr/share/vlc/http/images/
   /usr/share/vlc/lua/http/images/
   /usr/share/vlc/osdmenu/default/...
   ...
   /usr/share/webkit-1.0/...
   ...
   /usr/share/zenity/...
   /usr/share/zenmap/pixmaps/...

So you see that there are many more PNG files available than
PGM, PPM, and even GIF files on the typical computer installation,
circa 2013.

And there are even more JPEG files --- from the Internet and from 
digital cameras, iPhones, Android-Phones, iPads, Android-Pads, etc.

But, unfortunately, the Tk 8.6 (and before) 'image create photo'
command does not support JPEG files. Tcl-Tk extensions are necessary.
"

##+######################################################
##+######################################################
## Additional GUI INITIALIZATION: not needed
##+######################################################



You can put the file containing the code of the following shell script in the same directory as the file containing the code of the Tk script above.

 Code for the shell script 'sho_base64_data_from_imgfile.sh' :
#!/bin/sh
##
## SCRIPT: sho_base64_data_from_imgfile.sh
##
## PURPOSE: Applies the 'base64' command to the input
##          (image) filename and shows the resulting text
##          in a popup GUI text-editor or text-browser window.
##
## INPUT: a single filename (may be fully-qualified)
##
## CALLED BY: Tk script 'make_BASE64data_fromTkPhotoIMAGEfile.tk'
##
## MAINTENANCE HISTORY:
## Written by: Blaise Montandon 2013aug14
## Changed by: Blaise Montandon 2021aug21 Add ability to make the output
##                                        filename based on the input
##                                        filename.
##+###################################################################

##+############################################
## Remove the directory name from the filename.
##+############################################

RELFILENAME=`basename $1`


##+########################################################
## Get the 'midname' of the filename by removing the
## extension --- such as '.png' or '.gif'.
##   Assumes one dot (.) in the filename, at the extension.
##+########################################################

# FILENAMECROP=`echo "$RELFILENAME" | sed 's|\..*$||'`

FILENAMECROP=`echo "$RELFILENAME" | cut -d\. -f1`

##+###########################################
## Make a filename for the base64 output file.
##+###########################################

OUTFILE="/tmp/${FILENAMECROP}_base64.txt"


##+################################################
## If a file by that name already exists, delete it.
## (The user is probably re-making the file.)
##+################################################

if test -f "$OUTFILE"
then
   rm -f "$OUTFILE"
fi


##+################################################
## Use the 'base64' command to make the output file.
##+################################################

base64 "$1" > "$OUTFILE"


##+################################################
## Show the output file, with a text-file editor
## or a text-file browser.
##+################################################

# TXTBROWSER="/usr/bin/gedit"
# TXTBROWSER="/usr/bin/kedit"
# TXTBROWSER="/usr/bin/kate"
# TXTBROWSER="$HOME/apps/bin/xpg"
# TXTBROWSER="$HOME/apps/xpg/xpg"
# TXTBROWSER="$HOME/apps/gscite2-27/SciTE"
  TXTBROWSER="$HOME/apps/gscite_2.27/SciTE"

$TXTBROWSER "$OUTFILE" &


I may make an icon on my desktop for this Tk script --- so that I can quickly choose a GIF (or whatever) file and make base64 data from it.


A FEW SIMILAR UTILITIES COMING:

Although I do not plan to do much Tcl-Tk programming for games --- because I have too many 'user-friendly useful utilities' (UUU's) that are on my 'to-do' list --- I plan to make a few more utilities related to BASE64 data and/or 'photo' image files that might be useful to those Tcler's who ARE interested in making games.

A list of those utilities will be accumulating in a 'CGA' (Code for GAming) section near the bottom of my 'bio' page at uniquename --- or in the 'CIP' (Code for Interactive image Processing) group.


IN CONCLUSION

As I have said on several other code-donation pages on this wiki ...

There's a lot to like about a utility that is 'free freedom' --- that is, no-cost and open-source so that you can modify/enhance/fix it without having to wait for someone else to do it for you (which may be never).

A BIG THANK YOU to Ousterhout for starting Tcl-Tk, and a BIG THANK YOU to the Tcl-Tk developers and maintainers who have kept the simply MAH-velous 'wish' interpreter going.