xlsgen bug fixes

Generate Excel files. Speed. Flexibility. Depth. Robustness.

 

Subscribe our RSS feed

xlsgen 4.9.0.18 : Fix for pivot tables


Build 4.9.0.18 of xlsgen fixes a problem related to handling pivot table descriptors in files where both regular and extended descriptors are present, avoiding a file corruption problem.



Posted on 13-December-2021 23:19 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.17 : Fix pack for XLSB files


Build 4.9.0.17 of xlsgen is a fix pack for issues related to XLSB files.

- under certain circumstances, array formulas were not written correctly and would corrupt the Excel file (XLSB).

- incorrect external reference index when reading from a XLSB file and converting to a XLSX file

- generation of external reference cache when converting a XLSX file to a XLSB file

- internal formula building for a number of conditional formattings when converting a XLSB file to a XLSX file

Posted on 29-October-2021 13:00 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.16 : Other Lambda functions


Build 4.9.0.16 adds all other lambda functions introduced in Office 365 (reminder : xlsgen supports them, and they are not available in any installable Excel version other than the one associated to an Office 365 subscription).

BYROW()Applies a Lambda function to each row and returns an array of the results. For example, if the original array is 3 columns by 2 rows, the returned array is 1 column by 2 rows.
BYCOL()Applies a Lambda function to each column and returns an array of the results. For example, if the original array is 3 columns by 2 rows, the returned array is 3 columns by 1 row.
MAKEARRAY()Returns a calculated array of a specified row and column size, by applying a Lambda function.
SCAN()Scans an array by applying a Lambda function to each value and returns an array that has each intermediate value.
MAP()Returns an array formed by mapping each value in the array(s) to a new value by applying a Lambda function to create a new value.
REDUCE()Reduces an array to an accumulated value by applying a Lambda function to each value and returning the total value in the accumulator.
ISOMITTED()Checks whether the value in a Lambda function is missing and returns TRUE or FALSE.


Those functions basically iterate over the cells in a particular way, either cell by cell, row by row, and so on, and what they do is apply the lambda function on each cell value. The result is either written back to a new cell, with most functions above, or accumulated, as in functions SCAN() and REDUCE().


Example : =BYROW(A1:C2; LAMBDA(x;MAX(x)) )



Example : =BYCOL(A1:C2; LAMBDA(x;MAX(x)) )



Example : =MAKEARRAY(2; 3; LAMBDA(r; c; r*c) )



Example : =SCAN(""; A1:C2; LAMBDA(t; u; t&u) ). t is an accumulator with an initial value of "".



Example : =MAP(A1:C2; LAMBDA(a; IF(a>4;a*a;+a) ))



Example : =REDUCE(0; A1:C2; LAMBDA(t; u; IF(u>4; t+u; 0)) ). t is an accumulator with an initial value of 0.


As for the ISOMITTED() function, it works like this :

C++ code


xlsgen::IXlsWorksheetPtr worksheet = workbook->WorksheetByIndex[1];

// create a lambda function
xlsgen::IXlsDynamicRangePtr dynrange_xy = worksheet->NewDynamicRange(L"MyFuncXY");
dynrange_xy->Formula = L"=LAMBDA(x;y;IF(ISOMITTED(y);\"y param is omitted\";x&y))";
dynrange_xy->UserDefinedFunction = TRUE;
dynrange_xy->Description = L"(x,y) function";

worksheet->Formula[10][6] = L"=MyFuncXY(4;2)";
_bstr_t s106 = wksht->Label[10][6]; // returns "42"

worksheet->Formula[11][6] = L"=MyFuncXY(4)";
_bstr_t s116 = wksht->Label[11][6]; // returns "y param is omitted"




Posted on 12-October-2021 10:35 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.15 : Lambda function


Build 4.9.0.15 of xlsgen introduces Lambda functions, a mechanism in Excel that is meant to reduce the complexity of spreadsheets. It was introduced in 2021 as part of Office 365 subscriptions, is not available in any downloadable Excel version, but xlsgen supports it.

