How to send a shape to front in Aspose.Words - c#

I am placing a watermark on a Document, but sometimes the watermark ends up behind some image and I can't bring it to front. I tried to set the ZOrderPosition and ZOrder property to high values like 99 but it still not in front of everything else.

The problem occurs because watermark shape resides inside header footer story of Word document and main content is inside body story (please see Story class). If you insert a watermark using Microsoft Word 2016, you will observe the same behavior. All content of document's header/footer is always behind the main content of the document.
However, you may overcome this problem by manually inserting watermarks in each Page. You can achieve this by moving the cursor to the first Run in each Page of your document and then making those Runs as an anchor points for your watermarks. Please see the following code for example:
Document doc = new Document(MyDir + #"input.doc");
Node[] runs = doc.GetChildNodes(NodeType.Run, true).ToArray();
for (int i = 0; i < runs.Length; i++)
{
Run run = (Run)runs[i];
int length = run.Text.Length;
Run currentNode = run;
for (int x = 1; x < length; x++)
{
currentNode = SplitRun(currentNode, 1);
}
}
DocumentBuilder builder = new DocumentBuilder(doc);
PageSetup ps = builder.PageSetup;
NodeCollection smallRuns = doc.GetChildNodes(NodeType.Run, true);
LayoutCollector collector = new LayoutCollector(doc);
int pageIndex = 1;
foreach (Run run in smallRuns)
{
if (collector.GetStartPageIndex(run) == pageIndex)
{
Shape watermark = new Shape(doc, Aspose.Words.Drawing.ShapeType.TextPlainText);
watermark.RelativeHorizontalPosition = RelativeHorizontalPosition.Page;
watermark.RelativeVerticalPosition = RelativeVerticalPosition.Page;
watermark.Width = 300;
watermark.Height = 70;
watermark.HorizontalAlignment = HorizontalAlignment.Center;
watermark.VerticalAlignment = VerticalAlignment.Center;
watermark.Rotation = -40;
watermark.Fill.Color = Color.Gray;
watermark.StrokeColor = Color.Gray;
watermark.TextPath.Text = "watermarkText";
watermark.TextPath.FontFamily = "Arial";
watermark.Name = string.Format("WaterMark_{0}", Guid.NewGuid());
watermark.WrapType = WrapType.None;
builder.MoveTo(run);
builder.InsertNode(watermark);
pageIndex++;
}
}
doc.Save(MyDir + #"output\18.3.doc");
///////////////////////////////////////
private static Run SplitRun(Run run, int position)
{
Run afterRun = (Run)run.Clone(true);
afterRun.Text = run.Text.Substring(position);
run.Text = run.Text.Substring((0), (0) + (position));
run.ParentNode.InsertAfter(afterRun, run);
return afterRun;
}
Hope, this helps. I work with Aspose as Developer Evangelist.

Related

PdfSharpCore/MigraDocCore how can add a background image

I want to create invoices with PdfSharpCore. that also works very well.
I would like to add a background image for each page. Can someone help me here?
With this code I can insert a background image after the document is created.
CreateDocument();
PdfDocumentRenderer renderer = new(true)
{
Document = Pdf
};
if (!string.IsNullOrWhiteSpace(Invoice.BackgroundImage))
{
renderer.PrepareRenderPages();
int pages = renderer.DocumentRenderer.FormattedDocument.PageCount;
var background = XImage.FromStream(() => {
return System.IO.File.OpenRead(Invoice.BackgroundImage);
});
for (int i = 1; i <= pages; ++i)
{
PageInfo pageInfo = renderer.DocumentRenderer.FormattedDocument.GetPageInfo(i);
PdfPage page = renderer.PdfDocument.AddPage();
var gfx = XGraphics.FromPdfPage(page, XPageDirection.Downwards);
gfx.DrawImage(background, 0, 0, pageInfo.Width, pageInfo.Height);
renderer.DocumentRenderer.RenderPage(gfx, i);
}
}
else
renderer.RenderDocument();
if (!string.IsNullOrEmpty(password))
SetPassword(renderer, password);
renderer.PdfDocument.Save(filename);

WPF FixedPage with same instance of UserControl

First off, I'm not sure if I phrased the title correctly. There are UserControls which are added via a ViewModel and I find them by searching the VisualTree and add them to a ObservableCollection<Grid>. What I would like to do is Print each instance of the UserControl that I retrieve from the VisualTree into a FixedDocument but with each UserControl being on a single page until it fills that page and moves on to the next page.
Here is the code for my current Print Button Click Event:
private async void btnPrint_Click(object sender, RoutedEventArgs e)
{
try
{
if (tabMain.Items.Count > 0)
{
tab = new TabLayout();
tab.Payslip = new ObservableCollection<Grid>();
foreach (var grid in FindVisualChildren<Grid>(this))
{
if (grid.Name == "pdfFile")
{
tab.Payslip.Add(grid);
}
}
FrameworkElement toPrint = new FrameworkElement();
PrintDialog printDialog = new PrintDialog();
PrintCapabilities capabilities = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket);
Size pageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
Size visibleSize = new Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
FixedDocument fixedDoc = new FixedDocument();
StackPanel panel = new StackPanel(); //was trying to stack them in a stackpanel first but it threw an exception about same instance of usercontrol blah blah...
foreach (var doc in tab.Payslip.ToList())
{
double yOffset = 0;
doc.Measure((new Size(double.PositiveInfinity, double.PositiveInfinity)));
doc.Arrange(new Rect(new Point(0, 0), doc.DesiredSize));
Size size = doc.DesiredSize;
while (yOffset < size.Height)
{
VisualBrush vb = new VisualBrush(doc);
vb.Stretch = Stretch.None;
vb.AlignmentX = AlignmentX.Left;
vb.AlignmentY = AlignmentY.Top;
vb.ViewboxUnits = BrushMappingMode.Absolute;
vb.TileMode = TileMode.None;
vb.Viewbox = new Rect(0, yOffset, visibleSize.Width, visibleSize.Height);
FixedPage page = new FixedPage();
PageContent pageContent = new PageContent();
((IAddChild)pageContent).AddChild(page);
fixedDoc.Pages.Add(pageContent);
page.Width = fixedDoc.DocumentPaginator.PageSize.Width;
page.Height = fixedDoc.DocumentPaginator.PageSize.Height;
Canvas canvas = new Canvas();
FixedPage.SetLeft(canvas, capabilities.PageImageableArea.OriginWidth);
FixedPage.SetTop(canvas, capabilities.PageImageableArea.OriginHeight);
canvas.Width = visibleSize.Width;
canvas.Height = visibleSize.Height;
canvas.Background = vb;
page.Children.Add(canvas);
yOffset += visibleSize.Height;
}
}
//printDialog.PrintDocument(fixedDoc.DocumentPaginator, "");
ShowPrintPreview(fixedDoc);
}
}
catch(Exception ex)
{
var exceptionDialog = new MessageDialog
{
Message = { Text = ex.ToString() }
};
await DialogHost.Show(exceptionDialog, "RootDialog");
}
}
This is how it looks when I try printing three Tabs (UserControls are hosted in Tabs):
As you can see here it prints three separate pages
I want all three on one page and if I print 10 tabs, then it should fill the first page and go on to the next page.
The last time I asked a similar question I was questioned on whether I wrote the code. Bits and pieces of this code came from similar FixedDocument questions on StackOverflow but it has been edited to the point where it actually works for me. So yes I know that the FixedPage reference inside the foreach is whats causing the creation of the three separate pages and I do understand the code.
Summary:
What I want to know is how to get the UserControls from each Tab, onto a single page until its full, without getting the "Specified element is already the logical child of another element. Disconnect it first." error.
I ended up using ItemsControl to hold my ObservableCollection of Controls as a list and print from the ItemsControl using this XpsDocumentWriter method.
public void PrintItemsTo(ItemsControl ic, String jobName)
{
PrintDialog dlg = new PrintDialog();
dlg.UserPageRangeEnabled = true;
if (dlg.ShowDialog().GetValueOrDefault())
{
PageRange range = dlg.PageRange;
// range check - user selection starts from 1
if (range.PageTo > ic.Items.Count)
range.PageTo = ic.Items.Count;
dlg.PrintQueue.CurrentJobSettings.Description = jobName;
XpsDocumentWriter xdw = PrintQueue.CreateXpsDocumentWriter(dlg.PrintQueue);
if (dlg.UserPageRangeEnabled == false || range.PageTo < range.PageFrom)
WriteAllItems(ic, xdw);
else
WriteSelectedItems(ic, xdw, range);
}
}
private void WriteAllItems(ItemsControl ic, XpsDocumentWriter xdw)
{
PageRange range = new PageRange(1, ic.Items.Count);
WriteSelectedItems(ic, xdw, range);
}
private void WriteSelectedItems(ItemsControl ic, XpsDocumentWriter xdw, PageRange range)
{
IItemContainerGenerator generator = ic.ItemContainerGenerator;
// write visuals using a batch operation
VisualsToXpsDocument collator = (VisualsToXpsDocument)xdw.CreateVisualsCollator();
collator.BeginBatchWrite();
if (WritePageRange(collator, generator, range))
collator.EndBatchWrite();
}
private bool WritePageRange(VisualsToXpsDocument collator, IItemContainerGenerator generator, PageRange range)
{
// Get the generator position of the first data item
// PageRange reflects user's selection starts from 1
GeneratorPosition startPos = generator.GeneratorPositionFromIndex(range.PageFrom - 1);
// If PageFrom > PageTo, print in reverse order
// UPDATE: never occurs; PrintDialog always 'normalises' the PageRange
GeneratorDirection direction = (range.PageFrom <= range.PageTo) ? GeneratorDirection.Forward : GeneratorDirection.Backward;
using (generator.StartAt(startPos, direction, true))
{
for (int numPages = Math.Abs(range.PageTo - range.PageFrom) + 1; numPages > 0; --numPages)
{
bool newlyRealized;
// Get or create the child
UIElement next = generator.GenerateNext(out newlyRealized) as UIElement;
if (newlyRealized)
{
generator.PrepareItemContainer(next);
}
// The collator doesn't like ContentPresenters and produces blank pages
// for pages 2-n. Finding the child of the ContentPresenter and writing
// that solves seems to solve the problem
if (next is ContentPresenter)
{
ContentPresenter presenter = (ContentPresenter)next;
presenter.UpdateLayout();
presenter.ApplyTemplate(); // not sure if this is necessary
DependencyObject child = VisualTreeHelper.GetChild(presenter, 0);
if (child is UIElement)
next = (UIElement)child;
}
try
{
collator.Write(next);
}
catch
{
// if user prints to Microsoft XPS Document Writer
// and cancels the SaveAs dialog, we get an exception.
return false;
}
}
}
return true;
}
PrintItemsTo(), displays a PrintDialog to print to any printer while WriteAllItems displays a Dialog to save as PDF.

How to Print Listed invoices each on another (different) Page in C# windows form Application?

Scenario:
I am working on Windows form Application where i have a today sale report in datagridview and after selecting invoiceID in for loop from datagridview I have to print invoices but each invoice on different page.
How can i shift to next page while printing.
My Code :
private void prnDocument_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
CurrentY = 50;
CurrentX = 100;
for (int i = 0; i < dgvViewGeneralSaleReport.RowCount; i++)
{
if (dgvViewGeneralSaleReport.Rows[i].Cells[0].Value != " ")
{
invoiceID = Convert.ToInt32(dgvViewGeneralSaleReport.Rows[i].Cells[0].Value.ToString().Trim());
//// invoice design Start
leftMargin = 20;
// rightMargin = (int)e.MarginBounds.Right;
rightMargin = 830;
topMargin = (int)e.MarginBounds.Top;
bottomMargin = (int)e.MarginBounds.Bottom;
InvoiceWidth = (int)e.MarginBounds.Width;
InvoiceHeight = (int)e.MarginBounds.Height;
if (!ReadInvoice)
ReadInvoiceData();
SetInvoiceHead(e.Graphics); // Draw Invoice Head
SetOrderData(e.Graphics); // Draw Order Data
SetInvoiceData(e.Graphics, e); // Draw Invoice Data
//// invoice design End
ReadInvoice = true;
e.HasMorePages = true;
CurrentY = 50;
CurrentX = 100;
}
else
{
StopReading = true;
ReadInvoice = false;
e.HasMorePages = false;
break;
}
}
}
You need to place your loop outside of the PrintPage method. The call you make to start the printing needs to be the one in the loop.
Basically...
for (int i = 0; i < dgvViewGeneralSaleReport.RowCount; i++)
{
if (dgvViewGeneralSaleReport.Rows[i].Cells[0].Value != " ")
printDocument.Print();
}
Within the PrintPage method, continue to do all of the same things you are now, only that you will also have to maintain awareness of which page you are printing.
The important point here is that it's the individual call to PrintPage that defines the page. The control variable e.HasMorePages is the way you tell it to keep going.

