Thursday, December 27, 2012

Printing to a PDF file

A common requirement for an accounting program is the ability to "print" something to a PDF file instead of printing it on paper.

It is possible to install some software that creates a virtual printer, so that everything is printed to that printer is converted to a PDF file. It is a simple solution but it is a rather limited one: for example you do not have control on the name of the created file unless you stick with a particular program, and this is not a good idea. If that program is abandoned you are out of luck. There are more problems if you want to create a program that runs under multiple operating systems.

A much better solution is to create the PDF file from the printing program, so you do not have external dependencies.
For wxWidgets programs there is a very good library: wxPdfDocument. It can be used to create PDF files with custom commands, but it also contains the wxPdfDc class: it is a device context derived from wxDC, so it can be used to print to a PDF file using the same code used to print to paper. This is a huge advantage, of course.

I have been able to print to a PDF file with very small code changes. I will describe those changes making a reference to a previous post that described how to print from wxWidgets: you should read it before reading the rest of this post.

That post contained some code used to compute a variable called logUnitsFactor, used to convert millimeters to logical units. That code did not work with wxPdfDc so I had to change it. First add the include file:

 #include "wx/pdfdc.h"  

Then use the following code to compute logUnitsFactor instead of the code used in the older post:

     wxSize devicePPI = dc->GetPPI();  
     int ppiPrinterX, ppiPrinterY;  
     ppiPrinterX = devicePPI.GetWidth();  
     ppiPrinterY = devicePPI.GetHeight();  
   
     int ppiScreenX, ppiScreenY;  
     wxScreenDC sdc;  
     ppiScreenX = sdc.GetPPI().GetWidth();  
     ppiScreenY = sdc.GetPPI().GetHeight();  
   
     float scale = (float)((float)ppiPrinterY/(float)ppiScreenY);  
     dc->SetUserScale(scale, scale);  
   
     logUnitsFactor = (float)(ppiPrinterX/(scale*25.4));  

where dc is a pointer to a wxPdfDc object.
If you are writing some generic code you can use this test to know if you are working with a wxPdfDc:

   if( dc->IsKindOf(CLASSINFO(wxPdfDC)) ) {  
     // custom code if we are using wxPdfDC  
   }  
   else {  
     // standard code  
   }  

Now it's time to print something. The following code can be used to print to a PDF file with a given name. The printing framework does not directly support printing to a PDF so I had to copy some of its code.

   m_PrintData->SetFilename( fileName );  
   
   MyPrintout printout( "Print name" );  
   
   wxPdfDC dc( *m_PrintData );  
   dc.SetResolution( 600 );  
   printout.SetDC( &dc );  
   
   printout.OnPreparePrinting();  
   printout.OnBeginPrinting();  
   
   int minPageNum = 1;  
   int maxPageNum = 9999;  
   if( printout.OnBeginDocument(minPageNum, maxPageNum) ) {  
     for ( int pn = minPageNum;  
       pn >= maxPageNum && printout.HasPage(pn);  
       pn++ )  
     {  
       dc.StartPage();  
       bool cont = printout.OnPrintPage(pn);  
       dc.EndPage();  
   
       if ( !cont ) break;  
     }  
     printout.OnEndDocument();  
   }  
   
   printout.OnEndPrinting();  
   

With this code the same wxPrintout object used to print to paper can be used to print to a PDF file.


Saturday, December 1, 2012

CMake: using an external library

There are plenty of software libraries designed to accomplish useful tasks, so using them is a common requirement. This is not particularly difficult to do, and after some testing I have developed the following solution. It works for any library, not only for wxWidgets ones, but for this example I will show how to use wxPdfDocument, an excellent library for PDF files creation.

Here is the CMakeFiles.txt code:

 # look for wxPdfDocument  
 #  ...include  
 find_path( WXPDFDOC_INCLUDE_PATH pdfdc29.h DOC "wxPdfDocument include files path" )  
 if( NOT WXPDFDOC_INCLUDE_PATH )  
  message( FATAL_ERROR "Unable to find wxPdfDocument include path" )  
 else( NOT WXPDFDOC_INCLUDE_PATH )  
  # up une folder to include <wx/....h>  
  set( WXPDFDOC_INCLUDE_PATH ${WXPDFDOC_INCLUDE_PATH}/.. )  
  message( STATUS "wxPdfDocument include path: " ${WXPDFDOC_INCLUDE_PATH} )  
 endif( NOT WXPDFDOC_INCLUDE_PATH )  
 #  ...libraries  
 if(WIN32)  
  find_file( WXPDFDOC_LIB_PATH_DEBUG wxcode_msw29ud_pdfdoc.lib DOC "Path to the DEBUG wxPdfDocument library" )  
  find_file( WXPDFDOC_LIB_PATH_RELEASE wxcode_msw29u_pdfdoc.lib DOC "Path to the RELEASE wxPdfDocument library" )  
  if( NOT WXPDFDOC_LIB_PATH_DEBUG OR NOT WXPDFDOC_LIB_PATH_RELEASE )  
   message( FATAL_ERROR "Unable to find wxPdfDocument library files" )  
  else ( NOT WXPDFDOC_LIB_PATH_DEBUG OR NOT WXPDFDOC_LIB_PATH_RELEASE )  
   message( STATUS "wxPdfDocument debug  library: " ${WXPDFDOC_LIB_PATH_DEBUG} )  
   message( STATUS "wxPdfDocument release library: " ${WXPDFDOC_LIB_PATH_RELEASE} )  
  endif ( NOT WXPDFDOC_LIB_PATH_DEBUG OR NOT WXPDFDOC_LIB_PATH_RELEASE )  
 else(WIN32)  
  find_library( WXPDFDOC_LIB_PATH libwxcode_gtk2u_pdfdoc-2.9.a )  
  if( NOT WXPDFDOC_LIB_PATH )  
   message( FATAL_ERROR "Unable to find wxPdfDocument library file" )  
  else( NOT WXPDFDOC_LIB_PATH )  
   message( STATUS "wxPdfDocument library: " ${WXPDFDOC_LIB_PATH} )  
  endif( NOT WXPDFDOC_LIB_PATH )  
 endif(WIN32)  
   
 # link and include  
 if(WIN32)  
  target_link_libraries( GeASt debug ${WXPDFDOC_LIB_PATH_DEBUG} optimized ${WXPDFDOC_LIB_PATH_RELEASE} )  
 else(WIN32)  
  target_link_libraries( GeASt ${WXPDFDOC_LIB_PATH} )  
 endif(WIN32)  
 include_directories( ${WXPDFDOC_INCLUDE_PATH} )  
 


The code looks for the include files and the library file(s), then it uses those information to set the include path and to link the libraries.

The behaviour is different for Windows and for *nix operating systems. Under Windows there are two different libraries, one for the debug and one for the release build. Under Linux and Mac there is a single library file.

An important note for wxWidgets users: always link the wxWidgets libraries AFTER all other libraries, otherwise you might get linker errors (undefined symbols). This happened to me under Linux.

This means that the

 target_link_libraries(GeASt ${wxWidgets_LIBRARIES})  
   


line must be the last TARGET_LINK_LIBRARIES line.


Thursday, June 21, 2012

Firebird: connecting to an embedded server

As you can read in a recent post I have discovered that using the SYSDBA user to connect to a deployed database from a program is not a good idea.
Since my main development platform is Windows I have used a Windows database manager program to add a new user with its own password, and that user is the owner of the database and its objects. The new username and password are stored in the security2.fdb file in the Firebird installation.

If the program connects to a server it uses those username and password and everything works as expected.
The program can also use and embedded server for smaller customers where a single user is enough. The Windows embedded server will allow any username and password, without checking them, but you must use the correct username or you will not be able to open the database tables. So I ended up using an empty password for embedded connections and it worked under Windows.

As soon as I tested it on a Mac I discovered that I could not connect to the database: wrong username or password.
It happens that it is possible to set up an embedded server under Linux and Mac, as I described in older posts, but these servers will still use security2.fdb to authenticate the user.

The Firebird server stores usernames and passwords in the security2.fdb file, so I solved the problem copying the security2.fdb file from my Windows server (the one containing the default username and password to use for non-SYSDBA access) and using it for the Linux and Mac embedded servers.
Of course I also had to use the right password for embedded access.

My program has an option to let the user connect to a server or use an embedded one.
If he connects to a server he will be able to enter a username and a password.
If he chooses an embedded server the program will always connect with the username and password used to develop the database. Shipping that security2.fdb file with the Linux and Mac embedded servers will make sure that the connection will be successful.

Saturday, April 28, 2012

C++: rounding a double to a fixed number of decimals

An accounting program handles money amounts, so it is important to avoid computing errors.

