How can I split a given paragraph to 2 paragraphs, due to that it fits only partial into canvas. After split, I would like to add the first part into canvas and the second to a new canvas.
public Paragraph addParagraphToPage(PdfDocument pdfDocument, int pageNum, Rectangle rectangle, Paragraph p)
{
PdfPage page = pdfDocument.getPage(pageNum);
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDocument);
Canvas canvas = new Canvas(pdfCanvas, pdfDocument, rectangle);
ParagraphRenderer currentRenderer = (ParagraphRenderer) p.createRendererSubTree();
currentRenderer.setParent(canvas.getRenderer());
result = currentRenderer.layout(new LayoutContext(new LayoutArea(pageNum, rectangle)));
ArrayList<Paragraph> paragraphs = new ArrayList<Paragraph>();
if (result.getStatus() != LayoutResult.FULL)
{
paragraphs = ????? // getNextParagraph(paragraphs, result, pageNum, rectangle, canvas);
if(paragraphs.size() == 2)
{
canvas.add( paragraphs.get(0));
return paragraphs.get(1);
}
}
return null;
}
Your approach is correct in general and layout in iText7 is flexible enough to allow you to do required thing in an easy manner. The only thing I see that is not very clear is that Paragraph is actually an element that cannot split itself and no classes in layout framework facilitate element splitting. You could do it manually, but there is no need to. Instead you should work with IRenderer, and ParagraphRenderer in particular, directly.
IRenderer can split itself as a result of layout operation and represents the necessary portion of data only compared to the Paragraph which contains full data.
You can add an IRenderer to the CanvasRenderer:
canvas.getRenderer().addChild(rendererToAdd.setParent(canvas.getRenderer()));
And you can access the partial renderers (the portion that fit the passed area and overflow part) from LayoutResult#getSplitRenderer() and LayoutResult#getOverflowRenderer().
In general, your code can be adapted like follows:
public ParagraphRenderer addParagraphToPage(PdfDocument pdfDocument, int pageNum, Rectangle rectangle, ParagraphRenderer renderer) {
PdfPage page = pdfDocument.getPage(pageNum);
PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDocument);
Canvas canvas = new Canvas(pdfCanvas, pdfDocument, rectangle);
renderer.setParent(canvas.getRenderer());
LayoutResult result = renderer.layout(new LayoutContext(new LayoutArea(pageNum, rectangle)));
IRenderer rendererToAdd = result.getStatus() == LayoutResult.FULL ? renderer : result.getSplitRenderer();
canvas.getRenderer().addChild(rendererToAdd.setParent(canvas.getRenderer()));
return result.getStatus() != LayoutResult.FULL ? (ParagraphRenderer) result.getOverflowRenderer() : null;
}
And then for adding paragraph to sequential pages until all the content is placed you basically need only two lines of code:
ParagraphRenderer renderer = (ParagraphRenderer) p.createRendererSubTree();
while ((renderer = addParagraphToPage(pdfDocument, pageNum++, rectangle, renderer)) != null);
Related
I have a C# application that generates a PDF invoice. In this invoice is a table of items and prices. This is generated using a PdfPTable and PdfPCells.
I want to be able to right-align the price column but I cannot seem to be able to - the text always comes out left-aligned in the cell.
Here is my code for creating the table:
PdfPTable table = new PdfPTable(2);
table.TotalWidth = invoice.PageSize.Width;
float[] widths = { invoice.PageSize.Width - 70f, 70f };
table.SetWidths(widths);
table.AddCell(new Phrase("Item Name", tableHeadFont));
table.AddCell(new Phrase("Price", tableHeadFont));
SqlCommand cmdItems = new SqlCommand("SELECT...", con);
using (SqlDataReader rdrItems = cmdItems.ExecuteReader())
{
while (rdrItems.Read())
{
table.AddCell(new Phrase(rdrItems["itemName"].ToString(), tableFont));
double price = Convert.ToDouble(rdrItems["price"]);
PdfPCell pcell = new PdfPCell();
pcell.HorizontalAlignment = PdfPCell.ALIGN_RIGHT;
pcell.AddElement(new Phrase(price.ToString("0.00"), tableFont));
table.AddCell(pcell);
}
}
Can anyone help?
I'm the original developer of iText, and the problem you're experiencing is explained in my book.
You're mixing text mode and composite mode.
In text mode, you create the PdfPCell with a Phrase as the parameter of the constructor, and you define the alignment at the level of the cell. However, you're working in composite mode. This mode is triggered as soon as you use the addElement() method. In composite mode, the alignment defined at the level of the cell is ignored (which explains your problem). Instead, the alignment of the separate elements is used.
How to solve your problem?
Either work in text mode by adding your Phrase to the cell in a different way.
Or work in composite mode and use a Paragraph for which you define the alignment.
The advantage of composite mode over text mode is that different paragraphs in the same cell can have different alignments, whereas you can only have one alignment in text mode. Another advantage is that you can add more than just text: you can also add images, lists, tables,... An advantage of text mode is speed: it takes less processing time to deal with the content of a cell.
private static PdfPCell PhraseCell(Phrase phrase, int align)
{
PdfPCell cell = new PdfPCell(phrase);
cell.BorderColor = BaseColor.WHITE;
// cell.VerticalAlignment = PdfCell.ALIGN_TOP;
//cell.VerticalAlignment = align;
cell.HorizontalAlignment = align;
cell.PaddingBottom = 2f;
cell.PaddingTop = 0f;
return cell;
}
Here is my derivation of user2660112's answer - one method to return a cell for insertion into a bordered and background-colored table, and a similar, but borderless/colorless variety:
private static PdfPCell GetCellForBorderedTable(Phrase phrase, int align, BaseColor color)
{
PdfPCell cell = new PdfPCell(phrase);
cell.HorizontalAlignment = align;
cell.PaddingBottom = 2f;
cell.PaddingTop = 0f;
cell.BackgroundColor = color;
cell.VerticalAlignment = PdfPCell.ALIGN_CENTER;
return cell;
}
private static PdfPCell GetCellForBorderlessTable(Phrase phrase, int align)
{
PdfPCell cell = new PdfPCell(phrase);
cell.HorizontalAlignment = align;
cell.PaddingBottom = 2f;
cell.PaddingTop = 0f;
cell.BorderWidth = PdfPCell.NO_BORDER;
cell.VerticalAlignment = PdfPCell.ALIGN_CENTER;
return cell;
}
These can then be called like so:
Font timesRoman9Font = FontFactory.GetFont(FontFactory.TIMES_ROMAN, 9, BaseColor.BLACK);
Font timesRoman9BoldFont = FontFactory.GetFont(FontFactory.TIMES_BOLD, 9, BaseColor.BLACK);
Phrase phrasesec1Heading = new Phrase("Duckbills Unlimited", timesRoman9BoldFont);
PdfPCell cellSec1Heading = GetCellForBorderedTable(phrasesec1Heading, Element.ALIGN_LEFT, BaseColor.YELLOW);
tblHeadings.AddCell(cellSec1Heading);
Phrase phrasePoisonToe = new Phrase("Poison Toe Toxicity Level (Metric Richter Scale, adjusted for follicle hue)", timesRoman9Font);
PdfPCell cellPoisonToe = GetCellForBorderlessTable(phrasePoisonToe, Element.ALIGN_LEFT);
tblFirstRow.AddCell(cellPoisonToe);
I ended up here searching for java Right aligning text in PdfPCell. So no offense if you are using java please use given snippet to achieve right alignment.
private PdfPCell getParagraphWithRightAlignCell(Paragraph paragraph) {
PdfPCell cell = new PdfPCell(paragraph);
cell.setBorderColor( BaseColor.WHITE);
cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
return cell;
}
In getParagraphWithRightAlignCell pass paragraph
Thanks
Perhaps its because you are mixing the different ways to add the cells? Have you tried explicitly creating a cell object, massaging it however you want then adding it for every cell?
Another thing you could try is setting the vertical alignment as well as the horizontal.
cell.HorizontalAlignment = Element.ALIGN_RIGHT;
Following the given examples pdfClown can both highlight a specific text and draw a rectangle around the respective words.
Is there a possibility to make this reactangle editable afterwards with the Adobe Acrobat?
My current workflow (as planned):
Import a document
Search document for Highlightings
Determine the Hightlighting's color
Draw a rectangle around the outer boundaries of the rectangle
add a callout to a another rectangle containing a letter, depending on the determined color
I can not (e.g.) drag the rectangle around the formerly highlighted word with Acrobat Reader, as far as I can see. I used the provided example from pdfClown's webpage to draw a reactangle around every character.
Is there something that I forgot to consider?
File inFile = null;
String inFilePath = "/path/to/inputFile/input_highlight.pdf";
String outDirPath = "/tmp";
try {
inFile = new File(inFilePath);
} catch (Exception e) {
throw new RuntimeException(inFilePath + " file access error.", e);
}
Document document = inFile.getDocument();
Pages pages = document.getPages();
PageStamper stamper = new PageStamper();
for (Page page : pages) {
stamper.setPage(page);
PageAnnotations annotations = page.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.getColor() == null) {
continue;
}
Rectangle2D textStringBox = annotation.getBox();
PrimitiveComposer composer = stamper.getBackground();
composer.setStrokeColor(DeviceRGBColor.Black);
textStringBox.setRect(annotation.getBox().getX(), annotation.getBox().getY(), annotation.getBox().getWidth(), annotation.getBox().getHeight());
composer.drawRectangle(textStringBox);
composer.stroke();
composer.beginLocalState();
composer.setStrokeColor(DeviceRGBColor.Black);
composer.end();
stamper.flush();
System.out.println("Text: " + annotation.getText());
System.out.println("Color: " + annotation.getColor());
System.out.println("Coordinates: " + annotation.getBox().toString());
annotation.setColor(DeviceRGBColor.White);
}
}
As it seems your main issue is that
I can not (e.g.) drag the rectangle around the formerly highlighted word with Acrobat Reader
The reason is that you draw your rectangle in the page content (the PageStamper you use is documented as Tool for content insertion into existing pages). The page content is fixed, in particular as far as Acrobat Reader is concerned; Acrobat Reader only allows you to move annotations.
If you want to have a rectangle you can drag around, therefore, you have to use a rectangle annotation. Rectangle annotations can be created like this:
new org.pdfclown.documents.interaction.annotations.Rectangle(
page,
new Rectangle(50, 370, 100, 30),
"Text of the Rectangle annotation"
).withColor(DeviceRGBColor.get(Color.RED))
.withBorder(new Border(1, new LineDash(new double[]{5})))
.withAuthor("Stefano")
.withSubject("Rectangle")
.withPopup(new Popup(
page,
new Rectangle2D.Double(200, 325, 200, 75),
"Text of the Popup annotation (this text won't be visible as associating popups to markup annotations overrides the former's properties with the latter's)"
));
(AnnotationSample.java)
You also mention that you want to add a callout; a callout annotation can be created like this:
new StaticNote(
page,
new Rectangle(250, 90, 150, 70),
"Text of the Callout note annotation"
).withLine(
new StaticNote.CalloutLine(
page,
new Point(250,125),
new Point(150,125),
new Point(100,100)
)
)
.withLineEndStyle(LineEndStyleEnum.OpenArrow)
.withBorder(new Border(1))
.withColor(DeviceRGBColor.get(Color.YELLOW));
(AnnotationSample.java)
Using OnEndPage, I add a footer to my PDF created with iTextSharp. The footer font gets progressively bolder with each page.
How can I create consistent NORMAL fonts in my footer?
Here is my code:
public override void OnEndPage(PdfWriter writer, Document doc)
{
iTextSharp.text.Image gif = null;
if (FooterImage)
{
if (File.Exists(PathImages))
{
gif = iTextSharp.text.Image.GetInstance(PathImages);
gif.ScaleToFit(75f, 75f);
gif.SetAbsolutePosition(0, 0);
}
}
string sFooter = string.Empty;
if (FooterURL != null && FooterURL.Length > 0)
{
sFooter = FooterURL + " ";
}
if (FooterDate != null && FooterDate.Length > 0)
{
sFooter += FooterDate + " ";
}
if (FooterPage)
{
sFooter += "Page " + doc.PageNumber.ToString();
}
PdfPTable footerTbl = new PdfPTable(1);
footerTbl.TotalWidth = 900;
footerTbl.HorizontalAlignment = Element.ALIGN_CENTER;
Phrase ph = new Phrase(sFooter, FontFactory.GetFont(FontFactory.TIMES, 10, iTextSharp.text.Font.NORMAL));
PdfPCell cell = new PdfPCell(ph);
cell.Border = 0;
cell.PaddingLeft = 10;
footerTbl.AddCell(cell);
if (FooterImage)
{
PdfContentByte cbfoot = writer.DirectContent;
PdfTemplate tpl = cbfoot.CreateTemplate(gif.Width / 5, gif.Height / 5);
tpl.AddImage(gif);
cbfoot.AddTemplate(tpl, doc.PageSize.Width - 100, 10);
}
footerTbl.WriteSelectedRows(0, -1, 10, 30, writer.DirectContent);
}
In the old days, when there wasn't as much choice as today regarding fonts, people used workarounds to create bold fonts. One way to make a font bold, was by adding the same text over and over again at the same position. I think that this is happening to you.
When you use page events correctly, the onEndPage() method is triggered automatically each time a page ends. My guess is that you're doing something very wrong that triggers the onEndPage() many times. Maybe you are called the onEndPage() from your code, maybe you're adding the page event to the writer more than once (and page events are cumulative).
If I have to guess, I would guess that you are doing the latter. My guess is based on the fact that you are using variables such as FooterImage in your onEndPage() method. How are you setting that variable. If you are setting it in the constructor of the page event and you're adding the new page event over and over again to the writer, then you're doing it wrong.
I have a JEditorPane that displays a link containing an image like so:
<a href='http://somesite.com/'>
<img src='someImage.png' />
</a>
When the JEditorPane display this as HTML, it puts a blue border around the image which I am trying to remove without any luck.
I want it to look like this inside of the jeditorpane:
image: (http://randomcloud.net/img/prob/valid.png)
But this is how the JEditorPane displays it:
image(http://randomcloud.net/img/prob/jeditorpane.png)
This is what I have tried so far, and it still is not working
editorPane = new JEditorPane("http://randomcloud.net/ads/index.php?id=1");
StyleSheet style = ((HTMLDocument)editorPane.getDocument()).getStyleSheet();
style.addRule("a img {text-decoration: none; border: none;}");
Any suggestions or insight ?
-Michel
HTLEditorKit's ImageView source. As you can see borderSize is set to DEFAULT_BORDER (2 pixels). You can replace the ImageView creation in your implementation of ViewFactory and override the method to provide desired border (0 as I understand).
protected void setPropertiesFromAttributes() {
StyleSheet sheet = getStyleSheet();
this.attr = sheet.getViewAttributes(this);
// Gutters
borderSize = (short)getIntAttr(HTML.Attribute.BORDER, isLink() ?
DEFAULT_BORDER : 0);
leftInset = rightInset = (short)(getIntAttr(HTML.Attribute.HSPACE,
0) + borderSize);
topInset = bottomInset = (short)(getIntAttr(HTML.Attribute.VSPACE,
0) + borderSize);
borderColor = ((StyledDocument)getDocument()).getForeground
(getAttributes());
AttributeSet attr = getElement().getAttributes();
// Alignment.
// PENDING: This needs to be changed to support the CSS versions
// when conversion from ALIGN to VERTICAL_ALIGN is complete.
Object alignment = attr.getAttribute(HTML.Attribute.ALIGN);
vAlign = 1.0f;
if (alignment != null) {
alignment = alignment.toString();
if ("top".equals(alignment)) {
vAlign = 0f;
}
else if ("middle".equals(alignment)) {
vAlign = .5f;
}
}
AttributeSet anchorAttr = (AttributeSet)attr.getAttribute(HTML.Tag.A);
if (anchorAttr != null && anchorAttr.isDefined
(HTML.Attribute.HREF)) {
synchronized(this) {
state |= LINK_FLAG;
}
}
else {
synchronized(this) {
state = (state | LINK_FLAG) ^ LINK_FLAG;
}
}
}
I think the blue border is just selection of text. Try to deselect the content or use jEditorPaneInstance.getCaret().setSelectionVisible(false);
#Alien595: On img tags, you can add an attribute named border that is 0.
Example:
<a href="your_link.html">
<img border="0" src="your_image.png"/>
</a>
I have a JTextPane (or JEditorPane) in which I want to add some buttons to format text (as shown in the picture).
When I change the selected text to Bold (making a new Style), the font family (and others attributes) also changes. Why? I want to set (or remove) the bold attribute in the selected text and other stays unchanged, as they were.
This is what I'm trying:
private void setBold(boolean flag){
HTMLDocument doc = (HTMLDocument) editorPane.getDocument();
int start = editorPane.getSelectionStart();
int end = editorPane.getSelectedText().length();
StyleContext ss = doc.getStyleSheet();
//check if BoldStyle exists and then add / remove it
Style style = ss.getStyle("BoldStyle");
if(style == null){
style = ss.addStyle("BoldStyle", null);
style.addAttribute(StyleConstants.Bold, true);
} else {
style.addAttribute(StyleConstants.Bold, false);
ss.removeStyle("BoldStyle");
}
doc.setCharacterAttributes(start, end, style, true);
}
But as I explained above, other attributes also change:
Any help will be appreciated. Thanks in advance!
http://oi40.tinypic.com/riuec9.jpg
What you are trying to do can be accomplished with one of the following two lines of code:
new StyledEditorKit.BoldAction().actionPerformed(null);
or
editorPane.getActionMap().get("font-bold").actionPerformed(null);
... where editorPane is an instance of JEditorPane of course.
Both will seamlessly take care of any attributes already defined and supports text selection.
Regarding your code, it does not work with previously styled text because you are overwriting the corresponding attributes with nothing. I mean, you never gather the values for the attributes already set for the current selected text using, say, the getAttributes() method. So, you are effectively resetting them to whatever default the global stylesheet specifies.
The good news is you don't need to worry about all this if you use one of the snippets above. Hope that helps.
I made some minor modifications to your code and it worked here:
private void setBold(boolean flag){
HTMLDocument doc = (HTMLDocument) editorPane.getDocument();
int start = editorPane.getSelectionStart();
int end = editorPane.getSelectionEnd();
if (start == end) {
return;
}
if (start > end) {
int life = start;
start = end;
end = life;
}
StyleContext ss = doc.getStyleSheet();
//check if BoldStyle exists and then add / remove it
Style style = ss.getStyle(editorPane.getSelectedText());
if(style == null){
style = ss.addStyle(editorPane.getSelectedText(), null);
style.addAttribute(StyleConstants.Bold, true);
} else {
style.addAttribute(StyleConstants.Bold, false);
ss.removeStyle(editorPane.getSelectedText());
}
doc.setCharacterAttributes(start, end - start, style, true);
}