Adding graphics to components created at runtime

For anybody that can help me out here I'd be very grateful!
I've got a very small app which creates several panels at runtime with a for LOOP. At the moment the number of panels to be created is derived from a value entered in a textbox, but will ultimately be determined by an integer read from a database
Each panel has a Label which is created in the same loop
My problem is that I want to draw 120 lines in each panel as it is created (in each iteration of the FOR loop), and I'm doing this with a nested WHILE loop
I can get everything to work fine, the panels are creating along with the Label titles, but for some reason I can't get the lines to draw
It's all in one method for testing, can anybody help me?
The code I currently have is as follows:
public void CreatePanels()
{
int PanelPosX = 50;
int PanelPosY = 500;
int LabelPosX = 10;
int LabelPosY = 10;
for (int i = 0; i < (Convert.ToInt32(textBox2.Text)); i++)
{
Panel pnlOverview = new Panel();
pnlOverview.Name = "InspectorPanel" + i.ToString();
pnlOverview.Text = "Inspector Panel " + i.ToString();
pnlOverview.Location = new Point(PanelPosX, PanelPosY);
pnlOverview.Size = new Size(974, 136);
pnlOverview.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
Controls.Add(pnlOverview);
PanelPosY += 170;
Label lblInspectorName = new Label();
lblInspectorName.Name = "InspectorName" + i.ToString();
lblInspectorName.Text = " Inspector " + i.ToString();
lblInspectorName.Width = 100;
lblInspectorName.Height = 13;
lblInspectorName.Location = new Point(LabelPosX, LabelPosY);
lblInspectorName.Size = new Size(974, 136);
pnlOverview.Controls.Add(lblInspectorName);
// Draw the Overview Chart
int x = 10;
int y = 0;
Pen OVTable = new Pen(Color.Black, 0);
while (y < 120)
{
Graphics mp = pnlOverview.CreateGraphics();
mp.DrawLine(OVTable, x, 40, x, 100);
y++;
x += 8;
}
}
return;
}
Thanks
Ivan
You might need to create an empty Image and draw into it and then add it to the Panel. The other option would be to Derive from Panel and override the OnPaint method, at which point you would draw your chart.
Perhaps there is a C# Chart component that would effect this for you.
Here's a link to a MSDN reference on rendering custom controls, which is what you are pursuing.