Usually, cell with calculations end up with multiple functions nested at multiple levels, making it hard to understand and maintain over time. The fix for this is usually to either factorize a static piece of it into a defined name, or to externalize it to a VBA macro function, both of which have limitations. The limitation of static defined names is that parameters can't be passed to the defined name so a dynamic call is impossible. A limitation which isn't in VBA macros, except that VBA is a different thing than Excel functions, a different language and that it's been years that running VBA macros has become a security problem.

So this lays down the path for something else, and that's lambda functions, that's how it is named in Excel. Lambda functions can be seen as a mechanism for overcoming the limit of not being able to pass arguments to defined names, while remaining in the Excel function context.

Introducing lambda functions is actually straight forward. Let's assume we have a mathematical function y = x + 3. We could specify this function like this as well : x ==> x + 3, or rather function(x; x+3). Back to Excel context, if we create a defined name that we shall call y, and whose definition is =lambda(x;x+3), that's all we need then to make a call anywhere in the spreadsheet of the form y(1) and it would return 4.

When we create that defined name, we can use the description to explain further details about what the function accomplishes, what the parameters are, etc. which carries a meaning to anyone who will be tasked to understand and maintain the spreadsheet over time.

Anywhere in the spreadsheet where this x + 3 calculation is stored in formulas, it can be replaced by the y(x) function call and doing so this removes complexity to formulas across the entire spreadsheet.

Here is another example. Assuming we have data layed down like this :



In C12, we have a calculation whose only point is to obtain the price of a coffee type of a given size. Allegedly, all what we want for this is a handy function of the form CalcCoffeePriceForSize(coffee_type; coffee_size), but in Excel we usually end up with a string of functions like this :

=VLOOKUP(A12;Table1_data;MATCH(B12;Table1_headers;0);FALSE)

where A12 is the coffee type and B12 the coffee size. The MATCH function is there to find the appropriate coffee size column, and the VLOOKUP finds the coffee price for the coffee type given its coffee size.
A string of Excel functions like this isn't particularly self-describing, and it would get far worse if we added error handling, etc.
And that's where the lambda function mechanism comes handy. Let's create a new defined name, called CalcCoffeePriceForSize, then paste the formula above in there.
Then edit this definition and replace it by :

=VLOOKUP(coffee_type;Table1_data;MATCH(coffee_size;Table1_headers;0);FALSE)

Then edit again and prefix the above with the =LAMBDA function call along with parameters :

=LAMBDA(coffee_type;coffee_size;VLOOKUP(coffee_type;Table1_data;MATCH(coffee_size;Table1_headers;0);FALSE))

Don't forget to describe this function and parameters in the Comment area. And click OK. Of course if you are doing this in Excel, this won't work unless a subscription-based Excel product is being used. In other words, it does not work with Excel 2016, Excel 2019 or Excel 2021.
What is being done here is pass the list of all function parameters, and then we have the calculation itself.



Now back in cell C12 where the calculation occurs, replace the previous string of formulas with :

=CalcCoffeePriceForSize(A12;B12)

That's it. Cell C12 is a calculated formula and the intention is self-describing. If the user needs to know more about the actual calculation, it's available, but not shown unless needed. Complexity has been lowered without compromising the maintainability of the spreadsheet.
So that's a brief introduction to lambda functions.

in xlsgen, creating the lambda function is done like this :

C++ code


xlsgen::IXlsWorksheetPtr worksheet = workbook->WorksheetByIndex[1];

// create a lambda function
xlsgen::IXlsDynamicRangePtr dynrange = worksheet->NewDynamicRange(L"CalcCoffeePriceForSize");
dynrange->Formula = L"=LAMBDA(coffee_type;coffee_size;VLOOKUP(coffee_type;Table1_data;MATCH(coffee_size;Table1_headers;0);FALSE))";
dynrange->UserDefinedFunction = TRUE;
dynrange->Description = L"CalcCoffeePriceForSize computes the price of a coffee for a given coffee type and a given coffee size.";

// use the lambda function
worksheet->Formula[12][3] = L"=CalcCoffeePriceForSize(A12;B12)";



Posted on 30-September-2021 15:48 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.14 : Fix for header/footer


Build 4.9.0.14 of xlsgen improves the positioning and sizing of sheet headers and footers in rendering scenarios (print, PDF, ...). This has particular effect when the content of such blocks is large.

Posted on 10-September-2021 12:14 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.13 : Fix pack


Build 4.9.0.13 of xlsgen is a fix pack :

- sheet header/footer : check that the length is no more than 255 characters (Excel limit).

- HTML import : fix for parser

- HTML import : import   cells differently, assumes no content

- HTML import : import XML-type markup in HTML streams

- HTML import : uninitialized pointer.

- HTML import : improved number mapping resolutions. For instance, a number candidate cannot be seen as an integer in Excel if it has more than 11 digits : imported as a string.

Posted on 09-September-2021 07:39 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.12 : Improved JSON, HTML and XML imports


Build 4.9.0.12 of xlsgen adds to JSON data import, HTML data import and XML data import the same properties added to CSV data import in the previous build. Namely,

- AutoFormatMapping (boolean) : enable or disable the automatic pattern matching algorithm. It needs to be disabled in cases where the algorithm tries hard too much finding patterns and ends up creating more number formats than Excel actually accepts (200).

- FirstRow : as the name implies this allows to tell at which first row the CSV content should be inserted. Before this, row could be specified on a per column basis, but this property is for all columns in a single statement.

- FirstColumn : same than FirstRow, except for columns.


Sample :


// importing a sample JSON file

xlsgen::IXlsWorksheetPtr worksheet = workbook->AddWorksheet(L"Sheet1");

xlsgen::IXlsJSONImportPtr json = worksheet->Import->JSON;

json->Options->AutoFormatMapping = FALSE;
json->Options->FirstRow = worksheet->HorizontalPane->SplitLimit; // insert the data right after the split

json->ImportFile(L"input\\jsonfile3.json");


Posted on 12-August-2021 17:42 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.11 : Improved CSV import


xlsgen 4.9.0.11 adds 3 new properties to CSV import :

- AutoFormatMapping (boolean) : enable or disable the automatic pattern matching algorithm. It needs to be disabled in cases where the algorithm tries hard too much finding patterns and ends up creating more number formats than Excel actually accepts (200).

- FirstRow : as the name implies this allows to tell at which first row the CSV content should be inserted. Before this, row could be specified on a per column basis, but this property is for all columns in a single statement.

- FirstColumn : same than FirstRow, except for columns.


Posted on 11-August-2021 09:23 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.10 : Memory performance improvements


Build 4.9.0.10 of xlsgen improves memory performance in the following scenario : whenever a scenario implies a massive iteration amount of any of the following operations : insert row/col, delete row/col, sort row/col, remove duplicates, xlsgen 4.9.0.10 makes sure there is less memory consumption and much less memory re-allocations, making all those operations scalable.


Posted on 16-May-2021 15:02 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.9 : Range fill


Filling cells is an extension of paste where a source area gets duplicated to a larger area multiple times both horizontally and vertically, hence the fill.


Creating a range fill


As an example, you could have content at row 3 that according to some scenario needs to be duplicated in rows 6 to 8. If the paste/pasteTo function is used, this needs iteration over rows 6 to 8. But using fill, a single statement will do :

C/C++ code


worksheet->Rows[L"3:3"]->Range->FillTo(worksheet, worksheet->Rows[L"6:8"]->Range, xlsgen::pasteoption_valuesandformattingandformulas);



Another example is range C3:F4 that gets used in order to fill range B5:Z8 of another worksheet :

C/C++ code


worksheet->NewRange(L"C3:F4")->FillTo(worksheet2, worksheet2->NewRange(L"B5:Z8"), xlsgen::pasteoption_valuesandformattingandformulas);



Posted on 02-May-2021 18:02 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.8 : Fix for pivot table report filters


Build 4.9.0.8 of xlsgen fixes a problem for generating custom report filters in pivot tables. The bug appeared in XLSX and XLSB files.

Posted on 18-April-2021 10:56 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.7 : Rust programming language


Build 4.9.0.7 of xlsgen adds support for Rust as a programming language.

xlsgen supports Rust as a programming language which allows to build rust applications of any size and complexity.


