I have a task where I need to merge more than 2 cells, with below code I am able to merge only 2 cells in the table header under word document.
var tc = new TableCell();
Text header = new Text("");
if (j == 0)
{
header = new Text("Header1");
tc.Append(new TableCellProperties(
new TableCellWidth() { Type = TableWidthUnitValues.Dxa, Width = "1620" },
new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }));
}
else if (j == 1)
{
header = new Text("");
tc.Append(new TableCellProperties(
new TableCellWidth() { Type = TableWidthUnitValues.Dxa, Width = "0" }));
}
else if (j == 2)
{
header = new Text("");
tc.Append(new TableCellProperties(
new TableCellWidth() { Type = TableWidthUnitValues.Dxa, Width = "0" }));
}
else if (j == 3)
{
header = new Text("");
tc.Append(new TableCellProperties(
new TableCellWidth() { Type = TableWidthUnitValues.Dxa, Width = "0" }));
}
else if (j == 4)
{
header = new Text("Header2");
tc.Append(new TableCellProperties(
new TableCellWidth() { Type = TableWidthUnitValues.Dxa, Width = "1076" },
new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }));
}
else if (j == 5)
{
header = new Text("Header3");
tc.Append(new TableCellProperties(
new TableCellWidth() { Type = TableWidthUnitValues.Dxa, Width = "1004" },
new TableCellVerticalAlignment() { Val = TableVerticalAlignmentValues.Center }));
}
Run runHeaderRun = new Run();
runHeaderRun.Append(runHeader);
runHeaderRun.Append(header);
paraHeader.Append(runHeaderRun);
tc.Append(paraHeader);
if (j == 0 || j == 2)
{
tc.TableCellProperties = new TableCellProperties();
tc.TableCellProperties.HorizontalMerge = new HorizontalMerge { Val = MergedCellValues.Restart };
}
else if (j == 1 || j == 3)
{
tc.TableCellProperties = new TableCellProperties();
tc.TableCellProperties.HorizontalMerge = new HorizontalMerge { Val = MergedCellValues.Continue };
}
headerRow.Append(tc);
table.Append(headerRow);
I get a result like this:
But I need it like this:
Although the code you posted does not compile, it looks like the problem is here:
if (j == 0 || j == 2)
{
tc.TableCellProperties = new TableCellProperties();
tc.TableCellProperties.HorizontalMerge = new HorizontalMerge { Val = MergedCellValues.Restart };
}
else if (j == 1 || j == 3)
{
tc.TableCellProperties = new TableCellProperties();
tc.TableCellProperties.HorizontalMerge = new HorizontalMerge { Val = MergedCellValues.Continue };
}
I'm assuming that j is a loop index for the columns. Your code says to start a new horizontal merge on column 0 and continue it to column 1, then restart the merge on column 2 and continue it to column 3. This is exactly what we see in the output image you provided. If you want all four cells to be merged, then the first cell should have MergedCellValues.Restart and the other three should have MergedCellValues.Continue.
Also I notice that you are creating a brand new TableCellProperties to set the HorizontalMerge in, rather than adding it to the TableCellProperties you created earlier in your code for those same cells. So that means that the properties you set earlier, like the TableCellVerticalAlignment, for example, will be lost for those cells.
I think if you change the above section of code to the following, it will fix both problems:
if (j < 4)
{
var merge = j == 0 ? MergedCellValues.Restart : MergedCellValues.Continue;
tc.TableCellProperties.HorizontalMerge = new HorizontalMerge { Val = merge };
}
Related
I am trying to create otp view where each item is individually editable, problem arises when user types fast, the input is skipped. I believe there is some delay in focus manager. Below is the code, let me know if there is any issues in the snippet itself
#Composable
#Preview
fun OtpLayout(modifier: Modifier = Modifier, otpLength: Int = 6) {
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = modifier.fillMaxWidth()) {
val focusManager = LocalFocusManager.current
val list = remember {
List(6) {
mutableStateOf(TextFieldValue(text = "", selection = TextRange(0)))
}
}
repeat(otpLength) { index ->
key(index) {
BasicTextField(
modifier = Modifier.onKeyEvent {
if (it.key == Key.Backspace && index > 0 && list[index].value.text.isEmpty()) {
focusManager.moveFocus(FocusDirection.Left)
}
true
},
maxLines = 1,
value = list[index].value,
textStyle = TextStyle(color = Color.White, fontSize = 16.sp),
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
onValueChange = { value ->
Log.e("OnValue Changed called", value.text)
if (value.text.length <= 1 && value.text.isDigitsOnly()) {
list[index].value = value
if (value.text != "" && index < otpLength - 1) {
focusManager.moveFocus(FocusDirection.Right)
} else if (value.text == "" && index > 0) {
focusManager.moveFocus(FocusDirection.Left)
}
} else if (list[index].value.text.length == 1 && index < otpLength - 1) {
list[index + 1].value = value.copy(text = value.text.last().toString())
focusManager.moveFocus(FocusDirection.Right)
}
},
decorationBox = { innerTextField ->
Box(contentAlignment = Alignment.Center,
modifier = Modifier
.size(width = 42.dp, height = 52.dp)
.border(width = 1.dp,
color = PrimaryColor,
shape = RoundedCornerShape(6.dp))
.background(color = Gray, shape = RoundedCornerShape(6.dp))) {
innerTextField()
}
}
)
}
}
}
}
I want to change this
var cell = {
v: value,
s: {alignment: {horizontal:"left"}}
};
to add hyperlink formula on 'value' like this
`{ formula:'HYPERLINK("' + mainhyperlinkurl + '","Download.Zip")' };`
In the below addcell function code sample
function addCell(range, value, row, col, ws, isHeader) {
if (range.s.r > row) range.s.r = row;
if (range.s.c > col) range.s.c = col;
if (range.e.r < row) range.e.r = row;
if (range.e.c < col) range.e.c = col;
var cell = {
v: value,
s: {alignment: {horizontal:"left"}}
};
if (cell.v == null) cell.v = '-';
var cell_ref = XLSX.utils.encode_cell({
c: col,
r: row
});
//if (typeof cell.v === 'number') cell.t = 'n';
//if (!isNaN(cell.v)) cell.t = 'n';
//else
if (typeof cell.v === 'boolean') cell.t = 'b';
else if (cell.v instanceof Date) {
cell.t = 'n';
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
} else cell.t = 's';
if(isHeader){
cell.s = {
font: {
bold:isHeader
}
};
}
ws[cell_ref] = cell;
}
waiting for your response, Thanks.
I've been fiddling around with the jpeg-js module and Node JS Buffer, and attempting to create a small command line program that modifies the decoded JPEG buffer data and creates a pattern of X number of reversed scanlines and X number of normal scanlines before saving a new JPEG. In other words, I'm looking to flip portions of the image, but not the entire image itself (plenty of modules that do such a thing, of course, but not the specific use case I have).
To create the reversed/normal line patterns, I've been reading/writing line by line, and saving a slice of that line to a variable, then starting at the end of scanline and incrementally going down by slices of 4 bytes (the alloc for an RGBA value) until I'm at the beginning of the line. Code for the program:
'use strict';
const fs = require('fs');
const jpeg = require('jpeg-js');
const getPixels = require('get-pixels');
let a = fs.readFileSync('./IMG_0006_2.jpg');
let d = Buffer.allocUnsafe(a.width * a.height * 4);
let c = jpeg.decode(a);
let val = false; // track whether normal or reversed scanlines
let lineWidth = b.width * 4;
let lineCount = 0;
let track = 0;
let track2 = 0;
let track3 = 0;
let curr, currLine; // storage for writing/reading scnalines, respectively
let limit = {
one: Math.floor(Math.random() * 141),
two: Math.floor(Math.random() * 151),
three: Math.floor(Math.random() * 121)
};
if (limit.one < 30) {
limit.one = 30;
}
if (limit.two < 40) {
limit.two = 40;
}
if (limit.two < 20) {
limit.two = 20;
}
let calc = {};
calc.floor = 0;
calc.ceil = 0 + lineWidth;
d.forEach(function(item, i) {
if (i % lineWidth === 0) {
lineCount++;
/* // alternate scanline type, currently disabled to figure out how to succesfully reverse image
if (lineCount > 1 && lineCount % limit.one === 0) {
// val = !val;
}
*/
if (lineCount === 1) {
val = !val; // setting alt scanline check to true initially
} else if (calc.floor + lineWidth < b.data.length - 1) {
calc.floor += lineWidth;
calc.ceil += lineWidth;
}
currLine = c.data.slice(calc.floor, calc.ceil); // current line
track = val ? lineWidth : 0; // tracking variable for reading from scanline
track2 = val ? 4 : 0; // tracking variable for writing from scanline
}
//check if reversed and writing variable has written 4 bytes for RGBA
//if so, set writing source to 4 bytes at end of line and read from there incrementally
if (val && track2 === 4) {
track2 = 0; // reset writing count
curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
} else {
curr = currLine; //set normal scanline
}
d[i] = curr[track2];
// check if there is no match between data source and decoded image
if (d[i] !== curr[track2]) {
if (track3 < 50) {
console.log(i);
}
track3++;
}
track2++; //update tracking variable
track = val ? track - 1 : track + 1; //update tracking variable
});
var rawImageData = {
data: d,
width: b.width,
height: b.height
};
console.log(b.data.length);
console.log('errors\t', track3);
var jpegImageData = jpeg.encode(rawImageData, 100);
fs.writeFile('foo2223.jpg', jpegImageData.data);
Alas, the reversed scanline code I've written does not properly. Unfortunately, I've only been able successfully reverse the red channel of my test image (see below left), with the blue and green channels just turning into vague blurs. The color scheme should look something like the right image.
What am I doing wrong here?
For reversed lines, you stored slices of 4 bytes(4 bytes = 1 pixel), then write the first value of the pixel(red) correctly.
But in the next iteration, you overwrite the slice curr with currLine, rest of channels gets wrong values.
if (val && track2 === 4) {
track2 = 0; // reset writing count
curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
} else {
curr = currLine; //set normal scanline
}
Iteration 0: val == true, track2 == 4, set curr to next pixel, write red channel.
Iteration 1: val == true, track2 == 1, (val && track2 === 4) == false, set curr to currLine, write green channel.
You can move track2 === 4 branch to avoid this:
if (val) {
if (track2 === 4) {
track2 = 0; // reset writing count
curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
}
} else {
curr = currLine; //set normal scanline
}
Fixed code should look like this:
function flipAlt(input, output) {
const fs = require('fs');
const jpeg = require('jpeg-js');
let a = fs.readFileSync(input);
let b = jpeg.decode(a);
let d = Buffer.allocUnsafe(b.width * b.height * 4);
let val = false; // track whether normal or reversed scanlines
let lineWidth = b.width * 4;
let lineCount = 0;
let track = 0;
let track2 = 0;
let track3 = 0;
let curr, currLine; // storage for writing/reading scnalines, respectively
let limit = {
one: Math.floor(Math.random() * 141),
two: Math.floor(Math.random() * 151),
three: Math.floor(Math.random() * 121)
};
if (limit.one < 30) {
limit.one = 30;
}
if (limit.two < 40) {
limit.two = 40;
}
if (limit.two < 20) {
limit.two = 20;
}
let calc = {};
calc.floor = 0;
calc.ceil = 0 + lineWidth;
d.forEach(function(item, i) {
if (i % lineWidth === 0) {
lineCount++;
if (lineCount > 1) {
val = !val;
}
if (lineCount === 1) {
val = !val; // setting alt scanline check to true initially
} else if (calc.floor + lineWidth < b.data.length - 1) {
calc.floor += lineWidth;
calc.ceil += lineWidth;
}
currLine = b.data.slice(calc.floor, calc.ceil); // current line
track = val ? lineWidth : 0; // tracking variable for reading from scanline
track2 = val ? 4 : 0; // tracking variable for writing from scanline
}
//check if reversed and writing variable has written 4 bytes for RGBA
//if so, set writing source to 4 bytes at end of line and read from there incrementally
if (val) {
if (track2 === 4) {
track2 = 0; // reset writing count
curr = currLine.slice(track - 4, track); // store 4 previous bytes as writing source
if (lineCount === 1 && lineWidth - track < 30) console.log(curr); //debug
}
} else {
curr = currLine; //set normal scanline
}
d[i] = curr[track2];
// check if there is no match between data source and decoded image
if (d[i] !== curr[track2]) {
if (track3 < 50) {
console.log(i);
}
track3++;
}
track2++; //update tracking variable
track = val ? track - 1 : track + 1; //update tracking variable
});
var rawImageData = {
data: d,
width: b.width,
height: b.height
};
console.log(b.data.length);
console.log('errors\t', track3);
var jpegImageData = jpeg.encode(rawImageData, 100);
fs.writeFile(output, jpegImageData.data);
}
flipAlt('input.jpg', 'output.jpg');
Instead of tracking array indices, you can use utility library like lodash, it should make things easier:
function flipAlt(input, output) {
const fs = require('fs');
const jpeg = require('jpeg-js');
const _ = require('lodash');
const image = jpeg.decode(fs.readFileSync(input));
const lines = _.chunk(image.data, image.width*4);
const flipped = _.flatten(lines.map((line, index) => {
if (index % 2 != 0) {
return line;
}
const pixels = _.chunk(line, 4);
return _.flatten(pixels.reverse());
}));
const imageData = jpeg.encode({
width: image.width,
height: image.height,
data: new Buffer(flipped)
}, 100).data;
fs.writeFile(output, imageData);
}
flipAlt('input.jpg', 'output.jpg');
How to get the headers of the given Excel file in node xlsx (https://www.npmjs.com/package/xlsx) module?
Did the following in xlsx v0.16.9
const workbookHeaders = xlsx.readFile(filePath, { sheetRows: 1 });
const columnsArray = xlsx.utils.sheet_to_json(workbookHeaders.Sheets[sheetName], { header: 1 })[0];
As I could find, there's no exposed method to get headers of the Excel file from the module. So I copied few functions (With all respect to author. https://github.com/SheetJS/js-xlsx) from their source code and make that work with few changes.
function getHeaders(sheet){
var header=0, offset = 1;
var hdr=[];
var o = {};
if (sheet == null || sheet["!ref"] == null) return [];
var range = o.range !== undefined ? o.range : sheet["!ref"];
var r;
if (o.header === 1) header = 1;
else if (o.header === "A") header = 2;
else if (Array.isArray(o.header)) header = 3;
switch (typeof range) {
case 'string':
r = safe_decode_range(range);
break;
case 'number':
r = safe_decode_range(sheet["!ref"]);
r.s.r = range;
break;
default:
r = range;
}
if (header > 0) offset = 0;
var rr = XLSX.utils.encode_row(r.s.r);
var cols = new Array(r.e.c - r.s.c + 1);
for (var C = r.s.c; C <= r.e.c; ++C) {
cols[C] = XLSX.utils.encode_col(C);
var val = sheet[cols[C] + rr];
switch (header) {
case 1:
hdr.push(C);
break;
case 2:
hdr.push(cols[C]);
break;
case 3:
hdr.push(o.header[C - r.s.c]);
break;
default:
if (val === undefined) continue;
hdr.push(XLSX.utils.format_cell(val));
}
}
return hdr;
}
function safe_decode_range(range) {
var o = {s:{c:0,r:0},e:{c:0,r:0}};
var idx = 0, i = 0, cc = 0;
var len = range.length;
for(idx = 0; i < len; ++i) {
if((cc=range.charCodeAt(i)-64) < 1 || cc > 26) break;
idx = 26*idx + cc;
}
o.s.c = --idx;
for(idx = 0; i < len; ++i) {
if((cc=range.charCodeAt(i)-48) < 0 || cc > 9) break;
idx = 10*idx + cc;
}
o.s.r = --idx;
if(i === len || range.charCodeAt(++i) === 58) { o.e.c=o.s.c; o.e.r=o.s.r; return o; }
for(idx = 0; i != len; ++i) {
if((cc=range.charCodeAt(i)-64) < 1 || cc > 26) break;
idx = 26*idx + cc;
}
o.e.c = --idx;
for(idx = 0; i != len; ++i) {
if((cc=range.charCodeAt(i)-48) < 0 || cc > 9) break;
idx = 10*idx + cc;
}
o.e.r = --idx;
return o;
}
Call getHeaders function by passing the Work Sheet will return the headers array of the excel sheet.
Have a look here:
https://www.npmjs.com/package/xlsx if we go through the documentation. It says that we need to pass options By default, sheet_to_json scans the first row and uses the values as headers. With the header: 1 option, the function exports an array of arrays of values.
So the whole code goes like this:
const data = e.target.result;
const workbook = XLSX.read(data, { type: "array" });
console.log(workbook);
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const options = { header: 1 };
const sheetData2 = XLSX.utils.sheet_to_json(worksheet, options);
const header = sheetData2.shift();
console.log(header); //you should get your header right here
Where e.target comes from your input.
I am using C1Flexgrid and I need to make parent child relation in this grid. But child details need to show in same grid (no other grid ) and when I clicked on + expand should happen and vice versa.
I have written below code where I am having one column in datatable related to parent and child . If it is parent then I am making it 1 else 0.
When I tried with this code. R2 row is coming as child node of r which should not be a case as it is parent node.
Please help me on this .
private void Form3_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable("customers");
dt.Columns.Add("abc");
dt.Columns.Add("ddd");
dt.Columns.Add("eee");
dt.Columns.Add("parent");
var r = dt.NewRow();
r["abc"] = "11";
r["ddd"] = "12";
r["eee"] = "13";
r["parent"] = "1";
var r1 = dt.NewRow();
r1["ddd"] = "12";
r1["eee"] = "14";
r1["parent"] = "0";
var r2 = dt.NewRow();
r2["abc"] = "11";
r2["ddd"] = "1222";
r2["eee"] = "14";
r2["parent"] = "1";
var rr32 = dt.NewRow();
rr32["abc"] = "11";
rr32["ddd"] = "1222";
rr32["eee"] = "14";
rr32["parent"] = "0";
dt.Rows.Add(r);
dt.Rows.Add(r1);
dt.Rows.Add(r2);
dt.Rows.Add(rr32);
grid1.DataSource = dt;
GroupBy("parent", 1);
// show outline tree
grid1.Tree.Column = 2;
// autosize to accommodate tree
grid1.AutoSizeCol(grid1.Tree.Column);
grid1.Tree.Show(1);
}
void GroupBy(string columnName, int level)
{
object current = null;
for (int r = grid1.Rows.Fixed; r < grid1.Rows.Count; r++)
{
if (!grid1.Rows[r].IsNode)
{
var value = grid1[r, columnName];
string value2 = grid1[r, "parent"].ToString();
if (!object.Equals(value, current))
{
// value changed: insert node, apply style
if (value2.Equals("0"))
{
grid1.Rows.InsertNode(r, level);
grid1.Rows[r].Style = _nodeStyle[Math.Min(level, _nodeStyle.Length - 1)];
r++;
}
// show group name in first scrollable column
//grid1[r, grid1.Cols.Fixed+1] = value;
// update current value
current = value;
}
}
}
}
}
Your code was almost there, i have manipulated GroupBy method to fit your need. It solves your current requirement but you have to handle sorting and other functionalists of grid yourself.
Hope this helps!
void GroupBy(string columnName, int level)
{
object current = null;
for (int r = grid1.Rows.Fixed; r < grid1.Rows.Count; r++)
{
if (!grid1.Rows[r].IsNode)
{
var value = grid1[r, columnName];
if (!object.Equals(value, current))
{
// value changed: insert node, apply style
grid1.Rows.InsertNode(r, level);
grid1.Rows[r].Style = _nodeStyle[Math.Min(level, _nodeStyle.Length - 1)];
// show group name in first scrollable column
Row row = grid1.Rows[r + 1];
for (int i = 0; i < grid1.Cols.Count; i++)
{
grid1[r, i] = row[i];
}
grid1.Rows[r + 1].Visible = false;
r++;
// update current value
current = value;
}
}
}
}