Using the OpenXML SDK, 2.0 CTP, I am trying to programmatically create a Word document. In my document I have to insert a bulleted list, an some of the elements of the list must be underlined. How can I do this?
Lists in OpenXML are a little confusing.
There is a NumberingDefinitionsPart that describes all of the lists in the document. It contains information on how the lists should appear (bulleted, numbered, etc.) and also assigns and ID to each one.
Then in the MainDocumentPart, for every item in the list you want to create, you add a new paragraph and assign the ID of the list you want to that paragraph.
So to create a bullet list such as:
Hello,
world!
You would first have to create a NumberingDefinitionsPart:
NumberingDefinitionsPart numberingPart =
mainDocumentPart.AddNewPart<NumberingDefinitionsPart>("someUniqueIdHere");
Numbering element =
new Numbering(
new AbstractNum(
new Level(
new NumberingFormat() { Val = NumberFormatValues.Bullet },
new LevelText() { Val = "·" }
) { LevelIndex = 0 }
) { AbstractNumberId = 1 },
new NumberingInstance(
new AbstractNumId() { Val = 1 }
) { NumberID = 1 });
element.Save(numberingPart);
Then you create the MainDocumentPart as you normally would, except in the paragraph properties, assign the numbering ID:
MainDocumentPart mainDocumentPart =
package.AddMainDocumentPart();
Document element =
new Document(
new Body(
new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = 1 })),
new Run(
new RunProperties(),
new Text("Hello, ") { Space = "preserve" })),
new Paragraph(
new ParagraphProperties(
new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = 1 })),
new Run(
new RunProperties(),
new Text("world!") { Space = "preserve" }))));
element.Save(mainDocumentPart);
There is a better explanation of the options available in the OpenXML reference guide in Section 2.9.
I wanted something that would allow me to add more than one bullet list to a document. After banging my head against my desk for a while, I managed to combine a bunch of different posts and examine my document with the Open XML SDK 2.0 Productity Tool and figured some stuff out. The document it produces now passes validation for by version 2.0 and 2.5 of the SDK Productivity tool.
Here is the code; hopefully it saves someone some time and aggravation.
Usage:
const string fileToCreate = "C:\\temp\\bulletTest.docx";
if (File.Exists(fileToCreate))
File.Delete(fileToCreate);
var writer = new SimpleDocumentWriter();
List<string> fruitList = new List<string>() { "Apple", "Banana", "Carrot"};
writer.AddBulletList(fruitList);
writer.AddParagraph("This is a spacing paragraph 1.");
List<string> animalList = new List<string>() { "Dog", "Cat", "Bear" };
writer.AddBulletList(animalList);
writer.AddParagraph("This is a spacing paragraph 2.");
List<string> stuffList = new List<string>() { "Ball", "Wallet", "Phone" };
writer.AddBulletList(stuffList);
writer.AddParagraph("Done.");
writer.SaveToFile(fileToCreate);
Using statements:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
Code
public class SimpleDocumentWriter : IDisposable
{
private MemoryStream _ms;
private WordprocessingDocument _wordprocessingDocument;
public SimpleDocumentWriter()
{
_ms = new MemoryStream();
_wordprocessingDocument = WordprocessingDocument.Create(_ms, WordprocessingDocumentType.Document);
var mainDocumentPart = _wordprocessingDocument.AddMainDocumentPart();
Body body = new Body();
mainDocumentPart.Document = new Document(body);
}
public void AddParagraph(string sentence)
{
List<Run> runList = ListOfStringToRunList(new List<string> { sentence});
AddParagraph(runList);
}
public void AddParagraph(List<string> sentences)
{
List<Run> runList = ListOfStringToRunList(sentences);
AddParagraph(runList);
}
public void AddParagraph(List<Run> runList)
{
var para = new Paragraph();
foreach (Run runItem in runList)
{
para.AppendChild(runItem);
}
Body body = _wordprocessingDocument.MainDocumentPart.Document.Body;
body.AppendChild(para);
}
public void AddBulletList(List<string> sentences)
{
var runList = ListOfStringToRunList(sentences);
AddBulletList(runList);
}
public void AddBulletList(List<Run> runList)
{
// Introduce bulleted numbering in case it will be needed at some point
NumberingDefinitionsPart numberingPart = _wordprocessingDocument.MainDocumentPart.NumberingDefinitionsPart;
if (numberingPart == null)
{
numberingPart = _wordprocessingDocument.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>("NumberingDefinitionsPart001");
Numbering element = new Numbering();
element.Save(numberingPart);
}
// Insert an AbstractNum into the numbering part numbering list. The order seems to matter or it will not pass the
// Open XML SDK Productity Tools validation test. AbstractNum comes first and then NumberingInstance and we want to
// insert this AFTER the last AbstractNum and BEFORE the first NumberingInstance or we will get a validation error.
var abstractNumberId = numberingPart.Numbering.Elements<AbstractNum>().Count() + 1;
var abstractLevel = new Level(new NumberingFormat() {Val = NumberFormatValues.Bullet}, new LevelText() {Val = "·"}) {LevelIndex = 0};
var abstractNum1 = new AbstractNum(abstractLevel) {AbstractNumberId = abstractNumberId};
if (abstractNumberId == 1)
{
numberingPart.Numbering.Append(abstractNum1);
}
else
{
AbstractNum lastAbstractNum = numberingPart.Numbering.Elements<AbstractNum>().Last();
numberingPart.Numbering.InsertAfter(abstractNum1, lastAbstractNum);
}
// Insert an NumberingInstance into the numbering part numbering list. The order seems to matter or it will not pass the
// Open XML SDK Productity Tools validation test. AbstractNum comes first and then NumberingInstance and we want to
// insert this AFTER the last NumberingInstance and AFTER all the AbstractNum entries or we will get a validation error.
var numberId = numberingPart.Numbering.Elements<NumberingInstance>().Count() + 1;
NumberingInstance numberingInstance1 = new NumberingInstance() {NumberID = numberId};
AbstractNumId abstractNumId1 = new AbstractNumId() {Val = abstractNumberId};
numberingInstance1.Append(abstractNumId1);
if (numberId == 1)
{
numberingPart.Numbering.Append(numberingInstance1);
}
else
{
var lastNumberingInstance = numberingPart.Numbering.Elements<NumberingInstance>().Last();
numberingPart.Numbering.InsertAfter(numberingInstance1, lastNumberingInstance);
}
Body body = _wordprocessingDocument.MainDocumentPart.Document.Body;
foreach (Run runItem in runList)
{
// Create items for paragraph properties
var numberingProperties = new NumberingProperties(new NumberingLevelReference() {Val = 0}, new NumberingId() {Val = numberId});
var spacingBetweenLines1 = new SpacingBetweenLines() { After = "0" }; // Get rid of space between bullets
var indentation = new Indentation() { Left = "720", Hanging = "360" }; // correct indentation
ParagraphMarkRunProperties paragraphMarkRunProperties1 = new ParagraphMarkRunProperties();
RunFonts runFonts1 = new RunFonts() { Ascii = "Symbol", HighAnsi = "Symbol" };
paragraphMarkRunProperties1.Append(runFonts1);
// create paragraph properties
var paragraphProperties = new ParagraphProperties(numberingProperties, spacingBetweenLines1, indentation, paragraphMarkRunProperties1);
// Create paragraph
var newPara = new Paragraph(paragraphProperties);
// Add run to the paragraph
newPara.AppendChild(runItem);
// Add one bullet item to the body
body.AppendChild(newPara);
}
}
public void Dispose()
{
CloseAndDisposeOfDocument();
if (_ms != null)
{
_ms.Dispose();
_ms = null;
}
}
public MemoryStream SaveToStream()
{
_ms.Position = 0;
return _ms;
}
public void SaveToFile(string fileName)
{
if (_wordprocessingDocument != null)
{
CloseAndDisposeOfDocument();
}
if (_ms == null)
throw new ArgumentException("This object has already been disposed of so you cannot save it!");
using (var fs = File.Create(fileName))
{
_ms.WriteTo(fs);
}
}
private void CloseAndDisposeOfDocument()
{
if (_wordprocessingDocument != null)
{
_wordprocessingDocument.Close();
_wordprocessingDocument.Dispose();
_wordprocessingDocument = null;
}
}
private static List<Run> ListOfStringToRunList(List<string> sentences)
{
var runList = new List<Run>();
foreach (string item in sentences)
{
var newRun = new Run();
newRun.AppendChild(new Text(item));
runList.Add(newRun);
}
return runList;
}
}
Adam's answer above is correct except it is new NumberingInstance( instead of new Num( as noted in a comment.
Additionally, if you have multiple lists, you should have multiple Numbering elements (each with it's own id eg 1, 2, 3 etc -- one for each list in the document. This doesn't seem to be a problem with bullet lists, but numbered lists will continue using the same numbering sequence (as opposed to starting over again at 1) because it will think that it's the same list. The NumberingId has to be referenced in your paragraph like this:
ParagraphProperties paragraphProperties1 = new ParagraphProperties();
ParagraphStyleId paragraphStyleId1 = new ParagraphStyleId() { Val = "ListParagraph" };
NumberingProperties numberingProperties1 = new NumberingProperties();
NumberingLevelReference numberingLevelReference1 = new NumberingLevelReference() { Val = 0 };
NumberingId numberingId1 = new NumberingId(){ Val = 1 }; //Val is 1, 2, 3 etc based on your numberingid in your numbering element
numberingProperties1.Append(numberingLevelReference1);
numberingProperties1.Append(numberingId1);
paragraphProperties1.Append(paragraphStyleId1);
paragraphProperties1.Append(numberingProperties1);
Children of the Level element will have an effect on the type of bullet, and the indentation.
My bullets were too small until I added this to the Level element:
new NumberingSymbolRunProperties(
new RunFonts() { Hint = FontTypeHintValues.Default, Ascii = "Symbol", HighAnsi = "Symbol" })
Indentation was a problem until I added this element to the Level element as well:
new PreviousParagraphProperties(
new Indentation() { Left = "864", Hanging = "360" })
And if you are like me - creating a document from a template, then you may want to use this code, to handle both situations - when your template does or does not contain any numbering definitions:
// Introduce bulleted numbering in case it will be needed at some point
NumberingDefinitionsPart numberingPart = document.MainDocumentPart.NumberingDefinitionsPart;
if (numberingPart == null)
{
numberingPart = document.MainDocumentPart.AddNewPart<NumberingDefinitionsPart>("NumberingDefinitionsPart001");
}
Not sure if this helps anyone, but here is my snippet for inserting a list of bullets.
Create the word processing document and word document body
WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open($"{tempFolder}{tempFileName}", true);
Body wordDocumentBody = wordprocessingDocument.MainDocumentPart.Document.Body;
Then get the insert index. This is placeholder text inside the word document, so you can insert into the correct place in the word document.
int insertIndex = wordDocumentBody.ToList().IndexOf(wordDocumentBody.Where(p => p.InnerText.Contains("PLACEHOLDER_TEXT")).First());
You can then call this method to insert into the word document in both bullet points for lettering.
public static void InsertBullets(Body wordDocumentBody, int insertIndex, int bulletStyle, List<string> strToAdd)
{
//// Bullet Styles:
// 1 - Standard bullet
// 2 - Numbered
foreach (string item in strToAdd)
{
Paragraph para = wordDocumentBody.InsertAt(new Paragraph(), insertIndex);
ParagraphProperties paragraphProperties = new ParagraphProperties();
paragraphProperties.Append(new ParagraphStyleId() { Val = "ListParagraph" });
paragraphProperties.Append(new NumberingProperties(
new NumberingLevelReference() { Val = 0 },
new NumberingId() { Val = bulletStyle }));
para.Append(paragraphProperties);
para.Append(new Run(
new RunProperties(
new RunStyle() { Val = "ListParagraph" },
new NoProof()),
new Text($"{item}")));
insertIndex++;
}
}
You can then remove the placeholder text after with this.
wordDocumentBody.Elements().ElementAt(wordDocumentBody.ToList().IndexOf(wordDocumentBody.Where(p => p.InnerText.Contains("PLACEHOLDER_TEXT")).First())).Remove();
Related
This question is a follow-up from this
After the previous post I managed to create the following method that fits text in certain spaces in paragraphs.
public static void getPlainFill2(String str, Document doc, PdfDocument document, Paragraph root,
Paragraph space, boolean isCentred) {
// System.out.println("prevText: "+prev.getText());
float width = doc.getPageEffectiveArea(PageSize.A4).getWidth();
float height = doc.getPageEffectiveArea(PageSize.A4).getHeight();
if (str.isEmpty() || str.isBlank()) {
str = "________";
}
IRenderer spaceRenderer = space.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult spaceResult = spaceRenderer
.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
Rectangle rectSpaceBox = ((ParagraphRenderer) spaceRenderer).getOccupiedArea().getBBox();
float writingWidth = rectSpaceBox.getWidth();
float writingHeight = rectSpaceBox.getHeight();
Rectangle remaining = doc.getRenderer().getCurrentArea().getBBox();
float yReal = remaining.getTop() + 2f;// orig 4f
float sizet = 0;
for (int i = 0; i < root.getChildren().size(); i++) {
IElement e = root.getChildren().get(i);
if (e.equals(space)) {
break;
}
IRenderer ss = e.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult ss2 = ss.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
sizet += ss.getOccupiedArea().getBBox().getWidth();
System.out.println("width: " + width + " current: " + sizet);
}
float start = sizet+doc.getLeftMargin();
if(isCentred)
start = (width - getRealWidth(doc, root,width,height))/2+doc.getLeftMargin()+sizet;
Rectangle towr = new Rectangle(start, yReal, writingWidth, writingHeight);// sizet+doc.getLeftMargin()
PdfCanvas pdfcanvas = new PdfCanvas(document.getFirstPage());
Canvas canvas = new Canvas(pdfcanvas, towr);
canvas.setTextAlignment(TextAlignment.CENTER);
canvas.setHorizontalAlignment(HorizontalAlignment.CENTER);
Paragraph paragraph = new Paragraph(str).setTextAlignment(TextAlignment.CENTER).setBold();//.setMultipliedLeading(0.9f);
Div lineDiv = new Div();
lineDiv.setVerticalAlignment(VerticalAlignment.MIDDLE);
lineDiv.add(paragraph);
float fontSizeL = 1f;
float fontSizeR = 12;
int adjust = 0;
while (Math.abs(fontSizeL - fontSizeR) > 1e-1) {
float curFontSize = (fontSizeL + fontSizeR) / 2;
lineDiv.setFontSize(curFontSize);
// It is important to set parent for the current element renderer to a root
// renderer
IRenderer renderer = lineDiv.createRendererSubTree().setParent(canvas.getRenderer());
LayoutContext context = new LayoutContext(new LayoutArea(1, towr));
if (renderer.layout(context).getStatus() == LayoutResult.FULL) {
// we can fit all the text with curFontSize
fontSizeL = curFontSize;
} else {
fontSizeR = curFontSize;
}
if(adjust>=2) {
writingHeight -=1.3f;
yReal += 1.4f;
adjust= 0;
}
}
lineDiv.setFontSize(fontSizeL);
canvas.add(lineDiv);
// border
// PdfCanvas(document.getFirstPage()).rectangle(towr).setStrokeColor(ColorConstants.BLACK).stroke();
canvas.close();
}
public static float getRealWidth (Document doc, Paragraph root,float width,float height) {
float sizet = 0;
for(int i = 0;i<root.getChildren().size();i++) {
IElement e = root.getChildren().get(i);
IRenderer ss = e.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult ss2 = ss.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width,height))));
sizet +=ss.getOccupiedArea().getBBox().getWidth();
}
return sizet;}
Now this works almost decent, there are minor issues when text scales to lower sizes and it goes like:
https://i.ibb.co/MkxfwjQ/Screenshot-from-2021-06-14-18-27-09.png (I can't post images because I have no rep.)
but the main issue is that you have to write Paragraphs line by line to work. As next example:
Cell cell3 = new Cell();
LineCountingParagraph line3 = new LineCountingParagraph("");
Text ch07 = new Text("Paragraph Prev ");
line3.add(ch07);
Paragraph nrZile = getEmptySpace(15);
line3.add(nrZile);
Text ch08 = new Text("afterStr, textasdsadasdas ");
line3.add(ch08);
Paragraph data = getEmptySpace(18);
line3.add(data);
Text ch09 = new Text(".\n");
line3.add(ch09);
line3.setTextAlignment(TextAlignment.CENTER);
cell3.add(line3);
doc.add(cell3);
getPlainFill2("thisisalongstring", doc, document, line3, nrZile, true);
getPlainFill2("1333", doc, document, line3, data, true);
Cell cell4 = new Cell();
LineCountingParagraph line4 = new LineCountingParagraph("");
Paragraph loc2 = getEmptySpace(30);
line4.add(loc2);
Text pr32 = new Text(" aasdbsadasd ");
line4.add(pr32);
Paragraph nr2 = getEmptySpace(8);
line4.add(nr2);
Text pr33 = new Text(" asdasdasdasd.\n");
line4.add(pr33);
line4.setTextAlignment(TextAlignment.CENTER);
cell4.add(line4);
doc.add(cell4);
getPlainFill2("1333", doc, document, line4, nr2, true);
If you need more code, I'll upload it somewhere.
Now is there a way to insert text within the same paragraph on multiple lines ? because there seems I cannot find a way to detect line break in IText 7.1.11.
Full code:
package pdfFill;
import java.io.File;
import java.io.IOException;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Div;
import com.itextpdf.layout.element.IElement;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Text;
import com.itextpdf.layout.layout.LayoutArea;
import com.itextpdf.layout.layout.LayoutContext;
import com.itextpdf.layout.layout.LayoutResult;
import com.itextpdf.layout.property.HorizontalAlignment;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;
import com.itextpdf.layout.renderer.DrawContext;
import com.itextpdf.layout.renderer.IRenderer;
import com.itextpdf.layout.renderer.ParagraphRenderer;
public class Newway4 {
public static void main(String[] args) {
PdfWriter writer;
try {
writer = new PdfWriter(new File("test4.pdf"));
PdfDocument document = new PdfDocument(writer);
document.getDocumentInfo().addCreationDate();
document.getDocumentInfo().setAuthor("Piri");
document.getDocumentInfo().setTitle("Test_Stackoverflow");
document.setDefaultPageSize(PageSize.A4);
Document doc = new Document(document);
doc.setFontSize(12);
final Paragraph titlu = new Paragraph();
final Text t1 = new Text("\n\n\n\nTest Stackoverflow\n\n\n").setBold().setUnderline();
titlu.setHorizontalAlignment(HorizontalAlignment.CENTER);
titlu.setTextAlignment(TextAlignment.CENTER);
titlu.add(t1).setBold();
doc.add(titlu);
Cell cell1 = new Cell();
LineCountingParagraph line1 = new LineCountingParagraph("");
line1.add( addTab());
Text ch01 = new Text("This is the 1st example ");
line1.add(ch01);
Paragraph name = getEmptySpace(42);
line1.add(name);// cnp new line
Text ch02 = new Text(" that works ");
line1.add(ch02);
Paragraph domiciliu = getEmptySpace(63);
line1.add(domiciliu);
/* Text ch03 = new Text("\njudet");
line1.add(ch03);
Paragraph judet = getEmptySpace(12);
line1.add(judet);*/
Text ch031 = new Text("\n");
line1.add(ch031);
cell1.add(line1);
doc.add(cell1);
getPlainFill2("with insertion str", doc, document, line1, name, false);
getPlainFill2("because is writtin line by line", doc, document, line1, domiciliu, false);
Cell cell2 = new Cell();
LineCountingParagraph line2 = new LineCountingParagraph("");
Text p51 = new Text("as you can see in this");
line2.add(p51);
Paragraph localitatea = getEmptySpace(30);
line2.add(localitatea);
Text p7 = new Text(" and ");
line2.add(p7);
Paragraph nrCasa =getEmptySpace(8);
line2.add(nrCasa);
Text p09 = new Text(" of text scalling ");
line2.add(p09);
Paragraph telefon = getEmptySpace(22);
line2.add(telefon);
Text p11 = new Text(".");
line2.add(p11);
line2.setTextAlignment(TextAlignment.CENTER);
cell2.add(line2);
doc.add(cell2);
getPlainFill2("sentence", doc, document, line2, localitatea, true);
getPlainFill2("example", doc, document, line2, nrCasa, true);
getPlainFill2("text scalling bla bla", doc, document, line2, telefon, true);
doc.add(new Paragraph("\n\n\n"));
LineCountingParagraph paragraphTest = new LineCountingParagraph("");
paragraphTest.add(addTab());
Text testch01 = new Text("This is the 2nd example ");
paragraphTest.add(testch01);
Paragraph emptyTest01 = getEmptySpace(42);
paragraphTest.add(emptyTest01);
Text testch02 = new Text(" that doesn't work ");
paragraphTest.add(testch02);
Paragraph emptyTest02 = getEmptySpace(53);
paragraphTest.add(emptyTest02);
Text testch04 = new Text(" this next goes to the next line but ");
paragraphTest.add(testch04);
Paragraph emptyTest03 = getEmptySpace(42);
paragraphTest.add(emptyTest03);
Text testch05 = new Text(" won't appear !!");
paragraphTest.add(testch05);
doc.add(paragraphTest);
getPlainFill2("with insertion str", doc, document, paragraphTest, emptyTest01, false);
getPlainFill2("because next text goes next line", doc, document, paragraphTest, emptyTest02, false);
getPlainFill2("this text", doc, document, paragraphTest, emptyTest03, false);
doc.close();
writer.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static String getStrWithDots(final int dots, final String str) {
final int strSize = str.length();
final StringBuilder sb = new StringBuilder();
int dotsRemained;
if (strSize > dots) {
dotsRemained = 0;
} else {
dotsRemained = dots - strSize;
}
for (int i = 0; i < dotsRemained; ++i) {
if (i == dotsRemained / 2) {
sb.append(str);
}
sb.append(".");
}
return sb.toString();
}
public static void getPlainFill2(String str, Document doc, PdfDocument document, Paragraph root,
Paragraph space, boolean isCentred) {
// System.out.println("prevText: "+prev.getText());
float width = doc.getPageEffectiveArea(PageSize.A4).getWidth();
float height = doc.getPageEffectiveArea(PageSize.A4).getHeight();
if (str.isEmpty() || str.isBlank()) {
str = "________";
}
IRenderer spaceRenderer = space.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult spaceResult = spaceRenderer
.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
Rectangle rectSpaceBox = ((ParagraphRenderer) spaceRenderer).getOccupiedArea().getBBox();
float writingWidth = rectSpaceBox.getWidth();
float writingHeight = rectSpaceBox.getHeight();
Rectangle remaining = doc.getRenderer().getCurrentArea().getBBox();
float yReal = remaining.getTop() + 2f;// orig 4f
float sizet = 0;
for (int i = 0; i < root.getChildren().size(); i++) {
IElement e = root.getChildren().get(i);
if (e.equals(space)) {
break;
}
IRenderer ss = e.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult ss2 = ss.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width, height))));
sizet += ss.getOccupiedArea().getBBox().getWidth();
}
float start = sizet+doc.getLeftMargin();
if(isCentred)
start = (width - getRealWidth(doc, root,width,height))/2+doc.getLeftMargin()+sizet;
Rectangle towr = new Rectangle(start, yReal, writingWidth, writingHeight);// sizet+doc.getLeftMargin()
PdfCanvas pdfcanvas = new PdfCanvas(document.getFirstPage());
Canvas canvas = new Canvas(pdfcanvas, towr);
canvas.setTextAlignment(TextAlignment.CENTER);
canvas.setHorizontalAlignment(HorizontalAlignment.CENTER);
Paragraph paragraph = new Paragraph(str).setTextAlignment(TextAlignment.CENTER).setBold();//.setMultipliedLeading(0.9f);//setbold oprtional
Div lineDiv = new Div();
lineDiv.setVerticalAlignment(VerticalAlignment.MIDDLE);
lineDiv.add(paragraph);
float fontSizeL = 0.0001f, fontSizeR= 10000;
int adjust = 0;
while (Math.abs(fontSizeL - fontSizeR) > 1e-1) {
float curFontSize = (fontSizeL + fontSizeR) / 2;
lineDiv.setFontSize(curFontSize);
// It is important to set parent for the current element renderer to a root
// renderer
IRenderer renderer = lineDiv.createRendererSubTree().setParent(canvas.getRenderer());
LayoutContext context = new LayoutContext(new LayoutArea(1, towr));
if (renderer.layout(context).getStatus() == LayoutResult.FULL) {
// we can fit all the text with curFontSize
fontSizeL = curFontSize;
if (++adjust>1)
towr.setHeight(towr.getHeight()-0.90f);
} else {
fontSizeR = curFontSize;
}
}
lineDiv.setFontSize(fontSizeL);
canvas.add(lineDiv);
new PdfCanvas(document.getFirstPage()).rectangle(towr).setStrokeColor(ColorConstants.BLACK).stroke();
canvas.close();
}
public static Text addTab() {
StringBuilder sb = new StringBuilder();
for(int i = 0;i<8;i++)
sb.append("\u00a0");
return new Text(sb.toString());
}
public static float getRealWidth (Document doc, Paragraph root,float width,float height) {
float sizet = 0;
for(int i = 0;i<root.getChildren().size();i++) {
IElement e = root.getChildren().get(i);
IRenderer ss = e.createRendererSubTree().setParent(doc.getRenderer());
LayoutResult ss2 = ss.layout(new LayoutContext(new LayoutArea(1, new Rectangle(width,height))));
sizet +=ss.getOccupiedArea().getBBox().getWidth();
}
return sizet;
}
private static Paragraph getEmptySpace(int size) {
Paragraph space = new Paragraph();
space.setMaxWidth(size);
for(int i=0;i<size;i++) {
// par.add("\u00a0");
space.add("\u00a0");
}
return space;
}
private static class LineCountingParagraph extends Paragraph {
private int linesWritten = 0;
public LineCountingParagraph(String text) {
super(text);
}
public void addWrittenLines(int toAdd) {
linesWritten += toAdd;
}
public int getNumberOfWrittenLines() {
return linesWritten;
}
#Override
protected IRenderer makeNewRenderer() {
return new LineCountingParagraphRenderer(this);
}
}
private static class LineCountingParagraphRenderer extends ParagraphRenderer {
public LineCountingParagraphRenderer(LineCountingParagraph modelElement) {
super(modelElement);
}
#Override
public void drawChildren(DrawContext drawContext) {
((LineCountingParagraph)modelElement).addWrittenLines(lines.size());
super.drawChildren(drawContext);
}
#Override
public IRenderer getNextRenderer() {
return new LineCountingParagraphRenderer((LineCountingParagraph) modelElement);
}
}
}
The issue: in the top half of the PDF you can see the result of two LineCountingParagraph instances being created, one per line. In the bottom half of the PDF you can see the result when only one instance of LineCountingParagraph is created. So fitting the text in boxes does not work well in case content of the paragraph wraps to the next line.
You have unnecessarily complicated things in such a way that we have to start from scratch :)
So the goal is to be able to create paragraphs with boxed intrusions of fixed width, where we need to copy-fit (place) some text, making sure the font size is selected in such a way that the text fits into that box.
The result should look similar to this picture:
The idea is that we will add paragraphs of fixed width into our main paragraph (iText allows adding block elements and a paragraph is a block element - into paragraphs). The fixed width will be guaranteed by the contents of our paragraph - it will just contain non-breakable spaces. Our paragraph will actually be backed by another paragraph with the actual content we want to fit into our wrapping paragraph. During the layout of the wrapping paragraph we will know its effective boundary and we will just use that area to determine the right font size for our content paragraph using binary search algorithm. Once the right font size has been determined we will just make sure the paragraph with the content gets drawn right next to our wrapping paragraph.
The code for our wrapping paragraph is pretty simple. It just expects the underlying paragraph with real content as the parameter. As always with iText layout, we should customize the renderer of our autoscaling paragraph:
private static class AutoScalingParagraph extends Paragraph {
Paragraph innerParagraph;
public AutoScalingParagraph(Paragraph innerParagraph) {
this.innerParagraph = innerParagraph;
}
#Override
protected IRenderer makeNewRenderer() {
return new AutoScalingParagraphRenderer(this);
}
}
private static class AutoScalingParagraphRenderer extends ParagraphRenderer {
private IRenderer innerRenderer;
public AutoScalingParagraphRenderer(AutoScalingParagraph modelElement) {
super(modelElement);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult baseResult = super.layout(layoutContext);
this.innerRenderer = ((AutoScalingParagraph)modelElement).innerParagraph.createRendererSubTree().setParent(this);
if (baseResult.getStatus() == LayoutResult.FULL) {
float fontSizeL = 0.0001f, fontSizeR= 10000;
while (Math.abs(fontSizeL - fontSizeR) > 1e-1) {
float curFontSize = (fontSizeL + fontSizeR) / 2;
this.innerRenderer.setProperty(Property.FONT_SIZE, UnitValue.createPointValue(curFontSize));
if (this.innerRenderer.layout(new LayoutContext(getOccupiedArea().clone())).getStatus() == LayoutResult.FULL) {
// we can fit all the text with curFontSize
fontSizeL = curFontSize;
} else {
fontSizeR = curFontSize;
}
}
this.innerRenderer.setProperty(Property.FONT_SIZE, UnitValue.createPointValue(fontSizeL));
this.innerRenderer.layout(new LayoutContext(getOccupiedArea().clone()));
}
return baseResult;
}
#Override
public void drawChildren(DrawContext drawContext) {
super.drawChildren(drawContext);
innerRenderer.draw(drawContext);
}
#Override
public IRenderer getNextRenderer() {
return new AutoScalingParagraphRenderer((AutoScalingParagraph) modelElement);
}
}
Now we add the helper function for creating our wrapper paragraphs that just accepts the desired paragraph width in spaces and the underlying content we want to fit into that space:
private static Paragraph createAdjustableParagraph(int widthInSpaces, Paragraph innerContent) {
AutoScalingParagraph paragraph = new AutoScalingParagraph(innerContent);
paragraph.setBorder(new SolidBorder(1));
StringBuilder sb = new StringBuilder();
for(int i=0;i<widthInSpaces;i++) {
sb.append("\u00a0");
}
paragraph.add(sb.toString());
return paragraph;
}
Finally, the main code:
PdfWriter writer = new PdfWriter(new File("test4.pdf"));
PdfDocument document = new PdfDocument(writer);
Document doc = new Document(document);
Paragraph paragraphTest = new Paragraph();
Text testch01 = new Text("This is the 2nd example ");
paragraphTest.add(testch01);
paragraphTest.add(createAdjustableParagraph(42, new Paragraph("with insertion str")));
Text testch02 = new Text(" that doesn't work ");
paragraphTest.add(testch02);
paragraphTest.add(createAdjustableParagraph(53, new Paragraph("because next text goes next line")));
Text testch04 = new Text(" this next goes to the next line but ");
paragraphTest.add(testch04);
paragraphTest.add(createAdjustableParagraph(42, new Paragraph("this text")));
Text testch05 = new Text(" won't appear !!");
paragraphTest.add(testch05);
doc.add(paragraphTest);
doc.close();
Which gives us the following result:
So we just have the one main paragraph which contains some content and our paragraph wrappers, which in tern have the underlying content we want to fit.
Hint: centering the text is very easy, you don't need to calculate coordinates etc. Just set the right property to the paragraph with the content that you feed to your wrapper paragraph:
paragraphTest.add(createAdjustableParagraph(42, new Paragraph("this text").setTextAlignment(TextAlignment.CENTER)));
And you get the result:
I want to set page number when I merge pdf files. The page number paragraph will defind by someone to custom style what he want. Now I can add text to the paragraph(like doc.add(paragraph.add(text))), but I can not replace it.
public static byte[] mergePdf(Map<String, PdfDocument> filesToMerge, Paragraph paragraph) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(baos));
Document doc = new Document(pdfDoc);
pdfDoc.initializeOutlines();
PdfPageFormCopier formCopier = new PdfPageFormCopier();
int page = 1;
for (Map.Entry<String, PdfDocument> entry : filesToMerge.entrySet()) {
String title = entry.getKey();
PdfDocument srcDoc = entry.getValue();
int numberOfPages = srcDoc.getNumberOfPages();
for (int i = 1; i <= numberOfPages; i++, page++) {
Text text = new Text(String.format("page %d", page));
srcDoc.copyPagesTo(i, i, pdfDoc, formCopier);
if (i == 1) {
text.setDestination("p" + page);
PdfOutline rootOutLine = pdfDoc.getOutlines(false);
PdfOutline outline = rootOutLine.addOutline(title);
outline.addDestination(PdfDestination.makeDestination(new PdfString("p" + page)));
}
// I want do like "doc.add(paragraph.set(text))";
// paragraph already have been set position,font,fontSize and so on. SO I dont want to "new Paragraph(text)"
doc.add(paragraph.add(text));
}
}
for (PdfDocument srcDoc : filesToMerge.values()) {
srcDoc.close();
}
doc.close();
return baos.toByteArray();
}
I am the questioner. At last I resolve the question do like this:
public class MyParagraph extends Paragraph {
public MyParagraph() {
}
public void setContent(Text text) {
List<IElement> children = this.getChildren();
if (!children.isEmpty()) {
children.clear();
}
children.add(text);
}
}
I need to update the Styles (styles.xml) part of an MS Word document due to a problem with a vendor's product.
So far I've been able to extract and update the xml I need. The only problem, is that I don't know how to save my changes back to the document.
The code below is working just fine. I usually output the xml to the console to make sure it's going in just fine. At the end, I know I need to perform some save operation, but the XDocument.Save( /stream/) hasn't worked.
Here's where I am so far
static void FixNormal()
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(_path, true))
{
// Get the Styles part for this document.
StyleDefinitionsPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (stylesPart == null)
{
Console.WriteLine("No Style Part");
}
else
{
XDocument stylesDoc;
using (var reader = XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
Console.WriteLine(stylesPart.Styles.OuterXml);
// Create the XDocument.
stylesDoc = XDocument.Load(reader);
var xStyle = stylesDoc.Descendants(w + "styles").Descendants(w + "style").Where(x => x.Attribute(w + "styleId").Value.Equals("Normal"));
XElement style = xStyle.Single();
var q = style.Descendants(w + "qFormat").FirstOrDefault();
if (q is null)
{
XElement qFormat = new XElement(w + "qFormat");
style.Add(qFormat);
}
var r = style.Descendants(w + "rsid").FirstOrDefault();
if (r is null)
{
XElement rsid = new XElement(w + "rsid");
XAttribute val = new XAttribute(w + "val", "003C4F1E");
rsid.Add(val);
style.Add(rsid);
}
}
//doc.Save(); --- Did not work
}
}
}
I found the answer in the SAVE THE PARTS section of this page Replace the styles parts in a word processing document (Open XML SDK)
See the end of this code for the solution. You'll also see what I've tried.
static void FixNormal()
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(_path, true))
{
// Get the Styles part for this document.
StyleDefinitionsPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (stylesPart == null)
{
Console.WriteLine("No Style Part");
}
else
{
XDocument stylesDoc;
using (var reader = XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
// Create the XDocument.
stylesDoc = XDocument.Load(reader);
var xStyle = stylesDoc.Descendants(w + "styles").Descendants(w + "style").Where(x => x.Attribute(w + "styleId").Value.Equals("Normal"));
XElement style = xStyle.Single();
var q = style.Descendants(w + "qFormat").FirstOrDefault();
if (q is null)
{
XElement qFormat = new XElement(w + "qFormat");
style.Add(qFormat);
}
var r = style.Descendants(w + "rsid").FirstOrDefault();
if (r is null)
{
XElement rsid = new XElement(w + "rsid");
XAttribute val = new XAttribute(w + "val", "003C4F1E");
rsid.Add(val);
style.Add(rsid);
}
}
//doc.Save(); --- Did not work
//stylesDoc.Save(#"C:\WinTest\HooRah.xml"); -- I only use this to verify that I've updated everything correctly
//using (XmlWriter xw = XmlWriter.Create(stylesPart.GetStream(FileMode.Create, FileAccess.Write)))
//{
// stylesDoc.Save(xw); -- DID NOT WORK EITHER
// doc.Save();
//}
// THIS WORKED
stylesDoc.Save(new StreamWriter(stylesPart.GetStream(FileMode.Create, FileAccess.Write)));
}
}
}
I was under the impression that it is now possible to copy AcroFields using PdfCopy. In the release notes for iText 5.4.4.0 this is listed as possible now. However, when I try to do so it appears all the annotations (I think I am using that term correctly, still fairly new to iText...) for the fields are stripped out. It looks like the fields are there (meaning I can see the blue boxes that indicate an editable field), but they are not editable. If I try to bring the PDF up in Acrobat I get a message saying that "there are no fields, would you like Acrobat to discover them?" and most are found and marked and fields properly (check boxes aren't, but the text fields are).
I assume there is an additional step somewhere along the lines to re-add the annotations to the PdfCopy object, but I do not see a way to get the annotations from the PdfReader. I also cannot seem to find any documentation on how to do this (since AcroFields were for so long not supported in PdfCopy most of what I find is along that vein).
Due to sensitivity I cannot provide a copy of the PDF's in question, but using an altered version of a test program used earlier you can see the issue with the following code. It should generate a table with some check boxes in the four right columns. If I use the exact same code with PdfCopyFields in the MergePdfs method instead of PdfCopy it works as expected. This code does not produce any text fields, but in my main project they are part of the original parent PDF that is used as a template.
(Sorry for the long example, it has been cherry picked from a much larger application. You will need a PDF with a field named "TableStartPosition" somewhere in it and update RunTest with the correct paths for your local machine to get this to work.)
Has the PdfCopy functionality not made it into iTextSharp yet? I am using version 5.4.5.0.
class Program
{
Stream _pdfTemplateStream;
MemoryStream _pdfResultStream;
PdfReader _pdfTemplateReader;
PdfStamper _pdfResultStamper;
static void Main(string[] args)
{
Program p = new Program();
try
{
p.RunTest();
}
catch (Exception f)
{
Console.WriteLine(f.Message);
Console.ReadLine();
}
}
internal void RunTest()
{
FileStream fs = File.OpenRead(#"C:\temp\a\RenameFieldTest\RenameFieldTest\Library\CoverPage.pdf");
_pdfTemplateStream = fs;
_pdfResultStream = new MemoryStream();
//PDFTemplateStream = new FileStream(_templatePath, FileMode.Open);
_pdfTemplateReader = new PdfReader(_pdfTemplateStream);
_pdfResultStamper = new PdfStamper(_pdfTemplateReader, _pdfResultStream);
#region setup objects
List<CustomCategory> Categories = new List<CustomCategory>();
CustomCategory c1 = new CustomCategory();
c1.CategorySizesInUse.Add(CustomCategory.AvailableSizes[1]);
c1.CategorySizesInUse.Add(CustomCategory.AvailableSizes[2]);
Categories.Add(c1);
CustomCategory c2 = new CustomCategory();
c2.CategorySizesInUse.Add(CustomCategory.AvailableSizes[0]);
c2.CategorySizesInUse.Add(CustomCategory.AvailableSizes[1]);
Categories.Add(c2);
List<CustomObject> Items = new List<CustomObject>();
CustomObject co1 = new CustomObject();
co1.Category = c1;
co1.Title = "Object 1";
Items.Add(co1);
CustomObject co2 = new CustomObject();
co2.Category = c2;
co2.Title = "Object 2";
Items.Add(co2);
#endregion
FillCoverPage(Items);
_pdfResultStamper.Close();
_pdfTemplateReader.Close();
List<MemoryStream> pdfStreams = new List<MemoryStream>();
pdfStreams.Add(new MemoryStream(_pdfResultStream.ToArray()));
MergePdfs(#"C:\temp\a\RenameFieldTest\RenameFieldTest\Library\Outfile.pdf", pdfStreams);
_pdfResultStream.Dispose();
_pdfTemplateStream.Dispose();
}
internal void FillCoverPage(List<CustomObject> Items)
{
//Before we start we need to figure out where to start adding the table
var fieldPositions = _pdfResultStamper.AcroFields.GetFieldPositions("TableStartPosition");
if (fieldPositions == null)
{ throw new Exception("Could not find the TableStartPosition field. Unable to determine point of origin for the table!"); }
_pdfResultStamper.AcroFields.RemoveField("TableStartPosition");
var fieldPosition = fieldPositions[0];
// Get the position of the field
var targetPosition = fieldPosition.position;
//First, get all the available card sizes
List<string> availableSizes = CustomCategory.AvailableSizes;
//Generate a table with the number of available card sizes + 1 for the device name
PdfPTable table = new PdfPTable(availableSizes.Count + 1);
float[] columnWidth = new float[availableSizes.Count + 1];
for (int y = 0; y < columnWidth.Length; y++)
{
if (y == 0)
{ columnWidth[y] = 320; }
else
{ columnWidth[y] = 120; }
}
table.SetTotalWidth(columnWidth);
table.WidthPercentage = 100;
PdfContentByte canvas;
List<PdfFormField> checkboxes = new List<PdfFormField>();
//Build the header row
table.Rows.Add(new PdfPRow(this.GetTableHeaderRow(availableSizes)));
//Insert the global check boxes
PdfPCell[] globalRow = new PdfPCell[availableSizes.Count + 1];
Phrase tPhrase = new Phrase("Select/Unselect All");
PdfPCell tCell = new PdfPCell();
tCell.BackgroundColor = BaseColor.LIGHT_GRAY;
tCell.AddElement(tPhrase);
globalRow[0] = tCell;
for (int x = 0; x < availableSizes.Count; x++)
{
tCell = new PdfPCell();
tCell.BackgroundColor = BaseColor.LIGHT_GRAY;
PdfFormField f = PdfFormField.CreateCheckBox(_pdfResultStamper.Writer);
string fieldName = string.Format("InkSaver.Global.chk{0}", availableSizes[x].Replace(".", ""));
//f.FieldName = fieldName;
string js = string.Format("hideAll(event.target, '{0}');", availableSizes[x].Replace(".", ""));
f.Action = PdfAction.JavaScript(js, _pdfResultStamper.Writer);
tCell.CellEvent = new ChildFieldEvent(_pdfResultStamper.Writer, f, fieldName);
globalRow[x + 1] = tCell;
checkboxes.Add(f);
}
table.Rows.Add(new PdfPRow(globalRow));
int status = 0;
int pageNum = 1;
for (int itemIndex = 0; itemIndex < Items.Count; itemIndex++)
{
tCell = new PdfPCell();
Phrase p = new Phrase(Items[itemIndex].Title);
tCell.AddElement(p);
tCell.HorizontalAlignment = Element.ALIGN_LEFT;
PdfPCell[] cells = new PdfPCell[availableSizes.Count + 1];
cells[0] = tCell;
for (int availCardSizeIndex = 0; availCardSizeIndex < availableSizes.Count; availCardSizeIndex++)
{
if (Items[itemIndex].Category.CategorySizesInUse.Contains(availableSizes[availCardSizeIndex]))
{
string str = availableSizes[availCardSizeIndex];
tCell = new PdfPCell();
tCell.PaddingLeft = 10f;
tCell.PaddingRight = 10f;
cells[availCardSizeIndex + 1] = tCell;
cells[availCardSizeIndex].HorizontalAlignment = Element.ALIGN_CENTER;
PdfFormField f = PdfFormField.CreateCheckBox(_pdfResultStamper.Writer);
string fieldName = string.Format("InkSaver.chk{0}.{1}", availableSizes[availCardSizeIndex].Replace(".", ""), itemIndex + 1);
//f.FieldName = fieldName; <-- This causes the checkbox to be double-named (i.e. InkSaver.Global.chk0.InkSaver.Global.chk0
string js = string.Format("hideCardSize(event.target, {0}, '{1}');", itemIndex + 1, availableSizes[availCardSizeIndex]);
f.Action = PdfAction.JavaScript(js, _pdfResultStamper.Writer);
tCell.CellEvent = new ChildFieldEvent(_pdfResultStamper.Writer, f, fieldName);
checkboxes.Add(f);
}
else
{
//Add a blank cell
tCell = new PdfPCell();
cells[availCardSizeIndex + 1] = tCell;
}
}
//Test if the column text will fit
table.Rows.Add(new PdfPRow(cells));
canvas = _pdfResultStamper.GetUnderContent(pageNum);
ColumnText ct2 = new ColumnText(canvas);
ct2.AddElement(new PdfPTable(table));
ct2.Alignment = Element.ALIGN_LEFT;
ct2.SetSimpleColumn(targetPosition.Left, 0, targetPosition.Right, targetPosition.Top, 0, 0);
status = ct2.Go(true);
if ((status != ColumnText.NO_MORE_TEXT) || (itemIndex == (Items.Count - 1)))
{
ColumnText ct3 = new ColumnText(canvas);
ct3.AddElement(table);
ct3.Alignment = Element.ALIGN_LEFT;
ct3.SetSimpleColumn(targetPosition.Left, 0, targetPosition.Right, targetPosition.Top, 0, 0);
ct3.Go();
foreach (PdfFormField f in checkboxes)
{
_pdfResultStamper.AddAnnotation(f, pageNum);
}
checkboxes.Clear();
if (itemIndex < (Items.Count - 1))
{
pageNum++;
_pdfResultStamper.InsertPage(pageNum, _pdfTemplateReader.GetPageSize(1));
table = new PdfPTable(availableSizes.Count + 1);
table.SetTotalWidth(columnWidth);
table.WidthPercentage = 100;
table.Rows.Add(new PdfPRow(this.GetTableHeaderRow(availableSizes)));
}
}
}
}
private PdfPCell[] GetTableHeaderRow(List<string> AvailableSizes)
{
PdfPCell[] sizeHeaders = new PdfPCell[AvailableSizes.Count + 1];
Phrase devName = new Phrase("Device Name");
PdfPCell deviceHeader = new PdfPCell(devName);
deviceHeader.HorizontalAlignment = Element.ALIGN_CENTER;
deviceHeader.BackgroundColor = BaseColor.GRAY;
sizeHeaders[0] = deviceHeader;
for (int x = 0; x < AvailableSizes.Count; x++)
{
PdfPCell hCell = new PdfPCell(new Phrase(AvailableSizes[x]));
hCell.HorizontalAlignment = Element.ALIGN_CENTER;
hCell.BackgroundColor = BaseColor.GRAY;
sizeHeaders[x + 1] = hCell;
}
return sizeHeaders;
}
public void MergePdfs(string filePath, List<MemoryStream> pdfStreams)
{
//Create output stream
FileStream outStream = new FileStream(filePath, FileMode.Create);
Document document = null;
if (pdfStreams.Count > 0)
{
try
{
int PageCounter = 0;
//Create Main reader
PdfReader reader = new PdfReader(pdfStreams[0]);
PageCounter = reader.NumberOfPages;//This is if we have multiple pages in the cover page, we need to adjust the offset.
//rename fields in the PDF. This is required because PDF's cannot have more than one field with the same name
RenameFields(reader, PageCounter++);
//Create Main Doc
document = new Document(reader.GetPageSizeWithRotation(1));
//Create main writer
PdfCopy Writer = new PdfCopy(document, outStream);
//PdfCopyFields Writer = new PdfCopyFields(outStream);
//Open document for writing
document.Open();
////Add pages
Writer.AddDocument(reader);
//For each additional pdf after first combine them into main document
foreach (var PdfStream in pdfStreams.Skip(1))
{
PdfReader reader2 = new PdfReader(PdfStream);
//rename PDF fields
RenameFields(reader2, PageCounter++);
// Add content
Writer.AddDocument(reader);
}
//Writer.AddJavaScript(PostProcessing.GetSuperscriptJavaScript());
Writer.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
if (document != null)
document.Close();
foreach (var Strm in pdfStreams)
{
try { if (null != Strm) Strm.Dispose(); }
catch { }
}
//pdfStamper.Close();
outStream.Close();
}
}
}
private void RenameFields(PdfReader reader, int PageNum)
{
int tempPageNum = 1;
//rename all fields
foreach (string field in reader.AcroFields.Fields.Keys)
{
if (((reader.AcroFields.GetFieldType(field) == 1) || (reader.AcroFields.GetFieldType(field) == 2)) && (field.StartsWith("InkSaver")))
{
//This is a InkSaver button, set the name so its subclassed
string classPath;
if (reader.AcroFields.GetFieldType(field) == 2)
{
classPath = field.Substring(0, field.LastIndexOf("."));
if (field.StartsWith("InkSaver.chk"))
{
int a = field.LastIndexOf(".");
string sub = field.Substring(a + 1, (field.Length - a - 1));
int pageNum = int.Parse(sub);
int realPageNum = pageNum + tempPageNum;//PostProcessing.Instance.CoverPageLength;
PageNum = realPageNum;
}
}
else
{
classPath = field.Substring(0, field.LastIndexOf("."));
}
string newID = classPath + ".page" + PageNum.ToString();
bool ret = reader.AcroFields.RenameField(field, newID);
}
else
{
reader.AcroFields.RenameField(field, field + "_" + PageNum.ToString());// field + Guid.NewGuid().ToString("N"));
}
}
}
}
public class ChildFieldEvent : IPdfPCellEvent
{
protected PdfWriter writer;
protected PdfFormField parent;
protected string checkBoxName;
internal ChildFieldEvent(PdfWriter writer, PdfFormField parent, string CheckBoxName)
{
this.writer = writer;
this.parent = parent;
this.checkBoxName = CheckBoxName;
}
public void CellLayout(PdfPCell cell, Rectangle rect, PdfContentByte[] cb)
{
createCheckboxField(rect);
}
private void createCheckboxField(Rectangle rect)
{
RadioCheckField bt = new RadioCheckField(this.writer, rect, this.checkBoxName, "Yes");
bt.CheckType = RadioCheckField.TYPE_SQUARE;
bt.Checked = true;
this.parent.AddKid(bt.CheckField);
}
}
internal class CustomCategory
{
internal static List<string> AvailableSizes
{
get
{
List<string> retVal = new List<string>();
retVal.Add("1");
retVal.Add("2");
retVal.Add("3");
retVal.Add("4");
return retVal;
}
}
internal CustomCategory()
{
CategorySizesInUse = new List<string>();
}
internal List<string> CategorySizesInUse { get; set; }
}
internal class CustomObject
{
internal string Title { get; set; }
internal CustomCategory Category { get;set; }
}
Please take a look at the MergeForms example. Your example is too long for me to read, but at first sight, I'm missing the following line:
copy.setMergeFields();
By the way, in MergeForms2, the fields are also renamed before the form is merged.
OK i am parsing HTML from a string into a PDFCEll.
It works great thanks to some help from here.
Here is how i am doing it.
How do i use an external css file so i can use class's and not STYLE=""
public class XhtmlToListHelper : IElementHandler
{
// Generic list of elements
public List<IElement> elements = new List<IElement>();
// Add the item to the list
public void Add(IWritable w)
{
if (w is WritableElement)
{
elements.AddRange(((WritableElement)w).Elements());
}
}
string html = "<ul class=\"list\"><li>html 1</li><li>html 2</li><li>html 3</li></ul>";
using (TextReader sr = new StringReader(html))
{
XMLWorkerHelper.GetInstance().ParseXHtml(XhtmlHelper, sr);
}
foreach (var element in XhtmlHelper.elements)
{
if (element.IsContent())
{
PDFCell.AddElement(element);
}
}
Now i have got this far, but how to tye it all in evades me. Any help would be much apreacheted.
HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory());
ICSSResolver cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
cssResolver.AddCssFile(HttpContext.Current.Server.MapPath("~/Templates/css/core.css"), true);
If you poke around the source here and you should see how to implement it. Basically, your three line using block quadruples in size and complexity:
var XhtmlHelper = new XhtmlToListHelper();
var htmlContext = new HtmlPipelineContext(null);
htmlContext.SetTagFactory(iTextSharp.tool.xml.html.Tags.GetHtmlTagProcessorFactory());
var cssResolver = XMLWorkerHelper.GetInstance().GetDefaultCssResolver(false);
cssResolver.AddCssFile(#"c:\test.css", true);
var pipeline = new CssResolverPipeline(cssResolver, new HtmlPipeline(htmlContext, new ElementHandlerPipeline(XhtmlHelper, null)));//Here's where we add our IElementHandler
var worker = new XMLWorker(pipeline, true);
var parser = new XMLParser();
parser.AddListener(worker);
using (TextReader sr = new StringReader(html)) {
parser.Parse(sr);
}