Is it possible to have cell spacing within a table (PdfPTable) in iTextSharp? I can't see anywhere that it is possible. I did see one suggestion of using the iTextSharp.text.Table instead but that doesn't seem available on my version of iTextSharp (5.2.1).
If you're looking for true cell spacing like HTML's then no, the PdfPTable doesn't support that natively. However, the PdfPCell supports a property that takes a custom implementation of IPdfPCellEvent which will get called whenever a cell layout happens. Below is a simple implementation of one, you'll probably want to tweak it to your needs.
public class CellSpacingEvent : IPdfPCellEvent {
private int cellSpacing;
public CellSpacingEvent(int cellSpacing) {
this.cellSpacing = cellSpacing;
}
void IPdfPCellEvent.CellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases) {
//Grab the line canvas for drawing lines on
PdfContentByte cb = canvases[PdfPTable.LINECANVAS];
//Create a new rectangle using our previously supplied spacing
cb.Rectangle(
position.Left + this.cellSpacing,
position.Bottom + this.cellSpacing,
(position.Right - this.cellSpacing) - (position.Left + this.cellSpacing),
(position.Top - this.cellSpacing) - (position.Bottom + this.cellSpacing)
);
//Set a color
cb.SetColorStroke(BaseColor.RED);
//Draw the rectangle
cb.Stroke();
}
}
To use it:
//Create a two column table
PdfPTable table = new PdfPTable(2);
//Don't let the system draw the border, we'll do that
table.DefaultCell.Border = 0;
//Bind our custom event to the default cell
table.DefaultCell.CellEvent = new CellSpacingEvent(2);
//We're not changing actual layout so we're going to cheat and padd the cells a little
table.DefaultCell.Padding = 4;
//Add some cells
table.AddCell("Test");
table.AddCell("Test");
table.AddCell("Test");
table.AddCell("Test");
doc.Add(table);
The Table class has been removed from iText starting from 5.x, in favor of PdfPTable.
As for spacing, what you are looking for are the setPadding methods.
Have a look at iText's API for more information:
http://api.itextpdf.com/itext/com/itextpdf/text/pdf/PdfPCell.html
(It's for the Java version, but the C# port maintains the names of the methods)
Related
I'm using iText 7 to construct reusable PDF components that I reuse across multiple pages within a document. I'm using iText-dotnet for this task (v7), using F# as the language. (This shouldn't be hard to follow for non-F# people as it's just iText calls :D)
I know how to add annotations to a Page, that isn't the issue. Adding the annotation to the page is as simple as page.AddAnnotation(newAnnotation).
Where I'm having difficulty, is that there is no "Page" associated with a Canvas when you are using a PdfFormXObject() to render a Pdf fragment.
let template = new PdfFormXObject(rect)
let templateCanvas = PdfCanvas(template, pageContext.Canvas.GetPdfDocument())
let newCanvas = new Canvas(templateCanvas, rect)
Once I have the new Canvas, I try to write to the Canvas and add the Annotation via Page.AddAnnotation(). The problem is that there is no Page attached to the PdfFormXObject!
// Create the destination and annotation (destPage is the pageNumber)
let dest = PdfExplicitDestination.CreateFitB(destPage)
let action = PdfAction.CreateGoTo(dest)
let annotation = PdfLinkAnnotation(rect)
let border = iText.Kernel.Pdf.PdfAnnotationBorder(0f, 0f, 0f)
// set up the Annotation with action and display information
annotation
.SetHighlightMode(PdfAnnotation.HIGHLIGHT_PUSH)
.SetAction(action)
.SetBorder(border)
|> ignore
// Try adding the annotation to the page BOOM! (There is *NO* page (null) associated with newCanvas)
newCanvas.GetPage().AddAnnotation(annotation) |> ignore // HELP HERE: Is there another way to do this?
The issue is that I do not know of a different way to set the Annotation on the canvas. Is there a way to render the annotation and just add the annotation directly to the canvas as raw PDF instructions?
Alternatively, is there a way create a different reusable PDF fragment in iText so I can also reuse the GoTo annotation.
N.B. I could split off the annotations and then apply them every time I use the PdfFormXObject() on a new page, but that sort of defeats the purpose of reusing Pdf fragments (template) in my final PDF to reduce it's size.
If you can point me in the right direction, that would be great.
Again, this is not how to add an annotation to a Page(), that's easy. It's how to add an annotation to a PdfFormXObject (or similar mechanism that I'm unaware of for constructing rusable Pdf fragments).
-- As per John's comments below:
I cannot seem to find any reference to single use annotations.
I'm aware of the following example link, so I modified it to look like this:
private static void Main(string[] args)
{
try
{
PdfDocument pdfDocument = new PdfDocument(new PdfWriter("TestMultiLink.pdf"));
Document document = new Document(pdfDocument);
string destinationName = "MyForwardDestination";
// Create a PdfStringDestination to use more than once.
var stringDestination = new PdfStringDestination(destinationName);
for (int page = 1; page <= 50; page++)
{
document.Add(new Paragraph().SetFontSize(100).Add($"{page}"));
switch (page)
{
case 1: // First use of PdfStringDestination
document.Add(new Paragraph(new Link("Click here for a forward jump", stringDestination).SetFontSize(20)));
break;
case 3: // Re-use the stringDestination
document.Add(new Paragraph(new Link("Click here for a forward jump", stringDestination).SetFontSize(10)));
break;
case 42:
pdfDocument.AddNamedDestination(destinationName, PdfExplicitDestination.CreateFit(pdfDocument.GetLastPage()).GetPdfObject());
break;
}
if (page < 50)
document.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
document.Close();
}
catch (Exception e)
{
Console.WriteLine($"Ouch: {e.Message}");
}
}
If you dig into the iText source for iText.Layout.Link, you'll see that the String Destination is added as an Annotation. Therefore, I'm not sure if John's answer is true anymore.
Does anyone know how I can convert the Annotation to a Dictionary and how I would go about adding the PdfDictionary (raw) info into the PftFormXObject?
Thanks
#johnwhitington is correct.
Per PDF specification, annotations can only be added to a page, they cannot be added to a form XObject. It is not a limitation of iText or any other PDF library.
Annotations cannot be reused, each annotation is a distinct object.
First time working with PDFSharp, I have a win form that enables the user to select a font and size of it for the PDF to be created. Also the app draws some rectangles on the page with currently hardcoded color like this:
rect = new XRect(5, 300, 25, 15);
gfx.DrawRectangle(XBrushes.SeaShell, rect); //SET COLOR TO RECT
tf.Alignment = XParagraphAlignment.Center;
tf.DrawString("No", invoiceItemsHeaderFont, XBrushes.Black, rect,
XStringFormats.TopLeft);
I want the user to be able to pick the color via a drop down. How can I load all of the colors from XBrushes. to a list and then parse it as such, this is one of the attempts:
foreach(XBrushes xbrush in typeof(XBrushes)
{
colorsRect.Add(xbrush.ToString());
}
Thank you all!
I know this is a bit late but maybe this could help someone else.
I take it you are just looking to get a collection of the different brushes?
I looked at the XBrushes class and all its public static properties seem to be brushes. You could use reflection to go through all its properties and get the values of the ones that are brushes.
Something like:
public static IEnumerable<XSolidBrush> Brushes
{
get
{
return typeof(XBrushes).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)
.Select(p => p.GetValue(null))
.Where(b=>b.GetType()==typeof(XSolidBrush))
.Cast<XSolidBrush>();
}
}
Does anyone know whether it's possible, in Photoshop extend script, to convert an irregular selection (e.g. magic wand tool selection) into a rectangular selection encompassing the top, left, bottom and right bounds of the selection?
Here it is, I have documented the code so you can modify it later if you need. Also, check page 166 and following of Photoshop's JS reference manual, you may read more about selections - you can set feather, extend/intersect/etc. the selection if you need to.
Made for CS6, should work with latter.
#target photoshop
if (documents.length == 0) {
alert("nothing opened");
} else {
// start
//setup
var file = app.activeDocument;
var selec = file.selection;
//run
var bnds = selec.bounds; // get the bounds of current selection
var // save the particular pixel values
xLeft = bnds[0],
yTop = bnds[1],
xRight = bnds[2],
yBottom = bnds[3];
var newRect = [ [xLeft,yTop], [xLeft,yBottom], [xRight,yBottom], [xRight,yTop] ]; // set coords for selection, counter-clockwise
selec.deselect;
selec.select(newRect);
// end
}
I'm in the process of converting an application to use the MahApps colour and brush resources, to allow us to programmatically change the application theme/accent.
I need a very pale accented background colour (which will be used in dozens of places, and by different types of control), but the palest MahApps colour ("AccentColor4") isn't light enough. Short of modifying the MahApps library, is there any way to create additional Color and Brush resources in my application that would somehow be able to alter their colour based on the current MahApps accent?
In your case, I think I'd add a new key to my Accent (maybe AccentColor5?) based on the AccentColor4 value.
Here you can find a ThemeManagerHelper class made by punker76, which might help you calculating AccentColor5 from AccentColor4 (see CreateAppStyleBy method).
I also made this, which should make your life easier:
private void AddKeyForCurrentAccent(object key, object value, bool changeAccentImmediately = false)
{
Tuple<MahApps.Metro.AppTheme, MahApps.Metro.Accent> currentAppStyle = MahApps.Metro.ThemeManager.DetectAppStyle(Application.Current);
if(currentAppStyle.Item2.Resources.Contains(key))
{
currentAppStyle.Item2.Resources.Remove(key);
}
currentAppStyle.Item2.Resources.Add(key, value);
if(changeAccentImmediately)
{
var accentName = string.Format("CustomTheme.xaml");
CreateAccentFrom(accentName, currentAppStyle.Item2.Resources);
MahApps.Metro.ThemeManager.ChangeAppStyle(Application.Current, MahApps.Metro.ThemeManager.GetAccent(accentName), currentAppStyle.Item1);
}
}
private void CreateAccentFrom(string accentName, ResourceDictionary resourceDictionary)
{
var fileName = System.IO.Path.Combine(System.IO.Path.GetTempPath(), accentName);
using (var writer = System.Xml.XmlWriter.Create(fileName, new System.Xml.XmlWriterSettings { Indent = true }))
{
System.Windows.Markup.XamlWriter.Save(resourceDictionary, writer);
writer.Close();
}
resourceDictionary = new ResourceDictionary() { Source = new Uri(fileName, UriKind.Absolute) };
var newAccent = new MahApps.Metro.Accent { Name = accentName, Resources = resourceDictionary };
MahApps.Metro.ThemeManager.AddAccent(accentName, newAccent.Resources.Source);
}
AddKeyForCurrentAccent will add (or replace - if it already exists) a key in the current Accent for the application.
If changeAccentImmediately is set to true, it will create a new file and save the new Accent (with the inserted/replaced keys) and set it as the Accent for the application
TIP: You can add/replace all the keys you want and set changeAccentImmediately to true only on the last one (this should improve performance).
Use it like this:
AddKeyForCurrentAccent("AccentColor5", Brushes.Pink); // Does not replace the accent immediately (so changes are not visible) - works faster
// AND/OR
AddKeyForCurrentAccent("AccentColor5", Brushes.HotPink, true); // Will update the UI
IMPORTANT: Please note that if you use your custom keys, and you forget to add it to your Accent, it'll use the default value for that type.
Good luck!
I'm working with dojox.drawing.Drawing to create a simple diagramming tool. I have created a custom tool to draw rounded rectangle by extending dojox.drawing.tools.Rect as shown below -
dojo.provide("dojox.drawing.tools.custom.RoundedRect");
dojo.require("dojox.drawing.tools.Rect");
dojox.drawing.tools.custom.RoundedRect = dojox.drawing.util.oo.declare(
dojox.drawing.tools.Rect,
function(options){
},
{
customType:"roundedrect"
}
);
dojox.drawing.tools.custom.RoundedRect.setup = {
name:"dojox.drawing.tools.custom.RoundedRect",
tooltip:"Rounded Rect",
iconClass:"iconRounded"
};
dojox.drawing.register(dojox.drawing.tools.custom.RoundedRect.setup, "tool");
I was able to add my tool to the toolbar and use it to draw a rectagle on canvas. Now, I would like to customize the rectangle created by my custom tool to have rounded corners, but I'm not able to figure out how.
I have checked the source of dojox.drawing.tools.Rect class as well as it's parent dojox.drawing.stencil.Rect class and I can see the actual rectangle being created in dojox.drawing.stencil.Rect as follows -
_create: function(/*String*/shp, /*StencilData*/d, /*Object*/sty){
// summary:
// Creates a dojox.gfx.shape based on passed arguments.
// Can be called many times by implementation to create
// multiple shapes in one stencil.
//
//console.log("render rect", d)
//console.log("rect sty:", sty)
this.remove(this[shp]);
this[shp] = this.container.createRect(d)
.setStroke(sty)
.setFill(sty.fill);
this._setNodeAtts(this[shp]);
}
In dojox.gfx, rounded corners can be added to a a rectangle by setting r property.
With this context, could anybody please provide answers to my following questions?
What's the mechanism in dojox.drawing to customize the appearance of rectangle to have
rounded corners?
In the code snippet above, StencilData is passed to createRect call. What's the mechanism to customize this data? Can the r property of a rectangle that governs rounded corners be set in this data?
Adding rounded rectangles programmatically is easy. In the tests folder you'll find test_shadows.html which has a line that adds a rectangle with rounded corners:
myDrawing.addStencil("rect", {data:{x:50, y:175, width:100, height:50, r:10}});
You create a data object with x,y,width,height, and a value for r (otherwise it defaults to 0).
If you wanted to do it by extending rect, the easiest way to do it would just be to set the value in the constructor function (data.r=10, for example), or you could create a pointsToData function to override Rect's version. Either you would have set the value for this.data.r, or the default:
pointsToData: function(/*Array*/p){
// summary:
// Converts points to data
p = p || this.points;
var s = p[0];
var e = p[2];
this.data = {
x: s.x,
y: s.y,
width: e.x-s.x,
height: e.y-s.y,
r:this.data.r || 10
};
return this.data;
},
In that example I give r the value 10 as the default, instead of 0 as it was before. This works because every time stencil goes to draw your rect, it converts canvas x,y points (all stencils remember their points) to data (which gfx uses to draw). In other words this function will always be called before rect renders.