Migradoc: Avoid a page break in case of merged rows - pdf

When there isn't enough space left on a page, merged rows (cells) in table are placed into a new page.
How to prevent this and assure the table is filling the free space on the current page?
Section section = document.AddSection();
Table t5 = new Table();
t5.AddColumn(Unit.FromCentimeter(4));
t5.AddColumn(Unit.FromCentimeter(4));
Row first = t5.AddRow();
first.Cells[0].AddParagraph("Header 1");
first.Cells[1].AddParagraph("Header 2");
for (int j = 0; j < 4; j++)
{
var rowpd = t5.AddRow();
rowpd.VerticalAlignment = VerticalAlignment.Center;
rowpd.Cells[0].MergeDown = 17;
rowpd.Cells[0].AddParagraph("Merged 18 cells. ");
for (int i = 0; i < 18; i++)
{
if (i == 0)
{
rowpd.Cells[1].AddParagraph($"value {i}");
}
else
{
var row = t5.AddRow();
row.Cells[1].AddParagraph($"value {i}");
}
}
}
document.LastSection.Add(t5);

MigraDoc does not (yet) split cells, it only splits between cells. With MergeDown you create a huge cell that will not split.
Option: Avoid the MergeDown and use many small cells on the left column without horizontal borders to achieve a similar optical effect, but with page breaking as expected. Depending on the text in the left column this may or may not be an option.

Related

Dynamic Web table multiple checkbox selection for matching column value

I have application where there is a dynamic web table, I want to select multiple check box for matching value in one of column.
Refer attached image:
Here is what I've tried so far:
List<WebElement> Rows = s.findElements((By.xpath("//a[text()='Nitesh Kumar']")));
System.out.println("No of rows = "+Rows.size());
int z=Rows.size();
int i =0; for(i=0;i<z;i++)
{
for (int j = 0; j < Rows.size(); j++ )
{
s.findElement(By.xpath("//a[text()='Nitesh Kumar']/../../td[1]")).click();
}
}
Change the xpath to
//table[#id='id of the table']/tbody/*/td[3]/a[text()='Nitesh Kumar')]
to find the elements from the table.
Once the elements are found get the size of the list and use the below xpath to click the check box
//table[#id='id of the table']/tbody/*/td[3]/a[text()='Nitesh Kumar')]../td[1]
The complete code would be something like below.
List<WebElement> Rows = s.findElements((By.xpath("//table[#id='id of the table']/tbody/*/td[3]/a[text()='Nitesh Kumar')]")));
System.out.println("No of rows = "+Rows.size());
int z=Rows.size();
int i =0;
for(i=0;i<z;i++)
{
for (int j = 0; j < Rows.size(); j++ )
{
s.findElement(By.xpath("//table[#id='id of the table']/tbody/*/td[3]/a[text()='Nitesh Kumar')]../td[1]")).click();
}
}

How do I generate a ranking of columns in Google Sheets? Do I use ArrayFormula?

Here's a picture as an example:
The left side is the raw data I currently have, consisting of columns of teams A, B, C, D, E, F. Each row represents 1 game, and many games are played (this spreadsheet has several thousand rows).
How do I generate a ranking of the teams, like what I manually did on the right side? I want to sort the teams by the number of points they have.
In the first row, Team A scores the most points, so they are ranked 1st; Team C scores the second most points, and thus are in the second column, etc.
I'm having trouble figuring out how to sort by column in each row, rather than sorting by column, which is what the default sort() function does. I've tried multiple methods, including using the google SQL query function, (https://developers.google.com/chart/interactive/docs/querylanguage) but couldn't figure out how to use that either.
Is using ArrayFormula() a good idea in this situation? How would I implement that? This sheet does have several thousand rows, so it would be much better if I can just put an ArrayFormula() in the first row, rather than manually autofilling every row.
You may use costum formula, for cell H2:
=ArraySort(A1:F1,A2:F2001)
here's the code to paste into script editor:
function ArraySort(Arr1, Arr2) {
var w = Arr1[0].length;
var column = [];
var row = [];
var result = [];
for (var i = 0; i < Arr2.length; i++) {
row = [];
for (var k = 0; k < w; k++) {
column[k] = [Arr2[i][k],Arr1[0][k]];
}
column.sort(sortFunction);
for (var j = 0; j < w; j++) {
row[j] = column[j][1];
}
result.push(row);
}
return result;
}
function sortFunction(a, b) {
if (a[0] === b[0]) {
return 0;
}
else {
return (a[0] > b[0]) ? -1 : 1;
}
}
The following formula creates the ranking in the second row:
=array_constrain(transpose(sort(transpose({A$1:F$1; A2:F2}), 2, False)), 1, 6)
It can be copied down to rank the rest of rows. I do not think there is an arrayformula approach that would sort an array of arrays.
Explanation of the formula: it creates a two-row array (team names and scores), transposes it so the scores become the second column, sorts by that column, transposes again, and keeps only the names (the first row).

Number of filled rows in a SapTable

I'm using Silk4J and I have a table which is reported as SapTable in the Locator Spy. From that table, I'm trying to get all the texts of the second column, but it hangs or terminates with an exception. In the following you find the code for my tries. Finally I reached the last row of the table, but it hangs there again.
In all examples I'm using a while loop instead of a for loop, because I want to insert more conditions later.
Try 1: Straight forward (I thought)
SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
while (row < maxrows)
{
String text = table.getCell(row, COLUMN).getText();
logger.debug(text);
row++;
}
However, this code prints all visible columns, then hangs.
Try 2: adding a PageDn keypress via Silk
Since try 1 printed only the visible cells, I thought adding a keypress every page could help. That was my code:
SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
int visibleRows = table.getVisibleRowCount();
table.setFocus();
while (row < maxrows)
{
String text = table.getCell(row, COLUMN).getText();
logger.debug(text);
row++;
if (row % visibleRows == 0)
window.sendVKey(VKey.PAGE_DOWN);
}
Unfortunately this results in an exception "The virtual key is not enabled".
Try 3: adding a PageDn keypress via AwtRobot
Since the built-in sendVKey method did not work, but pressing the PageDn manually works, I switched to an AwtRobot:
SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
int visibleRows = table.getVisibleRowCount();
table.setFocus();
while (row < maxrows)
{
String text = table.getCell(row, COLUMN).getText();
logger.debug(text);
row++;
if (row % visibleRows == 0)
{
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_PAGE_DOWN);
robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
}
}
Pressing the key now works and I can see the table scroll to the next entry. However, my test application still hangs.
Try 4: Resetting the row count
Using the Locator Spy again, I found out that the index of the row is reset to zero, so I mimiced that in my code:
SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
int visibleRows = table.getVisibleRowCount();
table.setFocus();
while (row < maxrows)
{
String text = table.getCell(row, COLUMN).getText();
logger.debug(text);
row++;
if (row % visibleRows == 0)
{
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_PAGE_DOWN);
robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
row = 0; // <--
}
}
In this case, it prints the first N (number of visible) items of the list, scrolls to position N+1, prints the name of the first (!) row and then hangs when accessing the item with index 1 (after the reset).
Try 5: Sleeping
With some sleeping, I can reach the end of the table:
SapTable table; // initialized somewhere else
int maxrows = table.getRowCount();
int row = 0;
int visibleRows = table.getVisibleRowCount();
table.setFocus();
while (row < maxrows)
{
String text = table.getCell(row, COLUMN).getText();
logger.debug(text);
row++;
if (row % visibleRows == 0)
{
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_PAGE_DOWN);
robot.keyRelease(KeyEvent.VK_PAGE_DOWN);
row = 0;
Thread.sleep(1000); // <--
}
}
In this case, I get all items in the table. But since I don't know when the table ends, it does another getCell() call, which results in a hang again.
The question
I'm really stuck. I've also looked for other methods like getting the real number of rows in the table (getRowCount() doesn't), but didn't find one yet.
How do I get the real number of rows of a SapTable in Silk4J?
It took some trying - the underlying SAP automation API is not really helpful in this case - but here is how you can make it work:
private List<String> fetchItems() {
SapTable table = desktop.find("sap.Table");
SapVerticalScrollBar scrollBar = table.find("/SapVerticalScrollBar");
// the scrollbar maximum value seems to be a more reliable
// way to get the number of items than getRowCount
int itemCount = scrollBar.getMaximum() + 1;
List<String> items = new ArrayList<String>();
int currentAbsoluteRow = 0;
// the first loop iterates through the table page by page
for (int firstRowInPage = 0;
firstRowInPage < itemCount;
firstRowInPage = scrollToNextPage(firstRowInPage)) {
// this loop goes through the items of the current page
for (int currentRowInPage = 0;
currentRowInPage < table.getVisibleRowCount();
currentRowInPage++) {
if(++currentAbsoluteRow > itemCount) {
// we've read all the available items
return items;
}
SapComponent cell = table.getCell(currentRowInPage, 1);
items.add(cell.getProperty("Text").toString());
}
}
return items;
}
private int scrollToNextPage(int firstRowInPage) {
SapTable table = desktop.find("sap.Table");
SapVerticalScrollBar scrollBar = table.find("/SapVerticalScrollBar");
firstRowInPage += scrollBar.getPageSize();
scrollBar.scrollTo(firstRowInPage);
return firstRowInPage;
}
Some pitfalls that I encountered:
getRowCount() returned a higher count than there was in actual items
getVisibleRowCount() returns the number of items that would fit on the current page, even if not all rows were filled with actual items
The returned cell objects are only valid as long as the cell is on the screen, so you'll need to pull the information you want before you scroll to the next page.

controlP5: matrix/ 2D array for multiple RadioButton results

I'm trying to query a user to select from 4 or more sets of radio buttons where there are 5 buttons in each set (Processing 2+). Where I'm having trouble is taking the array created by selecting from each set of buttons and getting it to fill columns in a matrix where the elements can be queried and the 2D array can be printed and ultimately written as csv or tab txt file.
import controlP5.*;
ControlP5 controlP5;
RadioButton c0;
RadioButton c1;
RadioButton c2;
RadioButton c3;
int cols = 5;
int rows = 4;
int[][] myArray = new int[cols][rows];
void setup() {
size(600,650);
controlP5 = new ControlP5(this);
c0 = controlP5.addRadioButton("ch0",60,60)
.setSize(20,20)
.setItemsPerRow(5)
.setSpacingColumn(50)
.addItem("c03", 1)
.addItem("c04", 2)
.addItem("c05", 3)
.addItem("c0AM", 4)
.addItem("c0AF", 5)
;
c1 = controlP5.addRadioButton("ch1",60,80)
.setSize(20,20)
.setItemsPerRow(5)
.setSpacingColumn(50)
.addItem("c13", 1)
.addItem("c14", 2)
.addItem("c15", 3)
.addItem("c1AM", 4)
.addItem("c1AF", 5)
;
c2 = controlP5.addRadioButton("ch2",60,100)
.setSize(20,20)
.setItemsPerRow(5)
.setSpacingColumn(50)
.addItem("c23", 1)
.addItem("c24", 2)
.addItem("c25", 3)
.addItem("c2AM", 4)
.addItem("c2AF", 5)
;
c3 = controlP5.addRadioButton("ch3",60,120)
.setSize(20,20)
.setItemsPerRow(5)
.setSpacingColumn(50)
.addItem("c33", 1)
.addItem("c34", 2)
.addItem("c35", 3)
.addItem("c3AM", 4)
.addItem("c3AF", 5)
;
}
void draw() {
background(0);
}
void controlEvent(ControlEvent theEvent) {
if(theEvent.isGroup() && theEvent.name().equals("ch0") || theEvent.name().equals("ch0") || theEvent.name().equals("ch2") || theEvent.name().equals("ch3")){
println(theEvent.name());
println(theEvent.arrayValue());
//float t=float(theEvent.arrayValue());
//int[][] = { {float getGroup(),float[] getArrayValue()}, {3,2,1,0}, {3,5,6,1}, {3,8,3,4} };
//int cols = 4;
//int rows = 5;
//int[][] myArray = new int[cols][rows];
// Two nested loops allow us to visit every spot in a 2D array.
// For every column I, visit every row J.
//for (int i = 0; i < cols; i++) {
// for (int j = 0; j < rows; j++) {
//myArray[i][j] = float(theEvent.arrayValue);
}
}
You were mixing different things together. Also you don't need to check whole array and store this information. Just update clicked button. Here is new version of your controlEvent
void controlEvent(ControlEvent theEvent) {
int cols = 4;
int rows = 5;
int[][] myArray = new int[cols][rows];
switch(theEvent.getId()){
case 0:
myArray[0][(int)theEvent.value()-1] = 1;
break;
case 1:
myArray[1][(int)theEvent.value()-1] = 1;
break;
case 2:
myArray[2][(int)theEvent.value()-1] = 1;
break;
case 3:
myArray[3][(int)theEvent.value()-1] = 1;
break;
}
println("==== " + theEvent.getId() + " ===");
println(myArray[theEvent.getId()]);
}
To do this simple switch you need to add ID parameter to all your radio buttons like this:
c3 = controlP5.addRadioButton("ch3", 60, 120)
.setId(3)
.setSize(20, 20)
...
I don't know how exactly you want use this array so my implementation use it as local variable so it will be deleted every time this event is called but this could be avoided by declaring array as global variable and then deleting only updated column.

HTML5 web sql: adding button to each row

I have some script that is delivering a series of rows from an SQL database.
I want to append.() a button to each row. My current code is:
tx.executeSql('SELECT * FROM myprogram', [], function (tx, results) {
var len = results.rows.length, i;
for (i = 0; i < len; i++) {
$('#myprog').append(results.rows.item(i).event)
$('.event').append('<button class="save_event">Remove</button>');
}
});
But the buttons get doubled up. For example if I get 3 rows, the first row will have 3 buttons, the 2nd will have 2 and the last will have 1 button.
Can anyone help me out to have the buttons display only once per row?
Thanks!
I found the answer, quite simple:
var len = results.rows.length, i;
for (i = 0; i < len; i++) {
$('#myprog').append(results.rows.item(i).event).append('<button class="remove_event">Remove</button>');
}
$('#myprog').append(results.rows.item(i).event).append('<button class="remove_event">Remove</button>');
You need to append to $('#myprog') selector.