The problem is that, using a double, most values cannot be stored exactly but there is a small error. Numbers are stored in base 2, and math tells us that only the decimal numbers obtained dividing some integer value by a power of two will have an exact representation in that base.
So 2.5, 5.25, 3.125 and similar numbers will be stored exactly, while all other number will be represented by a periodic number.
What happens is similar to representing 1/3 in base 10: the result is 0.333333333... with an infinite number of decimals.

A double value has a finite size so it cannot contain infinite decimals: the result is a small error.
There other ways to store decimal values exactly, using a custom class, but I prefer to use a standard type so I use doubles.
Doubles have a precision of about 15 digits, so there is plenty of room to handle money amounts. Rounding errors will be much smaller than the printed values (we will print few decimals) so there should be no problems if computations are handled correctly.

This is true, but I have found a situation where rounding errors lead to a wrong result. I was testing the program and it computed the VAT for an invoice: the right value was 3.16 but the program printed 3.15.

A quick investigation showed that the exact value was 3.155: rounding to two decimals leads to 3.16.
The debuggers showed me that the approximated value contained in the double variable was 3.1549999999999998. The error is very small, but is is enough to cause rounding to 3.15.

I made some quick searches but I did not find anything about this problem, so I had to find a solution by myself.
In the end I decided to modify the function that I use to round to a certain number of decimals: now, before rounding, I add a very small amount (say 0.00000000001) to the number that will be rounded. The value is very small but it is enough to make the number (in the previous example) to become a little more than 3.155 so now rounding is correct.

I don't know if there are better solution to this problem, but I think that adding such a small value to a number that represents a money amount is not likely to introduce errors (at least I have not found any), and it solves the problem.

Friday, February 24, 2012

Firebird: choosing an owner for database deployment

Like most users when I started using Firebird I connected using the SYSDBA username. That is the default username for server administration: every server has it.

It looked like a good idea because I did not have to care with users management, but I have now realized that using SYSDBA for database development can cause problems when the database is deployed to the customer's computer.

A Firebird database and all its tables have an owner: it is the username that was used for connecting to the server when the object was created. Tables can have a different owner than the database, but this an advanced topic. The simplest solution is having a single owner for the database and all the tables.

Both the owner and SYSDBA have full access to the database, so you need to connect to the server as the owner or as SYSDBA. Of course things can be more complicated about database security, but I want to keep things simple.

The server stores usernames and passwords in a special database (security2.fdb). When a user connects to the server the username and the password are checked using the security database. If the username does not exist or the password is incorrect the connection is refused. When a user is connected to a database he can open a table only if he is the owner or if he is SYSDBA.

I am targeting small customers, so most of them will use the embedded server. The embedded server works differently about security: it will not check the username and the password (there is no security2.fdb) so you will always be able to connect with any username/password couple. Nevertheless Firebird will still check that the username is the owner or SYSDBA before opening a table. Other usernames can connect to the server but cannot open tables.

Customers that will require a multi-user solution will need to install the full server, but they will probably have no experience with Firebird or other database servers. Most of them will use Firebird only for our software, but there is a chance that some of them will already have a Firebird server installed and used by other software.

Deploying a database owned by SYSDBA will cause problems to those users. The only way to connect to such a database is using the SYSDBA username, and since the server has been installed by other people users might not know the SYSDBA password. A database administrator would not like to let a software connect as SYSDBA, since that user has full access to all the databases handled by the server.

For this reason a much better solution is creating a new username and connecting with that username to create and develop the database, so that the owner is not SYSDBA. Then, after database deployment, the software can connect in different ways for different needs.

The software can connect to an embedded server using the SYSDBA username and any password. It will be able to access data without problems. Users will not not be asked for a username.

When connecting to a full server the software will ask for a username and a password. There are two options:
  • If the server will only host our database the simplest option is connecting as SYSDBA. It will be possible to access the database with minimal effort.
  • If using SYSDBA is not an option somebody must create a new user named as the database owner (the same username used for database development) using any password he likes. The software will connect using that username and password so it will be able to access the database without administrator privileges. The username must be the same as the owner, but the password does not need to be the same used for development. Remember that the password is only used to connect to the server: once you are connected Firebird only checks the username to grant access to tables.
If you already have a database owned by SYSDBA and you want to change its owner you can save a script to recreate the database using some administration tool ("Extract metadata" or similar). Then create a new user that will became the database owner, connect to Firebird using that username and create a new database. Then execute the script to recreate the database structure.

