Cross-Platform Programming with wxWidgets
Just Make It Cross-Platform
Subscribe to Feed
  • Home
  • Projects
  • Links

Creating Nice Reports with wxWidgets ZIP API

wxWidgets Add comments |

wxWidgets contains a set of classes which handle several archive formats. Most commonly used archive format is ZIP. This tutorial shows how to use wxWidgets API for reading and writing ZIP-files.
As you probably know, Microsoft Office 2007 produces .DOCX files which are ZIP archives which contain several XML files, images and few directories inside. After visiting Microsoft Developer’s Days I decided to add new feature to my current project and create new type of report which is based on .DOCX file format. This tutorial contains some techniques which you can use for generating such reports by yourself.

First of all, we have to open Microsoft Word 2007 and create a stub document. The document which is used in this article contains:

  • Header
  • Textual description
  • Table with two columns
  • Image at the bottom of the table

Creating nice reports in wxWidgets using ZPI API

So, we created our document and saved it as “Sample.docx”. Now we have to rename it to “Sample.zip” and extract all files from our archive to some directory.

Creating nice reports in wxWidgets using ZPI API - 2

Well, now we can start the development of our sample application.

ZIPTestApp.h

#ifndef _ZIP_TEST_APP_H
#define _ZIP_TEST_APP_H

#include 

class ZIPTestApp : public wxApp
{
public:
	virtual bool OnInit();
};

DECLARE_APP(ZIPTestApp)

#endif

ZIPTestApp.cpp

#include "ZIPTestApp.h"
#include "ZIPTestMainFrame.h"
#include <wx/image.h>
#include <wx/filesys.h>
#include <wx/fs_zip.h>

IMPLEMENT_APP(ZIPTestApp)

bool ZIPTestApp::OnInit()
{
	wxImage::AddHandler(new wxPNGHandler);
	wxFileSystem::AddHandler(new wxZipFSHandler);
	ZIPTestMainFrame * frame = new ZIPTestMainFrame;
	SetTopWindow(frame);
	frame->Centre();
	frame->Show();
	return true;
}

ZIPTestMainFrame.h

#ifndef _ZIP_TEST_MAINFRAME_H
#define _ZIP_TEST_MAINFRAME_H

#include <wx/wx.h>
#include <wx/grid.h>
#include <wx/spinctrl.h>
#include <wx/filepicker.h>
#include <wx/aui/aui.h>

class ZIPTestMainFrame : public wxFrame
{
	DECLARE_DYNAMIC_CLASS(ZIPTestMainFrame)
	int m_RowCount;
	wxString m_ImageFile;
	wxAuiManager m_FrameManager;
	wxTextCtrl * m_Editor;
	wxSpinCtrl * m_RowCountSpin;
	wxGrid * m_Grid;
	wxFilePickerCtrl * m_ImageFilePicker;
	wxArrayString m_DocumentTemplate;
	void CreateControls();

public:
	ZIPTestMainFrame();
	~ZIPTestMainFrame();
	bool Create(wxWindow * parent,
		wxWindowID id = wxID_ANY,
		const wxString & title = wxEmptyString);

	DECLARE_EVENT_TABLE()
	void OnSave(wxCommandEvent & event);
	void OnExit(wxCommandEvent & event);
	void OnAbout(wxCommandEvent & event);
	void OnRowCountSpin(wxSpinEvent & event);
};

#endif

ZIPTestMainFrame.cpp

#include "ZIPTestMainFrame.h"
#include "ZIPTestApp.h"
#include "ZIPTestBinaryData.h"
#include <wx/valgen.h>
#include <wx/aboutdlg.h>

IMPLEMENT_DYNAMIC_CLASS(ZIPTestMainFrame, wxFrame)

enum
{
	ID_EDITOR = 10001,
	ID_ROWCOUNT_SPIN,
	ID_GRID,
	ID_IMAGE_FILEPICKER
};

BEGIN_EVENT_TABLE(ZIPTestMainFrame, wxFrame)
EVT_MENU(wxID_SAVE, ZIPTestMainFrame::OnSave)
EVT_MENU(wxID_EXIT, ZIPTestMainFrame::OnExit)
EVT_MENU(wxID_ABOUT, ZIPTestMainFrame::OnAbout)
EVT_SPINCTRL(ID_ROWCOUNT_SPIN, ZIPTestMainFrame::OnRowCountSpin)
END_EVENT_TABLE()