Rust in xlsgen
Rust is a functional language popularized in a number of environments and places, and it turns out that it can also make low-level calls. The xlsgen install package takes advantage of this and comes with an interface layer (called xlsgen.rs) which exposes the entire object model to Rust code environments. Both enumerated types and interfaces are available.

It is assumed that the basic Rust development or runtime environment is installed on the computer. The xlsgen interface layer does not use any particular feature of a recent Rust version, so most likely any Rust version of the language and environment is compatible with it.
In fact, what xlsgen relies on is the Rust winapi crate, which provides a number of Windows types and macros that make it easier to expose and handle.
Here is how a simple Rust program looks like in structure :

- src
|
|- main.rs (your code goes here)
|- xlsgen.rs (xlsgen interface layer (available from the xlsgen install package))
- Cargo.toml (known dependency declaration (winapi))


Here is how you build it :

cd \tmp\rust\simple
cargo build

Here is how you run it :

cd \tmp\rust\simple
cargo run


During the first run, the package manager will download a compatible version of the winapi crate in order to meet the dependency specifications, so the compuer where this runs must be online and ready to download stuff, even though this stuff has nothing to do with xlsgen.
xlsgen.rs on the other hand is part of the xlsgen install package, so it can be copied over in your source code folder structure. xlsgen.rs is computer-generated by a tool that was designed to convert a COM IDL object model to a Rust compatible object model.
Rust can execute low-level statements, but because of what they are those statements have to be enclosed inside an unsafe section.
Here is how you put a number in cell B4 :

let _hr = unsafe {
(*p_worksheet).put_float(4, 2, 4.5);
};

The return value of any call is a HRESULT, i.e. a value of zero means everything went fine, and you can look up winerr.h to find the list of existing HRESULT errors (for instance 0x80070057 = invalid parameter).
Before you can load xlsgen in memory, the COM library must be loaded, which means a paired CoInitialize/CoUninitalize call.
In Rust, all depending types must be declared, tha's why your typical application will include statements such as :

use winapi::{Class, Interface};

Last but not least, Rust macros can help group code patterns together. For instance, if you'd like to create a pointer to an object, initialize to null, you would declare a macro like this :

macro_rules! new_pointer {
($param:ident, $type:ty) => {let mut $param: *mut $type = unsafe { std::mem::zeroed() };}
}

And then you would use this macro like this :

new_pointer!(p_engine, xlsgen::IXlsEngine);

Here is a Rust sample application in full that creates a spreadsheet with two sheets, puts a couple numbers in it and reads back the content of a cell. It's available in the /samples folder of the xlsgen install :

use winapi::um::winnt::{LPWSTR};
use winapi::{Class, Interface};
use std::ffi::{OsStr, OsString};
use std::os::windows::ffi::OsStrExt;
use std::os::windows::ffi::OsStringExt;
use std::slice;

pub mod xlsgen;


// *** MACROS begin here ***

macro_rules! retVal {
($param:ident) => {&mut $param as *mut _ as *mut _}
}

macro_rules! new_pointer {
($param:ident, $type:ty) => {let mut $param: *mut $type = unsafe { std::mem::zeroed() };}
}

macro_rules! release_pointer {
($param:ident) => { unsafe { (&mut * $param).Release() }; }
}

macro_rules! release_bstr_string {
($param:ident) => { unsafe { winapi::um::oleauto::SysFreeString( $param as LPWSTR ) }; }
}


// *** MACROS end here ***

// unicode strings (Rust stores strings using UTF8 encoding, not UTF16) so we have to convert back and forth

fn to_wstring(string: &str) -> Vec<u16> {
let mut v: Vec<u16> = OsStr::new(string).encode_wide().collect();
v.push(0); // EOL
v
}

fn from_wstring(string: LPWSTR) -> String {
let string_slice = unsafe {
let len = winapi::um::oleauto::SysStringLen(string);
slice::from_raw_parts(string, len as usize)
};

OsString::from_wide(string_slice).into_string().unwrap()
}


