I'm using Itext to generate a pdf and I need to do something like Chunk.keepOnSameLine.
I'm building a Phrase that can have several lines that are composed by multiple Chunks and when the line ends, I want the chunk not to be broken.
The Chunk is a name and a date. Ex: "John Smith (2016-01-13 11:13)"
The code is like
Phrase p = new Phrase();
Chunk c1 = new Chunk("FirstName LastName (2016-01-13 11:13)");
p.Add(c1);
Chunk c2 = new Chunk("FirstName LastName (2016-01-13 11:13)")
p.Add(c2);
Chunk c3 = new Chunk("FirstName LastName (2016-01-13 11:13)")
p.Add(c3);
Chunk c4 = new Chunk("FirstName LastName (2016-01-13 11:13)")
p.Add(c4);
Chunk c5 = new Chunk("FirstName LastName (2016-01-13 11:13)")
p.Add(c5);
Chunk c6 = new Chunk("FirstName LastName (2016-01-13 11:13)")
p.Add(c6);
Chunk c7 = new Chunk("FirstName LastName (2016-01-13 11:13)")
p.Add(c7);
(the code is dynamic so I don't know how many chunks will exist.
On the result phrase is showing like
the resulting phrase is then added to a PdfPCell
You can modify how iText splits lines by implementing ISplitCharacter and replacing the defaults (hyphen and space) with your own:
public class CustomSplitCharacter : ISplitCharacter
{
public bool IsSplitCharacter(
int start, int current, int end, char[] cc, PdfChunk[] ck)
{
char c = ck == null
? cc[current]
: (char)ck[Math.Min(current, ck.Length - 1)]
.GetUnicodeEquivalent(cc[current])
;
return (c == ')');
}
}
Then call SetSplitCharacter() on Chunk:
string chunkText = "FirstName LastName (2016-01-13 11:13)";
Random random = new Random();
var font = new Font(Font.FontFamily.HELVETICA, 10, Font.BOLD);
using (Document document = new Document())
{
PdfWriter.GetInstance(document, stream);
document.Open();
Phrase phrase = new Phrase();
for (var i = 0; i < 1000; ++i)
{
var asterisk = new String('*', random.Next(1, 20));
Chunk chunk = new Chunk(
string.Format("[{0}] {1}", asterisk, chunkText),
font
);
chunk.SetSplitCharacter(new CustomSplitCharacter());
phrase.Add(chunk);
}
document.Add(phrase);
}
This assumes your Chunk ends with ) like your example code, or that you have control of the Chunk's last character of text.
No dependency on a specific font. :)
Use Unicode U+00A0 which is a non-breaking space. You'll also want to use U+2011 which is a non-breaking hyphen. However, a non-breaking hyphen is technically a different character than a regular hyphen so you'll need a font that supports it, too.
var testString = "FirstName LastName (2016-01-13 11:13)";
var nbsp = "\u00a0";
var nbhy = "\u2011";
//With a normal space
var p1 = new Phrase();
for (var i = 0; i < 100; i++) {
p1.Add(new Chunk(testString + " "));
}
doc.Add(p1);
//Create a font that supports our "hard hyphen"
var bf = BaseFont.CreateFont(fontFile, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
var f = new iTextSharp.text.Font(bf, 12);
testString = testString.Replace(" ", nbsp).Replace("-", nbhy);
//With a non-breaking space
var p2 = new Phrase();
for (var i = 0; i < 100; i++) {
p2.Add(new Chunk(testString + " ", f));
}
doc.Add(p2);
Related
I would like to split a string in a column to n rows in Talend.
For example :
column
2aabbccdd
The first number is the "n" which I use to define the row lenght, so the expected result should be :
row 1 = aa
row 2 = bb
row 3 = cc
row 4 = dd
The idea here is to iterate on the string and cut it every 2 characters.
Any idea please ?
I would use a tJavaFlex to split the string, with a trick to have n rows coming out of it.
tJavaFlex's main code:
int n = Integer.parseInt(row1.str.substring(0, 4)); //get n from the first 4 characters
String str2 = row1.str.substring(4); //get the string after n
int nbParts = (str2.length() + 1) / n;
System.out.println("number of parts = " + nbParts);
for (int i = 0; i < nbParts; i++)
{
String part = str2.substring(i * n);
if(part.length() > n)
{
part = part.substring(0, n);
}
row2.str = part;
And tJavaFlex's end code is just a closing brace:
}
The trick is to use a for loop in the main code, but only close it in the end code.
tFixedFlowInput contains just one column holding the input string.
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.
I have a octal value, its - 0110 0145 0154 0154 0157 054 040 0110 0151, the result must be - Hello, Hi.
Here is my code :
String octal = "0110 0145 0154 0154 0157 054 040 0110 0151 ";
List<String> result = Arrays.asList(octal.split("\\s*,\\s*"));
long item = 1;
String res = "";
while(item < result.size()) {
char re = (char) Integer.parseInt(result.get((int) item), 8);
res = res + " "+ re;
item += 1;
}
System.out.println("Its" + res);
But the output :
Its e
Expected
Hello, Hi
I tried everything, but failed ):
Why did you think the split pattern "\\s*,\\s*" were a solution for your needs? To split at space, we can use "\\s+".
And in order to start at the first letter, you should initialize item = 0.
I’m dealing with BigDecimal in Java and I need to make 2 check against BigDecimal fields in my DTO:
Number of digits of full part (before point) < 15
Total number of
digits < 32 including scale (zeros after point)
What is the best way to implement it? I extremely don’t want toBigInteger().toString() and .toString()
I think this will work.
BigDecimal d = new BigDecimal("921229392299229.2922929292920000");
int fractionCount = d.scale();
System.out.println(fractionCount);
int wholeCount = (int) (Math.ceil(Math.log10(d.longValue())));
System.out.println(wholeCount);
I did some testing of the above method vs using indexOf and subtracting lengths of strings. The above seems to be signficantly faster if my testing methodology is reasonable. Here is how I tested it.
Random r = new Random(29);
int nRuns = 1_000_000;
// create a list of 1 million BigDecimals
List<BigDecimal> testData = new ArrayList<>();
for (int j = 0; j < nRuns; j++) {
String wholePart = r.ints(r.nextInt(15) + 1, 0, 10).mapToObj(
String::valueOf).collect(Collectors.joining());
String fractionalPart = r.ints(r.nextInt(31) + 1, 0, 10).mapToObj(
String::valueOf).collect(Collectors.joining());
BigDecimal d = new BigDecimal(wholePart + "." + fractionalPart);
testData.add(d);
}
long start = System.nanoTime();
// Using math
for (BigDecimal d : testData) {
int fractionCount = d.scale();
int wholeCount = (int) (Math.ceil(Math.log10(d.longValue())));
}
long time = System.nanoTime() - start;
System.out.println(time / 1_000_000.);
start = System.nanoTime();
//Using strings
for (BigDecimal d : testData) {
String sd = d.toPlainString();
int n = sd.indexOf(".");
int m = sd.length() - n - 1;
}
time = System.nanoTime() - start;
System.out.println(time / 1_000_000.);
}
As the code below, I need to create a dynamic table in which each cell gets validated. If there is any wrong input, error message will pop up for that specific cell. Now our BA doesn’t want to display multiple error messages for each column. Only simply display one error message for each column like last name like “Last Name is invalid” no matter how many last names are invalid. How can I accomplish this? Is that possible?
Thanks in advance. I will really appreciate your response.
Thanks,
Dev2016
Protected Sub ShowBoxes(ByVal startRow As Integer, ByVal nRowsAdd As Integer)
Dim d As Integer = 10
For d = startRow To (startRow + nRowsAdd - 1)
Dim row As New HtmlTableRow()
row.VAlign = "center"
row.Attributes("class") = "bgA"
row.ID = "IndivRow_" & d.ToString
Dim col1 As New HtmlTableCell()
col1.Align = "center"
col1.Width = 29
col1.InnerHtml = (d + 1).ToString
Dim col2 As New HtmlTableCell()
col2.Width = 53
col2.VAlign = "left"
Dim txt2 As New TextBox()
txt2.ID = "Last_" & d.ToString
txt2.Columns = "29"
txt2.MaxLength = "30"
Dim reg2 As New RegularExpressionValidator()
reg2.ID = "regLast_" & d.ToString
reg2.ControlToValidate = "Last_" & d.ToString
reg2.ValidationExpression = "^[a-zA-Z0-9""()-. '\s]{1,50}$"
reg2.ErrorMessage = "Last Name on line " & (d + 1).ToString & " contains invalid characters."
reg2.Text = "*"
reg2.Display = ValidatorDisplay.Static
reg2.ValidationGroup = "SubmitFormClient"
reg2.SetFocusOnError = True
col2.Controls.Add(txt2)
col2.Controls.Add(reg2)
Dim col3 As New HtmlTableCell()
col3.Width = 53
col3.VAlign = "left"
Dim txt3 As New TextBox()
txt3.ID = "First_" & d.ToString
txt3.MaxLength = "30"
Dim reg3 As New RegularExpressionValidator()
reg3.ID = "regFirst_" & d.ToString
reg3.ControlToValidate = "First_" & d.ToString
reg3.ValidationExpression = "^[a-zA-Z0-9""()-. '\s]{1,50}$"
reg3.ErrorMessage = "First Name on line " & (d + 1).ToString & " contains invalid characters."
reg3.Text = "*"
reg3.Display = ValidatorDisplay.Static
reg3.ValidationGroup = "SubmitFormClient"
reg3.SetFocusOnError = True
col3.Controls.Add(txt3)
col3.controls.add(reg3)
row.Cells.Add(col1)
row.Cells.Add(col2)
row.Cells.Add(col3)
tableIndiv.Rows.Add(row)
Next d
End Sub