MigraDoc Formatting - format

I am completely new to PDF creation including MigraDoc. I have gotten this far, which is really close to what I want for now. My question is that the text string (myMessage) that I pass to the "bodyParagraph" is up to 100 lines long, which causes three pages to be created, which is good. However the first page's Top margin is slightly greater than the second and third pages. I have no idea of why...
Basically, I am trying to create every page the same. Same header, footer and the body to take the same space regardless of the number of lines in the "bodyParagraph" content. If I have taken the completely wrong approach I would be open to suggestions.
Also, if there is a good tutorial to point me to that would be great. I can't really find anything but samples. I have learned everything from the samples, but sections, paragraph, etc is all new to me and I would like to get a better understanding of what I've done.
public static Document CreateWorkOrderPDF2(Document document, string filename, string WorkOrderHeader, string myMessage)
{
Section section = document.AddSection();
section.PageSetup.PageFormat = PageFormat.Letter;
section.PageSetup.StartingNumber = 1;
section.PageSetup.LeftMargin = 40;
//Sets the height of the top margin
section.PageSetup.TopMargin = 100;
section.PageSetup.RightMargin = 40;
section.PageSetup.BottomMargin = 40;
//MARGIN
HeaderFooter header = section.Headers.Primary;
header.Format.Font.Size = 16;
header.Format.Font.Color = Colors.DarkBlue;
MigraDoc.DocumentObjectModel.Shapes.Image headerImage = header.AddImage("../../Fonts/castorgate.regular.png");
headerImage.Width = "2cm";
Paragraph headerParagraph = section.AddParagraph();
headerParagraph = header.AddParagraph(WorkOrderHeader);
//BODY PARAGRAPH
Paragraph bodyParagraph = section.AddParagraph();
bodyParagraph = section.AddParagraph(myMessage);
bodyParagraph.Format.Font.Size = 10;
bodyParagraph.Format.Font.Color = Colors.DarkRed;
//paragraph.Format.Distancne = "3cm";
Paragraph renderDate = section.AddParagraph();
renderDate = section.AddParagraph("Work Order Generated: ");
renderDate.AddDateField();
return document;
}

The line Paragraph bodyParagraph = section.AddParagraph(); adds an empty paragraph. I assume that is the extra space on the first page.
Same issue with renderDate in the following code block.
Just remove the calls section.AddParagraph() to remove the empty paragraphs if you don't want them.
MigraDoc is much like Word and understanding sections, paragraphs, &c. in Word will also help you with MigraDoc. That knowledge along with the samples and IntelliSense should get you going.
You can use MigraDoc to create an RTF file, open the RTF in Word, and click the pilcrow to show formatting characters in Word.

Related

How to force with iText7 a new blank page only when necessary in order to have each text exactly on two pages?

I'm using itext 7.2.1 and I've this situation: I have a list of letters with variable content that normally fits in one page, but occasionally can span over two pages.
My goal is to force a new blank page after the "short" letters so they start alway on odd pages. The length of text is not know in advance.
Basically I have the following code:
PdfWriter writer = new PdfWriter("letters.pdf");
Document document = new Document(new PdfDocument(writer));
List<String> letters = . . .code to retrieve letters text;
int nLetter = 0;
for (String text : letters) {
Paragraph p = new Paragraph().add(text);
doc.add(p);
nLetter++;
//now after layout of last paragraph I must ensure that
//the next paragraph starts on page (nLetter*2 + 1): how can I do this?
}
. . .
doc.close();
What is the best way to do so in iText7? I tried with custom DocumentRenderer but I haven't found a clean and working solution.
The best and simpliest way to know if your next paragraph starts on the next page of the document is to get the root renderer of your document and get current area. And it will return the area(rectangle and page) where the end of the text is located.
doc.getRenderer().getCurrentArea();

In InDesign, is there a way to bold a whole word that has one bold character?

I'm working on an index in InDesign. Some of the page numbers are in bold, others are in italics or regular. During editing, somehow the first numbers of some of the bold page numbers got changed. I've figured out how to highlight those page numbers by coloring the bold numbers and recoloring the page numbers that are correct using a GREP search for bold words (\b\w+\b). What I can't figure out is how to select the "bad" page numbers that have only some numbers and make the entire "word" bold. Any ideas? It would be nice not to have to fix them manually.
I just tried this on a document and added a few numbers that were only partially bold.
I was able to fix it by doing a search for only digits with (\b\d+\b), changing all to $1. I left find format blank and change format to regular font. This changed all numbers to regular with no mixed bold and regular.
After that you can run the same find and replace again but switching format to bold. This will change all numbers to be fully bold.
It heavily depends on the text you have. If it's just one first digit that need to change, if you don't use character styles, if you have no digits in your body text, if the font you're using has the common names for styles, if ... there is a lot of 'if's, actually. I'd recommend to share a sample of your file (IDML).
So, here is the script that could do the job (if all of those "if"'s are true):
var doc = app.activeDocument;
var styles = doc.characterStyles;
// STEP 1 -- apply style1 (regular) to all regular numbers \d\d+
var style1 = styles.add();
style1.name = 'digits_regular';
style1.fontStyle = 'Regular';
app.findGrepPreferences = NothingEnum.nothing;
app.findGrepPreferences.findWhat = '\\b\\d\\d+'; // two or more digits
app.findGrepPreferences.fontStyle = 'Regular';
app.changeGrepPreferences.changeTo = '$0';
app.changeGrepPreferences.appliedCharacterStyle = style1;
doc.changeGrep();
// STEP 2 -- apply style2 (italic) to all italic numbers \d\d+
var style2 = styles.add();
style2.name = 'digits_italic';
style2.fontStyle = 'Italic';
app.findGrepPreferences = NothingEnum.nothing;
app.findGrepPreferences.findWhat = '\\b\\d\\d+';
app.findGrepPreferences.fontStyle = 'Italic';
app.changeGrepPreferences.changeTo = '$0';
app.changeGrepPreferences.appliedCharacterStyle = style2;
doc.changeGrep();
// STEP 3 -- apply style3 (bold) to all unstyled numbers
var style3 = styles.add();
style3.name = 'digits_bold';
style3.fontStyle = 'Bold';
app.findGrepPreferences = NothingEnum.nothing;
app.findGrepPreferences.findWhat = '\\b\\d\\d+';
app.findGrepPreferences.appliedCharacterStyle = styles[0]; // syle '[None]'
app.changeGrepPreferences.changeTo = '$0';
app.changeGrepPreferences.appliedCharacterStyle = style3;
doc.changeGrep();
// clean prefs
app.findGrepPreferences = NothingEnum.nothing;
Input:
Result:
Then you can remove the character styles you don't need them. But I'd recommend to use styles. They make the life easier exactly in such cases.
It's much easier to use the Find/Change interface in Indesign.

kendo ui editor how to modify user selection with range object

Kendo UI 2015.2.805 Kendo UI Editor for Jacascript
I want to extend the kendo ui editor by adding a custom tool that will convert a user selected block that spans two or more paragraphs into block of single spaced text. This can be done by locating all interior p tags and converting them into br tags, taking care not to change the first or last tag.
My problem is working with the range object.
Getting the range is easy:
var range = editor.getRange();
The range object has a start and end container, and a start and end offset (within that container). I can access the text (without markup)
console.log(range.toString());
Oddly, other examples I have seen, including working examples, show that
console.log(range);
will dump the text, however that does not work in my project, I just get the word 'Range', which is the type of the object. This concerns me.
However, all I really need however is a start and end offset in the editor's markup (editor.value()) then I can locate and change the p's to br's.
I've read the telerik documentation and the referenced quirksmode site's explanation of html ranges, and while informative nothing shows how to locate the range withing the text (which seems pretty basic to me).
I suspect I'm overlooking something simple.
Given a range object how can I locate the start and end offset within the editor's content?
EDIT: After additional research it appears much more complex than I anticipated. It seems I must deal with the range and/or selection objects rather than directly with the editor content. Smarter minds than I came up with the range object for reasons I cannot fathom.
Here is what I have so far:
var range = letterEditor.editor.getRange();
var divSelection;
divSelection = range.cloneRange();
//cloning may be needless extra work...
//here manipulate the divSelection to how I want it.
//divSeletion is a range, not sure how to manipulate it
var sel = letterEditor.editor.getSelection()
sel.removeAllRanges();
sel.addRange(divSelection);
EDIT 2:
Based on Tim Down's Solution I came up with this simple test:
var html;
var sel = letterEditor.editor.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
html = html.replace("</p><p>", "<br/>")
var range = letterEditor.editor.getRange();
range.deleteContents();
var div = document.createElement("div");
div.innerHTML = html;
var frag = document.createDocumentFragment(), child;
while ((child = div.firstChild)) {
frag.appendChild(child);
}
range.insertNode(frag);
The first part, getting the html selection works fine, the second part also works however the editor inserts tags around all lines so the result is incorrect; extra lines including fragments of the selection.
The editor supports a view html popup which shows the editor content as html and it allows for editing the html. If I change the targeted p tags to br's I get the desired result. (The editor does support br as a default line feed vs p, but I want p's most of the time). That I can edit the html with the html viewer tool lets me know this is possible, I just need identify the selection start and end in the editor content, then a simple textual replacement via regex on the editor value would do the trick.
Edit 3:
Poking around kendo.all.max.js I discovered that pressing shift+enter creates a br instead of a p tag for the line feed. I was going to extend it to do just that as a workaround for the single-space tool. I would still like a solution to this if anyone knows, but for now I will instruct users to shift-enter for single spaced blocks of text.
This will accomplish it. Uses Tim Down's code to get html. RegEx could probably be made more efficient. 'Trick' is using split = false in insertHtml.
var sel = letterEditor.editor.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
var block = container.innerHTML;
var rgx = new RegExp(/<br class="k-br">/gi);
block = block.replace(rgx, "");
rgx = new RegExp(/<\/p><p>/gi);
block = block.replace(rgx, "<br/>");
rgx = new RegExp(/<\/p>|<p>/gi);
block = block.replace(rgx, "");
letterEditor.editor.exec("insertHtml", { html: block, split: false });
}

How to get the entire Visual Studio active document... with formatting

I know how to use VS Extensibility to get the entire active document's text. Unfortunately, that only gets me the text and doesn't give me the formatting, and I want that too.
I can, for example, get an IWpfTextView but once I get it, I'm not sure what to do with it. Are there examples of actually getting all the formatting from it? I'm only really interested in text foreground/background color, that's it.
Note: I need the formatted text on every edit, so unfortunately doing cut-and-paste using the clipboard is not an option.
Possibly the simplest method is to select all of the text and copy it to the clipboard. VS puts the rich text into the clipboard, so when you paste, elsewhere, you'll get the colors (assuming you handle rich text in your destination).
Here's my not-the-simplest solution. TL;DR: you can jump to the code at https://github.com/jimmylewis/GetVSTextViewFormattedTextSample.
The VS editor uses "classifications" to show segments of text which have special meaning. These classifications can then be formatted differently according to the language and user settings.
There's an API for getting the classifications in a document, but it didn't work for me. Or other people, apparently. But we can still get the classifications through an ITagAggregator<IClassificationTag>, as described in the preceding link, or right here:
[Import]
IViewTagAggregatorFactoryService tagAggregatorFactory = null;
// in some method...
var classificationAggregator = tagAggregatorFactory.CreateTagAggregator<IClassificationTag>(textView);
var wholeBufferSpan = new SnapshotSpan(textBuffer.CurrentSnapshot, 0, textBuffer.CurrentSnapshot.Length);
var tags = classificationAggregator.GetTags(wholeBufferSpan);
Armed with these, we can rebuild the document. It's important to note that some text is not classified, so you have to piece everything together in chunks.
It's also notable that at this point, we have no idea how any of these tags are formatted - i.e. the colors used during rendering. If you want to, you can define your own mapping from IClassificationType to a color of your choice. Or, we can ask VS for what it would do using an IClassificationFormatMap. Again, remember, this is affected by user settings, Light vs. Dark theme, etc.
Either way, it could look something like this:
// Magic sauce pt1: See the example repo for an RTFStringBuilder I threw together.
RTFStringBuilder sb = new RTFStringBuilder();
var wholeBufferSpan = new SnapshotSpan(textBuffer.CurrentSnapshot, 0, textBuffer.CurrentSnapshot.Length);
// Magic sauce pt2: see the example repo, but it's basically just
// mapping the spans from the snippet above with the formatting settings
// from the IClassificationFormatMap.
var textSpans = GetTextSpansWithFormatting(textBuffer);
int currentPos = 0;
var formattedSpanEnumerator = textSpans.GetEnumerator();
while (currentPos < wholeBufferSpan.Length && formattedSpanEnumerator.MoveNext())
{
var spanToFormat = formattedSpanEnumerator.Current;
if (currentPos < spanToFormat.Span.Start)
{
int unformattedLength = spanToFormat.Span.Start - currentPos;
SnapshotSpan unformattedSpan = new SnapshotSpan(textBuffer.CurrentSnapshot, currentPos, unformattedLength);
sb.AppendText(unformattedSpan.GetText(), System.Drawing.Color.Black);
}
System.Drawing.Color textColor = GetTextColor(spanToFormat.Formatting.ForegroundBrush);
sb.AppendText(spanToFormat.Span.GetText(), textColor);
currentPos = spanToFormat.Span.End;
}
if (currentPos < wholeBufferSpan.Length)
{
// append any remaining unformatted text
SnapshotSpan unformattedSpan = new SnapshotSpan(textBuffer.CurrentSnapshot, currentPos, wholeBufferSpan.Length - currentPos);
sb.AppendText(unformattedSpan.GetText(), System.Drawing.Color.Black);
}
return sb.ToString();
Hope this helps with whatever you're doing. The example repo will ask if you you want the formatted text in the clipboard after each edit, but that was just a dirty way that I could test and see that it worked. It's annoying, but it was just a PoC.

inDesign JSX scripted add of heading and content into textFrame

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.

Resources