ZIPTestMainFrame::ZIPTestMainFrame()
{
	Create(NULL, wxID_ANY, _("ZIP Test"));
}

ZIPTestMainFrame::~ZIPTestMainFrame()
{
	m_FrameManager.UnInit();
}

bool ZIPTestMainFrame::Create(wxWindow * parent,
	wxWindowID id, const wxString & title)
{
	bool res = wxFrame::Create(parent, id, title,
		wxDefaultPosition, wxSize(500,400));
	if(res)
	{
		CreateControls();
	}
	return res;
}

void ZIPTestMainFrame::CreateControls()
{
	SetMinSize(wxSize(400, 300));
	SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);

	wxMenuBar * menuBar = new wxMenuBar;
	SetMenuBar(menuBar);

	wxMenu * fileMenu = new wxMenu;
	fileMenu->Append(wxID_SAVE, _("Save\tCtrl+S"));
	fileMenu->AppendSeparator();
	fileMenu->Append(wxID_EXIT, _("Exit\tAlt+F4"));

	wxMenu * helpMenu = new wxMenu;
	helpMenu->Append(wxID_ABOUT, _("About..."));

	menuBar->Append(fileMenu, _("File"));
	menuBar->Append(helpMenu, _("Help"));

	m_FrameManager.SetManagedWindow(this);

	wxPanel * panel = new wxPanel(this, wxID_ANY);
	wxBoxSizer * panelSizer = new wxBoxSizer(wxVERTICAL);
	panel->SetSizer(panelSizer);

	wxStaticText * editor_label = new wxStaticText(panel,
		wxID_ANY, _("Description:"));
	m_Editor = new wxTextCtrl(panel, ID_EDITOR);
	wxBoxSizer * description_sizer = new wxBoxSizer(wxHORIZONTAL);
	description_sizer->Add(editor_label, 0, wxALIGN_CENTER_VERTICAL);
	description_sizer->Add(m_Editor, 1, wxGROW|wxLEFT, 5);

	wxStaticBoxSizer * grid_sizer = new wxStaticBoxSizer(wxVERTICAL,
		panel, _("Grid"));
	wxStaticText * rows_label = new wxStaticText(panel,
		wxID_ANY, _("Amount of rows:"));
	m_RowCountSpin = new wxSpinCtrl(panel, ID_ROWCOUNT_SPIN);
	m_RowCountSpin->SetValidator(wxGenericValidator(&m_RowCount));
	wxBoxSizer * rows_sizer = new wxBoxSizer(wxHORIZONTAL);
	rows_sizer->Add(rows_label, 0, wxALIGN_CENTER_VERTICAL);
	rows_sizer->Add(m_RowCountSpin, 1, wxGROW|wxLEFT, 5);
	m_Grid = new wxGrid(panel, ID_GRID, wxDefaultPosition,
		wxSize(-1, 300), wxSUNKEN_BORDER);
	m_Grid->CreateGrid(0, 2);
	m_Grid->SetColLabelValue(0, _("Name"));
	m_Grid->SetColLabelValue(1, _("Price"));
	m_Grid->SetColMinimalWidth(0, 150);
	m_Grid->SetColMinimalWidth(1, 150);
	m_Grid->SetColFormatFloat(1, 2, 2);
	grid_sizer->Add(rows_sizer, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5);
	grid_sizer->Add(m_Grid, 1, wxEXPAND|wxLEFT|wxRIGHT|wxBOTTOM, 5);

	wxStaticText * image_label = new wxStaticText(panel,
		wxID_ANY, _("Image file:"));
	m_ImageFilePicker = new wxFilePickerCtrl(panel,
		ID_IMAGE_FILEPICKER, wxEmptyString, wxFileSelectorPromptStr,
		_("PNG Images (*.png)|*.png"));
	wxBoxSizer * image_sizer = new wxBoxSizer(wxHORIZONTAL);
	image_sizer->Add(image_label, 0, wxALIGN_CENTER_VERTICAL);
	image_sizer->Add(m_ImageFilePicker, 1, wxGROW|wxLEFT, 5);

	panelSizer->Add(description_sizer, 0, wxGROW|wxALL, 5);
	panelSizer->Add(grid_sizer, 1, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5);
	panelSizer->Add(image_sizer, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5);

	CreateStatusBar(2);

	m_FrameManager.AddPane(panel, wxAuiPaneInfo().CenterPane());
	m_FrameManager.Update();
}