C# Add Controls To Panel In a Loop

I wish to add a button for every line in a file to a panel.
My code so far is:
StreamReader menu = new StreamReader("menu.prefs");
int repetition = 0;
while(!menu.EndOfStream)
{
Button dynamicbutton = new Button();
dynamicbutton.Click += new System.EventHandler(menuItem_Click);
dynamicbutton.Text = menu.ReadLine();
dynamicbutton.Visible = true;
dynamicbutton.Location = new Point(4+repetition*307, 4);
dynamicbutton.Height = 44;
dynamicbutton.Width = 203;
dynamicbutton.BackColor = Color.FromArgb(40,40,40);
dynamicbutton.ForeColor = Color.White;
dynamicbutton.Font = new Font("Lucida Console", 16);
dynamicbutton.Show();
menuPanel.Controls.Add(dynamicbutton);
repetition++;
MessageBox.Show(dynamicbutton.Location.ToString());
}
menu.Close();
The problem is that only the first control gets created.
The code looks fine but there could be a following situations.
1.You might have only one entry in the file, so you are experiencing only One Button added to the panel.
2.Your panel width is smaller than the sum of all the dynamic buttons width.
I suspect no 2 is the main reason that is causing problem.
So, I recommend that you use FlowLayoutPanel. To add a dynamic content as it automatically layout all the child controls.
Each time it is generating the same name for dynamic controls. That's the reason why it is showing only the last one. It simply overwrites the previous control each time.
int x = 4;
int y = 4;
foreach(PhysicianData pd in listPhysicians)
{
x = 4;
y = panPhysicians.Controls.Count * 30;
RadioButton rb = new RadioButton();
rb.CheckedChanged += new System.EventHandler(rbPhysician_CheckedChanged);
rb.Text = pd.name;
rb.Visible = true;
rb.Location = new Point(x, y);
rb.Height = 40;
rb.Width = 200;
rb.BackColor = SystemColors.Control;
rb.ForeColor = Color.Black;
rb.Font = new Font("Microsoft Sans Serif", 10);
rb.Show();
rb.Name = "rb" + panPhysicians.Controls.Count;
panPhysicians.Controls.Add(rb);
}
Try this code
StreamReader menu = new StreamReader("menu.prefs");
var str = menu.ReadToEnd();
var items = str.Split(new string[] {"\r\n" } , StringSplitOptions.RemoveEmptyEntries);
foreach (var item in items)
{
Button dynamicbutton = new Button();
dynamicbutton.Click += new System.EventHandler(menuItem_Click);
dynamicbutton.Text = item;
dynamicbutton.Visible = true;
dynamicbutton.Location = new Point(4+repetition*307, 4);
dynamicbutton.Height = 44;
dynamicbutton.Width = 203;
dynamicbutton.BackColor = Color.FromArgb(40,40,40);
dynamicbutton.ForeColor = Color.White;
dynamicbutton.Font = new Font("Lucida Console", 16);
dynamicbutton.Show();
menuPanel.Controls.Add(dynamicbutton);
repetition++;
}
The problem with Panel and similar controls other than the FlowLayoutPanel is when you create a control and a second one, the second is created at the same position if you are not changing it's location dynamically or setting it according to the other already added controls. Your control is there, it's in the back of the first control.
A flowLayoutPanel is better as it will add the controls next to each other as you add them while compromising more finer control at their positioning.
I also have similar problems with panels. For what you are doing it could be useful to just add strings to a listbox rather than using labels and a panel. That should be simpler.

Categories