Using iText 7.1.3 and trying to add a SVG file into PdfDocument gives an output where texts with length 1 are not rendered. I found where it might be the problem.
I please the iText team members to check it.
try {
SvgConverter.drawOnCanvas(svgUrl.openStream(), pdfCanvas_, imageLlx, imageLly);
} catch (IOException e) {
e.printStackTrace();
}
Debugger callings:
processText:255, DefaultSvgProcessor (com.itextpdf.svg.processors.impl)
visit:212, DefaultSvgProcessor (com.itextpdf.svg.processors.impl)
visit:204, DefaultSvgProcessor (com.itextpdf.svg.processors.impl)
visit:204, DefaultSvgProcessor (com.itextpdf.svg.processors.impl)
executeDepthFirstTraversal:153, DefaultSvgProcessor (com.itextpdf.svg.processors.impl)
process:106, DefaultSvgProcessor (com.itextpdf.svg.processors.impl)
process:768, SvgConverter (com.itextpdf.svg.converter)
convertToXObject:555, SvgConverter (com.itextpdf.svg.converter)
convertToXObject:590, SvgConverter (com.itextpdf.svg.converter)
drawOnCanvas:380, SvgConverter (com.itextpdf.svg.converter)
In function processText, in the line for Trim trailing whitespace
trimmedText = SvgTextUtil.trimTrailingWhitespace("A");
for the trimmedText = A (length = 1) returns empty String
/**
* Process the text contained in the text-node
*
* #param textNode node containing text to process
*/
private void processText(ITextNode textNode) {
ISvgNodeRenderer parentRenderer = this.processorState.top();
if (parentRenderer instanceof TextSvgNodeRenderer) {
// when svg is parsed by jsoup it leaves all whitespace in text element as is. Meaning that
// tab/space indented xml files will retain their tabs and spaces.
// The following regex replaces all whitespace with a single space.
//TODO(RND-906) evaluate regex and trim methods
String trimmedText = textNode.wholeText().replaceAll("\\s+", " ");
//Trim leading whitespace
trimmedText = SvgTextUtil.trimLeadingWhitespace(trimmedText);
//Trim trailing whitespace
trimmedText = SvgTextUtil.trimTrailingWhitespace(trimmedText);
parentRenderer.setAttribute(SvgConstants.Attributes.TEXT_CONTENT, trimmedText);
}
}
There indeed is a bug in the trimTrailingWhitespace you pointed out
public static String trimTrailingWhitespace(String toTrim) {
if(toTrim == null){
return "";
}
int end = toTrim.length();
if (end > 0) {
int current = end - 1;
while (current > 0) {
char currentChar = toTrim.charAt(current);
if (Character.isWhitespace(currentChar) && !(currentChar == '\n' || currentChar == '\r')) {
//if the character is whitespace and not a newline, increase current
current--;
} else {
break;
}
}
if(current == 0){
return "";
}else {
return toTrim.substring(0, current + 1);
}
}else{
return toTrim;
}
}
As the comment //if the character is whitespace and not a newline, increase current followed by current--; already indicates, this method is a copy of trimLeadingWhitespace (where the line after the identical comment indeed increases current) modified to work at the other end of the String parameter. Unfortunately, though, the modification was incorrect: If the string has a non-whitespace character at position 0 and thereafter only whitespaces, it erroneously is considered empty.
The fix would be to replace
while (current > 0)
by
while (current >= 0)
and
if(current == 0)
by
if(current < 0)
With that fix the
if (end > 0) {
[...]
}else{
return toTrim;
}
frame around [...] furthermore becomes unnecessary. And the while loop could more compactly have been formulated as a for loop, e.g. like this:
public static String trimTrailingWhitespace(String toTrim) {
if (toTrim == null) {
return "";
}
int current = toTrim.length() - 1;
for ( ; current >= 0; current--) {
char currentChar = toTrim.charAt(current);
if (!(Character.isWhitespace(currentChar) && !(currentChar == '\n' || currentChar == '\r'))) {
break;
}
}
return current < 0 ? "" : toTrim.substring(0, current + 1);
}
Related
public class isogram {
public static boolean isIsogram(String str) {
// ...
boolean flag = false;
String st = str.toLowerCase();
String checker = String.valueOf(st.charAt(0));
// check if the string is empty and return false
if (st == ""){
return flag;
}
// for each char in the string, check if it lies in the a-z range
// and that it obeys the isogram conition (no repetitions)
for(int i=1; i<st.length()-1 ; i++){
boolean cond_check = !checker.contains(String.valueOf(st.charAt(i)));
if (st.charAt(i) >= 'a' && st.charAt(i) <= 'z' && cond_check ){
flag = true;
}
else{
flag = false;
break;
}
checker = checker + st.charAt(i);
}
return flag;
}
}
I'm trying to check if the given word is an isogram (no repetitions and numbers). Not able to figure what the issue is. And also while eliminating the empty string, I think I have to try to avoid " " (empty strings with spaces as well because they count as characters). How to do that?
The rest of the code is working perfectly but I cannot figure out how to prevent punctuation from being translated.
public class PigLatintranslator
{
public static String translateWord (String word)
{
String lowerCaseWord = word.toLowerCase ();
int pos = -1;
char ch;
for (int i = 0 ; i < lowerCaseWord.length () ; i++)
{
ch = lowerCaseWord.charAt (i);
if (isVowel (ch))
{
pos = i;
break;
}
}
if (pos == 0 && lowerCaseWord.length () != 1) //translates if word starts with vowel
{
return lowerCaseWord + "way"; // Adding "way" to the end of string
}
else if (lowerCaseWord.length () == 1) //Ignores words that are only 1 character
{
return lowerCaseWord;
}
else if (lowerCaseWord.charAt(0) == 'q' && lowerCaseWord.charAt(1) == 'u')//words that start with qu
{
String a = lowerCaseWord.substring (2);
return a + "qu" + "ay";
}
else
{
String a = lowerCaseWord.substring (1);
String b = lowerCaseWord.substring (0,1);
return a + b + "ay"; // Adding "ay" at the end of the extracted words after joining them.
}
}
public static boolean isVowel (char ch) checks for vowel
{
if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u' || ch == 'y')
{
return true;
}
return false;
}
}
I need the translation to ignore punctuation. For example "Question?" should be translated to "estionquay?" (question mark still in the same position and not translated)
As Andreas said, if the function is expecting only one word, it should be the responsibility of the calling function to ensure there's no full sentence or punctuation being passed to it. With that said, if you require the translator to handle this, you need to find the index of the string where the punctuation or non-letter character occurs. I added in a main method to test the function:
public static void main(String[] args) {
System.out.println(translateWord("QUESTION?"));
}
I added a loop into the qu case to find the punctuation being input, the two checks are to see if the character at position i is inside the range of a - z. The sub-string then only goes to the point where the punctuation is found.
int i;
for (i = 0; i < lowerCaseWord.length(); i++) {
if(lowerCaseWord.charAt(i) > 'z' || lowerCaseWord.charAt(i) < 'a') {
break;
}
}
String a = lowerCaseWord.substring (2, i);
String b = lowerCaseWord.substring(i);
return a + "qu" + "ay" + b;
This may need some tweaking if you're worried about words with hyphens and whatnot but this should put across the basic idea.
Here's the output I received:
$javac PigLatintranslator.java
$java -Xmx128M -Xms16M PigLatintranslator
estionquay?
I have a String like
value 1, value 2, " value 3," value 4, value 5 " ", value 6
I want to split this by comma and ignoring commas found in an expression enclosed by multiple double quotes
My desired output should be
value 1
value 2
" value 3," value 4, value 5 " "
value 6
I tried this Splitting on comma outside quotes but it doesn't work
Thanks in advance........Elsayed
Well first I would recommend to escape inner double quotes, e. g. value 1, value 2, " value 3,\" value 4, value 5 \" ", value 6. With this sort of syntax a method I use for this purpose is below. It is a little bit more complex than the first proposal, because it ignores blanks and line breaks between a comma and the next element in the list.
public static String[] splitSet(String inStr, char delimiter) {
if (inStr == null)
return null;
if (inStr.isEmpty())
return new String[]{};
/*
* add an empty element here and remove it at the end to simplify
* algorithm
*/
String delimiterStr = String.valueOf(delimiter);
String parseStr = inStr + delimiterStr + " ";
/*
* prepare parsing.
*/
Vector<String> list = new Vector<>();
String element = "";
int lc = 0;
char b = ' ';
char c;
boolean inBetweenQuotes = false;
/*
* parsing loop.
*/
while (lc < parseStr.length()) {
c = parseStr.charAt(lc);
/*
* add current entry and all following empty entries to list vector.
* Ignore space and new line characters following the delimiter.
*/
if ((c == delimiter) && !inBetweenQuotes) {
// flag to avoid adding empty elements for delimiter being blank
// or new line
boolean added = false;
while ((lc < parseStr.length())
&& ((c == delimiter) || (c == ' ') || (c == '\n'))) {
if ((c == delimiter)
&& !(added && ((c == ' ') || (c == '\n')))) {
list.add((String) UFormatter.parseElement(element,
DataType.STRING, delimiterStr));
element = "";
added = true;
}
lc++;
if (lc < parseStr.length())
c = parseStr.charAt(lc);
if (lc > 0)
b = parseStr.charAt(lc - 1);
}
}
/*
* add character to tmpList. Close String literal or Vector literal
*/
else {
element = element + c;
// toggle inBetweenQuotes at not escaped '"'
if ((c == '"') && (b != '\\'))
inBetweenQuotes = !inBetweenQuotes;
lc++;
b = c;
}
}
if (!element.isEmpty() && inBetweenQuotes)
list.add(element.substring(0, element.length() - 1) + "\"");
else if (!element.isEmpty())
list.add(element.substring(0, element.length() - 1));
// put Vector to array.
String[] ret = new String[list.size()];
for (int i = 0; i < list.size(); i++)
ret[i] = list.elementAt(i);
return ret;
}
I don't know how to use regex to solve it.
Is the double quotes included now? I haven't tried this code yet.
public static List<String> splitByComma(String text) {
ArrayList<String> ret = new ArrayList<>();
char[] chars = text.toCharArray();
boolean inQuote = false;
StringBuilder tmp = new StringBuilder();
for (char ch : chars) {
if (ch == ',') {
if (inQuote) tmp.append(ch);
else {
ret.add(tmp.toString());
tmp.setLength(0);
}
} else if (ch == '"') {
tmp.append(ch); // I just add this code
inQuote = !inQuote;
} else tmp.append(ch);
}
ret.add(tmp.toString());
return ret;
}
Please tell me if my code has any problem.
Method should take in a word, recursively go thru the string and find letters that are the same distance from either end of the alphabet and remove them. If it removes a match, those letters cannot be used again. If every letter is removed, then it is a match.
for (int i = 1; i < word.length()-1; i++)
{
if (word.charAt(0) + word.charAt(i) == 155)
{
StringBuilder sb = new StringBuilder(word);
sb.deleteCharAt(0);
sb.deleteCharAt(i);
String strNew = sb.toString();
System.out.println(strNew);
return isAlphaOpp(strNew);
}
}
return false;
}
I have modified your method a bit, have a look at it. You need compare with 155 if your string is all capitals, if all lower case letters you need compare with 219. As #Raghu suggested, this doesnt required recursion (that is making things complicated), but I am assuming you want to try this with recursion.
public static boolean isAlphaOpp (String word)
{
//if word has odd number of characters, it cannot be an alpha opp
if (word.length() % 2 != 0)
{
return false;
}
//if string makes it to 0, then word must be an alpha opp
if (word.length() == 0)
{
return true;
}
/*if (word.charAt(0) + word.charAt(word.length()-1) == 155)
{
System.out.println(word.substring(1, word.length()-1));
return isAlphaOpp(word.substring(1, word.length()-1));
}
*/
//Should go thru each letter and compare the values with char(0). If char(0) + //char(i) == 155 (a match) then it should remove them and call the method again.
int length = word.length()-1;
int start = 0;
String newStr = null;
while(start < length) {
if(word.charAt(start) + word.charAt(length) == 219) {
StringBuilder sb = new StringBuilder(word);
sb.deleteCharAt(length);
sb.deleteCharAt(start);
newStr = sb.toString();
System.out.println(newStr);
start++;
length--;
break;
} else {
start++;
}
}
if(newStr != null) {
return isAlphaOpp(newStr);
}
return false;
}
I am trying to modify a config file in Java using Properties.
I read, write and modify the lines successfully using Properties.store, load and setProperty, but I noticed that after doing such operation the file is overwritten and thus I loose al the lines in the config file that are not key-value pairs. Namely, I loose the comments.
Is there a way to keep such lines using java.util?
Placing a prefix in each line is not a problem. I know how to do it 'manually' reading line by line; I'am asking instead for an alternative
I don't think it is possible. Note that properties also don't promise that the ordering will be the same from load() to store(), or from one store() to another. If it is possible, the javadoc for Properties will tell you how.
import java.io.*;
import java.util.*;
/**
* The CommentedProperties class is an extension of java.util.Properties
* to allow retention of comment lines and blank (whitespace only) lines
* in the properties file.
*
* Written for Java version 1.4
*/
public class CommentedProperties extends java.util.Properties {
/**
* Use a Vector to keep a copy of lines that are a comment or 'blank'
*/
public Vector lineData = new Vector(0, 1);
/**
* Use a Vector to keep a copy of lines containing a key, i.e. they are a property.
*/
public Vector keyData = new Vector(0, 1);
/**
* Load properties from the specified InputStream.
* Overload the load method in Properties so we can keep comment and blank lines.
* #param inStream The InputStream to read.
*/
public void load(InputStream inStream) throws IOException
{
// The spec says that the file must be encoded using ISO-8859-1.
BufferedReader reader =
new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1"));
String line;
while ((line = reader.readLine()) != null) {
char c = 0;
int pos = 0;
// Leading whitespaces must be deleted first.
while ( pos < line.length()
&& Character.isWhitespace(c = line.charAt(pos))) {
pos++;
}
// If empty line or begins with a comment character, save this line
// in lineData and save a "" in keyData.
if ( (line.length() - pos) == 0
|| line.charAt(pos) == '#' || line.charAt(pos) == '!') {
lineData.add(line);
keyData.add("");
continue;
}
// The characters up to the next Whitespace, ':', or '='
// describe the key. But look for escape sequences.
// Try to short-circuit when there is no escape char.
int start = pos;
boolean needsEscape = line.indexOf('\\', pos) != -1;
StringBuffer key = needsEscape ? new StringBuffer() : null;
while ( pos < line.length()
&& ! Character.isWhitespace(c = line.charAt(pos++))
&& c != '=' && c != ':') {
if (needsEscape && c == '\\') {
if (pos == line.length()) {
// The line continues on the next line. If there
// is no next line, just treat it as a key with an
// empty value.
line = reader.readLine();
if (line == null)
line = "";
pos = 0;
while ( pos < line.length()
&& Character.isWhitespace(c = line.charAt(pos)))
pos++;
} else {
c = line.charAt(pos++);
switch (c) {
case 'n':
key.append('\n');
break;
case 't':
key.append('\t');
break;
case 'r':
key.append('\r');
break;
case 'u':
if (pos + 4 <= line.length()) {
char uni = (char) Integer.parseInt
(line.substring(pos, pos + 4), 16);
key.append(uni);
pos += 4;
} // else throw exception?
break;
default:
key.append(c);
break;
}
}
} else if (needsEscape)
key.append(c);
}
boolean isDelim = (c == ':' || c == '=');
String keyString;
if (needsEscape)
keyString = key.toString();
else if (isDelim || Character.isWhitespace(c))
keyString = line.substring(start, pos - 1);
else
keyString = line.substring(start, pos);
while ( pos < line.length()
&& Character.isWhitespace(c = line.charAt(pos)))
pos++;
if (! isDelim && (c == ':' || c == '=')) {
pos++;
while ( pos < line.length()
&& Character.isWhitespace(c = line.charAt(pos)))
pos++;
}
// Short-circuit if no escape chars found.
if (!needsEscape) {
put(keyString, line.substring(pos));
// Save a "" in lineData and save this
// keyString in keyData.
lineData.add("");
keyData.add(keyString);
continue;
}
// Escape char found so iterate through the rest of the line.
StringBuffer element = new StringBuffer(line.length() - pos);
while (pos < line.length()) {
c = line.charAt(pos++);
if (c == '\\') {
if (pos == line.length()) {
// The line continues on the next line.
line = reader.readLine();
// We might have seen a backslash at the end of
// the file. The JDK ignores the backslash in
// this case, so we follow for compatibility.
if (line == null)
break;
pos = 0;
while ( pos < line.length()
&& Character.isWhitespace(c = line.charAt(pos)))
pos++;
element.ensureCapacity(line.length() - pos +
element.length());
} else {
c = line.charAt(pos++);
switch (c) {
case 'n':
element.append('\n');
break;
case 't':
element.append('\t');
break;
case 'r':
element.append('\r');
break;
case 'u':
if (pos + 4 <= line.length()) {
char uni = (char) Integer.parseInt
(line.substring(pos, pos + 4), 16);
element.append(uni);
pos += 4;
} // else throw exception?
break;
default:
element.append(c);
break;
}
}
} else
element.append(c);
}
put(keyString, element.toString());
// Save a "" in lineData and save this
// keyString in keyData.
lineData.add("");
keyData.add(keyString);
}
}
/**
* Write the properties to the specified OutputStream.
*
* Overloads the store method in Properties so we can put back comment
* and blank lines.
*
* #param out The OutputStream to write to.
* #param header Ignored, here for compatability w/ Properties.
*
* #exception IOException
*/
public void store(OutputStream out, String header) throws IOException
{
// The spec says that the file must be encoded using ISO-8859-1.
PrintWriter writer
= new PrintWriter(new OutputStreamWriter(out, "ISO-8859-1"));
// We ignore the header, because if we prepend a commented header
// then read it back in it is now a comment, which will be saved
// and then when we write again we would prepend Another header...
String line;
String key;
StringBuffer s = new StringBuffer ();
for (int i=0; i<lineData.size(); i++) {
line = (String) lineData.get(i);
key = (String) keyData.get(i);
if (key.length() > 0) { // This is a 'property' line, so rebuild it
formatForOutput (key, s, true);
s.append ('=');
formatForOutput ((String) get(key), s, false);
writer.println (s);
} else { // was a blank or comment line, so just restore it
writer.println (line);
}
}
writer.flush ();
}
/**
* Need this method from Properties because original code has StringBuilder,
* which is an element of Java 1.5, used StringBuffer instead (because
* this code was written for Java 1.4)
*
* #param str - the string to format
* #param buffer - buffer to hold the string
* #param key - true if str the key is formatted, false if the value is formatted
*/
private void formatForOutput(String str, StringBuffer buffer, boolean key)
{
if (key) {
buffer.setLength(0);
buffer.ensureCapacity(str.length());
} else
buffer.ensureCapacity(buffer.length() + str.length());
boolean head = true;
int size = str.length();
for (int i = 0; i < size; i++) {
char c = str.charAt(i);
switch (c) {
case '\n':
buffer.append("\\n");
break;
case '\r':
buffer.append("\\r");
break;
case '\t':
buffer.append("\\t");
break;
case ' ':
buffer.append(head ? "\\ " : " ");
break;
case '\\':
case '!':
case '#':
case '=':
case ':':
buffer.append('\\').append(c);
break;
default:
if (c < ' ' || c > '~') {
String hex = Integer.toHexString(c);
buffer.append("\\u0000".substring(0, 6 - hex.length()));
buffer.append(hex);
} else
buffer.append(c);
}
if (c != ' ')
head = key;
}
}
/**
* Add a Property to the end of the CommentedProperties.
*
* #param keyString The Property key.
* #param value The value of this Property.
*/
public void add(String keyString, String value)
{
put(keyString, value);
lineData.add("");
keyData.add(keyString);
}
/**
* Add a comment or blank line or comment to the end of the CommentedProperties.
*
* #param line The string to add to the end, make sure this is a comment
* or a 'whitespace' line.
*/
public void addLine(String line)
{
lineData.add(line);
keyData.add("");
}
}