void ZIPTestMainFrame::OnExit(wxCommandEvent & event)
{
	Close();
}

void ZIPTestMainFrame::OnAbout(wxCommandEvent & event)
{
	wxAboutDialogInfo info;
	info.SetName(wxT("ZIPTest"));
	info.SetVersion(wxT("v0.1"));
	info.SetWebSite(wxT("http://wxwidgets.info"));
	info.AddDeveloper(wxT("Volodymir (T-Rex) Tryapichko"));
	wxAboutBox(info);
}

void ZIPTestMainFrame::OnRowCountSpin(wxSpinEvent & event)
{
	TransferDataFromWindow();
	if(m_Grid->GetNumberRows() > m_RowCount)
	{
		m_Grid->DeleteRows(0, m_Grid->GetNumberRows()-m_RowCount);
	}
	else
	{
		m_Grid->AppendRows(m_RowCount-m_Grid->GetNumberRows());
	}

}

void ZIPTestMainFrame::OnSave(wxCommandEvent & event)
{
	wxFileDialog dlg(this, wxFileSelectorPromptStr,
		wxEmptyString, wxEmptyString,
		_("Microsoft Word 2007 Documents (*.docx)|*.docx"),
		wxFD_SAVE);
	if(dlg.ShowModal() == wxID_OK)
	{
		TransferDataFromWindow();
		m_ImageFile = m_ImageFilePicker->GetPath();
	}
}

Creating nice reports in wxWidgets using ZPI API - 3

As you can see, our sample application contains a single frame with menu bar and status bar. At the top of main frame we have a text box where we should enter a description of our report. Also our main frame contains a grid and a spin control where we can specify the amount of rows in our grid. At the bottom of main frame we have a file picker. It is used for loading an image which should be displayed at the bottom of our report.
Now we can start implementing the functionality related to our ZIP archive. As you can see, there is “[Content_Types].xml” file in root directory of .DOCX file. It is a small XML document. We can use wxXmlDpcument class and generate this file manually.
Creating nice reports in wxWidgets using ZPI API - 4

ZIPTestMainFrame.cpp

