Is there any way in iTextSharp to 'keeptogether' chapter/sections, such as PdfpTable and Paragraph?
In my case I have chapters that always contain only one tiny table. If the chaptertitle + the tiny table run out of space I would like to trigger a new page so there is no break in between them.
Thanks for an answer!
Edit:
Here is an example with chapters and paragraphs.
Each chapter has one paragraph.
If the chapter is on the bottom of the page i want it to 'trigger' a new page so that the chapter and the paragraph are always on the same site and not seperatet trough a pagebreak.
iTextManager manager = new iTextManager();
manager.NewDocument(PageSize.A4, true, 20,20,20,20);
manager.OpenForOutput(false, true,null);
for (int i = 0; i < 100; i++)
{
Chapter c = new Chapter("Chapter " + i, i);
c.TriggerNewPage = false;
c.Add(new Paragraph("Paragraph " + i));
manager.Doc.Add(c);
}
manager.SaveToFile(#"C:\temp\test.pdf", System.IO.FileMode.Create);
manager.Close();
Process.Start(#"C:\temp\test.pdf");
Related
I was looking for a solution to send automated emails from a Google Sheet connected to a Google Form, so I found Google Scripts. I never worked with it (or other scripts) before, so it's all new to me. I did find a lot of information on the internet, but I still have some questions.
Let's explain the situation. So I'm selling two different products in a Google Form, they both have a different price, so I was looking for a solution to send mails to the customers with the total price and some extra information. I have a Google Sheet where all the answers are put in automatically.
So I have 12 columns, as you can see in a copy of my sheet. https://docs.google.com/spreadsheets/d/1CrA9cNAwsUdLyaxzFiJrtXxu-0eiIMiA3IEaEaIYlZo/edit?usp=sharing
The script I use is added as a picture.
The problems:
The script should prevent Google from sending duplicates, but that doesn't work
I used a formula to fill the total amount in the sheet (column L) but I can't add it in the text of the script to send it in the mail
What I want:
Automated mails, can I add the script as a add-on or how do I automate it in the sheet? Now I need to press the "play script" button every time
I want the duplicate tracker to work, so no-one gets 2 mails
I want the script to scan enough rows, so everyone gets a mail
I want the total amount in the text of the mail
That's it I guess, thanks for helping!
// This constant is written in column C for rows for which an email
// has been sent successfully.
let EMAIL_SENT = 'EMAIL_SENT';
/**
* Sends non-duplicate emails with data from the current spreadsheet.
*/
function sendNonDuplicateEmails() {
try{
// Get the active sheet in spreadsheet
const sheet = SpreadsheetApp.getActiveSheet();
let startRow = 3; // First row of data to process
let numRows = 3; // Number of rows to process
// Fetch the range of cells A2:B3
const dataRange = sheet.getRange(startRow, 1, numRows, 300);
// Fetch values for each row in the Range.
const data = dataRange.getValues();
for (let i = 0; i < data.length; ++i) {
const row = data[i];
const emailAddress = row[1]; // Second column
const message = 'Beste ' + row[4] + '\n\nBedankt voor uw bestelling, we hebben deze succesvol ontvangen!' + '\n\nU bestelde ' + row[2] + ' pakjes droge worsten en ' + row[3] + ' pakjes pannenkoeken.' + '\nDit vormt dus ' + row[2] + ' x €6 en ' + row[3] + ' x €6,5.' + '\nIn totaal komt dit op ' + ' euro.' + '\n\nDe bestelling is pas officieel na overschrijving van bovenstaand bedrag op het rekeningnummer BE04 7755 8091 4631 met als mededeling: ' + row[2] + ' DW en ' + row[3] + ' PK.' + '\n\nBij vragen of opmerkingen kunt u ons altijd een mailtje sturen via info#chirozedelgemdorp.be.' + '\n\nChiro Zedelgem Dorp dankt u, laat het u smaken!';
const emailSent = row[11]; // Elfde kolom
if (emailSent !== EMAIL_SENT) { // Prevents sending duplicates
let subject = 'Worst- en pannenkoekenverkoop | Chiro Zedelgem Dorp';
// Send emails to emailAddresses which are presents in First column
MailApp.sendEmail(emailAddress, subject, message);
sheet.getRange(startRow + i, 11).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
}
}
}
catch(err){
Logger.log(err)
}
}
I've tried to test TOAST functionality and created the code:
int length = 20;
using (NpgsqlConnection conn = new NpgsqlConnection(""))
{
conn.Open();
StringBuilder ct = new StringBuilder();
ct.Append("CREATE TABLE t300 (");
for (int i = 0; i < 300; i++)
{
ct.Append("i").Append(i).Append(" int not null, n").Append(i).Append(" varchar(").Append(length).Append(") not null, ");
}
ct.Remove(ct.Length - 2, 2).Append(");");
using (NpgsqlCommand cmd = new NpgsqlCommand(ct.ToString(), conn))
{
cmd.ExecuteNonQuery();
}
StringBuilder isql = new StringBuilder();
isql.Append("INSERT INTO t300 (");
StringBuilder vsql = new StringBuilder();
vsql.Append("VALUES (");
for (int i = 0; i < 300; i++)
{
isql.Append("i").Append(i).Append(", n").Append(i).Append(", ");
vsql.Append(":i").Append(i).Append(", :n").Append(i).Append(", ");
}
isql.Remove(isql.Length - 2, 2).Append(") ").Append(vsql).Remove(isql.Length - 2, 2).Append(");");
using (NpgsqlCommand cmd = new NpgsqlCommand(isql.ToString(), conn))
{
for (int i = 0; i < 300; i++)
{
cmd.Parameters.AddWithValue("i" + i.ToString(), NpgsqlDbType.Integer, i);
cmd.Parameters.AddWithValue("n" + i.ToString(), NpgsqlDbType.Varchar, length, i.ToString() + new string('n', length - i.ToString().Length));
}
for (int i = 0; i < 10000; i++)
{
cmd.ExecuteNonQuery();
}
}
}
This code fails on INSERT with exception '54000, row size (8424) exceeds limit (8160)'.
When I set 'length' variable to 26, the code works fine. Please tell me the workaround to eliminate this situation.
Postgres 12, Npgsql 4.1.5
Perhaps you have a misconception of how TOAST storage works. PostgreSQL does not compress the whole row and store it in the TOAST table, but each column of a varying length data type independently.
So after toasting, the row still consists of 600 columns, 300 of which (the integers) won't be toasted (4 bytes), and the other 300 toasted columns (the varchars) will now contain a TOAST header and a TOAST pointer.
Together this happens to be more than fits into a single block, and rows cannot span more than a single block. That causes the error.
The solution is not to use tables with so many columns. You should split the data in several tables (normalization usually takes care of that). If there are truly very many attributes to a single entity, chances are that not all of these attributes will get used in join or WHERE conditions. You could consider storing such attributes in a single jsonb column, where TOASTing will be much more efficient.
for my Thesis I must count cells on pictures stained with Immunofluorescence and I am writing a macro in ImageJ to do it for me.
For this I coloursplit the picture, analyse particles in the red channel (my Antibody´s colour) and then I want to take the coordinates of analysed particles and only count them if at the same coordinates in the blue channel there is also a staining (DAPI - just a general cell staining).
This way I assure that there is as little dirt counted as possible.
The problem is that when I get the coordinates from the results table and use them to makePoint(x,y) the coordinates are "distorted" - usually the correct coordinates but plus 4ish, though never in exactly the same, which is why i can´t simply distract a number from the coordinates
Below I first write down the critical lines of code, then the whole code.
Thank you very much in advance
run("Analyze Particles...", " circularity=r1-r2 display clear in_situ");
roiManager("deselect");
z=nResults;
for (j=0; j<z; j++) { //loops through the Results table and adds to "counter" if a match is found
selectImage(channelsplit[2]); //selects blue window
setThreshold(d,l);
run("Threshold...");
setOption("BlackBackground", true);
run("Convert to Mask", "method=Default background=Default black");
run("Coordinates...", "left=0 right=19373 top=0 bottom=13600"); //I tried to set the boundaries of the pictures equally, but it didnt work
makePoint(getResult("X", j)), (getResult("Y", j));
print ("X" + j + ": " + getResult("X",j)); //this and the following line are not to be in the program, once it works
print ("Y" + j + ": " + getResult("Y",j));
run ("Measure");
if (getResult("Mean", nResults-1)>100) {
counter++;
}
} //for j
print (i + ": " + counter);
setBatchMode(true);
if (isOpen("ROI Manager")) {
r=roiManager("count");
} else {
setBatchMode(false);
exit("You need a ROI Manager open with the ROIs of all the pictures to be measured");
}
Dialog.create ("Variables")
Dialog.addCheckbox ("red", true);
Dialog.addCheckbox ("green", false);
Dialog.addCheckbox ("blue", false);
Dialog.addCheckbox ("watershed", true);
Dialog.addNumber ("Thresholddark:", 110);
Dialog.addNumber ("Thresholdlight:", 255);
Dialog.addNumber ("greenThresholddark:", 110);
Dialog.addNumber ("greenThresholdlight:", 253);
Dialog.addNumber ("pmin:", 15);
Dialog.addNumber ("pmax:", 100);
Dialog.addNumber ("roundness1:", 0.5);
Dialog.addNumber ("roundness2:", 1.0);
Dialog.addMessage("batch - select 'true' for your macro to run faster, but you will not see what it does until finished\nstack - selecht this box if you use unstacked pictures and want them stacked, deselect if you use a readied stack or a single picture\nred - select this box if you want to count red coloured cells \ngreen - select this box if you want to count green coloured cells \nblue - select this box if you want to count blue coloured cells\nall variables are the same for the colours you count. It may be better to adjust variables for each colour and count seperately\nwatershed - select this box only if you have overlapping cells in your image\nThresholdlight - particles lighter than this won't be measured/counted \nThresholddark - particles darker than this won't be measured/counted \nThresholdlight and Thresholddark can be between 0 and 255 \ntry thresholding manually at least once manually to get best results \npmin - particles with a size smaller than this won't be counted \npmax - particles with a size greater than this won't be counted")
Dialog.show ();
cr=Dialog.getCheckbox();
cg=Dialog.getCheckbox();
cb=Dialog.getCheckbox();
w=Dialog.getCheckbox();
d=Dialog.getNumber();
l=Dialog.getNumber();
dg=Dialog.getNumber();
lg=Dialog.getNumber();
pmin=Dialog.getNumber();
pmax=Dialog.getNumber();
r1=Dialog.getNumber();
r2=Dialog.getNumber();
dir = getDirectory("Choose a Directory "); //choose the folder with all the pictures to be measured
listFiles(dir); //I found this in a listFiles recursively Demo and changed it to open my pictures
function listFiles(dir) {
list = getFileList(dir);
for (o=0; o<list.length; o++) { //loops through the file list, opening then analysing then closing one after another
if (endsWith(list[o], "/")) {
listFiles(""+dir+list[i]);
} else {
open(dir + list[o]);
//the previous opens all pictures in a given folder in a way i do not understand
//I only use this function because I could not use the variable list.length outside of it for unknown reasons
//following is my cellcount program to be executed for each picture
//then the picture will be closed before the new one is opened
run("Split Channels");
channelsplit = getList("image.titles");
if (cr==1) {
a=0;
}
if (cg==1) {
a=1;
}
selectImage(channelsplit[a]); //selects red or green window, depending on input in the Dialog
setThreshold(d,l);
run("Threshold...");
setOption("BlackBackground", true);
run("Convert to Mask", "method=Default background=Default black");
run("Coordinates...", "left=0 right=19373 top=0 bottom=13600");
for (i=2*o; i<=((2*o)+1); i++) { //loops through two ROIs of the ROIManager
counter=0;
selectImage(channelsplit[a]);
roiManager("deselect");
roiManager("select", i);
if (w==1) {
run("Watershed", "slice");
}
run("Analyze Particles...", " circularity=r1-r2 display clear in_situ");
roiManager("deselect");
z=nResults;
for (j=0; j<z; j++) { //loops through the Results table and adds to "counter" if a match is found
selectImage(channelsplit[2]); //selects blue window
setThreshold(d,l);
run("Threshold...");
setOption("BlackBackground", true);
run("Convert to Mask", "method=Default background=Default black");
run("Coordinates...", "left=0 right=19373 top=0 bottom=13600"); //I tried to set the boundaries of the pictures equally, but it didnt work
makePoint(getResult("X", j)), (getResult("Y", j));
print ("X" + j + ": " + getResult("X",j)); //this and the following line are not to be in the program, once it works
print ("Y" + j + ": " + getResult("Y",j));
run ("Measure");
if (getResult("Mean", nResults-1)>100) {
counter++;
}
} //for j
print (i + ": " + counter);
close("Results"); //closes Results to get the variables in order for the next window
// IJ.renameResults("res" + i);
} //for i
close("*"); //closes all image windows to save RAM
} //else
} //for o
} //function
setBatchMode(false);
I need to achieve a table that looks like the one in the picure, with space between columns. I tried:
cell.setPaddingLeft(10);
cell.setMarginLeft(10);
extractionMediaTable.setVerticalBorderSpacing(10);
But none of these seem to affect the table. Any suggestions?
This should help:
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
table.setVerticalBorderSpacing(10);
table.setHorizontalBorderSpacing(10);
Some explanations:
By default iText creates tables with collapsed borders, so the first line overrides that.
Once the borders are separated, one can set the spacing (either horizontal or vertical) between them.
For example, look at the snippet below and the screenshot of the resultant pdf:
Table table = new Table(3);
table.setBorderCollapse(BorderCollapsePropertyValue.SEPARATE);
table.setVerticalBorderSpacing(10);
table.setHorizontalBorderSpacing(10);
for (int j = 0; j < 10; j++) {
for (int i = 0; i < 3; i++) {
table.addCell(new Cell().add(new Paragraph("Cell row " + j + "col " + i )));
}
}
[Flextable1][Flextable2]
[Flextable3][Flextable4]
Inside every flextable: put vertical panel
inside every verticalpanel: consists of label and link shown as diagram below:
I want to use for loop in the flextable but i don't know where to start first.
Please help me to solve. Thanks.
It's a very simple code. Read inline comments for more info.
// root flex table that contains other widgets
FlexTable rootFlexTable = new FlexTable();
int counter = 0;
// 2 rows and 2 columns (change it as per your requirement)
for (int i = 0; i < 2; i++) { // rows
for (int j = 0; j < 2; j++) {// columns
counter++;
FlexTable flexTable = new FlexTable();
VerticalPanel verticalPanel = new VerticalPanel();
Label label = new Label("No " + counter + "(label)");
Hyperlink hyperlink = new Hyperlink("Name " + counter + "(link)",
"link" + counter);
verticalPanel.add(label);
verticalPanel.add(hyperlink);
// why are you using one extra flex table
// that contains only single component?
flexTable.setWidget(0, 0, verticalPanel);
// add inner flex table at row i and column j
rootFlexTable.setWidget(i, j, flexTable);
}
}
RootPanel.get().add(rootFlexTable);
Screenshot: