I'm trying to justify align a block of text, however getting inconsistent results using iText7
Here my block of text stored in a database:
<p style="text-align: justify;">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
Here is my code:
List<IElement> lst = HtmlConverter.ConvertToElements(dt.Rows[i]["contents"].ToString()).ToList();
for (int j = 0; j < lst.Count; j++)
{
IBlockElement element = (IBlockElement)lst[j];
if (dt.Rows[i]["contents"].ToString().StartsWith("<br /><br /><h3 style=color:#0000ff;margin: 0 3px 0 3px;><strong>"))
{
contents.SetFontSize(12)
.SetBold()
.SetFontColor(ColorConstants.BLUE);
}
else if (dt.Rows[i]["contents"].ToString().StartsWith("<h4><strong>- "))
{
contents.SetFontSize(10)
.SetBold()
.SetFontColor(ColorConstants.BLACK);
}
else
{
contents.SetTextAlignment(TextAlignment.JUSTIFIED_ALL)
.SetFontSize(10)
.SetFontColor(ColorConstants.BLACK);
}
element.SetProperty(Property.LEADING, new Leading(Leading.MULTIPLIED, -1f));
document.Add(element);
}
Here is the PDF Output:
I've trying this question without success
I've checked the iText7 documentation, but is there a way to do this ?
I'm not sure what the contents object is in your code sample. I assume you're getting it somehow from the list of IElements that's generated by HtmlConverter. If not, you should review that, because you're setting properties on contents, but adding element to the document. If those do not refer to the same object, the properties on contents will be lost.
That being said, HtmlConverter will already apply the text alignment property defined by text-align: justify:
String html = "<p style=\"text-align: justify;\">Lorem Ipsum is simply dummy " +
"text of the printing and typesetting industry. Lorem Ipsum has been the " +
"industry's standard dummy text ever since the 1500s, when an unknown " +
"printer took a galley of type and scrambled it to make a type specimen " +
"book. It has survived not only five centuries, but also the leap into " +
"electronic typesetting, remaining essentially unchanged. It was " +
"popularised in the 1960s with the release of Letraset sheets containing " +
"Lorem Ipsum passages, and more recently with desktop publishing software " +
"like Aldus PageMaker including versions of Lorem Ipsum.</p>";
PdfWriter writer = new PdfWriter("SO66970672.pdf");
PdfDocument pdfDoc = new PdfDocument(writer);
Document document = new Document(pdfDoc);
IList<IElement> lst = HtmlConverter.ConvertToElements(html);
for (int j = 0; j < lst.Count; j++)
{
IBlockElement element = (IBlockElement)lst[j];
// Text alignment is already applied by HtmlConverter
//Paragraph contents = (Paragraph)element;
//contents.SetTextAlignment(TextAlignment.JUSTIFIED_ALL)
//.SetFontSize(10)
//.SetFontColor(ColorConstants.BLACK);
element.SetProperty(Property.LEADING, new Leading(Leading.MULTIPLIED, -1f));
document.Add(element);
}
document.Close();
Resulting PDF:
When setting the properties on contents (commented code in code sample above):
To illustrate that HtmlConverter respects the text-align property, this is the output with text-align: right in the HTML snippet:
Related
Looking for a selector of ALL paragraphs in the selected TextFrame, including the "not visible ones" in the overset. Script is already working and looping through the visible paragraphs:
[...]
if(app.selection[0].constructor.name=="TextFrame") {
var myParagraphs = app.selection[0].paragraphs; // only visible ones?!
var myArray = myParagraphs.everyItem().contents;
for (var i=0; i<myArray.length; i++) {
// do some fancy styling - WORKING
myParagraphs[i].appliedParagraphStyle = app.activeDocument.paragraphStyles.item('Format XYZ');
}
}
myArray.length changes when I set another hight for the TextFrame. But how can I work with ALL paragraphs? Already tested .anyItem() with the same result :(
Well, the paragraphs in the overset are not paragraphs of the text frame, so it makes sense that they are skipped in your script. To access all the paragraphs of the text frames + those that are in the overset part, you will need to access all paragraphs of the parent story (a story is the text entity that describes all the text within linked text frames and the overset text) of the text frame.
You can do so like this:
if(app.selection[0].constructor.name === "TextFrame") {
var myParagraphs = app.selection[0].parentStory.paragraphs;
for (var i = 0; i < myParagraphs.length; i++) {
myParagraphs[i].appliedParagraphStyle = app.activeDocument.paragraphStyles.item("Format XYZ");
}
}
Be aware though that this will handle all paragraphs in all text frames that are linked to your text frame in case there are any of those.
Also, since it looks like you need to apply the paragraph style on each paragraph of the entire story, you might as well apply the paragraph style to the entire story directly instead of looping over the paragraphs:
app.selection[0].parentStory.appliedParagraphStyle = app.activeDocument.paragraphStyles.item("Format XYZ");
I can set text bold using iText 7 like so:
parExecSummHeader2.Add(new Text(subj).SetBold());
...but when I try to combine a "normal" (non-bolded) bit of text with a bolded part, it doesn't work. I have this, which prints the line all "regular" (no bold):
parExecSummHeader2.Add("Average words per sentence (the general average is 15 - 20): " + Math.Round(WordsPerSentenceInDoc, 2).ToString());
...but want to make the calculated value bold. I tried both this:
parExecSummHeader2.Add("Average words per sentence (the general average is 15 - 20): ");
parExecSummHeader2.Add(new Text(Math.Round(WordsPerSentenceInDoc, 2).ToString().SetBold()));
...and this:
parExecSummHeader2.Add("Average words per sentence (the general average is 15 - 20): ");
string mathval = Math.Round(WordsPerSentenceInDoc, 2).ToString();
parExecSummHeader2.Add(new Text(mathval.SetBold()));
...but they both won't compile, complaining, "Error CS1061 'string' does not contain a definition for 'SetBold' and no accessible extension method 'SetBold' accepting a first argument of type 'string' could be found"
Shorter option which may result in not perfect quality of text rendering because bold simulation is used instead of a proper bold font:
Paragraph parExecSummHeader2 = new Paragraph();
parExecSummHeader2.Add("Average words per sentence (the general average is 15 - 20): ");
parExecSummHeader2.Add(new Text("123").SetBold());
Option with more code but better output quality because the proper bold font is use:
PdfFont boldFont = PdfFontFactory.CreateFont(StandardFonts.HELVETICA_BOLD);
Paragraph parExecSummHeader2 = new Paragraph();
parExecSummHeader2.Add("Average words per sentence (the general average is 15 - 20): ");
parExecSummHeader2.Add(new Text("123").SetFont(boldFont));
For iText 7:
public static final Font HELVETICA_BOLD =
new Font(FontFamily.HELVETICA, 12, Font.BOLD, BaseColor.BLUE);
new Text("MyText").setFontColor(Color.BLUE)
.setFont(PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD));
You have some more details examples here
For iText 5:
public const string DEFAULT_FONT_FAMILY = "Arial";
public static Font SmallFontBold
{
get
{
BaseColor black = new BaseColor(0, 0, 0);
Font font = FontFactory.GetFont(DEFAULT_FONT_FAMILY, 10, Font.BOLD, black);
return font;
}//get
}//SmallFontBold
...
Phrase aPh = new Phrase("My Bold", SmallFontBold);
And from here, you can try to use it combined.
I've been automating a process that we do for work that involves converting word documents to html files in TextWrangler. I've got everything sorted except for one final step, which involves moving the first character of the article inside of span tags (Boss's design choice, and the website doesn't support CSS that isn't inline or I'd just use first-letter).
Anyway, the HTML looks like this:
<p style='color: #444; font-size: 1.4em; line-height: 1.8em; font-weight: bold;'>
<img style='display: block; float: right; margin-bottom: 30px; margin-left: 15px; width: 475px;' src='*****.jpg' alt='' width='950' height='713' />
<span style='color: #444; float: left; font-size: 3em;'></span>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer at rhoncus velit. Sed velit libero, venenatis non ornare ac, egestas vitae quam.</p>
The idea would just be to move the first letter following the closing span tag to inside the span tag, but I've looked around and don't really have any idea where to begin.
Edit: This might be made easier knowing in that the number of characters prior to the necessary letter in the line won't ever change. So maybe finding a way to isolate the nth character in the line and go from there?
This is a silly example, you'll want to polish it a bit:
set htmlText to "... your HTML ..."
set endSpanLength to 7
set endSpanPos to offset of "</span>" in htmlText
set firstCharPos to (endSpanPos + endSpanLength + 1)
set firstChar to character firstCharPos of htmlText
set oldEndSpan to "</span>" & linefeed & firstChar
set newEndSpan to firstChar & "</span>" & linefeed
findAndReplaceInText(htmlText, oldEndSpan, newEndSpan)
on findAndReplaceInText(theText, theSearchString, theReplacementString)
set AppleScript's text item delimiters to theSearchString
set theTextItems to every text item of theText
set AppleScript's text item delimiters to theReplacementString
set theText to theTextItems as string
set AppleScript's text item delimiters to ""
return theText
end findAndReplaceInText
I'm attempting to use inDesign JSX scripts to insert the following data into a document:
data = [{heading:"Heading 1", content: ["Some content"]},
{heading:"Heading 2", content: ["Some other content with", "Multiple paragraphs"]}]
The data has to be placed into a single TextFrame, but have different styling on the heading and content.
The only way I can see to add the text is in one go via the textFrame.contents variable:
allContent = "";
headingParagraphs = []; // keep track of which paragraphs are headings
paragraph = 0;
for (var i = 0; i < data.length; i++) {
allContent += data.heading + "\r"; // Use a newline to split the paragraph
headingParagraphs.push(paragraph);
paragraph++;
for (var j = 0; j < data.content.length; j++) {
allContent += data.content[j] + "\r"; // Use a newline to split the paragraph
paragraph++;
}
}
textFrame.contents = allContent; // all data is in, but all text is styled the same
Then once the data is in, I iterate the paragraphs and add some style to the headings:
for (var i = 0; i < textFrame.paragraphs.count(); i++) {
if (headingParagraphs.indexOf(i) != -1) { // this is a heading paragraph
textFrame.paragraphs[i].pointSize = 20;
}
}
This works fine for small data sets that fit on one page, but once the contents gets bigger than the frame, paragraphs only returns visible paragraphs. And if I follow on to a new textFrame, paragraphs get split and the headingParagraphs[] array no longer lines up.
Ideally I'd like to append to the contents and set styles before I append the next content - but the API docs aren't very clear on how you might do that (if at all)
// Pseudo code:
for all sections:
append the heading to the frame, split to next page if needed
style all the *new* paragraphs as headings
for all section contents
append the content to the frame, split to next page if needed
style any *new* paragraphs as normal content
Is there a way to achieve this using either an append function or some other way to assign headings to the right place after content has been added? Perhaps special characters in the content to define style?
Your longer text gets messed up because currently you are working inside a single text frame. As soon as the text runs out of this one frame, you can't refer to them as this frame's "owned" paragraphs anymore. Use parentStory instead, as it points to the whole story, inside one text frame or spanning more than one. It also keeps on working if the text gets overset.
So if you have a starting frame called textFrame, set a new variable story to textFrame.parentStory and use that to add text.
As for adding text to this frame(/story): indeed, there is no fast way to add formatted text. Setting contents only works for long swathes with the same formatting. One way I've used is to write INX formatted text to a temporary file and importing that. It's slow for short fragments, but larger stories (up to several hundreds of pages) can be created very efficiently in Javascript itself, and then importing it into ID is .. well, it aint fast but faster than trying to do it "manually".
The other way is to add contents one paragraph at a time. The trick is to set formatting and add your text to story.insertionPoints[-1]. This, in a particularly handy notation, refers to the very last text insertion point of the story. You can think of an insertion point as "the text cursor"; you can 'apply' formatting to it, and any text added will then have this formatting as well.
Your code snippet reworked to add one data item at a time:
for (var i = 0; i < data.length; i++)
{
story.insertionPoints[-1].pointSize = 20;
story.insertionPoints[-1].contents = data[i].heading + "\r"; // Use a newline to split the paragraph
story.insertionPoints[-1].pointSize = 10;
for (var j = 0; j < data[i].content.length; j++)
{
story.insertionPoints[-1].contents = data[i].content[j] + "\r"; // Use a newline to split the paragraph
}
}
One thing to note is that you cannot temporarily override the pointSize here. If you set it to your larger size, you must also set it back to the original size again (the '10' in my snippet).
Can I convince you to look in to using paragraph styles? With paragraph styles, you'd have something like
hdrStyle = app.activeDocument.paragraphStyles.item("Header");
textStyle = app.activeDocument.paragraphStyles.item("Text");
for (var i = 0; i < data.length; i++)
{
story.insertionPoints[-1].contents = data[i].heading + "\r"; // Use a newline to split the paragraph
story.insertionPoints[-2].appliedParagraphStyle = hdrStyle;
for (var j = 0; j < data[i].content.length; j++)
{
story.insertionPoints[-1].contents = data[i].content[j] + "\r"; // Use a newline to split the paragraph
story.insertionPoints[-2].appliedParagraphStyle = textStyle;
}
}
Note that it's worth here to invert inserting contents and applying formatting. This is so any previous 'temporary' formatting gets cleared; applying a paragraph style this way overrides any and all local overrides. As you have to apply the style to the previous paragraph (the one before the hard return you just inserted), you would use insertionPoints[-2] here.
The advantages of using styles over local formatting are countless. You can apply all formatting you want with a single command, safely remove all local overridden formatting, and change any part of the formatting globally if you are not satisfied with it, rather than having to re-run your script with slightly different settings.
I would like to have a line aligned in such way, that some first words are aligned left and some last words are aligned right, but all are in the same line:
| text 1 text2 |
| Lorem ipsum dolor sit amet |
Is that possibile? How can I do that?
As far as I know, the only way is to use tab stops. You simply create a right-justified tab stop at the very right edge. Then you have to write your text as
text 1\ttext2
Lorem ipsum\tdolor sit amet
where \t is a Tab character (i.e. U+0009).
I had this same problem, you need to send the message EM_SETTYPEPOGRAPHYOPTIONS to the control with wParam = TO_ADVANCEDTYPOGRAPHY, and lParam = TO_ADVANCEDTYPOGRAPHY, then right justifying using PARAFORMAT with the rgxTabs[ n ] += 0x2000000 works.