void ZIPTestMainFrame::SaveContentTypesXML(wxZipOutputStream & zip)
{
	wxXmlDocument doc;
	wxXmlNode * root = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Types"));
	root->AddProperty(wxT("xmlns"),
		wxT("http://schemas.openxmlformats.org/package/2006/content-types"));
	doc.SetRoot(root);
	wxXmlNode * node;

	if(!m_ImageFile.IsEmpty() && wxFileExists(m_ImageFile))
	{
		node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Default"));
		node->AddProperty(wxT("Extension"), wxT("png"));
		node->AddProperty(wxT("ContentType"), wxT("image/png"));
		root->AddChild(node);
	}

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Default"));
	node->AddProperty(wxT("Extension"), wxT("rels"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-package.relationships+xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Default"));
	node->AddProperty(wxT("Extension"), wxT("xml"));
	node->AddProperty(wxT("ContentType"), wxT("application/xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Override"));
	node->AddProperty(wxT("PartName"), wxT("/word/document.xml"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-officedocument.")
			wxT("wordprocessingml.document.main+xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Override"));
	node->AddProperty(wxT("PartName"), wxT("/word/styles.xml"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-officedocument.")
		wxT("wordprocessingml.styles+xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Override"));
	node->AddProperty(wxT("PartName"), wxT("/docProps/app.xml"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-officedocument.")
		wxT("extended-properties+xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Override"));
	node->AddProperty(wxT("PartName"), wxT("/word/settings.xml"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-officedocument.")
		wxT("wordprocessingml.settings+xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Override"));
	node->AddProperty(wxT("PartName"), wxT("/word/theme/theme1.xml"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-officedocument.theme+xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Override"));
	node->AddProperty(wxT("PartName"), wxT("/word/fontTable.xml"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-officedocument.")
		wxT("wordprocessingml.fontTable+xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Override"));
	node->AddProperty(wxT("PartName"), wxT("/word/webSettings.xml"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-officedocument.")
		wxT("wordprocessingml.webSettings+xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Override"));
	node->AddProperty(wxT("PartName"), wxT("/docProps/core.xml"));
	node->AddProperty(wxT("ContentType"),
		wxT("application/vnd.openxmlformats-package.")
		wxT("core-properties+xml"));
	root->AddChild(node);

	zip.PutNextEntry(wxT("[Content_Types].xml"));
	doc.Save(zip);
}

This method creates XML document, populates it with nodes, then creates a new entry named “[Content_Types].xml” in ZIP archive and saves XML document into this entry.
The same way we use for generating “_rels/.rels” file.

ZIPTestMainFrame.cpp

void ZIPTestMainFrame::SaveDocumentRels(wxZipOutputStream & zip)
{
	wxXmlDocument doc;
	wxXmlNode * root = new wxXmlNode(wxXML_ELEMENT_NODE,
		wxT("Relationships"));
	root->AddProperty(wxT("xmlns"),
		wxT("http://schemas.openxmlformats.org/package/2006/relationships"));
	doc.SetRoot(root);
	wxXmlNode * node;

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Relationship"));
	node->AddProperty(wxT("Id"), wxT("rId3"));
	node->AddProperty(wxT("Type"),
		wxT("http://schemas.openxmlformats.org/officeDocument")
		wxT("/2006/relationships/extended-properties"));
	node->AddProperty(wxT("Target"),
		wxT("docProps/app.xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Relationship"));
	node->AddProperty(wxT("Id"), wxT("rId2"));
	node->AddProperty(wxT("Type"),
		wxT("http://schemas.openxmlformats.org/package/2006/")
		wxT("relationships/metadata/core-properties"));
	node->AddProperty(wxT("Target"),
		wxT("docProps/core.xml"));
	root->AddChild(node);

	node = new wxXmlNode(wxXML_ELEMENT_NODE, wxT("Relationship"));
	node->AddProperty(wxT("Id"), wxT("rId1"));
	node->AddProperty(wxT("Type"),
		wxT("http://schemas.openxmlformats.org/officeDocument/")
		wxT("2006/relationships/officeDocument"));
	node->AddProperty(wxT("Target"),
		wxT("word/document.xml"));
	root->AddChild(node);

	zip.PutNextEntry(wxT("_rels/.rels"));
	doc.Save(zip);
}

Other XML documents placed into .DOCX file are rather large and it is not a good idea to generate them manually. We will used Bin2C utility, whose source code is freely available from wxForum for generating c-arrays from this files.

ZIPTestBinaryData.h

#ifndef _ZIP_TEST_BINARY_DATA_H
#define _ZIP_TEST_BINARY_DATA_H
...
unsigned char websettings_xml[] ={
	0x3C,0x3F,0x78,0x6D,0x6C,0x20,0x76,0x65,0x72,0x73,0x69,0x6F,0x6E,0x3D,
	0x22,0x31,0x2E,0x30,0x22,0x20,0x65,0x6E,0x63,0x6F,0x64,0x69,0x6E,0x67,
	...
	0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x3E
};
...

So, these files will be hard-coded into our executable but you can create e.g. a set of DLLs which will contain different templates for different types of reports and then use these DLLs as plug-ins.
Now we can generate new entries in our ZIP archive using this binary data:

ZIPTestMainFrame.cpp

void ZIPTestMainFrame::SaveDocumentSettings(wxZipOutputStream & zip)
{
	zip.PutNextEntry(wxT("word/fontTable.xml"));
	zip.Write(fonttable_xml, sizeof(fonttable_xml));
	zip.PutNextEntry(wxT("word/settings.xml"));
	zip.Write(settings_xml, sizeof(settings_xml));
	zip.PutNextEntry(wxT("word/styles.xml"));
	zip.Write(styles_xml, sizeof(styles_xml));
	zip.PutNextEntry(wxT("word/webSettings.xml"));
	zip.Write(websettings_xml, sizeof(websettings_xml));
	zip.PutNextEntry(wxT("word/_rels/document.xml.rels"));
	zip.Write(document_xml_rels, sizeof(document_xml_rels));
	SaveTheme(zip);
}

void ZIPTestMainFrame::SaveTheme(wxZipOutputStream & zip)
{
	wxFileInputStream theme(wxT("theme1.xml"));
	if(theme.IsOk())
	{
		zip.PutNextEntry(wxT("word/theme/theme1.xml"));
		zip.Write(theme);
	}
}

As you can see, ZIPTestMainFrame::SaveTheme loads “theme1.xml” file from disk and then saves it into ZIP archive. This way is used just as an example and in your applications you can also load “theme1.xml” from binary data.
Now we have load an image from disk and place it into archive. There is no need to load a bitmap from image file and save it manually. We just open wxFileInputStream and save it onto ZIP entry

ZIPTestMainFrame.cpp

void ZIPTestMainFrame::SaveImageFile(wxZipOutputStream & zip)
{
	if(!m_ImageFile.IsEmpty() && wxFileExists(m_ImageFile))
	{
		zip.PutNextEntry(wxString::Format(wxT("word/media/%s"),
			wxFileNameFromPath(m_ImageFile).GetData()));
		wxFileInputStream stream(m_ImageFile);
		zip.Write(stream);
	}
}

OK, now we have to perform the most complicated task: we have to generate the body of our report and place it into word/document.xml file inside our ZIP archive. If you open document.xml file in text editor then you will see that it is an XML document which describes the structure of Word document. All paragraphs, tables, images and other objects are described inside this file.
We can split document.xml in several logical parts, save them as separate text files, pack them into ZIP archive and load these parts in our application.
Creating nice reports in wxWidgets using ZPI API - 5
In this sample I replaced all textual values with macros and then used these macros inside my application for populating the report.
Creating nice reports in wxWidgets using ZPI API - 6
E.g. “report_name.txt” file contains %REPORTNAME% macro which should be replaced by some text when generating the report.
Now, after we packed all parts inside ZIP archive, we can load them when application starts…

ZIPTestMainFrame.cpp

bool ZIPTestMainFrame::Create(wxWindow * parent,
	wxWindowID id, const wxString & title)
{
	bool res = wxFrame::Create(parent, id, title,
		wxDefaultPosition, wxSize(500,400));
	if(res)
	{
		LoadDocumentTemplate();
		CreateControls();
	}
	return res;
}

void ZIPTestMainFrame::LoadDocumentTemplate()
{
	wxFileSystem fs;
	wxString wildcard = wxT("document.template.dat#zip:*.txt");
	wxString filename;
	wxFSFile * file(NULL);
	m_DocumentTemplate.SetCount(DOCUMENT_PARTS_COUNT);
	for(filename = fs.FindFirst(wildcard, wxFILE);
			!filename.IsEmpty(); filename = fs.FindNext())
	{
		if(LoadTemplatePart(fs, filename,
			wxT("document_start.txt"), DOCUMENT_START)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("document_end.txt"), DOCUMENT_END)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("report_name.txt"), REPORT_NAME)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("report_description.txt"), REPORT_DESCRIPTION)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("table_start.txt"), TABLE_START)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("table_end.txt"), TABLE_END)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("table_header_start.txt"), TABLE_HEADER_START)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("table_header_cell.txt"), TABLE_HEADER_CELL)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("table_header_end.txt"), TABLE_HEADER_END)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("table_row_start.txt"), TABLE_ROW_START)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("table_row_cell.txt"), TABLE_ROW_CELL)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("table_row_end.txt"), TABLE_ROW_END)) continue;
		if(LoadTemplatePart(fs, filename,
			wxT("image.txt"), DOCUMENT_IMAGE)) continue;
	}
}

bool ZIPTestMainFrame::LoadTemplatePart(wxFileSystem & fs,
		const wxString & filename, const wxString & part_name,
		DocumentTemplateParts part_id)
{
	if(filename.EndsWith(part_name))
	{
		wxFSFile * file = fs.OpenFile(filename);
		if(file)
		{
			wxTextInputStream stream(*file->GetStream());
			wxChar ch;
			while((ch = stream.GetChar()))
			{
				m_DocumentTemplate[part_id] += ch;
			}
			wxDELETE(file);
			return true;
		}
	}
	return false;
}

…and then generate document.xml file using this parts
ZIPTestMainFrame.cpp

void ZIPTestMainFrame::SaveDocumentBody(wxZipOutputStream & zip)
{
	do
	{
		if(m_DocumentTemplate.Count() != DOCUMENT_PARTS_COUNT) break;
		int i, j;
		wxString part;
		zip.PutNextEntry(wxT("word/document.xml"));
		wxCharBuffer buffer;

		part = m_DocumentTemplate[DOCUMENT_START];
		buffer = wxConvertWX2MB(part);
		zip.Write(buffer.data(), strlen(buffer.data()));

		part = m_DocumentTemplate[REPORT_NAME];
		part.Replace(wxT("%REPORTNAME%"), _("Sample Report"));
		buffer = wxConvertWX2MB(part);
		zip.Write(buffer.data(), strlen(buffer.data()));

		part = m_DocumentTemplate[REPORT_DESCRIPTION];
		part.Replace(wxT("%DESCRIPTION%"), m_Editor->GetValue());
		buffer = wxConvertWX2MB(part);
		zip.Write(buffer.data(), strlen(buffer.data()));

		part = m_DocumentTemplate[TABLE_START];
		buffer = wxConvertWX2MB(part);
		zip.Write(buffer.data(), strlen(buffer.data()));

		part = m_DocumentTemplate[TABLE_HEADER_START];
		buffer = wxConvertWX2MB(part);
		zip.Write(buffer.data(), strlen(buffer.data()));

		for(i = 0; i < m_Grid->GetNumberCols(); i++)
		{
			part = m_DocumentTemplate[TABLE_HEADER_CELL];
			part.Replace(wxT("%HEADERCELL%"), m_Grid->GetColLabelValue(i));
			buffer = wxConvertWX2MB(part);
			zip.Write(buffer.data(), strlen(buffer.data()));
		}

		part = m_DocumentTemplate[TABLE_HEADER_END];
		buffer = wxConvertWX2MB(part);
		zip.Write(buffer.data(), strlen(buffer.data()));

		for(i = 0; i < m_Grid->GetNumberRows(); i++)
		{
			part = m_DocumentTemplate[TABLE_ROW_START];
			buffer = wxConvertWX2MB(part);
			zip.Write(buffer.data(), strlen(buffer.data()));

			for(j = 0; j < m_Grid->GetNumberCols(); j++)
			{
				part = m_DocumentTemplate[TABLE_ROW_CELL];
				part.Replace(wxT("%CELL%"), m_Grid->GetCellValue(i, j));
				buffer = wxConvertWX2MB(part);
				zip.Write(buffer.data(), strlen(buffer.data()));
			}

			part = m_DocumentTemplate[TABLE_ROW_END];
			buffer = wxConvertWX2MB(part);
			zip.Write(buffer.data(), strlen(buffer.data()));
		}
		part = m_DocumentTemplate[TABLE_END];
		buffer = wxConvertWX2MB(part);
		zip.Write(buffer.data(), strlen(buffer.data()));

		if(!m_ImageFile.IsEmpty() && wxFileExists(m_ImageFile))
		{
			part = m_DocumentTemplate[DOCUMENT_IMAGE];
			part.Replace(wxT("%IMAGENAME%"), wxT("Sample Image"));
			part.Replace(wxT("%IMAGEDESCR%"), wxT("Some description"));
			part.Replace(wxT("%IMAGEFILE%"), wxFileNameFromPath(m_ImageFile));
			buffer = wxConvertWX2MB(part);
			zip.Write(buffer.data(), strlen(buffer.data()));
		}
		part = m_DocumentTemplate[DOCUMENT_END];
		buffer = wxConvertWX2MB(part);
		zip.Write(buffer.data(), strlen(buffer.data()));
	}
	while(false);
}