fn main() {

println!("step1");

let _hr = unsafe { winapi::um::objbase::CoInitialize(std::ptr::null_mut()) };

println!("step2");

new_pointer!(p_engine, xlsgen::IXlsEngine);

let _hr = unsafe { winapi::um::combaseapi::CoCreateInstance(
&xlsgen::XlsEngine::uuidof(),
std::ptr::null_mut(),
winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER,
&xlsgen::IXlsEngine::uuidof(),
retVal!(p_engine))
};

println!("step3 {}", _hr);

new_pointer!(p_workbook, xlsgen::IXlsWorkbook);

let _hr = unsafe { (*p_engine).new(to_wstring("d:\\tmp\\eer.xlsx").as_ptr(), retVal!(p_workbook)) };

new_pointer!(p_worksheet1, xlsgen::IXlsWorksheet);

let _hr = unsafe { (*p_workbook).addworksheet(to_wstring("sheet 1").as_ptr(), retVal!(p_worksheet1)) };

new_pointer!(p_worksheet2, xlsgen::IXlsWorksheet);

let _hr = unsafe { (*p_workbook).addworksheet(to_wstring("sheet 2").as_ptr(), retVal!(p_worksheet2)) };

println!("step4");

let mut i_wksht_count : i32 = 0;

let _hr = unsafe { (*p_workbook).get_worksheetcount(&mut i_wksht_count) };

println!("step4a {}, wkshtCount = {}", _hr, i_wksht_count);

unsafe { (*p_workbook).put_factorizedstringsmode(true) };

let mut b_factorized : bool = false;

let _hr = unsafe { (*p_workbook).get_factorizedstringsmode(&mut b_factorized) };

println!("step4ab {}, bFactorized = {}", _hr, b_factorized);

// write some content
for r in 1..10 {
let _hr = unsafe { (*p_worksheet1).put_label(r, 2, to_wstring("hello world").as_ptr()) };
}

// read some content

println!("step4b");

new_pointer!(cell_string, LPWSTR);

println!("step4c");

let _hr = unsafe { (*p_worksheet1).get_label(3, 2, retVal!(cell_string)) };

println!("step5");

println!("cell value is : {}", from_wstring(cell_string as LPWSTR));

println!("step6");

new_pointer!(p_richlabel, xlsgen::IXlsRichLabel);

println!("step6b");

let _hr = unsafe { (*p_worksheet1).newrichlabel(retVal!(p_richlabel)) };

println!("step6c {}", _hr);

let _hr = unsafe { (*p_richlabel).put_htmllabel(to_wstring("<span>hello <b>world</b></span>").as_ptr()) };

println!("step6d");

let _hr = unsafe { (*p_worksheet1).put_richlabel(1, 4, p_richlabel) };

println!("step6e");

let _hr = unsafe { (*p_workbook).close(); };

println!("step7");

release_bstr_string!(cell_string);
release_pointer!(p_worksheet1);
release_pointer!(p_worksheet2);
release_pointer!(p_workbook);
release_pointer!(p_engine);

unsafe { winapi::um::combaseapi::CoUninitialize() };

println!("step8");

}


Here is an excerpt from xlsgen.rs :

(...)

