/*
i-net software provides programming examples for illustration only, without warranty
either expressed or implied, including, but not limited to, the implied warranties
of merchantability and/or fitness for a particular purpose. This programming example
assumes that you are familiar with the programming language being demonstrated and
the tools used to create and debug procedures. i-net software support professionals
can help explain the functionality of a particular procedure, but they will not modify
these examples to provide added functionality or construct procedures to meet your
specific needs.
� i-net software 1998-2013
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Inet.Viewer.Resources;
using System.Diagnostics;
namespace Inet.Viewer.Data
{
///
/// Progress class for the export. This class sends the export request to the Engine, receives the chunks and saves it into one or more files.
/// Also, it displays the status of the export in the progress and status bar.
///
///
public class ExportProgress : Progress
{
private const string PropFile = "file";
private const char SlashChar = '/';
private const char DotChar = '.';
///
/// The original report data instance. May not be used directly, instead should be used.
///
private readonly IRenderData originalData;
// Connection to the exported report
private IRenderData copiedData;
// Paramters that have been set in the Export Dialog
private Dictionary exportParameter;
// Buffer to read the chunks
private byte[] chunkBuffer;
private int chunkBufferIndex;
private int chunkBufferSize;
private List fileNames;
///
/// Creates a new export progress and set it to indeterminate. This method use the default value for all not specified properties.
/// For a list of properties see interface or the
/// i-net Clear Reports documentation at: https://docs.inetsoftware.de/reporting/help/report-url-parameters.
///
/// IReportView, used for showError
/// IReportData for the report that will be exported
/// Properties specified in the export dialog or with API
///
///
public ExportProgress(Action parent, IRenderData data, Dictionary parameters)
: base(parent, Progress.ProgressType.Export)
{
// First set Progressbar with unknown max-value (Indeterminate=true)
// When amount of chunks is available switch Progressbar to know max value (Indeterminate=false)
this.Indeterminate = true;
// we used to copy here, but this can take long in situations like Ad-Hoc where we are working with a serialized remote ReportData
// so instead we now copy lazily
this.originalData = data;
this.exportParameter = parameters;
this.ProgressMode = Data.ProgressMode.Continuous;
}
///
///
///
public override string Name
{
get
{
return strings.ProgressExport;
}
}
///
/// Stops this export progress and stops the rendering process (export) for this report.
public override void Cancel()
{
ReportDataCopy.Stop();
Status = Progress.ProgressStatus.Canceled;
}
///
/// FOR INTERNAL USE ONLY
///
/// Gets ExportChunkCount and all Chunks from server and saves them accoring to the format and depending on the settings of the format
///
protected override void Run()
{
OnProgressChanged();
fileNames = new List();
FileInfo exportFile; // file that will be created for the xport. For HTML (if layout was set) and for SVG this is only the first file
string fileName; // path for the to be created (for all formats). For HTML and SVG includingt the sub-directories for more files
try
{
IRenderData data = ReportDataCopy;
string format = exportParameter[URLRenderData.ParameterExportFmt];
// get amount of chunks. For this the report might need to be rendered on the server it is not in its cache.
int exportCount = data.GetExportChunkCount(exportParameter);
if (Status == Progress.ProgressStatus.Canceled)
{
return;
}
// change status of the ProgressBar, as now the max value is available
Indeterminate = false;
// if length is unknown, remove more chunks until the stream is finished
if (exportCount == 0)
{
this.ProgressMode = Data.ProgressMode.Continuous;
exportCount = int.MaxValue;
}
else
{
this.ProgressMode = Data.ProgressMode.Steps;
}
// set total amount of steps for ProgressBar
TotalProgress = exportCount;
fileName = exportParameter[PropFile];
if (format.StartsWith("htm") || format.StartsWith("svg"))
{
// HTML / SVG Export
exportFile = ExportMultiFile(format, fileName);
}
else if (format.Equals(ReportInfo.FormatJPG) || format.Equals(ReportInfo.FormatGIF) || format.Equals(ReportInfo.FormatPNG) || format.Equals(ReportInfo.FormatBMP))
{
exportFile = ExportFilePerPage(fileName, exportCount);
}
else
{
// for all other formats (e.g. pdf, rtf, ...)
exportFile = ExportOneFile(fileName, data, exportCount);
}
Status = Progress.ProgressStatus.Completed;
// Start application for choosen export format
if (exportParameter.ContainsKey("exportInApplication") && exportParameter["exportInApplication"] != null && exportParameter["exportInApplication"].Equals("true"))
{
if (exportFile != null)
{
ExportInApplication(exportFile);
}
}
}
catch (Exception t)
{
ViewerUtils.PrintStackTrace(t);
// Don't show error message if was canceled (e.g. after pressing the stop button)
if (Status == Progress.ProgressStatus.Canceled)
{
ViewerUtils.Log("Export: Export canceled by user");
}
else
{
ShowError(t);
Status = Progress.ProgressStatus.Error;
this.Cancel();
}
}
}
///
/// Get the generated file names on the export.
/// the file names after the run, else null
public virtual List FileNames
{
get
{
return fileNames;
}
}
///
/// Opens the exported file in a suitable application
///
/// the file that should be opened
private void ExportInApplication(FileInfo file)
{
try
{
System.Diagnostics.Process.Start(file.FullName);
}
catch (Exception)
{
try
{
Process.Start("rundll32.exe", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "shell32.dll") + ",OpenAs_RunDLL " + file.FullName);
}
catch (Exception e)
{
ShowError(new ViewerException(strings.Export_InApplicationFailed, e));
}
}
}
///
/// Read the next 4 bytes from idx on and return it as integer value
/// byte array to read from
/// index of the position the specified byte array
/// the read integer value
private int ReadInt(byte[] buffer, int idx)
{
return (buffer[idx + 0] & 0xFF) + ((buffer[idx + 1] & 0xFF) << 8) + ((buffer[idx + 2] & 0xFF) << 16) + (buffer[idx + 3] << 24);
}
///
/// Needed for ExportMultiFile to check if there are enough bytes left iin the local buffer.
/// If not get the next chunk
/// Amount of needed bytes
/// When set to true a ViewerException is thrown, if not enough data are available
/// Wenn alle Daten gefetcht und nicht gen�gend Daten vorhanden
/// true, if there are more data
private bool EnsureReadBufferSize(int needed, bool throwException)
{
int oldDataSize = chunkBufferSize - chunkBufferIndex;
if (oldDataSize < needed)
{
byte[] chunk = ReportDataCopy.NextExportChunk();
this.ProgressCount = this.ProgressCount + 1;
if (chunk == null)
{
if (throwException)
{
throw new ViewerException(strings.ErrorEexport_UnexpectedEnd);
}
else
{
return false;
}
}
if (chunkBuffer == null)
{
// first call
chunkBuffer = chunk;
chunkBufferIndex = 0;
chunkBufferSize = chunk.Length;
}
else
{
if (chunkBuffer.Length - oldDataSize < chunk.Length)
{
// increase buffer
byte[] newPuffer = new byte[oldDataSize + chunk.Length];
Array.Copy(chunkBuffer, chunkBufferIndex, newPuffer, 0, oldDataSize);
chunkBuffer = newPuffer;
}
else
{
Array.Copy(chunkBuffer, chunkBufferIndex, chunkBuffer, 0, oldDataSize);
}
chunkBufferIndex = 0;
chunkBufferSize = oldDataSize;
Array.Copy(chunk, 0, chunkBuffer, chunkBufferSize, chunk.Length);
chunkBufferSize += chunk.Length;
}
}
return true;
}
///
/// Export formats that contain only one file (e.g. pdf, rtf, ps)
///
/// the exported file name
/// the report data
/// the number of chunks
///
private FileInfo ExportOneFile(string fileName, IRenderData data, int exportCount)
{
FileInfo exportFile;
exportFile = new FileInfo(fileName);
fileNames.Add(exportFile.FullName);
FileStream fos = new FileStream(exportFile.FullName, FileMode.Create);
for (int i = 1; i <= exportCount; i++)
{
byte[] pageData = data.NextExportChunk();
if (pageData != null)
{
fos.Write(pageData, 0, pageData.Length);
}
else
{
break;
}
// number of the step that was processed for the ProgressBar
this.ProgressCount = i;
}
fos.Close();
return exportFile;
}
///
/// Exports formats that contain more than one file, e.g. export in HTML and SVG
///
/// the export format
/// path for the to exported files. For HTML und SVG it should also contain the name of the subdirectory for the files
///
/// First file that was created for HTML or SVG export
/// If an error with the creation of the file has occurred
private FileInfo ExportMultiFile(string format, string outputDir)
{
chunkBufferIndex = 0;
FileInfo firstFile = null;
FileStream fos;
string subDirName = string.Empty;
StringBuilder filePath;
bool directoryCreated = false;
bool firstFileCreated = false;
while (chunkBufferSize > chunkBufferIndex || TotalProgress > ProgressCount)
{
// Read filename length
EnsureReadBufferSize(4, true);
int length = ReadInt(chunkBuffer, chunkBufferIndex);
if (length == -1)
{
// eof
break;
}
if (length > 512)
{
// A problem occured if filename longer than 512
string text = System.Text.Encoding.UTF8.GetString(chunkBuffer, 0, chunkBufferSize);
throw new ViewerException(0, strings.ErrorMessage_Export_WrongData, format, null, null, null, 0, text);
}
chunkBufferIndex += 4; // set index to the beginning of the block, that contains the filename
// read filename
EnsureReadBufferSize(length, true);
// file name was encoded with UTF8, it need to be decoded here with UTF8
string filename = System.Text.Encoding.UTF8.GetString(chunkBuffer, chunkBufferIndex, length);
if (!firstFileCreated)
{
string layout = null;
exportParameter.TryGetValue("layout", out layout);
if (layout == null)
{
outputDir = outputDir.Substring(0, outputDir.LastIndexOf(SlashChar) + 1) + filename;
}
DirectoryInfo dir = new DirectoryInfo(outputDir);
dir.Parent.Create();
firstFile = new FileInfo(outputDir);
fileNames.Add(firstFile.FullName);
fos = new FileStream(firstFile.FullName, FileMode.Create);
// create directory with the name of the first html file (without extensions)
subDirName = filename.Substring(0, filename.LastIndexOf(DotChar));
firstFileCreated = true;
}
else
{
// all other files in subdirectory
filePath = new StringBuilder(outputDir.Substring(0, outputDir.LastIndexOf(SlashChar) + 1));
filePath.Append(subDirName);
if (!directoryCreated)
{
Directory.CreateDirectory(filePath.ToString());
directoryCreated = true;
}
filePath.Append(SlashChar);
filePath.Append(filename);
string strFilePath = filePath.ToString();
fileNames.Add(strFilePath);
fos = new FileStream(strFilePath, FileMode.Create);
}
chunkBufferIndex += length; // set index to the beginning of the block which contains the length of the file content
// read file length
EnsureReadBufferSize(4, true);
length = ReadInt(chunkBuffer, chunkBufferIndex);
chunkBufferIndex += 4; // set index to the beginning of the block which contains the file content
// Write file
while (length > 0)
{
EnsureReadBufferSize(1, true); // There must be at least one byte in the buffer to avoid a endless loop
int available = Math.Min(chunkBufferSize - chunkBufferIndex, length);
fos.Write(chunkBuffer, chunkBufferIndex, available);
chunkBufferIndex += available;
length -= available;
}
fos.Close();
}
return firstFile;
}
///
/// Export for formats where for each page a file will be created like the Image Renderer.
/// name of the output file to export
/// amount of chunks from IReportData
/// first file that was created for html or svg export
/// If an error with the creation of the file has occurred
///
private FileInfo ExportFilePerPage(string fileName, int exportCount)
{
IRenderData data = ReportDataCopy;
byte[] pageData = data.NextExportChunk();
if (pageData != null && pageData.Length > 2 && pageData[0] == 'P' && pageData[1] == 'K' && !fileName.ToLower().EndsWith(".zip"))
{
string zipFileName = fileName + ".zip";
if (File.Exists(zipFileName))
{
int c = 1;
do
{
zipFileName = fileName + "(" + (c++) + ").zip";
}
while (File.Exists(zipFileName));
}
fileName = zipFileName;
}
FileInfo exportFile = new FileInfo(fileName);
fileNames.Add(exportFile.FullName);
FileStream output = new FileStream(exportFile.FullName, FileMode.Create);
for (int i = 1; i <= exportCount; i++)
{
if (pageData == null)
{
break;
}
output.Write(pageData, 0, pageData.Length);
this.ProgressCount++;
if (i >= exportCount)
{
break;
}
pageData = data.NextExportChunk();
}
output.Close();
return exportFile;
}
///
/// Gets the ReportData copy (copy so we can do more than one export at the same time ).
/// synchronized so we never create more than the one copy for this instance
/// the copy of the IReportData we are using
private IRenderData ReportDataCopy
{
get
{
if (copiedData == null)
{
copiedData = (IRenderData)originalData.Clone();
}
return copiedData;
}
}
///
/// Converts the ExportFormat enm into strings for the paramter url.
///
/// the export format
/// the export format as string
public static string ExportFormatToString(ExportFormat format)
{
switch (format)
{
case ExportFormat.BMP:
return "bmp";
case ExportFormat.CSV:
return "csv";
case ExportFormat.DATA:
return "csv";
case ExportFormat.GIF:
return "gif";
case ExportFormat.HTML:
return "htm";
case ExportFormat.JPG:
return "jpg";
case ExportFormat.ODS:
return "ods";
case ExportFormat.PDF:
return "pdf";
case ExportFormat.PNG:
return "png";
case ExportFormat.PS:
return "ps";
case ExportFormat.PS2:
return "ps2";
case ExportFormat.PS3:
return "ps3";
case ExportFormat.RTF:
return "rtf";
case ExportFormat.SVG:
return "svg";
case ExportFormat.TXT:
return "txt";
case ExportFormat.XLS:
return "xls";
case ExportFormat.XML:
return "xml";
case ExportFormat.None:
return string.Empty;
default:
return null;
}
}
}
}