OK, now we have to call all methods which save the parts of our report from event handler for wxID_SAVE command:
ZIPTestMainFrame.cpp

void ZIPTestMainFrame::OnSave(wxCommandEvent & event)
{
	wxFileDialog dlg(this, wxFileSelectorPromptStr,
		wxEmptyString, wxEmptyString,
		_("Microsoft Word 2007 Documents (*.docx)|*.docx"),
		wxFD_SAVE);
	if(dlg.ShowModal() == wxID_OK)
	{
		TransferDataFromWindow();
		m_ImageFile = m_ImageFilePicker->GetPath();
		wxFFileOutputStream out(dlg.GetPath());
		wxZipOutputStream zip(out);
		SaveContentTypesXML(zip);
		SaveDocumentRels(zip);
		SaveDocProps(zip);
		SaveDocumentSettings(zip);
		SaveImageFile(zip);
		SaveDocumentBody(zip);
	}
}

Well, it seems that we finished implementing all functionality and can test the application.

  • Enter some text into “Description” filed
  • Add few rows to the grid and enter some text into the cells
  • Load an image file
  • Select “File” -> “Save”, enter the file name and press OK

Creating nice reports in wxWidgets using ZPI API - 7
Under Linux, our main frame should look like this:
Creating nice reports in wxWidgets using ZPI API - 8
Now, if we open the file, generated by our application, in Microsoft Word, we will see something like this:
Creating nice reports in wxWidgets using ZPI API - 9
The most important thing is that we can create Microsoft Word documents without Microsoft Office installed on local machine. Certainly if we have no Microsoft Office installed then we have no way to see the result but in any case we have the portable way of generating nice reports. It works under Linux and, I’m sure, that it should work on mobile devices.
Download sample source code for this article (VC++ 2005, CodeBlocks – Windows) (Eclipse – Linux)
Source code of Bin2C utility.