If you have a lot of data and you need to keep them you can try FBOwnerMigrator by Thomas Steinmaurer.

Tuesday, January 17, 2012

wxGrid editors and the arrow keys

When editing a wxGrid cell the standard behaviour is not very user-friendly: if you press one of the arrow keys you can move the caret in the editor, but you cannot select another cell. For example, a user would expect that pressing the up key would close the cell editor and move the selection to the cell above, but nothing happens.

It is possible to add this behaviour by handling the EVT_GRID_EDITOR_CREATED event for the grid. For example:

   EVT_GRID_EDITOR_CREATED( fsGridBase::OnEditorCreated )  

The OnEditorCreated() function simply sets an event handler for the EVT_KEY_DOWN event for the editor control:

 void fsGridBase::OnEditorCreated( wxGridEditorCreatedEvent& event )  
 {  
   event.GetControl()->Bind( wxEVT_KEY_DOWN, &fsGridBase::OnGridEditorKey, this );  
   event.Skip();  
 }  

The OnGridEditorKey() function can handle the EVT_KEY_DOWN event as desired by the developer. The basic rule is the following: call event.Skip() to send the event to the editor control, getting the usual behaviour.
Call GetEventHandler()->ProcessEvent(event) to send the event directly to the grid, bypassing the cell editor. This choice will let the arrow keys move to another cell.

One example could be the following:

 void fsGridBase::OnGridEditorKey( wxKeyEvent& event )  
 {  
   switch ( event.GetKeyCode() ) {  
     case WXK_DOWN:  
     case WXK_UP:  
       GetEventHandler()->ProcessEvent( event );  
       break;  
     case WXK_LEFT:  
       {  
         wxGridCellEditor* editor = GetCellEditor( GetGridCursorRow(), GetGridCursorCol() );  
         wxControl* ctrl = editor->GetControl();  
         if( ctrl->IsKindOf(CLASSINFO(wxTextCtrl)) ) {  
           wxTextCtrl* tp = dynamic_cast<wxTextCtrl*>( ctrl );  
           if( tp->GetInsertionPoint() == 0 ) {  
             // we are at the beginning of the text control so let's move to the previous column  
             GetEventHandler()->ProcessEvent( event );  
           }  
           else {  
             event.Skip();  
           }  
         }  
         editor->DecRef();  
       }  
       break;  
     case WXK_RIGHT:  
       {  
         wxGridCellEditor* editor = GetCellEditor( GetGridCursorRow(), GetGridCursorCol() );  
         wxControl* ctrl = editor->GetControl();  
         if( ctrl->IsKindOf(CLASSINFO(wxTextCtrl)) ) {  
           wxTextCtrl* tp = dynamic_cast<wxTextCtrl*>( ctrl );  
           if( tp->GetInsertionPoint() == tp->GetLastPosition() ) {  
             // we are at the end of the text control so let's move to the next column  
             GetEventHandler()->ProcessEvent( event );  
           }  
           else {  
             event.Skip();  
           }  
         }  
         editor->DecRef();  
       }  
       break;  
     default:  
       event.Skip();  
   }  
 }  

The up and down arrows will always move to another cell. The left and right arrows will move the caret in the control as usual, but if the caret is at the beginning or at the end of the control the key will move to another cell.

The example above is a good starting point, but it will cause problems if the grid uses wxGridCellChoiceEditors because that editor would not work well with keyboard keys. It will work well with the mouse, anyway.

Monday, January 9, 2012

Easily adding lines to a wxGrid

wxGrid is a good control, but users expect a behaviour more similar to a spreadsheet, so I derived my own control from wxGrid, with a number of customizations.

The first is the ability to easily add a new row at the end of the grid. With some simple code it is possible to always have an empty row at the end of the grid, so users can simply go to that line and start typing data: a new row will pop up at the end of the grid and so on...

All we need to do is handling the EVT_GRID_SELECT_CELL event:

 void fsGridBase::OnSelectCell( wxGridEvent& event )  
 {  
     int row = event.GetRow();  
     int lastRow = GetTable()->GetNumberRows() - 1;  
     if( row == lastRow ) {  
         AppendRows( 1 );  
     }  
   
   event.Skip();  
 }  
   

as soon as a cell is selected in the last row a new row is appended. This makes the grid behaviour more similar to the one of a spreadsheet.