2007-01-17

Print Individual Images Revisited

Referring to a previous post, from may 2006 entitled "Print individual images", Naomi Nosonovsky asks for the possibility to obtain a better control over the image.

That's really easy, and the original code needed a very simple modification, in a first moment clearing and then sending the desired information to the expr field of the report table.

The EXPR, TAG, and TAG2 columns in the header record are used for printer setup attributes.

In some limited cases, we may edit the values in the expr column and, if these values are reasonable for the associated attributes, they will be recognized by the designer, and used by the report engine at run time.

For example, if a report definition includes in the expr field : orientation=1, copies=3 and color=1, we will obtain a landscape report, three printed copies and an image printed in greyscale.

For the case related, we need to pass these information as parameters for the udf printimage(), eg.

=PrintImage(getpict(), "orientation=1" + chr(13) + "copies=3"

Notice that I added a CHR(13) to separate each configuration. That's very important, and VFP needs this to recognize the settings.

 

Here are some of the available settings:

driver=winspool
device=ms publisher imagesetter
output=file:
orientation=0 (0 = portrait ; 1 = landscape)
papersize=1
scale=100
ascii=100
copies=1
defaultsource=15
printquality=600
color=1 (0 = color ; 1 = greyscale)
yresolution=600
ttoption=2
collate=1


We can find precious information about these settings in vfp's help for prtinfo(): http://msdn2.microsoft.com/en-us/library/7h4yx3sf(vs.80).aspx

 

Here's the code - save it as PRINTIMAGE.PRG and call it like this:

 

= PrintImage (getpict(),"copies = 2" + chr(13) + "orientation = 1" + chr(13) + "color = 1")

* program: printimage.prg
function P
rintImage(tcimage, tcprintsettings)
*--------------------------------------------------------
* vfp code that shows how to print image files.
* code adapted from microsoft knowledge base article
* 895602.
http://support.microsoft.com/kb/895602/en-us/
*
* most of the codes and comments below come from
* trevor hancock, from ms
*--------------------------------------------------------
*--------------------------------------------------------
* parameters accepted:
* tcimage = image file name
* tcprintsettings = special printing settings, that allow to choose orientation, copies, etc
* eg. "copies = 2" + chr(13) + "orientation = 1"
* will print 2 copies with orientation set as landscape
* more printer settings can be found at vfp help for prtinfo()
* below is an example with some of the parameters allowed:
* driver=winspool
* device=ms publisher imagesetter
* output=file:
* orientation=0
* papersize=1
* scale=100
* ascii=100
* copies=1
* defaultsource=15
* printquality=600
* color=1
* yresolution=600
* ttoption=2
* collate=1
* if no parameter is passed, will print using the default printer settings
*--------------------------------------------------------
* related links
* msdn - prtinfo()
*
http://msdn2.microsoft.com/en-us/library/7h4yx3sf(vs.80).aspx
* pete sass : the visual foxpro report writer
* http://www.foxite.com/articles/read.aspx?id=24&document=the-visual-foxpro-report-writer

local
lnarea
lnarea =
select()
create cursor
reporttemp (imagefile c(150))
insert into
reporttemp values (tcimage)
*-- this calls a function that makes a report programmatically.
*-- this is included here to make sure that this sample can be run
*-- as-is, without asking the developer to manually create a report.

makereport(tcprintsettings)

*-- make sure that the cursor is selected,
*-- and then run the report to preview using
*-- the instance of our report listener.

select
reporttemp
report form
___imagereport preview
delete file
"___imagereport.fr*"
select
(lnarea)
return


 

*--------------------------------
*-- this function programmatically creates a report
*-- with an ole bound control and other fields. this is included
*-- only for demonstration purposes so this article code can stand-alone.
*-- typically, you would create your own report manually by using
*-- the report designer.

function
makereport(tcprintsettings)

if vartype(tcprintsettings) <> "c"
tcprintsettings = ""

endif

tcprintsettings =
strtran(tcprintsettings, " ", "")

create report ___imagereport from reporttemp

*-- open the report file (frx) as a table.
use
___imagereport.frx in 0 alias thereport exclusive
select
thereport

*-- remove from the frx the auto generated fields and labels
delete from
thereport where objtype = 5 and objcode = 0 && remove the labels
delete from
thereport where objtype = 8 and objcode = 0 && remove the fields

*-- find the header record (normally the first one, but who knows)
locate for
objtype = 1 and objcode = 53
if found
()
replace tag with "",; && clears the tag, tag2
tag2 with "",;
expr with tcprintsettings && stores the new expr
endif

*-- add a picture/ole bound control to the report by inserting a
*-- record with appropriate values. using an object that is based on the empty
*-- class here and the gather name class later to insert the record makes it easier to
*-- see which values line up to which fields (when compared to a large
*-- sql-insert command).

local
lonewrecobj as empty
lonewrecobj =
newobject( 'empty' )
addproperty
( lonewrecobj, 'platform', 'windows' )
addproperty
( lonewrecobj, 'uniqueid', sys(2015) )
addproperty
( lonewrecobj, 'objtype', 17 ) && "picture/ole bound control"
addproperty
( lonewrecobj, 'name', 'reporttemp.imagefile' ) && the object ref to the image object.
addproperty
( lonewrecobj, 'hpos', 100)
addproperty
( lonewrecobj, 'vpos', 600)
addproperty
( lonewrecobj, 'height', 100000)
addproperty
( lonewrecobj, 'width', 100000)
addproperty
( lonewrecobj, 'double', .t. ) && picture is centered in the "picture/ole bound control"
addproperty
( lonewrecobj, 'supalways', .t. )
*-- for the picture/ole bound control, the contents of the offset field specify whether
*-- filename (0), general field name (1), or expression (2) is the source.

addproperty
( lonewrecobj, 'offset', 2 )
*-- add the picture/ole bound control record to the report.

append blank in
thereport
gather name
lonewrecobj memo
*-- clean up and then close the report table.

pack
use in select
('thereport')
endfunc

2007-01-09

Send Images to the Clipboard with GdiPlus-X

Important: THIS IS AN OLD SAMPLE, KEPT HERE ONLY FOR A SAMPLE CODE. SEE THE METHOD 'TOCLIPBOARD()' IN GDIPLUSX

All samples below use the new GdiPlus-X library, from VFPX
http://www.codeplex.com/wiki/view.aspx?projectname=vfpx&title=gdiplusx

 

 
** the following code example demonstrates how to send an
** image to the clipboard
** sample totally based on the method "toclipboard" from gpimage gdi+ class
** from alexander golovlev

DO LOCFILE("system.app")
WITH _SCREEN.SYSTEM.drawing
LOCAL lobmp AS xfcbitmap
lobmp = .BITMAP.new(GETPICT())
LOCAL lhbitmap, hbmp, hndl
lhbitmap = lobmp.gethbitmap(.COLOR.lightgray)

#DEFINE err_clipnotopen "cannot open the clipboard"
#DEFINE err_clipnodata "no bitmap data found on the clipboard"
#DEFINE err_clipsetdata "cannot place data on the clipboard"

*-- predefined clipboard formats
#DEFINE cf_bitmap 2
#DEFINE cf_palette 9
#DEFINE obj_bitmap 7

DECLARE LONG openclipboard IN win32api LONG HWND
DECLARE LONG closeclipboard IN win32api
DECLARE LONG emptyclipboard IN win32api
*declare long getclipboarddata in win32api long uformat
DECLARE LONG setclipboarddata IN win32api LONG uformat, LONG HMEM
DECLARE LONG copyimage IN win32api LONG himage, LONG utype, LONG cx, LONG cy, LONG uflags
DECLARE LONG deleteobject IN win32api LONG hobject

hbmp = copyimage(lhbitmap, 0, 0, 0, 0)
deleteobject(lhbitmap)

IF openclipboard(0) != 0
emptyclipboard()
hndl = setclipboarddata(cf_bitmap, hbmp)
closeclipboard()
IF hndl = 0
ERROR err_clipsetdata
ENDIF
ELSE
ERROR err_clipnotopen
ENDIF

ENDWITH
RETURN

2006-11-27

FUNCTION TO CONVERT COLORS

The short function below converts any color to its darker or brighter version, just as the original colorpicker slidebar.

parameters:

      rgb - original rgb value to convert

      tnlevel - ranges from -100 to + 100. zero means no changes; +100 = white; -100 = black; positive values will return brighter images, while negative will bring darker.

 

lparameters tnRgb, tnLevel

if tnLevel = 0
   return tnrgb
endif


tnLevel = tnLevel / 100

local lnRed, lnGreen, lnBlue
lnRed   = bitand(tnRgb, 0x000000ff)
lnGreen = bitrshift(bitand(tnRgb, 0x0000ff00), 8 )
lnBlue  = bitrshift(bitand(tnRgb, 0x00ff0000), 16)


if tnLevel > 0
   return rgb( ;
      lnRed   + ((255 - lnRed)   * tnLevel) , ;
      lnGreen + ((255 - lnGreen) * tnLevel) , ;
      lnBlue  + ((255 - lnBlue)  * tnLevel) )
else
   return rgb( ;
      lnRed   + (lnRed   * tnLevel) , ;
      lnGreen + (lnGreen * tnLevel) , ;
      lnBlue  + (lnBlue  * tnLevel) )
endif


To better understand how it works, you can download the attached file, that contains a simple example. Click on "select color" to pick a color then play with the spinner to see the darker and brighter resulting colors.



2006-11-23

Updated version of the My namespace

Doug Hennig very kindly published a new version of the my namespace on the White papers and source code page of the stonefield web site.

It's a 240 k download. This version contains some fixes, a small project and a simple form showing how to use some functions.

In my tests, I installed "MY" with no errors. It works fine even without having a previous version installed, and does not need the sedna ctp to run, just vfp9. Intellisense worked perfectly, showing only the properties and methods of interest, and inside a form. The help file is nice, and contains good documentation about the more than 60 functions included.

As always, another great job from Doug.

2006-09-18

HOWTO: CHANGE BITMAP RESOLUTION

The bitmap resolution can be also very easily changed with gdi+.

The sample below uses the new GdiPlus-X classes from VFPX projects that can be downloaded from here: http://www.codeplex.com/wiki/view.aspx?projectname=vfpx&title=gdiplusx

*!* HOW TO CHNGE THE BITMAP RESOLUTION
* the code loads an image, changes its resolution to 200x200
* and saves it again as a bmp
*
http://msdn2.microsoft.com/en-us/library/system.drawing.bitmap.setresolution.aspx

do locfile("System.app")

with _screen.system.drawing
   * load image to gdi+
   local loBmp as xfcBitmap
   loBmp = .Bitmap.New(getpict())

   * change Bitmap Resolution
   loBmp.SetResolution(200,200)

   * save the bitmap
   loBmp.Save("c:\newresolution.bmp", .imaging.imageformat.bmp)

endwith
return

2006-09-17

Using TIFFs with the new GDI+ classes - UPDATE

As a continuation of my article from may 2006 published in UTMAG, entitled "Multiframe images with Gdi+" i wrote a New article published in the september 2006 issue of utmag called "Using TIFFs with the New Gdi+ classes" showing how to manipulate TIFF images using the New gdiplus-x library from VFP-X project. It also shows how to create TIFFs using all Compressions supported.

But I left one point not very clear. This came to me after I saw a question from Jennifer Slusher in the universal thread forums (thread id #1153672), asking how to create multiPage TIFFs in Compression 4 (ccitt4). in that article, I showed how to create common Multiframe TIFFs and also how to create a single frame TIFF using Compression, but forgot to show or comment about creating Multiframe TIFFs with Compression.

Multiframe TIFFs with Compression

Gdi+ by default uses the 'lzw lossless' Compression whenever asked to save as TIFF. According to Wayne Fulton, from www.scantips.com, "lossless means there is no quality loss due to Compression. lossless guarantees that you can always read back exactly what you thought you saved, bit-for-bit identical, without data corruption". so, unless we specify, gdi+ will always use lzw Compression on TIFFs.

To create a Multiframe TIFF with Compression different from lzw, we need to send some additional Encoder Parameters to gdi+, even if the original picture is already a TIFF with another Compression. The methods "save" and "saveadd" permit us to send more than one Encoder Parameter at a time. Then, all we need to do is to create another Encoder object based on the guid for the Compression Parameter category, and set its EncoderValue to the desired Compression, eg Compressionccitt4.

The sample below will ask for any three images. To ensure that it will be able to save using Compression ccitt4, it will first convert all the selected images to monochrome. Then it will create a Multiframe TIFF image file containing all selected images in the Compression ccitt4.



** The following example loads three Bitmap objects
** converts all Bitmaps to monochrome to be compatible with rle, ccitt3/4 Compression
** the code saves all three images in a single, multiple-frame TIFF
** file, using Compression format CCITT4 
DO LOCFILE("System.app")

WITH _SCREEN.SYSTEM.Drawing
    LOCAL loBmp AS xfcBitmap
    LOCAL loMultif AS xfcBitmap
    LOCAL loPage2 AS xfcBitmap
    LOCAL loPage3 AS xfcBitmap
    LOCAL myEncoder AS xfcEncoder
    LOCAL myCompEncoder AS xfcEncoder
    LOCAL myEncoderParameter AS xfcEncoderParameter
    LOCAL myCompEncoderParameter AS xfcEncoderParameter
    LOCAL myEncoderParameters AS xfcEncoderParameters

    *!* create three Bitmap objects.
    *!* 1st we load the original Bitmap,
    *!* and then get its monochrome version
    m.loBmp    = .BITMAP.New(GETPICT())
    m.loMultif = m.loBmp.getMonochrome()
    m.loBmp    = .BITMAP.New(GETPICT())
    m.loPage2  = m.loBmp.getMonochrome()
    m.loBmp    = .BITMAP.New(GETPICT())
    m.loPage3  = m.loBmp.getMonochrome()

    *!* release the original Bitmap because we'll not use it any more
    m.loBmp = NULL

    *!* create Encoder object based on the guid for the saveflag Parameter category.
    m.myEncoder = .Imaging.Encoder.saveflag

    *!* create Encoder object based on the guid for the Compression Parameter category.
    m.mycompEncoder = .Imaging.Encoder.Compression

    && create an EncoderParameters object.
    && EncoderParameters object has an array of EncoderParameter objects
    && in this case, there'll be 2 EncoderParameter objects in the array.
    m.myEncoderParameters = .Imaging.EncoderParameters.New(2)

    *!* save the first Page (frame).
    m.myEncoderParameter = .Imaging.EncoderParameter.New(m.myEncoder, ;
          .Imaging.EncoderValue.Multiframe)
    m.mycompEncoderParameter = .Imaging.EncoderParameter.New(m.mycompEncoder, ;
          .Imaging.EncoderValue.Compressionccitt4)

    m.myEncoderParameters.PARAM[1] = m.myEncoderParameter
    m.myEncoderParameters.PARAM[2] = m.mycompEncoderParameter
    m.loMultif.SAVE("c:\NewMultiframeCompress.tif", ;
          .Imaging.imageformat.TIFF, m.myEncoderParameters)

    *!* save the second Page (frame).
    *!* this time we will only change the 1st Parameter to "framedimensionPage"
    *!* the 2nd Parameter that sets the Compression will remain the same
    m.myEncoderParameter = .Imaging.EncoderParameter.New(m.myEncoder, ;
          .Imaging.EncoderValue.framedimensionPage)
    m.myEncoderParameters.PARAM[1] = m.myEncoderParameter
    m.loMultif.saveadd(m.loPage2, m.myEncoderParameters)

    *!* save the third Page (frame).
    *!* this time we don't need to make any change to the Encoder paramenters
    *!* we'll keep using the same Parameters used when we added the 2nd frame
    m.loMultif.saveadd(m.loPage3, m.myEncoderParameters)

    *!* close the multiple-frame file.
    *!* this time we call the "flush" Parameter to close the file.
    *!* we don't need the 2nd Parameter any more, so change it to null
    m.myEncoderParameter = .Imaging.EncoderParameter.New(m.myEncoder, ;
          .Imaging.EncoderValue.FLUSH)
    m.myEncoderParameters.PARAM[1] = m.myEncoderParameter
    m.myEncoderParameters.PARAM[2] = NULL
    m.loMultif.saveadd(m.myEncoderParameters)
ENDWITH
RETURN



Notice that the object myEncoderParameters contains a property "params" that is an array of Parameters.


Other related links:
TIFF, tag image file format
A few scanning tips

 

 

2006-09-12

Helper code to create image on the fly for "Using the Alpha Channel in Visual Foxpro Images" from Bernard Bout

Here's another very simple code that attends Bernard Bout, creating an image on the fly, based on his post entitled "Using the alpha channel in visual foxpro images". Again in this sample, I'm using the new Gdiplus-X classes from VFPX project that can be downloaded from GitHub



* Helper code to create image on the fly for
* Bernard Bout "Using the alpha channel in VFP images"
* http://weblogs.foxite.com/bernardbout/archive/2006/09/11/2436.aspx
*    image dimensions 300 x 270 pixels
*    make all image totally transparent
*    draw a light yellow - rgb(254,254,228) rectangle
*         centered in the main image.
*    save as PNG, to preserve the transparencies
DO LOCFILE("System.app")

WITH _SCREEN.SYSTEM.Drawing
    * create an empty bitmap
    LOCAL loBitmap AS xfcBitmap
    m.loBitmap = .BITMAP.New(300,270)

    * initialize the graphics object
    LOCAL loGfx AS xfcGraphics
    m.loGfx = .Graphics.FromImage(m.loBitmap)

    * make all image transparent
    m.loGfx.CLEAR(.COLOR.FromARGB(0,0,0,0))

    * draw the yellow rectangle
    m.loGfx.FillRectangle(.SolidBrush.New(.COLOR.FromRGB(254,254,228)), 10,9,278,249)

    * save as png to keep transparencies
    m.loBitmap.SAVE("c:\bernardboutagain.png", .Imaging.ImageFormat.Png)
ENDWITH
RETURN