May 27th, 2007 |

Tags: Articles, Reports, wxWidgets, wxZipInputStream, wxZipOutputStream, XML, Статьи

Related Posts

  • wxJSON Tutorial – Part II – Сonfiguration File
  • wxJSON Tutorial – Part III – Describing a Table
  • wxJSON Tutorial – Part I – Introduction
  • Перевод книги Julian’а Smart’а – Глава XIV – Файлы и потоки
  • wxJSON Tutorial – Part IV – Using Comment Lines in wxJSON

Leave a Reply

  • This blog is about…

    Библиотека Книги Статьи Articles Code::Blocks Components Controls Database DatabaseLayer Document/View Eclipse Localization NetBeans News Printing Reports SQLite Tutorilas Video Visual Studio wxAUI wxButton wxDev-CPP wxGrid wxHelpController wxJavaScript wxJSON wxLocale wxLog wxPaintDC wxPropertyGrid wxSQLite3 wxThread wxValidator wxWidgets wxWinCE wxZipInputStream wxZipOutputStream XML
  • Showcase

    Visit wxToolBox Homepage

    Buy wxToolBox (with source code)

  • Archives

    • May 2009
    • April 2009
    • March 2009
    • February 2009
    • January 2009
    • December 2008
    • September 2008
    • August 2008
    • July 2008
    • June 2008
    • May 2008
    • April 2008
    • March 2008
    • February 2008
    • January 2008
    • December 2007
    • June 2007
    • May 2007
    • January 2007
  • Recent Comments

    • T-Rex on Taking Screenshots with wxWidgets under Mac OS is Really Tricky.
    • al.zatv on Taking Screenshots with wxWidgets under Mac OS is Really Tricky.
    • T-Rex on wxWidgets-2.8 and Code::Blocks (Windows)
    • T-Rex on Taking Screenshots with wxWidgets under Mac OS is Really Tricky.
    • FirE on wxWidgets-2.8 and Code::Blocks (Windows)
  • Blogroll

    • In C++
  • Buttons

    Locations of visitors to this page

    Rambler's Top100

    Рейтинг@Mail.ru
    Blog Flux Directory

Copyright © 2009 Cross-Platform Programming with wxWidgets All Rights Reserved
RSS XHTML CSS Log in