RIDL!{#[uuid(0xD97500CF,0x37CC,0x48fd,0x87,0x72,0x0E,0xBC,0x8F,0x8A,0x93,0x76)]
interface IXlsEngine(IXlsEngineVtbl): IDispatch(IDispatchVtbl) {
fn new( // creates a new .xls/.xlsx file with the name (filename or filepath) passed in parameter.
excelfilename : LPCWSTR,
workbook : *mut *mut IXlsWorkbook,
) -> HRESULT,

(...)

RIDL!{#[uuid(0x12B7B224,0xC026,0x4eb6,0xBA,0x94,0xC4,0x9B,0x91,0x6F,0x77,0x40)]
interface IXlsWorkbook(IXlsWorkbookVtbl): IDispatch(IDispatchVtbl) {
fn addworksheet( // creates a new worksheet with the name passed in parameter. The following characters are forbidden : / \\ : * [ ], and the name must be less than 32 characters long.
name : LPCWSTR,
worksheet : *mut *mut IXlsWorksheet,
) -> HRESULT,

(...)

RIDL!{#[uuid(0xB150D9DA,0x1F10,0x4850,0x9A,0xBB,0x76,0xD9,0x95,0x47,0x96,0xEE)]
interface IXlsWorksheet(IXlsWorksheetVtbl): IDispatch(IDispatchVtbl) {
fn put_label( // puts a label in a cell.
row : i32,
col : i32,
label : LPCWSTR,
) -> HRESULT,



Posted on 17-April-2021 18:37 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.6 : Fix for relative cells (II)


Build 4.9.0.6 of xlsgen fixes another problem related to relative cells and inserting/deleting rows and columns. This time the problem could corrupt the Excel file without producing an error message, and was related to rebuilding internal shared formulas.

Posted on 07-April-2021 19:27 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.5 : Fix for relative cells


Build 4.9.0.5 of xlsgen fixes a problem related to manipulating rows/columns with formulas using partially relative rows or columns, such as R$170 (R is relative as a column, 170 is an absolute reference due to the presence of the $ prefix). Inserting, deleting rows or columns would corrupt such formulas. There was no problem with cell references either not relative at all (for instance $A$5), or both relative for rows and columns (for instance A5).

Posted on 24-March-2021 18:22 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.4 : Style optimisation


xlsgen 4.9.0.4 adds a new property to the xlsgen object model, to be used before loading files. This Style optimisation property allows to disable the style optimisation algorithm done by xlsgen on behalf of the client application, trying to remove useless styles. This algorithm is on by default and has been going on for years, it is nothing new. By setting this property as false, the algorithm is disabled, in order to meet scenarios where style optimization is not expected to occur (i.e. pure template scenarios).

engine.Workbooks.StyleOptimisation = False

engine.Open("mytemplatefile.xlsx", "")

...

Posted on 24-February-2021 12:48 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.3 : Elapsed time


Build 4.9.0.3 of xlsgen adds direct support for elapsed time, i.e. time used with either hour, minute or second enclosed in square brackets, as in [hh]:mm:ss in order to display out of boundaries numbers.

Using elapsed time, you can display with 30:00:00 the fact that you've had an event lasting 30 hours, notably bypassing the implicit limit of 24 hours. All it takes is to pass 30:00:00 as a date and apply a number format of the form [h]:mm:ss.

It works like this :

worksheet->Cell[2][1]->HtmlDate = L"<div format=\"[h]:mm:ss\">30:00:00</div>";


In Excel this is effectively stored as an elapsed duration of 30 hours, not a display artefact. Which means that formulas that depend on it can do their calculations properly.


Square brackets can apply to minutes or seconds as well, to represent an arbitrary duration in minutes or seconds.

This feature has been in Excel just about forever, but it was not added to xlsgen until now.

Posted on 26-January-2021 18:06 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.2 : Fix pack for memory leaks


Build 4.9.0.2 of xlsgen removes half a dozen memory leaks, in all parts of the library. This makes sustained scenarios better work.



Posted on 25-January-2021 15:30 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9.0.1 : Retargeting name scope


Build 4.9.0.1 of xlsgen makes it possible to retarget the scope of a defined name. Particularly make it local to a sheet. To that end, the Local property of existing named ranges can be set accordingly as in :


// let's say we have two sheets, Sheet1 and Sheet2

// also let's say range2 = Sheet2!R2C2:R20C5, and of global scope

workbook->WorksheetByName[L"Sheet1"]->DynamicRange[L"range2"]->Local = TRUE; // tell range2 to be local to Sheet2

Posted on 11-December-2020 09:02 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9 ships !


xlsgen 4.9 developer friendly edition ships !

Features, most notably :

- multithreaded calc engine
- PDF Unicode and font embedding
- SVG charts
- HTML markup for rich labels
- Rendering treemaps and sunbursts
- Advanced OpenOffice
- Performance improvements

Posted on 26-November-2020 06:27 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

xlsgen 4.9 preview #7 : Improved LibreOffice/OpenOffice


xlsgen 4.9 adds the following support to LibreOffice/OpenOffice files :

  • read existing pivot tables
  • write pivot charts (introduced in libre office 5.4.1)
  • read cell data validations
  • read auto filter and custom filters
  • improved writing of custom filters


Posted on 25-November-2020 11:38 | Category: xlsgen, Excel generator | Tags: generating Excel spreadsheets

 

 

<-- previous page